aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java')
-rw-r--r--libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java279
1 files changed, 279 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java
new file mode 100644
index 000000000..ca1558d82
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/macs/Poly1305.java
@@ -0,0 +1,279 @@
+package org.spongycastle.crypto.macs;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.util.Pack;
+
+/**
+ * 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>
+ * The polynomial calculation in this implementation is adapted from the public domain <a
+ * href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
+ * by Andrew M (@floodyberry).
+ * @see Poly1305KeyGenerator
+ */
+public class Poly1305
+ implements Mac
+{
+ private static final int BLOCK_SIZE = 16;
+
+ private final BlockCipher cipher;
+
+ private final byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private int r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private int s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private int k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private final byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private int h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(final BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /**
+ * Initialises the Poly1305 MAC.
+ *
+ * @param a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+ * a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.
+ */
+ public void init(final 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(final byte[] key, final byte[] nonce)
+ {
+ if (nonce.length != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
+ }
+ Poly1305KeyGenerator.checkKey(key);
+
+ // Extract r portion of key
+ int t0 = Pack.littleEndianToInt(key, BLOCK_SIZE + 0);
+ int t1 = Pack.littleEndianToInt(key, BLOCK_SIZE + 4);
+ int t2 = Pack.littleEndianToInt(key, BLOCK_SIZE + 8);
+ int t3 = Pack.littleEndianToInt(key, BLOCK_SIZE + 12);
+
+ r0 = t0 & 0x3ffffff; t0 >>>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>>= 8;
+ r4 = t3 & 0x00fffff;
+
+ // Precompute multipliers
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ // Compute 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, cipherKey, 0);
+
+ k0 = Pack.littleEndianToInt(cipherKey, 0);
+ k1 = Pack.littleEndianToInt(cipherKey, 4);
+ k2 = Pack.littleEndianToInt(cipherKey, 8);
+ k3 = Pack.littleEndianToInt(cipherKey, 12);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Poly1305-" + cipher.getAlgorithmName();
+ }
+
+ public int getMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void update(final byte in)
+ throws IllegalStateException
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(final byte[] in, final int inOff, final int len)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ private void processBlock()
+ {
+ if (currentBlockOffset < BLOCK_SIZE)
+ {
+ currentBlock[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+ {
+ currentBlock[i] = 0;
+ }
+ }
+
+ final long t0 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 0);
+ final long t1 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 4);
+ final long t2 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 8);
+ final long t3 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += (((t1 << 32) | t0) >>> 26) & 0x3ffffff;
+ h2 += (((t2 << 32) | t1) >>> 20) & 0x3ffffff;
+ h3 += (((t3 << 32) | t2) >>> 14) & 0x3ffffff;
+ h4 += (t3 >>> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ long tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ long tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ long tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ long tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ long tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ long b;
+ h0 = (int)tp0 & 0x3ffffff; b = (tp0 >>> 26);
+ tp1 += b; h1 = (int)tp1 & 0x3ffffff; b = ((tp1 >>> 26) & 0xffffffff);
+ tp2 += b; h2 = (int)tp2 & 0x3ffffff; b = ((tp2 >>> 26) & 0xffffffff);
+ tp3 += b; h3 = (int)tp3 & 0x3ffffff; b = (tp3 >>> 26);
+ tp4 += b; h4 = (int)tp4 & 0x3ffffff; b = (tp4 >>> 26);
+ h0 += b * 5;
+ }
+
+ public int doFinal(final byte[] out, final 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();
+ }
+
+ long f0, f1, f2, f3;
+
+ int b = h0 >>> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >>> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >>> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >>> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >>> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5;
+
+ int g0, g1, g2, g3, g4;
+ g0 = h0 + 5; b = g0 >>> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >>> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >>> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >>> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >>> 31) - 1;
+ int nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = (((h0 ) | (h1 << 26)) & 0xffffffffl) + (0xffffffffL & k0);
+ f1 = (((h1 >>> 6 ) | (h2 << 20)) & 0xffffffffl) + (0xffffffffL & k1);
+ f2 = (((h2 >>> 12) | (h3 << 14)) & 0xffffffffl) + (0xffffffffL & k2);
+ f3 = (((h3 >>> 18) | (h4 << 8 )) & 0xffffffffl) + (0xffffffffL & k3);
+
+ Pack.intToLittleEndian((int)f0, out, outOff);
+ f1 += (f0 >>> 32);
+ Pack.intToLittleEndian((int)f1, out, outOff + 4);
+ f2 += (f1 >>> 32);
+ Pack.intToLittleEndian((int)f2, out, outOff + 8);
+ f3 += (f2 >>> 32);
+ Pack.intToLittleEndian((int)f3, out, outOff + 12);
+
+ reset();
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static final long mul32x32_64(int i1, int i2)
+ {
+ return ((long)i1) * i2;
+ }
+}