Initial import.
[create_hmac.git] / create_hmac / create_hmac.c
1 /**\r
2  * Copyright (c) 2010 Florian Forster\r
3  *\r
4  * Permission is hereby granted, free of charge, to any person obtaining a copy\r
5  * of this software and associated documentation files (the "Software"), to deal\r
6  * in the Software without restriction, including without limitation the rights\r
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
8  * copies of the Software, and to permit persons to whom the Software is\r
9  * furnished to do so, subject to the following conditions:\r
10  *\r
11  * The above copyright notice and this permission notice shall be included in\r
12  * all copies or substantial portions of the Software.\r
13  *\r
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
20  * THE SOFTWARE.\r
21  **/\r
22 \r
23 #include "create_hmac.h"\r
24 \r
25 #include <stdlib.h>\r
26 #include <stdio.h>\r
27 #include <assert.h>\r
28 \r
29 /*\r
30  * Creating a HCRYPTKEY from a plain text password is probably the most tricky\r
31  * part when calculating an RFC2104 HMAC using Microsoft CryptoAPI. The\r
32  * examples provided at the MSDN website encourage you to use "CryptDeriveKey",\r
33  * which doesn't do the conditional hashing required by HMAC. (That is, it\r
34  * might. The documentation on any of the functions is so vague that it's well\r
35  * possible the elegant solution is just not documented nor demonstrated.\r
36  */\r
37 static HCRYPTKEY create_hmac_key_exact (HCRYPTPROV hProv,\r
38                                                                                 BYTE *pbKey, DWORD dwKeySize)\r
39 {\r
40         /* Layout of the memory expected when importing a plain text blob is\r
41          * documented at the "CryptImportKey" under "Remarks". The four bytes\r
42          * following the PUBLICKEYSTRUC structure hold the size of the key, then\r
43          * follows the key itself. */\r
44         struct plain_text_data_s\r
45         {\r
46                 PUBLICKEYSTRUC blob;\r
47                 DWORD key_size;\r
48         } *data;\r
49         DWORD data_size;\r
50         HCRYPTKEY ret_key = 0;\r
51         BOOL status;\r
52 \r
53         data_size = sizeof (*data) + dwKeySize;\r
54         data = (struct plain_text_data_s *) malloc (data_size);\r
55         if (data == NULL)\r
56                 return (0);\r
57         memset (data, 0, data_size);\r
58 \r
59         /* The key is not encrypted. */\r
60         data->blob.bType = PLAINTEXTKEYBLOB;\r
61         data->blob.bVersion = CUR_BLOB_VERSION;\r
62         data->blob.reserved = 0;\r
63         /* The "CryptImportKey" page explicitly states that "RC2" is to be used for\r
64          * HMAC keys. What anyone might have been thinking, I don't know. */\r
65         data->blob.aiKeyAlg = CALG_RC2;\r
66 \r
67         /* Copy the key to the memory right behind the struct. "memcpy" should only\r
68          * add memory protection crap *after* the region written to, so the blob\r
69          * shouldn't be destroyed. We play it save, though, and set the key size\r
70          * last. Should problems arise, we should switch to a loop just to be sure. */\r
71         memcpy (data + 1, pbKey, dwKeySize);\r
72         data->key_size = dwKeySize;\r
73 \r
74         /* Actually convert our memory region to this mysterious key structure.\r
75          * The "CRYPT_IPSEC_HMAC_KEY" is required to allow RC2 keys longer than\r
76          * 16 byte. Again, this is documented on the "CryptImportKey" page as a\r
77          * side note. */\r
78         status = CryptImportKey (hProv, (BYTE *) data, sizeof (data),\r
79                 /* public key = */ 0,\r
80                 /* flags = */ CRYPT_IPSEC_HMAC_KEY,\r
81                 &ret_key);\r
82         if (!status)\r
83         {\r
84                 free (data);\r
85                 return (0);\r
86         }\r
87 \r
88         free (data);\r
89         return (ret_key);\r
90 } /* HCRYPTKEY create_hmac_key_exact */\r
91 \r
92 /* If the key of the HMAC is larger than the hash size, use the hash of the\r
93  * key instead of using the key directly. */\r
94 static HCRYPTKEY create_hmac_key_hashed (HCRYPTPROV hProv,\r
95                                                                                  BYTE *pbKey, DWORD dwKeySize,\r
96                                                                                  DWORD dwHashSize, HCRYPTHASH hHash)\r
97 {\r
98         BOOL status;\r
99         BYTE *hash_data;\r
100         DWORD hash_data_size;\r
101         HCRYPTKEY key;\r
102 \r
103         assert (dwKeySize > dwHashSize);\r
104 \r
105         status = CryptHashData (hHash, pbKey, dwKeySize, /* dwFlags = */ 0);\r
106         if (!status)\r
107                 return (0);\r
108 \r
109         hash_data = (BYTE *) malloc (dwHashSize);\r
110         if (hash_data == NULL)\r
111                 return (0);\r
112         memset (hash_data, 0, dwHashSize);\r
113         hash_data_size = dwHashSize;\r
114 \r
115         status = CryptGetHashParam (hHash, HP_HASHVAL,\r
116                 hash_data, &hash_data_size, /* flags = */ 0);\r
117         if (!status)\r
118         {\r
119                 free (hash_data);\r
120                 return (0);\r
121         }\r
122 \r
123         assert (hash_data_size == dwHashSize);\r
124 \r
125         key = create_hmac_key_exact (hProv, hash_data, hash_data_size);\r
126 \r
127         free (hash_data);\r
128         return (key);\r
129 } /* HCRYPTKEY create_hmac_key_hashed */\r
130 \r
131 /* If the key is short enough, it is (right-)padded with zeros and otherwise\r
132  * used as-is. */\r
133 static HCRYPTKEY create_hmac_key_padded (HCRYPTPROV hProv,\r
134                                                                                  BYTE *pbKey, DWORD dwKeySize,\r
135                                                                                  DWORD dwHashSize)\r
136 {\r
137         BYTE *padded_key;\r
138         HCRYPTKEY key;\r
139         DWORD i;\r
140 \r
141         assert (dwKeySize <= dwHashSize);\r
142 \r
143         if (dwKeySize == dwHashSize)\r
144                 return (create_hmac_key_exact (hProv, pbKey, dwKeySize));\r
145 \r
146         padded_key = (BYTE *) malloc (dwHashSize);\r
147         if (padded_key == NULL)\r
148                 return (0);\r
149 \r
150         /* Copy the key and right-pad with zeros. Don't use "memcpy" here because\r
151          * the fucked up version of VS will corrupt memory. */\r
152         for (i = 0; i < dwHashSize; i++)\r
153                 padded_key[i] = (i < dwKeySize) ? pbKey[i] : 0;\r
154 \r
155         key = create_hmac_key_exact (hProv, padded_key, dwHashSize);\r
156 \r
157         free (padded_key);\r
158         return (key);\r
159 } /* HCRYPTKEY create_hmac_key_padded */\r
160 \r
161 static HCRYPTKEY create_hmac_key (HCRYPTPROV hProv,\r
162                                                                   ALG_ID Algid,\r
163                                                                   BYTE *pbKey, DWORD dwKeySize)\r
164 {\r
165         HCRYPTHASH hash = 0;\r
166         HCRYPTKEY key;\r
167         DWORD hash_size = 0;\r
168         DWORD param_size;\r
169         BOOL status;\r
170 \r
171         /* Allocate a hash object to determine the hash size. */\r
172         status = CryptCreateHash (hProv, Algid,\r
173                 /* hKey = */ 0,\r
174                 /* dwFlags = */ 0,\r
175                 /* out phHash = */ &hash);\r
176         if (!status)\r
177                 return (0);\r
178 \r
179         param_size = (DWORD) sizeof (hash_size);\r
180         status = CryptGetHashParam (hash, HP_HASHSIZE,\r
181                 (BYTE *) &hash_size, &param_size,\r
182                 /* flags = */ 0);\r
183         if (!status)\r
184         {\r
185                 CryptDestroyHash (hash);\r
186                 return (0);\r
187         }\r
188 \r
189         /* Determine whether we need to calculate the hash of the key or if\r
190          * padding is sufficient. */\r
191         if (dwKeySize > hash_size)\r
192                 key = create_hmac_key_hashed (hProv, pbKey, dwKeySize, hash_size, hash);\r
193         else\r
194                 key = create_hmac_key_padded (hProv, pbKey, dwKeySize, hash_size);\r
195 \r
196         CryptDestroyHash (hash);\r
197         return (key);\r
198 } /* HCRYPTKEY create_hmac_key */\r
199 \r
200 BOOL CreateHMAC (HCRYPTPROV hProv,\r
201                                  ALG_ID Algid,\r
202                                  BYTE *pbKey, DWORD dwKeySize,\r
203                                  DWORD dwFlags,\r
204                                  HCRYPTHASH *phHash,\r
205                                  HCRYPTKEY *phKey)\r
206 {\r
207         HCRYPTKEY hmac_key;\r
208         HCRYPTHASH hash = 0;\r
209         HMAC_INFO hmac_info;\r
210         BOOL status;\r
211 \r
212         hmac_key = create_hmac_key (hProv, Algid, pbKey, dwKeySize);\r
213         if (hmac_key == 0)\r
214                 return (FALSE);\r
215 \r
216         status = CryptCreateHash (hProv, CALG_HMAC, hmac_key,\r
217                 /* flags = */ dwFlags,\r
218                 &hash);\r
219         if (!status)\r
220         {\r
221                 CryptDestroyKey (hmac_key);\r
222                 return (status);\r
223         }\r
224 \r
225         memset (&hmac_info, 0, sizeof (hmac_info));\r
226         hmac_info.HashAlgid = Algid;\r
227         hmac_info.pbInnerString = NULL;\r
228         hmac_info.cbInnerString = 0;\r
229         hmac_info.pbOuterString = NULL;\r
230         hmac_info.cbOuterString = 0;\r
231 \r
232         status = CryptSetHashParam (hash, HP_HMAC_INFO, (BYTE *) &hmac_info,\r
233                 /* flags = */ 0);\r
234         if (!status)\r
235         {\r
236                 CryptDestroyHash (hash);\r
237                 CryptDestroyKey (hmac_key);\r
238                 return (status);\r
239         }\r
240 \r
241         *phHash = hash;\r
242         *phKey = hmac_key;\r
243         return (TRUE);\r
244 } /* BOOL CreateHMAC */\r
245 \r
246 BOOL DestroyHMAC (HCRYPTHASH hHash, HCRYPTKEY hKey)\r
247 {\r
248         if (hHash != 0)\r
249                 CryptDestroyHash (hHash);\r
250 \r
251         if (hKey != 0)\r
252                 CryptDestroyKey (hKey);\r
253 \r
254         return (TRUE);\r
255 } /* BOOL DestroyHMAC */\r
256 \r
257 /* vim: set ts=4 sw=4 noet : */\r