mirror of
https://github.com/google/go-attestation.git
synced 2024-12-19 04:57:59 +00:00
Support RSA application keys (#218)
This commit is contained in:
parent
7f6fec6b36
commit
0b7298fb18
@ -26,7 +26,7 @@ type key interface {
|
||||
close(tpmBase) error
|
||||
marshal() ([]byte, error)
|
||||
certificationParameters() CertificationParameters
|
||||
sign(tpmBase, []byte) ([]byte, error)
|
||||
sign(tpmBase, []byte, crypto.PublicKey, crypto.SignerOpts) ([]byte, error)
|
||||
decrypt(tpmBase, []byte) ([]byte, error)
|
||||
blobs() ([]byte, []byte, error)
|
||||
}
|
||||
@ -48,7 +48,7 @@ type signer struct {
|
||||
|
||||
// Sign signs digest with the TPM-stored private signing key.
|
||||
func (s *signer) Sign(r io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
return s.key.sign(s.tpm, digest)
|
||||
return s.key.sign(s.tpm, digest, s.pub, opts)
|
||||
}
|
||||
|
||||
// Public returns the public key corresponding to the private signing key.
|
||||
|
@ -84,9 +84,23 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
|
||||
Size: 521,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RSA-1024",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 1024,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RSA-2048",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sk, err := tpm.NewKey(ak, nil)
|
||||
sk, err := tpm.NewKey(ak, test.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewKey() failed: %v", err)
|
||||
}
|
||||
@ -164,48 +178,131 @@ func TestTPM20KeySign(t *testing.T) {
|
||||
testKeySign(t, tpm)
|
||||
}
|
||||
|
||||
type simpleOpts struct {
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
func (o *simpleOpts) HashFunc() crypto.Hash {
|
||||
return o.Hash
|
||||
}
|
||||
|
||||
func testKeySign(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
|
||||
keyOpts *KeyConfig
|
||||
signOpts crypto.SignerOpts
|
||||
digest []byte
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
opts: nil,
|
||||
keyOpts: nil,
|
||||
signOpts: nil,
|
||||
digest: []byte("12345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "ECDSAP256-SHA256",
|
||||
opts: &KeyConfig{
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 256,
|
||||
},
|
||||
signOpts: nil,
|
||||
digest: []byte("12345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "ECDSAP384-SHA384",
|
||||
opts: &KeyConfig{
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 384,
|
||||
},
|
||||
signOpts: nil,
|
||||
digest: []byte("123456789012345678901234567890121234567890123456"),
|
||||
},
|
||||
{
|
||||
name: "ECDSAP521-SHA512",
|
||||
opts: &KeyConfig{
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 521,
|
||||
},
|
||||
signOpts: nil,
|
||||
digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PKCS1v15-SHA256",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &simpleOpts{
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
digest: []byte("12345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PKCS1v15-SHA384",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &simpleOpts{
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
digest: []byte("123456789012345678901234567890121234567890123456"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PKCS1v15-SHA512",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &simpleOpts{
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PSS-SHA256",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
digest: []byte("12345678901234567890123456789012"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PSS-SHA384",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
digest: []byte("123456789012345678901234567890121234567890123456"),
|
||||
},
|
||||
{
|
||||
name: "RSA2048-PSS-SHA512",
|
||||
keyOpts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
signOpts: &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sk, err := tpm.NewKey(ak, test.opts)
|
||||
sk, err := tpm.NewKey(ak, test.keyOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewKey() failed: %v", err)
|
||||
}
|
||||
@ -220,12 +317,16 @@ func testKeySign(t *testing.T, tpm *TPM) {
|
||||
if !ok {
|
||||
t.Fatalf("want crypto.Signer, got %T", priv)
|
||||
}
|
||||
sig, err := signer.Sign(rand.Reader, test.digest, nil)
|
||||
sig, err := signer.Sign(rand.Reader, test.digest, test.signOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("signer.Sign() failed: %v", err)
|
||||
}
|
||||
|
||||
if test.keyOpts == nil || test.keyOpts.Algorithm == ECDSA {
|
||||
verifyECDSA(t, pub, test.digest, sig)
|
||||
} else {
|
||||
verifyRSA(t, pub, test.digest, sig, test.signOpts)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -246,6 +347,23 @@ func verifyECDSA(t *testing.T, pub crypto.PublicKey, digest, sig []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func verifyRSA(t *testing.T, pub crypto.PublicKey, digest, sig []byte, opts crypto.SignerOpts) {
|
||||
t.Helper()
|
||||
pubRSA, ok := pub.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
t.Fatalf("want *rsa.PublicKey, got %T", pub)
|
||||
}
|
||||
if pss, ok := opts.(*rsa.PSSOptions); ok {
|
||||
if err := rsa.VerifyPSS(pubRSA, opts.HashFunc(), digest, sig, pss); err != nil {
|
||||
t.Fatalf("rsa.VerifyPSS(): %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := rsa.VerifyPKCS1v15(pubRSA, opts.HashFunc(), digest, sig); err != nil {
|
||||
t.Fatalf("signature verification failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20KeyOpts(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
@ -318,6 +436,22 @@ func testKeyOpts(t *testing.T, tpm *TPM) {
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
name: "RSA-1024",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 1024,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
name: "RSA-2048",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sk, err := tpm.NewKey(ak, test.opts)
|
||||
|
@ -25,10 +25,25 @@ import (
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
)
|
||||
|
||||
func TestCertificationParametersTPM20(t *testing.T) {
|
||||
s, tpm := setupSimulatedTPM(t)
|
||||
defer s.Close()
|
||||
func TestSimTPM20CertificationParameters(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testCertificationParameters(t, tpm)
|
||||
}
|
||||
|
||||
func TestTPM20CertificationParameters(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testCertificationParameters(t, tpm)
|
||||
}
|
||||
|
||||
func testCertificationParameters(t *testing.T, tpm *TPM) {
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -148,3 +163,109 @@ func TestCertificationParametersTPM20(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimTPM20KeyCertification(t *testing.T) {
|
||||
sim, tpm := setupSimulatedTPM(t)
|
||||
defer sim.Close()
|
||||
testKeyCertification(t, tpm)
|
||||
}
|
||||
|
||||
func TestTPM20KeyCertification(t *testing.T) {
|
||||
if !*testLocal {
|
||||
t.SkipNow()
|
||||
}
|
||||
tpm, err := OpenTPM(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenTPM() failed: %v", err)
|
||||
}
|
||||
defer tpm.Close()
|
||||
testKeyCertification(t, tpm)
|
||||
}
|
||||
|
||||
func testKeyCertification(t *testing.T, tpm *TPM) {
|
||||
ak, err := tpm.NewAK(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAK() failed: %v", err)
|
||||
}
|
||||
akAttestParams := ak.AttestationParameters()
|
||||
pub, err := tpm2.DecodePublic(akAttestParams.Public)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodePublic() failed: %v", err)
|
||||
}
|
||||
pk := &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}
|
||||
hash, err := pub.RSAParameters.Sign.Hash.Hash()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot access AK's hash function: %v", err)
|
||||
}
|
||||
verifyOpts := VerifyOpts{
|
||||
Public: pk,
|
||||
Hash: hash,
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
opts *KeyConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
opts: nil,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "ECDSAP256-SHA256",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 256,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "ECDSAP384-SHA384",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 384,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "ECDSAP521-SHA512",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: ECDSA,
|
||||
Size: 521,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "RSA-1024, key too short",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 1024,
|
||||
},
|
||||
err: cmpopts.AnyError,
|
||||
},
|
||||
{
|
||||
name: "RSA-2048",
|
||||
opts: &KeyConfig{
|
||||
Algorithm: RSA,
|
||||
Size: 2048,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
} {
|
||||
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()
|
||||
p := sk.CertificationParameters()
|
||||
err = p.Verify(verifyOpts)
|
||||
if test.err == nil && err == nil {
|
||||
return
|
||||
}
|
||||
if got, want := err, test.err; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
|
||||
t.Errorf("p.Verify() err = %v, want = %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,14 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
// Basic template for an RSA key signing outside-TPM objects. Other
|
||||
// fields are populated depending on the key creation options.
|
||||
rsaKeyTemplate = tpm2.Public{
|
||||
Type: tpm2.AlgRSA,
|
||||
NameAlg: tpm2.AlgSHA256,
|
||||
Attributes: tpm2.FlagSignerDefault ^ tpm2.FlagRestricted,
|
||||
RSAParameters: &tpm2.RSAParams{},
|
||||
}
|
||||
)
|
||||
|
||||
type tpm20Info struct {
|
||||
|
@ -17,9 +17,11 @@ package attest
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
@ -224,7 +226,11 @@ 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")
|
||||
tmpl = rsaKeyTemplate
|
||||
if opts.Size < 0 || opts.Size > 65535 { // basic sanity check
|
||||
return tmpl, fmt.Errorf("incorrect size parameter")
|
||||
}
|
||||
tmpl.RSAParameters.KeyBits = uint16(opts.Size)
|
||||
|
||||
case ECDSA:
|
||||
tmpl = ecdsaKeyTemplate
|
||||
@ -468,25 +474,56 @@ func (k *wrappedKey20) certificationParameters() CertificationParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) sign(tb tpmBase, digest []byte) ([]byte, error) {
|
||||
func (k *wrappedKey20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opts crypto.SignerOpts) ([]byte, error) {
|
||||
t, ok := tb.(*wrappedTPM20)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
||||
}
|
||||
sig, err := tpm2.Sign(t.rwc, k.hnd, "", digest, nil, nil)
|
||||
switch pub.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
return signECDSA(t.rwc, k.hnd, digest)
|
||||
case *rsa.PublicKey:
|
||||
return signRSA(t.rwc, k.hnd, digest, opts)
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported signing key type: %T", pub)
|
||||
}
|
||||
|
||||
func signECDSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte) ([]byte, error) {
|
||||
sig, err := tpm2.Sign(rw, key, "", digest, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("signing data: %v", err)
|
||||
return nil, fmt.Errorf("cannot sign: %v", err)
|
||||
}
|
||||
if sig.RSA != nil {
|
||||
return sig.RSA.Signature, nil
|
||||
if sig.ECC == nil {
|
||||
return nil, fmt.Errorf("expected ECDSA signature, got: %v", sig.Alg)
|
||||
}
|
||||
if sig.ECC != nil {
|
||||
return asn1.Marshal(struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}{sig.ECC.R, sig.ECC.S})
|
||||
}
|
||||
|
||||
func signRSA(rw io.ReadWriter, key tpmutil.Handle, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
h, err := tpm2.HashToAlgorithm(opts.HashFunc())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("incorrect hash algorithm: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported signature type: %v", sig.Alg)
|
||||
|
||||
scheme := &tpm2.SigScheme{
|
||||
Alg: tpm2.AlgRSASSA,
|
||||
Hash: h,
|
||||
}
|
||||
if _, ok := opts.(*rsa.PSSOptions); ok {
|
||||
scheme.Alg = tpm2.AlgRSAPSS
|
||||
}
|
||||
|
||||
sig, err := tpm2.Sign(rw, key, "", digest, nil, scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot sign: %v", err)
|
||||
}
|
||||
if sig.RSA == nil {
|
||||
return nil, fmt.Errorf("expected RSA signature, got: %v", sig.Alg)
|
||||
}
|
||||
return sig.RSA.Signature, nil
|
||||
}
|
||||
|
||||
func (k *wrappedKey20) decrypt(tb tpmBase, ctxt []byte) ([]byte, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user