Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload generated timestamps #336

Merged
merged 5 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions cmd/rekor-cli/app/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"

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

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

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

type timestampCmdOutput struct {
Location string
Timestamp time.Time
Location string
UUID string
Index int64
}

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

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

var respBytes bytes.Buffer
_, err = rekorClient.Timestamp.GetTimestampResponse(params, &respBytes)
resp, err := rekorClient.Timestamp.GetTimestampResponse(params, &respBytes)
if err != nil {
return nil, err
}
// Sanity check response and check if the TimeStampToken was successfully created
if _, err = timestampReq.ParseResponse(respBytes.Bytes()); err != nil {
psd, err := timestampReq.ParseResponse(respBytes.Bytes())
if err != nil {
return nil, err
}
genTime, err := util.GetSigningTime(psd)
if err != nil {
return nil, err
}

Expand All @@ -238,7 +245,10 @@ var timestampCmd = &cobra.Command{

// TODO: Add log index after support for uploading to transparency log is added.
asraa marked this conversation as resolved.
Show resolved Hide resolved
return &timestampCmdOutput{
Location: outStr,
Location: outStr,
UUID: string(resp.Location),
Timestamp: genTime,
Index: resp.Index,
}, nil
}),
}
Expand Down
11 changes: 11 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,17 @@ paths:
schema:
asraa marked this conversation as resolved.
Show resolved Hide resolved
type: string
format: binary
headers:
ETag:
type: string
description: UUID of log entry
asraa marked this conversation as resolved.
Show resolved Hide resolved
Location:
type: string
description: URI location of log entry
format: uri
Index:
type: integer
description: Log index of the log entry
400:
$ref: '#/responses/BadContent'
501:
Expand Down
39 changes: 26 additions & 13 deletions pkg/api/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,26 +127,22 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa
return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry)
}

// CreateLogEntryHandler creates new entry into log
func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder {
ctx := params.HTTPRequest.Context()
httpReq := params.HTTPRequest
func createLogEntry(ctx context.Context, params entries.CreateLogEntryParams) (models.LogEntry, int, string, error) {
asraa marked this conversation as resolved.
Show resolved Hide resolved
entry, err := types.NewEntry(params.ProposedEntry)
if err != nil {
return handleRekorAPIError(params, http.StatusBadRequest, err, err.Error())
return nil, http.StatusBadRequest, err.Error(), err
}

leaf, err := entry.Canonicalize(httpReq.Context())
leaf, err := entry.Canonicalize(ctx)
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalEntry)
return nil, http.StatusInternalServerError, failedToGenerateCanonicalEntry, err
}

tc := NewTrillianClient(httpReq.Context())
tc := NewTrillianClient(ctx)

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

// this represents the results of inserting the proposed leaf into the log; status is nil in success path
Expand All @@ -156,9 +152,9 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo
case int32(code.Code_OK):
case int32(code.Code_ALREADY_EXISTS), int32(code.Code_FAILED_PRECONDITION):
existingUUID := hex.EncodeToString(rfc6962.DefaultHasher.HashLeaf(leaf))
return handleRekorAPIError(params, http.StatusConflict, fmt.Errorf("grpc error: %v", insertionStatus.String()), fmt.Sprintf(entryAlreadyExists, existingUUID), "entryURL", getEntryURL(*httpReq.URL, existingUUID))
return nil, http.StatusConflict, fmt.Sprintf("%s%s%s, ", fmt.Sprintf(entryAlreadyExists, existingUUID), "entryURL", getEntryURL(*params.HTTPRequest.URL, existingUUID)), fmt.Errorf("grpc error: %v", insertionStatus.String())
default:
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %v", insertionStatus.String()), trillianUnexpectedResult)
return nil, http.StatusInternalServerError, trillianUnexpectedResult, fmt.Errorf("grpc error: %v", insertionStatus.String())
}
}

Expand Down Expand Up @@ -201,7 +197,7 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo

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

logEntryAnon.Verification = &models.LogEntryAnonVerification{
Expand All @@ -211,6 +207,23 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo
logEntry := models.LogEntry{
uuid: logEntryAnon,
}
return logEntry, http.StatusOK, "", nil
}

// CreateLogEntryHandler creates new entry into log
func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder {
ctx := params.HTTPRequest.Context()
httpReq := params.HTTPRequest

logEntry, code, message, err := createLogEntry(ctx, params)
if err != nil {
return handleRekorAPIError(params, code, err, message)
asraa marked this conversation as resolved.
Show resolved Hide resolved
}

var uuid string
for location := range logEntry {
uuid = location
}

return entries.NewCreateLogEntryCreated().WithPayload(logEntry).WithLocation(getEntryURL(*httpReq.URL, uuid)).WithETag(uuid)
}
Expand Down
47 changes: 42 additions & 5 deletions pkg/api/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ import (
"net/http"

"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/pkg/errors"
"github.com/sassoftware/relic/lib/pkcs9"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/generated/restapi/operations/entries"
"github.com/sigstore/rekor/pkg/generated/restapi/operations/timestamp"
"github.com/sigstore/rekor/pkg/log"
rfc3161_v001 "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1"
"github.com/sigstore/rekor/pkg/util"
)

Expand All @@ -45,6 +49,22 @@ func RequestFromRekor(ctx context.Context, req pkcs9.TimeStampReq) ([]byte, erro
return body, nil
}

func createRFC3161FromBytes(resp []byte) models.ProposedEntry {
asraa marked this conversation as resolved.
Show resolved Hide resolved
b64 := strfmt.Base64(resp)
re := rfc3161_v001.V001Entry{
Rfc3161Obj: models.Rfc3161V001Schema{
Tsr: &models.Rfc3161V001SchemaTsr{
Content: &b64,
},
},
}

return &models.Rfc3161{
Spec: re.Rfc3161Obj,
APIVersion: swag.String(re.APIVersion()),
}
}

func TimestampResponseHandler(params timestamp.GetTimestampResponseParams) middleware.Responder {
// Fail early if we don't haven't configured rekor with a certificate for timestamping.
if len(api.certChain) == 0 {
Expand All @@ -62,15 +82,32 @@ func TimestampResponseHandler(params timestamp.GetTimestampResponseParams) middl
}

// Create response
ctx := params.HTTPRequest.Context()
httpReq := params.HTTPRequest
ctx := httpReq.Context()
resp, err := RequestFromRekor(ctx, *req)
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateTimestampResponse)
}

// TODO: Upload to transparency log and add entry UUID to location header.
log.Logger.Errorf("generated OK")
return timestamp.NewGetTimestampResponseOK().WithPayload(ioutil.NopCloser(bytes.NewReader(resp)))
// Upload to transparency log and add entry UUID to location header.
entryParams := entries.CreateLogEntryParams{
HTTPRequest: httpReq,
ProposedEntry: createRFC3161FromBytes(resp),
}

logEntry, code, msg, err := createLogEntry(ctx, entryParams)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vaguely remember something about this being behind a flag like --log or something, not sure if we care about that anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think looking back on it we probably want all timestamps to be logged (even if only using as a timestamping machine) just for sake of transparency. But I'm open to creating a default upload flag and making it configurable per req.

if err != nil {
return handleRekorAPIError(params, code, err, msg)
}

var uuid string
var newIndex int64
for location, entry := range logEntry {
uuid = location
newIndex = *entry.LogIndex
}

return timestamp.NewGetTimestampResponseOK().WithPayload(ioutil.NopCloser(bytes.NewReader(resp))).WithLocation(getEntryURL(*httpReq.URL, uuid)).WithETag(uuid).WithIndex(newIndex)
}

func GetTimestampCertChainHandler(params timestamp.GetTimestampCertChainParams) middleware.Responder {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions pkg/generated/restapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading