Как реализовать BN_num_bytes() (и BN_num_bits()) в С#?

Я переносит эту строку с С++ на С#, и я не являюсь опытным программистом на С++:

 unsigned int nSize = BN_num_bytes(this); 

В .NET я используя System.Numerics.BigInteger

 BigInteger num = originalBigNumber;
 byte[] numAsBytes = num.ToByteArray();
 uint compactBitsRepresentation = 0;
 uint size2 = (uint)numAsBytes.Length;

Я думаю, что существует фундаментальное различие в том, как они работают внутри страны, поскольку результаты тестирования модулей источника не соответствуют, если BigInt равно:

  • 0
  • Любое отрицательное число
  • 0x00123456

Я ничего не знаю о BN_num_bytes (редактирование: комментарии просто сказали мне, что это макрос для BN_num_bits).

Вопрос

Вы проверили бы эти предположения о коде:

  • Мне нужно порт BN_num_bytes, который является макросом для ((BN_num_bits(bn)+7)/8) (Спасибо @WhozCraig)

  • Мне нужно порт BN_num_bits, который floor(log2(w))+1

Тогда, если существует вероятность того, что ведущий и конечный байты не учитываются, то что происходит на машинах Big/Little endian? Это имеет значение?

На основе этих ответов на Security.StackExchange, и что мое приложение не критично для производительности, я могу использовать реализацию по умолчанию в .NET и не использовать альтернативный библиотека, которая может уже реализовать сопоставимые методы обхода.


Изменить: пока моя реализация выглядит примерно так, но я не уверен, что "LookupTable" указан в комментариях.

   private static int BN_num_bytes(byte[] numAsBytes)
    {
        int bits = BN_num_bits(numAsBytes);
        return (bits + 7) / 8; 
    }

    private static int BN_num_bits(byte[] numAsBytes)
    {
        var log2 = Math.Log(numAsBytes.Length, 2);
        var floor = Math.Floor(log2);
        return (uint)floor + 1;
    }

Изменить 2:

После очередного поиска я обнаружил, что:

BN_num_bits не возвращает число значимых бит заданного бигнама, а скорее положение наиболее значимого 1 бит, что не обязательно одно и то же.

Хотя я до сих пор не знаю, как выглядит источник этого...

Ответ 1

man page (проект OpenSSL) BN_num_bits говорит, что "В принципе, за исключением нуля, он возвращает floor(log2(w))+1.". Таким образом, это правильные реализации функций BN_num_bytes и BN_num_bits для .Net BigInteger.

public static int BN_num_bytes(BigInteger number) {
    if (number == 0) {
        return 0;
    }
    return 1 + (int)Math.Floor(BigInteger.Log(BigInteger.Abs(number), 2)) / 8;
}

public static int BN_num_bits(BigInteger number) {
    if (number == 0) {
        return 0;
    }
    return 1 + (int)Math.Floor(BigInteger.Log(BigInteger.Abs(number), 2));
}

Вы должны, вероятно, изменить их на методы расширения для удобства.

Вы должны понимать, что эти функции измеряют минимальное количество бит/байтов, которые необходимы для выражения заданного целого числа. Переменные, объявленные как int (System.Int32), занимают 4 байта памяти, но вам нужно только 1 байт (или 3 бита), чтобы выразить целочисленное число 7. Это то, что BN_num_bytes и BN_num_bits вычисляют - минимально необходимый размер хранилища для конкретное число.

Вы можете найти исходный код исходных реализаций функций в официальном репозитории OpenSSL .

Ответ 2

Объедините то, что WhozCraig в комментариях говорит с этой ссылкой, объясняя BN_num_bits:

http://www.openssl.org/docs/crypto/BN_num_bytes.html

И вы получите что-то вроде этого, которое должно сказать вам значительное количество байтов:

public static int NumberOfBytes(BigInteger bigInt)
{
    if (bigInt == 0)
    {
        return 0; //you need to check what BN_num_bits actually does here as not clear from docs, probably returns 0
    }

    return (int)Math.Ceiling(BigInteger.Log(bigInt + 1, 2) + 7) / 8;
}