diff --git a/configure.ac b/configure.ac index ed8df1bf..c61e8be1 100644 --- a/configure.ac +++ b/configure.ac @@ -506,14 +506,25 @@ else AM_CONDITIONAL([ENABLE_MESSAGE_DIGEST], false) fi -if test "x$have_libnettle" = "xyes" && test "x$have_libgmp" = "xyes" || - test "x$have_libgcrypt" = "xyes" || test "x$have_openssl" = "xyes"; then - enable_bignum=yes +if test "x$have_libgmp" = "xyes" || + test "x$have_libgcrypt" = "xyes" || + test "x$have_openssl" = "xyes"; then + AM_CONDITIONAL([USE_INTERNAL_BIGNUM], false) +else + AC_DEFINE([USE_INTERNAL_BIGNUM], [1], [Define to 1 if internal BIGNUM support is enabled.]) + AM_CONDITIONAL([USE_INTERNAL_BIGNUM], true) fi +if test "x$have_libnettle" = "xyes" || + test "x$have_libgcrypt" = "xyes" || + test "x$have_openssl" = "xyes"; then + have_rc4=yes +fi + + if test "x$enable_bittorrent" = "xyes" && - test "x$enable_message_digest" = "xyes" && - test "x$enable_bignum" = "xyes"; then + test "x$have_rc4" = "xyes" && + test "x$enable_message_digest" = "xyes"; then AC_DEFINE([ENABLE_BITTORRENT], [1], [Define to 1 if BitTorrent support is enabled.]) AM_CONDITIONAL([ENABLE_BITTORRENT], true) diff --git a/src/DHKeyExchange.h b/src/DHKeyExchange.h index 1fd7b86f..8c52d262 100644 --- a/src/DHKeyExchange.h +++ b/src/DHKeyExchange.h @@ -36,7 +36,9 @@ #define D_DH_KEY_EXCHANGE_H #include "common.h" -#ifdef HAVE_LIBGMP +#ifdef USE_INTERNAL_BIGNUM +# include "InternalDHKeyExchange.h" +#elif HAVE_LIBGMP # include "LibgmpDHKeyExchange.h" #elif HAVE_LIBGCRYPT # include "LibgcryptDHKeyExchange.h" diff --git a/src/InternalDHKeyExchange.cc b/src/InternalDHKeyExchange.cc new file mode 100644 index 00000000..bbf0e4fe --- /dev/null +++ b/src/InternalDHKeyExchange.cc @@ -0,0 +1,121 @@ +/* */ + +#include "InternalDHKeyExchange.h" + +#include + +#include "DlAbortEx.h" +#include "LogFactory.h" +#include "fmt.h" +#include "util.h" + +namespace aria2 { + +void DHKeyExchange::init(const unsigned char* prime, size_t primeBits, + const unsigned char* generator, size_t privateKeyBits) +{ + std::string pr = reinterpret_cast(prime); + if (pr.length() % 2) { + pr = "0" + pr; + } + pr = util::fromHex(pr.begin(), pr.end()); + if (pr.empty()) { + throw DL_ABORT_EX("No valid prime supplied"); + } + prime_ = n(pr.c_str(), pr.length()); + + std::string gen = reinterpret_cast(generator); + if (gen.length() % 2) { + gen = "0" + gen; + } + gen = util::fromHex(gen.begin(), gen.end()); + if (gen.empty()) { + throw DL_ABORT_EX("No valid generator supplied"); + } + generator_ = n(gen.c_str(), gen.length()); + + size_t pbytes = (privateKeyBits + 7) / 8; + unsigned char buf[pbytes]; + util::generateRandomData(buf, pbytes); + privateKey_ = n(reinterpret_cast(buf), pbytes); + + keyLength_ = (primeBits + 7) / 8; +} + +void DHKeyExchange::generatePublicKey() +{ + publicKey_ = generator_.mul_mod(privateKey_, prime_); +} + +size_t DHKeyExchange::getPublicKey(unsigned char* out, size_t outLength) const +{ + if (outLength < keyLength_) { + throw DL_ABORT_EX(fmt("Insufficient buffer for public key. expect:%lu, actual:%lu", + static_cast(keyLength_), + static_cast(outLength))); + } + publicKey_.binary(reinterpret_cast(out), outLength); + return keyLength_; +} + +void DHKeyExchange::generateNonce(unsigned char* out, size_t outLength) const +{ + util::generateRandomData(out, outLength); +} + +size_t DHKeyExchange::computeSecret(unsigned char* out, size_t outLength, + const unsigned char* peerPublicKeyData, + size_t peerPublicKeyLength) const +{ + if (outLength < keyLength_) { + throw DL_ABORT_EX(fmt("Insufficient buffer for secret. expect:%lu, actual:%lu", + static_cast(keyLength_), + static_cast(outLength))); + } + if (prime_.length() < peerPublicKeyLength) { + throw DL_ABORT_EX(fmt("peer public key overflows bignum. max:%lu, actual:%lu", + static_cast(prime_.length()), + static_cast(peerPublicKeyLength))); + } + + n peerKey(reinterpret_cast(peerPublicKeyData), peerPublicKeyLength); + n secret = peerKey.mul_mod(privateKey_, prime_); + secret.binary(reinterpret_cast(out), outLength); + + return outLength; +} + +} // namespace aria2 diff --git a/src/InternalDHKeyExchange.h b/src/InternalDHKeyExchange.h new file mode 100644 index 00000000..34d18f76 --- /dev/null +++ b/src/InternalDHKeyExchange.h @@ -0,0 +1,73 @@ +/* */ + +#ifndef D_INTERNAL_DH_KEY_EXCHANGE_H +#define D_INTERNAL_DH_KEY_EXCHANGE_H + +#include "common.h" +#include "bignum.h" + +namespace aria2 { + +class DHKeyExchange { +private: + typedef bignum::ulong<1024> n; // aka max. 8096 bits + size_t keyLength_; + n prime_; + n generator_; + n privateKey_; + n publicKey_; + +public: + DHKeyExchange() : keyLength_(0) {} + ~DHKeyExchange() {} + + void init(const unsigned char* prime, size_t primeBits, + const unsigned char* generator, size_t privateKeyBits); + + void generatePublicKey(); + + size_t getPublicKey(unsigned char* out, size_t outLength) const; + + void generateNonce(unsigned char* out, size_t outLength) const; + + size_t computeSecret(unsigned char* out, size_t outLength, + const unsigned char* peerPublicKeyData, + size_t peerPublicKeyLength) const; +}; + +} // namespace aria2 + +#endif // D_INTERNAL_DH_KEY_EXCHANGE_H diff --git a/src/Makefile.am b/src/Makefile.am index a6efa20f..f4466018 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -333,6 +333,10 @@ if USE_WINDOWS_MD SRCS += WinMessageDigestImpl.cc endif # USE_WINDOWS_MD +if USE_INTERNAL_BIGNUM +SRCS += InternalDHKeyExchange.cc InternalDHKeyExchange.h bignum.h +endif + if USE_INTERNAL_MD SRCS += InternalMessageDigestImpl.cc sha1.c md5.c endif # USE_WINDOWS_MD diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 00000000..58e4d03b --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,311 @@ +/*** + * This software was written by Nils Maier. No copyright is claimed, and the + * software is hereby placed in the public domain. + * + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void in your jurisdiction, then the + * software is Copyright 2004,2013 Nils Maier and it is hereby released to the + * general public under the following terms: + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * There's ABSOLUTELY NO WARRANTY, express or implied. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +#include +#include +#include + +namespace bignum { + + template + class ulong { + + public: + typedef char char_t; + typedef std::make_unsigned::type uchar_t; + + private: + std::unique_ptr buf_; + + public: + inline ulong() + : buf_(new char_t[dim]()) + { + } + inline ulong(size_t t) + : buf_(new char_t[dim]()) + { + memcpy(buf_.get(), (char_t*)&t, sizeof(t)); + } + inline ulong(const ulong& rhs) + : buf_(new char_t[dim]()) + { + memcpy(buf_.get(), rhs.buf_.get(), dim); + } + explicit inline ulong(const char_t *data, size_t size) + : buf_(new char_t[dim]()) + { + if (size > dim) { + throw std::bad_alloc(); + } + memcpy(buf_.get(), data, size); + } + + virtual ~ulong() {} + + ulong& operator=(const ulong& rhs) { + memcpy(buf_.get(), rhs.buf_.get(), dim); + return *this; + } + + bool operator==(const ulong& rhs) const { + return memcmp(buf_.get(), rhs.buf_.get(), dim) == 0; + } + bool operator!=(const ulong& rhs) const { + return memcmp(buf_.get(), rhs.buf_.get(), dim) != 0; + } + bool operator>(const ulong& rhs) const { + const auto b1 = buf_.get(); + const auto b2 = rhs.buf_.get(); + for (ssize_t i = dim - 1; i >= 0; --i) { + for (ssize_t j = 1; j >= 0; --j) { + char_t t = ((uchar_t)(b1[i] << 4 * (1 - j))) >> 4; + char_t r = ((uchar_t)(b2[i] << 4 * (1 - j))) >> 4; + if (t != r) { + return t > r; + } + } + } + return false; + } + bool operator>=(const ulong& rhs) const { + return *this == rhs || *this > rhs; + } + bool operator<(const ulong& rhs) const { + return !(*this >= rhs); + } + bool operator<=(const ulong& rhs) const { + return *this == rhs || *this < rhs; + } + + ulong operator+(const ulong& rhs) const { + ulong rv; + const auto b1 = buf_.get(); + const auto b2 = rhs.buf_.get(); + const auto rb = rv.buf_.get(); + bool base = false; + for (size_t i = 0; i < dim; ++i) { + for (ssize_t j = 0; j < 2; ++j) { + char_t t = ((uchar_t)(b1[i] << 4 * (1 - j))) >> 4; + char_t r = ((uchar_t)(b2[i] << 4 * (1 - j))) >> 4; + if (base) { + t++; + } + if (r + t >= 16) { + rb[i] += (t + r - 16) << j * 4; + base = true; + } + else { + rb[i] += (t + r) << j * 4; + base = false; + } + } + } + return rv; + } + ulong& operator+=(const ulong& rhs) { + *this = *this + rhs; + return *this; + } + ulong& operator++() { + *this = *this + 1; + return *this; + } + ulong operator++(int) { + ulong tmp = *this; + *this = *this + 1; + return tmp; + } + + ulong operator-(const ulong& rhs) const { + ulong rv; + const auto b1 = buf_.get(); + const auto b2 = rhs.buf_.get(); + const auto rb = rv.buf_.get(); + bool base = false; + for (size_t i = 0; i < dim; ++i) { + for (ssize_t j = 0; j < 2; ++j) { + char_t t = ((uchar_t)(b1[i] << 4 * (1 - j))) >> 4; + char_t r = ((uchar_t)(b2[i] << 4 * (1 - j))) >> 4; + if (base) { + t--; + } + if (t >= r) { + rb[i] += (t - r) << j * 4; + base = false; + } + else { + rb[i] += (t + 16 - r) << j * 4; + base = true; + } + } + } + return rv; + } + ulong& operator-=(const ulong& rhs) { + *this = *this - rhs; + return *this; + } + ulong& operator--() { + *this = *this - 1; + return *this; + } + ulong operator--(int) { + ulong tmp = *this; + *this = *this - 1; + return tmp; + } + + ulong operator*(const ulong& rhs) const { + ulong c = rhs, rv; + const ulong null; + size_t cap = c.capacity(); + while (c != null) { + ulong tmp = *this; + tmp.mul(cap - 1); + rv += tmp; + + ulong diff(1); + diff.mul(cap - 1); + c -= diff; + + cap = c.capacity(); + } + return rv; + } + ulong& operator*=(const ulong& rhs) { + *this = *this * rhs; + return *this; + } + + ulong operator/(const ulong& rhs) const { + ulong quotient, remainder; + div(rhs, quotient, remainder); + return quotient; + } + ulong& operator/=(const ulong& rhs) { + *this = *this / rhs; + return *this; + } + + ulong operator%(const ulong& rhs) const { + ulong quotient, remainder; + div(rhs, quotient, remainder); + return remainder; + } + ulong& operator%=(const ulong& rhs) { + *this = *this % rhs; + return *this; + } + + ulong mul_mod(const ulong& mul, const ulong& mod) const { + if (capacity() + mul.capacity() <= dim) { + return (*this * mul) % mod; + } + ulong et(buf_.get(), dim), + emul(mul.buf_.get(), dim), + emod(mod.buf_.get(), dim), + erv = (et * emul) % emod; + ulong rv; + erv.binary(rv.buf_.get(), dim); + return rv; + } + + std::unique_ptr binary() const { + ulong c = *this; + std::unique_ptr rv; + rv.swap(c.buf_); + return rv; + } + void binary(char_t *buf, size_t len) const { + memcpy(buf, buf_.get(), std::min(dim, len)); + } + + size_t length() const { + return dim; + } + + private: + size_t capacity() const { + size_t rv = dim * 2; + const auto b = buf_.get(); + for (ssize_t i = dim - 1; i >= 0; --i) { + char_t f = b[i] >> 4; + char_t s = (b[i] << 4) >> 4; + if (!f && !s) { + rv -= 2; + continue; + } + if (!f) { + --rv; + } + return rv; + } + return rv; + } + + void mul(size_t digits) { + ulong tmp = *this; + auto bt = tmp.buf_.get(); + auto b = buf_.get(); + memset(b, 0, dim); + const size_t npar = digits % 2; + const size_t d2 = digits / 2; + for (size_t i = d2; i < dim; ++i) { + for (size_t j = 0; j < 2; ++j) { + char_t c = ((uchar_t)(bt[(dim - 1) - i] << 4 * (1 - j))) >> 4; + char_t r = c << (npar * (1 - j) * 4 + (1 - npar) * j * 4); + ssize_t idx = i - d2 - npar * j; + if (idx >= 0) { + b[(dim - 1) - idx] += r; + } + } + } + } + + void div(const ulong& d, ulong& q, ulong& r) const { + ulong tmp = d; + r = *this; + q = 0; + size_t cr = r.capacity(); + const size_t cd = d.capacity(); + while (cr > cd) { + tmp = d; + tmp.mul(cr - cd - 1); + ulong qt(1); + qt.mul(cr - cd - 1); + ulong t = tmp; + t.mul(1); + if (r >= t) { + tmp = t; + qt.mul(1); + } + while (r >= tmp) { + r -= tmp; + q += qt; + } + cr = r.capacity(); + } + while (r >= d) { + r -= d; + ++q; + } + } + }; + +} // namespace bignum + +#endif