![ewillbefull@gmail.com](/assets/img/avatar_default.png)
23 changed files with 1206 additions and 735 deletions
@ -1,20 +0,0 @@ |
|||
package=crypto++ |
|||
$(package)_version=5.6.2 |
|||
$(package)_download_path=http://www.cryptopp.com/ |
|||
$(package)_file_name=cryptopp562.zip |
|||
$(package)_sha256_hash=5cbfd2fcb4a6b3aab35902e2e0f3b59d9171fee12b3fc2b363e1801dfec53574 |
|||
$(package)_dependencies= |
|||
|
|||
# SECURITY BUG: _extract_cmds is responsible for verifying the archive
|
|||
# hash, but does not do so here:
|
|||
define $(package)_extract_cmds |
|||
unzip $($(package)_source_dir)/$($(package)_file_name) |
|||
endef |
|||
|
|||
define $(package)_build_cmds |
|||
$(MAKE) static CXXFLAGS='-DNDEBUG -g -O2 -fPIC' |
|||
endef |
|||
|
|||
define $(package)_stage_cmds |
|||
$(MAKE) install PREFIX=$($(package)_staging_dir)$(host_prefix) |
|||
endef |
File diff suppressed because one or more lines are too long
@ -0,0 +1,183 @@ |
|||
#include "test/test_bitcoin.h" |
|||
#include "sodium.h" |
|||
|
|||
#include <stdexcept> |
|||
|
|||
#include "zcash/NoteEncryption.hpp" |
|||
#include "zcash/prf.h" |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
|
|||
class TestNoteDecryption : public ZCNoteDecryption { |
|||
public: |
|||
TestNoteDecryption(uint256 sk_enc) : ZCNoteDecryption(sk_enc) {} |
|||
|
|||
void change_pk_enc(uint256 to) { |
|||
pk_enc = to; |
|||
} |
|||
}; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE(noteencryption_tests, BasicTestingSetup) |
|||
|
|||
BOOST_AUTO_TEST_CASE(noteencryption) |
|||
{ |
|||
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77")); |
|||
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc); |
|||
|
|||
ZCNoteEncryption b = ZCNoteEncryption(uint256()); |
|||
|
|||
boost::array<unsigned char, 216> message; |
|||
for (unsigned char i = 0; i < 216; i++) { |
|||
// Fill the message with dummy data
|
|||
message[i] = (unsigned char) i; |
|||
} |
|||
|
|||
for (int i = 0; i < 255; i++) { |
|||
auto ciphertext = b.encrypt(pk_enc, message); |
|||
|
|||
{ |
|||
ZCNoteDecryption decrypter(sk_enc); |
|||
|
|||
// Test decryption
|
|||
auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i); |
|||
BOOST_CHECK(plaintext == message); |
|||
|
|||
// Test wrong nonce
|
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)), std::runtime_error); |
|||
|
|||
// Test wrong ephemeral key
|
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, ZCNoteEncryption::generate_privkey(uint256()), uint256(), i), std::runtime_error); |
|||
|
|||
// Test wrong seed
|
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i), std::runtime_error); |
|||
|
|||
// Test corrupted ciphertext
|
|||
ciphertext[10] ^= 0xff; |
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error); |
|||
ciphertext[10] ^= 0xff; |
|||
} |
|||
|
|||
{ |
|||
// Test wrong private key
|
|||
uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint256()); |
|||
ZCNoteDecryption decrypter(sk_enc_2); |
|||
|
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error); |
|||
} |
|||
|
|||
{ |
|||
TestNoteDecryption decrypter(sk_enc); |
|||
|
|||
// Test decryption
|
|||
auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i); |
|||
BOOST_CHECK(plaintext == message); |
|||
|
|||
// Test wrong public key (test of KDF)
|
|||
decrypter.change_pk_enc(uint256()); |
|||
BOOST_CHECK_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error); |
|||
} |
|||
} |
|||
|
|||
// Nonce space should run out here
|
|||
BOOST_CHECK_THROW(b.encrypt(pk_enc, message), std::logic_error); |
|||
} |
|||
|
|||
uint256 test_prf( |
|||
unsigned char distinguisher, |
|||
uint256 x, |
|||
uint256 y |
|||
) { |
|||
*x.begin() &= 0x0f; |
|||
*x.begin() |= distinguisher; |
|||
CSHA256 hasher; |
|||
hasher.Write(x.begin(), 32); |
|||
hasher.Write(y.begin(), 32); |
|||
|
|||
uint256 ret; |
|||
hasher.FinalizeNoPadding(ret.begin()); |
|||
return ret; |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(prf_addr) |
|||
{ |
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 a_sk = libzcash::random_uint256(); |
|||
uint256 rest; |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0xc0, a_sk, rest) == PRF_addr_a_pk(a_sk), |
|||
a_sk.GetHex() |
|||
); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 a_sk = libzcash::random_uint256(); |
|||
uint256 rest; |
|||
*rest.begin() = 0x01; |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0xc0, a_sk, rest) == PRF_addr_sk_enc(a_sk), |
|||
a_sk.GetHex() |
|||
); |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(prf_nf) |
|||
{ |
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 a_sk = libzcash::random_uint256(); |
|||
uint256 rho = libzcash::random_uint256(); |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0xe0, a_sk, rho) == PRF_nf(a_sk, rho), |
|||
a_sk.GetHex() + " and " + rho.GetHex() |
|||
); |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(prf_pk) |
|||
{ |
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 a_sk = libzcash::random_uint256(); |
|||
uint256 h_sig = libzcash::random_uint256(); |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0x00, a_sk, h_sig) == PRF_pk(a_sk, 0, h_sig), |
|||
a_sk.GetHex() + " and " + h_sig.GetHex() |
|||
); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 a_sk = libzcash::random_uint256(); |
|||
uint256 h_sig = libzcash::random_uint256(); |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0x40, a_sk, h_sig) == PRF_pk(a_sk, 1, h_sig), |
|||
a_sk.GetHex() + " and " + h_sig.GetHex() |
|||
); |
|||
} |
|||
|
|||
uint256 dummy; |
|||
BOOST_CHECK_THROW(PRF_pk(dummy, 2, dummy), std::domain_error); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(prf_rho) |
|||
{ |
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 phi = libzcash::random_uint256(); |
|||
uint256 h_sig = libzcash::random_uint256(); |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0x20, phi, h_sig) == PRF_rho(phi, 0, h_sig), |
|||
phi.GetHex() + " and " + h_sig.GetHex() |
|||
); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 100; i++) { |
|||
uint256 phi = libzcash::random_uint256(); |
|||
uint256 h_sig = libzcash::random_uint256(); |
|||
BOOST_CHECK_MESSAGE( |
|||
test_prf(0x60, phi, h_sig) == PRF_rho(phi, 1, h_sig), |
|||
phi.GetHex() + " and " + h_sig.GetHex() |
|||
); |
|||
} |
|||
|
|||
uint256 dummy; |
|||
BOOST_CHECK_THROW(PRF_rho(dummy, 2, dummy), std::domain_error); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
@ -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>; |
|||
|
|||
} |
@ -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_ */ |
@ -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); |
|||
} |
@ -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_
|
Loading…
Reference in new issue