/src/nss-nspr/nss/lib/freebl/alghmac.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #ifdef FREEBL_NO_DEPEND |
6 | | #include "stubs.h" |
7 | | #endif |
8 | | |
9 | | #include "secport.h" |
10 | | #include "hasht.h" |
11 | | #include "blapit.h" |
12 | | #include "alghmac.h" |
13 | | #include "secerr.h" |
14 | | |
15 | | #define HMAC_PAD_SIZE HASH_BLOCK_LENGTH_MAX |
16 | | |
17 | | struct HMACContextStr { |
18 | | void *hash; |
19 | | const SECHashObject *hashobj; |
20 | | PRBool wasAllocated; |
21 | | unsigned char ipad[HMAC_PAD_SIZE]; |
22 | | unsigned char opad[HMAC_PAD_SIZE]; |
23 | | }; |
24 | | |
25 | | void |
26 | | HMAC_Destroy(HMACContext *cx, PRBool freeit) |
27 | 371 | { |
28 | 371 | if (cx == NULL) |
29 | 0 | return; |
30 | | |
31 | 371 | PORT_Assert(!freeit == !cx->wasAllocated); |
32 | 371 | if (cx->hash != NULL) { |
33 | 371 | cx->hashobj->destroy(cx->hash, PR_TRUE); |
34 | 371 | PORT_Memset(cx, 0, sizeof *cx); |
35 | 371 | } |
36 | 371 | if (freeit) |
37 | 371 | PORT_Free(cx); |
38 | 371 | } |
39 | | |
40 | | static SECStatus |
41 | | hmac_initKey(HMACContext *cx, const unsigned char *secret, |
42 | | unsigned int secret_len, PRBool isFIPS) |
43 | 371 | { |
44 | 371 | unsigned int i; |
45 | 371 | unsigned char hashed_secret[HASH_LENGTH_MAX]; |
46 | | |
47 | | /* required by FIPS 198 Section 3 */ |
48 | 371 | if (isFIPS && secret_len < cx->hashobj->length / 2) { |
49 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
50 | 0 | return SECFailure; |
51 | 0 | } |
52 | | |
53 | 371 | if (secret_len > cx->hashobj->blocklength) { |
54 | 212 | cx->hashobj->begin(cx->hash); |
55 | 212 | cx->hashobj->update(cx->hash, secret, secret_len); |
56 | 212 | PORT_Assert(cx->hashobj->length <= sizeof hashed_secret); |
57 | 212 | cx->hashobj->end(cx->hash, hashed_secret, &secret_len, |
58 | 212 | sizeof hashed_secret); |
59 | 212 | if (secret_len != cx->hashobj->length) { |
60 | 0 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
61 | 0 | goto loser; |
62 | 0 | } |
63 | 212 | secret = (const unsigned char *)&hashed_secret[0]; |
64 | 212 | } |
65 | | |
66 | 371 | PORT_Memset(cx->ipad, 0x36, cx->hashobj->blocklength); |
67 | 371 | PORT_Memset(cx->opad, 0x5c, cx->hashobj->blocklength); |
68 | | |
69 | | /* fold secret into padding */ |
70 | 9.49k | for (i = 0; i < secret_len; i++) { |
71 | 9.12k | cx->ipad[i] ^= secret[i]; |
72 | 9.12k | cx->opad[i] ^= secret[i]; |
73 | 9.12k | } |
74 | 371 | PORT_Memset(hashed_secret, 0, sizeof hashed_secret); |
75 | 371 | return SECSuccess; |
76 | | |
77 | 0 | loser: |
78 | 0 | PORT_Memset(hashed_secret, 0, sizeof hashed_secret); |
79 | 0 | return SECFailure; |
80 | 371 | } |
81 | | |
82 | | SECStatus |
83 | | HMAC_Init(HMACContext *cx, const SECHashObject *hash_obj, |
84 | | const unsigned char *secret, unsigned int secret_len, PRBool isFIPS) |
85 | 371 | { |
86 | 371 | SECStatus rv; |
87 | | |
88 | 371 | if (cx == NULL) { |
89 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
90 | 0 | return SECFailure; |
91 | 0 | } |
92 | 371 | cx->wasAllocated = PR_FALSE; |
93 | 371 | cx->hashobj = hash_obj; |
94 | 371 | cx->hash = cx->hashobj->create(); |
95 | 371 | if (cx->hash == NULL) |
96 | 0 | goto loser; |
97 | | |
98 | 371 | rv = hmac_initKey(cx, secret, secret_len, isFIPS); |
99 | 371 | if (rv != SECSuccess) |
100 | 0 | goto loser; |
101 | | |
102 | 371 | return rv; |
103 | 0 | loser: |
104 | 0 | if (cx->hash != NULL) |
105 | 0 | cx->hashobj->destroy(cx->hash, PR_TRUE); |
106 | 0 | return SECFailure; |
107 | 371 | } |
108 | | |
109 | | HMACContext * |
110 | | HMAC_Create(const SECHashObject *hash_obj, const unsigned char *secret, |
111 | | unsigned int secret_len, PRBool isFIPS) |
112 | 371 | { |
113 | 371 | SECStatus rv; |
114 | 371 | HMACContext *cx = PORT_ZNew(HMACContext); |
115 | 371 | if (cx == NULL) |
116 | 0 | return NULL; |
117 | 371 | rv = HMAC_Init(cx, hash_obj, secret, secret_len, isFIPS); |
118 | 371 | cx->wasAllocated = PR_TRUE; |
119 | 371 | if (rv != SECSuccess) { |
120 | 0 | PORT_Free(cx); /* contains no secret info */ |
121 | 0 | cx = NULL; |
122 | 0 | } |
123 | 371 | return cx; |
124 | 371 | } |
125 | | |
126 | | /* this allows us to reuse an existing HMACContext with a new key and |
127 | | * Hash function */ |
128 | | SECStatus |
129 | | HMAC_ReInit(HMACContext *cx, const SECHashObject *hash_obj, |
130 | | const unsigned char *secret, unsigned int secret_len, PRBool isFIPS) |
131 | 0 | { |
132 | 0 | PRBool wasAllocated; |
133 | 0 | SECStatus rv; |
134 | | |
135 | | /* if we are using the same hash, keep the hash contexts and only |
136 | | * init the key */ |
137 | 0 | if ((cx->hashobj == hash_obj) && (cx->hash != NULL)) { |
138 | 0 | return hmac_initKey(cx, secret, secret_len, isFIPS); |
139 | 0 | } |
140 | | /* otherwise we destroy the contents of the context and |
141 | | * initalize it from scratch. We need to preseve the current state |
142 | | * of wasAllocated to the final destroy works correctly */ |
143 | 0 | wasAllocated = cx->wasAllocated; |
144 | 0 | cx->wasAllocated = PR_FALSE; |
145 | 0 | HMAC_Destroy(cx, PR_FALSE); |
146 | 0 | rv = HMAC_Init(cx, hash_obj, secret, secret_len, isFIPS); |
147 | 0 | if (rv != SECSuccess) { |
148 | 0 | return rv; |
149 | 0 | } |
150 | 0 | cx->wasAllocated = wasAllocated; |
151 | 0 | return SECSuccess; |
152 | 0 | } |
153 | | |
154 | | void |
155 | | HMAC_Begin(HMACContext *cx) |
156 | 6.89k | { |
157 | | /* start inner hash */ |
158 | 6.89k | cx->hashobj->begin(cx->hash); |
159 | 6.89k | cx->hashobj->update(cx->hash, cx->ipad, cx->hashobj->blocklength); |
160 | 6.89k | } |
161 | | |
162 | | void |
163 | | HMAC_Update(HMACContext *cx, const unsigned char *data, unsigned int data_len) |
164 | 18.7k | { |
165 | 18.7k | cx->hashobj->update(cx->hash, data, data_len); |
166 | 18.7k | } |
167 | | |
168 | | SECStatus |
169 | | HMAC_Finish(HMACContext *cx, unsigned char *result, unsigned int *result_len, |
170 | | unsigned int max_result_len) |
171 | 6.89k | { |
172 | 6.89k | if (max_result_len < cx->hashobj->length) { |
173 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
174 | 0 | return SECFailure; |
175 | 0 | } |
176 | | |
177 | 6.89k | cx->hashobj->end(cx->hash, result, result_len, max_result_len); |
178 | 6.89k | if (*result_len != cx->hashobj->length) |
179 | 0 | return SECFailure; |
180 | | |
181 | 6.89k | cx->hashobj->begin(cx->hash); |
182 | 6.89k | cx->hashobj->update(cx->hash, cx->opad, cx->hashobj->blocklength); |
183 | 6.89k | cx->hashobj->update(cx->hash, result, *result_len); |
184 | 6.89k | cx->hashobj->end(cx->hash, result, result_len, max_result_len); |
185 | 6.89k | return SECSuccess; |
186 | 6.89k | } |
187 | | |
188 | | HMACContext * |
189 | | HMAC_Clone(HMACContext *cx) |
190 | 0 | { |
191 | 0 | HMACContext *newcx; |
192 | |
|
193 | 0 | newcx = (HMACContext *)PORT_ZAlloc(sizeof(HMACContext)); |
194 | 0 | if (newcx == NULL) |
195 | 0 | goto loser; |
196 | | |
197 | 0 | newcx->wasAllocated = PR_TRUE; |
198 | 0 | newcx->hashobj = cx->hashobj; |
199 | 0 | newcx->hash = cx->hashobj->clone(cx->hash); |
200 | 0 | if (newcx->hash == NULL) |
201 | 0 | goto loser; |
202 | 0 | PORT_Memcpy(newcx->ipad, cx->ipad, cx->hashobj->blocklength); |
203 | 0 | PORT_Memcpy(newcx->opad, cx->opad, cx->hashobj->blocklength); |
204 | 0 | return newcx; |
205 | | |
206 | 0 | loser: |
207 | 0 | HMAC_Destroy(newcx, PR_TRUE); |
208 | 0 | return NULL; |
209 | 0 | } |