Added HMAC code from TLS project
[people/adir/gpxe.git] / src / crypto / hmac.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /**
20  * @file
21  *
22  * Keyed-Hashing for Message Authentication
23  */
24
25 #include <string.h>
26 #include <assert.h>
27 #include <gpxe/crypto.h>
28 #include <gpxe/hmac.h>
29
30 /**
31  * Reduce HMAC key length
32  *
33  * @v digest            Digest algorithm to use
34  * @v digest_ctx        Digest context
35  * @v key               Key
36  * @v key_len           Length of key
37  */
38 static void hmac_reduce_key ( struct crypto_algorithm *digest,
39                               void *key, size_t *key_len ) {
40         uint8_t digest_ctx[digest->ctxsize];
41
42         digest_init ( digest, digest_ctx );
43         digest_update ( digest, digest_ctx, key, *key_len );
44         digest_final ( digest, digest_ctx, key );
45         *key_len = digest->digestsize;
46 }
47
48 /**
49  * Initialise HMAC
50  *
51  * @v digest            Digest algorithm to use
52  * @v digest_ctx        Digest context
53  * @v key               Key
54  * @v key_len           Length of key
55  *
56  * The length of the key should be less than the block size of the
57  * digest algorithm being used.  (If the key length is greater, it
58  * will be replaced with its own digest, and key_len will be updated
59  * accordingly).
60  */
61 void hmac_init ( struct crypto_algorithm *digest, void *digest_ctx,
62                  void *key, size_t *key_len ) {
63         unsigned char k_ipad[digest->blocksize];
64         unsigned int i;
65
66         /* Reduce key if necessary */
67         if ( *key_len > sizeof ( k_ipad ) )
68                 hmac_reduce_key ( digest, key, key_len );
69
70         /* Construct input pad */
71         memset ( k_ipad, 0, sizeof ( k_ipad ) );
72         memcpy ( k_ipad, key, *key_len );
73         for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) {
74                 k_ipad[i] ^= 0x36;
75         }
76         
77         /* Start inner hash */
78         digest_init ( digest, digest_ctx );
79         digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) );
80 }
81
82 /**
83  * Finalise HMAC
84  *
85  * @v digest            Digest algorithm to use
86  * @v digest_ctx        Digest context
87  * @v key               Key
88  * @v key_len           Length of key
89  * @v hmac              HMAC digest to fill in
90  *
91  * The length of the key should be less than the block size of the
92  * digest algorithm being used.  (If the key length is greater, it
93  * will be replaced with its own digest, and key_len will be updated
94  * accordingly).
95  */
96 void hmac_final ( struct crypto_algorithm *digest, void *digest_ctx,
97                   void *key, size_t *key_len, void *hmac ) {
98         unsigned char k_opad[digest->blocksize];
99         unsigned int i;
100
101         /* Reduce key if necessary */
102         if ( *key_len > sizeof ( k_opad ) )
103                 hmac_reduce_key ( digest, key, key_len );
104
105         /* Construct output pad */
106         memset ( k_opad, 0, sizeof ( k_opad ) );
107         memcpy ( k_opad, key, *key_len );
108         for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) {
109                 k_opad[i] ^= 0x5c;
110         }
111         
112         /* Finish inner hash */
113         digest_final ( digest, digest_ctx, hmac );
114
115         /* Perform outer hash */
116         digest_init ( digest, digest_ctx );
117         digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) );
118         digest_update ( digest, digest_ctx, hmac, digest->digestsize );
119         digest_final ( digest, digest_ctx, hmac );
120 }