SkeinEngine.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections.Generic;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  10. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests
  11. {
  12. /// <summary>
  13. /// Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
  14. /// sizes, based on the <see cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
  15. /// </summary>
  16. /// <remarks>
  17. /// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
  18. /// competition in October 2010.
  19. /// <p/>
  20. /// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
  21. /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
  22. /// <p/>
  23. /// This implementation is the basis for <see cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> and <see cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs.SkeinMac"/>, implementing the
  24. /// parameter based configuration system that allows Skein to be adapted to multiple applications. <br/>
  25. /// Initialising the engine with <see cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> allows standard and arbitrary parameters to
  26. /// be applied during the Skein hash function.
  27. /// <p/>
  28. /// Implemented:
  29. /// <ul>
  30. /// <li>256, 512 and 1024 bit internal states.</li>
  31. /// <li>Full 96 bit input length.</li>
  32. /// <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
  33. /// parameters.</li>
  34. /// <li>Arbitrary output size in 1 byte intervals.</li>
  35. /// </ul>
  36. /// <p/>
  37. /// Not implemented:
  38. /// <ul>
  39. /// <li>Sub-byte length input (bit padding).</li>
  40. /// <li>Tree hashing.</li>
  41. /// </ul>
  42. /// </remarks>
  43. /// <seealso cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/>
  44. public class SkeinEngine
  45. : IMemoable
  46. {
  47. /// <summary>
  48. /// 256 bit block size - Skein-256
  49. /// </summary>
  50. public const int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
  51. /// <summary>
  52. /// 512 bit block size - Skein-512
  53. /// </summary>
  54. public const int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
  55. /// <summary>
  56. /// 1024 bit block size - Skein-1024
  57. /// </summary>
  58. public const int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
  59. // Minimal at present, but more complex when tree hashing is implemented
  60. private class Configuration
  61. {
  62. private byte[] bytes = new byte[32];
  63. public Configuration(long outputSizeBits)
  64. {
  65. // 0..3 = ASCII SHA3
  66. bytes[0] = (byte)'S';
  67. bytes[1] = (byte)'H';
  68. bytes[2] = (byte)'A';
  69. bytes[3] = (byte)'3';
  70. // 4..5 = version number in LSB order
  71. bytes[4] = 1;
  72. bytes[5] = 0;
  73. // 8..15 = output length
  74. Pack.UInt64_To_LE((ulong)outputSizeBits, bytes, 8);
  75. }
  76. public byte[] Bytes
  77. {
  78. get { return bytes; }
  79. }
  80. }
  81. public class Parameter
  82. {
  83. private int type;
  84. private byte[] value;
  85. public Parameter(int type, byte[] value)
  86. {
  87. this.type = type;
  88. this.value = value;
  89. }
  90. public int Type
  91. {
  92. get { return type; }
  93. }
  94. public byte[] Value
  95. {
  96. get { return value; }
  97. }
  98. }
  99. /**
  100. * The parameter type for the Skein key.
  101. */
  102. private const int PARAM_TYPE_KEY = 0;
  103. /**
  104. * The parameter type for the Skein configuration block.
  105. */
  106. private const int PARAM_TYPE_CONFIG = 4;
  107. /**
  108. * The parameter type for the message.
  109. */
  110. private const int PARAM_TYPE_MESSAGE = 48;
  111. /**
  112. * The parameter type for the output transformation.
  113. */
  114. private const int PARAM_TYPE_OUTPUT = 63;
  115. /**
  116. * Precalculated UBI(CFG) states for common state/output combinations without key or other
  117. * pre-message params.
  118. */
  119. private static readonly IDictionary<int, ulong[]> InitialStates = new Dictionary<int, ulong[]>();
  120. static SkeinEngine()
  121. {
  122. // From Appendix C of the Skein 1.3 NIST submission
  123. InitialState(SKEIN_256, 128, new ulong[]{
  124. 0xe1111906964d7260UL,
  125. 0x883daaa77c8d811cUL,
  126. 0x10080df491960f7aUL,
  127. 0xccf7dde5b45bc1c2UL});
  128. InitialState(SKEIN_256, 160, new ulong[]{
  129. 0x1420231472825e98UL,
  130. 0x2ac4e9a25a77e590UL,
  131. 0xd47a58568838d63eUL,
  132. 0x2dd2e4968586ab7dUL});
  133. InitialState(SKEIN_256, 224, new ulong[]{
  134. 0xc6098a8c9ae5ea0bUL,
  135. 0x876d568608c5191cUL,
  136. 0x99cb88d7d7f53884UL,
  137. 0x384bddb1aeddb5deUL});
  138. InitialState(SKEIN_256, 256, new ulong[]{
  139. 0xfc9da860d048b449UL,
  140. 0x2fca66479fa7d833UL,
  141. 0xb33bc3896656840fUL,
  142. 0x6a54e920fde8da69UL});
  143. InitialState(SKEIN_512, 128, new ulong[]{
  144. 0xa8bc7bf36fbf9f52UL,
  145. 0x1e9872cebd1af0aaUL,
  146. 0x309b1790b32190d3UL,
  147. 0xbcfbb8543f94805cUL,
  148. 0x0da61bcd6e31b11bUL,
  149. 0x1a18ebead46a32e3UL,
  150. 0xa2cc5b18ce84aa82UL,
  151. 0x6982ab289d46982dUL});
  152. InitialState(SKEIN_512, 160, new ulong[]{
  153. 0x28b81a2ae013bd91UL,
  154. 0xc2f11668b5bdf78fUL,
  155. 0x1760d8f3f6a56f12UL,
  156. 0x4fb747588239904fUL,
  157. 0x21ede07f7eaf5056UL,
  158. 0xd908922e63ed70b8UL,
  159. 0xb8ec76ffeccb52faUL,
  160. 0x01a47bb8a3f27a6eUL});
  161. InitialState(SKEIN_512, 224, new ulong[]{
  162. 0xccd0616248677224UL,
  163. 0xcba65cf3a92339efUL,
  164. 0x8ccd69d652ff4b64UL,
  165. 0x398aed7b3ab890b4UL,
  166. 0x0f59d1b1457d2bd0UL,
  167. 0x6776fe6575d4eb3dUL,
  168. 0x99fbc70e997413e9UL,
  169. 0x9e2cfccfe1c41ef7UL});
  170. InitialState(SKEIN_512, 384, new ulong[]{
  171. 0xa3f6c6bf3a75ef5fUL,
  172. 0xb0fef9ccfd84faa4UL,
  173. 0x9d77dd663d770cfeUL,
  174. 0xd798cbf3b468fddaUL,
  175. 0x1bc4a6668a0e4465UL,
  176. 0x7ed7d434e5807407UL,
  177. 0x548fc1acd4ec44d6UL,
  178. 0x266e17546aa18ff8UL});
  179. InitialState(SKEIN_512, 512, new ulong[]{
  180. 0x4903adff749c51ceUL,
  181. 0x0d95de399746df03UL,
  182. 0x8fd1934127c79bceUL,
  183. 0x9a255629ff352cb1UL,
  184. 0x5db62599df6ca7b0UL,
  185. 0xeabe394ca9d5c3f4UL,
  186. 0x991112c71a75b523UL,
  187. 0xae18a40b660fcc33UL});
  188. }
  189. private static void InitialState(int blockSize, int outputSize, ulong[] state)
  190. {
  191. InitialStates.Add(VariantIdentifier(blockSize / 8, outputSize / 8), state);
  192. }
  193. private static int VariantIdentifier(int blockSizeBytes, int outputSizeBytes)
  194. {
  195. return (outputSizeBytes << 16) | blockSizeBytes;
  196. }
  197. private class UbiTweak
  198. {
  199. /**
  200. * Point at which position might overflow long, so switch to add with carry logic
  201. */
  202. private const ulong LOW_RANGE = ulong.MaxValue - uint.MaxValue;
  203. /**
  204. * Bit 127 = final
  205. */
  206. private const ulong T1_FINAL = 1UL << 63;
  207. /**
  208. * Bit 126 = first
  209. */
  210. private const ulong T1_FIRST = 1UL << 62;
  211. /**
  212. * UBI uses a 128 bit tweak
  213. */
  214. private ulong[] tweak = new ulong[2];
  215. /**
  216. * Whether 64 bit position exceeded
  217. */
  218. private bool extendedPosition;
  219. public UbiTweak()
  220. {
  221. Reset();
  222. }
  223. public void Reset(UbiTweak tweak)
  224. {
  225. this.tweak = Arrays.Clone(tweak.tweak, this.tweak);
  226. this.extendedPosition = tweak.extendedPosition;
  227. }
  228. public void Reset()
  229. {
  230. tweak[0] = 0;
  231. tweak[1] = 0;
  232. extendedPosition = false;
  233. First = true;
  234. }
  235. public uint Type
  236. {
  237. get
  238. {
  239. return (uint)((tweak[1] >> 56) & 0x3FUL);
  240. }
  241. set
  242. {
  243. // Bits 120..125 = type
  244. tweak[1] = (tweak[1] & 0xFFFFFFC000000000UL) | ((value & 0x3FUL) << 56);
  245. }
  246. }
  247. public bool First
  248. {
  249. get
  250. {
  251. return ((tweak[1] & T1_FIRST) != 0);
  252. }
  253. set
  254. {
  255. if (value)
  256. {
  257. tweak[1] |= T1_FIRST;
  258. }
  259. else
  260. {
  261. tweak[1] &= ~T1_FIRST;
  262. }
  263. }
  264. }
  265. public bool Final
  266. {
  267. get
  268. {
  269. return ((tweak[1] & T1_FINAL) != 0);
  270. }
  271. set
  272. {
  273. if (value)
  274. {
  275. tweak[1] |= T1_FINAL;
  276. }
  277. else
  278. {
  279. tweak[1] &= ~T1_FINAL;
  280. }
  281. }
  282. }
  283. /**
  284. * Advances the position in the tweak by the specified value.
  285. */
  286. public void AdvancePosition(int advance)
  287. {
  288. // Bits 0..95 = position
  289. if (extendedPosition)
  290. {
  291. ulong[] parts = new ulong[3];
  292. parts[0] = tweak[0] & 0xFFFFFFFFUL;
  293. parts[1] = (tweak[0] >> 32) & 0xFFFFFFFFUL;
  294. parts[2] = tweak[1] & 0xFFFFFFFFUL;
  295. ulong carry = (ulong)advance;
  296. for (int i = 0; i < parts.Length; i++)
  297. {
  298. carry += parts[i];
  299. parts[i] = carry;
  300. carry >>= 32;
  301. }
  302. tweak[0] = ((parts[1] & 0xFFFFFFFFUL) << 32) | (parts[0] & 0xFFFFFFFFUL);
  303. tweak[1] = (tweak[1] & 0xFFFFFFFF00000000UL) | (parts[2] & 0xFFFFFFFFUL);
  304. }
  305. else
  306. {
  307. ulong position = tweak[0];
  308. position += (uint)advance;
  309. tweak[0] = position;
  310. if (position > LOW_RANGE)
  311. {
  312. extendedPosition = true;
  313. }
  314. }
  315. }
  316. public ulong[] GetWords()
  317. {
  318. return tweak;
  319. }
  320. public override string ToString()
  321. {
  322. return Type + " first: " + First + ", final: " + Final;
  323. }
  324. }
  325. /**
  326. * The Unique Block Iteration chaining mode.
  327. */
  328. // TODO: This might be better as methods...
  329. private class UBI
  330. {
  331. private readonly UbiTweak tweak = new UbiTweak();
  332. private readonly SkeinEngine engine;
  333. /**
  334. * Buffer for the current block of message data
  335. */
  336. private byte[] currentBlock;
  337. /**
  338. * Offset into the current message block
  339. */
  340. private int currentOffset;
  341. /**
  342. * Buffer for message words for feedback into encrypted block
  343. */
  344. private ulong[] message;
  345. public UBI(SkeinEngine engine, int blockSize)
  346. {
  347. this.engine = engine;
  348. currentBlock = new byte[blockSize];
  349. message = new ulong[currentBlock.Length / 8];
  350. }
  351. public void Reset(UBI ubi)
  352. {
  353. currentBlock = Arrays.Clone(ubi.currentBlock, currentBlock);
  354. currentOffset = ubi.currentOffset;
  355. message = Arrays.Clone(ubi.message, this.message);
  356. tweak.Reset(ubi.tweak);
  357. }
  358. public void Reset(int type)
  359. {
  360. tweak.Reset();
  361. tweak.Type = (uint)type;
  362. currentOffset = 0;
  363. }
  364. public void Update(byte[] value, int offset, int len, ulong[] output)
  365. {
  366. /*
  367. * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
  368. * are subsequent bytes (last block must be processed in doFinal() with final=true set).
  369. */
  370. int copied = 0;
  371. while (len > copied)
  372. {
  373. if (currentOffset == currentBlock.Length)
  374. {
  375. ProcessBlock(output);
  376. tweak.First = false;
  377. currentOffset = 0;
  378. }
  379. int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset);
  380. Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy);
  381. copied += toCopy;
  382. currentOffset += toCopy;
  383. tweak.AdvancePosition(toCopy);
  384. }
  385. }
  386. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  387. public void Update(ReadOnlySpan<byte> input, ulong[] output)
  388. {
  389. /*
  390. * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
  391. * are subsequent bytes (last block must be processed in doFinal() with final=true set).
  392. */
  393. int copied = 0, len = input.Length;
  394. while (len > copied)
  395. {
  396. if (currentOffset == currentBlock.Length)
  397. {
  398. ProcessBlock(output);
  399. tweak.First = false;
  400. currentOffset = 0;
  401. }
  402. int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset);
  403. input.Slice(copied, toCopy).CopyTo(currentBlock.AsSpan(currentOffset));
  404. copied += toCopy;
  405. currentOffset += toCopy;
  406. tweak.AdvancePosition(toCopy);
  407. }
  408. }
  409. #endif
  410. private void ProcessBlock(ulong[] output)
  411. {
  412. engine.threefish.Init(true, engine.chain, tweak.GetWords());
  413. Pack.LE_To_UInt64(currentBlock, 0, message);
  414. engine.threefish.ProcessBlock(message, output);
  415. for (int i = 0; i < output.Length; i++)
  416. {
  417. output[i] ^= message[i];
  418. }
  419. }
  420. public void DoFinal(ulong[] output)
  421. {
  422. // Pad remainder of current block with zeroes
  423. for (int i = currentOffset; i < currentBlock.Length; i++)
  424. {
  425. currentBlock[i] = 0;
  426. }
  427. tweak.Final = true;
  428. ProcessBlock(output);
  429. }
  430. }
  431. /**
  432. * Underlying Threefish tweakable block cipher
  433. */
  434. private readonly ThreefishEngine threefish;
  435. /**
  436. * Size of the digest output, in bytes
  437. */
  438. private readonly int outputSizeBytes;
  439. /**
  440. * The current chaining/state value
  441. */
  442. private ulong[] chain;
  443. /**
  444. * The initial state value
  445. */
  446. private ulong[] initialState;
  447. /**
  448. * The (optional) key parameter
  449. */
  450. private byte[] key;
  451. /**
  452. * Parameters to apply prior to the message
  453. */
  454. private Parameter[] preMessageParameters;
  455. /**
  456. * Parameters to apply after the message, but prior to output
  457. */
  458. private Parameter[] postMessageParameters;
  459. /**
  460. * The current UBI operation
  461. */
  462. private readonly UBI ubi;
  463. /**
  464. * Buffer for single byte update method
  465. */
  466. private readonly byte[] singleByte = new byte[1];
  467. /// <summary>
  468. /// Constructs a Skein digest with an internal state size and output size.
  469. /// </summary>
  470. /// <param name="blockSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or
  471. /// <see cref="SKEIN_1024"/>.</param>
  472. /// <param name="outputSizeBits">the output/digest size to produce in bits, which must be an integral number of
  473. /// bytes.</param>
  474. public SkeinEngine(int blockSizeBits, int outputSizeBits)
  475. {
  476. if (outputSizeBits % 8 != 0)
  477. {
  478. throw new ArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
  479. }
  480. // TODO: Prevent digest sizes > block size?
  481. this.outputSizeBytes = outputSizeBits / 8;
  482. this.threefish = new ThreefishEngine(blockSizeBits);
  483. this.ubi = new UBI(this,threefish.GetBlockSize());
  484. }
  485. /// <summary>
  486. /// Creates a SkeinEngine as an exact copy of an existing instance.
  487. /// </summary>
  488. public SkeinEngine(SkeinEngine engine)
  489. : this(engine.BlockSize * 8, engine.OutputSize * 8)
  490. {
  491. CopyIn(engine);
  492. }
  493. private void CopyIn(SkeinEngine engine)
  494. {
  495. this.ubi.Reset(engine.ubi);
  496. this.chain = Arrays.Clone(engine.chain, this.chain);
  497. this.initialState = Arrays.Clone(engine.initialState, this.initialState);
  498. this.key = Arrays.Clone(engine.key, this.key);
  499. this.preMessageParameters = Clone(engine.preMessageParameters, this.preMessageParameters);
  500. this.postMessageParameters = Clone(engine.postMessageParameters, this.postMessageParameters);
  501. }
  502. private static Parameter[] Clone(Parameter[] data, Parameter[] existing)
  503. {
  504. if (data == null)
  505. {
  506. return null;
  507. }
  508. if ((existing == null) || (existing.Length != data.Length))
  509. {
  510. existing = new Parameter[data.Length];
  511. }
  512. Array.Copy(data, 0, existing, 0, existing.Length);
  513. return existing;
  514. }
  515. public IMemoable Copy()
  516. {
  517. return new SkeinEngine(this);
  518. }
  519. public void Reset(IMemoable other)
  520. {
  521. SkeinEngine s = (SkeinEngine)other;
  522. if ((BlockSize != s.BlockSize) || (outputSizeBytes != s.outputSizeBytes))
  523. {
  524. throw new MemoableResetException("Incompatible parameters in provided SkeinEngine.");
  525. }
  526. CopyIn(s);
  527. }
  528. public int OutputSize
  529. {
  530. get { return outputSizeBytes; }
  531. }
  532. public int BlockSize
  533. {
  534. get { return threefish.GetBlockSize (); }
  535. }
  536. /// <summary>
  537. /// Initialises the Skein engine with the provided parameters. See <see cref="Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> for
  538. /// details on the parameterisation of the Skein hash function.
  539. /// </summary>
  540. /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param>
  541. public void Init(SkeinParameters parameters)
  542. {
  543. this.chain = null;
  544. this.key = null;
  545. this.preMessageParameters = null;
  546. this.postMessageParameters = null;
  547. if (parameters != null)
  548. {
  549. byte[] key = parameters.GetKey();
  550. if (key.Length < 16)
  551. {
  552. throw new ArgumentException("Skein key must be at least 128 bits.");
  553. }
  554. InitParams(parameters.GetParameters());
  555. }
  556. CreateInitialState();
  557. // Initialise message block
  558. UbiInit(PARAM_TYPE_MESSAGE);
  559. }
  560. private void InitParams(IDictionary<int, byte[]> parameters)
  561. {
  562. //IEnumerator keys = parameters.Keys.GetEnumerator();
  563. var pre = new List<Parameter>();
  564. var post = new List<Parameter>();
  565. //while (keys.MoveNext())
  566. foreach (var parameter in parameters)
  567. {
  568. int type = parameter.Key;
  569. byte[] value = parameter.Value;
  570. if (type == PARAM_TYPE_KEY)
  571. {
  572. this.key = value;
  573. }
  574. else if (type < PARAM_TYPE_MESSAGE)
  575. {
  576. pre.Add(new Parameter(type, value));
  577. }
  578. else
  579. {
  580. post.Add(new Parameter(type, value));
  581. }
  582. }
  583. preMessageParameters = new Parameter[pre.Count];
  584. pre.CopyTo(preMessageParameters, 0);
  585. Array.Sort(preMessageParameters);
  586. postMessageParameters = new Parameter[post.Count];
  587. post.CopyTo(postMessageParameters, 0);
  588. Array.Sort(postMessageParameters);
  589. }
  590. /**
  591. * Calculate the initial (pre message block) chaining state.
  592. */
  593. private void CreateInitialState()
  594. {
  595. var precalc = CollectionUtilities.GetValueOrNull(InitialStates, VariantIdentifier(BlockSize, OutputSize));
  596. if ((key == null) && (precalc != null))
  597. {
  598. // Precalculated UBI(CFG)
  599. chain = Arrays.Clone(precalc);
  600. }
  601. else
  602. {
  603. // Blank initial state
  604. chain = new ulong[BlockSize / 8];
  605. // Process key block
  606. if (key != null)
  607. {
  608. UbiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
  609. }
  610. // Process configuration block
  611. UbiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).Bytes);
  612. }
  613. // Process additional pre-message parameters
  614. if (preMessageParameters != null)
  615. {
  616. for (int i = 0; i < preMessageParameters.Length; i++)
  617. {
  618. Parameter param = preMessageParameters[i];
  619. UbiComplete(param.Type, param.Value);
  620. }
  621. }
  622. initialState = Arrays.Clone(chain);
  623. }
  624. /// <summary>
  625. /// Reset the engine to the initial state (with the key and any pre-message parameters , ready to
  626. /// accept message input.
  627. /// </summary>
  628. public void Reset()
  629. {
  630. Array.Copy(initialState, 0, chain, 0, chain.Length);
  631. UbiInit(PARAM_TYPE_MESSAGE);
  632. }
  633. private void UbiComplete(int type, byte[] value)
  634. {
  635. UbiInit(type);
  636. this.ubi.Update(value, 0, value.Length, chain);
  637. UbiFinal();
  638. }
  639. private void UbiInit(int type)
  640. {
  641. this.ubi.Reset(type);
  642. }
  643. private void UbiFinal()
  644. {
  645. ubi.DoFinal(chain);
  646. }
  647. private void CheckInitialised()
  648. {
  649. if (this.ubi == null)
  650. {
  651. throw new ArgumentException("Skein engine is not initialised.");
  652. }
  653. }
  654. public void Update(byte inByte)
  655. {
  656. singleByte[0] = inByte;
  657. BlockUpdate(singleByte, 0, 1);
  658. }
  659. public void BlockUpdate(byte[] inBytes, int inOff, int len)
  660. {
  661. CheckInitialised();
  662. ubi.Update(inBytes, inOff, len, chain);
  663. }
  664. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  665. public void BlockUpdate(ReadOnlySpan<byte> input)
  666. {
  667. CheckInitialised();
  668. ubi.Update(input, chain);
  669. }
  670. #endif
  671. public int DoFinal(byte[] outBytes, int outOff)
  672. {
  673. CheckInitialised();
  674. if (outBytes.Length < (outOff + outputSizeBytes))
  675. {
  676. throw new DataLengthException("Output buffer is too short to hold output");
  677. }
  678. // Finalise message block
  679. UbiFinal();
  680. // Process additional post-message parameters
  681. if (postMessageParameters != null)
  682. {
  683. for (int i = 0; i < postMessageParameters.Length; i++)
  684. {
  685. Parameter param = postMessageParameters[i];
  686. UbiComplete(param.Type, param.Value);
  687. }
  688. }
  689. // Perform the output transform
  690. int blockSize = BlockSize;
  691. int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
  692. for (int i = 0; i < blocksRequired; i++)
  693. {
  694. int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize));
  695. Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite);
  696. }
  697. Reset();
  698. return outputSizeBytes;
  699. }
  700. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  701. public int DoFinal(Span<byte> output)
  702. {
  703. CheckInitialised();
  704. if (output.Length < outputSizeBytes)
  705. throw new DataLengthException("Output span is too short to hold output");
  706. // Finalise message block
  707. UbiFinal();
  708. // Process additional post-message parameters
  709. if (postMessageParameters != null)
  710. {
  711. for (int i = 0; i < postMessageParameters.Length; i++)
  712. {
  713. Parameter param = postMessageParameters[i];
  714. UbiComplete(param.Type, param.Value);
  715. }
  716. }
  717. // Perform the output transform
  718. int blockSize = BlockSize;
  719. int blocksRequired = (outputSizeBytes + blockSize - 1) / blockSize;
  720. for (int i = 0; i < blocksRequired; i++)
  721. {
  722. int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize));
  723. //Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite);
  724. Output((ulong)i, output[(i * blockSize)..], toWrite);
  725. }
  726. Reset();
  727. return outputSizeBytes;
  728. }
  729. #endif
  730. private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes)
  731. {
  732. byte[] currentBytes = new byte[8];
  733. Pack.UInt64_To_LE(outputSequence, currentBytes, 0);
  734. // Output is a sequence of UBI invocations all of which use and preserve the pre-output state
  735. ulong[] outputWords = new ulong[chain.Length];
  736. UbiInit(PARAM_TYPE_OUTPUT);
  737. this.ubi.Update(currentBytes, 0, currentBytes.Length, outputWords);
  738. ubi.DoFinal(outputWords);
  739. int wordsRequired = (outputBytes + 8 - 1) / 8;
  740. for (int i = 0; i < wordsRequired; i++)
  741. {
  742. int toWrite = System.Math.Min(8, outputBytes - (i * 8));
  743. if (toWrite == 8)
  744. {
  745. Pack.UInt64_To_LE(outputWords[i], outBytes, outOff + (i * 8));
  746. }
  747. else
  748. {
  749. Pack.UInt64_To_LE(outputWords[i], currentBytes, 0);
  750. Array.Copy(currentBytes, 0, outBytes, outOff + (i * 8), toWrite);
  751. }
  752. }
  753. }
  754. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  755. private void Output(ulong outputSequence, Span<byte> output, int outputBytes)
  756. {
  757. Span<byte> currentBytes = stackalloc byte[8];
  758. Pack.UInt64_To_LE(outputSequence, currentBytes);
  759. // Output is a sequence of UBI invocations all of which use and preserve the pre-output state
  760. ulong[] outputWords = new ulong[chain.Length];
  761. UbiInit(PARAM_TYPE_OUTPUT);
  762. this.ubi.Update(currentBytes, outputWords);
  763. ubi.DoFinal(outputWords);
  764. int wordsRequired = (outputBytes + 8 - 1) / 8;
  765. for (int i = 0; i < wordsRequired; i++)
  766. {
  767. int toWrite = System.Math.Min(8, outputBytes - (i * 8));
  768. if (toWrite == 8)
  769. {
  770. Pack.UInt64_To_LE(outputWords[i], output[(i * 8)..]);
  771. }
  772. else
  773. {
  774. Pack.UInt64_To_LE(outputWords[i], currentBytes);
  775. currentBytes[..toWrite].CopyTo(output[(i * 8)..]);
  776. }
  777. }
  778. }
  779. #endif
  780. }
  781. }
  782. #pragma warning restore
  783. #endif