Module: OpenSSL::KDF
| Relationships & Source Files | |
| Namespace Children | |
|
Exceptions:
| |
| Defined in: | ext/openssl/ossl_kdf.c |
Overview
Provides functionality of various KDFs (key derivation function).
KDF is typically used for securely deriving arbitrary length symmetric
keys to be used with an Cipher from passwords. Another use case
is for storing passwords: Due to the ability to tweak the effort of
computation by increasing the iteration count, computation can be slowed
down artificially in order to render possible attacks infeasible.
Currently, KDF provides implementations for the following KDF:
- PKCS
#5PBKDF2 (Password-Based Key Derivation Function 2) in combination with HMAC - scrypt
- HKDF
Examples
Generating a 128 bit key for a Cipher (e.g. AES)
pass = "secret" salt = OpenSSL::Random.random_bytes(16) iter = 20_000 key_len = 16 key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, length: key_len, hash: "sha1")
Storing Passwords
pass = "secret"
store this with the generated value
salt = OpenSSL::Random.random_bytes(16) iter = 20_000 hash = OpenSSL::Digest.new('SHA256') len = hash.digest_length
the final value to be stored
value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, length: len, hash: hash)
Important Note on Checking Passwords
When comparing passwords provided by the user with previously stored values, a common mistake made is comparing the two values using "==". Typically, "==" short-circuits on evaluation, and is therefore vulnerable to timing attacks. The proper way is to use a method that always takes the same amount of time when comparing two values, thus not leaking any information to potential attackers. To do this, use fixed_length_secure_compare.
Class Method Summary
-
.hkdf(ikm, salt:, info:, length:, hash:) ⇒ String
mod_func
HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in RFC 5869.
-
.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) ⇒ String
mod_func
PKCS
#5PBKDF2 (Password-Based Key Derivation Function 2) in combination withHMAC. -
.scrypt(pass, salt:, N:, r:, p:, length:) ⇒ String
mod_func
Derives a key from pass using given parameters with the scrypt password-based key derivation function.
Class Method Details
.hkdf(ikm, salt:, info:, length:, hash:) ⇒ String (mod_func)
HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in RFC 5869.
New in ::OpenSSL 1.1.0.
Parameters
- ikm
The input keying material.
- salt
The salt.
- info
The context and application specific information.
- length
The output length in octets. Must be <= 255 * HashLen, where HashLen is the length of the hash function output in octets.
- hash
The hash function.
Example
The values from https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1
ikm = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*") salt = ["000102030405060708090a0b0c"].pack("H*") info = ["f0f1f2f3f4f5f6f7f8f9"].pack("H*") p OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: 42, hash: "SHA256").unpack1("H*")
=> "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
# File 'ext/openssl/ossl_kdf.c', line 240
static VALUE
kdf_hkdf(int argc, VALUE *argv, VALUE self)
{
VALUE ikm, salt, info, opts, kwargs[4], str, md_holder;
static ID kwargs_ids[4];
int saltlen, ikmlen, infolen;
size_t len;
const EVP_MD *md;
EVP_PKEY_CTX *pctx;
if (!kwargs_ids[0]) {
kwargs_ids[0] = rb_intern_const("salt");
kwargs_ids[1] = rb_intern_const("info");
kwargs_ids[2] = rb_intern_const("length");
kwargs_ids[3] = rb_intern_const("hash");
}
rb_scan_args(argc, argv, "1:", &ikm, &opts);
rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
StringValue(ikm);
ikmlen = RSTRING_LENINT(ikm);
salt = StringValue(kwargs[0]);
saltlen = RSTRING_LENINT(salt);
info = StringValue(kwargs[1]);
infolen = RSTRING_LENINT(info);
len = (size_t)NUM2LONG(kwargs[2]);
if (len > LONG_MAX)
rb_raise(rb_eArgError, "length must be non-negative");
md = ossl_evp_md_fetch(kwargs[3], &md_holder);
str = rb_str_new(NULL, (long)len);
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
if (!pctx)
ossl_raise(eKDF, "EVP_PKEY_CTX_new_id");
if (EVP_PKEY_derive_init(pctx) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_derive_init");
}
if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md");
}
if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt),
saltlen) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt");
}
if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm),
ikmlen) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key");
}
if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info),
infolen) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info");
}
if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) {
EVP_PKEY_CTX_free(pctx);
ossl_raise(eKDF, "EVP_PKEY_derive");
}
rb_str_set_len(str, (long)len);
EVP_PKEY_CTX_free(pctx);
return str;
}
.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) ⇒ String (mod_func)
PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination
with HMAC. Takes pass, salt and iterations, and then derives a key
of length bytes.
For more information about PBKDF2, see RFC 2898 Section 5.2 (https://www.rfc-editor.org/rfc/rfc2898#section-5.2).
Parameters
- pass
The password.
- salt
The salt. Salts prevent attacks based on dictionaries of common passwords and attacks based on rainbow tables. It is a public value that can be safely stored along with the password (e.g. if the derived value is used for password storage).
- iterations
The iteration count. This provides the ability to tune the algorithm. It is better to use the highest count possible for the maximum resistance to brute-force attacks.
- length
The desired length of the derived key in octets.
- hash
The hash algorithm used with
HMACfor the PRF. May be a String representing the algorithm name, or an instance of OpenSSL::Digest.
# File 'ext/openssl/ossl_kdf.c', line 56
static VALUE
kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
{
VALUE pass, salt, opts, kwargs[4], str, md_holder, pass_tmp, salt_tmp;
static ID kwargs_ids[4];
int passlen, saltlen, iters, len;
const EVP_MD *md;
if (!kwargs_ids[0]) {
kwargs_ids[0] = rb_intern_const("salt");
kwargs_ids[1] = rb_intern_const("iterations");
kwargs_ids[2] = rb_intern_const("length");
kwargs_ids[3] = rb_intern_const("hash");
}
rb_scan_args(argc, argv, "1:", &pass, &opts);
rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
StringValue(pass);
salt = StringValue(kwargs[0]);
iters = NUM2INT(kwargs[1]);
len = NUM2INT(kwargs[2]);
md = ossl_evp_md_fetch(kwargs[3], &md_holder);
passlen = RSTRING_LENINT(pass);
saltlen = RSTRING_LENINT(salt);
str = rb_str_new(NULL, len);
struct pbkdf2_hmac_args args = {
.pass = ALLOCV(pass_tmp, passlen),
.passlen = passlen,
.salt = ALLOCV(salt_tmp, saltlen),
.saltlen = saltlen,
.iters = iters,
.md = md,
.len = len,
.out = (unsigned char *)RSTRING_PTR(str),
};
memcpy(args.pass, RSTRING_PTR(pass), passlen);
memcpy(args.salt, RSTRING_PTR(salt), saltlen);
if (!rb_thread_call_without_gvl(pbkdf2_hmac_nogvl, &args, NULL, NULL))
ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC");
OPENSSL_cleanse(args.pass, passlen);
ALLOCV_END(pass_tmp);
ALLOCV_END(salt_tmp);
return str;
}
.scrypt(pass, salt:, N:, r:, p:, length:) ⇒ String (mod_func)
Derives a key from pass using given parameters with the scrypt password-based key derivation function. The result can be used for password storage.
scrypt is designed to be memory-hard and more secure against brute-force attacks using custom hardwares than alternative KDFs such as PBKDF2 or bcrypt.
The keyword arguments N, r and p can be used to tune scrypt. RFC 7914 (published on 2016-08, https://www.rfc-editor.org/rfc/rfc7914#section-2) states that using values r=8 and p=1 appears to yield good results.
See RFC 7914 (https://www.rfc-editor.org/rfc/rfc7914) for more information.
Parameters
- pass
Passphrase.
- salt
Salt.
- N
CPU/memory cost parameter. This must be a power of 2.
- r
Block size parameter.
- p
Parallelization parameter.
- length
Length in octets of the derived key.
Example
pass = "password" salt = SecureRandom.random_bytes(16) dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32) p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T"
# File 'ext/openssl/ossl_kdf.c', line 160
static VALUE
kdf_scrypt(int argc, VALUE *argv, VALUE self)
{
VALUE pass, salt, opts, kwargs[5], str, pass_tmp, salt_tmp;
static ID kwargs_ids[5];
size_t passlen, saltlen;
long len;
uint64_t N, r, p;
if (!kwargs_ids[0]) {
kwargs_ids[0] = rb_intern_const("salt");
kwargs_ids[1] = rb_intern_const("N");
kwargs_ids[2] = rb_intern_const("r");
kwargs_ids[3] = rb_intern_const("p");
kwargs_ids[4] = rb_intern_const("length");
}
rb_scan_args(argc, argv, "1:", &pass, &opts);
rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs);
StringValue(pass);
salt = StringValue(kwargs[0]);
N = NUM2UINT64T(kwargs[1]);
r = NUM2UINT64T(kwargs[2]);
p = NUM2UINT64T(kwargs[3]);
len = NUM2LONG(kwargs[4]);
passlen = RSTRING_LEN(pass);
saltlen = RSTRING_LEN(salt);
str = rb_str_new(NULL, len);
struct scrypt_args args = {
.pass = ALLOCV(pass_tmp, passlen),
.passlen = passlen,
.salt = ALLOCV(salt_tmp, saltlen),
.saltlen = saltlen,
.N = N,
.r = r,
.p = p,
.len = len,
.out = (unsigned char *)RSTRING_PTR(str),
};
memcpy(args.pass, RSTRING_PTR(pass), passlen);
memcpy(args.salt, RSTRING_PTR(salt), saltlen);
if (!rb_thread_call_without_gvl(scrypt_nogvl, &args, NULL, NULL))
ossl_raise(eKDF, "EVP_PBE_scrypt");
OPENSSL_cleanse(args.pass, passlen);
ALLOCV_END(pass_tmp);
ALLOCV_END(salt_tmp);
return str;
}