diff --git a/src/ECIES.cpp b/src/ECIES.cpp index ed6d5e5c96..d51119aac4 100644 --- a/src/ECIES.cpp +++ b/src/ECIES.cpp @@ -11,15 +11,30 @@ #include "key.h" +#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_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 + 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) + { + assert(false); 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) +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"); @@ -37,23 +52,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) + 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: -// 1) 256-bits of SHA-512 HMAC of original plaintext -// 2) Original plaintext +// 1) IV (unencrypted) +// 2) Encrypted: 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(ECIES_KEY_TYPE 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.begin(), ECIES_KEY_LENGTH, ECIES_HMAC_ALGO, NULL) != 1) { HMAC_CTX_cleanup(&ctx); throw std::runtime_error("init hmac"); @@ -67,50 +84,47 @@ static uint256 makeHMAC(const std::vector& secret, const std::vec 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"); } - 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; } 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); - uint256 hmac=makeHMAC(secret, plaintext); - - uint128 iv; - if(RAND_bytes(static_cast(iv.begin()), 128/8) != 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, EVP_aes_128_cbc(), 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() + (256/8) + (512/8) + 48, 0); + std::vector out(plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_KEY_SIZE + ECIES_ENC_BLK_SIZE, 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_BLK_SIZE); + len=ECIES_ENC_BLK_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(""); @@ -120,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(""); @@ -129,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; @@ -143,16 +160,95 @@ 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); - // 1) Decrypt + // minimum ciphertext = IV + HMAC + 1 block + if(ciphertext.size() < ((2*ECIES_ENC_BLK_SIZE) + ECIES_HMAC_SIZE) ) + throw std::runtime_error("ciphertext too short"); - // 2) Extract length and plaintext + // extract IV + 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.begin(), iv.begin()) != 1) + { + EVP_CIPHER_CTX_cleanup(&ctx); + throw std::runtime_error("unable to init cipher"); + } - // 3) Compute HMAC + // decrypt mac + ECIES_HMAC_TYPE hmac; + int 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"); + } - // 4) Verify + // 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_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; + 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; +} + +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/SerializedTypes.cpp b/src/SerializedTypes.cpp index 885886adf0..80d63de302 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -61,6 +61,16 @@ std::string STUInt64::getText() const return boost::lexical_cast(value); } +STHash128* STHash128::construct(SerializerIterator& u, const char *name) +{ + return new STHash128(name, u.get128()); +} + +std::string STHash128::getText() const +{ + return value.GetHex(); +} + STHash160* STHash160::construct(SerializerIterator& u, const char *name) { return new STHash160(name, u.get160()); diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 824d170fa7..dc231b49ec 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -14,10 +14,10 @@ enum SerializedTypeID // standard types STI_OBJECT=1, STI_UINT8=2, STI_UINT16=3, STI_UINT32=4, STI_UINT64=5, - STI_HASH160=6, STI_HASH256=7, STI_VL=8, STI_TL=9, + STI_HASH128=6, STI_HASH160=7, STI_HASH256=8, STI_VL=9, STI_TL=10, // high level types - STI_ACCOUNT=10, STI_TRANSACTION=10 + STI_ACCOUNT=100, STI_TRANSACTION=101 }; class SerializedType @@ -144,6 +144,31 @@ public: STUInt64& operator=(uint64 v) { value=v; return *this; } }; +class STHash128 : public SerializedType +{ +protected: + uint128 value; + +public: + + STHash128(const uint128& v=uint128()) : value(v) { ; } + STHash128(const char *n, const uint128& v=uint128()) : SerializedType(n), value(v) { ; } + STHash128() { ; } + static STHash128* construct(SerializerIterator&, const char *name=NULL); + + int getLength() const { return 20; } + SerializedTypeID getType() const { return STI_HASH128; } + STHash128* duplicate() const { return new STHash128(name, value); } + virtual std::string getText() const; + void add(Serializer& s) const { s.add128(value); } + + const uint128& getValue() const { return value; } + void setValue(const uint128& v) { value=v; } + + operator uint128() const { return value; } + STHash128& operator=(const uint128& v) { value=v; return *this; } +}; + class STHash160 : public SerializedType { protected: diff --git a/src/Serializer.cpp b/src/Serializer.cpp index 30fbfae9c0..0962338b48 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -99,25 +99,32 @@ bool Serializer::get64(uint64& o, int offset) const return true; } +bool Serializer::get128(uint128& o, int offset) const +{ + if((offset+(128/8))>mData.size()) return false; + memcpy(o.begin(), &(mData.front())+offset, (128/8)); + return true; +} + bool Serializer::get160(uint160& o, int offset) const { - if((offset+20)>mData.size()) return false; - memcpy(o.begin(), &(mData.front())+offset, 20); + if((offset+(160/8))>mData.size()) return false; + memcpy(o.begin(), &(mData.front())+offset, (160/8)); return true; } bool Serializer::get256(uint256& o, int offset) const { - if((offset+32)>mData.size()) return false; - memcpy(o.begin(), &(mData.front())+offset, 32); + if((offset+(256/8))>mData.size()) return false; + memcpy(o.begin(), &(mData.front())+offset, (256/8)); return true; } uint256 Serializer::get256(int offset) const { uint256 ret; - if((offset+32)>mData.size()) return ret; - memcpy(&ret, &(mData.front())+offset, 32); + if((offset+(256/8))>mData.size()) return ret; + memcpy(&ret, &(mData.front())+offset, (256/8)); return ret; } @@ -403,7 +410,7 @@ bool Serializer::getTaggedList(std::vector& list, int offset, in return true; } -std::vector Serializer::encodeVL(int length) throw() +std::vector Serializer::encodeVL(int length) { unsigned char lenBytes[4]; if(length<=192) @@ -429,7 +436,7 @@ std::vector Serializer::encodeVL(int length) throw() else throw(std::overflow_error("lenlen")); } -int Serializer::encodeLengthLength(int length) throw() +int Serializer::encodeLengthLength(int length) { if(length<0) throw(std::overflow_error("len<0")); if(length<=192) return 1; @@ -438,7 +445,7 @@ int Serializer::encodeLengthLength(int length) throw() throw(std::overflow_error("len>918644")); } -int Serializer::decodeLengthLength(int b1) throw() +int Serializer::decodeLengthLength(int b1) { if(b1<0) throw(std::overflow_error("b1<0")); if(b1<=192) return 1; @@ -447,21 +454,21 @@ int Serializer::decodeLengthLength(int b1) throw() throw(std::overflow_error("b1>254")); } -int Serializer::decodeVLLength(int b1) throw() +int Serializer::decodeVLLength(int b1) { if(b1<0) throw(std::overflow_error("b1<0")); if(b1>254) throw(std::overflow_error("b1>254")); return b1; } -int Serializer::decodeVLLength(int b1, int b2) throw() +int Serializer::decodeVLLength(int b1, int b2) { if(b1<193) throw(std::overflow_error("b1<193")); if(b1>240) throw(std::overflow_error("b1>240")); return 193+(b1-193)*256+b2; } -int Serializer::decodeVLLength(int b1, int b2, int b3) throw() +int Serializer::decodeVLLength(int b1, int b2, int b3) { if(b1<241) throw(std::overflow_error("b1<241")); if(b1>254) throw(std::overflow_error("b1>254")); @@ -478,7 +485,7 @@ int SerializerIterator::getBytesLeft() return mSerializer.getLength()-mPos; } -unsigned char SerializerIterator::get8() throw() +unsigned char SerializerIterator::get8() { int val; if(!mSerializer.get8(val, mPos)) throw(0); @@ -486,7 +493,7 @@ unsigned char SerializerIterator::get8() throw() return val; } -uint16 SerializerIterator::get16() throw() +uint16 SerializerIterator::get16() { uint16 val; if(!mSerializer.get16(val, mPos)) throw(0); @@ -494,7 +501,7 @@ uint16 SerializerIterator::get16() throw() return val; } -uint32 SerializerIterator::get32() throw() +uint32 SerializerIterator::get32() { uint32 val; if(!mSerializer.get32(val, mPos)) throw(0); @@ -502,7 +509,7 @@ uint32 SerializerIterator::get32() throw() return val; } -uint64 SerializerIterator::get64() throw() +uint64 SerializerIterator::get64() { uint64 val; if(!mSerializer.get64(val, mPos)) throw(0); @@ -510,7 +517,15 @@ uint64 SerializerIterator::get64() throw() return val; } -uint160 SerializerIterator::get160() throw() +uint128 SerializerIterator::get128() +{ + uint128 val; + if(!mSerializer.get128(val, mPos)) throw(0); + mPos+=128/8; + return val; +} + +uint160 SerializerIterator::get160() { uint160 val; if(!mSerializer.get160(val, mPos)) throw(0); @@ -518,7 +533,7 @@ uint160 SerializerIterator::get160() throw() return val; } -uint256 SerializerIterator::get256() throw() +uint256 SerializerIterator::get256() { uint256 val; if(!mSerializer.get256(val, mPos)) throw(0); @@ -526,7 +541,7 @@ uint256 SerializerIterator::get256() throw() return val; } -std::vector SerializerIterator::getVL() throw() +std::vector SerializerIterator::getVL() { int length; std::vector vl; @@ -535,7 +550,7 @@ std::vector SerializerIterator::getVL() throw() return vl; } -std::vector SerializerIterator::getTaggedList() throw() +std::vector SerializerIterator::getTaggedList() { int length; std::vector tl; diff --git a/src/Serializer.h b/src/Serializer.h index 8ed1ffd5a9..0573ef3be0 100644 --- a/src/Serializer.h +++ b/src/Serializer.h @@ -49,6 +49,7 @@ class Serializer bool get16(uint16&, int offset) const; bool get32(uint32&, int offset) const; bool get64(uint64&, int offset) const; + bool get128(uint128&, int offset) const; bool get160(uint160&, int offset) const; bool get256(uint256&, int offset) const; uint256 get256(int offset) const; @@ -87,12 +88,12 @@ class Serializer bool addSignature(CKey& rkey); // low-level VL length encode/decode functions - static std::vector encodeVL(int length) throw(); - static int encodeLengthLength(int length) throw(); // length to encode length - static int decodeLengthLength(int b1) throw(); - static int decodeVLLength(int b1) throw(); - static int decodeVLLength(int b1, int b2) throw(); - static int decodeVLLength(int b1, int b2, int b3) throw(); + static std::vector encodeVL(int length); + static int encodeLengthLength(int length); // length to encode length + static int decodeLengthLength(int b1); + static int decodeVLLength(int b1); + static int decodeVLLength(int b1, int b2); + static int decodeVLLength(int b1, int b2, int b3); static void TestSerializer(); }; @@ -113,15 +114,17 @@ public: int getPos(void) { return mPos; } int getBytesLeft(); - unsigned char get8() throw(); - uint16 get16() throw(); - uint32 get32() throw(); - uint64 get64() throw(); - uint160 get160() throw(); - uint256 get256() throw(); + // get functions throw on error + unsigned char get8(); + uint16 get16(); + uint32 get32(); + uint64 get64(); + uint128 get128(); + uint160 get160(); + uint256 get256(); - std::vector getVL() throw(); - std::vector getTaggedList() throw(); + std::vector getVL(); + std::vector getTaggedList(); }; #endif diff --git a/src/key.h b/src/key.h index f61892801e..2586cbf919 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