From 98490d1f71a6d7072c8bf439a88e08c3762b27dd Mon Sep 17 00:00:00 2001 From: emboss Date: Thu, 20 Oct 2011 13:48:21 +0000 Subject: * ext/openssl/ossl_pkcs5.c: add note on timing attacks and general documentation. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@33495 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/openssl/ossl_pkcs5.c | 100 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 6 deletions(-) (limited to 'ext/openssl/ossl_pkcs5.c') diff --git a/ext/openssl/ossl_pkcs5.c b/ext/openssl/ossl_pkcs5.c index d3eaf2d073..7f9c48d170 100644 --- a/ext/openssl/ossl_pkcs5.c +++ b/ext/openssl/ossl_pkcs5.c @@ -14,12 +14,12 @@ VALUE ePKCS5; * * === Parameters * * +pass+ - string - * * +salt+ - string - * * +iter+ - integer - should be greater than 1000. 2000 is better. + * * +salt+ - string - should be at least 8 bytes long. + * * +iter+ - integer - should be greater than 1000. 20000 is better. * * +keylen+ - integer * * +digest+ - a string or OpenSSL::Digest object. * - * Available in OpenSSL 0.9.9?. + * Available in OpenSSL 0.9.4. * * Digests other than SHA1 may not be supported by other cryptography libraries. */ @@ -56,11 +56,11 @@ ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE key * * === Parameters * * +pass+ - string - * * +salt+ - string - * * +iter+ - integer - should be greater than 1000. 2000 is better. + * * +salt+ - string - should be at least 8 bytes long. + * * +iter+ - integer - should be greater than 1000. 20000 is better. * * +keylen+ - integer * - * This method is available almost any version OpenSSL. + * This method is available in almost any version of OpenSSL. * * Conforms to rfc2898. */ @@ -93,7 +93,95 @@ Init_ossl_pkcs5() * Password-based Encryption * */ + + #if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ + #endif + + /* Document-class: OpenSSL::PKCS5 + * + * Provides password-based encryption functionality based on PKCS#5. + * Typically used for securely deriving arbitrary length symmetric keys + * to be used with an OpenSSL::Cipher from passwords. Another use case + * is for storing passwords: Due to the ability to tweak the effort of + * computation by increasing the iteration count, computation can be + * slowed down artificially in order to render possible attacks infeasible. + * + * PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based + * HMAC, or an arbitrary Digest if the underlying version of OpenSSL + * already supports it (>= 0.9.4). + * + * === Parameters + * ==== Password + * Typically an arbitrary String that represents the password to be used + * for deriving a key. + * ==== Salt + * Prevents attacks based on dictionaries of common passwords. It is a + * public value that can be safely stored along with the password (e.g. + * if PBKDF2 is used for password storage). For maximum security, a fresh, + * random salt should be generated for each stored password. According + * to PKCS#5, a salt should be at least 8 bytes long. + * ==== Iteration Count + * Allows to tweak the length that the actual computation will take. The + * larger the iteration count, the longer it will take. + * ==== Key Length + * Specifies the length in bytes of the output that will be generated. + * Typically, the key length should be larger than or equal to the output + * length of the underlying digest function, otherwise an attacker could + * simply try to brute-force the key. According to PKCS#5, security is + * limited by the output length of the underlying digest function, i.e. + * security is not improved if a key length strictly larger than the + * digest output length is chosen. Therefore, when using PKCS5 for + * password storage, it suffices to store values equal to the digest + * output length, nothing is gained by storing larger values. + * + * == Examples + * === Generating a 128 bit key for a Cipher (e.g. AES) + * pass = "secret" + * salt = OpenSSL::Random.random_bytes(16) + * iter = 20000 + * key_len = 16 + * key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len) + * + * === Storing Passwords + * pass = "secret" + * salt = OpenSSL::Random.random_bytes(16) #store this with the generated value + * iter = 20000 + * digest = OpenSSL::Digest::SHA256.new + * len = digest.digest_length + * #the final value to be stored + * value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest) + * + * === Important Note on Checking Passwords + * When comparing passwords provided by the user with previously stored + * values, a common mistake made is comparing the two values using "==". + * Typically, "==" short-circuits on evaluation, and is therefore + * vulnerable to timing attacks. The proper way is to use a method that + * always takes the same amount of time when comparing two values, thus + * not leaking any information to potential attackers. To compare two + * values, the following could be used: + * def eql_time_cmp(a, b) + * unless a.length == b.length + * return false + * end + * cmp = b.bytes.to_a + * result = 0 + * a.bytes.each_with_index {|c,i| + * result |= c ^ cmp[i] + * } + * result == 0 + * end + * Please note that the premature return in case of differing lengths + * typically does not leak valuable information - when using PKCS#5, the + * length of the values to be compared is of fixed size. + */ + mPKCS5 = rb_define_module_under(mOSSL, "PKCS5"); + /* Document-class: OpenSSL::PKCS5::PKCS5Error + * + * Generic Exception class that is raised if an error occurs during a + * computation. + */ ePKCS5 = rb_define_class_under(mPKCS5, "PKCS5Error", eOSSLError); rb_define_module_function(mPKCS5, "pbkdf2_hmac", ossl_pkcs5_pbkdf2_hmac, 5); -- cgit v1.2.3