mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-09-16 20:47:51 -04:00
Compare commits
4 Commits
proposal
...
no-more-by
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f113977419 | ||
![]() |
86dc323348 | ||
![]() |
a80c9973f9 | ||
![]() |
7adf3d7577 |
26
.circleci/config.yml
Normal file
26
.circleci/config.yml
Normal 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
|
2
Makefile
2
Makefile
@@ -19,7 +19,7 @@ $(EXE):
|
||||
$(GO) build -v -o $(EXE)
|
||||
|
||||
test:
|
||||
$(GO) test ./...
|
||||
$(GO) test -v -failfast ./lib/common
|
||||
|
||||
clean:
|
||||
$(GO) clean -v
|
||||
|
189
PROPOSAL.md
189
PROPOSAL.md
@@ -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.
|
@@ -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
|
||||
|
@@ -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")
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -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")
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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:]
|
||||
|
@@ -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")
|
||||
}
|
||||
|
@@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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 {
|
||||
|
@@ -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{})
|
||||
|
@@ -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()
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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")
|
||||
}
|
||||
|
Reference in New Issue
Block a user