Is there a better way to detect endianness in .NET

2019-09-09 21:59发布

问题:

It would be nice if the .NET framework just gave functions/methods from the BitConverter class that just explicitly returned an array of bytes in the proper requested endianness.

I've done some functions like this in other code, but is there a shorter more direct way? (efficiency is key since this concept is used a TON in various crypto and password derivation contexts, including PBKDF2, Skein, HMAC, BLAKE2, AES and others)

// convert an unsigned int into an array of bytes BIG ENDIEN
// per the spec section 5.2 step 3 for PBKDF2 RFC2898
static internal byte[] IntToBytes(uint i)
{
    byte[] bytes = BitConverter.GetBytes(i);
    if (!BitConverter.IsLittleEndian)
    {
        return bytes;
    }
    else
    {
        Array.Reverse(bytes);
        return bytes;
    }
}

I also see that others struggle with this question, and I haven't seen a good answer yet :( How to deal with 'Endianness'

回答1:

The way I convert between integers and byte[] is by using bitshifts with fixed endianness. You don't need to worry about host endianness with such code. When you care that much about performance, you should avoid allocating a new array each time.

In my crypto library I use:

public static UInt32 LoadLittleEndian32(byte[] buf, int offset)
{
    return
        (UInt32)(buf[offset + 0])
    | (((UInt32)(buf[offset + 1])) << 8)
    | (((UInt32)(buf[offset + 2])) << 16)
    | (((UInt32)(buf[offset + 3])) << 24);
}

public static void StoreLittleEndian32(byte[] buf, int offset, UInt32 value)
{
    buf[offset + 0] = (byte)value;
    buf[offset + 1] = (byte)(value >> 8);
    buf[offset + 2] = (byte)(value >> 16);
    buf[offset + 3] = (byte)(value >> 24);
}

With big endian you just need to change the shift amounts or the offsets:

public static void StoreBigEndian32(byte[] buf, int offset, UInt32 value)
{
    buf[offset + 3] = (byte)value;
    buf[offset + 2] = (byte)(value >> 8);
    buf[offset + 1] = (byte)(value >> 16);
    buf[offset + 0] = (byte)(value >> 24);
}

If you're targetting .net 4.5 it can be useful to mark these methods with [MethodImpl(MethodImplOptions.AggressiveInlining)].

Another performance tip for crypto is avoiding arrays as much as possible. Load the data from the array at the beginning of the function, then run everything using local variables and only in the very end you copy back to the array.