123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- using System;
- using System.Collections;
- using Org.BouncyCastle.Crypto.Parameters;
- using Org.BouncyCastle.Utilities;
- namespace Org.BouncyCastle.Crypto.Modes
- {
- /**
- * An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
- * Authenticated-Encryption Algorithm</a>, licensed per:
- *
- * <blockquote><p><a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
- * Open-Source Software Implementations of OCB</a> (Jan 9, 2013) - 'License 1'<br/>
- * Under this license, you are authorized to make, use, and distribute open-source software
- * implementations of OCB. This license terminates for you if you sue someone over their open-source
- * software implementation of OCB claiming that you have a patent covering their implementation.
- * </p><p>
- * This is a non-binding summary of a legal document (the link above). The parameters of the license
- * are specified in the license document and that document is controlling.</p></blockquote>
- */
- public class OcbBlockCipher
- : IAeadBlockCipher
- {
- private const int BLOCK_SIZE = 16;
- private readonly IBlockCipher hashCipher;
- private readonly IBlockCipher mainCipher;
- /*
- * CONFIGURATION
- */
- private bool forEncryption;
- private int macSize;
- private byte[] initialAssociatedText;
- /*
- * KEY-DEPENDENT
- */
- // NOTE: elements are lazily calculated
- private IList L;
- private byte[] L_Asterisk, L_Dollar;
- /*
- * NONCE-DEPENDENT
- */
- private byte[] KtopInput = null;
- private byte[] Stretch = new byte[24];
- private byte[] OffsetMAIN_0 = new byte[16];
- /*
- * PER-ENCRYPTION/DECRYPTION
- */
- private byte[] hashBlock, mainBlock;
- private int hashBlockPos, mainBlockPos;
- private long hashBlockCount, mainBlockCount;
- private byte[] OffsetHASH;
- private byte[] Sum;
- private byte[] OffsetMAIN = new byte[16];
- private byte[] Checksum;
- // NOTE: The MAC value is preserved after doFinal
- private byte[] macBlock;
- public OcbBlockCipher(IBlockCipher hashCipher, IBlockCipher mainCipher)
- {
- if (hashCipher == null)
- throw new ArgumentNullException("hashCipher");
- if (hashCipher.GetBlockSize() != BLOCK_SIZE)
- throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "hashCipher");
- if (mainCipher == null)
- throw new ArgumentNullException("mainCipher");
- if (mainCipher.GetBlockSize() != BLOCK_SIZE)
- throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "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 IBlockCipher GetUnderlyingCipher()
- {
- return mainCipher;
- }
- public virtual string AlgorithmName
- {
- get { return mainCipher.AlgorithmName + "/OCB"; }
- }
- public virtual void Init(bool forEncryption, ICipherParameters parameters)
- {
- bool oldForEncryption = this.forEncryption;
- this.forEncryption = forEncryption;
- this.macBlock = null;
- KeyParameter keyParameter;
- byte[] N;
- if (parameters is AeadParameters)
- {
- AeadParameters aeadParameters = (AeadParameters) parameters;
- N = aeadParameters.GetNonce();
- initialAssociatedText = aeadParameters.GetAssociatedText();
- int macSizeBits = aeadParameters.MacSize;
- if (macSizeBits < 64 || macSizeBits > 128 || macSizeBits % 8 != 0)
- throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
- macSize = macSizeBits / 8;
- keyParameter = aeadParameters.Key;
- }
- else if (parameters is ParametersWithIV)
- {
- ParametersWithIV parametersWithIV = (ParametersWithIV) parameters;
- N = parametersWithIV.GetIV();
- initialAssociatedText = null;
- macSize = 16;
- keyParameter = (KeyParameter) parametersWithIV.Parameters;
- }
- else
- {
- throw new ArgumentException("invalid parameters passed to OCB");
- }
- this.hashBlock = new byte[16];
- this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)];
- if (N == null)
- {
- N = new byte[0];
- }
- if (N.Length > 15)
- {
- throw new ArgumentException("IV must be no more than 15 bytes");
- }
- /*
- * KEY-DEPENDENT INITIALISATION
- */
- if (keyParameter != null)
- {
- // hashCipher always used in forward mode
- hashCipher.Init(true, keyParameter);
- mainCipher.Init(forEncryption, keyParameter);
- KtopInput = null;
- }
- else if (oldForEncryption != forEncryption)
- {
- throw new ArgumentException("cannot change encrypting state without providing key.");
- }
- this.L_Asterisk = new byte[16];
- hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0);
- this.L_Dollar = OCB_double(L_Asterisk);
- this.L = Org.BouncyCastle.Utilities.Platform.CreateArrayList();
- this.L.Add(OCB_double(L_Dollar));
- /*
- * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION
- */
- int bottom = ProcessNonce(N);
- int bits = bottom % 8, bytes = bottom / 8;
- if (bits == 0)
- {
- Array.Copy(Stretch, bytes, OffsetMAIN_0, 0, 16);
- }
- else
- {
- for (int i = 0; i < 16; ++i)
- {
- uint b1 = Stretch[bytes];
- uint b2 = Stretch[++bytes];
- this.OffsetMAIN_0[i] = (byte) ((b1 << bits) | (b2 >> (8 - bits)));
- }
- }
- this.hashBlockPos = 0;
- this.mainBlockPos = 0;
- this.hashBlockCount = 0;
- this.mainBlockCount = 0;
- this.OffsetHASH = new byte[16];
- this.Sum = new byte[16];
- Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
- this.Checksum = new byte[16];
- if (initialAssociatedText != null)
- {
- ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
- }
- }
- protected virtual int ProcessNonce(byte[] N)
- {
- byte[] nonce = new byte[16];
- Array.Copy(N, 0, nonce, nonce.Length - N.Length, N.Length);
- nonce[0] = (byte)(macSize << 4);
- nonce[15 - N.Length] |= 1;
- int bottom = nonce[15] & 0x3F;
- nonce[15] &= 0xC0;
- /*
- * When used with incrementing nonces, the cipher is only applied once every 64 inits.
- */
- if (KtopInput == null || !Arrays.AreEqual(nonce, KtopInput))
- {
- byte[] Ktop = new byte[16];
- KtopInput = nonce;
- hashCipher.ProcessBlock(KtopInput, 0, Ktop, 0);
- Array.Copy(Ktop, 0, Stretch, 0, 16);
- for (int i = 0; i < 8; ++i)
- {
- Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
- }
- }
- return bottom;
- }
- public virtual int GetBlockSize()
- {
- return BLOCK_SIZE;
- }
- public virtual byte[] GetMac()
- {
- return Arrays.Clone(macBlock);
- }
- public virtual int GetOutputSize(int len)
- {
- int totalData = len + mainBlockPos;
- if (forEncryption)
- {
- return totalData + macSize;
- }
- return totalData < macSize ? 0 : totalData - macSize;
- }
- public virtual int GetUpdateOutputSize(int len)
- {
- int totalData = len + mainBlockPos;
- if (!forEncryption)
- {
- if (totalData < macSize)
- {
- return 0;
- }
- totalData -= macSize;
- }
- return totalData - totalData % BLOCK_SIZE;
- }
- 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 BLOCK_SIZE;
- }
- return 0;
- }
- public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
- {
- int resultLen = 0;
- for (int i = 0; i < len; ++i)
- {
- mainBlock[mainBlockPos] = input[inOff + i];
- if (++mainBlockPos == mainBlock.Length)
- {
- ProcessMainBlock(output, outOff + resultLen);
- resultLen += BLOCK_SIZE;
- }
- }
- return resultLen;
- }
- public virtual int DoFinal(byte[] output, int outOff)
- {
- /*
- * For decryption, get the tag from the end of the message
- */
- byte[] tag = null;
- if (!forEncryption) {
- if (mainBlockPos < macSize)
- throw new InvalidCipherTextException("data too short");
- mainBlockPos -= macSize;
- tag = new byte[macSize];
- Array.Copy(mainBlock, mainBlockPos, tag, 0, macSize);
- }
- /*
- * HASH: Process any final partial block; compute final hash value
- */
- if (hashBlockPos > 0)
- {
- OCB_extend(hashBlock, hashBlockPos);
- UpdateHASH(L_Asterisk);
- }
- /*
- * OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block
- */
- if (mainBlockPos > 0)
- {
- if (forEncryption)
- {
- OCB_extend(mainBlock, mainBlockPos);
- Xor(Checksum, mainBlock);
- }
- Xor(OffsetMAIN, L_Asterisk);
- byte[] Pad = new byte[16];
- hashCipher.ProcessBlock(OffsetMAIN, 0, Pad, 0);
- Xor(mainBlock, Pad);
- 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);
- }
- }
- /*
- * OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag
- */
- Xor(Checksum, OffsetMAIN);
- Xor(Checksum, L_Dollar);
- hashCipher.ProcessBlock(Checksum, 0, Checksum, 0);
- Xor(Checksum, Sum);
- this.macBlock = new byte[macSize];
- Array.Copy(Checksum, 0, macBlock, 0, macSize);
- /*
- * Validate or append tag and reset this cipher for the next run
- */
- int resultLen = mainBlockPos;
- if (forEncryption)
- {
- Check.OutputLength(output, outOff, resultLen + macSize, "Output buffer too short");
- // Append tag to the message
- Array.Copy(macBlock, 0, output, outOff + resultLen, macSize);
- resultLen += macSize;
- }
- else
- {
- // Compare the tag from the message with the calculated one
- if (!Arrays.ConstantTimeAreEqual(macBlock, tag))
- throw new InvalidCipherTextException("mac check in OCB failed");
- }
- Reset(false);
- return resultLen;
- }
- 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((byte[]) L[L.Count - 1]));
- }
- return (byte[])L[n];
- }
- protected virtual void ProcessHashBlock()
- {
- /*
- * HASH: Process any whole blocks
- */
- UpdateHASH(GetLSub(OCB_ntz(++hashBlockCount)));
- hashBlockPos = 0;
- }
- protected virtual void ProcessMainBlock(byte[] output, int outOff)
- {
- Check.DataLength(output, outOff, BLOCK_SIZE, "Output buffer too short");
- /*
- * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
- */
- 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, BLOCK_SIZE, mainBlock, 0, macSize);
- mainBlockPos = macSize;
- }
- }
- protected virtual void Reset(bool clearMac)
- {
- hashCipher.Reset();
- mainCipher.Reset();
- 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[] result = new byte[16];
- int carry = ShiftLeft(block, result);
- /*
- * NOTE: This construction is an attempt at a constant-time implementation.
- */
- result[15] ^= (byte)(0x87 >> ((1 - carry) << 3));
- return result;
- }
- protected static void OCB_extend(byte[] block, int pos)
- {
- block[pos] = (byte) 0x80;
- while (++pos < 16)
- {
- block[pos] = 0;
- }
- }
- protected static int OCB_ntz(long x)
- {
- if (x == 0)
- {
- return 64;
- }
- int n = 0;
- ulong ux = (ulong)x;
- while ((ux & 1UL) == 0UL)
- {
- ++n;
- ux >>= 1;
- }
- return n;
- }
- protected static int ShiftLeft(byte[] block, byte[] output)
- {
- int i = 16;
- uint bit = 0;
- while (--i >= 0)
- {
- uint b = block[i];
- output[i] = (byte) ((b << 1) | bit);
- bit = (b >> 7) & 1;
- }
- return (int)bit;
- }
- protected static void Xor(byte[] block, byte[] val)
- {
- for (int i = 15; i >= 0; --i)
- {
- block[i] ^= val[i];
- }
- }
- }
- }
- #endif
|