123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- #pragma warning disable
- using System;
- using System.Collections;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
- namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests
- {
- /// <summary>
- /// Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
- /// sizes, based on the <see cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
- /// </summary>
- /// <remarks>
- /// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
- /// competition in October 2010.
- /// <p/>
- /// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
- /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
- /// <p/>
- /// 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
- /// parameter based configuration system that allows Skein to be adapted to multiple applications. <br/>
- /// Initialising the engine with <see cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> allows standard and arbitrary parameters to
- /// be applied during the Skein hash function.
- /// <p/>
- /// Implemented:
- /// <ul>
- /// <li>256, 512 and 1024 bit internal states.</li>
- /// <li>Full 96 bit input length.</li>
- /// <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
- /// parameters.</li>
- /// <li>Arbitrary output size in 1 byte intervals.</li>
- /// </ul>
- /// <p/>
- /// Not implemented:
- /// <ul>
- /// <li>Sub-byte length input (bit padding).</li>
- /// <li>Tree hashing.</li>
- /// </ul>
- /// </remarks>
- /// <seealso cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/>
- public class SkeinEngine
- : IMemoable
- {
- /// <summary>
- /// 256 bit block size - Skein-256
- /// </summary>
- public const int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
- /// <summary>
- /// 512 bit block size - Skein-512
- /// </summary>
- public const int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
- /// <summary>
- /// 1024 bit block size - Skein-1024
- /// </summary>
- public const int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
- // Minimal at present, but more complex when tree hashing is implemented
- private class Configuration
- {
- private byte[] bytes = new byte[32];
- public Configuration(long outputSizeBits)
- {
- // 0..3 = ASCII SHA3
- bytes[0] = (byte)'S';
- bytes[1] = (byte)'H';
- bytes[2] = (byte)'A';
- bytes[3] = (byte)'3';
- // 4..5 = version number in LSB order
- bytes[4] = 1;
- bytes[5] = 0;
- // 8..15 = output length
- ThreefishEngine.WordToBytes((ulong)outputSizeBits, bytes, 8);
- }
- public byte[] Bytes
- {
- get { return bytes; }
- }
- }
- public class Parameter
- {
- private int type;
- private byte[] value;
- public Parameter(int type, byte[] value)
- {
- this.type = type;
- this.value = value;
- }
- public int Type
- {
- get { return type; }
- }
- public byte[] Value
- {
- get { return value; }
- }
- }
- /**
- * The parameter type for the Skein key.
- */
- private const int PARAM_TYPE_KEY = 0;
- /**
- * The parameter type for the Skein configuration block.
- */
- private const int PARAM_TYPE_CONFIG = 4;
- /**
- * The parameter type for the message.
- */
- private const int PARAM_TYPE_MESSAGE = 48;
- /**
- * The parameter type for the output transformation.
- */
- private const int PARAM_TYPE_OUTPUT = 63;
- /**
- * Precalculated UBI(CFG) states for common state/output combinations without key or other
- * pre-message params.
- */
- private static readonly IDictionary INITIAL_STATES = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable();
- static SkeinEngine()
- {
- // From Appendix C of the Skein 1.3 NIST submission
- InitialState(SKEIN_256, 128, new ulong[]{
- 0xe1111906964d7260UL,
- 0x883daaa77c8d811cUL,
- 0x10080df491960f7aUL,
- 0xccf7dde5b45bc1c2UL});
- InitialState(SKEIN_256, 160, new ulong[]{
- 0x1420231472825e98UL,
- 0x2ac4e9a25a77e590UL,
- 0xd47a58568838d63eUL,
- 0x2dd2e4968586ab7dUL});
- InitialState(SKEIN_256, 224, new ulong[]{
- 0xc6098a8c9ae5ea0bUL,
- 0x876d568608c5191cUL,
- 0x99cb88d7d7f53884UL,
- 0x384bddb1aeddb5deUL});
- InitialState(SKEIN_256, 256, new ulong[]{
- 0xfc9da860d048b449UL,
- 0x2fca66479fa7d833UL,
- 0xb33bc3896656840fUL,
- 0x6a54e920fde8da69UL});
- InitialState(SKEIN_512, 128, new ulong[]{
- 0xa8bc7bf36fbf9f52UL,
- 0x1e9872cebd1af0aaUL,
- 0x309b1790b32190d3UL,
- 0xbcfbb8543f94805cUL,
- 0x0da61bcd6e31b11bUL,
- 0x1a18ebead46a32e3UL,
- 0xa2cc5b18ce84aa82UL,
- 0x6982ab289d46982dUL});
- InitialState(SKEIN_512, 160, new ulong[]{
- 0x28b81a2ae013bd91UL,
- 0xc2f11668b5bdf78fUL,
- 0x1760d8f3f6a56f12UL,
- 0x4fb747588239904fUL,
- 0x21ede07f7eaf5056UL,
- 0xd908922e63ed70b8UL,
- 0xb8ec76ffeccb52faUL,
- 0x01a47bb8a3f27a6eUL});
- InitialState(SKEIN_512, 224, new ulong[]{
- 0xccd0616248677224UL,
- 0xcba65cf3a92339efUL,
- 0x8ccd69d652ff4b64UL,
- 0x398aed7b3ab890b4UL,
- 0x0f59d1b1457d2bd0UL,
- 0x6776fe6575d4eb3dUL,
- 0x99fbc70e997413e9UL,
- 0x9e2cfccfe1c41ef7UL});
- InitialState(SKEIN_512, 384, new ulong[]{
- 0xa3f6c6bf3a75ef5fUL,
- 0xb0fef9ccfd84faa4UL,
- 0x9d77dd663d770cfeUL,
- 0xd798cbf3b468fddaUL,
- 0x1bc4a6668a0e4465UL,
- 0x7ed7d434e5807407UL,
- 0x548fc1acd4ec44d6UL,
- 0x266e17546aa18ff8UL});
- InitialState(SKEIN_512, 512, new ulong[]{
- 0x4903adff749c51ceUL,
- 0x0d95de399746df03UL,
- 0x8fd1934127c79bceUL,
- 0x9a255629ff352cb1UL,
- 0x5db62599df6ca7b0UL,
- 0xeabe394ca9d5c3f4UL,
- 0x991112c71a75b523UL,
- 0xae18a40b660fcc33UL});
- }
- private static void InitialState(int blockSize, int outputSize, ulong[] state)
- {
- INITIAL_STATES.Add(VariantIdentifier(blockSize / 8, outputSize / 8), state);
- }
- private static int VariantIdentifier(int blockSizeBytes, int outputSizeBytes)
- {
- return (outputSizeBytes << 16) | blockSizeBytes;
- }
- private class UbiTweak
- {
- /**
- * Point at which position might overflow long, so switch to add with carry logic
- */
- private const ulong LOW_RANGE = UInt64.MaxValue - UInt32.MaxValue;
- /**
- * Bit 127 = final
- */
- private const ulong T1_FINAL = 1UL << 63;
- /**
- * Bit 126 = first
- */
- private const ulong T1_FIRST = 1UL << 62;
- /**
- * UBI uses a 128 bit tweak
- */
- private ulong[] tweak = new ulong[2];
- /**
- * Whether 64 bit position exceeded
- */
- private bool extendedPosition;
- public UbiTweak()
- {
- Reset();
- }
- public void Reset(UbiTweak tweak)
- {
- this.tweak = Arrays.Clone(tweak.tweak, this.tweak);
- this.extendedPosition = tweak.extendedPosition;
- }
- public void Reset()
- {
- tweak[0] = 0;
- tweak[1] = 0;
- extendedPosition = false;
- First = true;
- }
- public uint Type
- {
- get
- {
- return (uint)((tweak[1] >> 56) & 0x3FUL);
- }
- set
- {
- // Bits 120..125 = type
- tweak[1] = (tweak[1] & 0xFFFFFFC000000000UL) | ((value & 0x3FUL) << 56);
- }
- }
- public bool First
- {
- get
- {
- return ((tweak[1] & T1_FIRST) != 0);
- }
- set
- {
- if (value)
- {
- tweak[1] |= T1_FIRST;
- }
- else
- {
- tweak[1] &= ~T1_FIRST;
- }
- }
- }
- public bool Final
- {
- get
- {
- return ((tweak[1] & T1_FINAL) != 0);
- }
- set
- {
- if (value)
- {
- tweak[1] |= T1_FINAL;
- }
- else
- {
- tweak[1] &= ~T1_FINAL;
- }
- }
- }
- /**
- * Advances the position in the tweak by the specified value.
- */
- public void AdvancePosition(int advance)
- {
- // Bits 0..95 = position
- if (extendedPosition)
- {
- ulong[] parts = new ulong[3];
- parts[0] = tweak[0] & 0xFFFFFFFFUL;
- parts[1] = (tweak[0] >> 32) & 0xFFFFFFFFUL;
- parts[2] = tweak[1] & 0xFFFFFFFFUL;
- ulong carry = (ulong)advance;
- for (int i = 0; i < parts.Length; i++)
- {
- carry += parts[i];
- parts[i] = carry;
- carry >>= 32;
- }
- tweak[0] = ((parts[1] & 0xFFFFFFFFUL) << 32) | (parts[0] & 0xFFFFFFFFUL);
- tweak[1] = (tweak[1] & 0xFFFFFFFF00000000UL) | (parts[2] & 0xFFFFFFFFUL);
- }
- else
- {
- ulong position = tweak[0];
- position += (uint)advance;
- tweak[0] = position;
- if (position > LOW_RANGE)
- {
- extendedPosition = true;
- }
- }
- }
- public ulong[] GetWords()
- {
- return tweak;
- }
- public override string ToString()
- {
- return Type + " first: " + First + ", final: " + Final;
- }
- }
- /**
- * The Unique Block Iteration chaining mode.
- */
- // TODO: This might be better as methods...
- private class UBI
- {
- private readonly UbiTweak tweak = new UbiTweak();
- private readonly SkeinEngine engine;
- /**
- * Buffer for the current block of message data
- */
- private byte[] currentBlock;
- /**
- * Offset into the current message block
- */
- private int currentOffset;
- /**
- * Buffer for message words for feedback into encrypted block
- */
- private ulong[] message;
- public UBI(SkeinEngine engine, int blockSize)
- {
- this.engine = engine;
- currentBlock = new byte[blockSize];
- message = new ulong[currentBlock.Length / 8];
- }
- public void Reset(UBI ubi)
- {
- currentBlock = Arrays.Clone(ubi.currentBlock, currentBlock);
- currentOffset = ubi.currentOffset;
- message = Arrays.Clone(ubi.message, this.message);
- tweak.Reset(ubi.tweak);
- }
- public void Reset(int type)
- {
- tweak.Reset();
- tweak.Type = (uint)type;
- currentOffset = 0;
- }
- public void Update(byte[] value, int offset, int len, ulong[] output)
- {
- /*
- * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
- * are subsequent bytes (last block must be processed in doFinal() with final=true set).
- */
- int copied = 0;
- while (len > copied)
- {
- if (currentOffset == currentBlock.Length)
- {
- ProcessBlock(output);
- tweak.First = false;
- currentOffset = 0;
- }
- int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset);
- Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy);
- copied += toCopy;
- currentOffset += toCopy;
- tweak.AdvancePosition(toCopy);
- }
- }
- private void ProcessBlock(ulong[] output)
- {
- engine.threefish.Init(true, engine.chain, tweak.GetWords());
- for (int i = 0; i < message.Length; i++)
- {
- message[i] = ThreefishEngine.BytesToWord(currentBlock, i * 8);
- }
- engine.threefish.ProcessBlock(message, output);
- for (int i = 0; i < output.Length; i++)
- {
- output[i] ^= message[i];
- }
- }
- public void DoFinal(ulong[] output)
- {
- // Pad remainder of current block with zeroes
- for (int i = currentOffset; i < currentBlock.Length; i++)
- {
- currentBlock[i] = 0;
- }
- tweak.Final = true;
- ProcessBlock(output);
- }
- }
- /**
- * Underlying Threefish tweakable block cipher
- */
- private readonly ThreefishEngine threefish;
- /**
- * Size of the digest output, in bytes
- */
- private readonly int outputSizeBytes;
- /**
- * The current chaining/state value
- */
- private ulong[] chain;
- /**
- * The initial state value
- */
- private ulong[] initialState;
- /**
- * The (optional) key parameter
- */
- private byte[] key;
- /**
- * Parameters to apply prior to the message
- */
- private Parameter[] preMessageParameters;
- /**
- * Parameters to apply after the message, but prior to output
- */
- private Parameter[] postMessageParameters;
- /**
- * The current UBI operation
- */
- private readonly UBI ubi;
- /**
- * Buffer for single byte update method
- */
- private readonly byte[] singleByte = new byte[1];
- /// <summary>
- /// Constructs a Skein digest with an internal state size and output size.
- /// </summary>
- /// <param name="blockSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or
- /// <see cref="SKEIN_1024"/>.</param>
- /// <param name="outputSizeBits">the output/digest size to produce in bits, which must be an integral number of
- /// bytes.</param>
- public SkeinEngine(int blockSizeBits, int outputSizeBits)
- {
- if (outputSizeBits % 8 != 0)
- {
- throw new ArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
- }
- // TODO: Prevent digest sizes > block size?
- this.outputSizeBytes = outputSizeBits / 8;
- this.threefish = new ThreefishEngine(blockSizeBits);
- this.ubi = new UBI(this,threefish.GetBlockSize());
- }
- /// <summary>
- /// Creates a SkeinEngine as an exact copy of an existing instance.
- /// </summary>
- public SkeinEngine(SkeinEngine engine)
- : this(engine.BlockSize * 8, engine.OutputSize * 8)
- {
- CopyIn(engine);
- }
- private void CopyIn(SkeinEngine engine)
- {
- this.ubi.Reset(engine.ubi);
- this.chain = Arrays.Clone(engine.chain, this.chain);
- this.initialState = Arrays.Clone(engine.initialState, this.initialState);
- this.key = Arrays.Clone(engine.key, this.key);
- this.preMessageParameters = Clone(engine.preMessageParameters, this.preMessageParameters);
- this.postMessageParameters = Clone(engine.postMessageParameters, this.postMessageParameters);
- }
- private static Parameter[] Clone(Parameter[] data, Parameter[] existing)
- {
- if (data == null)
- {
- return null;
- }
- if ((existing == null) || (existing.Length != data.Length))
- {
- existing = new Parameter[data.Length];
- }
- Array.Copy(data, 0, existing, 0, existing.Length);
- return existing;
- }
- public IMemoable Copy()
- {
- return new SkeinEngine(this);
- }
- public void Reset(IMemoable other)
- {
- SkeinEngine s = (SkeinEngine)other;
- if ((BlockSize != s.BlockSize) || (outputSizeBytes != s.outputSizeBytes))
- {
- throw new MemoableResetException("Incompatible parameters in provided SkeinEngine.");
- }
- CopyIn(s);
- }
- public int OutputSize
- {
- get { return outputSizeBytes; }
- }
- public int BlockSize
- {
- get { return threefish.GetBlockSize (); }
- }
- /// <summary>
- /// Initialises the Skein engine with the provided parameters. See <see cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> for
- /// details on the parameterisation of the Skein hash function.
- /// </summary>
- /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param>
- public void Init(SkeinParameters parameters)
- {
- this.chain = null;
- this.key = null;
- this.preMessageParameters = null;
- this.postMessageParameters = null;
- if (parameters != null)
- {
- byte[] key = parameters.GetKey();
- if (key.Length < 16)
- {
- throw new ArgumentException("Skein key must be at least 128 bits.");
- }
- InitParams(parameters.GetParameters());
- }
- CreateInitialState();
- // Initialise message block
- UbiInit(PARAM_TYPE_MESSAGE);
- }
- private void InitParams(IDictionary parameters)
- {
- IEnumerator keys = parameters.Keys.GetEnumerator();
- IList pre = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
- IList post = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
- while (keys.MoveNext())
- {
- int type = (int)keys.Current;
- byte[] value = (byte[])parameters[type];
- if (type == PARAM_TYPE_KEY)
- {
- this.key = value;
- }
- else if (type < PARAM_TYPE_MESSAGE)
- {
- pre.Add(new Parameter(type, value));
- }
- else
- {
- post.Add(new Parameter(type, value));
- }
- }
- preMessageParameters = new Parameter[pre.Count];
- pre.CopyTo(preMessageParameters, 0);
- Array.Sort(preMessageParameters);
- postMessageParameters = new Parameter[post.Count];
- post.CopyTo(postMessageParameters, 0);
- Array.Sort(postMessageParameters);
- }
- /**
- * Calculate the initial (pre message block) chaining state.
- */
- private void CreateInitialState()
- {
- ulong[] precalc = (ulong[])INITIAL_STATES[VariantIdentifier(BlockSize, OutputSize)];
- if ((key == null) && (precalc != null))
- {
- // Precalculated UBI(CFG)
- chain = Arrays.Clone(precalc);
- }
- else
- {
- // Blank initial state
- chain = new ulong[BlockSize / 8];
- // Process key block
- if (key != null)
- {
- UbiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
- }
- // Process configuration block
- UbiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).Bytes);
- }
- // Process additional pre-message parameters
- if (preMessageParameters != null)
- {
- for (int i = 0; i < preMessageParameters.Length; i++)
- {
- Parameter param = preMessageParameters[i];
- UbiComplete(param.Type, param.Value);
- }
- }
- initialState = Arrays.Clone(chain);
- }
- /// <summary>
- /// Reset the engine to the initial state (with the key and any pre-message parameters , ready to
- /// accept message input.
- /// </summary>
- public void Reset()
- {
- Array.Copy(initialState, 0, chain, 0, chain.Length);
- UbiInit(PARAM_TYPE_MESSAGE);
- }
- private void UbiComplete(int type, byte[] value)
- {
- UbiInit(type);
- this.ubi.Update(value, 0, value.Length, chain);
- UbiFinal();
- }
- private void UbiInit(int type)
- {
- this.ubi.Reset(type);
- }
- private void UbiFinal()
- {
- ubi.DoFinal(chain);
- }
- private void CheckInitialised()
- {
- if (this.ubi == null)
- {
- throw new ArgumentException("Skein engine is not initialised.");
- }
- }
- public void Update(byte inByte)
- {
- singleByte[0] = inByte;
- Update(singleByte, 0, 1);
- }
- public void Update(byte[] inBytes, int inOff, int len)
- {
- CheckInitialised();
- ubi.Update(inBytes, inOff, len, chain);
- }
- public int DoFinal(byte[] outBytes, int outOff)
- {
- CheckInitialised();
- if (outBytes.Length < (outOff + outputSizeBytes))
- {
- throw new DataLengthException("Output buffer is too short to hold output");
- }
- // Finalise message block
- UbiFinal();
- // Process additional post-message parameters
- if (postMessageParameters != null)
- {
- for (int i = 0; i < postMessageParameters.Length; i++)
- {
- Parameter param = postMessageParameters[i];
- UbiComplete(param.Type, param.Value);
- }
- }
- // Perform the output transform
- int blockSize = BlockSize;
- int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
- for (int i = 0; i < blocksRequired; i++)
- {
- int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize));
- Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite);
- }
- Reset();
- return outputSizeBytes;
- }
- private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes)
- {
- byte[] currentBytes = new byte[8];
- ThreefishEngine.WordToBytes(outputSequence, currentBytes, 0);
- // Output is a sequence of UBI invocations all of which use and preserve the pre-output
- // state
- ulong[] outputWords = new ulong[chain.Length];
- UbiInit(PARAM_TYPE_OUTPUT);
- this.ubi.Update(currentBytes, 0, currentBytes.Length, outputWords);
- ubi.DoFinal(outputWords);
- int wordsRequired = ((outputBytes + 8 - 1) / 8);
- for (int i = 0; i < wordsRequired; i++)
- {
- int toWrite = System.Math.Min(8, outputBytes - (i * 8));
- if (toWrite == 8)
- {
- ThreefishEngine.WordToBytes(outputWords[i], outBytes, outOff + (i * 8));
- }
- else
- {
- ThreefishEngine.WordToBytes(outputWords[i], currentBytes, 0);
- Array.Copy(currentBytes, 0, outBytes, outOff + (i * 8), toWrite);
- }
- }
- }
- }
- }
- #pragma warning restore
- #endif
|