diff --git a/CMakeLists.txt b/CMakeLists.txt index a95eff3..fd2eb7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ set( fc_sources src/log/file_appender.cpp src/log/gelf_appender.cpp src/log/logger_config.cpp + src/crypto/_digest_common.cpp src/crypto/openssl.cpp src/crypto/aes.cpp src/crypto/crc.cpp @@ -365,6 +366,8 @@ add_executable( all_tests tests/all_tests.cpp tests/crypto/bigint_test.cpp tests/crypto/blind.cpp tests/crypto/blowfish_test.cpp + tests/crypto/dh_test.cpp + tests/crypto/rand_test.cpp tests/crypto/sha_tests.cpp tests/network/ntp_test.cpp tests/thread/task_cancel.cpp diff --git a/include/fc/crypto/dh.hpp b/include/fc/crypto/dh.hpp index 5e6f89f..9bd4d7b 100644 --- a/include/fc/crypto/dh.hpp +++ b/include/fc/crypto/dh.hpp @@ -1,11 +1,12 @@ #pragma once +#include #include #include namespace fc { struct diffie_hellman { - diffie_hellman():valid(0),g(5){} + diffie_hellman():valid(0),g(5){ fc::init_openssl(); } bool generate_params( int s, uint8_t g ); bool generate_pub_key(); bool compute_shared_key( const char* buf, uint32_t s ); diff --git a/include/fc/crypto/openssl.hpp b/include/fc/crypto/openssl.hpp index 5811390..af883d6 100644 --- a/include/fc/crypto/openssl.hpp +++ b/include/fc/crypto/openssl.hpp @@ -22,11 +22,10 @@ namespace fc { ssl_wrapper(ssl_type* obj):obj(obj) {} - operator ssl_type*() - { - return obj; - } + operator ssl_type*() { return obj; } + operator const ssl_type*() const { return obj; } ssl_type* operator->() { return obj; } + const ssl_type* operator->() const { return obj; } ssl_type* obj; }; diff --git a/src/crypto/_digest_common.cpp b/src/crypto/_digest_common.cpp new file mode 100644 index 0000000..2c5c652 --- /dev/null +++ b/src/crypto/_digest_common.cpp @@ -0,0 +1,29 @@ +#include +#include "_digest_common.hpp" + +namespace fc { namespace detail { + static void shift_l( const uint8_t* in, uint8_t* out, std::size_t n, unsigned int i) { + if (i < n) { + memcpy( out, in + i, n-i ); + } else { + i = n; + } + memset( out + (n-i), 0, i ); + } + + void shift_l( const char* in, char* out, std::size_t n, unsigned int i) { + const uint8_t* in8 = (uint8_t*) in; + uint8_t* out8 = (uint8_t*) out; + + if (i >= 8) { + shift_l( in8, out8, n, i >> 3 ); + i &= 7; + in8 = out8; + } + + std::size_t p; + for( p = 0; p < n-1; ++p ) + out8[p] = (in8[p] << i) | (in8[p+1]>>(8-i)); + out8[p] = in8[p] << i; + } +}} diff --git a/src/crypto/_digest_common.hpp b/src/crypto/_digest_common.hpp new file mode 100644 index 0000000..8aa21b3 --- /dev/null +++ b/src/crypto/_digest_common.hpp @@ -0,0 +1,7 @@ +#pragma once + +/* Common stuff for cryptographic hashes + */ +namespace fc { namespace detail { + void shift_l( const char* in, char* out, std::size_t n, unsigned int i); +}} diff --git a/src/crypto/dh.cpp b/src/crypto/dh.cpp index 7b49398..cbd7dcc 100644 --- a/src/crypto/dh.cpp +++ b/src/crypto/dh.cpp @@ -2,52 +2,45 @@ #include namespace fc { + SSL_TYPE(ssl_dh, DH, DH_free) + + static bool validate( const ssl_dh& dh, bool& valid ) { + int check; + DH_check(dh,&check); + return valid = !(check /*& DH_CHECK_P_NOT_SAFE_PRIME*/); + } + bool diffie_hellman::generate_params( int s, uint8_t g ) { - DH* dh = DH_generate_parameters( s, g, NULL, NULL ); + ssl_dh dh = DH_generate_parameters( s, g, NULL, NULL ); p.resize( BN_num_bytes( dh->p ) ); if( p.size() ) BN_bn2bin( dh->p, (unsigned char*)&p.front() ); this->g = g; - - int check; - DH_check(dh,&check); - DH_free(dh); - - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) - return valid = false; - return valid = true; + return fc::validate( dh, valid ); } + bool diffie_hellman::validate() { if( !p.size() ) return valid = false; - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->g = BN_bin2bn( (unsigned char*)&g, 1, NULL ); - - int check; - DH_check(dh,&check); - DH_free(dh); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) - return valid = false; - return valid = true; + return fc::validate( dh, valid ); } bool diffie_hellman::generate_pub_key() { if( !p.size() ) return valid = false; - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->g = BN_bin2bn( (unsigned char*)&g, 1, NULL ); - int check; - DH_check(dh,&check); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) + if( !fc::validate( dh, valid ) ) { - DH_free(dh); - return valid = false; + return false; } DH_generate_key(dh); @@ -58,11 +51,10 @@ namespace fc { if( priv_key.size() ) BN_bn2bin( dh->priv_key, (unsigned char*)&priv_key.front() ); - DH_free(dh); - return valid = true; + return true; } bool diffie_hellman::compute_shared_key( const char* buf, uint32_t s ) { - DH* dh = DH_new(); + ssl_dh dh = DH_new(); dh->p = BN_bin2bn( (unsigned char*)&p.front(), p.size(), NULL ); dh->pub_key = BN_bin2bn( (unsigned char*)&pub_key.front(), pub_key.size(), NULL ); dh->priv_key = BN_bin2bn( (unsigned char*)&priv_key.front(), priv_key.size(), NULL ); @@ -70,22 +62,19 @@ namespace fc { int check; DH_check(dh,&check); - if( check & DH_CHECK_P_NOT_SAFE_PRIME ) + if( !fc::validate( dh, valid ) ) { - DH_free(dh); - return valid = false; + return false; } - - BIGNUM* pk = BN_bin2bn( (unsigned char*)buf, s, NULL ); + ssl_bignum pk; + BN_bin2bn( (unsigned char*)buf, s, pk ); shared_key.resize( DH_size(dh) ); DH_compute_key( (unsigned char*)&shared_key.front(), pk, dh ); - BN_free(pk); - DH_free(dh); - return valid = true; + + return true; } bool diffie_hellman::compute_shared_key( const std::vector& pubk ) { return compute_shared_key( &pubk.front(), pubk.size() ); } - } diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp index ed3671a..e336b39 100644 --- a/src/crypto/ripemd160.cpp +++ b/src/crypto/ripemd160.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "_digest_common.hpp" namespace fc { @@ -69,11 +70,7 @@ void ripemd160::encoder::reset() { ripemd160 operator << ( const ripemd160& h1, uint32_t i ) { ripemd160 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[19] = s[19] << i; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); return result; } ripemd160 operator ^ ( const ripemd160& h1, const ripemd160& h2 ) { diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp index 479d1ed..88107db 100644 --- a/src/crypto/sha1.cpp +++ b/src/crypto/sha1.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "_digest_common.hpp" namespace fc { @@ -54,11 +55,7 @@ void sha1::encoder::reset() { sha1 operator << ( const sha1& h1, uint32_t i ) { sha1 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[19] = s[19] << i; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); return result; } sha1 operator ^ ( const sha1& h1, const sha1& h2 ) { diff --git a/src/crypto/sha224.cpp b/src/crypto/sha224.cpp index d55802c..83f1a6a 100644 --- a/src/crypto/sha224.cpp +++ b/src/crypto/sha224.cpp @@ -4,7 +4,8 @@ #include #include #include - +#include "_digest_common.hpp" + namespace fc { sha224::sha224() { memset( _hash, 0, sizeof(_hash) ); } @@ -52,11 +53,7 @@ namespace fc { sha224 operator << ( const sha224& h1, uint32_t i ) { sha224 result; - uint8_t* r = (uint8_t*)&result;//result._hash; - uint8_t* s = (uint8_t*)&h1;//h1._hash; - for( uint32_t p = 0; p < sizeof(sha224)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[sizeof(sha224)-1] = s[sizeof(sha224)-1] << i; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); return result; } sha224 operator ^ ( const sha224& h1, const sha224& h2 ) { diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index ae1d6af..1af5822 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -5,7 +5,8 @@ #include #include #include - +#include "_digest_common.hpp" + namespace fc { sha256::sha256() { memset( _hash, 0, sizeof(_hash) ); } @@ -64,11 +65,7 @@ namespace fc { sha256 operator << ( const sha256& h1, uint32_t i ) { sha256 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[31] = s[31] << i; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); return result; } sha256 operator ^ ( const sha256& h1, const sha256& h2 ) { diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp index d177fe3..0baa03e 100644 --- a/src/crypto/sha512.cpp +++ b/src/crypto/sha512.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "_digest_common.hpp" namespace fc { @@ -52,11 +53,7 @@ namespace fc { sha512 operator << ( const sha512& h1, uint32_t i ) { sha512 result; - uint8_t* r = (uint8_t*)result._hash; - uint8_t* s = (uint8_t*)h1._hash; - for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p ) - r[p] = s[p] << i | (s[p+1]>>(8-i)); - r[63] = s[63] << i; + fc::detail::shift_l( h1.data(), result.data(), result.data_size(), i ); return result; } sha512 operator ^ ( const sha512& h1, const sha512& h2 ) { diff --git a/tests/crypto/dh_test.cpp b/tests/crypto/dh_test.cpp new file mode 100644 index 0000000..e972b78 --- /dev/null +++ b/tests/crypto/dh_test.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(dh_test) +{ + fc::diffie_hellman alice; + BOOST_CHECK( alice.generate_params( 128, 5 ) ); + BOOST_CHECK( alice.generate_pub_key() ); + + fc::diffie_hellman bob; + bob.p = alice.p; + BOOST_CHECK( bob.validate() ); + BOOST_CHECK( bob.generate_pub_key() ); + + fc::diffie_hellman charlie; + BOOST_CHECK( !charlie.validate() ); + BOOST_CHECK( !charlie.generate_pub_key() ); + charlie.p = alice.p; + BOOST_CHECK( charlie.validate() ); + BOOST_CHECK( charlie.generate_pub_key() ); + + BOOST_CHECK( alice.compute_shared_key( bob.pub_key ) ); + BOOST_CHECK( bob.compute_shared_key( alice.pub_key ) ); + BOOST_CHECK_EQUAL( alice.shared_key.size(), bob.shared_key.size() ); + BOOST_CHECK( !memcmp( alice.shared_key.data(), bob.shared_key.data(), alice.shared_key.size() ) ); + std::vector alice_bob = alice.shared_key; + + BOOST_CHECK( alice.compute_shared_key( charlie.pub_key ) ); + BOOST_CHECK( charlie.compute_shared_key( alice.pub_key ) ); + BOOST_CHECK_EQUAL( alice.shared_key.size(), charlie.shared_key.size() ); + BOOST_CHECK( !memcmp( alice.shared_key.data(), charlie.shared_key.data(), alice.shared_key.size() ) ); + std::vector alice_charlie = alice.shared_key; + + BOOST_CHECK( charlie.compute_shared_key( bob.pub_key ) ); + BOOST_CHECK( bob.compute_shared_key( charlie.pub_key ) ); + BOOST_CHECK_EQUAL( charlie.shared_key.size(), bob.shared_key.size() ); + BOOST_CHECK( !memcmp( charlie.shared_key.data(), bob.shared_key.data(), bob.shared_key.size() ) ); + std::vector bob_charlie = charlie.shared_key; + + BOOST_CHECK_EQUAL( alice_bob.size(), alice_charlie.size() ); + BOOST_CHECK( memcmp( alice_bob.data(), alice_charlie.data(), alice_bob.size() ) ); + + BOOST_CHECK_EQUAL( alice_bob.size(), bob_charlie.size() ); + BOOST_CHECK( memcmp( alice_bob.data(), bob_charlie.data(), alice_bob.size() ) ); + + BOOST_CHECK_EQUAL( alice_charlie.size(), bob_charlie.size() ); + BOOST_CHECK( memcmp( alice_charlie.data(), bob_charlie.data(), alice_charlie.size() ) ); + + alice.p.clear(); alice.p.push_back(100); alice.p.push_back(2); + BOOST_CHECK( !alice.validate() ); + alice.p = bob.p; + alice.g = 9; + BOOST_CHECK( !alice.validate() ); + +// It ain't easy... +// alice.g = 2; +// BOOST_CHECK( alice.validate() ); +// BOOST_CHECK( alice.generate_pub_key() ); +// BOOST_CHECK( alice.compute_shared_key( bob.pub_key ) ); +// BOOST_CHECK( bob.compute_shared_key( alice.pub_key ) ); +// BOOST_CHECK_EQUAL( alice.shared_key.size(), bob.shared_key.size() ); +// BOOST_CHECK( memcmp( alice.shared_key.data(), bob.shared_key.data(), alice.shared_key.size() ) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/rand_test.cpp b/tests/crypto/rand_test.cpp new file mode 100644 index 0000000..22d3ab8 --- /dev/null +++ b/tests/crypto/rand_test.cpp @@ -0,0 +1,41 @@ +#include + +#include + +static void check_randomness( const char* buffer, size_t len ) { + if (len == 0) { return; } + // count bit runs and 0's / 1's + unsigned int zc = 0, oc = 0, rc = 0, last = 2; + for (size_t k = len; k; k--) { + char c = *buffer++; + for (int i = 0; i < 8; i++) { + unsigned int bit = c & 1; + c >>= 1; + if (bit) { oc++; } else { zc++; } + if (bit != last) { rc++; last = bit; } + } + } + BOOST_CHECK_EQUAL( 8*len, zc + oc ); + double E = 1 + (zc + oc) / 2.0; + double variance = (E - 1) * (E - 2) / (oc + zc - 1); + double sigma = sqrt(variance); + BOOST_CHECK( rc > E - sigma && rc < E + sigma); +} + +BOOST_AUTO_TEST_SUITE(fc_crypto) + +BOOST_AUTO_TEST_CASE(rand_test) +{ + char buffer[128]; + fc::rand_bytes( buffer, sizeof(buffer) ); + check_randomness( buffer, sizeof(buffer) ); +} + +BOOST_AUTO_TEST_CASE(pseudo_rand_test) +{ + char buffer[10013]; + fc::rand_pseudo_bytes( buffer, sizeof(buffer) ); + check_randomness( buffer, sizeof(buffer) ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/crypto/sha_tests.cpp b/tests/crypto/sha_tests.cpp index 005f659..850c2f8 100644 --- a/tests/crypto/sha_tests.cpp +++ b/tests/crypto/sha_tests.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -14,6 +16,7 @@ static const std::string TEST2(""); static const std::string TEST3("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); static const std::string TEST4("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); static char TEST5[1000001]; +static const std::string TEST6("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"); static void init_5() { memset( TEST5, 'a', sizeof(TEST5) - 1 ); @@ -30,16 +33,81 @@ void test( const char* to_hash, const std::string& expected ) { template void test( const std::string& to_hash, const std::string& expected ) { + H hash = H::hash( to_hash ); + BOOST_CHECK_EQUAL( expected, (std::string) hash ); test( to_hash.c_str(), expected ); } -template void test( const std::string& test, const std::string& expected ); -template void test( const std::string& test, const std::string& expected ); -template void test( const std::string& test, const std::string& expected ); -template void test( const std::string& test, const std::string& expected ); +template +void test_big( const std::string& expected ) { + typename H::encoder enc; + for (char c : TEST6) { enc.put(c); } + for (int i = 0; i < 16777215; i++) { + enc.write( TEST6.c_str(), TEST6.size() ); + } + H hash = enc.result(); + BOOST_CHECK_EQUAL( expected, (std::string) hash ); + + enc.reset(); + enc.write( TEST1.c_str(), TEST1.size() ); + hash = enc.result(); + BOOST_CHECK( hash >= H::hash( TEST1 ) ); + test( TEST1, (std::string) hash ); + + hash = hash ^ hash; + hash.data()[hash.data_size() - 1] = 1; + for (int i = hash.data_size() * 8 - 1; i > 0; i--) { + H other = hash << i; + BOOST_CHECK( other != hash ); + BOOST_CHECK( other > hash ); + BOOST_CHECK( hash < other ); + } + + H hash2( expected ); + fc::variant v; + to_variant( hash2, v ); + from_variant( v, hash ); + BOOST_CHECK( hash == hash2 ); + + H hash3( expected.substr(15) + "000000000000000" ); + BOOST_CHECK( hash3 == hash2 << 60 ); +} + +template +void test_stream( ) { + H hash( TEST1 ); + std::stringstream stream; + stream << hash; + + H other; + stream >> other; + BOOST_CHECK( hash == other ); +} + +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); +template void test_big( const std::string& expected ); + +template void test_stream(); +template void test_stream(); +template void test_stream(); BOOST_AUTO_TEST_SUITE(fc_crypto) +BOOST_AUTO_TEST_CASE(ripemd160_test) +{ + init_5(); + test( TEST1, "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" ); + test( TEST2, "9c1185a5c5e9fc54612808977ee8f548b2258d31" ); + test( TEST3, "12a053384a9c0c88e405a06c27dcf49ada62eb2b" ); +// test( TEST4, "" ); + test( TEST5, "52783243c1697bdbe16d37f97f68f08325dc1528" ); + test_big( "29b6df855772aa9a95442bf83b282b495f9f6541" ); + test_stream(); +} + BOOST_AUTO_TEST_CASE(sha1_test) { init_5(); @@ -48,6 +116,7 @@ BOOST_AUTO_TEST_CASE(sha1_test) test( TEST3, "84983e441c3bd26ebaae4aa1f95129e5e54670f1" ); test( TEST4, "a49b2446a02c645bf419f995b67091253a04a259" ); test( TEST5, "34aa973cd4c4daa4f61eeb2bdbad27316534016f" ); + test_big( "7789f0c9ef7bfc40d93311143dfbe69e2017f592" ); } BOOST_AUTO_TEST_CASE(sha224_test) @@ -58,6 +127,7 @@ BOOST_AUTO_TEST_CASE(sha224_test) test( TEST3, "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525" ); test( TEST4, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3" ); test( TEST5, "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67" ); + test_big( "b5989713ca4fe47a009f8621980b34e6d63ed3063b2a0a2c867d8a85" ); } BOOST_AUTO_TEST_CASE(sha256_test) @@ -68,6 +138,23 @@ BOOST_AUTO_TEST_CASE(sha256_test) test( TEST3, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" ); test( TEST4, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" ); test( TEST5, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" ); + test_big( "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e" ); + test_stream(); + + std::vector test_object; + test_object.push_back( 42 ); + fc::sha256 digest = fc::digest( test_object ); + BOOST_CHECK( digest == fc::sha256::hash( test_object ) ); + fc::sha256 other( digest.data(), digest.data_size() ); + BOOST_CHECK( digest == other ); + fc::sha512 yet_another = fc::sha512::hash( TEST1 ); + try { + fc::sha256 fourth( yet_another.data(), yet_another.data_size() ); + BOOST_FAIL( "Expected exception!" ); + } catch ( fc::exception& expected ) {} + + fc::sha256 fourth( "445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094" ); + BOOST_CHECK_EQUAL( "d61967f63c7dd183914a4ae452c9f6ad5d462ce3d277798075b107615c1a8a30", (std::string) fc::sha256::hash(fourth) ); } BOOST_AUTO_TEST_CASE(sha512_test) @@ -83,6 +170,9 @@ BOOST_AUTO_TEST_CASE(sha512_test) "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909" ); test( TEST5, "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b" ); + test_big( "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d" + "77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086" ); + test_stream(); } BOOST_AUTO_TEST_SUITE_END()