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

Skip to content
Snippets Groups Projects
Commit e34b6462 authored by zzz's avatar zzz
Browse files

Crypto: New ChaCha20 wrapper around ChaChaCore, for use with Encrypted LS2

parent 14ac8fe5
No related branches found
No related tags found
No related merge requests found
...@@ -169,4 +169,45 @@ public final class ChaChaCore { ...@@ -169,4 +169,45 @@ public final class ChaChaCore {
v[c] += v[d]; v[c] += v[d];
v[b] = leftRotate7(v[b] ^ v[c]); v[b] = leftRotate7(v[b] ^ v[c]);
} }
/**
* XOR's the output of ChaCha20 with a byte buffer.
*
* @param input The input byte buffer.
* @param inputOffset The offset of the first input byte.
* @param output The output byte buffer (can be the same as the input).
* @param outputOffset The offset of the first output byte.
* @param length The number of bytes to XOR between 1 and 64.
* @param block The ChaCha20 output block.
*
* @since 0.9.39 moved from ChaChaPolyCipherState
*/
public static void xorBlock(byte[] input, int inputOffset, byte[] output, int outputOffset, int length, int[] block)
{
int posn = 0;
int value;
while (length >= 4) {
value = block[posn++];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));
output[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24));
inputOffset += 4;
outputOffset += 4;
length -= 4;
}
if (length == 3) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));
} else if (length == 2) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
} else if (length == 1) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
}
}
} }
package net.i2p.crypto;
/*
* Contains code from Noise ChaChaPolyCipherState:
*
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
import com.southernstorm.noise.crypto.chacha20.ChaChaCore;
import net.i2p.data.DataHelper;
/**
* ChaCha20, wrapper around Noise ChaChaCore.
* RFC 7539
*
* @since 0.9.39
*/
public final class ChaCha20 {
private ChaCha20() {}
/**
* Encrypt from plaintext to ciphertext
*
* @param key first 32 bytes used as the key
* @param iv first 12 bytes used as the iv
*/
public static void encrypt(byte[] key, byte[] iv,
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);
//System.out.println("initkey");
//dumpBlock(input);
// RFC 7539
// block counter
input[12] = 1;
// Words 13-15 are a nonce, which should not be repeated for the same
// key. The 13th word is the first 32 bits of the input nonce taken
// as a little-endian integer, while the 15th word is the last 32
// 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);
//System.out.println("initIV");
//dumpBlock(input);
ChaChaCore.hash(output, input);
//int ctr = 1;
//System.out.println("hash " + ctr);
//dumpBlock(output);
while (length > 0) {
int tempLen = 64;
if (tempLen > length)
tempLen = length;
ChaChaCore.hash(output, input);
//System.out.println("hash " + ++ctr);
//dumpBlock(output);
ChaChaCore.xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output);
if (++(input[12]) == 0)
++(input[13]);
plaintextOffset += tempLen;
ciphertextOffset += tempLen;
length -= tempLen;
}
}
/**
* Encrypt from ciphertext to plaintext
*
* @param key first 32 bytes used as the key
* @param iv first 12 bytes used as the iv
*/
public static void decrypt(byte[] key, byte[] iv,
byte[] ciphertext, int ciphertextOffset,
byte[] plaintext, int plaintextOffset, int length) {
// it's symmetric!
encrypt(key, iv, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
}
/****
public static void main(String[] args) {
// vectors as in RFC 7539
byte[] plaintext = DataHelper.getASCII("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.");
byte[] key = new byte[32];
for (int i = 0; i < 32; i++) {
key[i] = (byte) i;
}
byte[] iv = new byte[12];
iv[7] = 0x4a;
byte[] out = new byte[plaintext.length];
encrypt(key, iv, plaintext, 0, out, 0, plaintext.length);
// Ciphertext Sunscreen:
// 000 6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81 n.5.%h..A..(..i.
// 016 e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b .~z..C`..'......
// 032 f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57 ..e.RG3..Y=..b.W
// 048 16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8 .9.$.QR..S.5..a.
// 064 07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e ....P.jaV....".^
// 080 52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36 R.QM.........y76
// 096 5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42 Z...t.[......x^B
// 112 87 4d .M
System.out.println("Ciphertext:\n" + net.i2p.util.HexDump.dump(out));
byte[] out2 = new byte[plaintext.length];
decrypt(key, iv, out, 0, out2, 0, plaintext.length);
System.out.println("Plaintext:\n" + net.i2p.util.HexDump.dump(out2));
}
private static void dumpBlock(int[] b) {
byte[] d = new byte[64];
for (int i = 0; i < 16; i++) {
//DataHelper.toLongLE(d, i*4, 4, b[i] & 0xffffffffL);
// use BE so the bytes look right
DataHelper.toLong(d, i*4, 4, b[i] & 0xffffffffL);
}
System.out.println(net.i2p.util.HexDump.dump(d));
}
****/
}
...@@ -89,45 +89,6 @@ public class ChaChaPolyCipherState implements CipherState { ...@@ -89,45 +89,6 @@ public class ChaChaPolyCipherState implements CipherState {
public boolean hasKey() { public boolean hasKey() {
return haskey; return haskey;
} }
/**
* XOR's the output of ChaCha20 with a byte buffer.
*
* @param input The input byte buffer.
* @param inputOffset The offset of the first input byte.
* @param output The output byte buffer (can be the same as the input).
* @param outputOffset The offset of the first output byte.
* @param length The number of bytes to XOR between 1 and 64.
* @param block The ChaCha20 output block.
*/
private static void xorBlock(byte[] input, int inputOffset, byte[] output, int outputOffset, int length, int[] block)
{
int posn = 0;
int value;
while (length >= 4) {
value = block[posn++];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));
output[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24));
inputOffset += 4;
outputOffset += 4;
length -= 4;
}
if (length == 3) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16));
} else if (length == 2) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8));
} else if (length == 1) {
value = block[posn];
output[outputOffset] = (byte)(input[inputOffset] ^ value);
}
}
/** /**
* Set up to encrypt or decrypt the next packet. * Set up to encrypt or decrypt the next packet.
...@@ -141,7 +102,7 @@ public class ChaChaPolyCipherState implements CipherState { ...@@ -141,7 +102,7 @@ public class ChaChaPolyCipherState implements CipherState {
ChaChaCore.initIV(input, n++); ChaChaCore.initIV(input, n++);
ChaChaCore.hash(output, input); ChaChaCore.hash(output, input);
Arrays.fill(polyKey, (byte)0); Arrays.fill(polyKey, (byte)0);
xorBlock(polyKey, 0, polyKey, 0, 32, output); ChaChaCore.xorBlock(polyKey, 0, polyKey, 0, 32, output);
poly.reset(polyKey, 0); poly.reset(polyKey, 0);
if (ad != null) { if (ad != null) {
poly.update(ad, 0, ad.length); poly.update(ad, 0, ad.length);
...@@ -201,7 +162,7 @@ public class ChaChaPolyCipherState implements CipherState { ...@@ -201,7 +162,7 @@ public class ChaChaPolyCipherState implements CipherState {
if (tempLen > length) if (tempLen > length)
tempLen = length; tempLen = length;
ChaChaCore.hash(output, input); ChaChaCore.hash(output, input);
xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output); ChaChaCore.xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output);
if (++(input[12]) == 0) if (++(input[12]) == 0)
++(input[13]); ++(input[13]);
plaintextOffset += tempLen; plaintextOffset += tempLen;
......
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