OcbBlockCipher
An implementation of RFC 7253 on The OCB
Authenticated-Encryption Algorithm.
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
namespace Org.BouncyCastle.Crypto.Modes
{
public class OcbBlockCipher : IAeadBlockCipher, IAeadCipher
{
private const int BLOCK_SIZE = 16;
private readonly IBlockCipher hashCipher;
private readonly IBlockCipher mainCipher;
private bool forEncryption;
private int macSize;
private byte[] initialAssociatedText;
private IList<byte[]> L;
private byte[] L_Asterisk;
private byte[] L_Dollar;
private byte[] KtopInput;
private byte[] Stretch = new byte[24];
private byte[] OffsetMAIN_0 = new byte[16];
private byte[] hashBlock;
private byte[] mainBlock;
private int hashBlockPos;
private int mainBlockPos;
private long hashBlockCount;
private long mainBlockCount;
private byte[] OffsetHASH;
private byte[] Sum;
private byte[] OffsetMAIN = new byte[16];
private byte[] Checksum;
private byte[] macBlock;
public virtual string AlgorithmName => mainCipher.AlgorithmName + "/OCB";
public virtual IBlockCipher UnderlyingCipher => mainCipher;
public OcbBlockCipher(IBlockCipher hashCipher, IBlockCipher mainCipher)
{
if (hashCipher == null)
throw new ArgumentNullException("hashCipher");
int num;
if (hashCipher.GetBlockSize() != 16) {
num = 16;
throw new ArgumentException("must have a block size of " + num.ToString(), "hashCipher");
}
if (mainCipher == null)
throw new ArgumentNullException("mainCipher");
if (mainCipher.GetBlockSize() != 16) {
num = 16;
throw new ArgumentException("must have a block size of " + num.ToString(), "mainCipher");
}
if (!hashCipher.AlgorithmName.Equals(mainCipher.AlgorithmName))
throw new ArgumentException("'hashCipher' and 'mainCipher' must be the same algorithm");
this.hashCipher = hashCipher;
this.mainCipher = mainCipher;
}
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
bool flag = this.forEncryption;
this.forEncryption = forEncryption;
macBlock = null;
AeadParameters aeadParameters = parameters as AeadParameters;
byte[] array;
KeyParameter keyParameter;
if (aeadParameters != null) {
array = aeadParameters.GetNonce();
initialAssociatedText = aeadParameters.GetAssociatedText();
int num = aeadParameters.MacSize;
if (num < 64 || num > 128 || num % 8 != 0)
throw new ArgumentException("Invalid value for MAC size: " + num.ToString());
macSize = num / 8;
keyParameter = aeadParameters.Key;
} else {
ParametersWithIV parametersWithIV = parameters as ParametersWithIV;
if (parametersWithIV == null)
throw new ArgumentException("invalid parameters passed to OCB");
array = parametersWithIV.GetIV();
initialAssociatedText = null;
macSize = 16;
keyParameter = (KeyParameter)parametersWithIV.Parameters;
}
hashBlock = new byte[16];
mainBlock = new byte[forEncryption ? 16 : (16 + macSize)];
if (array.Length > 15)
throw new ArgumentException("IV must be no more than 15 bytes");
if (keyParameter != null) {
hashCipher.Init(true, keyParameter);
mainCipher.Init(forEncryption, keyParameter);
KtopInput = null;
} else if (flag != forEncryption) {
throw new ArgumentException("cannot change encrypting state without providing key.");
}
L_Asterisk = new byte[16];
hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0);
L_Dollar = OCB_double(L_Asterisk);
L = new List<byte[]>();
L.Add(OCB_double(L_Dollar));
int num2 = ProcessNonce(array);
int num3 = num2 % 8;
int num4 = num2 / 8;
if (num3 == 0)
Array.Copy(Stretch, num4, OffsetMAIN_0, 0, 16);
else {
for (int i = 0; i < 16; i++) {
uint num5 = Stretch[num4];
uint num6 = Stretch[++num4];
OffsetMAIN_0[i] = (byte)((num5 << num3) | (num6 >> 8 - num3));
}
}
hashBlockPos = 0;
mainBlockPos = 0;
hashBlockCount = 0;
mainBlockCount = 0;
OffsetHASH = new byte[16];
Sum = new byte[16];
Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
Checksum = new byte[16];
if (initialAssociatedText != null)
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
protected virtual int ProcessNonce(byte[] N)
{
byte[] array = new byte[16];
Array.Copy(N, 0, array, array.Length - N.Length, N.Length);
array[0] = (byte)(macSize << 4);
array[15 - N.Length] |= 1;
int result = array[15] & 63;
array[15] &= 192;
if (KtopInput == null || !Arrays.AreEqual(array, KtopInput)) {
byte[] array2 = new byte[16];
KtopInput = array;
hashCipher.ProcessBlock(KtopInput, 0, array2, 0);
Array.Copy(array2, 0, Stretch, 0, 16);
for (int i = 0; i < 8; i++) {
Stretch[16 + i] = (byte)(array2[i] ^ array2[i + 1]);
}
}
return result;
}
public virtual int GetBlockSize()
{
return 16;
}
public virtual byte[] GetMac()
{
if (macBlock != null)
return Arrays.Clone(macBlock);
return new byte[macSize];
}
public virtual int GetOutputSize(int len)
{
int num = len + mainBlockPos;
if (forEncryption)
return num + macSize;
if (num >= macSize)
return num - macSize;
return 0;
}
public virtual int GetUpdateOutputSize(int len)
{
int num = len + mainBlockPos;
if (!forEncryption) {
if (num < macSize)
return 0;
num -= macSize;
}
return num - num % 16;
}
public virtual void ProcessAadByte(byte input)
{
hashBlock[hashBlockPos] = input;
if (++hashBlockPos == hashBlock.Length)
ProcessHashBlock();
}
public virtual void ProcessAadBytes(byte[] input, int off, int len)
{
for (int i = 0; i < len; i++) {
hashBlock[hashBlockPos] = input[off + i];
if (++hashBlockPos == hashBlock.Length)
ProcessHashBlock();
}
}
public virtual int ProcessByte(byte input, byte[] output, int outOff)
{
mainBlock[mainBlockPos] = input;
if (++mainBlockPos == mainBlock.Length) {
ProcessMainBlock(output, outOff);
return 16;
}
return 0;
}
public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
int num = 0;
for (int i = 0; i < len; i++) {
mainBlock[mainBlockPos] = input[inOff + i];
if (++mainBlockPos == mainBlock.Length) {
ProcessMainBlock(output, outOff + num);
num += 16;
}
}
return num;
}
public virtual int DoFinal(byte[] output, int outOff)
{
byte[] array = null;
if (!forEncryption) {
if (mainBlockPos < macSize)
throw new InvalidCipherTextException("data too short");
mainBlockPos -= macSize;
array = new byte[macSize];
Array.Copy(mainBlock, mainBlockPos, array, 0, macSize);
}
if (hashBlockPos > 0) {
OCB_extend(hashBlock, hashBlockPos);
UpdateHASH(L_Asterisk);
}
if (mainBlockPos > 0) {
if (forEncryption) {
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
Xor(OffsetMAIN, L_Asterisk);
byte[] array2 = new byte[16];
hashCipher.ProcessBlock(OffsetMAIN, 0, array2, 0);
Xor(mainBlock, array2);
Check.OutputLength(output, outOff, mainBlockPos, "output buffer too short");
Array.Copy(mainBlock, 0, output, outOff, mainBlockPos);
if (!forEncryption) {
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
}
Xor(Checksum, OffsetMAIN);
Xor(Checksum, L_Dollar);
hashCipher.ProcessBlock(Checksum, 0, Checksum, 0);
Xor(Checksum, Sum);
macBlock = new byte[macSize];
Array.Copy(Checksum, 0, macBlock, 0, macSize);
int num = mainBlockPos;
if (forEncryption) {
Check.OutputLength(output, outOff, num + macSize, "output buffer too short");
Array.Copy(macBlock, 0, output, outOff + num, macSize);
num += macSize;
} else if (!Arrays.FixedTimeEquals(macBlock, array)) {
throw new InvalidCipherTextException("mac check in OCB failed");
}
Reset(false);
return num;
}
public virtual void Reset()
{
Reset(true);
}
protected virtual void Clear(byte[] bs)
{
if (bs != null)
Array.Clear(bs, 0, bs.Length);
}
protected virtual byte[] GetLSub(int n)
{
while (n >= L.Count) {
L.Add(OCB_double(L[L.Count - 1]));
}
return L[n];
}
protected virtual void ProcessHashBlock()
{
UpdateHASH(GetLSub(OCB_ntz(++hashBlockCount)));
hashBlockPos = 0;
}
protected virtual void ProcessMainBlock(byte[] output, int outOff)
{
Check.OutputLength(output, outOff, 16, "output buffer too short");
if (forEncryption) {
Xor(Checksum, mainBlock);
mainBlockPos = 0;
}
Xor(OffsetMAIN, GetLSub(OCB_ntz(++mainBlockCount)));
Xor(mainBlock, OffsetMAIN);
mainCipher.ProcessBlock(mainBlock, 0, mainBlock, 0);
Xor(mainBlock, OffsetMAIN);
Array.Copy(mainBlock, 0, output, outOff, 16);
if (!forEncryption) {
Xor(Checksum, mainBlock);
Array.Copy(mainBlock, 16, mainBlock, 0, macSize);
mainBlockPos = macSize;
}
}
protected virtual void Reset(bool clearMac)
{
Clear(hashBlock);
Clear(mainBlock);
hashBlockPos = 0;
mainBlockPos = 0;
hashBlockCount = 0;
mainBlockCount = 0;
Clear(OffsetHASH);
Clear(Sum);
Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
Clear(Checksum);
if (clearMac)
macBlock = null;
if (initialAssociatedText != null)
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
protected virtual void UpdateHASH(byte[] LSub)
{
Xor(OffsetHASH, LSub);
Xor(hashBlock, OffsetHASH);
hashCipher.ProcessBlock(hashBlock, 0, hashBlock, 0);
Xor(Sum, hashBlock);
}
protected static byte[] OCB_double(byte[] block)
{
byte[] array = new byte[16];
int num = ShiftLeft(block, array);
array[15] ^= (byte)(135 >> (1 - num << 3));
return array;
}
protected static void OCB_extend(byte[] block, int pos)
{
block[pos] = 128;
while (++pos < 16) {
block[pos] = 0;
}
}
protected static int OCB_ntz(long x)
{
return Longs.NumberOfTrailingZeros(x);
}
protected static int ShiftLeft(byte[] block, byte[] output)
{
int num = 16;
uint num2 = 0;
while (--num >= 0) {
uint num3 = block[num];
output[num] = (byte)((num3 << 1) | num2);
num2 = ((num3 >> 7) & 1);
}
return (int)num2;
}
protected static void Xor(byte[] block, byte[] val)
{
Bytes.XorTo(16, val, block);
}
}
}