Compare commits
39 Commits
noise-expe
...
structify
Author | SHA1 | Date | |
---|---|---|---|
836f287c9d | |||
d9543745a4 | |||
a87847ef51 | |||
bffc1dfe38 | |||
c181a974cd | |||
27547611ed | |||
3f4d02dc3e | |||
6431100245 | |||
e6b02ab341 | |||
53b629dd15 | |||
a576f0685f | |||
fc0404a11a | |||
cfc3cc97ca | |||
a3b83b5e1e | |||
4a9943de9b | |||
82ba9c1b68 | |||
4f574a28d3 | |||
9d248eda5a | |||
4f19c48da3 | |||
961dfe4266 | |||
afb38b6165 | |||
0aa32b4aad | |||
3adf694c25 | |||
a11c3b73cb | |||
853bc79f8f | |||
77f1c6dd0a | |||
ee7d8a0d63 | |||
c253bf31ac | |||
b97b2854c1 | |||
d5266f8980 | |||
6de4dde1f2 | |||
648c05b15f | |||
bfc7237ba6 | |||
ffbdc7f967 | |||
1cd9d16760 | |||
896df4e483 | |||
49d7eeb441 | |||
7893694c91 | |||
1ea426da9c |
@ -1,26 +0,0 @@
|
|||||||
# 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
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,7 +5,4 @@
|
|||||||
*.coverprofile
|
*.coverprofile
|
||||||
*exportable-fuzz.zip
|
*exportable-fuzz.zip
|
||||||
go-i2p
|
go-i2p
|
||||||
*.exe
|
*.exe*.log
|
||||||
.idea/
|
|
||||||
router.info
|
|
||||||
log
|
|
||||||
|
9
.vscode/launch.json
vendored
Normal file
9
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
@ -9,6 +9,7 @@ Install required dependencies
|
|||||||
This example assumes Ubuntu 16.04
|
This example assumes Ubuntu 16.04
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
sudo apt-get install pkg-config libsodium-dev
|
||||||
go get github.com/hkparker/go-i2p
|
go get github.com/hkparker/go-i2p
|
||||||
go get github.com/Sirupsen/logrus
|
go get github.com/Sirupsen/logrus
|
||||||
go get github.com/stretchr/testify/assert
|
go get github.com/stretchr/testify/assert
|
||||||
|
59
Makefile
59
Makefile
@ -1,8 +1,5 @@
|
|||||||
RELEASE_TAG=0.0.1
|
|
||||||
RELEASE_VERSION=${RELEASE_TAG}
|
|
||||||
RELEASE_DESCRIPTION=`cat PASTA.md`
|
|
||||||
REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
CGO_ENABLED=0
|
|
||||||
|
|
||||||
ifdef GOROOT
|
ifdef GOROOT
|
||||||
GO = $(GOROOT)/bin/go
|
GO = $(GOROOT)/bin/go
|
||||||
@ -16,61 +13,19 @@ else
|
|||||||
EXE := $(REPO)/go-i2p
|
EXE := $(REPO)/go-i2p
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#check for gofumpt
|
|
||||||
check_gofumpt:
|
|
||||||
@which gofumpt > /dev/null 2>&1 || (echo "gofumpt is required but not installed. Please install it from https://github.com/mvdan/gofumpt."; exit 1)
|
|
||||||
|
|
||||||
build: clean $(EXE)
|
build: clean $(EXE)
|
||||||
|
|
||||||
$(EXE):
|
$(EXE):
|
||||||
$(GO) build --tags netgo,osusergo -v -o $(EXE)
|
$(GO) build -v -o $(EXE)
|
||||||
|
|
||||||
test: check_gofumpt fmt
|
test:
|
||||||
$(GO) test -v -failfast ./lib/common/...
|
$(GO) test -failfast ./...
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(GO) clean -v
|
$(GO) clean -v
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
find . -name '*.go' -exec gofumpt -w {} \;
|
find . -name '*.go' -exec gofmt -w -s {} \;
|
||||||
|
|
||||||
info:
|
testcommon:
|
||||||
echo "GOROOT: ${GOROOT}"
|
$(GO) test -failfast ./lib/common/...
|
||||||
echo "GO: ${GO}"
|
|
||||||
echo "REPO: ${REPO}"
|
|
||||||
|
|
||||||
release:
|
|
||||||
github-release release -u go-i2p -r go-i2p -n "${RELEASE_VERSION}" -t "${RELEASE_TAG}" -d "${RELEASE_DESCRIPTION}" -p
|
|
||||||
|
|
||||||
callvis:
|
|
||||||
go-callvis -format svg -focus upgrade -group pkg,type -limit github.com/go-i2p/go-i2p github.com/go-i2p/go-i2p
|
|
||||||
|
|
||||||
godoc:
|
|
||||||
find lib -type d -exec bash -c "ls {}/*.go && godocdown -o ./{}/doc.md ./{}" \;
|
|
||||||
|
|
||||||
# Include test definitions
|
|
||||||
-include doc/tests/*.mk
|
|
||||||
|
|
||||||
# Define the all-tests target that runs every test suite
|
|
||||||
test-all: test-string-all \
|
|
||||||
test-mapping-all \
|
|
||||||
test-crypto-aes-all \
|
|
||||||
test-crypto-dsa-all \
|
|
||||||
test-crypto-ed25519-all \
|
|
||||||
test-crypto-elg-all \
|
|
||||||
test-crypto-hmac-all \
|
|
||||||
test-i2np-header-all \
|
|
||||||
test-i2np-build-request-all \
|
|
||||||
test-key-cert-all \
|
|
||||||
test-keys-cert-all \
|
|
||||||
test-lease-set-all \
|
|
||||||
test-noise-transport-all \
|
|
||||||
test-router-address-all \
|
|
||||||
test-router-info-all \
|
|
||||||
test-su3-all \
|
|
||||||
test-tunnel-all
|
|
||||||
|
|
||||||
#-include $(shell find doc/tests -type f -name '*.mk') #search for .mk files recursively
|
|
||||||
|
|
||||||
#test-base64-encode-decode-not-mangled:
|
|
||||||
#go test -v ./lib/common/base64 -run TestEncodeDecodeNotMangled
|
|
18
PASTA.md
18
PASTA.md
@ -1,18 +0,0 @@
|
|||||||
At long last... something useful
|
|
||||||
================================
|
|
||||||
|
|
||||||
It's been 2 years of me mostly not having time to work on go-i2p itself since my last update.
|
|
||||||
However, after much waiting, this library is actually **useful** for something.
|
|
||||||
It is now being used in the `reseed-tools` application to examine RouterInfos prior to including them in reseed bundles.
|
|
||||||
Routers that self-report as unreachable or congested will be excluded from future reseed bundles.
|
|
||||||
Additionally, routers that self-report an old version will be excluded from reseed bundles.
|
|
||||||
This should help new users build better connections faster with the existing, working router implementations.
|
|
||||||
|
|
||||||
This is not a working release of a go-i2p router
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
It is a numbered version of the go-i2p library, which is pre-release, expressly for use in the `reseed-tools` application.
|
|
||||||
The common library works, and so do some of the cryptographic primitives, however the API is unstable and the software itself is certain to have serious bugs outside of a few well-tested areas.
|
|
||||||
If you're using it for something other than parsing and analyzing RouterInfos and LeaseSets, you'll probably encounter bugs.
|
|
||||||
Please report them to the https://github.com/go-i2p/go-i2p
|
|
||||||
Use any part of it at your own risk.
|
|
142
README.md
142
README.md
@ -4,20 +4,35 @@ A pure Go implementation of the I2P router.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
go-i2p is in early development. The master branch is being refactored and API's are
|
go-i2p was in early development. Now it's being restructured in some
|
||||||
definitely going to change. If you choose to use any part of this code right now,
|
fundamental ways, so it's even less done than before(on this branch, for now)
|
||||||
please keep up with these changes, as they will not be backward compatible and require
|
but when this restructuring is complete, it will be a fully-fledged I2P router
|
||||||
**Fundamentally** changing any code that treats this as a dependency.
|
and library for writing, embedding, and possiblly extending I2P routers in Go
|
||||||
|
applications.
|
||||||
|
|
||||||
|
The go module is declared as: `github.com/go-i2p/go-i2p`, in order to clone
|
||||||
|
anonymously you may use `torsocks` with `go get`(YMMV) or you may clone
|
||||||
|
it from git.idk.i2p using:
|
||||||
|
|
||||||
|
#Set your $GOPATH, if it isn't set already then GOPATH=$HOME/go
|
||||||
|
$GOPATH/go/src/i2pgit.org/idk/
|
||||||
|
git clone git@127.0.0.1:idk/go-i2p $GOPATH/go/src/github.com/go-i2p/go-i2p
|
||||||
|
$GOPATH/go/src/github.com/go-i2p/go-i2p
|
||||||
|
|
||||||
|
And build with `GO111MODULES=off` or use a `replace` directive in your `go.mod`
|
||||||
|
to direct to the local module source. Or you may run your own Go Modules proxy as
|
||||||
|
a hidden service. I'll make this about a billion times easier in the near future I
|
||||||
|
promise.
|
||||||
|
|
||||||
### Implemented Features
|
### Implemented Features
|
||||||
|
|
||||||
- Clients
|
As the application is restructured and moved away from representing I2P data
|
||||||
- [ ] Datagrams
|
structures as byte slices, this chart will be filled in, when the tests pass,
|
||||||
- [ ] I2CP
|
the item will be checked off. Currently, much of this is partially implemented
|
||||||
- [ ] Message routing
|
in byte-slice versions and partially implemented as Go Structs. Very little of
|
||||||
- [ ] SAM
|
it will work until it's all moved to Go Structs where appropriate. Most of
|
||||||
- [ ] Streaming
|
this will happen in /lib/common.
|
||||||
- [ ] Tunnel Manager
|
|
||||||
- Cryptographic primitives
|
- Cryptographic primitives
|
||||||
- Signing
|
- Signing
|
||||||
- [ ] ECDSA_SHA256_P256
|
- [ ] ECDSA_SHA256_P256
|
||||||
@ -33,85 +48,62 @@ please keep up with these changes, as they will not be backward compatible and r
|
|||||||
- [ ] RSA_SHA384_3072
|
- [ ] RSA_SHA384_3072
|
||||||
- [ ] RSA_SHA512_4096
|
- [ ] RSA_SHA512_4096
|
||||||
- [ ] Ed25519
|
- [ ] Ed25519
|
||||||
- [ ] Red25519
|
- [x] ElGamal
|
||||||
- [ ] ElGamal
|
- [x] AES256
|
||||||
- [ ] AES256
|
- Common Structures
|
||||||
- [ ] X25519
|
- Common Type Specification
|
||||||
- [ ] ChaCha20/Poly1305
|
- [x] Integer
|
||||||
- [ ] Elligator2
|
- [x] Date
|
||||||
- [ ] HKDF
|
- [x] String
|
||||||
- [ ] HMAC
|
- [x] PublicKey* As interface in lib/crypto
|
||||||
- [/] Noise subsystem
|
- [x] PrivateKey* As interface in lib/crypto
|
||||||
- End-to-End Crypto
|
- [ ] SessionKey
|
||||||
- [ ] Garlic messages
|
- [ ] SigningPublicKey
|
||||||
- [ ] ElGamal/AES+SessionTag
|
- [ ] Signature
|
||||||
- [ ] Ratchet/X25519
|
- [x] Hash
|
||||||
|
- [ ] Session Tag
|
||||||
|
- [ ] Tunnel ID
|
||||||
|
- [x] Certificate
|
||||||
|
- [ ] Mapping
|
||||||
|
- Common Structure Specification
|
||||||
|
- [ ] KeysAndCert
|
||||||
|
- [ ] RouterIdentity
|
||||||
|
- [ ] Destination
|
||||||
|
- [ ] Lease
|
||||||
|
- [ ] LeaseSet
|
||||||
|
- [ ] Lease2
|
||||||
|
- [ ] OfflineSigntature
|
||||||
|
- [ ] LeaseSet2Header
|
||||||
|
- [ ] LeaseSet2
|
||||||
|
- [ ] MetaLease
|
||||||
|
- [ ] MetaLeaseSet
|
||||||
|
- [ ] EncryptedLeaseSet
|
||||||
|
- [ ] RouterAddress
|
||||||
|
- [ ] RouterInfo
|
||||||
- I2NP
|
- I2NP
|
||||||
- [ ] Message parsing
|
- [ ] Message parsing
|
||||||
- [ ] Message handling
|
- [ ] Message handling
|
||||||
- NetDB
|
- NetDB
|
||||||
- [/] Local storage
|
- [ ] Local storage
|
||||||
- [/] Persistence to disk
|
- [ ] Persistence to disk
|
||||||
- [X] Reseeding
|
- [ ] Reseeding
|
||||||
- [ ] Lookups
|
- [ ] Lookups
|
||||||
- [ ] Expiry
|
- [ ] Expiry
|
||||||
- [ ] Exploration
|
- [ ] Exploration
|
||||||
- [ ] Publishing
|
- [ ] Publishing
|
||||||
- [ ] Floodfill
|
- [ ] Floodfill
|
||||||
- [ ] LS2 and Encrypted Leasesets
|
|
||||||
- Transports
|
- Transports
|
||||||
- [X] Transport manager
|
- [ ] Transport manager
|
||||||
|
- NTCP
|
||||||
|
- [ ] Handshake
|
||||||
|
- [ ] Session tracking
|
||||||
|
- [ ] Automatic session creation
|
||||||
- NTCP2
|
- NTCP2
|
||||||
- [ ] Handshake
|
- [ ] Handshake
|
||||||
- [ ] Session tracking
|
- [ ] Session tracking
|
||||||
- [ ] Automatic session creation
|
- [ ] Automatic session creation
|
||||||
- SSU2
|
- [ ] SSU
|
||||||
- [ ] Handshake
|
|
||||||
- [ ] Session tracking
|
|
||||||
- [ ] Automatic session creation
|
|
||||||
- [ ] Peer Tests
|
|
||||||
- [ ] Introducers
|
|
||||||
- Tunnels
|
|
||||||
- [ ] Building
|
|
||||||
- [ ] Build Message Crypto (ElGamal)
|
|
||||||
- [ ] Build Message Crypto (ECIES)
|
|
||||||
- [ ] Participating
|
|
||||||
- [ ] Tunnel Message Crypto
|
|
||||||
- [ ] Tunnel Message Fragmentation/Reassembly
|
|
||||||
- Common Data Structures
|
|
||||||
- [X] Keys and Cert
|
|
||||||
- [X] Key Certificates
|
|
||||||
- [X] Certificate
|
|
||||||
- [X] Lease
|
|
||||||
- [X] Lease Set
|
|
||||||
- [X] Router Info
|
|
||||||
- [X] Router Identity
|
|
||||||
- [X] Router Address
|
|
||||||
- [X] Session Key
|
|
||||||
- [X] Signature Types
|
|
||||||
- [X] Destination
|
|
||||||
- [X] Data Types
|
|
||||||
- [X] Session Tag
|
|
||||||
|
|
||||||
## Verbosity ##
|
|
||||||
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
|
|
||||||
|
|
||||||
There are three available log levels:
|
|
||||||
|
|
||||||
- Debug
|
|
||||||
```shell
|
|
||||||
export DEBUG_I2P=debug
|
|
||||||
```
|
|
||||||
- Warn
|
|
||||||
```shell
|
|
||||||
export DEBUG_I2P=warn
|
|
||||||
```
|
|
||||||
- Error
|
|
||||||
```shell
|
|
||||||
export DEBUG_I2P=error
|
|
||||||
```
|
|
||||||
|
|
||||||
If I2P_DEBUG is set to an unrecognized variable, it will fall back to "debug".
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
test-crypto-aes-all: test-crypto-aes-core test-crypto-aes-validation test-crypto-aes-padding
|
|
||||||
|
|
||||||
test-crypto-aes-core:
|
|
||||||
go test -v ./lib/crypto -run TestAESEncryptDecrypt
|
|
||||||
|
|
||||||
test-crypto-aes-validation:
|
|
||||||
go test -v ./lib/crypto -run TestAESEncryptInvalidKey
|
|
||||||
go test -v ./lib/crypto -run TestAESDecryptInvalidInput
|
|
||||||
|
|
||||||
test-crypto-aes-padding:
|
|
||||||
go test -v ./lib/crypto -run TestPKCS7PadUnpad
|
|
||||||
go test -v ./lib/crypto -run TestPKCS7UnpadInvalidInput
|
|
||||||
|
|
||||||
.PHONY: test-crypto-aes-all \
|
|
||||||
test-crypto-aes-core \
|
|
||||||
test-crypto-aes-validation \
|
|
||||||
test-crypto-aes-padding
|
|
@ -1,4 +0,0 @@
|
|||||||
test-base32-encode-decode-not-mangled:
|
|
||||||
go test -v ./lib/common/base32 -run TestEncodeDecodeNotMangled
|
|
||||||
|
|
||||||
.PHONY: test-base32-encode-decode-not-mangled
|
|
@ -1,4 +0,0 @@
|
|||||||
test-base64-encode-decode-not-mangled:
|
|
||||||
go test -v ./lib/common/base64 -run TestEncodeDecodeNotMangled
|
|
||||||
|
|
||||||
.PHONY: test-base64-encode-decode-not-mangled
|
|
@ -1,24 +0,0 @@
|
|||||||
test-build-request-all: test-build-request-receive test-build-request-ident test-build-request-components
|
|
||||||
|
|
||||||
test-build-request-receive:
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordReceiveTunnel
|
|
||||||
|
|
||||||
test-build-request-ident:
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordOurIdent
|
|
||||||
|
|
||||||
test-build-request-components:
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordNextTunnel
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordNextIdent
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordLayerKey
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordIVKey
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordReplyKey
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordReplyIV
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordFlag
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordRequestTime
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordSendMessageID
|
|
||||||
go test -v ./lib/i2np -run TestReadBuildRequestRecordPadding
|
|
||||||
|
|
||||||
.PHONY: test-build-request-all \
|
|
||||||
test-build-request-receive \
|
|
||||||
test-build-request-ident \
|
|
||||||
test-build-request-components
|
|
@ -1,61 +0,0 @@
|
|||||||
|
|
||||||
test-cert-all: test-cert-type test-cert-length test-cert-data test-cert-read test-cert-length-correct test-cert-length-too-short test-cert-length-data-short test-cert-data-correct test-cert-data-too-long test-cert-data-too-short test-cert-read-correct test-cert-read-short test-cert-read-remainder test-cert-read-invalid
|
|
||||||
|
|
||||||
test-cert-type:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateTypeIsFirstByte
|
|
||||||
|
|
||||||
test-cert-length:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateLength
|
|
||||||
|
|
||||||
test-cert-data:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateData
|
|
||||||
|
|
||||||
test-cert-read:
|
|
||||||
go test -v ./lib/common/certificate -run TestReadCertificate
|
|
||||||
|
|
||||||
test-cert-length-correct:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateLengthCorrect
|
|
||||||
|
|
||||||
test-cert-length-too-short:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateLengthErrWhenTooShort
|
|
||||||
|
|
||||||
test-cert-length-data-short:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateLengthErrWhenDataTooShort
|
|
||||||
|
|
||||||
test-cert-data-correct:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateDataWhenCorrectSize
|
|
||||||
|
|
||||||
test-cert-data-too-long:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateDataWhenTooLong
|
|
||||||
|
|
||||||
test-cert-data-too-short:
|
|
||||||
go test -v ./lib/common/certificate -run TestCertificateDataWhenTooShort
|
|
||||||
|
|
||||||
test-cert-read-correct:
|
|
||||||
go test -v ./lib/common/certificate -run TestReadCertificateWithCorrectData
|
|
||||||
|
|
||||||
test-cert-read-short:
|
|
||||||
go test -v ./lib/common/certificate -run TestReadCertificateWithDataTooShort
|
|
||||||
|
|
||||||
test-cert-read-remainder:
|
|
||||||
go test -v ./lib/common/certificate -run TestReadCertificateWithRemainder
|
|
||||||
|
|
||||||
test-cert-read-invalid:
|
|
||||||
go test -v ./lib/common/certificate -run TestReadCertificateWithInvalidLength
|
|
||||||
|
|
||||||
# Declare all targets as PHONY
|
|
||||||
.PHONY: test-cert-all \
|
|
||||||
test-cert-type \
|
|
||||||
test-cert-length \
|
|
||||||
test-cert-data \
|
|
||||||
test-cert-read \
|
|
||||||
test-cert-length-correct \
|
|
||||||
test-cert-length-too-short \
|
|
||||||
test-cert-length-data-short \
|
|
||||||
test-cert-data-correct \
|
|
||||||
test-cert-data-too-long \
|
|
||||||
test-cert-data-too-short \
|
|
||||||
test-cert-read-correct \
|
|
||||||
test-cert-read-short \
|
|
||||||
test-cert-read-remainder \
|
|
||||||
test-cert-read-invalid
|
|
@ -1,2 +0,0 @@
|
|||||||
test-date-time-from-milliseconds:
|
|
||||||
go test -v ./lib/common/data -run TestTimeFromMilliseconds
|
|
@ -1,20 +0,0 @@
|
|||||||
test-crypto-dsa-all: test-crypto-dsa test-crypto-dsa-benchmarks
|
|
||||||
|
|
||||||
test-crypto-dsa:
|
|
||||||
go test -v ./lib/crypto -run TestDSA
|
|
||||||
|
|
||||||
test-crypto-dsa-benchmarks:
|
|
||||||
go test -v ./lib/crypto -bench=DSA -run=^$
|
|
||||||
|
|
||||||
# Individual benchmarks
|
|
||||||
test-crypto-dsa-bench-generate:
|
|
||||||
go test -v ./lib/crypto -bench=DSAGenerate -run=^$
|
|
||||||
|
|
||||||
test-crypto-dsa-bench-sign-verify:
|
|
||||||
go test -v ./lib/crypto -bench=DSASignVerify -run=^$
|
|
||||||
|
|
||||||
.PHONY: test-crypto-dsa-all \
|
|
||||||
test-crypto-dsa \
|
|
||||||
test-crypto-dsa-benchmarks \
|
|
||||||
test-crypto-dsa-bench-generate \
|
|
||||||
test-crypto-dsa-bench-sign-verify
|
|
@ -1,7 +0,0 @@
|
|||||||
test-crypto-ed25519-all: test-crypto-ed25519
|
|
||||||
|
|
||||||
test-crypto-ed25519:
|
|
||||||
go test -v ./lib/crypto -run TestEd25519
|
|
||||||
|
|
||||||
.PHONY: test-crypto-ed25519-all \
|
|
||||||
test-crypto-ed25519
|
|
@ -1,24 +0,0 @@
|
|||||||
test-crypto-elg-all: test-crypto-elg test-crypto-elg-benchmarks
|
|
||||||
|
|
||||||
test-crypto-elg:
|
|
||||||
go test -v ./lib/crypto -run TestElg
|
|
||||||
|
|
||||||
test-crypto-elg-benchmarks:
|
|
||||||
go test -v ./lib/crypto -bench=Elg -run=^$
|
|
||||||
|
|
||||||
# Individual benchmarks
|
|
||||||
test-crypto-elg-bench-generate:
|
|
||||||
go test -v ./lib/crypto -bench=ElgGenerate -run=^$
|
|
||||||
|
|
||||||
test-crypto-elg-bench-encrypt:
|
|
||||||
go test -v ./lib/crypto -bench=ElgEncrypt -run=^$
|
|
||||||
|
|
||||||
test-crypto-elg-bench-decrypt:
|
|
||||||
go test -v ./lib/crypto -bench=ElgDecrypt -run=^$
|
|
||||||
|
|
||||||
.PHONY: test-crypto-elg-all \
|
|
||||||
test-crypto-elg \
|
|
||||||
test-crypto-elg-benchmarks \
|
|
||||||
test-crypto-elg-bench-generate \
|
|
||||||
test-crypto-elg-bench-encrypt \
|
|
||||||
test-crypto-elg-bench-decrypt
|
|
@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
test-i2np-header-all: test-i2np-type test-i2np-message test-i2np-expiration test-i2np-ntcp-components test-i2np-data test-i2np-regression
|
|
||||||
|
|
||||||
test-i2np-type:
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPTypeWith
|
|
||||||
|
|
||||||
test-i2np-message:
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPNTCPMessageID
|
|
||||||
|
|
||||||
test-i2np-expiration:
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPNTCPMessageExpiration
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPSSUMessageExpiration
|
|
||||||
|
|
||||||
test-i2np-ntcp-components:
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPNTCPMessageSize
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPNTCPMessageChecksum
|
|
||||||
|
|
||||||
test-i2np-data:
|
|
||||||
go test -v ./lib/i2np -run TestReadI2NPNTCPData
|
|
||||||
|
|
||||||
test-i2np-regression:
|
|
||||||
go test -v ./lib/i2np -run TestCrasherRegression123781
|
|
||||||
|
|
||||||
.PHONY: test-i2np-header-all \
|
|
||||||
test-i2np-type \
|
|
||||||
test-i2np-message \
|
|
||||||
test-i2np-expiration \
|
|
||||||
test-i2np-ntcp-components \
|
|
||||||
test-i2np-data \
|
|
||||||
test-i2np-regression
|
|
@ -1,7 +0,0 @@
|
|||||||
test-crypto-hmac-all: test-crypto-hmac
|
|
||||||
|
|
||||||
test-crypto-hmac:
|
|
||||||
go test -v ./lib/crypto -run Test_I2PHMAC
|
|
||||||
|
|
||||||
.PHONY: test-crypto-hmac-all \
|
|
||||||
test-crypto-hmac
|
|
@ -1,15 +0,0 @@
|
|||||||
test-integer-all: test-integer-big-endian test-integer-one-byte test-integer-zero
|
|
||||||
|
|
||||||
test-integer-big-endian:
|
|
||||||
go test -v ./lib/common/integer -run TestIntegerBigEndian
|
|
||||||
|
|
||||||
test-integer-one-byte:
|
|
||||||
go test -v ./lib/common/integer -run TestWorksWithOneByte
|
|
||||||
|
|
||||||
test-integer-zero:
|
|
||||||
go test -v ./lib/common/integer -run TestIsZeroWithNoData
|
|
||||||
|
|
||||||
.PHONY: test-integer-all \
|
|
||||||
test-integer-big-endian \
|
|
||||||
test-integer-one-byte \
|
|
||||||
test-integer-zero
|
|
@ -1,23 +0,0 @@
|
|||||||
test-key-cert-all: test-key-cert-signing test-key-cert-public test-key-cert-construct
|
|
||||||
|
|
||||||
test-key-cert-signing:
|
|
||||||
go test -v ./lib/common/key_certificate -run TestSingingPublicKeyTypeReturnsCorrectInteger
|
|
||||||
go test -v ./lib/common/key_certificate -run TestSingingPublicKeyTypeReportsWhenDataTooSmall
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructSigningPublicKeyReportsWhenDataTooSmall
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructSigningPublicKeyWithDSASHA1
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructSigningPublicKeyWithP256
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructSigningPublicKeyWithP384
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructSigningPublicKeyWithP521
|
|
||||||
|
|
||||||
test-key-cert-public:
|
|
||||||
go test -v ./lib/common/key_certificate -run TestPublicKeyTypeReturnsCorrectInteger
|
|
||||||
go test -v ./lib/common/key_certificate -run TestPublicKeyTypeReportsWhenDataTooSmall
|
|
||||||
|
|
||||||
test-key-cert-construct:
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructPublicKeyReportsWhenDataTooSmall
|
|
||||||
go test -v ./lib/common/key_certificate -run TestConstructPublicKeyReturnsCorrectDataWithElg
|
|
||||||
|
|
||||||
.PHONY: test-key-cert-all \
|
|
||||||
test-key-cert-signing \
|
|
||||||
test-key-cert-public \
|
|
||||||
test-key-cert-construct
|
|
@ -1,30 +0,0 @@
|
|||||||
test-keys-cert-all: test-keys-cert-certificate test-keys-cert-public test-keys-cert-signing test-keys-cert-creation
|
|
||||||
|
|
||||||
test-keys-cert-certificate:
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestCertificateWithValidData
|
|
||||||
|
|
||||||
test-keys-cert-public:
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestPublicKeyWithBadData
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestPublicKeyWithBadCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestPublicKeyWithNullCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestPublicKeyWithKeyCertificate
|
|
||||||
|
|
||||||
test-keys-cert-signing:
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestSigningPublicKeyWithBadData
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestSigningPublicKeyWithBadCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestSigningPublicKeyWithNullCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestSigningPublicKeyWithKeyCertificate
|
|
||||||
|
|
||||||
test-keys-cert-creation:
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithMissingData
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithMissingCertData
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithValidDataWithCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithValidDataWithoutCertificate
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithValidDataWithCertificateAndRemainder
|
|
||||||
go test -v ./lib/common/keys_and_cert -run TestNewKeysAndCertWithValidDataWithoutCertificateAndRemainder
|
|
||||||
|
|
||||||
.PHONY: test-keys-cert-all \
|
|
||||||
test-keys-cert-certificate \
|
|
||||||
test-keys-cert-public \
|
|
||||||
test-keys-cert-signing \
|
|
||||||
test-keys-cert-creation
|
|
@ -1,22 +0,0 @@
|
|||||||
test-lease-set-all: test-lease-set-basic test-lease-set-leases test-lease-set-expiration
|
|
||||||
|
|
||||||
test-lease-set-basic:
|
|
||||||
go test -v ./lib/common/lease_set -run TestDestinationIsCorrect
|
|
||||||
go test -v ./lib/common/lease_set -run TestPublicKeyIsCorrect
|
|
||||||
go test -v ./lib/common/lease_set -run TestSigningKeyIsCorrect
|
|
||||||
go test -v ./lib/common/lease_set -run TestSignatureIsCorrect
|
|
||||||
|
|
||||||
test-lease-set-leases:
|
|
||||||
go test -v ./lib/common/lease_set -run TestLeaseCountCorrect
|
|
||||||
go test -v ./lib/common/lease_set -run TestLeaseCountCorrectWithMultiple
|
|
||||||
go test -v ./lib/common/lease_set -run TestLeaseCountErrorWithTooMany
|
|
||||||
go test -v ./lib/common/lease_set -run TestLeasesHaveCorrectData
|
|
||||||
|
|
||||||
test-lease-set-expiration:
|
|
||||||
go test -v ./lib/common/lease_set -run TestNewestExpirationIsCorrect
|
|
||||||
go test -v ./lib/common/lease_set -run TestOldestExpirationIsCorrect
|
|
||||||
|
|
||||||
.PHONY: test-lease-set-all \
|
|
||||||
test-lease-set-basic \
|
|
||||||
test-lease-set-leases \
|
|
||||||
test-lease-set-expiration
|
|
@ -1,28 +0,0 @@
|
|||||||
test-mapping-all: test-mapping-values test-mapping-duplicates test-mapping-conversion test-mapping-utils
|
|
||||||
|
|
||||||
test-mapping-values:
|
|
||||||
go test -v ./lib/common/data -run TestValuesExclusesPairWithBadData
|
|
||||||
go test -v ./lib/common/data -run TestValuesWarnsMissingData
|
|
||||||
go test -v ./lib/common/data -run TestValuesWarnsExtraData
|
|
||||||
go test -v ./lib/common/data -run TestValuesEnforcesEqualDelimitor
|
|
||||||
go test -v ./lib/common/data -run TestValuesEnforcedSemicolonDelimitor
|
|
||||||
go test -v ./lib/common/data -run TestValuesReturnsValues
|
|
||||||
|
|
||||||
test-mapping-duplicates:
|
|
||||||
go test -v ./lib/common/data -run TestHasDuplicateKeysTrueWhenDuplicates
|
|
||||||
go test -v ./lib/common/data -run TestHasDuplicateKeysFalseWithoutDuplicates
|
|
||||||
go test -v ./lib/common/data -run TestReadMappingHasDuplicateKeys
|
|
||||||
|
|
||||||
test-mapping-conversion:
|
|
||||||
go test -v ./lib/common/data -run TestGoMapToMappingProducesCorrectMapping
|
|
||||||
go test -v ./lib/common/data -run TestFullGoMapToMappingProducesCorrectMapping
|
|
||||||
|
|
||||||
test-mapping-utils:
|
|
||||||
go test -v ./lib/common/data -run TestStopValueRead
|
|
||||||
go test -v ./lib/common/data -run TestBeginsWith
|
|
||||||
|
|
||||||
.PHONY: test-mapping-all \
|
|
||||||
test-mapping-values \
|
|
||||||
test-mapping-duplicates \
|
|
||||||
test-mapping-conversion \
|
|
||||||
test-mapping-utils
|
|
@ -1,2 +0,0 @@
|
|||||||
test-mapping-values-order:
|
|
||||||
go test -v ./lib/common/data -run TestMappingOrderSortsValuesThenKeys
|
|
@ -1,11 +0,0 @@
|
|||||||
test-noise-transport-all: test-noise-packet-encryption test-noise-transport-connection
|
|
||||||
|
|
||||||
test-noise-packet-encryption:
|
|
||||||
go test -v ./lib/transport/noise -run TestEncryptDecryptPacketOffline
|
|
||||||
|
|
||||||
test-noise-transport-connection:
|
|
||||||
go test -v ./lib/transport/noise -run TestTransport
|
|
||||||
|
|
||||||
.PHONY: test-noise-transport-all \
|
|
||||||
test-noise-packet-encryption \
|
|
||||||
test-noise-transport-connection
|
|
@ -1,19 +0,0 @@
|
|||||||
test-router-address-all: test-router-address-validation test-router-address-functionality test-router-address-fuzz
|
|
||||||
|
|
||||||
test-router-address-validation:
|
|
||||||
go test -v ./lib/common/router_address -run TestCheckValidReportsEmptySlice
|
|
||||||
go test -v ./lib/common/router_address -run TestCheckRouterAddressValidReportsDataMissing
|
|
||||||
go test -v ./lib/common/router_address -run TestCheckRouterAddressValidNoErrWithValidData
|
|
||||||
|
|
||||||
test-router-address-functionality:
|
|
||||||
go test -v ./lib/common/router_address -run TestRouterAddressCostReturnsFirstByte
|
|
||||||
go test -v ./lib/common/router_address -run TestRouterAddressExpirationReturnsCorrectData
|
|
||||||
go test -v ./lib/common/router_address -run TestReadRouterAddressReturnsCorrectRemainderWithoutError
|
|
||||||
|
|
||||||
test-router-address-fuzz:
|
|
||||||
go test -v ./lib/common/router_address -run TestCorrectsFuzzCrasher1
|
|
||||||
|
|
||||||
.PHONY: test-router-address-all \
|
|
||||||
test-router-address-validation \
|
|
||||||
test-router-address-functionality \
|
|
||||||
test-router-address-fuzz
|
|
@ -1,26 +0,0 @@
|
|||||||
test-router-info-all: test-router-info-published test-router-info-addresses test-router-info-identity test-router-info-misc
|
|
||||||
|
|
||||||
test-router-info-published:
|
|
||||||
go test -v ./lib/common/router_info -run TestPublishedReturnsCorrectDate
|
|
||||||
go test -v ./lib/common/router_info -run TestPublishedReturnsCorrectErrorWithPartialDate
|
|
||||||
go test -v ./lib/common/router_info -run TestPublishedReturnsCorrectErrorWithInvalidData
|
|
||||||
|
|
||||||
test-router-info-addresses:
|
|
||||||
go test -v ./lib/common/router_info -run TestRouterAddressCountReturnsCorrectCount
|
|
||||||
go test -v ./lib/common/router_info -run TestRouterAddressCountReturnsCorrectErrorWithInvalidData
|
|
||||||
go test -v ./lib/common/router_info -run TestRouterAddressesReturnsAddresses
|
|
||||||
go test -v ./lib/common/router_info -run TestRouterAddressesReturnsAddressesWithMultiple
|
|
||||||
|
|
||||||
test-router-info-identity:
|
|
||||||
go test -v ./lib/common/router_info -run TestRouterIdentityIsCorrect
|
|
||||||
|
|
||||||
test-router-info-misc:
|
|
||||||
go test -v ./lib/common/router_info -run TestPeerSizeIsZero
|
|
||||||
go test -v ./lib/common/router_info -run TestOptionsAreCorrect
|
|
||||||
go test -v ./lib/common/router_info -run TestSignatureIsCorrectSize
|
|
||||||
|
|
||||||
.PHONY: test-router-info-all \
|
|
||||||
test-router-info-published \
|
|
||||||
test-router-info-addresses \
|
|
||||||
test-router-info-identity \
|
|
||||||
test-router-info-misc
|
|
@ -1,27 +0,0 @@
|
|||||||
test-string-all: test-string-length test-string-data test-string-conversion test-string-read
|
|
||||||
|
|
||||||
test-string-length:
|
|
||||||
go test -v ./lib/common/data -run TestStringReportsCorrectLength
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringReportsLengthZeroError
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringReportsExtraDataError
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringDataReportsLengthZeroError
|
|
||||||
|
|
||||||
test-string-data:
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringDataReportsExtraDataError
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringDataEmptyWhenZeroLength
|
|
||||||
go test -v ./lib/common/data -run TestI2PStringDataErrorWhenNonZeroLengthOnly
|
|
||||||
|
|
||||||
test-string-conversion:
|
|
||||||
go test -v ./lib/common/data -run TestToI2PI2PStringFormatsCorrectly
|
|
||||||
go test -v ./lib/common/data -run TestToI2PStringReportsOverflows
|
|
||||||
|
|
||||||
test-string-read:
|
|
||||||
go test -v ./lib/common/data -run TestReadStringReadsLength
|
|
||||||
go test -v ./lib/common/data -run TestReadI2PStringErrWhenEmptySlice
|
|
||||||
go test -v ./lib/common/data -run TestReadI2PStringErrWhenDataTooShort
|
|
||||||
|
|
||||||
.PHONY: test-string-all \
|
|
||||||
test-string-length \
|
|
||||||
test-string-data \
|
|
||||||
test-string-conversion \
|
|
||||||
test-string-read
|
|
@ -1,11 +0,0 @@
|
|||||||
test-su3-all: test-su3-read test-su3-signature
|
|
||||||
|
|
||||||
test-su3-read:
|
|
||||||
go test -v ./lib/su3 -run TestRead
|
|
||||||
|
|
||||||
test-su3-signature:
|
|
||||||
go test -v ./lib/su3 -run TestReadSignatureFirst
|
|
||||||
|
|
||||||
.PHONY: test-su3-all \
|
|
||||||
test-su3-read \
|
|
||||||
test-su3-signature
|
|
@ -1,22 +0,0 @@
|
|||||||
test-tunnel-all: test-tunnel-delivery-instructions test-tunnel-message
|
|
||||||
|
|
||||||
# Tests from delivery_test.go
|
|
||||||
test-tunnel-delivery-instructions:
|
|
||||||
go test -v ./lib/tunnel -run TestReadDeliveryInstructions
|
|
||||||
|
|
||||||
# Tests from message_test.go
|
|
||||||
test-tunnel-message: test-tunnel-message-padding test-tunnel-message-fragments
|
|
||||||
|
|
||||||
test-tunnel-message-padding:
|
|
||||||
go test -v ./lib/tunnel -run TestDeliveryInstructionDataWithNoPadding
|
|
||||||
go test -v ./lib/tunnel -run TestDeliveryInstructionDataWithSomePadding
|
|
||||||
go test -v ./lib/tunnel -run TestDeliveryInstructionDataWithOnlyPadding
|
|
||||||
|
|
||||||
test-tunnel-message-fragments:
|
|
||||||
go test -v ./lib/tunnel -run TestDeliveryInstructionsWithFragments
|
|
||||||
|
|
||||||
.PHONY: test-tunnel-all \
|
|
||||||
test-tunnel-delivery-instructions \
|
|
||||||
test-tunnel-message \
|
|
||||||
test-tunnel-message-padding \
|
|
||||||
test-tunnel-message-fragments
|
|
20
go.mod
20
go.mod
@ -1,21 +1,9 @@
|
|||||||
module github.com/go-i2p/go-i2p
|
module github.com/go-i2p/go-i2p
|
||||||
|
|
||||||
go 1.23.1
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/emirpasic/gods v1.18.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/flynn/noise v1.1.0
|
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
|
||||||
github.com/sirupsen/logrus v1.9.3
|
|
||||||
github.com/stretchr/testify v1.9.0
|
|
||||||
go.step.sm/crypto v0.53.0
|
|
||||||
golang.org/x/crypto v0.27.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
39
go.sum
39
go.sum
@ -1,43 +1,24 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
|
||||||
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e h1:NMjWYVkgcQHGOy0/VxU0TU6smrcoxzj9hwDesx2sB0w=
|
|
||||||
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e/go.mod h1:fKfFM3BsOOyjtZmEty7FsGzGabXo8Eb/dHjyIhTtxsE=
|
|
||||||
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
|
||||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
go.step.sm/crypto v0.53.0 h1:+1as1ogzuCzx15/468M4mEC5juogI5a0Fzbsyh1CuYY=
|
|
||||||
go.step.sm/crypto v0.53.0/go.mod h1:AqLU78RqNUHepLzyOWZuNN/2++Lu7dZENdO9UzWOGSk=
|
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/router_info"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
// interface defining a way to bootstrap into the i2p network
|
// interface defining a way to bootstrap into the i2p network
|
||||||
type Bootstrap interface {
|
type Bootstrap interface {
|
||||||
@ -9,5 +9,5 @@ type Bootstrap interface {
|
|||||||
// if n is 0 then try obtaining as many router infos as possible
|
// if n is 0 then try obtaining as many router infos as possible
|
||||||
// returns nil and error if we cannot fetch ANY router infos
|
// returns nil and error if we cannot fetch ANY router infos
|
||||||
// returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use
|
// returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use
|
||||||
GetPeers(n int) (chan []router_info.RouterInfo, error)
|
GetPeers(n int) (chan []common.RouterInfo, error)
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
|
//
|
||||||
// provides generic interfaces for initial bootstrap into network and network reseeding
|
// provides generic interfaces for initial bootstrap into network and network reseeding
|
||||||
|
//
|
||||||
package bootstrap
|
package bootstrap
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
# bootstrap
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/bootstrap"
|
|
||||||
|
|
||||||
provides generic interfaces for initial bootstrap into network and network
|
|
||||||
### reseeding
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### type Bootstrap
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Bootstrap interface {
|
|
||||||
// get more peers for bootstrap
|
|
||||||
// try obtaining at most n router infos
|
|
||||||
// if n is 0 then try obtaining as many router infos as possible
|
|
||||||
// returns nil and error if we cannot fetch ANY router infos
|
|
||||||
// returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use
|
|
||||||
GetPeers(n int) (chan []router_info.RouterInfo, error)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
interface defining a way to bootstrap into the i2p network
|
|
@ -1,23 +1,18 @@
|
|||||||
// Package base32 implmenets utilities for encoding and decoding text using I2P's alphabet
|
//
|
||||||
|
// base32 encoding using I2P's alphabet
|
||||||
|
//
|
||||||
package base32
|
package base32
|
||||||
|
|
||||||
import (
|
import (
|
||||||
b32 "encoding/base32"
|
b32 "encoding/base32"
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2PEncodeAlphabet is the base32 encoding used throughout I2P.
|
var I2PEncoding *b32.Encoding = b32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
|
||||||
// RFC 3548 using lowercase characters.
|
|
||||||
const I2PEncodeAlphabet = "abcdefghijklmnopqrstuvwxyz234567"
|
|
||||||
|
|
||||||
// I2PEncoding is the standard base32 encoding used through I2P.
|
//
|
||||||
var I2PEncoding *b32.Encoding = b32.NewEncoding(I2PEncodeAlphabet)
|
// Return a go string of the I2P base32
|
||||||
|
// encoding of the provided byte slice
|
||||||
// EncodeToString encodes []byte to a base32 string using I2PEncoding
|
//
|
||||||
func EncodeToString(data []byte) string {
|
func EncodeToString(data []byte) string {
|
||||||
return I2PEncoding.EncodeToString(data)
|
return I2PEncoding.EncodeToString(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeString decodes base64 string to []byte I2PEncoding
|
|
||||||
func DecodeString(data string) ([]byte, error) {
|
|
||||||
return I2PEncoding.DecodeString(data)
|
|
||||||
}
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package base32
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeDecodeNotMangled(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
// Random pangram
|
|
||||||
testInput := []byte("How vexingly quick daft zebras jump!")
|
|
||||||
|
|
||||||
encodedString := EncodeToString(testInput)
|
|
||||||
decodedString, err := DecodeString(encodedString)
|
|
||||||
assert.Nil(err)
|
|
||||||
|
|
||||||
assert.ElementsMatch(testInput, decodedString)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
# base32
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/base32"
|
|
||||||
|
|
||||||
Package base32 implmenets utilities for encoding and decoding text using I2P's
|
|
||||||
### alphabet
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const I2PEncodeAlphabet = "abcdefghijklmnopqrstuvwxyz234567"
|
|
||||||
```
|
|
||||||
I2PEncodeAlphabet is the base32 encoding used throughout I2P. RFC 3548 using
|
|
||||||
lowercase characters.
|
|
||||||
|
|
||||||
```go
|
|
||||||
var I2PEncoding *b32.Encoding = b32.NewEncoding(I2PEncodeAlphabet)
|
|
||||||
```
|
|
||||||
I2PEncoding is the standard base32 encoding used through I2P.
|
|
||||||
|
|
||||||
#### func DecodeString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func DecodeString(data string) ([]byte, error)
|
|
||||||
```
|
|
||||||
DecodeString decodes base64 string to []byte I2PEncoding
|
|
||||||
|
|
||||||
#### func EncodeToString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func EncodeToString(data []byte) string
|
|
||||||
```
|
|
||||||
EncodeToString encodes []byte to a base32 string using I2PEncoding
|
|
@ -1,23 +1,30 @@
|
|||||||
// Package base64 implmenets utilities for encoding and decoding text using I2P's alphabet
|
//
|
||||||
|
// base64 encoding using I2P's alphabet
|
||||||
|
//
|
||||||
package base64
|
package base64
|
||||||
|
|
||||||
import (
|
import (
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2PEncodeAlphabet is the base64 encoding used throughout I2P.
|
// i2p base64 alphabet
|
||||||
// RFC 4648 with "/"" replaced with "~", and "+" replaced with "-".
|
const Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
|
||||||
const I2PEncodeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
|
|
||||||
|
|
||||||
// I2PEncoding is the standard base64 encoding used through I2P.
|
// i2p base64 encoding
|
||||||
var I2PEncoding *b64.Encoding = b64.NewEncoding(I2PEncodeAlphabet)
|
var I2PEncoding *b64.Encoding = b64.NewEncoding(Alphabet)
|
||||||
|
|
||||||
// I2PEncoding is the standard base64 encoding used through I2P.
|
//
|
||||||
|
// Return a go string of the I2P base64
|
||||||
|
// encoding of the provided byte slice
|
||||||
|
//
|
||||||
func EncodeToString(data []byte) string {
|
func EncodeToString(data []byte) string {
|
||||||
return I2PEncoding.EncodeToString(data)
|
return I2PEncoding.EncodeToString(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeString decodes base64 string to []byte I2PEncoding
|
//
|
||||||
func DecodeString(str string) ([]byte, error) {
|
// decode string using i2p base64 encoding
|
||||||
|
// returns error if data is malfromed
|
||||||
|
//
|
||||||
|
func DecodeFromString(str string) (d []byte, err error) {
|
||||||
return I2PEncoding.DecodeString(str)
|
return I2PEncoding.DecodeString(str)
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package base64
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeDecodeNotMangled(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
// Random pangram
|
|
||||||
testInput := []byte("Glib jocks quiz nymph to vex dwarf.")
|
|
||||||
|
|
||||||
encodedString := EncodeToString(testInput)
|
|
||||||
decodedString, err := DecodeString(encodedString)
|
|
||||||
assert.Nil(err)
|
|
||||||
|
|
||||||
assert.ElementsMatch(testInput, decodedString)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
# base64
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/base64"
|
|
||||||
|
|
||||||
Package base64 implmenets utilities for encoding and decoding text using I2P's
|
|
||||||
### alphabet
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const I2PEncodeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
|
|
||||||
```
|
|
||||||
I2PEncodeAlphabet is the base64 encoding used throughout I2P. RFC 4648 with "/""
|
|
||||||
replaced with "~", and "+" replaced with "-".
|
|
||||||
|
|
||||||
```go
|
|
||||||
var I2PEncoding *b64.Encoding = b64.NewEncoding(I2PEncodeAlphabet)
|
|
||||||
```
|
|
||||||
I2PEncoding is the standard base64 encoding used through I2P.
|
|
||||||
|
|
||||||
#### func DecodeString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func DecodeString(str string) ([]byte, error)
|
|
||||||
```
|
|
||||||
DecodeString decodes base64 string to []byte I2PEncoding
|
|
||||||
|
|
||||||
#### func EncodeToString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func EncodeToString(data []byte) string
|
|
||||||
```
|
|
||||||
I2PEncoding is the standard base64 encoding used through I2P.
|
|
220
lib/common/certificate.go
Normal file
220
lib/common/certificate.go
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Certificate
|
||||||
|
https://geti2p.net/spec/common-structures#certificate
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+-//
|
||||||
|
|type| length | payload
|
||||||
|
+----+----+----+----+----+-//
|
||||||
|
|
||||||
|
type :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
|
||||||
|
case 0 -> NULL
|
||||||
|
case 1 -> HASHCASH
|
||||||
|
case 2 -> HIDDEN
|
||||||
|
case 3 -> SIGNED
|
||||||
|
case 4 -> MULTIPLE
|
||||||
|
case 5 -> KEY
|
||||||
|
|
||||||
|
length :: Integer
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
payload :: data
|
||||||
|
length -> $length bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Certificate Types
|
||||||
|
const (
|
||||||
|
CERT_NULL = iota
|
||||||
|
CERT_HASHCASH
|
||||||
|
CERT_HIDDEN
|
||||||
|
CERT_SIGNED
|
||||||
|
CERT_MULTIPLE
|
||||||
|
CERT_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
// Minimum size of a valid Certificate
|
||||||
|
const (
|
||||||
|
CERT_MIN_SIZE = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type CertificateInterface interface {
|
||||||
|
Cert() []byte
|
||||||
|
Length() (length int, err error)
|
||||||
|
Data() (data []byte, err error)
|
||||||
|
Type() (cert_type int, type_bytes []byte, err error)
|
||||||
|
SignatureSize() (size int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Certificate struct {
|
||||||
|
CertType *Integer
|
||||||
|
CertLen *Integer
|
||||||
|
CertBytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var ci CertificateInterface = &Certificate{}
|
||||||
|
|
||||||
|
func (certificate Certificate) SignatureSize() (size int) {
|
||||||
|
return 40
|
||||||
|
}
|
||||||
|
|
||||||
|
func (certificate Certificate) Cert() []byte {
|
||||||
|
var ret []byte
|
||||||
|
ret = append(ret, certificate.CertType.Bytes()...)
|
||||||
|
l, _ := certificate.Length()
|
||||||
|
//if err != nil && err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" {
|
||||||
|
//}
|
||||||
|
data, _ := certificate.Data()
|
||||||
|
if l != 0 && len(data) != 0 {
|
||||||
|
ret = append(ret, certificate.CertLen.Bytes()...)
|
||||||
|
ret = append(ret, data...)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, certificate.CertLen.Bytes()...)
|
||||||
|
}
|
||||||
|
//log.Println("\n\n CERTIFICATE: ", ret, l+CERT_MIN_SIZE, err)
|
||||||
|
return ret //[:l+CERT_MIN_SIZE]
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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, type_bytes []byte, err error) {
|
||||||
|
return certificate.CertType.Value(), certificate.CertType.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Look up the length of the Certificate, reporting errors if the certificate is
|
||||||
|
// 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) {
|
||||||
|
if certificate.CertLen.Value() < 1 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) Length",
|
||||||
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
|
"certificate_min_size": CERT_MIN_SIZE - 1,
|
||||||
|
"reason": "certificate is too short",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("error parsing certificate length: certificate is too short")
|
||||||
|
}
|
||||||
|
if certificate.CertLen.Value() > len(certificate.CertBytes) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) Length",
|
||||||
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
|
"certificate_actual_length": len(certificate.CertBytes),
|
||||||
|
"reason": "certificate data is shorter than specified by length",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
||||||
|
length = certificate.CertLen.Value()
|
||||||
|
}
|
||||||
|
if certificate.CertLen.Value() < len(certificate.CertBytes) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) Length",
|
||||||
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
|
"certificate_actual_length": len(certificate.CertBytes),
|
||||||
|
"reason": "certificate contains data beyond length",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
|
||||||
|
length = certificate.CertLen.Value()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length = certificate.CertLen.Value()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Certificate data and any errors encountered parsing the Certificate.
|
||||||
|
//
|
||||||
|
func (certificate Certificate) Data() (data []byte, err error) {
|
||||||
|
_, err = certificate.Length()
|
||||||
|
data = certificate.CertBytes
|
||||||
|
if err != nil {
|
||||||
|
switch err.Error() {
|
||||||
|
case "error parsing certificate length: certificate is too short":
|
||||||
|
return
|
||||||
|
case "certificate parsing warning: certificate data is shorter than specified by length":
|
||||||
|
data = certificate.CertBytes
|
||||||
|
return
|
||||||
|
case "certificate parsing warning: certificate data is longer than specified by length":
|
||||||
|
data = certificate.CertBytes[:certificate.CertLen.Value()]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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{}
|
||||||
|
certificate.CertType, err = NewInteger(data[0:1])
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) ReadCertificate",
|
||||||
|
"certificate": certificate,
|
||||||
|
"data": data,
|
||||||
|
"reason": "error parsing certificate type",
|
||||||
|
"error": err,
|
||||||
|
"error_reason": err.Error(),
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
}
|
||||||
|
certificate.CertLen = &Integer{}
|
||||||
|
cert_len := len(data)
|
||||||
|
|
||||||
|
if cert_len < CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) ReadCertificate",
|
||||||
|
"certificate_bytes_length": cert_len,
|
||||||
|
"certificate_min_size": CERT_MIN_SIZE,
|
||||||
|
"reason": "certificate is too short",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("error parsing certificate length: certificate is too short")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
certificate.CertLen, err = NewInteger(data[1:CERT_MIN_SIZE])
|
||||||
|
// _, err = certificate.Type()
|
||||||
|
//log.Println("Calculated len AT LEN", cert_len, "Stated len AT LEN", certificate.CertLen.Value())
|
||||||
|
if err != nil {
|
||||||
|
//return
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) ReadCertificate",
|
||||||
|
"certificate_bytes_length": cert_len,
|
||||||
|
"certificate_min_size": CERT_MIN_SIZE,
|
||||||
|
"reason": "certificate size is invalid",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
//err = errors.New("error parsing certificate type: certificate type is invalid")
|
||||||
|
}
|
||||||
|
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||||
|
_, err = certificate.Length()
|
||||||
|
if err != nil {
|
||||||
|
switch err.Error() {
|
||||||
|
case "error parsing certificate length: certificate is too short":
|
||||||
|
certificate.CertLen, err = NewInteger([]byte{00000000})
|
||||||
|
return
|
||||||
|
case "certificate parsing warning: certificate data is shorter than specified by length":
|
||||||
|
return
|
||||||
|
case "certificate parsing warning: certificate data is longer than specified by length":
|
||||||
|
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||||
|
l, _ := certificate.Length()
|
||||||
|
remainder = data[CERT_MIN_SIZE+l:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -1,210 +0,0 @@
|
|||||||
// Package certificate implements the certificate common-structure of I2P.
|
|
||||||
|
|
||||||
package certificate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
// log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
// Certificate Types
|
|
||||||
const (
|
|
||||||
CERT_NULL = iota
|
|
||||||
CERT_HASHCASH
|
|
||||||
CERT_HIDDEN
|
|
||||||
CERT_SIGNED
|
|
||||||
CERT_MULTIPLE
|
|
||||||
CERT_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
// CERT_MIN_SIZE is the minimum size of a valid Certificate in []byte
|
|
||||||
// 1 byte for type
|
|
||||||
// 2 bytes for payload length
|
|
||||||
const CERT_MIN_SIZE = 3
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P Certificate]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
A certifificate is a container for various receipts of proof of works used throughout the I2P network.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
1 byte Integer specifying certificate type, followed by a 2 byte Integer specifying the size of the certificate playload, then that many bytes.
|
|
||||||
|
|
||||||
+----+----+----+----+----+-//
|
|
||||||
|type| length | payload
|
|
||||||
+----+----+----+----+----+-//
|
|
||||||
|
|
||||||
type :: Integer
|
|
||||||
length -> 1 byte
|
|
||||||
|
|
||||||
case 0 -> NULL
|
|
||||||
case 1 -> HASHCASH
|
|
||||||
case 2 -> HIDDEN
|
|
||||||
case 3 -> SIGNED
|
|
||||||
case 4 -> MULTIPLE
|
|
||||||
case 5 -> KEY
|
|
||||||
|
|
||||||
length :: Integer
|
|
||||||
length -> 2 bytes
|
|
||||||
|
|
||||||
payload :: data
|
|
||||||
length -> $length bytes
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Certificate is the representation of an I2P Certificate.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#certificate
|
|
||||||
type Certificate struct {
|
|
||||||
kind Integer
|
|
||||||
len Integer
|
|
||||||
payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawBytes returns the entire certificate in []byte form, includes excess payload data.
|
|
||||||
func (c *Certificate) RawBytes() []byte {
|
|
||||||
bytes := c.kind.Bytes()
|
|
||||||
bytes = append(bytes, c.len.Bytes()...)
|
|
||||||
bytes = append(bytes, c.payload...)
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"raw_bytes_length": len(bytes),
|
|
||||||
}).Debug("Generated raw bytes for certificate")
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExcessBytes returns the excess bytes in a certificate found after the specified payload length.
|
|
||||||
func (c *Certificate) ExcessBytes() []byte {
|
|
||||||
if len(c.payload) >= c.len.Int() {
|
|
||||||
excess := c.payload[c.len.Int():]
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"excess_bytes_length": len(excess),
|
|
||||||
}).Debug("Found excess bytes in certificate")
|
|
||||||
return excess
|
|
||||||
}
|
|
||||||
log.Debug("No excess bytes found in certificate")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the entire certificate in []byte form, trims payload to specified length.
|
|
||||||
func (c *Certificate) Bytes() []byte {
|
|
||||||
bytes := c.kind.Bytes()
|
|
||||||
bytes = append(bytes, c.len.Bytes()...)
|
|
||||||
bytes = append(bytes, c.Data()...)
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"bytes_length": len(bytes),
|
|
||||||
}).Debug("Generated bytes for certificate")
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Certificate) length() (cert_len int) {
|
|
||||||
cert_len = len(c.Bytes())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the Certificate type specified in the first byte of the Certificate,
|
|
||||||
func (c *Certificate) Type() (cert_type int) {
|
|
||||||
cert_type = c.kind.Int()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"cert_type": cert_type,
|
|
||||||
}).Debug("Retrieved certificate type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length returns the payload length of a Certificate.
|
|
||||||
func (c *Certificate) Length() (length int) {
|
|
||||||
length = c.len.Int()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"length": length,
|
|
||||||
}).Debug("Retrieved certificate length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the payload of a Certificate, payload is trimmed to the specified length.
|
|
||||||
func (c *Certificate) Data() (data []byte) {
|
|
||||||
lastElement := c.Length()
|
|
||||||
if lastElement > len(c.payload) {
|
|
||||||
data = c.payload
|
|
||||||
log.Warn("Certificate payload shorter than specified length")
|
|
||||||
} else {
|
|
||||||
data = c.payload[0:lastElement]
|
|
||||||
}
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"data_length": len(data),
|
|
||||||
}).Debug("Retrieved certificate data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCertificate creates a new Certficiate from []byte
|
|
||||||
// returns err if the certificate is too short or if the payload doesn't match specified length.
|
|
||||||
func NewCertificate(data []byte) (certificate Certificate, err error) {
|
|
||||||
certificate = Certificate{}
|
|
||||||
switch len(data) {
|
|
||||||
case 0:
|
|
||||||
certificate.kind = Integer([]byte{0})
|
|
||||||
certificate.len = Integer([]byte{0})
|
|
||||||
log.WithFields(logrus.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, empty")
|
|
||||||
err = fmt.Errorf("error parsing certificate: certificate is empty")
|
|
||||||
return
|
|
||||||
case 1, 2:
|
|
||||||
certificate.kind = Integer(data[0 : len(data)-1])
|
|
||||||
certificate.len = Integer([]byte{0})
|
|
||||||
log.WithFields(logrus.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, too short")
|
|
||||||
err = fmt.Errorf("error parsing certificate: certificate is too short")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
certificate.kind = Integer(data[0:1])
|
|
||||||
certificate.len = Integer(data[1:3])
|
|
||||||
payleng := len(data) - CERT_MIN_SIZE
|
|
||||||
certificate.payload = data[CERT_MIN_SIZE:]
|
|
||||||
if certificate.len.Int() > len(data)-CERT_MIN_SIZE {
|
|
||||||
err = fmt.Errorf("certificate parsing warning: certificate data is shorter than specified by length")
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Certificate) NewCertificate",
|
|
||||||
"certificate_bytes_length": certificate.len.Int(),
|
|
||||||
"certificate_payload_length": payleng,
|
|
||||||
"data_bytes:": string(data),
|
|
||||||
"kind_bytes": data[0:1],
|
|
||||||
"len_bytes": data[1:3],
|
|
||||||
"reason": err.Error(),
|
|
||||||
}).Error("invalid certificate, shorter than specified by length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"type": certificate.kind.Int(),
|
|
||||||
"length": certificate.len.Int(),
|
|
||||||
}).Debug("Successfully created new certificate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadCertificate creates a Certificate from []byte and returns any ExcessBytes at the end of the input.
|
|
||||||
// returns err if the certificate could not be read.
|
|
||||||
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" {
|
|
||||||
log.Warn("Certificate data longer than specified length")
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
remainder = certificate.ExcessBytes()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Read certificate and extracted remainder")
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
# certificate
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/certificate"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
CERT_NULL = iota
|
|
||||||
CERT_HASHCASH
|
|
||||||
CERT_HIDDEN
|
|
||||||
CERT_SIGNED
|
|
||||||
CERT_MULTIPLE
|
|
||||||
CERT_KEY
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Certificate Types
|
|
||||||
|
|
||||||
```go
|
|
||||||
const CERT_MIN_SIZE = 3
|
|
||||||
```
|
|
||||||
CERT_MIN_SIZE is the minimum size of a valid Certificate in []byte 1 byte for
|
|
||||||
type 2 bytes for payload length
|
|
||||||
|
|
||||||
#### type Certificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Certificate struct {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Certificate is the representation of an I2P Certificate.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#certificate
|
|
||||||
|
|
||||||
#### func NewCertificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewCertificate(data []byte) (certificate Certificate, err error)
|
|
||||||
```
|
|
||||||
NewCertificate creates a new Certficiate from []byte returns err if the
|
|
||||||
certificate is too short or if the payload doesn't match specified length.
|
|
||||||
|
|
||||||
#### func ReadCertificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadCertificate creates a Certificate from []byte and returns any ExcessBytes at
|
|
||||||
the end of the input. returns err if the certificate could not be read.
|
|
||||||
|
|
||||||
#### func (*Certificate) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) Bytes() []byte
|
|
||||||
```
|
|
||||||
Bytes returns the entire certificate in []byte form, trims payload to specified
|
|
||||||
length.
|
|
||||||
|
|
||||||
#### func (*Certificate) Data
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) Data() (data []byte)
|
|
||||||
```
|
|
||||||
Data returns the payload of a Certificate, payload is trimmed to the specified
|
|
||||||
length.
|
|
||||||
|
|
||||||
#### func (*Certificate) ExcessBytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) ExcessBytes() []byte
|
|
||||||
```
|
|
||||||
ExcessBytes returns the excess bytes in a certificate found after the specified
|
|
||||||
payload length.
|
|
||||||
|
|
||||||
#### func (*Certificate) Length
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) Length() (length int)
|
|
||||||
```
|
|
||||||
Length returns the payload length of a Certificate.
|
|
||||||
|
|
||||||
#### func (*Certificate) RawBytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) RawBytes() []byte
|
|
||||||
```
|
|
||||||
RawBytes returns the entire certificate in []byte form, includes excess payload
|
|
||||||
data.
|
|
||||||
|
|
||||||
#### func (*Certificate) Type
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Certificate) Type() (cert_type int)
|
|
||||||
```
|
|
||||||
Type returns the Certificate type specified in the first byte of the
|
|
||||||
Certificate,
|
|
@ -1,4 +1,4 @@
|
|||||||
package certificate
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -10,8 +10,11 @@ func TestCertificateTypeIsFirstByte(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x00}
|
bytes := []byte{0x03, 0x00, 0x00}
|
||||||
certificate, err := NewCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_type := certificate.Type()
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
cert_type, _, err := certificate.Type()
|
||||||
|
|
||||||
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
|
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
@ -21,11 +24,14 @@ func TestCertificateLengthCorrect(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
|
||||||
certificate, err := NewCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_len := certificate.Length()
|
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||||
|
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||||
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
|
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
|
||||||
assert.Nil(err)
|
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCertificateLengthErrWhenTooShort(t *testing.T) {
|
func TestCertificateLengthErrWhenTooShort(t *testing.T) {
|
||||||
@ -33,11 +39,14 @@ func TestCertificateLengthErrWhenTooShort(t *testing.T) {
|
|||||||
|
|
||||||
bytes := []byte{0x03, 0x01}
|
bytes := []byte{0x03, 0x01}
|
||||||
certificate, _, err := ReadCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_len := certificate.Length()
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||||
|
}
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
|
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing certificate: certificate is too short", err.Error(), "correct error message should be returned")
|
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +54,15 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||||
certificate, err := NewCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_len := certificate.Length()
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
|
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
@ -58,25 +74,34 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
|
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
|
||||||
certificate, err := NewCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_data := certificate.Data()
|
assert.Nil(err, "certificate.Data() returned error with valid data")
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Nil(err, "certificate.Data() returned error with valid data")
|
assert.Nil(err, "certificate.Data() returned error with valid data")
|
||||||
cert_len := len(cert_data)
|
|
||||||
assert.Equal(cert_len, 1, "certificate.Length() did not return indicated length when data was valid")
|
assert.Equal(cert_len, 1, "certificate.Length() did not return indicated length when data was valid")
|
||||||
assert.Equal(170, int(cert_data[0]), "certificate.Data() returned incorrect data")
|
data, _ := NewInteger(certificate.CertBytes)
|
||||||
|
assert.Equal(170, data.Value(), "certificate.Data() returned incorrect data")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCertificateDataWhenTooLong(t *testing.T) {
|
func TestCertificateDataWhenTooLong(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
|
||||||
certificate, _, _ := ReadCertificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_data := certificate.Data()
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
|
||||||
|
}
|
||||||
|
|
||||||
cert_len := certificate.Length() // len(cert_data)
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was too long")
|
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 {
|
if certificate.CertBytes[0] != 0xff || certificate.CertBytes[1] != 0xff {
|
||||||
t.Fatal("certificate.Data() returned incorrect data when data was too long")
|
t.Fatal("certificate.Data() returned incorrect data when data was too long")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +110,11 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||||
certificate, err := NewCertificate(bytes)
|
certificate, _, err := ReadCertificate(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")
|
||||||
|
}
|
||||||
|
cert_data, err := certificate.Data()
|
||||||
|
|
||||||
if assert.NotNil(err) {
|
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")
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
|
||||||
@ -102,7 +130,8 @@ func TestReadCertificateWithCorrectData(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
|
t.Log("CERT IS:", cert.Cert())
|
||||||
|
assert.Equal(len(cert.Cert()), 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.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")
|
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||||
}
|
}
|
||||||
@ -113,7 +142,7 @@ func TestReadCertificateWithDataTooShort(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(cert.length(), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
|
assert.Equal(len(cert.Cert()), 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")
|
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on certificate with missing data")
|
||||||
if assert.NotNil(err) {
|
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")
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
|
||||||
@ -126,10 +155,10 @@ func TestReadCertificateWithRemainder(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
|
assert.Equal(len(cert.Cert()), 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(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)
|
assert.NotNil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadCertificateWithInvalidLength(t *testing.T) {
|
func TestReadCertificateWithInvalidLength(t *testing.T) {
|
||||||
@ -138,9 +167,9 @@ func TestReadCertificateWithInvalidLength(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00}
|
bytes := []byte{0x00, 0x00}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(cert.length(), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
|
assert.Equal(len(cert.Cert()), 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")
|
assert.Equal(len(remainder), 0, "ReadCertificate() returned non-zero length remainder on invalid certificate")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing certificate: certificate is too short", err.Error(), "correct error message should be returned")
|
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,87 +0,0 @@
|
|||||||
// Package data implements common data structures used in higher level structures.
|
|
||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
// DATE_SIZE is the length in bytes of an I2P Date.
|
|
||||||
const DATE_SIZE = 8
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P Date]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
The number of milliseconds since midnight on Januyar 1, 1970 in the GMT timezone.
|
|
||||||
If the number is 0, the date is undefined or null.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
8 byte Integer
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Date is the represenation of an I2P Date.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#date
|
|
||||||
type Date [8]byte
|
|
||||||
|
|
||||||
// Bytes returns the raw []byte content of a Date.
|
|
||||||
func (i Date) Bytes() []byte {
|
|
||||||
return i[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int returns the Date as a Go integer.
|
|
||||||
func (i Date) Int() int {
|
|
||||||
return intFromBytes(i.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time takes the value stored in date as an 8 byte big-endian integer representing the
|
|
||||||
// number of milliseconds since the beginning of unix time and converts it to a Go time.Time
|
|
||||||
// struct.
|
|
||||||
func (date Date) Time() (date_time time.Time) {
|
|
||||||
seconds := Integer(date[:])
|
|
||||||
date_time = time.Unix(0, int64(seconds.Int()*1000000))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDate creates a Date from []byte using the first DATE_SIZE bytes.
|
|
||||||
// Any data after DATE_SIZE is returned as a remainder.
|
|
||||||
func ReadDate(data []byte) (date Date, remainder []byte, err error) {
|
|
||||||
if len(data) < 8 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"data": data,
|
|
||||||
}).Error("ReadDate: data is too short")
|
|
||||||
err = errors.New("ReadDate: data is too short")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(date[:], data[:8])
|
|
||||||
remainder = data[8:]
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"date_value": date.Int(),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully read Date from data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDate creates a new Date from []byte using ReadDate.
|
|
||||||
// Returns a pointer to Date unlike ReadDate.
|
|
||||||
func NewDate(data []byte) (date *Date, remainder []byte, err error) {
|
|
||||||
objdate, remainder, err := ReadDate(data)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to create new Date")
|
|
||||||
return nil, remainder, err
|
|
||||||
}
|
|
||||||
|
|
||||||
date = &objdate
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"date_value": date.Int(),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully created new Date")
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,297 +0,0 @@
|
|||||||
# data
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
|
|
||||||
Package data implements common data structures used in higher level structures.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const DATE_SIZE = 8
|
|
||||||
```
|
|
||||||
DATE_SIZE is the length in bytes of an I2P Date.
|
|
||||||
|
|
||||||
```go
|
|
||||||
const MAX_INTEGER_SIZE = 8
|
|
||||||
```
|
|
||||||
MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes.
|
|
||||||
|
|
||||||
```go
|
|
||||||
const STRING_MAX_SIZE = 255
|
|
||||||
```
|
|
||||||
STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P
|
|
||||||
string
|
|
||||||
|
|
||||||
#### func PrintErrors
|
|
||||||
|
|
||||||
```go
|
|
||||||
func PrintErrors(errs []error)
|
|
||||||
```
|
|
||||||
PrintErrors prints a formatted list of errors to the console.
|
|
||||||
|
|
||||||
#### func WrapErrors
|
|
||||||
|
|
||||||
```go
|
|
||||||
func WrapErrors(errs []error) error
|
|
||||||
```
|
|
||||||
WrapErrors compiles a slice of errors and returns them wrapped together as a
|
|
||||||
single error.
|
|
||||||
|
|
||||||
#### type Date
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Date [8]byte
|
|
||||||
```
|
|
||||||
|
|
||||||
Date is the represenation of an I2P Date.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#date
|
|
||||||
|
|
||||||
#### func NewDate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewDate(data []byte) (date *Date, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
NewDate creates a new Date from []byte using ReadDate. Returns a pointer to Date
|
|
||||||
unlike ReadDate.
|
|
||||||
|
|
||||||
#### func ReadDate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadDate(data []byte) (date Date, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadDate creates a Date from []byte using the first DATE_SIZE bytes. Any data
|
|
||||||
after DATE_SIZE is returned as a remainder.
|
|
||||||
|
|
||||||
#### func (Date) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (i Date) Bytes() []byte
|
|
||||||
```
|
|
||||||
Bytes returns the raw []byte content of a Date.
|
|
||||||
|
|
||||||
#### func (Date) Int
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (i Date) Int() int
|
|
||||||
```
|
|
||||||
Int returns the Date as a Go integer.
|
|
||||||
|
|
||||||
#### func (Date) Time
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (date Date) Time() (date_time time.Time)
|
|
||||||
```
|
|
||||||
Time takes the value stored in date as an 8 byte big-endian integer representing
|
|
||||||
the number of milliseconds since the beginning of unix time and converts it to a
|
|
||||||
Go time.Time struct.
|
|
||||||
|
|
||||||
#### type Hash
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Hash [32]byte
|
|
||||||
```
|
|
||||||
|
|
||||||
Hash is the represenation of an I2P Hash.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#hash
|
|
||||||
|
|
||||||
#### func HashData
|
|
||||||
|
|
||||||
```go
|
|
||||||
func HashData(data []byte) (h Hash)
|
|
||||||
```
|
|
||||||
HashData returns the SHA256 sum of a []byte input as Hash.
|
|
||||||
|
|
||||||
#### func HashReader
|
|
||||||
|
|
||||||
```go
|
|
||||||
func HashReader(r io.Reader) (h Hash, err error)
|
|
||||||
```
|
|
||||||
HashReader returns the SHA256 sum from all data read from an io.Reader. return
|
|
||||||
error if one occurs while reading from reader
|
|
||||||
|
|
||||||
#### func (Hash) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (h Hash) Bytes() [32]byte
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type I2PString
|
|
||||||
|
|
||||||
```go
|
|
||||||
type I2PString []byte
|
|
||||||
```
|
|
||||||
|
|
||||||
I2PString is the represenation of an I2P String.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#string
|
|
||||||
|
|
||||||
#### func ReadI2PString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadI2PString returns I2PString from a []byte. The remaining bytes after the
|
|
||||||
specified length are also returned. Returns a list of errors that occurred
|
|
||||||
during parsing.
|
|
||||||
|
|
||||||
#### func ToI2PString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ToI2PString(data string) (str I2PString, err error)
|
|
||||||
```
|
|
||||||
ToI2PString converts a Go string to an I2PString. Returns error if the string
|
|
||||||
exceeds STRING_MAX_SIZE.
|
|
||||||
|
|
||||||
#### func (I2PString) Data
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (str I2PString) Data() (data string, err error)
|
|
||||||
```
|
|
||||||
Data returns the I2PString content as a string trimmed to the specified length
|
|
||||||
and not including the length byte. Returns error encountered by Length.
|
|
||||||
|
|
||||||
#### func (I2PString) Length
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (str I2PString) Length() (length int, err error)
|
|
||||||
```
|
|
||||||
Length returns the length specified in the first byte. Returns error if the
|
|
||||||
specified does not match the actual length or the string is otherwise invalid.
|
|
||||||
|
|
||||||
#### type Integer
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Integer []byte
|
|
||||||
```
|
|
||||||
|
|
||||||
Integer is the represenation of an I2P Integer.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#integer
|
|
||||||
|
|
||||||
#### func NewInteger
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewInteger(bytes []byte, size int) (integer *Integer, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
NewInteger creates a new Integer from []byte using ReadInteger. Limits the
|
|
||||||
length of the created Integer to MAX_INTEGER_SIZE. Returns a pointer to Integer
|
|
||||||
unlike ReadInteger.
|
|
||||||
|
|
||||||
#### func NewIntegerFromInt
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewIntegerFromInt(value int, size int) (integer *Integer, err error)
|
|
||||||
```
|
|
||||||
NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte
|
|
||||||
length.
|
|
||||||
|
|
||||||
#### func ReadInteger
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadInteger(bytes []byte, size int) (Integer, []byte)
|
|
||||||
```
|
|
||||||
ReadInteger returns an Integer from a []byte of specified length. The remaining
|
|
||||||
bytes after the specified length are also returned.
|
|
||||||
|
|
||||||
#### func (Integer) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (i Integer) Bytes() []byte
|
|
||||||
```
|
|
||||||
Bytes returns the raw []byte content of an Integer.
|
|
||||||
|
|
||||||
#### func (Integer) Int
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (i Integer) Int() int
|
|
||||||
```
|
|
||||||
Int returns the Date as a Go integer
|
|
||||||
|
|
||||||
#### type Mapping
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Mapping struct {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Mapping is the represenation of an I2P Mapping.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#mapping
|
|
||||||
|
|
||||||
#### func GoMapToMapping
|
|
||||||
|
|
||||||
```go
|
|
||||||
func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error)
|
|
||||||
```
|
|
||||||
GoMapToMapping converts a Go map of unformatted strings to *Mapping.
|
|
||||||
|
|
||||||
#### func NewMapping
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewMapping(bytes []byte) (values *Mapping, remainder []byte, err []error)
|
|
||||||
```
|
|
||||||
NewMapping creates a new *Mapping from []byte using ReadMapping. Returns a
|
|
||||||
pointer to Mapping unlike ReadMapping.
|
|
||||||
|
|
||||||
#### func ReadMapping
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
|
|
||||||
```
|
|
||||||
ReadMapping returns Mapping from a []byte. The remaining bytes after the
|
|
||||||
specified length are also returned. Returns a list of errors that occurred
|
|
||||||
during parsing.
|
|
||||||
|
|
||||||
#### func ValuesToMapping
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ValuesToMapping(values MappingValues) *Mapping
|
|
||||||
```
|
|
||||||
ValuesToMapping creates a *Mapping using MappingValues. The values are sorted in
|
|
||||||
the order defined in mappingOrder.
|
|
||||||
|
|
||||||
#### func (*Mapping) Data
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (mapping *Mapping) Data() []byte
|
|
||||||
```
|
|
||||||
Data returns a Mapping in its []byte form.
|
|
||||||
|
|
||||||
#### func (*Mapping) HasDuplicateKeys
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (mapping *Mapping) HasDuplicateKeys() bool
|
|
||||||
```
|
|
||||||
HasDuplicateKeys returns true if two keys in a mapping are identical.
|
|
||||||
|
|
||||||
#### func (Mapping) Values
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (mapping Mapping) Values() MappingValues
|
|
||||||
```
|
|
||||||
Values returns the values contained in a Mapping as MappingValues.
|
|
||||||
|
|
||||||
#### type MappingValues
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MappingValues [][2]I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
MappingValues represents the parsed key value pairs inside of an I2P Mapping.
|
|
||||||
|
|
||||||
#### func ReadMappingValues
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingValues, remainder_bytes []byte, errs []error)
|
|
||||||
```
|
|
||||||
ReadMappingValues returns *MappingValues from a []byte. The remaining bytes
|
|
||||||
after the specified length are also returned. Returns a list of errors that
|
|
||||||
occurred during parsing.
|
|
||||||
|
|
||||||
#### func (MappingValues) Get
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (m MappingValues) Get(key I2PString) I2PString
|
|
||||||
```
|
|
@ -1,19 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// WrapErrors compiles a slice of errors and returns them wrapped together as a single error.
|
|
||||||
func WrapErrors(errs []error) error {
|
|
||||||
var err error
|
|
||||||
for i, e := range errs {
|
|
||||||
err = fmt.Errorf("%v\n\t%d: %v", err, i, e)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintErrors prints a formatted list of errors to the console.
|
|
||||||
func PrintErrors(errs []error) {
|
|
||||||
for i, e := range errs {
|
|
||||||
fmt.Printf("\t%d: %v\n", i, e)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P Hash]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
Represents the SHA256 of some data.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
32 bytes
|
|
||||||
|
|
||||||
[I2P Hash]:
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Hash is the represenation of an I2P Hash.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#hash
|
|
||||||
type Hash [32]byte
|
|
||||||
|
|
||||||
func (h Hash) Bytes() [32]byte {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashData returns the SHA256 sum of a []byte input as Hash.
|
|
||||||
func HashData(data []byte) (h Hash) {
|
|
||||||
// log.Println("Hashing Data:", data)
|
|
||||||
h = sha256.Sum256(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashReader returns the SHA256 sum from all data read from an io.Reader.
|
|
||||||
// return error if one occurs while reading from reader
|
|
||||||
func HashReader(r io.Reader) (h Hash, err error) {
|
|
||||||
sha := sha256.New()
|
|
||||||
_, err = io.Copy(sha, r)
|
|
||||||
if err == nil {
|
|
||||||
d := sha.Sum(nil)
|
|
||||||
copy(h[:], d)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes.
|
|
||||||
const MAX_INTEGER_SIZE = 8
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P Hash]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
Represents a non-negative integer.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
1 to 8 bytes in network byte order (big endian) representing an unsigned integer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Integer is the represenation of an I2P Integer.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#integer
|
|
||||||
type Integer []byte
|
|
||||||
|
|
||||||
// Bytes returns the raw []byte content of an Integer.
|
|
||||||
func (i Integer) Bytes() []byte {
|
|
||||||
return i[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int returns the Date as a Go integer
|
|
||||||
func (i Integer) Int() int {
|
|
||||||
return intFromBytes(i.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadInteger returns an Integer from a []byte of specified length.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
func ReadInteger(bytes []byte, size int) (Integer, []byte) {
|
|
||||||
if len(bytes) < size {
|
|
||||||
return bytes[:size], bytes[len(bytes):]
|
|
||||||
}
|
|
||||||
return bytes[:size], bytes[size:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInteger creates a new Integer from []byte using ReadInteger.
|
|
||||||
// Limits the length of the created Integer to MAX_INTEGER_SIZE.
|
|
||||||
// Returns a pointer to Integer unlike ReadInteger.
|
|
||||||
func NewInteger(bytes []byte, size int) (integer *Integer, remainder []byte, err error) {
|
|
||||||
integerSize := MAX_INTEGER_SIZE
|
|
||||||
if size < MAX_INTEGER_SIZE {
|
|
||||||
integerSize = size
|
|
||||||
}
|
|
||||||
intBytes := bytes[:integerSize]
|
|
||||||
remainder = bytes[integerSize:]
|
|
||||||
i, _ := ReadInteger(intBytes, integerSize)
|
|
||||||
integer = &i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte length.
|
|
||||||
func NewIntegerFromInt(value int, size int) (integer *Integer, err error) {
|
|
||||||
bytes := make([]byte, MAX_INTEGER_SIZE)
|
|
||||||
binary.BigEndian.PutUint64(bytes, uint64(value))
|
|
||||||
integerSize := MAX_INTEGER_SIZE
|
|
||||||
if size < MAX_INTEGER_SIZE {
|
|
||||||
integerSize = size
|
|
||||||
}
|
|
||||||
objinteger, _, err := NewInteger(bytes[MAX_INTEGER_SIZE-integerSize:], integerSize)
|
|
||||||
integer = objinteger
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
|
|
||||||
// integer and return an int representation.
|
|
||||||
func intFromBytes(number []byte) (value int) {
|
|
||||||
num_len := len(number)
|
|
||||||
if num_len < MAX_INTEGER_SIZE {
|
|
||||||
number = append(
|
|
||||||
make([]byte, MAX_INTEGER_SIZE-num_len),
|
|
||||||
number...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
value = int(binary.BigEndian.Uint64(number))
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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)
|
|
||||||
|
|
||||||
assert.Equal(integer.Int(), 1, "Integer() did not parse bytes big endian")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWorksWithOneByte(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
integer := Integer([]byte{0x01})
|
|
||||||
|
|
||||||
assert.Equal(integer.Int(), 1, "Integer() did not correctly parse single byte slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsZeroWithNoData(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
integer := Integer([]byte{})
|
|
||||||
|
|
||||||
assert.Equal(integer.Int(), 0, "Integer() did not correctly parse zero length byte slice")
|
|
||||||
}
|
|
@ -1,233 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P Mapping]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
A set of key/value mappings or properties
|
|
||||||
|
|
||||||
|
|
||||||
Contents
|
|
||||||
A 2-byte size Integer followed by a series of String=String; pairs
|
|
||||||
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| size |key_string (len + data) | = |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| val_string (len + data) | ; | ...
|
|
||||||
+----+----+----+----+----+----+----+
|
|
||||||
size :: Integer
|
|
||||||
length -> 2 bytes
|
|
||||||
Total number of bytes that follow
|
|
||||||
|
|
||||||
key_string :: String
|
|
||||||
A string (one byte length followed by UTF-8 encoded characters)
|
|
||||||
|
|
||||||
= :: A single byte containing '='
|
|
||||||
|
|
||||||
val_string :: String
|
|
||||||
A string (one byte length followed by UTF-8 encoded characters)
|
|
||||||
|
|
||||||
; :: A single byte containing ';'
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Mapping is the represenation of an I2P Mapping.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#mapping
|
|
||||||
type Mapping struct {
|
|
||||||
size *Integer
|
|
||||||
vals *MappingValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values returns the values contained in a Mapping as MappingValues.
|
|
||||||
func (mapping Mapping) Values() MappingValues {
|
|
||||||
if mapping.vals == nil {
|
|
||||||
log.Debug("Mapping values are nil, returning empty MappingValues")
|
|
||||||
return MappingValues{}
|
|
||||||
}
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"values_count": len(*mapping.vals),
|
|
||||||
}).Debug("Retrieved Mapping values")
|
|
||||||
return *mapping.vals
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns a Mapping in its []byte form.
|
|
||||||
func (mapping *Mapping) Data() []byte {
|
|
||||||
keyOrValIntegerLength := 1
|
|
||||||
bytes := mapping.size.Bytes()
|
|
||||||
for _, pair := range mapping.Values() {
|
|
||||||
klen, _ := pair[0].Length()
|
|
||||||
keylen, _ := NewIntegerFromInt(klen, keyOrValIntegerLength)
|
|
||||||
bytes = append(bytes, keylen.Bytes()...)
|
|
||||||
bytes = append(bytes, pair[0][1:]...)
|
|
||||||
bytes = append(bytes, 0x3d)
|
|
||||||
vlen, _ := pair[1].Length()
|
|
||||||
vallen, _ := NewIntegerFromInt(vlen, keyOrValIntegerLength)
|
|
||||||
bytes = append(bytes, vallen.Bytes()...)
|
|
||||||
bytes = append(bytes, pair[1][1:]...)
|
|
||||||
bytes = append(bytes, 0x3b)
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasDuplicateKeys returns true if two keys in a mapping are identical.
|
|
||||||
func (mapping *Mapping) HasDuplicateKeys() bool {
|
|
||||||
log.Debug("Checking for duplicate keys in Mapping")
|
|
||||||
seen_values := make(map[string]bool)
|
|
||||||
values := mapping.Values()
|
|
||||||
for _, pair := range values {
|
|
||||||
key, _ := pair[0].Data()
|
|
||||||
if _, present := seen_values[key]; present {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"duplicate_key": key,
|
|
||||||
}).Warn("Found duplicate key in Mapping")
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
seen_values[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debug("No duplicate keys found in Mapping")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GoMapToMapping converts a Go map of unformatted strings to *Mapping.
|
|
||||||
func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_map_size": len(gomap),
|
|
||||||
}).Debug("Converting Go map to Mapping")
|
|
||||||
map_vals := MappingValues{}
|
|
||||||
for k, v := range gomap {
|
|
||||||
key_str, kerr := ToI2PString(k)
|
|
||||||
if kerr != nil {
|
|
||||||
log.WithError(kerr).Error("Failed to convert key to I2PString")
|
|
||||||
err = kerr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val_str, verr := ToI2PString(v)
|
|
||||||
if verr != nil {
|
|
||||||
log.WithError(verr).Error("Failed to convert value to I2PString")
|
|
||||||
err = verr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
map_vals = append(
|
|
||||||
map_vals,
|
|
||||||
[2]I2PString{key_str, val_str},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
mapping = ValuesToMapping(map_vals)
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"mapping_size": len(map_vals),
|
|
||||||
}).Debug("Successfully converted Go map to Mapping")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the string parsing error indicates that the Mapping
|
|
||||||
// should no longer be parsed.
|
|
||||||
func stopValueRead(err error) bool {
|
|
||||||
result := err.Error() == "error parsing string: zero length"
|
|
||||||
if result {
|
|
||||||
log.WithError(err).Debug("Stopping value read due to zero length error")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the first byte in a slice of bytes is the provided byte.
|
|
||||||
func beginsWith(bytes []byte, chr byte) bool {
|
|
||||||
/*
|
|
||||||
return len(bytes) != 0 &&
|
|
||||||
bytes[0] == chr
|
|
||||||
*/
|
|
||||||
result := len(bytes) != 0 && bytes[0] == chr
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"bytes_length": len(bytes),
|
|
||||||
"expected_char": string(chr),
|
|
||||||
"result": result,
|
|
||||||
}).Debug("Checked if bytes begin with specific character")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMapping returns Mapping from a []byte.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(bytes),
|
|
||||||
}).Debug("Reading Mapping from bytes")
|
|
||||||
if len(bytes) < 3 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ReadMapping",
|
|
||||||
"reason": "zero length",
|
|
||||||
}).Warn("mapping format violation")
|
|
||||||
e := errors.New("zero length")
|
|
||||||
err = append(err, e)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size, remainder, e := NewInteger(bytes, 2)
|
|
||||||
if e != nil {
|
|
||||||
log.WithError(e).Error("Failed to read Mapping size")
|
|
||||||
err = append(err, e)
|
|
||||||
}
|
|
||||||
if size.Int() == 0 {
|
|
||||||
log.Warn("Mapping size is zero")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mapping.size = size
|
|
||||||
map_bytes := remainder[:mapping.size.Int()]
|
|
||||||
remainder = remainder[mapping.size.Int():]
|
|
||||||
if len(remainder) == 0 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ReadMapping",
|
|
||||||
"reason": "zero length",
|
|
||||||
}).Warn("mapping format violation")
|
|
||||||
e := errors.New("zero length")
|
|
||||||
err = append(err, e)
|
|
||||||
}
|
|
||||||
// TODO: this should take the remainder and the length we already parsed above, as a parameter.
|
|
||||||
// Like tomorrow morning.
|
|
||||||
// ReadMappingValues should not attempt to figure out the length of the bytes it's reading over.
|
|
||||||
vals, _, mappingValueErrs := ReadMappingValues(map_bytes, *mapping.size)
|
|
||||||
|
|
||||||
err = append(err, mappingValueErrs...)
|
|
||||||
mapping.vals = vals
|
|
||||||
if len(mappingValueErrs) > 0 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ReadMapping",
|
|
||||||
"reason": "error parsing mapping values",
|
|
||||||
}).Warn("mapping format violation")
|
|
||||||
e := errors.New("error parsing mapping values")
|
|
||||||
err = append(err, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"mapping_size": mapping.size.Int(),
|
|
||||||
"values_count": len(*mapping.vals),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
"error_count": len(err),
|
|
||||||
}).Debug("Finished reading Mapping")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMapping creates a new *Mapping from []byte using ReadMapping.
|
|
||||||
// Returns a pointer to Mapping unlike ReadMapping.
|
|
||||||
func NewMapping(bytes []byte) (values *Mapping, remainder []byte, err []error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(bytes),
|
|
||||||
}).Debug("Creating new Mapping")
|
|
||||||
|
|
||||||
objvalues, remainder, err := ReadMapping(bytes)
|
|
||||||
values = &objvalues
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"values_count": len(values.Values()),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
"error_count": len(err),
|
|
||||||
}).Debug("Finished creating new Mapping")
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValuesExclusesPairWithBadData(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
bad_key, _, errs := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
|
|
||||||
values := bad_key.Values()
|
|
||||||
|
|
||||||
e := WrapErrors(errs)
|
|
||||||
t.Log(e)
|
|
||||||
|
|
||||||
assert.NotNil(errs, "Values() did not return errors when some values had bad key")
|
|
||||||
|
|
||||||
if assert.Equal(1, len(values), "Values() did not return valid values when some values had bad key") {
|
|
||||||
k := values[0][0]
|
|
||||||
key, _ := k.Data()
|
|
||||||
v := values[0][1]
|
|
||||||
val, _ := v.Data()
|
|
||||||
assert.Equal(key, "a", "Values() returned by data with invalid key contains incorrect present key")
|
|
||||||
assert.Equal(val, "b", "Values() returned by data with invalid key contains incorrect present key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValuesWarnsMissingData(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
_, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62})
|
|
||||||
|
|
||||||
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had missing data") {
|
|
||||||
assert.Equal(errs[0].Error(), "warning parsing mapping: mapping length exceeds provided data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValuesWarnsExtraData(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
|
|
||||||
values := mapping.Values()
|
|
||||||
|
|
||||||
key, kerr := values[0][0].Data()
|
|
||||||
val, verr := values[0][1].Data()
|
|
||||||
|
|
||||||
assert.Nil(kerr)
|
|
||||||
assert.Nil(verr)
|
|
||||||
assert.Equal(key, "a", "Values() did not return key in valid data")
|
|
||||||
assert.Equal(val, "b", "Values() did not return value in valid data")
|
|
||||||
|
|
||||||
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had extra data") {
|
|
||||||
assert.Equal("warning parsing mapping: data exists beyond length of mapping", errs[0].Error(), "correct error message should be returned")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValuesEnforcesEqualDelimitor(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x30, 0x01, 0x62, 0x3b})
|
|
||||||
values := mapping.Values()
|
|
||||||
|
|
||||||
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had = format error") {
|
|
||||||
assert.Equal("mapping format violation, expected =", errs[0].Error(), "correct error message should be returned")
|
|
||||||
}
|
|
||||||
assert.Equal(0, len(values), "Values() not empty with invalid data due to = format error")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValuesEnforcedSemicolonDelimitor(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x30})
|
|
||||||
values := mapping.Values()
|
|
||||||
|
|
||||||
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had ; format error") {
|
|
||||||
assert.Equal("mapping format violation, expected ;", errs[0].Error(), "correct error message should be returned")
|
|
||||||
}
|
|
||||||
assert.Equal(0, len(values), "Values() not empty with invalid data due to ; format error")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValuesReturnsValues(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
|
||||||
values := mapping.Values()
|
|
||||||
|
|
||||||
key, kerr := values[0][0].Data()
|
|
||||||
val, verr := values[0][1].Data()
|
|
||||||
|
|
||||||
assert.Nil(errs, "Values() returned a errors with parsing valid data")
|
|
||||||
assert.Nil(kerr)
|
|
||||||
assert.Nil(verr)
|
|
||||||
assert.Equal("a", key, "Values() did not return key in valid data")
|
|
||||||
assert.Equal("b", val, "Values() did not return value in valid data")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasDuplicateKeysTrueWhenDuplicates(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
dups, _, _ := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
|
||||||
|
|
||||||
assert.Equal(true, dups.HasDuplicateKeys(), "HasDuplicateKeys() did not report true when duplicate keys present")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasDuplicateKeysFalseWithoutDuplicates(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
mapping, _, _ := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
|
||||||
|
|
||||||
assert.Equal(false, mapping.HasDuplicateKeys(), "HasDuplicateKeys() did not report false when no duplicate keys present")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadMappingHasDuplicateKeys(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
_, _, errs := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
|
||||||
|
|
||||||
assert.Equal("mapping format violation, duplicate key in mapping", errs[0].Error(), "ReadMapping should throw an error when duplicate keys are present.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGoMapToMappingProducesCorrectMapping(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
gomap := map[string]string{"a": "b"}
|
|
||||||
mapping, err := GoMapToMapping(gomap)
|
|
||||||
|
|
||||||
assert.Nil(err, "GoMapToMapping() returned error with valid data")
|
|
||||||
expected := []byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b}
|
|
||||||
if bytes.Compare(mapping.Data(), expected) != 0 {
|
|
||||||
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFullGoMapToMappingProducesCorrectMapping(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
gomap := map[string]string{
|
|
||||||
"a": "b",
|
|
||||||
"c": "d",
|
|
||||||
}
|
|
||||||
mapping, err := GoMapToMapping(gomap)
|
|
||||||
|
|
||||||
assert.Nil(err, "GoMapToMapping() returned error with valid data")
|
|
||||||
expected := []byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x63, 0x3d, 0x01, 0x64, 0x3b}
|
|
||||||
if bytes.Compare(mapping.Data(), expected) != 0 {
|
|
||||||
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStopValueReadTrueWhenCorrectErr(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
status := stopValueRead(errors.New("error parsing string: zero length"))
|
|
||||||
|
|
||||||
assert.Equal(true, status, "stopValueRead() did not return true when String error found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStopValueReadFalseWhenWrongErr(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
status := stopValueRead(errors.New("something else"))
|
|
||||||
|
|
||||||
assert.Equal(false, status, "stopValueRead() did not return false when non String error found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBeginsWithCorrectWhenTrue(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
slice := []byte{0x41}
|
|
||||||
|
|
||||||
assert.Equal(true, beginsWith(slice, 0x41), "beginsWith() did not return true when correct")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBeginsWithCorrectWhenFalse(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
slice := []byte{0x00}
|
|
||||||
|
|
||||||
assert.Equal(false, beginsWith(slice, 0x41), "beginsWith() did not false when incorrect")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBeginsWithCorrectWhenNil(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
slice := make([]byte, 0)
|
|
||||||
|
|
||||||
assert.Equal(false, beginsWith(slice, 0x41), "beginsWith() did not return false on empty slice")
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MappingValues represents the parsed key value pairs inside of an I2P Mapping.
|
|
||||||
type MappingValues [][2]I2PString
|
|
||||||
|
|
||||||
func (m MappingValues) Get(key I2PString) I2PString {
|
|
||||||
keyBytes, _ := key.Data()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key": string(keyBytes),
|
|
||||||
}).Debug("Searching for key in MappingValues")
|
|
||||||
for _, pair := range m {
|
|
||||||
kb, _ := pair[0][0:].Data()
|
|
||||||
if kb == keyBytes {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key": string(keyBytes),
|
|
||||||
"value": string(pair[1][1:]),
|
|
||||||
}).Debug("Found matching key in MappingValues")
|
|
||||||
return pair[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key": string(keyBytes),
|
|
||||||
}).Debug("Key not found in MappingValues")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValuesToMapping creates a *Mapping using MappingValues.
|
|
||||||
// The values are sorted in the order defined in mappingOrder.
|
|
||||||
func ValuesToMapping(values MappingValues) *Mapping {
|
|
||||||
// Default length to 2 * len
|
|
||||||
// 1 byte for ;
|
|
||||||
// 1 byte for =
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"values_count": len(values),
|
|
||||||
}).Debug("Converting MappingValues to Mapping")
|
|
||||||
baseLength := 2 * len(values)
|
|
||||||
for _, mappingVals := range values {
|
|
||||||
for _, keyOrVal := range mappingVals {
|
|
||||||
baseLength += len(keyOrVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"mapping_size": baseLength,
|
|
||||||
}).Debug("Created Mapping from MappingValues")
|
|
||||||
|
|
||||||
mappingSize, _ := NewIntegerFromInt(baseLength, 2)
|
|
||||||
return &Mapping{
|
|
||||||
size: mappingSize,
|
|
||||||
vals: &values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2P Mappings require consistent order in some cases for cryptographic signing, and sorting
|
|
||||||
// by keys. The Mapping is sorted lexographically by keys. Duplicate keys are allowed in general,
|
|
||||||
// but in implementations where they must be sorted like I2CP SessionConfig duplicate keys are not allowed.
|
|
||||||
// In practice routers do not seem to allow duplicate keys.
|
|
||||||
func mappingOrder(values MappingValues) {
|
|
||||||
sort.SliceStable(values, func(i, j int) bool {
|
|
||||||
// Lexographic sort on keys only
|
|
||||||
data1, _ := values[i][0].Data()
|
|
||||||
data2, _ := values[j][0].Data()
|
|
||||||
return data1 < data2
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMappingValues returns *MappingValues from a []byte.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingValues, remainder_bytes []byte, errs []error) {
|
|
||||||
// mapping := remainder
|
|
||||||
// var remainder = mapping
|
|
||||||
// var err error
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(remainder),
|
|
||||||
"map_length": map_length.Int(),
|
|
||||||
}).Debug("Reading MappingValues")
|
|
||||||
|
|
||||||
if remainder == nil || len(remainder) < 1 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"reason": "data shorter than expected",
|
|
||||||
}).Error("mapping contained no data")
|
|
||||||
errs = []error{errors.New("mapping contained no data")}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
map_values := make(MappingValues, 0)
|
|
||||||
int_map_length := map_length.Int()
|
|
||||||
mapping_len := len(remainder)
|
|
||||||
if mapping_len > int_map_length {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"mapping_bytes_length": mapping_len,
|
|
||||||
"mapping_length_field": int_map_length,
|
|
||||||
"reason": "data longer than expected",
|
|
||||||
}).Warn("mapping format warning")
|
|
||||||
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
|
|
||||||
} else if int_map_length > mapping_len {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"mapping_bytes_length": mapping_len,
|
|
||||||
"mapping_length_field": int_map_length,
|
|
||||||
"reason": "data shorter than expected",
|
|
||||||
}).Warn("mapping format warning")
|
|
||||||
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
|
|
||||||
}
|
|
||||||
|
|
||||||
encounteredKeysMap := map[string]bool{}
|
|
||||||
// pop off length bytes before parsing kv pairs
|
|
||||||
// remainder = remainder[2:]
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Read a key, breaking on fatal errors
|
|
||||||
// and appending warnings
|
|
||||||
|
|
||||||
// Minimum byte length required for another KV pair.
|
|
||||||
// Two bytes for each string length
|
|
||||||
// At least 1 byte per string
|
|
||||||
// One byte for =
|
|
||||||
// One byte for ;
|
|
||||||
if len(remainder) < 6 {
|
|
||||||
// Not returning an error here as the issue is already flagged by mapping length being wrong.
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"reason": "mapping format violation",
|
|
||||||
}).Warn("mapping format violation, too few bytes for a kv pair")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
key_str, more, err := ReadI2PString(remainder)
|
|
||||||
if err != nil {
|
|
||||||
if stopValueRead(err) {
|
|
||||||
errs = append(errs, err)
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// overwriting remainder with more as another var to prevent memory weirdness in loops
|
|
||||||
remainder = more
|
|
||||||
// log.Printf("(MAPPING VALUES DEBUG) Remainder: %s\n", remainder)
|
|
||||||
|
|
||||||
// Check if key has already been encountered in this mapping
|
|
||||||
keyBytes, _ := key_str.Data()
|
|
||||||
keyAsString := string(keyBytes)
|
|
||||||
_, ok := encounteredKeysMap[keyAsString]
|
|
||||||
if ok {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"reason": "duplicate key in mapping",
|
|
||||||
"key": string(key_str),
|
|
||||||
}).Error("mapping format violation")
|
|
||||||
log.Printf("DUPE: %s", key_str)
|
|
||||||
errs = append(errs, errors.New("mapping format violation, duplicate key in mapping"))
|
|
||||||
// Based on other implementations this does not seem to happen often?
|
|
||||||
// Java throws an exception in this case, the base object is a Hashmap so the value is overwritten and an exception is thrown.
|
|
||||||
// i2pd as far as I can tell just overwrites the original value
|
|
||||||
// Continue on, we can check if the Mapping contains duplicate keys later.
|
|
||||||
}
|
|
||||||
|
|
||||||
if !beginsWith(remainder, 0x3d) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"reason": "expected =",
|
|
||||||
"value:": string(remainder),
|
|
||||||
}).Warn("mapping format violation")
|
|
||||||
errs = append(errs, errors.New("mapping format violation, expected ="))
|
|
||||||
log.Printf("ERRVAL: %s", remainder)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
remainder = remainder[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a value, breaking on fatal errors
|
|
||||||
// and appending warnings
|
|
||||||
val_str, more, err := ReadI2PString(remainder)
|
|
||||||
if err != nil {
|
|
||||||
if stopValueRead(err) {
|
|
||||||
errs = append(errs, err)
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// overwriting remainder with more as another var to prevent memory weirdness in loops
|
|
||||||
remainder = more
|
|
||||||
// log.Printf("(MAPPING VALUES DEBUG) Remainder: %s\n", remainder)
|
|
||||||
// log.Printf("(MAPPING VALUES DEBUG) String: value: %s", val_str)
|
|
||||||
if !beginsWith(remainder, 0x3b) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(Mapping) Values",
|
|
||||||
"reason": "expected ;",
|
|
||||||
"value:": string(remainder),
|
|
||||||
}).Warn("mapping format violation")
|
|
||||||
errs = append(errs, errors.New("mapping format violation, expected ;"))
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
remainder = remainder[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the key-value pair and break if there is no more data to read
|
|
||||||
map_values = append(map_values, [2]I2PString{key_str, val_str})
|
|
||||||
if len(remainder) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the encountered key with arbitrary data
|
|
||||||
encounteredKeysMap[keyAsString] = true
|
|
||||||
}
|
|
||||||
values = &map_values
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"values_count": len(map_values),
|
|
||||||
"remainder_length": len(remainder_bytes),
|
|
||||||
"error_count": len(errs),
|
|
||||||
}).Debug("Finished reading MappingValues")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMappingOrderSortsValuesThenKeys(t *testing.T) {
|
|
||||||
a, _ := ToI2PString("a")
|
|
||||||
b, _ := ToI2PString("b")
|
|
||||||
aa, _ := ToI2PString("aa")
|
|
||||||
ab, _ := ToI2PString("ab")
|
|
||||||
ac, _ := ToI2PString("ac")
|
|
||||||
values := MappingValues{
|
|
||||||
[2]I2PString{b, b},
|
|
||||||
[2]I2PString{ac, a},
|
|
||||||
[2]I2PString{ab, b},
|
|
||||||
[2]I2PString{aa, a},
|
|
||||||
[2]I2PString{a, a},
|
|
||||||
}
|
|
||||||
mappingOrder(values)
|
|
||||||
for i, pair := range values {
|
|
||||||
key, _ := pair[0].Data()
|
|
||||||
switch i {
|
|
||||||
case 0:
|
|
||||||
if !(key == "a") {
|
|
||||||
t.Fatal(fmt.Sprintf("mappingOrder expected key a, got %s at index", key), i)
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
if !(key == "aa") {
|
|
||||||
t.Fatal(fmt.Sprintf("mappingOrder expected key aa, got %s at index", key), i)
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if !(key == "ab") {
|
|
||||||
t.Fatal(fmt.Sprintf("mappingOrder expected key ab, got %s at index", key), i)
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if !(key == "ac") {
|
|
||||||
t.Fatal(fmt.Sprintf("mappingOrder expected key ac, got %s at index", key), i)
|
|
||||||
}
|
|
||||||
case 4:
|
|
||||||
if !(key == "b") {
|
|
||||||
t.Fatal(fmt.Sprintf("mappingOrder expected key b, got %s at index", key), i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string
|
|
||||||
const STRING_MAX_SIZE = 255
|
|
||||||
|
|
||||||
/*
|
|
||||||
[I2P String]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
Represents a UTF-8 encoded string.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
1 or more bytes where the first byte is the number of bytes (not characters!) in the string
|
|
||||||
and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
|
|
||||||
Length limit is 255 bytes (not characters). Length may be 0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// I2PString is the represenation of an I2P String.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#string
|
|
||||||
type I2PString []byte
|
|
||||||
|
|
||||||
// Length returns the length specified in the first byte.
|
|
||||||
// Returns error if the specified does not match the actual length or the string is otherwise invalid.
|
|
||||||
func (str I2PString) Length() (length int, err error) {
|
|
||||||
if len(str) == 0 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(I2PString) Length",
|
|
||||||
"reason": "no data",
|
|
||||||
}).Error("error parsing string")
|
|
||||||
err = errors.New("error parsing string: zero length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l, _, err := NewInteger(str[:], 1)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to create Integer from I2PString")
|
|
||||||
return l.Int(), err
|
|
||||||
}
|
|
||||||
length = l.Int()
|
|
||||||
str_len := len(str)
|
|
||||||
if length > str_len {
|
|
||||||
/*log.WithFields(log.Fields{
|
|
||||||
"at": "(I2PString) Length",
|
|
||||||
"string_bytes_length": str_len,
|
|
||||||
"string_length_field": length,
|
|
||||||
"data": string(str),
|
|
||||||
"reason": "data less than specified by length",
|
|
||||||
}).Error("string format warning")*/
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(I2PString) Length",
|
|
||||||
"string_bytes_length": str_len,
|
|
||||||
"string_length_field": length,
|
|
||||||
"reason": "data less than specified by length",
|
|
||||||
}).Warn("string format warning")
|
|
||||||
err = errors.New("string parsing warning: string data is shorter than specified by length")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the I2PString content as a string trimmed to the specified length and not including the length byte.
|
|
||||||
// Returns error encountered by Length.
|
|
||||||
func (str I2PString) Data() (data string, err error) {
|
|
||||||
length, err := str.Length()
|
|
||||||
if err != nil {
|
|
||||||
switch err.Error() {
|
|
||||||
case "error parsing string: zero length":
|
|
||||||
log.WithError(err).Warn("Zero length I2PString")
|
|
||||||
return
|
|
||||||
case "string parsing warning: string data is shorter than specified by length":
|
|
||||||
log.WithError(err).Warn("I2PString data shorter than specified length")
|
|
||||||
if is, e := ToI2PString(string(str[:])); e != nil {
|
|
||||||
log.WithError(e).Error("Failed to convert short I2PString")
|
|
||||||
return "", e
|
|
||||||
} else {
|
|
||||||
return is.Data()
|
|
||||||
}
|
|
||||||
case "string parsing warning: string contains data beyond length":
|
|
||||||
log.WithError(err).Warn("I2PString contains data beyond specified length")
|
|
||||||
data = string(str[1:])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if length == 0 {
|
|
||||||
log.Debug("I2PString is empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data = string(str[1 : length+1])
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"data_length": len(data),
|
|
||||||
}).Debug("Retrieved I2PString data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToI2PString converts a Go string to an I2PString.
|
|
||||||
// Returns error if the string exceeds STRING_MAX_SIZE.
|
|
||||||
func ToI2PString(data string) (str I2PString, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Converting string to I2PString")
|
|
||||||
data_len := len(data)
|
|
||||||
if data_len > STRING_MAX_SIZE {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ToI2PI2PString",
|
|
||||||
"string_len": data_len,
|
|
||||||
"max_len": STRING_MAX_SIZE,
|
|
||||||
"reason": "too much data",
|
|
||||||
}).Error("cannot create I2P string")
|
|
||||||
err = errors.New("cannot store that much data in I2P string")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i2p_string := []byte{byte(data_len)}
|
|
||||||
i2p_string = append(i2p_string, []byte(data)...)
|
|
||||||
str = I2PString(i2p_string)
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"i2pstring_length": len(str),
|
|
||||||
}).Debug("Successfully converted string to I2PString")
|
|
||||||
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 I2PString.
|
|
||||||
//
|
|
||||||
|
|
||||||
// ReadI2PString returns I2PString from a []byte.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Reading I2PString from bytes")
|
|
||||||
length, _, err := NewInteger(data, 1)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to read I2PString length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data_len := length.Int() + 1
|
|
||||||
str = data[:data_len]
|
|
||||||
remainder = data[data_len:]
|
|
||||||
l, err := str.Length()
|
|
||||||
if l != data_len-1 {
|
|
||||||
err = fmt.Errorf("error reading I2P string, length does not match data")
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"expected_length": data_len - 1,
|
|
||||||
"actual_length": l,
|
|
||||||
}).Error("I2PString length mismatch")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"string_length": l,
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully read I2PString from bytes")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewI2PString creates a new *I2PString from []byte using ReadI2PString.
|
|
||||||
// Returns a pointer to I2PString unlike ReadI2PString.
|
|
||||||
/*func NewI2PString(data []byte) (str *I2PString, remainder []byte, err error) {
|
|
||||||
objstr, remainder, err := ReadI2PString(data)
|
|
||||||
str = &objstr
|
|
||||||
return
|
|
||||||
}*/
|
|
45
lib/common/date.go
Normal file
45
lib/common/date.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Date
|
||||||
|
https://geti2p.net/spec/common-structures#date
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Date [8]byte
|
||||||
|
|
||||||
|
const DATE_SIZE = 8
|
||||||
|
|
||||||
|
//
|
||||||
|
// Time takes the value stored in date as an 8 byte big-endian integer representing the
|
||||||
|
// number of milliseconds since the beginning of unix time and converts it to a Go time.Time
|
||||||
|
// struct.
|
||||||
|
//
|
||||||
|
func (date Date) Time() (date_time time.Time) {
|
||||||
|
seconds, _ := NewInteger(date[:])
|
||||||
|
date_time = time.Unix(0, int64(seconds.Value()*1000000))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadDate(data []byte) (h Date, remainder []byte, err error) {
|
||||||
|
if len(data) < DATE_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Date) ReadDate",
|
||||||
|
"data_len": len(data),
|
||||||
|
"required_len": "8",
|
||||||
|
"reason": "date missing data",
|
||||||
|
}).Error("date error")
|
||||||
|
err = errors.New("error reading date, insufficient length")
|
||||||
|
copy(h[:], data[0:len(data)-1])
|
||||||
|
} else {
|
||||||
|
copy(h[:], data[0:DATE_SIZE-1])
|
||||||
|
copy(remainder, data[DATE_SIZE-1:])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
package data
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTimeFromMilliseconds(t *testing.T) {
|
func TestTimeFromMiliseconds(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
next_day := Date{0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00}
|
next_day := Date{0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00}
|
62
lib/common/destination.go
Normal file
62
lib/common/destination.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Destination
|
||||||
|
https://geti2p.net/spec/common-structures#destination
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
Identical to KeysAndCert
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// A Destination is a KeysAndCert with functionallity
|
||||||
|
// for generating base32 and base64 addresses.
|
||||||
|
//
|
||||||
|
type Destination struct {
|
||||||
|
KeysAndCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (destination Destination) PublicKey() (crypto.PublicKey, error) {
|
||||||
|
return destination.KeysAndCert.GetPublicKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
||||||
|
return destination.KeysAndCert.GetSigningPublicKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (destination Destination) Certificate() (CertificateInterface, error) {
|
||||||
|
return destination.KeysAndCert.GetCertificate()
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate the I2P base32 address for this Destination.
|
||||||
|
//
|
||||||
|
func (destination Destination) Base32Address() (str string) {
|
||||||
|
hash := crypto.SHA256(destination.Cert())
|
||||||
|
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
|
||||||
|
str = str + ".b32.i2p"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate the I2P base64 address for this Destination.
|
||||||
|
//
|
||||||
|
func (destination Destination) Base64() string {
|
||||||
|
return base64.EncodeToString(destination.Cert())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
|
||||||
|
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
destination.KeysAndCert = keys_and_cert
|
||||||
|
return
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
// Package destination implements the I2P Destination common data structure
|
|
||||||
package destination
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
/*
|
|
||||||
[Destination]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
A Destination defines a particular endpoint to which messages can be directed for secure delivery.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
Identical to KeysAndCert.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Destination is the represenation of an I2P Destination.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#destination
|
|
||||||
type Destination struct {
|
|
||||||
KeysAndCert
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base32Address returns the I2P base32 address for this Destination.
|
|
||||||
func (destination Destination) Base32Address() (str string) {
|
|
||||||
log.Debug("Generating Base32 address for Destination")
|
|
||||||
|
|
||||||
dest := destination.KeysAndCert.KeyCertificate.Bytes()
|
|
||||||
hash := crypto.SHA256(dest)
|
|
||||||
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
|
|
||||||
str = str + ".b32.i2p"
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"base32_address": str,
|
|
||||||
}).Debug("Generated Base32 address for Destination")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 returns the I2P base64 address for this Destination.
|
|
||||||
func (destination Destination) Base64() string {
|
|
||||||
log.Debug("Generating Base64 address for Destination")
|
|
||||||
|
|
||||||
dest := destination.KeysAndCert.KeyCertificate.Bytes()
|
|
||||||
base64Address := base64.EncodeToString(dest)
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"base64_address_length": len(base64Address),
|
|
||||||
}).Debug("Generated Base64 address for Destination")
|
|
||||||
|
|
||||||
return base64Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDestination returns Destination from a []byte.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Reading Destination from bytes")
|
|
||||||
|
|
||||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
|
||||||
destination = Destination{
|
|
||||||
keys_and_cert,
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully read Destination from bytes")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
# destination
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/destination"
|
|
||||||
|
|
||||||
Package destination implements the I2P Destination common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### type Destination
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Destination struct {
|
|
||||||
KeysAndCert
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Destination is the represenation of an I2P Destination.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#destination
|
|
||||||
|
|
||||||
#### func ReadDestination
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadDestination returns Destination from a []byte. The remaining bytes after the
|
|
||||||
specified length are also returned. Returns a list of errors that occurred
|
|
||||||
during parsing.
|
|
||||||
|
|
||||||
#### func (Destination) Base32Address
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (destination Destination) Base32Address() (str string)
|
|
||||||
```
|
|
||||||
Base32Address returns the I2P base32 address for this Destination.
|
|
||||||
|
|
||||||
#### func (Destination) Base64
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (destination Destination) Base64() string
|
|
||||||
```
|
|
||||||
Base64 returns the I2P base64 address for this Destination.
|
|
@ -1,7 +1,8 @@
|
|||||||
FROM golang
|
FROM golang
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get upgrade -y
|
apt-get upgrade -y && \
|
||||||
|
apt-get install libsodium-dev -y
|
||||||
|
|
||||||
RUN go get github.com/dvyukov/go-fuzz/go-fuzz
|
RUN go get github.com/dvyukov/go-fuzz/go-fuzz
|
||||||
RUN go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
RUN go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/certificate"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,9 +1,9 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/certificate"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
cert, _, _ := common.ReadCertificate(data)
|
cert := common.Certificate(data)
|
||||||
cert.Data()
|
cert.Data()
|
||||||
cert.Length()
|
cert.Length()
|
||||||
cert.Type()
|
cert.Type()
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/destination"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,9 +1,9 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/destination"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
destination, _, _ := common.ReadDestination(data)
|
destination := common.Destination(data)
|
||||||
destination.Base32Address()
|
destination.Base32Address()
|
||||||
destination.Base64()
|
destination.Base64()
|
||||||
return 0
|
return 0
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/keys_and_cert"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,6 +1,6 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
keys_and_cert, _, _ := common.ReadKeysAndCert(data)
|
keys_and_cert, _, _ := common.ReadKeysAndCert(data)
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/router_address"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,6 +1,6 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/router_address"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
router_address, _, _ := common.ReadRouterAddress(data)
|
router_address, _, _ := common.ReadRouterAddress(data)
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/router_identity"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,6 +1,6 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/router_identity"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
router_identity, _, _ := common.ReadRouterIdentity(data)
|
router_identity, _, _ := common.ReadRouterIdentity(data)
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# exportable
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/fuzz/string"
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### func Fuzz
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Fuzz(data []byte) int
|
|
||||||
```
|
|
@ -1,9 +1,9 @@
|
|||||||
package exportable
|
package exportable
|
||||||
|
|
||||||
import common "github.com/go-i2p/go-i2p/lib/common/data"
|
import "github.com/go-i2p/go-i2p/lib/common"
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
func Fuzz(data []byte) int {
|
||||||
str := common.I2PString(data)
|
str, _, _ := common.ReadString(data)
|
||||||
str.Data()
|
str.Data()
|
||||||
str.Length()
|
str.Length()
|
||||||
str, _ = common.ToI2PString(string(data))
|
str, _ = common.ToI2PString(string(data))
|
||||||
|
48
lib/common/hash.go
Normal file
48
lib/common/hash.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const HASH_SIZE = 32
|
||||||
|
|
||||||
|
// sha256 hash of some data
|
||||||
|
type Hash [32]byte
|
||||||
|
|
||||||
|
// calculate sha256 of a byte slice
|
||||||
|
func HashData(data []byte) (h Hash) {
|
||||||
|
h = sha256.Sum256(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calulate sha256 of all data being read from an io.Reader
|
||||||
|
// return error if one occurs while reading from reader
|
||||||
|
func HashReader(r io.Reader) (h Hash, err error) {
|
||||||
|
sha := sha256.New()
|
||||||
|
_, err = io.Copy(sha, r)
|
||||||
|
if err == nil {
|
||||||
|
d := sha.Sum(nil)
|
||||||
|
copy(h[:], d)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadHash(data []byte) (h Hash, remainder []byte, err error) {
|
||||||
|
if len(data) < HASH_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Hash) ReadHash",
|
||||||
|
"data_len": len(data),
|
||||||
|
"required_len": "32",
|
||||||
|
"reason": "hash missing data",
|
||||||
|
}).Error("hash error")
|
||||||
|
err = errors.New("error reading hash, insufficient length")
|
||||||
|
copy(h[:], data[0:len(data)-1])
|
||||||
|
} else {
|
||||||
|
copy(h[:], data[0:HASH_SIZE-1])
|
||||||
|
copy(remainder, data[HASH_SIZE-1:])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
33
lib/common/ident.go
Normal file
33
lib/common/ident.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Tunnel Identity Helpers
|
||||||
|
https://geti2p.net/spec/common-structures#ident
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ident [4]byte
|
||||||
|
|
||||||
|
const IDENT_SIZE = 4
|
||||||
|
|
||||||
|
func ReadIdent(data []byte) (h Ident, remainder []byte, err error) {
|
||||||
|
if len(data) < IDENT_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Ident) ReadIdent",
|
||||||
|
"data_len": len(data),
|
||||||
|
"required_len": "8",
|
||||||
|
"reason": "ident missing data",
|
||||||
|
}).Error("ident error")
|
||||||
|
err = errors.New("error reading ident, insufficient length")
|
||||||
|
copy(h[:], data[0:len(data)-1])
|
||||||
|
} else {
|
||||||
|
copy(h[:], data[0:IDENT_SIZE-1])
|
||||||
|
copy(remainder, data[IDENT_SIZE-1:])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
83
lib/common/integer.go
Normal file
83
lib/common/integer.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Integer
|
||||||
|
https://geti2p.net/spec/common-structures#integer
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
// log "github.com/sirupsen/logrus"
|
||||||
|
// "errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Total byte length of an I2P integer
|
||||||
|
const (
|
||||||
|
INTEGER_SIZE = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
type Integer []byte
|
||||||
|
|
||||||
|
func (i *Integer) longBytes() (value [INTEGER_SIZE]byte) {
|
||||||
|
value = [INTEGER_SIZE]byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
pad := INTEGER_SIZE - len([]byte(*i))
|
||||||
|
for index, element := range []byte(*i) {
|
||||||
|
value[pad+index] = element
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) Value() int {
|
||||||
|
if i == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r := i.longBytes()
|
||||||
|
// log.Println("LONG BYTES", r)
|
||||||
|
return int(binary.BigEndian.Uint64(r[:]))
|
||||||
|
// return int(binary.BigEndian.Int64(r[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) Bytes() []byte {
|
||||||
|
if i == nil {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
if len([]byte(*i)) == 0 {
|
||||||
|
return []byte{0}
|
||||||
|
}
|
||||||
|
r := []byte(*i)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
|
||||||
|
// integer and return an int representation.
|
||||||
|
//
|
||||||
|
func NewInteger(number []byte) (value *Integer, err error) {
|
||||||
|
var integer Integer = number
|
||||||
|
value = &integer //[INTEGER_SIZE]byte(number)
|
||||||
|
// for index, element := range number {
|
||||||
|
// value[INTEGER_SIZE-1-index] = element
|
||||||
|
// }
|
||||||
|
/*length := len(number)
|
||||||
|
if length < INTEGER_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Integer) NewInteger",
|
||||||
|
"length": length,
|
||||||
|
"required_len": INTEGER_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing Integer")
|
||||||
|
err = errors.New("error parsing Integer, not enough data")
|
||||||
|
}else if length > INTEGER_SIZE{
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Integer) NewInteger",
|
||||||
|
"length": length,
|
||||||
|
"required_len": INTEGER_SIZE,
|
||||||
|
"reason": "too much data",
|
||||||
|
}).Error("error parsing Integer")
|
||||||
|
err = errors.New("error parsing Integer, too much data")
|
||||||
|
}else{
|
||||||
|
err = nil
|
||||||
|
}*/
|
||||||
|
return
|
||||||
|
}
|
34
lib/common/integer_test.go
Normal file
34
lib/common/integer_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntegerBigEndian(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
bytes := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||||
|
integer, err := NewInteger(bytes)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
assert.Equal(integer.Value(), 1, "Integer() did not parse bytes big endian")
|
||||||
|
|
||||||
|
checkbytes := integer.Bytes()
|
||||||
|
|
||||||
|
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorksWithOneByte(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
bytes := []byte{0x00}
|
||||||
|
integer, err := NewInteger(bytes)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
assert.Equal(integer.Value(), 0, "Integer() did not correctly parse single byte slice")
|
||||||
|
|
||||||
|
checkbytes := integer.Bytes()
|
||||||
|
|
||||||
|
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
|
||||||
|
}
|
303
lib/common/key_certificate.go
Normal file
303
lib/common/key_certificate.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Key Certificate
|
||||||
|
https://geti2p.net/spec/common-structures#certificate
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+-//
|
||||||
|
|type| length | payload
|
||||||
|
+----+----+----+----+----+-//
|
||||||
|
|
||||||
|
type :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
|
||||||
|
case 0 -> NULL
|
||||||
|
case 1 -> HASHCASH
|
||||||
|
case 2 -> HIDDEN
|
||||||
|
case 3 -> SIGNED
|
||||||
|
case 4 -> MULTIPLE
|
||||||
|
case 5 -> KEY
|
||||||
|
|
||||||
|
length :: Integer
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
payload :: data
|
||||||
|
length -> $length bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/go-i2p/go-i2p/lib/crypto"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key Certificate Signing Key Types
|
||||||
|
const (
|
||||||
|
KEYCERT_SIGN_DSA_SHA1 = iota
|
||||||
|
KEYCERT_SIGN_P256
|
||||||
|
KEYCERT_SIGN_P384
|
||||||
|
KEYCERT_SIGN_P521
|
||||||
|
KEYCERT_SIGN_RSA2048
|
||||||
|
KEYCERT_SIGN_RSA3072
|
||||||
|
KEYCERT_SIGN_RSA4096
|
||||||
|
KEYCERT_SIGN_ED25519
|
||||||
|
KEYCERT_SIGN_ED25519PH
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key Certificate Public Key Types
|
||||||
|
const (
|
||||||
|
KEYCERT_CRYPTO_ELG = iota
|
||||||
|
KEYCERT_CRYPTO_P256
|
||||||
|
KEYCERT_CRYPTO_P384
|
||||||
|
KEYCERT_CRYPTO_P521
|
||||||
|
KEYCERT_CRYPTO_X25519
|
||||||
|
)
|
||||||
|
|
||||||
|
// SigningPublicKey sizes for Signing Key Types
|
||||||
|
const (
|
||||||
|
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
|
||||||
|
KEYCERT_SIGN_P256_SIZE = 64
|
||||||
|
KEYCERT_SIGN_P384_SIZE = 96
|
||||||
|
KEYCERT_SIGN_P521_SIZE = 132
|
||||||
|
KEYCERT_SIGN_RSA2048_SIZE = 256
|
||||||
|
KEYCERT_SIGN_RSA3072_SIZE = 384
|
||||||
|
KEYCERT_SIGN_RSA4096_SIZE = 512
|
||||||
|
KEYCERT_SIGN_ED25519_SIZE = 32
|
||||||
|
KEYCERT_SIGN_ED25519PH_SIZE = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicKey sizes for Public Key Types
|
||||||
|
const (
|
||||||
|
KEYCERT_CRYPTO_ELG_SIZE = 256
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sizes of structures in KeyCertificates
|
||||||
|
const (
|
||||||
|
KEYCERT_PUBKEY_SIZE = 256
|
||||||
|
KEYCERT_SPK_SIZE = 128
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KEYCERT_MIN_SIZE = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyCertificate struct {
|
||||||
|
CertificateInterface
|
||||||
|
PKType *Integer
|
||||||
|
PKExtra []byte
|
||||||
|
SPKType *Integer
|
||||||
|
SPKExtra []byte
|
||||||
|
} //[]byte
|
||||||
|
|
||||||
|
//
|
||||||
|
// The data contained in the Key Certificate.
|
||||||
|
//
|
||||||
|
func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
||||||
|
var r []byte
|
||||||
|
r = append(r, key_certificate.CertificateInterface.Cert()...)
|
||||||
|
r = append(r, key_certificate.PKType.Bytes()...)
|
||||||
|
r = append(r, key_certificate.SPKType.Bytes()...)
|
||||||
|
return r, 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) {
|
||||||
|
// signing_key_type := key_certificate.SPKType
|
||||||
|
// data_len := len(key_certificate.CertificateInterface.CertBytes)
|
||||||
|
if len(key_certificate.SPKType.Bytes()) < 2 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) SigningPublicKeyType",
|
||||||
|
"data_len": len(key_certificate.SPKType.Bytes()),
|
||||||
|
"required_len": 2,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error retrieving Signing Public Key type")
|
||||||
|
err = errors.New("error retrieving signing public key type: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Signing Public Key Type", key_certificate.SPKType) //.Value())
|
||||||
|
return key_certificate.SPKType.Value(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// The PublicKey type this Key Certificate describes and any errors encountered parsing
|
||||||
|
// this KeyCertificate.
|
||||||
|
//
|
||||||
|
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
|
||||||
|
if len(key_certificate.PKType.Bytes()) < 2 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) SingingPublicKeyType",
|
||||||
|
"data_len": len(key_certificate.PKType.Bytes()),
|
||||||
|
"required_len": 2,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error retrieving Singning Public Key type")
|
||||||
|
err = errors.New("error retrieving signing public key type: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Public Key Type", key_certificate.PKType)
|
||||||
|
return key_certificate.PKType.Value(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Given some bytes, build a PublicKey using any excess data that may be stored in the KeyCertificate and return
|
||||||
|
// 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.SigningPublicKeyType()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < KEYCERT_PUBKEY_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) ConstructPublicKey",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYCERT_PUBKEY_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error constructing public key")
|
||||||
|
err = errors.New("error constructing public key: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch key_type {
|
||||||
|
case KEYCERT_CRYPTO_ELG:
|
||||||
|
var elg_key crypto.ElgPublicKey
|
||||||
|
copy(elg_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
|
||||||
|
public_key = elg_key
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Given some bytes, build a SigningPublicKey using any excess data that may be stored in the KeyCertificate and return
|
||||||
|
// 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()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < KEYCERT_SPK_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) ConstructSigningPublicKey",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYCERT_SPK_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error constructing signing public key")
|
||||||
|
err = errors.New("error constructing signing public key: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch signing_key_type {
|
||||||
|
case KEYCERT_SIGN_DSA_SHA1:
|
||||||
|
var dsa_key crypto.DSAPublicKey
|
||||||
|
copy(dsa_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_DSA_SHA1_SIZE:KEYCERT_SPK_SIZE])
|
||||||
|
signing_public_key = dsa_key
|
||||||
|
case KEYCERT_SIGN_P256:
|
||||||
|
var ec_key crypto.ECP256PublicKey
|
||||||
|
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P256_SIZE:KEYCERT_SPK_SIZE])
|
||||||
|
signing_public_key = ec_key
|
||||||
|
case KEYCERT_SIGN_P384:
|
||||||
|
var ec_key crypto.ECP384PublicKey
|
||||||
|
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P384_SIZE:KEYCERT_SPK_SIZE])
|
||||||
|
signing_public_key = ec_key
|
||||||
|
case KEYCERT_SIGN_P521:
|
||||||
|
var ec_key crypto.ECP521PublicKey
|
||||||
|
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
|
||||||
|
copy(ec_key[:], data)
|
||||||
|
d, _ := key_certificate.Data()
|
||||||
|
copy(ec_key[KEYCERT_SPK_SIZE:], d[4:4+extra])
|
||||||
|
signing_public_key = ec_key
|
||||||
|
case KEYCERT_SIGN_RSA2048:
|
||||||
|
//var rsa_key crypto.RSA2048PublicKey
|
||||||
|
//extra := KEYCERT_SIGN_RSA2048_SIZE - 128
|
||||||
|
//copy(rsa_key[:], data)
|
||||||
|
//copy(rsa_key[128:], key_certificate[4:4+extra])
|
||||||
|
//signing_public_key = rsa_key
|
||||||
|
case KEYCERT_SIGN_RSA3072:
|
||||||
|
case KEYCERT_SIGN_RSA4096:
|
||||||
|
case KEYCERT_SIGN_ED25519:
|
||||||
|
case KEYCERT_SIGN_ED25519PH:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the size of a Signature corresponding to the Key Certificate's
|
||||||
|
// SigningPublicKey type.
|
||||||
|
//
|
||||||
|
func (key_certificate KeyCertificate) SignatureSize() (size int) {
|
||||||
|
sizes := map[int]int{
|
||||||
|
KEYCERT_SIGN_DSA_SHA1: 40,
|
||||||
|
KEYCERT_SIGN_P256: 64,
|
||||||
|
KEYCERT_SIGN_P384: 96,
|
||||||
|
KEYCERT_SIGN_P521: 132,
|
||||||
|
KEYCERT_SIGN_RSA2048: 256,
|
||||||
|
KEYCERT_SIGN_RSA3072: 384,
|
||||||
|
KEYCERT_SIGN_RSA4096: 512,
|
||||||
|
KEYCERT_SIGN_ED25519: 64,
|
||||||
|
KEYCERT_SIGN_ED25519PH: 64,
|
||||||
|
}
|
||||||
|
key_type, err := 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)]
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read a KeyCertificate from a slice of bytes
|
||||||
|
//
|
||||||
|
func ReadKeyCertificate(data []byte) (key_certificate KeyCertificate, err error) {
|
||||||
|
key_certificate.SPKType = &Integer{}
|
||||||
|
key_certificate.PKType = &Integer{}
|
||||||
|
cert, remainder, err := ReadCertificate(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cert_type, _, err := cert.Type()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("KEYSANDCERT CERT TYPE=", cert_type, cert.CertBytes, remainder)
|
||||||
|
key_certificate.CertificateInterface = cert
|
||||||
|
data = cert.Cert()
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < KEYCERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) PublicKeyType",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYCERT_MIN_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing key certificate public key")
|
||||||
|
err = errors.New("error parsing key certificate public key: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("KEYSANDCERT=", data, "| len=", data_len, "| 0=", data[0], "| 1=", data[1])
|
||||||
|
key_certificate.SPKType, err = NewInteger(data[len(data)-2 : len(data)])
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) SigningPublicKeyType",
|
||||||
|
"key_type": key_certificate.PKType,
|
||||||
|
"reason": "failed to read signing public key type",
|
||||||
|
}).Error("error parsing key certificate signing public key")
|
||||||
|
}
|
||||||
|
key_certificate.PKType, err = NewInteger(data[len(data)-4 : len(data)-2])
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) PublicKeyType",
|
||||||
|
"key_type": key_certificate.PKType,
|
||||||
|
"reason": "failed to read public key type",
|
||||||
|
}).Error("error parsing key certificate public key")
|
||||||
|
err = errors.New("error parsing key certificate public key: not enough data")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -1,154 +0,0 @@
|
|||||||
# key_certificate
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/key_certificate"
|
|
||||||
|
|
||||||
Package key_certificate implements the I2P Destination common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_SIGN_DSA_SHA1 = iota
|
|
||||||
KEYCERT_SIGN_P256
|
|
||||||
KEYCERT_SIGN_P384
|
|
||||||
KEYCERT_SIGN_P521
|
|
||||||
KEYCERT_SIGN_RSA2048
|
|
||||||
KEYCERT_SIGN_RSA3072
|
|
||||||
KEYCERT_SIGN_RSA4096
|
|
||||||
KEYCERT_SIGN_ED25519
|
|
||||||
KEYCERT_SIGN_ED25519PH
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Key Certificate Signing Key Types
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_CRYPTO_ELG = iota
|
|
||||||
KEYCERT_CRYPTO_P256
|
|
||||||
KEYCERT_CRYPTO_P384
|
|
||||||
KEYCERT_CRYPTO_P521
|
|
||||||
KEYCERT_CRYPTO_X25519
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Key Certificate Public Key Types
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
|
|
||||||
KEYCERT_SIGN_P256_SIZE = 64
|
|
||||||
KEYCERT_SIGN_P384_SIZE = 96
|
|
||||||
KEYCERT_SIGN_P521_SIZE = 132
|
|
||||||
KEYCERT_SIGN_RSA2048_SIZE = 256
|
|
||||||
KEYCERT_SIGN_RSA3072_SIZE = 384
|
|
||||||
KEYCERT_SIGN_RSA4096_SIZE = 512
|
|
||||||
KEYCERT_SIGN_ED25519_SIZE = 32
|
|
||||||
KEYCERT_SIGN_ED25519PH_SIZE = 32
|
|
||||||
)
|
|
||||||
```
|
|
||||||
SigningPublicKey sizes for Signing Key Types
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_CRYPTO_ELG_SIZE = 256
|
|
||||||
KEYCERT_CRYPTO_P256_SIZE = 64
|
|
||||||
KEYCERT_CRYPTO_P384_SIZE = 96
|
|
||||||
KEYCERT_CRYPTO_P521_SIZE = 132
|
|
||||||
KEYCERT_CRYPTO_X25519_SIZE = 32
|
|
||||||
)
|
|
||||||
```
|
|
||||||
PublicKey sizes for Public Key Types
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_PUBKEY_SIZE = 256
|
|
||||||
KEYCERT_SPK_SIZE = 128
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Sizes of structures in KeyCertificates
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYCERT_MIN_SIZE = 7
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### type KeyCertificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
type KeyCertificate struct {
|
|
||||||
Certificate
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
type KeyCertificate []byte
|
|
||||||
|
|
||||||
#### func KeyCertificateFromCertificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func KeyCertificateFromCertificate(certificate Certificate) *KeyCertificate
|
|
||||||
```
|
|
||||||
KeyCertificateFromCertificate returns a *KeyCertificate from a *Certificate.
|
|
||||||
|
|
||||||
#### func NewKeyCertificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
NewKeyCertificate creates a new *KeyCertificate from []byte using
|
|
||||||
ReadCertificate. The remaining bytes after the specified length are also
|
|
||||||
returned. Returns a list of errors that occurred during parsing.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) ConstructPublicKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error)
|
|
||||||
```
|
|
||||||
ConstructPublicKey returns a PublicKey constructed using any excess data that
|
|
||||||
may be stored in the KeyCertififcate. Returns enr errors encountered while
|
|
||||||
parsing.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) ConstructSigningPublicKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error)
|
|
||||||
```
|
|
||||||
ConstructSigningPublicKey returns a SingingPublicKey constructed using any
|
|
||||||
excess data that may be stored in the KeyCertificate. Returns any errors
|
|
||||||
encountered while parsing.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) CryptoSize
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) CryptoSize() (size int)
|
|
||||||
```
|
|
||||||
CryptoSize return the size of a Public Key corresponding to the Key
|
|
||||||
Certificate's PublicKey type.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) Data
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) Data() ([]byte, error)
|
|
||||||
```
|
|
||||||
Data returns the raw []byte contained in the Certificate.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) PublicKeyType
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int)
|
|
||||||
```
|
|
||||||
PublicKeyType returns the PublicKey type as a Go integer.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) SignatureSize
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) SignatureSize() (size int)
|
|
||||||
```
|
|
||||||
SignatureSize return the size of a Signature corresponding to the Key
|
|
||||||
Certificate's SigningPublicKey type.
|
|
||||||
|
|
||||||
#### func (KeyCertificate) SigningPublicKeyType
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int)
|
|
||||||
```
|
|
||||||
SigningPublicKeyType returns the SigningPublicKey type as a Go integer.
|
|
@ -1,339 +0,0 @@
|
|||||||
// Package key_certificate implements the I2P Destination common data structure
|
|
||||||
package key_certificate
|
|
||||||
|
|
||||||
/*
|
|
||||||
I2P Key Certificate
|
|
||||||
https://geti2p.net/spec/common-structures#certificate
|
|
||||||
Accurate for version 0.9.24
|
|
||||||
|
|
||||||
+----+----+----+----+----+-//
|
|
||||||
|type| length | payload
|
|
||||||
+----+----+----+----+----+-//
|
|
||||||
|
|
||||||
type :: Integer
|
|
||||||
length -> 1 byte
|
|
||||||
|
|
||||||
case 0 -> NULL
|
|
||||||
case 1 -> HASHCASH
|
|
||||||
case 2 -> HIDDEN
|
|
||||||
case 3 -> SIGNED
|
|
||||||
case 4 -> MULTIPLE
|
|
||||||
case 5 -> KEY
|
|
||||||
|
|
||||||
length :: Integer
|
|
||||||
length -> 2 bytes
|
|
||||||
|
|
||||||
payload :: data
|
|
||||||
length -> $length bytes
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/certificate"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
// Key Certificate Signing Key Types
|
|
||||||
const (
|
|
||||||
KEYCERT_SIGN_DSA_SHA1 = iota
|
|
||||||
KEYCERT_SIGN_P256
|
|
||||||
KEYCERT_SIGN_P384
|
|
||||||
KEYCERT_SIGN_P521
|
|
||||||
KEYCERT_SIGN_RSA2048
|
|
||||||
KEYCERT_SIGN_RSA3072
|
|
||||||
KEYCERT_SIGN_RSA4096
|
|
||||||
KEYCERT_SIGN_ED25519
|
|
||||||
KEYCERT_SIGN_ED25519PH
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key Certificate Public Key Types
|
|
||||||
const (
|
|
||||||
KEYCERT_CRYPTO_ELG = iota
|
|
||||||
KEYCERT_CRYPTO_P256
|
|
||||||
KEYCERT_CRYPTO_P384
|
|
||||||
KEYCERT_CRYPTO_P521
|
|
||||||
KEYCERT_CRYPTO_X25519
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
KEYCERT_MIN_SIZE = 7
|
|
||||||
)
|
|
||||||
|
|
||||||
// SigningPublicKey sizes for Signing Key Types
|
|
||||||
const (
|
|
||||||
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
|
|
||||||
KEYCERT_SIGN_P256_SIZE = 64
|
|
||||||
KEYCERT_SIGN_P384_SIZE = 96
|
|
||||||
KEYCERT_SIGN_P521_SIZE = 132
|
|
||||||
KEYCERT_SIGN_RSA2048_SIZE = 256
|
|
||||||
KEYCERT_SIGN_RSA3072_SIZE = 384
|
|
||||||
KEYCERT_SIGN_RSA4096_SIZE = 512
|
|
||||||
KEYCERT_SIGN_ED25519_SIZE = 32
|
|
||||||
KEYCERT_SIGN_ED25519PH_SIZE = 32
|
|
||||||
)
|
|
||||||
|
|
||||||
// PublicKey sizes for Public Key Types
|
|
||||||
const (
|
|
||||||
KEYCERT_CRYPTO_ELG_SIZE = 256
|
|
||||||
KEYCERT_CRYPTO_P256_SIZE = 64
|
|
||||||
KEYCERT_CRYPTO_P384_SIZE = 96
|
|
||||||
KEYCERT_CRYPTO_P521_SIZE = 132
|
|
||||||
KEYCERT_CRYPTO_X25519_SIZE = 32
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sizes of structures in KeyCertificates
|
|
||||||
const (
|
|
||||||
KEYCERT_PUBKEY_SIZE = 256
|
|
||||||
KEYCERT_SPK_SIZE = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
// type KeyCertificate []byte
|
|
||||||
type KeyCertificate struct {
|
|
||||||
Certificate
|
|
||||||
spkType Integer
|
|
||||||
cpkType Integer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the raw []byte contained in the Certificate.
|
|
||||||
func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
|
||||||
data := key_certificate.Certificate.RawBytes()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"data_length": len(data),
|
|
||||||
}).Debug("Retrieved raw data from KeyCertificate")
|
|
||||||
return key_certificate.Certificate.RawBytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SigningPublicKeyType returns the SigningPublicKey type as a Go integer.
|
|
||||||
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int) {
|
|
||||||
signing_pubkey_type = key_certificate.spkType.Int()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_pubkey_type": signing_pubkey_type,
|
|
||||||
}).Debug("Retrieved SigningPublicKey type")
|
|
||||||
return key_certificate.spkType.Int()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKeyType returns the PublicKey type as a Go integer.
|
|
||||||
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int) {
|
|
||||||
pubkey_type = key_certificate.cpkType.Int()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"pubkey_type": pubkey_type,
|
|
||||||
}).Debug("Retrieved PublicKey type")
|
|
||||||
return key_certificate.cpkType.Int()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConstructPublicKey returns a PublicKey constructed using any excess data that may be stored in the KeyCertififcate.
|
|
||||||
// Returns enr errors encountered while parsing.
|
|
||||||
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Constructing PublicKey from KeyCertificate")
|
|
||||||
key_type := key_certificate.PublicKeyType()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data_len := len(data)
|
|
||||||
if data_len < key_certificate.CryptoSize() {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(KeyCertificate) ConstructPublicKey",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": KEYCERT_PUBKEY_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error constructing public key")
|
|
||||||
err = errors.New("error constructing public key: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch key_type {
|
|
||||||
case KEYCERT_CRYPTO_ELG:
|
|
||||||
var elg_key crypto.ElgPublicKey
|
|
||||||
copy(elg_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
|
|
||||||
public_key = elg_key
|
|
||||||
log.Debug("Constructed ElgPublicKey")
|
|
||||||
case KEYCERT_CRYPTO_X25519:
|
|
||||||
var ed25519_key crypto.Ed25519PublicKey
|
|
||||||
copy(ed25519_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
|
|
||||||
public_key = ed25519_key
|
|
||||||
log.Debug("Constructed Ed25519PublicKey")
|
|
||||||
default:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key_type": key_type,
|
|
||||||
}).Warn("Unknown public key type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConstructSigningPublicKey returns a SingingPublicKey constructed using any excess data that may be stored in the KeyCertificate.
|
|
||||||
// Returns any errors encountered while parsing.
|
|
||||||
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Constructing SigningPublicKey from KeyCertificate")
|
|
||||||
signing_key_type := key_certificate.PublicKeyType()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data_len := len(data)
|
|
||||||
if data_len < key_certificate.SignatureSize() {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(KeyCertificate) ConstructSigningPublicKey",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": KEYCERT_SPK_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error constructing signing public key")
|
|
||||||
err = errors.New("error constructing signing public key: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch signing_key_type {
|
|
||||||
case KEYCERT_SIGN_DSA_SHA1:
|
|
||||||
var dsa_key crypto.DSAPublicKey
|
|
||||||
copy(dsa_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_DSA_SHA1_SIZE:KEYCERT_SPK_SIZE])
|
|
||||||
signing_public_key = dsa_key
|
|
||||||
log.Debug("Constructed DSAPublicKey")
|
|
||||||
case KEYCERT_SIGN_P256:
|
|
||||||
var ec_key crypto.ECP256PublicKey
|
|
||||||
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P256_SIZE:KEYCERT_SPK_SIZE])
|
|
||||||
signing_public_key = ec_key
|
|
||||||
log.Debug("Constructed ECP256PublicKey")
|
|
||||||
case KEYCERT_SIGN_P384:
|
|
||||||
var ec_key crypto.ECP384PublicKey
|
|
||||||
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P384_SIZE:KEYCERT_SPK_SIZE])
|
|
||||||
signing_public_key = ec_key
|
|
||||||
log.Debug("Constructed ECP384PublicKey")
|
|
||||||
case KEYCERT_SIGN_P521:
|
|
||||||
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.Certificate.RawBytes()[4:4+extra])
|
|
||||||
signing_public_key = ec_key
|
|
||||||
log.Debug("Constructed ECP521PublicKey")
|
|
||||||
case KEYCERT_SIGN_RSA2048:
|
|
||||||
// var rsa_key crypto.RSA2048PublicKey
|
|
||||||
// extra := KEYCERT_SIGN_RSA2048_SIZE - 128
|
|
||||||
// copy(rsa_key[:], data)
|
|
||||||
// copy(rsa_key[128:], key_certificate[4:4+extra])
|
|
||||||
// signing_public_key = rsa_key
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Signing key type KEYCERT_SIGN_RSA2048 not implemented")
|
|
||||||
case KEYCERT_SIGN_RSA3072:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Signing key type KEYCERT_SIGN_RSA3072 not implemented")
|
|
||||||
case KEYCERT_SIGN_RSA4096:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Signing key type KEYCERT_SIGN_RSA4096 not implemented")
|
|
||||||
case KEYCERT_SIGN_ED25519:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Signing key type KEYCERT_SIGN_ED25519 not implemented")
|
|
||||||
case KEYCERT_SIGN_ED25519PH:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Signing key type KEYCERT_SIGN_ED25519PH not implemented")
|
|
||||||
default:
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"signing_key_type": signing_key_type,
|
|
||||||
}).Warn("Unknown signing key type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignatureSize return the size of a Signature corresponding to the Key Certificate's SigningPublicKey type.
|
|
||||||
func (key_certificate KeyCertificate) SignatureSize() (size int) {
|
|
||||||
sizes := map[int]int{
|
|
||||||
KEYCERT_SIGN_DSA_SHA1: KEYCERT_SIGN_DSA_SHA1_SIZE,
|
|
||||||
KEYCERT_SIGN_P256: KEYCERT_SIGN_P256_SIZE,
|
|
||||||
KEYCERT_SIGN_P384: KEYCERT_SIGN_P384_SIZE,
|
|
||||||
KEYCERT_SIGN_P521: KEYCERT_SIGN_P521_SIZE,
|
|
||||||
KEYCERT_SIGN_RSA2048: KEYCERT_SIGN_RSA2048_SIZE,
|
|
||||||
KEYCERT_SIGN_RSA3072: KEYCERT_SIGN_RSA3072_SIZE,
|
|
||||||
KEYCERT_SIGN_RSA4096: KEYCERT_SIGN_RSA4096_SIZE,
|
|
||||||
KEYCERT_SIGN_ED25519: KEYCERT_SIGN_ED25519_SIZE,
|
|
||||||
KEYCERT_SIGN_ED25519PH: KEYCERT_SIGN_ED25519PH_SIZE,
|
|
||||||
}
|
|
||||||
key_type := key_certificate.SigningPublicKeyType()
|
|
||||||
size = sizes[int(key_type)]
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key_type": key_type,
|
|
||||||
"signature_size": size,
|
|
||||||
}).Debug("Retrieved signature size")
|
|
||||||
return sizes[int(key_type)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// CryptoSize return the size of a Public Key corresponding to the Key Certificate's PublicKey type.
|
|
||||||
func (key_certificate KeyCertificate) CryptoSize() (size int) {
|
|
||||||
sizes := map[int]int{
|
|
||||||
KEYCERT_CRYPTO_ELG: KEYCERT_CRYPTO_ELG_SIZE,
|
|
||||||
KEYCERT_CRYPTO_P256: KEYCERT_CRYPTO_P256_SIZE,
|
|
||||||
KEYCERT_CRYPTO_P384: KEYCERT_CRYPTO_P384_SIZE,
|
|
||||||
KEYCERT_CRYPTO_P521: KEYCERT_CRYPTO_P521_SIZE,
|
|
||||||
KEYCERT_CRYPTO_X25519: KEYCERT_CRYPTO_X25519_SIZE,
|
|
||||||
}
|
|
||||||
key_type := key_certificate.PublicKeyType()
|
|
||||||
size = sizes[int(key_type)]
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"key_type": key_type,
|
|
||||||
"crypto_size": size,
|
|
||||||
}).Debug("Retrieved crypto size")
|
|
||||||
return sizes[int(key_type)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeyCertificate creates a new *KeyCertificate from []byte using ReadCertificate.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder []byte, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(bytes),
|
|
||||||
}).Debug("Creating new KeyCertificate")
|
|
||||||
|
|
||||||
var certificate Certificate
|
|
||||||
certificate, remainder, err = ReadCertificate(bytes)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to read Certificate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(bytes) < KEYCERT_MIN_SIZE {
|
|
||||||
err = errors.New("error parsing key certificate: not enough data")
|
|
||||||
remainder = bytes[KEYCERT_MIN_SIZE:]
|
|
||||||
log.WithError(err).Error("KeyCertificate data too short")
|
|
||||||
}
|
|
||||||
key_certificate = &KeyCertificate{
|
|
||||||
Certificate: certificate,
|
|
||||||
}
|
|
||||||
if len(bytes) >= 5 {
|
|
||||||
key_certificate.spkType = Integer(bytes[4:5])
|
|
||||||
}
|
|
||||||
if len(bytes) >= 7 {
|
|
||||||
key_certificate.cpkType = Integer(bytes[6:7])
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"spk_type": key_certificate.spkType.Int(),
|
|
||||||
"cpk_type": key_certificate.cpkType.Int(),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully created new KeyCertificate")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyCertificateFromCertificate returns a *KeyCertificate from a *Certificate.
|
|
||||||
func KeyCertificateFromCertificate(certificate Certificate) *KeyCertificate {
|
|
||||||
log.Debug("Creating KeyCertificate from Certificate")
|
|
||||||
// k, _, _ := NewKeyCertificate(certificate.RawBytes())
|
|
||||||
k, _, err := NewKeyCertificate(certificate.RawBytes())
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to create KeyCertificate from Certificate")
|
|
||||||
} else {
|
|
||||||
log.Debug("Successfully created KeyCertificate from Certificate")
|
|
||||||
}
|
|
||||||
return k
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package key_certificate
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -9,52 +9,60 @@ import (
|
|||||||
func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
||||||
pk_type := key_cert.SigningPublicKeyType()
|
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||||
|
spk_type, err := key_cert.SigningPublicKeyType()
|
||||||
|
|
||||||
assert.Nil(err, "SigningPublicKeyType() returned error with valid data")
|
assert.Nil(err, "SigningPublicKeyType() returned error with valid data")
|
||||||
assert.Equal(pk_type, KEYCERT_SIGN_P521, "SigningPublicKeyType() did not return correct typec")
|
assert.Equal(spk_type, KEYCERT_SIGN_DSA_SHA1, "SigningPublicKeyType() did not return correct type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSingingPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
|
||||||
sk_type := key_cert.SigningPublicKeyType()
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
|
||||||
assert.Equal(sk_type, 0, "SigningPublicKeyType() did not return correct typec")
|
}
|
||||||
|
// assert.NotNil(err, "ReadKeyCertificate() returned error with valid data")
|
||||||
|
_, err = key_cert.PublicKeyType()
|
||||||
|
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
|
assert.Equal("error retrieving signing public key type: not enough data", err.Error(), "correct error message should be returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
||||||
pk_type := key_cert.PublicKeyType()
|
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||||
|
pk_type, err := key_cert.PublicKeyType()
|
||||||
|
|
||||||
assert.Nil(err, "PublicKey() returned error with valid data")
|
assert.Nil(err, "PublicKeyType() returned error with valid data")
|
||||||
assert.Equal(pk_type, KEYCERT_SIGN_P521, "PublicKeyType() did not return correct typec")
|
assert.Equal(pk_type, KEYCERT_CRYPTO_P521, "PublicKeyType() did not return correct type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
func TestSigningPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
|
||||||
pk_type := key_cert.PublicKeyType()
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
|
||||||
|
}
|
||||||
|
_, err = key_cert.SigningPublicKeyType()
|
||||||
|
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
|
assert.Equal("error retrieving signing public key type: 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) {
|
func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||||
data := make([]byte, 255)
|
data := make([]byte, 255)
|
||||||
_, err = key_cert.ConstructPublicKey(data)
|
_, err = key_cert.ConstructPublicKey(data)
|
||||||
|
|
||||||
@ -62,22 +70,25 @@ func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
|||||||
assert.Equal("error constructing public key: not enough data", err.Error(), "correct error message should be returned")
|
assert.Equal("error constructing public key: not enough data", err.Error(), "correct error message should be returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
|
func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||||
data := make([]byte, 256)
|
data := make([]byte, 256)
|
||||||
pk, err := key_cert.ConstructPublicKey(data)
|
pk, err := key_cert.ConstructPublicKey(data)
|
||||||
|
|
||||||
assert.Nil(err, "ConstructPublicKey() returned error with valid data")
|
assert.Nil(err, "ConstructPublicKey() returned error with valid data")
|
||||||
assert.Equal(pk.Len(), 256, "ConstructPublicKey() did not return public key with correct length")
|
assert.Equal(pk.Len(), 256, "ConstructPublicKey() did not return public key with correct length")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||||
data := make([]byte, 127)
|
data := make([]byte, 127)
|
||||||
_, err = key_cert.ConstructSigningPublicKey(data)
|
_, err = key_cert.ConstructSigningPublicKey(data)
|
||||||
|
|
||||||
@ -89,7 +100,7 @@ func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
|||||||
func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
|
func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||||
data := make([]byte, 128)
|
data := make([]byte, 128)
|
||||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||||
|
|
||||||
@ -100,7 +111,7 @@ func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
|
|||||||
func TestConstructSigningPublicKeyWithP256(t *testing.T) {
|
func TestConstructSigningPublicKeyWithP256(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
|
||||||
data := make([]byte, 128)
|
data := make([]byte, 128)
|
||||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||||
|
|
||||||
@ -111,7 +122,7 @@ func TestConstructSigningPublicKeyWithP256(t *testing.T) {
|
|||||||
func TestConstructSigningPublicKeyWithP384(t *testing.T) {
|
func TestConstructSigningPublicKeyWithP384(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
|
||||||
data := make([]byte, 128)
|
data := make([]byte, 128)
|
||||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||||
|
|
||||||
@ -122,10 +133,11 @@ func TestConstructSigningPublicKeyWithP384(t *testing.T) {
|
|||||||
func TestConstructSigningPublicKeyWithP521(t *testing.T) {
|
func TestConstructSigningPublicKeyWithP521(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
|
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
|
||||||
data := make([]byte, 128)
|
data := make([]byte, 128)
|
||||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||||
|
|
||||||
assert.Nil(err, "ConstructSigningPublicKey() with P521 returned err on valid data")
|
assert.Nil(err, "ConstructSigningPublicKey() with P521 returned err on valid data")
|
||||||
assert.Equal(spk.Len(), KEYCERT_SIGN_P521_SIZE, "ConstructSigningPublicKey() with P521 returned incorrect SigningPublicKey length")
|
assert.Equal(spk.Len(), KEYCERT_SIGN_P521_SIZE, "ConstructSigningPublicKey() with P521 returned incorrect SigningPublicKey length")
|
||||||
}
|
}
|
||||||
|
*/
|
280
lib/common/keys_and_cert.go
Normal file
280
lib/common/keys_and_cert.go
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P KeysAndCert
|
||||||
|
https://geti2p.net/spec/common-structures#keysandcert
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| public_key |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| padding (optional) |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| signing_key |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| certificate |
|
||||||
|
+----+----+----+-//
|
||||||
|
|
||||||
|
public_key :: PublicKey (partial or full)
|
||||||
|
length -> 256 bytes or as specified in key certificate
|
||||||
|
|
||||||
|
padding :: random data
|
||||||
|
length -> 0 bytes or as specified in key certificate
|
||||||
|
padding length + signing_key length == KEYS_AND_CERT_SPK_SIZE bytes
|
||||||
|
|
||||||
|
signing__key :: SigningPublicKey (partial or full)
|
||||||
|
length -> 128 bytes or as specified in key certificate
|
||||||
|
padding length + signing_key length == KEYS_AND_CERT_SPK_SIZE bytes
|
||||||
|
|
||||||
|
certificate :: Certificate
|
||||||
|
length -> >= 3 bytes
|
||||||
|
|
||||||
|
total length: 387+ bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/go-i2p/go-i2p/lib/crypto"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sizes of various KeysAndCert structures and requirements
|
||||||
|
const (
|
||||||
|
KEYS_AND_CERT_PUBKEY_SIZE = 256
|
||||||
|
KEYS_AND_CERT_SPK_SIZE = 128
|
||||||
|
KEYS_AND_CERT_MIN_SIZE = 387
|
||||||
|
KEYS_AND_CERT_DATA_SIZE = 384
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeysAndCertInterface interface {
|
||||||
|
GetPublicKey() (key crypto.PublicKey, err error)
|
||||||
|
GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error)
|
||||||
|
GetCertificate() (cert Certificate, err error)
|
||||||
|
Bytes() (bytes []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeysAndCert struct {
|
||||||
|
crypto.SigningPublicKey
|
||||||
|
crypto.PublicKey
|
||||||
|
CertificateInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keys_and_cert KeysAndCert) Bytes() (bytes []byte) { //, err error) {
|
||||||
|
pubkey, _ := keys_and_cert.GetPublicKey()
|
||||||
|
signpubkey, _ := keys_and_cert.GetSigningPublicKey()
|
||||||
|
elg_key := pubkey.(crypto.ElgPublicKey)
|
||||||
|
dsa_key := signpubkey.(crypto.DSAPublicKey)
|
||||||
|
bytes = append(bytes, dsa_key[:]...)
|
||||||
|
bytes = append(bytes, elg_key[:]...)
|
||||||
|
bytes = append(bytes, keys_and_cert.CertificateInterface.Cert()...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the PublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
||||||
|
// determine correct lengths.
|
||||||
|
//
|
||||||
|
func (keys_and_cert KeysAndCert) GetPublicKey() (key crypto.PublicKey, err error) {
|
||||||
|
data := make([]byte, KEYS_AND_CERT_PUBKEY_SIZE)
|
||||||
|
if keys_and_cert.PublicKey == nil {
|
||||||
|
epk := crypto.ElgPublicKey{}
|
||||||
|
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], epk[:])
|
||||||
|
keys_and_cert.PublicKey = epk
|
||||||
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
|
}
|
||||||
|
/*cert, err := keys_and_cert.GetCertificate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cert_len, err := cert.Length()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cert_len != 0 {*/
|
||||||
|
key = keys_and_cert.PublicKey
|
||||||
|
/*}*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the SigningPublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
||||||
|
// determine correct lengths.
|
||||||
|
//
|
||||||
|
func (keys_and_cert KeysAndCert) GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||||
|
if keys_and_cert.SigningPublicKey == nil {
|
||||||
|
keys_and_cert.SigningPublicKey = crypto.DSAPublicKey{}
|
||||||
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
|
}
|
||||||
|
/*cert, err := keys_and_cert.GetCertificate()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cert_len, err := cert.Length()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cert_len != 0 {*/
|
||||||
|
signing_public_key = keys_and_cert.SigningPublicKey
|
||||||
|
/*}*/
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Certificate contained in the KeysAndCert and any errors encountered while parsing the
|
||||||
|
// KeysAndCert or Certificate.
|
||||||
|
//
|
||||||
|
func (keys_and_cert KeysAndCert) GetCertificate() (cert CertificateInterface, err error) {
|
||||||
|
data_len := len(keys_and_cert.Bytes())
|
||||||
|
log.Println("LEN IS", data_len, "KEYS_AND_CERT_MIN_SIZE", KEYS_AND_CERT_MIN_SIZE)
|
||||||
|
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "GetCertificate",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
||||||
|
}
|
||||||
|
/*if data_len > CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "ReadKeysAndCert",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "too much data",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
|
||||||
|
}*/
|
||||||
|
cert = keys_and_cert.CertificateInterface
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.PublicKey, remainder []byte, err error) {
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "ReadKeys",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
// No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
||||||
|
// PublicKey space as ElgPublicKey.
|
||||||
|
var elg_key crypto.ElgPublicKey
|
||||||
|
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
||||||
|
pk = elg_key
|
||||||
|
} else {
|
||||||
|
// A Certificate is present in this KeysAndCert
|
||||||
|
cert_type, cert_bytes, e := cert.Type()
|
||||||
|
err = e
|
||||||
|
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.
|
||||||
|
cert_integer, _ := NewInteger(cert_bytes)
|
||||||
|
pk, err = KeyCertificate{PKType: cert_integer}.ConstructPublicKey(
|
||||||
|
data[:KEYS_AND_CERT_PUBKEY_SIZE],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Key Certificate is not present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
||||||
|
// PublicKey space as ElgPublicKey. No other Certificate
|
||||||
|
// types are currently in use.
|
||||||
|
var elg_key crypto.ElgPublicKey
|
||||||
|
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
||||||
|
pk = elg_key
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeysAndCert) PublicKey",
|
||||||
|
"cert_type": cert_type,
|
||||||
|
}).Warn("unused certificate type observed")
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
if data_len == 0 {
|
||||||
|
// No Certificate is present, return the KEYS_AND_CERT_SPK_SIZE byte
|
||||||
|
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
|
||||||
|
var dsa_pk crypto.DSAPublicKey
|
||||||
|
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
||||||
|
spk = dsa_pk
|
||||||
|
} else {
|
||||||
|
// A Certificate is present in this KeysAndCert
|
||||||
|
cert_type, cert_bytes, e := cert.Type()
|
||||||
|
err = e
|
||||||
|
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.
|
||||||
|
cert_integer, _ := NewInteger(cert_bytes)
|
||||||
|
spk, err = KeyCertificate{SPKType: cert_integer}.ConstructSigningPublicKey(
|
||||||
|
data[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Key Certificate is not present, return the KEYS_AND_CERT_SPK_SIZE byte
|
||||||
|
// SigningPublicKey space as legacy SHA DSA1 SigningPublicKey.
|
||||||
|
// No other Certificate types are currently in use.
|
||||||
|
var dsa_pk crypto.DSAPublicKey
|
||||||
|
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
||||||
|
spk = dsa_pk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cert_len, e := cert.Length()
|
||||||
|
err = e
|
||||||
|
if cert_len == 0 {
|
||||||
|
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remainder = data[KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read a KeysAndCert from a slice of bytes, retuning it and the remaining data as well as any errors
|
||||||
|
// encoutered parsing the KeysAndCert.
|
||||||
|
//
|
||||||
|
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
|
||||||
|
data_len := len(data)
|
||||||
|
keys_and_cert.CertificateInterface = &Certificate{}
|
||||||
|
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "ReadKeysAndCert",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cert, remainder, err := ReadCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "ReadKeysAndCert",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "error parsing certificate",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("error parsing KeysAndCert: error parsing certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys_and_cert.CertificateInterface = cert
|
||||||
|
spk, pk, remainder, err := ReadKeys(data, cert)
|
||||||
|
keys_and_cert.SigningPublicKey = spk
|
||||||
|
keys_and_cert.PublicKey = pk
|
||||||
|
return
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
# keys_and_cert
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
|
|
||||||
|
|
||||||
Package keys_and_cert implements the I2P KeysAndCert common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
KEYS_AND_CERT_PUBKEY_SIZE = 256
|
|
||||||
KEYS_AND_CERT_SPK_SIZE = 128
|
|
||||||
KEYS_AND_CERT_MIN_SIZE = 387
|
|
||||||
KEYS_AND_CERT_DATA_SIZE = 384
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Sizes of various KeysAndCert structures and requirements
|
|
||||||
|
|
||||||
#### type KeysAndCert
|
|
||||||
|
|
||||||
```go
|
|
||||||
type KeysAndCert struct {
|
|
||||||
KeyCertificate *KeyCertificate
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
KeysAndCert is the represenation of an I2P KeysAndCert.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#keysandcert
|
|
||||||
|
|
||||||
#### func ReadKeysAndCert
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadKeysAndCert creates a new *KeysAndCert from []byte using ReadKeysAndCert.
|
|
||||||
Returns a pointer to KeysAndCert unlike ReadKeysAndCert.
|
|
||||||
|
|
||||||
#### func (KeysAndCert) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (keys_and_cert KeysAndCert) Bytes() []byte
|
|
||||||
```
|
|
||||||
Bytes returns the entire KeyCertificate in []byte form, trims payload to
|
|
||||||
specified length.
|
|
||||||
|
|
||||||
#### func (*KeysAndCert) Certificate
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (keys_and_cert *KeysAndCert) Certificate() (cert Certificate)
|
|
||||||
```
|
|
||||||
Certfificate returns the certificate.
|
|
||||||
|
|
||||||
#### func (*KeysAndCert) PublicKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey)
|
|
||||||
```
|
|
||||||
PublicKey returns the public key as a crypto.PublicKey.
|
|
||||||
|
|
||||||
#### func (*KeysAndCert) SigningPublicKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (keys_and_cert *KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey)
|
|
||||||
```
|
|
||||||
SigningPublicKey returns the signing public key.
|
|
||||||
|
|
||||||
#### type PrivateKeysAndCert
|
|
||||||
|
|
||||||
```go
|
|
||||||
type PrivateKeysAndCert struct {
|
|
||||||
KeysAndCert
|
|
||||||
PK_KEY crypto.PrivateKey
|
|
||||||
SPK_KEY crypto.PrivateKey
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
PrivateKeysAndCert contains a KeysAndCert along with the corresponding private
|
|
||||||
keys for the Public Key and the Signing Public Key
|
|
||||||
|
|
||||||
#### func NewPrivateKeysAndCert
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewPrivateKeysAndCert() (*PrivateKeysAndCert, error)
|
|
||||||
```
|
|
@ -1,167 +0,0 @@
|
|||||||
// Package keys_and_cert implements the I2P KeysAndCert common data structure
|
|
||||||
package keys_and_cert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/certificate"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/key_certificate"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/crypto"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
// Sizes of various KeysAndCert structures and requirements
|
|
||||||
const (
|
|
||||||
KEYS_AND_CERT_PUBKEY_SIZE = 256
|
|
||||||
KEYS_AND_CERT_SPK_SIZE = 128
|
|
||||||
KEYS_AND_CERT_MIN_SIZE = 387
|
|
||||||
KEYS_AND_CERT_DATA_SIZE = 384
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
[KeysAndCert]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
An encryption public key, a signing public key, and a certificate, used as either a RouterIdentity or a Destination.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
A PublicKey followed by a SigningPublicKey and then a Certificate.
|
|
||||||
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| public_key |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| padding (optional) |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| signing_key |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| certificate |
|
|
||||||
+----+----+----+-//
|
|
||||||
|
|
||||||
public_key :: PublicKey (partial or full)
|
|
||||||
length -> 256 bytes or as specified in key certificate
|
|
||||||
|
|
||||||
padding :: random data
|
|
||||||
length -> 0 bytes or as specified in key certificate
|
|
||||||
padding length + signing_key length == 128 bytes
|
|
||||||
|
|
||||||
signing__key :: SigningPublicKey (partial or full)
|
|
||||||
length -> 128 bytes or as specified in key certificate
|
|
||||||
padding length + signing_key length == 128 bytes
|
|
||||||
|
|
||||||
certificate :: Certificate
|
|
||||||
length -> >= 3 bytes
|
|
||||||
|
|
||||||
total length: 387+ bytes
|
|
||||||
*/
|
|
||||||
|
|
||||||
// KeysAndCert is the represenation of an I2P KeysAndCert.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#keysandcert
|
|
||||||
type KeysAndCert struct {
|
|
||||||
KeyCertificate *KeyCertificate
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
padding []byte
|
|
||||||
signingPublicKey crypto.SigningPublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the entire KeyCertificate in []byte form, trims payload to specified length.
|
|
||||||
func (keys_and_cert KeysAndCert) Bytes() []byte {
|
|
||||||
bytes := keys_and_cert.KeyCertificate.Bytes()
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"bytes_length": len(bytes),
|
|
||||||
}).Debug("Retrieved bytes from KeysAndCert")
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey returns the public key as a crypto.PublicKey.
|
|
||||||
func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey) {
|
|
||||||
return keys_and_cert.publicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// SigningPublicKey returns the signing public key.
|
|
||||||
func (keys_and_cert *KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey) {
|
|
||||||
return keys_and_cert.signingPublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Certfificate returns the certificate.
|
|
||||||
func (keys_and_cert *KeysAndCert) Certificate() (cert Certificate) {
|
|
||||||
return keys_and_cert.KeyCertificate.Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadKeysAndCert creates a new *KeysAndCert from []byte using ReadKeysAndCert.
|
|
||||||
// Returns a pointer to KeysAndCert unlike ReadKeysAndCert.
|
|
||||||
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"input_length": len(data),
|
|
||||||
}).Debug("Reading KeysAndCert from data")
|
|
||||||
|
|
||||||
data_len := len(data)
|
|
||||||
// keys_and_cert = KeysAndCert{}
|
|
||||||
if data_len < KEYS_AND_CERT_MIN_SIZE && data_len > KEYS_AND_CERT_DATA_SIZE {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ReadKeysAndCert",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing keys and cert")
|
|
||||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
|
||||||
keys_and_cert.KeyCertificate, remainder, _ = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
|
|
||||||
return
|
|
||||||
} else if data_len < KEYS_AND_CERT_DATA_SIZE {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "ReadKeysAndCert",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing keys and cert")
|
|
||||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keys_and_cert.KeyCertificate, remainder, err = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to create KeyCertificate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: this only supports one key type right now and it's the old key type, but the layout is the same.
|
|
||||||
// a case-switch which sets the size of the SPK and the PK should be used to replace the referenced KEYS_AND_CERT_PUBKEY_SIZE
|
|
||||||
// and KEYS_AND_CERT_SPK_SIZE constants in the future.
|
|
||||||
keys_and_cert.publicKey, err = keys_and_cert.KeyCertificate.ConstructPublicKey(data[:keys_and_cert.KeyCertificate.CryptoSize()])
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to construct PublicKey")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keys_and_cert.signingPublicKey, err = keys_and_cert.KeyCertificate.ConstructSigningPublicKey(data[KEYS_AND_CERT_DATA_SIZE-keys_and_cert.KeyCertificate.SignatureSize() : KEYS_AND_CERT_DATA_SIZE])
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to construct SigningPublicKey")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
padding := data[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_DATA_SIZE-KEYS_AND_CERT_SPK_SIZE]
|
|
||||||
keys_and_cert.padding = padding
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"public_key_type": keys_and_cert.KeyCertificate.PublicKeyType(),
|
|
||||||
"signing_public_key_type": keys_and_cert.KeyCertificate.SigningPublicKeyType(),
|
|
||||||
"padding_length": len(padding),
|
|
||||||
"remainder_length": len(remainder),
|
|
||||||
}).Debug("Successfully read KeysAndCert")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package keys_and_cert
|
|
||||||
|
|
||||||
import "crypto"
|
|
||||||
|
|
||||||
// PrivateKeysAndCert contains a KeysAndCert along with the corresponding private keys for the
|
|
||||||
// Public Key and the Signing Public Key
|
|
||||||
type PrivateKeysAndCert struct {
|
|
||||||
KeysAndCert
|
|
||||||
PK_KEY crypto.PrivateKey
|
|
||||||
SPK_KEY crypto.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivateKeysAndCert() (*PrivateKeysAndCert, error) {
|
|
||||||
var pkc PrivateKeysAndCert
|
|
||||||
var err error
|
|
||||||
// pkc.PK_KEY, err =
|
|
||||||
return &pkc, err
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package keys_and_cert
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -6,17 +6,29 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*func TestCertificateWithMissingData(t *testing.T) {
|
func TestCertificateWithMissingData(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
|
//cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
|
||||||
data := make([]byte, 128+256)
|
data := make([]byte, 128+256)
|
||||||
data = append(data, cert_data...)
|
//data = append(data, cert_data...)
|
||||||
_, _, err := NewKeysAndCert(data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
|
}
|
||||||
|
t.Log("\n\nREMAINDER", remainder, "\n\n")
|
||||||
|
cert, err := keys_and_cert.GetCertificate()
|
||||||
|
t.Log("\n\nSTART\n\n")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
|
} else {
|
||||||
|
t.Log("\n\nEND\n\n", cert.Cert())
|
||||||
}
|
}
|
||||||
}*/
|
// cert_bytes := []byte(cert.Cert())
|
||||||
|
// if assert.Equal(len(cert_data), len(cert_bytes)) {
|
||||||
|
// assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return available data when cert was missing some data")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
func TestCertificateWithValidData(t *testing.T) {
|
func TestCertificateWithValidData(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
@ -25,13 +37,12 @@ func TestCertificateWithValidData(t *testing.T) {
|
|||||||
data := make([]byte, 128+256)
|
data := make([]byte, 128+256)
|
||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
|
cert, err := keys_and_cert.GetCertificate()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
cert_bytes := []byte(cert.Cert())
|
||||||
cert := keys_and_cert.Certificate()
|
|
||||||
|
|
||||||
cert_bytes := cert.Bytes()
|
|
||||||
if assert.Equal(len(cert_data), len(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")
|
assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return correct data with valid cert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,11 +56,13 @@ func TestPublicKeyWithBadData(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
pub_key := keys_and_cert.PublicKey()
|
//pub_key
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
}
|
}
|
||||||
assert.Nil(pub_key)
|
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
|
||||||
|
//assert.Nil(pub_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeyWithBadCertificate(t *testing.T) {
|
func TestPublicKeyWithBadCertificate(t *testing.T) {
|
||||||
@ -62,11 +75,13 @@ func TestPublicKeyWithBadCertificate(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
pub_key := keys_and_cert.PublicKey()
|
//pub_key
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
}
|
}
|
||||||
assert.Nil(pub_key)
|
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
|
||||||
|
//assert.Nil(pub_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeyWithNullCertificate(t *testing.T) {
|
func TestPublicKeyWithNullCertificate(t *testing.T) {
|
||||||
@ -79,7 +94,7 @@ func TestPublicKeyWithNullCertificate(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
pub_key := keys_and_cert.PublicKey()
|
pub_key, err := keys_and_cert.GetPublicKey()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(len(pub_key_data), pub_key.Len())
|
assert.Equal(len(pub_key_data), pub_key.Len())
|
||||||
}
|
}
|
||||||
@ -93,8 +108,9 @@ func TestPublicKeyWithKeyCertificate(t *testing.T) {
|
|||||||
data = append(data, pub_key_data...)
|
data = append(data, pub_key_data...)
|
||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
pub_key := keys_and_cert.PublicKey()
|
pub_key, err := keys_and_cert.GetPublicKey()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(len(pub_key_data), pub_key.Len())
|
assert.Equal(len(pub_key_data), pub_key.Len())
|
||||||
}
|
}
|
||||||
@ -109,7 +125,7 @@ func TestSigningPublicKeyWithBadData(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
signing_pub_key := keys_and_cert.SigningPublicKey()
|
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
}
|
}
|
||||||
@ -126,7 +142,7 @@ func TestSigningPublicKeyWithBadCertificate(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
signing_pub_key := keys_and_cert.SigningPublicKey()
|
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
}
|
}
|
||||||
@ -143,7 +159,7 @@ func TestSigningPublicKeyWithNullCertificate(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
signing_pub_key := keys_and_cert.SigningPublicKey()
|
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
||||||
}
|
}
|
||||||
@ -158,74 +174,128 @@ func TestSigningPublicKeyWithKeyCertificate(t *testing.T) {
|
|||||||
data = append(data, cert_data...)
|
data = append(data, cert_data...)
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||||
|
|
||||||
signing_pub_key := keys_and_cert.SigningPublicKey()
|
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithMissingData(t *testing.T) {
|
func TestReadKeysAndCertWithMissingData(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128)
|
cert_data := make([]byte, 128)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
assert.Equal(0, len(remainder))
|
assert.Equal(0, len(remainder))
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
|
}
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
|
}
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithMissingCertData(t *testing.T) {
|
func TestReadKeysAndCertWithMissingCertData(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128+256)
|
cert_data := make([]byte, 128+256)
|
||||||
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...)
|
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
assert.Equal(0, len(remainder))
|
assert.Equal(0, len(remainder))
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
|
}
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
|
}
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithValidDataWithCertificate(t *testing.T) {
|
func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128+256)
|
cert_data := make([]byte, 128+256)
|
||||||
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
assert.Equal(0, len(remainder))
|
assert.Equal(0, len(remainder))
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
|
func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128+256)
|
cert_data := make([]byte, 128+256)
|
||||||
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...)
|
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
assert.Equal(0, len(remainder))
|
assert.Equal(0, len(remainder))
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
|
func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128+256)
|
cert_data := make([]byte, 128+256)
|
||||||
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...)
|
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
if assert.Equal(1, len(remainder)) {
|
if assert.Equal(1, len(remainder)) {
|
||||||
assert.Equal("A", string(remainder[0]))
|
assert.Equal("A", string(remainder[0]))
|
||||||
}
|
}
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
|
func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
cert_data := make([]byte, 128+256)
|
cert_data := make([]byte, 128+256)
|
||||||
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...)
|
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...)
|
||||||
_, remainder, err := ReadKeysAndCert(cert_data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
|
||||||
if assert.Equal(1, len(remainder)) {
|
if assert.Equal(1, len(remainder)) {
|
||||||
assert.Equal("A", string(remainder[0]))
|
assert.Equal("A", string(remainder[0]))
|
||||||
}
|
}
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = keys_and_cert.GetPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
|
||||||
|
_, err = keys_and_cert.GetSigningPublicKey()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
|
||||||
|
_, err = keys_and_cert.GetCertificate()
|
||||||
|
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
|
||||||
}
|
}
|
108
lib/common/lease.go
Normal file
108
lib/common/lease.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Lease
|
||||||
|
https://geti2p.net/spec/common-structures#lease
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_gw |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_id | end_date
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
|
||||||
|
+----+----+----+----+
|
||||||
|
|
||||||
|
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
|
||||||
|
length -> 32 bytes
|
||||||
|
|
||||||
|
tunnel_id :: TunnelId
|
||||||
|
length -> 4 bytes
|
||||||
|
|
||||||
|
end_date :: Date
|
||||||
|
length -> 8 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sizes or various components of a Lease
|
||||||
|
const (
|
||||||
|
LEASE_SIZE = 44
|
||||||
|
LEASE_HASH_SIZE = 32
|
||||||
|
LEASE_TUNNEL_ID_SIZE = 4
|
||||||
|
LEASE_TUNNEL_DATE_SIZE = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
type LeaseInterface interface {
|
||||||
|
TunnelGateway() (hash Hash)
|
||||||
|
TunnelID() uint32
|
||||||
|
Date() (date Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Lease struct {
|
||||||
|
LeaseHash Hash
|
||||||
|
TunnelIdent *Integer
|
||||||
|
TunnelDate Date
|
||||||
|
} //[LEASE_SIZE]byte
|
||||||
|
|
||||||
|
var li LeaseInterface = &Lease{}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the first 32 bytes of the Lease as a Hash.
|
||||||
|
//
|
||||||
|
func (lease Lease) TunnelGateway() (hash Hash) {
|
||||||
|
copy(hash[:], lease.LeaseHash[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the TunnelID Integer in the Lease.
|
||||||
|
//
|
||||||
|
func (lease Lease) TunnelID() uint32 {
|
||||||
|
return uint32(lease.TunnelIdent.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Date inside the Lease.
|
||||||
|
//
|
||||||
|
func (lease Lease) Date() (date Date) {
|
||||||
|
copy(date[:], lease.TunnelDate[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Possibly temporary? Just to make it compile for now
|
||||||
|
//
|
||||||
|
func (lease Lease) Bytes() (bytes []byte) {
|
||||||
|
var r []byte
|
||||||
|
r = append(r, lease.LeaseHash[:]...)
|
||||||
|
r = append(r, lease.TunnelIdent.Bytes()...)
|
||||||
|
r = append(r, lease.TunnelDate[:]...)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLease(data []byte) (lease Lease, remainder []byte, err error) {
|
||||||
|
if len(data) < LEASE_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Lease) ReadLease",
|
||||||
|
"data_len": len(data),
|
||||||
|
"required_len": "44",
|
||||||
|
"reason": "lease missing data",
|
||||||
|
}).Error("error parsnig lease")
|
||||||
|
err = errors.New("error parsing lease: lease missing data")
|
||||||
|
}
|
||||||
|
lease.LeaseHash, remainder, err = ReadHash(data)
|
||||||
|
identbytes, remainder, err := ReadIdent(remainder)
|
||||||
|
lease.TunnelIdent, err = NewInteger(identbytes[:])
|
||||||
|
lease.TunnelDate, remainder, err = ReadDate(remainder)
|
||||||
|
return
|
||||||
|
}
|
@ -1,63 +0,0 @@
|
|||||||
# lease
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/lease"
|
|
||||||
|
|
||||||
Package lease implements the I2P lease common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
LEASE_SIZE = 44
|
|
||||||
LEASE_HASH_SIZE = 32
|
|
||||||
LEASE_TUNNEL_ID_SIZE = 4
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Sizes in bytes of various components of a Lease
|
|
||||||
|
|
||||||
#### type Lease
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Lease [LEASE_SIZE]byte
|
|
||||||
```
|
|
||||||
|
|
||||||
Lease is the represenation of an I2P Lease.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#lease
|
|
||||||
|
|
||||||
#### func NewLease
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewLease(data []byte) (lease *Lease, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
NewLease creates a new *NewLease from []byte using ReadLease. Returns a pointer
|
|
||||||
to KeysAndCert unlike ReadLease.
|
|
||||||
|
|
||||||
#### func ReadLease
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadLease(data []byte) (lease Lease, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadLease returns Lease from a []byte. The remaining bytes after the specified
|
|
||||||
length are also returned. Returns a list of errors that occurred during parsing.
|
|
||||||
|
|
||||||
#### func (Lease) Date
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease Lease) Date() (date Date)
|
|
||||||
```
|
|
||||||
Date returns the date as an I2P Date.
|
|
||||||
|
|
||||||
#### func (Lease) TunnelGateway
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease Lease) TunnelGateway() (hash Hash)
|
|
||||||
```
|
|
||||||
TunnelGateway returns the tunnel gateway as a Hash.
|
|
||||||
|
|
||||||
#### func (Lease) TunnelID
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease Lease) TunnelID() uint32
|
|
||||||
```
|
|
||||||
TunnelID returns the tunnel id as a uint23.
|
|
@ -1,85 +0,0 @@
|
|||||||
// Package lease implements the I2P lease common data structure
|
|
||||||
package lease
|
|
||||||
|
|
||||||
import . "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
|
|
||||||
// Sizes in bytes of various components of a Lease
|
|
||||||
const (
|
|
||||||
LEASE_SIZE = 44
|
|
||||||
LEASE_HASH_SIZE = 32
|
|
||||||
LEASE_TUNNEL_ID_SIZE = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
[Lease]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
Defines the authorization for a particular tunnel to receive messages targeting a Destination.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
SHA256 Hash of the RouterIdentity of the gateway router, then the TunnelId and finally an end Date.
|
|
||||||
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| tunnel_gw |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| tunnel_id | end_date
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
|
|
|
||||||
+----+----+----+----+
|
|
||||||
|
|
||||||
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
|
|
||||||
length -> 32 bytes
|
|
||||||
|
|
||||||
tunnel_id :: TunnelId
|
|
||||||
length -> 4 bytes
|
|
||||||
|
|
||||||
end_date :: Date
|
|
||||||
length -> 8 bytes
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Lease is the represenation of an I2P Lease.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#lease
|
|
||||||
type Lease [LEASE_SIZE]byte
|
|
||||||
|
|
||||||
// TunnelGateway returns the tunnel gateway as a Hash.
|
|
||||||
func (lease Lease) TunnelGateway() (hash Hash) {
|
|
||||||
copy(hash[:], lease[:LEASE_HASH_SIZE])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TunnelID returns the tunnel id as a uint23.
|
|
||||||
func (lease Lease) TunnelID() uint32 {
|
|
||||||
i := Integer(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE])
|
|
||||||
return uint32(
|
|
||||||
i.Int(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Date returns the date as an I2P Date.
|
|
||||||
func (lease Lease) Date() (date Date) {
|
|
||||||
copy(date[:], lease[LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE:])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadLease returns Lease from a []byte.
|
|
||||||
// The remaining bytes after the specified length are also returned.
|
|
||||||
// Returns a list of errors that occurred during parsing.
|
|
||||||
func ReadLease(data []byte) (lease Lease, remainder []byte, err error) {
|
|
||||||
// TODO: stub
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLease creates a new *NewLease from []byte using ReadLease.
|
|
||||||
// Returns a pointer to KeysAndCert unlike ReadLease.
|
|
||||||
func NewLease(data []byte) (lease *Lease, remainder []byte, err error) {
|
|
||||||
// TODO: stub
|
|
||||||
return
|
|
||||||
}
|
|
40
lib/common/lease2.go
Normal file
40
lib/common/lease2.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lease2
|
||||||
|
https://geti2p.net/spec/common-structures#lease2
|
||||||
|
Description
|
||||||
|
|
||||||
|
Defines the authorization for a particular tunnel to receive messages targeting a Destination. Same as Lease but with a 4-byte end_date. Used by LeaseSet2. Supported as of 0.9.38; see proposal 123 for more information.
|
||||||
|
Contents
|
||||||
|
|
||||||
|
SHA256 Hash of the RouterIdentity of the gateway router, then the TunnelId, and finally a 4 byte end date.
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_gw |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_id | end_date |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
|
||||||
|
length -> 32 bytes
|
||||||
|
|
||||||
|
tunnel_id :: TunnelId
|
||||||
|
length -> 4 bytes
|
||||||
|
|
||||||
|
end_date :: 4 byte date
|
||||||
|
length -> 4 bytes
|
||||||
|
Seconds since the epoch, rolls over in 2106.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
|
||||||
|
Total size: 40 bytes
|
||||||
|
|
||||||
|
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/Lease2.html
|
||||||
|
*/
|
336
lib/common/lease_set.go
Normal file
336
lib/common/lease_set.go
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P LeaseSet
|
||||||
|
https://geti2p.net/spec/common-structures#leaseset
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| destination |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| encryption_key |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| signing_key |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|num | Lease 0 |
|
||||||
|
+----+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| Lease 1 |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| Lease ($num-1) |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| signature |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
destination :: Destination
|
||||||
|
length -> >= 387 bytes
|
||||||
|
|
||||||
|
encryption_key :: PublicKey
|
||||||
|
length -> 256 bytes
|
||||||
|
|
||||||
|
signing_key :: SigningPublicKey
|
||||||
|
length -> 128 bytes or as specified in destination's key certificate
|
||||||
|
|
||||||
|
num :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
Number of leases to follow
|
||||||
|
value: 0 <= num <= 16
|
||||||
|
|
||||||
|
leases :: [Lease]
|
||||||
|
length -> $num*44 bytes
|
||||||
|
|
||||||
|
signature :: Signature
|
||||||
|
length -> 40 bytes or as specified in destination's key certificate
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/go-i2p/go-i2p/lib/crypto"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sizes of various structures in an I2P LeaseSet
|
||||||
|
const (
|
||||||
|
LEASE_SET_PUBKEY_SIZE = 256
|
||||||
|
LEASE_SET_SPK_SIZE = 128
|
||||||
|
LEASE_SET_SIG_SIZE = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
type LeaseSetInterface interface {
|
||||||
|
GetPublicKey() (public_key crypto.ElgPublicKey, err error)
|
||||||
|
GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error)
|
||||||
|
Leases() (leases []Lease, err error)
|
||||||
|
/* LeaseCount() (count int, err error)*/
|
||||||
|
|
||||||
|
GetSignature() (signature Signature, err error)
|
||||||
|
/* Verify() error
|
||||||
|
NewestExpiration() (oldest Date, err error)
|
||||||
|
OldestExpiration() (earliest Date, err error)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
type LeaseSet struct {
|
||||||
|
Destination
|
||||||
|
crypto.SigningPublicKey
|
||||||
|
crypto.ElgPublicKey
|
||||||
|
LeaseList []Lease
|
||||||
|
}
|
||||||
|
|
||||||
|
var lsi LeaseSetInterface = &LeaseSet{}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read a Destination from the LeaseSet.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) GetDestination() (destination Destination, err error) {
|
||||||
|
if &lease_set.Destination != nil {
|
||||||
|
destination = lease_set.Destination
|
||||||
|
} else {
|
||||||
|
err = errors.New("Error leaseset does not contain a destination")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the PublicKey in this LeaseSet and any errors ancountered parsing the LeaseSet.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) GetPublicKey() (public_key crypto.ElgPublicKey, err error) {
|
||||||
|
public_key = lease_set.ElgPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the SigningPublicKey, as specified in the LeaseSet's Destination's Key Certificate if
|
||||||
|
// present, or a legacy DSA key.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||||
|
if lease_set.SigningPublicKey == nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(LeaseSet) SigningKey",
|
||||||
|
"public": lease_set.SigningPublicKey,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing signing public key")
|
||||||
|
err = errors.New("error parsing signing public key: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
signing_public_key = lease_set.SigningPublicKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
||||||
|
leases = lease_set.LeaseList
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the number of Leases specified by the LeaseCount value in this LeaseSet.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
||||||
|
count = len(lease_set.LeaseList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Signature data for the LeaseSet, as specified in the Destination's
|
||||||
|
// Key Certificate if present or the 40 bytes following the Leases.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) GetSignature() (signature Signature, err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
func (lease_set LeaseSet) Verify() error {
|
||||||
|
//data_end := len(destination) +
|
||||||
|
// LEASE_SET_PUBKEY_SIZE +
|
||||||
|
// LEASE_SET_SPK_SIZE +
|
||||||
|
// 1 +
|
||||||
|
// (44 * lease_set.LeaseCount())
|
||||||
|
//data := lease_set[:data_end]
|
||||||
|
//spk, _ := lease_set.
|
||||||
|
// Destination().
|
||||||
|
// SigningPublicKey()
|
||||||
|
//verifier, err := spk.NewVerifier()
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
return nil // verifier.Verify(data, lease_set.Signature())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Return the oldest date from all the Leases in the LeaseSet.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) NewestExpiration() (oldest Date, err error) {
|
||||||
|
leases, err := lease_set.Leases()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oldest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
for _, lease := range leases {
|
||||||
|
date := lease.Date()
|
||||||
|
if date.Time().After(oldest.Time()) {
|
||||||
|
oldest = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the oldest date from all the Leases in the LeaseSet.
|
||||||
|
//
|
||||||
|
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
|
||||||
|
leases, err := lease_set.Leases()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||||
|
for _, lease := range leases {
|
||||||
|
date := lease.Date()
|
||||||
|
if date.Time().Before(earliest.Time()) {
|
||||||
|
earliest = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLeaseSetSignature(bytes []byte, cert CertificateInterface) (signature Signature, remainder []byte, err error) {
|
||||||
|
start := 0
|
||||||
|
cert_type, _, _ := cert.Type()
|
||||||
|
var end int
|
||||||
|
if cert_type == CERT_KEY {
|
||||||
|
end = start + cert.SignatureSize()
|
||||||
|
} else {
|
||||||
|
end = start + LEASE_SET_SIG_SIZE
|
||||||
|
}
|
||||||
|
bytes_len := len(bytes)
|
||||||
|
if bytes_len < end {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(LeaseSet) Signature",
|
||||||
|
"data_len": bytes_len,
|
||||||
|
"required_len": end,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing signatre")
|
||||||
|
err = errors.New("error parsing signature: not enough data")
|
||||||
|
signature = []byte(bytes[start:bytes_len])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
signature = []byte(bytes[start:end])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLeaseCount(bytes []byte) (count *Integer, err error) {
|
||||||
|
remainder_len := len(bytes)
|
||||||
|
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(LeaseSet) LeaseCount",
|
||||||
|
"data_len": remainder_len,
|
||||||
|
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing lease count")
|
||||||
|
err = errors.New("error parsing lease count: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
count, err = NewInteger([]byte{bytes[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
|
||||||
|
if count.Value() > 16 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(LeaseSet) LeaseCount",
|
||||||
|
"lease_count": count,
|
||||||
|
"reason": "more than 16 leases",
|
||||||
|
}).Warn("invalid lease set")
|
||||||
|
err = errors.New("invalid lease set: more than 16 leases")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
|
||||||
|
//
|
||||||
|
func ReadLeases(bytes []byte) (leases []Lease, remainder []byte, err error) {
|
||||||
|
count, err := ReadLeaseCount(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < count.Value(); i++ {
|
||||||
|
start := 0 //offset + (i * LEASE_SIZE)
|
||||||
|
end := start + LEASE_SIZE
|
||||||
|
lease_set_len := len(bytes)
|
||||||
|
if lease_set_len < end {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(LeaseSet) Leases",
|
||||||
|
"data_len": lease_set_len,
|
||||||
|
"required_len": end,
|
||||||
|
"reason": "some leases missing",
|
||||||
|
}).Error("error parsnig lease set")
|
||||||
|
err = errors.New("error parsing lease set: some leases missing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var lease Lease
|
||||||
|
lease, remainder, err = ReadLease(bytes[start:end])
|
||||||
|
leases = append(leases, lease)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLeaseSetKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.ElgPublicKey, remainder []byte, err error) {
|
||||||
|
spk, ppk, remainder, err := ReadKeys(data, cert)
|
||||||
|
switch ppk.(type) {
|
||||||
|
case crypto.ElgPublicKey:
|
||||||
|
pk = ppk.(crypto.ElgPublicKey)
|
||||||
|
default:
|
||||||
|
err = errors.New("LeaseSet1 uses Elgamal public keys.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLeaseSet(data []byte) (lease_set LeaseSet, remainder []byte, err error) {
|
||||||
|
destination, remainder, err := ReadDestination(data)
|
||||||
|
lease_set.Destination = destination
|
||||||
|
//offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
|
||||||
|
spk, pk, remainder, err := ReadLeaseSetKeys(remainder, nil)
|
||||||
|
lease_set.SigningPublicKey = spk
|
||||||
|
lease_set.ElgPublicKey = pk
|
||||||
|
leases, remainder, err := ReadLeases(data)
|
||||||
|
lease_set.LeaseList = leases
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -1,95 +0,0 @@
|
|||||||
# lease_set
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/lease_set"
|
|
||||||
|
|
||||||
Package lease_set implements the I2P LeastSet common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
LEASE_SET_PUBKEY_SIZE = 256
|
|
||||||
LEASE_SET_SPK_SIZE = 128
|
|
||||||
LEASE_SET_SIG_SIZE = 40
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Sizes of various structures in an I2P LeaseSet
|
|
||||||
|
|
||||||
#### type LeaseSet
|
|
||||||
|
|
||||||
```go
|
|
||||||
type LeaseSet []byte
|
|
||||||
```
|
|
||||||
|
|
||||||
LeaseSet is the represenation of an I2P LeaseSet.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#leaseset
|
|
||||||
|
|
||||||
#### func (LeaseSet) Destination
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) Destination() (destination Destination, err error)
|
|
||||||
```
|
|
||||||
Destination returns the Destination as []byte.
|
|
||||||
|
|
||||||
#### func (LeaseSet) LeaseCount
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) LeaseCount() (count int, err error)
|
|
||||||
```
|
|
||||||
LeaseCount returns the numbert of leases specified by the LeaseCount value as
|
|
||||||
int. returns errors encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) Leases
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) Leases() (leases []Lease, err error)
|
|
||||||
```
|
|
||||||
Leases returns the leases as []Lease. returns errors encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) NewestExpiration
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error)
|
|
||||||
```
|
|
||||||
NewestExpiration returns the newest lease expiration as an I2P Date. Returns
|
|
||||||
errors encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) OldestExpiration
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error)
|
|
||||||
```
|
|
||||||
OldestExpiration returns the oldest lease expiration as an I2P Date. Returns
|
|
||||||
errors encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) PublicKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error)
|
|
||||||
```
|
|
||||||
PublicKey returns the public key as crypto.ElgPublicKey. Returns errors
|
|
||||||
encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) Signature
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) Signature() (signature Signature, err error)
|
|
||||||
```
|
|
||||||
Signature returns the signature as Signature. returns errors encountered during
|
|
||||||
parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) SigningKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error)
|
|
||||||
```
|
|
||||||
SigningKey returns the signing public key as crypto.SigningPublicKey. returns
|
|
||||||
errors encountered during parsing.
|
|
||||||
|
|
||||||
#### func (LeaseSet) Verify
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (lease_set LeaseSet) Verify() error
|
|
||||||
```
|
|
||||||
Verify returns nil
|
|
@ -1,408 +0,0 @@
|
|||||||
// Package lease_set implements the I2P LeastSet common data structure
|
|
||||||
package lease_set
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/util/logger"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/certificate"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/destination"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/key_certificate"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/lease"
|
|
||||||
. "github.com/go-i2p/go-i2p/lib/common/signature"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.GetGoI2PLogger()
|
|
||||||
|
|
||||||
// Sizes of various structures in an I2P LeaseSet
|
|
||||||
const (
|
|
||||||
LEASE_SET_PUBKEY_SIZE = 256
|
|
||||||
LEASE_SET_SPK_SIZE = 128
|
|
||||||
LEASE_SET_SIG_SIZE = 40
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
[LeaseSet]
|
|
||||||
Accurate for version 0.9.49
|
|
||||||
|
|
||||||
Description
|
|
||||||
Contains all of the currently authorized Leases for a particular Destination, the
|
|
||||||
PublicKey to which garlic messages can be encrypted, and then the SigningPublicKey
|
|
||||||
that can be used to revoke this particular version of the structure. The LeaseSet is one
|
|
||||||
of the two structures stored in the network database (the other being RouterInfo), and
|
|
||||||
is kered under the SHA256 of the contained Destination.
|
|
||||||
|
|
||||||
Contents
|
|
||||||
Destination, followed by a PublicKey for encryption, then a SigningPublicKey which
|
|
||||||
can be used to revoke this version of the LeaseSet, then a 1 byte Integer specifying how
|
|
||||||
many Lease structures are in the set, followed by the actual Lease structures and
|
|
||||||
finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey.
|
|
||||||
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| destination |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| encryption_key |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| signing_key |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
|num | Lease 0 |
|
|
||||||
+----+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| Lease 1 |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| Lease ($num-1) |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
~ ~
|
|
||||||
~ ~
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
| signature |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+ +
|
|
||||||
| |
|
|
||||||
+----+----+----+----+----+----+----+----+
|
|
||||||
|
|
||||||
destination :: Destination
|
|
||||||
length -> >= 387 bytes
|
|
||||||
|
|
||||||
encryption_key :: PublicKey
|
|
||||||
length -> 256 bytes
|
|
||||||
|
|
||||||
signing_key :: SigningPublicKey
|
|
||||||
length -> 128 bytes or as specified in destination's key certificate
|
|
||||||
|
|
||||||
num :: Integer
|
|
||||||
length -> 1 byte
|
|
||||||
Number of leases to follow
|
|
||||||
value: 0 <= num <= 16
|
|
||||||
|
|
||||||
leases :: [Lease]
|
|
||||||
length -> $num*44 bytes
|
|
||||||
|
|
||||||
signature :: Signature
|
|
||||||
length -> 40 bytes or as specified in destination's key certificate
|
|
||||||
*/
|
|
||||||
|
|
||||||
// LeaseSet is the represenation of an I2P LeaseSet.
|
|
||||||
//
|
|
||||||
// https://geti2p.net/spec/common-structures#leaseset
|
|
||||||
type LeaseSet []byte
|
|
||||||
|
|
||||||
/*
|
|
||||||
type LeaseSet struct {
|
|
||||||
Destination *Destination
|
|
||||||
EncryptionKey *crypto.ElgPublicKey
|
|
||||||
SigningKey *crypto.ElgPublicKey
|
|
||||||
Size *Integer
|
|
||||||
Leases []*Lease
|
|
||||||
Signature *Signature
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Destination returns the Destination as []byte.
|
|
||||||
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
|
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(lease_set)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to read KeysAndCert from LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
destination, _, err = ReadDestination(keys_and_cert.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to read Destination from KeysAndCert")
|
|
||||||
} else {
|
|
||||||
log.Debug("Successfully retrieved Destination from LeaseSet")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey returns the public key as crypto.ElgPublicKey.
|
|
||||||
// Returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error) {
|
|
||||||
_, remainder, err := ReadKeysAndCert(lease_set)
|
|
||||||
remainder_len := len(remainder)
|
|
||||||
if remainder_len < LEASE_SET_PUBKEY_SIZE {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) PublicKey",
|
|
||||||
"data_len": remainder_len,
|
|
||||||
"required_len": LEASE_SET_PUBKEY_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing public key")
|
|
||||||
err = errors.New("error parsing public key: not enough data")
|
|
||||||
copy(public_key[:], remainder)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
|
|
||||||
log.Debug("Successfully retrieved PublicKey from LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SigningKey returns the signing public key as crypto.SigningPublicKey.
|
|
||||||
// returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
|
||||||
log.Debug("Retrieving SigningKey from LeaseSet")
|
|
||||||
destination, err := lease_set.Destination()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve Destination for SigningKey")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE
|
|
||||||
cert := destination.Certificate()
|
|
||||||
cert_len := cert.Length()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to get Certificate length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lease_set_len := len(lease_set)
|
|
||||||
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) SigningKey",
|
|
||||||
"data_len": lease_set_len,
|
|
||||||
"required_len": offset + LEASE_SET_SPK_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing signing public key")
|
|
||||||
err = errors.New("error parsing signing public key: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cert_len == 0 {
|
|
||||||
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
|
|
||||||
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
|
|
||||||
var dsa_pk crypto.DSAPublicKey
|
|
||||||
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
|
|
||||||
signing_public_key = dsa_pk
|
|
||||||
log.Debug("Retrieved legacy DSA SHA1 SigningPublicKey")
|
|
||||||
} else {
|
|
||||||
// A Certificate is present in this LeaseSet's Destination
|
|
||||||
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 = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey(
|
|
||||||
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to construct SigningPublicKey from KeyCertificate")
|
|
||||||
} else {
|
|
||||||
log.Debug("Retrieved SigningPublicKey from KeyCertificate")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
|
|
||||||
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
|
|
||||||
var dsa_pk crypto.DSAPublicKey
|
|
||||||
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
|
|
||||||
signing_public_key = dsa_pk
|
|
||||||
log.Debug("Retrieved legacy DSA SHA1 SigningPublicKey (Certificate present but not Key Certificate)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// LeaseCount returns the numbert of leases specified by the LeaseCount value as int.
|
|
||||||
// returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
|
||||||
log.Debug("Retrieving LeaseCount from LeaseSet")
|
|
||||||
_, remainder, err := ReadKeysAndCert(lease_set)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to read KeysAndCert for LeaseCount")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
remainder_len := len(remainder)
|
|
||||||
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) LeaseCount",
|
|
||||||
"data_len": remainder_len,
|
|
||||||
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing lease count")
|
|
||||||
err = errors.New("error parsing lease count: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c := Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
|
|
||||||
count = c.Int()
|
|
||||||
if count > 16 {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) LeaseCount",
|
|
||||||
"lease_count": count,
|
|
||||||
"reason": "more than 16 leases",
|
|
||||||
}).Warn("invalid lease set")
|
|
||||||
err = errors.New("invalid lease set: more than 16 leases")
|
|
||||||
} else {
|
|
||||||
log.WithField("lease_count", count).Debug("Retrieved LeaseCount from LeaseSet")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leases returns the leases as []Lease.
|
|
||||||
// returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
|
||||||
log.Debug("Retrieving Leases from LeaseSet")
|
|
||||||
destination, err := lease_set.Destination()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve Destination for Leases")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
|
|
||||||
count, err := lease_set.LeaseCount()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve LeaseCount for Leases")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
start := offset + (i * LEASE_SIZE)
|
|
||||||
end := start + LEASE_SIZE
|
|
||||||
lease_set_len := len(lease_set)
|
|
||||||
if lease_set_len < end {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) Leases",
|
|
||||||
"data_len": lease_set_len,
|
|
||||||
"required_len": end,
|
|
||||||
"reason": "some leases missing",
|
|
||||||
}).Error("error parsnig lease set")
|
|
||||||
err = errors.New("error parsing lease set: some leases missing")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var lease Lease
|
|
||||||
copy(lease[:], lease_set[start:end])
|
|
||||||
leases = append(leases, lease)
|
|
||||||
}
|
|
||||||
log.WithField("lease_count", len(leases)).Debug("Retrieved Leases from LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signature returns the signature as Signature.
|
|
||||||
// returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) Signature() (signature Signature, err error) {
|
|
||||||
log.Debug("Retrieving Signature from LeaseSet")
|
|
||||||
destination, err := lease_set.Destination()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve Destination for Signature")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lease_count, err := lease_set.LeaseCount()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve LeaseCount for Signature")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
start := len(destination.Bytes()) +
|
|
||||||
LEASE_SET_PUBKEY_SIZE +
|
|
||||||
LEASE_SET_SPK_SIZE +
|
|
||||||
1 +
|
|
||||||
(LEASE_SIZE * lease_count)
|
|
||||||
cert := destination.Certificate()
|
|
||||||
cert_type := cert.Type()
|
|
||||||
var end int
|
|
||||||
if cert_type == CERT_KEY {
|
|
||||||
end = start + KeyCertificateFromCertificate(cert).SignatureSize()
|
|
||||||
} else {
|
|
||||||
end = start + LEASE_SET_SIG_SIZE
|
|
||||||
}
|
|
||||||
lease_set_len := len(lease_set)
|
|
||||||
if lease_set_len < end {
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"at": "(LeaseSet) Signature",
|
|
||||||
"data_len": lease_set_len,
|
|
||||||
"required_len": end,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing signatre")
|
|
||||||
err = errors.New("error parsing signature: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
signature = []byte(lease_set[start:end])
|
|
||||||
log.WithField("signature_length", len(signature)).Debug("Retrieved Signature from LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify returns nil
|
|
||||||
func (lease_set LeaseSet) Verify() error {
|
|
||||||
log.Debug("Verifying LeaseSet")
|
|
||||||
//data_end := len(destination) +
|
|
||||||
// LEASE_SET_PUBKEY_SIZE +
|
|
||||||
// LEASE_SET_SPK_SIZE +
|
|
||||||
// 1 +
|
|
||||||
// (44 * lease_set.LeaseCount())
|
|
||||||
//data := lease_set[:data_end]
|
|
||||||
//spk, _ := lease_set.
|
|
||||||
// Destination().
|
|
||||||
// SigningPublicKey()
|
|
||||||
//verifier, err := spk.NewVerifier()
|
|
||||||
//if err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
log.Warn("LeaseSet verification not implemented")
|
|
||||||
return nil // verifier.Verify(data, lease_set.Signature())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewestExpiration returns the newest lease expiration as an I2P Date.
|
|
||||||
// Returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
|
|
||||||
log.Debug("Finding newest expiration in LeaseSet")
|
|
||||||
leases, err := lease_set.Leases()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve Leases for NewestExpiration")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
||||||
for _, lease := range leases {
|
|
||||||
date := lease.Date()
|
|
||||||
if date.Time().After(newest.Time()) {
|
|
||||||
newest = date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.WithField("newest_expiration", newest.Time()).Debug("Found newest expiration in LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// OldestExpiration returns the oldest lease expiration as an I2P Date.
|
|
||||||
// Returns errors encountered during parsing.
|
|
||||||
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
|
|
||||||
log.Debug("Finding oldest expiration in LeaseSet")
|
|
||||||
leases, err := lease_set.Leases()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("Failed to retrieve Leases for OldestExpiration")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
|
||||||
for _, lease := range leases {
|
|
||||||
date := lease.Date()
|
|
||||||
if date.Time().Before(earliest.Time()) {
|
|
||||||
earliest = date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.WithField("oldest_expiration", earliest.Time()).Debug("Found oldest expiration in LeaseSet")
|
|
||||||
return
|
|
||||||
}
|
|
105
lib/common/lease_set_2.go
Normal file
105
lib/common/lease_set_2.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
https://geti2p.net/spec/common-structures#leaseset2
|
||||||
|
LeaseSet2
|
||||||
|
Description
|
||||||
|
|
||||||
|
Contained in a I2NP DatabaseStore message of type 3. Supported as of 0.9.38; see proposal 123 for more information.
|
||||||
|
|
||||||
|
Contains all of the currently authorized Lease2 for a particular Destination, and the PublicKey to which garlic messages can be encrypted. A LeaseSet is one of the two structures stored in the network database (the other being RouterInfo), and is keyed under the SHA256 of the contained Destination.
|
||||||
|
Contents
|
||||||
|
|
||||||
|
LeaseSet2Header, followed by a options, then one or more PublicKey for encryption, Integer specifying how many Lease2 structures are in the set, followed by the actual Lease2 structures and finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey or the transient key.
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| ls2_header |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| options |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|numk| keytype0| keylen0 | |
|
||||||
|
+----+----+----+----+----+ +
|
||||||
|
| encryption_key_0 |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| keytypen| keylenn | |
|
||||||
|
+----+----+----+----+ +
|
||||||
|
| encryption_key_n |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| num| Lease2 0 |
|
||||||
|
+----+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| Lease2($num-1) |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| signature |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
ls2header :: LeaseSet2Header
|
||||||
|
length -> varies
|
||||||
|
|
||||||
|
options :: Mapping
|
||||||
|
length -> varies, 2 bytes minimum
|
||||||
|
|
||||||
|
numk :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
Number of key types, key lengths, and PublicKeys to follow
|
||||||
|
value: 1 <= numk <= max TBD
|
||||||
|
|
||||||
|
keytype :: The encryption type of the PublicKey to follow.
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
keylen :: The length of the PublicKey to follow.
|
||||||
|
Must match the specified length of the encryption type.
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
encryption_key :: PublicKey
|
||||||
|
length -> 256 bytes
|
||||||
|
|
||||||
|
num :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
Number of Lease2s to follow
|
||||||
|
value: 0 <= num <= 16
|
||||||
|
|
||||||
|
leases :: [Lease2]
|
||||||
|
length -> $num*40 bytes
|
||||||
|
|
||||||
|
signature :: Signature
|
||||||
|
length -> 40 bytes or as specified in destination's key
|
||||||
|
certificate, or by the sigtype of the transient public key,
|
||||||
|
if present in the header
|
||||||
|
|
||||||
|
Notes
|
||||||
|
|
||||||
|
The public key of the destination was used for the old I2CP-to-I2CP encryption which was disabled in version 0.6, it is currently unused.
|
||||||
|
The encryption keys are used for end-to-end ElGamal/AES+SessionTag encryption [ELGAMAL-AES] (type 0) or other end-to-end encryption schemes. See [ECIES] and proposals 145 and 156. They may be generated anew at every router startup or they may be persistent. X25519 (type 4, see [ECIES]) is supported as of release 0.9.44.
|
||||||
|
The signature is over the data above, PREPENDED with the single byte containing the DatabaseStore type (3).
|
||||||
|
The signature may be verified using the signing public key of the destination, or the transient signing public key, if an offline signature is included in the leaseset2 header.
|
||||||
|
The key length is provided for each key, so that floodfills and clients may parse the structure even if not all encryption types are known or supported.
|
||||||
|
|
||||||
|
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/LeaseSet2.html
|
||||||
|
|
||||||
|
*/
|
@ -1,22 +1,16 @@
|
|||||||
package lease_set
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/go-i2p/go-i2p/lib/common/certificate"
|
|
||||||
common "github.com/go-i2p/go-i2p/lib/common/data"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/common/lease"
|
|
||||||
"github.com/go-i2p/go-i2p/lib/common/router_identity"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildDestination() *router_identity.RouterIdentity {
|
func buildDestination() RouterIdentity {
|
||||||
router_ident_data := make([]byte, 128+256)
|
router_ident_data := make([]byte, 128+256)
|
||||||
router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
||||||
ident, _, err := router_identity.ReadRouterIdentity(router_ident_data)
|
rri, _, _ := ReadRouterIdentity(router_ident_data)
|
||||||
panic(err)
|
return rri
|
||||||
return &ident
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildPublicKey() []byte {
|
func buildPublicKey() []byte {
|
||||||
@ -38,15 +32,15 @@ func buildSigningKey() []byte {
|
|||||||
func buildLease(n int) []byte {
|
func buildLease(n int) []byte {
|
||||||
data := make([]byte, 0)
|
data := make([]byte, 0)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
l := make([]byte, lease.LEASE_SIZE)
|
lease := make([]byte, LEASE_SIZE)
|
||||||
for p := range l {
|
for p := range lease {
|
||||||
l[p] = byte(i)
|
lease[p] = byte(i)
|
||||||
}
|
}
|
||||||
for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ {
|
for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ {
|
||||||
l[q] = 0x00
|
lease[q] = 0x00
|
||||||
}
|
}
|
||||||
l[lease.LEASE_SIZE-1] = byte(i + 10)
|
lease[LEASE_SIZE-1] = byte(i + 10)
|
||||||
data = append(data, l...)
|
data = append(data, lease...)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@ -61,33 +55,34 @@ func buildSignature(size int) []byte {
|
|||||||
|
|
||||||
func buildFullLeaseSet(n int) LeaseSet {
|
func buildFullLeaseSet(n int) LeaseSet {
|
||||||
lease_set_data := make([]byte, 0)
|
lease_set_data := make([]byte, 0)
|
||||||
lease_set_data = append(lease_set_data, buildDestination().KeysAndCert.KeyCertificate.RawBytes()...)
|
lease_set_data = append(lease_set_data, buildDestination().Bytes()...)
|
||||||
lease_set_data = append(lease_set_data, buildPublicKey()...)
|
lease_set_data = append(lease_set_data, buildPublicKey()...)
|
||||||
lease_set_data = append(lease_set_data, buildSigningKey()...)
|
lease_set_data = append(lease_set_data, buildSigningKey()...)
|
||||||
lease_set_data = append(lease_set_data, byte(n))
|
lease_set_data = append(lease_set_data, byte(n))
|
||||||
lease_set_data = append(lease_set_data, buildLease(n)...)
|
lease_set_data = append(lease_set_data, buildLease(n)...)
|
||||||
lease_set_data = append(lease_set_data, buildSignature(64)...)
|
lease_set_data = append(lease_set_data, buildSignature(64)...)
|
||||||
return LeaseSet(lease_set_data)
|
leaseSet, _, _ := ReadLeaseSet(lease_set_data)
|
||||||
|
return leaseSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDestinationIsCorrect(t *testing.T) {
|
func TestDestinationIsCorrect(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
lease_set := buildFullLeaseSet(1)
|
lease_set := buildFullLeaseSet(1)
|
||||||
dest, err := lease_set.Destination()
|
dest, err := lease_set.GetDestination()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
dest_cert := dest.Certificate()
|
dest_cert, err := dest.Certificate()
|
||||||
// assert.Nil(err)
|
|
||||||
cert_type := dest_cert.Type()
|
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(certificate.CERT_KEY, cert_type)
|
_, cert_bytes, err := dest_cert.Type()
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(CERT_KEY, cert_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeyIsCorrect(t *testing.T) {
|
func TestPublicKeyIsCorrect(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
lease_set := buildFullLeaseSet(1)
|
lease_set := buildFullLeaseSet(1)
|
||||||
pk, err := lease_set.PublicKey()
|
pk, err := lease_set.GetPublicKey()
|
||||||
if assert.Nil(err) {
|
if assert.Nil(err) {
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
0,
|
0,
|
||||||
@ -103,7 +98,7 @@ func TestSigningKeyIsCorrect(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
lease_set := buildFullLeaseSet(1)
|
lease_set := buildFullLeaseSet(1)
|
||||||
sk, err := lease_set.SigningKey()
|
sk, err := lease_set.GetSigningKey()
|
||||||
if assert.Nil(err) {
|
if assert.Nil(err) {
|
||||||
assert.Equal(128, sk.Len())
|
assert.Equal(128, sk.Len())
|
||||||
}
|
}
|
||||||
@ -149,19 +144,19 @@ func TestLeasesHaveCorrectData(t *testing.T) {
|
|||||||
leases, err := lease_set.Leases()
|
leases, err := lease_set.Leases()
|
||||||
if assert.Nil(err) {
|
if assert.Nil(err) {
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
l := make([]byte, lease.LEASE_SIZE)
|
lease := make([]byte, LEASE_SIZE)
|
||||||
for p := range l {
|
for p := range lease {
|
||||||
l[p] = byte(i)
|
lease[p] = byte(i)
|
||||||
}
|
}
|
||||||
for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ {
|
for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ {
|
||||||
l[q] = 0x00
|
lease[q] = 0x00
|
||||||
}
|
}
|
||||||
l[lease.LEASE_SIZE-1] = byte(i + 10)
|
lease[LEASE_SIZE-1] = byte(i + 10)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
0,
|
0,
|
||||||
bytes.Compare(
|
bytes.Compare(
|
||||||
l,
|
lease,
|
||||||
leases[i][:],
|
leases[i].Bytes()[:],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -173,7 +168,7 @@ func TestSignatureIsCorrect(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
lease_set := buildFullLeaseSet(1)
|
lease_set := buildFullLeaseSet(1)
|
||||||
sig, err := lease_set.Signature()
|
sig, err := lease_set.GetSignature()
|
||||||
if assert.Nil(err) {
|
if assert.Nil(err) {
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
0,
|
0,
|
||||||
@ -191,9 +186,8 @@ func TestNewestExpirationIsCorrect(t *testing.T) {
|
|||||||
lease_set := buildFullLeaseSet(5)
|
lease_set := buildFullLeaseSet(5)
|
||||||
latest, err := lease_set.NewestExpiration()
|
latest, err := lease_set.NewestExpiration()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)})
|
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
Date,
|
Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)},
|
||||||
latest,
|
latest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -204,9 +198,8 @@ func TestOldestExpirationIsCorrect(t *testing.T) {
|
|||||||
lease_set := buildFullLeaseSet(5)
|
lease_set := buildFullLeaseSet(5)
|
||||||
latest, err := lease_set.OldestExpiration()
|
latest, err := lease_set.OldestExpiration()
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a})
|
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
Date,
|
Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
|
||||||
latest,
|
latest,
|
||||||
)
|
)
|
||||||
}
|
}
|
228
lib/common/mapping.go
Normal file
228
lib/common/mapping.go
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Mapping
|
||||||
|
https://geti2p.net/spec/common-structures#mapping
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| size |key_string (len + data) | = |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| val_string (len + data) | ; | ...
|
||||||
|
+----+----+----+----+----+----+----+
|
||||||
|
size :: Integer
|
||||||
|
length -> 2 bytes
|
||||||
|
Total number of bytes that follow
|
||||||
|
|
||||||
|
key_string :: String
|
||||||
|
A string (one byte length followed by UTF-8 encoded characters)
|
||||||
|
|
||||||
|
= :: A single byte containing '='
|
||||||
|
|
||||||
|
val_string :: String
|
||||||
|
A string (one byte length followed by UTF-8 encoded characters)
|
||||||
|
|
||||||
|
; :: A single byte containing ';'
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mapping []byte
|
||||||
|
|
||||||
|
// Parsed key-values pairs inside a Mapping.
|
||||||
|
type MappingValues [][2]String
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 remainder = mapping
|
||||||
|
var err error
|
||||||
|
|
||||||
|
length, err := NewInteger(remainder[:2])
|
||||||
|
inferred_length := length.Value() + 2
|
||||||
|
remainder = remainder[2:]
|
||||||
|
mapping_len := len(mapping)
|
||||||
|
if mapping_len > inferred_length {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Mapping) Values",
|
||||||
|
"mappnig_bytes_length": mapping_len,
|
||||||
|
"mapping_length_field": length,
|
||||||
|
"expected_bytes_length": inferred_length,
|
||||||
|
"reason": "data longer than expected",
|
||||||
|
}).Warn("mapping format warning")
|
||||||
|
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
|
||||||
|
} else if inferred_length > mapping_len {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Mapping) Values",
|
||||||
|
"mappnig_bytes_length": mapping_len,
|
||||||
|
"mapping_length_field": length,
|
||||||
|
"expected_bytes_length": inferred_length,
|
||||||
|
"reason": "data shorter than expected",
|
||||||
|
}).Warn("mapping format warning")
|
||||||
|
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Read a key, breaking on fatal errors
|
||||||
|
// and appending warnings
|
||||||
|
str, remainder, err = ReadString(remainder)
|
||||||
|
key_str := str
|
||||||
|
if err != nil {
|
||||||
|
if stopValueRead(err) {
|
||||||
|
errs = append(errs, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !beginsWith(remainder, 0x3d) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Mapping) Values",
|
||||||
|
"reason": "expected =",
|
||||||
|
}).Warn("mapping format violation")
|
||||||
|
errs = append(errs, errors.New("mapping format violation, expected ="))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remainder = remainder[1:]
|
||||||
|
|
||||||
|
// Read a value, breaking on fatal errors
|
||||||
|
// and appending warnings
|
||||||
|
str, remainder, err = ReadString(remainder)
|
||||||
|
val_str := str
|
||||||
|
if err != nil {
|
||||||
|
if stopValueRead(err) {
|
||||||
|
errs = append(errs, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !beginsWith(remainder, 0x3b) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Mapping) Values",
|
||||||
|
"reason": "expected ;",
|
||||||
|
}).Warn("mapping format violation")
|
||||||
|
errs = append(errs, errors.New("mapping format violation, expected ;"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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})
|
||||||
|
if len(remainder) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return true if two keys in a mapping are identical.
|
||||||
|
//
|
||||||
|
func (mapping Mapping) HasDuplicateKeys() bool {
|
||||||
|
seen_values := make(map[string]bool)
|
||||||
|
values, _ := mapping.Values()
|
||||||
|
for _, pair := range values {
|
||||||
|
key, _ := pair[0].Data()
|
||||||
|
if _, present := seen_values[key]; present {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
seen_values[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Convert a MappingValue struct to a Mapping. The values are first
|
||||||
|
// sorted in the order defined in mappingOrder.
|
||||||
|
//
|
||||||
|
func ValuesToMapping(values MappingValues) (mapping Mapping) {
|
||||||
|
mappingOrder(values)
|
||||||
|
for _, kv_pair := range values {
|
||||||
|
key_string := kv_pair[0]
|
||||||
|
key_string = append(key_string, []byte("=")[0])
|
||||||
|
key_value := kv_pair[1]
|
||||||
|
key_value = append(key_value, []byte(";")[0])
|
||||||
|
mapping = append(append(mapping, key_string...), key_value...)
|
||||||
|
}
|
||||||
|
map_len := len(mapping)
|
||||||
|
len_bytes := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(len_bytes, uint16(map_len))
|
||||||
|
mapping = append(len_bytes, mapping...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Convert a Go map of unformatted strings to a sorted Mapping.
|
||||||
|
//
|
||||||
|
func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
|
||||||
|
map_vals := MappingValues{}
|
||||||
|
for k, v := range gomap {
|
||||||
|
key_str, kerr := ToI2PString(k)
|
||||||
|
if kerr != nil {
|
||||||
|
err = kerr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val_str, verr := ToI2PString(v)
|
||||||
|
if verr != nil {
|
||||||
|
err = verr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
map_vals = append(
|
||||||
|
map_vals,
|
||||||
|
[2]String{key_str, val_str},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mapping = ValuesToMapping(map_vals)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type byValue MappingValues
|
||||||
|
|
||||||
|
func (set byValue) Len() int { return len(set) }
|
||||||
|
func (set byValue) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
|
||||||
|
func (set byValue) Less(i, j int) bool {
|
||||||
|
data1, _ := set[i][1].Data()
|
||||||
|
data2, _ := set[j][1].Data()
|
||||||
|
return data1 < data2
|
||||||
|
}
|
||||||
|
|
||||||
|
type byKey MappingValues
|
||||||
|
|
||||||
|
func (set byKey) Len() int { return len(set) }
|
||||||
|
func (set byKey) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
|
||||||
|
func (set byKey) Less(i, j int) bool {
|
||||||
|
data1, _ := set[i][0].Data()
|
||||||
|
data2, _ := set[j][0].Data()
|
||||||
|
return data1 < data2
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// I2P Mappings require consistent order for for cryptographic signing, and sorting
|
||||||
|
// by keys. When new Mappings are created, they are stable sorted first by values
|
||||||
|
// than by keys to ensure a consistent order.
|
||||||
|
//
|
||||||
|
func mappingOrder(values MappingValues) {
|
||||||
|
sort.Stable(byValue(values))
|
||||||
|
sort.Stable(byKey(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the string parsing error indicates that the Mapping
|
||||||
|
// should no longer be parsed.
|
||||||
|
//
|
||||||
|
func stopValueRead(err error) bool {
|
||||||
|
return err.Error() == "error parsing string: zero length"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Determine if the first byte in a slice of bytes is the provided byte.
|
||||||
|
//
|
||||||
|
func beginsWith(bytes []byte, chr byte) bool {
|
||||||
|
return len(bytes) != 0 &&
|
||||||
|
bytes[0] == chr
|
||||||
|
}
|
187
lib/common/mapping_test.go
Normal file
187
lib/common/mapping_test.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValuesExclusesPairWithBadData(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
bad_key := Mapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
|
||||||
|
values, errs := bad_key.Values()
|
||||||
|
|
||||||
|
if assert.Equal(len(values), 1, "Values() did not return valid values when some values had bad key") {
|
||||||
|
key, _ := values[0][0].Data()
|
||||||
|
val, _ := values[0][1].Data()
|
||||||
|
assert.Equal(key, "a", "Values() returned by data with invalid key contains incorrect present key")
|
||||||
|
assert.Equal(val, "b", "Values() returned by data with invalid key contains incorrect present key")
|
||||||
|
}
|
||||||
|
assert.Equal(len(errs), 2, "Values() reported wrong error count when some values had invalid data")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValuesWarnsMissingData(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62})
|
||||||
|
_, errs := mapping.Values()
|
||||||
|
|
||||||
|
if assert.Equal(len(errs), 2, "Values() reported wrong error count when mapping had missing data") {
|
||||||
|
assert.Equal(errs[0].Error(), "warning parsing mapping: mapping length exceeds provided data", "correct error message should be returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValuesWarnsExtraData(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
|
||||||
|
_, errs := mapping.Values()
|
||||||
|
|
||||||
|
if assert.Equal(len(errs), 2, "Values() reported wrong error count when mapping had extra data") {
|
||||||
|
assert.Equal(errs[0].Error(), "warning parsing mapping: data exists beyond length of mapping", "correct error message should be returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValuesEnforcesEqualDelimitor(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x30, 0x01, 0x62, 0x3b})
|
||||||
|
values, errs := mapping.Values()
|
||||||
|
|
||||||
|
if assert.Equal(len(errs), 1, "Values() reported wrong error count when mapping had = format error") {
|
||||||
|
assert.Equal(errs[0].Error(), "mapping format violation, expected =", "correct error message should be returned")
|
||||||
|
}
|
||||||
|
assert.Equal(len(values), 0, "Values() not empty with invalid data due to = format error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValuesEnforcedSemicolonDelimitor(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x30})
|
||||||
|
values, errs := mapping.Values()
|
||||||
|
|
||||||
|
if assert.Equal(len(errs), 1, "Values() reported wrong error count when mapping had ; format error") {
|
||||||
|
assert.Equal(errs[0].Error(), "mapping format violation, expected ;", "correct error message should be returned")
|
||||||
|
}
|
||||||
|
assert.Equal(len(values), 0, "Values() not empty with invalid data due to ; format error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValuesReturnsValues(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
||||||
|
values, errs := mapping.Values()
|
||||||
|
key, kerr := values[0][0].Data()
|
||||||
|
val, verr := values[0][1].Data()
|
||||||
|
|
||||||
|
assert.Nil(errs, "Values() returned a errors with parsing valid data")
|
||||||
|
assert.Nil(kerr)
|
||||||
|
assert.Nil(verr)
|
||||||
|
assert.Equal(key, "a", "Values() did not return key in valid data")
|
||||||
|
assert.Equal(val, "b", "Values() did not return value in valid data")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasDuplicateKeysTrueWhenDuplicates(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
dups := Mapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
||||||
|
|
||||||
|
assert.Equal(dups.HasDuplicateKeys(), true, "HasDuplicateKeys() did not report true when duplicate keys present")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasDuplicateKeysFalseWithoutDuplicates(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
|
||||||
|
|
||||||
|
assert.Equal(mapping.HasDuplicateKeys(), false, "HasDuplicateKeys() did not report false when no duplicate keys present")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoMapToMappingProducesCorrectMapping(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
gomap := map[string]string{"a": "b"}
|
||||||
|
mapping, err := GoMapToMapping(gomap)
|
||||||
|
|
||||||
|
assert.Nil(err, "GoMapToMapping() returned error with valid data")
|
||||||
|
expected := []byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b}
|
||||||
|
if bytes.Compare(mapping, expected) != 0 {
|
||||||
|
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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},
|
||||||
|
}
|
||||||
|
mappingOrder(values)
|
||||||
|
for i, pair := range values {
|
||||||
|
key, _ := pair[0].Data()
|
||||||
|
value, _ := pair[1].Data()
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
if !(key == "a" && value == "a") {
|
||||||
|
t.Fatal("mappingOrder produced incorrect sort output at", i)
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if !(key == "a" && value == "b") {
|
||||||
|
t.Fatal("mappingOrder produced incorrect sort output at", i)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if !(key == "b" && value == "a") {
|
||||||
|
t.Fatal("mappingOrder produced incorrect sort output at", i)
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if !(key == "b" && value == "b") {
|
||||||
|
t.Fatal("mappingOrder produced incorrect sort output at", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStopValueReadTrueWhenCorrectErr(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
status := stopValueRead(errors.New("error parsing string: zero length"))
|
||||||
|
|
||||||
|
assert.Equal(status, true, "stopValueRead() did not return true when String error found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStopValueReadFalseWhenWrongErr(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
status := stopValueRead(errors.New("something else"))
|
||||||
|
|
||||||
|
assert.Equal(status, false, "stopValueRead() did not return false when non String error found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBeginsWithCorrectWhenTrue(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
slice := []byte{0x41}
|
||||||
|
|
||||||
|
assert.Equal(beginsWith(slice, 0x41), true, "beginsWith() did not return true when correct")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBeginsWithCorrectWhenFalse(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
slice := []byte{0x00}
|
||||||
|
|
||||||
|
assert.Equal(beginsWith(slice, 0x41), false, "beginsWith() did not false when incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBeginsWithCorrectWhenNil(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
slice := make([]byte, 0)
|
||||||
|
|
||||||
|
assert.Equal(beginsWith(slice, 0x41), false, "beginsWith() did not return false on empty slice")
|
||||||
|
}
|
161
lib/common/router_address.go
Normal file
161
lib/common/router_address.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P RouterAddress
|
||||||
|
https://geti2p.net/spec/common-structures#routeraddress
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|cost| expiration
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| transport_style |
|
||||||
|
+----+----+----+----+-//-+----+----+----+
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| options |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
cost :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
|
||||||
|
case 0 -> free
|
||||||
|
case 255 -> expensive
|
||||||
|
|
||||||
|
expiration :: Date (must be all zeros, see notes below)
|
||||||
|
length -> 8 bytes
|
||||||
|
|
||||||
|
case null -> never expires
|
||||||
|
|
||||||
|
transport_style :: String
|
||||||
|
length -> 1-256 bytes
|
||||||
|
|
||||||
|
options :: Mapping
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Minimum number of bytes in a valid RouterAddress
|
||||||
|
const (
|
||||||
|
ROUTER_ADDRESS_MIN_SIZE = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
type RouterAddress []byte
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the cost integer for this RouterAddress and any errors encountered
|
||||||
|
// parsing the RouterAddress.
|
||||||
|
//
|
||||||
|
func (router_address RouterAddress) Cost() (cost *Integer, err error) {
|
||||||
|
err, exit := router_address.checkValid()
|
||||||
|
if exit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cost, err = NewInteger([]byte{router_address[0]})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Date this RouterAddress expires and any errors encountered
|
||||||
|
// parsing the RouterAddress.
|
||||||
|
//
|
||||||
|
func (router_address RouterAddress) Expiration() (date Date, err error) {
|
||||||
|
err, exit := router_address.checkValid()
|
||||||
|
if exit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(date[:], router_address[1:ROUTER_ADDRESS_MIN_SIZE])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Transport type for this RouterAddress and any errors encountered
|
||||||
|
// parsing the RouterAddress.
|
||||||
|
//
|
||||||
|
func (router_address RouterAddress) TransportStyle() (str String, err error) {
|
||||||
|
err, exit := router_address.checkValid()
|
||||||
|
if exit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
str, _, err = ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Mapping containing the options for this RouterAddress and any
|
||||||
|
// errors encountered parsing the RouterAddress.
|
||||||
|
//
|
||||||
|
func (router_address RouterAddress) Options() (mapping Mapping, err error) {
|
||||||
|
err, exit := router_address.checkValid()
|
||||||
|
if exit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, remainder, err := ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||||
|
if len(remainder) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mapping = Mapping(remainder)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the RouterAddress is empty or if it is too small to contain valid data.
|
||||||
|
//
|
||||||
|
func (router_address RouterAddress) checkValid() (err error, exit bool) {
|
||||||
|
addr_len := len(router_address)
|
||||||
|
exit = false
|
||||||
|
if addr_len == 0 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(RouterAddress) checkValid",
|
||||||
|
"reason": "no data",
|
||||||
|
}).Error("invalid router address")
|
||||||
|
err = errors.New("error parsing RouterAddress: no data")
|
||||||
|
exit = true
|
||||||
|
} else if addr_len < ROUTER_ADDRESS_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(RouterAddress) checkValid",
|
||||||
|
"reason": "data too small (len < ROUTER_ADDRESS_MIN_SIZE)",
|
||||||
|
}).Warn("router address format warning")
|
||||||
|
err = errors.New("warning parsing RouterAddress: data too small")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Given a slice of bytes, read a RouterAddress, returning the remaining bytes and any
|
||||||
|
// errors encountered parsing the RouterAddress.
|
||||||
|
//
|
||||||
|
func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []byte, err error) {
|
||||||
|
test_address := RouterAddress(data)
|
||||||
|
err, _ = test_address.checkValid()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router_address = append(router_address, data[:ROUTER_ADDRESS_MIN_SIZE]...)
|
||||||
|
str, remainder, err := ReadString(data[ROUTER_ADDRESS_MIN_SIZE:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router_address = append(router_address, str...)
|
||||||
|
map_size := &Integer{}
|
||||||
|
mapping := make([]byte, 0)
|
||||||
|
if len(remainder) >= 2 {
|
||||||
|
map_size, err = NewInteger(remainder[:2])
|
||||||
|
if len(remainder) < map_size.Value()+2 {
|
||||||
|
err = errors.New("not enough data for map inside router address")
|
||||||
|
router_address = RouterAddress([]byte{})
|
||||||
|
remainder = []byte{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mapping = remainder[:map_size.Value()+2]
|
||||||
|
router_address = append(router_address, mapping...)
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = data[ROUTER_ADDRESS_MIN_SIZE+len(str)+len(mapping):]
|
||||||
|
return
|
||||||
|
}
|
@ -1,192 +0,0 @@
|
|||||||
# router_address
|
|
||||||
--
|
|
||||||
import "github.com/go-i2p/go-i2p/lib/common/router_address"
|
|
||||||
|
|
||||||
Package router_address implements the I2P RouterAddress common data structure
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
ROUTER_ADDRESS_MIN_SIZE = 9
|
|
||||||
)
|
|
||||||
```
|
|
||||||
Minimum number of bytes in a valid RouterAddress
|
|
||||||
|
|
||||||
#### type RouterAddress
|
|
||||||
|
|
||||||
```go
|
|
||||||
type RouterAddress struct {
|
|
||||||
TransportCost *Integer
|
|
||||||
ExpirationDate *Date
|
|
||||||
TransportType I2PString
|
|
||||||
TransportOptions *Mapping
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
RouterAddress is the represenation of an I2P RouterAddress.
|
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#routeraddress
|
|
||||||
|
|
||||||
#### func ReadRouterAddress
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []byte, err error)
|
|
||||||
```
|
|
||||||
ReadRouterAddress returns RouterAddress from a []byte. The remaining bytes after
|
|
||||||
the specified length are also returned. Returns a list of errors that occurred
|
|
||||||
during parsing.
|
|
||||||
|
|
||||||
#### func (RouterAddress) Bytes
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Bytes() []byte
|
|
||||||
```
|
|
||||||
Bytes returns the router address as a []byte.
|
|
||||||
|
|
||||||
#### func (RouterAddress) CapsString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) CapsString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) Cost
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Cost() int
|
|
||||||
```
|
|
||||||
Cost returns the cost for this RouterAddress as a Go integer.
|
|
||||||
|
|
||||||
#### func (RouterAddress) Expiration
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Expiration() Date
|
|
||||||
```
|
|
||||||
Expiration returns the expiration for this RouterAddress as an I2P Date.
|
|
||||||
|
|
||||||
#### func (RouterAddress) GetOption
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) GetOption(key I2PString) I2PString
|
|
||||||
```
|
|
||||||
GetOption returns the value of the option specified by the key
|
|
||||||
|
|
||||||
#### func (RouterAddress) Host
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Host() (net.Addr, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) HostString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) HostString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*RouterAddress) IPVersion
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address *RouterAddress) IPVersion() string
|
|
||||||
```
|
|
||||||
IPVersion returns a string "4" for IPv4 or 6 for IPv6
|
|
||||||
|
|
||||||
#### func (RouterAddress) InitializationVector
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) InitializationVector() ([32]byte, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) InitializationVectorString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) InitializationVectorString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) IntroducerExpirationString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) IntroducerExpirationString(num int) I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) IntroducerHashString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) IntroducerHashString(num int) I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) IntroducerTagString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) IntroducerTagString(num int) I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*RouterAddress) Network
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address *RouterAddress) Network() string
|
|
||||||
```
|
|
||||||
Network implements net.Addr. It returns the transport type plus 4 or 6
|
|
||||||
|
|
||||||
#### func (RouterAddress) Options
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Options() Mapping
|
|
||||||
```
|
|
||||||
Options returns the options for this RouterAddress as an I2P Mapping.
|
|
||||||
|
|
||||||
#### func (RouterAddress) Port
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) Port() (string, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) PortString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) PortString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) ProtocolVersion
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) ProtocolVersion() (string, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) ProtocolVersionString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) ProtocolVersionString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) StaticKey
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) StaticKey() ([32]byte, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (RouterAddress) StaticKeyString
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) StaticKeyString() I2PString
|
|
||||||
```
|
|
||||||
|
|
||||||
#### func (*RouterAddress) String
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address *RouterAddress) String() string
|
|
||||||
```
|
|
||||||
String implements net.Addr. It returns the IP address, followed by the options
|
|
||||||
|
|
||||||
#### func (RouterAddress) TransportStyle
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address RouterAddress) TransportStyle() I2PString
|
|
||||||
```
|
|
||||||
TransportStyle returns the transport style for this RouterAddress as an
|
|
||||||
I2PString.
|
|
||||||
|
|
||||||
#### func (*RouterAddress) UDP
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (router_address *RouterAddress) UDP() bool
|
|
||||||
```
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user