Project

General

Profile

0001-add-timing-safe-string-compare-method.patch

arrtchiu (Matt U), 08/23/2014 09:12 AM

Download (4.31 KB)

View differences:

configure.in
2066 2066
AC_CHECK_FUNCS(utimes)
2067 2067
AC_CHECK_FUNCS(wait4)
2068 2068
AC_CHECK_FUNCS(waitpid)
2069
AC_CHECK_FUNCS(consttime_memequal)
2070
AC_CHECK_FUNCS(timingsafe_memcmp)
2069 2071

  
2070 2072
AS_IF([test "$ac_cv_func_getcwd" = yes], [
2071 2073
    AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
string.c
2490 2490
    return str_eql(str1, str2);
2491 2491
}
2492 2492

  
2493
static inline int
2494
rb_consttime_memequal(const char *buf1, const char *buf2, long len)
2495
{
2496
#if defined(HAVE_TIMINGSAFE_MEMCMP)
2497
    return (timingsafe_memcmp(buf1, buf2, len) == 0);
2498
#elif defined(HAVE_CONSTTIME_MEMEQUAL)
2499
    return (consttime_memequal(buf1, buf2, len) != 0);
2500
#else
2501
    VALUE result;
2502
    long idx;
2503

  
2504
    result = 0;
2505
    idx = 0;
2506
    if (UNALIGNED_WORD_ACCESS || !((VALUE)buf1 % sizeof(VALUE)) && !((VALUE)buf2 % sizeof(VALUE))) {
2507
        for (; idx < len; idx += sizeof(VALUE)) {
2508
            result |= *(const VALUE *)(buf1+idx) ^ *(const VALUE *)(buf2+idx);
2509
        }
2510
    }
2511

  
2512
    for (; idx < len; idx++) {
2513
        result |= buf1[idx] ^ buf2[idx];
2514
    }
2515

  
2516
    return (result == 0);
2517
#endif
2518
}
2519

  
2520
/*
2521
 * call-seq:
2522
 *   str.consttime_bytes_eq?(other)   -> true or false
2523
 *
2524
 * Ignoring encoding, compares each byte of +str+ against +other+ in constant time.
2525
 */
2526

  
2527
static VALUE
2528
rb_str_consttime_bytes_eq(VALUE str1, VALUE str2)
2529
{
2530
    long len;
2531

  
2532
    str2 = StringValue(str2);
2533
    len = RSTRING_LEN(str1);
2534

  
2535
    if (RSTRING_LEN(str2) != len) return Qfalse;
2536
    if (rb_consttime_memequal(RSTRING_PTR(str1), RSTRING_PTR(str2), len) != 0) return Qtrue;
2537

  
2538
    return Qfalse;
2539
}
2540

  
2493 2541
/*
2494 2542
 *  call-seq:
2495 2543
 *     string <=> other_string   -> -1, 0, +1 or nil
......
8761 8809
    rb_define_method(rb_cString, "==", rb_str_equal, 1);
8762 8810
    rb_define_method(rb_cString, "===", rb_str_equal, 1);
8763 8811
    rb_define_method(rb_cString, "eql?", rb_str_eql, 1);
8812
    rb_define_method(rb_cString, "consttime_bytes_eq?", rb_str_consttime_bytes_eq, 1);
8764 8813
    rb_define_method(rb_cString, "hash", rb_str_hash_m, 0);
8765 8814
    rb_define_method(rb_cString, "casecmp", rb_str_casecmp, 1);
8766 8815
    rb_define_method(rb_cString, "+", rb_str_plus, 1);
test/ruby/test_string.rb
1 1
require 'test/unit'
2 2
require_relative 'envutil'
3
require 'benchmark'
3 4

  
4 5
# use of $= is deprecated after 1.7.1
5 6
def pre_1_7_1
......
304 305
    casetest(S("CaT"), S('cAt'), true) # find these in the case.
305 306
  end
306 307

  
308
  def test_consttime_bytes_eq # 'consttime_bytes_eq?'
309
    assert_equal(true, S("foo").consttime_bytes_eq?(S("foo")))
310
    assert_equal(false, S("foo").consttime_bytes_eq?(S("foO")))
311
    assert_equal(true, S("f\x00oo").consttime_bytes_eq?(S("f\x00oo")))
312
    assert_equal(false, S("f\x00oo").consttime_bytes_eq?(S("f\x00oO")))
313
  end
314

  
315
  def test_consttime_bytes_eq_timing
316
    # ensure using consttime_bytes_eq? takes almost exactly the same amount of time to compare two
317
    # different strings.
318
    # NOTE: this test may be susceptible to noise if the system running the tests is otherwise under
319
    # load.
320
    a = "x"*1024_000
321
    b = a+"y"
322
    c = "y"+a
323
    a << "x"
324

  
325
    def measure(&block)
326
      Benchmark.measure(&block).real
327
    end
328

  
329
    n = 10_000
330
    a_b_time = measure { n.times { a.consttime_bytes_eq?(b) } }
331
    a_c_time = measure { n.times { a.consttime_bytes_eq?(c) } }
332
    assert_in_delta(a_b_time, a_c_time, 0.25, "consttime_bytes_eq? timing test failed")
333
  end
334

  
307 335
  def test_capitalize
308 336
    assert_equal(S("Hello"),  S("hello").capitalize)
309 337
    assert_equal(S("Hello"),  S("hELLO").capitalize)
310
-