def test_initialize_with_leaves(num_leaves): depth = math.ceil(math.log(num_leaves, 2)) leaves = [b'asdf'] * num_leaves hashed_leaves = [sha3(leaf) for leaf in leaves] empty_leaves = [sha3(NULL_HASH)] * (2**depth - num_leaves) assert FixedMerkle(depth, leaves).leaves == hashed_leaves + empty_leaves
def test_create_membership_proof(): leaf = b'c' leaves = [b'a', b'b', leaf] proof = FixedMerkle(2, leaves).create_membership_proof(leaf) sibling_hash = sha3(LEAF_SALT + NULL_HASH) node_hash = sha3(NODE_SALT + sha3(LEAF_SALT + leaves[0]) + sha3(LEAF_SALT + leaves[1])) assert proof == sibling_hash + node_hash
def check_membership(self, leaf, index, proof): hashed_leaf = sha3(leaf) computed_hash = hashed_leaf computed_index = index for i in range(0, self.depth * 32, 32): proof_segment = proof[i:i + 32] if computed_index % 2 == 0: computed_hash = sha3(computed_hash + proof_segment) else: computed_hash = sha3(proof_segment + computed_hash) computed_index = computed_index // 2 return computed_hash == self.root
def __init__(self, depth, leaves=[]): if depth < 1: raise ValueError('depth must be at least 1') self.depth = depth self.leaf_count = 2 ** depth if len(leaves) > self.leaf_count: raise ValueError('number of leaves should be at most depth ** 2') leaves = [sha3(leaf) for leaf in leaves] self.leaves = leaves + [sha3(NULL_HASH)] * (self.leaf_count - len(leaves)) self.tree = [self.__create_nodes(self.leaves)] self.__create_tree(self.tree[0])
def __create_tree(self, leaves): if len(leaves) == 1: self.root = leaves[0].data return next_level = len(leaves) tree_level = [] for i in range(0, next_level, 2): combined = sha3(leaves[i].data + leaves[i + 1].data) next_node = MerkleNode(combined, leaves[i], leaves[i + 1]) tree_level.append(next_node) self.tree.append(tree_level) self.__create_tree(tree_level)
def create_membership_proof(self, leaf): hashed_leaf = sha3(leaf) if not self.__is_member(hashed_leaf): raise MemberNotExistException('leaf is not in the merkle tree') index = self.leaves.index(hashed_leaf) proof = b'' for i in range(0, self.depth, 1): if index % 2 == 0: sibling_index = index + 1 else: sibling_index = index - 1 index = index // 2 proof += self.tree[i][sibling_index].data return proof
def make_keystore_json(priv, pw, kdf="pbkdf2", cipher="aes-128-ctr"): # Get the hash function and default parameters if kdf not in kdfs: raise Exception("Hash algo %s not supported" % kdf) kdfeval = kdfs[kdf]["calc"] kdfparams = kdfs[kdf]["mkparams"]() # Compute derived key derivedkey = kdfeval(pw, kdfparams) # Get the cipher and default parameters if cipher not in ciphers: raise Exception("Encryption algo %s not supported" % cipher) encrypt = ciphers[cipher]["encrypt"] cipherparams = ciphers[cipher]["mkparams"]() # Produce the encryption key and encrypt enckey = derivedkey[:16] c = encrypt(priv, enckey, cipherparams) # Compute the MAC mac = sha3(derivedkey[16:32] + c) # Make a UUID u = encode_hex(os.urandom(16)) uuid = b'-'.join((u[:8], u[8:12], u[12:16], u[16:20], u[20:])) addr = privtoaddr(priv).encode('hex') # Return the keystore json return { "address": addr, "crypto": { "cipher": cipher, "ciphertext": encode_hex(c), "cipherparams": cipherparams, "kdf": kdf, "kdfparams": kdfparams, "mac": encode_hex(mac), "version": 1 }, "id": uuid, "version": 3 }
def decode_keystore_json(jsondata, pw): # Get KDF function and parameters if "crypto" in jsondata: cryptdata = jsondata["crypto"] elif "Crypto" in jsondata: cryptdata = jsondata["Crypto"] else: raise Exception("JSON data must contain \"crypto\" object") kdfparams = cryptdata["kdfparams"] kdf = cryptdata["kdf"] if cryptdata["kdf"] not in kdfs: raise HashNotSupportedError("Hash algo %s not supported" % kdf) kdfeval = kdfs[kdf]["calc"] # Get cipher and parameters cipherparams = cryptdata["cipherparams"] cipher = cryptdata["cipher"] if cryptdata["cipher"] not in ciphers: raise EncryptionNotSupportedError("Encryption algo %s not supported" % cipher) decrypt = ciphers[cipher]["decrypt"] # Compute the derived key derivedkey = kdfeval(pw, kdfparams) assert len(derivedkey) >= 32, \ "Derived key must be at least 32 bytes long" # print(b'derivedkey: ' + encode_hex(derivedkey)) enckey = derivedkey[:16] # print(b'enckey: ' + encode_hex(enckey)) ctext = decode_hex(cryptdata["ciphertext"]) # Decrypt the ciphertext o = decrypt(ctext, enckey, cipherparams) # Compare the provided MAC with a locally computed MAC # print(b'macdata: ' + encode_hex(derivedkey[16:32] + ctext)) mac1 = sha3(derivedkey[16:32] + ctext) mac2 = decode_hex(cryptdata["mac"]) if mac1 != mac2: raise PasswordError("MAC mismatch. Password incorrect?") return o
def privtoaddr(x): if len(x) > 32: x = decode_hex(x) return sha3(bitcoin.privtopub(x)[1:])[12:]
def get_empty_tree_hash(depth): root = sha3(NULL_HASH) for _ in range(depth): root = sha3(root + root) return root
def test_create_membership_proof(): leaves = [b'a', b'b', b'c'] proof = FixedMerkle(2, leaves).create_membership_proof(leaves[2]) assert proof == sha3(NULL_HASH) + sha3(sha3(leaves[0]) + sha3(leaves[1]))
def test_initial_state(depth): assert FixedMerkle(depth).leaves == [sha3(NULL_HASH)] * (2**depth)
def get_empty_tree_hash(depth): root = sha3(LEAF_SALT + NULL_HASH) for _ in range(depth): root = sha3(NODE_SALT + root + root) return root