0001-add-timing-safe-string-compare-method.patch
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 |
- |