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