diff --git a/pyproject.toml b/pyproject.toml index a0f9359..bc787b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,3 +69,4 @@ orchard_note_encryption = "zcash_test_vectors.orchard.note_encryption:main" orchard_poseidon = "zcash_test_vectors.orchard.poseidon:main" orchard_poseidon_hash = "zcash_test_vectors.orchard.poseidon:hash_test_vectors" orchard_sinsemilla = "zcash_test_vectors.orchard.sinsemilla:main" +orchard_zip32 = "zcash_test_vectors.orchard.zip32:main" diff --git a/zcash_test_vectors/orchard/zip32.py b/zcash_test_vectors/orchard/zip32.py new file mode 100755 index 0000000..be87e26 --- /dev/null +++ b/zcash_test_vectors/orchard/zip32.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +import sys; assert sys.version_info[0] >= 3, "Python 3 required." + +from hashlib import blake2b + +from .key_components import FullViewingKey, ExtendedSpendingKey + +from ..hd_common import hardened +from ..utils import i2leosp +from ..output import render_args, render_tv + + +class DerivedSpendingKey(object): + def __init__(self, extsk, depth=0, parent_tag=i2leosp(32, 0), i=0): + self._extsk = sk + self._depth = depth + self._parent_tag = parent_tag + self._i = i + + def __eq__(self, other): + return (self._extsk == other._extsk and + self._depth == other._depth and + self._parent_tag == other._parent_tag and + self._i == other._i) + + @classmethod + def master(cls, S): + return cls(ExtendedSpendingKey(S)) + + def sk(self): + return self._extsk.data + + def c(self): + return self._extsk.chaincode + + def depth(self): + return self._depth + + def parent_tag(self): + return self._parent_tag + + def i(self): + return self._i + + def fingerprint(self): + fvk = FullViewingKey.from_spending_key(self._extsk) + digest = blake2b(person=b'ZcashOrchardFVFP', digest_size=32) + digest.update(bytes(fvk)) + return digest.digest() + + def tag(self): + return self.fingerprint()[:4] + + def __bytes__(self): + return (i2leosp(8, self.depth()) + + self.parent_tag() + + i2leosp(32, self.i()) + + self.c() + + self.sk()) + + def child(self, i): + return self.__class__(self._extsk.child(i), self.depth()+1, self.tag(), i) + + +def main(): + args = render_args() + + seed = bytes(range(32)) + m = DerivedSpendingKey.master(seed) + m_1h = m.child(hardened(1)) + m_1h_2h = m_1h.child(hardened(2)) + m_1h_2h_3h = m_1h_2h.child(hardened(3)) + + keys = [m, m_1h, m_1h_2h, m_1h_2h_3h] + + render_tvs(args, keys) + +def render_tvs(args, keys): + test_vectors = [ + {'sk' : k.sk(), + 'c' : k.c(), + 'xsk' : bytes(k), + 'fp' : k.fingerprint(), + } + for k in keys + ] + + render_tv( + args, + 'orchard_zip32', + ( + ('sk', '[u8; 32]'), + ('c', '[u8; 32]'), + ('xsk', '[u8; 169]'), + ('fp', '[u8; 32]'), + ), + test_vectors, + ) + +if __name__ == '__main__': + main()