diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e121a71 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Matt Drollette + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/cmd/keygen.go b/cmd/keygen.go index b334f4a..8e3ff53 100644 --- a/cmd/keygen.go +++ b/cmd/keygen.go @@ -12,6 +12,7 @@ import ( "os" "time" + "github.com/MDrollette/go-i2p/reseed" "github.com/codegangsta/cli" ) @@ -23,7 +24,7 @@ func NewKeygenCommand() cli.Command { Action: keygenAction, Flags: []cli.Flag{ cli.StringFlag{ - Name: "signer", + Name: "email", Usage: "Your email address (ex. something@mail.i2p)", }, }, @@ -31,18 +32,29 @@ func NewKeygenCommand() cli.Command { } func keygenAction(c *cli.Context) { - //"CN=" + cname + ",OU=" + ou + ",O=I2P Anonymous Network,L=XX,ST=XX,C=XX", + signer := c.String("email") + if signer == "" { + log.Fatalln("You must specify an email address (ex. --email=something@mail.i2p)") + } + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + template := &x509.Certificate{ BasicConstraintsValid: true, IsCA: true, - SubjectKeyId: []byte{1, 2, 3}, - SerialNumber: big.NewInt(1234), + SubjectKeyId: []byte(signer), + SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"I2P Anonymous Network"}, OrganizationalUnit: []string{"I2P"}, Locality: []string{"XX"}, StreetAddress: []string{"XX"}, Country: []string{"XX"}, + CommonName: signer, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), @@ -51,6 +63,7 @@ func keygenAction(c *cli.Context) { } // generate private key + fmt.Println("Generating keys. This may take a moment...") privatekey, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { log.Fatalln(err) @@ -78,11 +91,12 @@ func keygenAction(c *cli.Context) { fmt.Println("private key saved to reseed_private.pem") // save cert - certOut, err := os.Create("reseed_cert.pem") + filename := reseed.SignerFilename(signer) + certOut, err := os.Create(filename) if err != nil { - log.Fatalf("failed to open reseed_cert.pem for writing: %s", err) + log.Fatalf("failed to open %s for writing: %s", filename, err) } pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) certOut.Close() - fmt.Println("certificate saved to reseed_cert.pem") + fmt.Println("certificate saved to", filename) } diff --git a/cmd/verify.go b/cmd/verify.go index 256f7ae..7ad4a8d 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/MDrollette/go-i2p/reseed" "github.com/MDrollette/go-i2p/su3" "github.com/codegangsta/cli" ) @@ -32,7 +33,14 @@ func su3VerifyAction(c *cli.Context) { fmt.Println(su3File.String()) - if err := su3File.VerifySignature(); nil != err { + // get the reseeder key + ks := reseed.KeyStore{Path: "./certificates"} + cert, err := ks.ReseederCertificate(su3File.SignerId) + if nil != err { + panic(err) + } + + if err := su3File.VerifySignature(cert); nil != err { panic(err) } diff --git a/cmd/verify_public.go b/cmd/verify_public.go index 7015201..ec342ad 100644 --- a/cmd/verify_public.go +++ b/cmd/verify_public.go @@ -7,6 +7,7 @@ import ( "net/http" "sync" + "github.com/MDrollette/go-i2p/reseed" "github.com/MDrollette/go-i2p/su3" "github.com/codegangsta/cli" ) @@ -57,7 +58,6 @@ func download(out chan *http.Response, urls []string) { 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) @@ -77,6 +77,8 @@ func download(out chan *http.Response, urls []string) { } func validate(in chan *http.Response) { + ks := reseed.KeyStore{Path: "./certificates"} + for resp := range in { fmt.Printf("Validating: %s\n", resp.Request.URL) @@ -92,7 +94,14 @@ func validate(in chan *http.Response) { } resp.Body.Close() - if err := su3File.VerifySignature(); nil != err { + cert, err := ks.ReseederCertificate(su3File.SignerId) + if nil != err { + fmt.Println("Invalid: Unable to find public key.", err) + fmt.Println("") + continue + } + + if err := su3File.VerifySignature(cert); nil != err { fmt.Println("Invalid: Unable to verify signature", err) } else { fmt.Printf("Valid: For signer '%s'\n", su3File.SignerId) diff --git a/reseed/keystore.go b/reseed/keystore.go new file mode 100644 index 0000000..aca63fb --- /dev/null +++ b/reseed/keystore.go @@ -0,0 +1,28 @@ +package reseed + +import ( + "crypto/x509" + "encoding/pem" + "io/ioutil" + "path/filepath" + "strings" +) + +type KeyStore struct { + Path string +} + +func (ks *KeyStore) ReseederCertificate(signer []byte) (*x509.Certificate, error) { + certFile := filepath.Base(SignerFilename(string(signer))) + certString, err := ioutil.ReadFile(filepath.Join(ks.Path, "reseed", certFile)) + if nil != err { + return nil, err + } + + certPem, _ := pem.Decode(certString) + return x509.ParseCertificate(certPem.Bytes) +} + +func SignerFilename(signer string) string { + return strings.Replace(signer, "@", "_at_", 2) + ".crt" +} diff --git a/reseed/utils.go b/reseed/utils.go index 018f976..52220b7 100644 --- a/reseed/utils.go +++ b/reseed/utils.go @@ -30,3 +30,23 @@ func zipSeeds(seeds Seed) ([]byte, error) { return buf.Bytes(), nil } + +func uzipSeeds(c []byte) ([]byte, error) { + input := bytes.NewReader(c) + zipReader, err := zip.NewReader(input, int64(len(c))) + if nil != err { + return nil, err + } + + var uncompressed []byte + for _, f := range zipReader.File { + rc, err := f.Open() + if err != nil { + panic(err) + } + uncompressed = append(uncompressed, []byte(f.Name+"\n")...) + rc.Close() + } + + return uncompressed, nil +} diff --git a/su3/reseed_certs.go b/su3/reseed_certs.go deleted file mode 100644 index e504401..0000000 --- a/su3/reseed_certs.go +++ /dev/null @@ -1,24 +0,0 @@ -package su3 - -import ( - "crypto/x509" - "encoding/pem" - "io/ioutil" - "path/filepath" - "strings" -) - -func signerCertificate(signer string) (*x509.Certificate, error) { - certFile := filepath.Base(signerFilename(signer)) - certString, err := ioutil.ReadFile(filepath.Join("./certificates/reseed", certFile)) - if nil != err { - return nil, err - } - - certPem, _ := pem.Decode(certString) - return x509.ParseCertificate(certPem.Bytes) -} - -func signerFilename(signer string) string { - return strings.Replace(signer, "@", "_at_", 2) + ".crt" -} diff --git a/su3/su3.go b/su3/su3.go index bfdd8ce..98d4e6d 100644 --- a/su3/su3.go +++ b/su3/su3.go @@ -1,7 +1,6 @@ package su3 import ( - "archive/zip" "bytes" "crypto" "crypto/rand" @@ -156,7 +155,7 @@ func (s *Su3File) Bytes() []byte { return buf.Bytes() } -func (s *Su3File) VerifySignature() error { +func (s *Su3File) VerifySignature(cert *x509.Certificate) error { var sigAlg x509.SignatureAlgorithm switch s.SignatureType { case SIGTYPE_DSA: @@ -177,11 +176,7 @@ func (s *Su3File) VerifySignature() error { return fmt.Errorf("Unsupported signature type.") } - if cert, err := signerCertificate(string(s.SignerId)); nil != err { - return err - } else { - return checkSignature(cert, sigAlg, s.BodyBytes(), s.Signature) - } + return checkSignature(cert, sigAlg, s.BodyBytes(), s.Signature) } func (s *Su3File) String() string { @@ -205,26 +200,6 @@ func (s *Su3File) String() string { return b.String() } -func uzipData(c []byte) ([]byte, error) { - input := bytes.NewReader(c) - zipReader, err := zip.NewReader(input, int64(len(c))) - if nil != err { - return nil, err - } - - var uncompressed []byte - for _, f := range zipReader.File { - rc, err := f.Open() - if err != nil { - panic(err) - } - uncompressed = append(uncompressed, []byte(f.Name+"\n")...) - rc.Close() - } - - return uncompressed, nil -} - func Parse(r io.Reader) (*Su3File, error) { var ( s = Su3File{}