Safe equality: why is inequality still shorter?

I have a method that compares two byte arrays for equality, with a basic warning that it does not interrupt and exits earlier when it detects an inequality. Basically, the code is used to compare subroutine tokens for cross-site requests and avoids (as much as possible) the ability to use a timeout to crack a key. I'm sorry that I cannot find a link to an article detailing the attack, but the important thing is that the code I have still has a statistically measurable offset to return earlier if the two byte arrays are equal - although this is the order it's better. So, without further ado, here is the code:

public static bool SecureEquals(byte[] original, byte[] potential)
{
    // They should be the same size, but we don't want to throw an
    // exception if we are wrong.
    bool isEqual = original.Length == potential.Length;
    int maxLenth = Math.Max(original.Length, potential.Length);

    for(int i=0; i < maxLength; i++)
    {
        byte originalByte = (i < original.Length) ? original[i] : (byte)0;
        byte potentialByte = (i < potential.Length) ? potential[i] : (byte)0;

        isEqual = isEqual && (originalByte == potentialByte);
    }

    return isEqual;
}

10-25 ( ) . . , , . , . , , 80 .

, . , , - . , , .

- , , ? , , , . , , .

. " ":

+3
3

:

isEqual = isEqual && (originalByte == potentialByte);

originalByte == potentialByte, isEquals false. , :

isEqual = isEqual & (originalByte == potentialByte);

: , , , potential original, . , , ... ", ", , .

EDIT: , :

// Let assume you'll never really need more than this
private static readonly byte[] LargeJunkArray = new byte[1024 * 32];

public static bool SecureEquals(byte[] original, byte[] potential)
{
    // Reveal that the original array will never be more than 32K.
    // This is unlikely to be particularly useful.
    if (potential.Length > LargeJunkArray.Length)
    {
        return false;
    }
    byte[] copy = new byte[potential.Length];
    int bytesFromOriginal = Math.Min(original.Length, copy.Length);
    // Always copy the same amount of data
    Array.Copy(original, 0, copy, 0, bytesFromOriginal);
    Array.Copy(LargeJunkArray, 0, copy, bytesFromOriginal,
               copy.Length - bytesFromOriginal);

    bool isEqual = original.Length == potential.Length;
    for(int i=0; i < copy.Length; i++)
    {
        isEqual = isEqual & (copy[i] == potential[i]);
    }

    return isEqual;
}

, , Array.Copy , - , CPU...

+10

, .NET , , , true , ?

public static bool SecureEquals(byte[] original, byte[] potential)
{
    // They should be the same size, but we don't want to throw an
    // exception if we are wrong.
    int maxLength = Math.Max(original.Length, potential.Length);
    int equals = maxLength - Math.Min(original.Length, potential.Length);

    for(int i=0; i < maxLength; i++)
    {
        byte originalByte = (i < original.Length) ? original[i] : (byte)0;
        byte potentialByte = (i < potential.Length) ? potential[i] : (byte)0;

        equals += originalByte != potentialByte ? 1 : 0;
    }

    return equals == 0;
}
+4

, - , . .

, , . ( "".) - - . , , , , , , . , .

The best part about this approach is that now you have a common mechanism that can be reused when other temporary attacks win, and the meaning of the code is obvious; nobody is going to go and try to optimize it.

And, like any other Security code, review it with a security specialist before submitting it.

+3
source

Source: https://habr.com/ru/post/1784755/


All Articles