using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; namespace Org.BouncyCastle.Crypto.Macs; public class Poly1305 : IMac { private const int BlockSize = 16; private readonly IBlockCipher cipher; private readonly byte[] singleByte = new byte[1]; private uint r0; private uint r1; private uint r2; private uint r3; private uint r4; private uint s1; private uint s2; private uint s3; private uint s4; private uint k0; private uint k1; private uint k2; private uint k3; private byte[] currentBlock = new byte[16]; private int currentBlockOffset = 0; private uint h0; private uint h1; private uint h2; private uint h3; private uint h4; public string AlgorithmName { get { if (cipher != null) { return "Poly1305-" + cipher.AlgorithmName; } return "Poly1305"; } } public Poly1305() { cipher = null; } public Poly1305(IBlockCipher cipher) { if (cipher.GetBlockSize() != 16) { throw new ArgumentException("Poly1305 requires a 128 bit block cipher."); } this.cipher = cipher; } public void Init(ICipherParameters parameters) { byte[] nonce = null; if (cipher != null) { if (!(parameters is ParametersWithIV)) { throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters"); } ParametersWithIV parametersWithIV = (ParametersWithIV)parameters; nonce = parametersWithIV.GetIV(); parameters = parametersWithIV.Parameters; } if (!(parameters is KeyParameter)) { throw new ArgumentException("Poly1305 requires a key."); } KeyParameter keyParameter = (KeyParameter)parameters; SetKey(keyParameter.GetKey(), nonce); Reset(); } private void SetKey(byte[] key, byte[] nonce) { if (key.Length != 32) { throw new ArgumentException("Poly1305 key must be 256 bits."); } if (cipher != null && (nonce == null || nonce.Length != 16)) { throw new ArgumentException("Poly1305 requires a 128 bit IV."); } uint num = Pack.LE_To_UInt32(key, 0); uint num2 = Pack.LE_To_UInt32(key, 4); uint num3 = Pack.LE_To_UInt32(key, 8); uint num4 = Pack.LE_To_UInt32(key, 12); r0 = num & 0x3FFFFFF; r1 = ((num >> 26) | (num2 << 6)) & 0x3FFFF03; r2 = ((num2 >> 20) | (num3 << 12)) & 0x3FFC0FF; r3 = ((num3 >> 14) | (num4 << 18)) & 0x3F03FFF; r4 = (num4 >> 8) & 0xFFFFF; s1 = r1 * 5; s2 = r2 * 5; s3 = r3 * 5; s4 = r4 * 5; byte[] array; int num5; if (cipher == null) { array = key; num5 = 16; } else { array = new byte[16]; num5 = 0; cipher.Init(forEncryption: true, new KeyParameter(key, 16, 16)); cipher.ProcessBlock(nonce, 0, array, 0); } k0 = Pack.LE_To_UInt32(array, num5); k1 = Pack.LE_To_UInt32(array, num5 + 4); k2 = Pack.LE_To_UInt32(array, num5 + 8); k3 = Pack.LE_To_UInt32(array, num5 + 12); } public int GetMacSize() { return 16; } public void Update(byte input) { singleByte[0] = input; BlockUpdate(singleByte, 0, 1); } public void BlockUpdate(byte[] input, int inOff, int len) { int num = 0; while (len > num) { if (currentBlockOffset == 16) { ProcessBlock(); currentBlockOffset = 0; } int num2 = System.Math.Min(len - num, 16 - currentBlockOffset); Array.Copy(input, num + inOff, currentBlock, currentBlockOffset, num2); num += num2; currentBlockOffset += num2; } } private void ProcessBlock() { if (currentBlockOffset < 16) { currentBlock[currentBlockOffset] = 1; for (int i = currentBlockOffset + 1; i < 16; i++) { currentBlock[i] = 0; } } ulong num = Pack.LE_To_UInt32(currentBlock, 0); ulong num2 = Pack.LE_To_UInt32(currentBlock, 4); ulong num3 = Pack.LE_To_UInt32(currentBlock, 8); ulong num4 = Pack.LE_To_UInt32(currentBlock, 12); h0 += (uint)(int)(num & 0x3FFFFFF); h1 += (uint)(int)((((num2 << 32) | num) >> 26) & 0x3FFFFFF); h2 += (uint)(int)((((num3 << 32) | num2) >> 20) & 0x3FFFFFF); h3 += (uint)(int)((((num4 << 32) | num3) >> 14) & 0x3FFFFFF); h4 += (uint)(int)(num4 >> 8); if (currentBlockOffset == 16) { h4 += 16777216u; } ulong num5 = mul32x32_64(h0, r0) + mul32x32_64(h1, s4) + mul32x32_64(h2, s3) + mul32x32_64(h3, s2) + mul32x32_64(h4, s1); ulong num6 = mul32x32_64(h0, r1) + mul32x32_64(h1, r0) + mul32x32_64(h2, s4) + mul32x32_64(h3, s3) + mul32x32_64(h4, s2); ulong num7 = mul32x32_64(h0, r2) + mul32x32_64(h1, r1) + mul32x32_64(h2, r0) + mul32x32_64(h3, s4) + mul32x32_64(h4, s3); ulong num8 = mul32x32_64(h0, r3) + mul32x32_64(h1, r2) + mul32x32_64(h2, r1) + mul32x32_64(h3, r0) + mul32x32_64(h4, s4); ulong num9 = mul32x32_64(h0, r4) + mul32x32_64(h1, r3) + mul32x32_64(h2, r2) + mul32x32_64(h3, r1) + mul32x32_64(h4, r0); h0 = (uint)((int)num5 & 0x3FFFFFF); num6 += num5 >> 26; h1 = (uint)((int)num6 & 0x3FFFFFF); num7 += num6 >> 26; h2 = (uint)((int)num7 & 0x3FFFFFF); num8 += num7 >> 26; h3 = (uint)((int)num8 & 0x3FFFFFF); num9 += num8 >> 26; h4 = (uint)((int)num9 & 0x3FFFFFF); h0 += (uint)((int)(num9 >> 26) * 5); h1 += h0 >> 26; h0 &= 67108863u; } public int DoFinal(byte[] output, int outOff) { Check.DataLength(output, outOff, 16, "Output buffer is too short."); if (currentBlockOffset > 0) { ProcessBlock(); } h1 += h0 >> 26; h0 &= 67108863u; h2 += h1 >> 26; h1 &= 67108863u; h3 += h2 >> 26; h2 &= 67108863u; h4 += h3 >> 26; h3 &= 67108863u; h0 += (h4 >> 26) * 5; h4 &= 67108863u; h1 += h0 >> 26; h0 &= 67108863u; uint num = h0 + 5; uint num2 = num >> 26; num &= 0x3FFFFFF; uint num3 = h1 + num2; num2 = num3 >> 26; num3 &= 0x3FFFFFF; uint num4 = h2 + num2; num2 = num4 >> 26; num4 &= 0x3FFFFFF; uint num5 = h3 + num2; num2 = num5 >> 26; num5 &= 0x3FFFFFF; uint num6 = h4 + num2 - 67108864; num2 = (num6 >> 31) - 1; uint num7 = ~num2; h0 = (h0 & num7) | (num & num2); h1 = (h1 & num7) | (num3 & num2); h2 = (h2 & num7) | (num4 & num2); h3 = (h3 & num7) | (num5 & num2); h4 = (h4 & num7) | (num6 & num2); ulong num8 = (ulong)(h0 | (h1 << 26)) + (ulong)k0; ulong num9 = (ulong)((h1 >> 6) | (h2 << 20)) + (ulong)k1; ulong num10 = (ulong)((h2 >> 12) | (h3 << 14)) + (ulong)k2; ulong num11 = (ulong)((h3 >> 18) | (h4 << 8)) + (ulong)k3; Pack.UInt32_To_LE((uint)num8, output, outOff); num9 += num8 >> 32; Pack.UInt32_To_LE((uint)num9, output, outOff + 4); num10 += num9 >> 32; Pack.UInt32_To_LE((uint)num10, output, outOff + 8); num11 += num10 >> 32; Pack.UInt32_To_LE((uint)num11, output, outOff + 12); Reset(); return 16; } public void Reset() { currentBlockOffset = 0; h0 = (h1 = (h2 = (h3 = (h4 = 0u)))); } private static ulong mul32x32_64(uint i1, uint i2) { return (ulong)i1 * (ulong)i2; } }