使用OpenSSL 1.1.0可以更简单地减少bignum,从而使结果为模,适合无符号long
我想做的事情如下:
char p_str[] = "489133282872437279"; // A big prime
BIGNUM *p = BN_new();
BN_dec2bn(&p, p_str); // Prime converted into bignum
BIGNUM *a = BN_new();
BN_rand_range(a, p); // Find random number from 0 to prime
// Define max modulo for data type. Note the +1! wont work
BN_ULONG m = std::numeric_limits<unsigned long>::max() + 1;
// Reduce random number in field Z_p such that it fits into unsigned long
unsigned long result = BN_mod_word(a, m);
std::cout << result << std::endl;
其他方法是定义另一个BIGNUM
char m_str[] = "4294967296"; // Note that this is now 2**32, which we want
BIGNUM *m_2 = BN_new();
BN_dec2bn(&m_2, m_str);
然后按以下方式进行计算:
BN_CTX *ctx = BN_CTX_new();
BIGNUM *remainder = BN_new();
BN_nnmod(remainder, a, m_2, ctx);
这现在需要将现在产生的BN剩余量转换回数据类型。
我想知道是否有更简单的方法来进行这种减少,以使其余部分适合数据类型。
答案 0 :(得分:0)
我要解决的方法是将BIGNUM转换为二进制字节,然后将那些二进制字节转换为无符号整数类型,并对任何多余的不必要字节取模。
BN_bn2bin()的文档说,返回的字节为big-endian格式。
这是我的解决方案(以及用于BIGNUM的最小c ++包装器):
#include <openssl/bn.h>
#include <limits>
#include <limits>
#include <stdexcept>
#include <utility>
#include <iostream>
#include <memory>
#include <vector>
#include <cstdint>
struct bn_failure : std::runtime_error
{
using runtime_error::runtime_error;
};
struct BigNum
{
BigNum()
: bn_(BN_new())
{
if (!bn_) throw bn_failure("BN_new()");
}
BigNum(const char* str)
: BigNum()
{
if (!bn_) throw bn_failure("BN_new()");
auto len = BN_dec2bn(&bn_, str); // Prime converted into bignum
if (len == 0) throw bn_failure("BN_dec2bn()");
}
BigNum(BigNum&& other) noexcept
: bn_(other.bn_)
{
other.bn_ = nullptr;
}
BigNum& operator=(BigNum&& other) noexcept
{
auto tmp = BigNum(std::move(other));
swap(tmp);
return *this;
}
BigNum(BigNum const& other)
: bn_(BN_dup(other.bn_))
{
if (!bn_) throw bn_failure("BN_dup()");
}
BigNum& operator=(BigNum const& other)
{
if(!BN_copy(bn_, other.bn_))
throw bn_failure("BN_copy()");
return *this;
}
~BigNum() noexcept
{
if (bn_)
{
BN_free(bn_);
}
}
void swap(BigNum& other) noexcept
{
std::swap(bn_, other.bn_);
}
void randRange(const BigNum& other)
{
BN_rand_range(bn_, other.bn_);
}
friend std::ostream& operator<<(std::ostream& os, BigNum const& bn)
{
return os << bn.as_text().get();
}
struct string_free {
void operator()(char *str) const noexcept {
OPENSSL_free(str);
}
};
auto as_text() const -> std::unique_ptr<char, string_free>
{
auto p = BN_bn2dec(bn_);
return { p, string_free() };
}
auto as_bytes() const -> std::vector<std::uint8_t>
{
auto bytes = BN_num_bytes(bn_);
auto result = std::vector<std::uint8_t>(bytes);
BN_bn2bin(bn_, result.data());
return result;
}
BIGNUM* bn_;
};
template<class Type>
auto be_bytes_to_unsigned(std::vector<std::uint8_t> const& vec) -> Type
{
auto result = Type(0);
auto first = rbegin(vec);
auto last = rend(vec);
auto count = std::size_t(std::distance(first, last));
count = std::min(sizeof(Type), count);
last = std::next(first , count);
int i = 0;
while (first != last)
{
auto b = *first++;
auto v = Type(b);
v <<= (i * std::numeric_limits<std::uint8_t>::digits);
++i;
result |= v;
}
return result;
}
int main()
{
auto p = BigNum("489133282872437279");
auto b = BigNum();
b.randRange(p);
std::cout << p << std::endl << b << std::endl;
auto modval = be_bytes_to_unsigned<unsigned long>(b.as_bytes());
std::cout << modval << std::endl;
}
样本输出(64位长整数):
489133282872437279
281503461139622433
281503461139622433