diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 270119512..1e43281f1 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -4,6 +4,8 @@ #include "prf.h" #include "streams.h" +#include + namespace libzcash { uint256 SproutPaymentAddress::GetHash() const { @@ -36,6 +38,43 @@ SproutPaymentAddress SproutSpendingKey::address() const { return viewing_key().address(); } +//! Sapling +SaplingFullViewingKey SaplingExpandedSpendingKey::full_viewing_key() const { + uint256 ak; + uint256 nk; + librustzcash_ask_to_ak(ask.begin(), ak.begin()); + librustzcash_nsk_to_nk(nsk.begin(), nk.begin()); + return SaplingFullViewingKey(ak, nk, ovk); +} + +SaplingExpandedSpendingKey SaplingSpendingKey::expanded_spending_key() const { + return SaplingExpandedSpendingKey(PRF_ask(*this), PRF_nsk(*this), PRF_ovk(*this)); +} + +SaplingFullViewingKey SaplingSpendingKey::full_viewing_key() const { + return expanded_spending_key().full_viewing_key(); +} + +SaplingInViewingKey SaplingFullViewingKey::in_viewing_key() const { + uint256 ivk; + librustzcash_crh_ivk(ak.begin(), nk.begin(), ivk.begin()); + return SaplingInViewingKey(ivk); +} + +SaplingSpendingKey SaplingSpendingKey::random() { + return SaplingSpendingKey(random_uint256()); +} + +SaplingPaymentAddress SaplingInViewingKey::address(diversifier_t d) const { + uint256 pk_d; + librustzcash_ivk_to_pkd(this->begin(), d.data(), pk_d.begin()); + return SaplingPaymentAddress(d, pk_d); +} + +SaplingPaymentAddress SaplingSpendingKey::default_address() const { + return full_viewing_key().in_viewing_key().address(default_diversifier(*this)); +} + } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 1fba11d9d..e710ae275 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -18,6 +18,8 @@ const size_t SerializedPaymentAddressSize = 64; const size_t SerializedViewingKeySize = 64; const size_t SerializedSpendingKeySize = 32; +typedef std::array diversifier_t; + class SproutPaymentAddress { public: uint256 a_pk; @@ -97,6 +99,116 @@ typedef boost::variant PaymentAddress; typedef boost::variant ViewingKey; typedef boost::variant SpendingKey; +//! Sapling functions. +class SaplingPaymentAddress { +public: + diversifier_t d; + uint256 pk_d; + + SaplingPaymentAddress() : d(), pk_d() { } + SaplingPaymentAddress(diversifier_t d, uint256 pk_d) : d(d), pk_d(pk_d) { } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(d); + READWRITE(pk_d); + } + + friend inline bool operator==(const SaplingPaymentAddress& a, const SaplingPaymentAddress& b) { + return a.d == b.d && a.pk_d == b.pk_d; + } + friend inline bool operator<(const SaplingPaymentAddress& a, const SaplingPaymentAddress& b) { + return (a.d < b.d || + (a.d == b.d && a.pk_d < b.pk_d)); + } +}; + +class SaplingInViewingKey : public uint256 { +public: + SaplingInViewingKey() : uint256() { } + SaplingInViewingKey(uint256 ivk) : uint256(ivk) { } + + // Can pass in diversifier for Sapling addr + SaplingPaymentAddress address(diversifier_t d) const; +}; + +class SaplingFullViewingKey { +public: + uint256 ak; + uint256 nk; + uint256 ovk; + + SaplingFullViewingKey() : ak(), nk(), ovk() { } + SaplingFullViewingKey(uint256 ak, uint256 nk, uint256 ovk) : ak(ak), nk(nk), ovk(ovk) { } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(ak); + READWRITE(nk); + READWRITE(ovk); + } + + SaplingInViewingKey in_viewing_key() const; + + friend inline bool operator==(const SaplingFullViewingKey& a, const SaplingFullViewingKey& b) { + return a.ak == b.ak && a.nk == b.nk && a.ovk == b.ovk; + } + friend inline bool operator<(const SaplingFullViewingKey& a, const SaplingFullViewingKey& b) { + return (a.ak < b.ak || + (a.ak == b.ak && a.nk < b.nk) || + (a.ak == b.ak && a.nk == b.nk && a.ovk < b.ovk)); + } +}; + + +class SaplingExpandedSpendingKey { +public: + uint256 ask; + uint256 nsk; + uint256 ovk; + + SaplingExpandedSpendingKey() : ask(), nsk(), ovk() { } + SaplingExpandedSpendingKey(uint256 ask, uint256 nsk, uint256 ovk) : ask(ask), nsk(nsk), ovk(ovk) { } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(ask); + READWRITE(nsk); + READWRITE(ovk); + } + + SaplingFullViewingKey full_viewing_key() const; + + friend inline bool operator==(const SaplingExpandedSpendingKey& a, const SaplingExpandedSpendingKey& b) { + return a.ask == b.ask && a.nsk == b.nsk && a.ovk == b.ovk; + } + friend inline bool operator<(const SaplingExpandedSpendingKey& a, const SaplingExpandedSpendingKey& b) { + return (a.ask < b.ask || + (a.ask == b.ask && a.nsk < b.nsk) || + (a.ask == b.ask && a.nsk == b.nsk && a.ovk < b.ovk)); + } +}; + +class SaplingSpendingKey : public uint256 { +public: + SaplingSpendingKey() : uint256() { } + SaplingSpendingKey(uint256 sk) : uint256(sk) { } + + static SaplingSpendingKey random(); + + SaplingExpandedSpendingKey expanded_spending_key() const; + SaplingFullViewingKey full_viewing_key() const; + + // Can derive Sapling addr from default diversifier + SaplingPaymentAddress default_address() const; +}; + } /** Check whether a PaymentAddress is not an InvalidEncoding. */ diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index d0f1dc4ec..45dfd9c27 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -1,6 +1,78 @@ #include "prf.h" #include "crypto/sha256.h" +#include "hash.h" +#include +#include + +const unsigned char ZCASH_EXPANDSEED_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = {'Z','c','a','s','h','_','E','x','p','a','n','d','S','e','e','d'}; + +// Sapling +std::array PRF_expand(const uint256& x, unsigned char t) +{ + std::array res; + unsigned char blob[33]; + + memcpy(&blob[0], x.begin(), 32); + blob[32] = t; + + crypto_generichash_blake2b_state state; + crypto_generichash_blake2b_init_salt_personal(&state, nullptr, 0, 64, nullptr, ZCASH_EXPANDSEED_PERSONALIZATION); + crypto_generichash_blake2b_update(&state, blob, 33); + crypto_generichash_blake2b_final(&state, res.data(), 64); + + return res; +} + +uint256 PRF_ask(const uint256& sk) +{ + uint256 ask; + auto tmp = PRF_expand(sk, 0); + librustzcash_to_scalar(tmp.data(), ask.begin()); + return ask; +} + +uint256 PRF_nsk(const uint256& sk) +{ + uint256 nsk; + auto tmp = PRF_expand(sk, 1); + librustzcash_to_scalar(tmp.data(), nsk.begin()); + return nsk; +} + +uint256 PRF_ovk(const uint256& sk) +{ + uint256 ovk; + auto tmp = PRF_expand(sk, 2); + memcpy(ovk.begin(), tmp.data(), 32); + return ovk; +} + +std::array default_diversifier(const uint256& sk) +{ + std::array res; + unsigned char blob[34]; + + memcpy(&blob[0], sk.begin(), 32); + blob[32] = 3; + + blob[33] = 0; + while (true) { + crypto_generichash_blake2b_state state; + crypto_generichash_blake2b_init_salt_personal(&state, nullptr, 0, 64, nullptr, ZCASH_EXPANDSEED_PERSONALIZATION); + crypto_generichash_blake2b_update(&state, blob, 33); + crypto_generichash_blake2b_final(&state, res.data(), 11); + + if (librustzcash_check_diversifier(res.data())) { + break; + } + blob[33] += 1; + } + + return res; +} + +// Sprout uint256 PRF(bool a, bool b, bool c, bool d, const uint252& x, const uint256& y) diff --git a/src/zcash/prf.h b/src/zcash/prf.h index dec934f4a..0d74f294f 100644 --- a/src/zcash/prf.h +++ b/src/zcash/prf.h @@ -9,6 +9,14 @@ within the zkSNARK circuit. #include "uint256.h" #include "uint252.h" +#include + +uint256 PRF_ask(const uint256& sk); +uint256 PRF_nsk(const uint256& sk); +uint256 PRF_ovk(const uint256& sk); + +std::array default_diversifier(const uint256& sk); + uint256 PRF_addr_a_pk(const uint252& a_sk); uint256 PRF_addr_sk_enc(const uint252& a_sk); uint256 PRF_nf(const uint252& a_sk, const uint256& rho);