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)
|
AM_CONDITIONAL([ENABLE_MESSAGE_DIGEST], false)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x$have_libnettle" = "xyes" && test "x$have_libgmp" = "xyes" ||
|
if test "x$have_libgmp" = "xyes" ||
|
||||||
test "x$have_libgcrypt" = "xyes" || test "x$have_openssl" = "xyes"; then
|
test "x$have_libgcrypt" = "xyes" ||
|
||||||
enable_bignum=yes
|
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
|
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" &&
|
if test "x$enable_bittorrent" = "xyes" &&
|
||||||
test "x$enable_message_digest" = "xyes" &&
|
test "x$have_rc4" = "xyes" &&
|
||||||
test "x$enable_bignum" = "xyes"; then
|
test "x$enable_message_digest" = "xyes"; then
|
||||||
AC_DEFINE([ENABLE_BITTORRENT], [1],
|
AC_DEFINE([ENABLE_BITTORRENT], [1],
|
||||||
[Define to 1 if BitTorrent support is enabled.])
|
[Define to 1 if BitTorrent support is enabled.])
|
||||||
AM_CONDITIONAL([ENABLE_BITTORRENT], true)
|
AM_CONDITIONAL([ENABLE_BITTORRENT], true)
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
#define D_DH_KEY_EXCHANGE_H
|
#define D_DH_KEY_EXCHANGE_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#ifdef HAVE_LIBGMP
|
#ifdef USE_INTERNAL_BIGNUM
|
||||||
|
# include "InternalDHKeyExchange.h"
|
||||||
|
#elif HAVE_LIBGMP
|
||||||
# include "LibgmpDHKeyExchange.h"
|
# include "LibgmpDHKeyExchange.h"
|
||||||
#elif HAVE_LIBGCRYPT
|
#elif HAVE_LIBGCRYPT
|
||||||
# include "LibgcryptDHKeyExchange.h"
|
# 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
|
SRCS += WinMessageDigestImpl.cc
|
||||||
endif # USE_WINDOWS_MD
|
endif # USE_WINDOWS_MD
|
||||||
|
|
||||||
|
if USE_INTERNAL_BIGNUM
|
||||||
|
SRCS += InternalDHKeyExchange.cc InternalDHKeyExchange.h bignum.h
|
||||||
|
endif
|
||||||
|
|
||||||
if USE_INTERNAL_MD
|
if USE_INTERNAL_MD
|
||||||
SRCS += InternalMessageDigestImpl.cc sha1.c md5.c
|
SRCS += InternalMessageDigestImpl.cc sha1.c md5.c
|
||||||
endif # USE_WINDOWS_MD
|
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