1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package api
17
18 import (
19 "bytes"
20 "context"
21 "encoding/hex"
22 "errors"
23 "fmt"
24 "net/http"
25 "net/url"
26 "strconv"
27 "time"
28
29 "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
30 "github.com/go-openapi/runtime"
31 "github.com/go-openapi/runtime/middleware"
32 "github.com/go-openapi/strfmt"
33 "github.com/go-openapi/swag"
34 "github.com/google/trillian"
35 ttypes "github.com/google/trillian/types"
36 "github.com/spf13/viper"
37 "github.com/transparency-dev/merkle/rfc6962"
38 "google.golang.org/genproto/googleapis/rpc/code"
39 "google.golang.org/grpc/codes"
40
41 "github.com/sigstore/rekor/pkg/events"
42 "github.com/sigstore/rekor/pkg/events/newentry"
43 "github.com/sigstore/rekor/pkg/generated/models"
44 "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries"
45 "github.com/sigstore/rekor/pkg/log"
46 "github.com/sigstore/rekor/pkg/pubsub"
47 "github.com/sigstore/rekor/pkg/sharding"
48 "github.com/sigstore/rekor/pkg/tle"
49 "github.com/sigstore/rekor/pkg/trillianclient"
50 "github.com/sigstore/rekor/pkg/types"
51 "github.com/sigstore/rekor/pkg/util"
52 "github.com/sigstore/sigstore/pkg/signature"
53 "github.com/sigstore/sigstore/pkg/signature/options"
54 )
55
56 const (
57 maxSearchQueries = 10
58 )
59
60 func signEntry(ctx context.Context, signer signature.Signer, entry models.LogEntryAnon) ([]byte, error) {
61 payload, err := entry.MarshalBinary()
62 if err != nil {
63 return nil, fmt.Errorf("marshalling error: %w", err)
64 }
65 canonicalized, err := jsoncanonicalizer.Transform(payload)
66 if err != nil {
67 return nil, fmt.Errorf("canonicalizing error: %w", err)
68 }
69 signature, err := signer.SignMessage(bytes.NewReader(canonicalized), options.WithContext(ctx))
70 if err != nil {
71 return nil, fmt.Errorf("signing error: %w", err)
72 }
73 return signature, nil
74 }
75
76
77 func logEntryFromLeaf(ctx context.Context, signer signature.Signer, _ trillianclient.TrillianClient, leaf *trillian.LogLeaf,
78 signedLogRoot *trillian.SignedLogRoot, proof *trillian.Proof, tid int64, ranges sharding.LogRanges) (models.LogEntry, error) {
79
80 log.ContextLogger(ctx).Debugf("log entry from leaf %d", leaf.GetLeafIndex())
81 root := &ttypes.LogRootV1{}
82 if err := root.UnmarshalBinary(signedLogRoot.LogRoot); err != nil {
83 return nil, err
84 }
85 hashes := []string{}
86 for _, hash := range proof.Hashes {
87 hashes = append(hashes, hex.EncodeToString(hash))
88 }
89
90 virtualIndex := sharding.VirtualLogIndex(leaf.GetLeafIndex(), tid, ranges)
91 logEntryAnon := models.LogEntryAnon{
92 LogID: swag.String(api.pubkeyHash),
93 LogIndex: &virtualIndex,
94 Body: leaf.LeafValue,
95 IntegratedTime: swag.Int64(leaf.IntegrateTimestamp.AsTime().Unix()),
96 }
97
98 signature, err := signEntry(ctx, signer, logEntryAnon)
99 if err != nil {
100 return nil, fmt.Errorf("signing entry error: %w", err)
101 }
102
103 scBytes, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), tid, root.TreeSize, root.RootHash, api.signer)
104 if err != nil {
105 return nil, err
106 }
107
108 inclusionProof := models.InclusionProof{
109 TreeSize: swag.Int64(int64(root.TreeSize)),
110 RootHash: swag.String(hex.EncodeToString(root.RootHash)),
111 LogIndex: swag.Int64(proof.GetLeafIndex()),
112 Hashes: hashes,
113 Checkpoint: stringPointer(string(scBytes)),
114 }
115
116 uuid := hex.EncodeToString(leaf.MerkleLeafHash)
117 treeID := fmt.Sprintf("%x", tid)
118 entryIDstruct, err := sharding.CreateEntryIDFromParts(treeID, uuid)
119 if err != nil {
120 return nil, fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", treeID, uuid, err)
121 }
122 entryID := entryIDstruct.ReturnEntryIDString()
123
124 if viper.GetBool("enable_attestation_storage") {
125 pe, err := models.UnmarshalProposedEntry(bytes.NewReader(leaf.LeafValue), runtime.JSONConsumer())
126 if err != nil {
127 return nil, err
128 }
129 eimpl, err := types.UnmarshalEntry(pe)
130 if err != nil {
131 return nil, err
132 }
133
134 if entryWithAtt, ok := eimpl.(types.EntryWithAttestationImpl); ok {
135 var att []byte
136 var fetchErr error
137 attKey := entryWithAtt.AttestationKey()
138
139 if attKey != "" {
140 att, fetchErr = attestationStorageClient.FetchAttestation(ctx, attKey)
141 if fetchErr != nil {
142 log.ContextLogger(ctx).Debugf("error fetching attestation by key, trying by UUID: %s %v", attKey, fetchErr)
143 }
144 }
145
146 if attKey == "" || fetchErr != nil {
147 att, fetchErr = attestationStorageClient.FetchAttestation(ctx, entryIDstruct.UUID)
148 if fetchErr != nil {
149 log.ContextLogger(ctx).Debugf("error fetching attestation by uuid: %s %v", entryIDstruct.UUID, fetchErr)
150 }
151 }
152 if fetchErr == nil {
153 logEntryAnon.Attestation = &models.LogEntryAnonAttestation{
154 Data: att,
155 }
156 }
157 }
158 }
159
160 logEntryAnon.Verification = &models.LogEntryAnonVerification{
161 InclusionProof: &inclusionProof,
162 SignedEntryTimestamp: strfmt.Base64(signature),
163 }
164
165 return models.LogEntry{
166 entryID: logEntryAnon}, nil
167 }
168
169
170 func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middleware.Responder {
171 ctx := params.HTTPRequest.Context()
172 logEntry, err := retrieveLogEntryByIndex(ctx, int(params.LogIndex))
173 if err != nil {
174 if errors.Is(err, ErrNotFound) {
175 return handleRekorAPIError(params, http.StatusNotFound, fmt.Errorf("grpc error: %w", err), "")
176 }
177 return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error())
178 }
179 return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry)
180 }
181
182 func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) {
183 ctx := params.HTTPRequest.Context()
184 entry, err := types.CreateVersionedEntry(params.ProposedEntry)
185 if err != nil {
186 return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
187 }
188 leaf, err := types.CanonicalizeEntry(ctx, entry)
189 if err != nil {
190 if _, ok := (err).(types.ValidationError); ok {
191 return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
192 }
193 return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalEntry)
194 }
195
196 tc := trillianclient.NewTrillianClient(ctx, api.logClient, api.logID)
197
198 resp := tc.AddLeaf(leaf)
199
200 if resp.Status != codes.OK {
201 return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.Err), trillianUnexpectedResult)
202 }
203
204
205 insertionStatus := resp.GetAddResult.QueuedLeaf.Status
206 if insertionStatus != nil {
207 switch insertionStatus.Code {
208 case int32(code.Code_OK):
209 case int32(code.Code_ALREADY_EXISTS), int32(code.Code_FAILED_PRECONDITION):
210 existingUUID := hex.EncodeToString(rfc6962.DefaultHasher.HashLeaf(leaf))
211 activeTree := fmt.Sprintf("%x", api.logID)
212 entryIDstruct, err := sharding.CreateEntryIDFromParts(activeTree, existingUUID)
213 if err != nil {
214 err := fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", activeTree, existingUUID, err)
215 return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf(validationError, err))
216 }
217 existingEntryID := entryIDstruct.ReturnEntryIDString()
218 err = fmt.Errorf("grpc error: %v", insertionStatus.String())
219 return nil, handleRekorAPIError(params, http.StatusConflict, err, fmt.Sprintf(entryAlreadyExists, existingEntryID), "entryURL", getEntryURL(*params.HTTPRequest.URL, existingEntryID))
220 default:
221 err := fmt.Errorf("grpc error: %v", insertionStatus.String())
222 return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult)
223 }
224 }
225
226
227 metricNewEntries.Inc()
228
229 queuedLeaf := resp.GetAddResult.QueuedLeaf.Leaf
230
231 uuid := hex.EncodeToString(queuedLeaf.GetMerkleLeafHash())
232 activeTree := fmt.Sprintf("%x", api.logID)
233 entryIDstruct, err := sharding.CreateEntryIDFromParts(activeTree, uuid)
234 if err != nil {
235 err := fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", activeTree, uuid, err)
236 return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf(validationError, err))
237 }
238 entryID := entryIDstruct.ReturnEntryIDString()
239
240
241 virtualIndex := sharding.VirtualLogIndex(queuedLeaf.LeafIndex, api.logRanges.ActiveTreeID(), api.logRanges)
242 logEntryAnon := models.LogEntryAnon{
243 LogID: swag.String(api.pubkeyHash),
244 LogIndex: swag.Int64(virtualIndex),
245 Body: queuedLeaf.GetLeafValue(),
246 IntegratedTime: swag.Int64(queuedLeaf.IntegrateTimestamp.AsTime().Unix()),
247 }
248
249 if indexStorageClient != nil {
250 go func() {
251 start := time.Now()
252 var err error
253 defer func() {
254 labels := map[string]string{
255 "success": strconv.FormatBool(err == nil),
256 }
257 metricIndexStorageLatency.With(labels).Observe(float64(time.Since(start)))
258 }()
259 keys, err := entry.IndexKeys()
260 if err != nil {
261 log.ContextLogger(ctx).Errorf("getting entry index keys: %v", err)
262 return
263 }
264 if err := addToIndex(context.Background(), keys, entryID); err != nil {
265 log.ContextLogger(ctx).Errorf("adding keys to index: %v", err)
266 }
267 }()
268 }
269
270 if viper.GetBool("enable_attestation_storage") {
271 if entryWithAtt, ok := entry.(types.EntryWithAttestationImpl); ok {
272 attKey, attVal := entryWithAtt.AttestationKeyValue()
273 if attVal != nil {
274 go func() {
275 if err := storeAttestation(context.Background(), attKey, attVal); err != nil {
276
277 log.ContextLogger(ctx).Debugf("error storing attestation: %s", err)
278 } else {
279 log.ContextLogger(ctx).Debugf("stored attestation for uuid %s with filename %s", entryIDstruct.UUID, attKey)
280 }
281 }()
282 } else {
283 log.ContextLogger(ctx).Infof("no attestation returned for %s", uuid)
284 }
285 }
286 }
287
288 signature, err := signEntry(ctx, api.signer, logEntryAnon)
289 if err != nil {
290 return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing entry error: %w", err), signingError)
291 }
292
293 root := &ttypes.LogRootV1{}
294 if err := root.UnmarshalBinary(resp.GetLeafAndProofResult.SignedLogRoot.LogRoot); err != nil {
295 return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error unmarshalling log root: %w", err), sthGenerateError)
296 }
297 hashes := []string{}
298 for _, hash := range resp.GetLeafAndProofResult.Proof.Hashes {
299 hashes = append(hashes, hex.EncodeToString(hash))
300 }
301
302 scBytes, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), api.logID, root.TreeSize, root.RootHash, api.signer)
303 if err != nil {
304 return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, sthGenerateError)
305 }
306
307 inclusionProof := models.InclusionProof{
308 TreeSize: swag.Int64(int64(root.TreeSize)),
309 RootHash: swag.String(hex.EncodeToString(root.RootHash)),
310 LogIndex: swag.Int64(queuedLeaf.LeafIndex),
311 Hashes: hashes,
312 Checkpoint: swag.String(string(scBytes)),
313 }
314
315 logEntryAnon.Verification = &models.LogEntryAnonVerification{
316 InclusionProof: &inclusionProof,
317 SignedEntryTimestamp: strfmt.Base64(signature),
318 }
319
320 logEntry := models.LogEntry{
321 entryID: logEntryAnon,
322 }
323
324 if api.newEntryPublisher != nil {
325
326 go func() {
327 verifiers, err := entry.Verifiers()
328 if err != nil {
329 incPublishEvent(newentry.Name, "", false)
330 log.ContextLogger(ctx).Errorf("Could not get verifiers for log entry %s: %v", entryID, err)
331 return
332 }
333 var subjects []string
334 for _, v := range verifiers {
335 subjects = append(subjects, v.Subjects()...)
336 }
337
338 pbEntry, err := tle.GenerateTransparencyLogEntry(logEntryAnon)
339 if err != nil {
340 incPublishEvent(newentry.Name, "", false)
341 log.ContextLogger(ctx).Error(err)
342 return
343 }
344 event, err := newentry.New(entryID, pbEntry, subjects)
345 if err != nil {
346 incPublishEvent(newentry.Name, "", false)
347 log.ContextLogger(ctx).Error(err)
348 return
349 }
350 if viper.GetBool("rekor_server.publish_events_protobuf") {
351 go publishEvent(ctx, api.newEntryPublisher, event, events.ContentTypeProtobuf)
352 }
353 if viper.GetBool("rekor_server.publish_events_json") {
354 go publishEvent(ctx, api.newEntryPublisher, event, events.ContentTypeJSON)
355 }
356 }()
357 }
358
359 return logEntry, nil
360 }
361
362 func publishEvent(ctx context.Context, publisher pubsub.Publisher, event *events.Event, contentType events.EventContentType) {
363 err := publisher.Publish(context.WithoutCancel(ctx), event, contentType)
364 incPublishEvent(event.Type().Name(), contentType, err == nil)
365 if err != nil {
366 log.ContextLogger(ctx).Error(err)
367 }
368 }
369
370 func incPublishEvent(event string, contentType events.EventContentType, ok bool) {
371 status := "SUCCESS"
372 if !ok {
373 status = "ERROR"
374 }
375 labels := map[string]string{
376 "event": event,
377 "status": status,
378 "content_type": string(contentType),
379 }
380 metricPublishEvents.With(labels).Inc()
381 }
382
383
384 func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder {
385 httpReq := params.HTTPRequest
386
387 logEntry, err := createLogEntry(params)
388 if err != nil {
389 return err
390 }
391
392 var uuid string
393 for location := range logEntry {
394 uuid = location
395 }
396
397 return entries.NewCreateLogEntryCreated().WithPayload(logEntry).WithLocation(getEntryURL(*httpReq.URL, uuid)).WithETag(uuid)
398 }
399
400
401 func getEntryURL(locationURL url.URL, uuid string) strfmt.URI {
402
403 query := locationURL.Query()
404 query.Del("apiKey")
405 locationURL.RawQuery = query.Encode()
406 locationURL.Path = fmt.Sprintf("%v/%v", locationURL.Path, uuid)
407 return strfmt.URI(locationURL.String())
408
409 }
410
411
412 func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware.Responder {
413 logEntry, err := retrieveLogEntry(params.HTTPRequest.Context(), params.EntryUUID)
414 if err != nil {
415 if errors.Is(err, ErrNotFound) {
416 return handleRekorAPIError(params, http.StatusNotFound, err, "")
417 }
418 if _, ok := (err).(types.ValidationError); ok {
419 return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("incorrectly formatted uuid %s", params.EntryUUID), params.EntryUUID)
420 }
421 return handleRekorAPIError(params, http.StatusInternalServerError, err, trillianCommunicationError)
422 }
423 return entries.NewGetLogEntryByUUIDOK().WithPayload(logEntry)
424 }
425
426
427 func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Responder {
428 httpReqCtx := params.HTTPRequest.Context()
429 resultPayload := []models.LogEntry{}
430
431 totalQueries := len(params.Entry.EntryUUIDs) + len(params.Entry.Entries()) + len(params.Entry.LogIndexes)
432 if totalQueries > maxSearchQueries {
433 return handleRekorAPIError(params, http.StatusUnprocessableEntity, fmt.Errorf(maxSearchQueryLimit, maxSearchQueries), fmt.Sprintf(maxSearchQueryLimit, maxSearchQueries))
434 }
435
436 if len(params.Entry.EntryUUIDs) > 0 || len(params.Entry.Entries()) > 0 {
437 var searchHashes [][]byte
438 for _, entryID := range params.Entry.EntryUUIDs {
439
440 err := sharding.ValidateEntryID(entryID)
441 if err == nil {
442 logEntry, err := retrieveLogEntry(httpReqCtx, entryID)
443 if err != nil && !errors.Is(err, ErrNotFound) {
444 return handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf("error getting log entry for %s", entryID))
445 } else if err == nil {
446 resultPayload = append(resultPayload, logEntry)
447 }
448 continue
449 } else if len(entryID) == sharding.EntryIDHexStringLen {
450
451 return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("invalid entryID %s", entryID))
452 }
453
454 uuid := entryID
455 if err := sharding.ValidateUUID(uuid); err != nil {
456 return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("invalid uuid %s", uuid))
457 }
458 hash, err := hex.DecodeString(uuid)
459 if err != nil {
460 return handleRekorAPIError(params, http.StatusBadRequest, err, malformedUUID)
461 }
462 searchHashes = append(searchHashes, hash)
463 }
464
465 entries := params.Entry.Entries()
466 for _, e := range entries {
467 entry, err := types.UnmarshalEntry(e)
468 if err != nil {
469 return handleRekorAPIError(params, http.StatusBadRequest, fmt.Errorf("unmarshalling entry: %w", err), err.Error())
470 }
471
472 leaf, err := types.CanonicalizeEntry(httpReqCtx, entry)
473 if err != nil {
474 return handleRekorAPIError(params, http.StatusBadRequest, fmt.Errorf("canonicalizing entry: %w", err), err.Error())
475 }
476 hasher := rfc6962.DefaultHasher
477 leafHash := hasher.HashLeaf(leaf)
478 searchHashes = append(searchHashes, leafHash)
479 }
480
481 searchByHashResults := make([]map[int64]*trillian.GetEntryAndProofResponse, len(searchHashes))
482 for i, hash := range searchHashes {
483 var results map[int64]*trillian.GetEntryAndProofResponse
484 for _, shard := range api.logRanges.AllShards() {
485 tcs := trillianclient.NewTrillianClient(httpReqCtx, api.logClient, shard)
486 resp := tcs.GetLeafAndProofByHash(hash)
487 switch resp.Status {
488 case codes.OK:
489 leafResult := resp.GetLeafAndProofResult
490 if leafResult != nil && leafResult.Leaf != nil {
491 if results == nil {
492 results = map[int64]*trillian.GetEntryAndProofResponse{}
493 }
494 results[shard] = resp.GetLeafAndProofResult
495 }
496 case codes.NotFound:
497
498 continue
499 default:
500 return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error getLeafAndProofByHash(%s): code: %v, msg %v", hex.EncodeToString(hash), resp.Status, resp.Err), trillianCommunicationError)
501 }
502 }
503 searchByHashResults[i] = results
504 }
505
506 for _, hashMap := range searchByHashResults {
507 for shard, leafResp := range hashMap {
508 if leafResp == nil {
509 continue
510 }
511 tcs := trillianclient.NewTrillianClient(httpReqCtx, api.logClient, shard)
512 logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tcs, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges)
513 if err != nil {
514 return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error())
515 }
516 resultPayload = append(resultPayload, logEntry)
517 }
518 }
519 }
520
521 if len(params.Entry.LogIndexes) > 0 {
522 for _, logIndex := range params.Entry.LogIndexes {
523 logEntry, err := retrieveLogEntryByIndex(httpReqCtx, int(swag.Int64Value(logIndex)))
524 if err != nil && !errors.Is(err, ErrNotFound) {
525 return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error())
526 } else if err == nil {
527 resultPayload = append(resultPayload, logEntry)
528 }
529 }
530 }
531
532 return entries.NewSearchLogQueryOK().WithPayload(resultPayload)
533 }
534
535 var ErrNotFound = errors.New("grpc returned 0 leaves with success code")
536
537 func retrieveLogEntryByIndex(ctx context.Context, logIndex int) (models.LogEntry, error) {
538 log.ContextLogger(ctx).Infof("Retrieving log entry by index %d", logIndex)
539
540 tid, resolvedIndex := api.logRanges.ResolveVirtualIndex(logIndex)
541 tc := trillianclient.NewTrillianClient(ctx, api.logClient, tid)
542 log.ContextLogger(ctx).Debugf("Retrieving resolved index %v from TreeID %v", resolvedIndex, tid)
543
544 resp := tc.GetLeafAndProofByIndex(resolvedIndex)
545 switch resp.Status {
546 case codes.OK:
547 case codes.NotFound, codes.OutOfRange, codes.InvalidArgument:
548 return models.LogEntry{}, ErrNotFound
549 default:
550 return models.LogEntry{}, fmt.Errorf("grpc err: %w: %s", resp.Err, trillianCommunicationError)
551 }
552
553 result := resp.GetLeafAndProofResult
554 leaf := result.Leaf
555 if leaf == nil {
556 return models.LogEntry{}, ErrNotFound
557 }
558
559 return logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges)
560 }
561
562
563
564
565 func retrieveLogEntry(ctx context.Context, entryUUID string) (models.LogEntry, error) {
566 log.ContextLogger(ctx).Debugf("Retrieving log entry %v", entryUUID)
567
568 uuid, err := sharding.GetUUIDFromIDString(entryUUID)
569 if err != nil {
570 return nil, sharding.ErrPlainUUID
571 }
572
573
574 tid, err := sharding.TreeID(entryUUID)
575 if err == nil {
576 return retrieveUUIDFromTree(ctx, uuid, tid)
577 }
578
579
580 if errors.Is(err, sharding.ErrPlainUUID) {
581 trees := []sharding.LogRange{{TreeID: api.logRanges.ActiveTreeID()}}
582 trees = append(trees, api.logRanges.GetInactive()...)
583
584 for _, t := range trees {
585 logEntry, err := retrieveUUIDFromTree(ctx, uuid, t.TreeID)
586 if err != nil {
587 if errors.Is(err, ErrNotFound) {
588 continue
589 }
590 return nil, err
591 }
592 return logEntry, nil
593 }
594 return nil, ErrNotFound
595 }
596
597 return nil, err
598 }
599
600 func retrieveUUIDFromTree(ctx context.Context, uuid string, tid int64) (models.LogEntry, error) {
601 log.ContextLogger(ctx).Debugf("Retrieving log entry %v from tree %d", uuid, tid)
602
603 hashValue, err := hex.DecodeString(uuid)
604 if err != nil {
605 return models.LogEntry{}, types.ValidationError(err)
606 }
607
608 tc := trillianclient.NewTrillianClient(ctx, api.logClient, tid)
609 log.ContextLogger(ctx).Debugf("Attempting to retrieve UUID %v from TreeID %v", uuid, tid)
610
611 resp := tc.GetLeafAndProofByHash(hashValue)
612 switch resp.Status {
613 case codes.OK:
614 result := resp.GetLeafAndProofResult
615 if resp.Err != nil {
616
617
618 if result.Leaf == nil {
619 return models.LogEntry{}, ErrNotFound
620 }
621 return models.LogEntry{}, err
622 }
623
624 logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, result.Leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges)
625 if err != nil {
626 return models.LogEntry{}, fmt.Errorf("could not create log entry from leaf: %w", err)
627 }
628 return logEntry, nil
629
630 case codes.NotFound:
631 return models.LogEntry{}, ErrNotFound
632 default:
633 log.ContextLogger(ctx).Errorf("Unexpected response code while attempting to retrieve UUID %v from TreeID %v: %v", uuid, tid, resp.Status)
634 return models.LogEntry{}, errors.New("unexpected error")
635 }
636 }
637
638
639
640 func CreateLogEntryNotImplementedHandler(_ entries.CreateLogEntryParams) middleware.Responder {
641 err := &models.Error{
642 Code: http.StatusNotImplemented,
643 Message: "Create Entry API not enabled in this Rekor instance",
644 }
645
646 return entries.NewCreateLogEntryDefault(http.StatusNotImplemented).WithPayload(err)
647 }
648
649 func GetLogEntryByIndexNotImplementedHandler(_ entries.GetLogEntryByIndexParams) middleware.Responder {
650 err := &models.Error{
651 Code: http.StatusNotImplemented,
652 Message: "Get Log Entry by Index API not enabled in this Rekor instance",
653 }
654
655 return entries.NewGetLogEntryByIndexDefault(http.StatusNotImplemented).WithPayload(err)
656 }
657
658 func GetLogEntryByUUIDNotImplementedHandler(_ entries.GetLogEntryByUUIDParams) middleware.Responder {
659 err := &models.Error{
660 Code: http.StatusNotImplemented,
661 Message: "Get Log Entry by UUID API not enabled in this Rekor instance",
662 }
663
664 return entries.NewGetLogEntryByUUIDDefault(http.StatusNotImplemented).WithPayload(err)
665 }
666
667 func SearchLogQueryNotImplementedHandler(_ entries.SearchLogQueryParams) middleware.Responder {
668 err := &models.Error{
669 Code: http.StatusNotImplemented,
670 Message: "Search Log Query API not enabled in this Rekor instance",
671 }
672
673 return entries.NewSearchLogQueryDefault(http.StatusNotImplemented).WithPayload(err)
674 }
675
View as plain text