SkeinEngine.cs 26 KB

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