Poly1305
Poly1305 message authentication code, designed by D. J. Bernstein.
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Macs
{
public class Poly1305 : IMac
{
private const int BlockSize = 16;
private readonly IBlockCipher cipher;
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;
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) {
ParametersWithIV obj = parameters as ParametersWithIV;
if (obj == null)
throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
nonce = obj.GetIV();
parameters = obj.Parameters;
}
KeyParameter keyParameter = parameters as KeyParameter;
if (keyParameter == null)
throw new ArgumentException("Poly1305 requires a key.");
SetKey(keyParameter, nonce);
Reset();
}
private unsafe void SetKey(KeyParameter keyParameter, byte[] nonce)
{
ReadOnlySpan<byte> key = keyParameter.Key;
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 & 67108863);
r1 = (((num >> 26) | (num2 << 6)) & 67108611);
r2 = (((num2 >> 20) | (num3 << 12)) & 67092735);
r3 = (((num3 >> 14) | (num4 << 18)) & 66076671);
r4 = ((num4 >> 8) & 1048575);
s1 = r1 * 5;
s2 = r2 * 5;
s3 = r3 * 5;
s4 = r4 * 5;
if (cipher == null) {
k0 = Pack.LE_To_UInt32(key, 16);
k1 = Pack.LE_To_UInt32(key, 20);
k2 = Pack.LE_To_UInt32(key, 24);
k3 = Pack.LE_To_UInt32(key, 28);
} else {
Span<byte> span = new Span<byte>(stackalloc byte[16], 16);
cipher.Init(true, new KeyParameter(key.Slice(16, 16)));
cipher.ProcessBlock(nonce, span);
k0 = Pack.LE_To_UInt32(span, 0);
k1 = Pack.LE_To_UInt32(span, 4);
k2 = Pack.LE_To_UInt32(span, 8);
k3 = Pack.LE_To_UInt32(span, 12);
}
}
public int GetMacSize()
{
return 16;
}
public void Update(byte input)
{
currentBlock[currentBlockOffset++] = input;
if (currentBlockOffset == 16) {
ProcessBlock(currentBlock);
currentBlockOffset = 0;
}
}
public void BlockUpdate(byte[] input, int inOff, int len)
{
Check.DataLength(input, inOff, len, "input buffer too short");
BlockUpdate(input.AsSpan(inOff, len));
}
public void BlockUpdate(ReadOnlySpan<byte> input)
{
int num = 16 - currentBlockOffset;
if (input.Length < num) {
input.CopyTo(currentBlock.AsSpan(currentBlockOffset));
currentBlockOffset += input.Length;
} else {
int num2 = 0;
ReadOnlySpan<byte> readOnlySpan;
if (currentBlockOffset > 0) {
readOnlySpan = input.Slice(0, num);
readOnlySpan.CopyTo(currentBlock.AsSpan(currentBlockOffset));
num2 = num;
ProcessBlock(currentBlock);
}
int num3;
int num4;
while ((num3 = input.Length - num2) >= 16) {
num4 = num2;
ProcessBlock(input.Slice(num4, input.Length - num4));
num2 += 16;
}
num4 = num2;
readOnlySpan = input.Slice(num4, input.Length - num4);
readOnlySpan.CopyTo(currentBlock);
currentBlockOffset = num3;
}
}
private void ProcessBlock(ReadOnlySpan<byte> block)
{
uint num = Pack.LE_To_UInt32(block);
uint num2 = Pack.LE_To_UInt32(block.Slice(4, block.Length - 4));
uint num3 = Pack.LE_To_UInt32(block.Slice(8, block.Length - 8));
uint num4 = Pack.LE_To_UInt32(block.Slice(12, block.Length - 12));
h0 += (num & 67108863);
h1 += (((num2 << 6) | (num >> 26)) & 67108863);
h2 += (((num3 << 12) | (num2 >> 20)) & 67108863);
h3 += (((num4 << 18) | (num3 >> 14)) & 67108863);
h4 += (16777216 | (num4 >> 8));
ulong num5 = (ulong)((long)h0 * (long)r0 + (long)h1 * (long)s4 + (long)h2 * (long)s3 + (long)h3 * (long)s2 + (long)h4 * (long)s1);
ulong num6 = (ulong)((long)h0 * (long)r1 + (long)h1 * (long)r0 + (long)h2 * (long)s4 + (long)h3 * (long)s3 + (long)h4 * (long)s2);
ulong num7 = (ulong)((long)h0 * (long)r2 + (long)h1 * (long)r1 + (long)h2 * (long)r0 + (long)h3 * (long)s4 + (long)h4 * (long)s3);
ulong num8 = (ulong)((long)h0 * (long)r3 + (long)h1 * (long)r2 + (long)h2 * (long)r1 + (long)h3 * (long)r0 + (long)h4 * (long)s4);
ulong num9 = (ulong)((long)h0 * (long)r4 + (long)h1 * (long)r3 + (long)h2 * (long)r2 + (long)h3 * (long)r1 + (long)h4 * (long)r0);
h0 = (uint)((int)num5 & 67108863);
num6 += num5 >> 26;
h1 = (uint)((int)num6 & 67108863);
num7 += num6 >> 26;
h2 = (uint)((int)num7 & 67108863);
num8 += num7 >> 26;
h3 = (uint)((int)num8 & 67108863);
num9 += num8 >> 26;
h4 = (uint)((int)num9 & 67108863);
h0 += (uint)((int)(num9 >> 26) * 5);
h1 += h0 >> 26;
h0 &= 67108863;
}
public int DoFinal(byte[] output, int outOff)
{
return DoFinal(output.AsSpan(outOff));
}
public int DoFinal(Span<byte> output)
{
Check.OutputLength(output, 16, "output buffer too short.");
if (currentBlockOffset > 0) {
if (currentBlockOffset < 16) {
currentBlock[currentBlockOffset++] = 1;
while (currentBlockOffset < 16) {
currentBlock[currentBlockOffset++] = 0;
}
h4 -= 16777216;
}
ProcessBlock(currentBlock);
}
h0 += 5;
h1 += h0 >> 26;
h0 &= 67108863;
h2 += h1 >> 26;
h1 &= 67108863;
h3 += h2 >> 26;
h2 &= 67108863;
h4 += h3 >> 26;
h3 &= 67108863;
long num = (int)(((h4 >> 26) - 1) * 5) + ((long)k0 + (long)(h0 | (h1 << 26)));
Pack.UInt32_To_LE((uint)num, output);
long num2 = (num >> 32) + ((long)k1 + (long)((h1 >> 6) | (h2 << 20)));
Pack.UInt32_To_LE((uint)num2, output.Slice(4, output.Length - 4));
long num3 = (num2 >> 32) + ((long)k2 + (long)((h2 >> 12) | (h3 << 14)));
Pack.UInt32_To_LE((uint)num3, output.Slice(8, output.Length - 8));
Pack.UInt32_To_LE((uint)((num3 >> 32) + ((long)k3 + (long)((h3 >> 18) | (h4 << 8)))), output.Slice(12, output.Length - 12));
Reset();
return 16;
}
public void Reset()
{
currentBlockOffset = 0;
h0 = (h1 = (h2 = (h3 = (h4 = 0))));
}
}
}