Add AK.Certify() and use CertifyEx() for certification (#210)

* replace CertifyCreation() by CertifyEx() to handle certification of objects for which we cannot extract CreationData
* add AK.Certify(handle) allowing to certify externally-created keys
This commit is contained in:
Paweł Szałachowski 2021-04-23 14:41:30 -07:00 committed by GitHub
parent e24a847d44
commit 6848928436
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 45 deletions

View File

@ -103,6 +103,7 @@ type ak interface {
activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error)
attestationParameters() AttestationParameters
certify(tb tpmBase, handle interface{}) (*CertificationParameters, error)
}
// AK represents a key which can be used for attestation.
@ -145,6 +146,14 @@ func (k *AK) AttestationParameters() AttestationParameters {
return k.ak.attestationParameters()
}
// Certify uses the attestation key to certify the key with `handle`. It returns
// certification parameters which allow to verify the properties of the attested
// key. Depending on the actual instantiation it can accept different handle
// types (e.g., tpmutil.Handle on Linux or uintptr on Windows).
func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) {
return k.ak.certify(tpm.tpm, handle)
}
// AKConfig encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type AKConfig struct {

View File

@ -20,8 +20,10 @@ import (
"crypto/rsa"
"errors"
"fmt"
"io"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
)
// secureCurves represents a set of secure elliptic curves. For now,
@ -32,7 +34,6 @@ var secureCurves = map[tpm2.EllipticCurve]bool{
tpm2.CurveNISTP521: true,
tpm2.CurveBNP256: true,
tpm2.CurveBNP638: true,
tpm2.CurveSM2P256: true,
}
// CertificationParameters encapsulates the inputs for certifying an application key.
@ -73,16 +74,12 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
if err != nil {
return fmt.Errorf("DecodePublic() failed: %v", err)
}
_, err = tpm2.DecodeCreationData(p.CreateData)
if err != nil {
return fmt.Errorf("DecodeCreationData() failed: %v", err)
}
att, err := tpm2.DecodeAttestationData(p.CreateAttestation)
if err != nil {
return fmt.Errorf("DecodeAttestationData() failed: %v", err)
}
if att.Type != tpm2.TagAttestCreation {
return fmt.Errorf("attestation does not apply to creation data, got tag %x", att.Type)
if att.Type != tpm2.TagAttestCertify {
return fmt.Errorf("attestation does not apply to certification data, got tag %x", att.Type)
}
switch pub.Type {
@ -98,18 +95,6 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
return fmt.Errorf("public key of alg 0x%x not supported", pub.Type)
}
// Compute & verify that the creation data matches the digest in the
// attestation structure.
nameHash, err := pub.NameAlg.Hash()
if err != nil {
return fmt.Errorf("HashConstructor() failed: %v", err)
}
h := nameHash.New()
h.Write(p.CreateData)
if !bytes.Equal(att.AttestedCreationInfo.OpaqueDigest, h.Sum(nil)) {
return errors.New("attestation refers to different public key")
}
// Make sure the key has sane parameters (e.g., attestation can be faked if an AK
// can be used for arbitrary signatures).
// We verify the following:
@ -136,19 +121,19 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
// Verify the attested creation name matches what is computed from
// the public key.
match, err := att.AttestedCreationInfo.Name.MatchesPublic(pub)
match, err := att.AttestedCertifyInfo.Name.MatchesPublic(pub)
if err != nil {
return err
}
if !match {
return errors.New("creation attestation refers to a different key")
return errors.New("certification refers to a different key")
}
// Check the signature over the attestation data verifies correctly.
// TODO: Support ECC certifying keys
pk, ok := opts.Public.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("Only RSA verification keys are supported")
return fmt.Errorf("only RSA verification keys are supported")
}
if !opts.Hash.Available() {
return fmt.Errorf("hash function is unavailable")
@ -171,3 +156,29 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error {
return nil
}
// certify uses AK's handle and the passed signature scheme to certify the key
// with the `hnd` handle.
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigScheme) (*CertificationParameters, error) {
pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
if err != nil {
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err)
}
public, err := pub.Encode()
if err != nil {
return nil, fmt.Errorf("could not encode public key: %v", err)
}
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, nil, scheme)
if err != nil {
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err)
}
signature, err := tpmutil.Pack(scheme.Alg, scheme.Hash, tpmutil.U16Bytes(sig))
if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
}
return &CertificationParameters{
Public: public,
CreateAttestation: att,
CreateSignature: signature,
}, nil
}

View File

@ -110,18 +110,6 @@ func TestCertificationParametersTPM20(t *testing.T) {
name: "modified Public",
p: &CertificationParameters{
Public: akAttestParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
opts: correctOpts,
err: cmpopts.AnyError,
},
{
name: "modified CreateData",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: []byte("unparsable"),
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
@ -132,7 +120,6 @@ func TestCertificationParametersTPM20(t *testing.T) {
name: "modified CreateAttestation",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: akAttestParams.CreateAttestation,
CreateSignature: skCertParams.CreateSignature,
},
@ -143,7 +130,6 @@ func TestCertificationParametersTPM20(t *testing.T) {
name: "modified CreateSignature",
p: &CertificationParameters{
Public: skCertParams.Public,
CreateData: skCertParams.CreateData,
CreateAttestation: skCertParams.CreateAttestation,
CreateSignature: akAttestParams.CreateSignature,
},

View File

@ -91,3 +91,7 @@ func (k *trousersKey12) attestationParameters() AttestationParameters {
UseTCSDActivationFormat: true,
}
}
func (k *trousersKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented")
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
)
// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
@ -110,6 +111,9 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
Public: k.public,
}
}
func (k *windowsKey12) certify(tb tpmBase, handle Handle) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented")
}
// windowsKey20 represents a key bound to a TPM 2.0.
type windowsKey20 struct {
@ -184,3 +188,31 @@ func (k *windowsKey20) attestationParameters() AttestationParameters {
CreateSignature: k.createSignature,
}
}
func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
t, ok := tb.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
}
h, ok := handle.(uintptr)
if !ok {
return nil, fmt.Errorf("expected uinptr, got %T", handle)
}
hnd, err := t.pcp.TPMKeyHandle(h)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}
akHnd, err := t.pcp.TPMKeyHandle(k.hnd)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}
tpm, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
}
scheme := tpm2.SigScheme{
Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
}
return certify(tpm, hnd, akHnd, scheme)
}

View File

@ -15,6 +15,7 @@
package attest
import (
"bytes"
"crypto"
"crypto/rsa"
"errors"
@ -169,7 +170,7 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", eccKeyTemplate)
blob, pub, creationData, _, _, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", eccKeyTemplate)
if err != nil {
return nil, fmt.Errorf("CreateKey() failed: %v", err)
}
@ -185,16 +186,15 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
}()
// Certify application key by AK
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, k.hnd, nil, creationHash, tpm2.SigScheme{tpm2.AlgRSASSA, tpm2.AlgSHA256, 0}, tix)
cp, err := k.certify(t, keyHandle)
if err != nil {
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
return nil, fmt.Errorf("ak.Certify() failed: %v", err)
}
// Pack the raw structure into a TPMU_SIGNATURE.
signature, err := tpmutil.Pack(tpm2.AlgRSASSA, tpm2.AlgSHA256, tpmutil.U16Bytes(sig))
if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
if !bytes.Equal(pub, cp.Public) {
return nil, fmt.Errorf("certified incorrect key, expected: %v, certified: %v", pub, cp.Public)
}
// Pack the raw structure into a TPMU_SIGNATURE.
tpmPub, err := tpm2.DecodePublic(pub)
if err != nil {
return nil, fmt.Errorf("decode public key: %v", err)
@ -203,7 +203,7 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
if err != nil {
return nil, fmt.Errorf("access public key: %v", err)
}
return &Key{key: newWrappedKey20(keyHandle, blob, pub, creationData, attestation, signature), pub: pubKey, tpm: t}, nil
return &Key{key: newWrappedKey20(keyHandle, blob, pub, creationData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil
}
func (t *wrappedTPM20) deserializeAndLoad(opaqueBlob []byte) (tpmutil.Handle, *serializedKey, error) {
@ -370,6 +370,22 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([
}, k.hnd, ekHnd, credential, secret)
}
func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
t, ok := tb.(*wrappedTPM20)
if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
}
hnd, ok := handle.(tpmutil.Handle)
if !ok {
return nil, fmt.Errorf("expected tpmutil.Handle, got %T", handle)
}
scheme := tpm2.SigScheme{
Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA256,
}
return certify(t.rwc, hnd, k.hnd, scheme)
}
func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
t, ok := tb.(*wrappedTPM20)
if !ok {
@ -390,7 +406,6 @@ func (k *wrappedKey20) attestationParameters() AttestationParameters {
func (k *wrappedKey20) certificationParameters() CertificationParameters {
return CertificationParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}