Skip to content

Instantly share code, notes, and snippets.

@hodzanassredin
Created July 30, 2023 20:02
Show Gist options
  • Save hodzanassredin/1c6e26b781993e4cb48aea0e3646a09a to your computer and use it in GitHub Desktop.
Save hodzanassredin/1c6e26b781993e4cb48aea0e3646a09a to your computer and use it in GitHub Desktop.
GCM mode single file, pure C# based on https://github.com/bcgit/bc-csharp
using System;
using System.Diagnostics;
using System.Numerics;
using System.Text;
namespace Org.BouncyCastle.Crypto.Modes
{
internal static class Check
{
internal static void DataLength(bool condition, string message)
{
if (condition)
ThrowDataLengthException(message);
}
internal static void DataLength(byte[] buf, int off, int len, string message)
{
if (off > (buf.Length - len))
ThrowDataLengthException(message);
}
internal static void OutputLength(bool condition, string message)
{
if (condition)
ThrowOutputLengthException(message);
}
internal static void OutputLength(byte[] buf, int off, int len, string message)
{
if (off > (buf.Length - len))
ThrowOutputLengthException(message);
}
internal static void ThrowDataLengthException(string message) => throw new Exception(message);
internal static void ThrowOutputLengthException(string message) => throw new Exception(message);
}
internal static class Pack
{
internal static void UInt16_To_BE(ushort n, byte[] bs)
{
bs[0] = (byte)(n >> 8);
bs[1] = (byte)n;
}
internal static void UInt16_To_BE(ushort n, byte[] bs, int off)
{
bs[off] = (byte)(n >> 8);
bs[off + 1] = (byte)n;
}
internal static void UInt16_To_BE(ushort[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt16_To_BE(ns[i], bs, off);
off += 2;
}
}
internal static void UInt16_To_BE(ushort[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt16_To_BE(ns[nsOff + i], bs, bsOff);
bsOff += 2;
}
}
internal static byte[] UInt16_To_BE(ushort n)
{
byte[] bs = new byte[2];
UInt16_To_BE(n, bs, 0);
return bs;
}
internal static byte[] UInt16_To_BE(ushort[] ns)
{
return UInt16_To_BE(ns, 0, ns.Length);
}
internal static byte[] UInt16_To_BE(ushort[] ns, int nsOff, int nsLen)
{
byte[] bs = new byte[2 * nsLen];
UInt16_To_BE(ns, nsOff, nsLen, bs, 0);
return bs;
}
internal static ushort BE_To_UInt16(byte[] bs, int off)
{
uint n = (uint)bs[off] << 8
| bs[off + 1];
return (ushort)n;
}
internal static void BE_To_UInt16(byte[] bs, int bsOff, ushort[] ns, int nsOff)
{
ns[nsOff] = BE_To_UInt16(bs, bsOff);
}
internal static ushort[] BE_To_UInt16(byte[] bs)
{
return BE_To_UInt16(bs, 0, bs.Length);
}
internal static ushort[] BE_To_UInt16(byte[] bs, int off, int len)
{
if ((len & 1) != 0)
throw new ArgumentException("must be a multiple of 2", "len");
ushort[] ns = new ushort[len / 2];
for (int i = 0; i < len; i += 2)
{
BE_To_UInt16(bs, off + i, ns, i >> 1);
}
return ns;
}
internal static void UInt24_To_BE(uint n, byte[] bs)
{
bs[0] = (byte)(n >> 16);
bs[1] = (byte)(n >> 8);
bs[2] = (byte)n;
}
internal static void UInt24_To_BE(uint n, byte[] bs, int off)
{
bs[off + 0] = (byte)(n >> 16);
bs[off + 1] = (byte)(n >> 8);
bs[off + 2] = (byte)n;
}
internal static uint BE_To_UInt24(byte[] bs)
{
return (uint)bs[0] << 16
| (uint)bs[1] << 8
| bs[2];
}
internal static uint BE_To_UInt24(byte[] bs, int off)
{
return (uint)bs[off] << 16
| (uint)bs[off + 1] << 8
| bs[off + 2];
}
internal static void UInt32_To_BE(uint n, byte[] bs)
{
bs[0] = (byte)(n >> 24);
bs[1] = (byte)(n >> 16);
bs[2] = (byte)(n >> 8);
bs[3] = (byte)n;
}
internal static void UInt32_To_BE(uint n, byte[] bs, int off)
{
bs[off] = (byte)(n >> 24);
bs[off + 1] = (byte)(n >> 16);
bs[off + 2] = (byte)(n >> 8);
bs[off + 3] = (byte)n;
}
internal static void UInt32_To_BE_High(uint n, byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 4);
int pos = 24;
bs[off] = (byte)(n >> pos);
for (int i = 1; i < len; ++i)
{
pos -= 8;
bs[off + i] = (byte)(n >> pos);
}
}
internal static void UInt32_To_BE_Low(uint n, byte[] bs, int off, int len)
{
UInt32_To_BE_High(n << ((4 - len) << 3), bs, off, len);
}
internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt32_To_BE(ns[i], bs, off);
off += 4;
}
}
internal static void UInt32_To_BE(uint[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt32_To_BE(ns[nsOff + i], bs, bsOff);
bsOff += 4;
}
}
internal static byte[] UInt32_To_BE(uint n)
{
byte[] bs = new byte[4];
UInt32_To_BE(n, bs, 0);
return bs;
}
internal static byte[] UInt32_To_BE(uint[] ns)
{
byte[] bs = new byte[4 * ns.Length];
UInt32_To_BE(ns, bs, 0);
return bs;
}
internal static uint BE_To_UInt32(byte[] bs)
{
return (uint)bs[0] << 24
| (uint)bs[1] << 16
| (uint)bs[2] << 8
| bs[3];
}
internal static uint BE_To_UInt32(byte[] bs, int off)
{
return (uint)bs[off] << 24
| (uint)bs[off + 1] << 16
| (uint)bs[off + 2] << 8
| bs[off + 3];
}
internal static uint BE_To_UInt32_High(byte[] bs, int off, int len)
{
return BE_To_UInt32_Low(bs, off, len) << ((4 - len) << 3);
}
internal static uint BE_To_UInt32_Low(byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 4);
uint result = bs[off];
for (int i = 1; i < len; ++i)
{
result <<= 8;
result |= bs[off + i];
}
return result;
}
internal static void BE_To_UInt32(byte[] bs, int off, uint[] ns)
{
for (int i = 0; i < ns.Length; ++i)
{
ns[i] = BE_To_UInt32(bs, off);
off += 4;
}
}
internal static void BE_To_UInt32(byte[] bs, int bsOff, uint[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = BE_To_UInt32(bs, bsOff);
bsOff += 4;
}
}
internal static byte[] UInt64_To_BE(ulong n)
{
byte[] bs = new byte[8];
UInt64_To_BE(n, bs, 0);
return bs;
}
internal static void UInt64_To_BE(ulong n, byte[] bs)
{
UInt32_To_BE((uint)(n >> 32), bs);
UInt32_To_BE((uint)n, bs, 4);
}
internal static void UInt64_To_BE(ulong n, byte[] bs, int off)
{
UInt32_To_BE((uint)(n >> 32), bs, off);
UInt32_To_BE((uint)n, bs, off + 4);
}
internal static void UInt64_To_BE_High(ulong n, byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 8);
int pos = 56;
bs[off] = (byte)(n >> pos);
for (int i = 1; i < len; ++i)
{
pos -= 8;
bs[off + i] = (byte)(n >> pos);
}
}
internal static void UInt64_To_BE_Low(ulong n, byte[] bs, int off, int len)
{
UInt64_To_BE_High(n << ((8 - len) << 3), bs, off, len);
}
internal static byte[] UInt64_To_BE(ulong[] ns)
{
byte[] bs = new byte[8 * ns.Length];
UInt64_To_BE(ns, bs, 0);
return bs;
}
internal static void UInt64_To_BE(ulong[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt64_To_BE(ns[i], bs, off);
off += 8;
}
}
internal static void UInt64_To_BE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt64_To_BE(ns[nsOff + i], bs, bsOff);
bsOff += 8;
}
}
internal static ulong BE_To_UInt64(byte[] bs)
{
uint hi = BE_To_UInt32(bs);
uint lo = BE_To_UInt32(bs, 4);
return ((ulong)hi << 32) | (ulong)lo;
}
internal static ulong BE_To_UInt64(byte[] bs, int off)
{
uint hi = BE_To_UInt32(bs, off);
uint lo = BE_To_UInt32(bs, off + 4);
return ((ulong)hi << 32) | (ulong)lo;
}
internal static ulong BE_To_UInt64_High(byte[] bs, int off, int len)
{
return BE_To_UInt64_Low(bs, off, len) << ((8 - len) << 3);
}
internal static ulong BE_To_UInt64_Low(byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 8);
ulong result = bs[off];
for (int i = 1; i < len; ++i)
{
result <<= 8;
result |= bs[off + i];
}
return result;
}
internal static void BE_To_UInt64(byte[] bs, int off, ulong[] ns)
{
for (int i = 0; i < ns.Length; ++i)
{
ns[i] = BE_To_UInt64(bs, off);
off += 8;
}
}
internal static void BE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = BE_To_UInt64(bs, bsOff);
bsOff += 8;
}
}
internal static void UInt16_To_LE(ushort n, byte[] bs)
{
bs[0] = (byte)n;
bs[1] = (byte)(n >> 8);
}
internal static void UInt16_To_LE(ushort n, byte[] bs, int off)
{
bs[off] = (byte)n;
bs[off + 1] = (byte)(n >> 8);
}
internal static byte[] UInt16_To_LE(ushort n)
{
byte[] bs = new byte[2];
UInt16_To_LE(n, bs, 0);
return bs;
}
internal static byte[] UInt16_To_LE(ushort[] ns)
{
byte[] bs = new byte[2 * ns.Length];
UInt16_To_LE(ns, bs, 0);
return bs;
}
internal static void UInt16_To_LE(ushort[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt16_To_LE(ns[i], bs, off);
off += 2;
}
}
internal static void UInt16_To_LE(ushort[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt16_To_LE(ns[nsOff + i], bs, bsOff);
bsOff += 2;
}
}
internal static ushort LE_To_UInt16(byte[] bs)
{
uint n = bs[0]
| (uint)bs[1] << 8;
return (ushort)n;
}
internal static ushort LE_To_UInt16(byte[] bs, int off)
{
uint n = bs[off]
| (uint)bs[off + 1] << 8;
return (ushort)n;
}
internal static void LE_To_UInt16(byte[] bs, int off, ushort[] ns)
{
for (int i = 0; i < ns.Length; ++i)
{
ns[i] = LE_To_UInt16(bs, off);
off += 2;
}
}
internal static void LE_To_UInt16(byte[] bs, int bOff, ushort[] ns, int nOff, int count)
{
for (int i = 0; i < count; ++i)
{
ns[nOff + i] = LE_To_UInt16(bs, bOff);
bOff += 2;
}
}
internal static ushort[] LE_To_UInt16(byte[] bs, int off, int count)
{
ushort[] ns = new ushort[count];
LE_To_UInt16(bs, off, ns);
return ns;
}
internal static byte[] UInt32_To_LE(uint n)
{
byte[] bs = new byte[4];
UInt32_To_LE(n, bs, 0);
return bs;
}
internal static void UInt32_To_LE(uint n, byte[] bs)
{
bs[0] = (byte)n;
bs[1] = (byte)(n >> 8);
bs[2] = (byte)(n >> 16);
bs[3] = (byte)(n >> 24);
}
internal static void UInt32_To_LE(uint n, byte[] bs, int off)
{
bs[off] = (byte)n;
bs[off + 1] = (byte)(n >> 8);
bs[off + 2] = (byte)(n >> 16);
bs[off + 3] = (byte)(n >> 24);
}
internal static void UInt32_To_LE_High(uint n, byte[] bs, int off, int len)
{
UInt32_To_LE_Low(n >> ((4 - len) << 3), bs, off, len);
}
internal static void UInt32_To_LE_Low(uint n, byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 4);
bs[off] = (byte)n;
for (int i = 1; i < len; ++i)
{
n >>= 8;
bs[off + i] = (byte)n;
}
}
internal static byte[] UInt32_To_LE(uint[] ns)
{
byte[] bs = new byte[4 * ns.Length];
UInt32_To_LE(ns, bs, 0);
return bs;
}
internal static void UInt32_To_LE(uint[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt32_To_LE(ns[i], bs, off);
off += 4;
}
}
internal static void UInt32_To_LE(uint[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt32_To_LE(ns[nsOff + i], bs, bsOff);
bsOff += 4;
}
}
internal static uint LE_To_UInt24(byte[] bs, int off)
{
return bs[off]
| (uint)bs[off + 1] << 8
| (uint)bs[off + 2] << 16;
}
internal static uint LE_To_UInt32(byte[] bs)
{
return bs[0]
| (uint)bs[1] << 8
| (uint)bs[2] << 16
| (uint)bs[3] << 24;
}
internal static uint LE_To_UInt32(byte[] bs, int off)
{
return bs[off]
| (uint)bs[off + 1] << 8
| (uint)bs[off + 2] << 16
| (uint)bs[off + 3] << 24;
}
internal static uint LE_To_UInt32_High(byte[] bs, int off, int len)
{
return LE_To_UInt32_Low(bs, off, len) << ((4 - len) << 3);
}
internal static uint LE_To_UInt32_Low(byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 4);
uint result = bs[off];
int pos = 0;
for (int i = 1; i < len; ++i)
{
pos += 8;
result |= (uint)bs[off + i] << pos;
}
return result;
}
internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns)
{
for (int i = 0; i < ns.Length; ++i)
{
ns[i] = LE_To_UInt32(bs, off);
off += 4;
}
}
internal static void LE_To_UInt32(byte[] bs, int bOff, uint[] ns, int nOff, int count)
{
for (int i = 0; i < count; ++i)
{
ns[nOff + i] = LE_To_UInt32(bs, bOff);
bOff += 4;
}
}
internal static uint[] LE_To_UInt32(byte[] bs, int off, int count)
{
uint[] ns = new uint[count];
LE_To_UInt32(bs, off, ns);
return ns;
}
internal static byte[] UInt64_To_LE(ulong n)
{
byte[] bs = new byte[8];
UInt64_To_LE(n, bs, 0);
return bs;
}
internal static void UInt64_To_LE(ulong n, byte[] bs)
{
UInt32_To_LE((uint)n, bs);
UInt32_To_LE((uint)(n >> 32), bs, 4);
}
internal static void UInt64_To_LE(ulong n, byte[] bs, int off)
{
UInt32_To_LE((uint)n, bs, off);
UInt32_To_LE((uint)(n >> 32), bs, off + 4);
}
internal static void UInt64_To_LE_High(ulong n, byte[] bs, int off, int len)
{
UInt64_To_LE_Low(n >> ((8 - len) << 3), bs, off, len);
}
internal static void UInt64_To_LE_Low(ulong n, byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 8);
bs[off] = (byte)n;
for (int i = 1; i < len; ++i)
{
n >>= 8;
bs[off + i] = (byte)n;
}
}
internal static byte[] UInt64_To_LE(ulong[] ns)
{
byte[] bs = new byte[8 * ns.Length];
UInt64_To_LE(ns, bs, 0);
return bs;
}
internal static void UInt64_To_LE(ulong[] ns, byte[] bs, int off)
{
for (int i = 0; i < ns.Length; ++i)
{
UInt64_To_LE(ns[i], bs, off);
off += 8;
}
}
internal static void UInt64_To_LE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
{
for (int i = 0; i < nsLen; ++i)
{
UInt64_To_LE(ns[nsOff + i], bs, bsOff);
bsOff += 8;
}
}
internal static ulong LE_To_UInt64(byte[] bs)
{
uint lo = LE_To_UInt32(bs);
uint hi = LE_To_UInt32(bs, 4);
return ((ulong)hi << 32) | (ulong)lo;
}
internal static ulong LE_To_UInt64(byte[] bs, int off)
{
uint lo = LE_To_UInt32(bs, off);
uint hi = LE_To_UInt32(bs, off + 4);
return ((ulong)hi << 32) | (ulong)lo;
}
internal static ulong LE_To_UInt64_High(byte[] bs, int off, int len)
{
return LE_To_UInt64_Low(bs, off, len) << ((8 - len) << 3);
}
internal static ulong LE_To_UInt64_Low(byte[] bs, int off, int len)
{
Debug.Assert(1 <= len && len <= 8);
ulong result = bs[off];
int pos = 0;
for (int i = 1; i < len; ++i)
{
pos += 8;
result |= (ulong)bs[off + i] << pos;
}
return result;
}
internal static void LE_To_UInt64(byte[] bs, int off, ulong[] ns)
{
for (int i = 0; i < ns.Length; ++i)
{
ns[i] = LE_To_UInt64(bs, off);
off += 8;
}
}
internal static void LE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen)
{
for (int i = 0; i < nsLen; ++i)
{
ns[nsOff + i] = LE_To_UInt64(bs, bsOff);
bsOff += 8;
}
}
internal static ulong[] LE_To_UInt64(byte[] bs, int off, int count)
{
ulong[] ns = new ulong[count];
LE_To_UInt64(bs, off, ns);
return ns;
}
}
/// <remarks>Base interface for a symmetric key block cipher.</remarks>
public interface IBlockCipher
{
/// <summary>The name of the algorithm this cipher implements.</summary>
string AlgorithmName { get; }
/// <summary>Initialise the cipher.</summary>
/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
/// <param name="parameters">The key or other data required by the cipher.</param>
void Init(bool forEncryption, ICipherParameters parameters);
/// <returns>The block size for this cipher, in bytes.</returns>
int GetBlockSize();
/// <summary>Process a block.</summary>
/// <param name="inBuf">The input buffer.</param>
/// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
/// <param name="outBuf">The output buffer.</param>
/// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param>
/// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception>
/// <returns>The number of bytes processed and produced.</returns>
int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff);
}
public interface ICipherParameters
{
}
/// <summary>
/// A cipher mode that includes authenticated encryption with a streaming mode and optional
/// associated data.
/// </summary>
/// <remarks>
/// Implementations of this interface may operate in a packet mode (where all input data is
/// buffered and processed during the call to DoFinal, or in a streaming mode (where output
/// data is incrementally produced with each call to ProcessByte or ProcessBytes. This is
/// important to consider during decryption: in a streaming mode, unauthenticated plaintext
/// data may be output prior to the call to DoFinal that results in an authentication failure.
/// The higher level protocol utilising this cipher must ensure the plaintext data is handled
/// appropriately until the end of data is reached and the entire ciphertext is authenticated.
/// </remarks>
/// <see cref="AeadParameters"/>
public interface IAeadCipher
{
/// <summary>The name of the algorithm this cipher implements.</summary>
string AlgorithmName { get; }
/// <summary>Initialise the cipher.</summary>
/// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
/// <param name="parameters">The key or other data required by the cipher.</param>
void Init(bool forEncryption, ICipherParameters parameters);
/// <summary>Add a single byte to the associated data check.</summary>
/// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
/// <param name="input">The byte to be processed.</param>
void ProcessAadByte(byte input);
/// <summary>Add a sequence of bytes to the associated data check.</summary>
/// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
/// <param name="inBytes">The input byte array.</param>
/// <param name="inOff">The offset into the input array where the data to be processed starts.</param>
/// <param name="len">The number of bytes to be processed.</param>
void ProcessAadBytes(byte[] inBytes, int inOff, int len);
/**
* Encrypt/decrypt a single byte.
*
* @param input the byte to be processed.
* @param outBytes the output buffer the processed byte goes into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
int ProcessByte(byte input, byte[] outBytes, int outOff);
/**
* Process a block of bytes from in putting the result into out.
*
* @param inBytes the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
* @param outBytes the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff);
/**
* Finish the operation either appending or verifying the MAC at the end of the data.
*
* @param outBytes space for any resulting output data.
* @param outOff offset into out to start copying the data at.
* @return number of bytes written into out.
* @throws InvalidOperationException if the cipher is in an inappropriate state.
* @throws InvalidCipherTextException if the MAC fails to match.
*/
int DoFinal(byte[] outBytes, int outOff);
/**
* Return the value of the MAC associated with the last stream processed.
*
* @return MAC for plaintext data.
*/
byte[] GetMac();
/**
* Return the size of the output buffer required for a ProcessBytes
* an input of len bytes.
*
* @param len the length of the input.
* @return the space required to accommodate a call to ProcessBytes
* with len bytes of input.
*/
int GetUpdateOutputSize(int len);
/**
* Return the size of the output buffer required for a ProcessBytes plus a
* DoFinal with an input of len bytes.
*
* @param len the length of the input.
* @return the space required to accommodate a call to ProcessBytes and DoFinal
* with len bytes of input.
*/
int GetOutputSize(int len);
/// <summary>
/// Reset the cipher to the same state as it was after the last init (if there was one).
/// </summary>
void Reset();
}
public interface IAeadBlockCipher
: IAeadCipher
{
/// <returns>The block size for this cipher, in bytes.</returns>
int GetBlockSize();
/// <summary>The block cipher underlying this algorithm.</summary>
IBlockCipher UnderlyingCipher { get; }
}
public interface IGcmMultiplier
{
void Init(byte[] H);
void MultiplyH(byte[] x);
}
public static class Integers
{
public const int NumBits = 32;
public const int NumBytes = 4;
private static readonly byte[] DeBruijnTZ = {
0x1F, 0x00, 0x1B, 0x01, 0x1C, 0x0D, 0x17, 0x02, 0x1D, 0x15, 0x13, 0x0E, 0x18, 0x10, 0x03, 0x07,
0x1E, 0x1A, 0x0C, 0x16, 0x14, 0x12, 0x0F, 0x06, 0x19, 0x0B, 0x11, 0x05, 0x0A, 0x04, 0x09, 0x08 };
public static int HighestOneBit(int i)
{
return (int)HighestOneBit((uint)i);
}
[CLSCompliant(false)]
public static uint HighestOneBit(uint i)
{
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
return i - (i >> 1);
}
public static int LowestOneBit(int i)
{
return i & -i;
}
[CLSCompliant(false)]
public static uint LowestOneBit(uint i)
{
return (uint)LowestOneBit((int)i);
}
public static int NumberOfLeadingZeros(int i)
{
if (i <= 0)
return (~i >> (31 - 5)) & (1 << 5);
uint u = (uint)i;
int n = 1;
if (0 == (u >> 16)) { n += 16; u <<= 16; }
if (0 == (u >> 24)) { n += 8; u <<= 8; }
if (0 == (u >> 28)) { n += 4; u <<= 4; }
if (0 == (u >> 30)) { n += 2; u <<= 2; }
n -= (int)(u >> 31);
return n;
}
public static int NumberOfTrailingZeros(int i)
{
int n = DeBruijnTZ[(uint)((i & -i) * 0x0EF96A62) >> 27];
int m = (((i & 0xFFFF) | (int)((uint)i >> 16)) - 1) >> 31;
return n - m;
}
public static int PopCount(int i)
{
return PopCount((uint)i);
}
[CLSCompliant(false)]
public static int PopCount(uint u)
{
u -= (u >> 1) & 0x55555555;
u = (u & 0x33333333) + ((u >> 2) & 0x33333333);
u = (u + (u >> 4)) & 0x0f0f0f0f;
u += (u >> 8);
u += (u >> 16);
u &= 0x3f;
return (int)u;
}
public static int Reverse(int i)
{
return (int)Reverse((uint)i);
}
[CLSCompliant(false)]
public static uint Reverse(uint i)
{
i = Bits.BitPermuteStepSimple(i, 0x55555555U, 1);
i = Bits.BitPermuteStepSimple(i, 0x33333333U, 2);
i = Bits.BitPermuteStepSimple(i, 0x0F0F0F0FU, 4);
return ReverseBytes(i);
}
public static int ReverseBytes(int i)
{
return (int)ReverseBytes((uint)i);
}
[CLSCompliant(false)]
public static uint ReverseBytes(uint i)
{
return RotateLeft(i & 0xFF00FF00U, 8) |
RotateLeft(i & 0x00FF00FFU, 24);
}
public static int RotateLeft(int i, int distance)
{
return (i << distance) | (int)((uint)i >> -distance);
}
[CLSCompliant(false)]
public static uint RotateLeft(uint i, int distance)
{
return (i << distance) | (i >> -distance);
}
public static int RotateRight(int i, int distance)
{
return (int)((uint)i >> distance) | (i << -distance);
}
[CLSCompliant(false)]
public static uint RotateRight(uint i, int distance)
{
return (i >> distance) | (i << -distance);
}
}
internal static class Bits
{
internal static uint BitPermuteStep(uint x, uint m, int s)
{
Debug.Assert((m & (m << s)) == 0U);
Debug.Assert((m << s) >> s == m);
uint t = (x ^ (x >> s)) & m;
return t ^ (t << s) ^ x;
}
internal static ulong BitPermuteStep(ulong x, ulong m, int s)
{
Debug.Assert((m & (m << s)) == 0UL);
Debug.Assert((m << s) >> s == m);
ulong t = (x ^ (x >> s)) & m;
return t ^ (t << s) ^ x;
}
internal static void BitPermuteStep2(ref uint hi, ref uint lo, uint m, int s)
{
Debug.Assert((m << s) >> s == m);
uint t = ((lo >> s) ^ hi) & m;
lo ^= t << s;
hi ^= t;
}
internal static void BitPermuteStep2(ref ulong hi, ref ulong lo, ulong m, int s)
{
Debug.Assert((m << s) >> s == m);
ulong t = ((lo >> s) ^ hi) & m;
lo ^= t << s;
hi ^= t;
}
internal static uint BitPermuteStepSimple(uint x, uint m, int s)
{
Debug.Assert((m << s) == ~m);
Debug.Assert((m & ~m) == 0U);
return ((x & m) << s) | ((x >> s) & m);
}
internal static ulong BitPermuteStepSimple(ulong x, ulong m, int s)
{
Debug.Assert((m << s) == ~m);
Debug.Assert((m & ~m) == 0UL);
return ((x & m) << s) | ((x >> s) & m);
}
}
public static class Longs
{
public const int NumBits = 64;
public const int NumBytes = 8;
private static readonly byte[] DeBruijnTZ = {
0x3F, 0x00, 0x01, 0x34, 0x02, 0x06, 0x35, 0x1A, 0x03, 0x25, 0x28, 0x07, 0x21, 0x36, 0x2F, 0x1B,
0x3D, 0x04, 0x26, 0x2D, 0x2B, 0x29, 0x15, 0x08, 0x17, 0x22, 0x3A, 0x37, 0x30, 0x11, 0x1C, 0x0A,
0x3E, 0x33, 0x05, 0x19, 0x24, 0x27, 0x20, 0x2E, 0x3C, 0x2C, 0x2A, 0x14, 0x16, 0x39, 0x10, 0x09,
0x32, 0x18, 0x23, 0x1F, 0x3B, 0x13, 0x38, 0x0F, 0x31, 0x1E, 0x12, 0x0E, 0x1D, 0x0D, 0x0C, 0x0B };
public static long HighestOneBit(long i)
{
return (long)HighestOneBit((ulong)i);
}
[CLSCompliant(false)]
public static ulong HighestOneBit(ulong i)
{
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i |= i >> 32;
return i - (i >> 1);
}
public static long LowestOneBit(long i)
{
return i & -i;
}
[CLSCompliant(false)]
public static ulong LowestOneBit(ulong i)
{
return (ulong)LowestOneBit((long)i);
}
public static int NumberOfLeadingZeros(long i)
{
int x = (int)(i >> 32), n = 0;
if (x == 0)
{
n = 32;
x = (int)i;
}
return n + Integers.NumberOfLeadingZeros(x);
}
public static int NumberOfTrailingZeros(long i)
{
int n = DeBruijnTZ[(uint)((ulong)((i & -i) * 0x045FBAC7992A70DAL) >> 58)];
long m = (((i & 0xFFFFFFFFL) | (long)((ulong)i >> 32)) - 1L) >> 63;
return n - (int)m;
}
public static long Reverse(long i)
{
return (long)Reverse((ulong)i);
}
[CLSCompliant(false)]
public static ulong Reverse(ulong i)
{
i = Bits.BitPermuteStepSimple(i, 0x5555555555555555UL, 1);
i = Bits.BitPermuteStepSimple(i, 0x3333333333333333UL, 2);
i = Bits.BitPermuteStepSimple(i, 0x0F0F0F0F0F0F0F0FUL, 4);
return ReverseBytes(i);
}
public static long ReverseBytes(long i)
{
return (long)ReverseBytes((ulong)i);
}
[CLSCompliant(false)]
public static ulong ReverseBytes(ulong i)
{
return RotateLeft(i & 0xFF000000FF000000UL, 8) |
RotateLeft(i & 0x00FF000000FF0000UL, 24) |
RotateLeft(i & 0x0000FF000000FF00UL, 40) |
RotateLeft(i & 0x000000FF000000FFUL, 56);
}
public static long RotateLeft(long i, int distance)
{
return (i << distance) | (long)((ulong)i >> -distance);
}
[CLSCompliant(false)]
public static ulong RotateLeft(ulong i, int distance)
{
return (i << distance) | (i >> -distance);
}
public static long RotateRight(long i, int distance)
{
return (long)((ulong)i >> distance) | (i << -distance);
}
[CLSCompliant(false)]
public static ulong RotateRight(ulong i, int distance)
{
return (i >> distance) | (i << -distance);
}
}
internal static class Interleave
{
private const ulong M32 = 0x55555555UL;
private const ulong M64 = 0x5555555555555555UL;
private const ulong M64R = 0xAAAAAAAAAAAAAAAAUL;
internal static uint Expand8to16(byte x)
{
uint t = x;
t = (t | (t << 4)) & 0x0F0FU;
t = (t | (t << 2)) & 0x3333U;
t = (t | (t << 1)) & 0x5555U;
return t;
}
internal static uint Expand16to32(ushort x)
{
uint t = x;
t = (t | (t << 8)) & 0x00FF00FFU;
t = (t | (t << 4)) & 0x0F0F0F0FU;
t = (t | (t << 2)) & 0x33333333U;
t = (t | (t << 1)) & 0x55555555U;
return t;
}
internal static ulong Expand32to64(uint x)
{
// "shuffle" low half to even bits and high half to odd bits
x = Bits.BitPermuteStep(x, 0x0000FF00U, 8);
x = Bits.BitPermuteStep(x, 0x00F000F0U, 4);
x = Bits.BitPermuteStep(x, 0x0C0C0C0CU, 2);
x = Bits.BitPermuteStep(x, 0x22222222U, 1);
return ((x >> 1) & M32) << 32 | (x & M32);
}
internal static void Expand64To128(ulong x, ulong[] z, int zOff)
{
// "shuffle" low half to even bits and high half to odd bits
x = Bits.BitPermuteStep(x, 0x00000000FFFF0000UL, 16);
x = Bits.BitPermuteStep(x, 0x0000FF000000FF00UL, 8);
x = Bits.BitPermuteStep(x, 0x00F000F000F000F0UL, 4);
x = Bits.BitPermuteStep(x, 0x0C0C0C0C0C0C0C0CUL, 2);
x = Bits.BitPermuteStep(x, 0x2222222222222222UL, 1);
z[zOff] = (x) & M64;
z[zOff + 1] = (x >> 1) & M64;
}
internal static void Expand64To128(ulong[] xs, int xsOff, int xsLen, ulong[] zs, int zsOff)
{
int xsPos = xsLen, zsPos = zsOff + (xsLen << 1);
while (--xsPos >= 0)
{
zsPos -= 2;
Expand64To128(xs[xsOff + xsPos], zs, zsPos);
}
}
internal static ulong Expand64To128Rev(ulong x, out ulong low)
{
// "shuffle" low half to even bits and high half to odd bits
x = Bits.BitPermuteStep(x, 0x00000000FFFF0000UL, 16);
x = Bits.BitPermuteStep(x, 0x0000FF000000FF00UL, 8);
x = Bits.BitPermuteStep(x, 0x00F000F000F000F0UL, 4);
x = Bits.BitPermuteStep(x, 0x0C0C0C0C0C0C0C0CUL, 2);
x = Bits.BitPermuteStep(x, 0x2222222222222222UL, 1);
low = (x) & M64R;
return (x << 1) & M64R;
}
internal static uint Shuffle(uint x)
{
// "shuffle" low half to even bits and high half to odd bits
x = Bits.BitPermuteStep(x, 0x0000FF00U, 8);
x = Bits.BitPermuteStep(x, 0x00F000F0U, 4);
x = Bits.BitPermuteStep(x, 0x0C0C0C0CU, 2);
x = Bits.BitPermuteStep(x, 0x22222222U, 1);
return x;
}
internal static ulong Shuffle(ulong x)
{
// "shuffle" low half to even bits and high half to odd bits
x = Bits.BitPermuteStep(x, 0x00000000FFFF0000UL, 16);
x = Bits.BitPermuteStep(x, 0x0000FF000000FF00UL, 8);
x = Bits.BitPermuteStep(x, 0x00F000F000F000F0UL, 4);
x = Bits.BitPermuteStep(x, 0x0C0C0C0C0C0C0C0CUL, 2);
x = Bits.BitPermuteStep(x, 0x2222222222222222UL, 1);
return x;
}
internal static uint Shuffle2(uint x)
{
// 4 3 2 1 0 => 2 1 4 3 0
x = Bits.BitPermuteStep(x, 0x0000F0F0U, 12);
x = Bits.BitPermuteStep(x, 0x00CC00CCU, 6);
// 2 1 4 3 0 => 2 1 4 0 3
x = Bits.BitPermuteStep(x, 0x22222222U, 1);
// 2 1 4 0 3 => 2 1 0 4 3
x = Bits.BitPermuteStep(x, 0x0C0C0C0CU, 2);
return x;
}
internal static ulong Shuffle2(ulong x)
{
// 5 4 3 2 1 0 => 3 2 5 4 1 0
x = Bits.BitPermuteStep(x, 0x00000000FF00FF00UL, 24);
x = Bits.BitPermuteStep(x, 0x0000F0F00000F0F0UL, 12);
// 3 2 5 4 1 0 => 3 2 1 0 5 4
x = Bits.BitPermuteStep(x, 0x00CC00CC00CC00CCUL, 6);
x = Bits.BitPermuteStep(x, 0x0A0A0A0A0A0A0A0AUL, 3);
return x;
}
internal static uint Unshuffle(uint x)
{
// "unshuffle" even bits to low half and odd bits to high half
x = Bits.BitPermuteStep(x, 0x22222222U, 1);
x = Bits.BitPermuteStep(x, 0x0C0C0C0CU, 2);
x = Bits.BitPermuteStep(x, 0x00F000F0U, 4);
x = Bits.BitPermuteStep(x, 0x0000FF00U, 8);
return x;
}
internal static ulong Unshuffle(ulong x)
{
// "unshuffle" even bits to low half and odd bits to high half
x = Bits.BitPermuteStep(x, 0x2222222222222222UL, 1);
x = Bits.BitPermuteStep(x, 0x0C0C0C0C0C0C0C0CUL, 2);
x = Bits.BitPermuteStep(x, 0x00F000F000F000F0UL, 4);
x = Bits.BitPermuteStep(x, 0x0000FF000000FF00UL, 8);
x = Bits.BitPermuteStep(x, 0x00000000FFFF0000UL, 16);
return x;
}
internal static ulong Unshuffle(ulong x, out ulong even)
{
ulong u0 = Unshuffle(x);
even = u0 & 0x00000000FFFFFFFFUL;
return u0 >> 32;
}
internal static ulong Unshuffle(ulong x0, ulong x1, out ulong even)
{
ulong u0 = Unshuffle(x0);
ulong u1 = Unshuffle(x1);
even = (u1 << 32) | (u0 & 0x00000000FFFFFFFFUL);
return (u0 >> 32) | (u1 & 0xFFFFFFFF00000000UL);
}
internal static uint Unshuffle2(uint x)
{
// 4 3 2 1 0 => 4 3 1 2 0
x = Bits.BitPermuteStep(x, 0x0C0C0C0CU, 2);
// 4 3 1 2 0 => 4 3 1 0 2
x = Bits.BitPermuteStep(x, 0x22222222U, 1);
// 4 3 1 0 2 => 1 0 4 3 2
x = Bits.BitPermuteStep(x, 0x0000F0F0U, 12);
x = Bits.BitPermuteStep(x, 0x00CC00CCU, 6);
return x;
}
internal static ulong Unshuffle2(ulong x)
{
// 5 4 3 2 1 0 => 5 4 1 0 3 2
x = Bits.BitPermuteStep(x, 0x00CC00CC00CC00CCUL, 6);
x = Bits.BitPermuteStep(x, 0x0A0A0A0A0A0A0A0AUL, 3);
// 5 4 1 0 3 2 => 1 0 5 4 3 2
x = Bits.BitPermuteStep(x, 0x00000000FF00FF00UL, 24);
x = Bits.BitPermuteStep(x, 0x0000F0F00000F0F0UL, 12);
return x;
}
internal static ulong Transpose(ulong x)
{
x = Bits.BitPermuteStep(x, 0x00000000F0F0F0F0UL, 28);
x = Bits.BitPermuteStep(x, 0x0000CCCC0000CCCCUL, 14);
x = Bits.BitPermuteStep(x, 0x00AA00AA00AA00AAUL, 7);
return x;
}
}
internal static class GcmUtilities
{
internal struct FieldElement
{
internal ulong n0, n1;
}
private const uint E1 = 0xe1000000;
private const ulong E1UL = (ulong)E1 << 32;
internal static void One(out FieldElement x)
{
x.n0 = 1UL << 63;
x.n1 = 0UL;
}
internal static void AsBytes(ulong x0, ulong x1, byte[] z)
{
Pack.UInt64_To_BE(x0, z, 0);
Pack.UInt64_To_BE(x1, z, 8);
}
internal static void AsBytes(ref FieldElement x, byte[] z)
{
AsBytes(x.n0, x.n1, z);
}
internal static void AsFieldElement(byte[] x, out FieldElement z)
{
z.n0 = Pack.BE_To_UInt64(x, 0);
z.n1 = Pack.BE_To_UInt64(x, 8);
}
internal static void DivideP(ref FieldElement x, out FieldElement z)
{
ulong x0 = x.n0, x1 = x.n1;
ulong m = (ulong)((long)x0 >> 63);
x0 ^= (m & E1UL);
z.n0 = (x0 << 1) | (x1 >> 63);
z.n1 = (x1 << 1) | (ulong)(-(long)m);
}
internal static void Multiply(byte[] x, byte[] y)
{
AsFieldElement(x, out FieldElement X);
AsFieldElement(y, out FieldElement Y);
Multiply(ref X, ref Y);
AsBytes(ref X, x);
}
internal static void Multiply(ref FieldElement x, ref FieldElement y)
{
ulong z0, z1, z2;
{
/*
* "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
*
* Without access to the high part of a 64x64 product x * y, we use a bit reversal to calculate it:
* rev(x) * rev(y) == rev((x * y) << 1)
*/
ulong x0 = x.n0, x1 = x.n1;
ulong y0 = y.n0, y1 = y.n1;
ulong x0r = Longs.Reverse(x0), x1r = Longs.Reverse(x1);
ulong y0r = Longs.Reverse(y0), y1r = Longs.Reverse(y1);
ulong z3;
ulong h0 = Longs.Reverse(ImplMul64(x0r, y0r));
ulong h1 = ImplMul64(x0, y0) << 1;
ulong h2 = Longs.Reverse(ImplMul64(x1r, y1r));
ulong h3 = ImplMul64(x1, y1) << 1;
ulong h4 = Longs.Reverse(ImplMul64(x0r ^ x1r, y0r ^ y1r));
ulong h5 = ImplMul64(x0 ^ x1, y0 ^ y1) << 1;
z0 = h0;
z1 = h1 ^ h0 ^ h2 ^ h4;
z2 = h2 ^ h1 ^ h3 ^ h5;
z3 = h3;
Debug.Assert(z3 << 63 == 0);
z1 ^= z3 ^ (z3 >> 1) ^ (z3 >> 2) ^ (z3 >> 7);
// z2 ^= (z3 << 63) ^ (z3 << 62) ^ (z3 << 57);
z2 ^= (z3 << 62) ^ (z3 << 57);
z0 ^= z2 ^ (z2 >> 1) ^ (z2 >> 2) ^ (z2 >> 7);
z1 ^= (z2 << 63) ^ (z2 << 62) ^ (z2 << 57);
}
x.n0 = z0;
x.n1 = z1;
}
internal static void MultiplyP7(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 57;
x.n0 = (x0 >> 7) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 7) | (x0 << 57);
}
internal static void MultiplyP8(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 56;
x.n0 = (x0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 8) | (x0 << 56);
}
internal static void MultiplyP8(ref FieldElement x, out FieldElement y)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 56;
y.n0 = (x0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
y.n1 = (x1 >> 8) | (x0 << 56);
}
internal static void MultiplyP16(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 48;
x.n0 = (x0 >> 16) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 16) | (x0 << 48);
}
internal static void Square(ref FieldElement x)
{
ulong t1 = Interleave.Expand64To128Rev(x.n0, out ulong t0);
ulong t3 = Interleave.Expand64To128Rev(x.n1, out ulong t2);
Debug.Assert((t0 | t1 | t2 | t3) << 63 == 0UL);
var z1 = t1 ^ t3 ^ (t3 >> 1) ^ (t3 >> 2) ^ (t3 >> 7);
var z2 = t2 ^ (t3 << 62) ^ (t3 << 57);
x.n0 = t0 ^ z2 ^ (z2 >> 1) ^ (z2 >> 2) ^ (z2 >> 7);
x.n1 = z1 ^ (t2 << 62) ^ (t2 << 57);
}
internal static void Xor(byte[] x, byte[] y)
{
int i = 0;
do
{
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
}
while (i < 16);
}
internal static void Xor(byte[] x, byte[] y, int yOff)
{
int i = 0;
do
{
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
}
while (i < 16);
}
internal static void Xor(byte[] x, byte[] y, int yOff, int yLen)
{
while (--yLen >= 0)
{
x[yLen] ^= y[yOff + yLen];
}
}
internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, int len)
{
while (--len >= 0)
{
x[xOff + len] ^= y[yOff + len];
}
}
internal static void Xor(ref FieldElement x, ref FieldElement y)
{
x.n0 ^= y.n0;
x.n1 ^= y.n1;
}
internal static void Xor(ref FieldElement x, ref FieldElement y, out FieldElement z)
{
z.n0 = x.n0 ^ y.n0;
z.n1 = x.n1 ^ y.n1;
}
private static ulong ImplMul64(ulong x, ulong y)
{
ulong x0 = x & 0x1111111111111111UL;
ulong x1 = x & 0x2222222222222222UL;
ulong x2 = x & 0x4444444444444444UL;
ulong x3 = x & 0x8888888888888888UL;
ulong y0 = y & 0x1111111111111111UL;
ulong y1 = y & 0x2222222222222222UL;
ulong y2 = y & 0x4444444444444444UL;
ulong y3 = y & 0x8888888888888888UL;
ulong z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
ulong z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
ulong z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
ulong z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
z0 &= 0x1111111111111111UL;
z1 &= 0x2222222222222222UL;
z2 &= 0x4444444444444444UL;
z3 &= 0x8888888888888888UL;
return z0 | z1 | z2 | z3;
}
}
/// <summary> General array utilities.</summary>
public static class Arrays
{
public static readonly byte[] EmptyBytes = new byte[0];
public static readonly int[] EmptyInts = new int[0];
public static bool AreAllZeroes(byte[] buf, int off, int len)
{
uint bits = 0;
for (int i = 0; i < len; ++i)
{
bits |= buf[off + i];
}
return bits == 0;
}
public static bool AreEqual(
bool[] a,
bool[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
public static bool AreEqual(
char[] a,
char[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
/// <summary>
/// Are two arrays equal.
/// </summary>
/// <param name="a">Left side.</param>
/// <param name="b">Right side.</param>
/// <returns>True if equal.</returns>
public static bool AreEqual(byte[] a, byte[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
public static bool AreEqual(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex)
{
int aLength = aToIndex - aFromIndex;
int bLength = bToIndex - bFromIndex;
if (aLength != bLength)
return false;
for (int i = 0; i < aLength; ++i)
{
if (a[aFromIndex + i] != b[bFromIndex + i])
return false;
}
return true;
}
[CLSCompliant(false)]
public static bool AreEqual(ulong[] a, int aFromIndex, int aToIndex, ulong[] b, int bFromIndex, int bToIndex)
{
int aLength = aToIndex - aFromIndex;
int bLength = bToIndex - bFromIndex;
if (aLength != bLength)
return false;
for (int i = 0; i < aLength; ++i)
{
if (a[aFromIndex + i] != b[bFromIndex + i])
return false;
}
return true;
}
[Obsolete("Use 'FixedTimeEquals' instead")]
public static bool ConstantTimeAreEqual(byte[] a, byte[] b)
{
return FixedTimeEquals(a, b);
}
[Obsolete("Use 'FixedTimeEquals' instead")]
public static bool ConstantTimeAreEqual(int len, byte[] a, int aOff, byte[] b, int bOff)
{
return FixedTimeEquals(len, a, aOff, b, bOff);
}
public static bool FixedTimeEquals(byte[] a, byte[] b)
{
if (null == a || null == b)
return false;
int len = a.Length;
if (len != b.Length)
return false;
int d = 0;
for (int i = 0; i < len; ++i)
{
d |= a[i] ^ b[i];
}
return 0 == d;
}
public static bool FixedTimeEquals(int len, byte[] a, int aOff, byte[] b, int bOff)
{
if (null == a)
throw new ArgumentNullException("a");
if (null == b)
throw new ArgumentNullException("b");
if (len < 0)
throw new ArgumentException("cannot be negative", "len");
if (aOff > (a.Length - len))
throw new IndexOutOfRangeException("'aOff' value invalid for specified length");
if (bOff > (b.Length - len))
throw new IndexOutOfRangeException("'bOff' value invalid for specified length");
int d = 0;
for (int i = 0; i < len; ++i)
{
d |= a[aOff + i] ^ b[bOff + i];
}
return 0 == d;
}
public static bool AreEqual(
int[] a,
int[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
[CLSCompliant(false)]
public static bool AreEqual(uint[] a, uint[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
public static bool AreEqual(long[] a, long[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
[CLSCompliant(false)]
public static bool AreEqual(ulong[] a, ulong[] b)
{
if (a == b)
return true;
if (a == null || b == null)
return false;
return HaveSameContents(a, b);
}
private static bool HaveSameContents(
bool[] a,
bool[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(
char[] a,
char[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(
byte[] a,
byte[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(
int[] a,
int[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(uint[] a, uint[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(long[] a, long[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
private static bool HaveSameContents(ulong[] a, ulong[] b)
{
int i = a.Length;
if (i != b.Length)
return false;
while (i != 0)
{
--i;
if (a[i] != b[i])
return false;
}
return true;
}
public static string ToString(
object[] a)
{
StringBuilder sb = new StringBuilder("[");
if (a.Length > 0)
{
sb.Append(a[0]);
for (int index = 1; index < a.Length; ++index)
{
sb.Append(", ").Append(a[index]);
}
}
sb.Append(']');
return sb.ToString();
}
public static int GetHashCode(byte[] data)
{
if (data == null)
{
return 0;
}
int i = data.Length;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= data[i];
}
return hc;
}
public static int GetHashCode(byte[] data, int off, int len)
{
if (data == null)
{
return 0;
}
int i = len;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= data[off + i];
}
return hc;
}
public static int GetHashCode(int[] data)
{
if (data == null)
return 0;
int i = data.Length;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= data[i];
}
return hc;
}
[CLSCompliant(false)]
public static int GetHashCode(ushort[] data)
{
if (data == null)
return 0;
int i = data.Length;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= data[i];
}
return hc;
}
public static int GetHashCode(int[] data, int off, int len)
{
if (data == null)
return 0;
int i = len;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= data[off + i];
}
return hc;
}
[CLSCompliant(false)]
public static int GetHashCode(uint[] data)
{
if (data == null)
return 0;
int i = data.Length;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= (int)data[i];
}
return hc;
}
[CLSCompliant(false)]
public static int GetHashCode(uint[] data, int off, int len)
{
if (data == null)
return 0;
int i = len;
int hc = i + 1;
while (--i >= 0)
{
hc *= 257;
hc ^= (int)data[off + i];
}
return hc;
}
[CLSCompliant(false)]
public static int GetHashCode(ulong[] data)
{
if (data == null)
return 0;
int i = data.Length;
int hc = i + 1;
while (--i >= 0)
{
ulong di = data[i];
hc *= 257;
hc ^= (int)di;
hc *= 257;
hc ^= (int)(di >> 32);
}
return hc;
}
[CLSCompliant(false)]
public static int GetHashCode(ulong[] data, int off, int len)
{
if (data == null)
return 0;
int i = len;
int hc = i + 1;
while (--i >= 0)
{
ulong di = data[off + i];
hc *= 257;
hc ^= (int)di;
hc *= 257;
hc ^= (int)(di >> 32);
}
return hc;
}
public static bool[] Clone(bool[] data)
{
return data == null ? null : (bool[])data.Clone();
}
public static byte[] Clone(byte[] data)
{
return data == null ? null : (byte[])data.Clone();
}
public static short[] Clone(short[] data)
{
return data == null ? null : (short[])data.Clone();
}
[CLSCompliant(false)]
public static ushort[] Clone(ushort[] data)
{
return data == null ? null : (ushort[])data.Clone();
}
public static int[] Clone(int[] data)
{
return data == null ? null : (int[])data.Clone();
}
[CLSCompliant(false)]
public static uint[] Clone(uint[] data)
{
return data == null ? null : (uint[])data.Clone();
}
public static long[] Clone(long[] data)
{
return data == null ? null : (long[])data.Clone();
}
[CLSCompliant(false)]
public static ulong[] Clone(ulong[] data)
{
return data == null ? null : (ulong[])data.Clone();
}
public static byte[] Clone(byte[] data, byte[] existing)
{
if (data == null)
return null;
if (existing == null || existing.Length != data.Length)
return Clone(data);
Array.Copy(data, 0, existing, 0, existing.Length);
return existing;
}
[CLSCompliant(false)]
public static ulong[] Clone(ulong[] data, ulong[] existing)
{
if (data == null)
return null;
if (existing == null || existing.Length != data.Length)
return Clone(data);
Array.Copy(data, 0, existing, 0, existing.Length);
return existing;
}
public static bool Contains(byte[] a, byte n)
{
for (int i = 0; i < a.Length; ++i)
{
if (a[i] == n)
return true;
}
return false;
}
public static bool Contains(short[] a, short n)
{
for (int i = 0; i < a.Length; ++i)
{
if (a[i] == n)
return true;
}
return false;
}
public static bool Contains(int[] a, int n)
{
for (int i = 0; i < a.Length; ++i)
{
if (a[i] == n)
return true;
}
return false;
}
public static void Fill(byte[] buf, byte b)
{
int i = buf.Length;
while (i > 0)
{
buf[--i] = b;
}
}
[CLSCompliant(false)]
public static void Fill(ulong[] buf, ulong b)
{
int i = buf.Length;
while (i > 0)
{
buf[--i] = b;
}
}
public static void Fill(byte[] buf, int from, int to, byte b)
{
for (int i = from; i < to; ++i)
{
buf[i] = b;
}
}
public static void Fill<T>(T[] ts, T t)
{
for (int i = 0; i < ts.Length; ++i)
{
ts[i] = t;
}
}
public static byte[] CopyOf(byte[] data, int newLength)
{
byte[] tmp = new byte[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
public static char[] CopyOf(char[] data, int newLength)
{
char[] tmp = new char[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
public static int[] CopyOf(int[] data, int newLength)
{
int[] tmp = new int[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
[CLSCompliant(false)]
public static uint[] CopyOf(uint[] data, int newLength)
{
uint[] tmp = new uint[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
public static long[] CopyOf(long[] data, int newLength)
{
long[] tmp = new long[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
public static BigInteger[] CopyOf(BigInteger[] data, int newLength)
{
BigInteger[] tmp = new BigInteger[newLength];
Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
return tmp;
}
/**
* Make a copy of a range of bytes from the passed in data array. The range can
* extend beyond the end of the input array, in which case the return array will
* be padded with zeroes.
*
* @param data the array from which the data is to be copied.
* @param from the start index at which the copying should take place.
* @param to the final index of the range (exclusive).
*
* @return a new byte array containing the range given.
*/
public static byte[] CopyOfRange(byte[] data, int from, int to)
{
int newLength = GetLength(from, to);
byte[] tmp = new byte[newLength];
Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
return tmp;
}
public static int[] CopyOfRange(int[] data, int from, int to)
{
int newLength = GetLength(from, to);
int[] tmp = new int[newLength];
Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
return tmp;
}
public static long[] CopyOfRange(long[] data, int from, int to)
{
int newLength = GetLength(from, to);
long[] tmp = new long[newLength];
Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
return tmp;
}
public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to)
{
int newLength = GetLength(from, to);
BigInteger[] tmp = new BigInteger[newLength];
Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
return tmp;
}
private static int GetLength(int from, int to)
{
int newLength = to - from;
if (newLength < 0)
throw new ArgumentException(from + " > " + to);
return newLength;
}
public static byte[] Append(byte[] a, byte b)
{
if (a == null)
return new byte[] { b };
int length = a.Length;
byte[] result = new byte[length + 1];
Array.Copy(a, 0, result, 0, length);
result[length] = b;
return result;
}
public static short[] Append(short[] a, short b)
{
if (a == null)
return new short[] { b };
int length = a.Length;
short[] result = new short[length + 1];
Array.Copy(a, 0, result, 0, length);
result[length] = b;
return result;
}
public static int[] Append(int[] a, int b)
{
if (a == null)
return new int[] { b };
int length = a.Length;
int[] result = new int[length + 1];
Array.Copy(a, 0, result, 0, length);
result[length] = b;
return result;
}
public static byte[] Concatenate(byte[] a, byte[] b)
{
if (a == null)
return Clone(b);
if (b == null)
return Clone(a);
byte[] rv = new byte[a.Length + b.Length];
Array.Copy(a, 0, rv, 0, a.Length);
Array.Copy(b, 0, rv, a.Length, b.Length);
return rv;
}
[CLSCompliant(false)]
public static ushort[] Concatenate(ushort[] a, ushort[] b)
{
if (a == null)
return Clone(b);
if (b == null)
return Clone(a);
ushort[] rv = new ushort[a.Length + b.Length];
Array.Copy(a, 0, rv, 0, a.Length);
Array.Copy(b, 0, rv, a.Length, b.Length);
return rv;
}
public static byte[] ConcatenateAll(params byte[][] vs)
{
byte[][] nonNull = new byte[vs.Length][];
int count = 0;
int totalLength = 0;
for (int i = 0; i < vs.Length; ++i)
{
byte[] v = vs[i];
if (v != null)
{
nonNull[count++] = v;
totalLength += v.Length;
}
}
byte[] result = new byte[totalLength];
int pos = 0;
for (int j = 0; j < count; ++j)
{
byte[] v = nonNull[j];
Array.Copy(v, 0, result, pos, v.Length);
pos += v.Length;
}
return result;
}
public static int[] Concatenate(int[] a, int[] b)
{
if (a == null)
return Clone(b);
if (b == null)
return Clone(a);
int[] rv = new int[a.Length + b.Length];
Array.Copy(a, 0, rv, 0, a.Length);
Array.Copy(b, 0, rv, a.Length, b.Length);
return rv;
}
[CLSCompliant(false)]
public static uint[] Concatenate(uint[] a, uint[] b)
{
if (a == null)
return Clone(b);
if (b == null)
return Clone(a);
uint[] rv = new uint[a.Length + b.Length];
Array.Copy(a, 0, rv, 0, a.Length);
Array.Copy(b, 0, rv, a.Length, b.Length);
return rv;
}
public static byte[] Prepend(byte[] a, byte b)
{
if (a == null)
return new byte[] { b };
int length = a.Length;
byte[] result = new byte[length + 1];
Array.Copy(a, 0, result, 1, length);
result[0] = b;
return result;
}
public static short[] Prepend(short[] a, short b)
{
if (a == null)
return new short[] { b };
int length = a.Length;
short[] result = new short[length + 1];
Array.Copy(a, 0, result, 1, length);
result[0] = b;
return result;
}
public static int[] Prepend(int[] a, int b)
{
if (a == null)
return new int[] { b };
int length = a.Length;
int[] result = new int[length + 1];
Array.Copy(a, 0, result, 1, length);
result[0] = b;
return result;
}
public static T[] Prepend<T>(T[] a, T b)
{
if (a == null)
return new T[1] { b };
T[] result = new T[1 + a.Length];
result[0] = b;
a.CopyTo(result, 1);
return result;
}
public static byte[] Reverse(byte[] a)
{
if (a == null)
return null;
int p1 = 0, p2 = a.Length;
byte[] result = new byte[p2];
while (--p2 >= 0)
{
result[p2] = a[p1++];
}
return result;
}
public static int[] Reverse(int[] a)
{
if (a == null)
return null;
int p1 = 0, p2 = a.Length;
int[] result = new int[p2];
while (--p2 >= 0)
{
result[p2] = a[p1++];
}
return result;
}
internal static void Reverse<T>(T[] input, T[] output)
{
int last = input.Length - 1;
for (int i = 0; i <= last; ++i)
{
output[i] = input[last - i];
}
}
public static T[] ReverseInPlace<T>(T[] array)
{
if (null == array)
return null;
Array.Reverse(array);
return array;
}
public static void Clear(byte[] data)
{
if (null != data)
{
Array.Clear(data, 0, data.Length);
}
}
public static void Clear(int[] data)
{
if (null != data)
{
Array.Clear(data, 0, data.Length);
}
}
public static bool IsNullOrContainsNull(object[] array)
{
if (null == array)
return true;
int count = array.Length;
for (int i = 0; i < count; ++i)
{
if (null == array[i])
return true;
}
return false;
}
public static bool IsNullOrEmpty(byte[] array)
{
return null == array || array.Length < 1;
}
public static bool IsNullOrEmpty(object[] array)
{
return null == array || array.Length < 1;
}
}
[Obsolete("Will be removed")]
public class Tables4kGcmMultiplier
: IGcmMultiplier
{
private byte[] H;
private GcmUtilities.FieldElement[] T;
public void Init(byte[] H)
{
if (T == null)
{
T = new GcmUtilities.FieldElement[256];
}
else if (Arrays.AreEqual(this.H, H))
{
return;
}
this.H = Arrays.Clone(H);
// T[0] = 0
// T[1] = H.p^7
GcmUtilities.AsFieldElement(this.H, out T[1]);
GcmUtilities.MultiplyP7(ref T[1]);
for (int n = 1; n < 128; ++n)
{
// T[2.n] = T[n].p^-1
GcmUtilities.DivideP(ref T[n], out T[n << 1]);
// T[2.n + 1] = T[2.n] + T[1]
GcmUtilities.Xor(ref T[n << 1], ref T[1], out T[(n << 1) + 1]);
}
}
public void MultiplyH(byte[] x)
{
//GcmUtilities.FieldElement z = T[x[15]];
//for (int i = 14; i >= 0; --i)
//{
// GcmUtilities.MultiplyP8(ref z);
// GcmUtilities.Xor(ref z, ref T[x[i]]);
//}
//GcmUtilities.AsBytes(ref z, x);
int pos = x[15];
ulong z0 = T[pos].n0, z1 = T[pos].n1;
for (int i = 14; i >= 0; --i)
{
pos = x[i];
ulong c = z1 << 56;
z1 = T[pos].n1 ^ ((z1 >> 8) | (z0 << 56));
z0 = T[pos].n0 ^ (z0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
}
GcmUtilities.AsBytes(z0, z1, x);
}
}
// Type or member is obsolete
/// <summary>
/// Implements the Galois/Counter mode (GCM) detailed in NIST Special Publication 800-38D.
/// </summary>
public sealed class GcmBlockCipher
: IAeadBlockCipher
{
internal static IGcmMultiplier CreateGcmMultiplier()
{
//if (BasicGcmMultiplier.IsHardwareAccelerated)
// return new BasicGcmMultiplier();
return new Tables4kGcmMultiplier();
}
private const int BlockSize = 16;
private readonly IBlockCipher cipher;
private readonly IGcmMultiplier multiplier;
private IGcmExponentiator exp;
// These fields are set by Init and not modified by processing
private bool forEncryption;
private bool initialised;
private int macSize;
private byte[] lastKey;
private byte[] nonce;
private byte[] initialAssociatedText;
private byte[] H;
private byte[] J0;
// These fields are modified during processing
private byte[] bufBlock;
private byte[] macBlock;
private byte[] S, S_at, S_atPre;
private byte[] counter;
private uint counter32;
private uint blocksRemaining;
private int bufOff;
private ulong totalLength;
private byte[] atBlock;
private int atBlockPos;
private ulong atLength;
private ulong atLengthPre;
public GcmBlockCipher(
IBlockCipher c)
: this(c, null)
{
}
public interface IGcmExponentiator
{
void Init(byte[] x);
void ExponentiateX(long pow, byte[] output);
}
[Obsolete("Will be removed")]
public class BasicGcmExponentiator
: IGcmExponentiator
{
private GcmUtilities.FieldElement x;
public void Init(byte[] x)
{
GcmUtilities.AsFieldElement(x, out this.x);
}
public void ExponentiateX(long pow, byte[] output)
{
GcmUtilities.FieldElement y;
GcmUtilities.One(out y);
if (pow > 0)
{
GcmUtilities.FieldElement powX = x;
do
{
if ((pow & 1L) != 0)
{
GcmUtilities.Multiply(ref y, ref powX);
}
GcmUtilities.Square(ref powX);
pow >>= 1;
}
while (pow > 0);
}
GcmUtilities.AsBytes(ref y, output);
}
}
public class AeadParameters
: ICipherParameters
{
private readonly byte[] associatedText;
private readonly byte[] nonce;
private readonly KeyParameter key;
private readonly int macSize;
/**
* Base constructor.
*
* @param key key to be used by underlying cipher
* @param macSize macSize in bits
* @param nonce nonce to be used
*/
public AeadParameters(KeyParameter key, int macSize, byte[] nonce)
: this(key, macSize, nonce, null)
{
}
/**
* Base constructor.
*
* @param key key to be used by underlying cipher
* @param macSize macSize in bits
* @param nonce nonce to be used
* @param associatedText associated text, if any
*/
public AeadParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText)
{
if (nonce == null)
throw new ArgumentNullException(nameof(nonce));
this.key = key;
this.nonce = nonce;
this.macSize = macSize;
this.associatedText = associatedText;
}
public virtual KeyParameter Key
{
get { return key; }
}
public virtual int MacSize
{
get { return macSize; }
}
public virtual byte[] GetAssociatedText()
{
return associatedText;
}
public virtual byte[] GetNonce()
{
return (byte[])nonce.Clone();
}
}
public class ParametersWithIV
: ICipherParameters
{
internal static ICipherParameters ApplyOptionalIV(ICipherParameters parameters, byte[] iv)
{
return iv == null ? parameters : new ParametersWithIV(parameters, iv);
}
private readonly ICipherParameters m_parameters;
private readonly byte[] m_iv;
public ParametersWithIV(ICipherParameters parameters, byte[] iv)
: this(parameters, iv, 0, iv.Length)
{
// NOTE: 'parameters' may be null to imply key re-use
if (iv == null)
throw new ArgumentNullException(nameof(iv));
m_parameters = parameters;
m_iv = (byte[])iv.Clone();
}
public ParametersWithIV(ICipherParameters parameters, byte[] iv, int ivOff, int ivLen)
{
// NOTE: 'parameters' may be null to imply key re-use
if (iv == null)
throw new ArgumentNullException(nameof(iv));
m_parameters = parameters;
m_iv = new byte[ivLen];
Array.Copy(iv, ivOff, m_iv, 0, ivLen);
}
private ParametersWithIV(ICipherParameters parameters, int ivLength)
{
if (ivLength < 0)
throw new ArgumentOutOfRangeException(nameof(ivLength));
// NOTE: 'parameters' may be null to imply key re-use
m_parameters = parameters;
m_iv = new byte[ivLength];
}
public byte[] GetIV()
{
return (byte[])m_iv.Clone();
}
public int IVLength => m_iv.Length;
public ICipherParameters Parameters => m_parameters;
}
public class KeyParameter
: ICipherParameters
{
private readonly byte[] m_key;
public KeyParameter(byte[] key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
m_key = (byte[])key.Clone();
}
public KeyParameter(byte[] key, int keyOff, int keyLen)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (keyOff < 0 || keyOff > key.Length)
throw new ArgumentOutOfRangeException(nameof(keyOff));
if (keyLen < 0 || keyLen > (key.Length - keyOff))
throw new ArgumentOutOfRangeException(nameof(keyLen));
m_key = new byte[keyLen];
Array.Copy(key, keyOff, m_key, 0, keyLen);
}
private KeyParameter(int length)
{
if (length < 1)
throw new ArgumentOutOfRangeException(nameof(length));
m_key = new byte[length];
}
public void CopyTo(byte[] buf, int off, int len)
{
if (m_key.Length != len)
throw new ArgumentOutOfRangeException(nameof(len));
Array.Copy(m_key, 0, buf, off, len);
}
public byte[] GetKey()
{
return (byte[])m_key.Clone();
}
public int KeyLength => m_key.Length;
internal bool FixedTimeEquals(byte[] data)
{
return Arrays.FixedTimeEquals(m_key, data);
}
public KeyParameter Reverse()
{
var reversed = new KeyParameter(m_key.Length);
Arrays.Reverse(m_key, reversed.m_key);
return reversed;
}
}
[Obsolete("Will be removed")]
public GcmBlockCipher(
IBlockCipher c,
IGcmMultiplier m)
{
if (c.GetBlockSize() != BlockSize)
throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
if (m == null)
{
m = CreateGcmMultiplier();
}
this.cipher = c;
this.multiplier = m;
}
public string AlgorithmName => cipher.AlgorithmName + "/GCM";
public IBlockCipher UnderlyingCipher => cipher;
public int GetBlockSize()
{
return BlockSize;
}
/// <remarks>
/// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits.
/// Sizes less than 96 are not recommended, but are supported for specialized applications.
/// </remarks>
public void Init(bool forEncryption, ICipherParameters parameters)
{
this.forEncryption = forEncryption;
this.macBlock = null;
this.initialised = true;
KeyParameter keyParam;
byte[] newNonce;
if (parameters is AeadParameters aeadParameters)
{
newNonce = aeadParameters.GetNonce();
initialAssociatedText = aeadParameters.GetAssociatedText();
int macSizeBits = aeadParameters.MacSize;
if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0)
throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
macSize = macSizeBits / 8;
keyParam = aeadParameters.Key;
}
else if (parameters is ParametersWithIV withIV)
{
newNonce = withIV.GetIV();
initialAssociatedText = null;
macSize = 16;
keyParam = (KeyParameter)withIV.Parameters;
}
else
{
throw new ArgumentException("invalid parameters passed to GCM");
}
int bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
this.bufBlock = new byte[bufLength];
if (newNonce.Length < 1)
throw new ArgumentException("IV must be at least 1 byte");
if (forEncryption)
{
if (nonce != null && Arrays.AreEqual(nonce, newNonce))
{
if (keyParam == null)
throw new ArgumentException("cannot reuse nonce for GCM encryption");
if (lastKey != null && keyParam.FixedTimeEquals(lastKey))
throw new ArgumentException("cannot reuse nonce for GCM encryption");
}
}
nonce = newNonce;
if (keyParam != null)
{
lastKey = keyParam.GetKey();
}
// TODO Restrict macSize to 16 if nonce length not 12?
// Cipher always used in forward mode
// if keyParam is null we're reusing the last key.
if (keyParam != null)
{
cipher.Init(true, keyParam);
this.H = new byte[BlockSize];
cipher.ProcessBlock(H, 0, H, 0);
// if keyParam is null we're reusing the last key and the multiplier doesn't need re-init
multiplier.Init(H);
exp = null;
}
else if (this.H == null)
{
throw new ArgumentException("Key must be specified in initial Init");
}
this.J0 = new byte[BlockSize];
if (nonce.Length == 12)
{
Array.Copy(nonce, 0, J0, 0, nonce.Length);
this.J0[BlockSize - 1] = 0x01;
}
else
{
gHASH(J0, nonce, nonce.Length);
byte[] X = new byte[BlockSize];
Pack.UInt64_To_BE((ulong)nonce.Length * 8UL, X, 8);
gHASHBlock(J0, X);
}
this.S = new byte[BlockSize];
this.S_at = new byte[BlockSize];
this.S_atPre = new byte[BlockSize];
this.atBlock = new byte[BlockSize];
this.atBlockPos = 0;
this.atLength = 0;
this.atLengthPre = 0;
this.counter = Arrays.Clone(J0);
this.counter32 = Pack.BE_To_UInt32(counter, 12);
this.blocksRemaining = uint.MaxValue - 1; // page 8, len(P) <= 2^39 - 256, 1 block used by tag
this.bufOff = 0;
this.totalLength = 0;
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
public byte[] GetMac()
{
return macBlock == null ? new byte[macSize] : (byte[])macBlock.Clone();
}
public int GetOutputSize(int len)
{
int totalData = len + bufOff;
if (forEncryption)
return totalData + macSize;
return totalData < macSize ? 0 : totalData - macSize;
}
public int GetUpdateOutputSize(int len)
{
int totalData = len + bufOff;
if (!forEncryption)
{
if (totalData < macSize)
return 0;
totalData -= macSize;
}
return totalData - totalData % BlockSize;
}
public void ProcessAadByte(byte input)
{
CheckStatus();
atBlock[atBlockPos] = input;
if (++atBlockPos == BlockSize)
{
// Hash each block as it fills
gHASHBlock(S_at, atBlock);
atBlockPos = 0;
atLength += BlockSize;
}
}
public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
CheckStatus();
if (atBlockPos > 0)
{
int available = BlockSize - atBlockPos;
if (len < available)
{
Array.Copy(inBytes, inOff, atBlock, atBlockPos, len);
atBlockPos += len;
return;
}
Array.Copy(inBytes, inOff, atBlock, atBlockPos, available);
gHASHBlock(S_at, atBlock);
atLength += BlockSize;
inOff += available;
len -= available;
//atBlockPos = 0;
}
int inLimit = inOff + len - BlockSize;
while (inOff <= inLimit)
{
gHASHBlock(S_at, inBytes, inOff);
atLength += BlockSize;
inOff += BlockSize;
}
atBlockPos = BlockSize + inLimit - inOff;
Array.Copy(inBytes, inOff, atBlock, 0, atBlockPos);
}
private void InitCipher()
{
if (atLength > 0)
{
Array.Copy(S_at, 0, S_atPre, 0, BlockSize);
atLengthPre = atLength;
}
// Finish hash for partial AAD block
if (atBlockPos > 0)
{
gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
atLengthPre += (uint)atBlockPos;
}
if (atLengthPre > 0)
{
Array.Copy(S_atPre, 0, S, 0, BlockSize);
}
}
public int ProcessByte(byte input, byte[] output, int outOff)
{
CheckStatus();
bufBlock[bufOff] = input;
if (++bufOff == bufBlock.Length)
{
Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
if (blocksRemaining == 0)
throw new InvalidOperationException("Attempt to process too many blocks");
--blocksRemaining;
if (totalLength == 0)
{
InitCipher();
}
if (forEncryption)
{
EncryptBlock(bufBlock, 0, output, outOff);
bufOff = 0;
}
else
{
DecryptBlock(bufBlock, 0, output, outOff);
Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
bufOff = macSize;
}
totalLength += BlockSize;
return BlockSize;
}
return 0;
}
public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
CheckStatus();
Check.DataLength(input, inOff, len, "input buffer too short");
int resultLen = bufOff + len;
if (forEncryption)
{
resultLen &= -BlockSize;
if (resultLen > 0)
{
Check.OutputLength(output, outOff, resultLen, "output buffer too short");
uint blocksNeeded = (uint)resultLen >> 4;
if (blocksRemaining < blocksNeeded)
throw new InvalidOperationException("Attempt to process too many blocks");
blocksRemaining -= blocksNeeded;
if (totalLength == 0)
{
InitCipher();
}
}
if (bufOff > 0)
{
int available = BlockSize - bufOff;
if (len < available)
{
Array.Copy(input, inOff, bufBlock, bufOff, len);
bufOff += len;
return 0;
}
Array.Copy(input, inOff, bufBlock, bufOff, available);
inOff += available;
len -= available;
EncryptBlock(bufBlock, 0, output, outOff);
outOff += BlockSize;
//bufOff = 0;
}
int inLimit1 = inOff + len - BlockSize;
int inLimit2 = inLimit1 - BlockSize;
while (inOff <= inLimit2)
{
EncryptBlocks2(input, inOff, output, outOff);
inOff += BlockSize * 2;
outOff += BlockSize * 2;
}
if (inOff <= inLimit1)
{
EncryptBlock(input, inOff, output, outOff);
inOff += BlockSize;
//outOff += BlockSize;
}
bufOff = BlockSize + inLimit1 - inOff;
Array.Copy(input, inOff, bufBlock, 0, bufOff);
}
else
{
resultLen -= macSize;
resultLen &= -BlockSize;
if (resultLen > 0)
{
Check.OutputLength(output, outOff, resultLen, "output buffer too short");
uint blocksNeeded = (uint)resultLen >> 4;
if (blocksRemaining < blocksNeeded)
throw new InvalidOperationException("Attempt to process too many blocks");
blocksRemaining -= blocksNeeded;
if (totalLength == 0)
{
InitCipher();
}
}
int available = bufBlock.Length - bufOff;
if (len < available)
{
Array.Copy(input, inOff, bufBlock, bufOff, len);
bufOff += len;
return 0;
}
if (bufOff >= BlockSize)
{
DecryptBlock(bufBlock, 0, output, outOff);
outOff += BlockSize;
bufOff -= BlockSize;
Array.Copy(bufBlock, BlockSize, bufBlock, 0, bufOff);
available += BlockSize;
if (len < available)
{
Array.Copy(input, inOff, bufBlock, bufOff, len);
bufOff += len;
totalLength += BlockSize;
return BlockSize;
}
}
int inLimit1 = inOff + len - bufBlock.Length;
int inLimit2 = inLimit1 - BlockSize;
available = BlockSize - bufOff;
Array.Copy(input, inOff, bufBlock, bufOff, available);
inOff += available;
DecryptBlock(bufBlock, 0, output, outOff);
outOff += BlockSize;
//bufOff = 0;
while (inOff <= inLimit2)
{
DecryptBlocks2(input, inOff, output, outOff);
inOff += BlockSize * 2;
outOff += BlockSize * 2;
}
if (inOff <= inLimit1)
{
DecryptBlock(input, inOff, output, outOff);
inOff += BlockSize;
//outOff += BlockSize;
}
bufOff = bufBlock.Length + inLimit1 - inOff;
Array.Copy(input, inOff, bufBlock, 0, bufOff);
}
totalLength += (uint)resultLen;
return resultLen;
}
public int DoFinal(byte[] output, int outOff)
{
CheckStatus();
int extra = bufOff;
if (forEncryption)
{
Check.OutputLength(output, outOff, extra + macSize, "output buffer too short");
}
else
{
if (extra < macSize)
throw new Exception("data too short");
extra -= macSize;
Check.OutputLength(output, outOff, extra, "output buffer too short");
}
if (totalLength == 0)
{
InitCipher();
}
if (extra > 0)
{
if (blocksRemaining == 0)
throw new InvalidOperationException("Attempt to process too many blocks");
--blocksRemaining;
ProcessPartial(bufBlock, 0, extra, output, outOff);
}
atLength += (uint)atBlockPos;
if (atLength > atLengthPre)
{
/*
* Some AAD was sent after the cipher started. We determine the difference b/w the hash value
* we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
* Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
* partial) cipher-text blocks produced, and adjust the current hash.
*/
// Finish hash for partial AAD block
if (atBlockPos > 0)
{
gHASHPartial(S_at, atBlock, 0, atBlockPos);
}
// Find the difference between the AAD hashes
if (atLengthPre > 0)
{
GcmUtilities.Xor(S_at, S_atPre);
}
// Number of cipher-text blocks produced
long c = (long)(((totalLength * 8) + 127) >> 7);
// Calculate the adjustment factor
byte[] H_c = new byte[16];
if (exp == null)
{
exp = new BasicGcmExponentiator();
exp.Init(H);
}
exp.ExponentiateX(c, H_c);
// Carry the difference forward
GcmUtilities.Multiply(S_at, H_c);
// Adjust the current hash
GcmUtilities.Xor(S, S_at);
}
// Final gHASH
byte[] X = new byte[BlockSize];
Pack.UInt64_To_BE(atLength * 8UL, X, 0);
Pack.UInt64_To_BE(totalLength * 8UL, X, 8);
gHASHBlock(S, X);
// T = MSBt(GCTRk(J0,S))
byte[] tag = new byte[BlockSize];
cipher.ProcessBlock(J0, 0, tag, 0);
GcmUtilities.Xor(tag, S);
int resultLen = extra;
// We place into macBlock our calculated value for T
this.macBlock = new byte[macSize];
Array.Copy(tag, 0, macBlock, 0, macSize);
if (forEncryption)
{
// Append T to the message
Array.Copy(macBlock, 0, output, outOff + bufOff, macSize);
resultLen += macSize;
}
else
{
// Retrieve the T value from the message and compare to calculated one
byte[] msgMac = new byte[macSize];
Array.Copy(bufBlock, extra, msgMac, 0, macSize);
if (!Arrays.FixedTimeEquals(this.macBlock, msgMac))
throw new Exception("mac check in GCM failed");
}
Reset(false);
return resultLen;
}
public void Reset()
{
Reset(true);
}
private void Reset(bool clearMac)
{
// note: we do not reset the nonce.
S = new byte[BlockSize];
S_at = new byte[BlockSize];
S_atPre = new byte[BlockSize];
atBlock = new byte[BlockSize];
atBlockPos = 0;
atLength = 0;
atLengthPre = 0;
counter = Arrays.Clone(J0);
counter32 = Pack.BE_To_UInt32(counter, 12);
blocksRemaining = uint.MaxValue - 1;
bufOff = 0;
totalLength = 0;
if (bufBlock != null)
{
Arrays.Fill(bufBlock, 0);
}
if (clearMac)
{
macBlock = null;
}
if (forEncryption)
{
initialised = false;
}
else if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
private void DecryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
{
byte[] ctrBlock = new byte[BlockSize];
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = inBuf[inOff + i + 0];
byte c1 = inBuf[inOff + i + 1];
byte c2 = inBuf[inOff + i + 2];
byte c3 = inBuf[inOff + i + 3];
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]);
outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]);
outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]);
outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]);
}
}
multiplier.MultiplyH(S);
}
private void DecryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
{
byte[] ctrBlock = new byte[BlockSize];
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = inBuf[inOff + i + 0];
byte c1 = inBuf[inOff + i + 1];
byte c2 = inBuf[inOff + i + 2];
byte c3 = inBuf[inOff + i + 3];
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]);
outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]);
outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]);
outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]);
}
}
multiplier.MultiplyH(S);
inOff += BlockSize;
outOff += BlockSize;
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = inBuf[inOff + i + 0];
byte c1 = inBuf[inOff + i + 1];
byte c2 = inBuf[inOff + i + 2];
byte c3 = inBuf[inOff + i + 3];
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = (byte)(c0 ^ ctrBlock[i + 0]);
outBuf[outOff + i + 1] = (byte)(c1 ^ ctrBlock[i + 1]);
outBuf[outOff + i + 2] = (byte)(c2 ^ ctrBlock[i + 2]);
outBuf[outOff + i + 3] = (byte)(c3 ^ ctrBlock[i + 3]);
}
}
multiplier.MultiplyH(S);
}
private void EncryptBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
{
byte[] ctrBlock = new byte[BlockSize];
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]);
byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]);
byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]);
byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]);
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = c0;
outBuf[outOff + i + 1] = c1;
outBuf[outOff + i + 2] = c2;
outBuf[outOff + i + 3] = c3;
}
}
multiplier.MultiplyH(S);
}
private void EncryptBlocks2(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
{
byte[] ctrBlock = new byte[BlockSize];
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]);
byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]);
byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]);
byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]);
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = c0;
outBuf[outOff + i + 1] = c1;
outBuf[outOff + i + 2] = c2;
outBuf[outOff + i + 3] = c3;
}
}
multiplier.MultiplyH(S);
inOff += BlockSize;
outOff += BlockSize;
GetNextCtrBlock(ctrBlock);
{
for (int i = 0; i < BlockSize; i += 4)
{
byte c0 = (byte)(ctrBlock[i + 0] ^ inBuf[inOff + i + 0]);
byte c1 = (byte)(ctrBlock[i + 1] ^ inBuf[inOff + i + 1]);
byte c2 = (byte)(ctrBlock[i + 2] ^ inBuf[inOff + i + 2]);
byte c3 = (byte)(ctrBlock[i + 3] ^ inBuf[inOff + i + 3]);
S[i + 0] ^= c0;
S[i + 1] ^= c1;
S[i + 2] ^= c2;
S[i + 3] ^= c3;
outBuf[outOff + i + 0] = c0;
outBuf[outOff + i + 1] = c1;
outBuf[outOff + i + 2] = c2;
outBuf[outOff + i + 3] = c3;
}
}
multiplier.MultiplyH(S);
}
private void GetNextCtrBlock(byte[] block)
{
Pack.UInt32_To_BE(++counter32, counter, 12);
cipher.ProcessBlock(counter, 0, block, 0);
}
private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff)
{
byte[] ctrBlock = new byte[BlockSize];
GetNextCtrBlock(ctrBlock);
if (forEncryption)
{
GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
gHASHPartial(S, buf, off, len);
}
else
{
gHASHPartial(S, buf, off, len);
GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
}
Array.Copy(buf, off, output, outOff, len);
totalLength += (uint)len;
}
private void gHASH(byte[] Y, byte[] b, int len)
{
for (int pos = 0; pos < len; pos += BlockSize)
{
int num = System.Math.Min(len - pos, BlockSize);
gHASHPartial(Y, b, pos, num);
}
}
private void gHASHBlock(byte[] Y, byte[] b)
{
GcmUtilities.Xor(Y, b);
multiplier.MultiplyH(Y);
}
private void gHASHBlock(byte[] Y, byte[] b, int off)
{
GcmUtilities.Xor(Y, b, off);
multiplier.MultiplyH(Y);
}
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
{
GcmUtilities.Xor(Y, b, off, len);
multiplier.MultiplyH(Y);
}
private void CheckStatus()
{
if (!initialised)
{
if (forEncryption)
throw new InvalidOperationException("GCM cipher cannot be reused for encryption");
throw new InvalidOperationException("GCM cipher needs to be initialized");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment