Faster constant-time string comparison in Ruby

To make things fast while keeping them simple, I've reimplemented the function in C, and made it available as a gem.

The source is on GitHub (https://github.com/daxtens/fast_secure_compare), but the crux of it is the following very simple C routine.

int secure_compare_bytes(const unsigned char * secret, unsigned int secret_len, 
                     const unsigned char * input, unsigned int input_len) {

    int input_pos;
    int secret_pos = 0;
    int result = secret_len - input_len;
    // make sure our time isn't dependent on secret_len, and only dependent
    // on input_len
    for (input_pos = 0; input_pos < input_len; input_pos++) {
        result |= input[input_pos] ^ secret[secret_pos];
        secret_pos = (secret_pos + 1) % secret_len;
    }

    return result;
}

```

There's also a bit of FFI glue to get it to talk to Ruby.

It's much faster than the original pure Ruby, and is somewhat faster (and much simpler) than hashing. I have edited out rehearsals for brevity. This is on a 2008 MacBook. You can replicate this with timing.rb in the demo directory.

==== Long text ====
                                         user     system      total        real
==, early fail                       0.000000   0.000000   0.000000 (  0.000028)
==, late fail                        0.000000   0.000000   0.000000 (  0.000710)
Pure Ruby secure_compare, 'early'    1.730000   0.040000   1.770000 (  1.777258)
Pure Ruby secure_compare, 'late'     1.730000   0.050000   1.780000 (  1.774144)
C-based FastSecureCompare, 'early'   0.040000   0.000000   0.040000 (  0.047612)
C-based FastSecureCompare, 'late'    0.040000   0.000000   0.040000 (  0.045767)
SHA512-then-==, 'early'              0.050000   0.000000   0.050000 (  0.048569)
SHA512-then-==, 'late'               0.050000   0.000000   0.050000 (  0.046100)

==== Short text ====
                                         user     system      total        real
==, early fail                       0.000000   0.000000   0.000000 (  0.000028)
==, late fail                        0.000000   0.000000   0.000000 (  0.000031)
Pure Ruby secure_compare, 'early'    0.010000   0.000000   0.010000 (  0.010552)
Pure Ruby secure_compare, 'late'     0.010000   0.000000   0.010000 (  0.010805)
C-based FastSecureCompare, 'early'   0.000000   0.000000   0.000000 (  0.000556)
C-based FastSecureCompare, 'late'    0.000000   0.000000   0.000000 (  0.000516)
SHA512-then-==, 'early'              0.000000   0.000000   0.000000 (  0.000780)
SHA512-then-==, 'late'               0.000000   0.000000   0.000000 (  0.000812)

Tags:

Ruby