Skip to content

Commit

Permalink
attest: Support "qualifyingData" when creating a new key. (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhsh authored Feb 3, 2025
1 parent c7aee80 commit dfabc9c
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 16 deletions.
4 changes: 4 additions & 0 deletions attest/application_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ type KeyConfig struct {
// If nil, the default SRK (i.e. RSA with handle 0x81000001) is assumed.
// Supported only by TPM 2.0 on Linux.
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.
Expand Down
16 changes: 16 additions & 0 deletions attest/application_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
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) {
sk, err := tpm.NewKey(ak, test.opts)
Expand Down
4 changes: 2 additions & 2 deletions attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ type ak interface {
activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error)
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.
Expand Down Expand Up @@ -185,7 +185,7 @@ func (k *AK) AttestationParameters() AttestationParameters {
// 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)
return k.ak.certify(tpm.tpm, handle, CertifyOpts{})
}

// AKConfig encapsulates parameters for minting keys.
Expand Down
14 changes: 10 additions & 4 deletions attest/certification.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ type ActivateOpts struct {
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.
// 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) {
Expand Down Expand Up @@ -241,9 +247,9 @@ func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts,
}, 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) {
// certify uses AK's handle, the passed user qualifying data, and the passed
// signature scheme to certify the key with the `hnd` handle.
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, qualifyingData []byte, scheme tpm2.SigScheme) (*CertificationParameters, error) {
pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
if err != nil {
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err)
Expand All @@ -252,7 +258,7 @@ func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigS
if err != nil {
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 {
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err)
}
Expand Down
40 changes: 37 additions & 3 deletions attest/certification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"crypto"
"crypto/rand"
"crypto/rsa"
"slices"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -224,6 +225,15 @@ func TestTPM20KeyCertificationECC(t *testing.T) {
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) {
ak, err := tpm.NewAK(&AKConfig{Algorithm: akAlg})
if err != nil {
Expand All @@ -247,9 +257,10 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
Hash: hash,
}
for _, test := range []struct {
name string
opts *KeyConfig
err error
name string
opts *KeyConfig
wantExtraData []byte
err error
}{
{
name: "default",
Expand Down Expand Up @@ -296,6 +307,26 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
},
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) {
sk, err := tpm.NewKey(ak, test.opts)
Expand All @@ -304,6 +335,9 @@ func testKeyCertification(t *testing.T, tpm *TPM, akAlg Algorithm) {
}
defer sk.Close()
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)
if test.err == nil && err == nil {
return
Expand Down
2 changes: 1 addition & 1 deletion attest/key_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
6 changes: 3 additions & 3 deletions attest/key_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (k *windowsKey12) attestationParameters() AttestationParameters {
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")
}

Expand Down Expand Up @@ -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)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
Expand All @@ -210,5 +210,5 @@ func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
}
return certify(tpm, hnd, akHnd, scheme)
return certify(tpm, hnd, akHnd, nil, scheme)
}
7 changes: 4 additions & 3 deletions attest/wrapped_tpm20.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) {
}()

// 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 {
return nil, fmt.Errorf("ak.Certify() failed: %v", err)
}
Expand Down Expand Up @@ -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)
if !ok {
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
Expand All @@ -600,7 +601,7 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
if err != nil {
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) {
Expand Down

0 comments on commit dfabc9c

Please sign in to comment.