diff --git a/crypto/src/crypto/generators/Poly1305KeyGenerator.cs b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
new file mode 100644
index 000000000..f2aa85262
--- /dev/null
+++ b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
@@ -0,0 +1,122 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Generators
+{
+ /// <summary>
+ /// Generates keys for the Poly1305 MAC.
+ /// </summary>
+ /// <remarks>
+ /// Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block
+ /// cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac. <br>
+ /// The {@code r} value has a specific format with some bits required to be cleared, resulting in an
+ /// effective 106 bit key. <br>
+ /// A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the
+ /// {@link #clamp(byte[])} method to clear the required bits.
+ /// </remarks>
+ /// <seealso cref="Org.Bouncycastle.Crypto.Macs.Poly1305"/>
+ public class Poly1305KeyGenerator
+ : CipherKeyGenerator
+ {
+ private const byte R_MASK_LOW_2 = (byte)0xFC;
+ private const byte R_MASK_HIGH_4 = (byte)0x0F;
+
+ /// <summary>
+ /// Initialises the key generator.
+ /// </summary>
+ /// <remarks>
+ /// Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored.
+ /// </remarks>
+ protected override void engineInit(KeyGenerationParameters param)
+ {
+ // Poly1305 keys are always 256 bits
+ this.random = param.Random;
+ this.strength = 32;
+ }
+
+ /// <summary>
+ /// Generates a 256 bit key in the format required for Poly1305 - e.g.
+ /// <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+ /// as per <see cref="Clamp(byte[])"/>.
+ /// </summary>
+ protected override byte[] engineGenerateKey()
+ {
+ byte[] key = base.engineGenerateKey();
+ Clamp(key);
+ return key;
+ }
+
+ /// <summary>
+ /// Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
+ /// clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br>
+ /// Specifically:
+ /// <ul>
+ /// <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
+ /// <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
+ /// </ul>
+ /// </summary>
+ /// <param name="key">a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code></param>
+ public static void Clamp(byte[] key)
+ {
+ /*
+ * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
+ */
+ if (key.Length != 32)
+ {
+ throw new ArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ /*
+ * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
+ */
+ key[19] &= R_MASK_HIGH_4;
+ key[23] &= R_MASK_HIGH_4;
+ key[27] &= R_MASK_HIGH_4;
+ key[31] &= R_MASK_HIGH_4;
+
+ /*
+ * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
+ */
+ key[20] &= R_MASK_LOW_2;
+ key[24] &= R_MASK_LOW_2;
+ key[28] &= R_MASK_LOW_2;
+ }
+
+ /// <summary>
+ /// Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
+ /// <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+ /// as per <see cref="Clamp(byte[])"/>.
+ /// </summary>
+ /// <param name="key">Key.</param>
+ /// <exception cref="System.ArgumentException">if the key is of the wrong length, or has invalid bits set
+ /// in the <code>r</code> portion of the key.</exception>
+ public static void CheckKey(byte[] key)
+ {
+ if (key.Length != 32)
+ {
+ throw new ArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ checkMask(key[19], R_MASK_HIGH_4);
+ checkMask(key[23], R_MASK_HIGH_4);
+ checkMask(key[27], R_MASK_HIGH_4);
+ checkMask(key[31], R_MASK_HIGH_4);
+
+ checkMask(key[20], R_MASK_LOW_2);
+ checkMask(key[24], R_MASK_LOW_2);
+ checkMask(key[28], R_MASK_LOW_2);
+ }
+
+ private static void checkMask(byte b, byte mask)
+ {
+ if ((b & (~mask)) != 0)
+ {
+ throw new ArgumentException("Invalid format for r portion of Poly1305 key.");
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
new file mode 100644
index 000000000..2d453b6ad
--- /dev/null
+++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -0,0 +1,272 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+
+ /// <summary>
+ /// Poly1305 message authentication code, designed by D. J. Bernstein.
+ /// </summary>
+ /// <remarks>
+ /// 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.
+ ///
+ /// 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).
+ /// </remarks>
+ /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
+ public class Poly1305
+ : IMac
+ {
+ private const int BLOCK_SIZE = 16;
+
+ private readonly IBlockCipher cipher;
+
+ private readonly byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private uint r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private uint s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private uint k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private uint h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(IBlockCipher cipher)
+ {
+ if (cipher.GetBlockSize() != BLOCK_SIZE)
+ {
+ throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /// <summary>
+ /// Initialises the Poly1305 MAC.
+ /// </summary>
+ /// <param name="parameters">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}.</param>
+ public void Init(ICipherParameters parameters)
+ {
+ byte[] nonce;
+ byte[] key;
+ if ((parameters is ParametersWithIV) && ((ParametersWithIV)parameters).Parameters is KeyParameter)
+ {
+ nonce = ((ParametersWithIV)parameters).GetIV();
+ key = ((KeyParameter)((ParametersWithIV)parameters).Parameters).GetKey();
+ }
+ else
+ {
+ throw new ArgumentException("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 ArgumentException("Poly1305 requires a 128 bit IV.");
+ }
+ Poly1305KeyGenerator.CheckKey(key);
+
+ // Extract r portion of key
+ uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0);
+ uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4);
+ uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8);
+ uint t3 = Pack.LE_To_UInt32(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
+ byte[] cipherKey = new byte[BLOCK_SIZE];
+ Array.Copy(key, 0, cipherKey, 0, cipherKey.Length);
+
+ cipher.Init(true, new KeyParameter(cipherKey));
+ cipher.ProcessBlock(nonce, 0, cipherKey, 0);
+
+ k0 = Pack.LE_To_UInt32(cipherKey, 0);
+ k1 = Pack.LE_To_UInt32(cipherKey, 4);
+ k2 = Pack.LE_To_UInt32(cipherKey, 8);
+ k3 = Pack.LE_To_UInt32(cipherKey, 12);
+ }
+
+ public string AlgorithmName
+ {
+ get { return "Poly1305-" + cipher.AlgorithmName; }
+ }
+
+ public int GetMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void Update(byte input)
+ {
+ singleByte[0] = input;
+ BlockUpdate(singleByte, 0, 1);
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ Array.Copy(input, 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;
+ }
+ }
+
+ ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
+ ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
+ ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
+ ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
+
+ h0 += (uint)(t0 & 0x3ffffffU);
+ h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
+ h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
+ h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
+ h4 += (uint)(t3 >> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ ulong b;
+ h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26);
+ tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26);
+ tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26);
+ tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26);
+ tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26);
+ h0 += (uint)(b * 5);
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ if (outOff + BLOCK_SIZE > output.Length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded block
+ processBlock();
+ }
+
+ ulong f0, f1, f2, f3;
+
+ uint 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;
+
+ uint 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;
+ uint 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)) + (ulong)k0;
+ f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
+ f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
+ f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
+
+ Pack.UInt32_To_LE((uint)f0, output, outOff);
+ f1 += (f0 >> 32);
+ Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
+ f2 += (f1 >> 32);
+ Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
+ f3 += (f2 >> 32);
+ Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
+
+ Reset();
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static ulong mul32x32_64(uint i1, uint i2)
+ {
+ return ((ulong)i1) * i2;
+ }
+ }
+}
|