Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement BN_num_bytes() (and BN_num_bits() ) in C#?

I'm porting this line from C++ to C#, and I'm not an experienced C++ programmer:

 unsigned int nSize = BN_num_bytes(this); 

In .NET I'm using System.Numerics.BigInteger

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

I think there is a fundamental difference in how they operate internally, since the sources' unit tests' results don't match if the BigInt equals:

  • 0
  • Any negative number
  • 0x00123456

I know literally nothing about BN_num_bytes (edit: the comments just told me that it's a macro for BN_num_bits).

Question

Would you verify these guesses about the code:

  • I need to port BN_num_bytes which is a macro for ((BN_num_bits(bn)+7)/8) (Thank you @WhozCraig)

  • I need to port BN_num_bits which is floor(log2(w))+1

Then, if the possibility exists that leading and trailing bytes aren't counted, then what happens on Big/Little endian machines? Does it matter?

Based on these answers on Security.StackExchange, and that my application isn't performance critical, I may use the default implementation in .NET and not use an alternate library that may already implement a comparable workaround.


Edit: so far my implementation looks something like this, but I'm not sure what the "LookupTable" is as mentioned in the comments.

   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;
    }

Edit 2:

After some more searching, I found that:

BN_num_bits does not return the number of significant bits of a given bignum, but rather the position of the most significant 1 bit, which is not necessarily the same thing

Though I still don't know what the source of it looks like...

like image 250
makerofthings7 Avatar asked Mar 05 '13 00:03

makerofthings7


1 Answers

The man page (OpenSSL project) of BN_num_bits says that "Basically, except for a zero, it returns floor(log2(w))+1.". So these are the correct implementations of the BN_num_bytes and BN_num_bits functions for .Net's 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));
}

You should probably change these into extension methods for convenience.

You should understand that these functions measure the minimum number of bits/bytes that are needed to express a given integer number. Variables declared as int (System.Int32) take 4 bytes of memory, but you only need 1 byte (or 3 bits) to express the integer number 7. This is what BN_num_bytes and BN_num_bits calculate - the minimum required storage size for a concrete number.

You can find the source code of the original implementations of the functions in the official OpenSSL repository.

like image 144
Ark-kun Avatar answered Sep 28 '22 18:09

Ark-kun