1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/src/nsCryptoHash.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,404 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifdef MOZ_LOGGING 1.11 +#define FORCE_PR_LOG 1 1.12 +#endif 1.13 + 1.14 +#include <algorithm> 1.15 + 1.16 +#include "nsCryptoHash.h" 1.17 + 1.18 +#include "nsIInputStream.h" 1.19 +#include "nsIKeyModule.h" 1.20 + 1.21 +#include "nsString.h" 1.22 + 1.23 +#include "sechash.h" 1.24 +#include "pk11pub.h" 1.25 +#include "base64.h" 1.26 + 1.27 +#define NS_CRYPTO_HASH_BUFFER_SIZE 4096 1.28 + 1.29 +//--------------------------------------------- 1.30 +// Implementing nsICryptoHash 1.31 +//--------------------------------------------- 1.32 + 1.33 +nsCryptoHash::nsCryptoHash() 1.34 + : mHashContext(nullptr) 1.35 + , mInitialized(false) 1.36 +{ 1.37 +} 1.38 + 1.39 +nsCryptoHash::~nsCryptoHash() 1.40 +{ 1.41 + nsNSSShutDownPreventionLock locker; 1.42 + if (isAlreadyShutDown()) { 1.43 + return; 1.44 + } 1.45 + destructorSafeDestroyNSSReference(); 1.46 + shutdown(calledFromObject); 1.47 +} 1.48 + 1.49 +void nsCryptoHash::virtualDestroyNSSReference() 1.50 +{ 1.51 + destructorSafeDestroyNSSReference(); 1.52 +} 1.53 + 1.54 +void nsCryptoHash::destructorSafeDestroyNSSReference() 1.55 +{ 1.56 + if (mHashContext) 1.57 + HASH_Destroy(mHashContext); 1.58 + mHashContext = nullptr; 1.59 +} 1.60 + 1.61 +NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash) 1.62 + 1.63 +NS_IMETHODIMP 1.64 +nsCryptoHash::Init(uint32_t algorithm) 1.65 +{ 1.66 + nsNSSShutDownPreventionLock locker; 1.67 + 1.68 + HASH_HashType hashType = (HASH_HashType)algorithm; 1.69 + if (mHashContext) 1.70 + { 1.71 + if ((!mInitialized) && (HASH_GetType(mHashContext) == hashType)) 1.72 + { 1.73 + mInitialized = true; 1.74 + HASH_Begin(mHashContext); 1.75 + return NS_OK; 1.76 + } 1.77 + 1.78 + // Destroy current hash context if the type was different 1.79 + // or Finish method wasn't called. 1.80 + HASH_Destroy(mHashContext); 1.81 + mInitialized = false; 1.82 + } 1.83 + 1.84 + mHashContext = HASH_Create(hashType); 1.85 + if (!mHashContext) 1.86 + return NS_ERROR_INVALID_ARG; 1.87 + 1.88 + HASH_Begin(mHashContext); 1.89 + mInitialized = true; 1.90 + return NS_OK; 1.91 +} 1.92 + 1.93 +NS_IMETHODIMP 1.94 +nsCryptoHash::InitWithString(const nsACString & aAlgorithm) 1.95 +{ 1.96 + if (aAlgorithm.LowerCaseEqualsLiteral("md2")) 1.97 + return Init(nsICryptoHash::MD2); 1.98 + 1.99 + if (aAlgorithm.LowerCaseEqualsLiteral("md5")) 1.100 + return Init(nsICryptoHash::MD5); 1.101 + 1.102 + if (aAlgorithm.LowerCaseEqualsLiteral("sha1")) 1.103 + return Init(nsICryptoHash::SHA1); 1.104 + 1.105 + if (aAlgorithm.LowerCaseEqualsLiteral("sha256")) 1.106 + return Init(nsICryptoHash::SHA256); 1.107 + 1.108 + if (aAlgorithm.LowerCaseEqualsLiteral("sha384")) 1.109 + return Init(nsICryptoHash::SHA384); 1.110 + 1.111 + if (aAlgorithm.LowerCaseEqualsLiteral("sha512")) 1.112 + return Init(nsICryptoHash::SHA512); 1.113 + 1.114 + return NS_ERROR_INVALID_ARG; 1.115 +} 1.116 + 1.117 +NS_IMETHODIMP 1.118 +nsCryptoHash::Update(const uint8_t *data, uint32_t len) 1.119 +{ 1.120 + nsNSSShutDownPreventionLock locker; 1.121 + 1.122 + if (!mInitialized) 1.123 + return NS_ERROR_NOT_INITIALIZED; 1.124 + 1.125 + HASH_Update(mHashContext, data, len); 1.126 + return NS_OK; 1.127 +} 1.128 + 1.129 +NS_IMETHODIMP 1.130 +nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen) 1.131 +{ 1.132 + if (!mInitialized) 1.133 + return NS_ERROR_NOT_INITIALIZED; 1.134 + 1.135 + if (!data) 1.136 + return NS_ERROR_INVALID_ARG; 1.137 + 1.138 + uint64_t n; 1.139 + nsresult rv = data->Available(&n); 1.140 + if (NS_FAILED(rv)) 1.141 + return rv; 1.142 + 1.143 + // if the user has passed UINT32_MAX, then read 1.144 + // everything in the stream 1.145 + 1.146 + uint64_t len = aLen; 1.147 + if (aLen == UINT32_MAX) 1.148 + len = n; 1.149 + 1.150 + // So, if the stream has NO data available for the hash, 1.151 + // or if the data available is less then what the caller 1.152 + // requested, we can not fulfill the hash update. In this 1.153 + // case, just return NS_ERROR_NOT_AVAILABLE indicating 1.154 + // that there is not enough data in the stream to satisify 1.155 + // the request. 1.156 + 1.157 + if (n == 0 || n < len) 1.158 + return NS_ERROR_NOT_AVAILABLE; 1.159 + 1.160 + char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; 1.161 + uint32_t read, readLimit; 1.162 + 1.163 + while(NS_SUCCEEDED(rv) && len>0) 1.164 + { 1.165 + readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); 1.166 + 1.167 + rv = data->Read(buffer, readLimit, &read); 1.168 + 1.169 + if (NS_SUCCEEDED(rv)) 1.170 + rv = Update((const uint8_t*)buffer, read); 1.171 + 1.172 + len -= read; 1.173 + } 1.174 + 1.175 + return rv; 1.176 +} 1.177 + 1.178 +NS_IMETHODIMP 1.179 +nsCryptoHash::Finish(bool ascii, nsACString & _retval) 1.180 +{ 1.181 + nsNSSShutDownPreventionLock locker; 1.182 + 1.183 + if (!mInitialized) 1.184 + return NS_ERROR_NOT_INITIALIZED; 1.185 + 1.186 + uint32_t hashLen = 0; 1.187 + unsigned char buffer[HASH_LENGTH_MAX]; 1.188 + unsigned char* pbuffer = buffer; 1.189 + 1.190 + HASH_End(mHashContext, pbuffer, &hashLen, HASH_LENGTH_MAX); 1.191 + 1.192 + mInitialized = false; 1.193 + 1.194 + if (ascii) 1.195 + { 1.196 + char *asciiData = BTOA_DataToAscii(buffer, hashLen); 1.197 + NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); 1.198 + 1.199 + _retval.Assign(asciiData); 1.200 + PORT_Free(asciiData); 1.201 + } 1.202 + else 1.203 + { 1.204 + _retval.Assign((const char*)buffer, hashLen); 1.205 + } 1.206 + 1.207 + return NS_OK; 1.208 +} 1.209 + 1.210 +//--------------------------------------------- 1.211 +// Implementing nsICryptoHMAC 1.212 +//--------------------------------------------- 1.213 + 1.214 +NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC) 1.215 + 1.216 +nsCryptoHMAC::nsCryptoHMAC() 1.217 +{ 1.218 + mHMACContext = nullptr; 1.219 +} 1.220 + 1.221 +nsCryptoHMAC::~nsCryptoHMAC() 1.222 +{ 1.223 + nsNSSShutDownPreventionLock locker; 1.224 + if (isAlreadyShutDown()) { 1.225 + return; 1.226 + } 1.227 + destructorSafeDestroyNSSReference(); 1.228 + shutdown(calledFromObject); 1.229 +} 1.230 + 1.231 +void nsCryptoHMAC::virtualDestroyNSSReference() 1.232 +{ 1.233 + destructorSafeDestroyNSSReference(); 1.234 +} 1.235 + 1.236 +void nsCryptoHMAC::destructorSafeDestroyNSSReference() 1.237 +{ 1.238 + if (mHMACContext) 1.239 + PK11_DestroyContext(mHMACContext, true); 1.240 + mHMACContext = nullptr; 1.241 +} 1.242 + 1.243 +/* void init (in unsigned long aAlgorithm, in nsIKeyObject aKeyObject); */ 1.244 +NS_IMETHODIMP nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject) 1.245 +{ 1.246 + nsNSSShutDownPreventionLock locker; 1.247 + 1.248 + if (mHMACContext) 1.249 + { 1.250 + PK11_DestroyContext(mHMACContext, true); 1.251 + mHMACContext = nullptr; 1.252 + } 1.253 + 1.254 + CK_MECHANISM_TYPE HMACMechType; 1.255 + switch (aAlgorithm) 1.256 + { 1.257 + case nsCryptoHMAC::MD2: 1.258 + HMACMechType = CKM_MD2_HMAC; break; 1.259 + case nsCryptoHMAC::MD5: 1.260 + HMACMechType = CKM_MD5_HMAC; break; 1.261 + case nsCryptoHMAC::SHA1: 1.262 + HMACMechType = CKM_SHA_1_HMAC; break; 1.263 + case nsCryptoHMAC::SHA256: 1.264 + HMACMechType = CKM_SHA256_HMAC; break; 1.265 + case nsCryptoHMAC::SHA384: 1.266 + HMACMechType = CKM_SHA384_HMAC; break; 1.267 + case nsCryptoHMAC::SHA512: 1.268 + HMACMechType = CKM_SHA512_HMAC; break; 1.269 + default: 1.270 + return NS_ERROR_INVALID_ARG; 1.271 + } 1.272 + 1.273 + NS_ENSURE_ARG_POINTER(aKeyObject); 1.274 + 1.275 + nsresult rv; 1.276 + 1.277 + int16_t keyType; 1.278 + rv = aKeyObject->GetType(&keyType); 1.279 + NS_ENSURE_SUCCESS(rv, rv); 1.280 + 1.281 + NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG); 1.282 + 1.283 + PK11SymKey* key; 1.284 + // GetKeyObj doesn't addref the key 1.285 + rv = aKeyObject->GetKeyObj((void**)&key); 1.286 + NS_ENSURE_SUCCESS(rv, rv); 1.287 + 1.288 + SECItem rawData; 1.289 + rawData.data = 0; 1.290 + rawData.len = 0; 1.291 + mHMACContext = PK11_CreateContextBySymKey( 1.292 + HMACMechType, CKA_SIGN, key, &rawData); 1.293 + NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE); 1.294 + 1.295 + SECStatus ss = PK11_DigestBegin(mHMACContext); 1.296 + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); 1.297 + 1.298 + return NS_OK; 1.299 +} 1.300 + 1.301 +/* void update ([array, size_is (aLen), const] in octet aData, in unsigned long aLen); */ 1.302 +NS_IMETHODIMP nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen) 1.303 +{ 1.304 + nsNSSShutDownPreventionLock locker; 1.305 + 1.306 + if (!mHMACContext) 1.307 + return NS_ERROR_NOT_INITIALIZED; 1.308 + 1.309 + if (!aData) 1.310 + return NS_ERROR_INVALID_ARG; 1.311 + 1.312 + SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen); 1.313 + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); 1.314 + 1.315 + return NS_OK; 1.316 +} 1.317 + 1.318 +/* void updateFromStream (in nsIInputStream aStream, in unsigned long aLen); */ 1.319 +NS_IMETHODIMP nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen) 1.320 +{ 1.321 + if (!mHMACContext) 1.322 + return NS_ERROR_NOT_INITIALIZED; 1.323 + 1.324 + if (!aStream) 1.325 + return NS_ERROR_INVALID_ARG; 1.326 + 1.327 + uint64_t n; 1.328 + nsresult rv = aStream->Available(&n); 1.329 + if (NS_FAILED(rv)) 1.330 + return rv; 1.331 + 1.332 + // if the user has passed UINT32_MAX, then read 1.333 + // everything in the stream 1.334 + 1.335 + uint64_t len = aLen; 1.336 + if (aLen == UINT32_MAX) 1.337 + len = n; 1.338 + 1.339 + // So, if the stream has NO data available for the hash, 1.340 + // or if the data available is less then what the caller 1.341 + // requested, we can not fulfill the HMAC update. In this 1.342 + // case, just return NS_ERROR_NOT_AVAILABLE indicating 1.343 + // that there is not enough data in the stream to satisify 1.344 + // the request. 1.345 + 1.346 + if (n == 0 || n < len) 1.347 + return NS_ERROR_NOT_AVAILABLE; 1.348 + 1.349 + char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; 1.350 + uint32_t read, readLimit; 1.351 + 1.352 + while(NS_SUCCEEDED(rv) && len > 0) 1.353 + { 1.354 + readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); 1.355 + 1.356 + rv = aStream->Read(buffer, readLimit, &read); 1.357 + if (read == 0) 1.358 + return NS_BASE_STREAM_CLOSED; 1.359 + 1.360 + if (NS_SUCCEEDED(rv)) 1.361 + rv = Update((const uint8_t*)buffer, read); 1.362 + 1.363 + len -= read; 1.364 + } 1.365 + 1.366 + return rv; 1.367 +} 1.368 + 1.369 +/* ACString finish (in bool aASCII); */ 1.370 +NS_IMETHODIMP nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) 1.371 +{ 1.372 + nsNSSShutDownPreventionLock locker; 1.373 + 1.374 + if (!mHMACContext) 1.375 + return NS_ERROR_NOT_INITIALIZED; 1.376 + 1.377 + uint32_t hashLen = 0; 1.378 + unsigned char buffer[HASH_LENGTH_MAX]; 1.379 + unsigned char* pbuffer = buffer; 1.380 + 1.381 + PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX); 1.382 + if (aASCII) 1.383 + { 1.384 + char *asciiData = BTOA_DataToAscii(buffer, hashLen); 1.385 + NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); 1.386 + 1.387 + _retval.Assign(asciiData); 1.388 + PORT_Free(asciiData); 1.389 + } 1.390 + else 1.391 + { 1.392 + _retval.Assign((const char*)buffer, hashLen); 1.393 + } 1.394 + 1.395 + return NS_OK; 1.396 +} 1.397 + 1.398 +/* void reset (); */ 1.399 +NS_IMETHODIMP nsCryptoHMAC::Reset() 1.400 +{ 1.401 + nsNSSShutDownPreventionLock locker; 1.402 + 1.403 + SECStatus ss = PK11_DigestBegin(mHMACContext); 1.404 + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); 1.405 + 1.406 + return NS_OK; 1.407 +}