Hush Full Node software. We were censored from Github, this is where all development happens now.
https://hush.is
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
7.8 KiB
324 lines
7.8 KiB
// Copyright (c) 2016-2023 The Hush developers
|
|
// Distributed under the GPLv3 software license, see the accompanying
|
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
|
|
#include "Note.hpp"
|
|
#include "prf.h"
|
|
#include "crypto/sha256.h"
|
|
|
|
#include "random.h"
|
|
#include "version.h"
|
|
#include "streams.h"
|
|
|
|
#include "zcash/util.h"
|
|
#include "librustzcash.h"
|
|
#include <boost/thread.hpp>
|
|
|
|
using namespace libzcash;
|
|
|
|
// Construct and populate Sapling note for a given payment address and value.
|
|
SaplingNote::SaplingNote(const SaplingPaymentAddress& address, const uint64_t value) : BaseNote(value) {
|
|
d = address.d;
|
|
pk_d = address.pk_d;
|
|
librustzcash_sapling_generate_r(r.begin());
|
|
}
|
|
|
|
// Call librustzcash to compute the commitment
|
|
boost::optional<uint256> SaplingNote::cm() const {
|
|
uint256 result;
|
|
if (!librustzcash_sapling_compute_cm(
|
|
d.data(),
|
|
pk_d.begin(),
|
|
value(),
|
|
r.begin(),
|
|
result.begin()
|
|
))
|
|
{
|
|
return boost::none;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Call librustzcash to compute the nullifier
|
|
boost::optional<uint256> SaplingNote::nullifier(const SaplingFullViewingKey& vk, const uint64_t position) const
|
|
{
|
|
auto ak = vk.ak;
|
|
auto nk = vk.nk;
|
|
|
|
uint256 result;
|
|
if (!librustzcash_sapling_compute_nf(
|
|
d.data(),
|
|
pk_d.begin(),
|
|
value(),
|
|
r.begin(),
|
|
ak.begin(),
|
|
nk.begin(),
|
|
position,
|
|
result.begin()
|
|
))
|
|
{
|
|
return boost::none;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Construct and populate SaplingNotePlaintext for a given note and memo.
|
|
SaplingNotePlaintext::SaplingNotePlaintext(
|
|
const SaplingNote& note,
|
|
std::array<unsigned char, HUSH_MEMO_SIZE> memo) : BaseNotePlaintext(note, memo)
|
|
{
|
|
d = note.d;
|
|
rcm = note.r;
|
|
}
|
|
|
|
|
|
boost::optional<SaplingNote> SaplingNotePlaintext::note(const SaplingIncomingViewingKey& ivk) const
|
|
{
|
|
auto addr = ivk.address(d);
|
|
if (addr) {
|
|
return SaplingNote(d, addr.get().pk_d, value_, rcm);
|
|
} else {
|
|
return boost::none;
|
|
}
|
|
}
|
|
|
|
boost::optional<SaplingOutgoingPlaintext> SaplingOutgoingPlaintext::decrypt(
|
|
const SaplingOutCiphertext &ciphertext,
|
|
const uint256& ovk,
|
|
const uint256& cv,
|
|
const uint256& cm,
|
|
const uint256& epk
|
|
)
|
|
{
|
|
auto pt = AttemptSaplingOutDecryption(ciphertext, ovk, cv, cm, epk);
|
|
if (!pt) {
|
|
return boost::none;
|
|
}
|
|
|
|
// Deserialize from the plaintext
|
|
try {
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << pt.get();
|
|
|
|
SaplingOutgoingPlaintext ret;
|
|
ss >> ret;
|
|
|
|
assert(ss.size() == 0);
|
|
|
|
return ret;
|
|
} catch (const boost::thread_interrupted&) {
|
|
throw;
|
|
} catch (...) {
|
|
return boost::none;
|
|
}
|
|
}
|
|
|
|
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
|
const SaplingEncCiphertext &ciphertext,
|
|
const uint256 &ivk,
|
|
const uint256 &epk,
|
|
const uint256 &cmu
|
|
)
|
|
{
|
|
auto pt = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
|
|
if (!pt) {
|
|
return boost::none;
|
|
}
|
|
|
|
// Deserialize from the plaintext
|
|
SaplingNotePlaintext ret;
|
|
try {
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << pt.get();
|
|
ss >> ret;
|
|
assert(ss.size() == 0);
|
|
} catch (const boost::thread_interrupted&) {
|
|
throw;
|
|
} catch (...) {
|
|
return boost::none;
|
|
}
|
|
|
|
uint256 pk_d;
|
|
if (!librustzcash_ivk_to_pkd(ivk.begin(), ret.d.data(), pk_d.begin())) {
|
|
return boost::none;
|
|
}
|
|
|
|
uint256 cmu_expected;
|
|
if (!librustzcash_sapling_compute_cm(
|
|
ret.d.data(),
|
|
pk_d.begin(),
|
|
ret.value(),
|
|
ret.rcm.begin(),
|
|
cmu_expected.begin()
|
|
))
|
|
{
|
|
return boost::none;
|
|
}
|
|
|
|
if (cmu_expected != cmu) {
|
|
return boost::none;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
|
|
const SaplingEncCiphertext &ciphertext,
|
|
const uint256 &epk,
|
|
const uint256 &esk,
|
|
const uint256 &pk_d,
|
|
const uint256 &cmu
|
|
)
|
|
{
|
|
auto pt = AttemptSaplingEncDecryption(ciphertext, epk, esk, pk_d);
|
|
if (!pt) {
|
|
return boost::none;
|
|
}
|
|
|
|
// Deserialize from the plaintext
|
|
SaplingNotePlaintext ret;
|
|
try {
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << pt.get();
|
|
ss >> ret;
|
|
assert(ss.size() == 0);
|
|
} catch (const boost::thread_interrupted&) {
|
|
throw;
|
|
} catch (...) {
|
|
return boost::none;
|
|
}
|
|
|
|
uint256 cmu_expected;
|
|
if (!librustzcash_sapling_compute_cm(
|
|
ret.d.data(),
|
|
pk_d.begin(),
|
|
ret.value(),
|
|
ret.rcm.begin(),
|
|
cmu_expected.begin()
|
|
))
|
|
{
|
|
return boost::none;
|
|
}
|
|
|
|
if (cmu_expected != cmu) {
|
|
return boost::none;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
boost::optional<SaplingNotePlaintextEncryptionResult> SaplingNotePlaintext::encrypt(const uint256& pk_d) const
|
|
{
|
|
// Get the encryptor
|
|
auto sne = SaplingNoteEncryption::FromDiversifier(d);
|
|
if (!sne) {
|
|
return boost::none;
|
|
}
|
|
auto enc = sne.get();
|
|
|
|
// Create the plaintext
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << (*this);
|
|
SaplingEncPlaintext pt;
|
|
assert(pt.size() == ss.size());
|
|
memcpy(&pt[0], &ss[0], pt.size());
|
|
|
|
// Encrypt the plaintext
|
|
auto encciphertext = enc.encrypt_to_recipient(pk_d, pt);
|
|
if (!encciphertext) {
|
|
return boost::none;
|
|
}
|
|
return SaplingNotePlaintextEncryptionResult(encciphertext.get(), enc);
|
|
}
|
|
|
|
SaplingOutCiphertext SaplingOutgoingPlaintext::encrypt(
|
|
const uint256& ovk,
|
|
const uint256& cv,
|
|
const uint256& cm,
|
|
SaplingNoteEncryption& enc
|
|
) const
|
|
{
|
|
// Create the plaintext
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << (*this);
|
|
SaplingOutPlaintext pt;
|
|
assert(pt.size() == ss.size());
|
|
memcpy(&pt[0], &ss[0], pt.size());
|
|
|
|
return enc.encrypt_to_ourselves(ovk, cv, cm, pt);
|
|
}
|
|
|
|
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization(
|
|
const SaplingEncCiphertext &ciphertext,
|
|
const uint256 &ivk,
|
|
const uint256 &epk
|
|
)
|
|
{
|
|
auto encPlaintext = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
|
|
|
|
if (!encPlaintext) {
|
|
return boost::none;
|
|
}
|
|
|
|
// Deserialize from the plaintext
|
|
SaplingNotePlaintext ret;
|
|
try {
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << encPlaintext.value();
|
|
ss >> ret;
|
|
assert(ss.size() == 0);
|
|
return ret;
|
|
} catch (const boost::thread_interrupted&) {
|
|
throw;
|
|
} catch (...) {
|
|
return boost::none;
|
|
}
|
|
}
|
|
|
|
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::plaintext_checks_without_height(
|
|
const SaplingNotePlaintext &plaintext,
|
|
const uint256 &ivk,
|
|
const uint256 &epk,
|
|
const uint256 &cmu
|
|
)
|
|
{
|
|
uint256 pk_d;
|
|
if (!librustzcash_ivk_to_pkd(ivk.begin(), plaintext.d.data(), pk_d.begin())) {
|
|
return boost::none;
|
|
}
|
|
|
|
uint256 cmu_expected;
|
|
uint256 rcm = plaintext.rcm();
|
|
if (!librustzcash_sapling_compute_cmu(
|
|
plaintext.d.data(),
|
|
pk_d.begin(),
|
|
plaintext.value(),
|
|
rcm.begin(),
|
|
cmu_expected.begin()
|
|
))
|
|
{
|
|
return boost::none;
|
|
}
|
|
|
|
if (cmu_expected != cmu) {
|
|
return boost::none;
|
|
}
|
|
|
|
if (plaintext.get_leadbyte() != 0x01) {
|
|
assert(plaintext.get_leadbyte() == 0x02);
|
|
// ZIP 212: Check that epk is consistent to guard against linkability
|
|
// attacks without relying on the soundness of the SNARK.
|
|
uint256 expected_epk;
|
|
uint256 esk = plaintext.generate_or_derive_esk();
|
|
if (!librustzcash_sapling_ka_derivepublic(plaintext.d.data(), esk.begin(), expected_epk.begin())) {
|
|
return boost::none;
|
|
}
|
|
if (expected_epk != epk) {
|
|
return boost::none;
|
|
}
|
|
}
|
|
|
|
return plaintext;
|
|
}
|