security/manager/ssl/src/nsCryptoHash.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial