Ruby 2.7.6p219 (2022-04-12 revision c9c2245c0a25176072e02db9254f0e0c84c805cd)
ossl_kdf.c
Go to the documentation of this file.
1/*
2 * Ruby/OpenSSL Project
3 * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors
4 */
5#include "ossl.h"
6#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
7# include <openssl/kdf.h>
8#endif
9
10static VALUE mKDF, eKDF;
11
12/*
13 * call-seq:
14 * KDF.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) -> aString
15 *
16 * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination
17 * with HMAC. Takes _pass_, _salt_ and _iterations_, and then derives a key
18 * of _length_ bytes.
19 *
20 * For more information about PBKDF2, see RFC 2898 Section 5.2
21 * (https://tools.ietf.org/html/rfc2898#section-5.2).
22 *
23 * === Parameters
24 * pass :: The passphrase.
25 * salt :: The salt. Salts prevent attacks based on dictionaries of common
26 * passwords and attacks based on rainbow tables. It is a public
27 * value that can be safely stored along with the password (e.g.
28 * if the derived value is used for password storage).
29 * iterations :: The iteration count. This provides the ability to tune the
30 * algorithm. It is better to use the highest count possible for
31 * the maximum resistance to brute-force attacks.
32 * length :: The desired length of the derived key in octets.
33 * hash :: The hash algorithm used with HMAC for the PRF. May be a String
34 * representing the algorithm name, or an instance of
35 * OpenSSL::Digest.
36 */
37static VALUE
38kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
39{
40 VALUE pass, salt, opts, kwargs[4], str;
41 static ID kwargs_ids[4];
42 int iters, len;
43 const EVP_MD *md;
44
45 if (!kwargs_ids[0]) {
46 kwargs_ids[0] = rb_intern_const("salt");
47 kwargs_ids[1] = rb_intern_const("iterations");
48 kwargs_ids[2] = rb_intern_const("length");
49 kwargs_ids[3] = rb_intern_const("hash");
50 }
51 rb_scan_args(argc, argv, "1:", &pass, &opts);
52 rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
53
54 StringValue(pass);
55 salt = StringValue(kwargs[0]);
56 iters = NUM2INT(kwargs[1]);
57 len = NUM2INT(kwargs[2]);
58 md = ossl_evp_get_digestbyname(kwargs[3]);
59
60 str = rb_str_new(0, len);
61 if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
62 (unsigned char *)RSTRING_PTR(salt),
63 RSTRING_LENINT(salt), iters, md, len,
64 (unsigned char *)RSTRING_PTR(str)))
65 ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC");
66
67 return str;
68}
69
70#if defined(HAVE_EVP_PBE_SCRYPT)
71/*
72 * call-seq:
73 * KDF.scrypt(pass, salt:, N:, r:, p:, length:) -> aString
74 *
75 * Derives a key from _pass_ using given parameters with the scrypt
76 * password-based key derivation function. The result can be used for password
77 * storage.
78 *
79 * scrypt is designed to be memory-hard and more secure against brute-force
80 * attacks using custom hardwares than alternative KDFs such as PBKDF2 or
81 * bcrypt.
82 *
83 * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914
84 * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states
85 * that using values r=8 and p=1 appears to yield good results.
86 *
87 * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information.
88 *
89 * === Parameters
90 * pass :: Passphrase.
91 * salt :: Salt.
92 * N :: CPU/memory cost parameter. This must be a power of 2.
93 * r :: Block size parameter.
94 * p :: Parallelization parameter.
95 * length :: Length in octets of the derived key.
96 *
97 * === Example
98 * pass = "password"
99 * salt = SecureRandom.random_bytes(16)
100 * dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32)
101 * p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T"
102 */
103static VALUE
104kdf_scrypt(int argc, VALUE *argv, VALUE self)
105{
106 VALUE pass, salt, opts, kwargs[5], str;
107 static ID kwargs_ids[5];
108 size_t len;
109 uint64_t N, r, p, maxmem;
110
111 if (!kwargs_ids[0]) {
112 kwargs_ids[0] = rb_intern_const("salt");
113 kwargs_ids[1] = rb_intern_const("N");
114 kwargs_ids[2] = rb_intern_const("r");
115 kwargs_ids[3] = rb_intern_const("p");
116 kwargs_ids[4] = rb_intern_const("length");
117 }
118 rb_scan_args(argc, argv, "1:", &pass, &opts);
119 rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs);
120
121 StringValue(pass);
122 salt = StringValue(kwargs[0]);
123 N = NUM2UINT64T(kwargs[1]);
124 r = NUM2UINT64T(kwargs[2]);
125 p = NUM2UINT64T(kwargs[3]);
126 len = NUM2LONG(kwargs[4]);
127 /*
128 * OpenSSL uses 32MB by default (if zero is specified), which is too small.
129 * Let's not limit memory consumption but just let malloc() fail inside
130 * OpenSSL. The amount is controllable by other parameters.
131 */
132 maxmem = SIZE_MAX;
133
134 str = rb_str_new(0, len);
135 if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass),
136 (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt),
137 N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len))
138 ossl_raise(eKDF, "EVP_PBE_scrypt");
139
140 return str;
141}
142#endif
143
144#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
145/*
146 * call-seq:
147 * KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
148 *
149 * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in
150 * {RFC 5869}[https://tools.ietf.org/html/rfc5869].
151 *
152 * New in OpenSSL 1.1.0.
153 *
154 * === Parameters
155 * _ikm_::
156 * The input keying material.
157 * _salt_::
158 * The salt.
159 * _info_::
160 * The context and application specific information.
161 * _length_::
162 * The output length in octets. Must be <= <tt>255 * HashLen</tt>, where
163 * HashLen is the length of the hash function output in octets.
164 * _hash_::
165 * The hash function.
166 */
167static VALUE
168kdf_hkdf(int argc, VALUE *argv, VALUE self)
169{
170 VALUE ikm, salt, info, opts, kwargs[4], str;
171 static ID kwargs_ids[4];
172 int saltlen, ikmlen, infolen;
173 size_t len;
174 const EVP_MD *md;
175 EVP_PKEY_CTX *pctx;
176
177 if (!kwargs_ids[0]) {
178 kwargs_ids[0] = rb_intern_const("salt");
179 kwargs_ids[1] = rb_intern_const("info");
180 kwargs_ids[2] = rb_intern_const("length");
181 kwargs_ids[3] = rb_intern_const("hash");
182 }
183 rb_scan_args(argc, argv, "1:", &ikm, &opts);
184 rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
185
186 StringValue(ikm);
187 ikmlen = RSTRING_LENINT(ikm);
188 salt = StringValue(kwargs[0]);
189 saltlen = RSTRING_LENINT(salt);
190 info = StringValue(kwargs[1]);
191 infolen = RSTRING_LENINT(info);
192 len = (size_t)NUM2LONG(kwargs[2]);
193 if (len > LONG_MAX)
194 rb_raise(rb_eArgError, "length must be non-negative");
195 md = ossl_evp_get_digestbyname(kwargs[3]);
196
197 str = rb_str_new(NULL, (long)len);
198 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
199 if (!pctx)
200 ossl_raise(eKDF, "EVP_PKEY_CTX_new_id");
201 if (EVP_PKEY_derive_init(pctx) <= 0) {
202 EVP_PKEY_CTX_free(pctx);
203 ossl_raise(eKDF, "EVP_PKEY_derive_init");
204 }
205 if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) {
206 EVP_PKEY_CTX_free(pctx);
207 ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md");
208 }
209 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt),
210 saltlen) <= 0) {
211 EVP_PKEY_CTX_free(pctx);
212 ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt");
213 }
214 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm),
215 ikmlen) <= 0) {
216 EVP_PKEY_CTX_free(pctx);
217 ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key");
218 }
219 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info),
220 infolen) <= 0) {
221 EVP_PKEY_CTX_free(pctx);
222 ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info");
223 }
224 if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) {
225 EVP_PKEY_CTX_free(pctx);
226 ossl_raise(eKDF, "EVP_PKEY_derive");
227 }
228 rb_str_set_len(str, (long)len);
229 EVP_PKEY_CTX_free(pctx);
230
231 return str;
232}
233#endif
234
235void
237{
238#if 0
239 mOSSL = rb_define_module("OpenSSL");
241#endif
242
243 /*
244 * Document-module: OpenSSL::KDF
245 *
246 * Provides functionality of various KDFs (key derivation function).
247 *
248 * KDF is typically used for securely deriving arbitrary length symmetric
249 * keys to be used with an OpenSSL::Cipher from passwords. Another use case
250 * is for storing passwords: Due to the ability to tweak the effort of
251 * computation by increasing the iteration count, computation can be slowed
252 * down artificially in order to render possible attacks infeasible.
253 *
254 * Currently, OpenSSL::KDF provides implementations for the following KDF:
255 *
256 * * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in
257 * combination with HMAC
258 * * scrypt
259 * * HKDF
260 *
261 * == Examples
262 * === Generating a 128 bit key for a Cipher (e.g. AES)
263 * pass = "secret"
264 * salt = OpenSSL::Random.random_bytes(16)
265 * iter = 20_000
266 * key_len = 16
267 * key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
268 * length: key_len, hash: "sha1")
269 *
270 * === Storing Passwords
271 * pass = "secret"
272 * # store this with the generated value
273 * salt = OpenSSL::Random.random_bytes(16)
274 * iter = 20_000
275 * hash = OpenSSL::Digest::SHA256.new
276 * len = hash.digest_length
277 * # the final value to be stored
278 * value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
279 * length: len, hash: hash)
280 *
281 * == Important Note on Checking Passwords
282 * When comparing passwords provided by the user with previously stored
283 * values, a common mistake made is comparing the two values using "==".
284 * Typically, "==" short-circuits on evaluation, and is therefore
285 * vulnerable to timing attacks. The proper way is to use a method that
286 * always takes the same amount of time when comparing two values, thus
287 * not leaking any information to potential attackers. To compare two
288 * values, the following could be used:
289 *
290 * def eql_time_cmp(a, b)
291 * unless a.length == b.length
292 * return false
293 * end
294 * cmp = b.bytes
295 * result = 0
296 * a.bytes.each_with_index {|c,i|
297 * result |= c ^ cmp[i]
298 * }
299 * result == 0
300 * end
301 *
302 * Please note that the premature return in case of differing lengths
303 * typically does not leak valuable information - when using PBKDF2, the
304 * length of the values to be compared is of fixed size.
305 */
306 mKDF = rb_define_module_under(mOSSL, "KDF");
307 /*
308 * Generic exception class raised if an error occurs in OpenSSL::KDF module.
309 */
310 eKDF = rb_define_class_under(mKDF, "KDFError", eOSSLError);
311
312 rb_define_module_function(mKDF, "pbkdf2_hmac", kdf_pbkdf2_hmac, -1);
313#if defined(HAVE_EVP_PBE_SCRYPT)
314 rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1);
315#endif
316#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
317 rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1);
318#endif
319}
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
VALUE rb_define_class_under(VALUE, const char *, VALUE)
Defines a class under the namespace of outer.
Definition: class.c:711
VALUE rb_define_module(const char *)
Definition: class.c:785
VALUE rb_define_module_under(VALUE, const char *)
Definition: class.c:810
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *)
Definition: class.c:1904
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
VALUE rb_eStandardError
Definition: error.c:921
VALUE rb_eArgError
Definition: error.c:925
#define N
Definition: lgamma_r.c:20
VALUE mOSSL
Definition: ossl.c:231
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:293
VALUE eOSSLError
Definition: ossl.c:236
const EVP_MD * ossl_evp_get_digestbyname(VALUE obj)
Definition: ossl_digest.c:45
void Init_ossl_kdf(void)
Definition: ossl_kdf.c:236
#define NULL
use StringValue() instead")))
#define RSTRING_LEN(str)
#define SIZE_MAX
#define RSTRING_PTR(str)
#define rb_str_new(str, len)
#define LONG_MAX
#define RSTRING_LENINT(str)
#define rb_intern_const(str)
void rb_str_set_len(VALUE, long)
Definition: string.c:2692
unsigned long VALUE
__inline__ const void *__restrict__ size_t len
__uint64_t uint64_t
void rb_define_module_function(VALUE, const char *, VALUE(*)(), int)
#define NUM2INT(x)
#define rb_scan_args(argc, argvp, fmt,...)
long unsigned int size_t
const VALUE * argv
unsigned long ID
#define NUM2LONG(x)