@@ -5,12 +5,10 @@ import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_CODE_SIZE; | |||
import java.util.Arrays; | |||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||
import com.jd.blockchain.crypto.CryptoBytes; | |||
import com.jd.blockchain.crypto.CryptoException; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.HashFunction; | |||
import com.jd.blockchain.crypto.utils.classic.RIPEMD160Utils; | |||
import com.jd.blockchain.utils.security.RipeMD160Utils; | |||
public class RIPEMD160HashFunction implements HashFunction { | |||
@@ -19,7 +17,7 @@ public class RIPEMD160HashFunction implements HashFunction { | |||
private static final int DIGEST_BYTES = 160 / 8; | |||
private static final int DIGEST_LENGTH = ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | |||
RIPEMD160HashFunction() { | |||
} | |||
@@ -30,7 +28,6 @@ public class RIPEMD160HashFunction implements HashFunction { | |||
@Override | |||
public HashDigest hash(byte[] data) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
@@ -39,6 +36,16 @@ public class RIPEMD160HashFunction implements HashFunction { | |||
return new HashDigest(RIPEMD160, digestBytes); | |||
} | |||
@Override | |||
public HashDigest hash(byte[] data, int offset, int len) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
byte[] digestBytes = RIPEMD160Utils.hash(data, offset, len); | |||
return new HashDigest(RIPEMD160, digestBytes); | |||
} | |||
@Override | |||
public boolean verify(HashDigest digest, byte[] data) { | |||
HashDigest hashDigest = hash(data); | |||
@@ -59,5 +66,5 @@ public class RIPEMD160HashFunction implements HashFunction { | |||
throw new CryptoException("digestBytes is invalid!"); | |||
} | |||
} | |||
} |
@@ -5,12 +5,10 @@ import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_CODE_SIZE; | |||
import java.util.Arrays; | |||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||
import com.jd.blockchain.crypto.CryptoBytes; | |||
import com.jd.blockchain.crypto.CryptoException; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.HashFunction; | |||
import com.jd.blockchain.crypto.utils.classic.SHA256Utils; | |||
import com.jd.blockchain.utils.security.ShaUtils; | |||
public class SHA256HashFunction implements HashFunction { | |||
@@ -30,7 +28,6 @@ public class SHA256HashFunction implements HashFunction { | |||
@Override | |||
public HashDigest hash(byte[] data) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
@@ -38,6 +35,16 @@ public class SHA256HashFunction implements HashFunction { | |||
byte[] digestBytes = SHA256Utils.hash(data); | |||
return new HashDigest(SHA256, digestBytes); | |||
} | |||
@Override | |||
public HashDigest hash(byte[] data, int offset, int len) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
byte[] digestBytes = SHA256Utils.hash(data, offset, len); | |||
return new HashDigest(SHA256, digestBytes); | |||
} | |||
@Override | |||
public boolean verify(HashDigest digest, byte[] data) { | |||
@@ -10,16 +10,26 @@ import org.bouncycastle.crypto.digests.RIPEMD160Digest; | |||
*/ | |||
public class RIPEMD160Utils { | |||
// The length of RIPEMD160 output is 20 bytes | |||
private static final int RIPEMD160DIGEST_LENGTH = 160 / 8; | |||
// The length of RIPEMD160 output is 20 bytes | |||
public static final int RIPEMD160DIGEST_LENGTH = 160 / 8; | |||
public static byte[] hash(byte[] data){ | |||
public static byte[] hash(byte[] data) { | |||
byte[] result = new byte[RIPEMD160DIGEST_LENGTH]; | |||
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); | |||
byte[] result = new byte[RIPEMD160DIGEST_LENGTH]; | |||
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); | |||
ripemd160Digest.update(data,0,data.length); | |||
ripemd160Digest.doFinal(result,0); | |||
return result; | |||
} | |||
ripemd160Digest.update(data, 0, data.length); | |||
ripemd160Digest.doFinal(result, 0); | |||
return result; | |||
} | |||
public static byte[] hash(byte[] data, int offset, int len) { | |||
byte[] result = new byte[RIPEMD160DIGEST_LENGTH]; | |||
RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest(); | |||
ripemd160Digest.update(data, offset, len); | |||
ripemd160Digest.doFinal(result, 0); | |||
return result; | |||
} | |||
} |
@@ -11,7 +11,7 @@ import org.bouncycastle.crypto.digests.SHA256Digest; | |||
public class SHA256Utils { | |||
// The length of SHA256 output is 32 bytes | |||
private static final int SHA256DIGEST_LENGTH = 256 / 8; | |||
public static final int SHA256DIGEST_LENGTH = 256 / 8; | |||
public static byte[] hash(byte[] data){ | |||
@@ -22,4 +22,14 @@ public class SHA256Utils { | |||
sha256Digest.doFinal(result,0); | |||
return result; | |||
} | |||
public static byte[] hash(byte[] data, int offset, int len){ | |||
byte[] result = new byte[SHA256DIGEST_LENGTH]; | |||
SHA256Digest sha256Digest = new SHA256Digest(); | |||
sha256Digest.update(data, offset, len); | |||
sha256Digest.doFinal(result,0); | |||
return result; | |||
} | |||
} |
@@ -10,6 +10,14 @@ public interface HashFunction extends CryptoFunction { | |||
*/ | |||
HashDigest hash(byte[] data); | |||
/** | |||
* 计算指定数据的 hash; | |||
* | |||
* @param data | |||
* @return | |||
*/ | |||
HashDigest hash(byte[] data, int offset, int len); | |||
/** | |||
* 校验 hash 摘要与指定的数据是否匹配; | |||
@@ -16,7 +16,7 @@ public class SM3HashFunction implements HashFunction { | |||
private static final int DIGEST_BYTES = 256 / 8; | |||
private static final int DIGEST_LENGTH = CryptoBytes.ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | |||
SM3HashFunction() { | |||
} | |||
@@ -27,7 +27,6 @@ public class SM3HashFunction implements HashFunction { | |||
@Override | |||
public HashDigest hash(byte[] data) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
@@ -36,6 +35,16 @@ public class SM3HashFunction implements HashFunction { | |||
return new HashDigest(SM3, digestBytes); | |||
} | |||
@Override | |||
public HashDigest hash(byte[] data, int offset, int len) { | |||
if (data == null) { | |||
throw new CryptoException("data is null!"); | |||
} | |||
byte[] digestBytes = SM3Utils.hash(data, offset, len); | |||
return new HashDigest(SM3, digestBytes); | |||
} | |||
@Override | |||
public boolean verify(HashDigest digest, byte[] data) { | |||
HashDigest hashDigest = hash(data); | |||
@@ -4,19 +4,30 @@ import org.bouncycastle.crypto.digests.SM3Digest; | |||
public class SM3Utils { | |||
// The length of sm3 output is 32 bytes | |||
private static final int SM3DIGEST_LENGTH = 32; | |||
// The length of sm3 output is 32 bytes | |||
public static final int SM3DIGEST_LENGTH = 32; | |||
public static byte[] hash(byte[] data) { | |||
public static byte[] hash(byte[] data) { | |||
byte[] result = new byte[SM3DIGEST_LENGTH]; | |||
byte[] result = new byte[SM3DIGEST_LENGTH]; | |||
SM3Digest sm3digest = new SM3Digest(); | |||
SM3Digest sm3digest = new SM3Digest(); | |||
sm3digest.update(data, 0, data.length); | |||
sm3digest.doFinal(result, 0); | |||
sm3digest.update(data, 0, data.length); | |||
sm3digest.doFinal(result, 0); | |||
return result; | |||
} | |||
} | |||
return result; | |||
} | |||
public static byte[] hash(byte[] data, int offset, int len) { | |||
byte[] result = new byte[SM3DIGEST_LENGTH]; | |||
SM3Digest sm3digest = new SM3Digest(); | |||
sm3digest.update(data, offset, len); | |||
sm3digest.doFinal(result, 0); | |||
return result; | |||
} | |||
} |
@@ -11,14 +11,14 @@ import com.jd.blockchain.ledger.HashProof; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class HashDegistList implements HashProof { | |||
public class HashDigestList implements HashProof { | |||
private List<HashDigest> proofs = new ArrayList<HashDigest>(); | |||
public HashDegistList() { | |||
public HashDigestList() { | |||
} | |||
public HashDegistList(HashProof proof) { | |||
public HashDigestList(HashProof proof) { | |||
concat(proof); | |||
} | |||
@@ -208,7 +208,7 @@ public class MerkleAccount implements CompositeAccount, HashProvable, MerkleSnap | |||
if (rootProof == null) { | |||
return null; | |||
} | |||
HashDegistList proof = new HashDegistList(rootProof); | |||
HashDigestList proof = new HashDigestList(rootProof); | |||
proof.concat(dataProof); | |||
return proof; | |||
} | |||
@@ -556,6 +556,9 @@ public class MerkleTreeTest { | |||
/** | |||
* 测试从存储重新加载 Merkle 树的正确性; | |||
*/ | |||
/** | |||
* | |||
*/ | |||
@Test | |||
public void testMerkleReload() { | |||
CryptoSetting setting = Mockito.mock(CryptoSetting.class); | |||
@@ -563,7 +566,7 @@ public class MerkleTreeTest { | |||
when(setting.getAutoVerifyHash()).thenReturn(true); | |||
// 保存所有写入的数据节点的 SN-Hash 映射表; | |||
TreeMap<Long, HashDigest> dataNodes = new TreeMap<>(); | |||
TreeMap<Long, HashDigest> expectedDataNodes = new TreeMap<>(); | |||
MerkleNode nd; | |||
// 测试从空的树开始,顺序增加数据节点; | |||
@@ -580,7 +583,7 @@ public class MerkleTreeTest { | |||
for (int i = 0; i < count; i++) { | |||
rand.nextBytes(dataBuf); | |||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | |||
dataNodes.put(sn, nd.getNodeHash()); | |||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||
sn++; | |||
} | |||
mkt.commit(); | |||
@@ -610,6 +613,24 @@ public class MerkleTreeTest { | |||
// 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; | |||
long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; | |||
assertEquals(expectedNodes, storage.getCount()); | |||
//重新加载,判断数据是否正确; | |||
MerkleTree r1_mkt = new MerkleTree(r1_rootHash, setting, keyPrefix, storage, true); | |||
{ | |||
// 验证每一个数据节点都产生了存在性证明; | |||
MerkleProof proof = null; | |||
HashDigest expectedNodeHash = null; | |||
MerkleDataNode reallyDataNode = null; | |||
for (long n = 0; n < maxSN; n++) { | |||
expectedNodeHash = expectedDataNodes.get(n); | |||
reallyDataNode = r1_mkt.getData(n); | |||
assertEquals(expectedNodeHash, reallyDataNode.getNodeHash()); | |||
proof = r1_mkt.getProof(n); | |||
assertNotNull(proof); | |||
assertEquals(expectedNodeHash, proof.getHash(0)); | |||
} | |||
} | |||
} | |||
// 覆盖到每一路分支修改数据节点; | |||
@@ -621,7 +642,7 @@ public class MerkleTreeTest { | |||
rand.nextBytes(dataBuf); | |||
sn = i; | |||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | |||
dataNodes.put(sn, nd.getNodeHash()); | |||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||
} | |||
mkt.commit(); | |||
@@ -658,16 +679,18 @@ public class MerkleTreeTest { | |||
rand.nextBytes(dataBuf); | |||
sn = maxSN + 1 + i; | |||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | |||
dataNodes.put(sn, nd.getNodeHash()); | |||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||
} | |||
mkt.commit(); | |||
// 验证每一个数据节点都产生了存在性证明; | |||
MerkleProof proof = null; | |||
for (Long n : dataNodes.keySet()) { | |||
proof = mkt.getProof(n.longValue()); | |||
assertNotNull(proof); | |||
assertEquals(dataNodes.get(n), proof.getHash(0)); | |||
{ | |||
// 验证每一个数据节点都产生了存在性证明; | |||
MerkleProof proof = null; | |||
for (Long n : expectedDataNodes.keySet()) { | |||
proof = mkt.getProof(n.longValue()); | |||
assertNotNull(proof); | |||
assertEquals(expectedDataNodes.get(n), proof.getHash(0)); | |||
} | |||
} | |||
// 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验; | |||
@@ -700,6 +723,7 @@ public class MerkleTreeTest { | |||
assertEquals(r1_proof1, r1_mkt.getProof(r1_sn1).toString()); | |||
assertEquals(r1_proof2, r1_mkt.getProof(r1_sn2).toString()); | |||
// 从第 2 轮提交的 Merkle 根哈希加载; | |||
// 第 2 轮生成的 Merkle 树是对第 1 轮的数据的全部节点的修改,因此同一个 SN 的节点的证明是不同的; | |||
MerkleTree r2_mkt = new MerkleTree(r2_rootHash, setting, keyPrefix, storage, true); | |||
@@ -730,10 +754,13 @@ public class MerkleTreeTest { | |||
assertEquals(r3_proof3, r3_mkt.getProof(r3_sn3).toString()); | |||
// 验证每一个数据节点都产生了存在性证明; | |||
for (Long n : dataNodes.keySet()) { | |||
proof = r3_mkt.getProof(n.longValue()); | |||
assertNotNull(proof); | |||
assertEquals(dataNodes.get(n), proof.getHash(0)); | |||
{ | |||
MerkleProof proof = null; | |||
for (Long n : expectedDataNodes.keySet()) { | |||
proof = r3_mkt.getProof(n.longValue()); | |||
assertNotNull(proof); | |||
assertEquals(expectedDataNodes.get(n), proof.getHash(0)); | |||
} | |||
} | |||
} | |||
@@ -772,8 +799,7 @@ public class MerkleTreeTest { | |||
* 注:此方法不处理溢出;调用者需要自行规避; | |||
* | |||
* @param value | |||
* @param x | |||
* 大于等于 0 的整数; | |||
* @param x 大于等于 0 的整数; | |||
* @return | |||
*/ | |||
private static long power(long value, int x) { | |||