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