1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package verify
17
18 import (
19 "bytes"
20 "context"
21 "encoding/base64"
22 "encoding/hex"
23 "encoding/json"
24 "errors"
25 "fmt"
26
27 "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
28 "github.com/sigstore/rekor/pkg/generated/client"
29 "github.com/sigstore/rekor/pkg/generated/client/tlog"
30 "github.com/sigstore/rekor/pkg/generated/models"
31 "github.com/sigstore/rekor/pkg/util"
32 "github.com/sigstore/sigstore/pkg/signature"
33 "github.com/sigstore/sigstore/pkg/signature/options"
34 "github.com/transparency-dev/merkle/proof"
35 "github.com/transparency-dev/merkle/rfc6962"
36 )
37
38
39
40 func ProveConsistency(ctx context.Context, rClient *client.Rekor,
41 oldSTH *util.SignedCheckpoint, newSTH *util.SignedCheckpoint, treeID string) error {
42 oldTreeSize := int64(oldSTH.Size)
43 switch {
44 case oldTreeSize == 0:
45 return errors.New("consistency proofs can not be computed starting from an empty log")
46 case oldTreeSize == int64(newSTH.Size):
47 if !bytes.Equal(oldSTH.Hash, newSTH.Hash) {
48 return errors.New("old root hash does not match STH hash")
49 }
50 case oldTreeSize < int64(newSTH.Size):
51 consistencyParams := tlog.NewGetLogProofParamsWithContext(ctx)
52 consistencyParams.FirstSize = &oldTreeSize
53 consistencyParams.LastSize = int64(newSTH.Size)
54 consistencyParams.TreeID = &treeID
55 consistencyProof, err := rClient.Tlog.GetLogProof(consistencyParams)
56 if err != nil {
57 return err
58 }
59 var hashes [][]byte
60 for _, h := range consistencyProof.Payload.Hashes {
61 b, err := hex.DecodeString(h)
62 if err != nil {
63 return errors.New("error decoding consistency proof hashes")
64 }
65 hashes = append(hashes, b)
66 }
67 if err := proof.VerifyConsistency(rfc6962.DefaultHasher,
68 oldSTH.Size, newSTH.Size, hashes, oldSTH.Hash, newSTH.Hash); err != nil {
69 return err
70 }
71 case oldTreeSize > int64(newSTH.Size):
72 return errors.New("inclusion proof returned a tree size larger than the verified tree size")
73 }
74 return nil
75
76 }
77
78
79
80
81 func VerifyCurrentCheckpoint(ctx context.Context, rClient *client.Rekor, verifier signature.Verifier,
82 oldSTH *util.SignedCheckpoint) (*util.SignedCheckpoint, error) {
83
84 if !oldSTH.Verify(verifier) {
85 return nil, errors.New("signature on old tree head did not verify")
86 }
87
88
89 infoParams := tlog.NewGetLogInfoParamsWithContext(ctx)
90 result, err := rClient.Tlog.GetLogInfo(infoParams)
91 if err != nil {
92 return nil, err
93 }
94
95 logInfo := result.GetPayload()
96 sth := util.SignedCheckpoint{}
97 if err := sth.UnmarshalText([]byte(*logInfo.SignedTreeHead)); err != nil {
98 return nil, err
99 }
100
101
102 if !sth.Verify(verifier) {
103 return nil, errors.New("signature on tree head did not verify")
104 }
105
106
107 if err := ProveConsistency(ctx, rClient, oldSTH, &sth, *logInfo.TreeID); err != nil {
108 return nil, err
109 }
110 return &sth, nil
111 }
112
113
114
115
116 func VerifyCheckpointSignature(e *models.LogEntryAnon, verifier signature.Verifier) error {
117 sth := &util.SignedCheckpoint{}
118 if err := sth.UnmarshalText([]byte(*e.Verification.InclusionProof.Checkpoint)); err != nil {
119 return fmt.Errorf("unmarshalling log entry checkpoint to SignedCheckpoint: %w", err)
120 }
121 if !sth.Verify(verifier) {
122 return errors.New("signature on checkpoint did not verify")
123 }
124 rootHash, err := hex.DecodeString(*e.Verification.InclusionProof.RootHash)
125 if err != nil {
126 return errors.New("decoding inclusion proof root has")
127 }
128
129 if !bytes.EqualFold(rootHash, sth.Hash) {
130 return fmt.Errorf("proof root hash does not match signed tree head, expected %s got %s",
131 *e.Verification.InclusionProof.RootHash,
132 hex.EncodeToString(sth.Hash))
133 }
134 return nil
135 }
136
137
138
139
140
141 func VerifyInclusion(ctx context.Context, e *models.LogEntryAnon) error {
142 if e.Verification == nil || e.Verification.InclusionProof == nil {
143 return errors.New("inclusion proof not provided")
144 }
145
146 hashes := [][]byte{}
147 for _, h := range e.Verification.InclusionProof.Hashes {
148 hb, _ := hex.DecodeString(h)
149 hashes = append(hashes, hb)
150 }
151
152 rootHash, err := hex.DecodeString(*e.Verification.InclusionProof.RootHash)
153 if err != nil {
154 return err
155 }
156
157
158 entryBytes, err := base64.StdEncoding.DecodeString(e.Body.(string))
159 if err != nil {
160 return err
161 }
162 leafHash := rfc6962.DefaultHasher.HashLeaf(entryBytes)
163
164 if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(*e.Verification.InclusionProof.LogIndex),
165 uint64(*e.Verification.InclusionProof.TreeSize), leafHash, hashes, rootHash); err != nil {
166 return err
167 }
168
169 return nil
170 }
171
172
173
174
175 func VerifySignedEntryTimestamp(ctx context.Context, e *models.LogEntryAnon, verifier signature.Verifier) error {
176 if e.Verification == nil {
177 return errors.New("missing verification")
178 }
179 if e.Verification.SignedEntryTimestamp == nil {
180 return errors.New("signature missing")
181 }
182
183 type bundle struct {
184 Body interface{} `json:"body"`
185 IntegratedTime int64 `json:"integratedTime"`
186
187 LogIndex int64 `json:"logIndex"`
188 LogID string `json:"logID"`
189 }
190 bundlePayload := bundle{
191 Body: e.Body,
192 IntegratedTime: *e.IntegratedTime,
193 LogIndex: *e.LogIndex,
194 LogID: *e.LogID,
195 }
196 contents, err := json.Marshal(bundlePayload)
197 if err != nil {
198 return fmt.Errorf("marshaling bundle: %w", err)
199 }
200 canonicalized, err := jsoncanonicalizer.Transform(contents)
201 if err != nil {
202 return fmt.Errorf("canonicalizing bundle: %w", err)
203 }
204
205
206 if err := verifier.VerifySignature(bytes.NewReader(e.Verification.SignedEntryTimestamp),
207 bytes.NewReader(canonicalized), options.WithContext(ctx)); err != nil {
208 return fmt.Errorf("unable to verify bundle: %w", err)
209 }
210 return nil
211 }
212
213
214
215
216
217 func VerifyLogEntry(ctx context.Context, e *models.LogEntryAnon, verifier signature.Verifier) error {
218
219 if err := VerifyInclusion(ctx, e); err != nil {
220 return err
221 }
222
223
224 if err := VerifyCheckpointSignature(e, verifier); err != nil {
225 return err
226 }
227
228
229 if err := VerifySignedEntryTimestamp(ctx, e, verifier); err != nil {
230 return err
231 }
232
233 return nil
234 }
235
View as plain text