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

Merged
merged 6 commits into from Jul 11, 2018

Conversation

5 participants
@ebfull
Copy link
Contributor

ebfull commented Jun 6, 2018

Closes #3055

Implemented along with @gtank and @Eirik0

DH key exchange was implemented in zcash/librustzcash#18

@bitcartel bitcartel referenced this pull request Jun 8, 2018

Merged

Add SaplingNote class #3272

@str4d str4d added this to the v2.0.0 milestone Jun 13, 2018

@str4d str4d added this to In Progress in Zcashd Team Jun 13, 2018

@ebfull ebfull force-pushed the ebfull:sapling-note-encryption branch from 1b79f3b to 9526d2c Jun 14, 2018

@ebfull ebfull force-pushed the ebfull:sapling-note-encryption branch from 9526d2c to 90073ae Jun 14, 2018

@ebfull ebfull changed the title [WIP] Sapling note encryption implementation Sapling note encryption implementation Jun 14, 2018

@str4d str4d moved this from In Progress to In Review in Zcashd Team Jun 15, 2018

@bitcartel

This comment has been minimized.

Copy link
Contributor

bitcartel commented Jun 15, 2018

@zkbot try

@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jun 15, 2018

⌛️ Trying commit c03e226 with merge b69131d...

zkbot added a commit that referenced this pull request Jun 15, 2018

Auto merge of #3324 - ebfull:sapling-note-encryption, r=<try>
Sapling note encryption implementation

Closes #3055

Implemented along with @gtank and @Eirik0

DH key exchange was implemented in zcash/librustzcash#18
@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jun 15, 2018

💔 Test failed - pr-try

@ebfull

This comment has been minimized.

Copy link
Contributor

ebfull commented Jun 16, 2018

@zkbot retry

@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jun 16, 2018

⌛️ Trying commit c03e226 with merge ef181f0...

zkbot added a commit that referenced this pull request Jun 16, 2018

Auto merge of #3324 - ebfull:sapling-note-encryption, r=<try>
Sapling note encryption implementation

Closes #3055

Implemented along with @gtank and @Eirik0

DH key exchange was implemented in zcash/librustzcash#18
@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jun 16, 2018

☀️ Test successful - pr-try
State: approved= try=True

@str4d

str4d approved these changes Jun 19, 2018

Copy link
Contributor

str4d left a comment

ut(ACK+cov), assuming that my comments are addressed (either in this PR, or in a subsequent one).

#define NOTEENCRYPTION_AUTH_BYTES 16
// Ciphertext for the recipient to decrypt
typedef std::array<unsigned char, ZC_SAPLING_ENCCIPHERTEXT_SIZE> SaplingEncCiphertext;
typedef std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> SaplingEncPlaintext;

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

SaplingEncPlaintext should be a subclass of BaseNotePlaintext, with appropriate internal structure and serialization.

This comment has been minimized.

@ebfull

ebfull Jun 19, 2018

Contributor

These are typedefs just like the Sprout ciphertexts/plaintexts effectively are.

This comment has been minimized.

@str4d

str4d Jun 20, 2018

Contributor

Sprout ciphertexts are typedefs. Sprout plaintexts are classes, specifically subclasses of BaseNotePlaintext.

This comment has been minimized.

@bitcartel

bitcartel Jun 21, 2018

Contributor

I think @ebfull is talking about these Sprout typedefs: https://github.com/zcash/zcash/blob/master/src/zcash/NoteEncryption.hpp#L31

    typedef std::array<unsigned char, CLEN> Ciphertext;
    typedef std::array<unsigned char, MLEN> Plaintext;

@str4d Yes, there will be a subclass, I had one sketched out before this PR started, see snippet here:

class SaplingNotePlaintext : public BaseNotePlaintext {
public:
    diversifier_t d;
    uint256 r;

// Ciphertext for outgoing viewing key to decrypt
typedef std::array<unsigned char, ZC_SAPLING_OUTCIPHERTEXT_SIZE> SaplingOutCiphertext;
typedef std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> SaplingOutPlaintext;

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

SaplingOutPlaintext should be a class with appropriate internal structure and serialization.

@@ -13,6 +14,58 @@ void clamp_curve25519(unsigned char key[crypto_scalarmult_SCALARBYTES])
key[31] |= 64;
}

void PRF_ock(

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

Move this into src/zcash/prf.cpp (below PRF_ovk).

@@ -13,6 +14,58 @@ void clamp_curve25519(unsigned char key[crypto_scalarmult_SCALARBYTES])
key[31] |= 64;
}

void PRF_ock(
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

Name this ock.

@@ -17,6 +18,154 @@ class TestNoteDecryption : public ZCNoteDecryption {
}
};

TEST(noteencryption, sapling_api)

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

No underscores in test names.

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

That gets us every time.

}

// Invalid diversifier
ASSERT_FALSE(SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));

This comment has been minimized.

@str4d

str4d Jun 19, 2018

Contributor

Does this work? I seem to recall that boost::optional doesn't get correctly coerced to a boolean in Boost Test's macros; does it work in Google Test?

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

The test passes but is the behaviour correct? Hmm. To remove any doubt, consider using a variable to store the boost::optional and then assert on the value of the variable itself.

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

Violation of substitutability, and for a pure expression at that, makes me very sad.

image

@bitcartel
Copy link
Contributor

bitcartel left a comment

Looks good, some minor points raised.

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

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

Style. I prefer using .get() rather than *ivk

}

// Invalid diversifier
ASSERT_FALSE(SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

The test passes but is the behaviour correct? Hmm. To remove any doubt, consider using a variable to store the boost::optional and then assert on the value of the variable itself.

ivk,
epk_1
);
ASSERT_TRUE(message == plaintext_1);

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

== on arrays is deprecated in C++20 but since we use this syntax everywhere, we can deal with it later when we get to C++17...

memcpy(block+96, epk.begin(), 32);

unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "Zcash_Derive_ock", 16);

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

Where is this in the protocol spec? Don't see it in the current revision (from 28 days ago, dated May 22)

This comment has been minimized.

@ebfull

This comment has been minimized.

@daira
memcpy(block+32, epk.begin(), 32);

unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "Zcash_SaplingKDF", 16);

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

Use constexpr or macro for "Zcash_SaplingKDF"

memcpy(block+96, epk.begin(), 32);

unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "Zcash_Derive_ock", 16);

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

Use constexpr or macro for "Zcash_Derive_ock"

uint256 esk;

SaplingNoteEncryption(uint256 epk, uint256 esk) : epk(epk), esk(esk) {

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

nit: remove redundant line of whitespace, and have { } on the same line after function sig.

);

ASSERT_THROW(tmp_enc.encrypt_to_recipient(
pk_1.pk_d,

This comment has been minimized.

@bitcartel

bitcartel Jun 19, 2018

Contributor

If we change this line to pk_2.pk_d the call still throws the same error due to already_encrypted_enc having been set, so we're not really testing nonce reuse, but the fact that encrypt_to_recipient can be only called once successfully.

This comment has been minimized.

@ebfull

ebfull Jun 19, 2018

Contributor

That's the intent of the nonce reuse resistant API; if you call encrypt_to_recipient twice (which you should never do, ephemeral keys are not reused) then the ChaCha20 nonce will be reused, which we cannot allow.

This comment has been minimized.

@bitcartel

bitcartel Jun 20, 2018

Contributor

Ok. Note that encrypt_to_recipient could be called twice because the method is not thread safe with regards to the boolean flag. Perhaps document that the note encryption class should not be used by multiple threads concurrently.

@str4d str4d moved this from In Review to In Progress in Zcashd Team Jun 22, 2018

@daira

This comment has been minimized.

Copy link
Contributor

daira commented Jun 22, 2018

Currently reviewing this for consistency with the spec (since there were fairly recent spec changes), so please hold off merging for a bit.

@daira
Copy link
Contributor

daira left a comment

Flushing pending comments.

}

// Invalid diversifier
ASSERT_FALSE(SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

Violation of substitutability, and for a pure expression at that, makes me very sad.

image

uint256(),
epk_2
));
}

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

Also test malleating the ciphertext.

unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
const uint256 &ovk,
const uint256 &cv,
const uint256 &cm,

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

This is now called cmu. (It was always the u-coordinate.)

memcpy(block+96, epk.begin(), 32);

unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "Zcash_Derive_ock", 16);

This comment has been minimized.

@daira
uint256 esk;

// Pick random esk
librustzcash_sapling_generate_r(esk.begin());

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

Note that the beta-21 spec says that esk should not be 0. (This is a consequence of the implementation already checking that epk is not of small order.) This has negligible probability so it's not so important.


// Construct the symmetric key
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
KDF_Sapling(K, dhsecret, epk);

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

This puts the output parameter first, whereas for librustzcash_sapling_ka_agree for example it is last. I don't know which one should change; crypto_aead_chacha20poly1305_ietf_encrypt also puts the output parameter first.

SaplingOutCiphertext SaplingNoteEncryption::encrypt_to_ourselves(
const uint256 &ovk,
const uint256 &cv,
const uint256 &cm,

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

This appears to also be cmu, given that it's passed directly to PRF_ock.

@daira

daira approved these changes Jun 22, 2018

Copy link
Contributor

daira left a comment

ACK as far as it goes, but missing a lot of the checks in the spec. I assume these will be in another PR?

const SaplingOutCiphertext &ciphertext,
const uint256 &ovk,
const uint256 &cv,
const uint256 &cm,

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

Also cmu.

return boost::none;
}

return plaintext;

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

This only does the Cout decryption. In the spec (section 4.17.3), that is combined with:

  • checking esk and pkd
  • reconstructing the sharedSecret and using it to decrypt Cenc
  • checking rcm and gd
  • checking that epk is consistent with gd and esk
  • checking the commitment of the reconstructed note against cmu.
return boost::none;
}

return plaintext;

This comment has been minimized.

@daira

daira Jun 22, 2018

Contributor

This only does the Cenc decryption. In the spec (section 4.17.2), that is combined with:

  • checking rcm and gd
  • checking the commitment of the reconstructed note against cmu.
@ebfull

This comment has been minimized.

Copy link
Contributor

ebfull commented Jun 23, 2018

Yup, this abstraction does not deal with checks of the contents, only encrypting and decrypting.

@str4d str4d modified the milestones: v1.1.2, v2.0.0 Jul 2, 2018

@bitcartel

This comment has been minimized.

Copy link
Contributor

bitcartel commented Jul 11, 2018

I've reviewed minor edits in pairing with @ebfull

@ebfull

This comment has been minimized.

Copy link
Contributor

ebfull commented Jul 11, 2018

Comments about renaming and refactorings will be addressed at a later date, and this PR is not responsible for plaintext serialization and the other kinds of Receive-like checks, just the encryption/decryption.

@zkbot r+

@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jul 11, 2018

📌 Commit 7478876 has been approved by ebfull

@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jul 11, 2018

⌛️ Testing commit 7478876 with merge d86f60f...

zkbot added a commit that referenced this pull request Jul 11, 2018

Auto merge of #3324 - ebfull:sapling-note-encryption, r=ebfull
Sapling note encryption implementation

Closes #3055

Implemented along with @gtank and @Eirik0

DH key exchange was implemented in zcash/librustzcash#18
@zkbot

This comment has been minimized.

Copy link
Collaborator

zkbot commented Jul 11, 2018

☀️ Test successful - pr-merge
Approved by: ebfull
Pushing d86f60f to master...

@zkbot zkbot merged commit 7478876 into zcash:master Jul 11, 2018

1 check passed

homu Test successful
Details

Zcashd Team automation moved this from In Progress to Released (Merged in Master) Jul 11, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment