github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/securelaunch/tpm/tpm.go (about)

     1  // Copyright 2019 the u-root 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 tpm reads and extends pcrs with measurements.
     6  package tpm
     7  
     8  import (
     9  	"bytes"
    10  	"crypto/sha256"
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"log"
    16  
    17  	"github.com/google/go-tpm/tpm2"
    18  	slaunch "github.com/mvdan/u-root-coreutils/pkg/securelaunch"
    19  	"github.com/mvdan/u-root-coreutils/pkg/securelaunch/eventlog"
    20  	"github.com/mvdan/u-root-coreutils/pkg/tss"
    21  )
    22  
    23  var (
    24  	hashAlgo  = tpm2.AlgSHA256
    25  	tpmHandle *tss.TPM
    26  )
    27  
    28  // marshalPcrEvent writes structure fields piecemeal to a buffer.
    29  func marshalPcrEvent(pcr uint32, h []byte, eventDesc []byte) ([]byte, error) {
    30  	const baseTypeTXT = 0x400                       // TXT specification base event value for DRTM values
    31  	const slaunchType = uint32(baseTypeTXT + 0x102) // Secure Launch event log entry type.
    32  	count := uint32(1)
    33  	eventDescLen := uint32(len(eventDesc))
    34  	slaunch.Debug("marshalPcrEvent: pcr=[%v], slaunchType=[%v], count=[%v], hashAlgo=[%v], eventDesc=[%s], eventDescLen=[%v]",
    35  		pcr, slaunchType, count, hashAlgo, eventDesc, eventDescLen)
    36  
    37  	endianess := binary.LittleEndian
    38  	var buf bytes.Buffer
    39  
    40  	if err := binary.Write(&buf, endianess, pcr); err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	if err := binary.Write(&buf, endianess, slaunchType); err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	if err := binary.Write(&buf, endianess, count); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	for i := uint32(0); i < count; i++ {
    53  		if err := binary.Write(&buf, endianess, hashAlgo); err != nil {
    54  			return nil, err
    55  		}
    56  
    57  		if err := binary.Write(&buf, endianess, h); err != nil {
    58  			return nil, err
    59  		}
    60  	}
    61  
    62  	if err := binary.Write(&buf, endianess, eventDescLen); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	if err := binary.Write(&buf, endianess, eventDesc); err != nil {
    67  		return nil, err
    68  	}
    69  	return buf.Bytes(), nil
    70  }
    71  
    72  // sendEventToSysfs marshals measurement events and writes them to sysfs.
    73  func sendEventToSysfs(pcr uint32, h []byte, eventDesc []byte) {
    74  	b, err := marshalPcrEvent(pcr, h, eventDesc)
    75  	if err != nil {
    76  		log.Println(err)
    77  	}
    78  
    79  	if e := eventlog.Add(b); e != nil {
    80  		log.Println(e)
    81  	}
    82  }
    83  
    84  // hashReader calculates the sha256 sum of an io reader.
    85  func hashReader(f io.Reader) []byte {
    86  	h := sha256.New()
    87  	if _, err := io.Copy(h, f); err != nil {
    88  		log.Fatal(err)
    89  	}
    90  
    91  	return h.Sum(nil)
    92  }
    93  
    94  // New sets up a TPM device handle that can be used for storing hashes.
    95  func New() error {
    96  	tpm, err := tss.NewTPM()
    97  	if err != nil {
    98  		return fmt.Errorf("couldn't talk to TPM Device: err=%v", err)
    99  	}
   100  
   101  	tpmHandle = tpm
   102  	return nil
   103  }
   104  
   105  // Close closes the connection of a TPM device handle.
   106  func Close() {
   107  	if tpmHandle != nil {
   108  		tpmHandle.Close()
   109  		tpmHandle = nil
   110  	}
   111  }
   112  
   113  // readPCR reads the given PCR and returns the result in a byte slice.
   114  func readPCR(pcr uint32) ([]byte, error) {
   115  	if tpmHandle == nil {
   116  		return nil, errors.New("tpmHandle is nil")
   117  	}
   118  
   119  	val, err := tpmHandle.ReadPCR(pcr)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("can't read PCR %d, err= %v", pcr, err)
   122  	}
   123  	return val, nil
   124  }
   125  
   126  // extendPCR extends the given PCR with the given hash.
   127  func extendPCR(pcr uint32, hash []byte) error {
   128  	if tpmHandle == nil {
   129  		return errors.New("tpmHandle is nil")
   130  	}
   131  
   132  	return tpmHandle.Extend(hash, pcr)
   133  }
   134  
   135  // ExtendPCRDebug extends a PCR with the contents of a byte slice and notifies
   136  // the kernel of this measurement by sending an event via sysfs.
   137  //
   138  // In debug mode, it prints:
   139  //  1. The old PCR value before the hash is extended to the PCR
   140  //  2. The new PCR value after the hash is extended to the PCR
   141  func ExtendPCRDebug(pcr uint32, data io.Reader, eventDesc string) error {
   142  	oldPCRValue, err := readPCR(pcr)
   143  	if err != nil {
   144  		return fmt.Errorf("readPCR failed, err=%v", err)
   145  	}
   146  	slaunch.Debug("ExtendPCRDebug: oldPCRValue = [%x]", oldPCRValue)
   147  
   148  	hash := hashReader(data)
   149  
   150  	slaunch.Debug("Adding hash=[%x] to PCR #%d", hash, pcr)
   151  	if e := extendPCR(pcr, hash); e != nil {
   152  		return fmt.Errorf("can't extend PCR %d, err=%v", pcr, e)
   153  	}
   154  	slaunch.Debug(eventDesc)
   155  
   156  	// send event if PCR was successfully extended above.
   157  	sendEventToSysfs(pcr, hash, []byte(eventDesc))
   158  
   159  	newPCRValue, err := readPCR(pcr)
   160  	if err != nil {
   161  		return fmt.Errorf("readPCR failed, err=%v", err)
   162  	}
   163  	slaunch.Debug("ExtendPCRDebug: newPCRValue = [%x]", newPCRValue)
   164  
   165  	finalPCR := hashReader(bytes.NewReader(append(oldPCRValue, hash...)))
   166  	if !bytes.Equal(finalPCR, newPCRValue) {
   167  		return fmt.Errorf("PCRs not equal, got %x, want %x", finalPCR, newPCRValue)
   168  	}
   169  
   170  	return nil
   171  }