4 Commits

Author SHA1 Message Date
idk
f113977419 refactor certs, keycerts 2022-04-27 10:50:10 -04:00
idk
86dc323348 refactor certs, keycerts 2022-04-27 10:48:59 -04:00
idk
a80c9973f9 Merge pull request #1 from eyedeekay/circleci-project-setup
Add .circleci/config.yml
2021-05-11 10:46:52 -07:00
idk
7adf3d7577 Add .circleci/config.yml 2021-05-11 07:34:35 -07:00
23 changed files with 380 additions and 440 deletions

26
.circleci/config.yml Normal file
View File

@@ -0,0 +1,26 @@
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/golang:1.15.8
steps:
- checkout
- restore_cache:
keys:
- go-mod-v4-{{ checksum "go.sum" }}
- run:
name: Install Dependencies
command: go mod download
- save_cache:
key: go-mod-v4-{{ checksum "go.sum" }}
paths:
- "/go/pkg/mod"
- run:
name: Run tests
command: |
mkdir -p /tmp/test-reports
gotestsum --junitfile /tmp/test-reports/unit-tests.xml
- store_test_results:
path: /tmp/test-reports

View File

@@ -19,7 +19,7 @@ $(EXE):
$(GO) build -v -o $(EXE)
test:
$(GO) test ./...
$(GO) test -v -failfast ./lib/common
clean:
$(GO) clean -v

View File

@@ -1,189 +0,0 @@
Proposal for developing go-i2p
==============================
Goals:
------
Make it easy to seamlessly integrate Go applications with I2P routers where
a pre-installed I2P router with SAM is not already present.
Implement an I2P library with a memory-safe language capable of outputting
shared objects and C libraries for use by other languages, in order to make
embedding I2P in other projects easier.
### Why Go?
Go is a popular programming language developed at Google and now
implemented by several projects. It is a memory-safe language which compiles
binary executables for a target platform, as opposed to running on a virtual
machine or interpreter. Go features a suite of cross-compilers with identical
usage, making it a "Write-once, compile-anywhere" language. This is especially
true when writing pure Go. Go compilers normally produce executables which are
maximally "static" and only link dynamic libraries provided by the platform
when instructed to specifically, however this behavior can be disabled. Go
libraries can produce shared objects for other applications to use, and third
party Go applications can seamlessly generate C bindings as a bridge to other
languages. I can do this automatically with Java by generating JNI bindings,
enabling go-i2p to interface with Java I2P readily.
### Why go-i2p?
go-i2p was a project to implement an I2P router and library of I2P structures
using Go which gained interest for a time 7-8 years ago, but which has since
gone dormant. In spite of that considerable lapse in time, the structure is
a sound, understandable way of laying out a Go project and the extant code is
usable as the basis for beginning the development of a Go based I2P router.
It will considerably reduce the amount of work required to create a Go I2P
router.
### Why Go Applications?
Go applications manage network connections and listeners in a way which
enables easily configuring alternate transports and building different types
of "addresses" which are useful for contacting people on those transports.
The advantages of this approach will likely affect all parts of the go-i2p
router and the applications that come with it. At this time the power of this
approach is primarily visible in the power of Go's SAM libraries, which
implement all of Go's interface types for network connections and addresses
and can be "swapped" with any Go library which uses those interface types.
In a matter of an hour or two, sometimes even less a developer who wishes
to make their application able to build I2P connections can do so.
Moreover, these connections can often be used to transport other connections
inside. It is therefore possible to use Go as an alternate way of doing
"Native WebRTC" using I2P connections and add WebTorrent support to a
desktop I2P BitTorrent Application. The best way to do this would be to
add support to the `anacrolix/torrent` library which already supports regular
WebTorrent.
Another key application is IPFS. IPFS is designed to use transports in a
way which allows them to be readily substituted out, nested and combined.
Interest in I2P transports has been expressed to me before, and I've enabled
them using SAM in the past. Interestingly, however, IPFS has it's own pluggable
peer-discovery methods as well, inclusing the "Hashmatter" anonymous DHT and
in fact an IPFS network could hypothetically use a NetDB-like structure for
anonymous peer discovery and also have I2P transports(related or unrelated).
Deeper into the router, this approach yields possibilities for experimenting
with other types of transports, in particular transports which imitate other
traffic. Tor's pluggable transports are largely written in Go, for instance,
but perhaps more interestingly Go has a library for building custom SSH clients
and servers(`gliderlabs/ssh`) which could be used to build ssh-alike transports
that wouldn't be easily distinguishable from the real thing. Besides that,
there is `pion/webrtc` and the accompanying libraries, which implement a
memory-safe desktop WebRTC implementation that is used in Snowflake to mimic
browser-to-browser connections WebRTC as a Tor pluggable transport. There are
popular Go libraries which are used for everything from TLS to KCP, and each
potential transport would need to be evaluated for utility, security, etc,
however implementing such an "imitating" transport should eventually be
something we are able to rapidly prototype by implementing our own `transport`
interface and wrapping existing connection types.
#### Specific Applications
Besides having the most extensive SAM and I2CP libraries available in a Non-Java
language, go has several applications which could improve I2P's ecosystem.
##### Extant, applications that have users
- [XD](https://github.com/majestrate/XD) - Simple bittorrent client with a WebUI
and a custom RPC interface
- [libanonvpn](https://github.com/RTradeLtd/libanonvpn) - Easy, self-healing TUN
Devices over I2P on Linux, OSX, TAP devices over I2P on Windows
- [BRB](https://github.com/eyedeekay/brb) - I2P IRC client with the ability to
support multiple simultaneous anonymous users, a built-in IRC server, and a
WebIRC interface for easy ephemeral groupchat.
- [Railroad](https://github.com/eyedeekay/railroad) - Easy selfhosted blogging
tool which supports live, WYSIWYG editing using a side-by-side Mardown Editor
and Preview Panel.
- [sam-forwarder](https://github.com/eyedeekay/sam-forwarder) - Versatile tunnel
building and management tool like i2ptunnel with similar support. Slightly easier
HTTPS support.
- [eephttpd](https://github.com/eyedeekay/eephttpd) - Simple static http server
with the ability to clone a git repository and automatically generate a site,
and to in-turn be cloned by another git client. Also has a built-in bittorrent
tracker and generates/shares a .torrent of everything in the docroot, with itself
as a web seed.
- [reseed-tools](https://i2pgit.org/idk/reseed-tools) Reseed server and library for
handling `.su3` files in Go.
- [syndie](https://github.com/kpetku/syndie-core) Maintained implementation of the
Syndie message board system in Go.
... Many, many others but these are the most useful.
##### Partial/In Development
- [Brook](https;//github.com/txthinking/brook) - Selfhosting multi-transport VPN and
transparent proxy with Android support.
- [bt](https://github.com/xgfone/bt) - a very simple, readable, and safe pure-Go
bittorent library with a similar set of features to I2PSnark. Although `anacrolix/torrent`
supports more features, `xgfone/bt` is slightly easier to work with when cross-compiling.
- [gophertunnel/gopherhole](https://i2pgit.org/idk/gophertunnel) - Are a simple Gopher
client and server in pure Go which automatically configure themselves with I2P. Also
has the ability to proxy Gopher content into the I2P Web.
- [darkssh/darksshd](https://github.com/eyedeekay/darkssh) - SSH client and server
with transparent support for I2P and Tor addresses, making MITM attacks based on
social-engineering SSH clients into connecting to malicious servers impossible.
- [samsocks](https://github.com/eyedeekay/samsocks) - Transparent socksifier with UDP
support, built on SAM.
- [i2pbrowser](https://github.com/eyedeekay/i2pbrowser) - Not pure go, this is
actually an installer and bundling tool intended to pre-configure a browser
for use with I2P and a suite of I2P applications. In a far-fling future where
go-i2p is completed, this i2pbrowser would embed go-i2p instead of i2p-zero,
while retaining it's other "router-agnostic" attitudes.
##### Proposed
- [Smallstep] - Smallstep is a Certificate Authority by Let's Encrypt which is often
used for private CA's for SSH servers. It has ACME protocol support. It could be used
in I2P as a CA for I2P sites
- [torrent](https://github.com/anacrolix/torrent) - Anacrolix torrent is a very popular
Bittorrent library used in 20-30 bittorrent clients, and which has features which are
comparable to BiglyBT.
- [Gitea](https://github.com/gitea/gitea) - Gitea is a Git web server similar to Gitea
but in most ways simpler to self-host.
- [Syncthing](https://github.com/syncthing/syncthing) - Syncthing is a continuous,
multi-device file synchronization tool which combines concepts from Git with Bittorrent
downloads to provide fast, decentralized file synchronization.
- [webrtc](https://github.com/pion/webrtc) - Go has the only implementation of the WebRTC
stack in a memory-safe language. `pion/webrtc` can be used with alternate transports and
listeners as is standard in Go so it lends itself to adapting WebRTC applications to Go.
- [SAM-PT] This is a pluggable transport for Tor which has two parts: on the server side,
an I2P-enabled Tor bridge serving itself over a single hop. On the client side, an I2P
enabled pluggable transport client connecting to the Tor bridge over any number of hops.
This is a means of hiding the address of long-term bridge operators from probing by
malicious actors who attempt to access Tor bridges for enumeration purposes.
### What are the alternatives?
- Wrap `libi2pd/api.h` in a C library, provide a CGO wrapper to interface with Go.
- I can't think of a single reason not to do this, regardless of whether go-i2p
development is supported by the project. There are good reasons to do both, but
it's not actually a good reason not to develop go-i2p. This also does not gain the same
ability to experiment with i2p at the transport level that a complete go-i2p would.
Nonetheless, the value for embedders is tremendous so a C interface to i2pd is likely
to be completed by me soon anyway.
- Continue development on `str4d/ire`.
- While this is a fine idea, and ire is technically more complete than go-i2p,
I've written hundreds of thousands of lines of Go, and understand the details of
the language intimately. On the other hand, I've written exactly 98 lines of Rust,
exactly the amount required to stand up my pastebin. I also know developers in the
Go application community who are already asking me about contributing.
Milestones and Ongoing Tasks
----------------------------
- Milestone 1: Common Structures Update
- Milestone 2: Have a transport(NTCP2)
- Milestone 3: Connect 2 go-i2p routers on the same network.
- Milestone 4: Have a working NetDB
- Milestone 5: Communicate across a tunnel with an extant I2P router on a testnet.
- Milestone 6: Be a functioning standalone Reseed Server
- Milestone 7: Streaming and Datgram Libraries
- Milestone 9: Provide a usable I2CP Socket
- Milestone 9: Build a SAM API on the I2CP Socket
It should be considered essential that in particular all exposed function, struct, and
interface comments pass `golint` and `go vet` at all times, since this is expressly intended
to produce a useful library for building I2P routers.

View File

@@ -27,7 +27,8 @@ payload :: data
*/
import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
)
@@ -46,24 +47,41 @@ const (
CERT_MIN_SIZE = 3
)
type Certificate []byte
type Certificate struct {
kind Integer
leng Integer
payl []byte
}
func (c *Certificate) RawBytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.leng.Bytes()...)
bytes = append(bytes, c.payl...)
return bytes
}
func (c *Certificate) ExcessBytes() []byte {
return c.payl[c.leng.Int():]
}
func (c *Certificate) Bytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.leng.Bytes()...)
bytes = append(bytes, c.Data()...)
return bytes
}
func (c *Certificate) length() (cert_len int) {
cert_len = len(c.Bytes())
return
}
//
// Return the Certificate Type specified in the first byte of the Certificate,
// and an error if the certificate is shorter than the minimum certificate size.
//
func (certificate Certificate) Type() (cert_type int, err error) {
cert_len := len(certificate)
if cert_len < CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "(Certificate) Type",
"certificate_bytes_length": cert_len,
"reason": "too short (len < CERT_MIN_SIZE)",
}).Error("invalid certificate")
err = errors.New("error parsing certificate length: certificate is too short")
return
}
cert_type = Integer([]byte{certificate[0]})
func (c *Certificate) Type() (cert_type int) {
cert_type = c.kind.Int()
return
}
@@ -72,67 +90,93 @@ func (certificate Certificate) Type() (cert_type int, err error) {
// shorter than the minimum certificate size or if the reported length doesn't
// match the provided data.
//
func (certificate Certificate) Length() (length int, err error) {
cert_len := len(certificate)
_, err = certificate.Type()
if err != nil {
return
}
length = Integer(certificate[1:CERT_MIN_SIZE])
inferred_len := length + CERT_MIN_SIZE
if inferred_len > cert_len {
log.WithFields(log.Fields{
"at": "(Certificate) Length",
"certificate_bytes_length": cert_len,
"certificate_length_field": length,
"expected_bytes_length": inferred_len,
"reason": "data shorter than specified",
}).Warn("certificate format warning")
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
} else if cert_len > inferred_len {
log.WithFields(log.Fields{
"at": "(Certificate) Length",
"certificate_bytes_length": cert_len,
"certificate_length_field": length,
"expected_bytes_length": inferred_len,
"reason": "data longer than expected",
}).Warn("certificate format warning")
err = errors.New("certificate parsing warning: certificate contains data beyond length")
}
func (c *Certificate) Length() (length int) {
length = c.leng.Int()
return
}
//
// Return the Certificate data and any errors encountered parsing the Certificate.
//
func (certificate Certificate) Data() (data []byte, err error) {
length, err := certificate.Length()
if err != nil {
switch err.Error() {
case "error parsing certificate length: certificate is too short":
func (c *Certificate) Data() (data []byte) {
lastElement := c.Length()
if lastElement > len(c.payl) {
data = c.payl
} else {
data = c.payl[0:lastElement]
}
return
}
func NewCertificate(data []byte) (certificate *Certificate, err error) {
certificate = &Certificate{}
switch len(data) {
case 0:
certificate.kind = NewInteger([]byte{0})
certificate.leng = NewInteger([]byte{0})
log.WithFields(log.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
}).Error("invalid certificate")
err = fmt.Errorf("error parsing certificate: certificate is too short")
return
case 1:
certificate.kind = NewInteger(data[0:0])
certificate.leng = NewInteger([]byte{0})
log.WithFields(log.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
}).Error("invalid certificate")
err = fmt.Errorf("error parsing certificate: certificate is too short")
return
case 2:
certificate.kind = NewInteger(data[0:1])
certificate.leng = NewInteger([]byte{0})
log.WithFields(log.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
}).Error("invalid certificate")
err = fmt.Errorf("error parsing certificate length: certificate is too short")
return
default:
certificate.kind = NewInteger(data[0:1])
certificate.leng = NewInteger(data[1:3])
payleng := len(data) - CERT_MIN_SIZE
certificate.payl = data[CERT_MIN_SIZE:]
if certificate.leng.Int() > len(data)-CERT_MIN_SIZE {
err = fmt.Errorf("certificate parsing warning: certificate data is shorter than specified by length")
log.WithFields(log.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": certificate.leng.Int(),
"certificate_payload_length": payleng,
"reason": err.Error(),
}).Error("invalid certificate")
return
case "certificate parsing warning: certificate data is shorter than specified by length":
data = certificate[CERT_MIN_SIZE:]
return
case "certificate parsing warning: certificate contains data beyond length":
data = certificate[CERT_MIN_SIZE : length+CERT_MIN_SIZE]
} else if certificate.leng.Int() < len(data)-CERT_MIN_SIZE {
err = fmt.Errorf("certificate parsing warning: certificate data is longer than specified by length")
log.WithFields(log.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": certificate.leng.Int(),
"certificate_payload_length": payleng,
"reason": err.Error(),
}).Error("invalid certificate")
return
}
return
}
data = certificate[CERT_MIN_SIZE:]
return
}
//
// Read a Certificate from a slice of bytes, returning any extra data on the end of the slice
// and any errors if a valid Certificate could not be read.
//
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
certificate = Certificate(data)
length, err := certificate.Length()
if err != nil && err.Error() == "certificate parsing warning: certificate contains data beyond length" {
certificate = Certificate(data[:length+CERT_MIN_SIZE])
remainder = data[length+CERT_MIN_SIZE:]
func ReadCertificate(data []byte) (certificate *Certificate, remainder []byte, err error) {
certificate, err = NewCertificate(data)
if err != nil && err.Error() == "certificate parsing warning: certificate data is longer than specified by length" {
remainder = certificate.ExcessBytes()
err = nil
}
return

View File

@@ -1,16 +1,17 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCertificateTypeIsFirstByte(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x00}
certificate := Certificate(bytes)
cert_type, err := certificate.Type()
certificate, err := NewCertificate(bytes)
cert_type := certificate.Type()
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
assert.Nil(err)
@@ -20,8 +21,8 @@ func TestCertificateLengthCorrect(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
certificate := Certificate(bytes)
cert_len, err := certificate.Length()
certificate, err := NewCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
assert.Nil(err)
@@ -31,8 +32,8 @@ func TestCertificateLengthErrWhenTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x01}
certificate := Certificate(bytes)
cert_len, err := certificate.Length()
certificate, err := NewCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
if assert.NotNil(err) {
@@ -44,8 +45,8 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate := Certificate(bytes)
cert_len, err := certificate.Length()
certificate, err := NewCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
if assert.NotNil(err) {
@@ -57,8 +58,8 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
certificate := Certificate(bytes)
cert_data, err := certificate.Data()
certificate, err := NewCertificate(bytes)
cert_data := certificate.Data()
assert.Nil(err, "certificate.Data() returned error with valid data")
cert_len := len(cert_data)
@@ -70,13 +71,13 @@ func TestCertificateDataWhenTooLong(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
certificate := Certificate(bytes)
cert_data, err := certificate.Data()
certificate, err := NewCertificate(bytes)
cert_data := certificate.Data()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate contains data beyond length", err.Error(), "correct error message should be returned")
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
}
cert_len := len(cert_data)
cert_len := certificate.Length() //len(cert_data)
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was too long")
if cert_data[0] != 0xff || cert_data[1] != 0xff {
t.Fatal("certificate.Data() returned incorrect data when data was too long")
@@ -87,8 +88,8 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate := Certificate(bytes)
cert_data, err := certificate.Data()
certificate, err := NewCertificate(bytes)
cert_data := certificate.Data()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
@@ -104,7 +105,7 @@ func TestReadCertificateWithCorrectData(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on a valid certificate")
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
}
@@ -115,7 +116,7 @@ func TestReadCertificateWithDataTooShort(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
assert.Equal(cert.length(), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on certificate with missing data")
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
@@ -128,9 +129,9 @@ func TestReadCertificateWithRemainder(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
assert.Equal(len(remainder), 1, "ReadCertificate() returned incorrect length remainder on certificate with extra data")
assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
// assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
assert.Nil(err)
}
@@ -140,7 +141,7 @@ func TestReadCertificateWithInvalidLength(t *testing.T) {
bytes := []byte{0x00, 0x00}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
assert.Equal(cert.length(), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
assert.Equal(len(remainder), 0, "ReadCertificate() returned non-zero length remainder on invalid certificate")
if assert.NotNil(err) {
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")

View File

@@ -18,7 +18,7 @@ type Date [8]byte
// struct.
//
func (date Date) Time() (date_time time.Time) {
seconds := Integer(date[:])
date_time = time.Unix(0, int64(seconds*1000000))
seconds := NewInteger(date[:])
date_time = time.Unix(0, int64(seconds.Int()*1000000))
return
}

View File

@@ -9,10 +9,11 @@ Identical to KeysAndCert
*/
import (
"strings"
"github.com/go-i2p/go-i2p/lib/common/base32"
"github.com/go-i2p/go-i2p/lib/common/base64"
"github.com/go-i2p/go-i2p/lib/crypto"
"strings"
)
//
@@ -29,7 +30,7 @@ func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, erro
return KeysAndCert(destination).SigningPublicKey()
}
func (destination Destination) Certificate() (Certificate, error) {
func (destination Destination) Certificate() (*Certificate, error) {
return KeysAndCert(destination).Certificate()
}

View File

@@ -15,11 +15,26 @@ const (
INTEGER_SIZE = 8
)
type Integer []byte
func (i Integer) Bytes() []byte {
return i[:]
}
func (i Integer) Int() int {
return intFromBytes(i.Bytes())
}
func NewInteger(bytes []byte) Integer {
i := Integer(bytes)
return i
}
//
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
// integer and return an int representation.
//
func Integer(number []byte) (value int) {
func intFromBytes(number []byte) (value int) {
num_len := len(number)
if num_len < INTEGER_SIZE {
number = append(

View File

@@ -1,31 +1,32 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIntegerBigEndian(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
integer := Integer(bytes)
integer := NewInteger(bytes)
assert.Equal(integer, 1, "Integer() did not parse bytes big endian")
assert.Equal(integer.Int(), 1, "NewInteger() did not parse bytes big endian")
}
func TestWorksWithOneByte(t *testing.T) {
assert := assert.New(t)
integer := Integer([]byte{0x01})
integer := NewInteger([]byte{0x01})
assert.Equal(integer, 1, "Integer() did not correctly parse single byte slice")
assert.Equal(integer.Int(), 1, "NewInteger() did not correctly parse single byte slice")
}
func TestIsZeroWithNoData(t *testing.T) {
assert := assert.New(t)
integer := Integer([]byte{})
integer := NewInteger([]byte{})
assert.Equal(integer, 0, "Integer() did not correctly parse zero length byte slice")
assert.Equal(integer.Int(), 0, "NewInteger() did not correctly parse zero length byte slice")
}

View File

@@ -28,6 +28,7 @@ payload :: data
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
@@ -50,6 +51,10 @@ const (
KEYCERT_CRYPTO_ELG = iota
)
const (
KEYCERT_MIN_SIZE = 7
)
// SigningPublicKey sizes for Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
@@ -74,65 +79,34 @@ const (
KEYCERT_SPK_SIZE = 128
)
type KeyCertificate []byte
//type KeyCertificate []byte
type KeyCertificate struct {
*Certificate
spkType Integer
cpkType Integer
}
//
// The data contained in the Key Certificate.
//
func (key_certificate KeyCertificate) Data() ([]byte, error) {
return Certificate(key_certificate).Data()
return key_certificate.Certificate.RawBytes(), nil
}
//
// The SigningPublicKey type this Key Certificate describes and any errors encountered
// parsing the KeyCertificate.
//
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int, err error) {
data, err := key_certificate.Data()
if err != nil {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SigningPublicKeyType",
"reason": err.Error(),
}).Error("error getting signing public key")
return
}
data_len := len(data)
if data_len < 2 {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SigningPublicKeyType",
"data_len": data_len,
"required_len": 2,
"reason": "not enough data",
}).Error("error parsing key certificate")
err = errors.New("error parsing key certificate: not enough data")
return
}
signing_pubkey_type = Integer(data[:2])
return
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int) {
return key_certificate.spkType.Int()
}
//
// The PublicKey type this Key Certificate describes and any errors encountered parsing
// this KeyCertificate.
//
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
data, err := key_certificate.Data()
if err != nil {
return
}
data_len := len(data)
if data_len < 4 {
log.WithFields(log.Fields{
"at": "(KeyCertificate) PublicKeyType",
"data_len": data_len,
"required_len": 4,
"reason": "not enough data",
}).Error("error parsing key certificate")
err = errors.New("error parsing key certificate: not enough data")
return
}
pubkey_type = Integer(data[2:4])
return
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int) {
return key_certificate.cpkType.Int()
}
//
@@ -140,7 +114,7 @@ func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err erro
// it along with any errors encountered constructing the PublicKey.
//
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) {
key_type, err := key_certificate.PublicKeyType()
key_type := key_certificate.PublicKeyType()
if err != nil {
return
}
@@ -169,7 +143,7 @@ func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_ke
// it along with any errors encountered constructing the SigningPublicKey.
//
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
signing_key_type, err := key_certificate.PublicKeyType()
signing_key_type := key_certificate.PublicKeyType()
if err != nil {
return
}
@@ -201,7 +175,7 @@ func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (si
var ec_key crypto.ECP521PublicKey
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
copy(ec_key[:], data)
copy(ec_key[KEYCERT_SPK_SIZE:], key_certificate[4:4+extra])
copy(ec_key[KEYCERT_SPK_SIZE:], key_certificate.Certificate.RawBytes()[4:4+extra])
signing_public_key = ec_key
case KEYCERT_SIGN_RSA2048:
//var rsa_key crypto.RSA2048PublicKey
@@ -233,14 +207,59 @@ func (key_certificate KeyCertificate) SignatureSize() (size int) {
KEYCERT_SIGN_ED25519: 64,
KEYCERT_SIGN_ED25519PH: 64,
}
key_type, err := key_certificate.SigningPublicKeyType()
if err != nil {
key_type := key_certificate.SigningPublicKeyType()
/*if err != nil {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SignatureSize",
"key_type": key_type,
"reason": "failed to read signing public key type",
}).Error("error getting signature size")
return 0
}
}*/
return sizes[int(key_type)]
}
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, err error) {
var certificate *Certificate
certificate, _, err = ReadCertificate(bytes)
//if err != nil {
// return nil, err
//}
if len(bytes) < KEYCERT_MIN_SIZE {
err = errors.New("error parsing key certificate: not enough data")
}
switch len(bytes) {
case 4:
key_certificate = &KeyCertificate{
Certificate: certificate,
spkType: Integer(bytes[4:]),
cpkType: Integer([]byte{0}),
}
case 5:
key_certificate = &KeyCertificate{
Certificate: certificate,
spkType: Integer(bytes[4:5]),
cpkType: Integer([]byte{0}),
}
case 6:
key_certificate = &KeyCertificate{
Certificate: certificate,
spkType: Integer(bytes[4:5]),
cpkType: Integer(bytes[6:]),
}
default:
key_certificate = &KeyCertificate{
Certificate: certificate,
spkType: Integer(bytes[4:5]),
cpkType: Integer(bytes[6:7]),
}
}
//key_certificate.PublicKey = NewPublicKey(bytes)
return
}
func KeyCertificateFromCertificate(certificate *Certificate) *KeyCertificate {
k, _ := NewKeyCertificate(certificate.RawBytes())
return k
}

View File

@@ -1,15 +1,16 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
pk_type, err := key_cert.SigningPublicKeyType()
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
pk_type := key_cert.SigningPublicKeyType()
assert.Nil(err, "SigningPublicKeyType() returned error with valid data")
assert.Equal(pk_type, KEYCERT_SIGN_P521, "SigningPublicKeyType() did not return correct typec")
@@ -18,8 +19,10 @@ func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
func TestSingingPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
_, err := key_cert.SigningPublicKeyType()
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
sk_type := key_cert.SigningPublicKeyType()
assert.Equal(sk_type, 0, "SigningPublicKeyType() did not return correct typec")
if assert.NotNil(err) {
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
@@ -29,8 +32,8 @@ func TestSingingPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
pk_type, err := key_cert.PublicKeyType()
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
pk_type := key_cert.PublicKeyType()
assert.Nil(err, "PublicKey() returned error with valid data")
assert.Equal(pk_type, KEYCERT_SIGN_P521, "PublicKeyType() did not return correct typec")
@@ -39,20 +42,21 @@ func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
_, err := key_cert.PublicKeyType()
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
pk_type := key_cert.PublicKeyType()
if assert.NotNil(err) {
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
}
assert.Equal(pk_type, 0, "PublicKeyType() did not return correct typec")
}
func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 255)
_, err := key_cert.ConstructPublicKey(data)
_, err = key_cert.ConstructPublicKey(data)
if assert.NotNil(err) {
assert.Equal("error constructing public key: not enough data", err.Error(), "correct error message should be returned")
@@ -62,7 +66,7 @@ func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 256)
pk, err := key_cert.ConstructPublicKey(data)
@@ -73,9 +77,9 @@ func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 127)
_, err := key_cert.ConstructSigningPublicKey(data)
_, err = key_cert.ConstructSigningPublicKey(data)
if assert.NotNil(err) {
assert.Equal("error constructing signing public key: not enough data", err.Error(), "correct error message should be returned")
@@ -85,7 +89,7 @@ func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
@@ -96,7 +100,7 @@ func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
func TestConstructSigningPublicKeyWithP256(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
@@ -107,7 +111,7 @@ func TestConstructSigningPublicKeyWithP256(t *testing.T) {
func TestConstructSigningPublicKeyWithP384(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
@@ -118,7 +122,7 @@ func TestConstructSigningPublicKeyWithP384(t *testing.T) {
func TestConstructSigningPublicKeyWithP521(t *testing.T) {
assert := assert.New(t)
key_cert := KeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)

View File

@@ -47,6 +47,7 @@ total length: 387+ bytes
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
@@ -70,7 +71,7 @@ func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
if err != nil {
return
}
cert_len, err := cert.Length()
cert_len := cert.Length()
if err != nil {
return
}
@@ -82,12 +83,12 @@ func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
key = elg_key
} else {
// A Certificate is present in this KeysAndCert
cert_type, _ := cert.Type()
cert_type := cert.Type()
if cert_type == CERT_KEY {
// This KeysAndCert contains a Key Certificate, construct
// a PublicKey from the data in the KeysAndCert and
// any additional data in the Certificate.
key, err = KeyCertificate(cert).ConstructPublicKey(
key, err = KeyCertificateFromCertificate(cert).ConstructPublicKey(
keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE],
)
} else {
@@ -116,7 +117,7 @@ func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.S
if err != nil {
return
}
cert_len, err := cert.Length()
cert_len := cert.Length()
if err != nil {
return
}
@@ -128,12 +129,12 @@ func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.S
signing_public_key = dsa_pk
} else {
// A Certificate is present in this KeysAndCert
cert_type, _ := cert.Type()
cert_type := cert.Type()
if cert_type == CERT_KEY {
// This KeysAndCert contains a Key Certificate, construct
// a SigningPublicKey from the data in the KeysAndCert and
// any additional data in the Certificate.
signing_public_key, err = KeyCertificate(cert).ConstructSigningPublicKey(
signing_public_key, err = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey(
keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE],
)
} else {
@@ -153,7 +154,7 @@ func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.S
// Return the Certificate contained in the KeysAndCert and any errors encountered while parsing the
// KeysAndCert or Certificate.
//
func (keys_and_cert KeysAndCert) Certificate() (cert Certificate, err error) {
func (keys_and_cert KeysAndCert) Certificate() (cert *Certificate, err error) {
keys_cert_len := len(keys_and_cert)
if keys_cert_len < KEYS_AND_CERT_MIN_SIZE {
log.WithFields(log.Fields{
@@ -187,14 +188,14 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
}
keys_and_cert = KeysAndCert(data[:KEYS_AND_CERT_MIN_SIZE])
cert, _ := keys_and_cert.Certificate()
cert_len, cert_len_err := cert.Length()
cert_len := cert.Length()
if cert_len == 0 {
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
return
}
if data_len < KEYS_AND_CERT_MIN_SIZE+cert_len {
keys_and_cert = append(keys_and_cert, data[KEYS_AND_CERT_MIN_SIZE:]...)
err = cert_len_err
//err = cert_len_err
} else {
keys_and_cert = append(keys_and_cert, data[KEYS_AND_CERT_MIN_SIZE:KEYS_AND_CERT_MIN_SIZE+cert_len]...)
remainder = data[KEYS_AND_CERT_MIN_SIZE+cert_len:]

View File

@@ -1,8 +1,9 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCertificateWithMissingData(t *testing.T) {
@@ -17,7 +18,7 @@ func TestCertificateWithMissingData(t *testing.T) {
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
cert_bytes := []byte(cert)
cert_bytes := cert.Bytes()
if assert.Equal(len(cert_data), len(cert_bytes)) {
assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return available data when cert was missing some data")
}
@@ -33,7 +34,7 @@ func TestCertificateWithValidData(t *testing.T) {
cert, err := keys_and_cert.Certificate()
assert.Nil(err)
cert_bytes := []byte(cert)
cert_bytes := cert.Bytes()
if assert.Equal(len(cert_data), len(cert_bytes)) {
assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return correct data with valid cert")
}

View File

@@ -50,8 +50,9 @@ func (lease Lease) TunnelGateway() (hash Hash) {
// Parse the TunnelID Integer in the Lease.
//
func (lease Lease) TunnelID() uint32 {
i := NewInteger(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE])
return uint32(
Integer(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE]),
i.Int(),
)
}

View File

@@ -82,6 +82,7 @@ signature :: Signature
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
@@ -139,7 +140,7 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
if err != nil {
return
}
cert_len, err := cert.Length()
cert_len := cert.Length()
if err != nil {
return
}
@@ -162,12 +163,12 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
signing_public_key = dsa_pk
} else {
// A Certificate is present in this LeaseSet's Destination
cert_type, _ := cert.Type()
cert_type := cert.Type()
if cert_type == CERT_KEY {
// This LeaseSet's Destination's Certificate is a Key Certificate,
// create the signing publickey key using any data that might be
// contained in the key certificate.
signing_public_key, err = KeyCertificate(cert).ConstructSigningPublicKey(
signing_public_key, err = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey(
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
)
} else {
@@ -201,7 +202,8 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
err = errors.New("error parsing lease count: not enough data")
return
}
count = Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
c := NewInteger([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
count = c.Int()
if count > 16 {
log.WithFields(log.Fields{
"at": "(LeaseSet) LeaseCount",
@@ -269,10 +271,10 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
if err != nil {
return
}
cert_type, _ := cert.Type()
cert_type := cert.Type()
var end int
if cert_type == CERT_KEY {
end = start + KeyCertificate(cert).SignatureSize()
end = start + KeyCertificateFromCertificate(cert).SignatureSize()
} else {
end = start + LEASE_SET_SIG_SIZE
}

View File

@@ -2,8 +2,9 @@ package common
import (
"bytes"
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func buildDestination() RouterIdentity {
@@ -71,7 +72,7 @@ func TestDestinationIsCorrect(t *testing.T) {
assert.Nil(err)
dest_cert, err := dest.Certificate()
assert.Nil(err)
cert_type, err := dest_cert.Type()
cert_type := dest_cert.Type()
assert.Nil(err)
assert.Equal(CERT_KEY, cert_type)
}

View File

@@ -28,24 +28,26 @@ val_string :: String
import (
"encoding/binary"
"errors"
log "github.com/sirupsen/logrus"
"sort"
log "github.com/sirupsen/logrus"
)
type Mapping []byte
// Parsed key-values pairs inside a Mapping.
type MappingValues [][2]String
type MappingValues [][2]I2PString
//
// Returns the values contained in a Mapping in the form of a MappingValues.
//
func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
var str String
var str I2PString
var remainder = mapping
var err error
length := Integer(remainder[:2])
l := NewInteger(remainder[:2])
length := l.Int()
inferred_length := length + 2
remainder = remainder[2:]
mapping_len := len(mapping)
@@ -72,7 +74,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
for {
// Read a key, breaking on fatal errors
// and appending warnings
str, remainder, err = ReadString(remainder)
str, remainder, err = ReadI2PString(remainder)
key_str := str
if err != nil {
if stopValueRead(err) {
@@ -92,7 +94,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
// Read a value, breaking on fatal errors
// and appending warnings
str, remainder, err = ReadString(remainder)
str, remainder, err = ReadI2PString(remainder)
val_str := str
if err != nil {
if stopValueRead(err) {
@@ -111,7 +113,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
remainder = remainder[1:]
// Append the key-value pair and break if there is no more data to read
map_values = append(map_values, [2]String{key_str, val_str})
map_values = append(map_values, [2]I2PString{key_str, val_str})
if len(remainder) == 0 {
break
}
@@ -174,7 +176,7 @@ func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
}
map_vals = append(
map_vals,
[2]String{key_str, val_str},
[2]I2PString{key_str, val_str},
)
}
mapping = ValuesToMapping(map_vals)

View File

@@ -3,8 +3,9 @@ package common
import (
"bytes"
"errors"
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestValuesExclusesPairWithBadData(t *testing.T) {
@@ -116,10 +117,10 @@ func TestMappingOrderSortsValuesThenKeys(t *testing.T) {
a, _ := ToI2PString("a")
b, _ := ToI2PString("b")
values := MappingValues{
[2]String{b, b},
[2]String{b, a},
[2]String{a, b},
[2]String{a, a},
[2]I2PString{b, b},
[2]I2PString{b, a},
[2]I2PString{a, b},
[2]I2PString{a, a},
}
mappingOrder(values)
for i, pair := range values {

View File

@@ -37,6 +37,7 @@ options :: Mapping
import (
"errors"
log "github.com/sirupsen/logrus"
)
@@ -56,7 +57,8 @@ func (router_address RouterAddress) Cost() (cost int, err error) {
if exit {
return
}
cost = Integer([]byte{router_address[0]})
c := NewInteger([]byte{router_address[0]})
cost = c.Int()
return
}
@@ -77,12 +79,12 @@ func (router_address RouterAddress) Expiration() (date Date, err error) {
// Return the Transport type for this RouterAddress and any errors encountered
// parsing the RouterAddress.
//
func (router_address RouterAddress) TransportStyle() (str String, err error) {
func (router_address RouterAddress) TransportStyle() (str I2PString, err error) {
err, exit := router_address.checkValid()
if exit {
return
}
str, _, err = ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
str, _, err = ReadI2PString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
return
}
@@ -95,7 +97,7 @@ func (router_address RouterAddress) Options() (mapping Mapping, err error) {
if exit {
return
}
_, remainder, err := ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
_, remainder, err := ReadI2PString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
if len(remainder) == 0 {
return
}
@@ -137,7 +139,7 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
return
}
router_address = append(router_address, data[:ROUTER_ADDRESS_MIN_SIZE]...)
str, remainder, err := ReadString(data[ROUTER_ADDRESS_MIN_SIZE:])
str, remainder, err := ReadI2PString(data[ROUTER_ADDRESS_MIN_SIZE:])
if err != nil {
return
}
@@ -145,7 +147,8 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
map_size := 0
mapping := make([]byte, 0)
if len(remainder) >= 2 {
map_size = Integer(remainder[:2])
ms := NewInteger(remainder[:2])
map_size = ms.Int()
if len(remainder) < map_size+2 {
err = errors.New("not enough data for map inside router address")
router_address = RouterAddress([]byte{})

View File

@@ -25,7 +25,7 @@ func (router_identity RouterIdentity) SigningPublicKey() (crypto.SigningPublicKe
return KeysAndCert(router_identity).SigningPublicKey()
}
func (router_identity RouterIdentity) Certificate() (Certificate, error) {
func (router_identity RouterIdentity) Certificate() (*Certificate, error) {
return KeysAndCert(router_identity).Certificate()
}

View File

@@ -75,6 +75,7 @@ signature :: Signature
import (
"errors"
log "github.com/sirupsen/logrus"
)
@@ -144,7 +145,8 @@ func (router_info RouterInfo) RouterAddressCount() (count int, err error) {
err = errors.New("error parsing router addresses: not enough data")
return
}
count = Integer([]byte{remainder[8]})
c := NewInteger([]byte{remainder[8]})
count = c.Int()
return
}
@@ -210,7 +212,7 @@ func (router_info RouterInfo) Signature() (signature Signature) {
head := router_info.optionsLocation()
size := head + router_info.optionsSize()
ident, _ := router_info.RouterIdentity()
keyCert := KeyCertificate(ident)
keyCert, _ := NewKeyCertificate(ident)
sigSize := keyCert.SignatureSize()
signature = Signature(router_info[size : size+sigSize])
return
@@ -263,6 +265,7 @@ func (router_info RouterInfo) optionsLocation() (location int) {
//
func (router_info RouterInfo) optionsSize() (size int) {
head := router_info.optionsLocation()
size = Integer(router_info[head:head+2]) + 2
s := NewInteger(router_info[head : head+2])
size = s.Int() + 2
return
}

View File

@@ -1,13 +1,14 @@
package common
/*
I2P String
I2P I2PString
https://geti2p.net/spec/common-structures#string
Accurate for version 0.9.24
*/
import (
"errors"
log "github.com/sirupsen/logrus"
)
@@ -16,27 +17,28 @@ const (
STRING_MAX_SIZE = 255
)
type String []byte
type I2PString []byte
//
// Look up the length of the string, reporting errors if the string is
// invalid or the specified length does not match the provided data.
//
func (str String) Length() (length int, err error) {
func (str I2PString) Length() (length int, err error) {
if len(str) == 0 {
log.WithFields(log.Fields{
"at": "(String) Length",
"at": "(I2PString) Length",
"reason": "no data",
}).Error("error parsing string")
err = errors.New("error parsing string: zero length")
return
}
length = Integer([]byte{byte(str[0])})
l := NewInteger([]byte{byte(str[0])})
length = l.Int()
inferred_len := length + 1
str_len := len(str)
if inferred_len > str_len {
log.WithFields(log.Fields{
"at": "(String) Length",
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"expected_bytes_length": inferred_len,
@@ -45,7 +47,7 @@ func (str String) Length() (length int, err error) {
err = errors.New("string parsing warning: string data is shorter than specified by length")
} else if str_len > inferred_len {
log.WithFields(log.Fields{
"at": "(String) Length",
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"expected_bytes_length": inferred_len,
@@ -59,7 +61,7 @@ func (str String) Length() (length int, err error) {
//
// Return the string data and any errors encountered by Length.
//
func (str String) Data() (data string, err error) {
func (str I2PString) Data() (data string, err error) {
length, err := str.Length()
if err != nil {
switch err.Error() {
@@ -78,14 +80,14 @@ func (str String) Data() (data string, err error) {
}
//
// This function takes an unformatted Go string and returns a String
// This function takes an unformatted Go string and returns a I2PString
// and any errors encountered during the encoding.
//
func ToI2PString(data string) (str String, err error) {
func ToI2PString(data string) (str I2PString, err error) {
data_len := len(data)
if data_len > STRING_MAX_SIZE {
log.WithFields(log.Fields{
"at": "ToI2PString",
"at": "ToI2PI2PString",
"string_len": data_len,
"max_len": STRING_MAX_SIZE,
"reason": "too much data",
@@ -95,19 +97,19 @@ func ToI2PString(data string) (str String, err error) {
}
i2p_string := []byte{byte(data_len)}
i2p_string = append(i2p_string, []byte(data)...)
str = String(i2p_string)
str = I2PString(i2p_string)
return
}
//
// Read a string from a slice of bytes, returning any extra data on the end
// of the slice and any errors encountered parsing the String.
// of the slice and any errors encountered parsing the I2PString.
//
func ReadString(data []byte) (str String, remainder []byte, err error) {
str = String(data)
length, err := String(data).Length()
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
str = I2PString(data)
length, err := I2PString(data).Length()
if err != nil && err.Error() == "string parsing warning: string contains data beyond length" {
str = String(data[:length+1])
str = I2PString(data[:length+1])
remainder = data[length+1:]
err = nil
}

View File

@@ -1,23 +1,24 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestStringReportsCorrectLength(t *testing.T) {
assert := assert.New(t)
str_len, err := String([]byte{0x02, 0x00, 0x00}).Length()
str_len, err := I2PString([]byte{0x02, 0x00, 0x00}).Length()
assert.Equal(str_len, 2, "Length() did not report correct length")
assert.Nil(err, "Length() reported an error on valid string")
}
func TestStringReportsLengthZeroError(t *testing.T) {
func TestI2PStringReportsLengthZeroError(t *testing.T) {
assert := assert.New(t)
str_len, err := String(make([]byte, 0)).Length()
str_len, err := I2PString(make([]byte, 0)).Length()
assert.Equal(str_len, 0, "Length() reported non-zero length on empty slice")
if assert.NotNil(err) {
@@ -25,10 +26,10 @@ func TestStringReportsLengthZeroError(t *testing.T) {
}
}
func TestStringReportsExtraDataError(t *testing.T) {
func TestI2PStringReportsExtraDataError(t *testing.T) {
assert := assert.New(t)
str_len, err := String([]byte{0x01, 0x00, 0x00}).Length()
str_len, err := I2PString([]byte{0x01, 0x00, 0x00}).Length()
assert.Equal(str_len, 1, "Length() reported wrong size when extra data present")
if assert.NotNil(err) {
@@ -36,10 +37,10 @@ func TestStringReportsExtraDataError(t *testing.T) {
}
}
func TestStringDataReportsLengthZeroError(t *testing.T) {
func TestI2PStringDataReportsLengthZeroError(t *testing.T) {
assert := assert.New(t)
str_len, err := String([]byte{0x01}).Length()
str_len, err := I2PString([]byte{0x01}).Length()
assert.Equal(str_len, 1, "Length() reported wrong size with missing data")
if assert.NotNil(err) {
@@ -47,10 +48,10 @@ func TestStringDataReportsLengthZeroError(t *testing.T) {
}
}
func TestStringDataReportsExtraDataError(t *testing.T) {
func TestI2PStringDataReportsExtraDataError(t *testing.T) {
assert := assert.New(t)
data, err := String([]byte{0x01, 0x00, 0x01}).Data()
data, err := I2PString([]byte{0x01, 0x00, 0x01}).Data()
data_len := len(data)
assert.Equal(data_len, 1, "Data() reported wrong size on string with extra data")
@@ -59,10 +60,10 @@ func TestStringDataReportsExtraDataError(t *testing.T) {
}
}
func TestStringDataEmptyWhenZeroLength(t *testing.T) {
func TestI2PStringDataEmptyWhenZeroLength(t *testing.T) {
assert := assert.New(t)
data, err := String(make([]byte, 0)).Data()
data, err := I2PString(make([]byte, 0)).Data()
assert.Equal(len(data), 0, "Data() returned data when none was present:")
if assert.NotNil(err) {
@@ -70,10 +71,10 @@ func TestStringDataEmptyWhenZeroLength(t *testing.T) {
}
}
func TestStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
func TestI2PStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
assert := assert.New(t)
data, err := String([]byte{0x01}).Data()
data, err := I2PString([]byte{0x01}).Data()
assert.Equal(len(data), 0, "Data() returned data when only length was present")
if assert.NotNil(err) {
@@ -81,7 +82,7 @@ func TestStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
}
}
func TestToI2PStringFormatsCorrectly(t *testing.T) {
func TestToI2PI2PStringFormatsCorrectly(t *testing.T) {
assert := assert.New(t)
i2p_string, err := ToI2PString(string([]byte{0x08, 0x09}))
@@ -111,38 +112,38 @@ func TestReadStringReadsLength(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x01, 0x04, 0x06}
str, remainder, err := ReadString(bytes)
str, remainder, err := ReadI2PString(bytes)
assert.Nil(err, "ReadString() returned error reading string with extra data")
assert.Equal(len(str), 2, "ReadString() did not return correct string length")
assert.Equal(1, int(str[0]), "ReadString() did not return correct string")
assert.Equal(4, int(str[1]), "ReadString() did not return correct string")
assert.Equal(len(remainder), 1, "ReadString() did not return correct remainder length")
assert.Equal(6, int(remainder[0]), "ReadString() did not return correct remainder")
assert.Nil(err, "ReadI2PString() returned error reading string with extra data")
assert.Equal(len(str), 2, "ReadI2PString() did not return correct string length")
assert.Equal(1, int(str[0]), "ReadI2PString() did not return correct string")
assert.Equal(4, int(str[1]), "ReadI2PString() did not return correct string")
assert.Equal(len(remainder), 1, "ReadI2PString() did not return correct remainder length")
assert.Equal(6, int(remainder[0]), "ReadI2PString() did not return correct remainder")
}
func TestReadStringErrWhenEmptySlice(t *testing.T) {
func TestReadI2PStringErrWhenEmptySlice(t *testing.T) {
assert := assert.New(t)
bytes := make([]byte, 0)
_, _, err := ReadString(bytes)
_, _, err := ReadI2PString(bytes)
if assert.NotNil(err) {
assert.Equal(err.Error(), "error parsing string: zero length", "correct error message should be returned")
}
}
func TestReadStringErrWhenDataTooShort(t *testing.T) {
func TestReadI2PStringErrWhenDataTooShort(t *testing.T) {
assert := assert.New(t)
short_str := []byte{0x03, 0x01}
str, remainder, err := ReadString(short_str)
str, remainder, err := ReadI2PString(short_str)
if assert.NotNil(err) {
assert.Equal(err.Error(), "string parsing warning: string data is shorter than specified by length", "correct error message should be returned")
}
assert.Equal(len(str), 2, "ReadString() did not return the slice as string when too long")
assert.Equal(3, int(str[0]), "ReadString() did not return the correct partial string")
assert.Equal(1, int(str[1]), "ReadString() did not return the correct partial string")
assert.Equal(len(remainder), 0, "ReadString() returned a remainder when the string data was too short")
assert.Equal(len(str), 2, "ReadI2PString() did not return the slice as string when too long")
assert.Equal(3, int(str[0]), "ReadI2PString() did not return the correct partial string")
assert.Equal(1, int(str[1]), "ReadI2PString() did not return the correct partial string")
assert.Equal(len(remainder), 0, "ReadI2PString() returned a remainder when the string data was too short")
}