mirror of
https://github.com/google/go-attestation.git
synced 2025-03-10 14:33:54 +00:00
Switch over to trying the PCP provider for TPM 1.2, to mitigate missing ownerauth. (#25)
* Implement decoding for TPM 1.2 PCP AIK properties * Switch all TPM 1.2 methods that rely on ownerAuth to use the PCP API.
This commit is contained in:
parent
8e4a5ce762
commit
2da0098d9d
@ -316,6 +316,71 @@ func (h *winPCP) AIKProperties(kh uintptr) (*aikProps, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r := bytes.NewReader(idBlob)
|
r := bytes.NewReader(idBlob)
|
||||||
|
// Because the TPM 1.2 blob leads with a version tag,
|
||||||
|
// we can switch decoding logic based on it.
|
||||||
|
if bytes.Equal(idBlob[0:4], []byte{1, 1, 0, 0}) {
|
||||||
|
return decodeAIKProps12(r)
|
||||||
|
}
|
||||||
|
return decodeAIKProps20(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeAIKProps12 separates the single TPM 1.2 blob from the PCP property
|
||||||
|
// into its constituents, returning information about the public key
|
||||||
|
// of the AIK.
|
||||||
|
func decodeAIKProps12(r *bytes.Reader) (*aikProps, error) {
|
||||||
|
var out aikProps
|
||||||
|
// Skip over fixed-size fields in TPM_IDENTITY_CONTENTS which
|
||||||
|
// we don't need to read.
|
||||||
|
// Specifically: ver, ordinal, & labelPrivCADigest.
|
||||||
|
r.Seek(4+4+20, io.SeekCurrent)
|
||||||
|
pubKeyStartIdx := int(r.Size()) - r.Len()
|
||||||
|
|
||||||
|
// Skip over fixed-size key parameters in TPM_PUBKEY, so
|
||||||
|
// we can read the length of the exponent &
|
||||||
|
// determine where the pubkey structure ends.
|
||||||
|
// Specifically: algID, encScheme, sigScheme, paramSize, keyLength,
|
||||||
|
// and numPrimes.
|
||||||
|
r.Seek(4+2+2+4+4+4, io.SeekCurrent)
|
||||||
|
|
||||||
|
// Read the size of the exponent section.
|
||||||
|
var exponentSize uint32
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &exponentSize); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode exponentSize: %v", err)
|
||||||
|
}
|
||||||
|
// Consume the bytes representing the exponent.
|
||||||
|
exp := make([]byte, int(exponentSize))
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &exp); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode exp: %v", err)
|
||||||
|
}
|
||||||
|
// Read the size of the key data.
|
||||||
|
var keyDataSize uint32
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &keyDataSize); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode keyDataSize: %v", err)
|
||||||
|
}
|
||||||
|
// Seek to the end of the key data.
|
||||||
|
r.Seek(int64(keyDataSize), io.SeekCurrent)
|
||||||
|
|
||||||
|
// Read the trailing signature.
|
||||||
|
out.RawSignature = make([]byte, r.Len())
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &out.RawSignature); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode signature: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back to the location of the public key, and consume it.
|
||||||
|
r.Seek(int64(pubKeyStartIdx), io.SeekStart)
|
||||||
|
out.RawPublic = make([]byte, 24+int(exponentSize)+4+int(keyDataSize))
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &out.RawPublic); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode public: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeAIKProps20 separates the single TPM 2.0 blob from the PCP property
|
||||||
|
// into its constituents. For TPM 2.0 devices, these are bytes representing
|
||||||
|
// the following structures: TPM2B_PUBLIC, TPM2B_CREATION_DATA, TPM2B_ATTEST,
|
||||||
|
// and TPMT_SIGNATURE.
|
||||||
|
func decodeAIKProps20(r *bytes.Reader) (*aikProps, error) {
|
||||||
var out aikProps
|
var out aikProps
|
||||||
|
|
||||||
var publicSize uint16
|
var publicSize uint16
|
||||||
@ -351,7 +416,6 @@ func (h *winPCP) AIKProperties(kh uintptr) (*aikProps, error) {
|
|||||||
if err := binary.Read(r, binary.BigEndian, &out.RawSignature); err != nil {
|
if err := binary.Read(r, binary.BigEndian, &out.RawSignature); err != nil {
|
||||||
return nil, fmt.Errorf("failed to decode TPMT_SIGNATURE.data: %v", err)
|
return nil, fmt.Errorf("failed to decode TPMT_SIGNATURE.data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,19 +19,16 @@ package attest
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/google/certificate-transparency-go/x509"
|
|
||||||
tpm1 "github.com/google/go-tpm/tpm"
|
tpm1 "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"
|
||||||
tpmtbs "github.com/google/go-tpm/tpmutil/tbs"
|
tpmtbs "github.com/google/go-tpm/tpmutil/tbs"
|
||||||
"golang.org/x/sys/windows/registry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var wellKnownAuth [20]byte
|
var wellKnownAuth [20]byte
|
||||||
@ -141,66 +138,9 @@ func (t *TPM) Info() (*TPMInfo, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOwnerAuth() ([20]byte, error) {
|
|
||||||
var ret [20]byte
|
|
||||||
regkey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\\CurrentControlSet\\Services\\TPM\\WMI\\Admin`, registry.QUERY_VALUE)
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
defer regkey.Close()
|
|
||||||
|
|
||||||
ownerAuthUTF16, _, err := regkey.GetStringValue("OwnerAuthFull")
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
ownerAuthBytes, err := base64.StdEncoding.DecodeString(ownerAuthUTF16)
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
if size := len(ownerAuthBytes); size != 20 {
|
|
||||||
return ret, fmt.Errorf("OwnerAuth is an unexpected size: %d", size)
|
|
||||||
}
|
|
||||||
// Check OwnerAuthStatus first maybe?
|
|
||||||
for i := range ret {
|
|
||||||
ret[i] = ownerAuthBytes[i]
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TPM) readEKCert12() ([]*x509.Certificate, error) {
|
|
||||||
tpm, err := t.pcp.TPMCommandInterface()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ownAuth, err := getOwnerAuth()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ekcert, err := tpm1.ReadEKCert(tpm, ownAuth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cert, err := x509.ParseCertificate(ekcert)
|
|
||||||
if err != nil && x509.IsFatal(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []*x509.Certificate{cert}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EKs returns the Endorsement Keys burned-in to the platform.
|
// EKs returns the Endorsement Keys burned-in to the platform.
|
||||||
func (t *TPM) EKs() ([]PlatformEK, error) {
|
func (t *TPM) EKs() ([]PlatformEK, error) {
|
||||||
var ekCerts []*x509.Certificate
|
ekCerts, err := t.pcp.EKCerts()
|
||||||
var err error
|
|
||||||
switch t.version {
|
|
||||||
case TPMVersion12:
|
|
||||||
ekCerts, err = t.readEKCert12()
|
|
||||||
|
|
||||||
case TPMVersion20:
|
|
||||||
ekCerts, err = t.pcp.EKCerts()
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read EKCerts: %v", err)
|
return nil, fmt.Errorf("could not read EKCerts: %v", err)
|
||||||
}
|
}
|
||||||
@ -247,22 +187,7 @@ func (k *Key) ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, erro
|
|||||||
if k.TPMVersion != tpm.version {
|
if k.TPMVersion != tpm.version {
|
||||||
return nil, fmt.Errorf("tpm and key version mismatch")
|
return nil, fmt.Errorf("tpm and key version mismatch")
|
||||||
}
|
}
|
||||||
switch tpm.version {
|
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
|
||||||
case TPMVersion12:
|
|
||||||
rw, err := tpm.pcp.TPMCommandInterface()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pcp.TPMCommandInterface() failed: %v", err)
|
|
||||||
}
|
|
||||||
ownAuth, err := getOwnerAuth()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getOwnerAuth failed: %v", err)
|
|
||||||
}
|
|
||||||
return tpm1.ActivateIdentity(rw, wellKnownAuth[:], ownAuth[:], k.hnd12, in.Credential, in.Secret)
|
|
||||||
case TPMVersion20:
|
|
||||||
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", tpm.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Key) quote12(tpm io.ReadWriter, nonce []byte) (*Quote, error) {
|
func (k *Key) quote12(tpm io.ReadWriter, nonce []byte) (*Quote, error) {
|
||||||
@ -316,83 +241,39 @@ func (k *Key) Quote(t *TPM, nonce []byte, alg tpm2.Algorithm) (*Quote, error) {
|
|||||||
|
|
||||||
// Close frees any resources associated with the key.
|
// Close frees any resources associated with the key.
|
||||||
func (k *Key) Close(tpm *TPM) error {
|
func (k *Key) Close(tpm *TPM) error {
|
||||||
switch tpm.version {
|
return closeNCryptObject(k.hnd)
|
||||||
case TPMVersion12:
|
|
||||||
return nil
|
|
||||||
case TPMVersion20:
|
|
||||||
return closeNCryptObject(k.hnd)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported TPM version: %x", tpm.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MintAIK creates a persistent attestation key. The returned key must be
|
// MintAIK creates a persistent attestation key. The returned key must be
|
||||||
// closed with a call to key.Close() when the caller has finished using it.
|
// closed with a call to key.Close() when the caller has finished using it.
|
||||||
func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
|
func (t *TPM) MintAIK(opts *MintOptions) (*Key, error) {
|
||||||
switch t.version {
|
nameHex := make([]byte, 5)
|
||||||
case TPMVersion12:
|
if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) {
|
||||||
tpm, err := t.pcp.TPMCommandInterface()
|
return nil, fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get command interface on 1.2 device: %v", err)
|
|
||||||
}
|
|
||||||
ownAuth, err := getOwnerAuth()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read owner auth from registry: %v", err)
|
|
||||||
}
|
|
||||||
blob, err := tpm1.MakeIdentity(tpm, wellKnownAuth[:], ownAuth[:], wellKnownAuth[:], nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("MakeIdentityEx failed: %v", err)
|
|
||||||
}
|
|
||||||
hnd, err := tpm1.LoadKey2(tpm, blob, wellKnownAuth[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("LoadKey2 failed: %v", err)
|
|
||||||
}
|
|
||||||
pub, err := tpm1.GetPubKey(tpm, hnd, wellKnownAuth[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetPubKey failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Key{
|
|
||||||
hnd12: hnd,
|
|
||||||
KeyEncoding: KeyEncodingOSManaged,
|
|
||||||
TPMVersion: t.version,
|
|
||||||
Purpose: AttestationKey,
|
|
||||||
KeyBlob: blob,
|
|
||||||
Public: pub,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TPMVersion20:
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
name := fmt.Sprintf("aik-%x", nameHex)
|
|
||||||
|
|
||||||
kh, err := t.pcp.MintAIK(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pcp failed to mint attestation key: %v", err)
|
|
||||||
}
|
|
||||||
props, err := t.pcp.AIKProperties(kh)
|
|
||||||
if err != nil {
|
|
||||||
closeNCryptObject(kh)
|
|
||||||
return nil, fmt.Errorf("pcp failed to read attestation key properties: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Key{
|
|
||||||
hnd: kh,
|
|
||||||
KeyEncoding: KeyEncodingOSManaged,
|
|
||||||
TPMVersion: t.version,
|
|
||||||
Purpose: AttestationKey,
|
|
||||||
PCPKeyName: name,
|
|
||||||
Public: props.RawPublic,
|
|
||||||
CreateData: props.RawCreationData,
|
|
||||||
CreateAttestation: props.RawAttest,
|
|
||||||
CreateSignature: props.RawSignature,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
}
|
||||||
|
name := fmt.Sprintf("aik-%x", nameHex)
|
||||||
|
|
||||||
|
kh, err := t.pcp.MintAIK(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("pcp failed to mint attestation key: %v", err)
|
||||||
|
}
|
||||||
|
props, err := t.pcp.AIKProperties(kh)
|
||||||
|
if err != nil {
|
||||||
|
closeNCryptObject(kh)
|
||||||
|
return nil, fmt.Errorf("pcp failed to read attestation key properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Key{
|
||||||
|
hnd: kh,
|
||||||
|
KeyEncoding: KeyEncodingOSManaged,
|
||||||
|
TPMVersion: t.version,
|
||||||
|
Purpose: AttestationKey,
|
||||||
|
PCPKeyName: name,
|
||||||
|
Public: props.RawPublic,
|
||||||
|
CreateData: props.RawCreationData,
|
||||||
|
CreateAttestation: props.RawAttest,
|
||||||
|
CreateSignature: props.RawSignature,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadKey loads a previously-created key into the TPM for use.
|
// LoadKey loads a previously-created key into the TPM for use.
|
||||||
@ -414,23 +295,8 @@ func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) {
|
|||||||
return nil, fmt.Errorf("unsupported key kind: %x", k.Purpose)
|
return nil, fmt.Errorf("unsupported key kind: %x", k.Purpose)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.version {
|
if k.hnd, err = t.pcp.LoadKeyByName(k.PCPKeyName); err != nil {
|
||||||
case TPMVersion12:
|
return nil, fmt.Errorf("pcp failed to load key: %v", err)
|
||||||
tpm, err := t.pcp.TPMCommandInterface()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get interface to TPM: %v", err)
|
|
||||||
}
|
|
||||||
if k.hnd12, err = tpm1.LoadKey2(tpm, k.KeyBlob, wellKnownAuth[:]); err != nil {
|
|
||||||
return nil, fmt.Errorf("go-tpm failed to load key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case TPMVersion20:
|
|
||||||
if k.hnd, err = t.pcp.LoadKeyByName(k.PCPKeyName); err != nil {
|
|
||||||
return nil, fmt.Errorf("pcp failed to load key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported TPM version: %x", t.version)
|
|
||||||
}
|
}
|
||||||
return &k, nil
|
return &k, nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user