Merge pull request #19 from go-i2p/noise-experimental

Noise Subsystem
This commit is contained in:
idk
2024-11-02 02:59:43 +00:00
committed by GitHub
90 changed files with 4565 additions and 958 deletions

View File

@ -47,3 +47,30 @@ callvis:
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

View File

@ -93,6 +93,26 @@ please keep up with these changes, as they will not be backward compatible and r
- [X] Data Types
- [X] Session Tag
## Verbosity ##
Logging can be enabled and configured using the DEBUG_I2P environment variable. By default, logging is disabled.
There are three available log levels:
- Debug
```shell
export DEBUG_I2P=debug
```
- Warn
```shell
export DEBUG_I2P=warn
```
- Error
```shell
export DEBUG_I2P=error
```
If I2P_DEBUG is set to an unrecognized variable, it will fall back to "debug".
## Contributing
See CONTRIBUTING.md for more information.

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

32
go.mod
View File

@ -1,21 +1,43 @@
module github.com/go-i2p/go-i2p
go 1.22
toolchain go1.22.5
go 1.23.1
require (
github.com/emirpasic/gods v1.18.1
github.com/beevik/ntp v1.4.3
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/stretchr/testify v1.9.0
go.step.sm/crypto v0.51.2
golang.org/x/crypto v0.26.0
go.step.sm/crypto v0.53.0
golang.org/x/crypto v0.27.0
)
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/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // 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/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.28.0 // indirect

67
go.sum
View File

@ -1,45 +1,100 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e h1:NMjWYVkgcQHGOy0/VxU0TU6smrcoxzj9hwDesx2sB0w=
github.com/eyedeekay/go-unzip v0.0.0-20240201194209-560d8225b50e/go.mod h1:fKfFM3BsOOyjtZmEty7FsGzGabXo8Eb/dHjyIhTtxsE=
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.step.sm/crypto v0.51.2 h1:5EiCGIMg7IvQTGmJrwRosbXeprtT80OhoS/PJarg60o=
go.step.sm/crypto v0.51.2/go.mod h1:QK7czLjN2k+uqVp5CHXxJbhc70kVRSP+0CQF3zsR5M0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.step.sm/crypto v0.53.0 h1:+1as1ogzuCzx15/468M4mEC5juogI5a0Fzbsyh1CuYY=
go.step.sm/crypto v0.53.0/go.mod h1:AqLU78RqNUHepLzyOWZuNN/2++Lu7dZENdO9UzWOGSk=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -5,11 +5,16 @@ package certificate
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
// log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/util/logger"
. "github.com/go-i2p/go-i2p/lib/common/data"
)
var log = logger.GetGoI2PLogger()
// Certificate Types
const (
CERT_NULL = iota
@ -70,14 +75,22 @@ func (c *Certificate) RawBytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.len.Bytes()...)
bytes = append(bytes, c.payload...)
log.WithFields(logrus.Fields{
"raw_bytes_length": len(bytes),
}).Debug("Generated raw bytes for certificate")
return bytes
}
// ExcessBytes returns the excess bytes in a certificate found after the specified payload length.
func (c *Certificate) ExcessBytes() []byte {
if len(c.payload) >= c.len.Int() {
return c.payload[c.len.Int():]
excess := c.payload[c.len.Int():]
log.WithFields(logrus.Fields{
"excess_bytes_length": len(excess),
}).Debug("Found excess bytes in certificate")
return excess
}
log.Debug("No excess bytes found in certificate")
return nil
}
@ -86,6 +99,9 @@ func (c *Certificate) Bytes() []byte {
bytes := c.kind.Bytes()
bytes = append(bytes, c.len.Bytes()...)
bytes = append(bytes, c.Data()...)
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
}).Debug("Generated bytes for certificate")
return bytes
}
@ -97,12 +113,18 @@ func (c *Certificate) length() (cert_len int) {
// Type returns the Certificate type specified in the first byte of the Certificate,
func (c *Certificate) Type() (cert_type int) {
cert_type = c.kind.Int()
log.WithFields(logrus.Fields{
"cert_type": cert_type,
}).Debug("Retrieved certificate type")
return
}
// Length returns the payload length of a Certificate.
func (c *Certificate) Length() (length int) {
length = c.len.Int()
log.WithFields(logrus.Fields{
"length": length,
}).Debug("Retrieved certificate length")
return
}
@ -111,9 +133,13 @@ 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
}
@ -125,7 +151,7 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
case 0:
certificate.kind = Integer([]byte{0})
certificate.len = Integer([]byte{0})
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
@ -135,7 +161,7 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
case 1, 2:
certificate.kind = Integer(data[0 : len(data)-1])
certificate.len = Integer([]byte{0})
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": len(data),
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
@ -149,7 +175,7 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
certificate.payload = data[CERT_MIN_SIZE:]
if certificate.len.Int() > len(data)-CERT_MIN_SIZE {
err = fmt.Errorf("certificate parsing warning: certificate data is shorter than specified by length")
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Certificate) NewCertificate",
"certificate_bytes_length": certificate.len.Int(),
"certificate_payload_length": payleng,
@ -160,6 +186,10 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
}).Error("invalid certificate, shorter than specified by length")
return
}
log.WithFields(logrus.Fields{
"type": certificate.kind.Int(),
"length": certificate.len.Int(),
}).Debug("Successfully created new certificate")
return
}
}
@ -169,8 +199,12 @@ func NewCertificate(data []byte) (certificate Certificate, err error) {
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
certificate, err = NewCertificate(data)
if err != nil && err.Error() == "certificate parsing warning: certificate data is longer than specified by length" {
log.Warn("Certificate data longer than specified length")
err = nil
}
remainder = certificate.ExcessBytes()
log.WithFields(logrus.Fields{
"remainder_length": len(remainder),
}).Debug("Read certificate and extracted remainder")
return
}

View File

@ -5,9 +5,12 @@ import (
"errors"
"time"
log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// DATE_SIZE is the length in bytes of an I2P Date.
const DATE_SIZE = 8
@ -51,7 +54,7 @@ func (date Date) Time() (date_time time.Time) {
// Any data after DATE_SIZE is returned as a remainder.
func ReadDate(data []byte) (date Date, remainder []byte, err error) {
if len(data) < 8 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"data": data,
}).Error("ReadDate: data is too short")
err = errors.New("ReadDate: data is too short")
@ -59,6 +62,10 @@ func ReadDate(data []byte) (date Date, remainder []byte, err error) {
}
copy(date[:], data[:8])
remainder = data[8:]
log.WithFields(logrus.Fields{
"date_value": date.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully read Date from data")
return
}
@ -66,6 +73,15 @@ func ReadDate(data []byte) (date Date, remainder []byte, err error) {
// Returns a pointer to Date unlike ReadDate.
func NewDate(data []byte) (date *Date, remainder []byte, err error) {
objdate, remainder, err := ReadDate(data)
if err != nil {
log.WithError(err).Error("Failed to create new Date")
return nil, remainder, err
}
date = &objdate
log.WithFields(logrus.Fields{
"date_value": date.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully created new Date")
return
}

View File

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

View File

@ -3,7 +3,7 @@ package data
import (
"errors"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
/*
@ -48,8 +48,12 @@ type Mapping struct {
// Values returns the values contained in a Mapping as MappingValues.
func (mapping Mapping) Values() MappingValues {
if mapping.vals == nil {
log.Debug("Mapping values are nil, returning empty MappingValues")
return MappingValues{}
}
log.WithFields(logrus.Fields{
"values_count": len(*mapping.vals),
}).Debug("Retrieved Mapping values")
return *mapping.vals
}
@ -74,30 +78,40 @@ func (mapping *Mapping) Data() []byte {
// HasDuplicateKeys returns true if two keys in a mapping are identical.
func (mapping *Mapping) HasDuplicateKeys() bool {
log.Debug("Checking for duplicate keys in Mapping")
seen_values := make(map[string]bool)
values := mapping.Values()
for _, pair := range values {
key, _ := pair[0].Data()
if _, present := seen_values[key]; present {
log.WithFields(logrus.Fields{
"duplicate_key": key,
}).Warn("Found duplicate key in Mapping")
return true
} else {
seen_values[key] = true
}
}
log.Debug("No duplicate keys found in Mapping")
return false
}
// GoMapToMapping converts a Go map of unformatted strings to *Mapping.
func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error) {
log.WithFields(logrus.Fields{
"input_map_size": len(gomap),
}).Debug("Converting Go map to Mapping")
map_vals := MappingValues{}
for k, v := range gomap {
key_str, kerr := ToI2PString(k)
if kerr != nil {
log.WithError(kerr).Error("Failed to convert key to I2PString")
err = kerr
return
}
val_str, verr := ToI2PString(v)
if verr != nil {
log.WithError(verr).Error("Failed to convert value to I2PString")
err = verr
return
}
@ -107,27 +121,46 @@ func GoMapToMapping(gomap map[string]string) (mapping *Mapping, err error) {
)
}
mapping = ValuesToMapping(map_vals)
log.WithFields(logrus.Fields{
"mapping_size": len(map_vals),
}).Debug("Successfully converted Go map to Mapping")
return
}
// Check if the string parsing error indicates that the Mapping
// should no longer be parsed.
func stopValueRead(err error) bool {
return err.Error() == "error parsing string: zero length"
result := err.Error() == "error parsing string: zero length"
if result {
log.WithError(err).Debug("Stopping value read due to zero length error")
}
return result
}
// Determine if the first byte in a slice of bytes is the provided byte.
func beginsWith(bytes []byte, chr byte) bool {
/*
return len(bytes) != 0 &&
bytes[0] == chr
*/
result := len(bytes) != 0 && bytes[0] == chr
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
"expected_char": string(chr),
"result": result,
}).Debug("Checked if bytes begin with specific character")
return result
}
// ReadMapping returns Mapping from a []byte.
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Reading Mapping from bytes")
if len(bytes) < 3 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
@ -137,16 +170,18 @@ func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
}
size, remainder, e := NewInteger(bytes, 2)
if e != nil {
log.WithError(e).Error("Failed to read Mapping size")
err = append(err, e)
}
if size.Int() == 0 {
log.Warn("Mapping size is zero")
return
}
mapping.size = size
map_bytes := remainder[:mapping.size.Int()]
remainder = remainder[mapping.size.Int():]
if len(remainder) == 0 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "zero length",
}).Warn("mapping format violation")
@ -161,20 +196,38 @@ func ReadMapping(bytes []byte) (mapping Mapping, remainder []byte, err []error)
err = append(err, mappingValueErrs...)
mapping.vals = vals
if len(mappingValueErrs) > 0 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadMapping",
"reason": "error parsing mapping values",
}).Warn("mapping format violation")
e := errors.New("error parsing mapping values")
err = append(err, e)
}
log.WithFields(logrus.Fields{
"mapping_size": mapping.size.Int(),
"values_count": len(*mapping.vals),
"remainder_length": len(remainder),
"error_count": len(err),
}).Debug("Finished reading Mapping")
return
}
// NewMapping creates a new *Mapping from []byte using ReadMapping.
// Returns a pointer to Mapping unlike ReadMapping.
func NewMapping(bytes []byte) (values *Mapping, remainder []byte, err []error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Creating new Mapping")
objvalues, remainder, err := ReadMapping(bytes)
values = &objvalues
log.WithFields(logrus.Fields{
"values_count": len(values.Values()),
"remainder_length": len(remainder),
"error_count": len(err),
}).Debug("Finished creating new Mapping")
return
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"sort"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
// MappingValues represents the parsed key value pairs inside of an I2P Mapping.
@ -12,12 +12,22 @@ type MappingValues [][2]I2PString
func (m MappingValues) Get(key I2PString) I2PString {
keyBytes, _ := key.Data()
log.WithFields(logrus.Fields{
"key": string(keyBytes),
}).Debug("Searching for key in MappingValues")
for _, pair := range m {
kb, _ := pair[0][0:].Data()
if kb == keyBytes {
return pair[1][1:]
log.WithFields(logrus.Fields{
"key": string(keyBytes),
"value": string(pair[1][1:]),
}).Debug("Found matching key in MappingValues")
return pair[1]
}
}
log.WithFields(logrus.Fields{
"key": string(keyBytes),
}).Debug("Key not found in MappingValues")
return nil
}
@ -27,6 +37,9 @@ func ValuesToMapping(values MappingValues) *Mapping {
// Default length to 2 * len
// 1 byte for ;
// 1 byte for =
log.WithFields(logrus.Fields{
"values_count": len(values),
}).Debug("Converting MappingValues to Mapping")
baseLength := 2 * len(values)
for _, mappingVals := range values {
for _, keyOrVal := range mappingVals {
@ -34,6 +47,10 @@ func ValuesToMapping(values MappingValues) *Mapping {
}
}
log.WithFields(logrus.Fields{
"mapping_size": baseLength,
}).Debug("Created Mapping from MappingValues")
mappingSize, _ := NewIntegerFromInt(baseLength, 2)
return &Mapping{
size: mappingSize,
@ -61,8 +78,13 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// mapping := remainder
// var remainder = mapping
// var err error
log.WithFields(logrus.Fields{
"input_length": len(remainder),
"map_length": map_length.Int(),
}).Debug("Reading MappingValues")
if remainder == nil || len(remainder) < 1 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "data shorter than expected",
}).Error("mapping contained no data")
@ -73,7 +95,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
int_map_length := map_length.Int()
mapping_len := len(remainder)
if mapping_len > int_map_length {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
@ -81,7 +103,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
}).Warn("mapping format warning")
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
} else if int_map_length > mapping_len {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"mapping_bytes_length": mapping_len,
"mapping_length_field": int_map_length,
@ -105,7 +127,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// One byte for ;
if len(remainder) < 6 {
// Not returning an error here as the issue is already flagged by mapping length being wrong.
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "mapping format violation",
}).Warn("mapping format violation, too few bytes for a kv pair")
@ -128,7 +150,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
keyAsString := string(keyBytes)
_, ok := encounteredKeysMap[keyAsString]
if ok {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "duplicate key in mapping",
"key": string(key_str),
@ -142,7 +164,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
}
if !beginsWith(remainder, 0x3d) {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected =",
"value:": string(remainder),
@ -168,7 +190,7 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
// log.Printf("(MAPPING VALUES DEBUG) Remainder: %s\n", remainder)
// log.Printf("(MAPPING VALUES DEBUG) String: value: %s", val_str)
if !beginsWith(remainder, 0x3b) {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(Mapping) Values",
"reason": "expected ;",
"value:": string(remainder),
@ -189,5 +211,12 @@ func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingVal
encounteredKeysMap[keyAsString] = true
}
values = &map_values
log.WithFields(logrus.Fields{
"values_count": len(map_values),
"remainder_length": len(remainder_bytes),
"error_count": len(errs),
}).Debug("Finished reading MappingValues")
return
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
// STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string
@ -32,7 +32,7 @@ type I2PString []byte
// Returns error if the specified does not match the actual length or the string is otherwise invalid.
func (str I2PString) Length() (length int, err error) {
if len(str) == 0 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"reason": "no data",
}).Error("error parsing string")
@ -41,6 +41,7 @@ func (str I2PString) Length() (length int, err error) {
}
l, _, err := NewInteger(str[:], 1)
if err != nil {
log.WithError(err).Error("Failed to create Integer from I2PString")
return l.Int(), err
}
length = l.Int()
@ -53,6 +54,12 @@ func (str I2PString) Length() (length int, err error) {
"data": string(str),
"reason": "data less than specified by length",
}).Error("string format warning")*/
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"reason": "data less than specified by length",
}).Warn("string format warning")
err = errors.New("string parsing warning: string data is shorter than specified by length")
}
return
@ -65,31 +72,42 @@ func (str I2PString) Data() (data string, err error) {
if err != nil {
switch err.Error() {
case "error parsing string: zero length":
log.WithError(err).Warn("Zero length I2PString")
return
case "string parsing warning: string data is shorter than specified by length":
log.WithError(err).Warn("I2PString data shorter than specified length")
if is, e := ToI2PString(string(str[:])); e != nil {
log.WithError(e).Error("Failed to convert short I2PString")
return "", e
} else {
return is.Data()
}
case "string parsing warning: string contains data beyond length":
log.WithError(err).Warn("I2PString contains data beyond specified length")
data = string(str[1:])
return
}
}
if length == 0 {
log.Debug("I2PString is empty")
return
}
data = string(str[1 : length+1])
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved I2PString data")
return
}
// ToI2PString converts a Go string to an I2PString.
// Returns error if the string exceeds STRING_MAX_SIZE.
func ToI2PString(data string) (str I2PString, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Converting string to I2PString")
data_len := len(data)
if data_len > STRING_MAX_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ToI2PI2PString",
"string_len": data_len,
"max_len": STRING_MAX_SIZE,
@ -101,6 +119,9 @@ func ToI2PString(data string) (str I2PString, err error) {
i2p_string := []byte{byte(data_len)}
i2p_string = append(i2p_string, []byte(data)...)
str = I2PString(i2p_string)
log.WithFields(logrus.Fields{
"i2pstring_length": len(str),
}).Debug("Successfully converted string to I2PString")
return
}
@ -113,8 +134,12 @@ func ToI2PString(data string) (str I2PString, err error) {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
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
@ -123,8 +148,16 @@ func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
l, err := str.Length()
if l != data_len-1 {
err = fmt.Errorf("error reading I2P string, length does not match data")
log.WithFields(logrus.Fields{
"expected_length": data_len - 1,
"actual_length": l,
}).Error("I2PString length mismatch")
return
}
log.WithFields(logrus.Fields{
"string_length": l,
"remainder_length": len(remainder),
}).Debug("Successfully read I2PString from bytes")
return
}

View File

@ -4,6 +4,9 @@ package destination
import (
"strings"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"github.com/go-i2p/go-i2p/lib/common/base32"
@ -11,6 +14,8 @@ import (
"github.com/go-i2p/go-i2p/lib/crypto"
)
var log = logger.GetGoI2PLogger()
/*
[Destination]
Accurate for version 0.9.49
@ -31,26 +36,50 @@ type Destination struct {
// Base32Address returns the I2P base32 address for this Destination.
func (destination Destination) Base32Address() (str string) {
log.Debug("Generating Base32 address for Destination")
dest := destination.KeysAndCert.KeyCertificate.Bytes()
hash := crypto.SHA256(dest)
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
str = str + ".b32.i2p"
log.WithFields(logrus.Fields{
"base32_address": str,
}).Debug("Generated Base32 address for Destination")
return
}
// Base64 returns the I2P base64 address for this Destination.
func (destination Destination) Base64() string {
log.Debug("Generating Base64 address for Destination")
dest := destination.KeysAndCert.KeyCertificate.Bytes()
return base64.EncodeToString(dest)
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

@ -30,12 +30,16 @@ payload :: data
import (
"errors"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/certificate"
. "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Key Certificate Signing Key Types
const (
KEYCERT_SIGN_DSA_SHA1 = iota
@ -99,29 +103,44 @@ type KeyCertificate struct {
// Data returns the raw []byte contained in the Certificate.
func (key_certificate KeyCertificate) Data() ([]byte, error) {
data := key_certificate.Certificate.RawBytes()
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved raw data from KeyCertificate")
return key_certificate.Certificate.RawBytes(), nil
}
// SigningPublicKeyType returns the SigningPublicKey type as a Go integer.
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(log.Fields{
log.WithFields(logrus.Fields{
"at": "(KeyCertificate) ConstructPublicKey",
"data_len": data_len,
"required_len": KEYCERT_PUBKEY_SIZE,
@ -135,24 +154,34 @@ func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_ke
var elg_key crypto.ElgPublicKey
copy(elg_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
public_key = elg_key
log.Debug("Constructed ElgPublicKey")
case KEYCERT_CRYPTO_X25519:
var ed25519_key crypto.Ed25519PublicKey
copy(ed25519_key[:], data[KEYCERT_PUBKEY_SIZE-KEYCERT_CRYPTO_ELG_SIZE:KEYCERT_PUBKEY_SIZE])
public_key = ed25519_key
log.Debug("Constructed Ed25519PublicKey")
default:
log.WithFields(logrus.Fields{
"key_type": key_type,
}).Warn("Unknown public key type")
}
return
}
// ConstructSigningPublicKey returns a SingingPublicKey constructed using any excess data that may be stored in the KeyCertificate.
// Returns any errors encountered while parsing.
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Constructing SigningPublicKey from KeyCertificate")
signing_key_type := key_certificate.PublicKeyType()
if err != nil {
return
}
data_len := len(data)
if data_len < key_certificate.SignatureSize() {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(KeyCertificate) ConstructSigningPublicKey",
"data_len": data_len,
"required_len": KEYCERT_SPK_SIZE,
@ -166,31 +195,55 @@ func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (si
var dsa_key crypto.DSAPublicKey
copy(dsa_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_DSA_SHA1_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = dsa_key
log.Debug("Constructed DSAPublicKey")
case KEYCERT_SIGN_P256:
var ec_key crypto.ECP256PublicKey
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P256_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_key
log.Debug("Constructed ECP256PublicKey")
case KEYCERT_SIGN_P384:
var ec_key crypto.ECP384PublicKey
copy(ec_key[:], data[KEYCERT_SPK_SIZE-KEYCERT_SIGN_P384_SIZE:KEYCERT_SPK_SIZE])
signing_public_key = ec_key
log.Debug("Constructed ECP384PublicKey")
case KEYCERT_SIGN_P521:
var ec_key crypto.ECP521PublicKey
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
copy(ec_key[:], data)
copy(ec_key[KEYCERT_SPK_SIZE:], key_certificate.Certificate.RawBytes()[4:4+extra])
signing_public_key = ec_key
log.Debug("Constructed ECP521PublicKey")
case KEYCERT_SIGN_RSA2048:
// var rsa_key crypto.RSA2048PublicKey
// extra := KEYCERT_SIGN_RSA2048_SIZE - 128
// copy(rsa_key[:], data)
// copy(rsa_key[128:], key_certificate[4:4+extra])
// signing_public_key = rsa_key
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Signing key type KEYCERT_SIGN_RSA2048 not implemented")
case KEYCERT_SIGN_RSA3072:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Signing key type KEYCERT_SIGN_RSA3072 not implemented")
case KEYCERT_SIGN_RSA4096:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Signing key type KEYCERT_SIGN_RSA4096 not implemented")
case KEYCERT_SIGN_ED25519:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Signing key type KEYCERT_SIGN_ED25519 not implemented")
case KEYCERT_SIGN_ED25519PH:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Signing key type KEYCERT_SIGN_ED25519PH not implemented")
default:
log.WithFields(logrus.Fields{
"signing_key_type": signing_key_type,
}).Warn("Unknown signing key type")
}
return
}
@ -208,6 +261,11 @@ func (key_certificate KeyCertificate) SignatureSize() (size int) {
KEYCERT_SIGN_ED25519PH: KEYCERT_SIGN_ED25519PH_SIZE,
}
key_type := key_certificate.SigningPublicKeyType()
size = sizes[int(key_type)]
log.WithFields(logrus.Fields{
"key_type": key_type,
"signature_size": size,
}).Debug("Retrieved signature size")
return sizes[int(key_type)]
}
@ -221,6 +279,11 @@ func (key_certificate KeyCertificate) CryptoSize() (size int) {
KEYCERT_CRYPTO_X25519: KEYCERT_CRYPTO_X25519_SIZE,
}
key_type := key_certificate.PublicKeyType()
size = sizes[int(key_type)]
log.WithFields(logrus.Fields{
"key_type": key_type,
"crypto_size": size,
}).Debug("Retrieved crypto size")
return sizes[int(key_type)]
}
@ -228,14 +291,20 @@ func (key_certificate KeyCertificate) CryptoSize() (size int) {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(bytes),
}).Debug("Creating new KeyCertificate")
var certificate Certificate
certificate, remainder, err = ReadCertificate(bytes)
if err != nil {
log.WithError(err).Error("Failed to read Certificate")
return
}
if len(bytes) < KEYCERT_MIN_SIZE {
err = errors.New("error parsing key certificate: not enough data")
remainder = bytes[KEYCERT_MIN_SIZE:]
log.WithError(err).Error("KeyCertificate data too short")
}
key_certificate = &KeyCertificate{
Certificate: certificate,
@ -246,11 +315,25 @@ func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, remainder
if len(bytes) >= 7 {
key_certificate.cpkType = Integer(bytes[6:7])
}
log.WithFields(logrus.Fields{
"spk_type": key_certificate.spkType.Int(),
"cpk_type": key_certificate.cpkType.Int(),
"remainder_length": len(remainder),
}).Debug("Successfully created new KeyCertificate")
return
}
// KeyCertificateFromCertificate returns a *KeyCertificate from a *Certificate.
func KeyCertificateFromCertificate(certificate Certificate) *KeyCertificate {
k, _, _ := NewKeyCertificate(certificate.RawBytes())
log.Debug("Creating KeyCertificate from Certificate")
// k, _, _ := NewKeyCertificate(certificate.RawBytes())
k, _, err := NewKeyCertificate(certificate.RawBytes())
if err != nil {
log.WithError(err).Error("Failed to create KeyCertificate from Certificate")
} else {
log.Debug("Successfully created KeyCertificate from Certificate")
}
return k
}

View File

@ -4,12 +4,16 @@ package keys_and_cert
import (
"errors"
"github.com/go-i2p/go-i2p/lib/util/logger"
. "github.com/go-i2p/go-i2p/lib/common/certificate"
. "github.com/go-i2p/go-i2p/lib/common/key_certificate"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Sizes of various KeysAndCert structures and requirements
const (
KEYS_AND_CERT_PUBKEY_SIZE = 256
@ -80,7 +84,11 @@ type KeysAndCert struct {
// Bytes returns the entire KeyCertificate in []byte form, trims payload to specified length.
func (keys_and_cert KeysAndCert) Bytes() []byte {
return keys_and_cert.KeyCertificate.Bytes()
bytes := keys_and_cert.KeyCertificate.Bytes()
log.WithFields(logrus.Fields{
"bytes_length": len(bytes),
}).Debug("Retrieved bytes from KeysAndCert")
return bytes
}
// PublicKey returns the public key as a crypto.PublicKey.
@ -101,10 +109,14 @@ func (keys_and_cert *KeysAndCert) Certificate() (cert Certificate) {
// ReadKeysAndCert creates a new *KeysAndCert from []byte using ReadKeysAndCert.
// Returns a pointer to KeysAndCert unlike ReadKeysAndCert.
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Reading KeysAndCert from data")
data_len := len(data)
// keys_and_cert = KeysAndCert{}
if data_len < KEYS_AND_CERT_MIN_SIZE && data_len > KEYS_AND_CERT_DATA_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
@ -114,7 +126,7 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
keys_and_cert.KeyCertificate, remainder, _ = NewKeyCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
return
} else if data_len < KEYS_AND_CERT_DATA_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "ReadKeysAndCert",
"data_len": data_len,
"required_len": KEYS_AND_CERT_MIN_SIZE,
@ -125,6 +137,7 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
}
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.
@ -132,13 +145,23 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
// 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
}

View File

@ -4,6 +4,9 @@ package lease_set
import (
"errors"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/certificate"
. "github.com/go-i2p/go-i2p/lib/common/data"
. "github.com/go-i2p/go-i2p/lib/common/destination"
@ -12,9 +15,10 @@ import (
. "github.com/go-i2p/go-i2p/lib/common/lease"
. "github.com/go-i2p/go-i2p/lib/common/signature"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Sizes of various structures in an I2P LeaseSet
const (
LEASE_SET_PUBKEY_SIZE = 256
@ -134,9 +138,15 @@ type LeaseSet struct {
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
keys_and_cert, _, err := ReadKeysAndCert(lease_set)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert from LeaseSet")
return
}
destination, _, err = ReadDestination(keys_and_cert.Bytes())
if err != nil {
log.WithError(err).Error("Failed to read Destination from KeysAndCert")
} else {
log.Debug("Successfully retrieved Destination from LeaseSet")
}
return
}
@ -146,7 +156,7 @@ func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error
_, remainder, err := ReadKeysAndCert(lease_set)
remainder_len := len(remainder)
if remainder_len < LEASE_SET_PUBKEY_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) PublicKey",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE,
@ -157,25 +167,29 @@ func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error
return
}
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
log.Debug("Successfully retrieved PublicKey from LeaseSet")
return
}
// SigningKey returns the signing public key as crypto.SigningPublicKey.
// returns errors encountered during parsing.
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
log.Debug("Retrieving SigningKey from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for SigningKey")
return
}
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE
cert := destination.Certificate()
cert_len := cert.Length()
if err != nil {
log.WithError(err).Error("Failed to get Certificate length")
return
}
lease_set_len := len(lease_set)
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) SigningKey",
"data_len": lease_set_len,
"required_len": offset + LEASE_SET_SPK_SIZE,
@ -190,6 +204,7 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
signing_public_key = dsa_pk
log.Debug("Retrieved legacy DSA SHA1 SigningPublicKey")
} else {
// A Certificate is present in this LeaseSet's Destination
cert_type := cert.Type()
@ -200,14 +215,19 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
signing_public_key, err = KeyCertificateFromCertificate(cert).ConstructSigningPublicKey(
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
)
if err != nil {
log.WithError(err).Error("Failed to construct SigningPublicKey from KeyCertificate")
} else {
log.Debug("Retrieved SigningPublicKey from KeyCertificate")
}
} else {
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
var dsa_pk crypto.DSAPublicKey
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
signing_public_key = dsa_pk
log.Debug("Retrieved legacy DSA SHA1 SigningPublicKey (Certificate present but not Key Certificate)")
}
}
return
}
@ -215,13 +235,15 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
// LeaseCount returns the numbert of leases specified by the LeaseCount value as int.
// returns errors encountered during parsing.
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
log.Debug("Retrieving LeaseCount from LeaseSet")
_, remainder, err := ReadKeysAndCert(lease_set)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert for LeaseCount")
return
}
remainder_len := len(remainder)
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) LeaseCount",
"data_len": remainder_len,
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
@ -233,12 +255,14 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
c := Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
count = c.Int()
if count > 16 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) LeaseCount",
"lease_count": count,
"reason": "more than 16 leases",
}).Warn("invalid lease set")
err = errors.New("invalid lease set: more than 16 leases")
} else {
log.WithField("lease_count", count).Debug("Retrieved LeaseCount from LeaseSet")
}
return
}
@ -246,13 +270,16 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
// Leases returns the leases as []Lease.
// returns errors encountered during parsing.
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
log.Debug("Retrieving Leases from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for Leases")
return
}
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
count, err := lease_set.LeaseCount()
if err != nil {
log.WithError(err).Error("Failed to retrieve LeaseCount for Leases")
return
}
for i := 0; i < count; i++ {
@ -260,7 +287,7 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
end := start + LEASE_SIZE
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Leases",
"data_len": lease_set_len,
"required_len": end,
@ -273,18 +300,22 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
copy(lease[:], lease_set[start:end])
leases = append(leases, lease)
}
log.WithField("lease_count", len(leases)).Debug("Retrieved Leases from LeaseSet")
return
}
// Signature returns the signature as Signature.
// returns errors encountered during parsing.
func (lease_set LeaseSet) Signature() (signature Signature, err error) {
log.Debug("Retrieving Signature from LeaseSet")
destination, err := lease_set.Destination()
if err != nil {
log.WithError(err).Error("Failed to retrieve Destination for Signature")
return
}
lease_count, err := lease_set.LeaseCount()
if err != nil {
log.WithError(err).Error("Failed to retrieve LeaseCount for Signature")
return
}
start := len(destination.Bytes()) +
@ -302,7 +333,7 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
}
lease_set_len := len(lease_set)
if lease_set_len < end {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(LeaseSet) Signature",
"data_len": lease_set_len,
"required_len": end,
@ -312,11 +343,13 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
return
}
signature = []byte(lease_set[start:end])
log.WithField("signature_length", len(signature)).Debug("Retrieved Signature from LeaseSet")
return
}
// Verify returns nil
func (lease_set LeaseSet) Verify() error {
log.Debug("Verifying LeaseSet")
//data_end := len(destination) +
// LEASE_SET_PUBKEY_SIZE +
// LEASE_SET_SPK_SIZE +
@ -330,14 +363,17 @@ func (lease_set LeaseSet) Verify() error {
//if err != nil {
// return err
//}
log.Warn("LeaseSet verification not implemented")
return nil // verifier.Verify(data, lease_set.Signature())
}
// NewestExpiration returns the newest lease expiration as an I2P Date.
// Returns errors encountered during parsing.
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
log.Debug("Finding newest expiration in LeaseSet")
leases, err := lease_set.Leases()
if err != nil {
log.WithError(err).Error("Failed to retrieve Leases for NewestExpiration")
return
}
newest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
@ -347,14 +383,17 @@ func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
newest = date
}
}
log.WithField("newest_expiration", newest.Time()).Debug("Found newest expiration in LeaseSet")
return
}
// OldestExpiration returns the oldest lease expiration as an I2P Date.
// Returns errors encountered during parsing.
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
log.Debug("Finding oldest expiration in LeaseSet")
leases, err := lease_set.Leases()
if err != nil {
log.WithError(err).Error("Failed to retrieve Leases for OldestExpiration")
return
}
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
@ -364,5 +403,6 @@ func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
earliest = date
}
}
log.WithField("oldest_expiration", earliest.Time()).Debug("Found oldest expiration in LeaseSet")
return
}

View File

@ -8,8 +8,10 @@ import (
"strconv"
"strings"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/data"
log "github.com/sirupsen/logrus"
)
// Minimum number of bytes in a valid RouterAddress
@ -17,6 +19,8 @@ const (
ROUTER_ADDRESS_MIN_SIZE = 9
)
var log = logger.GetGoI2PLogger()
/*
[RouterAddress]
Accurate for version 0.9.49
@ -75,34 +79,48 @@ type RouterAddress struct {
// Network implements net.Addr. It returns the transport type plus 4 or 6
func (router_address *RouterAddress) Network() string {
log.Debug("Getting network for RouterAddress")
if router_address.TransportType == nil {
log.Warn("TransportType is nil in RouterAddress")
return ""
}
str, err := router_address.TransportType.Data()
if err != nil {
log.WithError(err).Error("Failed to get TransportType data")
return ""
}
return string(str) + router_address.IPVersion()
network := string(str) + router_address.IPVersion()
log.WithField("network", network).Debug("Retrieved network for RouterAddress")
return network
}
// IPVersion returns a string "4" for IPv4 or 6 for IPv6
func (router_address *RouterAddress) IPVersion() string {
log.Debug("Getting IP version for RouterAddress")
str, err := router_address.CapsString().Data()
if err != nil {
log.WithError(err).Error("Failed to get CapsString data")
return ""
}
if strings.HasSuffix(str, "6") {
log.Debug("IP version is IPv6")
return "6"
}
log.Debug("IP version is IPv4")
return "4"
}
func (router_address *RouterAddress) UDP() bool {
return strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
// return strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
log.Debug("Checking if RouterAddress is UDP")
isUDP := strings.HasPrefix(strings.ToLower(router_address.Network()), "ssu")
log.WithField("is_udp", isUDP).Debug("Checked if RouterAddress is UDP")
return isUDP
}
// String implements net.Addr. It returns the IP address, followed by the options
func (router_address *RouterAddress) String() string {
log.Debug("Converting RouterAddress to string")
var rv []string
rv = append(rv, string(router_address.TransportStyle()))
rv = append(rv, string(router_address.HostString()))
@ -121,25 +139,29 @@ func (router_address *RouterAddress) String() string {
rv = append(rv, string(router_address.IntroducerExpirationString(2)))
rv = append(rv, string(router_address.IntroducerTagString(2)))
}
return strings.TrimSpace(strings.Join(rv, " "))
str := strings.TrimSpace(strings.Join(rv, " "))
log.WithField("router_address_string", str).Debug("Converted RouterAddress to string")
return str
}
var ex_addr net.Addr = &RouterAddress{}
// Bytes returns the router address as a []byte.
func (router_address RouterAddress) Bytes() []byte {
log.Debug("Converting RouterAddress to bytes")
bytes := make([]byte, 0)
bytes = append(bytes, router_address.TransportCost.Bytes()...)
bytes = append(bytes, router_address.ExpirationDate.Bytes()...)
strData, err := router_address.TransportType.Data()
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"error": err,
}).Error("RouterAddress.Bytes: error getting transport_style bytes")
} else {
bytes = append(bytes, strData...)
}
bytes = append(bytes, router_address.TransportOptions.Data()...)
log.WithField("bytes_length", len(bytes)).Debug("Converted RouterAddress to bytes")
return bytes
}
@ -224,29 +246,45 @@ func (router_address RouterAddress) IntroducerTagString(num int) I2PString {
}
func (router_address RouterAddress) Host() (net.Addr, error) {
log.Debug("Getting host from RouterAddress")
host := router_address.HostString()
hostBytes, err := host.Data()
if err != nil {
log.WithError(err).Error("Failed to get host data")
return nil, err
}
ip := net.ParseIP(hostBytes)
if ip == nil {
log.Error("Failed to parse IP address")
return nil, fmt.Errorf("null host error")
}
return net.ResolveIPAddr("", ip.String())
// return net.ResolveIPAddr("", ip.String())
addr, err := net.ResolveIPAddr("", ip.String())
if err != nil {
log.WithError(err).Error("Failed to resolve IP address")
} else {
log.WithField("addr", addr).Debug("Retrieved host from RouterAddress")
}
return addr, err
}
func (router_address RouterAddress) Port() (string, error) {
log.Debug("Getting port from RouterAddress")
port := router_address.PortString()
portBytes, err := port.Data()
if err != nil {
log.WithError(err).Error("Failed to get port data")
return "", err
}
val, err := strconv.Atoi(portBytes)
if err != nil {
log.WithError(err).Error("Failed to convert port to integer")
return "", err
}
return strconv.Itoa(val), nil
// return strconv.Itoa(val), nil
portStr := strconv.Itoa(val)
log.WithField("port", portStr).Debug("Retrieved port from RouterAddress")
return portStr, nil
}
func (router_address RouterAddress) StaticKey() ([32]byte, error) {
@ -283,6 +321,7 @@ func (router_address RouterAddress) checkValid() (err error, exit bool) {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []byte, err error) {
log.WithField("data_length", len(data)).Debug("Reading RouterAddress from data")
if len(data) == 0 {
log.WithField("at", "(RouterAddress) ReadRouterAddress").Error("error parsing RouterAddress: no data")
err = errors.New("error parsing RouterAddress: no data")
@ -290,21 +329,21 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
}
router_address.TransportCost, remainder, err = NewInteger(data, 1)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing cost",
}).Warn("error parsing RouterAddress")
}
router_address.ExpirationDate, remainder, err = NewDate(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing expiration",
}).Error("error parsing RouterAddress")
}
router_address.TransportType, remainder, err = ReadI2PString(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing transport_style",
}).Error("error parsing RouterAddress")
@ -312,7 +351,7 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
var errs []error
router_address.TransportOptions, remainder, errs = NewMapping(remainder)
for _, err := range errs {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterAddress) ReadNewRouterAddress",
"reason": "error parsing options",
"error": err,

View File

@ -3,8 +3,12 @@ package router_identity
import (
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
/*
[RouterIdentity]
Accurate for version 0.9.49
@ -27,9 +31,19 @@ type RouterIdentity struct {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder []byte, err error) {
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Reading RouterIdentity from data")
keys_and_cert, remainder, err := ReadKeysAndCert(data)
if err != nil {
log.WithError(err).Error("Failed to read KeysAndCert for RouterIdentity")
return
}
router_identity = RouterIdentity{
keys_and_cert,
}
log.WithFields(logrus.Fields{
"remainder_length": len(remainder),
}).Debug("Successfully read RouterIdentity")
return
}

View File

@ -6,13 +6,17 @@ import (
"strconv"
"strings"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
. "github.com/go-i2p/go-i2p/lib/common/data"
. "github.com/go-i2p/go-i2p/lib/common/router_address"
. "github.com/go-i2p/go-i2p/lib/common/router_identity"
. "github.com/go-i2p/go-i2p/lib/common/signature"
log "github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
const ROUTER_INFO_MIN_SIZE = 439
const (
@ -115,6 +119,7 @@ type RouterInfo struct {
// Bytes returns the RouterInfo as a []byte suitable for writing to a stream.
func (router_info RouterInfo) Bytes() (bytes []byte, err error) {
log.Debug("Converting RouterInfo to bytes")
bytes = append(bytes, router_info.router_identity.KeysAndCert.Bytes()...)
bytes = append(bytes, router_info.published.Bytes()...)
bytes = append(bytes, router_info.size.Bytes()...)
@ -124,11 +129,12 @@ func (router_info RouterInfo) Bytes() (bytes []byte, err error) {
bytes = append(bytes, router_info.peer_size.Bytes()...)
bytes = append(bytes, router_info.options.Data()...)
bytes = append(bytes, []byte(*router_info.signature)...)
log.WithField("bytes_length", len(bytes)).Debug("Converted RouterInfo to bytes")
return bytes, err
}
func (router_info RouterInfo) String() string {
log.Debug("Converting RouterInfo to string")
str := "Certificate: " + string(router_info.router_identity.KeysAndCert.Bytes())
str += "Published: " + string(router_info.published.Bytes())
str += "Addresses:" + string(router_info.size.Bytes())
@ -138,6 +144,7 @@ func (router_info RouterInfo) String() string {
str += "Peer Size: " + string(router_info.peer_size.Bytes())
str += "Options: " + string(router_info.options.Data())
str += "Signature: " + string([]byte(*router_info.signature))
log.WithField("string_length", len(str)).Debug("Converted RouterInfo to string")
return str
}
@ -148,7 +155,10 @@ func (router_info *RouterInfo) RouterIdentity() *RouterIdentity {
// IndentHash returns the identity hash (sha256 sum) for this RouterInfo.
func (router_info *RouterInfo) IdentHash() Hash {
log.Debug("Calculating IdentHash for RouterInfo")
data, _ := router_info.RouterIdentity().KeyCertificate.Data()
hash := HashData(data)
log.WithField("hash", hash).Debug("Calculated IdentHash for RouterInfo")
return HashData(data)
}
@ -159,11 +169,14 @@ func (router_info *RouterInfo) Published() *Date {
// RouterAddressCount returns the count of RouterAddress in this RouterInfo as a Go integer.
func (router_info *RouterInfo) RouterAddressCount() int {
return router_info.size.Int()
count := router_info.size.Int()
log.WithField("count", count).Debug("Retrieved RouterAddressCount from RouterInfo")
return count
}
// RouterAddresses returns all RouterAddresses for this RouterInfo as []*RouterAddress.
func (router_info *RouterInfo) RouterAddresses() []*RouterAddress {
log.WithField("address_count", len(router_info.addresses)).Debug("Retrieved RouterAddresses from RouterInfo")
return router_info.addresses
}
@ -193,9 +206,11 @@ func (router_info RouterInfo) Network() string {
// The remaining bytes after the specified length are also returned.
// Returns a list of errors that occurred during parsing.
func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error) {
log.WithField("input_length", len(bytes)).Debug("Reading RouterInfo from bytes")
info.router_identity, remainder, err = ReadRouterIdentity(bytes)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(bytes),
"required_len": ROUTER_INFO_MIN_SIZE,
@ -206,7 +221,7 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
}
info.published, remainder, err = NewDate(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
"required_len": DATE_SIZE,
@ -216,7 +231,7 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
}
info.size, remainder, err = NewInteger(remainder, 1)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
"required_len": info.size.Int(),
@ -227,7 +242,7 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
address, more, err := ReadRouterAddress(remainder)
remainder = more
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": ROUTER_ADDRESS_SIZE,
@ -238,10 +253,14 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
info.addresses = append(info.addresses, &address)
}
info.peer_size, remainder, err = NewInteger(remainder, 1)
if err != nil {
log.WithError(err).Error("Failed to read PeerSize")
return
}
var errs []error
info.options, remainder, errs = NewMapping(remainder)
if len(errs) != 0 {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": MAPPING_SIZE,
@ -255,7 +274,7 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
}
info.signature, remainder, err = NewSignature(remainder)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(RouterInfo) ReadRouterInfo",
"data_len": len(remainder),
//"required_len": MAPPING_SIZE,
@ -263,29 +282,49 @@ func ReadRouterInfo(bytes []byte) (info RouterInfo, remainder []byte, err error)
}).Error("error parsing router info")
err = errors.New("error parsing router info: not enough data")
}
log.WithFields(logrus.Fields{
"router_identity": info.router_identity,
"published": info.published,
"address_count": len(info.addresses),
"remainder_length": len(remainder),
}).Debug("Successfully read RouterInfo")
return
}
func (router_info *RouterInfo) RouterCapabilities() string {
log.Debug("Retrieving RouterCapabilities")
str, err := ToI2PString("caps")
if err != nil {
log.WithError(err).Error("Failed to create I2PString for 'caps'")
return ""
}
return string(router_info.options.Values().Get(str))
// return string(router_info.options.Values().Get(str))
caps := string(router_info.options.Values().Get(str))
log.WithField("capabilities", caps).Debug("Retrieved RouterCapabilities")
return caps
}
func (router_info *RouterInfo) RouterVersion() string {
log.Debug("Retrieving RouterVersion")
str, err := ToI2PString("router.version")
if err != nil {
log.WithError(err).Error("Failed to create I2PString for 'router.version'")
return ""
}
return string(router_info.options.Values().Get(str))
// return string(router_info.options.Values().Get(str))
version := string(router_info.options.Values().Get(str))
log.WithField("version", version).Debug("Retrieved RouterVersion")
return version
}
func (router_info *RouterInfo) GoodVersion() bool {
log.Debug("Checking if RouterVersion is good")
version := router_info.RouterVersion()
v := strings.Split(version, ".")
if len(v) != 3 {
log.WithField("version", version).Warn("Invalid version format")
return false
}
if v[0] == "0" {
@ -296,27 +335,41 @@ func (router_info *RouterInfo) GoodVersion() bool {
}
}
}
log.WithField("version", version).Warn("Version not in good range")
return false
}
func (router_info *RouterInfo) UnCongested() bool {
log.Debug("Checking if RouterInfo is uncongested")
caps := router_info.RouterCapabilities()
if strings.Contains(caps, "K") {
log.WithField("reason", "K capability").Warn("RouterInfo is congested")
return false
}
if strings.Contains(caps, "G") {
log.WithField("reason", "G capability").Warn("RouterInfo is congested")
return false
}
if strings.Contains(caps, "E") {
log.WithField("reason", "E capability").Warn("RouterInfo is congested")
return false
}
log.Debug("RouterInfo is uncongested")
return true
}
func (router_info *RouterInfo) Reachable() bool {
log.Debug("Checking if RouterInfo is reachable")
caps := router_info.RouterCapabilities()
if strings.Contains(caps, "U") {
log.WithField("reason", "U capability").Debug("RouterInfo is unreachable")
return false
}
return strings.Contains(caps, "R")
// return strings.Contains(caps, "R")
reachable := strings.Contains(caps, "R")
log.WithFields(logrus.Fields{
"reachable": reachable,
"reason": "R capability",
}).Debug("Checked RouterInfo reachability")
return reachable
}

View File

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

View File

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

View File

@ -1,6 +1,13 @@
// Package signature implements the I2P Signature common data structure
package signature
import (
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
// Lengths of signature keys
const (
DSA_SHA1_SIZE = 40
@ -37,13 +44,25 @@ type Signature []byte
// Returns a list of errors that occurred during parsing.
func ReadSignature(bytes []byte) (info Signature, remainder []byte, err error) {
// TODO: stub
log.Warn("ReadSignature is not implemented")
return
}
// NewSignature creates a new *Signature from []byte using ReadSignature.
// Returns a pointer to Signature unlike ReadSignature.
func NewSignature(data []byte) (session_tag *Signature, remainder []byte, err error) {
sessionTag, remainder, err := ReadSignature(data)
session_tag = &sessionTag
log.WithField("input_length", len(data)).Debug("Creating new Signature")
// sessionTag, remainder, err := ReadSignature(data)
sig, remainder, err := ReadSignature(data)
if err != nil {
log.WithError(err).Error("Failed to read Signature")
return nil, remainder, err
}
session_tag = &sig
log.WithFields(logrus.Fields{
"signature_length": len(sig),
"remainder_length": len(remainder),
}).Debug("Successfully created new Signature")
return
}

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,8 @@ import (
"errors"
"io"
"math/big"
"github.com/sirupsen/logrus"
)
var Ed25519EncryptTooBig = errors.New("failed to encrypt data, too big for Ed25519")
@ -30,10 +32,14 @@ func (k Ed25519PublicKey) Len() int {
}
func createEd25519PublicKey(data []byte) (k *ed25519.PublicKey) {
log.WithField("data_length", len(data)).Debug("Creating Ed25519 public key")
if len(data) == 256 {
k2 := ed25519.PublicKey{}
copy(k2[:], data)
k = &k2
log.Debug("Ed25519 public key created successfully")
} else {
log.Warn("Invalid data length for Ed25519 public key")
}
return
}
@ -52,6 +58,7 @@ func createEd25519Encryption(pub *ed25519.PublicKey, rand io.Reader) (enc *Ed255
if err == nil {
enc = &Ed25519Encryption{}
}*/
log.Warn("createEd25519Encryption is not implemented")
return
}
@ -60,11 +67,18 @@ type Ed25519Encryption struct {
}
func (ed25519 *Ed25519Encryption) Encrypt(data []byte) (enc []byte, err error) {
log.Warn("createEd25519Encryption is not implemented")
return ed25519.EncryptPadding(data, true)
}
func (ed25519 *Ed25519Encryption) EncryptPadding(data []byte, zeroPadding bool) (encrypted []byte, err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"zero_padding": zeroPadding,
}).Debug("Encrypting data with padding using Ed25519")
if len(data) > 222 {
log.Error("Data too big for Ed25519 encryption")
err = Ed25519EncryptTooBig
return
}
@ -87,33 +101,56 @@ func (ed25519 *Ed25519Encryption) EncryptPadding(data []byte, zeroPadding bool)
copy(encrypted, ed25519.a.Bytes())
copy(encrypted[256:], b)
}
log.WithField("encrypted_length", len(encrypted)).Debug("Data encrypted successfully with Ed25519")
return
}
func (elg Ed25519PublicKey) NewEncrypter() (enc Encrypter, err error) {
log.Debug("Creating new Ed25519 encrypter")
k := createEd25519PublicKey(elg[:])
enc, err = createEd25519Encryption(k, rand.Reader)
if err != nil {
log.WithError(err).Error("Failed to create Ed25519 encrypter")
} else {
log.Debug("Ed25519 encrypter created successfully")
}
return
}
func (v *Ed25519Verifier) VerifyHash(h, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"hash_length": len(h),
"sig_length": len(sig),
}).Debug("Verifying Ed25519 signature hash")
if len(sig) != ed25519.SignatureSize {
log.Error("Bad Ed25519 signature size")
err = ErrBadSignatureSize
return
}
if len(v.k) != ed25519.PublicKeySize {
log.Error("Invalid Ed25519 public key size")
err = errors.New("failed to verify: invalid ed25519 public key size")
return
}
ok := ed25519.Verify(v.k, h, sig)
if !ok {
log.Warn("Invalid Ed25519 signature")
err = errors.New("failed to verify: invalid signature")
} else {
log.Debug("Ed25519 signature verified successfully")
}
return
}
func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
log.WithFields(logrus.Fields{
"data_length": len(data),
"sig_length": len(sig),
}).Debug("Verifying Ed25519 signature")
h := sha512.Sum512(data)
err = v.VerifyHash(h[:], sig)
return
@ -126,7 +163,10 @@ type Ed25519Signer struct {
}
func (s *Ed25519Signer) Sign(data []byte) (sig []byte, err error) {
log.WithField("data_length", len(data)).Debug("Signing data with Ed25519")
if len(s.k) != ed25519.PrivateKeySize {
log.Error("Invalid Ed25519 private key size")
err = errors.New("failed to sign: invalid ed25519 private key size")
return
}
@ -136,6 +176,8 @@ func (s *Ed25519Signer) Sign(data []byte) (sig []byte, err error) {
}
func (s *Ed25519Signer) SignHash(h []byte) (sig []byte, err error) {
log.WithField("hash_length", len(h)).Debug("Signing hash with Ed25519")
sig = ed25519.Sign(s.k, h)
log.WithField("signature_length", len(sig)).Debug("Ed25519 signature created successfully")
return
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,41 @@
package noise
import (
"sync"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/go-i2p/go-i2p/lib/common/router_info"
)
var log = logger.GetGoI2PLogger()
func (c *NoiseTransport) Handshake(routerInfo router_info.RouterInfo) error {
log.WithField("router_info", routerInfo.IdentHash()).Debug("Starting Noise handshake")
c.Mutex.Lock()
defer c.Mutex.Unlock()
session, err := c.getSession(routerInfo)
if err != nil {
log.WithError(err).Error("Failed to get session for handshake")
return err
}
log.Debug("Session obtained for handshake")
// Set handshakeCond to indicate that this goroutine is committing to
// running the handshake.
session.(*NoiseSession).Cond = sync.NewCond(&c.Mutex)
c.Mutex.Unlock()
session.(*NoiseSession).Mutex.Lock()
defer session.(*NoiseSession).Mutex.Unlock()
c.Mutex.Lock()
log.Debug("Running outgoing handshake")
if err := session.(*NoiseSession).RunOutgoingHandshake(); err != nil {
return err
}
log.Debug("Outgoing handshake completed successfully")
// Wake any other goroutines that are waiting for this handshake to
// complete.
session.(*NoiseSession).Cond.Broadcast()
session.(*NoiseSession).Cond = nil
log.Debug("Noise handshake completed successfully")
return nil
}

View File

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

View File

@ -0,0 +1,88 @@
package noise
import (
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"github.com/sirupsen/logrus"
"github.com/flynn/noise"
)
func ComposeReceiverHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error) {
log.Debug("Starting ComposeReceiverHandshakeMessage")
if len(rs) != 0 && len(rs) != noise.DH25519.DHLen() {
log.WithField("rs_length", len(rs)).Error("Invalid remote static key length")
return nil, nil, nil, errors.New("only 32 byte curve25519 public keys are supported")
}
negData = make([]byte, 6)
copy(negData, initNegotiationData(nil))
pattern := noise.HandshakeXK
negData[5] = NOISE_PATTERN_XK
log.WithField("pattern", "XK").Debug("Noise pattern set")
var random io.Reader
if len(ePrivate) == 0 {
random = rand.Reader
log.Debug("Using crypto/rand as random source")
} else {
random = bytes.NewBuffer(ePrivate)
log.Debug("Using provided ePrivate as random source")
}
prologue := make([]byte, 2, uint16Size+len(negData))
binary.BigEndian.PutUint16(prologue, uint16(len(negData)))
prologue = append(prologue, negData...)
log.WithField("prologue_length", len(prologue)).Debug("Prologue created")
// prologue = append(initString, prologue...)
state, err = noise.NewHandshakeState(noise.Config{
StaticKeypair: s,
Initiator: false,
Pattern: pattern,
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256),
PeerStatic: rs,
Prologue: prologue,
Random: random,
})
if err != nil {
log.WithError(err).Error("Failed to create new handshake state")
return
}
log.WithField("message_length", len(msg)).Debug("Handshake message composed successfully")
// log.Debug("Handshake state created successfully")
padBuf := make([]byte, 2+len(payload))
copy(padBuf[2:], payload)
msg, _, _, err = state.WriteMessage(msg, padBuf)
return
}
func (c *NoiseSession) RunIncomingHandshake() error {
log.Debug("Starting incoming handshake")
negData, msg, state, err := ComposeReceiverHandshakeMessage(c.HandKey, nil, nil, nil)
if err != nil {
log.WithError(err).Error("Failed to compose receiver handshake message")
return err
}
log.WithFields(logrus.Fields{
"negData_length": len(negData),
"msg_length": len(msg),
}).Debug("Receiver handshake message composed")
if _, err = c.Write(negData); err != nil {
log.WithError(err).Error("Failed to write negotiation data")
return err
}
log.Debug("Negotiation data written successfully")
if _, err = c.Write(msg); err != nil {
log.WithError(err).Error("Failed to write handshake message")
return err
}
log.Debug("Handshake message written successfully")
log.WithField("state", state).Debug("Handshake state after message write")
log.Println(state)
c.handshakeComplete = true
log.Debug("Incoming handshake completed successfully")
return nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,94 @@
package noise
import (
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"github.com/sirupsen/logrus"
"github.com/flynn/noise"
)
func ComposeInitiatorHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error) {
log.Debug("Starting ComposeInitiatorHandshakeMessage")
if len(rs) != 0 && len(rs) != noise.DH25519.DHLen() {
log.WithField("rs_length", len(rs)).Error("Invalid remote static key length")
return nil, nil, nil, errors.New("only 32 byte curve25519 public keys are supported")
}
negData = make([]byte, 6)
copy(negData, initNegotiationData(nil))
pattern := noise.HandshakeXK
negData[5] = NOISE_PATTERN_XK
log.WithField("pattern", "XK").Debug("Noise pattern set")
var random io.Reader
if len(ePrivate) == 0 {
random = rand.Reader
log.Debug("Using crypto/rand as random source")
} else {
random = bytes.NewBuffer(ePrivate)
log.Debug("Using provided ePrivate as random source")
}
prologue := make([]byte, 2, uint16Size+len(negData))
binary.BigEndian.PutUint16(prologue, uint16(len(negData)))
prologue = append(prologue, negData...)
log.WithField("prologue_length", len(prologue)).Debug("Prologue created")
// prologue = append(initString, prologue...)
state, err = noise.NewHandshakeState(noise.Config{
StaticKeypair: s,
Initiator: true,
Pattern: pattern,
CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256),
PeerStatic: rs,
Prologue: prologue,
Random: random,
})
if err != nil {
log.WithError(err).Error("Failed to create new handshake state")
return
}
log.Debug("Handshake state created successfully")
padBuf := make([]byte, 2+len(payload))
copy(padBuf[2:], payload)
msg, _, _, err = state.WriteMessage(msg, padBuf)
if err != nil {
log.WithError(err).Error("Failed to write handshake message")
return
}
log.WithField("message_length", len(msg)).Debug("Handshake message composed successfully")
return
}
func (c *NoiseSession) RunOutgoingHandshake() error {
log.Debug("Starting outgoing handshake")
negData, msg, state, err := ComposeInitiatorHandshakeMessage(c.HandKey, nil, nil, nil)
if err != nil {
log.WithError(err).Error("Failed to compose initiator handshake message")
return err
}
log.WithFields(logrus.Fields{
"negData_length": len(negData),
"msg_length": len(msg),
}).Debug("Initiator handshake message composed")
if _, err = c.Write(negData); err != nil {
log.WithError(err).Error("Failed to write negotiation data")
return err
}
log.Debug("Negotiation data written successfully")
if _, err = c.Write(msg); err != nil {
log.WithError(err).Error("Failed to write handshake message")
return err
}
log.Debug("Handshake message written successfully")
log.WithField("state", state).Debug("Handshake state after message write")
log.Println(state)
c.handshakeComplete = true
log.Debug("Outgoing handshake completed successfully")
return nil
}

View File

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

View File

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

View File

@ -0,0 +1 @@
package noise

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,8 +43,8 @@ type Transport interface {
// returns nil and an error on error
GetSession(routerInfo router_info.RouterInfo) (TransportSession, error)
// return true if a routerInfo is compatable with this transport
Compatable(routerInfo router_info.RouterInfo) bool
// return true if a routerInfo is compatible with this transport
Compatible(routerInfo router_info.RouterInfo) bool
// close the transport cleanly
// blocks until done

View File

@ -5,9 +5,12 @@ import (
"errors"
common "github.com/go-i2p/go-i2p/lib/common/data"
log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/sirupsen/logrus"
)
var log = logger.GetGoI2PLogger()
/*
I2P First Fragment Delivery Instructions
https://geti2p.net/spec/tunnel-message#struct-tunnelmessagedeliveryinstructions
@ -152,6 +155,7 @@ type DeliveryInstructions []byte
// Return if the DeliveryInstructions are of type FIRST_FRAGMENT or FOLLOW_ON_FRAGMENT.
func (delivery_instructions DeliveryInstructions) Type() (int, error) {
log.Debug("Determining DeliveryInstructions type")
if len(delivery_instructions) >= 1 {
/*
Check if the 7 bit of the Delivery Instructions
@ -169,18 +173,23 @@ func (delivery_instructions DeliveryInstructions) Type() (int, error) {
fragment or a complete fragment
*/
if (delivery_instructions[0] & 0x08) == 0x08 {
log.Debug("DeliveryInstructions type: FOLLOW_ON_FRAGMENT")
return FOLLOW_ON_FRAGMENT, nil
}
log.Debug("DeliveryInstructions type: FIRST_FRAGMENT")
return FIRST_FRAGMENT, nil
}
log.Error("DeliveryInstructions contains no data")
return 0, errors.New("DeliveryInstructions contains no data")
}
// Read the integer stored in the 6-1 bits of a FOLLOW_ON_FRAGMENT's flag, indicating
// the fragment number.
func (delivery_instructions DeliveryInstructions) FragmentNumber() (int, error) {
log.Debug("Getting FragmentNumber")
di_type, err := delivery_instructions.Type()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryInstructions type")
return 0, err
}
/*
@ -193,18 +202,26 @@ func (delivery_instructions DeliveryInstructions) FragmentNumber() (int, error)
0??????0 >> 1 => Integer(??????)
*/
if di_type == FOLLOW_ON_FRAGMENT {
/*
return common.Integer(
[]byte{((delivery_instructions[0] & 0x7e) >> 1)},
).Int(), nil
*/
fragNum := common.Integer([]byte{((delivery_instructions[0] & 0x7e) >> 1)}).Int()
log.WithField("fragment_number", fragNum).Debug("FragmentNumber retrieved")
return fragNum, nil
}
log.Error("Fragment Number only exists on FOLLOW_ON_FRAGMENT Delivery Instructions")
return 0, errors.New("Fragment Number only exists on FOLLOW_ON_FRAGMENT Delivery Instructions")
}
// Read the value of the 0 bit of a FOLLOW_ON_FRAGMENT, which is set to 1 to indicate the
// last fragment.
func (delivery_instructions DeliveryInstructions) LastFollowOnFragment() (bool, error) {
log.Debug("Checking if this is the LastFollowOnFragment")
di_type, err := delivery_instructions.Type()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryInstructions type")
return false, err
}
/*
@ -217,18 +234,25 @@ func (delivery_instructions DeliveryInstructions) LastFollowOnFragment() (bool,
0000000? => n
*/
if di_type == FOLLOW_ON_FRAGMENT {
/*
if delivery_instructions[0]&0x01 == 0x01 {
return true, nil
} else {
return false, nil
}
*/
isLast := delivery_instructions[0]&0x01 == 0x01
log.WithField("is_last", isLast).Debug("LastFollowOnFragment status determined")
return isLast, nil
}
log.Error("Last Fragment only exists for FOLLOW_ON_FRAGMENT Delivery Instructions")
return false, errors.New("Last Fragment only exists for FOLLOW_ON_FRAGMENT Delivery Instructions")
}
// Return the delivery type for these DeliveryInstructions, can be of type
// DT_LOCAL, DT_TUNNEL, DT_ROUTER, or DT_UNUSED.
func (delivery_instructions DeliveryInstructions) DeliveryType() (byte, error) {
log.Debug("Getting DeliveryType")
if len(delivery_instructions) >= 1 {
/*
Check if the 6-5 bits of the Delivery Instructions
@ -240,13 +264,18 @@ func (delivery_instructions DeliveryInstructions) DeliveryType() (byte, error) {
---------
000?0000 >> 4 => n (DT_* consts)
*/
return ((delivery_instructions[0] & 0x30) >> 4), nil
//return ((delivery_instructions[0] & 0x30) >> 4), nil
deliveryType := (delivery_instructions[0] & 0x30) >> 4
log.WithField("delivery_type", deliveryType).Debug("DeliveryType retrieved")
return deliveryType, nil
}
log.Error("DeliveryInstructions contains no data")
return 0, errors.New("DeliveryInstructions contains no data")
}
// Check if the delay bit is set. This feature in unimplemented in the Java router.
func (delivery_instructions DeliveryInstructions) HasDelay() (bool, error) {
log.Debug("Checking if DeliveryInstructions has delay")
if len(delivery_instructions) >= 1 {
/*
Check if the 4 bit of the Delivery Instructions
@ -266,19 +295,22 @@ func (delivery_instructions DeliveryInstructions) HasDelay() (bool, error) {
*/
delay := (delivery_instructions[0] & 0x10) == 0x10
if delay {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(DeliveryInstructions) HasDelay",
"info": "this feature is unimplemented in the Java router",
}).Warn("DeliveryInstructions found with delay bit set")
}
log.WithField("has_delay", delay).Debug("HasDelay status determined")
return delay, nil
}
log.Error("DeliveryInstructions contains no data")
return false, errors.New("DeliveryInstructions contains no data")
}
// Returns true if the Delivery Instructions are fragmented or false
// if the following data contains the entire message
func (delivery_instructions DeliveryInstructions) Fragmented() (bool, error) {
log.Debug("Checking if DeliveryInstructions is fragmented")
if len(delivery_instructions) >= 1 {
/*
Check if the 3 bit of the Delivery Instructions
@ -295,13 +327,18 @@ func (delivery_instructions DeliveryInstructions) Fragmented() (bool, error) {
message is message is not
fragmented fragmented
*/
return ((delivery_instructions[0] & 0x08) == 0x08), nil
fragmented := (delivery_instructions[0] & 0x08) == 0x08
log.WithField("fragmented", fragmented).Debug("Fragmented status determined")
return fragmented, nil
// return ((delivery_instructions[0] & 0x08) == 0x08), nil
}
log.Error("DeliveryInstructions contains no data")
return false, errors.New("DeliveryInstructions contains no data")
}
// Check if the extended options bit is set. This feature in unimplemented in the Java router.
func (delivery_instructions DeliveryInstructions) HasExtendedOptions() (bool, error) {
log.Debug("Checking if DeliveryInstructions has extended options")
if len(delivery_instructions) >= 1 {
/*
Check if the 2 bit of the Delivery Instructions
@ -322,28 +359,37 @@ func (delivery_instructions DeliveryInstructions) HasExtendedOptions() (bool, er
*/
extended_options := (delivery_instructions[0] & 0x04) == 0x04
if extended_options {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(DeliveryInstructions) ExtendedOptions",
"info": "this feature is unimplemented in the Java router",
}).Warn("DeliveryInstructions found with extended_options bit set")
}
log.WithField("has_extended_options", extended_options).Debug("HasExtendedOptions status determined")
return extended_options, nil
}
log.Error("DeliveryInstructions contains no data")
return false, errors.New("DeliveryInstructions contains no data")
}
// Check if the DeliveryInstructions is of type DT_TUNNEL.
func (delivery_instructions DeliveryInstructions) HasTunnelID() (bool, error) {
log.Debug("Checking if DeliveryInstructions has TunnelID")
di_type, err := delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return false, err
}
return di_type == DT_TUNNEL, nil
// return di_type == DT_TUNNEL, nil
hasTunnelID := di_type == DT_TUNNEL
log.WithField("has_tunnel_id", hasTunnelID).Debug("HasTunnelID status determined")
return hasTunnelID, nil
}
func (delivery_instructions DeliveryInstructions) HasHash() (bool, error) {
log.Debug("Checking if DeliveryInstructions has Hash")
di_type, err := delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return false, err
}
if di_type == DT_TUNNEL || di_type == DT_ROUTER {
@ -352,28 +398,37 @@ func (delivery_instructions DeliveryInstructions) HasHash() (bool, error) {
min_size += TUNNEL_ID_SIZE
}
if len(delivery_instructions) < min_size {
log.Error("Delivery Instructions indicates hash present but has too little data")
return false, errors.New("Delivery Instructions indicates hash present but has too little data")
}
log.Debug("DeliveryInstructions has Hash")
} else {
log.Debug("DeliveryInstructions does not have Hash")
return false, nil
}
log.Debug("DeliveryInstructions does not have Hash(?)")
return true, nil
}
// Return the tunnel ID in this DeliveryInstructions or 0 and an error if the
// DeliveryInstructions are not of type DT_TUNNEL.
func (delivery_instructions DeliveryInstructions) TunnelID() (tunnel_id uint32, err error) {
log.Debug("Getting TunnelID")
has_tunnel_id, err := delivery_instructions.HasTunnelID()
if err != nil {
log.WithError(err).Error("Failed to check HasTunnelID")
return
}
if has_tunnel_id {
if len(delivery_instructions) >= FLAG_SIZE+TUNNEL_ID_SIZE {
tunnel_id = binary.BigEndian.Uint32(delivery_instructions[FLAG_SIZE:TUNNEL_ID_SIZE])
log.WithField("tunnel_id", tunnel_id).Debug("TunnelID retrieved")
} else {
log.Error("DeliveryInstructions are invalid, too little data for Tunnel ID")
err = errors.New("DeliveryInstructions are invalid, too little data for Tunnel ID")
}
} else {
log.Error("DeliveryInstructions are not of type DT_TUNNEL")
err = errors.New("DeliveryInstructions are not of type DT_TUNNEL")
}
return
@ -384,8 +439,10 @@ func (delivery_instructions DeliveryInstructions) TunnelID() (tunnel_id uint32,
// If the type is DT_TUNNEL, hash is the SHA256 of the gateway router, if
// the type is DT_ROUTER it is the SHA256 of the router.
func (delivery_instructions DeliveryInstructions) Hash() (hash common.Hash, err error) {
log.Debug("Getting Hash")
delivery_type, err := delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return
}
hash_start := FLAG_SIZE
@ -395,16 +452,21 @@ func (delivery_instructions DeliveryInstructions) Hash() (hash common.Hash, err
hash_end := hash_end + TUNNEL_ID_SIZE
if len(delivery_instructions) >= hash_end {
copy(hash[:], delivery_instructions[hash_start:hash_end])
log.WithField("hash", hash).Debug("Hash retrieved for DT_TUNNEL")
} else {
log.Error("DeliveryInstructions is invalid, not contain enough data for hash given type DT_TUNNEL")
err = errors.New("DeliveryInstructions is invalid, not contain enough data for hash given type DT_TUNNEL")
}
} else if delivery_type == DT_ROUTER {
if len(delivery_instructions) >= hash_end {
copy(hash[:], delivery_instructions[hash_start:hash_end])
log.WithField("hash", hash).Debug("Hash retrieved for DT_ROUTER")
} else {
log.Error("DeliveryInstructions is invalid, not contain enough data for hash given type DT_ROUTER")
err = errors.New("DeliveryInstructions is invalid, not contain enough data for hash given type DT_ROUTER")
}
} else {
log.Error("No Hash on DeliveryInstructions not of type DT_TUNNEL or DT_ROUTER")
err = errors.New("No Hash on DeliveryInstructions not of type DT_TUNNEL or DT_ROUTER")
}
return
@ -412,32 +474,39 @@ func (delivery_instructions DeliveryInstructions) Hash() (hash common.Hash, err
// Return the DelayFactor if present and any errors encountered parsing the DeliveryInstructions.
func (delivery_instructions DeliveryInstructions) Delay() (delay_factor DelayFactor, err error) {
log.Debug("Getting Delay")
delay, err := delivery_instructions.HasDelay()
if err != nil {
log.WithError(err).Error("Failed to check HasDelay")
return
}
if delay {
var di_type byte
di_type, err = delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return
}
if di_type == DT_TUNNEL {
if len(delivery_instructions) >= FLAG_SIZE+TUNNEL_ID_SIZE+HASH_SIZE {
delay_factor = DelayFactor(delivery_instructions[FLAG_SIZE+TUNNEL_ID_SIZE+HASH_SIZE])
log.WithField("delay_factor", delay_factor).Debug("Delay factor retrieved for DT_TUNNEL")
} else {
log.Error("DeliveryInstructions is invalid, does not contain enough data for DelayFactor")
err = errors.New("DeliveryInstructions is invalid, does not contain enough data for DelayFactor")
return
}
} else if di_type == DT_ROUTER {
if len(delivery_instructions) >= FLAG_SIZE+HASH_SIZE {
delay_factor = DelayFactor(delivery_instructions[FLAG_SIZE+HASH_SIZE])
log.WithField("delay_factor", delay_factor).Debug("Delay factor retrieved for DT_ROUTER")
} else {
log.Error("DeliveryInstructions is invalid, does not contain enough data for DelayFactor")
err = errors.New("DeliveryInstructions is invalid, does not contain enough data for DelayFactor")
return
}
} else {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(DeliveryInstructions) Delay",
}).Warn("Delay not present on DeliveryInstructions not of type DT_TUNNEL or DT_ROUTER")
}
@ -448,28 +517,36 @@ func (delivery_instructions DeliveryInstructions) Delay() (delay_factor DelayFac
// Return the I2NP Message ID or 0 and an error if the data is not available for this
// DeliveryInstructions.
func (delivery_instructions DeliveryInstructions) MessageID() (msgid uint32, err error) {
log.Debug("Getting MessageID")
di_type, err := delivery_instructions.Type()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryInstructions type")
return
}
if di_type == FOLLOW_ON_FRAGMENT {
if len(delivery_instructions) >= 5 {
msgid = binary.BigEndian.Uint32(delivery_instructions[1:5])
log.WithField("message_id", msgid).Debug("MessageID retrieved for FOLLOW_ON_FRAGMENT")
} else {
log.Error("DeliveryInstructions are invalid, not enough data for Message ID")
err = errors.New("DeliveryInstructions are invalid, not enough data for Message ID")
}
} else if di_type == FIRST_FRAGMENT {
var message_id_index int
message_id_index, err = delivery_instructions.message_id_index()
if err != nil {
log.WithError(err).Error("Failed to get message_id_index")
return
}
if len(delivery_instructions) >= message_id_index+4 {
msgid = binary.BigEndian.Uint32(delivery_instructions[message_id_index : message_id_index+4])
log.WithField("message_id", msgid).Debug("MessageID retrieved for FIRST_FRAGMENT")
} else {
log.Error("DeliveryInstructions are invalid, not enough data for Message ID")
err = errors.New("DeliveryInstructions are invalid, not enough data for Message ID")
}
} else {
log.Error("No Message ID for DeliveryInstructions not of type FIRST_FRAGMENT or FOLLOW_ON_FRAGMENT")
err = errors.New("No Message ID for DeliveryInstructions not of type FIRST_FRAGMENT or FOLLOW_ON_FRAGMENT")
}
return
@ -478,31 +555,38 @@ func (delivery_instructions DeliveryInstructions) MessageID() (msgid uint32, err
// Return the Extended Options data if present, or an error if not present. Extended Options in unimplemented
// in the Java router and the presence of extended options will generate a warning.
func (delivery_instructions DeliveryInstructions) ExtendedOptions() (data []byte, err error) {
log.Debug("Getting ExtendedOptions")
ops, err := delivery_instructions.HasExtendedOptions()
if err != nil {
log.WithError(err).Error("Failed to check HasExtendedOptions")
return
}
if ops {
var extended_options_index int
extended_options_index, err = delivery_instructions.extended_options_index()
if err != nil {
log.WithError(err).Error("Failed to get extended_options_index")
return
}
if len(delivery_instructions) < extended_options_index+2 {
log.Error("DeliveryInstructions are invalid, length is shorter than required for Extended Options")
err = errors.New("DeliveryInstructions are invalid, length is shorter than required for Extended Options")
return
} else {
extended_options_size := common.Integer([]byte{delivery_instructions[extended_options_index]})
if len(delivery_instructions) < extended_options_index+1+extended_options_size.Int() {
log.Error("DeliveryInstructions are invalid, length is shorter than specified in Extended Options")
err = errors.New("DeliveryInstructions are invalid, length is shorter than specified in Extended Options")
return
} else {
data = delivery_instructions[extended_options_index+1 : extended_options_size.Int()]
log.WithField("extended_options_length", len(data)).Debug("Extended Options retrieved")
return
}
}
} else {
log.Error("DeliveryInstruction does not have the ExtendedOptions flag set")
err = errors.New("DeliveryInstruction does not have the ExtendedOptions flag set")
}
return
@ -510,28 +594,36 @@ func (delivery_instructions DeliveryInstructions) ExtendedOptions() (data []byte
// Return the size of the associated I2NP fragment and an error if the data is unavailable.
func (delivery_instructions DeliveryInstructions) FragmentSize() (frag_size uint16, err error) {
log.Debug("Getting FragmentSize")
di_type, err := delivery_instructions.Type()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryInstructions type")
return
}
if di_type == FOLLOW_ON_FRAGMENT {
if len(delivery_instructions) >= 7 {
frag_size = binary.BigEndian.Uint16(delivery_instructions[5:7])
log.WithField("fragment_size", frag_size).Debug("FragmentSize retrieved for FOLLOW_ON_FRAGMENT")
} else {
log.Error("DeliveryInstructions are invalid, not enough data for Fragment Size")
err = errors.New("DeliveryInstructions are invalid, not enough data for Fragment Size")
}
} else if di_type == FIRST_FRAGMENT {
var fragment_size_index int
fragment_size_index, err = delivery_instructions.fragment_size_index()
if err != nil {
log.WithError(err).Error("Failed to get fragment_size_index")
return
}
if len(delivery_instructions) >= fragment_size_index+2 {
frag_size = binary.BigEndian.Uint16(delivery_instructions[fragment_size_index : fragment_size_index+2])
log.WithField("fragment_size", frag_size).Debug("FragmentSize retrieved for FIRST_FRAGMENT")
} else {
log.Error("DeliveryInstructions are invalid, not enough data for Fragment Size")
err = errors.New("DeliveryInstructions are invalid, not enough data for Fragment Size")
}
} else {
log.Error("No Fragment Size for DeliveryInstructions not of type FIRST_FRAGMENT or FOLLOW_ON_FRAGMENT")
err = errors.New("No Fragment Size for DeliveryInstructions not of type FIRST_FRAGMENT or FOLLOW_ON_FRAGMENT")
}
return
@ -539,8 +631,10 @@ func (delivery_instructions DeliveryInstructions) FragmentSize() (frag_size uint
// Find the correct index for the Message ID in a FIRST_FRAGMENT DeliveryInstructions
func (delivery_instructions DeliveryInstructions) message_id_index() (message_id int, err error) {
log.Debug("Calculating message_id_index")
fragmented, err := delivery_instructions.Fragmented()
if err != nil {
log.WithError(err).Error("Failed to check if DeliveryInstructions are fragmented")
return
}
if fragmented {
@ -551,6 +645,7 @@ func (delivery_instructions DeliveryInstructions) message_id_index() (message_id
var di_type byte
di_type, err = delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return
}
if di_type == DT_TUNNEL {
@ -563,22 +658,26 @@ func (delivery_instructions DeliveryInstructions) message_id_index() (message_id
var delay bool
delay, err = delivery_instructions.HasDelay()
if err != nil {
log.WithError(err).Error("Failed to check HasDelay")
return
}
if delay {
message_id++
}
log.WithField("message_id_index", message_id).Debug("message_id_index calculated")
return message_id, nil
} else {
log.Error("DeliveryInstruction must be fragmented to have a Message ID")
return 0, errors.New("DeliveryInstruction must be fragmented to have a Message ID")
}
}
// Find the index of the extended options in this Delivery Instruction, if they exist.
func (delivery_instructions DeliveryInstructions) extended_options_index() (extended_options int, err error) {
log.Debug("Calculating extended_options_index")
ops, err := delivery_instructions.HasExtendedOptions()
if err != nil {
log.WithError(err).Error("Failed to check HasExtendedOptions")
return
}
if ops {
@ -589,6 +688,7 @@ func (delivery_instructions DeliveryInstructions) extended_options_index() (exte
var di_type byte
di_type, err = delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return
}
if di_type == DT_TUNNEL {
@ -601,6 +701,7 @@ func (delivery_instructions DeliveryInstructions) extended_options_index() (exte
var delay bool
delay, err = delivery_instructions.HasDelay()
if err != nil {
log.WithError(err).Error("Failed to check HasDelay")
return
}
if delay {
@ -613,9 +714,11 @@ func (delivery_instructions DeliveryInstructions) extended_options_index() (exte
} else {
err = nil
}
log.WithField("extended_options_index", extended_options).Debug("extended_options_index calculated")
return extended_options, nil
} else {
log.Error("DeliveryInstruction does not have the ExtendedOptions flag set")
err = errors.New("DeliveryInstruction does not have the ExtendedOptions flag set")
}
return
@ -623,6 +726,7 @@ func (delivery_instructions DeliveryInstructions) extended_options_index() (exte
// Find the index of the Fragment Size data in this Delivery Instruction.
func (delivery_instructions DeliveryInstructions) fragment_size_index() (fragment_size int, err error) {
log.Debug("Calculating fragment_size_index")
// Start counting after the flags
fragment_size = 1
@ -630,6 +734,7 @@ func (delivery_instructions DeliveryInstructions) fragment_size_index() (fragmen
var di_type byte
di_type, err = delivery_instructions.DeliveryType()
if err != nil {
log.WithError(err).Error("Failed to get DeliveryType")
return
}
if di_type == DT_TUNNEL {
@ -642,6 +747,7 @@ func (delivery_instructions DeliveryInstructions) fragment_size_index() (fragmen
var delay bool
delay, err = delivery_instructions.HasDelay()
if err != nil {
log.WithError(err).Error("Failed to check HasDelay")
return
}
if delay {
@ -661,22 +767,29 @@ func (delivery_instructions DeliveryInstructions) fragment_size_index() (fragmen
fragment_size += len(extended_opts) + 1
}
}
log.WithField("fragment_size_index", fragment_size).Debug("fragment_size_index calculated")
return fragment_size, nil
}
func maybeAppendTunnelID(data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append TunnelID")
if has_tunnel_id, _ := DeliveryInstructions(data).HasTunnelID(); has_tunnel_id {
_, err = DeliveryInstructions(data).TunnelID()
if err == nil {
now = append(current, data[1:5]...)
log.Debug("TunnelID appended")
} else {
log.WithError(err).Error("Failed to get TunnelID")
return
}
} else {
log.Debug("No TunnelID to append")
}
return
}
func maybeAppendHash(di_flag DeliveryInstructions, data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append Hash")
delivery_type, _ := di_flag.DeliveryType()
if _, err := DeliveryInstructions(data).HasHash(); err == nil {
hash_start := 1
@ -687,12 +800,16 @@ func maybeAppendHash(di_flag DeliveryInstructions, data, current []byte) (now []
}
if err == nil {
now = append(current, data[hash_start:hash_end]...)
log.Debug("Hash appended")
}
} else {
log.Debug("No Hash to append")
}
return
}
func maybeAppendDelay(di_flag DeliveryInstructions, data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append Delay")
delivery_type, _ := di_flag.DeliveryType()
if _, err = DeliveryInstructions(data).HasHash(); err == nil {
delay_start := 1
@ -704,12 +821,16 @@ func maybeAppendDelay(di_flag DeliveryInstructions, data, current []byte) (now [
}
if err == nil {
now = append(current, data[delay_start])
log.Debug("Delay appended")
}
} else {
log.Debug("No Delay to append")
}
return
}
func maybeAppendMessageID(di_flag DeliveryInstructions, di_type int, data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append MessageID")
if di_type == FIRST_FRAGMENT {
if fragmented, _ := di_flag.Fragmented(); fragmented {
message_id_index := 1
@ -723,47 +844,61 @@ func maybeAppendMessageID(di_flag DeliveryInstructions, di_type int, data, curre
message_id_index += 1
}
if len(data) < message_id_index+4 {
log.Error("Data is too short to contain message ID in FIRST_FRAGMENT")
err = errors.New("data is too short to contain message ID in FIRST_FRAGMENT")
} else {
now = append(current, data[message_id_index:message_id_index+4]...)
log.Debug("MessageID appended for FIRST_FRAGMENT")
}
}
} else if di_type == FOLLOW_ON_FRAGMENT {
if len(data) < 5 {
log.Error("Data is too short to contain message ID in FOLLOW_ON_FRAGMENT")
err = errors.New("data is too short to contain message ID in FOLLOW_ON_FRAGMENT")
} else {
now = append(current, data[1:5]...)
log.Debug("MessageID appended for FOLLOW_ON_FRAGMENT")
}
}
return
}
func maybeAppendExtendedOptions(di_flag DeliveryInstructions, data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append ExtendedOptions")
if index, err := DeliveryInstructions(data).extended_options_index(); err != nil {
extended_options_length := common.Integer([]byte{data[index]})
now = append(current, data[index:index+extended_options_length.Int()]...)
log.WithField("extended_options_length", extended_options_length.Int()).Debug("ExtendedOptions appended")
} else {
log.Debug("No ExtendedOptions to append")
}
return
}
func maybeAppendSize(di_flag DeliveryInstructions, di_type int, data, current []byte) (now []byte, err error) {
log.Debug("Attempting to append Size")
if di_type == FIRST_FRAGMENT {
if index, err := DeliveryInstructions(data).extended_options_index(); err != nil {
extended_options_length := common.Integer([]byte{data[index]})
now = append(current, data[index+extended_options_length.Int():index+extended_options_length.Int()+2]...)
log.Debug("Size appended for FIRST_FRAGMENT")
}
} else if di_type == FOLLOW_ON_FRAGMENT {
if len(data) < 7 {
log.Error("Data is too short to contain size data")
err = errors.New("data is too short to contain size data")
} else {
now = append(now, data[5:7]...)
log.Debug("Size appended for FOLLOW_ON_FRAGMENT")
}
}
return
}
func readDeliveryInstructions(data []byte) (instructions DeliveryInstructions, remainder []byte, err error) {
log.Debug("Reading DeliveryInstructions")
if len(data) < 1 {
log.Error("No data provided")
err = errors.New("no data provided")
return
}
@ -775,42 +910,57 @@ func readDeliveryInstructions(data []byte) (instructions DeliveryInstructions, r
di_data = append(di_data, data[0])
if di_type == FIRST_FRAGMENT {
log.Debug("Processing FIRST_FRAGMENT")
di_data, err = maybeAppendTunnelID(data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append TunnelID")
return
}
di_data, err = maybeAppendHash(di_flag, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append Hash")
return
}
di_data, err = maybeAppendDelay(di_flag, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append Delay")
return
}
di_data, err = maybeAppendMessageID(di_flag, di_type, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append MessageID")
return
}
di_data, err = maybeAppendExtendedOptions(di_flag, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append ExtendedOptions")
return
}
di_data, err = maybeAppendSize(di_flag, di_type, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append Size")
return
}
} else if di_type == FOLLOW_ON_FRAGMENT {
di_data, err = maybeAppendMessageID(di_flag, di_type, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append MessageID")
return
}
di_data, err = maybeAppendSize(di_flag, di_type, data, di_data)
if err != nil {
log.WithError(err).Error("Failed to append Size")
return
}
}
remainder = data[len(di_data):]
instructions = DeliveryInstructions(di_data)
log.WithFields(logrus.Fields{
"instructions_length": len(instructions),
"remainder_length": len(remainder),
}).Debug("Successfully read DeliveryInstructions")
return
}

View File

@ -3,8 +3,9 @@ package tunnel
import (
"encoding/binary"
"github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/crypto"
log "github.com/sirupsen/logrus"
)
/*
@ -157,12 +158,17 @@ func (decrypted_tunnel_message DecryptedTunnelMessage) Checksum() crypto.TunnelI
// Returns the contents of a decrypted tunnel message that contain the data for the
// DeliveryInstructions.
func (decrypted_tunnel_message DecryptedTunnelMessage) deliveryInstructionData() []byte {
log.Debug("Retrieving delivery instruction data from DecryptedTunnelMessage")
data_area := decrypted_tunnel_message[4+4+16:]
for i := 0; i < len(data_area); i++ {
if data_area[i] == 0x00 {
return data_area[i+1:]
// return data_area[i+1:]
delivery_data := data_area[i+1:]
log.WithField("delivery_data_length", len(delivery_data)).Debug("Retrieved delivery instruction data")
return delivery_data
}
}
log.Warn("No delivery instruction data found in DecryptedTunnelMessage")
return []byte{}
}
@ -174,7 +180,7 @@ func (decrypted_tunnel_message DecryptedTunnelMessage) DeliveryInstructionsWithF
for {
instructions, remainder, err := readDeliveryInstructions(data)
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(DecryptedTunnelMessage) DeliveryInstructionsWithFragments",
"err": err.Error(),
}).Error("error reading delivery instructions")
@ -183,7 +189,7 @@ func (decrypted_tunnel_message DecryptedTunnelMessage) DeliveryInstructionsWithF
fragment_size, err := instructions.FragmentSize()
if err != nil {
log.WithFields(log.Fields{
log.WithFields(logrus.Fields{
"at": "(DecryptedTunnelMessage) DeliveryInstructionsWithFragments",
"err": err.Error(),
}).Error("error getting delivery instructions fragment size")

51
lib/util/logger/log.go Normal file
View File

@ -0,0 +1,51 @@
package logger
import (
"io/ioutil"
"os"
"strings"
"sync"
"github.com/sirupsen/logrus"
)
var (
log *logrus.Logger
once sync.Once
)
func InitializeGoI2PLogger() {
once.Do(func() {
log = logrus.New()
// We do not want to log by default
log.SetOutput(ioutil.Discard)
log.SetLevel(logrus.PanicLevel)
// Check if DEBUG_I2P is set
if logLevel := os.Getenv("DEBUG_I2P"); logLevel != "" {
log.SetOutput(os.Stdout)
switch strings.ToLower(logLevel) {
case "debug":
log.SetLevel(logrus.DebugLevel)
case "warn":
log.SetLevel(logrus.WarnLevel)
case "error":
log.SetLevel(logrus.ErrorLevel)
default:
log.SetLevel(logrus.DebugLevel)
}
log.WithField("level", log.GetLevel()).Debug("Logging enabled.")
}
})
}
// GetGoI2PLogger returns the initialized logger
func GetGoI2PLogger() *logrus.Logger {
if log == nil {
InitializeGoI2PLogger()
}
return log
}
func init() {
InitializeGoI2PLogger()
}

137
main.go
View File

@ -1,22 +1,147 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/go-i2p/go-i2p/lib/config"
"github.com/go-i2p/go-i2p/lib/router"
"github.com/go-i2p/go-i2p/lib/util/logger"
"github.com/go-i2p/go-i2p/lib/util/signals"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
var (
routerInstance *router.Router
log = logger.GetGoI2PLogger()
)
var RootCmd = &cobra.Command{
Use: "go-i2p",
Short: "I2P Router implementation in Go",
Run: func(cmd *cobra.Command, args []string) {
runRouter()
},
}
func init() {
cobra.OnInitialize(config.InitConfig)
// Global flags
RootCmd.PersistentFlags().StringVar(&config.CfgFile, "config", "", "config file (default is $HOME/.go-i2p/config.yaml)")
// Router configuration flags
RootCmd.PersistentFlags().String("base-dir", config.DefaultRouterConfig().BaseDir, "Base directory for I2P router")
RootCmd.PersistentFlags().String("working-dir", config.DefaultRouterConfig().WorkingDir, "Working directory for I2P router")
// NetDb flags
RootCmd.PersistentFlags().String("netdb.path", config.DefaultNetDbConfig.Path, "Path to the netDb")
// Bootstrap flags
RootCmd.PersistentFlags().Int("bootstrap.low-peer-threshold", config.DefaultBootstrapConfig.LowPeerThreshold,
"Minimum number of peers before reseeding")
// Bind flags to viper
viper.BindPFlag("base_dir", RootCmd.PersistentFlags().Lookup("base-dir"))
viper.BindPFlag("working_dir", RootCmd.PersistentFlags().Lookup("working-dir"))
viper.BindPFlag("netdb.path", RootCmd.PersistentFlags().Lookup("netdb.path"))
viper.BindPFlag("bootstrap.low_peer_threshold", RootCmd.PersistentFlags().Lookup("bootstrap.low-peer-threshold"))
}
// configCmd shows current configuration
var configCmd = &cobra.Command{
Use: "config",
Short: "Show current configuration",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Configuration file: %s\n", viper.ConfigFileUsed())
fmt.Printf("\nRouter Configuration:")
fmt.Printf(" Base Directory: %s", config.RouterConfigProperties.BaseDir)
fmt.Printf(" Working Directory: %s", config.RouterConfigProperties.WorkingDir)
fmt.Printf("\nNetDb Configuration:")
fmt.Printf(" Path: %s", config.RouterConfigProperties.NetDb.Path)
fmt.Printf("\nBootstrap Configuration:")
fmt.Printf(" Low Peer Threshold: %d", config.RouterConfigProperties.Bootstrap.LowPeerThreshold)
fmt.Printf(" Reseed Servers:")
for _, server := range config.RouterConfigProperties.Bootstrap.ReseedServers {
fmt.Printf(" - URL: %s", server.Url)
fmt.Printf(" SU3 Fingerprint: %s", server.SU3Fingerprint)
}
},
}
func debugPrintConfig() {
currentConfig := struct {
BaseDir string `yaml:"base_dir"`
WorkingDir string `yaml:"working_dir"`
NetDB config.NetDbConfig `yaml:"netdb"`
Bootstrap config.BootstrapConfig `yaml:"bootstrap"`
}{
BaseDir: config.RouterConfigProperties.BaseDir,
WorkingDir: config.RouterConfigProperties.WorkingDir,
NetDB: *config.RouterConfigProperties.NetDb,
Bootstrap: *config.RouterConfigProperties.Bootstrap,
}
yamlData, err := yaml.Marshal(currentConfig)
if err != nil {
log.Errorf("Error marshaling config for debug: %s", err)
return
}
log.Debugf("Current configuration:\n%s", string(yamlData))
}
func runRouter() {
go signals.Handle()
log.Debug("parsing i2p router configuration")
log.Debug("using netDb in:", config.RouterConfigProperties.NetDb.Path)
log.Debug("starting up i2p router")
var err error
routerInstance, err = router.CreateRouter(config.RouterConfigProperties)
if err == nil {
signals.RegisterReloadHandler(func() {
if err := viper.ReadInConfig(); err != nil {
log.Errorf("failed to reload config: %s", err)
return
}
config.UpdateRouterConfig()
})
signals.RegisterInterruptHandler(func() {
if routerInstance != nil {
routerInstance.Stop()
}
})
routerInstance.Start()
routerInstance.Wait()
routerInstance.Close()
} else {
log.Errorf("failed to create i2p router: %s", err)
}
}
func main() {
RootCmd.AddCommand(configCmd)
if err := RootCmd.Execute(); err != nil {
log.Error(err)
debugPrintConfig()
os.Exit(1)
}
/*
netDbPath := flag.String("netDb", config.DefaultNetDbConfig.Path, "Path to the netDb")
flag.Parse()
config.RouterConfigProperties.NetDb.Path = *netDbPath
go signals.Handle()
log.Info("parsing i2p router configuration")
log.Info("using netDb in:", config.RouterConfigProperties.NetDb.Path)
log.Info("starting up i2p router")
log.Debug("parsing i2p router configuration")
log.Debug("using netDb in:", config.RouterConfigProperties.NetDb.Path)
log.Debug("starting up i2p router")
r, err := router.CreateRouter()
if err == nil {
signals.RegisterReloadHandler(func() {
@ -32,4 +157,6 @@ func main() {
} else {
log.Errorf("failed to create i2p router: %s", err)
}
*/
}