Skip to content

Commit db09d9b

Browse files
committed
tmputil: introduce non-global encoding logic
Right now it's not possible to use the tpm and tpm2 packages simultaneously. Refactor the encoding logic into separate structs, so using one doesn't impact the other, while maintaining the top level API. As a follow up, refactor the tpm and tpm2 packages to use the encoders. For example instead of: tpmutil.Pack(digest) Use: tpmutil.TPM2.Pack(digest)
1 parent 60fe40e commit db09d9b

File tree

2 files changed

+81
-34
lines changed

2 files changed

+81
-34
lines changed

tpmutil/encoding.go

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,49 @@ import (
2323
"reflect"
2424
)
2525

26-
// lengthPrefixSize is the size in bytes of length prefix for byte slices.
27-
//
28-
// In TPM 1.2 this is 4 bytes.
29-
// In TPM 2.0 this is 2 bytes.
30-
var lengthPrefixSize int
26+
// Encoding implements encoding logic for different versions of the TPM
27+
// specification.
28+
type Encoding struct {
29+
// lengthPrefixSize is the size in bytes of length prefix for byte slices.
30+
//
31+
// In TPM 1.2 this is 4 bytes.
32+
// In TPM 2.0 this is 2 bytes.
33+
lengthPrefixSize int
34+
}
35+
36+
var (
37+
// TPM implements TPM 1.2 encoding.
38+
TPM = &Encoding{
39+
lengthPrefixSize: tpm12PrefixSize,
40+
}
41+
// TPM implements TPM 2.0 encoding.
42+
TPM2 = &Encoding{
43+
lengthPrefixSize: tpm20PrefixSize,
44+
}
45+
46+
defaultEncoding *Encoding
47+
)
3148

3249
const (
3350
tpm12PrefixSize = 4
3451
tpm20PrefixSize = 2
3552
)
3653

37-
// UseTPM12LengthPrefixSize makes Pack/Unpack use TPM 1.2 encoding for byte
38-
// arrays.
54+
// UseTPM12LengthPrefixSize makes the package level Pack/Unpack functions use
55+
// TPM 1.2 encoding for byte arrays.
3956
func UseTPM12LengthPrefixSize() {
40-
lengthPrefixSize = tpm12PrefixSize
57+
defaultEncoding = TPM
4158
}
4259

43-
// UseTPM20LengthPrefixSize makes Pack/Unpack use TPM 2.0 encoding for byte
44-
// arrays.
60+
// UseTPM20LengthPrefixSize makes the package level Pack/Unpack functions use
61+
// TPM 2.0 encoding for byte arrays.
4562
func UseTPM20LengthPrefixSize() {
46-
lengthPrefixSize = tpm20PrefixSize
63+
defaultEncoding = TPM2
4764
}
4865

4966
// packedSize computes the size of a sequence of types that can be passed to
5067
// binary.Read or binary.Write.
51-
func packedSize(elts ...interface{}) (int, error) {
68+
func (enc *Encoding) packedSize(elts ...interface{}) (int, error) {
5269
var size int
5370
for _, e := range elts {
5471
marshaler, ok := e.(SelfMarshaler)
@@ -59,15 +76,15 @@ func packedSize(elts ...interface{}) (int, error) {
5976
v := reflect.ValueOf(e)
6077
switch v.Kind() {
6178
case reflect.Ptr:
62-
s, err := packedSize(reflect.Indirect(v).Interface())
79+
s, err := enc.packedSize(reflect.Indirect(v).Interface())
6380
if err != nil {
6481
return 0, err
6582
}
6683

6784
size += s
6885
case reflect.Struct:
6986
for i := 0; i < v.NumField(); i++ {
70-
s, err := packedSize(v.Field(i).Interface())
87+
s, err := enc.packedSize(v.Field(i).Interface())
7188
if err != nil {
7289
return 0, err
7390
}
@@ -77,7 +94,7 @@ func packedSize(elts ...interface{}) (int, error) {
7794
case reflect.Slice:
7895
switch s := e.(type) {
7996
case []byte:
80-
size += lengthPrefixSize + len(s)
97+
size += enc.lengthPrefixSize + len(s)
8198
case RawBytes:
8299
size += len(s)
83100
default:
@@ -100,9 +117,9 @@ func packedSize(elts ...interface{}) (int, error) {
100117
// fixed length or slices of fixed-length types and packs them into a single
101118
// byte array using binary.Write. It updates the CommandHeader to have the right
102119
// length.
103-
func packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) {
120+
func (enc *Encoding) packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) {
104121
hdrSize := binary.Size(ch)
105-
bodySize, err := packedSize(cmd...)
122+
bodySize, err := enc.packedSize(cmd...)
106123
if err != nil {
107124
return nil, fmt.Errorf("couldn't compute packed size for message body: %v", err)
108125
}
@@ -112,20 +129,27 @@ func packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) {
112129
return Pack(in...)
113130
}
114131

132+
// Pack encodes a set of elements using the package's default encoding.
133+
//
134+
// Callers must call UseTPM12LengthPrefixSize() or UseTPM20LengthPrefixSize()
135+
// before calling this method.
136+
func Pack(elts ...interface{}) ([]byte, error) {
137+
if defaultEncoding == nil {
138+
return nil, errors.New("default encoding not initialized")
139+
}
140+
return defaultEncoding.Pack(elts...)
141+
}
142+
115143
// Pack encodes a set of elements into a single byte array, using
116144
// encoding/binary. This means that all the elements must be encodeable
117145
// according to the rules of encoding/binary.
118146
//
119147
// It has one difference from encoding/binary: it encodes byte slices with a
120148
// prepended length, to match how the TPM encodes variable-length arrays. If
121149
// you wish to add a byte slice without length prefix, use RawBytes.
122-
func Pack(elts ...interface{}) ([]byte, error) {
123-
if lengthPrefixSize == 0 {
124-
return nil, errors.New("lengthPrefixSize must be initialized")
125-
}
126-
150+
func (enc *Encoding) Pack(elts ...interface{}) ([]byte, error) {
127151
buf := new(bytes.Buffer)
128-
if err := packType(buf, elts...); err != nil {
152+
if err := enc.packType(buf, elts...); err != nil {
129153
return nil, err
130154
}
131155

@@ -137,7 +161,7 @@ func Pack(elts ...interface{}) ([]byte, error) {
137161
// lengthPrefixSize size followed by the bytes. The function unpackType
138162
// performs the inverse operation of unpacking slices stored in this manner and
139163
// using encoding/binary for everything else.
140-
func packType(buf io.Writer, elts ...interface{}) error {
164+
func (enc *Encoding) packType(buf io.Writer, elts ...interface{}) error {
141165
for _, e := range elts {
142166
marshaler, ok := e.(SelfMarshaler)
143167
if ok {
@@ -149,20 +173,20 @@ func packType(buf io.Writer, elts ...interface{}) error {
149173
v := reflect.ValueOf(e)
150174
switch v.Kind() {
151175
case reflect.Ptr:
152-
if err := packType(buf, reflect.Indirect(v).Interface()); err != nil {
176+
if err := enc.packType(buf, reflect.Indirect(v).Interface()); err != nil {
153177
return err
154178
}
155179
case reflect.Struct:
156180
// TODO(awly): Currently packType cannot handle non-struct fields that implement SelfMarshaler
157181
for i := 0; i < v.NumField(); i++ {
158-
if err := packType(buf, v.Field(i).Interface()); err != nil {
182+
if err := enc.packType(buf, v.Field(i).Interface()); err != nil {
159183
return err
160184
}
161185
}
162186
case reflect.Slice:
163187
switch s := e.(type) {
164188
case []byte:
165-
switch lengthPrefixSize {
189+
switch enc.lengthPrefixSize {
166190
case tpm20PrefixSize:
167191
if err := binary.Write(buf, binary.BigEndian, uint16(len(s))); err != nil {
168192
return err
@@ -172,7 +196,7 @@ func packType(buf io.Writer, elts ...interface{}) error {
172196
return err
173197
}
174198
default:
175-
return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", lengthPrefixSize)
199+
return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", enc.lengthPrefixSize)
176200
}
177201
if err := binary.Write(buf, binary.BigEndian, s); err != nil {
178202
return err
@@ -204,12 +228,24 @@ func Unpack(b []byte, elts ...interface{}) (int, error) {
204228
return read, err
205229
}
206230

231+
// UnpackBuf recursively unpacks types from a reader using the package's default
232+
// encoder.
233+
//
234+
// Callers must call UseTPM12LengthPrefixSize() or UseTPM20LengthPrefixSize()
235+
// before calling this method.
236+
func UnpackBuf(buf io.Reader, elts ...interface{}) error {
237+
if defaultEncoding == nil {
238+
return errors.New("default encoding not initialized")
239+
}
240+
return defaultEncoding.UnpackBuf(buf, elts...)
241+
}
242+
207243
// UnpackBuf recursively unpacks types from a reader just as encoding/binary
208244
// does under binary.BigEndian, but with one difference: it unpacks a byte
209245
// slice by first reading an integer with lengthPrefixSize bytes, then reading
210246
// that many bytes. It assumes that incoming values are pointers to values so
211247
// that, e.g., underlying slices can be resized as needed.
212-
func UnpackBuf(buf io.Reader, elts ...interface{}) error {
248+
func (enc *Encoding) UnpackBuf(buf io.Reader, elts ...interface{}) error {
213249
for _, e := range elts {
214250
v := reflect.ValueOf(e)
215251
k := v.Kind()
@@ -250,21 +286,21 @@ func UnpackBuf(buf io.Reader, elts ...interface{}) error {
250286
}
251287
size = int(tmpSize)
252288
// TPM 2.0
253-
case lengthPrefixSize == tpm20PrefixSize:
289+
case enc.lengthPrefixSize == tpm20PrefixSize:
254290
var tmpSize uint16
255291
if err := binary.Read(buf, binary.BigEndian, &tmpSize); err != nil {
256292
return err
257293
}
258294
size = int(tmpSize)
259295
// TPM 1.2
260-
case lengthPrefixSize == tpm12PrefixSize:
296+
case enc.lengthPrefixSize == tpm12PrefixSize:
261297
var tmpSize uint32
262298
if err := binary.Read(buf, binary.BigEndian, &tmpSize); err != nil {
263299
return err
264300
}
265301
size = int(tmpSize)
266302
default:
267-
return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", lengthPrefixSize)
303+
return fmt.Errorf("lengthPrefixSize is %d, must be either 2 or 4", enc.lengthPrefixSize)
268304
}
269305

270306
// A zero size is used by the TPM to signal that certain elements

tpmutil/run.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,27 @@ import (
3131
// returning a header and a body in separate responses.
3232
const maxTPMResponse = 4096
3333

34+
// RunCommand executes cmd with the package's default encoding.
35+
//
36+
// Callers must call UseTPM12LengthPrefixSize() or UseTPM20LengthPrefixSize()
37+
// before calling this method.
38+
func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) {
39+
if defaultEncoding == nil {
40+
return nil, 0, errors.New("default encoding not initialized")
41+
}
42+
return defaultEncoding.RunCommand(rw, tag, cmd, in...)
43+
}
44+
3445
// RunCommand executes cmd with given tag and arguments. Returns TPM response
3546
// body (without response header) and response code from the header. Returned
3647
// error may be nil if response code is not RCSuccess; caller should check
3748
// both.
38-
func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) {
49+
func (enc *Encoding) RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) {
3950
if rw == nil {
4051
return nil, 0, errors.New("nil TPM handle")
4152
}
4253
ch := commandHeader{tag, 0, cmd}
43-
inb, err := packWithHeader(ch, in...)
54+
inb, err := enc.packWithHeader(ch, in...)
4455
if err != nil {
4556
return nil, 0, err
4657
}

0 commit comments

Comments
 (0)