-
Notifications
You must be signed in to change notification settings - Fork 6
/
cosign.go
135 lines (117 loc) · 3.98 KB
/
cosign.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package testutil
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"strings"
"github.com/DataDog/go-tuf/encrypted"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/verify"
"github.com/sigstore/cosign/v2/pkg/cosign"
)
// resolveImage gets a image and returns its resolved tag version
func resolveImage(image string, opts ...crane.Option) (string, error) {
o := crane.GetOptions(opts...)
ref, err := name.ParseReference(image)
if err != nil {
return "", fmt.Errorf("failed to parse image reference: %w", err)
}
switch v := ref.(type) {
case name.Tag:
desc, err := remote.Get(ref, o.Remote...)
if err != nil {
return "", fmt.Errorf("failed to get remote descriptor: %w", err)
}
return fmt.Sprintf("%s@%s", ref.Context().Name(), desc.Digest), nil
case name.Digest:
// We already got a digest
return image, nil
default:
return "", fmt.Errorf("unsupported reference type %T", v)
}
}
// CosignImage signs a remote artifact with the provided key
func CosignImage(url string, key string, opts ...crane.Option) error {
o := crane.GetOptions(opts...)
url = strings.TrimPrefix(url, "oci://")
// cosign complains if we sign a tag with
// WARNING: Image reference 127.0.0.1/test:mytag uses a tag, not a digest, to identify the image to sign.
image, err := resolveImage(url, opts...)
if err != nil {
return fmt.Errorf("failed to sign %q: %v", url, err)
}
return sign.SignCmd(
&options.RootOptions{Timeout: options.DefaultTimeout, Verbose: false},
options.KeyOpts{KeyRef: key},
options.SignOptions{Upload: true, Registry: options.RegistryOptions{RegistryClientOpts: o.Remote}},
[]string{image},
)
}
// CosignVerifyImage verifies a remote artifact signature with the provided key
func CosignVerifyImage(url string, key string, opts ...crane.Option) error {
o := crane.GetOptions(opts...)
url = strings.TrimPrefix(url, "oci://")
v := &verify.VerifyCommand{
RegistryOptions: options.RegistryOptions{RegistryClientOpts: o.Remote},
KeyRef: key,
IgnoreTlog: true,
}
v.NameOptions = append(v.NameOptions, name.Insecure)
ctx := context.Background()
return v.Exec(ctx, []string{url})
}
func writeTempFile(dir, name string, data []byte) (*os.File, error) {
fh, err := os.CreateTemp(dir, name)
if err != nil {
return nil, fmt.Errorf("failed to create temp file: %v", err)
}
defer fh.Close()
if _, err := fh.Write(data); err != nil {
return nil, err
}
return fh, nil
}
// GenerateCosignCertificateFiles generates sample signing keys for usage with cosign
func GenerateCosignCertificateFiles(tmpDir string) (privFile, pubFile string, err error) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return "", "", err
}
encodedPub, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
if err != nil {
return "", "", fmt.Errorf("failed to encode public key: %v", err)
}
encodedPriv, err := x509.MarshalPKCS8PrivateKey(privKey)
if err != nil {
return "", "", fmt.Errorf("failed to encode private key: %v", err)
}
password := []byte{}
encryptedPrivBytes, err := encrypted.Encrypt(encodedPriv, password)
if err != nil {
return "", "", fmt.Errorf("failed to encrypt key: %v", err)
}
privKeyFile, err := writeTempFile(tmpDir, "cosign_test_*.key", pem.EncodeToMemory(&pem.Block{
Bytes: encryptedPrivBytes,
Type: cosign.CosignPrivateKeyPemType,
}))
if err != nil {
return "", "", fmt.Errorf("failed to create temp key file: %v", err)
}
pubKeyFile, err := writeTempFile(tmpDir, "cosign_test_*.pub", pem.EncodeToMemory(&pem.Block{
Bytes: encodedPub,
Type: "PUBLIC KEY",
}))
if err != nil {
return "", "", fmt.Errorf("failed to write pub key file: %v", err)
}
return privKeyFile.Name(), pubKeyFile.Name(), nil
}