attestation: bind user data to PCR state

This commit is contained in:
Thomas Tendyck 2023-02-12 17:26:54 +01:00 committed by Thomas Tendyck
parent 5e7dc0d7db
commit dd7d6334ba
3 changed files with 55 additions and 32 deletions

View File

@ -47,3 +47,7 @@ func (t *simulatedTPM) Close() error {
// never close the underlying simulated TPM to allow calling the TPMOpenFunc again // never close the underlying simulated TPM to allow calling the TPMOpenFunc again
return nil return nil
} }
func (*simulatedTPM) EventLog() ([]byte, error) {
return nil, nil
}

View File

@ -80,9 +80,8 @@ type AttestationDocument struct {
Attestation *attest.Attestation Attestation *attest.Attestation
// InstanceInfo is used to verify the provided public key. // InstanceInfo is used to verify the provided public key.
InstanceInfo []byte InstanceInfo []byte
// arbitrary data, signed by the TPM. // arbitrary data, quoted by the TPM.
UserData []byte UserData []byte
UserDataSignature []byte
} }
// Issuer handles issuing of TPM based attestation documents. // Issuer handles issuing of TPM based attestation documents.
@ -117,7 +116,8 @@ func (i *Issuer) Issue(userData []byte, nonce []byte) ([]byte, error) {
defer aK.Close() defer aK.Close()
// Create an attestation using the loaded key // Create an attestation using the loaded key
attestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: nonce}) extraData := makeExtraData(userData, nonce)
attestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: extraData})
if err != nil { if err != nil {
return nil, fmt.Errorf("creating attestation: %w", err) return nil, fmt.Errorf("creating attestation: %w", err)
} }
@ -128,17 +128,10 @@ func (i *Issuer) Issue(userData []byte, nonce []byte) ([]byte, error) {
return nil, fmt.Errorf("fetching instance info: %w", err) return nil, fmt.Errorf("fetching instance info: %w", err)
} }
// Sign user provided data using the loaded key
userDataSigned, err := aK.SignData(userData)
if err != nil {
return nil, fmt.Errorf("signing user data: %w", err)
}
attDoc := AttestationDocument{ attDoc := AttestationDocument{
Attestation: attestation, Attestation: attestation,
InstanceInfo: instanceInfo, InstanceInfo: instanceInfo,
UserData: userData, UserData: userData,
UserDataSignature: userDataSigned,
} }
return json.Marshal(attDoc) return json.Marshal(attDoc)
} }
@ -198,7 +191,7 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
if _, err := tpmServer.VerifyAttestation( if _, err := tpmServer.VerifyAttestation(
attDoc.Attestation, attDoc.Attestation,
tpmServer.VerifyOpts{ tpmServer.VerifyOpts{
Nonce: nonce, Nonce: makeExtraData(attDoc.UserData, nonce),
TrustedAKs: []crypto.PublicKey{aKP}, TrustedAKs: []crypto.PublicKey{aKP},
AllowSHA1: false, AllowSHA1: false,
}, },
@ -220,12 +213,6 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
} }
} }
// Verify signed user data
digest := sha256.Sum256(attDoc.UserData)
if err = v.verifyUserData(aKP, crypto.SHA256, digest[:], attDoc.UserDataSignature); err != nil {
return nil, fmt.Errorf("verifying signed user data: %w", err)
}
v.log.Infof("Successfully validated attestation document") v.log.Infof("Successfully validated attestation document")
return attDoc.UserData, nil return attDoc.UserData, nil
} }
@ -284,6 +271,13 @@ func GetSelectedMeasurements(open TPMOpenFunc, selection tpm2.PCRSelection) (mea
return m, nil return m, nil
} }
func makeExtraData(userData []byte, nonce []byte) []byte {
data := append([]byte{}, userData...)
data = append(data, nonce...)
digest := sha256.Sum256(data)
return digest[:]
}
// nopAttestationLogger is a no-op implementation of AttestationLogger. // nopAttestationLogger is a no-op implementation of AttestationLogger.
type nopAttestationLogger struct{} type nopAttestationLogger struct{}

View File

@ -66,13 +66,17 @@ func TestValidate(t *testing.T) {
} }
testExpectedPCRs := measurements.M{ testExpectedPCRs := measurements.M{
0: measurements.WithAllBytes(0x00, true), 0: measurements.WithAllBytes(0x00, false),
1: measurements.WithAllBytes(0x00, true), 1: measurements.WithAllBytes(0x00, false),
uint32(measurements.PCRIndexClusterID): measurements.WithAllBytes(0x00, false),
} }
warnLog := &testAttestationLogger{} warnLog := &testAttestationLogger{}
issuer := NewIssuer(newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo) tpmOpen, tpmCloser := tpmsim.NewSimulatedTPMOpenFunc()
validator := NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog) defer tpmCloser.Close()
issuer := NewIssuer(tpmOpen, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo)
validator := NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, nil)
nonce := []byte{1, 2, 3, 4} nonce := []byte{1, 2, 3, 4}
challenge := []byte("Constellation") challenge := []byte("Constellation")
@ -90,6 +94,24 @@ func TestValidate(t *testing.T) {
require.NoError(err) require.NoError(err)
require.Equal(challenge, out) require.Equal(challenge, out)
// validation must fail after bootstrapping (change of enforced PCR)
require.NoError(MarkNodeAsBootstrapped(tpmOpen, []byte{2}))
attDocBootstrappedRaw, err := issuer.Issue(challenge, nonce)
require.NoError(err)
_, err = validator.Validate(attDocBootstrappedRaw, nonce)
require.Error(err)
// userData must be bound to PCR state
attDocBootstrappedRaw, err = issuer.Issue([]byte{2, 3}, nonce)
require.NoError(err)
var attDocBootstrapped AttestationDocument
require.NoError(json.Unmarshal(attDocBootstrappedRaw, &attDocBootstrapped))
attDocBootstrapped.Attestation = attDoc.Attestation
attDocBootstrappedRaw, err = json.Marshal(attDocBootstrapped)
require.NoError(err)
_, err = validator.Validate(attDocBootstrappedRaw, nonce)
require.Error(err)
expectedPCRs := measurements.M{ expectedPCRs := measurements.M{
0: measurements.WithAllBytes(0x00, true), 0: measurements.WithAllBytes(0x00, true),
1: measurements.WithAllBytes(0x00, true), 1: measurements.WithAllBytes(0x00, true),
@ -128,6 +150,11 @@ func TestValidate(t *testing.T) {
nonce []byte nonce []byte
wantErr bool wantErr bool
}{ }{
"valid": {
validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require),
nonce: nonce,
},
"invalid nonce": { "invalid nonce": {
validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog), validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require), attDoc: mustMarshalAttestation(attDoc, require),
@ -137,10 +164,9 @@ func TestValidate(t *testing.T) {
"invalid signature": { "invalid signature": {
validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog), validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(AttestationDocument{ attDoc: mustMarshalAttestation(AttestationDocument{
Attestation: attDoc.Attestation, Attestation: attDoc.Attestation,
InstanceInfo: attDoc.InstanceInfo, InstanceInfo: attDoc.InstanceInfo,
UserData: []byte("wrong data"), UserData: []byte("wrong data"),
UserDataSignature: attDoc.UserDataSignature,
}, require), }, require),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,
@ -194,9 +220,8 @@ func TestValidate(t *testing.T) {
EventLog: attDoc.Attestation.EventLog, EventLog: attDoc.Attestation.EventLog,
InstanceInfo: attDoc.Attestation.InstanceInfo, InstanceInfo: attDoc.Attestation.InstanceInfo,
}, },
InstanceInfo: attDoc.InstanceInfo, InstanceInfo: attDoc.InstanceInfo,
UserData: attDoc.UserData, UserData: attDoc.UserData,
UserDataSignature: attDoc.UserDataSignature,
}, require), }, require),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,