add ecdsa configuration options (#217)

Add configuration options for ECDSA key generation.
This commit is contained in:
Paweł Szałachowski 2021-05-19 11:32:54 -07:00 committed by GitHub
parent ee5bb94c43
commit 7f6fec6b36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 370 additions and 91 deletions

View File

@ -56,9 +56,29 @@ func (s *signer) Public() crypto.PublicKey {
return s.pub
}
// KeyConfig encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
// Algorithm indicates an asymmetric algorithm to be used.
type Algorithm string
// Algorithm types supported.
const (
ECDSA Algorithm = "ECDSA"
// TODO(szp): RSA is not supported yet
RSA Algorithm = "RSA"
)
// KeyConfig encapsulates parameters for minting keys.
type KeyConfig struct {
// Algorithm to be used, either RSA or ECDSA.
Algorithm Algorithm
// Size is used to specify the bit size of the key or elliptic curve. For
// example, '256' is used to specify curve P-256.
Size int
}
// defaultConfig is used when no other configuration is specified.
var defaultConfig = &KeyConfig{
Algorithm: ECDSA,
Size: 256,
}
// Public returns the public key corresponding to the private key.

View File

@ -23,7 +23,9 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"math/big"
@ -53,61 +55,94 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
for _, test := range []struct {
name string
opts *KeyConfig
}{
{
name: "default",
opts: nil,
},
{
name: "ECDSAP256-SHA256",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 256,
},
},
{
name: "ECDSAP384-SHA384",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
},
},
{
name: "ECDSAP521-SHA512",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 521,
},
},
} {
t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
enc, err := sk.Marshal()
if err != nil {
t.Fatalf("sk.Marshal() failed: %v", err)
}
if err := sk.Close(); err != nil {
t.Fatalf("sk.Close() failed: %v", err)
}
enc, err := sk.Marshal()
if err != nil {
t.Fatalf("sk.Marshal() failed: %v", err)
}
if err := sk.Close(); err != nil {
t.Fatalf("sk.Close() failed: %v", err)
}
loaded, err := tpm.LoadKey(enc)
if err != nil {
t.Fatalf("LoadKey() failed: %v", err)
}
defer loaded.Close()
loaded, err := tpm.LoadKey(enc)
if err != nil {
t.Fatalf("LoadKey() failed: %v", err)
}
defer loaded.Close()
k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20)
if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded Key public blobs did not match.")
t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public)
}
k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20)
if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded Key public blobs did not match.")
t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public)
}
priv1, err := sk.Private(sk.Public())
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer1, ok := priv1.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv1)
}
pk1, err := x509.MarshalPKIXPublicKey(signer1.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
priv1, err := sk.Private(sk.Public())
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer1, ok := priv1.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv1)
}
pk1, err := x509.MarshalPKIXPublicKey(signer1.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
priv2, err := loaded.Private(loaded.Public())
if err != nil {
t.Fatalf("loaded.Private() failed: %v", err)
}
signer2, ok := priv2.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv2)
}
pk2, err := x509.MarshalPKIXPublicKey(signer2.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
priv2, err := loaded.Private(loaded.Public())
if err != nil {
t.Fatalf("loaded.Private() failed: %v", err)
}
signer2, ok := priv2.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv2)
}
pk2, err := x509.MarshalPKIXPublicKey(signer2.Public())
if err != nil {
t.Fatalf("cannot marshal public key: %v", err)
}
if !bytes.Equal(pk1, pk2) {
t.Error("public keys do not match")
if !bytes.Equal(pk1, pk2) {
t.Error("public keys do not match")
}
})
}
}
@ -134,33 +169,74 @@ func testKeySign(t *testing.T, tpm *TPM) {
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
sk, err := tpm.NewKey(ak, nil)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
for _, test := range []struct {
name string
opts *KeyConfig
digest []byte
}{
{
name: "default",
opts: nil,
digest: []byte("12345678901234567890123456789012"),
},
{
name: "ECDSAP256-SHA256",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 256,
},
digest: []byte("12345678901234567890123456789012"),
},
{
name: "ECDSAP384-SHA384",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
},
digest: []byte("123456789012345678901234567890121234567890123456"),
},
{
name: "ECDSAP521-SHA512",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 521,
},
digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
},
} {
t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts)
if err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
defer sk.Close()
pub := sk.Public()
priv, err := sk.Private(pub)
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer, ok := priv.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv)
}
digest := []byte("12345678901234567890123456789012")
sig, err := signer.Sign(rand.Reader, digest, nil)
if err != nil {
t.Fatalf("signer.Sign() failed: %v", err)
}
pub := sk.Public()
priv, err := sk.Private(pub)
if err != nil {
t.Fatalf("sk.Private() failed: %v", err)
}
signer, ok := priv.(crypto.Signer)
if !ok {
t.Fatalf("want crypto.Signer, got %T", priv)
}
sig, err := signer.Sign(rand.Reader, test.digest, nil)
if err != nil {
t.Fatalf("signer.Sign() failed: %v", err)
}
verifyECDSA(t, pub, test.digest, sig)
})
}
}
func verifyECDSA(t *testing.T, pub crypto.PublicKey, digest, sig []byte) {
t.Helper()
parsed := struct{ R, S *big.Int }{}
_, err = asn1.Unmarshal(sig, &parsed)
_, err := asn1.Unmarshal(sig, &parsed)
if err != nil {
t.Fatalf("signature parsing failed: %v", err)
}
pubECDSA, ok := pub.(*ecdsa.PublicKey)
if !ok {
t.Fatalf("want *ecdsa.PublicKey, got %T", pub)
@ -169,3 +245,128 @@ func testKeySign(t *testing.T, tpm *TPM) {
t.Fatalf("ecdsa.Verify() failed")
}
}
func TestSimTPM20KeyOpts(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
testKeyOpts(t, tpm)
}
func TestTPM20KeyOpts(t *testing.T) {
if !*testLocal {
t.SkipNow()
}
tpm, err := OpenTPM(nil)
if err != nil {
t.Fatalf("OpenTPM() failed: %v", err)
}
defer tpm.Close()
testKeyOpts(t, tpm)
}
func testKeyOpts(t *testing.T, tpm *TPM) {
ak, err := tpm.NewAK(nil)
if err != nil {
t.Fatalf("NewAK() failed: %v", err)
}
for _, test := range []struct {
name string
opts *KeyConfig
err bool
}{
{
name: "wrong alg",
opts: &KeyConfig{
Algorithm: "fake alg",
},
err: true,
},
{
name: "wrong size",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 1234,
},
err: true,
},
{
name: "default",
opts: nil,
err: false,
},
{
name: "ECDSAP256",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 256,
},
err: false,
},
{
name: "ECDSAP384",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
},
err: false,
},
{
name: "ECDSAP521",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 521,
},
err: false,
},
} {
t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts)
if !test.err && err != nil {
t.Fatalf("NewKey() failed: %v", err)
}
if test.err {
if err == nil {
sk.Close()
t.Fatalf("NewKey(): expected err != nil")
}
return
}
defer sk.Close()
expected := test.opts
if expected == nil {
expected = defaultConfig
}
pub := sk.Public()
switch pub.(type) {
case *ecdsa.PublicKey:
if expected.Algorithm != ECDSA {
t.Errorf("incorrect key type generated, expected %q, got EC", expected.Algorithm)
}
sizeToCurve := map[int]elliptic.Curve{
256: elliptic.P256(),
384: elliptic.P384(),
521: elliptic.P521(),
}
expectedCurve, ok := sizeToCurve[expected.Size]
if !ok {
t.Fatalf("cannot match curve to key size %d", expected.Size)
}
curve := pub.(*ecdsa.PublicKey).Curve
if expectedCurve != curve {
t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, curve)
}
case *rsa.PublicKey:
if expected.Algorithm != RSA {
t.Errorf("incorrect key type, expected %q, got RSA", expected.Algorithm)
}
if pub.(*rsa.PublicKey).Size()*8 != expected.Size {
t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.(*rsa.PublicKey).Size()*8)
}
default:
t.Errorf("unsupported key type: %T", pub)
}
})
}
}

View File

@ -96,20 +96,14 @@ var (
ModulusRaw: make([]byte, 256),
},
}
// Template for an ECC key for signing outside-TPM objects.
eccKeyTemplate = tpm2.Public{
// Basic template for an ECDSA key signing outside-TPM objects. Other
// fields are populated depending on the key creation options.
ecdsaKeyTemplate = tpm2.Public{
Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagSignerDefault ^ tpm2.FlagRestricted,
ECCParameters: &tpm2.ECCParams{
Sign: &tpm2.SigScheme{
Alg: tpm2.AlgECDSA,
Hash: tpm2.AlgSHA256,
},
CurveID: tpm2.CurveNISTP256,
Point: tpm2.ECPoint{
XRaw: make([]byte, 32),
YRaw: make([]byte, 32),
Alg: tpm2.AlgECDSA,
},
},
}
@ -354,8 +348,15 @@ func (t *TPM) NewAK(opts *AKConfig) (*AK, error) {
return t.tpm.newAK(opts)
}
// NewKey creates an application key certified by the attestation key.
// NewKey creates an application key certified by the attestation key. If opts is nil
// then DefaultConfig is used.
func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) {
if opts == nil {
opts = defaultConfig
}
if opts.Algorithm == "" && opts.Size == 0 {
opts = defaultConfig
}
return t.tpm.newKey(ak, opts)
}

View File

@ -159,22 +159,17 @@ func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
}
func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
// TODO(szp): TODO(jsonp): Abstract choice of hierarchy & parent.
k, ok := ak.ak.(*wrappedKey20)
if !ok {
return nil, fmt.Errorf("expected *wrappedKey20, got: %T", k)
}
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
parent, blob, pub, creationData, err := createKey(t, opts)
if err != nil {
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
return nil, fmt.Errorf("cannot create key: %v", err)
}
blob, pub, creationData, _, _, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", eccKeyTemplate)
if err != nil {
return nil, fmt.Errorf("CreateKey() failed: %v", err)
}
keyHandle, _, err := tpm2.Load(t.rwc, srk, "", pub, blob)
keyHandle, _, err := tpm2.Load(t.rwc, parent, "", pub, blob)
if err != nil {
return nil, fmt.Errorf("Load() failed: %v", err)
}
@ -206,6 +201,68 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
return &Key{key: newWrappedKey20(keyHandle, blob, pub, creationData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil
}
func createKey(t *wrappedTPM20, opts *KeyConfig) (tpmutil.Handle, []byte, []byte, []byte, error) {
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil {
return 0, nil, nil, nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
tmpl, err := templateFromConfig(opts)
if err != nil {
return 0, nil, nil, nil, fmt.Errorf("incorrect key options: %v", err)
}
blob, pub, creationData, _, _, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", tmpl)
if err != nil {
return 0, nil, nil, nil, fmt.Errorf("CreateKey() failed: %v", err)
}
return srk, blob, pub, creationData, err
}
func templateFromConfig(opts *KeyConfig) (tpm2.Public, error) {
var tmpl tpm2.Public
switch opts.Algorithm {
case RSA:
return tmpl, fmt.Errorf("RSA keys are not implemented")
case ECDSA:
tmpl = ecdsaKeyTemplate
switch opts.Size {
case 256:
tmpl.NameAlg = tpm2.AlgSHA256
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA256
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP256
tmpl.ECCParameters.Point = tpm2.ECPoint{
XRaw: make([]byte, 32),
YRaw: make([]byte, 32),
}
case 384:
tmpl.NameAlg = tpm2.AlgSHA384
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA384
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP384
tmpl.ECCParameters.Point = tpm2.ECPoint{
XRaw: make([]byte, 48),
YRaw: make([]byte, 48),
}
case 521:
tmpl.NameAlg = tpm2.AlgSHA512
tmpl.ECCParameters.Sign.Hash = tpm2.AlgSHA512
tmpl.ECCParameters.CurveID = tpm2.CurveNISTP521
tmpl.ECCParameters.Point = tpm2.ECPoint{
XRaw: make([]byte, 65),
YRaw: make([]byte, 65),
}
default:
return tmpl, fmt.Errorf("unsupported key size: %v", opts.Size)
}
default:
return tmpl, fmt.Errorf("unsupported algorithm type: %q", opts.Algorithm)
}
return tmpl, nil
}
func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) {
sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
if err != nil {