@@ -2,6 +2,7 @@ package tpm2
22
33import (
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.
2666type 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.
169230func 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.
185268func 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.
197275func 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