diff --git a/src/DeterministicKeys.cpp b/src/DeterministicKeys.cpp index e9bb109f0f..e6717540bc 100644 --- a/src/DeterministicKeys.cpp +++ b/src/DeterministicKeys.cpp @@ -1,3 +1,4 @@ + #include #include #include @@ -285,4 +286,5 @@ EC_KEY* CKey::GeneratePrivateDeterministicKey(const NewcoinAddress& family, cons return pkey; } + // vim:ts=4 diff --git a/src/ECIES.cpp b/src/ECIES.cpp new file mode 100644 index 0000000000..ed6d5e5c96 --- /dev/null +++ b/src/ECIES.cpp @@ -0,0 +1,158 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "key.h" + +static void* ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) +{ // This function must not be changed as it must be what ECDH_compute_key expects + if (*olen < SHA512_DIGEST_LENGTH) + return NULL; + *olen = SHA512_DIGEST_LENGTH; + return SHA512(static_cast(input), ilen, static_cast(output)); +} + +std::vector CKey::getECIESSecret(CKey& otherKey) +{ // Retrieve a secret generated from an EC key pair. At least one private key must be known. + if(!pkey || !otherKey.pkey) + throw std::runtime_error("missing key"); + + EC_KEY *pubkey, *privkey; + if(EC_KEY_get0_private_key(pkey)) + { + privkey=pkey; + pubkey=otherKey.pkey; + } + else if(EC_KEY_get0_private_key(otherKey.pkey)) + { + privkey=otherKey.pkey; + pubkey=pkey; + } + else throw std::runtime_error("no private key"); + + std::vector ret(SHA512_DIGEST_LENGTH); + if (ECDH_compute_key(&(ret.front()), SHA512_DIGEST_LENGTH, EC_KEY_get0_public_key(pubkey), + privkey, ecies_key_derivation) != SHA512_DIGEST_LENGTH) + throw std::runtime_error("ecdh key failed"); + return ret; +} + +// Our ciphertext is all encrypted except the IV. The encrypted data decodes as follows: +// 1) 256-bits of SHA-512 HMAC of original plaintext +// 2) Original plaintext + +static uint256 makeHMAC(const std::vector& secret, const std::vector data) +{ + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + + if(HMAC_Init_ex(&ctx, &(secret.front()), secret.size(), EVP_sha512(), NULL) != 1) + { + HMAC_CTX_cleanup(&ctx); + throw std::runtime_error("init hmac"); + } + + if(HMAC_Update(&ctx, &(data.front()), data.size()) != 1) + { + HMAC_CTX_cleanup(&ctx); + throw std::runtime_error("update hmac"); + } + + unsigned int ml=EVP_MAX_MD_SIZE; + std::vector hmac(ml); + if(!HMAC_Final(&ctx, &(hmac.front()), &ml) != 1) + { + HMAC_CTX_cleanup(&ctx); + throw std::runtime_error("finalize hmac"); + } + assert((ml>=32) && (ml<=EVP_MAX_MD_SIZE)); + + uint256 ret; + memcpy(ret.begin(), &(hmac.front()), 32); + + return ret; +} + +std::vector CKey::encryptECIES(CKey& otherKey, const std::vector& plaintext) +{ + std::vector secret=getECIESSecret(otherKey); + + uint256 hmac=makeHMAC(secret, plaintext); + + uint128 iv; + if(RAND_bytes(static_cast(iv.begin()), 128/8) != 1) + throw std::runtime_error("insufficient entropy"); + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + + if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, + &(secret.front()), static_cast(iv.begin())) != 1) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error("init cipher ctx"); + } + + std::vector out(plaintext.size() + (256/8) + (512/8) + 48, 0); + int len=0, bytesWritten; + + // output 256-bit IV + memcpy(&(out.front()), iv.begin(), 32); + len=32; + + // Encrypt/output 512-bit HMAC + bytesWritten=out.capacity()-len; + assert(bytesWritten>0); + if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, hmac.begin(), 64) < 0) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error(""); + } + len+=bytesWritten; + + // encrypt/output plaintext + bytesWritten=out.capacity()-len; + assert(bytesWritten>0); + if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, &(plaintext.front()), plaintext.size()) < 0) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error(""); + } + len+=bytesWritten; + + // finalize + bytesWritten=out.capacity()-len; + if(EVP_EncryptFinal_ex(&ctx, &(out.front())+len, &bytesWritten) < 0) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error(""); + } + len+=bytesWritten; + + out.resize(len); + EVP_CIPHER_CTX_cleanup(&ctx); + return out; +} + +std::vector CKey::decryptECIES(CKey& otherKey, const std::vector& ciphertext) +{ + std::vector secret=getECIESSecret(otherKey); + + // 1) Decrypt + + // 2) Extract length and plaintext + + // 3) Compute HMAC + + // 4) Verify + +} + +// vim:ts=4 diff --git a/src/UniqueNodeList.h b/src/UniqueNodeList.h index d0ce94f259..e52925f5b2 100644 --- a/src/UniqueNodeList.h +++ b/src/UniqueNodeList.h @@ -1,6 +1,8 @@ #ifndef __UNIQUE_NODE_LIST__ #define __UNIQUE_NODE_LIST__ +#include + #include "../json/value.h" #include "NewcoinAddress.h" @@ -9,7 +11,6 @@ #include "ParseSection.h" #include -#include #define SYSTEM_NAME "newcoin" @@ -27,7 +28,7 @@ private: boost::mutex mFetchLock; int mFetchActive; // count of active fetches - boost::container::deque mFetchPending; + std::deque mFetchPending; std::string mStrIpsUrl; std::string mStrValidatorsUrl; diff --git a/src/key.h b/src/key.h index 7eb4d0aeae..f61892801e 100644 --- a/src/key.h +++ b/src/key.h @@ -273,6 +273,16 @@ public: return false; return true; } + + // ECIES functions. These throw on failure + + // returns a 64-byte secret unique to these two keys. At least one private key must be known. + std::vector getECIESSecret(CKey& otherKey); + + // encrypt/decrypt functions with integrity checking. + // Note that the other side must somehow know what keys to use + std::vector encryptECIES(CKey& otherKey, const std::vector& plaintext); + std::vector decryptECIES(CKey& otherKey, const std::vector& ciphertext); }; #endif