| /* |
| * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER ); |
| |
| /** |
| * @file |
| * |
| * Keyed-Hashing for Message Authentication |
| */ |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <gpxe/crypto.h> |
| #include <gpxe/hmac.h> |
| |
| /** |
| * Reduce HMAC key length |
| * |
| * @v digest Digest algorithm to use |
| * @v digest_ctx Digest context |
| * @v key Key |
| * @v key_len Length of key |
| */ |
| static void hmac_reduce_key ( struct digest_algorithm *digest, |
| void *key, size_t *key_len ) { |
| uint8_t digest_ctx[digest->ctxsize]; |
| |
| digest_init ( digest, digest_ctx ); |
| digest_update ( digest, digest_ctx, key, *key_len ); |
| digest_final ( digest, digest_ctx, key ); |
| *key_len = digest->digestsize; |
| } |
| |
| /** |
| * Initialise HMAC |
| * |
| * @v digest Digest algorithm to use |
| * @v digest_ctx Digest context |
| * @v key Key |
| * @v key_len Length of key |
| * |
| * The length of the key should be less than the block size of the |
| * digest algorithm being used. (If the key length is greater, it |
| * will be replaced with its own digest, and key_len will be updated |
| * accordingly). |
| */ |
| void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, |
| void *key, size_t *key_len ) { |
| unsigned char k_ipad[digest->blocksize]; |
| unsigned int i; |
| |
| /* Reduce key if necessary */ |
| if ( *key_len > sizeof ( k_ipad ) ) |
| hmac_reduce_key ( digest, key, key_len ); |
| |
| /* Construct input pad */ |
| memset ( k_ipad, 0, sizeof ( k_ipad ) ); |
| memcpy ( k_ipad, key, *key_len ); |
| for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { |
| k_ipad[i] ^= 0x36; |
| } |
| |
| /* Start inner hash */ |
| digest_init ( digest, digest_ctx ); |
| digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); |
| } |
| |
| /** |
| * Finalise HMAC |
| * |
| * @v digest Digest algorithm to use |
| * @v digest_ctx Digest context |
| * @v key Key |
| * @v key_len Length of key |
| * @v hmac HMAC digest to fill in |
| * |
| * The length of the key should be less than the block size of the |
| * digest algorithm being used. (If the key length is greater, it |
| * will be replaced with its own digest, and key_len will be updated |
| * accordingly). |
| */ |
| void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, |
| void *key, size_t *key_len, void *hmac ) { |
| unsigned char k_opad[digest->blocksize]; |
| unsigned int i; |
| |
| /* Reduce key if necessary */ |
| if ( *key_len > sizeof ( k_opad ) ) |
| hmac_reduce_key ( digest, key, key_len ); |
| |
| /* Construct output pad */ |
| memset ( k_opad, 0, sizeof ( k_opad ) ); |
| memcpy ( k_opad, key, *key_len ); |
| for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { |
| k_opad[i] ^= 0x5c; |
| } |
| |
| /* Finish inner hash */ |
| digest_final ( digest, digest_ctx, hmac ); |
| |
| /* Perform outer hash */ |
| digest_init ( digest, digest_ctx ); |
| digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); |
| digest_update ( digest, digest_ctx, hmac, digest->digestsize ); |
| digest_final ( digest, digest_ctx, hmac ); |
| } |