158 Commits

Author SHA1 Message Date
Haris Khan
ddba94d6ae remove printing 2024-11-15 22:48:57 -05:00
Haris Khan
767b91df49 clean up temp dirs 2024-11-15 22:47:36 -05:00
Haris Khan
1292098cf0 Merge remote-tracking branch 'origin/10k' into 10k 2024-11-15 22:45:04 -05:00
eyedeekay
24bc4c3c17 Implemented ed25519 SPK's 2024-11-15 22:29:42 -05:00
Haris Khan
81eb270351 !WIP! - 10k test 2024-11-15 22:06:23 -05:00
eyedeekay
b6f197cf92 This is not correct yet, work on key_certificate.go lines 216-245 2024-11-15 17:35:44 -05:00
eyedeekay
c10d98a3b2 export DEBUG_I2P=debug in Makefile so that extended logs show up in the tests 2024-11-15 16:52:37 -05:00
Haris Khan
6d16ca5f87 debugging info to investigate 2024-11-15 14:43:36 -05:00
Haris Khan
003d6c9ab8 !WIP! - 10k test 2024-11-15 13:53:44 -05:00
Haris Khan
015c4b23e2 pass sigType to NewRouterInfo 2024-11-15 11:49:10 -05:00
Haris Khan
e29c3c7abb formatting + implemented GetSignatureTypeFromCertificate 2024-11-15 11:31:29 -05:00
Haris Khan
6f6291a9f6 bounds checking 2024-11-15 11:31:10 -05:00
Haris Khan
767864d457 corrected order in Bytes() 2024-11-15 11:30:59 -05:00
Haris Khan
0a98236d85 adjusted test 2024-11-15 11:30:40 -05:00
Haris Khan
c1fa63f6ec added sigtype 2024-11-15 11:30:34 -05:00
Haris Khan
a75c275b4c Corrected Bytes()
-TODO: add logging
2024-11-15 11:30:11 -05:00
Haris Khan
d40b3e0cd3 added sig types 2024-11-15 11:14:05 -05:00
Haris Khan
2ee2d77d7c check if data slice is empty 2024-11-15 11:01:06 -05:00
eyedeekay
271cf56ded Fixes the padding generation but the reader still thinks something's wrong 2024-11-10 19:29:07 -05:00
eyedeekay
a29fa0bc03 Output the rest of the key certificate, that would probably help... 2024-11-10 17:15:56 -05:00
eyedeekay
63c48dd3b7 log the actual bytes in the test for now, make sure they're really there 2024-11-09 13:41:19 -05:00
eyedeekay
8bec47efd2 log the actual bytes in the test for now, make sure they're really there 2024-11-09 13:39:38 -05:00
eyedeekay
69a50e2035 Try adding a dummy address, change logging to show where not enough data was provided 2024-11-09 13:33:23 -05:00
eyedeekay
8319444890 Try adding a dummy address, change logging to show where not enough data was provided 2024-11-09 13:31:24 -05:00
eyedeekay
b378661e0e Fix LS test, update Noise Subsystem on README.md 2024-11-09 12:33:53 -05:00
Haris Khan
f4086e5f68 !WIP! - Router Info 2024-11-09 11:13:48 -05:00
Haris Khan
877fc707c4 !WIP! - Router Info 2024-11-09 01:04:44 -05:00
Haris Khan
98d05e27c8 !WIP! - Router Info 2024-11-09 00:53:17 -05:00
eyedeekay
4020db8a19 Refactor: Certificate Constructor: 2024-11-04 19:19:03 -05:00
eyedeekay
67a02f5d69 Noise: Refactor, remove unused fields 2024-11-04 18:26:17 -05:00
eyedeekay
ca1280231c Tidy 2024-11-04 15:25:54 -05:00
eyedeekay
02b309df43 Refactor: move HandshakeState to own struct in preparation for NTCP2 mods 2024-11-04 15:20:56 -05:00
eyedeekay
a5b3c3f194 NTCP2: Stub out NTCP2 and explain the task at hand, SSU2: Explain the plan 2024-11-04 00:24:08 -05:00
eyedeekay
0aa7a5554b Merge branch 'master' of github.com:go-i2p/go-i2p 2024-11-03 23:59:13 -05:00
eyedeekay
266a1b71d6 Cleanup: Move obfs.go to own package to avoid import cycle noise->ntcp. I probably should have caught that in review. 2024-11-03 23:58:49 -05:00
idk
d32f2e78ab Merge pull request #21 from hkh4n/config
move base dir from ~/go-i2p to ~/.go-i2p & other changes
2024-11-04 04:51:24 +00:00
idk
9e806bc32e Merge branch 'master' into config 2024-11-03 22:25:05 +00:00
idk
c52112a36f Merge pull request #22 from hkh4n/refactor
go mod tidy & fixed condition that was always true
2024-11-03 22:23:15 +00:00
eyedeekay
db0fd9f7e9 Management: rm PASTA.md 2024-11-03 15:53:23 -05:00
eyedeekay
3ab258cde6 Management: add ROADMAP.md 2024-11-03 15:53:13 -05:00
Haris Khan
20b9bbd8e4 go mod tidy & fixed condition that was always true 2024-11-03 01:52:48 -05:00
Haris Khan
1fa520613c replace deprecated function (ioutil -> os) 2024-11-03 00:14:45 -04:00
Haris Khan
fb99b98a7e minor typo fix 2024-11-02 21:37:56 -04:00
Haris Khan
d6b8cd9d4d go mod tidy 2024-11-02 21:37:29 -04:00
Haris Khan
92e4656774 move from ~/go-i2p to ~/.go-i2p 2024-11-02 21:37:23 -04:00
Haris Khan
5f2bfb8d9d gofumpt adjustment 2024-11-02 21:36:55 -04:00
idk
9494c226a6 Merge pull request #19 from go-i2p/noise-experimental
Noise Subsystem
2024-11-02 02:59:43 +00:00
idk
344edc6d41 Merge branch 'master' into noise-experimental 2024-11-02 02:59:24 +00:00
idk
9eea99b489 Merge pull request #20 from hkh4n/noise-experimental
Polishes
2024-11-01 14:30:25 +00:00
idk
c984f94b90 Merge pull request #9 from hkh4n/sntp-experimental
Implemented mimic implementation of sntp from the original java implementation
2024-11-01 14:28:00 +00:00
Haris Khan
a17f0208dd gofumpt adjustment 2024-10-31 10:53:22 -04:00
Haris Khan
487815f8f1 tests 2024-10-31 10:50:21 -04:00
Haris Khan
24e0baa879 Moved functions
-encryptPacketDeux -> testEncryptPacket in encrdecr_packet_test.go
-decryptPacketDeux -> testDecryptPacket in encrdecr_packet_test.go
2024-10-31 10:45:42 -04:00
Haris Khan
a5d2f0de8c lib/transport/noise/doc.md 2024-10-31 00:01:22 -04:00
Haris Khan
423f616d53 Info -> Debug 2024-10-30 23:29:46 -04:00
idk
c65048f3c4 Merge pull request #18 from hkh4n/config
Added config
2024-10-30 22:03:24 +00:00
Haris Khan
39b683dac8 fixed circular dependency 2024-10-30 11:20:15 -04:00
Haris Khan
46883f6457 build fails -> import cycle 2024-10-30 09:31:01 -04:00
Haris Khan
4be55062fc Moved config funcs to config.go 2024-10-30 09:22:32 -04:00
Haris Khan
12a3fd4623 Added config files + args
-added routerInstance
-added initConfig()
-added updateRouterConfig()
-yaml format
2024-10-30 09:16:22 -04:00
eyedeekay
80e539930e Mapping: fix off-by-one. Format: gofumpt 2024-10-28 18:11:54 -04:00
Haris Khan
e58d326d89 Move obfuscation functions to lib/transport/ntcp 2024-10-25 21:29:02 -04:00
Haris Khan
e6f84c16f6 added experimental functions which factor in packet length
-encryptPacketDeux
-decryptPacketDeux

Added test -> TestEncryptDecryptPacketObfsOfflineWithFunc()

added new functions in aes.go
-EncryptNoPadding
-DecryptNoPadding
2024-10-24 22:57:36 -04:00
Haris Khan
971a18de8d added Compatible warning message 2024-10-24 19:23:36 -04:00
Haris Khan
833836ae67 added TestEncryptDecryptPacketObfsOffline 2024-10-24 18:58:07 -04:00
Haris Khan
3f191db37a implement TODO for Compatible in NoiseTransport 2024-10-24 18:03:20 -04:00
idk
6865bae386 Merge pull request #17 from hkh4n/dirs
create ~/go-i2p and ~/go-i2p/config properly
2024-10-24 20:12:06 +00:00
Haris Khan
f0d9f89ed9 Mkdir -> MkdirAll 2024-10-24 14:26:51 -04:00
idk
c149eef1de Merge pull request #15 from hkh4n/makefile
Makefiles for tests + hotfix for logging naming convention
2024-10-23 17:34:06 +00:00
Haris Khan
748dc8a66f tweaks 2024-10-23 00:11:46 -04:00
Haris Khan
677aac500e fix logger naming collision with other libs 2024-10-23 00:06:06 -04:00
Haris Khan
4e06d3d5ed makefiles 2024-10-22 22:31:19 -04:00
Haris Khan
5eaa9cf588 log name collision fix 2024-10-22 22:10:14 -04:00
Haris Khan
4dbf537e94 log name collision fix 2024-10-22 22:07:39 -04:00
Haris Khan
882c018c0c log name collision fix 2024-10-22 22:06:06 -04:00
Haris Khan
5432502852 typo correction 2024-10-22 21:43:49 -04:00
idk
0ea5743365 Merge pull request #14 from hkh4n/noise-experimental
gofumpt adjustment
2024-10-22 21:53:24 +00:00
Haris Khan
a9dc482bda gofumpt 2024-10-22 17:37:17 -04:00
idk
c31d20fec0 Merge pull request #13 from hkh4n/logging
Logging
2024-10-22 18:55:15 +00:00
Haris Khan
647546a374 typo fix 2024-10-22 14:51:17 -04:00
Haris Khan
e468520906 Merge branch 'master' into sntp-experimental 2024-10-20 00:09:42 -04:00
Haris Khan
ade80e577c added sntp verification
-Leap Indicator
-Stratum level check
-Round-trip Delay
-Clock offset
-simple non-zero time
-Root Dispersion and Root Delay
2024-10-20 00:07:40 -04:00
Haris Khan
f45d301868 proposed refactor
-added secureRandBool() and performTimeQuery()
2024-10-19 22:17:10 -04:00
Haris Khan
0256908395 CRITICAL FIX, bytesRead -> n.
n wasn't being used before
2024-10-19 17:27:39 -04:00
Haris Khan
3c5aa206d1 expanded logging in message.go 2024-10-19 12:32:49 -04:00
Haris Khan
a4517cafd7 expanded logging in delivery.go 2024-10-19 12:25:31 -04:00
Haris Khan
f4f39ca53c expanded logging in multi.go 2024-10-19 11:18:20 -04:00
Haris Khan
220159476a expanded logging in write_session.go 2024-10-19 11:09:37 -04:00
Haris Khan
792cd49208 expanded logging in transport.go 2024-10-19 11:03:55 -04:00
Haris Khan
68051630c0 expanded logging in session.go 2024-10-19 10:55:30 -04:00
Haris Khan
a3340eb40a expanded logging in read_session.go + changed structure in readPacketLocked() 2024-10-19 10:50:13 -04:00
Haris Khan
465a7787a9 expanded logging in outgoing_handshake.go 2024-10-19 10:44:08 -04:00
Haris Khan
af3bc44dba expanded logging in incoming_handshake.go 2024-10-19 10:39:23 -04:00
Haris Khan
f850f482cf expanded logging in handshake.go 2024-10-19 10:30:11 -04:00
Haris Khan
3f23376d22 expanded logging in su3.go 2024-10-19 10:19:55 -04:00
Haris Khan
aa98589f1c expanded logging in router.go 2024-10-19 09:46:23 -04:00
Haris Khan
c31e990995 expanded logging in std.go 2024-10-18 23:06:44 -04:00
Haris Khan
8e97eb5f77 expanded logging in reseed.go 2024-10-18 22:54:08 -04:00
Haris Khan
be35267079 expanded logging in header.go 2024-10-18 22:47:28 -04:00
Haris Khan
013d35b447 expanded logging in build_request_record.go 2024-10-18 22:41:40 -04:00
Haris Khan
e8c9dc17a3 expanded logging in tunnel.go 2024-10-18 21:58:50 -04:00
Haris Khan
a1d574446b expanded logging in elg.go 2024-10-18 21:54:27 -04:00
Haris Khan
8680acc595 expanded logging in ed25519.go 2024-10-18 21:42:07 -04:00
Haris Khan
84e3c5accc expanded logging in ecdsa.go 2024-10-18 17:01:42 -04:00
Haris Khan
e772fb5ceb expanded logging in dsa.go 2024-10-18 16:56:25 -04:00
Haris Khan
a533cd7ce4 expanded logging in curve25519.go 2024-10-18 16:39:28 -04:00
Haris Khan
df37d49444 expanded logging to aes.go 2024-10-18 15:10:16 -04:00
Haris Khan
4b2600a065 expanded logging to signature.go
-changed sessionTag -> sig
2024-10-18 14:55:30 -04:00
Haris Khan
1c4f937002 expanded logging to session_tag.go 2024-10-18 14:51:53 -04:00
Haris Khan
5c2b408f65 expanded logging to session_key.go 2024-10-18 14:43:59 -04:00
Haris Khan
beb533a09b expanded logging to router_info.go 2024-10-18 14:40:52 -04:00
Haris Khan
f022522ad5 expanded logging to router_identity.go 2024-10-18 14:27:41 -04:00
Haris Khan
2191c40ac6 expanded logging to router_address.go + gofumpt adjustment 2024-10-18 13:22:36 -04:00
Haris Khan
3bca467f28 updated README.md to reflect new logging paradigm. 2024-10-18 12:59:49 -04:00
Haris Khan
a2fd65ee32 expanded logging in lease_set.go 2024-10-18 12:55:35 -04:00
Haris Khan
b894e8fb17 expanded logging in keys_and_cert.go 2024-10-18 12:41:48 -04:00
Haris Khan
93a71c7398 expanded logging in key_certificate.go 2024-10-18 12:27:42 -04:00
Haris Khan
b6544ad194 expanded logging in destination.go 2024-10-18 12:16:36 -04:00
Haris Khan
a72b61a886 expanded logging in string.go 2024-10-18 12:08:27 -04:00
Haris Khan
dda4f90b6f expanded logging in mapping_values.go 2024-10-18 12:00:58 -04:00
Haris Khan
1d1d8126c2 adjusted logging in main.go 2024-10-18 11:53:01 -04:00
Haris Khan
73db39ae50 expanded logging in mapping.go 2024-10-18 11:52:39 -04:00
Haris Khan
53e902f491 expanded logging to date.go 2024-10-18 11:49:10 -04:00
Haris Khan
2f2cd2249c expanded logging to certificate.go 2024-10-18 11:48:56 -04:00
Haris Khan
4496c11394 added lib/util/logger/log.go 2024-10-17 22:03:46 -04:00
idk
69449a20b5 Merge pull request #12 from hkh4n/crypto2
AES
2024-10-17 23:48:13 +00:00
Haris Khan
a7689e801a minor typo 2024-10-06 12:13:04 -04:00
Haris Khan
278bdee277 Various changes
-Aes -> AES
-doc.md
2024-10-06 10:48:22 -04:00
Haris Khan
684e89c957 minor typo 2024-10-05 10:15:31 -04:00
Haris Khan
50fa9fc374 gofumpt adjustment 2024-10-05 09:43:58 -04:00
Haris Khan
491b25022e AES
-Revamped encrypt and decrypt to fit with interfaces
-Adjusted test cases for TestPKCS7UnpadInvalidInput()
2024-10-05 09:00:33 -04:00
Haris Khan
677a6b354b AES test 2024-10-04 22:37:57 -04:00
Haris Khan
9469fd83aa AES 2024-10-04 22:36:32 -04:00
idk
8173ae49e6 Merge pull request #10 from hkh4n/makefile
Added gofumpt check
2024-10-04 03:26:01 +00:00
Haris Khan
2f109d5b4d Added gofumpt check 2024-10-03 23:24:12 -04:00
eyedeekay
d7378d7b08 more merge conflicts 2024-10-03 22:46:39 -04:00
eyedeekay
3a51a1229e more merge conflicts 2024-10-03 22:45:33 -04:00
eyedeekay
2b18b2941d resolve merge conflicts in lib/transport/noise 2024-10-03 22:42:00 -04:00
eyedeekay
1eb29ec4ab formatting issues only 2024-10-03 22:36:23 -04:00
eyedeekay
d900d7faf8 add doc.md files from master 2024-10-03 22:10:55 -04:00
eyedeekay
03c9d60ab9 update other non-transport parts of lib 2024-10-03 22:09:16 -04:00
eyedeekay
de2caf499e use makefile from Master 2024-10-03 21:58:16 -04:00
eyedeekay
284dd7287e use makefile from Master 2024-10-03 21:56:58 -04:00
eyedeekay
9f4154ff45 gofmt 2024-10-03 21:55:35 -04:00
eyedeekay
08a0d92742 gofmt 2024-10-03 21:52:49 -04:00
eyedeekay
524526d946 Bring in crypto stubs from master 2024-10-03 21:41:31 -04:00
eyedeekay
09c7d32797 update the common library to the master branch 2024-10-03 21:31:54 -04:00
Haris Khan
16961abc96 !WIP!
-added error handling in TestTransport()
-fixed typo to ComposeReceiverHandshakeMessage
-experimental implementation of encryptPacket
-added encrdecr_packet_test.go
2024-10-03 19:45:17 -04:00
idk
65febb5dcf Work on organizing this Noise over TCP Socket stuff 2022-12-15 23:52:05 +00:00
idk
116e22f8da Try to figure out how to implement the pieces I'm still missing from the Noise handshake, but I think that I probably need to redo it again anyway 2022-12-12 17:44:43 +00:00
idk
0419665d7b use separate incoming and outgoing queues 2022-11-14 00:10:58 -05:00
idk
dab108c270 Client=>Outgoing 2022-11-13 23:46:57 -05:00
idk
0fcded4c51 check in more noise handshake missing pieces 2022-11-13 22:24:31 -05:00
idk
7d16b0b257 update my notes 2022-10-17 02:16:44 -04:00
idk
a689f26d73 fix merge conflicts 2022-10-17 02:07:11 -04:00
idk
fdc34b382e Fix some tests so it compiles again 2022-10-16 17:19:38 -04:00
idk
a4ed06e530 update my notes 2022-09-26 12:18:33 -04:00
idk
6b5ea57cbd Move my noise transport tests to a branch so I don't mess up the other branch 2022-09-19 14:35:49 -04:00
111 changed files with 6092 additions and 1191 deletions

View File

@@ -3,6 +3,7 @@ RELEASE_VERSION=${RELEASE_TAG}
RELEASE_DESCRIPTION=`cat PASTA.md`
REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
CGO_ENABLED=0
export DEBUG_I2P=debug
ifdef GOROOT
GO = $(GOROOT)/bin/go
@@ -16,12 +17,16 @@ else
EXE := $(REPO)/go-i2p
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)
$(EXE):
$(GO) build --tags netgo,osusergo -v -o $(EXE)
test: fmt
test: check_gofumpt fmt
$(GO) test -v -failfast ./lib/common/...
clean:
@@ -42,4 +47,31 @@ 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 ./{}" \;
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

View File

@@ -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.

View File

@@ -41,7 +41,7 @@ please keep up with these changes, as they will not be backward compatible and r
- [ ] Elligator2
- [ ] HKDF
- [ ] HMAC
- [/] Noise subsystem
- [X] Noise subsystem
- End-to-End Crypto
- [ ] Garlic messages
- [ ] ElGamal/AES+SessionTag
@@ -93,6 +93,26 @@ please keep up with these changes, as they will not be backward compatible and r
- [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 DEBUG_I2P is set to an unrecognized variable, it will fall back to "debug".
## Contributing
See CONTRIBUTING.md for more information.

41
ROADMAP.md Normal file
View File

@@ -0,0 +1,41 @@
# go-i2p Implementation Roadmap
## Transport Layer (NTCP2)
- Build on existing lib/transport/noise implementation
- Core NTCP2 components:
* Session handshake using noise protocol
* Connection management
* I2NP message transport
## Reseed System
- SU3 file format implementation:
* Format parsing and validation(Much of this work is done in reseed-tools, may need to be moved here)
* Signature verification system(Much of this work is done in reseed-tools, may need to be moved here)
- Local reseed functionality:
* File-based reseed operations
- Self-signed/Package-pinned X.509 certificate handling for reseed validation
## NetDb and Database Store
- Database Store message handling:
* Message structure implementation
* Message handling implementation
- NetDb core implementation:
* RouterInfo management
* LeaseSet management
* Lookup system
* Storage interface
* Peer selection logic?(Maybe do something very basic for now like i2pd used to do, and then improve it later, the important part will be interface design at first)
## Tunnel Implementation
- Tunnel cryptography:
* Key generation and management
* Layered encryption scheme
- Message processing:
* Build request/response handling
* Gateway implementation
* Message forwarding logic
Notes:
- Excluding legacy protocols (SSU1, NTCP1, elgamal, DSA)
- Leveraging existing noise protocol implementation
- SSU2 is not on this roadmap but is fair game for implementation as soon as NTCP2 is done. We're focused on NTCP2 to get this thing sending I2NP messages.

17
doc/tests/aes.mk Normal file
View File

@@ -0,0 +1,17 @@
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

4
doc/tests/base32.mk Normal file
View File

@@ -0,0 +1,4 @@
test-base32-encode-decode-not-mangled:
$(GO) test -v ./lib/common/base32 -run TestEncodeDecodeNotMangled
.PHONY: test-base32-encode-decode-not-mangled

4
doc/tests/base64.mk Normal file
View File

@@ -0,0 +1,4 @@
test-base64-encode-decode-not-mangled:
$(GO) test -v ./lib/common/base64 -run TestEncodeDecodeNotMangled
.PHONY: test-base64-encode-decode-not-mangled

View File

@@ -0,0 +1,24 @@
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

61
doc/tests/certificate.mk Normal file
View File

@@ -0,0 +1,61 @@
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

2
doc/tests/date.mk Normal file
View File

@@ -0,0 +1,2 @@
test-date-time-from-milliseconds:
$(GO) test -v ./lib/common/data -run TestTimeFromMilliseconds

20
doc/tests/dsa.mk Normal file
View File

@@ -0,0 +1,20 @@
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

7
doc/tests/ed25519.mk Normal file
View File

@@ -0,0 +1,7 @@
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

24
doc/tests/elg.mk Normal file
View File

@@ -0,0 +1,24 @@
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

30
doc/tests/header.mk Normal file
View File

@@ -0,0 +1,30 @@
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

7
doc/tests/hmac.mk Normal file
View File

@@ -0,0 +1,7 @@
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

15
doc/tests/integer.mk Normal file
View File

@@ -0,0 +1,15 @@
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

View File

@@ -0,0 +1,23 @@
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

View File

@@ -0,0 +1,30 @@
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

22
doc/tests/lease_set.mk Normal file
View File

@@ -0,0 +1,22 @@
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

28
doc/tests/mapping.mk Normal file
View File

@@ -0,0 +1,28 @@
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

View File

@@ -0,0 +1,2 @@
test-mapping-values-order:
$(GO) test -v ./lib/common/data -run TestMappingOrderSortsValuesThenKeys

19
doc/tests/noise.mk Normal file
View File

@@ -0,0 +1,19 @@
test-noise-transport-all: test-noise-packet-encryption test-noise-transport-connection test-noise-packet-obfuscation test-noise-packet-obfuscation-func
test-noise-packet-encryption:
$(GO) test -v ./lib/transport/noise -run TestEncryptDecryptPacketOffline
test-noise-transport-connection:
$(GO) test -v ./lib/transport/noise -run TestTransport
test-noise-packet-obfuscation:
$(GO) test -v ./lib/transport/noise -run TestEncryptDecryptPacketObfsOffline
test-noise-packet-obfuscation-func:
$(GO) test -v ./lib/transport/noise -run TestEncryptDecryptPacketObfsOfflineWithFunc
.PHONY: test-noise-transport-all \
test-noise-packet-encryption \
test-noise-transport-connection \
test-noise-packet-obfuscation \
test-noise-packet-obfuscation-func

View File

@@ -0,0 +1,19 @@
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

26
doc/tests/router_info.mk Normal file
View File

@@ -0,0 +1,26 @@
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

27
doc/tests/string.mk Normal file
View File

@@ -0,0 +1,27 @@
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

11
doc/tests/su3.mk Normal file
View File

@@ -0,0 +1,11 @@
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

22
doc/tests/tunnel.mk Normal file
View File

@@ -0,0 +1,22 @@
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

39
go.mod
View File

@@ -1,24 +1,43 @@
module github.com/go-i2p/go-i2p
go 1.22
toolchain go1.22.5
go 1.23.1
require (
github.com/beevik/ntp v1.4.3
github.com/emirpasic/gods v1.18.1
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e
github.com/flynn/noise v1.1.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
go.step.sm/crypto v0.51.2
golang.org/x/crypto v0.26.0
go.step.sm/crypto v0.53.0
golang.org/x/crypto v0.27.0
gopkg.in/yaml.v3 v3.0.1
)
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/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

85
go.sum
View File

@@ -2,44 +2,105 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.step.sm/crypto v0.51.2 h1:5EiCGIMg7IvQTGmJrwRosbXeprtT80OhoS/PJarg60o=
go.step.sm/crypto v0.51.2/go.mod h1:QK7czLjN2k+uqVp5CHXxJbhc70kVRSP+0CQF3zsR5M0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.step.sm/crypto v0.53.0 h1:+1as1ogzuCzx15/468M4mEC5juogI5a0Fzbsyh1CuYY=
go.step.sm/crypto v0.53.0/go.mod h1:AqLU78RqNUHepLzyOWZuNN/2++Lu7dZENdO9UzWOGSk=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
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/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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=

View File

@@ -3,13 +3,20 @@
package certificate
import (
"encoding/binary"
"errors"
"fmt"
log "github.com/sirupsen/logrus"
"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
@@ -70,14 +77,22 @@ 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() {
return 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
}
@@ -86,6 +101,9 @@ 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
}
@@ -97,12 +115,18 @@ func (c *Certificate) length() (cert_len int) {
// 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
}
@@ -111,21 +135,25 @@ 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
// readCertificate 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) {
func readCertificate(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(log.Fields{
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()),
@@ -135,7 +163,7 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
case 1, 2:
certificate.kind = Integer(data[0 : len(data)-1])
certificate.len = Integer([]byte{0})
log.WithFields(log.Fields{
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()),
@@ -145,14 +173,14 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
default:
certificate.kind = Integer(data[0:1])
certificate.len = Integer(data[1:3])
payleng := len(data) - CERT_MIN_SIZE
payloadLength := 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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": certificate.len.Int(),
"certificate_payload_length": payleng,
"certificate_payload_length": payloadLength,
"data_bytes:": string(data),
"kind_bytes": data[0:1],
"len_bytes": data[1:3],
@@ -160,6 +188,10 @@ func NewCertificate(data []byte) (certificate Certificate, 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
}
}
@@ -167,10 +199,64 @@ func NewCertificate(data []byte) (certificate Certificate, 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 ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
certificate, err = NewCertificate(data)
certificate, err = readCertificate(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
}
// NewCertificate creates a new Certificate with default NULL type
func NewCertificate() *Certificate {
return &Certificate{
kind: Integer([]byte{CERT_NULL}),
len: Integer([]byte{0}),
payload: make([]byte, 0),
}
}
// NewCertificateWithType creates a new Certificate with specified type and payload
func NewCertificateWithType(certType uint8, payload []byte) (*Certificate, error) {
// Validate certificate type
switch certType {
case CERT_NULL, CERT_HASHCASH, CERT_HIDDEN, CERT_SIGNED, CERT_MULTIPLE, CERT_KEY:
// Valid type
default:
return nil, fmt.Errorf("invalid certificate type: %d", certType)
}
// For NULL certificates, payload should be empty
if certType == CERT_NULL && len(payload) > 0 {
return nil, errors.New("NULL certificates must have empty payload")
}
length, _ := NewIntegerFromInt(len(payload), 2)
cert := &Certificate{
kind: Integer([]byte{certType}),
len: *length,
payload: make([]byte, len(payload)),
}
// Copy payload if present
if len(payload) > 0 {
copy(cert.payload, payload)
}
return cert, nil
}
func GetSignatureTypeFromCertificate(cert Certificate) (int, error) {
if cert.Type() != CERT_KEY {
return 0, fmt.Errorf("unexpected certificate type: %d", cert.Type)
}
if len(cert.payload) < 2 {
return 0, fmt.Errorf("certificate payload too short to contain signature type")
}
sigType := int(binary.BigEndian.Uint16(cert.payload[0:2]))
return sigType, nil
}

View File

@@ -10,7 +10,7 @@ func TestCertificateTypeIsFirstByte(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x00}
certificate, err := NewCertificate(bytes)
certificate, err := readCertificate(bytes)
cert_type := certificate.Type()
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
@@ -21,7 +21,7 @@ func TestCertificateLengthCorrect(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
certificate, err := NewCertificate(bytes)
certificate, err := readCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
@@ -45,7 +45,7 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate, err := NewCertificate(bytes)
certificate, err := readCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
@@ -58,7 +58,7 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
assert := assert.New(t)
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")
@@ -85,7 +85,7 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate, err := NewCertificate(bytes)
certificate, err := readCertificate(bytes)
cert_data := certificate.Data()
if assert.NotNil(err) {

View File

@@ -5,9 +5,12 @@ import (
"errors"
"time"
log "github.com/sirupsen/logrus"
"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
@@ -51,7 +54,7 @@ func (date Date) Time() (date_time time.Time) {
// 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(log.Fields{
log.WithFields(logrus.Fields{
"data": data,
}).Error("ReadDate: data is too short")
err = errors.New("ReadDate: data is too short")
@@ -59,6 +62,10 @@ func ReadDate(data []byte) (date Date, remainder []byte, err error) {
}
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
}
@@ -66,6 +73,15 @@ func ReadDate(data []byte) (date Date, remainder []byte, err error) {
// 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
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestTimeFromMiliseconds(t *testing.T) {
func TestTimeFromMilliseconds(t *testing.T) {
assert := assert.New(t)
next_day := Date{0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00}

View File

@@ -3,7 +3,7 @@ package data
import (
"errors"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
/*
@@ -48,8 +48,12 @@ type Mapping struct {
// 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
}
@@ -74,30 +78,40 @@ func (mapping *Mapping) Data() []byte {
// 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
}
@@ -107,27 +121,46 @@ func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error) {
)
}
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 {
return err.Error() == "error parsing string: zero length"
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
/*
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(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
@@ -137,16 +170,18 @@ func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
}
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(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
@@ -161,20 +196,38 @@ func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
err = append(err, mappingValueErrs...)
mapping.vals = vals
if len(mappingValueErrs) > 0 {
log.WithFields(log.Fields{
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
}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"sort"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
// MappingValues represents the parsed key value pairs inside of an I2P Mapping.
@@ -12,12 +12,22 @@ 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 {
return pair[1][1:]
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
}
@@ -27,6 +37,9 @@ 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 {
@@ -34,6 +47,10 @@ func ValuesToMapping(values MappingValues) *Mapping {
}
}
log.WithFields(logrus.Fields{
"mapping_size": baseLength,
}).Debug("Created Mapping from MappingValues")
mappingSize, _ := NewIntegerFromInt(baseLength, 2)
return &Mapping{
size: mappingSize,
@@ -61,8 +78,13 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// 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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "data shorter than expected",
}).Error("mapping contained no data")
@@ -73,7 +95,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
int_map_length := map_length.Int()
mapping_len := len(remainder)
if mapping_len > int_map_length {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
@@ -81,7 +103,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
}).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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
@@ -105,7 +127,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// 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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "mapping format violation",
}).Warn("mapping format violation, too few bytes for a kv pair")
@@ -128,7 +150,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
keyAsString := string(keyBytes)
_, ok := encounteredKeysMap[keyAsString]
if ok {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "duplicate key in mapping",
"key": string(key_str),
@@ -142,7 +164,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
}
if !beginsWith(remainder, 0x3d) {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected =",
"value:": string(remainder),
@@ -168,7 +190,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// 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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected ;",
"value:": string(remainder),
@@ -189,5 +211,12 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
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
}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
// STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string
@@ -32,7 +32,7 @@ type I2PString []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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"reason": "no data",
}).Error("error parsing string")
@@ -41,6 +41,7 @@ func (str I2PString) Length() (length int, err error) {
}
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()
@@ -53,6 +54,12 @@ func (str I2PString) Length() (length int, err error) {
"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
@@ -65,31 +72,42 @@ func (str I2PString) Data() (data string, err error) {
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(log.Fields{
log.WithFields(logrus.Fields{
"at": "ToI2PI2PString",
"string_len": data_len,
"max_len": STRING_MAX_SIZE,
@@ -101,6 +119,9 @@ func ToI2PString(data string) (str I2PString, err error) {
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
}
@@ -113,18 +134,40 @@ func ToI2PString(data string) (str I2PString, err error) {
// 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) {
if len(data) == 0 {
err = errors.New("data slice is empty")
log.WithError(err).Error("Passed data with len == 0")
return
}
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
if data_len > len(data) {
err = fmt.Errorf("I2PString length %d exceeds available data %d", data_len-1, len(data)-1)
log.WithError(err).Error("Failed to read I2PString")
return
}
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
}

View File

@@ -4,6 +4,9 @@ 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"
@@ -11,6 +14,8 @@ import (
"github.com/go-i2p/go-i2p/lib/crypto"
)
var log = logger.GetGoI2PLogger()
/*
[Destination]
Accurate for version 0.9.49
@@ -31,26 +36,52 @@ type Destination struct {
// Base32Address returns the I2P base32 address for this Destination.
func (destination Destination) Base32Address() (str string) {
dest := destination.KeysAndCert.KeyCertificate.Bytes()
log.Debug("Generating Base32 address for Destination")
cert := destination.KeysAndCert.Certificate()
dest := cert.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 {
dest := destination.KeysAndCert.KeyCertificate.Bytes()
return base64.EncodeToString(dest)
log.Debug("Generating Base64 address for Destination")
cert := destination.KeysAndCert.Certificate()
dest := cert.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
}

View File

@@ -5,7 +5,7 @@ import common "github.com/go-i2p/go-i2p/lib/common/router_identity"
func Fuzz(data []byte) int {
router_identity, _, _ := common.ReadRouterIdentity(data)
router_identity.Certificate()
router_identity.PublicKey()
router_identity.SigningPublicKey()
//router_identity.publicKey()
//router_identity.signingPublicKey()
return 0
}

View File

@@ -30,12 +30,16 @@ payload :: data
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"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Key Certificate Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1 = iota
@@ -62,7 +66,7 @@ const (
KEYCERT_MIN_SIZE = 7
)
// SigningPublicKey sizes for Signing Key Types
// signingPublicKey sizes for Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
KEYCERT_SIGN_P256_SIZE = 64
@@ -75,7 +79,7 @@ const (
KEYCERT_SIGN_ED25519PH_SIZE = 32
)
// PublicKey sizes for Public Key Types
// publicKey sizes for Public Key Types
const (
KEYCERT_CRYPTO_ELG_SIZE = 256
KEYCERT_CRYPTO_P256_SIZE = 64
@@ -99,30 +103,45 @@ type KeyCertificate struct {
// 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.
// 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.
// 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.
// 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(log.Fields{
"at": "(KeyCertificate) ConstructPublicKey",
log.WithFields(logrus.Fields{
"at": "(keyCertificate) ConstructPublicKey",
"data_len": data_len,
"required_len": KEYCERT_PUBKEY_SIZE,
"reason": "not enough data",
@@ -135,25 +154,35 @@ func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_ke
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) {
signing_key_type := key_certificate.PublicKeyType()
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Constructing signingPublicKey from keyCertificate")
signing_key_type := key_certificate.SigningPublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < key_certificate.SignatureSize() {
log.WithFields(log.Fields{
"at": "(KeyCertificate) ConstructSigningPublicKey",
log.WithFields(logrus.Fields{
"at": "(keyCertificate) ConstructSigningPublicKey",
"data_len": data_len,
"required_len": KEYCERT_SPK_SIZE,
"reason": "not enough data",
@@ -166,35 +195,62 @@ func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (si
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
var ec_p256_key crypto.ECP256PublicKey
copy(ec_p256_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P256_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_p256_key
log.Debug("Constructed P256PublicKey")
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
var ec_p384_key crypto.ECP384PublicKey
copy(ec_p384_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P384_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_p384_key
log.Debug("Constructed P384PublicKey")
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
/*var ec_p521_key crypto.ECP521PublicKey
copy(ec_p521_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P521_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_p521_key
log.Debug("Constructed P521PublicKey")*/
panic("unimplemented P521SigningPublicKey")
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
/*var rsa2048_key crypto.RSA2048PublicKey
copy(rsa2048_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_RSA2048_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = rsa2048_key
log.Debug("Constructed RSA2048PublicKey")*/
panic("unimplemented RSA2048SigningPublicKey")
case KEYCERT_SIGN_RSA3072:
/*var rsa3072_key crypto.RSA3072PublicKey
copy(rsa3072_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_RSA3072_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = rsa3072_key
log.Debug("Constructed RSA3072PublicKey")*/
panic("unimplemented RSA3072SigningPublicKey")
case KEYCERT_SIGN_RSA4096:
/*var rsa4096_key crypto.RSA4096PublicKey
copy(rsa4096_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_RSA4096_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = rsa4096_key
log.Debug("Constructed RSA4096PublicKey")*/
panic("unimplemented RSA4096SigningPublicKey")
case KEYCERT_SIGN_ED25519:
var ed25519_key crypto.Ed25519PublicKey
copy(ed25519_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_ED25519_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ed25519_key
log.Debug("Constructed Ed25519PublicKey")
case KEYCERT_SIGN_ED25519PH:
var ed25519ph_key crypto.Ed25519PublicKey
copy(ed25519ph_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_ED25519PH_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ed25519ph_key
log.Debug("Constructed Ed25519PHPublicKey")
default:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Unknown signing key type")
panic(err)
}
return
}
// SignatureSize return the size of a Signature corresponding to the Key Certificate's SigningPublicKey type.
// 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,
@@ -208,10 +264,15 @@ func (key_certificate KeyCertificate) SignatureSize() (size int) {
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.
// 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,
@@ -221,6 +282,11 @@ func (key_certificate KeyCertificate) CryptoSize() (size int) {
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)]
}
@@ -228,12 +294,18 @@ func (key_certificate KeyCertificate) CryptoSize() (size int) {
// 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 {
log.WithError(err).Error("keyCertificate data too short")
err = errors.New("error parsing key certificate: not enough data")
remainder = bytes[KEYCERT_MIN_SIZE:]
}
@@ -246,11 +318,25 @@ func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder
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 {
k, _, _ := NewKeyCertificate(certificate.RawBytes())
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
}

View File

@@ -35,7 +35,7 @@ func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
pk_type := key_cert.PublicKeyType()
assert.Nil(err, "PublicKey() returned error with valid data")
assert.Nil(err, "publicKey() returned error with valid data")
assert.Equal(pk_type, KEYCERT_SIGN_P521, "PublicKeyType() did not return correct typec")
}
@@ -94,7 +94,7 @@ func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
spk, err := key_cert.ConstructSigningPublicKey(data)
assert.Nil(err, "ConstructSigningPublicKey() with DSA SHA1 returned error with valid data")
assert.Equal(spk.Len(), KEYCERT_SIGN_DSA_SHA1_SIZE, "ConstructSigningPublicKey() with DSA SHA1 returned incorrect SigningPublicKey length")
assert.Equal(spk.Len(), KEYCERT_SIGN_DSA_SHA1_SIZE, "ConstructSigningPublicKey() with DSA SHA1 returned incorrect signingPublicKey length")
}
func TestConstructSigningPublicKeyWithP256(t *testing.T) {
@@ -105,7 +105,7 @@ func TestConstructSigningPublicKeyWithP256(t *testing.T) {
spk, err := key_cert.ConstructSigningPublicKey(data)
assert.Nil(err, "ConstructSigningPublicKey() with P256 returned err on valid data")
assert.Equal(spk.Len(), KEYCERT_SIGN_P256_SIZE, "ConstructSigningPublicKey() with P256 returned incorrect SigningPublicKey length")
assert.Equal(spk.Len(), KEYCERT_SIGN_P256_SIZE, "ConstructSigningPublicKey() with P256 returned incorrect signingPublicKey length")
}
func TestConstructSigningPublicKeyWithP384(t *testing.T) {
@@ -116,7 +116,7 @@ func TestConstructSigningPublicKeyWithP384(t *testing.T) {
spk, err := key_cert.ConstructSigningPublicKey(data)
assert.Nil(err, "ConstructSigningPublicKey() with P384 returned err on valid data")
assert.Equal(spk.Len(), KEYCERT_SIGN_P384_SIZE, "ConstructSigningPublicKey() with P384 returned incorrect SigningPublicKey length")
assert.Equal(spk.Len(), KEYCERT_SIGN_P384_SIZE, "ConstructSigningPublicKey() with P384 returned incorrect signingPublicKey length")
}
func TestConstructSigningPublicKeyWithP521(t *testing.T) {
@@ -127,5 +127,5 @@ func TestConstructSigningPublicKeyWithP521(t *testing.T) {
spk, err := key_cert.ConstructSigningPublicKey(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")
}

View File

@@ -2,14 +2,19 @@
package keys_and_cert
import (
"crypto/rand"
"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"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Sizes of various KeysAndCert structures and requirements
const (
KEYS_AND_CERT_PUBKEY_SIZE = 256
@@ -26,7 +31,7 @@ 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.
A publicKey followed by a signingPublicKey and then a Certificate.
+----+----+----+----+----+----+----+----+
| public_key |
@@ -51,14 +56,14 @@ A PublicKey followed by a SigningPublicKey and then a Certificate.
| certificate |
+----+----+----+-//
public_key :: PublicKey (partial or full)
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)
signing__key :: signingPublicKey (partial or full)
length -> 128 bytes or as specified in key certificate
padding length + signing_key length == 128 bytes
@@ -72,49 +77,64 @@ total length: 387+ bytes
//
// https://geti2p.net/spec/common-structures#keysandcert
type KeysAndCert struct {
KeyCertificate *KeyCertificate
keyCertificate *KeyCertificate
publicKey crypto.PublicKey
padding []byte
Padding []byte
signingPublicKey crypto.SigningPublicKey
}
// Bytes returns the entire KeyCertificate in []byte form, trims payload to specified length.
// Bytes returns the entire keyCertificate in []byte form, trims payload to specified length.
func (keys_and_cert KeysAndCert) Bytes() []byte {
return keys_and_cert.KeyCertificate.Bytes()
bytes := keys_and_cert.publicKey.Bytes()
bytes = append(bytes, keys_and_cert.Padding...)
bytes = append(bytes, keys_and_cert.signingPublicKey.Bytes()...)
bytes = append(bytes, keys_and_cert.keyCertificate.Bytes()...)
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
"pk_bytes_length": len(keys_and_cert.publicKey.Bytes()),
"padding_bytes_length": len(keys_and_cert.Padding),
"spk_bytes_length": len(keys_and_cert.signingPublicKey.Bytes()),
"cert_bytes_length": len(keys_and_cert.keyCertificate.Bytes()),
}).Debug("Retrieved bytes from KeysAndCert")
return bytes
}
// PublicKey returns the public key as a crypto.PublicKey.
// 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.
// 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
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(log.Fields{
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:])
keys_and_cert.keyCertificate, remainder, _ = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
return
} else if data_len < KEYS_AND_CERT_DATA_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
@@ -123,22 +143,110 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
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:])
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()])
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])
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
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
}
// NewKeysAndCert creates a new KeysAndCert instance with the provided parameters.
// It validates the sizes of the provided keys and padding before assembling the struct.
func NewKeysAndCert(
keyCertificate *KeyCertificate,
publicKey crypto.PublicKey,
padding []byte,
signingPublicKey crypto.SigningPublicKey,
) (*KeysAndCert, error) {
log.Debug("Creating new KeysAndCert with provided parameters")
// 1. Validate keyCertificate
if keyCertificate == nil {
log.Error("KeyCertificate is nil")
return nil, errors.New("KeyCertificate cannot be nil")
}
// 2. Validate publicKey size
if publicKey.Len() != KEYS_AND_CERT_PUBKEY_SIZE {
log.WithFields(logrus.Fields{
"expected_size": KEYS_AND_CERT_PUBKEY_SIZE,
"actual_size": publicKey.Len(),
}).Error("Invalid publicKey size")
return nil, errors.New("publicKey has an invalid size")
}
/*
// 3. Validate signingPublicKey size
if signingPublicKey.Len() != KEYS_AND_CERT_SPK_SIZE {
log.WithFields(logrus.Fields{
"expected_size": KEYS_AND_CERT_SPK_SIZE,
"actual_size": signingPublicKey.Len(),
}).Error("Invalid signingPublicKey size")
return nil, errors.New("signingPublicKey has an invalid size")
}
*/
// 4. Validate padding size
publicKeyLength := publicKey.Len()
signingPublicKeyLength := signingPublicKey.Len()
totalKeysSize := publicKeyLength + signingPublicKeyLength
expectedPaddingSize := KEYS_AND_CERT_DATA_SIZE - totalKeysSize
if len(padding) != expectedPaddingSize {
log.WithFields(logrus.Fields{
"expected_size": expectedPaddingSize,
"actual_size": len(padding),
}).Warn("Invalid padding size")
// generate some random padding and continue
padding = make([]byte, expectedPaddingSize)
_, err := rand.Read(padding)
if err != nil {
log.WithError(err).Error("Failed to generate random padding")
return nil, err
}
log.WithFields(logrus.Fields{
"expected_size": expectedPaddingSize,
"actual_size": len(padding),
}).Warn("Generated random padding")
}
// 5. Assemble KeysAndCert
keysAndCert := &KeysAndCert{
keyCertificate: keyCertificate,
publicKey: publicKey,
Padding: padding,
signingPublicKey: signingPublicKey,
}
log.WithFields(logrus.Fields{
"public_key_length": publicKey.Len(),
"signing_public_key_length": signingPublicKey.Len(),
"padding_length": len(padding),
}).Debug("Successfully created KeysAndCert")
return keysAndCert, nil
}

View File

@@ -6,13 +6,13 @@ import "crypto"
// Public Key and the Signing Public Key
type PrivateKeysAndCert struct {
KeysAndCert
PK_KEY crypto.PrivateKey
PK_KEY crypto.PrivateKey
SPK_KEY crypto.PrivateKey
}
func NewPrivateKeysAndCert() (*PrivateKeysAndCert, error) {
var pkc PrivateKeysAndCert
var err error
//pkc.PK_KEY, err =
// pkc.PK_KEY, err =
return &pkc, err
}
}

View File

@@ -4,6 +4,9 @@ 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"
@@ -12,9 +15,10 @@ import (
. "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"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Sizes of various structures in an I2P LeaseSet
const (
LEASE_SET_PUBKEY_SIZE = 256
@@ -28,13 +32,13 @@ 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
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
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.
@@ -96,10 +100,10 @@ finally a Signature of the previous bytes signed by the Destination's SigningPri
destination :: Destination
length -> >= 387 bytes
encryption_key :: PublicKey
encryption_key :: publicKey
length -> 256 bytes
signing_key :: SigningPublicKey
signing_key :: signingPublicKey
length -> 128 bytes or as specified in destination's key certificate
num :: Integer
@@ -134,9 +138,15 @@ type LeaseSet struct {
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
}
@@ -146,8 +156,8 @@ 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(log.Fields{
"at": "(LeaseSet) PublicKey",
log.WithFields(logrus.Fields{
"at": "(LeaseSet) publicKey",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE,
"reason": "not enough data",
@@ -157,25 +167,29 @@ func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error
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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) SigningKey",
"data_len": lease_set_len,
"required_len": offset + LEASE_SET_SPK_SIZE,
@@ -186,10 +200,11 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
}
if cert_len == 0 {
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
// 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()
@@ -200,14 +215,19 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
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.
// 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
}
@@ -215,13 +235,15 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
// 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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) LeaseCount",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
@@ -233,12 +255,14 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
c := Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
count = c.Int()
if count > 16 {
log.WithFields(log.Fields{
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
}
@@ -246,13 +270,16 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
// 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++ {
@@ -260,7 +287,7 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
end := start + LEASE_SIZE
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Leases",
"data_len": lease_set_len,
"required_len": end,
@@ -273,18 +300,22 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
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()) +
@@ -302,7 +333,7 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
}
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Signature",
"data_len": lease_set_len,
"required_len": end,
@@ -312,11 +343,13 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
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 +
@@ -325,19 +358,22 @@ func (lease_set LeaseSet) Verify() error {
//data := lease_set[:data_end]
//spk, _ := lease_set.
// Destination().
// SigningPublicKey()
// 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}
@@ -347,14 +383,17 @@ func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
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}
@@ -364,5 +403,6 @@ func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
earliest = date
}
}
log.WithField("oldest_expiration", earliest.Time()).Debug("Found oldest expiration in LeaseSet")
return
}

View File

@@ -61,7 +61,7 @@ func buildSignature(size int) []byte {
func buildFullLeaseSet(n int) LeaseSet {
lease_set_data := make([]byte, 0)
lease_set_data = append(lease_set_data, buildDestination().KeysAndCert.KeyCertificate.RawBytes()...)
lease_set_data = append(lease_set_data, buildDestination().KeysAndCert.Bytes()...)
lease_set_data = append(lease_set_data, buildPublicKey()...)
lease_set_data = append(lease_set_data, buildSigningKey()...)
lease_set_data = append(lease_set_data, byte(n))

View File

@@ -2,14 +2,18 @@
package router_address
import (
"encoding/binary"
"errors"
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/data"
log "github.com/sirupsen/logrus"
)
// Minimum number of bytes in a valid RouterAddress
@@ -17,6 +21,8 @@ const (
ROUTER_ADDRESS_MIN_SIZE = 9
)
var log = logger.GetGoI2PLogger()
/*
[RouterAddress]
Accurate for version 0.9.49
@@ -75,34 +81,48 @@ type RouterAddress struct {
// Network implements net.Addr. It returns the transport type plus 4 or 6
func (router_address *RouterAddress) Network() string {
log.Debug("Getting network for RouterAddress")
if router_address.TransportType == nil {
log.Warn("TransportType is nil in RouterAddress")
return ""
}
str, err := router_address.TransportType.Data()
if err != nil {
log.WithError(err).Error("Failed to get TransportType data")
return ""
}
return string(str) + router_address.IPVersion()
network := string(str) + router_address.IPVersion()
log.WithField("network", network).Debug("Retrieved network for RouterAddress")
return network
}
// IPVersion returns a string "4" for IPv4 or 6 for IPv6
func (router_address *RouterAddress) IPVersion() string {
log.Debug("Getting IP version for RouterAddress")
str, err := router_address.CapsString().Data()
if err != nil {
log.WithError(err).Error("Failed to get CapsString data")
return ""
}
if strings.HasSuffix(str, "6") {
log.Debug("IP version is IPv6")
return "6"
}
log.Debug("IP version is IPv4")
return "4"
}
func (router_address *RouterAddress) UDP() bool {
return strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
// return strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
log.Debug("Checking if RouterAddress is UDP")
isUDP := strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
log.WithField("is_udp", isUDP).Debug("Checked if RouterAddress is UDP")
return isUDP
}
// String implements net.Addr. It returns the IP address, followed by the options
func (router_address *RouterAddress) String() string {
log.Debug("Converting RouterAddress to string")
var rv []string
rv = append(rv, string(router_address.TransportStyle()))
rv = append(rv, string(router_address.HostString()))
@@ -121,25 +141,22 @@ func (router_address *RouterAddress) String() string {
rv = append(rv, string(router_address.IntroducerExpirationString(2)))
rv = append(rv, string(router_address.IntroducerTagString(2)))
}
return strings.TrimSpace(strings.Join(rv, " "))
str := strings.TrimSpace(strings.Join(rv, " "))
log.WithField("router_address_string", str).Debug("Converted RouterAddress to string")
return str
}
var ex_addr net.Addr = &RouterAddress{}
// Bytes returns the router address as a []byte.
func (router_address RouterAddress) Bytes() []byte {
log.Debug("Converting RouterAddress to bytes")
bytes := make([]byte, 0)
bytes = append(bytes, router_address.TransportCost.Bytes()...)
bytes = append(bytes, router_address.ExpirationDate.Bytes()...)
strData, err := router_address.TransportType.Data()
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("RouterAddress.Bytes: error getting transport_style bytes")
} else {
bytes = append(bytes, strData...)
}
bytes = append(bytes, router_address.TransportType...)
bytes = append(bytes, router_address.TransportOptions.Data()...)
log.WithField("bytes_length", len(bytes)).Debug("Converted RouterAddress to bytes")
return bytes
}
@@ -224,29 +241,45 @@ func (router_address RouterAddress) IntroducerTagString(num int) I2PString {
}
func (router_address RouterAddress) Host() (net.Addr, error) {
log.Debug("Getting host from RouterAddress")
host := router_address.HostString()
hostBytes, err := host.Data()
if err != nil {
log.WithError(err).Error("Failed to get host data")
return nil, err
}
ip := net.ParseIP(hostBytes)
if ip == nil {
log.Error("Failed to parse IP address")
return nil, fmt.Errorf("null host error")
}
return net.ResolveIPAddr("", ip.String())
// return net.ResolveIPAddr("", ip.String())
addr, err := net.ResolveIPAddr("", ip.String())
if err != nil {
log.WithError(err).Error("Failed to resolve IP address")
} else {
log.WithField("addr", addr).Debug("Retrieved host from RouterAddress")
}
return addr, err
}
func (router_address RouterAddress) Port() (string, error) {
log.Debug("Getting port from RouterAddress")
port := router_address.PortString()
portBytes, err := port.Data()
if err != nil {
log.WithError(err).Error("Failed to get port data")
return "", err
}
val, err := strconv.Atoi(portBytes)
if err != nil {
log.WithError(err).Error("Failed to convert port to integer")
return "", err
}
return strconv.Itoa(val), nil
// return strconv.Itoa(val), nil
portStr := strconv.Itoa(val)
log.WithField("port", portStr).Debug("Retrieved port from RouterAddress")
return portStr, nil
}
func (router_address RouterAddress) StaticKey() ([32]byte, error) {
@@ -283,6 +316,7 @@ func (router_address RouterAddress) checkValid() (err error, exit bool) {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []byte, err error) {
log.WithField("data_length", len(data)).Debug("Reading RouterAddress from data")
if len(data) == 0 {
log.WithField("at", "(RouterAddress) ReadRouterAddress").Error("error parsing RouterAddress: no data")
err = errors.New("error parsing RouterAddress: no data")
@@ -290,21 +324,21 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
}
router_address.TransportCost, remainder, err = NewInteger(data, 1)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing cost",
}).Warn("error parsing RouterAddress")
}
router_address.ExpirationDate, remainder, err = NewDate(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing expiration",
}).Error("error parsing RouterAddress")
}
router_address.TransportType, remainder, err = ReadI2PString(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing transport_style",
}).Error("error parsing RouterAddress")
@@ -312,7 +346,7 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
var errs []error
router_address.TransportOptions, remainder, errs = NewMapping(remainder)
for _, err := range errs {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing options",
"error": err,
@@ -320,3 +354,57 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
}
return
}
// NewRouterAddress creates a new RouterAddress with the provided parameters.
// Returns a pointer to RouterAddress.
func NewRouterAddress(cost uint8, expiration time.Time, transportType string, options map[string]string) (*RouterAddress, error) {
log.Debug("Creating new RouterAddress")
// Create TransportCost as an Integer (1 byte)
transportCost, err := NewIntegerFromInt(int(cost), 1)
if err != nil {
log.WithError(err).Error("Failed to create TransportCost Integer")
return nil, err
}
// Create ExpirationDate as a Date
millis := expiration.UnixNano() / int64(time.Millisecond)
dateBytes := make([]byte, DATE_SIZE)
binary.BigEndian.PutUint64(dateBytes, uint64(millis))
expirationDate, _, err := NewDate(dateBytes)
if err != nil {
log.WithError(err).Error("Failed to create ExpirationDate")
return nil, err
}
// Create TransportType as an I2PString
transportTypeStr, err := ToI2PString(transportType)
if err != nil {
log.WithError(err).Error("Failed to create TransportType I2PString")
return nil, err
}
// Create TransportOptions as a Mapping
transportOptions, err := GoMapToMapping(options)
if err != nil {
log.WithError(err).Error("Failed to create TransportOptions Mapping")
return nil, err
}
// Create RouterAddress
ra := &RouterAddress{
TransportCost: transportCost,
ExpirationDate: expirationDate,
TransportType: transportTypeStr,
TransportOptions: transportOptions,
}
log.WithFields(logrus.Fields{
"cost": cost,
"expiration": expiration,
"transportType": transportType,
"options": options,
}).Debug("Successfully created new RouterAddress")
return ra, nil
}

View File

@@ -2,9 +2,16 @@
package router_identity
import (
"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/common/keys_and_cert"
"github.com/go-i2p/go-i2p/lib/crypto"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
/*
[RouterIdentity]
Accurate for version 0.9.49
@@ -27,9 +34,46 @@ type RouterIdentity struct {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Reading RouterIdentity from data")
keys_and_cert, remainder, err := ReadKeysAndCert(data)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert for RouterIdentity")
return
}
router_identity = RouterIdentity{
keys_and_cert,
}
log.WithFields(logrus.Fields{
"remainder_length": len(remainder),
}).Debug("Successfully read RouterIdentity")
return
}
func NewRouterIdentity(publicKey crypto.PublicKey, signingPublicKey crypto.SigningPublicKey, cert certificate.Certificate, padding []byte) (*RouterIdentity, error) {
log.Debug("Creating new RouterIdentity")
// Step 1: Create keyCertificate from the provided certificate.
// Assuming NewKeyCertificate is a constructor that takes a Certificate and returns a keyCertificate.
keyCert := key_certificate.KeyCertificateFromCertificate(cert)
// Step 2: Create KeysAndCert instance.
keysAndCert, err := NewKeysAndCert(keyCert, publicKey, padding, signingPublicKey)
if err != nil {
log.WithError(err).Error("NewKeysAndCert failed.")
}
// Step 3: Initialize RouterIdentity with KeysAndCert.
routerIdentity := RouterIdentity{
KeysAndCert: *keysAndCert,
}
log.WithFields(logrus.Fields{
"public_key_type": keyCert.PublicKeyType(),
"signing_public_key_type": keyCert.SigningPublicKeyType(),
"padding_length": len(padding),
}).Debug("Successfully created RouterIdentity")
return &routerIdentity, nil
}

View File

@@ -0,0 +1,194 @@
package router_info
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func consolidateNetDb(sourcePath string, destPath string) error {
// Create destination directory if it doesn't exist
if err := os.MkdirAll(destPath, 0755); err != nil {
return fmt.Errorf("failed to create destination directory: %v", err)
}
// Walk through all subdirectories
return filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error accessing path %q: %v", path, err)
}
// Skip if it's a directory
if info.IsDir() {
return nil
}
// Check if this is a routerInfo file
if strings.HasPrefix(info.Name(), "routerInfo-") && strings.HasSuffix(info.Name(), ".dat") {
// Create source file path
srcFile := path
// Create destination file path
dstFile := filepath.Join(destPath, info.Name())
// Copy the file
if err := copyFile(srcFile, dstFile); err != nil {
return fmt.Errorf("failed to copy %s: %v", info.Name(), err)
}
}
return nil
})
}
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(dst)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
return err
}
func consolidateAllNetDbs(tempDir string) error {
// Common paths for I2P and I2Pd netDb
i2pPath := filepath.Join(os.Getenv("HOME"), ".i2p/netDb")
i2pdPath := filepath.Join(os.Getenv("HOME"), ".i2pd/netDb")
// Create the temp directory
if err := os.MkdirAll(tempDir, 0755); err != nil {
return fmt.Errorf("failed to create temp directory: %v", err)
}
// Try to consolidate I2P netDb
if _, err := os.Stat(i2pPath); err == nil {
if err := consolidateNetDb(i2pPath, tempDir); err != nil {
fmt.Printf("Warning: Error processing I2P netDb: %v\n", err)
}
}
// Try to consolidate I2Pd netDb
if _, err := os.Stat(i2pdPath); err == nil {
if err := consolidateNetDb(i2pdPath, tempDir); err != nil {
fmt.Printf("Warning: Error processing I2Pd netDb: %v\n", err)
}
}
return nil
}
func cleanupTempDir(path string) error {
if err := os.RemoveAll(path); err != nil {
return fmt.Errorf("failed to cleanup temporary directory %s: %v", path, err)
}
return nil
}
func createTempNetDbDir() (string, error) {
// Get system's temp directory in a platform-independent way
baseDir := os.TempDir()
// Create unique directory name with timestamp
timestamp := time.Now().Unix()
dirName := fmt.Sprintf("go-i2p-testfiles-%d", timestamp)
// Join paths in a platform-independent way
tempDir := filepath.Join(baseDir, dirName)
// Create the directory with appropriate permissions
err := os.MkdirAll(tempDir, 0755)
if err != nil {
return "", fmt.Errorf("failed to create temporary directory: %v", err)
}
return tempDir, nil
}
func Test10K(t *testing.T) {
i2pPath := filepath.Join(os.Getenv("HOME"), ".i2p/netDb")
i2pdPath := filepath.Join(os.Getenv("HOME"), ".i2pd/netDb")
// Skip if neither directory exists
if _, err := os.Stat(i2pPath); os.IsNotExist(err) {
if _, err := os.Stat(i2pdPath); os.IsNotExist(err) {
t.Skip("Neither .i2p nor .i2pd netDb directories exist, so we will skip.")
}
}
tempDir, err := createTempNetDbDir()
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
//defer cleanupTempDir(tempDir)
if err := consolidateAllNetDbs(tempDir); err != nil {
t.Fatalf("Failed to consolidate netDbs: %v", err)
}
time.Sleep(1 * time.Second)
targetDir, err := createTempNetDbDir()
if err != nil {
panic(err)
}
// Read and process all router info files
files, err := os.ReadDir(tempDir)
if err != nil {
t.Fatalf("Failed to read temp directory: %v", err)
}
for _, file := range files {
if !file.IsDir() && strings.HasPrefix(file.Name(), "routerInfo-") {
// Read the router info file
log.Println("RI LOAD: ", file.Name())
data, err := os.ReadFile(filepath.Join(tempDir, file.Name()))
if err != nil {
t.Logf("Failed to read file %s: %v", file.Name(), err)
continue
}
// Parse the router info
//fmt.Printf("data: %s\n", string(data))
routerInfo, _, err := ReadRouterInfo(data)
if err != nil {
t.Logf("Failed to parse router info from %s: %v", file.Name(), err)
continue
}
// Write the router info to the target directory
routerBytes, err := routerInfo.Bytes()
if err != nil {
t.Logf("Failed to serialize router info %s: %v", file.Name(), err)
continue
}
err = os.WriteFile(filepath.Join(targetDir, file.Name()), routerBytes, 0644)
if err != nil {
t.Logf("Failed to write router info %s: %v", file.Name(), err)
continue
}
}
}
// Cleanup both directories
if err := cleanupTempDir(tempDir); err != nil {
log.WithError(err).Error("Failed to cleanup temp directory")
t.Errorf("Failed to cleanup temp directory: %v", err)
} else {
log.Debug("Successfully cleaned up temp directory")
}
if err := cleanupTempDir(targetDir); err != nil {
log.WithError(err).Error("Failed to cleanup target directory")
t.Errorf("Failed to cleanup target directory: %v", err)
} else {
log.Debug("Successfully cleaned up target directory")
}
}

View File

@@ -2,17 +2,26 @@
package router_info
import (
"encoding/binary"
"errors"
"github.com/go-i2p/go-i2p/lib/common/certificate"
"strconv"
"strings"
"time"
"github.com/go-i2p/go-i2p/lib/crypto"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/data"
. "github.com/go-i2p/go-i2p/lib/common/router_address"
. "github.com/go-i2p/go-i2p/lib/common/router_identity"
. "github.com/go-i2p/go-i2p/lib/common/signature"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
const ROUTER_INFO_MIN_SIZE = 439
const (
@@ -115,7 +124,8 @@ type RouterInfo struct {
// Bytes returns the RouterInfo as a []byte suitable for writing to a stream.
func (router_info RouterInfo) Bytes() (bytes []byte, err error) {
bytes = append(bytes, router_info.router_identity.KeysAndCert.Bytes()...)
log.Debug("Converting RouterInfo to bytes")
bytes = append(bytes, router_info.router_identity.Bytes()...)
bytes = append(bytes, router_info.published.Bytes()...)
bytes = append(bytes, router_info.size.Bytes()...)
for _, router_address := range router_info.addresses {
@@ -124,20 +134,35 @@ func (router_info RouterInfo) Bytes() (bytes []byte, err error) {
bytes = append(bytes, router_info.peer_size.Bytes()...)
bytes = append(bytes, router_info.options.Data()...)
bytes = append(bytes, []byte(*router_info.signature)...)
log.WithField("bytes_length", len(bytes)).Debug("Converted RouterInfo to bytes")
return bytes, err
}
func (router_info RouterInfo) String() string {
str := "Certificate: " + string(router_info.router_identity.KeysAndCert.Bytes())
str += "Published: " + string(router_info.published.Bytes())
str += "Addresses:" + string(router_info.size.Bytes())
for index, router_address := range router_info.addresses {
str += "Address " + strconv.Itoa(index) + ": " + router_address.String()
// Convert a byte slice into a string like [1, 2, 3] -> "1, 2, 3"
func bytesToString(bytes []byte) string {
str := "["
for i, b := range bytes {
str += strconv.Itoa(int(b))
if i < len(bytes)-1 {
str += ", "
}
}
str += "Peer Size: " + string(router_info.peer_size.Bytes())
str += "Options: " + string(router_info.options.Data())
str += "Signature: " + string([]byte(*router_info.signature))
str += "]"
return str
}
func (router_info RouterInfo) String() string {
log.Debug("Converting RouterInfo to string")
str := "Certificate: " + bytesToString(router_info.router_identity.Bytes()) + "\n"
str += "Published: " + bytesToString(router_info.published.Bytes()) + "\n"
str += "Addresses:" + bytesToString(router_info.size.Bytes()) + "\n"
for index, router_address := range router_info.addresses {
str += "Address " + strconv.Itoa(index) + ": " + router_address.String() + "\n"
}
str += "Peer Size: " + bytesToString(router_info.peer_size.Bytes()) + "\n"
str += "Options: " + bytesToString(router_info.options.Data()) + "\n"
str += "Signature: " + bytesToString([]byte(*router_info.signature)) + "\n"
log.WithField("string_length", len(str)).Debug("Converted RouterInfo to string")
return str
}
@@ -148,7 +173,12 @@ func (router_info *RouterInfo) RouterIdentity() *RouterIdentity {
// IndentHash returns the identity hash (sha256 sum) for this RouterInfo.
func (router_info *RouterInfo) IdentHash() Hash {
data, _ := router_info.RouterIdentity().KeyCertificate.Data()
log.Debug("Calculating IdentHash for RouterInfo")
//data, _ := router_info.RouterIdentity().keyCertificate.Data()
cert := router_info.RouterIdentity().KeysAndCert.Certificate()
data := cert.Data()
hash := HashData(data)
log.WithField("hash", hash).Debug("Calculated IdentHash for RouterInfo")
return HashData(data)
}
@@ -159,11 +189,14 @@ func (router_info *RouterInfo) Published() *Date {
// RouterAddressCount returns the count of RouterAddress in this RouterInfo as a Go integer.
func (router_info *RouterInfo) RouterAddressCount() int {
return router_info.size.Int()
count := router_info.size.Int()
log.WithField("count", count).Debug("Retrieved RouterAddressCount from RouterInfo")
return count
}
// RouterAddresses returns all RouterAddresses for this RouterInfo as []*RouterAddress.
func (router_info *RouterInfo) RouterAddresses() []*RouterAddress {
log.WithField("address_count", len(router_info.addresses)).Debug("Retrieved RouterAddresses from RouterInfo")
return router_info.addresses
}
@@ -193,30 +226,32 @@ func (router_info RouterInfo) Network() string {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error) {
log.WithField("input_length", len(bytes)).Debug("Reading RouterInfo from bytes")
info.router_identity, remainder, err = ReadRouterIdentity(bytes)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(bytes),
"required_len": ROUTER_INFO_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing router info")
err = errors.New("error parsing router info: not enough data")
err = errors.New("error parsing router info: not enough data to read identity")
return
}
info.published, remainder, err = NewDate(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
"required_len": DATE_SIZE,
"reason": "not enough data",
}).Error("error parsing router info")
err = errors.New("error parsing router info: not enough data")
err = errors.New("error parsing router info: not enough data to read publish date")
}
info.size, remainder, err = NewInteger(remainder, 1)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
"required_len": info.size.Int(),
@@ -227,21 +262,25 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
address, more, err := ReadRouterAddress(remainder)
remainder = more
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": ROUTER_ADDRESS_SIZE,
"reason": "not enough data",
}).Error("error parsing router address")
err = errors.New("error parsing router info: not enough data")
err = errors.New("error parsing router info: not enough data to read router addresses")
}
info.addresses = append(info.addresses, &address)
}
info.peer_size, remainder, err = NewInteger(remainder, 1)
if err != nil {
log.WithError(err).Error("Failed to read PeerSize")
return
}
var errs []error
info.options, remainder, errs = NewMapping(remainder)
if len(errs) != 0 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": MAPPING_SIZE,
@@ -253,39 +292,176 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
}
err = errors.New("error parsing router info: " + estring)
}
info.signature, remainder, err = NewSignature(remainder)
sigType, err := certificate.GetSignatureTypeFromCertificate(info.router_identity.Certificate())
log.WithFields(logrus.Fields{
"sigType": sigType,
}).Debug("Got sigType")
info.signature, remainder, err = NewSignature(remainder, sigType)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": MAPPING_SIZE,
"reason": "not enough data",
}).Error("error parsing router info")
err = errors.New("error parsing router info: not enough data")
err = errors.New("error parsing router info: not enough data to read signature")
}
log.WithFields(logrus.Fields{
"router_identity": info.router_identity,
"published": info.published,
"address_count": len(info.addresses),
"remainder_length": len(remainder),
}).Debug("Successfully read RouterInfo")
return
}
// serializeWithoutSignature serializes the RouterInfo up to (but not including) the signature.
func (ri *RouterInfo) serializeWithoutSignature() []byte {
var bytes []byte
// Serialize RouterIdentity
bytes = append(bytes, ri.router_identity.Bytes()...)
// Serialize Published Date
bytes = append(bytes, ri.published.Bytes()...)
// Serialize Size
bytes = append(bytes, ri.size.Bytes()...)
// Serialize Addresses
for _, addr := range ri.addresses {
bytes = append(bytes, addr.Bytes()...)
}
// Serialize PeerSize (always zero)
bytes = append(bytes, ri.peer_size.Bytes()...)
// Serialize Options
bytes = append(bytes, ri.options.Data()...)
return bytes
}
func NewRouterInfo(
routerIdentity *RouterIdentity,
publishedTime time.Time,
addresses []*RouterAddress,
options map[string]string,
signingPrivateKey crypto.SigningPrivateKey,
sigType int,
) (*RouterInfo, error) {
log.Debug("Creating new RouterInfo")
// 1. Create Published Date
millis := publishedTime.UnixNano() / int64(time.Millisecond)
dateBytes := make([]byte, DATE_SIZE)
binary.BigEndian.PutUint64(dateBytes, uint64(millis))
publishedDate, _, err := ReadDate(dateBytes)
if err != nil {
log.WithError(err).Error("Failed to create Published Date")
return nil, err
}
// 2. Create Size Integer
sizeInt, err := NewIntegerFromInt(len(addresses), 1)
if err != nil {
log.WithError(err).Error("Failed to create Size Integer")
return nil, err
}
// 3. Create PeerSize Integer (always 0)
peerSizeInt, err := NewIntegerFromInt(0, 1)
if err != nil {
log.WithError(err).Error("Failed to create PeerSize Integer")
return nil, err
}
// 4. Convert options map to Mapping
mapping, err := GoMapToMapping(options)
if err != nil {
log.WithError(err).Error("Failed to convert options map to Mapping")
return nil, err
}
// 5. Assemble RouterInfo without signature
routerInfo := &RouterInfo{
router_identity: *routerIdentity,
published: &publishedDate,
size: sizeInt,
addresses: addresses,
peer_size: peerSizeInt,
options: mapping,
signature: nil, // To be set after signing
}
// 6. Serialize RouterInfo without signature
dataBytes := routerInfo.serializeWithoutSignature()
// 7. Compute signature over serialized data
signer, err := signingPrivateKey.NewSigner()
if err != nil {
log.WithError(err).Error("Failed to create new signer")
return nil, err
}
signatureBytes, err := signer.Sign(dataBytes)
if err != nil {
log.WithError(err).Error("Failed to sign")
}
// 8. Create Signature struct from signatureBytes
sig, _, err := ReadSignature(signatureBytes, sigType)
if err != nil {
log.WithError(err).Error("Failed to create Signature from signature bytes")
return nil, err
}
// 9. Attach signature to RouterInfo
routerInfo.signature = &sig
log.WithFields(logrus.Fields{
"router_identity": routerIdentity,
"published": publishedDate,
"address_count": len(addresses),
"options": options,
"signature": sig,
}).Debug("Successfully created RouterInfo")
return routerInfo, nil
}
func (router_info *RouterInfo) RouterCapabilities() string {
log.Debug("Retrieving RouterCapabilities")
str, err := ToI2PString("caps")
if err != nil {
log.WithError(err).Error("Failed to create I2PString for 'caps'")
return ""
}
return string(router_info.options.Values().Get(str))
// return string(router_info.options.Values().Get(str))
caps := string(router_info.options.Values().Get(str))
log.WithField("capabilities", caps).Debug("Retrieved RouterCapabilities")
return caps
}
func (router_info *RouterInfo) RouterVersion() string {
log.Debug("Retrieving RouterVersion")
str, err := ToI2PString("router.version")
if err != nil {
log.WithError(err).Error("Failed to create I2PString for 'router.version'")
return ""
}
return string(router_info.options.Values().Get(str))
// return string(router_info.options.Values().Get(str))
version := string(router_info.options.Values().Get(str))
log.WithField("version", version).Debug("Retrieved RouterVersion")
return version
}
func (router_info *RouterInfo) GoodVersion() bool {
log.Debug("Checking if RouterVersion is good")
version := router_info.RouterVersion()
v := strings.Split(version, ".")
if len(v) != 3 {
log.WithField("version", version).Warn("Invalid version format")
return false
}
if v[0] == "0" {
@@ -296,27 +472,41 @@ func (router_info *RouterInfo) GoodVersion() bool {
}
}
}
log.WithField("version", version).Warn("Version not in good range")
return false
}
func (router_info *RouterInfo) UnCongested() bool {
log.Debug("Checking if RouterInfo is uncongested")
caps := router_info.RouterCapabilities()
if strings.Contains(caps, "K") {
log.WithField("reason", "K capability").Warn("RouterInfo is congested")
return false
}
if strings.Contains(caps, "G") {
log.WithField("reason", "G capability").Warn("RouterInfo is congested")
return false
}
if strings.Contains(caps, "E") {
log.WithField("reason", "E capability").Warn("RouterInfo is congested")
return false
}
log.Debug("RouterInfo is uncongested")
return true
}
func (router_info *RouterInfo) Reachable() bool {
log.Debug("Checking if RouterInfo is reachable")
caps := router_info.RouterCapabilities()
if strings.Contains(caps, "U") {
log.WithField("reason", "U capability").Debug("RouterInfo is unreachable")
return false
}
return strings.Contains(caps, "R")
// return strings.Contains(caps, "R")
reachable := strings.Contains(caps, "R")
log.WithFields(logrus.Fields{
"reachable": reachable,
"reason": "R capability",
}).Debug("Checked RouterInfo reachability")
return reachable
}

View File

@@ -0,0 +1,112 @@
package router_info
import (
"bytes"
"crypto/rand"
"encoding/binary"
"github.com/go-i2p/go-i2p/lib/common/signature"
"testing"
"time"
"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/router_address"
"github.com/go-i2p/go-i2p/lib/common/router_identity"
"github.com/go-i2p/go-i2p/lib/crypto"
"golang.org/x/crypto/openpgp/elgamal"
)
func TestCreateRouterInfo(t *testing.T) {
// Generate signing key pair (Ed25519)
var ed25519_privkey crypto.Ed25519PrivateKey
_, err := (&ed25519_privkey).Generate()
if err != nil {
t.Fatalf("Failed to generate Ed25519 private key: %v\n", err)
}
ed25519_pubkey_raw, err := ed25519_privkey.Public()
if err != nil {
t.Fatalf("Failed to derive Ed25519 public key: %v\n", err)
}
ed25519_pubkey, ok := ed25519_pubkey_raw.(crypto.SigningPublicKey)
if !ok {
t.Fatalf("Failed to get SigningPublicKey from Ed25519 public key")
}
// Generate encryption key pair (ElGamal)
var elgamal_privkey elgamal.PrivateKey
err = crypto.ElgamalGenerate(&elgamal_privkey, rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ElGamal private key: %v\n", err)
}
// Convert elgamal private key to crypto.ElgPrivateKey
var elg_privkey crypto.ElgPrivateKey
xBytes := elgamal_privkey.X.Bytes()
if len(xBytes) > 256 {
t.Fatalf("ElGamal private key X too large")
}
copy(elg_privkey[256-len(xBytes):], xBytes)
// Convert elgamal public key to crypto.ElgPublicKey
var elg_pubkey crypto.ElgPublicKey
yBytes := elgamal_privkey.PublicKey.Y.Bytes()
if len(yBytes) > 256 {
t.Fatalf("ElGamal public key Y too large")
}
copy(elg_pubkey[256-len(yBytes):], yBytes)
// Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey
// Create KeyCertificate specifying key types
var payload bytes.Buffer
signingPublicKeyType, _ := data.NewIntegerFromInt(7, 2)
cryptoPublicKeyType, _ := data.NewIntegerFromInt(0, 2)
err = binary.Write(&payload, binary.BigEndian, signingPublicKeyType)
if err != nil {
t.Fatalf("Failed to write signing public key type to payload: %v\n", err)
}
err = binary.Write(&payload, binary.BigEndian, cryptoPublicKeyType)
if err != nil {
t.Fatalf("Failed to write crypto public key type to payload: %v\n", err)
}
// Create KeyCertificate specifying key types
cert, err := certificate.NewCertificateWithType(certificate.CERT_KEY, payload.Bytes())
if err != nil {
t.Fatalf("Failed to create new certificate: %v\n", err)
}
// Create RouterIdentity
routerIdentity, err := router_identity.NewRouterIdentity(elg_pubkey, ed25519_pubkey, *cert, nil)
if err != nil {
t.Fatalf("Failed to create router identity: %v\n", err)
}
// create some dummy addresses
options := map[string]string{}
routerAddress, err := router_address.NewRouterAddress(3, <-time.After(1*time.Second), "NTCP2", options)
if err != nil {
t.Fatalf("Failed to create router address: %v\n", err)
}
routerAddresses := []*router_address.RouterAddress{routerAddress}
// create router info
routerInfo, err := NewRouterInfo(routerIdentity, time.Now(), routerAddresses, nil, &ed25519_privkey, signature.SIGNATURE_TYPE_EDDSA_SHA512_ED25519)
if err != nil {
t.Fatalf("Failed to create router info: %v\n", err)
}
t.Run("Serialize and Deserialize RouterInfo", func(t *testing.T) {
routerInfoBytes, err := routerInfo.Bytes()
t.Log(len(routerInfoBytes), routerInfo.String(), routerInfoBytes)
if err != nil {
t.Fatalf("Failed to write RouterInfo to bytes: %v\n", err)
}
_, _, err = ReadRouterInfo(routerInfoBytes)
if err != nil {
t.Fatalf("Failed to read routerInfoBytes: %v\n", err)
}
})
}

View File

@@ -1,6 +1,8 @@
// Package session_key implements the I2P SessionKey common data structure
package session_key
import log "github.com/sirupsen/logrus"
/*
[SessionKey]
Accurate for version 0.9.49
@@ -22,13 +24,20 @@ type SessionKey [32]byte
// Returns a list of errors that occurred during parsing.
func ReadSessionKey(bytes []byte) (info SessionKey, remainder []byte, err error) {
// TODO: stub
log.Warn("ReadSessionKey is not implemented")
return
}
// NewSessionKey creates a new *SessionKey from []byte using ReadSessionKey.
// Returns a pointer to SessionKey unlike ReadSessionKey.
func NewSessionKey(data []byte) (session_key *SessionKey, remainder []byte, err error) {
log.WithField("input_length", len(data)).Debug("Creating new SessionKey")
sessionKey, remainder, err := ReadSessionKey(data)
if err != nil {
log.WithError(err).Error("Failed to create new SessionKey")
return nil, remainder, err
}
session_key = &sessionKey
log.Debug("Successfully created new SessionKey")
return
}

View File

@@ -1,6 +1,13 @@
// Package session_tag implements the I2P SessionTag common data structure
package session_tag
import (
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
/*
[SessionKey]
Accurate for version 0.9.49
@@ -22,13 +29,22 @@ type SessionTag [32]byte
// Returns a list of errors that occurred during parsing.
func ReadSessionTag(bytes []byte) (info SessionTag, remainder []byte, err error) {
// TODO: stub
log.Warn("ReadSessionTag is not implemented")
return
}
// NewSessionTag creates a new *SessionTag from []byte using ReadSessionTag.
// Returns a pointer to SessionTag unlike ReadSessionTag.
func NewSessionTag(data []byte) (session_tag *SessionTag, remainder []byte, err error) {
log.WithField("input_length", len(data)).Debug("Creating new SessionTag")
sessionTag, remainder, err := ReadSessionTag(data)
if err != nil {
log.WithError(err).Error("Failed to read SessionTag")
return nil, remainder, err
}
session_tag = &sessionTag
log.WithFields(logrus.Fields{
"remainder_length": len(remainder),
}).Debug("Successfully created new SessionTag")
return
}

View File

@@ -1,6 +1,14 @@
// Package signature implements the I2P Signature common data structure
package signature
import (
"fmt"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Lengths of signature keys
const (
DSA_SHA1_SIZE = 40
@@ -15,6 +23,19 @@ const (
RedDSA_SHA512_Ed25519_SIZE = 64
)
const (
SIGNATURE_TYPE_DSA_SHA1 = 0
SIGNATURE_TYPE_ECDSA_SHA256_P256 = 1
SIGNATURE_TYPE_ECDSA_SHA384_P384 = 2
SIGNATURE_TYPE_ECDSA_SHA512_P521 = 3
SIGNATURE_TYPE_RSA_SHA256_2048 = 4
SIGNATURE_TYPE_RSA_SHA384_3072 = 5
SIGNATURE_TYPE_RSA_SHA512_4096 = 6
SIGNATURE_TYPE_EDDSA_SHA512_ED25519 = 7
SIGNATURE_TYPE_EDDSA_SHA512_ED25519PH = 8
SIGNATURE_TYPE_REDDSA_SHA512_ED25519 = 11
)
/*
[Signature]
Accurate for version 0.9.49
@@ -32,18 +53,51 @@ DSA_SHA1. As of release 0.9.12, other types may be supported, depending on conte
// https://geti2p.net/spec/common-structures#signature
type Signature []byte
// ReadSignature returns Signature from a []byte.
// ReadSignature returns a Signature from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadSignature(bytes []byte) (info Signature, remainder []byte, err error) {
// TODO: stub
// Returns an error if there is insufficient data to read the signature.
//
// Since the signature type and length are inferred from context (the type of key used),
// and are not explicitly stated, this function assumes the default signature type (DSA_SHA1)
// with a length of 40 bytes.
//
// If a different signature type is expected based on context, this function should be
// modified accordingly to handle the correct signature length.
func ReadSignature(data []byte, sigType int) (sig Signature, remainder []byte, err error) {
var sigLength int
switch sigType {
case SIGNATURE_TYPE_DSA_SHA1:
sigLength = DSA_SHA1_SIZE
case SIGNATURE_TYPE_EDDSA_SHA512_ED25519:
sigLength = EdDSA_SHA512_Ed25519_SIZE
default:
err = fmt.Errorf("unsupported signature type: %d", sigType)
return
}
if len(data) < sigLength {
err = fmt.Errorf("insufficient data to read signature: need %d bytes, have %d", sigLength, len(data))
log.WithError(err).Error("Failed to read Signature")
return
}
sig = data[:sigLength]
remainder = data[sigLength:]
return
}
// NewSignature creates a new *Signature from []byte using ReadSignature.
// Returns a pointer to Signature unlike ReadSignature.
func NewSignature(data []byte) (session_tag *Signature, remainder []byte, err error) {
sessionTag, remainder, err := ReadSignature(data)
session_tag = &sessionTag
func NewSignature(data []byte, sigType int) (signature *Signature, remainder []byte, err error) {
log.WithField("input_length", len(data)).Debug("Creating new Signature")
sig, remainder, err := ReadSignature(data, sigType)
if err != nil {
log.WithError(err).Error("Failed to read Signature")
return nil, remainder, err
}
signature = &sig
log.WithFields(logrus.Fields{
"signature_length": len(sig),
"remainder_length": len(remainder),
}).Debug("Successfully created new Signature")
return
}

113
lib/config/config.go Normal file
View File

@@ -0,0 +1,113 @@
package config
import (
"os"
"path/filepath"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
var (
CfgFile string
log = logger.GetGoI2PLogger()
)
const GOI2P_BASE_DIR = ".go-i2p"
func InitConfig() {
defaultConfigDir := filepath.Join(os.Getenv("HOME"), GOI2P_BASE_DIR)
defaultConfigFile := filepath.Join(defaultConfigDir, "config.yaml")
if CfgFile != "" {
// Use config file from the flag
viper.SetConfigFile(CfgFile)
} else {
// Create default config if it doesn't exist
if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
// Ensure directory exists
if err := os.MkdirAll(defaultConfigDir, 0o755); err != nil {
log.Fatalf("Could not create config directory: %s", err)
}
// Create default configuration
defaultConfig := struct {
BaseDir string `yaml:"base_dir"`
WorkingDir string `yaml:"working_dir"`
NetDB NetDbConfig `yaml:"netdb"`
Bootstrap BootstrapConfig `yaml:"bootstrap"`
}{
BaseDir: DefaultRouterConfig().BaseDir,
WorkingDir: DefaultRouterConfig().WorkingDir,
NetDB: *DefaultRouterConfig().NetDb,
Bootstrap: *DefaultRouterConfig().Bootstrap,
}
yamlData, err := yaml.Marshal(defaultConfig)
if err != nil {
log.Fatalf("Could not marshal default config: %s", err)
}
// Write default config file
if err := os.WriteFile(defaultConfigFile, yamlData, 0o644); err != nil {
log.Fatalf("Could not write default config file: %s", err)
}
log.Debugf("Created default configuration at: %s", defaultConfigFile)
}
// Set up viper to use the config file
viper.AddConfigPath(defaultConfigDir)
viper.SetConfigName("config")
viper.SetConfigType("yaml")
}
// Load defaults
setDefaults()
if err := viper.ReadInConfig(); err != nil {
log.Warnf("Error reading config file: %s", err)
} else {
log.Debugf("Using config file: %s", viper.ConfigFileUsed())
}
// Update RouterConfigProperties
UpdateRouterConfig()
}
func setDefaults() {
// Router defaults
viper.SetDefault("base_dir", DefaultRouterConfig().BaseDir)
viper.SetDefault("working_dir", DefaultRouterConfig().WorkingDir)
// NetDb defaults
viper.SetDefault("netdb.path", DefaultNetDbConfig.Path)
// Bootstrap defaults
viper.SetDefault("bootstrap.low_peer_threshold", DefaultBootstrapConfig.LowPeerThreshold)
viper.SetDefault("bootstrap.reseed_servers", []ReseedConfig{})
}
func UpdateRouterConfig() {
// Update Router configuration
RouterConfigProperties.BaseDir = viper.GetString("base_dir")
RouterConfigProperties.WorkingDir = viper.GetString("working_dir")
// Update NetDb configuration
RouterConfigProperties.NetDb = &NetDbConfig{
Path: viper.GetString("netdb.path"),
}
// Update Bootstrap configuration
var reseedServers []*ReseedConfig
if err := viper.UnmarshalKey("bootstrap.reseed_servers", &reseedServers); err != nil {
log.Warnf("Error parsing reseed servers: %s", err)
reseedServers = []*ReseedConfig{}
}
RouterConfigProperties.Bootstrap = &BootstrapConfig{
LowPeerThreshold: viper.GetInt("bootstrap.low_peer_threshold"),
ReseedServers: reseedServers,
}
}

View File

@@ -26,11 +26,11 @@ func home() string {
}
func defaultBase() string {
return filepath.Join(home(), "go-i2p", "base")
return filepath.Join(home(), GOI2P_BASE_DIR, "base")
}
func defaultConfig() string {
return filepath.Join(home(), "go-i2p", "config")
return filepath.Join(home(), GOI2P_BASE_DIR, "config")
}
// defaults for router

View File

@@ -1 +1,177 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// AESSymmetricKey represents a symmetric key for AES encryption/decryption
type AESSymmetricKey struct {
Key []byte // AES key (must be 16, 24, or 32 bytes for AES-128, AES-192, AES-256)
IV []byte // Initialization Vector (must be 16 bytes for AES)
}
// AESSymmetricEncrypter implements the Encrypter interface using AES
type AESSymmetricEncrypter struct {
Key []byte
IV []byte
}
// Encrypt encrypts data using AES-CBC with PKCS#7 padding
func (e *AESSymmetricEncrypter) Encrypt(data []byte) ([]byte, error) {
log.WithField("data_length", len(data)).Debug("Encrypting data")
block, err := aes.NewCipher(e.Key)
if err != nil {
log.WithError(err).Error("Failed to create AES cipher")
return nil, err
}
plaintext := pkcs7Pad(data, aes.BlockSize)
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewCBCEncrypter(block, e.IV)
mode.CryptBlocks(ciphertext, plaintext)
log.WithField("ciphertext_length", len(ciphertext)).Debug("Data encrypted successfully")
return ciphertext, nil
}
// AESSymmetricDecrypter implements the Decrypter interface using AES
type AESSymmetricDecrypter struct {
Key []byte
IV []byte
}
// Decrypt decrypts data using AES-CBC with PKCS#7 padding
func (d *AESSymmetricDecrypter) Decrypt(data []byte) ([]byte, error) {
log.WithField("data_length", len(data)).Debug("Decrypting data")
block, err := aes.NewCipher(d.Key)
if err != nil {
log.WithError(err).Error("Failed to create AES cipher")
return nil, err
}
if len(data)%aes.BlockSize != 0 {
log.Error("Ciphertext is not a multiple of the block size")
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
}
plaintext := make([]byte, len(data))
mode := cipher.NewCBCDecrypter(block, d.IV)
mode.CryptBlocks(plaintext, data)
plaintext, err = pkcs7Unpad(plaintext)
if err != nil {
log.WithError(err).Error("Failed to unpad plaintext")
return nil, err
}
log.WithField("plaintext_length", len(plaintext)).Debug("Data decrypted successfully")
return plaintext, nil
}
// NewEncrypter creates a new AESSymmetricEncrypter
func (k *AESSymmetricKey) NewEncrypter() (Encrypter, error) {
log.Debug("Creating new AESSymmetricEncrypter")
return &AESSymmetricEncrypter{
Key: k.Key,
IV: k.IV,
}, nil
}
// Len returns the length of the key
func (k *AESSymmetricKey) Len() int {
return len(k.Key)
}
// NewDecrypter creates a new AESSymmetricDecrypter
func (k *AESSymmetricKey) NewDecrypter() (Decrypter, error) {
return &AESSymmetricDecrypter{
Key: k.Key,
IV: k.IV,
}, nil
}
func pkcs7Pad(data []byte, blockSize int) []byte {
log.WithFields(logrus.Fields{
"data_length": len(data),
"block_size": blockSize,
}).Debug("Applying PKCS#7 padding")
padding := blockSize - (len(data) % blockSize)
padText := bytes.Repeat([]byte{byte(padding)}, padding)
padded := append(data, padText...)
log.WithField("padded_length", len(padded)).Debug("PKCS#7 padding applied")
return append(data, padText...)
}
func pkcs7Unpad(data []byte) ([]byte, error) {
log.WithField("data_length", len(data)).Debug("Removing PKCS#7 padding")
length := len(data)
if length == 0 {
log.Error("Data is empty")
return nil, fmt.Errorf("data is empty")
}
padding := int(data[length-1])
if padding == 0 || padding > aes.BlockSize {
log.WithField("padding", padding).Error("Invalid padding")
return nil, fmt.Errorf("invalid padding")
}
paddingStart := length - padding
for i := paddingStart; i < length; i++ {
if data[i] != byte(padding) {
log.Error("Invalid padding")
return nil, fmt.Errorf("invalid padding")
}
}
unpadded := data[:paddingStart]
log.WithField("unpadded_length", len(unpadded)).Debug("PKCS#7 padding removed")
return unpadded, nil
}
// EncryptNoPadding encrypts data using AES-CBC without padding
func (e *AESSymmetricEncrypter) EncryptNoPadding(data []byte) ([]byte, error) {
if len(data)%aes.BlockSize != 0 {
return nil, fmt.Errorf("data length must be a multiple of block size")
}
block, err := aes.NewCipher(e.Key)
if err != nil {
return nil, err
}
ciphertext := make([]byte, len(data))
mode := cipher.NewCBCEncrypter(block, e.IV)
mode.CryptBlocks(ciphertext, data)
return ciphertext, nil
}
// DecryptNoPadding decrypts data using AES-CBC without padding
func (d *AESSymmetricDecrypter) DecryptNoPadding(data []byte) ([]byte, error) {
if len(data)%aes.BlockSize != 0 {
return nil, fmt.Errorf("data length must be a multiple of block size")
}
block, err := aes.NewCipher(d.Key)
if err != nil {
return nil, err
}
plaintext := make([]byte, len(data))
mode := cipher.NewCBCDecrypter(block, d.IV)
mode.CryptBlocks(plaintext, data)
return plaintext, nil
}

182
lib/crypto/aes_test.go Normal file
View File

@@ -0,0 +1,182 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/rand"
"encoding/hex"
"testing"
)
func TestAESEncryptDecrypt(t *testing.T) {
key := make([]byte, 32) // 256-bit key
iv := make([]byte, aes.BlockSize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate random key: %v", err)
}
_, err = rand.Read(iv)
if err != nil {
t.Fatalf("Failed to generate random IV: %v", err)
}
symmetricKey := AESSymmetricKey{
Key: key,
IV: iv,
}
encrypter, err := symmetricKey.NewEncrypter()
if err != nil {
log.Fatalf("Error creating encrypter: %v", err)
}
decrypter, err := symmetricKey.NewDecrypter()
if err != nil {
log.Fatalf("Error creating decrypter: %v", err)
}
testCases := []struct {
name string
plaintext []byte
}{
{"Empty string", []byte("")},
{"Short string", []byte("Hello, World!")},
{"Long string", bytes.Repeat([]byte("A"), 1000)},
{"Exact block size", bytes.Repeat([]byte("A"), aes.BlockSize)},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ciphertext, err := encrypter.Encrypt(tc.plaintext)
if err != nil {
t.Fatalf("Encryption failed: %v", err)
}
decrypted, err := decrypter.Decrypt(ciphertext)
if err != nil {
t.Fatalf("Decryption failed: %v", err)
}
if !bytes.Equal(tc.plaintext, decrypted) {
t.Errorf("Decrypted text doesn't match original plaintext.\nOriginal: %s\nDecrypted: %s",
hex.EncodeToString(tc.plaintext), hex.EncodeToString(decrypted))
}
})
}
}
func TestAESEncryptInvalidKey(t *testing.T) {
invalidKeys := [][]byte{
make([]byte, 15), // Too short
make([]byte, 17), // Invalid length
make([]byte, 31), // Too short for AES-256
make([]byte, 33), // Too long
make([]byte, 0), // Empty
nil, // Nil
}
plaintext := []byte("Test plaintext")
iv := make([]byte, aes.BlockSize)
_, _ = rand.Read(iv)
for _, key := range invalidKeys {
symmetricKey := &AESSymmetricKey{
Key: key,
IV: iv,
}
encrypter, err := symmetricKey.NewEncrypter()
if err == nil {
_, err = encrypter.Encrypt(plaintext)
}
if err == nil {
t.Errorf("Expected error for invalid key length %d, but got none", len(key))
} else {
t.Logf("Correctly got error for key length %d: %v", len(key), err)
}
}
}
func TestAESDecryptInvalidInput(t *testing.T) {
key := make([]byte, 32) // Valid key length for AES-256
iv := make([]byte, aes.BlockSize)
_, _ = rand.Read(key)
_, _ = rand.Read(iv)
symmetricKey := &AESSymmetricKey{
Key: key,
IV: iv,
}
decrypter, err := symmetricKey.NewDecrypter()
if err != nil {
t.Fatalf("Failed to create decrypter: %v", err)
}
invalidCiphertexts := [][]byte{
make([]byte, 15), // Not a multiple of block size
make([]byte, 0), // Empty
nil, // Nil
}
for _, ciphertext := range invalidCiphertexts {
_, err := decrypter.Decrypt(ciphertext)
if err == nil {
t.Errorf("Expected error for invalid ciphertext length %d, but got none", len(ciphertext))
} else {
t.Logf("Correctly got error for ciphertext length %d: %v", len(ciphertext), err)
}
}
}
func TestPKCS7PadUnpad(t *testing.T) {
testCases := []struct {
name string
input []byte
blockSize int
}{
{"Empty input", []byte{}, 16},
{"Exact block size", bytes.Repeat([]byte("A"), 16), 16},
{"One byte short", bytes.Repeat([]byte("A"), 15), 16},
{"Multiple blocks", bytes.Repeat([]byte("A"), 32), 16},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
padded := pkcs7Pad(tc.input, tc.blockSize)
if len(padded)%tc.blockSize != 0 {
t.Errorf("Padded data length (%d) is not a multiple of block size (%d)", len(padded), tc.blockSize)
}
unpadded, err := pkcs7Unpad(padded)
if err != nil {
t.Fatalf("Unpadding failed: %v", err)
}
if !bytes.Equal(tc.input, unpadded) {
t.Errorf("Unpadded data doesn't match original input.\nOriginal: %s\nUnpadded: %s",
hex.EncodeToString(tc.input), hex.EncodeToString(unpadded))
}
})
}
}
func TestPKCS7UnpadInvalidInput(t *testing.T) {
invalidInputs := []struct {
name string
input []byte
}{
{"Empty slice", []byte{}},
{"Invalid padding value", []byte{1, 2, 3, 4, 0}}, // Padding value 0 is invalid
{"Padding larger than block size", append(bytes.Repeat([]byte{17}, 17))}, // Padding value 17 (>16) is invalid
{"Incorrect padding bytes", []byte{1, 2, 3, 4, 5, 6, 2, 3, 3}}, // Last padding bytes do not match padding value
{"Valid block size but invalid padding", append(bytes.Repeat([]byte{1}, 15), 3)}, // Padding value 3, but bytes are 1
}
for _, tc := range invalidInputs {
t.Run(tc.name, func(t *testing.T) {
_, err := pkcs7Unpad(tc.input)
if err == nil {
t.Errorf("Expected error for invalid input %v, but got none", tc.input)
}
})
}
}

View File

@@ -8,6 +8,8 @@ import (
"io"
"math/big"
"github.com/sirupsen/logrus"
curve25519 "go.step.sm/crypto/x25519"
)
@@ -27,14 +29,20 @@ func (k Curve25519PublicKey) NewVerifier() (v Verifier, err error) {
}
func (k Curve25519PublicKey) Len() int {
return len(k)
length := len(k)
log.WithField("length", length).Debug("Retrieved Curve25519PublicKey length")
return length
}
func createCurve25519PublicKey(data []byte) (k *curve25519.PublicKey) {
log.WithField("data_length", len(data)).Debug("Creating Curve25519PublicKey")
if len(data) == 256 {
k2 := curve25519.PublicKey{}
copy(k2[:], data)
k = &k2
log.Debug("Curve25519PublicKey created successfully")
} else {
log.Warn("Invalid data length for Curve25519PublicKey")
}
return
}
@@ -53,6 +61,7 @@ func createCurve25519Encryption(pub *curve25519.PublicKey, rand io.Reader) (enc
if err == nil {
enc = &Curve25519Encryption{}
}*/
log.Warn("createCurve25519Encryption is not implemented")
return
}
@@ -61,11 +70,17 @@ type Curve25519Encryption struct {
}
func (curve25519 *Curve25519Encryption) Encrypt(data []byte) (enc []byte, err error) {
log.WithField("data_length", len(data)).Debug("Encrypting data with Curve25519")
return curve25519.EncryptPadding(data, true)
}
func (curve25519 *Curve25519Encryption) EncryptPadding(data []byte, zeroPadding bool) (encrypted []byte, err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"zero_padding": zeroPadding,
}).Debug("Encrypting data with padding using Curve25519")
if len(data) > 222 {
log.Error("Data too big for Curve25519 encryption")
err = Curve25519EncryptTooBig
return
}
@@ -88,33 +103,55 @@ func (curve25519 *Curve25519Encryption) EncryptPadding(data []byte, zeroPadding
copy(encrypted, curve25519.a.Bytes())
copy(encrypted[256:], b)
}
log.WithField("encrypted_length", len(encrypted)).Debug("Data encrypted successfully")
return
}
func (elg Curve25519PublicKey) NewEncrypter() (enc Encrypter, err error) {
log.Debug("Creating new Curve25519 Encrypter")
k := createCurve25519PublicKey(elg[:])
enc, err = createCurve25519Encryption(k, rand.Reader)
if err != nil {
log.WithError(err).Error("Failed to create Curve25519 Encrypter")
} else {
log.Debug("Curve25519 Encrypter created successfully")
}
return
}
func (v *Curve25519Verifier) VerifyHash(h, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"hash_length": len(h),
"signature_length": len(sig),
}).Debug("Verifying hash with Curve25519")
if len(sig) != curve25519.SignatureSize {
log.Error("Bad signature size")
err = ErrBadSignatureSize
return
}
if len(v.k) != curve25519.PublicKeySize {
log.Error("Invalid Curve25519 public key size")
err = errors.New("failed to verify: invalid curve25519 public key size")
return
}
ok := curve25519.Verify(v.k, h, sig)
if !ok {
log.Error("Invalid signature")
err = errors.New("failed to verify: invalid signature")
} else {
log.Debug("Hash verified successfully")
}
return
}
func (v *Curve25519Verifier) Verify(data, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"signature_length": len(sig),
}).Debug("Verifying data with Curve25519")
h := sha512.Sum512(data)
err = v.VerifyHash(h[:], sig)
return
@@ -127,15 +164,31 @@ type Curve25519Signer struct {
}
func (s *Curve25519Signer) Sign(data []byte) (sig []byte, err error) {
log.WithField("data_length", len(data)).Debug("Signing data with Curve25519")
if len(s.k) != curve25519.PrivateKeySize {
log.Error("Invalid Curve25519 private key size")
err = errors.New("failed to sign: invalid curve25519 private key size")
return
}
h := sha512.Sum512(data)
sig, err = s.SignHash(h[:])
if err != nil {
log.WithError(err).Error("Failed to sign data")
} else {
log.WithField("signature_length", len(sig)).Debug("Data signed successfully")
}
return
}
func (s *Curve25519Signer) SignHash(h []byte) (sig []byte, err error) {
return curve25519.Sign(rand.Reader, s.k, h)
log.WithField("hash_length", len(h)).Debug("Signing hash with Curve25519")
sig, err = curve25519.Sign(rand.Reader, s.k, h)
if err != nil {
log.WithError(err).Error("Failed to sign hash")
} else {
log.WithField("signature_length", len(sig)).Debug("Hash signed successfully")
}
// return curve25519.Sign(rand.Reader, s.k, h)
return
}

View File

@@ -2,7 +2,7 @@
--
import "github.com/go-i2p/go-i2p/lib/crypto"
package for i2p specific crpytography
package for i2p specific cryptography
## Usage
@@ -12,7 +12,72 @@ const (
OPAD = byte(0x5C)
)
```
#### type AESSymmetricKey
```go
type AESSymmetricKey struct {
Key []byte // AES key (must be 16, 24, or 32 bytes for AES-128, AES-192, AES-256)
IV []byte // Initialization Vector (must be 16 bytes for AES)
}
```
AESSymmetricKey represents a symmetric key for AES encryption/decryption
#### func (AESSymmetricKey) NewEncrypter
```go
func (k *AESSymmetricKey) NewEncrypter() (Encrypter, error)
```
NewEncrypter creates a new AESSymmetricEncrypter
#### func (AESSymmetricKey) NewDecrypter
```go
func (k *AESSymmetricKey) NewDecrypter() (Decrypter, error)
```
NewDecrypter creates a new AESSymmetricDecrypter
#### func (AESSymmetricKey) Len
```go
func (k *AESSymmetricKey) Len() int
```
Len returns the length of the key
#### type AESSymmetricEncrypter
```go
type AESSymmetricEncrypter struct {
Key []byte
IV []byte
}
```
AESSymmetricEncrypter implements the Encrypter interface using AES
#### func (*AESSymmetricEncrypter) Encrypt
```go
func (e *AESSymmetricEncrypter) Encrypt(data []byte) ([]byte, error)
```
Encrypt encrypts data using AES-CBC with PKCS#7 padding
#### type AESSymmetricDecrypter
```go
type AESSymmetricDecrypter struct {
Key []byte
IV []byte
}
```
AESSymmetricDecrypter implements the Decrypter interface using AES
#### func (*AESSymmetricDecrypter) Decrypt
```go
func (d *AESSymmetricDecrypter) Decrypt(data []byte) ([]byte, error)
```
Decrypt decrypts data using AES-CBC with PKCS#7 padding
```go
var (
ElgDecryptFail = errors.New("failed to decrypt elgamal encrypted data")

View File

@@ -6,6 +6,8 @@ import (
"crypto/sha1"
"io"
"math/big"
"github.com/sirupsen/logrus"
)
var dsap = new(big.Int).SetBytes([]byte{
@@ -43,16 +45,24 @@ var param = dsa.Parameters{
// generate a dsa keypair
func generateDSA(priv *dsa.PrivateKey, rand io.Reader) error {
log.Debug("Generating DSA key pair")
// put our paramters in
priv.P = param.P
priv.Q = param.Q
priv.G = param.G
// generate the keypair
return dsa.GenerateKey(priv, rand)
err := dsa.GenerateKey(priv, rand)
if err != nil {
log.WithError(err).Error("Failed to generate DSA key pair")
} else {
log.Debug("DSA key pair generated successfully")
}
return err
}
// create i2p dsa public key given its public component
func createDSAPublicKey(Y *big.Int) *dsa.PublicKey {
log.Debug("Creating DSA public key")
return &dsa.PublicKey{
Parameters: param,
Y: Y,
@@ -61,6 +71,7 @@ func createDSAPublicKey(Y *big.Int) *dsa.PublicKey {
// createa i2p dsa private key given its public component
func createDSAPrivkey(X *big.Int) (k *dsa.PrivateKey) {
log.Debug("Creating DSA private key")
if X.Cmp(dsap) == -1 {
Y := new(big.Int)
Y.Exp(dsag, X, dsap)
@@ -71,6 +82,9 @@ func createDSAPrivkey(X *big.Int) (k *dsa.PrivateKey) {
},
X: X,
}
log.Debug("DSA private key created successfully")
} else {
log.Warn("Failed to create DSA private key: X is not less than p")
}
return
}
@@ -81,8 +95,13 @@ type DSAVerifier struct {
type DSAPublicKey [128]byte
func (k DSAPublicKey) Bytes() []byte {
return k[:]
}
// create a new dsa verifier
func (k DSAPublicKey) NewVerifier() (v Verifier, err error) {
log.Debug("Creating new DSA verifier")
v = &DSAVerifier{
k: createDSAPublicKey(new(big.Int).SetBytes(k[:])),
}
@@ -91,6 +110,10 @@ func (k DSAPublicKey) NewVerifier() (v Verifier, err error) {
// verify data with a dsa public key
func (v *DSAVerifier) Verify(data, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"sig_length": len(sig),
}).Debug("Verifying DSA signature")
h := sha1.Sum(data)
err = v.VerifyHash(h[:], sig)
return
@@ -98,16 +121,23 @@ func (v *DSAVerifier) Verify(data, sig []byte) (err error) {
// verify hash of data with a dsa public key
func (v *DSAVerifier) VerifyHash(h, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"hash_length": len(h),
"sig_length": len(sig),
}).Debug("Verifying DSA signature hash")
if len(sig) == 40 {
r := new(big.Int).SetBytes(sig[:20])
s := new(big.Int).SetBytes(sig[20:])
if dsa.Verify(v.k, h, r, s) {
// valid signature
log.Debug("DSA signature verified successfully")
} else {
// invalid signature
log.Warn("Invalid DSA signature")
err = ErrInvalidSignature
}
} else {
log.Error("Bad DSA signature size")
err = ErrBadSignatureSize
}
return
@@ -125,6 +155,7 @@ type DSAPrivateKey [20]byte
// create a new dsa signer
func (k DSAPrivateKey) NewSigner() (s Signer, err error) {
log.Debug("Creating new DSA signer")
s = &DSASigner{
k: createDSAPrivkey(new(big.Int).SetBytes(k[:])),
}
@@ -134,30 +165,38 @@ func (k DSAPrivateKey) NewSigner() (s Signer, err error) {
func (k DSAPrivateKey) Public() (pk DSAPublicKey, err error) {
p := createDSAPrivkey(new(big.Int).SetBytes(k[:]))
if p == nil {
log.Error("Invalid DSA private key format")
err = ErrInvalidKeyFormat
} else {
copy(pk[:], p.Y.Bytes())
log.Debug("DSA public key derived successfully")
}
return
}
func (k DSAPrivateKey) Generate() (s DSAPrivateKey, err error) {
log.Debug("Generating new DSA private key")
dk := new(dsa.PrivateKey)
err = generateDSA(dk, rand.Reader)
if err == nil {
copy(k[:], dk.X.Bytes())
s = k
log.Debug("New DSA private key generated successfully")
} else {
log.WithError(err).Error("Failed to generate new DSA private key")
}
return
}
func (ds *DSASigner) Sign(data []byte) (sig []byte, err error) {
log.WithField("data_length", len(data)).Debug("Signing data with DSA")
h := sha1.Sum(data)
sig, err = ds.SignHash(h[:])
return
}
func (ds *DSASigner) SignHash(h []byte) (sig []byte, err error) {
log.WithField("hash_length", len(h)).Debug("Signing hash with DSA")
var r, s *big.Int
r, s, err = dsa.Sign(rand.Reader, ds.k, h)
if err == nil {
@@ -168,6 +207,9 @@ func (ds *DSASigner) SignHash(h []byte) (sig []byte, err error) {
sb := s.Bytes()
sl := len(sb)
copy(sig[20+(20-sl):], sb)
log.WithField("sig_length", len(sig)).Debug("DSA signature created successfully")
} else {
log.WithError(err).Error("Failed to create DSA signature")
}
return
}

View File

@@ -4,8 +4,6 @@ import (
"crypto/rand"
"io"
"testing"
log "github.com/sirupsen/logrus"
)
func TestDSA(t *testing.T) {
@@ -86,5 +84,5 @@ func BenchmarkDSASignVerify(b *testing.B) {
fail++
}
}
log.Infof("%d fails %d signs", fail, b.N)
log.Debugf("%d fails %d signs", fail, b.N)
}

View File

@@ -4,6 +4,8 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"github.com/sirupsen/logrus"
)
type ECDSAVerifier struct {
@@ -14,15 +16,27 @@ type ECDSAVerifier struct {
// verify a signature given the hash
func (v *ECDSAVerifier) VerifyHash(h, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"hash_length": len(h),
"sig_length": len(sig),
}).Debug("Verifying ECDSA signature hash")
r, s := elliptic.Unmarshal(v.c, sig)
if r == nil || s == nil || !ecdsa.Verify(v.k, h, r, s) {
log.Warn("Invalid ECDSA signature")
err = ErrInvalidSignature
} else {
log.Debug("ECDSA signature verified successfully")
}
return
}
// verify a block of data by hashing it and comparing the hash against the signature
func (v *ECDSAVerifier) Verify(data, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"sig_length": len(sig),
}).Debug("Verifying ECDSA signature")
// sum the data and get the hash
h := v.h.New().Sum(data)[len(data):]
// verify
@@ -31,8 +45,13 @@ func (v *ECDSAVerifier) Verify(data, sig []byte) (err error) {
}
func createECVerifier(c elliptic.Curve, h crypto.Hash, k []byte) (ev *ECDSAVerifier, err error) {
log.WithFields(logrus.Fields{
"curve": c.Params().Name,
"hash": h.String(),
}).Debug("Creating ECDSA verifier")
x, y := elliptic.Unmarshal(c, k[:])
if x == nil {
log.Error("Invalid ECDSA key format")
err = ErrInvalidKeyFormat
} else {
ev = &ECDSAVerifier{
@@ -40,6 +59,7 @@ func createECVerifier(c elliptic.Curve, h crypto.Hash, k []byte) (ev *ECDSAVerif
h: h,
}
ev.k = &ecdsa.PublicKey{c, x, y}
log.Debug("ECDSA verifier created successfully")
}
return
}
@@ -53,8 +73,18 @@ func (k ECP256PublicKey) Len() int {
return len(k)
}
func (k ECP256PublicKey) Bytes() []byte {
return k[:]
}
func (k ECP256PublicKey) NewVerifier() (Verifier, error) {
return createECVerifier(elliptic.P256(), crypto.SHA256, k[:])
log.Debug("Creating new P256 ECDSA verifier")
// return createECVerifier(elliptic.P256(), crypto.SHA256, k[:])
v, err := createECVerifier(elliptic.P256(), crypto.SHA256, k[:])
if err != nil {
log.WithError(err).Error("Failed to create P256 ECDSA verifier")
}
return v, err
}
type (
@@ -62,12 +92,22 @@ type (
ECP384PrivateKey [48]byte
)
func (k ECP384PublicKey) Bytes() []byte {
return k[:]
}
func (k ECP384PublicKey) Len() int {
return len(k)
}
func (k ECP384PublicKey) NewVerifier() (Verifier, error) {
return createECVerifier(elliptic.P384(), crypto.SHA384, k[:])
log.Debug("Creating new P384 ECDSA verifier")
v, err := createECVerifier(elliptic.P384(), crypto.SHA384, k[:])
if err != nil {
log.WithError(err).Error("Failed to create P384 ECDSA verifier")
}
return v, err
// return createECVerifier(elliptic.P384(), crypto.SHA384, k[:])
}
type (
@@ -75,10 +115,20 @@ type (
ECP521PrivateKey [66]byte
)
func (k ECP521PublicKey) Bytes() []byte {
return k[:]
}
func (k ECP521PublicKey) Len() int {
return len(k)
}
func (k ECP521PublicKey) NewVerifier() (Verifier, error) {
return createECVerifier(elliptic.P521(), crypto.SHA512, k[:])
log.Debug("Creating new P521 ECDSA verifier")
v, err := createECVerifier(elliptic.P521(), crypto.SHA512, k[:])
if err != nil {
log.WithError(err).Error("Failed to create P521 ECDSA verifier")
}
return v, err
// return createECVerifier(elliptic.P521(), crypto.SHA512, k[:])
}

View File

@@ -8,9 +8,14 @@ import (
"errors"
"io"
"math/big"
"github.com/sirupsen/logrus"
)
var Ed25519EncryptTooBig = errors.New("failed to encrypt data, too big for Ed25519")
var (
Ed25519EncryptTooBig = errors.New("failed to encrypt data, too big for Ed25519")
ErrInvalidPublicKeySize = errors.New("failed to verify: invalid ed25519 public key size")
)
type Ed25519PublicKey []byte
@@ -29,30 +34,53 @@ func (k Ed25519PublicKey) Len() int {
return len(k)
}
func (k Ed25519PublicKey) Bytes() []byte {
return k
}
func createEd25519PublicKey(data []byte) (k *ed25519.PublicKey) {
log.WithField("data_length", len(data)).Debug("Creating Ed25519 public key")
if len(data) == 256 {
k2 := ed25519.PublicKey{}
copy(k2[:], data)
k = &k2
log.Debug("Ed25519 public key created successfully")
} else {
log.Warn("Invalid data length for Ed25519 public key")
}
return
}
func createEd25519Encryption(pub *ed25519.PublicKey, rand io.Reader) (enc *Ed25519Encryption, err error) {
/*kbytes := make([]byte, 256)
k := new(big.Int)
for err == nil {
_, err = io.ReadFull(rand, kbytes)
k = new(big.Int).SetBytes(kbytes)
k = k.Mod(k, pub.P)
if k.Sign() != 0 {
break
}
// createEd25519Encryption initializes the Ed25519Encryption struct using the public key.
func createEd25519Encryption(pub *ed25519.PublicKey, randReader io.Reader) (*Ed25519Encryption, error) {
// Define p = 2^255 - 19
p := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))
// Validate public key length
if len(*pub) != ed25519.PublicKeySize {
log.WithField("pub_length", len(*pub)).Error("Invalid Ed25519 public key size")
return nil, ErrInvalidPublicKeySize
}
if err == nil {
enc = &Ed25519Encryption{}
}*/
return
// Convert public key bytes to big.Int
a := new(big.Int).SetBytes(*pub)
// Generate a random scalar b1 in [0, p)
b1, err := rand.Int(randReader, p)
if err != nil {
log.WithError(err).Error("Failed to generate b1 for Ed25519Encryption")
return nil, err
}
// Initialize Ed25519Encryption struct
enc := &Ed25519Encryption{
p: p,
a: a,
b1: b1,
}
log.Debug("Ed25519Encryption created successfully")
return enc, nil
}
type Ed25519Encryption struct {
@@ -60,11 +88,18 @@ type Ed25519Encryption struct {
}
func (ed25519 *Ed25519Encryption) Encrypt(data []byte) (enc []byte, err error) {
log.Warn("createEd25519Encryption is not implemented")
return ed25519.EncryptPadding(data, true)
}
func (ed25519 *Ed25519Encryption) EncryptPadding(data []byte, zeroPadding bool) (encrypted []byte, err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"zero_padding": zeroPadding,
}).Debug("Encrypting data with padding using Ed25519")
if len(data) > 222 {
log.Error("Data too big for Ed25519 encryption")
err = Ed25519EncryptTooBig
return
}
@@ -87,33 +122,61 @@ func (ed25519 *Ed25519Encryption) EncryptPadding(data []byte, zeroPadding bool)
copy(encrypted, ed25519.a.Bytes())
copy(encrypted[256:], b)
}
log.WithField("encrypted_length", len(encrypted)).Debug("Data encrypted successfully with Ed25519")
return
}
func (elg Ed25519PublicKey) NewEncrypter() (enc Encrypter, err error) {
log.Debug("Creating new Ed25519 encrypter")
k := createEd25519PublicKey(elg[:])
if k == nil {
return nil, errors.New("invalid public key format")
}
enc, err = createEd25519Encryption(k, rand.Reader)
return
if err != nil {
log.WithError(err).Error("Failed to create Ed25519 encrypter")
return nil, err
}
log.Debug("Ed25519 encrypter created successfully")
return enc, nil
}
func (v *Ed25519Verifier) VerifyHash(h, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"hash_length": len(h),
"sig_length": len(sig),
}).Debug("Verifying Ed25519 signature hash")
if len(sig) != ed25519.SignatureSize {
log.Error("Bad Ed25519 signature size")
err = ErrBadSignatureSize
return
}
if len(v.k) != ed25519.PublicKeySize {
log.Error("Invalid Ed25519 public key size")
err = errors.New("failed to verify: invalid ed25519 public key size")
return
}
ok := ed25519.Verify(v.k, h, sig)
if !ok {
log.Warn("Invalid Ed25519 signature")
err = errors.New("failed to verify: invalid signature")
} else {
log.Debug("Ed25519 signature verified successfully")
}
return
}
func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"sig_length": len(sig),
}).Debug("Verifying Ed25519 signature")
h := sha512.Sum512(data)
err = v.VerifyHash(h[:], sig)
return
@@ -121,12 +184,51 @@ func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
type Ed25519PrivateKey ed25519.PrivateKey
func (k Ed25519PrivateKey) NewDecrypter() (Decrypter, error) {
//TODO implement me
panic("implement me")
}
func (k Ed25519PrivateKey) NewSigner() (Signer, error) {
if len(k) != ed25519.PrivateKeySize {
return nil, errors.New("invalid ed25519 private key size")
}
return &Ed25519Signer{k: k}, nil
}
func (k Ed25519PrivateKey) Len() int {
return len(k)
}
func (k *Ed25519PrivateKey) Generate() (SigningPrivateKey, error) {
// Generate a new Ed25519 key pair
_, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
// Assign the generated private key to the receiver
*k = Ed25519PrivateKey(priv)
return k, nil
}
func (k Ed25519PrivateKey) Public() (SigningPublicKey, error) {
if len(k) != ed25519.PrivateKeySize {
return nil, errors.New("invalid ed25519 private key size")
}
// The public key is the first 32 bytes of the private key's seed
pubKey := k[32:]
return Ed25519PublicKey(pubKey), nil
}
type Ed25519Signer struct {
k []byte
}
func (s *Ed25519Signer) Sign(data []byte) (sig []byte, err error) {
log.WithField("data_length", len(data)).Debug("Signing data with Ed25519")
if len(s.k) != ed25519.PrivateKeySize {
log.Error("Invalid Ed25519 private key size")
err = errors.New("failed to sign: invalid ed25519 private key size")
return
}
@@ -136,6 +238,8 @@ func (s *Ed25519Signer) Sign(data []byte) (sig []byte, err error) {
}
func (s *Ed25519Signer) SignHash(h []byte) (sig []byte, err error) {
log.WithField("hash_length", len(h)).Debug("Signing hash with Ed25519")
sig = ed25519.Sign(s.k, h)
log.WithField("signature_length", len(sig)).Debug("Ed25519 signature created successfully")
return
}

View File

@@ -8,6 +8,8 @@ import (
"io"
"math/big"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/openpgp/elgamal"
)
@@ -42,6 +44,7 @@ var (
// generate an elgamal key pair
func ElgamalGenerate(priv *elgamal.PrivateKey, rand io.Reader) (err error) {
log.Debug("Generating ElGamal key pair")
priv.P = elgp
priv.G = elgg
xBytes := make([]byte, priv.P.BitLen()/8)
@@ -51,6 +54,9 @@ func ElgamalGenerate(priv *elgamal.PrivateKey, rand io.Reader) (err error) {
priv.X = new(big.Int).SetBytes(xBytes)
// compute public key
priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
log.Debug("ElGamal key pair generated successfully")
} else {
log.WithError(err).Error("Failed to generate ElGamal key pair")
}
return
}
@@ -60,12 +66,23 @@ type elgDecrypter struct {
}
func (elg *elgDecrypter) Decrypt(data []byte) (dec []byte, err error) {
log.WithField("data_length", len(data)).Debug("Decrypting ElGamal data")
dec, err = elgamalDecrypt(elg.k, data, true) // TODO(psi): should this be true or false?
if err != nil {
log.WithError(err).Error("Failed to decrypt ElGamal data")
} else {
log.WithField("decrypted_length", len(dec)).Debug("ElGamal data decrypted successfully")
}
return
}
// decrypt an elgamal encrypted message, i2p style
func elgamalDecrypt(priv *elgamal.PrivateKey, data []byte, zeroPadding bool) (decrypted []byte, err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"zero_padding": zeroPadding,
}).Debug("Decrypting ElGamal data")
a := new(big.Int)
b := new(big.Int)
idx := 0
@@ -87,9 +104,11 @@ func elgamalDecrypt(priv *elgamal.PrivateKey, data []byte, zeroPadding bool) (de
if subtle.ConstantTimeCompare(d[:], m[1:33]) == 1 {
// decryption successful
good = 1
log.Debug("ElGamal decryption successful")
} else {
// decrypt failed
err = ElgDecryptFail
log.WithError(err).Error("ElGamal decryption failed")
}
// copy result
decrypted = make([]byte, 222)
@@ -107,10 +126,16 @@ type ElgamalEncryption struct {
}
func (elg *ElgamalEncryption) Encrypt(data []byte) (enc []byte, err error) {
log.WithField("data_length", len(data)).Debug("Encrypting data with ElGamal")
return elg.EncryptPadding(data, true)
}
func (elg *ElgamalEncryption) EncryptPadding(data []byte, zeroPadding bool) (encrypted []byte, err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"zero_padding": zeroPadding,
}).Debug("Encrypting data with ElGamal padding")
if len(data) > 222 {
err = ElgEncryptTooBig
return
@@ -134,23 +159,31 @@ func (elg *ElgamalEncryption) EncryptPadding(data []byte, zeroPadding bool) (enc
copy(encrypted, elg.a.Bytes())
copy(encrypted[256:], b)
}
log.WithField("encrypted_length", len(encrypted)).Debug("Data encrypted successfully with ElGamal")
return
}
// create an elgamal public key from byte slice
func createElgamalPublicKey(data []byte) (k *elgamal.PublicKey) {
log.WithField("data_length", len(data)).Debug("Creating ElGamal public key")
if len(data) == 256 {
k = &elgamal.PublicKey{
G: elgg,
P: elgp,
Y: new(big.Int).SetBytes(data),
}
log.Debug("ElGamal public key created successfully")
} else {
log.Warn("Invalid data length for ElGamal public key")
}
return
}
// create an elgamal private key from byte slice
func createElgamalPrivateKey(data []byte) (k *elgamal.PrivateKey) {
log.WithField("data_length", len(data)).Debug("Creating ElGamal private key")
if len(data) == 256 {
x := new(big.Int).SetBytes(data)
y := new(big.Int).Exp(elgg, x, elgp)
@@ -162,12 +195,16 @@ func createElgamalPrivateKey(data []byte) (k *elgamal.PrivateKey) {
},
X: x,
}
log.Debug("ElGamal private key created successfully")
} else {
log.Warn("Invalid data length for ElGamal private key")
}
return
}
// create a new elgamal encryption session
func createElgamalEncryption(pub *elgamal.PublicKey, rand io.Reader) (enc *ElgamalEncryption, err error) {
log.Debug("Creating ElGamal encryption session")
kbytes := make([]byte, 256)
k := new(big.Int)
for err == nil {
@@ -184,6 +221,9 @@ func createElgamalEncryption(pub *elgamal.PublicKey, rand io.Reader) (enc *Elgam
a: new(big.Int).Exp(pub.G, k, pub.P),
b1: new(big.Int).Exp(pub.Y, k, pub.P),
}
log.Debug("ElGamal encryption session created successfully")
} else {
log.WithError(err).Error("Failed to create ElGamal encryption session")
}
return
}
@@ -197,9 +237,19 @@ func (elg ElgPublicKey) Len() int {
return len(elg)
}
func (elg ElgPublicKey) Bytes() []byte {
return elg[:]
}
func (elg ElgPublicKey) NewEncrypter() (enc Encrypter, err error) {
log.Debug("Creating new ElGamal encrypter")
k := createElgamalPublicKey(elg[:])
enc, err = createElgamalEncryption(k, rand.Reader)
if err != nil {
log.WithError(err).Error("Failed to create ElGamal encrypter")
} else {
log.Debug("ElGamal encrypter created successfully")
}
return
}
@@ -208,8 +258,10 @@ func (elg ElgPrivateKey) Len() int {
}
func (elg ElgPrivateKey) NewDecrypter() (dec Decrypter, err error) {
log.Debug("Creating new ElGamal decrypter")
dec = &elgDecrypter{
k: createElgamalPrivateKey(elg[:]),
}
log.Debug("ElGamal decrypter created successfully")
return
}

View File

@@ -6,7 +6,6 @@ import (
"io"
"testing"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/openpgp/elgamal"
)
@@ -46,7 +45,7 @@ func BenchmarkElgDecrypt(b *testing.B) {
fails++
}
}
log.Infof("%d fails %d rounds", fails, b.N)
log.Debugf("%d fails %d rounds", fails, b.N)
}
func BenchmarkElgEncrypt(b *testing.B) {
@@ -69,7 +68,7 @@ func BenchmarkElgEncrypt(b *testing.B) {
fails++
}
}
log.Infof("%d fails %d rounds", fails, b.N)
log.Debugf("%d fails %d rounds", fails, b.N)
}
func TestElg(t *testing.T) {

View File

@@ -5,12 +5,57 @@ type (
RSA2048PrivateKey [512]byte
)
// Bytes implements SigningPublicKey.
func (r RSA2048PublicKey) Bytes() []byte {
panic("unimplemented")
}
// Len implements SigningPublicKey.
func (r RSA2048PublicKey) Len() int {
panic("unimplemented")
}
// NewVerifier implements SigningPublicKey.
func (r RSA2048PublicKey) NewVerifier() (Verifier, error) {
panic("unimplemented")
}
type (
RSA3072PublicKey [384]byte
RSA3072PrivateKey [786]byte
)
// Bytes implements SigningPublicKey.
func (r RSA3072PublicKey) Bytes() []byte {
panic("unimplemented")
}
// Len implements SigningPublicKey.
func (r RSA3072PublicKey) Len() int {
panic("unimplemented")
}
// NewVerifier implements SigningPublicKey.
func (r RSA3072PublicKey) NewVerifier() (Verifier, error) {
panic("unimplemented")
}
type (
RSA4096PublicKey [512]byte
RSA4096PrivateKey [1024]byte
)
// Bytes implements SigningPublicKey.
func (r RSA4096PublicKey) Bytes() []byte {
panic("unimplemented")
}
// Len implements SigningPublicKey.
func (r RSA4096PublicKey) Len() int {
panic("unimplemented")
}
// NewVerifier implements SigningPublicKey.
func (r RSA4096PublicKey) NewVerifier() (Verifier, error) {
panic("unimplemented")
}

View File

@@ -26,10 +26,11 @@ type SigningPublicKey interface {
NewVerifier() (Verifier, error)
// get the size of this public key
Len() int
Bytes() []byte
}
type PublicKey interface {
Len() int
Bytes() []byte
NewEncrypter() (Encrypter, error)
}

View File

@@ -19,6 +19,7 @@ type Tunnel struct {
}
func NewTunnelCrypto(layerKey, ivKey TunnelKey) (t *Tunnel, err error) {
log.Debug("Creating new Tunnel crypto")
t = new(Tunnel)
t.layerKey, err = aes.NewCipher(layerKey[:])
if err == nil {
@@ -27,24 +28,31 @@ func NewTunnelCrypto(layerKey, ivKey TunnelKey) (t *Tunnel, err error) {
if err != nil {
// error happened we don't need t
// log.WithError(err).Error("Failed to create Tunnel crypto")
t = nil
} else {
log.Debug("Tunnel crypto created successfully")
}
return
}
// encrypt tunnel data in place
func (t *Tunnel) Encrypt(td *TunnelData) {
log.Debug("Encrypting Tunnel data")
data := *td
t.ivKey.Encrypt(data[16:1024], data[16:1024])
layerBlock := cipher.NewCBCEncrypter(t.layerKey, data[:16])
layerBlock.CryptBlocks(data[16:1024], data[16:1024])
t.ivKey.Encrypt(data[16:1024], data[16:1024])
log.Debug("Tunnel data encrypted successfully")
}
func (t *Tunnel) Decrypt(td *TunnelData) {
log.Debug("Decrypting Tunnel data")
data := *td
t.ivKey.Decrypt(data[16:1024], data[16:1024])
layerBlock := cipher.NewCBCDecrypter(t.layerKey, data[:16])
layerBlock.CryptBlocks(data[16:1024], data[16:1024])
t.ivKey.Decrypt(data[16:1024], data[16:1024])
log.Debug("Tunnel data decrypted successfully")
}

View File

@@ -4,12 +4,16 @@ import (
"errors"
"time"
"github.com/sirupsen/logrus"
common "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/session_key"
"github.com/go-i2p/go-i2p/lib/tunnel"
log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/util/logger"
)
var log = logger.GetGoI2PLogger()
/*
I2P I2NP BuildRequestRecord
https://geti2p.net/spec/i2np
@@ -172,80 +176,94 @@ type BuildRequestRecord struct {
var ERR_BUILD_REQUEST_RECORD_NOT_ENOUGH_DATA = errors.New("not enough i2np build request record data")
func ReadBuildRequestRecord(data []byte) (BuildRequestRecord, error) {
log.Debug("Reading BuildRequestRecord")
build_request_record := BuildRequestRecord{}
receive_tunnel, err := readBuildRequestRecordReceiveTunnel(data)
if err != nil {
log.WithError(err).Error("Failed to read ReceiveTunnel")
return build_request_record, err
}
build_request_record.ReceiveTunnel = receive_tunnel
our_ident, err := readBuildRequestRecordOurIdent(data)
if err != nil {
log.WithError(err).Error("Failed to read OurIdent")
return build_request_record, err
}
build_request_record.OurIdent = our_ident
next_tunnel, err := readBuildRequestRecordNextTunnel(data)
if err != nil {
log.WithError(err).Error("Failed to read NextTunnel")
return build_request_record, err
}
build_request_record.NextTunnel = next_tunnel
next_ident, err := readBuildRequestRecordNextIdent(data)
if err != nil {
log.WithError(err).Error("Failed to read NextIdent")
return build_request_record, err
}
build_request_record.NextIdent = next_ident
layer_key, err := readBuildRequestRecordLayerKey(data)
if err != nil {
log.WithError(err).Error("Failed to read LayerKey")
return build_request_record, err
}
build_request_record.LayerKey = layer_key
iv_key, err := readBuildRequestRecordIVKey(data)
if err != nil {
log.WithError(err).Error("Failed to read IVKey")
return build_request_record, err
}
build_request_record.IVKey = iv_key
reply_key, err := readBuildRequestRecordReplyKey(data)
if err != nil {
log.WithError(err).Error("Failed to read ReplyKey")
return build_request_record, err
}
build_request_record.ReplyKey = reply_key
reply_iv, err := readBuildRequestRecordReplyIV(data)
if err != nil {
log.WithError(err).Error("Failed to read ReplyIV")
return build_request_record, err
}
build_request_record.ReplyIV = reply_iv
flag, err := readBuildRequestRecordFlag(data)
if err != nil {
log.WithError(err).Error("Failed to read Flag")
return build_request_record, err
}
build_request_record.Flag = flag
request_time, err := readBuildRequestRecordRequestTime(data)
if err != nil {
log.WithError(err).Error("Failed to read RequestTime")
return build_request_record, err
}
build_request_record.RequestTime = request_time
send_message_id, err := readBuildRequestRecordSendMessageID(data)
if err != nil {
log.WithError(err).Error("Failed to read SendMessageID")
return build_request_record, err
}
build_request_record.SendMessageID = send_message_id
padding, err := readBuildRequestRecordPadding(data)
if err != nil {
log.WithError(err).Error("Failed to read Padding")
return build_request_record, err
}
build_request_record.Padding = padding
log.Debug("BuildRequestRecord read successfully")
return build_request_record, nil
}
@@ -258,7 +276,7 @@ func readBuildRequestRecordReceiveTunnel(data []byte) (tunnel.TunnelID, error) {
common.Integer(data[0:4]).Int(),
)
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordReceiveTunnel",
"receieve_tunnel": receive_tunnel,
}).Debug("parsed_build_request_record_receive_tunnel")
@@ -273,7 +291,7 @@ func readBuildRequestRecordOurIdent(data []byte) (common.Hash, error) {
hash := common.Hash{}
copy(hash[:], data[4:36])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordOurIdent",
}).Debug("parsed_build_request_record_our_ident")
return hash, nil
@@ -288,7 +306,7 @@ func readBuildRequestRecordNextTunnel(data []byte) (tunnel.TunnelID, error) {
common.Integer(data[36:40]).Int(),
)
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordNextTunnel",
"next_tunnel": next_tunnel,
}).Debug("parsed_build_request_record_next_tunnel")
@@ -303,7 +321,7 @@ func readBuildRequestRecordNextIdent(data []byte) (common.Hash, error) {
hash := common.Hash{}
copy(hash[:], data[40:72])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordNextIdent",
}).Debug("parsed_build_request_record_next_ident")
return hash, nil
@@ -317,7 +335,7 @@ func readBuildRequestRecordLayerKey(data []byte) (session_key.SessionKey, error)
session_key := session_key.SessionKey{}
copy(session_key[:], data[72:104])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordLayerKey",
}).Debug("parsed_build_request_record_layer_key")
return session_key, nil
@@ -331,7 +349,7 @@ func readBuildRequestRecordIVKey(data []byte) (session_key.SessionKey, error) {
session_key := session_key.SessionKey{}
copy(session_key[:], data[104:136])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordIVKey",
}).Debug("parsed_build_request_record_iv_key")
return session_key, nil
@@ -345,7 +363,7 @@ func readBuildRequestRecordReplyKey(data []byte) (session_key.SessionKey, error)
session_key := session_key.SessionKey{}
copy(session_key[:], data[136:168])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordReplyKey",
}).Debug("parsed_build_request_record_reply_key")
return session_key, nil
@@ -359,7 +377,7 @@ func readBuildRequestRecordReplyIV(data []byte) ([16]byte, error) {
iv := [16]byte{}
copy(iv[:], data[168:184])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordReplyIV",
}).Debug("parsed_build_request_record_reply_iv")
return iv, nil
@@ -372,7 +390,7 @@ func readBuildRequestRecordFlag(data []byte) (int, error) {
flag := common.Integer([]byte{data[185]}).Int()
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordFlag",
"flag": flag,
}).Debug("parsed_build_request_record_flag")
@@ -387,7 +405,7 @@ func readBuildRequestRecordRequestTime(data []byte) (time.Time, error) {
count := common.Integer(data[185:189]).Int()
rtime := time.Unix(0, 0).Add(time.Duration(count) * time.Hour)
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordRequestTime",
}).Debug("parsed_build_request_record_request_time")
return rtime, nil
@@ -400,7 +418,7 @@ func readBuildRequestRecordSendMessageID(data []byte) (int, error) {
send_message_id := common.Integer(data[189:193]).Int()
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordSendMessageID",
}).Debug("parsed_build_request_record_send_message_id")
return send_message_id, nil
@@ -414,7 +432,7 @@ func readBuildRequestRecordPadding(data []byte) ([29]byte, error) {
padding := [29]byte{}
copy(padding[:], data[193:222])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.readBuildRequestRecordPadding",
}).Debug("parsed_build_request_record_padding")
return padding, nil

View File

@@ -4,9 +4,9 @@ import (
"errors"
"time"
datalib "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
datalib "github.com/go-i2p/go-i2p/lib/common/data"
)
/*
@@ -95,10 +95,12 @@ var ERR_I2NP_NOT_ENOUGH_DATA = errors.New("not enough i2np header data")
// Read an entire I2NP message and return the parsed header
// with embedded encrypted data
func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
log.Debug("Reading I2NP NTCP Header")
header := I2NPNTCPHeader{}
message_type, err := ReadI2NPType(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP type")
return header, err
} else {
header.Type = message_type
@@ -106,6 +108,7 @@ func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
message_id, err := ReadI2NPNTCPMessageID(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP NTCP message ID")
return header, err
} else {
header.MessageID = message_id
@@ -113,6 +116,7 @@ func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
message_date, err := ReadI2NPNTCPMessageExpiration(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP NTCP message expiration")
return header, err
} else {
header.Expiration = message_date.Time()
@@ -120,6 +124,7 @@ func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
message_size, err := ReadI2NPNTCPMessageSize(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP NTCP message size")
return header, err
} else {
header.Size = message_size
@@ -127,6 +132,7 @@ func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
message_checksum, err := ReadI2NPNTCPMessageChecksum(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP NTCP message checksum")
return header, err
} else {
header.Checksum = message_checksum
@@ -134,12 +140,13 @@ func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
message_data, err := ReadI2NPNTCPData(data, header.Size)
if err != nil {
log.WithError(err).Error("Failed to read I2NP NTCP message data")
return header, err
} else {
header.Data = message_data
}
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPNTCPHeader",
}).Debug("parsed_i2np_ntcp_header")
return header, nil
@@ -150,6 +157,7 @@ func ReadI2NPSSUHeader(data []byte) (I2NPSSUHeader, error) {
message_type, err := ReadI2NPType(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP type")
return header, err
} else {
header.Type = message_type
@@ -157,11 +165,14 @@ func ReadI2NPSSUHeader(data []byte) (I2NPSSUHeader, error) {
message_date, err := ReadI2NPSSUMessageExpiration(data)
if err != nil {
log.WithError(err).Error("Failed to read I2NP SSU message expiration")
return header, err
} else {
header.Expiration = message_date.Time()
}
log.WithFields(logrus.Fields{
"type": header.Type,
}).Debug("Parsed I2NP SSU header")
return header, nil
}
@@ -174,27 +185,27 @@ func ReadI2NPType(data []byte) (int, error) {
if (message_type.Int() >= 4 || message_type.Int() <= 9) ||
(message_type.Int() >= 12 || message_type.Int() <= 17) {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPType",
"type": message_type,
}).Warn("unknown_i2np_type")
}
if message_type.Int() >= 224 || message_type.Int() <= 254 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPType",
"type": message_type,
}).Warn("experimental_i2np_type")
}
if message_type.Int() == 255 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPType",
"type": message_type,
}).Warn("reserved_i2np_type")
}
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPType",
"type": message_type,
}).Debug("parsed_i2np_type")
@@ -208,7 +219,7 @@ func ReadI2NPNTCPMessageID(data []byte) (int, error) {
message_id := datalib.Integer(data[1:5])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPNTCPMessageID",
"type": message_id,
}).Debug("parsed_i2np_message_id")
@@ -223,7 +234,7 @@ func ReadI2NPNTCPMessageExpiration(data []byte) (datalib.Date, error) {
date := datalib.Date{}
copy(date[:], data[5:13])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPNTCPMessageExpiration",
"date": date,
}).Debug("parsed_i2np_message_date")
@@ -238,7 +249,7 @@ func ReadI2NPSSUMessageExpiration(data []byte) (datalib.Date, error) {
date := datalib.Date{}
copy(date[4:], data[1:5])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPSSUMessageExpiration",
"date": date,
}).Debug("parsed_i2np_message_date")
@@ -252,7 +263,7 @@ func ReadI2NPNTCPMessageSize(data []byte) (int, error) {
size := datalib.Integer(data[13:15])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPNTCPMessageSize",
"size": size,
}).Debug("parsed_i2np_message_size")
@@ -266,7 +277,7 @@ func ReadI2NPNTCPMessageChecksum(data []byte) (int, error) {
checksum := datalib.Integer(data[15:16])
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "i2np.ReadI2NPNTCPMessageCHecksum",
"checksum": checksum,
}).Debug("parsed_i2np_message_checksum")
@@ -277,6 +288,6 @@ func ReadI2NPNTCPData(data []byte, size int) ([]byte, error) {
if len(data) < 16+size {
return []byte{}, ERR_I2NP_NOT_ENOUGH_DATA
}
log.WithField("data_size", size).Debug("Read I2NP NTCP message data")
return data[16 : 16+size], nil
}

View File

@@ -3,19 +3,23 @@ package reseed
import (
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
"github.com/eyedeekay/go-unzip/pkg/unzip"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/config"
"github.com/go-i2p/go-i2p/lib/su3"
)
var log = logger.GetGoI2PLogger()
const (
I2pUserAgent = "Wget/1.11.4"
)
@@ -25,6 +29,8 @@ type Reseed struct {
}
func (r Reseed) SingleReseed(uri string) ([]router_info.RouterInfo, error) {
log.WithField("uri", uri).Debug("Starting single reseed operation")
transport := http.Transport{
DialContext: r.DialContext,
}
@@ -33,6 +39,7 @@ func (r Reseed) SingleReseed(uri string) ([]router_info.RouterInfo, error) {
}
URL, err := url.Parse(uri)
if err != nil {
log.WithError(err).Error("Failed to parse reseed URI")
return nil, err
}
header := http.Header{}
@@ -43,52 +50,76 @@ func (r Reseed) SingleReseed(uri string) ([]router_info.RouterInfo, error) {
}
response, err := client.Do(&request)
if err != nil {
log.WithError(err).Error("Failed to perform HTTP request")
return nil, err
}
log.Debug("Successfully received response from reseed server")
su3file, err := su3.Read(response.Body)
if err != nil {
log.WithError(err).Error("Failed to read SU3 file")
return nil, err
}
log.WithFields(logrus.Fields{
"file_type": su3file.FileType,
"content_type": su3file.ContentType,
}).Debug("Successfully read SU3 file")
if su3file.FileType == su3.ZIP {
if su3file.ContentType == su3.RESEED {
content, err := io.ReadAll(su3file.Content(""))
if err == nil {
content, err := io.ReadAll(su3file.Content(""))
if err == nil {
signature, err := io.ReadAll(su3file.Signature())
if err != nil {
return nil, err
}
log.Println("warning: this doesn't validate the signature yet", signature)
}
zip := filepath.Join(config.RouterConfigProperties.NetDb.Path, "reseed.zip")
err = os.WriteFile(zip, content, 0o644)
signature, err := io.ReadAll(su3file.Signature())
if err != nil {
log.WithError(err).Error("Failed to read SU3 file signature")
return nil, err
}
// content is a zip file, unzip it and get the files
files, err := unzip.New().Extract(zip, config.RouterConfigProperties.NetDb.Path)
if err != nil {
return nil, err
}
if len(files) <= 0 {
return nil, fmt.Errorf("error: reseed appears to have no content")
}
var ris []router_info.RouterInfo
for _, f := range files {
riB, err := os.ReadFile(f)
if err != nil {
continue
}
ri, _, err := router_info.ReadRouterInfo(riB)
if err != nil {
continue
}
ris = append(ris, ri)
}
err = os.Remove(zip)
return ris, err
log.Println("warning: this doesn't validate the signature yet", signature)
log.Warn("Doesn't validate the signature yet", logrus.Fields{"signature": signature})
}
zip := filepath.Join(config.RouterConfigProperties.NetDb.Path, "reseed.zip")
err = os.WriteFile(zip, content, 0o644)
if err != nil {
log.WithError(err).Error("Failed to write reseed zip file")
return nil, err
}
// content is a zip file, unzip it and get the files
files, err := unzip.New().Extract(zip, config.RouterConfigProperties.NetDb.Path)
if err != nil {
log.WithError(err).Error("Failed to extract reseed zip file")
return nil, err
}
if len(files) <= 0 {
log.Error("Reseed appears to have no content")
return nil, fmt.Errorf("error: reseed appears to have no content")
}
log.WithField("file_count", len(files)).Debug("Successfully extracted reseed files")
var ris []router_info.RouterInfo
for _, f := range files {
riB, err := os.ReadFile(f)
if err != nil {
log.WithError(err).WithField("file", f).Warn("Failed to read router info file")
continue
}
ri, _, err := router_info.ReadRouterInfo(riB)
if err != nil {
log.WithError(err).WithField("file", f).Warn("Failed to parse router info")
continue
}
ris = append(ris, ri)
}
err = os.Remove(zip)
if err != nil {
log.WithError(err).Warn("Failed to remove reseed zip file")
}
log.WithField("router_info_count", len(ris)).Debug("Successfully processed reseed data")
return ris, err
}
}
log.Error("Undefined reseed error")
return nil, fmt.Errorf("error: undefined reseed error")
}

View File

@@ -4,21 +4,24 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/bootstrap"
"github.com/go-i2p/go-i2p/lib/common/base32"
"github.com/go-i2p/go-i2p/lib/common/base64"
common "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/util"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// standard network database implementation using local filesystem skiplist
type StdNetDB struct {
DB string
@@ -27,6 +30,7 @@ type StdNetDB struct {
}
func NewStdNetDB(db string) StdNetDB {
log.WithField("db_path", db).Debug("Creating new StdNetDB")
return StdNetDB{
DB: db,
RouterInfos: make(map[common.Hash]Entry),
@@ -35,16 +39,20 @@ func NewStdNetDB(db string) StdNetDB {
}
func (db *StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan router_info.RouterInfo) {
log.WithField("hash", hash).Debug("Getting RouterInfo")
if ri, ok := db.RouterInfos[hash]; ok {
log.Debug("RouterInfo found in memory cache")
chnl <- *ri.RouterInfo
return
}
fname := db.SkiplistFile(hash)
buff := new(bytes.Buffer)
if f, err := os.Open(fname); err != nil {
log.WithError(err).Error("Failed to open RouterInfo file")
return nil
} else {
if _, err := io.Copy(buff, f); err != nil {
log.WithError(err).Error("Failed to read RouterInfo file")
return nil
}
defer f.Close()
@@ -53,11 +61,14 @@ func (db *StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan router_info.Route
ri, _, err := router_info.ReadRouterInfo(buff.Bytes())
if err == nil {
if _, ok := db.RouterInfos[hash]; !ok {
log.Debug("Adding RouterInfo to memory cache")
db.RouterInfos[hash] = Entry{
RouterInfo: &ri,
}
}
chnl <- ri
} else {
log.WithError(err).Error("Failed to parse RouterInfo")
}
return
}
@@ -66,6 +77,7 @@ func (db *StdNetDB) GetRouterInfo(hash common.Hash) (chnl chan router_info.Route
func (db *StdNetDB) SkiplistFile(hash common.Hash) (fpath string) {
fname := base64.EncodeToString(hash[:])
fpath = filepath.Join(db.Path(), fmt.Sprintf("r%c", fname[0]), fmt.Sprintf("routerInfo-%s.dat", fname))
log.WithField("file_path", fpath).Debug("Generated skiplist file path")
return
}
@@ -77,19 +89,27 @@ func (db *StdNetDB) Path() string {
// return how many routers we know about in our network database
func (db *StdNetDB) Size() (routers int) {
// TODO: implement this
log.Debug("Calculating NetDB size")
var err error
var data []byte
if !util.CheckFileExists(db.cacheFilePath()) || util.CheckFileAge(db.cacheFilePath(), 2) || len(db.RouterInfos) == 0 {
// regenerate
log.Debug("Recalculating NetDB size")
err = db.RecalculateSize()
if err != nil {
// TODO : what now? let's panic for now
util.Panicf("could not recalculate netdb size: %s", err)
// util.Panicf("could not recalculate netdb size: %s", err)
log.WithError(err).Panic("Failed to recalculate NetDB size")
}
}
data, err = ioutil.ReadFile(db.cacheFilePath())
data, err = os.ReadFile(db.cacheFilePath())
if err == nil {
routers, err = strconv.Atoi(string(data))
if err != nil {
log.WithError(err).Error("Failed to parse NetDB size from cache")
}
} else {
log.WithError(err).Error("Failed to read NetDB size cache file")
}
return
}
@@ -104,61 +124,83 @@ func (db *StdNetDB) cacheFilePath() string {
func (db *StdNetDB) CheckFilePathValid(fpath string) bool {
// TODO: make this better
return strings.HasSuffix(fpath, ".dat")
// return strings.HasSuffix(fpath, ".dat")
isValid := strings.HasSuffix(fpath, ".dat")
log.WithFields(logrus.Fields{
"file_path": fpath,
"is_valid": isValid,
}).Debug("Checking file path validity")
return isValid
}
// recalculateSize recalculates cached size of netdb
func (db *StdNetDB) RecalculateSize() (err error) {
log.Debug("Recalculating NetDB size")
count := 0
err = filepath.Walk(db.Path(), func(fname string, info os.FileInfo, err error) error {
if info.IsDir() {
if !strings.HasPrefix(fname, db.Path()) {
if db.Path() == fname {
log.Info("path==name time to exit")
log.Debug("Reached end of NetDB directory")
log.Debug("path==name time to exit")
return nil
}
log.Info("Outside of netDb dir time to exit", db.Path(), " ", fname)
log.Debug("Outside of netDb dir time to exit", db.Path(), " ", fname)
return err
}
return err
}
if db.CheckFilePathValid(fname) {
log.WithField("file_name", fname).Debug("Reading RouterInfo file")
log.Println("Reading in file:", fname)
b, err := os.ReadFile(fname)
if err != nil {
log.WithError(err).Error("Failed to read RouterInfo file")
return err
}
ri, _, err := router_info.ReadRouterInfo(b)
if err != nil {
log.WithError(err).Error("Failed to parse RouterInfo")
return err
}
ih := ri.IdentHash().Bytes()
log.WithError(err).Error("Failed to parse RouterInfo")
log.Printf("Read in IdentHash: %s", base32.EncodeToString(ih[:]))
for _, addr := range ri.RouterAddresses() {
log.Println(string(addr.Bytes()))
log.WithField("address", string(addr.Bytes())).Debug("RouterInfo address")
}
if ent, ok := db.RouterInfos[ih]; !ok {
log.Debug("Adding new RouterInfo to memory cache")
db.RouterInfos[ri.IdentHash()] = Entry{
RouterInfo: &ri,
}
} else {
log.Debug("RouterInfo already in memory cache")
log.Println("entry previously found in table", ent, fname)
}
ri = router_info.RouterInfo{}
count++
} else {
log.WithField("file_path", fname).Warn("Invalid file path")
log.Println("Invalid path error")
}
return err
})
if err == nil {
log.WithField("count", count).Debug("Finished recalculating NetDB size")
str := fmt.Sprintf("%d", count)
var f *os.File
f, err = os.OpenFile(db.cacheFilePath(), os.O_CREATE|os.O_WRONLY, 0o600)
if err == nil {
_, err = io.WriteString(f, str)
f.Close()
log.Debug("Updated NetDB size cache file")
} else {
log.WithError(err).Error("Failed to update NetDB size cache file")
}
} else {
log.WithError(err).Error("Failed to update NetDB size cache file")
}
return
}
@@ -182,24 +224,35 @@ func (db *StdNetDB) Exists() bool {
func (db *StdNetDB) SaveEntry(e *Entry) (err error) {
var f io.WriteCloser
h := e.RouterInfo.IdentHash()
log.WithField("hash", h).Debug("Saving NetDB entry")
// if err == nil {
f, err = os.OpenFile(db.SkiplistFile(h), os.O_WRONLY|os.O_CREATE, 0o700)
if err == nil {
err = e.WriteTo(f)
f.Close()
if err == nil {
log.Debug("Successfully saved NetDB entry")
} else {
log.WithError(err).Error("Failed to write NetDB entry")
}
} else {
log.WithError(err).Error("Failed to open file for saving NetDB entry")
}
//}
if err != nil {
log.Errorf("failed to save netdb entry: %s", err.Error())
}
/*
if err != nil {
log.Errorf("failed to save netdb entry: %s", err.Error())
}
*/
return
}
func (db *StdNetDB) Save() (err error) {
log.Debug("Saving all NetDB entries")
for _, dbe := range db.RouterInfos {
if e := db.SaveEntry(&dbe); e != nil {
err = e
// TODO: log this
log.WithError(e).Error("Failed to save NetDB entry")
}
}
return
@@ -208,16 +261,22 @@ func (db *StdNetDB) Save() (err error) {
// reseed if we have less than minRouters known routers
// returns error if reseed failed
func (db *StdNetDB) Reseed(b bootstrap.Bootstrap, minRouters int) (err error) {
log.WithField("min_routers", minRouters).Debug("Checking if reseed is necessary")
if db.Size() > minRouters {
log.Debug("Reseed not necessary")
return nil
}
log.Warn("NetDB size below minimum, reseed required")
return
}
// ensure that the network database exists
func (db *StdNetDB) Ensure() (err error) {
if !db.Exists() {
log.Debug("NetDB directory does not exist, creating it")
err = db.Create()
} else {
log.Debug("NetDB directory already exists")
}
return
}
@@ -226,18 +285,20 @@ func (db *StdNetDB) Ensure() (err error) {
func (db *StdNetDB) Create() (err error) {
mode := os.FileMode(0o700)
p := db.Path()
log.Infof("Create network database in %s", p)
log.WithField("path", p).Debug("Creating network database directory")
// create root for skiplist
err = os.Mkdir(p, mode)
err = os.MkdirAll(p, mode)
if err == nil {
// create all subdirectories for skiplist
for _, c := range base64.I2PEncodeAlphabet {
err = os.Mkdir(filepath.Join(p, fmt.Sprintf("r%c", c)), mode)
err = os.MkdirAll(filepath.Join(p, fmt.Sprintf("r%c", c)), mode)
if err != nil {
log.WithError(err).Error("Failed to create subdirectory")
return
}
}
} else {
log.WithError(err).Error("Failed to create root network database directory")
}
return
}

View File

@@ -3,11 +3,15 @@ package router
import (
"time"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/config"
"github.com/go-i2p/go-i2p/lib/netdb"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// i2p router type
type Router struct {
cfg *config.RouterConfig
@@ -16,75 +20,94 @@ type Router struct {
running bool
}
// create router with default configuration
func CreateRouter() (r *Router, err error) {
cfg := config.RouterConfigProperties
r, err = FromConfig(cfg)
return
// CreateRouter creates a router with the provided configuration
func CreateRouter(cfg *config.RouterConfig) (*Router, error) {
log.Debug("Creating router with provided configuration")
r, err := FromConfig(cfg)
if err != nil {
log.WithError(err).Error("Failed to create router from configuration")
} else {
log.Debug("Router created successfully with provided configuration")
}
return r, err
}
// create router from configuration
func FromConfig(c *config.RouterConfig) (r *Router, err error) {
log.WithField("config", c).Debug("Creating router from configuration")
r = new(Router)
r.cfg = c
r.closeChnl = make(chan bool)
log.Debug("Router created successfully from configuration")
return
}
// Wait blocks until router is fully stopped
func (r *Router) Wait() {
log.Debug("Waiting for router to stop")
<-r.closeChnl
log.Debug("Router has stopped")
}
// Stop starts stopping internal state of router
func (r *Router) Stop() {
log.Debug("Stopping router")
r.closeChnl <- true
r.running = false
log.Debug("Router stop signal sent")
}
// Close closes any internal state and finallizes router resources so that nothing can start up again
func (r *Router) Close() error {
log.Warn("Closing router not implemented(?)")
return nil
}
// Start starts router mainloop
func (r *Router) Start() {
if r.running {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Router) Start",
"reason": "router is already running",
}).Error("Error Starting router")
return
}
log.Debug("Starting router")
r.running = true
go r.mainloop()
}
// run i2p router mainloop
func (r *Router) mainloop() {
log.Debug("Entering router mainloop")
r.ndb = netdb.NewStdNetDB(r.cfg.NetDb.Path)
log.WithField("netdb_path", r.cfg.NetDb.Path).Debug("Created StdNetDB")
// make sure the netdb is ready
var e error
if err := r.ndb.Ensure(); err != nil {
e = err
log.WithError(err).Error("Failed to ensure NetDB")
}
if sz := r.ndb.Size(); sz >= 0 {
log.Info("NetDB Size:", sz)
log.WithField("size", sz).Debug("NetDB Size")
} else {
log.Warn("Unable to determine NetDB size")
}
if e == nil {
// netdb ready
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Router) mainloop",
}).Info("Router ready")
}).Debug("Router ready")
for e == nil {
time.Sleep(time.Second)
}
} else {
// netdb failed
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Router) mainloop",
"reason": e.Error(),
}).Error("Netdb Startup failed")
r.Stop()
}
log.Debug("Exiting router mainloop")
}

View File

@@ -78,8 +78,13 @@ import (
"io/ioutil"
"strings"
"sync"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
type SignatureType string
const (
@@ -190,11 +195,13 @@ type SU3 struct {
}
func (su3 *SU3) Content(publicKey interface{}) io.Reader {
log.WithField("signer_id", su3.SignerID).Debug("Accessing SU3 content")
su3.publicKey = publicKey
return su3.contentReader
}
func (su3 *SU3) Signature() io.Reader {
log.Debug("Accessing SU3 signature")
return su3.signatureReader
}
@@ -202,45 +209,57 @@ func Read(reader io.Reader) (su3 *SU3, err error) {
// We will buffer everything up to the content, so that once we know
// the hash type being used for the signature, we can write these bytes
// into the hash.
log.Debug("Starting to read SU3 file")
var buff bytes.Buffer
// Magic bytes.
mbytes := make([]byte, len(magicBytes))
l, err := reader.Read(mbytes)
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read magic bytes")
return nil, fmt.Errorf("reading magic bytes: %w", err)
}
if l != len(mbytes) {
log.Error("Missing magic bytes")
return nil, ErrMissingMagicBytes
}
if string(mbytes) != magicBytes {
log.Error("Invalid magic bytes")
return nil, ErrMissingMagicBytes
}
buff.Write(mbytes)
log.Debug("Magic bytes verified")
// Unused byte 6.
unused := [1]byte{}
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused byte 6")
return nil, fmt.Errorf("reading unused byte 6: %w", err)
}
if l != 1 {
log.Error("Missing unused byte 6")
return nil, ErrMissingUnusedByte6
}
buff.Write(unused[:])
log.Debug("Read unused byte 6")
// SU3 file format version (always 0).
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read SU3 file format version")
return nil, fmt.Errorf("reading SU3 file format version: %w", err)
}
if l != 1 {
log.Error("Missing SU3 file format version")
return nil, ErrMissingFileFormatVersion
}
if unused[0] != 0x00 {
log.Error("Invalid SU3 file format version")
return nil, ErrMissingFileFormatVersion
}
buff.Write(unused[:])
log.Debug("SU3 file format version verified")
su3 = &SU3{
mut: sync.Mutex{},
@@ -251,200 +270,258 @@ func Read(reader io.Reader) (su3 *SU3, err error) {
sigTypeBytes := [2]byte{}
l, err = reader.Read(sigTypeBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read signature type")
return nil, fmt.Errorf("reading signature type: %w", err)
}
if l != 2 {
log.Error("Missing signature type")
return nil, ErrMissingSignatureType
}
sigType, ok := sigTypes[sigTypeBytes]
if !ok {
log.WithField("signature_type", sigTypeBytes).Error("Unsupported signature type")
return nil, ErrUnsupportedSignatureType
}
su3.SignatureType = sigType
buff.Write(sigTypeBytes[:])
log.WithField("signature_type", sigType).Debug("Signature type read")
// Signature length.
sigLengthBytes := [2]byte{}
l, err = reader.Read(sigLengthBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read signature length")
return nil, fmt.Errorf("reading signature length: %w", err)
}
if l != 2 {
log.Error("Missing signature length")
return nil, ErrMissingSignatureLength
}
sigLen := binary.BigEndian.Uint16(sigLengthBytes[:])
// TODO check that sigLen is the correct length for sigType.
su3.SignatureLength = sigLen
buff.Write(sigLengthBytes[:])
log.WithField("signature_length", sigLen).Debug("Signature length read")
// Unused byte 12.
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused byte 12")
return nil, fmt.Errorf("reading unused byte 12: %w", err)
}
if l != 1 {
log.Error("Missing unused byte 12")
return nil, ErrMissingUnusedByte12
}
buff.Write(unused[:])
log.Debug("Read unused byte 12")
// Version length.
verLengthBytes := [1]byte{}
l, err = reader.Read(verLengthBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read version length")
return nil, fmt.Errorf("reading version length: %w", err)
}
if l != 1 {
log.Error("Missing version length")
return nil, ErrMissingVersionLength
}
verLen := binary.BigEndian.Uint16([]byte{0x00, verLengthBytes[0]})
if verLen < 16 {
log.WithField("version_length", verLen).Error("Version length too short")
return nil, ErrVersionTooShort
}
buff.Write(verLengthBytes[:])
log.WithField("version_length", verLen).Debug("Version length read")
// Unused byte 14.
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused byte 14")
return nil, fmt.Errorf("reading unused byte 14: %w", err)
}
if l != 1 {
log.Error("Missing unused byte 14")
return nil, ErrMissingUnusedByte14
}
buff.Write(unused[:])
log.Debug("Read unused byte 14")
// Signer ID length.
sigIDLengthBytes := [1]byte{}
l, err = reader.Read(sigIDLengthBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read signer ID length")
return nil, fmt.Errorf("reading signer id length: %w", err)
}
if l != 1 {
log.Error("Missing signer ID length")
return nil, ErrMissingSignerIDLength
}
signIDLen := binary.BigEndian.Uint16([]byte{0x00, sigIDLengthBytes[0]})
buff.Write(sigIDLengthBytes[:])
log.WithField("signer_id_length", signIDLen).Debug("Signer ID length read")
// Content length.
contentLengthBytes := [8]byte{}
l, err = reader.Read(contentLengthBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read content length")
return nil, fmt.Errorf("reading content length: %w", err)
}
if l != 8 {
log.Error("Missing content length")
return nil, ErrMissingContentLength
}
contentLen := binary.BigEndian.Uint64(contentLengthBytes[:])
su3.ContentLength = contentLen
buff.Write(contentLengthBytes[:])
log.WithField("content_length", contentLen).Debug("Content length read")
// Unused byte 24.
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused byte 24")
return nil, fmt.Errorf("reading unused byte 24: %w", err)
}
if l != 1 {
log.Error("Missing unused byte 24")
return nil, ErrMissingUnusedByte24
}
buff.Write(unused[:])
log.Debug("Read unused byte 24")
// File type.
fileTypeBytes := [1]byte{}
l, err = reader.Read(fileTypeBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read file type")
return nil, fmt.Errorf("reading file type: %w", err)
}
if l != 1 {
log.Error("Missing file type")
return nil, ErrMissingFileType
}
fileType, ok := fileTypes[fileTypeBytes[0]]
if !ok {
log.WithField("file_type_byte", fileTypeBytes[0]).Error("Invalid file type")
return nil, ErrMissingFileType
}
su3.FileType = fileType
buff.Write(fileTypeBytes[:])
log.WithField("file_type", fileType).Debug("File type read")
// Unused byte 26.
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused byte 26")
return nil, fmt.Errorf("reading unused byte 26: %w", err)
}
if l != 1 {
log.Error("Missing unused byte 26")
return nil, ErrMissingUnusedByte26
}
buff.Write(unused[:])
log.Debug("Read unused byte 26")
// Content type.
contentTypeBytes := [1]byte{}
l, err = reader.Read(contentTypeBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read content type")
return nil, fmt.Errorf("reading content type: %w", err)
}
if l != 1 {
log.Error("Missing content type")
return nil, ErrMissingContentType
}
contentType, ok := contentTypes[contentTypeBytes[0]]
if !ok {
log.WithField("content_type_byte", contentTypeBytes[0]).Error("Invalid content type")
return nil, ErrMissingContentType
}
su3.ContentType = contentType
buff.Write(contentTypeBytes[:])
log.WithField("content_type", contentType).Debug("Content type read")
// Unused bytes 28-39.
for i := 0; i < 12; i++ {
l, err = reader.Read(unused[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read unused bytes 28-39")
return nil, fmt.Errorf("reading unused bytes 28-39: %w", err)
}
if l != 1 {
log.WithField("byte_number", 28+i).Error("Missing unused byte")
return nil, ErrMissingUnusedBytes28To39
}
buff.Write(unused[:])
}
log.Debug("Read unused bytes 28-39")
// Version.
versionBytes := make([]byte, verLen)
l, err = reader.Read(versionBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.Debug("Read unused bytes 28-39")
return nil, fmt.Errorf("reading version: %w", err)
}
if l != int(verLen) {
log.Error("Missing version")
return nil, ErrMissingVersion
}
version := strings.TrimRight(string(versionBytes), "\x00")
su3.Version = version
buff.Write(versionBytes[:])
log.WithField("version", version).Debug("Version read")
// Signer ID.
signerIDBytes := make([]byte, signIDLen)
l, err = reader.Read(signerIDBytes[:])
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Failed to read signer ID")
return nil, fmt.Errorf("reading signer id: %w", err)
}
if l != int(signIDLen) {
log.Error("Missing signer ID")
return nil, ErrMissingSignerID
}
signerID := string(signerIDBytes)
su3.SignerID = signerID
buff.Write(signerIDBytes[:])
log.WithField("signer_id", signerID).Debug("Signer ID read")
su3.contentReader = &contentReader{
su3: su3,
}
log.Debug("Content reader initialized")
switch sigType {
case RSA_SHA256_2048:
su3.contentReader.hash = sha256.New()
log.Debug("Using SHA256 hash for content")
case RSA_SHA512_4096:
su3.contentReader.hash = sha512.New()
log.Debug("Using SHA512 hash for content")
}
if su3.contentReader.hash != nil {
su3.contentReader.hash.Write(buff.Bytes())
log.Debug("Wrote header to content hash")
}
su3.signatureReader = &signatureReader{
su3: su3,
}
log.WithFields(logrus.Fields{
"signature_type": su3.SignatureType,
"file_type": su3.FileType,
"content_type": su3.ContentType,
"version": su3.Version,
"signer_id": su3.SignerID,
}).Debug("SU3 file read successfully")
return su3, nil
}
@@ -456,6 +533,7 @@ type fixedLengthReader struct {
func (r *fixedLengthReader) Read(p []byte) (n int, err error) {
if r.readSoFar >= r.length {
log.Debug("Fixed length reader: EOF reached")
return 0, io.EOF
}
if uint64(len(p)) > r.length-r.readSoFar {
@@ -463,6 +541,11 @@ func (r *fixedLengthReader) Read(p []byte) (n int, err error) {
}
n, err = r.reader.Read(p)
r.readSoFar += uint64(n)
log.WithFields(logrus.Fields{
"bytes_read": n,
"total_read": r.readSoFar,
"total_length": r.length,
}).Debug("Fixed length reader: Read operation")
return n, err
}
@@ -478,6 +561,7 @@ func (r *contentReader) Read(p []byte) (n int, err error) {
defer r.su3.mut.Unlock()
if r.finished {
log.Warn("Attempt to read content after finishing")
return 0, errors.New("out of bytes, maybe you read the signature before you read the content")
}
@@ -487,16 +571,20 @@ func (r *contentReader) Read(p []byte) (n int, err error) {
readSoFar: 0,
reader: r.su3.reader,
}
log.WithField("content_length", r.su3.ContentLength).Debug("Initialized content reader")
}
l, err := r.reader.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
log.WithError(err).Error("Error reading content")
return l, fmt.Errorf("reading content: %w", err)
} else if errors.Is(err, io.EOF) && r.reader.readSoFar != r.su3.ContentLength {
log.Error("Content shorter than expected")
return l, ErrMissingContent
} else if errors.Is(err, io.EOF) {
r.finished = true
log.Debug("Finished reading content")
}
if r.hash != nil {
@@ -505,37 +593,47 @@ func (r *contentReader) Read(p []byte) (n int, err error) {
if r.finished {
if r.su3.publicKey == nil {
log.Error("No public key provided for signature verification")
return l, ErrInvalidSignature
}
r.su3.signatureReader.getBytes()
if r.su3.signatureReader.err != nil {
log.WithError(r.su3.signatureReader.err).Error("Failed to get signature bytes")
return l, r.su3.signatureReader.err
}
log.WithField("signature_type", r.su3.SignatureType).Debug("Verifying signature")
// TODO support all signature types
switch r.su3.SignatureType {
case RSA_SHA256_2048:
var pubKey *rsa.PublicKey
if k, ok := r.su3.publicKey.(*rsa.PublicKey); !ok {
log.Error("Invalid public key type")
return l, ErrInvalidPublicKey
} else {
pubKey = k
}
err := rsa.VerifyPKCS1v15(pubKey, 0, r.hash.Sum(nil), r.su3.signatureReader.bytes)
if err != nil {
log.WithError(err).Error("Signature verification failed")
return l, ErrInvalidSignature
}
log.Debug("Signature verified successfully")
case RSA_SHA512_4096:
var pubKey *rsa.PublicKey
if k, ok := r.su3.publicKey.(*rsa.PublicKey); !ok {
log.Error("Invalid public key type")
return l, ErrInvalidPublicKey
} else {
pubKey = k
}
err := rsa.VerifyPKCS1v15(pubKey, 0, r.hash.Sum(nil), r.su3.signatureReader.bytes)
if err != nil {
log.WithError(err).Error("Signature verification failed")
return l, ErrInvalidSignature
}
log.Debug("Signature verified successfully")
default:
log.WithField("signature_type", r.su3.SignatureType).Error("Unsupported signature type")
return l, ErrUnsupportedSignatureType
}
}
@@ -551,10 +649,13 @@ type signatureReader struct {
}
func (r *signatureReader) getBytes() {
log.Debug("Getting signature bytes")
// If content hasn't been read yet, throw it away.
if !r.su3.contentReader.finished {
log.Warn("Content not fully read, reading remaining content")
_, err := ioutil.ReadAll(r.su3.contentReader)
if err != nil {
log.WithError(err).Error("Failed to read remaining content")
r.err = fmt.Errorf("reading content: %w", err)
return
}
@@ -569,12 +670,15 @@ func (r *signatureReader) getBytes() {
sigBytes, err := ioutil.ReadAll(reader)
if err != nil {
log.WithError(err).Error("Failed to read signature")
r.err = fmt.Errorf("reading signature: %w", err)
} else if reader.readSoFar != uint64(r.su3.SignatureLength) {
log.Error("Signature shorter than expected")
r.err = ErrMissingSignature
} else {
r.bytes = sigBytes
r.reader = bytes.NewReader(sigBytes)
log.WithField("signature_length", len(sigBytes)).Debug("Signature bytes read successfully")
}
}
@@ -582,10 +686,15 @@ func (r *signatureReader) Read(p []byte) (n int, err error) {
r.su3.mut.Lock()
defer r.su3.mut.Unlock()
if len(r.bytes) == 0 {
log.Debug("Signature bytes not yet read, getting bytes")
r.getBytes()
}
if r.err != nil {
log.WithError(r.err).Error("Error encountered while getting signature bytes")
return 0, r.err
}
return r.reader.Read(p)
// return r.reader.Read(p)
n, err = r.reader.Read(p)
log.WithField("bytes_read", n).Debug("Read from signature")
return n, err
}

View File

@@ -53,7 +53,7 @@ func fileRSAPubKey(t *testing.T, filename string) *rsa.PublicKey {
}
var pubKey *rsa.PublicKey
if k, ok := cert.PublicKey.(*rsa.PublicKey); !ok {
t.Fatalf("expected rsa.PublicKey from file %s", filename)
t.Fatalf("expected rsa.publicKey from file %s", filename)
} else {
pubKey = k
}

View File

@@ -35,8 +35,8 @@ type Transport interface {
// returns nil and an error on error
GetSession(routerInfo router_info.RouterInfo) (TransportSession, error)
// return true if a routerInfo is compatable with this transport
Compatable(routerInfo router_info.RouterInfo) bool
// return true if a routerInfo is compatible with this transport
Compatible(routerInfo router_info.RouterInfo) bool
// close the transport cleanly
// blocks until done
@@ -72,12 +72,12 @@ func (tmux *TransportMuxer) Close() (err error)
```
close every transport that this transport muxer has
#### func (*TransportMuxer) Compatable
#### func (*TransportMuxer) Compatible
```go
func (tmux *TransportMuxer) Compatable(routerInfo router_info.RouterInfo) (compat bool)
func (tmux *TransportMuxer) Compatible(routerInfo router_info.RouterInfo) (compat bool)
```
is there a transport that we mux that is compatable with this router info?
is there a transport that we mux that is compatible with this router info?
#### func (*TransportMuxer) GetSession

View File

@@ -3,8 +3,11 @@ package transport
import (
"github.com/go-i2p/go-i2p/lib/common/router_identity"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/util/logger"
)
var log = logger.GetGoI2PLogger()
// muxes multiple transports into 1 Transport
// implements transport.Transport
type TransportMuxer struct {
@@ -14,73 +17,96 @@ type TransportMuxer struct {
// mux a bunch of transports together
func Mux(t ...Transport) (tmux *TransportMuxer) {
log.WithField("transport_count", len(t)).Debug("Creating new TransportMuxer")
tmux = new(TransportMuxer)
tmux.trans = append(tmux.trans, t...)
log.Debug("TransportMuxer created successfully")
return
}
// set the identity for every transport
func (tmux *TransportMuxer) SetIdentity(ident router_identity.RouterIdentity) (err error) {
for _, t := range tmux.trans {
log.WithField("identity", ident).Debug("TransportMuxer: Setting identity for all transports")
for i, t := range tmux.trans {
err = t.SetIdentity(ident)
if err != nil {
log.WithError(err).WithField("transport_index", i).Error("TransportMuxer: Failed to set identity for transport")
// an error happened let's return and complain
return
}
log.WithField("transport_index", i).Debug("TransportMuxer: Identity set successfully for transport")
}
log.Debug("TransportMuxer: Identity set successfully for all transports")
return
}
// close every transport that this transport muxer has
func (tmux *TransportMuxer) Close() (err error) {
for _, t := range tmux.trans {
log.Debug("TransportMuxer: Closing all transports")
for i, t := range tmux.trans {
err = t.Close()
if t != nil {
// TODO: handle error (?)
log.WithError(err).WithField("transport_index", i).Warn("TransportMuxer: Error closing transport")
} else {
log.WithField("transport_index", i).Debug("TransportMuxer: Transport closed successfully")
}
}
log.Debug("TransportMuxer: All transports closed")
return
}
// the name of this transport with the names of all the ones that we mux
func (tmux *TransportMuxer) Name() string {
log.Debug("TransportMuxer: Generating muxed transport name")
name := "Muxed Transport: "
for _, t := range tmux.trans {
name += t.Name() + ", "
}
return name[len(name)-3:]
// return name[len(name)-3:]
_name := name[len(name)-3:]
log.WithField("name", _name).Debug("TransportMuxer: Muxed transport name generated")
return _name
}
// get a transport session given a router info
// return session and nil if successful
// return nil and ErrNoTransportAvailable if we failed to get a session
func (tmux *TransportMuxer) GetSession(routerInfo router_info.RouterInfo) (s TransportSession, err error) {
for _, t := range tmux.trans {
log.WithField("router_info", routerInfo.String()).Debug("TransportMuxer: Attempting to get session")
for i, t := range tmux.trans {
// pick the first one that is compatable
if t.Compatable(routerInfo) {
if t.Compatible(routerInfo) {
log.WithField("transport_index", i).Debug("TransportMuxer: Found compatible transport, attempting to get session")
// try to get a session
s, err = t.GetSession(routerInfo)
if err != nil {
log.WithError(err).WithField("transport_index", i).Warn("TransportMuxer: Failed to get session from compatible transport")
// we could not get a session
// try the next transport
continue
}
// we got a session
log.WithField("transport_index", i).Debug("TransportMuxer: Successfully got session from transport")
return
}
}
log.Error("TransportMuxer: Failed to get session, no compatible transport available")
// we failed to get a session for this routerInfo
err = ErrNoTransportAvailable
return
}
// is there a transport that we mux that is compatable with this router info?
func (tmux *TransportMuxer) Compatable(routerInfo router_info.RouterInfo) (compat bool) {
for _, t := range tmux.trans {
if t.Compatable(routerInfo) {
func (tmux *TransportMuxer) Compatible(routerInfo router_info.RouterInfo) (compat bool) {
log.WithField("router_info", routerInfo.String()).Debug("TransportMuxer: Checking compatibility")
for i, t := range tmux.trans {
if t.Compatible(routerInfo) {
log.WithField("transport_index", i).Debug("TransportMuxer: Found compatible transport")
compat = true
return
}
}
log.Debug("TransportMuxer: No compatible transport found")
return
}

View File

@@ -1,270 +1,300 @@
# noise
--
import "github.com/go-i2p/go-i2p/lib/transport/noise"
## Overview
The `noise` package implements the Noise Protocol to establish secure, authenticated sessions over TCP. This package includes functions for session management, handshake initiation, packet encryption, decryption, and transport abstraction.
## Usage
- [handshake.go](#handshakego)
- [i2np.go](#i2npgo)
- [incoming_handshake.go](#incoming_handshakego)
- [outgoing_handshake.go](#outgoing_handshakego)
- [noise_constants.go](#noise_constantsgo)
- [read_session.go](#read_sessiongo)
- [session.go](#sessiongo)
- [transport.go](#transportgo)
- [write_session.go](#write_sessiongo)
---
## handshake.go
Defines the `Handshake` function, which initiates the Noise handshake process for secure, authenticated sessions.
### Package
```go
const FlushLimit = 640 * 1024
package noise
```
#### type Noise
### Imports
```go
type Noise struct {
noise.Config
router_address.RouterAddress // always the local addr
*noise.HandshakeState
sync.Mutex
import (
"sync"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/go-i2p/go-i2p/lib/common/router_info"
)
```
HandshakeStateResponsibility bool
### Variables
- **`log`**: Logger instance for capturing debug and error messages related to the handshake process.
### Function: `Handshake`
#### Definition
```go
func (c *NoiseTransport) Handshake(routerInfo router_info.RouterInfo) error
```
#### Parameters
- `routerInfo`: Information about the router with which the handshake is established.
#### Returns
- `error`: Returns `nil` on success, or an error if the handshake fails.
#### Description
The `Handshake` function initiates an authenticated handshake with a router, establishing a secure session.
#### Workflow
1. **Logging Start**: Logs initiation of the handshake.
2. **Lock Mutex**: Locks `c.Mutex` to prevent concurrent access.
3. **Session Retrieval**: Calls `c.getSession(routerInfo)`.
4. **Condition Variable Setup**: Sets a `Cond` for the session.
5. **Outgoing Handshake**: Executes `RunOutgoingHandshake`.
6. **Completion Broadcast**: Broadcasts to waiting goroutines.
7. **Finalize and Unlock**: Logs success.
---
## i2np.go
Provides functions to queue and send I2NP messages using a `NoiseSession`.
### Package
```go
package noise
```
### Imports
```go
import "github.com/go-i2p/go-i2p/lib/i2np"
```
### Functions
#### `QueueSendI2NP`
Queues an I2NP message for sending.
```go
func (s *NoiseSession) QueueSendI2NP(msg i2np.I2NPMessage)
```
#### Parameters
- `msg`: The I2NP message.
---
#### `SendQueueSize`
Returns the size of the send queue.
```go
func (s *NoiseSession) SendQueueSize() int
```
---
#### `ReadNextI2NP`
Attempts to read the next I2NP message from the queue.
```go
func (s *NoiseSession) ReadNextI2NP() (i2np.I2NPMessage, error)
```
---
## incoming_handshake.go
Defines functions for the incoming (receiver) side of the handshake.
### Functions
#### `ComposeReceiverHandshakeMessage`
Creates a receiver handshake message using Noise patterns.
```go
func ComposeReceiverHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error)
```
- **`s`**: Static Diffie-Hellman key.
- **`rs`**: Remote static key.
- **`payload`**: Optional payload data.
- **`ePrivate`**: Private ephemeral key.
---
#### `RunIncomingHandshake`
Executes an incoming handshake process.
```go
func (c *NoiseSession) RunIncomingHandshake() error
```
- Initializes and sends the negotiation data and handshake message.
---
## outgoing_handshake.go
Defines functions for the outgoing (initiator) side of the handshake.
### Functions
#### `ComposeInitiatorHandshakeMessage`
Creates an initiator handshake message.
```go
func ComposeInitiatorHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error)
```
---
#### `RunOutgoingHandshake`
Executes the outgoing handshake process.
```go
func (c *NoiseSession) RunOutgoingHandshake() error
```
- Sends negotiation data and handshake message.
---
## noise_constants.go
Defines constants and utility functions for configuring Noise protocol parameters.
### Constants
```go
const (
NOISE_DH_CURVE25519 = 1
NOISE_CIPHER_CHACHAPOLY = 1
NOISE_HASH_SHA256 = 3
NOISE_PATTERN_XK = 11
uint16Size = 2
MaxPayloadSize = 65537
)
```
### Functions
#### `initNegotiationData`
Initializes negotiation data with default values.
```go
func initNegotiationData(negotiationData []byte) []byte
```
---
## read_session.go
Functions related to reading encrypted data in a Noise session.
### Functions
#### `Read`
Reads from the Noise session.
```go
func (c *NoiseSession) Read(b []byte) (int, error)
```
#### `decryptPacket`
Decrypts a packet.
```go
func (c *NoiseSession) decryptPacket(data []byte) (int, []byte, error)
```
---
## session.go
Defines the `NoiseSession` struct and associated methods for session management.
### Struct: `NoiseSession`
Defines session properties.
```go
type NoiseSession struct {
// Session properties here
}
```
wrapper around flynn/noise with just enough options exposed to enable
configuring NTCP2 possible and/or relatively intuitive
---
#### func NewNoise
## transport.go
Defines the `NoiseTransport` struct and its methods for session compatibility, accepting connections, etc.
### Struct: `NoiseTransport`
```go
func NewNoise(ra router_address.RouterAddress) (ns *Noise, err error)
```
#### func (*Noise) Addr
```go
func (ns *Noise) Addr() net.Addr
```
#### func (*Noise) DialNoise
```go
func (ns *Noise) DialNoise(addr router_address.RouterAddress) (conn net.Conn, err error)
```
#### func (*Noise) ListenNoise
```go
func (ns *Noise) ListenNoise() (list NoiseListener, err error)
```
#### func (*Noise) LocalAddr
```go
func (ns *Noise) LocalAddr() net.Addr
```
#### type NoiseConn
```go
type NoiseConn struct {
*Noise
net.Conn
type NoiseTransport struct {
sync.Mutex
router_identity.RouterIdentity
*noise.CipherState
Listener net.Listener
peerConnections map[data.Hash]transport.TransportSession
}
```
#### Methods
#### func (*NoiseConn) Close
- `Compatible`: Checks compatibility.
- `Accept`: Accepts a connection.
- `Addr`: Returns the address.
- `SetIdentity`: Sets the router identity.
- `GetSession`: Obtains a session.
---
## write_session.go
Functions for writing encrypted data in a Noise session.
### Functions
#### `Write`
Writes data in a Noise session.
```go
func (nc *NoiseConn) Close() error
func (c *NoiseSession) Write(b []byte) (int, error)
```
Close implements net.Conn.
#### func (*NoiseConn) Frame
#### `encryptPacket`
Encrypts a packet.
```go
func (nc *NoiseConn) Frame(header, body []byte) (err error)
```
#### func (*NoiseConn) HandshakeStateCreate
```go
func (nc *NoiseConn) HandshakeStateCreate(out, payload []byte) (by []byte, err error)
```
#### func (*NoiseConn) HandshakeStateRead
```go
func (nc *NoiseConn) HandshakeStateRead() (err error)
```
HandshakeStateRead reads a handshake's state off the socket for storage in the
NoiseConn.HandshakeState
#### func (*NoiseConn) LocalAddr
```go
func (nc *NoiseConn) LocalAddr() net.Addr
```
LocalAddr implements net.Conn.
#### func (*NoiseConn) Read
```go
func (nc *NoiseConn) Read(b []byte) (n int, err error)
```
Read implements net.Conn.
#### func (*NoiseConn) ReadMsg
```go
func (nc *NoiseConn) ReadMsg(b []byte) (by []byte, err error)
```
#### func (*NoiseConn) RemoteAddr
```go
func (nc *NoiseConn) RemoteAddr() net.Addr
```
RemoteAddr implements net.Conn.
#### func (*NoiseConn) SetCipherStates
```go
func (nc *NoiseConn) SetCipherStates(cs1, cs2 *noise.CipherState)
```
#### func (*NoiseConn) SetDeadline
```go
func (nc *NoiseConn) SetDeadline(t time.Time) error
```
SetDeadline implements net.Conn.
#### func (*NoiseConn) SetReadDeadline
```go
func (nc *NoiseConn) SetReadDeadline(t time.Time) error
```
SetReadDeadline implements net.Conn.
#### func (*NoiseConn) SetWriteDeadline
```go
func (nc *NoiseConn) SetWriteDeadline(t time.Time) error
```
SetWriteDeadline implements net.Conn.
#### func (*NoiseConn) Write
```go
func (nc *NoiseConn) Write(b []byte) (n int, err error)
```
Write implements net.Conn.
#### type NoiseListener
```go
type NoiseListener struct {
*Noise
net.Listener
}
```
#### func (*NoiseListener) Accept
```go
func (ns *NoiseListener) Accept() (net.Conn, error)
```
Accept implements net.Listener.
#### func (*NoiseListener) Addr
```go
func (ns *NoiseListener) Addr() net.Addr
```
Addr implements net.Listener.
#### func (*NoiseListener) Close
```go
func (ns *NoiseListener) Close() error
```
Close implements net.Listener.
#### type NoisePacketConn
```go
type NoisePacketConn struct {
*Noise
// this is always a actually a PacketConn
net.Conn
}
```
#### func (*NoisePacketConn) Close
```go
func (n *NoisePacketConn) Close() error
```
Close implements net.PacketConn. Subtle: this method shadows the method
(Conn).Close of NoisePacketConn.Conn.
#### func (*NoisePacketConn) LocalAddr
```go
func (n *NoisePacketConn) LocalAddr() net.Addr
```
LocalAddr implements net.PacketConn.
#### func (*NoisePacketConn) Read
```go
func (*NoisePacketConn) Read(b []byte) (n int, err error)
```
Read implements net.Conn.
#### func (*NoisePacketConn) ReadFrom
```go
func (*NoisePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
```
ReadFrom implements net.PacketConn.
#### func (*NoisePacketConn) RemoteAddr
```go
func (n *NoisePacketConn) RemoteAddr() net.Addr
```
RemoteAddr implements net.Conn.
#### func (*NoisePacketConn) SetDeadline
```go
func (n *NoisePacketConn) SetDeadline(t time.Time) error
```
SetDeadline implements net.PacketConn. Subtle: this method shadows the method
(PacketConn).SetDeadline of NoisePacketConn.PacketConn.
#### func (*NoisePacketConn) SetReadDeadline
```go
func (n *NoisePacketConn) SetReadDeadline(t time.Time) error
```
SetReadDeadline implements net.PacketConn. Subtle: this method shadows the
method (PacketConn).SetReadDeadline of NoisePacketConn.PacketConn.
#### func (*NoisePacketConn) SetWriteDeadline
```go
func (n *NoisePacketConn) SetWriteDeadline(t time.Time) error
```
SetWriteDeadline implements net.PacketConn. Subtle: this method shadows the
method (PacketConn).SetWriteDeadline of NoisePacketConn.PacketConn.
#### func (*NoisePacketConn) Write
```go
func (*NoisePacketConn) Write(b []byte) (n int, err error)
```
Write implements net.Conn.
#### func (*NoisePacketConn) WriteTo
```go
func (*NoisePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error)
```
WriteTo implements net.PacketConn.
func (c *NoiseSession) encryptPacket(data []byte) (int, []byte, error)
```

View File

@@ -0,0 +1,670 @@
package noise
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"fmt"
"testing"
"github.com/go-i2p/go-i2p/lib/crypto"
"github.com/go-i2p/go-i2p/lib/transport/obfs"
"github.com/flynn/noise"
"github.com/stretchr/testify/assert"
)
func (ns *NoiseSession) testEncryptPacket(plaintext []byte) (int, []byte, error) {
if ns.CipherState == nil {
return 0, nil, fmt.Errorf("CipherState is nil")
}
// Encrypt the data
ciphertext, err := ns.CipherState.Encrypt(nil, nil, plaintext)
if err != nil {
log.Fatalf("unimplemented\nerror:%v\n", err)
}
// Prepend the length of the ciphertext as a 2-byte big-endian value
packetLength := uint16(len(ciphertext))
packet := make([]byte, 2+len(ciphertext))
binary.BigEndian.PutUint16(packet[:2], packetLength)
copy(packet[2:], ciphertext)
return len(packet), packet, nil
}
func (ns *NoiseSession) testPacketDeux(packet []byte) (int, []byte, error) {
if ns.CipherState == nil {
return 0, nil, fmt.Errorf("CipherState is nil")
}
if len(packet) < 2 {
return 0, nil, fmt.Errorf("Packet too short to contain length prefix")
}
// Extract the length prefix
packetLength := binary.BigEndian.Uint16(packet[:2])
if len(packet[2:]) < int(packetLength) {
return 0, nil, fmt.Errorf("Packet data is shorter than indicated length")
}
ciphertext := packet[2 : 2+packetLength]
// Decrypt the data
plaintext, err := ns.CipherState.Decrypt(nil, nil, ciphertext)
if err != nil {
return 0, nil, err
}
return len(plaintext), plaintext, nil
}
func TestEncryptDecryptPacketOffline(t *testing.T) {
// Generate static keypairs
initiatorStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate initiator static keypair: %v", err)
}
responderStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate responder static keypair: %v", err)
}
pattern := noise.HandshakeXK
cipherSuite := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)
// Negotiation
negData := initNegotiationData(nil)
prologue := make([]byte, 2, uint16Size+len(negData))
binary.BigEndian.PutUint16(prologue, uint16(len(negData)))
prologue = append(prologue, negData...)
// Handshake
initiatorHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: initiatorStatic,
Initiator: true,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
PeerStatic: responderStatic.Public, // Must set this
})
if err != nil {
t.Fatalf("Failed to create initiator handshake state: %v", err)
}
responderHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: responderStatic,
Initiator: false,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
})
if err != nil {
t.Fatalf("Failed to create responder handshake state: %v", err)
}
var (
initiatorSendCS *noise.CipherState
initiatorRecvCS *noise.CipherState
responderSendCS *noise.CipherState
responderRecvCS *noise.CipherState
)
// Simulate the handshake message exchange
// Message 1: Initiator -> Responder
msg1, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after message 1")
}
// Responder processes message 1
_, cs0, cs1, err = responderHS.ReadMessage(nil, msg1)
if err != nil {
t.Fatalf("Responder failed to read handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after reading message 1")
}
// Responder writes message 2
msg2, cs0, cs1, err := responderHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Responder failed to write handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after writing message 2")
}
// Initiator processes message 2
_, cs0, cs1, err = initiatorHS.ReadMessage(nil, msg2)
if err != nil {
t.Fatalf("Initiator failed to read handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after reading message 2")
}
// Initiator writes message 3
msg3, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Initiator did not receive CipherStates after writing message 3")
}
initiatorSendCS = cs0
initiatorRecvCS = cs1
// Responder processes message 3
_, cs0, cs1, err = responderHS.ReadMessage(nil, msg3)
if err != nil {
t.Fatalf("Responder failed to read handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Responder did not receive CipherStates after reading message 3")
}
responderRecvCS = cs0
responderSendCS = cs1
// Now both parties have the CipherStates
// Initiator sends a message to Responder
initiatorSession := &NoiseSession{
CipherState: initiatorSendCS,
}
responderSession := &NoiseSession{
CipherState: responderRecvCS,
}
originalData := []byte("This is a test message.")
_, encryptedPacket, err := initiatorSession.encryptPacket(originalData)
if err != nil {
t.Fatalf("Encryption failed: %v", err)
}
_, decryptedData, err := responderSession.decryptPacket(encryptedPacket[2:])
if err != nil {
t.Fatalf("Decryption failed: %v", err)
}
assert.Equal(t, originalData, decryptedData, "Decrypted data does not match the original data")
// Responder sends a message to Initiator
responderSession = &NoiseSession{
CipherState: responderSendCS,
}
initiatorSession = &NoiseSession{
CipherState: initiatorRecvCS,
}
responseData := []byte("This is a response message.")
_, encryptedResponse, err := responderSession.encryptPacket(responseData)
if err != nil {
t.Fatalf("Responder encryption failed: %v", err)
}
_, decryptedResponse, err := initiatorSession.decryptPacket(encryptedResponse[2:])
if err != nil {
t.Fatalf("Initiator decryption failed: %v", err)
}
assert.Equal(t, responseData, decryptedResponse, "Decrypted response does not match original data")
}
func TestEncryptDecryptPacketObfsOffline(t *testing.T) {
// Simulate Bob's Router Hash (RH_B)
bobRouterHash := make([]byte, 32)
rand.Read(bobRouterHash)
// Simulate Bob's IV (ri.IV)
bobIV := make([]byte, 16)
rand.Read(bobIV)
// Create AES cipher block
aesBlock, err := aes.NewCipher(bobRouterHash)
if err != nil {
t.Fatalf("Failed to create AES cipher block: %v", err)
}
// Create AES CBC encrypter and decrypter
aesEncrypter := cipher.NewCBCEncrypter(aesBlock, bobIV)
aesDecrypter := cipher.NewCBCDecrypter(aesBlock, bobIV)
// Generate static keypairs
initiatorStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate initiator static keypair: %v", err)
}
responderStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate responder static keypair: %v", err)
}
pattern := noise.HandshakeXK
cipherSuite := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)
// Negotiation
negData := initNegotiationData(nil)
prologue := make([]byte, 2, uint16Size+len(negData))
binary.BigEndian.PutUint16(prologue, uint16(len(negData)))
prologue = append(prologue, negData...)
// Alice's Handshake State
initiatorHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: initiatorStatic,
Initiator: true,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
PeerStatic: responderStatic.Public, // Bob's static public key
})
if err != nil {
t.Fatalf("Failed to create initiator handshake state: %v", err)
}
// Bob's Handshake State
responderHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: responderStatic,
Initiator: false,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
})
if err != nil {
t.Fatalf("Failed to create responder handshake state: %v", err)
}
var (
initiatorSendCS *noise.CipherState
initiatorRecvCS *noise.CipherState
responderSendCS *noise.CipherState
responderRecvCS *noise.CipherState
)
// Simulate the handshake message exchange
// -------------------------------
// Message 1: Initiator -> Responder
// -------------------------------
// Alice writes message 1
msg1, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after message 1")
}
// Encrypt Alice's ephemeral public key using AES-256-CBC
if len(msg1) < 32 {
t.Fatalf("Message 1 is too short to contain ephemeral public key")
}
aliceEphemeralPubKey := msg1[:32] // First 32 bytes
encryptedX := make([]byte, len(aliceEphemeralPubKey))
aesEncrypter.CryptBlocks(encryptedX, aliceEphemeralPubKey)
// Construct the modified message 1
fullMsg1 := append(encryptedX, msg1[32:]...)
// -------------------------------
// Responder processes message 1
// -------------------------------
// Extract encrypted ephemeral public key
encryptedXReceived := fullMsg1[:32]
// Decrypt the ephemeral public key
decryptedX := make([]byte, len(encryptedXReceived))
aesDecrypter.CryptBlocks(decryptedX, encryptedXReceived)
// Replace the encrypted ephemeral key with the decrypted one
modifiedMsg1 := append(decryptedX, fullMsg1[32:]...)
// Bob reads message 1
_, cs0, cs1, err = responderHS.ReadMessage(nil, modifiedMsg1)
if err != nil {
t.Fatalf("Responder failed to read handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after reading message 1")
}
// -------------------------------
// Message 2: Responder -> Initiator
// -------------------------------
// Bob writes message 2
msg2, cs0, cs1, err := responderHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Responder failed to write handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after writing message 2")
}
// Encrypt Bob's ephemeral public key using AES-256-CBC
if len(msg2) < 32 {
t.Fatalf("Message 2 is too short to contain ephemeral public key")
}
bobEphemeralPubKey := msg2[:32] // First 32 bytes
encryptedY := make([]byte, len(bobEphemeralPubKey))
aesEncrypter.CryptBlocks(encryptedY, bobEphemeralPubKey)
// Construct the modified message 2
fullMsg2 := append(encryptedY, msg2[32:]...)
// -------------------------------
// Initiator processes message 2
// -------------------------------
// Extract encrypted ephemeral public key
encryptedYReceived := fullMsg2[:32]
// Decrypt the ephemeral public key
decryptedY := make([]byte, len(encryptedYReceived))
aesDecrypter.CryptBlocks(decryptedY, encryptedYReceived)
// Replace the encrypted ephemeral key with the decrypted one
modifiedMsg2 := append(decryptedY, fullMsg2[32:]...)
// Alice reads message 2
_, cs0, cs1, err = initiatorHS.ReadMessage(nil, modifiedMsg2)
if err != nil {
t.Fatalf("Initiator failed to read handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after reading message 2")
}
// -------------------------------
// Message 3: Initiator -> Responder
// -------------------------------
// Alice writes message 3
msg3, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Initiator did not receive CipherStates after writing message 3")
}
initiatorSendCS = cs0
initiatorRecvCS = cs1
// Responder processes message 3
_, cs0, cs1, err = responderHS.ReadMessage(nil, msg3)
if err != nil {
t.Fatalf("Responder failed to read handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Responder did not receive CipherStates after reading message 3")
}
responderRecvCS = cs0
responderSendCS = cs1
// Now both parties have the CipherStates
// Initiator sends a message to Responder
initiatorSession := &NoiseSession{
CipherState: initiatorSendCS,
}
responderSession := &NoiseSession{
CipherState: responderRecvCS,
}
originalData := []byte("This is a test message.")
_, encryptedPacket, err := initiatorSession.encryptPacket(originalData)
if err != nil {
t.Fatalf("Encryption failed: %v", err)
}
_, decryptedData, err := responderSession.decryptPacket(encryptedPacket[2:])
if err != nil {
t.Fatalf("Decryption failed: %v", err)
}
assert.Equal(t, originalData, decryptedData, "Decrypted data does not match the original data")
// Responder sends a message to Initiator
responderSession = &NoiseSession{
CipherState: responderSendCS,
}
initiatorSession = &NoiseSession{
CipherState: initiatorRecvCS,
}
responseData := []byte("This is a response message.")
_, encryptedResponse, err := responderSession.encryptPacket(responseData)
if err != nil {
t.Fatalf("Responder encryption failed: %v", err)
}
_, decryptedResponse, err := initiatorSession.decryptPacket(encryptedResponse[2:])
if err != nil {
t.Fatalf("Initiator decryption failed: %v", err)
}
assert.Equal(t, responseData, decryptedResponse, "Decrypted response does not match original data")
}
// TestEncryptDecryptPacketObfsOffline tests the encryption and decryption with AES obfuscation
func TestEncryptDecryptPacketObfsOfflineWithFunc(t *testing.T) {
// Simulate Bob's Router Hash (RH_B)
bobRouterHash := make([]byte, 32)
rand.Read(bobRouterHash)
// Simulate Bob's IV (ri.IV)
bobIV := make([]byte, 16)
rand.Read(bobIV)
// Create AES symmetric key
aesKey := &crypto.AESSymmetricKey{
Key: bobRouterHash,
IV: bobIV,
}
// Generate static keypairs
initiatorStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate initiator static keypair: %v", err)
}
responderStatic, err := noise.DH25519.GenerateKeypair(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate responder static keypair: %v", err)
}
pattern := noise.HandshakeXK
cipherSuite := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)
// Negotiation
negData := initNegotiationData(nil)
prologue := make([]byte, 2+len(negData))
binary.BigEndian.PutUint16(prologue, uint16(len(negData)))
copy(prologue[2:], negData)
// Alice's Handshake State
initiatorHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: initiatorStatic,
Initiator: true,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
PeerStatic: responderStatic.Public, // Bob's static public key
})
if err != nil {
t.Fatalf("Failed to create initiator handshake state: %v", err)
}
// Bob's Handshake State
responderHS, err := noise.NewHandshakeState(noise.Config{
StaticKeypair: responderStatic,
Initiator: false,
Pattern: pattern,
CipherSuite: cipherSuite,
Prologue: prologue,
})
if err != nil {
t.Fatalf("Failed to create responder handshake state: %v", err)
}
var (
initiatorSendCS *noise.CipherState
initiatorRecvCS *noise.CipherState
responderSendCS *noise.CipherState
responderRecvCS *noise.CipherState
)
// Simulate the handshake message exchange
// -------------------------------
// Message 1: Initiator -> Responder
// -------------------------------
// Alice writes message 1
msg1, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after message 1")
}
// Obfuscate Alice's ephemeral public key in message 1
obfuscatedMsg1, err := obfs.ObfuscateEphemeralKey(msg1, aesKey)
if err != nil {
t.Fatalf("Failed to obfuscate message 1: %v", err)
}
// -------------------------------
// Responder processes message 1
// -------------------------------
// Deobfuscate Alice's ephemeral public key in message 1
deobfuscatedMsg1, err := obfs.DeobfuscateEphemeralKey(obfuscatedMsg1, aesKey)
if err != nil {
t.Fatalf("Failed to deobfuscate message 1: %v", err)
}
// Bob reads message 1
_, cs0, cs1, err = responderHS.ReadMessage(nil, deobfuscatedMsg1)
if err != nil {
t.Fatalf("Responder failed to read handshake message 1: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after reading message 1")
}
// -------------------------------
// Message 2: Responder -> Initiator
// -------------------------------
// Bob writes message 2
msg2, cs0, cs1, err := responderHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Responder failed to write handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Responder should not have CipherStates after writing message 2")
}
// Obfuscate Bob's ephemeral public key in message 2
obfuscatedMsg2, err := obfs.ObfuscateEphemeralKey(msg2, aesKey)
if err != nil {
t.Fatalf("Failed to obfuscate message 2: %v", err)
}
// -------------------------------
// Initiator processes message 2
// -------------------------------
// Deobfuscate Bob's ephemeral public key in message 2
deobfuscatedMsg2, err := obfs.DeobfuscateEphemeralKey(obfuscatedMsg2, aesKey)
if err != nil {
t.Fatalf("Failed to deobfuscate message 2: %v", err)
}
// Alice reads message 2
_, cs0, cs1, err = initiatorHS.ReadMessage(nil, deobfuscatedMsg2)
if err != nil {
t.Fatalf("Initiator failed to read handshake message 2: %v", err)
}
if cs0 != nil || cs1 != nil {
t.Fatalf("Initiator should not have CipherStates after reading message 2")
}
// -------------------------------
// Message 3: Initiator -> Responder
// -------------------------------
// Alice writes message 3
msg3, cs0, cs1, err := initiatorHS.WriteMessage(nil, nil)
if err != nil {
t.Fatalf("Initiator failed to write handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Initiator did not receive CipherStates after writing message 3")
}
initiatorSendCS = cs0
initiatorRecvCS = cs1
// Responder processes message 3
_, cs0, cs1, err = responderHS.ReadMessage(nil, msg3)
if err != nil {
t.Fatalf("Responder failed to read handshake message 3: %v", err)
}
if cs0 == nil || cs1 == nil {
t.Fatalf("Responder did not receive CipherStates after reading message 3")
}
responderRecvCS = cs0
responderSendCS = cs1
// Now both parties have the CipherStates
// Initiator sends a message to Responder
initiatorSession := &NoiseSession{
CipherState: initiatorSendCS,
}
responderSession := &NoiseSession{
CipherState: responderRecvCS,
}
originalData := []byte("This is a test message.")
_, encryptedPacket, err := initiatorSession.testEncryptPacket(originalData)
if err != nil {
t.Fatalf("Encryption failed: %v", err)
}
_, decryptedData, err := responderSession.testPacketDeux(encryptedPacket)
if err != nil {
t.Fatalf("Decryption failed: %v", err)
}
assert.Equal(t, originalData, decryptedData, "Decrypted data does not match the original data")
// Responder sends a message to Initiator
responderSession = &NoiseSession{
CipherState: responderSendCS,
}
initiatorSession = &NoiseSession{
CipherState: initiatorRecvCS,
}
responseData := []byte("This is a response message.")
_, encryptedResponse, err := responderSession.testEncryptPacket(responseData)
if err != nil {
t.Fatalf("Responder encryption failed: %v", err)
}
_, decryptedResponse, err := initiatorSession.testPacketDeux(encryptedResponse)
if err != nil {
t.Fatalf("Initiator decryption failed: %v", err)
}
assert.Equal(t, responseData, decryptedResponse, "Decrypted response does not match original data")
}

View File

@@ -0,0 +1,109 @@
package noise
import (
"sync"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/flynn/noise"
)
type HandshakeState struct {
mutex sync.Mutex
ephemeral *noise.DHKey
pattern noise.HandshakePattern
handshakeComplete bool
HandKey noise.DHKey
*noise.HandshakeState
}
func NewHandshakeState(staticKey noise.DHKey, isInitiator bool) (*HandshakeState, error) {
hs := &HandshakeState{
pattern: noise.HandshakeXK,
}
config := noise.Config{
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherAESGCM, noise.HashSHA256),
Pattern: hs.pattern,
Initiator: isInitiator,
StaticKeypair: staticKey,
}
protocol, err := noise.NewHandshakeState(config)
if err != nil {
return nil, err
}
hs.HandshakeState = protocol
return hs, nil
}
// GenerateEphemeral creates the ephemeral keypair that will be used in handshake
// This needs to be separate so NTCP2 can obfuscate it
func (h *HandshakeState) GenerateEphemeral() (*noise.DHKey, error) {
h.mutex.Lock()
defer h.mutex.Unlock()
dhKey, err := noise.DH25519.GenerateKeypair(nil)
if err != nil {
return nil, err
}
h.ephemeral = &dhKey
return &dhKey, nil
}
// SetEphemeral allows setting a potentially modified ephemeral key
// This is needed for NTCP2's obfuscation layer
func (h *HandshakeState) SetEphemeral(key *noise.DHKey) error {
h.mutex.Lock()
defer h.mutex.Unlock()
h.ephemeral = key
return nil
}
func (h *HandshakeState) WriteMessage(payload []byte) ([]byte, *noise.CipherState, *noise.CipherState, error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.HandshakeState.WriteMessage(nil, payload)
}
func (h *HandshakeState) ReadMessage(message []byte) ([]byte, *noise.CipherState, *noise.CipherState, error) {
h.mutex.Lock()
defer h.mutex.Unlock()
return h.HandshakeState.ReadMessage(nil, message)
}
var log = logger.GetGoI2PLogger()
func (c *NoiseTransport) Handshake(routerInfo router_info.RouterInfo) error {
log.WithField("router_info", routerInfo.IdentHash()).Debug("Starting Noise handshake")
c.Mutex.Lock()
defer c.Mutex.Unlock()
session, err := c.getSession(routerInfo)
if err != nil {
log.WithError(err).Error("Failed to get session for handshake")
return err
}
log.Debug("Session obtained for handshake")
// Set handshakeCond to indicate that this goroutine is committing to
// running the handshake.
session.(*NoiseSession).Cond = sync.NewCond(&c.Mutex)
c.Mutex.Unlock()
session.(*NoiseSession).Mutex.Lock()
defer session.(*NoiseSession).Mutex.Unlock()
c.Mutex.Lock()
log.Debug("Running outgoing handshake")
if err := session.(*NoiseSession).RunOutgoingHandshake(); err != nil {
return err
}
log.Debug("Outgoing handshake completed successfully")
// Wake any other goroutines that are waiting for this handshake to
// complete.
session.(*NoiseSession).Cond.Broadcast()
session.(*NoiseSession).Cond = nil
log.Debug("Noise handshake completed successfully")
return nil
}

View File

@@ -0,0 +1,15 @@
package noise
import "github.com/go-i2p/go-i2p/lib/i2np"
func (s *NoiseSession) QueueSendI2NP(msg i2np.I2NPMessage) {
s.SendQueue.Enqueue(msg)
}
func (s *NoiseSession) SendQueueSize() int {
return s.SendQueue.Size()
}
func (s *NoiseSession) ReadNextI2NP() (i2np.I2NPMessage, error) {
return i2np.I2NPMessage{}, nil
}

View File

@@ -0,0 +1,91 @@
package noise
import (
"bytes"
"crypto/rand"
"errors"
"io"
"github.com/flynn/noise"
"github.com/sirupsen/logrus"
)
func (c *NoiseSession) RunIncomingHandshake() error {
log.Debug("Starting incoming handshake")
negData, msg, state, err := ComposeReceiverHandshakeMessage(c.HandKey, nil, nil, nil)
if err != nil {
log.WithError(err).Error("Failed to compose receiver handshake message")
return err
}
c.HandshakeState = &HandshakeState{
HandshakeState: state,
}
log.WithFields(logrus.Fields{
"negData_length": len(negData),
"msg_length": len(msg),
}).Debug("Receiver handshake message composed")
if _, err = c.Write(negData); err != nil {
log.WithError(err).Error("Failed to write negotiation data")
return err
}
log.Debug("Negotiation data written successfully")
if _, err = c.Write(msg); err != nil {
log.WithError(err).Error("Failed to write handshake message")
return err
}
log.Debug("Handshake message written successfully")
log.WithField("state", state).Debug("Handshake state after message write")
log.Println(state)
c.handshakeComplete = true
log.Debug("Incoming handshake completed successfully")
return nil
}
func ComposeReceiverHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error) {
log.Debug("Starting ComposeReceiverHandshakeMessage")
if len(rs) != 0 && len(rs) != noise.DH25519.DHLen() {
log.WithField("rs_length", len(rs)).Error("Invalid remote static key length")
return nil, nil, nil, errors.New("only 32 byte curve25519 public keys are supported")
}
negData = make([]byte, 6)
copy(negData, initNegotiationData(nil))
pattern := noise.HandshakeXK
negData[5] = NOISE_PATTERN_XK
var random io.Reader
if len(ePrivate) == 0 {
random = rand.Reader
log.Debug("Using crypto/rand as random source")
} else {
random = bytes.NewBuffer(ePrivate)
}
config := noise.Config{
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherAESGCM, noise.HashSHA256),
Pattern: pattern,
Initiator: false,
StaticKeypair: s,
Random: random,
}
state, err = noise.NewHandshakeState(config)
if err != nil {
return nil, nil, nil, err
}
// Write message 2, expecting no CipherStates yet
msg, cs0, cs1, err := state.WriteMessage(nil, payload)
if err != nil {
return nil, nil, nil, err
}
// Verify no CipherStates are returned yet
if cs0 != nil || cs1 != nil {
return nil, nil, nil, errors.New("unexpected cipher states in message 2")
}
return negData, msg, state, nil
}

View File

@@ -1,154 +0,0 @@
package noise
import (
"fmt"
"log"
"net"
"strconv"
"github.com/flynn/noise"
"github.com/go-i2p/go-i2p/lib/common/router_address"
"github.com/go-i2p/go-i2p/lib/common/router_info"
)
// wrapper around flynn/noise with just enough options exposed to enable configuring NTCP2
// possible and/or relatively intuitive
type Noise struct {
noise.Config
router_info.RouterInfo // always the local
*noise.HandshakeState
HandshakeStateResponsibility bool
handshakeHash []byte
send, recv *noise.CipherState
readMsgBuf []byte
writeMsgBuf []byte
readBuf []byte
}
var (
ex_ns net.Conn = &NoiseConn{}
ex_ns_l net.Listener = &NoiseListener{}
ex_ns_u net.PacketConn = &NoisePacketConn{}
)
// NewNoise creates a new Noise-based transport with only the config for our side of the connection.
// It accepts a RouterInfo which should always be our own RouterInfo.
func NewNoise(ri router_info.RouterInfo) (ns *Noise, err error) {
ns = &Noise{}
ns.RouterInfo = ri
// sk, err := ra.StaticKey()
if err != nil {
return nil, err
}
ns.Config = noise.Config{
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256),
Pattern: noise.HandshakeXK,
// StaticKeypair: ,
// StaticKeypair: ,
// EphemeralKeypair: ,
}
return
}
func (ns *Noise) LocalAddr() net.Addr {
return &ns.RouterInfo
}
func (ns *Noise) Addr() net.Addr {
return ns.LocalAddr()
}
// MatchAddr finds a transport suitable for an incoming RouterAddress
func (ns *Noise) MatchAddr(addr net.Addr) (*router_address.RouterAddress, error) {
for index, address := range ns.RouterInfo.RouterAddresses() {
log.Println("index", index, "address", address)
if addr.Network() == address.Network() {
return address, nil
}
}
return nil, fmt.Errorf("no suitable address found for type %s from %s", addr.Network(), addr.String())
}
func dialWrapper(network, address string) (net.Conn, error) {
switch network {
case "SSU24":
return net.Dial("udp4", address)
case "SSU26":
return net.Dial("udp6", address)
case "NTCP2":
return net.Dial("tcp", address)
case "NTCP24":
return net.Dial("tcp4", address)
case "NTCP26":
return net.Dial("tcp6", address)
case "NTCP4":
return net.Dial("tcp4", address)
case "NTCP6":
return net.Dial("tcp6", address)
default:
return nil, fmt.Errorf("unknown transport, cannot dial %s", network)
}
}
func (ns Noise) DialNoise(addr router_address.RouterAddress) (net.Conn, error) {
cfg := ns
cfg.Initiator = false
network := addr.Network()
host, err := addr.Host()
if err != nil {
return nil, fmt.Errorf("host error: %s", err)
}
port, err := addr.Port()
if err != nil {
return nil, fmt.Errorf("port error: %s", err)
}
raddr := net.JoinHostPort(host.String(), port)
var netConn net.Conn
netConn, err = dialWrapper(network, raddr)
if err != nil {
return nil, fmt.Errorf("dial error: %s", err)
}
cfg.HandshakeState, err = noise.NewHandshakeState(cfg.Config)
if err != nil {
return nil, fmt.Errorf("handshake state initialization error: %s", err)
}
laddr, err := ns.MatchAddr(&addr)
if err != nil {
return nil, fmt.Errorf("transport mismatch error %s", err)
}
// cfg.Config.PeerEphemeral, err = AESDeObfuscateEphemeralKeys()
return &NoiseConn{
Noise: cfg,
Conn: netConn,
raddr: addr,
laddr: *laddr,
}, nil
}
func (ns Noise) ListenNoise(addr router_address.RouterAddress) (list NoiseListener, err error) {
cfg := ns
cfg.Initiator = false
network := "tcp"
host, err := addr.Host()
if err != nil {
return
}
port, err := addr.Port()
if err != nil {
return
}
portNum, _ := strconv.Atoi(port)
port = strconv.Itoa(portNum + 1)
hostip := net.JoinHostPort(host.String(), port)
listener, err := net.Listen(network, hostip)
if err != nil {
return
}
return NoiseListener{
Noise: cfg,
Listener: listener,
}, nil
}

View File

@@ -1,138 +0,0 @@
package noise
import (
"net"
"sync"
"time"
"github.com/flynn/noise"
"github.com/go-i2p/go-i2p/lib/common/router_address"
)
const FlushLimit = 640 * 1024
type NoiseConn struct {
Noise
net.Conn
sync.Mutex
laddr, raddr router_address.RouterAddress
lock bool
}
func (ns *NoiseConn) unlockMutex() {
if ns.lock {
ns.lock = false
ns.Mutex.Unlock()
}
}
func (ns *NoiseConn) lockMutex() {
if !ns.lock {
ns.lock = true
ns.Mutex.Lock()
}
}
// Close implements net.Conn.
func (nc *NoiseConn) Close() error {
return nc.Conn.Close()
}
// LocalAddr implements net.Conn.
func (nc *NoiseConn) LocalAddr() net.Addr {
return &nc.laddr
}
// Write implements net.Conn.
func (nc *NoiseConn) Write(b []byte) (n int, err error) {
nc.lockMutex()
if nc.HandshakeState != nil {
defer nc.unlockMutex()
for nc.HandshakeState != nil && len(b) > 0 {
if !nc.Initiator {
// If we're the initiator, then we set that in advance and we already know.
// If not, we need to read the handshake state first.
err = nc.HandshakeStateRead()
if err != nil {
return n, err
}
}
// if the HandshakeState is not populated here we are the initiator.
// we could(should? shouldn't?) check both but for now I'm sticking with what
// NoiseConn does
if nc.HandshakeState != nil {
// choose either the length of b or the maximum length of a message
l := min(noise.MaxMsgLen, len(b))
// update the HandshakeState using l number of bytes to the write message buffer
nc.writeMsgBuf, err = nc.HandshakeStateCreate(nc.writeMsgBuf[:0], b[:l])
if err != nil {
return n, err
}
// write the message buffer to the socket
_, err = nc.Conn.Write(nc.writeMsgBuf)
if err != nil {
return n, err
}
n += l
b = b[l:]
}
}
}
nc.unlockMutex()
// zero-out the write buffer
nc.writeMsgBuf = nc.writeMsgBuf[:0]
for len(b) > 0 {
outlen := len(nc.writeMsgBuf)
l := min(noise.MaxMsgLen, len(b))
nc.writeMsgBuf, err = nc.send.Encrypt(append(nc.writeMsgBuf, make([]byte, 4)...), nil, b[:l])
if err != nil {
return n, err
}
err = nc.Frame(nc.writeMsgBuf[outlen:], nc.writeMsgBuf[outlen+4:])
if err != nil {
return n, err
}
n += l
b = b[l:]
if len(nc.writeMsgBuf) > FlushLimit {
_, err = nc.Conn.Write(nc.writeMsgBuf)
if err != nil {
return n, err
}
nc.writeMsgBuf = nc.writeMsgBuf[:0]
}
}
if len(nc.writeMsgBuf) > 0 {
_, err = nc.Conn.Write(nc.writeMsgBuf)
if err != nil {
return n, err
}
nc.writeMsgBuf = nc.writeMsgBuf[:0]
}
return n, nil
}
// Read implements net.Conn.
func (nc *NoiseConn) Read(b []byte) (n int, err error) {
panic("unimplemented")
}
// RemoteAddr implements net.Conn.
func (nc *NoiseConn) RemoteAddr() net.Addr {
panic("unimplemented")
}
// SetDeadline implements net.Conn.
func (nc *NoiseConn) SetDeadline(t time.Time) error {
panic("unimplemented")
}
// SetReadDeadline implements net.Conn.
func (nc *NoiseConn) SetReadDeadline(t time.Time) error {
panic("unimplemented")
}
// SetWriteDeadline implements net.Conn.
func (nc *NoiseConn) SetWriteDeadline(t time.Time) error {
panic("unimplemented")
}

View File

@@ -1,63 +0,0 @@
package noise
import "github.com/flynn/noise"
// HandshakeStateRead reads a handshake's state off the socket for storage in the
// NoiseConn.HandshakeState
func (nc *NoiseConn) HandshakeStateRead() (err error) {
nc.readMsgBuf, err = nc.ReadMsg(nc.readMsgBuf[:0])
if err != nil {
return err
}
var cs1, cs2 *noise.CipherState
nc.readBuf, cs1, cs2, err = nc.HandshakeState.ReadMessage(nc.readBuf, nc.readMsgBuf)
if err != nil {
return err
}
nc.SetCipherStates(cs1, cs2)
nc.HandshakeStateResponsibility = true
//if nc.rfmValidate != nil {
//err = nc.rfmValidate(nc.Conn.RemoteAddr(), nc.readMsgBuf)
//nc.rfmValidate = nil
//return err
//}
return nil
}
func (nc *NoiseConn) HandshakeStateCreate(out, payload []byte) (by []byte, err error) {
var cs1, cs2 *noise.CipherState
outlen := len(out)
out, cs1, cs2, err = nc.HandshakeState.WriteMessage(append(out, make([]byte, 4)...), payload)
if err != nil {
return nil, err
}
//if nc.rfmValidate != nil {
// only applies to responders, not initiators.
//nc.rfmValidate = nil
//}
nc.SetCipherStates(cs1, cs2)
nc.HandshakeStateResponsibility = false
// nc.readBarrier.Release()
return out, nc.Frame(out[outlen:], out[outlen+4:])
}
func (nc *NoiseConn) Frame(header, body []byte) (err error) {
return
}
func (nc *NoiseConn) ReadMsg(b []byte) (by []byte, err error) {
return
}
func (nc *NoiseConn) SetCipherStates(cs1, cs2 *noise.CipherState) {
if nc.Initiator {
nc.send, nc.recv = cs1, cs2
} else {
nc.send, nc.recv = cs2, cs1
}
if nc.send != nil {
// nc.readBarrier.Release()
nc.handshakeHash = nc.HandshakeState.ChannelBinding()
nc.HandshakeState = nil
}
}

View File

@@ -0,0 +1,46 @@
package noise
import (
"encoding/binary"
"github.com/flynn/noise"
)
const (
NOISE_DH_CURVE25519 = 1
NOISE_CIPHER_CHACHAPOLY = 1
NOISE_CIPHER_AESGCM = 2
NOISE_HASH_SHA256 = 3
NOISE_PATTERN_XK = 11
uint16Size = 2 // uint16 takes 2 bytes
MaxPayloadSize = 65537
)
var ciphers = map[byte]noise.CipherFunc{
NOISE_CIPHER_CHACHAPOLY: noise.CipherChaChaPoly,
NOISE_CIPHER_AESGCM: noise.CipherAESGCM,
}
var hashes = map[byte]noise.HashFunc{
NOISE_HASH_SHA256: noise.HashSHA256,
}
var patterns = map[byte]noise.HandshakePattern{
NOISE_PATTERN_XK: noise.HandshakeXK,
}
func initNegotiationData(negotiationData []byte) []byte {
if negotiationData != nil {
return negotiationData
}
negotiationData = make([]byte, 6)
binary.BigEndian.PutUint16(negotiationData, 1) // version
negotiationData[2] = NOISE_DH_CURVE25519
negotiationData[3] = NOISE_CIPHER_CHACHAPOLY
negotiationData[4] = NOISE_HASH_SHA256
return negotiationData
}

View File

@@ -1,44 +0,0 @@
package noise
import (
"net"
"sync"
"github.com/flynn/noise"
)
type NoiseListener struct {
Noise
net.Listener
sync.Mutex
lock bool
}
// Addr implements net.Listener.
func (ns *NoiseListener) Addr() net.Addr {
return ns.Noise.Addr()
}
// Close implements net.Listener.
func (ns *NoiseListener) Close() error {
return ns.Listener.Close()
}
// Accept implements net.Listener.
func (ns *NoiseListener) Accept() (net.Conn, error) {
cfg := ns.Noise
cfg.Initiator = false
accept, err := ns.Listener.Accept()
if err != nil {
return nil, err
}
hs, err := noise.NewHandshakeState(ns.Config)
if err != nil {
return nil, err
}
cfg.HandshakeState = hs
return &NoiseConn{
Noise: cfg,
Conn: accept,
}, nil
}

View File

@@ -1,40 +0,0 @@
package noise
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"github.com/flynn/noise"
"github.com/go-i2p/go-i2p/lib/common/router_address"
log "github.com/sirupsen/logrus"
)
// Noise obfuscation functions used in I2P NTCP2 and SSU2 Handshakes,
// including obfuscating the ephemeral keys with a known key and IV found
// in the netDb.
func AESDeObfuscateEphemeralKeys(cipherText string, config noise.Config, bob router_address.RouterAddress) ([]byte, error) {
bobsStaticKey, err := bob.StaticKey()
if err != nil {
return nil, err
}
bobsInitializatonVector, err := bob.InitializationVector()
if err != nil {
return nil, err
}
log.WithFields(
log.Fields{
"at": "(noise) AESObfuscateEphemeralKeys",
}).Debugf("getting ready to deobfuscate our bob's ephemeral keys with bob's static key %s and IV %s", bobsStaticKey, bobsInitializatonVector)
deobfuscate, err := hex.DecodeString(cipherText)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(bobsStaticKey[:])
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, bobsInitializatonVector[:])
mode.CryptBlocks([]byte(deobfuscate), []byte(deobfuscate))
return deobfuscate, nil
}

View File

@@ -1,66 +0,0 @@
package noise
import (
"net"
"time"
)
type NoisePacketConn struct {
*Noise
// this is always a actually a PacketConn
net.Conn
}
// Read implements net.Conn.
func (*NoisePacketConn) Read(b []byte) (n int, err error) {
panic("unimplemented")
}
// RemoteAddr implements net.Conn.
func (n *NoisePacketConn) RemoteAddr() net.Addr {
panic("unimplemented")
}
// Write implements net.Conn.
func (*NoisePacketConn) Write(b []byte) (n int, err error) {
panic("unimplemented")
}
// Close implements net.PacketConn.
// Subtle: this method shadows the method (Conn).Close of NoisePacketConn.Conn.
func (n *NoisePacketConn) Close() error {
return n.Conn.Close()
}
// LocalAddr implements net.PacketConn.
func (n *NoisePacketConn) LocalAddr() net.Addr {
return &n.Noise.RouterInfo
}
// ReadFrom implements net.PacketConn.
func (*NoisePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
panic("unimplemented")
}
// SetDeadline implements net.PacketConn.
// Subtle: this method shadows the method (PacketConn).SetDeadline of NoisePacketConn.PacketConn.
func (n *NoisePacketConn) SetDeadline(t time.Time) error {
panic("unimplemented")
}
// SetReadDeadline implements net.PacketConn.
// Subtle: this method shadows the method (PacketConn).SetReadDeadline of NoisePacketConn.PacketConn.
func (n *NoisePacketConn) SetReadDeadline(t time.Time) error {
panic("unimplemented")
}
// SetWriteDeadline implements net.PacketConn.
// Subtle: this method shadows the method (PacketConn).SetWriteDeadline of NoisePacketConn.PacketConn.
func (n *NoisePacketConn) SetWriteDeadline(t time.Time) error {
panic("unimplemented")
}
// WriteTo implements net.PacketConn.
func (*NoisePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
panic("unimplemented")
}

View File

@@ -1,13 +0,0 @@
package noise
import (
"testing"
)
func TestServer(t *testing.T) {
}
func TestEstablishment(t *testing.T) {
}

View File

@@ -0,0 +1,92 @@
package noise
import (
"bytes"
"crypto/rand"
"errors"
"io"
"github.com/sirupsen/logrus"
"github.com/flynn/noise"
)
func (c *NoiseSession) RunOutgoingHandshake() error {
log.Debug("Starting outgoing handshake")
negData, msg, state, err := ComposeInitiatorHandshakeMessage(c.HandKey, nil, nil, nil)
if err != nil {
log.WithError(err).Error("Failed to compose initiator handshake message")
return err
}
log.WithFields(logrus.Fields{
"negData_length": len(negData),
"msg_length": len(msg),
}).Debug("Initiator handshake message composed")
c.HandshakeState = &HandshakeState{
HandshakeState: state,
}
if _, err = c.Write(negData); err != nil {
log.WithError(err).Error("Failed to write negotiation data")
return err
}
log.Debug("Negotiation data written successfully")
if _, err = c.Write(msg); err != nil {
log.WithError(err).Error("Failed to write handshake message")
return err
}
log.Debug("Handshake message written successfully")
log.WithField("state", state).Debug("Handshake state after message write")
log.Println(state)
c.handshakeComplete = true
log.Debug("Outgoing handshake completed successfully")
return nil
}
func ComposeInitiatorHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error) {
log.Debug("Starting ComposeInitiatorHandshakeMessage")
if len(rs) != 0 && len(rs) != noise.DH25519.DHLen() {
return nil, nil, nil, errors.New("only 32 byte curve25519 public keys are supported")
}
negData = make([]byte, 6)
copy(negData, initNegotiationData(nil))
pattern := noise.HandshakeXK
negData[5] = NOISE_PATTERN_XK
var random io.Reader
if len(ePrivate) == 0 {
random = rand.Reader
} else {
random = bytes.NewBuffer(ePrivate)
}
config := noise.Config{
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherAESGCM, noise.HashSHA256),
Pattern: pattern,
Initiator: true,
StaticKeypair: s,
Random: random,
}
state, err = noise.NewHandshakeState(config)
if err != nil {
return nil, nil, nil, err
}
// Write message, expecting no CipherStates yet since this is message 1
msg, cs0, cs1, err := state.WriteMessage(nil, payload)
if err != nil {
return nil, nil, nil, err
}
// Verify no CipherStates are returned yet
if cs0 != nil || cs1 != nil {
return nil, nil, nil, errors.New("unexpected cipher states in message 1")
}
return negData, msg, state, nil
}

View File

@@ -0,0 +1,125 @@
package noise
import (
"errors"
"sync/atomic"
"github.com/sirupsen/logrus"
)
func (c *NoiseSession) Read(b []byte) (int, error) {
log.WithField("buffer_length", len(b)).Debug("Starting NoiseSession Read")
// interlock with Close below
for {
x := atomic.LoadInt32(&c.activeCall)
if x&1 != 0 {
log.WithFields(logrus.Fields{
"at": "(NoiseSession) Read",
"reason": "session is closed",
}).Error("session is closed")
return 0, errors.New("session is closed")
}
if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
defer atomic.AddInt32(&c.activeCall, -2)
break
}
log.Debug("NoiseSession Read: retrying atomic operation")
}
if !c.handshakeComplete {
log.Debug("NoiseSession Read: handshake not complete, running incoming handshake")
if err := c.RunIncomingHandshake(); err != nil {
log.WithError(err).Error("NoiseSession Read: failed to run incoming handshake")
return 0, err
}
}
c.Mutex.Lock()
defer c.Mutex.Unlock()
if !c.handshakeComplete {
log.Error("NoiseSession Read: internal error - handshake still not complete after running")
return 0, errors.New("internal error")
}
n, err := c.readPacketLocked(b)
if err != nil {
log.WithError(err).Error("NoiseSession Read: failed to read packet")
} else {
log.WithField("bytes_read", n).Debug("NoiseSession Read: successfully read packet")
}
return n, err
}
func (c *NoiseSession) decryptPacket(data []byte) (int, []byte, error) {
log.WithField("data_length", len(data)).Debug("Starting packet decryption")
if c.CipherState == nil {
log.Error("Packet decryption: CipherState is nil")
return 0, nil, errors.New("CipherState is nil")
}
// Decrypt
decryptedData, err := c.CipherState.Decrypt(nil, nil, data)
if err != nil {
log.WithError(err).Error("Packet decryption: failed to decrypt data")
return 0, nil, err
}
m := len(decryptedData)
log.WithField("decrypted_length", m).Debug("Packet decryption: successfully decrypted data")
return m, decryptedData, nil
/*packet := c.InitializePacket()
maxPayloadSize := c.maxPayloadSizeForRead(packet)
if m > int(maxPayloadSize) {
m = int(maxPayloadSize)
}
if c.CipherState != nil {
////fmt.Println("writing encrypted packet:", m)
packet.reserve(uint16Size + uint16Size + m + macSize)
packet.resize(uint16Size + uint16Size + m)
copy(packet.data[uint16Size+uint16Size:], data[:m])
binary.BigEndian.PutUint16(packet.data[uint16Size:], uint16(m))
//fmt.Println("encrypt size", uint16(m))
} else {
packet.resize(len(packet.data) + len(data))
copy(packet.data[uint16Size:len(packet.data)], data[:m])
binary.BigEndian.PutUint16(packet.data, uint16(len(data)))
}
b := c.encryptIfNeeded(packet)*/
//c.freeBlock(packet)
}
func (c *NoiseSession) readPacketLocked(data []byte) (int, error) {
log.WithField("data_length", len(data)).Debug("Starting readPacketLocked")
var n int
if len(data) == 0 { // special case to answer when everything is ok during handshake
log.Debug("readPacketLocked: special case - reading 2 bytes during handshake")
if _, err := c.Conn.Read(make([]byte, 2)); err != nil {
log.WithError(err).Error("readPacketLocked: failed to read 2 bytes during handshake")
return 0, err
}
}
for len(data) > 0 {
m, b, err := c.encryptPacket(data)
if err != nil {
log.WithError(err).Error("readPacketLocked: failed to encrypt packet")
return 0, err
}
/*
if n, err := c.Conn.Read(b); err != nil {
return n, err
} else {
n += m
data = data[m:]
}
*/
n, err := c.Conn.Read(b)
if err != nil {
log.WithError(err).WithField("bytes_read (aka n)", n).Error("readPacketLocked: failed to read from connection")
return n, err
}
n += m
data = data[m:]
log.WithFields(logrus.Fields{
"bytes_read": n,
"remaining_data": len(data),
}).Debug("readPacketLocked: read packet chunk")
}
return n, nil
}

View File

@@ -0,0 +1,124 @@
package noise
import (
"fmt"
"net"
"sync"
"time"
"github.com/sirupsen/logrus"
cb "github.com/emirpasic/gods/queues/circularbuffer"
"github.com/flynn/noise"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/transport"
)
type NoiseSession struct {
router_info.RouterInfo
*noise.CipherState
*sync.Cond
*NoiseTransport // The parent transport, which "Dialed" the connection to the peer whith whom we established the session
*HandshakeState
RecvQueue *cb.Queue
SendQueue *cb.Queue
VerifyCallback VerifyCallbackFunc
activeCall int32
Conn net.Conn
}
// RemoteAddr implements net.Conn
func (noise_session *NoiseSession) RemoteAddr() net.Addr {
log.WithField("remote_addr", noise_session.RouterInfo.String()).Debug("Getting RemoteAddr")
return &noise_session.RouterInfo
}
// SetDeadline implements net.Conn
func (noise_session *NoiseSession) SetDeadline(t time.Time) error {
log.WithField("deadline", t).Debug("Setting deadline")
return noise_session.Conn.SetDeadline(t)
}
// SetReadDeadline implements net.Conn
func (noise_session *NoiseSession) SetReadDeadline(t time.Time) error {
log.WithField("read_deadline", t).Debug("Setting read deadline")
return noise_session.Conn.SetReadDeadline(t)
}
// SetWriteDeadline implements net.Conn
func (noise_session *NoiseSession) SetWriteDeadline(t time.Time) error {
log.WithField("write_deadline", t).Debug("Setting write deadline")
return noise_session.Conn.SetWriteDeadline(t)
}
var (
exampleNoiseSession transport.TransportSession = &NoiseSession{}
ExampleNoiseSession net.Conn = exampleNoiseSession.(*NoiseSession)
)
func (s *NoiseSession) LocalAddr() net.Addr {
localAddr := s.Conn.LocalAddr()
log.WithField("local_addr", localAddr.String()).Debug("Getting LocalAddr")
return s.Conn.LocalAddr()
}
func (s *NoiseSession) Close() error {
log.Debug("Closing NoiseSession")
s.SendQueue.Clear()
s.RecvQueue.Clear()
log.Debug("SendQueue and RecvQueue cleared")
return nil
}
func (c *NoiseSession) processCallback(publicKey []byte, payload []byte) error {
log.WithFields(logrus.Fields{
"public_key_length": len(publicKey),
"payload_length": len(payload),
}).Debug("Processing callback")
if c.VerifyCallback == nil {
log.Debug("VerifyCallback is nil, skipping verification")
return nil
}
err := c.VerifyCallback(publicKey, payload)
if err != nil {
log.WithError(err).Error("VerifyCallback failed")
} else {
log.Debug("VerifyCallback succeeded")
}
return err
}
// newBlock allocates a new packet, from hc's free list if possible.
func newBlock() []byte {
// return make([]byte, MaxPayloadSize)
block := make([]byte, MaxPayloadSize)
log.WithField("block_size", MaxPayloadSize).Debug("Created new block")
return block
}
type VerifyCallbackFunc func(publicKey []byte, data []byte) error
func NewNoiseTransportSession(ri router_info.RouterInfo) (transport.TransportSession, error) {
log.WithField("router_info", ri.String()).Debug("Creating new NoiseTransportSession")
// socket, err := DialNoise("noise", ri)
for _, addr := range ri.RouterAddresses() {
log.WithField("address", string(addr.Bytes())).Debug("Attempting to dial")
socket, err := net.Dial("tcp", string(addr.Bytes()))
if err != nil {
log.WithError(err).Error("Failed to dial address")
return nil, err
}
session := &NoiseSession{
SendQueue: cb.New(1024),
RecvQueue: cb.New(1024),
RouterInfo: ri,
Conn: socket,
}
log.WithField("local_addr", socket.LocalAddr().String()).Debug("NoiseTransportSession created successfully")
return session, nil
}
log.Error("Failed to create NoiseTransportSession, all addresses failed")
return nil, fmt.Errorf("Transport constructor error")
}

View File

@@ -0,0 +1 @@
package noise

View File

@@ -0,0 +1,177 @@
package noise
/**
* NoiseTransport is an unused transport which is used only for testing the
* transport interfaces. I2P adds obfuscation to NOISE with the NTCP2 protocol
* which is one of the transports which we use in practice.
**/
import (
"errors"
"net"
"sync"
"github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/router_identity"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/transport"
)
type NoiseTransport struct {
sync.Mutex
router_identity.RouterIdentity
Listener net.Listener
peerConnections map[data.Hash]transport.TransportSession
}
func (noopt *NoiseTransport) Compatible(routerInfo router_info.RouterInfo) bool {
// TODO implement
// panic("implement me")
log.Warn("func (noopt *NoiseTransport) Compatible(routerInfo router_info.RouterInfo) is not implemented!")
return true
}
var exampleNoiseTransport transport.Transport = &NoiseTransport{}
// ExampleNoiseListener is not a real Noise Listener, do not use it.
// It is exported so that it can be confirmed that the transport
// implements net.Listener
var ExampleNoiseListener net.Listener = exampleNoiseTransport
// Accept a connection on a listening socket.
func (noopt *NoiseTransport) Accept() (net.Conn, error) {
log.Debug("NoiseTransport: Accepting new connection")
// return noopt.Listener.Accept()
conn, err := noopt.Listener.Accept()
if err != nil {
log.WithError(err).Error("NoiseTransport: Failed to accept connection")
} else {
log.WithField("remote_addr", conn.RemoteAddr().String()).Debug("NoiseTransport: Accepted new connection")
}
return conn, err
}
// Addr of the transport, for now this is returning the IP:Port the transport is listening on,
// but this might actually be the router identity
func (noopt *NoiseTransport) Addr() net.Addr {
// return noopt.Listener.Addr()
addr := noopt.Listener.Addr()
log.WithField("addr", addr.String()).Debug("NoiseTransport: Returning address")
return addr
}
// Name of the transport TYPE, in this case `noise`
func (noopt *NoiseTransport) Name() string {
return "noise"
}
// SetIdentity will set the router identity for this transport.
// will bind if the underlying socket is not already
// if the underlying socket is already bound update the RouterIdentity
// returns any errors that happen if they do
func (noopt *NoiseTransport) SetIdentity(ident router_identity.RouterIdentity) (err error) {
log.WithField("identity", ident).Debug("NoiseTransport: Setting identity")
noopt.RouterIdentity = ident
if noopt.Listener == nil {
log.WithFields(logrus.Fields{
"at": "(NoiseTransport) SetIdentity",
"reason": "network socket is null",
}).Error("network socket is null")
err = errors.New("network socket is null")
return
}
log.Debug("NoiseTransport: Identity set successfully")
return nil
}
// Obtain a transport session with a router given its RouterInfo.
// If a session with this router is NOT already made attempt to create one and block until made or until an error happens
// returns an established TransportSession and nil on success
// returns nil and an error on error
func (noopt *NoiseTransport) GetSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error) {
hash := routerInfo.IdentHash()
log.WithField("hash", hash).Debug("NoiseTransport: Getting session")
if len(hash) == 0 {
log.Error("NoiseTransport: RouterInfo has no IdentityHash")
return nil, errors.New("NoiseTransport: GetSession: RouterInfo has no IdentityHash")
}
if t, ok := noopt.peerConnections[hash]; ok {
log.Debug("NoiseTransport: Existing session found")
return t, nil
}
log.Debug("NoiseTransport: Creating new session")
var err error
if noopt.peerConnections[hash], err = NewNoiseTransportSession(routerInfo); err != nil {
log.WithError(err).Error("NoiseTransport: Failed to create new session")
return noopt.peerConnections[hash], err
}
log.Debug("NoiseTransport: New session created successfully")
return nil, err
}
func (c *NoiseTransport) getSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error) {
log.WithField("router_info", routerInfo.String()).Debug("NoiseTransport: Getting session (internal)")
session, err := c.GetSession(routerInfo)
if err != nil {
log.WithError(err).Error("NoiseTransport: Failed to get session")
return nil, err
}
for {
if session.(*NoiseSession).handshakeComplete {
log.Debug("NoiseTransport: Handshake complete")
return nil, nil
}
if session.(*NoiseSession).Cond == nil {
log.Debug("NoiseTransport: No condition variable, breaking")
break
}
log.Debug("NoiseTransport: Waiting for handshake to complete")
session.(*NoiseSession).Cond.Wait()
}
log.Debug("NoiseTransport: Returning session")
return session, nil
}
// Compatable return true if a routerInfo is compatable with this transport
func (noopt *NoiseTransport) Compatable(routerInfo router_info.RouterInfo) bool {
_, ok := noopt.peerConnections[routerInfo.IdentHash()]
log.WithFields(logrus.Fields{
"router_info": routerInfo.String(),
"compatible": ok,
}).Debug("NoiseTransport: Checking compatibility")
return ok
}
// close the transport cleanly
// blocks until done
// returns an error if one happens
func (noopt *NoiseTransport) Close() error {
log.Debug("NoiseTransport: Closing transport")
return nil
}
// NewNoiseTransport create a NoiseTransport using a supplied net.Listener
func NewNoiseTransport(netSocket net.Listener) *NoiseTransport {
log.WithField("listener_addr", netSocket.Addr().String()).Debug("Creating new NoiseTransport")
return &NoiseTransport{
peerConnections: make(map[data.Hash]transport.TransportSession),
Listener: netSocket,
}
}
// NewNoiseTransportSocket creates a Noise transport socket with a random
// host and port.
func NewNoiseTransportSocket() (*NoiseTransport, error) {
log.Debug("Creating new NoiseTransportSocket")
netSocket, err := net.Listen("tcp", "")
if err != nil {
log.WithError(err).Error("Failed to create listener for NoiseTransportSocket")
return nil, err
}
// return NewNoiseTransport(netSocket), nil
_transport := NewNoiseTransport(netSocket)
log.WithField("addr", netSocket.Addr().String()).Debug("Created new NoiseTransportSocket")
return _transport, nil
}

View File

@@ -0,0 +1,33 @@
package noise
import (
"net"
"testing"
)
func TestTransport(t *testing.T) {
ln, err := net.Listen("tcp", ":42069")
if err != nil {
t.Error(err)
}
nt := NewNoiseTransport(ln)
go func() {
for {
conn, err := nt.Accept()
if err != nil {
t.Log(err)
}
_, err = conn.Write([]byte("World"))
if err != nil {
t.Error(err)
}
}
}()
lnn, err := net.Listen("tcp", ":42070")
if err != nil {
t.Error(err)
}
ntt := NewNoiseTransport(lnn)
t.Log(ntt.Name())
// ntt.GetSession()
}

View File

@@ -0,0 +1,133 @@
package noise
import (
"encoding/binary"
"errors"
"fmt"
"sync/atomic"
"github.com/sirupsen/logrus"
)
func (c *NoiseSession) Write(b []byte) (int, error) {
log.WithField("data_length", len(b)).Debug("NoiseSession: Starting Write operation")
// interlock with Close below
for {
x := atomic.LoadInt32(&c.activeCall)
if x&1 != 0 {
log.WithFields(logrus.Fields{
"at": "(NoiseSession) Write",
"reason": "session is closed",
}).Error("session is closed")
return 0, errors.New("session is closed")
}
if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
defer atomic.AddInt32(&c.activeCall, -2)
break
}
log.Debug("NoiseSession: Write - retrying atomic operation")
}
if !c.handshakeComplete {
log.Debug("NoiseSession: Write - handshake not complete, running outgoing handshake")
if err := c.RunOutgoingHandshake(); err != nil {
log.WithError(err).Error("NoiseSession: Write - failed to run outgoing handshake")
return 0, err
}
}
c.Mutex.Lock()
defer c.Mutex.Unlock()
if !c.handshakeComplete {
log.Error("NoiseSession: Write - internal error, handshake still not complete")
return 0, errors.New("internal error")
}
n, err := c.writePacketLocked(b)
if err != nil {
log.WithError(err).Error("NoiseSession: Write - failed to write packet")
} else {
log.WithField("bytes_written", n).Debug("NoiseSession: Write - successfully wrote packet")
}
return n, err
}
func (c *NoiseSession) encryptPacket(data []byte) (int, []byte, error) {
log.WithField("data_length", len(data)).Debug("NoiseSession: Starting packet encryption")
m := len(data)
if c.CipherState == nil {
log.Error("NoiseSession: encryptPacket - CipherState is nil")
return 0, nil, errors.New("CipherState is nil")
}
// Encrypt the data
encryptedData, err := c.CipherState.Encrypt(nil, nil, data)
if err != nil {
log.WithError(err).Error("NoiseSession: encryptPacket - failed to encrypt data")
return 0, nil, fmt.Errorf("failed to encrypt: '%w'", err)
}
// m := len(encryptedData)
lengthPrefix := make([]byte, 2)
binary.BigEndian.PutUint16(lengthPrefix, uint16(len(encryptedData)))
// Append encr data to prefix
packet := append(lengthPrefix, encryptedData...)
log.WithFields(logrus.Fields{
"original_length": m,
"encrypted_length": len(encryptedData),
"packet_length": len(packet),
}).Debug("NoiseSession: encryptPacket - packet encrypted successfully")
return m, packet, nil
/*packet := c.InitializePacket()
maxPayloadSize := c.maxPayloadSizeForWrite(packet)
if m > int(maxPayloadSize) {
m = int(maxPayloadSize)
}
if c.CipherState != nil {
////fmt.Println("writing encrypted packet:", m)
packet.reserve(uint16Size + uint16Size + m + macSize)
packet.resize(uint16Size + uint16Size + m)
copy(packet.data[uint16Size+uint16Size:], data[:m])
binary.BigEndian.PutUint16(packet.data[uint16Size:], uint16(m))
//fmt.Println("encrypt size", uint16(m))
} else {
packet.resize(len(packet.data) + len(data))
copy(packet.data[uint16Size:len(packet.data)], data[:m])
binary.BigEndian.PutUint16(packet.data, uint16(len(data)))
}
b := c.encryptIfNeeded(packet)*/
//c.freeBlock(packet)
}
func (c *NoiseSession) writePacketLocked(data []byte) (int, error) {
log.WithField("data_length", len(data)).Debug("NoiseSession: Starting writePacketLocked")
var n int
if len(data) == 0 { // special case to answer when everything is ok during handshake
log.Debug("NoiseSession: writePacketLocked - special case, writing 2 empty bytes")
if _, err := c.Conn.Write(make([]byte, 2)); err != nil {
log.WithError(err).Error("NoiseSession: writePacketLocked - failed to write empty bytes")
return 0, err
}
}
for len(data) > 0 {
m, b, err := c.encryptPacket(data)
if err != nil {
log.WithError(err).Error("NoiseSession: writePacketLocked - failed to encrypt packet")
return 0, err
}
if n, err := c.Conn.Write(b); err != nil {
log.WithError(err).WithField("bytes_written", n).Error("NoiseSession: writePacketLocked - failed to write to connection")
return n, err
} else {
n += m
data = data[m:]
log.WithFields(logrus.Fields{
"bytes_written": n,
"remaining_data": len(data),
}).Debug("NoiseSession: writePacketLocked - wrote packet chunk")
}
}
log.WithField("total_bytes_written", n).Debug("NoiseSession: writePacketLocked - completed writing all packets")
return n, nil
}

View File

@@ -0,0 +1,34 @@
package ntcp
import (
"math"
"github.com/flynn/noise"
)
const (
NOISE_DH_CURVE25519 = 1
NOISE_CIPHER_CHACHAPOLY = 1
NOISE_CIPHER_AESGCM = 2
NOISE_HASH_SHA256 = 3
NOISE_PATTERN_XK = 11
uint16Size = 2 // uint16 takes 2 bytes
MaxPayloadSize = math.MaxUint16 - 16 /*mac size*/ - uint16Size /*data len*/
)
var ciphers = map[byte]noise.CipherFunc{
NOISE_CIPHER_CHACHAPOLY: noise.CipherChaChaPoly,
NOISE_CIPHER_AESGCM: noise.CipherAESGCM,
}
var hashes = map[byte]noise.HashFunc{
NOISE_HASH_SHA256: noise.HashSHA256,
}
var patterns = map[byte]noise.HandshakePattern{
NOISE_PATTERN_XK: noise.HandshakeXK,
}

View File

@@ -1,5 +1,101 @@
package ntcp
// Session implements TransportSession
// An established transport session
type Session struct{}
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/transport/noise"
)
/*
Summary of what needs to be done:
NTCP and SSU2 are both transport protocols based on noise, with additional features designed to prevent p2p traffic from being blocked by firewalls.
These modifications affect how the Noise handshake takes place, in particular:
- Ephemeral keys are transmitted **obfuscated** by encrypting them with the peer's known static public key.
these modifications are simple enough, but for our purposes we also want to be able to re-use as much code as possible.
So, what we need to do is devise a means of adding these modifications to the existing NoiseSession implementation.
We could do this in any number of ways, we could:
1. Implement a custom struct that embeds a NoiseSession and overrides the Compose*HandshakeMessage functions
2. Modify the NoiseSession handshake functions to allow passing an obfuscation and/or padding function as a parameter
3. Modify the NoiseSession implementation to allow replacing the Compose*HandshakeMessage functions with custom ones
4. Refactor the NoiseSession implementation to break Compose*HandshakeMessage out into a separate interface, and implement that interface in a custom struct
Ideally, we're already set up to do #1, but we'll see how it goes.
Now is the right time to make changes if we need to, go-i2p is the only consumer of go-i2p right now, we can make our lives as easy as we want to.
*/
// NTCP2Session extends the base noise.NoiseSession with NTCP2-specific functionality
type NTCP2Session struct {
*noise.NoiseSession
paddingStrategy PaddingStrategy
}
// NewNTCP2Session creates a new NTCP2 session using the existing noise implementation
func NewNTCP2Session(noiseConfig router_info.RouterInfo) (*NTCP2Session, error) {
baseNoiseSession, err := noise.NewNoiseTransportSession(noiseConfig)
if err != nil {
return nil, err
}
return &NTCP2Session{
NoiseSession: baseNoiseSession.(*noise.NoiseSession),
}, nil
}
type PaddingStrategy interface {
AddPadding(message []byte) []byte
RemovePadding(message []byte) []byte
}
// PeerStaticKey is equal to the NTCP2 peer's static public key, found in their router info
func (s *NTCP2Session) peerStaticKey() ([32]byte, error) {
for _, addr := range s.RouterInfo.RouterAddresses() {
transportStyle, err := addr.TransportStyle().Data()
if err != nil {
continue
}
if transportStyle == NTCP_PROTOCOL_NAME {
return addr.StaticKey()
}
}
return [32]byte{}, fmt.Errorf("Remote static key error")
}
// ObfuscateEphemeral implements NTCP2's key obfuscation using AES-256-CBC
func (s *NTCP2Session) ObfuscateEphemeral(key []byte) ([]byte, error) {
static, err := s.peerStaticKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(static[:])
if err != nil {
return nil, err
}
obfuscated := make([]byte, len(key))
iv := make([]byte, aes.BlockSize)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(obfuscated, key)
return obfuscated, nil
}
// DeobfuscateEphemeral reverses the key obfuscation
func (s *NTCP2Session) DeobfuscateEphemeral(obfuscated []byte) ([]byte, error) {
static, err := s.peerStaticKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(static[:])
if err != nil {
return nil, err
}
key := make([]byte, len(obfuscated))
iv := make([]byte, aes.BlockSize)
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(key, obfuscated)
return key, nil
}

View File

@@ -4,11 +4,85 @@ package ntcp
* https://geti2p.net/spec/ntcp2
**/
import (
"fmt"
"net"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/transport"
"github.com/go-i2p/go-i2p/lib/transport/noise"
)
const (
NTCP_PROTOCOL_VERSION = 2
NTCP_PROTOCOL_NAME = "NTCP2"
NTCP_MESSAGE_MAX_SIZE = 65537
)
// Transport is an ntcp transport implementing transport.Transport interface
type Transport struct{}
var exampleNTCPTransport transport.Transport = &Transport{}
// Transport is an ntcp2 transport implementing transport.Transport interface
type Transport struct {
*noise.NoiseTransport
}
func (t *Transport) Name() string {
return NTCP_PROTOCOL_NAME
}
func (t *Transport) Compatible(routerInfo router_info.RouterInfo) bool {
// Check if the router info contains NTCP2 address and capabilities
addresses := routerInfo.RouterAddresses()
for _, addr := range addresses {
transportStyle, err := addr.TransportStyle().Data()
if err != nil {
continue
}
if transportStyle == NTCP_PROTOCOL_NAME {
return true
}
}
return false
}
func (t *Transport) GetSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error) {
// Create new NTCP2 session
session, err := NewNTCP2Session(routerInfo)
if err != nil {
return nil, err
}
// Perform handshake
if err := session.Handshake(routerInfo); err != nil {
return nil, err
}
return session, nil
}
func (t *Transport) Accept() (net.Conn, error) {
conn, err := t.NoiseTransport.Accept()
if err != nil {
return nil, err
}
// check if remote router address contains a compatible transport
// first get the RemoteAddr
remoteAddr := conn.LocalAddr()
// then check if it's a router address
routerAddr, ok := remoteAddr.(*router_info.RouterInfo)
if !ok {
return nil, fmt.Errorf("remote address is not a router address")
}
// then check if it's compatible
if !t.Compatible(*routerAddr) {
return nil, fmt.Errorf("remote router address is not compatible with NTCP2")
}
// Wrap connection with NTCP2 session
session, err := NewNTCP2Session(remoteAddr.(router_info.RouterInfo)) // nil for incoming connections
if err != nil {
conn.Close()
return nil, err
}
return session, nil
}

View File

@@ -0,0 +1,61 @@
package obfs
import (
"fmt"
"github.com/go-i2p/go-i2p/lib/crypto"
)
// ObfuscateEphemeralKey encrypts the ephemeral public key in the message using AES-256-CBC without padding
func ObfuscateEphemeralKey(message []byte, aesKey *crypto.AESSymmetricKey) ([]byte, error) {
if len(message) < 32 {
return nil, fmt.Errorf("message is too short to contain ephemeral public key")
}
// Extract the ephemeral public key (first 32 bytes)
ephemeralPubKey := message[:32]
// Create AES encrypter
encrypter := &crypto.AESSymmetricEncrypter{
Key: aesKey.Key,
IV: aesKey.IV,
}
// Encrypt the ephemeral public key without padding
encryptedKey, err := encrypter.EncryptNoPadding(ephemeralPubKey)
if err != nil {
return nil, err
}
// Replace the ephemeral public key in the message with the encrypted key
obfuscatedMessage := append(encryptedKey, message[32:]...)
return obfuscatedMessage, nil
}
// DeobfuscateEphemeralKey decrypts the ephemeral public key in the message using AES-256-CBC without padding
func DeobfuscateEphemeralKey(message []byte, aesKey *crypto.AESSymmetricKey) ([]byte, error) {
if len(message) < 32 {
return nil, fmt.Errorf("message is too short to contain ephemeral public key")
}
// Extract the encrypted ephemeral public key (first 32 bytes)
encryptedKey := message[:32]
// Create AES decrypter
decrypter := &crypto.AESSymmetricDecrypter{
Key: aesKey.Key,
IV: aesKey.IV,
}
// Decrypt the ephemeral public key without padding
decryptedKey, err := decrypter.DecryptNoPadding(encryptedKey)
if err != nil {
return nil, err
}
// Replace the encrypted ephemeral key in the message with the decrypted key
deobfuscatedMessage := append(decryptedKey, message[32:]...)
return deobfuscatedMessage, nil
}

Some files were not shown because too many files have changed in this diff Show More