@@ -64,6 +64,80 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
6464 return str ;
6565}
6666
67+ #if defined(HAVE_EVP_PBE_SCRYPT )
68+ /*
69+ * call-seq:
70+ * KDF.scrypt(pass, salt:, N:, r:, p:, length:) -> aString
71+ *
72+ * Derives a key from _pass_ using given parameters with the scrypt
73+ * password-based key derivation function. The result can be used for password
74+ * storage.
75+ *
76+ * scrypt is designed to be memory-hard and more secure against brute-force
77+ * attacks using custom hardwares than alternative KDFs such as PBKDF2 or
78+ * bcrypt.
79+ *
80+ * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914
81+ * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states
82+ * that using values r=8 and p=1 appears to yield good results.
83+ *
84+ * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information.
85+ *
86+ * === Parameters
87+ * pass :: Passphrase.
88+ * salt :: Salt.
89+ * N :: CPU/memory cost parameter. This must be a power of 2.
90+ * r :: Block size parameter.
91+ * p :: Parallelization parameter.
92+ * length :: Length in octets of the derived key.
93+ *
94+ * === Example
95+ * pass = "password"
96+ * salt = SecureRandom.random_bytes(16)
97+ * dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32)
98+ * p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T"
99+ */
100+ static VALUE
101+ kdf_scrypt (int argc , VALUE * argv , VALUE self )
102+ {
103+ VALUE pass , salt , opts , kwargs [5 ], str ;
104+ static ID kwargs_ids [5 ];
105+ size_t len ;
106+ uint64_t N , r , p , maxmem ;
107+
108+ if (!kwargs_ids [0 ]) {
109+ kwargs_ids [0 ] = rb_intern_const ("salt" );
110+ kwargs_ids [1 ] = rb_intern_const ("N" );
111+ kwargs_ids [2 ] = rb_intern_const ("r" );
112+ kwargs_ids [3 ] = rb_intern_const ("p" );
113+ kwargs_ids [4 ] = rb_intern_const ("length" );
114+ }
115+ rb_scan_args (argc , argv , "1:" , & pass , & opts );
116+ rb_get_kwargs (opts , kwargs_ids , 5 , 0 , kwargs );
117+
118+ StringValue (pass );
119+ salt = StringValue (kwargs [0 ]);
120+ N = NUM2UINT64T (kwargs [1 ]);
121+ r = NUM2UINT64T (kwargs [2 ]);
122+ p = NUM2UINT64T (kwargs [3 ]);
123+ len = NUM2LONG (kwargs [4 ]);
124+ /*
125+ * OpenSSL uses 32MB by default (if zero is specified), which is too small.
126+ * Let's not limit memory consumption but just let malloc() fail inside
127+ * OpenSSL. The amount is controllable by other parameters.
128+ */
129+ maxmem = SIZE_MAX ;
130+
131+ str = rb_str_new (0 , len );
132+ if (!EVP_PBE_scrypt (RSTRING_PTR (pass ), RSTRING_LEN (pass ),
133+ (unsigned char * )RSTRING_PTR (salt ), RSTRING_LEN (salt ),
134+ N , r , p , maxmem , (unsigned char * )RSTRING_PTR (str ), len ))
135+ ossl_raise (eKDF , "EVP_PBE_scrypt" );
136+
137+ return str ;
138+ }
139+ #endif
140+
67141void
68142Init_ossl_kdf (void )
69143{
@@ -87,6 +161,7 @@ Init_ossl_kdf(void)
87161 *
88162 * * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in
89163 * combination with HMAC
164+ * * scrypt
90165 *
91166 * == Examples
92167 * === Generating a 128 bit key for a Cipher (e.g. AES)
@@ -140,4 +215,7 @@ Init_ossl_kdf(void)
140215 eKDF = rb_define_class_under (mKDF , "KDFError" , eOSSLError );
141216
142217 rb_define_module_function (mKDF , "pbkdf2_hmac" , kdf_pbkdf2_hmac , -1 );
218+ #if defined(HAVE_EVP_PBE_SCRYPT )
219+ rb_define_module_function (mKDF , "scrypt" , kdf_scrypt , -1 );
220+ #endif
143221}
0 commit comments