package api import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "errors" "log" "math/big" "github.com/docker/notary/signer/keys" "github.com/endophage/gotuf/data" "github.com/miekg/pkcs11" ) // RSAHardwareCryptoService is an implementation of SigningService type RSAHardwareCryptoService struct { keys map[string]*keys.HSMRSAKey context *pkcs11.Ctx session pkcs11.SessionHandle } // Create creates a key and returns its public components func (s *RSAHardwareCryptoService) Create(role string, algo data.KeyAlgorithm) (data.PublicKey, error) { // For now generate random labels for keys // (diogo): add link between keyID and label in database so we can support multiple keys randomLabel := make([]byte, 32) _, err := rand.Read(randomLabel) if err != nil { return nil, errors.New("Could not generate a random key label.") } // Set the public key template // CKA_TOKEN: Guarantees key persistence in hardware // CKA_LABEL: Identifies this specific key inside of the HSM publicKeyTemplate := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{3}), pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, 2048), pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)), } privateKeyTemplate := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)), } // Generate a new RSA private/public keypair inside of the HSM pub, priv, err := s.context.GenerateKeyPair(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)}, publicKeyTemplate, privateKeyTemplate) if err != nil { return nil, errors.New("Could not generate a new key inside of the HSM.") } // (diogo): This template is used for the GetAttribute template := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil), pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, nil), pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil), } // Retrieve the public-key material to be able to create a new HSMRSAKey attr, err := s.context.GetAttributeValue(s.session, pub, template) if err != nil { return nil, errors.New("Failed to get Attribute value.") } // We're going to store the elements of the RSA Public key, exponent and Modulus inside of exp and mod var exp int mod := big.NewInt(0) // Iterate through all the attributes of this key and saves CKA_PUBLIC_EXPONENT and CKA_MODULUS. Removes ordering specific issues. for _, a := range attr { if a.Type == pkcs11.CKA_PUBLIC_EXPONENT { exp, _ = readInt(a.Value) } if a.Type == pkcs11.CKA_MODULUS { mod.SetBytes(a.Value) } } rsaPublicKey := rsa.PublicKey{N: mod, E: exp} // Using x509 to Marshal the Public key into der encoding pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey) if err != nil { return nil, errors.New("Failed to Marshal public key.") } // (diogo): Ideally I would like to return base64 PEM encoded public keys to the client k := keys.NewHSMRSAKey(pubBytes, priv) keyID := k.ID() s.keys[keyID] = k return k, nil } // RemoveKey removes a key from the key database func (s *RSAHardwareCryptoService) RemoveKey(keyID string) error { if _, ok := s.keys[keyID]; !ok { return keys.ErrInvalidKeyID } delete(s.keys, keyID) return nil } // GetKey returns the public components of a particular key func (s *RSAHardwareCryptoService) GetKey(keyID string) data.PublicKey { key, ok := s.keys[keyID] if !ok { return nil } return key } // Sign returns a signature for a given signature request func (s *RSAHardwareCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { signatures := make([]data.Signature, 0, len(keyIDs)) for _, keyid := range keyIDs { privateKey, present := s.keys[keyid] if !present { // We skip keys that aren't found continue } priv := privateKey.PKCS11ObjectHandle() var sig []byte var err error for i := 0; i < 3; i++ { s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv) sig, err = s.context.Sign(s.session, payload) if err != nil { log.Printf("Error while signing: %s", err) continue } digest := sha256.Sum256(payload) pub, err := x509.ParsePKIXPublicKey(privateKey.Public()) if err != nil { log.Printf("Failed to parse public key: %s\n", err) return nil, err } rsaPub, ok := pub.(*rsa.PublicKey) if !ok { log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key") return nil, err } err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig) if err != nil { log.Printf("Failed verification. Retrying: %s", err) continue } break } if sig == nil { return nil, errors.New("Failed to create signature") } signatures = append(signatures, data.Signature{ KeyID: keyid, Method: data.RSAPKCS1v15Signature, Signature: sig[:], }) } return signatures, nil } // NewRSAHardwareCryptoService returns an instance of RSAHardwareCryptoService func NewRSAHardwareCryptoService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSAHardwareCryptoService { return &RSAHardwareCryptoService{ keys: make(map[string]*keys.HSMRSAKey), context: ctx, session: session, } } // readInt converts a []byte into an int. It is used to convert the RSA Public key exponent into an int to create a crypto.PublicKey func readInt(data []byte) (int, error) { var ret int if len(data) > 4 { return 0, errors.New("Cannot convert byte array due to size") } for i, a := range data { ret |= (int(a) << uint(i*8)) } return ret, nil }