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 "content/child/webcrypto/crypto_data.h"
6#include "content/child/webcrypto/openssl/key_openssl.h"
7#include "content/child/webcrypto/openssl/rsa_key_openssl.h"
8#include "content/child/webcrypto/openssl/util_openssl.h"
9#include "content/child/webcrypto/status.h"
10#include "crypto/openssl_util.h"
11#include "crypto/scoped_openssl_types.h"
12#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
13
14namespace content {
15
16namespace webcrypto {
17
18namespace {
19
20// Extracts the OpenSSL key and digest from a WebCrypto key. The returned
21// pointers will remain valid as long as |key| is alive.
22Status GetPKeyAndDigest(const blink::WebCryptoKey& key,
23                        EVP_PKEY** pkey,
24                        const EVP_MD** digest) {
25  *pkey = AsymKeyOpenSsl::Cast(key)->key();
26
27  *digest = GetDigest(key.algorithm().rsaHashedParams()->hash().id());
28  if (!*digest)
29    return Status::ErrorUnsupported();
30
31  return Status::Success();
32}
33
34class RsaSsaImplementation : public RsaHashedAlgorithm {
35 public:
36  RsaSsaImplementation()
37      : RsaHashedAlgorithm(blink::WebCryptoKeyUsageVerify,
38                           blink::WebCryptoKeyUsageSign) {}
39
40  virtual const char* GetJwkAlgorithm(
41      const blink::WebCryptoAlgorithmId hash) const OVERRIDE {
42    switch (hash) {
43      case blink::WebCryptoAlgorithmIdSha1:
44        return "RS1";
45      case blink::WebCryptoAlgorithmIdSha256:
46        return "RS256";
47      case blink::WebCryptoAlgorithmIdSha384:
48        return "RS384";
49      case blink::WebCryptoAlgorithmIdSha512:
50        return "RS512";
51      default:
52        return NULL;
53    }
54  }
55
56  virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm,
57                      const blink::WebCryptoKey& key,
58                      const CryptoData& data,
59                      std::vector<uint8_t>* buffer) const OVERRIDE {
60    if (key.type() != blink::WebCryptoKeyTypePrivate)
61      return Status::ErrorUnexpectedKeyType();
62
63    crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
64    crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create());
65
66    EVP_PKEY* private_key = NULL;
67    const EVP_MD* digest = NULL;
68    Status status = GetPKeyAndDigest(key, &private_key, &digest);
69    if (status.IsError())
70      return status;
71
72    // NOTE: A call to EVP_DigestSignFinal() with a NULL second parameter
73    // returns a maximum allocation size, while the call without a NULL returns
74    // the real one, which may be smaller.
75    size_t sig_len = 0;
76    if (!ctx.get() ||
77        !EVP_DigestSignInit(ctx.get(), NULL, digest, NULL, private_key) ||
78        !EVP_DigestSignUpdate(ctx.get(), data.bytes(), data.byte_length()) ||
79        !EVP_DigestSignFinal(ctx.get(), NULL, &sig_len)) {
80      return Status::OperationError();
81    }
82
83    buffer->resize(sig_len);
84    if (!EVP_DigestSignFinal(ctx.get(), &buffer->front(), &sig_len))
85      return Status::OperationError();
86
87    buffer->resize(sig_len);
88    return Status::Success();
89  }
90
91  virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm,
92                        const blink::WebCryptoKey& key,
93                        const CryptoData& signature,
94                        const CryptoData& data,
95                        bool* signature_match) const OVERRIDE {
96    if (key.type() != blink::WebCryptoKeyTypePublic)
97      return Status::ErrorUnexpectedKeyType();
98
99    crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
100    crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create());
101
102    EVP_PKEY* public_key = NULL;
103    const EVP_MD* digest = NULL;
104    Status status = GetPKeyAndDigest(key, &public_key, &digest);
105    if (status.IsError())
106      return status;
107
108    if (!EVP_DigestVerifyInit(ctx.get(), NULL, digest, NULL, public_key))
109      return Status::OperationError();
110
111    if (!EVP_DigestVerifyUpdate(ctx.get(), data.bytes(), data.byte_length())) {
112      return Status::OperationError();
113    }
114
115    // Note that the return value can be:
116    //   1 --> Success
117    //   0 --> Verification failed
118    //  <0 --> Operation error
119    int rv = EVP_DigestVerifyFinal(
120        ctx.get(), signature.bytes(), signature.byte_length());
121    *signature_match = rv == 1;
122    return rv >= 0 ? Status::Success() : Status::OperationError();
123  }
124};
125
126}  // namespace
127
128AlgorithmImplementation* CreatePlatformRsaSsaImplementation() {
129  return new RsaSsaImplementation;
130}
131
132}  // namespace webcrypto
133
134}  // namespace content
135