From 2d445337c8cece09b2a7422174f90145a09bc889 Mon Sep 17 00:00:00 2001 From: Matt Drollette Date: Tue, 9 Dec 2014 17:00:18 -0600 Subject: [PATCH] su3 parser --- .gitignore | 1 + cmd/reseeder.go | 1 + cmd/verify.go | 40 ++++++++ main.go | 5 +- reseed/legacy.go | 14 --- reseed/server.go | 4 - su3/crypto.go | 74 +++++++++++++++ su3/reseed_certs.go | 119 ++++++++++++++++++++++++ su3/su3.go | 220 ++++++++++++++++++++++++++++++++++---------- 9 files changed, 408 insertions(+), 70 deletions(-) create mode 100644 cmd/verify.go create mode 100644 su3/crypto.go create mode 100644 su3/reseed_certs.go diff --git a/.gitignore b/.gitignore index 0c1efa2..f6234f3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /cert.pem /key.pem /netdb +i2pseeds.su3 \ No newline at end of file diff --git a/cmd/reseeder.go b/cmd/reseeder.go index febb06a..b12aa58 100644 --- a/cmd/reseeder.go +++ b/cmd/reseeder.go @@ -3,6 +3,7 @@ package cmd import ( "log" + // "github.com/MDrollette/go-i2p/reseed" "github.com/codegangsta/cli" ) diff --git a/cmd/verify.go b/cmd/verify.go new file mode 100644 index 0000000..8c0a806 --- /dev/null +++ b/cmd/verify.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/MDrollette/go-i2p/su3" + "github.com/codegangsta/cli" +) + +func NewSu3VerifyCommand() cli.Command { + return cli.Command{ + Name: "verify", + Usage: "Verify a Su3 file", + Description: "Verify a Su3 file", + Action: su3VerifyAction, + Flags: []cli.Flag{}, + } +} + +func su3VerifyAction(c *cli.Context) { + file, err := os.Open(c.Args().Get(0)) + if err != nil { + panic(err) + } + defer file.Close() + + su3File := su3.Su3File{} + if err := su3.ReadSu3(file, &su3File); err != nil { + panic(err) + } + + if err := su3File.VerifySignature(); nil != err { + panic(err) + } + + fmt.Println(su3File.String()) + + fmt.Println("Verified signature.") +} diff --git a/main.go b/main.go index c5e1cb8..8fd6b3d 100644 --- a/main.go +++ b/main.go @@ -13,12 +13,9 @@ func main() { app.Version = "0.1.0" app.Usage = "I2P commands" app.Flags = []cli.Flag{} - app.Before = func(c *cli.Context) error { - cmd.Init() - return nil - } app.Commands = []cli.Command{ cmd.NewReseederCommand(), + cmd.NewSu3VerifyCommand(), } if err := app.Run(os.Args); err != nil { diff --git a/reseed/legacy.go b/reseed/legacy.go index 238124b..f8fa87e 100644 --- a/reseed/legacy.go +++ b/reseed/legacy.go @@ -1,19 +1,5 @@ package reseed -// read in all files from netdb dir into a slice of routerinfos - -// for every unique requesting IP -// look up that IP in the db -// - if it exists, check the creation time -// - if the creation time is within the threshold, serve up the routerinfos -// - if the creation time is outside the threshold, or if it does not exist generate a new slice of routerinfos from the current master set - -// at some regular interval, update the master slice with fresh netdb routerinfos - -// can serve up html/ul of routerinfos -// can serve up su3 signed file -// https://geti2p.net/en/docs/spec/updates - import ( "bytes" "fmt" diff --git a/reseed/server.go b/reseed/server.go index e712e77..f4dc837 100644 --- a/reseed/server.go +++ b/reseed/server.go @@ -1,5 +1 @@ package reseed - -const ( - LIST_TEMPLATE = `NetDB` -) diff --git a/su3/crypto.go b/su3/crypto.go new file mode 100644 index 0000000..007e03c --- /dev/null +++ b/su3/crypto.go @@ -0,0 +1,74 @@ +package su3 + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "errors" + "math/big" +) + +type dsaSignature struct { + R, S *big.Int +} + +type ecdsaSignature dsaSignature + +func checkSignature(c *x509.Certificate, algo x509.SignatureAlgorithm, signed, signature []byte) (err error) { + var hashType crypto.Hash + + switch algo { + case x509.SHA1WithRSA, x509.DSAWithSHA1, x509.ECDSAWithSHA1: + hashType = crypto.SHA1 + case x509.SHA256WithRSA, x509.DSAWithSHA256, x509.ECDSAWithSHA256: + hashType = crypto.SHA256 + case x509.SHA384WithRSA, x509.ECDSAWithSHA384: + hashType = crypto.SHA384 + case x509.SHA512WithRSA, x509.ECDSAWithSHA512: + hashType = crypto.SHA512 + default: + return x509.ErrUnsupportedAlgorithm + } + + if !hashType.Available() { + return x509.ErrUnsupportedAlgorithm + } + h := hashType.New() + + h.Write(signed) + digest := h.Sum(nil) + + switch pub := c.PublicKey.(type) { + case *rsa.PublicKey: + // the digest is already hashed, so we force a 0 here + return rsa.VerifyPKCS1v15(pub, 0, digest, signature) + case *dsa.PublicKey: + dsaSig := new(dsaSignature) + if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { + return err + } + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return errors.New("x509: DSA signature contained zero or negative values") + } + if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { + return errors.New("x509: DSA verification failure") + } + return + case *ecdsa.PublicKey: + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("x509: ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("x509: ECDSA verification failure") + } + return + } + return x509.ErrUnsupportedAlgorithm +} diff --git a/su3/reseed_certs.go b/su3/reseed_certs.go new file mode 100644 index 0000000..f640d75 --- /dev/null +++ b/su3/reseed_certs.go @@ -0,0 +1,119 @@ +package su3 + +import ( + "crypto/x509" + "encoding/pem" + "fmt" +) + +func certForSigner(signer string) (*x509.Certificate, error) { + var certString []byte + var ok bool + if certString, ok = reseedKeys[string(signer)]; !ok { + return nil, fmt.Errorf("Unknown signer '%s'", signer) + } + + certPem, _ := pem.Decode(certString) + return x509.ParseCertificate(certPem.Bytes) +} + +var ( + reseedKeys = map[string][]byte{ + "backup@mail.i2p": []byte(`-----BEGIN CERTIFICATE----- +MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu +aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC +WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255 +bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls +LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d +roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr +omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H +uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV +Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA +3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo +dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ +HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv +TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl +/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV +exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe +jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl +1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T +5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa +0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME +Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge +vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S +DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O +lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs +cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA +FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG +1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/ +4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI +flpzWXkFM2D36OUaubfe9YY= +-----END CERTIFICATE-----`), + "echelon@mail.i2p": []byte(`-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIESg3kkzANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQZWNoZWxvbkBtYWls +LmkycDAeFw0xNDA3MzExNjQ3MDJaFw0yNDA3MzAxNjQ3MDJaMHAxCzAJBgNVBAYT +AlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9u +eW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQDDBBlY2hlbG9uQG1h +aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmcEgLwwhzLNe +XLOMSrhwB8hWpOhfjo4s6S/wjBtjjUc8nI3D0hSn3HY26p0rvcvNEWexPUpPULmC +exGkU463nu7PiFONiORI1eJAiUFHibRiaA7Wboyo38pO73KirwjG07Y+Ua0jp+HS ++4FQ/I/9H/bPplReTOU/6hmRbgQ69U8nE68HzZHQxP68yVJ2rPHSXMPhF4R1h0G1 +1mCAT+TgTsnwHNGF77XHJnY4/M4e2cgycEZjZow36C3t2mNDVkMgF19QQeb9WmLR +zREn3nq9BJqHpUkn9yWw0kKXTZSds+7UxESfzf3BzK0+hky2fh5H+qbYAo2lz4yj +81MXTAu+4RRkg4DBLlF+2dkclhwQLxxzvkRC6tPkn5i33Yltg7EfzA9IoQ05potJ +I+iOcF+aStfFgFj9u3B5UkcF4P0cH1QD3c6BK4hIezQYqRoPly1gHqg+XdwjG/dr +4as7HA9FTz3p2E8nClpIC1x3hfgwAdfd29aeBxO1WW/z99iMF7TBAF+u5T86XEW1 +WpknqCbTli36yJ8a5fPWxZHrryBRJT5yLxejjFeadtutBSwljiVFq+Y38VqwFivq +VLiBt7IxAsZ8iilgfnnnAvBH6chWfSKb4H7kB4TJvDiV96QmmvoEaWYNHZozMhyK +tO3b5w+xqbJXyCLA3Q75jD0km76hjcECAwEAAaMhMB8wHQYDVR0OBBYEFAHQcAam +QRS/EUhuCSr9pB4Ux0rYMA0GCSqGSIb3DQEBDQUAA4ICAQBq1+1QLmgLAjrTg3tb +4XKgAVICQRoBDNUEobQg3pYeUX9eFNya2RxNljuvYpwT80ilGMPOXcjddmr5ngiK +dbGRcuuJk9MPEHtPaPT3+JJlvKQ3B3g2wva2Wz2OAyLZUGQs389K4nTbwh4QF0n2 +aHFL8BHiD62hiKnCoNaW4ZovUNNvOxo9lMyAiaFU2gqQNcdad8hP9EAllbvbxDx9 +Tjww2UbwQUIHS9rna4Tlu+f0hDXTWIutc2A51W2fJCb7L3+lYO7Wv55ND/WtryLZ +XpMp27+MpuEnN3kQmz/l9R0hIJsWc/x9GQkjm5wEaIZEyTtenqwRKGmVCtAj0Pgv +jn1L3/lWmrNq+OZHb/QeyfKtA3nXfQKVmT98ewQiK/S5i1xIAXCJPytOD887b/o1 +cdurTmCiZMwgiQ+HLJqCg3MDa5mvKqRkRdZXfE6aQWEcSbpAhpV15R17q7L+Fg0W +shLSNucxyGNU8PjiC/nOmqfqUiPiMltJjPmscxBLim8foyxjakC4+6N6m+Jzgznj +PocBehFAfKYj66XEwzIBN7Z2uuXoYH9YptkocFjTzvchcryVulDWZ4FWxreUMhpM +4oyjjhSB4tB9clXlwMqg577q3D6Ms0zLTqsztyPN3zr6jGev3jpVq7Q1GOlciHPv +JNJOWTH/Vas1W6XlwGcOOAARTQ== +-----END CERTIFICATE-----`), + "meeh@mail.i2p": []byte(`-----BEGIN CERTIFICATE----- +MIIFeTCCA2GgAwIBAgIEZZozujANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNbWVlaEBtYWlsLmky +cDAeFw0xNDA2MjgyMjQ5MDlaFw0yNDA2MjcyMjQ5MDlaMG0xCzAJBgNVBAYTAlhY +MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v +dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1tZWVoQG1haWwuaTJw +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnVnmPE4uUvCky0yCnnVH +cJEDqzwDPupx0zr0YDlhZk5VOPPecx5haayJ/V6nXPc1aVVWn+CHfedcF2aBgN4K +5aBueS/l6l5WHcv02DofAqlTmyAws3oQeR1qoTuW24cKRtLR7h5bxv63f6bgp6e+ +RihFNez6UxErnRPuJOJEO2Im6EgVp6fz7tQ7R35zxAUeES2YILPySvzy2vYm/EEG +jXX7Ap2A5svVo90xCMOeUZ/55vLsjyIshN+tV87U4xwvAkUmwsmWVHm3BQpHkI6z +zMJie6epB8Bqm0GYm0EcElJH4OCxGTvDLoghpswbuUO7iy3JSfoL7ZCnoiQdK9K4 +yVVChj8lG+r7KaTowK96iZep+sZefjOt5VFGuW2Fi/WBv3ldiLlJAo/ZfrUM4+vG +fyNBXbl6bX87uTCGOT1p3dazo+zJMsAZ+Y93DlM/mDEWFa1kKNrs74syzaWEqF4L +KQE6VoYn80OOzafSigTVQgSwUtQtB0XGhMzJhyxU2XHWe1LFIy7Pta0B+lDiZj7c +I8nXxYjsDfEu/Elj/Ra9N6bH0awmgB5JDa+Tbir+oEM5SyDfpSaCGuatdGxjweGI +kVmFU0SqCZV/8TXbIu6MUVzTZMZVT94edifFSRad4fqw7eZbSXlPu++3d1/btn6h +ibM04nkv0mm+FxCKB/wdAkECAwEAAaMhMB8wHQYDVR0OBBYEFO7jIkSRkoXyJcho +9/Q0gDOINa5EMA0GCSqGSIb3DQEBDQUAA4ICAQBzfWO7+8HWOKLaYWToJ6XZbpNF +3wXv1yC4W/HRR80m4JSsq9r0d7838Nvd7vLVP6MY6MaVb/JnV76FdQ5WQ6ticD0Y +o3zmpqqbKVSspN0lrkig4surT88AjfVQz/vEIzKNQEbpzc3hC2LCiE2u+cK/ix4j +b9RohnaPvwLnew5RNQRpcmk+XejaNITISr2yQIwXL7TEYy8HdGCfzFSSFhKe9vkb +GsWS5ASrUzRoprswmlgRe8gEHI+d51Z7mWgna0/5mBz9bH/3QXtpxlLWm3bVV+kt +pZjQDTHE0GqG2YsD1Gmp4LU/JFhCojMTtiPCXmr9KFtpiVlx06DuKm5PC8Ak+5w+ +m/DQYYfv9z+AA5Y430bjnzwg67bhqVyyek4wcDQinFswv3h4bIB7CJujDcEqXXza +lhG1ufPPCUTMrVjh7AShohZraqlSlyQPY9vEppLwD4W1d+MqDHM7ljOH7gQYaUPi +wE30AdXEOxLZcT3aRKxkKf2esNofSuUC/+NXQvPjpuI4UJKO3eegi+M9dbnKoNWs +MPPLPpycecWPheFYM5K6Ao63cjlUY2wYwCfDTFgjA5q8i/Rp7i6Z6fLE3YWJ4VdR +WOFB7hlluQ//jMW6M1qz6IYXmlUjcXl81VEvlOH/QBNrPvX3I3SYXYgVRnVGUudB +o3eNsanvTU+TIFBh2Q== +-----END CERTIFICATE-----`), + } +) diff --git a/su3/su3.go b/su3/su3.go index a1967f4..736f057 100644 --- a/su3/su3.go +++ b/su3/su3.go @@ -1,70 +1,194 @@ package su3 -type Su3 struct { - // 0-5 Magic number "I2Psu3" - Magic [6]byte +import ( + "archive/zip" + "bytes" + "crypto/x509" + "encoding/binary" + "fmt" + "os" +) - // 6 unused = 0 - Unused1 [1]byte +const ( + MAGIC_BYTES = "I2Psu3" - // 7 su3 file format version = 0 - Format [1]byte + SIGTYPE_DSA = uint16(0) + SIGTYPE_ECDSA_SHA256 = uint16(1) + SIGTYPE_ECDSA_SHA384 = uint16(2) + SIGTYPE_ECDSA_SHA512 = uint16(3) + SIGTYPE_RSA_SHA256 = uint16(4) + SIGTYPE_RSA_SHA384 = uint16(5) + SIGTYPE_RSA_SHA512 = uint16(6) - // 8-9 Signature type - // 0x0000 = DSA-160 - // 0x0001 = ECDSA-SHA256-P256 - // 0x0002 = ECDSA-SHA384-P384 - // 0x0003 = ECDSA-SHA512-P521 - // 0x0004 = RSA-SHA256-2048 - // 0x0005 = RSA-SHA384-3072 - // 0x0006 = RSA-SHA512-4096 - SignatureType [2]byte + CONTENT_TYPE_UNKNOWN = uint16(0) + CONTENT_TYPE_ROUTER = uint16(1) + CONTENT_TYPE_PLUGIN = uint16(2) + CONTENT_TYPE_RESEED = uint16(3) + CONTENT_TYPE_NEWS = uint16(4) - // 10-11 Signature length, e.g. 40 (0x0028) for DSA-160 - SignatureLength [2]byte + FILE_TYPE_ZIP = uint8(0) + FILE_TYPE_XML = uint8(1) + FILE_TYPE_HTML = uint8(2) + FILE_TYPE_XMLGZ = uint8(3) +) - // 12 unused - Unused2 [1]byte +type Su3File struct { + Magic [6]byte + Format [1]byte + SignatureType uint16 + SignatureLength uint16 + VersionLength uint8 + SignerIdLength uint8 + ContentLength uint64 + FileType [1]byte + ContentType [1]byte - // 13 Version length (in bytes not chars, including padding) must be at least 16 (0x10) for compatibility - VersionLength [1]byte + Version []byte + SignerId []byte + Content []byte + Signature []byte + SignedBytes []byte +} - // 14 unused - Unused3 [1]byte +func (s *Su3File) String() string { + var b bytes.Buffer - // 15 Signer ID length (in bytes not chars) - SignerIdLength [1]byte + // header + fmt.Fprintln(&b, "---------------------------") - // 16-23 Content length (not including header or sig) - ContentLength [8]byte + fmt.Fprintf(&b, "Magic: %s\n", s.Magic) + fmt.Fprintf(&b, "Format: %q\n", s.Format) + fmt.Fprintf(&b, "SignatureType: %q\n", s.SignatureType) + fmt.Fprintf(&b, "SignatureLength: %s\n", s.SignatureLength) + fmt.Fprintf(&b, "VersionLength: %s\n", s.VersionLength) + fmt.Fprintf(&b, "SignerIdLength: %s\n", s.SignerIdLength) + fmt.Fprintf(&b, "ContentLength: %s\n", s.ContentLength) + fmt.Fprintf(&b, "FileType: %q\n", s.FileType) + fmt.Fprintf(&b, "ContentType: %q\n", s.ContentType) - // 24 unused - Unused4 [1]byte + // content + fmt.Fprintln(&b, "---------------------------") - // 25 File type - // 0x00 = zip file - // 0x01 = xml file (as of 0.9.15) - // 0x02 = html file (as of 0.9.17) - // 0x03 = xml.gz file (as of 0.9.17) - FileType [1]byte + fmt.Fprintf(&b, "Version: %q\n", bytes.Trim(s.Version, "\x00")) + fmt.Fprintf(&b, "SignerId: %q\n", s.SignerId) + // fmt.Fprintf(&b, "Content: %q\n", s.Content) + // fmt.Fprintf(&b, "Signature: %q\n", s.Signature) - // 26 unused - Unused5 [1]byte + fmt.Fprintln(&b, "---------------------------") - // 27 Content type - // 0x00 = unknown - // 0x01 = router update - // 0x02 = plugin or plugin update - // 0x03 = reseed data - // 0x04 = news feed (as of 0.9.15) - ContentType [1]byte + return b.String() +} - // 28-39 unused - Unused6 [12]byte +func (s *Su3File) VerifySignature() error { + return verifySig(s.SignatureType, s.SignerId, s.Signature, s.SignedBytes) +} + +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 verifySig(sigType uint16, signer, signature, signed []byte) (err error) { + var cert *x509.Certificate + if cert, err = certForSigner(string(signer)); nil != err { + return err + } + + var sigAlg x509.SignatureAlgorithm + switch sigType { + case SIGTYPE_DSA: + sigAlg = x509.DSAWithSHA1 + case SIGTYPE_ECDSA_SHA256: + sigAlg = x509.ECDSAWithSHA256 + case SIGTYPE_ECDSA_SHA384: + sigAlg = x509.ECDSAWithSHA384 + case SIGTYPE_ECDSA_SHA512: + sigAlg = x509.ECDSAWithSHA512 + case SIGTYPE_RSA_SHA256: + sigAlg = x509.SHA256WithRSA + case SIGTYPE_RSA_SHA384: + sigAlg = x509.SHA384WithRSA + case SIGTYPE_RSA_SHA512: + sigAlg = x509.SHA512WithRSA + default: + return fmt.Errorf("Unsupported signature type.") + } + + return checkSignature(cert, sigAlg, signed, signature) +} + +func ReadSu3(file *os.File, su3File *Su3File) error { + var ( + skip [1]byte + bigSkip [12]byte + ) + + // 0-5 + binary.Read(file, binary.BigEndian, &su3File.Magic) + // 6 + binary.Read(file, binary.BigEndian, &skip) + // 7 + binary.Read(file, binary.BigEndian, &su3File.Format) + // 8-9 + binary.Read(file, binary.BigEndian, &su3File.SignatureType) + // 10-11 + binary.Read(file, binary.BigEndian, &su3File.SignatureLength) + // 12 + binary.Read(file, binary.BigEndian, &skip) + // 13 + binary.Read(file, binary.BigEndian, &su3File.VersionLength) + // 14 + binary.Read(file, binary.BigEndian, &skip) + // 15 + binary.Read(file, binary.BigEndian, &su3File.SignerIdLength) + // 16-23 + binary.Read(file, binary.BigEndian, &su3File.ContentLength) + // 24 + binary.Read(file, binary.BigEndian, &skip) + // 25 + binary.Read(file, binary.BigEndian, &su3File.FileType) + // 26 + binary.Read(file, binary.BigEndian, &skip) + // 27 + binary.Read(file, binary.BigEndian, &su3File.ContentType) + // 28-39 + binary.Read(file, binary.BigEndian, &bigSkip) + + su3File.Version = make([]byte, su3File.VersionLength) + su3File.SignerId = make([]byte, su3File.SignerIdLength) + su3File.Content = make([]byte, su3File.ContentLength) + su3File.Signature = make([]byte, su3File.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, su3File.Version) // xx+ ID of signer, (e.g. "zzz@mail.i2p") UTF-8, not padded, length specified at byte 15 + binary.Read(file, binary.BigEndian, su3File.SignerId) // xx+ Content, length and format specified in header + binary.Read(file, binary.BigEndian, su3File.Content) + + // re-read from the beginning to get the signed content + signedEnd, _ := file.Seek(0, 1) + file.Seek(0, 0) + su3File.SignedBytes = make([]byte, signedEnd) + binary.Read(file, binary.BigEndian, su3File.SignedBytes) + // xx+ Signature, length specified in header, covers everything starting at byte 0 - Version [16]byte + binary.Read(file, binary.BigEndian, su3File.Signature) + + return nil }