diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | THANKS.in | 1 | ||||
-rwxr-xr-x | build-aux/gen-lists-of-programs.sh | 1 | ||||
-rw-r--r-- | cfg.mk | 3 | ||||
-rw-r--r-- | doc/coreutils.texi | 32 | ||||
-rw-r--r-- | man/.gitignore | 1 | ||||
-rw-r--r-- | man/b2sum.x | 4 | ||||
-rw-r--r-- | man/local.mk | 1 | ||||
-rwxr-xr-x | scripts/git-hooks/commit-msg | 4 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/blake2/.gitignore | 2 | ||||
-rw-r--r-- | src/blake2/b2sum.c | 389 | ||||
-rw-r--r-- | src/blake2/b2sum.h | 19 | ||||
-rw-r--r-- | src/blake2/blake2-impl.h | 160 | ||||
-rw-r--r-- | src/blake2/blake2.h | 195 | ||||
-rw-r--r-- | src/blake2/blake2b-ref.c | 379 | ||||
-rw-r--r-- | src/local.mk | 6 | ||||
-rw-r--r-- | src/md5sum.c | 184 | ||||
-rw-r--r-- | tests/local.mk | 1 | ||||
-rwxr-xr-x | tests/misc/b2sum.sh | 49 | ||||
-rwxr-xr-x | tests/misc/md5sum-bsd.sh | 10 |
23 files changed, 1428 insertions, 24 deletions
@@ -2,6 +2,7 @@ Here are the names of the programs in this package, each followed by the name(s) of its author(s). arch: David MacKenzie, Karel Zak +b2sum: Padraig Brady, Samuel Neves base32: Simon Josefsson base64: Simon Josefsson basename: David MacKenzie @@ -104,6 +104,11 @@ GNU coreutils NEWS -*- outline -*- stat --format=%N for quoting file names now honors the same QUOTING_STYLE environment variable values as ls. +** New programs + + b2sum is added to support the BLAKE2 digest algorithm with + a similar interface to the existing md5sum and sha1sum, etc. commands. + ** New Features date now accepts the --debug option, to annotate the parsed date string, @@ -7,8 +7,8 @@ arbitrary limits. The programs that can be built with this package are: - [ arch base32 base64 basename cat chcon chgrp chmod chown chroot cksum comm - coreutils cp csplit cut date dd df dir dircolors dirname du echo env + [ arch b2sum base32 base64 basename cat chcon chgrp chmod chown chroot cksum + comm coreutils cp csplit cut date dd df dir dircolors dirname du echo env expand expr factor false fmt fold groups head hostid hostname id install join kill link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd @@ -558,6 +558,7 @@ Ross Ridge rridge@calum.csclub.uwaterloo.ca Rudolf Kastl rkastl@redhat.com Sahil Amoli sahilamoli@gmail.com Sami Farin sfarin@ratol.fi +Samuel Neves sneves@dei.uc.pt Samuel Tardieu sam@rfc1149.net Samuel Thibault samuel.thibault@ens-lyon.org Samuli Karkkainen Samuli.Karkkainen@hut.fi diff --git a/build-aux/gen-lists-of-programs.sh b/build-aux/gen-lists-of-programs.sh index 40c3a3236..2666492e8 100755 --- a/build-aux/gen-lists-of-programs.sh +++ b/build-aux/gen-lists-of-programs.sh @@ -41,6 +41,7 @@ build_if_possible_progs=' # be buildable without problems on any target system. normal_progs=' [ + b2sum base64 base32 basename @@ -21,6 +21,9 @@ manual_title = Core GNU utilities # it can take a while for the faster mirror links to become usable. url_dir_list = http://ftp.gnu.org/gnu/$(PACKAGE) +# Exclude bundled external projects from syntax checks +VC_LIST_ALWAYS_EXCLUDE_REGEX = src/blake2/.*$$ + # Tests not to run as part of "make distcheck". local-checks-to-skip = \ sc_proper_name_utf8_requires_ICONV diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 81101485c..d0694fdd0 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -37,6 +37,7 @@ @dircategory Individual utilities @direntry * arch: (coreutils)arch invocation. Print machine hardware name. +* b2sum: (coreutils)b2sum invocation. Print or check BLAKE2 digests. * base32: (coreutils)base32 invocation. Base32 encode/decode data. * base64: (coreutils)base64 invocation. Base64 encode/decode data. * basename: (coreutils)basename invocation. Strip directory and suffix. @@ -186,7 +187,7 @@ Free Documentation License''. * Output of entire files:: cat tac nl od base32 base64 * Formatting file contents:: fmt pr fold * Output of parts of files:: head tail split csplit -* Summarizing files:: wc sum cksum md5sum sha1sum sha2 +* Summarizing files:: wc sum cksum b2sum md5sum sha1sum sha2 * Operating on sorted files:: sort shuf uniq comm ptx tsort * Operating on fields:: cut paste join * Operating on characters:: tr expand unexpand @@ -259,6 +260,7 @@ Summarizing files * wc invocation:: Print newline, word, and byte counts * sum invocation:: Print checksum and block counts * cksum invocation:: Print CRC checksum and byte counts +* b2sum invocation:: Print or check BLAKE2 digests * md5sum invocation:: Print or check MD5 digests * sha1sum invocation:: Print or check SHA-1 digests * sha2 utilities:: Print or check SHA-2 digests @@ -3557,6 +3559,7 @@ contents of files. * wc invocation:: Print newline, word, and byte counts. * sum invocation:: Print checksum and block counts. * cksum invocation:: Print CRC checksum and byte counts. +* b2sum invocation:: Print or check BLAKE2 digests. * md5sum invocation:: Print or check MD5 digests. * sha1sum invocation:: Print or check SHA-1 digests. * sha2 utilities:: Print or check SHA-2 digests. @@ -3769,6 +3772,33 @@ options}. @exitstatus +@node b2sum invocation +@section @command{b2sum}: Print or check BLAKE2 digests + +@pindex b2sum +@cindex BLAKE2 +@cindex 512-bit checksum +@cindex checksum, 512-bit +@cindex fingerprint, 512-bit +@cindex message-digest, 512-bit + +@command{b2sum} computes a 512-bit checksum for each specified +@var{file}. The same usage and options as the @command{md5sum} +command are supported. @xref{md5sum invocation}. +In addition @command{b2sum} supports the following options. + +@table @samp +@item -l +@itemx --length +@opindex -l +@opindex --length +@cindex BLAKE2 hash length +Change (shorten) the default digest length. +This is specified in bits and thus must be a multiple of 8. +This option is ignored when @option{--check} is specified, +as the length is automatically determined when checking. +@end table + @node md5sum invocation @section @command{md5sum}: Print or check MD5 digests diff --git a/man/.gitignore b/man/.gitignore index 37bc6c426..9ef048a4c 100644 --- a/man/.gitignore +++ b/man/.gitignore @@ -1,5 +1,6 @@ Makefile Makefile.in +b2sum.1 base32.1 base64.1 basename.1 diff --git a/man/b2sum.x b/man/b2sum.x new file mode 100644 index 000000000..3ba490aee --- /dev/null +++ b/man/b2sum.x @@ -0,0 +1,4 @@ +[NAME] +b2sum \- compute and check BLAKE2 message digest +[DESCRIPTION] +.\" Add any additional description here diff --git a/man/local.mk b/man/local.mk index 7b72d3be7..a39bb6506 100644 --- a/man/local.mk +++ b/man/local.mk @@ -60,6 +60,7 @@ man/install.1: src/ginstall$(EXEEXT) man/test.1: src/[$(EXEEXT) man/arch.1: src/arch$(EXEEXT) +man/b2sum.1: src/b2sum$(EXEEXT) man/base32.1: src/base32$(EXEEXT) man/base64.1: src/base64$(EXEEXT) man/basename.1: src/basename$(EXEEXT) diff --git a/scripts/git-hooks/commit-msg b/scripts/git-hooks/commit-msg index d860f0ba2..6aedf807f 100755 --- a/scripts/git-hooks/commit-msg +++ b/scripts/git-hooks/commit-msg @@ -14,8 +14,8 @@ $editor = "vi" if $? != 0 or $editor =~ /^\s*\z/; # Keywords allowed before the colon on the first line of a commit message: # program names and a few general category names. my @valid = qw( - arch base32 base64 basename cat chcon chgrp chmod chown chroot cksum comm - cp csplit cut date dd df dir dircolors dirname du echo env expand + arch b2sum base32 base64 basename cat chcon chgrp chmod chown chroot cksum + comm cp csplit cut date dd df dir dircolors dirname du echo env expand expr factor false fmt fold groups head hostid hostname id install join kill link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf diff --git a/src/.gitignore b/src/.gitignore index c1f3b0d13..70ab2cac8 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,6 +2,7 @@ /.dirstamp \[ arch +b2sum base32 base64 basename diff --git a/src/blake2/.gitignore b/src/blake2/.gitignore new file mode 100644 index 000000000..93d1552ba --- /dev/null +++ b/src/blake2/.gitignore @@ -0,0 +1,2 @@ +/.dirstamp +/.deps diff --git a/src/blake2/b2sum.c b/src/blake2/b2sum.c new file mode 100644 index 000000000..1c565e08c --- /dev/null +++ b/src/blake2/b2sum.c @@ -0,0 +1,389 @@ +/* + BLAKE2 reference source code package - b2sum tool + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <ctype.h> +#include <unistd.h> +#include <getopt.h> +#include <stdbool.h> + +#include "blake2.h" + +/* This will help compatibility with coreutils */ +int blake2b_stream( FILE *stream, void *resstream, size_t outbytes ) +{ + int ret = -1; + size_t sum, n; + blake2b_state S[1]; + static const size_t buffer_length = 32768; + uint8_t *buffer = ( uint8_t * )malloc( buffer_length ); + + if( !buffer ) return -1; + + blake2b_init( S, outbytes ); + + while( 1 ) + { + sum = 0; + + while( 1 ) + { + n = fread( buffer + sum, 1, buffer_length - sum, stream ); + sum += n; + + if( buffer_length == sum ) + break; + + if( 0 == n ) + { + if( ferror( stream ) ) + goto cleanup_buffer; + + goto final_process; + } + + if( feof( stream ) ) + goto final_process; + } + + blake2b_update( S, buffer, buffer_length ); + } + +final_process:; + + if( sum > 0 ) blake2b_update( S, buffer, sum ); + + blake2b_final( S, resstream, outbytes ); + ret = 0; +cleanup_buffer: + free( buffer ); + return ret; +} + +#if 0 +int blake2s_stream( FILE *stream, void *resstream, size_t outbytes ) +{ + int ret = -1; + size_t sum, n; + blake2s_state S[1]; + static const size_t buffer_length = 32768; + uint8_t *buffer = ( uint8_t * )malloc( buffer_length ); + + if( !buffer ) return -1; + + blake2s_init( S, outbytes ); + + while( 1 ) + { + sum = 0; + + while( 1 ) + { + n = fread( buffer + sum, 1, buffer_length - sum, stream ); + sum += n; + + if( buffer_length == sum ) + break; + + if( 0 == n ) + { + if( ferror( stream ) ) + goto cleanup_buffer; + + goto final_process; + } + + if( feof( stream ) ) + goto final_process; + } + + blake2s_update( S, buffer, buffer_length ); + } + +final_process:; + + if( sum > 0 ) blake2s_update( S, buffer, sum ); + + blake2s_final( S, resstream, outbytes ); + ret = 0; +cleanup_buffer: + free( buffer ); + return ret; +} + + +int blake2sp_stream( FILE *stream, void *resstream, size_t outbytes ) +{ + int ret = -1; + size_t sum, n; + blake2sp_state S[1]; + static const size_t buffer_length = 16 * ( 1UL << 20 ); + uint8_t *buffer = ( uint8_t * )malloc( buffer_length ); + + if( !buffer ) return -1; + + blake2sp_init( S, outbytes ); + + while( 1 ) + { + sum = 0; + + while( 1 ) + { + n = fread( buffer + sum, 1, buffer_length - sum, stream ); + sum += n; + + if( buffer_length == sum ) + break; + + if( 0 == n ) + { + if( ferror( stream ) ) + goto cleanup_buffer; + + goto final_process; + } + + if( feof( stream ) ) + goto final_process; + } + + blake2sp_update( S, buffer, buffer_length ); + } + +final_process:; + + if( sum > 0 ) blake2sp_update( S, buffer, sum ); + + blake2sp_final( S, resstream, outbytes ); + ret = 0; +cleanup_buffer: + free( buffer ); + return ret; +} + + +int blake2bp_stream( FILE *stream, void *resstream, size_t outbytes ) +{ + int ret = -1; + size_t sum, n; + blake2bp_state S[1]; + static const size_t buffer_length = 16 * ( 1UL << 20 ); + uint8_t *buffer = ( uint8_t * )malloc( buffer_length ); + + if( !buffer ) return -1; + + blake2bp_init( S, outbytes ); + + while( 1 ) + { + sum = 0; + + while( 1 ) + { + n = fread( buffer + sum, 1, buffer_length - sum, stream ); + sum += n; + + if( buffer_length == sum ) + break; + + if( 0 == n ) + { + if( ferror( stream ) ) + goto cleanup_buffer; + + goto final_process; + } + + if( feof( stream ) ) + goto final_process; + } + + blake2bp_update( S, buffer, buffer_length ); + } + +final_process:; + + if( sum > 0 ) blake2bp_update( S, buffer, sum ); + + blake2bp_final( S, resstream, outbytes ); + ret = 0; +cleanup_buffer: + free( buffer ); + return ret; +} + +typedef int ( *blake2fn )( FILE *, void *, size_t ); + + +static void usage( char **argv, int errcode ) +{ + FILE *out = errcode ? stderr : stdout; + fprintf( out, "Usage: %s [OPTION]... [FILE]...\n", argv[0] ); + fprintf( out, "\n" ); + fprintf( out, "With no FILE, or when FILE is -, read standard input.\n" ); + fprintf( out, "\n" ); + fprintf( out, " -a <algo> hash algorithm (blake2b is default): \n" + " [blake2b|blake2s|blake2bp|blake2sp]\n" ); + fprintf( out, " -l <length> digest length in bits, must not exceed the maximum for\n" + " the selected algorithm and must be a multiple of 8\n" ); + fprintf( out, " --tag create a BSD-style checksum\n" ); + fprintf( out, " --help display this help and exit\n" ); + exit( errcode ); +} + +int main( int argc, char **argv ) +{ + blake2fn blake2_stream = blake2b_stream; + unsigned long maxbytes = BLAKE2B_OUTBYTES; + const char *algorithm = "BLAKE2b"; + unsigned long outbytes = 0; + unsigned char hash[BLAKE2B_OUTBYTES] = {0}; + bool bsdstyle = false; + int c, i; + opterr = 1; + + while( 1 ) + { + int option_index = 0; + char *end = NULL; + unsigned long outbits; + static struct option long_options[] = { + { "help", no_argument, 0, 0 }, + { "tag", no_argument, 0, 0 }, + { NULL, 0, NULL, 0 } + }; + + c = getopt_long( argc, argv, "a:l:", long_options, &option_index ); + if( c == -1 ) break; + switch( c ) + { + case 'a': + if( 0 == strcmp( optarg, "blake2b" ) ) + { + blake2_stream = blake2b_stream; + maxbytes = BLAKE2B_OUTBYTES; + algorithm = "BLAKE2b"; + } + else if ( 0 == strcmp( optarg, "blake2s" ) ) + { + blake2_stream = blake2s_stream; + maxbytes = BLAKE2S_OUTBYTES; + algorithm = "BLAKE2s"; + } + else if ( 0 == strcmp( optarg, "blake2bp" ) ) + { + blake2_stream = blake2bp_stream; + maxbytes = BLAKE2B_OUTBYTES; + algorithm = "BLAKE2bp"; + } + else if ( 0 == strcmp( optarg, "blake2sp" ) ) + { + blake2_stream = blake2sp_stream; + maxbytes = BLAKE2S_OUTBYTES; + algorithm = "BLAKE2sp"; + } + else + { + printf( "Invalid function name: `%s'\n", optarg ); + usage( argv, 111 ); + } + + break; + + case 'l': + outbits = strtoul(optarg, &end, 10); + if( !end || *end != '\0' || outbits % 8 != 0) + { + printf( "Invalid length argument: `%s'\n", optarg); + usage( argv, 111 ); + } + outbytes = outbits / 8; + break; + + case 0: + if( 0 == strcmp( "help", long_options[option_index].name ) ) + usage( argv, 0 ); + else if( 0 == strcmp( "tag", long_options[option_index].name ) ) + bsdstyle = true; + break; + + case '?': + usage( argv, 1 ); + break; + } + } + + if(outbytes > maxbytes) + { + printf( "Invalid length argument: %lu\n", outbytes * 8 ); + printf( "Maximum digest length for %s is %lu\n", algorithm, maxbytes * 8 ); + usage( argv, 111 ); + } + else if( outbytes == 0 ) + outbytes = maxbytes; + + if( optind == argc ) + argv[argc++] = (char *) "-"; + + for( i = optind; i < argc; ++i ) + { + FILE *f = NULL; + if( argv[i][0] == '-' && argv[i][1] == '\0' ) + f = stdin; + else + f = fopen( argv[i], "rb" ); + + if( !f ) + { + fprintf( stderr, "Could not open `%s': %s\n", argv[i], strerror( errno ) ); + continue; + } + + if( blake2_stream( f, hash, outbytes ) < 0 ) + { + fprintf( stderr, "Failed to hash `%s'\n", argv[i] ); + } + else + { + size_t j; + if( bsdstyle ) + { + if( outbytes < maxbytes ) + printf( "%s-%lu (%s) = ", algorithm, outbytes * 8, argv[i] ); + else + printf( "%s (%s) = ", algorithm, argv[i] ); + } + + for( j = 0; j < outbytes; ++j ) + printf( "%02x", hash[j] ); + + if( bsdstyle ) + printf( "\n" ); + else + printf( " %s\n", argv[i] ); + } + + if( f != stdin ) fclose( f ); + } + + return 0; +} +#endif diff --git a/src/blake2/b2sum.h b/src/blake2/b2sum.h new file mode 100644 index 000000000..59120f6e8 --- /dev/null +++ b/src/blake2/b2sum.h @@ -0,0 +1,19 @@ +/* + BLAKE2 reference source code package - b2sum tool + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +int blake2b_stream( FILE *stream, void *resstream, size_t outbytes ); +typedef int ( *blake2fn )( FILE *, void *, size_t ); +#define BLAKE2S_OUTBYTES 32 +#define BLAKE2B_OUTBYTES 64 diff --git a/src/blake2/blake2-impl.h b/src/blake2/blake2-impl.h new file mode 100644 index 000000000..5dff7fc7a --- /dev/null +++ b/src/blake2/blake2-impl.h @@ -0,0 +1,160 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_IMPL_H +#define BLAKE2_IMPL_H + +#include <stdint.h> +#include <string.h> + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) + #if defined(_MSC_VER) + #define BLAKE2_INLINE __inline + #elif defined(__GNUC__) + #define BLAKE2_INLINE __inline__ + #else + #define BLAKE2_INLINE + #endif +#else + #define BLAKE2_INLINE inline +#endif + +static BLAKE2_INLINE uint32_t load32( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8) | + (( uint32_t )( p[2] ) << 16) | + (( uint32_t )( p[3] ) << 24) ; +#endif +} + +static BLAKE2_INLINE uint64_t load64( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) | + (( uint64_t )( p[6] ) << 48) | + (( uint64_t )( p[7] ) << 56) ; +#endif +} + +static BLAKE2_INLINE uint16_t load16( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint16_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint16_t )( p[0] ) << 0) | + (( uint16_t )( p[1] ) << 8) ; +#endif +} + +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + *p++ = ( uint8_t )w; w >>= 8; + *p++ = ( uint8_t )w; +#endif +} + +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +#endif +} + +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); +#endif +} + +static BLAKE2_INLINE uint64_t load48( const void *src ) +{ + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) ; +} + +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +{ + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); +} + +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 32 - c ) ); +} + +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 64 - c ) ); +} + +/* prevents compiler optimizing out memset() */ +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) +{ + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); +} + +#endif diff --git a/src/blake2/blake2.h b/src/blake2/blake2.h new file mode 100644 index 000000000..ad62f260e --- /dev/null +++ b/src/blake2/blake2.h @@ -0,0 +1,195 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_H +#define BLAKE2_H + +#include <stddef.h> +#include <stdint.h> + +#if defined(_MSC_VER) +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) +#else +#define BLAKE2_PACKED(x) x __attribute__((packed)) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2s_constant + { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 + }; + + enum blake2b_constant + { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + + typedef struct blake2s_state__ + { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2s_state; + + typedef struct blake2b_state__ + { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2b_state; + + typedef struct blake2sp_state__ + { + blake2s_state S[8][1]; + blake2s_state R[1]; + uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2sp_state; + + typedef struct blake2bp_state__ + { + blake2b_state S[4][1]; + blake2b_state R[1]; + uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2bp_state; + + + BLAKE2_PACKED(struct blake2s_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ + }); + + typedef struct blake2s_param__ blake2s_param; + + BLAKE2_PACKED(struct blake2b_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + }); + + typedef struct blake2b_param__ blake2b_param; + + typedef struct blake2xs_state__ + { + blake2s_state S[1]; + blake2s_param P[1]; + } blake2xs_state; + + typedef struct blake2xb_state__ + { + blake2b_state S[1]; + blake2b_param P[1]; + } blake2xb_state; + + /* Padded structs result in a compile-time error */ + enum { + BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), + BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) + }; + + /* Streaming API */ + int blake2s_init( blake2s_state *S, size_t outlen ); + int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); + int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); + int blake2s_final( blake2s_state *S, void *out, size_t outlen ); + + int blake2b_init( blake2b_state *S, size_t outlen ); + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); + + int blake2sp_init( blake2sp_state *S, size_t outlen ); + int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); + int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); + + int blake2bp_init( blake2bp_state *S, size_t outlen ); + int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); + int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); + + /* Variable output length API */ + int blake2xs_init( blake2xs_state *S, const size_t outlen ); + int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); + int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); + + int blake2xb_init( blake2xb_state *S, const size_t outlen ); + int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); + int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); + + /* Simple API */ + int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + /* This is simply an alias for blake2b */ + int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/blake2/blake2b-ref.c b/src/blake2/blake2b-ref.c new file mode 100644 index 000000000..cd38b1ba0 --- /dev/null +++ b/src/blake2/blake2b-ref.c @@ -0,0 +1,379 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + + +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2b_init0( blake2b_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2b_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + const uint8_t *p = ( const uint8_t * )( P ); + size_t i; + + blake2b_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + + S->outlen = P->digest_length; + return 0; +} + + + +int blake2b_init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + uint64_t m[16]; + uint64_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load64( block + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2b_IV[0]; + v[ 9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +/* inlen, at least, should be uint64_t. Others can be size_t. */ +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2b_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2b_init( S, outlen ) < 0 ) return -1; + } + + blake2b_update( S, ( const uint8_t * )in, inlen ); + blake2b_final( S, out, outlen ); + return 0; +} + +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { + return blake2b(out, outlen, in, inlen, key, keylen); +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2B_SELFTEST) +#include <string.h> +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2b_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/local.mk b/src/local.mk index 9e36ad9bd..c5898cca1 100644 --- a/src/local.mk +++ b/src/local.mk @@ -393,6 +393,12 @@ src_sha384sum_SOURCES = src/md5sum.c src_sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS) src_sha512sum_SOURCES = src/md5sum.c src_sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS) +src_b2sum_CPPFLAGS = -include config.h -DHASH_ALGO_BLAKE2=1 \ + $(AM_CPPFLAGS) +src_b2sum_SOURCES = src/md5sum.c \ + src/blake2/blake2.h src/blake2/blake2-impl.h \ + src/blake2/blake2b-ref.c \ + src/blake2/b2sum.c src/blake2/b2sum.h src_base64_CPPFLAGS = -DBASE_TYPE=64 $(AM_CPPFLAGS) src_base32_SOURCES = src/base64.c diff --git a/src/md5sum.c b/src/md5sum.c index 6e85cb150..8e2160967 100644 --- a/src/md5sum.c +++ b/src/md5sum.c @@ -22,7 +22,14 @@ #include <sys/types.h> #include "system.h" +#include "argmatch.h" +#include "quote.h" +#include "xdectoint.h" +#include "xstrtol.h" +#if HASH_ALGO_BLAKE2 +# include "blake2/b2sum.h" +#endif #if HASH_ALGO_MD5 # include "md5.h" #endif @@ -49,6 +56,13 @@ # define DIGEST_BITS 128 # define DIGEST_REFERENCE "RFC 1321" # define DIGEST_ALIGN 4 +#elif HASH_ALGO_BLAKE2 +# define PROGRAM_NAME "b2sum" +# define DIGEST_TYPE_STRING "BLAKE2" +# define DIGEST_STREAM blake2fns[b2_algorithm] +# define DIGEST_BITS 512 +# define DIGEST_REFERENCE "RFC 7693" +# define DIGEST_ALIGN 8 #elif HASH_ALGO_SHA1 # define PROGRAM_NAME "sha1sum" # define DIGEST_TYPE_STRING "SHA1" @@ -88,20 +102,30 @@ # error "Can't decide which hash algorithm to compile." #endif -#define DIGEST_HEX_BYTES (DIGEST_BITS / 4) -#define DIGEST_BIN_BYTES (DIGEST_BITS / 8) - -#define AUTHORS \ +#if HASH_ALGO_BLAKE2 +# define AUTHORS \ + proper_name ("Padraig Brady"), \ + proper_name ("Samuel Neves") +#else +# define AUTHORS \ proper_name ("Ulrich Drepper"), \ proper_name ("Scott Miller"), \ proper_name ("David Madore") +# define DIGEST_HEX_BYTES (DIGEST_BITS / 4) +#endif +#define DIGEST_BIN_BYTES (DIGEST_BITS / 8) + /* The minimum length of a valid digest line. This length does not include any newline character at the end of a line. */ -#define MIN_DIGEST_LINE_LENGTH \ - (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \ - + 1 /* blank */ \ - + 1 /* minimum filename length */ ) +#if HASH_ALGO_BLAKE2 +# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8. */ +#else +# define MIN_DIGEST_LINE_LENGTH \ + (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \ + + 1 /* blank */ \ + + 1 /* minimum filename length */ ) +#endif /* True if any of the files read were the standard input. */ static bool have_read_stdin; @@ -133,6 +157,38 @@ static bool strict = false; /* Whether a BSD reversed format checksum is detected. */ static int bsd_reversed = -1; +#if HASH_ALGO_BLAKE2 +static char const *const algorithm_in_string[] = +{ + "blake2b", NULL +}; +static char const *const algorithm_out_string[] = +{ + "BLAKE2b", NULL +}; +enum Algorithm +{ + BLAKE2b +}; +static enum Algorithm const algorithm[] = +{ + BLAKE2b +}; +ARGMATCH_VERIFY (algorithm_in_string, algorithm); +ARGMATCH_VERIFY (algorithm_out_string, algorithm); + +static enum Algorithm b2_algorithm; +static uintmax_t b2_length; +static blake2fn blake2fns[]= +{ + blake2b_stream +}; +static uintmax_t blake2_max_len[]= +{ + BLAKE2B_OUTBYTES +}; +#endif /* HASH_ALGO_BLAKE2 */ + /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum @@ -146,6 +202,9 @@ enum static struct option const long_options[] = { +#if HASH_ALGO_BLAKE2 + { "length", required_argument, NULL, 'l'}, +#endif { "binary", no_argument, NULL, 'b' }, { "check", no_argument, NULL, 'c' }, { "ignore-missing", no_argument, NULL, IGNORE_MISSING_OPTION}, @@ -176,7 +235,6 @@ Print or check %s (%d-bit) checksums.\n\ DIGEST_BITS); emit_stdin_note (); - if (O_BINARY) fputs (_("\ \n\ @@ -187,9 +245,16 @@ Print or check %s (%d-bit) checksums.\n\ \n\ -b, --binary read in binary mode\n\ "), stdout); + printf (_("\ -c, --check read %s sums from the FILEs and check them\n"), DIGEST_TYPE_STRING); +#if HASH_ALGO_BLAKE2 + fputs (_("\ + -l, --length digest length in bits; must not exceed the maximum for\n\ + the blake2 algorithm and must be a multiple of 8\n\ +"), stdout); +#endif fputs (_("\ --tag create a BSD-style checksum\n\ "), stdout); @@ -350,15 +415,51 @@ split_3 (char *s, size_t s_len, algo_name_len = strlen (DIGEST_TYPE_STRING); if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len)) { - if (s[i + algo_name_len] == ' ') + i += algo_name_len; +#if HASH_ALGO_BLAKE2 + /* Terminate and match algorithm name. */ + char const *algo_name = &s[i - algo_name_len]; + while (! ISWHITE (s[i]) && s[i] != '-' && s[i] != '(') ++i; - if (s[i + algo_name_len] == '(') + bool length_specified = s[i] == '-'; + bool openssl_format = s[i] == '('; /* and no length_specified */ + s[i++] = '\0'; + ptrdiff_t algo = argmatch (algo_name, algorithm_out_string, NULL, 0); + if (algo < 0) + return false; + else + b2_algorithm = algo; + if (openssl_format) + s[--i] = '('; + + if (length_specified) { + unsigned long int tmp_ulong; + if (xstrtoul (s + i, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK + && 0 < tmp_ulong && tmp_ulong <= blake2_max_len[b2_algorithm] * 8 + && tmp_ulong % 8 == 0) + b2_length = tmp_ulong; + else + return false; + + while (ISDIGIT (s[i])) + ++i; + } + else + b2_length = blake2_max_len[b2_algorithm] * 8; + + digest_hex_bytes = b2_length / 4; +#endif + if (s[i] == ' ') + ++i; + if (s[i] == '(') + { + ++i; *binary = 0; - return bsd_split_3 (s + i + algo_name_len + 1, - s_len - (i + algo_name_len + 1), + return bsd_split_3 (s + i, s_len - i, hex_digest, file_name, escaped_filename); } + return false; } /* Ignore this line if it is too short. @@ -370,6 +471,18 @@ split_3 (char *s, size_t s_len, *hex_digest = (unsigned char *) &s[i]; +#if HASH_ALGO_BLAKE2 + /* Auto determine length. */ + unsigned char const *hp = *hex_digest; + digest_hex_bytes = 0; + while (isxdigit (*hp++)) + digest_hex_bytes++; + if (digest_hex_bytes < 2 || digest_hex_bytes % 2 + || blake2_max_len[b2_algorithm] * 2 < digest_hex_bytes) + return false; + b2_length = digest_hex_bytes * 4; +#endif + /* The first field has to be the n-character hexadecimal representation of the message digest. If it is not followed immediately by a white space it's an error. */ @@ -505,7 +618,11 @@ digest_file (const char *filename, int *binary, unsigned char *bin_result, fadvise (fp, FADVISE_SEQUENTIAL); +#if HASH_ALGO_BLAKE2 + err = DIGEST_STREAM (fp, bin_result, b2_length / 8); +#else err = DIGEST_STREAM (fp, bin_result); +#endif if (err) { error (0, errno, "%s", quotef (filename)); @@ -758,9 +875,28 @@ main (int argc, char **argv) so that processes running in parallel do not intersperse their output. */ setvbuf (stdout, NULL, _IOLBF, 0); - while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1) +#if HASH_ALGO_BLAKE2 + const char* short_opts = "l:bctw"; + const char* b2_length_str = ""; +#else + const char* short_opts = "bctw"; +#endif + + while ((opt = getopt_long (argc, argv, short_opts, long_options, NULL)) != -1) switch (opt) { +#if HASH_ALGO_BLAKE2 + case 'l': + b2_length = xdectoumax (optarg, 0, UINTMAX_MAX, "", + _("invalid length"), 0); + b2_length_str = optarg; + if (b2_length % 8 != 0) + { + error (0, 0, _("invalid length: %s"), quote (b2_length_str)); + die (EXIT_FAILURE, 0, _("length is not a multiple of 8")); + } + break; +#endif case 'b': binary = 1; break; @@ -802,7 +938,21 @@ main (int argc, char **argv) } min_digest_line_length = MIN_DIGEST_LINE_LENGTH; +#if HASH_ALGO_BLAKE2 + if (b2_length > blake2_max_len[b2_algorithm] * 8) + { + error (0, 0, _("invalid length: %s"), quote (b2_length_str)); + die (EXIT_FAILURE, 0, + _("maximum digest length for %s is %"PRIuMAX" bits"), + quote (algorithm_in_string[b2_algorithm]), + blake2_max_len[b2_algorithm] * 8); + } + if (b2_length == 0 && ! do_check) + b2_length = blake2_max_len[b2_algorithm] * 8; + digest_hex_bytes = b2_length / 4; +#else digest_hex_bytes = DIGEST_HEX_BYTES; +#endif if (prefix_tag && !binary) { @@ -901,7 +1051,13 @@ main (int argc, char **argv) if (needs_escape) putchar ('\\'); +#if HASH_ALGO_BLAKE2 + fputs (algorithm_out_string[b2_algorithm], stdout); + if (b2_length < blake2_max_len[b2_algorithm] * 8) + printf ("-%"PRIuMAX, b2_length); +#else fputs (DIGEST_TYPE_STRING, stdout); +#endif fputs (" (", stdout); print_filename (file, needs_escape); fputs (") = ", stdout); diff --git a/tests/local.mk b/tests/local.mk index 86de37603..af34e2428 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -296,6 +296,7 @@ all_tests = \ tests/misc/head-pos.sh \ tests/misc/head-write-error.sh \ tests/misc/kill.sh \ + tests/misc/b2sum.sh \ tests/misc/md5sum.pl \ tests/misc/md5sum-bsd.sh \ tests/misc/md5sum-newline.pl \ diff --git a/tests/misc/b2sum.sh b/tests/misc/b2sum.sh new file mode 100755 index 000000000..d254e0e7a --- /dev/null +++ b/tests/misc/b2sum.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# 'b2sum' tests + +# Copyright (C) 2016 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ b2sum + +# Ensure we can --check the --tag format we produce +rm check.b2sum +for i in 'a' ' b' '*c' '44' ' '; do + echo "$i" > "$i" + for l in 0 128; do + b2sum -l $l --tag "$i" >> check.b2sum + done +done +# Note -l is infered from the tags in the mixed format file +b2sum --strict -c check.b2sum || fail=1 +# Also ensure the openssl tagged variant works +sed 's/ //; s/ =/=/' < check.b2sum > openssl.b2sum || framework_failure_ +b2sum --strict -c openssl.b2sum || fail=1 + +# Ensure we can check non tagged format +for l in 0 128; do + b2sum -l $l /dev/null | tee -a check.vals > check.b2sum + b2sum -l $l --strict -c check.b2sum || fail=1 + b2sum --strict -c check.b2sum || fail=1 +done + +# Ensure the checksum values are correct. The reference +# check.vals was created with the upstream SSE reference implementation. +b2sum -l 128 check.vals > out || fail=1 +printf '%s\n' '796485dd32fe9b754ea5fd6c721271d9 check.vals' > exp +compare exp out || fail=1 + +Exit $fail diff --git a/tests/misc/md5sum-bsd.sh b/tests/misc/md5sum-bsd.sh index e86434cf0..9f9a9a771 100755 --- a/tests/misc/md5sum-bsd.sh +++ b/tests/misc/md5sum-bsd.sh @@ -28,7 +28,7 @@ print_ver_ md5sum # I.e., one not starting with ' ' or '*' for i in 'a' ' b' '*c' 'dd' ' '; do echo "$i" > "$i" - md5sum "$i" >> check.md5sum + md5sum "$i" >> check.md5sum || fail=1 done sed 's/ / /' check.md5sum > check.md5 @@ -59,7 +59,7 @@ returns_ 1 md5sum --tag --text /dev/null || fail=1 rm check.md5 for i in 'a' ' b' '*c' 'dd' ' '; do echo "$i" > "$i" - md5sum --tag "$i" >> check.md5 + md5sum --tag "$i" >> check.md5 || fail=1 done md5sum --strict -c check.md5 || fail=1 @@ -70,8 +70,8 @@ nl=' tab=' ' rm check.md5 for i in 'a\b' 'a\' "a${nl}b" "a${tab}b"; do - > "$i" - md5sum --tag "$i" >> check.md5 + : > "$i" + md5sum --tag "$i" >> check.md5 || fail=1 done md5sum --strict -c check.md5 || fail=1 @@ -82,7 +82,7 @@ ex_file='test ex_output='\MD5 (test\n\\\\file) = d41d8cd98f00b204e9800998ecf8427e' touch "$ex_file" printf "%s\n" "$ex_output" > exp -md5sum --tag "$ex_file" > out +md5sum --tag "$ex_file" > out || fail=1 compare exp out || fail=1 Exit $fail |