44 */
55#include "ossl.h"
66#include <openssl/kdf.h>
7+ #ifdef OSSL_USE_PROVIDER
8+ # include <openssl/core_names.h>
9+ #endif
710
811static VALUE mKDF , eKDF ;
912
@@ -16,21 +19,53 @@ struct pbkdf2_hmac_args {
1619 const EVP_MD * md ;
1720 int len ;
1821 unsigned char * out ;
22+ int pkcs5 ;
1923};
2024
2125static void *
2226pbkdf2_hmac_nogvl (void * args_ )
2327{
2428 struct pbkdf2_hmac_args * args = (struct pbkdf2_hmac_args * )args_ ;
29+ #ifdef OSSL_USE_PROVIDER
30+ static char empty [] = "" ;
31+ int ret = 0 ;
32+ EVP_KDF * kdf ;
33+ EVP_KDF_CTX * kctx ;
34+ OSSL_PARAM params [6 ], * p = params ;
35+
36+ kdf = EVP_KDF_fetch (NULL , OSSL_KDF_NAME_PBKDF2 , NULL );
37+ if (kdf == NULL )
38+ return (void * )(uintptr_t )0 ;
39+ kctx = EVP_KDF_CTX_new (kdf );
40+ EVP_KDF_free (kdf );
41+ if (kctx == NULL )
42+ return (void * )(uintptr_t )0 ;
43+ * p ++ = OSSL_PARAM_construct_int (OSSL_KDF_PARAM_PKCS5 , & args -> pkcs5 );
44+ * p ++ = OSSL_PARAM_construct_octet_string (OSSL_KDF_PARAM_PASSWORD ,
45+ args -> passlen ? args -> pass : empty ,
46+ (size_t )args -> passlen );
47+ * p ++ = OSSL_PARAM_construct_octet_string (OSSL_KDF_PARAM_SALT ,
48+ args -> saltlen ? (char * )args -> salt : empty ,
49+ (size_t )args -> saltlen );
50+ * p ++ = OSSL_PARAM_construct_int (OSSL_KDF_PARAM_ITER , & args -> iters );
51+ * p ++ = OSSL_PARAM_construct_utf8_string (OSSL_KDF_PARAM_DIGEST ,
52+ (char * )EVP_MD_get0_name (args -> md ),
53+ 0 );
54+ * p = OSSL_PARAM_construct_end ();
55+ if (EVP_KDF_derive (kctx , args -> out , (size_t )args -> len , params ) == 1 )
56+ ret = 1 ;
57+ EVP_KDF_CTX_free (kctx );
58+ #else
2559 int ret = PKCS5_PBKDF2_HMAC (args -> pass , args -> passlen , args -> salt ,
2660 args -> saltlen , args -> iters , args -> md ,
2761 args -> len , args -> out );
62+ #endif
2863 return (void * )(uintptr_t )ret ;
2964}
3065
3166/*
3267 * call-seq:
33- * KDF.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) -> aString
68+ * KDF.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:, pkcs5: 1 ) -> aString
3469 *
3570 * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination
3671 * with HMAC. Takes _pass_, _salt_ and _iterations_, and then derives a key
@@ -56,29 +91,57 @@ pbkdf2_hmac_nogvl(void *args_)
5691 * hash :: The hash algorithm used with HMAC for the PRF. May be a String
5792 * representing the algorithm name, or an instance of
5893 * OpenSSL::Digest.
94+ * pkcs5 :: Optional. Enforces or bypasses the SP 800-132 lower bound
95+ * checks done by OpenSSL. If 1 (the default) or any other
96+ * non-zero value, the checks are bypassed, for compatibility
97+ * with plain PKCS #5. If 0, OpenSSL 3.0 and later enforce the
98+ * lower bound checks: an iteration count of at least 1000, a
99+ * salt of at least 128 bits, a derived key of at least 112
100+ * bits, and a non-empty password (at least 8 bytes with the
101+ * FIPS provider). With the FIPS provider, bypassing the checks
102+ * might still block the operation, or raise the non-approved
103+ * usage indicator, which allows it for decryption and legacy
104+ * purposes. With other libraries, such as LibreSSL, this
105+ * argument is accepted but has no effect.
106+ *
107+ * The default matches the behavior of PKCS5_PBKDF2_HMAC()
108+ * prior to the OpenSSL 4.0 release. It allows existing data
109+ * to be decrypted, such as Active Record encrypted attributes,
110+ * which use a salt that is too short by default
111+ * (https://github.com/rails/rails/pull/58937).
112+ *
113+ * *NOTE*: The lower bounds enforced by OpenSSL are minimums, not
114+ * recommendations. They are far too low to protect stored passwords
115+ * against modern brute-force attacks. For security-sensitive uses, choose
116+ * parameters as recommended by the OWASP Password Storage Cheat Sheet
117+ * (https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html),
118+ * for example 600,000 iterations with HMAC-SHA-256 or 210,000 iterations
119+ * with HMAC-SHA-512.
59120 */
60121static VALUE
61122kdf_pbkdf2_hmac (int argc , VALUE * argv , VALUE self )
62123{
63- VALUE pass , salt , opts , kwargs [4 ], str , md_holder , pass_tmp , salt_tmp ;
64- static ID kwargs_ids [4 ];
65- int passlen , saltlen , iters , len ;
124+ VALUE pass , salt , opts , kwargs [5 ], str , md_holder , pass_tmp , salt_tmp ;
125+ static ID kwargs_ids [5 ];
126+ int passlen , saltlen , iters , len , pkcs5 ;
66127 const EVP_MD * md ;
67128
68129 if (!kwargs_ids [0 ]) {
69130 kwargs_ids [0 ] = rb_intern_const ("salt" );
70131 kwargs_ids [1 ] = rb_intern_const ("iterations" );
71132 kwargs_ids [2 ] = rb_intern_const ("length" );
72133 kwargs_ids [3 ] = rb_intern_const ("hash" );
134+ kwargs_ids [4 ] = rb_intern_const ("pkcs5" );
73135 }
74136 rb_scan_args (argc , argv , "1:" , & pass , & opts );
75- rb_get_kwargs (opts , kwargs_ids , 4 , 0 , kwargs );
137+ rb_get_kwargs (opts , kwargs_ids , 4 , 1 , kwargs );
76138
77139 StringValue (pass );
78140 salt = StringValue (kwargs [0 ]);
79141 iters = NUM2INT (kwargs [1 ]);
80142 len = NUM2INT (kwargs [2 ]);
81143 md = ossl_evp_md_fetch (kwargs [3 ], & md_holder );
144+ pkcs5 = kwargs [4 ] == Qundef ? 1 : NUM2INT (kwargs [4 ]);
82145 passlen = RSTRING_LENINT (pass );
83146 saltlen = RSTRING_LENINT (salt );
84147 str = rb_str_new (NULL , len );
@@ -91,11 +154,16 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
91154 .md = md ,
92155 .len = len ,
93156 .out = (unsigned char * )RSTRING_PTR (str ),
157+ .pkcs5 = pkcs5 ,
94158 };
95159 memcpy (args .pass , RSTRING_PTR (pass ), passlen );
96160 memcpy (args .salt , RSTRING_PTR (salt ), saltlen );
97161 if (!rb_thread_call_without_gvl (pbkdf2_hmac_nogvl , & args , NULL , NULL ))
162+ #ifdef OSSL_USE_PROVIDER
163+ ossl_raise (eKDF , "EVP_KDF_derive" );
164+ #else
98165 ossl_raise (eKDF , "PKCS5_PBKDF2_HMAC" );
166+ #endif
99167 OPENSSL_cleanse (args .pass , passlen );
100168 ALLOCV_END (pass_tmp );
101169 ALLOCV_END (salt_tmp );
0 commit comments