From ce8094fb6c34c09c27ba06279d82d968808dd3f1 Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Thu, 13 Feb 2025 10:54:18 -0500 Subject: [PATCH] Add underlining support for the PKI Enterprise SCEP work (#29604) --- builtin/logical/pki/cert_util.go | 51 +----- builtin/logical/pki/issuing/signing_utils.go | 162 +++++++++++++++++++ builtin/logical/pki/parsing/asn1.go | 18 +++ builtin/logical/pki/parsing/asn1_test.go | 52 ++++++ helper/pkcs7/decrypt.go | 39 ++++- helper/pkcs7/encrypt.go | 97 ++++++++--- helper/pkcs7/encrypt_test.go | 10 ++ helper/pkcs7/pkcs7.go | 4 + 8 files changed, 356 insertions(+), 77 deletions(-) create mode 100644 builtin/logical/pki/issuing/signing_utils.go create mode 100644 builtin/logical/pki/parsing/asn1.go create mode 100644 builtin/logical/pki/parsing/asn1_test.go diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 8804682122..eef8590168 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -62,59 +62,10 @@ var ( endWildRegex = labelRegex + `\*` middleWildRegex = labelRegex + `\*` + labelRegex leftWildLabelRegex = regexp.MustCompile(`^(` + allWildRegex + `|` + startWildRegex + `|` + endWildRegex + `|` + middleWildRegex + `)$`) - - // Cloned from https://github.com/golang/go/blob/82c713feb05da594567631972082af2fcba0ee4f/src/crypto/x509/x509.go#L327-L379 - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} - oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} - oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112} - oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} - - signatureAlgorithmDetails = []struct { - algo x509.SignatureAlgorithm - name string - oid asn1.ObjectIdentifier - pubKeyAlgo x509.PublicKeyAlgorithm - hash crypto.Hash - }{ - {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, - {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, - {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, - {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, - {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, - {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, - {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, - {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, - {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, - {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, - {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, - {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, - {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, - {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, - {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, - {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, - {x509.PureEd25519, "Ed25519", oidSignatureEd25519, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */}, - } ) func doesPublicKeyAlgoMatchSignatureAlgo(pubKey x509.PublicKeyAlgorithm, algo x509.SignatureAlgorithm) bool { - for _, detail := range signatureAlgorithmDetails { - if detail.algo == algo { - return pubKey == detail.pubKeyAlgo - } - } - - return false + return issuing.DoesPublicKeyAlgoMatchSignatureAlgo(pubKey, algo) } func getFormat(data *framework.FieldData) string { diff --git a/builtin/logical/pki/issuing/signing_utils.go b/builtin/logical/pki/issuing/signing_utils.go new file mode 100644 index 0000000000..09efbd8fdd --- /dev/null +++ b/builtin/logical/pki/issuing/signing_utils.go @@ -0,0 +1,162 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package issuing + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" +) + +// DER encoded RSA PSS parameters for the +// SHA256, SHA384, and SHA512 hashes as defined in RFC 3447, Appendix A.2.3. +// The parameters contain the following values: +// - hashAlgorithm contains the associated hash identifier with NULL parameters +// - maskGenAlgorithm always contains the default mgf1SHA1 identifier +// - saltLength contains the length of the associated hash +// - trailerField always contains the default trailerFieldBC value +var ( + pssParametersSHA256 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 162, 3, 2, 1, 32}} + pssParametersSHA384 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 162, 3, 2, 1, 48}} + pssParametersSHA512 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 162, 3, 2, 1, 64}} +) + +var ( + emptyRawValue = asn1.RawValue{} + + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112} + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} + + signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + params asn1.RawValue + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash + isRSAPSS bool + }{ + {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, asn1.NullRawValue, x509.RSA, crypto.MD5, false}, + {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA1, false}, + {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA1, false}, + {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA256, false}, + {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA384, false}, + {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA512, false}, + {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, pssParametersSHA256, x509.RSA, crypto.SHA256, true}, + {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, pssParametersSHA384, x509.RSA, crypto.SHA384, true}, + {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, pssParametersSHA512, x509.RSA, crypto.SHA512, true}, + {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, emptyRawValue, x509.DSA, crypto.SHA1, false}, + {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, emptyRawValue, x509.DSA, crypto.SHA256, false}, + {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, emptyRawValue, x509.ECDSA, crypto.SHA1, false}, + {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, emptyRawValue, x509.ECDSA, crypto.SHA256, false}, + {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, emptyRawValue, x509.ECDSA, crypto.SHA384, false}, + {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, emptyRawValue, x509.ECDSA, crypto.SHA512, false}, + {x509.PureEd25519, "Ed25519", oidSignatureEd25519, emptyRawValue, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */, false}, + } +) + +// SigningParamsForKey returns the signature algorithm and its Algorithm +// Identifier to use for signing, based on the key type. If sigAlgo is not zero +// then it overrides the default. +// This function is copied from crypto/x509/x509.go in the Go standard library. +func SigningParamsForKey(key crypto.Signer, sigAlgo x509.SignatureAlgorithm) (x509.SignatureAlgorithm, pkix.AlgorithmIdentifier, error) { + var ai pkix.AlgorithmIdentifier + var pubType x509.PublicKeyAlgorithm + var defaultAlgo x509.SignatureAlgorithm + + switch pub := key.Public().(type) { + case *rsa.PublicKey: + pubType = x509.RSA + defaultAlgo = x509.SHA256WithRSA + + case *ecdsa.PublicKey: + pubType = x509.ECDSA + switch pub.Curve { + case elliptic.P224(), elliptic.P256(): + defaultAlgo = x509.ECDSAWithSHA256 + case elliptic.P384(): + defaultAlgo = x509.ECDSAWithSHA384 + case elliptic.P521(): + defaultAlgo = x509.ECDSAWithSHA512 + default: + return 0, ai, errors.New("x509: unsupported elliptic curve") + } + + case ed25519.PublicKey: + pubType = x509.Ed25519 + defaultAlgo = x509.PureEd25519 + + default: + return 0, ai, errors.New("x509: only RSA, ECDSA and Ed25519 keys supported") + } + + if sigAlgo == 0 { + sigAlgo = defaultAlgo + } + + for _, details := range signatureAlgorithmDetails { + if details.algo == sigAlgo { + if details.pubKeyAlgo != pubType { + return 0, ai, errors.New("x509: requested SignatureAlgorithm does not match private key type") + } + if details.hash == crypto.MD5 { + return 0, ai, errors.New("x509: signing with MD5 is not supported") + } + + return sigAlgo, pkix.AlgorithmIdentifier{ + Algorithm: details.oid, + Parameters: details.params, + }, nil + } + } + + return 0, ai, errors.New("x509: unknown SignatureAlgorithm") +} + +func DoesPublicKeyAlgoMatchSignatureAlgo(pubKey x509.PublicKeyAlgorithm, algo x509.SignatureAlgorithm) bool { + for _, detail := range signatureAlgorithmDetails { + if detail.algo == algo { + return pubKey == detail.pubKeyAlgo + } + } + + return false +} + +func IsRSAPSS(algo x509.SignatureAlgorithm) bool { + for _, details := range signatureAlgorithmDetails { + if details.algo == algo { + return details.isRSAPSS + } + } + return false +} + +func HashFunc(algo x509.SignatureAlgorithm) crypto.Hash { + for _, details := range signatureAlgorithmDetails { + if details.algo == algo { + return details.hash + } + } + return crypto.Hash(0) +} diff --git a/builtin/logical/pki/parsing/asn1.go b/builtin/logical/pki/parsing/asn1.go new file mode 100644 index 0000000000..e972e6b31e --- /dev/null +++ b/builtin/logical/pki/parsing/asn1.go @@ -0,0 +1,18 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package parsing + +import "encoding/asn1" + +// Asn1UnmarshallNoTrailing is a wrapper around asn1.Unmarshal that ensures there +// is no trailing data in the input returning an error if there is. +func Asn1UnmarshallNoTrailing(b []byte, val any) error { + rest, err := asn1.Unmarshal(b, val) + if err != nil { + return err + } else if len(rest) != 0 { + return asn1.SyntaxError{Msg: "trailing data"} + } + return nil +} diff --git a/builtin/logical/pki/parsing/asn1_test.go b/builtin/logical/pki/parsing/asn1_test.go new file mode 100644 index 0000000000..5f6acaa442 --- /dev/null +++ b/builtin/logical/pki/parsing/asn1_test.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package parsing + +import ( + "testing" + + "github.com/google/certificate-transparency-go/asn1" + "github.com/stretchr/testify/require" +) + +// TestAsn1UnmarshallNoTrailing tests the Asn1UnmarshallNoTrailing function returns +// errors as we expect if the input is not marshalled correctly or there is trailing +// data. +func TestAsn1UnmarshallNoTrailing(t *testing.T) { + stringToMarshal := "a string" + marshal, err := asn1.Marshal(stringToMarshal) + require.NoError(t, err, "marshal failed") + + var myTestString string + + type args struct { + b []byte + val any + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"happy-path", args{marshal, &myTestString}, false}, + {"bad-marshalling", args{[]byte("incorrect"), &myTestString}, true}, + {"trailing-data", args{append(marshal, []byte("\n")...), &myTestString}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + myTestString = "" + err := Asn1UnmarshallNoTrailing(tt.args.b, tt.args.val) + + if (err != nil) != tt.wantErr { + t.Errorf("Asn1UnmarshallNoTrailing() error = %v, wantErr %v", err, tt.wantErr) + } + + if !tt.wantErr { + if myTestString != stringToMarshal { + t.Errorf("Asn1UnmarshallNoTrailing() = %v, want %v", myTestString, stringToMarshal) + } + } + }) + } +} diff --git a/helper/pkcs7/decrypt.go b/helper/pkcs7/decrypt.go index acedb1ec92..b88cd922bb 100644 --- a/helper/pkcs7/decrypt.go +++ b/helper/pkcs7/decrypt.go @@ -52,15 +52,40 @@ func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { return data.EncryptedContentInfo.decrypt(key) } +func (p7 *PKCS7) GetEncryptionAlgo() (int, error) { + data, ok := p7.raw.(envelopedData) + if !ok { + return -1, ErrNotEncryptedContent + } + + alg := data.EncryptedContentInfo.ContentEncryptionAlgorithm.Algorithm + return encryptedAlgoOidToConstant(alg) +} + +// convert the Encrypted Algorithm OID to our internal constants for Encryption Algorithms +func encryptedAlgoOidToConstant(alg asn1.ObjectIdentifier) (int, error) { + switch { + case alg.Equal(OIDEncryptionAlgorithmDESCBC): + return EncryptionAlgorithmDESCBC, nil + case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): + return EncryptionAlgorithmDESEDE3CBC, nil + case alg.Equal(OIDEncryptionAlgorithmAES128CBC): + return EncryptionAlgorithmAES128CBC, nil + case alg.Equal(OIDEncryptionAlgorithmAES256CBC): + return EncryptionAlgorithmAES256CBC, nil + case alg.Equal(OIDEncryptionAlgorithmAES128GCM): + return EncryptionAlgorithmAES128GCM, nil + case alg.Equal(OIDEncryptionAlgorithmAES256GCM): + return EncryptionAlgorithmAES256GCM, nil + default: + return -1, ErrUnsupportedAlgorithm + } +} + func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { alg := eci.ContentEncryptionAlgorithm.Algorithm - if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && - !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && - !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && - !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { - return nil, ErrUnsupportedAlgorithm + if _, err := encryptedAlgoOidToConstant(alg); err != nil { + return nil, err } // EncryptedContent can either be constructed of multple OCTET STRINGs diff --git a/helper/pkcs7/encrypt.go b/helper/pkcs7/encrypt.go index 90da67e4ee..551c121f12 100644 --- a/helper/pkcs7/encrypt.go +++ b/helper/pkcs7/encrypt.go @@ -55,6 +55,9 @@ const ( // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm EncryptionAlgorithmAES256GCM + + // EncryptionAlgorithmDESEDE3CBC is the 3DES CBC encryption algorithm (currently only supported on decrypt) + EncryptionAlgorithmDESEDE3CBC ) // ContentEncryptionAlgorithm determines the algorithm used to encrypt the @@ -77,10 +80,10 @@ type aesGCMParameters struct { ICVLen int } -func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { +func encryptAESGCM(content []byte, key []byte, algo int) ([]byte, *encryptedContentInfo, error) { var keyLen int var algID asn1.ObjectIdentifier - switch ContentEncryptionAlgorithm { + switch algo { case EncryptionAlgorithmAES128GCM: keyLen = 16 algID = OIDEncryptionAlgorithmAES128GCM @@ -88,7 +91,7 @@ func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, e keyLen = 32 algID = OIDEncryptionAlgorithmAES256GCM default: - return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm) + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", algo) } if key == nil { // Create AES key @@ -147,10 +150,23 @@ func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, e return key, &eci, nil } -func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { +func encryptDESCBC(content []byte, key []byte, algo int) ([]byte, *encryptedContentInfo, error) { + var keyLen int + var algID asn1.ObjectIdentifier + switch algo { + case EncryptionAlgorithmDESCBC: + keyLen = 8 + algID = OIDEncryptionAlgorithmDESCBC + case EncryptionAlgorithmDESEDE3CBC: + keyLen = 24 + algID = OIDEncryptionAlgorithmDESEDE3CBC + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptDESCBC: %d", algo) + } + if key == nil { - // Create DES key - key = make([]byte, 8) + // Create our key + key = make([]byte, keyLen) _, err := rand.Read(key) if err != nil { @@ -166,9 +182,20 @@ func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, e } // Encrypt padded content - block, err := des.NewCipher(key) - if err != nil { - return nil, nil, err + var block cipher.Block + switch algo { + case EncryptionAlgorithmDESCBC: + block, err = des.NewCipher(key) + if err != nil { + return nil, nil, err + } + case EncryptionAlgorithmDESEDE3CBC: + block, err = des.NewTripleDESCipher(key) + if err != nil { + return nil, nil, err + } + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptDESCBC: %d", algo) } mode := cipher.NewCBCEncrypter(block, iv) plaintext, err := pad(content, mode.BlockSize()) @@ -182,7 +209,7 @@ func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, e eci := encryptedContentInfo{ ContentType: OIDData, ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: OIDEncryptionAlgorithmDESCBC, + Algorithm: algID, Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, }, EncryptedContent: marshalEncryptedContent(cyphertext), @@ -191,10 +218,10 @@ func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, e return key, &eci, nil } -func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { +func encryptAESCBC(content []byte, key []byte, algo int) ([]byte, *encryptedContentInfo, error) { var keyLen int var algID asn1.ObjectIdentifier - switch ContentEncryptionAlgorithm { + switch algo { case EncryptionAlgorithmAES128CBC: keyLen = 16 algID = OIDEncryptionAlgorithmAES128CBC @@ -202,7 +229,7 @@ func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, e keyLen = 32 algID = OIDEncryptionAlgorithmAES256CBC default: - return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm) + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", algo) } if key == nil { @@ -260,22 +287,38 @@ func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, e // // TODO(fullsailor): Add support for encrypting content with other algorithms func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { + return EncryptWithAlgo(content, recipients, ContentEncryptionAlgorithm) +} + +// EncryptWithAlgo creates and returns an envelope data PKCS7 structure with encrypted +// recipient keys for each recipient public key using the specified algorithm. +// +// The algorithm must be one of the supported encryption algorithms: +// - EncryptionAlgorithmDESCBC +// - EncryptionAlgorithmDESEDE3CBC +// - EncryptionAlgorithmAES128CBC +// - EncryptionAlgorithmAES256CBC +// - EncryptionAlgorithmAES128GCM +// - EncryptionAlgorithmAES256GCM +func EncryptWithAlgo(content []byte, recipients []*x509.Certificate, algo int) ([]byte, error) { var eci *encryptedContentInfo var key []byte var err error // Apply chosen symmetric encryption method - switch ContentEncryptionAlgorithm { + switch algo { case EncryptionAlgorithmDESCBC: - key, eci, err = encryptDESCBC(content, nil) + fallthrough + case EncryptionAlgorithmDESEDE3CBC: + key, eci, err = encryptDESCBC(content, nil, algo) case EncryptionAlgorithmAES128CBC: fallthrough case EncryptionAlgorithmAES256CBC: - key, eci, err = encryptAESCBC(content, nil) + key, eci, err = encryptAESCBC(content, nil, algo) case EncryptionAlgorithmAES128GCM: fallthrough case EncryptionAlgorithmAES256GCM: - key, eci, err = encryptAESGCM(content, nil) + key, eci, err = encryptAESGCM(content, nil, algo) default: return nil, ErrUnsupportedEncryptionAlgorithm @@ -330,6 +373,18 @@ func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { // EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, // encrypted using caller provided pre-shared secret. func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { + return EncryptUsingPSKWithAlgo(content, key, ContentEncryptionAlgorithm) +} + +// EncryptUsingPSKWithAlgo creates and returns an encrypted data PKCS7 structure, +// encrypted using caller provided pre-shared secret with the specified algorithm. +// +// The algorithm must be one of the supported encryption algorithms: +// - EncryptionAlgorithmDESCBC +// - EncryptionAlgorithmDESEDE3CBC +// - EncryptionAlgorithmAES128GCM +// - EncryptionAlgorithmAES256GCM +func EncryptUsingPSKWithAlgo(content []byte, key []byte, algo int) ([]byte, error) { var eci *encryptedContentInfo var err error @@ -338,14 +393,16 @@ func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { } // Apply chosen symmetric encryption method - switch ContentEncryptionAlgorithm { + switch algo { case EncryptionAlgorithmDESCBC: - _, eci, err = encryptDESCBC(content, key) + fallthrough + case EncryptionAlgorithmDESEDE3CBC: + _, eci, err = encryptDESCBC(content, key, algo) case EncryptionAlgorithmAES128GCM: fallthrough case EncryptionAlgorithmAES256GCM: - _, eci, err = encryptAESGCM(content, key) + _, eci, err = encryptAESGCM(content, key, algo) default: return nil, ErrUnsupportedEncryptionAlgorithm diff --git a/helper/pkcs7/encrypt_test.go b/helper/pkcs7/encrypt_test.go index 7f1bead232..cf0bb5b347 100644 --- a/helper/pkcs7/encrypt_test.go +++ b/helper/pkcs7/encrypt_test.go @@ -9,6 +9,7 @@ import ( func TestEncrypt(t *testing.T) { modes := []int{ EncryptionAlgorithmDESCBC, + EncryptionAlgorithmDESEDE3CBC, EncryptionAlgorithmAES128CBC, EncryptionAlgorithmAES256CBC, EncryptionAlgorithmAES128GCM, @@ -49,7 +50,9 @@ func TestEncrypt(t *testing.T) { func TestEncryptUsingPSK(t *testing.T) { modes := []int{ EncryptionAlgorithmDESCBC, + EncryptionAlgorithmDESEDE3CBC, EncryptionAlgorithmAES128GCM, + EncryptionAlgorithmAES256GCM, } for _, mode := range modes { @@ -60,9 +63,16 @@ func TestEncryptUsingPSK(t *testing.T) { switch mode { case EncryptionAlgorithmDESCBC: key = []byte("64BitKey") + case EncryptionAlgorithmDESEDE3CBC: + key = []byte("192BitKeyForMoreSecurity") case EncryptionAlgorithmAES128GCM: key = []byte("128BitKey4AESGCM") + case EncryptionAlgorithmAES256GCM: + key = []byte("256BitKey4AESGCM256BitKey4AESGCM") + default: + t.Errorf("unsupported mode %d", mode) } + ciphertext, err := EncryptUsingPSK(plaintext, key) if err != nil { t.Fatal(err) diff --git a/helper/pkcs7/pkcs7.go b/helper/pkcs7/pkcs7.go index 69155692e3..c94317e42f 100644 --- a/helper/pkcs7/pkcs7.go +++ b/helper/pkcs7/pkcs7.go @@ -81,6 +81,10 @@ var ( OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} ) +func GetHashForDigestAlgorithm(digestAlg asn1.ObjectIdentifier) (crypto.Hash, error) { + return getHashForOID(digestAlg) +} + func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { switch { case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1),