From b3fa7082cf179fe2228ae945efd4ed1c79ff805c Mon Sep 17 00:00:00 2001 From: Hayden Parker Date: Fri, 5 Feb 2016 02:23:11 -0800 Subject: [PATCH] testing and improvements to the common data structures --- lib/common/certificate.go | 105 +++++++++---------- lib/common/certificate_test.go | 160 +++++++++++++++++++++++++++++ lib/common/data.go | 4 - lib/common/destination.go | 51 +++------ lib/common/hash.go | 3 + lib/common/key_certificate.go | 46 +++++++++ lib/common/key_certificate_test.go | 1 + lib/common/keys_and_cert.go | 46 ++++++++- lib/common/keys_and_cert_test.go | 1 + lib/common/lease.go | 2 +- lib/common/lease_set.go | 2 +- lib/common/lease_set_test.go | 1 + lib/common/lease_test.go | 1 + lib/common/mapping.go | 70 +++++++------ lib/common/mapping_test.go | 36 ------- lib/common/router_address.go | 2 +- lib/common/router_address_test.go | 1 + lib/common/router_identity.go | 29 ++---- lib/common/router_info.go | 8 +- lib/common/router_info_test.go | 1 + lib/common/session_key.go | 3 + lib/common/session_tag.go | 3 + lib/common/string.go | 74 ++++++++++--- lib/common/string_test.go | 136 ++++++++++++++++++++---- 24 files changed, 551 insertions(+), 235 deletions(-) create mode 100644 lib/common/certificate_test.go delete mode 100644 lib/common/data.go create mode 100644 lib/common/hash.go create mode 100644 lib/common/key_certificate.go create mode 100644 lib/common/key_certificate_test.go create mode 100644 lib/common/keys_and_cert_test.go create mode 100644 lib/common/lease_set_test.go create mode 100644 lib/common/lease_test.go create mode 100644 lib/common/router_address_test.go create mode 100644 lib/common/router_info_test.go create mode 100644 lib/common/session_key.go create mode 100644 lib/common/session_tag.go diff --git a/lib/common/certificate.go b/lib/common/certificate.go index eeb6786..a43c66d 100644 --- a/lib/common/certificate.go +++ b/lib/common/certificate.go @@ -1,7 +1,7 @@ package common import ( - "github.com/bounce-chat/go-i2p/lib/crypto" + "errors" ) const ( @@ -28,73 +28,46 @@ const ( KEYCERT_CRYPTO_ELG = iota ) -// used to append data to existing data structures type Certificate []byte -// return the type of this certificate -func (c Certificate) Type() byte { - return c[0] +func (certificate Certificate) Type() byte { + return certificate[0] } -// get the length of the data in this certificate -// return -1 if the size of the certificate is invalid -func (c Certificate) Len() int { - if len(c) <= 2 { - // invalid size - return -1 +func (certificate Certificate) Length() (int, error) { + if len(certificate) < 3 { + // log + return 0, errors.New("error parsing certificate length: certificate is too short") } - return Integer(c[1:3]) -} - -// get the data for this certificate or null if none exists -func (c Certificate) Data() (d []byte) { - l := c.Len() - if l > 0 && len(c) <= 3+l { - d = c[3 : 3+l] + length := Integer(certificate[1:3]) + inferred_len := length + 3 + cert_len := len(certificate) + if inferred_len > cert_len { + // log + return length, errors.New("certificate parsing warning: certificate data is shorter than specified by length") + } else if cert_len > inferred_len { + //log + return length, errors.New("certificate parsing warning: certificate contains data beyond length") } - return + return length, nil } -// a Certificate of type KEY -type KeyCert []byte - -func (c KeyCert) Type() byte { - return Certificate(c).Type() -} - -func (c KeyCert) Data() []byte { - return Certificate(c).Data() -} - -// get the signing public key from this key cert -func (c KeyCert) SigningPublicKey() (k crypto.SigningPublicKey) { - data := c.Data() - ktype := Integer(data[:2]) - // set data to be the key data now - data = data[4:] - // determine the key type - if ktype == KEYCERT_SIGN_DSA_SHA1 { - var pk crypto.DSAPublicKey - copy(pk[:], data[:pk.Len()]) - k = pk - } else if ktype == KEYCERT_SIGN_P256 { - var pk crypto.ECP256PublicKey - copy(pk[:], data[:pk.Len()]) - k = pk - } else if ktype == KEYCERT_SIGN_P384 { - var pk crypto.ECP384PublicKey - copy(pk[:], data[:pk.Len()]) - k = pk - } else if ktype == KEYCERT_SIGN_P521 { - var pk crypto.ECP521PublicKey - copy(pk[:], data[:pk.Len()]) - k = pk +func (certificate Certificate) Data() ([]byte, error) { + length, err := certificate.Length() + if err != nil { + switch err.Error() { + case "error parsing certificate length: certificate is too short": + return make([]byte, 0), err + case "certificate parsing warning: certificate data is shorter than specified by length": + return certificate[3:], err + case "certificate parsing warning: certificate contains data beyond length": + return certificate[3 : length+3], err + } } - // TODO: rsa/eddsa - return + return certificate[3:], nil } -func (c Certificate) signatureSize() int { +func (certificate Certificate) SignatureSize() int { sizes := map[int]int{ KEYCERT_SIGN_DSA_SHA1: 40, KEYCERT_SIGN_P256: 64, @@ -105,5 +78,21 @@ func (c Certificate) signatureSize() int { KEYCERT_SIGN_RSA4096: 512, KEYCERT_SIGN_ED25519: 64, } - return sizes[int(c.Type())] + return sizes[int(certificate.Type())] +} + +func ReadCertificate(data []byte) (Certificate, []byte, error) { + certificate := Certificate(data) + length, err := certificate.Length() + if err != nil { + switch err.Error() { + case "error parsing certificate length: certificate is too short": + return Certificate{}, make([]byte, 0), err + case "certificate parsing warning: certificate data is shorter than specified by length": + return certificate, make([]byte, 0), err + case "certificate parsing warning: certificate contains data beyond length": + return Certificate(certificate[:length+3]), certificate[length+3:], nil + } + } + return certificate, make([]byte, 0), nil } diff --git a/lib/common/certificate_test.go b/lib/common/certificate_test.go new file mode 100644 index 0000000..be52dc3 --- /dev/null +++ b/lib/common/certificate_test.go @@ -0,0 +1,160 @@ +package common + +import "testing" + +func TestCertificateTypeIsFirstByte(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x00} + certificate := Certificate(bytes) + if certificate.Type() != 0x03 { + t.Fatal("certificate.Type() is not first byte") + } +} + +func TestCertificateLengthCorrect(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff} + certificate := Certificate(bytes) + cert_len, err := certificate.Length() + if cert_len != 2 { + t.Fatal("certificate.Length() is not correct:", cert_len) + } + if err != nil { + t.Fatal("certificate.Length() returned err", err) + } +} + +func TestCertificateLengthErrWhenTooShort(t *testing.T) { + bytes := []byte{0x03, 0x00} + certificate := Certificate(bytes) + cert_len, err := certificate.Length() + if cert_len != 0 { + t.Fatal("certificate.Length() is not correct:", cert_len) + } + if err == nil || err.Error() != "error parsing certificate length: certificate is too short" { + t.Fatal("certificate.Length() did not return correct err:", err) + } +} + +func TestCertificateLengthErrWhenDataTooShort(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x02, 0xff} + certificate := Certificate(bytes) + cert_len, err := certificate.Length() + if cert_len != 2 { + t.Fatal("certificate.Length() is not correct:", cert_len) + } + if err == nil || err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" { + t.Fatal("certificate.Length() did not return correct err:", err) + } +} + +func TestCertificateDataWhenCorrectSize(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff} + certificate := Certificate(bytes) + cert_data, err := certificate.Data() + if err != nil { + t.Fatal("certificate.Data() returned error", err) + } + cert_len := len(cert_data) + if cert_len != 2 { + t.Fatal("certificate.Data() did not return correct length:", cert_len) + } + if cert_data[0] != 0xff || cert_data[1] != 0xff { + t.Fatal("certificate.Data() returned incorrect data") + } +} + +func TestCertificateDataWhenTooLong(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa} + certificate := Certificate(bytes) + cert_data, err := certificate.Data() + if err == nil || err.Error() != "certificate parsing warning: certificate contains data beyond length" { + t.Fatal("certificate.Data() returned wrong error:", err) + } + cert_len := len(cert_data) + if cert_len != 2 { + t.Fatal("certificate.Data() did not return correct length:", cert_len) + } + if cert_data[0] != 0xff || cert_data[1] != 0xff { + t.Fatal("certificate.Data() returned incorrect data") + } +} + +func TestCertificateDataWhenTooShort(t *testing.T) { + bytes := []byte{0x03, 0x00, 0x02, 0xff} + certificate := Certificate(bytes) + cert_data, err := certificate.Data() + if err == nil || err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" { + t.Fatal("certificate.Data() did not return correct error:", err) + } + cert_len := len(cert_data) + if cert_len != 1 { + t.Fatal("certificate.Data() did not return correct length when too short:", cert_len) + } + if cert_data[0] != 0xff { + t.Fatal("certificate.Data() returned incorrect data") + } + +} + +func TestReadCertificateWithCorrectData(t *testing.T) { + bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff} + cert, remainder, err := ReadCertificate(bytes) + cert_len := len(cert) + if cert_len != 5 { + t.Fatal("ReadCertificate() did not return correct certificate length:", cert_len) + } + if len(remainder) != 0 { + t.Fatal("ReadCertificate() returned a remainder incorrectly:", len(remainder)) + } + if err != nil { + t.Fatal("ReadCertificate returned error:", err) + } +} + +func TestReadCertificateWithDataTooShort(t *testing.T) { + bytes := []byte{0x00, 0x00, 0x02, 0xff} + cert, remainder, err := ReadCertificate(bytes) + cert_len := len(cert) + if cert_len != 4 { + t.Fatal("ReadCertificate() did not return correct certificate length:", cert_len) + } + if len(remainder) != 0 { + t.Fatal("ReadCertificate() returned a remainder incorrectly when data too short:", len(remainder)) + } + if err == nil || err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" { + t.Fatal("ReadCertificate returned incorrect error:", err) + } +} + +func TestReadCertificateWithRemainder(t *testing.T) { + bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x00} + cert, remainder, err := ReadCertificate(bytes) + cert_len := len(cert) + if cert_len != 5 { + t.Fatal("ReadCertificate() did not return correct certificate length:", cert_len) + } + if len(remainder) != 1 { + t.Fatal("ReadCertificate() returned a remainder incorrectly:", len(remainder)) + } + if remainder[0] != 0x00 { + t.Fatal("ReadCertificate() did not return correct remainder value") + } + if err != nil { + t.Fatal("ReadCertificate returned error:", err) + } +} + +func TestReadCertificateWithInvalidLength(t *testing.T) { + bytes := []byte{0x00, 0x00} + cert, remainder, err := ReadCertificate(bytes) + cert_len := len(cert) + if cert_len != 0 { + t.Fatal("ReadCertificate() did not return 0 length certificate with invalid length:", cert_len) + } + remainder_len := len(remainder) + if remainder_len != 0 { + t.Fatal("ReadCertificate() did not return 0 length remainder with invalid length:", remainder_len) + } + if err == nil || err.Error() != "error parsing certificate length: certificate is too short" { + t.Fatal("ReadCertificate() returned an incorrect error with invalid length:", err) + } +} diff --git a/lib/common/data.go b/lib/common/data.go deleted file mode 100644 index 9d54432..0000000 --- a/lib/common/data.go +++ /dev/null @@ -1,4 +0,0 @@ -package common - -// the sha256 of some datastructure -type IdentHash [32]byte diff --git a/lib/common/destination.go b/lib/common/destination.go index 4b1b795..a967f62 100644 --- a/lib/common/destination.go +++ b/lib/common/destination.go @@ -7,53 +7,26 @@ import ( "strings" ) -// a network endpoint inside i2p -// effectively a public key blob type Destination []byte -// obtain public elgamal key -func (dest Destination) PublicKey() (k crypto.PublicEncryptionKey) { - cert := dest.Certificate() - if cert.Type() == CERT_KEY { - // TODO(psi): check for key cert and included encryption key - } else { - var ek crypto.ElgPublicKey - copy(ek[:], dest[:256]) - k = ek - } - return +func (destination Destination) PublicKey() (key crypto.ElgPublicKey) { + return KeysAndCert(destination).PublicKey() } -// obtain destination certificate -func (dest Destination) Certificate() Certificate { - return Certificate(dest[128+256:]) +func (destination Destination) SigningPublicKey() (key crypto.SigningPublicKey) { + return KeysAndCert(destination).SigningPublicKey() } -// gets this destination's signing key -// if there is a keycert in this destination the signing key in there is used -func (dest Destination) SigningPublicKey() (k crypto.SigningPublicKey) { - cert := dest.Certificate() - if cert.Type() == CERT_KEY { - // we have a key certificate - // extract the signing key from the key cert - k = KeyCert(cert).SigningPublicKey() - } else { - var pk crypto.DSAPublicKey - copy(pk[:], dest[256:256+128]) - k = pk - } - return +func (destination Destination) Certificate() (cert Certificate) { + return KeysAndCert(destination).Certificate() } -// return the .b32.i2p address -func (dest Destination) Base32Address() (str string) { - h := crypto.SHA256(dest) - str = strings.Trim(base32.EncodeToString(h[:]), "=") - str += ".b32.i2p" - return +func (destination Destination) Base32Address() string { + hash := crypto.SHA256(destination) + str := strings.Trim(base32.EncodeToString(hash[:]), "=") + return str + ".b32.i2p" } -func (dest Destination) Base64() (str string) { - str = base64.EncodeToString(dest) - return +func (destination Destination) Base64() string { + return base64.EncodeToString(destination) } diff --git a/lib/common/hash.go b/lib/common/hash.go new file mode 100644 index 0000000..14fcb9f --- /dev/null +++ b/lib/common/hash.go @@ -0,0 +1,3 @@ +package common + +type Hash [32]byte diff --git a/lib/common/key_certificate.go b/lib/common/key_certificate.go new file mode 100644 index 0000000..2ad23ff --- /dev/null +++ b/lib/common/key_certificate.go @@ -0,0 +1,46 @@ +package common + +import ( + "github.com/bounce-chat/go-i2p/lib/crypto" +) + +type KeyCertificate []byte + +func (key_certificate KeyCertificate) Type() byte { + return Certificate(key_certificate).Type() +} + +func (key_certificate KeyCertificate) Data() ([]byte, error) { + return Certificate(key_certificate).Data() +} + +// get the signing public key from this key cert +func (key_certificate KeyCertificate) SigningPublicKey() (k crypto.SigningPublicKey) { + data, err := key_certificate.Data() + if err != nil { + return + } + ktype := Integer(data[:2]) + // set data to be the key data now + data = data[4:] + // determine the key type + if ktype == KEYCERT_SIGN_DSA_SHA1 { + var pk crypto.DSAPublicKey + copy(pk[:], data[:pk.Len()]) + k = pk + } else if ktype == KEYCERT_SIGN_P256 { + var pk crypto.ECP256PublicKey + copy(pk[:], data[:pk.Len()]) + k = pk + } else if ktype == KEYCERT_SIGN_P384 { + var pk crypto.ECP384PublicKey + copy(pk[:], data[:pk.Len()]) + k = pk + } else if ktype == KEYCERT_SIGN_P521 { + var pk crypto.ECP521PublicKey + copy(pk[:], data[:pk.Len()]) + k = pk + } + // TODO: rsa/eddsa + return +} diff --git a/lib/common/key_certificate_test.go b/lib/common/key_certificate_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/key_certificate_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/keys_and_cert.go b/lib/common/keys_and_cert.go index f8370ee..5d54ca9 100644 --- a/lib/common/keys_and_cert.go +++ b/lib/common/keys_and_cert.go @@ -1,7 +1,43 @@ package common -// -// Both Destination and RouterIdentity share the same structure -// https://geti2p.net/en/docs/spec/common-structures#struct_KeysAndCert -// Perhaps we can avoid repeating ourselves being using commong functions -// +import ( + "github.com/bounce-chat/go-i2p/lib/crypto" +) + +type KeysAndCert []byte + +func (keys_and_cert KeysAndCert) PublicKey() (key crypto.ElgPublicKey) { + if len(keys_and_cert) < 387 { + + } + copy(keys_and_cert[:256], key[:]) + return +} + +func (keys_and_cert KeysAndCert) SigningPublicKey() (key crypto.SigningPublicKey) { + cert := keys_and_cert.Certificate() + if cert.Type() == CERT_KEY { + key = KeyCertificate(cert).SigningPublicKey() + } else { + var pk crypto.DSAPublicKey + copy(pk[:], keys_and_cert[256:256+128]) + key = pk + } + return +} + +func (keys_and_cert KeysAndCert) Certificate() (cert Certificate) { + copy(keys_and_cert[256+128:], cert) + return +} + +func ReadKeysAndCert(data []byte) (KeysAndCert, []byte, error) { + var keys_and_cert KeysAndCert + copy(data[:387], keys_and_cert) + n, err := keys_and_cert.Certificate().Length() + if err != nil { + return keys_and_cert, data, err + } + keys_and_cert = append(keys_and_cert, data[387:n]...) + return keys_and_cert, data[387+n:], nil +} diff --git a/lib/common/keys_and_cert_test.go b/lib/common/keys_and_cert_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/keys_and_cert_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/lease.go b/lib/common/lease.go index d24963e..45c8d77 100644 --- a/lib/common/lease.go +++ b/lib/common/lease.go @@ -10,7 +10,7 @@ const ( type Lease [LEASE_SIZE]byte -func (lease Lease) TunnelGateway() (h IdentHash) { +func (lease Lease) TunnelGateway() (h Hash) { copy(lease[:32], h[:]) return } diff --git a/lib/common/lease_set.go b/lib/common/lease_set.go index 03ad857..bf89d37 100644 --- a/lib/common/lease_set.go +++ b/lib/common/lease_set.go @@ -49,7 +49,7 @@ func (lease_set LeaseSet) Signature() []byte { sig_size := lease_set. Destination(). Certificate(). - signatureSize() + SignatureSize() return lease_set[data_end : data_end+sig_size] } diff --git a/lib/common/lease_set_test.go b/lib/common/lease_set_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/lease_set_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/lease_test.go b/lib/common/lease_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/lease_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/mapping.go b/lib/common/mapping.go index 21e2038..fba5bb3 100644 --- a/lib/common/mapping.go +++ b/lib/common/mapping.go @@ -1,41 +1,49 @@ package common import ( - "encoding/binary" - "strings" +// "encoding/binary" +// "strings" ) type Mapping []byte -func (mapping Mapping) ToMap() map[string]string { - gomap := make(map[string]string) - kv_store := string(mapping[2:]) - pairs := strings.Split(kv_store, ";") - for _, pair := range pairs { - values := strings.Split(pair, "=") - if len(values) != 2 { - continue - } - gomap[values[0]] = values[1] - } - return gomap +func (mapping Mapping) Values() [][2][]byte { + return make([][2][]byte, 0) } -func MappingFromMap(gomap map[string]string) Mapping { - kv_store := make([]byte, 0) - for k, v := range gomap { - key_bytes := []byte(k) - key_bytes = append(key_bytes, 0x3d) - value_bytes := []byte(v) - value_bytes = append(value_bytes, 0x3b) - kv_store = append(kv_store, key_bytes...) - kv_store = append(kv_store, value_bytes...) - } - kv_size := uint16(len(kv_store)) - var size [2]byte - binary.BigEndian.PutUint16(size[:], kv_size) - mapping := Mapping{} - mapping = append(mapping, size[:]...) - mapping = append(mapping, kv_store...) - return mapping +func ValuesToMapping(values [][2][]byte) Mapping { + return Mapping{} } + +//func (mapping Mapping) ToMap() map[string]string { +// gomap := make(map[string]string) +// kv_store := string(mapping[2:]) +// pairs := strings.Split(kv_store, ";") +// for _, pair := range pairs { +// values := strings.Split(pair, "=") +// if len(values) != 2 { +// continue +// } +// gomap[values[0]] = values[1] +// } +// return gomap +//} + +//func MappingFromMap(gomap map[string]string) Mapping { +// kv_store := make([]byte, 0) +// for k, v := range gomap { +// key_bytes := []byte(k) +// key_bytes = append(key_bytes, 0x3d) +// value_bytes := []byte(v) +// value_bytes = append(value_bytes, 0x3b) +// kv_store = append(kv_store, key_bytes...) +// kv_store = append(kv_store, value_bytes...) +// } +// kv_size := uint16(len(kv_store)) +// var size [2]byte +// binary.BigEndian.PutUint16(size[:], kv_size) +// mapping := Mapping{} +// mapping = append(mapping, size[:]...) +// mapping = append(mapping, kv_store...) +// return mapping +//} diff --git a/lib/common/mapping_test.go b/lib/common/mapping_test.go index b5653ec..805d0c7 100644 --- a/lib/common/mapping_test.go +++ b/lib/common/mapping_test.go @@ -1,37 +1 @@ package common - -import ( - "bytes" - "testing" -) - -func TestToMapping(t *testing.T) { - gomap := map[string]string{ - "a": "1", - } - mapping := MappingFromMap(gomap) - if !bytes.Equal(mapping, []byte{0x00, 0x04, 0x61, 0x3d, 0x31, 0x3b}) { - t.Fatal("go map to mapping did not create correct mapping") - } -} - -func TestMappingToMap(t *testing.T) { - mapping := Mapping{0x00, 0x08, 0x61, 0x3d, 0x31, 0x3b, 0x62, 0x3d, 0x32, 0x3b} - gomap := mapping.ToMap() - if gomap["a"] != "1" { - t.Fatal("map does not contain encoded data") - } - if gomap["b"] != "2" { - t.Fatal("map does not comtain encoded data") - } -} - -func TestToAndFromMapping(t *testing.T) { - gomap := make(map[string]string) - gomap["foo"] = "bar" - mapping := MappingFromMap(gomap) - gomap2 := mapping.ToMap() - if gomap["foo"] != gomap2["foo"] { - t.Fatal("rebuilt map has different data") - } -} diff --git a/lib/common/router_address.go b/lib/common/router_address.go index 00fa624..bfcef39 100644 --- a/lib/common/router_address.go +++ b/lib/common/router_address.go @@ -12,7 +12,7 @@ func (router_address RouterAddress) Expiration() (d Date) { } func (router_address RouterAddress) TransportStyle() string { - return string( + return string( //String router_address[10:router_address.stringLength()], ) } diff --git a/lib/common/router_address_test.go b/lib/common/router_address_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/router_address_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/router_identity.go b/lib/common/router_identity.go index 6f75145..951c459 100644 --- a/lib/common/router_identity.go +++ b/lib/common/router_identity.go @@ -1,41 +1,24 @@ package common import ( - "errors" "github.com/bounce-chat/go-i2p/lib/crypto" ) type RouterIdentity []byte func (router_identity RouterIdentity) PublicKey() (key crypto.ElgPublicKey) { - copy(router_identity[:256], key[:]) - return + return KeysAndCert(router_identity).PublicKey() } func (router_identity RouterIdentity) SigningPublicKey() (key crypto.SigningPublicKey) { - cert := router_identity.Certificate() - if cert.Type() == CERT_KEY { - key = KeyCert(cert).SigningPublicKey() - } else { - var pk crypto.DSAPublicKey - copy(pk[:], router_identity[256:256+128]) - key = pk - } - return + return KeysAndCert(router_identity).SigningPublicKey() } func (router_identity RouterIdentity) Certificate() (cert Certificate) { - copy(router_identity[256+128:], cert) - return + return KeysAndCert(router_identity).Certificate() } -func readRouterIdentity(data []byte) (RouterIdentity, []byte, error) { - var router_identity RouterIdentity - copy(data[:387], router_identity) - n := router_identity.Certificate().Len() - if n == -1 { - return router_identity, data, errors.New("invalid certificate") - } - router_identity = append(router_identity, data[387:n]...) - return router_identity, data[387+n:], nil +func ReadRouterIdentity(data []byte) (RouterIdentity, []byte, error) { + keys_and_certs, remainder, err := ReadKeysAndCert(data) + return RouterIdentity(keys_and_certs), remainder, err } diff --git a/lib/common/router_info.go b/lib/common/router_info.go index 25bf209..ef1c0f1 100644 --- a/lib/common/router_info.go +++ b/lib/common/router_info.go @@ -3,18 +3,18 @@ package common type RouterInfo []byte func (router_info RouterInfo) RouterIdentity() RouterIdentity { - router_identity, _, _ := readRouterIdentity(router_info) + router_identity, _, _ := ReadRouterIdentity(router_info) return router_identity } func (router_info RouterInfo) Published() (d Date) { - _, remainder, _ := readRouterIdentity(router_info) + _, remainder, _ := ReadRouterIdentity(router_info) copy(remainder[:8], d[:]) return } func (router_info RouterInfo) RouterAddressCount() int { - _, remainder, _ := readRouterIdentity(router_info) + _, remainder, _ := ReadRouterIdentity(router_info) return Integer([]byte{remainder[8]}) } @@ -47,7 +47,7 @@ func (router_info RouterInfo) Signature() []byte { sig_size := router_info. RouterIdentity(). Certificate(). - signatureSize() + SignatureSize() return router_info[offset:sig_size] } diff --git a/lib/common/router_info_test.go b/lib/common/router_info_test.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/lib/common/router_info_test.go @@ -0,0 +1 @@ +package common diff --git a/lib/common/session_key.go b/lib/common/session_key.go new file mode 100644 index 0000000..9456634 --- /dev/null +++ b/lib/common/session_key.go @@ -0,0 +1,3 @@ +package common + +type SessionKey [32]byte diff --git a/lib/common/session_tag.go b/lib/common/session_tag.go new file mode 100644 index 0000000..92a947f --- /dev/null +++ b/lib/common/session_tag.go @@ -0,0 +1,3 @@ +package common + +type SessionTag [32]byte diff --git a/lib/common/string.go b/lib/common/string.go index 99aef11..e863019 100644 --- a/lib/common/string.go +++ b/lib/common/string.go @@ -6,20 +6,66 @@ import ( type String []byte -func ReadString(data []byte) (str String, remainder []byte, err error) { - if len(data) == 0 { - err = errors.New("no string in empty byte slice") - return +func (str String) Length() (int, error) { + if len(str) == 0 { + // log + return 0, errors.New("error parsing string: zero length") } - length := Integer([]byte{data[0]}) - data = data[1:] - - if len(data) < length { - str = data - err = errors.New("string longer than provided slice") - return + length := Integer([]byte{str[0]}) + inferred_len := length + 1 + str_len := len(str) + if inferred_len > str_len { + // log + return length, errors.New("string parsing warning: string data is shorter than specified by length") + } else if str_len > inferred_len { + //log + return length, errors.New("string parsing warning: string contains data beyond length") } - str = data[:length] - remainder = data[length:] - return + return length, nil +} + +func (str String) Data() ([]byte, error) { + length, err := str.Length() + if err != nil { + switch err.Error() { + case "error parsing string: zero length": + return make([]byte, 0), err + case "string parsing warning: string data is shorter than specified by length": + return str[1:], err + case "string parsing warning: string contains data beyond length": + return str[1 : length+1], err + } + } + return str[1:], nil +} + +func (str String) GoString() (string, error) { + content, err := str.Data() + return string(content), err +} + +func ToI2PString(data []byte) (String, error) { + data_len := len(data) + if data_len >= 256 { + return make([]byte, 0), errors.New("cannot store that much data in I2P string") + } + i2p_string := []byte{byte(data_len)} + i2p_string = append(i2p_string, data...) + return String(i2p_string), nil +} + +func ReadString(data []byte) (String, []byte, error) { + str := String(data) + length, err := String(data).Length() + if err != nil { + switch err.Error() { + case "error parsing string: zero length": + return String{}, make([]byte, 0), err + case "string parsing warning: string data is shorter than specified by length": + return str, make([]byte, 0), err + case "string parsing warning: string contains data beyond length": + return String(str[:length+1]), str[length+1:], err + } + } + return str, make([]byte, 0), nil } diff --git a/lib/common/string_test.go b/lib/common/string_test.go index a5b44d4..9452ebf 100644 --- a/lib/common/string_test.go +++ b/lib/common/string_test.go @@ -2,47 +2,147 @@ package common import "testing" +func TestStringReportsCorrectLength(t *testing.T) { + str_len, err := String([]byte{0x02, 0x00, 0x00}).Length() + if str_len != 2 { + t.Fatal("string.Length() did not report correct length") + } + if err != nil { + t.Fatal("string.Length() reported an error on valid string:", err) + } +} + +func TestStringReportsLengthZeroError(t *testing.T) { + str_len, err := String(make([]byte, 0)).Length() + if str_len != 0 { + t.Fatal("string.Length() reported non-zero length on empty slice") + } + if err == nil || err.Error() != "error parsing string: zero length" { + t.Fatal("string.Length() reported incorrect error on zero length slice:", err) + } +} + +func TestStringReportsExtraDataError(t *testing.T) { + str_len, err := String([]byte{0x01, 0x00, 0x00}).Length() + if str_len != 1 { + t.Fatal("string.Length() reported wrong size when extra data present") + } + if err == nil || err.Error() != "string parsing warning: string contains data beyond length" { + t.Fatal("string.Length() reported incorrect error on extra data:", err) + } +} + +func TestStringDataReportsLengthZeroError(t *testing.T) { + str_len, err := String([]byte{0x01}).Length() + if str_len != 1 { + t.Fatal("string.Length() reported wring length with missing data", str_len) + } + if err == nil || err.Error() != "string parsing warning: string data is shorter than specified by length" { + t.Fatal("string.Length() reported wrong error when data was missing", err) + } +} + +func TestStringDataReportsExtraDataError(t *testing.T) { + data, err := String([]byte{0x01, 0x00, 0x01}).Data() + data_len := len(data) + if data_len != 1 { + t.Fatal("string.Data() returned wrong size data for length with extra data:", data_len) + } + if err == nil || err.Error() != "string parsing warning: string contains data beyond length" { + t.Fatal("string.Length() reported wrong error with extra data", err) + } +} + +func TestStringDataEmptyWhenZeroLength(t *testing.T) { + data, err := String(make([]byte, 0)).Data() + data_len := len(data) + if data_len != 0 { + t.Fatal("string.Data() returned data when none was present:", data_len) + } + if err == nil || err.Error() != "error parsing string: zero length" { + t.Fatal("string.Length() reported wrong error with no data", err) + } +} + +func TestStringDataErrorWhenNonZeroLengthOnly(t *testing.T) { + data, err := String([]byte{0x01}).Data() + data_len := len(data) + if data_len != 0 { + t.Fatal("string.Data() returned data when only length was present:", data_len) + } + if err == nil || err.Error() != "string parsing warning: string data is shorter than specified by length" { + t.Fatal("string.Length() reported wrong error with length but no data", err) + } +} + +func TestToI2PStringFormatsCorrectly(t *testing.T) { + i2p_string, err := ToI2PString([]byte{0x22, 0x33}) + if err != nil { + t.Fatal("ToI2PString() returned error on valid data:", err) + } + if i2p_string[0] != 0x02 { + t.Fatal("ToI2PString() did not prepend the correct length") + } + if i2p_string[1] != 0x22 && i2p_string[2] != 0x33 { + t.Fatal("ToI2PString() did not preserve string") + } +} + +func TestToI2PStringReportsOverflows(t *testing.T) { + i2p_string, err := ToI2PString(make([]byte, 256)) + if len(i2p_string) != 0 { + t.Fatal("ToI2PString() returned data when overflowed") + } + if err == nil || err.Error() != "cannot store that much data in I2P string" { + t.Fatal("ToI2pString() did not report overflow") + } + _, err = ToI2PString(make([]byte, 255)) + if err != nil { + t.Fatal("ToI2PString() reported error with acceptable size:", err) + } +} + func TestReadStringReadsLength(t *testing.T) { bytes := []byte{0x01, 0x04, 0x06} str, remainder, err := ReadString(bytes) - if err != nil { - t.Fatal("ReadString() returner error,", err) + if err == nil || err.Error() != "string parsing warning: string contains data beyond length" { + t.Fatal("ReadString(t *testing.T) returned incorrect error,", err) } - if len(str) != 1 { - t.Fatal("ReadString() did not return correct string length:", len(str)) + if len(str) != 2 { + t.Fatal("ReadString(t *testing.T) did not return correct string length:", len(str)) } - if str[0] != 0x04 { - t.Fatal("ReadString() did not return correct string") + if str[0] != 0x01 && str[1] != 0x04 { + t.Fatal("ReadString(t *testing.T) did not return correct string") } if len(remainder) != 1 { - t.Fatal("ReadString() did not return correct remainder length") + t.Fatal("ReadString(t *testing.T) did not return correct remainder length") } if remainder[0] != 0x06 { - t.Fatal("ReadString() did not return correct remainder") + t.Fatal("ReadString(t *testing.T) did not return correct remainder") } } func TestReadStringErrWhenEmptySlice(t *testing.T) { bytes := make([]byte, 0) _, _, err := ReadString(bytes) - if err != nil && err.Error() != "no string in empty byte slice" { - t.Fatal("ReadString() did not report empty slice error", err) + if err != nil && err.Error() != "error parsing string: zero length" { + t.Fatal("ReadString(t *testing.T) did not report empty slice error", err) } } -func TestReadStringErrWhenStringTooLong(t *testing.T) { +func TestReadStringErrWhenDataTooShort(t *testing.T) { bytes := []byte{0x03, 0x01} str, remainder, err := ReadString(bytes) - if err != nil && err.Error() != "string longer than provided slice" { - t.Fatal("ReadString() did not report string too long", err) + if err != nil && err.Error() != "string parsing warning: string data is shorter than specified by length" { + t.Fatal("ReadString(t *testing.T) did not report string too long", err) } - if len(str) != 1 { - t.Fatal("ReadString() did not return the slice as string when too long") + if len(str) != 2 { + t.Fatal("ReadString(t *testing.T) did not return the slice as string when too long") } - if str[0] != 0x01 { - t.Fatal("ReadString() did not return the correct partial string") + if str[0] != 0x03 && str[1] != 0x01 { + t.Fatal("ReadString(t *testing.T) did not return the correct partial string") } if len(remainder) != 0 { - t.Fatal("ReadString() returned a remainder when the string was too long") + t.Fatal("ReadString(t *testing.T) returned a remainder when the string data was too short") } }