From f15655690b37364439d0a24be8b9471910dffe35 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 18 Jul 2017 13:31:06 +1000 Subject: speed ups --- crypto/src/crypto/digests/KeccakDigest.cs | 212 ++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 71 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs index 20aa225b8..f4d913e14 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs @@ -73,7 +73,9 @@ namespace Org.BouncyCastle.Crypto.Digests return keccakRhoOffsets; } - protected byte[] state = new byte[(1600 / 8)]; + private static readonly int STATE_LENGTH = (1600 / 8); + + protected byte[] state = new byte[STATE_LENGTH]; protected byte[] dataQueue = new byte[(1536 / 8)]; protected int rate; protected int bitsInQueue; @@ -377,34 +379,50 @@ namespace Org.BouncyCastle.Crypto.Digests { for (int i = 0; i < (1600 / 64); i++) { - stateAsWords[i] = 0; - int index = i * (64 / 8); - for (int j = 0; j < (64 / 8); j++) - { - stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j)); - } + stateAsWords[i] = BytesToWord(state, i * 8); } } + private static ulong BytesToWord(byte[] state, int off) + { + return ((ulong)state[off + 0]) + | (((ulong)state[off + 1]) << 8) + | (((ulong)state[off + 2]) << 16) + | (((ulong)state[off + 3]) << 24) + | (((ulong)state[off + 4]) << 32) + | (((ulong)state[off + 5]) << 40) + | (((ulong)state[off + 6]) << 48) + | (((ulong)state[off + 7]) << 56); + } + private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) { for (int i = 0; i < (1600 / 64); i++) { - int index = i * (64 / 8); - for (int j = 0; j < (64 / 8); j++) - { - state[index + j] = (byte)(stateAsWords[i] >> (8 * j)); - } + WordToBytes(stateAsWords[i], state, i * 8); } } - private void KeccakPermutation(byte[] state) + private static void WordToBytes(ulong word, byte[] state, int off) { - ulong[] longState = new ulong[state.Length / 8]; + state[off + 0] = (byte)(word); + state[off + 1] = (byte)(word >> (8 * 1)); + state[off + 2] = (byte)(word >> (8 * 2)); + state[off + 3] = (byte)(word >> (8 * 3)); + state[off + 4] = (byte)(word >> (8 * 4)); + state[off + 5] = (byte)(word >> (8 * 5)); + state[off + 6] = (byte)(word >> (8 * 6)); + state[off + 7] = (byte)(word >> (8 * 7)); + } + private ulong[] longState = new ulong[STATE_LENGTH / 8]; + private ulong[] tempLongState = new ulong[STATE_LENGTH / 8]; + + private void KeccakPermutation(byte[] state) + { FromBytesToWords(longState, state); - KeccakPermutationOnWords(longState); + KeccakPermutationOnWords(longState, tempLongState); FromWordsToBytes(state, longState); } @@ -419,7 +437,12 @@ namespace Org.BouncyCastle.Crypto.Digests KeccakPermutation(state); } - private void KeccakPermutationOnWords(ulong[] state) + private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) + { + KeccakPermutationAfterXor(byteState, data, dataInBytes); + } + + private static void KeccakPermutationOnWords(ulong[] state, ulong[] tempState) { int i; @@ -427,75 +450,127 @@ namespace Org.BouncyCastle.Crypto.Digests { Theta(state); Rho(state); - Pi(state); + Pi(state, tempState); Chi(state); Iota(state, i); } } - ulong[] C = new ulong[5]; + private static ulong leftRotate(ulong v, int r) + { + return (v << r) | (v >> (64 - r)); + } - private void Theta(ulong[] A) + private static void Theta(ulong[] A) { - for (int x = 0; x < 5; x++) - { - C[x] = 0; - for (int y = 0; y < 5; y++) - { - C[x] ^= A[x + 5 * y]; - } - } - for (int x = 0; x < 5; x++) - { - ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5]; - for (int y = 0; y < 5; y++) - { - A[x + 5 * y] ^= dX; - } - } + ulong C0 = A[0 + 0] ^ A[0 + 5] ^ A[0 + 10] ^ A[0 + 15] ^ A[0 + 20]; + ulong C1 = A[1 + 0] ^ A[1 + 5] ^ A[1 + 10] ^ A[1 + 15] ^ A[1 + 20]; + ulong C2 = A[2 + 0] ^ A[2 + 5] ^ A[2 + 10] ^ A[2 + 15] ^ A[2 + 20]; + ulong C3 = A[3 + 0] ^ A[3 + 5] ^ A[3 + 10] ^ A[3 + 15] ^ A[3 + 20]; + ulong C4 = A[4 + 0] ^ A[4 + 5] ^ A[4 + 10] ^ A[4 + 15] ^ A[4 + 20]; + + ulong dX = leftRotate(C1, 1) ^ C4; + + A[0] ^= dX; + A[5] ^= dX; + A[10] ^= dX; + A[15] ^= dX; + A[20] ^= dX; + + dX = leftRotate(C2, 1) ^ C0; + + A[1] ^= dX; + A[6] ^= dX; + A[11] ^= dX; + A[16] ^= dX; + A[21] ^= dX; + + dX = leftRotate(C3, 1) ^ C1; + + A[2] ^= dX; + A[7] ^= dX; + A[12] ^= dX; + A[17] ^= dX; + A[22] ^= dX; + + dX = leftRotate(C4, 1) ^ C2; + + A[3] ^= dX; + A[8] ^= dX; + A[13] ^= dX; + A[18] ^= dX; + A[23] ^= dX; + + dX = leftRotate(C0, 1) ^ C3; + + A[4] ^= dX; + A[9] ^= dX; + A[14] ^= dX; + A[19] ^= dX; + A[24] ^= dX; } - private void Rho(ulong[] A) + private static void Rho(ulong[] A) { - for (int x = 0; x < 5; x++) + // KeccakRhoOffsets[0] == 0 + for (int x = 1; x < 25; x++) { - for (int y = 0; y < 5; y++) - { - int index = x + 5 * y; - A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]); - } + A[x] = leftRotate(A[x], KeccakRhoOffsets[x]); } } - ulong[] tempA = new ulong[25]; - - private void Pi(ulong[] A) + private static void Pi(ulong[] A, ulong[] tempA) { Array.Copy(A, 0, tempA, 0, tempA.Length); - for (int x = 0; x < 5; x++) - { - for (int y = 0; y < 5; y++) - { - A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; - } - } - } + A[0 + 5 * ((0 * 1 + 3 * 0) % 5)] = tempA[0 + 5 * 0]; + A[1 + 5 * ((0 * 1 + 3 * 1) % 5)] = tempA[0 + 5 * 1]; + A[2 + 5 * ((0 * 1 + 3 * 2) % 5)] = tempA[0 + 5 * 2]; + A[3 + 5 * ((0 * 1 + 3 * 3) % 5)] = tempA[0 + 5 * 3]; + A[4 + 5 * ((0 * 1 + 3 * 4) % 5)] = tempA[0 + 5 * 4]; + + A[0 + 5 * ((2 * 1 + 3 * 0) % 5)] = tempA[1 + 5 * 0]; + A[1 + 5 * ((2 * 1 + 3 * 1) % 5)] = tempA[1 + 5 * 1]; + A[2 + 5 * ((2 * 1 + 3 * 2) % 5)] = tempA[1 + 5 * 2]; + A[3 + 5 * ((2 * 1 + 3 * 3) % 5)] = tempA[1 + 5 * 3]; + A[4 + 5 * ((2 * 1 + 3 * 4) % 5)] = tempA[1 + 5 * 4]; + + A[0 + 5 * ((2 * 2 + 3 * 0) % 5)] = tempA[2 + 5 * 0]; + A[1 + 5 * ((2 * 2 + 3 * 1) % 5)] = tempA[2 + 5 * 1]; + A[2 + 5 * ((2 * 2 + 3 * 2) % 5)] = tempA[2 + 5 * 2]; + A[3 + 5 * ((2 * 2 + 3 * 3) % 5)] = tempA[2 + 5 * 3]; + A[4 + 5 * ((2 * 2 + 3 * 4) % 5)] = tempA[2 + 5 * 4]; + + A[0 + 5 * ((2 * 3 + 3 * 0) % 5)] = tempA[3 + 5 * 0]; + A[1 + 5 * ((2 * 3 + 3 * 1) % 5)] = tempA[3 + 5 * 1]; + A[2 + 5 * ((2 * 3 + 3 * 2) % 5)] = tempA[3 + 5 * 2]; + A[3 + 5 * ((2 * 3 + 3 * 3) % 5)] = tempA[3 + 5 * 3]; + A[4 + 5 * ((2 * 3 + 3 * 4) % 5)] = tempA[3 + 5 * 4]; - ulong[] chiC = new ulong[5]; + A[0 + 5 * ((2 * 4 + 3 * 0) % 5)] = tempA[4 + 5 * 0]; + A[1 + 5 * ((2 * 4 + 3 * 1) % 5)] = tempA[4 + 5 * 1]; + A[2 + 5 * ((2 * 4 + 3 * 2) % 5)] = tempA[4 + 5 * 2]; + A[3 + 5 * ((2 * 4 + 3 * 3) % 5)] = tempA[4 + 5 * 3]; + A[4 + 5 * ((2 * 4 + 3 * 4) % 5)] = tempA[4 + 5 * 4]; + } - private void Chi(ulong[] A) + private static void Chi(ulong[] A) { - for (int y = 0; y < 5; y++) + ulong chiC0, chiC1, chiC2, chiC3, chiC4; + + for (int yBy5 = 0; yBy5 < 25; yBy5 += 5) { - for (int x = 0; x < 5; x++) - { - chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); - } - for (int x = 0; x < 5; x++) - { - A[x + 5 * y] = chiC[x]; - } + chiC0 = A[0 + yBy5] ^ ((~A[(((0 + 1) % 5) + yBy5)]) & A[(((0 + 2) % 5) + yBy5)]); + chiC1 = A[1 + yBy5] ^ ((~A[(((1 + 1) % 5) + yBy5)]) & A[(((1 + 2) % 5) + yBy5)]); + chiC2 = A[2 + yBy5] ^ ((~A[(((2 + 1) % 5) + yBy5)]) & A[(((2 + 2) % 5) + yBy5)]); + chiC3 = A[3 + yBy5] ^ ((~A[(((3 + 1) % 5) + yBy5)]) & A[(((3 + 2) % 5) + yBy5)]); + chiC4 = A[4 + yBy5] ^ ((~A[(((4 + 1) % 5) + yBy5)]) & A[(((4 + 2) % 5) + yBy5)]); + + A[0 + yBy5] = chiC0; + A[1 + yBy5] = chiC1; + A[2 + yBy5] = chiC2; + A[3 + yBy5] = chiC3; + A[4 + yBy5] = chiC4; } } @@ -504,17 +579,12 @@ namespace Org.BouncyCastle.Crypto.Digests A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; } - private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) - { - KeccakPermutationAfterXor(byteState, data, dataInBytes); - } - - private void KeccakExtract1024bits(byte[] byteState, byte[] data) + private static void KeccakExtract1024bits(byte[] byteState, byte[] data) { Array.Copy(byteState, 0, data, 0, 128); } - private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) + private static void KeccakExtract(byte[] byteState, byte[] data, int laneCount) { Array.Copy(byteState, 0, data, 0, laneCount * 8); } -- cgit 1.5.1 From e32961e6fa9ec43c9d4baaa0d906846b0a3ac2cc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 19 Jul 2017 17:13:40 +0700 Subject: Fix GetValidSeconds for multi-sigs (port from Java version) --- crypto/src/openpgp/PgpPublicKey.cs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs index 904e29913..fc125e8c8 100644 --- a/crypto/src/openpgp/PgpPublicKey.cs +++ b/crypto/src/openpgp/PgpPublicKey.cs @@ -374,26 +374,37 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return 0; } - private long GetExpirationTimeFromSig( - bool selfSigned, - int signatureType) + private long GetExpirationTimeFromSig(bool selfSigned, int signatureType) { + long expiryTime = -1; + long lastDate = -1; + foreach (PgpSignature sig in GetSignaturesOfType(signatureType)) { - if (!selfSigned || sig.KeyId == KeyId) - { - PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets(); + if (selfSigned && sig.KeyId != this.KeyId) + continue; + + PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets(); + if (hashed == null) + continue; - if (hashed != null) + long current = hashed.GetKeyExpirationTime(); + + if (sig.KeyId == this.KeyId) + { + if (sig.CreationTime.Ticks > lastDate) { - return hashed.GetKeyExpirationTime(); + lastDate = sig.CreationTime.Ticks; + expiryTime = current; } - - return 0; + } + else if (current == 0 || current > expiryTime) + { + expiryTime = current; } } - return -1; + return expiryTime; } /// The keyId associated with the public key. -- cgit 1.5.1 From 22af70a2b1f47c4a2d27e09126d17e2db225ba24 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 19 Jul 2017 18:15:23 +0700 Subject: Add a few method variations to Pack class --- crypto/src/crypto/util/Pack.cs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'crypto/src') diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs index ebe5b7af1..1b94fee0e 100644 --- a/crypto/src/crypto/util/Pack.cs +++ b/crypto/src/crypto/util/Pack.cs @@ -285,6 +285,31 @@ namespace Org.BouncyCastle.Crypto.Utilities UInt32_To_LE((uint)(n >> 32), bs, off + 4); } + internal static byte[] UInt64_To_LE(ulong[] ns) + { + byte[] bs = new byte[8 * ns.Length]; + UInt64_To_LE(ns, bs, 0); + return bs; + } + + internal static void UInt64_To_LE(ulong[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.Length; ++i) + { + UInt64_To_LE(ns[i], bs, off); + off += 8; + } + } + + internal static void UInt64_To_LE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) + { + for (int i = 0; i < nsLen; ++i) + { + UInt64_To_LE(ns[nsOff + i], bs, bsOff); + bsOff += 8; + } + } + internal static ulong LE_To_UInt64(byte[] bs) { uint lo = LE_To_UInt32(bs); @@ -307,5 +332,14 @@ namespace Org.BouncyCastle.Crypto.Utilities off += 8; } } + + internal static void LE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen) + { + for (int i = 0; i < nsLen; ++i) + { + ns[nsOff + i] = LE_To_UInt64(bs, bsOff); + bsOff += 8; + } + } } } -- cgit 1.5.1 From 90ab127a3ca698ef9c18ba83919a687ca840cb4d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 19 Jul 2017 18:16:13 +0700 Subject: Use Pack methods for byte <-> ulong conversions --- crypto/src/crypto/digests/KeccakDigest.cs | 45 +++---------------------------- 1 file changed, 3 insertions(+), 42 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs index f4d913e14..57f3d9bc5 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs @@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests @@ -375,56 +376,16 @@ namespace Org.BouncyCastle.Crypto.Digests } } - private static void FromBytesToWords(ulong[] stateAsWords, byte[] state) - { - for (int i = 0; i < (1600 / 64); i++) - { - stateAsWords[i] = BytesToWord(state, i * 8); - } - } - - private static ulong BytesToWord(byte[] state, int off) - { - return ((ulong)state[off + 0]) - | (((ulong)state[off + 1]) << 8) - | (((ulong)state[off + 2]) << 16) - | (((ulong)state[off + 3]) << 24) - | (((ulong)state[off + 4]) << 32) - | (((ulong)state[off + 5]) << 40) - | (((ulong)state[off + 6]) << 48) - | (((ulong)state[off + 7]) << 56); - } - - private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) - { - for (int i = 0; i < (1600 / 64); i++) - { - WordToBytes(stateAsWords[i], state, i * 8); - } - } - - private static void WordToBytes(ulong word, byte[] state, int off) - { - state[off + 0] = (byte)(word); - state[off + 1] = (byte)(word >> (8 * 1)); - state[off + 2] = (byte)(word >> (8 * 2)); - state[off + 3] = (byte)(word >> (8 * 3)); - state[off + 4] = (byte)(word >> (8 * 4)); - state[off + 5] = (byte)(word >> (8 * 5)); - state[off + 6] = (byte)(word >> (8 * 6)); - state[off + 7] = (byte)(word >> (8 * 7)); - } - private ulong[] longState = new ulong[STATE_LENGTH / 8]; private ulong[] tempLongState = new ulong[STATE_LENGTH / 8]; private void KeccakPermutation(byte[] state) { - FromBytesToWords(longState, state); + Pack.LE_To_UInt64(state, 0, longState); KeccakPermutationOnWords(longState, tempLongState); - FromWordsToBytes(state, longState); + Pack.UInt64_To_LE(longState, state, 0); } private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) -- cgit 1.5.1 From 53c509a269ae9571b75e106992a1eb08bbff8a1f Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 19 Jul 2017 20:01:36 +0700 Subject: Keccak performance - avoid temp copying --- crypto/src/crypto/digests/KeccakDigest.cs | 72 ++++++++++++++----------------- 1 file changed, 32 insertions(+), 40 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs index 57f3d9bc5..37cc6dc41 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs @@ -76,6 +76,7 @@ namespace Org.BouncyCastle.Crypto.Digests private static readonly int STATE_LENGTH = (1600 / 8); + private ulong[] longState = new ulong[STATE_LENGTH / 8]; protected byte[] state = new byte[STATE_LENGTH]; protected byte[] dataQueue = new byte[(1536 / 8)]; protected int rate; @@ -376,14 +377,11 @@ namespace Org.BouncyCastle.Crypto.Digests } } - private ulong[] longState = new ulong[STATE_LENGTH / 8]; - private ulong[] tempLongState = new ulong[STATE_LENGTH / 8]; - private void KeccakPermutation(byte[] state) { Pack.LE_To_UInt64(state, 0, longState); - KeccakPermutationOnWords(longState, tempLongState); + KeccakPermutationOnWords(longState); Pack.UInt64_To_LE(longState, state, 0); } @@ -403,7 +401,7 @@ namespace Org.BouncyCastle.Crypto.Digests KeccakPermutationAfterXor(byteState, data, dataInBytes); } - private static void KeccakPermutationOnWords(ulong[] state, ulong[] tempState) + private static void KeccakPermutationOnWords(ulong[] state) { int i; @@ -411,7 +409,7 @@ namespace Org.BouncyCastle.Crypto.Digests { Theta(state); Rho(state); - Pi(state, tempState); + Pi(state); Chi(state); Iota(state, i); } @@ -480,39 +478,33 @@ namespace Org.BouncyCastle.Crypto.Digests } } - private static void Pi(ulong[] A, ulong[] tempA) - { - Array.Copy(A, 0, tempA, 0, tempA.Length); - - A[0 + 5 * ((0 * 1 + 3 * 0) % 5)] = tempA[0 + 5 * 0]; - A[1 + 5 * ((0 * 1 + 3 * 1) % 5)] = tempA[0 + 5 * 1]; - A[2 + 5 * ((0 * 1 + 3 * 2) % 5)] = tempA[0 + 5 * 2]; - A[3 + 5 * ((0 * 1 + 3 * 3) % 5)] = tempA[0 + 5 * 3]; - A[4 + 5 * ((0 * 1 + 3 * 4) % 5)] = tempA[0 + 5 * 4]; - - A[0 + 5 * ((2 * 1 + 3 * 0) % 5)] = tempA[1 + 5 * 0]; - A[1 + 5 * ((2 * 1 + 3 * 1) % 5)] = tempA[1 + 5 * 1]; - A[2 + 5 * ((2 * 1 + 3 * 2) % 5)] = tempA[1 + 5 * 2]; - A[3 + 5 * ((2 * 1 + 3 * 3) % 5)] = tempA[1 + 5 * 3]; - A[4 + 5 * ((2 * 1 + 3 * 4) % 5)] = tempA[1 + 5 * 4]; - - A[0 + 5 * ((2 * 2 + 3 * 0) % 5)] = tempA[2 + 5 * 0]; - A[1 + 5 * ((2 * 2 + 3 * 1) % 5)] = tempA[2 + 5 * 1]; - A[2 + 5 * ((2 * 2 + 3 * 2) % 5)] = tempA[2 + 5 * 2]; - A[3 + 5 * ((2 * 2 + 3 * 3) % 5)] = tempA[2 + 5 * 3]; - A[4 + 5 * ((2 * 2 + 3 * 4) % 5)] = tempA[2 + 5 * 4]; - - A[0 + 5 * ((2 * 3 + 3 * 0) % 5)] = tempA[3 + 5 * 0]; - A[1 + 5 * ((2 * 3 + 3 * 1) % 5)] = tempA[3 + 5 * 1]; - A[2 + 5 * ((2 * 3 + 3 * 2) % 5)] = tempA[3 + 5 * 2]; - A[3 + 5 * ((2 * 3 + 3 * 3) % 5)] = tempA[3 + 5 * 3]; - A[4 + 5 * ((2 * 3 + 3 * 4) % 5)] = tempA[3 + 5 * 4]; - - A[0 + 5 * ((2 * 4 + 3 * 0) % 5)] = tempA[4 + 5 * 0]; - A[1 + 5 * ((2 * 4 + 3 * 1) % 5)] = tempA[4 + 5 * 1]; - A[2 + 5 * ((2 * 4 + 3 * 2) % 5)] = tempA[4 + 5 * 2]; - A[3 + 5 * ((2 * 4 + 3 * 3) % 5)] = tempA[4 + 5 * 3]; - A[4 + 5 * ((2 * 4 + 3 * 4) % 5)] = tempA[4 + 5 * 4]; + private static void Pi(ulong[] A) + { + ulong a1 = A[1]; + A[1] = A[6]; + A[6] = A[9]; + A[9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[2]; + A[2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[4]; + A[4] = A[24]; + A[24] = A[21]; + A[21] = A[8]; + A[8] = A[16]; + A[16] = A[5]; + A[5] = A[3]; + A[3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[7]; + A[7] = A[10]; + A[10] = a1; } private static void Chi(ulong[] A) @@ -537,7 +529,7 @@ namespace Org.BouncyCastle.Crypto.Digests private static void Iota(ulong[] A, int indexRound) { - A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; + A[0] ^= KeccakRoundConstants[indexRound]; } private static void KeccakExtract1024bits(byte[] byteState, byte[] data) -- cgit 1.5.1 From 096c4894f62b7f4178ab869ec342e9351bd198dd Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 20 Jul 2017 20:26:00 +0700 Subject: Keccak performance improvements --- crypto/src/crypto/digests/KeccakDigest.cs | 276 +++++++++++------------------- crypto/src/crypto/digests/SHA3Digest.cs | 5 +- crypto/src/crypto/digests/ShakeDigest.cs | 12 +- 3 files changed, 106 insertions(+), 187 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs index 37cc6dc41..8b16e5d3a 100644 --- a/crypto/src/crypto/digests/KeccakDigest.cs +++ b/crypto/src/crypto/digests/KeccakDigest.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; @@ -57,12 +58,11 @@ namespace Org.BouncyCastle.Crypto.Digests int x, y, t, newX, newY; int rhoOffset = 0; - keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset; + keccakRhoOffsets[0] = rhoOffset; x = 1; y = 0; for (t = 1; t < 25; t++) { - //rhoOffset = ((t + 1) * (t + 2) / 2) % 64; rhoOffset = (rhoOffset + t) & 63; keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset; newX = (0 * x + 1 * y) % 5; @@ -76,24 +76,13 @@ namespace Org.BouncyCastle.Crypto.Digests private static readonly int STATE_LENGTH = (1600 / 8); - private ulong[] longState = new ulong[STATE_LENGTH / 8]; - protected byte[] state = new byte[STATE_LENGTH]; - protected byte[] dataQueue = new byte[(1536 / 8)]; + private ulong[] state = new ulong[STATE_LENGTH / 8]; + protected byte[] dataQueue = new byte[1536 / 8]; protected int rate; protected int bitsInQueue; protected int fixedOutputLength; protected bool squeezing; protected int bitsAvailableForSqueezing; - protected byte[] chunk; - protected byte[] oneByte; - - private void ClearDataQueueSection(int off, int len) - { - for (int i = off; i != off + len; i++) - { - dataQueue[i] = 0; - } - } public KeccakDigest() : this(288) @@ -119,8 +108,6 @@ namespace Org.BouncyCastle.Crypto.Digests this.fixedOutputLength = source.fixedOutputLength; this.squeezing = source.squeezing; this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; - this.chunk = Arrays.Clone(source.chunk); - this.oneByte = Arrays.Clone(source.oneByte); } public virtual string AlgorithmName @@ -130,24 +117,22 @@ namespace Org.BouncyCastle.Crypto.Digests public virtual int GetDigestSize() { - return fixedOutputLength / 8; + return fixedOutputLength >> 3; } public virtual void Update(byte input) { - oneByte[0] = input; - - Absorb(oneByte, 0, 8L); + Absorb(new byte[]{ input }, 0, 1); } public virtual void BlockUpdate(byte[] input, int inOff, int len) { - Absorb(input, inOff, len * 8L); + Absorb(input, inOff, len); } public virtual int DoFinal(byte[] output, int outOff) { - Squeeze(output, outOff, fixedOutputLength); + Squeeze(output, outOff, fixedOutputLength >> 3); Reset(); @@ -161,11 +146,10 @@ namespace Org.BouncyCastle.Crypto.Digests { if (partialBits > 0) { - oneByte[0] = partialByte; - Absorb(oneByte, 0, partialBits); + AbsorbBits(partialByte, partialBits); } - Squeeze(output, outOff, fixedOutputLength); + Squeeze(output, outOff, fixedOutputLength >> 3); Reset(); @@ -184,7 +168,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ public virtual int GetByteLength() { - return rate / 8; + return rate >> 3; } private void Init(int bitLength) @@ -192,220 +176,170 @@ namespace Org.BouncyCastle.Crypto.Digests switch (bitLength) { case 128: - InitSponge(1344, 256); - break; case 224: - InitSponge(1152, 448); - break; case 256: - InitSponge(1088, 512); - break; case 288: - InitSponge(1024, 576); - break; case 384: - InitSponge(832, 768); - break; case 512: - InitSponge(576, 1024); + InitSponge(1600 - (bitLength << 1)); break; default: throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength"); } } - private void InitSponge(int rate, int capacity) + private void InitSponge(int rate) { - if (rate + capacity != 1600) - { - throw new InvalidOperationException("rate + capacity != 1600"); - } - if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) - { + if (rate <= 0 || rate >= 1600 || (rate & 63) != 0) throw new InvalidOperationException("invalid rate value"); - } this.rate = rate; - // this is never read, need to check to see why we want to save it - // this.capacity = capacity; - this.fixedOutputLength = 0; - Arrays.Fill(this.state, (byte)0); + Array.Clear(state, 0, state.Length); Arrays.Fill(this.dataQueue, (byte)0); this.bitsInQueue = 0; this.squeezing = false; this.bitsAvailableForSqueezing = 0; - this.fixedOutputLength = capacity / 2; - this.chunk = new byte[rate / 8]; - this.oneByte = new byte[1]; - } - - private void AbsorbQueue() - { - KeccakAbsorb(state, dataQueue, rate / 8); - - bitsInQueue = 0; + this.fixedOutputLength = (1600 - rate) >> 1; } - protected virtual void Absorb(byte[] data, int off, long databitlen) + protected void Absorb(byte[] data, int off, int len) { - long i, j, wholeBlocks; - - if ((bitsInQueue % 8) != 0) - { + if ((bitsInQueue & 7) != 0) throw new InvalidOperationException("attempt to absorb with odd length queue"); - } if (squeezing) - { throw new InvalidOperationException("attempt to absorb while squeezing"); - } - i = 0; - while (i < databitlen) + int bytesInQueue = bitsInQueue >> 3; + int rateBytes = rate >> 3; + + int count = 0; + while (count < len) { - if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) + if (bytesInQueue == 0 && count <= (len - rateBytes)) { - wholeBlocks = (databitlen - i) / rate; - - for (j = 0; j < wholeBlocks; j++) + do { - Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length); - - KeccakAbsorb(state, chunk, chunk.Length); + KeccakAbsorb(data, off + count); + count += rateBytes; } - - i += wholeBlocks * rate; + while (count <= (len - rateBytes)); } else { - int partialBlock = (int)(databitlen - i); - if (partialBlock + bitsInQueue > rate) - { - partialBlock = rate - bitsInQueue; - } - int partialByte = partialBlock % 8; - partialBlock -= partialByte; - Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); + int partialBlock = System.Math.Min(rateBytes - bytesInQueue, len - count); + Array.Copy(data, off + count, dataQueue, bytesInQueue, partialBlock); - bitsInQueue += partialBlock; - i += partialBlock; - if (bitsInQueue == rate) - { - AbsorbQueue(); - } - if (partialByte > 0) + bytesInQueue += partialBlock; + count += partialBlock; + + if (bytesInQueue == rateBytes) { - int mask = (1 << partialByte) - 1; - dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); - bitsInQueue += partialByte; - i += partialByte; + KeccakAbsorb(dataQueue, 0); + bytesInQueue = 0; } } } + + bitsInQueue = bytesInQueue << 3; + } + + protected void AbsorbBits(int data, int bits) + { + if (bits < 1 || bits > 7) + throw new ArgumentException("must be in the range 1 to 7", "bits"); + if ((bitsInQueue & 7) != 0) + throw new InvalidOperationException("attempt to absorb with odd length queue"); + if (squeezing) + throw new InvalidOperationException("attempt to absorb while squeezing"); + + int mask = (1 << bits) - 1; + dataQueue[bitsInQueue >> 3] = (byte)(data & mask); + + // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work + bitsInQueue += bits; } private void PadAndSwitchToSqueezingPhase() { - if (bitsInQueue + 1 == rate) - { - dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); - AbsorbQueue(); - ClearDataQueueSection(0, rate / 8); - } - else - { - ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); - dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); - } - dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8)); - AbsorbQueue(); + Debug.Assert(bitsInQueue < rate); + + dataQueue[bitsInQueue >> 3] |= (byte)(1U << (bitsInQueue & 7)); - if (rate == 1024) + if (++bitsInQueue == rate) { - KeccakExtract1024bits(state, dataQueue); - bitsAvailableForSqueezing = 1024; + KeccakAbsorb(dataQueue, 0); + bitsInQueue = 0; } - else + { - KeccakExtract(state, dataQueue, rate / 64); - bitsAvailableForSqueezing = rate; + int full = bitsInQueue >> 6, partial = bitsInQueue & 63; + int off = 0; + for (int i = 0; i < full; ++i) + { + state[i] ^= Pack.LE_To_UInt64(dataQueue, off); + off += 8; + } + if (partial > 0) + { + ulong mask = (1UL << partial) - 1UL; + state[full] ^= Pack.LE_To_UInt64(dataQueue, off) & mask; + } + state[(rate - 1) >> 6] ^= (1UL << 63); } + KeccakPermutation(); + KeccakExtract(); + bitsAvailableForSqueezing = rate; + + bitsInQueue = 0; squeezing = true; } - protected virtual void Squeeze(byte[] output, int offset, long outputLength) + protected void Squeeze(byte[] output, int off, int len) { - long i; - int partialBlock; - if (!squeezing) { PadAndSwitchToSqueezingPhase(); } - if ((outputLength % 8) != 0) - { - throw new InvalidOperationException("outputLength not a multiple of 8"); - } - i = 0; + long outputLength = (long)len << 3; + long i = 0; while (i < outputLength) { if (bitsAvailableForSqueezing == 0) { - KeccakPermutation(state); - - if (rate == 1024) - { - KeccakExtract1024bits(state, dataQueue); - bitsAvailableForSqueezing = 1024; - } - else - { - KeccakExtract(state, dataQueue, rate / 64); - bitsAvailableForSqueezing = rate; - } - } - partialBlock = bitsAvailableForSqueezing; - if ((long)partialBlock > outputLength - i) - { - partialBlock = (int)(outputLength - i); + KeccakPermutation(); + KeccakExtract(); + bitsAvailableForSqueezing = rate; } - Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); + int partialBlock = (int)System.Math.Min((long)bitsAvailableForSqueezing, outputLength - i); + Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) >> 3, output, off + (int)(i >> 3), partialBlock >> 3); bitsAvailableForSqueezing -= partialBlock; i += partialBlock; } } - private void KeccakPermutation(byte[] state) - { - Pack.LE_To_UInt64(state, 0, longState); - - KeccakPermutationOnWords(longState); - - Pack.UInt64_To_LE(longState, state, 0); - } - - private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) + private void KeccakAbsorb(byte[] data, int off) { - for (int i = 0; i < dataLengthInBytes; i++) + int count = rate >> 6; + for (int i = 0; i < count; ++i) { - state[i] ^= data[i]; + state[i] ^= Pack.LE_To_UInt64(data, off); + off += 8; } - KeccakPermutation(state); + KeccakPermutation(); } - private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) + private void KeccakExtract() { - KeccakPermutationAfterXor(byteState, data, dataInBytes); + Pack.UInt64_To_LE(state, 0, rate >> 6, dataQueue, 0); } - private static void KeccakPermutationOnWords(ulong[] state) + private void KeccakPermutation() { - int i; - - for (i = 0; i < 24; i++) + for (int i = 0; i < 24; i++) { Theta(state); Rho(state); @@ -417,7 +351,7 @@ namespace Org.BouncyCastle.Crypto.Digests private static ulong leftRotate(ulong v, int r) { - return (v << r) | (v >> (64 - r)); + return (v << r) | (v >> -r); } private static void Theta(ulong[] A) @@ -532,16 +466,6 @@ namespace Org.BouncyCastle.Crypto.Digests A[0] ^= KeccakRoundConstants[indexRound]; } - private static void KeccakExtract1024bits(byte[] byteState, byte[] data) - { - Array.Copy(byteState, 0, data, 0, 128); - } - - private static void KeccakExtract(byte[] byteState, byte[] data, int laneCount) - { - Array.Copy(byteState, 0, data, 0, laneCount * 8); - } - public virtual IMemoable Copy() { return new KeccakDigest(this); @@ -549,9 +473,7 @@ namespace Org.BouncyCastle.Crypto.Digests public virtual void Reset(IMemoable other) { - KeccakDigest d = (KeccakDigest)other; - - CopyIn(d); + CopyIn((KeccakDigest)other); } } } diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs index 890b665cf..4683af5b3 100644 --- a/crypto/src/crypto/digests/SHA3Digest.cs +++ b/crypto/src/crypto/digests/SHA3Digest.cs @@ -50,7 +50,7 @@ namespace Org.BouncyCastle.Crypto.Digests public override int DoFinal(byte[] output, int outOff) { - Absorb(new byte[]{ 0x02 }, 0, 2); + AbsorbBits(0x02, 2); return base.DoFinal(output, outOff); } @@ -69,8 +69,7 @@ namespace Org.BouncyCastle.Crypto.Digests if (finalBits >= 8) { - oneByte[0] = (byte)finalInput; - Absorb(oneByte, 0, 8); + Absorb(new byte[]{ (byte)finalInput }, 0, 1); finalBits -= 8; finalInput >>= 8; } diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs index a7bddccba..13e8838c1 100644 --- a/crypto/src/crypto/digests/ShakeDigest.cs +++ b/crypto/src/crypto/digests/ShakeDigest.cs @@ -64,10 +64,10 @@ namespace Org.BouncyCastle.Crypto.Digests { if (!squeezing) { - Absorb(new byte[] { 0x0F }, 0, 4); + AbsorbBits(0x0F, 4); } - Squeeze(output, outOff, ((long)outLen) * 8); + Squeeze(output, outOff, outLen); return outLen; } @@ -94,19 +94,17 @@ namespace Org.BouncyCastle.Crypto.Digests if (finalBits >= 8) { - oneByte[0] = (byte)finalInput; - Absorb(oneByte, 0, 8); + Absorb(new byte[]{ (byte)finalInput }, 0, 1); finalBits -= 8; finalInput >>= 8; } if (finalBits > 0) { - oneByte[0] = (byte)finalInput; - Absorb(oneByte, 0, finalBits); + AbsorbBits(finalInput, finalBits); } - Squeeze(output, outOff, ((long)outLen) * 8); + Squeeze(output, outOff, outLen); Reset(); -- cgit 1.5.1 From 6c64c1f96c311783146c9da652c64ef4a5254c91 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 28 Jul 2017 12:43:40 +0700 Subject: Can't use MemoryStream.GetBuffer under PORTABLE --- crypto/src/crypto/modes/KCcmBlockCipher.cs | 64 ++++++++++++++---------------- 1 file changed, 30 insertions(+), 34 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs index 1911ba53b..4f7821452 100644 --- a/crypto/src/crypto/modes/KCcmBlockCipher.cs +++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs @@ -1,10 +1,10 @@ -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Utilities; -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Text; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Modes { public class KCcmBlockCipher: IAeadBlockCipher @@ -229,10 +229,8 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff) { - if (input.Length< (inOff + inLen)) - { - throw new DataLengthException("input buffer too short"); - } + Check.DataLength(input, inOff, inLen, "input buffer too short"); + data.Write(input, inOff, inLen); return 0; @@ -240,34 +238,27 @@ namespace Org.BouncyCastle.Crypto.Modes public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff) { - if (input.Length - inOff 0) { - if (forEncryption) - { - ProcessAAD(associatedText.GetBuffer(), 0, (int)associatedText.Length, (int)data.Length); - } - else - { - ProcessAAD(associatedText.GetBuffer(), 0, (int)associatedText.Length, (int)data.Length - macSize); - } +#if PORTABLE + byte[] aad = associatedText.ToArray(); + int aadLen = aad.Length; +#else + byte[] aad = associatedText.GetBuffer(); + int aadLen = (int)associatedText.Length; +#endif + + int dataLen = forEncryption ? (int)data.Length : ((int)data.Length - macSize); + + ProcessAAD(aad, 0, aadLen, dataLen); } if (forEncryption) { - if ((len % engine.GetBlockSize()) != 0) - { - throw new DataLengthException("partial blocks not supported"); - } + Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported"); CalculateMac(input, inOff, len); engine.ProcessBlock(nonce, 0, s, 0); @@ -301,10 +292,7 @@ namespace Org.BouncyCastle.Crypto.Modes } else { - if ((len - macSize) % engine.GetBlockSize() != 0) - { - throw new DataLengthException("partial blocks not supported"); - } + Check.DataLength((len - macSize) % engine.GetBlockSize() != 0, "partial blocks not supported"); engine.ProcessBlock(nonce, 0, s, 0); @@ -397,7 +385,15 @@ namespace Org.BouncyCastle.Crypto.Modes public virtual int DoFinal(byte[] output, int outOff) { - int len = ProcessPacket(data.GetBuffer(), 0, (int)data.Length, output, outOff); +#if PORTABLE + byte[] buf = data.ToArray(); + int bufLen = buf.Length; +#else + byte[] buf = data.GetBuffer(); + int bufLen = (int)data.Length; +#endif + + int len = ProcessPacket(buf, 0, bufLen, output, outOff); Reset(); -- cgit 1.5.1