1
1
package cli
2
2
3
3
import (
4
+ "context"
5
+ "crypto/x509"
4
6
"fmt"
7
+ "io"
5
8
"os"
9
+ "os/exec"
10
+ "path/filepath"
11
+ "strings"
6
12
13
+ cms "github.com/github/smimesign/ietf-cms"
7
14
"github.com/spf13/cobra"
8
15
"golang.org/x/xerrors"
9
16
17
+ "cdr.dev/slog"
10
18
"github.com/coder/code-marketplace/extensionsign"
19
+ "github.com/coder/code-marketplace/storage/easyzip"
11
20
)
12
21
13
22
func signature () * cobra.Command {
@@ -17,10 +26,187 @@ func signature() *cobra.Command {
17
26
Hidden : true , // Debugging tools
18
27
Aliases : []string {"sig" , "sigs" , "signatures" },
19
28
}
20
- cmd .AddCommand (compareSignatureSigZips ())
29
+
30
+ cmd .AddCommand (compareSignatureSigZips (), verifySig ())
31
+ return cmd
32
+ }
33
+
34
+ func verifySig () * cobra.Command {
35
+ cmd := & cobra.Command {
36
+ Use : "verify" ,
37
+ Short : "Decode & verify a signature archive." ,
38
+ Args : cobra .ExactArgs (2 ),
39
+ RunE : func (cmd * cobra.Command , args []string ) error {
40
+ logger := cmdLogger (cmd )
41
+ ctx := cmd .Context ()
42
+ extensionVsix := args [0 ]
43
+ p7sFile := args [1 ]
44
+
45
+ logger .Info (ctx , fmt .Sprintf ("Decoding %q" , p7sFile ))
46
+
47
+ data , err := os .ReadFile (p7sFile )
48
+ if err != nil {
49
+ return xerrors .Errorf ("read %q: %w" , p7sFile , err )
50
+ }
51
+
52
+ msg , err := easyzip .GetZipFileReader (data , ".signature.manifest" )
53
+ if err != nil {
54
+ return xerrors .Errorf ("get manifest: %w" , err )
55
+ }
56
+ msgData , err := io .ReadAll (msg )
57
+ if err != nil {
58
+ return xerrors .Errorf ("read manifest: %w" , err )
59
+ }
60
+
61
+ signed , err := extensionsign .ExtractP7SSig (data )
62
+ if err != nil {
63
+ return xerrors .Errorf ("extract p7s: %w" , err )
64
+ }
65
+
66
+ fmt .Println ("----------------Golang Verify----------------" )
67
+ valid , err := goVerify (ctx , logger , msgData , signed )
68
+ if err != nil {
69
+ logger .Error (ctx , "go verify" , slog .Error (err ))
70
+ }
71
+ logger .Info (ctx , fmt .Sprintf ("Valid: %t" , valid ))
72
+
73
+ fmt .Println ("----------------OpenSSL Verify----------------" )
74
+ valid , err = openSSLVerify (ctx , logger , msgData , signed )
75
+ if err != nil {
76
+ logger .Error (ctx , "openssl verify" , slog .Error (err ))
77
+ }
78
+ logger .Info (ctx , fmt .Sprintf ("Valid: %t" , valid ))
79
+
80
+ fmt .Println ("----------------vsce-sign Verify----------------" )
81
+ valid , err = vsceSignVerify (ctx , logger , extensionVsix , p7sFile )
82
+ if err != nil {
83
+ logger .Error (ctx , "openssl verify" , slog .Error (err ))
84
+ }
85
+ logger .Info (ctx , fmt .Sprintf ("Valid: %t" , valid ))
86
+
87
+ return nil
88
+ },
89
+ }
21
90
return cmd
22
91
}
23
92
93
+ func goVerify (ctx context.Context , logger slog.Logger , message []byte , signature []byte ) (bool , error ) {
94
+ sd , err := cms .ParseSignedData (signature )
95
+ if err != nil {
96
+ return false , xerrors .Errorf ("new signed data: %w" , err )
97
+ }
98
+
99
+ fmt .Println ("Detached:" , sd .IsDetached ())
100
+ certs , err := sd .GetCertificates ()
101
+ if err != nil {
102
+ return false , xerrors .Errorf ("get certs: %w" , err )
103
+ }
104
+ fmt .Println ("Certificates:" , len (certs ))
105
+
106
+ sdData , err := sd .GetData ()
107
+ if err != nil {
108
+ return false , xerrors .Errorf ("get data: %w" , err )
109
+ }
110
+ fmt .Println ("Data:" , len (sdData ))
111
+
112
+ var verifyErr error
113
+ var vcerts [][][]* x509.Certificate
114
+
115
+ sys , err := x509 .SystemCertPool ()
116
+ if err != nil {
117
+ return false , xerrors .Errorf ("system cert pool: %w" , err )
118
+ }
119
+ opts := x509.VerifyOptions {
120
+ Intermediates : sys ,
121
+ Roots : sys ,
122
+ }
123
+
124
+ if sd .IsDetached () {
125
+ vcerts , verifyErr = sd .VerifyDetached (message , opts )
126
+ } else {
127
+ vcerts , verifyErr = sd .Verify (opts )
128
+ }
129
+ if verifyErr != nil {
130
+ logger .Error (ctx , "verify" , slog .Error (verifyErr ))
131
+ }
132
+
133
+ certChain := dimensions (vcerts )
134
+ fmt .Println (certChain )
135
+ return verifyErr == nil , nil
136
+ }
137
+
138
+ func openSSLVerify (ctx context.Context , logger slog.Logger , message []byte , signature []byte ) (bool , error ) {
139
+ // openssl cms -verify -in message_from_alice_for_bob.msg -inform DER -CAfile ehealth_root_ca.cer | openssl cms -decrypt -inform DER -recip bob_etk_pair.pem | openssl cms -inform DER -cmsout -print
140
+ tmpdir := os .TempDir ()
141
+ tmpdir = filepath .Join (tmpdir , "verify-sigs" )
142
+ defer os .RemoveAll (tmpdir )
143
+ os .MkdirAll (tmpdir , 0755 )
144
+ msgPath := filepath .Join (tmpdir , ".signature.manifest" )
145
+ err := os .WriteFile (msgPath , message , 0644 )
146
+ if err != nil {
147
+ return false , xerrors .Errorf ("write message: %w" , err )
148
+ }
149
+
150
+ sigPath := filepath .Join (tmpdir , ".signature.p7s" )
151
+ err = os .WriteFile (sigPath , signature , 0644 )
152
+ if err != nil {
153
+ return false , xerrors .Errorf ("write signature: %w" , err )
154
+ }
155
+
156
+ cmd := exec .CommandContext (ctx , "openssl" , "smime" , "-verify" ,
157
+ "-in" , sigPath , "-content" , msgPath , "-inform" , "DER" ,
158
+ "-CAfile" , "/home/steven/go/src/github.com/coder/code-marketplace/extensionsign/testdata/cert2.pem" ,
159
+ )
160
+ output := & strings.Builder {}
161
+ cmd .Stdout = output
162
+ cmd .Stderr = output
163
+ err = cmd .Run ()
164
+ fmt .Println (output .String ())
165
+ if err != nil {
166
+ return false , xerrors .Errorf ("run verify %q: %w" , cmd .String (), err )
167
+ }
168
+
169
+ return cmd .ProcessState .ExitCode () == 0 , nil
170
+ }
171
+
172
+ func vsceSignVerify (ctx context.Context , logger slog.Logger , vsixPath , sigPath string ) (bool , error ) {
173
+ bin := os .Getenv ("VSCE_SIGN_PATH" )
174
+ if bin == "" {
175
+ return false , xerrors .Errorf ("VSCE_SIGN_PATH not set" )
176
+ }
177
+
178
+ cmd := exec .CommandContext (ctx , bin , "verify" ,
179
+ "--package" , vsixPath ,
180
+ "--signaturearchive" , sigPath ,
181
+ "-v" ,
182
+ )
183
+ fmt .Println (cmd .String ())
184
+ output := & strings.Builder {}
185
+ cmd .Stdout = output
186
+ cmd .Stderr = output
187
+ err := cmd .Run ()
188
+ fmt .Println (output .String ())
189
+ if err != nil {
190
+ return false , xerrors .Errorf ("run verify %q: %w" , cmd .String (), err )
191
+ }
192
+
193
+ return cmd .ProcessState .ExitCode () == 0 , nil
194
+ }
195
+
196
+ func dimensions (chain [][][]* x509.Certificate ) string {
197
+ var str strings.Builder
198
+ for _ , top := range chain {
199
+ str .WriteString (fmt .Sprintf ("Chain, len=%d\n " , len (top )))
200
+ for _ , second := range top {
201
+ str .WriteString (fmt .Sprintf (" Certs len=%d\n " , len (second )))
202
+ for _ , cert := range second {
203
+ str .WriteString (fmt .Sprintf (" Cert: %s\n " , cert .Subject ))
204
+ }
205
+ }
206
+ }
207
+ return str .String ()
208
+ }
209
+
24
210
func compareSignatureSigZips () * cobra.Command {
25
211
cmd := & cobra.Command {
26
212
Use : "compare" ,
0 commit comments