OpenSSL轻松计算最大数据类型大小的模数

时间:2018-07-17 08:33:21

标签: c++ openssl

使用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剩余量转换回数据类型。

我想知道是否有更简单的方法来进行这种减少,以使其余部分适合数据类型。

1 个答案:

答案 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