diff --git a/attest/attest.go b/attest/attest.go index 90c87cf..898168b 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -119,6 +119,9 @@ func (k *AIK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byt } // Quote returns a quote over the platform state, signed by the AIK. +// +// This is a low-level API. Consumers seeking to attest the state of the +// platform should use tpm.AttestPlatform() instead. func (k *AIK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) { return k.aik.quote(tpm.tpm, nonce, alg) } diff --git a/attest/example_test.go b/attest/example_test.go index 03c5f5a..384e35b 100644 --- a/attest/example_test.go +++ b/attest/example_test.go @@ -91,46 +91,22 @@ func ExampleAIK_credentialActivation() { } } -func ExampleAIK_quote() { - tpm, err := attest.OpenTPM(nil) - if err != nil { - log.Fatalf("Failed to open TPM: %v", err) - } - defer tpm.Close() - - // Create a new AIK. - aik, err := tpm.NewAIK(nil) - if err != nil { - log.Fatalf("Failed to create AIK: %v", err) - } - defer aik.Close(tpm) - - // The nonce would typically be provided by the server. - nonce := []byte{1, 2, 3, 4, 5, 6, 7, 8} - - // Perform the quote & gather information necessary to verify it. - quote, err := aik.Quote(tpm, nonce, attest.HashSHA1) - if err != nil { - log.Fatalf("Failed to generate quote: %v", err) - } - pcrs, err := tpm.PCRs(attest.HashSHA1) - if err != nil { - log.Fatalf("Failed to collect PCR values: %v", err) - } - log.Printf("quote = %+v", quote) - log.Printf("PCRs = %+v", pcrs) -} - func TestExampleAIK(t *testing.T) { if !*testExamples { t.SkipNow() } ExampleAIK() ExampleAIK_credentialActivation() - ExampleAIK_quote() } -func ExampleAIKPublic_Verify() { +func TestExampleTPM(t *testing.T) { + if !*testExamples { + t.SkipNow() + } + ExampleTPM_AttestPlatform() +} + +func ExampleTPM_AttestPlatform() { tpm, err := attest.OpenTPM(nil) if err != nil { log.Fatalf("Failed to open TPM: %v", err) @@ -147,30 +123,27 @@ func ExampleAIKPublic_Verify() { // The nonce would typically be provided by the server. nonce := []byte{1, 2, 3, 4, 5, 6, 7, 8} - // Perform the quote & gather information necessary to verify it. - quote, err := aik.Quote(tpm, nonce, attest.HashSHA256) + // Perform an attestation against the state of the plaform. Usually, you + // would pass a nil config, and the event log would be read from the + // platform. To ensure this example runs on platforms without event logs, + // we pass a fake EventLog value. + att, err := tpm.AttestPlatform(aik, nonce, &attest.PlatformAttestConfig{ + EventLog: []byte{0}, + }) if err != nil { - log.Fatalf("Failed to generate quote: %v", err) - } - pcrs, err := tpm.PCRs(attest.HashSHA256) - if err != nil { - log.Fatalf("Failed to collect PCR values: %v", err) + log.Fatalf("Failed to attest the platform state: %v", err) } - // Construct an AIKPublic struct from the parameters of the key. + // Construct an AIKPublic struct from the parameters of the key. This + // will be used to verify the quote signatures. pub, err := attest.ParseAIKPublic(tpm.Version(), aik.AttestationParameters().Public) if err != nil { log.Fatalf("Failed to parse AIK public: %v", err) } - if err := pub.Verify(*quote, pcrs, nonce); err != nil { - log.Fatalf("Verification failed: %v", err) + for i, q := range att.Quotes { + if err := pub.Verify(q, att.PCRs, nonce); err != nil { + log.Fatalf("quote[%d] verification failed: %v", i, err) + } } } - -func TestExampleAIKPublic(t *testing.T) { - if !*testExamples { - t.SkipNow() - } - ExampleAIKPublic_Verify() -} diff --git a/attest/tpm.go b/attest/tpm.go index 8b0d08d..cb2db1f 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -298,6 +298,9 @@ func (t *TPM) LoadAIK(opaqueBlob []byte) (*AIK, error) { } // MeasurementLog returns the present value of the System Measurement Log. +// +// This is a low-level API. Consumers seeking to attest the state of the +// platform should use tpm.AttestPlatform() instead. func (t *TPM) MeasurementLog() ([]byte, error) { return t.tpm.measurementLog() } @@ -310,9 +313,8 @@ func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) { // PCRs returns the present value of Platform Configuration Registers with // the given digest algorithm. // -// Use ParseEventLog to determine which algorithm to use to match the values -// present in the event log. It's not always guarenteed that a system with TPM -// 2.0 will extend PCRs with SHA256 digests. +// This is a low-level API. Consumers seeking to attest the state of the +// platform should use tpm.AttestPlatform() instead. func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { return t.tpm.pcrs(alg) } @@ -358,16 +360,36 @@ func (t *TPM) attestPlatform(aik *AIK, nonce []byte, eventLog []byte) (*Platform return &out, nil } +// PlatformAttestConfig configures how attestations are generated through +// tpm.AttestPlatform(). +type PlatformAttestConfig struct { + // If non-nil, the raw event log will be read from EventLog + // instead of being obtained from the running system. + EventLog []byte +} + // AttestPlatform computes the set of information necessary to attest the // state of the platform. For TPM 2.0 devices, AttestPlatform will attempt // to read both SHA1 & SHA256 PCR banks and quote both of them, so bugs in // platform firmware which break replay for one PCR bank can be mitigated // using the other. -func (t *TPM) AttestPlatform(aik *AIK, nonce []byte) (*PlatformParameters, error) { - el, err := t.MeasurementLog() - if err != nil { - return nil, fmt.Errorf("failed to read event log: %v", err) +// The provided config, if not nil, can be used to configure aspects of the +// platform attestation. +func (t *TPM) AttestPlatform(aik *AIK, nonce []byte, config *PlatformAttestConfig) (*PlatformParameters, error) { + if config == nil { + config = &PlatformAttestConfig{} } + + var el []byte + if config.EventLog != nil { + el = config.EventLog + } else { + var err error + if el, err = t.MeasurementLog(); err != nil { + return nil, fmt.Errorf("failed to read event log: %v", err) + } + } + return t.attestPlatform(aik, nonce, el) }