Skip to content

Commit

Permalink
PBKDF2 updates to conform to SP800-132
Browse files Browse the repository at this point in the history
The existing code used PKCS5 specifications.
SP800-132 adds the following additional constraints for:
  - the range of the key length.
  - the minimum iteration count (1000 recommended).
  - salt length (at least 128 bits).
These additional constraints may cause errors (in scrypt, and
some PKCS5 related test vectors). To disable the new
constraints use the new ctrl string "pkcs5".
For backwards compatability, the checks are only enabled by
default for fips mode.

Reviewed-by: Matt Caswell <[email protected]>
(Merged from openssl#8868)
  • Loading branch information
slontis committed Jun 11, 2019
1 parent 83b4a24 commit f0efeea
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 67 deletions.
11 changes: 10 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@

Changes between 1.1.1 and 3.0.0 [xx XXX xxxx]

*) Change PBKDF2 to conform to SP800-132 instead of the older PKCS5 RFC2898.
This checks that the salt length is at least 128 bits, the derived key
length is at least 112 bits, and that the iteration count is at least 1000.
For backwards compatibility these checks are disabled by default in the
default provider, but are enabled by default in the fips provider.
To enable or disable these checks use the control
EVP_KDF_CTRL_SET_PBKDF2_PKCS5_MODE.
[Shane Lontis]

*) Default cipher lists/suites are now avaialble via a function, the
#defines are deprecated.
[Todd Short]
Expand All @@ -30,7 +39,7 @@
*) Added command 'openssl kdf' that uses the EVP_KDF API.
[Shane Lontis]

*) Added command 'openssl mac' that uses the EVP_MAC API.
*) Added command 'openssl mac' that uses the EVP_MAC API.
[Shane Lontis]

*) Added OPENSSL_info() to get diverse built-in OpenSSL data, such
Expand Down
5 changes: 5 additions & 0 deletions crypto/err/openssl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ KDF_F_KDF_HKDF_DERIVE:113:kdf_hkdf_derive
KDF_F_KDF_HKDF_NEW:114:kdf_hkdf_new
KDF_F_KDF_HKDF_SIZE:115:kdf_hkdf_size
KDF_F_KDF_MD2CTRL:116:kdf_md2ctrl
KDF_F_KDF_PBKDF2_CTRL:140:kdf_pbkdf2_ctrl
KDF_F_KDF_PBKDF2_CTRL_STR:117:kdf_pbkdf2_ctrl_str
KDF_F_KDF_PBKDF2_DERIVE:118:kdf_pbkdf2_derive
KDF_F_KDF_PBKDF2_NEW:119:kdf_pbkdf2_new
Expand All @@ -910,6 +911,7 @@ KDF_F_KDF_SSHKDF_NEW:133:kdf_sshkdf_new
KDF_F_KDF_TLS1_PRF_CTRL_STR:125:kdf_tls1_prf_ctrl_str
KDF_F_KDF_TLS1_PRF_DERIVE:126:kdf_tls1_prf_derive
KDF_F_KDF_TLS1_PRF_NEW:127:kdf_tls1_prf_new
KDF_F_PBKDF2_DERIVE:141:pbkdf2_derive
KDF_F_PBKDF2_SET_MEMBUF:128:pbkdf2_set_membuf
KDF_F_PKEY_HKDF_CTRL_STR:103:pkey_hkdf_ctrl_str
KDF_F_PKEY_HKDF_DERIVE:102:pkey_hkdf_derive
Expand Down Expand Up @@ -2458,7 +2460,10 @@ EVP_R_WRONG_FINAL_BLOCK_LENGTH:109:wrong final block length
EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE:191:xts data unit is too large
EVP_R_XTS_DUPLICATED_KEYS:192:xts duplicated keys
KDF_R_INVALID_DIGEST:100:invalid digest
KDF_R_INVALID_ITERATION_COUNT:119:invalid iteration count
KDF_R_INVALID_KEY_LEN:120:invalid key len
KDF_R_INVALID_MAC_TYPE:116:invalid mac type
KDF_R_INVALID_SALT_LEN:121:invalid salt len
KDF_R_MISSING_ITERATION_COUNT:109:missing iteration count
KDF_R_MISSING_KEY:104:missing key
KDF_R_MISSING_MESSAGE_DIGEST:105:missing message digest
Expand Down
3 changes: 2 additions & 1 deletion crypto/evp/p5_crpt2.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 1999-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand Down Expand Up @@ -40,6 +40,7 @@ int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
if (kctx == NULL)
return 0;
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_PASS, pass, (size_t)passlen) != 1
|| EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_PBKDF2_PKCS5_MODE, 1) != 1
|| EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT,
salt, (size_t)saltlen) != 1
|| EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_ITER, iter) != 1
Expand Down
6 changes: 6 additions & 0 deletions crypto/kdf/kdf_err.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static const ERR_STRING_DATA KDF_str_functs[] = {
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_HKDF_NEW, 0), "kdf_hkdf_new"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_HKDF_SIZE, 0), "kdf_hkdf_size"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_MD2CTRL, 0), "kdf_md2ctrl"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_PBKDF2_CTRL, 0), "kdf_pbkdf2_ctrl"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_PBKDF2_CTRL_STR, 0),
"kdf_pbkdf2_ctrl_str"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_PBKDF2_DERIVE, 0), "kdf_pbkdf2_derive"},
Expand All @@ -41,6 +42,7 @@ static const ERR_STRING_DATA KDF_str_functs[] = {
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_TLS1_PRF_DERIVE, 0),
"kdf_tls1_prf_derive"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_TLS1_PRF_NEW, 0), "kdf_tls1_prf_new"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_PBKDF2_DERIVE, 0), "pbkdf2_derive"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_PBKDF2_SET_MEMBUF, 0), "pbkdf2_set_membuf"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_PKEY_HKDF_CTRL_STR, 0), "pkey_hkdf_ctrl_str"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_PKEY_HKDF_DERIVE, 0), "pkey_hkdf_derive"},
Expand Down Expand Up @@ -71,7 +73,11 @@ static const ERR_STRING_DATA KDF_str_functs[] = {

static const ERR_STRING_DATA KDF_str_reasons[] = {
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_DIGEST), "invalid digest"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_ITERATION_COUNT),
"invalid iteration count"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_KEY_LEN), "invalid key len"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_MAC_TYPE), "invalid mac type"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_SALT_LEN), "invalid salt len"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_ITERATION_COUNT),
"missing iteration count"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_KEY), "missing key"},
Expand Down
96 changes: 78 additions & 18 deletions crypto/kdf/pbkdf2.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
* Copyright 2018-2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
Expand All @@ -17,12 +17,27 @@
#include "internal/evp_int.h"
#include "kdf_local.h"

/* Constants specified in SP800-132 */
#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112
#define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF
#define KDF_PBKDF2_MIN_ITERATIONS 1000
#define KDF_PBKDF2_MIN_SALT_LEN (128 / 8)
/*
* For backwards compatibility reasons,
* Extra checks are done by default in fips mode only.
*/
#ifdef FIPS_MODE
# define KDF_PBKDF2_DEFAULT_CHECKS 1
#else
# define KDF_PBKDF2_DEFAULT_CHECKS 0
#endif /* FIPS_MODE */

static void kdf_pbkdf2_reset(EVP_KDF_IMPL *impl);
static void kdf_pbkdf2_init(EVP_KDF_IMPL *impl);
static int pkcs5_pbkdf2_alg(const char *pass, size_t passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest, unsigned char *key,
size_t keylen);
static int pbkdf2_derive(const char *pass, size_t passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest, unsigned char *key,
size_t keylen, int extra_checks);

struct evp_kdf_impl_st {
unsigned char *pass;
Expand All @@ -31,6 +46,7 @@ struct evp_kdf_impl_st {
size_t salt_len;
int iter;
const EVP_MD *md;
int lower_bound_checks;
};

static EVP_KDF_IMPL *kdf_pbkdf2_new(void)
Expand Down Expand Up @@ -64,6 +80,7 @@ static void kdf_pbkdf2_init(EVP_KDF_IMPL *impl)
{
impl->iter = PKCS5_DEFAULT_ITER;
impl->md = EVP_sha1();
impl->lower_bound_checks = KDF_PBKDF2_DEFAULT_CHECKS;
}

static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen,
Expand Down Expand Up @@ -91,12 +108,16 @@ static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen,

static int kdf_pbkdf2_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args)
{
int iter;
int iter, pkcs5, min_iter;
const unsigned char *p;
size_t len;
const EVP_MD *md;

switch (cmd) {
case EVP_KDF_CTRL_SET_PBKDF2_PKCS5_MODE:
pkcs5 = va_arg(args, int);
impl->lower_bound_checks = (pkcs5 == 0) ? 1 : 0;
return 1;
case EVP_KDF_CTRL_SET_PASS:
p = va_arg(args, const unsigned char *);
len = va_arg(args, size_t);
Expand All @@ -105,20 +126,28 @@ static int kdf_pbkdf2_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args)
case EVP_KDF_CTRL_SET_SALT:
p = va_arg(args, const unsigned char *);
len = va_arg(args, size_t);
if (impl->lower_bound_checks != 0 && len < KDF_PBKDF2_MIN_SALT_LEN) {
KDFerr(KDF_F_KDF_PBKDF2_CTRL, KDF_R_INVALID_SALT_LEN);
return 0;
}
return pbkdf2_set_membuf(&impl->salt, &impl->salt_len, p, len);

case EVP_KDF_CTRL_SET_ITER:
iter = va_arg(args, int);
if (iter < 1)
min_iter = impl->lower_bound_checks != 0 ? KDF_PBKDF2_MIN_ITERATIONS : 1;
if (iter < min_iter) {
KDFerr(KDF_F_KDF_PBKDF2_CTRL, KDF_R_INVALID_ITERATION_COUNT);
return 0;

}
impl->iter = iter;
return 1;

case EVP_KDF_CTRL_SET_MD:
md = va_arg(args, const EVP_MD *);
if (md == NULL)
if (md == NULL) {
KDFerr(KDF_F_KDF_PBKDF2_CTRL, KDF_R_VALUE_MISSING);
return 0;
}

impl->md = md;
return 1;
Expand Down Expand Up @@ -159,6 +188,9 @@ static int kdf_pbkdf2_ctrl_str(EVP_KDF_IMPL *impl, const char *type,
if (strcmp(type, "digest") == 0)
return kdf_md2ctrl(impl, kdf_pbkdf2_ctrl, EVP_KDF_CTRL_SET_MD, value);

if (strcmp(type, "pkcs5") == 0)
return kdf_str2ctrl(impl, kdf_pbkdf2_ctrl,
EVP_KDF_CTRL_SET_PBKDF2_PKCS5_MODE, value);
return -2;
}

Expand All @@ -175,9 +207,9 @@ static int kdf_pbkdf2_derive(EVP_KDF_IMPL *impl, unsigned char *key,
return 0;
}

return pkcs5_pbkdf2_alg((char *)impl->pass, impl->pass_len,
impl->salt, impl->salt_len, impl->iter,
impl->md, key, keylen);
return pbkdf2_derive((char *)impl->pass, impl->pass_len,
impl->salt, impl->salt_len, impl->iter,
impl->md, key, keylen, impl->lower_bound_checks);
}

const EVP_KDF pbkdf2_kdf_meth = {
Expand All @@ -195,12 +227,16 @@ const EVP_KDF pbkdf2_kdf_meth = {
* This is an implementation of PKCS#5 v2.0 password based encryption key
* derivation function PBKDF2. SHA1 version verified against test vectors
* posted by Peter Gutmann to the PKCS-TNG mailing list.
*
* The constraints specified by SP800-132 have been added i.e.
* - Check the range of the key length.
* - Minimum iteration count of 1000.
* - Randomly-generated portion of the salt shall be at least 128 bits.
*/

static int pkcs5_pbkdf2_alg(const char *pass, size_t passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest, unsigned char *key,
size_t keylen)
static int pbkdf2_derive(const char *pass, size_t passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest, unsigned char *key,
size_t keylen, int lower_bound_checks)
{
int ret = 0;
unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
Expand All @@ -209,8 +245,32 @@ static int pkcs5_pbkdf2_alg(const char *pass, size_t passlen,
HMAC_CTX *hctx_tpl = NULL, *hctx = NULL;

mdlen = EVP_MD_size(digest);
if (mdlen < 0)
if (mdlen <= 0)
return 0;

/*
* This check should always be done because keylen / mdlen >= (2^32 - 1)
* results in an overflow of the loop counter 'i'.
*/
if ((keylen / mdlen) >= KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO) {
KDFerr(KDF_F_PBKDF2_DERIVE, KDF_R_INVALID_KEY_LEN);
return 0;
}

if (lower_bound_checks) {
if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) {
KDFerr(KDF_F_PBKDF2_DERIVE, KDF_R_INVALID_KEY_LEN);
return 0;
}
if (saltlen < KDF_PBKDF2_MIN_SALT_LEN) {
KDFerr(KDF_F_PBKDF2_DERIVE, KDF_R_INVALID_SALT_LEN);
return 0;
}
if (iter < KDF_PBKDF2_MIN_ITERATIONS) {
KDFerr(KDF_F_PBKDF2_DERIVE, KDF_R_INVALID_ITERATION_COUNT);
return 0;
}
}

hctx_tpl = HMAC_CTX_new();
if (hctx_tpl == NULL)
Expand Down
41 changes: 35 additions & 6 deletions doc/man7/EVP_KDF_PBKDF2.pod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Support for computing the B<PBKDF2> password-based KDF through the B<EVP_KDF>
API.

The EVP_KDF_PBKDF2 algorithm implements the PBKDF2 password-based key
derivation function, as described in RFC 2898; it derives a key from a password
derivation function, as described in SP800-132; it derives a key from a password
using a salt and iteration count.

=head2 Numeric identity
Expand All @@ -30,13 +30,38 @@ The supported controls are:

=item B<EVP_KDF_CTRL_SET_ITER>

This control has a default value of 2048.

=item B<EVP_KDF_CTRL_SET_MD>

These controls work as described in L<EVP_KDF_CTX(3)/CONTROLS>.

B<iter> is the iteration count and its value should be greater than or equal to
1. RFC 2898 suggests an iteration count of at least 1000. The default value is
2048. Any B<iter> less than 1 is treated as a single iteration.
=item B<EVP_KDF_CTRL_SET_PBKDF2_PKCS5_MODE>

This control expects one argument: C<int mode>

This control can be used to enable or disable SP800-132 compliance checks.

Setting the mode to 0 enables the compliance checks.

The checks performed are:

=over 4

=item - the iteration count is at least 1000.

=item - the salt length is at least 128 bits.

=item - the derived key length is at least 112 bits.

=back

The default provider uses a default mode of 1 for backwards compatibility,
and the fips provider uses a default mode of 0.

EVP_KDF_ctrl_str() type string: "pkcs5"

The value string is expected to be a decimal number 0 or 1.

=back

Expand All @@ -55,7 +80,7 @@ byte sequence.

=head1 CONFORMING TO

RFC 2898
SP800-132

=head1 SEE ALSO

Expand All @@ -66,9 +91,13 @@ L<EVP_KDF_ctrl(3)>,
L<EVP_KDF_derive(3)>,
L<EVP_KDF_CTX(3)/CONTROLS>

=head1 HISTORY

This functionality was added to OpenSSL 3.0.0.

=head1 COPYRIGHT

Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
Copyright 2018-2019 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
Expand Down
Loading

0 comments on commit f0efeea

Please sign in to comment.