Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XChaCha20Engine and XChaCha20Poly1305 #957

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CONTRIBUTORS.html
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@
<li>pelzvieh &lt;https://github.com/pelzvieh&gt; fix for CRLs with absent next update in PKIXCRLUtil.</li>
<li>Matthias Valvekens &lt;matthias.valvekens&#064itextpdf.com&gt; new implementation and additional testing for BasicConstraints task in validations package.</li>
<li>Bart Bakker &lt;https://github.com/bjpbakker&gt; RFC 5280 boundaries checks for CRLReason and CRLNumber. Addition of binary-signing-time CMS attribute.</li>
<li>Matthias Neugschwandtner and Gergo Barany from Oracle Labs; SICBlockCipher performance optimisation.</li>
<li>Matthias Neugschwandtner and Gergo Barany from Oracle Labs; SICBlockCipher performance optimisation.</li>
<li>Johannes Leupold from KOBIL GmbH; XChaCha20 and XChaCha20Poly1305 initial implementation.</li>
</ul>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.bouncycastle.crypto.engines;

import org.bouncycastle.util.Pack;

/**
* Implementation of eXtended Nonce ChaCha (XChaCha20).
*
* The implementation follows the IETF Draft for XChaCha20-Poly1305
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*
* XChaCha20 requires a 256bit key and a 192bit IV.
*/
public class XChaCha20Engine extends ChaChaEngine
{

/**
* Create a new XChaCha20 engine.
*/
public XChaCha20Engine()
{
super();
}

@Override
public String getAlgorithmName()
{
return "XChaCha20";
}

@Override
protected int getNonceSize()
{
return 24;
}

@Override
protected void setKey(byte[] keyBytes, byte[] ivBytes)
{
if (keyBytes == null)
{
throw new IllegalArgumentException(
getAlgorithmName() + " doesn't support re-init with null key");
}

if (keyBytes.length != 32)
{
throw new IllegalStateException(getAlgorithmName() + " requires a 256 bit key");
}

// Derive sub key using the HChaCha algorithm and set copy it to the engine state
int[] subKey = hChaChaDeriveSubKey(keyBytes, ivBytes);
System.arraycopy(subKey, 0, engineState, 4, subKey.length);

// Use last 64 bits of input IV as nonce for ChaCha20
Pack.littleEndianToInt(ivBytes, 16, engineState, 14, 2);
}

public int[] hChaChaDeriveSubKey(byte[] keyBytes, byte[] ivBytes)
{
if (keyBytes == null)
{
throw new IllegalArgumentException("HChaCha" + rounds + " doesn't support null keys");
}

if (keyBytes.length != 32)
{
throw new IllegalStateException("HChaCha" + rounds + " requires a 256 bit key");
}

if (ivBytes == null)
{
throw new IllegalArgumentException("HChaCha" + rounds + " needs a non-null IV");
}

if (ivBytes.length < 16)
{
throw new IllegalArgumentException(
"HChaCha" + rounds + " needs an at least 128 bit nonce");
}

// Set key for HChaCha20
super.setKey(keyBytes, ivBytes);
Pack.littleEndianToInt(ivBytes, 0, engineState, 12, 4);

// Process engine state to generate ChaCha20 key
int[] hchacha20Out = new int[engineState.length];
chachaCore(20, engineState, hchacha20Out);

// Take first and last 128 bits of output as the sub key
int[] subkey = new int[8];
System.arraycopy(hchacha20Out, 0, subkey, 0, 4);
System.arraycopy(hchacha20Out, 12, subkey, 4, 4);

// Remove addition in final round of chachaCore
subkey[0] -= engineState[0];
subkey[1] -= engineState[1];
subkey[2] -= engineState[2];
subkey[3] -= engineState[3];
subkey[4] -= engineState[12];
subkey[5] -= engineState[13];
subkey[6] -= engineState[14];
subkey[7] -= engineState[15];

return subkey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public class ChaCha20Poly1305
implements AEADCipher
{
private static final class State
protected static final class State
{
static final int UNINITIALIZED = 0;
static final int ENC_INIT = 1;
Expand All @@ -41,7 +41,7 @@ private static final class State
private final ChaCha7539Engine chacha20;
private final Mac poly1305;

private final byte[] key = new byte[KEY_SIZE];
protected final byte[] key = new byte[KEY_SIZE];
private final byte[] nonce = new byte[NONCE_SIZE];
private final byte[] buf = new byte[BUF_SIZE + MAC_SIZE];
private final byte[] mac = new byte[MAC_SIZE];
Expand All @@ -50,7 +50,7 @@ private static final class State

private long aadCount;
private long dataCount;
private int state = State.UNINITIALIZED;
protected int state = State.UNINITIALIZED;
private int bufPos;

public ChaCha20Poly1305()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.bouncycastle.crypto.modes;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.XChaCha20Engine;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Pack;

public class XChaCha20Poly1305 extends ChaCha20Poly1305 implements AEADCipher
{
private static final int MAC_BYTES = 16;
private static final int KEY_SIZE = 32;
private static final int NONCE_SIZE = 24;

@Override
public String getAlgorithmName()
{
return "XChaCha20Poly1305";
}

@Override
public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
{
KeyParameter keyParam;
byte[] nonce;
byte[] aad = null;

if (params instanceof AEADParameters)
{
AEADParameters aeadParams = (AEADParameters) params;
int macSize = aeadParams.getMacSize();

if (MAC_BYTES * 8 != macSize)
{
throw new IllegalArgumentException("Invalid value for MAC size: " + macSize);
}

keyParam = aeadParams.getKey();
nonce = aeadParams.getNonce();
aad = aeadParams.getAssociatedText();
}
else if (params instanceof ParametersWithIV)
{
ParametersWithIV ivParams = (ParametersWithIV) params;
keyParam = (KeyParameter) ivParams.getParameters();
nonce = ivParams.getIV();
}
else
{
throw new IllegalArgumentException("invalid parameters passed to XChaCha20Poly1305");
}

// Validate nonce
if (nonce == null || NONCE_SIZE != nonce.length)
{
throw new IllegalArgumentException("XChaCha20Poly1305 requires a 192 bit nonce");
}

// Validate key
if (null == keyParam)
{
if (State.UNINITIALIZED == state)
{
throw new IllegalArgumentException("Key must be specified in initial init");
}

// Derive sub key from old key and new nonce using the HChaCha20 algorithm
XChaCha20Engine xChaCha = new XChaCha20Engine();
byte[] subKey = Pack.intToLittleEndian(xChaCha.hChaChaDeriveSubKey(key, nonce));

keyParam = new KeyParameter(subKey);
}
else
{
if (KEY_SIZE != keyParam.getKey().length)
{
throw new IllegalArgumentException("Key must be 256 bits");
}

// Derive sub key using the HChaCha20 algorithm
XChaCha20Engine xChaCha = new XChaCha20Engine();
byte[] subKey = Pack.intToLittleEndian(xChaCha.hChaChaDeriveSubKey(keyParam.getKey(),
nonce));

keyParam = new KeyParameter(subKey);
}

// Use last 64 bits of nonce prefixed with 4 NUL bytes as nonce for ChaCha20Poly1305
// Nonce reuse will be caught in super.init(...)
byte[] chaChaNonce = new byte[12];
System.arraycopy(nonce, 16, chaChaNonce, 4, 8);

if (aad == null) {
super.init(forEncryption, new ParametersWithIV(keyParam, chaChaNonce));
} else {
super.init(forEncryption, new AEADParameters(keyParam,
8 * MAC_BYTES, chaChaNonce, aad));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.bouncycastle.crypto.engines.TEAEngine;
import org.bouncycastle.crypto.engines.ThreefishEngine;
import org.bouncycastle.crypto.engines.TwofishEngine;
import org.bouncycastle.crypto.engines.XChaCha20Engine;
import org.bouncycastle.crypto.engines.XSalsa20Engine;
import org.bouncycastle.crypto.engines.XTEAEngine;
import org.bouncycastle.crypto.io.CipherInputStream;
Expand Down Expand Up @@ -508,6 +509,7 @@ private void performTests()
testMode(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
testMode(new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[32]), new byte[24]));
testMode(new ChaChaEngine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
testMode(new XChaCha20Engine(), new ParametersWithIV(new KeyParameter(new byte[32]), new byte[24]));
testMode(new Grainv1Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
testMode(new Grain128Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[12]));
testMode(new HC128Engine(), new KeyParameter(new byte[16]));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public class RegressionTest
new XSalsa20Test(),
new ChaChaTest(),
new ChaCha20Poly1305Test(),
new XChaCha20Test(),
new XChaCha20Poly1305Test(),
new CMacTest(),
new EAXTest(),
new GCMTest(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.bouncycastle.crypto.engines.ISAACEngine;
import org.bouncycastle.crypto.engines.RC4Engine;
import org.bouncycastle.crypto.engines.Salsa20Engine;
import org.bouncycastle.crypto.engines.XChaCha20Engine;
import org.bouncycastle.crypto.engines.XSalsa20Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
Expand Down Expand Up @@ -42,6 +43,7 @@ public void performTest()
random(24)));
testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(32)), random(8)));
testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(16)), random(8)));
testReset(new XChaCha20Engine(), new XChaCha20Engine(), new ParametersWithIV(new KeyParameter(random(32)), random(24)));
testReset(new RC4Engine(), new RC4Engine(), new KeyParameter(random(16)));
testReset(new ISAACEngine(), new ISAACEngine(), new KeyParameter(random(16)));
testReset(new HC128Engine(), new HC128Engine(), new ParametersWithIV(new KeyParameter(random(16)), random(16)));
Expand Down
Loading