mirror of
https://github.com/project-zot/zot.git
synced 2025-01-06 22:40:28 -05:00
41b05c60dd
In order to verify signatures, users could upload their certificates and public keys using these routes: -> for public keys: /v2/_zot/ext/mgmt?resource=signatures&tool=cosign -> for certificates: /v2/_zot/ext/mgmt?resource=signatures&tool=notation&truststoreType=ca&truststoreName=name Then the public keys will be stored under $rootdir/_cosign and the certificates will be stored under $rootdir/_notation/truststore/x509/$truststoreType/$truststoreName. Also, for notation case, the "truststores" field of $rootir/_notation/trustpolicy.json file will be updated with a new entry "$truststoreType:$truststoreName". Also based on the uploaded files, the information about the signatures validity will be updated periodically. Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com>
143 lines
2.8 KiB
Go
143 lines
2.8 KiB
Go
package signatures
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto"
|
|
"encoding/base64"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
|
|
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"
|
|
"github.com/sigstore/sigstore/pkg/signature/options"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
)
|
|
|
|
const (
|
|
CosignSigKey = "dev.cosignproject.cosign/signature"
|
|
cosignDirRelativePath = "_cosign"
|
|
)
|
|
|
|
var cosignDir = "" //nolint:gochecknoglobals
|
|
|
|
func InitCosignDir(rootDir string) error {
|
|
dir := path.Join(rootDir, cosignDirRelativePath)
|
|
|
|
_, err := os.Stat(dir)
|
|
if os.IsNotExist(err) {
|
|
err = os.MkdirAll(dir, defaultDirPerms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err == nil {
|
|
cosignDir = dir
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func GetCosignDirPath() (string, error) {
|
|
if cosignDir != "" {
|
|
return cosignDir, nil
|
|
}
|
|
|
|
return "", zerr.ErrSignConfigDirNotSet
|
|
}
|
|
|
|
func VerifyCosignSignature(
|
|
repo string, digest godigest.Digest, signatureKey string, layerContent []byte,
|
|
) (string, bool, error) {
|
|
cosignDir, err := GetCosignDirPath()
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
files, err := os.ReadDir(cosignDir)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
// cosign verify the image
|
|
ctx := context.Background()
|
|
keyRef := path.Join(cosignDir, file.Name())
|
|
hashAlgorithm := crypto.SHA256
|
|
|
|
pubKey, err := sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, hashAlgorithm)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
pkcs11Key, ok := pubKey.(*pkcs11key.Key)
|
|
if ok {
|
|
defer pkcs11Key.Close()
|
|
}
|
|
|
|
verifier := pubKey
|
|
|
|
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(ctx))
|
|
|
|
if err == nil {
|
|
publicKey, err := os.ReadFile(keyRef)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
return string(publicKey), true, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return "", false, nil
|
|
}
|
|
|
|
func UploadPublicKey(publicKeyContent []byte) error {
|
|
// validate public key
|
|
if ok, err := validatePublicKey(publicKeyContent); !ok {
|
|
return err
|
|
}
|
|
|
|
// add public key to "{rootDir}/_cosign/{name.pub}"
|
|
configDir, err := GetCosignDirPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
name := godigest.FromBytes(publicKeyContent)
|
|
|
|
// store public key
|
|
publicKeyPath := path.Join(configDir, name.String())
|
|
|
|
return os.WriteFile(publicKeyPath, publicKeyContent, defaultFilePerms)
|
|
}
|
|
|
|
func validatePublicKey(publicKeyContent []byte) (bool, error) {
|
|
_, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyContent)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|