github.com/jlowellwofford/u-root@v1.0.0/pkg/boot/measurement.go (about)

     1  // Copyright 2018 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 boot
     6  
     7  import (
     8  	"bytes"
     9  	"crypto"
    10  	"crypto/rand"
    11  	"crypto/rsa"
    12  	"crypto/sha1"
    13  	"crypto/sha256"
    14  	_ "crypto/sha512"
    15  	//"encoding/binary"
    16  	"fmt"
    17  	"io"
    18  
    19  	"github.com/google/go-tpm/tpm"
    20  	"github.com/u-root/u-root/pkg/cpio"
    21  	"github.com/u-root/u-root/pkg/uio"
    22  	"golang.org/x/sys/unix"
    23  )
    24  
    25  // MeasuringReader is a cpio.Reader that collects the signed data and compares
    26  // it against the signature in the given cpio archive.
    27  type MeasuringReader struct {
    28  	r cpio.RecordReader
    29  
    30  	signed    *bytes.Buffer
    31  	signature *bytes.Buffer
    32  }
    33  
    34  // NewMeasuringReader returns a new measuring reader.
    35  func NewMeasuringReader(r cpio.RecordReader) *MeasuringReader {
    36  	return &MeasuringReader{
    37  		r:         r,
    38  		signed:    &bytes.Buffer{},
    39  		signature: &bytes.Buffer{},
    40  	}
    41  }
    42  
    43  // Verify verifies the contents of the archive as read so far.
    44  //
    45  // NOTE(UGH): Go crypto stuff is totally incompatible. ecdsa.PrivateKey.Sign
    46  // does not output shit that is compatible with ecdsa.Verify -- COME ON. Only
    47  // ecdsa.Sign does.
    48  func (mr *MeasuringReader) Verify(pk *rsa.PublicKey) error {
    49  	hashed := sha256.Sum256(mr.signed.Bytes())
    50  	return rsa.VerifyPKCS1v15(pk, crypto.SHA256, hashed[:], mr.signature.Bytes())
    51  }
    52  
    53  // ExtendTPM extends the given tpm at pcrIndex with the content of the package.
    54  func (mr *MeasuringReader) ExtendTPM(tpmRW io.ReadWriter, pcrIndex uint32) error {
    55  	pcrValue := sha1.Sum(mr.signed.Bytes())
    56  	_, err := tpm.PcrExtend(tpmRW, pcrIndex, pcrValue)
    57  	return err
    58  }
    59  
    60  // ReadRecord wraps cpio.Reader.ReadRecord and adds the content to `signed` as
    61  // necessary.
    62  func (mr *MeasuringReader) ReadRecord() (cpio.Record, error) {
    63  	for {
    64  		rec, err := mr.r.ReadRecord()
    65  		if err != nil {
    66  			return rec, err
    67  		}
    68  
    69  		switch rec.Name {
    70  		case "signature":
    71  			// Error return is intentionally ignored.
    72  			mr.signature.ReadFrom(uio.Reader(rec))
    73  			continue
    74  
    75  		case "signature_algo":
    76  			//err = binary.Read(uio.Reader(rec), binary.LittleEndian, &mr.algo)
    77  			continue
    78  
    79  		default:
    80  			// Measure all regular files.
    81  			if rec.Info.Mode&unix.S_IFMT == unix.S_IFREG {
    82  				if _, err := mr.signed.WriteString(rec.Name); err != nil {
    83  					return cpio.Record{}, err
    84  				}
    85  				if _, err := mr.signed.ReadFrom(uio.Reader(rec)); err != nil {
    86  					return cpio.Record{}, err
    87  				}
    88  			}
    89  			return rec, nil
    90  		}
    91  	}
    92  }
    93  
    94  // SigningWriter is a cpio.RecordWriter that collects digests as it writes
    95  // files to the cpio archive.
    96  type SigningWriter struct {
    97  	w cpio.RecordWriter
    98  
    99  	digest *bytes.Buffer
   100  }
   101  
   102  // NewSigningWriter returns a new signing cpio writer.
   103  func NewSigningWriter(w cpio.RecordWriter) *SigningWriter {
   104  	return &SigningWriter{
   105  		w:      w,
   106  		digest: &bytes.Buffer{},
   107  	}
   108  }
   109  
   110  // WriteRecord implements cpio.RecordWriter.
   111  func (sw *SigningWriter) WriteRecord(rec cpio.Record) error {
   112  	rec = cpio.MakeReproducible(rec)
   113  	if rec.Info.Name == "signature" || rec.Info.Name == "signature_algo" {
   114  		return fmt.Errorf("cannot write signature or signature_algo files")
   115  	}
   116  	if rec.Info.Mode&unix.S_IFMT == unix.S_IFREG {
   117  		if _, err := sw.digest.WriteString(rec.Info.Name); err != nil {
   118  			return err
   119  		}
   120  		if _, err := sw.digest.ReadFrom(uio.Reader(rec)); err != nil {
   121  			return err
   122  		}
   123  	}
   124  	return sw.w.WriteRecord(rec)
   125  }
   126  
   127  // SHA1Sum returns the SHA1 sum of the collected digest.
   128  func (sw *SigningWriter) SHA1Sum() [sha1.Size]byte {
   129  	return sha1.Sum(sw.digest.Bytes())
   130  }
   131  
   132  // WriteSignature writes the signature and signature_algo files based on the
   133  // collected digest.
   134  //
   135  // TODO(hugelgupf): stop hard-coding the private key and algorithm. Use
   136  // crypto.Signer so TPM could be used to sign this if so desired.
   137  func (sw *SigningWriter) WriteSignature(signer *rsa.PrivateKey) error {
   138  	hashed := sha256.Sum256(sw.digest.Bytes())
   139  	signature, err := signer.Sign(rand.Reader, hashed[:], crypto.SHA256)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	if err := sw.w.WriteRecord(cpio.StaticFile("signature", string(signature), 0700)); err != nil {
   144  		return err
   145  	}
   146  
   147  	return nil
   148  	/*algo := &bytes.Buffer{}
   149  	if err := binary.Write(algo, binary.LittleEndian, x509.ECDSAWithSHA512); err != nil {
   150  		return err
   151  	}
   152  	// TODO(hugelgupf): use x509 package for all of this.
   153  	// TODO(hugelgupf, later): no, please don't.
   154  	return sw.w.WriteRecord(cpio.StaticFile("signature_algo", string(algo.Bytes()), 0700))*/
   155  }