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
return nil
}
func (*simulatedTPM) EventLog() ([]byte, error) {
return nil, nil
}

View File

@ -80,9 +80,8 @@ type AttestationDocument struct {
Attestation *attest.Attestation
// InstanceInfo is used to verify the provided public key.
InstanceInfo []byte
// arbitrary data, signed by the TPM.
// arbitrary data, quoted by the TPM.
UserData []byte
UserDataSignature []byte
}
// 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()
// 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 {
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)
}
// 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{
Attestation: attestation,
InstanceInfo: instanceInfo,
UserData: userData,
UserDataSignature: userDataSigned,
}
return json.Marshal(attDoc)
}
@ -198,7 +191,7 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
if _, err := tpmServer.VerifyAttestation(
attDoc.Attestation,
tpmServer.VerifyOpts{
Nonce: nonce,
Nonce: makeExtraData(attDoc.UserData, nonce),
TrustedAKs: []crypto.PublicKey{aKP},
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")
return attDoc.UserData, nil
}
@ -284,6 +271,13 @@ func GetSelectedMeasurements(open TPMOpenFunc, selection tpm2.PCRSelection) (mea
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.
type nopAttestationLogger struct{}

View File

@ -66,13 +66,17 @@ func TestValidate(t *testing.T) {
}
testExpectedPCRs := measurements.M{
0: measurements.WithAllBytes(0x00, true),
1: measurements.WithAllBytes(0x00, true),
0: measurements.WithAllBytes(0x00, false),
1: measurements.WithAllBytes(0x00, false),
uint32(measurements.PCRIndexClusterID): measurements.WithAllBytes(0x00, false),
}
warnLog := &testAttestationLogger{}
issuer := NewIssuer(newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo)
validator := NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog)
tpmOpen, tpmCloser := tpmsim.NewSimulatedTPMOpenFunc()
defer tpmCloser.Close()
issuer := NewIssuer(tpmOpen, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo)
validator := NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, nil)
nonce := []byte{1, 2, 3, 4}
challenge := []byte("Constellation")
@ -90,6 +94,24 @@ func TestValidate(t *testing.T) {
require.NoError(err)
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{
0: measurements.WithAllBytes(0x00, true),
1: measurements.WithAllBytes(0x00, true),
@ -128,6 +150,11 @@ func TestValidate(t *testing.T) {
nonce []byte
wantErr bool
}{
"valid": {
validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require),
nonce: nonce,
},
"invalid nonce": {
validator: NewValidator(testExpectedPCRs, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require),
@ -140,7 +167,6 @@ func TestValidate(t *testing.T) {
Attestation: attDoc.Attestation,
InstanceInfo: attDoc.InstanceInfo,
UserData: []byte("wrong data"),
UserDataSignature: attDoc.UserDataSignature,
}, require),
nonce: nonce,
wantErr: true,
@ -196,7 +222,6 @@ func TestValidate(t *testing.T) {
},
InstanceInfo: attDoc.InstanceInfo,
UserData: attDoc.UserData,
UserDataSignature: attDoc.UserDataSignature,
}, require),
nonce: nonce,
wantErr: true,