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
4 changed files with 370 additions and 91 deletions

View File

@ -56,9 +56,29 @@ func (s *signer) Public() crypto.PublicKey {
return s.pub return s.pub
} }
// KeyConfig encapsulates parameters for minting keys. This type is defined // Algorithm indicates an asymmetric algorithm to be used.
// now (despite being empty) for future interface compatibility. 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 { 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. // Public returns the public key corresponding to the private key.

View File

@ -23,7 +23,9 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/asn1" "encoding/asn1"
"math/big" "math/big"
@ -53,61 +55,94 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
if err != nil { if err != nil {
t.Fatalf("NewAK() failed: %v", err) t.Fatalf("NewAK() failed: %v", err)
} }
sk, err := tpm.NewKey(ak, nil) for _, test := range []struct {
if err != nil { name string
t.Fatalf("NewKey() failed: %v", err) opts *KeyConfig
} }{
defer sk.Close() {
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() enc, err := sk.Marshal()
if err != nil { if err != nil {
t.Fatalf("sk.Marshal() failed: %v", err) t.Fatalf("sk.Marshal() failed: %v", err)
} }
if err := sk.Close(); err != nil { if err := sk.Close(); err != nil {
t.Fatalf("sk.Close() failed: %v", err) t.Fatalf("sk.Close() failed: %v", err)
} }
loaded, err := tpm.LoadKey(enc) loaded, err := tpm.LoadKey(enc)
if err != nil { if err != nil {
t.Fatalf("LoadKey() failed: %v", err) t.Fatalf("LoadKey() failed: %v", err)
} }
defer loaded.Close() defer loaded.Close()
k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20) k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20)
if !bytes.Equal(k1.public, k2.public) { if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded Key public blobs did not match.") t.Error("Original & loaded Key public blobs did not match.")
t.Logf("Original = %v", k1.public) t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public) t.Logf("Loaded = %v", k2.public)
} }
priv1, err := sk.Private(sk.Public()) priv1, err := sk.Private(sk.Public())
if err != nil { if err != nil {
t.Fatalf("sk.Private() failed: %v", err) t.Fatalf("sk.Private() failed: %v", err)
} }
signer1, ok := priv1.(crypto.Signer) signer1, ok := priv1.(crypto.Signer)
if !ok { if !ok {
t.Fatalf("want crypto.Signer, got %T", priv1) t.Fatalf("want crypto.Signer, got %T", priv1)
} }
pk1, err := x509.MarshalPKIXPublicKey(signer1.Public()) pk1, err := x509.MarshalPKIXPublicKey(signer1.Public())
if err != nil { if err != nil {
t.Fatalf("cannot marshal public key: %v", err) t.Fatalf("cannot marshal public key: %v", err)
} }
priv2, err := loaded.Private(loaded.Public()) priv2, err := loaded.Private(loaded.Public())
if err != nil { if err != nil {
t.Fatalf("loaded.Private() failed: %v", err) t.Fatalf("loaded.Private() failed: %v", err)
} }
signer2, ok := priv2.(crypto.Signer) signer2, ok := priv2.(crypto.Signer)
if !ok { if !ok {
t.Fatalf("want crypto.Signer, got %T", priv2) t.Fatalf("want crypto.Signer, got %T", priv2)
} }
pk2, err := x509.MarshalPKIXPublicKey(signer2.Public()) pk2, err := x509.MarshalPKIXPublicKey(signer2.Public())
if err != nil { if err != nil {
t.Fatalf("cannot marshal public key: %v", err) t.Fatalf("cannot marshal public key: %v", err)
} }
if !bytes.Equal(pk1, pk2) { if !bytes.Equal(pk1, pk2) {
t.Error("public keys do not match") t.Error("public keys do not match")
}
})
} }
} }
@ -134,33 +169,74 @@ func testKeySign(t *testing.T, tpm *TPM) {
if err != nil { if err != nil {
t.Fatalf("NewAK() failed: %v", err) t.Fatalf("NewAK() failed: %v", err)
} }
sk, err := tpm.NewKey(ak, nil) for _, test := range []struct {
if err != nil { name string
t.Fatalf("NewKey() failed: %v", err) opts *KeyConfig
} digest []byte
defer sk.Close() }{
{
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() pub := sk.Public()
priv, err := sk.Private(pub) priv, err := sk.Private(pub)
if err != nil { if err != nil {
t.Fatalf("sk.Private() failed: %v", err) t.Fatalf("sk.Private() failed: %v", err)
} }
signer, ok := priv.(crypto.Signer) signer, ok := priv.(crypto.Signer)
if !ok { if !ok {
t.Fatalf("want crypto.Signer, got %T", priv) t.Fatalf("want crypto.Signer, got %T", priv)
} }
digest := []byte("12345678901234567890123456789012") sig, err := signer.Sign(rand.Reader, test.digest, nil)
sig, err := signer.Sign(rand.Reader, digest, nil) if err != nil {
if err != nil { t.Fatalf("signer.Sign() failed: %v", err)
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 }{} parsed := struct{ R, S *big.Int }{}
_, err = asn1.Unmarshal(sig, &parsed) _, err := asn1.Unmarshal(sig, &parsed)
if err != nil { if err != nil {
t.Fatalf("signature parsing failed: %v", err) t.Fatalf("signature parsing failed: %v", err)
} }
pubECDSA, ok := pub.(*ecdsa.PublicKey) pubECDSA, ok := pub.(*ecdsa.PublicKey)
if !ok { if !ok {
t.Fatalf("want *ecdsa.PublicKey, got %T", pub) t.Fatalf("want *ecdsa.PublicKey, got %T", pub)
@ -169,3 +245,128 @@ func testKeySign(t *testing.T, tpm *TPM) {
t.Fatalf("ecdsa.Verify() failed") 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), ModulusRaw: make([]byte, 256),
}, },
} }
// Template for an ECC key for signing outside-TPM objects. // Basic template for an ECDSA key signing outside-TPM objects. Other
eccKeyTemplate = tpm2.Public{ // fields are populated depending on the key creation options.
ecdsaKeyTemplate = tpm2.Public{
Type: tpm2.AlgECC, Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagSignerDefault ^ tpm2.FlagRestricted, Attributes: tpm2.FlagSignerDefault ^ tpm2.FlagRestricted,
ECCParameters: &tpm2.ECCParams{ ECCParameters: &tpm2.ECCParams{
Sign: &tpm2.SigScheme{ Sign: &tpm2.SigScheme{
Alg: tpm2.AlgECDSA, Alg: tpm2.AlgECDSA,
Hash: tpm2.AlgSHA256,
},
CurveID: tpm2.CurveNISTP256,
Point: tpm2.ECPoint{
XRaw: make([]byte, 32),
YRaw: make([]byte, 32),
}, },
}, },
} }
@ -354,8 +348,15 @@ func (t *TPM) NewAK(opts *AKConfig) (*AK, error) {
return t.tpm.newAK(opts) 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) { 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) 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) { func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
// TODO(szp): TODO(jsonp): Abstract choice of hierarchy & parent.
k, ok := ak.ak.(*wrappedKey20) k, ok := ak.ak.(*wrappedKey20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedKey20, got: %T", k) 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 { 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) keyHandle, _, err := tpm2.Load(t.rwc, parent, "", pub, blob)
if err != nil {
return nil, fmt.Errorf("CreateKey() failed: %v", err)
}
keyHandle, _, err := tpm2.Load(t.rwc, srk, "", pub, blob)
if err != nil { if err != nil {
return nil, fmt.Errorf("Load() failed: %v", err) 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 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) { func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) {
sKey, err := deserializeKey(opaqueBlob, TPMVersion20) sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
if err != nil { if err != nil {