mirror of
https://github.com/google/go-attestation.git
synced 2025-06-01 06:50:52 +00:00
Split linuxTPM -> {trousersTPM,wrappedTPM20}, implement CommandChannel
* Split linuxTPM -> {trousersTPM,wrappedTPM20} + prefix windows key type with windows * Address feedback, implement CommandChannel
This commit is contained in:
parent
1045ef6327
commit
ebb0b62d80
@ -19,6 +19,7 @@ import (
|
|||||||
"crypto"
|
"crypto"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"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/tpm"
|
||||||
@ -45,14 +46,26 @@ const (
|
|||||||
TPMInterfaceDirect TPMInterface = iota
|
TPMInterfaceDirect TPMInterface = iota
|
||||||
TPMInterfaceKernelManaged
|
TPMInterfaceKernelManaged
|
||||||
TPMInterfaceDaemonManaged
|
TPMInterfaceDaemonManaged
|
||||||
|
TPMInterfaceCommandChannel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CommandChannelTPM20 represents a pipe along which TPM 2.0 commands
|
||||||
|
// can be issued, and measurement logs read.
|
||||||
|
type CommandChannelTPM20 interface {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
MeasurementLog() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
// OpenConfig encapsulates settings passed to OpenTPM().
|
// OpenConfig encapsulates settings passed to OpenTPM().
|
||||||
type OpenConfig struct {
|
type OpenConfig struct {
|
||||||
// TPMVersion indicates which TPM version the library should
|
// TPMVersion indicates which TPM version the library should
|
||||||
// attempt to use. If the specified version is not available,
|
// attempt to use. If the specified version is not available,
|
||||||
// ErrTPMNotAvailable is returned. Defaults to TPMVersionAgnostic.
|
// ErrTPMNotAvailable is returned. Defaults to TPMVersionAgnostic.
|
||||||
TPMVersion TPMVersion
|
TPMVersion TPMVersion
|
||||||
|
|
||||||
|
// CommandChannel provides a TPM 2.0 command channel, which can be
|
||||||
|
// used in-lieu of any TPM present on the platform.
|
||||||
|
CommandChannel CommandChannelTPM20
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyEncoding indicates how an exported TPM key is represented.
|
// keyEncoding indicates how an exported TPM key is represented.
|
||||||
@ -396,6 +409,18 @@ func OpenTPM(config *OpenConfig) (*TPM, error) {
|
|||||||
if config == nil {
|
if config == nil {
|
||||||
config = defaultOpenConfig
|
config = defaultOpenConfig
|
||||||
}
|
}
|
||||||
|
// As a special case, if the user provided us with a command channel,
|
||||||
|
// we should use that.
|
||||||
|
if config.CommandChannel != nil {
|
||||||
|
if config.TPMVersion > TPMVersionAgnostic && config.TPMVersion != TPMVersion20 {
|
||||||
|
return nil, errors.New("command channel can only be used as a TPM 2.0 device")
|
||||||
|
}
|
||||||
|
return &TPM{&wrappedTPM20{
|
||||||
|
interf: TPMInterfaceCommandChannel,
|
||||||
|
rwc: config.CommandChannel,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
candidateTPMs, err := probeSystemTPMs()
|
candidateTPMs, err := probeSystemTPMs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -33,12 +33,11 @@ func setupSimulatedTPM(t *testing.T) (*simulator.Simulator, *TPM) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return tpm, &TPM{tpm: &linuxTPM{
|
attestTPM, err := OpenTPM(&OpenConfig{CommandChannel: &linuxCmdChannel{tpm}})
|
||||||
version: TPMVersion20,
|
if err != nil {
|
||||||
interf: TPMInterfaceKernelManaged,
|
t.Fatal(err)
|
||||||
sysPath: "/dev/tpmrm0",
|
}
|
||||||
rwc: tpm,
|
return tpm, attestTPM
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimTPM20EK(t *testing.T) {
|
func TestSimTPM20EK(t *testing.T) {
|
||||||
@ -87,7 +86,7 @@ func TestSimTPM20AKCreateAndLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer loaded.Close(tpm)
|
defer loaded.Close(tpm)
|
||||||
|
|
||||||
k1, k2 := ak.ak.(*key20), loaded.ak.(*key20)
|
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||||
|
|
||||||
if !bytes.Equal(k1.public, k2.public) {
|
if !bytes.Equal(k1.public, k2.public) {
|
||||||
t.Error("Original & loaded AK public blobs did not match.")
|
t.Error("Original & loaded AK public blobs did not match.")
|
||||||
@ -240,7 +239,7 @@ func TestSimTPM20Persistence(t *testing.T) {
|
|||||||
sim, tpm := setupSimulatedTPM(t)
|
sim, tpm := setupSimulatedTPM(t)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
ekHnd, _, err := tpm.tpm.(*linuxTPM).getPrimaryKeyHandle(commonEkEquivalentHandle)
|
ekHnd, _, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonEkEquivalentHandle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("getPrimaryKeyHandle() failed: %v", err)
|
t.Fatalf("getPrimaryKeyHandle() failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -248,7 +247,7 @@ func TestSimTPM20Persistence(t *testing.T) {
|
|||||||
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle)
|
t.Fatalf("bad EK-equivalent handle: got 0x%x, wanted 0x%x", ekHnd, commonEkEquivalentHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
ekHnd, p, err := tpm.tpm.(*linuxTPM).getPrimaryKeyHandle(commonEkEquivalentHandle)
|
ekHnd, p, err := tpm.tpm.(*wrappedTPM20).getPrimaryKeyHandle(commonEkEquivalentHandle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("second getPrimaryKeyHandle() failed: %v", err)
|
t.Fatalf("second getPrimaryKeyHandle() failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ func TestAKCreateAndLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer loaded.Close(tpm)
|
defer loaded.Close(tpm)
|
||||||
|
|
||||||
k1, k2 := ak.ak.(*key20), loaded.ak.(*key20)
|
k1, k2 := ak.ak.(*wrappedKey20), loaded.ak.(*wrappedKey20)
|
||||||
|
|
||||||
if !bytes.Equal(k1.public, k2.public) {
|
if !bytes.Equal(k1.public, k2.public) {
|
||||||
t.Error("Original & loaded AK public blobs did not match.")
|
t.Error("Original & loaded AK public blobs did not match.")
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
testTPM12 = flag.Bool("testTPM12", false, "run tests for TPM1.2")
|
testTPM12 = flag.Bool("testTPM12", false, "run tests for TPM1.2")
|
||||||
tpm12config = &OpenConfig{TPMVersion12}
|
tpm12config = &OpenConfig{TPMVersion: TPMVersion12}
|
||||||
)
|
)
|
||||||
|
|
||||||
func openTPM12(t *testing.T) *TPM {
|
func openTPM12(t *testing.T) *TPM {
|
||||||
|
@ -19,19 +19,17 @@ package attest
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/go-tpm/tpm2"
|
|
||||||
"github.com/google/go-tpm/tpmutil"
|
|
||||||
"github.com/google/go-tspi/attestation"
|
"github.com/google/go-tspi/attestation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// key12 represents a key bound to a TPM 1.2 device via tcsd.
|
// trousersKey12 represents a key bound to a TPM 1.2 device via tcsd.
|
||||||
type key12 struct {
|
type trousersKey12 struct {
|
||||||
blob []byte
|
blob []byte
|
||||||
public []byte
|
public []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKey12(blob, public []byte) ak {
|
func newTrousersKey12(blob, public []byte) ak {
|
||||||
return &key12{
|
return &trousersKey12{
|
||||||
blob: blob,
|
blob: blob,
|
||||||
public: public,
|
public: public,
|
||||||
}
|
}
|
||||||
@ -39,7 +37,7 @@ func newKey12(blob, public []byte) ak {
|
|||||||
|
|
||||||
// Marshal represents the key in a persistent format which may be
|
// Marshal represents the key in a persistent format which may be
|
||||||
// loaded at a later time using tpm.LoadKey().
|
// loaded at a later time using tpm.LoadKey().
|
||||||
func (k *key12) marshal() ([]byte, error) {
|
func (k *trousersKey12) marshal() ([]byte, error) {
|
||||||
out := serializedKey{
|
out := serializedKey{
|
||||||
Encoding: keyEncodingEncrypted,
|
Encoding: keyEncodingEncrypted,
|
||||||
TPMVersion: TPMVersion12,
|
TPMVersion: TPMVersion12,
|
||||||
@ -49,12 +47,12 @@ func (k *key12) marshal() ([]byte, error) {
|
|||||||
return out.Serialize()
|
return out.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) close(tpm tpmBase) error {
|
func (k *trousersKey12) close(tpm tpmBase) error {
|
||||||
return nil // No state for tpm 1.2.
|
return nil // No state for tpm 1.2.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
func (k *trousersKey12) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||||
t, ok := tb.(*linuxTPM)
|
t, ok := tb.(*trousersTPM)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
||||||
}
|
}
|
||||||
@ -66,8 +64,8 @@ func (k *key12) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte,
|
|||||||
return cred, nil
|
return cred, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
func (k *trousersKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||||
t, ok := tb.(*linuxTPM)
|
t, ok := tb.(*trousersTPM)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
||||||
}
|
}
|
||||||
@ -87,104 +85,9 @@ func (k *key12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) attestationParameters() AttestationParameters {
|
func (k *trousersKey12) attestationParameters() AttestationParameters {
|
||||||
return AttestationParameters{
|
return AttestationParameters{
|
||||||
Public: k.public,
|
Public: k.public,
|
||||||
UseTCSDActivationFormat: true,
|
UseTCSDActivationFormat: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func newKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) ak {
|
|
||||||
return &key20{
|
|
||||||
hnd: hnd,
|
|
||||||
blob: blob,
|
|
||||||
public: public,
|
|
||||||
createData: createData,
|
|
||||||
createAttestation: createAttestation,
|
|
||||||
createSignature: createSig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *key20) close(t tpmBase) error {
|
|
||||||
tpm, ok := t.(*linuxTPM)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected *linuxTPM, got %T", t)
|
|
||||||
}
|
|
||||||
return tpm2.FlushContext(tpm.rwc, k.hnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *key20) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
|
||||||
t, ok := tb.(*linuxTPM)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
|
||||||
}
|
|
||||||
|
|
||||||
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:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *key20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
|
||||||
t, ok := tb.(*linuxTPM)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("expected *linuxTPM, got %T", tb)
|
|
||||||
}
|
|
||||||
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *key20) attestationParameters() AttestationParameters {
|
|
||||||
return AttestationParameters{
|
|
||||||
Public: k.public,
|
|
||||||
CreateData: k.createData,
|
|
||||||
CreateAttestation: k.createAttestation,
|
|
||||||
CreateSignature: k.createSignature,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -22,22 +22,22 @@ import (
|
|||||||
tpm1 "github.com/google/go-tpm/tpm"
|
tpm1 "github.com/google/go-tpm/tpm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// key12 represents a Windows-managed key on a TPM1.2 TPM.
|
// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
|
||||||
type key12 struct {
|
type windowsKey12 struct {
|
||||||
hnd uintptr
|
hnd uintptr
|
||||||
pcpKeyName string
|
pcpKeyName string
|
||||||
public []byte
|
public []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKey12(hnd uintptr, pcpKeyName string, public []byte) ak {
|
func newWindowsKey12(hnd uintptr, pcpKeyName string, public []byte) ak {
|
||||||
return &key12{
|
return &windowsKey12{
|
||||||
hnd: hnd,
|
hnd: hnd,
|
||||||
pcpKeyName: pcpKeyName,
|
pcpKeyName: pcpKeyName,
|
||||||
public: public,
|
public: public,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) marshal() ([]byte, error) {
|
func (k *windowsKey12) marshal() ([]byte, error) {
|
||||||
out := serializedKey{
|
out := serializedKey{
|
||||||
Encoding: keyEncodingOSManaged,
|
Encoding: keyEncodingOSManaged,
|
||||||
TPMVersion: TPMVersion12,
|
TPMVersion: TPMVersion12,
|
||||||
@ -47,7 +47,7 @@ func (k *key12) marshal() ([]byte, error) {
|
|||||||
return out.Serialize()
|
return out.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||||
tpm, ok := t.(*windowsTPM)
|
tpm, ok := t.(*windowsTPM)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
||||||
@ -59,7 +59,7 @@ func (k *key12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, e
|
|||||||
return decryptCredential(secretKey, in.Secret)
|
return decryptCredential(secretKey, in.Secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||||
if alg != HashSHA1 {
|
if alg != HashSHA1 {
|
||||||
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg)
|
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg)
|
||||||
}
|
}
|
||||||
@ -101,18 +101,18 @@ func (k *key12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) close(tpm tpmBase) error {
|
func (k *windowsKey12) close(tpm tpmBase) error {
|
||||||
return closeNCryptObject(k.hnd)
|
return closeNCryptObject(k.hnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key12) attestationParameters() AttestationParameters {
|
func (k *windowsKey12) attestationParameters() AttestationParameters {
|
||||||
return AttestationParameters{
|
return AttestationParameters{
|
||||||
Public: k.public,
|
Public: k.public,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// key20 represents a key bound to a TPM 2.0.
|
// windowsKey20 represents a key bound to a TPM 2.0.
|
||||||
type key20 struct {
|
type windowsKey20 struct {
|
||||||
hnd uintptr
|
hnd uintptr
|
||||||
|
|
||||||
pcpKeyName string
|
pcpKeyName string
|
||||||
@ -122,8 +122,8 @@ type key20 struct {
|
|||||||
createSignature []byte
|
createSignature []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) ak {
|
func newWindowsKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) ak {
|
||||||
return &key20{
|
return &windowsKey20{
|
||||||
hnd: hnd,
|
hnd: hnd,
|
||||||
pcpKeyName: pcpKeyName,
|
pcpKeyName: pcpKeyName,
|
||||||
public: public,
|
public: public,
|
||||||
@ -133,7 +133,7 @@ func newKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key20) marshal() ([]byte, error) {
|
func (k *windowsKey20) marshal() ([]byte, error) {
|
||||||
out := serializedKey{
|
out := serializedKey{
|
||||||
Encoding: keyEncodingOSManaged,
|
Encoding: keyEncodingOSManaged,
|
||||||
TPMVersion: TPMVersion20,
|
TPMVersion: TPMVersion20,
|
||||||
@ -147,7 +147,7 @@ func (k *key20) marshal() ([]byte, error) {
|
|||||||
return out.Serialize()
|
return out.Serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||||
tpm, ok := t.(*windowsTPM)
|
tpm, ok := t.(*windowsTPM)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
|
||||||
@ -155,7 +155,7 @@ func (k *key20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, e
|
|||||||
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
|
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||||
t, ok := tb.(*windowsTPM)
|
t, ok := tb.(*windowsTPM)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
|
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
|
||||||
@ -172,11 +172,11 @@ func (k *key20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
|||||||
return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce)
|
return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key20) close(tpm tpmBase) error {
|
func (k *windowsKey20) close(tpm tpmBase) error {
|
||||||
return closeNCryptObject(k.hnd)
|
return closeNCryptObject(k.hnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *key20) attestationParameters() AttestationParameters {
|
func (k *windowsKey20) attestationParameters() AttestationParameters {
|
||||||
return AttestationParameters{
|
return AttestationParameters{
|
||||||
Public: k.public,
|
Public: k.public,
|
||||||
CreateData: k.createData,
|
CreateData: k.createData,
|
||||||
|
@ -18,9 +18,7 @@ package attest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -29,31 +27,17 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/certificate-transparency-go/x509"
|
"github.com/google/certificate-transparency-go/x509"
|
||||||
|
"github.com/google/go-tspi/attestation"
|
||||||
"github.com/google/go-tspi/tspi" //for tpm12 support
|
"github.com/google/go-tspi/tspi" //for tpm12 support
|
||||||
"github.com/google/go-tspi/tspiconst"
|
"github.com/google/go-tspi/tspiconst"
|
||||||
|
|
||||||
"github.com/google/go-tpm/tpm2"
|
"github.com/google/go-tpm/tpm2"
|
||||||
"github.com/google/go-tpm/tpmutil"
|
|
||||||
"github.com/google/go-tspi/attestation"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tpmRoot = "/sys/class/tpm"
|
tpmRoot = "/sys/class/tpm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// linuxTPM interfaces with a TPM device on the system.
|
|
||||||
type linuxTPM struct {
|
|
||||||
version TPMVersion
|
|
||||||
interf TPMInterface
|
|
||||||
|
|
||||||
sysPath string
|
|
||||||
rwc io.ReadWriteCloser
|
|
||||||
ctx *tspi.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*linuxTPM) isTPMBase() {}
|
|
||||||
|
|
||||||
func probeSystemTPMs() ([]probedTPM, error) {
|
func probeSystemTPMs() ([]probedTPM, error) {
|
||||||
var tpms []probedTPM
|
var tpms []probedTPM
|
||||||
|
|
||||||
@ -84,27 +68,30 @@ func probeSystemTPMs() ([]probedTPM, error) {
|
|||||||
return tpms, nil
|
return tpms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openTPM(tpm probedTPM) (*TPM, error) {
|
type linuxCmdChannel struct {
|
||||||
interf := TPMInterfaceDirect
|
io.ReadWriteCloser
|
||||||
var rwc io.ReadWriteCloser
|
}
|
||||||
var ctx *tspi.Context
|
|
||||||
var err error
|
|
||||||
|
|
||||||
|
// MeasurementLog implements CommandChannelTPM20.
|
||||||
|
func (cc *linuxCmdChannel) MeasurementLog() ([]byte, error) {
|
||||||
|
return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||||
|
}
|
||||||
|
|
||||||
|
func openTPM(tpm probedTPM) (*TPM, error) {
|
||||||
switch tpm.Version {
|
switch tpm.Version {
|
||||||
case TPMVersion12:
|
case TPMVersion12:
|
||||||
// TPM1.2 must be using Daemon (Connect will fail if not the case)
|
// TPM1.2 must be using Daemon (Connect will fail if not the case)
|
||||||
interf = TPMInterfaceDaemonManaged
|
ctx, err := tspi.NewContext()
|
||||||
ctx, err = tspi.NewContext()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = ctx.Connect(); err != nil {
|
||||||
err = ctx.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &TPM{tpm: &trousersTPM{ctx: ctx}}, nil
|
||||||
|
|
||||||
case TPMVersion20:
|
case TPMVersion20:
|
||||||
|
interf := TPMInterfaceDirect
|
||||||
// If the TPM has a kernel-provided resource manager, we should
|
// If the TPM has a kernel-provided resource manager, we should
|
||||||
// use that instead of communicating directly.
|
// use that instead of communicating directly.
|
||||||
devPath := path.Join("/dev", path.Base(tpm.Path))
|
devPath := path.Join("/dev", path.Base(tpm.Path))
|
||||||
@ -118,36 +105,36 @@ func openTPM(tpm probedTPM) (*TPM, error) {
|
|||||||
interf = TPMInterfaceKernelManaged
|
interf = TPMInterfaceKernelManaged
|
||||||
}
|
}
|
||||||
|
|
||||||
rwc, err = tpm2.OpenTPM(devPath)
|
rwc, err := tpm2.OpenTPM(devPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return &TPM{tpm: &linuxTPM{
|
return &TPM{tpm: &wrappedTPM20{
|
||||||
version: tpm.Version,
|
|
||||||
interf: interf,
|
interf: interf,
|
||||||
sysPath: tpm.Path,
|
rwc: &linuxCmdChannel{rwc},
|
||||||
rwc: rwc,
|
|
||||||
ctx: ctx,
|
|
||||||
}}, nil
|
}}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (t *linuxTPM) tpmVersion() TPMVersion {
|
|
||||||
return t.version
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *linuxTPM) close() error {
|
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
|
||||||
return t.ctx.Close()
|
|
||||||
case TPMVersion20:
|
|
||||||
return t.rwc.Close()
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported TPM version: %x", t.version)
|
return nil, fmt.Errorf("unsuported TPM version: %v", tpm.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trousersTPM interfaces with a TPM 1.2 device via tcsd.
|
||||||
|
type trousersTPM struct {
|
||||||
|
ctx *tspi.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*trousersTPM) isTPMBase() {}
|
||||||
|
|
||||||
|
func (t *trousersTPM) tpmVersion() TPMVersion {
|
||||||
|
return TPMVersion12
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *trousersTPM) close() error {
|
||||||
|
return t.ctx.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func readTPM12VendorAttributes(context *tspi.Context) (TCGVendorID, string, error) {
|
func readTPM12VendorAttributes(context *tspi.Context) (TCGVendorID, string, error) {
|
||||||
// TPM 1.2 doesn't seem to store vendor data (other than unique ID)
|
// TPM 1.2 doesn't seem to store vendor data (other than unique ID)
|
||||||
vendor, err := context.GetCapability(tspiconst.TSS_TPMCAP_PROPERTY, 4, tspiconst.TSS_TPMCAP_PROP_MANUFACTURER)
|
vendor, err := context.GetCapability(tspiconst.TSS_TPMCAP_PROPERTY, 4, tspiconst.TSS_TPMCAP_PROP_MANUFACTURER)
|
||||||
@ -162,61 +149,19 @@ func readTPM12VendorAttributes(context *tspi.Context) (TCGVendorID, string, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Info returns information about the TPM.
|
// Info returns information about the TPM.
|
||||||
func (t *linuxTPM) info() (*TPMInfo, error) {
|
func (t *trousersTPM) info() (*TPMInfo, error) {
|
||||||
tInfo := TPMInfo{
|
tInfo := TPMInfo{
|
||||||
Version: t.version,
|
Version: TPMVersion12,
|
||||||
Interface: t.interf,
|
Interface: TPMInterfaceDaemonManaged,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
if tInfo.Manufacturer, tInfo.VendorInfo, err = readTPM12VendorAttributes(t.ctx); err != nil {
|
||||||
tInfo.Manufacturer, tInfo.VendorInfo, err = readTPM12VendorAttributes(t.ctx)
|
|
||||||
case TPMVersion20:
|
|
||||||
var t2Info tpm20Info
|
|
||||||
t2Info, err = readTPM2VendorAttributes(t.rwc)
|
|
||||||
tInfo.Manufacturer = t2Info.manufacturer
|
|
||||||
tInfo.VendorInfo = t2Info.vendor
|
|
||||||
tInfo.FirmwareVersionMajor = t2Info.fwMajor
|
|
||||||
tInfo.FirmwareVersionMinor = t2Info.fwMinor
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tInfo, nil
|
return &tInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return value: handle, whether we generated a new one, error
|
|
||||||
func (t *linuxTPM) 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.
|
|
||||||
return pHnd, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyHnd tpmutil.Handle
|
|
||||||
switch pHnd {
|
|
||||||
case commonSrkEquivalentHandle:
|
|
||||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate)
|
|
||||||
case commonEkEquivalentHandle:
|
|
||||||
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", defaultEKTemplate)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, fmt.Errorf("CreatePrimary failed: %v", err)
|
|
||||||
}
|
|
||||||
defer tpm2.FlushContext(t.rwc, keyHnd)
|
|
||||||
|
|
||||||
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, pHnd)
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pHnd, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
|
func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
|
||||||
ekCert, err := attestation.GetEKCert(ctx)
|
ekCert, err := attestation.GetEKCert(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -225,9 +170,7 @@ func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
|
|||||||
return ParseEKCertificate(ekCert)
|
return ParseEKCertificate(ekCert)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linuxTPM) eks() ([]EK, error) {
|
func (t *trousersTPM) eks() ([]EK, error) {
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
|
||||||
cert, err := readEKCertFromNVRAM12(t.ctx)
|
cert, err := readEKCertFromNVRAM12(t.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err)
|
return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err)
|
||||||
@ -235,88 +178,18 @@ func (t *linuxTPM) eks() ([]EK, error) {
|
|||||||
return []EK{
|
return []EK{
|
||||||
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert},
|
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert},
|
||||||
}, nil
|
}, nil
|
||||||
case TPMVersion20:
|
|
||||||
if cert, err := readEKCertFromNVRAM20(t.rwc); err == nil {
|
|
||||||
return []EK{
|
|
||||||
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create an EK.
|
|
||||||
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", defaultEKTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("EK CreatePrimary failed: %v", err)
|
|
||||||
}
|
|
||||||
defer tpm2.FlushContext(t.rwc, ekHnd)
|
|
||||||
|
|
||||||
pub, _, _, err := tpm2.ReadPublic(t.rwc, ekHnd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("EK ReadPublic failed: %v", err)
|
|
||||||
}
|
|
||||||
if pub.RSAParameters == nil {
|
|
||||||
return nil, errors.New("ECC EK not yet supported")
|
|
||||||
}
|
|
||||||
return []EK{
|
|
||||||
{
|
|
||||||
Public: &rsa.PublicKey{
|
|
||||||
E: int(pub.RSAParameters.Exponent()),
|
|
||||||
N: pub.RSAParameters.Modulus(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linuxTPM) newAK(opts *AKConfig) (*AK, error) {
|
func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) {
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
|
||||||
pub, blob, err := attestation.CreateAIK(t.ctx)
|
pub, blob, err := attestation.CreateAIK(t.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("CreateAIK failed: %v", err)
|
return nil, fmt.Errorf("CreateAIK failed: %v", err)
|
||||||
}
|
}
|
||||||
return &AK{ak: newKey12(blob, pub)}, nil
|
return &AK{ak: newTrousersKey12(blob, pub)}, nil
|
||||||
case TPMVersion20:
|
|
||||||
// TODO(jsonp): Abstract choice of hierarchy & parent.
|
|
||||||
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", akTemplate)
|
|
||||||
if err != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
// If any errors occur, free the AK's handle.
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
tpm2.FlushContext(t.rwc, keyHandle)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// We can only certify the creation immediately afterwards, so we cache the result.
|
|
||||||
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, tpm2.SigScheme{tpm2.AlgRSASSA, tpm2.AlgSHA256, 0}, &tix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
|
|
||||||
}
|
|
||||||
// Pack the raw structure into a TPMU_SIGNATURE.
|
|
||||||
signature, err := tpmutil.Pack(tpm2.AlgRSASSA, tpm2.AlgSHA256, tpmutil.U16Bytes(sig))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
|
|
||||||
}
|
|
||||||
return &AK{ak: newKey20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linuxTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
func (t *trousersTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||||
sKey, err := deserializeKey(opaqueBlob, t.version)
|
sKey, err := deserializeKey(opaqueBlob, TPMVersion12)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("deserializeKey() failed: %v", err)
|
return nil, fmt.Errorf("deserializeKey() failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -324,22 +197,7 @@ func (t *linuxTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
|||||||
return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
|
return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch sKey.TPMVersion {
|
return &AK{ak: newTrousersKey12(sKey.Blob, sKey.Public)}, nil
|
||||||
case TPMVersion12:
|
|
||||||
return &AK{ak: newKey12(sKey.Blob, sKey.Public)}, nil
|
|
||||||
case TPMVersion20:
|
|
||||||
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get SRK handle: %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 &AK{ak: newKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("cannot load AK with TPM version: %v", sKey.TPMVersion)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allPCRs12 returns a map of all the PCR values on the TPM
|
// allPCRs12 returns a map of all the PCR values on the TPM
|
||||||
@ -357,30 +215,15 @@ func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) {
|
|||||||
return PCRs, nil
|
return PCRs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linuxTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
func (t *trousersTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
||||||
var PCRs map[uint32][]byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
|
||||||
if alg != HashSHA1 {
|
if alg != HashSHA1 {
|
||||||
return nil, fmt.Errorf("non-SHA1 algorithm %v is not supported on TPM 1.2", alg)
|
return nil, fmt.Errorf("non-SHA1 algorithm %v is not supported on TPM 1.2", alg)
|
||||||
}
|
}
|
||||||
PCRs, err = allPCRs12(t.ctx)
|
PCRs, err := allPCRs12(t.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read PCRs: %v", err)
|
return nil, fmt.Errorf("failed to read PCRs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TPMVersion20:
|
|
||||||
PCRs, err = readAllPCRs20(t.rwc, alg.goTPMAlg())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read PCRs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make([]PCR, len(PCRs))
|
out := make([]PCR, len(PCRs))
|
||||||
for index, digest := range PCRs {
|
for index, digest := range PCRs {
|
||||||
out[int(index)] = PCR{
|
out[int(index)] = PCR{
|
||||||
@ -393,6 +236,6 @@ func (t *linuxTPM) pcrs(alg HashAlg) ([]PCR, error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linuxTPM) measurementLog() ([]byte, error) {
|
func (t *trousersTPM) measurementLog() ([]byte, error) {
|
||||||
return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements")
|
||||||
}
|
}
|
||||||
|
@ -285,9 +285,9 @@ func (t *windowsTPM) newAK(opts *AKConfig) (*AK, error) {
|
|||||||
|
|
||||||
switch t.version {
|
switch t.version {
|
||||||
case TPMVersion12:
|
case TPMVersion12:
|
||||||
return &AK{ak: newKey12(kh, name, props.RawPublic)}, nil
|
return &AK{ak: newWindowsKey12(kh, name, props.RawPublic)}, nil
|
||||||
case TPMVersion20:
|
case TPMVersion20:
|
||||||
return &AK{ak: newKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, nil
|
return &AK{ak: newWindowsKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, 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)
|
||||||
}
|
}
|
||||||
@ -309,9 +309,9 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) {
|
|||||||
|
|
||||||
switch t.version {
|
switch t.version {
|
||||||
case TPMVersion12:
|
case TPMVersion12:
|
||||||
return &AK{ak: newKey12(hnd, sKey.Name, sKey.Public)}, nil
|
return &AK{ak: newWindowsKey12(hnd, sKey.Name, sKey.Public)}, nil
|
||||||
case TPMVersion20:
|
case TPMVersion20:
|
||||||
return &AK{ak: newKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
|
return &AK{ak: newWindowsKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, 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)
|
||||||
}
|
}
|
||||||
|
293
attest/wrapped_tpm20.go
Normal file
293
attest/wrapped_tpm20.go
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// Copyright 2020 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 (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/go-tpm/tpm2"
|
||||||
|
"github.com/google/go-tpm/tpmutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// wrappedTPM20 interfaces with a TPM 2.0 command channel.
|
||||||
|
type wrappedTPM20 struct {
|
||||||
|
interf TPMInterface
|
||||||
|
rwc CommandChannelTPM20
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*wrappedTPM20) isTPMBase() {}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) tpmVersion() TPMVersion {
|
||||||
|
return TPMVersion20
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) close() error {
|
||||||
|
return t.rwc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info returns information about the TPM.
|
||||||
|
func (t *wrappedTPM20) info() (*TPMInfo, error) {
|
||||||
|
var (
|
||||||
|
tInfo = TPMInfo{
|
||||||
|
Version: TPMVersion20,
|
||||||
|
Interface: t.interf,
|
||||||
|
}
|
||||||
|
t2Info tpm20Info
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if t2Info, err = readTPM2VendorAttributes(t.rwc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tInfo.Manufacturer = t2Info.manufacturer
|
||||||
|
tInfo.VendorInfo = t2Info.vendor
|
||||||
|
tInfo.FirmwareVersionMajor = t2Info.fwMajor
|
||||||
|
tInfo.FirmwareVersionMinor = t2Info.fwMinor
|
||||||
|
return &tInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return value: handle, whether we generated a new one, error
|
||||||
|
func (t *wrappedTPM20) 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.
|
||||||
|
return pHnd, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyHnd tpmutil.Handle
|
||||||
|
switch pHnd {
|
||||||
|
case commonSrkEquivalentHandle:
|
||||||
|
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", defaultSRKTemplate)
|
||||||
|
case commonEkEquivalentHandle:
|
||||||
|
keyHnd, _, err = tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", defaultEKTemplate)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, fmt.Errorf("CreatePrimary failed: %v", err)
|
||||||
|
}
|
||||||
|
defer tpm2.FlushContext(t.rwc, keyHnd)
|
||||||
|
|
||||||
|
err = tpm2.EvictControl(t.rwc, "", tpm2.HandleOwner, keyHnd, pHnd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, fmt.Errorf("EvictControl failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pHnd, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) eks() ([]EK, error) {
|
||||||
|
if cert, err := readEKCertFromNVRAM20(t.rwc); err == nil {
|
||||||
|
return []EK{
|
||||||
|
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to create an EK.
|
||||||
|
ekHnd, _, err := tpm2.CreatePrimary(t.rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", defaultEKTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("EK CreatePrimary failed: %v", err)
|
||||||
|
}
|
||||||
|
defer tpm2.FlushContext(t.rwc, ekHnd)
|
||||||
|
|
||||||
|
pub, _, _, err := tpm2.ReadPublic(t.rwc, ekHnd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("EK ReadPublic failed: %v", err)
|
||||||
|
}
|
||||||
|
if pub.RSAParameters == nil {
|
||||||
|
return nil, errors.New("ECC EK not yet supported")
|
||||||
|
}
|
||||||
|
return []EK{
|
||||||
|
{
|
||||||
|
Public: &rsa.PublicKey{
|
||||||
|
E: int(pub.RSAParameters.Exponent()),
|
||||||
|
N: pub.RSAParameters.Modulus(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) newAK(opts *AKConfig) (*AK, error) {
|
||||||
|
// TODO(jsonp): Abstract choice of hierarchy & parent.
|
||||||
|
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", akTemplate)
|
||||||
|
if err != nil {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// If any errors occur, free the AK's handle.
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
tpm2.FlushContext(t.rwc, keyHandle)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We can only certify the creation immediately afterwards, so we cache the result.
|
||||||
|
attestation, sig, err := tpm2.CertifyCreation(t.rwc, "", keyHandle, keyHandle, nil, creationHash, tpm2.SigScheme{tpm2.AlgRSASSA, tpm2.AlgSHA256, 0}, &tix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CertifyCreation failed: %v", err)
|
||||||
|
}
|
||||||
|
// Pack the raw structure into a TPMU_SIGNATURE.
|
||||||
|
signature, err := tpmutil.Pack(tpm2.AlgRSASSA, tpm2.AlgSHA256, tpmutil.U16Bytes(sig))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to pack TPMT_SIGNATURE: %v", err)
|
||||||
|
}
|
||||||
|
return &AK{ak: newWrappedKey20(keyHandle, blob, pub, creationData, attestation, signature)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) {
|
||||||
|
sKey, err := deserializeKey(opaqueBlob, TPMVersion20)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
srk, _, err := t.getPrimaryKeyHandle(commonSrkEquivalentHandle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get SRK handle: %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 &AK{ak: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) {
|
||||||
|
PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read PCRs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]PCR, len(PCRs))
|
||||||
|
for index, digest := range PCRs {
|
||||||
|
out[int(index)] = PCR{
|
||||||
|
Index: int(index),
|
||||||
|
Digest: digest,
|
||||||
|
DigestAlg: alg.cryptoHash(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wrappedTPM20) measurementLog() ([]byte, error) {
|
||||||
|
return t.rwc.MeasurementLog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrappedKey20 represents a key manipulated through a *wrappedTPM20.
|
||||||
|
type wrappedKey20 struct {
|
||||||
|
hnd tpmutil.Handle
|
||||||
|
|
||||||
|
blob []byte
|
||||||
|
public []byte // used by both TPM1.2 and 2.0
|
||||||
|
createData []byte
|
||||||
|
createAttestation []byte
|
||||||
|
createSignature []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedKey20(hnd tpmutil.Handle, blob, public, createData, createAttestation, createSig []byte) ak {
|
||||||
|
return &wrappedKey20{
|
||||||
|
hnd: hnd,
|
||||||
|
blob: blob,
|
||||||
|
public: public,
|
||||||
|
createData: createData,
|
||||||
|
createAttestation: createAttestation,
|
||||||
|
createSignature: createSig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *wrappedKey20) 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *wrappedKey20) close(t tpmBase) error {
|
||||||
|
tpm, ok := t.(*wrappedTPM20)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected *wrappedTPM20, got %T", t)
|
||||||
|
}
|
||||||
|
return tpm2.FlushContext(tpm.rwc, k.hnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([]byte, error) {
|
||||||
|
t, ok := tb.(*wrappedTPM20)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
||||||
|
}
|
||||||
|
|
||||||
|
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:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) {
|
||||||
|
t, ok := tb.(*wrappedTPM20)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb)
|
||||||
|
}
|
||||||
|
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *wrappedKey20) attestationParameters() AttestationParameters {
|
||||||
|
return AttestationParameters{
|
||||||
|
Public: k.public,
|
||||||
|
CreateData: k.createData,
|
||||||
|
CreateAttestation: k.createAttestation,
|
||||||
|
CreateSignature: k.createSignature,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user