github.com/linuxboot/fiano@v1.2.0/pkg/intel/metadata/fit/ent_startup_ac_module_entry.go (about)

     1  // Copyright 2017-2021 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fit
     6  
     7  import (
     8  	"crypto/rsa"
     9  	"encoding/binary"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"math/big"
    14  	"reflect"
    15  
    16  	"github.com/linuxboot/fiano/pkg/intel/metadata/fit/check"
    17  	"github.com/xaionaro-go/bytesextra"
    18  )
    19  
    20  // EntrySACM represents a FIT entry of type "Startup AC Module Entry" (0x02)
    21  type EntrySACM struct{ EntryBase }
    22  
    23  var _ EntryCustomGetDataSegmentSizer = (*EntrySACM)(nil)
    24  
    25  func (entry *EntrySACM) CustomGetDataSegmentSize(firmware io.ReadSeeker) (uint64, error) {
    26  	offset, err := entry.Headers.getDataSegmentOffset(firmware)
    27  	if err != nil {
    28  		return 0, fmt.Errorf("unable to detect data segment offset: %w", err)
    29  	}
    30  
    31  	// See point "7" of "2.7" of the specification: the size field is
    32  	// always zero. So we parsing the size from it's data right now:
    33  	var size uint32
    34  	size, err = EntrySACMParseSizeFrom(firmware, offset)
    35  	if err != nil {
    36  		return 0, fmt.Errorf("unable to detect data segment size: %w", err)
    37  	}
    38  	return uint64(size), nil
    39  }
    40  
    41  var _ EntryCustomRecalculateHeaderser = (*EntrySACM)(nil)
    42  
    43  func (entry *EntrySACM) CustomRecalculateHeaders() error {
    44  	// See 4.4.7 of the FIT specification.
    45  	entry.Headers.Size.SetUint32(0)
    46  	return nil
    47  }
    48  
    49  // See the section "A.1" of the specification
    50  // "Intel ® Trusted Execution Technology (Intel ® TXT)"
    51  // https://www.intel.com/content/www/us/en/software-developers/txt-software-development-guide.html
    52  
    53  // EntrySACMDataInterface is the interface of a startup AC module
    54  // data (of any version)
    55  type EntrySACMDataInterface interface {
    56  	io.ReadWriter
    57  	io.ReaderFrom
    58  	io.WriterTo
    59  
    60  	// Field getters:
    61  
    62  	GetModuleType() ACModuleType
    63  	GetModuleSubType() ACModuleSubType
    64  	GetHeaderLen() SizeM4
    65  	GetHeaderVersion() ACModuleHeaderVersion
    66  	GetChipsetID() ACChipsetID
    67  	GetFlags() ACFlags
    68  	GetModuleVendor() ACModuleVendor
    69  	GetDate() BCDDate
    70  	GetSize() SizeM4
    71  	GetTXTSVN() TXTSVN
    72  	GetSESVN() SESVN
    73  	GetCodeControl() CodeControl
    74  	GetErrorEntryPoint() ErrorEntryPoint
    75  	GetGDTLimit() GDTLimit
    76  	GetGDTBasePtr() GDTBasePtr
    77  	GetSegSel() SegSel
    78  	GetEntryPoint() EntryPoint
    79  	GetReserved2() [64]byte
    80  	GetKeySize() SizeM4
    81  	GetScratchSize() SizeM4
    82  	GetRSAPubKey() rsa.PublicKey
    83  	GetRSAPubExp() uint32
    84  	GetRSASig() []byte
    85  	GetScratch() []byte
    86  
    87  	// Auxiliary methods:
    88  	RSASigBinaryOffset() uint64
    89  
    90  	// DateBinaryOffset returns the offset of the field 'Date'
    91  	// relatively to the beginning of the structure in the binary
    92  	// format (see 'encoding/binary').
    93  	DateBinaryOffset() uint
    94  }
    95  
    96  // ACModuleType defines the type of AC module
    97  type ACModuleType uint16
    98  
    99  // ACModuleSubType defines the subtype of AC module (0 - TXT ACM; 1 - S-ACM)
   100  type ACModuleSubType uint16
   101  
   102  // ACModuleHeaderVersion defines module format version:
   103  // * 0.0 – for SINIT ACM before 2017
   104  // * 3.0 – for SINIT ACM of converge of BtG and TXT
   105  type ACModuleHeaderVersion uint32
   106  
   107  const (
   108  	// ACHeaderVersion0 is version "0.0 – for SINIT ACM before 2017"
   109  	ACHeaderVersion0 = ACModuleHeaderVersion(0x00000000)
   110  
   111  	// ACHeaderVersion3 is version "3.0 – for SINIT ACM of converge of BtG and TXT"
   112  	ACHeaderVersion3 = ACModuleHeaderVersion(0x00030000)
   113  )
   114  
   115  func (ver ACModuleHeaderVersion) GoString() string {
   116  	return fmt.Sprintf("0x%08X", ver)
   117  }
   118  
   119  // ACChipsetID defines the module release identifier
   120  type ACChipsetID uint16
   121  
   122  // ACFlags defines the module-specific flags
   123  type ACFlags uint16
   124  
   125  // ACModuleVendor defines the module vendor identifier
   126  type ACModuleVendor uint32
   127  
   128  // BCDDate is a date in format ("year.month.day")
   129  type BCDDate uint32
   130  
   131  // SizeM4 is a size in multiples of four bytes
   132  type SizeM4 uint32
   133  
   134  // Size return the size in bytes
   135  func (size SizeM4) Size() uint64      { return uint64(size) << 2 }
   136  func (size SizeM4) String() string    { return fmt.Sprintf("%d*4", uint32(size)) }
   137  func (size *SizeM4) SetSize(v uint64) { *size = SizeM4(v >> 2) }
   138  
   139  // TXTSVN is the TXT Security Version Number
   140  type TXTSVN uint16
   141  
   142  // SESVN is the Software Guard Extensions (Secure Enclaves) Security Version Number
   143  type SESVN uint16
   144  
   145  // CodeControl is the authenticated code control flags
   146  type CodeControl uint32
   147  
   148  // ErrorEntryPoint is the error response entry point offset (bytes)
   149  type ErrorEntryPoint uint32
   150  
   151  // Pointer returns the value of ErrorEntryPoint as a pointer which
   152  // could be used for pointer arithmetics.
   153  func (ptr ErrorEntryPoint) Pointer() uint64 { return uint64(ptr) }
   154  
   155  // GDTLimit is the GDT limit (defines last byte of GDT)
   156  type GDTLimit uint32
   157  
   158  // GDTBasePtr is the GDT base pointer offset (bytes)
   159  type GDTBasePtr uint32
   160  
   161  // Offset returns the GDTBasePtr value as a pointer which
   162  // could be used for pointer arithmetics.
   163  func (ptr GDTBasePtr) Offset() uint64 { return uint64(ptr) }
   164  
   165  // SegSel is the segment selector initializer
   166  type SegSel uint32
   167  
   168  // EntryPoint is the authenticated code entry point offset (bytes)
   169  type EntryPoint uint32
   170  
   171  // EntrySACMDataCommon is the common part from the beginning of a startup AC module
   172  // entry of any version.
   173  type EntrySACMDataCommon struct {
   174  	ModuleType      ACModuleType
   175  	ModuleSubType   ACModuleSubType
   176  	HeaderLen       SizeM4
   177  	HeaderVersion   ACModuleHeaderVersion
   178  	ChipsetID       ACChipsetID
   179  	Flags           ACFlags
   180  	ModuleVendor    ACModuleVendor
   181  	Date            BCDDate
   182  	Size            SizeM4
   183  	TXTSVN          TXTSVN
   184  	SESVN           SESVN
   185  	CodeControl     CodeControl
   186  	ErrorEntryPoint ErrorEntryPoint
   187  	GDTLimit        GDTLimit
   188  	GDTBasePtr      GDTBasePtr
   189  	SegSel          SegSel
   190  	EntryPoint      EntryPoint
   191  	Reserved2       [64]byte
   192  	KeySize         SizeM4
   193  	ScratchSize     SizeM4
   194  }
   195  
   196  var entrySACMDataCommonSize = uint(binary.Size(EntrySACMDataCommon{}))
   197  
   198  // Read parses the ACM common headers
   199  func (entryData *EntrySACMDataCommon) Read(b []byte) (int, error) {
   200  	n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b))
   201  	return int(n), err
   202  }
   203  
   204  // ReadFrom parses the ACM common headers
   205  func (entryData *EntrySACMDataCommon) ReadFrom(r io.Reader) (int64, error) {
   206  	err := binary.Read(r, binary.LittleEndian, entryData)
   207  	if err != nil {
   208  		return -1, err
   209  	}
   210  	return int64(entrySACMDataCommonSize), nil
   211  }
   212  
   213  // Write compiles the SACM common headers into a binary representation
   214  func (entryData *EntrySACMDataCommon) Write(b []byte) (int, error) {
   215  	n, err := entryData.WriteTo(bytesextra.NewReadWriteSeeker(b))
   216  	return int(n), err
   217  }
   218  
   219  // WriteTo compiles the SACM common headers into a binary representation
   220  func (entryData *EntrySACMDataCommon) WriteTo(w io.Writer) (int64, error) {
   221  	err := binary.Write(w, binary.LittleEndian, entryData)
   222  	if err != nil {
   223  		return -1, err
   224  	}
   225  	return int64(entrySACMDataCommonSize), nil
   226  }
   227  
   228  // GetModuleType returns the type of AC module
   229  func (entryData *EntrySACMDataCommon) GetModuleType() ACModuleType { return entryData.ModuleType }
   230  
   231  // GetModuleSubType returns the subtype of AC module (0 - TXT ACM; 1 - S-ACM)
   232  func (entryData *EntrySACMDataCommon) GetModuleSubType() ACModuleSubType {
   233  	return entryData.ModuleSubType
   234  }
   235  
   236  // GetHeaderLen returns HeaderLen field value
   237  func (entryData *EntrySACMDataCommon) GetHeaderLen() SizeM4 { return entryData.HeaderLen }
   238  
   239  // GetHeaderVersion returns module format version:
   240  // * 0.0 – for SINIT ACM before 2017
   241  // * 3.0 – for SINIT ACM of converge of BtG and TXT
   242  func (entryData *EntrySACMDataCommon) GetHeaderVersion() ACModuleHeaderVersion {
   243  	return entryData.HeaderVersion
   244  }
   245  
   246  // GetChipsetID returns ChipsetID field value
   247  func (entryData *EntrySACMDataCommon) GetChipsetID() ACChipsetID { return entryData.ChipsetID }
   248  
   249  // GetFlags returns Flags field value (the module-specific flags)
   250  func (entryData *EntrySACMDataCommon) GetFlags() ACFlags { return entryData.Flags }
   251  
   252  // GetModuleVendor returns ModuleVendor field value
   253  func (entryData *EntrySACMDataCommon) GetModuleVendor() ACModuleVendor { return entryData.ModuleVendor }
   254  
   255  // GetDate returns Date field value ("year.month.day")
   256  func (entryData *EntrySACMDataCommon) GetDate() BCDDate { return entryData.Date }
   257  
   258  // GetSize returns Size field value (the size in multiples of four bytes)
   259  func (entryData *EntrySACMDataCommon) GetSize() SizeM4 { return entryData.Size }
   260  
   261  // GetTXTSVN returns TXT security version number
   262  func (entryData *EntrySACMDataCommon) GetTXTSVN() TXTSVN { return entryData.TXTSVN }
   263  
   264  // GetSESVN returns Software Guard Extensions (Secure Enclaves) Security Version Number
   265  func (entryData *EntrySACMDataCommon) GetSESVN() SESVN { return entryData.SESVN }
   266  
   267  // GetCodeControl returns the authenticated code control flags
   268  func (entryData *EntrySACMDataCommon) GetCodeControl() CodeControl { return entryData.CodeControl }
   269  
   270  // GetErrorEntryPoint returns error entry point field value
   271  func (entryData *EntrySACMDataCommon) GetErrorEntryPoint() ErrorEntryPoint {
   272  	return entryData.ErrorEntryPoint
   273  }
   274  
   275  // GetGDTLimit returns GDTLimit field value
   276  func (entryData *EntrySACMDataCommon) GetGDTLimit() GDTLimit { return entryData.GDTLimit }
   277  
   278  // GetGDTBasePtr returns the GDT base pointer offset (bytes)
   279  func (entryData *EntrySACMDataCommon) GetGDTBasePtr() GDTBasePtr { return entryData.GDTBasePtr }
   280  
   281  // GetSegSel the segment selector initializer
   282  func (entryData *EntrySACMDataCommon) GetSegSel() SegSel { return entryData.SegSel }
   283  
   284  // GetEntryPoint returns the authenticated code entry point offset (bytes)
   285  func (entryData *EntrySACMDataCommon) GetEntryPoint() EntryPoint { return entryData.EntryPoint }
   286  
   287  // GetReserved2 returns the Reserved2 field value
   288  func (entryData *EntrySACMDataCommon) GetReserved2() [64]byte { return entryData.Reserved2 }
   289  
   290  // GetKeySize returns the KeySize field value (the size in multiples of four bytes)
   291  func (entryData *EntrySACMDataCommon) GetKeySize() SizeM4 { return entryData.KeySize }
   292  
   293  // GetScratchSize returns the ScratchSize field value (the size in multiples of four bytes)
   294  func (entryData *EntrySACMDataCommon) GetScratchSize() SizeM4 { return entryData.ScratchSize }
   295  
   296  // GetRSAPubKey returns the RSA public key
   297  func (entryData *EntrySACMDataCommon) GetRSAPubKey() rsa.PublicKey { return rsa.PublicKey{} }
   298  
   299  // GetRSAPubExp returns the RSA exponent
   300  func (entryData *EntrySACMDataCommon) GetRSAPubExp() uint32 { return 0 }
   301  
   302  // GetRSASig returns the RSA signature.
   303  func (entryData *EntrySACMDataCommon) GetRSASig() []byte { return nil }
   304  
   305  // RSASigBinaryOffset returns the RSA signature offset
   306  func (entryData *EntrySACMDataCommon) RSASigBinaryOffset() uint64 { return 0 }
   307  
   308  // GetScratch returns the Scratch field value
   309  func (entryData *EntrySACMDataCommon) GetScratch() []byte { return nil }
   310  
   311  // HeaderVersionBinaryOffset returns the offset of the field 'HeaderVersion'
   312  // relatively to the beginning of the structure in the binary
   313  // format (see 'encoding/binary').
   314  func (entryData EntrySACMDataCommon) HeaderVersionBinaryOffset() uint {
   315  	return 8
   316  }
   317  
   318  // DateBinaryOffset returns the offset of the field 'Date'
   319  // relatively to the beginning of the structure in the binary
   320  // format (see 'encoding/binary').
   321  func (entryData EntrySACMDataCommon) DateBinaryOffset() uint {
   322  	return 20
   323  }
   324  
   325  // SizeBinaryOffset returns the offset of the field 'Size'
   326  // relatively to the beginning of the structure in the binary
   327  // format (see 'encoding/binary').
   328  func (entryData EntrySACMDataCommon) SizeBinaryOffset() uint {
   329  	return 24
   330  }
   331  
   332  // TXTSVNBinaryOffset returns the offset of the field 'TXTSVN'
   333  // relatively to the beginning of the structure in the binary
   334  // format (see 'encoding/binary').
   335  func (entryData *EntrySACMDataCommon) TXTSVNBinaryOffset() uint64 {
   336  	return 28
   337  }
   338  
   339  // KeySizeBinaryOffset returns the offset of the field 'KeySize'
   340  // relatively to the beginning of the structure in the binary
   341  // format (see 'encoding/binary').
   342  func (entryData EntrySACMDataCommon) KeySizeBinaryOffset() uint {
   343  	return 120
   344  }
   345  
   346  // EntrySACMData0 is the structure for ACM of version 0.0.
   347  type EntrySACMData0 struct {
   348  	EntrySACMDataCommon
   349  
   350  	RSAPubKey [256]byte
   351  	RSAPubExp [4]byte
   352  	RSASig    [256]byte
   353  	Scratch   [572]byte
   354  }
   355  
   356  var entrySACMData0Size = uint(binary.Size(EntrySACMData0{}))
   357  
   358  // Read parses the ACM v0 headers
   359  func (entryData *EntrySACMData0) Read(b []byte) (int, error) {
   360  	n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b))
   361  	return int(n), err
   362  }
   363  
   364  // ReadFrom parses the ACM v0 headers
   365  func (entryData *EntrySACMData0) ReadFrom(r io.Reader) (int64, error) {
   366  	err := binary.Read(r, binary.LittleEndian, entryData)
   367  	if err != nil {
   368  		return -1, err
   369  	}
   370  	return int64(entrySACMData0Size), nil
   371  }
   372  
   373  // Write compiles the SACM v0 headers into a binary representation
   374  func (entryData *EntrySACMData0) Write(b []byte) (int, error) {
   375  	n, err := entryData.WriteTo(bytesextra.NewReadWriteSeeker(b))
   376  	return int(n), err
   377  }
   378  
   379  // WriteTo compiles the SACM v0 headers into a binary representation
   380  func (entryData *EntrySACMData0) WriteTo(w io.Writer) (int64, error) {
   381  	err := binary.Write(w, binary.LittleEndian, entryData)
   382  	if err != nil {
   383  		return -1, err
   384  	}
   385  	return int64(entrySACMData0Size), nil
   386  }
   387  
   388  // GetRSAPubKey returns the RSA public key
   389  func (entryData *EntrySACMData0) GetRSAPubKey() rsa.PublicKey {
   390  	pubKey := rsa.PublicKey{
   391  		N: big.NewInt(0),
   392  		E: int(entryData.GetRSAPubExp()),
   393  	}
   394  	pubKey.N.SetBytes(entryData.RSAPubKey[:])
   395  	return pubKey
   396  }
   397  
   398  // GetRSAPubExp returns the RSA exponent
   399  func (entryData *EntrySACMData0) GetRSAPubExp() uint32 {
   400  	return binary.LittleEndian.Uint32(entryData.RSAPubExp[:])
   401  }
   402  
   403  // GetRSASig returns the RSA signature.
   404  func (entryData *EntrySACMData0) GetRSASig() []byte { return entryData.RSASig[:] }
   405  
   406  // RSASigBinaryOffset returns the RSA signature offset
   407  func (entryData *EntrySACMData0) RSASigBinaryOffset() uint64 {
   408  	return uint64(binary.Size(entryData.EntrySACMDataCommon)) +
   409  		uint64(binary.Size(entryData.RSAPubKey)) +
   410  		uint64(binary.Size(entryData.RSAPubExp))
   411  }
   412  
   413  // GetScratch returns the Scratch field value
   414  func (entryData *EntrySACMData0) GetScratch() []byte { return entryData.Scratch[:] }
   415  
   416  // EntrySACMData3 is the structure for ACM of version 3.0
   417  type EntrySACMData3 struct {
   418  	EntrySACMDataCommon
   419  
   420  	RSAPubKey [384]byte
   421  	RSASig    [384]byte
   422  	Scratch   [832]byte
   423  }
   424  
   425  var entrySACMData3Size = uint(binary.Size(EntrySACMData3{}))
   426  
   427  // Read parses the ACM v3 headers
   428  func (entryData *EntrySACMData3) Read(b []byte) (int, error) {
   429  	n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b))
   430  	return int(n), err
   431  }
   432  
   433  // ReadFrom parses the ACM v3 headers
   434  func (entryData *EntrySACMData3) ReadFrom(r io.Reader) (int64, error) {
   435  	err := binary.Read(r, binary.LittleEndian, entryData)
   436  	if err != nil {
   437  		return -1, err
   438  	}
   439  	return int64(entrySACMData3Size), nil
   440  }
   441  
   442  // Write compiles the SACM v3 headers into a binary representation
   443  func (entryData *EntrySACMData3) Write(b []byte) (int, error) {
   444  	n, err := entryData.WriteTo(bytesextra.NewReadWriteSeeker(b))
   445  	return int(n), err
   446  }
   447  
   448  // WriteTo compiles the SACM v3 headers into a binary representation
   449  func (entryData *EntrySACMData3) WriteTo(w io.Writer) (int64, error) {
   450  	err := binary.Write(w, binary.LittleEndian, entryData)
   451  	if err != nil {
   452  		return -1, err
   453  	}
   454  	return int64(entrySACMData3Size), nil
   455  }
   456  
   457  // GetRSAPubKey returns the RSA public key
   458  func (entryData *EntrySACMData3) GetRSAPubKey() rsa.PublicKey {
   459  	pubKey := rsa.PublicKey{
   460  		N: big.NewInt(0),
   461  		E: 0x10001, // see Table 9. "RSAPubExp" of https://www.intel.com/content/www/us/en/software-developers/txt-software-development-guide.html
   462  	}
   463  	pubKey.N.SetBytes(entryData.RSAPubKey[:])
   464  	return pubKey
   465  }
   466  
   467  // GetRSASig returns the RSA signature.
   468  func (entryData *EntrySACMData3) GetRSASig() []byte { return entryData.RSASig[:] }
   469  
   470  // RSASigBinaryOffset returns the RSA signature offset
   471  func (entryData *EntrySACMData3) RSASigBinaryOffset() uint64 {
   472  	return uint64(binary.Size(entryData.EntrySACMDataCommon)) +
   473  		uint64(binary.Size(entryData.RSAPubKey))
   474  }
   475  
   476  // GetScratch returns the Scratch field value
   477  func (entryData *EntrySACMData3) GetScratch() []byte { return entryData.Scratch[:] }
   478  
   479  // EntrySACMData combines the structure for ACM and the user area.
   480  type EntrySACMData struct {
   481  	EntrySACMDataInterface
   482  
   483  	UserArea []byte
   484  }
   485  
   486  // Read parses the ACM
   487  func (entryData *EntrySACMData) Read(b []byte) (int, error) {
   488  	n, err := entryData.ReadFrom(bytesextra.NewReadWriteSeeker(b))
   489  	return int(n), err
   490  }
   491  
   492  // ReadFrom parses the ACM
   493  func (entryData *EntrySACMData) ReadFrom(r io.Reader) (int64, error) {
   494  	parsedEntryData, err := ParseSACMData(r)
   495  	if err != nil {
   496  		return -1, err
   497  	}
   498  	*entryData = *parsedEntryData
   499  	return int64(binary.Size(entryData.EntrySACMDataInterface) + len(entryData.UserArea)), nil
   500  }
   501  
   502  // Write compiles the SACM into a binary representation
   503  func (entryData *EntrySACMData) Write(b []byte) (int, error) {
   504  	n, err := entryData.WriteTo(bytesextra.NewReadWriteSeeker(b))
   505  	return int(n), err
   506  }
   507  
   508  // WriteTo compiles the SACM into a binary representation
   509  func (entryData *EntrySACMData) WriteTo(w io.Writer) (int64, error) {
   510  	totalN, err := entryData.EntrySACMDataInterface.WriteTo(w)
   511  	if err != nil {
   512  		return -1, err
   513  	}
   514  	n, err := w.Write(entryData.UserArea)
   515  	if n >= 0 {
   516  		totalN += int64(n)
   517  	}
   518  	if err != nil {
   519  		return totalN, fmt.Errorf("unable to write UserArea: %w", err)
   520  	}
   521  	if n != len(entryData.UserArea) {
   522  		return totalN, fmt.Errorf("unable to complete writing UserArea: %d != %d: %w", n, len(entryData.UserArea), err)
   523  	}
   524  	return totalN, nil
   525  }
   526  
   527  // GetCommon returns the common part of the structures for different ACM versions.
   528  func (entryData *EntrySACMData) GetCommon() *EntrySACMDataCommon {
   529  	if entryData == nil {
   530  		return nil
   531  	}
   532  	switch data := entryData.EntrySACMDataInterface.(type) {
   533  	case *EntrySACMDataCommon:
   534  		return data
   535  	case *EntrySACMData0:
   536  		return &data.EntrySACMDataCommon
   537  	case *EntrySACMData3:
   538  		return &data.EntrySACMDataCommon
   539  	}
   540  	return nil
   541  }
   542  
   543  // EntrySACMParseSizeFrom parses SACM structure size
   544  func EntrySACMParseSizeFrom(r io.ReadSeeker, offset uint64) (uint32, error) {
   545  	sizeFieldLocalOffset := EntrySACMDataCommon{}.SizeBinaryOffset()
   546  	sizeFieldOffset := int64(offset) + int64(sizeFieldLocalOffset)
   547  	_, err := r.Seek(sizeFieldOffset, io.SeekStart)
   548  	if err != nil {
   549  		return 0, fmt.Errorf("unable to seek(%d, start): %w", sizeFieldOffset, err)
   550  	}
   551  	var result uint32
   552  	err = binary.Read(r, binary.LittleEndian, &result)
   553  	if err != nil {
   554  		return 0, fmt.Errorf("unable to read: %w", err)
   555  	}
   556  	return result << 2, nil
   557  }
   558  
   559  // EntrySACMParseSize parses SACM structure size
   560  func EntrySACMParseSize(b []byte) (uint32, error) {
   561  	sizeFieldOffset := EntrySACMDataCommon{}.SizeBinaryOffset()
   562  	if int(sizeFieldOffset) >= len(b)-4 {
   563  		return 0, &check.ErrEndLessThanStart{StartIdx: int(sizeFieldOffset), EndIdx: len(b) - 4}
   564  	}
   565  	return binary.LittleEndian.Uint32(b[sizeFieldOffset:]) << 2, nil
   566  }
   567  
   568  // ParseData parses SACM entry and returns EntrySACMData.
   569  func (entry *EntrySACM) ParseData() (*EntrySACMData, error) {
   570  	entryData := EntrySACMData{}
   571  	_, err := entryData.Read(entry.DataSegmentBytes)
   572  	if err != nil {
   573  		return nil, err
   574  	}
   575  	return &entryData, nil
   576  }
   577  
   578  // ParseSACMData parses SACM entry and returns EntrySACMData.
   579  func ParseSACMData(r io.Reader) (*EntrySACMData, error) {
   580  
   581  	// Read common headers
   582  
   583  	common := EntrySACMDataCommon{}
   584  	if _, err := common.ReadFrom(r); err != nil {
   585  		return nil, fmt.Errorf("unable to parse startup AC module entry: %w", err)
   586  	}
   587  	result := &EntrySACMData{EntrySACMDataInterface: &common, UserArea: nil}
   588  
   589  	var requiredKeySize uint64
   590  	switch common.HeaderVersion {
   591  	case ACHeaderVersion0:
   592  		result.EntrySACMDataInterface = &EntrySACMData0{EntrySACMDataCommon: common}
   593  		requiredKeySize = uint64(len(EntrySACMData0{}.RSAPubKey))
   594  	case ACHeaderVersion3:
   595  		result.EntrySACMDataInterface = &EntrySACMData3{EntrySACMDataCommon: common}
   596  		requiredKeySize = uint64(len(EntrySACMData3{}.RSAPubKey))
   597  	default:
   598  		return result, &ErrUnknownACMHeaderVersion{ACHeaderVersion: common.HeaderVersion}
   599  	}
   600  
   601  	if common.KeySize.Size() != requiredKeySize {
   602  		return result, &ErrACMInvalidKeySize{ExpectedKeySize: requiredKeySize, RealKeySize: common.KeySize.Size()}
   603  	}
   604  
   605  	// Read version-specific headers
   606  	//
   607  	// Here we need to continue reading from the reader,
   608  	// but in the resulting struct we need to skip the first field (because it contains
   609  	// already read common headers).
   610  
   611  	// Creating a substruct without the first field (which is already read)
   612  	t := reflect.TypeOf(result.EntrySACMDataInterface).Elem()
   613  	var fieldsToBeFilled []reflect.StructField
   614  	for fieldNum := 1; fieldNum < t.NumField(); fieldNum++ {
   615  		fieldsToBeFilled = append(fieldsToBeFilled, t.Field(fieldNum))
   616  	}
   617  	subStructToBeFilled := reflect.New(reflect.StructOf(fieldsToBeFilled))
   618  	// Reading the substruct
   619  	if err := binary.Read(r, binary.LittleEndian, subStructToBeFilled.Interface()); err != nil {
   620  		return result, fmt.Errorf("cannot parse version-specific headers (version 0x%04X): %w", common.HeaderVersion, err)
   621  	}
   622  	// Copying values from the substruct to the headers struct
   623  	subStructToBeFilled = subStructToBeFilled.Elem()
   624  	v := reflect.ValueOf(result.EntrySACMDataInterface).Elem()
   625  	for fieldNum := 1; fieldNum < v.NumField(); fieldNum++ {
   626  		v.Field(fieldNum).Set(subStructToBeFilled.Field(fieldNum - 1))
   627  	}
   628  
   629  	// Read UserArea
   630  
   631  	// `UserArea` has variable length and therefore was not included into
   632  	// `EntrySACMData0` and `EntrySACMData3`, but it is in the tail,
   633  	// so we just calculate the startIndex as the end of
   634  	// EntrySACMData0/EntrySACMData3.
   635  	userAreaStartIdx := uint64(binary.Size(result.EntrySACMDataInterface))
   636  	userAreaEndIdx := result.EntrySACMDataInterface.GetSize().Size()
   637  	if userAreaEndIdx > userAreaStartIdx {
   638  		var err error
   639  		result.UserArea, err = readBytesFromReader(r, userAreaEndIdx-userAreaStartIdx)
   640  		if err != nil {
   641  			return result, fmt.Errorf("unable to read user area: %w", err)
   642  		}
   643  	}
   644  
   645  	return result, nil
   646  }
   647  
   648  type entrySACMJSON struct {
   649  	Headers        *EntryHeaders
   650  	DataParsed     *EntrySACMData `json:",omitempty"`
   651  	DataNotParsed  []byte         `json:"DataNotParsedBase64,omitempty"`
   652  	HeadersErrors  []error
   653  	DataParseError error
   654  }
   655  
   656  // MarshalJSON implements json.Marshaler
   657  func (entry *EntrySACM) MarshalJSON() ([]byte, error) {
   658  	result := entrySACMJSON{}
   659  	result.DataParsed, result.DataParseError = entry.ParseData()
   660  	result.Headers = &entry.Headers
   661  	result.HeadersErrors = make([]error, len(entry.HeadersErrors))
   662  	copy(result.HeadersErrors, entry.HeadersErrors)
   663  	result.DataNotParsed = entry.DataSegmentBytes
   664  	return json.Marshal(&result)
   665  }
   666  
   667  // UnmarshalJSON implements json.Unmarshaller
   668  func (entry *EntrySACM) UnmarshalJSON(b []byte) error {
   669  	result := entrySACMJSON{}
   670  	err := json.Unmarshal(b, &result)
   671  	if err != nil {
   672  		return err
   673  	}
   674  	entry.Headers = *result.Headers
   675  	entry.HeadersErrors = result.HeadersErrors
   676  	entry.DataSegmentBytes = result.DataNotParsed
   677  	return nil
   678  }