mirror of https://github.com/aria2/aria2
Internal implementation of DHKeyExchange
Reusing a bignum (well, unsigned very-long) implementation I had lying around for years and just cleaned up a bit and brought to C++11 land. It might not be the most performant implementation, but it shoud be fast enough for our purposes and will go a long way of removing gcrypt, nettle, gmp, openssl dependencies when using AppleTLS and WinTLS (upcoming).pull/131/head
parent
213de910fd
commit
e854463b2d
21
configure.ac
21
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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2013 Nils Maier
|
||||
*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
|
||||
#include "InternalDHKeyExchange.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#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<const char*>(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<const char*>(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<char*>(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<unsigned long>(keyLength_),
|
||||
static_cast<unsigned long>(outLength)));
|
||||
}
|
||||
publicKey_.binary(reinterpret_cast<char*>(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<unsigned long>(keyLength_),
|
||||
static_cast<unsigned long>(outLength)));
|
||||
}
|
||||
if (prime_.length() < peerPublicKeyLength) {
|
||||
throw DL_ABORT_EX(fmt("peer public key overflows bignum. max:%lu, actual:%lu",
|
||||
static_cast<unsigned long>(prime_.length()),
|
||||
static_cast<unsigned long>(peerPublicKeyLength)));
|
||||
}
|
||||
|
||||
n peerKey(reinterpret_cast<const char*>(peerPublicKeyData), peerPublicKeyLength);
|
||||
n secret = peerKey.mul_mod(privateKey_, prime_);
|
||||
secret.binary(reinterpret_cast<char*>(out), outLength);
|
||||
|
||||
return outLength;
|
||||
}
|
||||
|
||||
} // namespace aria2
|
|
@ -0,0 +1,73 @@
|
|||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2013 Nils Maier
|
||||
*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
|
||||
#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
|
|
@ -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
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace bignum {
|
||||
|
||||
template<size_t dim>
|
||||
class ulong {
|
||||
|
||||
public:
|
||||
typedef char char_t;
|
||||
typedef std::make_unsigned<char_t>::type uchar_t;
|
||||
|
||||
private:
|
||||
std::unique_ptr<char_t[]> 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<dim>& 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<dim>& operator=(const ulong<dim>& rhs) {
|
||||
memcpy(buf_.get(), rhs.buf_.get(), dim);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ulong<dim>& rhs) const {
|
||||
return memcmp(buf_.get(), rhs.buf_.get(), dim) == 0;
|
||||
}
|
||||
bool operator!=(const ulong<dim>& rhs) const {
|
||||
return memcmp(buf_.get(), rhs.buf_.get(), dim) != 0;
|
||||
}
|
||||
bool operator>(const ulong<dim>& 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<dim>& rhs) const {
|
||||
return *this == rhs || *this > rhs;
|
||||
}
|
||||
bool operator<(const ulong<dim>& rhs) const {
|
||||
return !(*this >= rhs);
|
||||
}
|
||||
bool operator<=(const ulong<dim>& rhs) const {
|
||||
return *this == rhs || *this < rhs;
|
||||
}
|
||||
|
||||
ulong<dim> operator+(const ulong<dim>& rhs) const {
|
||||
ulong<dim> 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<dim>& operator+=(const ulong<dim>& rhs) {
|
||||
*this = *this + rhs;
|
||||
return *this;
|
||||
}
|
||||
ulong<dim>& operator++() {
|
||||
*this = *this + 1;
|
||||
return *this;
|
||||
}
|
||||
ulong<dim> operator++(int) {
|
||||
ulong<dim> tmp = *this;
|
||||
*this = *this + 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ulong<dim> operator-(const ulong<dim>& rhs) const {
|
||||
ulong<dim> 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<dim>& operator-=(const ulong<dim>& rhs) {
|
||||
*this = *this - rhs;
|
||||
return *this;
|
||||
}
|
||||
ulong<dim>& operator--() {
|
||||
*this = *this - 1;
|
||||
return *this;
|
||||
}
|
||||
ulong<dim> operator--(int) {
|
||||
ulong<dim> tmp = *this;
|
||||
*this = *this - 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ulong<dim> operator*(const ulong<dim>& rhs) const {
|
||||
ulong<dim> c = rhs, rv;
|
||||
const ulong<dim> null;
|
||||
size_t cap = c.capacity();
|
||||
while (c != null) {
|
||||
ulong<dim> tmp = *this;
|
||||
tmp.mul(cap - 1);
|
||||
rv += tmp;
|
||||
|
||||
ulong<dim> diff(1);
|
||||
diff.mul(cap - 1);
|
||||
c -= diff;
|
||||
|
||||
cap = c.capacity();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
ulong<dim>& operator*=(const ulong<dim>& rhs) {
|
||||
*this = *this * rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ulong<dim> operator/(const ulong<dim>& rhs) const {
|
||||
ulong<dim> quotient, remainder;
|
||||
div(rhs, quotient, remainder);
|
||||
return quotient;
|
||||
}
|
||||
ulong<dim>& operator/=(const ulong<dim>& rhs) {
|
||||
*this = *this / rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ulong<dim> operator%(const ulong<dim>& rhs) const {
|
||||
ulong<dim> quotient, remainder;
|
||||
div(rhs, quotient, remainder);
|
||||
return remainder;
|
||||
}
|
||||
ulong<dim>& operator%=(const ulong<dim>& rhs) {
|
||||
*this = *this % rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ulong<dim> mul_mod(const ulong<dim>& mul, const ulong<dim>& mod) const {
|
||||
if (capacity() + mul.capacity() <= dim) {
|
||||
return (*this * mul) % mod;
|
||||
}
|
||||
ulong<dim*2> et(buf_.get(), dim),
|
||||
emul(mul.buf_.get(), dim),
|
||||
emod(mod.buf_.get(), dim),
|
||||
erv = (et * emul) % emod;
|
||||
ulong<dim> rv;
|
||||
erv.binary(rv.buf_.get(), dim);
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::unique_ptr<char_t[]> binary() const {
|
||||
ulong<dim> c = *this;
|
||||
std::unique_ptr<char_t[]> 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<dim> 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<dim>& d, ulong<dim>& q, ulong<dim>& r) const {
|
||||
ulong<dim> 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<dim> qt(1);
|
||||
qt.mul(cr - cd - 1);
|
||||
ulong<dim> 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
|
Loading…
Reference in New Issue