Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/nsCryptoHash.cpp
Line
Count
Source (jump to first uncovered line)
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
nsCryptoHash::nsCryptoHash()
34
  : mHashContext(nullptr)
35
  , mInitialized(false)
36
0
{
37
0
}
38
39
NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash)
40
41
NS_IMETHODIMP
42
nsCryptoHash::Init(uint32_t algorithm)
43
0
{
44
0
  HASH_HashType hashType;
45
0
  switch (algorithm) {
46
0
    case nsICryptoHash::MD2:
47
0
      hashType = HASH_AlgMD2; break;
48
0
    case nsICryptoHash::MD5:
49
0
      hashType = HASH_AlgMD5; break;
50
0
    case nsICryptoHash::SHA1:
51
0
      hashType = HASH_AlgSHA1; break;
52
0
    case nsICryptoHash::SHA256:
53
0
      hashType = HASH_AlgSHA256; break;
54
0
    case nsICryptoHash::SHA384:
55
0
      hashType = HASH_AlgSHA384; break;
56
0
    case nsICryptoHash::SHA512:
57
0
      hashType = HASH_AlgSHA512; break;
58
0
    default:
59
0
      return NS_ERROR_INVALID_ARG;
60
0
  }
61
0
62
0
  if (mHashContext) {
63
0
    if (!mInitialized && HASH_GetType(mHashContext.get()) == hashType) {
64
0
      mInitialized = true;
65
0
      HASH_Begin(mHashContext.get());
66
0
      return NS_OK;
67
0
    }
68
0
69
0
    // Destroy current hash context if the type was different
70
0
    // or Finish method wasn't called.
71
0
    mHashContext = nullptr;
72
0
    mInitialized = false;
73
0
  }
74
0
75
0
  mHashContext.reset(HASH_Create(hashType));
76
0
  if (!mHashContext) {
77
0
    return NS_ERROR_INVALID_ARG;
78
0
  }
79
0
80
0
  HASH_Begin(mHashContext.get());
81
0
  mInitialized = true;
82
0
  return NS_OK;
83
0
}
84
85
NS_IMETHODIMP
86
nsCryptoHash::InitWithString(const nsACString & aAlgorithm)
87
0
{
88
0
  if (aAlgorithm.LowerCaseEqualsLiteral("md2"))
89
0
    return Init(nsICryptoHash::MD2);
90
0
91
0
  if (aAlgorithm.LowerCaseEqualsLiteral("md5"))
92
0
    return Init(nsICryptoHash::MD5);
93
0
94
0
  if (aAlgorithm.LowerCaseEqualsLiteral("sha1"))
95
0
    return Init(nsICryptoHash::SHA1);
96
0
97
0
  if (aAlgorithm.LowerCaseEqualsLiteral("sha256"))
98
0
    return Init(nsICryptoHash::SHA256);
99
0
100
0
  if (aAlgorithm.LowerCaseEqualsLiteral("sha384"))
101
0
    return Init(nsICryptoHash::SHA384);
102
0
103
0
  if (aAlgorithm.LowerCaseEqualsLiteral("sha512"))
104
0
    return Init(nsICryptoHash::SHA512);
105
0
106
0
  return NS_ERROR_INVALID_ARG;
107
0
}
108
109
NS_IMETHODIMP
110
nsCryptoHash::Update(const uint8_t *data, uint32_t len)
111
0
{
112
0
  if (!mInitialized) {
113
0
    return NS_ERROR_NOT_INITIALIZED;
114
0
  }
115
0
116
0
  HASH_Update(mHashContext.get(), data, len);
117
0
  return NS_OK;
118
0
}
119
120
NS_IMETHODIMP
121
nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen)
122
0
{
123
0
  if (!mInitialized)
124
0
    return NS_ERROR_NOT_INITIALIZED;
125
0
126
0
  if (!data)
127
0
    return NS_ERROR_INVALID_ARG;
128
0
129
0
  uint64_t n;
130
0
  nsresult rv = data->Available(&n);
131
0
  if (NS_FAILED(rv))
132
0
    return rv;
133
0
134
0
  // if the user has passed UINT32_MAX, then read
135
0
  // everything in the stream
136
0
137
0
  uint64_t len = aLen;
138
0
  if (aLen == UINT32_MAX)
139
0
    len = n;
140
0
141
0
  // So, if the stream has NO data available for the hash,
142
0
  // or if the data available is less then what the caller
143
0
  // requested, we can not fulfill the hash update.  In this
144
0
  // case, just return NS_ERROR_NOT_AVAILABLE indicating
145
0
  // that there is not enough data in the stream to satisify
146
0
  // the request.
147
0
148
0
  if (n == 0 || n < len) {
149
0
    return NS_ERROR_NOT_AVAILABLE;
150
0
  }
151
0
152
0
  char buffer[STREAM_BUFFER_SIZE];
153
0
  while (len > 0) {
154
0
    uint64_t readLimit = std::min<uint64_t>(STREAM_BUFFER_SIZE, len);
155
0
    uint32_t read;
156
0
    rv = data->Read(buffer, AssertedCast<uint32_t>(readLimit), &read);
157
0
    if (NS_FAILED(rv)) {
158
0
      return rv;
159
0
    }
160
0
161
0
    rv = Update(BitwiseCast<uint8_t*>(buffer), read);
162
0
    if (NS_FAILED(rv)) {
163
0
      return rv;
164
0
    }
165
0
166
0
    len -= read;
167
0
  }
168
0
169
0
  return NS_OK;
170
0
}
171
172
NS_IMETHODIMP
173
nsCryptoHash::Finish(bool ascii, nsACString & _retval)
174
0
{
175
0
  if (!mInitialized) {
176
0
    return NS_ERROR_NOT_INITIALIZED;
177
0
  }
178
0
179
0
  uint32_t hashLen = 0;
180
0
  unsigned char buffer[HASH_LENGTH_MAX];
181
0
  HASH_End(mHashContext.get(), buffer, &hashLen, HASH_LENGTH_MAX);
182
0
183
0
  mInitialized = false;
184
0
185
0
  if (ascii) {
186
0
    nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen);
187
0
    return Base64Encode(dataStr, _retval);
188
0
  }
189
0
190
0
  _retval.Assign(BitwiseCast<char*>(buffer), hashLen);
191
0
  return NS_OK;
192
0
}
193
194
//---------------------------------------------
195
// Implementing nsICryptoHMAC
196
//---------------------------------------------
197
198
NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC)
199
200
nsCryptoHMAC::nsCryptoHMAC()
201
  : mHMACContext(nullptr)
202
0
{
203
0
}
204
205
NS_IMETHODIMP
206
nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject)
207
0
{
208
0
  if (mHMACContext) {
209
0
    mHMACContext = nullptr;
210
0
  }
211
0
212
0
  CK_MECHANISM_TYPE mechType;
213
0
  switch (aAlgorithm) {
214
0
    case nsICryptoHMAC::MD5:
215
0
      mechType = CKM_MD5_HMAC; break;
216
0
    case nsICryptoHMAC::SHA1:
217
0
      mechType = CKM_SHA_1_HMAC; break;
218
0
    case nsICryptoHMAC::SHA256:
219
0
      mechType = CKM_SHA256_HMAC; break;
220
0
    case nsICryptoHMAC::SHA384:
221
0
      mechType = CKM_SHA384_HMAC; break;
222
0
    case nsICryptoHMAC::SHA512:
223
0
      mechType = CKM_SHA512_HMAC; break;
224
0
    default:
225
0
      return NS_ERROR_INVALID_ARG;
226
0
  }
227
0
228
0
  NS_ENSURE_ARG_POINTER(aKeyObject);
229
0
230
0
  nsresult rv;
231
0
232
0
  int16_t keyType;
233
0
  rv = aKeyObject->GetType(&keyType);
234
0
  NS_ENSURE_SUCCESS(rv, rv);
235
0
236
0
  NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG);
237
0
238
0
  PK11SymKey* key;
239
0
  // GetKeyObj doesn't addref the key
240
0
  rv = aKeyObject->GetKeyObj(&key);
241
0
  NS_ENSURE_SUCCESS(rv, rv);
242
0
243
0
  SECItem rawData;
244
0
  rawData.data = 0;
245
0
  rawData.len = 0;
246
0
  mHMACContext.reset(PK11_CreateContextBySymKey(mechType, CKA_SIGN, key,
247
0
                                                &rawData));
248
0
  NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE);
249
0
250
0
  if (PK11_DigestBegin(mHMACContext.get()) != SECSuccess) {
251
0
    return NS_ERROR_FAILURE;
252
0
  }
253
0
254
0
  return NS_OK;
255
0
}
256
257
NS_IMETHODIMP
258
nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen)
259
0
{
260
0
  if (!mHMACContext)
261
0
    return NS_ERROR_NOT_INITIALIZED;
262
0
263
0
  if (!aData)
264
0
    return NS_ERROR_INVALID_ARG;
265
0
266
0
  if (PK11_DigestOp(mHMACContext.get(), aData, aLen) != SECSuccess) {
267
0
    return NS_ERROR_FAILURE;
268
0
  }
269
0
270
0
  return NS_OK;
271
0
}
272
273
NS_IMETHODIMP
274
nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen)
275
0
{
276
0
  if (!mHMACContext)
277
0
    return NS_ERROR_NOT_INITIALIZED;
278
0
279
0
  if (!aStream)
280
0
    return NS_ERROR_INVALID_ARG;
281
0
282
0
  uint64_t n;
283
0
  nsresult rv = aStream->Available(&n);
284
0
  if (NS_FAILED(rv))
285
0
    return rv;
286
0
287
0
  // if the user has passed UINT32_MAX, then read
288
0
  // everything in the stream
289
0
290
0
  uint64_t len = aLen;
291
0
  if (aLen == UINT32_MAX)
292
0
    len = n;
293
0
294
0
  // So, if the stream has NO data available for the hash,
295
0
  // or if the data available is less then what the caller
296
0
  // requested, we can not fulfill the HMAC update.  In this
297
0
  // case, just return NS_ERROR_NOT_AVAILABLE indicating
298
0
  // that there is not enough data in the stream to satisify
299
0
  // the request.
300
0
301
0
  if (n == 0 || n < len)
302
0
    return NS_ERROR_NOT_AVAILABLE;
303
0
304
0
  char buffer[STREAM_BUFFER_SIZE];
305
0
  while (len > 0) {
306
0
    uint64_t readLimit = std::min<uint64_t>(STREAM_BUFFER_SIZE, len);
307
0
    uint32_t read;
308
0
    rv = aStream->Read(buffer, AssertedCast<uint32_t>(readLimit), &read);
309
0
    if (NS_FAILED(rv)) {
310
0
      return rv;
311
0
    }
312
0
313
0
    if (read == 0) {
314
0
      return NS_BASE_STREAM_CLOSED;
315
0
    }
316
0
317
0
    rv = Update(BitwiseCast<uint8_t*>(buffer), read);
318
0
    if (NS_FAILED(rv)) {
319
0
      return rv;
320
0
    }
321
0
322
0
    len -= read;
323
0
  }
324
0
325
0
  return NS_OK;
326
0
}
327
328
NS_IMETHODIMP
329
nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval)
330
0
{
331
0
  if (!mHMACContext)
332
0
    return NS_ERROR_NOT_INITIALIZED;
333
0
334
0
  uint32_t hashLen = 0;
335
0
  unsigned char buffer[HASH_LENGTH_MAX];
336
0
  SECStatus srv = PK11_DigestFinal(mHMACContext.get(), buffer, &hashLen,
337
0
                                   HASH_LENGTH_MAX);
338
0
  if (srv != SECSuccess) {
339
0
    return NS_ERROR_FAILURE;
340
0
  }
341
0
342
0
  if (aASCII) {
343
0
    nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen);
344
0
    return Base64Encode(dataStr, _retval);
345
0
  }
346
0
347
0
  _retval.Assign(BitwiseCast<char*>(buffer), hashLen);
348
0
  return NS_OK;
349
0
}
350
351
NS_IMETHODIMP
352
nsCryptoHMAC::Reset()
353
0
{
354
0
  if (PK11_DigestBegin(mHMACContext.get()) != SECSuccess) {
355
0
    return NS_ERROR_FAILURE;
356
0
  }
357
0
358
0
  return NS_OK;
359
0
}