diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-10-23 04:31:11 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-10-23 04:31:11 +0000 |
commit | 4ccad563d2a3559f0557bfb177bcf45144219bdf (patch) | |
tree | 46bb86f514fbf6bad82da48e69a18fb09d878834 /libgo/go/crypto/x509/pem_decrypt.go | |
parent | 0b7463235f0e23c624d1911c9b15f531108cc5a6 (diff) | |
download | gcc-4ccad563d2a3559f0557bfb177bcf45144219bdf.zip gcc-4ccad563d2a3559f0557bfb177bcf45144219bdf.tar.gz gcc-4ccad563d2a3559f0557bfb177bcf45144219bdf.tar.bz2 |
libgo: Update to current sources.
From-SVN: r192704
Diffstat (limited to 'libgo/go/crypto/x509/pem_decrypt.go')
-rw-r--r-- | libgo/go/crypto/x509/pem_decrypt.go | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/libgo/go/crypto/x509/pem_decrypt.go b/libgo/go/crypto/x509/pem_decrypt.go new file mode 100644 index 0000000..21f62e5 --- /dev/null +++ b/libgo/go/crypto/x509/pem_decrypt.go @@ -0,0 +1,133 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +// RFC 1423 describes the encryption of PEM blocks. The algorithm used to +// generate a key from the password was derived by looking at the OpenSSL +// implementation. + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/md5" + "encoding/hex" + "encoding/pem" + "errors" + "strings" +) + +// rfc1423Algos represents how to create a block cipher for a decryption mode. +type rfc1423Algo struct { + cipherFunc func([]byte) (cipher.Block, error) + keySize int +} + +// deriveKey uses a key derivation function to stretch the password into a key +// with the number of bits our cipher requires. This algorithm was derived from +// the OpenSSL source. +func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { + hash := md5.New() + out := make([]byte, c.keySize) + var digest []byte + + for i := 0; i < len(out); i += len(digest) { + hash.Reset() + hash.Write(digest) + hash.Write(password) + hash.Write(salt) + digest = hash.Sum(digest[:0]) + copy(out[i:], digest) + } + + return out +} + +// rfc1423Algos is a mapping of encryption algorithm to an rfc1423Algo that can +// create block ciphers for that mode. +var rfc1423Algos = map[string]rfc1423Algo{ + "DES-CBC": {des.NewCipher, 8}, + "DES-EDE3-CBC": {des.NewTripleDESCipher, 24}, + "AES-128-CBC": {aes.NewCipher, 16}, + "AES-192-CBC": {aes.NewCipher, 24}, + "AES-256-CBC": {aes.NewCipher, 32}, +} + +// IsEncryptedPEMBlock returns if the PEM block is password encrypted. +func IsEncryptedPEMBlock(b *pem.Block) bool { + _, ok := b.Headers["DEK-Info"] + return ok +} + +// IncorrectPasswordError is returned when an incorrect password is detected. +var IncorrectPasswordError = errors.New("x509: decryption password incorrect") + +// DecryptPEMBlock takes a password encrypted PEM block and the password used to +// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects +// the DEK-Info header to determine the algorithm used for decryption. If no +// DEK-Info header is present, an error is returned. If an incorrect password +// is detected an IncorrectPasswordError is returned. +func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { + dek, ok := b.Headers["DEK-Info"] + if !ok { + return nil, errors.New("x509: no DEK-Info header in block") + } + + idx := strings.Index(dek, ",") + if idx == -1 { + return nil, errors.New("x509: malformed DEK-Info header") + } + + mode, hexIV := dek[:idx], dek[idx+1:] + iv, err := hex.DecodeString(hexIV) + if err != nil { + return nil, err + } + if len(iv) < 8 { + return nil, errors.New("x509: not enough bytes in IV") + } + + ciph, ok := rfc1423Algos[mode] + if !ok { + return nil, errors.New("x509: unknown encryption mode") + } + + // Based on the OpenSSL implementation. The salt is the first 8 bytes + // of the initialization vector. + key := ciph.deriveKey(password, iv[:8]) + block, err := ciph.cipherFunc(key) + if err != nil { + return nil, err + } + + data := make([]byte, len(b.Bytes)) + dec := cipher.NewCBCDecrypter(block, iv) + dec.CryptBlocks(data, b.Bytes) + + // Blocks are padded using a scheme where the last n bytes of padding are all + // equal to n. It can pad from 1 to 8 bytes inclusive. See RFC 1423. + // For example: + // [x y z 2 2] + // [x y 7 7 7 7 7 7 7] + // If we detect a bad padding, we assume it is an invalid password. + dlen := len(data) + if dlen == 0 { + return nil, errors.New("x509: invalid padding") + } + last := data[dlen-1] + if dlen < int(last) { + return nil, IncorrectPasswordError + } + if last == 0 || last > 8 { + return nil, IncorrectPasswordError + } + for _, val := range data[dlen-int(last):] { + if val != last { + return nil, IncorrectPasswordError + } + } + + return data[:dlen-int(last)], nil +} |