From 07bb47f793a073c0d0e7ad5ee12ebd4984c780d6 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Sat, 2 Sep 2017 16:09:29 -0700 Subject: [PATCH] Replace pkcs12 code with upstream --- builder/azure/pkcs12/README.md | 9 - builder/azure/pkcs12/bmp-string.go | 34 ++- builder/azure/pkcs12/bmp-string_test.go | 101 ++++---- builder/azure/pkcs12/crypto.go | 131 ++++++++-- builder/azure/pkcs12/crypto_test.go | 197 ++++++++------- builder/azure/pkcs12/errors.go | 6 +- builder/azure/pkcs12/mac.go | 25 +- builder/azure/pkcs12/mac_test.go | 18 +- builder/azure/pkcs12/pbkdf.go | 108 ++++----- builder/azure/pkcs12/pbkdf_test.go | 24 +- builder/azure/pkcs12/pkcs12.go | 308 +++++++++++++++++++++++- builder/azure/pkcs12/pkcs12_test.go | 138 ++++++++++- builder/azure/pkcs12/pkcs8.go | 5 +- builder/azure/pkcs12/pkcs8_test.go | 2 +- builder/azure/pkcs12/rc2/bench_test.go | 27 +++ builder/azure/pkcs12/rc2/rc2.go | 36 +-- builder/azure/pkcs12/rc2/rc2_test.go | 22 +- builder/azure/pkcs12/safebags.go | 50 +++- builder/azure/pkcs12/safebags_test.go | 34 +-- 19 files changed, 916 insertions(+), 359 deletions(-) delete mode 100644 builder/azure/pkcs12/README.md create mode 100644 builder/azure/pkcs12/rc2/bench_test.go diff --git a/builder/azure/pkcs12/README.md b/builder/azure/pkcs12/README.md deleted file mode 100644 index c8b4603e9..000000000 --- a/builder/azure/pkcs12/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This is a fork of the from the original PKCS#12 parsing code -published in the Azure repository [go-pkcs12](https://github.com/Azure/go-pkcs12). -This fork adds serializing a x509 certificate and private key as PKCS#12 binary blob -(aka .PFX file). Due to the specific nature of this code it was not accepted for -inclusion in the official Go crypto repository. - -The methods used for decoding PKCS#12 have been moved to the test files to further -discourage the use of this library for decoding. Please use the official -[pkcs12](https://godoc.org/golang.org/x/crypto/pkcs12) library for decode support. diff --git a/builder/azure/pkcs12/bmp-string.go b/builder/azure/pkcs12/bmp-string.go index e98069b82..284d2a68f 100644 --- a/builder/azure/pkcs12/bmp-string.go +++ b/builder/azure/pkcs12/bmp-string.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 pkcs12 import ( @@ -5,6 +9,7 @@ import ( "unicode/utf16" ) +// bmpString returns s encoded in UCS-2 with a zero terminator. func bmpString(s string) ([]byte, error) { // References: // https://tools.ietf.org/html/rfc7292#appendix-B.1 @@ -13,14 +18,33 @@ func bmpString(s string) ([]byte, error) { // EncodeRune returns 0xfffd if the rune does not need special encoding // - the above RFC provides the info that BMPStrings are NULL terminated. - rv := make([]byte, 0, 2*len(s)+2) + ret := make([]byte, 0, 2*len(s)+2) for _, r := range s { if t, _ := utf16.EncodeRune(r); t != 0xfffd { - return nil, errors.New("string contains characters that cannot be encoded in UCS-2") + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") } - rv = append(rv, byte(r/256), byte(r%256)) + ret = append(ret, byte(r/256), byte(r%256)) } - rv = append(rv, 0, 0) - return rv, nil + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil } diff --git a/builder/azure/pkcs12/bmp-string_test.go b/builder/azure/pkcs12/bmp-string_test.go index 7b861c3e8..711528b81 100644 --- a/builder/azure/pkcs12/bmp-string_test.go +++ b/builder/azure/pkcs12/bmp-string_test.go @@ -1,70 +1,69 @@ +// Copyright 2015 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 pkcs12 import ( "bytes" - "errors" + "encoding/hex" "testing" - "unicode/utf16" ) -func decodeBMPString(bmpString []byte) (string, error) { - if len(bmpString)%2 != 0 { - return "", errors.New("expected BMP byte string to be an even length") - } - - // strip terminator if present - if terminator := bmpString[len(bmpString)-2:]; terminator[0] == terminator[1] && terminator[1] == 0 { - bmpString = bmpString[:len(bmpString)-2] - } - - s := make([]uint16, 0, len(bmpString)/2) - for len(bmpString) > 0 { - s = append(s, uint16(bmpString[0])*265+uint16(bmpString[1])) - bmpString = bmpString[2:] - } - - return string(utf16.Decode(s)), nil +var bmpStringTests = []struct { + in string + expectedHex string + shouldFail bool +}{ + {"", "0000", false}, + // Example from https://tools.ietf.org/html/rfc7292#appendix-B. + {"Beavis", "0042006500610076006900730000", false}, + // Some characters from the "Letterlike Symbols Unicode block". + {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false}, + // any character outside the BMP should trigger an error. + {"\U0001f000 East wind (Mahjong)", "", true}, } func TestBMPStringDecode(t *testing.T) { - _, err := decodeBMPString([]byte("a")) - if err == nil { - t.Fatal("expected decode to fail, but it succeeded") + if _, err := decodeBMPString([]byte("a")); err == nil { + t.Fatalf("expected decode to fail, but it succeeded") } } func TestBMPString(t *testing.T) { - str, err := bmpString("") - if !bytes.Equal(str, []byte{0, 0}) { - t.Errorf("expected empty string to return double 0, but found: % x", str) - } - if err != nil { - t.Errorf("err: %v", err) - } + for i, test := range bmpStringTests { + expected, err := hex.DecodeString(test.expectedHex) + if err != nil { + t.Fatalf("#%d: failed to decode expectation", i) + } - // Example from https://tools.ietf.org/html/rfc7292#appendix-B - str, err = bmpString("Beavis") - if !bytes.Equal(str, []byte{0x00, 0x42, 0x00, 0x65, 0x00, 0x61, 0x00, 0x0076, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00}) { - t.Errorf("expected 'Beavis' to return 0x00 0x42 0x00 0x65 0x00 0x61 0x00 0x76 0x00 0x69 0x00 0x73 0x00 0x00, but found: % x", str) - } - if err != nil { - t.Errorf("err: %v", err) - } + out, err := bmpString(test.in) + if err == nil && test.shouldFail { + t.Errorf("#%d: expected to fail, but produced %x", i, out) + continue + } - // some characters from the "Letterlike Symbols Unicode block" - tst := "\u2115 - Double-struck N" - str, err = bmpString(tst) - if !bytes.Equal(str, []byte{0x21, 0x15, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x75, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x20, 0x00, 0x4e, 0x00, 0x00}) { - t.Errorf("expected '%s' to return 0x21 0x15 0x00 0x20 0x00 0x2d 0x00 0x20 0x00 0x44 0x00 0x6f 0x00 0x75 0x00 0x62 0x00 0x6c 0x00 0x65 0x00 0x2d 0x00 0x73 0x00 0x74 0x00 0x72 0x00 0x75 0x00 0x63 0x00 0x6b 0x00 0x20 0x00 0x4e 0x00 0x00, but found: % x", tst, str) - } - if err != nil { - t.Errorf("err: %v", err) - } + if err != nil && !test.shouldFail { + t.Errorf("#%d: failed unexpectedly: %s", i, err) + continue + } - // some character outside the BMP should error - tst = "\U0001f000 East wind (Mahjong)" - _, err = bmpString(tst) - if err == nil { - t.Errorf("expected '%s' to throw error because the first character is not in the BMP", tst) + if !test.shouldFail { + if !bytes.Equal(out, expected) { + t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out) + continue + } + + roundTrip, err := decodeBMPString(out) + if err != nil { + t.Errorf("#%d: decoding output gave an error: %s", i, err) + continue + } + + if roundTrip != test.in { + t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in) + continue + } + } } } diff --git a/builder/azure/pkcs12/crypto.go b/builder/azure/pkcs12/crypto.go index 5095a4f7f..6c81eb774 100644 --- a/builder/azure/pkcs12/crypto.go +++ b/builder/azure/pkcs12/crypto.go @@ -1,4 +1,6 @@ -// implementation of https://tools.ietf.org/html/rfc2898#section-6.1.2 +// Copyright 2015 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 pkcs12 @@ -15,31 +17,52 @@ import ( "github.com/hashicorp/packer/builder/azure/pkcs12/rc2" ) -const ( - pbeWithSHAAnd3KeyTripleDESCBC = "pbeWithSHAAnd3-KeyTripleDES-CBC" - pbewithSHAAnd40BitRC2CBC = "pbewithSHAAnd40BitRC2-CBC" -) - const ( pbeIterationCount = 2048 pbeSaltSizeBytes = 8 ) var ( - oidPbeWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 3} - oidPbewithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 6} + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) ) -var algByOID = map[string]string{ - oidPbeWithSHAAnd3KeyTripleDESCBC.String(): pbeWithSHAAnd3KeyTripleDESCBC, - oidPbewithSHAAnd40BitRC2CBC.String(): pbewithSHAAnd40BitRC2CBC, +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte } -var blockcodeByAlg = map[string]func(key []byte) (cipher.Block, error){ - pbeWithSHAAnd3KeyTripleDESCBC: des.NewTripleDESCipher, - pbewithSHAAnd40BitRC2CBC: func(key []byte) (cipher.Block, error) { - return rc2.New(key, len(key)*8) - }, +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) } type pbeParams struct { @@ -47,6 +70,67 @@ type pbeParams struct { Iterations int } +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + default: + return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, 0, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + func pad(src []byte, blockSize int) []byte { paddingLength := blockSize - len(src)%blockSize paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength) @@ -54,15 +138,15 @@ func pad(src []byte, blockSize int) []byte { } func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []byte, err error) { - _, err = io.ReadFull(rand.Reader, salt) - if err != nil { + if _, err := io.ReadFull(rand.Reader, salt); err != nil { return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error()) } - key := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations) - iv := deriveIVByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations) + cipherType := shaWithTripleDESCBC{} + key := cipherType.deriveKey(salt, password, iterations) + iv := cipherType.deriveIV(salt, password, iterations) - block, err := des.NewTripleDESCipher(key) + block, err := cipherType.create(key) if err != nil { return nil, errors.New("pkcs12: failed to create a block cipher: " + err.Error()) } @@ -76,7 +160,8 @@ func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []b return cipherText, nil } +// decryptable abstracts a object that contains ciphertext. type decryptable interface { - GetAlgorithm() pkix.AlgorithmIdentifier - GetData() []byte + Algorithm() pkix.AlgorithmIdentifier + Data() []byte } diff --git a/builder/azure/pkcs12/crypto_test.go b/builder/azure/pkcs12/crypto_test.go index 46cbc21cd..efcd90820 100644 --- a/builder/azure/pkcs12/crypto_test.go +++ b/builder/azure/pkcs12/crypto_test.go @@ -1,60 +1,19 @@ +// Copyright 2015 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 pkcs12 import ( "bytes" - "crypto/cipher" + "crypto/rand" "crypto/x509/pkix" "encoding/asn1" + "io" "testing" ) -func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, error) { - algorithmName, supported := algByOID[algorithm.Algorithm.String()] - if !supported { - return nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") - } - - var params pbeParams - if _, err := asn1.Unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { - return nil, err - } - - k := deriveKeyByAlg[algorithmName](params.Salt, password, params.Iterations) - iv := deriveIVByAlg[algorithmName](params.Salt, password, params.Iterations) - - code, err := blockcodeByAlg[algorithmName](k) - if err != nil { - return nil, err - } - - cbc := cipher.NewCBCDecrypter(code, iv) - return cbc, nil -} - -func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { - cbc, err := pbDecrypterFor(info.GetAlgorithm(), password) - if err != nil { - return nil, err - } - - encrypted := info.GetData() - - decrypted = make([]byte, len(encrypted)) - cbc.CryptBlocks(decrypted, encrypted) - - if psLen := int(decrypted[len(decrypted)-1]); psLen > 0 && psLen <= cbc.BlockSize() { - m := decrypted[:len(decrypted)-psLen] - ps := decrypted[len(decrypted)-psLen:] - if !bytes.Equal(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) { - return nil, ErrDecryption - } - decrypted = m - } else { - return nil, ErrDecryption - } - - return -} +var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) func TestPbDecrypterFor(t *testing.T) { params, _ := asn1.Marshal(pbeParams{ @@ -70,79 +29,131 @@ func TestPbDecrypterFor(t *testing.T) { pass, _ := bmpString("Sesame open") - _, err := pbDecrypterFor(alg, pass) + _, _, err := pbDecrypterFor(alg, pass) if _, ok := err.(NotImplementedError); !ok { t.Errorf("expected not implemented error, got: %T %s", err, err) } - alg.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) - cbc, err := pbDecrypterFor(alg, pass) + alg.Algorithm = sha1WithTripleDES + cbc, blockSize, err := pbDecrypterFor(alg, pass) if err != nil { - t.Errorf("err: %v", err) + t.Errorf("unexpected error from pbDecrypterFor %v", err) + } + if blockSize != 8 { + t.Errorf("unexpected block size %d, wanted 8", blockSize) } - M := []byte{1, 2, 3, 4, 5, 6, 7, 8} - expectedM := []byte{185, 73, 135, 249, 137, 1, 122, 247} - cbc.CryptBlocks(M, M) + plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8} + expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247} + ciphertext := make([]byte, len(plaintext)) + cbc.CryptBlocks(ciphertext, plaintext) - if !bytes.Equal(M, expectedM) { - t.Errorf("expected M to be '%d', but found '%d", expectedM, M) + if bytes.Compare(ciphertext, expectedCiphertext) != 0 { + t.Errorf("bad ciphertext, got %x but wanted %x", ciphertext, expectedCiphertext) } } -func TestPbDecrypt(t *testing.T) { - - tests := [][]byte{ +var pbDecryptTests = []struct { + in []byte + expected []byte + expectedError error +}{ + { []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes - []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes - []byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect - []byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] - } - expected := []interface{}{ []byte("A secret!"), + nil, + }, + { + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes []byte("A secret"), + nil, + }, + { + []byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect + nil, ErrDecryption, + }, + { + []byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] + nil, ErrDecryption, - } + }, +} - for i, c := range tests { - td := testDecryptable{ - data: c, - algorithm: pkix.AlgorithmIdentifier{ - Algorithm: asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}), // SHA1/3TDES - Parameters: pbeParams{ - Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"), - Iterations: 4096, - }.RawASN1(), - }, +func TestPbDecrypt(t *testing.T) { + salt := []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8") + + for i, test := range pbDecryptTests { + decryptable := makeTestDecryptable(test.in, salt) + password, _ := bmpString("sesame") + + plaintext, err := pbDecrypt(decryptable, password) + if err != test.expectedError { + t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError) + continue } - p, _ := bmpString("sesame") - m, err := pbDecrypt(td, p) - - switch e := expected[i].(type) { - case []byte: - if err != nil { - t.Errorf("error decrypting C=%x: %v", c, err) - } - if !bytes.Equal(m, e) { - t.Errorf("expected C=%x to be decoded to M=%x, but found %x", c, e, m) - } - case error: - if err == nil || err.Error() != e.Error() { - t.Errorf("expecting error '%v' during decryption of c=%x, but found err='%v'", e, c, err) - } + if !bytes.Equal(plaintext, test.expected) { + t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected) } } } +func TestRoundTripPkc12EncryptDecrypt(t *testing.T) { + salt := []byte{0xfe, 0xee, 0xfa, 0xce} + password := salt + + // Sweep the possible padding lengths + for i := 0; i < 9; i++ { + bs := make([]byte, i) + _, err := io.ReadFull(rand.Reader, bs) + if err != nil { + t.Fatalf("failed to read: %s", err) + } + + cipherText, err := pbEncrypt(bs, salt, password, 4096) + if err != nil { + t.Fatalf("failed to encrypt: %s\n", err) + } + + if len(cipherText)%8 != 0 { + t.Fatalf("plain text was not padded as expected") + } + + decryptable := makeTestDecryptable(cipherText, salt) + plainText, err := pbDecrypt(decryptable, password) + if err != nil { + t.Fatalf("failed to decrypt: %s\n", err) + } + + if !bytes.Equal(bs, plainText) { + t.Fatalf("got %x, but wanted %x", bs, plainText) + } + } +} + +func makeTestDecryptable(bytes, salt []byte) testDecryptable { + decryptable := testDecryptable{ + data: bytes, + algorithm: pkix.AlgorithmIdentifier{ + Algorithm: sha1WithTripleDES, + Parameters: pbeParams{ + Salt: salt, + Iterations: 4096, + }.RawASN1(), + }, + } + + return decryptable +} + type testDecryptable struct { data []byte algorithm pkix.AlgorithmIdentifier } -func (d testDecryptable) GetAlgorithm() pkix.AlgorithmIdentifier { return d.algorithm } -func (d testDecryptable) GetData() []byte { return d.data } +func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm } +func (d testDecryptable) Data() []byte { return d.data } func (params pbeParams) RawASN1() (raw asn1.RawValue) { asn1Bytes, err := asn1.Marshal(params) diff --git a/builder/azure/pkcs12/errors.go b/builder/azure/pkcs12/errors.go index 64a9433ef..36ad6e957 100644 --- a/builder/azure/pkcs12/errors.go +++ b/builder/azure/pkcs12/errors.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 pkcs12 import "errors" @@ -16,7 +20,7 @@ type NotImplementedError string type EncodeError string func (e NotImplementedError) Error() string { - return string(e) + return "pkcs12: " + string(e) } func (e EncodeError) Error() string { diff --git a/builder/azure/pkcs12/mac.go b/builder/azure/pkcs12/mac.go index c7e428117..76ad0cdc5 100644 --- a/builder/azure/pkcs12/mac.go +++ b/builder/azure/pkcs12/mac.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 pkcs12 import ( @@ -7,10 +11,6 @@ import ( "encoding/asn1" ) -var ( - oidSha1Algorithm = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} -) - type macData struct { Mac digestInfo MacSalt []byte @@ -23,6 +23,23 @@ type digestInfo struct { Digest []byte } +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) +) + +func verifyMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + expectedMAC := computeMac(message, macData.Iterations, macData.MacSalt, password) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} + func computeMac(message []byte, iterations int, salt, password []byte) []byte { key := pbkdf(sha1Sum, 20, 64, salt, password, iterations, 3, 20) diff --git a/builder/azure/pkcs12/mac_test.go b/builder/azure/pkcs12/mac_test.go index 0ccea7104..1ed4ff21e 100644 --- a/builder/azure/pkcs12/mac_test.go +++ b/builder/azure/pkcs12/mac_test.go @@ -1,24 +1,14 @@ +// Copyright 2015 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 pkcs12 import ( - "crypto/hmac" "encoding/asn1" "testing" ) -func verifyMac(macData *macData, message, password []byte) error { - if !macData.Mac.Algorithm.Algorithm.Equal(oidSha1Algorithm) { - return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) - } - - expectedMAC := computeMac(message, macData.Iterations, macData.MacSalt, password) - - if !hmac.Equal(macData.Mac.Digest, expectedMAC) { - return ErrIncorrectPassword - } - return nil -} - func TestVerifyMac(t *testing.T) { td := macData{ Mac: digestInfo{ diff --git a/builder/azure/pkcs12/pbkdf.go b/builder/azure/pkcs12/pbkdf.go index ea789566b..5c419d41e 100644 --- a/builder/azure/pkcs12/pbkdf.go +++ b/builder/azure/pkcs12/pbkdf.go @@ -1,34 +1,35 @@ +// Copyright 2015 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 pkcs12 import ( + "bytes" "crypto/sha1" "math/big" ) var ( - deriveKeyByAlg = map[string]func(salt, password []byte, iterations int) []byte{ - pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) - }, - pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) - }, - } - deriveIVByAlg = map[string]func(salt, password []byte, iterations int) []byte{ - pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) - }, - pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte { - return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) - }, - } + one = big.NewInt(1) ) +// sha1Sum returns the SHA-1 hash of in. func sha1Sum(in []byte) []byte { sum := sha1.Sum(in) return sum[:] } +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments @@ -75,7 +76,7 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID // 1. Construct a string, D (the "diversifier"), by concatenating v/8 // copies of ID. - D := []byte{} + var D []byte for i := 0; i < v; i++ { D = append(D, ID) } @@ -85,86 +86,71 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID // truncated to create S). Note that if the salt is the empty // string, then so is S. - S := []byte{} - { - s := len(salt) - times := s / v - if s%v > 0 { - times++ - } - for len(S) < times*v { - S = append(S, salt...) - } - S = S[:times*v] - } + S := fillWithRepeats(salt, v) // 3. Concatenate copies of the password together to create a string P // of length v(ceiling(p/v)) bits (the final copy of the password // may be truncated to create P). Note that if the password is the // empty string, then so is P. - P := []byte{} - { - s := len(password) - times := s / v - if s%v > 0 { - times++ - } - for len(P) < times*v { - P = append(P, password...) - } - P = P[:times*v] - } + P := fillWithRepeats(password, v) // 4. Set I=S||P to be the concatenation of S and P. I := append(S, P...) // 5. Set c=ceiling(n/u). - c := size / u - if size%u > 0 { - c++ - } + c := (size + u - 1) / u // 6. For i=1, 2, ..., c, do the following: A := make([]byte, c*20) + var IjBuf []byte for i := 0; i < c; i++ { - // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, // H(H(H(... H(D||I)))) Ai := hash(append(D, I...)) for j := 1; j < r; j++ { - Ai = hash(Ai[:]) + Ai = hash(Ai) } copy(A[i*20:], Ai[:]) if i < c-1 { // skip on last iteration - - // B. Concatenate copies of Ai to create a string B of length v - // bits (the final copy of Ai may be truncated to create B). - B := []byte{} + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte for len(B) < v { B = append(B, Ai[:]...) } B = B[:v] - // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit - // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by - // setting I_j=(I_j+B+1) mod 2^v for each j. + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. { - Bbi := new(big.Int) - Bbi.SetBytes(B) - - one := big.NewInt(1) + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) for j := 0; j < len(I)/v; j++ { - Ij := new(big.Int) Ij.SetBytes(I[j*v : (j+1)*v]) Ij.Add(Ij, Bbi) Ij.Add(Ij, one) Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. if len(Ijb) > v { Ijb = Ijb[len(Ijb)-v:] } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } copy(I[j*v:(j+1)*v], Ijb) } } @@ -174,9 +160,7 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID // bit string, A. // 8. Use the first n bits of A as the output of this entire process. - A = A[:size] - - return A + return A[:size] // If the above process is being used to generate a DES key, the process // should be used to create 64 random bits, and the key's parity bits diff --git a/builder/azure/pkcs12/pbkdf_test.go b/builder/azure/pkcs12/pbkdf_test.go index cd66e059e..262037d7e 100644 --- a/builder/azure/pkcs12/pbkdf_test.go +++ b/builder/azure/pkcs12/pbkdf_test.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 pkcs12 import ( @@ -6,13 +10,25 @@ import ( ) func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { - pbkdf := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC] + cipherInfo := shaWithTripleDESCBC{} salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") password, _ := bmpString("sesame") - key := pbkdf(salt, password, 2048) + key := cipherInfo.deriveKey(salt, password, 2048) - if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); !bytes.Equal(key, expected) { - t.Fatalf("expected key '% x', but found '% x'", key, expected) + if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) + } +} + +func TestThatPBKDFHandlesLeadingZeros(t *testing.T) { + // This test triggers a case where I_j (in step 6C) ends up with leading zero + // byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int). + // This was previously causing bug whereby certain inputs would break the + // derivation and produce the wrong output. + key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24) + expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1") + if bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) } } diff --git a/builder/azure/pkcs12/pkcs12.go b/builder/azure/pkcs12/pkcs12.go index 7db1a2e05..8279cb572 100644 --- a/builder/azure/pkcs12/pkcs12.go +++ b/builder/azure/pkcs12/pkcs12.go @@ -1,20 +1,34 @@ -// Package pkcs12 provides some implementations of PKCS#12. +// Copyright 2015 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 pkcs12 implements some of PKCS#12. // -// This implementation is distilled from https://tools.ietf.org/html/rfc7292 and referenced documents. -// It is intended for decoding P12/PFX-stored certificate+key for use with the crypto/tls package. +// This implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. It is intended for decoding P12/PFX-stored +// certificates and keys for use with the crypto/tls package. package pkcs12 import ( + "crypto/ecdsa" "crypto/rand" + "crypto/rsa" + "crypto/x509" "crypto/x509/pkix" "encoding/asn1" + "encoding/hex" + "encoding/pem" "errors" "io" ) var ( - oidLocalKeyID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 21} - oidDataContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) localKeyId = []byte{0x01, 0x00, 0x00, 0x00} ) @@ -30,6 +44,23 @@ type contentInfo struct { Content asn1.RawValue `asn1:"tag:0,explicit,optional"` } +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + type safeBag struct { Id asn1.ObjectIdentifier Value asn1.RawValue `asn1:"tag:0,explicit"` @@ -38,7 +69,7 @@ type safeBag struct { type pkcs12Attribute struct { Id asn1.ObjectIdentifier - Value asn1.RawValue `ans1:"set"` + Value asn1.RawValue `asn1:"set"` } type encryptedPrivateKeyInfo struct { @@ -46,8 +77,19 @@ type encryptedPrivateKeyInfo struct { EncryptedData []byte } -func (i encryptedPrivateKeyInfo) GetAlgorithm() pkix.AlgorithmIdentifier { return i.AlgorithmIdentifier } -func (i encryptedPrivateKeyInfo) GetData() []byte { return i.EncryptedData } +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) // unmarshal calls asn1.Unmarshal, but also returns an error if there is any // trailing data after unmarshaling. @@ -62,6 +104,168 @@ func unmarshal(in []byte, out interface{}) error { return nil } +// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroudedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData. This function +// assumes that there is only one certificate and only one private key in the +// pfxData. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, err + } + + if len(bags) != 2 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + return + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + if certificate != nil { + err = errors.New("pkcs12: expected exactly one certificate bag") + } + + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, err + } + certificate = certs[0] + + case bag.Id.Equal(oidPKCS8ShroudedKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + } + if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { + return nil, nil, err + } + } + } + + if certificate == nil { + return nil, nil, errors.New("pkcs12: certificate missing") + } + if privateKey == nil { + return nil, nil, errors.New("pkcs12: private key missing") + } + + return +} + func getLocalKeyId(id []byte) (attribute pkcs12Attribute, err error) { octetString := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: id} bytes, err := asn1.Marshal(octetString) @@ -115,7 +319,7 @@ func makeCertBagContentInfo(derBytes []byte) (*contentInfo, error) { return nil, EncodeError("encoding cert bag: " + err.Error()) } - certSafeBags, err := makeSafeBags(oidCertBagType, bytes) + certSafeBags, err := makeSafeBags(oidCertBag, bytes) if err != nil { return nil, EncodeError("safe bags: " + err.Error()) } @@ -129,7 +333,7 @@ func makeShroudedKeyBagContentInfo(privateKey interface{}, password []byte) (*co return nil, EncodeError("encode PKCS#8 shrouded key bag: " + err.Error()) } - safeBags, err := makeSafeBags(oidPkcs8ShroudedKeyBagType, shroudedKeyBagBytes) + safeBags, err := makeSafeBags(oidPKCS8ShroudedKeyBag, shroudedKeyBagBytes) if err != nil { return nil, EncodeError("safe bags: " + err.Error()) } @@ -183,7 +387,7 @@ func makeSalt(saltByteCount int) ([]byte, error) { // // derBytes is a DER encoded certificate. // privateKey is an RSA -func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, error) { +func Encode(derBytes []byte, privateKey interface{}, password string) (pfxBytes []byte, err error) { secret, err := bmpString(password) if err != nil { return nil, ErrIncorrectPassword @@ -230,7 +434,7 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e MacSalt: salt, Mac: digestInfo{ Algorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidSha1Algorithm, + Algorithm: oidSHA1, }, Digest: digest, }, @@ -244,3 +448,83 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e return bytes, err } + +func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != 2 { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + + bags = append(bags, safeContents...) + } + + return bags, password, nil +} diff --git a/builder/azure/pkcs12/pkcs12_test.go b/builder/azure/pkcs12/pkcs12_test.go index 2cfbc44d8..2ac382a0b 100644 --- a/builder/azure/pkcs12/pkcs12_test.go +++ b/builder/azure/pkcs12/pkcs12_test.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 pkcs12 import ( @@ -5,16 +9,36 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/rsa" + "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" "fmt" "math/big" "testing" "time" - - gopkcs12 "golang.org/x/crypto/pkcs12" ) +func TestPfx(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + priv, cert, err := Decode(p12, "") + if err != nil { + t.Fatal(err) + } + + if err := priv.(*rsa.PrivateKey).Validate(); err != nil { + t.Errorf("error while validating private key: %v", err) + } + + if cert.Subject.CommonName != commonName { + t.Errorf("expected common name to be %q, but found %q", commonName, cert.Subject.CommonName) + } + } +} + func TestPfxRoundTriRsa(t *testing.T) { privateKey, err := rsa.GenerateKey(rand.Reader, 512) if err != nil { @@ -25,7 +49,7 @@ func TestPfxRoundTriRsa(t *testing.T) { actualPrivateKey, ok := key.(*rsa.PrivateKey) if !ok { - t.Fatal("failed to decode private key") + t.Fatalf("failed to decode private key") } if privateKey.D.Cmp(actualPrivateKey.D) != 0 { @@ -62,7 +86,7 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} { t.Fatal(err.Error()) } - key, _, err := gopkcs12.Decode(bytes, "sesame") + key, _, err := Decode(bytes, "sesame") if err != nil { t.Fatalf(err.Error()) } @@ -70,6 +94,61 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} { return key } +func TestPEM(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + blocks, err := ToPEM(p12, "") + if err != nil { + t.Fatalf("error while converting to PEM: %s", err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + t.Errorf("err while converting to key pair: %v", err) + } + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + } + config.BuildNameToCertificate() + + if _, exists := config.NameToCertificate[commonName]; !exists { + t.Errorf("did not find our cert in PEM?: %v", config.NameToCertificate) + } + } +} + +func ExampleToPEM() { + p12, _ := base64.StdEncoding.DecodeString(`MIIJzgIBAzCCCZQGCS ... CA+gwggPk==`) + + blocks, err := ToPEM(p12, "password") + if err != nil { + panic(err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + // then use PEM data for tls to construct tls certificate: + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + panic(err) + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + _ = config +} + func newCertificate(hostname string, privateKey interface{}) ([]byte, error) { t, _ := time.Parse("2006-01-02", "2016-01-01") notBefore := t @@ -115,3 +194,54 @@ func newCertificate(hostname string, privateKey interface{}) ([]byte, error) { return derBytes, nil } + +var testdata = map[string]string{ + // 'null' password test case + "Windows Azure Tools": `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`, + // empty string password test case + "testing@example.com": `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 +HQ8CAggA`, +} diff --git a/builder/azure/pkcs12/pkcs8.go b/builder/azure/pkcs12/pkcs8.go index 5203d394f..22ab14d25 100644 --- a/builder/azure/pkcs12/pkcs8.go +++ b/builder/azure/pkcs12/pkcs8.go @@ -1,3 +1,6 @@ +// Copyright 2016 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 pkcs12 import ( @@ -28,7 +31,7 @@ var ( // marshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. // See http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. -func marshalPKCS8PrivateKey(key interface{}) ([]byte, error) { +func marshalPKCS8PrivateKey(key interface{}) (der []byte, err error) { pkcs := pkcs8{ Version: 0, } diff --git a/builder/azure/pkcs12/pkcs8_test.go b/builder/azure/pkcs12/pkcs8_test.go index 1f891ef80..7d12119f5 100644 --- a/builder/azure/pkcs12/pkcs8_test.go +++ b/builder/azure/pkcs12/pkcs8_test.go @@ -35,7 +35,7 @@ func TestRoundTripPkcs8Rsa(t *testing.T) { } if actualPrivateKey.Validate() != nil { - t.Fatal("private key did not validate") + t.Fatalf("private key did not validate") } if actualPrivateKey.N.Cmp(privateKey.N) != 0 { diff --git a/builder/azure/pkcs12/rc2/bench_test.go b/builder/azure/pkcs12/rc2/bench_test.go new file mode 100644 index 000000000..3347f338c --- /dev/null +++ b/builder/azure/pkcs12/rc2/bench_test.go @@ -0,0 +1,27 @@ +// Copyright 2015 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 rc2 + +import ( + "testing" +) + +func BenchmarkEncrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Encrypt(src[:], src[:]) + } +} + +func BenchmarkDecrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Decrypt(src[:], src[:]) + } +} diff --git a/builder/azure/pkcs12/rc2/rc2.go b/builder/azure/pkcs12/rc2/rc2.go index aa194e501..8c7090258 100644 --- a/builder/azure/pkcs12/rc2/rc2.go +++ b/builder/azure/pkcs12/rc2/rc2.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 rc2 implements the RC2 cipher /* https://www.ietf.org/rfc/rfc2268.txt @@ -10,7 +14,6 @@ package rc2 import ( "crypto/cipher" "encoding/binary" - "strconv" ) // The rc2 block size in bytes @@ -20,34 +23,15 @@ type rc2Cipher struct { k [64]uint16 } -// KeySizeError indicates the supplied key was invalid -type KeySizeError int - -func (k KeySizeError) Error() string { return "rc2: invalid key size " + strconv.Itoa(int(k)) } - -// EffectiveKeySizeError indicates the supplied effective key length was invalid -type EffectiveKeySizeError int - -func (k EffectiveKeySizeError) Error() string { - return "rc2: invalid effective key size " + strconv.Itoa(int(k)) -} - // New returns a new rc2 cipher with the given key and effective key length t1 func New(key []byte, t1 int) (cipher.Block, error) { - if l := len(key); l == 0 || l > 128 { - return nil, KeySizeError(l) - } - - if t1 < 8 || t1 > 1024 { - return nil, EffectiveKeySizeError(t1) - } - + // TODO(dgryski): error checking for key length return &rc2Cipher{ k: expandKey(key, t1), }, nil } -func (c *rc2Cipher) BlockSize() int { return BlockSize } +func (*rc2Cipher) BlockSize() int { return BlockSize } var piTable = [256]byte{ 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, @@ -109,7 +93,6 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) { var j int - // These three mix blocks have not been extracted to a common function for to performance reasons. for j <= 16 { // mix r0 r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) @@ -130,6 +113,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) { r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) r3 = rotl16(r3, 5) j++ + } r0 = r0 + c.k[r3&63] @@ -138,6 +122,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) { r3 = r3 + c.k[r2&63] for j <= 40 { + // mix r0 r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) r0 = rotl16(r0, 1) @@ -157,6 +142,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) { r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) r3 = rotl16(r3, 5) j++ + } r0 = r0 + c.k[r3&63] @@ -165,6 +151,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) { r3 = r3 + c.k[r2&63] for j <= 60 { + // mix r0 r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) r0 = rotl16(r0, 1) @@ -248,6 +235,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) { r0 = rotl16(r0, 16-1) r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) j-- + } r3 = r3 - c.k[r2&63] @@ -256,6 +244,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) { r0 = r0 - c.k[r3&63] for j >= 0 { + // unmix r3 r3 = rotl16(r3, 16-5) r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) @@ -275,6 +264,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) { r0 = rotl16(r0, 16-1) r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) j-- + } binary.LittleEndian.PutUint16(dst[0:], r0) diff --git a/builder/azure/pkcs12/rc2/rc2_test.go b/builder/azure/pkcs12/rc2/rc2_test.go index adb73c592..8a49dfaf3 100644 --- a/builder/azure/pkcs12/rc2/rc2_test.go +++ b/builder/azure/pkcs12/rc2/rc2_test.go @@ -1,3 +1,7 @@ +// Copyright 2015 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 rc2 import ( @@ -8,6 +12,7 @@ import ( func TestEncryptDecrypt(t *testing.T) { + // TODO(dgryski): add the rest of the test vectors from the RFC var tests = []struct { key string plain string @@ -86,20 +91,3 @@ func TestEncryptDecrypt(t *testing.T) { } } } - -func BenchmarkEncrypt(b *testing.B) { - r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) - b.ResetTimer() - var src [8]byte - for i := 0; i < b.N; i++ { - r.Encrypt(src[:], src[:]) - } -} -func BenchmarkDecrypt(b *testing.B) { - r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) - b.ResetTimer() - var src [8]byte - for i := 0; i < b.N; i++ { - r.Decrypt(src[:], src[:]) - } -} diff --git a/builder/azure/pkcs12/safebags.go b/builder/azure/pkcs12/safebags.go index 5d4793b3e..bbd1526b8 100644 --- a/builder/azure/pkcs12/safebags.go +++ b/builder/azure/pkcs12/safebags.go @@ -1,17 +1,21 @@ +// Copyright 2015 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 pkcs12 import ( + "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" ) -//see https://tools.ietf.org/html/rfc7292#appendix-D var ( - oidPkcs8ShroudedKeyBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 2} - oidCertBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 3} - - oidCertTypeX509Certificate = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 22, 1} + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroudedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) ) type certBag struct { @@ -52,7 +56,7 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [ pkinfo := encryptedPrivateKeyInfo{ AlgorithmIdentifier: pkix.AlgorithmIdentifier{ - Algorithm: oidPbeWithSHAAnd3KeyTripleDESCBC, + Algorithm: oidPBEWithSHAAnd3KeyTripleDESCBC, Parameters: params, }, EncryptedData: pkData, @@ -65,3 +69,37 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [ return bytes, err } + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} diff --git a/builder/azure/pkcs12/safebags_test.go b/builder/azure/pkcs12/safebags_test.go index 384dd5ce3..f21a83c68 100644 --- a/builder/azure/pkcs12/safebags_test.go +++ b/builder/azure/pkcs12/safebags_test.go @@ -1,39 +1,15 @@ +// Copyright 2016 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 pkcs12 import ( "crypto/rand" "crypto/rsa" - "crypto/x509" "encoding/asn1" - "fmt" "testing" ) -func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { - pkinfo := new(encryptedPrivateKeyInfo) - if _, err = asn1.Unmarshal(asn1Data, pkinfo); err != nil { - err = fmt.Errorf("error decoding PKCS8 shrouded key bag: %v", err) - return nil, err - } - - pkData, err := pbDecrypt(pkinfo, password) - if err != nil { - err = fmt.Errorf("error decrypting PKCS8 shrouded key bag: %v", err) - return - } - - rv := new(asn1.RawValue) - if _, err = asn1.Unmarshal(pkData, rv); err != nil { - err = fmt.Errorf("could not decode decrypted private key data") - } - - if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { - err = fmt.Errorf("error parsing PKCS8 private key: %v", err) - return nil, err - } - return -} - // Assert the default algorithm parameters are in the correct order, // and default to the correct value. Defaults are based on OpenSSL. // 1. IterationCount, defaults to 2,048 long. @@ -61,7 +37,7 @@ func TestDefaultAlgorithmParametersPkcs8ShroudedKeyBag(t *testing.T) { } var params pbeParams - rest, err = asn1.Unmarshal(pkinfo.GetAlgorithm().Parameters.FullBytes, ¶ms) + rest, err = asn1.Unmarshal(pkinfo.Algorithm().Parameters.FullBytes, ¶ms) if err != nil { t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err) } @@ -97,6 +73,6 @@ func TestRoundTripPkcs8ShroudedKeyBag(t *testing.T) { actualPrivateKey := key.(*rsa.PrivateKey) if actualPrivateKey.D.Cmp(privateKey.D) != 0 { - t.Fatal("failed to round-trip rsa.PrivateKey.D") + t.Fatalf("failed to round-trip rsa.PrivateKey.D") } }