Implementation of Sapling in-band secret distribution.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "sodium.h"
|
||||
#include <boost/static_assert.hpp>
|
||||
#include "prf.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
#define NOTEENCRYPTION_CIPHER_KEYSIZE 32
|
||||
|
||||
@@ -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],
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
unsigned char block[128] = {};
|
||||
memcpy(block+0, ovk.begin(), 32);
|
||||
memcpy(block+32, cv.begin(), 32);
|
||||
memcpy(block+64, cm.begin(), 32);
|
||||
memcpy(block+96, epk.begin(), 32);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "Zcash_Derive_ock", 16);
|
||||
|
||||
if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
|
||||
block, 128,
|
||||
NULL, 0, // No key.
|
||||
NULL, // No salt.
|
||||
personalization
|
||||
) != 0)
|
||||
{
|
||||
throw std::logic_error("hash function failure");
|
||||
}
|
||||
}
|
||||
|
||||
void KDF_Sapling(
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &dhsecret,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
unsigned char block[64] = {};
|
||||
memcpy(block+0, dhsecret.begin(), 32);
|
||||
memcpy(block+32, epk.begin(), 32);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "Zcash_SaplingKDF", 16);
|
||||
|
||||
if (crypto_generichash_blake2b_salt_personal(K, NOTEENCRYPTION_CIPHER_KEYSIZE,
|
||||
block, 64,
|
||||
NULL, 0, // No key.
|
||||
NULL, // No salt.
|
||||
personalization
|
||||
) != 0)
|
||||
{
|
||||
throw std::logic_error("hash function failure");
|
||||
}
|
||||
}
|
||||
|
||||
void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &dhsecret,
|
||||
const uint256 &epk,
|
||||
@@ -48,6 +101,143 @@ void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
boost::optional<SaplingNoteEncryption> SaplingNoteEncryption::FromDiversifier(diversifier_t d) {
|
||||
uint256 epk;
|
||||
uint256 esk;
|
||||
|
||||
// Pick random esk
|
||||
librustzcash_sapling_generate_r(esk.begin());
|
||||
|
||||
// Compute epk given the diversifier
|
||||
if (!librustzcash_sapling_ka_derivepublic(d.begin(), esk.begin(), epk.begin())) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return SaplingNoteEncryption(epk, esk);
|
||||
}
|
||||
|
||||
boost::optional<SaplingEncCiphertext> SaplingNoteEncryption::encrypt_to_recipient(
|
||||
const uint256 &pk_d,
|
||||
const SaplingEncPlaintext &message
|
||||
)
|
||||
{
|
||||
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] = {};
|
||||
|
||||
SaplingEncCiphertext ciphertext;
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt(
|
||||
ciphertext.begin(), NULL,
|
||||
message.begin(), ZC_SAPLING_ENCPLAINTEXT_SIZE,
|
||||
NULL, 0, // no "additional data"
|
||||
NULL, cipher_nonce, K
|
||||
);
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
boost::optional<SaplingEncPlaintext> AttemptSaplingEncDecryption(
|
||||
const SaplingEncCiphertext &ciphertext,
|
||||
const uint256 &ivk,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
uint256 dhsecret;
|
||||
|
||||
if (!librustzcash_sapling_ka_agree(epk.begin(), ivk.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,
|
||||
const uint256 &cm,
|
||||
const SaplingOutPlaintext &message
|
||||
)
|
||||
{
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
PRF_ock(K, ovk, cv, cm, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingOutCiphertext ciphertext;
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt(
|
||||
ciphertext.begin(), NULL,
|
||||
message.begin(), ZC_SAPLING_OUTPLAINTEXT_SIZE,
|
||||
NULL, 0, // no "additional data"
|
||||
NULL, cipher_nonce, K
|
||||
);
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
boost::optional<SaplingOutPlaintext> AttemptSaplingOutDecryption(
|
||||
const SaplingOutCiphertext &ciphertext,
|
||||
const uint256 &ovk,
|
||||
const uint256 &cv,
|
||||
const uint256 &cm,
|
||||
const uint256 &epk
|
||||
)
|
||||
{
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
PRF_ock(K, ovk, cv, cm, epk);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
SaplingOutPlaintext plaintext;
|
||||
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
plaintext.begin(), NULL,
|
||||
NULL,
|
||||
ciphertext.begin(), ZC_SAPLING_OUTCIPHERTEXT_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
cipher_nonce, K) != 0)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
NoteEncryption<MLEN>::NoteEncryption(uint256 hSig) : nonce(0), hSig(hSig) {
|
||||
// All of this code assumes crypto_scalarmult_BYTES is 32
|
||||
|
||||
Reference in New Issue
Block a user