Skip to content

Commit

Permalink
attest: re-work EK API
Browse files Browse the repository at this point in the history
This PR adds:
* Renames 'PlatformEK' to 'EK'
* More consistant support of EKs without certificates
* Removes HTTP GET to Intel EK certificate service
* Always populates EK.Public
  • Loading branch information
ericchiang committed Aug 20, 2019
1 parent 6e2e869 commit 9e4e0b0
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 121 deletions.
41 changes: 34 additions & 7 deletions attest/attest-tool/attest-tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main

import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"encoding/hex"
Expand Down Expand Up @@ -107,9 +108,11 @@ func runCommand(tpm *attest.TPM) error {
return fmt.Errorf("failed to read EKs: %v", err)
}
for _, ek := range eks {
if ek.Cert != nil {
fmt.Printf("EK certificate: %x\n", ek.Cert.Raw)
data, err := encodeEK(ek)
if err != nil {
return fmt.Errorf("encoding ek: %v", err)
}
fmt.Printf("%s\n", data)
}

case "list-pcrs":
Expand Down Expand Up @@ -145,6 +148,33 @@ func runCommand(tpm *attest.TPM) error {
return nil
}

func encodeEK(ek attest.EK) ([]byte, error) {
if ek.Certificate != nil {
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: ek.Certificate.Raw,
}), nil
}
switch pub := ek.Public.(type) {
case *ecdsa.PublicKey:
data, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, fmt.Errorf("marshaling ec public key: %v", err)
}
return pem.EncodeToMemory(&pem.Block{
Type: "EC PUBLIC KEY",
Bytes: data,
}), nil
case *rsa.PublicKey:
return pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(pub),
}), nil
default:
return nil, fmt.Errorf("unsupported public key type %T", pub)
}
}

func runDump(tpm *attest.TPM) (*internal.Dump, error) {
var (
out internal.Dump
Expand Down Expand Up @@ -201,11 +231,8 @@ func rsaEKPEM(tpm *attest.TPM) ([]byte, error) {
buf bytes.Buffer
)
for _, ek := range eks {
if ek.Cert != nil && ek.Cert.PublicKeyAlgorithm == x509.RSA {
pk = ek.Cert.PublicKey.(*rsa.PublicKey)
break
} else if ek.Public != nil {
pk = ek.Public.(*rsa.PublicKey)
if pub, ok := ek.Public.(*rsa.PublicKey); ok {
pk = pub
break
}
}
Expand Down
16 changes: 12 additions & 4 deletions attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,19 @@ type PCR struct {
DigestAlg crypto.Hash
}

// PlatformEK represents a burned-in Endorsement Key, and its
// corrresponding EKCert (where present).
type PlatformEK struct {
Cert *x509.Certificate
// EK is a burned-in endorcement key bound to a TPM. This optionally contains
// a certificate that can chain to the TPM manufacturer.
type EK struct {
// Public key of the EK.
Public crypto.PublicKey

// Certificate is the EK certificate for TPMs that provide it.
Certificate *x509.Certificate

// For Intel TPMs, Intel hosts certificates at a public URL derived from the
// Public key. Clients or servers can perform an HTTP GET to this URL, and
// use ParseEKCertificate on the response body.
CertificateURL string
}

// AttestationParameters describes information about a key which is necessary
Expand Down
23 changes: 2 additions & 21 deletions attest/attest_simulated_tpm20_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ package attest
import (
"bytes"
"crypto"
"crypto/rsa"
"testing"

"github.com/google/certificate-transparency-go/x509"

"github.com/google/go-tpm-tools/simulator"
)

Expand All @@ -49,7 +46,7 @@ func TestSimTPM20EK(t *testing.T) {
if err != nil {
t.Errorf("EKs() failed: %v", err)
}
if len(eks) == 0 || (eks[0].Cert == nil && eks[0].Public == nil) {
if len(eks) == 0 || (eks[0].Public == nil) {
t.Errorf("EKs() = %v, want at least 1 EK with populated fields", eks)
}
}
Expand Down Expand Up @@ -99,22 +96,6 @@ func TestSimTPM20AIKCreateAndLoad(t *testing.T) {
}
}

// chooseEKPub selects the EK public which will be activated against.
func chooseEKPub(t *testing.T, eks []PlatformEK) crypto.PublicKey {
t.Helper()

for _, ek := range eks {
if ek.Cert != nil && ek.Cert.PublicKeyAlgorithm == x509.RSA {
return ek.Cert.PublicKey.(*rsa.PublicKey)
} else if ek.Public != nil {
return ek.Public
}
}

t.Skip("No suitable RSA EK found")
return nil
}

func TestSimTPM20ActivateCredential(t *testing.T) {
sim, tpm := setupSimulatedTPM(t)
defer sim.Close()
Expand All @@ -129,7 +110,7 @@ func TestSimTPM20ActivateCredential(t *testing.T) {
if err != nil {
t.Fatalf("EKs() failed: %v", err)
}
ek := chooseEKPub(t, EKs)
ek := chooseEK(t, EKs)

ap := ActivationParameters{
TPMVersion: TPMVersion20,
Expand Down
13 changes: 3 additions & 10 deletions attest/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ package attest
import (
"bytes"
"crypto"
"crypto/rsa"
"flag"
"testing"

"github.com/google/certificate-transparency-go/x509"
)

var (
Expand Down Expand Up @@ -121,18 +118,14 @@ func TestAIKCreateAndLoad(t *testing.T) {
}

// chooseEK selects the EK public which will be activated against.
func chooseEK(t *testing.T, eks []PlatformEK) crypto.PublicKey {
func chooseEK(t *testing.T, eks []EK) crypto.PublicKey {
t.Helper()

for _, ek := range eks {
if ek.Cert != nil && ek.Cert.PublicKeyAlgorithm == x509.RSA {
return ek.Cert.PublicKey.(*rsa.PublicKey)
} else if ek.Public != nil {
return ek.Public
}
return ek.Public
}

t.Skip("No suitable RSA EK found")
t.Fatalf("No suitable EK found")
return nil
}

Expand Down
8 changes: 3 additions & 5 deletions attest/attest_tpm12_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,14 @@ func TestTPM12EKs(t *testing.T) {
tpm := openTPM12(t)
defer tpm.Close()

EKs, err := tpm.EKs()
eks, err := tpm.EKs()
if err != nil {
t.Fatalf("Failed to get EKs: %v", err)
}

if len(EKs) == 0 {
if len(eks) == 0 {
t.Fatalf("EKs returned nothing")
}

t.Logf("EKCert Raw: %x\n", EKs[0].Cert.Raw)
}

func TestMintAIK(t *testing.T) {
Expand Down Expand Up @@ -151,7 +149,7 @@ func TestTPMActivateCredential(t *testing.T) {
if err != nil {
t.Fatalf("failed to read EKs: %v", err)
}
ek := chooseEKPub(t, EKs)
ek := chooseEK(t, EKs)

ap := ActivationParameters{
TPMVersion: TPMVersion12,
Expand Down
2 changes: 1 addition & 1 deletion attest/pcp_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func (h *winPCP) EKCerts() ([]*x509.Certificate, error) {

var out []*x509.Certificate
for _, der := range c {
cert, err := parseCert(der)
cert, err := ParseEKCertificate(der)
if err != nil {
return nil, err
}
Expand Down
21 changes: 19 additions & 2 deletions attest/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package attest

import (
"bytes"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
Expand Down Expand Up @@ -147,7 +150,8 @@ func readTPM2VendorAttributes(tpm io.ReadWriter) (tpm20Info, error) {
}, nil
}

func parseCert(ekCert []byte) (*x509.Certificate, error) {
// ParseEKCertificate parses a raw DER encoded EK certificate blob.
func ParseEKCertificate(ekCert []byte) (*x509.Certificate, error) {
var wasWrapped bool

// TCG PC Specific Implementation section 7.3.2 specifies
Expand Down Expand Up @@ -184,12 +188,25 @@ func parseCert(ekCert []byte) (*x509.Certificate, error) {
return c, nil
}

const (
manufacturerIntel = "Intel"
intelEKCertServiceURL = "https://ekop.intel.com/ekcertservice/"
)

func intelEKURL(ekPub *rsa.PublicKey) string {
pubHash := sha256.New()
pubHash.Write(ekPub.N.Bytes())
pubHash.Write([]byte{0x1, 0x00, 0x01})

return intelEKCertServiceURL + base64.URLEncoding.EncodeToString(pubHash.Sum(nil))
}

func readEKCertFromNVRAM20(tpm io.ReadWriter) (*x509.Certificate, error) {
ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, tpm2.HandleOwner, "", 0)
if err != nil {
return nil, fmt.Errorf("reading EK cert: %v", err)
}
return parseCert(ekCert)
return ParseEKCertificate(ekCert)
}

func quote20(tpm io.ReadWriter, aikHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte) (*Quote, error) {
Expand Down
60 changes: 31 additions & 29 deletions attest/tpm_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package attest

import (
"crypto"
"crypto/rsa"
"encoding/binary"
"errors"
Expand Down Expand Up @@ -221,51 +222,52 @@ func readEKCertFromNVRAM12(ctx *tspi.Context) (*x509.Certificate, error) {
if err != nil {
return nil, fmt.Errorf("reading EK cert: %v", err)
}
return parseCert(ekCert)
return ParseEKCertificate(ekCert)
}

// EKs returns the endorsement keys burned-in to the platform.
func (t *TPM) EKs() ([]PlatformEK, error) {
var cert *x509.Certificate
var err error
func (t *TPM) EKs() ([]EK, error) {
switch t.version {
case TPMVersion12:
cert, err = readEKCertFromNVRAM12(t.ctx)

cert, err := readEKCertFromNVRAM12(t.ctx)
if err != nil {
return nil, fmt.Errorf("readEKCertFromNVRAM failed: %v", err)
}

return []EK{
{Public: crypto.PublicKey(cert.PublicKey), Certificate: cert},
}, nil
case TPMVersion20:
cert, err = readEKCertFromNVRAM20(t.rwc)
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 {
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 []PlatformEK{
{nil, &rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()}},
}, 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)
}

return []PlatformEK{
{cert, cert.PublicKey},
}, nil
}

// MintAIK creates an attestation key.
Expand Down
Loading

0 comments on commit 9e4e0b0

Please sign in to comment.