diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-01-21 16:46:07 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-01-21 16:46:07 +0700 |
commit | 95c58fd065df852598412fa8b7d06c3cee72c29c (patch) | |
tree | 01d8a520a72b008656881a37ec88d0ab2c98c361 /crypto/src | |
parent | Merge branch 'pkix-validator-throw' of git://github.com/jstedfast/bc-csharp i... (diff) | |
parent | Port HMac optimisation using Memoable digests from bc-java. (diff) | |
download | BouncyCastle.NET-ed25519-95c58fd065df852598412fa8b7d06c3cee72c29c.tar.xz |
Merge branch 'feature/threefish-skein-memoable-sm3' of git://github.com/timw/bc-csharp into timw-feature/threefish-skein-memoable-sm3
Conflicts: crypto/crypto.mdp crypto/src/util/Arrays.cs
Diffstat (limited to 'crypto/src')
30 files changed, 3708 insertions, 75 deletions
diff --git a/crypto/src/crypto/digests/GOST3411Digest.cs b/crypto/src/crypto/digests/GOST3411Digest.cs index 9f0bec9e6..218adf68c 100644 --- a/crypto/src/crypto/digests/GOST3411Digest.cs +++ b/crypto/src/crypto/digests/GOST3411Digest.cs @@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * implementation of GOST R 34.11-94 */ public class Gost3411Digest - : IDigest + : IDigest, IMemoable { private const int DIGEST_LENGTH = 32; @@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests private ulong byteCount; private readonly IBlockCipher cipher = new Gost28147Engine(); - private readonly byte[] sBox; + private byte[] sBox; private static byte[][] MakeC() { @@ -65,22 +65,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ public Gost3411Digest(Gost3411Digest t) { - this.sBox = t.sBox; - cipher.Init(true, new ParametersWithSBox(null, sBox)); - - Reset(); - - Array.Copy(t.H, 0, this.H, 0, t.H.Length); - Array.Copy(t.L, 0, this.L, 0, t.L.Length); - Array.Copy(t.M, 0, this.M, 0, t.M.Length); - Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length); - Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length); - Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length); - Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length); - Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length); - - this.xBufOff = t.xBufOff; - this.byteCount = t.byteCount; + Reset(t); } public string AlgorithmName @@ -339,5 +324,33 @@ namespace Org.BouncyCastle.Crypto.Digests { return 32; } + + public IMemoable Copy() + { + return new Gost3411Digest(this); + } + + public void Reset(IMemoable other) + { + Gost3411Digest t = (Gost3411Digest)other; + + this.sBox = t.sBox; + cipher.Init(true, new ParametersWithSBox(null, sBox)); + + Reset(); + + Array.Copy(t.H, 0, this.H, 0, t.H.Length); + Array.Copy(t.L, 0, this.L, 0, t.L.Length); + Array.Copy(t.M, 0, this.M, 0, t.M.Length); + Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length); + Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length); + Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length); + Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length); + Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length); + + this.xBufOff = t.xBufOff; + this.byteCount = t.byteCount; + } } + } diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs index 77c17ed58..54a09ae05 100644 --- a/crypto/src/crypto/digests/GeneralDigest.cs +++ b/crypto/src/crypto/digests/GeneralDigest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -7,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Digests * "Handbook of Applied Cryptography", pages 344 - 347. */ public abstract class GeneralDigest - : IDigest + : IDigest, IMemoable { private const int BYTE_LENGTH = 64; @@ -22,8 +24,13 @@ namespace Org.BouncyCastle.Crypto.Digests } internal GeneralDigest(GeneralDigest t) - { - xBuf = new byte[t.xBuf.Length]; + { + xBuf = new byte[t.xBuf.Length]; + CopyIn(t); + } + + protected void CopyIn(GeneralDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -114,5 +121,7 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract string AlgorithmName { get; } public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs index 702753b2b..9ee9bcd57 100644 --- a/crypto/src/crypto/digests/LongDigest.cs +++ b/crypto/src/crypto/digests/LongDigest.cs @@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Digests * Base class for SHA-384 and SHA-512. */ public abstract class LongDigest - : IDigest + : IDigest, IMemoable { private int MyByteLength = 128; @@ -41,8 +42,14 @@ namespace Org.BouncyCastle.Crypto.Digests */ internal LongDigest( LongDigest t) - { - xBuf = new byte[t.xBuf.Length]; + { + xBuf = new byte[t.xBuf.Length]; + + CopyIn(t); + } + + protected void CopyIn(LongDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -342,5 +349,7 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract string AlgorithmName { get; } public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs index 78c696f33..6d90f3f9d 100644 --- a/crypto/src/crypto/digests/MD2Digest.cs +++ b/crypto/src/crypto/digests/MD2Digest.cs @@ -1,5 +1,7 @@ using System; + using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 */ public class MD2Digest - : IDigest + : IDigest, IMemoable { private const int DigestLength = 16; private const int BYTE_LENGTH = 16; @@ -32,8 +34,14 @@ namespace Org.BouncyCastle.Crypto.Digests { Reset(); } + public MD2Digest(MD2Digest t) - { + { + CopyIn(t); + } + + private void CopyIn(MD2Digest t) + { Array.Copy(t.X, 0, X, 0, t.X.Length); xOff = t.xOff; Array.Copy(t.M, 0, M, 0, t.M.Length); @@ -41,6 +49,7 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Copy(t.C, 0, C, 0, t.C.Length); COff = t.COff; } + /** * return the algorithm name * @@ -242,6 +251,19 @@ namespace Org.BouncyCastle.Crypto.Digests (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51, (byte)159,(byte)17,(byte)131,(byte)20 }; + + public IMemoable Copy() + { + return new MD2Digest(this); + } + + public void Reset(IMemoable other) + { + MD2Digest d = (MD2Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs index bc4eae0fd..8743f7dad 100644 --- a/crypto/src/crypto/digests/MD4Digest.cs +++ b/crypto/src/crypto/digests/MD4Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -32,7 +34,13 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public MD4Digest(MD4Digest t) : base(t) - { + { + CopyIn(t); + } + + private void CopyIn(MD4Digest t) + { + base.CopyIn(t); H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -266,6 +274,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new MD4Digest(this); + } + + public override void Reset(IMemoable other) + { + MD4Digest d = (MD4Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs index 2116d64f0..c60ac92a3 100644 --- a/crypto/src/crypto/digests/MD5Digest.cs +++ b/crypto/src/crypto/digests/MD5Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -28,7 +29,13 @@ namespace Org.BouncyCastle.Crypto.Digests */ public MD5Digest(MD5Digest t) : base(t) - { + { + CopyIn(t); + } + + private void CopyIn(MD5Digest t) + { + base.CopyIn(t); H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -287,7 +294,20 @@ namespace Org.BouncyCastle.Crypto.Digests xOff = 0; } - } + + public override IMemoable Copy() + { + return new MD5Digest(this); + } + + public override void Reset(IMemoable other) + { + MD5Digest d = (MD5Digest)other; + + CopyIn(d); + } + + } } diff --git a/crypto/src/crypto/digests/RipeMD128Digest.cs b/crypto/src/crypto/digests/RipeMD128Digest.cs index 8977583a4..e8a0331ca 100644 --- a/crypto/src/crypto/digests/RipeMD128Digest.cs +++ b/crypto/src/crypto/digests/RipeMD128Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -28,8 +30,15 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public RipeMD128Digest(RipeMD128Digest t) : base(t) - { - H0 = t.H0; + { + CopyIn(t); + } + + private void CopyIn(RipeMD128Digest t) + { + base.CopyIn(t); + + H0 = t.H0; H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -457,6 +466,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD128Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD128Digest d = (RipeMD128Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs index 8ce52ae58..af4aa44bb 100644 --- a/crypto/src/crypto/digests/RipeMD160Digest.cs +++ b/crypto/src/crypto/digests/RipeMD160Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -30,6 +32,13 @@ namespace Org.BouncyCastle.Crypto.Digests */ public RipeMD160Digest(RipeMD160Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD160Digest t) + { + base.CopyIn(t); + H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -418,6 +427,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD160Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD160Digest d = (RipeMD160Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs index 950e94f80..306275767 100644 --- a/crypto/src/crypto/digests/RipeMD256Digest.cs +++ b/crypto/src/crypto/digests/RipeMD256Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /// <remarks> @@ -37,6 +39,12 @@ namespace Org.BouncyCastle.Crypto.Digests /// </summary> public RipeMD256Digest(RipeMD256Digest t):base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD256Digest t) + { + base.CopyIn(t); H0 = t.H0; H1 = t.H1; @@ -405,5 +413,18 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD256Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD256Digest d = (RipeMD256Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs index 25c74baef..767d74dba 100644 --- a/crypto/src/crypto/digests/RipeMD320Digest.cs +++ b/crypto/src/crypto/digests/RipeMD320Digest.cs @@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /// <remarks> @@ -38,6 +40,12 @@ namespace Org.BouncyCastle.Crypto.Digests public RipeMD320Digest(RipeMD320Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD320Digest t) + { + base.CopyIn(t); H0 = t.H0; H1 = t.H1; @@ -434,5 +442,18 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD320Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD320Digest d = (RipeMD320Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs index a115495f4..2c6837b3c 100644 --- a/crypto/src/crypto/digests/SHA3Digest.cs +++ b/crypto/src/crypto/digests/SHA3Digest.cs @@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests /// Following the naming conventions used in the C source code to enable easy review of the implementation. /// </remarks> public class Sha3Digest - : IDigest + : IDigest, IMemoable { private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants(); @@ -103,6 +103,11 @@ namespace Org.BouncyCastle.Crypto.Digests public Sha3Digest(Sha3Digest source) { + CopyIn(source); + } + + private void CopyIn(Sha3Digest source) + { Array.Copy(source.state, 0, this.state, 0, source.state.Length); Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length); this.rate = source.rate; @@ -537,5 +542,19 @@ namespace Org.BouncyCastle.Crypto.Digests { Array.Copy(byteState, 0, data, 0, laneCount * 8); } + + public IMemoable Copy() + { + return new Sha3Digest(this); + } + + public void Reset(IMemoable other) + { + Sha3Digest d = (Sha3Digest)other; + + CopyIn(d); + } + + } } diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs index e72b84f94..60ec651d5 100644 --- a/crypto/src/crypto/digests/Sha1Digest.cs +++ b/crypto/src/crypto/digests/Sha1Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -33,6 +34,13 @@ namespace Org.BouncyCastle.Crypto.Digests public Sha1Digest(Sha1Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha1Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -259,5 +267,18 @@ namespace Org.BouncyCastle.Crypto.Digests xOff = 0; Array.Clear(X, 0, 16); } + + public override IMemoable Copy() + { + return new Sha1Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha1Digest d = (Sha1Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs index 66ecd4ecd..b4e853745 100644 --- a/crypto/src/crypto/digests/Sha224Digest.cs +++ b/crypto/src/crypto/digests/Sha224Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -41,6 +42,13 @@ namespace Org.BouncyCastle.Crypto.Digests Sha224Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha224Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -264,5 +272,18 @@ namespace Org.BouncyCastle.Crypto.Digests 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public override IMemoable Copy() + { + return new Sha224Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha224Digest d = (Sha224Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs index 1c00ab71f..98e10a34d 100644 --- a/crypto/src/crypto/digests/Sha256Digest.cs +++ b/crypto/src/crypto/digests/Sha256Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -36,6 +37,13 @@ namespace Org.BouncyCastle.Crypto.Digests */ public Sha256Digest(Sha256Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha256Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -305,5 +313,18 @@ namespace Org.BouncyCastle.Crypto.Digests 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public override IMemoable Copy() + { + return new Sha256Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha256Digest d = (Sha256Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs index f1372d0a9..e6c9a9aa9 100644 --- a/crypto/src/crypto/digests/Sha384Digest.cs +++ b/crypto/src/crypto/digests/Sha384Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -83,5 +84,18 @@ namespace Org.BouncyCastle.Crypto.Digests H7 = 0xdb0c2e0d64f98fa7; H8 = 0x47b5481dbefa4fa4; } + + public override IMemoable Copy() + { + return new Sha384Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha384Digest d = (Sha384Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs index b38ff8ee7..2a0964fd3 100644 --- a/crypto/src/crypto/digests/Sha512Digest.cs +++ b/crypto/src/crypto/digests/Sha512Digest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -86,5 +87,18 @@ namespace Org.BouncyCastle.Crypto.Digests H7 = 0x1f83d9abfb41bd6b; H8 = 0x5be0cd19137e2179; } + + public override IMemoable Copy() + { + return new Sha512Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha512Digest d = (Sha512Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs index 7580d6267..2caefa763 100644 --- a/crypto/src/crypto/digests/Sha512tDigest.cs +++ b/crypto/src/crypto/digests/Sha512tDigest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -44,14 +45,7 @@ namespace Org.BouncyCastle.Crypto.Digests { this.digestLength = t.digestLength; - this.H1t = t.H1t; - this.H2t = t.H2t; - this.H3t = t.H3t; - this.H4t = t.H4t; - this.H5t = t.H5t; - this.H6t = t.H6t; - this.H7t = t.H7t; - this.H8t = t.H8t; + Reset(t); } public override string AlgorithmName @@ -175,5 +169,32 @@ namespace Org.BouncyCastle.Crypto.Digests bs[off + num] = (byte)(n >> shift); } } - } + + public override IMemoable Copy() + { + return new Sha512tDigest(this); + } + + public override void Reset(IMemoable other) + { + Sha512tDigest t = (Sha512tDigest)other; + + if (this.digestLength != t.digestLength) + { + throw new MemoableResetException("digestLength inappropriate in other"); + } + + base.CopyIn(t); + + this.H1t = t.H1t; + this.H2t = t.H2t; + this.H3t = t.H3t; + this.H4t = t.H4t; + this.H5t = t.H5t; + this.H6t = t.H6t; + this.H7t = t.H7t; + this.H8t = t.H8t; + } + + } } diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs new file mode 100644 index 000000000..f826ce503 --- /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..49ec51304 --- /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 ((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="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/digests/Sm3Digest.cs b/crypto/src/crypto/digests/Sm3Digest.cs new file mode 100644 index 000000000..7a9e6db36 --- /dev/null +++ b/crypto/src/crypto/digests/Sm3Digest.cs @@ -0,0 +1,328 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /// <summary> + /// Implementation of Chinese SM3 digest as described at + /// http://tools.ietf.org/html/draft-shen-sm3-hash-00 + /// and at .... ( Chinese PDF ) + /// </summary> + /// <remarks> + /// The specification says "process a bit stream", + /// but this is written to process bytes in blocks of 4, + /// meaning this will process 32-bit word groups. + /// But so do also most other digest specifications, + /// including the SHA-256 which was a origin for + /// this specification. + /// </remarks> + public class Sm3Digest + : GeneralDigest + { + private const int DIGEST_LENGTH = 32; // bytes + private const int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints) + + private uint[] V = new uint[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints) + private uint[] inwords = new uint[BLOCK_SIZE]; + private int xOff; + + // Work-bufs used within processBlock() + private uint[] W = new uint[68]; + private uint[] W1 = new uint[64]; + + // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions. + private static readonly uint[] T = new uint[64]; + + static Sm3Digest() + { + for (int i = 0; i < 16; ++i) + { + uint t = 0x79CC4519; + T[i] = (t << i) | (t >> (32 - i)); + } + for (int i = 16; i < 64; ++i) + { + int n = i % 32; + uint t = 0x7A879D8A; + T[i] = (t << n) | (t >> (32 - n)); + } + } + + + /// <summary> + /// Standard constructor + /// </summary> + public Sm3Digest() + { + Reset(); + } + + /// <summary> + /// Copy constructor. This will copy the state of the provided + /// message digest. + /// </summary> + public Sm3Digest(Sm3Digest t) + : base(t) + { + CopyIn(t); + } + + private void CopyIn(Sm3Digest t) + { + Array.Copy(t.V, 0, this.V, 0, this.V.Length); + Array.Copy(t.inwords, 0, this.inwords, 0, this.inwords.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SM3"; } + } + + public override int GetDigestSize() + { + return DIGEST_LENGTH; + } + + public override IMemoable Copy() + { + return new Sm3Digest(this); + } + + public override void Reset(IMemoable other) + { + Sm3Digest d = (Sm3Digest)other; + + base.CopyIn(d); + CopyIn(d); + } + + /// <summary> + /// reset the chaining variables + /// </summary> + public override void Reset() + { + base.Reset(); + + this.V[0] = 0x7380166F; + this.V[1] = 0x4914B2B9; + this.V[2] = 0x172442D7; + this.V[3] = 0xDA8A0600; + this.V[4] = 0xA96F30BC; + this.V[5] = 0x163138AA; + this.V[6] = 0xE38DEE4D; + this.V[7] = 0xB0FB0E4E; + + this.xOff = 0; + } + + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + Pack.UInt32_To_BE(this.V[0], output, outOff + 0); + Pack.UInt32_To_BE(this.V[1], output, outOff + 4); + Pack.UInt32_To_BE(this.V[2], output, outOff + 8); + Pack.UInt32_To_BE(this.V[3], output, outOff + 12); + Pack.UInt32_To_BE(this.V[4], output, outOff + 16); + Pack.UInt32_To_BE(this.V[5], output, outOff + 20); + Pack.UInt32_To_BE(this.V[6], output, outOff + 24); + Pack.UInt32_To_BE(this.V[7], output, outOff + 28); + + Reset(); + + return DIGEST_LENGTH; + } + + + internal override void ProcessWord(byte[] input, + int inOff) + { + uint n = Pack.BE_To_UInt32(input, inOff); + this.inwords[this.xOff] = n; + ++this.xOff; + + if (this.xOff >= 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength(long bitLength) + { + if (this.xOff > (BLOCK_SIZE - 2)) + { + // xOff == 15 --> can't fit the 64 bit length field at tail.. + this.inwords[this.xOff] = 0; // fill with zero + ++this.xOff; + + ProcessBlock(); + } + // Fill with zero words, until reach 2nd to last slot + while (this.xOff < (BLOCK_SIZE - 2)) + { + this.inwords[this.xOff] = 0; + ++this.xOff; + } + + // Store input data length in BITS + this.inwords[this.xOff++] = (uint)(bitLength >> 32); + this.inwords[this.xOff++] = (uint)(bitLength); + } + + /* + + 3.4.2. Constants + + + Tj = 79cc4519 when 0 < = j < = 15 + Tj = 7a879d8a when 16 < = j < = 63 + + 3.4.3. Boolean function + + + FFj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63 + + GGj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (NOT X AND Z) when 16 < = j < = 63 + + The X, Y, Z in the fomular are words!GBP + + 3.4.4. Permutation function + + + P0(X) = X XOR (X <<< 9) XOR (X <<< 17) ## ROLL, not SHIFT + P1(X) = X XOR (X <<< 15) XOR (X <<< 23) ## ROLL, not SHIFT + + The X in the fomular are a word. + + ---------- + + Each ROLL converted to Java expression: + + ROLL 9 : ((x << 9) | (x >> (32-9)))) + ROLL 17 : ((x << 17) | (x >> (32-17))) + ROLL 15 : ((x << 15) | (x >> (32-15))) + ROLL 23 : ((x << 23) | (x >> (32-23))) + + */ + + private uint P0(uint x) + { + uint r9 = ((x << 9) | (x >> (32 - 9))); + uint r17 = ((x << 17) | (x >> (32 - 17))); + return (x ^ r9 ^ r17); + } + + private uint P1(uint x) + { + uint r15 = ((x << 15) | (x >> (32 - 15))); + uint r23 = ((x << 23) | (x >> (32 - 23))); + return (x ^ r15 ^ r23); + } + + private uint FF0(uint x, uint y, uint z) + { + return (x ^ y ^ z); + } + + private uint FF1(uint x, uint y, uint z) + { + return ((x & y) | (x & z) | (y & z)); + } + + private uint GG0(uint x, uint y, uint z) + { + return (x ^ y ^ z); + } + + private uint GG1(uint x, uint y, uint z) + { + return ((x & y) | ((~x) & z)); + } + + + internal override void ProcessBlock() + { + for (int j = 0; j < 16; ++j) + { + this.W[j] = this.inwords[j]; + } + for (int j = 16; j < 68; ++j) + { + uint wj3 = this.W[j - 3]; + uint r15 = ((wj3 << 15) | (wj3 >> (32 - 15))); + uint wj13 = this.W[j - 13]; + uint r7 = ((wj13 << 7) | (wj13 >> (32 - 7))); + this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6]; + } + for (int j = 0; j < 64; ++j) + { + this.W1[j] = this.W[j] ^ this.W[j + 4]; + } + + uint A = this.V[0]; + uint B = this.V[1]; + uint C = this.V[2]; + uint D = this.V[3]; + uint E = this.V[4]; + uint F = this.V[5]; + uint G = this.V[6]; + uint H = this.V[7]; + + + for (int j = 0; j < 16; ++j) + { + uint a12 = ((A << 12) | (A >> (32 - 12))); + uint s1_ = a12 + E + T[j]; + uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7))); + uint SS2 = SS1 ^ a12; + uint TT1 = FF0(A, B, C) + D + SS2 + this.W1[j]; + uint TT2 = GG0(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >> (32 - 19))); + F = E; + E = P0(TT2); + } + + // Different FF,GG functions on rounds 16..63 + for (int j = 16; j < 64; ++j) + { + uint a12 = ((A << 12) | (A >> (32 - 12))); + uint s1_ = a12 + E + T[j]; + uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7))); + uint SS2 = SS1 ^ a12; + uint TT1 = FF1(A, B, C) + D + SS2 + this.W1[j]; + uint TT2 = GG1(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >> (32 - 19))); + F = E; + E = P0(TT2); + } + + this.V[0] ^= A; + this.V[1] ^= B; + this.V[2] ^= C; + this.V[3] ^= D; + this.V[4] ^= E; + this.V[5] ^= F; + this.V[6] ^= G; + this.V[7] ^= H; + + this.xOff = 0; + } + } +} \ No newline at end of file diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs index b8c9a7664..059232de0 100644 --- a/crypto/src/crypto/digests/TigerDigest.cs +++ b/crypto/src/crypto/digests/TigerDigest.cs @@ -1,5 +1,7 @@ using System; + using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * http://www.cs.technion.ac.il/~biham/Reports/Tiger</a> */ public class TigerDigest - : IDigest + : IDigest, IMemoable { private const int MyByteLength = 64; @@ -571,17 +573,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ public TigerDigest(TigerDigest t) { - a = t.a; - b = t.b; - c = t.c; - - Array.Copy(t.x, 0, x, 0, t.x.Length); - xOff = t.xOff; - - Array.Copy(t.Buffer, 0, Buffer, 0, t.Buffer.Length); - bOff = t.bOff; - - byteCount = t.byteCount; + Reset(t); } public string AlgorithmName @@ -864,5 +856,28 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount = 0; } - } + + public IMemoable Copy() + { + return new TigerDigest(this); + } + + public void Reset(IMemoable other) + { + TigerDigest t = (TigerDigest)other; + + a = t.a; + b = t.b; + c = t.c; + + Array.Copy(t.x, 0, x, 0, t.x.Length); + xOff = t.xOff; + + Array.Copy(t.Buffer, 0, Buffer, 0, t.Buffer.Length); + bOff = t.bOff; + + byteCount = t.byteCount; + } + + } } diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs index df83f4508..55b71205e 100644 --- a/crypto/src/crypto/digests/WhirlpoolDigest.cs +++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs @@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +10,8 @@ namespace Org.BouncyCastle.Crypto.Digests * and Rijmen. * */ - public sealed class WhirlpoolDigest : IDigest + public sealed class WhirlpoolDigest + : IDigest, IMemoable { private const int BYTE_LENGTH = 64; @@ -149,19 +151,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ public WhirlpoolDigest(WhirlpoolDigest originalDigest) { - Array.Copy(originalDigest._rc, 0, _rc, 0, _rc.Length); - - Array.Copy(originalDigest._buffer, 0, _buffer, 0, _buffer.Length); - - this._bufferPos = originalDigest._bufferPos; - Array.Copy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.Length); - - // -- internal hash state -- - Array.Copy(originalDigest._hash, 0, _hash, 0, _hash.Length); - Array.Copy(originalDigest._K, 0, _K, 0, _K.Length); - Array.Copy(originalDigest._L, 0, _L, 0, _L.Length); - Array.Copy(originalDigest._block, 0, _block, 0, _block.Length); - Array.Copy(originalDigest._state, 0, _state, 0, _state.Length); + Reset(originalDigest); } public string AlgorithmName @@ -393,5 +383,31 @@ namespace Org.BouncyCastle.Crypto.Digests { return BYTE_LENGTH; } + + public IMemoable Copy() + { + return new WhirlpoolDigest(this); + } + + public void Reset(IMemoable other) + { + WhirlpoolDigest originalDigest = (WhirlpoolDigest)other; + + Array.Copy(originalDigest._rc, 0, _rc, 0, _rc.Length); + + Array.Copy(originalDigest._buffer, 0, _buffer, 0, _buffer.Length); + + this._bufferPos = originalDigest._bufferPos; + Array.Copy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.Length); + + // -- internal hash state -- + Array.Copy(originalDigest._hash, 0, _hash, 0, _hash.Length); + Array.Copy(originalDigest._K, 0, _K, 0, _K.Length); + Array.Copy(originalDigest._L, 0, _L, 0, _L.Length); + Array.Copy(originalDigest._block, 0, _block, 0, _block.Length); + Array.Copy(originalDigest._state, 0, _state, 0, _state.Length); + } + + } } diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs new file mode 100644 index 000000000..3d4ee8835 --- /dev/null +++ b/crypto/src/crypto/engines/ThreefishEngine.cs @@ -0,0 +1,1491 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Engines +{ + + /// <summary> + /// Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block + /// sizes. + /// </summary> + /// <remarks> + /// This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST + /// SHA-3 competition in October 2010. + /// <p/> + /// Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + /// <p/> + /// This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables + /// to speed up key schedule injection. <br> + /// 2 x block size state is retained by each cipher instance. + /// </remarks> + public class ThreefishEngine + : IBlockCipher + { + /// <summary> + /// 256 bit block size - Threefish-256 + /// </summary> + public const int BLOCKSIZE_256 = 256; + /// <summary> + /// 512 bit block size - Threefish-512 + /// </summary> + public const int BLOCKSIZE_512 = 512; + /// <summary> + /// 1024 bit block size - Threefish-1024 + /// </summary> + public const int BLOCKSIZE_1024 = 1024; + + /** + * Size of the tweak in bytes (always 128 bit/16 bytes) + */ + private const int TWEAK_SIZE_BYTES = 16; + private const int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8; + + /** + * Rounds in Threefish-256 + */ + private const int ROUNDS_256 = 72; + /** + * Rounds in Threefish-512 + */ + private const int ROUNDS_512 = 72; + /** + * Rounds in Threefish-1024 + */ + private const int ROUNDS_1024 = 80; + + /** + * Max rounds of any of the variants + */ + private const int MAX_ROUNDS = ROUNDS_1024; + + /** + * Key schedule parity constant + */ + private const ulong C_240 = 0x1BD11BDAA9FC1A22L; + + /* Pre-calculated modulo arithmetic tables for key schedule lookups */ + private static readonly int[] MOD9 = new int[MAX_ROUNDS]; + private static readonly int[] MOD17 = new int[MOD9.Length]; + private static readonly int[] MOD5 = new int[MOD9.Length]; + private static readonly int[] MOD3 = new int[MOD9.Length]; + + static ThreefishEngine() + { + for (int i = 0; i < MOD9.Length; i++) + { + MOD17[i] = i % 17; + MOD9[i] = i % 9; + MOD5[i] = i % 5; + MOD3[i] = i % 3; + } + } + + /** + * Block size in bytes + */ + private readonly int blocksizeBytes; + + /** + * Block size in 64 bit words + */ + private readonly int blocksizeWords; + + /** + * Buffer for byte oriented processBytes to call internal word API + */ + private readonly ulong[] currentBlock; + + /** + * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup + */ + private readonly ulong[] t = new ulong[5]; + + /** + * Key schedule words + */ + private readonly ulong[] kw; + + /** + * The internal cipher implementation (varies by blocksize) + */ + private readonly ThreefishCipher cipher; + + private bool forEncryption; + + /// <summary> + /// Constructs a new Threefish cipher, with a specified block size. + /// </summary> + /// <param name="blocksizeBits">the block size in bits, one of <see cref="BLOCKSIZE_256"/>, <see cref="BLOCKSIZE_512"/>, + /// <see cref="BLOCKSIZE_1024"/> .</param> + public ThreefishEngine(int blocksizeBits) + { + this.blocksizeBytes = (blocksizeBits / 8); + this.blocksizeWords = (this.blocksizeBytes / 8); + this.currentBlock = new ulong[blocksizeWords]; + + /* + * Provide room for original key words, extended key word and repeat of key words for modulo + * free lookup of key schedule words. + */ + this.kw = new ulong[2 * blocksizeWords + 1]; + + switch (blocksizeBits) + { + case BLOCKSIZE_256: + cipher = new Threefish256Cipher(kw, t); + break; + case BLOCKSIZE_512: + cipher = new Threefish512Cipher(kw, t); + break; + case BLOCKSIZE_1024: + cipher = new Threefish1024Cipher(kw, t); + break; + default: + throw new ArgumentException( + "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits"); + } + } + + /// <summary> + /// Initialise the engine. + /// </summary> + /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> + /// <param name="parameters">an instance of <see cref="TweakableBlockCipherParameters"/> or <see cref="KeyParameter"/> (to + /// use a 0 tweak)</param> + public void Init(bool forEncryption, ICipherParameters parameters) + { + byte[] keyBytes; + byte[] tweakBytes; + + if (parameters is TweakableBlockCipherParameters) + { + TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)parameters; + keyBytes = tParams.Key.GetKey(); + tweakBytes = tParams.Tweak; + } + else if (parameters is KeyParameter) + { + keyBytes = ((KeyParameter)parameters).GetKey(); + tweakBytes = null; + } + else + { + throw new ArgumentException("Invalid parameter passed to Threefish init - " + + parameters.GetType().Name); + } + + ulong[] keyWords = null; + ulong[] tweakWords = null; + + if (keyBytes != null) + { + if (keyBytes.Length != this.blocksizeBytes) + { + throw new ArgumentException("Threefish key must be same size as block (" + blocksizeBytes + + " bytes)"); + } + keyWords = new ulong[blocksizeWords]; + for (int i = 0; i < keyWords.Length; i++) + { + keyWords[i] = BytesToWord(keyBytes, i * 8); + } + } + if (tweakBytes != null) + { + if (tweakBytes.Length != TWEAK_SIZE_BYTES) + { + throw new ArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes"); + } + tweakWords = new ulong[]{BytesToWord(tweakBytes, 0), BytesToWord(tweakBytes, 8)}; + } + Init(forEncryption, keyWords, tweakWords); + } + + /// <summary> + /// Initialise the engine, specifying the key and tweak directly. + /// </summary> + /// <param name="forEncryption">the cipher mode.</param> + /// <param name="key">the words of the key, or <code>null</code> to use the current key.</param> + /// <param name="tweak">the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.</param> + internal void Init(bool forEncryption, ulong[] key, ulong[] tweak) + { + this.forEncryption = forEncryption; + if (key != null) + { + SetKey(key); + } + if (tweak != null) + { + SetTweak(tweak); + } + } + + private void SetKey(ulong[] key) + { + if (key.Length != this.blocksizeWords) + { + throw new ArgumentException("Threefish key must be same size as block (" + blocksizeWords + + " words)"); + } + + /* + * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512, + * 20k for 1024). + * + * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations + * used, to avoid expensive mod computations during cipher operation. + */ + + ulong knw = C_240; + for (int i = 0; i < blocksizeWords; i++) + { + kw[i] = key[i]; + knw = knw ^ kw[i]; + } + kw[blocksizeWords] = knw; + Array.Copy(kw, 0, kw, blocksizeWords + 1, blocksizeWords); + } + + private void SetTweak(ulong[] tweak) + { + if (tweak.Length != TWEAK_SIZE_WORDS) + { + throw new ArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words."); + } + + /* + * Tweak schedule partially repeated to avoid mod computations during cipher operation + */ + t[0] = tweak[0]; + t[1] = tweak[1]; + t[2] = t[0] ^ t[1]; + t[3] = t[0]; + t[4] = t[1]; + } + + public string AlgorithmName + { + get { return "Threefish-" + (blocksizeBytes * 8); } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return blocksizeBytes; + } + + public void Reset() + { + } + + public int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + if ((outOff + blocksizeBytes) > outBytes.Length) + { + throw new DataLengthException("Output buffer too short"); + } + + if ((inOff + blocksizeBytes) > inBytes.Length) + { + throw new DataLengthException("Input buffer too short"); + } + + for (int i = 0; i < blocksizeBytes; i += 8) + { + currentBlock[i >> 3] = BytesToWord(inBytes, inOff + i); + } + ProcessBlock(this.currentBlock, this.currentBlock); + for (int i = 0; i < blocksizeBytes; i += 8) + { + WordToBytes(this.currentBlock[i >> 3], outBytes, outOff + i); + } + + return blocksizeBytes; + } + + /// <summary> + /// Process a block of data represented as 64 bit words. + /// </summary> + /// <returns>the number of 8 byte words processed (which will be the same as the block size).</returns> + /// <param name="inWords">a block sized buffer of words to process.</param> + /// <param name="outWords">a block sized buffer of words to receive the output of the operation.</param> + /// <exception cref="DataLengthException">if either the input or output is not block sized</exception> + /// <exception cref="InvalidOperationException">if this engine is not initialised</exception> + internal int ProcessBlock(ulong[] inWords, ulong[] outWords) + { + if (kw[blocksizeWords] == 0) + { + throw new InvalidOperationException("Threefish engine not initialised"); + } + + if (inWords.Length != blocksizeWords) + { + throw new DataLengthException("Input buffer too short"); + } + if (outWords.Length != blocksizeWords) + { + throw new DataLengthException("Output buffer too short"); + } + + if (forEncryption) + { + cipher.EncryptBlock(inWords, outWords); + } + else + { + cipher.DecryptBlock(inWords, outWords); + } + + return blocksizeWords; + } + + /// <summary> + /// Read a single 64 bit word from input in LSB first order. + /// </summary> + internal static ulong BytesToWord(byte[] bytes, int off) + { + if ((off + 8) > bytes.Length) + { + // Help the JIT avoid index checks + throw new ArgumentException(); + } + + ulong word = 0; + int index = off; + + word = (bytes[index++] & 0xffUL); + word |= (bytes[index++] & 0xffUL) << 8; + word |= (bytes[index++] & 0xffUL) << 16; + word |= (bytes[index++] & 0xffUL) << 24; + word |= (bytes[index++] & 0xffUL) << 32; + word |= (bytes[index++] & 0xffUL) << 40; + word |= (bytes[index++] & 0xffUL) << 48; + word |= (bytes[index++] & 0xffUL) << 56; + + return word; + } + + /// <summary> + /// Write a 64 bit word to output in LSB first order. + /// </summary> + internal static void WordToBytes(ulong word, byte[] bytes, int off) + { + if ((off + 8) > bytes.Length) + { + // Help the JIT avoid index checks + throw new ArgumentException(); + } + int index = off; + + bytes[index++] = (byte)word; + bytes[index++] = (byte)(word >> 8); + bytes[index++] = (byte)(word >> 16); + bytes[index++] = (byte)(word >> 24); + bytes[index++] = (byte)(word >> 32); + bytes[index++] = (byte)(word >> 40); + bytes[index++] = (byte)(word >> 48); + bytes[index++] = (byte)(word >> 56); + } + + /** + * Rotate left + xor part of the mix operation. + */ + private static ulong RotlXor(ulong x, int n, ulong xor) + { + return ((x << n) | (x >> (64 - n))) ^ xor; + } + + /** + * Rotate xor + rotate right part of the unmix operation. + */ + private static ulong XorRotr(ulong x, int n, ulong xor) + { + ulong xored = x ^ xor; + return (xored >> n) | (xored << (64 - n)); + } + + private abstract class ThreefishCipher + { + /** + * The extended + repeated tweak words + */ + protected readonly ulong[] t; + /** + * The extended + repeated key words + */ + protected readonly ulong[] kw; + + protected ThreefishCipher(ulong[] kw, ulong[] t) + { + this.kw = kw; + this.t = t; + } + + internal abstract void EncryptBlock(ulong[] block, ulong[] outWords); + + internal abstract void DecryptBlock(ulong[] block, ulong[] outWords); + + } + + private sealed class Threefish256Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 14, ROTATION_0_1 = 16; + private const int ROTATION_1_0 = 52, ROTATION_1_1 = 57; + private const int ROTATION_2_0 = 23, ROTATION_2_1 = 40; + private const int ROTATION_3_0 = 5, ROTATION_3_1 = 37; + + private const int ROTATION_4_0 = 25, ROTATION_4_1 = 33; + private const int ROTATION_5_0 = 46, ROTATION_5_1 = 12; + private const int ROTATION_6_0 = 58, ROTATION_6_1 = 22; + private const int ROTATION_7_0 = 32, ROTATION_7_1 = 32; + + public Threefish256Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod5 = MOD5; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 9) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 4 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1] + t[0]; + b2 += kw[2] + t[1]; + b3 += kw[3]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 2 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_256 / 4); d += 2) + { + int dm5 = mod5[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 2 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_1_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_1_1, b2 += b1); + + b1 = RotlXor(b1, ROTATION_2_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_2_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_3_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_3_1, b2 += b1); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm5]; + b1 += kw[dm5 + 1] + t[dm3]; + b2 += kw[dm5 + 2] + t[dm3 + 1]; + b3 += kw[dm5 + 3] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_5_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_5_1, b2 += b1); + + b1 = RotlXor(b1, ROTATION_6_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_6_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_7_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_7_1, b2 += b1); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm5 + 1]; + b1 += kw[dm5 + 2] + t[dm3 + 1]; + b2 += kw[dm5 + 3] + t[dm3 + 2]; + b3 += kw[dm5 + 4] + (uint)d + 1; + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod5 = MOD5; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 9) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + + for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2) + { + int dm5 = mod5[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm5 + 1]; + b1 -= kw[dm5 + 2] + t[dm3 + 1]; + b2 -= kw[dm5 + 3] + t[dm3 + 2]; + b3 -= kw[dm5 + 4] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b3 = XorRotr(b3, ROTATION_7_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_7_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_6_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_6_1, b2); + b2 -= b3; + + b3 = XorRotr(b3, ROTATION_5_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_5_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm5]; + b1 -= kw[dm5 + 1] + t[dm3]; + b2 -= kw[dm5 + 2] + t[dm3 + 1]; + b3 -= kw[dm5 + 3] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b3 = XorRotr(b3, ROTATION_3_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_3_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_2_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_2_1, b2); + b2 -= b3; + + b3 = XorRotr(b3, ROTATION_1_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_1_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1] + t[0]; + b2 -= kw[2] + t[1]; + b3 -= kw[3]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + } + + } + + private sealed class Threefish512Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37; + private const int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42; + private const int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39; + private const int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56; + + private const int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24; + private const int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17; + private const int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43; + private const int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22; + + internal Threefish512Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod9 = MOD9; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 17) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 8 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5] + t[0]; + b6 += kw[6] + t[1]; + b7 += kw[7]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_512 / 4); d += 2) + { + int dm9 = mod9[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_0_3, b6 += b7); + + b1 = RotlXor(b1, ROTATION_1_0, b2 += b1); + b7 = RotlXor(b7, ROTATION_1_1, b4 += b7); + b5 = RotlXor(b5, ROTATION_1_2, b6 += b5); + b3 = RotlXor(b3, ROTATION_1_3, b0 += b3); + + b1 = RotlXor(b1, ROTATION_2_0, b4 += b1); + b3 = RotlXor(b3, ROTATION_2_1, b6 += b3); + b5 = RotlXor(b5, ROTATION_2_2, b0 += b5); + b7 = RotlXor(b7, ROTATION_2_3, b2 += b7); + + b1 = RotlXor(b1, ROTATION_3_0, b6 += b1); + b7 = RotlXor(b7, ROTATION_3_1, b0 += b7); + b5 = RotlXor(b5, ROTATION_3_2, b2 += b5); + b3 = RotlXor(b3, ROTATION_3_3, b4 += b3); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm9]; + b1 += kw[dm9 + 1]; + b2 += kw[dm9 + 2]; + b3 += kw[dm9 + 3]; + b4 += kw[dm9 + 4]; + b5 += kw[dm9 + 5] + t[dm3]; + b6 += kw[dm9 + 6] + t[dm3 + 1]; + b7 += kw[dm9 + 7] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_4_3, b6 += b7); + + b1 = RotlXor(b1, ROTATION_5_0, b2 += b1); + b7 = RotlXor(b7, ROTATION_5_1, b4 += b7); + b5 = RotlXor(b5, ROTATION_5_2, b6 += b5); + b3 = RotlXor(b3, ROTATION_5_3, b0 += b3); + + b1 = RotlXor(b1, ROTATION_6_0, b4 += b1); + b3 = RotlXor(b3, ROTATION_6_1, b6 += b3); + b5 = RotlXor(b5, ROTATION_6_2, b0 += b5); + b7 = RotlXor(b7, ROTATION_6_3, b2 += b7); + + b1 = RotlXor(b1, ROTATION_7_0, b6 += b1); + b7 = RotlXor(b7, ROTATION_7_1, b0 += b7); + b5 = RotlXor(b5, ROTATION_7_2, b2 += b5); + b3 = RotlXor(b3, ROTATION_7_3, b4 += b3); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm9 + 1]; + b1 += kw[dm9 + 2]; + b2 += kw[dm9 + 3]; + b3 += kw[dm9 + 4]; + b4 += kw[dm9 + 5]; + b5 += kw[dm9 + 6] + t[dm3 + 1]; + b6 += kw[dm9 + 7] + t[dm3 + 2]; + b7 += kw[dm9 + 8] + (uint)d + 1; + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + outWords[4] = b4; + outWords[5] = b5; + outWords[6] = b6; + outWords[7] = b7; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod9 = MOD9; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 17) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + + for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2) + { + int dm9 = mod9[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm9 + 1]; + b1 -= kw[dm9 + 2]; + b2 -= kw[dm9 + 3]; + b3 -= kw[dm9 + 4]; + b4 -= kw[dm9 + 5]; + b5 -= kw[dm9 + 6] + t[dm3 + 1]; + b6 -= kw[dm9 + 7] + t[dm3 + 2]; + b7 -= kw[dm9 + 8] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b1 = XorRotr(b1, ROTATION_7_0, b6); + b6 -= b1; + b7 = XorRotr(b7, ROTATION_7_1, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_7_2, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_7_3, b4); + b4 -= b3; + + b1 = XorRotr(b1, ROTATION_6_0, b4); + b4 -= b1; + b3 = XorRotr(b3, ROTATION_6_1, b6); + b6 -= b3; + b5 = XorRotr(b5, ROTATION_6_2, b0); + b0 -= b5; + b7 = XorRotr(b7, ROTATION_6_3, b2); + b2 -= b7; + + b1 = XorRotr(b1, ROTATION_5_0, b2); + b2 -= b1; + b7 = XorRotr(b7, ROTATION_5_1, b4); + b4 -= b7; + b5 = XorRotr(b5, ROTATION_5_2, b6); + b6 -= b5; + b3 = XorRotr(b3, ROTATION_5_3, b0); + b0 -= b3; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm9]; + b1 -= kw[dm9 + 1]; + b2 -= kw[dm9 + 2]; + b3 -= kw[dm9 + 3]; + b4 -= kw[dm9 + 4]; + b5 -= kw[dm9 + 5] + t[dm3]; + b6 -= kw[dm9 + 6] + t[dm3 + 1]; + b7 -= kw[dm9 + 7] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b1 = XorRotr(b1, ROTATION_3_0, b6); + b6 -= b1; + b7 = XorRotr(b7, ROTATION_3_1, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_3_2, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_3_3, b4); + b4 -= b3; + + b1 = XorRotr(b1, ROTATION_2_0, b4); + b4 -= b1; + b3 = XorRotr(b3, ROTATION_2_1, b6); + b6 -= b3; + b5 = XorRotr(b5, ROTATION_2_2, b0); + b0 -= b5; + b7 = XorRotr(b7, ROTATION_2_3, b2); + b2 -= b7; + + b1 = XorRotr(b1, ROTATION_1_0, b2); + b2 -= b1; + b7 = XorRotr(b7, ROTATION_1_1, b4); + b4 -= b7; + b5 = XorRotr(b5, ROTATION_1_2, b6); + b6 -= b5; + b3 = XorRotr(b3, ROTATION_1_3, b0); + b0 -= b3; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5] + t[0]; + b6 -= kw[6] + t[1]; + b7 -= kw[7]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + } + } + + private sealed class Threefish1024Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47; + private const int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37; + private const int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55; + private const int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52; + private const int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13; + private const int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17; + private const int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41; + private const int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25; + + private const int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31; + private const int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30; + private const int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51; + private const int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41; + private const int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46; + private const int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25; + private const int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52; + private const int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20; + + public Threefish1024Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod17 = MOD17; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 33) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 16 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + ulong b8 = block[8]; + ulong b9 = block[9]; + ulong b10 = block[10]; + ulong b11 = block[11]; + ulong b12 = block[12]; + ulong b13 = block[13]; + ulong b14 = block[14]; + ulong b15 = block[15]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5]; + b6 += kw[6]; + b7 += kw[7]; + b8 += kw[8]; + b9 += kw[9]; + b10 += kw[10]; + b11 += kw[11]; + b12 += kw[12]; + b13 += kw[13] + t[0]; + b14 += kw[14] + t[1]; + b15 += kw[15]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_1024 / 4); d += 2) + { + int dm17 = mod17[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_0_3, b6 += b7); + b9 = RotlXor(b9, ROTATION_0_4, b8 += b9); + b11 = RotlXor(b11, ROTATION_0_5, b10 += b11); + b13 = RotlXor(b13, ROTATION_0_6, b12 += b13); + b15 = RotlXor(b15, ROTATION_0_7, b14 += b15); + + b9 = RotlXor(b9, ROTATION_1_0, b0 += b9); + b13 = RotlXor(b13, ROTATION_1_1, b2 += b13); + b11 = RotlXor(b11, ROTATION_1_2, b6 += b11); + b15 = RotlXor(b15, ROTATION_1_3, b4 += b15); + b7 = RotlXor(b7, ROTATION_1_4, b10 += b7); + b3 = RotlXor(b3, ROTATION_1_5, b12 += b3); + b5 = RotlXor(b5, ROTATION_1_6, b14 += b5); + b1 = RotlXor(b1, ROTATION_1_7, b8 += b1); + + b7 = RotlXor(b7, ROTATION_2_0, b0 += b7); + b5 = RotlXor(b5, ROTATION_2_1, b2 += b5); + b3 = RotlXor(b3, ROTATION_2_2, b4 += b3); + b1 = RotlXor(b1, ROTATION_2_3, b6 += b1); + b15 = RotlXor(b15, ROTATION_2_4, b12 += b15); + b13 = RotlXor(b13, ROTATION_2_5, b14 += b13); + b11 = RotlXor(b11, ROTATION_2_6, b8 += b11); + b9 = RotlXor(b9, ROTATION_2_7, b10 += b9); + + b15 = RotlXor(b15, ROTATION_3_0, b0 += b15); + b11 = RotlXor(b11, ROTATION_3_1, b2 += b11); + b13 = RotlXor(b13, ROTATION_3_2, b6 += b13); + b9 = RotlXor(b9, ROTATION_3_3, b4 += b9); + b1 = RotlXor(b1, ROTATION_3_4, b14 += b1); + b5 = RotlXor(b5, ROTATION_3_5, b8 += b5); + b3 = RotlXor(b3, ROTATION_3_6, b10 += b3); + b7 = RotlXor(b7, ROTATION_3_7, b12 += b7); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm17]; + b1 += kw[dm17 + 1]; + b2 += kw[dm17 + 2]; + b3 += kw[dm17 + 3]; + b4 += kw[dm17 + 4]; + b5 += kw[dm17 + 5]; + b6 += kw[dm17 + 6]; + b7 += kw[dm17 + 7]; + b8 += kw[dm17 + 8]; + b9 += kw[dm17 + 9]; + b10 += kw[dm17 + 10]; + b11 += kw[dm17 + 11]; + b12 += kw[dm17 + 12]; + b13 += kw[dm17 + 13] + t[dm3]; + b14 += kw[dm17 + 14] + t[dm3 + 1]; + b15 += kw[dm17 + 15] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_4_3, b6 += b7); + b9 = RotlXor(b9, ROTATION_4_4, b8 += b9); + b11 = RotlXor(b11, ROTATION_4_5, b10 += b11); + b13 = RotlXor(b13, ROTATION_4_6, b12 += b13); + b15 = RotlXor(b15, ROTATION_4_7, b14 += b15); + + b9 = RotlXor(b9, ROTATION_5_0, b0 += b9); + b13 = RotlXor(b13, ROTATION_5_1, b2 += b13); + b11 = RotlXor(b11, ROTATION_5_2, b6 += b11); + b15 = RotlXor(b15, ROTATION_5_3, b4 += b15); + b7 = RotlXor(b7, ROTATION_5_4, b10 += b7); + b3 = RotlXor(b3, ROTATION_5_5, b12 += b3); + b5 = RotlXor(b5, ROTATION_5_6, b14 += b5); + b1 = RotlXor(b1, ROTATION_5_7, b8 += b1); + + b7 = RotlXor(b7, ROTATION_6_0, b0 += b7); + b5 = RotlXor(b5, ROTATION_6_1, b2 += b5); + b3 = RotlXor(b3, ROTATION_6_2, b4 += b3); + b1 = RotlXor(b1, ROTATION_6_3, b6 += b1); + b15 = RotlXor(b15, ROTATION_6_4, b12 += b15); + b13 = RotlXor(b13, ROTATION_6_5, b14 += b13); + b11 = RotlXor(b11, ROTATION_6_6, b8 += b11); + b9 = RotlXor(b9, ROTATION_6_7, b10 += b9); + + b15 = RotlXor(b15, ROTATION_7_0, b0 += b15); + b11 = RotlXor(b11, ROTATION_7_1, b2 += b11); + b13 = RotlXor(b13, ROTATION_7_2, b6 += b13); + b9 = RotlXor(b9, ROTATION_7_3, b4 += b9); + b1 = RotlXor(b1, ROTATION_7_4, b14 += b1); + b5 = RotlXor(b5, ROTATION_7_5, b8 += b5); + b3 = RotlXor(b3, ROTATION_7_6, b10 += b3); + b7 = RotlXor(b7, ROTATION_7_7, b12 += b7); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm17 + 1]; + b1 += kw[dm17 + 2]; + b2 += kw[dm17 + 3]; + b3 += kw[dm17 + 4]; + b4 += kw[dm17 + 5]; + b5 += kw[dm17 + 6]; + b6 += kw[dm17 + 7]; + b7 += kw[dm17 + 8]; + b8 += kw[dm17 + 9]; + b9 += kw[dm17 + 10]; + b10 += kw[dm17 + 11]; + b11 += kw[dm17 + 12]; + b12 += kw[dm17 + 13]; + b13 += kw[dm17 + 14] + t[dm3 + 1]; + b14 += kw[dm17 + 15] + t[dm3 + 2]; + b15 += kw[dm17 + 16] + (uint)d + 1; + + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + outWords[4] = b4; + outWords[5] = b5; + outWords[6] = b6; + outWords[7] = b7; + outWords[8] = b8; + outWords[9] = b9; + outWords[10] = b10; + outWords[11] = b11; + outWords[12] = b12; + outWords[13] = b13; + outWords[14] = b14; + outWords[15] = b15; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod17 = MOD17; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 33) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + ulong b8 = block[8]; + ulong b9 = block[9]; + ulong b10 = block[10]; + ulong b11 = block[11]; + ulong b12 = block[12]; + ulong b13 = block[13]; + ulong b14 = block[14]; + ulong b15 = block[15]; + + for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2) + { + int dm17 = mod17[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm17 + 1]; + b1 -= kw[dm17 + 2]; + b2 -= kw[dm17 + 3]; + b3 -= kw[dm17 + 4]; + b4 -= kw[dm17 + 5]; + b5 -= kw[dm17 + 6]; + b6 -= kw[dm17 + 7]; + b7 -= kw[dm17 + 8]; + b8 -= kw[dm17 + 9]; + b9 -= kw[dm17 + 10]; + b10 -= kw[dm17 + 11]; + b11 -= kw[dm17 + 12]; + b12 -= kw[dm17 + 13]; + b13 -= kw[dm17 + 14] + t[dm3 + 1]; + b14 -= kw[dm17 + 15] + t[dm3 + 2]; + b15 -= kw[dm17 + 16] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + b15 = XorRotr(b15, ROTATION_7_0, b0); + b0 -= b15; + b11 = XorRotr(b11, ROTATION_7_1, b2); + b2 -= b11; + b13 = XorRotr(b13, ROTATION_7_2, b6); + b6 -= b13; + b9 = XorRotr(b9, ROTATION_7_3, b4); + b4 -= b9; + b1 = XorRotr(b1, ROTATION_7_4, b14); + b14 -= b1; + b5 = XorRotr(b5, ROTATION_7_5, b8); + b8 -= b5; + b3 = XorRotr(b3, ROTATION_7_6, b10); + b10 -= b3; + b7 = XorRotr(b7, ROTATION_7_7, b12); + b12 -= b7; + + b7 = XorRotr(b7, ROTATION_6_0, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_6_1, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_6_2, b4); + b4 -= b3; + b1 = XorRotr(b1, ROTATION_6_3, b6); + b6 -= b1; + b15 = XorRotr(b15, ROTATION_6_4, b12); + b12 -= b15; + b13 = XorRotr(b13, ROTATION_6_5, b14); + b14 -= b13; + b11 = XorRotr(b11, ROTATION_6_6, b8); + b8 -= b11; + b9 = XorRotr(b9, ROTATION_6_7, b10); + b10 -= b9; + + b9 = XorRotr(b9, ROTATION_5_0, b0); + b0 -= b9; + b13 = XorRotr(b13, ROTATION_5_1, b2); + b2 -= b13; + b11 = XorRotr(b11, ROTATION_5_2, b6); + b6 -= b11; + b15 = XorRotr(b15, ROTATION_5_3, b4); + b4 -= b15; + b7 = XorRotr(b7, ROTATION_5_4, b10); + b10 -= b7; + b3 = XorRotr(b3, ROTATION_5_5, b12); + b12 -= b3; + b5 = XorRotr(b5, ROTATION_5_6, b14); + b14 -= b5; + b1 = XorRotr(b1, ROTATION_5_7, b8); + b8 -= b1; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + b9 = XorRotr(b9, ROTATION_4_4, b8); + b8 -= b9; + b11 = XorRotr(b11, ROTATION_4_5, b10); + b10 -= b11; + b13 = XorRotr(b13, ROTATION_4_6, b12); + b12 -= b13; + b15 = XorRotr(b15, ROTATION_4_7, b14); + b14 -= b15; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm17]; + b1 -= kw[dm17 + 1]; + b2 -= kw[dm17 + 2]; + b3 -= kw[dm17 + 3]; + b4 -= kw[dm17 + 4]; + b5 -= kw[dm17 + 5]; + b6 -= kw[dm17 + 6]; + b7 -= kw[dm17 + 7]; + b8 -= kw[dm17 + 8]; + b9 -= kw[dm17 + 9]; + b10 -= kw[dm17 + 10]; + b11 -= kw[dm17 + 11]; + b12 -= kw[dm17 + 12]; + b13 -= kw[dm17 + 13] + t[dm3]; + b14 -= kw[dm17 + 14] + t[dm3 + 1]; + b15 -= kw[dm17 + 15] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b15 = XorRotr(b15, ROTATION_3_0, b0); + b0 -= b15; + b11 = XorRotr(b11, ROTATION_3_1, b2); + b2 -= b11; + b13 = XorRotr(b13, ROTATION_3_2, b6); + b6 -= b13; + b9 = XorRotr(b9, ROTATION_3_3, b4); + b4 -= b9; + b1 = XorRotr(b1, ROTATION_3_4, b14); + b14 -= b1; + b5 = XorRotr(b5, ROTATION_3_5, b8); + b8 -= b5; + b3 = XorRotr(b3, ROTATION_3_6, b10); + b10 -= b3; + b7 = XorRotr(b7, ROTATION_3_7, b12); + b12 -= b7; + + b7 = XorRotr(b7, ROTATION_2_0, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_2_1, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_2_2, b4); + b4 -= b3; + b1 = XorRotr(b1, ROTATION_2_3, b6); + b6 -= b1; + b15 = XorRotr(b15, ROTATION_2_4, b12); + b12 -= b15; + b13 = XorRotr(b13, ROTATION_2_5, b14); + b14 -= b13; + b11 = XorRotr(b11, ROTATION_2_6, b8); + b8 -= b11; + b9 = XorRotr(b9, ROTATION_2_7, b10); + b10 -= b9; + + b9 = XorRotr(b9, ROTATION_1_0, b0); + b0 -= b9; + b13 = XorRotr(b13, ROTATION_1_1, b2); + b2 -= b13; + b11 = XorRotr(b11, ROTATION_1_2, b6); + b6 -= b11; + b15 = XorRotr(b15, ROTATION_1_3, b4); + b4 -= b15; + b7 = XorRotr(b7, ROTATION_1_4, b10); + b10 -= b7; + b3 = XorRotr(b3, ROTATION_1_5, b12); + b12 -= b3; + b5 = XorRotr(b5, ROTATION_1_6, b14); + b14 -= b5; + b1 = XorRotr(b1, ROTATION_1_7, b8); + b8 -= b1; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + b9 = XorRotr(b9, ROTATION_0_4, b8); + b8 -= b9; + b11 = XorRotr(b11, ROTATION_0_5, b10); + b10 -= b11; + b13 = XorRotr(b13, ROTATION_0_6, b12); + b12 -= b13; + b15 = XorRotr(b15, ROTATION_0_7, b14); + b14 -= b15; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5]; + b6 -= kw[6]; + b7 -= kw[7]; + b8 -= kw[8]; + b9 -= kw[9]; + b10 -= kw[10]; + b11 -= kw[11]; + b12 -= kw[12]; + b13 -= kw[13] + t[0]; + b14 -= kw[14] + t[1]; + b15 -= kw[15]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + state[8] = b8; + state[9] = b9; + state[10] = b10; + state[11] = b11; + state[12] = b12; + state[13] = b13; + state[14] = b14; + state[15] = b15; + } + + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/macs/HMac.cs b/crypto/src/crypto/macs/HMac.cs index 7ae1e02b9..460f3c5a0 100644 --- a/crypto/src/crypto/macs/HMac.cs +++ b/crypto/src/crypto/macs/HMac.cs @@ -3,6 +3,7 @@ using System.Collections; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs { @@ -20,6 +21,8 @@ namespace Org.BouncyCastle.Crypto.Macs private readonly IDigest digest; private readonly int digestSize; private readonly int blockLength; + private IMemoable ipadState; + private IMemoable opadState; private readonly byte[] inputPad; private readonly byte[] outputBuf; @@ -68,8 +71,19 @@ namespace Org.BouncyCastle.Crypto.Macs XorPad(inputPad, blockLength, IPAD); XorPad(outputBuf, blockLength, OPAD); - // Initialise the digest + if (digest is IMemoable) + { + opadState = ((IMemoable)digest).Copy(); + + ((IDigest)opadState).BlockUpdate(outputBuf, 0, blockLength); + } + digest.BlockUpdate(inputPad, 0, inputPad.Length); + + if (digest is IMemoable) + { + ipadState = ((IMemoable)digest).Copy(); + } } public virtual int GetMacSize() @@ -90,13 +104,29 @@ namespace Org.BouncyCastle.Crypto.Macs public virtual int DoFinal(byte[] output, int outOff) { digest.DoFinal(outputBuf, blockLength); - digest.BlockUpdate(outputBuf, 0, outputBuf.Length); - int len = digest.DoFinal(output, outOff); - Array.Clear(outputBuf, blockLength, digestSize); - - // Initialise the digest - digest.BlockUpdate(inputPad, 0, inputPad.Length); + if (opadState != null) + { + ((IMemoable)digest).Reset(opadState); + digest.BlockUpdate(outputBuf, blockLength, digest.GetDigestSize()); + } + else + { + digest.BlockUpdate(outputBuf, 0, outputBuf.Length); + } + + int len = digest.DoFinal(output, outOff); + + Array.Clear(outputBuf, blockLength, digestSize); + + if (ipadState != null) + { + ((IMemoable)digest).Reset(ipadState); + } + else + { + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } return len; } 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/crypto/parameters/TweakableBlockCipherParameters.cs b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs new file mode 100644 index 000000000..f75726600 --- /dev/null +++ b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs @@ -0,0 +1,40 @@ +using System; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// <summary> + /// Parameters for tweakable block ciphers. + /// </summary> + public class TweakableBlockCipherParameters + : ICipherParameters + { + private readonly byte[] tweak; + private readonly KeyParameter key; + + public TweakableBlockCipherParameters(KeyParameter key, byte[] tweak) + { + this.key = key; + this.tweak = Arrays.Clone(tweak); + } + + /// <summary> + /// Gets the key. + /// </summary> + /// <value>the key to use, or <code>null</code> to use the current key.</value> + public KeyParameter Key + { + get { return key; } + } + + /// <summary> + /// Gets the tweak value. + /// </summary> + /// <value>The tweak to use, or <code>null</code> to use the current tweak.</value> + public byte[] Tweak + { + get { return tweak; } + } + } +} \ No newline at end of file diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 5eab42bd7..d90bbdd90 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -236,6 +236,30 @@ namespace Org.BouncyCastle.Utilities } } + [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 byte[] Copy(byte[] data, int off, int len) { byte[] result = new byte[len]; diff --git a/crypto/src/util/IMemoable.cs b/crypto/src/util/IMemoable.cs new file mode 100644 index 000000000..befc10fbf --- /dev/null +++ b/crypto/src/util/IMemoable.cs @@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + public interface IMemoable + { + /// <summary> + /// Produce a copy of this object with its configuration and in its current state. + /// </summary> + /// <remarks> + /// The returned object may be used simply to store the state, or may be used as a similar object + /// starting from the copied state. + /// </remarks> + IMemoable Copy(); + + /// <summary> + /// Restore a copied object state into this object. + /// </summary> + /// <remarks> + /// Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset. + /// </remarks> + /// <param name="other">an object originally {@link #copy() copied} from an object of the same type as this instance.</param> + /// <exception cref="ClassCastException">if the provided object is not of the correct type.</exception> + /// <exception cref="MemoableResetException">if the <b>other</b> parameter is in some other way invalid.</exception> + void Reset(IMemoable other); + } + +} + diff --git a/crypto/src/util/MemoableResetException.cs b/crypto/src/util/MemoableResetException.cs new file mode 100644 index 000000000..d9542dab2 --- /dev/null +++ b/crypto/src/util/MemoableResetException.cs @@ -0,0 +1,27 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + /** + * Exception to be thrown on a failure to reset an object implementing Memoable. + * <p> + * The exception extends ClassCastException to enable users to have a single handling case, + * only introducing specific handling of this one if required. + * </p> + */ + public class MemoableResetException + : InvalidCastException + { + /** + * Basic Constructor. + * + * @param msg message to be associated with this exception. + */ + public MemoableResetException(string msg) + : base(msg) + { + } + } + +} + |