|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifdef MOZ_LOGGING |
|
8 #define FORCE_PR_LOG 1 |
|
9 #endif |
|
10 |
|
11 #include <algorithm> |
|
12 |
|
13 #include "nsCryptoHash.h" |
|
14 |
|
15 #include "nsIInputStream.h" |
|
16 #include "nsIKeyModule.h" |
|
17 |
|
18 #include "nsString.h" |
|
19 |
|
20 #include "sechash.h" |
|
21 #include "pk11pub.h" |
|
22 #include "base64.h" |
|
23 |
|
24 #define NS_CRYPTO_HASH_BUFFER_SIZE 4096 |
|
25 |
|
26 //--------------------------------------------- |
|
27 // Implementing nsICryptoHash |
|
28 //--------------------------------------------- |
|
29 |
|
30 nsCryptoHash::nsCryptoHash() |
|
31 : mHashContext(nullptr) |
|
32 , mInitialized(false) |
|
33 { |
|
34 } |
|
35 |
|
36 nsCryptoHash::~nsCryptoHash() |
|
37 { |
|
38 nsNSSShutDownPreventionLock locker; |
|
39 if (isAlreadyShutDown()) { |
|
40 return; |
|
41 } |
|
42 destructorSafeDestroyNSSReference(); |
|
43 shutdown(calledFromObject); |
|
44 } |
|
45 |
|
46 void nsCryptoHash::virtualDestroyNSSReference() |
|
47 { |
|
48 destructorSafeDestroyNSSReference(); |
|
49 } |
|
50 |
|
51 void nsCryptoHash::destructorSafeDestroyNSSReference() |
|
52 { |
|
53 if (mHashContext) |
|
54 HASH_Destroy(mHashContext); |
|
55 mHashContext = nullptr; |
|
56 } |
|
57 |
|
58 NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash) |
|
59 |
|
60 NS_IMETHODIMP |
|
61 nsCryptoHash::Init(uint32_t algorithm) |
|
62 { |
|
63 nsNSSShutDownPreventionLock locker; |
|
64 |
|
65 HASH_HashType hashType = (HASH_HashType)algorithm; |
|
66 if (mHashContext) |
|
67 { |
|
68 if ((!mInitialized) && (HASH_GetType(mHashContext) == hashType)) |
|
69 { |
|
70 mInitialized = true; |
|
71 HASH_Begin(mHashContext); |
|
72 return NS_OK; |
|
73 } |
|
74 |
|
75 // Destroy current hash context if the type was different |
|
76 // or Finish method wasn't called. |
|
77 HASH_Destroy(mHashContext); |
|
78 mInitialized = false; |
|
79 } |
|
80 |
|
81 mHashContext = HASH_Create(hashType); |
|
82 if (!mHashContext) |
|
83 return NS_ERROR_INVALID_ARG; |
|
84 |
|
85 HASH_Begin(mHashContext); |
|
86 mInitialized = true; |
|
87 return NS_OK; |
|
88 } |
|
89 |
|
90 NS_IMETHODIMP |
|
91 nsCryptoHash::InitWithString(const nsACString & aAlgorithm) |
|
92 { |
|
93 if (aAlgorithm.LowerCaseEqualsLiteral("md2")) |
|
94 return Init(nsICryptoHash::MD2); |
|
95 |
|
96 if (aAlgorithm.LowerCaseEqualsLiteral("md5")) |
|
97 return Init(nsICryptoHash::MD5); |
|
98 |
|
99 if (aAlgorithm.LowerCaseEqualsLiteral("sha1")) |
|
100 return Init(nsICryptoHash::SHA1); |
|
101 |
|
102 if (aAlgorithm.LowerCaseEqualsLiteral("sha256")) |
|
103 return Init(nsICryptoHash::SHA256); |
|
104 |
|
105 if (aAlgorithm.LowerCaseEqualsLiteral("sha384")) |
|
106 return Init(nsICryptoHash::SHA384); |
|
107 |
|
108 if (aAlgorithm.LowerCaseEqualsLiteral("sha512")) |
|
109 return Init(nsICryptoHash::SHA512); |
|
110 |
|
111 return NS_ERROR_INVALID_ARG; |
|
112 } |
|
113 |
|
114 NS_IMETHODIMP |
|
115 nsCryptoHash::Update(const uint8_t *data, uint32_t len) |
|
116 { |
|
117 nsNSSShutDownPreventionLock locker; |
|
118 |
|
119 if (!mInitialized) |
|
120 return NS_ERROR_NOT_INITIALIZED; |
|
121 |
|
122 HASH_Update(mHashContext, data, len); |
|
123 return NS_OK; |
|
124 } |
|
125 |
|
126 NS_IMETHODIMP |
|
127 nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen) |
|
128 { |
|
129 if (!mInitialized) |
|
130 return NS_ERROR_NOT_INITIALIZED; |
|
131 |
|
132 if (!data) |
|
133 return NS_ERROR_INVALID_ARG; |
|
134 |
|
135 uint64_t n; |
|
136 nsresult rv = data->Available(&n); |
|
137 if (NS_FAILED(rv)) |
|
138 return rv; |
|
139 |
|
140 // if the user has passed UINT32_MAX, then read |
|
141 // everything in the stream |
|
142 |
|
143 uint64_t len = aLen; |
|
144 if (aLen == UINT32_MAX) |
|
145 len = n; |
|
146 |
|
147 // So, if the stream has NO data available for the hash, |
|
148 // or if the data available is less then what the caller |
|
149 // requested, we can not fulfill the hash update. In this |
|
150 // case, just return NS_ERROR_NOT_AVAILABLE indicating |
|
151 // that there is not enough data in the stream to satisify |
|
152 // the request. |
|
153 |
|
154 if (n == 0 || n < len) |
|
155 return NS_ERROR_NOT_AVAILABLE; |
|
156 |
|
157 char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; |
|
158 uint32_t read, readLimit; |
|
159 |
|
160 while(NS_SUCCEEDED(rv) && len>0) |
|
161 { |
|
162 readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); |
|
163 |
|
164 rv = data->Read(buffer, readLimit, &read); |
|
165 |
|
166 if (NS_SUCCEEDED(rv)) |
|
167 rv = Update((const uint8_t*)buffer, read); |
|
168 |
|
169 len -= read; |
|
170 } |
|
171 |
|
172 return rv; |
|
173 } |
|
174 |
|
175 NS_IMETHODIMP |
|
176 nsCryptoHash::Finish(bool ascii, nsACString & _retval) |
|
177 { |
|
178 nsNSSShutDownPreventionLock locker; |
|
179 |
|
180 if (!mInitialized) |
|
181 return NS_ERROR_NOT_INITIALIZED; |
|
182 |
|
183 uint32_t hashLen = 0; |
|
184 unsigned char buffer[HASH_LENGTH_MAX]; |
|
185 unsigned char* pbuffer = buffer; |
|
186 |
|
187 HASH_End(mHashContext, pbuffer, &hashLen, HASH_LENGTH_MAX); |
|
188 |
|
189 mInitialized = false; |
|
190 |
|
191 if (ascii) |
|
192 { |
|
193 char *asciiData = BTOA_DataToAscii(buffer, hashLen); |
|
194 NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); |
|
195 |
|
196 _retval.Assign(asciiData); |
|
197 PORT_Free(asciiData); |
|
198 } |
|
199 else |
|
200 { |
|
201 _retval.Assign((const char*)buffer, hashLen); |
|
202 } |
|
203 |
|
204 return NS_OK; |
|
205 } |
|
206 |
|
207 //--------------------------------------------- |
|
208 // Implementing nsICryptoHMAC |
|
209 //--------------------------------------------- |
|
210 |
|
211 NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC) |
|
212 |
|
213 nsCryptoHMAC::nsCryptoHMAC() |
|
214 { |
|
215 mHMACContext = nullptr; |
|
216 } |
|
217 |
|
218 nsCryptoHMAC::~nsCryptoHMAC() |
|
219 { |
|
220 nsNSSShutDownPreventionLock locker; |
|
221 if (isAlreadyShutDown()) { |
|
222 return; |
|
223 } |
|
224 destructorSafeDestroyNSSReference(); |
|
225 shutdown(calledFromObject); |
|
226 } |
|
227 |
|
228 void nsCryptoHMAC::virtualDestroyNSSReference() |
|
229 { |
|
230 destructorSafeDestroyNSSReference(); |
|
231 } |
|
232 |
|
233 void nsCryptoHMAC::destructorSafeDestroyNSSReference() |
|
234 { |
|
235 if (mHMACContext) |
|
236 PK11_DestroyContext(mHMACContext, true); |
|
237 mHMACContext = nullptr; |
|
238 } |
|
239 |
|
240 /* void init (in unsigned long aAlgorithm, in nsIKeyObject aKeyObject); */ |
|
241 NS_IMETHODIMP nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject) |
|
242 { |
|
243 nsNSSShutDownPreventionLock locker; |
|
244 |
|
245 if (mHMACContext) |
|
246 { |
|
247 PK11_DestroyContext(mHMACContext, true); |
|
248 mHMACContext = nullptr; |
|
249 } |
|
250 |
|
251 CK_MECHANISM_TYPE HMACMechType; |
|
252 switch (aAlgorithm) |
|
253 { |
|
254 case nsCryptoHMAC::MD2: |
|
255 HMACMechType = CKM_MD2_HMAC; break; |
|
256 case nsCryptoHMAC::MD5: |
|
257 HMACMechType = CKM_MD5_HMAC; break; |
|
258 case nsCryptoHMAC::SHA1: |
|
259 HMACMechType = CKM_SHA_1_HMAC; break; |
|
260 case nsCryptoHMAC::SHA256: |
|
261 HMACMechType = CKM_SHA256_HMAC; break; |
|
262 case nsCryptoHMAC::SHA384: |
|
263 HMACMechType = CKM_SHA384_HMAC; break; |
|
264 case nsCryptoHMAC::SHA512: |
|
265 HMACMechType = CKM_SHA512_HMAC; break; |
|
266 default: |
|
267 return NS_ERROR_INVALID_ARG; |
|
268 } |
|
269 |
|
270 NS_ENSURE_ARG_POINTER(aKeyObject); |
|
271 |
|
272 nsresult rv; |
|
273 |
|
274 int16_t keyType; |
|
275 rv = aKeyObject->GetType(&keyType); |
|
276 NS_ENSURE_SUCCESS(rv, rv); |
|
277 |
|
278 NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG); |
|
279 |
|
280 PK11SymKey* key; |
|
281 // GetKeyObj doesn't addref the key |
|
282 rv = aKeyObject->GetKeyObj((void**)&key); |
|
283 NS_ENSURE_SUCCESS(rv, rv); |
|
284 |
|
285 SECItem rawData; |
|
286 rawData.data = 0; |
|
287 rawData.len = 0; |
|
288 mHMACContext = PK11_CreateContextBySymKey( |
|
289 HMACMechType, CKA_SIGN, key, &rawData); |
|
290 NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE); |
|
291 |
|
292 SECStatus ss = PK11_DigestBegin(mHMACContext); |
|
293 NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); |
|
294 |
|
295 return NS_OK; |
|
296 } |
|
297 |
|
298 /* void update ([array, size_is (aLen), const] in octet aData, in unsigned long aLen); */ |
|
299 NS_IMETHODIMP nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen) |
|
300 { |
|
301 nsNSSShutDownPreventionLock locker; |
|
302 |
|
303 if (!mHMACContext) |
|
304 return NS_ERROR_NOT_INITIALIZED; |
|
305 |
|
306 if (!aData) |
|
307 return NS_ERROR_INVALID_ARG; |
|
308 |
|
309 SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen); |
|
310 NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); |
|
311 |
|
312 return NS_OK; |
|
313 } |
|
314 |
|
315 /* void updateFromStream (in nsIInputStream aStream, in unsigned long aLen); */ |
|
316 NS_IMETHODIMP nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen) |
|
317 { |
|
318 if (!mHMACContext) |
|
319 return NS_ERROR_NOT_INITIALIZED; |
|
320 |
|
321 if (!aStream) |
|
322 return NS_ERROR_INVALID_ARG; |
|
323 |
|
324 uint64_t n; |
|
325 nsresult rv = aStream->Available(&n); |
|
326 if (NS_FAILED(rv)) |
|
327 return rv; |
|
328 |
|
329 // if the user has passed UINT32_MAX, then read |
|
330 // everything in the stream |
|
331 |
|
332 uint64_t len = aLen; |
|
333 if (aLen == UINT32_MAX) |
|
334 len = n; |
|
335 |
|
336 // So, if the stream has NO data available for the hash, |
|
337 // or if the data available is less then what the caller |
|
338 // requested, we can not fulfill the HMAC update. In this |
|
339 // case, just return NS_ERROR_NOT_AVAILABLE indicating |
|
340 // that there is not enough data in the stream to satisify |
|
341 // the request. |
|
342 |
|
343 if (n == 0 || n < len) |
|
344 return NS_ERROR_NOT_AVAILABLE; |
|
345 |
|
346 char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; |
|
347 uint32_t read, readLimit; |
|
348 |
|
349 while(NS_SUCCEEDED(rv) && len > 0) |
|
350 { |
|
351 readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); |
|
352 |
|
353 rv = aStream->Read(buffer, readLimit, &read); |
|
354 if (read == 0) |
|
355 return NS_BASE_STREAM_CLOSED; |
|
356 |
|
357 if (NS_SUCCEEDED(rv)) |
|
358 rv = Update((const uint8_t*)buffer, read); |
|
359 |
|
360 len -= read; |
|
361 } |
|
362 |
|
363 return rv; |
|
364 } |
|
365 |
|
366 /* ACString finish (in bool aASCII); */ |
|
367 NS_IMETHODIMP nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) |
|
368 { |
|
369 nsNSSShutDownPreventionLock locker; |
|
370 |
|
371 if (!mHMACContext) |
|
372 return NS_ERROR_NOT_INITIALIZED; |
|
373 |
|
374 uint32_t hashLen = 0; |
|
375 unsigned char buffer[HASH_LENGTH_MAX]; |
|
376 unsigned char* pbuffer = buffer; |
|
377 |
|
378 PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX); |
|
379 if (aASCII) |
|
380 { |
|
381 char *asciiData = BTOA_DataToAscii(buffer, hashLen); |
|
382 NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); |
|
383 |
|
384 _retval.Assign(asciiData); |
|
385 PORT_Free(asciiData); |
|
386 } |
|
387 else |
|
388 { |
|
389 _retval.Assign((const char*)buffer, hashLen); |
|
390 } |
|
391 |
|
392 return NS_OK; |
|
393 } |
|
394 |
|
395 /* void reset (); */ |
|
396 NS_IMETHODIMP nsCryptoHMAC::Reset() |
|
397 { |
|
398 nsNSSShutDownPreventionLock locker; |
|
399 |
|
400 SECStatus ss = PK11_DigestBegin(mHMACContext); |
|
401 NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); |
|
402 |
|
403 return NS_OK; |
|
404 } |