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