Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(774)

Side by Side Diff: src/pkg/crypto/x509/pem_decrypt.go

Issue 6827058: code review 6827058: crypto/x509: fix DecryptPEMBlock (Closed)
Patch Set: diff -r 25dcee3d220c https://go.googlecode.com/hg/ Created 11 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/pkg/crypto/x509/pem_decrypt_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 }
OLDNEW
« no previous file with comments | « no previous file | src/pkg/crypto/x509/pem_decrypt_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b