Skip to content

Commit 6e34847

Browse files
committed
WIP: Convert SigstoreKeys to protobuf-specs type
Signed-off-by: Cody Soyland <[email protected]>
1 parent 4c6cc84 commit 6e34847

File tree

11 files changed

+414
-279
lines changed

11 files changed

+414
-279
lines changed

cmd/tester/main.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,10 @@ func main() {
154154
log.Fatal(err)
155155
}
156156

157-
c := &config.SigstoreKeys{}
158-
c.ConvertFrom(context.Background(), tr.Spec.SigstoreKeys)
159-
maps := make(map[string]config.SigstoreKeys, 0)
157+
c := config.ConvertSigstoreKeys(context.Background(), tr.Spec.SigstoreKeys)
158+
maps := make(map[string]*config.SigstoreKeys, 0)
160159

161-
maps[tr.Name] = *c
160+
maps[tr.Name] = c
162161
configCtx.SigstoreKeysConfig = &config.SigstoreKeysMap{SigstoreKeys: maps}
163162

164163
ctx = config.ToContext(ctx, configCtx)

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ require (
3030
github.com/mitchellh/mapstructure v1.5.0
3131
github.com/ryanuber/go-glob v1.0.0
3232
github.com/sigstore/cosign/v2 v2.2.3
33+
github.com/sigstore/protobuf-specs v0.3.0
3334
github.com/sigstore/rekor v1.3.5
3435
github.com/sigstore/sigstore v1.8.2
3536
github.com/stretchr/testify v1.9.0
@@ -41,7 +42,7 @@ require (
4142
golang.org/x/sys v0.18.0 // indirect
4243
golang.org/x/time v0.5.0
4344
google.golang.org/grpc v1.61.1 // indirect
44-
google.golang.org/protobuf v1.32.0 // indirect
45+
google.golang.org/protobuf v1.32.0
4546
gopkg.in/yaml.v3 v3.0.1
4647
k8s.io/api v0.29.2
4748
k8s.io/apimachinery v0.29.2

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,8 @@ github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh
724724
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
725725
github.com/sigstore/cosign/v2 v2.2.3 h1:WX7yawI+EXu9h7S5bZsfYCbB9XW6Jc43ctKy/NoOSiA=
726726
github.com/sigstore/cosign/v2 v2.2.3/go.mod h1:WpMn4MBt0cI23GdHsePwO4NxhX1FOz1ITGB3ALUjFaI=
727+
github.com/sigstore/protobuf-specs v0.3.0 h1:E49qS++llp4psM+3NNVEb+C4AD422bT9VkOQIPrNLpA=
728+
github.com/sigstore/protobuf-specs v0.3.0/go.mod h1:ynKzXpqr3dUj2Xk9O/5ZUhjnpi0F53DNi5AdH6pS3jc=
727729
github.com/sigstore/rekor v1.3.5 h1:QoVXcS7NppKY+rpbEFVHr4evGDZBBSh65X0g8PXoUkQ=
728730
github.com/sigstore/rekor v1.3.5/go.mod h1:CWqOk/fmnPwORQmm7SyDgB54GTJizqobbZ7yOP1lvw8=
729731
github.com/sigstore/sigstore v1.8.2 h1:0Ttjcn3V0fVQXlYq7+oHaaHkGFIt3ywm7SF4JTU/l8c=

pkg/apis/config/sigstore_keys.go

+102-90
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ package config
1717

1818
import (
1919
"context"
20-
"encoding/json"
20+
"encoding/pem"
2121
"fmt"
2222

23+
"github.com/sigstore/cosign/v2/pkg/cosign"
2324
"github.com/sigstore/policy-controller/pkg/apis/policy/v1alpha1"
25+
pbcommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
26+
pbtrustroot "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1"
27+
"github.com/sigstore/sigstore/pkg/cryptoutils"
28+
"google.golang.org/protobuf/encoding/protojson"
2429
corev1 "k8s.io/api/core/v1"
25-
"knative.dev/pkg/apis"
2630
"sigs.k8s.io/yaml"
2731
)
2832

@@ -33,81 +37,24 @@ const (
3337
SigstoreKeysConfigName = "config-sigstore-keys"
3438
)
3539

36-
// Note that these are 1:1 mapped to public API SigstoreKeys. Reasoning
37-
// being that we may choose to serialize these differently, or use the protos
38-
// that are defined upstream, so want to keep the public/private distinction, so
39-
// that we can change things independend of breaking the API. Time will tell
40-
// if this is the right call, but we can always reunify them later if we so
41-
// want.
42-
// TODO(vaikas): See about replacing these with the protos here once they land
43-
// and see how easy it is to replace with protos instead of our custom defs
44-
// above.
45-
// https://github.com/sigstore/protobuf-specs/pull/5
46-
// And in particular: https://github.com/sigstore/protobuf-specs/pull/5/files#diff-b1f89b7fd3eb27b519380b092a2416f893a96fbba3f8c90cfa767e7687383ad4R70
47-
48-
// TransparencyLogInstance describes the immutable parameters from a
49-
// transparency log.
50-
// See https://www.rfc-editor.org/rfc/rfc9162.html#name-log-parameters
51-
// for more details.
52-
// The incluced parameters are the minimal set required to identify a log,
53-
// and verify an inclusion promise.
54-
type TransparencyLogInstance struct {
55-
BaseURL apis.URL `json:"baseURL"`
56-
HashAlgorithm string `json:"hashAlgorithm"`
57-
// PEM encoded public key
58-
PublicKey []byte `json:"publicKey"`
59-
LogID string `json:"logID"`
60-
}
61-
62-
type DistinguishedName struct {
63-
Organization string `json:"organization"`
64-
CommonName string `json:"commonName"`
65-
}
66-
67-
type CertificateAuthority struct {
68-
// The root certificate MUST be self-signed, and so the subject and
69-
// issuer are the same.
70-
Subject DistinguishedName `json:"subject"`
71-
// The URI at which the CA can be accessed.
72-
URI apis.URL `json:"uri"`
73-
// The certificate chain for this CA.
74-
// CertChain is in PEM format.
75-
CertChain []byte `json:"certChain"`
76-
77-
// TODO(vaikas): How to best represent this
78-
// The time the *entire* chain was valid. This is at max the
79-
// longest interval when *all* certificates in the chain where valid,
80-
// but it MAY be shorter.
81-
// dev.sigstore.common.v1.TimeRange valid_for = 4;
82-
}
40+
// Type aliases for types from protobuf-specs. TODO: Consider just importing
41+
// the protobuf-specs types directly from each package as needed.
8342

8443
// SigstoreKeys contains all the necessary Keys and Certificates for validating
8544
// against a specific instance of Sigstore.
86-
// TODO(vaikas): See about replacing these with the protos here once they land
87-
// and see how easy it is to replace with protos instead of our custom defs
88-
// above.
89-
// https://github.com/sigstore/protobuf-specs/pull/5
90-
// And in particular: https://github.com/sigstore/protobuf-specs/pull/5/files#diff-b1f89b7fd3eb27b519380b092a2416f893a96fbba3f8c90cfa767e7687383ad4R70
91-
// Well, not the multi-root, but one instance of that is exactly the
92-
// SigstoreKeys.
93-
type SigstoreKeys struct {
94-
// Trusted certificate authorities (e.g Fulcio).
95-
CertificateAuthorities []CertificateAuthority `json:"certificateAuthorities,omitempty"`
96-
// Rekor log specifications
97-
TLogs []TransparencyLogInstance `json:"tLogs,omitempty"`
98-
// Certificate Transparency Log
99-
CTLogs []TransparencyLogInstance `json:"ctLogs,omitempty"`
100-
// Trusted timestamping authorities
101-
TimeStampAuthorities []CertificateAuthority `json:"timestampAuthorities"`
102-
}
45+
type SigstoreKeys = pbtrustroot.TrustedRoot
46+
type CertificateAuthority = pbtrustroot.CertificateAuthority
47+
type TransparencyLogInstance = pbtrustroot.TransparencyLogInstance
48+
type DistinguishedName = pbcommon.DistinguishedName
49+
type LogId = pbcommon.LogId
10350

10451
type SigstoreKeysMap struct {
105-
SigstoreKeys map[string]SigstoreKeys
52+
SigstoreKeys map[string]*SigstoreKeys
10653
}
10754

10855
// NewSigstoreKeysFromMap creates a map of SigstoreKeys to use for validation.
10956
func NewSigstoreKeysFromMap(data map[string]string) (*SigstoreKeysMap, error) {
110-
ret := make(map[string]SigstoreKeys, len(data))
57+
ret := make(map[string]*SigstoreKeys, len(data))
11158
// Spin through the ConfigMap. Each entry will have a serialized form of
11259
// necessary validation keys in the form of SigstoreKeys.
11360
for k, v := range data {
@@ -123,7 +70,7 @@ func NewSigstoreKeysFromMap(data map[string]string) (*SigstoreKeysMap, error) {
12370
if err := parseSigstoreKeys(v, sigstoreKeys); err != nil {
12471
return nil, fmt.Errorf("failed to parse the entry %q : %q : %w", k, v, err)
12572
}
126-
ret[k] = *sigstoreKeys
73+
ret[k] = sigstoreKeys
12774
}
12875
return &SigstoreKeysMap{SigstoreKeys: ret}, nil
12976
}
@@ -133,56 +80,121 @@ func NewSigstoreKeysFromConfigMap(config *corev1.ConfigMap) (*SigstoreKeysMap, e
13380
return NewSigstoreKeysFromMap(config.Data)
13481
}
13582

136-
func parseSigstoreKeys(entry string, out interface{}) error {
83+
func parseSigstoreKeys(entry string, out *pbtrustroot.TrustedRoot) error {
13784
j, err := yaml.YAMLToJSON([]byte(entry))
13885
if err != nil {
13986
return fmt.Errorf("config's value could not be converted to JSON: %w : %s", err, entry)
14087
}
141-
return json.Unmarshal(j, &out)
88+
return protojson.Unmarshal(j, out)
14289
}
14390

144-
// ConvertFrom takes a source and converts into a SigstoreKeys suitable
91+
// ConvertSigstoreKeys takes a source and converts into a SigstoreKeys suitable
14592
// for serialization into a ConfigMap entry.
146-
func (sk *SigstoreKeys) ConvertFrom(_ context.Context, source *v1alpha1.SigstoreKeys) {
147-
sk.CertificateAuthorities = make([]CertificateAuthority, len(source.CertificateAuthorities))
93+
func ConvertSigstoreKeys(_ context.Context, source *v1alpha1.SigstoreKeys) *SigstoreKeys {
94+
sk := &SigstoreKeys{}
95+
sk.CertificateAuthorities = make([]*pbtrustroot.CertificateAuthority, len(source.CertificateAuthorities))
14896
for i := range source.CertificateAuthorities {
14997
sk.CertificateAuthorities[i] = ConvertCertificateAuthority(source.CertificateAuthorities[i])
15098
}
15199

152-
sk.TLogs = make([]TransparencyLogInstance, len(source.TLogs))
100+
sk.Tlogs = make([]*pbtrustroot.TransparencyLogInstance, len(source.TLogs))
153101
for i := range source.TLogs {
154-
sk.TLogs[i] = ConvertTransparencyLogInstance(source.TLogs[i])
102+
sk.Tlogs[i] = ConvertTransparencyLogInstance(source.TLogs[i])
155103
}
156104

157-
sk.CTLogs = make([]TransparencyLogInstance, len(source.CTLogs))
105+
sk.Ctlogs = make([]*pbtrustroot.TransparencyLogInstance, len(source.CTLogs))
158106
for i := range source.CTLogs {
159-
sk.CTLogs[i] = ConvertTransparencyLogInstance(source.CTLogs[i])
107+
sk.Ctlogs[i] = ConvertTransparencyLogInstance(source.CTLogs[i])
160108
}
161109

162-
sk.TimeStampAuthorities = make([]CertificateAuthority, len(source.TimeStampAuthorities))
110+
sk.TimestampAuthorities = make([]*pbtrustroot.CertificateAuthority, len(source.TimeStampAuthorities))
163111
for i := range source.TimeStampAuthorities {
164-
sk.TimeStampAuthorities[i] = ConvertCertificateAuthority(source.TimeStampAuthorities[i])
112+
sk.TimestampAuthorities[i] = ConvertCertificateAuthority(source.TimeStampAuthorities[i])
165113
}
114+
return sk
166115
}
167116

168117
// ConvertCertificateAuthority converts public into private CertificateAuthority
169-
func ConvertCertificateAuthority(source v1alpha1.CertificateAuthority) CertificateAuthority {
170-
return CertificateAuthority{
171-
Subject: DistinguishedName{
118+
func ConvertCertificateAuthority(source v1alpha1.CertificateAuthority) *pbtrustroot.CertificateAuthority {
119+
return &pbtrustroot.CertificateAuthority{
120+
Subject: &pbcommon.DistinguishedName{
172121
Organization: source.Subject.Organization,
173122
CommonName: source.Subject.CommonName,
174123
},
175-
URI: *source.URI.DeepCopy(),
176-
CertChain: source.CertChain,
124+
Uri: source.URI.String(),
125+
CertChain: DeserializeCertChain(source.CertChain),
177126
}
178127
}
179128

180129
// ConvertTransparencyLogInstance converts public into private
181130
// TransparencyLogInstance.
182-
func ConvertTransparencyLogInstance(source v1alpha1.TransparencyLogInstance) TransparencyLogInstance {
183-
return TransparencyLogInstance{
184-
BaseURL: *source.BaseURL.DeepCopy(),
185-
HashAlgorithm: source.HashAlgorithm,
186-
PublicKey: source.PublicKey,
131+
func ConvertTransparencyLogInstance(source v1alpha1.TransparencyLogInstance) *pbtrustroot.TransparencyLogInstance {
132+
pk, err := cryptoutils.UnmarshalPEMToPublicKey(source.PublicKey)
133+
if err != nil {
134+
return nil // TODO: log error? Add return error?
135+
}
136+
logID, err := cosign.GetTransparencyLogID(pk)
137+
if err != nil {
138+
return nil // TODO: log error? Add return error?
139+
}
140+
141+
var hashAlgorithm pbcommon.HashAlgorithm
142+
switch source.HashAlgorithm {
143+
case "sha256":
144+
hashAlgorithm = pbcommon.HashAlgorithm_SHA2_256
145+
case "sha384":
146+
hashAlgorithm = pbcommon.HashAlgorithm_SHA2_384
147+
case "sha512":
148+
hashAlgorithm = pbcommon.HashAlgorithm_SHA2_512
149+
default:
150+
hashAlgorithm = pbcommon.HashAlgorithm_HASH_ALGORITHM_UNSPECIFIED
151+
}
152+
153+
return &pbtrustroot.TransparencyLogInstance{
154+
BaseUrl: source.BaseURL.String(),
155+
HashAlgorithm: hashAlgorithm,
156+
PublicKey: DeserializePublicKey(source.PublicKey),
157+
LogId: &pbcommon.LogId{
158+
KeyId: []byte(logID),
159+
},
187160
}
188161
}
162+
163+
func SerializeCertChain(certChain *pbcommon.X509CertificateChain) []byte {
164+
var chain []byte
165+
for _, cert := range certChain.Certificates {
166+
bytes := cert.RawBytes
167+
block := &pem.Block{
168+
Type: "CERTIFICATE",
169+
Bytes: bytes,
170+
}
171+
chain = append(chain, pem.EncodeToMemory(block)...)
172+
}
173+
return chain
174+
}
175+
176+
func SerializePublicKey(publicKey *pbcommon.PublicKey) []byte {
177+
block := &pem.Block{
178+
Type: "PUBLIC KEY",
179+
Bytes: publicKey.RawBytes,
180+
}
181+
return pem.EncodeToMemory(block)
182+
}
183+
184+
func DeserializeCertChain(chain []byte) *pbcommon.X509CertificateChain {
185+
var certs []*pbcommon.X509Certificate
186+
for {
187+
var block *pem.Block
188+
block, chain = pem.Decode(chain)
189+
if block == nil {
190+
break
191+
}
192+
certs = append(certs, &pbcommon.X509Certificate{RawBytes: block.Bytes})
193+
}
194+
return &pbcommon.X509CertificateChain{Certificates: certs}
195+
}
196+
197+
func DeserializePublicKey(publicKey []byte) *pbcommon.PublicKey {
198+
block, _ := pem.Decode(publicKey)
199+
return &pbcommon.PublicKey{RawBytes: block.Bytes}
200+
}

0 commit comments

Comments
 (0)