1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package util
17
18 import (
19 "bufio"
20 "bytes"
21 "crypto/ecdsa"
22 "crypto/ed25519"
23 "crypto/rsa"
24 "crypto/sha256"
25 "crypto/x509"
26 "encoding/base64"
27 "encoding/binary"
28 "errors"
29 "fmt"
30 "strings"
31
32 "github.com/sigstore/sigstore/pkg/signature"
33 "github.com/sigstore/sigstore/pkg/signature/options"
34 "golang.org/x/mod/sumdb/note"
35 )
36
37 type SignedNote struct {
38
39 Note string
40
41 Signatures []note.Signature
42 }
43
44
45
46 func (s *SignedNote) Sign(identity string, signer signature.Signer, opts signature.SignOption) (*note.Signature, error) {
47 sig, err := signer.SignMessage(bytes.NewReader([]byte(s.Note)), opts)
48 if err != nil {
49 return nil, fmt.Errorf("signing note: %w", err)
50 }
51
52 pk, err := signer.PublicKey()
53 if err != nil {
54 return nil, fmt.Errorf("retrieving public key: %w", err)
55 }
56 pubKeyBytes, err := x509.MarshalPKIXPublicKey(pk)
57 if err != nil {
58 return nil, fmt.Errorf("marshalling public key: %w", err)
59 }
60
61 pkSha := sha256.Sum256(pubKeyBytes)
62
63 signature := note.Signature{
64 Name: identity,
65 Hash: binary.BigEndian.Uint32(pkSha[:]),
66 Base64: base64.StdEncoding.EncodeToString(sig),
67 }
68
69 s.Signatures = append(s.Signatures, signature)
70 return &signature, nil
71 }
72
73
74
75 func (s SignedNote) Verify(verifier signature.Verifier) bool {
76 if len(s.Signatures) == 0 {
77 return false
78 }
79
80 msg := []byte(s.Note)
81 digest := sha256.Sum256(msg)
82
83 for _, s := range s.Signatures {
84 sigBytes, err := base64.StdEncoding.DecodeString(s.Base64)
85 if err != nil {
86 return false
87 }
88 pk, err := verifier.PublicKey()
89 if err != nil {
90 return false
91 }
92 opts := []signature.VerifyOption{}
93 switch pk.(type) {
94 case *rsa.PublicKey, *ecdsa.PublicKey:
95 opts = append(opts, options.WithDigest(digest[:]))
96 case ed25519.PublicKey:
97 break
98 default:
99 return false
100 }
101 if err := verifier.VerifySignature(bytes.NewReader(sigBytes), bytes.NewReader(msg), opts...); err != nil {
102 return false
103 }
104 }
105 return true
106 }
107
108
109 func (s SignedNote) MarshalText() ([]byte, error) {
110 return []byte(s.String()), nil
111 }
112
113
114 func (s SignedNote) String() string {
115 var b strings.Builder
116 b.WriteString(s.Note)
117 b.WriteRune('\n')
118 for _, sig := range s.Signatures {
119 var hbuf [4]byte
120 binary.BigEndian.PutUint32(hbuf[:], sig.Hash)
121 sigBytes, _ := base64.StdEncoding.DecodeString(sig.Base64)
122 b64 := base64.StdEncoding.EncodeToString(append(hbuf[:], sigBytes...))
123 fmt.Fprintf(&b, "%c %s %s\n", '\u2014', sig.Name, b64)
124 }
125
126 return b.String()
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141 func (s *SignedNote) UnmarshalText(data []byte) error {
142 sigSplit := []byte("\n\n")
143
144 split := bytes.LastIndex(data, sigSplit)
145 if split < 0 {
146 return errors.New("malformed note")
147 }
148 text, data := data[:split+1], data[split+2:]
149 if len(data) == 0 || data[len(data)-1] != '\n' {
150 return errors.New("malformed note")
151 }
152
153 sn := SignedNote{
154 Note: string(text),
155 }
156
157 b := bufio.NewScanner(bytes.NewReader(data))
158 for b.Scan() {
159 var name, signature string
160 if _, err := fmt.Fscanf(strings.NewReader(b.Text()), "\u2014 %s %s\n", &name, &signature); err != nil {
161 return fmt.Errorf("parsing signature: %w", err)
162 }
163
164 sigBytes, err := base64.StdEncoding.DecodeString(signature)
165 if err != nil {
166 return fmt.Errorf("decoding signature: %w", err)
167 }
168 if len(sigBytes) < 5 {
169 return errors.New("signature is too small")
170 }
171
172 sig := note.Signature{
173 Name: name,
174 Hash: binary.BigEndian.Uint32(sigBytes[0:4]),
175 Base64: base64.StdEncoding.EncodeToString(sigBytes[4:]),
176 }
177 sn.Signatures = append(sn.Signatures, sig)
178
179 }
180 if len(sn.Signatures) == 0 {
181 return errors.New("no signatures found in input")
182 }
183
184
185 *s = sn
186 return nil
187 }
188
189 func SignedNoteValidator(strToValidate string) bool {
190 s := SignedNote{}
191 return s.UnmarshalText([]byte(strToValidate)) == nil
192 }
193
View as plain text