From defe37a6d41ea1cc24570a86a80d218f2ee35de4 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 16 May 2016 09:50:31 -0600 Subject: [PATCH] Enforce first four bits are zero for all spending keys and phi. --- src/Makefile.am | 1 + src/gtest/test_joinsplit.cpp | 2 +- src/gtest/test_noteencryption.cpp | 36 ++++++++++++++--------- src/uint252.h | 48 +++++++++++++++++++++++++++++++ src/zcash/Address.cpp | 2 +- src/zcash/Address.hpp | 7 +++-- src/zcash/JoinSplit.cpp | 8 +++--- src/zcash/JoinSplit.hpp | 3 +- src/zcash/NoteEncryption.cpp | 10 ++++++- src/zcash/NoteEncryption.hpp | 4 ++- src/zcash/circuit/gadget.tcc | 4 +-- src/zcash/circuit/note.tcc | 2 +- src/zcash/circuit/utils.tcc | 13 ++++++++- src/zcash/prf.cpp | 14 ++++----- src/zcash/prf.h | 11 +++---- 15 files changed, 123 insertions(+), 42 deletions(-) create mode 100644 src/uint252.h diff --git a/src/Makefile.am b/src/Makefile.am index 6cb0f89b3..a6966b012 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,6 +155,7 @@ BITCOIN_CORE_H = \ txmempool.h \ ui_interface.h \ uint256.h \ + uint252.h \ undo.h \ util.h \ utilmoneystr.h \ diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp index 96e887e77..7711dc998 100644 --- a/src/gtest/test_joinsplit.cpp +++ b/src/gtest/test_joinsplit.cpp @@ -257,7 +257,7 @@ TEST(joinsplit, full_api_test) TEST(joinsplit, note_plaintexts) { - uint256 a_sk = uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e"); + uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e")); uint256 a_pk = PRF_addr_a_pk(a_sk); uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk); uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc); diff --git a/src/gtest/test_noteencryption.cpp b/src/gtest/test_noteencryption.cpp index bd4ca38ff..29c44f37b 100644 --- a/src/gtest/test_noteencryption.cpp +++ b/src/gtest/test_noteencryption.cpp @@ -18,7 +18,7 @@ public: TEST(noteencryption, api) { - uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77")); + uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07"))); uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc); ZCNoteEncryption b = ZCNoteEncryption(uint256()); @@ -66,7 +66,7 @@ TEST(noteencryption, api) { // Test wrong private key - uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint256()); + uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint252()); ZCNoteDecryption decrypter(sk_enc_2); ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error); @@ -100,9 +100,10 @@ TEST(noteencryption, api) uint256 test_prf( unsigned char distinguisher, - uint256 x, + uint252 seed_x, uint256 y ) { + uint256 x = seed_x.inner(); *x.begin() &= 0x0f; *x.begin() |= distinguisher; CSHA256 hasher; @@ -117,7 +118,7 @@ uint256 test_prf( TEST(noteencryption, prf_addr) { for (size_t i = 0; i < 100; i++) { - uint256 a_sk = libzcash::random_uint256(); + uint252 a_sk = libzcash::random_uint252(); uint256 rest; ASSERT_TRUE( test_prf(0xc0, a_sk, rest) == PRF_addr_a_pk(a_sk) @@ -125,7 +126,7 @@ TEST(noteencryption, prf_addr) } for (size_t i = 0; i < 100; i++) { - uint256 a_sk = libzcash::random_uint256(); + uint252 a_sk = libzcash::random_uint252(); uint256 rest; *rest.begin() = 0x01; ASSERT_TRUE( @@ -137,7 +138,7 @@ TEST(noteencryption, prf_addr) TEST(noteencryption, prf_nf) { for (size_t i = 0; i < 100; i++) { - uint256 a_sk = libzcash::random_uint256(); + uint252 a_sk = libzcash::random_uint252(); uint256 rho = libzcash::random_uint256(); ASSERT_TRUE( test_prf(0xe0, a_sk, rho) == PRF_nf(a_sk, rho) @@ -148,7 +149,7 @@ TEST(noteencryption, prf_nf) TEST(noteencryption, prf_pk) { for (size_t i = 0; i < 100; i++) { - uint256 a_sk = libzcash::random_uint256(); + uint252 a_sk = libzcash::random_uint252(); uint256 h_sig = libzcash::random_uint256(); ASSERT_TRUE( test_prf(0x00, a_sk, h_sig) == PRF_pk(a_sk, 0, h_sig) @@ -156,21 +157,22 @@ TEST(noteencryption, prf_pk) } for (size_t i = 0; i < 100; i++) { - uint256 a_sk = libzcash::random_uint256(); + uint252 a_sk = libzcash::random_uint252(); uint256 h_sig = libzcash::random_uint256(); ASSERT_TRUE( test_prf(0x40, a_sk, h_sig) == PRF_pk(a_sk, 1, h_sig) ); } - uint256 dummy; - ASSERT_THROW(PRF_pk(dummy, 2, dummy), std::domain_error); + uint252 dummy_a; + uint256 dummy_b; + ASSERT_THROW(PRF_pk(dummy_a, 2, dummy_b), std::domain_error); } TEST(noteencryption, prf_rho) { for (size_t i = 0; i < 100; i++) { - uint256 phi = libzcash::random_uint256(); + uint252 phi = libzcash::random_uint252(); uint256 h_sig = libzcash::random_uint256(); ASSERT_TRUE( test_prf(0x20, phi, h_sig) == PRF_rho(phi, 0, h_sig) @@ -178,13 +180,19 @@ TEST(noteencryption, prf_rho) } for (size_t i = 0; i < 100; i++) { - uint256 phi = libzcash::random_uint256(); + uint252 phi = libzcash::random_uint252(); uint256 h_sig = libzcash::random_uint256(); ASSERT_TRUE( test_prf(0x60, phi, h_sig) == PRF_rho(phi, 1, h_sig) ); } - uint256 dummy; - ASSERT_THROW(PRF_rho(dummy, 2, dummy), std::domain_error); + uint252 dummy_a; + uint256 dummy_b; + ASSERT_THROW(PRF_rho(dummy_a, 2, dummy_b), std::domain_error); } + +TEST(noteencryption, uint252) +{ + ASSERT_THROW(uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e")), std::domain_error); +} \ No newline at end of file diff --git a/src/uint252.h b/src/uint252.h new file mode 100644 index 000000000..c5ab1a380 --- /dev/null +++ b/src/uint252.h @@ -0,0 +1,48 @@ +#ifndef UINT252_H +#define UINT252_H + +#include +#include "uint256.h" +#include "serialize.h" + +// Wrapper of uint256 with guarantee that first +// four bits are zero. +class uint252 { +private: + uint256 contents; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(contents); + + if ((*contents.begin()) & 0xF0) { + throw std::ios_base::failure("spending key has invalid leading bits"); + } + } + + const unsigned char* begin() const + { + return contents.begin(); + } + + const unsigned char* end() const + { + return contents.end(); + } + + uint252() : contents() {}; + explicit uint252(const uint256& in) : contents(in) { + if (*contents.begin() & 0xF0) { + throw std::domain_error("leading bits are set in argument given to uint252 constructor"); + } + } + + uint256 inner() const { + return contents; + } +}; + +#endif diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index d7438ee2b..446a63db1 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -13,7 +13,7 @@ ViewingKey SpendingKey::viewing_key() { } SpendingKey SpendingKey::random() { - return SpendingKey(random_uint256()); + return SpendingKey(random_uint252()); } PaymentAddress SpendingKey::address() { diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index a240ace7f..91e35bd03 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -2,6 +2,7 @@ #define _ZCADDRESS_H_ #include "uint256.h" +#include "uint252.h" #include "serialize.h" namespace libzcash { @@ -37,10 +38,10 @@ public: uint256 pk_enc(); }; -class SpendingKey : public uint256 { +class SpendingKey : public uint252 { public: - SpendingKey() : uint256() { } - SpendingKey(uint256 a_sk) : uint256(a_sk) { } + SpendingKey() : uint252() { } + SpendingKey(uint252 a_sk) : uint252(a_sk) { } static SpendingKey random(); diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 05da1e87a..72a634b19 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -191,7 +191,7 @@ public: uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, pubKeyHash); // Sample phi - uint256 phi = random_uint256(); + uint252 phi = random_uint252(); // Compute notes for outputs for (size_t i = 0; i < NumOutputs; i++) { @@ -320,19 +320,19 @@ uint256 JoinSplit::h_sig( return output; } -Note JSOutput::note(const uint256& phi, const uint256& r, size_t i, const uint256& h_sig) const { +Note JSOutput::note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const { uint256 rho = PRF_rho(phi, i, h_sig); return Note(addr.a_pk, value, rho, r); } JSOutput::JSOutput() : addr(uint256(), uint256()), value(0) { - SpendingKey a_sk(random_uint256()); + SpendingKey a_sk = SpendingKey::random(); addr = a_sk.address(); } JSInput::JSInput() : witness(ZCIncrementalMerkleTree().witness()), - key(random_uint256()) { + key(SpendingKey::random()) { note = Note(key.address().a_pk, 0, random_uint256(), random_uint256()); ZCIncrementalMerkleTree dummy_tree; dummy_tree.append(note.cm()); diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index bd06c7ecb..bf824e1cb 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -8,6 +8,7 @@ #include "NoteEncryption.hpp" #include "uint256.h" +#include "uint252.h" #include @@ -37,7 +38,7 @@ public: JSOutput(); JSOutput(PaymentAddress addr, uint64_t value) : addr(addr), value(value) { } - Note note(const uint256& phi, const uint256& r, size_t i, const uint256& h_sig) const; + Note note(const uint252& phi, const uint256& r, size_t i, const uint256& h_sig) const; }; template diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index f7d0977dc..812dd22b5 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -136,7 +136,7 @@ typename NoteDecryption::Plaintext NoteDecryption::decrypt } template -uint256 NoteEncryption::generate_privkey(const uint256 &a_sk) +uint256 NoteEncryption::generate_privkey(const uint252 &a_sk) { uint256 sk = PRF_addr_sk_enc(a_sk); @@ -165,6 +165,14 @@ uint256 random_uint256() return ret; } +uint252 random_uint252() +{ + uint256 rand = random_uint256(); + (*rand.begin()) &= 0x0F; + + return uint252(rand); +} + template class NoteEncryption; template class NoteDecryption; diff --git a/src/zcash/NoteEncryption.hpp b/src/zcash/NoteEncryption.hpp index d5a831ad5..ac8fb321f 100644 --- a/src/zcash/NoteEncryption.hpp +++ b/src/zcash/NoteEncryption.hpp @@ -8,6 +8,7 @@ https://github.com/zcash/zips/blob/master/protocol/protocol.pdf #include #include "uint256.h" +#include "uint252.h" #include "zcash/Zcash.h" @@ -43,7 +44,7 @@ public: ); // Creates a NoteEncryption private key - static uint256 generate_privkey(const uint256 &a_sk); + static uint256 generate_privkey(const uint252 &a_sk); // Creates a NoteEncryption public key from a private key static uint256 generate_pubkey(const uint256 &sk_enc); @@ -70,6 +71,7 @@ public: }; uint256 random_uint256(); +uint252 random_uint252(); } diff --git a/src/zcash/circuit/gadget.tcc b/src/zcash/circuit/gadget.tcc index a79ad75c6..141ec834e 100644 --- a/src/zcash/circuit/gadget.tcc +++ b/src/zcash/circuit/gadget.tcc @@ -187,7 +187,7 @@ public: } void generate_r1cs_witness( - const uint256& phi, + const uint252& phi, const uint256& rt, const uint256& h_sig, const boost::array& inputs, @@ -234,7 +234,7 @@ public: // Witness phi zk_phi->bits.fill_with_bits( this->pb, - trailing252(uint256_to_bool_vector(phi)) + uint252_to_bool_vector(phi) ); // Witness h_sig diff --git a/src/zcash/circuit/note.tcc b/src/zcash/circuit/note.tcc index c011db175..87912b348 100644 --- a/src/zcash/circuit/note.tcc +++ b/src/zcash/circuit/note.tcc @@ -131,7 +131,7 @@ public: // Witness a_sk for the input a_sk->bits.fill_with_bits( this->pb, - trailing252(uint256_to_bool_vector(key)) + uint252_to_bool_vector(key) ); // Witness a_pk for a_sk with PRF_addr diff --git a/src/zcash/circuit/utils.tcc b/src/zcash/circuit/utils.tcc index 24ffe1b94..c54abc56b 100644 --- a/src/zcash/circuit/utils.tcc +++ b/src/zcash/circuit/utils.tcc @@ -1,3 +1,5 @@ +#include "uint252.h" + template pb_variable_array from_bits(std::vector bits, pb_variable& ZERO) { pb_variable_array acc; @@ -17,7 +19,8 @@ std::vector trailing252(std::vector input) { return std::vector(input.begin() + 4, input.end()); } -std::vector uint256_to_bool_vector(uint256 input) { +template +std::vector to_bool_vector(T input) { std::vector input_v(input.begin(), input.end()); std::vector output_bv(256, 0); libzerocash::convertBytesVectorToVector( @@ -28,6 +31,14 @@ std::vector uint256_to_bool_vector(uint256 input) { return output_bv; } +std::vector uint256_to_bool_vector(uint256 input) { + return to_bool_vector(input); +} + +std::vector uint252_to_bool_vector(uint252 input) { + return trailing252(to_bool_vector(input)); +} + std::vector uint64_to_bool_vector(uint64_t input) { auto num_bv = convertIntToVectorLE(input); std::vector num_v(64, 0); diff --git a/src/zcash/prf.cpp b/src/zcash/prf.cpp index a3fcdfe32..d0f1dc4ec 100644 --- a/src/zcash/prf.cpp +++ b/src/zcash/prf.cpp @@ -2,7 +2,7 @@ #include "crypto/sha256.h" uint256 PRF(bool a, bool b, bool c, bool d, - const uint256& x, + const uint252& x, const uint256& y) { uint256 res; @@ -21,7 +21,7 @@ uint256 PRF(bool a, bool b, bool c, bool d, return res; } -uint256 PRF_addr(const uint256& a_sk, unsigned char t) +uint256 PRF_addr(const uint252& a_sk, unsigned char t) { uint256 y; *(y.begin()) = t; @@ -29,22 +29,22 @@ uint256 PRF_addr(const uint256& a_sk, unsigned char t) return PRF(1, 1, 0, 0, a_sk, y); } -uint256 PRF_addr_a_pk(const uint256& a_sk) +uint256 PRF_addr_a_pk(const uint252& a_sk) { return PRF_addr(a_sk, 0); } -uint256 PRF_addr_sk_enc(const uint256& a_sk) +uint256 PRF_addr_sk_enc(const uint252& a_sk) { return PRF_addr(a_sk, 1); } -uint256 PRF_nf(const uint256& a_sk, const uint256& rho) +uint256 PRF_nf(const uint252& a_sk, const uint256& rho) { return PRF(1, 1, 1, 0, a_sk, rho); } -uint256 PRF_pk(const uint256& a_sk, size_t i0, const uint256& h_sig) +uint256 PRF_pk(const uint252& a_sk, size_t i0, const uint256& h_sig) { if ((i0 != 0) && (i0 != 1)) { throw std::domain_error("PRF_pk invoked with index out of bounds"); @@ -53,7 +53,7 @@ uint256 PRF_pk(const uint256& a_sk, size_t i0, const uint256& h_sig) return PRF(0, i0, 0, 0, a_sk, h_sig); } -uint256 PRF_rho(const uint256& phi, size_t i0, const uint256& h_sig) +uint256 PRF_rho(const uint252& phi, size_t i0, const uint256& h_sig) { if ((i0 != 0) && (i0 != 1)) { throw std::domain_error("PRF_rho invoked with index out of bounds"); diff --git a/src/zcash/prf.h b/src/zcash/prf.h index 445a39898..c6cb45384 100644 --- a/src/zcash/prf.h +++ b/src/zcash/prf.h @@ -7,11 +7,12 @@ within the zkSNARK circuit. #define _PRF_H_ #include "uint256.h" +#include "uint252.h" -uint256 PRF_addr_a_pk(const uint256& a_sk); -uint256 PRF_addr_sk_enc(const uint256& a_sk); -uint256 PRF_nf(const uint256& a_sk, const uint256& rho); -uint256 PRF_pk(const uint256& a_sk, size_t i0, const uint256& h_sig); -uint256 PRF_rho(const uint256& phi, size_t i0, const uint256& h_sig); +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); #endif // _PRF_H_