diff --git a/src/gtest/test_noteencryption.cpp b/src/gtest/test_noteencryption.cpp index 9fa7c466f..2861e0983 100644 --- a/src/gtest/test_noteencryption.cpp +++ b/src/gtest/test_noteencryption.cpp @@ -111,6 +111,25 @@ TEST(noteencryption, NotePlaintext) ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d); ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk); + + // Test sender can decrypt the note ciphertext. + foo = SaplingNotePlaintext::decryptUsingFullViewingKey( + ct, + epk, + decrypted_out_ct_unwrapped.esk, + decrypted_out_ct_unwrapped.pk_d + ); + + if (!foo) { + FAIL(); + } + + bar = foo.get(); + + ASSERT_TRUE(bar.value() == pt.value()); + ASSERT_TRUE(bar.memo() == pt.memo()); + ASSERT_TRUE(bar.d == pt.d); + ASSERT_TRUE(bar.rcm == pt.rcm); } TEST(noteencryption, SaplingApi) diff --git a/src/zcash/Note.cpp b/src/zcash/Note.cpp index 148fe8553..81d5457c1 100644 --- a/src/zcash/Note.cpp +++ b/src/zcash/Note.cpp @@ -208,6 +208,30 @@ boost::optional SaplingNotePlaintext::decrypt( return ret; } +boost::optional SaplingNotePlaintext::decryptUsingFullViewingKey( + const SaplingEncCiphertext &ciphertext, + const uint256 &epk, + const uint256 &esk, + const uint256 &pk_d +) +{ + auto pt = AttemptSaplingEncDecryptionUsingFullViewingKey(ciphertext, epk, esk, pk_d); + if (!pt) { + return boost::none; + } + + // Deserialize from the plaintext + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << pt.get(); + + SaplingNotePlaintext ret; + ss >> ret; + + assert(ss.size() == 0); + + return ret; +} + boost::optional SaplingNotePlaintext::encrypt(const uint256& pk_d) const { // Get the encryptor diff --git a/src/zcash/Note.hpp b/src/zcash/Note.hpp index 99d34f47b..b9f24f2e2 100644 --- a/src/zcash/Note.hpp +++ b/src/zcash/Note.hpp @@ -133,6 +133,13 @@ public: const uint256 &epk ); + static boost::optional decryptUsingFullViewingKey( + const SaplingEncCiphertext &ciphertext, + const uint256 &epk, + const uint256 &esk, + const uint256 &pk_d + ); + boost::optional note(const SaplingIncomingViewingKey& ivk) const; virtual ~SaplingNotePlaintext() {} diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index a00999bc3..fe8962dbb 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -187,6 +187,43 @@ boost::optional AttemptSaplingEncDecryption( return plaintext; } +boost::optional AttemptSaplingEncDecryptionUsingFullViewingKey ( + const SaplingEncCiphertext &ciphertext, + const uint256 &epk, + const uint256 &esk, + const uint256 &pk_d +) +{ + uint256 dhsecret; + + if (!librustzcash_sapling_ka_agree(pk_d.begin(), esk.begin(), dhsecret.begin())) { + return boost::none; + } + + // Construct the symmetric key + unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; + KDF_Sapling(K, dhsecret, epk); + + // The nonce is zero because we never reuse keys + unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; + + SaplingEncPlaintext plaintext; + + if (crypto_aead_chacha20poly1305_ietf_decrypt( + plaintext.begin(), NULL, + NULL, + ciphertext.begin(), ZC_SAPLING_ENCCIPHERTEXT_SIZE, + NULL, + 0, + cipher_nonce, K) != 0) + { + return boost::none; + } + + return plaintext; +} + + SaplingOutCiphertext SaplingNoteEncryption::encrypt_to_ourselves( const uint256 &ovk, const uint256 &cv, diff --git a/src/zcash/NoteEncryption.hpp b/src/zcash/NoteEncryption.hpp index 82f8b2abf..202d9a715 100644 --- a/src/zcash/NoteEncryption.hpp +++ b/src/zcash/NoteEncryption.hpp @@ -73,6 +73,15 @@ boost::optional AttemptSaplingEncDecryption( const uint256 &epk ); +// Attempts to decrypt a Sapling note using full viewing key. +// This will not check that the contents of the ciphertext are correct. +boost::optional AttemptSaplingEncDecryptionUsingFullViewingKey ( + const SaplingEncCiphertext &ciphertext, + const uint256 &epk, + const uint256 &esk, + const uint256 &pk_d +); + // Attempts to decrypt a Sapling note. This will not check that the contents // of the ciphertext are correct. boost::optional AttemptSaplingOutDecryption(