forked from I2P_Developers/i2p.i2p
Crypto: Prep for SSU2
- ChaCha20: Add ivOffset param - ChaCha20/Poly1305: Add adOffset/adLength params - Noise: Add XK-SSU2 initializer - Noise: Add notes about handshake offsets
This commit is contained in:
@ -47,6 +47,19 @@ public final class ChaCha20 {
|
||||
public static void encrypt(byte[] key, byte[] iv,
|
||||
byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) {
|
||||
encrypt(key, iv, 0, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt from plaintext to ciphertext
|
||||
*
|
||||
* @param key first 32 bytes used as the key
|
||||
* @param iv first 12 bytes starting at ivOffset used as the iv
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public static void encrypt(byte[] key, byte[] iv, int ivOffset,
|
||||
byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) {
|
||||
int[] input = new int[16];
|
||||
int[] output = new int[16];
|
||||
ChaChaCore.initKey256(input, key, 0);
|
||||
@ -61,9 +74,9 @@ public final class ChaCha20 {
|
||||
// bits.
|
||||
//ChaChaCore.initIV(input, iv, counter);
|
||||
//ChaChaCore.initIV(input, iv[4:11], iv[0:3]);
|
||||
input[13] = (int) DataHelper.fromLongLE(iv, 0, 4);
|
||||
input[14] = (int) DataHelper.fromLongLE(iv, 4, 4);
|
||||
input[15] = (int) DataHelper.fromLongLE(iv, 8, 4);
|
||||
input[13] = (int) DataHelper.fromLongLE(iv, ivOffset, 4);
|
||||
input[14] = (int) DataHelper.fromLongLE(iv, ivOffset + 4, 4);
|
||||
input[15] = (int) DataHelper.fromLongLE(iv, ivOffset + 8, 4);
|
||||
//System.out.println("initIV");
|
||||
//dumpBlock(input);
|
||||
ChaChaCore.hash(output, input);
|
||||
@ -96,7 +109,21 @@ public final class ChaCha20 {
|
||||
byte[] ciphertext, int ciphertextOffset,
|
||||
byte[] plaintext, int plaintextOffset, int length) {
|
||||
// it's symmetric!
|
||||
encrypt(key, iv, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
encrypt(key, iv, 0, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt from ciphertext to plaintext
|
||||
*
|
||||
* @param key first 32 bytes used as the key
|
||||
* @param iv first 12 bytes starting at ivOffset used as the iv
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public static void decrypt(byte[] key, byte[] iv, int ivOffset,
|
||||
byte[] ciphertext, int ciphertextOffset,
|
||||
byte[] plaintext, int plaintextOffset, int length) {
|
||||
// it's symmetric!
|
||||
encrypt(key, iv, ivOffset, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
}
|
||||
|
||||
/****
|
||||
|
@ -114,10 +114,14 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
|
||||
/**
|
||||
* Set up to encrypt or decrypt the next packet.
|
||||
* I2P add off/len
|
||||
*
|
||||
* @param ad The associated data for the packet.
|
||||
* @param off offset
|
||||
* @param len length
|
||||
* @since 0.9.54 added off/len
|
||||
*/
|
||||
private void setup(byte[] ad)
|
||||
private void setup(byte[] ad, int off, int len)
|
||||
{
|
||||
if (n == -1L)
|
||||
throw new IllegalStateException("Nonce has wrapped around");
|
||||
@ -127,7 +131,7 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
ChaChaCore.xorBlock(polyKey, 0, polyKey, 0, 32, output);
|
||||
poly.reset(polyKey, 0);
|
||||
if (ad != null) {
|
||||
poly.update(ad, 0, ad.length);
|
||||
poly.update(ad, off, len);
|
||||
poly.pad();
|
||||
}
|
||||
if (++(input[12]) == 0)
|
||||
@ -155,14 +159,16 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
|
||||
/**
|
||||
* Finishes up the authentication tag for a packet.
|
||||
*
|
||||
* @param ad The associated data.
|
||||
* I2P changed ad to adLength; ad data not used here
|
||||
*
|
||||
* @param adLength The length of the associated data, 0 if none.
|
||||
* @param length The length of the plaintext data.
|
||||
* @since 0.9.54 changed ad to adLength
|
||||
*/
|
||||
private void finish(byte[] ad, int length)
|
||||
private void finish(int adLength, int length)
|
||||
{
|
||||
poly.pad();
|
||||
putLittleEndian64(polyKey, 0, ad != null ? ad.length : 0);
|
||||
putLittleEndian64(polyKey, 0, adLength);
|
||||
putLittleEndian64(polyKey, 8, length);
|
||||
poly.update(polyKey, 0, 16);
|
||||
poly.finish(polyKey, 0);
|
||||
@ -195,7 +201,18 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
|
||||
@Override
|
||||
public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {
|
||||
return encryptWithAd(ad, 0, ad != null ? ad.length : 0, plaintext, plaintextOffset,
|
||||
ciphertext, ciphertextOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* I2P
|
||||
* @since 0.9.54
|
||||
*/
|
||||
@Override
|
||||
public int encryptWithAd(byte[] ad, int adOffset, int adLength, byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {
|
||||
int space;
|
||||
if (ciphertextOffset > ciphertext.length)
|
||||
space = 0;
|
||||
@ -211,18 +228,30 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
}
|
||||
if (space < 16 || length > (space - 16))
|
||||
throw new ShortBufferException();
|
||||
setup(ad);
|
||||
setup(ad, adOffset, adLength);
|
||||
encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
poly.update(ciphertext, ciphertextOffset, length);
|
||||
finish(ad, length);
|
||||
finish(adLength, length);
|
||||
System.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16);
|
||||
return length + 16;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int decryptWithAd(byte[] ad, byte[] ciphertext,
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) throws ShortBufferException, BadPaddingException {
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) throws ShortBufferException, BadPaddingException {
|
||||
return decryptWithAd(ad, 0, ad != null ? ad.length : 0, ciphertext, ciphertextOffset,
|
||||
plaintext, plaintextOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* I2P
|
||||
* @since 0.9.54
|
||||
*/
|
||||
@Override
|
||||
public int decryptWithAd(byte[] ad, int adOffset, int adLength, byte[] ciphertext,
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) throws ShortBufferException, BadPaddingException {
|
||||
int space;
|
||||
if (ciphertextOffset > ciphertext.length)
|
||||
space = 0;
|
||||
@ -247,9 +276,9 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
int dataLen = length - 16;
|
||||
if (dataLen > space)
|
||||
throw new ShortBufferException();
|
||||
setup(ad);
|
||||
setup(ad, adOffset, adLength);
|
||||
poly.update(ciphertext, ciphertextOffset, dataLen);
|
||||
finish(ad, dataLen);
|
||||
finish(adLength, dataLen);
|
||||
int temp = 0;
|
||||
for (int index = 0; index < 16; ++index)
|
||||
temp |= (polyKey[index] ^ ciphertext[ciphertextOffset + dataLen + index]);
|
||||
|
@ -109,6 +109,13 @@ public interface CipherState extends Destroyable, Cloneable {
|
||||
*/
|
||||
int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException;
|
||||
|
||||
/**
|
||||
* I2P
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public int encryptWithAd(byte[] ad, int adOffset, int adLength, byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException;
|
||||
|
||||
/**
|
||||
* Decrypts a ciphertext buffer using the cipher and a block of associated data.
|
||||
*
|
||||
@ -136,6 +143,14 @@ public interface CipherState extends Destroyable, Cloneable {
|
||||
*/
|
||||
int decryptWithAd(byte[] ad, byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, BadPaddingException;
|
||||
|
||||
/**
|
||||
* I2P
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public int decryptWithAd(byte[] ad, int adOffset, int adLength, byte[] ciphertext,
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) throws ShortBufferException, BadPaddingException;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this cipher and initializes it with a key.
|
||||
*
|
||||
|
@ -128,16 +128,26 @@ public class HandshakeState implements Destroyable, Cloneable {
|
||||
*/
|
||||
private static final int FALLBACK_POSSIBLE = 0x40;
|
||||
|
||||
/** NTCP2 */
|
||||
public static final String protocolName = "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256";
|
||||
/** Ratchet */
|
||||
public static final String protocolName2 = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256";
|
||||
/** Tunnels */
|
||||
public static final String protocolName3 = "Noise_N_25519_ChaChaPoly_SHA256";
|
||||
/** SSU2 */
|
||||
public static final String protocolName4 = "Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256";
|
||||
private static final String prefix;
|
||||
private final String patternId;
|
||||
/** NTCP2 */
|
||||
public static final String PATTERN_ID_XK = "XK";
|
||||
/** Ratchet */
|
||||
public static final String PATTERN_ID_IK = "IK";
|
||||
/** Tunnels */
|
||||
public static final String PATTERN_ID_N = "N";
|
||||
/** same as N but no post-mixHash needed */
|
||||
public static final String PATTERN_ID_N_NO_RESPONSE = "N!";
|
||||
/** SSU2 */
|
||||
public static final String PATTERN_ID_XK_SSU2 = "XK-SSU2";
|
||||
private static String dh;
|
||||
private static final String cipher;
|
||||
private static final String hash;
|
||||
@ -182,6 +192,11 @@ public class HandshakeState implements Destroyable, Cloneable {
|
||||
PATTERN_N = Pattern.lookup(id);
|
||||
if (PATTERN_N == null)
|
||||
throw new IllegalArgumentException("Handshake pattern is not recognized");
|
||||
// XK-SSU2
|
||||
components = protocolName4.split("_");
|
||||
id = components[1].substring(0, 2);
|
||||
if (!PATTERN_ID_XK.equals(id))
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,6 +224,8 @@ public class HandshakeState implements Destroyable, Cloneable {
|
||||
pattern = PATTERN_N;
|
||||
else if (patternId.equals(PATTERN_ID_N_NO_RESPONSE)) // same as N but no post-mixHash needed
|
||||
pattern = PATTERN_N;
|
||||
else if (patternId.equals(PATTERN_ID_XK_SSU2))
|
||||
pattern = PATTERN_XK;
|
||||
else
|
||||
throw new IllegalArgumentException("Handshake pattern is not recognized");
|
||||
short flags = pattern[0];
|
||||
@ -510,6 +527,18 @@ public class HandshakeState implements Destroyable, Cloneable {
|
||||
/**
|
||||
* Writes a message payload during the handshake.
|
||||
*
|
||||
* Payload (plaintext) and message (encrypted) may be in the same buffer if the payload
|
||||
* if offset enough past the message offset to leave room for the
|
||||
* key(s) and/or MAC. For 32 byte keys and 16 byte MACs,
|
||||
* if message == payload, payloadOffset must be at least this much
|
||||
* greater than messageOffset:
|
||||
*
|
||||
* XK: Message 1: 32; message 2: 32; message 3: 48
|
||||
*
|
||||
* IK: Message 1: 80; message 2: 48
|
||||
*
|
||||
* N: Message 1: 32
|
||||
*
|
||||
* @param message The buffer that will be populated with the
|
||||
* handshake packet to be written to the transport.
|
||||
* @param messageOffset First offset within the message buffer
|
||||
@ -679,7 +708,10 @@ public class HandshakeState implements Destroyable, Cloneable {
|
||||
|
||||
/**
|
||||
* Reads a message payload during the handshake.
|
||||
*
|
||||
*
|
||||
* Payload (plaintext) and message (encrypted) may be in the same buffer
|
||||
* and have the same offset.
|
||||
*
|
||||
* @param message Buffer containing the incoming handshake
|
||||
* that was read from the transport.
|
||||
* @param messageOffset Offset of the first message byte.
|
||||
|
@ -40,15 +40,18 @@ class SymmetricState implements Destroyable, Cloneable {
|
||||
private static final byte[] INIT_CK_XK;
|
||||
private static final byte[] INIT_CK_IK;
|
||||
private static final byte[] INIT_CK_N;
|
||||
private static final byte[] INIT_CK_XK_SSU2;
|
||||
// precalculated hash of the hash of the Noise name = mixHash(nullPrologue)
|
||||
private static final byte[] INIT_HASH_XK = new byte[32];
|
||||
private static final byte[] INIT_HASH_IK = new byte[32];
|
||||
private static final byte[] INIT_HASH_N = new byte[32];
|
||||
private static final byte[] INIT_HASH_XK_SSU2 = new byte[32];
|
||||
|
||||
static {
|
||||
INIT_CK_XK = initHash(HandshakeState.protocolName);
|
||||
INIT_CK_IK = initHash(HandshakeState.protocolName2);
|
||||
INIT_CK_N = initHash(HandshakeState.protocolName3);
|
||||
INIT_CK_XK_SSU2 = initHash(HandshakeState.protocolName4);
|
||||
try {
|
||||
MessageDigest md = Noise.createHash("SHA256");
|
||||
md.update(INIT_CK_XK, 0, 32);
|
||||
@ -57,6 +60,8 @@ class SymmetricState implements Destroyable, Cloneable {
|
||||
md.digest(INIT_HASH_IK, 0, 32);
|
||||
md.update(INIT_CK_N, 0, 32);
|
||||
md.digest(INIT_HASH_N, 0, 32);
|
||||
md.update(INIT_CK_XK_SSU2, 0, 32);
|
||||
md.digest(INIT_HASH_XK_SSU2, 0, 32);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
@ -125,6 +130,9 @@ class SymmetricState implements Destroyable, Cloneable {
|
||||
patternId.equals(HandshakeState.PATTERN_ID_N_NO_RESPONSE)) {
|
||||
initCK = INIT_CK_N;
|
||||
initHash = INIT_HASH_N;
|
||||
} else if (patternId.equals(HandshakeState.PATTERN_ID_XK_SSU2)) {
|
||||
initCK = INIT_CK_XK_SSU2;
|
||||
initHash = INIT_HASH_XK_SSU2;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Handshake pattern is not recognized");
|
||||
}
|
||||
|
Reference in New Issue
Block a user