diff --git a/attest/attest.go b/attest/attest.go index 3e629d8..5f9ce7f 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -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 { diff --git a/attest/certification.go b/attest/certification.go index c3287e3..ed54120 100644 --- a/attest/certification.go +++ b/attest/certification.go @@ -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 +} diff --git a/attest/certification_test.go b/attest/certification_test.go index 5d48158..ee38fcd 100644 --- a/attest/certification_test.go +++ b/attest/certification_test.go @@ -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, }, diff --git a/attest/key_linux.go b/attest/key_linux.go index d61061f..af252b6 100644 --- a/attest/key_linux.go +++ b/attest/key_linux.go @@ -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") +} diff --git a/attest/key_windows.go b/attest/key_windows.go index e1f47b8..8962d76 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -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) +} diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index 68f3cf6..cec9c8b 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -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, }