/
entries.go
158 lines (139 loc) · 5.87 KB
/
entries.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/url"
"reflect"
"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
"github.com/go-openapi/strfmt"
"github.com/mitchellh/mapstructure"
"github.com/sigstore/rekor/pkg/generated/models"
)
// EntryImpl specifies the behavior of a versioned type
type EntryImpl interface {
APIVersion() string // the supported versions for this implementation
IndexKeys() ([]string, error) // the keys that should be added to the external index for this entry
Canonicalize(ctx context.Context) ([]byte, error) // marshal the canonical entry to be put into the tlog
Unmarshal(e models.ProposedEntry) error // unmarshal the abstract entry into the specific struct for this versioned type
CreateFromArtifactProperties(context.Context, ArtifactProperties) (models.ProposedEntry, error)
}
// EntryWithAttestationImpl specifies the behavior of a versioned type that also stores attestations
type EntryWithAttestationImpl interface {
EntryImpl
AttestationKey() string // returns the key used to look up the attestation from storage (should be sha256:digest)
AttestationKeyValue() (string, []byte) // returns the key to be used when storing the attestation as well as the attestation itself
}
// EntryFactory describes a factory function that can generate structs for a specific versioned type
type EntryFactory func() EntryImpl
func NewProposedEntry(ctx context.Context, kind, version string, props ArtifactProperties) (models.ProposedEntry, error) {
if tf, found := TypeMap.Load(kind); found {
t := tf.(func() TypeImpl)()
if t == nil {
return nil, fmt.Errorf("error generating object for kind '%v'", kind)
}
return t.CreateProposedEntry(ctx, version, props)
}
return nil, fmt.Errorf("could not create entry for kind '%v'", kind)
}
// CreateVersionedEntry returns the specific instance for the type and version specified in the doc
// This method should be used on the insertion flow, which validates that the specific version proposed
// is permitted to be entered into the log.
func CreateVersionedEntry(pe models.ProposedEntry) (EntryImpl, error) {
ei, err := UnmarshalEntry(pe)
if err != nil {
return nil, err
}
kind := pe.Kind()
if tf, found := TypeMap.Load(kind); found {
if !tf.(func() TypeImpl)().IsSupportedVersion(ei.APIVersion()) {
return nil, fmt.Errorf("entry kind '%v' does not support inserting entries of version '%v'", kind, ei.APIVersion())
}
}
return ei, nil
}
// UnmarshalEntry returns the specific instance for the type and version specified in the doc
// This method does not check for whether the version of the entry could be currently inserted into the log,
// and is useful when dealing with entries that have been persisted to the log.
func UnmarshalEntry(pe models.ProposedEntry) (EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
kind := pe.Kind()
if tf, found := TypeMap.Load(kind); found {
t := tf.(func() TypeImpl)()
if t == nil {
return nil, fmt.Errorf("error generating object for kind '%v'", kind)
}
return t.UnmarshalEntry(pe)
}
return nil, fmt.Errorf("could not unmarshal entry for kind '%v'", kind)
}
// DecodeEntry maps the (abstract) input structure into the specific entry implementation class;
// while doing so, it detects the case where we need to convert from string to []byte and does
// the base64 decoding required to make that happen.
// This also detects converting from string to strfmt.DateTime
func DecodeEntry(input, output interface{}) error {
cfg := mapstructure.DecoderConfig{
DecodeHook: func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() != reflect.String || t.Kind() != reflect.Slice && t != reflect.TypeOf(strfmt.DateTime{}) {
return data, nil
}
if data == nil {
return nil, errors.New("attempted to decode nil data")
}
if t == reflect.TypeOf(strfmt.DateTime{}) {
return strfmt.ParseDateTime(data.(string))
}
bytes, err := base64.StdEncoding.DecodeString(data.(string))
if err != nil {
return []byte{}, fmt.Errorf("failed parsing base64 data: %v", err)
}
return bytes, nil
},
Result: output,
}
dec, err := mapstructure.NewDecoder(&cfg)
if err != nil {
return fmt.Errorf("error initializing decoder: %w", err)
}
return dec.Decode(input)
}
// CanonicalizeEntry returns the entry marshalled in JSON according to the
// canonicalization rules of RFC8785 to protect against any changes in golang's JSON
// marshalling logic that may reorder elements
func CanonicalizeEntry(ctx context.Context, entry EntryImpl) ([]byte, error) {
canonicalEntry, err := entry.Canonicalize(ctx)
if err != nil {
return nil, err
}
return jsoncanonicalizer.Transform(canonicalEntry)
}
// ArtifactProperties provide a consistent struct for passing values from
// CLI flags to the type+version specific CreateProposeEntry() methods
type ArtifactProperties struct {
AdditionalAuthenticatedData []byte
ArtifactPath *url.URL
ArtifactHash string
ArtifactBytes []byte
SignaturePath *url.URL
SignatureBytes []byte
PublicKeyPath *url.URL
PublicKeyBytes []byte
PKIFormat string
}