New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sapling note encryption implementation #3324
Changes from all commits
9e8e121
9e1c2c4
90073ae
c03e226
4e1f2da
7478876
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,9 @@ | |
|
||
#include "zcash/NoteEncryption.hpp" | ||
#include "zcash/prf.h" | ||
#include "zcash/Address.hpp" | ||
#include "crypto/sha256.h" | ||
#include "librustzcash.h" | ||
|
||
class TestNoteDecryption : public ZCNoteDecryption { | ||
public: | ||
|
@@ -17,6 +19,189 @@ class TestNoteDecryption : public ZCNoteDecryption { | |
} | ||
}; | ||
|
||
TEST(noteencryption, SaplingApi) | ||
{ | ||
using namespace libzcash; | ||
|
||
// Create recipient addresses | ||
auto sk = SaplingSpendingKey(uint256()).expanded_spending_key(); | ||
auto vk = sk.full_viewing_key(); | ||
auto ivk = vk.in_viewing_key(); | ||
SaplingPaymentAddress pk_1 = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); | ||
SaplingPaymentAddress pk_2 = *ivk.address({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); | ||
|
||
// Blob of stuff we're encrypting | ||
std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> message; | ||
for (size_t i = 0; i < ZC_SAPLING_ENCPLAINTEXT_SIZE; i++) { | ||
// Fill the message with dummy data | ||
message[i] = (unsigned char) i; | ||
} | ||
|
||
std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> small_message; | ||
for (size_t i = 0; i < ZC_SAPLING_OUTPLAINTEXT_SIZE; i++) { | ||
// Fill the message with dummy data | ||
small_message[i] = (unsigned char) i; | ||
} | ||
|
||
// Invalid diversifier | ||
ASSERT_EQ(boost::none, SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); | ||
|
||
// Encrypt to pk_1 | ||
auto enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d); | ||
auto ciphertext_1 = *enc.encrypt_to_recipient( | ||
pk_1.pk_d, | ||
message | ||
); | ||
auto epk_1 = enc.get_epk(); | ||
{ | ||
uint256 test_epk; | ||
uint256 test_esk = enc.get_esk(); | ||
ASSERT_TRUE(librustzcash_sapling_ka_derivepublic(pk_1.d.begin(), test_esk.begin(), test_epk.begin())); | ||
ASSERT_TRUE(test_epk == epk_1); | ||
} | ||
auto cv_1 = random_uint256(); | ||
auto cm_1 = random_uint256(); | ||
auto out_ciphertext_1 = enc.encrypt_to_ourselves( | ||
sk.ovk, | ||
cv_1, | ||
cm_1, | ||
small_message | ||
); | ||
|
||
// Encrypt to pk_2 | ||
enc = *SaplingNoteEncryption::FromDiversifier(pk_2.d); | ||
auto ciphertext_2 = *enc.encrypt_to_recipient( | ||
pk_2.pk_d, | ||
message | ||
); | ||
auto epk_2 = enc.get_epk(); | ||
|
||
auto cv_2 = random_uint256(); | ||
auto cm_2 = random_uint256(); | ||
auto out_ciphertext_2 = enc.encrypt_to_ourselves( | ||
sk.ovk, | ||
cv_2, | ||
cm_2, | ||
small_message | ||
); | ||
|
||
// Test nonce-reuse resistance of API | ||
{ | ||
auto tmp_enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d); | ||
|
||
tmp_enc.encrypt_to_recipient( | ||
pk_1.pk_d, | ||
message | ||
); | ||
|
||
ASSERT_THROW(tmp_enc.encrypt_to_recipient( | ||
pk_1.pk_d, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we change this line to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's the intent of the nonce reuse resistant API; if you call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Note that |
||
message | ||
), std::logic_error); | ||
|
||
tmp_enc.encrypt_to_ourselves( | ||
sk.ovk, | ||
cv_2, | ||
cm_2, | ||
small_message | ||
); | ||
|
||
ASSERT_THROW(tmp_enc.encrypt_to_ourselves( | ||
sk.ovk, | ||
cv_2, | ||
cm_2, | ||
small_message | ||
), std::logic_error); | ||
} | ||
|
||
// Try to decrypt | ||
auto plaintext_1 = *AttemptSaplingEncDecryption( | ||
ciphertext_1, | ||
ivk, | ||
epk_1 | ||
); | ||
ASSERT_TRUE(message == plaintext_1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
auto small_plaintext_1 = *AttemptSaplingOutDecryption( | ||
out_ciphertext_1, | ||
sk.ovk, | ||
cv_1, | ||
cm_1, | ||
epk_1 | ||
); | ||
ASSERT_TRUE(small_message == small_plaintext_1); | ||
|
||
auto plaintext_2 = *AttemptSaplingEncDecryption( | ||
ciphertext_2, | ||
ivk, | ||
epk_2 | ||
); | ||
ASSERT_TRUE(message == plaintext_2); | ||
|
||
auto small_plaintext_2 = *AttemptSaplingOutDecryption( | ||
out_ciphertext_2, | ||
sk.ovk, | ||
cv_2, | ||
cm_2, | ||
epk_2 | ||
); | ||
ASSERT_TRUE(small_message == small_plaintext_2); | ||
|
||
// Try to decrypt out ciphertext with wrong key material | ||
ASSERT_FALSE(AttemptSaplingOutDecryption( | ||
out_ciphertext_1, | ||
random_uint256(), | ||
cv_1, | ||
cm_1, | ||
epk_1 | ||
)); | ||
ASSERT_FALSE(AttemptSaplingOutDecryption( | ||
out_ciphertext_1, | ||
sk.ovk, | ||
random_uint256(), | ||
cm_1, | ||
epk_1 | ||
)); | ||
ASSERT_FALSE(AttemptSaplingOutDecryption( | ||
out_ciphertext_1, | ||
sk.ovk, | ||
cv_1, | ||
random_uint256(), | ||
epk_1 | ||
)); | ||
ASSERT_FALSE(AttemptSaplingOutDecryption( | ||
out_ciphertext_1, | ||
sk.ovk, | ||
cv_1, | ||
cm_1, | ||
random_uint256() | ||
)); | ||
|
||
// Try to decrypt with wrong ephemeral key | ||
ASSERT_FALSE(AttemptSaplingEncDecryption( | ||
ciphertext_1, | ||
ivk, | ||
epk_2 | ||
)); | ||
ASSERT_FALSE(AttemptSaplingEncDecryption( | ||
ciphertext_2, | ||
ivk, | ||
epk_1 | ||
)); | ||
|
||
// Try to decrypt with wrong ivk | ||
ASSERT_FALSE(AttemptSaplingEncDecryption( | ||
ciphertext_1, | ||
uint256(), | ||
epk_1 | ||
)); | ||
ASSERT_FALSE(AttemptSaplingEncDecryption( | ||
ciphertext_2, | ||
uint256(), | ||
epk_2 | ||
)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also test malleating the ciphertext. |
||
|
||
TEST(noteencryption, api) | ||
{ | ||
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07"))); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style. I prefer using
.get()
rather than*ivk