Auto merge of #3391 - bitcartel:3061_sapling_note_encryption, r=ebfull

Sapling note plaintext (encryption and decryption)

Add encryption and decryption of SaplingNotePlaintext and SaplingOutgoingPlaintext classes.

This is part of #3061 to add Sapling note functionality.
This commit is contained in:
Homu
2018-07-24 03:27:28 -07:00
5 changed files with 378 additions and 0 deletions

View File

@@ -137,3 +137,139 @@ ZCNoteEncryption::Ciphertext SproutNotePlaintext::encrypt(ZCNoteEncryption& encr
return encryptor.encrypt(pk_enc, pt);
}
// Construct and populate SaplingNotePlaintext for a given note and memo.
SaplingNotePlaintext::SaplingNotePlaintext(
const SaplingNote& note,
std::array<unsigned char, ZC_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
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pt.get();
SaplingOutgoingPlaintext ret;
ss >> ret;
assert(ss.size() == 0);
return ret;
}
boost::optional<SaplingNotePlaintext> SaplingNotePlaintext::decrypt(
const SaplingEncCiphertext &ciphertext,
const uint256 &ivk,
const uint256 &epk
)
{
auto pt = AttemptSaplingEncDecryption(ciphertext, ivk, epk);
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> SaplingNotePlaintext::decrypt(
const SaplingEncCiphertext &ciphertext,
const uint256 &epk,
const uint256 &esk,
const uint256 &pk_d
)
{
auto pt = AttemptSaplingEncDecryption(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<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);
}

View File

@@ -116,6 +116,89 @@ public:
) const;
};
typedef std::pair<SaplingEncCiphertext, SaplingNoteEncryption> SaplingNotePlaintextEncryptionResult;
class SaplingNotePlaintext : public BaseNotePlaintext {
public:
diversifier_t d;
uint256 rcm;
SaplingNotePlaintext() {}
SaplingNotePlaintext(const SaplingNote& note, std::array<unsigned char, ZC_MEMO_SIZE> memo);
static boost::optional<SaplingNotePlaintext> decrypt(
const SaplingEncCiphertext &ciphertext,
const uint256 &ivk,
const uint256 &epk
);
static boost::optional<SaplingNotePlaintext> decrypt(
const SaplingEncCiphertext &ciphertext,
const uint256 &epk,
const uint256 &esk,
const uint256 &pk_d
);
boost::optional<SaplingNote> note(const SaplingIncomingViewingKey& ivk) const;
virtual ~SaplingNotePlaintext() {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
unsigned char leadingByte = 0x01;
READWRITE(leadingByte);
if (leadingByte != 0x01) {
throw std::ios_base::failure("lead byte of SaplingNotePlaintext is not recognized");
}
READWRITE(d); // 11 bytes
READWRITE(value_); // 8 bytes
READWRITE(rcm); // 32 bytes
READWRITE(memo_); // 512 bytes
}
boost::optional<SaplingNotePlaintextEncryptionResult> encrypt(const uint256& pk_d) const;
};
class SaplingOutgoingPlaintext
{
public:
uint256 pk_d;
uint256 esk;
SaplingOutgoingPlaintext() {};
SaplingOutgoingPlaintext(uint256 pk_d, uint256 esk) : pk_d(pk_d), esk(esk) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(pk_d); // 8 bytes
READWRITE(esk); // 8 bytes
}
static boost::optional<SaplingOutgoingPlaintext> decrypt(
const SaplingOutCiphertext &ciphertext,
const uint256& ovk,
const uint256& cv,
const uint256& cm,
const uint256& epk
);
SaplingOutCiphertext encrypt(
const uint256& ovk,
const uint256& cv,
const uint256& cm,
SaplingNoteEncryption& enc
) const;
};
}
#endif // ZC_NOTE_H_

View File

@@ -187,6 +187,43 @@ boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
return plaintext;
}
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
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,

View File

@@ -73,6 +73,15 @@ boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
const uint256 &epk
);
// Attempts to decrypt a Sapling note using outgoing plaintext.
// This will not check that the contents of the ciphertext are correct.
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption (
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<SaplingOutPlaintext> AttemptSaplingOutDecryption(