CMac.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Paddings;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs
  8. {
  9. /**
  10. * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
  11. * <p>
  12. * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
  13. * </p><p>
  14. * CMAC is a NIST recomendation - see
  15. * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
  16. * </p><p>
  17. * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
  18. * analyzed by Tetsu Iwata and Kaoru Kurosawa.
  19. * </p><p>
  20. * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message
  21. * Authentication Code). OMAC stands for One-Key CBC MAC.
  22. * </p><p>
  23. * It supports 128- or 64-bits block ciphers, with any key size, and returns
  24. * a MAC with dimension less or equal to the block size of the underlying
  25. * cipher.
  26. * </p>
  27. */
  28. public class CMac
  29. : IMac
  30. {
  31. private const byte CONSTANT_128 = (byte)0x87;
  32. private const byte CONSTANT_64 = (byte)0x1b;
  33. private byte[] ZEROES;
  34. private byte[] mac;
  35. private byte[] buf;
  36. private int bufOff;
  37. private IBlockCipherMode m_cipherMode;
  38. private int macSize;
  39. private byte[] L, Lu, Lu2;
  40. /**
  41. * create a standard MAC based on a CBC block cipher (64 or 128 bit block).
  42. * This will produce an authentication code the length of the block size
  43. * of the cipher.
  44. *
  45. * @param cipher the cipher to be used as the basis of the MAC generation.
  46. */
  47. public CMac(
  48. IBlockCipher cipher)
  49. : this(cipher, cipher.GetBlockSize() * 8)
  50. {
  51. }
  52. /**
  53. * create a standard MAC based on a block cipher with the size of the
  54. * MAC been given in bits.
  55. * <p/>
  56. * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
  57. * or 16 bits if being used as a data authenticator (FIPS Publication 113),
  58. * and in general should be less than the size of the block cipher as it reduces
  59. * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
  60. *
  61. * @param cipher the cipher to be used as the basis of the MAC generation.
  62. * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and @lt;= 128.
  63. */
  64. public CMac(
  65. IBlockCipher cipher,
  66. int macSizeInBits)
  67. {
  68. if ((macSizeInBits % 8) != 0)
  69. throw new ArgumentException("MAC size must be multiple of 8");
  70. if (macSizeInBits > (cipher.GetBlockSize() * 8))
  71. {
  72. throw new ArgumentException(
  73. "MAC size must be less or equal to "
  74. + (cipher.GetBlockSize() * 8));
  75. }
  76. if (cipher.GetBlockSize() != 8 && cipher.GetBlockSize() != 16)
  77. {
  78. throw new ArgumentException(
  79. "Block size must be either 64 or 128 bits");
  80. }
  81. m_cipherMode = new CbcBlockCipher(cipher);
  82. this.macSize = macSizeInBits / 8;
  83. mac = new byte[cipher.GetBlockSize()];
  84. buf = new byte[cipher.GetBlockSize()];
  85. ZEROES = new byte[cipher.GetBlockSize()];
  86. bufOff = 0;
  87. }
  88. public string AlgorithmName
  89. {
  90. get { return m_cipherMode.AlgorithmName; }
  91. }
  92. private static int ShiftLeft(byte[] block, byte[] output)
  93. {
  94. int i = block.Length;
  95. uint bit = 0;
  96. while (--i >= 0)
  97. {
  98. uint b = block[i];
  99. output[i] = (byte)((b << 1) | bit);
  100. bit = (b >> 7) & 1;
  101. }
  102. return (int)bit;
  103. }
  104. private static byte[] DoubleLu(byte[] input)
  105. {
  106. byte[] ret = new byte[input.Length];
  107. int carry = ShiftLeft(input, ret);
  108. int xor = input.Length == 16 ? CONSTANT_128 : CONSTANT_64;
  109. /*
  110. * NOTE: This construction is an attempt at a constant-time implementation.
  111. */
  112. ret[input.Length - 1] ^= (byte)(xor >> ((1 - carry) << 3));
  113. return ret;
  114. }
  115. public void Init(ICipherParameters parameters)
  116. {
  117. if (parameters is KeyParameter)
  118. {
  119. m_cipherMode.Init(true, parameters);
  120. //initializes the L, Lu, Lu2 numbers
  121. L = new byte[ZEROES.Length];
  122. m_cipherMode.ProcessBlock(ZEROES, 0, L, 0);
  123. Lu = DoubleLu(L);
  124. Lu2 = DoubleLu(Lu);
  125. }
  126. else if (parameters != null)
  127. {
  128. // CMAC mode does not permit IV to underlying CBC mode
  129. throw new ArgumentException("CMac mode only permits key to be set.", "parameters");
  130. }
  131. Reset();
  132. }
  133. public int GetMacSize()
  134. {
  135. return macSize;
  136. }
  137. public void Update(byte input)
  138. {
  139. if (bufOff == buf.Length)
  140. {
  141. m_cipherMode.ProcessBlock(buf, 0, mac, 0);
  142. bufOff = 0;
  143. }
  144. buf[bufOff++] = input;
  145. }
  146. public void BlockUpdate(byte[] inBytes, int inOff, int len)
  147. {
  148. if (len < 0)
  149. throw new ArgumentException("Can't have a negative input length!");
  150. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  151. BlockUpdate(inBytes.AsSpan(inOff, len));
  152. #else
  153. int blockSize = m_cipherMode.GetBlockSize();
  154. int gapLen = blockSize - bufOff;
  155. if (len > gapLen)
  156. {
  157. Array.Copy(inBytes, inOff, buf, bufOff, gapLen);
  158. m_cipherMode.ProcessBlock(buf, 0, mac, 0);
  159. bufOff = 0;
  160. len -= gapLen;
  161. inOff += gapLen;
  162. while (len > blockSize)
  163. {
  164. m_cipherMode.ProcessBlock(inBytes, inOff, mac, 0);
  165. len -= blockSize;
  166. inOff += blockSize;
  167. }
  168. }
  169. Array.Copy(inBytes, inOff, buf, bufOff, len);
  170. bufOff += len;
  171. #endif
  172. }
  173. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  174. public void BlockUpdate(ReadOnlySpan<byte> input)
  175. {
  176. int blockSize = m_cipherMode.GetBlockSize();
  177. int gapLen = blockSize - bufOff;
  178. if (input.Length > gapLen)
  179. {
  180. input[..gapLen].CopyTo(buf.AsSpan(bufOff));
  181. m_cipherMode.ProcessBlock(buf, mac);
  182. bufOff = 0;
  183. input = input[gapLen..];
  184. while (input.Length > blockSize)
  185. {
  186. m_cipherMode.ProcessBlock(input, mac);
  187. input = input[blockSize..];
  188. }
  189. }
  190. input.CopyTo(buf.AsSpan(bufOff));
  191. bufOff += input.Length;
  192. }
  193. #endif
  194. public int DoFinal(byte[] outBytes, int outOff)
  195. {
  196. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  197. return DoFinal(outBytes.AsSpan(outOff));
  198. #else
  199. int blockSize = m_cipherMode.GetBlockSize();
  200. byte[] lu;
  201. if (bufOff == blockSize)
  202. {
  203. lu = Lu;
  204. }
  205. else
  206. {
  207. new ISO7816d4Padding().AddPadding(buf, bufOff);
  208. lu = Lu2;
  209. }
  210. for (int i = 0; i < mac.Length; i++)
  211. {
  212. buf[i] ^= lu[i];
  213. }
  214. m_cipherMode.ProcessBlock(buf, 0, mac, 0);
  215. Array.Copy(mac, 0, outBytes, outOff, macSize);
  216. Reset();
  217. return macSize;
  218. #endif
  219. }
  220. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  221. public int DoFinal(Span<byte> output)
  222. {
  223. int blockSize = m_cipherMode.GetBlockSize();
  224. byte[] lu;
  225. if (bufOff == blockSize)
  226. {
  227. lu = Lu;
  228. }
  229. else
  230. {
  231. new ISO7816d4Padding().AddPadding(buf, bufOff);
  232. lu = Lu2;
  233. }
  234. for (int i = 0; i < mac.Length; i++)
  235. {
  236. buf[i] ^= lu[i];
  237. }
  238. m_cipherMode.ProcessBlock(buf, mac);
  239. mac.AsSpan(0, macSize).CopyTo(output);
  240. Reset();
  241. return macSize;
  242. }
  243. #endif
  244. /**
  245. * Reset the mac generator.
  246. */
  247. public void Reset()
  248. {
  249. /*
  250. * clean the buffer.
  251. */
  252. Array.Clear(buf, 0, buf.Length);
  253. bufOff = 0;
  254. /*
  255. * Reset the underlying cipher.
  256. */
  257. m_cipherMode.Reset();
  258. }
  259. }
  260. }
  261. #pragma warning restore
  262. #endif