diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index c3b16ad3389..962c0188d59 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -277,6 +277,21 @@ bool dev::verify(Public const& _p, Signature const& _s, h256 const& _hash) return _p == recover(_s, _hash); } +bool dev::verify(PublicCompressed const& _key, h512 const& _signature, h256 const& _hash) +{ + auto* ctx = getCtx(); + + secp256k1_ecdsa_signature rawSig; + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &rawSig, _signature.data())) + return false; + + secp256k1_pubkey rawPubkey; + if (!secp256k1_ec_pubkey_parse(ctx, &rawPubkey, _key.data(), PublicCompressed::size)) + return false; // Invalid public key. + + return secp256k1_ecdsa_verify(ctx, &rawSig, _hash.data(), &rawPubkey); +} + bytesSec dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen) { bytesSec ret(_dkLen); diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 18125918fab..f7e0e63b344 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -133,6 +133,9 @@ Signature sign(Secret const& _k, h256 const& _hash); /// Verify signature. bool verify(Public const& _k, Signature const& _s, h256 const& _hash); +// Verify signature with compressed public key +bool verify(PublicCompressed const& _key, h512 const& _signature, h256 const& _hash); + /// Derive key via PBKDF2. bytesSec pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32); diff --git a/test/unittests/libdevcrypto/crypto.cpp b/test/unittests/libdevcrypto/crypto.cpp index 85f54e4a932..8a7e2d990d4 100644 --- a/test/unittests/libdevcrypto/crypto.cpp +++ b/test/unittests/libdevcrypto/crypto.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file crypto.cpp * @author Alex Leverington @@ -52,65 +52,65 @@ namespace utf = boost::unit_test; BOOST_AUTO_TEST_SUITE(Crypto) struct DevcryptoTestFixture: public TestOutputHelperFixture { - DevcryptoTestFixture() : s_secp256k1(Secp256k1PP::get()) {} + DevcryptoTestFixture() : s_secp256k1(Secp256k1PP::get()) {} - Secp256k1PP* s_secp256k1; + Secp256k1PP* s_secp256k1; }; BOOST_FIXTURE_TEST_SUITE(devcrypto, DevcryptoTestFixture) static CryptoPP::AutoSeededRandomPool& rng() { - static CryptoPP::AutoSeededRandomPool s_rng; - return s_rng; + static CryptoPP::AutoSeededRandomPool s_rng; + return s_rng; } static CryptoPP::OID& curveOID() { - static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); - return s_curveOID; + static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); + return s_curveOID; } static CryptoPP::DL_GroupParameters_EC& params() { - static CryptoPP::DL_GroupParameters_EC s_params(curveOID()); - return s_params; + static CryptoPP::DL_GroupParameters_EC s_params(curveOID()); + return s_params; } BOOST_AUTO_TEST_CASE(sha3general) { - BOOST_REQUIRE_EQUAL(sha3(""), h256("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - BOOST_REQUIRE_EQUAL(sha3("hello"), h256("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8")); + BOOST_REQUIRE_EQUAL(sha3(""), h256("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_REQUIRE_EQUAL(sha3("hello"), h256("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8")); } BOOST_AUTO_TEST_CASE(emptySHA3Types) { - h256 emptySHA3(fromHex("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - BOOST_REQUIRE_EQUAL(emptySHA3, EmptySHA3); + h256 emptySHA3(fromHex("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_REQUIRE_EQUAL(emptySHA3, EmptySHA3); - h256 emptyListSHA3(fromHex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); - BOOST_REQUIRE_EQUAL(emptyListSHA3, EmptyListSHA3); + h256 emptyListSHA3(fromHex("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")); + BOOST_REQUIRE_EQUAL(emptyListSHA3, EmptyListSHA3); } BOOST_AUTO_TEST_CASE(pubkeyOfZero) { - auto pub = toPublic({}); - BOOST_REQUIRE_EQUAL(pub, Public{}); + auto pub = toPublic({}); + BOOST_REQUIRE_EQUAL(pub, Public{}); } BOOST_AUTO_TEST_CASE(KeyPairMix) { - KeyPair k = KeyPair::create(); - BOOST_REQUIRE(!!k.secret()); - BOOST_REQUIRE(!!k.pub()); - Public test = toPublic(k.secret()); - BOOST_CHECK_EQUAL(k.pub(), test); + KeyPair k = KeyPair::create(); + BOOST_REQUIRE(!!k.secret()); + BOOST_REQUIRE(!!k.pub()); + Public test = toPublic(k.secret()); + BOOST_CHECK_EQUAL(k.pub(), test); } BOOST_AUTO_TEST_CASE(keypairs) { - KeyPair p(Secret(fromHex("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4"))); - BOOST_REQUIRE(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); - BOOST_REQUIRE(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); + KeyPair p(Secret(fromHex("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4"))); + BOOST_REQUIRE(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); + BOOST_REQUIRE(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); BOOST_REQUIRE(toPublicCompressed(p.secret()) == PublicCompressed(fromHex( "0397466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce7"))); @@ -118,722 +118,750 @@ BOOST_AUTO_TEST_CASE(keypairs) eth::Transaction t( 1000, 0, 0, h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")), {}, 0, p.secret()); auto rlp = t.rlp(eth::WithoutSignature); - auto expectedRlp = "dc80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e880"; - BOOST_CHECK_EQUAL(toHex(rlp), expectedRlp); - rlp = t.rlp(eth::WithSignature); - auto expectedRlp2 = "f85f80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8801ca0bd2402a510c9c9afddf2a3f63c869573bd257475bea91d6f164638134a3386d6a0609ad9775fd2715e6a359c627e9338478e4adba65dd0dc6ef2bcbe6398378984"; - BOOST_CHECK_EQUAL(toHex(rlp), expectedRlp2); - BOOST_CHECK_EQUAL(t.sender(), p.address()); + auto expectedRlp = "dc80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e880"; + BOOST_CHECK_EQUAL(toHex(rlp), expectedRlp); + rlp = t.rlp(eth::WithSignature); + auto expectedRlp2 = "f85f80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8801ca0bd2402a510c9c9afddf2a3f63c869573bd257475bea91d6f164638134a3386d6a0609ad9775fd2715e6a359c627e9338478e4adba65dd0dc6ef2bcbe6398378984"; + BOOST_CHECK_EQUAL(toHex(rlp), expectedRlp2); + BOOST_CHECK_EQUAL(t.sender(), p.address()); } BOOST_AUTO_TEST_CASE(KeyPairVerifySecret) { - auto keyPair = KeyPair::create(); - auto* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - BOOST_CHECK(secp256k1_ec_seckey_verify(ctx, keyPair.secret().data())); - secp256k1_context_destroy(ctx); + auto keyPair = KeyPair::create(); + auto* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + BOOST_CHECK(secp256k1_ec_seckey_verify(ctx, keyPair.secret().data())); + secp256k1_context_destroy(ctx); } BOOST_AUTO_TEST_CASE(SignAndRecover) { - // This basic test that compares **fixed** results. Useful to test new - // implementations or changes to implementations. - auto sec = Secret{sha3("sec")}; - auto msg = sha3("msg"); - auto sig = sign(sec, msg); - auto expectedSig = "b826808a8c41e00b7c5d71f211f005a84a7b97949d5e765831e1da4e34c9b8295d2a622eee50f25af78241c1cb7cfff11bcf2a13fe65dee1e3b86fd79a4e3ed000"; - BOOST_CHECK_EQUAL(sig.hex(), expectedSig); + // This basic test that compares **fixed** results. Useful to test new + // implementations or changes to implementations. + auto sec = Secret{sha3("sec")}; + auto msg = sha3("msg"); + auto sig = sign(sec, msg); + auto expectedSig = "b826808a8c41e00b7c5d71f211f005a84a7b97949d5e765831e1da4e34c9b8295d2a622eee50f25af78241c1cb7cfff11bcf2a13fe65dee1e3b86fd79a4e3ed000"; + BOOST_CHECK_EQUAL(sig.hex(), expectedSig); - auto pub = recover(sig, msg); - auto expectedPub = "e40930c838d6cca526795596e368d16083f0672f4ab61788277abfa23c3740e1cc84453b0b24f49086feba0bd978bb4446bae8dff1e79fcc1e9cf482ec2d07c3"; - BOOST_CHECK_EQUAL(pub.hex(), expectedPub); + auto pub = recover(sig, msg); + auto expectedPub = "e40930c838d6cca526795596e368d16083f0672f4ab61788277abfa23c3740e1cc84453b0b24f49086feba0bd978bb4446bae8dff1e79fcc1e9cf482ec2d07c3"; + BOOST_CHECK_EQUAL(pub.hex(), expectedPub); } BOOST_AUTO_TEST_CASE(SignAndRecoverLoop) { - auto num = 13; - auto msg = h256::random(); - while (--num) - { - msg = sha3(msg); - auto kp = KeyPair::create(); - auto sig = sign(kp.secret(), msg); - BOOST_CHECK(verify(kp.pub(), sig, msg)); - auto pub = recover(sig, msg); - BOOST_CHECK_EQUAL(kp.pub(), pub); - } + auto num = 13; + auto msg = h256::random(); + while (--num) + { + msg = sha3(msg); + auto kp = KeyPair::create(); + auto sig = sign(kp.secret(), msg); + BOOST_CHECK(verify(kp.pub(), sig, msg)); + auto pub = recover(sig, msg); + BOOST_CHECK_EQUAL(kp.pub(), pub); + } } BOOST_AUTO_TEST_CASE(cryptopp_patch) { - KeyPair k = KeyPair::create(); - bytes io_text; - s_secp256k1->decrypt(k.secret(), io_text); - BOOST_REQUIRE_EQUAL(io_text.size(), 0); + KeyPair k = KeyPair::create(); + bytes io_text; + s_secp256k1->decrypt(k.secret(), io_text); + BOOST_REQUIRE_EQUAL(io_text.size(), 0); } BOOST_AUTO_TEST_CASE(verify_secert) { - Secret empty; - KeyPair kNot(empty); - BOOST_REQUIRE(!kNot.address()); - KeyPair k(sha3(empty)); - BOOST_REQUIRE(k.address()); + Secret empty; + KeyPair kNot(empty); + BOOST_REQUIRE(!kNot.address()); + KeyPair k(sha3(empty)); + BOOST_REQUIRE(k.address()); +} + +BOOST_AUTO_TEST_CASE(verifyWithPublicCompressed) +{ + PublicCompressed const pub{ + fromHex("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")}; + h512 const sig{ + fromHex("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0" + "b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc93")}; + h256 const msg{fromHex("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")}; + BOOST_REQUIRE(verify(pub, sig, msg)); + + h512 invalidSig{sig}; + ++invalidSig[4]; + BOOST_REQUIRE(!verify(pub, invalidSig, msg)); +} + +BOOST_AUTO_TEST_CASE(signAndVerify) +{ + auto const kp = KeyPair::create(); + auto const msg = h256::random(); + auto const sig = sign(kp.secret(), msg); + + auto const pubCompressed = toPublicCompressed(kp.secret()); + // remove recovery id (65th byte) + h512 const sigWithoutV{sig}; + + BOOST_REQUIRE(verify(pubCompressed, sigWithoutV, msg)); } BOOST_AUTO_TEST_CASE(common_encrypt_decrypt) { - string message("Now is the time for all good persons to come to the aid of humanity."); - bytes m = asBytes(message); - bytesConstRef bcr(&m); + string message("Now is the time for all good persons to come to the aid of humanity."); + bytes m = asBytes(message); + bytesConstRef bcr(&m); - KeyPair k = KeyPair::create(); - bytes cipher; - encrypt(k.pub(), bcr, cipher); - BOOST_REQUIRE(cipher != asBytes(message) && cipher.size() > 0); - - bytes plain; - decrypt(k.secret(), bytesConstRef(&cipher), plain); - - BOOST_REQUIRE(asString(plain) == message); - BOOST_REQUIRE(plain == asBytes(message)); + KeyPair k = KeyPair::create(); + bytes cipher; + encrypt(k.pub(), bcr, cipher); + BOOST_REQUIRE(cipher != asBytes(message) && cipher.size() > 0); + + bytes plain; + decrypt(k.secret(), bytesConstRef(&cipher), plain); + + BOOST_REQUIRE(asString(plain) == message); + BOOST_REQUIRE(plain == asBytes(message)); } BOOST_AUTO_TEST_CASE(sha3_norestart) { - CryptoPP::Keccak_256 ctx; - bytes input(asBytes("test")); - ctx.Update(input.data(), 4); - CryptoPP::Keccak_256 ctxCopy(ctx); - bytes interimDigest(32); - ctx.Final(interimDigest.data()); - ctx.Update(input.data(), 4); - bytes firstDigest(32); - ctx.Final(firstDigest.data()); - BOOST_REQUIRE(interimDigest == firstDigest); - - ctxCopy.Update(input.data(), 4); - bytes finalDigest(32); - ctxCopy.Final(interimDigest.data()); - BOOST_REQUIRE(interimDigest != finalDigest); - - // we can do this another way -- copy the context for final - ctxCopy.Update(input.data(), 4); - ctxCopy.Update(input.data(), 4); - CryptoPP::Keccak_256 finalCtx(ctxCopy); - bytes finalDigest2(32); - finalCtx.Final(finalDigest2.data()); - BOOST_REQUIRE(finalDigest2 == interimDigest); - ctxCopy.Update(input.data(), 4); - bytes finalDigest3(32); - finalCtx.Final(finalDigest3.data()); - BOOST_REQUIRE(finalDigest2 != finalDigest3); + CryptoPP::Keccak_256 ctx; + bytes input(asBytes("test")); + ctx.Update(input.data(), 4); + CryptoPP::Keccak_256 ctxCopy(ctx); + bytes interimDigest(32); + ctx.Final(interimDigest.data()); + ctx.Update(input.data(), 4); + bytes firstDigest(32); + ctx.Final(firstDigest.data()); + BOOST_REQUIRE(interimDigest == firstDigest); + + ctxCopy.Update(input.data(), 4); + bytes finalDigest(32); + ctxCopy.Final(interimDigest.data()); + BOOST_REQUIRE(interimDigest != finalDigest); + + // we can do this another way -- copy the context for final + ctxCopy.Update(input.data(), 4); + ctxCopy.Update(input.data(), 4); + CryptoPP::Keccak_256 finalCtx(ctxCopy); + bytes finalDigest2(32); + finalCtx.Final(finalDigest2.data()); + BOOST_REQUIRE(finalDigest2 == interimDigest); + ctxCopy.Update(input.data(), 4); + bytes finalDigest3(32); + finalCtx.Final(finalDigest3.data()); + BOOST_REQUIRE(finalDigest2 != finalDigest3); } BOOST_AUTO_TEST_CASE(ecies_kdf) { - KeyPair local = KeyPair::create(); - KeyPair remote = KeyPair::create(); - // nonce - Secret z1; - BOOST_CHECK(ecdh::agree(local.secret(), remote.pub(), z1)); - auto key1 = ecies::kdf(z1, bytes(), 64); - bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); - bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); - sha3(mKey1, mKey1); - - Secret z2; - BOOST_CHECK(ecdh::agree(remote.secret(), local.pub(), z2)); - auto key2 = ecies::kdf(z2, bytes(), 64); - 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(!!z1); - BOOST_REQUIRE(z1 == z2); - - BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0); - BOOST_REQUIRE(key1 == key2); + KeyPair local = KeyPair::create(); + KeyPair remote = KeyPair::create(); + // nonce + Secret z1; + BOOST_CHECK(ecdh::agree(local.secret(), remote.pub(), z1)); + auto key1 = ecies::kdf(z1, bytes(), 64); + bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); + bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); + sha3(mKey1, mKey1); + + Secret z2; + BOOST_CHECK(ecdh::agree(remote.secret(), local.pub(), z2)); + auto key2 = ecies::kdf(z2, bytes(), 64); + 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(!!z1); + BOOST_REQUIRE(z1 == z2); + + BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0); + BOOST_REQUIRE(key1 == key2); } BOOST_AUTO_TEST_CASE(ecdh_agree_invalid_pubkey) { - KeyPair ok = KeyPair::create(); - Public pubkey; - ~pubkey; // Create a pubkey of all 1s. - Secret z; - BOOST_CHECK(!ecdh::agree(ok.secret(), pubkey, z)); + KeyPair ok = KeyPair::create(); + Public pubkey; + ~pubkey; // Create a pubkey of all 1s. + Secret z; + BOOST_CHECK(!ecdh::agree(ok.secret(), pubkey, z)); } BOOST_AUTO_TEST_CASE(ecdh_agree_invalid_seckey) { - KeyPair ok = KeyPair::create(); - Secret seckey; // "Null" seckey is invalid. - BOOST_CHECK(!ecdh::agree(seckey, ok.pub(), seckey)); + KeyPair ok = KeyPair::create(); + Secret seckey; // "Null" seckey is invalid. + BOOST_CHECK(!ecdh::agree(seckey, ok.pub(), seckey)); } 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 && b[0] == 0x04); - - s_secp256k1->decryptECIES(k.secret(), b); - BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); + 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 && b[0] == 0x04); + + s_secp256k1->decryptECIES(k.secret(), b); + BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); } BOOST_AUTO_TEST_CASE(ecies_sharedMacData) { - KeyPair k = KeyPair::create(); + KeyPair k = KeyPair::create(); - string message("Now is the time for all good persons to come to the aid of humanity."); - bytes original = asBytes(message); - bytes b = original; + string message("Now is the time for all good persons to come to the aid of humanity."); + bytes original = asBytes(message); + bytes b = original; - string shared("shared MAC data"); - string wrongShared("wrong shared MAC data"); + string shared("shared MAC data"); + string wrongShared("wrong shared MAC data"); - s_secp256k1->encryptECIES(k.pub(), shared, b); - BOOST_REQUIRE(b != original); - BOOST_REQUIRE(b.size() > 0 && b[0] == 0x04); + s_secp256k1->encryptECIES(k.pub(), shared, b); + BOOST_REQUIRE(b != original); + BOOST_REQUIRE(b.size() > 0 && b[0] == 0x04); - BOOST_REQUIRE(!s_secp256k1->decryptECIES(k.secret(), wrongShared, b)); + BOOST_REQUIRE(!s_secp256k1->decryptECIES(k.secret(), wrongShared, b)); - s_secp256k1->decryptECIES(k.secret(), shared, b); + s_secp256k1->decryptECIES(k.secret(), shared, b); - auto decrypted = bytesConstRef(&b).cropped(0, original.size()).toBytes(); - BOOST_CHECK_EQUAL(toHex(decrypted), toHex(original)); + auto decrypted = bytesConstRef(&b).cropped(0, original.size()).toBytes(); + BOOST_CHECK_EQUAL(toHex(decrypted), toHex(original)); } BOOST_AUTO_TEST_CASE(ecies_eckeypair) { - KeyPair k = KeyPair::create(); + 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->encrypt(k.pub(), b); - BOOST_REQUIRE(b != asBytes(original)); + 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->encrypt(k.pub(), b); + BOOST_REQUIRE(b != asBytes(original)); - s_secp256k1->decrypt(k.secret(), b); - BOOST_REQUIRE(b == asBytes(original)); + s_secp256k1->decrypt(k.secret(), b); + BOOST_REQUIRE(b == asBytes(original)); } BOOST_AUTO_TEST_CASE(ecdhCryptopp) { - CryptoPP::ECDH::Domain dhLocal(curveOID()); - CryptoPP::SecByteBlock privLocal(dhLocal.PrivateKeyLength()); - CryptoPP::SecByteBlock pubLocal(dhLocal.PublicKeyLength()); - dhLocal.GenerateKeyPair(rng(), privLocal, pubLocal); - - CryptoPP::ECDH::Domain dhRemote(curveOID()); - CryptoPP::SecByteBlock privRemote(dhRemote.PrivateKeyLength()); - CryptoPP::SecByteBlock pubRemote(dhRemote.PublicKeyLength()); - dhRemote.GenerateKeyPair(rng(), privRemote, pubRemote); - - assert(dhLocal.AgreedValueLength() == dhRemote.AgreedValueLength()); - - // local: send public to remote; remote: send public to local - - // Local - CryptoPP::SecByteBlock sharedLocal(dhLocal.AgreedValueLength()); - assert(dhLocal.Agree(sharedLocal, privLocal, pubRemote)); - - // Remote - CryptoPP::SecByteBlock sharedRemote(dhRemote.AgreedValueLength()); - assert(dhRemote.Agree(sharedRemote, privRemote, pubLocal)); - - // Test - CryptoPP::Integer ssLocal, ssRemote; - ssLocal.Decode(sharedLocal.BytePtr(), sharedLocal.SizeInBytes()); - ssRemote.Decode(sharedRemote.BytePtr(), sharedRemote.SizeInBytes()); - - assert(ssLocal != 0); - assert(ssLocal == ssRemote); - - - // Now use our keys - KeyPair a = KeyPair::create(); - byte puba[65] = {0x04}; - memcpy(&puba[1], a.pub().data(), 64); - - KeyPair b = KeyPair::create(); - byte pubb[65] = {0x04}; - memcpy(&pubb[1], b.pub().data(), 64); - - CryptoPP::ECDH::Domain dhA(curveOID()); - Secret shared; - BOOST_REQUIRE(dhA.Agree(shared.writable().data(), a.secret().data(), pubb)); - BOOST_REQUIRE(shared); + CryptoPP::ECDH::Domain dhLocal(curveOID()); + CryptoPP::SecByteBlock privLocal(dhLocal.PrivateKeyLength()); + CryptoPP::SecByteBlock pubLocal(dhLocal.PublicKeyLength()); + dhLocal.GenerateKeyPair(rng(), privLocal, pubLocal); + + CryptoPP::ECDH::Domain dhRemote(curveOID()); + CryptoPP::SecByteBlock privRemote(dhRemote.PrivateKeyLength()); + CryptoPP::SecByteBlock pubRemote(dhRemote.PublicKeyLength()); + dhRemote.GenerateKeyPair(rng(), privRemote, pubRemote); + + assert(dhLocal.AgreedValueLength() == dhRemote.AgreedValueLength()); + + // local: send public to remote; remote: send public to local + + // Local + CryptoPP::SecByteBlock sharedLocal(dhLocal.AgreedValueLength()); + assert(dhLocal.Agree(sharedLocal, privLocal, pubRemote)); + + // Remote + CryptoPP::SecByteBlock sharedRemote(dhRemote.AgreedValueLength()); + assert(dhRemote.Agree(sharedRemote, privRemote, pubLocal)); + + // Test + CryptoPP::Integer ssLocal, ssRemote; + ssLocal.Decode(sharedLocal.BytePtr(), sharedLocal.SizeInBytes()); + ssRemote.Decode(sharedRemote.BytePtr(), sharedRemote.SizeInBytes()); + + assert(ssLocal != 0); + assert(ssLocal == ssRemote); + + + // Now use our keys + KeyPair a = KeyPair::create(); + byte puba[65] = {0x04}; + memcpy(&puba[1], a.pub().data(), 64); + + KeyPair b = KeyPair::create(); + byte pubb[65] = {0x04}; + memcpy(&pubb[1], b.pub().data(), 64); + + CryptoPP::ECDH::Domain dhA(curveOID()); + Secret shared; + BOOST_REQUIRE(dhA.Agree(shared.writable().data(), a.secret().data(), pubb)); + BOOST_REQUIRE(shared); } BOOST_AUTO_TEST_CASE(ecdhe) { - auto local = KeyPair::create(); - auto remote = KeyPair::create(); - BOOST_CHECK_NE(local.pub(), remote.pub()); + auto local = KeyPair::create(); + auto remote = KeyPair::create(); + BOOST_CHECK_NE(local.pub(), remote.pub()); - // local tx pubkey -> remote - Secret sremote; - BOOST_CHECK(ecdh::agree(remote.secret(), local.pub(), sremote)); - - // remote tx pbukey -> local - Secret slocal; - BOOST_CHECK(ecdh::agree(local.secret(), remote.pub(), slocal)); + // local tx pubkey -> remote + Secret sremote; + BOOST_CHECK(ecdh::agree(remote.secret(), local.pub(), sremote)); + + // remote tx pbukey -> local + Secret slocal; + BOOST_CHECK(ecdh::agree(local.secret(), remote.pub(), slocal)); - BOOST_CHECK(sremote); - BOOST_CHECK(slocal); - BOOST_CHECK_EQUAL(sremote, slocal); + BOOST_CHECK(sremote); + BOOST_CHECK(slocal); + BOOST_CHECK_EQUAL(sremote, slocal); } BOOST_AUTO_TEST_CASE(ecdhAgree) { - auto sec = Secret{sha3("ecdhAgree")}; - auto pub = toPublic(sec); - Secret sharedSec; - BOOST_CHECK(ecdh::agree(sec, pub, sharedSec)); - BOOST_CHECK(sharedSec); - auto expectedSharedSec = "8ac7e464348b85d9fdfc0a81f2fdc0bbbb8ee5fb3840de6ed60ad9372e718977"; - BOOST_CHECK_EQUAL(sharedSec.makeInsecure().hex(), expectedSharedSec); + auto sec = Secret{sha3("ecdhAgree")}; + auto pub = toPublic(sec); + Secret sharedSec; + BOOST_CHECK(ecdh::agree(sec, pub, sharedSec)); + BOOST_CHECK(sharedSec); + auto expectedSharedSec = "8ac7e464348b85d9fdfc0a81f2fdc0bbbb8ee5fb3840de6ed60ad9372e718977"; + BOOST_CHECK_EQUAL(sharedSec.makeInsecure().hex(), expectedSharedSec); } BOOST_AUTO_TEST_CASE(handshakeNew) { - // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) - - h256 base(sha3("privacy")); - sha3(base.ref(), base.ref()); - Secret nodeAsecret(base); - KeyPair nodeA(nodeAsecret); - BOOST_REQUIRE(nodeA.pub()); - - sha3(base.ref(), base.ref()); - Secret nodeBsecret(base); - KeyPair nodeB(nodeBsecret); - BOOST_REQUIRE(nodeB.pub()); - - BOOST_REQUIRE_NE(nodeA.secret(), nodeB.secret()); - - // Initiator is Alice (nodeA) - auto eA = KeyPair::create(); - bytes nAbytes(fromHex("0xAAAA")); - h256 nonceA(sha3(nAbytes)); - bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); - Secret ssA; - { - 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); - - BOOST_CHECK(crypto::ecdh::agree(nodeA.secret(), nodeB.pub(), ssA)); - sign(eA.secret(), (ssA ^ nonceA).makeInsecure()).ref().copyTo(sig); - sha3(eA.pub().ref(), hepubk); - nodeA.pub().ref().copyTo(pubk); - nonceA.ref().copyTo(nonce); - auth[auth.size() - 1] = 0x0; - } - bytes authcipher; - encrypt(nodeB.pub(), &auth, authcipher); - BOOST_REQUIRE_EQUAL(authcipher.size(), 279); - - // Receipient is Bob (nodeB) - auto eB = KeyPair::create(); - bytes nBbytes(fromHex("0xBBBB")); - h256 nonceB(sha3(nAbytes)); - bytes ack(Public::size + h256::size + 1); - { - // todo: replace nodeA.pub() in encrypt() - // decrypt public key from auth - bytes authdecrypted; - decrypt(nodeB.secret(), &authcipher, authdecrypted); - Public node; - bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); - pubk.copyTo(node.ref()); - - bytesRef epubk(&ack[0], Public::size); - bytesRef nonce(&ack[Public::size], h256::size); - - eB.pub().ref().copyTo(epubk); - nonceB.ref().copyTo(nonce); - auth[auth.size() - 1] = 0x0; - } - bytes ackcipher; - encrypt(nodeA.pub(), &ack, ackcipher); - BOOST_REQUIRE_EQUAL(ackcipher.size(), 182); - - BOOST_REQUIRE(eA.pub()); - BOOST_REQUIRE(eB.pub()); - BOOST_REQUIRE_NE(eA.secret(), eB.secret()); - - /// Alice (after receiving ack) - Secret aEncryptK; - Secret aMacK; - Secret aEgressMac; - Secret aIngressMac; - { - bytes ackdecrypted; - decrypt(nodeA.secret(), &ackcipher, ackdecrypted); - BOOST_REQUIRE(ackdecrypted.size()); - bytesConstRef ackRef(&ackdecrypted); - Public eBAck; - h256 nonceBAck; - 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.pub()); - BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); - - // TODO: export ess and require equal to b - - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - Secret ess; - // todo: ecdh-agree should be able to output bytes - BOOST_CHECK(ecdh::agree(eA.secret(), eBAck, ess)); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) + + h256 base(sha3("privacy")); + sha3(base.ref(), base.ref()); + Secret nodeAsecret(base); + KeyPair nodeA(nodeAsecret); + BOOST_REQUIRE(nodeA.pub()); + + sha3(base.ref(), base.ref()); + Secret nodeBsecret(base); + KeyPair nodeB(nodeBsecret); + BOOST_REQUIRE(nodeB.pub()); + + BOOST_REQUIRE_NE(nodeA.secret(), nodeB.secret()); + + // Initiator is Alice (nodeA) + auto eA = KeyPair::create(); + bytes nAbytes(fromHex("0xAAAA")); + h256 nonceA(sha3(nAbytes)); + bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + Secret ssA; + { + 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); + + BOOST_CHECK(crypto::ecdh::agree(nodeA.secret(), nodeB.pub(), ssA)); + sign(eA.secret(), (ssA ^ nonceA).makeInsecure()).ref().copyTo(sig); + sha3(eA.pub().ref(), hepubk); + nodeA.pub().ref().copyTo(pubk); + nonceA.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes authcipher; + encrypt(nodeB.pub(), &auth, authcipher); + BOOST_REQUIRE_EQUAL(authcipher.size(), 279); + + // Receipient is Bob (nodeB) + auto eB = KeyPair::create(); + bytes nBbytes(fromHex("0xBBBB")); + h256 nonceB(sha3(nAbytes)); + bytes ack(Public::size + h256::size + 1); + { + // todo: replace nodeA.pub() in encrypt() + // decrypt public key from auth + bytes authdecrypted; + decrypt(nodeB.secret(), &authcipher, authdecrypted); + Public node; + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + pubk.copyTo(node.ref()); + + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); + + eB.pub().ref().copyTo(epubk); + nonceB.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes ackcipher; + encrypt(nodeA.pub(), &ack, ackcipher); + BOOST_REQUIRE_EQUAL(ackcipher.size(), 182); + + BOOST_REQUIRE(eA.pub()); + BOOST_REQUIRE(eB.pub()); + BOOST_REQUIRE_NE(eA.secret(), eB.secret()); + + /// Alice (after receiving ack) + Secret aEncryptK; + Secret aMacK; + Secret aEgressMac; + Secret aIngressMac; + { + bytes ackdecrypted; + decrypt(nodeA.secret(), &ackcipher, ackdecrypted); + BOOST_REQUIRE(ackdecrypted.size()); + bytesConstRef ackRef(&ackdecrypted); + Public eBAck; + h256 nonceBAck; + 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.pub()); + BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); + + // TODO: export ess and require equal to b + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + Secret ess; + // todo: ecdh-agree should be able to output bytes + BOOST_CHECK(ecdh::agree(eA.secret(), eBAck, ess)); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); - aEncryptK = sha3Secure(keyMaterial); - aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - aMacK = sha3Secure(keyMaterial); - - keyMaterialBytes.resize(h256::size + authcipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (aMacK ^ nonceBAck).ref().copyTo(keyMaterial); - bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); - aEgressMac = sha3Secure(keyMaterial); - - keyMaterialBytes.resize(h256::size + ackcipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (aMacK ^ nonceA).ref().copyTo(keyMaterial); - bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); - aIngressMac = sha3Secure(keyMaterial); - } - - - /// Bob (after sending ack) - Secret ssB; - BOOST_CHECK(crypto::ecdh::agree(nodeB.secret(), nodeA.pub(), ssB)); - BOOST_REQUIRE_EQUAL(ssA, ssB); - - Secret bEncryptK; - Secret bMacK; - Secret bEgressMac; - Secret bIngressMac; - { - bytes authdecrypted; - decrypt(nodeB.secret(), &authcipher, authdecrypted); - BOOST_REQUIRE(authdecrypted.size()); - bytesConstRef ackRef(&authdecrypted); - Signature sigAuth; - h256 heA; - Public eAAuth; - Public nodeAAuth; - h256 nonceAAuth; - bytesConstRef sig(&authdecrypted[0], Signature::size); - bytesConstRef hepubk(&authdecrypted[Signature::size], h256::size); - bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&authdecrypted[Signature::size + h256::size + Public::size], h256::size); - - nonce.copyTo(nonceAAuth.ref()); - pubk.copyTo(nodeAAuth.ref()); - BOOST_REQUIRE(nonceAAuth); - BOOST_REQUIRE_EQUAL(nonceA, nonceAAuth); - BOOST_REQUIRE(nodeAAuth); - BOOST_REQUIRE_EQUAL(nodeA.pub(), nodeAAuth); // bad test, bad!!! - hepubk.copyTo(heA.ref()); - sig.copyTo(sigAuth.ref()); - - Secret ss; - BOOST_CHECK(ecdh::agree(nodeB.secret(), nodeAAuth, ss)); - eAAuth = recover(sigAuth, (ss ^ nonceAAuth).makeInsecure()); - // todo: test when this fails; means remote is bad or packet bits were flipped - BOOST_REQUIRE_EQUAL(heA, sha3(eAAuth)); - BOOST_REQUIRE_EQUAL(eAAuth, eA.pub()); - - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - Secret ess; - // todo: ecdh-agree should be able to output bytes - BOOST_CHECK(ecdh::agree(eB.secret(), eAAuth, ess)); + aEncryptK = sha3Secure(keyMaterial); + aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + aMacK = sha3Secure(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceBAck).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + aEgressMac = sha3Secure(keyMaterial); + + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + aIngressMac = sha3Secure(keyMaterial); + } + + + /// Bob (after sending ack) + Secret ssB; + BOOST_CHECK(crypto::ecdh::agree(nodeB.secret(), nodeA.pub(), ssB)); + BOOST_REQUIRE_EQUAL(ssA, ssB); + + Secret bEncryptK; + Secret bMacK; + Secret bEgressMac; + Secret bIngressMac; + { + bytes authdecrypted; + decrypt(nodeB.secret(), &authcipher, authdecrypted); + BOOST_REQUIRE(authdecrypted.size()); + bytesConstRef ackRef(&authdecrypted); + Signature sigAuth; + h256 heA; + Public eAAuth; + Public nodeAAuth; + h256 nonceAAuth; + bytesConstRef sig(&authdecrypted[0], Signature::size); + bytesConstRef hepubk(&authdecrypted[Signature::size], h256::size); + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&authdecrypted[Signature::size + h256::size + Public::size], h256::size); + + nonce.copyTo(nonceAAuth.ref()); + pubk.copyTo(nodeAAuth.ref()); + BOOST_REQUIRE(nonceAAuth); + BOOST_REQUIRE_EQUAL(nonceA, nonceAAuth); + BOOST_REQUIRE(nodeAAuth); + BOOST_REQUIRE_EQUAL(nodeA.pub(), nodeAAuth); // bad test, bad!!! + hepubk.copyTo(heA.ref()); + sig.copyTo(sigAuth.ref()); + + Secret ss; + BOOST_CHECK(ecdh::agree(nodeB.secret(), nodeAAuth, ss)); + eAAuth = recover(sigAuth, (ss ^ nonceAAuth).makeInsecure()); + // todo: test when this fails; means remote is bad or packet bits were flipped + BOOST_REQUIRE_EQUAL(heA, sha3(eAAuth)); + BOOST_REQUIRE_EQUAL(eAAuth, eA.pub()); + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + Secret ess; + // todo: ecdh-agree should be able to output bytes + BOOST_CHECK(ecdh::agree(eB.secret(), eAAuth, ess)); // s_secp256k1->agree(eB.seckey(), eAAuth, ess); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); - bEncryptK = sha3Secure(keyMaterial); - bEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - bMacK = sha3Secure(keyMaterial); - - // todo: replace nonceB with decrypted nonceB - keyMaterialBytes.resize(h256::size + ackcipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (bMacK ^ nonceAAuth).ref().copyTo(keyMaterial); - bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); - bEgressMac = sha3Secure(keyMaterial); - - keyMaterialBytes.resize(h256::size + authcipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (bMacK ^ nonceB).ref().copyTo(keyMaterial); - bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); - bIngressMac = sha3Secure(keyMaterial); - } - - BOOST_REQUIRE_EQUAL(aEncryptK, bEncryptK); - BOOST_REQUIRE_EQUAL(aMacK, bMacK); - BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); - BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); - - - + bEncryptK = sha3Secure(keyMaterial); + bEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + bMacK = sha3Secure(keyMaterial); + + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceAAuth).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + bEgressMac = sha3Secure(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + bIngressMac = sha3Secure(keyMaterial); + } + + BOOST_REQUIRE_EQUAL(aEncryptK, bEncryptK); + BOOST_REQUIRE_EQUAL(aMacK, bMacK); + BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); + BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); + + + } BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) { - SecureFixedHash<16> encryptK(sha3("..."), h128::AlignLeft); - h256 egressMac(sha3("+++")); - // TESTING: send encrypt magic sequence - bytes magic {0x22,0x40,0x08,0x91}; - bytes magicCipherAndMac; - magicCipherAndMac = encryptSymNoAuth(encryptK, h128(), &magic); - - magicCipherAndMac.resize(magicCipherAndMac.size() + 32); - sha3mac(egressMac.ref(), &magic, egressMac.ref()); - egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); - - bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); - bytes plaintext = decryptSymNoAuth(encryptK, h128(), cipher).makeInsecure(); - - plaintext.resize(magic.size()); - // @alex @subtly TODO: FIX: this check is pointless with the above line. - BOOST_REQUIRE(plaintext.size() > 0); - BOOST_REQUIRE(magic == plaintext); + SecureFixedHash<16> encryptK(sha3("..."), h128::AlignLeft); + h256 egressMac(sha3("+++")); + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + bytes magicCipherAndMac; + magicCipherAndMac = encryptSymNoAuth(encryptK, h128(), &magic); + + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); + sha3mac(egressMac.ref(), &magic, egressMac.ref()); + egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + + bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); + bytes plaintext = decryptSymNoAuth(encryptK, h128(), cipher).makeInsecure(); + + plaintext.resize(magic.size()); + // @alex @subtly TODO: FIX: this check is pointless with the above line. + BOOST_REQUIRE(plaintext.size() > 0); + BOOST_REQUIRE(magic == plaintext); } BOOST_AUTO_TEST_CASE(ecies_aes128_ctr) { - SecureFixedHash<16> k(sha3("0xAAAA"), h128::AlignLeft); - string m = "AAAAAAAAAAAAAAAA"; - bytesConstRef msg((byte*)m.data(), m.size()); + SecureFixedHash<16> k(sha3("0xAAAA"), h128::AlignLeft); + string m = "AAAAAAAAAAAAAAAA"; + bytesConstRef msg((byte*)m.data(), m.size()); - bytes ciphertext; - h128 iv; - tie(ciphertext, iv) = encryptSymNoAuth(k, msg); - - bytes plaintext = decryptSymNoAuth(k, iv, &ciphertext).makeInsecure(); - BOOST_REQUIRE_EQUAL(asString(plaintext), m); + bytes ciphertext; + h128 iv; + tie(ciphertext, iv) = encryptSymNoAuth(k, msg); + + bytes plaintext = decryptSymNoAuth(k, iv, &ciphertext).makeInsecure(); + BOOST_REQUIRE_EQUAL(asString(plaintext), m); } BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) { - const int aesKeyLen = 16; - BOOST_REQUIRE(sizeof(char) == sizeof(byte)); - - // generate test key - CryptoPP::AutoSeededRandomPool rng; - CryptoPP::SecByteBlock key(0x00, aesKeyLen); - rng.GenerateBlock(key, key.size()); - - // cryptopp uses IV as nonce/counter which is same as using nonce w/0 ctr - FixedHash ctr; - rng.GenerateBlock(ctr.data(), sizeof(ctr)); - - // used for decrypt - FixedHash ctrcopy(ctr); - - string text = "Now is the time for all good persons to come to the aid of humanity."; - unsigned char const* in = (unsigned char*)&text[0]; - unsigned char* out = (unsigned char*)&text[0]; - string original = text; - string doublespeak = text + text; - - string cipherCopy; - try - { - CryptoPP::CTR_Mode::Encryption e; - e.SetKeyWithIV(key, key.size(), ctr.data()); - - // 68 % 255 should be difference of counter - e.ProcessData(out, in, text.size()); - ctr = h128(u128(ctr) + text.size() / 16); - - BOOST_REQUIRE(text != original); - cipherCopy = text; - } - catch (CryptoPP::Exception& _e) - { - cerr << _e.what() << endl; - } - - try - { - CryptoPP::CTR_Mode::Decryption d; - d.SetKeyWithIV(key, key.size(), ctrcopy.data()); - d.ProcessData(out, in, text.size()); - BOOST_REQUIRE(text == original); - } - catch (CryptoPP::Exception& _e) - { - cerr << _e.what() << endl; - } - - - // reencrypt ciphertext... - try - { - BOOST_REQUIRE(cipherCopy != text); - in = (unsigned char*)&cipherCopy[0]; - out = (unsigned char*)&cipherCopy[0]; - - CryptoPP::CTR_Mode::Encryption e; - e.SetKeyWithIV(key, key.size(), ctrcopy.data()); - e.ProcessData(out, in, text.size()); - - // yep, ctr mode. - BOOST_REQUIRE(cipherCopy == original); - } - catch (CryptoPP::Exception& _e) - { - cerr << _e.what() << endl; - } - + const int aesKeyLen = 16; + BOOST_REQUIRE(sizeof(char) == sizeof(byte)); + + // generate test key + CryptoPP::AutoSeededRandomPool rng; + CryptoPP::SecByteBlock key(0x00, aesKeyLen); + rng.GenerateBlock(key, key.size()); + + // cryptopp uses IV as nonce/counter which is same as using nonce w/0 ctr + FixedHash ctr; + rng.GenerateBlock(ctr.data(), sizeof(ctr)); + + // used for decrypt + FixedHash ctrcopy(ctr); + + string text = "Now is the time for all good persons to come to the aid of humanity."; + unsigned char const* in = (unsigned char*)&text[0]; + unsigned char* out = (unsigned char*)&text[0]; + string original = text; + string doublespeak = text + text; + + string cipherCopy; + try + { + CryptoPP::CTR_Mode::Encryption e; + e.SetKeyWithIV(key, key.size(), ctr.data()); + + // 68 % 255 should be difference of counter + e.ProcessData(out, in, text.size()); + ctr = h128(u128(ctr) + text.size() / 16); + + BOOST_REQUIRE(text != original); + cipherCopy = text; + } + catch (CryptoPP::Exception& _e) + { + cerr << _e.what() << endl; + } + + try + { + CryptoPP::CTR_Mode::Decryption d; + d.SetKeyWithIV(key, key.size(), ctrcopy.data()); + d.ProcessData(out, in, text.size()); + BOOST_REQUIRE(text == original); + } + catch (CryptoPP::Exception& _e) + { + cerr << _e.what() << endl; + } + + + // reencrypt ciphertext... + try + { + BOOST_REQUIRE(cipherCopy != text); + in = (unsigned char*)&cipherCopy[0]; + out = (unsigned char*)&cipherCopy[0]; + + CryptoPP::CTR_Mode::Encryption e; + e.SetKeyWithIV(key, key.size(), ctrcopy.data()); + e.ProcessData(out, in, text.size()); + + // yep, ctr mode. + BOOST_REQUIRE(cipherCopy == original); + } + catch (CryptoPP::Exception& _e) + { + cerr << _e.what() << endl; + } + } BOOST_AUTO_TEST_CASE(cryptopp_aes128_cbc) { - const int aesKeyLen = 16; - BOOST_REQUIRE(sizeof(char) == sizeof(byte)); - - CryptoPP::AutoSeededRandomPool rng; - CryptoPP::SecByteBlock key(0x00, aesKeyLen); - rng.GenerateBlock(key, key.size()); - - // Generate random IV - byte iv[CryptoPP::AES::BLOCKSIZE]; - rng.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE); - - string string128("AAAAAAAAAAAAAAAA"); - string plainOriginal = string128; - - CryptoPP::CBC_Mode::Encryption cbcEncryption(key, key.size(), iv); - cbcEncryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size()); - BOOST_REQUIRE(string128 != plainOriginal); - - CryptoPP::CBC_Mode::Decryption cbcDecryption(key, key.size(), iv); - cbcDecryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size()); - BOOST_REQUIRE(plainOriginal == string128); - - - // plaintext whose size isn't divisible by block size must use stream filter for padding - string string192("AAAAAAAAAAAAAAAABBBBBBBB"); - plainOriginal = string192; - - string cipher; - CryptoPP::StreamTransformationFilter* aesStream = new CryptoPP::StreamTransformationFilter(cbcEncryption, new CryptoPP::StringSink(cipher)); - CryptoPP::StringSource source(string192, true, aesStream); - BOOST_REQUIRE(cipher.size() == 32); - - byte* pOut = reinterpret_cast(&string192[0]); - byte const* pIn = reinterpret_cast(cipher.data()); - cbcDecryption.ProcessData(pOut, pIn, cipher.size()); - BOOST_REQUIRE(string192 == plainOriginal); + const int aesKeyLen = 16; + BOOST_REQUIRE(sizeof(char) == sizeof(byte)); + + CryptoPP::AutoSeededRandomPool rng; + CryptoPP::SecByteBlock key(0x00, aesKeyLen); + rng.GenerateBlock(key, key.size()); + + // Generate random IV + byte iv[CryptoPP::AES::BLOCKSIZE]; + rng.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE); + + string string128("AAAAAAAAAAAAAAAA"); + string plainOriginal = string128; + + CryptoPP::CBC_Mode::Encryption cbcEncryption(key, key.size(), iv); + cbcEncryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size()); + BOOST_REQUIRE(string128 != plainOriginal); + + CryptoPP::CBC_Mode::Decryption cbcDecryption(key, key.size(), iv); + cbcDecryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size()); + BOOST_REQUIRE(plainOriginal == string128); + + + // plaintext whose size isn't divisible by block size must use stream filter for padding + string string192("AAAAAAAAAAAAAAAABBBBBBBB"); + plainOriginal = string192; + + string cipher; + CryptoPP::StreamTransformationFilter* aesStream = new CryptoPP::StreamTransformationFilter(cbcEncryption, new CryptoPP::StringSink(cipher)); + CryptoPP::StringSource source(string192, true, aesStream); + BOOST_REQUIRE(cipher.size() == 32); + + byte* pOut = reinterpret_cast(&string192[0]); + byte const* pIn = reinterpret_cast(cipher.data()); + cbcDecryption.ProcessData(pOut, pIn, cipher.size()); + BOOST_REQUIRE(string192 == plainOriginal); } BOOST_AUTO_TEST_CASE(recoverVgt3) { - // base secret - Secret secret(sha3("privacy")); - - // we get ec params from signer - CryptoPP::ECDSA::Signer signer; - - // e := sha3(msg) - bytes e(fromHex("0x01")); - e.resize(32); - int tests = 13; - while (sha3(&e, &e), secret = sha3(secret), tests--) - { - KeyPair key(secret); - Public pkey = key.pub(); - signer.AccessKey().Initialize(params(), CryptoPP::Integer(secret.data(), Secret::size)); - - h256 he(sha3(e)); - CryptoPP::Integer heInt(he.asBytes().data(), 32); - h256 k(crypto::kdf(secret, he)); - CryptoPP::Integer kInt(k.asBytes().data(), 32); - kInt %= params().GetSubgroupOrder()-1; - - CryptoPP::ECP::Point rp = params().ExponentiateBase(kInt); - CryptoPP::Integer const& q = params().GetGroupOrder(); - CryptoPP::Integer r = params().ConvertElementToInteger(rp); - - CryptoPP::Integer kInv = kInt.InverseMod(q); - CryptoPP::Integer s = (kInv * (CryptoPP::Integer(secret.data(), 32) * r + heInt)) % q; - BOOST_REQUIRE(!!r && !!s); - - //try recover function on diffrent v values (should be invalid) - for (size_t i = 0; i < 10; i++) - { - Signature sig; - sig[64] = i; - r.Encode(sig.data(), 32); - s.Encode(sig.data() + 32, 32); - - Public p = dev::recover(sig, he); - size_t expectI = rp.y.IsOdd() ? 1 : 0; - if (i == expectI) - BOOST_REQUIRE(p == pkey); - else - BOOST_REQUIRE(p != pkey); - } - } + // base secret + Secret secret(sha3("privacy")); + + // we get ec params from signer + CryptoPP::ECDSA::Signer signer; + + // e := sha3(msg) + bytes e(fromHex("0x01")); + e.resize(32); + int tests = 13; + while (sha3(&e, &e), secret = sha3(secret), tests--) + { + KeyPair key(secret); + Public pkey = key.pub(); + signer.AccessKey().Initialize(params(), CryptoPP::Integer(secret.data(), Secret::size)); + + h256 he(sha3(e)); + CryptoPP::Integer heInt(he.asBytes().data(), 32); + h256 k(crypto::kdf(secret, he)); + CryptoPP::Integer kInt(k.asBytes().data(), 32); + kInt %= params().GetSubgroupOrder()-1; + + CryptoPP::ECP::Point rp = params().ExponentiateBase(kInt); + CryptoPP::Integer const& q = params().GetGroupOrder(); + CryptoPP::Integer r = params().ConvertElementToInteger(rp); + + CryptoPP::Integer kInv = kInt.InverseMod(q); + CryptoPP::Integer s = (kInv * (CryptoPP::Integer(secret.data(), 32) * r + heInt)) % q; + BOOST_REQUIRE(!!r && !!s); + + //try recover function on diffrent v values (should be invalid) + for (size_t i = 0; i < 10; i++) + { + Signature sig; + sig[64] = i; + r.Encode(sig.data(), 32); + s.Encode(sig.data() + 32, 32); + + Public p = dev::recover(sig, he); + size_t expectI = rp.y.IsOdd() ? 1 : 0; + if (i == expectI) + BOOST_REQUIRE(p == pkey); + else + BOOST_REQUIRE(p != pkey); + } + } } BOOST_AUTO_TEST_CASE(PerfSHA256_32, *utf::label("perf")) { - if (!test::Options::get().all) - { - std::cout << "Skipping test Crypto/devcrypto/PerfSHA256_32. Use --all to run it.\n"; - return; - } + if (!test::Options::get().all) + { + std::cout << "Skipping test Crypto/devcrypto/PerfSHA256_32. Use --all to run it.\n"; + return; + } - h256 hash; - for (auto i = 0; i < 1000000; ++i) - hash = sha256(hash.ref()); + h256 hash; + for (auto i = 0; i < 1000000; ++i) + hash = sha256(hash.ref()); - BOOST_CHECK_EQUAL(hash[0], 0x2a); + BOOST_CHECK_EQUAL(hash[0], 0x2a); } BOOST_AUTO_TEST_CASE(PerfSHA256_4000, *utf::label("perf")) { - if (!test::Options::get().all) - { - std::cout << "Skipping test Crypto/devcrypto/PerfSHA256_4000. Use --all to run it.\n"; - return; - } - - static const size_t dataSize = 4097; - bytes data(dataSize); - for (auto i = 0; i < 100000; ++i) - { - auto hash = sha256(&data); - auto idx = ((hash[1] << 8) | hash[2]) % (dataSize - hash.size); - std::copy(hash.data(), hash.data() + hash.size, data.begin() + idx); - } - - BOOST_CHECK_EQUAL(data[0], 0x4d); + if (!test::Options::get().all) + { + std::cout << "Skipping test Crypto/devcrypto/PerfSHA256_4000. Use --all to run it.\n"; + return; + } + + static const size_t dataSize = 4097; + bytes data(dataSize); + for (auto i = 0; i < 100000; ++i) + { + auto hash = sha256(&data); + auto idx = ((hash[1] << 8) | hash[2]) % (dataSize - hash.size); + std::copy(hash.data(), hash.data() + hash.size, data.begin() + idx); + } + + BOOST_CHECK_EQUAL(data[0], 0x4d); } BOOST_AUTO_TEST_SUITE_END()