Move X509Signer from ceremony to pkcs11helpers. (#5004)
This commit is contained in:
parent
82e9e41597
commit
ccec6cfa19
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue