diff --git a/cmd/verify.go b/cmd/verify.go index 8f78ce6..256f7ae 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -25,7 +25,7 @@ func su3VerifyAction(c *cli.Context) { } defer file.Close() - su3File, err := su3.ReadSu3(file) + su3File, err := su3.Parse(file) if err != nil { panic(err) } @@ -36,5 +36,5 @@ func su3VerifyAction(c *cli.Context) { panic(err) } - fmt.Println("Verified signature.") + fmt.Printf("Signature is valid for signer '%s'\n", su3File.SignerId) } diff --git a/cmd/verify_public.go b/cmd/verify_public.go new file mode 100644 index 0000000..7015201 --- /dev/null +++ b/cmd/verify_public.go @@ -0,0 +1,102 @@ +package cmd + +import ( + "crypto/tls" + "fmt" + "log" + "net/http" + "sync" + + "github.com/MDrollette/go-i2p/su3" + "github.com/codegangsta/cli" +) + +func NewSu3VerifyPublicCommand() cli.Command { + return cli.Command{ + Name: "vp", + Usage: "Verify all publicly listed reseed servers", + Description: "Verify all publicly listed reseed servers", + Action: su3VerifyPublicAction, + Flags: []cli.Flag{}, + } +} + +func su3VerifyPublicAction(c *cli.Context) { + public_servers := []string{ + "https://reseed.i2p-projekt.de/", + "https://cowpuncher.drollette.com/netdb/", + "https://i2p.mooo.com/netDb/", + "https://193.150.121.66/netDb/", + "https://netdb.i2p2.no/", + "https://reseed.info/", + "https://us.reseed.i2p2.no:444/", + "https://uk.reseed.i2p2.no:444/", + "https://i2p-netdb.innovatio.no/", + "https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/", + "https://link.mx24.eu/", + "https://ieb9oopo.mooo.com/", + } + + pipe := make(chan *http.Response) + + // Kick off goroutines to download the URLs + go download(pipe, public_servers) + + // Process them serially + validate(pipe) +} + +func download(out chan *http.Response, urls []string) { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := http.Client{Transport: tr} + + var wg sync.WaitGroup + for _, url := range urls { + wg.Add(1) + go func(url string) { + defer wg.Done() + + req, err := http.NewRequest("GET", fmt.Sprintf("%si2pseeds.su3", url), nil) + if err != nil { + log.Fatalln(err) + } + req.Header.Set("User-Agent", "Wget/1.11.4") + resp, err := client.Do(req) + if err != nil { + log.Fatalln(err) + } + + out <- resp + }(url) + } + + wg.Wait() + close(out) +} + +func validate(in chan *http.Response) { + for resp := range in { + fmt.Printf("Validating: %s\n", resp.Request.URL) + + if resp.StatusCode != 200 { + fmt.Printf("Invalid: Response code: %d\n", resp.StatusCode) + fmt.Println("") + continue + } + + su3File, err := su3.Parse(resp.Body) + if err != nil { + fmt.Println("Invalid: Unable to parse SU3 file:", err) + } + resp.Body.Close() + + if err := su3File.VerifySignature(); nil != err { + fmt.Println("Invalid: Unable to verify signature", err) + } else { + fmt.Printf("Valid: For signer '%s'\n", su3File.SignerId) + } + fmt.Println("") + } +} diff --git a/main.go b/main.go index fb57d62..9941e8b 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,7 @@ func main() { cmd.NewSu3VerifyCommand(), cmd.NewKeygenCommand(), cmd.NewSu3Command(), + cmd.NewSu3VerifyPublicCommand(), } if err := app.Run(os.Args); err != nil { diff --git a/su3/su3.go b/su3/su3.go index d7a8ee1..bfdd8ce 100644 --- a/su3/su3.go +++ b/su3/su3.go @@ -9,7 +9,7 @@ import ( "crypto/x509" "encoding/binary" "fmt" - "os" + "io" "strconv" "time" ) @@ -79,7 +79,7 @@ func (s *Su3File) Sign(privkey *rsa.PrivateKey) error { } h := hashType.New() - h.Write(s.ContentBytes()) + h.Write(s.BodyBytes()) digest := h.Sum(nil) sig, err := rsa.SignPKCS1v15(rand.Reader, privkey, 0, digest) @@ -92,7 +92,7 @@ func (s *Su3File) Sign(privkey *rsa.PrivateKey) error { return nil } -func (s *Su3File) ContentBytes() []byte { +func (s *Su3File) BodyBytes() []byte { buf := new(bytes.Buffer) var ( @@ -100,7 +100,7 @@ func (s *Su3File) ContentBytes() []byte { bigSkip [12]byte versionLength = uint8(len(s.Version)) - signatureLength = uint16(40) + signatureLength = uint16(512) signerIdLength = uint8(len(s.SignerId)) contentLength = uint64(len(s.Content)) ) @@ -124,41 +124,23 @@ func (s *Su3File) ContentBytes() []byte { versionLength = uint8(len(s.Version)) } - // 0-5 binary.Write(buf, binary.BigEndian, MAGIC_BYTES) - // 6 binary.Write(buf, binary.BigEndian, skip) - // 7 binary.Write(buf, binary.BigEndian, s.Format) - // 8-9 binary.Write(buf, binary.BigEndian, s.SignatureType) - // 10-11 binary.Write(buf, binary.BigEndian, signatureLength) - // 12 binary.Write(buf, binary.BigEndian, skip) - // 13 binary.Write(buf, binary.BigEndian, versionLength) - // 14 binary.Write(buf, binary.BigEndian, skip) - // 15 binary.Write(buf, binary.BigEndian, signerIdLength) - // 16-23 binary.Write(buf, binary.BigEndian, contentLength) - // 24 binary.Write(buf, binary.BigEndian, skip) - // 25 binary.Write(buf, binary.BigEndian, s.FileType) - // 26 binary.Write(buf, binary.BigEndian, skip) - // 27 binary.Write(buf, binary.BigEndian, s.ContentType) - // 28-39 binary.Write(buf, binary.BigEndian, bigSkip) - // 40-55+ Version, UTF-8 padded with trailing 0x00, 16 bytes minimum, length specified at byte 13. Do not append 0x00 bytes if the length is 16 or more. binary.Write(buf, binary.BigEndian, s.Version) - // xx+ ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified at byte 15 binary.Write(buf, binary.BigEndian, s.SignerId) - // xx+ Content, length and format specified in header binary.Write(buf, binary.BigEndian, s.Content) return buf.Bytes() @@ -166,7 +148,7 @@ func (s *Su3File) ContentBytes() []byte { func (s *Su3File) Bytes() []byte { buf := new(bytes.Buffer) - buf.Write(s.ContentBytes()) + buf.Write(s.BodyBytes()) // xx+ Signature, length specified in header, covers everything starting at byte 0 binary.Write(buf, binary.BigEndian, s.Signature) @@ -198,7 +180,7 @@ func (s *Su3File) VerifySignature() error { if cert, err := signerCertificate(string(s.SignerId)); nil != err { return err } else { - return checkSignature(cert, sigAlg, s.ContentBytes(), s.Signature) + return checkSignature(cert, sigAlg, s.BodyBytes(), s.Signature) } } @@ -213,7 +195,7 @@ func (s *Su3File) String() string { fmt.Fprintf(&b, "ContentType: %q\n", s.ContentType) fmt.Fprintf(&b, "Version: %q\n", bytes.Trim(s.Version, "\x00")) fmt.Fprintf(&b, "SignerId: %q\n", s.SignerId) - fmt.Fprintln(&b, "---------------------------") + fmt.Fprintf(&b, "---------------------------") // content & signature // fmt.Fprintf(&b, "Content: %q\n", s.Content) @@ -243,64 +225,45 @@ func uzipData(c []byte) ([]byte, error) { return uncompressed, nil } -func ReadSu3(file *os.File) (*Su3File, error) { +func Parse(r io.Reader) (*Su3File, error) { var ( s = Su3File{} - skip uint8 + magic = MAGIC_BYTES + skip [1]byte bigSkip [12]byte signatureLength uint16 versionLength uint8 signerIdLength uint8 contentLength uint64 - magic [6]byte ) - // 0-5 - binary.Read(file, binary.BigEndian, &magic) - // 6 - binary.Read(file, binary.BigEndian, &skip) - // 7 - binary.Read(file, binary.BigEndian, &s.Format) - // 8-9 - binary.Read(file, binary.BigEndian, &s.SignatureType) - // 10-11 - binary.Read(file, binary.BigEndian, &signatureLength) - // 12 - binary.Read(file, binary.BigEndian, &skip) - // 13 - binary.Read(file, binary.BigEndian, &versionLength) - // 14 - binary.Read(file, binary.BigEndian, &skip) - // 15 - binary.Read(file, binary.BigEndian, &signerIdLength) - // 16-23 - binary.Read(file, binary.BigEndian, &contentLength) - // 24 - binary.Read(file, binary.BigEndian, &skip) - // 25 - binary.Read(file, binary.BigEndian, &s.FileType) - // 26 - binary.Read(file, binary.BigEndian, &skip) - // 27 - binary.Read(file, binary.BigEndian, &s.ContentType) - // 28-39 - binary.Read(file, binary.BigEndian, &bigSkip) + binary.Read(r, binary.BigEndian, &magic) + binary.Read(r, binary.BigEndian, &skip) + binary.Read(r, binary.BigEndian, &s.Format) + binary.Read(r, binary.BigEndian, &s.SignatureType) + binary.Read(r, binary.BigEndian, &signatureLength) + binary.Read(r, binary.BigEndian, &skip) + binary.Read(r, binary.BigEndian, &versionLength) + binary.Read(r, binary.BigEndian, &skip) + binary.Read(r, binary.BigEndian, &signerIdLength) + binary.Read(r, binary.BigEndian, &contentLength) + binary.Read(r, binary.BigEndian, &skip) + binary.Read(r, binary.BigEndian, &s.FileType) + binary.Read(r, binary.BigEndian, &skip) + binary.Read(r, binary.BigEndian, &s.ContentType) + binary.Read(r, binary.BigEndian, &bigSkip) s.Version = make([]byte, versionLength) s.SignerId = make([]byte, signerIdLength) s.Content = make([]byte, contentLength) s.Signature = make([]byte, signatureLength) - // 40-55+ Version, UTF-8 padded with trailing 0x00, 16 bytes minimum, length specified at byte 13. Do not append 0x00 bytes if the length is 16 or more. - binary.Read(file, binary.BigEndian, &s.Version) - // xx+ ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified at byte 15 - binary.Read(file, binary.BigEndian, &s.SignerId) - // xx+ Content, length and format specified in header - binary.Read(file, binary.BigEndian, &s.Content) - // xx+ Signature, length specified in header, covers everything starting at byte 0 - binary.Read(file, binary.BigEndian, &s.Signature) + binary.Read(r, binary.BigEndian, &s.Version) + binary.Read(r, binary.BigEndian, &s.SignerId) + binary.Read(r, binary.BigEndian, &s.Content) + binary.Read(r, binary.BigEndian, &s.Signature) return &s, nil }