Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading