From d2d6f12745a420aa2840ce85db87511d6a548b37 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 5 Apr 2012 18:13:07 -0700 Subject: [PATCH] Complete ECIES functions. (Untested.) --- src/ECIES.cpp | 112 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/src/ECIES.cpp b/src/ECIES.cpp index ed6d5e5c96..1acb5f23c6 100644 --- a/src/ECIES.cpp +++ b/src/ECIES.cpp @@ -11,12 +11,21 @@ #include "key.h" +#define ECIES_KEY_HASH SHA256 +#define ECIES_KEY_LENGTH (256/8) +#define ECIES_ENC_ALGO EVP_aes_256_cbc() +#define ECIES_ENC_SIZE (256/8) +#define ECIES_ENC_TYPE uint256 +#define ECIES_HMAC_ALGO EVP_sha256() +#define ECIES_HMAC_SIZE (256/8) +#define ECIES_HMAC_TYPE uint256 + 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) + if (*olen < ECIES_KEY_LENGTH) return NULL; - *olen = SHA512_DIGEST_LENGTH; - return SHA512(static_cast(input), ilen, static_cast(output)); + *olen = ECIES_KEY_LENGTH; + return ECIES_KEY_HASH(static_cast(input), ilen, static_cast(output)); } std::vector CKey::getECIESSecret(CKey& otherKey) @@ -37,23 +46,25 @@ std::vector CKey::getECIESSecret(CKey& otherKey) } 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) + std::vector ret(ECIES_KEY_LENGTH); + if (ECDH_compute_key(&(ret.front()), 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; } // 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 +// 1) 256-bit IV (unencrypted) +// 2) Encrypted: 256-bits HMAC of original plaintext +// 3) Encrypted: Original plaintext +// 4) Encrypted: Rest of block/padding -static uint256 makeHMAC(const std::vector& secret, const std::vector data) +static ECIES_HMAC_TYPE 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) + if(HMAC_Init_ex(&ctx, &(secret.front()), secret.size(), ECIES_HMAC_ALGO, NULL) != 1) { HMAC_CTX_cleanup(&ctx); throw std::runtime_error("init hmac"); @@ -72,10 +83,9 @@ static uint256 makeHMAC(const std::vector& secret, const std::vec 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); + ECIES_HMAC_TYPE ret; + memcpy(ret.begin(), &(hmac.front()), ECIES_HMAC_SIZE); return ret; } @@ -84,33 +94,33 @@ std::vector CKey::encryptECIES(CKey& otherKey, const std::vector< { std::vector secret=getECIESSecret(otherKey); - uint256 hmac=makeHMAC(secret, plaintext); + ECIES_HMAC_TYPE hmac=makeHMAC(secret, plaintext); uint128 iv; - if(RAND_bytes(static_cast(iv.begin()), 128/8) != 1) + if(RAND_bytes(static_cast(iv.begin()), ECIES_ENC_SIZE) != 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, + if (EVP_EncryptInit_ex(&ctx, ECIES_ENC_ALGO, 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); + std::vector out(plaintext.size() + ECIES_HMAC_SIZE + (ECIES_ENC_SIZE*2), 0); int len=0, bytesWritten; - // output 256-bit IV - memcpy(&(out.front()), iv.begin(), 32); - len=32; + // output IV + memcpy(&(out.front()), iv.begin(), ECIES_ENC_SIZE); + len=ECIES_ENC_SIZE; - // Encrypt/output 512-bit HMAC + // Encrypt/output HMAC bytesWritten=out.capacity()-len; assert(bytesWritten>0); - if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, hmac.begin(), 64) < 0) + if(EVP_EncryptUpdate(&ctx, &(out.front())+len, &bytesWritten, hmac.begin(), ECIES_HMAC_SIZE) < 0) { EVP_CIPHER_CTX_cleanup(&ctx); throw std::runtime_error(""); @@ -145,14 +155,60 @@ std::vector CKey::decryptECIES(CKey& otherKey, const std::vector< { std::vector secret=getECIESSecret(otherKey); - // 1) Decrypt + // minimum ciphertext = IV + HMAC + 1 block + if(ciphertext.size() < ((2*ECIES_ENC_SIZE)+ECIES_HMAC_SIZE) ) + throw std::runtime_error("ciphertext too short"); - // 2) Extract length and plaintext - - // 3) Compute HMAC - - // 4) Verify + // extract IV + ECIES_ENC_TYPE iv; + memcpy(iv.begin(), &(ciphertext.front()), ECIES_ENC_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) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error("unable to init cipher"); + } + + // 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) ) + { + 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); + outlen=plaintext.size(); + if(EVP_DecryptUpdate(&ctx, &(plaintext.front()), &outlen, + &(ciphertext.front())+ECIES_HMAC_SIZE, ciphertext.size()-ECIES_HMAC_SIZE) != 1) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error("unable to extract plaintext"); + } + + 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); + + if(hmac != makeHMAC(secret, plaintext)) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error("plaintext had bad hmac"); + } + + EVP_CIPHER_CTX_cleanup(&ctx); + return plaintext; } // vim:ts=4