Browse Source

start of ecies interop with go

cl-refactor
subtly 10 years ago
parent
commit
953bfd2400
  1. 37
      libdevcrypto/Common.cpp
  2. 6
      libdevcrypto/Common.h
  3. 110
      libdevcrypto/CryptoPP.cpp
  4. 9
      libdevcrypto/CryptoPP.h
  5. 22
      libp2p/Host.cpp
  6. 78
      test/crypto.cpp

37
libdevcrypto/Common.cpp

@ -94,24 +94,23 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
return decrypt(_k, _cipher, o_plain);
}
h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
auto iv = Nonce::get();
h128 iv(Nonce::get());
return encryptSymNoAuth(_k, _plain, o_cipher, iv);
}
h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv)
h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv)
{
const int c_aesBlockLen = 16;
o_cipher.resize(_plain.size() + (c_aesBlockLen - (_plain.size() % c_aesBlockLen)) % c_aesBlockLen);
size_t extraBytes = _plain.size() % c_aesBlockLen;
size_t trimmedSize = _plain.size() - extraBytes;
size_t paddedSize = _plain.size() + ((16 - extraBytes) % 16);
o_cipher.resize(paddedSize);
bytes underflowBytes(0);
auto size = _plain.size() - (_plain.size() % c_aesBlockLen);
if (o_cipher.size() > _plain.size())
{
underflowBytes.resize(c_aesBlockLen);
_plain.cropped(size, _plain.size() - size).copyTo(&underflowBytes);
}
bytes underflowBytes(16);
if (o_cipher.size() != _plain.size())
_plain.cropped(trimmedSize, extraBytes).copyTo(&underflowBytes);
const int c_aesKeyLen = 32;
SecByteBlock key(_k.data(), c_aesKeyLen);
@ -119,28 +118,28 @@ h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph
{
CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), _iv.data());
if (size)
e.ProcessData(o_cipher.data(), _plain.data(), _plain.size());
if (underflowBytes.size())
e.ProcessData(o_cipher.data(), underflowBytes.data(), underflowBytes.size());
if (trimmedSize)
e.ProcessData(o_cipher.data(), _plain.data(), trimmedSize);
if (extraBytes)
e.ProcessData(o_cipher.data() + trimmedSize, underflowBytes.data(), underflowBytes.size());
return _iv;
}
catch(CryptoPP::Exception& e)
{
cerr << e.what() << endl;
o_cipher.resize(0);
return h256();
return h128();
}
}
bool dev::decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext)
bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext)
{
const int c_aesKeyLen = 32;
const int c_aesBlockLen = 16;
asserts(_cipher.size() % c_aesBlockLen == 0);
o_plaintext.resize(_cipher.size());
SecByteBlock key(_k.data(), c_aesKeyLen);
const int c_aesKeyLen = 32;
SecByteBlock key(_k.data(), c_aesKeyLen);
try
{
CTR_Mode<AES>::Decryption d;

6
libdevcrypto/Common.h

@ -96,9 +96,9 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Symmetric decryption.
bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv);
bool decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext);
h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv);
bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);

110
libdevcrypto/CryptoPP.cpp

@ -19,8 +19,9 @@
* @date 2014
*/
#include "CryptoPP.h"
#include <libdevcore/Guards.h>
#include "ECDHE.h"
#include "CryptoPP.h"
using namespace std;
using namespace dev;
@ -31,6 +32,113 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes.");
static_assert(dev::Public::size == 64, "Public key must be 64 bytes.");
static_assert(dev::Signature::size == 65, "Signature must be 65 bytes.");
bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen)
{
// similar to go ecies implementation
if (!_s1.size())
{
_s1.resize(1);
asserts(_s1[0] == 0);
}
// for sha3, hash.blocksize is 1088 bits, but this might really be digest size
auto reps = ((kdBitLen + 7) * 8) / (32 * 8);
bytes ctr({0, 0, 0, 1});
bytes k;
CryptoPP::SHA256 ctx;
while (reps--)
{
ctx.Update(ctr.data(), ctr.size());
ctx.Update(_z.data(), Secret::size);
ctx.Update(_s1.data(), _s1.size());
// append hash to k
bytes digest(32);
ctx.Final(digest.data());
ctx.Restart();
k.reserve(k.size() + h256::size);
move(digest.begin(), digest.end(), back_inserter(k));
if (ctr[3]++ && ctr[3] != 0) {
continue;
} else if (ctr[2]++ && ctr[2] != 0) {
continue;
} else if (ctr[1]++ && ctr[1] != 0) {
continue;
} else
ctr[0]++;
}
k.resize(kdBitLen / 8);
return move(k);
}
void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher)
{
// similar to go ecies implementation
// todo: s1/s2/tag
auto r = KeyPair::create();
h256 z;
ecdh::agree(r.sec(), _k, z);
auto key = eciesKDF(z, bytes(), 512);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32);
bytesRef mKey = bytesRef(&key).cropped(32, 32);
sha3(mKey, mKey);
bytes cipherText;
encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128());
if (!cipherText.size())
return;
// d := messageTag(params.Hash, Km, em, s2)
// copy(ct[len(Rb)+len(em):], d)
bytes msg(1 + Public::size + cipherText.size() + 32);
msg[0] = 0x04;
r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size));
bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size());
bytesConstRef(&cipherText).copyTo(msgCipherRef);
io_cipher.resize(msg.size());
io_cipher.swap(msg);
}
void eciesMessageTag()
{
}
void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text)
{
// similar to go ecies implementation
// todo: s1/s2
// io_cipher[0] must be 2, 3, or 4, else invalidpublickey
if (io_text[0] < 2 || io_text[0] > 4)
// invalid message: publickey
return;
if (io_text.size() < (1 + Public::size + h256::size + 1))
// invalid message: length
return;
h256 z;
ecdh::agree(_k, *(Public*)(io_text.data()+1), z);
auto key = eciesKDF(z, bytes(), 512);
bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32);
bytesRef mKey = bytesRef(&key).cropped(32, 32);
sha3(mKey, mKey);
bytes plain;
size_t cipherLen = io_text.size() - 1 - Public::size - h256::size;
bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen);
decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain);
io_text.resize(plain.size());
io_text.swap(plain);
}
void Secp256k1::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;

9
libdevcrypto/CryptoPP.h

@ -81,6 +81,15 @@ public:
/// Decrypts text (replace input).
void decrypt(Secret const& _k, bytes& io_text);
/// Temporary; to replace encrypt once interop w/go is passing.
void encryptECIES(Public const& _k, bytes& io_cipher);
/// Temporary; to replace decrypt once interop w/go is passing.
void decryptECIES(Secret const& _k, bytes& io_text);
/// Key derivation function used by ECIES.
bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256);
/// @returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);

22
libp2p/Host.cpp

@ -394,12 +394,12 @@ void PeerHandshake::transition(boost::system::error_code _ech)
{
clog(NetConnect) << "devp2p.connect.egress sending auth";
// egress: tx auth
asserts(remote);
asserts((bool)remote);
auth.resize(Signature::size + h256::size + Public::size + h256::size + 1);
bytesConstRef sig(&auth[0], Signature::size);
bytesConstRef hepubk(&auth[Signature::size], h256::size);
bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size);
bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size);
bytesRef sig(&auth[0], Signature::size);
bytesRef hepubk(&auth[Signature::size], h256::size);
bytesRef pubk(&auth[Signature::size + h256::size], Public::size);
bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size);
// E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
crypto::ecdh::agree(host->m_alias.sec(), remote, ss);
@ -469,8 +469,8 @@ void PeerHandshake::transition(boost::system::error_code _ech)
clog(NetConnect) << "devp2p.connect.ingress sending ack";
// ingress: tx ack
ack.resize(Public::size + h256::size + 1);
bytesConstRef epubk(&ack[0], Public::size);
bytesConstRef nonce(&ack[Public::size], h256::size);
bytesRef epubk(&ack[0], Public::size);
bytesRef nonce(&ack[Public::size], h256::size);
ecdhe.pubkey().ref().copyTo(epubk);
this->nonce.ref().copyTo(nonce);
ack[ack.size() - 1] = 0x0;
@ -489,7 +489,7 @@ void PeerHandshake::transition(boost::system::error_code _ech)
clog(NetConnect) << "devp2p.connect.ingress sending magic sequence";
PeerSecrets* k = new PeerSecrets;
bytes keyMaterialBytes(512);
bytesConstRef keyMaterial(&keyMaterialBytes);
bytesRef keyMaterial(&keyMaterialBytes);
ecdhe.agree(remoteEphemeral, ess);
ess.ref().copyTo(keyMaterial.cropped(0, h256::size));
@ -521,10 +521,10 @@ void PeerHandshake::transition(boost::system::error_code _ech)
// This test will be replaced with protocol-capabilities information (was Hello packet)
// TESTING: send encrypt magic sequence
bytes magic {0x22,0x40,0x08,0x91};
encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256());
encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128());
k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32);
sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref());
k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32));
k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32));
clog(NetConnect) << "devp2p.connect.egress txrx magic sequence";
k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size());
@ -554,7 +554,7 @@ void PeerHandshake::transition(boost::system::error_code _ech)
/// capabilities handshake (encrypted magic sequence is placeholder)
bytes decryptedMagic;
decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic);
decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic);
if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91)
{
shared_ptr<Peer> p;

78
test/crypto.cpp

@ -228,6 +228,51 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1)
}
}
BOOST_AUTO_TEST_CASE(ecies_kdf)
{
KeyPair local = KeyPair::create();
KeyPair remote = KeyPair::create();
// nonce
Secret z1;
ecdh::agree(local.sec(), remote.pub(), z1);
auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 512);
bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32);
bytesRef mKey1 = bytesRef(&key1).cropped(32, 32);
sha3(mKey1, mKey1);
Secret z2;
ecdh::agree(remote.sec(), local.pub(), z2);
auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 512);
bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32);
bytesRef mKey2 = bytesRef(&key2).cropped(32, 32);
sha3(mKey2, mKey2);
BOOST_REQUIRE(eKey1.toBytes() == eKey2.toBytes());
BOOST_REQUIRE(mKey1.toBytes() == mKey2.toBytes());
BOOST_REQUIRE((u256)h256(z1) > 0);
BOOST_REQUIRE(z1 == z2);
BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0);
BOOST_REQUIRE(key1 == key2);
}
BOOST_AUTO_TEST_CASE(ecies_standard)
{
KeyPair k = KeyPair::create();
string message("Now is the time for all good persons to come to the aid of humanity.");
string original = message;
bytes b = asBytes(message);
s_secp256k1.encryptECIES(k.pub(), b);
BOOST_REQUIRE(b != asBytes(original));
BOOST_REQUIRE(b.size() > 0 && ((u128)h128(b)) > 0);
s_secp256k1.decryptECIES(k.sec(), b);
BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original));
}
BOOST_AUTO_TEST_CASE(ecies_eckeypair)
{
KeyPair k = KeyPair::create();
@ -341,10 +386,10 @@ BOOST_AUTO_TEST_CASE(handshakeNew)
bytes auth(Signature::size + h256::size + Public::size + h256::size + 1);
Secret ssA;
{
bytesConstRef sig(&auth[0], Signature::size);
bytesConstRef hepubk(&auth[Signature::size], h256::size);
bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size);
bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size);
bytesRef sig(&auth[0], Signature::size);
bytesRef hepubk(&auth[Signature::size], h256::size);
bytesRef pubk(&auth[Signature::size + h256::size], Public::size);
bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size);
crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA);
sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig);
@ -371,8 +416,8 @@ BOOST_AUTO_TEST_CASE(handshakeNew)
bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size);
pubk.copyTo(node.ref());
bytesConstRef epubk(&ack[0], Public::size);
bytesConstRef nonce(&ack[Public::size], h256::size);
bytesRef epubk(&ack[0], Public::size);
bytesRef nonce(&ack[Public::size], h256::size);
eB.pubkey().ref().copyTo(epubk);
nonceB.ref().copyTo(nonce);
@ -398,7 +443,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew)
bytesConstRef ackRef(&ackdecrypted);
Public eBAck;
h256 nonceBAck;
ackRef.cropped(0, Public::size).copyTo(bytesConstRef(eBAck.data(), Public::size));
ackRef.cropped(0, Public::size).copyTo(bytesRef(eBAck.data(), Public::size));
ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref());
BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey());
BOOST_REQUIRE_EQUAL(nonceBAck, nonceB);
@ -406,7 +451,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew)
// TODO: export ess and require equal to b
bytes keyMaterialBytes(512);
bytesConstRef keyMaterial(&keyMaterialBytes);
bytesRef keyMaterial(&keyMaterialBytes);
h256 ess;
// todo: ecdh-agree should be able to output bytes
@ -473,7 +518,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew)
BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey());
bytes keyMaterialBytes(512);
bytesConstRef keyMaterial(&keyMaterialBytes);
bytesRef keyMaterial(&keyMaterialBytes);
h256 ess;
// todo: ecdh-agree should be able to output bytes
@ -519,24 +564,29 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac)
}
BOOST_AUTO_TEST_CASE(crypto_rlpxwire)
BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned)
{
Secret encryptK(sha3("..."));
h256 egressMac(sha3("+++"));
// TESTING: send encrypt magic sequence
bytes magic {0x22,0x40,0x08,0x91};
bytes magicCipherAndMac;
encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h256());
encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h128());
magicCipherAndMac.resize(magicCipherAndMac.size() + 32);
sha3mac(egressMac.ref(), &magic, egressMac.ref());
egressMac.ref().copyTo(bytesConstRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32));
egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32));
bytes plaintext;
bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32);
decryptSymNoAuth(encryptK, h256(), cipher, plaintext);
decryptSymNoAuth(encryptK, h128(), cipher, plaintext);
plaintext.resize(magic.size());
BOOST_REQUIRE(plaintext.size() > 0);
BOOST_REQUIRE(magic == plaintext);
}
BOOST_AUTO_TEST_CASE(crypto_aes128_ctr)
BOOST_AUTO_TEST_CASE(ecies_aes128_ctr)
{
Secret k(sha3("0xAAAA"));
string m = "AAAAAAAAAAAAAAAA";

Loading…
Cancel
Save