diff options
author | Tim Whittington <bc@whittington.net.nz> | 2013-10-11 20:27:51 +1300 |
---|---|---|
committer | Tim Whittington <bc@whittington.net.nz> | 2013-10-20 21:29:17 +1300 |
commit | 201d86c4ab73d81605402d2b0cae15106f9e372d (patch) | |
tree | b6fb1854c0c46a06201df6117abf5fc16256e1f1 /crypto/src | |
parent | Port of Threefish implementation from bc-java. (diff) | |
download | BouncyCastle.NET-ed25519-201d86c4ab73d81605402d2b0cae15106f9e372d.tar.xz |
Port SkeinDigest and SkeinMac from bc-java.
Skein digest and Mac in 256/512/1024 bit state sizes (and arbitrary byte level output size), with unit tests.
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/digests/SkeinDigest.cs | 117 | ||||
-rw-r--r-- | crypto/src/crypto/digests/SkeinEngine.cs | 803 | ||||
-rw-r--r-- | crypto/src/crypto/macs/SkeinMac.cs | 117 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/SkeinParameters.cs | 285 | ||||
-rw-r--r-- | crypto/src/util/Arrays.cs | 40 |
5 files changed, 1362 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs new file mode 100644 index 000000000..b24fb3995 --- /dev/null +++ b/crypto/src/crypto/digests/SkeinDigest.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /// <summary> + /// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, + /// based on the <see cref="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. + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> + public class SkeinDigest + : IDigest//, IMemoable + { + /// <summary> + /// 256 bit block size - Skein-256 + /// </summary> + public const int SKEIN_256 = SkeinEngine.SKEIN_256; + /// <summary> + /// 512 bit block size - Skein-512 + /// </summary> + public const int SKEIN_512 = SkeinEngine.SKEIN_512; + /// <summary> + /// 1024 bit block size - Skein-1024 + /// </summary> + public const int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private readonly SkeinEngine engine; + + /// <summary> + /// Constructs a Skein digest with an internal state size and output size. + /// </summary> + /// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or + /// <see cref="SKEIN_1024"/>.</param> + /// <param name="digestSizeBits">the output/digest size to produce in bits, which must be an integral number of + /// bytes.</param> + public SkeinDigest(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + Init(null); + } + + public SkeinDigest(SkeinDigest digest) + { + this.engine = new SkeinEngine(digest.engine); + } + + // public void Reset(IMemoable other) + // { + // SkeinDigest d = (SkeinDigest)other; + // engine.reset(d.engine); + // } + // + // public IMemoable copy() + // { + // return new SkeinDigest(this); + // } + + public String AlgorithmName + { + get { return "Skein-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); } + } + + public int GetDigestSize() + { + return engine.OutputSize; + } + + public int GetByteLength() + { + return engine.BlockSize; + } + + /// <summary> + /// Optionally initialises the Skein digest with the provided parameters. + /// </summary> + /// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function. + /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param> + public void Init(SkeinParameters parameters) + { + engine.Init(parameters); + } + + public void Reset() + { + engine.Reset(); + } + + public void Update(byte inByte) + { + engine.Update(inByte); + } + + public void BlockUpdate(byte[] inBytes, int inOff, int len) + { + engine.Update(inBytes, inOff, len); + } + + public int DoFinal(byte[] outBytes, int outOff) + { + return engine.DoFinal(outBytes, outOff); + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs new file mode 100644 index 000000000..94529bcf6 --- /dev/null +++ b/crypto/src/crypto/digests/SkeinEngine.cs @@ -0,0 +1,803 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace 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="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="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> and <see cref="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="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="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 = 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="stateSizeBits">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 ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes)) + // { + // throw new IllegalArgumentException("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="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 = Platform.CreateArrayList(); + IList post = 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 of " + outputSizeBytes + " bytes"); + } + + // 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); + } + } + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/macs/SkeinMac.cs b/crypto/src/crypto/macs/SkeinMac.cs new file mode 100644 index 000000000..1d61a41ca --- /dev/null +++ b/crypto/src/crypto/macs/SkeinMac.cs @@ -0,0 +1,117 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + + /// <summary> + /// Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes, + /// based on the <see cref="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. + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> + public class SkeinMac + : IMac + { + /// <summary> + /// 256 bit block size - Skein-256 + /// </summary> + public const int SKEIN_256 = SkeinEngine.SKEIN_256; + /// <summary> + /// 512 bit block size - Skein-512 + /// </summary> + public const int SKEIN_512 = SkeinEngine.SKEIN_512; + /// <summary> + /// 1024 bit block size - Skein-1024 + /// </summary> + public const int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private readonly SkeinEngine engine; + + /// <summary> + /// Constructs a Skein MAC with an internal state size and output size. + /// </summary> + /// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or + /// <see cref="SKEIN_1024"/>.</param> + /// <param name="digestSizeBits">the output/MAC size to produce in bits, which must be an integral number of + /// bytes.</param> + public SkeinMac(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + } + + public SkeinMac(SkeinMac mac) + { + this.engine = new SkeinEngine(mac.engine); + } + + public string AlgorithmName + { + get { return "Skein-MAC-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); } + } + + /// <summary> + /// Optionally initialises the Skein digest with the provided parameters. + /// </summary> + /// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function. + /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param> + public void Init(ICipherParameters parameters) + { + SkeinParameters skeinParameters; + if (parameters is SkeinParameters) + { + skeinParameters = (SkeinParameters)parameters; + } + else if (parameters is KeyParameter) + { + skeinParameters = new SkeinParameters.Builder().SetKey(((KeyParameter)parameters).GetKey()).Build(); + } + else + { + throw new ArgumentException("Invalid parameter passed to Skein MAC init - " + + parameters.GetType().Name); + } + if (skeinParameters.GetKey() == null) + { + throw new ArgumentException("Skein MAC requires a key parameter."); + } + engine.Init(skeinParameters); + } + + public int GetMacSize() + { + return engine.OutputSize; + } + + public void Reset() + { + engine.Reset(); + } + + public void Update(byte inByte) + { + engine.Update(inByte); + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + engine.Update(input, inOff, len); + } + + public int DoFinal(byte[] output, int outOff) + { + return engine.DoFinal(output, outOff); + } + + } +} diff --git a/crypto/src/crypto/parameters/SkeinParameters.cs b/crypto/src/crypto/parameters/SkeinParameters.cs new file mode 100644 index 000000000..bbd25e0e0 --- /dev/null +++ b/crypto/src/crypto/parameters/SkeinParameters.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// <summary> + /// Parameters for the Skein hash function - a series of byte[] strings identified by integer tags. + /// </summary> + /// <remarks> + /// Parameterised Skein can be used for: + /// <ul> + /// <li>MAC generation, by providing a <see cref="SkeinParameters.Builder.SetKey(byte[])">key</see>.</li> + /// <li>Randomised hashing, by providing a <see cref="SkeinParameters.Builder.SetNoce(byte[])">nonce</see>.</li> + /// <li>A hash function for digital signatures, associating a + /// <see cref="SkeinParameters.Builder.SetPublicKey(byte[])">public key</see> with the message digest.</li> + /// <li>A key derivation function, by providing a + /// <see cref="SkeinParameters.Builder.SetKeyIdentifier(byte[])">key identifier</see>.</li> + /// <li>Personalised hashing, by providing a + /// <see cref="SkeinParameters.Builder.SetPersonalisation(DateTime,string,string)">recommended format</see> or + /// <see cref="SkeinParameters.Builder.SetPersonalisation(byte[])">arbitrary</see> personalisation string.</li> + /// </ul> + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Macs.SkeinMac"/> + public class SkeinParameters + : ICipherParameters + { + /// <summary> + /// The parameter type for a secret key, supporting MAC or KDF functions: 0 + /// </summary> + public const int PARAM_TYPE_KEY = 0; + + /// <summary> + /// The parameter type for the Skein configuration block: 4 + /// </summary> + public const int PARAM_TYPE_CONFIG = 4; + + /// <summary> + /// The parameter type for a personalisation string: 8 + /// </summary> + public const int PARAM_TYPE_PERSONALISATION = 8; + + /// <summary> + /// The parameter type for a public key: 12 + /// </summary> + public const int PARAM_TYPE_PUBLIC_KEY = 12; + + /// <summary> + /// The parameter type for a key identifier string: 16 + /// </summary> + public const int PARAM_TYPE_KEY_IDENTIFIER = 16; + + /// <summary> + /// The parameter type for a nonce: 20 + /// </summary> + public const int PARAM_TYPE_NONCE = 20; + + /// <summary> + /// The parameter type for the message: 48 + /// </summary> + public const int PARAM_TYPE_MESSAGE = 48; + + /// <summary> + /// The parameter type for the output transformation: 63 + /// </summary> + public const int PARAM_TYPE_OUTPUT = 63; + + private IDictionary parameters; + + public SkeinParameters() + : this(Platform.CreateHashtable()) + + { + } + + private SkeinParameters(IDictionary parameters) + { + this.parameters = parameters; + } + + /// <summary> + /// Obtains a map of type (int) to value (byte[]) for the parameters tracked in this object. + /// </summary> + public IDictionary GetParameters() + { + return parameters; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_KEY">key parameter</see>, or <code>null</code> if not + /// set. + /// </summary> + /// <returns>The key.</returns> + public byte[] GetKey() + { + return (byte[])parameters[PARAM_TYPE_KEY]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_PERSONALISATION">personalisation parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetPersonalisation() + { + return (byte[])parameters[PARAM_TYPE_PERSONALISATION]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_PUBLIC_KEY">public key parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetPublicKey() + { + return (byte[])parameters[PARAM_TYPE_PUBLIC_KEY]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_KEY_IDENTIFIER key identifier parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetKeyIdentifier() + { + return (byte[])parameters[PARAM_TYPE_KEY_IDENTIFIER]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_NONCE nonce parameter</see>, or <code>null</code> if + /// not set. + /// </summary> + public byte[] GetNonce() + { + return (byte[])parameters[PARAM_TYPE_NONCE]; + } + + /// <summary> + /// A builder for <see cref="SkeinParameters"/>. + /// </summary> + public class Builder + { + private IDictionary parameters = Platform.CreateHashtable(); + + public Builder() + { + } + + public Builder(IDictionary paramsMap) + { + IEnumerator keys = paramsMap.Keys.GetEnumerator(); + while (keys.MoveNext()) + { + int key = (int)keys.Current; + parameters.Add(key, paramsMap[key]); + } + } + + public Builder(SkeinParameters parameters) + { + IEnumerator keys = parameters.parameters.Keys.GetEnumerator(); + while (keys.MoveNext()) + { + int key = (int)keys.Current; + this.parameters.Add(key, parameters.parameters[key]); + } + } + + /// <summary> + /// Sets a parameters to apply to the Skein hash function. + /// </summary> + /// <remarks> + /// Parameter types must be in the range 0,5..62, and cannot use the value 48 + /// (reserved for message body). + /// <p/> + /// Parameters with type < 48 are processed before + /// the message content, parameters with type > 48 + /// are processed after the message and prior to output. + /// </remarks> + /// <param name="type">the type of the parameter, in the range 5..62.</param> + /// <param name="value">the byte sequence of the parameter.</param> + public Builder Set(int type, byte[] value) + { + if (value == null) + { + throw new ArgumentException("Parameter value must not be null."); + } + if ((type != PARAM_TYPE_KEY) + && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE)) + { + throw new ArgumentException("Parameter types must be in the range 0,5..47,49..62."); + } + if (type == PARAM_TYPE_CONFIG) + { + throw new ArgumentException("Parameter type " + PARAM_TYPE_CONFIG + + " is reserved for internal use."); + } + this.parameters.Add(type, value); + return this; + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY"/> parameter. + /// </summary> + public Builder SetKey(byte[] key) + { + return Set(PARAM_TYPE_KEY, key); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_PERSONALISATION"/> parameter. + /// </summary> + public Builder SetPersonalisation(byte[] personalisation) + { + return Set(PARAM_TYPE_PERSONALISATION, personalisation); + } + + /// <summary> + /// Implements the recommended personalisation format for Skein defined in Section 4.11 of + /// the Skein 1.3 specification. + /// </summary> + /// <remarks> + /// The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte + /// sequence using UTF-8 encoding. + /// </remarks> + /// <param name="date">the date the personalised application of the Skein was defined.</param> + /// <param name="emailAddress">the email address of the creation of the personalised application.</param> + /// <param name="distinguisher">an arbitrary personalisation string distinguishing the application.</param> + public Builder SetPersonalisation(DateTime date, string emailAddress, string distinguisher) + { + try + { + MemoryStream bout = new MemoryStream(); + StreamWriter outBytes = new StreamWriter(bout, System.Text.Encoding.UTF8); + outBytes.Write(date.ToString("YYYYMMDD")); + outBytes.Write(" "); + outBytes.Write(emailAddress); + outBytes.Write(" "); + outBytes.Write(distinguisher); + outBytes.Close(); + return Set(PARAM_TYPE_PERSONALISATION, bout.ToArray()); + } + catch (IOException e) + { + throw new InvalidOperationException("Byte I/O failed.", e); + } + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter. + /// </summary> + public Builder SetPublicKey(byte[] publicKey) + { + return Set(PARAM_TYPE_PUBLIC_KEY, publicKey); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter. + /// </summary> + public Builder SetKeyIdentifier(byte[] keyIdentifier) + { + return Set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_NONCE"/> parameter. + /// </summary> + public Builder SetNonce(byte[] nonce) + { + return Set(PARAM_TYPE_NONCE, nonce); + } + + /// <summary> + /// Constructs a new <see cref="SkeinParameters"/> instance with the parameters provided to this + /// builder. + /// </summary> + public SkeinParameters Build() + { + return new SkeinParameters(parameters); + } + } + } +} \ No newline at end of file diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 3c083034e..7d211e5dc 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -206,12 +206,52 @@ namespace Org.BouncyCastle.Utilities return data == null ? null : (byte[]) data.Clone(); } + public static byte[] Clone( + byte[] data, + byte[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.Length != data.Length)) + { + return Clone(data); + } + Array.Copy(data, 0, existing, 0, existing.Length); + return existing; + } + public static int[] Clone( int[] data) { return data == null ? null : (int[]) data.Clone(); } + [CLSCompliantAttribute(false)] + public static ulong[] Clone( + ulong[] data) + { + return data == null ? null : (ulong[]) data.Clone(); + } + + [CLSCompliantAttribute(false)] + public static ulong[] Clone( + ulong[] data, + ulong[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.Length != data.Length)) + { + return Clone(data); + } + Array.Copy(data, 0, existing, 0, existing.Length); + return existing; + } + public static void Fill( byte[] buf, byte b) |