1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package verify
17
18 import (
19 "context"
20 "crypto/sha256"
21 "encoding/hex"
22 "testing"
23
24 "github.com/go-openapi/runtime"
25 "github.com/go-openapi/strfmt"
26 "github.com/go-openapi/swag"
27 "github.com/sigstore/rekor/pkg/generated/client"
28 "github.com/sigstore/rekor/pkg/generated/client/tlog"
29 "github.com/sigstore/rekor/pkg/generated/models"
30 "github.com/sigstore/rekor/pkg/util"
31 "github.com/sigstore/sigstore/pkg/signature"
32 )
33
34 type TlogClient struct {
35 Proof []string
36 Root string
37 LogInfo models.LogInfo
38 }
39
40 func (m *TlogClient) GetLogProof(_ *tlog.GetLogProofParams, _ ...tlog.ClientOption) (*tlog.GetLogProofOK, error) {
41 return &tlog.GetLogProofOK{
42 Payload: &models.ConsistencyProof{
43 Hashes: m.Proof,
44 RootHash: &m.Root,
45 }}, nil
46 }
47
48 func (m *TlogClient) GetLogInfo(_ *tlog.GetLogInfoParams, _ ...tlog.ClientOption) (*tlog.GetLogInfoOK, error) {
49 return &tlog.GetLogInfoOK{
50 Payload: &m.LogInfo,
51 }, nil
52 }
53
54
55 func (m *TlogClient) SetTransport(_ runtime.ClientTransport) {
56 }
57
58 func TestConsistency(t *testing.T) {
59 root2String := "5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"
60 root2, _ := hex.DecodeString(root2String)
61 root1, _ := hex.DecodeString("59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c")
62 root0, _ := hex.DecodeString("1a341bc342ff4e567387de9789ab14000b147124317841489172419874198147")
63 hashes := []string{"d3be742c8d73e2dd3c5635843e987ad3dfb3837616f412a07bf730c3ad73f5cb"}
64 for _, test := range []struct {
65 name string
66 oldC util.Checkpoint
67 newC util.Checkpoint
68 Proof []string
69 wantErr bool
70 }{
71 {
72 name: "zero length proof",
73 oldC: util.Checkpoint{
74 Origin: "test",
75 Size: uint64(2),
76 Hash: root2,
77 },
78 newC: util.Checkpoint{
79 Origin: "test",
80 Size: uint64(2),
81 Hash: root2,
82 },
83 wantErr: false,
84 },
85 {
86 name: "valid consistency proof",
87 oldC: util.Checkpoint{
88 Origin: "test",
89 Size: uint64(1),
90 Hash: root1,
91 },
92 newC: util.Checkpoint{
93 Origin: "test",
94 Size: uint64(2),
95 Hash: root2,
96 },
97 wantErr: false,
98 },
99 {
100 name: "invalid new sth request",
101 oldC: util.Checkpoint{
102 Origin: "test",
103 Size: uint64(2),
104 Hash: root1,
105 },
106 newC: util.Checkpoint{
107 Origin: "test",
108 Size: uint64(1),
109 Hash: root2,
110 },
111 wantErr: true,
112 },
113 {
114 name: "invalid consistency proof",
115 oldC: util.Checkpoint{
116 Origin: "test",
117 Size: uint64(1),
118 Hash: root2,
119 },
120 newC: util.Checkpoint{
121 Origin: "test",
122 Size: uint64(2),
123 Hash: root1,
124 },
125 wantErr: true,
126 },
127 {
128 name: "invalid consistency - same size",
129 oldC: util.Checkpoint{
130 Origin: "test",
131 Size: uint64(1),
132 Hash: root1,
133 },
134 newC: util.Checkpoint{
135 Origin: "test",
136 Size: uint64(1),
137 Hash: root2,
138 },
139 wantErr: true,
140 },
141 {
142 name: "invalid consistency - empty log",
143 oldC: util.Checkpoint{
144 Origin: "test",
145 Size: uint64(0),
146 Hash: root0,
147 },
148 newC: util.Checkpoint{
149 Origin: "test",
150 Size: uint64(2),
151 Hash: root2,
152 },
153 wantErr: true,
154 },
155 } {
156 var mClient client.Rekor
157 mClient.Tlog = &TlogClient{Proof: hashes, Root: root2String}
158
159 t.Run(string(test.name), func(t *testing.T) {
160
161 ctx := context.Background()
162 treeID := "123"
163 oldSTH, err := util.CreateSignedCheckpoint(test.oldC)
164 if err != nil {
165 t.Fatalf("creating old checkpoint")
166 }
167 newSTH, err := util.CreateSignedCheckpoint(test.newC)
168 if err != nil {
169 t.Fatalf("creating new checkpoint")
170 }
171
172 gotErr := ProveConsistency(ctx, &mClient, oldSTH, newSTH, treeID)
173
174 if (gotErr != nil) != test.wantErr {
175 t.Fatalf("ProveConsistency = %t, wantErr %t", gotErr, test.wantErr)
176 }
177 })
178 }
179 }
180
181 func TestInclusion(t *testing.T) {
182 time := int64(1661794812)
183 logID := "1701474e8cb504dbb853a5887bc2cf66936b0f36d2641bfb61f1abae80088e6a"
184 for _, test := range []struct {
185 name string
186 e models.LogEntryAnon
187 wantErr bool
188 }{
189 {
190 name: "valid inclusion",
191 e: models.LogEntryAnon{
192 Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19",
193 IntegratedTime: &time,
194 LogID: &logID,
195 LogIndex: swag.Int64(1),
196 Verification: &models.LogEntryAnonVerification{
197 InclusionProof: &models.InclusionProof{
198 TreeSize: swag.Int64(int64(2)),
199 RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"),
200 LogIndex: swag.Int64(1),
201 Hashes: []string{
202 "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c",
203 },
204 },
205 SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="),
206 },
207 },
208 wantErr: false,
209 },
210 {
211 name: "invalid inclusion - bad body hash",
212 e: models.LogEntryAnon{
213 Body: "ayJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19",
214 IntegratedTime: &time,
215 LogID: &logID,
216 LogIndex: swag.Int64(1),
217 Verification: &models.LogEntryAnonVerification{
218 InclusionProof: &models.InclusionProof{
219 TreeSize: swag.Int64(int64(2)),
220 RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"),
221 LogIndex: swag.Int64(1),
222 Hashes: []string{
223 "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c",
224 },
225 },
226 SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="),
227 },
228 },
229 wantErr: true,
230 },
231 } {
232 t.Run(string(test.name), func(t *testing.T) {
233 ctx := context.Background()
234
235 gotErr := VerifyInclusion(ctx, &test.e)
236
237 if (gotErr != nil) != test.wantErr {
238 t.Fatalf("VerifyInclusion = %t, wantErr %t", gotErr, test.wantErr)
239 }
240 })
241 }
242 }
243
244 func TestCheckpoint(t *testing.T) {
245 hostname := "rekor.localhost"
246 treeID := int64(123)
247 rootHash := sha256.Sum256([]byte{1, 2, 3})
248 rootHashString := hex.EncodeToString(rootHash[:])
249 treeSize := uint64(42)
250 signer, _, err := signature.NewDefaultECDSASignerVerifier()
251 if err != nil {
252 t.Fatalf("error generating signer: %v", err)
253 }
254 ctx := context.Background()
255 scBytes, err := util.CreateAndSignCheckpoint(ctx, hostname, treeID, treeSize, rootHash[:], signer)
256 if err != nil {
257 t.Fatalf("error creating signed checkpoint: %v", err)
258 }
259
260 time := int64(1661794812)
261 logID := "1701474e8cb504dbb853a5887bc2cf66936b0f36d2641bfb61f1abae80088e6a"
262
263 for _, test := range []struct {
264 name string
265 e models.LogEntryAnon
266 wantErr bool
267 }{
268 {
269 name: "valid checkpoint",
270 e: models.LogEntryAnon{
271 Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19",
272 IntegratedTime: &time,
273 LogID: &logID,
274 LogIndex: swag.Int64(1),
275 Verification: &models.LogEntryAnonVerification{
276 InclusionProof: &models.InclusionProof{
277 TreeSize: swag.Int64(int64(2)),
278 RootHash: swag.String(rootHashString),
279 LogIndex: swag.Int64(1),
280 Hashes: []string{
281 "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c",
282 },
283 Checkpoint: swag.String(string(scBytes)),
284 },
285 SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="),
286 },
287 },
288 wantErr: false,
289 },
290 {
291 name: "root hash mismatch",
292 e: models.LogEntryAnon{
293 Body: "ayJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19",
294 IntegratedTime: &time,
295 LogID: &logID,
296 LogIndex: swag.Int64(1),
297 Verification: &models.LogEntryAnonVerification{
298 InclusionProof: &models.InclusionProof{
299 TreeSize: swag.Int64(int64(2)),
300 RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"),
301 LogIndex: swag.Int64(1),
302 Hashes: []string{
303 "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c",
304 },
305 Checkpoint: swag.String(string(scBytes)),
306 },
307 SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="),
308 },
309 },
310 wantErr: true,
311 },
312 } {
313 t.Run(string(test.name), func(t *testing.T) {
314 gotErr := VerifyCheckpointSignature(&test.e, signer)
315
316 if (gotErr != nil) != test.wantErr {
317 t.Fatalf("VerifyCheckpointSignature = %t, wantErr %t", gotErr, test.wantErr)
318 }
319 })
320 }
321 }
322
View as plain text