Created
July 30, 2023 20:02
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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