mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
68a197d7cc
Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com>
273 lines
6.4 KiB
Go
273 lines
6.4 KiB
Go
//go:build imagetrust
|
|
// +build imagetrust
|
|
|
|
package imagetrust
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
|
|
godigest "github.com/opencontainers/go-digest"
|
|
"github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key"
|
|
sigs "github.com/sigstore/cosign/v2/pkg/signature"
|
|
"github.com/sigstore/sigstore/pkg/cryptoutils"
|
|
sigstoreSigs "github.com/sigstore/sigstore/pkg/signature"
|
|
"github.com/sigstore/sigstore/pkg/signature/options"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
)
|
|
|
|
const cosignDirRelativePath = "_cosign"
|
|
|
|
type PublicKeyLocalStorage struct {
|
|
cosignDir string
|
|
}
|
|
|
|
type PublicKeyAWSStorage struct {
|
|
secretsManagerClient SecretsManagerClient
|
|
secretsManagerCache SecretsManagerCache
|
|
}
|
|
|
|
type publicKeyStorage interface {
|
|
StorePublicKey(name godigest.Digest, publicKeyContent []byte) error
|
|
GetPublicKeyVerifier(name string) (sigstoreSigs.Verifier, []byte, error)
|
|
GetPublicKeys() ([]string, error)
|
|
}
|
|
|
|
func NewPublicKeyLocalStorage(rootDir string) (*PublicKeyLocalStorage, error) {
|
|
dir := path.Join(rootDir, cosignDirRelativePath)
|
|
|
|
_, err := os.Stat(dir)
|
|
if os.IsNotExist(err) {
|
|
err = os.MkdirAll(dir, defaultDirPerms)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &PublicKeyLocalStorage{
|
|
cosignDir: dir,
|
|
}, nil
|
|
}
|
|
|
|
func NewPublicKeyAWSStorage(
|
|
secretsManagerClient SecretsManagerClient, secretsManagerCache SecretsManagerCache,
|
|
) *PublicKeyAWSStorage {
|
|
return &PublicKeyAWSStorage{
|
|
secretsManagerClient: secretsManagerClient,
|
|
secretsManagerCache: secretsManagerCache,
|
|
}
|
|
}
|
|
|
|
func (local *PublicKeyLocalStorage) GetCosignDirPath() (string, error) {
|
|
if local.cosignDir != "" {
|
|
return local.cosignDir, nil
|
|
}
|
|
|
|
return "", zerr.ErrSignConfigDirNotSet
|
|
}
|
|
|
|
func VerifyCosignSignature(
|
|
cosignStorage publicKeyStorage, repo string, digest godigest.Digest, signatureKey string, layerContent []byte,
|
|
) (string, bool, error) {
|
|
publicKeys, err := cosignStorage.GetPublicKeys()
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
for _, publicKey := range publicKeys {
|
|
// cosign verify the image
|
|
pubKeyVerifier, pubKeyContent, err := cosignStorage.GetPublicKeyVerifier(publicKey)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
pkcs11Key, ok := pubKeyVerifier.(*pkcs11key.Key)
|
|
if ok {
|
|
defer pkcs11Key.Close()
|
|
}
|
|
|
|
verifier := pubKeyVerifier
|
|
|
|
b64sig := signatureKey
|
|
|
|
signature, err := base64.StdEncoding.DecodeString(b64sig)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
compressed := io.NopCloser(bytes.NewReader(layerContent))
|
|
|
|
payload, err := io.ReadAll(compressed)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
err = verifier.VerifySignature(bytes.NewReader(signature), bytes.NewReader(payload),
|
|
options.WithContext(context.Background()))
|
|
|
|
if err == nil {
|
|
return string(pubKeyContent), true, nil
|
|
}
|
|
}
|
|
|
|
return "", false, nil
|
|
}
|
|
|
|
func (local *PublicKeyLocalStorage) GetPublicKeyVerifier(fileName string) (sigstoreSigs.Verifier, []byte, error) {
|
|
cosignDir, err := local.GetCosignDirPath()
|
|
if err != nil {
|
|
return nil, []byte{}, err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
keyRef := path.Join(cosignDir, fileName)
|
|
hashAlgorithm := crypto.SHA256
|
|
|
|
pubKeyContent, err := os.ReadFile(keyRef)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
pubKey, err := sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, hashAlgorithm)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return pubKey, pubKeyContent, nil
|
|
}
|
|
|
|
func (cloud *PublicKeyAWSStorage) GetPublicKeyVerifier(secretName string) (sigstoreSigs.Verifier, []byte, error) {
|
|
hashAlgorithm := crypto.SHA256
|
|
|
|
// get key
|
|
raw, err := cloud.secretsManagerCache.GetSecretString(secretName)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
rawDecoded, err := base64.StdEncoding.DecodeString(raw)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// PEM encoded file.
|
|
key, err := cryptoutils.UnmarshalPEMToPublicKey(rawDecoded)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
pubKey, err := sigstoreSigs.LoadVerifier(key, hashAlgorithm)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return pubKey, rawDecoded, nil
|
|
}
|
|
|
|
func (local *PublicKeyLocalStorage) GetPublicKeys() ([]string, error) {
|
|
cosignDir, err := local.GetCosignDirPath()
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
files, err := os.ReadDir(cosignDir)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
publicKeys := []string{}
|
|
for _, file := range files {
|
|
publicKeys = append(publicKeys, file.Name())
|
|
}
|
|
|
|
return publicKeys, nil
|
|
}
|
|
|
|
func (cloud *PublicKeyAWSStorage) GetPublicKeys() ([]string, error) {
|
|
ctx := context.Background()
|
|
listSecretsInput := secretsmanager.ListSecretsInput{
|
|
Filters: []types.Filter{
|
|
{
|
|
Key: types.FilterNameStringTypeDescription,
|
|
Values: []string{"cosign public key"},
|
|
},
|
|
},
|
|
}
|
|
|
|
secrets, err := cloud.secretsManagerClient.ListSecrets(ctx, &listSecretsInput)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
publicKeys := []string{}
|
|
|
|
for _, secret := range secrets.SecretList {
|
|
publicKeys = append(publicKeys, *(secret.Name))
|
|
}
|
|
|
|
return publicKeys, nil
|
|
}
|
|
|
|
func UploadPublicKey(cosignStorage publicKeyStorage, publicKeyContent []byte) error {
|
|
// validate public key
|
|
if ok, err := validatePublicKey(publicKeyContent); !ok {
|
|
return err
|
|
}
|
|
|
|
name := godigest.FromBytes(publicKeyContent)
|
|
|
|
return cosignStorage.StorePublicKey(name, publicKeyContent)
|
|
}
|
|
|
|
func (local *PublicKeyLocalStorage) StorePublicKey(name godigest.Digest, publicKeyContent []byte) error {
|
|
// add public key to "{rootDir}/_cosign/{name.pub}"
|
|
cosignDir, err := local.GetCosignDirPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// store public key
|
|
publicKeyPath := path.Join(cosignDir, name.String())
|
|
|
|
return os.WriteFile(publicKeyPath, publicKeyContent, defaultFilePerms)
|
|
}
|
|
|
|
func (cloud *PublicKeyAWSStorage) StorePublicKey(name godigest.Digest, publicKeyContent []byte) error {
|
|
n := name.Encoded()
|
|
description := "cosign public key"
|
|
secret := base64.StdEncoding.EncodeToString(publicKeyContent)
|
|
secretInputParam := &secretsmanager.CreateSecretInput{
|
|
Name: &n,
|
|
Description: &description,
|
|
SecretString: &secret,
|
|
}
|
|
|
|
_, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validatePublicKey(publicKeyContent []byte) (bool, error) {
|
|
_, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyContent)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%w: %w", zerr.ErrInvalidPublicKeyContent, err)
|
|
}
|
|
|
|
return true, nil
|
|
}
|