@@ -16,6 +16,8 @@ import (
16
16
)
17
17
18
18
func main () {
19
+ verifyFlag := flag .String ("verify" , "" ,
20
+ "verify the file's spicy signature with the given public key" )
19
21
keyFlag := flag .String ("key" , "" ,
20
22
"the log's private key path (written by -init)" )
21
23
initFlag := flag .String ("init" , "" ,
@@ -24,6 +26,71 @@ func main() {
24
26
"directory where log entries and metadata are stored" )
25
27
flag .Parse ()
26
28
29
+ if * verifyFlag != "" {
30
+ if len (flag .Args ()) == 0 {
31
+ log .Fatalf ("no files to verify" )
32
+ }
33
+ vkey , err := note .NewVerifier (* verifyFlag )
34
+ if err != nil {
35
+ log .Fatalf ("could not parse public key: %v" , err )
36
+ }
37
+ for _ , path := range flag .Args () {
38
+ f , err := os .ReadFile (path )
39
+ if err != nil {
40
+ log .Fatalf ("could not read %q: %v" , path , err )
41
+ }
42
+ sig , err := os .ReadFile (path + ".spicy" )
43
+ if err != nil {
44
+ log .Fatalf ("could not read %q: %v" , path + ".spicy" , err )
45
+ }
46
+ s := string (sig )
47
+ s , ok := strings .CutPrefix (s , "index " )
48
+ if ! ok {
49
+ log .Fatalf ("malformed spicy signature for %q" , path )
50
+ }
51
+ i , s , ok := strings .Cut (s , "\n " )
52
+ if ! ok {
53
+ log .Fatalf ("malformed spicy signature for %q" , path )
54
+ }
55
+ index , err := strconv .ParseInt (i , 10 , 64 )
56
+ if err != nil {
57
+ log .Fatalf ("malformed spicy signature for %q: %v" , path , err )
58
+ }
59
+ var proof tlog.RecordProof
60
+ for {
61
+ var h string
62
+ h , s , ok = strings .Cut (s , "\n " )
63
+ if ! ok {
64
+ log .Fatalf ("malformed spicy signature for %q" , path )
65
+ }
66
+ if h == "" {
67
+ break
68
+ }
69
+ hh , err := tlog .ParseHash (h )
70
+ if err != nil {
71
+ log .Fatalf ("malformed spicy signature for %q: %v" , path , err )
72
+ }
73
+ proof = append (proof , hh )
74
+ }
75
+ m , err := note .Open ([]byte (s ), note .VerifierList (vkey ))
76
+ if err != nil {
77
+ log .Fatalf ("could not verify checkpoint for %q: %v" , path , err )
78
+ }
79
+ c , err := tlogx .ParseCheckpoint (m .Text )
80
+ if err != nil {
81
+ log .Fatalf ("could not parse checkpoint for %q: %v" , path , err )
82
+ }
83
+ if c .Origin != vkey .Name () {
84
+ log .Fatalf ("spicy signature for %q is for a different log: got %q, want %q" , path , c .Origin , vkey .Name ())
85
+ }
86
+ if err := tlog .CheckRecord (proof , c .N , c .Hash , index , tlog .RecordHash (f )); err != nil {
87
+ log .Fatalf ("could not verify inclusion for %q: %v" , path , err )
88
+ }
89
+ }
90
+ fmt .Fprintf (os .Stderr , "Spicy signature(s) verified! 🌶️\n " )
91
+ return
92
+ }
93
+
27
94
if * initFlag != "" {
28
95
latestPath := filepath .Join (* assetsFlag , "latest" )
29
96
if _ , err := os .Stat (latestPath ); err == nil {
0 commit comments