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 }