NoteEncryption implementation and integration, removal of ECIES and crypto++ dependencies.
This commit is contained in:
169
src/zcash/NoteEncryption.cpp
Normal file
169
src/zcash/NoteEncryption.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "NoteEncryption.hpp"
|
||||
#include <stdexcept>
|
||||
#include "sodium.h"
|
||||
#include <boost/static_assert.hpp>
|
||||
#include "prf.h"
|
||||
|
||||
#define NOTEENCRYPTION_CIPHER_KEYSIZE 32
|
||||
|
||||
void clamp_curve25519(unsigned char key[crypto_scalarmult_SCALARBYTES])
|
||||
{
|
||||
key[0] &= 248;
|
||||
key[31] &= 127;
|
||||
key[31] |= 64;
|
||||
}
|
||||
|
||||
void KDF(unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE],
|
||||
const uint256 &dhsecret,
|
||||
const uint256 &epk,
|
||||
const uint256 &pk_enc,
|
||||
const uint256 &hSig,
|
||||
unsigned char nonce
|
||||
)
|
||||
{
|
||||
if (nonce == 0xff) {
|
||||
throw std::logic_error("no additional nonce space for KDF");
|
||||
}
|
||||
|
||||
unsigned char block[128] = {};
|
||||
memcpy(block+0, hSig.begin(), 32);
|
||||
memcpy(block+32, dhsecret.begin(), 32);
|
||||
memcpy(block+64, epk.begin(), 32);
|
||||
memcpy(block+96, pk_enc.begin(), 32);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "ZcashKDF", 8);
|
||||
memcpy(personalization+8, &nonce, 1);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
template<size_t MLEN>
|
||||
NoteEncryption<MLEN>::NoteEncryption(uint256 hSig) : nonce(0), hSig(hSig) {
|
||||
// All of this code assumes crypto_scalarmult_BYTES is 32
|
||||
// There's no reason that will _ever_ change, but for
|
||||
// completeness purposes, let's check anyway.
|
||||
BOOST_STATIC_ASSERT(32 == crypto_scalarmult_BYTES);
|
||||
BOOST_STATIC_ASSERT(32 == crypto_scalarmult_SCALARBYTES);
|
||||
BOOST_STATIC_ASSERT(NOTEENCRYPTION_AUTH_BYTES == crypto_aead_chacha20poly1305_ABYTES);
|
||||
|
||||
// Create the ephemeral keypair
|
||||
esk = random_uint256();
|
||||
epk = generate_pubkey(esk);
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
NoteDecryption<MLEN>::NoteDecryption(uint256 sk_enc) : sk_enc(sk_enc) {
|
||||
this->pk_enc = NoteEncryption<MLEN>::generate_pubkey(sk_enc);
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
typename NoteEncryption<MLEN>::Ciphertext NoteEncryption<MLEN>::encrypt
|
||||
(const uint256 &pk_enc,
|
||||
const NoteEncryption<MLEN>::Plaintext &message
|
||||
)
|
||||
{
|
||||
uint256 dhsecret;
|
||||
|
||||
if (crypto_scalarmult(dhsecret.begin(), esk.begin(), pk_enc.begin()) != 0) {
|
||||
throw std::logic_error("Could not create DH secret");
|
||||
}
|
||||
|
||||
// Construct the symmetric key
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
KDF(K, dhsecret, epk, pk_enc, hSig, nonce);
|
||||
|
||||
// Increment the number of encryptions we've performed
|
||||
nonce++;
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
NoteEncryption<MLEN>::Ciphertext ciphertext;
|
||||
|
||||
crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext.begin(), NULL,
|
||||
message.begin(), MLEN,
|
||||
NULL, 0, // no "additional data"
|
||||
NULL, cipher_nonce, K);
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
typename NoteDecryption<MLEN>::Plaintext NoteDecryption<MLEN>::decrypt
|
||||
(const NoteDecryption<MLEN>::Ciphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &hSig,
|
||||
unsigned char nonce
|
||||
) const
|
||||
{
|
||||
uint256 dhsecret;
|
||||
|
||||
if (crypto_scalarmult(dhsecret.begin(), sk_enc.begin(), epk.begin()) != 0) {
|
||||
throw std::logic_error("Could not create DH secret");
|
||||
}
|
||||
|
||||
unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE];
|
||||
KDF(K, dhsecret, epk, pk_enc, hSig, nonce);
|
||||
|
||||
// The nonce is zero because we never reuse keys
|
||||
unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {};
|
||||
|
||||
NoteDecryption<MLEN>::Plaintext plaintext;
|
||||
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(plaintext.begin(), NULL,
|
||||
NULL,
|
||||
ciphertext.begin(), NoteDecryption<MLEN>::CLEN,
|
||||
NULL,
|
||||
0,
|
||||
cipher_nonce, K) != 0) {
|
||||
throw std::runtime_error("Could not decrypt message");
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
uint256 NoteEncryption<MLEN>::generate_privkey(const uint256 &a_sk)
|
||||
{
|
||||
uint256 sk = PRF_addr_sk_enc(a_sk);
|
||||
|
||||
clamp_curve25519(sk.begin());
|
||||
|
||||
return sk;
|
||||
}
|
||||
|
||||
template<size_t MLEN>
|
||||
uint256 NoteEncryption<MLEN>::generate_pubkey(const uint256 &sk_enc)
|
||||
{
|
||||
uint256 pk;
|
||||
|
||||
if (crypto_scalarmult_base(pk.begin(), sk_enc.begin()) != 0) {
|
||||
throw std::logic_error("Could not create public key");
|
||||
}
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
uint256 random_uint256()
|
||||
{
|
||||
uint256 ret;
|
||||
randombytes_buf(ret.begin(), 32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template class NoteEncryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
|
||||
template class NoteDecryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE>;
|
||||
|
||||
}
|
||||
79
src/zcash/NoteEncryption.hpp
Normal file
79
src/zcash/NoteEncryption.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
See the Zcash protocol specification for more information.
|
||||
https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||
*/
|
||||
|
||||
#ifndef ZC_NOTE_ENCRYPTION_H_
|
||||
#define ZC_NOTE_ENCRYPTION_H_
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include "uint256.h"
|
||||
|
||||
#include "zerocash/Zerocash.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
#define NOTEENCRYPTION_AUTH_BYTES 16
|
||||
|
||||
template<size_t MLEN>
|
||||
class NoteEncryption {
|
||||
protected:
|
||||
enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES };
|
||||
uint256 epk;
|
||||
uint256 esk;
|
||||
unsigned char nonce;
|
||||
uint256 hSig;
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef boost::array<unsigned char, MLEN> Plaintext;
|
||||
|
||||
NoteEncryption(uint256 hSig);
|
||||
|
||||
// Gets the ephemeral public key
|
||||
uint256 get_epk() {
|
||||
return epk;
|
||||
}
|
||||
|
||||
// Encrypts `message` with `pk_enc` and returns the ciphertext.
|
||||
// This can only be called twice for a given instantiation before
|
||||
// the nonce-space runs out.
|
||||
Ciphertext encrypt(const uint256 &pk_enc,
|
||||
const Plaintext &message
|
||||
);
|
||||
|
||||
// Creates a NoteEncryption private key
|
||||
static uint256 generate_privkey(const uint256 &a_sk);
|
||||
|
||||
// Creates a NoteEncryption public key from a private key
|
||||
static uint256 generate_pubkey(const uint256 &sk_enc);
|
||||
};
|
||||
|
||||
template<size_t MLEN>
|
||||
class NoteDecryption {
|
||||
protected:
|
||||
enum { CLEN=MLEN+NOTEENCRYPTION_AUTH_BYTES };
|
||||
uint256 sk_enc;
|
||||
uint256 pk_enc;
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, CLEN> Ciphertext;
|
||||
typedef boost::array<unsigned char, MLEN> Plaintext;
|
||||
|
||||
NoteDecryption(uint256 sk_enc);
|
||||
|
||||
Plaintext decrypt(const Ciphertext &ciphertext,
|
||||
const uint256 &epk,
|
||||
const uint256 &hSig,
|
||||
unsigned char nonce
|
||||
) const;
|
||||
};
|
||||
|
||||
uint256 random_uint256();
|
||||
|
||||
}
|
||||
|
||||
typedef libzcash::NoteEncryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteEncryption;
|
||||
typedef libzcash::NoteDecryption<ZC_V_SIZE + ZC_RHO_SIZE + ZC_R_SIZE + ZC_MEMO_SIZE> ZCNoteDecryption;
|
||||
|
||||
#endif /* ZC_NOTE_ENCRYPTION_H_ */
|
||||
63
src/zcash/prf.cpp
Normal file
63
src/zcash/prf.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "prf.h"
|
||||
#include "crypto/sha256.h"
|
||||
|
||||
uint256 PRF(bool a, bool b, bool c, bool d,
|
||||
const uint256& x,
|
||||
const uint256& y)
|
||||
{
|
||||
uint256 res;
|
||||
unsigned char blob[64];
|
||||
|
||||
memcpy(&blob[0], x.begin(), 32);
|
||||
memcpy(&blob[32], y.begin(), 32);
|
||||
|
||||
blob[0] &= 0x0F;
|
||||
blob[0] |= (a ? 1 << 7 : 0) | (b ? 1 << 6 : 0) | (c ? 1 << 5 : 0) | (d ? 1 << 4 : 0);
|
||||
|
||||
CSHA256 hasher;
|
||||
hasher.Write(blob, 64);
|
||||
hasher.FinalizeNoPadding(res.begin());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint256 PRF_addr(const uint256& a_sk, unsigned char t)
|
||||
{
|
||||
uint256 y;
|
||||
*(y.begin()) = t;
|
||||
|
||||
return PRF(1, 1, 0, 0, a_sk, y);
|
||||
}
|
||||
|
||||
uint256 PRF_addr_a_pk(const uint256& a_sk)
|
||||
{
|
||||
return PRF_addr(a_sk, 0);
|
||||
}
|
||||
|
||||
uint256 PRF_addr_sk_enc(const uint256& a_sk)
|
||||
{
|
||||
return PRF_addr(a_sk, 1);
|
||||
}
|
||||
|
||||
uint256 PRF_nf(const uint256& a_sk, const uint256& rho)
|
||||
{
|
||||
return PRF(1, 1, 1, 0, a_sk, rho);
|
||||
}
|
||||
|
||||
uint256 PRF_pk(const uint256& a_sk, size_t i0, const uint256& h_sig)
|
||||
{
|
||||
if ((i0 != 0) && (i0 != 1)) {
|
||||
throw std::domain_error("PRF_pk invoked with index out of bounds");
|
||||
}
|
||||
|
||||
return PRF(0, i0, 0, 0, a_sk, h_sig);
|
||||
}
|
||||
|
||||
uint256 PRF_rho(const uint256& phi, size_t i0, const uint256& h_sig)
|
||||
{
|
||||
if ((i0 != 0) && (i0 != 1)) {
|
||||
throw std::domain_error("PRF_rho invoked with index out of bounds");
|
||||
}
|
||||
|
||||
return PRF(0, i0, 1, 0, phi, h_sig);
|
||||
}
|
||||
17
src/zcash/prf.h
Normal file
17
src/zcash/prf.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Zcash uses SHA256Compress as a PRF for various components
|
||||
within the zkSNARK circuit.
|
||||
*/
|
||||
|
||||
#ifndef _PRF_H_
|
||||
#define _PRF_H_
|
||||
|
||||
#include "uint256.h"
|
||||
|
||||
uint256 PRF_addr_a_pk(const uint256& a_sk);
|
||||
uint256 PRF_addr_sk_enc(const uint256& a_sk);
|
||||
uint256 PRF_nf(const uint256& a_sk, const uint256& rho);
|
||||
uint256 PRF_pk(const uint256& a_sk, size_t i0, const uint256& h_sig);
|
||||
uint256 PRF_rho(const uint256& phi, size_t i0, const uint256& h_sig);
|
||||
|
||||
#endif // _PRF_H_
|
||||
Reference in New Issue
Block a user