Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2014-05-12 15:24:20 +0400
committerDavid Hook <dgh@cryptoworkshop.com>2014-05-12 15:24:20 +0400
commit3c5d2f379d29a802903cb36b22eb05470cdbb5b9 (patch)
tree1e8ee6305aee136d2a065ee2e07690d6815f38f4 /core/src
parent5cfa84e6052ca461c54421dd20cdded8199f6c8e (diff)
first cut at "skipping cipher".
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/SkippingCipher.java16
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java13
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java64
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/ChaChaTest.java97
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/Salsa20Test.java97
5 files changed, 286 insertions, 1 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/SkippingCipher.java b/core/src/main/java/org/bouncycastle/crypto/SkippingCipher.java
new file mode 100644
index 00000000..8f137bd2
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/SkippingCipher.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Ciphers producing a keystream which can be moved around
+ */
+public interface SkippingCipher
+{
+ /**
+ * Skip numberOfBlocks forwards, or backwards. If the cipher is a streamcipher a block
+ * size of 1 is assumed.
+ *
+ * @param numberOfBlocks the number of blocks to skip (positive forward, negative backwards).
+ * @return the number of blocks actually skipped.
+ */
+ long skip(long numberOfBlocks);
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
index 2ab53d8f..81b34dfa 100644
--- a/core/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
@@ -37,6 +37,19 @@ public class ChaChaEngine extends Salsa20Engine
}
}
+ protected boolean isCounterAtZero()
+ {
+ return engineState[12] == 0 && engineState[13] == 0;
+ }
+
+ protected void retreatCounter()
+ {
+ if (--engineState[12] == Integer.MIN_VALUE)
+ {
+ --engineState[13];
+ }
+ }
+
protected void resetCounter()
{
engineState[12] = engineState[13] = 0;
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
index 3f20ff31..b4e89900 100644
--- a/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
@@ -4,6 +4,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.MaxBytesExceededException;
import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.SkippingCipher;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -14,7 +15,7 @@ import org.bouncycastle.util.Strings;
* Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
*/
public class Salsa20Engine
- implements StreamCipher
+ implements StreamCipher, SkippingCipher
{
public final static int DEFAULT_ROUNDS = 20;
@@ -162,6 +163,19 @@ public class Salsa20Engine
}
}
+ protected boolean isCounterAtZero()
+ {
+ return engineState[8] == 0 && engineState[9] == 0;
+ }
+
+ protected void retreatCounter()
+ {
+ if (--engineState[8] == Integer.MIN_VALUE)
+ {
+ --engineState[9];
+ }
+ }
+
public void processBytes(
byte[] in,
int inOff,
@@ -202,6 +216,54 @@ public class Salsa20Engine
}
}
+ public long skip(long numberOfBlocks)
+ {
+ if (numberOfBlocks >= 0)
+ {
+ for (long i = 0; i < numberOfBlocks; i++)
+ {
+ if (index == 0)
+ {
+ if (numberOfBlocks - i < 64)
+ {
+ generateKeyStream(keyStream);
+ }
+
+ advanceCounter();
+ }
+
+ index = (index + 1) & 63;
+ }
+ }
+ else
+ {
+ for (long i = 0; i > numberOfBlocks; i--)
+ {
+ index = (index - 1) & 63;
+
+ if (index == 0)
+ {
+ retreatCounter();
+ if (i - numberOfBlocks < 63)
+ {
+ if (isCounterAtZero())
+ {
+ generateKeyStream(keyStream);
+ }
+ else
+ {
+ retreatCounter();
+ generateKeyStream(keyStream);
+ advanceCounter();
+ }
+ }
+ }
+ }
+ }
+
+ return numberOfBlocks;
+ }
+
public void reset()
{
index = 0;
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ChaChaTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ChaChaTest.java
index 67fbdfc3..e5b3967b 100644
--- a/core/src/test/java/org/bouncycastle/crypto/test/ChaChaTest.java
+++ b/core/src/test/java/org/bouncycastle/crypto/test/ChaChaTest.java
@@ -1,5 +1,7 @@
package org.bouncycastle.crypto.test;
+import java.security.SecureRandom;
+
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.engines.ChaChaEngine;
@@ -156,6 +158,7 @@ public class ChaChaTest
chachaTest2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
set6v1_0, set6v1_65472, set6v1_65536);
reinitBug();
+ skipTest();
}
private void chachaTest1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
@@ -265,6 +268,100 @@ public class ChaChaTest
}
}
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void skipTest()
+ {
+ SecureRandom rand = new SecureRandom();
+ byte[] plain = new byte[5000];
+ byte[] cipher = new byte[5000];
+
+ rand.nextBytes(plain);
+
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ ChaChaEngine engine = new ChaChaEngine();
+
+ engine.init(true, params);
+
+ engine.processBytes(plain, 0, plain.length, cipher, 0);
+
+ byte[] fragment = new byte[20];
+
+ engine.init(true, params);
+
+ engine.skip(10);
+
+ engine.processBytes(plain, 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 10, fragment, 0))
+ {
+ fail("skip forward 10 failed");
+ }
+
+ engine.skip(1000);
+
+ engine.processBytes(plain, 1010 + fragment.length, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + fragment.length, fragment, 0))
+ {
+ fail("skip forward 1000 failed");
+ }
+
+ engine.skip(-10);
+
+ engine.processBytes(plain, 1010 + 2 * fragment.length - 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + 2 * fragment.length - 10, fragment, 0))
+ {
+ fail("skip back 10 failed");
+ }
+
+ engine.skip(-1000);
+
+ engine.processBytes(plain, 60, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 60, fragment, 0))
+ {
+ fail("skip back 1000 failed");
+ }
+
+ engine.reset();
+
+ for (int i = 0; i != 1000; i++)
+ {
+ engine.skip(i);
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip forward i failed: " + i);
+ }
+
+ engine.skip(-fragment.length);
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip back i failed: " + i);
+ }
+
+ engine.reset();
+ }
+ }
+
public static void main(
String[] args)
{
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Salsa20Test.java b/core/src/test/java/org/bouncycastle/crypto/test/Salsa20Test.java
index 8cd35cc9..8e1de2c9 100644
--- a/core/src/test/java/org/bouncycastle/crypto/test/Salsa20Test.java
+++ b/core/src/test/java/org/bouncycastle/crypto/test/Salsa20Test.java
@@ -1,5 +1,7 @@
package org.bouncycastle.crypto.test;
+import java.security.SecureRandom;
+
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.engines.Salsa20Engine;
@@ -153,6 +155,7 @@ public class Salsa20Test
salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
set6v1_0, set6v1_65472, set6v1_65536);
reinitBug();
+ skipTest();
}
private void salsa20Test1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
@@ -262,6 +265,100 @@ public class Salsa20Test
}
}
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void skipTest()
+ {
+ SecureRandom rand = new SecureRandom();
+ byte[] plain = new byte[5000];
+ byte[] cipher = new byte[5000];
+
+ rand.nextBytes(plain);
+
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ Salsa20Engine engine = new Salsa20Engine();
+
+ engine.init(true, params);
+
+ engine.processBytes(plain, 0, plain.length, cipher, 0);
+
+ byte[] fragment = new byte[20];
+
+ engine.init(true, params);
+
+ engine.skip(10);
+
+ engine.processBytes(plain, 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 10, fragment, 0))
+ {
+ fail("skip forward 10 failed");
+ }
+
+ engine.skip(1000);
+
+ engine.processBytes(plain, 1010 + fragment.length, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + fragment.length, fragment, 0))
+ {
+ fail("skip forward 1000 failed");
+ }
+
+ engine.skip(-10);
+
+ engine.processBytes(plain, 1010 + 2 * fragment.length - 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + 2 * fragment.length - 10, fragment, 0))
+ {
+ fail("skip back 10 failed");
+ }
+
+ engine.skip(-1000);
+
+ engine.processBytes(plain, 60, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 60, fragment, 0))
+ {
+ fail("skip back 1000 failed");
+ }
+
+ engine.reset();
+
+ for (int i = 0; i != 1000; i++)
+ {
+ engine.skip(i);
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip forward i failed: " + i);
+ }
+
+ engine.skip(-fragment.length);
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip back i failed: " + i);
+ }
+
+ engine.reset();
+ }
+ }
+
public static void main(
String[] args)
{