| /* | |
| * Copyright (C) 2026 Michael Brown <mbrown@fensystems.co.uk>. | |
| * | |
| * 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 2 of the | |
| * License, or 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, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| * 02110-1301, USA. | |
| * | |
| * You can also choose to distribute this program under the terms of | |
| * the Unmodified Binary Distribution Licence (as given in the file | |
| * COPYING.UBDL), provided that you have satisfied its requirements. | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| /** @file | |
| * | |
| * Merkle-Damgård hash algorithms | |
| * | |
| */ | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <assert.h> | |
| #include <ipxe/crypto.h> | |
| #include <ipxe/mdhash.h> | |
| /** | |
| * Initialise algorithm | |
| * | |
| * @v digest Digest algorithm | |
| * @v ctx MD hash context | |
| */ | |
| void mdhash_init ( struct digest_algorithm *digest, void *ctx ) { | |
| const struct mdhash_algorithm *mdhash = digest->priv; | |
| size_t blocksize = digest->blocksize; | |
| size_t digestsize = mdhash->digestsize; | |
| mdhash_context_t ( digestsize, blocksize ) *context = ctx; | |
| /* Zero accumulated data length */ | |
| context->len = 0; | |
| /* Copy initial digest values */ | |
| memcpy ( &context->dd.digest, mdhash->init, digestsize ); | |
| } | |
| /** | |
| * Accumulate data | |
| * | |
| * @v digest Digest algorithm | |
| * @v ctx MD hash context | |
| * @v data Data | |
| * @v len Length of data | |
| */ | |
| void mdhash_update ( struct digest_algorithm *digest, void *ctx, | |
| const void *data, size_t len ) { | |
| const struct mdhash_algorithm *mdhash = digest->priv; | |
| size_t blocksize = digest->blocksize; | |
| size_t digestsize = mdhash->digestsize; | |
| mdhash_context_t ( digestsize, blocksize ) *context = ctx; | |
| const uint8_t *byte = data; | |
| uint8_t copy[digestsize]; | |
| unsigned int toggle; | |
| unsigned int mask; | |
| size_t offset; | |
| /* Accumulate data a byte at a time, swapping endianness as we | |
| * go and performing the digest whenever we fill the data | |
| * buffer. | |
| */ | |
| toggle = mdhash->toggle; | |
| mask = ( blocksize - 1 ); | |
| while ( len-- ) { | |
| offset = ( ( context->len++ ^ toggle ) & mask ); | |
| context->dd.data[offset] = *(byte++); | |
| if ( ( context->len & mask ) == 0 ) { | |
| DBGC2 ( digest, "MDHASH %s compressing:\n", | |
| digest->name ); | |
| DBGC2_HDA ( digest, 0, &context->dd.digest, | |
| sizeof ( context->dd.digest ) ); | |
| DBGC2_HDA ( digest, 0, &context->dd.data, | |
| sizeof ( context->dd.data ) ); | |
| memcpy ( copy, &context->dd.digest, digestsize ); | |
| mdhash->compress ( &context->dd, copy ); | |
| DBGC2 ( digest, "MDHASH %s compressed:\n", | |
| digest->name ); | |
| DBGC2_HDA ( digest, 0, &context->dd.digest, | |
| sizeof ( context->dd.digest ) ); | |
| } | |
| } | |
| } | |
| /** | |
| * Generate digest | |
| * | |
| * @v digest Digest algorithm | |
| * @v ctx MD hash context | |
| * @v out Output buffer | |
| */ | |
| void mdhash_final ( struct digest_algorithm *digest, void *ctx, void *out ) { | |
| const struct mdhash_algorithm *mdhash = digest->priv; | |
| size_t blocksize = digest->blocksize; | |
| size_t digestsize = mdhash->digestsize; | |
| mdhash_context_t ( digestsize, blocksize ) *context = ctx; | |
| uint8_t *byte = out; | |
| union mdhash_len len; | |
| unsigned int toggle; | |
| unsigned int mask; | |
| unsigned int stop; | |
| unsigned int i; | |
| uint8_t pad; | |
| /* Record length before pre-processing */ | |
| len.qword.bits = ( ( ( uint64_t ) context->len ) * 8 ); | |
| len.qword.pad = 0; | |
| /* Pad with a single "1" bit followed by as many "0" bits as required */ | |
| mask = ( blocksize - 1 ); | |
| stop = ( blocksize - mdhash->len_len ); | |
| pad = 0x80; | |
| do { | |
| mdhash_update ( digest, ctx, &pad, sizeof ( pad ) ); | |
| pad = 0x00; | |
| } while ( ( context->len & mask ) != stop ); | |
| /* Append length (in bits) */ | |
| toggle = mdhash->len_toggle; | |
| for ( i = 0 ; i < mdhash->len_len ; i++ ) { | |
| mdhash_update ( digest, ctx, &len.byte[ i ^ toggle ], | |
| sizeof ( len.byte[ i ^ toggle ] ) ); | |
| } | |
| assert ( ( context->len & mask ) == 0 ); | |
| /* Copy out final digest */ | |
| toggle = mdhash->toggle; | |
| for ( i = 0 ; i < digest->digestsize ; i++ ) | |
| byte[i] = context->dd.digest[ i ^ toggle ]; | |
| DBGC ( digest, "MDHASH %s digested:\n", digest->name ); | |
| DBGC_HDA ( digest, 0, out, digest->digestsize ); | |
| } |