diff --git a/.gitignore b/.gitignore index 80b127a..60c7ea2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ go-i2p *.exe .idea/ +router.info +log diff --git a/go.mod b/go.mod index 16d9373..504fcc6 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,22 @@ module github.com/go-i2p/go-i2p -go 1.21 +go 1.22 + +toolchain go1.22.5 require ( github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e github.com/flynn/noise v1.1.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.23.0 + github.com/stretchr/testify v1.9.0 + go.step.sm/crypto v0.51.2 + golang.org/x/crypto v0.26.0 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.20.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + golang.org/x/sys v0.24.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 579edcf..794a04b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,21 +17,25 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.step.sm/crypto v0.51.2 h1:5EiCGIMg7IvQTGmJrwRosbXeprtT80OhoS/PJarg60o= +go.step.sm/crypto v0.51.2/go.mod h1:QK7czLjN2k+uqVp5CHXxJbhc70kVRSP+0CQF3zsR5M0= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/common/data/string.go b/lib/common/data/string.go index 8b28c83..96399d2 100644 --- a/lib/common/data/string.go +++ b/lib/common/data/string.go @@ -46,13 +46,13 @@ func (str I2PString) Length() (length int, err error) { length = l.Int() str_len := len(str) if length > str_len { - log.WithFields(log.Fields{ + /*log.WithFields(log.Fields{ "at": "(I2PString) Length", "string_bytes_length": str_len, "string_length_field": length, "data": string(str), "reason": "data less than specified by length", - }).Error("string format warning") + }).Error("string format warning")*/ err = errors.New("string parsing warning: string data is shorter than specified by length") } return diff --git a/lib/common/keys_and_cert/keys_and_cert.go b/lib/common/keys_and_cert/keys_and_cert.go index 0ab5465..72ae89c 100644 --- a/lib/common/keys_and_cert/keys_and_cert.go +++ b/lib/common/keys_and_cert/keys_and_cert.go @@ -85,78 +85,11 @@ func (keys_and_cert KeysAndCert) Bytes() []byte { // PublicKey returns the public key as a crypto.PublicKey. func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey) { - /*cert := keys_and_cert.Certificate() - cert_len := cert.Length() - if err != nil { - return - } - if cert_len == 0 { - // No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte - // PublicKey space as ElgPublicKey. - var elg_key crypto.ElgPublicKey - copy(keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:]) - key = elg_key - } else { - // A Certificate is present in this KeysAndCert - cert_type := cert.Type() - if cert_type == CERT_KEY { - // This KeysAndCert contains a Key Certificate, construct - // a PublicKey from the data in the KeysAndCert and - // any additional data in the Certificate. - key, err = KeyCertificateFromCertificate(cert).ConstructPublicKey( - keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE], - ) - } else { - // Key Certificate is not present, return the KEYS_AND_CERT_PUBKEY_SIZE byte - // PublicKey space as ElgPublicKey. No other Certificate - // types are currently in use. - var elg_key crypto.ElgPublicKey - copy(keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:]) - key = elg_key - log.WithFields(log.Fields{ - "at": "(KeysAndCert) PublicKey", - "cert_type": cert_type, - }).Warn("unused certificate type observed") - } - - } - return*/ return keys_and_cert.publicKey } // SigningPublicKey returns the signing public key. func (keys_and_cert *KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey) { - /*cert := keys_and_cert.Certificate() - cert_len := cert.Length() - if err != nil { - return - } - if cert_len == 0 { - // No Certificate is present, return the KEYS_AND_CERT_SPK_SIZE byte - // SigningPublicKey space as legacy DSA SHA1 SigningPublicKey. - var dsa_pk crypto.DSAPublicKey - copy(dsa_pk[:], keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE]) - signing_public_key = dsa_pk - } else { - // A Certificate is present in this KeysAndCert - cert_type := cert.Type() - if cert_type == CERT_KEY { - // This KeysAndCert contains a Key Certificate, construct - // a SigningPublicKey from the data in the KeysAndCert and - // any additional data in the Certificate. - signing_public_key, err = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey( - keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE], - ) - } else { - // Key Certificate is not present, return the KEYS_AND_CERT_SPK_SIZE byte - // SigningPublicKey space as legacy SHA DSA1 SigningPublicKey. - // No other Certificate types are currently in use. - var dsa_pk crypto.DSAPublicKey - copy(dsa_pk[:], keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE]) - signing_public_key = dsa_pk - } - - }*/ return keys_and_cert.signingPublicKey } diff --git a/lib/common/keys_and_cert/private_keys_and_cert.go b/lib/common/keys_and_cert/private_keys_and_cert.go new file mode 100644 index 0000000..501fdc5 --- /dev/null +++ b/lib/common/keys_and_cert/private_keys_and_cert.go @@ -0,0 +1,18 @@ +package keys_and_cert + +import "crypto" + +// PrivateKeysAndCert contains a KeysAndCert along with the corresponding private keys for the +// Public Key and the Signing Public Key +type PrivateKeysAndCert struct { + KeysAndCert + PK_KEY crypto.PrivateKey + SPK_KEY crypto.PrivateKey +} + +func NewPrivateKeysAndCert() (*PrivateKeysAndCert, error) { + var pkc PrivateKeysAndCert + var err error + //pkc.PK_KEY, err = + return &pkc, err +} \ No newline at end of file diff --git a/lib/crypto/curve25519.go b/lib/crypto/curve25519.go new file mode 100644 index 0000000..d20d858 --- /dev/null +++ b/lib/crypto/curve25519.go @@ -0,0 +1,141 @@ +package crypto + +import ( + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "errors" + "io" + "math/big" + + curve25519 "go.step.sm/crypto/x25519" +) + +var Curve25519EncryptTooBig = errors.New("failed to encrypt data, too big for Curve25519") + +type Curve25519PublicKey []byte + +type Curve25519Verifier struct { + k []byte +} + +func (k Curve25519PublicKey) NewVerifier() (v Verifier, err error) { + temp := new(Curve25519Verifier) + temp.k = k + v = temp + return temp, nil +} + +func (k Curve25519PublicKey) Len() int { + return len(k) +} + +func createCurve25519PublicKey(data []byte) (k *curve25519.PublicKey) { + if len(data) == 256 { + k2 := curve25519.PublicKey{} + copy(k2[:], data) + k = &k2 + } + return +} + +func createCurve25519Encryption(pub *curve25519.PublicKey, rand io.Reader) (enc *Curve25519Encryption, err error) { + /*kbytes := make([]byte, 256) + k := new(big.Int) + for err == nil { + _, err = io.ReadFull(rand, kbytes) + k = new(big.Int).SetBytes(kbytes) + k = k.Mod(k, pub.P) + if k.Sign() != 0 { + break + } + } + if err == nil { + enc = &Curve25519Encryption{} + }*/ + return +} + +type Curve25519Encryption struct { + p, a, b1 *big.Int +} + +func (curve25519 *Curve25519Encryption) Encrypt(data []byte) (enc []byte, err error) { + return curve25519.EncryptPadding(data, true) +} + +func (curve25519 *Curve25519Encryption) EncryptPadding(data []byte, zeroPadding bool) (encrypted []byte, err error) { + if len(data) > 222 { + err = Curve25519EncryptTooBig + return + } + mbytes := make([]byte, 255) + mbytes[0] = 0xFF + copy(mbytes[33:], data) + // do sha256 of payload + d := sha256.Sum256(mbytes[33 : len(data)+33]) + copy(mbytes[1:], d[:]) + m := new(big.Int).SetBytes(mbytes) + // do encryption + b := new(big.Int).Mod(new(big.Int).Mul(curve25519.b1, m), curve25519.p).Bytes() + + if zeroPadding { + encrypted = make([]byte, 514) + copy(encrypted[1:], curve25519.a.Bytes()) + copy(encrypted[258:], b) + } else { + encrypted = make([]byte, 512) + copy(encrypted, curve25519.a.Bytes()) + copy(encrypted[256:], b) + } + return +} + +func (elg Curve25519PublicKey) NewEncrypter() (enc Encrypter, err error) { + k := createCurve25519PublicKey(elg[:]) + enc, err = createCurve25519Encryption(k, rand.Reader) + return +} + +func (v *Curve25519Verifier) VerifyHash(h, sig []byte) (err error) { + if len(sig) != curve25519.SignatureSize { + err = ErrBadSignatureSize + return + } + if len(v.k) != curve25519.PublicKeySize { + err = errors.New("failed to verify: invalid curve25519 public key size") + return + } + + ok := curve25519.Verify(v.k, h, sig) + if !ok { + err = errors.New("failed to verify: invalid signature") + } + return +} + +func (v *Curve25519Verifier) Verify(data, sig []byte) (err error) { + h := sha512.Sum512(data) + err = v.VerifyHash(h[:], sig) + return +} + +type Curve25519PrivateKey curve25519.PrivateKey + +type Curve25519Signer struct { + k []byte +} + +func (s *Curve25519Signer) Sign(data []byte) (sig []byte, err error) { + if len(s.k) != curve25519.PrivateKeySize { + err = errors.New("failed to sign: invalid curve25519 private key size") + return + } + h := sha512.Sum512(data) + sig, err = s.SignHash(h[:]) + return +} + +func (s *Curve25519Signer) SignHash(h []byte) (sig []byte, err error) { + return curve25519.Sign(rand.Reader, s.k, h) +} diff --git a/lib/transport/noise/noise.go b/lib/transport/noise/noise.go index 6269f9d..844e77e 100644 --- a/lib/transport/noise/noise.go +++ b/lib/transport/noise/noise.go @@ -72,31 +72,52 @@ func (ns *Noise) MatchAddr(addr net.Addr) (*router_address.RouterAddress, error) return nil, fmt.Errorf("no suitable address found for type %s from %s", addr.Network(), addr.String()) } +func dialWrapper(network, address string) (net.Conn, error) { + switch network { + case "SSU24": + return net.Dial("udp4", address) + case "SSU26": + return net.Dial("udp6", address) + case "NTCP2": + return net.Dial("tcp", address) + case "NTCP24": + return net.Dial("tcp4", address) + case "NTCP26": + return net.Dial("tcp6", address) + case "NTCP4": + return net.Dial("tcp4", address) + case "NTCP6": + return net.Dial("tcp6", address) + default: + return nil, fmt.Errorf("unknown transport, cannot dial %s", network) + } +} + func (ns Noise) DialNoise(addr router_address.RouterAddress) (net.Conn, error) { cfg := ns cfg.Initiator = false network := addr.Network() host, err := addr.Host() if err != nil { - return nil, err + return nil, fmt.Errorf("host error: %s", err) } port, err := addr.Port() if err != nil { - return nil, err + return nil, fmt.Errorf("port error: %s", err) } raddr := net.JoinHostPort(host.String(), port) var netConn net.Conn - netConn, err = net.Dial(network, raddr) + netConn, err = dialWrapper(network, raddr) if err != nil { - return nil, err + return nil, fmt.Errorf("dial error: %s", err) } cfg.HandshakeState, err = noise.NewHandshakeState(cfg.Config) if err != nil { - return nil, err + return nil, fmt.Errorf("handshake state initialization error: %s", err) } laddr, err := ns.MatchAddr(&addr) if err != nil { - return nil, err + return nil, fmt.Errorf("transport mismatch error %s", err) } // cfg.Config.PeerEphemeral, err = AESDeObfuscateEphemeralKeys() return &NoiseConn{ diff --git a/lib/transport/noise/noise_test.go b/lib/transport/noise/noise_test.go index 5603163..114d9fc 100644 --- a/lib/transport/noise/noise_test.go +++ b/lib/transport/noise/noise_test.go @@ -1,40 +1,13 @@ package noise import ( - "log" - "os" - "path/filepath" "testing" - - "github.com/go-i2p/go-i2p/lib/common/router_info" ) -func TestEstablishment(t *testing.T) { - // assert := assert.New(t) +func TestServer(t *testing.T) { + +} + +func TestEstablishment(t *testing.T) { - if home, err := os.UserHomeDir(); err != nil { - t.Error("ERROR", err) - } else { - riFile := filepath.Join(home, ".i2p", "router.info") - if riBytes, err := os.ReadFile(riFile); err != nil { - t.Error("ERROR", err) - } else { - if ri, rem, err := router_info.ReadRouterInfo(riBytes); err != nil { - t.Error("ERROR", err) - } else { - log.Println(ri.String(), rem) - if ns, err := NewNoise(ri); err != nil { - t.Error("ERROR", err) - } else { - host := ns.LocalAddr() - log.Println("NOISE TEST", host) - if nl, err := ns.DialNoise(*ri.RouterAddresses()[0]); err != nil { - t.Error("ERROR", err) - } else { - defer nl.Close() - } - } - } - } - } }