There are a handful of cryptographic algorithms in use within I2P, but we have reduced them to a bare minimum to deal with our needs - one symmetric algorithm one asymmetric algorithm, one signing algorithm, and one hashing algorithm. However, we do combine them in some particular ways to provide message integrity (rather than relying on a MAC). In addition, as much as we hate doing anything new in regards to cryptography, we can't seem to find a reference discussing (or even naming) the technique used in ElGamal/AES+SessionTag (but we're sure others have done it).
We use common primes for 2048 ElGamal encryption and decryption, and we currently only use ElGamal to encrypt the IV and session key in a single block, followed by the AES encrypted payload using that key and IV. Specifically, the unencrypted ElGamal block is formatted (in network byte order):
|_______1_______2_______3_______4_______5_______6_______7_______8 |nonzero|H(data) | | | | | data ... |
The H(data) is the SHA256 of the data that is encrypted in the ElGamal? block, and is preceeded by a random nonzero byte. The data encrypted in the block can be up to 222 bytes long. Specifically, see [the code].
ElGamal is never used on its own in I2P, but instead always as part of ElGamal/AES+SessionTag.
The shared prime is the [Oakley prime for 2048bit keys]
2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
Using 2 as the generator.
We use 256bit AES in CBC mode with PKCS#5 padding for 16 byte blocks (aka each block is end padded with the number of pad bytes). Speficially, see [the CBC code] and the Cryptix AES [implementation]
For situations where we stream AES data, we still use the same algorithm, as implemented in [AESOutputStream] [AESInputStream]
For situations where we know the size of the data to be sent, we AES encrypt the following:
|_______1_______2_______3_______4_______5_______6_______7_______8 |H(data)| size of data (in bytes) | data ... | rand |
After the data comes an application specified number of randomly generated padding bytes, and this entire segment (from H(data) through the end of the random bytes) is AES encrypted (256bit CBC w/ PKCS#5).
This code is implemented in the safeEncrypt and safeDecrypt methods of [AESEngine]
Signatures are generated and verified with 1024bit DSA, as implemented in [DSAEngine]
86108236b8526e296e923a4015b4282845b572cc
33
9C05B2AA 960D9B97 B8931963 C9CC9E8C 3026E9B8 ED92FAD0 A69CC886 D5BF8015 FCADAE31 A0AD18FA B3F01B00 A358DE23 7655C496 4AFAA2B3 37E96AD3 16B9FB1C C564B5AE C5B69A9F F6C3E454 8707FEF8 503D91DD 8602E867 E6D35D22 35C1869C E2479C3B 9D5401DE 04E0727F B33D6511 285D4CF2 9538D9E3 B6051F5B 22CC1C93
A5DFC28F EF4CA1E2 86744CD8 EED9D29D 684046B7
C1F4D27D 40093B42 9E962D72 23824E0B BC47E7C8 32A39236 FC683AF8 48895810 75FF9082 ED32353D 4374D730 1CDA1D23 C431F469 8599DDA0 2451824F F3697525 93647CC3 DDC197DE 985E43D1 36CDCFC6 BD5409CD 2F450821 142A5E6F 8EB1C3AB 5D0484B8 129FCF17 BCE4F7F3 3321C3CB 3DBB14A9 05E7B2B3 E93BE470 8CBCC82
Hashes within I2P are plain old SHA256, as implemented in [SHA256Generator]
TCP connections are currently negotiated with a 2048 Diffie-Hellman implementation, using the router's identity to proceed with a station to station agreement, followed by some encrypted protocol specific fields, with all subsequent data encrypted with AES (as above). Down the line, we will want to use session tags like we do with ElGamalAES+SessionTag to avoid the 2048bit DH negotiation.
We would like to migrate to a more standardized implementation (TLS/SSL or even SSH), but:
The basic TCP connection algorithm is implemented in establishConnection() (which calls exchangeKey() and identifyStationToStation()) in [TCPConnection]
However, this is extended by [RestrictiveTCPConnection] which updates the establishConnection() method to validate the protocol version, the time, and the peer's publicly reachable addresses (since we don't support restricted routes yet, we refuse to talk to those who other people can't talk to as well).