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