Home | History | Annotate | Download | only in openssl
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <vector>
      6 #include <openssl/evp.h>
      7 #include <openssl/sha.h>
      8 
      9 #include "base/logging.h"
     10 #include "base/stl_util.h"
     11 #include "content/child/webcrypto/algorithm_implementation.h"
     12 #include "content/child/webcrypto/crypto_data.h"
     13 #include "content/child/webcrypto/openssl/util_openssl.h"
     14 #include "content/child/webcrypto/status.h"
     15 #include "content/child/webcrypto/webcrypto_util.h"
     16 #include "crypto/openssl_util.h"
     17 #include "crypto/scoped_openssl_types.h"
     18 
     19 namespace content {
     20 
     21 namespace webcrypto {
     22 
     23 namespace {
     24 
     25 // Implementation of blink::WebCryptoDigester, an internal Blink detail not
     26 // part of WebCrypto, that allows chunks of data to be streamed in before
     27 // computing a SHA-* digest (as opposed to ShaImplementation, which computes
     28 // digests over complete messages)
     29 class DigestorOpenSsl : public blink::WebCryptoDigestor {
     30  public:
     31   explicit DigestorOpenSsl(blink::WebCryptoAlgorithmId algorithm_id)
     32       : initialized_(false),
     33         digest_context_(EVP_MD_CTX_create()),
     34         algorithm_id_(algorithm_id) {}
     35 
     36   virtual bool consume(const unsigned char* data, unsigned int size) {
     37     return ConsumeWithStatus(data, size).IsSuccess();
     38   }
     39 
     40   Status ConsumeWithStatus(const unsigned char* data, unsigned int size) {
     41     crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
     42     Status error = Init();
     43     if (!error.IsSuccess())
     44       return error;
     45 
     46     if (!EVP_DigestUpdate(digest_context_.get(), data, size))
     47       return Status::OperationError();
     48 
     49     return Status::Success();
     50   }
     51 
     52   virtual bool finish(unsigned char*& result_data,
     53                       unsigned int& result_data_size) {
     54     Status error = FinishInternal(result_, &result_data_size);
     55     if (!error.IsSuccess())
     56       return false;
     57     result_data = result_;
     58     return true;
     59   }
     60 
     61   Status FinishWithVectorAndStatus(std::vector<uint8_t>* result) {
     62     const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get());
     63     result->resize(hash_expected_size);
     64     unsigned char* const hash_buffer = vector_as_array(result);
     65     unsigned int hash_buffer_size;  // ignored
     66     return FinishInternal(hash_buffer, &hash_buffer_size);
     67   }
     68 
     69  private:
     70   Status Init() {
     71     if (initialized_)
     72       return Status::Success();
     73 
     74     const EVP_MD* digest_algorithm = GetDigest(algorithm_id_);
     75     if (!digest_algorithm)
     76       return Status::ErrorUnexpected();
     77 
     78     if (!digest_context_.get())
     79       return Status::OperationError();
     80 
     81     if (!EVP_DigestInit_ex(digest_context_.get(), digest_algorithm, NULL))
     82       return Status::OperationError();
     83 
     84     initialized_ = true;
     85     return Status::Success();
     86   }
     87 
     88   Status FinishInternal(unsigned char* result, unsigned int* result_size) {
     89     crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
     90     Status error = Init();
     91     if (!error.IsSuccess())
     92       return error;
     93 
     94     const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get());
     95     if (hash_expected_size <= 0)
     96       return Status::ErrorUnexpected();
     97     DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE);
     98 
     99     if (!EVP_DigestFinal_ex(digest_context_.get(), result, result_size) ||
    100         static_cast<int>(*result_size) != hash_expected_size)
    101       return Status::OperationError();
    102 
    103     return Status::Success();
    104   }
    105 
    106   bool initialized_;
    107   crypto::ScopedEVP_MD_CTX digest_context_;
    108   blink::WebCryptoAlgorithmId algorithm_id_;
    109   unsigned char result_[EVP_MAX_MD_SIZE];
    110 };
    111 
    112 class ShaImplementation : public AlgorithmImplementation {
    113  public:
    114   virtual Status Digest(const blink::WebCryptoAlgorithm& algorithm,
    115                         const CryptoData& data,
    116                         std::vector<uint8_t>* buffer) const OVERRIDE {
    117     DigestorOpenSsl digestor(algorithm.id());
    118     Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length());
    119     // http://crbug.com/366427: the spec does not define any other failures for
    120     // digest, so none of the subsequent errors are spec compliant.
    121     if (!error.IsSuccess())
    122       return error;
    123     return digestor.FinishWithVectorAndStatus(buffer);
    124   }
    125 };
    126 
    127 }  // namespace
    128 
    129 AlgorithmImplementation* CreatePlatformShaImplementation() {
    130   return new ShaImplementation();
    131 }
    132 
    133 scoped_ptr<blink::WebCryptoDigestor> CreatePlatformDigestor(
    134     blink::WebCryptoAlgorithmId algorithm) {
    135   return scoped_ptr<blink::WebCryptoDigestor>(new DigestorOpenSsl(algorithm));
    136 }
    137 
    138 }  // namespace webcrypto
    139 
    140 }  // namespace content
    141