security/manager/ssl/src/nsCryptoHash.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:ba5b630a206e
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 }

mercurial