File: challenge.go

package info (click to toggle)
golang-github-google-go-attestation 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,260 kB
  • sloc: sh: 158; makefile: 22
file content (140 lines) | stat: -rw-r--r-- 3,648 bytes parent folder | download
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
}