mirror of
https://github.com/google/go-attestation.git
synced 2025-02-21 01:11:21 +00:00
add ecdsa configuration options (#217)
Add configuration options for ECDSA key generation.
This commit is contained in:
parent
ee5bb94c43
commit
7f6fec6b36
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user