Add a PKCS#11 key generation tool (#3163)
Tested against master SoftHSMv2 and relevant hardware. Fixes #3125.
This commit is contained in:
parent
0882b86e6c
commit
29c95f0aed
|
|
@ -0,0 +1,219 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
var stringToCurve = map[string]*elliptic.CurveParams{
|
||||
elliptic.P224().Params().Name: elliptic.P224().Params(),
|
||||
elliptic.P256().Params().Name: elliptic.P256().Params(),
|
||||
elliptic.P384().Params().Name: elliptic.P384().Params(),
|
||||
elliptic.P521().Params().Name: elliptic.P521().Params(),
|
||||
}
|
||||
|
||||
// curveToOIDDER maps the name of the curves to their DER encoded OIDs
|
||||
var curveToOIDDER = map[string][]byte{
|
||||
elliptic.P224().Params().Name: []byte{6, 5, 43, 129, 4, 0, 33},
|
||||
elliptic.P256().Params().Name: []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7},
|
||||
elliptic.P384().Params().Name: []byte{6, 5, 43, 129, 4, 0, 34},
|
||||
elliptic.P521().Params().Name: []byte{6, 5, 43, 129, 4, 0, 35},
|
||||
}
|
||||
|
||||
// oidDERToCurve maps the hex of the DER encoding of the various curve OIDs to
|
||||
// the relevant curve parameters
|
||||
var oidDERToCurve = map[string]*elliptic.CurveParams{
|
||||
"06052B81040021": elliptic.P224().Params(),
|
||||
"06082A8648CE3D030107": elliptic.P256().Params(),
|
||||
"06052B81040022": elliptic.P384().Params(),
|
||||
"06052B81040023": elliptic.P521().Params(),
|
||||
}
|
||||
|
||||
var curveToHash = map[*elliptic.CurveParams]crypto.Hash{
|
||||
elliptic.P224().Params(): crypto.SHA256,
|
||||
elliptic.P256().Params(): crypto.SHA256,
|
||||
elliptic.P384().Params(): crypto.SHA384,
|
||||
elliptic.P521().Params(): crypto.SHA512,
|
||||
}
|
||||
|
||||
var hashToString = map[crypto.Hash]string{
|
||||
crypto.SHA256: "SHA-256",
|
||||
crypto.SHA384: "SHA-384",
|
||||
crypto.SHA512: "SHA-512",
|
||||
}
|
||||
|
||||
// ecArgs constructs the private and public key template attributes sent to the
|
||||
// device and specifies which mechanism should be used. curve determines which
|
||||
// type of key should be generated. compatMode is used to determine which
|
||||
// mechanism and attribute types should be used, for devices that implement
|
||||
// a pre-2.11 version of the PKCS#11 specification compatMode should be true.
|
||||
func ecArgs(label string, curve *elliptic.CurveParams, compatMode bool) generateArgs {
|
||||
encodedCurve := curveToOIDDER[curve.Name]
|
||||
log.Printf("\tEncoded curve parameters for %s: %X\n", curve.Params().Name, encodedCurve)
|
||||
var genMech, paramType uint
|
||||
if compatMode {
|
||||
genMech = pkcs11.CKM_ECDSA_KEY_PAIR_GEN
|
||||
paramType = pkcs11.CKA_ECDSA_PARAMS
|
||||
} else {
|
||||
genMech = pkcs11.CKM_EC_KEY_PAIR_GEN
|
||||
paramType = pkcs11.CKA_EC_PARAMS
|
||||
}
|
||||
return generateArgs{
|
||||
mechanism: []*pkcs11.Mechanism{
|
||||
pkcs11.NewMechanism(genMech, nil),
|
||||
},
|
||||
publicAttrs: []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
|
||||
pkcs11.NewAttribute(paramType, encodedCurve),
|
||||
},
|
||||
privateAttrs: []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
// Prevent attributes being retrieved
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
// Prevent the key being extracted from the device
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
// Allow the key to sign data
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ecPub extracts the generated public key, specified by the provided object
|
||||
// handle, and constructs an ecdsa.PublicKey. It also checks that the key is of
|
||||
// the correct curve type. For devices that implement a pre-2.11 version of the
|
||||
// PKCS#11 specification compatMode should be true.
|
||||
func ecPub(ctx PKCtx, session pkcs11.SessionHandle, object pkcs11.ObjectHandle, expectedCurve *elliptic.CurveParams, compatMode bool) (*ecdsa.PublicKey, error) {
|
||||
var paramType uint
|
||||
if compatMode {
|
||||
paramType = pkcs11.CKA_ECDSA_PARAMS
|
||||
} else {
|
||||
paramType = pkcs11.CKA_EC_PARAMS
|
||||
}
|
||||
// Retrieve the curve and public point for the generated public key
|
||||
attrs, err := ctx.GetAttributeValue(session, object, []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(paramType, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, nil),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve key attributes: %s", err)
|
||||
}
|
||||
|
||||
pubKey := &ecdsa.PublicKey{}
|
||||
gotCurve, gotPoint := false, false
|
||||
for _, a := range attrs {
|
||||
switch a.Type {
|
||||
case paramType:
|
||||
rCurve, present := oidDERToCurve[fmt.Sprintf("%X", a.Value)]
|
||||
if !present {
|
||||
return nil, errors.New("Unknown curve OID value returned")
|
||||
}
|
||||
pubKey.Curve = rCurve
|
||||
if pubKey.Curve != expectedCurve {
|
||||
return nil, errors.New("Returned EC parameters doesn't match expected curve")
|
||||
}
|
||||
gotCurve = true
|
||||
case pkcs11.CKA_EC_POINT:
|
||||
x, y := elliptic.Unmarshal(expectedCurve, a.Value)
|
||||
if x == nil {
|
||||
// http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html#_ftn1
|
||||
// PKCS#11 v2.20 specified that the CKA_EC_POINT was to be stored in a DER-encoded
|
||||
// OCTET STRING.
|
||||
var point asn1.RawValue
|
||||
_, err = asn1.Unmarshal(a.Value, &point)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshal returned CKA_EC_POINT: %s", err)
|
||||
}
|
||||
if len(point.Bytes) == 0 {
|
||||
return nil, errors.New("Invalid CKA_EC_POINT value returned, OCTET string is empty")
|
||||
}
|
||||
x, y = elliptic.Unmarshal(expectedCurve, point.Bytes)
|
||||
if x == nil {
|
||||
fmt.Println(point.Bytes)
|
||||
return nil, errors.New("Invalid CKA_EC_POINT value returned, point is malformed")
|
||||
}
|
||||
}
|
||||
pubKey.X, pubKey.Y = x, y
|
||||
gotPoint = true
|
||||
log.Printf("\tX: %X\n", pubKey.X.Bytes())
|
||||
log.Printf("\tY: %X\n", pubKey.Y.Bytes())
|
||||
}
|
||||
}
|
||||
if !gotPoint || !gotCurve {
|
||||
return nil, errors.New("Couldn't retrieve EC point and EC parameters")
|
||||
}
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// ecVerify verifies that the extracted public key corresponds with the generated
|
||||
// private key on the device, specified by the provided object handle, by signing
|
||||
// a nonce generated on the device and verifying the returned signature using the
|
||||
// public key.
|
||||
func ecVerify(ctx PKCtx, session pkcs11.SessionHandle, object pkcs11.ObjectHandle, pub *ecdsa.PublicKey) error {
|
||||
err := ctx.SignInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_ECDSA, nil)}, object)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize signing operation: %s", err)
|
||||
}
|
||||
nonce, err := getRandomBytes(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to construct nonce: %s", err)
|
||||
}
|
||||
log.Printf("\tConstructed nonce: %d (%X)\n", big.NewInt(0).SetBytes(nonce), nonce)
|
||||
hashFunc := curveToHash[pub.Curve.Params()].New()
|
||||
hashFunc.Write(nonce)
|
||||
hash := hashFunc.Sum(nil)
|
||||
log.Printf("\tMessage %s hash: %X\n", hashToString[curveToHash[pub.Curve.Params()]], hash)
|
||||
signature, err := ctx.Sign(session, hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sign data: %s", err)
|
||||
}
|
||||
log.Printf("\tMessage signature: %X\n", signature)
|
||||
r := big.NewInt(0).SetBytes(signature[:len(signature)/2])
|
||||
s := big.NewInt(0).SetBytes(signature[len(signature)/2:])
|
||||
if !ecdsa.Verify(pub, hash[:], r, s) {
|
||||
return errors.New("failed to verify ECDSA signature over test data")
|
||||
}
|
||||
log.Println("\tSignature verified")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ecGenerate is used to generate and verify a ECDSA key pair of the type
|
||||
// specified by curveStr and with the provided label. For devices that implement
|
||||
// a pre-2.11 version of the PKCS#11 specification compatMode should be true.
|
||||
// It returns the public part of the generated key pair as a ecdsa.PublicKey.
|
||||
func ecGenerate(ctx PKCtx, session pkcs11.SessionHandle, label, curveStr string, compatMode bool) (*ecdsa.PublicKey, error) {
|
||||
curve, present := stringToCurve[curveStr]
|
||||
if !present {
|
||||
return nil, fmt.Errorf("curve %q not supported", curveStr)
|
||||
}
|
||||
log.Printf("Generating ECDSA key with curve %s\n", curveStr)
|
||||
args := ecArgs(label, curve, compatMode)
|
||||
pub, priv, err := ctx.GenerateKeyPair(session, args.mechanism, args.publicAttrs, args.privateAttrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("Key generated")
|
||||
log.Println("Extracting public key")
|
||||
pk, err := ecPub(ctx, session, pub, curve, compatMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("Extracted public key")
|
||||
log.Println("Verifying public key")
|
||||
err = ecVerify(ctx, session, priv, pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("Key verified")
|
||||
return pk, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
func TestECPub(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// test attribute retrieval failing
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
_, err := ecPub(ctx, 0, 0, nil, false)
|
||||
test.AssertError(t, err, "ecPub didn't fail on GetAttributeValue error")
|
||||
|
||||
// test we fail to construct key with missing params and point
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, nil, false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with empty attribute list")
|
||||
|
||||
// test we fail to construct key with unknown curve
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{1, 2, 3}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P224().Params(), false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with unknown curve")
|
||||
|
||||
// test we fail to construct key with non-matching curve
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P224().Params(), false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with non-matching curve")
|
||||
|
||||
// test we fail to construct key with invalid EC point (invalid encoding)
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P256().Params(), false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with invalid EC point (invalid encoding)")
|
||||
|
||||
// test we fail to construct key with invalid EC point (empty octet string)
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 0}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P256().Params(), false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with invalid EC point (octet string, invalid contents)")
|
||||
// test we fail to construct key with invalid EC point (empty octet string)
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 4, 4, 1, 2, 3}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P256().Params(), false)
|
||||
test.AssertError(t, err, "ecPub didn't fail with invalid EC point (empty octet string)")
|
||||
|
||||
// test we don't fail with the correct attributes (traditional encoding)
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 5, 43, 129, 4, 0, 33}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 217, 225, 246, 210, 153, 134, 246, 104, 95, 79, 122, 206, 135, 241, 37, 114, 199, 87, 56, 167, 83, 56, 136, 174, 6, 145, 97, 239, 221, 49, 67, 148, 13, 126, 65, 90, 208, 195, 193, 171, 105, 40, 98, 132, 124, 30, 189, 215, 197, 178, 226, 166, 238, 240, 57, 215}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P224().Params(), false)
|
||||
test.AssertNotError(t, err, "ecPub failed with valid attributes (traditional encoding)")
|
||||
|
||||
// test we don't fail with the correct attributes (non-traditional encoding)
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 5, 43, 129, 4, 0, 33}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 57, 4, 217, 225, 246, 210, 153, 134, 246, 104, 95, 79, 122, 206, 135, 241, 37, 114, 199, 87, 56, 167, 83, 56, 136, 174, 6, 145, 97, 239, 221, 49, 67, 148, 13, 126, 65, 90, 208, 195, 193, 171, 105, 40, 98, 132, 124, 30, 189, 215, 197, 178, 226, 166, 238, 240, 57, 215}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecPub(ctx, 0, 0, elliptic.P224().Params(), false)
|
||||
test.AssertNotError(t, err, "ecPub failed with valid attributes (non-traditional encoding)")
|
||||
}
|
||||
|
||||
func TestECVerify(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// test SignInit failing
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return errors.New("yup")
|
||||
}
|
||||
err := ecVerify(ctx, 0, 0, nil)
|
||||
test.AssertError(t, err, "ecVerify didn't fail on SignInit error")
|
||||
|
||||
// test GenerateRandom failing
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return nil
|
||||
}
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
err = ecVerify(ctx, 0, 0, nil)
|
||||
test.AssertError(t, err, "ecVerify didn't fail on GenerateRandom error")
|
||||
|
||||
// test Sign failing
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
err = ecVerify(ctx, 0, 0, &ecdsa.PublicKey{Curve: elliptic.P256().Params()})
|
||||
test.AssertError(t, err, "ecVerify didn't fail on Sign error")
|
||||
|
||||
// test signature verification failing
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
tk, err := ecdsa.GenerateKey(elliptic.P256().Params(), rand.Reader)
|
||||
test.AssertNotError(t, err, "ecdsa.GenerateKey failed")
|
||||
err = ecVerify(ctx, 0, 0, &tk.PublicKey)
|
||||
test.AssertError(t, err, "ecVerify didn't fail on signature verification error")
|
||||
|
||||
// test we don't fail with valid signature
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
hash := sha256.Sum256([]byte{1, 2, 3})
|
||||
r, s, err := ecdsa.Sign(rand.Reader, tk, hash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(r.Bytes(), s.Bytes()...), nil
|
||||
}
|
||||
err = ecVerify(ctx, 0, 0, &tk.PublicKey)
|
||||
test.AssertNotError(t, err, "ecVerify failed with a valid signature")
|
||||
}
|
||||
|
||||
func TestECGenerate(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// Test ecGenerate fails with unknown curve
|
||||
_, err := ecGenerate(ctx, 0, "", "bad-curve", false)
|
||||
test.AssertError(t, err, "ecGenerate accepted unknown curve")
|
||||
|
||||
// Test ecGenerate fails when GenerateKeyPair fails
|
||||
ctx.GenerateKeyPairFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
|
||||
return 0, 0, errors.New("bad")
|
||||
}
|
||||
_, err = ecGenerate(ctx, 0, "", "P-256", false)
|
||||
test.AssertError(t, err, "ecGenerate didn't fail on GenerateKeyPair error")
|
||||
|
||||
// Test ecGenerate fails when ecPub fails
|
||||
ctx.GenerateKeyPairFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
|
||||
return 0, 0, nil
|
||||
}
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return nil, errors.New("bad")
|
||||
}
|
||||
_, err = ecGenerate(ctx, 0, "", "P-256", false)
|
||||
test.AssertError(t, err, "ecGenerate didn't fail on ecPub error")
|
||||
|
||||
// Test ecGenerate fails when ecVerify fails
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 71, 137, 101, 56, 44, 59, 172, 148, 152, 118, 61, 183, 215, 242, 168, 62, 77, 94, 246, 212, 164, 96, 210, 134, 87, 169, 142, 226, 189, 118, 137, 203, 117, 55, 2, 215, 177, 159, 42, 196, 33, 91, 92, 251, 98, 53, 137, 221, 167, 148, 25, 209, 1, 5, 90, 52, 43, 18, 7, 30, 33, 142, 228, 235}),
|
||||
}, nil
|
||||
}
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return errors.New("yup")
|
||||
}
|
||||
_, err = ecGenerate(ctx, 0, "", "P-256", false)
|
||||
test.AssertError(t, err, "ecGenerate didn't fail on ecVerify error")
|
||||
|
||||
// Test ecGenerate doesn't fail when everything works
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return nil
|
||||
}
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return []byte{82, 33, 179, 118, 118, 141, 38, 154, 5, 20, 207, 140, 127, 221, 237, 139, 222, 74, 189, 107, 84, 133, 127, 80, 226, 169, 25, 110, 141, 226, 196, 69, 202, 51, 204, 77, 22, 198, 104, 91, 74, 120, 221, 156, 122, 11, 43, 54, 106, 10, 165, 202, 229, 71, 44, 18, 113, 236, 213, 47, 208, 239, 198, 33}, nil
|
||||
}
|
||||
_, err = ecGenerate(ctx, 0, "", "P-256", false)
|
||||
test.AssertNotError(t, err, "ecGenerate didn't succeed when everything worked as expected")
|
||||
|
||||
// Test ecGenerate doesn't fail when everything works with compatibility mode
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ECDSA_PARAMS, []byte{6, 8, 42, 134, 72, 206, 61, 3, 1, 7}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, []byte{4, 71, 137, 101, 56, 44, 59, 172, 148, 152, 118, 61, 183, 215, 242, 168, 62, 77, 94, 246, 212, 164, 96, 210, 134, 87, 169, 142, 226, 189, 118, 137, 203, 117, 55, 2, 215, 177, 159, 42, 196, 33, 91, 92, 251, 98, 53, 137, 221, 167, 148, 25, 209, 1, 5, 90, 52, 43, 18, 7, 30, 33, 142, 228, 235}),
|
||||
}, nil
|
||||
}
|
||||
_, err = ecGenerate(ctx, 0, "", "P-256", true)
|
||||
test.AssertNotError(t, err, "ecGenerate didn't succeed when everything worked as expected")
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
// gen-key is a tool for generating RSA or ECDSA keys on a HSM using PKCS#11.
|
||||
// After generating the key pair it attempts to extract and construct the public
|
||||
// key and verifies a test message that was signed using the generated private
|
||||
// key. Any action it takes should be thoroughly logged and documented.
|
||||
//
|
||||
// When generating a key this tool follows the following steps:
|
||||
// 1. Constructs templates for the private and public keys consisting
|
||||
// of the appropriate PKCS#11 attributes.
|
||||
// 2. Executes a PKCS#11 GenerateKeyPair operation with the constructed
|
||||
// templates and either CKM_RSA_PKCS_KEY_PAIR_GEN or CKM_EC_KEY_PAIR_GEN
|
||||
// (or CKM_ECDSA_KEY_PAIR_GEN for pre-PKCS#11 v2.11 devices).
|
||||
// 3. Extracts the public key components from the returned public key object
|
||||
// handle and construct a Golang public key object from them.
|
||||
// 4. Generates 4 bytes of random data from the HSM using a PKCS#11 GenerateRandom
|
||||
// operation.
|
||||
// 5. Signs the random data with the private key object handle using a PKCS#11
|
||||
// SignInit/Sign operation.
|
||||
// 6. Verifies the returned signature of the random data with the constructed
|
||||
// public key.
|
||||
// 7. Marshals the public key into a PEM public key object and print it to STDOUT.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
type generateArgs struct {
|
||||
mechanism []*pkcs11.Mechanism
|
||||
privateAttrs []*pkcs11.Attribute
|
||||
publicAttrs []*pkcs11.Attribute
|
||||
}
|
||||
|
||||
type PKCtx interface {
|
||||
GenerateKeyPair(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error)
|
||||
GetAttributeValue(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error)
|
||||
SignInit(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error
|
||||
Sign(pkcs11.SessionHandle, []byte) ([]byte, error)
|
||||
GenerateRandom(pkcs11.SessionHandle, int) ([]byte, error)
|
||||
}
|
||||
|
||||
func getRandomBytes(ctx PKCtx, session pkcs11.SessionHandle) ([]byte, error) {
|
||||
r, err := ctx.GenerateRandom(session, 4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func initialize(module string, slot uint, pin string) (PKCtx, pkcs11.SessionHandle, error) {
|
||||
ctx := pkcs11.New(module)
|
||||
if ctx == nil {
|
||||
return nil, 0, errors.New("failed to load module")
|
||||
}
|
||||
err := ctx.Initialize()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("couldn't initialize context: %s", err)
|
||||
}
|
||||
|
||||
session, err := ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("couldn't open session: %s", err)
|
||||
}
|
||||
|
||||
err = ctx.Login(session, pkcs11.CKU_USER, pin)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("couldn't login: %s", err)
|
||||
}
|
||||
|
||||
return ctx, session, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
module := flag.String("module", "", "PKCS#11 module to use")
|
||||
keyType := flag.String("type", "", "Type of key to generate (RSA or ECDSA)")
|
||||
slot := flag.Uint("slot", 0, "Slot to generate key in")
|
||||
pin := flag.String("pin", "", "PIN for slot")
|
||||
label := flag.String("label", "", "Key label")
|
||||
rsaModLen := flag.Uint("modulus-bits", 0, "Size of RSA modulus in bits. Only used if --type=RSA")
|
||||
rsaExp := flag.Uint("public-exponent", 65537, "Public RSA exponent. Only used if --type=RSA")
|
||||
ecdsaCurve := flag.String("curve", "", "Type of ECDSA curve to use (P-224, P-256, P-384, P-521). Only used if --type=ECDSA")
|
||||
compatMode := flag.Bool("compat-mode", false, "Use pre PKCS#11 v2.11 style ECDSA parameters. Only used if --type=ECDSA")
|
||||
flag.Parse()
|
||||
|
||||
if *module == "" {
|
||||
log.Fatal("--module is required")
|
||||
}
|
||||
if *keyType == "" {
|
||||
log.Fatal("--type is required")
|
||||
}
|
||||
if *keyType != "RSA" && *keyType != "ECDSA" {
|
||||
log.Fatal("--type may only be RSA or ECDSA")
|
||||
}
|
||||
if *pin == "" {
|
||||
log.Fatal("--pin is required")
|
||||
}
|
||||
if *label == "" {
|
||||
log.Fatal("--label is required")
|
||||
}
|
||||
|
||||
ctx, session, err := initialize(*module, *slot, *pin)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to setup session and PKCS#11 context: %s", err)
|
||||
}
|
||||
|
||||
var pubKey interface{}
|
||||
switch *keyType {
|
||||
case "RSA":
|
||||
if *rsaModLen == 0 {
|
||||
log.Fatal("--modulus-bits is required")
|
||||
}
|
||||
pubKey, err = rsaGenerate(ctx, session, *label, *rsaModLen, *rsaExp)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate RSA key pair: %s", err)
|
||||
}
|
||||
case "ECDSA":
|
||||
if *ecdsaCurve == "" {
|
||||
log.Fatal("--ecdsaCurve is required")
|
||||
}
|
||||
pubKey, err = ecGenerate(ctx, session, *label, *ecdsaCurve, *compatMode)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate ECDSA key pair: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
der, err := x509.MarshalPKIXPublicKey(pubKey)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal public key: %s", err)
|
||||
}
|
||||
|
||||
err = pem.Encode(os.Stdout, &pem.Block{Type: "PUBLIC KEY", Bytes: der})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode public key as PEM object: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import "github.com/miekg/pkcs11"
|
||||
|
||||
type mockCtx struct {
|
||||
GenerateKeyPairFunc func(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error)
|
||||
GetAttributeValueFunc func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error)
|
||||
SignInitFunc func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error
|
||||
SignFunc func(pkcs11.SessionHandle, []byte) ([]byte, error)
|
||||
GenerateRandomFunc func(pkcs11.SessionHandle, int) ([]byte, error)
|
||||
}
|
||||
|
||||
func (mc mockCtx) GenerateKeyPair(s pkcs11.SessionHandle, m []*pkcs11.Mechanism, a1 []*pkcs11.Attribute, a2 []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
|
||||
return mc.GenerateKeyPairFunc(s, m, a1, a2)
|
||||
}
|
||||
|
||||
func (mc mockCtx) GetAttributeValue(s pkcs11.SessionHandle, o pkcs11.ObjectHandle, a []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return mc.GetAttributeValueFunc(s, o, a)
|
||||
}
|
||||
func (mc mockCtx) SignInit(s pkcs11.SessionHandle, m []*pkcs11.Mechanism, o pkcs11.ObjectHandle) error {
|
||||
return mc.SignInitFunc(s, m, o)
|
||||
}
|
||||
func (mc mockCtx) Sign(s pkcs11.SessionHandle, m []byte) ([]byte, error) {
|
||||
return mc.SignFunc(s, m)
|
||||
}
|
||||
func (mc mockCtx) GenerateRandom(s pkcs11.SessionHandle, c int) ([]byte, error) {
|
||||
return mc.GenerateRandomFunc(s, c)
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// rsaArgs constructs the private and public key template attributes sent to the
|
||||
// device and specifies which mechanism should be used. modulusLen specifies the
|
||||
// length of the modulus to be generated on the device in bits and exponent
|
||||
// specifies the public exponent that should be used.
|
||||
func rsaArgs(label string, modulusLen, exponent uint) generateArgs {
|
||||
// Encode as unpadded big endian encoded byte slice
|
||||
expSlice := big.NewInt(int64(exponent)).Bytes()
|
||||
log.Printf("\tEncoded public exponent (%d) as: %0X\n", exponent, expSlice)
|
||||
return generateArgs{
|
||||
mechanism: []*pkcs11.Mechanism{
|
||||
pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil),
|
||||
},
|
||||
publicAttrs: []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
// Allow the key to verify signatures
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
|
||||
// Set requested modulus length
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, modulusLen),
|
||||
// Set requested public exponent
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, expSlice),
|
||||
},
|
||||
privateAttrs: []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
// Prevent attributes being retrieved
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
// Prevent the key being extracted from the device
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
// Allow the key to create signatures
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rsaPub extracts the generated public key, specified by the provided object
|
||||
// handle, and constructs a rsa.PublicKey. It also checks that the key has the
|
||||
// correct length modulus and that the public exponent is what was requested in
|
||||
// the public key template.
|
||||
func rsaPub(ctx PKCtx, session pkcs11.SessionHandle, object pkcs11.ObjectHandle, modulusLen, exponent uint) (*rsa.PublicKey, error) {
|
||||
// Retrieve the public exponent and modulus for the generated public key
|
||||
attrs, err := ctx.GetAttributeValue(session, object, []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve key attributes: %s", err)
|
||||
}
|
||||
|
||||
// Attempt to build the public key from the retrieved attributes
|
||||
pubKey := &rsa.PublicKey{}
|
||||
gotMod, gotExp := false, false
|
||||
for _, a := range attrs {
|
||||
switch a.Type {
|
||||
case pkcs11.CKA_PUBLIC_EXPONENT:
|
||||
pubKey.E = int(big.NewInt(0).SetBytes(a.Value).Int64())
|
||||
// Check the provided public exponent was used
|
||||
if pubKey.E != int(exponent) {
|
||||
return nil, errors.New("Returned CKA_PUBLIC_EXPONENT doesn't match expected exponent")
|
||||
}
|
||||
gotExp = true
|
||||
log.Printf("\tPublic exponent: %d\n", pubKey.E)
|
||||
case pkcs11.CKA_MODULUS:
|
||||
pubKey.N = big.NewInt(0).SetBytes(a.Value)
|
||||
// Check the right length modulus was generated on the device
|
||||
if pubKey.N.BitLen() != int(modulusLen) {
|
||||
return nil, errors.New("Returned CKA_MODULUS isn't of the expected bit length")
|
||||
}
|
||||
gotMod = true
|
||||
log.Printf("\tModulus: (%d bits) %X\n", pubKey.N.BitLen(), pubKey.N.Bytes())
|
||||
}
|
||||
}
|
||||
// Fail if we are missing either the public exponent or modulus
|
||||
if !gotExp || !gotMod {
|
||||
return nil, errors.New("Couldn't retrieve modulus and exponent")
|
||||
}
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// rsaVerify verifies that the extracted public key corresponds with the generated
|
||||
// private key on the device, specified by the provided object handle, by signing
|
||||
// a nonce generated on the device and verifying the returned signature using the
|
||||
// public key.
|
||||
func rsaVerify(ctx PKCtx, session pkcs11.SessionHandle, object pkcs11.ObjectHandle, pub *rsa.PublicKey) error {
|
||||
// Initialize a signing operation
|
||||
err := ctx.SignInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)}, object)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to initialize signing operation: %s", err)
|
||||
}
|
||||
// PKCS#11 requires a hash identifier prefix to the message in order to determine which hash was used.
|
||||
// This prefix indicates SHA-256.
|
||||
input := []byte{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}
|
||||
nonce, err := getRandomBytes(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve nonce: %s", err)
|
||||
}
|
||||
log.Printf("\tConstructed nonce: %d (%X)\n", big.NewInt(0).SetBytes(nonce), nonce)
|
||||
hash := sha256.Sum256(nonce)
|
||||
log.Printf("\tMessage SHA-256 hash: %X\n", hash)
|
||||
input = append(input, hash[:]...)
|
||||
log.Println("\tSigning message")
|
||||
signature, err := ctx.Sign(session, input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to sign data: %s", err)
|
||||
}
|
||||
log.Printf("\tMessage signature: %X\n", signature)
|
||||
err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, hash[:], signature)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to verify signature: %s", err)
|
||||
}
|
||||
log.Println("\tSignature verified")
|
||||
return nil
|
||||
}
|
||||
|
||||
// rsaGenerate is used to generate and verify a RSA key pair of the size
|
||||
// specified by modulusLen and with the exponent specified by pubExponent.
|
||||
// It returns the public part of the generated key pair as a rsa.PublicKey.
|
||||
func rsaGenerate(ctx PKCtx, session pkcs11.SessionHandle, label string, modulusLen, pubExponent uint) (*rsa.PublicKey, error) {
|
||||
log.Printf("Generating RSA key with %d bit modulus and public exponent %d\n", modulusLen, pubExponent)
|
||||
args := rsaArgs(label, modulusLen, pubExponent)
|
||||
pub, priv, err := ctx.GenerateKeyPair(session, args.mechanism, args.publicAttrs, args.privateAttrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("Key generated")
|
||||
log.Println("Extracting public key")
|
||||
pk, err := rsaPub(ctx, session, pub, modulusLen, pubExponent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println("Extracted public key")
|
||||
log.Println("Verifying public key")
|
||||
err = rsaVerify(ctx, session, priv, pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pk, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
func TestRSAPub(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// test attribute retrieval failing
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
_, err := rsaPub(ctx, 0, 0, 0, 0)
|
||||
test.AssertError(t, err, "rsaPub didn't fail on GetAttributeValue error")
|
||||
|
||||
// test we fail to construct key with missing modulus and exp
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{}, nil
|
||||
}
|
||||
_, err = rsaPub(ctx, 0, 0, 0, 0)
|
||||
test.AssertError(t, err, "rsaPub didn't fail with empty attribute list")
|
||||
|
||||
// test we fail to construct key with non-matching exp
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
}, nil
|
||||
}
|
||||
_, err = rsaPub(ctx, 0, 0, 0, 0)
|
||||
test.AssertError(t, err, "rsaPub didn't fail with non-matching exp")
|
||||
|
||||
// test we fail to construct key with non-matching exp
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err = rsaPub(ctx, 0, 0, 16, 65537)
|
||||
test.AssertError(t, err, "rsaPub didn't fail with non-matching modulus size")
|
||||
|
||||
// test we don't fail with the correct attributes
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err = rsaPub(ctx, 0, 0, 8, 65537)
|
||||
test.AssertNotError(t, err, "rsaPub failed with valid attributes")
|
||||
}
|
||||
|
||||
func TestRSAVerify(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// test SignInit failing
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return errors.New("yup")
|
||||
}
|
||||
err := rsaVerify(ctx, 0, 0, nil)
|
||||
test.AssertError(t, err, "rsaVerify didn't fail on SignInit error")
|
||||
|
||||
// test GenerateRandom failing
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return nil
|
||||
}
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
err = rsaVerify(ctx, 0, 0, nil)
|
||||
test.AssertError(t, err, "rsaVerify didn't fail on GenerateRandom error")
|
||||
|
||||
// test Sign failing
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return nil, errors.New("yup")
|
||||
}
|
||||
err = rsaVerify(ctx, 0, 0, nil)
|
||||
test.AssertError(t, err, "rsaVerify didn't fail on Sign error")
|
||||
|
||||
// test signature verification failing
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
tk, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
test.AssertNotError(t, err, "rsa.GenerateKey failed")
|
||||
err = rsaVerify(ctx, 0, 0, &tk.PublicKey)
|
||||
test.AssertError(t, err, "rsaVerify didn't fail on signature verification error")
|
||||
|
||||
// test we don't fail with valid signature
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
hash := sha256.Sum256([]byte{1, 2, 3})
|
||||
return rsa.SignPKCS1v15(rand.Reader, tk, crypto.SHA256, hash[:])
|
||||
}
|
||||
err = rsaVerify(ctx, 0, 0, &tk.PublicKey)
|
||||
test.AssertNotError(t, err, "rsaVerify failed with a valid signature")
|
||||
}
|
||||
|
||||
func TestRSAGenerate(t *testing.T) {
|
||||
ctx := mockCtx{}
|
||||
|
||||
// Test rsaGenerate fails when GenerateKeyPair fails
|
||||
ctx.GenerateKeyPairFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
|
||||
return 0, 0, errors.New("bad")
|
||||
}
|
||||
_, err := rsaGenerate(ctx, 0, "", 1024, 65537)
|
||||
test.AssertError(t, err, "rsaGenerate didn't fail on GenerateKeyPair error")
|
||||
|
||||
// Test rsaGenerate fails when rsaPub fails
|
||||
ctx.GenerateKeyPairFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, []*pkcs11.Attribute, []*pkcs11.Attribute) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
|
||||
return 0, 0, nil
|
||||
}
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return nil, errors.New("bad")
|
||||
}
|
||||
_, err = rsaGenerate(ctx, 0, "", 1024, 65537)
|
||||
test.AssertError(t, err, "rsaGenerate didn't fail on rsaPub error")
|
||||
|
||||
// Test rsaGenerate fails when rsaVerify fails
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{217, 226, 207, 73, 127, 217, 136, 48, 203, 2, 12, 223, 251, 130, 143, 118, 13, 186, 82, 183, 220, 178, 158, 204, 19, 255, 121, 75, 243, 84, 118, 40, 128, 29, 11, 245, 43, 246, 217, 244, 166, 208, 36, 59, 69, 34, 142, 40, 22, 230, 195, 193, 111, 202, 186, 174, 233, 175, 140, 74, 19, 135, 191, 82, 27, 41, 123, 157, 174, 219, 38, 71, 19, 138, 28, 41, 48, 52, 142, 234, 196, 242, 51, 90, 204, 10, 235, 88, 150, 156, 89, 156, 199, 152, 173, 251, 88, 67, 138, 147, 86, 190, 236, 107, 190, 169, 53, 160, 219, 71, 147, 247, 230, 24, 188, 44, 61, 92, 106, 254, 125, 145, 233, 211, 76, 13, 159, 167}),
|
||||
}, nil
|
||||
}
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return errors.New("yup")
|
||||
}
|
||||
_, err = rsaGenerate(ctx, 0, "", 1024, 65537)
|
||||
test.AssertError(t, err, "rsaGenerate didn't fail on rsaVerify error")
|
||||
|
||||
// Test rsaGenerate doesn't fail when everything works
|
||||
ctx.SignInitFunc = func(pkcs11.SessionHandle, []*pkcs11.Mechanism, pkcs11.ObjectHandle) error {
|
||||
return nil
|
||||
}
|
||||
ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
ctx.SignFunc = func(pkcs11.SessionHandle, []byte) ([]byte, error) {
|
||||
return []byte{182, 42, 17, 237, 215, 151, 23, 254, 234, 219, 10, 119, 178, 76, 204, 254, 235, 67, 135, 83, 97, 134, 117, 38, 68, 115, 190, 250, 69, 200, 138, 225, 5, 188, 175, 45, 32, 179, 239, 145, 13, 168, 119, 75, 11, 171, 161, 220, 39, 185, 249, 87, 226, 132, 237, 82, 246, 187, 26, 232, 69, 86, 29, 12, 233, 8, 252, 59, 24, 194, 173, 74, 191, 101, 249, 108, 195, 240, 100, 28, 241, 70, 78, 236, 9, 136, 130, 218, 245, 195, 128, 80, 253, 42, 82, 99, 200, 115, 14, 75, 218, 176, 94, 98, 7, 226, 110, 24, 187, 108, 42, 144, 238, 244, 114, 153, 125, 3, 248, 129, 159, 51, 91, 26, 177, 118, 250, 79}, nil
|
||||
}
|
||||
_, err = rsaGenerate(ctx, 0, "", 1024, 65537)
|
||||
test.AssertNotError(t, err, "rsaGenerate didn't succeed when everything worked as expected")
|
||||
}
|
||||
Loading…
Reference in New Issue