su3 parser
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
/cert.pem
|
||||
/key.pem
|
||||
/netdb
|
||||
i2pseeds.su3
|
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"log"
|
||||
|
||||
// "github.com/MDrollette/go-i2p/reseed"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
|
40
cmd/verify.go
Normal file
40
cmd/verify.go
Normal file
@ -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.")
|
||||
}
|
5
main.go
5
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 {
|
||||
|
@ -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"
|
||||
|
@ -1,5 +1 @@
|
||||
package reseed
|
||||
|
||||
const (
|
||||
LIST_TEMPLATE = `<html><head><title>NetDB</title></head><body><ul>{{ range $index, $_ := . }}<li><a href="{{ $index }}">{{ $index }}</a></li>{{ end }}</ul></body></html>`
|
||||
)
|
||||
|
74
su3/crypto.go
Normal file
74
su3/crypto.go
Normal file
@ -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
|
||||
}
|
119
su3/reseed_certs.go
Normal file
119
su3/reseed_certs.go
Normal file
@ -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-----
|
||||
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
|
||||
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-----`),
|
||||
}
|
||||
)
|
220
su3/su3.go
220
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user