@@ -5,12 +5,10 @@ import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_CODE_SIZE; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import com.jd.blockchain.crypto.CryptoAlgorithm; | import com.jd.blockchain.crypto.CryptoAlgorithm; | ||||
import com.jd.blockchain.crypto.CryptoBytes; | |||||
import com.jd.blockchain.crypto.CryptoException; | import com.jd.blockchain.crypto.CryptoException; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.HashFunction; | import com.jd.blockchain.crypto.HashFunction; | ||||
import com.jd.blockchain.crypto.utils.classic.RIPEMD160Utils; | import com.jd.blockchain.crypto.utils.classic.RIPEMD160Utils; | ||||
import com.jd.blockchain.utils.security.RipeMD160Utils; | |||||
public class RIPEMD160HashFunction implements HashFunction { | 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_BYTES = 160 / 8; | ||||
private static final int DIGEST_LENGTH = ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | private static final int DIGEST_LENGTH = ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | ||||
RIPEMD160HashFunction() { | RIPEMD160HashFunction() { | ||||
} | } | ||||
@@ -30,7 +28,6 @@ public class RIPEMD160HashFunction implements HashFunction { | |||||
@Override | @Override | ||||
public HashDigest hash(byte[] data) { | public HashDigest hash(byte[] data) { | ||||
if (data == null) { | if (data == null) { | ||||
throw new CryptoException("data is null!"); | throw new CryptoException("data is null!"); | ||||
} | } | ||||
@@ -39,6 +36,16 @@ public class RIPEMD160HashFunction implements HashFunction { | |||||
return new HashDigest(RIPEMD160, digestBytes); | 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 | @Override | ||||
public boolean verify(HashDigest digest, byte[] data) { | public boolean verify(HashDigest digest, byte[] data) { | ||||
HashDigest hashDigest = hash(data); | HashDigest hashDigest = hash(data); | ||||
@@ -59,5 +66,5 @@ public class RIPEMD160HashFunction implements HashFunction { | |||||
throw new CryptoException("digestBytes is invalid!"); | 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 java.util.Arrays; | ||||
import com.jd.blockchain.crypto.CryptoAlgorithm; | import com.jd.blockchain.crypto.CryptoAlgorithm; | ||||
import com.jd.blockchain.crypto.CryptoBytes; | |||||
import com.jd.blockchain.crypto.CryptoException; | import com.jd.blockchain.crypto.CryptoException; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.HashFunction; | import com.jd.blockchain.crypto.HashFunction; | ||||
import com.jd.blockchain.crypto.utils.classic.SHA256Utils; | import com.jd.blockchain.crypto.utils.classic.SHA256Utils; | ||||
import com.jd.blockchain.utils.security.ShaUtils; | |||||
public class SHA256HashFunction implements HashFunction { | public class SHA256HashFunction implements HashFunction { | ||||
@@ -30,7 +28,6 @@ public class SHA256HashFunction implements HashFunction { | |||||
@Override | @Override | ||||
public HashDigest hash(byte[] data) { | public HashDigest hash(byte[] data) { | ||||
if (data == null) { | if (data == null) { | ||||
throw new CryptoException("data is null!"); | throw new CryptoException("data is null!"); | ||||
} | } | ||||
@@ -38,6 +35,16 @@ public class SHA256HashFunction implements HashFunction { | |||||
byte[] digestBytes = SHA256Utils.hash(data); | byte[] digestBytes = SHA256Utils.hash(data); | ||||
return new HashDigest(SHA256, digestBytes); | 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 | @Override | ||||
public boolean verify(HashDigest digest, byte[] data) { | public boolean verify(HashDigest digest, byte[] data) { | ||||
@@ -10,16 +10,26 @@ import org.bouncycastle.crypto.digests.RIPEMD160Digest; | |||||
*/ | */ | ||||
public class RIPEMD160Utils { | 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 { | public class SHA256Utils { | ||||
// The length of SHA256 output is 32 bytes | // 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){ | public static byte[] hash(byte[] data){ | ||||
@@ -22,4 +22,14 @@ public class SHA256Utils { | |||||
sha256Digest.doFinal(result,0); | sha256Digest.doFinal(result,0); | ||||
return result; | 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); | HashDigest hash(byte[] data); | ||||
/** | |||||
* 计算指定数据的 hash; | |||||
* | |||||
* @param data | |||||
* @return | |||||
*/ | |||||
HashDigest hash(byte[] data, int offset, int len); | |||||
/** | /** | ||||
* 校验 hash 摘要与指定的数据是否匹配; | * 校验 hash 摘要与指定的数据是否匹配; | ||||
@@ -16,7 +16,7 @@ public class SM3HashFunction implements HashFunction { | |||||
private static final int DIGEST_BYTES = 256 / 8; | private static final int DIGEST_BYTES = 256 / 8; | ||||
private static final int DIGEST_LENGTH = CryptoBytes.ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | private static final int DIGEST_LENGTH = CryptoBytes.ALGORYTHM_CODE_SIZE + DIGEST_BYTES; | ||||
SM3HashFunction() { | SM3HashFunction() { | ||||
} | } | ||||
@@ -27,7 +27,6 @@ public class SM3HashFunction implements HashFunction { | |||||
@Override | @Override | ||||
public HashDigest hash(byte[] data) { | public HashDigest hash(byte[] data) { | ||||
if (data == null) { | if (data == null) { | ||||
throw new CryptoException("data is null!"); | throw new CryptoException("data is null!"); | ||||
} | } | ||||
@@ -36,6 +35,16 @@ public class SM3HashFunction implements HashFunction { | |||||
return new HashDigest(SM3, digestBytes); | 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 | @Override | ||||
public boolean verify(HashDigest digest, byte[] data) { | public boolean verify(HashDigest digest, byte[] data) { | ||||
HashDigest hashDigest = hash(data); | HashDigest hashDigest = hash(data); | ||||
@@ -4,19 +4,30 @@ import org.bouncycastle.crypto.digests.SM3Digest; | |||||
public class SM3Utils { | 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 | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public class HashDegistList implements HashProof { | |||||
public class HashDigestList implements HashProof { | |||||
private List<HashDigest> proofs = new ArrayList<HashDigest>(); | private List<HashDigest> proofs = new ArrayList<HashDigest>(); | ||||
public HashDegistList() { | |||||
public HashDigestList() { | |||||
} | } | ||||
public HashDegistList(HashProof proof) { | |||||
public HashDigestList(HashProof proof) { | |||||
concat(proof); | concat(proof); | ||||
} | } | ||||
@@ -208,7 +208,7 @@ public class MerkleAccount implements CompositeAccount, HashProvable, MerkleSnap | |||||
if (rootProof == null) { | if (rootProof == null) { | ||||
return null; | return null; | ||||
} | } | ||||
HashDegistList proof = new HashDegistList(rootProof); | |||||
HashDigestList proof = new HashDigestList(rootProof); | |||||
proof.concat(dataProof); | proof.concat(dataProof); | ||||
return proof; | return proof; | ||||
} | } | ||||
@@ -556,6 +556,9 @@ public class MerkleTreeTest { | |||||
/** | /** | ||||
* 测试从存储重新加载 Merkle 树的正确性; | * 测试从存储重新加载 Merkle 树的正确性; | ||||
*/ | */ | ||||
/** | |||||
* | |||||
*/ | |||||
@Test | @Test | ||||
public void testMerkleReload() { | public void testMerkleReload() { | ||||
CryptoSetting setting = Mockito.mock(CryptoSetting.class); | CryptoSetting setting = Mockito.mock(CryptoSetting.class); | ||||
@@ -563,7 +566,7 @@ public class MerkleTreeTest { | |||||
when(setting.getAutoVerifyHash()).thenReturn(true); | when(setting.getAutoVerifyHash()).thenReturn(true); | ||||
// 保存所有写入的数据节点的 SN-Hash 映射表; | // 保存所有写入的数据节点的 SN-Hash 映射表; | ||||
TreeMap<Long, HashDigest> dataNodes = new TreeMap<>(); | |||||
TreeMap<Long, HashDigest> expectedDataNodes = new TreeMap<>(); | |||||
MerkleNode nd; | MerkleNode nd; | ||||
// 测试从空的树开始,顺序增加数据节点; | // 测试从空的树开始,顺序增加数据节点; | ||||
@@ -580,7 +583,7 @@ public class MerkleTreeTest { | |||||
for (int i = 0; i < count; i++) { | for (int i = 0; i < count; i++) { | ||||
rand.nextBytes(dataBuf); | rand.nextBytes(dataBuf); | ||||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | ||||
dataNodes.put(sn, nd.getNodeHash()); | |||||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||||
sn++; | sn++; | ||||
} | } | ||||
mkt.commit(); | mkt.commit(); | ||||
@@ -610,6 +613,24 @@ public class MerkleTreeTest { | |||||
// 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; | // 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; | ||||
long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; | long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; | ||||
assertEquals(expectedNodes, storage.getCount()); | 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); | rand.nextBytes(dataBuf); | ||||
sn = i; | sn = i; | ||||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | ||||
dataNodes.put(sn, nd.getNodeHash()); | |||||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||||
} | } | ||||
mkt.commit(); | mkt.commit(); | ||||
@@ -658,16 +679,18 @@ public class MerkleTreeTest { | |||||
rand.nextBytes(dataBuf); | rand.nextBytes(dataBuf); | ||||
sn = maxSN + 1 + i; | sn = maxSN + 1 + i; | ||||
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); | ||||
dataNodes.put(sn, nd.getNodeHash()); | |||||
expectedDataNodes.put(sn, nd.getNodeHash()); | |||||
} | } | ||||
mkt.commit(); | 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_proof1, r1_mkt.getProof(r1_sn1).toString()); | ||||
assertEquals(r1_proof2, r1_mkt.getProof(r1_sn2).toString()); | assertEquals(r1_proof2, r1_mkt.getProof(r1_sn2).toString()); | ||||
// 从第 2 轮提交的 Merkle 根哈希加载; | // 从第 2 轮提交的 Merkle 根哈希加载; | ||||
// 第 2 轮生成的 Merkle 树是对第 1 轮的数据的全部节点的修改,因此同一个 SN 的节点的证明是不同的; | // 第 2 轮生成的 Merkle 树是对第 1 轮的数据的全部节点的修改,因此同一个 SN 的节点的证明是不同的; | ||||
MerkleTree r2_mkt = new MerkleTree(r2_rootHash, setting, keyPrefix, storage, true); | 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()); | 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 value | ||||
* @param x | |||||
* 大于等于 0 的整数; | |||||
* @param x 大于等于 0 的整数; | |||||
* @return | * @return | ||||
*/ | */ | ||||
private static long power(long value, int x) { | private static long power(long value, int x) { | ||||