1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
package attest
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
"fmt"
"io"
)
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, akpub []byte) (blob []byte, err error) {
akHash := sha1.Sum(akpub)
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(akHash[:])
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(rand io.Reader, pubkey *rsa.PublicKey, akpub, secret []byte) (asymenc []byte, symenc []byte, err error) {
aeskey := make([]byte, 16)
iv := make([]byte, 16)
if _, err = io.ReadFull(rand, aeskey); err != nil {
return nil, nil, err
}
if _, err = io.ReadFull(rand, iv); err != nil {
return nil, nil, err
}
activationBlob, err := makeActivationBlob(aeskey, akpub)
if err != nil {
return nil, nil, err
}
label := []byte{'T', 'C', 'P', 'A'}
asymenc, err = rsa.EncryptOAEP(sha1.New(), rand, 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
}
|