module OpenSSL::KDF
Bietet Funktionalität für verschiedene KDFs (Key Derivation Function).
KDF wird typischerweise zum sicheren Ableiten von symmetrischen Schlüsseln beliebiger Länge, die mit einem OpenSSL::Cipher verwendet werden sollen, aus Passwörtern verwendet. Ein weiterer Anwendungsfall ist die Speicherung von Passwörtern: Aufgrund der Möglichkeit, den Rechenaufwand durch Erhöhung der Iterationsanzahl anzupassen, kann die Berechnung künstlich verlangsamt werden, um mögliche Angriffe unmöglich zu machen.
Derzeit bietet OpenSSL::KDF Implementierungen für die folgenden KDFs an:
-
PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in Kombination mit
HMAC -
scrypt
-
HKDF
Beispiele
Generieren eines 128-Bit-Schlüssels für einen Cipher (z.B. 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")
Passwörter speichern
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)
Wichtiger Hinweis zum Überprüfen von Passwörtern
Beim Vergleichen von vom Benutzer bereitgestellten Passwörtern mit zuvor gespeicherten Werten wird häufig der Fehler gemacht, die beiden Werte mit „==“ zu vergleichen. Typischerweise wird „==“ beim Auswerten frühzeitig abgebrochen und ist daher anfällig für Timing-Angriffe. Der richtige Weg ist die Verwendung einer Methode, die beim Vergleichen zweier Werte immer die gleiche Zeit benötigt und somit keine Informationen an potenzielle Angreifer durchsickern lässt. Verwenden Sie dazu OpenSSL.fixed_length_secure_compare.
Öffentliche Klassenmethoden
Source
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;
}
HMAC-based Extract-and-Expand Key Derivation Function (HKDF) wie in RFC 5869 spezifiziert.
Neu in OpenSSL 1.1.0.
Parameter
- ikm
-
Das Input Keying Material.
- salt
-
Das Salt.
- info
-
Der Kontext und anwendungsspezifische Informationen.
- Länge
-
Die Ausgabelänge in Oktetten. Muss <=
255 * HashLensein, wobei HashLen die Länge der Hash-Funktionsausgabe in Oktetten ist. - hash
-
Die Hash-Funktion.
Beispiel
# 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"
Source
static VALUE
kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
{
VALUE pass, salt, opts, kwargs[4], str, md_holder;
static ID kwargs_ids[4];
int 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);
str = rb_str_new(0, len);
if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
(unsigned char *)RSTRING_PTR(salt),
RSTRING_LENINT(salt), iters, md, len,
(unsigned char *)RSTRING_PTR(str)))
ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC");
return str;
}
PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in Kombination mit HMAC. Nimmt pass, salt und iterations entgegen und leitet dann einen Schlüssel von length Bytes ab.
Weitere Informationen zu PBKDF2 finden Sie in RFC 2898 Abschnitt 5.2 (www.rfc-editor.org/rfc/rfc2898#section-5.2).
Parameter
- pass
-
Das Passwort.
- salt
-
Das Salt. Salts verhindern Angriffe, die auf Wörterbüchern mit gängigen Passwörtern und Rainbow Tables basieren. Es ist ein öffentlicher Wert, der sicher zusammen mit dem Passwort gespeichert werden kann (z. B. wenn der abgeleitete Wert zur Passwortspeicherung verwendet wird).
- iterations
-
Die Anzahl der Iterationen. Dies ermöglicht die Anpassung des Algorithmus. Es ist besser, die höchstmögliche Anzahl für maximale Resistenz gegen Brute-Force-Angriffe zu verwenden.
- Länge
-
Die gewünschte Länge des abgeleiteten Schlüssels in Oktetten.
- hash
-
Der mit
HMACverwendete Hash-Algorithmus für die PRF. Kann einStringsein, der den Algorithmusnamen darstellt, oder eine Instanz vonOpenSSL::Digest.
Source
static VALUE
kdf_scrypt(int argc, VALUE *argv, VALUE self)
{
VALUE pass, salt, opts, kwargs[5], str;
static ID kwargs_ids[5];
size_t len;
uint64_t N, r, p, maxmem;
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]);
/*
* OpenSSL uses 32MB by default (if zero is specified), which is too small.
* Let's not limit memory consumption but just let malloc() fail inside
* OpenSSL. The amount is controllable by other parameters.
*/
maxmem = SIZE_MAX;
str = rb_str_new(0, len);
if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass),
(unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt),
N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len))
ossl_raise(eKDF, "EVP_PBE_scrypt");
return str;
}
Leitet einen Schlüssel vom pass unter Verwendung der angegebenen Parameter mit der scrypt-Passwort-basierten Schlüsselableitungsfunktion ab. Das Ergebnis kann zur Passwortspeicherung verwendet werden.
scrypt ist darauf ausgelegt, speicherintensiv und sicherer gegen Brute-Force-Angriffe mit benutzerdefinierter Hardware zu sein als alternative KDFs wie PBKDF2 oder bcrypt.
Die Schlüsselwortargumente N, r und p können zur Anpassung von scrypt verwendet werden. RFC 7914 (veröffentlicht im August 2016, www.rfc-editor.org/rfc/rfc7914#section-2) besagt, dass die Verwendung der Werte r=8 und p=1 gute Ergebnisse liefert.
Weitere Informationen finden Sie in RFC 7914 (www.rfc-editor.org/rfc/rfc7914).
Parameter
- pass
-
Passphrase.
- salt
-
Salt.
- N
-
CPU-/Speicherkostenparameter. Muss eine Zweierpotenz sein.
- r
-
Blockgrößenparameter.
- p
-
Parallelisierungsparameter.
- Länge
-
Länge in Oktetten des abgeleiteten Schlüssels.
Beispiel
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"