attest: move public key parsing server side

Event log parsing requires knowning both the public key and signing
parameters. Symmantically, this information should be from an attested
public key blob, not additional data passed by the client.

Introduce a new method for parsing an AIK's public key blob, returning
a new AIKPublic struct.
This commit is contained in:
Eric Chiang 2019-07-30 10:27:57 -07:00
parent f3f08037f8
commit 7d7676beda
7 changed files with 117 additions and 155 deletions

View File

@ -21,6 +21,7 @@ import (
"fmt"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
)
@ -100,7 +101,6 @@ type aik interface {
ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
AttestationParameters() AttestationParameters
Public() crypto.PublicKey
}
// AIK represents a key which can be used for attestation.
@ -138,11 +138,6 @@ func (k *AIK) AttestationParameters() AttestationParameters {
return k.aik.AttestationParameters()
}
// Public returns the public part of the AIK.
func (k *AIK) Public() crypto.PublicKey {
return k.aik.Public()
}
// MintOptions encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility.
type MintOptions struct {
@ -180,12 +175,16 @@ type PlatformEK struct {
// AttestationParameters describes information about a key which is necessary
// for verifying its properties remotely.
type AttestationParameters 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
// Public represents the AIK's canonical encoding. This blob includes the
// public key, as well as signing parameters such as the hash algorithm
// used to generate quotes.
//
// Use ParseAIKPublic to access the key's data.
Public []byte
// For TPM 2.0 devices, Public is encoded as a TPMT_PUBLIC structure.
// For TPM 1.2 devices, Public 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
// UseTCSDActivationFormat is set when tcsd (trousers daemon) is operating
// as an intermediary between this library and the TPM. A value of true
@ -209,6 +208,52 @@ type AttestationParameters struct {
CreateSignature []byte
}
// AIKPublic holds structured information about an AIK's public key.
type AIKPublic struct {
// Public is the public part of the AIK. This can either be an *rsa.PublicKey or
// and *ecdsa.PublicKey.
Public crypto.PublicKey
// Hash is the hashing algorithm the AIK will use when signing quotes.
Hash crypto.Hash
}
// ParseAIKPublic parses the Public blob from the AttestationParameters,
// returning the public key and signing parameters for the key.
func ParseAIKPublic(version TPMVersion, public []byte) (*AIKPublic, error) {
switch version {
case TPMVersion12:
rsaPub, err := tpm.UnmarshalPubRSAPublicKey(public)
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
return &AIKPublic{Public: rsaPub, Hash: crypto.SHA1}, nil
case TPMVersion20:
pub, err := tpm2.DecodePublic(public)
if err != nil {
return nil, fmt.Errorf("parsing TPM public key structure: %v", err)
}
pubKey, err := pub.Key()
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
var h crypto.Hash
switch pub.Type {
case tpm2.AlgRSA:
h, err = cryptoHash(pub.RSAParameters.Sign.Hash)
case tpm2.AlgECC:
h, err = cryptoHash(pub.ECCParameters.Sign.Hash)
default:
return nil, fmt.Errorf("unsupported public key type 0x%x", pub.Type)
}
if err != nil {
return nil, fmt.Errorf("invalid public key hash: %v", err)
}
return &AIKPublic{Public: pubKey, Hash: h}, nil
default:
return nil, fmt.Errorf("unknown tpm version 0x%x", version)
}
}
// HashAlg identifies a hashing Algorithm.
type HashAlg uint8

View File

@ -153,6 +153,21 @@ func TestSimTPM20ActivateCredential(t *testing.T) {
}
}
func TestParseAIKPublic20(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
aik, err := tpm.MintAIK(nil)
if err != nil {
t.Fatalf("MintAIK() failed: %v", err)
}
defer aik.Close(tpm)
params := aik.AttestationParameters()
if _, err := ParseAIKPublic(TPMVersion20, params.Public); err != nil {
t.Errorf("parsing AIK public blob: %v", err)
}
}
func TestSimTPM20Quote(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()

View File

@ -27,7 +27,7 @@ var (
tpm12config = &OpenConfig{TPMVersion12}
)
func TestTPM12Info(t *testing.T) {
func openTPM12(t *testing.T) *TPM {
if !*testTPM12 {
t.SkipNow()
}
@ -35,6 +35,11 @@ func TestTPM12Info(t *testing.T) {
if err != nil {
t.Fatalf("Failed to open tpm 1.2: %v", err)
}
return tpm
}
func TestTPM12Info(t *testing.T) {
tpm := openTPM12(t)
defer tpm.Close()
Info, err := tpm.Info()
@ -46,13 +51,7 @@ func TestTPM12Info(t *testing.T) {
}
func TestTPM12PCRs(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
tpm, err := OpenTPM(tpm12config)
if err != nil {
t.Fatalf("Failed to open tpm 1.2: %v", err)
}
tpm := openTPM12(t)
defer tpm.Close()
PCRs, _, err := tpm.PCRs()
@ -75,13 +74,7 @@ func TestTPM12PCRs(t *testing.T) {
}
func TestTPM12EKs(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
tpm, err := OpenTPM(tpm12config)
if err != nil {
t.Fatalf("Failed to open tpm 1.2: %v", err)
}
tpm := openTPM12(t)
defer tpm.Close()
EKs, err := tpm.EKs()
@ -97,13 +90,7 @@ func TestTPM12EKs(t *testing.T) {
}
func TestMintAIK(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
tpm, err := OpenTPM(tpm12config)
if err != nil {
t.Fatalf("failed to open tpm 1.2: %v", err)
}
tpm := openTPM12(t)
defer tpm.Close()
aik, err := tpm.MintAIK(nil)
@ -115,18 +102,14 @@ func TestMintAIK(t *testing.T) {
}
func TestTPMQuote(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
nonce := make([]byte, 20)
rand.Read(nonce)
tpm, err := OpenTPM(tpm12config)
if err != nil {
t.Fatalf("Failed to open tpm 1.2: %v", err)
}
tpm := openTPM12(t)
defer tpm.Close()
nonce := make([]byte, 20)
if _, err := rand.Read(nonce); err != nil {
t.Fatalf("reading nonce: %v", err)
}
aik, err := tpm.MintAIK(nil)
if err != nil {
t.Fatalf("MintAIK failed: %v", err)
@ -140,15 +123,23 @@ func TestTPMQuote(t *testing.T) {
t.Logf("Quote{version: %v, quote: %x, signature: %x}\n", quote.Version, quote.Quote, quote.Signature)
}
func TestTPMActivateCredential(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
func TestParseAIKPublic12(t *testing.T) {
tpm := openTPM12(t)
defer tpm.Close()
tpm, err := OpenTPM(tpm12config)
aik, err := tpm.MintAIK(nil)
if err != nil {
t.Fatalf("failed to open tpm 1.2: %v", err)
t.Fatalf("MintAIK() failed: %v", err)
}
defer aik.Close(tpm)
params := aik.AttestationParameters()
if _, err := ParseAIKPublic(TPMVersion12, params.Public); err != nil {
t.Errorf("parsing AIK public blob: %v", err)
}
}
func TestTPMActivateCredential(t *testing.T) {
tpm := openTPM12(t)
defer tpm.Close()
aik, err := tpm.MintAIK(nil)

View File

@ -17,10 +17,8 @@
package attest
import (
"crypto"
"fmt"
"github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpmutil"
"github.com/google/go-tspi/attestation"
@ -30,20 +28,13 @@ import (
type key12 struct {
blob []byte
public []byte
publicKey crypto.PublicKey
}
func newKey12(blob, public []byte) (aik, error) {
rsaPub, err := tpm.UnmarshalPubRSAPublicKey(public)
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
func newKey12(blob, public []byte) aik {
return &key12{
blob: blob,
public: public,
publicKey: rsaPub,
}, nil
blob: blob,
public: public,
}
}
// Marshal represents the key in a persistent format which may be
@ -86,10 +77,6 @@ func (k *key12) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
}, nil
}
func (k *key12) Public() crypto.PublicKey {
return k.publicKey
}
// AttestationParameters returns information about the AIK.
func (k *key12) AttestationParameters() AttestationParameters {
return AttestationParameters{
@ -107,19 +94,9 @@ type key20 struct {
createData []byte
createAttestation []byte
createSignature []byte
publicKey crypto.PublicKey
}
func newKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) (aik, error) {
pub, err := tpm2.DecodePublic(public)
if err != nil {
return nil, fmt.Errorf("parsing TPM public key structure: %v", err)
}
pubKey, err := pub.Key()
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
func newKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) aik {
return &key20{
hnd: hnd,
blob: blob,
@ -127,8 +104,7 @@ func newKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, c
createData: createData,
createAttestation: createAttestation,
createSignature: createSig,
publicKey: pubKey,
}, nil
}
}
// Marshal represents the key in a persistent format which may be
@ -197,7 +173,3 @@ func (k *key20) AttestationParameters() AttestationParameters {
CreateSignature: k.createSignature,
}
}
func (k *key20) Public() crypto.PublicKey {
return k.publicKey
}

View File

@ -17,7 +17,6 @@
package attest
import (
"crypto"
"fmt"
tpm1 "github.com/google/go-tpm/tpm"
@ -29,25 +28,14 @@ type key12 struct {
hnd uintptr
pcpKeyName string
public []byte
publicKey crypto.PublicKey
}
func newKey12(hnd uintptr, pcpKeyName string, public []byte) (aik, error) {
rsaPub, err := tpm1.UnmarshalPubRSAPublicKey(public)
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
func newKey12(hnd uintptr, pcpKeyName string, public []byte) aik {
return &key12{
hnd: hnd,
pcpKeyName: pcpKeyName,
public: public,
publicKey: rsaPub,
}, nil
}
func (k *key12) Public() crypto.PublicKey {
return k.publicKey
}
}
// Marshal represents the key in a persistent format which may be
@ -127,19 +115,9 @@ type key20 struct {
createData []byte
createAttestation []byte
createSignature []byte
publicKey crypto.PublicKey
}
func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) (aik, error) {
pub, err := tpm2.DecodePublic(public)
if err != nil {
return nil, fmt.Errorf("parsing TPM public key structure: %v", err)
}
pubKey, err := pub.Key()
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) aik {
return &key20{
hnd: hnd,
pcpKeyName: pcpKeyName,
@ -147,12 +125,7 @@ func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest,
createData: createData,
createAttestation: createAttest,
createSignature: createSig,
publicKey: pubKey,
}, nil
}
func (k *key20) Public() crypto.PublicKey {
return k.publicKey
}
}
// Marshal represents the key in a persistent format which may be

View File

@ -274,11 +274,7 @@ func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
if err != nil {
return nil, fmt.Errorf("CreateAIK failed: %v", err)
}
aik, err := newKey12(blob, pub)
if err != nil {
return nil, fmt.Errorf("")
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey12(blob, pub)}, nil
case TPMVersion20:
// TODO(jsonp): Abstract choice of hierarchy & parent.
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
@ -311,12 +307,7 @@ func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
}
aik, err := newKey20(keyHandle, blob, pub, creationData, attestation, signature)
if err != nil {
return nil, fmt.Errorf("unpacking public key: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
default:
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
}
@ -333,11 +324,7 @@ func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) {
switch sKey.TPMVersion {
case TPMVersion12:
aik, err := newKey12(sKey.Blob, sKey.Public)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey12(sKey.Blob, sKey.Public)}, nil
case TPMVersion20:
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil {
@ -347,12 +334,7 @@ func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) {
if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
return nil, fmt.Errorf("Load() failed: %v", err)
}
aik, err := newKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
default:
return nil, fmt.Errorf("cannot load AIK with TPM version: %v", sKey.TPMVersion)
}

View File

@ -311,17 +311,9 @@ func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
switch t.version {
case TPMVersion12:
aik, err := newKey12(kh, name, props.RawPublic)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey12(kh, name, props.RawPublic)}, nil
case TPMVersion20:
aik, err := newKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, nil
default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version)
}
@ -343,17 +335,9 @@ func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) {
switch t.version {
case TPMVersion12:
aik, err := newKey12(hnd, sKey.Name, sKey.Public)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey12(hnd, sKey.Name, sKey.Public)}, nil
case TPMVersion20:
aik, err := newKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
return &AIK{aik: newKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version)
}