Skip to content

Commit 59f5c5a

Browse files
committed
wip: include PR's feedbacks
1 parent 2bd232d commit 59f5c5a

File tree

4 files changed

+684
-95
lines changed

4 files changed

+684
-95
lines changed

tpm2/marshalling.go

Lines changed: 121 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tpm2
22

33
import (
44
"bytes"
5+
"encoding/binary"
56
"fmt"
67
"reflect"
78
)
@@ -22,6 +23,45 @@ type marshallableWithHint interface {
2223
get(hint int64) (reflect.Value, error)
2324
}
2425

26+
// CommandPreimage represents a structured preimage of cpHash for a TPM command.
27+
// This structure is marshaled to bytes using [Marshal] for storage/transmission
28+
// and can be converted to the raw cpHash preimage format for hashing.
29+
//
30+
// Format when marshaled:
31+
// - CommandCode: 4 bytes (TPMCC)
32+
// - Names: sized list of TPM2BName
33+
// - Parameters: sized buffer
34+
//
35+
// See definition in Part 1: Architecture, section 16.7.
36+
type CommandPreimage struct {
37+
marshalByReflection
38+
// CommandCode is the TPM command code
39+
CommandCode TPMCC
40+
// Names are the names of the handles referenced by the command
41+
Names []TPM2BName `gotpm:"list"`
42+
// Parameters are the marshaled command parameters
43+
Parameters TPM2BData
44+
}
45+
46+
// ToCPHashPreimage converts the CommandPreimage to the raw buffer format
47+
// used to compute cpHash according to TPM 2.0 spec.
48+
func (cp *CommandPreimage) ToCPHashPreimage() []byte {
49+
var buf bytes.Buffer
50+
51+
// Write command code (4 bytes, big endian)
52+
binary.Write(&buf, binary.BigEndian, cp.CommandCode)
53+
54+
// Write names (raw buffers without size prefix)
55+
for _, name := range cp.Names {
56+
buf.Write(name.Buffer)
57+
}
58+
59+
// Write parameters
60+
buf.Write(cp.Parameters.Buffer)
61+
62+
return buf.Bytes()
63+
}
64+
2565
// Unmarshallable represents any TPM type that can be marshalled or unmarshalled.
2666
type Unmarshallable interface {
2767
Marshallable
@@ -127,84 +167,118 @@ func (b *boxed[T]) unmarshal(buf *bytes.Buffer) error {
127167
return unmarshal(buf, reflect.ValueOf(b.Contents))
128168
}
129169

130-
// MarshalCommandResponse marshals both command and response.
131-
func MarshalCommandResponse[C Command[R, *R], R any](cmd C, rsp *R) (cmdData []byte, rspData []byte, err error) {
132-
cmdData, err = MarshalCommand(cmd)
170+
// toCommandPreimage convert a Command to a CommandPreimage structure from a command.
171+
func toCommandPreimage[C Command[R, *R], R any](cmd C) (*CommandPreimage, error) {
172+
cc := cmd.Command()
173+
174+
names, err := cmdNames(cmd)
133175
if err != nil {
134-
return nil, nil, fmt.Errorf("marshalling command: %w", err)
176+
return nil, err
135177
}
136-
rspData, err = MarshalResponse(rsp)
178+
179+
params, err := cmdParameters(cmd, nil)
137180
if err != nil {
138-
return nil, nil, fmt.Errorf("marshalling response: %w", err)
181+
return nil, err
139182
}
140-
return cmdData, rspData, nil
183+
184+
return &CommandPreimage{
185+
CommandCode: cc,
186+
Names: names,
187+
Parameters: TPM2BData{
188+
Buffer: params,
189+
},
190+
}, nil
141191
}
142192

143-
// UnmarshalCommandResponse unmarshals both command and response.
144-
func UnmarshalCommandResponse[C Command[R, *R], R any](cmdData []byte, rspData []byte) (cmd C, rsp *R, err error) {
145-
cmd, err = UnmarshalCommand[C, R](cmdData)
146-
if err != nil {
147-
return cmd, rsp, fmt.Errorf("unmarshalling command: %w", err)
148-
}
149-
rsp, err = UnmarshalResponse[R](rspData)
193+
// MarshalCommand marshals a TPM command into a serialized CommandPreimage.
194+
// The returned bytes contain a marshaled CommandPreimage structure that includes:
195+
// - CommandCode (4 bytes)
196+
// - Names (sized list)
197+
// - Parameters (sized buffer)
198+
//
199+
// This can be stored, transmitted, or later unmarshaled.
200+
//
201+
// To compute cpHash use [CommandPreimage.ToCPHashPreimage].
202+
func MarshalCommand[C Command[R, *R], R any](cmd C) ([]byte, error) {
203+
preimage, err := toCommandPreimage(cmd)
150204
if err != nil {
151-
return cmd, rsp, fmt.Errorf("unmarshalling response: %w", err)
205+
return nil, err
152206
}
153-
return cmd, rsp, nil
207+
return Marshal(preimage), nil
154208
}
155209

156-
// MarshalCommand marshals a TPM command.
157-
func MarshalCommand[C Command[R, *R], R any](cmd C) ([]byte, error) {
158-
var buf bytes.Buffer
159-
params := taggedMembers(reflect.ValueOf(cmd), "handle", true)
160-
for i := range len(params) {
161-
if err := marshalParameter(&buf, cmd, i); err != nil {
162-
return nil, fmt.Errorf("marshalling command's parameter: %w", err)
163-
}
210+
// unmarshalCommandPreimage unmarshals serialized data into CommandPreimage components.
211+
// Returns the command code, names, and parameters.
212+
func unmarshalCommandPreimage(data []byte) (TPMCC, []TPM2BName, []byte, error) {
213+
if data == nil {
214+
return 0, nil, nil, fmt.Errorf("data cannot be nil")
164215
}
165-
return buf.Bytes(), nil
216+
217+
preimage, err := Unmarshal[CommandPreimage](data)
218+
if err != nil {
219+
return 0, nil, nil, fmt.Errorf("unmarshalling CommandPreimage: %w", err)
220+
}
221+
222+
return preimage.CommandCode, preimage.Names, preimage.Parameters.Buffer, nil
166223
}
167224

168-
// UnmarshalCommand unmarshals a TPM command.
225+
// UnmarshalCommand unmarshals a serialized [CommandPreimage] back into a TPM command.
226+
// The data should be the output from [MarshalCommand].
227+
//
228+
// Note: command produced from this function is not meant to be executed directly on a TPM,
229+
// instead it is expected to be used for purposes such as auditing or inspection.
169230
func UnmarshalCommand[C Command[R, *R], R any](data []byte) (C, error) {
170231
var cmd C
171-
if data == nil {
172-
return cmd, fmt.Errorf("data cannot be nil")
232+
233+
cc, names, params, err := unmarshalCommandPreimage(data)
234+
if err != nil {
235+
return cmd, err
173236
}
174-
buf := bytes.NewBuffer(data)
175-
params := taggedMembers(reflect.ValueOf(cmd), "handle", true)
176-
for i := range len(params) {
177-
if err := unmarshalParameter(buf, &cmd, i); err != nil {
178-
return cmd, fmt.Errorf("unmarshalling command's parameter: %w", err)
237+
238+
if cc != cmd.Command() {
239+
return cmd, fmt.Errorf("command code mismatch: expected %v, got %v", cmd.Command(), cc)
240+
}
241+
242+
{
243+
n, err := cmdNames(cmd)
244+
if err != nil {
245+
return cmd, err
179246
}
247+
248+
expectedNames := len(names)
249+
if len(n) != expectedNames {
250+
return cmd, fmt.Errorf("name count mismatch: command expects %d names, got %d", expectedNames, len(n))
251+
}
252+
}
253+
254+
// Populate the command's handle fields from the names
255+
if err := populateHandlesFromNames(&cmd, names); err != nil {
256+
return cmd, fmt.Errorf("populating handles: %w", err)
257+
}
258+
259+
// Now unmarshal the parameters using the helper
260+
buf := bytes.NewBuffer(params)
261+
if err := unmarshalCmdParameters(buf, &cmd, nil); err != nil {
262+
return cmd, err
180263
}
181264
return cmd, nil
182265
}
183266

184267
// MarshalResponse marshals a TPM response.
185268
func MarshalResponse[R any](rsp *R) ([]byte, error) {
186-
var buf bytes.Buffer
187-
parameters := taggedMembers(reflect.ValueOf(rsp).Elem(), "handle", true)
188-
for i, parameter := range parameters {
189-
if err := marshal(&buf, parameter); err != nil {
190-
return nil, fmt.Errorf("marshalling response parameter %d: %w", i, err)
191-
}
192-
}
193-
return buf.Bytes(), nil
269+
return marshalRspParameters(rsp, nil)
194270
}
195271

196272
// UnmarshalResponse unmarshals a TPM response.
273+
//
274+
// Note: the result from this function is expected to be used for purposes such as auditing or inspection.
197275
func UnmarshalResponse[R any](data []byte) (*R, error) {
198276
var rsp R
199277
if data == nil {
200278
return nil, fmt.Errorf("data cannot be nil")
201279
}
202-
buf := bytes.NewBuffer(data)
203-
parameters := taggedMembers(reflect.ValueOf(&rsp).Elem(), "handle", true)
204-
for i, parameter := range parameters {
205-
if err := unmarshal(buf, parameter); err != nil {
206-
return nil, fmt.Errorf("unmarshalling response parameter %d: %w", i, err)
207-
}
280+
if err := rspParameters(data, nil, &rsp); err != nil {
281+
return nil, err
208282
}
209283
return &rsp, nil
210284
}

0 commit comments

Comments
 (0)