diff --git a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Digest.java b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Digest.java
index 62362d1d62d..6733d71d80a 100644
--- a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Digest.java
+++ b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Digest.java
@@ -18,6 +18,8 @@
package org.tron.common.crypto.cryptohash;
+import java.security.DigestException;
+
/**
* Copyright (c) 2007-2010 Project RNRT SAPHIR
*
@@ -57,9 +59,12 @@ public interface Digest {
/**
* Insert some more bytes.
*
+ *
Invalid input is rejected before consuming any bytes, preserving the current hash state.
+ *
* @param inbuf the data buffer
* @param off the data offset in {@code inbuf}
* @param len the data length (in bytes)
+ * @throws IllegalArgumentException if the input buffer is null or the input range is invalid
*/
void update(byte[] inbuf, int off, int len);
@@ -82,17 +87,17 @@ public interface Digest {
/**
* Finalize the current hash computation and store the hash value in the provided output buffer.
- * The {@code len} parameter contains the maximum number of bytes that should be written; no more
- * bytes than the natural hash function output length will be produced. If {@code len} is smaller
- * than the natural hash output length, the hash output is truncated to its first {@code len}
- * bytes. The object is resetted.
+ * The {@code len} parameter contains the number of bytes available for the digest. The complete
+ * digest is written and the object is reset on success. Partial digests are not returned.
*
* @param outbuf the output buffer
* @param off the output offset within {@code outbuf}
- * @param len the requested hash output length (in bytes)
+ * @param len the available output length (in bytes)
* @return the number of bytes actually written in {@code outbuf}
+ * @throws DigestException if {@code len} is smaller than the digest length
+ * @throws IllegalArgumentException if the output buffer is null or the output range is invalid
*/
- int digest(byte[] outbuf, int off, int len);
+ int digest(byte[] outbuf, int off, int len) throws DigestException;
/**
* Get the natural hash function output length (in bytes).
diff --git a/crypto/src/main/java/org/tron/common/crypto/cryptohash/DigestEngine.java b/crypto/src/main/java/org/tron/common/crypto/cryptohash/DigestEngine.java
index c7d013a0804..b13e1d14ea2 100644
--- a/crypto/src/main/java/org/tron/common/crypto/cryptohash/DigestEngine.java
+++ b/crypto/src/main/java/org/tron/common/crypto/cryptohash/DigestEngine.java
@@ -18,12 +18,13 @@
package org.tron.common.crypto.cryptohash;
+import java.security.DigestException;
import java.security.MessageDigest;
public abstract class DigestEngine extends MessageDigest implements Digest {
private int digestLen, blockLen, inputLen;
- private byte[] inputBuf, outputBuf;
+ private byte[] inputBuf;
private long blockCount;
/**
@@ -35,15 +36,18 @@ public DigestEngine(String alg) {
digestLen = engineGetDigestLength();
blockLen = getInternalBlockLength();
inputBuf = new byte[blockLen];
- outputBuf = new byte[digestLen];
inputLen = 0;
blockCount = 0;
}
/**
- * Reset the hash algorithm state.
+ * Reset the buffered input state. Subclasses must call this method and reset their algorithm
+ * state.
*/
- protected abstract void engineReset();
+ protected void engineReset() {
+ inputLen = 0;
+ blockCount = 0;
+ }
/**
* Process one block of data.
@@ -72,7 +76,6 @@ public DigestEngine(String alg) {
private void adjustDigestLen() {
if (digestLen == 0) {
digestLen = engineGetDigestLength();
- outputBuf = new byte[digestLen];
}
}
@@ -82,7 +85,8 @@ private void adjustDigestLen() {
public byte[] digest() {
adjustDigestLen();
byte[] result = new byte[digestLen];
- digest(result, 0, digestLen);
+ doPadding(result, 0);
+ reset();
return result;
}
@@ -97,18 +101,25 @@ public byte[] digest(byte[] input) {
/**
* @see Digest
*/
- public int digest(byte[] buf, int offset, int len) {
+ public int digest(byte[] buf, int offset, int len) throws DigestException {
+ if (buf == null) {
+ throw new IllegalArgumentException("Output buffer is null");
+ }
+ if (offset < 0 || len < 0 || offset > buf.length || len > buf.length - offset) {
+ throw new IllegalArgumentException("Invalid output buffer range");
+ }
adjustDigestLen();
- if (len >= digestLen) {
- doPadding(buf, offset);
- reset();
- return digestLen;
- } else {
- doPadding(outputBuf, 0);
- System.arraycopy(outputBuf, 0, buf, offset, len);
- reset();
- return len;
+ if (len < digestLen) {
+ throw new DigestException("Partial digests are not returned");
}
+ doPadding(buf, offset);
+ reset();
+ return digestLen;
+ }
+
+ @Override
+ protected int engineDigest(byte[] buf, int offset, int len) throws DigestException {
+ return digest(buf, offset, len);
}
/**
@@ -143,6 +154,12 @@ public void update(byte[] input) {
* @see Digest
*/
public void update(byte[] input, int offset, int len) {
+ if (input == null) {
+ throw new IllegalArgumentException("Input buffer is null");
+ }
+ if (offset < 0 || len < 0 || offset > input.length || len > input.length - offset) {
+ throw new IllegalArgumentException("Invalid input buffer range");
+ }
while (len > 0) {
int copyLen = blockLen - inputLen;
if (copyLen > len) {
@@ -222,8 +239,6 @@ protected Digest copyState(DigestEngine dest) {
inputBuf.length);
adjustDigestLen();
dest.adjustDigestLen();
- System.arraycopy(outputBuf, 0, dest.outputBuf, 0,
- outputBuf.length);
return dest;
}
}
diff --git a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak256.java b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak256.java
index 83c0577c789..7ac282e84b5 100644
--- a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak256.java
+++ b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak256.java
@@ -37,14 +37,16 @@ public int engineGetDigestLength() {
@Override
protected byte[] engineDigest() {
- return null;
+ return digest();
}
@Override
- protected void engineUpdate(byte arg0) {
+ protected void engineUpdate(byte input) {
+ update(input);
}
@Override
- protected void engineUpdate(byte[] arg0, int arg1, int arg2) {
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ update(input, offset, len);
}
}
diff --git a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak512.java b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak512.java
index 8283180370e..3846012ec46 100644
--- a/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak512.java
+++ b/crypto/src/main/java/org/tron/common/crypto/cryptohash/Keccak512.java
@@ -37,14 +37,16 @@ public int engineGetDigestLength() {
@Override
protected byte[] engineDigest() {
- return null;
+ return digest();
}
@Override
protected void engineUpdate(byte input) {
+ update(input);
}
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
+ update(input, offset, len);
}
}
diff --git a/crypto/src/main/java/org/tron/common/crypto/cryptohash/KeccakCore.java b/crypto/src/main/java/org/tron/common/crypto/cryptohash/KeccakCore.java
index 8e1bc558096..c8919f64904 100644
--- a/crypto/src/main/java/org/tron/common/crypto/cryptohash/KeccakCore.java
+++ b/crypto/src/main/java/org/tron/common/crypto/cryptohash/KeccakCore.java
@@ -79,6 +79,7 @@ private static long decodeLELong(byte[] buf, int off) {
}
protected void engineReset() {
+ super.engineReset();
doReset();
}
diff --git a/framework/src/test/java/org/tron/common/crypto/cryptohash/KeccakTest.java b/framework/src/test/java/org/tron/common/crypto/cryptohash/KeccakTest.java
new file mode 100644
index 00000000000..c3d1a96ba2e
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/crypto/cryptohash/KeccakTest.java
@@ -0,0 +1,357 @@
+package org.tron.common.crypto.cryptohash;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Collection;
+import org.bouncycastle.crypto.digests.KeccakDigest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.tron.common.crypto.jce.TronCastleProvider;
+
+@RunWith(Parameterized.class)
+public class KeccakTest {
+
+ private final int bits;
+
+ public KeccakTest(int bits) {
+ this.bits = bits;
+ }
+
+ @Parameterized.Parameters(name = "Keccak{0}")
+ public static Collection