Skip to content

Commit 34caf45

Browse files
authored
Upload generated timestamps (sigstore#336)
* upload all generated timestamps Signed-off-by: Asra Ali <asraa@google.com> * address bazooka comments Signed-off-by: Asra Ali <asraa@google.com> * simplify params Signed-off-by: Asra Ali <asraa@google.com> * address changes Signed-off-by: Asra Ali <asraa@google.com> * forgot to run swagger Signed-off-by: Asra Ali <asraa@google.com>
1 parent 0882cde commit 34caf45

File tree

14 files changed

+334
-94
lines changed

14 files changed

+334
-94
lines changed

cmd/rekor-cli/app/pflags.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -250,27 +250,13 @@ func CreateIntotoFromPFlags() (models.ProposedEntry, error) {
250250

251251
func CreateRFC3161FromPFlags() (models.ProposedEntry, error) {
252252
//TODO: how to select version of item to create
253-
returnVal := models.Rfc3161{}
254-
255253
rfc3161 := viper.GetString("artifact")
256254
b, err := ioutil.ReadFile(filepath.Clean(rfc3161))
257255
if err != nil {
258256
return nil, fmt.Errorf("error reading public key file: %w", err)
259257
}
260258

261-
b64 := strfmt.Base64(b)
262-
re := rfc3161_v001.V001Entry{
263-
Rfc3161Obj: models.Rfc3161V001Schema{
264-
Tsr: &models.Rfc3161V001SchemaTsr{
265-
Content: &b64,
266-
},
267-
},
268-
}
269-
270-
returnVal.Spec = re.Rfc3161Obj
271-
returnVal.APIVersion = swag.String(re.APIVersion())
272-
273-
return &returnVal, nil
259+
return rfc3161_v001.NewEntryFromBytes(b), nil
274260
}
275261

276262
func CreateRpmFromPFlags() (models.ProposedEntry, error) {

cmd/rekor-cli/app/timestamp.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"path/filepath"
2828
"strconv"
2929
"strings"
30+
"time"
3031

3132
"github.com/sassoftware/relic/lib/pkcs9"
3233
"github.com/sassoftware/relic/lib/x509tools"
@@ -94,7 +95,6 @@ func addTimestampFlags(cmd *cobra.Command) error {
9495

9596
cmd.Flags().String("out", "response.tsr", "path to a file to write response.")
9697

97-
// TODO: Add a flag to upload a pre-formed timestamp response to the log.
9898
// TODO: Add a flag to indicate a JSON formatted timestamp request/response.
9999
return nil
100100
}
@@ -175,13 +175,15 @@ func createRequestFromFlags() (*pkcs9.TimeStampReq, error) {
175175
}
176176

177177
type timestampCmdOutput struct {
178-
Location string
178+
Timestamp time.Time
179+
Location string
180+
UUID string
181+
Index int64
179182
}
180183

181184
func (t *timestampCmdOutput) String() string {
182-
return fmt.Sprintf(`
183-
Wrote response to: %v
184-
`, t.Location)
185+
return fmt.Sprintf("Artifact timestamped at %s\nWrote timestamp response to %v\nCreated entry at index %d, available at: %v%v\n",
186+
t.Timestamp, t.Location, t.Index, viper.GetString("rekor_server"), t.UUID)
185187
}
186188

187189
var timestampCmd = &cobra.Command{
@@ -218,12 +220,17 @@ var timestampCmd = &cobra.Command{
218220
params.Request = ioutil.NopCloser(bytes.NewReader(requestBytes))
219221

220222
var respBytes bytes.Buffer
221-
_, err = rekorClient.Timestamp.GetTimestampResponse(params, &respBytes)
223+
resp, err := rekorClient.Timestamp.GetTimestampResponse(params, &respBytes)
222224
if err != nil {
223225
return nil, err
224226
}
225227
// Sanity check response and check if the TimeStampToken was successfully created
226-
if _, err = timestampReq.ParseResponse(respBytes.Bytes()); err != nil {
228+
psd, err := timestampReq.ParseResponse(respBytes.Bytes())
229+
if err != nil {
230+
return nil, err
231+
}
232+
genTime, err := util.GetSigningTime(psd)
233+
if err != nil {
227234
return nil, err
228235
}
229236

@@ -236,9 +243,11 @@ var timestampCmd = &cobra.Command{
236243
return nil, err
237244
}
238245

239-
// TODO: Add log index after support for uploading to transparency log is added.
240246
return &timestampCmdOutput{
241-
Location: outStr,
247+
Location: outStr,
248+
UUID: string(resp.Location),
249+
Timestamp: genTime.Round(time.Second),
250+
Index: resp.Index,
242251
}, nil
243252
}),
244253
}

openapi.yaml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@ paths:
227227

228228
/api/v1/timestamp:
229229
post:
230-
# TODO: Add uploads to transparency log.
231-
summary: Returns a timestamp response generated by Rekor
230+
summary: Generates a new timestamp response and creates a new log entry for the timestamp in the transparency log
232231
operationId: getTimestampResponse
233232
tags:
234233
- timestamp
@@ -244,11 +243,22 @@ paths:
244243
type: string
245244
format: binary
246245
responses:
247-
200:
248-
description: Returns a timestamp response
246+
201:
247+
description: Returns a timestamp response and the location of the log entry in the transprency log
249248
schema:
250249
type: string
251250
format: binary
251+
headers:
252+
ETag:
253+
type: string
254+
description: UUID of the log entry made for the timestamp response
255+
Location:
256+
type: string
257+
description: URI location of the log entry made for the timestamp response
258+
format: uri
259+
Index:
260+
type: integer
261+
description: Log index of the log entry made for the timestamp response
252262
400:
253263
$ref: '#/responses/BadContent'
254264
501:

pkg/api/entries.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,26 +127,23 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa
127127
return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry)
128128
}
129129

130-
// CreateLogEntryHandler creates new entry into log
131-
func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder {
130+
func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) {
132131
ctx := params.HTTPRequest.Context()
133-
httpReq := params.HTTPRequest
134132
entry, err := types.NewEntry(params.ProposedEntry)
135133
if err != nil {
136-
return handleRekorAPIError(params, http.StatusBadRequest, err, err.Error())
134+
return nil, handleRekorAPIError(params, http.StatusBadRequest, err, err.Error())
137135
}
138-
139-
leaf, err := entry.Canonicalize(httpReq.Context())
136+
leaf, err := entry.Canonicalize(ctx)
140137
if err != nil {
141-
return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalEntry)
138+
return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalEntry)
142139
}
143140

144-
tc := NewTrillianClient(httpReq.Context())
141+
tc := NewTrillianClient(ctx)
145142

146143
resp := tc.addLeaf(leaf)
147144
// this represents overall GRPC response state (not the results of insertion into the log)
148145
if resp.status != codes.OK {
149-
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.err), trillianUnexpectedResult)
146+
return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.err), trillianUnexpectedResult)
150147
}
151148

152149
// this represents the results of inserting the proposed leaf into the log; status is nil in success path
@@ -156,9 +153,11 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo
156153
case int32(code.Code_OK):
157154
case int32(code.Code_ALREADY_EXISTS), int32(code.Code_FAILED_PRECONDITION):
158155
existingUUID := hex.EncodeToString(rfc6962.DefaultHasher.HashLeaf(leaf))
159-
return handleRekorAPIError(params, http.StatusConflict, fmt.Errorf("grpc error: %v", insertionStatus.String()), fmt.Sprintf(entryAlreadyExists, existingUUID), "entryURL", getEntryURL(*httpReq.URL, existingUUID))
156+
err := fmt.Errorf("grpc error: %v", insertionStatus.String())
157+
return nil, handleRekorAPIError(params, http.StatusConflict, err, fmt.Sprintf(entryAlreadyExists, existingUUID), "entryURL", getEntryURL(*params.HTTPRequest.URL, existingUUID))
160158
default:
161-
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %v", insertionStatus.String()), trillianUnexpectedResult)
159+
err := fmt.Errorf("grpc error: %v", insertionStatus.String())
160+
return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult)
162161
}
163162
}
164163

@@ -201,7 +200,7 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo
201200

202201
signature, err := signEntry(ctx, api.signer, logEntryAnon)
203202
if err != nil {
204-
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing entry error: %v", err), signingError)
203+
return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing entry error: %v", err), signingError)
205204
}
206205

207206
logEntryAnon.Verification = &models.LogEntryAnonVerification{
@@ -211,6 +210,22 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo
211210
logEntry := models.LogEntry{
212211
uuid: logEntryAnon,
213212
}
213+
return logEntry, nil
214+
}
215+
216+
// CreateLogEntryHandler creates new entry into log
217+
func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder {
218+
httpReq := params.HTTPRequest
219+
220+
logEntry, err := createLogEntry(params)
221+
if err != nil {
222+
return err
223+
}
224+
225+
var uuid string
226+
for location := range logEntry {
227+
uuid = location
228+
}
214229

215230
return entries.NewCreateLogEntryCreated().WithPayload(logEntry).WithLocation(getEntryURL(*httpReq.URL, uuid)).WithETag(uuid)
216231
}

pkg/api/timestamp.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ import (
2626
"github.com/go-openapi/runtime/middleware"
2727
"github.com/pkg/errors"
2828
"github.com/sassoftware/relic/lib/pkcs9"
29+
"github.com/sigstore/rekor/pkg/generated/restapi/operations/entries"
2930
"github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp"
30-
"github.com/sigstore/rekor/pkg/log"
31+
rfc3161_v001 "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1"
3132
"github.com/sigstore/rekor/pkg/util"
3233
)
3334

@@ -62,15 +63,36 @@ func TimestampResponseHandler(params timestamp.GetTimestampResponseParams) middl
6263
}
6364

6465
// Create response
65-
ctx := params.HTTPRequest.Context()
66+
httpReq := params.HTTPRequest
67+
ctx := httpReq.Context()
6668
resp, err := RequestFromRekor(ctx, *req)
6769
if err != nil {
6870
return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateTimestampResponse)
6971
}
7072

71-
// TODO: Upload to transparency log and add entry UUID to location header.
72-
log.Logger.Errorf("generated OK")
73-
return timestamp.NewGetTimestampResponseOK().WithPayload(ioutil.NopCloser(bytes.NewReader(resp)))
73+
// Upload to transparency log and add entry UUID to location header.
74+
cleReq := *httpReq
75+
cleURL := entries.CreateLogEntryURL{}
76+
cleReq.URL = cleURL.Must(cleURL.Build())
77+
entryParams := entries.CreateLogEntryParams{
78+
HTTPRequest: &cleReq,
79+
ProposedEntry: rfc3161_v001.NewEntryFromBytes(resp),
80+
}
81+
82+
// If middleware is returned, this indicates an error.
83+
logEntry, middleware := createLogEntry(entryParams)
84+
if middleware != nil {
85+
return middleware
86+
}
87+
88+
var uuid string
89+
var newIndex int64
90+
for location, entry := range logEntry {
91+
uuid = location
92+
newIndex = *entry.LogIndex
93+
}
94+
95+
return timestamp.NewGetTimestampResponseCreated().WithPayload(ioutil.NopCloser(bytes.NewReader(resp))).WithLocation(getEntryURL(*cleReq.URL, uuid)).WithETag(uuid).WithIndex(newIndex)
7496
}
7597

7698
func GetTimestampCertChainHandler(params timestamp.GetTimestampCertChainParams) middleware.Responder {

pkg/generated/client/timestamp/get_timestamp_response_responses.go

Lines changed: 58 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)