attest: Support "qualifyingData" when creating a new key. (#401)

This commit is contained in:
zhsh 2025-02-03 13:43:19 +11:00 committed by GitHub
parent c7aee80c5d
commit dfabc9c919
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 77 additions and 16 deletions

View File

@ -76,6 +76,10 @@ type KeyConfig struct {
// If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed. // If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed.
// Supported only by TPM 2.0 on Linux. // Supported only by TPM 2.0 on Linux.
Parent *ParentKeyConfig Parent *ParentKeyConfig
// QualifyingData is an optional data that will be included into
// a TPM-generated signature of the minted key.
// It may contain any data chosen by the caller.
QualifyingData []byte
} }
// defaultConfig is used when no other configuration is specified. // defaultConfig is used when no other configuration is specified.

View File

@ -100,6 +100,22 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
Size: 2048, Size: 2048,
}, },
}, },
{
name: "QualifyingData-RSA",
opts: &KeyConfig{
Algorithm: RSA,
Size: 2048,
QualifyingData: []byte("qualifying data"),
},
},
{
name: "QualifyingData-ECDSA",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 256,
QualifyingData: []byte("qualifying data"),
},
},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts) sk, err := tpm.NewKey(ak, test.opts)

View File

@ -117,7 +117,7 @@ type ak interface {
activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error)
attestationParameters() AttestationParameters attestationParameters() AttestationParameters
certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) certify(tb tpmBase, handle interface{}, opts CertifyOpts) (*CertificationParameters, error)
} }
// AK represents a key which can be used for attestation. // AK represents a key which can be used for attestation.
@ -185,7 +185,7 @@ func (k *AK) AttestationParameters() AttestationParameters {
// key. Depending on the actual instantiation it can accept different handle // key. Depending on the actual instantiation it can accept different handle
// types (e.g., tpmutil.Handle on Linux or uintptr on Windows). // types (e.g., tpmutil.Handle on Linux or uintptr on Windows).
func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) { func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) {
return k.ak.certify(tpm.tpm, handle) return k.ak.certify(tpm.tpm, handle, CertifyOpts{})
} }
// AKConfig encapsulates parameters for minting keys. // AKConfig encapsulates parameters for minting keys.

View File

@ -83,6 +83,12 @@ type ActivateOpts struct {
VerifierKeyNameDigest *tpm2.HashValue VerifierKeyNameDigest *tpm2.HashValue
} }
// CertifyOpts specifies options for the key's certification.
type CertifyOpts struct {
// QualifyingData is the user provided qualifying data.
QualifyingData []byte
}
// NewActivateOpts creates options for use in generating an activation challenge for a certified key. // NewActivateOpts creates options for use in generating an activation challenge for a certified key.
// The computed hash is the name digest of the public key used to verify the certification of our key. // The computed hash is the name digest of the public key used to verify the certification of our key.
func NewActivateOpts(verifierPubKey tpm2.Public, ek crypto.PublicKey) (*ActivateOpts, error) { func NewActivateOpts(verifierPubKey tpm2.Public, ek crypto.PublicKey) (*ActivateOpts, error) {
@ -241,9 +247,9 @@ func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts,
}, nil }, nil
} }
// certify uses AK's handle and the passed signature scheme to certify the key // certify uses AK's handle, the passed user qualifying data, and the passed
// with the `hnd` handle. // signature scheme to certify the key with the `hnd` handle.
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigScheme) (*CertificationParameters, error) { func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, qualifyingData []byte, scheme tpm2.SigScheme) (*CertificationParameters, error) {
pub, _, _, err := tpm2.ReadPublic(tpm, hnd) pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
if err != nil { if err != nil {
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err) return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err)
@ -252,7 +258,7 @@ func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigS
if err != nil { if err != nil {
return nil, fmt.Errorf("could not encode public key: %v", err) return nil, fmt.Errorf("could not encode public key: %v", err)
} }
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, nil, scheme) att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, qualifyingData, scheme)
if err != nil { if err != nil {
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err) return nil, fmt.Errorf("tpm2.Certify() failed: %v", err)
} }

View File

@ -24,6 +24,7 @@ import (
"crypto" "crypto"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"slices"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -224,6 +225,15 @@ func TestTPM20KeyCertificationECC(t *testing.T) {
testKeyCertification(t, tpm, ECDSA) testKeyCertification(t, tpm, ECDSA)
} }
func extraData(t *testing.T, p CertificationParameters) []byte {
t.Helper()
ad, err := tpm2.DecodeAttestationData(p.CreateAttestation)
if err != nil {
t.Fatalf("failed to decode attestation data: %v", err)
}
return ad.ExtraData
}
func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) { func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg}) ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg})
if err != nil { if err != nil {
@ -249,6 +259,7 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
for _, test := range []struct { for _, test := range []struct {
name string name string
opts *KeyConfig opts *KeyConfig
wantExtraData []byte
err error err error
}{ }{
{ {
@ -296,6 +307,26 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
}, },
err: nil, err: nil,
}, },
{
name: "QualifyingData-RSA",
opts: &KeyConfig{
Algorithm: RSA,
Size: 2048,
QualifyingData: []byte("qualifying data"),
},
wantExtraData: []byte("qualifying data"),
err: nil,
},
{
name: "QualifyingData-ECDSA",
opts: &KeyConfig{
Algorithm: ECDSA,
Size: 384,
QualifyingData: []byte("qualifying data"),
},
wantExtraData: []byte("qualifying data"),
err: nil,
},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
sk, err := tpm.NewKey(ak, test.opts) sk, err := tpm.NewKey(ak, test.opts)
@ -304,6 +335,9 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
} }
defer sk.Close() defer sk.Close()
p := sk.CertificationParameters() p := sk.CertificationParameters()
if gotExtraData, wantExtraData := extraData(t, p), test.wantExtraData; !slices.Equal(gotExtraData, wantExtraData) {
t.Errorf("ExtraData got = %v, want = %v", gotExtraData, wantExtraData)
}
err = p.Verify(verifyOpts) err = p.Verify(verifyOpts)
if test.err == nil && err == nil { if test.err == nil && err == nil {
return return

View File

@ -96,6 +96,6 @@ func (k *trousersKey12) attestationParameters() AttestationParameters {
} }
} }
func (k *trousersKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { func (k *trousersKey12) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }

View File

@ -107,7 +107,7 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
Public: k.public, Public: k.public,
} }
} }
func (k *windowsKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { func (k *windowsKey12) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }
@ -185,7 +185,7 @@ func (k *windowsKey20) attestationParameters() AttestationParameters {
} }
} }
func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { func (k *windowsKey20) certify(tb tpmBase, handle interface{}, _ CertifyOpts) (*CertificationParameters, error) {
t, ok := tb.(*windowsTPM) t, ok := tb.(*windowsTPM)
if !ok { if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb) return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
@ -210,5 +210,5 @@ func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
Alg: tpm2.AlgRSASSA, Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1 Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
} }
return certify(tpm, hnd, akHnd, scheme) return certify(tpm, hnd, akHnd, nil, scheme)
} }

View File

@ -302,7 +302,8 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
}() }()
// Certify application key by AK // Certify application key by AK
cp, err := k.certify(t, keyHandle) certifyOpts := CertifyOpts{QualifyingData: opts.QualifyingData}
cp, err := k.certify(t, keyHandle, certifyOpts)
if err != nil { if err != nil {
return nil, fmt.Errorf("ak.Certify() failed: %v", err) return nil, fmt.Errorf("ak.Certify() failed: %v", err)
} }
@ -587,7 +588,7 @@ func sigSchemeFromPublicKey(pub []byte) (tpm2.SigScheme, error) {
} }
} }
func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { func (k *wrappedKey20) certify(tb tpmBase, handle interface{}, opts CertifyOpts) (*CertificationParameters, error) {
t, ok := tb.(*wrappedTPM20) t, ok := tb.(*wrappedTPM20)
if !ok { if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
@ -600,7 +601,7 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
if err != nil { if err != nil {
return nil, fmt.Errorf("get signature scheme: %v", err) return nil, fmt.Errorf("get signature scheme: %v", err)
} }
return certify(t.rwc, hnd, k.hnd, scheme) return certify(t.rwc, hnd, k.hnd, opts.QualifyingData, scheme)
} }
func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) { func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {