github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/binary_transparency/firmware/internal/verify/bundle_test.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.package bundle_test 14 15 package verify_test 16 17 import ( 18 "bytes" 19 "crypto/sha512" 20 "encoding/base64" 21 "encoding/json" 22 "errors" 23 "testing" 24 25 "github.com/google/trillian-examples/binary_transparency/firmware/api" 26 "github.com/google/trillian-examples/binary_transparency/firmware/internal/crypto" 27 "github.com/google/trillian-examples/binary_transparency/firmware/internal/verify" 28 "github.com/transparency-dev/merkle/proof" 29 "golang.org/x/mod/sumdb/note" 30 ) 31 32 const ( 33 goldenProofBundle = `{"ManifestStatement":"eyJUeXBlIjoxMDIsIlN0YXRlbWVudCI6ImV5SkVaWFpwWTJWSlJDSTZJbVIxYlcxNUlpd2lSbWx5YlhkaGNtVlNaWFpwYzJsdmJpSTZNU3dpUm1seWJYZGhjbVZKYldGblpWTklRVFV4TWlJNkltZ3ZTblpLTURVeE1GZE5Ua05hZG1wWFQwTXdVMUZxTDFKUFJHVXpLMGh6Uld0dE5HMUhUbnBWVEhSd1lXVlVhblkyU2xrcmQzSTBlVk51Vm5aNVJqVXdZa055TVhSd2NYZERiMEZvWm5CeFFscHNNbUpSUFQwaUxDSkZlSEJsWTNSbFpFWnBjbTEzWVhKbFRXVmhjM1Z5WlcxbGJuUWlPaUkyZEZWWGVYbHJlbmRtYjI5dVIzVm5ibVl4WkV3clkyZGtObUpGVjFWb2IyeEJUbUZFVERoS1dVdFhkR1J0VUdORlpWbDJaMDgyYmsxeUwwbE1aMWRRWTFWWloyZDJRVUZ5Y25SaFUwSnRZVzQxU0RSTFp6MDlJaXdpUW5WcGJHUlVhVzFsYzNSaGJYQWlPaUl5TURJd0xURXdMVEV3VkRFMU9qTXdPakl3TGpFd1dpSjkiLCJTaWduYXR1cmUiOiJUeUxVdFpCdHJHbyt3anRoSjI2Rk8wVE5QUHpTTDZhU0c1V0ZTanRCcjZLZ2x4a0RjR3dmZUxTTEpjbklmUnhZMnVJZHZLL09tMStXMndLNEkxSFRUYTdIUFZlSHo2MmF0V09hZm9TL1ZGc01OdEx1RkplaU5WNE5uY2Y0bllYMFBDdnN0MHRpYm5TVFRzNnEwMUZ1cEhaMnFwc2lyY2hFVXgwLzFjOFFOM2hGZVArSXcwVWxPNTVvZUhlWGtlRGRwL2w5SCsvZjYxYndFMmpHZVl1cFcvbld2bmN2NFgrS00weXgrYW1oVi9od0lCMDQ2aitNQzVndkd4LzJ2TkkySk5JeTBOQk13YWRIK1VONnp1MzRIZzM5NkY4MkxJU2NTOXU2MENBY3hzRlozR3d5NGpoR1JXR1lwSnhXdEJ6Zk5hZ1IvaVdmUzRJY2tJVmZ5Z2ZQUXc9PSJ9","Checkpoint":"RmlybXdhcmUgVHJhbnNwYXJlbmN5IExvZwo1Clg2VEY4QWNkSUh2OVp0UWwrU1NhZVZOYy9aNVJjNDJweDFpRlJLVGNDdHc9CjE2MDc0NTA3MzgxMTE1MDYwODgKCuKAlCBmdF9wZXJzb25hbGl0eSA2S0pDdlg1NTYrSGxNSnozMlhnQVhBYit1ODVOUEpSTkt2eHJ5enU1WTVibGkxWTh0YURsNDNjS2lmeVdsT1pYQWVnYndCaDUyUlNWc3ZJSUovTXY1K2Z0WHdjPQo=","InclusionProof":{"Value":null,"LeafIndex":4,"Proof":["KFh4IVeIwbsvbWyz2QHVCXXyjWTRDqusRa0ZEjS2fls="]}}` 34 goldenFirmwareImage = `Firmware image` 35 // goldenFirmwareHashB64 is a base64 encoded string for ExpectedMeasurement field inside ManifestStatement. 36 // For the dummy device, this is SHA512("dummy"||img), where img is the base64 decoded bytes from 37 // goldenUpdate.FirmwareImage 38 goldenFirmwareHashB64 = "6tUWyykzwfoonGugnf1dL+cgd6bEWUholANaDL8JYKWtdmPcEeYvgO6nMr/ILgWPcUYggvAArrtaSBman5H4Kg==" 39 ) 40 41 func mustGetLogSigVerifier(t *testing.T) note.Verifier { 42 t.Helper() 43 v, err := note.NewVerifier(crypto.TestFTPersonalityPub) 44 if err != nil { 45 t.Fatalf("failed to create verifier: %q", err) 46 } 47 return v 48 } 49 50 // This test is useful for creating the Checkpoint field of the goldenProofBundle above. 51 func TestGenerateGoldenCheckpoint(t *testing.T) { 52 cp := "RmlybXdhcmUgVHJhbnNwYXJlbmN5IExvZwo1Clg2VEY4QWNkSUh2OVp0UWwrU1NhZVZOYy9aNVJjNDJweDFpRlJLVGNDdHc9CjE2MDc0NTA3MzgxMTE1MDYwODgKCuKAlCBmdF9wZXJzb25hbGl0eSA2S0pDdlg1NTYrSGxNSnozMlhnQVhBYit1ODVOUEpSTkt2eHJ5enU1WTVibGkxWTh0YURsNDNjS2lmeVdsT1pYQWVnYndCaDUyUlNWc3ZJSUovTXY1K2Z0WHdjPQo=" 53 nb, _ := base64.StdEncoding.DecodeString(cp) 54 s, err := note.NewSigner(crypto.TestFTPersonalityPriv) 55 if err != nil { 56 t.Fatalf("failed to create signer: %q", err) 57 } 58 n, err := note.Sign(¬e.Note{Text: string(nb)}, s) 59 if err != nil { 60 t.Fatalf("failed to sign note: %q", err) 61 } 62 t.Log(string(n)) 63 t.Log(base64.StdEncoding.EncodeToString(n)) 64 } 65 66 func TestBundleForUpdate(t *testing.T) { 67 var dc api.LogCheckpoint 68 getProof := func(from, to uint64) ([][]byte, error) { return [][]byte{}, nil } 69 70 for _, test := range []struct { 71 desc string 72 img []byte 73 wantErr bool 74 }{ 75 { 76 desc: "all good", 77 img: []byte(goldenFirmwareImage), 78 }, { 79 desc: "bad image hash", 80 img: []byte("this is wrong"), 81 wantErr: true, 82 }, 83 } { 84 t.Run(test.desc, func(t *testing.T) { 85 imgHash := sha512.Sum512(test.img) 86 _, _, err := verify.BundleForUpdate([]byte(goldenProofBundle), imgHash[:], dc, getProof, mustGetLogSigVerifier(t)) 87 if (err != nil) != test.wantErr { 88 var lve proof.RootMismatchError 89 if errors.As(err, &lve) { 90 // Printing this out allows `goldenProofBundle` to be updated if needed 91 t.Errorf("calculated root %s", base64.StdEncoding.EncodeToString(lve.CalculatedRoot)) 92 } 93 t.Fatalf("want err %v, got %q", test.wantErr, err) 94 } 95 }) 96 } 97 } 98 99 func b64Decode(t *testing.T, b64 string) []byte { 100 t.Helper() 101 st, err := base64.StdEncoding.DecodeString(b64) 102 if err != nil { 103 t.Fatalf("b64 decoding failed: %v", err) 104 } 105 return st 106 } 107 108 func TestBundleForBoot(t *testing.T) { 109 for _, test := range []struct { 110 desc string 111 measurement []byte 112 wantErr bool 113 }{ 114 { 115 desc: "all good", 116 measurement: []byte(b64Decode(t, goldenFirmwareHashB64)), 117 }, { 118 desc: "bad image hash", 119 measurement: []byte("this is wrong"), 120 wantErr: true, 121 }, 122 } { 123 t.Run(test.desc, func(t *testing.T) { 124 err := verify.BundleForBoot([]byte(goldenProofBundle), test.measurement, mustGetLogSigVerifier(t)) 125 if (err != nil) != test.wantErr { 126 t.Fatalf("want err %v, got %q", test.wantErr, err) 127 } 128 }) 129 } 130 } 131 132 // This test is really showing how the constants above are generated, and allows 133 // them to be regenerated should any of the underlying formats change. 134 func TestGoldenBundleGeneration(t *testing.T) { 135 h := sha512.Sum512([]byte(goldenFirmwareImage)) 136 meta := api.FirmwareMetadata{ 137 DeviceID: "dummy", 138 FirmwareRevision: 1, 139 FirmwareImageSHA512: h[:], 140 ExpectedFirmwareMeasurement: b64Decode(t, goldenFirmwareHashB64), 141 BuildTimestamp: "2020-10-10T15:30:20.10Z", 142 } 143 144 mbs, _ := json.Marshal(meta) 145 sig, err := crypto.Publisher.SignMessage(api.FirmwareMetadataType, mbs) 146 if err != nil { 147 t.Error(err) 148 } 149 ss := api.SignedStatement{ 150 Type: api.FirmwareMetadataType, 151 Statement: mbs, 152 Signature: sig, 153 } 154 var gpb api.ProofBundle 155 if err := json.Unmarshal([]byte(goldenProofBundle), &gpb); err != nil { 156 t.Error(err) 157 } 158 var gss api.SignedStatement 159 if err := json.Unmarshal(gpb.ManifestStatement, &gss); err != nil { 160 t.Error(err) 161 } 162 // Signature can't go into this check because they are non-deterministic. 163 if gss.Type != ss.Type || !bytes.Equal(gss.Statement, ss.Statement) { 164 gbs, _ := json.Marshal(gss) 165 sbs, _ := json.Marshal(ss) 166 // If this fails then the golden values in the test may need updating with `sbs` 167 t.Errorf("Golden != computed: %s, %s", gbs, sbs) 168 } 169 170 }