Skip to content

Commit

Permalink
UNTESTED: Replace sigstore/rekor/pkg/client with a manually-created c…
Browse files Browse the repository at this point in the history
…lient

This removes 4.728 MB from a macOS Skopeo binary.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
  • Loading branch information
mtrmac committed Sep 5, 2023
1 parent b41be19 commit d1f2d9e
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 281 deletions.
25 changes: 1 addition & 24 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ require (
github.com/docker/docker v24.0.5+incompatible
github.com/docker/docker-credential-helpers v0.8.0
github.com/docker/go-connections v0.4.0
github.com/go-openapi/strfmt v0.21.7
github.com/go-openapi/swag v0.22.4
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/klauspost/compress v1.16.7
Expand All @@ -27,7 +25,6 @@ require (
github.com/proglottis/gpgme v0.1.3
github.com/secure-systems-lab/go-securesystemslib v0.7.0
github.com/sigstore/fulcio v1.4.0
github.com/sigstore/rekor v1.2.2
github.com/sigstore/sigstore v1.7.3
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
Expand All @@ -51,7 +48,6 @@ require (
github.com/Microsoft/hcsshim v0.10.0 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/containerd v1.7.0 // indirect
Expand All @@ -61,16 +57,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/runtime v0.26.0 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand All @@ -80,23 +66,19 @@ require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
Expand All @@ -110,12 +92,8 @@ require (
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.12.0 // indirect
Expand All @@ -126,6 +104,5 @@ require (
google.golang.org/grpc v1.56.2 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.4.0 // indirect
)
173 changes: 0 additions & 173 deletions go.sum

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions signature/internal/rekor_api_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package internal

import (
"bytes"
"encoding/json"
"fmt"
)

const rekorHashedrekordKind = "hashedrekord"

type RekorHashedrekord struct {
APIVersion *string `json:"apiVersion"`
Spec json.RawMessage `json:"spec"`
}

func (m *RekorHashedrekord) Kind() string {
return rekorHashedrekordKind
}

func (m *RekorHashedrekord) SetKind(val string) {
}

func (m *RekorHashedrekord) UnmarshalJSON(raw []byte) error {
var base struct {
Kind string `json:"kind"`
}
dec := json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}

switch base.Kind {
case rekorHashedrekordKind:
var data struct { // We can’t use RekorHashedRekord directly, because that would be an infinite recursion.
APIVersion *string `json:"apiVersion"`
Spec json.RawMessage `json:"spec"`
}
dec = json.NewDecoder(bytes.NewReader(raw))
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
res := RekorHashedrekord{
APIVersion: data.APIVersion,
Spec: data.Spec,
}
*m = res
return nil

default:
return fmt.Errorf("invalid kind value: %q", base.Kind)
}
}

func (m RekorHashedrekord) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Kind string `json:"kind"`
APIVersion *string `json:"apiVersion"`
Spec json.RawMessage `json:"spec"`
}{
Kind: m.Kind(),
APIVersion: m.APIVersion,
Spec: m.Spec,
})
}

type RekorHashedrekordV001Schema struct {
Data *RekorHashedrekordV001SchemaData `json:"data"`
Signature *RekorHashedrekordV001SchemaSignature `json:"signature"`
}

type RekorHashedrekordV001SchemaData struct {
Hash *RekorHashedrekordV001SchemaDataHash `json:"hash,omitempty"`
}

type RekorHashedrekordV001SchemaDataHash struct {
Algorithm *string `json:"algorithm"`
Value *string `json:"value"`
}

const (
RekorHashedrekordV001SchemaDataHashAlgorithmSha256 string = "sha256"
)

type RekorHashedrekordV001SchemaSignature struct {
Content []byte `json:"content,omitempty"`
PublicKey *RekorHashedrekordV001SchemaSignaturePublicKey `json:"publicKey,omitempty"`
}

type RekorHashedrekordV001SchemaSignaturePublicKey struct {
Content []byte `json:"content,omitempty"`
}
28 changes: 8 additions & 20 deletions signature/internal/rekor_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import (
"time"

"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
"github.com/sigstore/rekor/pkg/generated/models"
)

// This is the github.com/sigstore/rekor/pkg/generated/models.Hashedrekord.APIVersion for github.com/sigstore/rekor/pkg/generated/models.HashedrekordV001Schema.
// We could alternatively use github.com/sigstore/rekor/pkg/types/hashedrekord.APIVERSION, but that subpackage adds too many dependencies.
const HashedRekordV001APIVersion = "0.0.1"
const RekorHashedRekordV001APIVersion = "0.0.1"

// UntrustedRekorSET is a parsed content of the sigstore-signature Rekor SET
// (note that this a signature-specific format, not a format directly used by the Rekor API).
Expand Down Expand Up @@ -140,31 +139,20 @@ func VerifyRekorSET(publicKey *ecdsa.PublicKey, unverifiedRekorSET []byte, unver
if err := json.Unmarshal(untrustedSETPayloadCanonicalBytes, &rekorPayload); err != nil {
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("parsing Rekor SET payload: %v", err.Error()))
}
// FIXME: Use a different decoder implementation? The Swagger-generated code is kinda ridiculous, with the need to re-marshal
// hashedRekor.Spec and so on.
// Especially if we anticipate needing to decode different data formats…
// That would also allow being much more strict about JSON.
//
// Alternatively, rely on the existing .Validate() methods instead of manually checking for nil all over the place.
var hashedRekord models.Hashedrekord
// FIXME: Consider being much more strict about decoding JSON.
var hashedRekord RekorHashedrekord
if err := json.Unmarshal(rekorPayload.Body, &hashedRekord); err != nil {
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("decoding the body of a Rekor SET payload: %v", err))
}
// The decode of models.HashedRekord validates the "kind": "hashedrecord" field, which is otherwise invisible to us.
// The decode of HashedRekord validates the "kind": "hashedrecord" field, which is otherwise invisible to us.
if hashedRekord.APIVersion == nil {
return time.Time{}, NewInvalidSignatureError("missing Rekor SET Payload API version")
}
if *hashedRekord.APIVersion != HashedRekordV001APIVersion {
if *hashedRekord.APIVersion != RekorHashedRekordV001APIVersion {
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("unsupported Rekor SET Payload hashedrekord version %#v", hashedRekord.APIVersion))
}
hashedRekordV001Bytes, err := json.Marshal(hashedRekord.Spec)
if err != nil {
// Coverage: hashedRekord.Spec is an any that was just unmarshaled,
// so this should never fail.
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("re-creating hashedrekord spec: %v", err))
}
var hashedRekordV001 models.HashedrekordV001Schema
if err := json.Unmarshal(hashedRekordV001Bytes, &hashedRekordV001); err != nil {
var hashedRekordV001 RekorHashedrekordV001Schema
if err := json.Unmarshal(hashedRekord.Spec, &hashedRekordV001); err != nil {
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf("decoding hashedrekod spec: %v", err))
}

Expand Down Expand Up @@ -216,7 +204,7 @@ func VerifyRekorSET(publicKey *ecdsa.PublicKey, unverifiedRekorSET []byte, unver
if hashedRekordV001.Data.Hash.Algorithm == nil {
return time.Time{}, NewInvalidSignatureError(`Missing "data.hash.algorithm" field in hashedrekord`)
}
if *hashedRekordV001.Data.Hash.Algorithm != models.HashedrekordV001SchemaDataHashAlgorithmSha256 {
if *hashedRekordV001.Data.Hash.Algorithm != RekorHashedrekordV001SchemaDataHashAlgorithmSha256 {
return time.Time{}, NewInvalidSignatureError(fmt.Sprintf(`Unexpected "data.hash.algorithm" value %#v`, *hashedRekordV001.Data.Hash.Algorithm))
}
if hashedRekordV001.Data.Hash.Value == nil {
Expand Down
36 changes: 20 additions & 16 deletions signature/internal/rekor_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import (
"testing"
"time"

"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigstoreSignature "github.com/sigstore/sigstore/pkg/signature"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -171,6 +168,11 @@ func TestUntrustedRekorPayloadUnmarshalJSON(t *testing.T) {
}
}

// stringPointer is a helper to create *string fields in JSON data.
func stringPointer(s string) *string {
return &s
}

func TestVerifyRekorSET(t *testing.T) {
cosignRekorKeyPEM, err := os.ReadFile("testdata/rekor.pub")
require.NoError(t, err)
Expand Down Expand Up @@ -246,22 +248,24 @@ func TestVerifyRekorSET(t *testing.T) {
cosignPayloadSHA256 := sha256.Sum256(cosignPayloadBytes)
cosignSigBytes, err := base64.StdEncoding.DecodeString(string(cosignSigBase64))
require.NoError(t, err)
validHashedRekord := models.Hashedrekord{
APIVersion: swag.String(HashedRekordV001APIVersion),
Spec: models.HashedrekordV001Schema{
Data: &models.HashedrekordV001SchemaData{
Hash: &models.HashedrekordV001SchemaDataHash{
Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
Value: swag.String(hex.EncodeToString(cosignPayloadSHA256[:])),
},
validHashedRekordSpec, err := json.Marshal(RekorHashedrekordV001Schema{
Data: &RekorHashedrekordV001SchemaData{
Hash: &RekorHashedrekordV001SchemaDataHash{
Algorithm: stringPointer(RekorHashedrekordV001SchemaDataHashAlgorithmSha256),
Value: stringPointer(hex.EncodeToString(cosignPayloadSHA256[:])),
},
Signature: &models.HashedrekordV001SchemaSignature{
Content: strfmt.Base64(cosignSigBytes),
PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{
Content: strfmt.Base64(cosignCertBytes),
},
},
Signature: &RekorHashedrekordV001SchemaSignature{
Content: cosignSigBytes,
PublicKey: &RekorHashedrekordV001SchemaSignaturePublicKey{
Content: cosignCertBytes,
},
},
})
require.NoError(t, err)
validHashedRekord := RekorHashedrekord{
APIVersion: stringPointer(RekorHashedRekordV001APIVersion),
Spec: validHashedRekordSpec,
}
validHashedRekordJSON, err := json.Marshal(validHashedRekord)
require.NoError(t, err)
Expand Down
79 changes: 79 additions & 0 deletions signature/sigstore/rekor/openapi_infra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package rekor

// The following code is the essence of the relevant code paths from github.com/go-openapi/runtime,
// heavily modified since.

// Copyright 2015 go-swagger maintainers
//
// 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.

import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"path"
)

// makeRequest makes a http request to the requested requestPath, and returns the received response.
func (r *rekorClient) makeRequest(ctx context.Context, method, requestPath string, bodyContent any) (*http.Response, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

var body io.Reader
headers := http.Header{}

headers.Set("Accept", "application/json")
if bodyContent != nil {
buf := bytes.NewBuffer(nil)
body = buf
headers.Set("Content-Type", "application/json")
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(bodyContent); err != nil {
return nil, err
}
}

req, err := http.NewRequestWithContext(ctx, method, path.Join(r.basePath, requestPath), body)
if err != nil {
return nil, err
}
// Only Scheme and Host are used from rekorURL.
// Really this should probabbly use r.rekorURL.JoinPath(requestPath) (which, notably, correctly deals with path escaping),
// and pass that to NewRequestWithContext, but this use of path.Join is consistent with go-openapi/runtime v0.24.1 .
req.URL.Scheme = r.rekorURL.Scheme
req.URL.Host = r.rekorURL.Host
req.Header = headers

res, err := r.httpClient.Do(req)
if err != nil {
return nil, err
}
// Note that we don’t care to even read the Content-Type: header; we blindly assume the format is the requested JSON.
return res, nil
}

// decodeHTTPResponseBodyAsJSON decodes the body of a HTTP response in a manner compatible with github.com/go-openapi/runtime.
func decodeHTTPResponseBodyAsJSON(res *http.Response, data any) error {
dec := json.NewDecoder(res.Body)
dec.UseNumber()
err := dec.Decode(data)
if err == io.EOF {
// This seems unwanted at a first glance; go-swagger added it in https://github.com/go-swagger/go-swagger/issues/192 , it’s unclear
// whether it’s correct or still necessary.
err = nil
}
return err
}

0 comments on commit d1f2d9e

Please sign in to comment.