From 11acfe6e9fb97addcdccf2ef126fe22f2953cc19 Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Thu, 17 May 2018 06:13:29 -0700 Subject: [PATCH 1/7] Add Sapling key classes to wallet, with new librustzcash APIs --- src/zcash/Address.cpp | 39 +++++++++++++++ src/zcash/Address.hpp | 112 ++++++++++++++++++++++++++++++++++++++++++ src/zcash/prf.cpp | 72 +++++++++++++++++++++++++++ src/zcash/prf.h | 8 +++ 4 files changed, 231 insertions(+) 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); From 84e3a2212f9c4d0dcd9d9c6d2c0a24bec4a76fac Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Thu, 17 May 2018 06:13:52 -0700 Subject: [PATCH 2/7] Change librustzcash dependency hash to work for new Sapling classes --- depends/packages/librustzcash.mk | 4 ++-- src/zcash/prf.cpp | 4 +++- src/zcash/prf.h | 14 ++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index 446080f9a..d33115a6e 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=ed8de6f820f4441a80457a25db022168df09ca945302d27004975b0d71c32718 +$(package)_git_commit=0ef61e6d460e49e70202f8599bbdb2d39e41d061 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index 45dfd9c27..e79bc814b 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -8,7 +8,7 @@ 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 PRF_expand(const uint256& sk, unsigned char t) { std::array res; unsigned char blob[33]; @@ -65,6 +65,8 @@ std::array default_diversifier(const uint256& sk) 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; } diff --git a/src/zcash/prf.h b/src/zcash/prf.h index 0d74f294f..f666cfa23 100644 --- a/src/zcash/prf.h +++ b/src/zcash/prf.h @@ -11,16 +11,18 @@ within the zkSNARK circuit. #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); - +//! 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_ From 52d81ff794ec60f1223029b9216edef87b77ed05 Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Fri, 1 Jun 2018 01:48:28 -0700 Subject: [PATCH 3/7] Update librustzcash dependency, address comments --- depends/packages/librustzcash.mk | 4 ++-- src/zcash/prf.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index d33115a6e..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=ed8de6f820f4441a80457a25db022168df09ca945302d27004975b0d71c32718 -$(package)_git_commit=0ef61e6d460e49e70202f8599bbdb2d39e41d061 +$(package)_sha256_hash=b96a0646d4c4856bc6171dc26cce10644a6129ac92b73a91f94246fb6b7f3516 +$(package)_git_commit=18f4945d942cc53e336c40bf13080934179a9047 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index e79bc814b..7a229d2fb 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -13,7 +13,7 @@ std::array PRF_expand(const uint256& sk, unsigned char t) std::array res; unsigned char blob[33]; - memcpy(&blob[0], x.begin(), 32); + memcpy(&blob[0], sk.begin(), 32); blob[32] = t; crypto_generichash_blake2b_state state; @@ -65,7 +65,7 @@ std::array default_diversifier(const uint256& sk) if (librustzcash_check_diversifier(res.data())) { break; - } else if (blob[33] > 255) { + } else if (blob[33] == 255) { throw std::runtime_error("librustzcash_check_diversifier did not return valid diversifier"); } blob[33] += 1; From 521a6f1fc08527908606da8bd18c6a7ee2703e3c Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Fri, 1 Jun 2018 02:32:17 -0700 Subject: [PATCH 4/7] Minimal sapling key test --- src/gtest/test_keystore.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 22e009e76..8b4fc421a 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -7,6 +7,17 @@ #endif #include "zcash/Address.hpp" +TEST(keystore_tests, sapling_keys) { + auto sk = libzcash::SaplingSpendingKey::random(); + + // 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; From dd099401a344fdfdc6a907d253f6d3da06da3d4d Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Fri, 1 Jun 2018 04:03:27 -0700 Subject: [PATCH 5/7] Fix default_address() --- src/gtest/test_keystore.cpp | 1 + src/zcash/prf.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gtest/test_keystore.cpp b/src/gtest/test_keystore.cpp index 8b4fc421a..e767f74db 100644 --- a/src/gtest/test_keystore.cpp +++ b/src/gtest/test_keystore.cpp @@ -9,6 +9,7 @@ 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(); diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index 7a229d2fb..2491de83e 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -60,7 +60,7 @@ std::array default_diversifier(const uint256& sk) 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_update(&state, blob, 34); crypto_generichash_blake2b_final(&state, res.data(), 11); if (librustzcash_check_diversifier(res.data())) { From 0da9aac63e03c59e049eefd923f72b8ce415af32 Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Mon, 4 Jun 2018 04:59:07 -0700 Subject: [PATCH 6/7] s/SaplingInViewingKey/SaplingIncomingViewingKey --- src/zcash/Address.cpp | 6 +++--- src/zcash/Address.hpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 1e43281f1..3ed8b7184 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -55,17 +55,17 @@ SaplingFullViewingKey SaplingSpendingKey::full_viewing_key() const { return expanded_spending_key().full_viewing_key(); } -SaplingInViewingKey SaplingFullViewingKey::in_viewing_key() const { +SaplingIncomingViewingKey SaplingFullViewingKey::in_viewing_key() const { uint256 ivk; librustzcash_crh_ivk(ak.begin(), nk.begin(), ivk.begin()); - return SaplingInViewingKey(ivk); + return SaplingIncomingViewingKey(ivk); } SaplingSpendingKey SaplingSpendingKey::random() { return SaplingSpendingKey(random_uint256()); } -SaplingPaymentAddress SaplingInViewingKey::address(diversifier_t d) const { +SaplingPaymentAddress SaplingIncomingViewingKey::address(diversifier_t d) const { uint256 pk_d; librustzcash_ivk_to_pkd(this->begin(), d.data(), pk_d.begin()); return SaplingPaymentAddress(d, pk_d); diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index e710ae275..5d0a33cbf 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -125,10 +125,10 @@ public: } }; -class SaplingInViewingKey : public uint256 { +class SaplingIncomingViewingKey : public uint256 { public: - SaplingInViewingKey() : uint256() { } - SaplingInViewingKey(uint256 ivk) : uint256(ivk) { } + SaplingIncomingViewingKey() : uint256() { } + SaplingIncomingViewingKey(uint256 ivk) : uint256(ivk) { } // Can pass in diversifier for Sapling addr SaplingPaymentAddress address(diversifier_t d) const; @@ -152,7 +152,7 @@ public: READWRITE(ovk); } - SaplingInViewingKey in_viewing_key() const; + 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; From 87c9994cb2f782a185e4a109c36935612eb6a91f Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Mon, 4 Jun 2018 07:47:46 -0700 Subject: [PATCH 7/7] Make diversifier functions return option --- src/zcash/Address.cpp | 12 ++++++++---- src/zcash/Address.hpp | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index 3ed8b7184..43bd45ecd 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -65,13 +65,17 @@ SaplingSpendingKey SaplingSpendingKey::random() { return SaplingSpendingKey(random_uint256()); } -SaplingPaymentAddress SaplingIncomingViewingKey::address(diversifier_t d) const { +boost::optional SaplingIncomingViewingKey::address(diversifier_t d) const { uint256 pk_d; - librustzcash_ivk_to_pkd(this->begin(), d.data(), pk_d.begin()); - return SaplingPaymentAddress(d, 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; + } } -SaplingPaymentAddress SaplingSpendingKey::default_address() const { +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 5d0a33cbf..6b8c310f4 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -131,7 +131,7 @@ public: SaplingIncomingViewingKey(uint256 ivk) : uint256(ivk) { } // Can pass in diversifier for Sapling addr - SaplingPaymentAddress address(diversifier_t d) const; + boost::optional address(diversifier_t d) const; }; class SaplingFullViewingKey { @@ -206,7 +206,7 @@ public: SaplingFullViewingKey full_viewing_key() const; // Can derive Sapling addr from default diversifier - SaplingPaymentAddress default_address() const; + boost::optional default_address() const; }; }