zcash-test-vectors/orchard_key_components.py

156 lines
4.3 KiB
Python

#!/usr/bin/env python3
import sys; assert sys.version_info[0] >= 3, "Python 3 required."
from ff1 import ff1_aes256_encrypt
from sapling_key_components import prf_expand
from orchard_generators import NULLIFIER_K_BASE, SPENDING_KEY_BASE, group_hash
from orchard_pallas import Fp, Scalar, Point
from orchard_poseidon_hash import poseidon_hash
from orchard_commitments import commit_ivk
from utils import i2leosp, i2lebsp, lebs2osp
from orchard_utils import to_base, to_scalar
from tv_output import render_args, render_tv
#
# PRFs and hashes
#
def diversify_hash(d):
P = group_hash(b'z.cash:Orchard-gd', d)
if P == Point.identity():
P = group_hash(b'z.cash:Orchard-gd', b'')
return P
def prf_nf_orchard(nk, rho):
return poseidon_hash(nk, rho)
def derive_nullifier(nk, rho: Fp, psi: Fp, cm):
scalar = prf_nf_orchard(nk, rho) + psi # addition mod p
point = NULLIFIER_K_BASE * Scalar(scalar.s) + cm
return point.extract()
#
# Key components
#
class SpendingKey:
def __init__(self, data):
self.data = data
self.ask = to_scalar(prf_expand(self.data, b'\x06'))
self.nk = to_base(prf_expand(self.data, b'\x07'))
self.rivk = to_scalar(prf_expand(self.data, b'\x08'))
if self.ask == Scalar.ZERO:
raise ValueError("invalid spending key")
self.akP = SPENDING_KEY_BASE * self.ask
if bytes(self.akP)[-1] & 0x80 != 0:
self.ask = -self.ask
self.ak = self.akP.extract()
assert commit_ivk(self.rivk, self.ak, self.nk) is not None
class FullViewingKey(object):
def __init__(self, sk):
(self.rivk, self.ak, self.nk) = (sk.rivk, sk.ak, sk.nk)
K = i2leosp(256, self.rivk.s)
R = prf_expand(K, b'\x82' + i2leosp(256, self.ak.s) + i2leosp(256, self.nk.s))
self.dk = R[:32]
self.ovk = R[32:]
def ivk(self):
return commit_ivk(self.rivk, self.ak, self.nk)
def default_d(self):
index = i2lebsp(88, 0)
return lebs2osp(ff1_aes256_encrypt(self.dk, b'', index))
def default_gd(self):
return diversify_hash(self.default_d())
def default_pkd(self):
return self.default_gd() * Scalar(self.ivk().s)
def main():
args = render_args()
from orchard_note import OrchardNote
from random import Random
from tv_rand import Rand
rng = Random(0xabad533d)
def randbytes(l):
ret = []
while len(ret) < l:
ret.append(rng.randrange(0, 256))
return bytes(ret)
rand = Rand(randbytes)
test_vectors = []
for _ in range(0, 10):
sk = SpendingKey(rand.b(32))
fvk = FullViewingKey(sk)
default_d = fvk.default_d()
default_pk_d = fvk.default_pkd()
note_v = rand.u64()
note_rho = Fp.random(rand)
note_rseed = rand.b(32)
note = OrchardNote(
default_d,
default_pk_d,
note_v,
note_rho,
note_rseed,
)
note_cm = note.note_commitment()
note_nf = derive_nullifier(fvk.nk, note_rho, note.psi, note_cm)
test_vectors.append({
'sk': sk.data,
'ask': bytes(sk.ask),
'ak': bytes(fvk.ak),
'nk': bytes(fvk.nk),
'rivk': bytes(fvk.rivk),
'ivk': bytes(fvk.ivk()),
'ovk': fvk.ovk,
'dk': fvk.dk,
'default_d': default_d,
'default_pk_d': bytes(default_pk_d),
'note_v': note_v,
'note_rho': bytes(note_rho),
'note_rseed': bytes(note_rseed),
'note_cmx': bytes(note_cm.extract()),
'note_nf': bytes(note_nf),
})
render_tv(
args,
'orchard_key_components',
(
('sk', '[u8; 32]'),
('ask', '[u8; 32]'),
('ak', '[u8; 32]'),
('nk', '[u8; 32]'),
('rivk', '[u8; 32]'),
('ivk', '[u8; 32]'),
('ovk', '[u8; 32]'),
('dk', '[u8; 32]'),
('default_d', '[u8; 11]'),
('default_pk_d', '[u8; 32]'),
('note_v', 'u64'),
('note_rho', '[u8; 32]'),
('note_rseed', '[u8; 32]'),
('note_cmx', '[u8; 32]'),
('note_nf', '[u8; 32]'),
),
test_vectors,
)
if __name__ == '__main__':
main()