package org.bouncycastle.crypto.test.speedy;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* Poly1305 message authentication code, designed by D. J. Bernstein.
* <p>
* Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
* consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
* effective key bits) used in the authenticator.
* <p>
* This implementation is adapted from the public domain <a href="http://nacl.cr.yp.to/">nacl</a>
* <code>ref</code> implementation, and is probably too slow for real usage.
*
* @see Poly1305KeyGenerator
*/
public class Poly1305Reference
implements Mac
{
private static final int BLOCK_SIZE = 16;
private static final int STATE_SIZE = BLOCK_SIZE + 1;
private static int[] minusp = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252};
private final BlockCipher cipher;
/** Encrypted nonce */
private final byte[] encryptedNonce = new byte[BLOCK_SIZE];
/** Private integer r *, expanded to 17 bytes */
private final int[] r = new int[STATE_SIZE];
/** Accumulated authenticator value */
private final int[] h = new int[STATE_SIZE];
/** Temp buffer for incorporating into authenticator */
private final int[] c = new int[STATE_SIZE];
private final byte[] singleByte = new byte[1];
/** Current block of buffered input */
private final byte[] currentBlock = new byte[BLOCK_SIZE];
/** Current offset in input buffer */
private int currentBlockOffset = 0;
public Poly1305Reference(BlockCipher cipher)
{
if (cipher.getBlockSize() != BLOCK_SIZE)
{
throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
}
this.cipher = cipher;
}
public void init(CipherParameters params)
throws IllegalArgumentException
{
final byte[] nonce;
final byte[] key;
if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
{
nonce = ((ParametersWithIV)params).getIV();
key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
}
else
{
throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
}
setKey(key, nonce);
reset();
}
private void setKey(byte[] key, byte[] nonce)
{
if (nonce.length != BLOCK_SIZE)
{
throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
}
Poly1305KeyGenerator.checkKey(key);
// Expand private integer r
for (int i = 0; i < BLOCK_SIZE; i++)
{
r[i] = key[BLOCK_SIZE + i] & 0xFF;
}
r[BLOCK_SIZE] = 0;
// Calculate encrypted nonce
final byte[] cipherKey = new byte[BLOCK_SIZE];
System.arraycopy(key, 0, cipherKey, 0, cipherKey.length);
cipher.init(true, new KeyParameter(cipherKey));
cipher.processBlock(nonce, 0, this.encryptedNonce, 0);
}
public String getAlgorithmName()
{
return "Poly1305-Ref-" + cipher.getAlgorithmName();
}
public int getMacSize()
{
return BLOCK_SIZE;
}
public void update(byte in)
throws IllegalStateException
{
singleByte[0] = in;
update(singleByte, 0, 1);
}
public void update(byte[] in, int inOff, int len)
throws DataLengthException,
IllegalStateException
{
int copied = 0;
while (len > copied)
{
if (currentBlockOffset == currentBlock.length)
{
processBlock();
currentBlockOffset = 0;
}
int toCopy = Math.min((len - copied), currentBlock.length - currentBlockOffset);
System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
copied += toCopy;
currentBlockOffset += toCopy;
}
}
/**
* Add a full block of 16 bytes of data, padded to 17 bytes, to the MAC
*/
private void processBlock()
{
for (int i = 0; i < currentBlockOffset; i++)
{
c[i] = currentBlock[i] & 0xFF;
}
c[currentBlockOffset] = 1;
for (int i = currentBlockOffset + 1; i < c.length; i++)
{
c[i] = 0;
}
add(h, c);
mulmod(h, r);
}
public int doFinal(byte[] out, int outOff)
throws DataLengthException,
IllegalStateException
{
if (outOff + BLOCK_SIZE > out.length)
{
throw new DataLengthException("Output buffer is too short.");
}
if (currentBlockOffset > 0)
{
// Process padded final block
processBlock();
}
freeze(h);
// Add encrypted nonce to result
for (int i = 0; i < BLOCK_SIZE; i++)
{
c[i] = encryptedNonce[i] & 0xFF;
}
c[BLOCK_SIZE] = 0;
add(h, c);
for (int i = 0; i < BLOCK_SIZE; i++)
{
out[outOff + i] = (byte)h[i];
}
reset();
return BLOCK_SIZE;
}
public void reset()
{
currentBlockOffset = 0;
for (int i = 0; i < h.length; i++)
{
h[i] = 0;
}
}
// 130 bit math adapted from nacl ref implementation
/**
* 130 bit add with carry.
*/
private static void add(int[] h, int[] c)
{
int u = 0;
for (int j = 0; j < 17; ++j)
{
u += h[j] + c[j];
h[j] = u & 255;
u >>= 8;
}
}
/**
* 130 bit multiplication mod 2^130-5
*/
private void mulmod(int[] h, int[] r)
{
final int[] hr = c;
for (int i = 0; i < 17; ++i)
{
int u = 0;
/* Basic multiply to compute term i */
for (int j = 0; j <= i; ++j)
{
u += h[j] * r[i - j];
}
/*
* Modular reduction
*
* Shift overflow >> 130 bits == (>> 17 bytes = 136 bits) + (<< 6 bits = * 64)
*
* Reduction mod 2^130-5 leaves 5x remainder, so 64 * 5 = 320.
*/
for (int j = i + 1; j < 17; ++j)
{
u += 320 * h[j] * r[i + 17 - j];
}
hr[i] = u;
}
System.arraycopy(hr, 0, h, 0, h.length);
squeeze(h);
}
/**
* Propagate carries following a modular multiplication.
*/
private static void squeeze(int[] h)
{
int u = 0;
for (int j = 0; j < 16; ++j)
{
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16];
h[16] = u & 3;
u = 5 * (u >> 2);
for (int j = 0; j < 16; ++j)
{
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16];
h[16] = u;
}
/**
* Constant time correction of h to be < p (2^130 - 5).
*/
private void freeze(int[] h)
{
final int[] horig = c;
System.arraycopy(h, 0, horig, 0, h.length);
add(h, minusp);
final int negative = -(h[16] >> 7);
for (int j = 0; j < 17; ++j)
{
h[j] ^= negative & (horig[j] ^ h[j]);
}
}
}