aboutsummaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
authorBart de Water <bartdewater@gmail.com>2019-08-18 00:26:54 -0400
committerSamuel Williams <samuel.williams@oriontransfer.co.nz>2019-10-08 10:00:11 +1300
commit21681810c87e0f97979873284596104c87a6f780 (patch)
tree894a333873a83d5c9e11013cf4e1d9acbadb49c5 /ext
parentf7fc8c12807c523fc7e91429f83b6ee978ee26cb (diff)
downloadruby-openssl-21681810c87e0f97979873284596104c87a6f780.tar.gz
Add `OpenSSL.memcmp?` for constant time/timing safe string comparison
Fixes https://bugs.ruby-lang.org/issues/10098
Diffstat (limited to 'ext')
-rw-r--r--ext/openssl/ossl.c36
1 files changed, 36 insertions, 0 deletions
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index ab9a6bac..c6749267 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -605,6 +605,41 @@ static void Init_ossl_locks(void)
#endif /* !HAVE_OPENSSL_110_THREADING_API */
/*
+ * call-seq:
+ * OpenSSL.memcmp?(string, string) -> boolean
+ *
+ * Constant time memory comparison. Inputs must be of equal length, otherwise
+ * an error is raised since timing attacks could leak the length of a
+ * secret.
+ *
+ * For securely comparing user input, it's recommended to use hashing and
+ * regularly compare after to prevent an unlikely false positive due to a
+ * collision.
+ *
+ * user_input = "..."
+ * expected = "..."
+ * hashed_input = OpenSSL::Digest::SHA256.digest(user_input)
+ * hashed_expected = OpenSSL::Digest::SHA256.digest(expected)
+ * OpenSSL.memcmp?(hashed_input, hashed_expected) && user_input == expected
+ */
+static VALUE
+ossl_crypto_memcmp(VALUE dummy, VALUE str1, VALUE str2)
+{
+ const unsigned char *p1 = (const unsigned char *)StringValuePtr(str1);
+ const unsigned char *p2 = (const unsigned char *)StringValuePtr(str2);
+ long len1 = RSTRING_LEN(str1);
+ long len2 = RSTRING_LEN(str2);
+
+ if(len1 != len2)
+ ossl_raise(rb_eArgError, "inputs must be of equal length");
+
+ switch (CRYPTO_memcmp(p1, p2, len1)) {
+ case 0: return Qtrue;
+ default: return Qfalse;
+ }
+}
+
+/*
* OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the
* OpenSSL[https://www.openssl.org/] library.
*
@@ -1125,6 +1160,7 @@ Init_openssl(void)
*/
mOSSL = rb_define_module("OpenSSL");
rb_global_variable(&mOSSL);
+ rb_define_singleton_method(mOSSL, "memcmp?", ossl_crypto_memcmp, 2);
/*
* OpenSSL ruby extension version