1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package util
17
18 import (
19 "bytes"
20 "context"
21 "encoding/base64"
22 "errors"
23 "fmt"
24 "strconv"
25 "strings"
26
27 "github.com/sigstore/sigstore/pkg/signature"
28 "github.com/sigstore/sigstore/pkg/signature/options"
29 )
30
31
32
33 type Checkpoint struct {
34
35 Origin string
36
37 Size uint64
38
39 Hash []byte
40
41 OtherContent []string
42 }
43
44
45 func (c Checkpoint) String() string {
46 var b strings.Builder
47 fmt.Fprintf(&b, "%s\n%d\n%s\n", c.Origin, c.Size, base64.StdEncoding.EncodeToString(c.Hash))
48 for _, line := range c.OtherContent {
49 fmt.Fprintf(&b, "%s\n", line)
50 }
51 return b.String()
52 }
53
54
55 func (c Checkpoint) MarshalCheckpoint() ([]byte, error) {
56 return []byte(c.String()), nil
57 }
58
59
60
61
62
63
64
65
66
67
68
69
70
71 func (c *Checkpoint) UnmarshalCheckpoint(data []byte) error {
72 l := bytes.Split(data, []byte("\n"))
73 if len(l) < 4 {
74 return errors.New("invalid checkpoint - too few newlines")
75 }
76 origin := string(l[0])
77 if len(origin) == 0 {
78 return errors.New("invalid checkpoint - empty ecosystem")
79 }
80 size, err := strconv.ParseUint(string(l[1]), 10, 64)
81 if err != nil {
82 return fmt.Errorf("invalid checkpoint - size invalid: %w", err)
83 }
84 h, err := base64.StdEncoding.DecodeString(string(l[2]))
85 if err != nil {
86 return fmt.Errorf("invalid checkpoint - invalid hash: %w", err)
87 }
88 *c = Checkpoint{
89 Origin: origin,
90 Size: size,
91 Hash: h,
92 }
93 if len(l) >= 3 {
94 for _, line := range l[3:] {
95 if len(line) == 0 {
96 break
97 }
98 c.OtherContent = append(c.OtherContent, string(line))
99 }
100 }
101 return nil
102 }
103
104 type SignedCheckpoint struct {
105 Checkpoint
106 SignedNote
107 }
108
109 func CreateSignedCheckpoint(c Checkpoint) (*SignedCheckpoint, error) {
110 text, err := c.MarshalCheckpoint()
111 if err != nil {
112 return nil, err
113 }
114 return &SignedCheckpoint{
115 Checkpoint: c,
116 SignedNote: SignedNote{Note: string(text)},
117 }, nil
118 }
119
120 func SignedCheckpointValidator(strToValidate string) bool {
121 s := SignedNote{}
122 if err := s.UnmarshalText([]byte(strToValidate)); err != nil {
123 return false
124 }
125 c := &Checkpoint{}
126 return c.UnmarshalCheckpoint([]byte(s.Note)) == nil
127 }
128
129 func CheckpointValidator(strToValidate string) bool {
130 c := &Checkpoint{}
131 return c.UnmarshalCheckpoint([]byte(strToValidate)) == nil
132 }
133
134 func (r *SignedCheckpoint) UnmarshalText(data []byte) error {
135 s := SignedNote{}
136 if err := s.UnmarshalText([]byte(data)); err != nil {
137 return fmt.Errorf("unmarshalling signed note: %w", err)
138 }
139 c := Checkpoint{}
140 if err := c.UnmarshalCheckpoint([]byte(s.Note)); err != nil {
141 return fmt.Errorf("unmarshalling checkpoint: %w", err)
142 }
143 *r = SignedCheckpoint{Checkpoint: c, SignedNote: s}
144 return nil
145 }
146
147
148 func CreateAndSignCheckpoint(ctx context.Context, hostname string, treeID int64, treeSize uint64, rootHash []byte, signer signature.Signer) ([]byte, error) {
149 sth, err := CreateSignedCheckpoint(Checkpoint{
150 Origin: fmt.Sprintf("%s - %d", hostname, treeID),
151 Size: treeSize,
152 Hash: rootHash,
153 })
154 if err != nil {
155 return nil, fmt.Errorf("error creating checkpoint: %w", err)
156 }
157 if _, err := sth.Sign(hostname, signer, options.WithContext(ctx)); err != nil {
158 return nil, fmt.Errorf("error signing checkpoint: %w", err)
159 }
160 scBytes, err := sth.SignedNote.MarshalText()
161 if err != nil {
162 return nil, fmt.Errorf("error marshalling checkpoint: %w", err)
163 }
164 return scBytes, nil
165 }
166
View as plain text