Implement credential activation API (#56)

This commit is contained in:
Tom D 2019-07-23 15:22:53 -07:00 committed by GitHub
parent 2464131d7c
commit 8f4f17e679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 610 additions and 73 deletions

280
attest/activation.go Normal file
View File

@ -0,0 +1,280 @@
package attest
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"
"io"
tpm1 "github.com/google/go-tpm/tpm"
"github.com/google/go-tpm/tpm2"
// TODO(jsonp): Move activation generation code to internal package.
"github.com/google/go-tpm/tpm2/credactivation"
"github.com/google/go-tspi/verification"
)
const (
// minRSABits is the minimum accepted bit size of an RSA key.
minRSABits = 2048
// activationSecretLen is the size in bytes of the generated secret
// which is generated for credential activation.
activationSecretLen = 32
// symBlockSize is the block size used for symmetric ciphers used
// when generating the credential activation challenge.
symBlockSize = 16
// tpm20GeneratedMagic is a magic tag when can only be present on a
// TPM structure if the structure was generated wholly by the TPM.
tpm20GeneratedMagic = 0xff544347
)
func cryptoHash(h tpm2.Algorithm) (crypto.Hash, error) {
switch h {
case tpm2.AlgSHA1:
return crypto.SHA1, nil
case tpm2.AlgSHA256:
return crypto.SHA256, nil
case tpm2.AlgSHA384:
return crypto.SHA384, nil
case tpm2.AlgSHA512:
return crypto.SHA512, nil
default:
return crypto.Hash(0), fmt.Errorf("unsupported signature digest: %v", h)
}
}
// ActivationParameters encapsulates the inputs for activating an AIK.
type ActivationParameters struct {
// TPMVersion holds the version of the TPM, either 1.2 or 2.0.
TPMVersion TPMVersion
// EK, the endorsement key, describes an asymmetric key who's
// private key is permenantly bound to the TPM.
//
// Activation will verify that the provided EK is held on the same
// TPM as the AIK. However, it is the callers responsibility to
// ensure the EK they provide corresponds to the the device which
// they are trying to associate the AIK with.
EK crypto.PublicKey
// AIK, the Attestation Identity Key, describes the properties of
// an asymmetric key (managed by the TPM) which signs attestation
// structures.
// The values from this structure can be obtained by calling
// Parameters() on an attest.AIK.
AIK AttestationParameters
// Rand is a source of randomness to generate a seed and secret for the
// challenge.
//
// If nil, this defaults to crypto.Rand.
Rand io.Reader
}
// checkAIKParameters examines properties of an AIK and a creation
// attestation, to determine if it is suitable for use as an attestation key.
func (p *ActivationParameters) checkAIKParameters() error {
switch p.TPMVersion {
case TPMVersion12:
return p.checkTPM12AIKParameters()
case TPMVersion20:
return p.checkTPM20AIKParameters()
default:
return fmt.Errorf("TPM version %d not supported", p.TPMVersion)
}
}
func (p *ActivationParameters) checkTPM12AIKParameters() error {
// TODO(jsonp): Implement helper to parse public blobs, ie:
// func ParsePublic(publicBlob []byte) (crypto.Public, error)
pub, err := tpm1.UnmarshalPubRSAPublicKey(p.AIK.Public)
if err != nil {
return fmt.Errorf("unmarshalling public key: %v", err)
}
if bits := pub.Size() * 8; bits < minRSABits {
return fmt.Errorf("attestation key too small: must be at least %d bits but was %d bits", minRSABits, bits)
}
return nil
}
func (p *ActivationParameters) checkTPM20AIKParameters() error {
if len(p.AIK.CreateSignature) < 8 {
return fmt.Errorf("signature is too short to be valid: only %d bytes", len(p.AIK.CreateSignature))
}
pub, err := tpm2.DecodePublic(p.AIK.Public)
if err != nil {
return fmt.Errorf("DecodePublic() failed: %v", err)
}
_, err = tpm2.DecodeCreationData(p.AIK.CreateData)
if err != nil {
return fmt.Errorf("DecodeCreationData() failed: %v", err)
}
att, err := tpm2.DecodeAttestationData(p.AIK.CreateAttestation)
if err != nil {
return fmt.Errorf("DecodeAttestationData() failed: %v", err)
}
if att.Type != tpm2.TagAttestCreation {
return fmt.Errorf("attestation does not apply to creation data, got tag %x", att.Type)
}
// TODO: Support ECC AIKs.
switch pub.Type {
case tpm2.AlgRSA:
if pub.RSAParameters.KeyBits < minRSABits {
return fmt.Errorf("attestation key too small: must be at least %d bits but was %d bits", minRSABits, pub.RSAParameters.KeyBits)
}
default:
return fmt.Errorf("public key of alg 0x%x not supported", pub.Type)
}
// Compute & verify that the creation data matches the digest in the
// attestation structure.
nameHashConstructor, err := pub.NameAlg.HashConstructor()
if err != nil {
return fmt.Errorf("HashConstructor() failed: %v", err)
}
h := nameHashConstructor()
h.Write(p.AIK.CreateData)
if !bytes.Equal(att.AttestedCreationInfo.OpaqueDigest, h.Sum(nil)) {
return errors.New("attestation refers to different public key")
}
// Make sure the AIK has sane key parameters (Attestation can be faked if an AIK
// can be used for arbitrary signatures).
// We verify the following:
// - Key is TPM backed.
// - Key is TPM generated.
// - Key is a restricted key (means it cannot do arbitrary signing/decrypt ops).
// - Key cannot be duplicated.
// - Key was generated by a call to TPM_Create*.
if att.Magic != tpm20GeneratedMagic {
return errors.New("creation attestation was not produced by a TPM")
}
if (pub.Attributes & tpm2.FlagFixedTPM) == 0 {
return errors.New("AIK is exportable")
}
if ((pub.Attributes & tpm2.FlagRestricted) == 0) || ((pub.Attributes & tpm2.FlagFixedParent) == 0) || ((pub.Attributes & tpm2.FlagSensitiveDataOrigin) == 0) {
return errors.New("provided key is not limited to attestation")
}
// Verify the attested creation name matches what is computed from
// the public key.
match, err := att.AttestedCreationInfo.Name.MatchesPublic(pub)
if err != nil {
return err
}
if !match {
return errors.New("creation attestation refers to a different key")
}
// Check the signature over the attestation data verifies correctly.
pk := rsa.PublicKey{E: int(pub.RSAParameters.Exponent), N: pub.RSAParameters.Modulus}
signHashConstructor, err := pub.RSAParameters.Sign.Hash.HashConstructor()
if err != nil {
return err
}
hsh := signHashConstructor()
hsh.Write(p.AIK.CreateAttestation)
verifyHash, err := cryptoHash(pub.RSAParameters.Sign.Hash)
if err != nil {
return err
}
if len(p.AIK.CreateSignature) < 8 {
return fmt.Errorf("signature invalid: length of %d is shorter than 8", len(p.AIK.CreateSignature))
}
sig, err := tpm2.DecodeSignature(bytes.NewBuffer(p.AIK.CreateSignature))
if err != nil {
return fmt.Errorf("DecodeSignature() failed: %v", err)
}
if err := rsa.VerifyPKCS1v15(&pk, verifyHash, hsh.Sum(nil), sig.RSA.Signature); err != nil {
return fmt.Errorf("could not verify attestation: %v", err)
}
return nil
}
// Generate returns a credential activation challenge, which can be provided
// to the TPM to verify the AIK parameters given are authentic & the AIK
// is present on the same TPM as the EK.
func (p *ActivationParameters) Generate() (secret []byte, ec *EncryptedCredential, err error) {
if err := p.checkAIKParameters(); err != nil {
return nil, nil, err
}
if p.EK == nil {
return nil, nil, errors.New("no EK provided")
}
rnd, secret := p.Rand, make([]byte, activationSecretLen)
if rnd == nil {
rnd = rand.Reader
}
if _, err = io.ReadFull(rnd, secret); err != nil {
return nil, nil, fmt.Errorf("error generating activation secret: %v", err)
}
switch p.TPMVersion {
case TPMVersion12:
ec, err = p.generateChallengeTPM12(secret)
case TPMVersion20:
ec, err = p.generateChallengeTPM20(secret)
default:
return nil, nil, fmt.Errorf("unrecognised TPM version: %v", p.TPMVersion)
}
if err != nil {
return nil, nil, err
}
return secret, ec, nil
}
func (p *ActivationParameters) generateChallengeTPM20(secret []byte) (*EncryptedCredential, error) {
att, err := tpm2.DecodeAttestationData(p.AIK.CreateAttestation)
if err != nil {
return nil, fmt.Errorf("DecodeAttestationData() failed: %v", err)
}
cred, encSecret, err := credactivation.Generate(att.AttestedCreationInfo.Name.Digest, p.EK, symBlockSize, secret)
if err != nil {
return nil, fmt.Errorf("credactivation.Generate() failed: %v", err)
}
return &EncryptedCredential{
Credential: cred,
Secret: encSecret,
}, nil
}
func (p *ActivationParameters) generateChallengeTPM12(secret []byte) (*EncryptedCredential, error) {
pk, ok := p.EK.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("got EK of type %T, want an RSA key", p.EK)
}
var (
cred, encSecret []byte
err error
)
if p.AIK.UseTCSDActivationFormat {
cred, encSecret, err = verification.GenerateChallengeEx(pk, p.AIK.Public, secret)
} else {
cred, encSecret, err = generateChallenge12(pk, p.AIK.Public, secret)
}
if err != nil {
return nil, fmt.Errorf("challenge generation failed: %v", err)
}
return &EncryptedCredential{
Credential: cred,
Secret: encSecret,
}, nil
}

69
attest/activation_test.go Normal file
View File

@ -0,0 +1,69 @@
package attest
import (
"bytes"
"crypto/rsa"
"encoding/base64"
"math/big"
"math/rand"
"testing"
)
func decodeBase10(base10 string, t *testing.T) *big.Int {
i, ok := new(big.Int).SetString(base10, 10)
if !ok {
t.Fatalf("failed decode of base10: %q", base10)
}
return i
}
func decodeBase64(in string, t *testing.T) []byte {
out, err := base64.StdEncoding.DecodeString(in)
if err != nil {
t.Fatal(err)
}
return out
}
func ekCertSigner(t *testing.T) *rsa.PrivateKey {
return &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: decodeBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557", t),
E: 3,
},
D: decodeBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731", t),
Primes: []*big.Int{
decodeBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433", t),
decodeBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029", t),
},
}
}
func TestActivationTPM20(t *testing.T) {
priv := ekCertSigner(t)
rand := rand.New(rand.NewSource(123456))
// These parameters represent an AIK generated on a real-world, infineon TPM.
params := ActivationParameters{
TPMVersion: TPMVersion20,
AIK: AttestationParameters{
Public: decodeBase64("AAEACwAFBHIAIJ3/y/NsODrmmfuYaNxty4nXFTiEvigDkiwSQVi/rSKuABAAFAAECAAAAAAAAQC/08gj/04z4xGMIVTmr02lzhI5epufXgU831xEpf2qpXfvtNGUfqTcgWF2EUux2HDPqgcj59dtXRobQdlr4uCGNzfZIGAej4JusLa4MjpG6W2DtJPot6F1Mry63talzJ36U47niy9Iesd34CO2p9Xk3+86ZmBnQ6PQ2roUNK3l7bKz6cFLM9drOLwCqU0AUl6pHvzYPPz+xXsPl3iaA2cM97oneUiJNmJM7wtR9OcaKyIA4wVlX5TndB9NwWq5Iuj8q2Sp40Dg0noXXGSPliAtVD8flkXtAcuI9UHkQbzu9cGPRdSJPMn743GONg3bYalFtcgh2VpACXkPbXB32J7B", t),
CreateData: decodeBase64("AAAAAAAg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFUBAAsAIgALWI9hwDRB3zYSkannqM5z0J1coQNA1Jz/oCRxJQwTaNwAIgALmyFYBhHeIU3FUKIAPgXFD3NXyasP3siQviDEyH7avu4AAA==", t),
CreateAttestation: decodeBase64("/1RDR4AaACIAC41+jhmEOue1MZhJjIk79ENar6i15rBvamXLpQnGTBCOAAAAAAAAD3GRNfU4syzJ1jQGATDCDteFC5C4ACIAC3ToMYGy9GXxcf8A0HvOuLOHbU7HPEppM47C7CMcU8TtACBDmJFUFO1f5+BYevaYdd3VtfMCsxIuHhoTZJczzLP2BA==", t),
CreateSignature: decodeBase64("ABQABAEALVzJSnKRJU39gHjETaI89/sM1L6HwBPGNekw6NojSW8bwD5/W1cLRDakCsYKUQu68mmbjs8xaIVBRvVM2YWP10tbTWNB0iJc9b8rERhkk3QIIFm/XsiVZsb0mysTxfeh8zygaAKQ/50sYyzp+raD0Ho0mYIRKJOEdQ6chsBflM3eB8mCXGTugUfrET80q3iu0gncaKWbfxQaQUb9ZTPSJrTN64HQ9tlOfnGT+8++WA3hV0NqKMnoAqiI9GZnI5MPXs6XxEncu/GJLJpAYZakBiS74Jvlr34Pur32B4xjm1M25AUGHEIgb6r49S0sV+hzaKu45858lQRMXj01GcyBhw==", t),
},
EK: &rsa.PublicKey{
E: priv.E,
N: priv.N,
},
Rand: rand,
}
secret, _, err := params.Generate()
if err != nil {
t.Fatalf("Generate() returned err: %v", err)
}
if got, want := secret, decodeBase64("0vhS7HtORX9uf/iyQ8Sf9WkpJuoJ1olCfTjSZuyNNxY=", t); !bytes.Equal(got, want) {
t.Fatalf("secret = %v, want %v", got, want)
}
}

View File

@ -99,7 +99,7 @@ type aik interface {
Marshal() ([]byte, error)
ActivateCredential(tpm *TPM, in EncryptedCredential) ([]byte, error)
Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error)
Parameters() AIKParameters
AttestationParameters() AttestationParameters
Public() crypto.PublicKey
}
@ -134,8 +134,8 @@ func (k *AIK) Quote(tpm *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
// Parameters returns information about the AIK, typically used to generate
// a credential activation challenge.
func (k *AIK) Parameters() AIKParameters {
return k.aik.Parameters()
func (k *AIK) AttestationParameters() AttestationParameters {
return k.aik.AttestationParameters()
}
// Public returns the public part of the AIK.
@ -177,9 +177,9 @@ type PlatformEK struct {
Public crypto.PublicKey
}
// AIKParameters describes information about an AIK. This information
// is typically used to generate an activation challenge.
type AIKParameters struct {
// AttestationParameters describes information about a key which is necessary
// for verifying its properties remotely.
type AttestationParameters struct {
// Public represents the public key in a TPM-version specific encoding.
// For TPM 2.0 devices, this is encoded as a TPMT_PUBLIC structure.
// For TPM 1.2 devices, this is a TPM_PUBKEY structure, as defined in
@ -187,6 +187,11 @@ type AIKParameters struct {
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011.pdf
Public []byte
// UseTCSDActivationFormat is set when tcsd (trousers daemon) is operating
// as an intermediary between this library and the TPM. A value of true
// indicates that activation challenges should use the TCSD-specific format.
UseTCSDActivationFormat bool
// Subsequent fields are only populated for AIKs generated on a TPM
// implementing version 2.0 of the specification. The specific structures
// referenced for each field are defined in the TPM Revision 2, Part 2 -

View File

@ -26,7 +26,6 @@ import (
"github.com/google/go-tpm-tools/simulator"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/credactivation"
)
func setupSimulatedTPM(t *testing.T) (*simulator.Simulator, *TPM) {
@ -133,21 +132,17 @@ func TestSimTPM20ActivateCredential(t *testing.T) {
}
ek := chooseEKPub(t, EKs)
att, err := tpm2.DecodeAttestationData(aik.aik.(*key20).createAttestation)
if err != nil {
t.Fatalf("tpm2.DecodeAttestationData() failed: %v", err)
ap := ActivationParameters{
TPMVersion: TPMVersion20,
AIK: aik.AttestationParameters(),
EK: ek,
}
secret := []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}
id, encSecret, err := credactivation.Generate(att.AttestedCreationInfo.Name.Digest, ek, 16, secret)
secret, challenge, err := ap.Generate()
if err != nil {
t.Fatalf("credactivation.Generate() failed: %v", err)
t.Fatalf("Generate() failed: %v", err)
}
decryptedSecret, err := aik.ActivateCredential(tpm, EncryptedCredential{
Credential: id,
Secret: encSecret,
})
decryptedSecret, err := aik.ActivateCredential(tpm, *challenge)
if err != nil {
t.Errorf("aik.ActivateCredential() failed: %v", err)
}

View File

@ -20,9 +20,6 @@ import (
"flag"
"sort"
"testing"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-tspi/verification"
)
var (
@ -143,27 +140,10 @@ func TestTPMQuote(t *testing.T) {
t.Logf("Quote{version: %v, quote: %x, signature: %x}\n", quote.Version, quote.Quote, quote.Signature)
}
// chooseEKCertRaw selects the EK cert which will be activated against.
func chooseEKCertRaw(t *testing.T, eks []PlatformEK) []byte {
t.Helper()
for _, ek := range eks {
if ek.Cert != nil && ek.Cert.PublicKeyAlgorithm == x509.RSA || ek.Cert.PublicKeyAlgorithm == x509.RSAESOAEP {
return ek.Cert.Raw
}
}
t.Skip("No suitable RSA EK found")
return nil
}
func TestTPMActivateCredential(t *testing.T) {
if !*testTPM12 {
t.SkipNow()
}
var challenge EncryptedCredential
nonce := make([]byte, 20)
rand.Read(nonce)
tpm, err := OpenTPM(tpm12config)
if err != nil {
@ -180,20 +160,25 @@ func TestTPMActivateCredential(t *testing.T) {
if err != nil {
t.Fatalf("failed to read EKs: %v", err)
}
ekcert := chooseEKCertRaw(t, EKs)
ek := chooseEKPub(t, EKs)
challenge.Credential, challenge.Secret, err = verification.GenerateChallenge(ekcert, aik.aik.(*key12).public, nonce)
ap := ActivationParameters{
TPMVersion: TPMVersion12,
AIK: aik.AttestationParameters(),
EK: ek,
}
secret, challenge, err := ap.Generate()
if err != nil {
t.Fatalf("GenerateChallenge failed: %v", err)
t.Fatalf("Generate() failed: %v", err)
}
validation, err := aik.ActivateCredential(tpm, challenge)
validation, err := aik.ActivateCredential(tpm, *challenge)
if err != nil {
t.Fatalf("ActivateCredential failed: %v", err)
}
if !bytes.Equal(validation, nonce) {
t.Errorf("secret mismatch: expected %x, got %x", nonce, validation)
if !bytes.Equal(validation, secret) {
t.Errorf("secret mismatch: expected %x, got %x", secret, validation)
}
t.Logf("validation: %x", validation)

140
attest/challenge.go Normal file
View File

@ -0,0 +1,140 @@
package attest
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
"fmt"
)
const (
ekBlobTag = 0x000c
ekBlobActivateTag = 0x002b
ekTypeActivate = 0x0001
algXOR = 0x0000000a
schemeESNone = 0x0001
)
type symKeyHeader struct {
Alg uint32
Scheme uint16
KeySize uint16
}
type activationBlobHeader struct {
Tag uint16
KeyHeader symKeyHeader
}
func makeEmptyPCRInfo() []byte {
var b bytes.Buffer
binary.Write(&b, binary.BigEndian, uint16(3)) // SIZE_OF_SELECT
b.Write([]byte{0x00, 0x00, 0x00}) // empty bitfield for 3 PCRs
b.Write([]byte{0x01}) // TPM_LOCALITY_SELECTION = TPM_LOC_ZERO
b.Write(bytes.Repeat([]byte{0}, sha1.Size)) // TPM_COMPOSITE_HASH
return b.Bytes()
}
func makeActivationBlob(symKey, aikpub []byte) (blob []byte, err error) {
aikHash := sha1.Sum(aikpub)
var out bytes.Buffer
if err := binary.Write(&out, binary.BigEndian, activationBlobHeader{
Tag: ekBlobActivateTag,
KeyHeader: symKeyHeader{
Alg: algXOR,
Scheme: schemeESNone,
KeySize: uint16(len(symKey)),
},
}); err != nil {
return nil, err
}
out.Write(symKey)
out.Write(aikHash[:])
out.Write(makeEmptyPCRInfo())
return out.Bytes(), nil
}
type ekBlobHeader struct {
Tag uint16
EkType uint16
BlobLen uint32
}
func makeEkBlob(activationBlob []byte) []byte {
var out bytes.Buffer
binary.Write(&out, binary.BigEndian, ekBlobHeader{
Tag: ekBlobTag,
EkType: ekTypeActivate,
BlobLen: uint32(len(activationBlob)),
})
out.Write(activationBlob)
return out.Bytes()
}
func pad(plaintext []byte, bsize int) []byte {
pad := bsize - (len(plaintext) % bsize)
if pad == 0 {
pad = bsize
}
for i := 0; i < pad; i++ {
plaintext = append(plaintext, byte(pad))
}
return plaintext
}
// generateChallenge12 generates a TPM_EK_BLOB challenge for a TPM 1.2 device.
// This process is defined in section 15.1 of the TPM 1.2 commands spec,
// available at: https://trustedcomputinggroup.org/wp-content/uploads/TPM-Main-Part-3-Commands_v1.2_rev116_01032011.pdf
//
// asymenc is a TPM_EK_BLOB structure containing a TPM_EK_BLOB_ACTIVATE structure,
// encrypted with the EK of the TPM. The contained credential is the aes key
// for symenc.
// symenc is a structure with TPM_SYM_MODE_CBC leading, then the IV, and then
// the secret encrypted with the session key credential contained in asymenc.
// To use this, pass asymenc as the input to the TPM_ActivateIdentity command.
// Use the returned credential as the aes key to decode the secret in symenc.
func generateChallenge12(pubkey *rsa.PublicKey, aikpub, secret []byte) (asymenc []byte, symenc []byte, err error) {
aeskey := make([]byte, 16)
iv := make([]byte, 16)
if _, err = rand.Read(aeskey); err != nil {
return nil, nil, err
}
if _, err = rand.Read(iv); err != nil {
return nil, nil, err
}
activationBlob, err := makeActivationBlob(aeskey, aikpub)
if err != nil {
return nil, nil, err
}
label := []byte{'T', 'C', 'P', 'A'}
asymenc, err = rsa.EncryptOAEP(sha1.New(), rand.Reader, pubkey, makeEkBlob(activationBlob), label)
if err != nil {
return nil, nil, fmt.Errorf("EncryptOAEP() failed: %v", err)
}
block, err := aes.NewCipher(aeskey)
if err != nil {
return nil, nil, err
}
cbc := cipher.NewCBCEncrypter(block, iv)
secret = pad(secret, len(iv))
symenc = make([]byte, len(secret))
cbc.CryptBlocks(symenc, secret)
var symOut bytes.Buffer
binary.Write(&symOut, binary.BigEndian, uint32(0x02)) // TPM_SYM_MODE_CBC
symOut.Write(iv)
symOut.Write(symenc)
return asymenc, symOut.Bytes(), nil
}

65
attest/challenge_test.go Normal file
View File

@ -0,0 +1,65 @@
package attest
import (
"bytes"
"crypto/rsa"
"crypto/x509"
"testing"
)
func TestMakeActivationBlob(t *testing.T) {
blob, err := makeActivationBlob([]byte{1, 2, 3, 4, 5}, []byte{5, 6, 7, 8})
if err != nil {
t.Fatal(err)
}
if got, want := blob[0:2], []byte{0, 0x2b}; !bytes.Equal(got, want) {
t.Errorf("tag = %v, want %v", got, want)
}
if got, want := blob[2:6], []byte{0, 0, 0, 0x0a}; !bytes.Equal(got, want) {
t.Errorf("alg = %v, want %v", got, want)
}
if got, want := blob[6:8], []byte{0, 1}; !bytes.Equal(got, want) {
t.Errorf("scheme = %v, want %v", got, want)
}
if got, want := blob[8:10], []byte{0, 5}; !bytes.Equal(got, want) {
t.Errorf("len = %v, want %v", got, want)
}
if got, want := blob[10:15], []byte{1, 2, 3, 4, 5}; !bytes.Equal(got, want) {
t.Errorf("symKey = %v, want %v", got, want)
}
if got, want := blob[15:35], []byte{133, 217, 101, 29, 154, 57, 154, 103, 224, 21, 208, 71, 253, 158, 106, 148, 30, 107, 32, 187}; !bytes.Equal(got, want) {
t.Errorf("aik digest = %v, want %v", got, want)
}
if got, want := blob[35:37], []byte{0, 3}; !bytes.Equal(got, want) {
t.Errorf("size of select = %v, want %v", got, want)
}
if got, want := blob[37:40], []byte{0, 0, 0}; !bytes.Equal(got, want) {
t.Errorf("select bitfield = %v, want %v", got, want)
}
if got, want := blob[40:41], []byte{1}; !bytes.Equal(got, want) {
t.Errorf("locality = %v, want %v", got, want)
}
if got, want := blob[41:61], bytes.Repeat([]byte{0}, 20); !bytes.Equal(got, want) {
t.Errorf("select digest = %v, want %v", got, want)
}
}
func TestGenerateChallengeSymHeader(t *testing.T) {
cert, err := x509.ParseCertificate(decodeBase64("MIID2jCCA4CgAwIBAgIKFsBPsR6KEUuzHjAKBggqhkjOPQQDAjBVMVMwHwYDVQQDExhOdXZvdG9uIFRQTSBSb290IENBIDIxMTAwJQYDVQQKEx5OdXZvdG9uIFRlY2hub2xvZ3kgQ29ycG9yYXRpb24wCQYDVQQGEwJUVzAeFw0xNzEwMTgyMzQ5MjBaFw0zNzEwMTQyMzQ5MjBaMAAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsiqa4C+4hxqQgQ93aFmVq+hvbV6FDvNod24lA1s24pVJzUdOW/D0ORY3TdvRKS1xEh+yPD+iao+XrRGELHSOrGxid/kaTiOF8KdR5BWJwCoedQasqMyQsgZNlU6nKoERcex2G6DDozkdUgrJ/A04qG8tkpEfwmS+0SVWEtDoTb4fzdehKmS32gKcY/I3ZnmpE/5+FCJHKUwIPxHPwAGdhWoYEGsb7ZwG3/S4UuPEfHaab/iwj/WxwbnGtysMu9r1ZkHjQx6FblWVLoXCqrTl0q0samTuW52MffybbOEzn9R0pnfiyQlpL8CLKP4/kBPUGkkvZm2MJsF2cwNRJtEXVAgMBAAGjggHAMIIBvDBKBgNVHREBAf8EQDA+pDwwOjE4MBQGBWeBBQIBEwtpZDo0RTU0NDMwMDAQBgVngQUCAhMHTlBDVDZ4eDAOBgVngQUCAxMFaWQ6MTMwDAYDVR0TAQH/BAIwADAQBgNVHSUECTAHBgVngQUIATAfBgNVHSMEGDAWgBSfu3mqD1JieL7RUJKacXHpajW+9zAOBgNVHQ8BAf8EBAMCBSAwcAYDVR0JBGkwZzAWBgVngQUCEDENMAsMAzIuMAIBAAIBdDBNBgVngQUCEjFEMEICAQABAf+gAwoBAaEDCgEAogMKAQCjFTATFgMzLjEKAQQKAQEBAf+gAwoBAqQPMA0WBTE0MC0yCgECAQEApQMBAQAwQQYDVR0gBDowODA2BgRVHSAAMC4wLAYIKwYBBQUHAgEWIGh0dHA6Ly93d3cubnV2b3Rvbi5jb20vc2VjdXJpdHkvMGgGCCsGAQUFBwEBBFwwWjBYBggrBgEFBQcwAoZMaHR0cDovL3d3dy5udXZvdG9uLmNvbS9zZWN1cml0eS9OVEMtVFBNLUVLLUNlcnQvTnV2b3RvbiBUUE0gUm9vdCBDQSAyMTEwLmNlcjAKBggqhkjOPQQDAgNIADBFAiEAtct+vD/l1Vv9TJOl6oRSI+IZk+k31YIqcscDZEGpZI0CIFFAsVKlFnQnXKTxo7sx9dGOio92Bschl0TQLQhVv0K7", t))
if err != nil {
t.Fatal(err)
}
_, sym, err := generateChallenge12(cert.PublicKey.(*rsa.PublicKey), []byte("pubkey yo"), []byte("secretz"))
if err != nil {
t.Fatal(err)
}
if got, want := len(sym), 36; got != want {
t.Errorf("len(sym) = %v, want %v", got, want)
}
if got, want := sym[0:4], []byte{0, 0, 0, 2}; !bytes.Equal(got, want) {
t.Errorf("symmetric mode = %v, want %v", got, want)
}
}

View File

@ -90,10 +90,11 @@ func (k *key12) Public() crypto.PublicKey {
return k.publicKey
}
// Parameters returns information about the AIK.
func (k *key12) Parameters() AIKParameters {
return AIKParameters{
Public: k.public,
// AttestationParameters returns information about the AIK.
func (k *key12) AttestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
UseTCSDActivationFormat: true,
}
}
@ -187,9 +188,9 @@ func (k *key20) Quote(t *TPM, nonce []byte, alg HashAlg) (*Quote, error) {
return quote20(t.rwc, k.hnd, tpm2.Algorithm(alg), nonce)
}
// Parameters returns information about the AIK.
func (k *key20) Parameters() AIKParameters {
return AIKParameters{
// AttestationParameters returns information about the AIK.
func (k *key20) AttestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,

View File

@ -111,9 +111,9 @@ func (k *key12) Close(tpm *TPM) error {
return closeNCryptObject(k.hnd)
}
// Parameters returns information about the AIK.
func (k *key12) Parameters() AIKParameters {
return AIKParameters{
// AttestationParameters returns information about the AIK.
func (k *key12) AttestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
}
}
@ -202,9 +202,9 @@ func (k *key20) Delete(tpm *TPM) error {
return tpm.pcp.DeleteKey(k.hnd)
}
// Parameters returns information about the AIK.
func (k *key20) Parameters() AIKParameters {
return AIKParameters{
// AttestationParameters returns information about the AIK.
func (k *key20) AttestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
CreateData: k.createData,
CreateAttestation: k.createAttestation,

View File

@ -286,7 +286,7 @@ func (t *TPM) MintAIK(opts *MintOptions) (*AIK, error) {
return nil, fmt.Errorf("failed to get SRK handle: %v", err)
}
_, blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", aikTemplate)
blob, pub, creationData, creationHash, tix, err := tpm2.CreateKey(t.rwc, srk, tpm2.PCRSelection{}, "", "", aikTemplate)
if err != nil {
return nil, fmt.Errorf("CreateKeyEx() failed: %v", err)
}

8
go.mod
View File

@ -5,9 +5,9 @@ go 1.12
require (
github.com/golang/protobuf v1.3.1
github.com/google/certificate-transparency-go v1.0.22-0.20190605205155-41fc2ef3a2a8
github.com/google/go-tpm v0.1.2-0.20190430183152-dcb1ada1f875
github.com/google/go-tpm v0.1.2-0.20190720204220-b46f7071bbfd
github.com/google/go-tpm-tools v0.0.0-20190328013357-5d2fd7f4b3e5
github.com/google/go-tspi v0.2.0
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd // indirect
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
)

19
go.sum
View File

@ -2,25 +2,22 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.0.22-0.20190403155334-84853901c6b8 h1:pZtGL2P6rU7wOnemTcvTgoH9s+QB646LB5dBcZ1w5yE=
github.com/google/certificate-transparency-go v1.0.22-0.20190403155334-84853901c6b8/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.0.22-0.20190605205155-41fc2ef3a2a8 h1:G3Wse9lGL7PmAl2jqdr0HgwhPkGA5KHu7guIPREa7DU=
github.com/google/certificate-transparency-go v1.0.22-0.20190605205155-41fc2ef3a2a8/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/go-tpm v0.1.2-0.20190430183152-dcb1ada1f875 h1:4+5g5+b2aKnnAXX1XiDcbkU/+daEu8T1HOGbRciBu08=
github.com/google/go-tpm v0.1.2-0.20190430183152-dcb1ada1f875/go.mod h1:70+xJCEPKoR1UFyG62ftF/qOTka+OVFVQpNcWmByY0g=
github.com/google/go-tpm v0.1.2-0.20190720204220-b46f7071bbfd h1:vdJl7SmJKhMKpc7XTDMjYCq/hvZT2u1YzABQD1VOqeA=
github.com/google/go-tpm v0.1.2-0.20190720204220-b46f7071bbfd/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm-tools v0.0.0-20190328013357-5d2fd7f4b3e5 h1:/moKuMi+BJ+OEva3jTms88ruyRkxaZn+f9EIZoGpQeY=
github.com/google/go-tpm-tools v0.0.0-20190328013357-5d2fd7f4b3e5/go.mod h1:ApmLTU8fd5JJJ4J67y9sV16nOTR00GW2OabMwk7kSnE=
github.com/google/go-tspi v0.2.0 h1:PMrHThARFgHtsCF6B8YNjLlnnGMDdFjVHZnxaqkcbzQ=
github.com/google/go-tspi v0.2.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad h1:LnpS22S8V1HqbxjveESGAazHhi6BX9SwI2Rij7qZcXQ=
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd h1:sMHc2rZHuzQmrbVoSpt9HgerkXPyIeCSO6k0zUMGfFk=
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=