From 7f623047ccd68862ac85609f72066971a172910b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 5 Apr 2012 19:53:35 -0700 Subject: [PATCH] Finalize code. Add unit test. --- src/ECIES.cpp | 106 ++++++++++++++++++++++++++++++++++---------------- src/key.h | 4 +- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/ECIES.cpp b/src/ECIES.cpp index 1acb5f23c..166134326 100644 --- a/src/ECIES.cpp +++ b/src/ECIES.cpp @@ -13,9 +13,12 @@ #define ECIES_KEY_HASH SHA256 #define ECIES_KEY_LENGTH (256/8) +#define ECIES_KEY_TYPE uint256 #define ECIES_ENC_ALGO EVP_aes_256_cbc() -#define ECIES_ENC_SIZE (256/8) -#define ECIES_ENC_TYPE uint256 +#define ECIES_ENC_KEY_SIZE (256/8) +#define ECIES_ENC_BLK_SIZE (128/8) +#define ECIES_ENC_KEY_TYPE uint256 +#define ECIES_ENC_IV_TYPE uint128 #define ECIES_HMAC_ALGO EVP_sha256() #define ECIES_HMAC_SIZE (256/8) #define ECIES_HMAC_TYPE uint256 @@ -23,12 +26,15 @@ 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 < ECIES_KEY_LENGTH) + { + assert(false); return NULL; + } *olen = ECIES_KEY_LENGTH; return ECIES_KEY_HASH(static_cast(input), ilen, static_cast(output)); } -std::vector CKey::getECIESSecret(CKey& otherKey) +ECIES_KEY_TYPE 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"); @@ -46,11 +52,11 @@ std::vector CKey::getECIESSecret(CKey& otherKey) } else throw std::runtime_error("no private key"); - std::vector ret(ECIES_KEY_LENGTH); - if (ECDH_compute_key(&(ret.front()), ECIES_KEY_LENGTH, EC_KEY_get0_public_key(pubkey), + ECIES_KEY_TYPE key; + if (ECDH_compute_key(key.begin(), ECIES_KEY_LENGTH, EC_KEY_get0_public_key(pubkey), privkey, ecies_key_derivation) != ECIES_KEY_LENGTH) throw std::runtime_error("ecdh key failed"); - return ret; + return key; } // Our ciphertext is all encrypted except the IV. The encrypted data decodes as follows: @@ -59,12 +65,12 @@ std::vector CKey::getECIESSecret(CKey& otherKey) // 3) Encrypted: Original plaintext // 4) Encrypted: Rest of block/padding -static ECIES_HMAC_TYPE makeHMAC(const std::vector& secret, const std::vector data) +static ECIES_HMAC_TYPE makeHMAC(ECIES_KEY_TYPE secret, const std::vector data) { HMAC_CTX ctx; HMAC_CTX_init(&ctx); - if(HMAC_Init_ex(&ctx, &(secret.front()), secret.size(), ECIES_HMAC_ALGO, NULL) != 1) + if(HMAC_Init_ex(&ctx, secret.begin(), ECIES_KEY_LENGTH, ECIES_HMAC_ALGO, NULL) != 1) { HMAC_CTX_cleanup(&ctx); throw std::runtime_error("init hmac"); @@ -78,7 +84,7 @@ static ECIES_HMAC_TYPE makeHMAC(const std::vector& secret, const unsigned int ml=EVP_MAX_MD_SIZE; std::vector hmac(ml); - if(!HMAC_Final(&ctx, &(hmac.front()), &ml) != 1) + if(HMAC_Final(&ctx, &(hmac.front()), &ml) != 1) { HMAC_CTX_cleanup(&ctx); throw std::runtime_error("finalize hmac"); @@ -92,35 +98,33 @@ static ECIES_HMAC_TYPE makeHMAC(const std::vector& secret, const std::vector CKey::encryptECIES(CKey& otherKey, const std::vector& plaintext) { - std::vector secret=getECIESSecret(otherKey); - + ECIES_KEY_TYPE secret=getECIESSecret(otherKey); ECIES_HMAC_TYPE hmac=makeHMAC(secret, plaintext); - uint128 iv; - if(RAND_bytes(static_cast(iv.begin()), ECIES_ENC_SIZE) != 1) + ECIES_ENC_IV_TYPE iv; + if(RAND_bytes(static_cast(iv.begin()), ECIES_ENC_BLK_SIZE) != 1) throw std::runtime_error("insufficient entropy"); EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); - if (EVP_EncryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, - &(secret.front()), static_cast(iv.begin())) != 1) + if (EVP_EncryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error("init cipher ctx"); } - std::vector out(plaintext.size() + ECIES_HMAC_SIZE + (ECIES_ENC_SIZE*2), 0); + std::vector out(plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_KEY_SIZE + ECIES_ENC_BLK_SIZE, 0); int len=0, bytesWritten; // output IV - memcpy(&(out.front()), iv.begin(), ECIES_ENC_SIZE); - len=ECIES_ENC_SIZE; + memcpy(&(out.front()), iv.begin(), ECIES_ENC_BLK_SIZE); + len=ECIES_ENC_BLK_SIZE; // Encrypt/output HMAC bytesWritten=out.capacity()-len; assert(bytesWritten>0); - if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, hmac.begin(), ECIES_HMAC_SIZE) < 0) + if(EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, hmac.begin(), ECIES_HMAC_SIZE) < 0) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error(""); @@ -130,7 +134,7 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< // encrypt/output plaintext bytesWritten=out.capacity()-len; assert(bytesWritten>0); - if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, &(plaintext.front()), plaintext.size()) < 0) + if(EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, &(plaintext.front()), plaintext.size()) < 0) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error(""); @@ -139,13 +143,16 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< // finalize bytesWritten=out.capacity()-len; - if(EVP_EncryptFinal_ex(&ctx, &(out.front())+len, &bytesWritten) < 0) + if(EVP_EncryptFinal_ex(&ctx, &(out.front()) + len, &bytesWritten) < 0) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error(""); } len+=bytesWritten; + // Output contains: IV, encrypted HMAC, encrypted data, encrypted padding + assert(len <= (plaintext.size() + ECIES_HMAC_SIZE + (2 * ECIES_ENC_BLK_SIZE))); + assert(len >= (plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_BLK_SIZE)); // IV, HMAC, data out.resize(len); EVP_CIPHER_CTX_cleanup(&ctx); return out; @@ -153,21 +160,21 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< std::vector CKey::decryptECIES(CKey& otherKey, const std::vector& ciphertext) { - std::vector secret=getECIESSecret(otherKey); + ECIES_KEY_TYPE secret=getECIESSecret(otherKey); // minimum ciphertext = IV + HMAC + 1 block - if(ciphertext.size() < ((2*ECIES_ENC_SIZE)+ECIES_HMAC_SIZE) ) + if(ciphertext.size() < ((2*ECIES_ENC_BLK_SIZE) + ECIES_HMAC_SIZE) ) throw std::runtime_error("ciphertext too short"); // extract IV - ECIES_ENC_TYPE iv; - memcpy(iv.begin(), &(ciphertext.front()), ECIES_ENC_SIZE); + ECIES_ENC_IV_TYPE iv; + memcpy(iv.begin(), &(ciphertext.front()), ECIES_ENC_BLK_SIZE); // begin decrypting EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); - if(EVP_DecryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, &(secret.front()), iv.begin()) != 1) + if(EVP_DecryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error("unable to init cipher"); @@ -176,30 +183,31 @@ std::vector CKey::decryptECIES(CKey& otherKey, const std::vector< // decrypt mac ECIES_HMAC_TYPE hmac; int outlen=ECIES_HMAC_SIZE; - if( (EVP_DecryptUpdate(&ctx, hmac.begin(), &outlen, &(ciphertext.front()), ECIES_HMAC_SIZE) != 1) || - (outlen != ECIES_HMAC_SIZE) ) + if( (EVP_DecryptUpdate(&ctx, hmac.begin(), &outlen, + &(ciphertext.front()) + ECIES_ENC_BLK_SIZE, ECIES_HMAC_SIZE+1) != 1) || (outlen != ECIES_HMAC_SIZE) ) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error("unable to extract hmac"); } - // decrypt plaintext - std::vector plaintext(ciphertext.size() - ECIES_HMAC_SIZE - ECIES_ENC_SIZE); + // decrypt plaintext (after IV and encrypted mac) + std::vector plaintext(ciphertext.size() - ECIES_HMAC_SIZE - ECIES_ENC_BLK_SIZE); outlen=plaintext.size(); if(EVP_DecryptUpdate(&ctx, &(plaintext.front()), &outlen, - &(ciphertext.front())+ECIES_HMAC_SIZE, ciphertext.size()-ECIES_HMAC_SIZE) != 1) + &(ciphertext.front())+ECIES_ENC_BLK_SIZE+ECIES_HMAC_SIZE+1, + ciphertext.size()-ECIES_ENC_BLK_SIZE-ECIES_HMAC_SIZE-1) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error("unable to extract plaintext"); } - int flen=0; + int flen = 0; if(EVP_DecryptFinal(&ctx, &(plaintext.front()) + outlen, &flen) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error("plaintext had bad padding"); } - plaintext.resize(flen+outlen); + plaintext.resize(flen + outlen); if(hmac != makeHMAC(secret, plaintext)) { @@ -211,4 +219,36 @@ std::vector CKey::decryptECIES(CKey& otherKey, const std::vector< return plaintext; } +bool checkECIES(void) +{ + CKey senderPriv, recipientPriv, senderPub, recipientPub; + senderPriv.MakeNewKey(); + recipientPriv.MakeNewKey(); + + if(!senderPub.SetPubKey(senderPriv.GetPubKey())) + throw std::runtime_error("key error"); + if(!recipientPub.SetPubKey(recipientPriv.GetPubKey())) + throw std::runtime_error("key error"); + + for(int i=0; i<30000; i++) + { + // generate message + std::vector message(4096); + int msglen=i%3000; + if(RAND_bytes(static_cast(&message.front()), msglen) != 1) + throw std::runtime_error("insufficient entropy"); + message.resize(msglen); + + // encrypt message with sender's private key and recipient's public key + std::vector ciphertext=senderPriv.encryptECIES(recipientPub, message); + + // decrypt message with recipient's private key and sender's public key + std::vector decrypt=recipientPriv.decryptECIES(senderPub, ciphertext); + + if(decrypt != message) return false; +// std::cerr << "Msg(" << msglen << ") ok " << ciphertext.size() << std::endl; + } + return true; +} + // vim:ts=4 diff --git a/src/key.h b/src/key.h index f61892801..2586cbf91 100644 --- a/src/key.h +++ b/src/key.h @@ -276,8 +276,8 @@ public: // 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); + // returns a 32-byte secret unique to these two keys. At least one private key must be known. + uint256 getECIESSecret(CKey& otherKey); // encrypt/decrypt functions with integrity checking. // Note that the other side must somehow know what keys to use