Ruby
2.0.0p247(2013-06-27revision41674)
|
00001 /* 00002 * $Id$ 00003 * Copyright (C) 2007 Technorama Ltd. <oss-ruby@technorama.net> 00004 */ 00005 #include "ossl.h" 00006 00007 VALUE mPKCS5; 00008 VALUE ePKCS5; 00009 00010 #ifdef HAVE_PKCS5_PBKDF2_HMAC 00011 /* 00012 * call-seq: 00013 * PKCS5.pbkdf2_hmac(pass, salt, iter, keylen, digest) => string 00014 * 00015 * === Parameters 00016 * * +pass+ - string 00017 * * +salt+ - string - should be at least 8 bytes long. 00018 * * +iter+ - integer - should be greater than 1000. 20000 is better. 00019 * * +keylen+ - integer 00020 * * +digest+ - a string or OpenSSL::Digest object. 00021 * 00022 * Available in OpenSSL 0.9.4. 00023 * 00024 * Digests other than SHA1 may not be supported by other cryptography libraries. 00025 */ 00026 static VALUE 00027 ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen, VALUE digest) 00028 { 00029 VALUE str; 00030 const EVP_MD *md; 00031 int len = NUM2INT(keylen); 00032 00033 StringValue(pass); 00034 StringValue(salt); 00035 md = GetDigestPtr(digest); 00036 00037 str = rb_str_new(0, len); 00038 00039 if (PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), 00040 (unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), 00041 NUM2INT(iter), md, len, 00042 (unsigned char *)RSTRING_PTR(str)) != 1) 00043 ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC"); 00044 00045 return str; 00046 } 00047 #else 00048 #define ossl_pkcs5_pbkdf2_hmac rb_f_notimplement 00049 #endif 00050 00051 00052 #ifdef HAVE_PKCS5_PBKDF2_HMAC_SHA1 00053 /* 00054 * call-seq: 00055 * PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, keylen) => string 00056 * 00057 * === Parameters 00058 * * +pass+ - string 00059 * * +salt+ - string - should be at least 8 bytes long. 00060 * * +iter+ - integer - should be greater than 1000. 20000 is better. 00061 * * +keylen+ - integer 00062 * 00063 * This method is available in almost any version of OpenSSL. 00064 * 00065 * Conforms to rfc2898. 00066 */ 00067 static VALUE 00068 ossl_pkcs5_pbkdf2_hmac_sha1(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen) 00069 { 00070 VALUE str; 00071 int len = NUM2INT(keylen); 00072 00073 StringValue(pass); 00074 StringValue(salt); 00075 00076 str = rb_str_new(0, len); 00077 00078 if (PKCS5_PBKDF2_HMAC_SHA1(RSTRING_PTR(pass), RSTRING_LENINT(pass), 00079 (const unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), NUM2INT(iter), 00080 len, (unsigned char *)RSTRING_PTR(str)) != 1) 00081 ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC_SHA1"); 00082 00083 return str; 00084 } 00085 #else 00086 #define ossl_pkcs5_pbkdf2_hmac_sha1 rb_f_notimplement 00087 #endif 00088 00089 void 00090 Init_ossl_pkcs5() 00091 { 00092 /* 00093 * Password-based Encryption 00094 * 00095 */ 00096 00097 #if 0 00098 mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ 00099 #endif 00100 00101 /* Document-class: OpenSSL::PKCS5 00102 * 00103 * Provides password-based encryption functionality based on PKCS#5. 00104 * Typically used for securely deriving arbitrary length symmetric keys 00105 * to be used with an OpenSSL::Cipher from passwords. Another use case 00106 * is for storing passwords: Due to the ability to tweak the effort of 00107 * computation by increasing the iteration count, computation can be 00108 * slowed down artificially in order to render possible attacks infeasible. 00109 * 00110 * PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based 00111 * HMAC, or an arbitrary Digest if the underlying version of OpenSSL 00112 * already supports it (>= 0.9.4). 00113 * 00114 * === Parameters 00115 * ==== Password 00116 * Typically an arbitrary String that represents the password to be used 00117 * for deriving a key. 00118 * ==== Salt 00119 * Prevents attacks based on dictionaries of common passwords. It is a 00120 * public value that can be safely stored along with the password (e.g. 00121 * if PBKDF2 is used for password storage). For maximum security, a fresh, 00122 * random salt should be generated for each stored password. According 00123 * to PKCS#5, a salt should be at least 8 bytes long. 00124 * ==== Iteration Count 00125 * Allows to tweak the length that the actual computation will take. The 00126 * larger the iteration count, the longer it will take. 00127 * ==== Key Length 00128 * Specifies the length in bytes of the output that will be generated. 00129 * Typically, the key length should be larger than or equal to the output 00130 * length of the underlying digest function, otherwise an attacker could 00131 * simply try to brute-force the key. According to PKCS#5, security is 00132 * limited by the output length of the underlying digest function, i.e. 00133 * security is not improved if a key length strictly larger than the 00134 * digest output length is chosen. Therefore, when using PKCS5 for 00135 * password storage, it suffices to store values equal to the digest 00136 * output length, nothing is gained by storing larger values. 00137 * 00138 * == Examples 00139 * === Generating a 128 bit key for a Cipher (e.g. AES) 00140 * pass = "secret" 00141 * salt = OpenSSL::Random.random_bytes(16) 00142 * iter = 20000 00143 * key_len = 16 00144 * key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len) 00145 * 00146 * === Storing Passwords 00147 * pass = "secret" 00148 * salt = OpenSSL::Random.random_bytes(16) #store this with the generated value 00149 * iter = 20000 00150 * digest = OpenSSL::Digest::SHA256.new 00151 * len = digest.digest_length 00152 * #the final value to be stored 00153 * value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest) 00154 * 00155 * === Important Note on Checking Passwords 00156 * When comparing passwords provided by the user with previously stored 00157 * values, a common mistake made is comparing the two values using "==". 00158 * Typically, "==" short-circuits on evaluation, and is therefore 00159 * vulnerable to timing attacks. The proper way is to use a method that 00160 * always takes the same amount of time when comparing two values, thus 00161 * not leaking any information to potential attackers. To compare two 00162 * values, the following could be used: 00163 * def eql_time_cmp(a, b) 00164 * unless a.length == b.length 00165 * return false 00166 * end 00167 * cmp = b.bytes.to_a 00168 * result = 0 00169 * a.bytes.each_with_index {|c,i| 00170 * result |= c ^ cmp[i] 00171 * } 00172 * result == 0 00173 * end 00174 * Please note that the premature return in case of differing lengths 00175 * typically does not leak valuable information - when using PKCS#5, the 00176 * length of the values to be compared is of fixed size. 00177 */ 00178 00179 mPKCS5 = rb_define_module_under(mOSSL, "PKCS5"); 00180 /* Document-class: OpenSSL::PKCS5::PKCS5Error 00181 * 00182 * Generic Exception class that is raised if an error occurs during a 00183 * computation. 00184 */ 00185 ePKCS5 = rb_define_class_under(mPKCS5, "PKCS5Error", eOSSLError); 00186 00187 rb_define_module_function(mPKCS5, "pbkdf2_hmac", ossl_pkcs5_pbkdf2_hmac, 5); 00188 rb_define_module_function(mPKCS5, "pbkdf2_hmac_sha1", ossl_pkcs5_pbkdf2_hmac_sha1, 4); 00189 } 00190