OLD | NEW |
1 // Copyright 2012 The Go Authors. All rights reserved. | 1 // Copyright 2012 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package x509 | 5 package x509 |
6 | 6 |
7 // RFC 1423 describes the encryption of PEM blocks. The algorithm used to | 7 // RFC 1423 describes the encryption of PEM blocks. The algorithm used to |
8 // generate a key from the password was derived by looking at the OpenSSL | 8 // generate a key from the password was derived by looking at the OpenSSL |
9 // implementation. | 9 // implementation. |
10 | 10 |
11 import ( | 11 import ( |
12 "crypto/aes" | 12 "crypto/aes" |
13 "crypto/cipher" | 13 "crypto/cipher" |
14 "crypto/des" | 14 "crypto/des" |
15 "crypto/md5" | 15 "crypto/md5" |
16 "encoding/hex" | 16 "encoding/hex" |
17 "encoding/pem" | 17 "encoding/pem" |
18 "errors" | 18 "errors" |
19 "strings" | 19 "strings" |
20 ) | 20 ) |
21 | 21 |
22 // rfc1423Algos represents how to create a block cipher for a decryption mode. | 22 // rfc1423Algos represents how to create a block cipher for a decryption mode. |
23 type rfc1423Algo struct { | 23 type rfc1423Algo struct { |
24 cipherFunc func([]byte) (cipher.Block, error) | 24 cipherFunc func([]byte) (cipher.Block, error) |
25 keySize int | 25 keySize int |
26 } | 26 } |
27 | 27 |
| 28 // rfc1423Algos is a mapping of encryption algorithm to an rfc1423Algo that can |
| 29 // create block ciphers for that mode. |
| 30 var rfc1423Algos = map[string]rfc1423Algo{ |
| 31 "DES-CBC": {des.NewCipher, 8}, |
| 32 "DES-EDE3-CBC": {des.NewTripleDESCipher, 24}, |
| 33 "AES-128-CBC": {aes.NewCipher, 16}, |
| 34 "AES-192-CBC": {aes.NewCipher, 24}, |
| 35 "AES-256-CBC": {aes.NewCipher, 32}, |
| 36 } |
| 37 |
28 // deriveKey uses a key derivation function to stretch the password into a key | 38 // deriveKey uses a key derivation function to stretch the password into a key |
29 // with the number of bits our cipher requires. This algorithm was derived from | 39 // with the number of bits our cipher requires. This algorithm was derived from |
30 // the OpenSSL source. | 40 // the OpenSSL source. |
31 func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { | 41 func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { |
32 hash := md5.New() | 42 hash := md5.New() |
33 out := make([]byte, c.keySize) | 43 out := make([]byte, c.keySize) |
34 var digest []byte | 44 var digest []byte |
35 | 45 |
36 for i := 0; i < len(out); i += len(digest) { | 46 for i := 0; i < len(out); i += len(digest) { |
37 hash.Reset() | 47 hash.Reset() |
38 hash.Write(digest) | 48 hash.Write(digest) |
39 hash.Write(password) | 49 hash.Write(password) |
40 hash.Write(salt) | 50 hash.Write(salt) |
41 digest = hash.Sum(digest[:0]) | 51 digest = hash.Sum(digest[:0]) |
42 copy(out[i:], digest) | 52 copy(out[i:], digest) |
43 } | 53 } |
44 | 54 |
45 return out | 55 return out |
46 } | 56 } |
47 | 57 |
48 // rfc1423Algos is a mapping of encryption algorithm to an rfc1423Algo that can | |
49 // create block ciphers for that mode. | |
50 var rfc1423Algos = map[string]rfc1423Algo{ | |
51 "DES-CBC": {des.NewCipher, 8}, | |
52 "DES-EDE3-CBC": {des.NewTripleDESCipher, 24}, | |
53 "AES-128-CBC": {aes.NewCipher, 16}, | |
54 "AES-192-CBC": {aes.NewCipher, 24}, | |
55 "AES-256-CBC": {aes.NewCipher, 32}, | |
56 } | |
57 | |
58 // IsEncryptedPEMBlock returns if the PEM block is password encrypted. | 58 // IsEncryptedPEMBlock returns if the PEM block is password encrypted. |
59 func IsEncryptedPEMBlock(b *pem.Block) bool { | 59 func IsEncryptedPEMBlock(b *pem.Block) bool { |
60 _, ok := b.Headers["DEK-Info"] | 60 _, ok := b.Headers["DEK-Info"] |
61 return ok | 61 return ok |
62 } | 62 } |
63 | 63 |
64 // IncorrectPasswordError is returned when an incorrect password is detected. | 64 // IncorrectPasswordError is returned when an incorrect password is detected. |
65 var IncorrectPasswordError = errors.New("x509: decryption password incorrect") | 65 var IncorrectPasswordError = errors.New("x509: decryption password incorrect") |
66 | 66 |
67 // DecryptPEMBlock takes a password encrypted PEM block and the password used to | 67 // DecryptPEMBlock takes a password encrypted PEM block and the password used to |
68 // encrypt it and returns a slice of decrypted DER encoded bytes. It inspects | 68 // encrypt it and returns a slice of decrypted DER encoded bytes. It inspects |
69 // the DEK-Info header to determine the algorithm used for decryption. If no | 69 // the DEK-Info header to determine the algorithm used for decryption. If no |
70 // DEK-Info header is present, an error is returned. If an incorrect password | 70 // DEK-Info header is present, an error is returned. If an incorrect password |
71 // is detected an IncorrectPasswordError is returned. | 71 // is detected an IncorrectPasswordError is returned. |
72 func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { | 72 func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { |
73 dek, ok := b.Headers["DEK-Info"] | 73 dek, ok := b.Headers["DEK-Info"] |
74 if !ok { | 74 if !ok { |
75 return nil, errors.New("x509: no DEK-Info header in block") | 75 return nil, errors.New("x509: no DEK-Info header in block") |
76 } | 76 } |
77 | 77 |
78 idx := strings.Index(dek, ",") | 78 idx := strings.Index(dek, ",") |
79 if idx == -1 { | 79 if idx == -1 { |
80 return nil, errors.New("x509: malformed DEK-Info header") | 80 return nil, errors.New("x509: malformed DEK-Info header") |
81 } | 81 } |
82 | 82 |
83 mode, hexIV := dek[:idx], dek[idx+1:] | 83 mode, hexIV := dek[:idx], dek[idx+1:] |
| 84 ciph, ok := rfc1423Algos[mode] |
| 85 if !ok { |
| 86 return nil, errors.New("x509: unknown encryption mode") |
| 87 } |
84 iv, err := hex.DecodeString(hexIV) | 88 iv, err := hex.DecodeString(hexIV) |
85 if err != nil { | 89 if err != nil { |
86 return nil, err | 90 return nil, err |
87 } | 91 } |
88 if len(iv) < 8 { | 92 if len(iv) < 8 { |
89 return nil, errors.New("x509: not enough bytes in IV") | 93 return nil, errors.New("x509: not enough bytes in IV") |
90 } | 94 } |
91 | 95 |
92 ciph, ok := rfc1423Algos[mode] | |
93 if !ok { | |
94 return nil, errors.New("x509: unknown encryption mode") | |
95 } | |
96 | |
97 // Based on the OpenSSL implementation. The salt is the first 8 bytes | 96 // Based on the OpenSSL implementation. The salt is the first 8 bytes |
98 // of the initialization vector. | 97 // of the initialization vector. |
99 key := ciph.deriveKey(password, iv[:8]) | 98 key := ciph.deriveKey(password, iv[:8]) |
100 block, err := ciph.cipherFunc(key) | 99 block, err := ciph.cipherFunc(key) |
101 if err != nil { | 100 if err != nil { |
102 return nil, err | 101 return nil, err |
103 } | 102 } |
104 | 103 |
105 data := make([]byte, len(b.Bytes)) | 104 data := make([]byte, len(b.Bytes)) |
106 dec := cipher.NewCBCDecrypter(block, iv) | 105 dec := cipher.NewCBCDecrypter(block, iv) |
107 dec.CryptBlocks(data, b.Bytes) | 106 dec.CryptBlocks(data, b.Bytes) |
108 | 107 |
109 // Blocks are padded using a scheme where the last n bytes of padding ar
e all | 108 // Blocks are padded using a scheme where the last n bytes of padding ar
e all |
110 » // equal to n. It can pad from 1 to 8 bytes inclusive. See RFC 1423. | 109 » // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1
423. |
111 // For example: | 110 // For example: |
112 // [x y z 2 2] | 111 // [x y z 2 2] |
113 // [x y 7 7 7 7 7 7 7] | 112 // [x y 7 7 7 7 7 7 7] |
114 // If we detect a bad padding, we assume it is an invalid password. | 113 // If we detect a bad padding, we assume it is an invalid password. |
115 dlen := len(data) | 114 dlen := len(data) |
116 » if dlen == 0 { | 115 » blockSize := block.BlockSize() |
| 116 » if dlen == 0 || dlen%blockSize != 0 { |
117 return nil, errors.New("x509: invalid padding") | 117 return nil, errors.New("x509: invalid padding") |
118 } | 118 } |
119 » last := data[dlen-1] | 119 » last := int(data[dlen-1]) |
120 » if dlen < int(last) { | 120 » if dlen < last { |
121 return nil, IncorrectPasswordError | 121 return nil, IncorrectPasswordError |
122 } | 122 } |
123 » if last == 0 || last > 8 { | 123 » if last == 0 || last > blockSize { |
124 return nil, IncorrectPasswordError | 124 return nil, IncorrectPasswordError |
125 } | 125 } |
126 » for _, val := range data[dlen-int(last):] { | 126 » for _, val := range data[dlen-last:] { |
127 » » if val != last { | 127 » » if int(val) != last { |
128 return nil, IncorrectPasswordError | 128 return nil, IncorrectPasswordError |
129 } | 129 } |
130 } | 130 } |
131 | 131 » return data[:dlen-last], nil |
132 » return data[:dlen-int(last)], nil | |
133 } | 132 } |
OLD | NEW |