public int DoFinal(byte[] output, int outOff)
{
int extra = bufOff;
if (!forEncryption)
{
if (extra < macSize)
throw new InvalidCipherTextException("data too short");
extra -= macSize;
}
if (extra > 0)
{
byte[] tmp = new byte[BlockSize];
Array.Copy(bufBlock, 0, tmp, 0, extra);
gCTRBlock(tmp, extra, output, outOff);
}
// Final gHASH
BigInteger X = BigInteger.ValueOf(A.Length * 8).ShiftLeft(64).Add(
BigInteger.ValueOf(totalLength * 8));
//trace("len(A)||len(C): " + dumpBigInt(X));
S = multiply(S.Xor(X), H);
//trace("GHASH(H,A,C): " + dumpBigInt(S));
// T = MSBt(GCTRk(J0,S))
byte[] tBytes = new byte[BlockSize];
cipher.ProcessBlock(J0, 0, tBytes, 0);
//trace("E(K,Y0): " + new string(Hex.encode(tmp)));
BigInteger T = S.Xor(new BigInteger(1, tBytes));
// TODO Fix this if tagLength becomes configurable
byte[] tag = asBlock(T);
//trace("T: " + new string(Hex.encode(tag)));
int resultLen = extra;
if (forEncryption)
{
this.macBlock = tag;
Array.Copy(tag, 0, output, outOff + bufOff, tag.Length);
resultLen += tag.Length;
}
else
{
this.macBlock = new byte[macSize];
Array.Copy(bufBlock, extra, macBlock, 0, macSize);
if (!Arrays.AreEqual(tag, this.macBlock))
throw new InvalidCipherTextException("mac check in GCM failed");
}
Reset(false);
return resultLen;
}