From 0ec4f55fa9d1dd71e25e67d7bb69660fa1ea5727 Mon Sep 17 00:00:00 2001 From: idk Date: Mon, 11 Jul 2022 23:41:58 -0400 Subject: [PATCH] Check in unchecked-in common library fixes, start implementing transports. Since I turned out to be implementing parts of Noise which I did not need to implement, I'm taking a different approach, and doing an unmodified Noise transport first and then making our modifications to it. That should reduce what I need to do to message pre-processing mostly, I think. --- go.mod | 1 + go.sum | 2 + lib/bootstrap/bootstrap.go | 4 +- lib/common/certificate/certificate.go | 2 +- lib/common/certificate/certificate_test.go | 2 +- lib/common/data/date.go | 2 +- lib/common/data/date_test.go | 5 +- lib/common/data/hash.go | 2 +- lib/common/data/integer.go | 2 +- lib/common/data/integer_test.go | 2 +- lib/common/data/mapping.go | 2 +- lib/common/data/mapping_test.go | 2 +- lib/common/data/string.go | 2 +- lib/common/data/string_test.go | 2 +- lib/common/destination/destination.go | 2 +- lib/common/key_certificate/key_certificate.go | 2 +- .../key_certificate/key_certificate_test.go | 2 +- lib/common/keys_and_cert/keys_and_cert.go | 2 +- .../keys_and_cert/keys_and_cert_test.go | 107 ++----- lib/common/lease/lease.go | 2 +- lib/common/lease_set/lease_set.go | 7 +- lib/common/lease_set/lease_set_test.go | 54 ++-- lib/common/router_address/router_address.go | 11 +- .../router_address/router_address_test.go | 2 +- lib/common/router_identity/router_identity.go | 2 +- lib/common/router_info/router_info.go | 17 +- lib/common/router_info/router_info_test.go | 33 ++- lib/common/session_key/session_key.go | 2 +- lib/common/session_tag/session_tag.go | 2 +- lib/common/signature/signature.go | 2 +- lib/config/su3.go | 21 +- lib/i2np/build_request_record.go | 40 +-- lib/i2np/build_request_record_test.go | 5 +- lib/i2np/build_response_record.go | 2 +- lib/i2np/database_lookup.go | 8 +- lib/i2np/database_search_reply.go | 2 +- lib/i2np/database_store.go | 2 +- lib/i2np/garlic.go | 5 +- lib/i2np/garlic_clove.go | 5 +- .../garlic_clove_delivery_instructions.go | 5 +- lib/i2np/header.go | 42 +-- lib/i2np/header_test.go | 5 +- lib/netdb/entry.go | 5 +- lib/netdb/kad.go | 8 +- lib/netdb/netdb.go | 12 +- lib/netdb/std.go | 33 ++- lib/transport/messages/message.go | 63 ++++ lib/transport/messages/sessionconfirmed.go | 248 ++++++++++++++++ lib/transport/messages/sessioncreated.go | 263 +++++++++++++++++ lib/transport/messages/sessionrequest.go | 270 ++++++++++++++++++ lib/transport/multi.go | 9 +- lib/transport/noise/session.go | 36 +++ lib/transport/transport.go | 10 +- lib/tunnel/delivery.go | 13 +- lib/tunnel/delivery_test.go | 9 +- 55 files changed, 1145 insertions(+), 257 deletions(-) create mode 100644 lib/transport/messages/message.go create mode 100644 lib/transport/messages/sessionconfirmed.go create mode 100644 lib/transport/messages/sessioncreated.go create mode 100644 lib/transport/messages/sessionrequest.go diff --git a/go.mod b/go.mod index b1a011d..f95d508 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/go-i2p/go-i2p go 1.16 require ( + github.com/emirpasic/gods v1.18.1 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc diff --git a/go.sum b/go.sum index 2f0e8b6..67a6b76 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ 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= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= diff --git a/lib/bootstrap/bootstrap.go b/lib/bootstrap/bootstrap.go index 9583b62..0c60af8 100644 --- a/lib/bootstrap/bootstrap.go +++ b/lib/bootstrap/bootstrap.go @@ -1,6 +1,6 @@ package bootstrap -import "github.com/go-i2p/go-i2p/lib/common" +import "github.com/go-i2p/go-i2p/lib/common/router_info" // interface defining a way to bootstrap into the i2p network type Bootstrap interface { @@ -9,5 +9,5 @@ type Bootstrap interface { // if n is 0 then try obtaining as many router infos as possible // returns nil and error if we cannot fetch ANY router infos // returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use - GetPeers(n int) (chan []common.RouterInfo, error) + GetPeers(n int) (chan []router_info.RouterInfo, error) } diff --git a/lib/common/certificate/certificate.go b/lib/common/certificate/certificate.go index 5836ced..fe658de 100644 --- a/lib/common/certificate/certificate.go +++ b/lib/common/certificate/certificate.go @@ -1,4 +1,4 @@ -package common +package certificate /* I2P Certificate diff --git a/lib/common/certificate/certificate_test.go b/lib/common/certificate/certificate_test.go index be97a8d..770db08 100644 --- a/lib/common/certificate/certificate_test.go +++ b/lib/common/certificate/certificate_test.go @@ -1,4 +1,4 @@ -package common +package certificate import ( "testing" diff --git a/lib/common/data/date.go b/lib/common/data/date.go index c91d70a..db22b7e 100644 --- a/lib/common/data/date.go +++ b/lib/common/data/date.go @@ -1,4 +1,4 @@ -package common +package data /* I2P Date diff --git a/lib/common/data/date_test.go b/lib/common/data/date_test.go index 9c17504..bf45ffe 100644 --- a/lib/common/data/date_test.go +++ b/lib/common/data/date_test.go @@ -1,8 +1,9 @@ -package common +package data import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestTimeFromMiliseconds(t *testing.T) { diff --git a/lib/common/data/hash.go b/lib/common/data/hash.go index 553eba4..9319be9 100644 --- a/lib/common/data/hash.go +++ b/lib/common/data/hash.go @@ -1,4 +1,4 @@ -package common +package data import ( "crypto/sha256" diff --git a/lib/common/data/integer.go b/lib/common/data/integer.go index 7f2e302..9d1cad8 100644 --- a/lib/common/data/integer.go +++ b/lib/common/data/integer.go @@ -1,4 +1,4 @@ -package common +package data /* I2P Integer diff --git a/lib/common/data/integer_test.go b/lib/common/data/integer_test.go index a2cb4dd..58e3e00 100644 --- a/lib/common/data/integer_test.go +++ b/lib/common/data/integer_test.go @@ -1,4 +1,4 @@ -package common +package data import ( "testing" diff --git a/lib/common/data/mapping.go b/lib/common/data/mapping.go index 2ad09be..da63a84 100644 --- a/lib/common/data/mapping.go +++ b/lib/common/data/mapping.go @@ -1,4 +1,4 @@ -package common +package data /* I2P Mapping diff --git a/lib/common/data/mapping_test.go b/lib/common/data/mapping_test.go index 3cbb894..45e3252 100644 --- a/lib/common/data/mapping_test.go +++ b/lib/common/data/mapping_test.go @@ -1,4 +1,4 @@ -package common +package data import ( "bytes" diff --git a/lib/common/data/string.go b/lib/common/data/string.go index c47ac69..add64d5 100644 --- a/lib/common/data/string.go +++ b/lib/common/data/string.go @@ -1,4 +1,4 @@ -package common +package data /* I2P I2PString diff --git a/lib/common/data/string_test.go b/lib/common/data/string_test.go index 1247060..419e292 100644 --- a/lib/common/data/string_test.go +++ b/lib/common/data/string_test.go @@ -1,4 +1,4 @@ -package common +package data import ( "testing" diff --git a/lib/common/destination/destination.go b/lib/common/destination/destination.go index 96a2e05..2207a0d 100644 --- a/lib/common/destination/destination.go +++ b/lib/common/destination/destination.go @@ -1,4 +1,4 @@ -package common +package destination /* I2P Destination diff --git a/lib/common/key_certificate/key_certificate.go b/lib/common/key_certificate/key_certificate.go index 5dc42ae..b66ae4c 100644 --- a/lib/common/key_certificate/key_certificate.go +++ b/lib/common/key_certificate/key_certificate.go @@ -1,4 +1,4 @@ -package common +package key_certificate /* I2P Key Certificate diff --git a/lib/common/key_certificate/key_certificate_test.go b/lib/common/key_certificate/key_certificate_test.go index 52a2a49..73ca283 100644 --- a/lib/common/key_certificate/key_certificate_test.go +++ b/lib/common/key_certificate/key_certificate_test.go @@ -1,4 +1,4 @@ -package common +package key_certificate import ( "testing" diff --git a/lib/common/keys_and_cert/keys_and_cert.go b/lib/common/keys_and_cert/keys_and_cert.go index 530996e..ac4ad50 100644 --- a/lib/common/keys_and_cert/keys_and_cert.go +++ b/lib/common/keys_and_cert/keys_and_cert.go @@ -1,4 +1,4 @@ -package common +package keys_and_cert /* I2P KeysAndCert diff --git a/lib/common/keys_and_cert/keys_and_cert_test.go b/lib/common/keys_and_cert/keys_and_cert_test.go index 2a05726..5182f8d 100644 --- a/lib/common/keys_and_cert/keys_and_cert_test.go +++ b/lib/common/keys_and_cert/keys_and_cert_test.go @@ -1,4 +1,4 @@ -package common +package keys_and_cert import ( "testing" @@ -12,9 +12,9 @@ func TestCertificateWithMissingData(t *testing.T) { cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01} data := make([]byte, 128+256) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - cert, err := keys_and_cert.Certificate() + cert := keys_and_cert.Certificate() if assert.NotNil(err) { assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) } @@ -30,9 +30,9 @@ func TestCertificateWithValidData(t *testing.T) { cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00} data := make([]byte, 128+256) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - cert, err := keys_and_cert.Certificate() + cert := keys_and_cert.Certificate() assert.Nil(err) cert_bytes := cert.Bytes() if assert.Equal(len(cert_data), len(cert_bytes)) { @@ -48,9 +48,9 @@ func TestPublicKeyWithBadData(t *testing.T) { data := make([]byte, 128) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - pub_key, err := keys_and_cert.PublicKey() + pub_key := keys_and_cert.PublicKey() if assert.NotNil(err) { assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) } @@ -65,9 +65,9 @@ func TestPublicKeyWithBadCertificate(t *testing.T) { data := make([]byte, 128) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - pub_key, err := keys_and_cert.PublicKey() + pub_key := keys_and_cert.PublicKey() if assert.NotNil(err) { assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) } @@ -82,9 +82,9 @@ func TestPublicKeyWithNullCertificate(t *testing.T) { data := make([]byte, 128) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - pub_key, err := keys_and_cert.PublicKey() + pub_key := keys_and_cert.PublicKey() assert.Nil(err) assert.Equal(len(pub_key_data), pub_key.Len()) } @@ -97,9 +97,9 @@ func TestPublicKeyWithKeyCertificate(t *testing.T) { data := make([]byte, 128) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - pub_key, err := keys_and_cert.PublicKey() + pub_key := keys_and_cert.PublicKey() assert.Nil(err) assert.Equal(len(pub_key_data), pub_key.Len()) } @@ -112,9 +112,9 @@ func TestSigningPublicKeyWithBadData(t *testing.T) { data := make([]byte, 93) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - signing_pub_key, err := keys_and_cert.SigningPublicKey() + signing_pub_key := keys_and_cert.SigningPublicKey() if assert.NotNil(err) { assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) } @@ -129,9 +129,9 @@ func TestSigningPublicKeyWithBadCertificate(t *testing.T) { data := make([]byte, 128) data = append(data, pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - signing_pub_key, err := keys_and_cert.SigningPublicKey() + signing_pub_key := keys_and_cert.SigningPublicKey() if assert.NotNil(err) { assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) } @@ -146,9 +146,9 @@ func TestSigningPublicKeyWithNullCertificate(t *testing.T) { signing_pub_key_data := make([]byte, 128) data := append(pub_key_data, signing_pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - signing_pub_key, err := keys_and_cert.SigningPublicKey() + signing_pub_key := keys_and_cert.SigningPublicKey() assert.Nil(err) assert.Equal(len(signing_pub_key_data), signing_pub_key.Len()) } @@ -161,9 +161,9 @@ func TestSigningPublicKeyWithKeyCertificate(t *testing.T) { signing_pub_key_data := make([]byte, 128) data := append(pub_key_data, signing_pub_key_data...) data = append(data, cert_data...) - keys_and_cert := KeysAndCert(data) + keys_and_cert, _, err := ReadKeysAndCert(data) - signing_pub_key, err := keys_and_cert.SigningPublicKey() + signing_pub_key := keys_and_cert.SigningPublicKey() assert.Nil(err) assert.Equal(len(signing_pub_key_data), signing_pub_key.Len()) } @@ -172,24 +172,12 @@ func TestReadKeysAndCertWithMissingData(t *testing.T) { assert := assert.New(t) cert_data := make([]byte, 128) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) assert.Equal(0, len(remainder)) if assert.NotNil(err) { assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) } - _, err = keys_and_cert.PublicKey() - if assert.NotNil(err) { - assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) - } - _, err = keys_and_cert.SigningPublicKey() - if assert.NotNil(err) { - assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) - } - _, err = keys_and_cert.Certificate() - if assert.NotNil(err) { - assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) - } } func TestReadKeysAndCertWithMissingCertData(t *testing.T) { @@ -197,24 +185,11 @@ func TestReadKeysAndCertWithMissingCertData(t *testing.T) { cert_data := make([]byte, 128+256) cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) assert.Equal(0, len(remainder)) if assert.NotNil(err) { assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) } - - _, err = keys_and_cert.PublicKey() - if assert.NotNil(err) { - assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) - } - _, err = keys_and_cert.SigningPublicKey() - if assert.NotNil(err) { - assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) - } - _, err = keys_and_cert.Certificate() - if assert.NotNil(err) { - assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error()) - } } func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) { @@ -222,16 +197,9 @@ func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) { cert_data := make([]byte, 128+256) cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) assert.Equal(0, len(remainder)) assert.Nil(err) - - _, err = keys_and_cert.PublicKey() - assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate") - _, err = keys_and_cert.SigningPublicKey() - assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate") - _, err = keys_and_cert.Certificate() - assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate") } func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) { @@ -239,16 +207,9 @@ func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) { cert_data := make([]byte, 128+256) cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) assert.Equal(0, len(remainder)) assert.Nil(err) - - _, err = keys_and_cert.PublicKey() - assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate") - _, err = keys_and_cert.SigningPublicKey() - assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate") - _, err = keys_and_cert.Certificate() - assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate") } func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) { @@ -256,18 +217,11 @@ func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) { cert_data := make([]byte, 128+256) cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) if assert.Equal(1, len(remainder)) { assert.Equal("A", string(remainder[0])) } assert.Nil(err) - - _, err = keys_and_cert.PublicKey() - assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate") - _, err = keys_and_cert.SigningPublicKey() - assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate") - _, err = keys_and_cert.Certificate() - assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate") } func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) { @@ -275,16 +229,9 @@ func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T cert_data := make([]byte, 128+256) cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...) - keys_and_cert, remainder, err := ReadKeysAndCert(cert_data) + _, remainder, err := ReadKeysAndCert(cert_data) if assert.Equal(1, len(remainder)) { assert.Equal("A", string(remainder[0])) } assert.Nil(err) - - _, err = keys_and_cert.PublicKey() - assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate") - _, err = keys_and_cert.SigningPublicKey() - assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate") - _, err = keys_and_cert.Certificate() - assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate") } diff --git a/lib/common/lease/lease.go b/lib/common/lease/lease.go index 2c7bc65..e75c83f 100644 --- a/lib/common/lease/lease.go +++ b/lib/common/lease/lease.go @@ -1,4 +1,4 @@ -package common +package lease /* I2P Lease diff --git a/lib/common/lease_set/lease_set.go b/lib/common/lease_set/lease_set.go index 4edebfe..9e4098f 100644 --- a/lib/common/lease_set/lease_set.go +++ b/lib/common/lease_set/lease_set.go @@ -1,4 +1,4 @@ -package common +package lease_set /* I2P LeaseSet @@ -285,10 +285,7 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) { LEASE_SET_SPK_SIZE + 1 + (LEASE_SIZE * lease_count) - cert, err := destination.Certificate() - if err != nil { - return - } + cert := destination.Certificate() cert_type := cert.Type() var end int if cert_type == CERT_KEY { diff --git a/lib/common/lease_set/lease_set_test.go b/lib/common/lease_set/lease_set_test.go index 0040916..97ddf55 100644 --- a/lib/common/lease_set/lease_set_test.go +++ b/lib/common/lease_set/lease_set_test.go @@ -1,16 +1,22 @@ -package common +package lease_set import ( "bytes" "testing" + "github.com/go-i2p/go-i2p/lib/common/certificate" + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/lease" + "github.com/go-i2p/go-i2p/lib/common/router_identity" "github.com/stretchr/testify/assert" ) -func buildDestination() RouterIdentity { +func buildDestination() *router_identity.RouterIdentity { router_ident_data := make([]byte, 128+256) router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...) - return RouterIdentity(router_ident_data) + ident, _, err := router_identity.NewRouterIdentity(router_ident_data) + panic(err) + return ident } func buildPublicKey() []byte { @@ -32,15 +38,15 @@ func buildSigningKey() []byte { func buildLease(n int) []byte { data := make([]byte, 0) for i := 0; i < n; i++ { - lease := make([]byte, LEASE_SIZE) - for p := range lease { - lease[p] = byte(i) + l := make([]byte, lease.LEASE_SIZE) + for p := range l { + l[p] = byte(i) } - for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ { - lease[q] = 0x00 + for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ { + l[q] = 0x00 } - lease[LEASE_SIZE-1] = byte(i + 10) - data = append(data, lease...) + l[lease.LEASE_SIZE-1] = byte(i + 10) + data = append(data, l...) } return data } @@ -55,7 +61,7 @@ func buildSignature(size int) []byte { func buildFullLeaseSet(n int) LeaseSet { lease_set_data := make([]byte, 0) - lease_set_data = append(lease_set_data, buildDestination()...) + lease_set_data = append(lease_set_data, buildDestination().KeysAndCert.KeyCertificate.RawBytes()...) lease_set_data = append(lease_set_data, buildPublicKey()...) lease_set_data = append(lease_set_data, buildSigningKey()...) lease_set_data = append(lease_set_data, byte(n)) @@ -70,11 +76,11 @@ func TestDestinationIsCorrect(t *testing.T) { lease_set := buildFullLeaseSet(1) dest, err := lease_set.Destination() assert.Nil(err) - dest_cert, err := dest.Certificate() - assert.Nil(err) + dest_cert := dest.Certificate() + //assert.Nil(err) cert_type := dest_cert.Type() assert.Nil(err) - assert.Equal(CERT_KEY, cert_type) + assert.Equal(certificate.CERT_KEY, cert_type) } func TestPublicKeyIsCorrect(t *testing.T) { @@ -143,18 +149,18 @@ func TestLeasesHaveCorrectData(t *testing.T) { leases, err := lease_set.Leases() if assert.Nil(err) { for i := 0; i < count; i++ { - lease := make([]byte, LEASE_SIZE) - for p := range lease { - lease[p] = byte(i) + l := make([]byte, lease.LEASE_SIZE) + for p := range l { + l[p] = byte(i) } - for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ { - lease[q] = 0x00 + for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ { + l[q] = 0x00 } - lease[LEASE_SIZE-1] = byte(i + 10) + l[lease.LEASE_SIZE-1] = byte(i + 10) assert.Equal( 0, bytes.Compare( - lease, + l, leases[i][:], ), ) @@ -185,8 +191,9 @@ func TestNewestExpirationIsCorrect(t *testing.T) { lease_set := buildFullLeaseSet(5) latest, err := lease_set.NewestExpiration() assert.Nil(err) + Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)}) assert.Equal( - Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)}, + Date, latest, ) } @@ -197,8 +204,9 @@ func TestOldestExpirationIsCorrect(t *testing.T) { lease_set := buildFullLeaseSet(5) latest, err := lease_set.OldestExpiration() assert.Nil(err) + Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}) assert.Equal( - Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + Date, latest, ) } diff --git a/lib/common/router_address/router_address.go b/lib/common/router_address/router_address.go index 9487918..5bbb797 100644 --- a/lib/common/router_address/router_address.go +++ b/lib/common/router_address/router_address.go @@ -1,4 +1,4 @@ -package common +package router_address /* I2P RouterAddress @@ -154,7 +154,14 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b }).Error("error parsing RouterAddress") router_address.parserErr = err } - options, remainder, err := NewMapping(remainder) + options, remainder, errs := NewMapping(remainder) + for _, err := range errs { + log.WithFields(log.Fields{ + "at": "(RouterAddress) ReadNewRouterAddress", + "reason": "error parsing options", + }).Error("error parsing RouterAddress") + router_address.parserErr = err + } router_address.options = options if err != nil { log.WithFields(log.Fields{ diff --git a/lib/common/router_address/router_address_test.go b/lib/common/router_address/router_address_test.go index b1eea80..6cb90c8 100644 --- a/lib/common/router_address/router_address_test.go +++ b/lib/common/router_address/router_address_test.go @@ -1,4 +1,4 @@ -package common +package router_address import ( "bytes" diff --git a/lib/common/router_identity/router_identity.go b/lib/common/router_identity/router_identity.go index 8b7413e..0f43d9d 100644 --- a/lib/common/router_identity/router_identity.go +++ b/lib/common/router_identity/router_identity.go @@ -1,4 +1,4 @@ -package common +package router_identity /* I2P RouterIdentity diff --git a/lib/common/router_info/router_info.go b/lib/common/router_info/router_info.go index ab649b1..042f1e5 100644 --- a/lib/common/router_info/router_info.go +++ b/lib/common/router_info/router_info.go @@ -1,4 +1,4 @@ -package common +package router_info /* I2P RouterInfo @@ -264,7 +264,20 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error) peer_size := Integer(remainder[:1]) info.peer_size = &peer_size remainder = remainder[1:] - options, remainder, err := NewMapping(remainder) + options, remainder, errs := NewMapping(remainder) + if len(errs) != 0 { + log.WithFields(log.Fields{ + "at": "(RouterInfo) ReadRouterInfo", + "data_len": len(remainder), + //"required_len": MAPPING_SIZE, + "reason": "not enough data", + }).Error("error parsing router info") + estring := "" + for _, e := range errs { + estring += e.Error() + " " + } + err = errors.New("error parsing router info: " + estring) + } info.options = options if err != nil { log.WithFields(log.Fields{ diff --git a/lib/common/router_info/router_info_test.go b/lib/common/router_info/router_info_test.go index 16ab5b1..5fad949 100644 --- a/lib/common/router_info/router_info_test.go +++ b/lib/common/router_info/router_info_test.go @@ -1,16 +1,21 @@ -package common +package router_info import ( "bytes" "fmt" - "github.com/stretchr/testify/assert" "testing" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/router_address" + "github.com/go-i2p/go-i2p/lib/common/router_identity" + "github.com/stretchr/testify/assert" ) -func buildRouterIdentity() RouterIdentity { +func buildRouterIdentity() router_identity.RouterIdentity { router_ident_data := make([]byte, 128+256) router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...) - return RouterIdentity(router_ident_data) + return_data, _, _ := router_identity.ReadRouterIdentity(router_ident_data) + return return_data } func buildDate() []byte { @@ -18,17 +23,18 @@ func buildDate() []byte { return date_data } -func buildMapping() Mapping { - mapping, _ := GoMapToMapping(map[string]string{"host": "127.0.0.1", "port": "4567"}) +func buildMapping() *common.Mapping { + mapping, _ := common.GoMapToMapping(map[string]string{"host": "127.0.0.1", "port": "4567"}) return mapping } -func buildRouterAddress(transport string) RouterAddress { +func buildRouterAddress(transport string) router_address.RouterAddress { router_address_bytes := []byte{0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - str, _ := ToI2PString(transport) + str, _ := common.ToI2PString(transport) router_address_bytes = append(router_address_bytes, []byte(str)...) - router_address_bytes = append(router_address_bytes, buildMapping()...) - return RouterAddress(router_address_bytes) + router_address_bytes = append(router_address_bytes, buildMapping().Data()...) + return_data, _, _ := router_address.ReadRouterAddress(router_address_bytes) + return return_data } func buildFullRouterInfo() RouterInfo { @@ -47,8 +53,7 @@ func TestPublishedReturnsCorrectDate(t *testing.T) { assert := assert.New(t) router_info := buildFullRouterInfo() - date, err := router_info.Published() - assert.Nil(err) + date := router_info.Published() assert.Equal(int64(86400), date.Time().Unix(), "RouterInfo.Published() did not return correct date") } @@ -179,8 +184,8 @@ func TestRouterIdentityIsCorrect(t *testing.T) { assert := assert.New(t) router_info := buildFullRouterInfo() - router_identity, err := router_info.RouterIdentity() - assert.Nil(err) + router_identity := router_info.RouterIdentity() + //assert.Nil(err) assert.Equal( 0, bytes.Compare( diff --git a/lib/common/session_key/session_key.go b/lib/common/session_key/session_key.go index 9456634..b6ed288 100644 --- a/lib/common/session_key/session_key.go +++ b/lib/common/session_key/session_key.go @@ -1,3 +1,3 @@ -package common +package session_key type SessionKey [32]byte diff --git a/lib/common/session_tag/session_tag.go b/lib/common/session_tag/session_tag.go index 92a947f..6c54791 100644 --- a/lib/common/session_tag/session_tag.go +++ b/lib/common/session_tag/session_tag.go @@ -1,3 +1,3 @@ -package common +package session_tag type SessionTag [32]byte diff --git a/lib/common/signature/signature.go b/lib/common/signature/signature.go index 08b4ca0..ba45c07 100644 --- a/lib/common/signature/signature.go +++ b/lib/common/signature/signature.go @@ -1,3 +1,3 @@ -package common +package signature type Signature []byte diff --git a/lib/config/su3.go b/lib/config/su3.go index 1514352..f088d2a 100644 --- a/lib/config/su3.go +++ b/lib/config/su3.go @@ -2,10 +2,11 @@ package config import ( "errors" - "github.com/go-i2p/go-i2p/lib/common" - log "github.com/sirupsen/logrus" "strings" "unicode/utf8" + + datalib "github.com/go-i2p/go-i2p/lib/common/data" + log "github.com/sirupsen/logrus" ) // @@ -289,7 +290,7 @@ func getSignatureLength(data []byte) (int, error) { signature_length_bytes[:], data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN:SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN], ) - signature_length = common.Integer(signature_length_bytes[:]) + signature_length = datalib.Integer(signature_length_bytes[:]).Int() return signature_length, nil } @@ -316,17 +317,17 @@ func getVersionLength(data []byte) (int, error) { return 0, ERR_NOT_ENOUGH_SU3_DATA } - version_length := common.Integer([]byte{data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1]}) + version_length := datalib.Integer([]byte{data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1]}) - if version_length < 16 { + if version_length.Int() < 16 { log.WithFields(log.Fields{ "at": "config.getSignatureType", "version_length": version_length, }).Debug(ERR_SU3_VERSION_LENGTH_TOO_SMALL) - return version_length, ERR_SU3_VERSION_LENGTH_TOO_SMALL + return version_length.Int(), ERR_SU3_VERSION_LENGTH_TOO_SMALL } - return version_length, nil + return version_length.Int(), nil } func checkByte14Unused(data []byte) error { @@ -351,9 +352,9 @@ func getSignerIDLength(data []byte) (int, error) { return 0, ERR_NOT_ENOUGH_SU3_DATA } - signer_id_length := common.Integer([]byte{data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1+1+1]}) + signer_id_length := datalib.Integer([]byte{data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1+1+1]}) - return signer_id_length, nil + return signer_id_length.Int(), nil } func getContentLength(data []byte) (int, error) { @@ -368,7 +369,7 @@ func getContentLength(data []byte) (int, error) { content_length_bytes[:], data[SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1+1+1+1:SU3_MAGIC_BYTE_LEN+1+1+SU3_SIGNATURE_TYPE_LEN+SU3_SIGNATURE_LENGTH_LEN+1+1+1+1+SU3_CONTENT_LENGTH_LEN], ) - content_length = common.Integer(content_length_bytes[:]) + content_length = datalib.Integer(content_length_bytes[:]).Int() return content_length, nil } diff --git a/lib/i2np/build_request_record.go b/lib/i2np/build_request_record.go index 1eabc68..827f1ba 100644 --- a/lib/i2np/build_request_record.go +++ b/lib/i2np/build_request_record.go @@ -2,10 +2,12 @@ package i2np import ( "errors" - "github.com/go-i2p/go-i2p/lib/common" + "time" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/session_key" "github.com/go-i2p/go-i2p/lib/tunnel" log "github.com/sirupsen/logrus" - "time" ) /* @@ -155,9 +157,9 @@ type BuildRequestRecord struct { OurIdent common.Hash NextTunnel tunnel.TunnelID NextIdent common.Hash - LayerKey common.SessionKey - IVKey common.SessionKey - ReplyKey common.SessionKey + LayerKey session_key.SessionKey + IVKey session_key.SessionKey + ReplyKey session_key.SessionKey ReplyIV [16]byte Flag int RequestTime time.Time @@ -251,7 +253,7 @@ func readBuildRequestRecordReceiveTunnel(data []byte) (tunnel.TunnelID, error) { } receive_tunnel := tunnel.TunnelID( - common.Integer(data[0:4]), + common.Integer(data[0:4]).Int(), ) log.WithFields(log.Fields{ @@ -281,7 +283,7 @@ func readBuildRequestRecordNextTunnel(data []byte) (tunnel.TunnelID, error) { } next_tunnel := tunnel.TunnelID( - common.Integer(data[36:40]), + common.Integer(data[36:40]).Int(), ) log.WithFields(log.Fields{ @@ -305,12 +307,12 @@ func readBuildRequestRecordNextIdent(data []byte) (common.Hash, error) { return hash, nil } -func readBuildRequestRecordLayerKey(data []byte) (common.SessionKey, error) { +func readBuildRequestRecordLayerKey(data []byte) (session_key.SessionKey, error) { if len(data) < 104 { - return common.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA + return session_key.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - session_key := common.SessionKey{} + session_key := session_key.SessionKey{} copy(session_key[:], data[72:104]) log.WithFields(log.Fields{ @@ -319,12 +321,12 @@ func readBuildRequestRecordLayerKey(data []byte) (common.SessionKey, error) { return session_key, nil } -func readBuildRequestRecordIVKey(data []byte) (common.SessionKey, error) { +func readBuildRequestRecordIVKey(data []byte) (session_key.SessionKey, error) { if len(data) < 136 { - return common.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA + return session_key.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - session_key := common.SessionKey{} + session_key := session_key.SessionKey{} copy(session_key[:], data[104:136]) log.WithFields(log.Fields{ @@ -333,12 +335,12 @@ func readBuildRequestRecordIVKey(data []byte) (common.SessionKey, error) { return session_key, nil } -func readBuildRequestRecordReplyKey(data []byte) (common.SessionKey, error) { +func readBuildRequestRecordReplyKey(data []byte) (session_key.SessionKey, error) { if len(data) < 168 { - return common.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA + return session_key.SessionKey{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - session_key := common.SessionKey{} + session_key := session_key.SessionKey{} copy(session_key[:], data[136:168]) log.WithFields(log.Fields{ @@ -366,7 +368,7 @@ func readBuildRequestRecordFlag(data []byte) (int, error) { return 0, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - flag := int(common.Integer([]byte{data[185]})) + flag := common.Integer([]byte{data[185]}).Int() log.WithFields(log.Fields{ "at": "i2np.readBuildRequestRecordFlag", @@ -380,7 +382,7 @@ func readBuildRequestRecordRequestTime(data []byte) (time.Time, error) { return time.Time{}, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - count := int(common.Integer(data[185:189])) + count := common.Integer(data[185:189]).Int() rtime := time.Unix(0, 0).Add(time.Duration(count) * time.Hour) log.WithFields(log.Fields{ @@ -394,7 +396,7 @@ func readBuildRequestRecordSendMessageID(data []byte) (int, error) { return 0, ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA } - send_message_id := int(common.Integer(data[189:193])) + send_message_id := common.Integer(data[189:193]).Int() log.WithFields(log.Fields{ "at": "i2np.readBuildRequestRecordSendMessageID", diff --git a/lib/i2np/build_request_record_test.go b/lib/i2np/build_request_record_test.go index 03c5226..b381df1 100644 --- a/lib/i2np/build_request_record_test.go +++ b/lib/i2np/build_request_record_test.go @@ -1,10 +1,11 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + "testing" + + common "github.com/go-i2p/go-i2p/lib/common/data" "github.com/go-i2p/go-i2p/lib/tunnel" "github.com/stretchr/testify/assert" - "testing" ) func TestReadBuildRequestRecordReceiveTunnelTooLittleData(t *testing.T) { diff --git a/lib/i2np/build_response_record.go b/lib/i2np/build_response_record.go index 30425cc..30f12c7 100644 --- a/lib/i2np/build_response_record.go +++ b/lib/i2np/build_response_record.go @@ -1,7 +1,7 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + common "github.com/go-i2p/go-i2p/lib/common/data" ) /* diff --git a/lib/i2np/database_lookup.go b/lib/i2np/database_lookup.go index 76f7855..88afe61 100644 --- a/lib/i2np/database_lookup.go +++ b/lib/i2np/database_lookup.go @@ -1,7 +1,9 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/session_key" + "github.com/go-i2p/go-i2p/lib/common/session_tag" ) /* @@ -143,7 +145,7 @@ type DatabaseLookup struct { ReplyTunnelID [4]byte Size int ExcludedPeers []common.Hash - ReplyKey common.SessionKey + ReplyKey session_key.SessionKey tags int - ReplyTags []common.SessionTag + ReplyTags []session_tag.SessionTag } diff --git a/lib/i2np/database_search_reply.go b/lib/i2np/database_search_reply.go index 0951a33..3c5bf42 100644 --- a/lib/i2np/database_search_reply.go +++ b/lib/i2np/database_search_reply.go @@ -1,7 +1,7 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + common "github.com/go-i2p/go-i2p/lib/common/data" ) /* diff --git a/lib/i2np/database_store.go b/lib/i2np/database_store.go index 10ce550..e85bf89 100644 --- a/lib/i2np/database_store.go +++ b/lib/i2np/database_store.go @@ -1,7 +1,7 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + common "github.com/go-i2p/go-i2p/lib/common/data" ) /* diff --git a/lib/i2np/garlic.go b/lib/i2np/garlic.go index 4b4a1e4..bab7434 100644 --- a/lib/i2np/garlic.go +++ b/lib/i2np/garlic.go @@ -1,8 +1,9 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" "time" + + "github.com/go-i2p/go-i2p/lib/common/certificate" ) /* @@ -66,7 +67,7 @@ type GarlicElGamal []byte type Garlic struct { Count int Cloves []GarlicClove - Certificate common.Certificate + Certificate certificate.Certificate MessageID int Expiration time.Time } diff --git a/lib/i2np/garlic_clove.go b/lib/i2np/garlic_clove.go index 4498a6f..a673bab 100644 --- a/lib/i2np/garlic_clove.go +++ b/lib/i2np/garlic_clove.go @@ -1,8 +1,9 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" "time" + + "github.com/go-i2p/go-i2p/lib/common/certificate" ) /* @@ -45,5 +46,5 @@ type GarlicClove struct { I2NPMessage I2NPMessage CloveID int Expiration time.Time - Certificate common.Certificate + Certificate certificate.Certificate } diff --git a/lib/i2np/garlic_clove_delivery_instructions.go b/lib/i2np/garlic_clove_delivery_instructions.go index 14a1306..9144d33 100644 --- a/lib/i2np/garlic_clove_delivery_instructions.go +++ b/lib/i2np/garlic_clove_delivery_instructions.go @@ -1,7 +1,8 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/session_key" "github.com/go-i2p/go-i2p/lib/tunnel" ) @@ -73,7 +74,7 @@ Total length: Typical length is: type GarlicCloveDeliveryInstructions struct { Flag byte - SessionKey common.SessionKey + SessionKey session_key.SessionKey Hash common.Hash TunnelID tunnel.TunnelID Delay int diff --git a/lib/i2np/header.go b/lib/i2np/header.go index df81287..9cfe7f3 100644 --- a/lib/i2np/header.go +++ b/lib/i2np/header.go @@ -2,9 +2,11 @@ package i2np import ( "errors" - "github.com/go-i2p/go-i2p/lib/common" - log "github.com/sirupsen/logrus" "time" + + datalib "github.com/go-i2p/go-i2p/lib/common/data" + + log "github.com/sirupsen/logrus" ) /* @@ -168,24 +170,24 @@ func ReadI2NPType(data []byte) (int, error) { return 0, ERR_I2NP_NOT_ENOUGH_DATA } - message_type := common.Integer([]byte{data[0]}) + message_type := datalib.Integer([]byte{data[0]}) - if (message_type >= 4 || message_type <= 9) || - (message_type >= 12 || message_type <= 17) { + if (message_type.Int() >= 4 || message_type.Int() <= 9) || + (message_type.Int() >= 12 || message_type.Int() <= 17) { log.WithFields(log.Fields{ "at": "i2np.ReadI2NPType", "type": message_type, }).Warn("unknown_i2np_type") } - if message_type >= 224 || message_type <= 254 { + if message_type.Int() >= 224 || message_type.Int() <= 254 { log.WithFields(log.Fields{ "at": "i2np.ReadI2NPType", "type": message_type, }).Warn("experimental_i2np_type") } - if message_type == 255 { + if message_type.Int() == 255 { log.WithFields(log.Fields{ "at": "i2np.ReadI2NPType", "type": message_type, @@ -196,7 +198,7 @@ func ReadI2NPType(data []byte) (int, error) { "at": "i2np.ReadI2NPType", "type": message_type, }).Debug("parsed_i2np_type") - return message_type, nil + return message_type.Int(), nil } func ReadI2NPNTCPMessageID(data []byte) (int, error) { @@ -204,21 +206,21 @@ func ReadI2NPNTCPMessageID(data []byte) (int, error) { return 0, ERR_I2NP_NOT_ENOUGH_DATA } - message_id := common.Integer(data[1:5]) + message_id := datalib.Integer(data[1:5]) log.WithFields(log.Fields{ "at": "i2np.ReadI2NPNTCPMessageID", "type": message_id, }).Debug("parsed_i2np_message_id") - return message_id, nil + return message_id.Int(), nil } -func ReadI2NPNTCPMessageExpiration(data []byte) (common.Date, error) { +func ReadI2NPNTCPMessageExpiration(data []byte) (datalib.Date, error) { if len(data) < 13 { - return common.Date{}, ERR_I2NP_NOT_ENOUGH_DATA + return datalib.Date{}, ERR_I2NP_NOT_ENOUGH_DATA } - date := common.Date{} + date := datalib.Date{} copy(date[:], data[5:13]) log.WithFields(log.Fields{ @@ -228,12 +230,12 @@ func ReadI2NPNTCPMessageExpiration(data []byte) (common.Date, error) { return date, nil } -func ReadI2NPSSUMessageExpiration(data []byte) (common.Date, error) { +func ReadI2NPSSUMessageExpiration(data []byte) (datalib.Date, error) { if len(data) < 5 { - return common.Date{}, ERR_I2NP_NOT_ENOUGH_DATA + return datalib.Date{}, ERR_I2NP_NOT_ENOUGH_DATA } - date := common.Date{} + date := datalib.Date{} copy(date[4:], data[1:5]) log.WithFields(log.Fields{ @@ -248,13 +250,13 @@ func ReadI2NPNTCPMessageSize(data []byte) (int, error) { return 0, ERR_I2NP_NOT_ENOUGH_DATA } - size := common.Integer(data[13:15]) + size := datalib.Integer(data[13:15]) log.WithFields(log.Fields{ "at": "i2np.ReadI2NPNTCPMessageSize", "size": size, }).Debug("parsed_i2np_message_size") - return size, nil + return size.Int(), nil } func ReadI2NPNTCPMessageChecksum(data []byte) (int, error) { @@ -262,13 +264,13 @@ func ReadI2NPNTCPMessageChecksum(data []byte) (int, error) { return 0, ERR_I2NP_NOT_ENOUGH_DATA } - checksum := common.Integer(data[15:16]) + checksum := datalib.Integer(data[15:16]) log.WithFields(log.Fields{ "at": "i2np.ReadI2NPNTCPMessageCHecksum", "checksum": checksum, }).Debug("parsed_i2np_message_checksum") - return checksum, nil + return checksum.Int(), nil } func ReadI2NPNTCPData(data []byte, size int) ([]byte, error) { diff --git a/lib/i2np/header_test.go b/lib/i2np/header_test.go index e1b60cc..38ecc33 100644 --- a/lib/i2np/header_test.go +++ b/lib/i2np/header_test.go @@ -1,9 +1,10 @@ package i2np import ( - "github.com/go-i2p/go-i2p/lib/common" - "github.com/stretchr/testify/assert" "testing" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/stretchr/testify/assert" ) func TestReadI2NPTypeWithNoData(t *testing.T) { diff --git a/lib/netdb/entry.go b/lib/netdb/entry.go index e7c6fa9..74176f4 100644 --- a/lib/netdb/entry.go +++ b/lib/netdb/entry.go @@ -1,14 +1,15 @@ package netdb import ( - "github.com/go-i2p/go-i2p/lib/common" "io" + + "github.com/go-i2p/go-i2p/lib/common/router_info" ) // netdb entry // wraps a router info and provides serialization type Entry struct { - ri common.RouterInfo + ri router_info.RouterInfo } func (e *Entry) WriteTo(w io.Writer) (err error) { diff --git a/lib/netdb/kad.go b/lib/netdb/kad.go index 3fca557..93cdddb 100644 --- a/lib/netdb/kad.go +++ b/lib/netdb/kad.go @@ -1,9 +1,11 @@ package netdb import ( - "github.com/go-i2p/go-i2p/lib/common" - "github.com/go-i2p/go-i2p/lib/tunnel" "time" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/router_info" + "github.com/go-i2p/go-i2p/lib/tunnel" ) // resolves router infos with recursive kademlia lookup @@ -16,7 +18,7 @@ type kadResolver struct { } // TODO: implement -func (kr *kadResolver) Lookup(h common.Hash, timeout time.Duration) (chnl chan common.RouterInfo) { +func (kr *kadResolver) Lookup(h common.Hash, timeout time.Duration) (chnl chan router_info.RouterInfo) { return } diff --git a/lib/netdb/netdb.go b/lib/netdb/netdb.go index 5ab19ff..4f3b73a 100644 --- a/lib/netdb/netdb.go +++ b/lib/netdb/netdb.go @@ -1,16 +1,18 @@ package netdb import ( - "github.com/go-i2p/go-i2p/lib/bootstrap" - "github.com/go-i2p/go-i2p/lib/common" "time" + + "github.com/go-i2p/go-i2p/lib/bootstrap" + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/router_info" ) // resolves unknown RouterInfos given the hash of their RouterIdentity type Resolver interface { // resolve a router info by hash // return a chan that yields the found RouterInfo or nil if it could not be found after timeout - Lookup(hash common.Hash, timeout time.Duration) chan common.RouterInfo + Lookup(hash common.Hash, timeout time.Duration) chan router_info.RouterInfo } // i2p network database, storage of i2p RouterInfos @@ -18,10 +20,10 @@ type NetworkDatabase interface { // obtain a RouterInfo by its hash locally // return a RouterInfo if we found it locally // return nil if the RouterInfo cannot be found locally - GetRouterInfo(hash common.Hash) common.RouterInfo + GetRouterInfo(hash common.Hash) router_info.RouterInfo // store a router info locally - StoreRouterInfo(ri common.RouterInfo) + StoreRouterInfo(ri router_info.RouterInfo) // try obtaining more peers with a bootstrap instance until we get minRouters number of router infos // returns error if bootstrap.GetPeers returns an error otherwise returns nil diff --git a/lib/netdb/std.go b/lib/netdb/std.go index 19ce9f0..a5c0d49 100644 --- a/lib/netdb/std.go +++ b/lib/netdb/std.go @@ -3,23 +3,25 @@ package netdb import ( "bytes" "fmt" - "github.com/go-i2p/go-i2p/lib/bootstrap" - "github.com/go-i2p/go-i2p/lib/common" - "github.com/go-i2p/go-i2p/lib/common/base64" - "github.com/go-i2p/go-i2p/lib/util" - log "github.com/sirupsen/logrus" "io" "io/ioutil" "os" "path/filepath" "strconv" "strings" + + "github.com/go-i2p/go-i2p/lib/bootstrap" + "github.com/go-i2p/go-i2p/lib/common/base64" + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/router_info" + "github.com/go-i2p/go-i2p/lib/util" + log "github.com/sirupsen/logrus" ) // standard network database implementation using local filesystem skiplist type StdNetDB string -func (db StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan common.RouterInfo) { +func (db StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan router_info.RouterInfo) { fname := db.SkiplistFile(hash) f, err := os.Open(fname) if err != nil { @@ -28,8 +30,11 @@ func (db StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan common.RouterInfo) buff := new(bytes.Buffer) _, err = io.Copy(buff, f) f.Close() - chnl = make(chan common.RouterInfo) - chnl <- common.RouterInfo(buff.Bytes()) + chnl = make(chan router_info.RouterInfo) + ri, _, err := router_info.ReadRouterInfo(buff.Bytes()) + if err == nil { + chnl <- ri + } return } @@ -125,14 +130,14 @@ func (db StdNetDB) Exists() bool { func (db StdNetDB) SaveEntry(e *Entry) (err error) { var f io.WriteCloser var h common.Hash - h, err = e.ri.IdentHash() + h = e.ri.IdentHash() + //if err == nil { + f, err = os.OpenFile(db.SkiplistFile(h), os.O_WRONLY|os.O_CREATE, 0700) if err == nil { - f, err = os.OpenFile(db.SkiplistFile(h), os.O_WRONLY|os.O_CREATE, 0700) - if err == nil { - err = e.WriteTo(f) - f.Close() - } + err = e.WriteTo(f) + f.Close() } + //} if err != nil { log.Errorf("failed to save netdb entry: %s", err.Error()) } diff --git a/lib/transport/messages/message.go b/lib/transport/messages/message.go new file mode 100644 index 0000000..8e87f53 --- /dev/null +++ b/lib/transport/messages/message.go @@ -0,0 +1,63 @@ +package ntcp + +/** + Messages + ======== + + All NTCP2 messages are less than or equal to 65537 bytes in length. The message + format is based on Noise messages, with modifications for framing and indistinguishability. + Implementations using standard Noise libraries may need to pre-process received + messages to/from the Noise message format. All encrypted fields are AEAD + ciphertexts. + + + The establishment sequence is as follows: + + Alice Bob + + SessionRequest -------------------> + <------------------- SessionCreated + SessionConfirmed -----------------> + + + Using Noise terminology, the establishment and data sequence is as follows: + (Payload Security Properties) + + XK(s, rs): Authentication Confidentiality + <- s + ... + -> e, es 0 2 + <- e, ee 2 1 + -> s, se 2 5 + <- 2 5 + + + + Once a session has been established, Alice and Bob can exchange Data messages. + + All message types (SessionRequest, SessionCreated, SessionConfirmed, Data and + TimeSync) are specified in this section. + + Some notations:: + + - RH_A = Router Hash for Alice (32 bytes) + - RH_B = Router Hash for Bob (32 bytes) +**/ + +type MessageType uint8 + +const MessageTypeSessionRequest = 0x00 +const MessageTypeSessionCreated = 0x01 +const MessageTypeSessionConfirmed = 0x02 +const MessageTypeData = 0x03 + +type Message interface { + // Type returns the message type + Type() MessageType + // Payload returns the message payload + Payload() []byte + // PayloadSize returns the message payload size + PayloadSize() int + // PayloadSecurityProperties returns the message payload security properties + // PayloadSecurityProperties() PayloadSecurityProperties +} diff --git a/lib/transport/messages/sessionconfirmed.go b/lib/transport/messages/sessionconfirmed.go new file mode 100644 index 0000000..53f65fa --- /dev/null +++ b/lib/transport/messages/sessionconfirmed.go @@ -0,0 +1,248 @@ +package ntcp + +/** + 3) SessionConfirmed + -------------------- + + Alice sends to Bob. + + Noise content: Alice's static key + Noise payload: Alice's RouterInfo and random padding + Non-noise payload: none + + (Payload Security Properties) + + + XK(s, rs): Authentication Confidentiality + -> s, se 2 5 + + Authentication: 2. + Sender authentication resistant to key-compromise impersonation (KCI). The + sender authentication is based on an ephemeral-static DH ("es" or "se") + between the sender's static key pair and the recipient's ephemeral key + pair. Assuming the corresponding private keys are secure, this + authentication cannot be forged. + + Confidentiality: 5. + Encryption to a known recipient, strong forward secrecy. This payload is + encrypted based on an ephemeral-ephemeral DH as well as an ephemeral-static + DH with the recipient's static key pair. Assuming the ephemeral private + keys are secure, and the recipient is not being actively impersonated by an + attacker that has stolen its static private key, this payload cannot be + decrypted. + + "s": Alice writes her static public key from the s variable into the + message buffer, encrypting it, and hashes the output along with the old h + to derive a new h. + + "se": A DH is performed between the Alice's static key pair and the Bob's + ephemeral key pair. The result is hashed along with the old ck to derive a + new ck and k, and n is set to zero. + + + This contains two ChaChaPoly frames. + The first is Alice's encrypted static public key. + The second is the Noise payload: Alice's encrypted RouterInfo, optional + options, and optional padding. They use different keys, because the MixKey() + function is called in between. + + + Raw contents: + + +----+----+----+----+----+----+----+----+ + | | + + ChaChaPoly frame (48 bytes) + + | Encrypted and authenticated | + + Alice static key S + + | (32 bytes) | + + + + | k defined in KDF for message 2 | + + n = 1 + + | see KDF for associated data | + + + + | | + +----+----+----+----+----+----+----+----+ + | | + + Length specified in message 1 + + | | + + ChaChaPoly frame + + | Encrypted and authenticated | + + + + | Alice RouterInfo | + + using block format 2 + + | Alice Options (optional) | + + using block format 1 + + | Arbitrary padding | + + using block format 254 + + | | + + + + | k defined in KDF for message 3 part 2 | + + n = 0 + + | see KDF for associated data | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + S :: 32 bytes, ChaChaPoly encrypted Alice's X25519 static key, little endian + inside 48 byte ChaChaPoly frame + + + Unencrypted data (Poly1305 auth tags not shown): + + +----+----+----+----+----+----+----+----+ + | | + + + + | S | + + Alice static key + + | (32 bytes) | + + + + | | + + + + +----+----+----+----+----+----+----+----+ + | | + + + + | | + + + + | Alice RouterInfo block | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + | | + + Optional Options block + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + | | + + Optional Padding block + + | | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + S :: 32 bytes, Alice's X25519 static key, little endian + + + + Notes + ````` + - Bob must perform the usual Router Info validation. + Ensure the signature type is supported, verify the signature, + verify the timestamp is within bounds, and any other checks necessary. + + - Bob must verify that Alice's static key received in the first frame matches + the static key in the Router Info. Bob must first search the Router Info for + a NTCP or NTCP2 Router Address with a matching version (v) option. + See Published Router Info and Unpublished Router Info sections below. + + - If Bob has an older version of Alice's RouterInfo in his netdb, verify + that the static key in the router info is the same in both, if present, + and if the older version is less than XXX old (see key rotate time below) + + - Bob must validate that Alice's static key is a valid point on the curve here. + + - Options should be included, to specify padding parameters. + + - On any error, including AEAD, RI, DH, timestamp, or key validation failure, + Bob must halt further message processing and close the connection without + responding. This should be an abnormal close (TCP RST). + + - To facilitate rapid handshaking, implementations must ensure that Alice + buffers and then flushes the entire contents of the third message at once, + including both AEAD frames. + This increases the likelihood that the data will be contained in a single TCP + packet (unless segmented by the OS or middleboxes), and received all at once + by Bob. This is also for efficiency and to ensure the effectiveness of the + random padding. + + - Message 3 part 2 frame length: The length of this frame (including MAC) is + sent by Alice in message 1. See that message for important notes on allowing + enough room for padding. + + - Message 3 part 2 frame content: This format of this frame is the same as the + format of data phase frames, except that the length of the frame is sent + by Alice in message 1. See below for the data phase frame format. + The frame must contain 1 to 3 blocks in the following order: + 1) Alice's Router Info block (required) + 2) Options block (optional) + 3) Padding block (optional) + This frame must never contain any other block type. + + - Message 3 part 2 padding is not required if Alice appends a data phase frame + (optionally containing padding) to the end of message 3 and sends both at once, + as it will appear as one big stream of bytes to an observer. + As Alice will generally, but not always, have an I2NP message to send to Bob + (that's why she connected to him), this is the recommended implementation, + for efficiency and to ensure the effectiveness of the random padding. + + - Total length of both Message 3 AEAD frames (parts 1 and 2) is 65535 bytes; + part 1 is 48 bytes so part 2 max frame length is 65487; + part 2 max plaintext length excluding MAC is 65471. + + + Key Derivation Function (KDF) (for data phase) + ---------------------------------------------- + + The data phase uses a zero-length associated data input. + + + The KDF generates two cipher keys k_ab and k_ba from the chaining key ck, + using HMAC-SHA256(key, data) as defined in [RFC-2104]. + This is the Split() function, exactly as defined in the Noise spec. + + ck = from handshake phase + + // k_ab, k_ba = HKDF(ck, zerolen) + // ask_master = HKDF(ck, zerolen, info="ask") + + // zerolen is a zero-length byte array + temp_key = HMAC-SHA256(ck, zerolen) + // overwrite the chaining key in memory, no longer needed + ck = (all zeros) + + // Output 1 + // cipher key, for Alice transmits to Bob (Noise doesn't make clear which is which, but Java code does) + k_ab = HMAC-SHA256(temp_key, byte(0x01)). + + // Output 2 + // cipher key, for Bob transmits to Alice (Noise doesn't make clear which is which, but Java code does) + k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)). + + + KDF for SipHash for length field: + Generate an Additional Symmetric Key (ask) for SipHash + SipHash uses two 8-byte keys (big endian) and 8 byte IV for first data. + + // "ask" is 3 bytes, US-ASCII, no null termination + ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01)) + // sip_master = HKDF(ask_master, h || "siphash") + // "siphash" is 7 bytes, US-ASCII, no null termination + // overwrite previous temp_key in memory + // h is from KDF for message 3 part 2 + temp_key = HMAC-SHA256(ask_master, h || "siphash") + // overwrite ask_master in memory, no longer needed + ask_master = (all zeros) + sip_master = HMAC-SHA256(temp_key, byte(0x01)) + + Alice to Bob SipHash k1, k2, IV: + // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen) + // overwrite previous temp_key in memory + temp_key = HMAC-SHA256(sip_master, zerolen) + // overwrite sip_master in memory, no longer needed + sip_master = (all zeros) + + sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)). + sipk1_ab = sipkeys_ab[0:7], little endian + sipk2_ab = sipkeys_ab[8:15], little endian + sipiv_ab = sipkeys_ab[16:23] + + Bob to Alice SipHash k1, k2, IV: + + sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)). + sipk1_ba = sipkeys_ba[0:7], little endian + sipk2_ba = sipkeys_ba[8:15], little endian + sipiv_ba = sipkeys_ba[16:23] + + // overwrite the temp_key in memory, no longer needed + temp_key = (all zeros) +**/ diff --git a/lib/transport/messages/sessioncreated.go b/lib/transport/messages/sessioncreated.go new file mode 100644 index 0000000..1263a62 --- /dev/null +++ b/lib/transport/messages/sessioncreated.go @@ -0,0 +1,263 @@ +package ntcp + +/** + 2) SessionCreated + ------------------ + + Bob sends to Alice. + + Noise content: Bob's ephemeral key Y + Noise payload: 16 byte option block + Non-noise payload: Random padding + + (Payload Security Properties) + + XK(s, rs): Authentication Confidentiality + <- e, ee 2 1 + + Authentication: 2. + Sender authentication resistant to key-compromise impersonation (KCI). + The sender authentication is based on an ephemeral-static DH ("es" or "se") + between the sender's static key pair and the recipient's ephemeral key pair. + Assuming the corresponding private keys are secure, this authentication cannot be forged. + + Confidentiality: 1. + Encryption to an ephemeral recipient. + This payload has forward secrecy, since encryption involves an ephemeral-ephemeral DH ("ee"). + However, the sender has not authenticated the recipient, + so this payload might be sent to any party, including an active attacker. + + + "e": Bob generates a new ephemeral key pair and stores it in the e variable, + writes the ephemeral public key as cleartext into the message buffer, + and hashes the public key along with the old h to derive a new h. + + "ee": A DH is performed between the Bob's ephemeral key pair and the Alice's ephemeral key pair. + The result is hashed along with the old ck to derive a new ck and k, and n is set to zero. + + + The Y value is encrypted to ensure payload indistinguishably and uniqueness, + which are necessary DPI countermeasures. We use AES encryption to achieve + this, rather than more complex and slower alternatives such as elligator2. + Asymmetric encryption to Alice's router public key would be far too slow. AES + encryption uses Bob's router hash as the key and the AES state from message 1 + (which was initialized with Bob's IV as published in the network database). + + AES encryption is for DPI resistance only. Any party knowing Bob's router hash + and IV, which are published in the network database, and captured the first 32 + bytes of message 1, may decrypt the Y value in this message. + + + Raw contents: + + +----+----+----+----+----+----+----+----+ + | | + + obfuscated with RH_B + + | AES-CBC-256 encrypted Y | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | ChaChaPoly frame | + + Encrypted and authenticated data + + | 32 bytes | + + k defined in KDF for message 2 + + | n = 0; see KDF for associated data | + + + + | | + +----+----+----+----+----+----+----+----+ + | unencrypted authenticated | + + padding (optional) + + | length defined in options block | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + Y :: 32 bytes, AES-256-CBC encrypted X25519 ephemeral key, little endian + key: RH_B + iv: Using AES state from message 1 + + + Unencrypted data (Poly1305 auth tag not shown): + + +----+----+----+----+----+----+----+----+ + | | + + + + | Y | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | options | + + (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + | unencrypted authenticated | + + padding (optional) + + | length defined in options block | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + Y :: 32 bytes, X25519 ephemeral key, little endian + + options :: options block, 16 bytes, see below + + padding :: Random data, 0 or more bytes. + Total message length must be 65535 bytes or less. + Alice and Bob will use the padding data in the KDF for message 3 part 1. + It is authenticated so that any tampering will cause the + next message to fail. + + + Notes + ````` + + - Alice must validate that Bob's ephemeral key is a valid point on the curve + here. + + - Padding should be limited to a reasonable amount. + Alice may reject connections with excessive padding. + Alice will specify her padding options in message 3. + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution is implementation-dependent) + + - On any error, including AEAD, DH, timestamp, apparent replay, or key + validation failure, Alice must halt further message processing and close the + connection without responding. This should be an abnormal close (TCP RST). + + - To facilitate rapid handshaking, implementations must ensure that Bob buffers + and then flushes the entire contents of the first message at once, including + the padding. This increases the likelihood that the data will be contained + in a single TCP packet (unless segmented by the OS or middleboxes), and + received all at once by Alice. This is also for efficiency and to ensure the + effectiveness of the random padding. + + - Alice must fail the connection if any incoming data remains after validating + message 2 and reading in the padding. There should be no extra data from Bob, + as Alice has not responded with message 3 yet. + + + Options block: + Note: All fields are big-endian. + + +----+----+----+----+----+----+----+----+ + | Rsvd(0) | padLen | Reserved (0) | + +----+----+----+----+----+----+----+----+ + | tsB | Reserved (0) | + +----+----+----+----+----+----+----+----+ + + Reserved :: 10 bytes total, set to 0 for compatibility with future options + + padLen :: 2 bytes, big endian, length of the padding, 0 or more + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution is implementation-dependent) + + tsB :: 4 bytes, big endian, Unix timestamp, unsigned seconds. + Wraps around in 2106 + + + Notes + ````` + - Alice must reject connections where the timestamp value is too far off from + the current time. Call the maximum delta time "D". Alice must maintain a + local cache of previously-used handshake values and reject duplicates, to + prevent replay attacks. Values in the cache must have a lifetime of at least + 2*D. The cache values are implementation-dependent, however the 32-byte Y + value (or its encrypted equivalent) may be used. + + Issues + `````` + - Include min/max padding options here? + + + + Encryption for for handshake message 3 part 1, using message 2 KDF) + ------------------------------------------------------------------- + + // take h saved from message 2 KDF + // MixHash(ciphertext) + h = SHA256(h || 24 byte encrypted payload from message 2) + + // MixHash(padding) + // Only if padding length is nonzero + h = SHA256(h || random padding from message 2) + // h is used as the associated data for the AEAD in message 3 part 1, below + + This is the "s" message pattern: + + Define s = Alice's static public key, 32 bytes + + // EncryptAndHash(s.publickey) + // EncryptWithAd(h, s.publickey) + // AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data) + // k is from handshake message 1 + // n is 1 + ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, s.publickey) + // MixHash(ciphertext) + // || below means append + h = SHA256(h || ciphertext); + + // h is used as the associated data for the AEAD in message 3 part 2 + + End of "s" message pattern. + + + + Key Derivation Function (KDF) (for handshake message 3 part 2) + -------------------------------------------------------------- + + This is the "se" message pattern: + + // DH(s, re) == DH(e, rs) + Define input_key_material = 32 byte DH result of Alice's static key and Bob's ephemeral key + Set input_key_material = X25519 DH result + // overwrite Bob's ephemeral key in memory, no longer needed + // Alice: + re = (all zeros) + // Bob: + e(public and private) = (all zeros) + + // MixKey(DH()) + + Define temp_key = 32 bytes + Define HMAC-SHA256(key, data) as in [RFC-2104] + // Generate a temp key from the chaining key and DH result + // ck is the chaining key, from the KDF for handshake message 1 + temp_key = HMAC-SHA256(ck, input_key_material) + // overwrite the DH result in memory, no longer needed + input_key_material = (all zeros) + + // Output 1 + // Set a new chaining key from the temp key + // byte() below means a single byte + ck = HMAC-SHA256(temp_key, byte(0x01)). + + // Output 2 + // Generate the cipher key k + Define k = 32 bytes + // || below means append + // byte() below means a single byte + k = HMAC-SHA256(temp_key, ck || byte(0x02)). + + // h from message 3 part 1 is used as the associated data for the AEAD in message 3 part 2 + + // EncryptAndHash(payload) + // EncryptWithAd(h, payload) + // AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data) + // n is 0 + ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, payload) + // MixHash(ciphertext) + // || below means append + h = SHA256(h || ciphertext); + + // retain the chaining key ck for the data phase KDF + // retain the hash h for the data phase Additional Symmetric Key (SipHash) KDF + + End of "se" message pattern. + + // overwrite the temp_key in memory, no longer needed + temp_key = (all zeros) +**/ diff --git a/lib/transport/messages/sessionrequest.go b/lib/transport/messages/sessionrequest.go new file mode 100644 index 0000000..6e455a3 --- /dev/null +++ b/lib/transport/messages/sessionrequest.go @@ -0,0 +1,270 @@ +package ntcp + +/** + 1) SessionRequest + ------------------ + + Alice sends to Bob. + + Noise content: Alice's ephemeral key X + Noise payload: 16 byte option block + Non-noise payload: Random padding + + (Payload Security Properties) + + XK(s, rs): Authentication Confidentiality + -> e, es 0 2 + + Authentication: None (0). + This payload may have been sent by any party, including an active attacker. + + Confidentiality: 2. + Encryption to a known recipient, forward secrecy for sender compromise + only, vulnerable to replay. This payload is encrypted based only on DHs + involving the recipient's static key pair. If the recipient's static + private key is compromised, even at a later date, this payload can be + decrypted. This message can also be replayed, since there's no ephemeral + contribution from the recipient. + + "e": Alice generates a new ephemeral key pair and stores it in the e + variable, writes the ephemeral public key as cleartext into the + message buffer, and hashes the public key along with the old h to + derive a new h. + + "es": A DH is performed between the Alice's ephemeral key pair and the + Bob's static key pair. The result is hashed along with the old ck to + derive a new ck and k, and n is set to zero. + + + The X value is encrypted to ensure payload indistinguishably + and uniqueness, which are necessary DPI countermeasures. + We use AES encryption to achieve this, + rather than more complex and slower alternatives such as elligator2. + Asymmetric encryption to Bob's router public key would be far too slow. + AES encryption uses Bob's router hash as the key and Bob's IV as published + in the network database. + + AES encryption is for DPI resistance only. + Any party knowing Bob's router hash, and IV, which are published in the network database, + may decrypt the X value in this message. + + The padding is not encrypted by Alice. + It may be necessary for Bob to decrypt the padding, + to inhibit timing attacks. + + + Raw contents: + + +----+----+----+----+----+----+----+----+ + | | + + obfuscated with RH_B + + | AES-CBC-256 encrypted X | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | | + + + + | ChaChaPoly frame | + + (32 bytes) + + | k defined in KDF for message 1 | + + n = 0 + + | see KDF for associated data | + +----+----+----+----+----+----+----+----+ + | unencrypted authenticated | + ~ padding (optional) ~ + | length defined in options block | + +----+----+----+----+----+----+----+----+ + + X :: 32 bytes, AES-256-CBC encrypted X25519 ephemeral key, little endian + key: RH_B + iv: As published in Bobs network database entry + + padding :: Random data, 0 or more bytes. + Total message length must be 65535 bytes or less. + Total message length must be 287 bytes or less if + Bob is publishing his address as NTCP + (see Version Detection section below). + Alice and Bob will use the padding data in the KDF for message 2. + It is authenticated so that any tampering will cause the + next message to fail. + + + Unencrypted data (Poly1305 authentication tag not shown): + + +----+----+----+----+----+----+----+----+ + | | + + + + | X | + + (32 bytes) + + | | + + + + | | + +----+----+----+----+----+----+----+----+ + | options | + + (16 bytes) + + | | + +----+----+----+----+----+----+----+----+ + | unencrypted authenticated | + + padding (optional) + + | length defined in options block | + ~ . . . ~ + | | + +----+----+----+----+----+----+----+----+ + + X :: 32 bytes, X25519 ephemeral key, little endian + + options :: options block, 16 bytes, see below + + padding :: Random data, 0 or more bytes. + Total message length must be 65535 bytes or less. + Total message length must be 287 bytes or less if + Bob is publishing his address as "NTCP" + (see Version Detection section below) + Alice and Bob will use the padding data in the KDF for message 2. + It is authenticated so that any tampering will cause the + next message to fail. + + + Options block: + Note: All fields are big-endian. + + +----+----+----+----+----+----+----+----+ + | id | ver| padLen | m3p2len | Rsvd(0) | + +----+----+----+----+----+----+----+----+ + | tsA | Reserved (0) | + +----+----+----+----+----+----+----+----+ + + id :: 1 byte, the network ID (currently 2, except for test networks) + As of 0.9.42. See proposal 147. + + ver :: 1 byte, protocol version (currently 2) + + padLen :: 2 bytes, length of the padding, 0 or more + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution is implementation-dependent) + + m3p2Len :: 2 bytes, length of the the second AEAD frame in SessionConfirmed + (message 3 part 2) See notes below + + Rsvd :: 2 bytes, set to 0 for compatibility with future options + + tsA :: 4 bytes, Unix timestamp, unsigned seconds. + Wraps around in 2106 + + Reserved :: 4 bytes, set to 0 for compatibility with future options + + + Notes + ````` + - When the published address is "NTCP", Bob supports both NTCP and NTCP2 on the + same port. For compatibility, when initiating a connection to an address + published as "NTCP", Alice must limit the maximum size of this message, + including padding, to 287 bytes or less. This facilitates automatic protocol + identification by Bob. When published as "NTCP2", there is no size + restriction. See the Published Addresses and Version Detection sections + below. + + - The unique X value in the initial AES block ensure that the ciphertext is + different for every session. + + - Bob must reject connections where the timestamp value is too far off from the + current time. Call the maximum delta time "D". Bob must maintain a local + cache of previously-used handshake values and reject duplicates, to prevent + replay attacks. Values in the cache must have a lifetime of at least 2*D. + The cache values are implementation-dependent, however the 32-byte X value + (or its encrypted equivalent) may be used. + + - Diffie-Hellman ephemeral keys may never be reused, to prevent cryptographic attacks, + and reuse will be rejected as a replay attack. + + - The "KE" and "auth" options must be compatible, i.e. the shared secret K must + be of the appropriate size. If more "auth" options are added, this could + implicitly change the meaning of the "KE" flag to use a different KDF or a + different truncation size. + + - Bob must validate that Alice's ephemeral key is a valid point on the curve + here. + + - Padding should be limited to a reasonable amount. Bob may reject connections + with excessive padding. Bob will specify his padding options in message 2. + Min/max guidelines TBD. Random size from 0 to 31 bytes minimum? + (Distribution is implementation-dependent) + + - On any error, including AEAD, DH, timestamp, apparent replay, or key + validation failure, Bob must halt further message processing and close the + connection without responding. This should be an abnormal close (TCP RST). + For probing resistance, after an AEAD failure, Bob should + set a random timeout (range TBD) and then read a random number of bytes (range TBD), + before closing the socket. + + - DoS Mitigation: DH is a relatively expensive operation. As with the previous NTCP protocol, + routers should take all necessary measures to prevent CPU or connection exhaustion. + Place limits on maximum active connections and maximum connection setups in progress. + Enforce read timeouts (both per-read and total for "slowloris"). + Limit repeated or simultaneous connections from the same source. + Maintain blacklists for sources that repeatedly fail. + Do not respond to AEAD failure. + + - To facilitate rapid version detection and handshaking, implementations must + ensure that Alice buffers and then flushes the entire contents of the first + message at once, including the padding. This increases the likelihood that + the data will be contained in a single TCP packet (unless segmented by the OS + or middleboxes), and received all at once by Bob. Additionally, + implementations must ensure that Bob buffers and then flushes the entire + contents of the second message at once, including the padding. and that Bob + buffers and then flushes the entire contents of the third message at once. + This is also for efficiency and to ensure the effectiveness of the random + padding. + + - "ver" field: The overall Noise protocol, extensions, and NTCP protocol + including payload specifications, indicating NTCP2. + This field may be used to indicate support for future changes. + + - Message 3 part 2 length: This is the size of the second AEAD frame (including 16-byte MAC) + containing Alice's Router Info and optional padding that will be sent in + the SessionConfirmed message. As routers periodically regenerate and republish + their Router Info, the size of the current Router Info may change before + message 3 is sent. Implementations must choose one of two strategies: + a) save the current Router Info to be sent in message 3, so the size is known, + and optionally add room for padding; + b) increase the specified size enough to allow for possible increase in + the Router Info size, and always add padding when message 3 is actually sent. + In either case, the "m3p2len" length included in message 1 must be exactly the + size of that frame when sent in message 3. + + - Bob must fail the connection if any incoming data remains after validating + message 1 and reading in the padding. There should be no extra data from Alice, + as Bob has not responded with message 2 yet. + + - The network ID field is used to quickly identify cross-network connections. + If this field is nonzero, and does not match Bob's network ID, + Bob should disconnect and block future connections. + Any connections from test networks should have a different ID and will fail the test. + As of 0.9.42. See proposal 147 for more information. + +**/ + +type SessionRequest struct { + XContent []byte // 32-byte X value + payload []byte // payload of message 1 + Padding []byte // padding of message 1 +} + +// Type returns the message type +func (sr *SessionRequest) Type() MessageType { + return MessageTypeSessionRequest +} + +// Payload returns the message payload +func (sr *SessionRequest) Payload() []byte { + return sr.payload +} + +// PayloadSize returns the message payload size +func (sr *SessionRequest) PayloadSize() int { + return len(sr.payload) +} + +var exampleSessionRequest Message = &SessionRequest{} diff --git a/lib/transport/multi.go b/lib/transport/multi.go index 55b8b54..65617ad 100644 --- a/lib/transport/multi.go +++ b/lib/transport/multi.go @@ -1,7 +1,8 @@ package transport import ( - "github.com/go-i2p/go-i2p/lib/common" + "github.com/go-i2p/go-i2p/lib/common/router_identity" + "github.com/go-i2p/go-i2p/lib/common/router_info" ) // muxes multiple transports into 1 Transport @@ -19,7 +20,7 @@ func Mux(t ...Transport) (tmux *TransportMuxer) { } // set the identity for every transport -func (tmux *TransportMuxer) SetIdentity(ident common.RouterIdentity) (err error) { +func (tmux *TransportMuxer) SetIdentity(ident router_identity.RouterIdentity) (err error) { for _, t := range tmux.trans { err = t.SetIdentity(ident) if err != nil { @@ -53,7 +54,7 @@ func (tmux *TransportMuxer) Name() string { // get a transport session given a router info // return session and nil if successful // return nil and ErrNoTransportAvailable if we failed to get a session -func (tmux *TransportMuxer) GetSession(routerInfo common.RouterInfo) (s TransportSession, err error) { +func (tmux *TransportMuxer) GetSession(routerInfo router_info.RouterInfo) (s TransportSession, err error) { for _, t := range tmux.trans { // pick the first one that is compatable if t.Compatable(routerInfo) { @@ -74,7 +75,7 @@ func (tmux *TransportMuxer) GetSession(routerInfo common.RouterInfo) (s Transpor } // is there a transport that we mux that is compatable with this router info? -func (tmux *TransportMuxer) Compatable(routerInfo common.RouterInfo) (compat bool) { +func (tmux *TransportMuxer) Compatable(routerInfo router_info.RouterInfo) (compat bool) { for _, t := range tmux.trans { if t.Compatable(routerInfo) { compat = true diff --git a/lib/transport/noise/session.go b/lib/transport/noise/session.go index 5b190a3..dbd2c56 100644 --- a/lib/transport/noise/session.go +++ b/lib/transport/noise/session.go @@ -1 +1,37 @@ package noise + +import ( + cb "github.com/emirpasic/gods/queues/circularbuffer" + //. "github.com/flynn/noise" + "github.com/go-i2p/go-i2p/lib/i2np" + "github.com/go-i2p/go-i2p/lib/transport" +) + +type NoiseSession struct { + *cb.Queue +} + +var exampleNoiseSession transport.TransportSession = &NoiseSession{} + +func (s *NoiseSession) QueueSendI2NP(msg i2np.I2NPMessage) { + s.Queue.Enqueue(msg) +} + +func (s *NoiseSession) SendQueueSize() int { + return s.Queue.Size() +} + +func (s *NoiseSession) ReadNextI2NP() (i2np.I2NPMessage, error) { + return i2np.I2NPMessage{}, nil +} + +func (s *NoiseSession) Close() error { + s.Queue.Clear() + return nil +} + +func NewNoiseSession() transport.TransportSession { + return &NoiseSession{ + Queue: cb.New(1024), + } +} diff --git a/lib/transport/transport.go b/lib/transport/transport.go index 5dbceb1..bdd2c8a 100644 --- a/lib/transport/transport.go +++ b/lib/transport/transport.go @@ -1,7 +1,9 @@ package transport import ( - "github.com/go-i2p/go-i2p/lib/common" + "github.com/go-i2p/go-i2p/lib/common/router_identity" + "github.com/go-i2p/go-i2p/lib/common/router_info" + "github.com/go-i2p/go-i2p/lib/i2np" ) @@ -26,16 +28,16 @@ type Transport interface { // will bind if the underlying socket is not already // if the underlying socket is already bound update the RouterIdentity // returns any errors that happen if they do - SetIdentity(ident common.RouterIdentity) error + SetIdentity(ident router_identity.RouterIdentity) error // Obtain a transport session with a router given its RouterInfo. // If a session with this router is NOT already made attempt to create one and block until made or until an error happens // returns an established TransportSession and nil on success // returns nil and an error on error - GetSession(routerInfo common.RouterInfo) (TransportSession, error) + GetSession(routerInfo router_info.RouterInfo) (TransportSession, error) // return true if a routerInfo is compatable with this transport - Compatable(routerInfo common.RouterInfo) bool + Compatable(routerInfo router_info.RouterInfo) bool // close the transport cleanly // blocks until done diff --git a/lib/tunnel/delivery.go b/lib/tunnel/delivery.go index faefd24..0f57067 100644 --- a/lib/tunnel/delivery.go +++ b/lib/tunnel/delivery.go @@ -3,7 +3,8 @@ package tunnel import ( "encoding/binary" "errors" - "github.com/go-i2p/go-i2p/lib/common" + + common "github.com/go-i2p/go-i2p/lib/common/data" log "github.com/sirupsen/logrus" ) @@ -194,7 +195,7 @@ func (delivery_instructions DeliveryInstructions) FragmentNumber() (int, error) if di_type == FOLLOW_ON_FRAGMENT { return common.Integer( []byte{((delivery_instructions[0] & 0x7e) >> 1)}, - ), nil + ).Int(), nil } return 0, errors.New("Fragment Number only exists on FOLLOW_ON_FRAGMENT Delivery Instructions") } @@ -491,11 +492,11 @@ func (delivery_instructions DeliveryInstructions) ExtendedOptions() (data []byte return } else { extended_options_size := common.Integer([]byte{delivery_instructions[extended_options_index]}) - if len(delivery_instructions) < extended_options_index+1+extended_options_size { + if len(delivery_instructions) < extended_options_index+1+extended_options_size.Int() { err = errors.New("DeliveryInstructions are invalid, length is shorter than specified in Extended Options") return } else { - data = delivery_instructions[extended_options_index+1 : extended_options_size] + data = delivery_instructions[extended_options_index+1 : extended_options_size.Int()] return } @@ -738,7 +739,7 @@ func maybeAppendMessageID(di_flag DeliveryInstructions, di_type int, data, curre func maybeAppendExtendedOptions(di_flag DeliveryInstructions, data, current []byte) (now []byte, err error) { if index, err := DeliveryInstructions(data).extended_options_index(); err != nil { extended_options_length := common.Integer([]byte{data[index]}) - now = append(current, data[index:index+extended_options_length]...) + now = append(current, data[index:index+extended_options_length.Int()]...) } return } @@ -747,7 +748,7 @@ func maybeAppendSize(di_flag DeliveryInstructions, di_type int, data, current [] if di_type == FIRST_FRAGMENT { if index, err := DeliveryInstructions(data).extended_options_index(); err != nil { extended_options_length := common.Integer([]byte{data[index]}) - now = append(current, data[index+extended_options_length:index+extended_options_length+2]...) + now = append(current, data[index+extended_options_length.Int():index+extended_options_length.Int()+2]...) } } else if di_type == FOLLOW_ON_FRAGMENT { if len(data) < 7 { diff --git a/lib/tunnel/delivery_test.go b/lib/tunnel/delivery_test.go index e258a24..3d22534 100644 --- a/lib/tunnel/delivery_test.go +++ b/lib/tunnel/delivery_test.go @@ -1,9 +1,10 @@ package tunnel import ( - "github.com/go-i2p/go-i2p/lib/common" - "github.com/stretchr/testify/assert" "testing" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/stretchr/testify/assert" ) type DeliveryInstructionsFlags struct { @@ -49,7 +50,7 @@ func validFirstFragmentDeliveryInstructions(mapping common.Mapping) []byte { message_id := []byte{0x00, 0x00, 0x00, 0x02} data = append(data, message_id...) - data = append(data, mapping...) + data = append(data, mapping.Data()...) return data } @@ -60,7 +61,7 @@ func TestReadDeliveryInstructions(t *testing.T) { mapping, _ := common.GoMapToMapping(map[string]string{}) _, _, err := readDeliveryInstructions( validFirstFragmentDeliveryInstructions( - mapping, + *mapping, ), ) assert.Nil(err)