diff --git a/crypto11/ecdsa.go b/crypto11/ecdsa.go index df592b2f3..a99cb6b8e 100644 --- a/crypto11/ecdsa.go +++ b/crypto11/ecdsa.go @@ -225,49 +225,21 @@ func GenerateECDSAKeyPair(c elliptic.Curve) (*PKCS11PrivateKeyECDSA, error) { return GenerateECDSAKeyPairOnSlot(instance.slot, nil, nil, c) } -// GenerateECDSAKeyPairOnSlot creates an ECDSA private key using curve c, on a specified slot. -// -// label and/or id can be nil, in which case random values will be generated. -// -// Only a limited set of named elliptic curves are supported. The -// underlying PKCS#11 implementation may impose further restrictions. -func GenerateECDSAKeyPairOnSlot(slot uint, id []byte, label []byte, c elliptic.Curve) (*PKCS11PrivateKeyECDSA, error) { - var k *PKCS11PrivateKeyECDSA - var err error - if err = ensureSessions(instance, slot); err != nil { - return nil, err +func getDefaultECDSAKeyAttributes(id []byte, label []byte, c elliptic.Curve) ([]*pkcs11.Attribute, []*pkcs11.Attribute, error) { + parameters, err := marshalEcParams(c) + if err != nil { + return nil, nil, err } - err = withSession(slot, func(session *PKCS11Session) error { - k, err = GenerateECDSAKeyPairOnSession(session, slot, id, label, c) - return err - }) - return k, err -} - -// GenerateECDSAKeyPairOnSession creates an ECDSA private key using curve c, using a specified session. -// -// label and/or id can be nil, in which case random values will be generated. -// -// Only a limited set of named elliptic curves are supported. The -// underlying PKCS#11 implementation may impose further restrictions. -func GenerateECDSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, label []byte, c elliptic.Curve) (*PKCS11PrivateKeyECDSA, error) { - var err error - var parameters []byte - var pub crypto.PublicKey - if label == nil { if label, err = generateKeyLabel(); err != nil { - return nil, err + return nil, nil, err } } if id == nil { if id, err = generateKeyLabel(); err != nil { - return nil, err + return nil, nil, err } } - if parameters, err = marshalEcParams(c); err != nil { - return nil, err - } publicKeyTemplate := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_ECDSA), @@ -285,6 +257,60 @@ func GenerateECDSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), pkcs11.NewAttribute(pkcs11.CKA_ID, id), } + + return publicKeyTemplate, privateKeyTemplate, nil +} + +// GenerateECDSAKeyPairOnSlot creates an ECDSA private key using curve c, on a specified slot. +// +// label and/or id can be nil, in which case random values will be generated. +// +// Only a limited set of named elliptic curves are supported. The +// underlying PKCS#11 implementation may impose further restrictions. +func GenerateECDSAKeyPairOnSlot(slot uint, id []byte, label []byte, c elliptic.Curve) (*PKCS11PrivateKeyECDSA, error) { + publicKeyTemplate, privateKeyTemplate, err := getDefaultECDSAKeyAttributes(id, label, c) + if err != nil { + return nil, err + } + return GenerateECDSAKeyPairOnSlotWithProvidedAttributes(slot, publicKeyTemplate, privateKeyTemplate) +} + +// GenerateECDSAKeyPairOnSession creates an ECDSA private key using curve c, using a specified session. +// +// label and/or id can be nil, in which case random values will be generated. +// +// Only a limited set of named elliptic curves are supported. The +// underlying PKCS#11 implementation may impose further restrictions. +func GenerateECDSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, label []byte, c elliptic.Curve) (*PKCS11PrivateKeyECDSA, error) { + publicKeyTemplate, privateKeyTemplate, err := getDefaultECDSAKeyAttributes(id, label, c) + if err != nil { + return nil, err + } + + return GenerateECDSAKeyPairOnSessionWithProvidedAttributes(session, slot, publicKeyTemplate, privateKeyTemplate) +} + +// GenerateECDSAKeyPairOnSlotWithProvidedAttributes generates a new ECDSA +// key pair in the given slot with the given public and private attributes +func GenerateECDSAKeyPairOnSlotWithProvidedAttributes(slot uint, publicKeyTemplate []*pkcs11.Attribute, privateKeyTemplate []*pkcs11.Attribute) (*PKCS11PrivateKeyECDSA, error) { + var k *PKCS11PrivateKeyECDSA + var err error + if err = ensureSessions(instance, slot); err != nil { + return nil, err + } + err = withSession(slot, func(session *PKCS11Session) error { + k, err = GenerateECDSAKeyPairOnSessionWithProvidedAttributes(session, slot, publicKeyTemplate, privateKeyTemplate) + return err + }) + return k, err +} + +// GenerateECDSAKeyPairOnSessionWithProvidedAttributes generates a new ECDSA +// key pair in the given slot with the given public and private attributes +func GenerateECDSAKeyPairOnSessionWithProvidedAttributes(session *PKCS11Session, slot uint, publicKeyTemplate []*pkcs11.Attribute, privateKeyTemplate []*pkcs11.Attribute) (*PKCS11PrivateKeyECDSA, error) { + var err error + var pub crypto.PublicKey + mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_ECDSA_KEY_PAIR_GEN, nil)} pubHandle, privHandle, err := session.Ctx.GenerateKeyPair(session.Handle, mech, diff --git a/crypto11/rsa.go b/crypto11/rsa.go index b243e2fe1..5bbbfcf1f 100644 --- a/crypto11/rsa.go +++ b/crypto11/rsa.go @@ -95,40 +95,16 @@ func GenerateRSAKeyPair(bits int) (*PKCS11PrivateKeyRSA, error) { return GenerateRSAKeyPairOnSlot(instance.slot, nil, nil, bits) } -// GenerateRSAKeyPairOnSlot creates a RSA private key on a specified slot -// -// Either or both label and/or id can be nil, in which case random values will be generated. -func GenerateRSAKeyPairOnSlot(slot uint, id []byte, label []byte, bits int) (*PKCS11PrivateKeyRSA, error) { - var k *PKCS11PrivateKeyRSA - var err error - if err = ensureSessions(instance, slot); err != nil { - return nil, err - } - err = withSession(slot, func(session *PKCS11Session) error { - k, err = GenerateRSAKeyPairOnSession(session, slot, id, label, bits) - return err - }) - return k, err -} - -// GenerateRSAKeyPairOnSession creates an RSA private key of given length, on a specified session. -// -// Either or both label and/or id can be nil, in which case random values will be generated. -// -// RSA private keys are generated with both sign and decrypt -// permissions, and a public exponent of 65537. -func GenerateRSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, label []byte, bits int) (*PKCS11PrivateKeyRSA, error) { +func getDefaultRSAKeyAttributes(id []byte, label []byte, bits int) ([]*pkcs11.Attribute, []*pkcs11.Attribute, error) { var err error - var pub crypto.PublicKey - if label == nil { if label, err = generateKeyLabel(); err != nil { - return nil, err + return nil, nil, err } } if id == nil { if id, err = generateKeyLabel(); err != nil { - return nil, err + return nil, nil, err } } publicKeyTemplate := []*pkcs11.Attribute{ @@ -151,6 +127,58 @@ func GenerateRSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, l pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), pkcs11.NewAttribute(pkcs11.CKA_ID, id), } + + return publicKeyTemplate, privateKeyTemplate, nil +} + +// GenerateRSAKeyPairOnSlot creates a RSA private key on a specified slot +// +// Either or both label and/or id can be nil, in which case random values will be generated. +func GenerateRSAKeyPairOnSlot(slot uint, id []byte, label []byte, bits int) (*PKCS11PrivateKeyRSA, error) { + publicKeyTemplate, privateKeyTemplate, err := getDefaultRSAKeyAttributes(id, label, bits) + if err != nil { + return nil, err + } + + return GenerateRSAKeyPairOnSlotWithProvidedAttributes(slot, publicKeyTemplate, privateKeyTemplate) +} + +// GenerateRSAKeyPairOnSession creates an RSA private key of given length, on a specified session. +// +// Either or both label and/or id can be nil, in which case random values will be generated. +// +// RSA private keys are generated with both sign and decrypt +// permissions, and a public exponent of 65537. +func GenerateRSAKeyPairOnSession(session *PKCS11Session, slot uint, id []byte, label []byte, bits int) (*PKCS11PrivateKeyRSA, error) { + publicKeyTemplate, privateKeyTemplate, err := getDefaultRSAKeyAttributes(id, label, bits) + if err != nil { + return nil, err + } + + return GenerateRSAKeyPairOnSessionWithProvidedAttributes(session, slot, publicKeyTemplate, privateKeyTemplate) +} + +// GenerateRSAKeyPairOnSlotWithProvidedAttributes generates a new RSA +// key pair in the given slot with the given public and private attributes +func GenerateRSAKeyPairOnSlotWithProvidedAttributes(slot uint, publicKeyTemplate []*pkcs11.Attribute, privateKeyTemplate []*pkcs11.Attribute) (*PKCS11PrivateKeyRSA, error) { + var k *PKCS11PrivateKeyRSA + var err error + if err = ensureSessions(instance, slot); err != nil { + return nil, err + } + err = withSession(slot, func(session *PKCS11Session) error { + k, err = GenerateRSAKeyPairOnSessionWithProvidedAttributes(session, slot, publicKeyTemplate, privateKeyTemplate) + return err + }) + return k, err +} + +// GenerateRSAKeyPairOnSessionWithProvidedAttributes generates a new RSA +// key pair in the given slot with the given public and private attributes +func GenerateRSAKeyPairOnSessionWithProvidedAttributes(session *PKCS11Session, slot uint, publicKeyTemplate []*pkcs11.Attribute, privateKeyTemplate []*pkcs11.Attribute) (*PKCS11PrivateKeyRSA, error) { + var err error + var pub crypto.PublicKey + mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)} pubHandle, privHandle, err := session.Ctx.GenerateKeyPair(session.Handle, mech, diff --git a/main.go b/main.go index a79d92e2e..62870b34a 100755 --- a/main.go +++ b/main.go @@ -385,6 +385,12 @@ func (a *autographer) initHSM(conf configuration) error { return fmt.Errorf("error in initHSM from crypto11.Configure: %w", err) } if tmpCtx != nil { + var hsm signer.HSM + if os.Getenv("KMS_PKCS11_CONFIG") != "" { + hsm = signer.NewGCPHSM(tmpCtx) + } else { + hsm = signer.NewAWSHSM(tmpCtx) + } // if we successfully initialized the crypto11 context, // tell the signers they can try using the HSM for i := range conf.Signers { @@ -402,7 +408,7 @@ func (a *autographer) initHSM(conf configuration) error { // TODO(AUT-203): when we make `signer.Configuration` immutable, // we'll not need this strange `conf.Signers[i]` and can loop // through them normally. - conf.Signers[i].InitHSM(signer.NewAWSHSM(tmpCtx)) + conf.Signers[i].InitHSM(hsm) signerConf := &conf.Signers[i] if signerConf.PrivateKeyHasPEMPrefix() { diff --git a/signer/hsm.go b/signer/hsm.go index 0ce2e093b..cba680129 100644 --- a/signer/hsm.go +++ b/signer/hsm.go @@ -7,11 +7,13 @@ import ( "fmt" "io" + "github.com/miekg/pkcs11" "github.com/mozilla-services/autograph/crypto11" ) type HSM interface { GetPrivateKey(label []byte) (crypto.PrivateKey, error) + // MakeKey generates a new keypair of type `keyTpl` and returns the new key structs. MakeKey(keyTpl interface{}, keyName string) (crypto.PrivateKey, crypto.PublicKey, error) GetRand() io.Reader } @@ -33,7 +35,6 @@ type AWSHSM struct { GenericHSM } -// MakeKey generates a new keypair of type `keyTpl` and returns the new key structs. func (hsm *AWSHSM) MakeKey(keyTpl interface{}, keyName string) (crypto.PrivateKey, crypto.PublicKey, error) { var slots []uint slots, err := hsm.ctx.GetSlotList(true) @@ -72,3 +73,100 @@ func NewAWSHSM(ctx crypto11.PKCS11Context) *AWSHSM { }, } } + +// Constants from https://github.com/GoogleCloudPlatform/kms-integrations/blob/master/kmsp11/kmsp11.h +// that are needed when generating ECDSA or RSA keys in GCP KMS. + +// A marker for a PKCS #11 attribute or flag defined by Google. +// (Note that 0x80000000UL is CKA_VENDOR_DEFINED). +const CKA_GOOGLE_DEFINED = 0x80000000 | 0x1E100 + +// An attribute that indicates the backing CryptoKeyVersionAlgorithm in Cloud +// KMS. +const CKA_KMS_ALGORITHM = CKA_GOOGLE_DEFINED | 0x01 + +// ECDSA on the NIST P-256 curve with a SHA256 digest. +const KMS_ALGORITHM_EC_SIGN_P256_SHA256 = 12 + +// ECDSA on the NIST P-384 curve with a SHA384 digest. +const KMS_ALGORITHM_EC_SIGN_P384_SHA384 = 13 + +// RSASSA-PKCS1-v1_5 with a 2048 bit key and a SHA256 digest. +const KMS_ALGORITHM_RSA_SIGN_PKCS1_2048_SHA256 = 5 + +// RSASSA-PKCS1-v1_5 with a 3072 bit key and a SHA256 digest. +const KMS_ALGORITHM_RSA_SIGN_PKCS1_3072_SHA256 = 6 + +// RSASSA-PKCS1-v1_5 with a 4096 bit key and a SHA256 digest. +const KMS_ALGORITHM_RSA_SIGN_PKCS1_4096_SHA256 = 7 + +// Our own constant; simply a shortcut for a combination we use in a few places +const CKA_GOOGLE_DEFINED_KMS_ALGORITHM = CKA_GOOGLE_DEFINED | CKA_KMS_ALGORITHM + +type GCPHSM struct { + GenericHSM +} + +func (hsm *GCPHSM) MakeKey(keyTpl interface{}, keyName string) (crypto.PrivateKey, crypto.PublicKey, error) { + var slots []uint + slots, err := hsm.ctx.GetSlotList(true) + if err != nil { + return nil, nil, fmt.Errorf("failed to list PKCS#11 Slots: %w", err) + } + if len(slots) < 1 { + return nil, nil, fmt.Errorf("failed to find a usable slot in hsm context") + } + publicKeyTemplate := []*pkcs11.Attribute{} + privateKeyTemplate := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_LABEL, []byte(keyName)), + } + switch keyTplType := keyTpl.(type) { + case *ecdsa.PublicKey: + size := keyTplType.Params().BitSize + switch size { + case 256: + privateKeyTemplate = append(privateKeyTemplate, pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_EC_SIGN_P256_SHA256)) + case 384: + privateKeyTemplate = append(privateKeyTemplate, pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_EC_SIGN_P384_SHA384)) + default: + return nil, nil, fmt.Errorf("invalid elliptic curve: must be p256 or p384") + } + + priv, err := crypto11.GenerateECDSAKeyPairOnSlotWithProvidedAttributes(slots[0], publicKeyTemplate, privateKeyTemplate) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate ecdsa key in hsm: %w", err) + } + pub := priv.PubKey.(*ecdsa.PublicKey) + return priv, pub, nil + + case *rsa.PublicKey: + keySizeBytes := keyTplType.Size() + switch keySizeBytes { + case 256: + privateKeyTemplate = append(privateKeyTemplate, pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_RSA_SIGN_PKCS1_2048_SHA256)) + case 384: + privateKeyTemplate = append(privateKeyTemplate, pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_RSA_SIGN_PKCS1_3072_SHA256)) + case 512: + privateKeyTemplate = append(privateKeyTemplate, pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_RSA_SIGN_PKCS1_4096_SHA256)) + default: + return nil, nil, fmt.Errorf("invalid rsa key size: got: %d", keySizeBytes) + } + + priv, err := crypto11.GenerateRSAKeyPairOnSlotWithProvidedAttributes(slots[0], publicKeyTemplate, privateKeyTemplate) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate rsa key in hsm: %w", err) + } + pub := priv.PubKey.(*rsa.PublicKey) + return priv, pub, nil + } + + return nil, nil, fmt.Errorf("making key of type %T is not supported", keyTpl) +} + +func NewGCPHSM(ctx crypto11.PKCS11Context) *GCPHSM { + return &GCPHSM{ + GenericHSM{ + ctx, + }, + } +} diff --git a/signer/signer_test.go b/signer/signer_test.go index d83cda236..59d04e223 100644 --- a/signer/signer_test.go +++ b/signer/signer_test.go @@ -28,19 +28,31 @@ func TestParseRSAPrivateKey(t *testing.T) { var rsaPrivateKey = ` -----BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQCxoeCUW5KJxNPxMp+KmCxKLc1Zv9Ny+4CFqcUXVUYH69L3mQ7v -IWrJ9GBfcaA7BPQqUlWxWM+OCEQZH1EZNIuqRMNQVuIGCbz5UQ8w6tS0gcgdeGX7 -J7jgCQ4RK3F/PuCM38QBLaHx988qG8NMc6VKErBjctCXFHQt14lerd5KpQIDAQAB -AoGAYrf6Hbk+mT5AI33k2Jt1kcweodBP7UkExkPxeuQzRVe0KVJw0EkcFhywKpr1 -V5eLMrILWcJnpyHE5slWwtFHBG6a5fLaNtsBBtcAIfqTQ0Vfj5c6SzVaJv0Z5rOd -7gQF6isy3t3w9IF3We9wXQKzT6q5ypPGdm6fciKQ8RnzREkCQQDZwppKATqQ41/R -vhSj90fFifrGE6aVKC1hgSpxGQa4oIdsYYHwMzyhBmWW9Xv/R+fPyr8ZwPxp2c12 -33QwOLPLAkEA0NNUb+z4ebVVHyvSwF5jhfJxigim+s49KuzJ1+A2RaSApGyBZiwS -rWvWkB471POAKUYt5ykIWVZ83zcceQiNTwJBAMJUFQZX5GDqWFc/zwGoKkeR49Yi -MTXIvf7Wmv6E++eFcnT461FlGAUHRV+bQQXGsItR/opIG7mGogIkVXa3E1MCQARX -AAA7eoZ9AEHflUeuLn9QJI/r0hyQQLEtrpwv6rDT1GCWaLII5HJ6NUFVf4TTcqxo -6vdM4QGKTJoO+SaCyP0CQFdpcxSAuzpFcKv0IlJ8XzS/cy+mweCMwyJ1PFEc4FX6 -wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY= +MIIEowIBAAKCAQEAmDYP45RPsmV9qZbISn3tu3yplRJRuqJtF+Fu01Rc4aXGhAMD +NgyEmCGZsOxKG/g6GH5KmnE6V21Z7Iz45Q+xCnCGpFKJhugZxa0K9U9grqV7MtP7 +2hF6Y9QPKaw7dHx/k3WwrPJe0Y/rKrGxImHjYKI4s0n2BrFdqntQq6AwC1zhXM+T +VNtGZpMwYfeEEZAXMjm7Goawy3qmUIBUbwSr9yKwbfuKRUCOZ8gQXiTL4VZ8xS8d +qJQBG4wx0VWEyaMoAolBERBJJnZWv6+phDen/1QGvlTcCFaYg9byrdI3KVOrKxqX +rKn7wKiaEWJpp/4OVcBhUegUhO8BSLegqliiAwIDAQABAoIBADVIh5tladjLiof5 +jrf1CWnepAbZWN76yTHY6tDz8WfUfn/sBg2/qBMRgBndPbw40y2L2FXkWUYNs7MJ +Tn/xVEqRRbD0a8xcJ9l5UCK73N6Gc3BBoSKfh7a2n3A5KL8IbiiSxHxmhCbcOLjD +Z3zfw5cqcqrgs014fY+Wh5DtDKSNG5ODfYPqpZ/oMzLiwrlhgh2AqjRYDVbzPE/F +Q8Ab3xvS/dPTo42DDSR1ccHLCZCEOK/wAm+qxqd3dkbUQ5H4YFVI6Xp9WQaHKeZ1 +xp8sdjQboMwHZ9YN4vWCNX3OrmfOzvN1F4wHdP0ptw+uY6Sbip0pDmQT3HHjrxve +rgj+xtECgYEA9dUtf5vRtTCN5GTxHT8Iz06uxWnn5+E3KHdudpWNx/YKITtImv5i +CuxhY1yESBgxfSojaV5QWPke4ma+qtN1tyRAjtF/k2WorjZsV9klMiXlAT63iPAG +abyOJ8deV3QIlYxSVhC0lAD+XfD5Rs+jBgWWayswZ+uA/8Ebm6PEgacCgYEAnoGj +5UPe23nvCMeCSdpnX3d/LfZnMkNenXt/rByN0a9vtjMEcZ5Sx1Tlv0roXGDtA3yv +xpgA8n4VDHvmvgpO5BK82TiA+1biUboHkF5sRYEJjsC2HtWQ0v6pl9xXTjecpSlC +eeFrsnWnSVhX9Z6V6e6B/EDkhp5LHPZk5bfm0EUCgYAYnFPmv5G6Avdhkx10YRgf +sO/cQaL+2tQrz/EWHBjKmP4gn4/APJFSKKIDUYLIuOtTbYGIDfIbRi1qWwDhlzPk +ttNjuON9vSKq9jXYgZuwroyDmGTFZ8oskbzljJcMSEiHuDmR9jAt1P+iJfq+tRDM +DIknh3ZcIP6UHCAIb9e/ZwKBgBbMKpiFBH6osPqgR1r78LZIZ6BiukD+c5NO+fP1 +P2iTRQv9lnwI+3rz+P9kdLskrbI8ssNrhWdbPwfGok9fCC3BjCvp9pMv0elTSlc3 +XXc5sfg4O3l/2g6e1iGjbWMwmHUg1BDXnTfTuDXSYQRQxNsalNOFOdkq1z7ZKXXo +12fJAoGBAOpDVpN70BSbBomI+wb+9Mx2GhLvPbKwhPEk9MaUGXGqxKjh0mDsMarR +WUElWLWSazm6kXPuqzyzDbJZRyKSHr9vH9AwlT8V/vQHebGz1CrErSd2Sv6ZIO5R +xYT2GfjxbFUqcnbLEKnjoccffVwxP6PONR9hzUXPIecMXfI3OOAU -----END RSA PRIVATE KEY----- ` @@ -497,3 +509,156 @@ func TestMakeKeyAWSRSA(t *testing.T) { t.Fatalf("MakeKey failed: %v", err) } } + +func TestMakeKeyGCPECDSA(t *testing.T) { + ctrl := gomock.NewController(t) + mockCtx := mockpkcs11.NewMockPKCS11Context(ctrl) + defer ctrl.Finish() + + // we don't actually have any use for the private key besides extracting + // the public key from it, but I couldn't find a way to directly construct + // the public key. + privKey, err := ParsePrivateKey([]byte(ecdsaPrivateKey)) + if err != nil { + t.Fatalf("failed to parse private key: %v", err) + } + pubKey := privKey.(*ecdsa.PrivateKey).PublicKey + + // annoyingly, we also need the ecdh form of the private key + // to find the length of it, to mock some things correctly. + ecdhPrivKey, err := privKey.(*ecdsa.PrivateKey).ECDH() + if err != nil { + t.Fatalf("failed to convert ecdsa private key to ecdh: %v", err) + } + ecdhPubKey := ecdhPrivKey.PublicKey() + + // p256, prefix, and ecPointValue are all just intermediaries to set up + // pubKeyAttrs, which is a return value needed by one of the mocks. + p256, err := asn1.Marshal(asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}) + if err != nil { + t.Fatalf("failed to marshal p256 object identifier") + } + // nasty hack because i couldn't find any other way to make unmarshalEcPoint + // happy. + prefix := make([]byte, 2) + prefix[0] = 0x04 + prefix[1] = byte(len(ecdhPubKey.Bytes())) + ecPointValue := append(prefix, ecdhPubKey.Bytes()...) + + pubKeyAttrs := []*pkcs11.Attribute{ + &pkcs11.Attribute{ + Type: pkcs11.CKA_ECDSA_PARAMS, + Value: p256, + }, + &pkcs11.Attribute{ + Type: pkcs11.CKA_EC_POINT, + Value: ecPointValue, + }, + } + + label := "test" + slot := uint(0) + session := pkcs11.SessionHandle(0) + object := pkcs11.ObjectHandle(0) + mechanism := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_ECDSA_KEY_PAIR_GEN, nil)} + attributeTemplate := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_ECDSA_PARAMS, nil), + pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, nil), + } + + publicKeyTemplate := []*pkcs11.Attribute{} + privateKeyTemplate := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), + pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_EC_SIGN_P256_SHA256), + } + + mockCtx.EXPECT().Initialize().Return(nil).Times(1) + mockCtx.EXPECT().GetSlotList(true).Return([]uint{slot}, nil).Times(3) + mockCtx.EXPECT().GetTokenInfo(slot).Return(pkcs11.TokenInfo{}, nil).Times(1) + mockCtx.EXPECT().OpenSession(slot, uint(6)).Return(session, nil).Times(1) + mockCtx.EXPECT().GenerateKeyPair(session, mechanism, publicKeyTemplate, privateKeyTemplate).Times(1) + mockCtx.EXPECT().GetAttributeValue(session, object, attributeTemplate).Return(pubKeyAttrs, nil).Times(1) + // these ones are called as part of Close(), not as part of our actual testing + mockCtx.EXPECT().CloseSession(session).Return(nil).Times(1) + mockCtx.EXPECT().CloseAllSessions(slot).Return(nil).Times(1) + mockCtx.EXPECT().Finalize().Return(nil).Times(1) + mockCtx.EXPECT().Destroy().Times(1) + + mockFactory := mockedPKCS11ContextFactory(mockCtx) + crypto11.Configure(&crypto11.PKCS11Config{}, mockFactory) + defer crypto11.Close() + + cfg := Configuration{ + isHsmAvailable: true, + Hsm: NewGCPHSM(mockCtx), + } + _, _, err = cfg.MakeKey(&pubKey, label) + if err != nil { + t.Fatalf("MakeKey failed: %v", err) + } +} + +func TestMakeKeyGCPRSA(t *testing.T) { + ctrl := gomock.NewController(t) + mockCtx := mockpkcs11.NewMockPKCS11Context(ctrl) + defer ctrl.Finish() + + // we don't actually have any use for the private key besides extracting + // the public key from it, but I couldn't find a way to directly construct + // the public key. + privKey, err := ParsePrivateKey([]byte(rsaPrivateKey)) + if err != nil { + t.Fatalf("failed to parse private key: %v", err) + } + pubKey := privKey.(*rsa.PrivateKey).PublicKey + + pubKeyAttrs := []*pkcs11.Attribute{ + &pkcs11.Attribute{ + Type: pkcs11.CKA_MODULUS, + Value: []byte("foo"), + }, + &pkcs11.Attribute{ + Type: pkcs11.CKA_PUBLIC_EXPONENT, + Value: []byte("foo"), + }, + } + + label := "test" + slot := uint(0) + session := pkcs11.SessionHandle(0) + object := pkcs11.ObjectHandle(0) + mechanism := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)} + attributeTemplate := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil), + pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil), + } + + publicKeyTemplate := []*pkcs11.Attribute{} + privateKeyTemplate := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), + pkcs11.NewAttribute(CKA_GOOGLE_DEFINED_KMS_ALGORITHM, KMS_ALGORITHM_RSA_SIGN_PKCS1_2048_SHA256), + } + mockCtx.EXPECT().Initialize().Return(nil).Times(1) + mockCtx.EXPECT().GetSlotList(true).Return([]uint{slot}, nil).Times(3) + mockCtx.EXPECT().GetTokenInfo(slot).Return(pkcs11.TokenInfo{}, nil).Times(1) + mockCtx.EXPECT().OpenSession(slot, uint(6)).Return(session, nil).Times(1) + mockCtx.EXPECT().GenerateKeyPair(session, mechanism, publicKeyTemplate, privateKeyTemplate).Times(1) + mockCtx.EXPECT().GetAttributeValue(session, object, attributeTemplate).Return(pubKeyAttrs, nil).Times(1) + // these ones are called as part of Close(), not as part of our actual testing + mockCtx.EXPECT().CloseSession(session).Return(nil).Times(1) + mockCtx.EXPECT().CloseAllSessions(slot).Return(nil).Times(1) + mockCtx.EXPECT().Finalize().Return(nil).Times(1) + mockCtx.EXPECT().Destroy().Times(1) + + mockFactory := mockedPKCS11ContextFactory(mockCtx) + crypto11.Configure(&crypto11.PKCS11Config{}, mockFactory) + defer crypto11.Close() + cfg := Configuration{ + isHsmAvailable: true, + Hsm: NewGCPHSM(mockCtx), + } + _, _, err = cfg.MakeKey(&pubKey, label) + if err != nil { + t.Fatalf("MakeKey failed: %v", err) + } +}