diff --git a/cmd/ceremony/cert.go b/cmd/ceremony/cert.go index 4f11c1784..ac6844582 100644 --- a/cmd/ceremony/cert.go +++ b/cmd/ceremony/cert.go @@ -1,8 +1,6 @@ package main import ( - "bytes" - "crypto" "crypto/sha1" "crypto/x509" "crypto/x509/pkix" @@ -14,9 +12,6 @@ import ( "strconv" "strings" "time" - - "github.com/letsencrypt/boulder/pkcs11helpers" - "github.com/miekg/pkcs11" ) type policyInfoConfig struct { @@ -312,108 +307,3 @@ type failReader struct{} func (fr *failReader) Read([]byte) (int, error) { return 0, errors.New("Empty reader used by x509.CreateCertificate") } - -// x509Signer is a convenience wrapper used for converting between the -// PKCS#11 ECDSA signature format and the RFC 5480 one which is required -// for X.509 certificates -type x509Signer struct { - session *pkcs11helpers.Session - objectHandle pkcs11.ObjectHandle - keyType pkcs11helpers.KeyType - - pub crypto.PublicKey -} - -// Sign wraps pkcs11helpers.Sign. If the signing key is ECDSA then the signature -// is converted from the PKCS#11 format to the RFC 5480 format. For RSA keys a -// conversion step is not needed. -func (p *x509Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - signature, err := p.session.Sign(p.objectHandle, p.keyType, digest, opts.HashFunc()) - if err != nil { - return nil, err - } - - if p.keyType == pkcs11helpers.ECDSAKey { - // Convert from the PKCS#11 format to the RFC 5480 format so that - // it can be used in a X.509 certificate - r := big.NewInt(0).SetBytes(signature[:len(signature)/2]) - s := big.NewInt(0).SetBytes(signature[len(signature)/2:]) - signature, err = asn1.Marshal(struct { - R, S *big.Int - }{R: r, S: s}) - if err != nil { - return nil, fmt.Errorf("failed to convert signature to RFC 5480 format: %s", err) - } - } - return signature, nil -} - -func (p *x509Signer) Public() crypto.PublicKey { - return p.pub -} - -// newSigner constructs a x509Signer for the private key object associated with the -// given label and ID. Unlike letsencrypt/pkcs11key this method doesn't rely on -// having the actual public key object in order to retrieve the private key -// handle. This is because we already have the key pair object ID, and as such -// do not need to query the HSM to retrieve it. -func newSigner(session *pkcs11helpers.Session, label string, id []byte) (crypto.Signer, error) { - // Retrieve the private key handle that will later be used for the certificate - // signing operation - privateHandle, err := session.FindObject([]*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), - pkcs11.NewAttribute(pkcs11.CKA_ID, id), - }) - if err != nil { - return nil, fmt.Errorf("failed to retrieve private key handle: %s", err) - } - attrs, err := session.GetAttributeValue(privateHandle, []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, nil)}, - ) - if err != nil { - return nil, fmt.Errorf("failed to retrieve key type: %s", err) - } - if len(attrs) == 0 { - return nil, errors.New("failed to retrieve key attributes") - } - - // Retrieve the public key handle with the same CKA_ID as the private key - // and construct a {rsa,ecdsa}.PublicKey for use in x509.CreateCertificate - pubHandle, err := session.FindObject([]*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), - pkcs11.NewAttribute(pkcs11.CKA_ID, id), - pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, attrs[0].Value), - }) - if err != nil { - return nil, fmt.Errorf("failed to retrieve public key handle: %s", err) - } - var pub crypto.PublicKey - var keyType pkcs11helpers.KeyType - switch { - // 0x00000000, CKK_RSA - case bytes.Equal(attrs[0].Value, []byte{0, 0, 0, 0, 0, 0, 0, 0}): - keyType = pkcs11helpers.RSAKey - pub, err = session.GetRSAPublicKey(pubHandle) - if err != nil { - return nil, fmt.Errorf("failed to retrieve public key: %s", err) - } - // 0x00000003, CKK_ECDSA - case bytes.Equal(attrs[0].Value, []byte{3, 0, 0, 0, 0, 0, 0, 0}): - keyType = pkcs11helpers.ECDSAKey - pub, err = session.GetECDSAPublicKey(pubHandle) - if err != nil { - return nil, fmt.Errorf("failed to retrieve public key: %s", err) - } - default: - return nil, errors.New("unsupported key type") - } - - return &x509Signer{ - session: session, - objectHandle: privateHandle, - keyType: keyType, - pub: pub, - }, nil -} diff --git a/cmd/ceremony/cert_test.go b/cmd/ceremony/cert_test.go index 3420a8529..3ac20d235 100644 --- a/cmd/ceremony/cert_test.go +++ b/cmd/ceremony/cert_test.go @@ -2,16 +2,11 @@ package main import ( "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" - "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/hex" "errors" - "math/big" "testing" "github.com/letsencrypt/boulder/pkcs11helpers" @@ -19,54 +14,6 @@ import ( "github.com/miekg/pkcs11" ) -func TestX509Signer(t *testing.T) { - s, ctx := pkcs11helpers.NewSessionWithMock() - - // test that x509Signer.Sign properly converts the PKCS#11 format signature to - // the RFC 5480 format signature - ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error { - return nil - } - tk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - test.AssertNotError(t, err, "Failed to generate test key") - ctx.SignFunc = func(_ pkcs11.SessionHandle, digest []byte) ([]byte, error) { - r, s, err := ecdsa.Sign(rand.Reader, tk, digest[:]) - if err != nil { - return nil, err - } - rBytes := r.Bytes() - sBytes := s.Bytes() - // http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html - // Section 2.3.1: EC Signatures - // "If r and s have different octet length, the shorter of both must be padded with - // leading zero octets such that both have the same octet length." - switch { - case len(rBytes) < len(sBytes): - padding := make([]byte, len(sBytes)-len(rBytes)) - rBytes = append(padding, rBytes...) - case len(rBytes) > len(sBytes): - padding := make([]byte, len(rBytes)-len(sBytes)) - sBytes = append(padding, sBytes...) - } - return append(rBytes, sBytes...), nil - } - digest := sha256.Sum256([]byte("hello")) - signer := &x509Signer{session: s, keyType: pkcs11helpers.ECDSAKey, pub: tk.Public()} - signature, err := signer.Sign(nil, digest[:], crypto.SHA256) - test.AssertNotError(t, err, "x509Signer.Sign failed") - - var rfcFormat struct { - R, S *big.Int - } - rest, err := asn1.Unmarshal(signature, &rfcFormat) - test.AssertNotError(t, err, "asn1.Unmarshal failed trying to parse signature") - test.Assert(t, len(rest) == 0, "Signature had trailing garbage") - verified := ecdsa.Verify(&tk.PublicKey, digest[:], rfcFormat.R, rfcFormat.S) - test.Assert(t, verified, "Failed to verify RFC format signature") - // For the sake of coverage - test.AssertEquals(t, signer.Public(), tk.Public()) -} - func TestParseOID(t *testing.T) { _, err := parseOID("") test.AssertError(t, err, "parseOID accepted an empty OID") @@ -458,94 +405,3 @@ func TestVerifyProfile(t *testing.T) { } } } - -func TestGetKey(t *testing.T) { - s, ctx := pkcs11helpers.NewSessionWithMock() - - // test newSigner fails when pkcs11helpers.FindObject for private key handle fails - ctx.FindObjectsInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Attribute) error { - return errors.New("broken") - } - _, err := newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when pkcs11helpers.FindObject for private key handle failed") - - // test newSigner fails when GetAttributeValue fails - ctx.FindObjectsInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Attribute) error { - return nil - } - ctx.FindObjectsFunc = func(pkcs11.SessionHandle, int) ([]pkcs11.ObjectHandle, bool, error) { - return []pkcs11.ObjectHandle{1}, false, nil - } - ctx.FindObjectsFinalFunc = func(pkcs11.SessionHandle) error { - return nil - } - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return nil, errors.New("broken") - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key type failed") - - // test newSigner fails when GetAttributeValue returns no attributes - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return nil, nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key type returned no attributes") - - // test newSigner fails when pkcs11helpers.FindObject for public key handle fails - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC)}, nil - } - ctx.FindObjectsInitFunc = func(_ pkcs11.SessionHandle, tmpl []*pkcs11.Attribute) error { - if bytes.Equal(tmpl[0].Value, []byte{2, 0, 0, 0, 0, 0, 0, 0}) { - return errors.New("broken") - } - return nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when pkcs11helpers.FindObject for public key handle failed") - - // test newSigner fails when pkcs11helpers.FindObject for private key returns unknown CKA_KEY_TYPE - ctx.FindObjectsInitFunc = func(_ pkcs11.SessionHandle, tmpl []*pkcs11.Attribute) error { - return nil - } - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{2, 0, 0, 0, 0, 0, 0, 0})}, nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key returned unknown key type") - - // test newSigner fails when GetRSAPublicKey fails - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{0, 0, 0, 0, 0, 0, 0, 0})}, nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when GetRSAPublicKey fails") - - // test newSigner fails when GetECDSAPublicKey fails - ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{3, 0, 0, 0, 0, 0, 0, 0})}, nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertError(t, err, "newSigner didn't fail when GetECDSAPublicKey fails") - - // test newSigner works when everything... works - ctx.GetAttributeValueFunc = func(_ pkcs11.SessionHandle, _ pkcs11.ObjectHandle, attrs []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { - var returns []*pkcs11.Attribute - for _, attr := range attrs { - switch attr.Type { - case pkcs11.CKA_KEY_TYPE: - returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{0, 0, 0, 0, 0, 0, 0, 0})) - case pkcs11.CKA_PUBLIC_EXPONENT: - returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 2, 3})) - case pkcs11.CKA_MODULUS: - returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{4, 5, 6})) - default: - return nil, errors.New("GetAttributeValue got unexpected attribute type") - } - } - return returns, nil - } - _, err = newSigner(s, "label", []byte{255, 255}) - test.AssertNotError(t, err, "newSigner failed when everything worked properly") -} diff --git a/cmd/ceremony/main.go b/cmd/ceremony/main.go index 4f3d86987..9c78ff2dc 100644 --- a/cmd/ceremony/main.go +++ b/cmd/ceremony/main.go @@ -352,7 +352,7 @@ func openSigner(cfg PKCS11SigningConfig, issuer *x509.Certificate) (crypto.Signe if err != nil { return nil, nil, fmt.Errorf("failed to decode key-id: %s", err) } - signer, err := newSigner(session, cfg.SigningLabel, keyID) + signer, err := session.NewSigner(cfg.SigningLabel, keyID) if err != nil { return nil, nil, fmt.Errorf("failed to retrieve private key handle: %s", err) } @@ -414,7 +414,7 @@ func rootCeremony(configBytes []byte) error { if err != nil { return err } - signer, err := newSigner(session, config.PKCS11.StoreLabel, keyInfo.id) + signer, err := session.NewSigner(config.PKCS11.StoreLabel, keyInfo.id) if err != nil { return fmt.Errorf("failed to retrieve signer: %s", err) } diff --git a/pkcs11helpers/helpers.go b/pkcs11helpers/helpers.go index d5606e353..45b68f3ae 100644 --- a/pkcs11helpers/helpers.go +++ b/pkcs11helpers/helpers.go @@ -1,6 +1,7 @@ package pkcs11helpers import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -8,6 +9,7 @@ import ( "encoding/asn1" "errors" "fmt" + "io" "math/big" "github.com/miekg/pkcs11" @@ -222,6 +224,111 @@ func (s *Session) FindObject(tmpl []*pkcs11.Attribute) (pkcs11.ObjectHandle, err return handles[0], nil } +// X509Signer is a convenience wrapper used for converting between the +// PKCS#11 ECDSA signature format and the RFC 5480 one which is required +// for X.509 certificates +type X509Signer struct { + session *Session + objectHandle pkcs11.ObjectHandle + keyType KeyType + + pub crypto.PublicKey +} + +// Sign signs a digest. If the signing key is ECDSA then the signature +// is converted from the PKCS#11 format to the RFC 5480 format. For RSA keys a +// conversion step is not needed. +func (p *X509Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + signature, err := p.session.Sign(p.objectHandle, p.keyType, digest, opts.HashFunc()) + if err != nil { + return nil, err + } + + if p.keyType == ECDSAKey { + // Convert from the PKCS#11 format to the RFC 5480 format so that + // it can be used in a X.509 certificate + r := big.NewInt(0).SetBytes(signature[:len(signature)/2]) + s := big.NewInt(0).SetBytes(signature[len(signature)/2:]) + signature, err = asn1.Marshal(struct { + R, S *big.Int + }{R: r, S: s}) + if err != nil { + return nil, fmt.Errorf("failed to convert signature to RFC 5480 format: %s", err) + } + } + return signature, nil +} + +func (p *X509Signer) Public() crypto.PublicKey { + return p.pub +} + +// NewSigner constructs an X509Signer for the private key object associated with the +// given label and ID. Unlike letsencrypt/pkcs11key this method doesn't rely on +// having the actual public key object in order to retrieve the private key +// handle. This is because we already have the key pair object ID, and as such +// do not need to query the HSM to retrieve it. +func (s *Session) NewSigner(label string, id []byte) (crypto.Signer, error) { + // Retrieve the private key handle that will later be used for the certificate + // signing operation + privateHandle, err := s.FindObject([]*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), + pkcs11.NewAttribute(pkcs11.CKA_ID, id), + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve private key handle: %s", err) + } + attrs, err := s.GetAttributeValue(privateHandle, []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, nil)}, + ) + if err != nil { + return nil, fmt.Errorf("failed to retrieve key type: %s", err) + } + if len(attrs) == 0 { + return nil, errors.New("failed to retrieve key attributes") + } + + // Retrieve the public key handle with the same CKA_ID as the private key + // and construct a {rsa,ecdsa}.PublicKey for use in x509.CreateCertificate + pubHandle, err := s.FindObject([]*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, label), + pkcs11.NewAttribute(pkcs11.CKA_ID, id), + pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, attrs[0].Value), + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve public key handle: %s", err) + } + var pub crypto.PublicKey + var keyType KeyType + switch { + // 0x00000000, CKK_RSA + case bytes.Equal(attrs[0].Value, []byte{0, 0, 0, 0, 0, 0, 0, 0}): + keyType = RSAKey + pub, err = s.GetRSAPublicKey(pubHandle) + if err != nil { + return nil, fmt.Errorf("failed to retrieve public key: %s", err) + } + // 0x00000003, CKK_ECDSA + case bytes.Equal(attrs[0].Value, []byte{3, 0, 0, 0, 0, 0, 0, 0}): + keyType = ECDSAKey + pub, err = s.GetECDSAPublicKey(pubHandle) + if err != nil { + return nil, fmt.Errorf("failed to retrieve public key: %s", err) + } + default: + return nil, errors.New("unsupported key type") + } + + return &X509Signer{ + session: s, + objectHandle: privateHandle, + keyType: keyType, + pub: pub, + }, nil +} + func NewMock() *MockCtx { return &MockCtx{} } diff --git a/pkcs11helpers/helpers_test.go b/pkcs11helpers/helpers_test.go index 769409114..226f76b4a 100644 --- a/pkcs11helpers/helpers_test.go +++ b/pkcs11helpers/helpers_test.go @@ -1,7 +1,15 @@ package pkcs11helpers import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/asn1" "errors" + "math/big" "strings" "testing" @@ -230,3 +238,142 @@ func TestFindObjectSucceeds(t *testing.T) { test.AssertNotError(t, err, "FindObject failed when everything worked as expected") test.AssertEquals(t, handle, pkcs11.ObjectHandle(1)) } + +func TestGetKey(t *testing.T) { + s, ctx := NewSessionWithMock() + + // test newSigner fails when FindObject for private key handle fails + ctx.FindObjectsInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Attribute) error { + return errors.New("broken") + } + _, err := s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when FindObject for private key handle failed") + + // test newSigner fails when GetAttributeValue fails + ctx.FindObjectsInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Attribute) error { + return nil + } + ctx.FindObjectsFunc = func(pkcs11.SessionHandle, int) ([]pkcs11.ObjectHandle, bool, error) { + return []pkcs11.ObjectHandle{1}, false, nil + } + ctx.FindObjectsFinalFunc = func(pkcs11.SessionHandle) error { + return nil + } + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return nil, errors.New("broken") + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key type failed") + + // test newSigner fails when GetAttributeValue returns no attributes + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return nil, nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key type returned no attributes") + + // test newSigner fails when FindObject for public key handle fails + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC)}, nil + } + ctx.FindObjectsInitFunc = func(_ pkcs11.SessionHandle, tmpl []*pkcs11.Attribute) error { + if bytes.Equal(tmpl[0].Value, []byte{2, 0, 0, 0, 0, 0, 0, 0}) { + return errors.New("broken") + } + return nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when FindObject for public key handle failed") + + // test newSigner fails when FindObject for private key returns unknown CKA_KEY_TYPE + ctx.FindObjectsInitFunc = func(_ pkcs11.SessionHandle, tmpl []*pkcs11.Attribute) error { + return nil + } + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{2, 0, 0, 0, 0, 0, 0, 0})}, nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when GetAttributeValue for private key returned unknown key type") + + // test newSigner fails when GetRSAPublicKey fails + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{0, 0, 0, 0, 0, 0, 0, 0})}, nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when GetRSAPublicKey fails") + + // test newSigner fails when GetECDSAPublicKey fails + ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + return []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{3, 0, 0, 0, 0, 0, 0, 0})}, nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertError(t, err, "newSigner didn't fail when GetECDSAPublicKey fails") + + // test newSigner works when everything... works + ctx.GetAttributeValueFunc = func(_ pkcs11.SessionHandle, _ pkcs11.ObjectHandle, attrs []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) { + var returns []*pkcs11.Attribute + for _, attr := range attrs { + switch attr.Type { + case pkcs11.CKA_KEY_TYPE: + returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, []byte{0, 0, 0, 0, 0, 0, 0, 0})) + case pkcs11.CKA_PUBLIC_EXPONENT: + returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 2, 3})) + case pkcs11.CKA_MODULUS: + returns = append(returns, pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{4, 5, 6})) + default: + return nil, errors.New("GetAttributeValue got unexpected attribute type") + } + } + return returns, nil + } + _, err = s.NewSigner("label", []byte{255, 255}) + test.AssertNotError(t, err, "newSigner failed when everything worked properly") +} + +func TestX509Signer(t *testing.T) { + s, ctx := NewSessionWithMock() + + // test that x509Signer.Sign properly converts the PKCS#11 format signature to + // the RFC 5480 format signature + ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error { + return nil + } + tk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + test.AssertNotError(t, err, "Failed to generate test key") + ctx.SignFunc = func(_ pkcs11.SessionHandle, digest []byte) ([]byte, error) { + r, s, err := ecdsa.Sign(rand.Reader, tk, digest[:]) + if err != nil { + return nil, err + } + rBytes := r.Bytes() + sBytes := s.Bytes() + // http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html + // Section 2.3.1: EC Signatures + // "If r and s have different octet length, the shorter of both must be padded with + // leading zero octets such that both have the same octet length." + switch { + case len(rBytes) < len(sBytes): + padding := make([]byte, len(sBytes)-len(rBytes)) + rBytes = append(padding, rBytes...) + case len(rBytes) > len(sBytes): + padding := make([]byte, len(rBytes)-len(sBytes)) + sBytes = append(padding, sBytes...) + } + return append(rBytes, sBytes...), nil + } + digest := sha256.Sum256([]byte("hello")) + signer := &X509Signer{session: s, keyType: ECDSAKey, pub: tk.Public()} + signature, err := signer.Sign(nil, digest[:], crypto.SHA256) + test.AssertNotError(t, err, "x509Signer.Sign failed") + + var rfcFormat struct { + R, S *big.Int + } + rest, err := asn1.Unmarshal(signature, &rfcFormat) + test.AssertNotError(t, err, "asn1.Unmarshal failed trying to parse signature") + test.Assert(t, len(rest) == 0, "Signature had trailing garbage") + verified := ecdsa.Verify(&tk.PublicKey, digest[:], rfcFormat.R, rfcFormat.S) + test.Assert(t, verified, "Failed to verify RFC format signature") + // For the sake of coverage + test.AssertEquals(t, signer.Public(), tk.Public()) +}