Added app for EVP_KDF

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8762)
This commit is contained in:
Shane Lontis 2019-04-16 20:10:04 +10:00 committed by Richard Levitte
parent bacc308130
commit c54492ecf8
6 changed files with 413 additions and 5 deletions

View file

@ -2,7 +2,7 @@
qw(openssl.c qw(openssl.c
asn1pars.c ca.c ciphers.c cms.c crl.c crl2p7.c dgst.c dhparam.c asn1pars.c ca.c ciphers.c cms.c crl.c crl2p7.c dgst.c dhparam.c
dsa.c dsaparam.c ec.c ecparam.c enc.c engine.c errstr.c gendsa.c dsa.c dsaparam.c ec.c ecparam.c enc.c engine.c errstr.c gendsa.c
genpkey.c genrsa.c mac.c nseq.c ocsp.c passwd.c pkcs12.c pkcs7.c genpkey.c genrsa.c kdf.c mac.c nseq.c ocsp.c passwd.c pkcs12.c pkcs7.c
pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c rsa.c pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c rsa.c
rsautl.c s_client.c s_server.c s_time.c sess_id.c smime.c speed.c rsautl.c s_client.c s_server.c s_time.c sess_id.c smime.c speed.c
spkac.c srp.c ts.c verify.c version.c x509.c rehash.c storeutl.c spkac.c srp.c ts.c verify.c version.c x509.c rehash.c storeutl.c

158
apps/kdf.c Normal file
View file

@ -0,0 +1,158 @@
/*
* Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <string.h>
#include "apps.h"
#include "progs.h"
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
typedef enum OPTION_choice {
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
OPT_KDFOPT, OPT_BIN, OPT_KEYLEN, OPT_OUT
} OPTION_CHOICE;
const OPTIONS kdf_options[] = {
{OPT_HELP_STR, 1, '-', "Usage: %s [options] kdf_name\n"},
{OPT_HELP_STR, 1, '-', "kdf_name\t KDF algorithm.\n"},
{"help", OPT_HELP, '-', "Display this summary"},
{"kdfopt", OPT_KDFOPT, 's', "KDF algorithm control parameters in n:v form. "
"See 'Supported Controls' in the EVP_KDF_ docs"},
{"keylen", OPT_KEYLEN, 's', "The size of the output derived key"},
{"out", OPT_OUT, '>', "Output to filename rather than stdout"},
{"binary", OPT_BIN, '-', "Output in binary format (Default is hexadecimal "
"output)"},
{NULL}
};
static int kdf_ctrl_string(EVP_KDF_CTX *ctx, const char *value)
{
int rv;
char *stmp, *vtmp = NULL;
stmp = OPENSSL_strdup(value);
if (stmp == NULL)
return -1;
vtmp = strchr(stmp, ':');
if (vtmp != NULL) {
*vtmp = 0;
vtmp++;
}
rv = EVP_KDF_ctrl_str(ctx, stmp, vtmp);
OPENSSL_free(stmp);
return rv;
}
int kdf_main(int argc, char **argv)
{
int ret = 1, i, id, out_bin = 0;
OPTION_CHOICE o;
STACK_OF(OPENSSL_STRING) *opts = NULL;
char *prog, *hexout = NULL;
const char *outfile = NULL;
unsigned char *dkm_bytes = NULL;
size_t dkm_len = 0;
BIO *out = NULL;
EVP_KDF_CTX *ctx = NULL;
prog = opt_init(argc, argv, kdf_options);
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
default:
opthelp:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
goto err;
case OPT_HELP:
opt_help(kdf_options);
ret = 0;
goto err;
case OPT_BIN:
out_bin = 1;
break;
case OPT_KEYLEN:
dkm_len = (size_t)atoi(opt_arg());
break;
case OPT_OUT:
outfile = opt_arg();
break;
case OPT_KDFOPT:
if (opts == NULL)
opts = sk_OPENSSL_STRING_new_null();
if (opts == NULL || !sk_OPENSSL_STRING_push(opts, opt_arg()))
goto opthelp;
break;
}
}
argc = opt_num_rest();
argv = opt_rest();
if (argc != 1) {
BIO_printf(bio_err, "Invalid number of extra arguments\n");
goto opthelp;
}
id = OBJ_sn2nid(argv[0]);
if (id == NID_undef) {
BIO_printf(bio_err, "Invalid KDF name %s\n", argv[0]);
goto opthelp;
}
ctx = EVP_KDF_CTX_new_id(id);
if (ctx == NULL)
goto err;
if (opts != NULL) {
for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) {
char *opt = sk_OPENSSL_STRING_value(opts, i);
if (kdf_ctrl_string(ctx, opt) <= 0) {
BIO_printf(bio_err, "KDF parameter error '%s'\n", opt);
ERR_print_errors(bio_err);
goto err;
}
}
}
out = bio_open_default(outfile, 'w', out_bin ? FORMAT_BINARY : FORMAT_TEXT);
if (out == NULL)
goto err;
if (dkm_len <= 0) {
BIO_printf(bio_err, "Invalid derived key length.\n");
goto err;
}
dkm_bytes = app_malloc(dkm_len, "out buffer");
if (dkm_bytes == NULL)
goto err;
if (!EVP_KDF_derive(ctx, dkm_bytes, dkm_len)) {
BIO_printf(bio_err, "EVP_KDF_derive failed\n");
goto err;
}
if (out_bin) {
BIO_write(out, dkm_bytes, dkm_len);
} else {
hexout = OPENSSL_buf2hexstr(dkm_bytes, dkm_len);
BIO_printf(out, "%s\n\n", hexout);
}
ret = 0;
err:
if (ret != 0)
ERR_print_errors(bio_err);
OPENSSL_clear_free(dkm_bytes, dkm_len);
sk_OPENSSL_STRING_free(opts);
EVP_KDF_CTX_free(ctx);
BIO_free(out);
OPENSSL_free(hexout);
return ret;
}

167
doc/man1/kdf.pod Normal file
View file

@ -0,0 +1,167 @@
=pod
=head1 NAME
openssl-kdf,
kdf - perform Key Derivation Function operations
=head1 SYNOPSIS
B<openssl kdf>
[B<-help>]
[B<-kdfopt> I<nm:v>]
[B<-keylen> I<num>]
[B<-out> I<filename>]
[B<-binary>]
I<kdf_name>
=head1 DESCRIPTION
The key derivation functions generate a derived key from either a secret or
password.
=head1 OPTIONS
=over 4
=item B<-help>
Print a usage message.
=item B<-keylen> I<num>
The output size of the derived key. This field is required.
=item B<-out> I<filename>
Filename to output to, or standard output by default.
=item B<-binary>
Output the derived key in binary form. Uses hexadecimal text format if not specified.
=item B<-kdfopt> I<nm:v>
Passes options to the KDF algorithm.
A comprehensive list of controls can be found in the EVP_KDF_CTX implementation
documentation.
Common control strings used by EVP_KDF_ctrl_str() are:
=over 4
=item B<key:>I<string>
Specifies the secret key as an alphanumeric string (use if the key contains
printable characters only).
The string length must conform to any restrictions of the KDF algorithm.
A key must be specified for most KDF algorithms.
=item B<hexkey:>I<string>
Specifies the secret key in hexadecimal form (two hex digits per byte).
The key length must conform to any restrictions of the KDF algorithm.
A key must be specified for most KDF algorithms.
=item B<pass:>I<string>
Specifies the password as an alphanumeric string (use if the password contains
printable characters only).
The password must be specified for PBKDF2 and scrypt.
=item B<hexpass:>I<string>
Specifies the password in hexadecimal form (two hex digits per byte).
The password must be specified for PBKDF2 and scrypt.
=item B<digest:>I<string>
Specifies the name of a digest as an alphanumeric string.
To see the list of supported digests, use the command I<list -digest-commands>.
=back
=item I<kdf_name>
Specifies the name of a supported KDF algorithm which will be used.
The supported algorithms names are TLS1-PRF, HKDF, SSKDF, PBKDF2, SSHKDF and id-scrypt.
=back
=head1 EXAMPLES
Use TLS1-PRF to create a hex-encoded derived key from a secret key and seed:
openssl kdf -keylen 16 -kdfopt digest:SHA256 -kdfopt key:secret \
-kdfopt seed:seed TLS1-PRF
Use HKDF to create a hex-encoded derived key from a secret key, salt and info:
openssl kdf -keylen 10 -kdfopt digest:SHA256 -kdfopt key:secret \
-kdfopt salt:salt -kdfopt info:label HKDF
Use SSKDF with KMAC to create a hex-encoded derived key from a secret key, salt and info:
openssl kdf -keylen 64 -kdfopt mac:KMAC128 -kdfopt maclen:20 \
-kdfopt hexkey:b74a149a161545 -kdfopt hexinfo:348a37a2 \
-kdfopt hexsalt:3638271ccd68a2 SSKDF
Use SSKDF with HMAC to create a hex-encoded derived key from a secret key, salt and info:
openssl kdf -keylen 16 -kdfopt mac:HMAC -kdfopt digest:SHA256 \
-kdfopt hexkey:b74a149a -kdfopt hexinfo:348a37a2 \
-kdfopt hexsalt:3638271c SSKDF
Use SSKDF with Hash to create a hex-encoded derived key from a secret key, salt and info:
openssl kdf -keylen 14 -kdfopt digest:SHA256 \
-kdfopt hexkey:6dbdc23f045488 \
-kdfopt hexinfo:a1b2c3d4 SSKDF
Use SSHKDF to create a hex-encoded derived key from a secret key, hash and session_id:
openssl kdf -keylen 16 -kdfopt digest:SHA256 \
-kdfopt hexkey:0102030405 \
-kdfopt hexxcghash:06090A \
-kdfopt hexsession_id:01020304 \
-kdfopt type:A SSHKDF
Use PBKDF2 to create a hex-encoded derived key from a password and salt:
openssl kdf -keylen 32 -kdfopt digest:SHA256 -kdfopt pass:password \
-kdfopt salt:salt -kdfopt iter:2 PBKDF2
Use scrypt to create a hex-encoded derived key from a password and salt:
openssl kdf -keylen 64 -kdfopt pass:password -kdfopt salt:NaCl \
-kdfopt N:1024 -kdfopt r:8 -kdfopt p:16 \
-kdfopt maxmem_bytes:10485760 id-scrypt
=head1 NOTES
The KDF mechanisms that are available will depend on the options
used when building OpenSSL.
=head1 SEE ALSO
L<EVP_KDF_CTX(3)>,
L<EVP_KDF_SCRYPT(7)>
L<EVP_KDF_TLS1_PRF(7)>
L<EVP_KDF_PBKDF2(7)>
L<EVP_KDF_HKDF(7)>
L<EVP_KDF_SS(7)>
L<EVP_KDF_SSHKDF(7)>
=head1 HISTORY
Added in OpenSSL 3.0
=head1 COPYRIGHT
Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the OpenSSL license (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View file

@ -171,6 +171,10 @@ Generation of RSA Private Key. Superseded by L<genpkey(1)>.
Display diverse information built into the OpenSSL libraries. Display diverse information built into the OpenSSL libraries.
=item B<kdf>
Key Derivation Functions.
=item B<mac> =item B<mac>
Message Authentication Code Calculation. Message Authentication Code Calculation.
@ -616,7 +620,7 @@ L<crl(1)>, L<crl2pkcs7(1)>, L<dgst(1)>,
L<dhparam(1)>, L<dsa(1)>, L<dsaparam(1)>, L<dhparam(1)>, L<dsa(1)>, L<dsaparam(1)>,
L<ec(1)>, L<ecparam(1)>, L<ec(1)>, L<ecparam(1)>,
L<enc(1)>, L<engine(1)>, L<errstr(1)>, L<gendsa(1)>, L<genpkey(1)>, L<enc(1)>, L<engine(1)>, L<errstr(1)>, L<gendsa(1)>, L<genpkey(1)>,
L<genrsa(1)>, L<mac(1)>, L<nseq(1)>, L<ocsp(1)>, L<genrsa(1)>, L<kdf(1)>, L<mac(1)>, L<nseq(1)>, L<ocsp(1)>,
L<passwd(1)>, L<passwd(1)>,
L<pkcs12(1)>, L<pkcs7(1)>, L<pkcs8(1)>, L<pkcs12(1)>, L<pkcs7(1)>, L<pkcs8(1)>,
L<pkey(1)>, L<pkeyparam(1)>, L<pkeyutl(1)>, L<prime(1)>, L<pkey(1)>, L<pkeyparam(1)>, L<pkeyutl(1)>, L<prime(1)>,
@ -636,7 +640,7 @@ manual pages.
=head1 COPYRIGHT =head1 COPYRIGHT
Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use 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 this file except in compliance with the License. You can obtain a copy

View file

@ -151,9 +151,9 @@ The value string is expected to be the name of a MAC.
This control expects one argument: C<EVP_MD *md> This control expects one argument: C<EVP_MD *md>
For MAC implementations that use a message digest as an underlying computation For MAC implementations that use a message digest as an underlying computation
algorithm, this control set what the digest algorithm should be. algorithm, this control sets what the digest algorithm should be.
EVP_KDF_ctrl_str() type string: "md" EVP_KDF_ctrl_str() type string: "digest"
The value string is expected to be the name of a digest. The value string is expected to be the name of a digest.
@ -232,6 +232,7 @@ L<EVP_KDF_TLS1_PRF(7)>
L<EVP_KDF_PBKDF2(7)> L<EVP_KDF_PBKDF2(7)>
L<EVP_KDF_HKDF(7)> L<EVP_KDF_HKDF(7)>
L<EVP_KDF_SS(7)> L<EVP_KDF_SS(7)>
L<EVP_KDF_SSHKDF(7)>
=head1 HISTORY =head1 HISTORY

View file

@ -0,0 +1,78 @@
#! /usr/bin/env perl
# Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
use warnings;
use OpenSSL::Test;
use OpenSSL::Test::Utils;
setup("test_kdf");
my @kdf_tests = (
{ cmd => [qw{openssl kdf -keylen 16 -kdfopt digest:SHA256 -kdfopt secret:secret -kdfopt seed:seed TLS1-PRF}],
expected => '8E:4D:93:25:30:D7:65:A0:AA:E9:74:C3:04:73:5E:CC',
desc => 'TLS1-PRF SHA256' },
{ cmd => [qw{openssl kdf -keylen 16 -kdfopt digest:MD5-SHA1 -kdfopt secret:secret -kdfopt seed:seed TLS1-PRF}],
expected => '65:6F:31:CB:04:03:D6:51:E2:E8:71:F8:20:04:AB:BA',
desc => 'TLS1-PRF MD5-SHA1' },
{ cmd => [qw{openssl kdf -keylen 10 -kdfopt digest:SHA256 -kdfopt key:secret -kdfopt salt:salt -kdfopt info:label HKDF}],
expected => '2a:c4:36:9f:52:59:96:f8:de:13',
desc => 'HKDF SHA256' },
{ cmd => [qw{openssl kdf -keylen 32 -kdfopt digest:SHA256 -kdfopt pass:password -kdfopt salt:salt -kdfopt iter:2 PBKDF2}],
expected => 'ae:4d:0c:95:af:6b:46:d3:2d:0a:df:f9:28:f0:6d:d0:2a:30:3f:8e:f3:c2:51:df:d6:e2:d8:5a:95:47:4c:43',
desc => 'PBKDF2 SHA256'},
{ cmd => [qw{openssl kdf -keylen 64 -kdfopt mac:KMAC128 -kdfopt maclen:20 -kdfopt hexkey:b74a149a161546f8c20b06ac4ed4 -kdfopt hexinfo:348a37a27ef1282f5f020dcc -kdfopt hexsalt:3638271ccd68a25dc24ecddd39ef3f89 SSKDF}],
expected => 'e9:c1:84:53:a0:62:b5:3b:db:fc:bb:5a:34:bd:b8:e5:e7:07:ee:bb:5d:d1:34:42:43:d8:cf:c2:c2:e6:33:2f:91:bd:a5:86:f3:7d:e4:8a:65:d4:c5:14:fd:ef:aa:1e:67:54:f3:73:d2:38:e1:95:ae:15:7e:1d:e8:14:98:03',
desc => 'SSKDF KMAC128'},
{ cmd => [qw{openssl kdf -keylen 16 -kdfopt mac:HMAC -kdfopt digest:SHA256 -kdfopt hexkey:b74a149a161546f8c20b06ac4ed4 -kdfopt hexinfo:348a37a27ef1282f5f020dcc -kdfopt hexsalt:3638271ccd68a25dc24ecddd39ef3f89 SSKDF}],
expected => '44:f6:76:e8:5c:1b:1a:8b:bc:3d:31:92:18:63:1c:a3',
desc => 'SSKDF HMAC SHA256'},
{ cmd => [qw{openssl kdf -keylen 14 -kdfopt digest:SHA224 -kdfopt hexkey:6dbdc23f045488e4062757b06b9ebae183fc5a5946d80db93fec6f62ec07e3727f0126aed12ce4b262f47d48d54287f81d474c7c3b1850e9 -kdfopt hexinfo:a1b2c3d4e54341565369643c832e9849dcdba71e9a3139e606e095de3c264a66e98a165854cd07989b1ee0ec3f8dbe SSKDF}],
expected => 'a4:62:de:16:a8:9d:e8:46:6e:f5:46:0b:47:b8',
desc => 'SSKDF HASH SHA224'},
{ cmd => [qw{openssl kdf -keylen 16 -kdfopt md:SHA256 -kdfopt hexkey:0102030405 -kdfopt hexxcghash:06090A -kdfopt hexsession_id:01020304 -kdfopt type:A SSHKDF}],
expected => '5C:49:94:47:3B:B1:53:3A:58:EB:19:42:04:D3:78:16',
desc => 'SSHKDF SHA256'},
);
my @scrypt_tests = (
{ cmd => [qw{openssl kdf -keylen 64 -kdfopt pass:password -kdfopt salt:NaCl -kdfopt N:1024 -kdfopt r:8 -kdfopt p:16 -kdfopt maxmem_bytes:10485760 id-scrypt}],
expected => 'fd:ba:be:1c:9d:34:72:00:78:56:e7:19:0d:01:e9:fe:7c:6a:d7:cb:c8:23:78:30:e7:73:76:63:4b:37:31:62:2e:af:30:d9:2e:22:a3:88:6f:f1:09:27:9d:98:30:da:c7:27:af:b9:4a:83:ee:6d:83:60:cb:df:a2:cc:06:40',
desc => 'SCRYPT' },
);
push @kdf_tests, @scrypt_tests unless disabled("scrypt");
plan tests => scalar @kdf_tests;
foreach (@kdf_tests) {
ok(compareline($_->{cmd}, $_->{expected}), $_->{desc});
}
# Check that the stdout output matches the expected value.
sub compareline {
my ($cmdarray, $expect) = @_;
if (defined($expect)) {
$expect = uc $expect;
}
my @lines = run(app($cmdarray), capture => 1);
if (defined($expect)) {
if ($lines[0] =~ m|^\Q${expect}\E\R$|) {
return 1;
} else {
print "Got: $lines[0]";
print "Exp: $expect\n";
return 0;
}
}
return 0;
}