diff options
author | David Hook <dgh@cryptoworkshop.com> | 2014-05-23 07:49:53 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2014-05-23 07:49:53 +0400 |
commit | e60d4ef8c6627c6b4a9c0bfccba206ab5ed9d9d5 (patch) | |
tree | 695168623f430239c2da79feb7be6b8c722d9daa | |
parent | cb6e7cb081d1db8a3a18bd773f2d81b9b5989c22 (diff) |
added EncodableDigest with constructors supporting the same.
9 files changed, 291 insertions, 7 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java new file mode 100644 index 00000000..d79fece8 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java @@ -0,0 +1,17 @@ +package org.bouncycastle.crypto.digests; + +/** + * Encodable digests allow you to download an encoded copy of their internal state. This is useful for the situation where + * you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the + * internal state of the digest, plus the last few blocks of the message are all that needs to be sent, rather than the + * entire message. + */ +public interface EncodableDigest +{ + /** + * Return an encoded byte array for the digest's internal state + * + * @return an encoding of the digests internal state. + */ + byte[] getEncodedState(); +} diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java index 15f3ebbd..29692bad 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java @@ -2,6 +2,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.Pack; /** * base implementation of MD4 family style digest as outlined in @@ -11,8 +12,9 @@ public abstract class GeneralDigest implements ExtendedDigest, Memoable { private static final int BYTE_LENGTH = 64; - private byte[] xBuf; - private int xBufOff; + + private final byte[] xBuf = new byte[4]; + private int xBufOff; private long byteCount; @@ -21,7 +23,6 @@ public abstract class GeneralDigest */ protected GeneralDigest() { - xBuf = new byte[4]; xBufOff = 0; } @@ -32,11 +33,16 @@ public abstract class GeneralDigest */ protected GeneralDigest(GeneralDigest t) { - xBuf = new byte[t.xBuf.length]; - copyIn(t); } + protected GeneralDigest(byte[] encodedState) + { + System.arraycopy(encodedState, 0, xBuf, 0, xBuf.length); + xBufOff = Pack.bigEndianToInt(encodedState, 4); + byteCount = Pack.bigEndianToLong(encodedState, 8); + } + protected void copyIn(GeneralDigest t) { System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); @@ -129,6 +135,13 @@ public abstract class GeneralDigest } } + protected void populateState(byte[] state) + { + System.arraycopy(xBuf, 0, state, 0, xBufOff); + Pack.intToBigEndian(xBufOff, state, 4); + Pack.longToBigEndian(byteCount, state, 8); + } + public int getByteLength() { return BYTE_LENGTH; diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java index ff2f5ca2..450dda46 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java @@ -11,6 +11,7 @@ import org.bouncycastle.util.Pack; */ public class SHA1Digest extends GeneralDigest + implements EncodableDigest { private static final int DIGEST_LENGTH = 20; @@ -38,6 +39,23 @@ public class SHA1Digest copyIn(t); } + public SHA1Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + + xOff = Pack.bigEndianToInt(encodedState, 36); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 40 + (i * 4)); + } + } + private void copyIn(SHA1Digest t) { H1 = t.H1; @@ -302,6 +320,27 @@ public class SHA1Digest super.copyIn(d); copyIn(d); } + + public byte[] getEncodedState() + { + byte[] state = new byte[40 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(xOff, state, 36); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 40 + (i * 4)); + } + + return state; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java index 16550149..4f2b2842 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java @@ -18,6 +18,7 @@ import org.bouncycastle.util.Pack; */ public class SHA224Digest extends GeneralDigest + implements EncodableDigest { private static final int DIGEST_LENGTH = 28; @@ -62,6 +63,26 @@ public class SHA224Digest xOff = t.xOff; } + public SHA224Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + H6 = Pack.bigEndianToInt(encodedState, 36); + H7 = Pack.bigEndianToInt(encodedState, 40); + H8 = Pack.bigEndianToInt(encodedState, 44); + + xOff = Pack.bigEndianToInt(encodedState, 48); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4)); + } + } + public String getAlgorithmName() { return "SHA-224"; @@ -307,5 +328,29 @@ public class SHA224Digest doCopy(d); } + + public byte[] getEncodedState() + { + byte[] state = new byte[52 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(H6, state, 36); + Pack.intToBigEndian(H7, state, 40); + Pack.intToBigEndian(H8, state, 44); + Pack.intToBigEndian(xOff, state, 48); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 52 + (i * 4)); + } + + return state; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java index e3dd4c37..600d2343 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java @@ -18,6 +18,7 @@ import org.bouncycastle.util.Pack; */ public class SHA256Digest extends GeneralDigest + implements EncodableDigest { private static final int DIGEST_LENGTH = 32; @@ -62,6 +63,27 @@ public class SHA256Digest xOff = t.xOff; } + public SHA256Digest(byte[] encodedState) + { + super(encodedState); + + H1 = Pack.bigEndianToInt(encodedState, 16); + H2 = Pack.bigEndianToInt(encodedState, 20); + H3 = Pack.bigEndianToInt(encodedState, 24); + H4 = Pack.bigEndianToInt(encodedState, 28); + H5 = Pack.bigEndianToInt(encodedState, 32); + H6 = Pack.bigEndianToInt(encodedState, 36); + H7 = Pack.bigEndianToInt(encodedState, 40); + H8 = Pack.bigEndianToInt(encodedState, 44); + + xOff = Pack.bigEndianToInt(encodedState, 48); + for (int i = 0; i != xOff; i++) + { + X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4)); + } + } + + public String getAlgorithmName() { return "SHA-256"; @@ -310,5 +332,29 @@ public class SHA256Digest copyIn(d); } + + public byte[] getEncodedState() + { + byte[] state = new byte[52 + xOff * 4]; + + super.populateState(state); + + Pack.intToBigEndian(H1, state, 16); + Pack.intToBigEndian(H2, state, 20); + Pack.intToBigEndian(H3, state, 24); + Pack.intToBigEndian(H4, state, 28); + Pack.intToBigEndian(H5, state, 32); + Pack.intToBigEndian(H6, state, 36); + Pack.intToBigEndian(H7, state, 40); + Pack.intToBigEndian(H8, state, 44); + Pack.intToBigEndian(xOff, state, 48); + + for (int i = 0; i != xOff; i++) + { + Pack.intToBigEndian(X[i], state, 52 + (i * 4)); + } + + return state; + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 746a308d..be115325 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -107,7 +107,7 @@ public abstract class DigestTest } } - private byte[] toByteArray(String input) + protected byte[] toByteArray(String input) { byte[] bytes = new byte[input.length()]; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SHA1DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SHA1DigestTest.java index 9bcba664..2b002c43 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SHA1DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SHA1DigestTest.java @@ -2,6 +2,7 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.util.encoders.Hex; /** * standard vector test for SHA-1 from "Handbook of Applied Cryptography", page 345. @@ -34,7 +35,51 @@ public class SHA1DigestTest { return new SHA1Digest((SHA1Digest)digest); } - + + public void performTest() + { + super.performTest(); + + // test state encoding; + + byte[] lastV = toByteArray(messages[messages.length - 1]); + byte[] lastDigest = Hex.decode(digests[digests.length - 1]); + + SHA1Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + digest.update(lastV, 0, lastV.length/2); + + // copy the Digest + SHA1Digest copy1 = new SHA1Digest(digest.getEncodedState()); + SHA1Digest copy2 = new SHA1Digest(copy1.getEncodedState()); + + digest.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + + digest.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy1.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + copy1.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy1 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy2.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + copy2.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy2 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + } + public static void main( String[] args) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SHA224DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SHA224DigestTest.java index c352bad0..1f3f6b05 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SHA224DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SHA224DigestTest.java @@ -2,6 +2,7 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA224Digest; +import org.bouncycastle.util.encoders.Hex; /** * standard vector test for SHA-224 from RFC 3874 - only the last three are in @@ -39,6 +40,44 @@ public class SHA224DigestTest super.performTest(); millionATest(million_a_digest); + + // test state encoding; + byte[] lastV = toByteArray(messages[messages.length - 1]); + byte[] lastDigest = Hex.decode(digests[digests.length - 1]); + + SHA224Digest digest = new SHA224Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + digest.update(lastV, 0, lastV.length/2); + + // copy the Digest + SHA224Digest copy1 = new SHA224Digest(digest.getEncodedState()); + SHA224Digest copy2 = new SHA224Digest(copy1.getEncodedState()); + + digest.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + + digest.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy1.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + copy1.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy1 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy2.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + copy2.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy2 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } } protected Digest cloneDigest(Digest digest) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SHA256DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SHA256DigestTest.java index a2f87292..15293968 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SHA256DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SHA256DigestTest.java @@ -2,6 +2,7 @@ package org.bouncycastle.crypto.test; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.util.encoders.Hex; /** * standard vector test for SHA-256 from FIPS Draft 180-2. @@ -40,6 +41,45 @@ public class SHA256DigestTest super.performTest(); millionATest(million_a_digest); + + // test state encoding; + + byte[] lastV = toByteArray(messages[messages.length - 1]); + byte[] lastDigest = Hex.decode(digests[digests.length - 1]); + + SHA256Digest digest = new SHA256Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + digest.update(lastV, 0, lastV.length/2); + + // copy the Digest + SHA256Digest copy1 = new SHA256Digest(digest.getEncodedState()); + SHA256Digest copy2 = new SHA256Digest(copy1.getEncodedState()); + + digest.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + + digest.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy1.update(lastV, lastV.length/2, lastV.length - lastV.length/2); + copy1.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy1 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } + + copy2.update(lastV, lastV.length / 2, lastV.length - lastV.length / 2); + copy2.doFinal(resBuf, 0); + + if (!areEqual(lastDigest, resBuf)) + { + fail("failing state copy2 vector test", digests[digests.length - 1], new String(Hex.encode(resBuf))); + } } protected Digest cloneDigest(Digest digest) |