Cache MessageDigests (#1984)

Fetching the hash from the java security providers is often longer than
the hashing itself, so we should cache results from the providers in a
reusable fashion.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/2015/head
Danno Ferrin 4 years ago committed by GitHub
parent 2e540e5241
commit bdf31634f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      crypto/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java
  2. 39
      crypto/src/main/java/org/hyperledger/besu/crypto/Hash.java
  3. 28
      crypto/src/test/java/org/hyperledger/besu/crypto/HashTest.java

@ -21,7 +21,7 @@ import org.bouncycastle.jcajce.provider.digest.BCMessageDigest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
public class Blake2bfMessageDigest extends BCMessageDigest {
public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable {
public Blake2bfMessageDigest() {
super(new Blake2bfDigest());

@ -16,7 +16,9 @@ package org.hyperledger.besu.crypto;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
@ -25,24 +27,41 @@ public abstract class Hash {
private Hash() {}
public static final String KECCAK256_ALG = "KECCAK-256";
private static final String SHA256_ALG = "SHA-256";
private static final String RIPEMD160 = "RIPEMD160";
private static final String RIPEMD160_ALG = "RIPEMD160";
private static final String BLAKE2BF_ALG = "BLAKE2BF";
private static final Supplier<MessageDigest> KECCAK256_SUPPLIER =
Suppliers.memoize(() -> messageDigest(KECCAK256_ALG));
private static final Supplier<MessageDigest> SHA256_SUPPLIER =
Suppliers.memoize(() -> messageDigest(SHA256_ALG));
private static final Supplier<MessageDigest> RIPEMD160_SUPPLIER =
Suppliers.memoize(() -> messageDigest(RIPEMD160_ALG));
private static final Supplier<MessageDigest> BLAKE2BF_SUPPLIER =
Suppliers.memoize(() -> messageDigest(BLAKE2BF_ALG));
private static MessageDigest messageDigest(final String algorithm) {
try {
return MessageDigestFactory.create(algorithm);
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**
* Helper method to generate a digest using the provided algorithm.
*
* @param input The input bytes to produce the digest for.
* @param alg The name of the digest algorithm to use.
* @param digestSupplier the digest supplier to use
* @return A digest.
*/
private static byte[] digestUsingAlgorithm(final Bytes input, final String alg) {
private static byte[] digestUsingAlgorithm(
final Bytes input, final Supplier<MessageDigest> digestSupplier) {
try {
final MessageDigest digest = MessageDigestFactory.create(alg);
final MessageDigest digest = (MessageDigest) digestSupplier.get().clone();
input.update(digest);
return digest.digest();
} catch (final NoSuchAlgorithmException e) {
} catch (final CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@ -54,7 +73,7 @@ public abstract class Hash {
* @return A digest.
*/
public static Bytes32 sha256(final Bytes input) {
return Bytes32.wrap(digestUsingAlgorithm(input, SHA256_ALG));
return Bytes32.wrap(digestUsingAlgorithm(input, SHA256_SUPPLIER));
}
/**
@ -64,7 +83,7 @@ public abstract class Hash {
* @return A digest.
*/
public static Bytes32 keccak256(final Bytes input) {
return Bytes32.wrap(digestUsingAlgorithm(input, KECCAK256_ALG));
return Bytes32.wrap(digestUsingAlgorithm(input, KECCAK256_SUPPLIER));
}
/**
@ -74,7 +93,7 @@ public abstract class Hash {
* @return A digest.
*/
public static Bytes ripemd160(final Bytes input) {
return Bytes.wrap(digestUsingAlgorithm(input, RIPEMD160));
return Bytes.wrap(digestUsingAlgorithm(input, RIPEMD160_SUPPLIER));
}
/**
@ -84,6 +103,6 @@ public abstract class Hash {
* @return A digest.
*/
public static Bytes blake2bf(final Bytes input) {
return Bytes.wrap(digestUsingAlgorithm(input, BLAKE2BF_ALG));
return Bytes.wrap(digestUsingAlgorithm(input, BLAKE2BF_SUPPLIER));
}
}

@ -28,6 +28,14 @@ public class HashTest {
private static final String horseKeccak256 =
"c87f65ff3f271bf5dc8643484f66b200109caffe4bf98c4cb393dc35740b28c0";
private static final String cowSha256 =
"beb134754910a4b4790c69ab17d3975221f4c534b70c8d6e82b30c165e8c0c09";
private static final String horseSha256 =
"fd62862b6dc213bee77c2badd6311528253c6cb3107e03c16051aa15584eca1c";
private static final String cowRipemd160 = "7e72ea7f1d397cca847d1393f744536b2ce99693";
private static final String horseRipemd160 = "79a19669bea55f3b2f67038155755fb0a68999ba";
private static final String inputBlake2bf =
"000000016a09e667f2bd8948bb67ae8584caa73b3c6ef372fe94f82ba54ff53a5f1d36f1510e527fade682d19b05688c2b3e6c1f1f83d9abfb41bd6b5be0cd19137e217907060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a191827262524232221202f2e2d2c2b2a292837363534333231303f3e3d3c3b3a3938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000001";
@ -44,6 +52,26 @@ public class HashTest {
assertThat(resultCow).isEqualTo(Bytes.fromHexString(cowKeccak256));
}
/** Validate sha256 hash. */
@Test
public void sha256Hash() {
final Bytes resultHorse = Hash.sha256(Bytes.wrap("horse".getBytes(UTF_8)));
assertThat(resultHorse).isEqualTo(Bytes.fromHexString(horseSha256));
final Bytes resultCow = Hash.sha256(Bytes.wrap("cow".getBytes(UTF_8)));
assertThat(resultCow).isEqualTo(Bytes.fromHexString(cowSha256));
}
/** Validate ripemd160 hash. */
@Test
public void ripemd160Hash() {
final Bytes resultHorse = Hash.ripemd160(Bytes.wrap("horse".getBytes(UTF_8)));
assertThat(resultHorse).isEqualTo(Bytes.fromHexString(horseRipemd160));
final Bytes resultCow = Hash.ripemd160(Bytes.wrap("cow".getBytes(UTF_8)));
assertThat(resultCow).isEqualTo(Bytes.fromHexString(cowRipemd160));
}
/** Validate blake2f compression digest. */
@Test
public void blake2bfCompression() {

Loading…
Cancel
Save