blob: f282e473f08336707a887089f2700a85e63c72d2 [file]
/*
* 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 );
}