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" "fmt"
"github.com/google/certificate-transparency-go/x509" "github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2"
) )
@ -100,7 +101,6 @@ type aik interface {
ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
AttestationParameters() AttestationParameters AttestationParameters() AttestationParameters
Public() crypto.PublicKey
} }
// AIK represents a key which can be used for attestation. // AIK represents a key which can be used for attestation.
@ -138,11 +138,6 @@ func (k *AIK) AttestationParameters() AttestationParameters {
return k.aik.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 // MintOptions encapsulates parameters for minting keys. This type is defined
// now (despite being empty) for future interface compatibility. // now (despite being empty) for future interface compatibility.
type MintOptions struct { type MintOptions struct {
@ -180,12 +175,16 @@ type PlatformEK struct {
// AttestationParameters describes information about a key which is necessary // AttestationParameters describes information about a key which is necessary
// for verifying its properties remotely. // for verifying its properties remotely.
type AttestationParameters struct { type AttestationParameters struct {
// Public represents the public key in a TPM-version specific encoding. // Public represents the AIK's canonical encoding. This blob includes the
// For TPM 2.0 devices, this is encoded as a TPMT_PUBLIC structure. // public key, as well as signing parameters such as the hash algorithm
// For TPM 1.2 devices, this is a TPM_PUBKEY structure, as defined in // 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 // 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 // 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 // UseTCSDActivationFormat is set when tcsd (trousers daemon) is operating
// as an intermediary between this library and the TPM. A value of true // as an intermediary between this library and the TPM. A value of true
@ -209,6 +208,52 @@ type AttestationParameters struct {
CreateSignature []byte 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. // HashAlg identifies a hashing Algorithm.
type HashAlg uint8 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) { func TestSimTPM20Quote(t *testing.T) {
sim, tpm := setupSimulatedTPM(t) sim, tpm := setupSimulatedTPM(t)
defer sim.Close() defer sim.Close()

View File

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

View File

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

View File

@ -17,7 +17,6 @@
package attest package attest
import ( import (
"crypto"
"fmt" "fmt"
tpm1 "github.com/google/go-tpm/tpm" tpm1 "github.com/google/go-tpm/tpm"
@ -29,25 +28,14 @@ type key12 struct {
hnd uintptr hnd uintptr
pcpKeyName string pcpKeyName string
public []byte public []byte
publicKey crypto.PublicKey
} }
func newKey12(hnd uintptr, pcpKeyName string, public []byte) (aik, error) { func newKey12(hnd uintptr, pcpKeyName string, public []byte) aik {
rsaPub, err := tpm1.UnmarshalPubRSAPublicKey(public)
if err != nil {
return nil, fmt.Errorf("parsing public key: %v", err)
}
return &key12{ return &key12{
hnd: hnd, hnd: hnd,
pcpKeyName: pcpKeyName, pcpKeyName: pcpKeyName,
public: public, 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 // Marshal represents the key in a persistent format which may be
@ -127,19 +115,9 @@ type key20 struct {
createData []byte createData []byte
createAttestation []byte createAttestation []byte
createSignature []byte createSignature []byte
publicKey crypto.PublicKey
} }
func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) (aik, error) { func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) aik {
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)
}
return &key20{ return &key20{
hnd: hnd, hnd: hnd,
pcpKeyName: pcpKeyName, pcpKeyName: pcpKeyName,
@ -147,12 +125,7 @@ func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest,
createData: createData, createData: createData,
createAttestation: createAttest, createAttestation: createAttest,
createSignature: createSig, 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 // 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 { if err != nil {
return nil, fmt.Errorf("CreateAIK failed: %v", err) return nil, fmt.Errorf("CreateAIK failed: %v", err)
} }
aik, err := newKey12(blob, pub) return &AIK{aik: newKey12(blob, pub)}, nil
if err != nil {
return nil, fmt.Errorf("")
}
return &AIK{aik: aik}, nil
case TPMVersion20: case TPMVersion20:
// TODO(jsonp): Abstract choice of hierarchy & parent. // TODO(jsonp): Abstract choice of hierarchy & parent.
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
@ -311,12 +307,7 @@ func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err) return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
} }
return &AIK{aik: newKey20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
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
default: default:
return nil, fmt.Errorf("unsupported TPM version: %x", t.version) 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 { switch sKey.TPMVersion {
case TPMVersion12: case TPMVersion12:
aik, err := newKey12(sKey.Blob, sKey.Public) return &AIK{aik: newKey12(sKey.Blob, sKey.Public)}, nil
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
case TPMVersion20: case TPMVersion20:
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle) srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
if err != nil { 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 { if hnd, _, err = tpm2.Load(t.rwc, srk, "", sKey.Public, sKey.Blob); err != nil {
return nil, fmt.Errorf("Load() failed: %v", err) return nil, fmt.Errorf("Load() failed: %v", err)
} }
return &AIK{aik: newKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
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
default: default:
return nil, fmt.Errorf("cannot load AIK with TPM version: %v", sKey.TPMVersion) 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 { switch t.version {
case TPMVersion12: case TPMVersion12:
aik, err := newKey12(kh, name, props.RawPublic) return &AIK{aik: newKey12(kh, name, props.RawPublic)}, nil
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
case TPMVersion20: case TPMVersion20:
aik, err := newKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature) return &AIK{aik: newKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, nil
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
default: default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version) 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 { switch t.version {
case TPMVersion12: case TPMVersion12:
aik, err := newKey12(hnd, sKey.Name, sKey.Public) return &AIK{aik: newKey12(hnd, sKey.Name, sKey.Public)}, nil
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
case TPMVersion20: case TPMVersion20:
aik, err := newKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature) return &AIK{aik: newKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
if err != nil {
return nil, fmt.Errorf("unpacking aik: %v", err)
}
return &AIK{aik: aik}, nil
default: default:
return nil, fmt.Errorf("cannot handle TPM version: %v", t.version) return nil, fmt.Errorf("cannot handle TPM version: %v", t.version)
} }