github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/binary_transparency/firmware/internal/verify/bundle.go (about) 1 // Copyright 2020 Google LLC. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package verify holds helpers for validating the correctness of various 16 // artifacts and proofs used in the system. 17 package verify 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 24 "github.com/golang/glog" 25 "github.com/google/trillian-examples/binary_transparency/firmware/api" 26 "github.com/google/trillian-examples/binary_transparency/firmware/internal/crypto" 27 "github.com/transparency-dev/merkle/proof" 28 "github.com/transparency-dev/merkle/rfc6962" 29 "golang.org/x/mod/sumdb/note" 30 ) 31 32 // ConsistencyProofFunc is a function which returns a consistency proof between two tree sizes. 33 type ConsistencyProofFunc func(from, to uint64) ([][]byte, error) 34 35 // BundleForUpdate checks that the manifest, checkpoint, and proofs in a raw bundle 36 // are all self-consistent, and that the provided firmware image hash matches 37 // the one in the bundle. It also checks consistency proof between update log point 38 // and device log point (for non zero device tree size). Upon successful verification 39 // returns a proof bundle 40 func BundleForUpdate(bundleRaw, fwHash []byte, dc api.LogCheckpoint, cpFunc ConsistencyProofFunc, logSigVerifier note.Verifier) (api.ProofBundle, api.FirmwareMetadata, error) { 41 proofBundle, fwMeta, err := verifyBundle(bundleRaw, logSigVerifier) 42 if err != nil { 43 return proofBundle, fwMeta, err 44 } 45 46 if got, want := fwHash, fwMeta.FirmwareImageSHA512; !bytes.Equal(got, want) { 47 return proofBundle, fwMeta, fmt.Errorf("firmware update image hash does not match metadata (0x%x != 0x%x)", got, want) 48 } 49 50 pc, err := api.ParseCheckpoint(proofBundle.Checkpoint, logSigVerifier) 51 if err != nil { 52 return proofBundle, fwMeta, fmt.Errorf("failed to open the device checkpoint: %w", err) 53 } 54 55 cProof, err := cpFunc(dc.Size, pc.Size) 56 if err != nil { 57 return proofBundle, fwMeta, fmt.Errorf("cpFunc failed: %q", err) 58 } 59 60 // Verify the consistency proof between device and bundle checkpoint 61 if dc.Size > 0 { 62 h := rfc6962.DefaultHasher 63 if err := proof.VerifyConsistency(h, dc.Size, pc.Size, cProof, dc.Hash, pc.Hash); err != nil { 64 return proofBundle, fwMeta, fmt.Errorf("failed verification of consistency proof %w", err) 65 } 66 } 67 return proofBundle, fwMeta, nil 68 } 69 70 // BundleConsistency verifies the log checkpoint in the bundle is consistent against a given checkpoint (e.g. one fetched from a witness). 71 func BundleConsistency(pb api.ProofBundle, rc api.LogCheckpoint, cpFunc ConsistencyProofFunc, logSigVerifier note.Verifier) error { 72 73 glog.V(1).Infof("Remote TreeSize=%d, Inclusion Index=%d \n", rc.Size, pb.InclusionProof.LeafIndex) 74 if rc.Size < pb.InclusionProof.LeafIndex { 75 return fmt.Errorf("remote verification failed wcp treesize(%d)<device cp index(%d)", rc.Size, pb.InclusionProof.LeafIndex) 76 } 77 78 bundleCP, err := api.ParseCheckpoint(pb.Checkpoint, logSigVerifier) 79 if err != nil { 80 return fmt.Errorf("failed to open the proof bundle checkpoint: %w", err) 81 } 82 fromCP, toCP := rc, *bundleCP 83 // swap the remote checkpoint(fromCP) with published checkpoint (toCP) if it is ahead of published checkpoint 84 if rc.Size > bundleCP.Size { 85 fromCP, toCP = toCP, fromCP 86 } 87 cProof, err := cpFunc(fromCP.Size, toCP.Size) 88 if err != nil { 89 return fmt.Errorf("cpFunc failed: %q", err) 90 } 91 h := rfc6962.DefaultHasher 92 if err := proof.VerifyConsistency(h, fromCP.Size, toCP.Size, cProof, fromCP.Hash, toCP.Hash); err != nil { 93 return fmt.Errorf("failed consistency proof between remote and client checkpoint %w", err) 94 } 95 return nil 96 } 97 98 // BundleForBoot checks that the manifest, checkpoint, and proofs in a bundle 99 // are all self-consistent, and that the provided firmware measurement matches 100 // the one expected by the bundle. 101 func BundleForBoot(bundleRaw, measurement []byte, logSigVerifier note.Verifier) error { 102 _, fwMeta, err := verifyBundle(bundleRaw, logSigVerifier) 103 if err != nil { 104 return err 105 } 106 107 if got, want := measurement, fwMeta.ExpectedFirmwareMeasurement; !bytes.Equal(got, want) { 108 return fmt.Errorf("firmware measurement does not match metadata (0x%x != 0x%x)", got, want) 109 } 110 return nil 111 } 112 113 // verifyBundle parses a proof bundle and verifies its self-consistency. 114 func verifyBundle(bundleRaw []byte, logSigVerifier note.Verifier) (api.ProofBundle, api.FirmwareMetadata, error) { 115 var pb api.ProofBundle 116 if err := json.Unmarshal(bundleRaw, &pb); err != nil { 117 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("failed to parse proof bundle: %w", err) 118 } 119 120 bundleCP, err := api.ParseCheckpoint(pb.Checkpoint, logSigVerifier) 121 if err != nil { 122 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("failed to open the proof bundle checkpoint: %w", err) 123 } 124 125 var fwStatement api.SignedStatement 126 if err := json.Unmarshal(pb.ManifestStatement, &fwStatement); err != nil { 127 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("failed to unmarshal SignedStatement: %w", err) 128 } 129 // Verify the statement signature: 130 if err := crypto.Publisher.VerifySignature(fwStatement.Type, fwStatement.Statement, fwStatement.Signature); err != nil { 131 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("failed to verify signature on SignedStatement: %w", err) 132 } 133 if fwStatement.Type != api.FirmwareMetadataType { 134 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("expected statement type %q, but got %q", api.FirmwareMetadataType, fwStatement.Type) 135 } 136 137 h := rfc6962.DefaultHasher 138 lh := h.HashLeaf(pb.ManifestStatement) 139 if err := proof.VerifyInclusion(h, pb.InclusionProof.LeafIndex, bundleCP.Size, lh, pb.InclusionProof.Proof, bundleCP.Hash); err != nil { 140 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("invalid inclusion proof in bundle: %w", err) 141 } 142 143 var fwMeta api.FirmwareMetadata 144 if err := json.Unmarshal(fwStatement.Statement, &fwMeta); err != nil { 145 return api.ProofBundle{}, api.FirmwareMetadata{}, fmt.Errorf("failed to unmarshal Metadata: %w", err) 146 } 147 148 return pb, fwMeta, nil 149 }