11package cli
22
33import (
4+ "context"
5+ "crypto/x509"
46 "fmt"
7+ "io"
58 "os"
9+ "os/exec"
10+ "path/filepath"
11+ "strings"
612
13+ cms "github.com/github/smimesign/ietf-cms"
714 "github.com/spf13/cobra"
815 "golang.org/x/xerrors"
916
17+ "cdr.dev/slog"
1018 "github.com/coder/code-marketplace/extensionsign"
19+ "github.com/coder/code-marketplace/storage/easyzip"
1120)
1221
1322func signature () * cobra.Command {
@@ -17,10 +26,187 @@ func signature() *cobra.Command {
1726 Hidden : true , // Debugging tools
1827 Aliases : []string {"sig" , "sigs" , "signatures" },
1928 }
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+ }
2190 return cmd
2291}
2392
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+
24210func compareSignatureSigZips () * cobra.Command {
25211 cmd := & cobra.Command {
26212 Use : "compare" ,
0 commit comments