// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/stl_util.h" #include "components/webcrypto/algorithm_implementation.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/nss/util_nss.h" #include "components/webcrypto/status.h" #include "components/webcrypto/webcrypto_util.h" #include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" namespace webcrypto { namespace { HASH_HashType WebCryptoAlgorithmToNSSHashType( blink::WebCryptoAlgorithmId algorithm) { switch (algorithm) { case blink::WebCryptoAlgorithmIdSha1: return HASH_AlgSHA1; case blink::WebCryptoAlgorithmIdSha256: return HASH_AlgSHA256; case blink::WebCryptoAlgorithmIdSha384: return HASH_AlgSHA384; case blink::WebCryptoAlgorithmIdSha512: return HASH_AlgSHA512; default: // Not a digest algorithm. return HASH_AlgNULL; } } // Implementation of blink::WebCryptoDigester, an internal Blink detail not // part of WebCrypto, that allows chunks of data to be streamed in before // computing a SHA-* digest (as opposed to ShaImplementation, which computes // digests over complete messages) class DigestorNSS : public blink::WebCryptoDigestor { public: explicit DigestorNSS(blink::WebCryptoAlgorithmId algorithm_id) : hash_context_(NULL), algorithm_id_(algorithm_id) {} ~DigestorNSS() override { if (!hash_context_) return; HASH_Destroy(hash_context_); hash_context_ = NULL; } bool consume(const unsigned char* data, unsigned int size) override { return ConsumeWithStatus(data, size).IsSuccess(); } Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { // Initialize everything if the object hasn't been initialized yet. if (!hash_context_) { Status error = Init(); if (!error.IsSuccess()) return error; } HASH_Update(hash_context_, data, size); return Status::Success(); } bool finish(unsigned char*& result_data, unsigned int& result_data_size) override { Status error = FinishInternal(result_, &result_data_size); if (!error.IsSuccess()) return false; result_data = result_; return true; } Status FinishWithVectorAndStatus(std::vector* result) { if (!hash_context_) return Status::ErrorUnexpected(); unsigned int result_length = HASH_ResultLenContext(hash_context_); result->resize(result_length); unsigned char* digest = vector_as_array(result); unsigned int digest_size; // ignored return FinishInternal(digest, &digest_size); } private: Status Init() { HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm_id_); if (hash_type == HASH_AlgNULL) return Status::ErrorUnsupported(); hash_context_ = HASH_Create(hash_type); if (!hash_context_) return Status::OperationError(); HASH_Begin(hash_context_); return Status::Success(); } Status FinishInternal(unsigned char* result, unsigned int* result_size) { if (!hash_context_) { Status error = Init(); if (!error.IsSuccess()) return error; } unsigned int hash_result_length = HASH_ResultLenContext(hash_context_); DCHECK_LE(hash_result_length, static_cast(HASH_LENGTH_MAX)); HASH_End(hash_context_, result, result_size, hash_result_length); if (*result_size != hash_result_length) return Status::ErrorUnexpected(); return Status::Success(); } HASHContext* hash_context_; blink::WebCryptoAlgorithmId algorithm_id_; unsigned char result_[HASH_LENGTH_MAX]; }; class ShaImplementation : public AlgorithmImplementation { public: Status Digest(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& data, std::vector* buffer) const override { DigestorNSS digestor(algorithm.id()); Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); // http://crbug.com/366427: the spec does not define any other failures for // digest, so none of the subsequent errors are spec compliant. if (!error.IsSuccess()) return error; return digestor.FinishWithVectorAndStatus(buffer); } }; } // namespace AlgorithmImplementation* CreatePlatformShaImplementation() { return new ShaImplementation(); } scoped_ptr CreatePlatformDigestor( blink::WebCryptoAlgorithmId algorithm) { return scoped_ptr(new DigestorNSS(algorithm)); } } // namespace webcrypto