I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Unverified Commit 9f7f1bbc authored by zzz's avatar zzz
Browse files

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
parent edc9d6fe
No related branches found
No related tags found
No related merge requests found
......@@ -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");
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment