diff --git a/cmd/rekor-cli/app/get.go b/cmd/rekor-cli/app/get.go index 2b3a7b4227d20da67f5b8b13092684198cd48d1e..d40801c37e6a0d47b6b748dffdbd54367729f4d6 100644 --- a/cmd/rekor-cli/app/get.go +++ b/cmd/rekor-cli/app/get.go @@ -40,10 +40,12 @@ type getCmdOutput struct { LogIndex int IntegratedTime int64 UUID string + LogID string } func (g *getCmdOutput) String() string { - s := fmt.Sprintf("Index: %d\n", g.LogIndex) + s := fmt.Sprintf("LogID: %v\n", g.LogID) + s += fmt.Sprintf("Index: %d\n", g.LogIndex) dt := time.Unix(g.IntegratedTime, 0).UTC().Format(time.RFC3339) s += fmt.Sprintf("IntegratedTime: %s\n", dt) s += fmt.Sprintf("UUID: %s\n", g.UUID) @@ -130,8 +132,9 @@ func parseEntry(uuid string, e models.LogEntryAnon) (interface{}, error) { obj := getCmdOutput{ Body: eimpl, UUID: uuid, - IntegratedTime: e.IntegratedTime, + IntegratedTime: *e.IntegratedTime, LogIndex: int(*e.LogIndex), + LogID: *e.LogID, } return &obj, nil diff --git a/openapi.yaml b/openapi.yaml index b531661d3076f18699cd4b016392ca9510d2e9a4..561167d160fe65a6ffeef8db7ba61b7e1e8e6afa 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -291,6 +291,10 @@ definitions: additionalProperties: type: object properties: + logID: + type: string + pattern: '^[0-9a-fA-F]{64}$' + description: This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log logIndex: type: integer minimum: 0 @@ -311,10 +315,12 @@ definitions: # 1. Remove the Verification object from the JSON Document # 2. Canonicalize the remaining JSON document by following RFC 8785 rules # 3. Verify the canonicalized payload and signedEntryTimestamp against rekor's public key - description: Signature over the logIndex, body and integratedTime. + description: Signature over the logID, logIndex, body and integratedTime. required: + - "logID" - "logIndex" - "body" + - "integratedTime" SearchIndex: type: object diff --git a/pkg/api/api.go b/pkg/api/api.go index cfd150d596eb0070851ef0eb65f06ff24330e610..d1b8fbdcf616179fb3caba4933748d8a9877307f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -17,7 +17,9 @@ package api import ( "context" + "crypto/sha256" "crypto/x509" + "encoding/hex" "encoding/pem" "fmt" "time" @@ -47,12 +49,12 @@ func dial(ctx context.Context, rpcServer string) (*grpc.ClientConn, error) { } type API struct { - logClient trillian.TrillianLogClient - logID int64 - // PEM encoded public key - pubkey string - signer signature.Signer - verifier *client.LogVerifier + logClient trillian.TrillianLogClient + logID int64 + pubkey string // PEM encoded public key + pubkeyHash string // SHA256 hash of DER-encoded public key + signer signature.Signer + verifier *client.LogVerifier } func NewAPI() (*API, error) { @@ -95,6 +97,12 @@ func NewAPI() (*API, error) { if err != nil { return nil, errors.Wrap(err, "marshalling public key") } + hasher := sha256.New() + if _, err = hasher.Write(b); err != nil { + return nil, errors.Wrap(err, "computing hash of public key") + } + pubkeyHashBytes := hasher.Sum(nil) + pubkey := pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Bytes: b, @@ -106,11 +114,12 @@ func NewAPI() (*API, error) { } return &API{ - logClient: logClient, - logID: tLogID, - pubkey: string(pubkey), - signer: signer, - verifier: verifier, + logClient: logClient, + logID: tLogID, + pubkey: string(pubkey), + pubkeyHash: hex.EncodeToString(pubkeyHashBytes), + signer: signer, + verifier: verifier, }, nil } diff --git a/pkg/api/entries.go b/pkg/api/entries.go index e67fafc33403e2150621d9d66a9f2bb1d899248e..69b649a59b180aa1e70ccf67ad56e786f178657f 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -62,9 +62,10 @@ func logEntryFromLeaf(tc TrillianClient, leaf *trillian.LogLeaf, signedLogRoot * logEntry := models.LogEntry{ hex.EncodeToString(leaf.MerkleLeafHash): models.LogEntryAnon{ + LogID: swag.String(api.pubkeyHash), LogIndex: &leaf.LeafIndex, Body: leaf.LeafValue, - IntegratedTime: leaf.IntegrateTimestamp.AsTime().Unix(), + IntegratedTime: swag.Int64(leaf.IntegrateTimestamp.AsTime().Unix()), Verification: &models.LogEntryAnonVerification{ InclusionProof: &inclusionProof, }, @@ -143,9 +144,10 @@ func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Respo uuid := hex.EncodeToString(queuedLeaf.GetMerkleLeafHash()) logEntryAnon := models.LogEntryAnon{ + LogID: swag.String(api.pubkeyHash), LogIndex: swag.Int64(queuedLeaf.LeafIndex), Body: queuedLeaf.GetLeafValue(), - IntegratedTime: queuedLeaf.IntegrateTimestamp.AsTime().Unix(), + IntegratedTime: swag.Int64(queuedLeaf.IntegrateTimestamp.AsTime().Unix()), } if viper.GetBool("enable_retrieve_api") { diff --git a/pkg/generated/models/log_entry.go b/pkg/generated/models/log_entry.go index 9924fd819d5a66031c80208261c3d277af100f7b..a9ae7cceb9f3089122c1b888075d9b683b4ffe7e 100644 --- a/pkg/generated/models/log_entry.go +++ b/pkg/generated/models/log_entry.go @@ -88,7 +88,13 @@ type LogEntryAnon struct { Body interface{} `json:"body"` // integrated time - IntegratedTime int64 `json:"integratedTime,omitempty"` + // Required: true + IntegratedTime *int64 `json:"integratedTime"` + + // This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log + // Required: true + // Pattern: ^[0-9a-fA-F]{64}$ + LogID *string `json:"logID"` // log index // Required: true @@ -107,6 +113,14 @@ func (m *LogEntryAnon) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateIntegratedTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateLogID(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogIndex(formats); err != nil { res = append(res, err) } @@ -130,6 +144,28 @@ func (m *LogEntryAnon) validateBody(formats strfmt.Registry) error { return nil } +func (m *LogEntryAnon) validateIntegratedTime(formats strfmt.Registry) error { + + if err := validate.Required("integratedTime", "body", m.IntegratedTime); err != nil { + return err + } + + return nil +} + +func (m *LogEntryAnon) validateLogID(formats strfmt.Registry) error { + + if err := validate.Required("logID", "body", m.LogID); err != nil { + return err + } + + if err := validate.Pattern("logID", "body", *m.LogID, `^[0-9a-fA-F]{64}$`); err != nil { + return err + } + + return nil +} + func (m *LogEntryAnon) validateLogIndex(formats strfmt.Registry) error { if err := validate.Required("logIndex", "body", m.LogIndex); err != nil { @@ -214,7 +250,7 @@ type LogEntryAnonVerification struct { // inclusion proof InclusionProof *InclusionProof `json:"inclusionProof,omitempty"` - // Signature over the logIndex, body and integratedTime. + // Signature over the logID, logIndex, body and integratedTime. // Format: byte SignedEntryTimestamp strfmt.Base64 `json:"signedEntryTimestamp,omitempty"` } diff --git a/pkg/generated/restapi/embedded_spec.go b/pkg/generated/restapi/embedded_spec.go index 1cd7cb5b1d224d9256458d75b73f38c3330f7c24..d9843d86708fe04b74508f62259144e747305d95 100644 --- a/pkg/generated/restapi/embedded_spec.go +++ b/pkg/generated/restapi/embedded_spec.go @@ -400,8 +400,10 @@ func init() { "additionalProperties": { "type": "object", "required": [ + "logID", "logIndex", - "body" + "body", + "integratedTime" ], "properties": { "body": { @@ -411,6 +413,11 @@ func init() { "integratedTime": { "type": "integer" }, + "logID": { + "description": "This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log", + "type": "string", + "pattern": "^[0-9a-fA-F]{64}$" + }, "logIndex": { "type": "integer" }, @@ -421,7 +428,7 @@ func init() { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { - "description": "Signature over the logIndex, body and integratedTime.", + "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" } @@ -1193,8 +1200,10 @@ func init() { "LogEntryAnon": { "type": "object", "required": [ + "logID", "logIndex", - "body" + "body", + "integratedTime" ], "properties": { "body": { @@ -1204,6 +1213,11 @@ func init() { "integratedTime": { "type": "integer" }, + "logID": { + "description": "This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log", + "type": "string", + "pattern": "^[0-9a-fA-F]{64}$" + }, "logIndex": { "type": "integer", "minimum": 0 @@ -1215,7 +1229,7 @@ func init() { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { - "description": "Signature over the logIndex, body and integratedTime.", + "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" } @@ -1230,7 +1244,7 @@ func init() { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { - "description": "Signature over the logIndex, body and integratedTime.", + "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" }