Skip to content

Commit

Permalink
Auto merge of zcash#3324 - ebfull:sapling-note-encryption, r=ebfull
Browse files Browse the repository at this point in the history
Sapling note encryption implementation

Closes zcash#3055

Implemented along with @gtank and @Eirik0

DH key exchange was implemented in zcash/librustzcash#18
  • Loading branch information
zkbot committed Jul 11, 2018
2 parents 579ad3b + 7478876 commit d86f60f
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 22 deletions.
4 changes: 2 additions & 2 deletions depends/packages/librustzcash.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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=5231145ea6abf61092c21b6770baf3af65994f83dff96b10118ba5dd53451f26
$(package)_git_commit=0af1ce8bf121e1ad367db907c39d214581e270a6
$(package)_sha256_hash=5a50aae38a9ef4823cd319278ad95706a129cc091e1cca9342802f1ff75aba15
$(package)_git_commit=93e26d1d8716ac88f8bb372d442315edcd2deabd
$(package)_dependencies=rust $(rust_crates)
$(package)_patches=cargo.config

Expand Down
185 changes: 185 additions & 0 deletions src/gtest/test_noteencryption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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,
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);

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
));
}

TEST(noteencryption, api)
{
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
Expand Down
20 changes: 2 additions & 18 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,33 +84,17 @@ class SpendDescription
}
};

static constexpr size_t SAPLING_ENC_CIPHERTEXT_SIZE = (
1 + // leading byte
11 + // d
8 + // value
32 + // rcm
ZC_MEMO_SIZE + // memo
NOTEENCRYPTION_AUTH_BYTES);

static constexpr size_t SAPLING_OUT_CIPHERTEXT_SIZE = (
32 + // pkd_new
32 + // esk
NOTEENCRYPTION_AUTH_BYTES);

/**
* A shielded output to a transaction. It contains data that describes an Output transfer.
*/
class OutputDescription
{
public:
typedef std::array<unsigned char, SAPLING_ENC_CIPHERTEXT_SIZE> sapling_enc_ct_t; // TODO: Replace with actual type
typedef std::array<unsigned char, SAPLING_OUT_CIPHERTEXT_SIZE> sapling_out_ct_t; // TODO: Replace with actual type

uint256 cv; //!< A value commitment to the value of the output note.
uint256 cm; //!< The note commitment for the output note.
uint256 ephemeralKey; //!< A Jubjub public key.
sapling_enc_ct_t encCiphertext; //!< A ciphertext component for the encrypted output note.
sapling_out_ct_t outCiphertext; //!< A ciphertext component for the encrypted output note.
libzcash::SaplingEncCiphertext encCiphertext; //!< A ciphertext component for the encrypted output note.
libzcash::SaplingOutCiphertext outCiphertext; //!< A ciphertext component for the encrypted output note.
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the output circuit.

OutputDescription() { }
Expand Down
3 changes: 2 additions & 1 deletion src/zcash/Address.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "uint256.h"
#include "uint252.h"
#include "serialize.h"
#include "Zcash.h"

#include <boost/variant.hpp>

Expand All @@ -18,7 +19,7 @@ const size_t SerializedPaymentAddressSize = 64;
const size_t SerializedViewingKeySize = 64;
const size_t SerializedSpendingKeySize = 32;

typedef std::array<unsigned char, 11> diversifier_t;
typedef std::array<unsigned char, ZC_DIVERSIFIER_SIZE> diversifier_t;

class SproutPaymentAddress {
public:
Expand Down
Loading

0 comments on commit d86f60f

Please sign in to comment.