//
// Copyright 2023 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 fuzz
import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"fmt"
"os"
"strings"
fuzz "github.com/AdamKorcz/go-fuzz-headers-1"
"github.com/sigstore/rekor/pkg/types"
)
// Allows the fuzzer to create a .SIGN filename
func getSignFilename(ff *fuzz.ConsumeFuzzer) (string, error) {
keyName, err := ff.GetString()
if err != nil {
return "", err
}
var b strings.Builder
b.WriteString(".SIGN.RSA.")
b.WriteString(keyName)
b.WriteString(".rsa.pub")
return b.String(), nil
}
// createPkgInfoFileContents creates a structured pkginfo file
//
// .PKGINFO files look like this:
//
// # Generated by abuild 3.9.0-r2
// # using fakeroot version 1.25.3
// # Wed Jul 6 19:09:49 UTC 2022
// pkgname = busybox
// pkgver = 1.35.0-r18
// pkgdesc = Size optimized toolbox of many common UNIX utilities
// url = https://busybox.net/
// builddate = 1657134589
// packager = Buildozer <developer@email.org>
// size = 958464
// arch = x86_64
// origin = busybox
// commit = 332d2fff53cd4537d415e15e55e8ceb6fe6eaedb
// maintainer = Sören Tempel <soeren+alpine@soeren-tempel.net>
// provider_priority = 100
// license = GPL-2.0-only
// replaces = busybox-initscripts
// provides = /bin/sh
// triggers = /bin /usr/bin /sbin /usr/sbin /lib/modules/*
// # automatically detected:
// provides = cmd:busybox=1.35.0-r18
// provides = cmd:sh=1.35.0-r18
// depend = so:libc.musl-x86_64.so.1
// datahash = 7d3351ac6c3ebaf18182efb5390061f50d077ce5ade60a15909d91278f70ada7
func createPkgInfoFileContents(ff *fuzz.ConsumeFuzzer) ([]byte, error) {
var b strings.Builder
noOfRows, err := ff.GetInt()
if err != nil {
return []byte(""), err
}
// Comments at the top of the pkginfo file
header, err := ff.GetBytes()
if err != nil {
return []byte(""), err
}
b.Write(header)
for i := 0; i < noOfRows; i++ {
key, err := ff.GetBytes()
if err != nil {
return []byte(""), err
}
value, err := ff.GetBytes()
if err != nil {
return []byte(""), err
}
b.Write(key)
b.Write([]byte(" = "))
b.Write(value)
b.WriteString("\n")
}
return []byte(b.String()), nil
}
// Adds a .SIGN file to tarBytes
func addSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) {
SIGNFileContents, err := ff.GetBytes()
if err != nil {
return tarFiles, err
}
SIGNFileName, err := getSignFilename(ff)
if err != nil {
return tarFiles, err
}
signFile := &fuzz.TarFile{
Body: SIGNFileContents,
Hdr: &tar.Header{
Name: SIGNFileName,
Mode: 0644,
Size: int64(len(SIGNFileContents)),
Typeflag: tar.TypeReg,
Gid: 0,
Uid: 0,
},
}
tarFiles = append(tarFiles, signFile)
return tarFiles, nil
}
// Allows the fuzzer to randomize whether a .SIGN file should
// be added to tarBytes
func shouldAddSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool {
shouldRequireSIGNFile, err := ff.GetBool()
if err != nil {
return false
}
if shouldRequireSIGNFile {
for _, tarFile := range tarFiles {
if strings.HasPrefix(tarFile.Hdr.Name, ".SIGN") {
return false
}
}
return true
}
return false
}
// Allows the fuzzer to randomize whether a .PKGINFO file should
// be added to tarBytes
func shouldAddPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool {
shouldRequirePKGINFOFile, err := ff.GetBool()
if err != nil {
return false
}
if shouldRequirePKGINFOFile {
for _, tarFile := range tarFiles {
if strings.HasPrefix(tarFile.Hdr.Name, ".PKGINFO") {
return false
}
}
return true
}
return false
}
// Adds the .PKGINFO file to the tar files
func addPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) {
tarFile := &fuzz.TarFile{}
PKGINFOFileContents, err := createPkgInfoFileContents(ff)
if err != nil {
return tarFiles, err
}
tarFile.Body = PKGINFOFileContents
tarFile.Hdr = &tar.Header{
Name: ".PKGINFO",
Mode: 0644,
Size: int64(len(PKGINFOFileContents)),
Typeflag: tar.TypeReg,
Gid: 0,
Uid: 0,
}
return tarFiles, nil
}
func AlpineArtifactBytes(ff *fuzz.ConsumeFuzzer) ([]byte, error) {
var tarFiles, tarFiles2 []*fuzz.TarFile
var err error
tarFiles, err = ff.TarFiles()
if err != nil {
return []byte(""), err
}
if shouldAddSignFile(ff, tarFiles) {
tarFiles, err = addSignFile(ff, tarFiles)
if err != nil {
return []byte(""), err
}
}
tarFiles2, err = ff.TarFiles()
if err != nil {
return []byte(""), err
}
if shouldAddPkgInfoFile(ff, tarFiles2) {
tarFiles2, err = addPkgInfoFile(ff, tarFiles2)
if err != nil {
return []byte(""), err
}
}
return concatenateTarArchives(tarFiles, tarFiles2)
}
func concatenateTarArchives(tarFiles1 []*fuzz.TarFile, tarFiles2 []*fuzz.TarFile) ([]byte, error) {
var buf1, buf2 bytes.Buffer
var err error
tw1 := tar.NewWriter(&buf1)
for _, tf := range tarFiles1 {
err = tw1.WriteHeader(tf.Hdr)
if err != nil {
return []byte(""), err
}
_, err = tw1.Write(tf.Body)
if err != nil {
return []byte(""), err
}
}
tw1.Close()
tarBytes := buf1.Bytes()
tw2 := tar.NewWriter(&buf2)
for _, tf := range tarFiles2 {
err = tw2.WriteHeader(tf.Hdr)
if err != nil {
return []byte(""), err
}
_, err = tw2.Write(tf.Body)
if err != nil {
return []byte(""), err
}
}
tw2.Close()
tarBytes2 := buf2.Bytes()
var b1 bytes.Buffer
w1 := gzip.NewWriter(&b1)
defer w1.Close()
_, err = w1.Write(tarBytes)
if err != nil {
return []byte(""), err
}
w1.Close()
var b2 bytes.Buffer
w2 := gzip.NewWriter(&b2)
defer w2.Close()
_, err = w2.Write(tarBytes2)
if err != nil {
return []byte(""), err
}
w2.Close()
concatenated := append(b1.Bytes(), b2.Bytes()...)
return concatenated, nil
}
func setAlpineArtifactFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) {
cleanup := func() {}
err := setArtifactHash(ff, props)
if err != nil {
return cleanup, err
}
artifactBytes, err := AlpineArtifactBytes(ff)
if err != nil {
return cleanup, err
}
shouldSetArtifactBytes, err := ff.GetBool()
if err != nil {
return cleanup, err
}
if shouldSetArtifactBytes {
props.ArtifactBytes = artifactBytes
return func() {
// do nothing
}, nil
}
artifactFile, err := createAbsFile(ff, "ArtifactFile", artifactBytes)
cleanup = func() {
os.Remove("ArtifactFile")
}
props.ArtifactPath = artifactFile
return cleanup, err
}
// Creates an ArtifactProperties with values determined by the fuzzer
func CreateAlpineProps(ff *fuzz.ConsumeFuzzer) (types.ArtifactProperties, func(), error) {
props := &types.ArtifactProperties{}
cleanupArtifactFile, err := setAlpineArtifactFields(ff, props)
if err != nil {
return *props, cleanupArtifactFile, err
}
if props.ArtifactPath == nil && props.ArtifactBytes == nil {
return *props, cleanupArtifactFile, errors.New("ArtifactPath and ArtifactBytes cannot both be nil")
}
err = setAdditionalAuthenticatedData(ff, props)
if err != nil {
return *props, cleanupArtifactFile, errors.New("failed setting AdditionalAuthenticatedData")
}
cleanupSignatureFile, err := setSignatureFields(ff, props)
if err != nil {
return *props, func() {
cleanupArtifactFile()
cleanupSignatureFile()
}, fmt.Errorf("failed setting signature fields: %w", err)
}
cleanupPublicKeyFile, err := setPublicKeyFields(ff, props)
if err != nil {
return *props, func() {
cleanupArtifactFile()
cleanupSignatureFile()
cleanupPublicKeyFile()
}, fmt.Errorf("failed setting public key fields: %w", err)
}
err = setPKIFormat(ff, props)
if err != nil {
return *props, func() {
cleanupArtifactFile()
cleanupSignatureFile()
cleanupPublicKeyFile()
}, fmt.Errorf("failed setting PKI Format: %w", err)
}
return *props, func() {
cleanupArtifactFile()
cleanupSignatureFile()
cleanupPublicKeyFile()
}, nil
}
//
// Copyright 2022 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 fuzz
import (
"archive/zip"
"bytes"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
fuzz "github.com/AdamKorcz/go-fuzz-headers-1"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/types"
)
// Sets ArtifactHash
func setArtifactHash(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error {
artifactHash, err := ff.GetString()
if err != nil {
return err
}
props.ArtifactHash = artifactHash
return nil
}
// creates a file on disk and returns the url of it.
func createAbsFile(_ *fuzz.ConsumeFuzzer, fileName string, fileContents []byte) (*url.URL, error) {
file, err := os.Create(fileName)
if err != nil {
return nil, err
}
defer file.Close()
filePath, err := filepath.Abs(fileName)
if err != nil {
return nil, err
}
fileURL, err := url.Parse(filePath)
if err != nil {
return nil, err
}
_, err = file.Write(fileContents)
if err != nil {
return nil, err
}
return fileURL, err
}
// Sets the signature fields of a props.
// It either sets SignatureBytes or SignaturePath
func setSignatureFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) {
cleanup := func() {}
shouldSetSignatureBytes, err := ff.GetBool()
if err != nil {
return cleanup, err
}
signatureBytes, err := ff.GetBytes()
if err != nil {
return cleanup, err
}
if shouldSetSignatureBytes {
props.SignatureBytes = signatureBytes
return cleanup, nil
}
signatureURL, err := createAbsFile(ff, "SignatureFile", signatureBytes)
if err != nil {
os.Remove("SignatureFile")
return cleanup, err
}
props.SignaturePath = signatureURL
return func() {
os.Remove("SignatureFile")
}, nil
}
func setPublicKeyFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) {
cleanup := func() {}
shouldSetPublicKeyBytes, err := ff.GetBool()
if err != nil {
return cleanup, err
}
if shouldSetPublicKeyBytes {
publicKeyBytes := make([][]byte, 0)
err := ff.GenerateStruct(&publicKeyBytes)
if err != nil || len(publicKeyBytes) == 0 {
return cleanup, err
}
props.PublicKeyBytes = publicKeyBytes
return cleanup, nil
}
publicKeyBytes, err := ff.GetBytes()
if err != nil {
return cleanup, err
}
publicKeyURL, err := createAbsFile(ff, "PublicKeyFile", publicKeyBytes)
if err != nil {
os.Remove("PublicKeyFile")
return cleanup, err
}
props.PublicKeyPaths = []*url.URL{publicKeyURL}
return func() {
os.Remove("PublicKeyFile")
}, nil
}
// Sets the "AdditionalAuthenticatedData" field of the props
func setAdditionalAuthenticatedData(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error {
shouldSetAdditionalAuthenticatedData, err := ff.GetBool()
if err != nil {
return err
}
if shouldSetAdditionalAuthenticatedData {
additionalAuthenticatedData, err := ff.GetBytes()
if err != nil {
return err
}
props.AdditionalAuthenticatedData = additionalAuthenticatedData
}
return nil
}
// Sets the PKI format if the fuzzer decides to.
func setPKIFormat(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error {
shouldSetPKIFormat, err := ff.GetBool()
if err != nil {
return err
}
if shouldSetPKIFormat {
pkiFormat, err := ff.GetString()
if err != nil {
return err
}
props.PKIFormat = pkiFormat
}
return nil
}
func createArtifactFiles(ff *fuzz.ConsumeFuzzer, artifactType string) ([]*fuzz.TarFile, error) {
switch artifactType {
case "jarV001":
return createJarArtifactFiles(ff)
default:
return createDefaultArtifactFiles(ff)
}
}
func createDefaultArtifactFiles(ff *fuzz.ConsumeFuzzer) ([]*fuzz.TarFile, error) {
var files []*fuzz.TarFile
files, err := ff.TarFiles()
if err != nil {
return files, err
}
if len(files) <= 1 {
return files, err
}
for _, file := range files {
if len(file.Body) == 0 {
return files, errors.New("created an empty file")
}
}
return files, nil
}
// Creates an ArtifactProperties with values determined by the fuzzer
func CreateProps(ff *fuzz.ConsumeFuzzer, fuzzType string) (types.ArtifactProperties, []func(), error) {
var cleanups []func()
props := &types.ArtifactProperties{}
err := setArtifactHash(ff, props)
if err != nil {
return *props, cleanups, err
}
artifactFiles, err := createArtifactFiles(ff, fuzzType)
if err != nil {
return *props, cleanups, err
}
err = setAdditionalAuthenticatedData(ff, props)
if err != nil {
return *props, cleanups, errors.New("failed setting AdditionalAuthenticatedData")
}
cleanupSignatureFile, err := setSignatureFields(ff, props)
if err != nil {
return *props, cleanups, fmt.Errorf("failed setting signature fields: %w", err)
}
cleanups = append(cleanups, cleanupSignatureFile)
cleanupPublicKeyFile, err := setPublicKeyFields(ff, props)
if err != nil {
return *props, cleanups, fmt.Errorf("failed setting public key fields: %w", err)
}
cleanups = append(cleanups, cleanupPublicKeyFile)
err = setPKIFormat(ff, props)
if err != nil {
return *props, cleanups, fmt.Errorf("failed setting PKI Format: %w", err)
}
artifactBytes, err := tarFilesToBytes(artifactFiles, fuzzType)
if err != nil {
return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %w", err)
}
setArtifactBytes, err := ff.GetBool()
if err != nil {
return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %w", err)
}
if setArtifactBytes {
props.ArtifactBytes = artifactBytes
} else {
artifactFile, err := createAbsFile(ff, "ArtifactFile", artifactBytes)
cleanups = append(cleanups, func() { os.Remove("ArtifactFile") })
if err != nil {
return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %w", err)
}
props.ArtifactPath = artifactFile
}
props.ArtifactBytes = artifactBytes
return *props, cleanups, nil
}
func tarFilesToBytes(artifactFiles []*fuzz.TarFile, artifactType string) ([]byte, error) {
switch artifactType {
case "jarV001":
return tarfilesToJar(artifactFiles)
default:
return defaultTarToBytes(artifactFiles)
}
}
func defaultTarToBytes(artifactFiles []*fuzz.TarFile) ([]byte, error) {
b := new(bytes.Buffer)
w := zip.NewWriter(b)
for _, file := range artifactFiles {
f, err := w.Create(file.Hdr.Name)
if err != nil {
continue
}
_, _ = f.Write(file.Body)
}
w.Close()
return b.Bytes(), nil
}
func SetFuzzLogger() {
config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(zapcore.FatalLevel)
logger, err := config.Build()
if err != nil {
panic(err)
}
log.Logger = logger.Named("rekor-fuzz-logger").Sugar()
}
//
// Copyright 2023 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 fuzz
import (
"archive/tar"
"archive/zip"
"bytes"
"context"
"crypto"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"os"
"time"
fuzz "github.com/AdamKorcz/go-fuzz-headers-1"
"github.com/sassoftware/relic/lib/zipslicer"
"github.com/sassoftware/relic/v7/lib/certloader"
"github.com/sassoftware/relic/v7/lib/signjar"
)
var (
CertPrivateKey *rsa.PrivateKey
Certificate *x509.Certificate
)
// copy pasted from rekor/pkg/pki/x509/e2e.go
const RSACert = `-----BEGIN CERTIFICATE-----
MIIDOjCCAiKgAwIBAgIUEP925shVBKERFCsymdSqESLZFyMwDQYJKoZIhvcNAQEL
BQAwHzEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYwHhcNMjEwNDIxMjAy
ODAzWhcNMjEwNTIxMjAyODAzWjAfMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHJla29y
LmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN8KiP08rFIik4GN
W8/sHSXxDopeDBLEQEihsyXXWesfYW/q59lFaCZrsTetlyNEzKDJ+JrpIHwoGOo4
EwefFfvy2nkgPFs9aeIDsYZNZnIGxeB8sUfsZUYGHx+Ikm18vhM//GYzNjjuvHyq
+CWRAOS12ZISa99iah/lIhcP8IEj1gPGldAH0QFx3XpCePAdQocSU6ziVkj054/x
NJXy1bKySrVw7gvE9LxZlVO9urSOnzg7BBOla0mob8NRDVB8yN+LG365q4IMDzuI
jAEL6sLtoJ9pcemo1rIfNOhSLYlzfg7oszJ8eCjASNCCcp6EKVjhW7LRoldC8oGZ
EOrKM78CAwEAAaNuMGwwHQYDVR0OBBYEFGjs8EHKT3x1itwwptJLuQQg/hQcMB8G
A1UdIwQYMBaAFGjs8EHKT3x1itwwptJLuQQg/hQcMA8GA1UdEwEB/wQFMAMBAf8w
GQYDVR0RBBIwEIEOdGVzdEByZWtvci5kZXYwDQYJKoZIhvcNAQELBQADggEBAAHE
bYuePN3XpM7pHoCz6g4uTHu0VrezqJyK1ohysgWJmSJzzazUeISXk0xWnHPk1Zxi
kzoEuysI8b0P7yodMA8e16zbIOL6QbGe3lNXYqRIg+bl+4OPFGVMX8xHNZmeh0kD
vX1JVS+y9uyo4/z/pm0JhaSCn85ft/Y5uXMQYn1wFR5DAcJH+iWjNX4fipGxGRE9
Cy0DjFnYJ3SRY4HPQ0oUSQmyhrwe2DiYzeqtbL2KJBXPcFQKWhkf/fupdYFljvcH
d9NNfRb0p2oFGG/J0ROg9pEcP1/aZP5k8P2pRdt3y7h1MAtmg2bgEdugZgXwAUmM
BmU8k2FeTuqV15piPCE=
-----END CERTIFICATE-----`
// copy pasted from rekor/pkg/pki/x509/e2e.go
const RSAKey = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDfCoj9PKxSIpOB
jVvP7B0l8Q6KXgwSxEBIobMl11nrH2Fv6ufZRWgma7E3rZcjRMygyfia6SB8KBjq
OBMHnxX78tp5IDxbPWniA7GGTWZyBsXgfLFH7GVGBh8fiJJtfL4TP/xmMzY47rx8
qvglkQDktdmSEmvfYmof5SIXD/CBI9YDxpXQB9EBcd16QnjwHUKHElOs4lZI9OeP
8TSV8tWyskq1cO4LxPS8WZVTvbq0jp84OwQTpWtJqG/DUQ1QfMjfixt+uauCDA87
iIwBC+rC7aCfaXHpqNayHzToUi2Jc34O6LMyfHgowEjQgnKehClY4Vuy0aJXQvKB
mRDqyjO/AgMBAAECggEBAIHOAs3Gis8+WjRSjXVjh882DG1QsJwXZQYgPT+vpiAl
YjKdNpOHRkbd9ARgXY5kEuccxDd7p7E6MM3XFpQf7M51ltpZfWboRgAIgD+WOiHw
eSbdytr95C6tj11twTJBH+naGk1sTokxv7aaVdKfIjL49oeBexBFmVe4pW9gkmrE
1z1y1a0RohqbZ0kprYPWjz5UhsNqbCzgkdDqS7IrcOwVg6zvKYFjHnqIHqaJXVif
FgIfoNt7tz+12FTHI+6OkKoN3YCJueaxneBhITXm6RLOpQWa9qhdUPbkJ9vQNfph
Qqke4faaxKY9UDma+GpEHR016AWufZp92pd9wQkDn0kCgYEA7w/ZizAkefHoZhZ8
Isn/fYu4fdtUaVgrnGUVZobiGxWrHRU9ikbAwR7UwbgRSfppGiJdAMq1lyH2irmb
4OHU64rjuYSlIqUWHLQHWmqUbLUvlDojH/vdmH/Zn0AbrLZaimC5UCjK3Eb7sAMq
G0tGeDX2JraQvx7KrbC6peTaaaMCgYEA7tgZBiRCQJ7+mNu+gX9x6OXtjsDCh516
vToRLkxWc7LAbC9LKsuEHl4e3vy1PY/nyuv12Ng2dBq4WDXozAmVgz0ok7rRlIFp
w8Yj8o/9KuGZkD/7tw/pLsVc9Q3Wf0ACrnAAh7+3dAvn3yg+WHwXzqWIbrseDPt9
ILCfUoNDpzUCgYAKFCX8y0PObFd67lm/cbq2xUw66iNN6ay1BEH5t5gSwkAbksis
ar03pyAbJrJ75vXFZ0t6fBFZ1NG7GYYr3fmHEKz3JlN7+W/MN/7TXgjx6FWgLy9J
6ul1w3YeU6qXBn0ctmU5ru6WiNuVmRyOWAcZjFTbXvkNRbQPzJKh6dsXdwKBgA1D
FIihxMf/zBVCxl48bF/JPJqbm3GaTfFp4wBWHsrH1yVqrtrOeCSTh1VMZOfpMK60
0W7b+pIR1cCYJbgGpDWoVLN3QSHk2bGUM/TJB/60jilTVC/DA2ikbtfwj8N7E2sK
Lw1amN4ptxNOEcAqC8xepqe3XiDMahNBm2cigMQtAoGBAKwrXvss2BKz+/6poJQU
A0c7jhMN8M9Y5S2Ockw07lrQeAgfu4q+/8ztm0NeHJbk01IJvJY5Nt7bSgwgNVlo
j7vR2BMAc9U73Ju9aeTl/L6GqmZyA+Ojhl5gA5DPZYqNiqi93ydgRaI6n4+o3dI7
5wnr40AmbuKCDvMOvN7nMybL
-----END PRIVATE KEY-----`
// copy pasted from rekor/pkg/pki/x509/e2e.go
func init() {
p, _ := pem.Decode([]byte(RSAKey))
priv, err := x509.ParsePKCS8PrivateKey(p.Bytes)
if err != nil {
panic(err)
}
cpk, ok := priv.(*rsa.PrivateKey)
if !ok {
panic("unsuccessful conversion")
}
CertPrivateKey = cpk
p, _ = pem.Decode([]byte(RSACert))
Certificate, err = x509.ParseCertificate(p.Bytes)
if err != nil {
panic(err)
}
}
// Creates jar artifact files.
func createJarArtifactFiles(ff *fuzz.ConsumeFuzzer) ([]*fuzz.TarFile, error) {
var files []*fuzz.TarFile
files, err := ff.TarFiles()
if err != nil {
return files, err
}
if len(files) <= 1 {
return files, err
}
for _, file := range files {
if len(file.Body) == 0 {
return files, errors.New("created an empty file")
}
}
// add "META-INF/MANIFEST.MF"
mfContents, err := ff.GetBytes()
if err != nil {
return files, err
}
// check the manifest early. This is an inexpensive check,
// so we want to call it before compressing.
_, err = signjar.ParseManifest(mfContents)
if err != nil {
return files, err
}
files = append(files, &fuzz.TarFile{
Hdr: &tar.Header{
Name: "META-INF/MANIFEST.MF",
Size: int64(len(mfContents)),
Mode: 0o600,
ModTime: time.Unix(int64(123), int64(456)),
},
Body: mfContents,
})
return files, nil
}
func tarfilesToJar(artifactFiles []*fuzz.TarFile) ([]byte, error) {
var jarBytes []byte
f, err := os.Create("artifactFile")
if err != nil {
return jarBytes, err
}
defer f.Close()
defer os.Remove("artifactFile")
zw := zip.NewWriter(f)
for _, zipFile := range artifactFiles {
jw, err := zw.Create(zipFile.Hdr.Name)
if err != nil {
zw.Close()
return jarBytes, err
}
_, err = jw.Write(zipFile.Body)
if err != nil {
continue
}
}
zw.Close()
err = f.Sync()
if err != nil {
return jarBytes, err
}
buf := bytes.Buffer{}
err = zipslicer.ZipToTar(f, &buf)
if err != nil {
return jarBytes, err
}
jd, err := signjar.DigestJarStream(&buf, crypto.SHA256)
if err != nil {
os.Remove("artifactFile")
return jarBytes, err
}
c := certloader.Certificate{
PrivateKey: CertPrivateKey,
Leaf: Certificate,
}
patch, _, err := jd.Sign(context.Background(), &c, "rekor", false, true, false)
if err != nil {
return jarBytes, err
}
if err := patch.Apply(f, "artifactFile"); err != nil {
return jarBytes, err
}
f.Close()
artifactBytes, err := os.ReadFile("artifactFile")
if err != nil {
return jarBytes, err
}
return artifactBytes, nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Alpine Alpine package
//
// swagger:model alpine
type Alpine struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec AlpineSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Alpine) Kind() string {
return "alpine"
}
// SetKind sets the kind of this subtype
func (m *Alpine) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Alpine) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec AlpineSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Alpine
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Alpine) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec AlpineSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this alpine
func (m *Alpine) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Alpine) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Alpine) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this alpine based on the context it is used
func (m *Alpine) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Alpine) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Alpine) UnmarshalBinary(b []byte) error {
var res Alpine
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// AlpineV001Schema Alpine v0.0.1 Schema
//
// # Schema for Alpine Package entries
//
// swagger:model alpineV001Schema
type AlpineV001Schema struct {
// package
// Required: true
Package *AlpineV001SchemaPackage `json:"package"`
// public key
// Required: true
PublicKey *AlpineV001SchemaPublicKey `json:"publicKey"`
}
// Validate validates this alpine v001 schema
func (m *AlpineV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePackage(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *AlpineV001Schema) validatePackage(formats strfmt.Registry) error {
if err := validate.Required("package", "body", m.Package); err != nil {
return err
}
if m.Package != nil {
if err := m.Package.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package")
}
return err
}
}
return nil
}
func (m *AlpineV001Schema) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this alpine v001 schema based on the context it is used
func (m *AlpineV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidatePackage(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *AlpineV001Schema) contextValidatePackage(ctx context.Context, formats strfmt.Registry) error {
if m.Package != nil {
if err := m.Package.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package")
}
return err
}
}
return nil
}
func (m *AlpineV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *AlpineV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AlpineV001Schema) UnmarshalBinary(b []byte) error {
var res AlpineV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// AlpineV001SchemaPackage Information about the package associated with the entry
//
// swagger:model AlpineV001SchemaPackage
type AlpineV001SchemaPackage struct {
// Specifies the package inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// hash
Hash *AlpineV001SchemaPackageHash `json:"hash,omitempty"`
// Values of the .PKGINFO key / value pairs
// Read Only: true
Pkginfo map[string]string `json:"pkginfo,omitempty"`
}
// Validate validates this alpine v001 schema package
func (m *AlpineV001SchemaPackage) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *AlpineV001SchemaPackage) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package" + "." + "hash")
}
return err
}
}
return nil
}
// ContextValidate validate this alpine v001 schema package based on the context it is used
func (m *AlpineV001SchemaPackage) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePkginfo(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *AlpineV001SchemaPackage) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package" + "." + "hash")
}
return err
}
}
return nil
}
func (m *AlpineV001SchemaPackage) contextValidatePkginfo(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *AlpineV001SchemaPackage) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AlpineV001SchemaPackage) UnmarshalBinary(b []byte) error {
var res AlpineV001SchemaPackage
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// AlpineV001SchemaPackageHash Specifies the hash algorithm and value for the package
//
// swagger:model AlpineV001SchemaPackageHash
type AlpineV001SchemaPackageHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the package
// Required: true
Value *string `json:"value"`
}
// Validate validates this alpine v001 schema package hash
func (m *AlpineV001SchemaPackageHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var alpineV001SchemaPackageHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
alpineV001SchemaPackageHashTypeAlgorithmPropEnum = append(alpineV001SchemaPackageHashTypeAlgorithmPropEnum, v)
}
}
const (
// AlpineV001SchemaPackageHashAlgorithmSha256 captures enum value "sha256"
AlpineV001SchemaPackageHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *AlpineV001SchemaPackageHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, alpineV001SchemaPackageHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *AlpineV001SchemaPackageHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("package"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("package"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *AlpineV001SchemaPackageHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("package"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this alpine v001 schema package hash based on the context it is used
func (m *AlpineV001SchemaPackageHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *AlpineV001SchemaPackageHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AlpineV001SchemaPackageHash) UnmarshalBinary(b []byte) error {
var res AlpineV001SchemaPackageHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// AlpineV001SchemaPublicKey The public key that can verify the package signature
//
// swagger:model AlpineV001SchemaPublicKey
type AlpineV001SchemaPublicKey struct {
// Specifies the content of the public key inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this alpine v001 schema public key
func (m *AlpineV001SchemaPublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *AlpineV001SchemaPublicKey) validateContent(formats strfmt.Registry) error {
if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validates this alpine v001 schema public key based on context it is used
func (m *AlpineV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *AlpineV001SchemaPublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AlpineV001SchemaPublicKey) UnmarshalBinary(b []byte) error {
var res AlpineV001SchemaPublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// ConsistencyProof consistency proof
//
// swagger:model ConsistencyProof
type ConsistencyProof struct {
// hashes
// Required: true
Hashes []string `json:"hashes"`
// The hash value stored at the root of the merkle tree at the time the proof was generated
// Required: true
// Pattern: ^[0-9a-fA-F]{64}$
RootHash *string `json:"rootHash"`
}
// Validate validates this consistency proof
func (m *ConsistencyProof) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHashes(formats); err != nil {
res = append(res, err)
}
if err := m.validateRootHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ConsistencyProof) validateHashes(formats strfmt.Registry) error {
if err := validate.Required("hashes", "body", m.Hashes); err != nil {
return err
}
for i := 0; i < len(m.Hashes); i++ {
if err := validate.Pattern("hashes"+"."+strconv.Itoa(i), "body", m.Hashes[i], `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
}
return nil
}
func (m *ConsistencyProof) validateRootHash(formats strfmt.Registry) error {
if err := validate.Required("rootHash", "body", m.RootHash); err != nil {
return err
}
if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
return nil
}
// ContextValidate validates this consistency proof based on context it is used
func (m *ConsistencyProof) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *ConsistencyProof) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ConsistencyProof) UnmarshalBinary(b []byte) error {
var res ConsistencyProof
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Cose COSE object
//
// swagger:model cose
type Cose struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec CoseSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Cose) Kind() string {
return "cose"
}
// SetKind sets the kind of this subtype
func (m *Cose) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Cose) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec CoseSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Cose
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Cose) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec CoseSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this cose
func (m *Cose) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Cose) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Cose) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this cose based on the context it is used
func (m *Cose) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Cose) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Cose) UnmarshalBinary(b []byte) error {
var res Cose
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// CoseV001Schema cose v0.0.1 Schema
//
// # Schema for cose object
//
// swagger:model coseV001Schema
type CoseV001Schema struct {
// data
Data *CoseV001SchemaData `json:"data,omitempty"`
// The COSE Sign1 Message
// Format: byte
Message strfmt.Base64 `json:"message,omitempty"`
// The public key that can verify the signature
// Required: true
// Format: byte
PublicKey *strfmt.Base64 `json:"publicKey"`
}
// Validate validates this cose v001 schema
func (m *CoseV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateData(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CoseV001Schema) validateData(formats strfmt.Registry) error {
if swag.IsZero(m.Data) { // not required
return nil
}
if m.Data != nil {
if err := m.Data.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
func (m *CoseV001Schema) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
return nil
}
// ContextValidate validate this cose v001 schema based on the context it is used
func (m *CoseV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateData(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CoseV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error {
if m.Data != nil {
if swag.IsZero(m.Data) { // not required
return nil
}
if err := m.Data.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *CoseV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CoseV001Schema) UnmarshalBinary(b []byte) error {
var res CoseV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// CoseV001SchemaData Information about the content associated with the entry
//
// swagger:model CoseV001SchemaData
type CoseV001SchemaData struct {
// Specifies the additional authenticated data required to verify the signature
// Format: byte
Aad strfmt.Base64 `json:"aad,omitempty"`
// envelope hash
EnvelopeHash *CoseV001SchemaDataEnvelopeHash `json:"envelopeHash,omitempty"`
// payload hash
PayloadHash *CoseV001SchemaDataPayloadHash `json:"payloadHash,omitempty"`
}
// Validate validates this cose v001 schema data
func (m *CoseV001SchemaData) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEnvelopeHash(formats); err != nil {
res = append(res, err)
}
if err := m.validatePayloadHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CoseV001SchemaData) validateEnvelopeHash(formats strfmt.Registry) error {
if swag.IsZero(m.EnvelopeHash) { // not required
return nil
}
if m.EnvelopeHash != nil {
if err := m.EnvelopeHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "envelopeHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "envelopeHash")
}
return err
}
}
return nil
}
func (m *CoseV001SchemaData) validatePayloadHash(formats strfmt.Registry) error {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if m.PayloadHash != nil {
if err := m.PayloadHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "payloadHash")
}
return err
}
}
return nil
}
// ContextValidate validate this cose v001 schema data based on the context it is used
func (m *CoseV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateEnvelopeHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *CoseV001SchemaData) contextValidateEnvelopeHash(ctx context.Context, formats strfmt.Registry) error {
if m.EnvelopeHash != nil {
if swag.IsZero(m.EnvelopeHash) { // not required
return nil
}
if err := m.EnvelopeHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "envelopeHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "envelopeHash")
}
return err
}
}
return nil
}
func (m *CoseV001SchemaData) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
if m.PayloadHash != nil {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "payloadHash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *CoseV001SchemaData) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CoseV001SchemaData) UnmarshalBinary(b []byte) error {
var res CoseV001SchemaData
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// CoseV001SchemaDataEnvelopeHash Specifies the hash algorithm and value for the COSE envelope
//
// swagger:model CoseV001SchemaDataEnvelopeHash
type CoseV001SchemaDataEnvelopeHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the envelope
// Required: true
Value *string `json:"value"`
}
// Validate validates this cose v001 schema data envelope hash
func (m *CoseV001SchemaDataEnvelopeHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum = append(coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum, v)
}
}
const (
// CoseV001SchemaDataEnvelopeHashAlgorithmSha256 captures enum value "sha256"
CoseV001SchemaDataEnvelopeHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *CoseV001SchemaDataEnvelopeHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *CoseV001SchemaDataEnvelopeHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"envelopeHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("data"+"."+"envelopeHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *CoseV001SchemaDataEnvelopeHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"envelopeHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this cose v001 schema data envelope hash based on the context it is used
func (m *CoseV001SchemaDataEnvelopeHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *CoseV001SchemaDataEnvelopeHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CoseV001SchemaDataEnvelopeHash) UnmarshalBinary(b []byte) error {
var res CoseV001SchemaDataEnvelopeHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// CoseV001SchemaDataPayloadHash Specifies the hash algorithm and value for the content
//
// swagger:model CoseV001SchemaDataPayloadHash
type CoseV001SchemaDataPayloadHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the content
// Required: true
Value *string `json:"value"`
}
// Validate validates this cose v001 schema data payload hash
func (m *CoseV001SchemaDataPayloadHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum = append(coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum, v)
}
}
const (
// CoseV001SchemaDataPayloadHashAlgorithmSha256 captures enum value "sha256"
CoseV001SchemaDataPayloadHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *CoseV001SchemaDataPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *CoseV001SchemaDataPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("data"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *CoseV001SchemaDataPayloadHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this cose v001 schema data payload hash based on the context it is used
func (m *CoseV001SchemaDataPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *CoseV001SchemaDataPayloadHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CoseV001SchemaDataPayloadHash) UnmarshalBinary(b []byte) error {
var res CoseV001SchemaDataPayloadHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// DSSE DSSE envelope
//
// swagger:model dsse
type DSSE struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec DSSESchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *DSSE) Kind() string {
return "dsse"
}
// SetKind sets the kind of this subtype
func (m *DSSE) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *DSSE) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec DSSESchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result DSSE
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m DSSE) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec DSSESchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this dsse
func (m *DSSE) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *DSSE) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *DSSE) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this dsse based on the context it is used
func (m *DSSE) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *DSSE) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSE) UnmarshalBinary(b []byte) error {
var res DSSE
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// DSSEV001Schema DSSE v0.0.1 Schema
//
// # Schema for DSSE envelopes
//
// swagger:model dsseV001Schema
type DSSEV001Schema struct {
// envelope hash
EnvelopeHash *DSSEV001SchemaEnvelopeHash `json:"envelopeHash,omitempty"`
// payload hash
PayloadHash *DSSEV001SchemaPayloadHash `json:"payloadHash,omitempty"`
// proposed content
ProposedContent *DSSEV001SchemaProposedContent `json:"proposedContent,omitempty"`
// extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings
// Read Only: true
// Min Items: 1
Signatures []*DSSEV001SchemaSignaturesItems0 `json:"signatures"`
}
// Validate validates this dsse v001 schema
func (m *DSSEV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEnvelopeHash(formats); err != nil {
res = append(res, err)
}
if err := m.validatePayloadHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateProposedContent(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignatures(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *DSSEV001Schema) validateEnvelopeHash(formats strfmt.Registry) error {
if swag.IsZero(m.EnvelopeHash) { // not required
return nil
}
if m.EnvelopeHash != nil {
if err := m.EnvelopeHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("envelopeHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("envelopeHash")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) validatePayloadHash(formats strfmt.Registry) error {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if m.PayloadHash != nil {
if err := m.PayloadHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("payloadHash")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) validateProposedContent(formats strfmt.Registry) error {
if swag.IsZero(m.ProposedContent) { // not required
return nil
}
if m.ProposedContent != nil {
if err := m.ProposedContent.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("proposedContent")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("proposedContent")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) validateSignatures(formats strfmt.Registry) error {
if swag.IsZero(m.Signatures) { // not required
return nil
}
iSignaturesSize := int64(len(m.Signatures))
if err := validate.MinItems("signatures", "body", iSignaturesSize, 1); err != nil {
return err
}
for i := 0; i < len(m.Signatures); i++ {
if swag.IsZero(m.Signatures[i]) { // not required
continue
}
if m.Signatures[i] != nil {
if err := m.Signatures[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signatures" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signatures" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this dsse v001 schema based on the context it is used
func (m *DSSEV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateEnvelopeHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateProposedContent(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSignatures(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *DSSEV001Schema) contextValidateEnvelopeHash(ctx context.Context, formats strfmt.Registry) error {
if m.EnvelopeHash != nil {
if swag.IsZero(m.EnvelopeHash) { // not required
return nil
}
if err := m.EnvelopeHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("envelopeHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("envelopeHash")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
if m.PayloadHash != nil {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("payloadHash")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) contextValidateProposedContent(ctx context.Context, formats strfmt.Registry) error {
if m.ProposedContent != nil {
if swag.IsZero(m.ProposedContent) { // not required
return nil
}
if err := m.ProposedContent.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("proposedContent")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("proposedContent")
}
return err
}
}
return nil
}
func (m *DSSEV001Schema) contextValidateSignatures(ctx context.Context, formats strfmt.Registry) error {
if err := validate.ReadOnly(ctx, "signatures", "body", []*DSSEV001SchemaSignaturesItems0(m.Signatures)); err != nil {
return err
}
for i := 0; i < len(m.Signatures); i++ {
if m.Signatures[i] != nil {
if swag.IsZero(m.Signatures[i]) { // not required
return nil
}
if err := m.Signatures[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signatures" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signatures" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *DSSEV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSEV001Schema) UnmarshalBinary(b []byte) error {
var res DSSEV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// DSSEV001SchemaEnvelopeHash Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor
//
// swagger:model DSSEV001SchemaEnvelopeHash
type DSSEV001SchemaEnvelopeHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The value of the computed digest over the entire envelope
// Required: true
Value *string `json:"value"`
}
// Validate validates this DSSE v001 schema envelope hash
func (m *DSSEV001SchemaEnvelopeHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum = append(dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, v)
}
}
const (
// DSSEV001SchemaEnvelopeHashAlgorithmSha256 captures enum value "sha256"
DSSEV001SchemaEnvelopeHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("envelopeHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("envelopeHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaEnvelopeHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("envelopeHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this DSSE v001 schema envelope hash based on the context it is used
func (m *DSSEV001SchemaEnvelopeHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *DSSEV001SchemaEnvelopeHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSEV001SchemaEnvelopeHash) UnmarshalBinary(b []byte) error {
var res DSSEV001SchemaEnvelopeHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// DSSEV001SchemaPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope
//
// swagger:model DSSEV001SchemaPayloadHash
type DSSEV001SchemaPayloadHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The value of the computed digest over the payload within the envelope
// Required: true
Value *string `json:"value"`
}
// Validate validates this DSSE v001 schema payload hash
func (m *DSSEV001SchemaPayloadHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var dsseV001SchemaPayloadHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
dsseV001SchemaPayloadHashTypeAlgorithmPropEnum = append(dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, v)
}
}
const (
// DSSEV001SchemaPayloadHashAlgorithmSha256 captures enum value "sha256"
DSSEV001SchemaPayloadHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *DSSEV001SchemaPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaPayloadHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("payloadHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this DSSE v001 schema payload hash based on the context it is used
func (m *DSSEV001SchemaPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *DSSEV001SchemaPayloadHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSEV001SchemaPayloadHash) UnmarshalBinary(b []byte) error {
var res DSSEV001SchemaPayloadHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// DSSEV001SchemaProposedContent DSSE v001 schema proposed content
//
// swagger:model DSSEV001SchemaProposedContent
type DSSEV001SchemaProposedContent struct {
// DSSE envelope specified as a stringified JSON object
// Required: true
Envelope *string `json:"envelope"`
// collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings
// Required: true
// Min Items: 1
Verifiers []strfmt.Base64 `json:"verifiers"`
}
// Validate validates this DSSE v001 schema proposed content
func (m *DSSEV001SchemaProposedContent) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEnvelope(formats); err != nil {
res = append(res, err)
}
if err := m.validateVerifiers(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *DSSEV001SchemaProposedContent) validateEnvelope(formats strfmt.Registry) error {
if err := validate.Required("proposedContent"+"."+"envelope", "body", m.Envelope); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaProposedContent) validateVerifiers(formats strfmt.Registry) error {
if err := validate.Required("proposedContent"+"."+"verifiers", "body", m.Verifiers); err != nil {
return err
}
iVerifiersSize := int64(len(m.Verifiers))
if err := validate.MinItems("proposedContent"+"."+"verifiers", "body", iVerifiersSize, 1); err != nil {
return err
}
return nil
}
// ContextValidate validates this DSSE v001 schema proposed content based on context it is used
func (m *DSSEV001SchemaProposedContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *DSSEV001SchemaProposedContent) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSEV001SchemaProposedContent) UnmarshalBinary(b []byte) error {
var res DSSEV001SchemaProposedContent
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// DSSEV001SchemaSignaturesItems0 a signature of the envelope's payload along with the verification material for the signature
//
// swagger:model DSSEV001SchemaSignaturesItems0
type DSSEV001SchemaSignaturesItems0 struct {
// base64 encoded signature of the payload
// Required: true
// Pattern: ^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$
Signature *string `json:"signature"`
// verification material that was used to verify the corresponding signature, specified as a base64 encoded string
// Required: true
// Format: byte
Verifier *strfmt.Base64 `json:"verifier"`
}
// Validate validates this DSSE v001 schema signatures items0
func (m *DSSEV001SchemaSignaturesItems0) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateSignature(formats); err != nil {
res = append(res, err)
}
if err := m.validateVerifier(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *DSSEV001SchemaSignaturesItems0) validateSignature(formats strfmt.Registry) error {
if err := validate.Required("signature", "body", m.Signature); err != nil {
return err
}
if err := validate.Pattern("signature", "body", *m.Signature, `^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$`); err != nil {
return err
}
return nil
}
func (m *DSSEV001SchemaSignaturesItems0) validateVerifier(formats strfmt.Registry) error {
if err := validate.Required("verifier", "body", m.Verifier); err != nil {
return err
}
return nil
}
// ContextValidate validates this DSSE v001 schema signatures items0 based on context it is used
func (m *DSSEV001SchemaSignaturesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *DSSEV001SchemaSignaturesItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *DSSEV001SchemaSignaturesItems0) UnmarshalBinary(b []byte) error {
var res DSSEV001SchemaSignaturesItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// Error error
//
// swagger:model Error
type Error struct {
// code
Code int64 `json:"code,omitempty"`
// message
Message string `json:"message,omitempty"`
}
// Validate validates this error
func (m *Error) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this error based on context it is used
func (m *Error) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *Error) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Error) UnmarshalBinary(b []byte) error {
var res Error
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Hashedrekord Hashed Rekord object
//
// swagger:model hashedrekord
type Hashedrekord struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HashedrekordSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Hashedrekord) Kind() string {
return "hashedrekord"
}
// SetKind sets the kind of this subtype
func (m *Hashedrekord) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Hashedrekord) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HashedrekordSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Hashedrekord
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Hashedrekord) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HashedrekordSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this hashedrekord
func (m *Hashedrekord) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Hashedrekord) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Hashedrekord) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this hashedrekord based on the context it is used
func (m *Hashedrekord) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Hashedrekord) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Hashedrekord) UnmarshalBinary(b []byte) error {
var res Hashedrekord
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// HashedrekordV001Schema Hashed Rekor v0.0.1 Schema
//
// # Schema for Hashed Rekord object
//
// swagger:model hashedrekordV001Schema
type HashedrekordV001Schema struct {
// data
// Required: true
Data *HashedrekordV001SchemaData `json:"data"`
// signature
// Required: true
Signature *HashedrekordV001SchemaSignature `json:"signature"`
}
// Validate validates this hashedrekord v001 schema
func (m *HashedrekordV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateData(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignature(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001Schema) validateData(formats strfmt.Registry) error {
if err := validate.Required("data", "body", m.Data); err != nil {
return err
}
if m.Data != nil {
if err := m.Data.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
func (m *HashedrekordV001Schema) validateSignature(formats strfmt.Registry) error {
if err := validate.Required("signature", "body", m.Signature); err != nil {
return err
}
if m.Signature != nil {
if err := m.Signature.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// ContextValidate validate this hashedrekord v001 schema based on the context it is used
func (m *HashedrekordV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateData(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSignature(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error {
if m.Data != nil {
if err := m.Data.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
func (m *HashedrekordV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error {
if m.Signature != nil {
if err := m.Signature.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HashedrekordV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HashedrekordV001Schema) UnmarshalBinary(b []byte) error {
var res HashedrekordV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HashedrekordV001SchemaData Information about the content associated with the entry
//
// swagger:model HashedrekordV001SchemaData
type HashedrekordV001SchemaData struct {
// hash
Hash *HashedrekordV001SchemaDataHash `json:"hash,omitempty"`
}
// Validate validates this hashedrekord v001 schema data
func (m *HashedrekordV001SchemaData) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001SchemaData) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "hash")
}
return err
}
}
return nil
}
// ContextValidate validate this hashedrekord v001 schema data based on the context it is used
func (m *HashedrekordV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001SchemaData) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "hash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HashedrekordV001SchemaData) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HashedrekordV001SchemaData) UnmarshalBinary(b []byte) error {
var res HashedrekordV001SchemaData
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HashedrekordV001SchemaDataHash Specifies the hash algorithm and value for the content
//
// swagger:model HashedrekordV001SchemaDataHash
type HashedrekordV001SchemaDataHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256","sha384","sha512"]
Algorithm *string `json:"algorithm"`
// The hash value for the content, as represented by a lower case hexadecimal string
// Required: true
Value *string `json:"value"`
}
// Validate validates this hashedrekord v001 schema data hash
func (m *HashedrekordV001SchemaDataHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256","sha384","sha512"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum = append(hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, v)
}
}
const (
// HashedrekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256"
HashedrekordV001SchemaDataHashAlgorithmSha256 string = "sha256"
// HashedrekordV001SchemaDataHashAlgorithmSha384 captures enum value "sha384"
HashedrekordV001SchemaDataHashAlgorithmSha384 string = "sha384"
// HashedrekordV001SchemaDataHashAlgorithmSha512 captures enum value "sha512"
HashedrekordV001SchemaDataHashAlgorithmSha512 string = "sha512"
)
// prop value enum
func (m *HashedrekordV001SchemaDataHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *HashedrekordV001SchemaDataHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("data"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *HashedrekordV001SchemaDataHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validates this hashedrekord v001 schema data hash based on context it is used
func (m *HashedrekordV001SchemaDataHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *HashedrekordV001SchemaDataHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HashedrekordV001SchemaDataHash) UnmarshalBinary(b []byte) error {
var res HashedrekordV001SchemaDataHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HashedrekordV001SchemaSignature Information about the detached signature associated with the entry
//
// swagger:model HashedrekordV001SchemaSignature
type HashedrekordV001SchemaSignature struct {
// Specifies the content of the signature inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// public key
PublicKey *HashedrekordV001SchemaSignaturePublicKey `json:"publicKey,omitempty"`
}
// Validate validates this hashedrekord v001 schema signature
func (m *HashedrekordV001SchemaSignature) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error {
if swag.IsZero(m.PublicKey) { // not required
return nil
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this hashedrekord v001 schema signature based on the context it is used
func (m *HashedrekordV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HashedrekordV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if swag.IsZero(m.PublicKey) { // not required
return nil
}
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HashedrekordV001SchemaSignature) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HashedrekordV001SchemaSignature) UnmarshalBinary(b []byte) error {
var res HashedrekordV001SchemaSignature
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HashedrekordV001SchemaSignaturePublicKey The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information
//
// swagger:model HashedrekordV001SchemaSignaturePublicKey
type HashedrekordV001SchemaSignaturePublicKey struct {
// Specifies the content of the public key or code signing certificate inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
}
// Validate validates this hashedrekord v001 schema signature public key
func (m *HashedrekordV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this hashedrekord v001 schema signature public key based on context it is used
func (m *HashedrekordV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *HashedrekordV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HashedrekordV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error {
var res HashedrekordV001SchemaSignaturePublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Helm Helm chart
//
// swagger:model helm
type Helm struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HelmSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Helm) Kind() string {
return "helm"
}
// SetKind sets the kind of this subtype
func (m *Helm) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Helm) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HelmSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Helm
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Helm) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec HelmSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this helm
func (m *Helm) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Helm) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Helm) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this helm based on the context it is used
func (m *Helm) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Helm) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Helm) UnmarshalBinary(b []byte) error {
var res Helm
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// HelmV001Schema Helm v0.0.1 Schema
//
// # Schema for Helm object
//
// swagger:model helmV001Schema
type HelmV001Schema struct {
// chart
// Required: true
Chart *HelmV001SchemaChart `json:"chart"`
// public key
// Required: true
PublicKey *HelmV001SchemaPublicKey `json:"publicKey"`
}
// Validate validates this helm v001 schema
func (m *HelmV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateChart(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001Schema) validateChart(formats strfmt.Registry) error {
if err := validate.Required("chart", "body", m.Chart); err != nil {
return err
}
if m.Chart != nil {
if err := m.Chart.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart")
}
return err
}
}
return nil
}
func (m *HelmV001Schema) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this helm v001 schema based on the context it is used
func (m *HelmV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateChart(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001Schema) contextValidateChart(ctx context.Context, formats strfmt.Registry) error {
if m.Chart != nil {
if err := m.Chart.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart")
}
return err
}
}
return nil
}
func (m *HelmV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001Schema) UnmarshalBinary(b []byte) error {
var res HelmV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HelmV001SchemaChart Information about the Helm chart associated with the entry
//
// swagger:model HelmV001SchemaChart
type HelmV001SchemaChart struct {
// hash
Hash *HelmV001SchemaChartHash `json:"hash,omitempty"`
// provenance
// Required: true
Provenance *HelmV001SchemaChartProvenance `json:"provenance"`
}
// Validate validates this helm v001 schema chart
func (m *HelmV001SchemaChart) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateProvenance(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChart) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "hash")
}
return err
}
}
return nil
}
func (m *HelmV001SchemaChart) validateProvenance(formats strfmt.Registry) error {
if err := validate.Required("chart"+"."+"provenance", "body", m.Provenance); err != nil {
return err
}
if m.Provenance != nil {
if err := m.Provenance.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "provenance")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "provenance")
}
return err
}
}
return nil
}
// ContextValidate validate this helm v001 schema chart based on the context it is used
func (m *HelmV001SchemaChart) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateProvenance(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChart) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "hash")
}
return err
}
}
return nil
}
func (m *HelmV001SchemaChart) contextValidateProvenance(ctx context.Context, formats strfmt.Registry) error {
if m.Provenance != nil {
if err := m.Provenance.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "provenance")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "provenance")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001SchemaChart) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001SchemaChart) UnmarshalBinary(b []byte) error {
var res HelmV001SchemaChart
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HelmV001SchemaChartHash Specifies the hash algorithm and value for the chart
//
// swagger:model HelmV001SchemaChartHash
type HelmV001SchemaChartHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the chart
// Required: true
Value *string `json:"value"`
}
// Validate validates this helm v001 schema chart hash
func (m *HelmV001SchemaChartHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var helmV001SchemaChartHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
helmV001SchemaChartHashTypeAlgorithmPropEnum = append(helmV001SchemaChartHashTypeAlgorithmPropEnum, v)
}
}
const (
// HelmV001SchemaChartHashAlgorithmSha256 captures enum value "sha256"
HelmV001SchemaChartHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *HelmV001SchemaChartHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, helmV001SchemaChartHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *HelmV001SchemaChartHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("chart"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("chart"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *HelmV001SchemaChartHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("chart"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this helm v001 schema chart hash based on the context it is used
func (m *HelmV001SchemaChartHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001SchemaChartHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001SchemaChartHash) UnmarshalBinary(b []byte) error {
var res HelmV001SchemaChartHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HelmV001SchemaChartProvenance The provenance entry associated with the signed Helm Chart
//
// swagger:model HelmV001SchemaChartProvenance
type HelmV001SchemaChartProvenance struct {
// Specifies the content of the provenance file inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// signature
Signature *HelmV001SchemaChartProvenanceSignature `json:"signature,omitempty"`
}
// Validate validates this helm v001 schema chart provenance
func (m *HelmV001SchemaChartProvenance) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateSignature(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChartProvenance) validateSignature(formats strfmt.Registry) error {
if swag.IsZero(m.Signature) { // not required
return nil
}
if m.Signature != nil {
if err := m.Signature.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "provenance" + "." + "signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "provenance" + "." + "signature")
}
return err
}
}
return nil
}
// ContextValidate validate this helm v001 schema chart provenance based on the context it is used
func (m *HelmV001SchemaChartProvenance) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateSignature(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChartProvenance) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error {
if m.Signature != nil {
if swag.IsZero(m.Signature) { // not required
return nil
}
if err := m.Signature.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("chart" + "." + "provenance" + "." + "signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("chart" + "." + "provenance" + "." + "signature")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001SchemaChartProvenance) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001SchemaChartProvenance) UnmarshalBinary(b []byte) error {
var res HelmV001SchemaChartProvenance
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HelmV001SchemaChartProvenanceSignature Information about the included signature in the provenance file
//
// swagger:model HelmV001SchemaChartProvenanceSignature
type HelmV001SchemaChartProvenanceSignature struct {
// Specifies the signature embedded within the provenance file
// Required: true
// Read Only: true
// Format: byte
Content strfmt.Base64 `json:"content"`
}
// Validate validates this helm v001 schema chart provenance signature
func (m *HelmV001SchemaChartProvenanceSignature) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChartProvenanceSignature) validateContent(formats strfmt.Registry) error {
if err := validate.Required("chart"+"."+"provenance"+"."+"signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil {
return err
}
return nil
}
// ContextValidate validate this helm v001 schema chart provenance signature based on the context it is used
func (m *HelmV001SchemaChartProvenanceSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateContent(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaChartProvenanceSignature) contextValidateContent(ctx context.Context, formats strfmt.Registry) error {
if err := validate.ReadOnly(ctx, "chart"+"."+"provenance"+"."+"signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001SchemaChartProvenanceSignature) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001SchemaChartProvenanceSignature) UnmarshalBinary(b []byte) error {
var res HelmV001SchemaChartProvenanceSignature
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// HelmV001SchemaPublicKey The public key that can verify the package signature
//
// swagger:model HelmV001SchemaPublicKey
type HelmV001SchemaPublicKey struct {
// Specifies the content of the public key inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this helm v001 schema public key
func (m *HelmV001SchemaPublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *HelmV001SchemaPublicKey) validateContent(formats strfmt.Registry) error {
if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validates this helm v001 schema public key based on context it is used
func (m *HelmV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *HelmV001SchemaPublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *HelmV001SchemaPublicKey) UnmarshalBinary(b []byte) error {
var res HelmV001SchemaPublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// InactiveShardLogInfo inactive shard log info
//
// swagger:model InactiveShardLogInfo
type InactiveShardLogInfo struct {
// The current hash value stored at the root of the merkle tree
// Required: true
// Pattern: ^[0-9a-fA-F]{64}$
RootHash *string `json:"rootHash"`
// The current signed tree head
// Required: true
SignedTreeHead *string `json:"signedTreeHead"`
// The current treeID
// Required: true
// Pattern: ^[0-9]+$
TreeID *string `json:"treeID"`
// The current number of nodes in the merkle tree
// Required: true
// Minimum: 1
TreeSize *int64 `json:"treeSize"`
}
// Validate validates this inactive shard log info
func (m *InactiveShardLogInfo) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateRootHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignedTreeHead(formats); err != nil {
res = append(res, err)
}
if err := m.validateTreeID(formats); err != nil {
res = append(res, err)
}
if err := m.validateTreeSize(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *InactiveShardLogInfo) validateRootHash(formats strfmt.Registry) error {
if err := validate.Required("rootHash", "body", m.RootHash); err != nil {
return err
}
if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
return nil
}
func (m *InactiveShardLogInfo) validateSignedTreeHead(formats strfmt.Registry) error {
if err := validate.Required("signedTreeHead", "body", m.SignedTreeHead); err != nil {
return err
}
return nil
}
func (m *InactiveShardLogInfo) validateTreeID(formats strfmt.Registry) error {
if err := validate.Required("treeID", "body", m.TreeID); err != nil {
return err
}
if err := validate.Pattern("treeID", "body", *m.TreeID, `^[0-9]+$`); err != nil {
return err
}
return nil
}
func (m *InactiveShardLogInfo) validateTreeSize(formats strfmt.Registry) error {
if err := validate.Required("treeSize", "body", m.TreeSize); err != nil {
return err
}
if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil {
return err
}
return nil
}
// ContextValidate validates this inactive shard log info based on context it is used
func (m *InactiveShardLogInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *InactiveShardLogInfo) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *InactiveShardLogInfo) UnmarshalBinary(b []byte) error {
var res InactiveShardLogInfo
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// InclusionProof inclusion proof
//
// swagger:model InclusionProof
type InclusionProof struct {
// The checkpoint (signed tree head) that the inclusion proof is based on
// Required: true
Checkpoint *string `json:"checkpoint"`
// A list of hashes required to compute the inclusion proof, sorted in order from leaf to root
// Required: true
Hashes []string `json:"hashes"`
// The index of the entry in the transparency log
// Required: true
// Minimum: 0
LogIndex *int64 `json:"logIndex"`
// The hash value stored at the root of the merkle tree at the time the proof was generated
// Required: true
// Pattern: ^[0-9a-fA-F]{64}$
RootHash *string `json:"rootHash"`
// The size of the merkle tree at the time the inclusion proof was generated
// Required: true
// Minimum: 1
TreeSize *int64 `json:"treeSize"`
}
// Validate validates this inclusion proof
func (m *InclusionProof) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateCheckpoint(formats); err != nil {
res = append(res, err)
}
if err := m.validateHashes(formats); err != nil {
res = append(res, err)
}
if err := m.validateLogIndex(formats); err != nil {
res = append(res, err)
}
if err := m.validateRootHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateTreeSize(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *InclusionProof) validateCheckpoint(formats strfmt.Registry) error {
if err := validate.Required("checkpoint", "body", m.Checkpoint); err != nil {
return err
}
return nil
}
func (m *InclusionProof) validateHashes(formats strfmt.Registry) error {
if err := validate.Required("hashes", "body", m.Hashes); err != nil {
return err
}
for i := 0; i < len(m.Hashes); i++ {
if err := validate.Pattern("hashes"+"."+strconv.Itoa(i), "body", m.Hashes[i], `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
}
return nil
}
func (m *InclusionProof) validateLogIndex(formats strfmt.Registry) error {
if err := validate.Required("logIndex", "body", m.LogIndex); err != nil {
return err
}
if err := validate.MinimumInt("logIndex", "body", *m.LogIndex, 0, false); err != nil {
return err
}
return nil
}
func (m *InclusionProof) validateRootHash(formats strfmt.Registry) error {
if err := validate.Required("rootHash", "body", m.RootHash); err != nil {
return err
}
if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
return nil
}
func (m *InclusionProof) validateTreeSize(formats strfmt.Registry) error {
if err := validate.Required("treeSize", "body", m.TreeSize); err != nil {
return err
}
if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil {
return err
}
return nil
}
// ContextValidate validates this inclusion proof based on context it is used
func (m *InclusionProof) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *InclusionProof) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *InclusionProof) UnmarshalBinary(b []byte) error {
var res InclusionProof
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Intoto Intoto object
//
// swagger:model intoto
type Intoto struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec IntotoSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Intoto) Kind() string {
return "intoto"
}
// SetKind sets the kind of this subtype
func (m *Intoto) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Intoto) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec IntotoSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Intoto
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Intoto) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec IntotoSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this intoto
func (m *Intoto) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Intoto) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Intoto) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this intoto based on the context it is used
func (m *Intoto) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Intoto) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Intoto) UnmarshalBinary(b []byte) error {
var res Intoto
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// IntotoV001Schema intoto v0.0.1 Schema
//
// # Schema for intoto object
//
// swagger:model intotoV001Schema
type IntotoV001Schema struct {
// content
// Required: true
Content *IntotoV001SchemaContent `json:"content"`
// The public key that can verify the signature
// Required: true
// Format: byte
PublicKey *strfmt.Base64 `json:"publicKey"`
}
// Validate validates this intoto v001 schema
func (m *IntotoV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV001Schema) validateContent(formats strfmt.Registry) error {
if err := validate.Required("content", "body", m.Content); err != nil {
return err
}
if m.Content != nil {
if err := m.Content.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content")
}
return err
}
}
return nil
}
func (m *IntotoV001Schema) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v001 schema based on the context it is used
func (m *IntotoV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateContent(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV001Schema) contextValidateContent(ctx context.Context, formats strfmt.Registry) error {
if m.Content != nil {
if err := m.Content.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV001Schema) UnmarshalBinary(b []byte) error {
var res IntotoV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV001SchemaContent intoto v001 schema content
//
// swagger:model IntotoV001SchemaContent
type IntotoV001SchemaContent struct {
// envelope
Envelope string `json:"envelope,omitempty"`
// hash
Hash *IntotoV001SchemaContentHash `json:"hash,omitempty"`
// payload hash
PayloadHash *IntotoV001SchemaContentPayloadHash `json:"payloadHash,omitempty"`
}
// Validate validates this intoto v001 schema content
func (m *IntotoV001SchemaContent) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if err := m.validatePayloadHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV001SchemaContent) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "hash")
}
return err
}
}
return nil
}
func (m *IntotoV001SchemaContent) validatePayloadHash(formats strfmt.Registry) error {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if m.PayloadHash != nil {
if err := m.PayloadHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// ContextValidate validate this intoto v001 schema content based on the context it is used
func (m *IntotoV001SchemaContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV001SchemaContent) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "hash")
}
return err
}
}
return nil
}
func (m *IntotoV001SchemaContent) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
if m.PayloadHash != nil {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001SchemaContent) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV001SchemaContent) UnmarshalBinary(b []byte) error {
var res IntotoV001SchemaContent
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV001SchemaContentHash Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored
//
// swagger:model IntotoV001SchemaContentHash
type IntotoV001SchemaContentHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the archive
// Required: true
Value *string `json:"value"`
}
// Validate validates this intoto v001 schema content hash
func (m *IntotoV001SchemaContentHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var intotoV001SchemaContentHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
intotoV001SchemaContentHashTypeAlgorithmPropEnum = append(intotoV001SchemaContentHashTypeAlgorithmPropEnum, v)
}
}
const (
// IntotoV001SchemaContentHashAlgorithmSha256 captures enum value "sha256"
IntotoV001SchemaContentHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *IntotoV001SchemaContentHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, intotoV001SchemaContentHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("content"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v001 schema content hash based on the context it is used
func (m *IntotoV001SchemaContentHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001SchemaContentHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV001SchemaContentHash) UnmarshalBinary(b []byte) error {
var res IntotoV001SchemaContentHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV001SchemaContentPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored
//
// swagger:model IntotoV001SchemaContentPayloadHash
type IntotoV001SchemaContentPayloadHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the envelope's payload
// Required: true
Value *string `json:"value"`
}
// Validate validates this intoto v001 schema content payload hash
func (m *IntotoV001SchemaContentPayloadHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum = append(intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, v)
}
}
const (
// IntotoV001SchemaContentPayloadHashAlgorithmSha256 captures enum value "sha256"
IntotoV001SchemaContentPayloadHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("content"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentPayloadHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v001 schema content payload hash based on the context it is used
func (m *IntotoV001SchemaContentPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001SchemaContentPayloadHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV001SchemaContentPayloadHash) UnmarshalBinary(b []byte) error {
var res IntotoV001SchemaContentPayloadHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// IntotoV002Schema intoto v0.0.2 Schema
//
// # Schema for intoto object
//
// swagger:model intotoV002Schema
type IntotoV002Schema struct {
// content
// Required: true
Content *IntotoV002SchemaContent `json:"content"`
}
// Validate validates this intoto v002 schema
func (m *IntotoV002Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002Schema) validateContent(formats strfmt.Registry) error {
if err := validate.Required("content", "body", m.Content); err != nil {
return err
}
if m.Content != nil {
if err := m.Content.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content")
}
return err
}
}
return nil
}
// ContextValidate validate this intoto v002 schema based on the context it is used
func (m *IntotoV002Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateContent(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002Schema) contextValidateContent(ctx context.Context, formats strfmt.Registry) error {
if m.Content != nil {
if err := m.Content.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002Schema) UnmarshalBinary(b []byte) error {
var res IntotoV002Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV002SchemaContent intoto v002 schema content
//
// swagger:model IntotoV002SchemaContent
type IntotoV002SchemaContent struct {
// envelope
// Required: true
Envelope *IntotoV002SchemaContentEnvelope `json:"envelope"`
// hash
Hash *IntotoV002SchemaContentHash `json:"hash,omitempty"`
// payload hash
PayloadHash *IntotoV002SchemaContentPayloadHash `json:"payloadHash,omitempty"`
}
// Validate validates this intoto v002 schema content
func (m *IntotoV002SchemaContent) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEnvelope(formats); err != nil {
res = append(res, err)
}
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if err := m.validatePayloadHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002SchemaContent) validateEnvelope(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"envelope", "body", m.Envelope); err != nil {
return err
}
if m.Envelope != nil {
if err := m.Envelope.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "envelope")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "envelope")
}
return err
}
}
return nil
}
func (m *IntotoV002SchemaContent) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "hash")
}
return err
}
}
return nil
}
func (m *IntotoV002SchemaContent) validatePayloadHash(formats strfmt.Registry) error {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if m.PayloadHash != nil {
if err := m.PayloadHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// ContextValidate validate this intoto v002 schema content based on the context it is used
func (m *IntotoV002SchemaContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateEnvelope(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002SchemaContent) contextValidateEnvelope(ctx context.Context, formats strfmt.Registry) error {
if m.Envelope != nil {
if err := m.Envelope.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "envelope")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "envelope")
}
return err
}
}
return nil
}
func (m *IntotoV002SchemaContent) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "hash")
}
return err
}
}
return nil
}
func (m *IntotoV002SchemaContent) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
if m.PayloadHash != nil {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002SchemaContent) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002SchemaContent) UnmarshalBinary(b []byte) error {
var res IntotoV002SchemaContent
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV002SchemaContentEnvelope dsse envelope
//
// swagger:model IntotoV002SchemaContentEnvelope
type IntotoV002SchemaContentEnvelope struct {
// payload of the envelope
// Format: byte
Payload strfmt.Base64 `json:"payload,omitempty"`
// type describing the payload
// Required: true
PayloadType *string `json:"payloadType"`
// collection of all signatures of the envelope's payload
// Required: true
// Min Items: 1
Signatures []*IntotoV002SchemaContentEnvelopeSignaturesItems0 `json:"signatures"`
}
// Validate validates this intoto v002 schema content envelope
func (m *IntotoV002SchemaContentEnvelope) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePayloadType(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignatures(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002SchemaContentEnvelope) validatePayloadType(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"envelope"+"."+"payloadType", "body", m.PayloadType); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentEnvelope) validateSignatures(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"envelope"+"."+"signatures", "body", m.Signatures); err != nil {
return err
}
iSignaturesSize := int64(len(m.Signatures))
if err := validate.MinItems("content"+"."+"envelope"+"."+"signatures", "body", iSignaturesSize, 1); err != nil {
return err
}
for i := 0; i < len(m.Signatures); i++ {
if swag.IsZero(m.Signatures[i]) { // not required
continue
}
if m.Signatures[i] != nil {
if err := m.Signatures[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this intoto v002 schema content envelope based on the context it is used
func (m *IntotoV002SchemaContentEnvelope) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateSignatures(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002SchemaContentEnvelope) contextValidateSignatures(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.Signatures); i++ {
if m.Signatures[i] != nil {
if swag.IsZero(m.Signatures[i]) { // not required
return nil
}
if err := m.Signatures[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002SchemaContentEnvelope) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002SchemaContentEnvelope) UnmarshalBinary(b []byte) error {
var res IntotoV002SchemaContentEnvelope
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV002SchemaContentEnvelopeSignaturesItems0 a signature of the envelope's payload along with the public key for the signature
//
// swagger:model IntotoV002SchemaContentEnvelopeSignaturesItems0
type IntotoV002SchemaContentEnvelopeSignaturesItems0 struct {
// optional id of the key used to create the signature
Keyid string `json:"keyid,omitempty"`
// public key that corresponds to this signature
// Required: true
// Format: byte
PublicKey *strfmt.Base64 `json:"publicKey"`
// signature of the payload
// Required: true
// Format: byte
Sig *strfmt.Base64 `json:"sig"`
}
// Validate validates this intoto v002 schema content envelope signatures items0
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateSig(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validateSig(formats strfmt.Registry) error {
if err := validate.Required("sig", "body", m.Sig); err != nil {
return err
}
return nil
}
// ContextValidate validates this intoto v002 schema content envelope signatures items0 based on context it is used
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) UnmarshalBinary(b []byte) error {
var res IntotoV002SchemaContentEnvelopeSignaturesItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV002SchemaContentHash Specifies the hash algorithm and value encompassing the entire signed envelope
//
// swagger:model IntotoV002SchemaContentHash
type IntotoV002SchemaContentHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the archive
// Required: true
Value *string `json:"value"`
}
// Validate validates this intoto v002 schema content hash
func (m *IntotoV002SchemaContentHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var intotoV002SchemaContentHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
intotoV002SchemaContentHashTypeAlgorithmPropEnum = append(intotoV002SchemaContentHashTypeAlgorithmPropEnum, v)
}
}
const (
// IntotoV002SchemaContentHashAlgorithmSha256 captures enum value "sha256"
IntotoV002SchemaContentHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *IntotoV002SchemaContentHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, intotoV002SchemaContentHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("content"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v002 schema content hash based on the context it is used
func (m *IntotoV002SchemaContentHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002SchemaContentHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002SchemaContentHash) UnmarshalBinary(b []byte) error {
var res IntotoV002SchemaContentHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IntotoV002SchemaContentPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope
//
// swagger:model IntotoV002SchemaContentPayloadHash
type IntotoV002SchemaContentPayloadHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value of the payload
// Required: true
Value *string `json:"value"`
}
// Validate validates this intoto v002 schema content payload hash
func (m *IntotoV002SchemaContentPayloadHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum = append(intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum, v)
}
}
const (
// IntotoV002SchemaContentPayloadHashAlgorithmSha256 captures enum value "sha256"
IntotoV002SchemaContentPayloadHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *IntotoV002SchemaContentPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("content"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *IntotoV002SchemaContentPayloadHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v002 schema content payload hash based on the context it is used
func (m *IntotoV002SchemaContentPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV002SchemaContentPayloadHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV002SchemaContentPayloadHash) UnmarshalBinary(b []byte) error {
var res IntotoV002SchemaContentPayloadHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Jar Java Archive (JAR)
//
// swagger:model jar
type Jar struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec JarSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Jar) Kind() string {
return "jar"
}
// SetKind sets the kind of this subtype
func (m *Jar) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Jar) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec JarSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Jar
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Jar) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec JarSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this jar
func (m *Jar) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Jar) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Jar) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this jar based on the context it is used
func (m *Jar) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Jar) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Jar) UnmarshalBinary(b []byte) error {
var res Jar
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// JarV001Schema JAR v0.0.1 Schema
//
// # Schema for JAR entries
//
// swagger:model jarV001Schema
type JarV001Schema struct {
// archive
// Required: true
Archive *JarV001SchemaArchive `json:"archive"`
// signature
Signature *JarV001SchemaSignature `json:"signature,omitempty"`
}
// Validate validates this jar v001 schema
func (m *JarV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateArchive(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignature(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001Schema) validateArchive(formats strfmt.Registry) error {
if err := validate.Required("archive", "body", m.Archive); err != nil {
return err
}
if m.Archive != nil {
if err := m.Archive.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("archive")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("archive")
}
return err
}
}
return nil
}
func (m *JarV001Schema) validateSignature(formats strfmt.Registry) error {
if swag.IsZero(m.Signature) { // not required
return nil
}
if m.Signature != nil {
if err := m.Signature.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// ContextValidate validate this jar v001 schema based on the context it is used
func (m *JarV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateArchive(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSignature(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001Schema) contextValidateArchive(ctx context.Context, formats strfmt.Registry) error {
if m.Archive != nil {
if err := m.Archive.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("archive")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("archive")
}
return err
}
}
return nil
}
func (m *JarV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error {
if m.Signature != nil {
if swag.IsZero(m.Signature) { // not required
return nil
}
if err := m.Signature.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *JarV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *JarV001Schema) UnmarshalBinary(b []byte) error {
var res JarV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// JarV001SchemaArchive Information about the archive associated with the entry
//
// swagger:model JarV001SchemaArchive
type JarV001SchemaArchive struct {
// Specifies the archive inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// hash
Hash *JarV001SchemaArchiveHash `json:"hash,omitempty"`
}
// Validate validates this jar v001 schema archive
func (m *JarV001SchemaArchive) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001SchemaArchive) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("archive" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("archive" + "." + "hash")
}
return err
}
}
return nil
}
// ContextValidate validate this jar v001 schema archive based on the context it is used
func (m *JarV001SchemaArchive) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001SchemaArchive) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("archive" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("archive" + "." + "hash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *JarV001SchemaArchive) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *JarV001SchemaArchive) UnmarshalBinary(b []byte) error {
var res JarV001SchemaArchive
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// JarV001SchemaArchiveHash Specifies the hash algorithm and value encompassing the entire signed archive
//
// swagger:model JarV001SchemaArchiveHash
type JarV001SchemaArchiveHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the archive
// Required: true
Value *string `json:"value"`
}
// Validate validates this jar v001 schema archive hash
func (m *JarV001SchemaArchiveHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var jarV001SchemaArchiveHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
jarV001SchemaArchiveHashTypeAlgorithmPropEnum = append(jarV001SchemaArchiveHashTypeAlgorithmPropEnum, v)
}
}
const (
// JarV001SchemaArchiveHashAlgorithmSha256 captures enum value "sha256"
JarV001SchemaArchiveHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *JarV001SchemaArchiveHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, jarV001SchemaArchiveHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *JarV001SchemaArchiveHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("archive"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("archive"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *JarV001SchemaArchiveHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("archive"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validates this jar v001 schema archive hash based on context it is used
func (m *JarV001SchemaArchiveHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *JarV001SchemaArchiveHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *JarV001SchemaArchiveHash) UnmarshalBinary(b []byte) error {
var res JarV001SchemaArchiveHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// JarV001SchemaSignature Information about the included signature in the JAR file
//
// swagger:model JarV001SchemaSignature
type JarV001SchemaSignature struct {
// Specifies the PKCS7 signature embedded within the JAR file
// Required: true
// Read Only: true
// Format: byte
Content strfmt.Base64 `json:"content"`
// public key
// Required: true
PublicKey *JarV001SchemaSignaturePublicKey `json:"publicKey"`
}
// Validate validates this jar v001 schema signature
func (m *JarV001SchemaSignature) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001SchemaSignature) validateContent(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil {
return err
}
return nil
}
func (m *JarV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"publicKey", "body", m.PublicKey); err != nil {
return err
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this jar v001 schema signature based on the context it is used
func (m *JarV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateContent(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001SchemaSignature) contextValidateContent(ctx context.Context, formats strfmt.Registry) error {
if err := validate.ReadOnly(ctx, "signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil {
return err
}
return nil
}
func (m *JarV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *JarV001SchemaSignature) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *JarV001SchemaSignature) UnmarshalBinary(b []byte) error {
var res JarV001SchemaSignature
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// JarV001SchemaSignaturePublicKey The X509 certificate containing the public key JAR which verifies the signature of the JAR
//
// swagger:model JarV001SchemaSignaturePublicKey
type JarV001SchemaSignaturePublicKey struct {
// Specifies the content of the X509 certificate containing the public key used to verify the signature
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this jar v001 schema signature public key
func (m *JarV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *JarV001SchemaSignaturePublicKey) validateContent(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"publicKey"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validate this jar v001 schema signature public key based on the context it is used
func (m *JarV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *JarV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *JarV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error {
var res JarV001SchemaSignaturePublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// LogEntry log entry
//
// swagger:model LogEntry
type LogEntry map[string]LogEntryAnon
// Validate validates this log entry
func (m LogEntry) Validate(formats strfmt.Registry) error {
var res []error
for k := range m {
if swag.IsZero(m[k]) { // not required
continue
}
if val, ok := m[k]; ok {
if err := val.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName(k)
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName(k)
}
return err
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// ContextValidate validate this log entry based on the context it is used
func (m LogEntry) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
for k := range m {
if val, ok := m[k]; ok {
if err := val.ContextValidate(ctx, formats); err != nil {
return err
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// LogEntryAnon log entry anon
//
// swagger:model LogEntryAnon
type LogEntryAnon struct {
// attestation
Attestation *LogEntryAnonAttestation `json:"attestation,omitempty"`
// body
// Required: true
Body interface{} `json:"body"`
// The time the entry was added to the log as a Unix timestamp in seconds
// 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
// Minimum: 0
LogIndex *int64 `json:"logIndex"`
// verification
Verification *LogEntryAnonVerification `json:"verification,omitempty"`
}
// Validate validates this log entry anon
func (m *LogEntryAnon) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAttestation(formats); err != nil {
res = append(res, err)
}
if err := m.validateBody(formats); err != nil {
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)
}
if err := m.validateVerification(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogEntryAnon) validateAttestation(formats strfmt.Registry) error {
if swag.IsZero(m.Attestation) { // not required
return nil
}
if m.Attestation != nil {
if err := m.Attestation.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("attestation")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("attestation")
}
return err
}
}
return nil
}
func (m *LogEntryAnon) validateBody(formats strfmt.Registry) error {
if m.Body == nil {
return errors.Required("body", "body", nil)
}
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 {
return err
}
if err := validate.MinimumInt("logIndex", "body", *m.LogIndex, 0, false); err != nil {
return err
}
return nil
}
func (m *LogEntryAnon) validateVerification(formats strfmt.Registry) error {
if swag.IsZero(m.Verification) { // not required
return nil
}
if m.Verification != nil {
if err := m.Verification.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("verification")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("verification")
}
return err
}
}
return nil
}
// ContextValidate validate this log entry anon based on the context it is used
func (m *LogEntryAnon) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateAttestation(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateVerification(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogEntryAnon) contextValidateAttestation(ctx context.Context, formats strfmt.Registry) error {
if m.Attestation != nil {
if swag.IsZero(m.Attestation) { // not required
return nil
}
if err := m.Attestation.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("attestation")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("attestation")
}
return err
}
}
return nil
}
func (m *LogEntryAnon) contextValidateVerification(ctx context.Context, formats strfmt.Registry) error {
if m.Verification != nil {
if swag.IsZero(m.Verification) { // not required
return nil
}
if err := m.Verification.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("verification")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("verification")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *LogEntryAnon) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LogEntryAnon) UnmarshalBinary(b []byte) error {
var res LogEntryAnon
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// LogEntryAnonAttestation log entry anon attestation
//
// swagger:model LogEntryAnonAttestation
type LogEntryAnonAttestation struct {
// data
// Format: byte
Data strfmt.Base64 `json:"data,omitempty"`
}
// Validate validates this log entry anon attestation
func (m *LogEntryAnonAttestation) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this log entry anon attestation based on context it is used
func (m *LogEntryAnonAttestation) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *LogEntryAnonAttestation) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LogEntryAnonAttestation) UnmarshalBinary(b []byte) error {
var res LogEntryAnonAttestation
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// LogEntryAnonVerification log entry anon verification
//
// swagger:model LogEntryAnonVerification
type LogEntryAnonVerification struct {
// inclusion proof
InclusionProof *InclusionProof `json:"inclusionProof,omitempty"`
// Signature over the logID, logIndex, body and integratedTime.
// Format: byte
SignedEntryTimestamp strfmt.Base64 `json:"signedEntryTimestamp,omitempty"`
}
// Validate validates this log entry anon verification
func (m *LogEntryAnonVerification) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateInclusionProof(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogEntryAnonVerification) validateInclusionProof(formats strfmt.Registry) error {
if swag.IsZero(m.InclusionProof) { // not required
return nil
}
if m.InclusionProof != nil {
if err := m.InclusionProof.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("verification" + "." + "inclusionProof")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("verification" + "." + "inclusionProof")
}
return err
}
}
return nil
}
// ContextValidate validate this log entry anon verification based on the context it is used
func (m *LogEntryAnonVerification) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateInclusionProof(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogEntryAnonVerification) contextValidateInclusionProof(ctx context.Context, formats strfmt.Registry) error {
if m.InclusionProof != nil {
if swag.IsZero(m.InclusionProof) { // not required
return nil
}
if err := m.InclusionProof.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("verification" + "." + "inclusionProof")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("verification" + "." + "inclusionProof")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *LogEntryAnonVerification) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LogEntryAnonVerification) UnmarshalBinary(b []byte) error {
var res LogEntryAnonVerification
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// LogInfo log info
//
// swagger:model LogInfo
type LogInfo struct {
// inactive shards
InactiveShards []*InactiveShardLogInfo `json:"inactiveShards"`
// The current hash value stored at the root of the merkle tree
// Required: true
// Pattern: ^[0-9a-fA-F]{64}$
RootHash *string `json:"rootHash"`
// The current signed tree head
// Required: true
SignedTreeHead *string `json:"signedTreeHead"`
// The current treeID
// Required: true
// Pattern: ^[0-9]+$
TreeID *string `json:"treeID"`
// The current number of nodes in the merkle tree
// Required: true
// Minimum: 1
TreeSize *int64 `json:"treeSize"`
}
// Validate validates this log info
func (m *LogInfo) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateInactiveShards(formats); err != nil {
res = append(res, err)
}
if err := m.validateRootHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignedTreeHead(formats); err != nil {
res = append(res, err)
}
if err := m.validateTreeID(formats); err != nil {
res = append(res, err)
}
if err := m.validateTreeSize(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogInfo) validateInactiveShards(formats strfmt.Registry) error {
if swag.IsZero(m.InactiveShards) { // not required
return nil
}
for i := 0; i < len(m.InactiveShards); i++ {
if swag.IsZero(m.InactiveShards[i]) { // not required
continue
}
if m.InactiveShards[i] != nil {
if err := m.InactiveShards[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("inactiveShards" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("inactiveShards" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *LogInfo) validateRootHash(formats strfmt.Registry) error {
if err := validate.Required("rootHash", "body", m.RootHash); err != nil {
return err
}
if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil {
return err
}
return nil
}
func (m *LogInfo) validateSignedTreeHead(formats strfmt.Registry) error {
if err := validate.Required("signedTreeHead", "body", m.SignedTreeHead); err != nil {
return err
}
return nil
}
func (m *LogInfo) validateTreeID(formats strfmt.Registry) error {
if err := validate.Required("treeID", "body", m.TreeID); err != nil {
return err
}
if err := validate.Pattern("treeID", "body", *m.TreeID, `^[0-9]+$`); err != nil {
return err
}
return nil
}
func (m *LogInfo) validateTreeSize(formats strfmt.Registry) error {
if err := validate.Required("treeSize", "body", m.TreeSize); err != nil {
return err
}
if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil {
return err
}
return nil
}
// ContextValidate validate this log info based on the context it is used
func (m *LogInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateInactiveShards(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *LogInfo) contextValidateInactiveShards(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.InactiveShards); i++ {
if m.InactiveShards[i] != nil {
if swag.IsZero(m.InactiveShards[i]) { // not required
return nil
}
if err := m.InactiveShards[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("inactiveShards" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("inactiveShards" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *LogInfo) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LogInfo) UnmarshalBinary(b []byte) error {
var res LogInfo
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"io"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
)
// ProposedEntry proposed entry
//
// swagger:discriminator ProposedEntry kind
type ProposedEntry interface {
runtime.Validatable
runtime.ContextValidatable
// kind
// Required: true
Kind() string
SetKind(string)
// AdditionalProperties in base type shoud be handled just like regular properties
// At this moment, the base type property is pushed down to the subtype
}
type proposedEntry struct {
kindField string
}
// Kind gets the kind of this polymorphic type
func (m *proposedEntry) Kind() string {
return "ProposedEntry"
}
// SetKind sets the kind of this polymorphic type
func (m *proposedEntry) SetKind(val string) {
}
// UnmarshalProposedEntrySlice unmarshals polymorphic slices of ProposedEntry
func UnmarshalProposedEntrySlice(reader io.Reader, consumer runtime.Consumer) ([]ProposedEntry, error) {
var elements []json.RawMessage
if err := consumer.Consume(reader, &elements); err != nil {
return nil, err
}
var result []ProposedEntry
for _, element := range elements {
obj, err := unmarshalProposedEntry(element, consumer)
if err != nil {
return nil, err
}
result = append(result, obj)
}
return result, nil
}
// UnmarshalProposedEntry unmarshals polymorphic ProposedEntry
func UnmarshalProposedEntry(reader io.Reader, consumer runtime.Consumer) (ProposedEntry, error) {
// we need to read this twice, so first into a buffer
data, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
return unmarshalProposedEntry(data, consumer)
}
func unmarshalProposedEntry(data []byte, consumer runtime.Consumer) (ProposedEntry, error) {
buf := bytes.NewBuffer(data)
buf2 := bytes.NewBuffer(data)
// the first time this is read is to fetch the value of the kind property.
var getType struct {
Kind string `json:"kind"`
}
if err := consumer.Consume(buf, &getType); err != nil {
return nil, err
}
if err := validate.RequiredString("kind", "body", getType.Kind); err != nil {
return nil, err
}
// The value of kind is used to determine which type to create and unmarshal the data into
switch getType.Kind {
case "ProposedEntry":
var result proposedEntry
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "alpine":
var result Alpine
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "cose":
var result Cose
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "dsse":
var result DSSE
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "hashedrekord":
var result Hashedrekord
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "helm":
var result Helm
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "intoto":
var result Intoto
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "jar":
var result Jar
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "rekord":
var result Rekord
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "rfc3161":
var result Rfc3161
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "rpm":
var result Rpm
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
case "tuf":
var result TUF
if err := consumer.Consume(buf2, &result); err != nil {
return nil, err
}
return &result, nil
}
return nil, errors.New(422, "invalid kind value: %q", getType.Kind)
}
// Validate validates this proposed entry
func (m *proposedEntry) Validate(formats strfmt.Registry) error {
return nil
}
// ContextValidate validates this proposed entry based on context it is used
func (m *proposedEntry) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Rekord Rekord object
//
// swagger:model rekord
type Rekord struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RekordSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Rekord) Kind() string {
return "rekord"
}
// SetKind sets the kind of this subtype
func (m *Rekord) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Rekord) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RekordSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Rekord
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Rekord) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RekordSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this rekord
func (m *Rekord) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rekord) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Rekord) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this rekord based on the context it is used
func (m *Rekord) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Rekord) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Rekord) UnmarshalBinary(b []byte) error {
var res Rekord
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// RekordV001Schema Rekor v0.0.1 Schema
//
// # Schema for Rekord object
//
// swagger:model rekordV001Schema
type RekordV001Schema struct {
// data
// Required: true
Data *RekordV001SchemaData `json:"data"`
// signature
// Required: true
Signature *RekordV001SchemaSignature `json:"signature"`
}
// Validate validates this rekord v001 schema
func (m *RekordV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateData(formats); err != nil {
res = append(res, err)
}
if err := m.validateSignature(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001Schema) validateData(formats strfmt.Registry) error {
if err := validate.Required("data", "body", m.Data); err != nil {
return err
}
if m.Data != nil {
if err := m.Data.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
func (m *RekordV001Schema) validateSignature(formats strfmt.Registry) error {
if err := validate.Required("signature", "body", m.Signature); err != nil {
return err
}
if m.Signature != nil {
if err := m.Signature.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// ContextValidate validate this rekord v001 schema based on the context it is used
func (m *RekordV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateData(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSignature(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error {
if m.Data != nil {
if err := m.Data.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data")
}
return err
}
}
return nil
}
func (m *RekordV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error {
if m.Signature != nil {
if err := m.Signature.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *RekordV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RekordV001Schema) UnmarshalBinary(b []byte) error {
var res RekordV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RekordV001SchemaData Information about the content associated with the entry
//
// swagger:model RekordV001SchemaData
type RekordV001SchemaData struct {
// Specifies the content inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// hash
Hash *RekordV001SchemaDataHash `json:"hash,omitempty"`
}
// Validate validates this rekord v001 schema data
func (m *RekordV001SchemaData) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001SchemaData) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "hash")
}
return err
}
}
return nil
}
// ContextValidate validate this rekord v001 schema data based on the context it is used
func (m *RekordV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001SchemaData) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("data" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("data" + "." + "hash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *RekordV001SchemaData) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RekordV001SchemaData) UnmarshalBinary(b []byte) error {
var res RekordV001SchemaData
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RekordV001SchemaDataHash Specifies the hash algorithm and value for the content
//
// swagger:model RekordV001SchemaDataHash
type RekordV001SchemaDataHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the content
// Required: true
Value *string `json:"value"`
}
// Validate validates this rekord v001 schema data hash
func (m *RekordV001SchemaDataHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var rekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
rekordV001SchemaDataHashTypeAlgorithmPropEnum = append(rekordV001SchemaDataHashTypeAlgorithmPropEnum, v)
}
}
const (
// RekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256"
RekordV001SchemaDataHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *RekordV001SchemaDataHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, rekordV001SchemaDataHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *RekordV001SchemaDataHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("data"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *RekordV001SchemaDataHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("data"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this rekord v001 schema data hash based on the context it is used
func (m *RekordV001SchemaDataHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *RekordV001SchemaDataHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RekordV001SchemaDataHash) UnmarshalBinary(b []byte) error {
var res RekordV001SchemaDataHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RekordV001SchemaSignature Information about the detached signature associated with the entry
//
// swagger:model RekordV001SchemaSignature
type RekordV001SchemaSignature struct {
// Specifies the content of the signature inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
// Specifies the format of the signature
// Required: true
// Enum: ["pgp","minisign","x509","ssh"]
Format *string `json:"format"`
// public key
// Required: true
PublicKey *RekordV001SchemaSignaturePublicKey `json:"publicKey"`
}
// Validate validates this rekord v001 schema signature
func (m *RekordV001SchemaSignature) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if err := m.validateFormat(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001SchemaSignature) validateContent(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
var rekordV001SchemaSignatureTypeFormatPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["pgp","minisign","x509","ssh"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
rekordV001SchemaSignatureTypeFormatPropEnum = append(rekordV001SchemaSignatureTypeFormatPropEnum, v)
}
}
const (
// RekordV001SchemaSignatureFormatPgp captures enum value "pgp"
RekordV001SchemaSignatureFormatPgp string = "pgp"
// RekordV001SchemaSignatureFormatMinisign captures enum value "minisign"
RekordV001SchemaSignatureFormatMinisign string = "minisign"
// RekordV001SchemaSignatureFormatX509 captures enum value "x509"
RekordV001SchemaSignatureFormatX509 string = "x509"
// RekordV001SchemaSignatureFormatSSH captures enum value "ssh"
RekordV001SchemaSignatureFormatSSH string = "ssh"
)
// prop value enum
func (m *RekordV001SchemaSignature) validateFormatEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, rekordV001SchemaSignatureTypeFormatPropEnum, true); err != nil {
return err
}
return nil
}
func (m *RekordV001SchemaSignature) validateFormat(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"format", "body", m.Format); err != nil {
return err
}
// value enum
if err := m.validateFormatEnum("signature"+"."+"format", "body", *m.Format); err != nil {
return err
}
return nil
}
func (m *RekordV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"publicKey", "body", m.PublicKey); err != nil {
return err
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this rekord v001 schema signature based on the context it is used
func (m *RekordV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("signature" + "." + "publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("signature" + "." + "publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *RekordV001SchemaSignature) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RekordV001SchemaSignature) UnmarshalBinary(b []byte) error {
var res RekordV001SchemaSignature
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RekordV001SchemaSignaturePublicKey The public key that can verify the signature
//
// swagger:model RekordV001SchemaSignaturePublicKey
type RekordV001SchemaSignaturePublicKey struct {
// Specifies the content of the public key inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this rekord v001 schema signature public key
func (m *RekordV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RekordV001SchemaSignaturePublicKey) validateContent(formats strfmt.Registry) error {
if err := validate.Required("signature"+"."+"publicKey"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validates this rekord v001 schema signature public key based on context it is used
func (m *RekordV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *RekordV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RekordV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error {
var res RekordV001SchemaSignaturePublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Rfc3161 RFC3161 Timestamp
//
// swagger:model rfc3161
type Rfc3161 struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec Rfc3161Schema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Rfc3161) Kind() string {
return "rfc3161"
}
// SetKind sets the kind of this subtype
func (m *Rfc3161) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Rfc3161) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec Rfc3161Schema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Rfc3161
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Rfc3161) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec Rfc3161Schema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this rfc3161
func (m *Rfc3161) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rfc3161) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Rfc3161) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this rfc3161 based on the context it is used
func (m *Rfc3161) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Rfc3161) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Rfc3161) UnmarshalBinary(b []byte) error {
var res Rfc3161
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Rfc3161V001Schema Timestamp v0.0.1 Schema
//
// # Schema for RFC3161 entries
//
// swagger:model rfc3161V001Schema
type Rfc3161V001Schema struct {
// tsr
// Required: true
Tsr *Rfc3161V001SchemaTsr `json:"tsr"`
}
// Validate validates this rfc3161 v001 schema
func (m *Rfc3161V001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateTsr(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rfc3161V001Schema) validateTsr(formats strfmt.Registry) error {
if err := validate.Required("tsr", "body", m.Tsr); err != nil {
return err
}
if m.Tsr != nil {
if err := m.Tsr.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("tsr")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("tsr")
}
return err
}
}
return nil
}
// ContextValidate validate this rfc3161 v001 schema based on the context it is used
func (m *Rfc3161V001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateTsr(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rfc3161V001Schema) contextValidateTsr(ctx context.Context, formats strfmt.Registry) error {
if m.Tsr != nil {
if err := m.Tsr.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("tsr")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("tsr")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *Rfc3161V001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Rfc3161V001Schema) UnmarshalBinary(b []byte) error {
var res Rfc3161V001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Rfc3161V001SchemaTsr Information about the tsr file associated with the entry
//
// swagger:model Rfc3161V001SchemaTsr
type Rfc3161V001SchemaTsr struct {
// Specifies the tsr file content inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this rfc3161 v001 schema tsr
func (m *Rfc3161V001SchemaTsr) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rfc3161V001SchemaTsr) validateContent(formats strfmt.Registry) error {
if err := validate.Required("tsr"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validates this rfc3161 v001 schema tsr based on context it is used
func (m *Rfc3161V001SchemaTsr) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *Rfc3161V001SchemaTsr) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Rfc3161V001SchemaTsr) UnmarshalBinary(b []byte) error {
var res Rfc3161V001SchemaTsr
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Rpm RPM package
//
// swagger:model rpm
type Rpm struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RpmSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *Rpm) Kind() string {
return "rpm"
}
// SetKind sets the kind of this subtype
func (m *Rpm) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *Rpm) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RpmSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result Rpm
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m Rpm) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec RpmSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this rpm
func (m *Rpm) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Rpm) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *Rpm) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this rpm based on the context it is used
func (m *Rpm) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *Rpm) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Rpm) UnmarshalBinary(b []byte) error {
var res Rpm
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// RpmV001Schema RPM v0.0.1 Schema
//
// # Schema for RPM entries
//
// swagger:model rpmV001Schema
type RpmV001Schema struct {
// package
// Required: true
Package *RpmV001SchemaPackage `json:"package"`
// public key
// Required: true
PublicKey *RpmV001SchemaPublicKey `json:"publicKey"`
}
// Validate validates this rpm v001 schema
func (m *RpmV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePackage(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RpmV001Schema) validatePackage(formats strfmt.Registry) error {
if err := validate.Required("package", "body", m.Package); err != nil {
return err
}
if m.Package != nil {
if err := m.Package.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package")
}
return err
}
}
return nil
}
func (m *RpmV001Schema) validatePublicKey(formats strfmt.Registry) error {
if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
return err
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this rpm v001 schema based on the context it is used
func (m *RpmV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidatePackage(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RpmV001Schema) contextValidatePackage(ctx context.Context, formats strfmt.Registry) error {
if m.Package != nil {
if err := m.Package.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package")
}
return err
}
}
return nil
}
func (m *RpmV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *RpmV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RpmV001Schema) UnmarshalBinary(b []byte) error {
var res RpmV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RpmV001SchemaPackage Information about the package associated with the entry
//
// swagger:model RpmV001SchemaPackage
type RpmV001SchemaPackage struct {
// Specifies the package inline within the document
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// hash
Hash *RpmV001SchemaPackageHash `json:"hash,omitempty"`
// Values of the RPM headers
// Read Only: true
Headers map[string]string `json:"headers,omitempty"`
}
// Validate validates this rpm v001 schema package
func (m *RpmV001SchemaPackage) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RpmV001SchemaPackage) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if m.Hash != nil {
if err := m.Hash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package" + "." + "hash")
}
return err
}
}
return nil
}
// ContextValidate validate this rpm v001 schema package based on the context it is used
func (m *RpmV001SchemaPackage) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateHash(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateHeaders(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RpmV001SchemaPackage) contextValidateHash(ctx context.Context, formats strfmt.Registry) error {
if m.Hash != nil {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := m.Hash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("package" + "." + "hash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("package" + "." + "hash")
}
return err
}
}
return nil
}
func (m *RpmV001SchemaPackage) contextValidateHeaders(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *RpmV001SchemaPackage) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RpmV001SchemaPackage) UnmarshalBinary(b []byte) error {
var res RpmV001SchemaPackage
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RpmV001SchemaPackageHash Specifies the hash algorithm and value for the package
//
// swagger:model RpmV001SchemaPackageHash
type RpmV001SchemaPackageHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: ["sha256"]
Algorithm *string `json:"algorithm"`
// The hash value for the package
// Required: true
Value *string `json:"value"`
}
// Validate validates this rpm v001 schema package hash
func (m *RpmV001SchemaPackageHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var rpmV001SchemaPackageHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
rpmV001SchemaPackageHashTypeAlgorithmPropEnum = append(rpmV001SchemaPackageHashTypeAlgorithmPropEnum, v)
}
}
const (
// RpmV001SchemaPackageHashAlgorithmSha256 captures enum value "sha256"
RpmV001SchemaPackageHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *RpmV001SchemaPackageHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, rpmV001SchemaPackageHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *RpmV001SchemaPackageHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("package"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("package"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *RpmV001SchemaPackageHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("package"+"."+"hash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validates this rpm v001 schema package hash based on context it is used
func (m *RpmV001SchemaPackageHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *RpmV001SchemaPackageHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RpmV001SchemaPackageHash) UnmarshalBinary(b []byte) error {
var res RpmV001SchemaPackageHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// RpmV001SchemaPublicKey The PGP public key that can verify the RPM signature
//
// swagger:model RpmV001SchemaPublicKey
type RpmV001SchemaPublicKey struct {
// Specifies the content of the public key inline within the document
// Required: true
// Format: byte
Content *strfmt.Base64 `json:"content"`
}
// Validate validates this rpm v001 schema public key
func (m *RpmV001SchemaPublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *RpmV001SchemaPublicKey) validateContent(formats strfmt.Registry) error {
if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil {
return err
}
return nil
}
// ContextValidate validates this rpm v001 schema public key based on context it is used
func (m *RpmV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *RpmV001SchemaPublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *RpmV001SchemaPublicKey) UnmarshalBinary(b []byte) error {
var res RpmV001SchemaPublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// SearchIndex search index
//
// swagger:model SearchIndex
type SearchIndex struct {
// email
// Format: email
Email strfmt.Email `json:"email,omitempty"`
// hash
// Pattern: ^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$
Hash string `json:"hash,omitempty"`
// operator
// Enum: ["and","or"]
Operator string `json:"operator,omitempty"`
// public key
PublicKey *SearchIndexPublicKey `json:"publicKey,omitempty"`
}
// Validate validates this search index
func (m *SearchIndex) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEmail(formats); err != nil {
res = append(res, err)
}
if err := m.validateHash(formats); err != nil {
res = append(res, err)
}
if err := m.validateOperator(formats); err != nil {
res = append(res, err)
}
if err := m.validatePublicKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *SearchIndex) validateEmail(formats strfmt.Registry) error {
if swag.IsZero(m.Email) { // not required
return nil
}
if err := validate.FormatOf("email", "body", "email", m.Email.String(), formats); err != nil {
return err
}
return nil
}
func (m *SearchIndex) validateHash(formats strfmt.Registry) error {
if swag.IsZero(m.Hash) { // not required
return nil
}
if err := validate.Pattern("hash", "body", m.Hash, `^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$`); err != nil {
return err
}
return nil
}
var searchIndexTypeOperatorPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["and","or"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
searchIndexTypeOperatorPropEnum = append(searchIndexTypeOperatorPropEnum, v)
}
}
const (
// SearchIndexOperatorAnd captures enum value "and"
SearchIndexOperatorAnd string = "and"
// SearchIndexOperatorOr captures enum value "or"
SearchIndexOperatorOr string = "or"
)
// prop value enum
func (m *SearchIndex) validateOperatorEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, searchIndexTypeOperatorPropEnum, true); err != nil {
return err
}
return nil
}
func (m *SearchIndex) validateOperator(formats strfmt.Registry) error {
if swag.IsZero(m.Operator) { // not required
return nil
}
// value enum
if err := m.validateOperatorEnum("operator", "body", m.Operator); err != nil {
return err
}
return nil
}
func (m *SearchIndex) validatePublicKey(formats strfmt.Registry) error {
if swag.IsZero(m.PublicKey) { // not required
return nil
}
if m.PublicKey != nil {
if err := m.PublicKey.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// ContextValidate validate this search index based on the context it is used
func (m *SearchIndex) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidatePublicKey(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *SearchIndex) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error {
if m.PublicKey != nil {
if swag.IsZero(m.PublicKey) { // not required
return nil
}
if err := m.PublicKey.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("publicKey")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("publicKey")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *SearchIndex) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *SearchIndex) UnmarshalBinary(b []byte) error {
var res SearchIndex
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// SearchIndexPublicKey search index public key
//
// swagger:model SearchIndexPublicKey
type SearchIndexPublicKey struct {
// content
// Format: byte
Content strfmt.Base64 `json:"content,omitempty"`
// format
// Required: true
// Enum: ["pgp","x509","minisign","ssh","tuf"]
Format *string `json:"format"`
// url
// Format: uri
URL strfmt.URI `json:"url,omitempty"`
}
// Validate validates this search index public key
func (m *SearchIndexPublicKey) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateFormat(formats); err != nil {
res = append(res, err)
}
if err := m.validateURL(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var searchIndexPublicKeyTypeFormatPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["pgp","x509","minisign","ssh","tuf"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
searchIndexPublicKeyTypeFormatPropEnum = append(searchIndexPublicKeyTypeFormatPropEnum, v)
}
}
const (
// SearchIndexPublicKeyFormatPgp captures enum value "pgp"
SearchIndexPublicKeyFormatPgp string = "pgp"
// SearchIndexPublicKeyFormatX509 captures enum value "x509"
SearchIndexPublicKeyFormatX509 string = "x509"
// SearchIndexPublicKeyFormatMinisign captures enum value "minisign"
SearchIndexPublicKeyFormatMinisign string = "minisign"
// SearchIndexPublicKeyFormatSSH captures enum value "ssh"
SearchIndexPublicKeyFormatSSH string = "ssh"
// SearchIndexPublicKeyFormatTUF captures enum value "tuf"
SearchIndexPublicKeyFormatTUF string = "tuf"
)
// prop value enum
func (m *SearchIndexPublicKey) validateFormatEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, searchIndexPublicKeyTypeFormatPropEnum, true); err != nil {
return err
}
return nil
}
func (m *SearchIndexPublicKey) validateFormat(formats strfmt.Registry) error {
if err := validate.Required("publicKey"+"."+"format", "body", m.Format); err != nil {
return err
}
// value enum
if err := m.validateFormatEnum("publicKey"+"."+"format", "body", *m.Format); err != nil {
return err
}
return nil
}
func (m *SearchIndexPublicKey) validateURL(formats strfmt.Registry) error {
if swag.IsZero(m.URL) { // not required
return nil
}
if err := validate.FormatOf("publicKey"+"."+"url", "body", "uri", m.URL.String(), formats); err != nil {
return err
}
return nil
}
// ContextValidate validates this search index public key based on context it is used
func (m *SearchIndexPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *SearchIndexPublicKey) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *SearchIndexPublicKey) UnmarshalBinary(b []byte) error {
var res SearchIndexPublicKey
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"io"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// SearchLogQuery search log query
//
// swagger:model SearchLogQuery
type SearchLogQuery struct {
entriesField []ProposedEntry
// entry u UI ds
// Max Items: 10
// Min Items: 1
EntryUUIDs []string `json:"entryUUIDs"`
// log indexes
// Max Items: 10
// Min Items: 1
LogIndexes []*int64 `json:"logIndexes"`
}
// Entries gets the entries of this base type
func (m *SearchLogQuery) Entries() []ProposedEntry {
return m.entriesField
}
// SetEntries sets the entries of this base type
func (m *SearchLogQuery) SetEntries(val []ProposedEntry) {
m.entriesField = val
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *SearchLogQuery) UnmarshalJSON(raw []byte) error {
var data struct {
Entries json.RawMessage `json:"entries"`
EntryUUIDs []string `json:"entryUUIDs"`
LogIndexes []*int64 `json:"logIndexes"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var propEntries []ProposedEntry
if string(data.Entries) != "null" {
entries, err := UnmarshalProposedEntrySlice(bytes.NewBuffer(data.Entries), runtime.JSONConsumer())
if err != nil && err != io.EOF {
return err
}
propEntries = entries
}
var result SearchLogQuery
// entries
result.entriesField = propEntries
// entryUUIDs
result.EntryUUIDs = data.EntryUUIDs
// logIndexes
result.LogIndexes = data.LogIndexes
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m SearchLogQuery) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
EntryUUIDs []string `json:"entryUUIDs"`
LogIndexes []*int64 `json:"logIndexes"`
}{
EntryUUIDs: m.EntryUUIDs,
LogIndexes: m.LogIndexes,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Entries []ProposedEntry `json:"entries"`
}{
Entries: m.entriesField,
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this search log query
func (m *SearchLogQuery) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateEntries(formats); err != nil {
res = append(res, err)
}
if err := m.validateEntryUUIDs(formats); err != nil {
res = append(res, err)
}
if err := m.validateLogIndexes(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *SearchLogQuery) validateEntries(formats strfmt.Registry) error {
if swag.IsZero(m.Entries()) { // not required
return nil
}
iEntriesSize := int64(len(m.Entries()))
if err := validate.MinItems("entries", "body", iEntriesSize, 1); err != nil {
return err
}
if err := validate.MaxItems("entries", "body", iEntriesSize, 10); err != nil {
return err
}
for i := 0; i < len(m.Entries()); i++ {
if err := m.entriesField[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("entries" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("entries" + "." + strconv.Itoa(i))
}
return err
}
}
return nil
}
func (m *SearchLogQuery) validateEntryUUIDs(formats strfmt.Registry) error {
if swag.IsZero(m.EntryUUIDs) { // not required
return nil
}
iEntryUUIDsSize := int64(len(m.EntryUUIDs))
if err := validate.MinItems("entryUUIDs", "body", iEntryUUIDsSize, 1); err != nil {
return err
}
if err := validate.MaxItems("entryUUIDs", "body", iEntryUUIDsSize, 10); err != nil {
return err
}
for i := 0; i < len(m.EntryUUIDs); i++ {
if err := validate.Pattern("entryUUIDs"+"."+strconv.Itoa(i), "body", m.EntryUUIDs[i], `^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$`); err != nil {
return err
}
}
return nil
}
func (m *SearchLogQuery) validateLogIndexes(formats strfmt.Registry) error {
if swag.IsZero(m.LogIndexes) { // not required
return nil
}
iLogIndexesSize := int64(len(m.LogIndexes))
if err := validate.MinItems("logIndexes", "body", iLogIndexesSize, 1); err != nil {
return err
}
if err := validate.MaxItems("logIndexes", "body", iLogIndexesSize, 10); err != nil {
return err
}
for i := 0; i < len(m.LogIndexes); i++ {
if swag.IsZero(m.LogIndexes[i]) { // not required
continue
}
if err := validate.MinimumInt("logIndexes"+"."+strconv.Itoa(i), "body", *m.LogIndexes[i], 0, false); err != nil {
return err
}
}
return nil
}
// ContextValidate validate this search log query based on the context it is used
func (m *SearchLogQuery) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateEntries(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *SearchLogQuery) contextValidateEntries(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.Entries()); i++ {
if swag.IsZero(m.entriesField[i]) { // not required
return nil
}
if err := m.entriesField[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("entries" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("entries" + "." + strconv.Itoa(i))
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *SearchLogQuery) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *SearchLogQuery) UnmarshalBinary(b []byte) error {
var res SearchLogQuery
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"bytes"
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// TUF TUF metadata
//
// swagger:model tuf
type TUF struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec TUFSchema `json:"spec"`
}
// Kind gets the kind of this subtype
func (m *TUF) Kind() string {
return "tuf"
}
// SetKind sets the kind of this subtype
func (m *TUF) SetKind(val string) {
}
// UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure
func (m *TUF) UnmarshalJSON(raw []byte) error {
var data struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec TUFSchema `json:"spec"`
}
buf := bytes.NewBuffer(raw)
dec := json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&data); err != nil {
return err
}
var base struct {
/* Just the base type fields. Used for unmashalling polymorphic types.*/
Kind string `json:"kind"`
}
buf = bytes.NewBuffer(raw)
dec = json.NewDecoder(buf)
dec.UseNumber()
if err := dec.Decode(&base); err != nil {
return err
}
var result TUF
if base.Kind != result.Kind() {
/* Not the type we're looking for. */
return errors.New(422, "invalid kind value: %q", base.Kind)
}
result.APIVersion = data.APIVersion
result.Spec = data.Spec
*m = result
return nil
}
// MarshalJSON marshals this object with a polymorphic type to a JSON structure
func (m TUF) MarshalJSON() ([]byte, error) {
var b1, b2, b3 []byte
var err error
b1, err = json.Marshal(struct {
// api version
// Required: true
// Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
APIVersion *string `json:"apiVersion"`
// spec
// Required: true
Spec TUFSchema `json:"spec"`
}{
APIVersion: m.APIVersion,
Spec: m.Spec,
})
if err != nil {
return nil, err
}
b2, err = json.Marshal(struct {
Kind string `json:"kind"`
}{
Kind: m.Kind(),
})
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2, b3), nil
}
// Validate validates this tuf
func (m *TUF) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAPIVersion(formats); err != nil {
res = append(res, err)
}
if err := m.validateSpec(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TUF) validateAPIVersion(formats strfmt.Registry) error {
if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil {
return err
}
if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil {
return err
}
return nil
}
func (m *TUF) validateSpec(formats strfmt.Registry) error {
if m.Spec == nil {
return errors.Required("spec", "body", nil)
}
return nil
}
// ContextValidate validate this tuf based on the context it is used
func (m *TUF) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *TUF) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TUF) UnmarshalBinary(b []byte) error {
var res TUF
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// Code generated by go-swagger; DO NOT EDIT.
//
// 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 models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// TUFV001Schema TUF v0.0.1 Schema
//
// # Schema for TUF metadata entries
//
// swagger:model tufV001Schema
type TUFV001Schema struct {
// metadata
// Required: true
Metadata *TUFV001SchemaMetadata `json:"metadata"`
// root
// Required: true
Root *TUFV001SchemaRoot `json:"root"`
// TUF specification version
// Read Only: true
SpecVersion string `json:"spec_version,omitempty"`
}
// Validate validates this tuf v001 schema
func (m *TUFV001Schema) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateMetadata(formats); err != nil {
res = append(res, err)
}
if err := m.validateRoot(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TUFV001Schema) validateMetadata(formats strfmt.Registry) error {
if err := validate.Required("metadata", "body", m.Metadata); err != nil {
return err
}
if m.Metadata != nil {
if err := m.Metadata.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("metadata")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("metadata")
}
return err
}
}
return nil
}
func (m *TUFV001Schema) validateRoot(formats strfmt.Registry) error {
if err := validate.Required("root", "body", m.Root); err != nil {
return err
}
if m.Root != nil {
if err := m.Root.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("root")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("root")
}
return err
}
}
return nil
}
// ContextValidate validate this tuf v001 schema based on the context it is used
func (m *TUFV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateMetadata(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateRoot(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateSpecVersion(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TUFV001Schema) contextValidateMetadata(ctx context.Context, formats strfmt.Registry) error {
if m.Metadata != nil {
if err := m.Metadata.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("metadata")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("metadata")
}
return err
}
}
return nil
}
func (m *TUFV001Schema) contextValidateRoot(ctx context.Context, formats strfmt.Registry) error {
if m.Root != nil {
if err := m.Root.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("root")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("root")
}
return err
}
}
return nil
}
func (m *TUFV001Schema) contextValidateSpecVersion(ctx context.Context, formats strfmt.Registry) error {
if err := validate.ReadOnly(ctx, "spec_version", "body", string(m.SpecVersion)); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *TUFV001Schema) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TUFV001Schema) UnmarshalBinary(b []byte) error {
var res TUFV001Schema
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// TUFV001SchemaMetadata TUF metadata
//
// swagger:model TUFV001SchemaMetadata
type TUFV001SchemaMetadata struct {
// Specifies the metadata inline within the document
// Required: true
Content interface{} `json:"content"`
}
// Validate validates this TUF v001 schema metadata
func (m *TUFV001SchemaMetadata) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TUFV001SchemaMetadata) validateContent(formats strfmt.Registry) error {
if m.Content == nil {
return errors.Required("metadata"+"."+"content", "body", nil)
}
return nil
}
// ContextValidate validates this TUF v001 schema metadata based on context it is used
func (m *TUFV001SchemaMetadata) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TUFV001SchemaMetadata) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TUFV001SchemaMetadata) UnmarshalBinary(b []byte) error {
var res TUFV001SchemaMetadata
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// TUFV001SchemaRoot root metadata containing about the public keys used to sign the manifest
//
// swagger:model TUFV001SchemaRoot
type TUFV001SchemaRoot struct {
// Specifies the metadata inline within the document
// Required: true
Content interface{} `json:"content"`
}
// Validate validates this TUF v001 schema root
func (m *TUFV001SchemaRoot) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateContent(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TUFV001SchemaRoot) validateContent(formats strfmt.Registry) error {
if m.Content == nil {
return errors.Required("root"+"."+"content", "body", nil)
}
return nil
}
// ContextValidate validates this TUF v001 schema root based on context it is used
func (m *TUFV001SchemaRoot) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TUFV001SchemaRoot) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TUFV001SchemaRoot) UnmarshalBinary(b []byte) error {
var res TUFV001SchemaRoot
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
//
// 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 log
import (
"context"
"fmt"
"log"
"github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Logger set the default logger to development mode
var Logger *zap.SugaredLogger
var traceStringPrefix string
func init() {
ConfigureLogger("dev", "")
}
func ConfigureLogger(logType, traceStrPrefix string) {
var cfg zap.Config
if logType == "prod" {
cfg = zap.NewProductionConfig()
cfg.EncoderConfig.LevelKey = "severity"
cfg.EncoderConfig.MessageKey = "message"
cfg.EncoderConfig.TimeKey = "time"
cfg.EncoderConfig.EncodeLevel = encodeLevel()
cfg.EncoderConfig.EncodeTime = zapcore.RFC3339NanoTimeEncoder
cfg.EncoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
cfg.EncoderConfig.EncodeCaller = zapcore.FullCallerEncoder
} else {
cfg = zap.NewDevelopmentConfig()
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
}
logger, err := cfg.Build()
if err != nil {
log.Fatalln("createLogger", err)
}
Logger = logger.Sugar()
if traceStrPrefix != "" {
traceStringPrefix = traceStrPrefix
}
}
func encodeLevel() zapcore.LevelEncoder {
return func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
switch l {
case zapcore.DebugLevel:
enc.AppendString("DEBUG")
case zapcore.InfoLevel:
enc.AppendString("INFO")
case zapcore.WarnLevel:
enc.AppendString("WARNING")
case zapcore.ErrorLevel:
enc.AppendString("ERROR")
case zapcore.DPanicLevel:
enc.AppendString("CRITICAL")
case zapcore.PanicLevel:
enc.AppendString("ALERT")
case zapcore.FatalLevel:
enc.AppendString("EMERGENCY")
}
}
}
var CliLogger = createCliLogger()
func createCliLogger() *zap.SugaredLogger {
cfg := zap.NewDevelopmentConfig()
cfg.EncoderConfig.TimeKey = ""
cfg.EncoderConfig.LevelKey = ""
cfg.DisableCaller = true
cfg.DisableStacktrace = true
logger, err := cfg.Build()
if err != nil {
log.Fatalln("createLogger", err)
}
return logger.Sugar()
}
func WithRequestID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, middleware.RequestIDKey, id)
}
type operation struct {
id string
}
func (o operation) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("id", o.id)
return nil
}
func ContextLogger(ctx context.Context) *zap.SugaredLogger {
proposedLogger := Logger
if ctx != nil {
if ctxRequestID, ok := ctx.Value(middleware.RequestIDKey).(string); ok {
requestID := operation{ctxRequestID}
proposedLogger = proposedLogger.With(zap.Object("operation", requestID))
if traceStringPrefix != "" {
proposedLogger = proposedLogger.With(zap.String("logging.googleapis.com/trace", fmt.Sprintf("%s/%s", traceStringPrefix, ctxRequestID)))
}
}
}
return proposedLogger
}
//
// 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 pki
import (
"fmt"
"io"
"github.com/sigstore/rekor/pkg/pki/minisign"
"github.com/sigstore/rekor/pkg/pki/pgp"
"github.com/sigstore/rekor/pkg/pki/pkcs7"
"github.com/sigstore/rekor/pkg/pki/ssh"
"github.com/sigstore/rekor/pkg/pki/tuf"
"github.com/sigstore/rekor/pkg/pki/x509"
)
type Format string
const (
PGP Format = "pgp"
Minisign Format = "minisign"
SSH Format = "ssh"
X509 Format = "x509"
PKCS7 Format = "pkcs7"
Tuf Format = "tuf"
)
type ArtifactFactory struct {
impl pkiImpl
}
func NewArtifactFactory(format Format) (*ArtifactFactory, error) {
if impl, ok := artifactFactoryMap[format]; ok {
return &ArtifactFactory{impl: impl}, nil
}
return nil, fmt.Errorf("%v is not a supported PKI format", format)
}
type pkiImpl struct {
newPubKey func(io.Reader) (PublicKey, error)
newSignature func(io.Reader) (Signature, error)
}
var artifactFactoryMap map[Format]pkiImpl
func init() {
artifactFactoryMap = map[Format]pkiImpl{
PGP: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return pgp.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return pgp.NewSignature(r)
},
},
Minisign: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return minisign.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return minisign.NewSignature(r)
},
},
SSH: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return ssh.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return ssh.NewSignature(r)
},
},
X509: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return x509.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return x509.NewSignature(r)
},
},
PKCS7: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return pkcs7.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return pkcs7.NewSignature(r)
},
},
Tuf: {
newPubKey: func(r io.Reader) (PublicKey, error) {
return tuf.NewPublicKey(r)
},
newSignature: func(r io.Reader) (Signature, error) {
return tuf.NewSignature(r)
},
},
}
}
func SupportedFormats() []string {
var formats []string
for f := range artifactFactoryMap {
formats = append(formats, string(f))
}
return formats
}
func (a ArtifactFactory) NewPublicKey(r io.Reader) (PublicKey, error) {
return a.impl.newPubKey(r)
}
func (a ArtifactFactory) NewSignature(r io.Reader) (Signature, error) {
return a.impl.newSignature(r)
}
//
// 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 minisign
import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
minisign "github.com/jedisct1/go-minisign"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
"golang.org/x/crypto/blake2b"
)
// Signature Signature that follows the minisign standard; supports both minisign and signify generated signatures
type Signature struct {
signature *minisign.Signature
}
// NewSignature creates and validates a minisign signature object
func NewSignature(r io.Reader) (*Signature, error) {
var s Signature
var inputBuffer bytes.Buffer
if _, err := io.Copy(&inputBuffer, r); err != nil {
return nil, fmt.Errorf("unable to read minisign signature: %w", err)
}
inputString := inputBuffer.String()
signature, err := minisign.DecodeSignature(inputString)
if err != nil {
// try to parse as signify
lines := strings.Split(strings.TrimRight(inputString, "\n"), "\n")
if len(lines) != 2 {
return nil, fmt.Errorf("invalid signature provided: %v lines detected", len(lines))
}
sigBytes, b64Err := base64.StdEncoding.DecodeString(lines[1])
if b64Err != nil {
return nil, errors.New("invalid signature provided: base64 decoding failed")
}
if len(sigBytes) != len(signature.SignatureAlgorithm)+len(signature.KeyId)+len(signature.Signature) {
return nil, fmt.Errorf("invalid signature provided: incorrect size %v detected", len(sigBytes))
}
copy(signature.SignatureAlgorithm[:], sigBytes[0:2])
copy(signature.KeyId[:], sigBytes[2:10])
copy(signature.Signature[:], sigBytes[10:])
}
s.signature = &signature
return &s, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
if s.signature == nil {
return nil, errors.New("minisign signature has not been initialized")
}
buf := bytes.NewBuffer([]byte("untrusted comment:\n"))
b64Buf := bytes.NewBuffer(s.signature.SignatureAlgorithm[:])
if _, err := b64Buf.Write(s.signature.KeyId[:]); err != nil {
return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err)
}
if _, err := b64Buf.Write(s.signature.Signature[:]); err != nil {
return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err)
}
if _, err := buf.WriteString(base64.StdEncoding.EncodeToString(b64Buf.Bytes())); err != nil {
return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err)
}
return buf.Bytes(), nil
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
if s.signature == nil {
return errors.New("minisign signature has not been initialized")
}
key, ok := k.(*PublicKey)
if !ok {
return errors.New("cannot use Verify with a non-minisign key")
}
if key.key == nil {
return errors.New("minisign public key has not been initialized")
}
verifier, err := sigsig.LoadED25519Verifier(key.key.PublicKey[:])
if err != nil {
return err
}
prehashed := s.signature.SignatureAlgorithm[1] == 0x44
if prehashed {
h, _ := blake2b.New512(nil)
_, err := io.Copy(h, r)
if err != nil {
return errors.New("reading minisign data")
}
r = bytes.NewReader(h.Sum(nil))
}
return verifier.VerifySignature(bytes.NewReader(s.signature.Signature[:]), r, opts...)
}
// PublicKey Public Key that follows the minisign standard; supports signify and minisign public keys
type PublicKey struct {
key *minisign.PublicKey
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
var k PublicKey
var inputBuffer bytes.Buffer
if _, err := io.Copy(&inputBuffer, r); err != nil {
return nil, fmt.Errorf("unable to read minisign public key: %w", err)
}
inputString := inputBuffer.String()
// There are three ways a minisign key can be stored.
// 1. The entire text key
// 2. A base64 encoded string
// 3. A legacy format we stored of just the key material (no key ID or Algorithm) due to bug fixed in https://github.com/sigstore/rekor/pull/562
key, err := minisign.DecodePublicKey(inputString)
if err == nil {
k.key = &key
return &k, nil
}
key, err = minisign.NewPublicKey(inputString)
if err == nil {
k.key = &key
return &k, nil
}
if len(inputString) == 32 {
k.key = &minisign.PublicKey{
SignatureAlgorithm: [2]byte{'E', 'd'},
KeyId: [8]byte{},
}
copy(k.key.PublicKey[:], inputBuffer.Bytes())
return &k, nil
}
return nil, fmt.Errorf("unable to read minisign public key: %w", err)
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() ([]byte, error) {
if k.key == nil {
return nil, errors.New("minisign public key has not been initialized")
}
bin := []byte{}
bin = append(bin, k.key.SignatureAlgorithm[:]...)
bin = append(bin, k.key.KeyId[:]...)
bin = append(bin, k.key.PublicKey[:]...)
b64Key := base64.StdEncoding.EncodeToString(bin)
return []byte(b64Key), nil
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
return nil
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
return nil
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// PKIX encode ed25519 public key
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(k.key.PublicKey[:]))
if err != nil {
return nil, err
}
digest := sha256.Sum256(pkixKey)
return []identity.Identity{{
Crypto: k.key,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(digest[:]),
}}, nil
}
//
// 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 pgp
import (
"bufio"
"bytes"
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"encoding/hex"
"errors"
"fmt"
"io"
"net/http"
"github.com/asaskevich/govalidator"
//TODO: https://github.com/sigstore/rekor/issues/286
"golang.org/x/crypto/openpgp" //nolint:staticcheck
"golang.org/x/crypto/openpgp/armor" //nolint:staticcheck
"golang.org/x/crypto/openpgp/packet" //nolint:staticcheck
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
)
// Signature that follows the PGP standard; supports both armored & binary detached signatures
type Signature struct {
isArmored bool
signature []byte
}
// NewSignature creates and validates a PGP signature object
func NewSignature(r io.Reader) (*Signature, error) {
var s Signature
var inputBuffer bytes.Buffer
if _, err := io.Copy(&inputBuffer, r); err != nil {
return nil, fmt.Errorf("unable to read PGP signature: %w", err)
}
sigByteReader := bytes.NewReader(inputBuffer.Bytes())
var sigReader io.Reader
sigBlock, err := armor.Decode(sigByteReader)
if err == nil {
s.isArmored = true
if sigBlock.Type != openpgp.SignatureType {
return nil, errors.New("invalid PGP signature provided")
}
sigReader = sigBlock.Body
} else {
s.isArmored = false
if _, err := sigByteReader.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("unable to read binary PGP signature: %w", err)
}
sigReader = sigByteReader
}
sigPktReader := packet.NewReader(sigReader)
sigPkt, err := sigPktReader.Next()
if err != nil {
return nil, fmt.Errorf("invalid PGP signature: %w", err)
}
if _, ok := sigPkt.(*packet.Signature); !ok {
if _, ok := sigPkt.(*packet.SignatureV3); !ok {
return nil, errors.New("valid PGP signature was not detected")
}
}
s.signature = inputBuffer.Bytes()
return &s, nil
}
// FetchSignature implements pki.Signature interface
func FetchSignature(ctx context.Context, url string) (*Signature, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("error initializing fetch for PGP signature: %w", err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error fetching PGP signature: %w", err)
}
defer resp.Body.Close()
sig, err := NewSignature(resp.Body)
if err != nil {
return nil, err
}
return sig, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
if len(s.signature) == 0 {
return nil, errors.New("PGP signature has not been initialized")
}
if s.isArmored {
return s.signature, nil
}
var canonicalBuffer bytes.Buffer
// Use an inner function so we can defer the Close()
if err := func() error {
ew, err := armor.Encode(&canonicalBuffer, openpgp.SignatureType, nil)
if err != nil {
return fmt.Errorf("error encoding canonical value of PGP signature: %w", err)
}
defer ew.Close()
if _, err := io.Copy(ew, bytes.NewReader(s.signature)); err != nil {
return fmt.Errorf("error generating canonical value of PGP signature: %w", err)
}
return nil
}(); err != nil {
return nil, err
}
return canonicalBuffer.Bytes(), nil
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error {
if len(s.signature) == 0 {
return errors.New("PGP signature has not been initialized")
}
key, ok := k.(*PublicKey)
if !ok {
return errors.New("cannot use Verify with a non-PGP signature")
}
if len(key.key) == 0 {
return errors.New("PGP public key has not been initialized")
}
verifyFn := openpgp.CheckDetachedSignature
if s.isArmored {
verifyFn = openpgp.CheckArmoredDetachedSignature
}
if _, err := verifyFn(key.key, r, bytes.NewReader(s.signature)); err != nil {
return err
}
return nil
}
// PublicKey Public Key that follows the PGP standard; supports both armored & binary detached signatures
type PublicKey struct {
key openpgp.EntityList
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
var k PublicKey
var inputBuffer bytes.Buffer
startToken := []byte(`-----BEGIN PGP`)
endToken := []byte(`-----END PGP`)
bufferedReader := bufio.NewReader(r)
armorCheck, err := bufferedReader.Peek(len(startToken))
if err != nil {
return nil, fmt.Errorf("unable to read PGP public key: %w", err)
}
if bytes.Equal(startToken, armorCheck) {
// looks like we have armored input
scan := bufio.NewScanner(bufferedReader)
scan.Split(bufio.ScanLines)
for scan.Scan() {
line := scan.Bytes()
inputBuffer.Write(line)
fmt.Fprintf(&inputBuffer, "\n")
if bytes.HasPrefix(line, endToken) {
// we have a complete armored message; process it
keyBlock, err := armor.Decode(&inputBuffer)
if err == nil {
if keyBlock.Type != openpgp.PublicKeyType && keyBlock.Type != openpgp.PrivateKeyType {
return nil, errors.New("invalid PGP type detected")
}
keys, err := openpgp.ReadKeyRing(keyBlock.Body)
if err != nil {
return nil, fmt.Errorf("error reading PGP public key: %w", err)
}
if k.key == nil {
k.key = keys
} else {
k.key = append(k.key, keys...)
}
inputBuffer.Reset()
} else {
return nil, fmt.Errorf("invalid PGP public key provided: %w", err)
}
}
}
} else {
// process as binary
k.key, err = openpgp.ReadKeyRing(bufferedReader)
if err != nil {
return nil, fmt.Errorf("error reading binary PGP public key: %w", err)
}
}
if len(k.key) == len(k.key.DecryptionKeys()) {
return nil, errors.New("no PGP public keys could be read")
}
return &k, nil
}
// FetchPublicKey implements pki.PublicKey interface
func FetchPublicKey(ctx context.Context, url string) (*PublicKey, error) {
//TODO: detect if url is hkp and adjust accordingly
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("error fetching PGP public key: %w", err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error fetching PGP public key: %w", err)
}
defer resp.Body.Close()
key, err := NewPublicKey(resp.Body)
if err != nil {
return nil, err
}
return key, nil
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() ([]byte, error) {
if k.key == nil {
return nil, errors.New("PGP public key has not been initialized")
}
var canonicalBuffer bytes.Buffer
// Use an inner function so we can defer the close()
if err := func() error {
armoredWriter, err := armor.Encode(&canonicalBuffer, openpgp.PublicKeyType, nil)
if err != nil {
return fmt.Errorf("error generating canonical value of PGP public key: %w", err)
}
defer armoredWriter.Close()
for _, entity := range k.key {
if err := entity.Serialize(armoredWriter); err != nil {
return fmt.Errorf("error generating canonical value of PGP public key: %w", err)
}
}
return nil
}(); err != nil {
return nil, err
}
return canonicalBuffer.Bytes(), nil
}
func (k PublicKey) KeyRing() (openpgp.KeyRing, error) {
if k.key == nil {
return nil, errors.New("PGP public key has not been initialized")
}
return k.key, nil
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
var names []string
// Extract from cert
for _, entity := range k.key {
for _, identity := range entity.Identities {
if govalidator.IsEmail(identity.UserId.Email) {
names = append(names, identity.UserId.Email)
}
}
}
return names
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
return k.EmailAddresses()
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
var ids []identity.Identity
for _, entity := range k.key {
var keys []*packet.PublicKey
keys = append(keys, entity.PrimaryKey)
for _, subKey := range entity.Subkeys {
keys = append(keys, subKey.PublicKey)
}
for _, pk := range keys {
pubKey := pk.PublicKey
// Only process supported types. Will ignore DSA
// and ElGamal keys.
// TODO: For a V2 PGP type, enforce on upload
switch pubKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
default:
continue
}
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pubKey)
if err != nil {
return nil, err
}
ids = append(ids, identity.Identity{
Crypto: pubKey,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(pk.Fingerprint[:]),
})
}
}
return ids, nil
}
/*
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 pkcs7
import (
"bytes"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"io"
"strings"
"github.com/sassoftware/relic/lib/pkcs7"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
)
// EmailAddressOID defined by https://oidref.com/1.2.840.113549.1.9.1
var EmailAddressOID asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 9, 1}
type Signature struct {
signedData pkcs7.SignedData
detached bool
raw *[]byte
}
// NewSignature creates and validates an PKCS7 signature object
func NewSignature(r io.Reader) (*Signature, error) {
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
// try PEM decoding first
var pkcsBytes *[]byte
block, _ := pem.Decode(b)
if block != nil {
if block.Type != "PKCS7" {
return nil, fmt.Errorf("unknown PEM block type %s found during PKCS7 parsing", block.Type)
}
pkcsBytes = &block.Bytes
} else {
// PEM decoding failed, it might just be raw ASN.1 data
pkcsBytes = &b
}
psd, err := pkcs7.Unmarshal(*pkcsBytes)
if err != nil {
return nil, err
}
// we store the detached signature as the raw, canonical format
if _, err := psd.Detach(); err != nil {
return nil, err
}
detached, err := psd.Marshal()
if err != nil {
return nil, err
}
cb, err := psd.Content.ContentInfo.Bytes()
if err != nil {
return nil, err
}
return &Signature{
signedData: psd.Content,
raw: &detached,
detached: cb == nil,
}, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
if s.raw == nil {
return nil, errors.New("PKCS7 signature has not been initialized")
}
p := pem.Block{
Type: "PKCS7",
Bytes: *s.raw,
}
var buf bytes.Buffer
if err := pem.Encode(&buf, &p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(r io.Reader, _ interface{}, _ ...sigsig.VerifyOption) error {
if len(*s.raw) == 0 {
return errors.New("PKCS7 signature has not been initialized")
}
// if content was passed to this, verify signature as if it were detached
bb := bytes.Buffer{}
var extContent []byte
if r != nil {
n, err := io.Copy(&bb, r)
if err != nil {
return err
}
if n > 0 {
extContent = bb.Bytes()
} else if s.detached {
return errors.New("PKCS7 signature is detached and there is no external content to verify against")
}
}
if _, err := s.signedData.Verify(extContent, false); err != nil {
return err
}
return nil
}
// PublicKey Public Key contained in cert inside PKCS7 bundle
type PublicKey struct {
key crypto.PublicKey
certs []*x509.Certificate
rawCert []byte
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
rawPub, err := io.ReadAll(r)
if err != nil {
return nil, err
}
// try PEM decoding first
var pkcsBytes *[]byte
block, _ := pem.Decode(rawPub)
if block != nil {
if block.Type != "PKCS7" {
return nil, fmt.Errorf("unknown PEM block type %s found during PKCS7 parsing", block.Type)
}
pkcsBytes = &block.Bytes
} else {
// PEM decoding failed, it might just be raw ASN.1 data
pkcsBytes = &rawPub
}
pkcs7, err := pkcs7.Unmarshal(*pkcsBytes)
if err != nil {
return nil, err
}
certs, err := pkcs7.Content.Certificates.Parse()
if err != nil {
return nil, err
}
for _, cert := range certs {
return &PublicKey{key: cert.PublicKey, certs: certs, rawCert: cert.Raw}, nil
}
return nil, errors.New("unable to extract public key from certificate inside PKCS7 bundle")
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() ([]byte, error) {
if k.rawCert == nil {
return nil, errors.New("PKCS7 public key has not been initialized")
}
//TODO: should we export the entire cert chain, not just the first one?
p := pem.Block{
Type: "CERTIFICATE",
Bytes: k.rawCert,
}
var buf bytes.Buffer
if err := pem.Encode(&buf, &p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
var names []string
// Get email address from Subject name in raw cert.
cert, err := x509.ParseCertificate(k.rawCert)
if err != nil {
// This should not happen from a valid PublicKey, but fail gracefully.
return names
}
for _, name := range cert.Subject.Names {
if name.Type.Equal(EmailAddressOID) {
names = append(names, strings.ToLower(name.Value.(string)))
}
}
return names
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
// combine identities in the subject and SANs
identities := k.EmailAddresses()
cert, err := x509.ParseCertificate(k.certs[0].Raw)
if err != nil {
// This should not happen from a valid PublicKey, but fail gracefully.
return identities
}
identities = append(identities, cryptoutils.GetSubjectAlternateNames(cert)...)
return identities
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// pkcs7 structure may contain both a key and certificate chain
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key)
if err != nil {
return nil, err
}
keyDigest := sha256.Sum256(pkixKey)
certDigest := sha256.Sum256(k.certs[0].Raw)
return []identity.Identity{{
Crypto: k.certs[0],
Raw: k.certs[0].Raw,
Fingerprint: hex.EncodeToString(certDigest[:]),
}, {
Crypto: k.key,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(keyDigest[:]),
},
}, nil
}
//
// 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 ssh
import (
"encoding/pem"
"errors"
"fmt"
"golang.org/x/crypto/ssh"
)
const (
namespace = "file"
pemType = "SSH SIGNATURE"
)
func Armor(s *ssh.Signature, p ssh.PublicKey) []byte {
sig := WrappedSig{
Version: 1,
PublicKey: string(p.Marshal()),
Namespace: namespace,
HashAlgorithm: defaultHashAlgorithm,
Signature: string(ssh.Marshal(s)),
}
copy(sig.MagicHeader[:], magicHeader)
enc := pem.EncodeToMemory(&pem.Block{
Type: pemType,
Bytes: ssh.Marshal(sig),
})
return enc
}
func Decode(b []byte) (*Signature, error) {
pemBlock, _ := pem.Decode(b)
if pemBlock == nil {
return nil, errors.New("unable to decode pem file")
}
if pemBlock.Type != pemType {
return nil, fmt.Errorf("wrong pem block type: %s. Expected SSH-SIGNATURE", pemBlock.Type)
}
// Now we unmarshal it into the Signature block
sig := WrappedSig{}
if err := ssh.Unmarshal(pemBlock.Bytes, &sig); err != nil {
return nil, err
}
if sig.Version != 1 {
return nil, fmt.Errorf("unsupported signature version: %d", sig.Version)
}
if string(sig.MagicHeader[:]) != magicHeader {
return nil, fmt.Errorf("invalid magic header: %s", sig.MagicHeader[:])
}
if sig.Namespace != "file" {
return nil, fmt.Errorf("invalid signature namespace: %s", sig.Namespace)
}
if _, ok := supportedHashAlgorithms[sig.HashAlgorithm]; !ok {
return nil, fmt.Errorf("unsupported hash algorithm: %s", sig.HashAlgorithm)
}
// Now we can unpack the Signature and PublicKey blocks
sshSig := ssh.Signature{}
if err := ssh.Unmarshal([]byte(sig.Signature), &sshSig); err != nil {
return nil, err
}
// TODO: check the format here (should be rsa-sha512)
pk, err := ssh.ParsePublicKey([]byte(sig.PublicKey))
if err != nil {
return nil, err
}
return &Signature{
signature: &sshSig,
pk: pk,
hashAlg: sig.HashAlgorithm,
}, nil
}
//
// 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 ssh
import (
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"hash"
"io"
"golang.org/x/crypto/ssh"
)
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L81
type MessageWrapper struct {
Namespace string
Reserved string
HashAlgorithm string
Hash string
}
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L34
type WrappedSig struct {
MagicHeader [6]byte
Version uint32
PublicKey string
Namespace string
Reserved string
HashAlgorithm string
Signature string
}
const (
magicHeader = "SSHSIG"
defaultHashAlgorithm = "sha512"
)
var supportedHashAlgorithms = map[string]func() hash.Hash{
"sha256": sha256.New,
"sha512": sha512.New,
}
func sign(s ssh.AlgorithmSigner, m io.Reader) (*ssh.Signature, error) {
hf := sha512.New()
if _, err := io.Copy(hf, m); err != nil {
return nil, err
}
mh := hf.Sum(nil)
sp := MessageWrapper{
Namespace: "file",
HashAlgorithm: defaultHashAlgorithm,
Hash: string(mh),
}
dataMessageWrapper := ssh.Marshal(sp)
dataMessageWrapper = append([]byte(magicHeader), dataMessageWrapper...)
// ssh-rsa is not supported for RSA keys:
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L71
// We can use the default value of "" for other key types though.
algo := ""
if s.PublicKey().Type() == ssh.KeyAlgoRSA {
algo = ssh.KeyAlgoRSASHA512
}
sig, err := s.SignWithAlgorithm(rand.Reader, dataMessageWrapper, algo)
if err != nil {
return nil, err
}
return sig, nil
}
func Sign(sshPrivateKey string, data io.Reader) ([]byte, error) {
s, err := ssh.ParsePrivateKey([]byte(sshPrivateKey))
if err != nil {
return nil, err
}
as, ok := s.(ssh.AlgorithmSigner)
if !ok {
return nil, err
}
sig, err := sign(as, data)
if err != nil {
return nil, err
}
armored := Armor(sig, s.PublicKey())
return armored, nil
}
//
// 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 ssh
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"encoding/binary"
"errors"
"fmt"
"io"
"net/http"
"github.com/asaskevich/govalidator"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
"golang.org/x/crypto/ssh"
)
type Signature struct {
signature *ssh.Signature
pk ssh.PublicKey
hashAlg string
}
// NewSignature creates and Validates an ssh signature object
func NewSignature(r io.Reader) (*Signature, error) {
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
sig, err := Decode(b)
if err != nil {
return nil, err
}
return sig, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
return []byte(Armor(s.signature, s.pk)), nil
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error {
if s.signature == nil {
return errors.New("ssh signature has not been initialized")
}
key, ok := k.(*PublicKey)
if !ok {
return fmt.Errorf("invalid public key type for: %v", k)
}
ck, err := key.CanonicalValue()
if err != nil {
return err
}
cs, err := s.CanonicalValue()
if err != nil {
return err
}
return Verify(r, cs, ck)
}
// PublicKey contains an ssh PublicKey
type PublicKey struct {
key ssh.PublicKey
comment string
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
// 64K seems generous as a limit for valid SSH keys
// we use http.MaxBytesReader and pass nil for ResponseWriter to reuse stdlib
// and not reimplement this; There is a proposal for this to be fixed in 1.20
// https://github.com/golang/go/issues/51115
// TODO: switch this to stdlib once golang 1.20 comes out
rawPub, err := io.ReadAll(http.MaxBytesReader(nil, io.NopCloser(r), 65536))
if err != nil {
return nil, err
}
key, comment, _, _, err := ssh.ParseAuthorizedKey(rawPub)
if err != nil {
return nil, err
}
return &PublicKey{key: key, comment: comment}, nil
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() ([]byte, error) {
if k.key == nil {
return nil, errors.New("ssh public key has not been initialized")
}
return ssh.MarshalAuthorizedKey(k.key), nil
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
if govalidator.IsEmail(k.comment) {
return []string{k.comment}
}
return nil
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
return k.EmailAddresses()
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// extract key from SSH certificate if present
var sshKey ssh.PublicKey
switch v := k.key.(type) {
case *ssh.Certificate:
sshKey = v.Key
default:
sshKey = k.key
}
// Extract crypto.PublicKey from SSH key
// Handle sk public keys which do not implement ssh.CryptoPublicKey
// Inspired by x/ssh/keys.go
// TODO: Simplify after https://github.com/golang/go/issues/62518
var cryptoPubKey crypto.PublicKey
if v, ok := sshKey.(ssh.CryptoPublicKey); ok {
cryptoPubKey = v.CryptoPublicKey()
} else {
switch sshKey.Type() {
case ssh.KeyAlgoSKECDSA256:
var w struct {
Curve string
KeyBytes []byte
Application string
Rest []byte `ssh:"rest"`
}
_, k, ok := parseString(sshKey.Marshal())
if !ok {
return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key")
}
if err := ssh.Unmarshal(k, &w); err != nil {
return nil, err
}
if w.Curve != "nistp256" {
return nil, errors.New("ssh: unsupported curve")
}
ecdsaPubKey := new(ecdsa.PublicKey)
ecdsaPubKey.Curve = elliptic.P256()
//nolint:staticcheck // ignore SA1019 for old code
ecdsaPubKey.X, ecdsaPubKey.Y = elliptic.Unmarshal(ecdsaPubKey.Curve, w.KeyBytes)
if ecdsaPubKey.X == nil || ecdsaPubKey.Y == nil {
return nil, errors.New("ssh: invalid curve point")
}
cryptoPubKey = ecdsaPubKey
case ssh.KeyAlgoSKED25519:
var w struct {
KeyBytes []byte
Application string
Rest []byte `ssh:"rest"`
}
_, k, ok := parseString(sshKey.Marshal())
if !ok {
return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key")
}
if err := ssh.Unmarshal(k, &w); err != nil {
return nil, err
}
if l := len(w.KeyBytes); l != ed25519.PublicKeySize {
return nil, fmt.Errorf("invalid size %d for Ed25519 public key", l)
}
cryptoPubKey = ed25519.PublicKey(w.KeyBytes)
default:
// Should not occur, as rsa, dsa, ecdsa, and ed25519 all implement ssh.CryptoPublicKey
return nil, fmt.Errorf("unknown key type: %T", sshKey)
}
}
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(cryptoPubKey)
if err != nil {
return nil, err
}
fp := ssh.FingerprintSHA256(k.key)
return []identity.Identity{{
Crypto: k.key,
Raw: pkixKey,
Fingerprint: fp,
}}, nil
}
// Copied by x/ssh/keys.go
// TODO: Remove after https://github.com/golang/go/issues/62518
func parseString(in []byte) (out, rest []byte, ok bool) {
if len(in) < 4 {
return
}
length := binary.BigEndian.Uint32(in)
in = in[4:]
if uint32(len(in)) < length {
return
}
out = in[:length]
rest = in[length:]
ok = true
return
}
//
// 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 ssh
import (
"io"
"golang.org/x/crypto/ssh"
)
func Verify(message io.Reader, armoredSignature []byte, publicKey []byte) error {
decodedSignature, err := Decode(armoredSignature)
if err != nil {
return err
}
desiredPk, _, _, _, err := ssh.ParseAuthorizedKey(publicKey)
if err != nil {
return err
}
// Hash the message so we can verify it against the signature.
h := supportedHashAlgorithms[decodedSignature.hashAlg]()
if _, err := io.Copy(h, message); err != nil {
return err
}
hm := h.Sum(nil)
toVerify := MessageWrapper{
Namespace: "file",
HashAlgorithm: decodedSignature.hashAlg,
Hash: string(hm),
}
signedMessage := ssh.Marshal(toVerify)
signedMessage = append([]byte(magicHeader), signedMessage...)
return desiredPk.Verify(signedMessage, decodedSignature.signature)
}
//
// 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 tuf
import (
"crypto/ed25519"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"time"
"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/pkg/keys"
"github.com/theupdateframework/go-tuf/verify"
)
type Signature struct {
signed *data.Signed
Role string
Version int
}
type signedMeta struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
SpecVersion string `json:"spec_version"`
}
// NewSignature creates and validates a TUF signed manifest
func NewSignature(r io.Reader) (*Signature, error) {
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
s := &data.Signed{}
if err := json.Unmarshal(b, s); err != nil {
return nil, err
}
// extract role
sm := &signedMeta{}
if err := json.Unmarshal(s.Signed, sm); err != nil {
return nil, err
}
return &Signature{
signed: s,
Role: sm.Type,
Version: sm.Version,
}, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
if s.signed == nil {
return nil, errors.New("tuf manifest has not been initialized")
}
marshalledBytes, err := json.Marshal(s.signed)
if err != nil {
return nil, fmt.Errorf("marshalling signature: %w", err)
}
return jsoncanonicalizer.Transform(marshalledBytes)
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(_ io.Reader, k interface{}, _ ...sigsig.VerifyOption) error {
key, ok := k.(*PublicKey)
if !ok {
return fmt.Errorf("invalid public key type for: %v", k)
}
if key.db == nil {
return errors.New("tuf root has not been initialized")
}
return key.db.Verify(s.signed, s.Role, 0)
}
// PublicKey Public Key database with verification keys
type PublicKey struct {
// we keep the signed root to retrieve the canonical value
root *data.Signed
db *verify.DB
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
rawRoot, err := io.ReadAll(r)
if err != nil {
return nil, err
}
// Unmarshal this to verify that this is a valid root.json
s := &data.Signed{}
if err := json.Unmarshal(rawRoot, s); err != nil {
return nil, err
}
root := &data.Root{}
if err := json.Unmarshal(s.Signed, root); err != nil {
return nil, err
}
// Now create a verification db that trusts all the keys
db := verify.NewDB()
for id, k := range root.Keys {
if err := db.AddKey(id, k); err != nil {
return nil, err
}
}
for name, role := range root.Roles {
if err := db.AddRole(name, role); err != nil {
return nil, err
}
}
// Verify that this root.json was signed.
if err := db.Verify(s, "root", 0); err != nil {
return nil, err
}
return &PublicKey{root: s, db: db}, nil
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() (encoded []byte, err error) {
if k.root == nil {
return nil, errors.New("tuf root has not been initialized")
}
marshalledBytes, err := json.Marshal(k.root)
if err != nil {
return nil, fmt.Errorf("marshalling tuf root: %w", err)
}
return jsoncanonicalizer.Transform(marshalledBytes)
}
func (k PublicKey) SpecVersion() (string, error) {
// extract role
sm := &signedMeta{}
if err := json.Unmarshal(k.root.Signed, sm); err != nil {
return "", err
}
return sm.SpecVersion, nil
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
return nil
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
return nil
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
root := &data.Root{}
if err := json.Unmarshal(k.root.Signed, root); err != nil {
return nil, err
}
var ids []identity.Identity
for _, k := range root.Keys {
verifier, err := keys.GetVerifier(k)
if err != nil {
return nil, err
}
switch k.Type {
// RSA and ECDSA keys are PKIX-encoded without PEM header for the Verifier type
case data.KeyTypeRSASSA_PSS_SHA256:
fallthrough
// TODO: Update to constants once go-tuf is updated to 0.6.0 (need PR #508)
case "ecdsa-sha2-nistp256":
fallthrough
case "ecdsa":
// parse and marshal to check format is correct
pub, err := x509.ParsePKIXPublicKey([]byte(verifier.Public()))
if err != nil {
return nil, err
}
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pub)
if err != nil {
return nil, err
}
digest := sha256.Sum256(pkixKey)
ids = append(ids, identity.Identity{
Crypto: pub,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(digest[:]),
})
case data.KeyTypeEd25519:
// key is stored as a 32-byte string
pub := ed25519.PublicKey(verifier.Public())
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pub)
if err != nil {
return nil, err
}
digest := sha256.Sum256(pkixKey)
ids = append(ids, identity.Identity{
Crypto: pub,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(digest[:]),
})
default:
return nil, fmt.Errorf("unsupported key type: %v", k.Type)
}
}
return ids, nil
}
//
// 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 x509
import (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"testing"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
)
// Generated with:
// openssl ecparam -genkey -name prime256v1 > ec_private.pem
// openssl pkcs8 -topk8 -in ec_private.pem -nocrypt
const ECDSAPriv = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmrLtCpBdXgXLUr7o
nSUPfo3oXMjmvuwTOjpTulIBKlKhRANCAATH6KSpTFe6uXFmW1qNEFXaO7fWPfZt
pPZrHZ1cFykidZoURKoYXfkohJ+U/USYy8Sd8b4DMd5xDRZCnlDM0h37
-----END PRIVATE KEY-----`
// Extracted from above with:
// openssl ec -in ec_private.pem -pubout
const ECDSAPub = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32
baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w==
-----END PUBLIC KEY-----`
// Generated with:
// openssl req -newkey rsa:2048 -nodes -keyout test.key -x509 -out test.crt
const RSACert = `-----BEGIN CERTIFICATE-----
MIIDOjCCAiKgAwIBAgIUEP925shVBKERFCsymdSqESLZFyMwDQYJKoZIhvcNAQEL
BQAwHzEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYwHhcNMjEwNDIxMjAy
ODAzWhcNMjEwNTIxMjAyODAzWjAfMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHJla29y
LmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN8KiP08rFIik4GN
W8/sHSXxDopeDBLEQEihsyXXWesfYW/q59lFaCZrsTetlyNEzKDJ+JrpIHwoGOo4
EwefFfvy2nkgPFs9aeIDsYZNZnIGxeB8sUfsZUYGHx+Ikm18vhM//GYzNjjuvHyq
+CWRAOS12ZISa99iah/lIhcP8IEj1gPGldAH0QFx3XpCePAdQocSU6ziVkj054/x
NJXy1bKySrVw7gvE9LxZlVO9urSOnzg7BBOla0mob8NRDVB8yN+LG365q4IMDzuI
jAEL6sLtoJ9pcemo1rIfNOhSLYlzfg7oszJ8eCjASNCCcp6EKVjhW7LRoldC8oGZ
EOrKM78CAwEAAaNuMGwwHQYDVR0OBBYEFGjs8EHKT3x1itwwptJLuQQg/hQcMB8G
A1UdIwQYMBaAFGjs8EHKT3x1itwwptJLuQQg/hQcMA8GA1UdEwEB/wQFMAMBAf8w
GQYDVR0RBBIwEIEOdGVzdEByZWtvci5kZXYwDQYJKoZIhvcNAQELBQADggEBAAHE
bYuePN3XpM7pHoCz6g4uTHu0VrezqJyK1ohysgWJmSJzzazUeISXk0xWnHPk1Zxi
kzoEuysI8b0P7yodMA8e16zbIOL6QbGe3lNXYqRIg+bl+4OPFGVMX8xHNZmeh0kD
vX1JVS+y9uyo4/z/pm0JhaSCn85ft/Y5uXMQYn1wFR5DAcJH+iWjNX4fipGxGRE9
Cy0DjFnYJ3SRY4HPQ0oUSQmyhrwe2DiYzeqtbL2KJBXPcFQKWhkf/fupdYFljvcH
d9NNfRb0p2oFGG/J0ROg9pEcP1/aZP5k8P2pRdt3y7h1MAtmg2bgEdugZgXwAUmM
BmU8k2FeTuqV15piPCE=
-----END CERTIFICATE-----`
const RSAKey = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDfCoj9PKxSIpOB
jVvP7B0l8Q6KXgwSxEBIobMl11nrH2Fv6ufZRWgma7E3rZcjRMygyfia6SB8KBjq
OBMHnxX78tp5IDxbPWniA7GGTWZyBsXgfLFH7GVGBh8fiJJtfL4TP/xmMzY47rx8
qvglkQDktdmSEmvfYmof5SIXD/CBI9YDxpXQB9EBcd16QnjwHUKHElOs4lZI9OeP
8TSV8tWyskq1cO4LxPS8WZVTvbq0jp84OwQTpWtJqG/DUQ1QfMjfixt+uauCDA87
iIwBC+rC7aCfaXHpqNayHzToUi2Jc34O6LMyfHgowEjQgnKehClY4Vuy0aJXQvKB
mRDqyjO/AgMBAAECggEBAIHOAs3Gis8+WjRSjXVjh882DG1QsJwXZQYgPT+vpiAl
YjKdNpOHRkbd9ARgXY5kEuccxDd7p7E6MM3XFpQf7M51ltpZfWboRgAIgD+WOiHw
eSbdytr95C6tj11twTJBH+naGk1sTokxv7aaVdKfIjL49oeBexBFmVe4pW9gkmrE
1z1y1a0RohqbZ0kprYPWjz5UhsNqbCzgkdDqS7IrcOwVg6zvKYFjHnqIHqaJXVif
FgIfoNt7tz+12FTHI+6OkKoN3YCJueaxneBhITXm6RLOpQWa9qhdUPbkJ9vQNfph
Qqke4faaxKY9UDma+GpEHR016AWufZp92pd9wQkDn0kCgYEA7w/ZizAkefHoZhZ8
Isn/fYu4fdtUaVgrnGUVZobiGxWrHRU9ikbAwR7UwbgRSfppGiJdAMq1lyH2irmb
4OHU64rjuYSlIqUWHLQHWmqUbLUvlDojH/vdmH/Zn0AbrLZaimC5UCjK3Eb7sAMq
G0tGeDX2JraQvx7KrbC6peTaaaMCgYEA7tgZBiRCQJ7+mNu+gX9x6OXtjsDCh516
vToRLkxWc7LAbC9LKsuEHl4e3vy1PY/nyuv12Ng2dBq4WDXozAmVgz0ok7rRlIFp
w8Yj8o/9KuGZkD/7tw/pLsVc9Q3Wf0ACrnAAh7+3dAvn3yg+WHwXzqWIbrseDPt9
ILCfUoNDpzUCgYAKFCX8y0PObFd67lm/cbq2xUw66iNN6ay1BEH5t5gSwkAbksis
ar03pyAbJrJ75vXFZ0t6fBFZ1NG7GYYr3fmHEKz3JlN7+W/MN/7TXgjx6FWgLy9J
6ul1w3YeU6qXBn0ctmU5ru6WiNuVmRyOWAcZjFTbXvkNRbQPzJKh6dsXdwKBgA1D
FIihxMf/zBVCxl48bF/JPJqbm3GaTfFp4wBWHsrH1yVqrtrOeCSTh1VMZOfpMK60
0W7b+pIR1cCYJbgGpDWoVLN3QSHk2bGUM/TJB/60jilTVC/DA2ikbtfwj8N7E2sK
Lw1amN4ptxNOEcAqC8xepqe3XiDMahNBm2cigMQtAoGBAKwrXvss2BKz+/6poJQU
A0c7jhMN8M9Y5S2Ockw07lrQeAgfu4q+/8ztm0NeHJbk01IJvJY5Nt7bSgwgNVlo
j7vR2BMAc9U73Ju9aeTl/L6GqmZyA+Ojhl5gA5DPZYqNiqi93ydgRaI6n4+o3dI7
5wnr40AmbuKCDvMOvN7nMybL
-----END PRIVATE KEY-----`
// Extracted from the certificate using:
// openssl x509 -pubkey -noout -in test.crt
const PubKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3wqI/TysUiKTgY1bz+wd
JfEOil4MEsRASKGzJddZ6x9hb+rn2UVoJmuxN62XI0TMoMn4mukgfCgY6jgTB58V
+/LaeSA8Wz1p4gOxhk1mcgbF4HyxR+xlRgYfH4iSbXy+Ez/8ZjM2OO68fKr4JZEA
5LXZkhJr32JqH+UiFw/wgSPWA8aV0AfRAXHdekJ48B1ChxJTrOJWSPTnj/E0lfLV
srJKtXDuC8T0vFmVU726tI6fODsEE6VrSahvw1ENUHzI34sbfrmrggwPO4iMAQvq
wu2gn2lx6ajWsh806FItiXN+DuizMnx4KMBI0IJynoQpWOFbstGiV0LygZkQ6soz
vwIDAQAB
-----END PUBLIC KEY-----`
var (
CertPrivateKey *rsa.PrivateKey
Certificate *x509.Certificate
)
func init() {
p, _ := pem.Decode([]byte(RSAKey))
priv, err := x509.ParsePKCS8PrivateKey(p.Bytes)
if err != nil {
panic(err)
}
cpk, ok := priv.(*rsa.PrivateKey)
if !ok {
panic("unsuccessful conversion")
}
CertPrivateKey = cpk
p, _ = pem.Decode([]byte(RSACert))
Certificate, err = x509.ParseCertificate(p.Bytes)
if err != nil {
panic(err)
}
}
func SignX509Cert(b []byte) ([]byte, error) {
dgst := sha256.Sum256(b)
signature, err := CertPrivateKey.Sign(rand.Reader, dgst[:], crypto.SHA256)
return signature, err
}
// CreatedX509SignedArtifact gets the test dir setup correctly with some random artifacts and keys.
func CreatedX509SignedArtifact(t *testing.T, artifactPath, sigPath string) {
t.Helper()
artifact := util.CreateArtifact(t, artifactPath)
// Sign it with our key and write that to a file
signature, err := SignX509Cert([]byte(artifact))
if err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(sigPath, []byte(signature), 0644); err != nil {
t.Fatal(err)
}
}
type Verifier struct {
S signature.Signer
v signature.Verifier
}
func (v *Verifier) KeyID() (string, error) {
return "", nil
}
func (v *Verifier) Public() crypto.PublicKey {
return v.v.PublicKey
}
func (v *Verifier) Sign(_ context.Context, data []byte) (sig []byte, err error) {
if v.S == nil {
return nil, errors.New("nil signer")
}
sig, err = v.S.SignMessage(bytes.NewReader(data), options.WithCryptoSignerOpts(crypto.SHA256))
if err != nil {
return nil, err
}
return sig, nil
}
func (v *Verifier) Verify(_ context.Context, data, sig []byte) error {
if v.v == nil {
return errors.New("nil Verifier")
}
return v.v.VerifySignature(bytes.NewReader(sig), bytes.NewReader(data))
}
//
// 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 x509
import (
"bytes"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"io"
"strings"
"github.com/asaskevich/govalidator"
"github.com/sigstore/rekor/pkg/pki/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigsig "github.com/sigstore/sigstore/pkg/signature"
)
// EmailAddressOID defined by https://oidref.com/1.2.840.113549.1.9.1
var EmailAddressOID asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 9, 1}
type Signature struct {
signature []byte
verifierLoadOpts []sigsig.LoadOption
}
// NewSignature creates and validates an x509 signature object
func NewSignature(r io.Reader) (*Signature, error) {
return NewSignatureWithOpts(r)
}
func NewSignatureWithOpts(r io.Reader, opts ...sigsig.LoadOption) (*Signature, error) {
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
return &Signature{
signature: b,
verifierLoadOpts: opts,
}, nil
}
// CanonicalValue implements the pki.Signature interface
func (s Signature) CanonicalValue() ([]byte, error) {
return s.signature, nil
}
// Verify implements the pki.Signature interface
func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error {
if len(s.signature) == 0 {
//lint:ignore ST1005 X509 is proper use of term
return errors.New("X509 signature has not been initialized")
}
key, ok := k.(*PublicKey)
if !ok {
return fmt.Errorf("invalid public key type for: %v", k)
}
p := key.key
if p == nil {
switch {
case key.cert != nil:
p = key.cert.c.PublicKey
case len(key.certs) > 0:
if err := verifyCertChain(key.certs); err != nil {
return err
}
p = key.certs[0].PublicKey
default:
return errors.New("no public key found")
}
}
verifier, err := sigsig.LoadVerifierWithOpts(p, s.verifierLoadOpts...)
if err != nil {
return err
}
return verifier.VerifySignature(bytes.NewReader(s.signature), r, opts...)
}
// PublicKey Public Key that follows the x509 standard
type PublicKey struct {
key interface{}
cert *cert
certs []*x509.Certificate
}
type cert struct {
c *x509.Certificate
b []byte
}
// NewPublicKey implements the pki.PublicKey interface
func NewPublicKey(r io.Reader) (*PublicKey, error) {
rawPub, err := io.ReadAll(r)
if err != nil {
return nil, err
}
trimmedRawPub := bytes.TrimSpace(rawPub)
block, rest := pem.Decode(trimmedRawPub)
if block == nil {
return nil, errors.New("invalid public key: failure decoding PEM")
}
// Handle certificate chain, concatenated PEM-encoded certificates
if len(rest) > 0 {
// Support up to 10 certificates in a chain, to avoid parsing extremely long chains
certs, err := cryptoutils.UnmarshalCertificatesFromPEMLimited(trimmedRawPub, 10)
if err != nil {
return nil, err
}
return &PublicKey{certs: certs}, nil
}
switch block.Type {
case string(cryptoutils.PublicKeyPEMType):
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
return &PublicKey{key: key}, nil
case string(cryptoutils.CertificatePEMType):
c, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
return &PublicKey{
cert: &cert{
c: c,
b: block.Bytes,
}}, nil
}
return nil, fmt.Errorf("invalid public key: cannot handle type %v", block.Type)
}
// CanonicalValue implements the pki.PublicKey interface
func (k PublicKey) CanonicalValue() (encoded []byte, err error) {
switch {
case k.key != nil:
encoded, err = cryptoutils.MarshalPublicKeyToPEM(k.key)
case k.cert != nil:
encoded, err = cryptoutils.MarshalCertificateToPEM(k.cert.c)
case k.certs != nil:
encoded, err = cryptoutils.MarshalCertificatesToPEM(k.certs)
default:
err = errors.New("x509 public key has not been initialized")
}
return
}
func (k PublicKey) CryptoPubKey() crypto.PublicKey {
if k.cert != nil {
return k.cert.c.PublicKey
}
if len(k.certs) > 0 {
return k.certs[0].PublicKey
}
return k.key
}
// EmailAddresses implements the pki.PublicKey interface
func (k PublicKey) EmailAddresses() []string {
var names []string
var cert *x509.Certificate
if k.cert != nil {
cert = k.cert.c
} else if len(k.certs) > 0 {
cert = k.certs[0]
}
if cert != nil {
for _, name := range cert.EmailAddresses {
if govalidator.IsEmail(name) {
names = append(names, strings.ToLower(name))
}
}
}
return names
}
// Subjects implements the pki.PublicKey interface
func (k PublicKey) Subjects() []string {
var subjects []string
var cert *x509.Certificate
if k.cert != nil {
cert = k.cert.c
} else if len(k.certs) > 0 {
cert = k.certs[0]
}
if cert != nil {
subjects = cryptoutils.GetSubjectAlternateNames(cert)
}
return subjects
}
// Identities implements the pki.PublicKey interface
func (k PublicKey) Identities() ([]identity.Identity, error) {
// k contains either a key, a cert, or a list of certs
if k.key != nil {
pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key)
if err != nil {
return nil, err
}
digest := sha256.Sum256(pkixKey)
return []identity.Identity{{
Crypto: k.key,
Raw: pkixKey,
Fingerprint: hex.EncodeToString(digest[:]),
}}, nil
}
var cert *x509.Certificate
switch {
case k.cert != nil:
cert = k.cert.c
case len(k.certs) > 0:
cert = k.certs[0]
default:
return nil, errors.New("no key, certificate or certificate chain provided")
}
digest := sha256.Sum256(cert.Raw)
return []identity.Identity{{
Crypto: cert,
Raw: cert.Raw,
Fingerprint: hex.EncodeToString(digest[:]),
}}, nil
}
func verifyCertChain(certChain []*x509.Certificate) error {
if len(certChain) == 0 {
return errors.New("no certificate chain provided")
}
// No certificate chain to verify
if len(certChain) == 1 {
return nil
}
rootPool := x509.NewCertPool()
rootPool.AddCert(certChain[len(certChain)-1])
subPool := x509.NewCertPool()
for _, c := range certChain[1 : len(certChain)-1] {
subPool.AddCert(c)
}
if _, err := certChain[0].Verify(x509.VerifyOptions{
Roots: rootPool,
Intermediates: subPool,
// Allow any key usage
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
// Expired certificates can be uploaded and should be verifiable
CurrentTime: certChain[0].NotBefore,
}); err != nil {
return err
}
return nil
}
// 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 sharding
// VirtualLogIndex returns the virtual log index for a given leaf index
func VirtualLogIndex(leafIndex int64, tid int64, ranges *LogRanges) int64 {
// if we have no inactive ranges, we have just one log! return the leafIndex as is
// as long as it matches the active tree ID
if ranges.NoInactive() {
if ranges.GetActive().TreeID == tid {
return leafIndex
}
return -1
}
var virtualIndex int64
for _, r := range ranges.GetInactive() {
if r.TreeID == tid {
return virtualIndex + leafIndex
}
virtualIndex += r.TreeLength
}
// If no TreeID in Inactive matches the tid, the virtual index should be the active tree
if ranges.GetActive().TreeID == tid {
return virtualIndex + leafIndex
}
// Otherwise, the tid is invalid
return -1
}
//
// 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 sharding
import (
"context"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"strconv"
"strings"
"github.com/google/trillian/types"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/signer"
"github.com/sigstore/rekor/pkg/trillianclient"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
"sigs.k8s.io/yaml"
)
// Active and inactive shards
type LogRanges struct {
// inactive shards are listed from oldest to newest
inactive Ranges
active LogRange
}
type Ranges []LogRange
// LogRange represents a log or tree shard
type LogRange struct {
TreeID int64 `json:"treeID" yaml:"treeID"`
TreeLength int64 `json:"treeLength" yaml:"treeLength"` // unused for active tree
SigningConfig signer.SigningConfig `json:"signingConfig" yaml:"signingConfig"` // if unset, assume same as active tree
GRPCConfig *trillianclient.GRPCConfig `json:"grpcEndpoint" yaml:"grpcEndpoint"` // if unset, assume same as active tree
Signer signature.Signer
PemPubKey string // PEM-encoded PKIX public key
LogID string // Hex-encoded SHA256 digest of PKIX-encoded public key
}
func (l LogRange) String() string {
return fmt.Sprintf("{ TreeID: %v, TreeLength: %v, SigningScheme: %v, PemPubKey: %v, LogID: %v }", l.TreeID, l.TreeLength, l.SigningConfig.SigningSchemeOrKeyPath, l.PemPubKey, l.LogID)
}
// NewLogRanges initializes the active and any inactive log shards from a config file.
func NewLogRanges(ctx context.Context, inactiveShardsPath string, activeTreeID int64, signingConfig signer.SigningConfig) (*LogRanges, error) {
if activeTreeID == 0 {
return nil, errors.New("non-zero active tree ID required; please set the active tree ID via the `--trillian_log_server.tlog_id` flag")
}
// Initialize active shard
activeLog, err := initializeRange(ctx, LogRange{TreeID: activeTreeID, SigningConfig: signingConfig})
if err != nil {
return nil, fmt.Errorf("creating range for active tree %d: %w", activeTreeID, err)
}
if inactiveShardsPath == "" {
log.Logger.Info("No config file specified, no inactive shards")
return &LogRanges{active: activeLog}, nil
}
// Initialize inactive shards from inactive tree IDs
ranges, err := logRangesFromPath(inactiveShardsPath)
if err != nil {
return nil, fmt.Errorf("log ranges from path: %w", err)
}
for i, r := range ranges {
// If no signing config is provided, use the active tree signing key
if r.SigningConfig.IsUnset() {
r.SigningConfig = signingConfig
}
r, err := initializeRange(ctx, r)
if err != nil {
return nil, fmt.Errorf("updating range for tree id %d: %w", r.TreeID, err)
}
ranges[i] = r
}
return &LogRanges{
inactive: ranges,
active: activeLog,
}, nil
}
// CompleteInitialization populates the tree length for all inactive shards.
func (l *LogRanges) CompleteInitialization(ctx context.Context, tcm *trillianclient.ClientManager) (map[int64]types.LogRootV1, error) {
sthMap := make(map[int64]types.LogRootV1)
for i, r := range l.inactive {
logClient, err := tcm.GetTrillianClient(r.TreeID)
if err != nil {
return nil, fmt.Errorf("getting log client for tree %d: %w", r.TreeID, err)
}
resp := logClient.GetLatest(ctx, 0)
if resp.Err != nil {
return nil, fmt.Errorf("getting signed log root for tree %d: %w", r.TreeID, err)
}
var root types.LogRootV1
if err := root.UnmarshalBinary(resp.GetLatestResult.SignedLogRoot.LogRoot); err != nil {
return nil, err
}
l.inactive[i].TreeLength = int64(root.TreeSize)
sthMap[r.TreeID] = root
}
return sthMap, nil
}
// logRangesFromPath unmarshals a shard config
func logRangesFromPath(path string) (Ranges, error) {
var ranges Ranges
contents, err := os.ReadFile(path)
if err != nil {
return Ranges{}, err
}
if string(contents) == "" {
log.Logger.Info("Sharding config file contents empty, skipping init of logRange map")
return Ranges{}, nil
}
if err := yaml.Unmarshal(contents, &ranges); err != nil {
// Try to use JSON
if jerr := json.Unmarshal(contents, &ranges); jerr == nil {
return ranges, nil
}
return Ranges{}, err
}
return ranges, nil
}
// initializeRange fills in any missing information about the range that can be derived without a client.
func initializeRange(ctx context.Context, r LogRange) (LogRange, error) {
if r.SigningConfig.IsUnset() {
return LogRange{}, fmt.Errorf("signing config not set, unable to initialize shard signer")
}
// Initialize shard signer
s, err := signer.New(ctx, r.SigningConfig.SigningSchemeOrKeyPath, r.SigningConfig.FileSignerPassword,
r.SigningConfig.TinkKEKURI, r.SigningConfig.TinkKeysetPath, r.SigningConfig.GCPKMSRetries, r.SigningConfig.GCPKMSTimeout)
if err != nil {
return LogRange{}, err
}
r.Signer = s
// Initialize public key
pubKey, err := s.PublicKey(options.WithContext(ctx))
if err != nil {
return LogRange{}, err
}
pemPubKey, err := cryptoutils.MarshalPublicKeyToPEM(pubKey)
if err != nil {
return LogRange{}, err
}
r.PemPubKey = string(pemPubKey)
// Initialize log ID from public key
b, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return LogRange{}, err
}
pubkeyHashBytes := sha256.Sum256(b)
r.LogID = hex.EncodeToString(pubkeyHashBytes[:])
return r, nil
}
func (l *LogRanges) ResolveVirtualIndex(index int) (int64, int64) {
indexLeft := index
for _, l := range l.inactive {
if indexLeft < int(l.TreeLength) {
return l.TreeID, int64(indexLeft)
}
indexLeft -= int(l.TreeLength)
}
// If index not found in inactive trees, return the active tree
return l.active.TreeID, int64(indexLeft)
}
func (l *LogRanges) NoInactive() bool {
return l.inactive == nil
}
// AllShards returns all shards, starting with the active shard and then the inactive shards
func (l *LogRanges) AllShards() []int64 {
shards := []int64{l.GetActive().TreeID}
for _, in := range l.GetInactive() {
shards = append(shards, in.TreeID)
}
return shards
}
// TotalInactiveLength returns the total length across all inactive shards;
// we don't know the length of the active shard.
func (l *LogRanges) TotalInactiveLength() int64 {
var total int64
for _, r := range l.inactive {
total += r.TreeLength
}
return total
}
// GetLogRangeByTreeID returns the active or inactive
// shard with the given tree ID
func (l *LogRanges) GetLogRangeByTreeID(treeID int64) (LogRange, error) {
if l.active.TreeID == treeID {
return l.active, nil
}
for _, i := range l.inactive {
if i.TreeID == treeID {
return i, nil
}
}
return LogRange{}, fmt.Errorf("no log range found for tree ID %d", treeID)
}
// GetInactive returns all inactive shards
func (l *LogRanges) GetInactive() []LogRange {
return l.inactive
}
// GetActive returns the cative shard
func (l *LogRanges) GetActive() LogRange {
return l.active
}
func (l *LogRanges) String() string {
ranges := []string{}
for _, r := range l.inactive {
ranges = append(ranges, fmt.Sprintf("%d=%d", r.TreeID, r.TreeLength))
}
ranges = append(ranges, fmt.Sprintf("active=%d", l.active.TreeID))
return strings.Join(ranges, ",")
}
// PublicKey returns the associated public key for the given Tree ID
// and returns the active public key by default
func (l *LogRanges) PublicKey(treeID string) (string, error) {
// if no tree ID is specified, assume the active tree
if treeID == "" {
return l.active.PemPubKey, nil
}
tid, err := strconv.ParseInt(treeID, 10, 64)
if err != nil {
return "", err
}
if tid == l.GetActive().TreeID {
return l.active.PemPubKey, nil
}
for _, i := range l.inactive {
if i.TreeID == tid {
return i.PemPubKey, nil
}
}
return "", fmt.Errorf("%d is not a valid tree ID and doesn't have an associated public key", tid)
}
//
// 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 sharding
import (
"encoding/hex"
"errors"
"fmt"
"strconv"
)
// An EntryID refers to a specific artifact's ID and is made of two components,
// the TreeID and the UUID. The TreeID is a hex-encoded uint64 (8 bytes)
// referring to the specific trillian tree (also known as log or shard) where
// the artifact can be found. The UUID is a hex-encoded 32-byte number
// referring to the artifact's merkle leaf hash from trillian. Artifact lookup
// by UUID occurs by finding the UUID within the tree specified by the TreeID.
//
// An EntryID is 40 bytes long and looks like this:
// FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
// |_______ ________| |_____________________________________ ______________________________________|
// \/ \/
// TreeID (8 bytes, hex) UUID (32 bytes, hex)
const TreeIDHexStringLen = 16
const UUIDHexStringLen = 64
const EntryIDHexStringLen = TreeIDHexStringLen + UUIDHexStringLen
type EntryID struct {
TreeID string
UUID string
}
// CreateEntryIDFromParts This function can take a TreeID of equal or lesser length than TreeIDHexStringLen. In
// case the TreeID length is less than TreeIDHexStringLen, it will be padded to the correct
// length.
func CreateEntryIDFromParts(treeid string, uuid string) (EntryID, error) {
if len(treeid) > TreeIDHexStringLen {
err := fmt.Errorf("invalid treeid len: %v", len(treeid))
return createEmptyEntryID(), err
}
if len(uuid) != UUIDHexStringLen {
err := fmt.Errorf("invalid uuid len: %v", len(uuid))
return createEmptyEntryID(), err
}
treeidFormatted, err := PadToTreeIDLen(treeid)
if err != nil {
return createEmptyEntryID(), err
}
if err := ValidateEntryID(treeidFormatted + uuid); err != nil {
return createEmptyEntryID(), err
}
return EntryID{
TreeID: treeidFormatted,
UUID: uuid}, nil
}
func createEmptyEntryID() EntryID {
return EntryID{
TreeID: "",
UUID: ""}
}
func (e EntryID) ReturnEntryIDString() string {
return e.TreeID + e.UUID
}
func PadToTreeIDLen(t string) (string, error) {
switch {
case len(t) == TreeIDHexStringLen:
return t, nil
case len(t) > TreeIDHexStringLen:
return "", fmt.Errorf("invalid treeID %v: too long", t)
default:
return fmt.Sprintf("%016s", t), nil
}
}
// GetUUIDFromIDString Returns UUID (with no prepended TreeID) from a UUID or EntryID string.
// Validates UUID and also TreeID if present.
func GetUUIDFromIDString(id string) (string, error) {
switch len(id) {
case UUIDHexStringLen:
if err := ValidateUUID(id); err != nil {
return "", err
}
return id, nil
case EntryIDHexStringLen:
if err := ValidateEntryID(id); err != nil {
if err.Error() == "0 is not a valid TreeID" {
return id[len(id)-UUIDHexStringLen:], nil
}
return "", err
}
return id[len(id)-UUIDHexStringLen:], nil
default:
return "", fmt.Errorf("invalid ID len %v for %v", len(id), id)
}
}
// ValidateUUID This is permissive in that if passed an EntryID, it will find the UUID and validate it.
func ValidateUUID(u string) error {
switch len(u) {
// If u is an EntryID, call validate on just the UUID
case EntryIDHexStringLen:
uid := u[len(u)-UUIDHexStringLen:]
if err := ValidateUUID(uid); err != nil {
return err
}
return nil
case UUIDHexStringLen:
if _, err := hex.DecodeString(u); err != nil {
return fmt.Errorf("id %v is not a valid hex string: %w", u, err)
}
return nil
default:
return fmt.Errorf("invalid ID len %v for %v", len(u), u)
}
}
// ValidateTreeID This is permissive in that if passed an EntryID, it will find the TreeID and validate it.
func ValidateTreeID(t string) error {
switch len(t) {
// If t is an EntryID, call validate on just the TreeID
case EntryIDHexStringLen:
tid := t[:TreeIDHexStringLen]
err := ValidateTreeID(tid)
if err != nil {
return err
}
return nil
case TreeIDHexStringLen:
// Check that it's a valid int64 in hex (base 16)
i, err := strconv.ParseInt(t, 16, 64)
if err != nil {
return fmt.Errorf("could not convert treeID %v to int64: %w", t, err)
}
// Check for invalid TreeID values
// TODO: test for more of these
if i == 0 {
return errors.New("0 is not a valid TreeID")
}
return nil
default:
return fmt.Errorf("TreeID len expected to be %v but got %v", TreeIDHexStringLen, len(t))
}
}
func ValidateEntryID(id string) error {
UUIDErr := ValidateUUID(id)
if UUIDErr != nil {
return UUIDErr
}
treeIDErr := ValidateTreeID(id)
if treeIDErr != nil {
return treeIDErr
}
return nil
}
var ErrPlainUUID = errors.New("cannot get treeID from plain UUID")
// GetTreeIDFromIDString Returns TreeID (with no appended UUID) from a TreeID or EntryID string.
// Validates TreeID and also UUID if present.
func GetTreeIDFromIDString(id string) (string, error) {
switch len(id) {
case UUIDHexStringLen:
return "", ErrPlainUUID
case EntryIDHexStringLen, TreeIDHexStringLen:
if err := ValidateEntryID(id); err != nil {
return "", err
}
return id[:TreeIDHexStringLen], nil
default:
return "", fmt.Errorf("invalid ID len %v for %v", len(id), id)
}
}
func TreeID(entryID string) (int64, error) {
tid, err := GetTreeIDFromIDString(entryID)
if err != nil {
return 0, err
}
i, err := strconv.ParseInt(tid, 16, 64)
if err != nil {
return 0, fmt.Errorf("could not convert treeID %v to int64: %w", tid, err)
}
return i, nil
}
/*
Copyright The Rekor 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 signer
import (
"crypto"
"fmt"
"github.com/sigstore/sigstore/pkg/signature"
"go.step.sm/crypto/pemutil"
)
// returns an file based signer and verify, used for spinning up local instances
type File struct {
signature.SignerVerifier
}
func NewFile(keyPath, keyPass string) (*File, error) {
opaqueKey, err := pemutil.Read(keyPath, pemutil.WithPassword([]byte(keyPass)))
if err != nil {
return nil, fmt.Errorf("file: provide a valid signer, %s is not valid: %w", keyPath, err)
}
signer, err := signature.LoadSignerVerifier(opaqueKey, crypto.SHA256)
if err != nil {
return nil, fmt.Errorf(`file: loaded private key from %s can't be used to sign: %w`, keyPath, err)
}
return &File{signer}, nil
}
/*
Copyright The Rekor 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 signer
import (
"crypto"
"crypto/elliptic"
"crypto/rand"
"github.com/sigstore/sigstore/pkg/signature"
)
const MemoryScheme = "memory"
// returns an in-memory signer and verify, used for spinning up local instances
type Memory struct {
signature.ECDSASignerVerifier
}
func NewMemory() (*Memory, error) {
// generate a keypair
sv, _, err := signature.NewECDSASignerVerifier(elliptic.P256(), rand.Reader, crypto.SHA256)
if err != nil {
return nil, err
}
return &Memory{
ECDSASignerVerifier: *sv,
}, nil
}
/*
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 signer
import (
"context"
"crypto"
"strings"
"time"
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/kms"
"golang.org/x/exp/slices"
"google.golang.org/api/option"
"google.golang.org/grpc"
"github.com/sigstore/sigstore/pkg/signature/kms/gcp"
// these are imported to load the providers via init() calls
_ "github.com/sigstore/sigstore/pkg/signature/kms/aws"
_ "github.com/sigstore/sigstore/pkg/signature/kms/azure"
_ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault"
)
// SigningConfig initializes the signer for a specific shard
type SigningConfig struct {
SigningSchemeOrKeyPath string `json:"signingSchemeOrKeyPath" yaml:"signingSchemeOrKeyPath"`
FileSignerPassword string `json:"fileSignerPassword" yaml:"fileSignerPassword"`
TinkKEKURI string `json:"tinkKEKURI" yaml:"tinkKEKURI"`
TinkKeysetPath string `json:"tinkKeysetPath" yaml:"tinkKeysetPath"`
GCPKMSRetries uint `json:"gcpkmsRetries" yaml:"gcpkmsRetries"`
GCPKMSTimeout uint `json:"gcpkmsTimeout" yaml:"gcpkmsTimeout"`
}
func (sc SigningConfig) IsUnset() bool {
return sc.SigningSchemeOrKeyPath == "" && sc.FileSignerPassword == "" &&
sc.TinkKEKURI == "" && sc.TinkKeysetPath == ""
}
func New(ctx context.Context, signer, pass, tinkKEKURI, tinkKeysetPath string, gcpkmsretries, gcpkmstimeout uint) (signature.Signer, error) {
switch {
case slices.ContainsFunc(kms.SupportedProviders(),
func(s string) bool {
return strings.HasPrefix(signer, s)
}):
opts := make([]signature.RPCOption, 0)
if strings.HasPrefix(signer, gcp.ReferenceScheme) {
callOpts := []grpc_retry.CallOption{grpc_retry.WithMax(gcpkmsretries), grpc_retry.WithPerRetryTimeout(time.Duration(gcpkmstimeout) * time.Second)}
opts = append(opts, gcp.WithGoogleAPIClientOption(option.WithGRPCDialOption(grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(callOpts...)))))
}
return kms.Get(ctx, signer, crypto.SHA256, opts...)
case signer == MemoryScheme:
return NewMemory()
case signer == TinkScheme:
return NewTinkSigner(ctx, tinkKEKURI, tinkKeysetPath)
default:
return NewFile(signer, pass)
}
}
// Copyright 2024 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 signer
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
tinkUtils "github.com/sigstore/sigstore/pkg/signature/tink"
"github.com/tink-crypto/tink-go-awskms/v2/integration/awskms"
"github.com/tink-crypto/tink-go-gcpkms/v2/integration/gcpkms"
"github.com/tink-crypto/tink-go/v2/core/registry"
"github.com/tink-crypto/tink-go/v2/keyset"
"github.com/tink-crypto/tink-go/v2/tink"
"github.com/sigstore/sigstore/pkg/signature"
)
const TinkScheme = "tink"
// NewTinkSigner returns a signature.SignerVerifier that wraps crypto.Signer and a hash function.
// Provide a path to the encrypted keyset and cloud KMS key URI for decryption
func NewTinkSigner(ctx context.Context, kekURI, keysetPath string) (signature.Signer, error) {
if kekURI == "" || keysetPath == "" {
return nil, fmt.Errorf("key encryption key URI or keyset path unset")
}
kek, err := getKeyEncryptionKey(ctx, kekURI)
if err != nil {
return nil, err
}
return NewTinkSignerWithHandle(kek, keysetPath)
}
// NewTinkSignerWithHandle returns a signature.SignerVerifier that wraps crypto.Signer and a hash function.
// Provide a path to the encrypted keyset and a key handle for decrypting the keyset
func NewTinkSignerWithHandle(kek tink.AEAD, keysetPath string) (signature.Signer, error) {
f, err := os.Open(filepath.Clean(keysetPath))
if err != nil {
return nil, err
}
defer f.Close()
kh, err := keyset.Read(keyset.NewJSONReader(f), kek)
if err != nil {
return nil, err
}
signer, err := tinkUtils.KeyHandleToSigner(kh)
if err != nil {
return nil, err
}
return signature.LoadDefaultSignerVerifier(signer)
}
// getKeyEncryptionKey returns a Tink AEAD encryption key from KMS
// Supports GCP and AWS
func getKeyEncryptionKey(ctx context.Context, kmsKey string) (tink.AEAD, error) {
switch {
case strings.HasPrefix(kmsKey, "gcp-kms://"):
gcpClient, err := gcpkms.NewClientWithOptions(ctx, kmsKey)
if err != nil {
return nil, err
}
registry.RegisterKMSClient(gcpClient)
return gcpClient.GetAEAD(kmsKey)
case strings.HasPrefix(kmsKey, "aws-kms://"):
awsClient, err := awskms.NewClientWithOptions(kmsKey)
if err != nil {
return nil, err
}
registry.RegisterKMSClient(awsClient)
return awsClient.GetAEAD(kmsKey)
default:
return nil, errors.New("unsupported KMS key type")
}
}
//
// Copyright 2025 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 trillianclient
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/google/trillian"
"github.com/google/trillian/client"
"github.com/sigstore/rekor/pkg/log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/types/known/durationpb"
)
// ClientManager creates and caches Trillian clients and their underlying gRPC connections.
type ClientManager struct {
// Mutex for connections map
connMu sync.RWMutex
// connections maps a specific gRPC configuration to a shared connection pool.
connections map[GRPCConfig]*grpc.ClientConn
// Mutex for trillianClients map
clientMu sync.RWMutex
// trillianClients caches the TrillianClient wrappers.
trillianClients map[int64]*TrillianClient
// flag to indicate whether the client manager is shutting down
shutdown bool
// treeIDToConfig maps a specific tree ID to its gRPC configuration.
treeIDToConfig map[int64]GRPCConfig
// defaultConfig is the global fallback configuration.
defaultConfig GRPCConfig
}
// NewClientManager creates a new ClientManager.
func NewClientManager(treeIDToConfig map[int64]GRPCConfig, defaultConfig GRPCConfig) *ClientManager {
return &ClientManager{
connections: make(map[GRPCConfig]*grpc.ClientConn),
treeIDToConfig: treeIDToConfig,
defaultConfig: defaultConfig,
trillianClients: make(map[int64]*TrillianClient),
}
}
// getConn finds the correct gRPC config for a tree ID, then dials or retrieves a cached connection.
func (cm *ClientManager) getConn(treeID int64) (*grpc.ClientConn, error) {
// Determine the correct GRPCConfig for this treeID.
config, ok := cm.treeIDToConfig[treeID]
if !ok {
// If no specific config exists, fall back to the global default.
config = cm.defaultConfig
}
cm.connMu.RLock()
conn, ok := cm.connections[config]
cm.connMu.RUnlock()
if ok {
return conn, nil
}
cm.connMu.Lock()
defer cm.connMu.Unlock()
// Double-check after acquiring the write lock.
conn, ok = cm.connections[config]
if ok {
return conn, nil
}
// Dial and cache the new connection.
newConn, err := dial(config.Address, config.Port, config.TLSCACert, config.UseSystemTrustStore, config.GRPCServiceConfig)
if err != nil {
return nil, err
}
cm.connections[config] = newConn
return newConn, nil
}
// GetTrillianClient returns a Rekor Trillian client wrapper for the given tree ID.
func (cm *ClientManager) GetTrillianClient(treeID int64) (*TrillianClient, error) {
cm.clientMu.RLock()
if cm.shutdown {
cm.clientMu.RUnlock()
return nil, errors.New("client manager is shutting down")
}
client, ok := cm.trillianClients[treeID]
cm.clientMu.RUnlock()
if ok {
return client, nil
}
conn, err := cm.getConn(treeID)
if err != nil {
return nil, err
}
cm.clientMu.Lock()
defer cm.clientMu.Unlock()
// Double-check after acquiring the write lock.
if cm.shutdown {
return nil, errors.New("client manager is shutting down")
}
if client, ok = cm.trillianClients[treeID]; ok {
return client, nil
}
newClient := newTrillianClient(trillian.NewTrillianLogClient(conn), treeID)
cm.trillianClients[treeID] = newClient
return newClient, nil
}
func CreateAndInitTree(ctx context.Context, config GRPCConfig) (*trillian.Tree, error) {
newConn, err := dial(config.Address, config.Port, config.TLSCACert, config.UseSystemTrustStore, config.GRPCServiceConfig)
if err != nil {
return nil, err
}
adminClient := trillian.NewTrillianAdminClient(newConn)
t, err := adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{
Tree: &trillian.Tree{
TreeType: trillian.TreeType_LOG,
TreeState: trillian.TreeState_ACTIVE,
MaxRootDuration: durationpb.New(time.Hour),
},
})
if err != nil {
return nil, fmt.Errorf("create tree: %w", err)
}
logClient := trillian.NewTrillianLogClient(newConn)
if err := client.InitLog(ctx, t, logClient); err != nil {
return nil, fmt.Errorf("init log: %w", err)
}
log.Logger.Infof("Created new tree with ID: %v", t.TreeId)
return t, nil
}
func dial(hostname string, port uint16, tlsCACertFile string, useSystemTrustStore bool, serviceConfig string) (*grpc.ClientConn, error) {
// Set up and test connection to rpc server
var creds credentials.TransportCredentials
switch {
case useSystemTrustStore:
creds = credentials.NewTLS(&tls.Config{
ServerName: hostname,
MinVersion: tls.VersionTLS12,
})
case tlsCACertFile != "":
tlsCaCert, err := os.ReadFile(filepath.Clean(tlsCACertFile))
if err != nil {
return nil, fmt.Errorf("failed to load tls_ca_cert: %w", err)
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(tlsCaCert) {
return nil, fmt.Errorf("failed to append CA certificate to pool")
}
creds = credentials.NewTLS(&tls.Config{
ServerName: hostname,
RootCAs: certPool,
MinVersion: tls.VersionTLS12,
})
default:
creds = insecure.NewCredentials()
}
opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
if serviceConfig != "" {
opts = append(opts, grpc.WithDefaultServiceConfig(serviceConfig))
}
conn, err := grpc.NewClient(fmt.Sprintf("%s:%d", hostname, port), opts...)
if err != nil {
return nil, fmt.Errorf("failed to connect to RPC server: %w", err)
}
return conn, nil
}
// Close stops clients and closes underlying gRPC connections.
func (cm *ClientManager) Close() error {
var err error
// set shutdown flag to true and clear cache of clients
cm.clientMu.Lock()
cm.shutdown = true
cm.trillianClients = make(map[int64]*TrillianClient)
cm.clientMu.Unlock()
cm.connMu.Lock()
for cfg, conn := range cm.connections {
if closeErr := conn.Close(); closeErr != nil {
err = errors.Join(err, fmt.Errorf("close conn %v:%d: %w", cfg.Address, cfg.Port, closeErr))
}
delete(cm.connections, cfg)
}
cm.connMu.Unlock()
return err
}
//
// 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 trillianclient
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"time"
"github.com/transparency-dev/merkle/proof"
"github.com/transparency-dev/merkle/rfc6962"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/google/trillian"
"github.com/google/trillian/client"
"github.com/google/trillian/types"
)
// TrillianClient provides a wrapper around the Trillian client
type TrillianClient struct {
client trillian.TrillianLogClient
logID int64
}
// newTrillianClient creates a TrillianClient with the given Trillian client and log/tree ID.
func newTrillianClient(logClient trillian.TrillianLogClient, logID int64) *TrillianClient {
return &TrillianClient{
client: logClient,
logID: logID,
}
}
// Response includes a status code, an optional error message, and one of the results based on the API call
type Response struct {
// Status is the status code of the response
Status codes.Code
// Error contains an error on request or client failure
Err error
// GetAddResult contains the response from queueing a leaf in Trillian
GetAddResult *trillian.QueueLeafResponse
// GetLeafAndProofResult contains the response for fetching an inclusion proof and leaf
GetLeafAndProofResult *trillian.GetEntryAndProofResponse
// GetLatestResult contains the response for the latest checkpoint
GetLatestResult *trillian.GetLatestSignedLogRootResponse
// GetConsistencyProofResult contains the response for a consistency proof between two log sizes
GetConsistencyProofResult *trillian.GetConsistencyProofResponse
// GetLeavesByRangeResult contains the response for fetching a leaf without an inclusion proof
GetLeavesByRangeResult *trillian.GetLeavesByRangeResponse
// getProofResult contains the response for an inclusion proof fetched by leaf hash
getProofResult *trillian.GetInclusionProofByHashResponse
}
func unmarshalLogRoot(logRoot []byte) (types.LogRootV1, error) {
var root types.LogRootV1
if err := root.UnmarshalBinary(logRoot); err != nil {
return types.LogRootV1{}, err
}
return root, nil
}
func (t *TrillianClient) root(ctx context.Context) (types.LogRootV1, error) {
rqst := &trillian.GetLatestSignedLogRootRequest{
LogId: t.logID,
}
resp, err := t.client.GetLatestSignedLogRoot(ctx, rqst)
if err != nil {
return types.LogRootV1{}, err
}
return unmarshalLogRoot(resp.SignedLogRoot.LogRoot)
}
func (t *TrillianClient) AddLeaf(ctx context.Context, byteValue []byte) *Response {
leaf := &trillian.LogLeaf{
LeafValue: byteValue,
}
rqst := &trillian.QueueLeafRequest{
LogId: t.logID,
Leaf: leaf,
}
resp, err := t.client.QueueLeaf(ctx, rqst)
// check for error
if err != nil || (resp.QueuedLeaf.Status != nil && resp.QueuedLeaf.Status.Code != int32(codes.OK)) {
return &Response{
Status: status.Code(err),
Err: err,
GetAddResult: resp,
}
}
root, err := t.root(ctx)
if err != nil {
return &Response{
Status: status.Code(err),
Err: err,
GetAddResult: resp,
}
}
v := client.NewLogVerifier(rfc6962.DefaultHasher)
logClient := client.New(t.logID, t.client, v, root)
waitForInclusion := func(ctx context.Context, _ []byte) *Response {
if logClient.MinMergeDelay > 0 {
select {
case <-ctx.Done():
return &Response{
Status: codes.DeadlineExceeded,
Err: ctx.Err(),
}
case <-time.After(logClient.MinMergeDelay):
}
}
for {
root = *logClient.GetRoot()
if root.TreeSize >= 1 {
proofResp := t.getProofByHash(ctx, resp.QueuedLeaf.Leaf.MerkleLeafHash)
// if this call succeeds or returns an error other than "not found", return
if proofResp.Err == nil || (proofResp.Err != nil && status.Code(proofResp.Err) != codes.NotFound) {
return proofResp
}
// otherwise wait for a root update before trying again
}
if _, err := logClient.WaitForRootUpdate(ctx); err != nil {
return &Response{
Status: codes.Unknown,
Err: err,
}
}
}
}
proofResp := waitForInclusion(ctx, resp.QueuedLeaf.Leaf.MerkleLeafHash)
if proofResp.Err != nil {
return &Response{
Status: status.Code(proofResp.Err),
Err: proofResp.Err,
GetAddResult: resp,
}
}
proofs := proofResp.getProofResult.Proof
if len(proofs) != 1 {
err := fmt.Errorf("expected 1 proof from getProofByHash for %v, found %v", hex.EncodeToString(resp.QueuedLeaf.Leaf.MerkleLeafHash), len(proofs))
return &Response{
Status: status.Code(err),
Err: err,
GetAddResult: resp,
}
}
leafIndex := proofs[0].LeafIndex
// fetch the leaf without re-requesting a proof (since we already have it)
leafOnlyResp := t.getStandaloneLeaf(ctx, leafIndex, resp.QueuedLeaf.Leaf.MerkleLeafHash, proofs[0], proofResp.getProofResult.SignedLogRoot)
if leafOnlyResp.Err != nil {
return &Response{
Status: status.Code(leafOnlyResp.Err),
Err: leafOnlyResp.Err,
GetAddResult: resp,
}
}
// Copy this value explicitly because it contains the integrated timestamp
resp.QueuedLeaf.Leaf = leafOnlyResp.GetLeafAndProofResult.Leaf
return &Response{
Status: codes.OK,
GetAddResult: resp,
GetLeafAndProofResult: leafOnlyResp.GetLeafAndProofResult,
}
}
func (t *TrillianClient) GetLeafAndProofByHash(ctx context.Context, hash []byte) *Response {
// get inclusion proof for hash, extract index, then fetch leaf using index
proofResp := t.getProofByHash(ctx, hash)
if proofResp.Err != nil {
return &Response{
Status: status.Code(proofResp.Err),
Err: proofResp.Err,
}
}
proofs := proofResp.getProofResult.Proof
if len(proofs) != 1 {
err := fmt.Errorf("expected 1 proof from getProofByHash for %v, found %v", hex.EncodeToString(hash), len(proofs))
return &Response{
Status: status.Code(err),
Err: err,
}
}
leafIndex := proofs[0].LeafIndex
// fetch the leaf without re-requesting a proof (since we already have it)
leafOnlyResp := t.getStandaloneLeaf(ctx, leafIndex, hash, proofs[0], proofResp.getProofResult.SignedLogRoot)
if leafOnlyResp.Err != nil {
return &Response{
Status: status.Code(leafOnlyResp.Err),
Err: leafOnlyResp.Err,
}
}
return leafOnlyResp
}
func (t *TrillianClient) GetLeafAndProofByIndex(ctx context.Context, index int64) *Response {
rootResp := t.GetLatest(ctx, 0)
if rootResp.Err != nil {
return &Response{
Status: status.Code(rootResp.Err),
Err: rootResp.Err,
}
}
root, err := unmarshalLogRoot(rootResp.GetLatestResult.SignedLogRoot.LogRoot)
if err != nil {
return &Response{
Status: status.Code(rootResp.Err),
Err: rootResp.Err,
}
}
resp, err := t.client.GetEntryAndProof(ctx,
&trillian.GetEntryAndProofRequest{
LogId: t.logID,
LeafIndex: index,
TreeSize: int64(root.TreeSize),
})
if resp != nil && resp.Proof != nil {
if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(index), root.TreeSize, resp.GetLeaf().MerkleLeafHash, resp.Proof.Hashes, root.RootHash); err != nil {
return &Response{
Status: status.Code(err),
Err: err,
}
}
return &Response{
Status: status.Code(err),
Err: err,
GetLeafAndProofResult: &trillian.GetEntryAndProofResponse{
Proof: resp.Proof,
Leaf: resp.Leaf,
SignedLogRoot: rootResp.GetLatestResult.SignedLogRoot,
},
}
}
return &Response{
Status: status.Code(err),
Err: err,
}
}
func (t *TrillianClient) GetLatest(ctx context.Context, leafSizeInt int64) *Response {
resp, err := t.client.GetLatestSignedLogRoot(ctx,
&trillian.GetLatestSignedLogRootRequest{
LogId: t.logID,
FirstTreeSize: leafSizeInt,
})
return &Response{
Status: status.Code(err),
Err: err,
GetLatestResult: resp,
}
}
func (t *TrillianClient) GetConsistencyProof(ctx context.Context, firstSize, lastSize int64) *Response {
resp, err := t.client.GetConsistencyProof(ctx,
&trillian.GetConsistencyProofRequest{
LogId: t.logID,
FirstTreeSize: firstSize,
SecondTreeSize: lastSize,
})
return &Response{
Status: status.Code(err),
Err: err,
GetConsistencyProofResult: resp,
}
}
func (t *TrillianClient) getProofByHash(ctx context.Context, hashValue []byte) *Response {
rootResp := t.GetLatest(ctx, 0)
if rootResp.Err != nil {
return &Response{
Status: status.Code(rootResp.Err),
Err: rootResp.Err,
}
}
root, err := unmarshalLogRoot(rootResp.GetLatestResult.SignedLogRoot.LogRoot)
if err != nil {
return &Response{
Status: status.Code(rootResp.Err),
Err: rootResp.Err,
}
}
// issue 1308: if the tree is empty, there's no way we can return a proof
if root.TreeSize == 0 {
return &Response{
Status: codes.NotFound,
Err: status.Error(codes.NotFound, "tree is empty"),
}
}
resp, err := t.client.GetInclusionProofByHash(ctx,
&trillian.GetInclusionProofByHashRequest{
LogId: t.logID,
LeafHash: hashValue,
TreeSize: int64(root.TreeSize),
})
if resp != nil {
v := client.NewLogVerifier(rfc6962.DefaultHasher)
for _, proof := range resp.Proof {
if err := v.VerifyInclusionByHash(&root, hashValue, proof); err != nil {
return &Response{
Status: status.Code(err),
Err: err,
}
}
}
// Return an inclusion proof response with the requested
return &Response{
Status: status.Code(err),
Err: err,
getProofResult: &trillian.GetInclusionProofByHashResponse{
Proof: resp.Proof,
SignedLogRoot: rootResp.GetLatestResult.SignedLogRoot,
},
}
}
return &Response{
Status: status.Code(err),
Err: err,
}
}
// GetLeavesByRange fetches leaves from startIndex (inclusive) up to count leaves without proofs.
func (t *TrillianClient) GetLeavesByRange(ctx context.Context, startIndex, count int64) *Response {
resp, err := t.client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{
LogId: t.logID,
StartIndex: startIndex,
Count: count,
})
return &Response{
Status: status.Code(err),
Err: err,
GetLeavesByRangeResult: resp,
}
}
// GetLeafWithoutProof is a convenience wrapper for fetching a single leaf by index without proofs.
func (t *TrillianClient) GetLeafWithoutProof(ctx context.Context, index int64) *Response {
return t.GetLeavesByRange(ctx, index, 1)
}
// getStandaloneLeaf gets just the leaf, returns it in GetLeafAndProof result for easier reuse
func (t *TrillianClient) getStandaloneLeaf(ctx context.Context, index int64, hash []byte, proof *trillian.Proof, signedRoot *trillian.SignedLogRoot) *Response {
leafOnlyResp := t.GetLeafWithoutProof(ctx, index)
if leafOnlyResp.Err != nil {
return &Response{
Status: status.Code(leafOnlyResp.Err),
Err: leafOnlyResp.Err,
}
}
if leafOnlyResp.GetLeavesByRangeResult == nil || len(leafOnlyResp.GetLeavesByRangeResult.Leaves) == 0 {
err := fmt.Errorf("no leaf returned for index %d", index)
return &Response{
Status: codes.NotFound,
Err: err,
}
}
// shouldn't happen since we're using a log mode that prevents duplicates
if len(leafOnlyResp.GetLeavesByRangeResult.Leaves) != 1 {
err := fmt.Errorf("multiple leaves returned for index %d", index)
return &Response{
Status: codes.FailedPrecondition,
Err: err,
}
}
leaf := leafOnlyResp.GetLeavesByRangeResult.Leaves[0]
if !bytes.Equal(leaf.MerkleLeafHash, hash) {
// extremely unlikely but this means the index in the proof doesn't match the content stored in the index
err := fmt.Errorf("leaf hash mismatch: expected %v, got %v", hex.EncodeToString(hash), hex.EncodeToString(leaf.MerkleLeafHash))
return &Response{
Status: status.Code(err),
Err: err,
}
}
return &Response{
Status: codes.OK,
GetLeafAndProofResult: &trillian.GetEntryAndProofResponse{
Proof: proof,
Leaf: leaf,
SignedLogRoot: signedRoot,
},
}
}
//
// 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 alpine
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "alpine"
)
type BaseAlpineType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bat := BaseAlpineType{}
bat.Kind = KIND
bat.VersionMap = VersionMap
return &bat
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (bat *BaseAlpineType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
apk, ok := pe.(*models.Alpine)
if !ok {
return nil, errors.New("cannot unmarshal non-Alpine types")
}
return bat.VersionedUnmarshal(apk, *apk.APIVersion)
}
func (bat *BaseAlpineType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = bat.DefaultVersion()
}
ei, err := bat.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching Intoto version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (bat BaseAlpineType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 alpine
import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"crypto"
"crypto/sha1" // #nosec G505
"crypto/sha256"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"hash"
"io"
"strings"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
"github.com/spf13/viper"
"gopkg.in/ini.v1"
)
type Package struct {
Pkginfo map[string]string // KVP pairs
Signature []byte
Datahash []byte
controlSHA1Digest []byte
}
type sha1Reader struct {
r *bufio.Reader
addToHash bool
hasher hash.Hash
}
func newSHA1Reader(b *bufio.Reader) *sha1Reader {
// #nosec G401
c := sha1Reader{
r: b,
hasher: sha1.New(), //nolint: gosec
}
return &c
}
func (s *sha1Reader) Read(p []byte) (int, error) {
n, err := s.r.Read(p)
if err == nil && n > 0 && s.addToHash {
s.hasher.Write(p)
}
return n, err
}
func (s *sha1Reader) ReadByte() (byte, error) {
b, err := s.r.ReadByte()
if err == nil && s.addToHash {
s.hasher.Write([]byte{b})
}
return b, err
}
func (s sha1Reader) Sum() []byte {
return s.hasher.Sum(nil)
}
func (s *sha1Reader) StartHashing() {
s.hasher.Reset()
s.addToHash = true
}
func (s *sha1Reader) StopHashing() {
s.addToHash = false
}
func (p *Package) Unmarshal(pkgReader io.Reader) error {
pkg := Package{}
// bufio.Reader is required if Multistream(false) is used
bufReader := bufio.NewReader(pkgReader)
sha1BufReader := newSHA1Reader(bufReader)
gzipReader, err := gzip.NewReader(sha1BufReader)
if err != nil {
return fmt.Errorf("create gzip reader: %w", err)
}
defer func() {
_ = gzipReader.Close()
}()
// APKs are concatenated gzip files so we want to know where the boundary is
gzipReader.Multistream(false)
// GZIP headers/footers are left unmodified; Tar footers are removed on first two archives
// signature.tar.gz | control.tar.gz | data.tar.gz
sigBuf := bytes.Buffer{}
for {
_, err := io.CopyN(&sigBuf, gzipReader, 1024)
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading signature.tar.gz: %w", err)
}
}
// the SHA1 sum used in the signature is over the entire file control.tar.gz so we need to
// intercept the buffered reading to compute the hash correctly
//
// we start sha1 hashing now since the Reset() call will begin reading control.tar.gz headers
sha1BufReader.StartHashing()
// we reset the reader since we've found the end of signature.tar.gz
if err := gzipReader.Reset(sha1BufReader); err != nil && err != io.EOF {
return fmt.Errorf("resetting to control.tar.gz: %w", err)
}
gzipReader.Multistream(false)
controlTar := bytes.Buffer{}
for {
_, err := io.CopyN(&controlTar, gzipReader, 1024)
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading control.tar.gz: %w", err)
}
}
// signature uses sha1 digest hardcoded in abuild-sign tool
pkg.controlSHA1Digest = sha1BufReader.Sum()
sha1BufReader.StopHashing()
// the gzip reader is NOT reset again since that advances the underlying reader
// by reading the next GZIP header, which affects the datahash computation below
sigReader := tar.NewReader(&sigBuf)
for {
header, err := sigReader.Next()
if err == io.EOF {
if pkg.Signature == nil {
return errors.New("no signature detected in alpine package")
}
break
} else if err != nil {
return fmt.Errorf("getting next entry in tar archive: %w", err)
}
if strings.HasPrefix(header.Name, ".SIGN") && pkg.Signature == nil {
if header.Size < 0 {
return errors.New("negative header size for .SIGN file")
}
if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 {
return fmt.Errorf("uncompressed .SIGN file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size"))
}
sigBytes := make([]byte, header.Size)
if _, err = sigReader.Read(sigBytes); err != nil && err != io.EOF {
return fmt.Errorf("reading signature: %w", err)
}
// we're not sure whether this is PEM encoded or not, so handle both cases
block, _ := pem.Decode(sigBytes)
if block == nil {
pkg.Signature = sigBytes
} else {
pkg.Signature = block.Bytes
}
}
}
ctlReader := tar.NewReader(&controlTar)
for {
header, err := ctlReader.Next()
if err == io.EOF {
if pkg.Pkginfo == nil {
return errors.New(".PKGINFO file was not located")
}
break
} else if err != nil {
return fmt.Errorf("getting next entry in tar archive: %w", err)
}
if header.Name == ".PKGINFO" {
if header.Size < 0 {
return errors.New("negative header size for .PKGINFO file")
}
if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 {
return fmt.Errorf("uncompressed .PKGINFO file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size"))
}
pkginfoContent := make([]byte, header.Size)
if _, err = ctlReader.Read(pkginfoContent); err != nil && err != io.EOF {
return fmt.Errorf("reading .PKGINFO: %w", err)
}
pkg.Pkginfo, err = parsePkginfo(pkginfoContent)
if err != nil {
return fmt.Errorf("parsing .PKGINFO: %w", err)
}
pkg.Datahash, err = hex.DecodeString(pkg.Pkginfo["datahash"])
if err != nil {
return fmt.Errorf("parsing datahash: %w", err)
}
}
}
// at this point, bufReader should point to first byte of data.tar.gz
// datahash value from .PKGINFO is sha256 sum of data.tar.gz
sha256 := sha256.New()
if _, err := io.Copy(sha256, bufReader); err != nil {
return fmt.Errorf("computing SHA256 sum of data.tar.gz: %w", err)
}
computedSum := sha256.Sum(nil)
if !bytes.Equal(computedSum, pkg.Datahash) {
return fmt.Errorf("checksum for data.tar.gz (%v) does not match value from .PKGINFO (%v)", hex.EncodeToString(computedSum), hex.EncodeToString(pkg.Datahash))
}
*p = pkg
return nil
}
// VerifySignature verifies the signature of the alpine package using the provided
// public key. It returns an error if verification fails, or nil if it is successful.
func (p Package) VerifySignature(pub crypto.PublicKey) error {
if p.Signature == nil {
return errors.New("no signature in alpine package object")
}
if p.controlSHA1Digest == nil {
return errors.New("no digest value for data.tar.gz known")
}
verifier, err := signature.LoadUnsafeVerifier(pub)
if err != nil {
return err
}
return verifier.VerifySignature(bytes.NewReader(p.Signature), nil, options.WithDigest(p.controlSHA1Digest), options.WithCryptoSignerOpts(crypto.SHA1))
}
// parsePkginfo parses the .PKGINFO file which is in a
// key[space]=[space]value\n
// format. it returns a map[string]string of the key/value pairs, or
// an error if parsing could not be completed successfully.
func parsePkginfo(input []byte) (map[string]string, error) {
cfg, err := ini.Load(input)
if err != nil {
return nil, err
}
// .PKGINFO does not use sections, so using "" grabs the default values
return cfg.Section("").KeysHash(), nil
}
//
// 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 alpine
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/alpine"
"github.com/sigstore/rekor/pkg/util"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := alpine.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
AlpineModel models.AlpineV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
keyObj, err := x509.NewPublicKey(bytes.NewReader(*v.AlpineModel.PublicKey.Content))
if err != nil {
return nil, err
}
key, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
result = append(result, keyObj.Subjects()...)
if v.AlpineModel.Package.Hash != nil {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.AlpineModel.Package.Hash.Algorithm, *v.AlpineModel.Package.Hash.Value))
result = append(result, hashKey)
}
return result, nil
}
// DecodeEntry performs direct JSON unmarshaling without reflection,
// equivalent to types.DecodeEntry but with better performance for alpine v0.0.1.
func DecodeEntry(input any, output *models.AlpineV001Schema) error {
var m models.AlpineV001Schema
switch data := input.(type) {
case map[string]any:
mp := data
if pk, ok := mp["publicKey"].(map[string]any); ok {
if cs, ok := pk["content"].(string); ok && cs != "" {
outBuf := make([]byte, base64.StdEncoding.DecodedLen(len(cs)))
n, err := base64.StdEncoding.Decode(outBuf, []byte(cs))
if err != nil {
return fmt.Errorf("failed parsing base64 data for public key content: %w", err)
}
m.PublicKey = &models.AlpineV001SchemaPublicKey{}
content := strfmt.Base64(outBuf[:n])
m.PublicKey.Content = &content
}
}
if p, ok := mp["package"].(map[string]any); ok {
m.Package = &models.AlpineV001SchemaPackage{}
if pi, ok := p["pkginfo"].(map[string]any); ok {
mm := make(map[string]string, len(pi))
for k, vv := range pi {
if s, ok := vv.(string); ok {
mm[k] = s
}
}
if len(mm) > 0 {
m.Package.Pkginfo = mm
}
}
if h, ok := p["hash"].(map[string]any); ok {
var alg, val *string
if a, ok := h["algorithm"].(string); ok {
alg = &a
}
if s, ok := h["value"].(string); ok {
val = &s
}
if alg != nil || val != nil {
m.Package.Hash = &models.AlpineV001SchemaPackageHash{
Algorithm: alg,
Value: val,
}
}
}
if cs, ok := p["content"].(string); ok && cs != "" {
outBuf := make([]byte, base64.StdEncoding.DecodedLen(len(cs)))
n, err := base64.StdEncoding.Decode(outBuf, []byte(cs))
if err != nil {
return fmt.Errorf("failed parsing base64 data for package content: %w", err)
}
m.Package.Content = strfmt.Base64(outBuf[:n])
}
}
*output = m
return nil
case *models.AlpineV001Schema:
if data == nil {
return fmt.Errorf("nil *models.AlpineV001Schema")
}
*output = *data
return nil
case models.AlpineV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
apk, ok := pe.(*models.Alpine)
if !ok {
return errors.New("cannot unmarshal non Alpine v0.0.1 type")
}
if err := DecodeEntry(apk.Spec, &v.AlpineModel); err != nil {
return err
}
// field validation
if err := v.AlpineModel.Validate(strfmt.Default); err != nil {
return err
}
return v.validate()
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (*x509.PublicKey, *alpine.Package, error) {
if err := v.validate(); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
oldSHA := ""
if v.AlpineModel.Package.Hash != nil && v.AlpineModel.Package.Hash.Value != nil {
oldSHA = conv.Value(v.AlpineModel.Package.Hash.Value)
}
// Parse public key
keyObj, err := x509.NewPublicKey(bytes.NewReader(*v.AlpineModel.PublicKey.Content))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Parse package and verify hash
dataReadCloser := bytes.NewReader(v.AlpineModel.Package.Content)
hasher := sha256.New()
/* #nosec G110 */
packageData := bytes.NewBuffer(nil)
if _, err := io.Copy(io.MultiWriter(hasher, packageData), dataReadCloser); err != nil {
return nil, nil, err
}
computedSHA := hex.EncodeToString(hasher.Sum(nil))
if oldSHA != "" && computedSHA != oldSHA {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA)}
}
// Parse and verify package
apk := alpine.Package{}
if err := apk.Unmarshal(packageData); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
if err := apk.VerifySignature(keyObj.CryptoPubKey()); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Set hash if not provided
if oldSHA == "" {
v.AlpineModel.Package.Hash = &models.AlpineV001SchemaPackageHash{}
v.AlpineModel.Package.Hash.Algorithm = conv.Pointer(models.AlpineV001SchemaPackageHashAlgorithmSha256)
v.AlpineModel.Package.Hash.Value = conv.Pointer(computedSHA)
}
return keyObj, &apk, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
key, apkObj, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
canonicalEntry := models.AlpineV001Schema{}
var content []byte
// need to canonicalize key content
canonicalEntry.PublicKey = &models.AlpineV001SchemaPublicKey{}
content, err = key.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&content)
canonicalEntry.Package = &models.AlpineV001SchemaPackage{}
canonicalEntry.Package.Hash = &models.AlpineV001SchemaPackageHash{}
canonicalEntry.Package.Hash.Algorithm = v.AlpineModel.Package.Hash.Algorithm
canonicalEntry.Package.Hash.Value = v.AlpineModel.Package.Hash.Value
// data content is not set deliberately
// set .PKGINFO headers
canonicalEntry.Package.Pkginfo = apkObj.Pkginfo
// wrap in valid object with kind and apiVersion set
apk := models.Alpine{}
apk.APIVersion = conv.Pointer(APIVERSION)
apk.Spec = &canonicalEntry
v.AlpineModel = canonicalEntry
return json.Marshal(&apk)
}
// validate performs cross-field validation for fields in object
func (v V001Entry) validate() error {
key := v.AlpineModel.PublicKey
if key == nil {
return errors.New("missing public key")
}
if key.Content == nil || len(*key.Content) == 0 {
return errors.New("'content' must be specified for publicKey")
}
pkg := v.AlpineModel.Package
if pkg == nil {
return errors.New("missing package")
}
hash := pkg.Hash
if hash != nil {
// Only sha256 is supported for alpine v0.0.1; enforce length accordingly.
if hash.Value == nil || len(*hash.Value) != crypto.SHA256.Size()*2 {
return errors.New("invalid value for hash")
}
if _, err := hex.DecodeString(*hash.Value); err != nil {
return errors.New("invalid value for hash")
}
} else if len(pkg.Content) == 0 {
return errors.New("'content' must be specified for package")
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Alpine{}
re := V001Entry{}
// we will need artifact, public-key, signature
re.AlpineModel = models.AlpineV001Schema{}
re.AlpineModel.Package = &models.AlpineV001SchemaPackage{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening artifact file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
}
re.AlpineModel.Package.Content = strfmt.Base64(artifactBytes)
re.AlpineModel.PublicKey = &models.AlpineV001SchemaPublicKey{}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
re.AlpineModel.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0])
if err := re.validate(); err != nil {
return nil, err
}
if _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.AlpineModel
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.AlpineModel.PublicKey == nil || v.AlpineModel.PublicKey.Content == nil {
return nil, errors.New("alpine v0.0.1 entry not initialized")
}
key, err := x509.NewPublicKey(bytes.NewReader(*v.AlpineModel.PublicKey.Content))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.AlpineModel.Package == nil || v.AlpineModel.Package.Hash == nil || v.AlpineModel.Package.Hash.Value == nil || v.AlpineModel.Package.Hash.Algorithm == nil {
return "", errors.New("alpine v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.AlpineModel.Package.Hash.Algorithm, *v.AlpineModel.Package.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.AlpineModel.Package == nil {
return false, errors.New("missing package entry")
}
if len(v.AlpineModel.Package.Content) == 0 {
return false, errors.New("missing package content")
}
if v.AlpineModel.PublicKey == nil {
return false, errors.New("missing public key")
}
if v.AlpineModel.PublicKey.Content == nil || len(*v.AlpineModel.PublicKey.Content) == 0 {
return false, errors.New("missing public key content")
}
return true, nil
}
//
// Copyright 2022 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 cose
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "cose"
)
type BaseCOSEType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bit := BaseCOSEType{}
bit.Kind = KIND
bit.VersionMap = VersionMap
return &bit
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (it BaseCOSEType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
in, ok := pe.(*models.Cose)
if !ok {
return nil, errors.New("cannot unmarshal non-COSE types")
}
return it.VersionedUnmarshal(in, *in.APIVersion)
}
func (it *BaseCOSEType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = it.DefaultVersion()
}
ei, err := it.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching COSE version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (it BaseCOSEType) DefaultVersion() string {
return "0.0.1"
}
//
// Copyright 2022 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 cose
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/spf13/viper"
gocose "github.com/veraison/go-cose"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/cose"
)
const (
APIVERSION = "0.0.1"
)
const (
CurveP256 = "P-256"
)
func init() {
if err := cose.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
CoseObj models.CoseV001Schema
keyObj pki.PublicKey
sign1Msg *gocose.Sign1Message
envelopeHash []byte
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
// We add the key, the hash of the overall cose envelope, and the hash of the payload itself as keys.
keyObj, err := x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey))
if err != nil {
return nil, err
}
// 1. Key
key, err := keyObj.CanonicalValue()
if err != nil {
log.Logger.Error(err)
} else {
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
}
result = append(result, keyObj.Subjects()...)
// 2. Overall envelope
result = append(result, formatKey(v.CoseObj.Message))
// 3. Payload
if v.sign1Msg != nil {
result = append(result, formatKey(v.sign1Msg.Payload))
} else {
// If no payload exists (it's unpacked in validate() method)
// return now, as we will not be able to extract any headers
return result, nil
}
// If payload is an in-toto statement, let's grab the subjects.
if rawContentType, ok := v.sign1Msg.Headers.Protected[gocose.HeaderLabelContentType]; ok {
contentType, ok := rawContentType.(string)
// Integers as defined by CoAP content format are valid too,
// but in-intoto payload type is not defined there, so only
// proceed if content type is a string.
// See list of CoAP content formats here:
// https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats
if ok && contentType == in_toto.PayloadType {
stmt, err := getIntotoStatement(v.sign1Msg.Payload)
if err != nil {
// ContentType header says intoto statement, but
// parsing failed, continue with a warning.
log.Logger.Warnf("Failed to parse intoto statement")
} else {
for _, sub := range stmt.Subject {
for alg, digest := range sub.Digest {
index := alg + ":" + digest
result = append(result, index)
}
}
}
}
}
return result, nil
}
func getIntotoStatement(b []byte) (*in_toto.Statement, error) {
var stmt in_toto.Statement
if err := json.Unmarshal(b, &stmt); err != nil {
return nil, err
}
return &stmt, nil
}
func formatKey(b []byte) string {
h := sha256.Sum256(b)
hash := hex.EncodeToString(h[:])
return strings.ToLower(fmt.Sprintf("%s:%s", models.CoseV001SchemaDataPayloadHashAlgorithmSha256, hash))
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
it, ok := pe.(*models.Cose)
if !ok {
return errors.New("cannot unmarshal non Cose v0.0.1 type")
}
var err error
if err := DecodeEntry(it.Spec, &v.CoseObj); err != nil {
return err
}
// field validation
if err := v.CoseObj.Validate(strfmt.Default); err != nil {
return err
}
v.keyObj, err = x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey))
if err != nil {
return err
}
// Store the envelope hash.
// The CoseObj.Message is only populated during entry creation.
// When marshalling from the database (retrieval) the envelope
// hash must be decoded from the stored hex string.
// The envelope hash is used to create the attestation key during
// retrieval of a record.
if len(v.CoseObj.Message) == 0 {
if v.CoseObj.Data == nil || v.CoseObj.Data.EnvelopeHash == nil || v.CoseObj.Data.EnvelopeHash.Value == nil {
return errors.New("envelope hash should have been previously computed")
}
b, err := hex.DecodeString(*v.CoseObj.Data.EnvelopeHash.Value)
if err != nil {
return err
}
v.envelopeHash = b
} else {
h := sha256.Sum256(v.CoseObj.Message)
v.envelopeHash = h[:]
}
return v.validate()
}
func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
if v.keyObj == nil {
return nil, errors.New("cannot canonicalze empty key")
}
pk, err := v.keyObj.CanonicalValue()
if err != nil {
return nil, err
}
pkb := strfmt.Base64(pk)
h := sha256.Sum256([]byte(v.sign1Msg.Payload))
canonicalEntry := models.CoseV001Schema{
PublicKey: &pkb,
Data: &models.CoseV001SchemaData{
PayloadHash: &models.CoseV001SchemaDataPayloadHash{
Algorithm: conv.Pointer(models.CoseV001SchemaDataPayloadHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(h[:])),
},
EnvelopeHash: &models.CoseV001SchemaDataEnvelopeHash{
Algorithm: conv.Pointer(models.CoseV001SchemaDataEnvelopeHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(v.envelopeHash)),
},
},
}
itObj := models.Cose{}
itObj.APIVersion = conv.Pointer(APIVERSION)
itObj.Spec = &canonicalEntry
return json.Marshal(&itObj)
}
// validate performs cross-field validation for fields in object
func (v *V001Entry) validate() error {
// This also gets called in the CLI, where we won't have this data
// or during record retrieval (message is the raw COSE object) which
// is only stored as an attestation.
if len(v.CoseObj.Message) == 0 {
return nil
}
alg, pk, err := getPublicKey(v.keyObj)
if err != nil {
return err
}
bv, err := gocose.NewVerifier(alg, pk)
if err != nil {
return err
}
sign1Msg := gocose.NewSign1Message()
if err := sign1Msg.UnmarshalCBOR(v.CoseObj.Message); err != nil {
return err
}
if err := sign1Msg.Verify(v.CoseObj.Data.Aad, bv); err != nil {
return err
}
v.sign1Msg = sign1Msg
return nil
}
func getPublicKey(pk pki.PublicKey) (gocose.Algorithm, crypto.PublicKey, error) {
invAlg := gocose.Algorithm(0)
x5pk, ok := pk.(*x509.PublicKey)
if !ok {
return invAlg, nil, errors.New("invalid public key type")
}
cryptoPub := x5pk.CryptoPubKey()
var alg gocose.Algorithm
switch t := cryptoPub.(type) {
case *rsa.PublicKey:
alg = gocose.AlgorithmPS256
case *ecdsa.PublicKey:
alg = gocose.AlgorithmES256
if t.Params().Name != CurveP256 {
return invAlg, nil, fmt.Errorf("unsupported elliptic curve %s",
t.Params().Name)
}
default:
return invAlg, nil, fmt.Errorf("unsupported algorithm type %T", t)
}
return alg, cryptoPub, nil
}
// AttestationKey returns the digest of the COSE envelope that was uploaded,
// to be used to lookup the attestation from storage.
func (v *V001Entry) AttestationKey() string {
return fmt.Sprintf("%s:%s",
models.CoseV001SchemaDataEnvelopeHashAlgorithmSha256,
hex.EncodeToString(v.envelopeHash))
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.CoseV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.CoseV001Schema")
}
var m models.CoseV001Schema
// Typed switch including map fast path
switch data := input.(type) {
case map[string]any:
mm := data
if msg, ok := mm["message"].(string); ok && msg != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(msg)))
n, err := base64.StdEncoding.Decode(outb, []byte(msg))
if err != nil {
return fmt.Errorf("failed parsing base64 data for message: %w", err)
}
m.Message = strfmt.Base64(outb[:n])
}
if pk, ok := mm["publicKey"].(string); ok && pk != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(pk)))
n, err := base64.StdEncoding.Decode(outb, []byte(pk))
if err != nil {
return fmt.Errorf("failed parsing base64 data for publicKey: %w", err)
}
b := strfmt.Base64(outb[:n])
m.PublicKey = &b
}
if d, ok := mm["data"].(map[string]any); ok {
m.Data = &models.CoseV001SchemaData{}
if ph, ok := d["payloadHash"].(map[string]any); ok {
m.Data.PayloadHash = &models.CoseV001SchemaDataPayloadHash{}
if alg, ok := ph["algorithm"].(string); ok {
m.Data.PayloadHash.Algorithm = &alg
}
if val, ok := ph["value"].(string); ok {
m.Data.PayloadHash.Value = &val
}
}
if eh, ok := d["envelopeHash"].(map[string]any); ok {
m.Data.EnvelopeHash = &models.CoseV001SchemaDataEnvelopeHash{}
if alg, ok := eh["algorithm"].(string); ok {
m.Data.EnvelopeHash.Algorithm = &alg
}
if val, ok := eh["value"].(string); ok {
m.Data.EnvelopeHash.Value = &val
}
}
if aad, ok := d["aad"].(string); ok && aad != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(aad)))
n, err := base64.StdEncoding.Decode(outb, []byte(aad))
if err != nil {
return fmt.Errorf("failed parsing base64 data for aad: %w", err)
}
m.Data.Aad = strfmt.Base64(outb[:n])
}
}
*output = m
return nil
case *models.CoseV001Schema:
if data == nil {
return fmt.Errorf("nil *models.CoseV001Schema")
}
*output = *data
return nil
case models.CoseV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
// AttestationKeyValue returns both the key and value to be persisted
// into attestation storage
func (v *V001Entry) AttestationKeyValue() (string, []byte) {
storageSize := len(v.CoseObj.Message)
if storageSize > viper.GetInt("max_attestation_size") {
log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size"))
return "", nil
}
return v.AttestationKey(), v.CoseObj.Message
}
func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Cose{}
var err error
messageBytes := props.ArtifactBytes
if messageBytes == nil {
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
return nil, errors.New("cose envelopes cannot be fetched over HTTP(S)")
}
messageBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, err
}
}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided to verify signature")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
kb := strfmt.Base64(publicKeyBytes[0])
mb := strfmt.Base64(messageBytes)
re := V001Entry{
CoseObj: models.CoseV001Schema{
Data: &models.CoseV001SchemaData{
Aad: props.AdditionalAuthenticatedData,
},
PublicKey: &kb,
Message: mb,
},
}
returnVal.Spec = re.CoseObj
returnVal.APIVersion = conv.Pointer(re.APIVersion())
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.CoseObj.PublicKey == nil {
return nil, errors.New("cose v0.0.1 entry not initialized")
}
key, err := x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.CoseObj.Data == nil || v.CoseObj.Data.PayloadHash == nil || v.CoseObj.Data.PayloadHash.Value == nil || v.CoseObj.Data.PayloadHash.Algorithm == nil {
return "", errors.New("cose v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.CoseObj.Data.PayloadHash.Algorithm, *v.CoseObj.Data.PayloadHash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if len(v.CoseObj.Message) == 0 {
return false, errors.New("missing COSE Sign1 message")
}
if v.CoseObj.PublicKey == nil || len(*v.CoseObj.PublicKey) == 0 {
return false, errors.New("missing public key")
}
if v.CoseObj.Data == nil {
return false, errors.New("missing COSE data property")
}
if len(v.envelopeHash) == 0 {
return false, errors.New("envelope hash has not been computed")
}
if v.keyObj == nil {
return false, errors.New("public key has not been parsed")
}
if v.sign1Msg == nil {
return false, errors.New("signature has not been validated")
}
return true, nil
}
//
// Copyright 2023 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 dsse
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "dsse"
)
type BaseDSSEType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bit := BaseDSSEType{}
bit.Kind = KIND
bit.VersionMap = VersionMap
return &bit
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (it BaseDSSEType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
in, ok := pe.(*models.DSSE)
if !ok {
return nil, errors.New("cannot unmarshal non-DSSE types")
}
return it.VersionedUnmarshal(in, *in.APIVersion)
}
func (it *BaseDSSEType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = it.DefaultVersion()
}
ei, err := it.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching DSSE version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (it BaseDSSEType) DefaultVersion() string {
return "0.0.1"
}
//
// Copyright 2023 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 dsse
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
dsseType "github.com/sigstore/rekor/pkg/types/dsse"
"github.com/sigstore/sigstore/pkg/signature"
sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := dsseType.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
DSSEObj models.DSSEV001Schema
env *dsse.Envelope
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
// IndexKeys computes the list of keys that should map back to this entry.
// It should *never* reference v.DSSEObj.ProposedContent as those values would only
// be present at the time of insertion
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
for _, sig := range v.DSSEObj.Signatures {
if sig == nil || sig.Verifier == nil {
return result, errors.New("missing or malformed public key")
}
keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.Verifier))
if err != nil {
return result, err
}
canonKey, err := keyObj.CanonicalValue()
if err != nil {
return result, fmt.Errorf("could not canonicalize key: %w", err)
}
keyHash := sha256.Sum256(canonKey)
result = append(result, "sha256:"+hex.EncodeToString(keyHash[:]))
result = append(result, keyObj.Subjects()...)
}
if v.DSSEObj.PayloadHash != nil {
payloadHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value))
result = append(result, payloadHashKey)
}
if v.DSSEObj.EnvelopeHash != nil {
envelopeHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.EnvelopeHash.Algorithm, *v.DSSEObj.EnvelopeHash.Value))
result = append(result, envelopeHashKey)
}
if v.env == nil {
log.Logger.Info("DSSEObj content or DSSE envelope is nil, returning partial set of keys")
return result, nil
}
switch v.env.PayloadType {
case in_toto.PayloadType:
if v.env.Payload == "" {
log.Logger.Info("DSSEObj DSSE payload is empty")
return result, nil
}
decodedPayload, err := v.env.DecodeB64Payload()
if err != nil {
return result, fmt.Errorf("could not decode envelope payload: %w", err)
}
statement, err := parseStatement(decodedPayload)
if err != nil {
return result, err
}
for _, s := range statement.Subject {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
// Not all in-toto statements will contain a SLSA provenance predicate.
// See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate
// for other predicates.
if predicate, err := parseSlsaPredicate(decodedPayload); err == nil {
if predicate.Predicate.Materials != nil {
for _, s := range predicate.Predicate.Materials {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
}
}
default:
log.Logger.Infof("Unknown DSSE envelope payloadType: %s", v.env.PayloadType)
}
return result, nil
}
func parseStatement(p []byte) (*in_toto.Statement, error) {
ps := in_toto.Statement{}
if err := json.Unmarshal(p, &ps); err != nil {
return nil, err
}
return &ps, nil
}
func parseSlsaPredicate(p []byte) (*in_toto.ProvenanceStatement, error) {
predicate := in_toto.ProvenanceStatement{}
if err := json.Unmarshal(p, &predicate); err != nil {
return nil, err
}
return &predicate, nil
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.DSSEV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.DSSEV001Schema")
}
var m models.DSSEV001Schema
// Single switch with map fast path
switch data := input.(type) {
case map[string]any:
mm := data
if pcRaw, ok := mm["proposedContent"].(map[string]any); ok {
m.ProposedContent = &models.DSSEV001SchemaProposedContent{}
if env, ok := pcRaw["envelope"].(string); ok {
m.ProposedContent.Envelope = &env
}
if vsIF, ok := pcRaw["verifiers"].([]any); ok {
m.ProposedContent.Verifiers = make([]strfmt.Base64, 0, len(vsIF))
for _, it := range vsIF {
if s, ok := it.(string); ok && s != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
n, err := base64.StdEncoding.Decode(outb, []byte(s))
if err != nil {
return fmt.Errorf("failed parsing base64 data for verifier: %w", err)
}
m.ProposedContent.Verifiers = append(m.ProposedContent.Verifiers, strfmt.Base64(outb[:n]))
}
}
} else if vsStr, ok := pcRaw["verifiers"].([]string); ok {
m.ProposedContent.Verifiers = make([]strfmt.Base64, 0, len(vsStr))
for _, s := range vsStr {
if s == "" {
continue
}
outb := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
n, err := base64.StdEncoding.Decode(outb, []byte(s))
if err != nil {
return fmt.Errorf("failed parsing base64 data for verifier: %w", err)
}
m.ProposedContent.Verifiers = append(m.ProposedContent.Verifiers, strfmt.Base64(outb[:n]))
}
}
}
if sigs, ok := mm["signatures"].([]any); ok {
m.Signatures = make([]*models.DSSEV001SchemaSignaturesItems0, 0, len(sigs))
for _, s := range sigs {
if sm, ok := s.(map[string]any); ok {
item := &models.DSSEV001SchemaSignaturesItems0{}
if sig, ok := sm["signature"].(string); ok {
item.Signature = &sig
}
if vr, ok := sm["verifier"].(string); ok && vr != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(vr)))
n, err := base64.StdEncoding.Decode(outb, []byte(vr))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature verifier: %w", err)
}
b := strfmt.Base64(outb[:n])
item.Verifier = &b
}
m.Signatures = append(m.Signatures, item)
}
}
}
if eh, ok := mm["envelopeHash"].(map[string]any); ok {
m.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{}
if alg, ok := eh["algorithm"].(string); ok {
m.EnvelopeHash.Algorithm = &alg
}
if val, ok := eh["value"].(string); ok {
m.EnvelopeHash.Value = &val
}
}
if ph, ok := mm["payloadHash"].(map[string]any); ok {
m.PayloadHash = &models.DSSEV001SchemaPayloadHash{}
if alg, ok := ph["algorithm"].(string); ok {
m.PayloadHash.Algorithm = &alg
}
if val, ok := ph["value"].(string); ok {
m.PayloadHash.Value = &val
}
}
*output = m
return nil
case *models.DSSEV001Schema:
if data == nil {
return fmt.Errorf("nil *models.DSSEV001Schema")
}
*output = *data
return nil
case models.DSSEV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
it, ok := pe.(*models.DSSE)
if !ok {
return errors.New("cannot unmarshal non DSSE v0.0.1 type")
}
dsseObj := &models.DSSEV001Schema{}
if err := DecodeEntry(it.Spec, dsseObj); err != nil {
return err
}
// field validation
if err := dsseObj.Validate(strfmt.Default); err != nil {
return err
}
// either we have just proposed content or the canonicalized fields
if dsseObj.ProposedContent == nil {
// then we need canonicalized fields, and all must be present (if present, they would have been validated in the above call to Validate())
if dsseObj.EnvelopeHash == nil || dsseObj.PayloadHash == nil || len(dsseObj.Signatures) == 0 {
return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present")
}
v.DSSEObj = *dsseObj
return nil
}
// if we're here, then we're trying to propose a new entry so we check to ensure client's aren't setting server-side computed fields
if dsseObj.EnvelopeHash != nil || dsseObj.PayloadHash != nil || len(dsseObj.Signatures) != 0 {
return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present but not both")
}
env := &dsse.Envelope{}
if err := json.Unmarshal([]byte(*dsseObj.ProposedContent.Envelope), env); err != nil {
return err
}
if len(env.Signatures) == 0 {
return errors.New("DSSE envelope must contain 1 or more signatures")
}
allPubKeyBytes := make([][]byte, 0)
for _, publicKey := range dsseObj.ProposedContent.Verifiers {
if publicKey == nil {
return errors.New("an invalid null verifier was provided in ProposedContent")
}
allPubKeyBytes = append(allPubKeyBytes, publicKey)
}
sigToKeyMap, err := verifyEnvelope(allPubKeyBytes, env)
if err != nil {
return err
}
// we need to ensure we canonicalize the ordering of signatures
sortedSigs := make([]string, 0, len(sigToKeyMap))
for sig := range sigToKeyMap {
sortedSigs = append(sortedSigs, sig)
}
sort.Strings(sortedSigs)
for i, sig := range sortedSigs {
key := sigToKeyMap[sig]
canonicalizedKey, err := key.CanonicalValue()
if err != nil {
return err
}
b64CanonicalizedKey := strfmt.Base64(canonicalizedKey)
dsseObj.Signatures = append(dsseObj.Signatures, &models.DSSEV001SchemaSignaturesItems0{
Signature: &sortedSigs[i],
Verifier: &b64CanonicalizedKey,
})
}
decodedPayload, err := env.DecodeB64Payload()
if err != nil {
// this shouldn't happen because failure would have occurred in verifyEnvelope call above
return err
}
payloadHash := sha256.Sum256(decodedPayload)
dsseObj.PayloadHash = &models.DSSEV001SchemaPayloadHash{
Algorithm: conv.Pointer(models.DSSEV001SchemaPayloadHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(payloadHash[:])),
}
envelopeHash := sha256.Sum256([]byte(*dsseObj.ProposedContent.Envelope))
dsseObj.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{
Algorithm: conv.Pointer(models.DSSEV001SchemaEnvelopeHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(envelopeHash[:])),
}
// we've gotten through all processing without error, now update the object we're unmarshalling into
v.DSSEObj = *dsseObj
v.env = env
return nil
}
// Canonicalize returns a JSON representation of the entry to be persisted into the log. This
// will be further canonicalized by JSON Canonicalization Scheme (JCS) before being written.
//
// This function should not use v.DSSEObj.ProposedContent fields as they are client provided and
// should not be trusted; the other fields at the top level are only set server side.
func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
canonicalEntry := models.DSSEV001Schema{
Signatures: v.DSSEObj.Signatures,
EnvelopeHash: v.DSSEObj.EnvelopeHash,
PayloadHash: v.DSSEObj.PayloadHash,
ProposedContent: nil, // this is explicitly done as we don't want to canonicalize the envelope
}
for _, s := range canonicalEntry.Signatures {
if s.Signature == nil {
return nil, errors.New("canonical entry missing required signature")
}
}
sort.Slice(canonicalEntry.Signatures, func(i, j int) bool {
return *canonicalEntry.Signatures[i].Signature < *canonicalEntry.Signatures[j].Signature
})
itObj := models.DSSE{}
itObj.APIVersion = conv.Pointer(APIVERSION)
itObj.Spec = &canonicalEntry
return json.Marshal(&itObj)
}
// AttestationKey and AttestationKeyValue are not implemented so the envelopes will not be persisted in Rekor
func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.DSSE{}
re := V001Entry{
DSSEObj: models.DSSEV001Schema{
ProposedContent: &models.DSSEV001SchemaProposedContent{},
},
}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
return nil, errors.New("dsse envelopes cannot be fetched over HTTP(S)")
}
artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, err
}
}
env := &dsse.Envelope{}
if err := json.Unmarshal(artifactBytes, env); err != nil {
return nil, fmt.Errorf("payload must be a valid DSSE envelope: %w", err)
}
allPubKeyBytes := make([][]byte, 0)
if len(props.PublicKeyBytes) > 0 {
allPubKeyBytes = append(allPubKeyBytes, props.PublicKeyBytes...)
}
if len(props.PublicKeyPaths) > 0 {
for _, path := range props.PublicKeyPaths {
if path.IsAbs() {
return nil, errors.New("dsse public keys cannot be fetched over HTTP(S)")
}
publicKeyBytes, err := os.ReadFile(filepath.Clean(path.Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
allPubKeyBytes = append(allPubKeyBytes, publicKeyBytes)
}
}
keysBySig, err := verifyEnvelope(allPubKeyBytes, env)
if err != nil {
return nil, err
}
for _, key := range keysBySig {
canonicalKey, err := key.CanonicalValue()
if err != nil {
return nil, err
}
re.DSSEObj.ProposedContent.Verifiers = append(re.DSSEObj.ProposedContent.Verifiers, strfmt.Base64(canonicalKey))
}
re.DSSEObj.ProposedContent.Envelope = conv.Pointer(string(artifactBytes))
returnVal.Spec = re.DSSEObj
returnVal.APIVersion = conv.Pointer(re.APIVersion())
return &returnVal, nil
}
// verifyEnvelope takes in an array of possible key bytes and attempts to parse them as x509 public keys.
// it then uses these to verify the envelope and makes sure that every signature on the envelope is verified.
// it returns a map of verifiers indexed by the signature the verifier corresponds to.
func verifyEnvelope(allPubKeyBytes [][]byte, env *dsse.Envelope) (map[string]*x509.PublicKey, error) {
// generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature
verifierBySig := make(map[string]*x509.PublicKey)
allSigs := make(map[string]struct{})
for _, sig := range env.Signatures {
allSigs[sig.Sig] = struct{}{}
}
for _, pubKeyBytes := range allPubKeyBytes {
if len(allSigs) == 0 {
break // if all signatures have been verified, do not attempt anymore
}
key, err := x509.NewPublicKey(bytes.NewReader(pubKeyBytes))
if err != nil {
return nil, fmt.Errorf("could not parse public key as x509: %w", err)
}
vfr, err := signature.LoadVerifier(key.CryptoPubKey(), crypto.SHA256)
if err != nil {
return nil, fmt.Errorf("could not load verifier: %w", err)
}
dsseVfr, err := dsse.NewEnvelopeVerifier(&sigdsse.VerifierAdapter{SignatureVerifier: vfr})
if err != nil {
return nil, fmt.Errorf("could not use public key as a dsse verifier: %w", err)
}
accepted, err := dsseVfr.Verify(context.Background(), env)
if err != nil {
return nil, fmt.Errorf("could not verify envelope: %w", err)
}
for _, accept := range accepted {
delete(allSigs, accept.Sig.Sig)
verifierBySig[accept.Sig.Sig] = key
}
}
if len(allSigs) > 0 {
return nil, errors.New("all signatures must have a key that verifies it")
}
return verifierBySig, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if len(v.DSSEObj.Signatures) == 0 {
return nil, errors.New("dsse v0.0.1 entry not initialized")
}
var keys []pki.PublicKey
for _, s := range v.DSSEObj.Signatures {
key, err := x509.NewPublicKey(bytes.NewReader(*s.Verifier))
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.DSSEObj.PayloadHash == nil || v.DSSEObj.PayloadHash.Algorithm == nil || v.DSSEObj.PayloadHash.Value == nil {
return "", errors.New("dsse v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.DSSEObj.ProposedContent == nil {
return false, errors.New("missing proposed content")
}
if v.DSSEObj.ProposedContent.Envelope == nil || len(*v.DSSEObj.ProposedContent.Envelope) == 0 {
return false, errors.New("missing proposed DSSE envelope")
}
if len(v.DSSEObj.ProposedContent.Verifiers) == 0 {
return false, errors.New("missing proposed verifiers")
}
return true, nil
}
//
// 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"
"github.com/sigstore/rekor/pkg/pki"
)
// 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)
Verifiers() ([]pki.PublicKey, error) // list of keys or certificates that can verify an entry's signature
ArtifactHash() (string, error) // hex-encoded artifact hash prefixed with hash name, e.g. sha256:abcdef
Insertable() (bool, error) // denotes whether the entry that was unmarshalled has the writeOnly fields required to validate and insert into the log
}
// 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
}
// ProposedEntryIterator is an iterator over a list of proposed entries
type ProposedEntryIterator interface {
models.ProposedEntry
HasNext() bool
Get() models.ProposedEntry
GetNext() models.ProposedEntry
}
// 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())
}
} else {
return nil, fmt.Errorf("unknown kind '%v' specified", kind)
}
if ok, err := ei.Insertable(); !ok {
return nil, fmt.Errorf("entry not insertable into log: %w", err)
}
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: %w", 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
PublicKeyPaths []*url.URL
PublicKeyBytes [][]byte
PKIFormat string
}
//
// 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
// ValidationError indicates that there is an issue with the content in the HTTP Request that
// should result in an HTTP 400 Bad Request error being returned to the client
//
// Deprecated: use InputValidationError instead to take advantage of Go's error wrapping
type ValidationError error
// InputValidationError indicates that there is an issue with the content in the HTTP Request that
// should result in an HTTP 400 Bad Request error being returned to the client
type InputValidationError struct {
Err error
}
func (v *InputValidationError) Error() string { return v.Err.Error() }
func (v *InputValidationError) Unwrap() error { return v.Err }
//
// 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 hashedrekord
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "hashedrekord"
)
type BaseRekordType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
brt := BaseRekordType{}
brt.Kind = KIND
brt.VersionMap = VersionMap
return &brt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (rt BaseRekordType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
rekord, ok := pe.(*models.Hashedrekord)
if !ok {
return nil, fmt.Errorf("cannot unmarshal non-hashed Rekord types: %s", pe.Kind())
}
return rt.VersionedUnmarshal(rekord, *rekord.APIVersion)
}
func (rt *BaseRekordType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = rt.DefaultVersion()
}
ei, err := rt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching hashed Rekord version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (rt BaseRekordType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 hashedrekord
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/sigstore/pkg/signature/options"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := hashedrekord.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
HashedRekordObj models.HashedrekordV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
key := v.HashedRekordObj.Signature.PublicKey.Content
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
pub, err := x509.NewPublicKey(bytes.NewReader(key))
if err != nil {
return nil, err
}
result = append(result, pub.Subjects()...)
if v.HashedRekordObj.Data.Hash != nil {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value))
result = append(result, hashKey)
}
return result, nil
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.HashedrekordV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.HashedrekordV001Schema")
}
var m models.HashedrekordV001Schema
// Single type-switch with map fast path
switch data := input.(type) {
case map[string]any:
mm := data
if sigRaw, ok := mm["signature"].(map[string]any); ok {
m.Signature = &models.HashedrekordV001SchemaSignature{}
if c, ok := sigRaw["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature content: %w", err)
}
m.Signature.Content = outb[:n]
}
if pkRaw, ok := sigRaw["publicKey"].(map[string]any); ok {
m.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{}
if c, ok := pkRaw["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for public key content: %w", err)
}
m.Signature.PublicKey.Content = outb[:n]
}
}
}
if dataRaw, ok := mm["data"].(map[string]any); ok {
if hRaw, ok := dataRaw["hash"].(map[string]any); ok {
m.Data = &models.HashedrekordV001SchemaData{Hash: &models.HashedrekordV001SchemaDataHash{}}
if alg, ok := hRaw["algorithm"].(string); ok {
m.Data.Hash.Algorithm = &alg
}
if val, ok := hRaw["value"].(string); ok {
m.Data.Hash.Value = &val
}
}
}
*output = m
return nil
case *models.HashedrekordV001Schema:
if data == nil {
return fmt.Errorf("nil *models.HashedrekordV001Schema")
}
*output = *data
return nil
case models.HashedrekordV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
rekord, ok := pe.(*models.Hashedrekord)
if !ok {
return errors.New("cannot unmarshal non Rekord v0.0.1 type")
}
if err := DecodeEntry(rekord.Spec, &v.HashedRekordObj); err != nil {
return err
}
// field validation
if err := v.HashedRekordObj.Validate(strfmt.Default); err != nil {
return err
}
// cross field validation
_, _, err := v.validate()
return err
}
func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
sigObj, keyObj, err := v.validate()
if err != nil {
return nil, &types.InputValidationError{Err: err}
}
canonicalEntry := models.HashedrekordV001Schema{}
// need to canonicalize signature & key content
canonicalEntry.Signature = &models.HashedrekordV001SchemaSignature{}
canonicalEntry.Signature.Content, err = sigObj.CanonicalValue()
if err != nil {
return nil, err
}
// key URL (if known) is not set deliberately
canonicalEntry.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{}
canonicalEntry.Signature.PublicKey.Content, err = keyObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.Data = &models.HashedrekordV001SchemaData{}
canonicalEntry.Data.Hash = v.HashedRekordObj.Data.Hash
// data content is not set deliberately
v.HashedRekordObj = canonicalEntry
// wrap in valid object with kind and apiVersion set
rekordObj := models.Hashedrekord{}
rekordObj.APIVersion = conv.Pointer(APIVERSION)
rekordObj.Spec = &canonicalEntry
return json.Marshal(&rekordObj)
}
// validate performs cross-field validation for fields in object
func (v *V001Entry) validate() (pki.Signature, pki.PublicKey, error) {
sig := v.HashedRekordObj.Signature
if sig == nil {
return nil, nil, &types.InputValidationError{Err: errors.New("missing signature")}
}
// Hashed rekord type only works for x509 signature types
sigObj, err := x509.NewSignatureWithOpts(bytes.NewReader(sig.Content), options.WithED25519ph())
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
key := sig.PublicKey
if key == nil {
return nil, nil, &types.InputValidationError{Err: errors.New("missing public key")}
}
keyObj, err := x509.NewPublicKey(bytes.NewReader(key.Content))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
data := v.HashedRekordObj.Data
if data == nil {
return nil, nil, &types.InputValidationError{Err: errors.New("missing data")}
}
hash := data.Hash
if hash == nil {
return nil, nil, &types.InputValidationError{Err: errors.New("missing hash")}
}
var alg crypto.Hash
switch conv.Value(hash.Algorithm) {
case models.HashedrekordV001SchemaDataHashAlgorithmSha384:
if len(*hash.Value) != crypto.SHA384.Size()*2 {
return nil, nil, &types.InputValidationError{Err: errors.New("invalid value for hash")}
}
alg = crypto.SHA384
case models.HashedrekordV001SchemaDataHashAlgorithmSha512:
if len(*hash.Value) != crypto.SHA512.Size()*2 {
return nil, nil, &types.InputValidationError{Err: errors.New("invalid value for hash")}
}
alg = crypto.SHA512
default:
if len(*hash.Value) != crypto.SHA256.Size()*2 {
return nil, nil, &types.InputValidationError{Err: errors.New("invalid value for hash")}
}
alg = crypto.SHA256
}
decoded, err := hex.DecodeString(*hash.Value)
if err != nil {
return nil, nil, err
}
if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded), options.WithCryptoSignerOpts(alg)); err != nil {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("verifying signature: %w", err)}
}
return sigObj, keyObj, nil
}
func getDataHashAlgorithm(hashAlgorithm crypto.Hash) string {
switch hashAlgorithm {
case crypto.SHA384:
return models.HashedrekordV001SchemaDataHashAlgorithmSha384
case crypto.SHA512:
return models.HashedrekordV001SchemaDataHashAlgorithmSha512
default:
return models.HashedrekordV001SchemaDataHashAlgorithmSha256
}
}
func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Hashedrekord{}
re := V001Entry{}
// we will need artifact, public-key, signature
re.HashedRekordObj.Data = &models.HashedrekordV001SchemaData{}
var err error
if props.PKIFormat != string(pki.X509) {
return nil, errors.New("hashedrekord entries can only be created for artifacts signed with x509-based PKI")
}
re.HashedRekordObj.Signature = &models.HashedrekordV001SchemaSignature{}
sigBytes := props.SignatureBytes
if sigBytes == nil {
if props.SignaturePath == nil {
return nil, errors.New("a detached signature must be provided")
}
sigBytes, err = os.ReadFile(filepath.Clean(props.SignaturePath.Path))
if err != nil {
return nil, fmt.Errorf("error reading signature file: %w", err)
}
}
re.HashedRekordObj.Signature.Content = strfmt.Base64(sigBytes)
re.HashedRekordObj.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided to verify detached signature")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
hashAlgorithm, hashValue := util.UnprefixSHA(props.ArtifactHash)
re.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(publicKeyBytes[0])
re.HashedRekordObj.Data.Hash = &models.HashedrekordV001SchemaDataHash{
Algorithm: conv.Pointer(getDataHashAlgorithm(hashAlgorithm)),
Value: conv.Pointer(hashValue),
}
if _, _, err := re.validate(); err != nil {
return nil, err
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.HashedRekordObj
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.HashedRekordObj.Signature == nil || v.HashedRekordObj.Signature.PublicKey == nil || v.HashedRekordObj.Signature.PublicKey.Content == nil {
return nil, errors.New("hashedrekord v0.0.1 entry not initialized")
}
key, err := x509.NewPublicKey(bytes.NewReader(v.HashedRekordObj.Signature.PublicKey.Content))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.HashedRekordObj.Data == nil || v.HashedRekordObj.Data.Hash == nil || v.HashedRekordObj.Data.Hash.Value == nil || v.HashedRekordObj.Data.Hash.Algorithm == nil {
return "", errors.New("hashedrekord v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.HashedRekordObj.Signature == nil {
return false, errors.New("missing signature property")
}
if len(v.HashedRekordObj.Signature.Content) == 0 {
return false, errors.New("missing signature content")
}
if v.HashedRekordObj.Signature.PublicKey == nil {
return false, errors.New("missing publicKey property")
}
if len(v.HashedRekordObj.Signature.PublicKey.Content) == 0 {
return false, errors.New("missing publicKey content")
}
if v.HashedRekordObj.Data == nil {
return false, errors.New("missing data property")
}
if v.HashedRekordObj.Data.Hash == nil {
return false, errors.New("missing hash property")
}
if v.HashedRekordObj.Data.Hash.Algorithm == nil {
return false, errors.New("missing hash algorithm")
}
if v.HashedRekordObj.Data.Hash.Value == nil {
return false, errors.New("missing hash value")
}
return true, nil
}
//
// 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 helm
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "helm"
)
type BaseHelmType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bit := BaseHelmType{}
bit.Kind = KIND
bit.VersionMap = VersionMap
return &bit
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (it BaseHelmType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
in, ok := pe.(*models.Helm)
if !ok {
return nil, errors.New("cannot unmarshal non-Rekord types")
}
return it.VersionedUnmarshal(in, *in.APIVersion)
}
func (it *BaseHelmType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = it.DefaultVersion()
}
ei, err := it.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching Rekord version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (it BaseHelmType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 helm
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
"sigs.k8s.io/yaml"
//TODO: https://github.com/sigstore/rekor/issues/286
"golang.org/x/crypto/openpgp/clearsign" //nolint:staticcheck
)
type Provenance struct {
ChartMetadata map[string]string
SumCollection *SumCollection
Block *clearsign.Block
}
type SumCollection struct {
Files map[string]string `json:"files"`
Images map[string]string `json:"images,omitempty"`
}
func (p *Provenance) Unmarshal(reader io.Reader) error {
buf := &bytes.Buffer{}
read, err := buf.ReadFrom(reader)
if err != nil {
return errors.New("failed to read from buffer")
} else if read == 0 {
return errors.New("provenance file contains no content")
}
block, _ := clearsign.Decode(buf.Bytes())
if block == nil {
return errors.New("unable to decode provenance file")
}
if block.ArmoredSignature == nil {
return errors.New("unable to locate armored signature in provenance file")
}
if err = p.parseMessageBlock(block.Plaintext); err != nil {
return fmt.Errorf("error occurred parsing message block: %w", err)
}
p.Block = block
return nil
}
func (p *Provenance) parseMessageBlock(data []byte) error {
parts := bytes.Split(data, []byte("\n...\n"))
if len(parts) < 2 {
return errors.New("message block must have at least two parts")
}
sc := &SumCollection{}
if err := yaml.Unmarshal(parts[1], sc); err != nil {
return fmt.Errorf("error occurred parsing SumCollection: %w", err)
}
p.SumCollection = sc
return nil
}
func (p *Provenance) GetChartAlgorithmHash() (string, string, error) {
if p.SumCollection == nil || p.SumCollection.Files == nil {
return "", "", errors.New("unable to locate chart hash")
}
for _, value := range p.SumCollection.Files {
parts := strings.Split(value, ":")
if len(parts) != 2 {
return "", "", errors.New("invalid hash found in Provenance file")
}
return parts[0], parts[1], nil
}
// Return error if no keys found
return "", "", errors.New("no checksums found")
}
//
// 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 helm
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/pgp"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/helm"
"github.com/sigstore/rekor/pkg/util"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := helm.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
HelmObj models.HelmV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.HelmObj.PublicKey.Content))
if err != nil {
return nil, err
}
provenance := helm.Provenance{}
if err := provenance.Unmarshal(bytes.NewReader(v.HelmObj.Chart.Provenance.Content)); err != nil {
return nil, err
}
key, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
result = append(result, keyObj.Subjects()...)
algorithm, chartHash, err := provenance.GetChartAlgorithmHash()
if err != nil {
log.Logger.Error(err)
} else {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", algorithm, chartHash))
result = append(result, hashKey)
}
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
helm, ok := pe.(*models.Helm)
if !ok {
return errors.New("cannot unmarshal non Helm v0.0.1 type")
}
if err := DecodeEntry(helm.Spec, &v.HelmObj); err != nil {
return err
}
// field validation
if err := v.HelmObj.Validate(strfmt.Default); err != nil {
return err
}
// cross field validation
return v.validate()
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.HelmV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.HelmV001Schema")
}
var m models.HelmV001Schema
// Single switch including map fast path
switch data := input.(type) {
case map[string]any:
mm := data
if pk, ok := mm["publicKey"].(map[string]any); ok {
m.PublicKey = &models.HelmV001SchemaPublicKey{}
if c, ok := pk["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for publicKey content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.PublicKey.Content = &b
}
}
if chart, ok := mm["chart"].(map[string]any); ok {
m.Chart = &models.HelmV001SchemaChart{}
if h, ok := chart["hash"].(map[string]any); ok {
m.Chart.Hash = &models.HelmV001SchemaChartHash{}
if alg, ok := h["algorithm"].(string); ok {
m.Chart.Hash.Algorithm = &alg
}
if val, ok := h["value"].(string); ok {
m.Chart.Hash.Value = &val
}
}
if prov, ok := chart["provenance"].(map[string]any); ok {
m.Chart.Provenance = &models.HelmV001SchemaChartProvenance{}
if s, ok := prov["signature"].(map[string]any); ok {
m.Chart.Provenance.Signature = &models.HelmV001SchemaChartProvenanceSignature{}
if c, ok := s["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for provenance signature content: %w", err)
}
m.Chart.Provenance.Signature.Content = strfmt.Base64(outb[:n])
}
}
if c, ok := prov["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for provenance content: %w", err)
}
m.Chart.Provenance.Content = strfmt.Base64(outb[:n])
}
}
}
*output = m
return nil
case *models.HelmV001Schema:
if data == nil {
return fmt.Errorf("nil *models.HelmV001Schema")
}
*output = *data
return nil
case models.HelmV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (*helm.Provenance, *pgp.PublicKey, *pgp.Signature, error) {
if err := v.validate(); err != nil {
return nil, nil, nil, &types.InputValidationError{Err: err}
}
// Parse public key
keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.HelmObj.PublicKey.Content))
if err != nil {
return nil, nil, nil, &types.InputValidationError{Err: err}
}
// Parse provenance
provenance := &helm.Provenance{}
if err := provenance.Unmarshal(bytes.NewReader(v.HelmObj.Chart.Provenance.Content)); err != nil {
return nil, nil, nil, &types.InputValidationError{Err: err}
}
// Create signature
sig, err := pgp.NewSignature(provenance.Block.ArmoredSignature.Body)
if err != nil {
return nil, nil, nil, &types.InputValidationError{Err: err}
}
// Verify signature
if err := sig.Verify(bytes.NewReader(provenance.Block.Bytes), keyObj); err != nil {
return nil, nil, nil, &types.InputValidationError{Err: err}
}
return provenance, keyObj, sig, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
provenanceObj, keyObj, sigObj, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
if keyObj == nil {
return nil, errors.New("key object not initialized before canonicalization")
}
canonicalEntry := models.HelmV001Schema{}
canonicalEntry.PublicKey = &models.HelmV001SchemaPublicKey{}
keyContent, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&keyContent)
canonicalEntry.Chart = &models.HelmV001SchemaChart{}
algorithm, chartHash, err := provenanceObj.GetChartAlgorithmHash()
if err != nil {
return nil, err
}
canonicalEntry.Chart.Hash = &models.HelmV001SchemaChartHash{}
canonicalEntry.Chart.Hash.Algorithm = &algorithm
canonicalEntry.Chart.Hash.Value = &chartHash
canonicalEntry.Chart.Provenance = &models.HelmV001SchemaChartProvenance{}
canonicalEntry.Chart.Provenance.Signature = &models.HelmV001SchemaChartProvenanceSignature{}
sigContent, err := sigObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.Chart.Provenance.Signature.Content = sigContent
// wrap in valid object with kind and apiVersion set
helmObj := models.Helm{}
helmObj.APIVersion = conv.Pointer(APIVERSION)
helmObj.Spec = &canonicalEntry
return json.Marshal(&helmObj)
}
// validate performs cross-field validation for fields in object
func (v V001Entry) validate() error {
key := v.HelmObj.PublicKey
if key == nil {
return errors.New("missing public key")
}
if key.Content == nil || len(*key.Content) == 0 {
return errors.New("'content' must be specified for publicKey")
}
chart := v.HelmObj.Chart
if chart == nil {
return errors.New("missing chart")
}
provenance := chart.Provenance
if provenance == nil {
return errors.New("missing provenance")
}
if provenance.Signature == nil || provenance.Signature.Content == nil {
if len(provenance.Content) == 0 {
return errors.New("'content' must be specified for provenance")
}
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
//TODO: how to select version of item to create
returnVal := models.Helm{}
re := V001Entry{}
// we will need provenance file and public-key
re.HelmObj = models.HelmV001Schema{}
re.HelmObj.Chart = &models.HelmV001SchemaChart{}
re.HelmObj.Chart.Provenance = &models.HelmV001SchemaChartProvenance{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading chart file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening chart file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading chart file: %w", err)
}
}
re.HelmObj.Chart.Provenance.Content = strfmt.Base64(artifactBytes)
re.HelmObj.PublicKey = &models.HelmV001SchemaPublicKey{}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
re.HelmObj.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0])
if err := re.validate(); err != nil {
return nil, err
}
if _, _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.HelmObj
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.HelmObj.PublicKey == nil || v.HelmObj.PublicKey.Content == nil {
return nil, errors.New("helm v0.0.1 entry not initialized")
}
key, err := pgp.NewPublicKey(bytes.NewReader(*v.HelmObj.PublicKey.Content))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.HelmObj.Chart == nil || v.HelmObj.Chart.Hash == nil || v.HelmObj.Chart.Hash.Algorithm == nil || v.HelmObj.Chart.Hash.Value == nil {
return "", errors.New("helm v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.HelmObj.Chart.Hash.Algorithm, *v.HelmObj.Chart.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.HelmObj.PublicKey == nil {
return false, errors.New("missing public key property")
}
if v.HelmObj.PublicKey.Content == nil || len(*v.HelmObj.PublicKey.Content) == 0 {
return false, errors.New("missing public key content")
}
if v.HelmObj.Chart == nil {
return false, errors.New("missing chart property")
}
if v.HelmObj.Chart.Provenance == nil {
return false, errors.New("missing provenance property")
}
if len(v.HelmObj.Chart.Provenance.Content) == 0 {
return false, errors.New("missing provenance content")
}
return true, nil
}
//
// 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 intoto
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/types"
"golang.org/x/exp/slices"
)
const (
KIND = "intoto"
)
type BaseIntotoType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bit := BaseIntotoType{}
bit.Kind = KIND
bit.VersionMap = VersionMap
return &bit
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (it BaseIntotoType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
in, ok := pe.(*models.Intoto)
if !ok {
return nil, errors.New("cannot unmarshal non-Rekord types")
}
return it.VersionedUnmarshal(in, *in.APIVersion)
}
func (it *BaseIntotoType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
var head ProposedIntotoEntryIterator
var next *ProposedIntotoEntryIterator
if version == "" {
// get default version as head of list
version = it.DefaultVersion()
ei, err := it.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching default Intoto version implementation: %w", err)
}
pe, err := ei.CreateFromArtifactProperties(ctx, props)
if err != nil {
return nil, fmt.Errorf("creating default Intoto entry: %w", err)
}
head.ProposedEntry = pe
next = &head
for _, v := range it.SupportedVersions() {
if v == it.DefaultVersion() {
continue
}
ei, err := it.VersionedUnmarshal(nil, v)
if err != nil {
log.ContextLogger(ctx).Errorf("fetching Intoto version (%v) implementation: %w", v, err)
continue
}
versionedPE, err := ei.CreateFromArtifactProperties(ctx, props)
if err != nil {
log.ContextLogger(ctx).Errorf("error creating Intoto entry of version (%v): %w", v, err)
continue
}
next.next = &ProposedIntotoEntryIterator{versionedPE, nil}
next = next.next.(*ProposedIntotoEntryIterator)
}
return head, nil
}
ei, err := it.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching Intoto version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (it BaseIntotoType) DefaultVersion() string {
return "0.0.2"
}
// SupportedVersions returns the supported versions for this type in the order of preference
func (it BaseIntotoType) SupportedVersions() []string {
return []string{"0.0.2", "0.0.1"}
}
// IsSupportedVersion returns true if the version can be inserted into the log, and false if not
func (it *BaseIntotoType) IsSupportedVersion(proposedVersion string) bool {
return slices.Contains(it.SupportedVersions(), proposedVersion)
}
type ProposedIntotoEntryIterator struct {
models.ProposedEntry
next models.ProposedEntry
}
func (p ProposedIntotoEntryIterator) HasNext() bool {
return p.next != nil
}
func (p ProposedIntotoEntryIterator) GetNext() models.ProposedEntry {
return p.next
}
func (p ProposedIntotoEntryIterator) Get() models.ProposedEntry {
return p.ProposedEntry
}
//
// 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 intoto
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/spf13/viper"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/intoto"
"github.com/sigstore/sigstore/pkg/signature"
dsse_verifier "github.com/sigstore/sigstore/pkg/signature/dsse"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := intoto.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
IntotoObj models.IntotoV001Schema
keyObj pki.PublicKey
env dsse.Envelope
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
// add digest over entire DSSE envelope
if v.IntotoObj.Content != nil && v.IntotoObj.Content.Hash != nil {
hashkey := strings.ToLower(fmt.Sprintf("%s:%s", conv.Value(v.IntotoObj.Content.Hash.Algorithm), conv.Value(v.IntotoObj.Content.Hash.Value)))
result = append(result, hashkey)
} else {
log.Logger.Error("could not find content digest to include in index keys")
}
// add digest over public key
if v.keyObj != nil {
key, err := v.keyObj.CanonicalValue()
if err == nil {
keyHash := sha256.Sum256(key)
result = append(result, fmt.Sprintf("sha256:%s", strings.ToLower(hex.EncodeToString(keyHash[:]))))
// add digest over any subjects within signing certificate
result = append(result, v.keyObj.Subjects()...)
} else {
log.Logger.Errorf("could not canonicalize public key to include in index keys: %w", err)
}
} else {
log.Logger.Error("could not find public key to include in index keys")
}
// add digest base64-decoded payload inside of DSSE envelope
if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil {
payloadHash := strings.ToLower(fmt.Sprintf("%s:%s", conv.Value(v.IntotoObj.Content.PayloadHash.Algorithm), conv.Value(v.IntotoObj.Content.PayloadHash.Value)))
result = append(result, payloadHash)
} else {
log.Logger.Error("could not find payload digest to include in index keys")
}
switch v.env.PayloadType {
case in_toto.PayloadType:
statement, err := parseStatement(v.env.Payload)
if err != nil {
log.Logger.Errorf("error parsing payload as intoto statement: %w", err)
break
}
for _, s := range statement.Subject {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
// Not all in-toto statements will contain a SLSA provenance predicate.
// See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate
// for other predicates.
if predicate, err := parseSlsaPredicate(v.env.Payload); err == nil {
if predicate.Predicate.Materials != nil {
for _, s := range predicate.Predicate.Materials {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
}
}
default:
log.Logger.Infof("unknown in_toto statement type (%s), cannot extract additional index keys", v.env.PayloadType)
}
return result, nil
}
func parseStatement(p string) (*in_toto.Statement, error) {
ps := in_toto.Statement{}
payload, err := base64.StdEncoding.DecodeString(p)
if err != nil {
return nil, err
}
if err := json.Unmarshal(payload, &ps); err != nil {
return nil, err
}
return &ps, nil
}
func parseSlsaPredicate(p string) (*in_toto.ProvenanceStatement, error) {
predicate := in_toto.ProvenanceStatement{}
payload, err := base64.StdEncoding.DecodeString(p)
if err != nil {
return nil, err
}
if err := json.Unmarshal(payload, &predicate); err != nil {
return nil, err
}
return &predicate, nil
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.IntotoV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.IntotoV001Schema")
}
var m models.IntotoV001Schema
switch data := input.(type) {
case map[string]any:
mm := data
if c, ok := mm["content"].(map[string]any); ok {
m.Content = &models.IntotoV001SchemaContent{}
if env, ok := c["envelope"].(string); ok {
m.Content.Envelope = env
}
if h, ok := c["hash"].(map[string]any); ok {
m.Content.Hash = &models.IntotoV001SchemaContentHash{}
if alg, ok := h["algorithm"].(string); ok {
m.Content.Hash.Algorithm = &alg
}
if val, ok := h["value"].(string); ok {
m.Content.Hash.Value = &val
}
}
if ph, ok := c["payloadHash"].(map[string]any); ok {
m.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{}
if alg, ok := ph["algorithm"].(string); ok {
m.Content.PayloadHash.Algorithm = &alg
}
if val, ok := ph["value"].(string); ok {
m.Content.PayloadHash.Value = &val
}
}
}
if pk, ok := mm["publicKey"].(string); ok && pk != "" {
dec, err := base64.StdEncoding.DecodeString(pk)
if err != nil {
return fmt.Errorf("failed parsing base64 data for public key: %w", err)
}
b := strfmt.Base64(dec)
m.PublicKey = &b
}
*output = m
return nil
case *models.IntotoV001Schema:
if data == nil {
return fmt.Errorf("nil *models.IntotoV001Schema")
}
*output = *data
return nil
case models.IntotoV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
it, ok := pe.(*models.Intoto)
if !ok {
return errors.New("cannot unmarshal non Intoto v0.0.1 type")
}
var err error
if err := DecodeEntry(it.Spec, &v.IntotoObj); err != nil {
return err
}
// field validation
if err := v.IntotoObj.Validate(strfmt.Default); err != nil {
return err
}
v.keyObj, err = x509.NewPublicKey(bytes.NewReader(*v.IntotoObj.PublicKey))
if err != nil {
return err
}
return v.validate()
}
func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
if v.keyObj == nil {
return nil, errors.New("cannot canonicalize empty key")
}
if v.IntotoObj.Content == nil {
return nil, errors.New("missing content")
}
if v.IntotoObj.Content.Hash == nil {
return nil, errors.New("missing envelope hash")
}
// PayloadHash is not present for old entries
pk, err := v.keyObj.CanonicalValue()
if err != nil {
return nil, err
}
pkb := strfmt.Base64(pk)
canonicalEntry := models.IntotoV001Schema{
PublicKey: &pkb,
Content: &models.IntotoV001SchemaContent{
Hash: &models.IntotoV001SchemaContentHash{
Algorithm: v.IntotoObj.Content.Hash.Algorithm,
Value: v.IntotoObj.Content.Hash.Value,
},
},
}
// Set PayloadHash if present
if v.IntotoObj.Content.PayloadHash != nil {
canonicalEntry.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{
Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm,
Value: v.IntotoObj.Content.PayloadHash.Value,
}
}
itObj := models.Intoto{}
itObj.APIVersion = conv.Pointer(APIVERSION)
itObj.Spec = &canonicalEntry
return json.Marshal(&itObj)
}
// validate performs cross-field validation for fields in object
func (v *V001Entry) validate() error {
// TODO handle multiple
pk := v.keyObj.(*x509.PublicKey)
// one of two cases must be true:
// - ProposedEntry: client gives an envelope; (client provided hash/payloadhash are ignored as they are computed server-side) OR
// - CommittedEntry: NO envelope and hash/payloadHash must be present
if v.IntotoObj.Content.Envelope == "" {
if v.IntotoObj.Content.Hash == nil {
return fmt.Errorf("missing hash value for envelope")
} else if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil {
return fmt.Errorf("validation error on envelope hash: %w", err)
}
// PayloadHash is not present for old entries
if v.IntotoObj.Content.PayloadHash != nil {
if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil {
return fmt.Errorf("validation error on payload hash: %w", err)
}
}
// if there is no envelope, and hash/payloadHash are valid, then there's nothing else to do here
return nil
}
vfr, err := signature.LoadVerifier(pk.CryptoPubKey(), crypto.SHA256)
if err != nil {
return err
}
dsseVerifier := dsse_verifier.WrapVerifier(vfr)
if err := dsseVerifier.VerifySignature(strings.NewReader(v.IntotoObj.Content.Envelope), nil); err != nil {
return err
}
if err := json.Unmarshal([]byte(v.IntotoObj.Content.Envelope), &v.env); err != nil {
return err
}
attBytes, err := base64.StdEncoding.DecodeString(v.env.Payload)
if err != nil {
return err
}
// validation logic complete without errors, hydrate local object
attHash := sha256.Sum256(attBytes)
v.IntotoObj.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{
Algorithm: conv.Pointer(models.IntotoV001SchemaContentPayloadHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(attHash[:])),
}
h := sha256.Sum256([]byte(v.IntotoObj.Content.Envelope))
v.IntotoObj.Content.Hash = &models.IntotoV001SchemaContentHash{
Algorithm: conv.Pointer(models.IntotoV001SchemaContentHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(h[:])),
}
return nil
}
// AttestationKey returns the digest of the attestation that was uploaded, to be used to lookup the attestation from storage
func (v *V001Entry) AttestationKey() string {
if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil {
return fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)
}
return ""
}
// AttestationKeyValue returns both the key and value to be persisted into attestation storage
func (v *V001Entry) AttestationKeyValue() (string, []byte) {
storageSize := base64.StdEncoding.DecodedLen(len(v.env.Payload))
if storageSize > viper.GetInt("max_attestation_size") {
log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size"))
return "", nil
}
attBytes, _ := base64.StdEncoding.DecodeString(v.env.Payload)
return v.AttestationKey(), attBytes
}
func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Intoto{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
return nil, errors.New("intoto envelopes cannot be fetched over HTTP(S)")
}
artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, err
}
}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided to verify signature")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
kb := strfmt.Base64(publicKeyBytes[0])
re := V001Entry{
IntotoObj: models.IntotoV001Schema{
Content: &models.IntotoV001SchemaContent{
Envelope: string(artifactBytes),
},
PublicKey: &kb,
},
}
h := sha256.Sum256([]byte(re.IntotoObj.Content.Envelope))
re.IntotoObj.Content.Hash = &models.IntotoV001SchemaContentHash{
Algorithm: conv.Pointer(models.IntotoV001SchemaContentHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(h[:])),
}
returnVal.Spec = re.IntotoObj
returnVal.APIVersion = conv.Pointer(re.APIVersion())
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.IntotoObj.PublicKey == nil {
return nil, errors.New("intoto v0.0.1 entry not initialized")
}
key, err := x509.NewPublicKey(bytes.NewReader(*v.IntotoObj.PublicKey))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil {
return "", errors.New("hashedrekord v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.IntotoObj.Content == nil {
return false, errors.New("missing content property")
}
if len(v.IntotoObj.Content.Envelope) == 0 {
return false, errors.New("missing envelope content")
}
if v.IntotoObj.PublicKey == nil || len(*v.IntotoObj.PublicKey) == 0 {
return false, errors.New("missing publicKey content")
}
if v.keyObj == nil {
return false, errors.New("failed to parse public key")
}
if v.env.Payload == "" || v.env.PayloadType == "" || len(v.env.Signatures) == 0 {
return false, errors.New("invalid DSSE envelope")
}
return true, nil
}
//
// Copyright 2022 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 intoto
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/spf13/viper"
"golang.org/x/exp/slices"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/intoto"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
)
const (
APIVERSION = "0.0.2"
)
func init() {
if err := intoto.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V002Entry struct {
IntotoObj models.IntotoV002Schema
env dsse.Envelope
}
func (v V002Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V002Entry{}
}
func (v V002Entry) IndexKeys() ([]string, error) {
var result []string
if v.IntotoObj.Content == nil || v.IntotoObj.Content.Envelope == nil {
log.Logger.Info("IntotoObj content or dsse envelope is nil")
return result, nil
}
for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
if sig == nil || sig.PublicKey == nil {
return result, errors.New("malformed or missing signature")
}
keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.PublicKey))
if err != nil {
return result, err
}
canonKey, err := keyObj.CanonicalValue()
if err != nil {
return result, fmt.Errorf("could not canonicize key: %w", err)
}
keyHash := sha256.Sum256(canonKey)
result = append(result, "sha256:"+hex.EncodeToString(keyHash[:]))
result = append(result, keyObj.Subjects()...)
}
payloadKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value))
result = append(result, payloadKey)
// since we can't deterministically calculate this server-side (due to public keys being added inline, and also canonicalization being potentially different),
// we'll just skip adding this index key
// hashkey := strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.Hash.Algorithm, *v.IntotoObj.Content.Hash.Value))
// result = append(result, hashkey)
switch *v.IntotoObj.Content.Envelope.PayloadType {
case in_toto.PayloadType:
if v.IntotoObj.Content.Envelope.Payload == nil {
log.Logger.Info("IntotoObj DSSE payload is empty")
return result, nil
}
decodedPayload, err := base64.StdEncoding.DecodeString(string(v.IntotoObj.Content.Envelope.Payload))
if err != nil {
return result, fmt.Errorf("could not decode envelope payload: %w", err)
}
statement, err := parseStatement(decodedPayload)
if err != nil {
return result, err
}
for _, s := range statement.Subject {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
// Not all in-toto statements will contain a SLSA provenance predicate.
// See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate
// for other predicates.
if predicate, err := parseSlsaPredicate(decodedPayload); err == nil {
if predicate.Predicate.Materials != nil {
for _, s := range predicate.Predicate.Materials {
for alg, ds := range s.Digest {
result = append(result, alg+":"+ds)
}
}
}
}
default:
log.Logger.Infof("Unknown in_toto DSSE envelope Type: %s", *v.IntotoObj.Content.Envelope.PayloadType)
}
return result, nil
}
func parseStatement(p []byte) (*in_toto.Statement, error) {
ps := in_toto.Statement{}
if err := json.Unmarshal(p, &ps); err != nil {
return nil, err
}
return &ps, nil
}
func parseSlsaPredicate(p []byte) (*in_toto.ProvenanceStatement, error) {
predicate := in_toto.ProvenanceStatement{}
if err := json.Unmarshal(p, &predicate); err != nil {
return nil, err
}
return &predicate, nil
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.IntotoV002Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.IntotoV002Schema")
}
var m models.IntotoV002Schema
switch in := input.(type) {
case map[string]any:
mm := in
m.Content = &models.IntotoV002SchemaContent{Envelope: &models.IntotoV002SchemaContentEnvelope{}}
if c, ok := mm["content"].(map[string]any); ok {
if env, ok := c["envelope"].(map[string]any); ok {
if pt, ok := env["payloadType"].(string); ok {
m.Content.Envelope.PayloadType = &pt
}
if p, ok := env["payload"].(string); ok && p != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(p)))
n, err := base64.StdEncoding.Decode(outb, []byte(p))
if err != nil {
return fmt.Errorf("failed parsing base64 data for payload: %w", err)
}
m.Content.Envelope.Payload = strfmt.Base64(outb[:n])
}
if raw, ok := env["signatures"]; ok {
switch sigs := raw.(type) {
case []any:
m.Content.Envelope.Signatures = make([]*models.IntotoV002SchemaContentEnvelopeSignaturesItems0, 0, len(sigs))
for _, s := range sigs {
if sm, ok := s.(map[string]any); ok {
item := &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{}
if kid, ok := sm["keyid"].(string); ok {
item.Keyid = kid
}
if sig, ok := sm["sig"].(string); ok {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(sig)))
n, err := base64.StdEncoding.Decode(outb, []byte(sig))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature: %w", err)
}
b := strfmt.Base64(outb[:n])
item.Sig = &b
}
if pk, ok := sm["publicKey"].(string); ok {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(pk)))
n, err := base64.StdEncoding.Decode(outb, []byte(pk))
if err != nil {
return fmt.Errorf("failed parsing base64 data for public key: %w", err)
}
b := strfmt.Base64(outb[:n])
item.PublicKey = &b
}
m.Content.Envelope.Signatures = append(m.Content.Envelope.Signatures, item)
}
}
case []map[string]any:
m.Content.Envelope.Signatures = make([]*models.IntotoV002SchemaContentEnvelopeSignaturesItems0, 0, len(sigs))
for _, sm := range sigs {
item := &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{}
if kid, ok := sm["keyid"].(string); ok {
item.Keyid = kid
}
if sig, ok := sm["sig"].(string); ok {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(sig)))
n, err := base64.StdEncoding.Decode(outb, []byte(sig))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature: %w", err)
}
b := strfmt.Base64(outb[:n])
item.Sig = &b
}
if pk, ok := sm["publicKey"].(string); ok {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(pk)))
n, err := base64.StdEncoding.Decode(outb, []byte(pk))
if err != nil {
return fmt.Errorf("failed parsing base64 data for public key: %w", err)
}
b := strfmt.Base64(outb[:n])
item.PublicKey = &b
}
m.Content.Envelope.Signatures = append(m.Content.Envelope.Signatures, item)
}
}
}
}
if h, ok := c["hash"].(map[string]any); ok {
m.Content.Hash = &models.IntotoV002SchemaContentHash{}
if alg, ok := h["algorithm"].(string); ok {
m.Content.Hash.Algorithm = &alg
}
if val, ok := h["value"].(string); ok {
m.Content.Hash.Value = &val
}
}
if ph, ok := c["payloadHash"].(map[string]any); ok {
m.Content.PayloadHash = &models.IntotoV002SchemaContentPayloadHash{}
if alg, ok := ph["algorithm"].(string); ok {
m.Content.PayloadHash.Algorithm = &alg
}
if val, ok := ph["value"].(string); ok {
m.Content.PayloadHash.Value = &val
}
}
}
*output = m
return nil
case *models.IntotoV002Schema:
if in == nil {
return fmt.Errorf("nil *models.IntotoV002Schema")
}
*output = *in
return nil
case models.IntotoV002Schema:
*output = in
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V002Entry) Unmarshal(pe models.ProposedEntry) error {
it, ok := pe.(*models.Intoto)
if !ok {
return errors.New("cannot unmarshal non Intoto v0.0.2 type")
}
var err error
if err := DecodeEntry(it.Spec, &v.IntotoObj); err != nil {
return err
}
// field validation
if err := v.IntotoObj.Validate(strfmt.Default); err != nil {
return err
}
if string(v.IntotoObj.Content.Envelope.Payload) == "" {
return nil
}
env := &dsse.Envelope{
Payload: string(v.IntotoObj.Content.Envelope.Payload),
PayloadType: *v.IntotoObj.Content.Envelope.PayloadType,
}
allPubKeyBytes := make([][]byte, 0)
for i, sig := range v.IntotoObj.Content.Envelope.Signatures {
if sig == nil {
v.IntotoObj.Content.Envelope.Signatures = slices.Delete(v.IntotoObj.Content.Envelope.Signatures, i, i)
continue
}
env.Signatures = append(env.Signatures, dsse.Signature{
KeyID: sig.Keyid,
Sig: string(*sig.Sig),
})
allPubKeyBytes = append(allPubKeyBytes, *sig.PublicKey)
}
if _, err := verifyEnvelope(allPubKeyBytes, env); err != nil {
return err
}
v.env = *env
decodedPayload, err := base64.StdEncoding.DecodeString(string(v.IntotoObj.Content.Envelope.Payload))
if err != nil {
return fmt.Errorf("could not decode envelope payload: %w", err)
}
h := sha256.Sum256(decodedPayload)
v.IntotoObj.Content.PayloadHash = &models.IntotoV002SchemaContentPayloadHash{
Algorithm: conv.Pointer(models.IntotoV002SchemaContentPayloadHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(h[:])),
}
return nil
}
func (v *V002Entry) Canonicalize(_ context.Context) ([]byte, error) {
if err := v.IntotoObj.Validate(strfmt.Default); err != nil {
return nil, err
}
if v.IntotoObj.Content.Hash == nil {
return nil, errors.New("missing envelope digest")
}
if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil {
return nil, fmt.Errorf("error validating envelope digest: %w", err)
}
if v.IntotoObj.Content.PayloadHash == nil {
return nil, errors.New("missing payload digest")
}
if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil {
return nil, fmt.Errorf("error validating payload digest: %w", err)
}
if len(v.IntotoObj.Content.Envelope.Signatures) == 0 {
return nil, errors.New("missing signatures")
}
canonicalEntry := models.IntotoV002Schema{
Content: &models.IntotoV002SchemaContent{
Envelope: &models.IntotoV002SchemaContentEnvelope{
PayloadType: v.IntotoObj.Content.Envelope.PayloadType,
Signatures: v.IntotoObj.Content.Envelope.Signatures,
},
Hash: v.IntotoObj.Content.Hash,
PayloadHash: v.IntotoObj.Content.PayloadHash,
},
}
itObj := models.Intoto{}
itObj.APIVersion = conv.Pointer(APIVERSION)
itObj.Spec = &canonicalEntry
return json.Marshal(&itObj)
}
// AttestationKey returns the digest of the attestation that was uploaded, to be used to lookup the attestation from storage
func (v *V002Entry) AttestationKey() string {
if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil {
return fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)
}
return ""
}
// AttestationKeyValue returns both the key and value to be persisted into attestation storage
func (v *V002Entry) AttestationKeyValue() (string, []byte) {
storageSize := base64.StdEncoding.DecodedLen(len(v.env.Payload))
if storageSize > viper.GetInt("max_attestation_size") {
log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size"))
return "", nil
}
attBytes, err := base64.StdEncoding.DecodeString(v.env.Payload)
if err != nil {
log.Logger.Infof("could not decode envelope payload: %w", err)
return "", nil
}
return v.AttestationKey(), attBytes
}
type verifier struct {
s signature.Signer
v signature.Verifier
}
func (v *verifier) KeyID() (string, error) {
return "", nil
}
func (v *verifier) Public() crypto.PublicKey {
// the dsse library uses this to generate a key ID if the KeyID function returns an empty string
// as well for the AcceptedKey return value. Unfortunately since key ids can be arbitrary, we don't
// know how to generate a matching id for the key id on the envelope's signature...
// dsse verify will skip verifiers whose key id doesn't match the signature's key id, unless it fails
// to generate one from the public key... so we trick it by returning nil ¯\_(ツ)_/¯
return nil
}
func (v *verifier) Sign(_ context.Context, data []byte) (sig []byte, err error) {
if v.s == nil {
return nil, errors.New("nil signer")
}
sig, err = v.s.SignMessage(bytes.NewReader(data), options.WithCryptoSignerOpts(crypto.SHA256))
if err != nil {
return nil, err
}
return sig, nil
}
func (v *verifier) Verify(_ context.Context, data, sig []byte) error {
if v.v == nil {
return errors.New("nil verifier")
}
return v.v.VerifySignature(bytes.NewReader(sig), bytes.NewReader(data))
}
func (v V002Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Intoto{}
re := V002Entry{
IntotoObj: models.IntotoV002Schema{
Content: &models.IntotoV002SchemaContent{
Envelope: &models.IntotoV002SchemaContentEnvelope{},
},
}}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
return nil, errors.New("intoto envelopes cannot be fetched over HTTP(S)")
}
artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, err
}
}
env := dsse.Envelope{}
if err := json.Unmarshal(artifactBytes, &env); err != nil {
return nil, fmt.Errorf("payload must be a valid dsse envelope: %w", err)
}
allPubKeyBytes := make([][]byte, 0)
if len(props.PublicKeyBytes) > 0 {
allPubKeyBytes = append(allPubKeyBytes, props.PublicKeyBytes...)
}
if len(props.PublicKeyPaths) > 0 {
for _, path := range props.PublicKeyPaths {
if path.IsAbs() {
return nil, errors.New("dsse public keys cannot be fetched over HTTP(S)")
}
publicKeyBytes, err := os.ReadFile(filepath.Clean(path.Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
allPubKeyBytes = append(allPubKeyBytes, publicKeyBytes)
}
}
keysBySig, err := verifyEnvelope(allPubKeyBytes, &env)
if err != nil {
return nil, err
}
b64 := strfmt.Base64([]byte(env.Payload))
re.IntotoObj.Content.Envelope.Payload = b64
re.IntotoObj.Content.Envelope.PayloadType = &env.PayloadType
for _, sig := range env.Signatures {
key, ok := keysBySig[sig.Sig]
if !ok {
return nil, errors.New("all signatures must have a key that verifies it")
}
canonKey, err := key.CanonicalValue()
if err != nil {
return nil, fmt.Errorf("could not canonicize key: %w", err)
}
keyBytes := strfmt.Base64(canonKey)
sigBytes := strfmt.Base64([]byte(sig.Sig))
re.IntotoObj.Content.Envelope.Signatures = append(re.IntotoObj.Content.Envelope.Signatures, &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
Keyid: sig.KeyID,
Sig: &sigBytes,
PublicKey: &keyBytes,
})
}
h := sha256.Sum256([]byte(artifactBytes))
re.IntotoObj.Content.Hash = &models.IntotoV002SchemaContentHash{
Algorithm: conv.Pointer(models.IntotoV001SchemaContentHashAlgorithmSha256),
Value: conv.Pointer(hex.EncodeToString(h[:])),
}
returnVal.Spec = re.IntotoObj
returnVal.APIVersion = conv.Pointer(re.APIVersion())
return &returnVal, nil
}
// verifyEnvelope takes in an array of possible key bytes and attempts to parse them as x509 public keys.
// it then uses these to verify the envelope and makes sure that every signature on the envelope is verified.
// it returns a map of verifiers indexed by the signature the verifier corresponds to.
func verifyEnvelope(allPubKeyBytes [][]byte, env *dsse.Envelope) (map[string]*x509.PublicKey, error) {
// generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature
verifierBySig := make(map[string]*x509.PublicKey)
allSigs := make(map[string]struct{})
for _, sig := range env.Signatures {
allSigs[sig.Sig] = struct{}{}
}
for _, pubKeyBytes := range allPubKeyBytes {
key, err := x509.NewPublicKey(bytes.NewReader(pubKeyBytes))
if err != nil {
return nil, fmt.Errorf("could not parse public key as x509: %w", err)
}
vfr, err := signature.LoadVerifier(key.CryptoPubKey(), crypto.SHA256)
if err != nil {
return nil, fmt.Errorf("could not load verifier: %w", err)
}
dsseVfr, err := dsse.NewEnvelopeVerifier(&verifier{
v: vfr,
})
if err != nil {
return nil, fmt.Errorf("could not use public key as a dsse verifier: %w", err)
}
accepted, err := dsseVfr.Verify(context.Background(), env)
if err != nil {
return nil, fmt.Errorf("could not verify envelope: %w", err)
}
for _, accept := range accepted {
delete(allSigs, accept.Sig.Sig)
verifierBySig[accept.Sig.Sig] = key
}
}
if len(allSigs) > 0 {
return nil, errors.New("all signatures must have a key that verifies it")
}
return verifierBySig, nil
}
func (v V002Entry) Verifiers() ([]pki.PublicKey, error) {
if v.IntotoObj.Content == nil || v.IntotoObj.Content.Envelope == nil {
return nil, errors.New("intoto v0.0.2 entry not initialized")
}
sigs := v.IntotoObj.Content.Envelope.Signatures
if len(sigs) == 0 {
return nil, errors.New("no signatures found on intoto entry")
}
var keys []pki.PublicKey
for _, s := range v.IntotoObj.Content.Envelope.Signatures {
key, err := x509.NewPublicKey(bytes.NewReader(*s.PublicKey))
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
}
func (v V002Entry) ArtifactHash() (string, error) {
if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil {
return "", errors.New("intoto v0.0.2 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil
}
func (v V002Entry) Insertable() (bool, error) {
if v.IntotoObj.Content == nil {
return false, errors.New("missing content property")
}
if v.IntotoObj.Content.Envelope == nil {
return false, errors.New("missing envelope property")
}
if len(v.IntotoObj.Content.Envelope.Payload) == 0 {
return false, errors.New("missing envelope content")
}
if v.IntotoObj.Content.Envelope.PayloadType == nil || len(*v.IntotoObj.Content.Envelope.PayloadType) == 0 {
return false, errors.New("missing payloadType content")
}
if len(v.IntotoObj.Content.Envelope.Signatures) == 0 {
return false, errors.New("missing signatures content")
}
for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
if sig == nil {
return false, errors.New("missing signature entry")
}
if sig.Sig == nil || len(*sig.Sig) == 0 {
return false, errors.New("missing signature content")
}
if sig.PublicKey == nil || len(*sig.PublicKey) == 0 {
return false, errors.New("missing publicKey content")
}
}
if v.env.Payload == "" || v.env.PayloadType == "" || len(v.env.Signatures) == 0 {
return false, errors.New("invalid DSSE envelope")
}
return true, nil
}
//
// 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 jar
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "jar"
)
type BaseJARType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
bjt := BaseJARType{}
bjt.Kind = KIND
bjt.VersionMap = VersionMap
return &bjt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (bjt *BaseJARType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
jar, ok := pe.(*models.Jar)
if !ok {
return nil, errors.New("cannot unmarshal non-JAR types")
}
return bjt.VersionedUnmarshal(jar, *jar.APIVersion)
}
func (bjt *BaseJARType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = bjt.DefaultVersion()
}
ei, err := bjt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching JAR version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (bjt BaseJARType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 jar
import (
"archive/zip"
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/pkcs7"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/jar"
"github.com/sigstore/rekor/pkg/util"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
jarutils "github.com/sassoftware/relic/lib/signjar"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/spf13/viper"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := jar.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
JARModel models.JarV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v *V001Entry) IndexKeys() ([]string, error) {
var result []string
keyObj, err := pkcs7.NewSignature(bytes.NewReader(v.JARModel.Signature.Content))
if err != nil {
return nil, err
}
key, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
if v.JARModel.Archive.Hash != nil {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.JARModel.Archive.Hash.Algorithm, *v.JARModel.Archive.Hash.Value))
result = append(result, hashKey)
}
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
jar, ok := pe.(*models.Jar)
if !ok {
return errors.New("cannot unmarshal non JAR v0.0.1 type")
}
if err := DecodeEntry(jar.Spec, &v.JARModel); err != nil {
return err
}
// field validation
if err := v.JARModel.Validate(strfmt.Default); err != nil {
return err
}
return v.validate()
}
// DecodeEntry performs direct decode into the provided output pointer
// without mutating the receiver on error.
func DecodeEntry(input any, output *models.JarV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.JarV001Schema")
}
var m models.JarV001Schema
switch data := input.(type) {
case map[string]any:
mm := data
if sig, ok := mm["signature"].(map[string]any); ok {
m.Signature = &models.JarV001SchemaSignature{}
if c, ok := sig["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature content: %w", err)
}
m.Signature.Content = strfmt.Base64(outb[:n])
}
if pk, ok := sig["publicKey"].(map[string]any); ok {
m.Signature.PublicKey = &models.JarV001SchemaSignaturePublicKey{}
if c, ok := pk["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature publicKey content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.Signature.PublicKey.Content = &b
}
}
}
if ar, ok := mm["archive"].(map[string]any); ok {
m.Archive = &models.JarV001SchemaArchive{}
if h, ok := ar["hash"].(map[string]any); ok {
m.Archive.Hash = &models.JarV001SchemaArchiveHash{}
if alg, ok := h["algorithm"].(string); ok {
m.Archive.Hash.Algorithm = &alg
}
if val, ok := h["value"].(string); ok {
m.Archive.Hash.Value = &val
}
}
if c, ok := ar["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for archive content: %w", err)
}
m.Archive.Content = strfmt.Base64(outb[:n])
}
}
*output = m
return nil
case *models.JarV001Schema:
if data == nil {
return fmt.Errorf("nil *models.JarV001Schema")
}
*output = *data
return nil
case models.JarV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (*pkcs7.PublicKey, *pkcs7.Signature, error) {
if err := v.validate(); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
oldSHA := ""
if v.JARModel.Archive.Hash != nil && v.JARModel.Archive.Hash.Value != nil {
oldSHA = conv.Value(v.JARModel.Archive.Hash.Value)
}
dataReadCloser := bytes.NewReader(v.JARModel.Archive.Content)
hasher := sha256.New()
b := &bytes.Buffer{}
n, err := io.Copy(io.MultiWriter(hasher, b), dataReadCloser)
if err != nil {
return nil, nil, err
}
computedSHA := hex.EncodeToString(hasher.Sum(nil))
if oldSHA != "" && computedSHA != oldSHA {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA)}
}
zipReader, err := zip.NewReader(bytes.NewReader(b.Bytes()), n)
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Checking that uncompressed metadata files are within acceptable bounds before reading into memory.
// Checks match those performed by the relic library in the jarutils.Verify method below. For example,
// the META-INF/MANIFEST.MF is read into memory by the relic lib, but a META-INF/LICENSE file is not.
for _, f := range zipReader.File {
dir, name := path.Split(strings.ToUpper(f.Name))
if dir != "META-INF/" || name == "" || strings.LastIndex(name, ".") < 0 {
continue
}
if f.UncompressedSize64 > viper.GetUint64("max_jar_metadata_size") && viper.GetUint64("max_jar_metadata_size") > 0 {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("uncompressed jar metadata of size %d exceeds max allowed size %d", f.UncompressedSize64, viper.GetUint64("max_jar_metadata_size"))}
}
}
// this ensures that the JAR is signed and the signature verifies, as
// well as checks that the hashes in the signed manifest are all valid
jarObjs, err := jarutils.Verify(zipReader, false)
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
switch len(jarObjs) {
case 0:
return nil, nil, &types.InputValidationError{Err: errors.New("no signatures detected in JAR archive")}
case 1:
default:
return nil, nil, &types.InputValidationError{Err: errors.New("multiple signatures detected in JAR; unable to process")}
}
// we need to find and extract the PKCS7 bundle from the JAR file manually
sigPKCS7, err := extractPKCS7SignatureFromJAR(zipReader)
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
keyObj, err := pkcs7.NewPublicKey(bytes.NewReader(sigPKCS7))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
sigObj, err := pkcs7.NewSignature(bytes.NewReader(sigPKCS7))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// if we get here, all goroutines succeeded without error
if oldSHA == "" {
v.JARModel.Archive.Hash = &models.JarV001SchemaArchiveHash{
Algorithm: conv.Pointer(models.JarV001SchemaArchiveHashAlgorithmSha256),
Value: conv.Pointer(computedSHA),
}
}
return keyObj, sigObj, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
keyObj, sigObj, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
// need to canonicalize key content
keyContent, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
sigContent, err := sigObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry := models.JarV001Schema{
Signature: &models.JarV001SchemaSignature{
PublicKey: &models.JarV001SchemaSignaturePublicKey{
Content: (*strfmt.Base64)(&keyContent),
},
Content: sigContent,
},
Archive: &models.JarV001SchemaArchive{
Hash: &models.JarV001SchemaArchiveHash{
Algorithm: v.JARModel.Archive.Hash.Algorithm,
Value: v.JARModel.Archive.Hash.Value,
},
},
}
// archive content is not set deliberately
v.JARModel = canonicalEntry
// wrap in valid object with kind and apiVersion set
jar := models.Jar{}
jar.APIVersion = conv.Pointer(APIVERSION)
jar.Spec = &canonicalEntry
return json.Marshal(&jar)
}
// validate performs cross-field validation for fields in object
func (v *V001Entry) validate() error {
archive := v.JARModel.Archive
if archive == nil {
return errors.New("missing package")
}
// if the signature isn't present, then we need content to extract
if v.JARModel.Signature == nil || v.JARModel.Signature.Content == nil {
if len(archive.Content) == 0 {
return errors.New("'content' must be specified for package")
}
}
hash := archive.Hash
if hash != nil {
// Only sha256 is supported for jar v0.0.1; enforce length-by-algorithm like hashedrekord.
if hash.Value == nil || len(*hash.Value) != crypto.SHA256.Size()*2 {
return errors.New("invalid value for hash")
}
if _, err := hex.DecodeString(*hash.Value); err != nil {
return errors.New("invalid value for hash")
}
}
return nil
}
// extractPKCS7SignatureFromJAR extracts the first signature file from the JAR and returns it
func extractPKCS7SignatureFromJAR(inz *zip.Reader) ([]byte, error) {
for _, f := range inz.File {
dir, name := path.Split(strings.ToUpper(f.Name))
if dir != "META-INF/" || name == "" {
continue
}
i := strings.LastIndex(name, ".")
if i < 0 {
continue
}
fileExt := name[i:]
if fileExt == ".RSA" || fileExt == ".DSA" || fileExt == ".EC" || strings.HasPrefix(name, "SIG-") {
fileReader, err := f.Open()
if err != nil {
return nil, err
}
contents, err := io.ReadAll(fileReader)
if err != nil {
return nil, err
}
if err = fileReader.Close(); err != nil {
return nil, err
}
return contents, nil
}
}
return nil, errors.New("unable to locate signature in JAR file")
}
func (v *V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Jar{}
re := V001Entry{}
// we will need only the artifact; public-key & signature are embedded in JAR
re.JARModel = models.JarV001Schema{}
re.JARModel.Archive = &models.JarV001SchemaArchive{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading JAR file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening JAR file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading JAR file: %w", err)
}
}
re.JARModel.Archive.Content = (strfmt.Base64)(artifactBytes)
if err := re.validate(); err != nil {
return nil, err
}
if _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.JARModel
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.JARModel.Signature == nil || v.JARModel.Signature.PublicKey == nil || v.JARModel.Signature.PublicKey.Content == nil {
return nil, errors.New("jar v0.0.1 entry not initialized")
}
key, err := x509.NewPublicKey(bytes.NewReader(*v.JARModel.Signature.PublicKey.Content))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.JARModel.Archive == nil || v.JARModel.Archive.Hash == nil || v.JARModel.Archive.Hash.Value == nil || v.JARModel.Archive.Hash.Algorithm == nil {
return "", errors.New("jar v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.JARModel.Archive.Hash.Algorithm, *v.JARModel.Archive.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.JARModel.Archive == nil {
return false, errors.New("missing archive property")
}
if len(v.JARModel.Archive.Content) == 0 {
return false, errors.New("missing archive content")
}
return true, nil
}
//
// 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 rekord
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "rekord"
)
type BaseRekordType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
brt := BaseRekordType{}
brt.Kind = KIND
brt.VersionMap = VersionMap
return &brt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (rt BaseRekordType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
rekord, ok := pe.(*models.Rekord)
if !ok {
return nil, errors.New("cannot unmarshal non-Rekord types")
}
return rt.VersionedUnmarshal(rekord, *rekord.APIVersion)
}
func (rt *BaseRekordType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = rt.DefaultVersion()
}
ei, err := rt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching Rekord version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (rt BaseRekordType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 rekord
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/minisign"
"github.com/sigstore/rekor/pkg/pki/pgp"
"github.com/sigstore/rekor/pkg/pki/ssh"
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/rekord"
"github.com/sigstore/rekor/pkg/util"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := rekord.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
RekordObj models.RekordV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
af, err := pki.NewArtifactFactory(pki.Format(*v.RekordObj.Signature.Format))
if err != nil {
return nil, err
}
keyObj, err := af.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
if err != nil {
return nil, err
}
key, err := keyObj.CanonicalValue()
if err != nil {
log.Logger.Error(err)
} else {
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
}
result = append(result, keyObj.Subjects()...)
if v.RekordObj.Data.Hash != nil {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.RekordObj.Data.Hash.Algorithm, *v.RekordObj.Data.Hash.Value))
result = append(result, hashKey)
}
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
rekord, ok := pe.(*models.Rekord)
if !ok {
return errors.New("cannot unmarshal non Rekord v0.0.1 type")
}
if err := DecodeEntry(rekord.Spec, &v.RekordObj); err != nil {
return err
}
// field validation
if err := v.RekordObj.Validate(strfmt.Default); err != nil {
return err
}
// cross field validation
return v.validate()
}
// DecodeEntry performs direct JSON unmarshaling without reflection,
// equivalent to types.DecodeEntry but with better performance for Rekord v0.0.1.
// It avoids mutating the receiver on error.
func DecodeEntry(input any, output *models.RekordV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.RekordV001Schema")
}
var m models.RekordV001Schema
// Single switch including map[string]any fast path
switch data := input.(type) {
case map[string]any:
mm := data
if s, ok := mm["signature"].(map[string]any); ok {
m.Signature = &models.RekordV001SchemaSignature{}
if f, ok := s["format"].(string); ok {
m.Signature.Format = &f
}
if c, ok := s["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.Signature.Content = &b
}
if pk, ok := s["publicKey"].(map[string]any); ok {
m.Signature.PublicKey = &models.RekordV001SchemaSignaturePublicKey{}
if c, ok := pk["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for signature publicKey content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.Signature.PublicKey.Content = &b
}
}
}
if d, ok := mm["data"].(map[string]any); ok {
m.Data = &models.RekordV001SchemaData{}
if h, ok := d["hash"].(map[string]any); ok {
m.Data.Hash = &models.RekordV001SchemaDataHash{}
if alg, ok := h["algorithm"].(string); ok {
m.Data.Hash.Algorithm = &alg
}
if val, ok := h["value"].(string); ok {
m.Data.Hash.Value = &val
}
}
if c, ok := d["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for data content: %w", err)
}
m.Data.Content = strfmt.Base64(outb[:n])
}
}
*output = m
return nil
case *models.RekordV001Schema:
if data == nil {
return fmt.Errorf("nil *models.RekordV001Schema")
}
*output = *data
return nil
case models.RekordV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (pki.PublicKey, pki.Signature, error) {
af, err := pki.NewArtifactFactory(pki.Format(*v.RekordObj.Signature.Format))
if err != nil {
return nil, nil, err
}
// Hash computation
hasher := sha256.New()
if _, err := hasher.Write(v.RekordObj.Data.Content); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
computedSHA := hex.EncodeToString(hasher.Sum(nil))
// Validate hash if provided
if v.RekordObj.Data.Hash != nil && v.RekordObj.Data.Hash.Value != nil {
oldSHA := conv.Value(v.RekordObj.Data.Hash.Value)
if computedSHA != oldSHA {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA)}
}
}
// Parse signature and key
sigObj, err := af.NewSignature(bytes.NewReader(*v.RekordObj.Signature.Content))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
keyObj, err := af.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Verify signature
if err := sigObj.Verify(bytes.NewReader(v.RekordObj.Data.Content), keyObj); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Set computed hash if not provided
if v.RekordObj.Data.Hash == nil {
v.RekordObj.Data.Hash = &models.RekordV001SchemaDataHash{}
v.RekordObj.Data.Hash.Algorithm = conv.Pointer(models.RekordV001SchemaDataHashAlgorithmSha256)
v.RekordObj.Data.Hash.Value = conv.Pointer(computedSHA)
}
return keyObj, sigObj, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
keyObj, sigObj, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
canonicalEntry := models.RekordV001Schema{}
// need to canonicalize signature & key content
canonicalEntry.Signature = &models.RekordV001SchemaSignature{}
// signature URL (if known) is not set deliberately
canonicalEntry.Signature.Format = v.RekordObj.Signature.Format
var sigContent []byte
sigContent, err = sigObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.Signature.Content = (*strfmt.Base64)(&sigContent)
var pubKeyContent []byte
canonicalEntry.Signature.PublicKey = &models.RekordV001SchemaSignaturePublicKey{}
pubKeyContent, err = keyObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.Signature.PublicKey.Content = (*strfmt.Base64)(&pubKeyContent)
canonicalEntry.Data = &models.RekordV001SchemaData{}
canonicalEntry.Data.Hash = v.RekordObj.Data.Hash
// data content is not set deliberately
// wrap in valid object with kind and apiVersion set
rekordObj := models.Rekord{}
rekordObj.APIVersion = conv.Pointer(APIVERSION)
rekordObj.Spec = &canonicalEntry
v.RekordObj = canonicalEntry
bytes, err := json.Marshal(&rekordObj)
if err != nil {
return nil, err
}
return bytes, nil
}
// validate performs cross-field validation for fields in object
func (v V001Entry) validate() error {
sig := v.RekordObj.Signature
if v.RekordObj.Signature == nil {
return errors.New("missing signature")
}
if sig.Content == nil || len(*sig.Content) == 0 {
return errors.New("'content' must be specified for signature")
}
key := sig.PublicKey
if key == nil {
return errors.New("missing public key")
}
if key.Content == nil || len(*key.Content) == 0 {
return errors.New("'content' must be specified for publicKey")
}
data := v.RekordObj.Data
if data == nil {
return errors.New("missing data")
}
hash := data.Hash
if hash != nil {
// Rekord v0.0.1 schema enumerates sha256; enforce length accordingly.
if hash.Value == nil || len(*hash.Value) != crypto.SHA256.Size()*2 {
return errors.New("invalid value for hash")
}
if _, err := hex.DecodeString(*hash.Value); err != nil {
return errors.New("invalid value for hash")
}
} else if len(data.Content) == 0 {
return errors.New("'content' must be specified for data")
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Rekord{}
re := V001Entry{}
// we will need artifact, public-key, signature
re.RekordObj.Data = &models.RekordV001SchemaData{}
var err error
artifactBytes := props.ArtifactBytes
if len(artifactBytes) == 0 {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening artifact file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
}
re.RekordObj.Data.Content = strfmt.Base64(artifactBytes)
re.RekordObj.Signature = &models.RekordV001SchemaSignature{}
switch props.PKIFormat {
case "pgp":
re.RekordObj.Signature.Format = conv.Pointer(models.RekordV001SchemaSignatureFormatPgp)
case "minisign":
re.RekordObj.Signature.Format = conv.Pointer(models.RekordV001SchemaSignatureFormatMinisign)
case "x509":
re.RekordObj.Signature.Format = conv.Pointer(models.RekordV001SchemaSignatureFormatX509)
case "ssh":
re.RekordObj.Signature.Format = conv.Pointer(models.RekordV001SchemaSignatureFormatSSH)
default:
return nil, fmt.Errorf("unexpected format of public key: %s", props.PKIFormat)
}
sigBytes := props.SignatureBytes
if len(sigBytes) == 0 {
if props.SignaturePath == nil {
return nil, errors.New("a detached signature must be provided")
}
sigBytes, err = os.ReadFile(filepath.Clean(props.SignaturePath.Path))
if err != nil {
return nil, fmt.Errorf("error reading signature file: %w", err)
}
re.RekordObj.Signature.Content = (*strfmt.Base64)(&sigBytes)
} else {
re.RekordObj.Signature.Content = (*strfmt.Base64)(&sigBytes)
}
re.RekordObj.Signature.PublicKey = &models.RekordV001SchemaSignaturePublicKey{}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided to verify detached signature")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
re.RekordObj.Signature.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0])
if err := re.validate(); err != nil {
return nil, err
}
if _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.RekordObj
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.RekordObj.Signature == nil || v.RekordObj.Signature.PublicKey == nil || v.RekordObj.Signature.PublicKey.Content == nil {
return nil, errors.New("rekord v0.0.1 entry not initialized")
}
var key pki.PublicKey
var err error
switch f := *v.RekordObj.Signature.Format; f {
case "x509":
key, err = x509.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
case "ssh":
key, err = ssh.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
case "pgp":
key, err = pgp.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
case "minisign":
key, err = minisign.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content))
default:
return nil, fmt.Errorf("unexpected format of public key: %s", f)
}
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.RekordObj.Data == nil || v.RekordObj.Data.Hash == nil || v.RekordObj.Data.Hash.Value == nil || v.RekordObj.Data.Hash.Algorithm == nil {
return "", errors.New("rekord v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.RekordObj.Data.Hash.Algorithm, *v.RekordObj.Data.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.RekordObj.Signature == nil {
return false, errors.New("missing signature property")
}
if v.RekordObj.Signature.Content == nil || len(*v.RekordObj.Signature.Content) == 0 {
return false, errors.New("missing signature content")
}
if v.RekordObj.Signature.PublicKey == nil {
return false, errors.New("missing publicKey property")
}
if v.RekordObj.Signature.PublicKey.Content == nil || len(*v.RekordObj.Signature.PublicKey.Content) == 0 {
return false, errors.New("missing publicKey content")
}
if v.RekordObj.Signature.Format == nil || len(*v.RekordObj.Signature.Format) == 0 {
return false, errors.New("missing signature format")
}
if v.RekordObj.Data == nil {
return false, errors.New("missing data property")
}
if len(v.RekordObj.Data.Content) == 0 {
return false, errors.New("missing data content")
}
return true, nil
}
//
// 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 rfc3161
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "rfc3161"
)
type BaseTimestampType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
btt := BaseTimestampType{}
btt.Kind = KIND
btt.VersionMap = VersionMap
return &btt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (btt BaseTimestampType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
rfc3161, ok := pe.(*models.Rfc3161)
if !ok {
return nil, errors.New("cannot unmarshal non-Timestamp types")
}
return btt.VersionedUnmarshal(rfc3161, *rfc3161.APIVersion)
}
func (btt *BaseTimestampType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = btt.DefaultVersion()
}
ei, err := btt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching RFC3161 version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (btt BaseTimestampType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 rfc3161
import (
"context"
"crypto/sha256"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/types/rfc3161"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sassoftware/relic/lib/pkcs9"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/types"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := rfc3161.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
Rfc3161Obj models.Rfc3161V001Schema
tsrContent *strfmt.Base64
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func NewEntryFromBytes(timestamp []byte) models.ProposedEntry {
b64 := strfmt.Base64(timestamp)
re := V001Entry{
Rfc3161Obj: models.Rfc3161V001Schema{
Tsr: &models.Rfc3161V001SchemaTsr{
Content: &b64,
},
},
}
return &models.Rfc3161{
Spec: re.Rfc3161Obj,
APIVersion: conv.Pointer(re.APIVersion()),
}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
str := v.Rfc3161Obj.Tsr.Content.String()
tb, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return nil, err
}
h := sha256.Sum256(tb)
hx := hex.EncodeToString(h[:])
payloadKey := "sha256:" + hx
result = append(result, payloadKey)
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
rfc3161Resp, ok := pe.(*models.Rfc3161)
if !ok {
return errors.New("cannot unmarshal non Rfc3161 v0.0.1 type")
}
if err := DecodeEntry(rfc3161Resp.Spec, &v.Rfc3161Obj); err != nil {
return err
}
// field validation
if err := v.Rfc3161Obj.Validate(strfmt.Default); err != nil {
return err
}
if err := v.validate(); err != nil {
return err
}
v.tsrContent = v.Rfc3161Obj.Tsr.Content
return nil
}
// DecodeEntry decodes input into provided output pointer without mutating receiver on error
func DecodeEntry(input any, output *models.Rfc3161V001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.Rfc3161V001Schema")
}
var m models.Rfc3161V001Schema
switch data := input.(type) {
case map[string]any:
if tsr, ok := data["tsr"].(map[string]any); ok {
m.Tsr = &models.Rfc3161V001SchemaTsr{}
if c, ok := tsr["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for tsr content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.Tsr.Content = &b
}
}
*output = m
return nil
case *models.Rfc3161V001Schema:
if data == nil {
return fmt.Errorf("nil *models.Rfc3161V001Schema")
}
*output = *data
return nil
case models.Rfc3161V001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) {
if v.tsrContent == nil {
return nil, &types.InputValidationError{Err: errors.New("tsr content must be set before canonicalizing")}
}
canonicalEntry := models.Rfc3161V001Schema{
Tsr: &models.Rfc3161V001SchemaTsr{
Content: v.tsrContent,
},
}
// wrap in valid object with kind and apiVersion set
ref3161Obj := models.Rfc3161{}
ref3161Obj.APIVersion = conv.Pointer(APIVERSION)
ref3161Obj.Spec = &canonicalEntry
return json.Marshal(&ref3161Obj)
}
// validate performs cross-field validation for fields in object
func (v V001Entry) validate() error {
data := v.Rfc3161Obj.Tsr
if data == nil {
return errors.New("missing tsr data")
}
content := *data.Content
if len(content) == 0 {
return errors.New("'content' must be specified for data")
}
b, err := base64.StdEncoding.DecodeString(content.String())
if err != nil {
return err
}
if len(b) > (10 * 1024) {
return fmt.Errorf("tsr exceeds maximum allowed size (10kB)")
}
var tsr pkcs9.TimeStampResp
_, err = asn1.Unmarshal(b, &tsr)
if err != nil {
return err
}
if tsr.Status.Status != pkcs9.StatusGranted && tsr.Status.Status != pkcs9.StatusGrantedWithMods {
return fmt.Errorf("tsr status not granted: %v", tsr.Status.Status)
}
if !tsr.TimeStampToken.ContentType.Equal(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}) {
return fmt.Errorf("tsr wrong content type: %v", tsr.TimeStampToken.ContentType)
}
_, err = tsr.TimeStampToken.Content.Verify(nil, false)
if err != nil {
return fmt.Errorf("tsr verification error: %w", err)
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Rfc3161{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
return nil, errors.New("RFC3161 timestamps cannot be fetched over HTTP(S)")
}
artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error reading artifact file: %w", err)
}
}
b64 := strfmt.Base64(artifactBytes)
re := V001Entry{
Rfc3161Obj: models.Rfc3161V001Schema{
Tsr: &models.Rfc3161V001SchemaTsr{
Content: &b64,
},
},
}
returnVal.Spec = re.Rfc3161Obj
returnVal.APIVersion = conv.Pointer(re.APIVersion())
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
return nil, errors.New("Verifiers() does not support rfc3161 entry type")
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.Rfc3161Obj.Tsr == nil || v.Rfc3161Obj.Tsr.Content == nil {
return "", errors.New("rfc3161 v0.0.1 entry not initialized")
}
tsrDecoded, err := base64.StdEncoding.DecodeString(v.Rfc3161Obj.Tsr.Content.String())
if err != nil {
return "", err
}
h := sha256.Sum256(tsrDecoded)
return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(h[:]))), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.Rfc3161Obj.Tsr == nil {
return false, errors.New("missing tsr property")
}
if v.Rfc3161Obj.Tsr.Content == nil || len(*v.Rfc3161Obj.Tsr.Content) == 0 {
return false, errors.New("missing tsr content")
}
if v.tsrContent == nil || len(*v.tsrContent) == 0 {
return false, errors.New("timestamp response has not been parsed")
}
return true, nil
}
//
// 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 rpm
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
)
const (
KIND = "rpm"
)
type BaseRPMType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
brt := BaseRPMType{}
brt.Kind = KIND
brt.VersionMap = VersionMap
return &brt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (brt *BaseRPMType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
rpm, ok := pe.(*models.Rpm)
if !ok {
return nil, errors.New("cannot unmarshal non-RPM types")
}
return brt.VersionedUnmarshal(rpm, *rpm.APIVersion)
}
func (brt *BaseRPMType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = brt.DefaultVersion()
}
ei, err := brt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching RPM version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (brt BaseRPMType) DefaultVersion() string {
return "0.0.1"
}
//
// 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 rpm
import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
rpmutils "github.com/cavaliercoder/go-rpm"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/pki/pgp"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/rpm"
"github.com/sigstore/rekor/pkg/util"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := rpm.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type V001Entry struct {
RPMModel models.RpmV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.RPMModel.PublicKey.Content))
if err != nil {
return nil, err
}
key, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
keyHash := sha256.Sum256(key)
result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:])))
result = append(result, keyObj.Subjects()...)
if v.RPMModel.Package.Hash != nil {
hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.RPMModel.Package.Hash.Algorithm, *v.RPMModel.Package.Hash.Value))
result = append(result, hashKey)
}
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
rpm, ok := pe.(*models.Rpm)
if !ok {
return errors.New("cannot unmarshal non RPM v0.0.1 type")
}
if err := DecodeEntry(rpm.Spec, &v.RPMModel); err != nil {
return err
}
// field validation
if err := v.RPMModel.Validate(strfmt.Default); err != nil {
return err
}
return v.validate()
}
// DecodeEntry decodes input into provided output pointer without mutating receiver on error
func DecodeEntry(input any, output *models.RpmV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.RpmV001Schema")
}
var m models.RpmV001Schema
// Single switch including map[string]any fast path
switch data := input.(type) {
case map[string]any:
mm := data
if pk, ok := mm["publicKey"].(map[string]any); ok {
m.PublicKey = &models.RpmV001SchemaPublicKey{}
if c, ok := pk["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for publicKey content: %w", err)
}
b := strfmt.Base64(outb[:n])
m.PublicKey.Content = &b
}
}
if pkgRaw, ok := mm["package"].(map[string]any); ok {
m.Package = &models.RpmV001SchemaPackage{}
if hdrs, ok := pkgRaw["headers"].(map[string]any); ok {
m.Package.Headers = make(map[string]string, len(hdrs))
for k, v2 := range hdrs {
if s, ok := v2.(string); ok {
m.Package.Headers[k] = s
}
}
}
if hRaw, ok := pkgRaw["hash"].(map[string]any); ok {
m.Package.Hash = &models.RpmV001SchemaPackageHash{}
if alg, ok := hRaw["algorithm"].(string); ok {
m.Package.Hash.Algorithm = &alg
}
if val, ok := hRaw["value"].(string); ok {
m.Package.Hash.Value = &val
}
}
if c, ok := pkgRaw["content"].(string); ok && c != "" {
outb := make([]byte, base64.StdEncoding.DecodedLen(len(c)))
n, err := base64.StdEncoding.Decode(outb, []byte(c))
if err != nil {
return fmt.Errorf("failed parsing base64 data for package content: %w", err)
}
m.Package.Content = strfmt.Base64(outb[:n])
}
}
*output = m
return nil
case *models.RpmV001Schema:
if data == nil {
return fmt.Errorf("nil *models.RpmV001Schema")
}
*output = *data
return nil
case models.RpmV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (*pgp.PublicKey, *rpmutils.PackageFile, error) {
if err := v.validate(); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
oldSHA := ""
if v.RPMModel.Package.Hash != nil && v.RPMModel.Package.Hash.Value != nil {
oldSHA = conv.Value(v.RPMModel.Package.Hash.Value)
}
// Parse public key
keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.RPMModel.PublicKey.Content))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Prepare package data and verify hash
dataReadCloser := bytes.NewReader(v.RPMModel.Package.Content)
hasher := sha256.New()
// Create buffers for signature verification and RPM parsing
sigBuffer := bytes.NewBuffer(nil)
rpmBuffer := bytes.NewBuffer(nil)
/* #nosec G110 */
if _, err := io.Copy(io.MultiWriter(hasher, sigBuffer, rpmBuffer), dataReadCloser); err != nil {
return nil, nil, err
}
computedSHA := hex.EncodeToString(hasher.Sum(nil))
if oldSHA != "" && computedSHA != oldSHA {
return nil, nil, &types.InputValidationError{Err: fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA)}
}
// Verify GPG signature
keyring, err := keyObj.KeyRing()
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
if _, err := rpmutils.GPGCheck(sigBuffer, keyring); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Parse RPM package
rpmObj, err := rpmutils.ReadPackageFile(rpmBuffer)
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Set hash if not provided
if oldSHA == "" {
v.RPMModel.Package.Hash = &models.RpmV001SchemaPackageHash{}
v.RPMModel.Package.Hash.Algorithm = conv.Pointer(models.RpmV001SchemaPackageHashAlgorithmSha256)
v.RPMModel.Package.Hash.Value = conv.Pointer(computedSHA)
}
return keyObj, rpmObj, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
keyObj, rpmObj, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
canonicalEntry := models.RpmV001Schema{}
// need to canonicalize key content
var pubKeyContent []byte
canonicalEntry.PublicKey = &models.RpmV001SchemaPublicKey{}
pubKeyContent, err = keyObj.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&pubKeyContent)
canonicalEntry.Package = &models.RpmV001SchemaPackage{}
canonicalEntry.Package.Hash = &models.RpmV001SchemaPackageHash{}
canonicalEntry.Package.Hash.Algorithm = v.RPMModel.Package.Hash.Algorithm
canonicalEntry.Package.Hash.Value = v.RPMModel.Package.Hash.Value
// data content is not set deliberately
// set NEVRA headers
canonicalEntry.Package.Headers = make(map[string]string)
canonicalEntry.Package.Headers["Name"] = rpmObj.Name()
canonicalEntry.Package.Headers["Epoch"] = strconv.Itoa(rpmObj.Epoch())
canonicalEntry.Package.Headers["Version"] = rpmObj.Version()
canonicalEntry.Package.Headers["Release"] = rpmObj.Release()
canonicalEntry.Package.Headers["Architecture"] = rpmObj.Architecture()
if md5sum := rpmObj.GetBytes(0, 1004); md5sum != nil {
canonicalEntry.Package.Headers["RPMSIGTAG_MD5"] = hex.EncodeToString(md5sum)
}
if sha1sum := rpmObj.GetBytes(0, 1012); sha1sum != nil {
canonicalEntry.Package.Headers["RPMSIGTAG_SHA1"] = hex.EncodeToString(sha1sum)
}
if sha256sum := rpmObj.GetBytes(0, 1016); sha256sum != nil {
canonicalEntry.Package.Headers["RPMSIGTAG_SHA256"] = hex.EncodeToString(sha256sum)
}
// wrap in valid object with kind and apiVersion set
rpm := models.Rpm{}
rpm.APIVersion = conv.Pointer(APIVERSION)
rpm.Spec = &canonicalEntry
return json.Marshal(&rpm)
}
// validate performs cross-field validation for fields in object
func (v V001Entry) validate() error {
key := v.RPMModel.PublicKey
if key == nil {
return errors.New("missing public key")
}
if key.Content == nil || len(*key.Content) == 0 {
return errors.New("'content' must be specified for publicKey")
}
pkg := v.RPMModel.Package
if pkg == nil {
return errors.New("missing package")
}
hash := pkg.Hash
if hash != nil {
// Only sha256 is supported for rpm v0.0.1; enforce length accordingly.
if hash.Value == nil || len(*hash.Value) != crypto.SHA256.Size()*2 {
return errors.New("invalid value for hash")
}
if _, err := hex.DecodeString(*hash.Value); err != nil {
return errors.New("invalid value for hash")
}
} else if len(pkg.Content) == 0 {
return errors.New("'content' must be specified for package")
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Rpm{}
re := V001Entry{}
// we will need artifact, public-key, signature
re.RPMModel = models.RpmV001Schema{}
re.RPMModel.Package = &models.RpmV001SchemaPackage{}
var err error
artifactBytes := props.ArtifactBytes
if artifactBytes == nil {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading RPM file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening RPM file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading RPM file: %w", err)
}
}
re.RPMModel.Package.Content = strfmt.Base64(artifactBytes)
re.RPMModel.PublicKey = &models.RpmV001SchemaPublicKey{}
publicKeyBytes := props.PublicKeyBytes
if len(publicKeyBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one public key must be provided to verify RPM signature")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading public key file: %w", err)
}
publicKeyBytes = append(publicKeyBytes, keyBytes)
} else if len(publicKeyBytes) != 1 {
return nil, errors.New("only one public key must be provided")
}
re.RPMModel.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0])
if err := re.validate(); err != nil {
return nil, err
}
if _, _, err := re.fetchExternalEntities(context.Background()); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.RPMModel
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.RPMModel.PublicKey == nil || v.RPMModel.PublicKey.Content == nil {
return nil, errors.New("rpm v0.0.1 entry not initialized")
}
key, err := pgp.NewPublicKey(bytes.NewReader(*v.RPMModel.PublicKey.Content))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.RPMModel.Package == nil || v.RPMModel.Package.Hash == nil || v.RPMModel.Package.Hash.Value == nil || v.RPMModel.Package.Hash.Algorithm == nil {
return "", errors.New("rpm v0.0.1 entry not initialized")
}
return strings.ToLower(fmt.Sprintf("%s:%s", *v.RPMModel.Package.Hash.Algorithm, *v.RPMModel.Package.Hash.Value)), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.RPMModel.PublicKey == nil {
return false, errors.New("missing publicKey property")
}
if v.RPMModel.PublicKey.Content == nil || len(*v.RPMModel.PublicKey.Content) == 0 {
return false, errors.New("missing publicKey content")
}
if v.RPMModel.Package == nil {
return false, errors.New("missing package property")
}
if len(v.RPMModel.Package.Content) == 0 {
return false, errors.New("missing package content")
}
return true, nil
}
/*
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"
"github.com/go-openapi/strfmt"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/pki"
)
type BaseUnmarshalTester struct{}
func (u BaseUnmarshalTester) NewEntry() EntryImpl {
return &BaseUnmarshalTester{}
}
func (u BaseUnmarshalTester) ArtifactHash() (string, error) {
return "", nil
}
func (u BaseUnmarshalTester) Verifiers() ([]pki.PublicKey, error) {
return nil, nil
}
func (u BaseUnmarshalTester) APIVersion() string {
return "2.0.1"
}
func (u BaseUnmarshalTester) IndexKeys() ([]string, error) {
return []string{}, nil
}
func (u BaseUnmarshalTester) Canonicalize(_ context.Context) ([]byte, error) {
return nil, nil
}
func (u BaseUnmarshalTester) Unmarshal(_ models.ProposedEntry) error {
return nil
}
func (u BaseUnmarshalTester) Validate() error {
return nil
}
func (u BaseUnmarshalTester) AttestationKey() string {
return ""
}
func (u BaseUnmarshalTester) AttestationKeyValue() (string, []byte) {
return "", nil
}
func (u BaseUnmarshalTester) CreateFromArtifactProperties(_ context.Context, _ ArtifactProperties) (models.ProposedEntry, error) {
return nil, nil
}
func (u BaseUnmarshalTester) Insertable() (bool, error) {
return false, nil
}
type BaseProposedEntryTester struct{}
func (b BaseProposedEntryTester) Kind() string {
return "nil"
}
func (b BaseProposedEntryTester) SetKind(_ string) {
}
func (b BaseProposedEntryTester) Validate(_ strfmt.Registry) error {
return nil
}
func (b BaseProposedEntryTester) ContextValidate(_ context.Context, _ strfmt.Registry) error {
return nil
}
/*
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 tuf
import (
"context"
"errors"
"fmt"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/generated/models"
)
const (
KIND = "tuf"
)
type BaseTufType struct {
types.RekorType
}
func init() {
types.TypeMap.Store(KIND, New)
}
func New() types.TypeImpl {
btt := BaseTufType{}
btt.Kind = KIND
btt.VersionMap = VersionMap
return &btt
}
var VersionMap = types.NewSemVerEntryFactoryMap()
func (btt BaseTufType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) {
if pe == nil {
return nil, errors.New("proposed entry cannot be nil")
}
tuf, ok := pe.(*models.TUF)
if !ok {
return nil, fmt.Errorf("cannot unmarshal non-tuf types %+v", pe)
}
return btt.VersionedUnmarshal(tuf, *tuf.APIVersion)
}
func (btt *BaseTufType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) {
if version == "" {
version = btt.DefaultVersion()
}
ei, err := btt.VersionedUnmarshal(nil, version)
if err != nil {
return nil, fmt.Errorf("fetching TUF version implementation: %w", err)
}
return ei.CreateFromArtifactProperties(ctx, props)
}
func (btt BaseTufType) DefaultVersion() string {
return "0.0.1"
}
/*
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 tuf
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/theupdateframework/go-tuf/data"
// This will support deprecated ECDSA hex-encoded keys in TUF metadata.
// Will be removed when sigstore migrates entirely off hex-encoded.
_ "github.com/theupdateframework/go-tuf/pkg/deprecated/set_ecdsa"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/tuf"
"github.com/sigstore/rekor/pkg/util"
"github.com/go-openapi/strfmt"
"github.com/sigstore/rekor/pkg/pki"
ptuf "github.com/sigstore/rekor/pkg/pki/tuf"
"github.com/go-openapi/swag/conv"
"github.com/sigstore/rekor/pkg/generated/models"
)
const (
APIVERSION = "0.0.1"
)
func init() {
if err := tuf.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil {
log.Logger.Panic(err)
}
}
type BaseSigned struct {
Type string `json:"_type"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
}
type V001Entry struct {
TufObj models.TUFV001Schema
}
func (v V001Entry) APIVersion() string {
return APIVERSION
}
func NewEntry() types.EntryImpl {
return &V001Entry{}
}
func (v V001Entry) IndexKeys() ([]string, error) {
var result []string
keyBytes, err := v.parseRootContent()
if err != nil {
return nil, err
}
sigBytes, err := v.parseMetadataContent()
if err != nil {
return nil, err
}
key, err := ptuf.NewPublicKey(bytes.NewReader(keyBytes))
if err != nil {
return nil, err
}
sig, err := ptuf.NewSignature(bytes.NewReader(sigBytes))
if err != nil {
return nil, err
}
// Index metadata hash, type, and version.
metadata, err := sig.CanonicalValue()
if err != nil {
return nil, err
}
metadataHash := sha256.Sum256(metadata)
result = append(result, strings.ToLower(hex.EncodeToString(metadataHash[:])))
result = append(result, sig.Role)
result = append(result, strconv.Itoa(sig.Version))
// Index root.json hash.
root, err := key.CanonicalValue()
if err != nil {
log.Logger.Error(err)
} else {
rootHash := sha256.Sum256(root)
result = append(result, strings.ToLower(hex.EncodeToString(rootHash[:])))
}
// TODO: Index individual key IDs
return result, nil
}
func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
tuf, ok := pe.(*models.TUF)
if !ok {
return errors.New("cannot unmarshal non tuf v0.0.1 type")
}
if err := DecodeEntry(tuf.Spec, &v.TufObj); err != nil {
return err
}
// field validation
if err := v.TufObj.Validate(strfmt.Default); err != nil {
return err
}
// cross field validation
return v.Validate()
}
// DecodeEntry performs a direct decode for TUF v0.0.1 without reflection-based mapstructure.
// Supported input shapes:
// - map[string]any with keys: metadata.root where each has a nested "content" value
// - *models.TUFV001Schema or models.TUFV001Schema
// It avoids mutating the output on error.
func DecodeEntry(input any, output *models.TUFV001Schema) error {
if output == nil {
return fmt.Errorf("nil output *models.TUFV001Schema")
}
var m models.TUFV001Schema
switch data := input.(type) {
case map[string]any:
mm := data
if md, ok := mm["metadata"].(map[string]any); ok {
m.Metadata = &models.TUFV001SchemaMetadata{}
if c, ok := md["content"]; ok {
m.Metadata.Content = c
}
}
if rt, ok := mm["root"].(map[string]any); ok {
m.Root = &models.TUFV001SchemaRoot{}
if c, ok := rt["content"]; ok {
m.Root.Content = c
}
}
*output = m
return nil
case *models.TUFV001Schema:
if data == nil {
return fmt.Errorf("nil *models.TUFV001Schema")
}
*output = *data
return nil
case models.TUFV001Schema:
*output = data
return nil
default:
return fmt.Errorf("unsupported input type %T for DecodeEntry", input)
}
}
func (v *V001Entry) fetchExternalEntities(_ context.Context) (pki.PublicKey, pki.Signature, error) {
// Parse metadata content
var contentBytes []byte
if v.TufObj.Metadata.Content != nil {
var err error
contentBytes, err = v.parseMetadataContent()
if err != nil {
return nil, nil, err
}
}
sigObj, err := ptuf.NewSignature(bytes.NewReader(contentBytes))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Parse root content
var rootContentBytes []byte
if v.TufObj.Root.Content != nil {
rootContentBytes, err = v.parseRootContent()
if err != nil {
return nil, nil, err
}
}
keyObj, err := ptuf.NewPublicKey(bytes.NewReader(rootContentBytes))
if err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
// Verify signature
if err := sigObj.Verify(nil, keyObj); err != nil {
return nil, nil, &types.InputValidationError{Err: err}
}
return keyObj, sigObj, nil
}
func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
key, sig, err := v.fetchExternalEntities(ctx)
if err != nil {
return nil, err
}
canonicalEntry := models.TUFV001Schema{}
canonicalEntry.SpecVersion, err = key.(*ptuf.PublicKey).SpecVersion()
if err != nil {
return nil, err
}
// need to canonicalize manifest (canonicalize JSON)
canonicalEntry.Root = &models.TUFV001SchemaRoot{}
canonicalEntry.Root.Content, err = key.CanonicalValue()
if err != nil {
return nil, err
}
canonicalEntry.Metadata = &models.TUFV001SchemaMetadata{}
canonicalEntry.Metadata.Content, err = sig.CanonicalValue()
if err != nil {
return nil, err
}
// wrap in valid object with kind and apiVersion set
tuf := models.TUF{}
tuf.APIVersion = conv.Pointer(APIVERSION)
tuf.Spec = &canonicalEntry
return json.Marshal(&tuf)
}
// Validate performs cross-field validation for fields in object
// FIXME: we can probably export ValidateMetablock on in-toto.go
func (v V001Entry) Validate() error {
root := v.TufObj.Root
if root == nil {
return errors.New("missing root")
}
if root.Content == nil {
return errors.New("root must be specified")
}
tufManifest := v.TufObj.Metadata
if tufManifest == nil {
return errors.New("missing TUF metadata")
}
if tufManifest.Content == nil {
return errors.New("TUF metadata must be specified")
}
return nil
}
func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
// This will do only syntactic checks of the metablock, not signature verification.
// Signature verification occurs in FetchExternalEntries()
returnVal := models.TUF{}
re := V001Entry{}
// we will need the manifest and root
var err error
artifactBytes := props.ArtifactBytes
re.TufObj.Metadata = &models.TUFV001SchemaMetadata{}
if artifactBytes == nil {
var artifactReader io.ReadCloser
if props.ArtifactPath == nil {
return nil, errors.New("path to artifact file must be specified")
}
if props.ArtifactPath.IsAbs() {
artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil)
if err != nil {
return nil, fmt.Errorf("error reading RPM file: %w", err)
}
} else {
artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path))
if err != nil {
return nil, fmt.Errorf("error opening RPM file: %w", err)
}
}
artifactBytes, err = io.ReadAll(artifactReader)
if err != nil {
return nil, fmt.Errorf("error reading RPM file: %w", err)
}
}
s := &data.Signed{}
if err := json.Unmarshal(artifactBytes, s); err != nil {
return nil, err
}
re.TufObj.Metadata.Content = s
rootBytes := props.PublicKeyBytes
re.TufObj.Root = &models.TUFV001SchemaRoot{}
if len(rootBytes) == 0 {
if len(props.PublicKeyPaths) != 1 {
return nil, errors.New("only one path to root file must be specified")
}
keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path))
if err != nil {
return nil, fmt.Errorf("error reading root file: %w", err)
}
rootBytes = append(rootBytes, keyBytes)
} else if len(rootBytes) != 1 {
return nil, errors.New("only one root key must be provided")
}
root := &data.Signed{}
if err := json.Unmarshal(rootBytes[0], root); err != nil {
return nil, err
}
re.TufObj.Root.Content = root
if err := re.Validate(); err != nil {
return nil, err
}
if _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
}
returnVal.APIVersion = conv.Pointer(re.APIVersion())
returnVal.Spec = re.TufObj
return &returnVal, nil
}
func (v V001Entry) Verifiers() ([]pki.PublicKey, error) {
if v.TufObj.Root == nil {
return nil, errors.New("tuf v0.0.1 entry not initialized")
}
keyBytes, err := v.parseRootContent()
if err != nil {
return nil, err
}
key, err := ptuf.NewPublicKey(bytes.NewReader(keyBytes))
if err != nil {
return nil, err
}
return []pki.PublicKey{key}, nil
}
func (v V001Entry) ArtifactHash() (string, error) {
if v.TufObj.Metadata == nil || v.TufObj.Metadata.Content == nil {
return "", errors.New("tuf v0.0.1 entry not initialized")
}
sigBytes, err := v.parseMetadataContent()
if err != nil {
return "", err
}
sig, err := ptuf.NewSignature(bytes.NewReader(sigBytes))
if err != nil {
return "", err
}
metadata, err := sig.CanonicalValue()
if err != nil {
return "", err
}
metadataHash := sha256.Sum256(metadata)
return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(metadataHash[:]))), nil
}
func (v V001Entry) Insertable() (bool, error) {
if v.TufObj.Metadata == nil {
return false, errors.New("missing metadata property")
}
if v.TufObj.Metadata.Content == nil {
return false, errors.New("missing metadata content")
}
if v.TufObj.Root == nil {
return false, errors.New("missing root property")
}
if v.TufObj.Root.Content == nil {
return false, errors.New("missing root content")
}
return true, nil
}
func (v V001Entry) parseRootContent() ([]byte, error) {
var keyBytes []byte
// Root.Content can either be a base64-encoded string or object
switch v := v.TufObj.Root.Content.(type) {
case string:
b, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return nil, fmt.Errorf("base64 decoding TUF root content: %w", err)
}
keyBytes = b
default:
var err error
keyBytes, err = json.Marshal(v)
if err != nil {
return nil, err
}
}
return keyBytes, nil
}
func (v V001Entry) parseMetadataContent() ([]byte, error) {
var sigBytes []byte
// Metadata.Content can either be a base64-encoded string or object
switch v := v.TufObj.Metadata.Content.(type) {
case string:
b, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return nil, fmt.Errorf("base64 decoding TUF metadata content: %w", err)
}
sigBytes = b
default:
var err error
sigBytes, err = json.Marshal(v)
if err != nil {
return nil, err
}
}
return sigBytes, nil
}
//
// 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"
"errors"
"fmt"
"sync"
"github.com/sigstore/rekor/pkg/generated/models"
"golang.org/x/exp/slices"
)
// TypeMap stores mapping between type strings and entry constructors
// entries are written once at process initialization and read for each transaction, so we use
// sync.Map which is optimized for this case
var TypeMap sync.Map
// RekorType is the base struct that is embedded in all type implementations
type RekorType struct {
Kind string // this is the unique string that identifies the type
VersionMap VersionEntryFactoryMap // this maps the supported versions to implementation
}
// TypeImpl is implemented by all types to support the polymorphic conversion of abstract
// proposed entry to a working implementation for the versioned type requested, if supported
type TypeImpl interface {
CreateProposedEntry(context.Context, string, ArtifactProperties) (models.ProposedEntry, error)
DefaultVersion() string
SupportedVersions() []string
IsSupportedVersion(string) bool
UnmarshalEntry(pe models.ProposedEntry) (EntryImpl, error)
}
// VersionedUnmarshal extracts the correct implementing factory function from the type's version map,
// creates an entry of that versioned type and then calls that versioned type's unmarshal method
func (rt *RekorType) VersionedUnmarshal(pe models.ProposedEntry, version string) (EntryImpl, error) {
ef, err := rt.VersionMap.GetEntryFactory(version)
if err != nil {
return nil, fmt.Errorf("%s implementation for version '%v' not found: %w", rt.Kind, version, err)
}
entry := ef()
if entry == nil {
return nil, errors.New("failure generating object")
}
if pe == nil {
return entry, nil
}
return entry, entry.Unmarshal(pe)
}
// SupportedVersions returns a list of versions of this type that can be currently entered into the log
func (rt *RekorType) SupportedVersions() []string {
return rt.VersionMap.SupportedVersions()
}
// IsSupportedVersion returns true if the version can be inserted into the log, and false if not
func (rt *RekorType) IsSupportedVersion(proposedVersion string) bool {
return slices.Contains(rt.SupportedVersions(), proposedVersion)
}
// ListImplementedTypes returns a list of all type strings currently known to
// be implemented
func ListImplementedTypes() []string {
retVal := []string{}
TypeMap.Range(func(k interface{}, v interface{}) bool {
tf := v.(func() TypeImpl)
for _, verStr := range tf().SupportedVersions() {
retVal = append(retVal, fmt.Sprintf("%v:%v", k.(string), verStr))
}
return true
})
return retVal
}
//
// 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 (
"fmt"
"sync"
"github.com/blang/semver"
"github.com/sigstore/rekor/pkg/log"
)
// VersionEntryFactoryMap defines a map-like interface to find the correct implementation for a version string
// This could be a simple map[string][EntryFactory], or something more elegant (e.g. semver)
type VersionEntryFactoryMap interface {
GetEntryFactory(string) (EntryFactory, error) // return the entry factory for the specified version
SetEntryFactory(string, EntryFactory) error // set the entry factory for the specified version
Count() int // return the count of entry factories currently in the map
SupportedVersions() []string // return a list of versions currently stored in the map
}
// SemVerEntryFactoryMap implements a map that allows implementations to specify their supported versions using
// semver-compliant strings
type SemVerEntryFactoryMap struct {
factoryMap map[string]EntryFactory
sync.RWMutex
}
func NewSemVerEntryFactoryMap() VersionEntryFactoryMap {
s := SemVerEntryFactoryMap{}
s.factoryMap = make(map[string]EntryFactory)
return &s
}
func (s *SemVerEntryFactoryMap) Count() int {
return len(s.factoryMap)
}
func (s *SemVerEntryFactoryMap) GetEntryFactory(version string) (EntryFactory, error) {
s.RLock()
defer s.RUnlock()
semverToMatch, err := semver.Parse(version)
if err != nil {
log.Logger.Error(err)
return nil, err
}
// will return first function that matches
for k, v := range s.factoryMap {
semverRange, err := semver.ParseRange(k)
if err != nil {
log.Logger.Error(err)
return nil, err
}
if semverRange(semverToMatch) {
return v, nil
}
}
return nil, fmt.Errorf("unable to locate entry for version %s", version)
}
func (s *SemVerEntryFactoryMap) SetEntryFactory(constraint string, ef EntryFactory) error {
s.Lock()
defer s.Unlock()
if _, err := semver.ParseRange(constraint); err != nil {
log.Logger.Error(err)
return err
}
s.factoryMap[constraint] = ef
return nil
}
func (s *SemVerEntryFactoryMap) SupportedVersions() []string {
var versions []string
for k := range s.factoryMap {
versions = append(versions, k)
}
return versions
}
//
// 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 util
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
)
// heavily borrowed from https://github.com/transparency-dev/formats/blob/main/log/checkpoint.go
type Checkpoint struct {
// Origin is the unique identifier/version string
Origin string
// Size is the number of entries in the log at this checkpoint.
Size uint64
// Hash is the hash which commits to the contents of the entire log.
Hash []byte
// OtherContent is any additional data to be included in the signed payload; each element is assumed to be one line
OtherContent []string
}
// String returns the String representation of the Checkpoint
func (c Checkpoint) String() string {
var b strings.Builder
fmt.Fprintf(&b, "%s\n%d\n%s\n", c.Origin, c.Size, base64.StdEncoding.EncodeToString(c.Hash))
for _, line := range c.OtherContent {
fmt.Fprintf(&b, "%s\n", line)
}
return b.String()
}
// MarshalCheckpoint returns the common format representation of this Checkpoint.
func (c Checkpoint) MarshalCheckpoint() ([]byte, error) {
return []byte(c.String()), nil
}
// UnmarshalCheckpoint parses the common formatted checkpoint data and stores the result
// in the Checkpoint.
//
// The supplied data is expected to begin with the following 3 lines of text,
// each followed by a newline:
// <ecosystem/version string>
// <decimal representation of log size>
// <base64 representation of root hash>
// <optional non-empty line of other content>...
// <optional non-empty line of other content>...
//
// This will discard any content found after the checkpoint (including signatures)
func (c *Checkpoint) UnmarshalCheckpoint(data []byte) error {
l := bytes.Split(data, []byte("\n"))
if len(l) < 4 {
return errors.New("invalid checkpoint - too few newlines")
}
origin := string(l[0])
if len(origin) == 0 {
return errors.New("invalid checkpoint - empty ecosystem")
}
size, err := strconv.ParseUint(string(l[1]), 10, 64)
if err != nil {
return fmt.Errorf("invalid checkpoint - size invalid: %w", err)
}
h, err := base64.StdEncoding.DecodeString(string(l[2]))
if err != nil {
return fmt.Errorf("invalid checkpoint - invalid hash: %w", err)
}
*c = Checkpoint{
Origin: origin,
Size: size,
Hash: h,
}
if len(l) >= 3 {
for _, line := range l[3:] {
if len(line) == 0 {
break
}
c.OtherContent = append(c.OtherContent, string(line))
}
}
return nil
}
type SignedCheckpoint struct {
Checkpoint
SignedNote
}
func CreateSignedCheckpoint(c Checkpoint) (*SignedCheckpoint, error) {
text, err := c.MarshalCheckpoint()
if err != nil {
return nil, err
}
return &SignedCheckpoint{
Checkpoint: c,
SignedNote: SignedNote{Note: string(text)},
}, nil
}
func SignedCheckpointValidator(strToValidate string) bool {
s := SignedNote{}
if err := s.UnmarshalText([]byte(strToValidate)); err != nil {
return false
}
c := &Checkpoint{}
return c.UnmarshalCheckpoint([]byte(s.Note)) == nil
}
func CheckpointValidator(strToValidate string) bool {
c := &Checkpoint{}
return c.UnmarshalCheckpoint([]byte(strToValidate)) == nil
}
func (r *SignedCheckpoint) UnmarshalText(data []byte) error {
s := SignedNote{}
if err := s.UnmarshalText([]byte(data)); err != nil {
return fmt.Errorf("unmarshalling signed note: %w", err)
}
c := Checkpoint{}
if err := c.UnmarshalCheckpoint([]byte(s.Note)); err != nil {
return fmt.Errorf("unmarshalling checkpoint: %w", err)
}
*r = SignedCheckpoint{Checkpoint: c, SignedNote: s}
return nil
}
// CreateAndSignCheckpoint creates a signed checkpoint as a commitment to the current root hash
func CreateAndSignCheckpoint(ctx context.Context, hostname string, treeID int64, treeSize uint64, rootHash []byte, signer signature.Signer) ([]byte, error) {
sth, err := CreateSignedCheckpoint(Checkpoint{
Origin: fmt.Sprintf("%s - %d", hostname, treeID),
Size: treeSize,
Hash: rootHash,
})
if err != nil {
return nil, fmt.Errorf("error creating checkpoint: %w", err)
}
if _, err := sth.Sign(hostname, signer, options.WithContext(ctx)); err != nil {
return nil, fmt.Errorf("error signing checkpoint: %w", err)
}
scBytes, err := sth.MarshalText()
if err != nil {
return nil, fmt.Errorf("error marshalling checkpoint: %w", err)
}
return scBytes, nil
}
//
// 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 util
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
)
// FileOrURLReadCloser Note: caller is responsible for closing ReadCloser returned from method!
func FileOrURLReadCloser(ctx context.Context, url string, content []byte) (io.ReadCloser, error) {
var dataReader io.ReadCloser
if url != "" {
//TODO: set timeout here, SSL settings?
client := &http.Client{}
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("error received while fetching artifact '%v': %v", url, resp.Status)
}
dataReader = resp.Body
} else {
dataReader = io.NopCloser(bytes.NewReader(content))
}
return dataReader, nil
}
// Copyright 2022 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 util
import (
"crypto"
"fmt"
"strings"
)
// PrefixSHA sets the prefix of a sha hash to match how it is stored based on the length.
func PrefixSHA(sha string) string {
var prefix string
var components = strings.Split(sha, ":")
if len(components) == 2 {
return sha
}
switch len(sha) {
case 40:
prefix = "sha1:"
case 64:
prefix = "sha256:"
case 96:
prefix = "sha384:"
case 128:
prefix = "sha512:"
}
return fmt.Sprintf("%v%v", prefix, sha)
}
func UnprefixSHA(sha string) (crypto.Hash, string) {
components := strings.Split(sha, ":")
if len(components) == 2 {
prefix := components[0]
sha = components[1]
switch prefix {
case "sha1":
return crypto.SHA1, sha
case "sha256":
return crypto.SHA256, sha
case "sha384":
return crypto.SHA384, sha
case "sha512":
return crypto.SHA512, sha
default:
return crypto.Hash(0), ""
}
}
switch len(sha) {
case 40:
return crypto.SHA1, sha
case 64:
return crypto.SHA256, sha
case 96:
return crypto.SHA384, sha
case 128:
return crypto.SHA512, sha
}
return crypto.Hash(0), ""
}
//
// 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 util
import (
"bufio"
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"strings"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
"golang.org/x/mod/sumdb/note"
)
type SignedNote struct {
// Textual representation of a note to sign.
Note string
// Signatures are one or more signature lines covering the payload
Signatures []note.Signature
}
// Sign adds a signature to a SignedCheckpoint object
// The signature is added to the signature array as well as being directly returned to the caller
func (s *SignedNote) Sign(identity string, signer signature.Signer, opts signature.SignOption) (*note.Signature, error) {
sig, err := signer.SignMessage(bytes.NewReader([]byte(s.Note)), opts)
if err != nil {
return nil, fmt.Errorf("signing note: %w", err)
}
pk, err := signer.PublicKey()
if err != nil {
return nil, fmt.Errorf("retrieving public key: %w", err)
}
pkHash, err := getPublicKeyHash(pk)
if err != nil {
return nil, err
}
signature := note.Signature{
Name: identity,
Hash: pkHash,
Base64: base64.StdEncoding.EncodeToString(sig),
}
s.Signatures = append(s.Signatures, signature)
return &signature, nil
}
// Verify checks that one of the signatures can be successfully verified using
// the supplied public key
func (s SignedNote) Verify(verifier signature.Verifier) bool {
if len(s.Signatures) == 0 {
return false
}
msg := []byte(s.Note)
digest := sha256.Sum256(msg)
pk, err := verifier.PublicKey()
if err != nil {
return false
}
verifierPkHash, err := getPublicKeyHash(pk)
if err != nil {
return false
}
for _, s := range s.Signatures {
sigBytes, err := base64.StdEncoding.DecodeString(s.Base64)
if err != nil {
return false
}
if s.Hash != verifierPkHash {
return false
}
opts := []signature.VerifyOption{}
switch pk.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey:
opts = append(opts, options.WithDigest(digest[:]))
case ed25519.PublicKey:
break
default:
return false
}
if err := verifier.VerifySignature(bytes.NewReader(sigBytes), bytes.NewReader(msg), opts...); err != nil {
return false
}
}
return true
}
// MarshalText returns the common format representation of this SignedNote.
func (s SignedNote) MarshalText() ([]byte, error) {
return []byte(s.String()), nil
}
// String returns the String representation of the SignedNote
func (s SignedNote) String() string {
var b strings.Builder
b.WriteString(s.Note)
b.WriteRune('\n')
for _, sig := range s.Signatures {
var hbuf [4]byte
binary.BigEndian.PutUint32(hbuf[:], sig.Hash)
sigBytes, _ := base64.StdEncoding.DecodeString(sig.Base64)
b64 := base64.StdEncoding.EncodeToString(append(hbuf[:], sigBytes...))
fmt.Fprintf(&b, "%c %s %s\n", '\u2014', sig.Name, b64)
}
return b.String()
}
// UnmarshalText parses the common formatted signed note data and stores the result
// in the SignedNote. THIS DOES NOT VERIFY SIGNATURES INSIDE THE CONTENT!
//
// The supplied data is expected to contain a single Note, followed by a single
// line with no comment, followed by one or more lines with the following format:
//
// \u2014 name signature
//
// - name is the string associated with the signer
// - signature is a base64 encoded string; the first 4 bytes of the decoded value is a
// hint to the public key; it is a big-endian encoded uint32 representing the first
// 4 bytes of the SHA256 hash of the public key
func (s *SignedNote) UnmarshalText(data []byte) error {
sigSplit := []byte("\n\n")
// Must end with signature block preceded by blank line.
split := bytes.LastIndex(data, sigSplit)
if split < 0 {
return errors.New("malformed note")
}
text, data := data[:split+1], data[split+2:]
if len(data) == 0 || data[len(data)-1] != '\n' {
return errors.New("malformed note")
}
sn := SignedNote{
Note: string(text),
}
b := bufio.NewScanner(bytes.NewReader(data))
for b.Scan() {
var name, signature string
if _, err := fmt.Fscanf(strings.NewReader(b.Text()), "\u2014 %s %s\n", &name, &signature); err != nil {
return fmt.Errorf("parsing signature: %w", err)
}
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return fmt.Errorf("decoding signature: %w", err)
}
if len(sigBytes) < 5 {
return errors.New("signature is too small")
}
sig := note.Signature{
Name: name,
Hash: binary.BigEndian.Uint32(sigBytes[0:4]),
Base64: base64.StdEncoding.EncodeToString(sigBytes[4:]),
}
sn.Signatures = append(sn.Signatures, sig)
}
if len(sn.Signatures) == 0 {
return errors.New("no signatures found in input")
}
// copy sc to s
*s = sn
return nil
}
func SignedNoteValidator(strToValidate string) bool {
s := SignedNote{}
return s.UnmarshalText([]byte(strToValidate)) == nil
}
func getPublicKeyHash(publicKey crypto.PublicKey) (uint32, error) {
pubKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return 0, fmt.Errorf("marshalling public key: %w", err)
}
pkSha := sha256.Sum256(pubKeyBytes)
hash := binary.BigEndian.Uint32(pkSha[:])
return hash, nil
}
//
// Copyright 2022 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 util
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"testing"
"time"
"golang.org/x/crypto/openpgp"
)
var (
cli = "rekor-cli"
server = "rekor-server"
keys openpgp.EntityList
)
type GetOut struct {
Attestation string
AttestationType string
Body interface{}
LogIndex int
IntegratedTime int64
}
// This was generated with gpg --gen-key, and all defaults.
// The email is "test@rekor.dev", and the name is Rekor Test.
// It should only be used for test purposes.
var secretKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
lQVYBF/11g0BDADciiDQKYjWjIZYTFC55kzaf3H7VcjKb7AdBSyHsN8OIZvLkbgx
1M5x+JPVXCiBEJMjp7YCVJeTQYixic4Ep+YeC8zIdP8ZcvLD9bgFumws+TBJMY7w
2cy3oPv/uVW4TRFv42PwKjO/sXpRg1gJx3EX2FJV+aYAPd8Z6pHxuOk6J49wLY1E
3hl1ZrPGUGsF4l7tVHniZG8IzTCgJGC6qrlsg1VGrIkactesr7U6+Xs4VJgNIdCs
2/7RqwWAtkSHumAKBe1hNY2ddt3p42jEM0P2g7Uwao7/ziSiS/N96dkEAdWCT99/
e0qLC4q6VisrFvdmfDQrY73eadL6Jf38H2IUpNrcHgVZtEBGhD6dOcjs2YBZNfX3
wfDJooRk0efcLlSFT1YVZhxez/zZTd+7nReKPmsOxiaUmP/bQSB4FZZ4ZxsfxH2t
wgX4dtwRV28JGHeA/ISJiWMQKrci1PRhRWF32EaE6dF+2VJwGi9mssEkAA+YHh1O
HjPgosqFp16rb1MAEQEAAQAL+gMzi2+6H/RirH3nowcFIs8hKSRphbVP6Gc4xgFf
kz1Um5BZmH+QrpZ/nJXCSrbk6LM3IgXn+HNOG4/dh5IQZd9rHcPjKY4oWax33/36
oMteVVHFWGUtTt1zhspFhHWybghebVBKgd8h0ma7LgdQ+oFKxeyIPTKlCJy1slH8
nytq8O1t8S5eEvyIoHTGghHfIVr3Q6BXrjebKD41iPnstIMGElzTmwHj8jbdg2yh
u8+A2twwm3jcO1dhJilM0V3Zr2L5upsrb20vdD0DMAKZyEcD20VkCt8sxFtTYfGw
q72aylHxooObicswblfgWXJMEjQ+3CJzPEfkPCEZpUb87QGRsBHSuToVfICkL6ZN
3TE1RznrItpwXGgWTwyoahXHkMmKLuDlf2PdOcGJd8YOiMFqSyJfh3nvAI2u83dS
/wzMZqzl77QEUo5YcmXY5LpLco6P/xQcTTgJ7VT0M2mXr/LneffbjbaxNS6q7rl4
uiGpPcpdevXqhf/VGS+e3JliUQYA5ny7nLYQOEN34O5AKHpfIYoqvGZJkLCp9BDx
fPGn/b7mGeB/quTb1y/7G28Ovkj7tDz3SGFfSaNeMVpLbkxcZhq05dasb13q2go+
la0pcv49lHnVIjGcQh+AqoEASm9+ZIyj9vTt6KQ60FDJ78Xkbe1iAOj/dggTe+wj
udYtyvmpYvK/fz5rzg10oh20afbYPTnIubVcSB8RD1muFIrHTAPSrJ4OsXt1nFgT
rvbIjBX5Q//cKDiCd/xHJOwDvIwtBgD084KdBPr8YAtQVThF2MULHeGp11nqo0Gb
dsOkxe8cixK7JjyDfGbK8H82fI1Fd47lcp9h1VLL5A0XnJgDGHNW/IWIdBfvhvjS
AnF0wPaN0ohpUvkfVAErG+n+RcLricL+afX/1+YoJZTNGW+fclbTBQCfWyFYBh49
YTxa6qH131Lj8VWbCuSdfo1jN5nUuVeutkW9VnMLuo0VCt+Phw8ok3SP8rdBMFRW
3eYmCCRw+XvLQT0vL3K0D4udts+nmX8F/30jPprjz09hyreERUWcqvQcUO3E5uc6
xQUOmMrIg5jVK6PdFRtUMNip+EMOfewoUDtNf2VOQ0WdSboopZyXXGG0SW+7FC5V
m/mFkffnxqHbj8odOI8l9xiK6ejeVMc3aKIL3tTAGZxNniKr4SfEFkT+nC4rNpLF
tM6PBxxaffTpG5G2GW2sy9A5jEygcuDz5wTjS5KnKoXlI8qaDrfeIiB/hBZMDtAM
KmFvCQ2AO3xDtxzHPphEhPZx793S7pqru+egtBtSZWtvciBUZXN0IDx0ZXN0QHJl
a29yLmRldj6JAdQEEwEIAD4WIQRpDIZrWp/rSB21PSTYo+vASJM64gUCX/XWDQIb
AwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDYo+vASJM64j/9C/4q
2iKsQBcOofjH7PklIlV1asTJP8Uxp2giPXnwgcfWYDGs+e/oHMjkmWwyXUkE0ki7
N4SB4m6ztfljkTsOPUFVcDtcjj2ScOx5lsrDW8wPMwiJpFM62HkJfg7mrAqDquTB
iue5X+9OFbxOBSRti9w+H5Uiw/jaChxUKpaDW5qtZiYEkgKpbEK03jFkewtu8SWD
zoFt2gMKSHg6btz+hqdrqA1R/n4Z5LbBuWk+hf+N7FGO9clQWoZrRr5qorSfOpQO
/7S4U5UN4w/IL2OtuPfajHb91aH9q81eddclutOS5kAzYLHgytHSVHIw8QJiqsbe
YqudCcYHo7aNRlpbIXnE6+FQqa7+hZd5Cv8IQgQngDiAi+C0khYKo3riTwORvlam
CqC30lzlNWxkFJzfW0E88B4j3rOFeqaXhIohPtxKr68vGVsuIMCnCOsbYfyoUiZm
RGc4tVAbCuwWJe+OoZEKsS0m6tY6CjT0ugpb+oxqQvyj2eB1cK0i0aiBrAuQCZWd
BVgEX/XWDQEMAKjSmPaQJdE9+4c+uuZ3plwfwodEY5nPG1qIQWj7LmvCtYQWwex/
rOPE0ec/6UdlrUSjiAQ0mV5JkdN2QRoxRGy8JsrLAnXadXeO3HI9SpuZaKvsUg5d
apvdJqcDWlzz/LoA2rl+Z/wo3q2Wx9rh/RHqPLWBUiJSkIlANsshatu9N2Mj5ody
defGn8gnj6b0JZRpUskyg4/9Wzns/w4OWql3CVm0BIGn6Tt/EplI7eCZg4VvujWN
T0gydK75hkbGkHE1Z45kBZU26Uge+YEyJ0PFcaXE/kCNetPOtsUz/tO+h6ZLJECI
lZlnG5/KxOGhoS3fG9F/XfyE3DNQE6qx7CuC6cWm+92wLlPz/Ir0iKTV0tPZLCgu
5rSNuSJyjTy71nMksFaVJxjb7PZHMbQPXEIbcIX4AvEGV0Icwsh+e6/yXlTgxux9
RszqyS1LHydtQLvx5X84d9iENkoGGNfVH99i2P1CrTbZ2v3KCnhvy+cTVLjW82XV
WploktfbdC55TQARAQABAAv+KR1e8N9ywlaK0SmDGZlGq/V1Kf3LFvykMARyj6dq
qwZYsBJdyKPgfnki2KONQ9zcmZSNDd8kgdy/dcU9PiyE+klJVkaiMwMQ7BzgDbdl
Ged+4S303vg7vDlcDj0oDu7B3CfUnOvO1c+7SYHo6uLyP+BwyBB2aRL8Dd0UaxyY
mmrm2A94d4C1+8w5AiU2XEXl+BK9fW/+r/zXMJCKHkl7JX3uykin906mI94C8M9c
1X/1krP+4MdpKU9WcP2miMqXIhm09rF09YDY1qLRBhvKWnaDDDjBSmIxIAc2AyCe
JbmFzLVXynduhxhplmOMDD2aIQNfxfiw2E+jq4MLgIGhrNV+yMGOInzMwT0qguB4
bJllfk7f7ikqwBva9hdC3pUx4zOogJyTkcBH/ETm7b1L26DyJkxlln/Je2Qr64aX
t5bhx/Y8rC7jVxYYwtIPKtn3zppwNFL3Vysg47BpYM6aAz0AZSKm+Y6jAi2/tWtV
jhFvQWRPBaDuMS7dzcnb4TY5BgDJ/lG27MpNMEYU5zqWQ7capmYTk8AV6nH+r5cm
QpoWld5p0gFw6qnjeJ1Q3XZs7QlPq0RQrXzjT3Drhu5XNjqeqQGDH6YY39OQrTSS
/1BhFhiWUMBpyqv4lc8ytJjbkgg0daNubrIKynwZ/H8Gy3vRe2rHjqaApcwQ5Fwc
Iy8FPeQI95rnw34b/0dohkxjz6ULJahdksVggI0NS312awjg6TlQx1V3Lv7hbuOE
Qv1p3kedwr4MgnVe0fZw6Y3ehukGANX13UKtkw6sHjO7h87F9qR5Wb47Rnb12oDa
fZHmn2jLDAr8Sius1mHFJie9nlXRvBxtVpjyliJxjg0hYc04PLdVKvGFP2a4WQep
WM+r3fU/Snuhn3VAI2ibMXgFUHW9ofxmhGhdDWImFnU7lvh4U+yoD8vqe9FPFMhu
zCrGSTo7Qy8PTKCzCf3frSPt3TorFrUOa5PBpq1/fOhLAQzpVC7F+hXZ/kIAWTVm
wSIilPk7TSVJdd07bsfNQt88xtJoxQX+OgRb8yK+pSluQxii6IgVwFWslOxuZn/O
Eg9nPh4VAlVGYCh/oleRTLZH+a73p9VQwUzmPjXUDkUFcdM0zysU4HmTZbwTZCQJ
608IqC+p9D6u289bdsBsCDzA6LAhEgU4vj6Zfm0N3MqEWBDuBOt9McwY1Mbo8jbp
slVnkz2B6Rw9UkMzQNVxRFCHfIWhPvbiWeiLQPD31Bs6hdBCzn44k75/+0qyBX0a
Jk8Wmv4z2vR7dh4ABRm4pfZx4IsFbWBS4sSJAbwEGAEIACYWIQRpDIZrWp/rSB21
PSTYo+vASJM64gUCX/XWDQIbDAUJA8JnAAAKCRDYo+vASJM64mceDACSkr9gsNRc
OOcnzglYJtmvtAG27ziVS6/ywGPxyZtyIwfEg8JVnIXuB0Fog1/uuZDdjiz4QO3j
Os9E8z8i6AUKdJgPjxlcr585lSLtKiz7TTPTDmKCF8aga2Gc6+yfjI92F0fEuGh5
GjdQu76x6hLPYT6+pjrvjmXq8gF030jTOiQ2n6o9oH7aQhehEIFsrQdtKh9ZrhWN
QWa1P4iPlzPf+Y7sG7irZqcm4wa/U+qxQPNVcA9FUziymPtbMGlqN4x2Z3Jr3VUP
QFhwXF6U8BM3ldZDNPmmB9OKlsDCR/7+AvwJ52hRxAzIm/lhuXj1xPj5JFuUErAX
aBIJN0iaJaXVB+JFbzXT1DLhqCR1T37zZSKnLMSKtvIe9UOO6Jy4mgX6CDjPM/Vu
9aJhzqmaVUbZOYwJh5ojrWLzswv1K9CdcmDEaK4X/u1z+eWiNjsHE3pzUiq4DJhb
T4CBiqLxHYsQ9n8dT95t+poqJ10PVFkehb+8kh05e3ENd4xpkkdTfIY=
=CwjQ
-----END PGP PRIVATE KEY BLOCK-----`
var PrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
lQVYBF/11g0BDADciiDQKYjWjIZYTFC55kzaf3H7VcjKb7AdBSyHsN8OIZvLkbgx
1M5x+JPVXCiBEJMjp7YCVJeTQYixic4Ep+YeC8zIdP8ZcvLD9bgFumws+TBJMY7w
2cy3oPv/uVW4TRFv42PwKjO/sXpRg1gJx3EX2FJV+aYAPd8Z6pHxuOk6J49wLY1E
3hl1ZrPGUGsF4l7tVHniZG8IzTCgJGC6qrlsg1VGrIkactesr7U6+Xs4VJgNIdCs
2/7RqwWAtkSHumAKBe1hNY2ddt3p42jEM0P2g7Uwao7/ziSiS/N96dkEAdWCT99/
e0qLC4q6VisrFvdmfDQrY73eadL6Jf38H2IUpNrcHgVZtEBGhD6dOcjs2YBZNfX3
wfDJooRk0efcLlSFT1YVZhxez/zZTd+7nReKPmsOxiaUmP/bQSB4FZZ4ZxsfxH2t
wgX4dtwRV28JGHeA/ISJiWMQKrci1PRhRWF32EaE6dF+2VJwGi9mssEkAA+YHh1O
HjPgosqFp16rb1MAEQEAAQAL+gMzi2+6H/RirH3nowcFIs8hKSRphbVP6Gc4xgFf
kz1Um5BZmH+QrpZ/nJXCSrbk6LM3IgXn+HNOG4/dh5IQZd9rHcPjKY4oWax33/36
oMteVVHFWGUtTt1zhspFhHWybghebVBKgd8h0ma7LgdQ+oFKxeyIPTKlCJy1slH8
nytq8O1t8S5eEvyIoHTGghHfIVr3Q6BXrjebKD41iPnstIMGElzTmwHj8jbdg2yh
u8+A2twwm3jcO1dhJilM0V3Zr2L5upsrb20vdD0DMAKZyEcD20VkCt8sxFtTYfGw
q72aylHxooObicswblfgWXJMEjQ+3CJzPEfkPCEZpUb87QGRsBHSuToVfICkL6ZN
3TE1RznrItpwXGgWTwyoahXHkMmKLuDlf2PdOcGJd8YOiMFqSyJfh3nvAI2u83dS
/wzMZqzl77QEUo5YcmXY5LpLco6P/xQcTTgJ7VT0M2mXr/LneffbjbaxNS6q7rl4
uiGpPcpdevXqhf/VGS+e3JliUQYA5ny7nLYQOEN34O5AKHpfIYoqvGZJkLCp9BDx
fPGn/b7mGeB/quTb1y/7G28Ovkj7tDz3SGFfSaNeMVpLbkxcZhq05dasb13q2go+
la0pcv49lHnVIjGcQh+AqoEASm9+ZIyj9vTt6KQ60FDJ78Xkbe1iAOj/dggTe+wj
udYtyvmpYvK/fz5rzg10oh20afbYPTnIubVcSB8RD1muFIrHTAPSrJ4OsXt1nFgT
rvbIjBX5Q//cKDiCd/xHJOwDvIwtBgD084KdBPr8YAtQVThF2MULHeGp11nqo0Gb
dsOkxe8cixK7JjyDfGbK8H82fI1Fd47lcp9h1VLL5A0XnJgDGHNW/IWIdBfvhvjS
AnF0wPaN0ohpUvkfVAErG+n+RcLricL+afX/1+YoJZTNGW+fclbTBQCfWyFYBh49
YTxa6qH131Lj8VWbCuSdfo1jN5nUuVeutkW9VnMLuo0VCt+Phw8ok3SP8rdBMFRW
3eYmCCRw+XvLQT0vL3K0D4udts+nmX8F/30jPprjz09hyreERUWcqvQcUO3E5uc6
xQUOmMrIg5jVK6PdFRtUMNip+EMOfewoUDtNf2VOQ0WdSboopZyXXGG0SW+7FC5V
m/mFkffnxqHbj8odOI8l9xiK6ejeVMc3aKIL3tTAGZxNniKr4SfEFkT+nC4rNpLF
tM6PBxxaffTpG5G2GW2sy9A5jEygcuDz5wTjS5KnKoXlI8qaDrfeIiB/hBZMDtAM
KmFvCQ2AO3xDtxzHPphEhPZx793S7pqru+egtBtSZWtvciBUZXN0IDx0ZXN0QHJl
a29yLmRldj6JAdQEEwEIAD4WIQRpDIZrWp/rSB21PSTYo+vASJM64gUCX/XWDQIb
AwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDYo+vASJM64j/9C/4q
2iKsQBcOofjH7PklIlV1asTJP8Uxp2giPXnwgcfWYDGs+e/oHMjkmWwyXUkE0ki7
N4SB4m6ztfljkTsOPUFVcDtcjj2ScOx5lsrDW8wPMwiJpFM62HkJfg7mrAqDquTB
iue5X+9OFbxOBSRti9w+H5Uiw/jaChxUKpaDW5qtZiYEkgKpbEK03jFkewtu8SWD
zoFt2gMKSHg6btz+hqdrqA1R/n4Z5LbBuWk+hf+N7FGO9clQWoZrRr5qorSfOpQO
/7S4U5UN4w/IL2OtuPfajHb91aH9q81eddclutOS5kAzYLHgytHSVHIw8QJiqsbe
YqudCcYHo7aNRlpbIXnE6+FQqa7+hZd5Cv8IQgQngDiAi+C0khYKo3riTwORvlam
CqC30lzlNWxkFJzfW0E88B4j3rOFeqaXhIohPtxKr68vGVsuIMCnCOsbYfyoUiZm
RGc4tVAbCuwWJe+OoZEKsS0m6tY6CjT0ugpb+oxqQvyj2eB1cK0i0aiBrAuQCZWd
BVgEX/XWDQEMAKjSmPaQJdE9+4c+uuZ3plwfwodEY5nPG1qIQWj7LmvCtYQWwex/
rOPE0ec/6UdlrUSjiAQ0mV5JkdN2QRoxRGy8JsrLAnXadXeO3HI9SpuZaKvsUg5d
apvdJqcDWlzz/LoA2rl+Z/wo3q2Wx9rh/RHqPLWBUiJSkIlANsshatu9N2Mj5ody
defGn8gnj6b0JZRpUskyg4/9Wzns/w4OWql3CVm0BIGn6Tt/EplI7eCZg4VvujWN
T0gydK75hkbGkHE1Z45kBZU26Uge+YEyJ0PFcaXE/kCNetPOtsUz/tO+h6ZLJECI
lZlnG5/KxOGhoS3fG9F/XfyE3DNQE6qx7CuC6cWm+92wLlPz/Ir0iKTV0tPZLCgu
5rSNuSJyjTy71nMksFaVJxjb7PZHMbQPXEIbcIX4AvEGV0Icwsh+e6/yXlTgxux9
RszqyS1LHydtQLvx5X84d9iENkoGGNfVH99i2P1CrTbZ2v3KCnhvy+cTVLjW82XV
WploktfbdC55TQARAQABAAv+KR1e8N9ywlaK0SmDGZlGq/V1Kf3LFvykMARyj6dq
qwZYsBJdyKPgfnki2KONQ9zcmZSNDd8kgdy/dcU9PiyE+klJVkaiMwMQ7BzgDbdl
Ged+4S303vg7vDlcDj0oDu7B3CfUnOvO1c+7SYHo6uLyP+BwyBB2aRL8Dd0UaxyY
mmrm2A94d4C1+8w5AiU2XEXl+BK9fW/+r/zXMJCKHkl7JX3uykin906mI94C8M9c
1X/1krP+4MdpKU9WcP2miMqXIhm09rF09YDY1qLRBhvKWnaDDDjBSmIxIAc2AyCe
JbmFzLVXynduhxhplmOMDD2aIQNfxfiw2E+jq4MLgIGhrNV+yMGOInzMwT0qguB4
bJllfk7f7ikqwBva9hdC3pUx4zOogJyTkcBH/ETm7b1L26DyJkxlln/Je2Qr64aX
t5bhx/Y8rC7jVxYYwtIPKtn3zppwNFL3Vysg47BpYM6aAz0AZSKm+Y6jAi2/tWtV
jhFvQWRPBaDuMS7dzcnb4TY5BgDJ/lG27MpNMEYU5zqWQ7capmYTk8AV6nH+r5cm
QpoWld5p0gFw6qnjeJ1Q3XZs7QlPq0RQrXzjT3Drhu5XNjqeqQGDH6YY39OQrTSS
/1BhFhiWUMBpyqv4lc8ytJjbkgg0daNubrIKynwZ/H8Gy3vRe2rHjqaApcwQ5Fwc
Iy8FPeQI95rnw34b/0dohkxjz6ULJahdksVggI0NS312awjg6TlQx1V3Lv7hbuOE
Qv1p3kedwr4MgnVe0fZw6Y3ehukGANX13UKtkw6sHjO7h87F9qR5Wb47Rnb12oDa
fZHmn2jLDAr8Sius1mHFJie9nlXRvBxtVpjyliJxjg0hYc04PLdVKvGFP2a4WQep
WM+r3fU/Snuhn3VAI2ibMXgFUHW9ofxmhGhdDWImFnU7lvh4U+yoD8vqe9FPFMhu
zCrGSTo7Qy8PTKCzCf3frSPt3TorFrUOa5PBpq1/fOhLAQzpVC7F+hXZ/kIAWTVm
wSIilPk7TSVJdd07bsfNQt88xtJoxQX+OgRb8yK+pSluQxii6IgVwFWslOxuZn/O
Eg9nPh4VAlVGYCh/oleRTLZH+a73p9VQwUzmPjXUDkUFcdM0zysU4HmTZbwTZCQJ
608IqC+p9D6u289bdsBsCDzA6LAhEgU4vj6Zfm0N3MqEWBDuBOt9McwY1Mbo8jbp
slVnkz2B6Rw9UkMzQNVxRFCHfIWhPvbiWeiLQPD31Bs6hdBCzn44k75/+0qyBX0a
Jk8Wmv4z2vR7dh4ABRm4pfZx4IsFbWBS4sSJAbwEGAEIACYWIQRpDIZrWp/rSB21
PSTYo+vASJM64gUCX/XWDQIbDAUJA8JnAAAKCRDYo+vASJM64mceDACSkr9gsNRc
OOcnzglYJtmvtAG27ziVS6/ywGPxyZtyIwfEg8JVnIXuB0Fog1/uuZDdjiz4QO3j
Os9E8z8i6AUKdJgPjxlcr585lSLtKiz7TTPTDmKCF8aga2Gc6+yfjI92F0fEuGh5
GjdQu76x6hLPYT6+pjrvjmXq8gF030jTOiQ2n6o9oH7aQhehEIFsrQdtKh9ZrhWN
QWa1P4iPlzPf+Y7sG7irZqcm4wa/U+qxQPNVcA9FUziymPtbMGlqN4x2Z3Jr3VUP
QFhwXF6U8BM3ldZDNPmmB9OKlsDCR/7+AvwJ52hRxAzIm/lhuXj1xPj5JFuUErAX
aBIJN0iaJaXVB+JFbzXT1DLhqCR1T37zZSKnLMSKtvIe9UOO6Jy4mgX6CDjPM/Vu
9aJhzqmaVUbZOYwJh5ojrWLzswv1K9CdcmDEaK4X/u1z+eWiNjsHE3pzUiq4DJhb
T4CBiqLxHYsQ9n8dT95t+poqJ10PVFkehb+8kh05e3ENd4xpkkdTfIY=
=CwjQ
-----END PGP PRIVATE KEY BLOCK-----`
var PubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBF/11g0BDADciiDQKYjWjIZYTFC55kzaf3H7VcjKb7AdBSyHsN8OIZvLkbgx
1M5x+JPVXCiBEJMjp7YCVJeTQYixic4Ep+YeC8zIdP8ZcvLD9bgFumws+TBJMY7w
2cy3oPv/uVW4TRFv42PwKjO/sXpRg1gJx3EX2FJV+aYAPd8Z6pHxuOk6J49wLY1E
3hl1ZrPGUGsF4l7tVHniZG8IzTCgJGC6qrlsg1VGrIkactesr7U6+Xs4VJgNIdCs
2/7RqwWAtkSHumAKBe1hNY2ddt3p42jEM0P2g7Uwao7/ziSiS/N96dkEAdWCT99/
e0qLC4q6VisrFvdmfDQrY73eadL6Jf38H2IUpNrcHgVZtEBGhD6dOcjs2YBZNfX3
wfDJooRk0efcLlSFT1YVZhxez/zZTd+7nReKPmsOxiaUmP/bQSB4FZZ4ZxsfxH2t
wgX4dtwRV28JGHeA/ISJiWMQKrci1PRhRWF32EaE6dF+2VJwGi9mssEkAA+YHh1O
HjPgosqFp16rb1MAEQEAAbQbUmVrb3IgVGVzdCA8dGVzdEByZWtvci5kZXY+iQHU
BBMBCAA+FiEEaQyGa1qf60gdtT0k2KPrwEiTOuIFAl/11g0CGwMFCQPCZwAFCwkI
BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ2KPrwEiTOuI//Qv+KtoirEAXDqH4x+z5
JSJVdWrEyT/FMadoIj158IHH1mAxrPnv6BzI5JlsMl1JBNJIuzeEgeJus7X5Y5E7
Dj1BVXA7XI49knDseZbKw1vMDzMIiaRTOth5CX4O5qwKg6rkwYrnuV/vThW8TgUk
bYvcPh+VIsP42gocVCqWg1uarWYmBJICqWxCtN4xZHsLbvElg86BbdoDCkh4Om7c
/oana6gNUf5+GeS2wblpPoX/jexRjvXJUFqGa0a+aqK0nzqUDv+0uFOVDeMPyC9j
rbj32ox2/dWh/avNXnXXJbrTkuZAM2Cx4MrR0lRyMPECYqrG3mKrnQnGB6O2jUZa
WyF5xOvhUKmu/oWXeQr/CEIEJ4A4gIvgtJIWCqN64k8Dkb5Wpgqgt9Jc5TVsZBSc
31tBPPAeI96zhXqml4SKIT7cSq+vLxlbLiDApwjrG2H8qFImZkRnOLVQGwrsFiXv
jqGRCrEtJurWOgo09LoKW/qMakL8o9ngdXCtItGogawLkAmVuQGNBF/11g0BDACo
0pj2kCXRPfuHPrrmd6ZcH8KHRGOZzxtaiEFo+y5rwrWEFsHsf6zjxNHnP+lHZa1E
o4gENJleSZHTdkEaMURsvCbKywJ12nV3jtxyPUqbmWir7FIOXWqb3SanA1pc8/y6
ANq5fmf8KN6tlsfa4f0R6jy1gVIiUpCJQDbLIWrbvTdjI+aHcnXnxp/IJ4+m9CWU
aVLJMoOP/Vs57P8ODlqpdwlZtASBp+k7fxKZSO3gmYOFb7o1jU9IMnSu+YZGxpBx
NWeOZAWVNulIHvmBMidDxXGlxP5AjXrTzrbFM/7TvoemSyRAiJWZZxufysThoaEt
3xvRf138hNwzUBOqsewrgunFpvvdsC5T8/yK9Iik1dLT2SwoLua0jbkico08u9Zz
JLBWlScY2+z2RzG0D1xCG3CF+ALxBldCHMLIfnuv8l5U4MbsfUbM6sktSx8nbUC7
8eV/OHfYhDZKBhjX1R/fYtj9Qq022dr9ygp4b8vnE1S41vNl1VqZaJLX23QueU0A
EQEAAYkBvAQYAQgAJhYhBGkMhmtan+tIHbU9JNij68BIkzriBQJf9dYNAhsMBQkD
wmcAAAoJENij68BIkzriZx4MAJKSv2Cw1Fw45yfOCVgm2a+0AbbvOJVLr/LAY/HJ
m3IjB8SDwlWche4HQWiDX+65kN2OLPhA7eM6z0TzPyLoBQp0mA+PGVyvnzmVIu0q
LPtNM9MOYoIXxqBrYZzr7J+Mj3YXR8S4aHkaN1C7vrHqEs9hPr6mOu+OZeryAXTf
SNM6JDafqj2gftpCF6EQgWytB20qH1muFY1BZrU/iI+XM9/5juwbuKtmpybjBr9T
6rFA81VwD0VTOLKY+1swaWo3jHZncmvdVQ9AWHBcXpTwEzeV1kM0+aYH04qWwMJH
/v4C/AnnaFHEDMib+WG5ePXE+PkkW5QSsBdoEgk3SJolpdUH4kVvNdPUMuGoJHVP
fvNlIqcsxIq28h71Q47onLiaBfoIOM8z9W71omHOqZpVRtk5jAmHmiOtYvOzC/Ur
0J1yYMRorhf+7XP55aI2OwcTenNSKrgMmFtPgIGKovEdixD2fx1P3m36mionXQ9U
WR6Fv7ySHTl7cQ13jGmSR1N8hg==
=Fen+
-----END PGP PUBLIC KEY BLOCK-----`
func init() {
p := os.Getenv("REKORTMPDIR")
if p != "" {
cli = path.Join(p, cli)
server = path.Join(p, server)
}
var err error
keys, err = openpgp.ReadArmoredKeyRing(strings.NewReader(secretKey))
if err != nil {
panic(err)
}
}
func OutputContains(t *testing.T, output, sub string) {
t.Helper()
if !strings.Contains(output, sub) {
t.Errorf("Expected [%s] in response, got %s", sub, output)
}
}
func Run(t *testing.T, stdin, cmd string, arg ...string) string {
t.Helper()
arg = append([]string{coverageFlag()}, arg...)
c := exec.Command(cmd, arg...)
if stdin != "" {
c.Stdin = strings.NewReader(stdin)
}
if os.Getenv("REKORTMPDIR") != "" {
// ensure that we use a clean state.json file for each Run
c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR"))
}
b, err := c.CombinedOutput()
if err != nil {
t.Log(string(b))
t.Fatal(err)
}
return stripCoverageOutput(string(b))
}
func RunCli(t *testing.T, arg ...string) string {
t.Helper()
arg = append(arg, rekorServerFlag())
// use a blank config file to ensure no collision
if os.Getenv("REKORTMPDIR") != "" {
arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml")
}
return Run(t, "", cli, arg...)
}
func RunCliErr(t *testing.T, arg ...string) string {
t.Helper()
arg = append([]string{coverageFlag()}, arg...)
arg = append(arg, rekorServerFlag())
// use a blank config file to ensure no collision
if os.Getenv("REKORTMPDIR") != "" {
arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml")
}
cmd := exec.Command(cli, arg...)
b, err := cmd.CombinedOutput()
if err == nil {
t.Log(string(b))
t.Fatalf("expected error, got %s", string(b))
}
return stripCoverageOutput(string(b))
}
func rekorServerFlag() string {
return fmt.Sprintf("--rekor_server=%s", rekorServer())
}
func rekorServer() string {
if s := os.Getenv("REKOR_SERVER"); s != "" {
return s
}
return "http://localhost:3000"
}
func coverageFlag() string {
return "-test.coverprofile=/tmp/pkg-rekor-cli." + RandomSuffix(8) + ".cov"
}
func stripCoverageOutput(out string) string {
return strings.Split(strings.Split(out, "PASS")[0], "FAIL")[0]
}
// RandomSuffix returns a random string of the given length.
func RandomSuffix(n int) string {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
// RandomData returns a random byte slice of the given size.
func RandomData(t *testing.T, n int) []byte {
t.Helper()
rand.Seed(time.Now().UnixNano())
data := make([]byte, n)
if _, err := rand.Read(data[:]); err != nil {
t.Fatal(err)
}
return data
}
func CreateArtifact(t *testing.T, artifactPath string) string {
t.Helper()
// First let's generate some random data so we don't have to worry about dupes.
data := RandomData(t, 100)
artifact := base64.StdEncoding.EncodeToString(data[:])
// Write this to a file
write(t, artifact, artifactPath)
return artifact
}
func write(t *testing.T, data string, path string) {
t.Helper()
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
t.Fatal(err)
}
}
func GetUUIDFromUploadOutput(t *testing.T, out string) string {
t.Helper()
// Output looks like "Artifact timestamped at ...\m Wrote response \n Created entry at index X, available at $URL/UUID", so grab the UUID:
urlTokens := strings.Split(strings.TrimSpace(out), " ")
url := urlTokens[len(urlTokens)-1]
splitUrl := strings.Split(url, "/")
return splitUrl[len(splitUrl)-1]
}
func SignPGP(b []byte) ([]byte, error) {
var buf bytes.Buffer
if err := openpgp.DetachSign(&buf, keys[0], bytes.NewReader(b), nil); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func Write(t *testing.T, data string, path string) {
t.Helper()
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
t.Fatal(err)
}
}
// CreatedPGPSignedArtifact gets the test dir setup correctly with some random artifacts and keys.
func CreatedPGPSignedArtifact(t *testing.T, artifactPath, sigPath string) {
t.Helper()
artifact := CreateArtifact(t, artifactPath)
// Sign it with our key and write that to a file
signature, err := SignPGP([]byte(artifact))
if err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(sigPath, signature, 0644); err != nil {
t.Fatal(err)
}
}
func GetUUIDFromTimestampOutput(t *testing.T, out string) string {
t.Helper()
// Output looks like "Created entry at index X, available at $URL/UUID", so grab the UUID:
urlTokens := strings.Split(strings.TrimSpace(out), "\n")
return GetUUIDFromUploadOutput(t, urlTokens[len(urlTokens)-1])
}
// SetupTestData is a helper function to setups the test data
func SetupTestData(t *testing.T) {
// create a temp directory
artifactPath := filepath.Join(t.TempDir(), "artifact")
// create a temp file
sigPath := filepath.Join(t.TempDir(), "signature.asc")
CreatedPGPSignedArtifact(t, artifactPath, sigPath)
// Write the public key to a file
pubPath := filepath.Join(t.TempDir(), "pubKey.asc")
if err := ioutil.WriteFile(pubPath, []byte(PubKey), 0644); err != nil { //nolint:gosec
t.Fatal(err)
}
// Now upload to rekor!
out := RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath)
OutputContains(t, out, "Created entry at")
}