forked from I2P_Developers/i2p.i2p
Crypto: New ChaCha20 wrapper around ChaChaCore, for use with Encrypted LS2
This commit is contained in:
@ -169,4 +169,45 @@ public final class ChaChaCore {
|
||||
v[c] += v[d];
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
139
core/java/src/net/i2p/crypto/ChaCha20.java
Normal file
139
core/java/src/net/i2p/crypto/ChaCha20.java
Normal file
@ -0,0 +1,139 @@
|
||||
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 {
|
||||
public boolean 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.
|
||||
@ -141,7 +102,7 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
ChaChaCore.initIV(input, n++);
|
||||
ChaChaCore.hash(output, input);
|
||||
Arrays.fill(polyKey, (byte)0);
|
||||
xorBlock(polyKey, 0, polyKey, 0, 32, output);
|
||||
ChaChaCore.xorBlock(polyKey, 0, polyKey, 0, 32, output);
|
||||
poly.reset(polyKey, 0);
|
||||
if (ad != null) {
|
||||
poly.update(ad, 0, ad.length);
|
||||
@ -201,7 +162,7 @@ public class ChaChaPolyCipherState implements CipherState {
|
||||
if (tempLen > length)
|
||||
tempLen = length;
|
||||
ChaChaCore.hash(output, input);
|
||||
xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output);
|
||||
ChaChaCore.xorBlock(plaintext, plaintextOffset, ciphertext, ciphertextOffset, tempLen, output);
|
||||
if (++(input[12]) == 0)
|
||||
++(input[13]);
|
||||
plaintextOffset += tempLen;
|
||||
|
Reference in New Issue
Block a user