openssl app for macs that uses the new EVP_MAC interface (the code inside dgst uses EVP_PKEY)

Reviewed-by: Paul Yang <yang.yang@baishancloud.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/7661)
This commit is contained in:
Shane Lontis 2018-11-20 10:45:44 +10:00 committed by Matt Caswell
parent 1b1ff9b94d
commit 4d768e966f
5 changed files with 555 additions and 10 deletions

View file

@ -2,10 +2,10 @@
qw(openssl.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
genpkey.c genrsa.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 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);
genpkey.c genrsa.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
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);
our @apps_lib_src =
( qw(apps.c apps_ui.c opt.c fmt.c s_cb.c s_socket.c app_rand.c
bf_prefix.c),

200
apps/mac.c Normal file
View file

@ -0,0 +1,200 @@
/*
* Copyright 2018 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>
#undef BUFSIZE
#define BUFSIZE 1024*8
typedef enum OPTION_choice {
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
OPT_MACOPT, OPT_BIN, OPT_IN, OPT_OUT
} OPTION_CHOICE;
const OPTIONS mac_options[] = {
{OPT_HELP_STR, 1, '-', "Usage: %s [options] mac_name\n"},
{OPT_HELP_STR, 1, '-', "mac_name\t\t MAC algorithm (See list "
"-mac-algorithms)"},
{"help", OPT_HELP, '-', "Display this summary"},
{"macopt", OPT_MACOPT, 's', "MAC algorithm control parameters in n:v form. "
"See 'Supported Controls' in the EVP_MAC_ docs"},
{"in", OPT_IN, '<', "Input file to MAC (default is stdin)"},
{"out", OPT_OUT, '>', "Output to filename rather than stdout"},
{"binary", OPT_BIN, '-', "Output in binary format (Default is hexadecimal "
"output)"},
{NULL}
};
static int mac_ctrl_string(EVP_MAC_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_MAC_ctrl_str(ctx, stmp, vtmp);
OPENSSL_free(stmp);
return rv;
}
int mac_main(int argc, char **argv)
{
int ret = 1;
char *prog;
const EVP_MAC *mac = NULL;
OPTION_CHOICE o;
EVP_MAC_CTX *ctx = NULL;
STACK_OF(OPENSSL_STRING) *opts = NULL;
unsigned char *buf = NULL;
size_t len;
int i;
BIO *in = NULL, *out = NULL;
const char *outfile = NULL;
const char *infile = NULL;
int out_bin = 0;
int inform = FORMAT_BINARY;
prog = opt_init(argc, argv, mac_options);
buf = app_malloc(BUFSIZE, "I/O buffer");
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
case OPT_EOF:
case OPT_ERR:
opthelp:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
goto err;
case OPT_HELP:
opt_help(mac_options);
ret = 0;
goto err;
case OPT_BIN:
out_bin = 1;
break;
case OPT_IN:
infile = opt_arg();
break;
case OPT_OUT:
outfile = opt_arg();
break;
case OPT_MACOPT:
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;
}
mac = EVP_get_macbyname(argv[0]);
if (mac == NULL) {
BIO_printf(bio_err, "Invalid MAC name %s\n", argv[0]);
goto opthelp;
}
ctx = EVP_MAC_CTX_new(mac);
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 (mac_ctrl_string(ctx, opt) <= 0) {
BIO_printf(bio_err, "MAC parameter error '%s'\n", opt);
ERR_print_errors(bio_err);
goto err;
}
}
}
/* Use text mode for stdin */
if (infile == NULL || strcmp(infile, "-") == 0)
inform = FORMAT_TEXT;
in = bio_open_default(infile, 'r', inform);
if (in == NULL)
goto err;
out = bio_open_default(outfile, 'w', out_bin ? FORMAT_BINARY : FORMAT_TEXT);
if (out == NULL)
goto err;
if (!EVP_MAC_init(ctx)) {
BIO_printf(bio_err, "EVP_MAC_Init failed\n");
goto err;
}
for (;;) {
i = BIO_read(in, (char *)buf, BUFSIZE);
if (i < 0) {
BIO_printf(bio_err, "Read Error in '%s'\n", infile);
goto err;
}
if (i == 0)
break;
if (!EVP_MAC_update(ctx, buf, i)) {
BIO_printf(bio_err, "EVP_MAC_update failed\n");
goto err;
}
}
if (!EVP_MAC_final(ctx, NULL, &len)) {
BIO_printf(bio_err, "EVP_MAC_final failed\n");
goto err;
}
if (len > BUFSIZE) {
BIO_printf(bio_err, "output len is too large\n");
goto err;
}
if (!EVP_MAC_final(ctx, buf, &len)) {
BIO_printf(bio_err, "EVP_MAC_final failed\n");
goto err;
}
if (out_bin) {
BIO_write(out, buf, len);
} else {
if (outfile == NULL)
BIO_printf(out,"\n");
for (i = 0; i < (int)len; ++i)
BIO_printf(out, "%02X", buf[i]);
if (outfile == NULL)
BIO_printf(out,"\n");
}
ret = 0;
err:
if (ret != 0)
ERR_print_errors(bio_err);
OPENSSL_clear_free(buf, BUFSIZE);
sk_OPENSSL_STRING_free(opts);
BIO_free(in);
BIO_free(out);
EVP_MAC_CTX_free(ctx);
return ret;
}

163
doc/man1/mac.pod Normal file
View file

@ -0,0 +1,163 @@
=pod
=head1 NAME
openssl-mac,
mac - perform Message Authentication Code operations
=head1 SYNOPSIS
B<openssl mac>
[B<-help>]
[B<-macopt>]
[B<-in filename>]
[B<-out filename>]
[B<-binary>]
B<mac_name>
B<openssl> I<mac> [B<...>] B<mac_name>
=head1 DESCRIPTION
The message authentication code functions output the MAC of a supplied input
file.
=head1 OPTIONS
=over 4
=item B<-help>
Print a usage message.
=item B<-in filename>
Input filename to calculate a MAC for, or standard input by default.
Standard input is used if the filename is '-'.
Files are expected to be in binary format, standard input uses hexadecimal text
format.
=item B<-out filename>
Filename to output to, or standard output by default.
=item B<-binary>
Output the MAC in binary form. Uses hexadecimal text format if not specified.
=item B<-macopt nm:v>
Passes options to the MAC algorithm.
A comprehensive list of controls can be found in the EVP_MAC implementation
documentation.
Common control strings used by EVP_MAC_ctrl_str() are:
=over 4
=item B<key:string>
Specifies the MAC key as an alphanumeric string (use if the key contains
printable characters only).
The string length must conform to any restrictions of the MAC algorithm.
A key must be specified for every MAC algorithm.
=item B<hexkey:string>
Specifies the MAC key in hexadecimal form (two hex digits per byte).
The key length must conform to any restrictions of the MAC algorithm.
A key must be specified for every MAC algorithm.
=item B<digest:string>
Used by HMAC as an alphanumeric string (use if the key contains printable
characters only).
The string length must conform to any restrictions of the MAC algorithm.
To see the list of supported digests, use the command I<list -digest-commands>.
=item B<cipher:string>
Used by CMAC and GMAC to specifiy the cipher algorithm.
For CMAC it must be one of AES-128-CBC, AES-192-CBC, AES-256-CBC or
DES-EDE3-CBC.
For GMAC it should be a GCM mode cipher e.g. AES-128-GCM.
=item B<iv:string>
Used by GMAC to specify an IV as an alphanumeric string (use if the IV contains
printable characters only).
=item B<hexiv:string>
Used by GMAC to specify an IV in hexadecimal form (two hex digits per byte).
=item B<outlen:int>
Used by KMAC128 or KMAC256 to specify an output length.
The default sizes are 32 or 64 bytes respectively.
=item B<custom:string>
Used by KMAC128 or KMAC256 to specify a customization string.
The default is the empty string "".
=back
=item B<mac_name>
Specifies the name of a supported MAC algorithm which will be used.
To see the list of supported MAC's use the command I<list -mac-algorithms>.
=back
=head1 EXAMPLES
To create a hex-encoded HMAC-SHA1 MAC of a file and write to stdout: \
openssl mac -macopt digest:SHA1 \
-macopt hexkey:000102030405060708090A0B0C0D0E0F10111213 \
-in msg.bin HMAC
To create a SipHash MAC from a file with a binary file output: \
openssl mac -macopt hexkey:000102030405060708090A0B0C0D0E0F \
-in msg.bin -out out.bin -binary SipHash
To create a hex-encoded CMAC-AES-128-CBC MAC from a file:\
openssl mac -macopt cipher:AES-128-CBC \
-macopt hexkey:77A77FAF290C1FA30C683DF16BA7A77B \
-in msg.bin CMAC
To create a hex-encoded KMAC128 MAC from a file with a Customisation String
'Tag' and output length of 16: \
openssl mac -macopt custom:Tag -macopt hexkey:40414243444546 \
-macopt outlen:16 -in msg.bin KMAC128
To create a hex-encoded GMAC-AES-128-GCM with a IV from a file: \
openssl mac -macopt cipher:AES-128-GCM -macopt hexiv:E0E00F19FED7BA0136A797F3 \
-macopt hexkey:77A77FAF290C1FA30C683DF16BA7A77B -in msg.bin GMAC
=head1 NOTES
The MAC mechanisms that are available will depend on the options
used when building OpenSSL.
The B<list -mac-algorithms> command can be used to list them.
=head1 SEE ALSO
L<EVP_MAC(3)>,
L<EVP_MAC_CMAC(7)>,
L<EVP_MAC_GMAC(7)>,
L<EVP_MAC_HMAC(7)>,
L<EVP_MAC_KMAC(7)>,
L<EVP_MAC_SIPHASH(7)>,
L<EVP_MAC_POLY1305(7)>
=head1 COPYRIGHT
Copyright 2018 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

@ -11,7 +11,7 @@ I<command>
[ I<command_opts> ]
[ I<command_args> ]
B<openssl> B<list> [ B<standard-commands> | B<digest-commands> | B<cipher-commands> | B<cipher-algorithms> | B<digest-algorithms> | B<public-key-algorithms>]
B<openssl> B<list> [ B<standard-commands> | B<digest-commands> | B<cipher-commands> | B<cipher-algorithms> | B<digest-algorithms> | B<mac-algorithms> | B<public-key-algorithms>]
B<openssl> B<no->I<XXX> [ I<arbitrary options> ]
@ -28,7 +28,7 @@ It can be used for
o Creation and management of private keys, public keys and parameters
o Public key cryptographic operations
o Creation of X.509 certificates, CSRs and CRLs
o Calculation of Message Digests
o Calculation of Message Digests and Message Authentication Codes
o Encryption and Decryption with Ciphers
o SSL/TLS Client and Server Tests
o Handling of S/MIME signed or encrypted mail
@ -57,8 +57,9 @@ and B<cipher-commands> output a list (one entry per line) of the names
of all standard commands, message digest commands, or cipher commands,
respectively, that are available in the present B<openssl> utility.
The list parameters B<cipher-algorithms> and
B<digest-algorithms> list all cipher and message digest names, one entry per line. Aliases are listed as:
The list parameters B<cipher-algorithms>, B<digest-algorithms>,
and B<mac-algorithms> list all cipher, message digest, and message
authentication code names, one entry per line. Aliases are listed as:
from => to
@ -106,7 +107,8 @@ CRL to PKCS#7 Conversion.
=item B<dgst>
Message Digest Calculation.
Message Digest calculation. MAC calculations are superseded by
L<mac(1)>.
=item B<dh>
@ -165,6 +167,10 @@ Generation of Private Key or Parameters.
Generation of RSA Private Key. Superseded by L<genpkey(1)>.
=item B<mac>
Message Authentication Code Calculation.
=item B<nseq>
Create or examine a Netscape certificate sequence.
@ -606,7 +612,7 @@ L<crl(1)>, L<crl2pkcs7(1)>, L<dgst(1)>,
L<dhparam(1)>, L<dsa(1)>, L<dsaparam(1)>,
L<ec(1)>, L<ecparam(1)>,
L<enc(1)>, L<engine(1)>, L<errstr(1)>, L<gendsa(1)>, L<genpkey(1)>,
L<genrsa(1)>, L<nseq(1)>, L<ocsp(1)>,
L<genrsa(1)>, L<mac(1)>, L<nseq(1)>, L<ocsp(1)>,
L<passwd(1)>,
L<pkcs12(1)>, L<pkcs7(1)>, L<pkcs8(1)>,
L<pkey(1)>, L<pkeyparam(1)>, L<pkeyutl(1)>, L<prime(1)>,

176
test/recipes/20-test_mac.t Normal file
View file

@ -0,0 +1,176 @@
#! /usr/bin/env perl
# Copyright 2018 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;
use Storable qw(dclone);
setup("test_mac");
my @mac_tests = (
{ cmd => [qw{openssl mac -macopt hexkey:000102030405060708090A0B0C0D0E0F}],
type => 'SipHash',
input => '00',
expected => 'da87c1d86b99af44347659119b22fc45',
desc => 'SipHash No input' },
{ cmd => [qw{openssl mac -macopt digest:SHA1 -macopt hexkey:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F}],
type => 'HMAC',
input => unpack("H*", "Sample message for keylen=blocklen"),
expected => '5FD596EE78D5553C8FF4E72D266DFD192366DA29',
desc => 'HMAC SHA1' },
{ cmd => [qw{openssl mac -macopt cipher:AES-256-CBC -macopt hexkey:0B122AC8F34ED1FE082A3625D157561454167AC145A10BBF77C6A70596D574F1}],
type => 'CMAC',
input => '498B53FDEC87EDCBF07097DCCDE93A084BAD7501A224E388DF349CE18959FE8485F8AD1537F0D896EA73BEDC7214713F',
expected => 'F62C46329B41085625669BAF51DEA66A',
desc => 'CMAC AES-256-CBC' },
{ cmd => [qw{openssl mac -macopt hexkey:02000000000000000000000000000000ffffffffffffffffffffffffffffffff}],
type => 'Poly1305',
input => '02000000000000000000000000000000',
expected => '03000000000000000000000000000000',
desc => 'Poly1305 (wrap 2^128)' },
{ cmd => [qw{openssl mac -macopt cipher:AES-256-GCM -macopt hexkey:4C973DBC7364621674F8B5B89E5C15511FCED9216490FB1C1A2CAA0FFE0407E5 -macopt hexiv:7AE8E2CA4EC500012E58495C}],
type => 'GMAC',
input => '68F2E77696CE7AE8E2CA4EC588E541002E58495C08000F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D0007',
expected => '00BDA1B7E87608BCBF470F12157F4C07',
desc => 'GMAC' },
{ cmd => [qw{openssl mac -macopt hexkey:404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F -macopt xof:0}],
type => 'KMAC128',
input => '00010203',
expected => 'E5780B0D3EA6F7D3A429C5706AA43A00FADBD7D49628839E3187243F456EE14E',
desc => 'KMAC128' },
{ cmd => [qw{openssl mac -macopt hexkey:404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F -macopt }, 'custom:My Tagged Application'],
type => 'KMAC256',
input => '00010203',
expected => '20C570C31346F703C9AC36C61C03CB64C3970D0CFC787E9B79599D273A68D2F7F69D4CC3DE9D104A351689F27CF6F5951F0103F33F4F24871024D9C27773A8DD',
desc => 'KMAC256' },
{ cmd => [qw{openssl mac -macopt hexkey:404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F -macopt xof:1 -macopt}, 'custom:My Tagged Application'],
type => 'KMAC256',
input => '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7',
expected => 'D5BE731C954ED7732846BB59DBE3A8E30F83E77A4BFF4459F2F1C2B4ECEBB8CE67BA01C62E8AB8578D2D499BD1BB276768781190020A306A97DE281DCC30305D',
desc => 'KMAC256 with xof len of 64' },
);
my @mac_fail_tests = (
{ cmd => [qw{openssl mac}],
type => 'SipHash',
input => '00',
err => '',
desc => 'SipHash Fail no key' },
{ cmd => [qw{openssl mac}],
type => 'KMAC128',
input => '00',
err => 'EVP_MAC_Init',
desc => 'KMAC128 Fail no key' },
);
plan tests => (scalar @mac_tests * 2) + scalar @mac_fail_tests;
foreach (@mac_tests) {
ok(compareline($_->{cmd}, $_->{type}, $_->{input}, $_->{expected}, $_->{err}), $_->{desc});
}
foreach (@mac_tests) {
ok(comparefile($_->{cmd}, $_->{type}, $_->{input}, $_->{expected}), $_->{desc});
}
foreach (@mac_fail_tests) {
ok(compareline($_->{cmd}, $_->{type}, $_->{input}, $_->{expected}, $_->{err}), $_->{desc});
}
# Create a temp input file and save the input data into it, and
# then compare the stdout output matches the expected value.
sub compareline {
my $tmpfile = 'tmp.bin';
my ($cmdarray_orig, $type, $input, $expect, $err) = @_;
my $cmdarray = dclone $cmdarray_orig;
if (defined($expect)) {
$expect = uc $expect;
}
# Open a temporary input file and write $input to it
open(my $in, '>', $tmpfile) or die "Could not open file";
binmode($in);
my $bin = pack("H*", $input);
print $in $bin;
close $in;
# The last cmd parameter is the temporary input file we just created.
my @other = ('-in', $tmpfile, $type);
push @$cmdarray, @other;
my @lines = run(app($cmdarray), capture => 1);
unlink $tmpfile;
if (defined($expect)) {
if ($lines[1] =~ m|^\Q${expect}\E\R$|) {
return 1;
} else {
print "Got: $lines[1]";
print "Exp: $expect\n";
return 0;
}
}
if (defined($err)) {
if (defined($lines[0])) {
$lines[0] =~ s/\s+$//;
if ($lines[0] eq $err) {
return 1;
} else {
print "Got: $lines[0]";
print "Exp: $err\n";
return 0;
}
} else {
# expected an error
return 1;
}
}
return 0;
}
# Create a temp input file and save the input data into it, and
# use the '-bin -out <file>' commandline options to save results out to a file.
# Read this file back in and check its output matches the expected value.
sub comparefile {
my $tmpfile = 'tmp.bin';
my $outfile = 'out.bin';
my ($cmdarray, $type, $input, $expect) = @_;
$expect = uc $expect;
# Open a temporary input file and write $input to it
open(my $in, '>', $tmpfile) or die "Could not open file";
binmode($in);
my $bin = pack("H*", $input);
print $in $bin;
close $in;
my @other = ("-binary", "-in", $tmpfile, "-out", $outfile, $type);
push @$cmdarray, @other;
run(app($cmdarray));
unlink $tmpfile;
open(my $out, '<', $outfile) or die "Could not open file";
binmode($out);
my $buffer;
my $BUFSIZE = 1024;
read($out, $buffer, $BUFSIZE) or die "unable to read";
my $line = uc unpack("H*", $buffer);
close($out);
unlink $outfile;
if ($line eq $expect) {
return 1;
} else {
print "Got: $line\n";
print "Exp: $expect\n";
return 0;
}
}