Field Summary
FieldsModifier and TypeFieldDescriptionprivate final LinkedBlockingQueue<Cipher> Initialized ciphers ready for reuse.private final ConcurrentMap<SessionKey, SecretKeySpec> Lock-free read side of the_keySpecsmemo.private final Map<SessionKey, SecretKeySpec> MemoizedSecretKeySpecperSessionKeyfor the system-AES path.private static final booleanWhether initialized ciphers are pooled.private static final intCiphers held for reuse.(package private) static final intUpper bound on the per-sessionSecretKeySpecmemo cache.private static final intSmallest message where the system AES is worth using, in bytes.private static final booleanTrue to use the JVM's AES when it is genuinely faster.Constructor Summary
ConstructorsConstructorDescriptionCryptixAESEngine(I2PAppContext context) Create the engine, allocating the cipher cache only when the system AES-NI provider is in use.Method Summary
Modifier and TypeMethodDescriptionprivate Cipheracquire()Obtain a Cipher from the cache, or create a new one if empty.voiddecrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int length) Decrypt the payload with the session key.voiddecrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int ivOffset, int length) Decrypt the payload with the session key.final voiddecryptBlock(byte[] payload, int inIndex, SessionKey sessionKey, byte[] rv, int outIndex) Decrypt exactly 16 bytes of data with the session key provided.voidencrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int length) Encrypt the payload with the session key.voidencrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int ivOffset, int length) Encrypt the payload with the session key.final voidencryptBlock(byte[] payload, int inIndex, SessionKey sessionKey, byte[] out, int outIndex) Encrypt exactly 16 bytes using the session key.(package private) SecretKeySpecgetKeySpec(SessionKey sessionKey) Get (or build and remember) theSecretKeySpecfor a session key.private static booleanhasAESNI()Check for AES support in processor and JVM.private voidReturn the Cipher to the cache without scrubbing it first.Methods inherited from class AESEngine
getPadding, getPaddingSize, safeDecrypt, safeEncryptModifier and TypeMethodDescriptionstatic final byte[]getPadding(I2PAppContext context, int curSize, long minPaddedSize) Return random bytes for padding the data to a mod 16 size so that it is at least minPaddedSizestatic final intgetPaddingSize(int curSize, long minPaddedSize) Return size for padding the data to a mod 16 size so that it is at least minPaddedSizebyte[]safeDecrypt(byte[] payload, SessionKey sessionKey, byte[] iv) SeeAESEngine.safeEncrypt(byte[], SessionKey, byte[], int)for description.byte[]safeEncrypt(byte[] payload, SessionKey sessionKey, byte[] iv, int paddedSize) Encrypt SHA-256 Hash of IV, 4 byte length, and payload, with random padding up to paddedSize, rounded up to next multiple of 16.
Field Details
_ciphers
Initialized ciphers ready for reuse.MIN_SYSTEM_AES_LENGTH
private static final int MIN_SYSTEM_AES_LENGTHSmallest message where the system AES is worth using, in bytes.- See Also:
USE_SYSTEM_AES
private static final boolean USE_SYSTEM_AESTrue to use the JVM's AES when it is genuinely faster.CACHE
private static final boolean CACHEWhether initialized ciphers are pooled.- See Also:
CACHE_SIZE
KEY_SPEC_CACHE_SIZE
static final int KEY_SPEC_CACHE_SIZEUpper bound on the per-sessionSecretKeySpecmemo cache. Plenty for the handful of concurrent transport and session keys the router works with at any moment. Package-visible for the unit tests.- See Also:
_keySpecs
MemoizedSecretKeySpecperSessionKeyfor the system-AES path.Why memoize at all: building the spec clones the key material, but
SecretKeySpec.getEncoded()returns the spec's *internal* array, so a reused instance passes the same byte[] reference toCipher.init(int, Key)and skips JCE makeSessionKey re-expansion of the 256-bit key on every JVM-path encrypt/decrypt.This cache is deliberately NOT the same as
SessionKey.getPreparedKey(), which is owned by the legacy Cryptix path and lives per SessionKey - keep the two fast paths decoupled.SessionKeyequality is value-based, so distinct-but-identical keys share one spec (their bytes are equal, so sharing is correct).DO NOT make this map access-ordered: with accessOrder=true,
get()counts as a structural modification, so every lookup would need a lock and that would serialize every AES op of at leastMIN_SYSTEM_AES_LENGTHbytes across the whole router. Insertion order keeps lookups lock-free and eviction drops the eldest, not the coldest._keySpecReads
Lock-free read side of the_keySpecsmemo. Written only inside thesynchronized(_keySpecs)miss path, so it is always a mirror of_keySpecs(plus, transiently, the entry being evicted). Bounded by the sameKEY_SPEC_CACHE_SIZE.
Constructor Details
CryptixAESEngine
Create the engine, allocating the cipher cache only when the system AES-NI provider is in use.- Parameters:
context- the app context supplying the provider and the RNG
Method Details
hasAESNI
private static boolean hasAESNI()Check for AES support in processor and JVM.On x86-64 this consults CPUID when libjcpuid is available; a missing helper library must not silently disable the JDK path, so the JDK's own implementation (AES-NI intrinsics where present, tables otherwise) is assumed instead - it is at least as good as the bundled Cryptix tables. On other 64-bit ABIs (notably AArch64, which has AES in the base ISA) the JDK path is used as well. 32-bit and non-mainstream JVMs keep the Cryptix tables.
- Returns:
- whether the JDK Cipher path should be used
encrypt
public void encrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int length) Encrypt the payload with the session key.- Overrides:
encryptin classAESEngine- Parameters:
payload- data to be encryptedpayloadIndex- index into the payload to start encryptingout- where to store the resultoutIndex- where in out to start writingsessionKey- private esession key to encrypt toiv- must be 16 byteslength- must be a multiple of 16
encrypt
public void encrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int ivOffset, int length) Encrypt the payload with the session key.- Overrides:
encryptin classAESEngine- Parameters:
payload- data to be encryptedpayloadIndex- index into payload to start encryptingout- where to store resultoutIndex- where in out to start writingsessionKey- private session key to encrypt toiv- must be 16 bytesivOffset- offset into IVlength- must be a multiple of 16
decrypt
public void decrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int length) Decrypt the payload with the session key.- Overrides:
decryptin classAESEngine- Parameters:
payload- data to be decryptedpayloadIndex- index into the payload to start decryptingout- where to store the cleartextoutIndex- where in out to start writingsessionKey- private session key to decrypt toiv- 16 byteslength- must be a multiple of 16 (will overrun to next mod 16 if not)
decrypt
public void decrypt(byte[] payload, int payloadIndex, byte[] out, int outIndex, SessionKey sessionKey, byte[] iv, int ivOffset, int length) Decrypt the payload with the session key.- Overrides:
decryptin classAESEngine- Parameters:
payload- data to be decryptedpayloadIndex- index into the payload to start decryptingout- where to store the cleartextoutIndex- where in out to start writingsessionKey- private session key to decrypt toiv- 16 bytes starting at ivOffsetivOffset- offset into IVlength- must be a multiple of 16 (will overrun to next mod 16 if not)
encryptBlock
public final void encryptBlock(byte[] payload, int inIndex, SessionKey sessionKey, byte[] out, int outIndex) Encrypt exactly 16 bytes using the session key.- Overrides:
encryptBlockin classAESEngine- Parameters:
payload- plaintext data, 16 bytes starting at inIndexinIndex- starting index in payloadsessionKey- private session keyout- out parameter, 16 bytes starting at outIndexoutIndex- starting index in output
decryptBlock
public final void decryptBlock(byte[] payload, int inIndex, SessionKey sessionKey, byte[] rv, int outIndex) Decrypt exactly 16 bytes of data with the session key provided.- Overrides:
decryptBlockin classAESEngine- Parameters:
payload- encrypted data, 16 bytes starting at inIndexinIndex- starting index in payloadsessionKey- private session keyrv- out parameter, 16 bytes starting at outIndexoutIndex- starting index in output
getKeySpec
Get (or build and remember) theSecretKeySpecfor a session key. Package-visible so the unit tests can verify reuse, longevity across sessions and the size bound.The hit path is lock-free: a
ConcurrentHashMaplookup only. The miss path takessynchronized(_keySpecs)- that is the only place theKEY_SPEC_CACHE_SIZEbound is enforced, by evicting the eldest entry in insertion order.- Parameters:
sessionKey- the session key (value-equality, may be a fresh object each call carrying the same bytes as a cached one)- Returns:
- a spec reusing the key's internal byte array; never null
acquire
Obtain a Cipher from the cache, or create a new one if empty.- Returns:
- cached or new
release
Return the Cipher to the cache without scrubbing it first.The pooled Cipher keeps the round keys of the operation that just finished. Re-initializing it with a zero key was dropped because
Cipher.init(int, Key)at the next acquire overwrites the schedule anyway, and the extra key schedule is pure overhead. It protected nothing either: the live key bytes are already resident for the life of theSessionKeyin the_keySpecReadsmemo and inSessionKey.getPreparedKey()on the legacy Cryptix path.- Parameters:
cipher- the Cipher to return to the cache