Ruby  2.0.0p247(2013-06-27revision41674)
ext/openssl/ossl_pkcs5.c
Go to the documentation of this file.
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