diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index 446080f9a..bf9b80385 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -3,8 +3,8 @@ $(package)_version=0.1 $(package)_download_path=https://github.com/zcash/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=1fb331a92b63da41e95ef9db671982d243a13bcd6d25570760c9ca83b8996887 -$(package)_git_commit=36d7acf3f37570f499fc8fe79fda372e5eb873ca +$(package)_sha256_hash=b96a0646d4c4856bc6171dc26cce10644a6129ac92b73a91f94246fb6b7f3516 +$(package)_git_commit=18f4945d942cc53e336c40bf13080934179a9047 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 22e009e76..e767f74db 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -7,6 +7,18 @@ #endif #include "zcash/Address.hpp" +TEST(keystore_tests, sapling_keys) { + auto sk = libzcash::SaplingSpendingKey::random(); + auto addr = sk.default_address(); + + // Check that full viewing key derived from sk and expanded sk are the same + auto exp_sk = sk.expanded_spending_key(); + auto full_viewing_key = sk.full_viewing_key(); + EXPECT_EQ(full_viewing_key, exp_sk.full_viewing_key()); + + auto in_viewing_key = full_viewing_key.in_viewing_key(); +} + TEST(keystore_tests, store_and_retrieve_spending_key) { CBasicKeyStore keyStore; libzcash::SproutSpendingKey skOut; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 270119512..43bd45ecd 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,47 @@ 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(); +} + +SaplingIncomingViewingKey SaplingFullViewingKey::in_viewing_key() const { + uint256 ivk; + librustzcash_crh_ivk(ak.begin(), nk.begin(), ivk.begin()); + return SaplingIncomingViewingKey(ivk); +} + +SaplingSpendingKey SaplingSpendingKey::random() { + return SaplingSpendingKey(random_uint256()); +} + +boost::optional SaplingIncomingViewingKey::address(diversifier_t d) const { + uint256 pk_d; + if (librustzcash_check_diversifier(d.data())) { + librustzcash_ivk_to_pkd(this->begin(), d.data(), pk_d.begin()); + return SaplingPaymentAddress(d, pk_d); + } else { + return boost::none; + } +} + +boost::optional 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..6b8c310f4 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 SaplingIncomingViewingKey : public uint256 { +public: + SaplingIncomingViewingKey() : uint256() { } + SaplingIncomingViewingKey(uint256 ivk) : uint256(ivk) { } + + // Can pass in diversifier for Sapling addr + boost::optional 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); + } + + SaplingIncomingViewingKey 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 + boost::optional 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..2491de83e 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -1,6 +1,80 @@ #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& sk, unsigned char t) +{ + std::array res; + unsigned char blob[33]; + + memcpy(&blob[0], sk.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, 34); + crypto_generichash_blake2b_final(&state, res.data(), 11); + + if (librustzcash_check_diversifier(res.data())) { + break; + } else if (blob[33] == 255) { + throw std::runtime_error("librustzcash_check_diversifier did not return valid diversifier"); + } + 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..f666cfa23 100644 --- a/src/zcash/prf.h +++ b/src/zcash/prf.h @@ -9,10 +9,20 @@ within the zkSNARK circuit. #include "uint256.h" #include "uint252.h" +#include + +//! Sprout functions 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); uint256 PRF_pk(const uint252& a_sk, size_t i0, const uint256& h_sig); uint256 PRF_rho(const uint252& phi, size_t i0, const uint256& h_sig); +//! Sapling functions +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); + #endif // ZC_PRF_H_