Feature #10098 » 0001-add-timing-safe-string-compare-method.patch
configure.in | ||
---|---|---|
AC_CHECK_FUNCS(utimes)
|
||
AC_CHECK_FUNCS(wait4)
|
||
AC_CHECK_FUNCS(waitpid)
|
||
AC_CHECK_FUNCS(consttime_memequal)
|
||
AC_CHECK_FUNCS(timingsafe_memcmp)
|
||
AS_IF([test "$ac_cv_func_getcwd" = yes], [
|
||
AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
|
string.c | ||
---|---|---|
return str_eql(str1, str2);
|
||
}
|
||
static inline int
|
||
rb_consttime_memequal(const char *buf1, const char *buf2, long len)
|
||
{
|
||
#if defined(HAVE_TIMINGSAFE_MEMCMP)
|
||
return (timingsafe_memcmp(buf1, buf2, len) == 0);
|
||
#elif defined(HAVE_CONSTTIME_MEMEQUAL)
|
||
return (consttime_memequal(buf1, buf2, len) != 0);
|
||
#else
|
||
VALUE result;
|
||
long idx;
|
||
result = 0;
|
||
idx = 0;
|
||
if (UNALIGNED_WORD_ACCESS || !((VALUE)buf1 % sizeof(VALUE)) && !((VALUE)buf2 % sizeof(VALUE))) {
|
||
for (; idx < len; idx += sizeof(VALUE)) {
|
||
result |= *(const VALUE *)(buf1+idx) ^ *(const VALUE *)(buf2+idx);
|
||
}
|
||
}
|
||
for (; idx < len; idx++) {
|
||
result |= buf1[idx] ^ buf2[idx];
|
||
}
|
||
return (result == 0);
|
||
#endif
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* str.consttime_bytes_eq?(other) -> true or false
|
||
*
|
||
* Ignoring encoding, compares each byte of +str+ against +other+ in constant time.
|
||
*/
|
||
static VALUE
|
||
rb_str_consttime_bytes_eq(VALUE str1, VALUE str2)
|
||
{
|
||
long len;
|
||
str2 = StringValue(str2);
|
||
len = RSTRING_LEN(str1);
|
||
if (RSTRING_LEN(str2) != len) return Qfalse;
|
||
if (rb_consttime_memequal(RSTRING_PTR(str1), RSTRING_PTR(str2), len) != 0) return Qtrue;
|
||
return Qfalse;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* string <=> other_string -> -1, 0, +1 or nil
|
||
... | ... | |
rb_define_method(rb_cString, "==", rb_str_equal, 1);
|
||
rb_define_method(rb_cString, "===", rb_str_equal, 1);
|
||
rb_define_method(rb_cString, "eql?", rb_str_eql, 1);
|
||
rb_define_method(rb_cString, "consttime_bytes_eq?", rb_str_consttime_bytes_eq, 1);
|
||
rb_define_method(rb_cString, "hash", rb_str_hash_m, 0);
|
||
rb_define_method(rb_cString, "casecmp", rb_str_casecmp, 1);
|
||
rb_define_method(rb_cString, "+", rb_str_plus, 1);
|
test/ruby/test_string.rb | ||
---|---|---|
require 'test/unit'
|
||
require_relative 'envutil'
|
||
require 'benchmark'
|
||
# use of $= is deprecated after 1.7.1
|
||
def pre_1_7_1
|
||
... | ... | |
casetest(S("CaT"), S('cAt'), true) # find these in the case.
|
||
end
|
||
def test_consttime_bytes_eq # 'consttime_bytes_eq?'
|
||
assert_equal(true, S("foo").consttime_bytes_eq?(S("foo")))
|
||
assert_equal(false, S("foo").consttime_bytes_eq?(S("foO")))
|
||
assert_equal(true, S("f\x00oo").consttime_bytes_eq?(S("f\x00oo")))
|
||
assert_equal(false, S("f\x00oo").consttime_bytes_eq?(S("f\x00oO")))
|
||
end
|
||
def test_consttime_bytes_eq_timing
|
||
# ensure using consttime_bytes_eq? takes almost exactly the same amount of time to compare two
|
||
# different strings.
|
||
# NOTE: this test may be susceptible to noise if the system running the tests is otherwise under
|
||
# load.
|
||
a = "x"*1024_000
|
||
b = a+"y"
|
||
c = "y"+a
|
||
a << "x"
|
||
def measure(&block)
|
||
Benchmark.measure(&block).real
|
||
end
|
||
n = 10_000
|
||
a_b_time = measure { n.times { a.consttime_bytes_eq?(b) } }
|
||
a_c_time = measure { n.times { a.consttime_bytes_eq?(c) } }
|
||
assert_in_delta(a_b_time, a_c_time, 0.25, "consttime_bytes_eq? timing test failed")
|
||
end
|
||
def test_capitalize
|
||
assert_equal(S("Hello"), S("hello").capitalize)
|
||
assert_equal(S("Hello"), S("hELLO").capitalize)
|
- « Previous
- 1
- …
- 3
- 4
- 5
- Next »