Support RSA application keys (#218)

This commit is contained in:
Paweł Szałachowski 2021-05-20 11:15:09 -07:00 committed by GitHub
parent 7f6fec6b36
commit 0b7298fb18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 336 additions and 36 deletions

View File

@ -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.

View File

@ -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
digest []byte
name string
keyOpts *KeyConfig
signOpts crypto.SignerOpts
digest []byte
}{
{
name: "default",
opts: nil,
digest: []byte("12345678901234567890123456789012"),
name: "default",
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",
keyOpts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
},
signOpts: nil,
digest: []byte("123456789012345678901234567890121234567890123456"),
},
{
name: "ECDSAP521-SHA512",
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: "ECDSAP384-SHA384",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
name: "RSA2048-PKCS1v15-SHA384",
keyOpts: &KeyConfig{
Algorithm: RSA,
Size: 2048,
},
signOpts: &simpleOpts{
Hash: crypto.SHA384,
},
digest: []byte("123456789012345678901234567890121234567890123456"),
},
{
name: "ECDSAP521-SHA512",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 521,
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)
}
verifyECDSA(t, pub, test.digest, sig)
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)

View File

@ -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)
}
})
}
}

View File

@ -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 {

View File

@ -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})
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) {