diff --git a/attest/attest.go b/attest/attest.go index 6ce145f..79efed3 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -85,11 +85,11 @@ const ( ) 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) - AttestationParameters() AttestationParameters + close(*platformTPM) error + marshal() ([]byte, error) + activateCredential(tpm *platformTPM, in EncryptedCredential) ([]byte, error) + quote(t *platformTPM, nonce []byte, alg HashAlg) (*Quote, error) + attestationParameters() AttestationParameters } // AIK represents a key which can be used for attestation. @@ -99,7 +99,7 @@ type AIK struct { // Close unloads the AIK from the system. func (k *AIK) Close(t *TPM) error { - return k.aik.Close(t) + return k.aik.close(t.tpm) } // Marshal encodes the AIK in a format that can be reloaded with tpm.LoadAIK(). @@ -107,7 +107,7 @@ func (k *AIK) Close(t *TPM) error { // 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() + return k.aik.marshal() } // ActivateCredential decrypts the secret using the key to prove that the AIK @@ -115,18 +115,18 @@ func (k *AIK) Marshal() ([]byte, error) { // // This operation is synonymous with TPM2_ActivateCredential. func (k *AIK) ActivateCredential(tpm *TPM, in EncryptedCredential) (secret []byte, err error) { - return k.aik.ActivateCredential(tpm, in) + return k.aik.activateCredential(tpm.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) + return k.aik.quote(tpm.tpm, nonce, alg) } // Parameters returns information about the AIK, typically used to generate // a credential activation challenge. func (k *AIK) AttestationParameters() AttestationParameters { - return k.aik.AttestationParameters() + return k.aik.attestationParameters() } // AIKConfig encapsulates parameters for minting keys. This type is defined diff --git a/attest/attest_simulated_tpm20_test.go b/attest/attest_simulated_tpm20_test.go index af7108f..73deecd 100644 --- a/attest/attest_simulated_tpm20_test.go +++ b/attest/attest_simulated_tpm20_test.go @@ -13,6 +13,9 @@ // the License. // +build !localtest !tpm12 +// +build cgo + +// NOTE: simulator requires cgo, hence the build tag. package attest @@ -30,12 +33,12 @@ func setupSimulatedTPM(t *testing.T) (*simulator.Simulator, *TPM) { if err != nil { t.Fatal(err) } - return tpm, &TPM{ + return tpm, &TPM{&platformTPM{ version: TPMVersion20, interf: TPMInterfaceKernelManaged, sysPath: "/dev/tpmrm0", rwc: tpm, - } + }} } func TestSimTPM20EK(t *testing.T) { @@ -195,7 +198,7 @@ func TestSimTPM20Persistence(t *testing.T) { sim, tpm := setupSimulatedTPM(t) defer sim.Close() - ekHnd, _, err := tpm.getPrimaryKeyHandle(commonEkEquivalentHandle) + ekHnd, _, err := tpm.tpm.getPrimaryKeyHandle(commonEkEquivalentHandle) if err != nil { t.Fatalf("getPrimaryKeyHandle() failed: %v", err) } @@ -203,7 +206,7 @@ func TestSimTPM20Persistence(t *testing.T) { t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle) } - ekHnd, p, err := tpm.getPrimaryKeyHandle(commonEkEquivalentHandle) + ekHnd, p, err := tpm.tpm.getPrimaryKeyHandle(commonEkEquivalentHandle) if err != nil { t.Fatalf("second getPrimaryKeyHandle() failed: %v", err) } diff --git a/attest/attest_tpm12_test.go b/attest/attest_tpm12_test.go index 7b2e2c8..a29c760 100644 --- a/attest/attest_tpm12_test.go +++ b/attest/attest_tpm12_test.go @@ -91,12 +91,9 @@ func TestNewAIK(t *testing.T) { tpm := openTPM12(t) defer tpm.Close() - aik, err := tpm.NewAIK(nil) - if err != nil { + if _, err := tpm.NewAIK(nil); err != nil { t.Fatalf("NewAIK failed: %v", err) } - k := aik.aik.(*key12) - t.Logf("aik blob: %x\naik pubkey: %x\n", k.blob, k.public) } func TestTPMQuote(t *testing.T) { diff --git a/attest/key_linux.go b/attest/key_linux.go index 14bfbf6..53f1a59 100644 --- a/attest/key_linux.go +++ b/attest/key_linux.go @@ -39,7 +39,7 @@ func newKey12(blob, public []byte) aik { // 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) { +func (k *key12) marshal() ([]byte, error) { out := serializedKey{ Encoding: keyEncodingEncrypted, TPMVersion: TPMVersion12, @@ -49,13 +49,11 @@ func (k *key12) Marshal() ([]byte, error) { return out.Serialize() } -func (k *key12) Close(tpm *TPM) error { +func (k *key12) close(tpm *platformTPM) 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) { +func (k *key12) activateCredential(t *platformTPM, 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) @@ -63,8 +61,7 @@ func (k *key12) ActivateCredential(t *TPM, in EncryptedCredential) ([]byte, erro 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) { +func (k *key12) quote(t *platformTPM, nonce []byte, alg HashAlg) (*Quote, error) { if alg != HashSHA1 { return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not HashAlg(%v)", alg) } @@ -81,8 +78,7 @@ func (k *key12) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) { }, nil } -// AttestationParameters returns information about the AIK. -func (k *key12) AttestationParameters() AttestationParameters { +func (k *key12) attestationParameters() AttestationParameters { return AttestationParameters{ Public: k.public, UseTCSDActivationFormat: true, @@ -111,9 +107,7 @@ func newKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, c } } -// 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) { +func (k *key20) marshal() ([]byte, error) { return (&serializedKey{ Encoding: keyEncodingEncrypted, TPMVersion: TPMVersion20, @@ -126,14 +120,11 @@ func (k *key20) Marshal() ([]byte, error) { }).Serialize() } -// Close frees any resources associated with the key. -func (k *key20) Close(tpm *TPM) error { +func (k *key20) close(tpm *platformTPM) 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) { +func (k *key20) activateCredential(t *platformTPM, in EncryptedCredential) ([]byte, error) { ekHnd, _, err := t.getPrimaryKeyHandle(commonEkEquivalentHandle) if err != nil { return nil, err @@ -163,13 +154,11 @@ func (k *key20) ActivateCredential(t *TPM, in EncryptedCredential) ([]byte, erro }, 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) { +func (k *key20) quote(t *platformTPM, nonce []byte, alg HashAlg) (*Quote, error) { return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce) } -// AttestationParameters returns information about the AIK. -func (k *key20) AttestationParameters() AttestationParameters { +func (k *key20) attestationParameters() AttestationParameters { return AttestationParameters{ Public: k.public, CreateData: k.createData, diff --git a/attest/key_windows.go b/attest/key_windows.go index 1cf5c3b..9e58242 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -37,9 +37,7 @@ func newKey12(hnd uintptr, pcpKeyName string, public []byte) aik { } } -// 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) { +func (k *key12) marshal() ([]byte, error) { out := serializedKey{ Encoding: keyEncodingOSManaged, TPMVersion: TPMVersion12, @@ -49,9 +47,7 @@ func (k *key12) Marshal() ([]byte, error) { 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) { +func (k *key12) activateCredential(tpm *platformTPM, in EncryptedCredential) ([]byte, error) { secretKey, err := tpm.pcp.ActivateCredential(k.hnd, in.Credential) if err != nil { return nil, err @@ -59,8 +55,7 @@ func (k *key12) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, er 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) { +func (k *key12) quote(t *platformTPM, nonce []byte, alg HashAlg) (*Quote, error) { if alg != HashSHA1 { return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not HashAlg(%v)", alg) } @@ -98,13 +93,11 @@ func (k *key12) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) { }, nil } -// Close frees any resources associated with the key. -func (k *key12) Close(tpm *TPM) error { +func (k *key12) close(tpm *platformTPM) error { return closeNCryptObject(k.hnd) } -// AttestationParameters returns information about the AIK. -func (k *key12) AttestationParameters() AttestationParameters { +func (k *key12) attestationParameters() AttestationParameters { return AttestationParameters{ Public: k.public, } @@ -132,9 +125,7 @@ func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, } } -// 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) { +func (k *key20) marshal() ([]byte, error) { out := serializedKey{ Encoding: keyEncodingOSManaged, TPMVersion: TPMVersion20, @@ -148,14 +139,11 @@ func (k *key20) Marshal() ([]byte, error) { 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) { +func (k *key20) activateCredential(tpm *platformTPM, 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) { +func (k *key20) quote(t *platformTPM, nonce []byte, alg HashAlg) (*Quote, error) { tpmKeyHnd, err := t.pcp.TPMKeyHandle(k.hnd) if err != nil { return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err) @@ -168,19 +156,11 @@ func (k *key20) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) { return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce) } -// Close frees any resources associated with the key. -func (k *key20) Close(tpm *TPM) error { +func (k *key20) close(tpm *platformTPM) 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) -} - -// AttestationParameters returns information about the AIK. -func (k *key20) AttestationParameters() AttestationParameters { +func (k *key20) attestationParameters() AttestationParameters { return AttestationParameters{ Public: k.public, CreateData: k.createData, diff --git a/attest/tpm.go b/attest/tpm.go index 196e48c..2cca3d8 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -267,10 +267,52 @@ func readAllPCRs20(tpm io.ReadWriter, alg tpm2.Algorithm) (map[uint32][]byte, er return out, nil } +//TPM interfaces with a TPM device on the system. +type TPM struct { + // tpm holds a platform specific implementation of TPM logic: Windows or Linux. + // see *_linux.go and *_windows.go files for definitions of these structs. + tpm *platformTPM +} + +// Close shuts down the connection to the TPM. +func (t *TPM) Close() error { + return t.tpm.close() +} + +// EKs returns the endorsement keys burned-in to the platform. +func (t *TPM) EKs() ([]EK, error) { + return t.tpm.eks() +} + +// Info returns information about the TPM. +func (t *TPM) Info() (*TPMInfo, error) { + return t.tpm.info() +} + // 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) + return t.tpm.loadAIK(opaqueBlob) +} + +// MeasurementLog returns the present value of the System Measurement Log. +func (t *TPM) MeasurementLog() ([]byte, error) { + return t.tpm.measurementLog() +} + +// NewAIK creates an attestation key. +func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) { + return t.tpm.newAIK(opts) +} + +// PCRs returns the present value of Platform Configuration Registers with the given digest algorithm. +func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { + return t.tpm.pcrs(alg) +} + +// Version returns the version of the TPM. +func (t *TPM) Version() TPMVersion { + return t.tpm.tpmVersion() } diff --git a/attest/tpm_linux.go b/attest/tpm_linux.go index 0218fb1..9dec895 100644 --- a/attest/tpm_linux.go +++ b/attest/tpm_linux.go @@ -42,8 +42,8 @@ const ( tpmRoot = "/sys/class/tpm" ) -// TPM interfaces with a TPM device on the system. -type TPM struct { +// platformTPM interfaces with a TPM device on the system. +type platformTPM struct { version TPMVersion interf TPMInterface @@ -122,22 +122,20 @@ func openTPM(tpm probedTPM) (*TPM, error) { } } - return &TPM{ + return &TPM{&platformTPM{ version: tpm.Version, interf: interf, sysPath: tpm.Path, rwc: rwc, ctx: ctx, - }, nil + }}, nil } -// Version returns the version of the TPM. -func (t *TPM) Version() TPMVersion { +func (t *platformTPM) tpmVersion() TPMVersion { return t.version } -// Close shuts down the connection to the TPM. -func (t *TPM) Close() error { +func (t *platformTPM) close() error { switch t.version { case TPMVersion12: return t.ctx.Close() @@ -162,7 +160,7 @@ func readTPM12VendorAttributes(context *tspi.Context) (TCGVendorID, string, erro } // Info returns information about the TPM. -func (t *TPM) Info() (*TPMInfo, error) { +func (t *platformTPM) info() (*TPMInfo, error) { tInfo := TPMInfo{ Version: t.version, Interface: t.interf, @@ -190,7 +188,7 @@ func (t *TPM) Info() (*TPMInfo, error) { } // Return value: handle, whether we generated a new one, error -func (t *TPM) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { +func (t *platformTPM) getPrimaryKeyHandle(pHnd tpmutil.Handle) (tpmutil.Handle, bool, error) { _, _, _, err := tpm2.ReadPublic(t.rwc, pHnd) if err == nil { // Found the persistent handle, assume it's the key we want. @@ -225,8 +223,7 @@ func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) { return ParseEKCertificate(ekCert) } -// EKs returns the endorsement keys burned-in to the platform. -func (t *TPM) EKs() ([]EK, error) { +func (t *platformTPM) eks() ([]EK, error) { switch t.version { case TPMVersion12: cert, err := readEKCertFromNVRAM12(t.ctx) @@ -270,8 +267,7 @@ func (t *TPM) EKs() ([]EK, error) { } } -// NewAIK creates an attestation key. -func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) { +func (t *platformTPM) newAIK(opts *AIKConfig) (*AIK, error) { switch t.version { case TPMVersion12: pub, blob, err := attestation.CreateAIK(t.ctx) @@ -317,7 +313,7 @@ func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) { } } -func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) { +func (t *platformTPM) loadAIK(opaqueBlob []byte) (*AIK, error) { sKey, err := deserializeKey(opaqueBlob, t.version) if err != nil { return nil, fmt.Errorf("deserializeKey() failed: %v", err) @@ -359,12 +355,7 @@ func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) { return PCRs, nil } -// TODO: Refactor PCRs() into a file not subject to build tags, and implement -// platform-specific logic in private methods. - -// PCRs returns the present value of Platform Configuration Registers with the -// given digest algorithm. -func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { +func (t *platformTPM) pcrs(alg HashAlg) ([]PCR, error) { var PCRs map[uint32][]byte var err error @@ -400,7 +391,6 @@ func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { return out, nil } -// MeasurementLog returns the present value of the System Measurement Log. -func (t *TPM) MeasurementLog() ([]byte, error) { +func (t *platformTPM) measurementLog() ([]byte, error) { return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements") } diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 9470bee..bdad5de 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -34,8 +34,7 @@ import ( var wellKnownAuth [20]byte -// TPM interfaces with a TPM device on the system. -type TPM struct { +type platformTPM struct { version TPMVersion pcp *winPCP } @@ -90,19 +89,17 @@ func openTPM(tpm probedTPM) (*TPM, error) { return nil, fmt.Errorf("tbsConvertVersion(%v) failed: %v", info.TBSInfo.TPMVersion, err) } - return &TPM{ + return &TPM{&platformTPM{ pcp: pcp, version: vers, - }, nil + }}, nil } -// Version returns the version of the TPM. -func (t *TPM) Version() TPMVersion { +func (t *platformTPM) tpmVersion() TPMVersion { return t.version } -// Close shuts down the connection to the TPM. -func (t *TPM) Close() error { +func (t *platformTPM) close() error { return t.pcp.Close() } @@ -115,8 +112,7 @@ func readTPM12VendorAttributes(tpm io.ReadWriter) (TCGVendorID, string, error) { return vendorID, vendorID.String(), nil } -// Info returns information about the TPM. -func (t *TPM) Info() (*TPMInfo, error) { +func (t *platformTPM) info() (*TPMInfo, error) { tInfo := TPMInfo{ Version: t.version, Interface: TPMInterfaceKernelManaged, @@ -146,8 +142,7 @@ func (t *TPM) Info() (*TPMInfo, error) { return &tInfo, nil } -// EKs returns the Endorsement Keys burned-in to the platform. -func (t *TPM) EKs() ([]EK, error) { +func (t *platformTPM) eks() ([]EK, error) { ekCerts, err := t.pcp.EKCerts() if err != nil { return nil, fmt.Errorf("could not read EKCerts: %v", err) @@ -166,7 +161,7 @@ func (t *TPM) EKs() ([]EK, error) { } ek := EK{Public: pub} - i, err := t.Info() + i, err := t.info() if err != nil { return nil, err } @@ -176,7 +171,7 @@ func (t *TPM) EKs() ([]EK, error) { return []EK{ek}, nil } -func (t *TPM) ekPub() (*rsa.PublicKey, error) { +func (t *platformTPM) ekPub() (*rsa.PublicKey, error) { p, err := t.pcp.EKPub() if err != nil { return nil, fmt.Errorf("could not read ekpub: %v", err) @@ -269,9 +264,7 @@ func decryptCredential(secretKey, blob []byte) ([]byte, error) { return secret, nil } -// NewAIK 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) NewAIK(opts *AIKConfig) (*AIK, error) { +func (t *platformTPM) newAIK(opts *AIKConfig) (*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) @@ -298,7 +291,7 @@ func (t *TPM) NewAIK(opts *AIKConfig) (*AIK, error) { } } -func (t *TPM) loadAIK(opaqueBlob []byte) (*AIK, error) { +func (t *platformTPM) loadAIK(opaqueBlob []byte) (*AIK, error) { sKey, err := deserializeKey(opaqueBlob, t.version) if err != nil { return nil, fmt.Errorf("deserializeKey() failed: %v", err) @@ -341,9 +334,7 @@ func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) { return out, nil } -// PCRs returns the present value of Platform Configuration Registers with the -// given digest algorithm. -func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { +func (t *platformTPM) pcrs(alg HashAlg) ([]PCR, error) { var PCRs map[uint32][]byte switch t.version { @@ -386,8 +377,7 @@ func (t *TPM) PCRs(alg HashAlg) ([]PCR, error) { return out, nil } -// MeasurementLog returns the present value of the System Measurement Log. -func (t *TPM) MeasurementLog() ([]byte, error) { +func (t *platformTPM) measurementLog() ([]byte, error) { context, err := tpmtbs.CreateContext(tpmtbs.TPMVersion20, tpmtbs.IncludeTPM20|tpmtbs.IncludeTPM12) if err != nil { return nil, err