250 Commits

Author SHA1 Message Date
ddba94d6ae remove printing 2024-11-15 22:48:57 -05:00
767b91df49 clean up temp dirs 2024-11-15 22:47:36 -05:00
1292098cf0 Merge remote-tracking branch 'origin/10k' into 10k 2024-11-15 22:45:04 -05:00
24bc4c3c17 Implemented ed25519 SPK's 2024-11-15 22:29:42 -05:00
81eb270351 !WIP! - 10k test 2024-11-15 22:06:23 -05:00
b6f197cf92 This is not correct yet, work on key_certificate.go lines 216-245 2024-11-15 17:35:44 -05:00
c10d98a3b2 export DEBUG_I2P=debug in Makefile so that extended logs show up in the tests 2024-11-15 16:52:37 -05:00
6d16ca5f87 debugging info to investigate 2024-11-15 14:43:36 -05:00
003d6c9ab8 !WIP! - 10k test 2024-11-15 13:53:44 -05:00
015c4b23e2 pass sigType to NewRouterInfo 2024-11-15 11:49:10 -05:00
e29c3c7abb formatting + implemented GetSignatureTypeFromCertificate 2024-11-15 11:31:29 -05:00
6f6291a9f6 bounds checking 2024-11-15 11:31:10 -05:00
767864d457 corrected order in Bytes() 2024-11-15 11:30:59 -05:00
0a98236d85 adjusted test 2024-11-15 11:30:40 -05:00
c1fa63f6ec added sigtype 2024-11-15 11:30:34 -05:00
a75c275b4c Corrected Bytes()
-TODO: add logging
2024-11-15 11:30:11 -05:00
d40b3e0cd3 added sig types 2024-11-15 11:14:05 -05:00
2ee2d77d7c check if data slice is empty 2024-11-15 11:01:06 -05:00
271cf56ded Fixes the padding generation but the reader still thinks something's wrong 2024-11-10 19:29:07 -05:00
a29fa0bc03 Output the rest of the key certificate, that would probably help... 2024-11-10 17:15:56 -05:00
63c48dd3b7 log the actual bytes in the test for now, make sure they're really there 2024-11-09 13:41:19 -05:00
8bec47efd2 log the actual bytes in the test for now, make sure they're really there 2024-11-09 13:39:38 -05:00
69a50e2035 Try adding a dummy address, change logging to show where not enough data was provided 2024-11-09 13:33:23 -05:00
8319444890 Try adding a dummy address, change logging to show where not enough data was provided 2024-11-09 13:31:24 -05:00
b378661e0e Fix LS test, update Noise Subsystem on README.md 2024-11-09 12:33:53 -05:00
f4086e5f68 !WIP! - Router Info 2024-11-09 11:13:48 -05:00
877fc707c4 !WIP! - Router Info 2024-11-09 01:04:44 -05:00
98d05e27c8 !WIP! - Router Info 2024-11-09 00:53:17 -05:00
4020db8a19 Refactor: Certificate Constructor: 2024-11-04 19:19:03 -05:00
67a02f5d69 Noise: Refactor, remove unused fields 2024-11-04 18:26:17 -05:00
ca1280231c Tidy 2024-11-04 15:25:54 -05:00
02b309df43 Refactor: move HandshakeState to own struct in preparation for NTCP2 mods 2024-11-04 15:20:56 -05:00
a5b3c3f194 NTCP2: Stub out NTCP2 and explain the task at hand, SSU2: Explain the plan 2024-11-04 00:24:08 -05:00
0aa7a5554b Merge branch 'master' of github.com:go-i2p/go-i2p 2024-11-03 23:59:13 -05:00
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
db0fd9f7e9 Management: rm PASTA.md 2024-11-03 15:53:23 -05:00
3ab258cde6 Management: add ROADMAP.md 2024-11-03 15:53:13 -05:00
20b9bbd8e4 go mod tidy & fixed condition that was always true 2024-11-03 01:52:48 -05:00
1fa520613c replace deprecated function (ioutil -> os) 2024-11-03 00:14:45 -04:00
fb99b98a7e minor typo fix 2024-11-02 21:37:56 -04:00
d6b8cd9d4d go mod tidy 2024-11-02 21:37:29 -04:00
92e4656774 move from ~/go-i2p to ~/.go-i2p 2024-11-02 21:37:23 -04:00
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
a17f0208dd gofumpt adjustment 2024-10-31 10:53:22 -04:00
487815f8f1 tests 2024-10-31 10:50:21 -04:00
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
a5d2f0de8c lib/transport/noise/doc.md 2024-10-31 00:01:22 -04:00
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
39b683dac8 fixed circular dependency 2024-10-30 11:20:15 -04:00
46883f6457 build fails -> import cycle 2024-10-30 09:31:01 -04:00
4be55062fc Moved config funcs to config.go 2024-10-30 09:22:32 -04:00
12a3fd4623 Added config files + args
-added routerInstance
-added initConfig()
-added updateRouterConfig()
-yaml format
2024-10-30 09:16:22 -04:00
80e539930e Mapping: fix off-by-one. Format: gofumpt 2024-10-28 18:11:54 -04:00
e58d326d89 Move obfuscation functions to lib/transport/ntcp 2024-10-25 21:29:02 -04:00
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
971a18de8d added Compatible warning message 2024-10-24 19:23:36 -04:00
833836ae67 added TestEncryptDecryptPacketObfsOffline 2024-10-24 18:58:07 -04:00
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
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
748dc8a66f tweaks 2024-10-23 00:11:46 -04:00
677aac500e fix logger naming collision with other libs 2024-10-23 00:06:06 -04:00
4e06d3d5ed makefiles 2024-10-22 22:31:19 -04:00
5eaa9cf588 log name collision fix 2024-10-22 22:10:14 -04:00
4dbf537e94 log name collision fix 2024-10-22 22:07:39 -04:00
882c018c0c log name collision fix 2024-10-22 22:06:06 -04:00
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
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
647546a374 typo fix 2024-10-22 14:51:17 -04:00
e468520906 Merge branch 'master' into sntp-experimental 2024-10-20 00:09:42 -04:00
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
f45d301868 proposed refactor
-added secureRandBool() and performTimeQuery()
2024-10-19 22:17:10 -04:00
0256908395 CRITICAL FIX, bytesRead -> n.
n wasn't being used before
2024-10-19 17:27:39 -04:00
3c5aa206d1 expanded logging in message.go 2024-10-19 12:32:49 -04:00
a4517cafd7 expanded logging in delivery.go 2024-10-19 12:25:31 -04:00
f4f39ca53c expanded logging in multi.go 2024-10-19 11:18:20 -04:00
220159476a expanded logging in write_session.go 2024-10-19 11:09:37 -04:00
792cd49208 expanded logging in transport.go 2024-10-19 11:03:55 -04:00
68051630c0 expanded logging in session.go 2024-10-19 10:55:30 -04:00
a3340eb40a expanded logging in read_session.go + changed structure in readPacketLocked() 2024-10-19 10:50:13 -04:00
465a7787a9 expanded logging in outgoing_handshake.go 2024-10-19 10:44:08 -04:00
af3bc44dba expanded logging in incoming_handshake.go 2024-10-19 10:39:23 -04:00
f850f482cf expanded logging in handshake.go 2024-10-19 10:30:11 -04:00
3f23376d22 expanded logging in su3.go 2024-10-19 10:19:55 -04:00
aa98589f1c expanded logging in router.go 2024-10-19 09:46:23 -04:00
c31e990995 expanded logging in std.go 2024-10-18 23:06:44 -04:00
8e97eb5f77 expanded logging in reseed.go 2024-10-18 22:54:08 -04:00
be35267079 expanded logging in header.go 2024-10-18 22:47:28 -04:00
013d35b447 expanded logging in build_request_record.go 2024-10-18 22:41:40 -04:00
e8c9dc17a3 expanded logging in tunnel.go 2024-10-18 21:58:50 -04:00
a1d574446b expanded logging in elg.go 2024-10-18 21:54:27 -04:00
8680acc595 expanded logging in ed25519.go 2024-10-18 21:42:07 -04:00
84e3c5accc expanded logging in ecdsa.go 2024-10-18 17:01:42 -04:00
e772fb5ceb expanded logging in dsa.go 2024-10-18 16:56:25 -04:00
a533cd7ce4 expanded logging in curve25519.go 2024-10-18 16:39:28 -04:00
df37d49444 expanded logging to aes.go 2024-10-18 15:10:16 -04:00
4b2600a065 expanded logging to signature.go
-changed sessionTag -> sig
2024-10-18 14:55:30 -04:00
1c4f937002 expanded logging to session_tag.go 2024-10-18 14:51:53 -04:00
5c2b408f65 expanded logging to session_key.go 2024-10-18 14:43:59 -04:00
beb533a09b expanded logging to router_info.go 2024-10-18 14:40:52 -04:00
f022522ad5 expanded logging to router_identity.go 2024-10-18 14:27:41 -04:00
2191c40ac6 expanded logging to router_address.go + gofumpt adjustment 2024-10-18 13:22:36 -04:00
3bca467f28 updated README.md to reflect new logging paradigm. 2024-10-18 12:59:49 -04:00
a2fd65ee32 expanded logging in lease_set.go 2024-10-18 12:55:35 -04:00
b894e8fb17 expanded logging in keys_and_cert.go 2024-10-18 12:41:48 -04:00
93a71c7398 expanded logging in key_certificate.go 2024-10-18 12:27:42 -04:00
b6544ad194 expanded logging in destination.go 2024-10-18 12:16:36 -04:00
a72b61a886 expanded logging in string.go 2024-10-18 12:08:27 -04:00
dda4f90b6f expanded logging in mapping_values.go 2024-10-18 12:00:58 -04:00
1d1d8126c2 adjusted logging in main.go 2024-10-18 11:53:01 -04:00
73db39ae50 expanded logging in mapping.go 2024-10-18 11:52:39 -04:00
53e902f491 expanded logging to date.go 2024-10-18 11:49:10 -04:00
2f2cd2249c expanded logging to certificate.go 2024-10-18 11:48:56 -04:00
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
a7689e801a minor typo 2024-10-06 12:13:04 -04:00
278bdee277 Various changes
-Aes -> AES
-doc.md
2024-10-06 10:48:22 -04:00
684e89c957 minor typo 2024-10-05 10:15:31 -04:00
50fa9fc374 gofumpt adjustment 2024-10-05 09:43:58 -04:00
491b25022e AES
-Revamped encrypt and decrypt to fit with interfaces
-Adjusted test cases for TestPKCS7UnpadInvalidInput()
2024-10-05 09:00:33 -04:00
677a6b354b AES test 2024-10-04 22:37:57 -04:00
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
2f109d5b4d Added gofumpt check 2024-10-03 23:24:12 -04:00
d7378d7b08 more merge conflicts 2024-10-03 22:46:39 -04:00
3a51a1229e more merge conflicts 2024-10-03 22:45:33 -04:00
2b18b2941d resolve merge conflicts in lib/transport/noise 2024-10-03 22:42:00 -04:00
1eb29ec4ab formatting issues only 2024-10-03 22:36:23 -04:00
d900d7faf8 add doc.md files from master 2024-10-03 22:10:55 -04:00
03c9d60ab9 update other non-transport parts of lib 2024-10-03 22:09:16 -04:00
de2caf499e use makefile from Master 2024-10-03 21:58:16 -04:00
284dd7287e use makefile from Master 2024-10-03 21:56:58 -04:00
9f4154ff45 gofmt 2024-10-03 21:55:35 -04:00
08a0d92742 gofmt 2024-10-03 21:52:49 -04:00
524526d946 Bring in crypto stubs from master 2024-10-03 21:41:31 -04:00
09c7d32797 update the common library to the master branch 2024-10-03 21:31:54 -04:00
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
8fa355f067 !WIP! router_timestamper 2024-09-29 22:03:30 -04:00
0c7a3f0f22 force static builds for slightly easier debugging 2024-09-09 20:13:20 -04:00
3d535f67a1 Work on curve25519 stuff so I can generate my own routerInfos instead of just sending crap to other people 2024-09-09 20:10:38 -04:00
bba9350506 WOO some of the important fields are actually populated with correct values 2024-08-28 20:47:54 -04:00
cbc0de4e7e Start working on de-obfuscating ephemeral keys from remote peers 2024-08-28 18:32:42 -04:00
310ef07d3c generate godoc locally so I can go over it offline 2024-08-26 16:21:54 -04:00
14fc6fc3a8 Work on noise tools, comment details of handshake stuff 2024-08-25 23:22:21 -04:00
a3ce9d36c6 If string data is shorter than specified by length create a new string based on the content after the first byte 2024-07-08 10:46:16 -04:00
58a43cdfaf Fix string length checks, sortof 2024-07-05 23:12:03 -04:00
15a5ca5daf Try and make noise work and also fix some tests 2024-07-05 18:12:58 -04:00
08a41686b6 Work on noise wrapper. It is now good enough to fail to connect to a remote I2P router in a way which is discernible on that remote router. 2024-07-03 01:19:31 -04:00
8318fd8f57 Work on noise wrapper. It is now good enough to fail to connect to a remote I2P router in a way which is discernible on that remote router. 2024-07-03 01:17:36 -04:00
9c0552e236 Make some netDb loading stuff work so I have RI's to talk to 2024-07-02 16:54:30 -04:00
20b018a708 Make some netDb loading stuff work so I have RI's to talk to 2024-07-02 16:53:57 -04:00
6c62faa49b finish stubbing out noise transport stuff 2024-07-01 21:37:05 -04:00
a7e31b7833 implement more noise stuff 2024-07-01 21:34:09 -04:00
c09161c824 check in beginnings of new cleaned-up noise prototype 2024-07-01 18:50:13 -04:00
aca62174e6 Load RI's into netDb struct for later referencing 2024-06-30 23:14:48 -04:00
bd27f00959 add my unzip fork which fixes the permissions bug 2024-06-29 00:36:11 -04:00
05c4d3d973 add my unzip fork which fixes the permissions bug 2024-06-29 00:25:23 -04:00
40d0ea5ff5 Start making it so I can configure things, so I can configure it to read the netDb I already have and attempt to make a connection. Implement a reseed puller, or at least most of one. 2024-06-29 00:23:55 -04:00
58e8f78c56 Start making it so I can configure things, so I can configure it to read the netDb I already have and attempt to make a connection. Implement a reseed puller, or at least most of one. 2024-06-29 00:23:42 -04:00
ac705dee76 Set up a pre-release 2024-06-25 12:04:18 -04:00
ca0443ae6a Set up a pre-release 2024-06-25 12:03:55 -04:00
94d0d8efb7 Set up a pre-release 2024-06-25 12:03:09 -04:00
8176e126cb Set up a pre-release 2024-06-25 12:02:49 -04:00
992c5537db Set up a pre-release 2024-06-25 12:02:11 -04:00
5ff9f5578e Update notes 2024-06-25 11:57:52 -04:00
889ec939b3 Eliminate superfluous/rules-breaking logging 2024-06-25 11:41:55 -04:00
6e3a5923cd Make MAX_GOOD_VERSION 99(0.9.99) 2024-06-25 09:35:39 -04:00
ce25a2f0fb Add functions to examine caps 2024-06-24 22:26:17 -04:00
ec22860bb5 Add Get function to MappingValues 2024-06-24 22:18:13 -04:00
cefdcd8c14 RI parsing works again with options and caps and everything 2024-06-24 21:18:54 -04:00
d519f7678d make logging clearer 2024-06-23 22:48:15 -04:00
8dfc5ee5b8 Fix some string errors 2024-06-23 22:39:11 -04:00
d8317351cf Write down my TODO thing 2024-06-23 03:41:40 -04:00
ef127d81c1 Work on figuring out why the mapping size disagrees with the bytes 2024-06-23 03:24:57 -04:00
8acfba160f Log some of the errors that came up after I fix the remainder emissions 2024-06-23 02:51:48 -04:00
d93e78d772 Fix how it handles remainders of structures when parsing routerInfos 2024-06-23 01:35:42 -04:00
bdc9ca4c2b Implement some missing ed25519 functions 2024-06-20 17:04:21 -04:00
83f3d11621 Update the roadmap to more accurately reflect the actual state of the code 2024-02-09 15:02:46 -05:00
784f72403b Fix padding, add TODO note, align spk to end of first 384 bytes 2024-02-05 12:41:49 -05:00
664a17e42a Fix construction of keys_and_cert from a stream of bytes 2024-02-05 12:32:33 -05:00
37718c36f8 Fix cert construction in keys_and_cert 2024-02-05 11:47:51 -05:00
b0fcf4bc11 Fix mapping values, remove redundant, unused functions 2024-02-05 10:42:57 -05: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
8d631239b7 Implements a mostly-working Noise IK handshake on a socket with multiple subsessions. Does not implement conns or listeners yet. 2022-10-17 02:03:59 -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
c6e359f152 Merge pull request #4 from Thuulium/MappingValues/TweakValuesSort
Rework MappingValues Sort
2022-09-20 11:14:51 -04:00
70415b34eb Tweak test message 2022-09-19 19:47:46 -04:00
dbd4c7d346 Allow duplicate keys, add back HasDuplicateKeys 2022-09-19 19:46:46 -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
43c3498389 Rework MappingValues Sort 2022-09-18 20:54:43 -04:00
idk
f90d1d5f42 Merge branch 'tweak-lib/common-godocs' into 'master'
Tweak godocs for lib/common

See merge request idk/go-i2p!4
2022-09-19 00:02:56 +00:00
c8c4196c6f Tweak godocs for lib/common 2022-09-12 04:31:02 -04:00
idk
68a6ed02be Merge branch 'unbreak-mappings' into 'master'
Attempt to fix mappings

See merge request idk/go-i2p!3
2022-09-11 19:13:49 +00:00
53dd3230df Attempt to fix mappings 2022-09-10 15:34:20 -04:00
idk
c3147c3570 Fix most of the router info tests, all errors are now Mapping-related. 2022-08-06 14:16:42 -04:00
idk
76fba3c8ba Merge pull request #3 from apeace/su3
This merges apeace's working SU3 implementation, which is more like idiomatic Go and will be easier to build upon. TODO: We need to write the godoc, we don't want that situation to get out of hand.
2022-08-05 00:55:57 -04:00
0cae80c698 documentation on novg.net reseed file 2022-08-03 16:14:21 -04:00
7deba5f725 when checking su3 signature, hash ALL file contents up to the signature 2022-08-03 16:10:27 -04:00
35983423ed update from PSS -> PKCS1v15 2022-08-02 15:46:06 -04:00
136133e643 add signature test for snowflake plugin 2022-08-02 11:50:02 -04:00
8d39f1512a add su3 test of snowflake plugin 2022-08-02 11:43:54 -04:00
c9b5fa1406 Add a test for verifying i2pgit reseed signature 2022-08-02 11:27:45 -04:00
cdc9998f10 improve su3 tests by only generating fake data once 2022-08-02 09:47:19 -04:00
4c06fcaff0 add su3 documentation on streaming to disk 2022-08-02 09:47:19 -04:00
403ef30119 verify su3 RSA signatures 2022-08-02 09:47:19 -04:00
feef6e6bbf refactor su3 readers to support signature verification 2022-08-02 09:47:19 -04:00
ad7a828d43 documentation on reading su3 from []byte 2022-08-02 09:47:19 -04:00
416cf6546a remove unused config/su3 implementation 2022-08-02 09:47:19 -04:00
9b81a5c6d8 package su3 documentation 2022-08-02 09:47:19 -04:00
940347351e package su3 documentation 2022-08-02 09:47:19 -04:00
726bff974c read SU3 files into io.Readers 2022-08-02 09:47:19 -04:00
74591a96cb read SU3 files into a struct 2022-08-02 09:47:19 -04:00
idk
eb117aa1bd Updates Go Modules and several tests.
Re: the tests, this happens largely because when you can check for errors changes fundamentally
based on whether or not you're just reading some bytes and storing them for later or whether
you're reading them and then immediately trying to store them in some intelligible datastructure.
The old code, before I started refactoring it, sort of split the difference by putting everything
into slices of bytes, but hanging functions off those byte slices to parse intellible structures
on the fly. The tests were largely designed around testing this format. Updating them has been
a menace. It is very difficult to tell which tests are necessary and how the necessary tests need
to be changed. A handful of high-level tests in router_info_test currently won't compile. I think
those are the only remaining broken tests.
2022-08-01 12:50:42 -04:00
idk
88d8d09527 write my notes for this week 2022-07-18 13:31:20 -04:00
idk
83e999992f start putting my copypasta in the repo 2022-07-18 12:48:49 -04:00
idk
a437bab373 Progress on pure-noise transport 2022-07-18 12:32:29 -04:00
idk
2d310fe91a Progress on pure-noise transport 2022-07-18 12:32:16 -04:00
idk
fefbfc4be6 move mapping_values to it's own file 2022-07-14 16:05:48 -04:00
idk
0ec4f55fa9 Check in unchecked-in common library fixes, start implementing transports.
Since I turned out to be implementing parts of Noise which I did not need to implement, I'm taking a different approach, and doing an unmodified Noise transport first and then making our modifications to it. That should reduce what I need to do to message pre-processing mostly, I think.
2022-07-11 23:41:58 -04:00
idk
8bd30d1e23 start pure-noise transport. Move messages to own module 2022-07-11 17:29:01 -04:00
idk
b7768d4d99 Work on Mapping tests, get ready to send Session messages for NTCP2 2022-07-11 10:33:33 -04:00
idk
171f09bba6 move every struct into it's own directory in order to run tests individually. 2022-05-22 19:59:20 -04:00
idk
13b996d0da Factor away byte slices in RouterInfo, KeysAndCert, Mapping, RouterAddress, RouterIdentity. 2022-05-09 20:43:24 -04:00
idk
82f0e86931 refactor Strings, Integers, Dates, Mapping, Certificates and KeyCertificates. The remaining structs will be checked in one-by-one. 2022-05-02 14:24:51 -04:00
idk
f113977419 refactor certs, keycerts 2022-04-27 10:50:10 -04:00
idk
86dc323348 refactor certs, keycerts 2022-04-27 10:48:59 -04:00
idk
a80c9973f9 Merge pull request #1 from eyedeekay/circleci-project-setup
Add .circleci/config.yml
2021-05-11 10:46:52 -07:00
idk
7adf3d7577 Add .circleci/config.yml 2021-05-11 07:34:35 -07:00
idk
42be0d6b5c Merge pull request #1 from kpetku/drop-libsodium-dep
remove libsodium dependency in favor of the native go/crypto ed25519 library
2021-05-07 20:07:10 -07:00
d837630ff6 remove libsodium dependency in favor of the native go/crypto ed25519 library 2021-05-07 21:02:54 -04:00
idk
590d576b74 Merge branch 'checklist' into 'master'
Checklist update: Add items, remove NTCP 1

See merge request idk/go-i2p!1
2021-04-27 14:10:02 +00:00
zzz
fa86f8e3b3 Checklist update: Add items, remove NTCP 1 2021-04-27 10:03:52 -04:00
240 changed files with 16576 additions and 4722 deletions

26
.circleci/config.yml Normal file
View File

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

5
.gitignore vendored
View File

@ -5,4 +5,7 @@
*.coverprofile
*exportable-fuzz.zip
go-i2p
*.exe*.log
*.exe
.idea/
router.info
log

9
.vscode/launch.json vendored
View File

@ -1,9 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
]
}

View File

@ -9,7 +9,6 @@ Install required dependencies
This example assumes Ubuntu 16.04
```sh
sudo apt-get install pkg-config libsodium-dev
go get github.com/hkparker/go-i2p
go get github.com/Sirupsen/logrus
go get github.com/stretchr/testify/assert

View File

@ -1,5 +1,9 @@
RELEASE_TAG=0.0.1
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
@ -13,19 +17,61 @@ 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 -v -o $(EXE)
$(GO) build --tags netgo,osusergo -v -o $(EXE)
test:
$(GO) test -failfast ./...
test: check_gofumpt fmt
$(GO) test -v -failfast ./lib/common/...
clean:
$(GO) clean -v
fmt:
find . -name '*.go' -exec gofmt -w -s {} \;
find . -name '*.go' -exec gofumpt -w {} \;
testcommon:
$(GO) test -failfast ./lib/common/...
info:
echo "GOROOT: ${GOROOT}"
echo "GO: ${GO}"
echo "REPO: ${REPO}"
release:
github-release release -u go-i2p -r go-i2p -n "${RELEASE_VERSION}" -t "${RELEASE_TAG}" -d "${RELEASE_DESCRIPTION}" -p
callvis:
go-callvis -format svg -focus upgrade -group pkg,type -limit github.com/go-i2p/go-i2p github.com/go-i2p/go-i2p
godoc:
find lib -type d -exec bash -c "ls {}/*.go && godocdown -o ./{}/doc.md ./{}" \;
# Include test definitions
-include doc/tests/*.mk
# Define the all-tests target that runs every test suite
test-all: test-string-all \
test-mapping-all \
test-crypto-aes-all \
test-crypto-dsa-all \
test-crypto-ed25519-all \
test-crypto-elg-all \
test-crypto-hmac-all \
test-i2np-header-all \
test-i2np-build-request-all \
test-key-cert-all \
test-keys-cert-all \
test-lease-set-all \
test-noise-transport-all \
test-router-address-all \
test-router-info-all \
test-su3-all \
test-tunnel-all
#-include $(shell find doc/tests -type f -name '*.mk') #search for .mk files recursively
#test-base64-encode-decode-not-mangled:
#go test -v ./lib/common/base64 -run TestEncodeDecodeNotMangled

142
README.md
View File

@ -4,35 +4,20 @@ A pure Go implementation of the I2P router.
## Status
go-i2p was in early development. Now it's being restructured in some
fundamental ways, so it's even less done than before(on this branch, for now)
but when this restructuring is complete, it will be a fully-fledged I2P router
and library for writing, embedding, and possiblly extending I2P routers in Go
applications.
The go module is declared as: `github.com/go-i2p/go-i2p`, in order to clone
anonymously you may use `torsocks` with `go get`(YMMV) or you may clone
it from git.idk.i2p using:
#Set your $GOPATH, if it isn't set already then GOPATH=$HOME/go
$GOPATH/go/src/i2pgit.org/idk/
git clone git@127.0.0.1:idk/go-i2p $GOPATH/go/src/github.com/go-i2p/go-i2p
$GOPATH/go/src/github.com/go-i2p/go-i2p
And build with `GO111MODULES=off` or use a `replace` directive in your `go.mod`
to direct to the local module source. Or you may run your own Go Modules proxy as
a hidden service. I'll make this about a billion times easier in the near future I
promise.
go-i2p is in early development. The master branch is being refactored and API's are
definitely going to change. If you choose to use any part of this code right now,
please keep up with these changes, as they will not be backward compatible and require
**Fundamentally** changing any code that treats this as a dependency.
### Implemented Features
As the application is restructured and moved away from representing I2P data
structures as byte slices, this chart will be filled in, when the tests pass,
the item will be checked off. Currently, much of this is partially implemented
in byte-slice versions and partially implemented as Go Structs. Very little of
it will work until it's all moved to Go Structs where appropriate. Most of
this will happen in /lib/common.
- Clients
- [ ] Datagrams
- [ ] I2CP
- [ ] Message routing
- [ ] SAM
- [ ] Streaming
- [ ] Tunnel Manager
- Cryptographic primitives
- Signing
- [ ] ECDSA_SHA256_P256
@ -48,62 +33,85 @@ this will happen in /lib/common.
- [ ] RSA_SHA384_3072
- [ ] RSA_SHA512_4096
- [ ] Ed25519
- [x] ElGamal
- [x] AES256
- Common Structures
- Common Type Specification
- [x] Integer
- [x] Date
- [x] String
- [x] PublicKey* As interface in lib/crypto
- [x] PrivateKey* As interface in lib/crypto
- [ ] SessionKey
- [ ] SigningPublicKey
- [ ] Signature
- [x] Hash
- [ ] Session Tag
- [ ] Tunnel ID
- [x] Certificate
- [ ] Mapping
- Common Structure Specification
- [ ] KeysAndCert
- [ ] RouterIdentity
- [ ] Destination
- [ ] Lease
- [ ] LeaseSet
- [ ] Lease2
- [ ] OfflineSigntature
- [ ] LeaseSet2Header
- [ ] LeaseSet2
- [ ] MetaLease
- [ ] MetaLeaseSet
- [ ] EncryptedLeaseSet
- [ ] RouterAddress
- [ ] RouterInfo
- [ ] Red25519
- [ ] ElGamal
- [ ] AES256
- [ ] X25519
- [ ] ChaCha20/Poly1305
- [ ] Elligator2
- [ ] HKDF
- [ ] HMAC
- [X] Noise subsystem
- End-to-End Crypto
- [ ] Garlic messages
- [ ] ElGamal/AES+SessionTag
- [ ] Ratchet/X25519
- I2NP
- [ ] Message parsing
- [ ] Message handling
- NetDB
- [ ] Local storage
- [ ] Persistence to disk
- [ ] Reseeding
- [/] Local storage
- [/] Persistence to disk
- [X] Reseeding
- [ ] Lookups
- [ ] Expiry
- [ ] Exploration
- [ ] Publishing
- [ ] Floodfill
- [ ] LS2 and Encrypted Leasesets
- Transports
- [ ] Transport manager
- NTCP
- [ ] Handshake
- [ ] Session tracking
- [ ] Automatic session creation
- [X] Transport manager
- NTCP2
- [ ] Handshake
- [ ] Session tracking
- [ ] Automatic session creation
- [ ] SSU
- SSU2
- [ ] Handshake
- [ ] Session tracking
- [ ] Automatic session creation
- [ ] Peer Tests
- [ ] Introducers
- Tunnels
- [ ] Building
- [ ] Build Message Crypto (ElGamal)
- [ ] Build Message Crypto (ECIES)
- [ ] Participating
- [ ] Tunnel Message Crypto
- [ ] Tunnel Message Fragmentation/Reassembly
- Common Data Structures
- [X] Keys and Cert
- [X] Key Certificates
- [X] Certificate
- [X] Lease
- [X] Lease Set
- [X] Router Info
- [X] Router Identity
- [X] Router Address
- [X] Session Key
- [X] Signature Types
- [X] Destination
- [X] Data Types
- [X] Session Tag
## Verbosity ##
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
There are three available log levels:
- Debug
```shell
export DEBUG_I2P=debug
```
- Warn
```shell
export DEBUG_I2P=warn
```
- Error
```shell
export DEBUG_I2P=error
```
If DEBUG_I2P is set to an unrecognized variable, it will fall back to "debug".
## Contributing

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

42
go.mod
View File

@ -1,9 +1,43 @@
module github.com/go-i2p/go-i2p
go 1.16
go 1.23.1
require (
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
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.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.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
)

106
go.sum
View File

@ -1,24 +1,106 @@
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
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=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
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=
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.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/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
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.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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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

@ -1,6 +1,6 @@
package bootstrap
import "github.com/go-i2p/go-i2p/lib/common"
import "github.com/go-i2p/go-i2p/lib/common/router_info"
// interface defining a way to bootstrap into the i2p network
type Bootstrap interface {
@ -9,5 +9,5 @@ type Bootstrap interface {
// if n is 0 then try obtaining as many router infos as possible
// returns nil and error if we cannot fetch ANY router infos
// returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use
GetPeers(n int) (chan []common.RouterInfo, error)
GetPeers(n int) (chan []router_info.RouterInfo, error)
}

View File

@ -1,4 +1,2 @@
//
// provides generic interfaces for initial bootstrap into network and network reseeding
//
package bootstrap

23
lib/bootstrap/doc.md Normal file
View File

@ -0,0 +1,23 @@
# bootstrap
--
import "github.com/go-i2p/go-i2p/lib/bootstrap"
provides generic interfaces for initial bootstrap into network and network
### reseeding
## Usage
#### type Bootstrap
```go
type Bootstrap interface {
// get more peers for bootstrap
// try obtaining at most n router infos
// if n is 0 then try obtaining as many router infos as possible
// returns nil and error if we cannot fetch ANY router infos
// returns a channel that yields 1 slice of router infos containing n or fewer router infos, caller must close channel after use
GetPeers(n int) (chan []router_info.RouterInfo, error)
}
```
interface defining a way to bootstrap into the i2p network

View File

@ -1,18 +1,23 @@
//
// base32 encoding using I2P's alphabet
//
// Package base32 implmenets utilities for encoding and decoding text using I2P's alphabet
package base32
import (
b32 "encoding/base32"
)
var I2PEncoding *b32.Encoding = b32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
// I2PEncodeAlphabet is the base32 encoding used throughout I2P.
// RFC 3548 using lowercase characters.
const I2PEncodeAlphabet = "abcdefghijklmnopqrstuvwxyz234567"
//
// Return a go string of the I2P base32
// encoding of the provided byte slice
//
// I2PEncoding is the standard base32 encoding used through I2P.
var I2PEncoding *b32.Encoding = b32.NewEncoding(I2PEncodeAlphabet)
// EncodeToString encodes []byte to a base32 string using I2PEncoding
func EncodeToString(data []byte) string {
return I2PEncoding.EncodeToString(data)
}
// DecodeString decodes base64 string to []byte I2PEncoding
func DecodeString(data string) ([]byte, error) {
return I2PEncoding.DecodeString(data)
}

View File

@ -0,0 +1,20 @@
package base32
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeNotMangled(t *testing.T) {
assert := assert.New(t)
// Random pangram
testInput := []byte("How vexingly quick daft zebras jump!")
encodedString := EncodeToString(testInput)
decodedString, err := DecodeString(encodedString)
assert.Nil(err)
assert.ElementsMatch(testInput, decodedString)
}

33
lib/common/base32/doc.md Normal file
View File

@ -0,0 +1,33 @@
# base32
--
import "github.com/go-i2p/go-i2p/lib/common/base32"
Package base32 implmenets utilities for encoding and decoding text using I2P's
### alphabet
## Usage
```go
const I2PEncodeAlphabet = "abcdefghijklmnopqrstuvwxyz234567"
```
I2PEncodeAlphabet is the base32 encoding used throughout I2P. RFC 3548 using
lowercase characters.
```go
var I2PEncoding *b32.Encoding = b32.NewEncoding(I2PEncodeAlphabet)
```
I2PEncoding is the standard base32 encoding used through I2P.
#### func DecodeString
```go
func DecodeString(data string) ([]byte, error)
```
DecodeString decodes base64 string to []byte I2PEncoding
#### func EncodeToString
```go
func EncodeToString(data []byte) string
```
EncodeToString encodes []byte to a base32 string using I2PEncoding

View File

@ -1,30 +1,23 @@
//
// base64 encoding using I2P's alphabet
//
// Package base64 implmenets utilities for encoding and decoding text using I2P's alphabet
package base64
import (
b64 "encoding/base64"
)
// i2p base64 alphabet
const Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
// I2PEncodeAlphabet is the base64 encoding used throughout I2P.
// RFC 4648 with "/"" replaced with "~", and "+" replaced with "-".
const I2PEncodeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
// i2p base64 encoding
var I2PEncoding *b64.Encoding = b64.NewEncoding(Alphabet)
// I2PEncoding is the standard base64 encoding used through I2P.
var I2PEncoding *b64.Encoding = b64.NewEncoding(I2PEncodeAlphabet)
//
// Return a go string of the I2P base64
// encoding of the provided byte slice
//
// I2PEncoding is the standard base64 encoding used through I2P.
func EncodeToString(data []byte) string {
return I2PEncoding.EncodeToString(data)
}
//
// decode string using i2p base64 encoding
// returns error if data is malfromed
//
func DecodeFromString(str string) (d []byte, err error) {
// DecodeString decodes base64 string to []byte I2PEncoding
func DecodeString(str string) ([]byte, error) {
return I2PEncoding.DecodeString(str)
}

View File

@ -0,0 +1,20 @@
package base64
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeNotMangled(t *testing.T) {
assert := assert.New(t)
// Random pangram
testInput := []byte("Glib jocks quiz nymph to vex dwarf.")
encodedString := EncodeToString(testInput)
decodedString, err := DecodeString(encodedString)
assert.Nil(err)
assert.ElementsMatch(testInput, decodedString)
}

33
lib/common/base64/doc.md Normal file
View File

@ -0,0 +1,33 @@
# base64
--
import "github.com/go-i2p/go-i2p/lib/common/base64"
Package base64 implmenets utilities for encoding and decoding text using I2P's
### alphabet
## Usage
```go
const I2PEncodeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~"
```
I2PEncodeAlphabet is the base64 encoding used throughout I2P. RFC 4648 with "/""
replaced with "~", and "+" replaced with "-".
```go
var I2PEncoding *b64.Encoding = b64.NewEncoding(I2PEncodeAlphabet)
```
I2PEncoding is the standard base64 encoding used through I2P.
#### func DecodeString
```go
func DecodeString(str string) ([]byte, error)
```
DecodeString decodes base64 string to []byte I2PEncoding
#### func EncodeToString
```go
func EncodeToString(data []byte) string
```
I2PEncoding is the standard base64 encoding used through I2P.

View File

@ -1,220 +0,0 @@
package common
/*
I2P Certificate
https://geti2p.net/spec/common-structures#certificate
Accurate for version 0.9.24
+----+----+----+----+----+-//
|type| length | payload
+----+----+----+----+----+-//
type :: Integer
length -> 1 byte
case 0 -> NULL
case 1 -> HASHCASH
case 2 -> HIDDEN
case 3 -> SIGNED
case 4 -> MULTIPLE
case 5 -> KEY
length :: Integer
length -> 2 bytes
payload :: data
length -> $length bytes
*/
import (
"errors"
log "github.com/sirupsen/logrus"
)
// Certificate Types
const (
CERT_NULL = iota
CERT_HASHCASH
CERT_HIDDEN
CERT_SIGNED
CERT_MULTIPLE
CERT_KEY
)
// Minimum size of a valid Certificate
const (
CERT_MIN_SIZE = 3
)
type CertificateInterface interface {
Cert() []byte
Length() (length int, err error)
Data() (data []byte, err error)
Type() (cert_type int, type_bytes []byte, err error)
SignatureSize() (size int)
}
type Certificate struct {
CertType *Integer
CertLen *Integer
CertBytes []byte
}
var ci CertificateInterface = &Certificate{}
func (certificate Certificate) SignatureSize() (size int) {
return 40
}
func (certificate Certificate) Cert() []byte {
var ret []byte
ret = append(ret, certificate.CertType.Bytes()...)
l, _ := certificate.Length()
//if err != nil && err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" {
//}
data, _ := certificate.Data()
if l != 0 && len(data) != 0 {
ret = append(ret, certificate.CertLen.Bytes()...)
ret = append(ret, data...)
} else {
ret = append(ret, certificate.CertLen.Bytes()...)
}
//log.Println("\n\n CERTIFICATE: ", ret, l+CERT_MIN_SIZE, err)
return ret //[:l+CERT_MIN_SIZE]
}
//
// Return the Certificate Type specified in the first byte of the Certificate,
// and an error if the certificate is shorter than the minimum certificate size.
//
func (certificate Certificate) Type() (cert_type int, type_bytes []byte, err error) {
return certificate.CertType.Value(), certificate.CertType.Bytes(), nil
}
//
// Look up the length of the Certificate, reporting errors if the certificate is
// shorter than the minimum certificate size or if the reported length doesn't
// match the provided data.
//
func (certificate Certificate) Length() (length int, err error) {
if certificate.CertLen.Value() < 1 {
log.WithFields(log.Fields{
"at": "(Certificate) Length",
"certificate_bytes_length": certificate.CertLen,
"certificate_min_size": CERT_MIN_SIZE - 1,
"reason": "certificate is too short",
}).Warn("certificate format warning")
err = errors.New("error parsing certificate length: certificate is too short")
}
if certificate.CertLen.Value() > len(certificate.CertBytes) {
log.WithFields(log.Fields{
"at": "(Certificate) Length",
"certificate_bytes_length": certificate.CertLen,
"certificate_actual_length": len(certificate.CertBytes),
"reason": "certificate data is shorter than specified by length",
}).Warn("certificate format warning")
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
length = certificate.CertLen.Value()
}
if certificate.CertLen.Value() < len(certificate.CertBytes) {
log.WithFields(log.Fields{
"at": "(Certificate) Length",
"certificate_bytes_length": certificate.CertLen,
"certificate_actual_length": len(certificate.CertBytes),
"reason": "certificate contains data beyond length",
}).Warn("certificate format warning")
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
length = certificate.CertLen.Value()
}
if err != nil {
return
}
length = certificate.CertLen.Value()
return
}
//
// Return the Certificate data and any errors encountered parsing the Certificate.
//
func (certificate Certificate) Data() (data []byte, err error) {
_, err = certificate.Length()
data = certificate.CertBytes
if err != nil {
switch err.Error() {
case "error parsing certificate length: certificate is too short":
return
case "certificate parsing warning: certificate data is shorter than specified by length":
data = certificate.CertBytes
return
case "certificate parsing warning: certificate data is longer than specified by length":
data = certificate.CertBytes[:certificate.CertLen.Value()]
return
}
}
return
}
//
// Read a Certificate from a slice of bytes, returning any extra data on the end of the slice
// and any errors if a valid Certificate could not be read.
//
func ReadCertificate(data []byte) (certificate *Certificate, remainder []byte, err error) {
certificate = &Certificate{}
certificate.CertType, err = NewInteger(data[0:1])
if err != nil {
log.WithFields(log.Fields{
"at": "(Certificate) ReadCertificate",
"certificate": certificate,
"data": data,
"reason": "error parsing certificate type",
"error": err,
"error_reason": err.Error(),
}).Warn("certificate format warning")
}
certificate.CertLen = &Integer{}
cert_len := len(data)
if cert_len < CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "(Certificate) ReadCertificate",
"certificate_bytes_length": cert_len,
"certificate_min_size": CERT_MIN_SIZE,
"reason": "certificate is too short",
}).Warn("certificate format warning")
err = errors.New("error parsing certificate length: certificate is too short")
return
} else {
certificate.CertLen, err = NewInteger(data[1:CERT_MIN_SIZE])
// _, err = certificate.Type()
//log.Println("Calculated len AT LEN", cert_len, "Stated len AT LEN", certificate.CertLen.Value())
if err != nil {
//return
log.WithFields(log.Fields{
"at": "(Certificate) ReadCertificate",
"certificate_bytes_length": cert_len,
"certificate_min_size": CERT_MIN_SIZE,
"reason": "certificate size is invalid",
}).Warn("certificate format warning")
//err = errors.New("error parsing certificate type: certificate type is invalid")
}
certificate.CertBytes = data[CERT_MIN_SIZE:]
_, err = certificate.Length()
if err != nil {
switch err.Error() {
case "error parsing certificate length: certificate is too short":
certificate.CertLen, err = NewInteger([]byte{00000000})
return
case "certificate parsing warning: certificate data is shorter than specified by length":
return
case "certificate parsing warning: certificate data is longer than specified by length":
certificate.CertBytes = data[CERT_MIN_SIZE:]
l, _ := certificate.Length()
remainder = data[CERT_MIN_SIZE+l:]
return
}
}
}
return
}

View File

@ -0,0 +1,262 @@
// Package certificate implements the certificate common-structure of I2P.
package certificate
import (
"encoding/binary"
"errors"
"fmt"
"github.com/sirupsen/logrus"
// log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/util/logger"
. "github.com/go-i2p/go-i2p/lib/common/data"
)
var log = logger.GetGoI2PLogger()
// Certificate Types
const (
CERT_NULL = iota
CERT_HASHCASH
CERT_HIDDEN
CERT_SIGNED
CERT_MULTIPLE
CERT_KEY
)
// CERT_MIN_SIZE is the minimum size of a valid Certificate in []byte
// 1 byte for type
// 2 bytes for payload length
const CERT_MIN_SIZE = 3
/*
[I2P Certificate]
Accurate for version 0.9.49
Description
A certifificate is a container for various receipts of proof of works used throughout the I2P network.
Contents
1 byte Integer specifying certificate type, followed by a 2 byte Integer specifying the size of the certificate playload, then that many bytes.
+----+----+----+----+----+-//
|type| length | payload
+----+----+----+----+----+-//
type :: Integer
length -> 1 byte
case 0 -> NULL
case 1 -> HASHCASH
case 2 -> HIDDEN
case 3 -> SIGNED
case 4 -> MULTIPLE
case 5 -> KEY
length :: Integer
length -> 2 bytes
payload :: data
length -> $length bytes
*/
// Certificate is the representation of an I2P Certificate.
//
// https://geti2p.net/spec/common-structures#certificate
type Certificate struct {
kind Integer
len Integer
payload []byte
}
// RawBytes returns the entire certificate in []byte form, includes excess payload data.
func (c *Certificate) RawBytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.len.Bytes()...)
bytes = append(bytes, c.payload...)
log.WithFields(logrus.Fields{
"raw_bytes_length": len(bytes),
}).Debug("Generated raw bytes for certificate")
return bytes
}
// ExcessBytes returns the excess bytes in a certificate found after the specified payload length.
func (c *Certificate) ExcessBytes() []byte {
if len(c.payload) >= c.len.Int() {
excess := c.payload[c.len.Int():]
log.WithFields(logrus.Fields{
"excess_bytes_length": len(excess),
}).Debug("Found excess bytes in certificate")
return excess
}
log.Debug("No excess bytes found in certificate")
return nil
}
// Bytes returns the entire certificate in []byte form, trims payload to specified length.
func (c *Certificate) Bytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.len.Bytes()...)
bytes = append(bytes, c.Data()...)
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
}).Debug("Generated bytes for certificate")
return bytes
}
func (c *Certificate) length() (cert_len int) {
cert_len = len(c.Bytes())
return
}
// Type returns the Certificate type specified in the first byte of the Certificate,
func (c *Certificate) Type() (cert_type int) {
cert_type = c.kind.Int()
log.WithFields(logrus.Fields{
"cert_type": cert_type,
}).Debug("Retrieved certificate type")
return
}
// Length returns the payload length of a Certificate.
func (c *Certificate) Length() (length int) {
length = c.len.Int()
log.WithFields(logrus.Fields{
"length": length,
}).Debug("Retrieved certificate length")
return
}
// Data returns the payload of a Certificate, payload is trimmed to the specified length.
func (c *Certificate) Data() (data []byte) {
lastElement := c.Length()
if lastElement > len(c.payload) {
data = c.payload
log.Warn("Certificate payload shorter than specified length")
} else {
data = c.payload[0:lastElement]
}
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved certificate data")
return
}
// 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 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(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
}).Error("invalid certificate, empty")
err = fmt.Errorf("error parsing certificate: certificate is empty")
return
case 1, 2:
certificate.kind = Integer(data[0 : len(data)-1])
certificate.len = Integer([]byte{0})
log.WithFields(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
}).Error("invalid certificate, too short")
err = fmt.Errorf("error parsing certificate: certificate is too short")
return
default:
certificate.kind = Integer(data[0:1])
certificate.len = Integer(data[1:3])
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(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": certificate.len.Int(),
"certificate_payload_length": payloadLength,
"data_bytes:": string(data),
"kind_bytes": data[0:1],
"len_bytes": data[1:3],
"reason": err.Error(),
}).Error("invalid certificate, shorter than specified by length")
return
}
log.WithFields(logrus.Fields{
"type": certificate.kind.Int(),
"length": certificate.len.Int(),
}).Debug("Successfully created new certificate")
return
}
}
// ReadCertificate creates a Certificate from []byte and returns any ExcessBytes at the end of the input.
// returns err if the certificate could not be read.
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
certificate, err = 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

@ -1,4 +1,4 @@
package common
package certificate
import (
"testing"
@ -10,11 +10,8 @@ func TestCertificateTypeIsFirstByte(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x00}
certificate, _, err := ReadCertificate(bytes)
if err != nil {
t.Log(err)
}
cert_type, _, err := certificate.Type()
certificate, err := readCertificate(bytes)
cert_type := certificate.Type()
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
assert.Nil(err)
@ -24,14 +21,11 @@ func TestCertificateLengthCorrect(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
certificate, _, err := ReadCertificate(bytes)
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
cert_len, err := certificate.Length()
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
certificate, err := readCertificate(bytes)
cert_len := certificate.Length()
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
assert.Nil(err)
}
func TestCertificateLengthErrWhenTooShort(t *testing.T) {
@ -39,14 +33,11 @@ func TestCertificateLengthErrWhenTooShort(t *testing.T) {
bytes := []byte{0x03, 0x01}
certificate, _, err := ReadCertificate(bytes)
if assert.NotNil(err) {
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
}
cert_len, err := certificate.Length()
cert_len := certificate.Length()
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
if assert.NotNil(err) {
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
assert.Equal("error parsing certificate: certificate is too short", err.Error(), "correct error message should be returned")
}
}
@ -54,15 +45,8 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate, _, err := ReadCertificate(bytes)
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
}
cert_len, err := certificate.Length()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
}
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")
if assert.NotNil(err) {
@ -74,34 +58,25 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
certificate, _, err := ReadCertificate(bytes)
assert.Nil(err, "certificate.Data() returned error with valid data")
cert_len, err := certificate.Length()
certificate, err := readCertificate(bytes)
cert_data := certificate.Data()
assert.Nil(err, "certificate.Data() returned error with valid data")
cert_len := len(cert_data)
assert.Equal(cert_len, 1, "certificate.Length() did not return indicated length when data was valid")
data, _ := NewInteger(certificate.CertBytes)
assert.Equal(170, data.Value(), "certificate.Data() returned incorrect data")
assert.Equal(170, int(cert_data[0]), "certificate.Data() returned incorrect data")
}
func TestCertificateDataWhenTooLong(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
certificate, _, err := ReadCertificate(bytes)
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
}
cert_len, err := certificate.Length()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
}
certificate, _, _ := ReadCertificate(bytes)
cert_data := certificate.Data()
cert_len := certificate.Length() // len(cert_data)
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was too long")
if certificate.CertBytes[0] != 0xff || certificate.CertBytes[1] != 0xff {
if cert_data[0] != 0xff || cert_data[1] != 0xff {
t.Fatal("certificate.Data() returned incorrect data when data was too long")
}
}
@ -110,11 +85,8 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x03, 0x00, 0x02, 0xff}
certificate, _, err := ReadCertificate(bytes)
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
}
cert_data, err := certificate.Data()
certificate, err := readCertificate(bytes)
cert_data := certificate.Data()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
@ -130,8 +102,7 @@ func TestReadCertificateWithCorrectData(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
cert, remainder, err := ReadCertificate(bytes)
t.Log("CERT IS:", cert.Cert())
assert.Equal(len(cert.Cert()), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on a valid certificate")
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
}
@ -142,7 +113,7 @@ func TestReadCertificateWithDataTooShort(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert.Cert()), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
assert.Equal(cert.length(), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on certificate with missing data")
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
@ -155,10 +126,10 @@ func TestReadCertificateWithRemainder(t *testing.T) {
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert.Cert()), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
assert.Equal(len(remainder), 1, "ReadCertificate() returned incorrect length remainder on certificate with extra data")
assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
assert.NotNil(err)
// assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
assert.Nil(err)
}
func TestReadCertificateWithInvalidLength(t *testing.T) {
@ -167,9 +138,9 @@ func TestReadCertificateWithInvalidLength(t *testing.T) {
bytes := []byte{0x00, 0x00}
cert, remainder, err := ReadCertificate(bytes)
assert.Equal(len(cert.Cert()), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
assert.Equal(cert.length(), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
assert.Equal(len(remainder), 0, "ReadCertificate() returned non-zero length remainder on invalid certificate")
if assert.NotNil(err) {
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
assert.Equal("error parsing certificate: certificate is too short", err.Error(), "correct error message should be returned")
}
}

View File

@ -0,0 +1,98 @@
# certificate
--
import "github.com/go-i2p/go-i2p/lib/common/certificate"
## Usage
```go
const (
CERT_NULL = iota
CERT_HASHCASH
CERT_HIDDEN
CERT_SIGNED
CERT_MULTIPLE
CERT_KEY
)
```
Certificate Types
```go
const CERT_MIN_SIZE = 3
```
CERT_MIN_SIZE is the minimum size of a valid Certificate in []byte 1 byte for
type 2 bytes for payload length
#### type Certificate
```go
type Certificate struct {
}
```
Certificate is the representation of an I2P Certificate.
https://geti2p.net/spec/common-structures#certificate
#### func NewCertificate
```go
func NewCertificate(data []byte) (certificate Certificate, err error)
```
NewCertificate creates a new Certficiate from []byte returns err if the
certificate is too short or if the payload doesn't match specified length.
#### func ReadCertificate
```go
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error)
```
ReadCertificate creates a Certificate from []byte and returns any ExcessBytes at
the end of the input. returns err if the certificate could not be read.
#### func (*Certificate) Bytes
```go
func (c *Certificate) Bytes() []byte
```
Bytes returns the entire certificate in []byte form, trims payload to specified
length.
#### func (*Certificate) Data
```go
func (c *Certificate) Data() (data []byte)
```
Data returns the payload of a Certificate, payload is trimmed to the specified
length.
#### func (*Certificate) ExcessBytes
```go
func (c *Certificate) ExcessBytes() []byte
```
ExcessBytes returns the excess bytes in a certificate found after the specified
payload length.
#### func (*Certificate) Length
```go
func (c *Certificate) Length() (length int)
```
Length returns the payload length of a Certificate.
#### func (*Certificate) RawBytes
```go
func (c *Certificate) RawBytes() []byte
```
RawBytes returns the entire certificate in []byte form, includes excess payload
data.
#### func (*Certificate) Type
```go
func (c *Certificate) Type() (cert_type int)
```
Type returns the Certificate type specified in the first byte of the
Certificate,

87
lib/common/data/date.go Normal file
View File

@ -0,0 +1,87 @@
// Package data implements common data structures used in higher level structures.
package data
import (
"errors"
"time"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// DATE_SIZE is the length in bytes of an I2P Date.
const DATE_SIZE = 8
/*
[I2P Date]
Accurate for version 0.9.49
Description
The number of milliseconds since midnight on Januyar 1, 1970 in the GMT timezone.
If the number is 0, the date is undefined or null.
Contents
8 byte Integer
*/
// Date is the represenation of an I2P Date.
//
// https://geti2p.net/spec/common-structures#date
type Date [8]byte
// Bytes returns the raw []byte content of a Date.
func (i Date) Bytes() []byte {
return i[:]
}
// Int returns the Date as a Go integer.
func (i Date) Int() int {
return intFromBytes(i.Bytes())
}
// Time takes the value stored in date as an 8 byte big-endian integer representing the
// number of milliseconds since the beginning of unix time and converts it to a Go time.Time
// struct.
func (date Date) Time() (date_time time.Time) {
seconds := Integer(date[:])
date_time = time.Unix(0, int64(seconds.Int()*1000000))
return
}
// ReadDate creates a Date from []byte using the first DATE_SIZE bytes.
// Any data after DATE_SIZE is returned as a remainder.
func ReadDate(data []byte) (date Date, remainder []byte, err error) {
if len(data) < 8 {
log.WithFields(logrus.Fields{
"data": data,
}).Error("ReadDate: data is too short")
err = errors.New("ReadDate: data is too short")
return
}
copy(date[:], data[:8])
remainder = data[8:]
log.WithFields(logrus.Fields{
"date_value": date.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully read Date from data")
return
}
// NewDate creates a new Date from []byte using ReadDate.
// Returns a pointer to Date unlike ReadDate.
func NewDate(data []byte) (date *Date, remainder []byte, err error) {
objdate, remainder, err := ReadDate(data)
if err != nil {
log.WithError(err).Error("Failed to create new Date")
return nil, remainder, err
}
date = &objdate
log.WithFields(logrus.Fields{
"date_value": date.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully created new Date")
return
}

View File

@ -1,11 +1,12 @@
package common
package data
import (
"github.com/stretchr/testify/assert"
"testing"
"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}

297
lib/common/data/doc.md Normal file
View File

@ -0,0 +1,297 @@
# data
--
import "github.com/go-i2p/go-i2p/lib/common/data"
Package data implements common data structures used in higher level structures.
## Usage
```go
const DATE_SIZE = 8
```
DATE_SIZE is the length in bytes of an I2P Date.
```go
const MAX_INTEGER_SIZE = 8
```
MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes.
```go
const STRING_MAX_SIZE = 255
```
STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P
string
#### func PrintErrors
```go
func PrintErrors(errs []error)
```
PrintErrors prints a formatted list of errors to the console.
#### func WrapErrors
```go
func WrapErrors(errs []error) error
```
WrapErrors compiles a slice of errors and returns them wrapped together as a
single error.
#### type Date
```go
type Date [8]byte
```
Date is the represenation of an I2P Date.
https://geti2p.net/spec/common-structures#date
#### func NewDate
```go
func NewDate(data []byte) (date *Date, remainder []byte, err error)
```
NewDate creates a new Date from []byte using ReadDate. Returns a pointer to Date
unlike ReadDate.
#### func ReadDate
```go
func ReadDate(data []byte) (date Date, remainder []byte, err error)
```
ReadDate creates a Date from []byte using the first DATE_SIZE bytes. Any data
after DATE_SIZE is returned as a remainder.
#### func (Date) Bytes
```go
func (i Date) Bytes() []byte
```
Bytes returns the raw []byte content of a Date.
#### func (Date) Int
```go
func (i Date) Int() int
```
Int returns the Date as a Go integer.
#### func (Date) Time
```go
func (date Date) Time() (date_time time.Time)
```
Time takes the value stored in date as an 8 byte big-endian integer representing
the number of milliseconds since the beginning of unix time and converts it to a
Go time.Time struct.
#### type Hash
```go
type Hash [32]byte
```
Hash is the represenation of an I2P Hash.
https://geti2p.net/spec/common-structures#hash
#### func HashData
```go
func HashData(data []byte) (h Hash)
```
HashData returns the SHA256 sum of a []byte input as Hash.
#### func HashReader
```go
func HashReader(r io.Reader) (h Hash, err error)
```
HashReader returns the SHA256 sum from all data read from an io.Reader. return
error if one occurs while reading from reader
#### func (Hash) Bytes
```go
func (h Hash) Bytes() [32]byte
```
#### type I2PString
```go
type I2PString []byte
```
I2PString is the represenation of an I2P String.
https://geti2p.net/spec/common-structures#string
#### func ReadI2PString
```go
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error)
```
ReadI2PString returns I2PString from a []byte. The remaining bytes after the
specified length are also returned. Returns a list of errors that occurred
during parsing.
#### func ToI2PString
```go
func ToI2PString(data string) (str I2PString, err error)
```
ToI2PString converts a Go string to an I2PString. Returns error if the string
exceeds STRING_MAX_SIZE.
#### func (I2PString) Data
```go
func (str I2PString) Data() (data string, err error)
```
Data returns the I2PString content as a string trimmed to the specified length
and not including the length byte. Returns error encountered by Length.
#### func (I2PString) Length
```go
func (str I2PString) Length() (length int, err error)
```
Length returns the length specified in the first byte. Returns error if the
specified does not match the actual length or the string is otherwise invalid.
#### type Integer
```go
type Integer []byte
```
Integer is the represenation of an I2P Integer.
https://geti2p.net/spec/common-structures#integer
#### func NewInteger
```go
func NewInteger(bytes []byte, size int) (integer *Integer, remainder []byte, err error)
```
NewInteger creates a new Integer from []byte using ReadInteger. Limits the
length of the created Integer to MAX_INTEGER_SIZE. Returns a pointer to Integer
unlike ReadInteger.
#### func NewIntegerFromInt
```go
func NewIntegerFromInt(value int, size int) (integer *Integer, err error)
```
NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte
length.
#### func ReadInteger
```go
func ReadInteger(bytes []byte, size int) (Integer, []byte)
```
ReadInteger returns an Integer from a []byte of specified length. The remaining
bytes after the specified length are also returned.
#### func (Integer) Bytes
```go
func (i Integer) Bytes() []byte
```
Bytes returns the raw []byte content of an Integer.
#### func (Integer) Int
```go
func (i Integer) Int() int
```
Int returns the Date as a Go integer
#### type Mapping
```go
type Mapping struct {
}
```
Mapping is the represenation of an I2P Mapping.
https://geti2p.net/spec/common-structures#mapping
#### func GoMapToMapping
```go
func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error)
```
GoMapToMapping converts a Go map of unformatted strings to *Mapping.
#### func NewMapping
```go
func NewMapping(bytes []byte) (values *Mapping, remainder []byte, err []error)
```
NewMapping creates a new *Mapping from []byte using ReadMapping. Returns a
pointer to Mapping unlike ReadMapping.
#### func ReadMapping
```go
func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
```
ReadMapping returns Mapping from a []byte. The remaining bytes after the
specified length are also returned. Returns a list of errors that occurred
during parsing.
#### func ValuesToMapping
```go
func ValuesToMapping(values MappingValues) *Mapping
```
ValuesToMapping creates a *Mapping using MappingValues. The values are sorted in
the order defined in mappingOrder.
#### func (*Mapping) Data
```go
func (mapping *Mapping) Data() []byte
```
Data returns a Mapping in its []byte form.
#### func (*Mapping) HasDuplicateKeys
```go
func (mapping *Mapping) HasDuplicateKeys() bool
```
HasDuplicateKeys returns true if two keys in a mapping are identical.
#### func (Mapping) Values
```go
func (mapping Mapping) Values() MappingValues
```
Values returns the values contained in a Mapping as MappingValues.
#### type MappingValues
```go
type MappingValues [][2]I2PString
```
MappingValues represents the parsed key value pairs inside of an I2P Mapping.
#### func ReadMappingValues
```go
func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingValues, remainder_bytes []byte, errs []error)
```
ReadMappingValues returns *MappingValues from a []byte. The remaining bytes
after the specified length are also returned. Returns a list of errors that
occurred during parsing.
#### func (MappingValues) Get
```go
func (m MappingValues) Get(key I2PString) I2PString
```

19
lib/common/data/errors.go Normal file
View File

@ -0,0 +1,19 @@
package data
import "fmt"
// WrapErrors compiles a slice of errors and returns them wrapped together as a single error.
func WrapErrors(errs []error) error {
var err error
for i, e := range errs {
err = fmt.Errorf("%v\n\t%d: %v", err, i, e)
}
return err
}
// PrintErrors prints a formatted list of errors to the console.
func PrintErrors(errs []error) {
for i, e := range errs {
fmt.Printf("\t%d: %v\n", i, e)
}
}

47
lib/common/data/hash.go Normal file
View File

@ -0,0 +1,47 @@
package data
import (
"crypto/sha256"
"io"
)
/*
[I2P Hash]
Accurate for version 0.9.49
Description
Represents the SHA256 of some data.
Contents
32 bytes
[I2P Hash]:
*/
// Hash is the represenation of an I2P Hash.
//
// https://geti2p.net/spec/common-structures#hash
type Hash [32]byte
func (h Hash) Bytes() [32]byte {
return h
}
// HashData returns the SHA256 sum of a []byte input as Hash.
func HashData(data []byte) (h Hash) {
// log.Println("Hashing Data:", data)
h = sha256.Sum256(data)
return
}
// HashReader returns the SHA256 sum from all data read from an io.Reader.
// return error if one occurs while reading from reader
func HashReader(r io.Reader) (h Hash, err error) {
sha := sha256.New()
_, err = io.Copy(sha, r)
if err == nil {
d := sha.Sum(nil)
copy(h[:], d)
}
return
}

View File

@ -0,0 +1,85 @@
package data
import (
"encoding/binary"
)
// MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes.
const MAX_INTEGER_SIZE = 8
/*
[I2P Hash]
Accurate for version 0.9.49
Description
Represents a non-negative integer.
Contents
1 to 8 bytes in network byte order (big endian) representing an unsigned integer.
*/
// Integer is the represenation of an I2P Integer.
//
// https://geti2p.net/spec/common-structures#integer
type Integer []byte
// Bytes returns the raw []byte content of an Integer.
func (i Integer) Bytes() []byte {
return i[:]
}
// Int returns the Date as a Go integer
func (i Integer) Int() int {
return intFromBytes(i.Bytes())
}
// ReadInteger returns an Integer from a []byte of specified length.
// The remaining bytes after the specified length are also returned.
func ReadInteger(bytes []byte, size int) (Integer, []byte) {
if len(bytes) < size {
return bytes[:size], bytes[len(bytes):]
}
return bytes[:size], bytes[size:]
}
// NewInteger creates a new Integer from []byte using ReadInteger.
// Limits the length of the created Integer to MAX_INTEGER_SIZE.
// Returns a pointer to Integer unlike ReadInteger.
func NewInteger(bytes []byte, size int) (integer *Integer, remainder []byte, err error) {
integerSize := MAX_INTEGER_SIZE
if size < MAX_INTEGER_SIZE {
integerSize = size
}
intBytes := bytes[:integerSize]
remainder = bytes[integerSize:]
i, _ := ReadInteger(intBytes, integerSize)
integer = &i
return
}
// NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte length.
func NewIntegerFromInt(value int, size int) (integer *Integer, err error) {
bytes := make([]byte, MAX_INTEGER_SIZE)
binary.BigEndian.PutUint64(bytes, uint64(value))
integerSize := MAX_INTEGER_SIZE
if size < MAX_INTEGER_SIZE {
integerSize = size
}
objinteger, _, err := NewInteger(bytes[MAX_INTEGER_SIZE-integerSize:], integerSize)
integer = objinteger
return
}
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
// integer and return an int representation.
func intFromBytes(number []byte) (value int) {
num_len := len(number)
if num_len < MAX_INTEGER_SIZE {
number = append(
make([]byte, MAX_INTEGER_SIZE-num_len),
number...,
)
}
value = int(binary.BigEndian.Uint64(number))
return
}

View File

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

233
lib/common/data/mapping.go Normal file
View File

@ -0,0 +1,233 @@
package data
import (
"errors"
"github.com/sirupsen/logrus"
)
/*
[I2P Mapping]
Accurate for version 0.9.49
Description
A set of key/value mappings or properties
Contents
A 2-byte size Integer followed by a series of String=String; pairs
+----+----+----+----+----+----+----+----+
| size |key_string (len + data) | = |
+----+----+----+----+----+----+----+----+
| val_string (len + data) | ; | ...
+----+----+----+----+----+----+----+
size :: Integer
length -> 2 bytes
Total number of bytes that follow
key_string :: String
A string (one byte length followed by UTF-8 encoded characters)
= :: A single byte containing '='
val_string :: String
A string (one byte length followed by UTF-8 encoded characters)
; :: A single byte containing ';'
*/
// Mapping is the represenation of an I2P Mapping.
//
// https://geti2p.net/spec/common-structures#mapping
type Mapping struct {
size *Integer
vals *MappingValues
}
// Values returns the values contained in a Mapping as MappingValues.
func (mapping Mapping) Values() MappingValues {
if mapping.vals == nil {
log.Debug("Mapping values are nil, returning empty MappingValues")
return MappingValues{}
}
log.WithFields(logrus.Fields{
"values_count": len(*mapping.vals),
}).Debug("Retrieved Mapping values")
return *mapping.vals
}
// Data returns a Mapping in its []byte form.
func (mapping *Mapping) Data() []byte {
keyOrValIntegerLength := 1
bytes := mapping.size.Bytes()
for _, pair := range mapping.Values() {
klen, _ := pair[0].Length()
keylen, _ := NewIntegerFromInt(klen, keyOrValIntegerLength)
bytes = append(bytes, keylen.Bytes()...)
bytes = append(bytes, pair[0][1:]...)
bytes = append(bytes, 0x3d)
vlen, _ := pair[1].Length()
vallen, _ := NewIntegerFromInt(vlen, keyOrValIntegerLength)
bytes = append(bytes, vallen.Bytes()...)
bytes = append(bytes, pair[1][1:]...)
bytes = append(bytes, 0x3b)
}
return bytes
}
// HasDuplicateKeys returns true if two keys in a mapping are identical.
func (mapping *Mapping) HasDuplicateKeys() bool {
log.Debug("Checking for duplicate keys in Mapping")
seen_values := make(map[string]bool)
values := mapping.Values()
for _, pair := range values {
key, _ := pair[0].Data()
if _, present := seen_values[key]; present {
log.WithFields(logrus.Fields{
"duplicate_key": key,
}).Warn("Found duplicate key in Mapping")
return true
} else {
seen_values[key] = true
}
}
log.Debug("No duplicate keys found in Mapping")
return false
}
// GoMapToMapping converts a Go map of unformatted strings to *Mapping.
func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error) {
log.WithFields(logrus.Fields{
"input_map_size": len(gomap),
}).Debug("Converting Go map to Mapping")
map_vals := MappingValues{}
for k, v := range gomap {
key_str, kerr := ToI2PString(k)
if kerr != nil {
log.WithError(kerr).Error("Failed to convert key to I2PString")
err = kerr
return
}
val_str, verr := ToI2PString(v)
if verr != nil {
log.WithError(verr).Error("Failed to convert value to I2PString")
err = verr
return
}
map_vals = append(
map_vals,
[2]I2PString{key_str, val_str},
)
}
mapping = ValuesToMapping(map_vals)
log.WithFields(logrus.Fields{
"mapping_size": len(map_vals),
}).Debug("Successfully converted Go map to Mapping")
return
}
// Check if the string parsing error indicates that the Mapping
// should no longer be parsed.
func stopValueRead(err error) bool {
result := err.Error() == "error parsing string: zero length"
if result {
log.WithError(err).Debug("Stopping value read due to zero length error")
}
return result
}
// Determine if the first byte in a slice of bytes is the provided byte.
func beginsWith(bytes []byte, chr byte) bool {
/*
return len(bytes) != 0 &&
bytes[0] == chr
*/
result := len(bytes) != 0 && bytes[0] == chr
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
"expected_char": string(chr),
"result": result,
}).Debug("Checked if bytes begin with specific character")
return result
}
// ReadMapping returns Mapping from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Reading Mapping from bytes")
if len(bytes) < 3 {
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
e := errors.New("zero length")
err = append(err, e)
return
}
size, remainder, e := NewInteger(bytes, 2)
if e != nil {
log.WithError(e).Error("Failed to read Mapping size")
err = append(err, e)
}
if size.Int() == 0 {
log.Warn("Mapping size is zero")
return
}
mapping.size = size
map_bytes := remainder[:mapping.size.Int()]
remainder = remainder[mapping.size.Int():]
if len(remainder) == 0 {
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
e := errors.New("zero length")
err = append(err, e)
}
// TODO: this should take the remainder and the length we already parsed above, as a parameter.
// Like tomorrow morning.
// ReadMappingValues should not attempt to figure out the length of the bytes it's reading over.
vals, _, mappingValueErrs := ReadMappingValues(map_bytes, *mapping.size)
err = append(err, mappingValueErrs...)
mapping.vals = vals
if len(mappingValueErrs) > 0 {
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "error parsing mapping values",
}).Warn("mapping format violation")
e := errors.New("error parsing mapping values")
err = append(err, e)
}
log.WithFields(logrus.Fields{
"mapping_size": mapping.size.Int(),
"values_count": len(*mapping.vals),
"remainder_length": len(remainder),
"error_count": len(err),
}).Debug("Finished reading Mapping")
return
}
// NewMapping creates a new *Mapping from []byte using ReadMapping.
// Returns a pointer to Mapping unlike ReadMapping.
func NewMapping(bytes []byte) (values *Mapping, remainder []byte, err []error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Creating new Mapping")
objvalues, remainder, err := ReadMapping(bytes)
values = &objvalues
log.WithFields(logrus.Fields{
"values_count": len(values.Values()),
"remainder_length": len(remainder),
"error_count": len(err),
}).Debug("Finished creating new Mapping")
return
}

View File

@ -0,0 +1,192 @@
package data
import (
"bytes"
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestValuesExclusesPairWithBadData(t *testing.T) {
assert := assert.New(t)
bad_key, _, errs := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
values := bad_key.Values()
e := WrapErrors(errs)
t.Log(e)
assert.NotNil(errs, "Values() did not return errors when some values had bad key")
if assert.Equal(1, len(values), "Values() did not return valid values when some values had bad key") {
k := values[0][0]
key, _ := k.Data()
v := values[0][1]
val, _ := v.Data()
assert.Equal(key, "a", "Values() returned by data with invalid key contains incorrect present key")
assert.Equal(val, "b", "Values() returned by data with invalid key contains incorrect present key")
}
}
func TestValuesWarnsMissingData(t *testing.T) {
assert := assert.New(t)
_, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62})
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had missing data") {
assert.Equal(errs[0].Error(), "warning parsing mapping: mapping length exceeds provided data")
}
}
func TestValuesWarnsExtraData(t *testing.T) {
assert := assert.New(t)
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
values := mapping.Values()
key, kerr := values[0][0].Data()
val, verr := values[0][1].Data()
assert.Nil(kerr)
assert.Nil(verr)
assert.Equal(key, "a", "Values() did not return key in valid data")
assert.Equal(val, "b", "Values() did not return value in valid data")
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had extra data") {
assert.Equal("warning parsing mapping: data exists beyond length of mapping", errs[0].Error(), "correct error message should be returned")
}
}
func TestValuesEnforcesEqualDelimitor(t *testing.T) {
assert := assert.New(t)
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x30, 0x01, 0x62, 0x3b})
values := mapping.Values()
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had = format error") {
assert.Equal("mapping format violation, expected =", errs[0].Error(), "correct error message should be returned")
}
assert.Equal(0, len(values), "Values() not empty with invalid data due to = format error")
}
func TestValuesEnforcedSemicolonDelimitor(t *testing.T) {
assert := assert.New(t)
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x30})
values := mapping.Values()
if assert.Equal(2, len(errs), "Values() reported wrong error count when mapping had ; format error") {
assert.Equal("mapping format violation, expected ;", errs[0].Error(), "correct error message should be returned")
}
assert.Equal(0, len(values), "Values() not empty with invalid data due to ; format error")
}
func TestValuesReturnsValues(t *testing.T) {
assert := assert.New(t)
mapping, _, errs := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
values := mapping.Values()
key, kerr := values[0][0].Data()
val, verr := values[0][1].Data()
assert.Nil(errs, "Values() returned a errors with parsing valid data")
assert.Nil(kerr)
assert.Nil(verr)
assert.Equal("a", key, "Values() did not return key in valid data")
assert.Equal("b", val, "Values() did not return value in valid data")
}
func TestHasDuplicateKeysTrueWhenDuplicates(t *testing.T) {
assert := assert.New(t)
dups, _, _ := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
assert.Equal(true, dups.HasDuplicateKeys(), "HasDuplicateKeys() did not report true when duplicate keys present")
}
func TestHasDuplicateKeysFalseWithoutDuplicates(t *testing.T) {
assert := assert.New(t)
mapping, _, _ := NewMapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
assert.Equal(false, mapping.HasDuplicateKeys(), "HasDuplicateKeys() did not report false when no duplicate keys present")
}
func TestReadMappingHasDuplicateKeys(t *testing.T) {
assert := assert.New(t)
_, _, errs := NewMapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
assert.Equal("mapping format violation, duplicate key in mapping", errs[0].Error(), "ReadMapping should throw an error when duplicate keys are present.")
}
func TestGoMapToMappingProducesCorrectMapping(t *testing.T) {
assert := assert.New(t)
gomap := map[string]string{"a": "b"}
mapping, err := GoMapToMapping(gomap)
assert.Nil(err, "GoMapToMapping() returned error with valid data")
expected := []byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b}
if bytes.Compare(mapping.Data(), expected) != 0 {
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
}
}
func TestFullGoMapToMappingProducesCorrectMapping(t *testing.T) {
assert := assert.New(t)
gomap := map[string]string{
"a": "b",
"c": "d",
}
mapping, err := GoMapToMapping(gomap)
assert.Nil(err, "GoMapToMapping() returned error with valid data")
expected := []byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x63, 0x3d, 0x01, 0x64, 0x3b}
if bytes.Compare(mapping.Data(), expected) != 0 {
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
}
}
func TestStopValueReadTrueWhenCorrectErr(t *testing.T) {
assert := assert.New(t)
status := stopValueRead(errors.New("error parsing string: zero length"))
assert.Equal(true, status, "stopValueRead() did not return true when String error found")
}
func TestStopValueReadFalseWhenWrongErr(t *testing.T) {
assert := assert.New(t)
status := stopValueRead(errors.New("something else"))
assert.Equal(false, status, "stopValueRead() did not return false when non String error found")
}
func TestBeginsWithCorrectWhenTrue(t *testing.T) {
assert := assert.New(t)
slice := []byte{0x41}
assert.Equal(true, beginsWith(slice, 0x41), "beginsWith() did not return true when correct")
}
func TestBeginsWithCorrectWhenFalse(t *testing.T) {
assert := assert.New(t)
slice := []byte{0x00}
assert.Equal(false, beginsWith(slice, 0x41), "beginsWith() did not false when incorrect")
}
func TestBeginsWithCorrectWhenNil(t *testing.T) {
assert := assert.New(t)
slice := make([]byte, 0)
assert.Equal(false, beginsWith(slice, 0x41), "beginsWith() did not return false on empty slice")
}

View File

@ -0,0 +1,222 @@
package data
import (
"errors"
"sort"
"github.com/sirupsen/logrus"
)
// MappingValues represents the parsed key value pairs inside of an I2P Mapping.
type MappingValues [][2]I2PString
func (m MappingValues) Get(key I2PString) I2PString {
keyBytes, _ := key.Data()
log.WithFields(logrus.Fields{
"key": string(keyBytes),
}).Debug("Searching for key in MappingValues")
for _, pair := range m {
kb, _ := pair[0][0:].Data()
if kb == keyBytes {
log.WithFields(logrus.Fields{
"key": string(keyBytes),
"value": string(pair[1][1:]),
}).Debug("Found matching key in MappingValues")
return pair[1]
}
}
log.WithFields(logrus.Fields{
"key": string(keyBytes),
}).Debug("Key not found in MappingValues")
return nil
}
// ValuesToMapping creates a *Mapping using MappingValues.
// The values are sorted in the order defined in mappingOrder.
func ValuesToMapping(values MappingValues) *Mapping {
// Default length to 2 * len
// 1 byte for ;
// 1 byte for =
log.WithFields(logrus.Fields{
"values_count": len(values),
}).Debug("Converting MappingValues to Mapping")
baseLength := 2 * len(values)
for _, mappingVals := range values {
for _, keyOrVal := range mappingVals {
baseLength += len(keyOrVal)
}
}
log.WithFields(logrus.Fields{
"mapping_size": baseLength,
}).Debug("Created Mapping from MappingValues")
mappingSize, _ := NewIntegerFromInt(baseLength, 2)
return &Mapping{
size: mappingSize,
vals: &values,
}
}
// I2P Mappings require consistent order in some cases for cryptographic signing, and sorting
// by keys. The Mapping is sorted lexographically by keys. Duplicate keys are allowed in general,
// but in implementations where they must be sorted like I2CP SessionConfig duplicate keys are not allowed.
// In practice routers do not seem to allow duplicate keys.
func mappingOrder(values MappingValues) {
sort.SliceStable(values, func(i, j int) bool {
// Lexographic sort on keys only
data1, _ := values[i][0].Data()
data2, _ := values[j][0].Data()
return data1 < data2
})
}
// ReadMappingValues returns *MappingValues from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingValues, remainder_bytes []byte, errs []error) {
// mapping := remainder
// var remainder = mapping
// var err error
log.WithFields(logrus.Fields{
"input_length": len(remainder),
"map_length": map_length.Int(),
}).Debug("Reading MappingValues")
if remainder == nil || len(remainder) < 1 {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "data shorter than expected",
}).Error("mapping contained no data")
errs = []error{errors.New("mapping contained no data")}
return
}
map_values := make(MappingValues, 0)
int_map_length := map_length.Int()
mapping_len := len(remainder)
if mapping_len > int_map_length {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
"reason": "data longer than expected",
}).Warn("mapping format warning")
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
} else if int_map_length > mapping_len {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
"reason": "data shorter than expected",
}).Warn("mapping format warning")
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
}
encounteredKeysMap := map[string]bool{}
// pop off length bytes before parsing kv pairs
// remainder = remainder[2:]
for {
// Read a key, breaking on fatal errors
// and appending warnings
// Minimum byte length required for another KV pair.
// Two bytes for each string length
// At least 1 byte per string
// One byte for =
// One byte for ;
if len(remainder) < 6 {
// Not returning an error here as the issue is already flagged by mapping length being wrong.
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "mapping format violation",
}).Warn("mapping format violation, too few bytes for a kv pair")
break
}
key_str, more, err := ReadI2PString(remainder)
if err != nil {
if stopValueRead(err) {
errs = append(errs, err)
// return
}
}
// overwriting remainder with more as another var to prevent memory weirdness in loops
remainder = more
// log.Printf("(MAPPING VALUES DEBUG) Remainder: %s\n", remainder)
// Check if key has already been encountered in this mapping
keyBytes, _ := key_str.Data()
keyAsString := string(keyBytes)
_, ok := encounteredKeysMap[keyAsString]
if ok {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "duplicate key in mapping",
"key": string(key_str),
}).Error("mapping format violation")
log.Printf("DUPE: %s", key_str)
errs = append(errs, errors.New("mapping format violation, duplicate key in mapping"))
// Based on other implementations this does not seem to happen often?
// Java throws an exception in this case, the base object is a Hashmap so the value is overwritten and an exception is thrown.
// i2pd as far as I can tell just overwrites the original value
// Continue on, we can check if the Mapping contains duplicate keys later.
}
if !beginsWith(remainder, 0x3d) {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected =",
"value:": string(remainder),
}).Warn("mapping format violation")
errs = append(errs, errors.New("mapping format violation, expected ="))
log.Printf("ERRVAL: %s", remainder)
break
} else {
remainder = remainder[1:]
}
// Read a value, breaking on fatal errors
// and appending warnings
val_str, more, err := ReadI2PString(remainder)
if err != nil {
if stopValueRead(err) {
errs = append(errs, err)
// return
}
}
// overwriting remainder with more as another var to prevent memory weirdness in loops
remainder = more
// log.Printf("(MAPPING VALUES DEBUG) Remainder: %s\n", remainder)
// log.Printf("(MAPPING VALUES DEBUG) String: value: %s", val_str)
if !beginsWith(remainder, 0x3b) {
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected ;",
"value:": string(remainder),
}).Warn("mapping format violation")
errs = append(errs, errors.New("mapping format violation, expected ;"))
break
} else {
remainder = remainder[1:]
}
// Append the key-value pair and break if there is no more data to read
map_values = append(map_values, [2]I2PString{key_str, val_str})
if len(remainder) == 0 {
break
}
// Store the encountered key with arbitrary data
encounteredKeysMap[keyAsString] = true
}
values = &map_values
log.WithFields(logrus.Fields{
"values_count": len(map_values),
"remainder_length": len(remainder_bytes),
"error_count": len(errs),
}).Debug("Finished reading MappingValues")
return
}

View File

@ -0,0 +1,47 @@
package data
import (
"fmt"
"testing"
)
func TestMappingOrderSortsValuesThenKeys(t *testing.T) {
a, _ := ToI2PString("a")
b, _ := ToI2PString("b")
aa, _ := ToI2PString("aa")
ab, _ := ToI2PString("ab")
ac, _ := ToI2PString("ac")
values := MappingValues{
[2]I2PString{b, b},
[2]I2PString{ac, a},
[2]I2PString{ab, b},
[2]I2PString{aa, a},
[2]I2PString{a, a},
}
mappingOrder(values)
for i, pair := range values {
key, _ := pair[0].Data()
switch i {
case 0:
if !(key == "a") {
t.Fatal(fmt.Sprintf("mappingOrder expected key a, got %s at index", key), i)
}
case 1:
if !(key == "aa") {
t.Fatal(fmt.Sprintf("mappingOrder expected key aa, got %s at index", key), i)
}
case 2:
if !(key == "ab") {
t.Fatal(fmt.Sprintf("mappingOrder expected key ab, got %s at index", key), i)
}
case 3:
if !(key == "ac") {
t.Fatal(fmt.Sprintf("mappingOrder expected key ac, got %s at index", key), i)
}
case 4:
if !(key == "b") {
t.Fatal(fmt.Sprintf("mappingOrder expected key b, got %s at index", key), i)
}
}
}
}

180
lib/common/data/string.go Normal file
View File

@ -0,0 +1,180 @@
package data
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
)
// STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string
const STRING_MAX_SIZE = 255
/*
[I2P String]
Accurate for version 0.9.49
Description
Represents a UTF-8 encoded string.
Contents
1 or more bytes where the first byte is the number of bytes (not characters!) in the string
and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
Length limit is 255 bytes (not characters). Length may be 0.
*/
// I2PString is the represenation of an I2P String.
//
// https://geti2p.net/spec/common-structures#string
type I2PString []byte
// Length returns the length specified in the first byte.
// Returns error if the specified does not match the actual length or the string is otherwise invalid.
func (str I2PString) Length() (length int, err error) {
if len(str) == 0 {
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"reason": "no data",
}).Error("error parsing string")
err = errors.New("error parsing string: zero length")
return
}
l, _, err := NewInteger(str[:], 1)
if err != nil {
log.WithError(err).Error("Failed to create Integer from I2PString")
return l.Int(), err
}
length = l.Int()
str_len := len(str)
if length > str_len {
/*log.WithFields(log.Fields{
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"data": string(str),
"reason": "data less than specified by length",
}).Error("string format warning")*/
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"reason": "data less than specified by length",
}).Warn("string format warning")
err = errors.New("string parsing warning: string data is shorter than specified by length")
}
return
}
// Data returns the I2PString content as a string trimmed to the specified length and not including the length byte.
// Returns error encountered by Length.
func (str I2PString) Data() (data string, err error) {
length, err := str.Length()
if err != nil {
switch err.Error() {
case "error parsing string: zero length":
log.WithError(err).Warn("Zero length I2PString")
return
case "string parsing warning: string data is shorter than specified by length":
log.WithError(err).Warn("I2PString data shorter than specified length")
if is, e := ToI2PString(string(str[:])); e != nil {
log.WithError(e).Error("Failed to convert short I2PString")
return "", e
} else {
return is.Data()
}
case "string parsing warning: string contains data beyond length":
log.WithError(err).Warn("I2PString contains data beyond specified length")
data = string(str[1:])
return
}
}
if length == 0 {
log.Debug("I2PString is empty")
return
}
data = string(str[1 : length+1])
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved I2PString data")
return
}
// ToI2PString converts a Go string to an I2PString.
// Returns error if the string exceeds STRING_MAX_SIZE.
func ToI2PString(data string) (str I2PString, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Converting string to I2PString")
data_len := len(data)
if data_len > STRING_MAX_SIZE {
log.WithFields(logrus.Fields{
"at": "ToI2PI2PString",
"string_len": data_len,
"max_len": STRING_MAX_SIZE,
"reason": "too much data",
}).Error("cannot create I2P string")
err = errors.New("cannot store that much data in I2P string")
return
}
i2p_string := []byte{byte(data_len)}
i2p_string = append(i2p_string, []byte(data)...)
str = I2PString(i2p_string)
log.WithFields(logrus.Fields{
"i2pstring_length": len(str),
}).Debug("Successfully converted string to I2PString")
return
}
//
// Read a string from a slice of bytes, returning any extra data on the end
// of the slice and any errors encountered parsing the I2PString.
//
// ReadI2PString returns I2PString from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
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
}
// NewI2PString creates a new *I2PString from []byte using ReadI2PString.
// Returns a pointer to I2PString unlike ReadI2PString.
/*func NewI2PString(data []byte) (str *I2PString, remainder []byte, err error) {
objstr, remainder, err := ReadI2PString(data)
str = &objstr
return
}*/

View File

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

View File

@ -1,45 +0,0 @@
package common
/*
I2P Date
https://geti2p.net/spec/common-structures#date
Accurate for version 0.9.24
*/
import (
"errors"
log "github.com/sirupsen/logrus"
"time"
)
type Date [8]byte
const DATE_SIZE = 8
//
// Time takes the value stored in date as an 8 byte big-endian integer representing the
// number of milliseconds since the beginning of unix time and converts it to a Go time.Time
// struct.
//
func (date Date) Time() (date_time time.Time) {
seconds, _ := NewInteger(date[:])
date_time = time.Unix(0, int64(seconds.Value()*1000000))
return
}
func ReadDate(data []byte) (h Date, remainder []byte, err error) {
if len(data) < DATE_SIZE {
log.WithFields(log.Fields{
"at": "(Date) ReadDate",
"data_len": len(data),
"required_len": "8",
"reason": "date missing data",
}).Error("date error")
err = errors.New("error reading date, insufficient length")
copy(h[:], data[0:len(data)-1])
} else {
copy(h[:], data[0:DATE_SIZE-1])
copy(remainder, data[DATE_SIZE-1:])
}
return
}

View File

@ -1,62 +0,0 @@
package common
/*
I2P Destination
https://geti2p.net/spec/common-structures#destination
Accurate for version 0.9.24
Identical to KeysAndCert
*/
import (
"github.com/go-i2p/go-i2p/lib/common/base32"
"github.com/go-i2p/go-i2p/lib/common/base64"
"github.com/go-i2p/go-i2p/lib/crypto"
"strings"
)
//
// A Destination is a KeysAndCert with functionallity
// for generating base32 and base64 addresses.
//
type Destination struct {
KeysAndCert
}
func (destination Destination) PublicKey() (crypto.PublicKey, error) {
return destination.KeysAndCert.GetPublicKey()
}
func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, error) {
return destination.KeysAndCert.GetSigningPublicKey()
}
func (destination Destination) Certificate() (CertificateInterface, error) {
return destination.KeysAndCert.GetCertificate()
}
//
// Generate the I2P base32 address for this Destination.
//
func (destination Destination) Base32Address() (str string) {
hash := crypto.SHA256(destination.Cert())
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
str = str + ".b32.i2p"
return
}
//
// Generate the I2P base64 address for this Destination.
//
func (destination Destination) Base64() string {
return base64.EncodeToString(destination.Cert())
}
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
keys_and_cert, remainder, err := ReadKeysAndCert(data)
if err != nil {
return
}
destination.KeysAndCert = keys_and_cert
return
}

View File

@ -0,0 +1,87 @@
// Package destination implements the I2P Destination common data structure
package destination
import (
"strings"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"github.com/go-i2p/go-i2p/lib/common/base32"
"github.com/go-i2p/go-i2p/lib/common/base64"
"github.com/go-i2p/go-i2p/lib/crypto"
)
var log = logger.GetGoI2PLogger()
/*
[Destination]
Accurate for version 0.9.49
Description
A Destination defines a particular endpoint to which messages can be directed for secure delivery.
Contents
Identical to KeysAndCert.
*/
// Destination is the represenation of an I2P Destination.
//
// https://geti2p.net/spec/common-structures#destination
type Destination struct {
KeysAndCert
}
// Base32Address returns the I2P base32 address for this Destination.
func (destination Destination) Base32Address() (str string) {
log.Debug("Generating Base32 address for Destination")
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 {
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

@ -0,0 +1,42 @@
# destination
--
import "github.com/go-i2p/go-i2p/lib/common/destination"
Package destination implements the I2P Destination common data structure
## Usage
#### type Destination
```go
type Destination struct {
KeysAndCert
}
```
Destination is the represenation of an I2P Destination.
https://geti2p.net/spec/common-structures#destination
#### func ReadDestination
```go
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error)
```
ReadDestination returns Destination from a []byte. The remaining bytes after the
specified length are also returned. Returns a list of errors that occurred
during parsing.
#### func (Destination) Base32Address
```go
func (destination Destination) Base32Address() (str string)
```
Base32Address returns the I2P base32 address for this Destination.
#### func (Destination) Base64
```go
func (destination Destination) Base64() string
```
Base64 returns the I2P base64 address for this Destination.

View File

@ -1,8 +1,7 @@
FROM golang
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install libsodium-dev -y
apt-get upgrade -y
RUN go get github.com/dvyukov/go-fuzz/go-fuzz
RUN go get github.com/dvyukov/go-fuzz/go-fuzz-build

View File

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/certificate"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,9 +1,9 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
import common "github.com/go-i2p/go-i2p/lib/common/certificate"
func Fuzz(data []byte) int {
cert := common.Certificate(data)
cert, _, _ := common.ReadCertificate(data)
cert.Data()
cert.Length()
cert.Type()

View File

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/destination"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,9 +1,9 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
import common "github.com/go-i2p/go-i2p/lib/common/destination"
func Fuzz(data []byte) int {
destination := common.Destination(data)
destination, _, _ := common.ReadDestination(data)
destination.Base32Address()
destination.Base64()
return 0

View File

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/keys_and_cert"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,6 +1,6 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
import common "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
func Fuzz(data []byte) int {
keys_and_cert, _, _ := common.ReadKeysAndCert(data)

View File

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/router_address"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,6 +1,6 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
import common "github.com/go-i2p/go-i2p/lib/common/router_address"
func Fuzz(data []byte) int {
router_address, _, _ := common.ReadRouterAddress(data)

View File

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/router_identity"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,11 +1,11 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
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

@ -0,0 +1,12 @@
# exportable
--
import "github.com/go-i2p/go-i2p/lib/common/fuzz/string"
## Usage
#### func Fuzz
```go
func Fuzz(data []byte) int
```

View File

@ -1,9 +1,9 @@
package exportable
import "github.com/go-i2p/go-i2p/lib/common"
import common "github.com/go-i2p/go-i2p/lib/common/data"
func Fuzz(data []byte) int {
str, _, _ := common.ReadString(data)
str := common.I2PString(data)
str.Data()
str.Length()
str, _ = common.ToI2PString(string(data))

View File

@ -1,48 +0,0 @@
package common
import (
"crypto/sha256"
"errors"
log "github.com/sirupsen/logrus"
"io"
)
const HASH_SIZE = 32
// sha256 hash of some data
type Hash [32]byte
// calculate sha256 of a byte slice
func HashData(data []byte) (h Hash) {
h = sha256.Sum256(data)
return
}
// calulate sha256 of all data being read from an io.Reader
// return error if one occurs while reading from reader
func HashReader(r io.Reader) (h Hash, err error) {
sha := sha256.New()
_, err = io.Copy(sha, r)
if err == nil {
d := sha.Sum(nil)
copy(h[:], d)
}
return
}
func ReadHash(data []byte) (h Hash, remainder []byte, err error) {
if len(data) < HASH_SIZE {
log.WithFields(log.Fields{
"at": "(Hash) ReadHash",
"data_len": len(data),
"required_len": "32",
"reason": "hash missing data",
}).Error("hash error")
err = errors.New("error reading hash, insufficient length")
copy(h[:], data[0:len(data)-1])
} else {
copy(h[:], data[0:HASH_SIZE-1])
copy(remainder, data[HASH_SIZE-1:])
}
return
}

View File

@ -1,33 +0,0 @@
package common
/*
I2P Tunnel Identity Helpers
https://geti2p.net/spec/common-structures#ident
Accurate for version 0.9.24
*/
import (
"errors"
log "github.com/sirupsen/logrus"
)
type Ident [4]byte
const IDENT_SIZE = 4
func ReadIdent(data []byte) (h Ident, remainder []byte, err error) {
if len(data) < IDENT_SIZE {
log.WithFields(log.Fields{
"at": "(Ident) ReadIdent",
"data_len": len(data),
"required_len": "8",
"reason": "ident missing data",
}).Error("ident error")
err = errors.New("error reading ident, insufficient length")
copy(h[:], data[0:len(data)-1])
} else {
copy(h[:], data[0:IDENT_SIZE-1])
copy(remainder, data[IDENT_SIZE-1:])
}
return
}

View File

@ -1,83 +0,0 @@
package common
/*
I2P Integer
https://geti2p.net/spec/common-structures#integer
Accurate for version 0.9.24
*/
import (
"encoding/binary"
// log "github.com/sirupsen/logrus"
// "errors"
)
// Total byte length of an I2P integer
const (
INTEGER_SIZE = 8
)
type Integer []byte
func (i *Integer) longBytes() (value [INTEGER_SIZE]byte) {
value = [INTEGER_SIZE]byte{0, 0, 0, 0, 0, 0, 0, 0}
pad := INTEGER_SIZE - len([]byte(*i))
for index, element := range []byte(*i) {
value[pad+index] = element
}
return value
}
func (i *Integer) Value() int {
if i == nil {
return 0
}
r := i.longBytes()
// log.Println("LONG BYTES", r)
return int(binary.BigEndian.Uint64(r[:]))
// return int(binary.BigEndian.Int64(r[:]))
}
func (i *Integer) Bytes() []byte {
if i == nil {
return []byte{}
}
if len([]byte(*i)) == 0 {
return []byte{0}
}
r := []byte(*i)
return r
}
//
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
// integer and return an int representation.
//
func NewInteger(number []byte) (value *Integer, err error) {
var integer Integer = number
value = &integer //[INTEGER_SIZE]byte(number)
// for index, element := range number {
// value[INTEGER_SIZE-1-index] = element
// }
/*length := len(number)
if length < INTEGER_SIZE {
log.WithFields(log.Fields{
"at": "(Integer) NewInteger",
"length": length,
"required_len": INTEGER_SIZE,
"reason": "not enough data",
}).Error("error parsing Integer")
err = errors.New("error parsing Integer, not enough data")
}else if length > INTEGER_SIZE{
log.WithFields(log.Fields{
"at": "(Integer) NewInteger",
"length": length,
"required_len": INTEGER_SIZE,
"reason": "too much data",
}).Error("error parsing Integer")
err = errors.New("error parsing Integer, too much data")
}else{
err = nil
}*/
return
}

View File

@ -1,34 +0,0 @@
package common
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestIntegerBigEndian(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
integer, err := NewInteger(bytes)
assert.Nil(err)
assert.Equal(integer.Value(), 1, "Integer() did not parse bytes big endian")
checkbytes := integer.Bytes()
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
}
func TestWorksWithOneByte(t *testing.T) {
assert := assert.New(t)
bytes := []byte{0x00}
integer, err := NewInteger(bytes)
assert.Nil(err)
assert.Equal(integer.Value(), 0, "Integer() did not correctly parse single byte slice")
checkbytes := integer.Bytes()
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
}

View File

@ -1,303 +0,0 @@
package common
/*
I2P Key Certificate
https://geti2p.net/spec/common-structures#certificate
Accurate for version 0.9.24
+----+----+----+----+----+-//
|type| length | payload
+----+----+----+----+----+-//
type :: Integer
length -> 1 byte
case 0 -> NULL
case 1 -> HASHCASH
case 2 -> HIDDEN
case 3 -> SIGNED
case 4 -> MULTIPLE
case 5 -> KEY
length :: Integer
length -> 2 bytes
payload :: data
length -> $length bytes
*/
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
// Key Certificate Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1 = iota
KEYCERT_SIGN_P256
KEYCERT_SIGN_P384
KEYCERT_SIGN_P521
KEYCERT_SIGN_RSA2048
KEYCERT_SIGN_RSA3072
KEYCERT_SIGN_RSA4096
KEYCERT_SIGN_ED25519
KEYCERT_SIGN_ED25519PH
)
// Key Certificate Public Key Types
const (
KEYCERT_CRYPTO_ELG = iota
KEYCERT_CRYPTO_P256
KEYCERT_CRYPTO_P384
KEYCERT_CRYPTO_P521
KEYCERT_CRYPTO_X25519
)
// SigningPublicKey sizes for Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
KEYCERT_SIGN_P256_SIZE = 64
KEYCERT_SIGN_P384_SIZE = 96
KEYCERT_SIGN_P521_SIZE = 132
KEYCERT_SIGN_RSA2048_SIZE = 256
KEYCERT_SIGN_RSA3072_SIZE = 384
KEYCERT_SIGN_RSA4096_SIZE = 512
KEYCERT_SIGN_ED25519_SIZE = 32
KEYCERT_SIGN_ED25519PH_SIZE = 32
)
// PublicKey sizes for Public Key Types
const (
KEYCERT_CRYPTO_ELG_SIZE = 256
)
// Sizes of structures in KeyCertificates
const (
KEYCERT_PUBKEY_SIZE = 256
KEYCERT_SPK_SIZE = 128
)
const (
KEYCERT_MIN_SIZE = 7
)
type KeyCertificate struct {
CertificateInterface
PKType *Integer
PKExtra []byte
SPKType *Integer
SPKExtra []byte
} //[]byte
//
// The data contained in the Key Certificate.
//
func (key_certificate KeyCertificate) Data() ([]byte, error) {
var r []byte
r = append(r, key_certificate.CertificateInterface.Cert()...)
r = append(r, key_certificate.PKType.Bytes()...)
r = append(r, key_certificate.SPKType.Bytes()...)
return r, nil
}
//
// The SigningPublicKey type this Key Certificate describes and any errors encountered
// parsing the KeyCertificate.
//
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int, err error) {
// signing_key_type := key_certificate.SPKType
// data_len := len(key_certificate.CertificateInterface.CertBytes)
if len(key_certificate.SPKType.Bytes()) < 2 {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SigningPublicKeyType",
"data_len": len(key_certificate.SPKType.Bytes()),
"required_len": 2,
"reason": "not enough data",
}).Error("error retrieving Signing Public Key type")
err = errors.New("error retrieving signing public key type: not enough data")
return
}
log.Println("Signing Public Key Type", key_certificate.SPKType) //.Value())
return key_certificate.SPKType.Value(), nil
}
//
// The PublicKey type this Key Certificate describes and any errors encountered parsing
// this KeyCertificate.
//
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
if len(key_certificate.PKType.Bytes()) < 2 {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SingingPublicKeyType",
"data_len": len(key_certificate.PKType.Bytes()),
"required_len": 2,
"reason": "not enough data",
}).Error("error retrieving Singning Public Key type")
err = errors.New("error retrieving signing public key type: not enough data")
return
}
log.Println("Public Key Type", key_certificate.PKType)
return key_certificate.PKType.Value(), nil
}
//
// Given some bytes, build a PublicKey using any excess data that may be stored in the KeyCertificate and return
// it along with any errors encountered constructing the PublicKey.
//
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) {
key_type, err := key_certificate.SigningPublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < KEYCERT_PUBKEY_SIZE {
log.WithFields(log.Fields{
"at": "(KeyCertificate) ConstructPublicKey",
"data_len": data_len,
"required_len": KEYCERT_PUBKEY_SIZE,
"reason": "not enough data",
}).Error("error constructing public key")
err = errors.New("error constructing public key: not enough data")
return
}
switch key_type {
case KEYCERT_CRYPTO_ELG:
var elg_key crypto.ElgPublicKey
copy(elg_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
public_key = elg_key
}
return
}
//
// Given some bytes, build a SigningPublicKey using any excess data that may be stored in the KeyCertificate and return
// it along with any errors encountered constructing the SigningPublicKey.
//
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
signing_key_type, err := key_certificate.PublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < KEYCERT_SPK_SIZE {
log.WithFields(log.Fields{
"at": "(KeyCertificate) ConstructSigningPublicKey",
"data_len": data_len,
"required_len": KEYCERT_SPK_SIZE,
"reason": "not enough data",
}).Error("error constructing signing public key")
err = errors.New("error constructing signing public key: not enough data")
return
}
switch signing_key_type {
case KEYCERT_SIGN_DSA_SHA1:
var dsa_key crypto.DSAPublicKey
copy(dsa_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_DSA_SHA1_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = dsa_key
case KEYCERT_SIGN_P256:
var ec_key crypto.ECP256PublicKey
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P256_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_key
case KEYCERT_SIGN_P384:
var ec_key crypto.ECP384PublicKey
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P384_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_key
case KEYCERT_SIGN_P521:
var ec_key crypto.ECP521PublicKey
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
copy(ec_key[:], data)
d, _ := key_certificate.Data()
copy(ec_key[KEYCERT_SPK_SIZE:], d[4:4+extra])
signing_public_key = ec_key
case KEYCERT_SIGN_RSA2048:
//var rsa_key crypto.RSA2048PublicKey
//extra := KEYCERT_SIGN_RSA2048_SIZE - 128
//copy(rsa_key[:], data)
//copy(rsa_key[128:], key_certificate[4:4+extra])
//signing_public_key = rsa_key
case KEYCERT_SIGN_RSA3072:
case KEYCERT_SIGN_RSA4096:
case KEYCERT_SIGN_ED25519:
case KEYCERT_SIGN_ED25519PH:
}
return
}
//
// Return the size of a Signature corresponding to the Key Certificate's
// SigningPublicKey type.
//
func (key_certificate KeyCertificate) SignatureSize() (size int) {
sizes := map[int]int{
KEYCERT_SIGN_DSA_SHA1: 40,
KEYCERT_SIGN_P256: 64,
KEYCERT_SIGN_P384: 96,
KEYCERT_SIGN_P521: 132,
KEYCERT_SIGN_RSA2048: 256,
KEYCERT_SIGN_RSA3072: 384,
KEYCERT_SIGN_RSA4096: 512,
KEYCERT_SIGN_ED25519: 64,
KEYCERT_SIGN_ED25519PH: 64,
}
key_type, err := key_certificate.SigningPublicKeyType()
if err != nil {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SignatureSize",
"key_type": key_type,
"reason": "failed to read signing public key type",
}).Error("error getting signature size")
return 0
}
return sizes[int(key_type)]
}
//
// Read a KeyCertificate from a slice of bytes
//
func ReadKeyCertificate(data []byte) (key_certificate KeyCertificate, err error) {
key_certificate.SPKType = &Integer{}
key_certificate.PKType = &Integer{}
cert, remainder, err := ReadCertificate(data)
if err != nil {
return
}
cert_type, _, err := cert.Type()
if err != nil {
return
}
log.Println("KEYSANDCERT CERT TYPE=", cert_type, cert.CertBytes, remainder)
key_certificate.CertificateInterface = cert
data = cert.Cert()
data_len := len(data)
if data_len < KEYCERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "(KeyCertificate) PublicKeyType",
"data_len": data_len,
"required_len": KEYCERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing key certificate public key")
err = errors.New("error parsing key certificate public key: not enough data")
return
}
log.Println("KEYSANDCERT=", data, "| len=", data_len, "| 0=", data[0], "| 1=", data[1])
key_certificate.SPKType, err = NewInteger(data[len(data)-2 : len(data)])
if err != nil {
log.WithFields(log.Fields{
"at": "(KeyCertificate) SigningPublicKeyType",
"key_type": key_certificate.PKType,
"reason": "failed to read signing public key type",
}).Error("error parsing key certificate signing public key")
}
key_certificate.PKType, err = NewInteger(data[len(data)-4 : len(data)-2])
if err != nil {
log.WithFields(log.Fields{
"at": "(KeyCertificate) PublicKeyType",
"key_type": key_certificate.PKType,
"reason": "failed to read public key type",
}).Error("error parsing key certificate public key")
err = errors.New("error parsing key certificate public key: not enough data")
}
return
}

View File

@ -0,0 +1,154 @@
# key_certificate
--
import "github.com/go-i2p/go-i2p/lib/common/key_certificate"
Package key_certificate implements the I2P Destination common data structure
## Usage
```go
const (
KEYCERT_SIGN_DSA_SHA1 = iota
KEYCERT_SIGN_P256
KEYCERT_SIGN_P384
KEYCERT_SIGN_P521
KEYCERT_SIGN_RSA2048
KEYCERT_SIGN_RSA3072
KEYCERT_SIGN_RSA4096
KEYCERT_SIGN_ED25519
KEYCERT_SIGN_ED25519PH
)
```
Key Certificate Signing Key Types
```go
const (
KEYCERT_CRYPTO_ELG = iota
KEYCERT_CRYPTO_P256
KEYCERT_CRYPTO_P384
KEYCERT_CRYPTO_P521
KEYCERT_CRYPTO_X25519
)
```
Key Certificate Public Key Types
```go
const (
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
KEYCERT_SIGN_P256_SIZE = 64
KEYCERT_SIGN_P384_SIZE = 96
KEYCERT_SIGN_P521_SIZE = 132
KEYCERT_SIGN_RSA2048_SIZE = 256
KEYCERT_SIGN_RSA3072_SIZE = 384
KEYCERT_SIGN_RSA4096_SIZE = 512
KEYCERT_SIGN_ED25519_SIZE = 32
KEYCERT_SIGN_ED25519PH_SIZE = 32
)
```
SigningPublicKey sizes for Signing Key Types
```go
const (
KEYCERT_CRYPTO_ELG_SIZE = 256
KEYCERT_CRYPTO_P256_SIZE = 64
KEYCERT_CRYPTO_P384_SIZE = 96
KEYCERT_CRYPTO_P521_SIZE = 132
KEYCERT_CRYPTO_X25519_SIZE = 32
)
```
PublicKey sizes for Public Key Types
```go
const (
KEYCERT_PUBKEY_SIZE = 256
KEYCERT_SPK_SIZE = 128
)
```
Sizes of structures in KeyCertificates
```go
const (
KEYCERT_MIN_SIZE = 7
)
```
#### type KeyCertificate
```go
type KeyCertificate struct {
Certificate
}
```
type KeyCertificate []byte
#### func KeyCertificateFromCertificate
```go
func KeyCertificateFromCertificate(certificate Certificate) *KeyCertificate
```
KeyCertificateFromCertificate returns a *KeyCertificate from a *Certificate.
#### func NewKeyCertificate
```go
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder []byte, err error)
```
NewKeyCertificate creates a new *KeyCertificate from []byte using
ReadCertificate. The remaining bytes after the specified length are also
returned. Returns a list of errors that occurred during parsing.
#### func (KeyCertificate) ConstructPublicKey
```go
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error)
```
ConstructPublicKey returns a PublicKey constructed using any excess data that
may be stored in the KeyCertififcate. Returns enr errors encountered while
parsing.
#### func (KeyCertificate) ConstructSigningPublicKey
```go
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error)
```
ConstructSigningPublicKey returns a SingingPublicKey constructed using any
excess data that may be stored in the KeyCertificate. Returns any errors
encountered while parsing.
#### func (KeyCertificate) CryptoSize
```go
func (key_certificate KeyCertificate) CryptoSize() (size int)
```
CryptoSize return the size of a Public Key corresponding to the Key
Certificate's PublicKey type.
#### func (KeyCertificate) Data
```go
func (key_certificate KeyCertificate) Data() ([]byte, error)
```
Data returns the raw []byte contained in the Certificate.
#### func (KeyCertificate) PublicKeyType
```go
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int)
```
PublicKeyType returns the PublicKey type as a Go integer.
#### func (KeyCertificate) SignatureSize
```go
func (key_certificate KeyCertificate) SignatureSize() (size int)
```
SignatureSize return the size of a Signature corresponding to the Key
Certificate's SigningPublicKey type.
#### func (KeyCertificate) SigningPublicKeyType
```go
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int)
```
SigningPublicKeyType returns the SigningPublicKey type as a Go integer.

View File

@ -0,0 +1,342 @@
// Package key_certificate implements the I2P Destination common data structure
package key_certificate
/*
I2P Key Certificate
https://geti2p.net/spec/common-structures#certificate
Accurate for version 0.9.24
+----+----+----+----+----+-//
|type| length | payload
+----+----+----+----+----+-//
type :: Integer
length -> 1 byte
case 0 -> NULL
case 1 -> HASHCASH
case 2 -> HIDDEN
case 3 -> SIGNED
case 4 -> MULTIPLE
case 5 -> KEY
length :: Integer
length -> 2 bytes
payload :: data
length -> $length bytes
*/
import (
"errors"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/certificate"
. "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/crypto"
)
var log = logger.GetGoI2PLogger()
// Key Certificate Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1 = iota
KEYCERT_SIGN_P256
KEYCERT_SIGN_P384
KEYCERT_SIGN_P521
KEYCERT_SIGN_RSA2048
KEYCERT_SIGN_RSA3072
KEYCERT_SIGN_RSA4096
KEYCERT_SIGN_ED25519
KEYCERT_SIGN_ED25519PH
)
// Key Certificate Public Key Types
const (
KEYCERT_CRYPTO_ELG = iota
KEYCERT_CRYPTO_P256
KEYCERT_CRYPTO_P384
KEYCERT_CRYPTO_P521
KEYCERT_CRYPTO_X25519
)
const (
KEYCERT_MIN_SIZE = 7
)
// signingPublicKey sizes for Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1_SIZE = 128
KEYCERT_SIGN_P256_SIZE = 64
KEYCERT_SIGN_P384_SIZE = 96
KEYCERT_SIGN_P521_SIZE = 132
KEYCERT_SIGN_RSA2048_SIZE = 256
KEYCERT_SIGN_RSA3072_SIZE = 384
KEYCERT_SIGN_RSA4096_SIZE = 512
KEYCERT_SIGN_ED25519_SIZE = 32
KEYCERT_SIGN_ED25519PH_SIZE = 32
)
// publicKey sizes for Public Key Types
const (
KEYCERT_CRYPTO_ELG_SIZE = 256
KEYCERT_CRYPTO_P256_SIZE = 64
KEYCERT_CRYPTO_P384_SIZE = 96
KEYCERT_CRYPTO_P521_SIZE = 132
KEYCERT_CRYPTO_X25519_SIZE = 32
)
// Sizes of structures in KeyCertificates
const (
KEYCERT_PUBKEY_SIZE = 256
KEYCERT_SPK_SIZE = 128
)
// type KeyCertificate []byte
type KeyCertificate struct {
Certificate
spkType Integer
cpkType Integer
}
// Data returns the raw []byte contained in the Certificate.
func (key_certificate KeyCertificate) Data() ([]byte, error) {
data := key_certificate.Certificate.RawBytes()
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved raw data from keyCertificate")
return key_certificate.Certificate.RawBytes(), nil
}
// SigningPublicKeyType returns the signingPublicKey type as a Go integer.
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int) {
signing_pubkey_type = key_certificate.spkType.Int()
log.WithFields(logrus.Fields{
"signing_pubkey_type": signing_pubkey_type,
}).Debug("Retrieved signingPublicKey type")
return key_certificate.spkType.Int()
}
// PublicKeyType returns the publicKey type as a Go integer.
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int) {
pubkey_type = key_certificate.cpkType.Int()
log.WithFields(logrus.Fields{
"pubkey_type": pubkey_type,
}).Debug("Retrieved publicKey type")
return key_certificate.cpkType.Int()
}
// ConstructPublicKey returns a publicKey constructed using any excess data that may be stored in the KeyCertififcate.
// Returns enr errors encountered while parsing.
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Constructing publicKey from keyCertificate")
key_type := key_certificate.PublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < key_certificate.CryptoSize() {
log.WithFields(logrus.Fields{
"at": "(keyCertificate) ConstructPublicKey",
"data_len": data_len,
"required_len": KEYCERT_PUBKEY_SIZE,
"reason": "not enough data",
}).Error("error constructing public key")
err = errors.New("error constructing public key: not enough data")
return
}
switch key_type {
case KEYCERT_CRYPTO_ELG:
var elg_key crypto.ElgPublicKey
copy(elg_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
public_key = elg_key
log.Debug("Constructed ElgPublicKey")
case KEYCERT_CRYPTO_X25519:
var ed25519_key crypto.Ed25519PublicKey
copy(ed25519_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
public_key = ed25519_key
log.Debug("Constructed Ed25519PublicKey")
default:
log.WithFields(logrus.Fields{
"key_type": key_type,
}).Warn("Unknown public key type")
}
return
}
// ConstructSigningPublicKey returns a SingingPublicKey constructed using any excess data that may be stored in the KeyCertificate.
// Returns any errors encountered while parsing.
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Constructing signingPublicKey from keyCertificate")
signing_key_type := key_certificate.SigningPublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < key_certificate.SignatureSize() {
log.WithFields(logrus.Fields{
"at": "(keyCertificate) ConstructSigningPublicKey",
"data_len": data_len,
"required_len": KEYCERT_SPK_SIZE,
"reason": "not enough data",
}).Error("error constructing signing public key")
err = errors.New("error constructing signing public key: not enough data")
return
}
switch signing_key_type {
case KEYCERT_SIGN_DSA_SHA1:
var dsa_key crypto.DSAPublicKey
copy(dsa_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_DSA_SHA1_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = dsa_key
log.Debug("Constructed DSAPublicKey")
case KEYCERT_SIGN_P256:
var ec_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_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_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 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.
func (key_certificate KeyCertificate) SignatureSize() (size int) {
sizes := map[int]int{
KEYCERT_SIGN_DSA_SHA1: KEYCERT_SIGN_DSA_SHA1_SIZE,
KEYCERT_SIGN_P256: KEYCERT_SIGN_P256_SIZE,
KEYCERT_SIGN_P384: KEYCERT_SIGN_P384_SIZE,
KEYCERT_SIGN_P521: KEYCERT_SIGN_P521_SIZE,
KEYCERT_SIGN_RSA2048: KEYCERT_SIGN_RSA2048_SIZE,
KEYCERT_SIGN_RSA3072: KEYCERT_SIGN_RSA3072_SIZE,
KEYCERT_SIGN_RSA4096: KEYCERT_SIGN_RSA4096_SIZE,
KEYCERT_SIGN_ED25519: KEYCERT_SIGN_ED25519_SIZE,
KEYCERT_SIGN_ED25519PH: KEYCERT_SIGN_ED25519PH_SIZE,
}
key_type := key_certificate.SigningPublicKeyType()
size = sizes[int(key_type)]
log.WithFields(logrus.Fields{
"key_type": key_type,
"signature_size": size,
}).Debug("Retrieved signature size")
return sizes[int(key_type)]
}
// CryptoSize return the size of a Public Key corresponding to the Key Certificate's publicKey type.
func (key_certificate KeyCertificate) CryptoSize() (size int) {
sizes := map[int]int{
KEYCERT_CRYPTO_ELG: KEYCERT_CRYPTO_ELG_SIZE,
KEYCERT_CRYPTO_P256: KEYCERT_CRYPTO_P256_SIZE,
KEYCERT_CRYPTO_P384: KEYCERT_CRYPTO_P384_SIZE,
KEYCERT_CRYPTO_P521: KEYCERT_CRYPTO_P521_SIZE,
KEYCERT_CRYPTO_X25519: KEYCERT_CRYPTO_X25519_SIZE,
}
key_type := key_certificate.PublicKeyType()
size = sizes[int(key_type)]
log.WithFields(logrus.Fields{
"key_type": key_type,
"crypto_size": size,
}).Debug("Retrieved crypto size")
return sizes[int(key_type)]
}
// NewKeyCertificate creates a new *KeyCertificate from []byte using ReadCertificate.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Creating new keyCertificate")
var certificate Certificate
certificate, remainder, err = ReadCertificate(bytes)
if err != nil {
log.WithError(err).Error("Failed to read Certificate")
return
}
if len(bytes) < KEYCERT_MIN_SIZE {
log.WithError(err).Error("keyCertificate data too short")
err = errors.New("error parsing key certificate: not enough data")
remainder = bytes[KEYCERT_MIN_SIZE:]
}
key_certificate = &KeyCertificate{
Certificate: certificate,
}
if len(bytes) >= 5 {
key_certificate.spkType = Integer(bytes[4:5])
}
if len(bytes) >= 7 {
key_certificate.cpkType = Integer(bytes[6:7])
}
log.WithFields(logrus.Fields{
"spk_type": key_certificate.spkType.Int(),
"cpk_type": key_certificate.cpkType.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully created new keyCertificate")
return
}
// KeyCertificateFromCertificate returns a *KeyCertificate from a *Certificate.
func KeyCertificateFromCertificate(certificate Certificate) *KeyCertificate {
log.Debug("Creating keyCertificate from Certificate")
// k, _, _ := NewKeyCertificate(certificate.RawBytes())
k, _, err := NewKeyCertificate(certificate.RawBytes())
if err != nil {
log.WithError(err).Error("Failed to create keyCertificate from Certificate")
} else {
log.Debug("Successfully created keyCertificate from Certificate")
}
return k
}

View File

@ -1,4 +1,4 @@
package common
package key_certificate
import (
"testing"
@ -9,60 +9,52 @@ import (
func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
spk_type, err := key_cert.SigningPublicKeyType()
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
pk_type := key_cert.SigningPublicKeyType()
assert.Nil(err, "SigningPublicKeyType() returned error with valid data")
assert.Equal(spk_type, KEYCERT_SIGN_DSA_SHA1, "SigningPublicKeyType() did not return correct type")
assert.Equal(pk_type, KEYCERT_SIGN_P521, "SigningPublicKeyType() did not return correct typec")
}
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
func TestSingingPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
if assert.NotNil(err) {
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
}
// assert.NotNil(err, "ReadKeyCertificate() returned error with valid data")
_, err = key_cert.PublicKeyType()
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
sk_type := key_cert.SigningPublicKeyType()
assert.Equal(sk_type, 0, "SigningPublicKeyType() did not return correct typec")
if assert.NotNil(err) {
assert.Equal("error retrieving signing public key type: not enough data", err.Error(), "correct error message should be returned")
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
}
}
func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
pk_type, err := key_cert.PublicKeyType()
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
pk_type := key_cert.PublicKeyType()
assert.Nil(err, "PublicKeyType() returned error with valid data")
assert.Equal(pk_type, KEYCERT_CRYPTO_P521, "PublicKeyType() did not return correct type")
assert.Nil(err, "publicKey() returned error with valid data")
assert.Equal(pk_type, KEYCERT_SIGN_P521, "PublicKeyType() did not return correct typec")
}
func TestSigningPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
if assert.NotNil(err) {
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
}
_, err = key_cert.SigningPublicKeyType()
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
pk_type := key_cert.PublicKeyType()
if assert.NotNil(err) {
assert.Equal("error retrieving signing public key type: not enough data", err.Error(), "correct error message should be returned")
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
}
assert.Equal(pk_type, 0, "PublicKeyType() did not return correct typec")
}
/*
func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 255)
_, err = key_cert.ConstructPublicKey(data)
@ -70,25 +62,22 @@ func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
assert.Equal("error constructing public key: not enough data", err.Error(), "correct error message should be returned")
}
}
*/
/*
func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 256)
pk, err := key_cert.ConstructPublicKey(data)
assert.Nil(err, "ConstructPublicKey() returned error with valid data")
assert.Equal(pk.Len(), 256, "ConstructPublicKey() did not return public key with correct length")
}
*/
/*
func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 127)
_, err = key_cert.ConstructSigningPublicKey(data)
@ -100,44 +89,43 @@ func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
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) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
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) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
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) {
assert := assert.New(t)
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
key_cert, _, err := NewKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
data := make([]byte, 128)
spk, err := key_cert.ConstructSigningPublicKey(data)
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

@ -1,280 +0,0 @@
package common
/*
I2P KeysAndCert
https://geti2p.net/spec/common-structures#keysandcert
Accurate for version 0.9.24
+----+----+----+----+----+----+----+----+
| public_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| padding (optional) |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signing_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| certificate |
+----+----+----+-//
public_key :: PublicKey (partial or full)
length -> 256 bytes or as specified in key certificate
padding :: random data
length -> 0 bytes or as specified in key certificate
padding length + signing_key length == KEYS_AND_CERT_SPK_SIZE bytes
signing__key :: SigningPublicKey (partial or full)
length -> 128 bytes or as specified in key certificate
padding length + signing_key length == KEYS_AND_CERT_SPK_SIZE bytes
certificate :: Certificate
length -> >= 3 bytes
total length: 387+ bytes
*/
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
// Sizes of various KeysAndCert structures and requirements
const (
KEYS_AND_CERT_PUBKEY_SIZE = 256
KEYS_AND_CERT_SPK_SIZE = 128
KEYS_AND_CERT_MIN_SIZE = 387
KEYS_AND_CERT_DATA_SIZE = 384
)
type KeysAndCertInterface interface {
GetPublicKey() (key crypto.PublicKey, err error)
GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error)
GetCertificate() (cert Certificate, err error)
Bytes() (bytes []byte)
}
type KeysAndCert struct {
crypto.SigningPublicKey
crypto.PublicKey
CertificateInterface
}
func (keys_and_cert KeysAndCert) Bytes() (bytes []byte) { //, err error) {
pubkey, _ := keys_and_cert.GetPublicKey()
signpubkey, _ := keys_and_cert.GetSigningPublicKey()
elg_key := pubkey.(crypto.ElgPublicKey)
dsa_key := signpubkey.(crypto.DSAPublicKey)
bytes = append(bytes, dsa_key[:]...)
bytes = append(bytes, elg_key[:]...)
bytes = append(bytes, keys_and_cert.CertificateInterface.Cert()...)
return
}
//
// Return the PublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
// determine correct lengths.
//
func (keys_and_cert KeysAndCert) GetPublicKey() (key crypto.PublicKey, err error) {
data := make([]byte, KEYS_AND_CERT_PUBKEY_SIZE)
if keys_and_cert.PublicKey == nil {
epk := crypto.ElgPublicKey{}
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], epk[:])
keys_and_cert.PublicKey = epk
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
}
/*cert, err := keys_and_cert.GetCertificate()
if err != nil {
return
}
cert_len, err := cert.Length()
if err != nil {
return
}
if cert_len != 0 {*/
key = keys_and_cert.PublicKey
/*}*/
return
}
//
// Return the SigningPublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
// determine correct lengths.
//
func (keys_and_cert KeysAndCert) GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
if keys_and_cert.SigningPublicKey == nil {
keys_and_cert.SigningPublicKey = crypto.DSAPublicKey{}
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
}
/*cert, err := keys_and_cert.GetCertificate()
if err != nil {
return
}
cert_len, err := cert.Length()
if err != nil {
return
}
if cert_len != 0 {*/
signing_public_key = keys_and_cert.SigningPublicKey
/*}*/
return
}
//
// Return the Certificate contained in the KeysAndCert and any errors encountered while parsing the
// KeysAndCert or Certificate.
//
func (keys_and_cert KeysAndCert) GetCertificate() (cert CertificateInterface, err error) {
data_len := len(keys_and_cert.Bytes())
log.Println("LEN IS", data_len, "KEYS_AND_CERT_MIN_SIZE", KEYS_AND_CERT_MIN_SIZE)
if data_len < KEYS_AND_CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "GetCertificate",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing keys and cert")
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
}
/*if data_len > CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "too much data",
}).Error("error parsing keys and cert")
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
}*/
cert = keys_and_cert.CertificateInterface
return
}
func ReadKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.PublicKey, remainder []byte, err error) {
data_len := len(data)
if data_len < KEYS_AND_CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "ReadKeys",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing keys and cert")
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
return
}
if cert == nil {
// No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
// PublicKey space as ElgPublicKey.
var elg_key crypto.ElgPublicKey
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
pk = elg_key
} else {
// A Certificate is present in this KeysAndCert
cert_type, cert_bytes, e := cert.Type()
err = e
if cert_type == CERT_KEY {
// This KeysAndCert contains a Key Certificate, construct
// a PublicKey from the data in the KeysAndCert and
// any additional data in the Certificate.
cert_integer, _ := NewInteger(cert_bytes)
pk, err = KeyCertificate{PKType: cert_integer}.ConstructPublicKey(
data[:KEYS_AND_CERT_PUBKEY_SIZE],
)
} else {
// Key Certificate is not present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
// PublicKey space as ElgPublicKey. No other Certificate
// types are currently in use.
var elg_key crypto.ElgPublicKey
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
pk = elg_key
log.WithFields(log.Fields{
"at": "(KeysAndCert) PublicKey",
"cert_type": cert_type,
}).Warn("unused certificate type observed")
}
// }
if data_len == 0 {
// No Certificate is present, return the KEYS_AND_CERT_SPK_SIZE byte
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
spk = dsa_pk
} else {
// A Certificate is present in this KeysAndCert
cert_type, cert_bytes, e := cert.Type()
err = e
if cert_type == CERT_KEY {
// This KeysAndCert contains a Key Certificate, construct
// a SigningPublicKey from the data in the KeysAndCert and
// any additional data in the Certificate.
cert_integer, _ := NewInteger(cert_bytes)
spk, err = KeyCertificate{SPKType: cert_integer}.ConstructSigningPublicKey(
data[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE],
)
} else {
// Key Certificate is not present, return the KEYS_AND_CERT_SPK_SIZE byte
// SigningPublicKey space as legacy SHA DSA1 SigningPublicKey.
// No other Certificate types are currently in use.
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
spk = dsa_pk
}
}
cert_len, e := cert.Length()
err = e
if cert_len == 0 {
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
return
}
remainder = data[KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE:]
}
return
}
//
// Read a KeysAndCert from a slice of bytes, retuning it and the remaining data as well as any errors
// encoutered parsing the KeysAndCert.
//
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
data_len := len(data)
keys_and_cert.CertificateInterface = &Certificate{}
if data_len < KEYS_AND_CERT_MIN_SIZE {
log.WithFields(log.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing keys and cert")
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
return
}
cert, remainder, err := ReadCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
if err != nil {
log.WithFields(log.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "error parsing certificate",
}).Error("error parsing keys and cert")
err = errors.New("error parsing KeysAndCert: error parsing certificate")
return
}
keys_and_cert.CertificateInterface = cert
spk, pk, remainder, err := ReadKeys(data, cert)
keys_and_cert.SigningPublicKey = spk
keys_and_cert.PublicKey = pk
return
}

View File

@ -0,0 +1,66 @@
# keys_and_cert
--
import "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
Package keys_and_cert implements the I2P KeysAndCert common data structure
## Usage
```go
const (
KEYS_AND_CERT_PUBKEY_SIZE = 256
KEYS_AND_CERT_SPK_SIZE = 128
KEYS_AND_CERT_MIN_SIZE = 387
KEYS_AND_CERT_DATA_SIZE = 384
)
```
Sizes of various KeysAndCert structures and requirements
#### type KeysAndCert
```go
type KeysAndCert struct {
KeyCertificate *KeyCertificate
}
```
KeysAndCert is the represenation of an I2P KeysAndCert.
https://geti2p.net/spec/common-structures#keysandcert
#### func ReadKeysAndCert
```go
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error)
```
ReadKeysAndCert creates a new *KeysAndCert from []byte using ReadKeysAndCert.
Returns a pointer to KeysAndCert unlike ReadKeysAndCert.
#### func (KeysAndCert) Bytes
```go
func (keys_and_cert KeysAndCert) Bytes() []byte
```
Bytes returns the entire KeyCertificate in []byte form, trims payload to
specified length.
#### func (*KeysAndCert) Certificate
```go
func (keys_and_cert *KeysAndCert) Certificate() (cert Certificate)
```
Certfificate returns the certificate.
#### func (*KeysAndCert) PublicKey
```go
func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey)
```
PublicKey returns the public key as a crypto.PublicKey.
#### func (*KeysAndCert) SigningPublicKey
```go
func (keys_and_cert *KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey)
```
SigningPublicKey returns the signing public key.

View File

@ -0,0 +1,252 @@
// Package keys_and_cert implements the I2P KeysAndCert common data structure
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"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Sizes of various KeysAndCert structures and requirements
const (
KEYS_AND_CERT_PUBKEY_SIZE = 256
KEYS_AND_CERT_SPK_SIZE = 128
KEYS_AND_CERT_MIN_SIZE = 387
KEYS_AND_CERT_DATA_SIZE = 384
)
/*
[KeysAndCert]
Accurate for version 0.9.49
Description
An encryption public key, a signing public key, and a certificate, used as either a RouterIdentity or a Destination.
Contents
A publicKey followed by a signingPublicKey and then a Certificate.
+----+----+----+----+----+----+----+----+
| public_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| padding (optional) |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signing_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| certificate |
+----+----+----+-//
public_key :: publicKey (partial or full)
length -> 256 bytes or as specified in key certificate
padding :: random data
length -> 0 bytes or as specified in key certificate
padding length + signing_key length == 128 bytes
signing__key :: signingPublicKey (partial or full)
length -> 128 bytes or as specified in key certificate
padding length + signing_key length == 128 bytes
certificate :: Certificate
length -> >= 3 bytes
total length: 387+ bytes
*/
// KeysAndCert is the represenation of an I2P KeysAndCert.
//
// https://geti2p.net/spec/common-structures#keysandcert
type KeysAndCert struct {
keyCertificate *KeyCertificate
publicKey crypto.PublicKey
Padding []byte
signingPublicKey crypto.SigningPublicKey
}
// Bytes returns the entire keyCertificate in []byte form, trims payload to specified length.
func (keys_and_cert KeysAndCert) Bytes() []byte {
bytes := keys_and_cert.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.
func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey) {
return keys_and_cert.publicKey
}
// signingPublicKey returns the signing public key.
func (keys_and_cert *KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey) {
return keys_and_cert.signingPublicKey
}
// Certfificate returns the certificate.
func (keys_and_cert *KeysAndCert) Certificate() (cert Certificate) {
return keys_and_cert.keyCertificate.Certificate
}
// ReadKeysAndCert creates a new *KeysAndCert from []byte using ReadKeysAndCert.
// Returns a pointer to KeysAndCert unlike ReadKeysAndCert.
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Reading KeysAndCert from data")
data_len := len(data)
// keys_and_cert = KeysAndCert{}
if data_len < KEYS_AND_CERT_MIN_SIZE && data_len > KEYS_AND_CERT_DATA_SIZE {
log.WithFields(logrus.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing keys and cert")
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
keys_and_cert.keyCertificate, remainder, _ = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
return
} else if data_len < KEYS_AND_CERT_DATA_SIZE {
log.WithFields(logrus.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
"reason": "not enough data",
}).Error("error parsing keys and cert")
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
return
}
keys_and_cert.keyCertificate, remainder, err = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
if err != nil {
log.WithError(err).Error("Failed to create keyCertificate")
return
}
// TODO: this only supports one key type right now and it's the old key type, but the layout is the same.
// a case-switch which sets the size of the SPK and the PK should be used to replace the referenced KEYS_AND_CERT_PUBKEY_SIZE
// and KEYS_AND_CERT_SPK_SIZE constants in the future.
keys_and_cert.publicKey, err = keys_and_cert.keyCertificate.ConstructPublicKey(data[:keys_and_cert.keyCertificate.CryptoSize()])
if err != nil {
log.WithError(err).Error("Failed to construct publicKey")
return
}
keys_and_cert.signingPublicKey, err = keys_and_cert.keyCertificate.ConstructSigningPublicKey(data[KEYS_AND_CERT_DATA_SIZE-keys_and_cert.keyCertificate.SignatureSize() : KEYS_AND_CERT_DATA_SIZE])
if err != nil {
log.WithError(err).Error("Failed to construct signingPublicKey")
return
}
padding := data[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_DATA_SIZE-KEYS_AND_CERT_SPK_SIZE]
keys_and_cert.Padding = padding
log.WithFields(logrus.Fields{
"public_key_type": keys_and_cert.keyCertificate.PublicKeyType(),
"signing_public_key_type": keys_and_cert.keyCertificate.SigningPublicKeyType(),
"padding_length": len(padding),
"remainder_length": len(remainder),
}).Debug("Successfully read KeysAndCert")
return
}
// 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

@ -1,4 +1,4 @@
package common
package keys_and_cert
import (
"testing"
@ -6,29 +6,17 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCertificateWithMissingData(t *testing.T) {
/*func TestCertificateWithMissingData(t *testing.T) {
assert := assert.New(t)
//cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
data := make([]byte, 128+256)
//data = append(data, cert_data...)
keys_and_cert, remainder, err := ReadKeysAndCert(data)
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
t.Log("\n\nREMAINDER", remainder, "\n\n")
cert, err := keys_and_cert.GetCertificate()
t.Log("\n\nSTART\n\n")
data = append(data, cert_data...)
_, _, err := NewKeysAndCert(data)
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
} else {
t.Log("\n\nEND\n\n", cert.Cert())
}
// cert_bytes := []byte(cert.Cert())
// if assert.Equal(len(cert_data), len(cert_bytes)) {
// assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return available data when cert was missing some data")
// }
}
}*/
func TestCertificateWithValidData(t *testing.T) {
assert := assert.New(t)
@ -37,12 +25,13 @@ func TestCertificateWithValidData(t *testing.T) {
data := make([]byte, 128+256)
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
cert, err := keys_and_cert.GetCertificate()
assert.Nil(err)
cert_bytes := []byte(cert.Cert())
cert := keys_and_cert.Certificate()
cert_bytes := cert.Bytes()
if assert.Equal(len(cert_data), len(cert_bytes)) {
assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return correct data with valid cert")
assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return correct data with valid cert")
}
}
@ -56,13 +45,11 @@ func TestPublicKeyWithBadData(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
//pub_key
_, err = keys_and_cert.GetPublicKey()
pub_key := keys_and_cert.PublicKey()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
//assert.Nil(pub_key)
assert.Nil(pub_key)
}
func TestPublicKeyWithBadCertificate(t *testing.T) {
@ -75,13 +62,11 @@ func TestPublicKeyWithBadCertificate(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
//pub_key
_, err = keys_and_cert.GetPublicKey()
pub_key := keys_and_cert.PublicKey()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
//assert.Nil(pub_key)
assert.Nil(pub_key)
}
func TestPublicKeyWithNullCertificate(t *testing.T) {
@ -94,7 +79,7 @@ func TestPublicKeyWithNullCertificate(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
pub_key, err := keys_and_cert.GetPublicKey()
pub_key := keys_and_cert.PublicKey()
assert.Nil(err)
assert.Equal(len(pub_key_data), pub_key.Len())
}
@ -108,9 +93,8 @@ func TestPublicKeyWithKeyCertificate(t *testing.T) {
data = append(data, pub_key_data...)
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
assert.Nil(err)
pub_key, err := keys_and_cert.GetPublicKey()
pub_key := keys_and_cert.PublicKey()
assert.Nil(err)
assert.Equal(len(pub_key_data), pub_key.Len())
}
@ -125,7 +109,7 @@ func TestSigningPublicKeyWithBadData(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
signing_pub_key := keys_and_cert.SigningPublicKey()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
@ -142,7 +126,7 @@ func TestSigningPublicKeyWithBadCertificate(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
signing_pub_key := keys_and_cert.SigningPublicKey()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
@ -159,7 +143,7 @@ func TestSigningPublicKeyWithNullCertificate(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
signing_pub_key := keys_and_cert.SigningPublicKey()
assert.Nil(err)
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
}
@ -174,128 +158,74 @@ func TestSigningPublicKeyWithKeyCertificate(t *testing.T) {
data = append(data, cert_data...)
keys_and_cert, _, err := ReadKeysAndCert(data)
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
signing_pub_key := keys_and_cert.SigningPublicKey()
assert.Nil(err)
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
}
func TestReadKeysAndCertWithMissingData(t *testing.T) {
func TestNewKeysAndCertWithMissingData(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
assert.Equal(0, len(remainder))
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
_, err = keys_and_cert.GetPublicKey()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
_, err = keys_and_cert.GetSigningPublicKey()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
_, err = keys_and_cert.GetCertificate()
if assert.NotNil(err) {
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
}
}
func TestReadKeysAndCertWithMissingCertData(t *testing.T) {
func TestNewKeysAndCertWithMissingCertData(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128+256)
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01}...)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
assert.Equal(0, len(remainder))
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
_, err = keys_and_cert.GetPublicKey()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
_, err = keys_and_cert.GetSigningPublicKey()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
_, err = keys_and_cert.GetCertificate()
if assert.NotNil(err) {
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
}
}
func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) {
func TestNewKeysAndCertWithValidDataWithCertificate(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128+256)
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
assert.Equal(0, len(remainder))
assert.Nil(err)
_, err = keys_and_cert.GetPublicKey()
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
_, err = keys_and_cert.GetSigningPublicKey()
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
_, err = keys_and_cert.GetCertificate()
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
}
func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
func TestNewKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128+256)
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00}...)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
assert.Equal(0, len(remainder))
assert.Nil(err)
_, err = keys_and_cert.GetPublicKey()
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
_, err = keys_and_cert.GetSigningPublicKey()
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
_, err = keys_and_cert.GetCertificate()
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
}
func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
func TestNewKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128+256)
cert_data = append(cert_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x41}...)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
if assert.Equal(1, len(remainder)) {
assert.Equal("A", string(remainder[0]))
}
assert.Nil(err)
_, err = keys_and_cert.GetPublicKey()
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
_, err = keys_and_cert.GetSigningPublicKey()
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
_, err = keys_and_cert.GetCertificate()
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
}
func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
func TestNewKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
assert := assert.New(t)
cert_data := make([]byte, 128+256)
cert_data = append(cert_data, []byte{0x00, 0x00, 0x00, 0x41}...)
keys_and_cert, remainder, err := ReadKeysAndCert(cert_data)
_, remainder, err := ReadKeysAndCert(cert_data)
if assert.Equal(1, len(remainder)) {
assert.Equal("A", string(remainder[0]))
}
assert.Nil(err)
_, err = keys_and_cert.GetPublicKey()
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
_, err = keys_and_cert.GetSigningPublicKey()
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
_, err = keys_and_cert.GetCertificate()
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
}

View File

@ -0,0 +1,18 @@
package keys_and_cert
import "crypto"
// PrivateKeysAndCert contains a KeysAndCert along with the corresponding private keys for the
// Public Key and the Signing Public Key
type PrivateKeysAndCert struct {
KeysAndCert
PK_KEY crypto.PrivateKey
SPK_KEY crypto.PrivateKey
}
func NewPrivateKeysAndCert() (*PrivateKeysAndCert, error) {
var pkc PrivateKeysAndCert
var err error
// pkc.PK_KEY, err =
return &pkc, err
}

View File

@ -1,108 +0,0 @@
package common
import (
"errors"
log "github.com/sirupsen/logrus"
)
/*
I2P Lease
https://geti2p.net/spec/common-structures#lease
Accurate for version 0.9.24
+----+----+----+----+----+----+----+----+
| tunnel_gw |
+ +
| |
+ +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| tunnel_id | end_date
+----+----+----+----+----+----+----+----+
|
+----+----+----+----+
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
length -> 32 bytes
tunnel_id :: TunnelId
length -> 4 bytes
end_date :: Date
length -> 8 bytes
*/
// Sizes or various components of a Lease
const (
LEASE_SIZE = 44
LEASE_HASH_SIZE = 32
LEASE_TUNNEL_ID_SIZE = 4
LEASE_TUNNEL_DATE_SIZE = 8
)
type LeaseInterface interface {
TunnelGateway() (hash Hash)
TunnelID() uint32
Date() (date Date)
}
type Lease struct {
LeaseHash Hash
TunnelIdent *Integer
TunnelDate Date
} //[LEASE_SIZE]byte
var li LeaseInterface = &Lease{}
//
// Return the first 32 bytes of the Lease as a Hash.
//
func (lease Lease) TunnelGateway() (hash Hash) {
copy(hash[:], lease.LeaseHash[:])
return
}
//
// Return the TunnelID Integer in the Lease.
//
func (lease Lease) TunnelID() uint32 {
return uint32(lease.TunnelIdent.Value())
}
//
// Return the Date inside the Lease.
//
func (lease Lease) Date() (date Date) {
copy(date[:], lease.TunnelDate[:])
return
}
//
// Possibly temporary? Just to make it compile for now
//
func (lease Lease) Bytes() (bytes []byte) {
var r []byte
r = append(r, lease.LeaseHash[:]...)
r = append(r, lease.TunnelIdent.Bytes()...)
r = append(r, lease.TunnelDate[:]...)
return r
}
func ReadLease(data []byte) (lease Lease, remainder []byte, err error) {
if len(data) < LEASE_SIZE {
log.WithFields(log.Fields{
"at": "(Lease) ReadLease",
"data_len": len(data),
"required_len": "44",
"reason": "lease missing data",
}).Error("error parsnig lease")
err = errors.New("error parsing lease: lease missing data")
}
lease.LeaseHash, remainder, err = ReadHash(data)
identbytes, remainder, err := ReadIdent(remainder)
lease.TunnelIdent, err = NewInteger(identbytes[:])
lease.TunnelDate, remainder, err = ReadDate(remainder)
return
}

63
lib/common/lease/doc.md Normal file
View File

@ -0,0 +1,63 @@
# lease
--
import "github.com/go-i2p/go-i2p/lib/common/lease"
Package lease implements the I2P lease common data structure
## Usage
```go
const (
LEASE_SIZE = 44
LEASE_HASH_SIZE = 32
LEASE_TUNNEL_ID_SIZE = 4
)
```
Sizes in bytes of various components of a Lease
#### type Lease
```go
type Lease [LEASE_SIZE]byte
```
Lease is the represenation of an I2P Lease.
https://geti2p.net/spec/common-structures#lease
#### func NewLease
```go
func NewLease(data []byte) (lease *Lease, remainder []byte, err error)
```
NewLease creates a new *NewLease from []byte using ReadLease. Returns a pointer
to KeysAndCert unlike ReadLease.
#### func ReadLease
```go
func ReadLease(data []byte) (lease Lease, remainder []byte, err error)
```
ReadLease returns Lease from a []byte. The remaining bytes after the specified
length are also returned. Returns a list of errors that occurred during parsing.
#### func (Lease) Date
```go
func (lease Lease) Date() (date Date)
```
Date returns the date as an I2P Date.
#### func (Lease) TunnelGateway
```go
func (lease Lease) TunnelGateway() (hash Hash)
```
TunnelGateway returns the tunnel gateway as a Hash.
#### func (Lease) TunnelID
```go
func (lease Lease) TunnelID() uint32
```
TunnelID returns the tunnel id as a uint23.

85
lib/common/lease/lease.go Normal file
View File

@ -0,0 +1,85 @@
// Package lease implements the I2P lease common data structure
package lease
import . "github.com/go-i2p/go-i2p/lib/common/data"
// Sizes in bytes of various components of a Lease
const (
LEASE_SIZE = 44
LEASE_HASH_SIZE = 32
LEASE_TUNNEL_ID_SIZE = 4
)
/*
[Lease]
Accurate for version 0.9.49
Description
Defines the authorization for a particular tunnel to receive messages targeting a Destination.
Contents
SHA256 Hash of the RouterIdentity of the gateway router, then the TunnelId and finally an end Date.
+----+----+----+----+----+----+----+----+
| tunnel_gw |
+ +
| |
+ +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| tunnel_id | end_date
+----+----+----+----+----+----+----+----+
|
+----+----+----+----+
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
length -> 32 bytes
tunnel_id :: TunnelId
length -> 4 bytes
end_date :: Date
length -> 8 bytes
*/
// Lease is the represenation of an I2P Lease.
//
// https://geti2p.net/spec/common-structures#lease
type Lease [LEASE_SIZE]byte
// TunnelGateway returns the tunnel gateway as a Hash.
func (lease Lease) TunnelGateway() (hash Hash) {
copy(hash[:], lease[:LEASE_HASH_SIZE])
return
}
// TunnelID returns the tunnel id as a uint23.
func (lease Lease) TunnelID() uint32 {
i := Integer(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE])
return uint32(
i.Int(),
)
}
// Date returns the date as an I2P Date.
func (lease Lease) Date() (date Date) {
copy(date[:], lease[LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE:])
return
}
// ReadLease returns Lease from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadLease(data []byte) (lease Lease, remainder []byte, err error) {
// TODO: stub
return
}
// NewLease creates a new *NewLease from []byte using ReadLease.
// Returns a pointer to KeysAndCert unlike ReadLease.
func NewLease(data []byte) (lease *Lease, remainder []byte, err error) {
// TODO: stub
return
}

View File

@ -1,40 +0,0 @@
package common
/*
Lease2
https://geti2p.net/spec/common-structures#lease2
Description
Defines the authorization for a particular tunnel to receive messages targeting a Destination. Same as Lease but with a 4-byte end_date. Used by LeaseSet2. Supported as of 0.9.38; see proposal 123 for more information.
Contents
SHA256 Hash of the RouterIdentity of the gateway router, then the TunnelId, and finally a 4 byte end date.
+----+----+----+----+----+----+----+----+
| tunnel_gw |
+ +
| |
+ +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
| tunnel_id | end_date |
+----+----+----+----+----+----+----+----+
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
length -> 32 bytes
tunnel_id :: TunnelId
length -> 4 bytes
end_date :: 4 byte date
length -> 4 bytes
Seconds since the epoch, rolls over in 2106.
Notes
Total size: 40 bytes
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/Lease2.html
*/

View File

@ -1,336 +0,0 @@
package common
/*
I2P LeaseSet
https://geti2p.net/spec/common-structures#leaseset
Accurate for version 0.9.24
+----+----+----+----+----+----+----+----+
| destination |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| encryption_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signing_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
|num | Lease 0 |
+----+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| Lease 1 |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| Lease ($num-1) |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signature |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
destination :: Destination
length -> >= 387 bytes
encryption_key :: PublicKey
length -> 256 bytes
signing_key :: SigningPublicKey
length -> 128 bytes or as specified in destination's key certificate
num :: Integer
length -> 1 byte
Number of leases to follow
value: 0 <= num <= 16
leases :: [Lease]
length -> $num*44 bytes
signature :: Signature
length -> 40 bytes or as specified in destination's key certificate
*/
import (
"errors"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
// Sizes of various structures in an I2P LeaseSet
const (
LEASE_SET_PUBKEY_SIZE = 256
LEASE_SET_SPK_SIZE = 128
LEASE_SET_SIG_SIZE = 40
)
type LeaseSetInterface interface {
GetPublicKey() (public_key crypto.ElgPublicKey, err error)
GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error)
Leases() (leases []Lease, err error)
/* LeaseCount() (count int, err error)*/
GetSignature() (signature Signature, err error)
/* Verify() error
NewestExpiration() (oldest Date, err error)
OldestExpiration() (earliest Date, err error)*/
}
type LeaseSet struct {
Destination
crypto.SigningPublicKey
crypto.ElgPublicKey
LeaseList []Lease
}
var lsi LeaseSetInterface = &LeaseSet{}
//
// Read a Destination from the LeaseSet.
//
func (lease_set LeaseSet) GetDestination() (destination Destination, err error) {
if &lease_set.Destination != nil {
destination = lease_set.Destination
} else {
err = errors.New("Error leaseset does not contain a destination")
}
return
}
//
// Return the PublicKey in this LeaseSet and any errors ancountered parsing the LeaseSet.
//
func (lease_set LeaseSet) GetPublicKey() (public_key crypto.ElgPublicKey, err error) {
public_key = lease_set.ElgPublicKey
return
}
//
// Return the SigningPublicKey, as specified in the LeaseSet's Destination's Key Certificate if
// present, or a legacy DSA key.
//
func (lease_set LeaseSet) GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
if lease_set.SigningPublicKey == nil {
log.WithFields(log.Fields{
"at": "(LeaseSet) SigningKey",
"public": lease_set.SigningPublicKey,
"reason": "not enough data",
}).Error("error parsing signing public key")
err = errors.New("error parsing signing public key: not enough data")
return
}
signing_public_key = lease_set.SigningPublicKey
return
}
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
leases = lease_set.LeaseList
return
}
//
// Return the number of Leases specified by the LeaseCount value in this LeaseSet.
//
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
count = len(lease_set.LeaseList)
return
}
//
// Return the Signature data for the LeaseSet, as specified in the Destination's
// Key Certificate if present or the 40 bytes following the Leases.
//
func (lease_set LeaseSet) GetSignature() (signature Signature, err error) {
return
}
//
//
//
/*
func (lease_set LeaseSet) Verify() error {
//data_end := len(destination) +
// LEASE_SET_PUBKEY_SIZE +
// LEASE_SET_SPK_SIZE +
// 1 +
// (44 * lease_set.LeaseCount())
//data := lease_set[:data_end]
//spk, _ := lease_set.
// Destination().
// SigningPublicKey()
//verifier, err := spk.NewVerifier()
//if err != nil {
// return err
//}
return nil // verifier.Verify(data, lease_set.Signature())
}
*/
//
// Return the oldest date from all the Leases in the LeaseSet.
//
func (lease_set LeaseSet) NewestExpiration() (oldest Date, err error) {
leases, err := lease_set.Leases()
if err != nil {
return
}
oldest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
for _, lease := range leases {
date := lease.Date()
if date.Time().After(oldest.Time()) {
oldest = date
}
}
return
}
//
// Return the oldest date from all the Leases in the LeaseSet.
//
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
leases, err := lease_set.Leases()
if err != nil {
return
}
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
for _, lease := range leases {
date := lease.Date()
if date.Time().Before(earliest.Time()) {
earliest = date
}
}
return
}
func ReadLeaseSetSignature(bytes []byte, cert CertificateInterface) (signature Signature, remainder []byte, err error) {
start := 0
cert_type, _, _ := cert.Type()
var end int
if cert_type == CERT_KEY {
end = start + cert.SignatureSize()
} else {
end = start + LEASE_SET_SIG_SIZE
}
bytes_len := len(bytes)
if bytes_len < end {
log.WithFields(log.Fields{
"at": "(LeaseSet) Signature",
"data_len": bytes_len,
"required_len": end,
"reason": "not enough data",
}).Error("error parsing signatre")
err = errors.New("error parsing signature: not enough data")
signature = []byte(bytes[start:bytes_len])
return
}
signature = []byte(bytes[start:end])
return
}
func ReadLeaseCount(bytes []byte) (count *Integer, err error) {
remainder_len := len(bytes)
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
log.WithFields(log.Fields{
"at": "(LeaseSet) LeaseCount",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
"reason": "not enough data",
}).Error("error parsing lease count")
err = errors.New("error parsing lease count: not enough data")
return
}
count, err = NewInteger([]byte{bytes[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
if count.Value() > 16 {
log.WithFields(log.Fields{
"at": "(LeaseSet) LeaseCount",
"lease_count": count,
"reason": "more than 16 leases",
}).Warn("invalid lease set")
err = errors.New("invalid lease set: more than 16 leases")
}
return
}
//
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
//
func ReadLeases(bytes []byte) (leases []Lease, remainder []byte, err error) {
count, err := ReadLeaseCount(bytes)
if err != nil {
return
}
for i := 0; i < count.Value(); i++ {
start := 0 //offset + (i * LEASE_SIZE)
end := start + LEASE_SIZE
lease_set_len := len(bytes)
if lease_set_len < end {
log.WithFields(log.Fields{
"at": "(LeaseSet) Leases",
"data_len": lease_set_len,
"required_len": end,
"reason": "some leases missing",
}).Error("error parsnig lease set")
err = errors.New("error parsing lease set: some leases missing")
return
}
var lease Lease
lease, remainder, err = ReadLease(bytes[start:end])
leases = append(leases, lease)
if err != nil {
return
}
}
return
}
func ReadLeaseSetKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.ElgPublicKey, remainder []byte, err error) {
spk, ppk, remainder, err := ReadKeys(data, cert)
switch ppk.(type) {
case crypto.ElgPublicKey:
pk = ppk.(crypto.ElgPublicKey)
default:
err = errors.New("LeaseSet1 uses Elgamal public keys.")
}
return
}
func ReadLeaseSet(data []byte) (lease_set LeaseSet, remainder []byte, err error) {
destination, remainder, err := ReadDestination(data)
lease_set.Destination = destination
//offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
spk, pk, remainder, err := ReadLeaseSetKeys(remainder, nil)
lease_set.SigningPublicKey = spk
lease_set.ElgPublicKey = pk
leases, remainder, err := ReadLeases(data)
lease_set.LeaseList = leases
return
}

View File

@ -0,0 +1,95 @@
# lease_set
--
import "github.com/go-i2p/go-i2p/lib/common/lease_set"
Package lease_set implements the I2P LeastSet common data structure
## Usage
```go
const (
LEASE_SET_PUBKEY_SIZE = 256
LEASE_SET_SPK_SIZE = 128
LEASE_SET_SIG_SIZE = 40
)
```
Sizes of various structures in an I2P LeaseSet
#### type LeaseSet
```go
type LeaseSet []byte
```
LeaseSet is the represenation of an I2P LeaseSet.
https://geti2p.net/spec/common-structures#leaseset
#### func (LeaseSet) Destination
```go
func (lease_set LeaseSet) Destination() (destination Destination, err error)
```
Destination returns the Destination as []byte.
#### func (LeaseSet) LeaseCount
```go
func (lease_set LeaseSet) LeaseCount() (count int, err error)
```
LeaseCount returns the numbert of leases specified by the LeaseCount value as
int. returns errors encountered during parsing.
#### func (LeaseSet) Leases
```go
func (lease_set LeaseSet) Leases() (leases []Lease, err error)
```
Leases returns the leases as []Lease. returns errors encountered during parsing.
#### func (LeaseSet) NewestExpiration
```go
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error)
```
NewestExpiration returns the newest lease expiration as an I2P Date. Returns
errors encountered during parsing.
#### func (LeaseSet) OldestExpiration
```go
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error)
```
OldestExpiration returns the oldest lease expiration as an I2P Date. Returns
errors encountered during parsing.
#### func (LeaseSet) PublicKey
```go
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error)
```
PublicKey returns the public key as crypto.ElgPublicKey. Returns errors
encountered during parsing.
#### func (LeaseSet) Signature
```go
func (lease_set LeaseSet) Signature() (signature Signature, err error)
```
Signature returns the signature as Signature. returns errors encountered during
parsing.
#### func (LeaseSet) SigningKey
```go
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error)
```
SigningKey returns the signing public key as crypto.SigningPublicKey. returns
errors encountered during parsing.
#### func (LeaseSet) Verify
```go
func (lease_set LeaseSet) Verify() error
```
Verify returns nil

View File

@ -0,0 +1,408 @@
// Package lease_set implements the I2P LeastSet common data structure
package lease_set
import (
"errors"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/certificate"
. "github.com/go-i2p/go-i2p/lib/common/data"
. "github.com/go-i2p/go-i2p/lib/common/destination"
. "github.com/go-i2p/go-i2p/lib/common/key_certificate"
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
. "github.com/go-i2p/go-i2p/lib/common/lease"
. "github.com/go-i2p/go-i2p/lib/common/signature"
"github.com/go-i2p/go-i2p/lib/crypto"
)
var log = logger.GetGoI2PLogger()
// Sizes of various structures in an I2P LeaseSet
const (
LEASE_SET_PUBKEY_SIZE = 256
LEASE_SET_SPK_SIZE = 128
LEASE_SET_SIG_SIZE = 40
)
/*
[LeaseSet]
Accurate for version 0.9.49
Description
Contains all of the currently authorized Leases for a particular Destination, the
publicKey to which garlic messages can be encrypted, and then the signingPublicKey
that can be used to revoke this particular version of the structure. The LeaseSet is one
of the two structures stored in the network database (the other being RouterInfo), and
is kered under the SHA256 of the contained Destination.
Contents
Destination, followed by a publicKey for encryption, then a signingPublicKey which
can be used to revoke this version of the LeaseSet, then a 1 byte Integer specifying how
many Lease structures are in the set, followed by the actual Lease structures and
finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey.
+----+----+----+----+----+----+----+----+
| destination |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| encryption_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signing_key |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
|num | Lease 0 |
+----+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| Lease 1 |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| Lease ($num-1) |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signature |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+----+----+----+----+----+----+----+----+
destination :: Destination
length -> >= 387 bytes
encryption_key :: publicKey
length -> 256 bytes
signing_key :: signingPublicKey
length -> 128 bytes or as specified in destination's key certificate
num :: Integer
length -> 1 byte
Number of leases to follow
value: 0 <= num <= 16
leases :: [Lease]
length -> $num*44 bytes
signature :: Signature
length -> 40 bytes or as specified in destination's key certificate
*/
// LeaseSet is the represenation of an I2P LeaseSet.
//
// https://geti2p.net/spec/common-structures#leaseset
type LeaseSet []byte
/*
type LeaseSet struct {
Destination *Destination
EncryptionKey *crypto.ElgPublicKey
SigningKey *crypto.ElgPublicKey
Size *Integer
Leases []*Lease
Signature *Signature
}
*/
// Destination returns the Destination as []byte.
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
keys_and_cert, _, err := ReadKeysAndCert(lease_set)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert from LeaseSet")
return
}
destination, _, err = ReadDestination(keys_and_cert.Bytes())
if err != nil {
log.WithError(err).Error("Failed to read Destination from KeysAndCert")
} else {
log.Debug("Successfully retrieved Destination from LeaseSet")
}
return
}
// PublicKey returns the public key as crypto.ElgPublicKey.
// Returns errors encountered during parsing.
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error) {
_, remainder, err := ReadKeysAndCert(lease_set)
remainder_len := len(remainder)
if remainder_len < LEASE_SET_PUBKEY_SIZE {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) publicKey",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE,
"reason": "not enough data",
}).Error("error parsing public key")
err = errors.New("error parsing public key: not enough data")
copy(public_key[:], remainder)
return
}
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
log.Debug("Successfully retrieved publicKey from LeaseSet")
return
}
// SigningKey returns the signing public key as crypto.SigningPublicKey.
// returns errors encountered during parsing.
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
log.Debug("Retrieving SigningKey from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for SigningKey")
return
}
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE
cert := destination.Certificate()
cert_len := cert.Length()
if err != nil {
log.WithError(err).Error("Failed to get Certificate length")
return
}
lease_set_len := len(lease_set)
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) SigningKey",
"data_len": lease_set_len,
"required_len": offset + LEASE_SET_SPK_SIZE,
"reason": "not enough data",
}).Error("error parsing signing public key")
err = errors.New("error parsing signing public key: not enough data")
return
}
if cert_len == 0 {
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
// signingPublicKey space as legacy DSA SHA1 signingPublicKey.
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
signing_public_key = dsa_pk
log.Debug("Retrieved legacy DSA SHA1 signingPublicKey")
} else {
// A Certificate is present in this LeaseSet's Destination
cert_type := cert.Type()
if cert_type == CERT_KEY {
// This LeaseSet's Destination's Certificate is a Key Certificate,
// create the signing publickey key using any data that might be
// contained in the key certificate.
signing_public_key, err = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey(
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
)
if err != nil {
log.WithError(err).Error("Failed to construct signingPublicKey from keyCertificate")
} else {
log.Debug("Retrieved signingPublicKey from keyCertificate")
}
} else {
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
// signingPublicKey space as legacy DSA SHA1 signingPublicKey.
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
signing_public_key = dsa_pk
log.Debug("Retrieved legacy DSA SHA1 signingPublicKey (Certificate present but not Key Certificate)")
}
}
return
}
// LeaseCount returns the numbert of leases specified by the LeaseCount value as int.
// returns errors encountered during parsing.
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
log.Debug("Retrieving LeaseCount from LeaseSet")
_, remainder, err := ReadKeysAndCert(lease_set)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert for LeaseCount")
return
}
remainder_len := len(remainder)
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) LeaseCount",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
"reason": "not enough data",
}).Error("error parsing lease count")
err = errors.New("error parsing lease count: not enough data")
return
}
c := Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
count = c.Int()
if count > 16 {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) LeaseCount",
"lease_count": count,
"reason": "more than 16 leases",
}).Warn("invalid lease set")
err = errors.New("invalid lease set: more than 16 leases")
} else {
log.WithField("lease_count", count).Debug("Retrieved LeaseCount from LeaseSet")
}
return
}
// Leases returns the leases as []Lease.
// returns errors encountered during parsing.
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
log.Debug("Retrieving Leases from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for Leases")
return
}
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
count, err := lease_set.LeaseCount()
if err != nil {
log.WithError(err).Error("Failed to retrieve LeaseCount for Leases")
return
}
for i := 0; i < count; i++ {
start := offset + (i * LEASE_SIZE)
end := start + LEASE_SIZE
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Leases",
"data_len": lease_set_len,
"required_len": end,
"reason": "some leases missing",
}).Error("error parsnig lease set")
err = errors.New("error parsing lease set: some leases missing")
return
}
var lease Lease
copy(lease[:], lease_set[start:end])
leases = append(leases, lease)
}
log.WithField("lease_count", len(leases)).Debug("Retrieved Leases from LeaseSet")
return
}
// Signature returns the signature as Signature.
// returns errors encountered during parsing.
func (lease_set LeaseSet) Signature() (signature Signature, err error) {
log.Debug("Retrieving Signature from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for Signature")
return
}
lease_count, err := lease_set.LeaseCount()
if err != nil {
log.WithError(err).Error("Failed to retrieve LeaseCount for Signature")
return
}
start := len(destination.Bytes()) +
LEASE_SET_PUBKEY_SIZE +
LEASE_SET_SPK_SIZE +
1 +
(LEASE_SIZE * lease_count)
cert := destination.Certificate()
cert_type := cert.Type()
var end int
if cert_type == CERT_KEY {
end = start + KeyCertificateFromCertificate(cert).SignatureSize()
} else {
end = start + LEASE_SET_SIG_SIZE
}
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Signature",
"data_len": lease_set_len,
"required_len": end,
"reason": "not enough data",
}).Error("error parsing signatre")
err = errors.New("error parsing signature: not enough data")
return
}
signature = []byte(lease_set[start:end])
log.WithField("signature_length", len(signature)).Debug("Retrieved Signature from LeaseSet")
return
}
// Verify returns nil
func (lease_set LeaseSet) Verify() error {
log.Debug("Verifying LeaseSet")
//data_end := len(destination) +
// LEASE_SET_PUBKEY_SIZE +
// LEASE_SET_SPK_SIZE +
// 1 +
// (44 * lease_set.LeaseCount())
//data := lease_set[:data_end]
//spk, _ := lease_set.
// Destination().
// signingPublicKey()
//verifier, err := spk.NewVerifier()
//if err != nil {
// return err
//}
log.Warn("LeaseSet verification not implemented")
return nil // verifier.Verify(data, lease_set.Signature())
}
// NewestExpiration returns the newest lease expiration as an I2P Date.
// Returns errors encountered during parsing.
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
log.Debug("Finding newest expiration in LeaseSet")
leases, err := lease_set.Leases()
if err != nil {
log.WithError(err).Error("Failed to retrieve Leases for NewestExpiration")
return
}
newest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
for _, lease := range leases {
date := lease.Date()
if date.Time().After(newest.Time()) {
newest = date
}
}
log.WithField("newest_expiration", newest.Time()).Debug("Found newest expiration in LeaseSet")
return
}
// OldestExpiration returns the oldest lease expiration as an I2P Date.
// Returns errors encountered during parsing.
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
log.Debug("Finding oldest expiration in LeaseSet")
leases, err := lease_set.Leases()
if err != nil {
log.WithError(err).Error("Failed to retrieve Leases for OldestExpiration")
return
}
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
for _, lease := range leases {
date := lease.Date()
if date.Time().Before(earliest.Time()) {
earliest = date
}
}
log.WithField("oldest_expiration", earliest.Time()).Debug("Found oldest expiration in LeaseSet")
return
}

View File

@ -1,16 +1,22 @@
package common
package lease_set
import (
"bytes"
"github.com/stretchr/testify/assert"
"testing"
"github.com/go-i2p/go-i2p/lib/common/certificate"
common "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/lease"
"github.com/go-i2p/go-i2p/lib/common/router_identity"
"github.com/stretchr/testify/assert"
)
func buildDestination() RouterIdentity {
func buildDestination() *router_identity.RouterIdentity {
router_ident_data := make([]byte, 128+256)
router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
rri, _, _ := ReadRouterIdentity(router_ident_data)
return rri
ident, _, err := router_identity.ReadRouterIdentity(router_ident_data)
panic(err)
return &ident
}
func buildPublicKey() []byte {
@ -32,15 +38,15 @@ func buildSigningKey() []byte {
func buildLease(n int) []byte {
data := make([]byte, 0)
for i := 0; i < n; i++ {
lease := make([]byte, LEASE_SIZE)
for p := range lease {
lease[p] = byte(i)
l := make([]byte, lease.LEASE_SIZE)
for p := range l {
l[p] = byte(i)
}
for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ {
lease[q] = 0x00
for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ {
l[q] = 0x00
}
lease[LEASE_SIZE-1] = byte(i + 10)
data = append(data, lease...)
l[lease.LEASE_SIZE-1] = byte(i + 10)
data = append(data, l...)
}
return data
}
@ -55,34 +61,33 @@ func buildSignature(size int) []byte {
func buildFullLeaseSet(n int) LeaseSet {
lease_set_data := make([]byte, 0)
lease_set_data = append(lease_set_data, buildDestination().Bytes()...)
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))
lease_set_data = append(lease_set_data, buildLease(n)...)
lease_set_data = append(lease_set_data, buildSignature(64)...)
leaseSet, _, _ := ReadLeaseSet(lease_set_data)
return leaseSet
return LeaseSet(lease_set_data)
}
func TestDestinationIsCorrect(t *testing.T) {
assert := assert.New(t)
lease_set := buildFullLeaseSet(1)
dest, err := lease_set.GetDestination()
dest, err := lease_set.Destination()
assert.Nil(err)
dest_cert, err := dest.Certificate()
dest_cert := dest.Certificate()
// assert.Nil(err)
cert_type := dest_cert.Type()
assert.Nil(err)
_, cert_bytes, err := dest_cert.Type()
assert.Nil(err)
assert.Equal(CERT_KEY, cert_bytes)
assert.Equal(certificate.CERT_KEY, cert_type)
}
func TestPublicKeyIsCorrect(t *testing.T) {
assert := assert.New(t)
lease_set := buildFullLeaseSet(1)
pk, err := lease_set.GetPublicKey()
pk, err := lease_set.PublicKey()
if assert.Nil(err) {
assert.Equal(
0,
@ -98,7 +103,7 @@ func TestSigningKeyIsCorrect(t *testing.T) {
assert := assert.New(t)
lease_set := buildFullLeaseSet(1)
sk, err := lease_set.GetSigningKey()
sk, err := lease_set.SigningKey()
if assert.Nil(err) {
assert.Equal(128, sk.Len())
}
@ -144,19 +149,19 @@ func TestLeasesHaveCorrectData(t *testing.T) {
leases, err := lease_set.Leases()
if assert.Nil(err) {
for i := 0; i < count; i++ {
lease := make([]byte, LEASE_SIZE)
for p := range lease {
lease[p] = byte(i)
l := make([]byte, lease.LEASE_SIZE)
for p := range l {
l[p] = byte(i)
}
for q := LEASE_SIZE - 9; q < LEASE_SIZE-1; q++ {
lease[q] = 0x00
for q := lease.LEASE_SIZE - 9; q < lease.LEASE_SIZE-1; q++ {
l[q] = 0x00
}
lease[LEASE_SIZE-1] = byte(i + 10)
l[lease.LEASE_SIZE-1] = byte(i + 10)
assert.Equal(
0,
bytes.Compare(
lease,
leases[i].Bytes()[:],
l,
leases[i][:],
),
)
}
@ -168,7 +173,7 @@ func TestSignatureIsCorrect(t *testing.T) {
assert := assert.New(t)
lease_set := buildFullLeaseSet(1)
sig, err := lease_set.GetSignature()
sig, err := lease_set.Signature()
if assert.Nil(err) {
assert.Equal(
0,
@ -186,8 +191,9 @@ func TestNewestExpirationIsCorrect(t *testing.T) {
lease_set := buildFullLeaseSet(5)
latest, err := lease_set.NewestExpiration()
assert.Nil(err)
Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)})
assert.Equal(
Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(4 + 10)},
Date,
latest,
)
}
@ -198,8 +204,9 @@ func TestOldestExpirationIsCorrect(t *testing.T) {
lease_set := buildFullLeaseSet(5)
latest, err := lease_set.OldestExpiration()
assert.Nil(err)
Date, _, err := common.NewDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a})
assert.Equal(
Date{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a},
Date,
latest,
)
}

View File

@ -1,105 +0,0 @@
package common
/*
https://geti2p.net/spec/common-structures#leaseset2
LeaseSet2
Description
Contained in a I2NP DatabaseStore message of type 3. Supported as of 0.9.38; see proposal 123 for more information.
Contains all of the currently authorized Lease2 for a particular Destination, and the PublicKey to which garlic messages can be encrypted. A LeaseSet is one of the two structures stored in the network database (the other being RouterInfo), and is keyed under the SHA256 of the contained Destination.
Contents
LeaseSet2Header, followed by a options, then one or more PublicKey for encryption, Integer specifying how many Lease2 structures are in the set, followed by the actual Lease2 structures and finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey or the transient key.
+----+----+----+----+----+----+----+----+
| ls2_header |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| options |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
|numk| keytype0| keylen0 | |
+----+----+----+----+----+ +
| encryption_key_0 |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| keytypen| keylenn | |
+----+----+----+----+ +
| encryption_key_n |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| num| Lease2 0 |
+----+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| Lease2($num-1) |
+ +
| |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
| signature |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
ls2header :: LeaseSet2Header
length -> varies
options :: Mapping
length -> varies, 2 bytes minimum
numk :: Integer
length -> 1 byte
Number of key types, key lengths, and PublicKeys to follow
value: 1 <= numk <= max TBD
keytype :: The encryption type of the PublicKey to follow.
length -> 2 bytes
keylen :: The length of the PublicKey to follow.
Must match the specified length of the encryption type.
length -> 2 bytes
encryption_key :: PublicKey
length -> 256 bytes
num :: Integer
length -> 1 byte
Number of Lease2s to follow
value: 0 <= num <= 16
leases :: [Lease2]
length -> $num*40 bytes
signature :: Signature
length -> 40 bytes or as specified in destination's key
certificate, or by the sigtype of the transient public key,
if present in the header
Notes
The public key of the destination was used for the old I2CP-to-I2CP encryption which was disabled in version 0.6, it is currently unused.
The encryption keys are used for end-to-end ElGamal/AES+SessionTag encryption [ELGAMAL-AES] (type 0) or other end-to-end encryption schemes. See [ECIES] and proposals 145 and 156. They may be generated anew at every router startup or they may be persistent. X25519 (type 4, see [ECIES]) is supported as of release 0.9.44.
The signature is over the data above, PREPENDED with the single byte containing the DatabaseStore type (3).
The signature may be verified using the signing public key of the destination, or the transient signing public key, if an offline signature is included in the leaseset2 header.
The key length is provided for each key, so that floodfills and clients may parse the structure even if not all encryption types are known or supported.
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/LeaseSet2.html
*/

View File

@ -1,228 +0,0 @@
package common
/*
I2P Mapping
https://geti2p.net/spec/common-structures#mapping
Accurate for version 0.9.24
+----+----+----+----+----+----+----+----+
| size |key_string (len + data) | = |
+----+----+----+----+----+----+----+----+
| val_string (len + data) | ; | ...
+----+----+----+----+----+----+----+
size :: Integer
length -> 2 bytes
Total number of bytes that follow
key_string :: String
A string (one byte length followed by UTF-8 encoded characters)
= :: A single byte containing '='
val_string :: String
A string (one byte length followed by UTF-8 encoded characters)
; :: A single byte containing ';'
*/
import (
"encoding/binary"
"errors"
log "github.com/sirupsen/logrus"
"sort"
)
type Mapping []byte
// Parsed key-values pairs inside a Mapping.
type MappingValues [][2]String
//
// Returns the values contained in a Mapping in the form of a MappingValues.
//
func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
var str String
var remainder = mapping
var err error
length, err := NewInteger(remainder[:2])
inferred_length := length.Value() + 2
remainder = remainder[2:]
mapping_len := len(mapping)
if mapping_len > inferred_length {
log.WithFields(log.Fields{
"at": "(Mapping) Values",
"mappnig_bytes_length": mapping_len,
"mapping_length_field": length,
"expected_bytes_length": inferred_length,
"reason": "data longer than expected",
}).Warn("mapping format warning")
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
} else if inferred_length > mapping_len {
log.WithFields(log.Fields{
"at": "(Mapping) Values",
"mappnig_bytes_length": mapping_len,
"mapping_length_field": length,
"expected_bytes_length": inferred_length,
"reason": "data shorter than expected",
}).Warn("mapping format warning")
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
}
for {
// Read a key, breaking on fatal errors
// and appending warnings
str, remainder, err = ReadString(remainder)
key_str := str
if err != nil {
if stopValueRead(err) {
errs = append(errs, err)
return
}
}
if !beginsWith(remainder, 0x3d) {
log.WithFields(log.Fields{
"at": "(Mapping) Values",
"reason": "expected =",
}).Warn("mapping format violation")
errs = append(errs, errors.New("mapping format violation, expected ="))
return
}
remainder = remainder[1:]
// Read a value, breaking on fatal errors
// and appending warnings
str, remainder, err = ReadString(remainder)
val_str := str
if err != nil {
if stopValueRead(err) {
errs = append(errs, err)
return
}
}
if !beginsWith(remainder, 0x3b) {
log.WithFields(log.Fields{
"at": "(Mapping) Values",
"reason": "expected ;",
}).Warn("mapping format violation")
errs = append(errs, errors.New("mapping format violation, expected ;"))
return
}
remainder = remainder[1:]
// Append the key-value pair and break if there is no more data to read
map_values = append(map_values, [2]String{key_str, val_str})
if len(remainder) == 0 {
break
}
}
return
}
//
// Return true if two keys in a mapping are identical.
//
func (mapping Mapping) HasDuplicateKeys() bool {
seen_values := make(map[string]bool)
values, _ := mapping.Values()
for _, pair := range values {
key, _ := pair[0].Data()
if _, present := seen_values[key]; present {
return true
} else {
seen_values[key] = true
}
}
return false
}
//
// Convert a MappingValue struct to a Mapping. The values are first
// sorted in the order defined in mappingOrder.
//
func ValuesToMapping(values MappingValues) (mapping Mapping) {
mappingOrder(values)
for _, kv_pair := range values {
key_string := kv_pair[0]
key_string = append(key_string, []byte("=")[0])
key_value := kv_pair[1]
key_value = append(key_value, []byte(";")[0])
mapping = append(append(mapping, key_string...), key_value...)
}
map_len := len(mapping)
len_bytes := make([]byte, 2)
binary.BigEndian.PutUint16(len_bytes, uint16(map_len))
mapping = append(len_bytes, mapping...)
return
}
//
// Convert a Go map of unformatted strings to a sorted Mapping.
//
func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
map_vals := MappingValues{}
for k, v := range gomap {
key_str, kerr := ToI2PString(k)
if kerr != nil {
err = kerr
return
}
val_str, verr := ToI2PString(v)
if verr != nil {
err = verr
return
}
map_vals = append(
map_vals,
[2]String{key_str, val_str},
)
}
mapping = ValuesToMapping(map_vals)
return
}
type byValue MappingValues
func (set byValue) Len() int { return len(set) }
func (set byValue) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
func (set byValue) Less(i, j int) bool {
data1, _ := set[i][1].Data()
data2, _ := set[j][1].Data()
return data1 < data2
}
type byKey MappingValues
func (set byKey) Len() int { return len(set) }
func (set byKey) Swap(i, j int) { set[i], set[j] = set[j], set[i] }
func (set byKey) Less(i, j int) bool {
data1, _ := set[i][0].Data()
data2, _ := set[j][0].Data()
return data1 < data2
}
//
// I2P Mappings require consistent order for for cryptographic signing, and sorting
// by keys. When new Mappings are created, they are stable sorted first by values
// than by keys to ensure a consistent order.
//
func mappingOrder(values MappingValues) {
sort.Stable(byValue(values))
sort.Stable(byKey(values))
}
//
// Check if the string parsing error indicates that the Mapping
// should no longer be parsed.
//
func stopValueRead(err error) bool {
return err.Error() == "error parsing string: zero length"
}
//
// Determine if the first byte in a slice of bytes is the provided byte.
//
func beginsWith(bytes []byte, chr byte) bool {
return len(bytes) != 0 &&
bytes[0] == chr
}

View File

@ -1,187 +0,0 @@
package common
import (
"bytes"
"errors"
"github.com/stretchr/testify/assert"
"testing"
)
func TestValuesExclusesPairWithBadData(t *testing.T) {
assert := assert.New(t)
bad_key := Mapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
values, errs := bad_key.Values()
if assert.Equal(len(values), 1, "Values() did not return valid values when some values had bad key") {
key, _ := values[0][0].Data()
val, _ := values[0][1].Data()
assert.Equal(key, "a", "Values() returned by data with invalid key contains incorrect present key")
assert.Equal(val, "b", "Values() returned by data with invalid key contains incorrect present key")
}
assert.Equal(len(errs), 2, "Values() reported wrong error count when some values had invalid data")
}
func TestValuesWarnsMissingData(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62})
_, errs := mapping.Values()
if assert.Equal(len(errs), 2, "Values() reported wrong error count when mapping had missing data") {
assert.Equal(errs[0].Error(), "warning parsing mapping: mapping length exceeds provided data", "correct error message should be returned")
}
}
func TestValuesWarnsExtraData(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x00})
_, errs := mapping.Values()
if assert.Equal(len(errs), 2, "Values() reported wrong error count when mapping had extra data") {
assert.Equal(errs[0].Error(), "warning parsing mapping: data exists beyond length of mapping", "correct error message should be returned")
}
}
func TestValuesEnforcesEqualDelimitor(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x30, 0x01, 0x62, 0x3b})
values, errs := mapping.Values()
if assert.Equal(len(errs), 1, "Values() reported wrong error count when mapping had = format error") {
assert.Equal(errs[0].Error(), "mapping format violation, expected =", "correct error message should be returned")
}
assert.Equal(len(values), 0, "Values() not empty with invalid data due to = format error")
}
func TestValuesEnforcedSemicolonDelimitor(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x30})
values, errs := mapping.Values()
if assert.Equal(len(errs), 1, "Values() reported wrong error count when mapping had ; format error") {
assert.Equal(errs[0].Error(), "mapping format violation, expected ;", "correct error message should be returned")
}
assert.Equal(len(values), 0, "Values() not empty with invalid data due to ; format error")
}
func TestValuesReturnsValues(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
values, errs := mapping.Values()
key, kerr := values[0][0].Data()
val, verr := values[0][1].Data()
assert.Nil(errs, "Values() returned a errors with parsing valid data")
assert.Nil(kerr)
assert.Nil(verr)
assert.Equal(key, "a", "Values() did not return key in valid data")
assert.Equal(val, "b", "Values() did not return value in valid data")
}
func TestHasDuplicateKeysTrueWhenDuplicates(t *testing.T) {
assert := assert.New(t)
dups := Mapping([]byte{0x00, 0x0c, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
assert.Equal(dups.HasDuplicateKeys(), true, "HasDuplicateKeys() did not report true when duplicate keys present")
}
func TestHasDuplicateKeysFalseWithoutDuplicates(t *testing.T) {
assert := assert.New(t)
mapping := Mapping([]byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b})
assert.Equal(mapping.HasDuplicateKeys(), false, "HasDuplicateKeys() did not report false when no duplicate keys present")
}
func TestGoMapToMappingProducesCorrectMapping(t *testing.T) {
assert := assert.New(t)
gomap := map[string]string{"a": "b"}
mapping, err := GoMapToMapping(gomap)
assert.Nil(err, "GoMapToMapping() returned error with valid data")
expected := []byte{0x00, 0x06, 0x01, 0x61, 0x3d, 0x01, 0x62, 0x3b}
if bytes.Compare(mapping, expected) != 0 {
t.Fatal("GoMapToMapping did not produce correct Mapping", mapping, expected)
}
}
func TestMappingOrderSortsValuesThenKeys(t *testing.T) {
a, _ := ToI2PString("a")
b, _ := ToI2PString("b")
values := MappingValues{
[2]String{b, b},
[2]String{b, a},
[2]String{a, b},
[2]String{a, a},
}
mappingOrder(values)
for i, pair := range values {
key, _ := pair[0].Data()
value, _ := pair[1].Data()
switch i {
case 0:
if !(key == "a" && value == "a") {
t.Fatal("mappingOrder produced incorrect sort output at", i)
}
case 1:
if !(key == "a" && value == "b") {
t.Fatal("mappingOrder produced incorrect sort output at", i)
}
case 2:
if !(key == "b" && value == "a") {
t.Fatal("mappingOrder produced incorrect sort output at", i)
}
case 3:
if !(key == "b" && value == "b") {
t.Fatal("mappingOrder produced incorrect sort output at", i)
}
}
}
}
func TestStopValueReadTrueWhenCorrectErr(t *testing.T) {
assert := assert.New(t)
status := stopValueRead(errors.New("error parsing string: zero length"))
assert.Equal(status, true, "stopValueRead() did not return true when String error found")
}
func TestStopValueReadFalseWhenWrongErr(t *testing.T) {
assert := assert.New(t)
status := stopValueRead(errors.New("something else"))
assert.Equal(status, false, "stopValueRead() did not return false when non String error found")
}
func TestBeginsWithCorrectWhenTrue(t *testing.T) {
assert := assert.New(t)
slice := []byte{0x41}
assert.Equal(beginsWith(slice, 0x41), true, "beginsWith() did not return true when correct")
}
func TestBeginsWithCorrectWhenFalse(t *testing.T) {
assert := assert.New(t)
slice := []byte{0x00}
assert.Equal(beginsWith(slice, 0x41), false, "beginsWith() did not false when incorrect")
}
func TestBeginsWithCorrectWhenNil(t *testing.T) {
assert := assert.New(t)
slice := make([]byte, 0)
assert.Equal(beginsWith(slice, 0x41), false, "beginsWith() did not return false on empty slice")
}

View File

@ -1,161 +0,0 @@
package common
/*
I2P RouterAddress
https://geti2p.net/spec/common-structures#routeraddress
Accurate for version 0.9.24
+----+----+----+----+----+----+----+----+
|cost| expiration
+----+----+----+----+----+----+----+----+
| transport_style |
+----+----+----+----+-//-+----+----+----+
| |
+ +
| options |
~ ~
~ ~
| |
+----+----+----+----+----+----+----+----+
cost :: Integer
length -> 1 byte
case 0 -> free
case 255 -> expensive
expiration :: Date (must be all zeros, see notes below)
length -> 8 bytes
case null -> never expires
transport_style :: String
length -> 1-256 bytes
options :: Mapping
*/
import (
"errors"
log "github.com/sirupsen/logrus"
)
// Minimum number of bytes in a valid RouterAddress
const (
ROUTER_ADDRESS_MIN_SIZE = 9
)
type RouterAddress []byte
//
// Return the cost integer for this RouterAddress and any errors encountered
// parsing the RouterAddress.
//
func (router_address RouterAddress) Cost() (cost *Integer, err error) {
err, exit := router_address.checkValid()
if exit {
return
}
cost, err = NewInteger([]byte{router_address[0]})
return
}
//
// Return the Date this RouterAddress expires and any errors encountered
// parsing the RouterAddress.
//
func (router_address RouterAddress) Expiration() (date Date, err error) {
err, exit := router_address.checkValid()
if exit {
return
}
copy(date[:], router_address[1:ROUTER_ADDRESS_MIN_SIZE])
return
}
//
// Return the Transport type for this RouterAddress and any errors encountered
// parsing the RouterAddress.
//
func (router_address RouterAddress) TransportStyle() (str String, err error) {
err, exit := router_address.checkValid()
if exit {
return
}
str, _, err = ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
return
}
//
// Return the Mapping containing the options for this RouterAddress and any
// errors encountered parsing the RouterAddress.
//
func (router_address RouterAddress) Options() (mapping Mapping, err error) {
err, exit := router_address.checkValid()
if exit {
return
}
_, remainder, err := ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
if len(remainder) == 0 {
return
}
mapping = Mapping(remainder)
return
}
//
// Check if the RouterAddress is empty or if it is too small to contain valid data.
//
func (router_address RouterAddress) checkValid() (err error, exit bool) {
addr_len := len(router_address)
exit = false
if addr_len == 0 {
log.WithFields(log.Fields{
"at": "(RouterAddress) checkValid",
"reason": "no data",
}).Error("invalid router address")
err = errors.New("error parsing RouterAddress: no data")
exit = true
} else if addr_len < ROUTER_ADDRESS_MIN_SIZE {
log.WithFields(log.Fields{
"at": "(RouterAddress) checkValid",
"reason": "data too small (len < ROUTER_ADDRESS_MIN_SIZE)",
}).Warn("router address format warning")
err = errors.New("warning parsing RouterAddress: data too small")
}
return
}
//
// Given a slice of bytes, read a RouterAddress, returning the remaining bytes and any
// errors encountered parsing the RouterAddress.
//
func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []byte, err error) {
test_address := RouterAddress(data)
err, _ = test_address.checkValid()
if err != nil {
return
}
router_address = append(router_address, data[:ROUTER_ADDRESS_MIN_SIZE]...)
str, remainder, err := ReadString(data[ROUTER_ADDRESS_MIN_SIZE:])
if err != nil {
return
}
router_address = append(router_address, str...)
map_size := &Integer{}
mapping := make([]byte, 0)
if len(remainder) >= 2 {
map_size, err = NewInteger(remainder[:2])
if len(remainder) < map_size.Value()+2 {
err = errors.New("not enough data for map inside router address")
router_address = RouterAddress([]byte{})
remainder = []byte{}
return
}
mapping = remainder[:map_size.Value()+2]
router_address = append(router_address, mapping...)
}
remainder = data[ROUTER_ADDRESS_MIN_SIZE+len(str)+len(mapping):]
return
}

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