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 }