Refactor part 1: Refactor logic for keys into structs for each TPM/platform invariant. (#53)

* Refactor serialized keys into own structure, in preparation for making Key an interface.

* Refactor key logic into separate structures for each platform/TPMversion invariant.

* Implement review feedback
This commit is contained in:
Tom D 2019-07-19 13:05:18 -07:00 committed by GitHub
parent ed3b03ef7f
commit 90e37eacce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 637 additions and 322 deletions

View File

@ -18,8 +18,10 @@ package attest
import (
"crypto"
"errors"
"fmt"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm2"
)
// TPMVersion is used to configure a preference in
@ -52,18 +54,33 @@ type OpenConfig struct {
TPMVersion TPMVersion
}
// KeyEncoding indicates how an exported TPM key is represented.
type KeyEncoding uint8
// keyEncoding indicates how an exported TPM key is represented.
type keyEncoding uint8
func (e keyEncoding) String() string {
switch e {
case keyEncodingInvalid:
return "invalid"
case keyEncodingOSManaged:
return "os-managed"
case keyEncodingEncrypted:
return "encrypted"
case keyEncodingParameterized:
return "parameterized"
default:
return fmt.Sprintf("keyEncoding<%d>", int(e))
}
}
// Key encodings
const (
KeyEncodingInvalid KeyEncoding = iota
keyEncodingInvalid keyEncoding = iota
// Managed by the OS but loadable by name.
KeyEncodingOSManaged
keyEncodingOSManaged
// Key fully represented but in encrypted form.
KeyEncodingEncrypted
keyEncodingEncrypted
// Parameters stored, but key must be regenerated before use.
KeyEncodingParameterized
keyEncodingParameterized
)
// KeyPurpose indicates the intended use of the key. It is implied that
@ -77,6 +94,49 @@ const (
StorageKey
)
type aik interface {
Close(*TPM) error
Marshal() ([]byte, error)
ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
Parameters() AIKParameters
}
// AIK represents a key which can be used for attestation.
type AIK struct {
aik aik
}
// Close unloads the AIK from the system.
func (k *AIK) Close(t *TPM) error {
return k.aik.Close(t)
}
// Marshal encodes the AIK in a format that can be reloaded with tpm.LoadAIK().
// This method exists to allow consumers to store the key persistently and load
// it as a later time. Users SHOULD NOT attempt to interpret or extract values
// from this blob.
func (k *AIK) Marshal() ([]byte, error) {
return k.aik.Marshal()
}
// ActivateCredential decrypts the specified credential using the key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *AIK) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) {
return k.aik.ActivateCredential(tpm, in)
}
// Quote returns a quote over the platform state, signed by the AIK.
func (k *AIK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
return k.aik.Quote(tpm, nonce, alg)
}
// Parameters returns information about the AIK, typically used to generate
// a credential activation challenge.
func (k *AIK) Parameters() AIKParameters {
return k.aik.Parameters()
}
// MintOptions encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type MintOptions struct {
@ -111,6 +171,42 @@ type PlatformEK struct {
Public crypto.PublicKey
}
// AIKParameters describes information about an AIK. This information
// is typically used to generate an activation challenge.
type AIKParameters struct {
// Public represents the public key in a TPM-version specific encoding.
// For TPM 2.0 devices, this is encoded as a TPMT_PUBLIC structure.
// For TPM 1.2 devices, this is a TPM_PUBKEY structure, as defined in
// the TPM Part 2 Structures specification, available at
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011.pdf
Public []byte
// Subsequent fields are only populated for AIKs generated on a TPM
// implementing version 2.0 of the specification. The specific structures
// referenced for each field are defined in the TPM Revision 2, Part 2 -
// Structures specification, available here:
// https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
// CreateData represents the properties of a TPM 2.0 key. It is encoded
// as a TPMS_CREATION_DATA structure.
CreateData []byte
// CreateAttestation represents an assertion as to the details of the key.
// It is encoded as a TPMS_ATTEST structure.
CreateAttestation []byte
// CreateSignature represents a signature of the CreateAttestation structure.
// It is encoded as a TPMT_SIGNATURE structure.
CreateSignature []byte
}
// HashAlg identifies a hashing Algorithm.
type HashAlg uint8
// Valid hash algorithms.
var (
HashSHA1 = HashAlg(tpm2.AlgSHA1)
HashSHA256 = HashAlg(tpm2.AlgSHA256)
)
var (
defaultOpenConfig = &OpenConfig{}

View File

@ -86,16 +86,18 @@ func TestSimTPM20AIKCreateAndLoad(t *testing.T) {
t.Fatalf("aik.Close() failed: %v", err)
}
loaded, err := tpm.LoadKey(enc)
loaded, err := tpm.LoadAIK(enc)
if err != nil {
t.Fatalf("LoadKey() failed: %v", err)
}
defer loaded.Close(tpm)
if !bytes.Equal(loaded.Public, aik.Public) {
k1, k2 := aik.aik.(*key20), loaded.aik.(*key20)
if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded AIK public blobs did not match.")
t.Logf("Original = %v", aik.Public)
t.Logf("Loaded = %v", loaded.Public)
t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public)
}
}
@ -131,7 +133,7 @@ func TestSimTPM20ActivateCredential(t *testing.T) {
}
ek := chooseEKPub(t, EKs)
att, err := tpm2.DecodeAttestationData(aik.CreateAttestation)
att, err := tpm2.DecodeAttestationData(aik.aik.(*key20).createAttestation)
if err != nil {
t.Fatalf("tpm2.DecodeAttestationData() failed: %v", err)
}
@ -167,7 +169,7 @@ func TestSimTPM20Quote(t *testing.T) {
defer aik.Close(tpm)
nonce := []byte{1, 2, 3, 4, 5, 6, 7, 8}
quote, err := aik.Quote(tpm, nonce, tpm2.AlgSHA256)
quote, err := aik.Quote(tpm, nonce, HashSHA256)
if err != nil {
t.Fatalf("aik.Quote() failed: %v", err)
}

View File

@ -105,16 +105,18 @@ func TestAIKCreateAndLoad(t *testing.T) {
t.Fatalf("aik.Close() failed: %v", err)
}
loaded, err := tpm.LoadKey(enc)
loaded, err := tpm.LoadAIK(enc)
if err != nil {
t.Fatalf("LoadKey() failed: %v", err)
}
defer loaded.Close(tpm)
if !bytes.Equal(loaded.Public, aik.Public) {
k1, k2 := aik.aik.(*key20), loaded.aik.(*key20)
if !bytes.Equal(k1.public, k2.public) {
t.Error("Original & loaded AIK public blobs did not match.")
t.Logf("Original = %v", aik.Public)
t.Logf("Loaded = %v", loaded.Public)
t.Logf("Original = %v", k1.public)
t.Logf("Loaded = %v", k2.public)
}
}

View File

@ -22,7 +22,6 @@ import (
"testing"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tspi/verification"
)
@ -114,12 +113,8 @@ func TestMintAIK(t *testing.T) {
if err != nil {
t.Fatalf("MintAIK failed: %v", err)
}
if (aik.TPMVersion != TPMVersion12) ||
(aik.Purpose != AttestationKey) {
t.Error("aik does not match expected format")
}
t.Logf("aik blob: %x\naik pubkey: %x\n", aik.KeyBlob, aik.Public)
k := aik.aik.(*key12)
t.Logf("aik blob: %x\naik pubkey: %x\n", k.blob, k.public)
}
func TestTPMQuote(t *testing.T) {
@ -140,7 +135,7 @@ func TestTPMQuote(t *testing.T) {
t.Fatalf("MintAIK failed: %v", err)
}
quote, err := aik.Quote(tpm, nonce, tpm2.AlgSHA1)
quote, err := aik.Quote(tpm, nonce, HashSHA1)
if err != nil {
t.Fatalf("Quote failed: %v", err)
}
@ -187,7 +182,7 @@ func TestTPMActivateCredential(t *testing.T) {
}
ekcert := chooseEKCertRaw(t, EKs)
challenge.Credential, challenge.Secret, err = verification.GenerateChallenge(ekcert, aik.Public, nonce)
challenge.Credential, challenge.Secret, err = verification.GenerateChallenge(ekcert, aik.aik.(*key12).public, nonce)
if err != nil {
t.Fatalf("GenerateChallenge failed: %v", err)
}

156
attest/key_linux.go Normal file
View File

@ -0,0 +1,156 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// +build linux
package attest
import (
"fmt"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
"github.com/google/go-tspi/attestation"
)
// key12 represents a key bound to a TPM 1.2 device via tcsd.
type key12 struct {
blob []byte
public []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *key12) Marshal() ([]byte, error) {
out := serializedKey{
Encoding: keyEncodingEncrypted,
TPMVersion: TPMVersion12,
Blob: k.blob,
Public: k.public,
}
return out.Serialize()
}
func (k *key12) Close(tpm *TPM) error {
return nil // No state for tpm 1.2.
}
// ActivateCredential decrypts the specified credential using key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *key12) ActivateCredential(t *TPM, in EncryptedCredential) ([]byte, error) {
cred, err := attestation.AIKChallengeResponse(t.ctx, k.blob, in.Credential, in.Secret)
if err != nil {
return nil, fmt.Errorf("failed to activate aik: %v", err)
}
return cred, nil
}
// Quote returns a quote over the platform state, signed by the key.
func (k *key12) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
quote, rawSig, err := attestation.GetQuote(t.ctx, k.blob, nonce)
if err != nil {
return nil, fmt.Errorf("Quote() failed: %v", err)
}
return &Quote{
Version: TPMVersion12,
Quote: quote,
Signature: rawSig,
}, nil
}
// Parameters returns information about the AIK.
func (k *key12) Parameters() AIKParameters {
return AIKParameters{
Public: k.public,
}
}
// key20 represents a key bound to a TPM 2.0.
type key20 struct {
hnd tpmutil.Handle
blob []byte
public []byte // used by both TPM1.2 and 2.0
createData []byte
createAttestation []byte
createSignature []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *key20) Marshal() ([]byte, error) {
return (&serializedKey{
Encoding: keyEncodingEncrypted,
TPMVersion: TPMVersion20,
Blob: k.blob,
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}).Serialize()
}
// Close frees any resources associated with the key.
func (k *key20) Close(tpm *TPM) error {
return tpm2.FlushContext(tpm.rwc, k.hnd)
}
// ActivateCredential decrypts the specified credential using key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *key20) ActivateCredential(t *TPM, in EncryptedCredential) ([]byte, error) {
ekHnd, _, err := t.getPrimaryKeyHandle(commonEkEquivalentHandle)
if err != nil {
return nil, err
}
sessHandle, _, err := tpm2.StartAuthSession(
t.rwc,
tpm2.HandleNull, /*tpmKey*/
tpm2.HandleNull, /*bindKey*/
make([]byte, 16), /*nonceCaller*/
nil, /*secret*/
tpm2.SessionPolicy,
tpm2.AlgNull,
tpm2.AlgSHA256)
if err != nil {
return nil, fmt.Errorf("creating session: %v", err)
}
defer tpm2.FlushContext(t.rwc, sessHandle)
if _, err := tpm2.PolicySecret(t.rwc, tpm2.HandleEndorsement, tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil {
return nil, fmt.Errorf("tpm2.PolicySecret() failed: %v", err)
}
return tpm2.ActivateCredentialUsingAuth(t.rwc, []tpm2.AuthCommand{
{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession},
{Session: sessHandle, Attributes: tpm2.AttrContinueSession},
}, k.hnd, ekHnd, in.Credential[2:], in.Secret[2:])
}
// Quote returns a quote over the platform state, signed by the key.
func (k *key20) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce)
}
// Parameters returns information about the AIK.
func (k *key20) Parameters() AIKParameters {
return AIKParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}
}

167
attest/key_windows.go Normal file
View File

@ -0,0 +1,167 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// +build windows
package attest
import (
"fmt"
tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
)
// key12 represents a Windows-managed key on a TPM1.2 TPM.
type key12 struct {
hnd uintptr
pcpKeyName string
public []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *key12) Marshal() ([]byte, error) {
out := serializedKey{
Encoding: keyEncodingOSManaged,
TPMVersion: TPMVersion12,
Name: k.pcpKeyName,
Public: k.public,
}
return out.Serialize()
}
// ActivateCredential decrypts the specified credential using key.
// This operation is synonymous with TPM_ActivateIdentity for TPM1.2.
func (k *key12) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) {
secretKey, err := tpm.pcp.ActivateCredential(k.hnd, in.Credential)
if err != nil {
return nil, err
}
return decryptCredential(secretKey, in.Secret)
}
// Quote returns a quote over the platform state, signed by the key.
func (k *key12) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
tpmKeyHnd, err := t.pcp.TPMKeyHandle(k.hnd)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}
tpm, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
}
selectedPCRs := make([]int, 24)
for pcr, _ := range selectedPCRs {
selectedPCRs[pcr] = pcr
}
sig, pcrc, err := tpm1.Quote(tpm, tpmKeyHnd, nonce, selectedPCRs[:], wellKnownAuth[:])
if err != nil {
return nil, fmt.Errorf("Quote() failed: %v", err)
}
// Construct and return TPM_QUOTE_INFO
// Returning TPM_QUOTE_INFO allows us to verify the Quote at a higher resolution
// and matches what go-tspi returns.
quote, err := tpm1.NewQuoteInfo(nonce, selectedPCRs[:], pcrc)
if err != nil {
return nil, fmt.Errorf("failed to construct Quote Info: %v", err)
}
return &Quote{
Quote: quote,
Signature: sig,
}, nil
}
// Close frees any resources associated with the key.
func (k *key12) Close(tpm *TPM) error {
return closeNCryptObject(k.hnd)
}
// Parameters returns information about the AIK.
func (k *key12) Parameters() AIKParameters {
return AIKParameters{
Public: k.public,
}
}
// key20 represents a key bound to a TPM 2.0.
type key20 struct {
hnd uintptr
pcpKeyName string
public []byte
createData []byte
createAttestation []byte
createSignature []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *key20) Marshal() ([]byte, error) {
out := serializedKey{
Encoding: keyEncodingOSManaged,
TPMVersion: TPMVersion20,
Name: k.pcpKeyName,
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}
return out.Serialize()
}
// ActivateCredential decrypts the specified credential using the key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *key20) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) {
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
}
// Quote returns a quote over the platform state, signed by the key.
func (k *key20) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
tpmKeyHnd, err := t.pcp.TPMKeyHandle(k.hnd)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}
tpm, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
}
return quote20(tpm, tpmKeyHnd, tpm2.Algorithm(alg), nonce)
}
// Close frees any resources associated with the key.
func (k *key20) Close(tpm *TPM) error {
return closeNCryptObject(k.hnd)
}
// Delete permenantly removes the key from the system. This method
// invalidates Key and any further method invocations are invalid.
func (k *key20) Delete(tpm *TPM) error {
return tpm.pcp.DeleteKey(k.hnd)
}
// Parameters returns information about the AIK.
func (k *key20) Parameters() AIKParameters {
return AIKParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}
}

69
attest/storage.go Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package attest
import (
"encoding/json"
"fmt"
)
// serializedKey represents a loadable, TPM-backed key.
type serializedKey struct {
// Encoding describes the strategy by which the key should be
// loaded/unloaded.
Encoding keyEncoding `json:"KeyEncoding"`
// TPMVersion describes the version of the TPM which the key was generated
// on. deserializeKey() returns an error if it attempts to deserialize a key
// which is from a different TPM version to the currently opened TPM.
TPMVersion TPMVersion
// Public represents the public key, in a TPM-specific format. This
// field is populated on all platforms and TPM versions.
Public []byte
// The following fields are only valid for TPM 2.0 hardware, holding
// information returned as the result to a TPM2_CertifyCreation command.
// These are stored alongside the key for later use, as the certification
// can only be obtained immediately after the key is generated.
CreateData []byte
CreateAttestation []byte
CreateSignature []byte
// Name is only valid for KeyEncodingOSManaged, which is only used
// on Windows.
Name string
// Blob represents the key material for KeyEncodingEncrypted keys. This
// is only used on Linux.
Blob []byte `json:"KeyBlob"`
}
// Serialize represents the key in a persistent format which may be
// loaded at a later time using deserializeKey().
func (k *serializedKey) Serialize() ([]byte, error) {
return json.Marshal(k)
}
func deserializeKey(b []byte, version TPMVersion) (*serializedKey, error) {
var k serializedKey
var err error
if err = json.Unmarshal(b, &k); err != nil {
return nil, fmt.Errorf("json.Unmarshal() failed: %v", err)
}
if k.TPMVersion != version {
return nil, fmt.Errorf("key for different TPM version: %v", k.TPMVersion)
}
return &k, nil
}

View File

@ -241,3 +241,11 @@ func allPCRs20(tpm io.ReadWriter) (map[uint32][]byte, crypto.Hash, error) {
}
return out256, crypto.SHA256, nil
}
// LoadAIK loads a previously-created aik into the TPM for use.
// A key loaded via this function needs to be closed with .Close().
// Only blobs generated by calling AIK.Serialize() are valid parameters
// to this function.
func (t *TPM) LoadAIK(opaqueBlob []byte) (*AIK, error) {
return t.loadAIK(opaqueBlob)
}

View File

@ -20,7 +20,6 @@ import (
"crypto"
"crypto/rsa"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
@ -132,6 +131,11 @@ func openTPM(tpm probedTPM) (*TPM, error) {
}, nil
}
// Version returns the version of the TPM.
func (t *TPM) Version() TPMVersion {
return t.version
}
// Close shuts down the connection to the TPM.
func (t *TPM) Close() error {
switch t.version {
@ -262,131 +266,19 @@ func (t *TPM) EKs() ([]PlatformEK, error) {
}, nil
}
// Key represents a key bound to the TPM.
type Key struct {
hnd tpmutil.Handle
KeyEncoding KeyEncoding
TPMVersion TPMVersion
Purpose KeyPurpose
KeyBlob []byte // exclusive to TPM1.2
Public []byte // used by both TPM1.2 and 2.0
CreateData []byte
CreateAttestation []byte
CreateSignature []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *Key) Marshal() ([]byte, error) {
return json.Marshal(k)
}
// Close frees any resources associated with the key.
func (k *Key) Close(tpm *TPM) error {
switch tpm.version {
case TPMVersion12:
return nil
case TPMVersion20:
return tpm2.FlushContext(tpm.rwc, k.hnd)
default:
return fmt.Errorf("unsupported TPM version: %x", tpm.version)
}
}
// Delete is not yet supported on linux systems.
func (k *Key) Delete(tpm *TPM) error {
return errors.New("key deletion is not yet supported on linux systems")
}
// ActivateCredential decrypts the specified credential using key.
// This operation is synonymous with TPM2_ActivateCredential.
func (k *Key) ActivateCredential(t *TPM, in EncryptedCredential) ([]byte, error) {
switch t.version {
case TPMVersion12:
cred, err := attestation.AIKChallengeResponse(t.ctx, k.KeyBlob, in.Credential, in.Secret)
if err != nil {
return nil, fmt.Errorf("failed to refresh aik: %v", err)
}
return cred, nil
case TPMVersion20:
ekHnd, _, err := t.getPrimaryKeyHandle(commonEkEquivalentHandle)
if err != nil {
return nil, err
}
sessHandle, _, err := tpm2.StartAuthSession(
t.rwc,
tpm2.HandleNull, /*tpmKey*/
tpm2.HandleNull, /*bindKey*/
make([]byte, 16), /*nonceCaller*/
nil, /*secret*/
tpm2.SessionPolicy,
tpm2.AlgNull,
tpm2.AlgSHA256)
if err != nil {
return nil, fmt.Errorf("creating session: %v", err)
}
defer tpm2.FlushContext(t.rwc, sessHandle)
if _, err := tpm2.PolicySecret(t.rwc, tpm2.HandleEndorsement, tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil {
return nil, fmt.Errorf("tpm2.PolicySecret() failed: %v", err)
}
return tpm2.ActivateCredentialUsingAuth(t.rwc, []tpm2.AuthCommand{
{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession},
{Session: sessHandle, Attributes: tpm2.AttrContinueSession},
}, k.hnd, ekHnd, in.Credential[2:], in.Secret[2:])
default:
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
}
}
func (k *Key) quote12(ctx *tspi.Context, nonce []byte) (*Quote, error) {
quote, rawSig, err := attestation.GetQuote(ctx, k.KeyBlob, nonce)
if err != nil {
return nil, fmt.Errorf("GetQuote() failed: %v", err)
}
return &Quote{
Version: TPMVersion12,
Quote: quote,
Signature: rawSig,
}, nil
}
// Quote returns a quote over the platform state, signed by the key.
func (k *Key) Quote(t *TPM, nonce []byte, alg tpm2.Algorithm) (*Quote, error) {
switch t.version {
case TPMVersion12:
return k.quote12(t.ctx, nonce)
case TPMVersion20:
return quote20(t.rwc, k.hnd, alg, nonce)
default:
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
}
}
// MintAIK creates an attestation key.
func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
switch t.version {
case TPMVersion12:
pub, blob, err := attestation.CreateAIK(t.ctx)
if err != nil {
return nil, fmt.Errorf("CreateAIK failed: %v", err)
}
return &Key{
KeyEncoding: KeyEncodingEncrypted,
TPMVersion: t.version,
Purpose: AttestationKey,
KeyBlob: blob,
Public: pub,
return &AIK{
aik: &key12{
blob: blob,
public: pub,
},
}, nil
case TPMVersion20:
@ -398,11 +290,11 @@ func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
_, blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", aikTemplate)
if err != nil {
return nil, fmt.Errorf("CreateKeyEx failed: %v", err)
return nil, fmt.Errorf("CreateKeyEx() failed: %v", err)
}
keyHandle, _, err := tpm2.Load(t.rwc, srk, "", pub, blob)
if err != nil {
return nil, fmt.Errorf("Load failed: %v", err)
return nil, fmt.Errorf("Load() failed: %v", err)
}
// If any errors occur, free the AIK's handle.
defer func() {
@ -422,16 +314,15 @@ func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
}
return &Key{
hnd: keyHandle,
KeyEncoding: KeyEncodingEncrypted,
TPMVersion: t.version,
Purpose: AttestationKey,
KeyBlob: blob,
Public: pub,
CreateData: creationData,
CreateAttestation: attestation,
CreateSignature: signature,
return &AIK{
aik: &key20{
hnd: keyHandle,
blob: blob,
public: pub,
createData: creationData,
createAttestation: attestation,
createSignature: signature,
},
}, nil
default:
@ -439,45 +330,46 @@ func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
}
}
// LoadKey loads a previously-created key into the TPM for use.
// A key loaded via this function needs to be closed with .Close().
func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
// TODO(b/124266168): Load under the key handle loaded by t.getPrimaryKeyHandle()
var k Key
var err error
if err = json.Unmarshal(opaqueBlob, &k); err != nil {
return nil, fmt.Errorf("Unmarshal failed: %v", err)
func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) {
sKey, err := deserializeKey(opaqueBlob, t.version)
if err != nil {
return nil, fmt.Errorf("deserializeKey() failed: %v", err)
}
if sKey.Encoding != keyEncodingEncrypted {
return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
}
if k.TPMVersion != t.version {
return nil, errors.New("key TPM version does not match opened TPM")
}
if k.Purpose != AttestationKey {
return nil, fmt.Errorf("unsupported key kind: %x", k.Purpose)
}
switch t.version {
switch sKey.TPMVersion {
case TPMVersion12:
if k.KeyEncoding != KeyEncodingEncrypted {
return nil, fmt.Errorf("unsupported key encoding: %x", k.KeyEncoding)
}
return &AIK{
aik: &key12{
blob: sKey.Blob,
public: sKey.Public,
},
}, nil
case TPMVersion20:
if k.KeyEncoding != KeyEncodingEncrypted {
return nil, fmt.Errorf("unsupported key encoding: %x", k.KeyEncoding)
}
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil {
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
if k.hnd, _, err = tpm2.Load(t.rwc, srk, "", k.Public, k.KeyBlob); err != nil {
return nil, fmt.Errorf("Load failed: %v", err)
var hnd tpmutil.Handle
if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
return nil, fmt.Errorf("Load() failed: %v", err)
}
}
return &k, nil
return &AIK{
aik: &key20{
hnd: hnd,
blob: sKey.Blob,
public: sKey.Public,
createData: sKey.CreateData,
createAttestation: sKey.CreateAttestation,
createSignature: sKey.CreateSignature,
},
}, nil
default:
return nil, fmt.Errorf("cannot load AIK with TPM version: %v", sKey.TPMVersion)
}
}
// allPCRs12 returns a map of all the PCR values on the TPM

View File

@ -26,7 +26,6 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
@ -36,7 +35,6 @@ import (
tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
tpmtbs "github.com/google/go-tpm/tpmutil/tbs"
)
@ -104,6 +102,11 @@ func openTPM(tpm probedTPM) (*TPM, error) {
}, nil
}
// Version returns the version of the TPM.
func (t *TPM) Version() TPMVersion {
return t.version
}
// Close shuts down the connection to the TPM.
func (t *TPM) Close() error {
return t.pcp.Close()
@ -248,27 +251,6 @@ func decodeWindowsBcryptRSABlob(b []byte) (*rsa.PublicKey, error) {
}, nil
}
// Key represents a key bound to the TPM.
type Key struct {
hnd uintptr
KeyEncoding KeyEncoding
TPMVersion TPMVersion
Purpose KeyPurpose
PCPKeyName string
KeyBlob []byte
Public []byte
CreateData []byte
CreateAttestation []byte
CreateSignature []byte
}
// Marshal represents the key in a persistent format which may be
// loaded at a later time using tpm.LoadKey().
func (k *Key) Marshal() ([]byte, error) {
return json.Marshal(k)
}
func decryptCredential(secretKey, blob []byte) ([]byte, error) {
var scheme uint32
symbuf := bytes.NewReader(blob)
@ -308,92 +290,9 @@ func decryptCredential(secretKey, blob []byte) ([]byte, error) {
return secret, nil
}
// ActivateCredential decrypts the specified credential using key.
// This operation is synonymous with TPM2_ActivateCredential for TPM2.0
// and TPM_ActivateIdentity with the trousers daemon for TPM1.2.
func (k *Key) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) {
if k.TPMVersion != tpm.version {
return nil, fmt.Errorf("tpm and key version mismatch")
}
switch tpm.version {
case TPMVersion12:
secretKey, err := tpm.pcp.ActivateCredential(k.hnd, in.Credential)
if err != nil {
return nil, err
}
return decryptCredential(secretKey, in.Secret)
case TPMVersion20:
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
default:
return nil, fmt.Errorf("invalid TPM version: %v", tpm.version)
}
}
func (k *Key) quote12(tpm io.ReadWriter, hnd tpmutil.Handle, nonce []byte) (*Quote, error) {
selectedPCRs := make([]int, 24)
for pcr, _ := range selectedPCRs {
selectedPCRs[pcr] = pcr
}
sig, pcrc, err := tpm1.Quote(tpm, hnd, nonce, selectedPCRs[:], wellKnownAuth[:])
if err != nil {
return nil, fmt.Errorf("Quote() failed: %v", err)
}
// Construct and return TPM_QUOTE_INFO
// Returning TPM_QUOTE_INFO allows us to verify the Quote at a higher resolution
// and matches what go-tspi returns.
quote, err := tpm1.NewQuoteInfo(nonce, selectedPCRs[:], pcrc)
if err != nil {
return nil, fmt.Errorf("failed to construct Quote Info: %v", err)
}
return &Quote{
Quote: quote,
Signature: sig,
}, nil
}
// Quote returns a quote over the platform state, signed by the key.
func (k *Key) Quote(t *TPM, nonce []byte, alg tpm2.Algorithm) (*Quote, error) {
tpmKeyHnd, err := t.pcp.TPMKeyHandle(k.hnd)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}
switch t.version {
case TPMVersion12:
tpm, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
}
return k.quote12(tpm, tpmKeyHnd, nonce)
case TPMVersion20:
tpm, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("TPMCommandInterface() failed: %v", err)
}
return quote20(tpm, tpmKeyHnd, alg, nonce)
default:
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
}
}
// Close frees any resources associated with the key.
func (k *Key) Close(tpm *TPM) error {
return closeNCryptObject(k.hnd)
}
// Delete permenantly removes the key from the system. This method
// invalidates Key and any further method invocations are invalid.
func (k *Key) Delete(tpm *TPM) error {
return tpm.pcp.DeleteKey(k.hnd)
}
// MintAIK creates a persistent attestation key. The returned key must be
// closed with a call to key.Close() when the caller has finished using it.
func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
nameHex := make([]byte, 5)
if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) {
return nil, fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err)
@ -410,42 +309,71 @@ func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
return nil, fmt.Errorf("pcp failed to read attestation key properties: %v", err)
}
return &Key{
hnd: kh,
KeyEncoding: KeyEncodingOSManaged,
TPMVersion: t.version,
Purpose: AttestationKey,
PCPKeyName: name,
Public: props.RawPublic,
CreateData: props.RawCreationData,
CreateAttestation: props.RawAttest,
CreateSignature: props.RawSignature,
}, nil
switch t.version {
case TPMVersion12:
return &AIK{
aik: &key12{
hnd: kh,
pcpKeyName: name,
public: props.RawPublic,
},
}, nil
case TPMVersion20:
return &AIK{
aik: &key20{
hnd: kh,
pcpKeyName: name,
public: props.RawPublic,
createData: props.RawCreationData,
createAttestation: props.RawAttest,
createSignature: props.RawSignature,
},
}, nil
default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version)
}
}
// LoadKey loads a previously-created key into the TPM for use.
// A key loaded via this function needs to be closed with .Close().
func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
var k Key
var err error
if err = json.Unmarshal(opaqueBlob, &k); err != nil {
return nil, fmt.Errorf("Unmarshal failed: %v", err)
func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) {
sKey, err := deserializeKey(opaqueBlob, t.version)
if err != nil {
return nil, fmt.Errorf("deserializeKey() failed: %v", err)
}
if sKey.Encoding != keyEncodingOSManaged {
return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
}
if k.TPMVersion != t.version {
return nil, errors.New("key TPM version does not match opened TPM")
}
if k.KeyEncoding != KeyEncodingOSManaged {
return nil, fmt.Errorf("unsupported key encoding: %x", k.KeyEncoding)
}
if k.Purpose != AttestationKey {
return nil, fmt.Errorf("unsupported key kind: %x", k.Purpose)
}
if k.hnd, err = t.pcp.LoadKeyByName(k.PCPKeyName); err != nil {
hnd, err := t.pcp.LoadKeyByName(sKey.Name)
if err != nil {
return nil, fmt.Errorf("pcp failed to load key: %v", err)
}
return &k, nil
switch t.version {
case TPMVersion12:
return &AIK{
aik: &key12{
hnd: hnd,
pcpKeyName: sKey.Name,
public: sKey.Public,
},
}, nil
case TPMVersion20:
return &AIK{
aik: &key20{
hnd: hnd,
pcpKeyName: sKey.Name,
public: sKey.Public,
createData: sKey.CreateData,
createAttestation: sKey.CreateAttestation,
createSignature: sKey.CreateSignature,
},
}, nil
default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version)
}
}
func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) {