NoteEncryption implementation and integration, removal of ECIES and crypto++ dependencies.

This commit is contained in:
Sean Bowe
2016-03-31 22:18:36 -06:00
parent b2cf9ba300
commit 6c36a9fe03
23 changed files with 1206 additions and 735 deletions

View 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>;
}

View 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
View 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
View 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_