普通文本  |  736行  |  30.1 KB

// 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 "jwk.h"

#include <algorithm>
#include <functional>
#include <map>

#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "content/child/webcrypto/crypto_data.h"
#include "content/child/webcrypto/status.h"
#include "content/child/webcrypto/webcrypto_util.h"

// TODO(eroman): The algorithm-specific logic in this file for AES and RSA
// should be moved into the corresponding AlgorithmImplementation file. It
// exists in this file to avoid duplication between OpenSSL and NSS
// implementations.

// JSON Web Key Format (JWK)
// http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
//
// A JWK is a simple JSON dictionary with the following entries
// - "kty" (Key Type) Parameter, REQUIRED
// - <kty-specific parameters, see below>, REQUIRED
// - "use" (Key Use) Parameter, OPTIONAL
// - "key_ops" (Key Operations) Parameter, OPTIONAL
// - "alg" (Algorithm) Parameter, OPTIONAL
// - "ext" (Key Exportability), OPTIONAL
// (all other entries are ignored)
//
// OPTIONAL here means that this code does not require the entry to be present
// in the incoming JWK, because the method input parameters contain similar
// information. If the optional JWK entry is present, it will be validated
// against the corresponding input parameter for consistency and combined with
// it according to rules defined below.
//
// Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
// values are parsed out and combined with the method input parameters to
// build a Web Crypto Key:
// Web Crypto Key type            <-- (deduced)
// Web Crypto Key extractable     <-- JWK ext + input extractable
// Web Crypto Key algorithm       <-- JWK alg + input algorithm
// Web Crypto Key keyUsage        <-- JWK use, key_ops + input usage_mask
// Web Crypto Key keying material <-- kty-specific parameters
//
// Values for each JWK entry are case-sensitive and defined in
// http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18.
// Note that not all values specified by JOSE are handled by this code. Only
// handled values are listed.
// - kty (Key Type)
//   +-------+--------------------------------------------------------------+
//   | "RSA" | RSA [RFC3447]                                                |
//   | "oct" | Octet sequence (used to represent symmetric keys)            |
//   +-------+--------------------------------------------------------------+
//
// - key_ops (Key Use Details)
//   The key_ops field is an array that contains one or more strings from
//   the table below, and describes the operations for which this key may be
//   used.
//   +-------+--------------------------------------------------------------+
//   | "encrypt"    | encrypt operations                                    |
//   | "decrypt"    | decrypt operations                                    |
//   | "sign"       | sign (MAC) operations                                 |
//   | "verify"     | verify (MAC) operations                               |
//   | "wrapKey"    | key wrap                                              |
//   | "unwrapKey"  | key unwrap                                            |
//   | "deriveKey"  | key derivation                                        |
//   | "deriveBits" | key derivation                                        |
//   +-------+--------------------------------------------------------------+
//
// - use (Key Use)
//   The use field contains a single entry from the table below.
//   +-------+--------------------------------------------------------------+
//   | "sig"     | equivalent to key_ops of [sign, verify]                  |
//   | "enc"     | equivalent to key_ops of [encrypt, decrypt, wrapKey,     |
//   |           | unwrapKey, deriveKey, deriveBits]                        |
//   +-------+--------------------------------------------------------------+
//
//   NOTE: If both "use" and "key_ops" JWK members are present, the usages
//   specified by them MUST be consistent.  In particular, the "use" value
//   "sig" corresponds to "sign" and/or "verify".  The "use" value "enc"
//   corresponds to all other values defined above.  If "key_ops" values
//   corresponding to both "sig" and "enc" "use" values are present, the "use"
//   member SHOULD NOT be present, and if present, its value MUST NOT be
//   either "sig" or "enc".
//
// - ext (Key Exportability)
//   +-------+--------------------------------------------------------------+
//   | true  | Key may be exported from the trusted environment             |
//   | false | Key cannot exit the trusted environment                      |
//   +-------+--------------------------------------------------------------+
//
// - alg (Algorithm)
//   See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18
//   +--------------+-------------------------------------------------------+
//   | Digital Signature or MAC Algorithm                                   |
//   +--------------+-------------------------------------------------------+
//   | "HS1"        | HMAC using SHA-1 hash algorithm                       |
//   | "HS256"      | HMAC using SHA-256 hash algorithm                     |
//   | "HS384"      | HMAC using SHA-384 hash algorithm                     |
//   | "HS512"      | HMAC using SHA-512 hash algorithm                     |
//   | "RS1"        | RSASSA using SHA-1 hash algorithm
//   | "RS256"      | RSASSA using SHA-256 hash algorithm                   |
//   | "RS384"      | RSASSA using SHA-384 hash algorithm                   |
//   | "RS512"      | RSASSA using SHA-512 hash algorithm                   |
//   +--------------+-------------------------------------------------------|
//   | Key Management Algorithm                                             |
//   +--------------+-------------------------------------------------------+
//   | "RSA-OAEP"   | RSAES using Optimal Asymmetric Encryption Padding     |
//   |              | (OAEP) [RFC3447], with the default parameters         |
//   |              | specified by RFC3447 in Section A.2.1                 |
//   | "A128KW"     | Advanced Encryption Standard (AES) Key Wrap Algorithm |
//   |              | [RFC3394] using 128 bit keys                          |
//   | "A192KW"     | AES Key Wrap Algorithm using 192 bit keys             |
//   | "A256KW"     | AES Key Wrap Algorithm using 256 bit keys             |
//   | "A128GCM"    | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
//   |              | 128 bit keys                                          |
//   | "A192GCM"    | AES GCM using 192 bit keys                            |
//   | "A256GCM"    | AES GCM using 256 bit keys                            |
//   | "A128CBC"    | AES in Cipher Block Chaining Mode (CBC) with PKCS #5  |
//   |              | padding [NIST.800-38A]                                |
//   | "A192CBC"    | AES CBC using 192 bit keys                            |
//   | "A256CBC"    | AES CBC using 256 bit keys                            |
//   +--------------+-------------------------------------------------------+
//
// kty-specific parameters
// The value of kty determines the type and content of the keying material
// carried in the JWK to be imported.
// // - kty == "oct" (symmetric or other raw key)
//   +-------+--------------------------------------------------------------+
//   | "k"   | Contains the value of the symmetric (or other single-valued) |
//   |       | key.  It is represented as the base64url encoding of the     |
//   |       | octet sequence containing the key value.                     |
//   +-------+--------------------------------------------------------------+
// - kty == "RSA" (RSA public key)
//   +-------+--------------------------------------------------------------+
//   | "n"   | Contains the modulus value for the RSA public key.  It is    |
//   |       | represented as the base64url encoding of the value's         |
//   |       | unsigned big endian representation as an octet sequence.     |
//   +-------+--------------------------------------------------------------+
//   | "e"   | Contains the exponent value for the RSA public key.  It is   |
//   |       | represented as the base64url encoding of the value's         |
//   |       | unsigned big endian representation as an octet sequence.     |
//   +-------+--------------------------------------------------------------+
// - If key == "RSA" and the "d" parameter is present then it is a private key.
//   All the parameters above for public keys apply, as well as the following.
//   (Note that except for "d", all of these are optional):
//   +-------+--------------------------------------------------------------+
//   | "d"   | Contains the private exponent value for the RSA private key. |
//   |       | It is represented as the base64url encoding of the value's   |
//   |       | unsigned big endian representation as an octet sequence.     |
//   +-------+--------------------------------------------------------------+
//   | "p"   | Contains the first prime factor value for the RSA private    |
//   |       | key.  It is represented as the base64url encoding of the     |
//   |       | value's                                                      |
//   |       | unsigned big endian representation as an octet sequence.     |
//   +-------+--------------------------------------------------------------+
//   | "q"   | Contains the second prime factor value for the RSA private   |
//   |       | key.  It is represented as the base64url encoding of the     |
//   |       | value's unsigned big endian representation as an octet       |
//   |       | sequence.                                                    |
//   +-------+--------------------------------------------------------------+
//   | "dp"  | Contains the first factor CRT exponent value for the RSA     |
//   |       | private key.  It is represented as the base64url encoding of |
//   |       | the value's unsigned big endian representation as an octet   |
//   |       | sequence.                                                    |
//   +-------+--------------------------------------------------------------+
//   | "dq"  | Contains the second factor CRT exponent value for the RSA    |
//   |       | private key.  It is represented as the base64url encoding of |
//   |       | the value's unsigned big endian representation as an octet   |
//   |       | sequence.                                                    |
//   +-------+--------------------------------------------------------------+
//   | "dq"  | Contains the first CRT coefficient value for the RSA private |
//   |       | key.  It is represented as the base64url encoding of the     |
//   |       | value's unsigned big endian representation as an octet       |
//   |       | sequence.                                                    |
//   +-------+--------------------------------------------------------------+
//
// Consistency and conflict resolution
// The 'algorithm', 'extractable', and 'usage_mask' input parameters
// may be different than the corresponding values inside the JWK. The Web
// Crypto spec says that if a JWK value is present but is inconsistent with
// the input value, it is an error and the operation must fail. If no
// inconsistency is found then the input parameters are used.
//
// algorithm
//   If the JWK algorithm is provided, it must match the web crypto input
//   algorithm (both the algorithm ID and inner hash if applicable).
//
// extractable
//   If the JWK ext field is true but the input parameter is false, make the
//   Web Crypto Key non-extractable. Conversely, if the JWK ext field is
//   false but the input parameter is true, it is an inconsistency. If both
//   are true or both are false, use that value.
//
// usage_mask
//   The input usage_mask must be a strict subset of the interpreted JWK use
//   value, else it is judged inconsistent. In all cases the input usage_mask
//   is used as the final usage_mask.
//

namespace content {

namespace webcrypto {

namespace {

// Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
const blink::WebCryptoKeyUsageMask kJwkEncUsage =
    blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
    blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
    blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits;
// Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
const blink::WebCryptoKeyUsageMask kJwkSigUsage =
    blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;

class JwkWriter {
 public:
  JwkWriter(const std::string& algorithm,
            bool extractable,
            blink::WebCryptoKeyUsageMask usage_mask,
            const std::string& kty) {
    dict_.SetString("alg", algorithm);
    dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask));
    dict_.SetBoolean("ext", extractable);
    dict_.SetString("kty", kty);
  }

  void Set(const std::string& key, const std::string& value) {
    dict_.SetString(key, value);
  }

  void SetBase64Encoded(const std::string& key, const CryptoData& value) {
    dict_.SetString(key,
                    Base64EncodeUrlSafe(base::StringPiece(
                        reinterpret_cast<const char*>(value.bytes()),
                        value.byte_length())));
  }

  void ToBytes(std::vector<uint8_t>* utf8_bytes) {
    std::string json;
    base::JSONWriter::Write(&dict_, &json);
    utf8_bytes->assign(json.begin(), json.end());
  }

 private:
  base::DictionaryValue dict_;
};

// Extracts the required string property with key |path| from |dict| and saves
// the result to |*result|. If the property does not exist or is not a string,
// returns an error.
Status GetJwkString(base::DictionaryValue* dict,
                    const std::string& path,
                    std::string* result) {
  base::Value* value = NULL;
  if (!dict->Get(path, &value))
    return Status::ErrorJwkPropertyMissing(path);
  if (!value->GetAsString(result))
    return Status::ErrorJwkPropertyWrongType(path, "string");
  return Status::Success();
}

// Extracts the optional string property with key |path| from |dict| and saves
// the result to |*result| if it was found. If the property exists and is not a
// string, returns an error. Otherwise returns success, and sets
// |*property_exists| if it was found.
Status GetOptionalJwkString(base::DictionaryValue* dict,
                            const std::string& path,
                            std::string* result,
                            bool* property_exists) {
  *property_exists = false;
  base::Value* value = NULL;
  if (!dict->Get(path, &value))
    return Status::Success();

  if (!value->GetAsString(result))
    return Status::ErrorJwkPropertyWrongType(path, "string");

  *property_exists = true;
  return Status::Success();
}

// Extracts the optional array property with key |path| from |dict| and saves
// the result to |*result| if it was found. If the property exists and is not an
// array, returns an error. Otherwise returns success, and sets
// |*property_exists| if it was found. Note that |*result| is owned by |dict|.
Status GetOptionalJwkList(base::DictionaryValue* dict,
                          const std::string& path,
                          base::ListValue** result,
                          bool* property_exists) {
  *property_exists = false;
  base::Value* value = NULL;
  if (!dict->Get(path, &value))
    return Status::Success();

  if (!value->GetAsList(result))
    return Status::ErrorJwkPropertyWrongType(path, "list");

  *property_exists = true;
  return Status::Success();
}

// Extracts the required string property with key |path| from |dict| and saves
// the base64url-decoded bytes to |*result|. If the property does not exist or
// is not a string, or could not be base64url-decoded, returns an error.
Status GetJwkBytes(base::DictionaryValue* dict,
                   const std::string& path,
                   std::string* result) {
  std::string base64_string;
  Status status = GetJwkString(dict, path, &base64_string);
  if (status.IsError())
    return status;

  if (!Base64DecodeUrlSafe(base64_string, result))
    return Status::ErrorJwkBase64Decode(path);

  return Status::Success();
}

// Extracts the required base64url property, which is interpreted as being a
// big-endian unsigned integer.
Status GetJwkBigInteger(base::DictionaryValue* dict,
                        const std::string& path,
                        std::string* result) {
  Status status = GetJwkBytes(dict, path, result);
  if (status.IsError())
    return status;

  if (result->empty())
    return Status::ErrorJwkEmptyBigInteger(path);

  // The JWA spec says that "The octet sequence MUST utilize the minimum number
  // of octets to represent the value." This means there shouldn't be any
  // leading zeros.
  if (result->size() > 1 && (*result)[0] == 0)
    return Status::ErrorJwkBigIntegerHasLeadingZero(path);

  return Status::Success();
}

// Extracts the optional boolean property with key |path| from |dict| and saves
// the result to |*result| if it was found. If the property exists and is not a
// boolean, returns an error. Otherwise returns success, and sets
// |*property_exists| if it was found.
Status GetOptionalJwkBool(base::DictionaryValue* dict,
                          const std::string& path,
                          bool* result,
                          bool* property_exists) {
  *property_exists = false;
  base::Value* value = NULL;
  if (!dict->Get(path, &value))
    return Status::Success();

  if (!value->GetAsBoolean(result))
    return Status::ErrorJwkPropertyWrongType(path, "boolean");

  *property_exists = true;
  return Status::Success();
}

Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) {
  // JWK "ext" (optional) --> extractable parameter
  bool jwk_ext_value = false;
  bool has_jwk_ext;
  Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext);
  if (status.IsError())
    return status;
  if (has_jwk_ext && expected_extractable && !jwk_ext_value)
    return Status::ErrorJwkExtInconsistent();
  return Status::Success();
}

Status VerifyUsages(base::DictionaryValue* dict,
                    blink::WebCryptoKeyUsageMask expected_usage_mask) {
  // JWK "key_ops" (optional) --> usage_mask parameter
  base::ListValue* jwk_key_ops_value = NULL;
  bool has_jwk_key_ops;
  Status status =
      GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops);
  if (status.IsError())
    return status;
  blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0;
  if (has_jwk_key_ops) {
    status =
        GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask);
    if (status.IsError())
      return status;
    // The input usage_mask must be a subset of jwk_key_ops_mask.
    if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask))
      return Status::ErrorJwkKeyopsInconsistent();
  }

  // JWK "use" (optional) --> usage_mask parameter
  std::string jwk_use_value;
  bool has_jwk_use;
  status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use);
  if (status.IsError())
    return status;
  blink::WebCryptoKeyUsageMask jwk_use_mask = 0;
  if (has_jwk_use) {
    if (jwk_use_value == "enc")
      jwk_use_mask = kJwkEncUsage;
    else if (jwk_use_value == "sig")
      jwk_use_mask = kJwkSigUsage;
    else
      return Status::ErrorJwkUnrecognizedUse();
    // The input usage_mask must be a subset of jwk_use_mask.
    if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask))
      return Status::ErrorJwkUseInconsistent();
  }

  // If both 'key_ops' and 'use' are present, ensure they are consistent.
  if (has_jwk_key_ops && has_jwk_use &&
      !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask))
    return Status::ErrorJwkUseAndKeyopsInconsistent();

  return Status::Success();
}

Status VerifyAlg(base::DictionaryValue* dict,
                 const std::string& expected_algorithm) {
  // JWK "alg" --> algorithm parameter
  bool has_jwk_alg;
  std::string jwk_alg_value;
  Status status =
      GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg);
  if (status.IsError())
    return status;

  if (has_jwk_alg && jwk_alg_value != expected_algorithm)
    return Status::ErrorJwkAlgorithmInconsistent();

  return Status::Success();
}

Status ParseJwkCommon(const CryptoData& bytes,
                      bool expected_extractable,
                      blink::WebCryptoKeyUsageMask expected_usage_mask,
                      std::string* kty,
                      scoped_ptr<base::DictionaryValue>* dict) {
  // Parse the incoming JWK JSON.
  base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()),
                                bytes.byte_length());

  scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
  base::DictionaryValue* dict_value = NULL;

  if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
    return Status::ErrorJwkNotDictionary();

  // Release |value|, as ownership will be transferred to |dict| via
  // |dict_value|, which points to the same object as |value|.
  ignore_result(value.release());
  dict->reset(dict_value);

  // JWK "kty". Exit early if this required JWK parameter is missing.
  Status status = GetJwkString(dict_value, "kty", kty);
  if (status.IsError())
    return status;

  status = VerifyExt(dict_value, expected_extractable);
  if (status.IsError())
    return status;

  status = VerifyUsages(dict_value, expected_usage_mask);
  if (status.IsError())
    return status;

  return Status::Success();
}

Status ReadSecretKeyNoExpectedAlg(
    const CryptoData& key_data,
    bool expected_extractable,
    blink::WebCryptoKeyUsageMask expected_usage_mask,
    std::vector<uint8_t>* raw_key_data,
    scoped_ptr<base::DictionaryValue>* dict) {
  if (!key_data.byte_length())
    return Status::ErrorImportEmptyKeyData();

  std::string kty;
  Status status = ParseJwkCommon(
      key_data, expected_extractable, expected_usage_mask, &kty, dict);
  if (status.IsError())
    return status;

  if (kty != "oct")
    return Status::ErrorJwkUnexpectedKty("oct");

  std::string jwk_k_value;
  status = GetJwkBytes(dict->get(), "k", &jwk_k_value);
  if (status.IsError())
    return status;
  raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end());

  return Status::Success();
}

}  // namespace

void WriteSecretKeyJwk(const CryptoData& raw_key_data,
                       const std::string& algorithm,
                       bool extractable,
                       blink::WebCryptoKeyUsageMask usage_mask,
                       std::vector<uint8_t>* jwk_key_data) {
  JwkWriter writer(algorithm, extractable, usage_mask, "oct");
  writer.SetBase64Encoded("k", raw_key_data);
  writer.ToBytes(jwk_key_data);
}

Status ReadSecretKeyJwk(const CryptoData& key_data,
                        const std::string& expected_algorithm,
                        bool expected_extractable,
                        blink::WebCryptoKeyUsageMask expected_usage_mask,
                        std::vector<uint8_t>* raw_key_data) {
  scoped_ptr<base::DictionaryValue> dict;
  Status status = ReadSecretKeyNoExpectedAlg(
      key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict);
  if (status.IsError())
    return status;
  return VerifyAlg(dict.get(), expected_algorithm);
}

std::string MakeJwkAesAlgorithmName(const std::string& suffix,
                                    unsigned int keylen_bytes) {
  if (keylen_bytes == 16)
    return std::string("A128") + suffix;
  if (keylen_bytes == 24)
    return std::string("A192") + suffix;
  if (keylen_bytes == 32)
    return std::string("A256") + suffix;
  return std::string();
}

Status ReadAesSecretKeyJwk(const CryptoData& key_data,
                           const std::string& algorithm_name_suffix,
                           bool expected_extractable,
                           blink::WebCryptoKeyUsageMask expected_usage_mask,
                           std::vector<uint8_t>* raw_key_data) {
  scoped_ptr<base::DictionaryValue> dict;
  Status status = ReadSecretKeyNoExpectedAlg(
      key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict);
  if (status.IsError())
    return status;

  bool has_jwk_alg;
  std::string jwk_alg;
  status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg);
  if (status.IsError())
    return status;

  if (has_jwk_alg) {
    std::string expected_algorithm_name =
        MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size());

    if (jwk_alg != expected_algorithm_name) {
      // Give a different error message if the key length was wrong.
      if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) ||
          jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) ||
          jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) {
        return Status::ErrorJwkIncorrectKeyLength();
      }
      return Status::ErrorJwkAlgorithmInconsistent();
    }
  }

  return Status::Success();
}

// Writes an RSA public key to a JWK dictionary
void WriteRsaPublicKeyJwk(const CryptoData& n,
                          const CryptoData& e,
                          const std::string& algorithm,
                          bool extractable,
                          blink::WebCryptoKeyUsageMask usage_mask,
                          std::vector<uint8_t>* jwk_key_data) {
  JwkWriter writer(algorithm, extractable, usage_mask, "RSA");
  writer.SetBase64Encoded("n", n);
  writer.SetBase64Encoded("e", e);
  writer.ToBytes(jwk_key_data);
}

// Writes an RSA private key to a JWK dictionary
void WriteRsaPrivateKeyJwk(const CryptoData& n,
                           const CryptoData& e,
                           const CryptoData& d,
                           const CryptoData& p,
                           const CryptoData& q,
                           const CryptoData& dp,
                           const CryptoData& dq,
                           const CryptoData& qi,
                           const std::string& algorithm,
                           bool extractable,
                           blink::WebCryptoKeyUsageMask usage_mask,
                           std::vector<uint8_t>* jwk_key_data) {
  JwkWriter writer(algorithm, extractable, usage_mask, "RSA");

  writer.SetBase64Encoded("n", n);
  writer.SetBase64Encoded("e", e);
  writer.SetBase64Encoded("d", d);
  // Although these are "optional" in the JWA, WebCrypto spec requires them to
  // be emitted.
  writer.SetBase64Encoded("p", p);
  writer.SetBase64Encoded("q", q);
  writer.SetBase64Encoded("dp", dp);
  writer.SetBase64Encoded("dq", dq);
  writer.SetBase64Encoded("qi", qi);
  writer.ToBytes(jwk_key_data);
}

JwkRsaInfo::JwkRsaInfo() : is_private_key(false) {
}

JwkRsaInfo::~JwkRsaInfo() {
}

Status ReadRsaKeyJwk(const CryptoData& key_data,
                     const std::string& expected_algorithm,
                     bool expected_extractable,
                     blink::WebCryptoKeyUsageMask expected_usage_mask,
                     JwkRsaInfo* result) {
  if (!key_data.byte_length())
    return Status::ErrorImportEmptyKeyData();

  scoped_ptr<base::DictionaryValue> dict;
  std::string kty;
  Status status = ParseJwkCommon(
      key_data, expected_extractable, expected_usage_mask, &kty, &dict);
  if (status.IsError())
    return status;

  status = VerifyAlg(dict.get(), expected_algorithm);
  if (status.IsError())
    return status;

  if (kty != "RSA")
    return Status::ErrorJwkUnexpectedKty("RSA");

  // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
  // in the JWK, while an RSA private key must have those, plus at least a "d"
  // (private exponent) entry.
  // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
  // section 6.3.
  status = GetJwkBigInteger(dict.get(), "n", &result->n);
  if (status.IsError())
    return status;
  status = GetJwkBigInteger(dict.get(), "e", &result->e);
  if (status.IsError())
    return status;

  result->is_private_key = dict->HasKey("d");
  if (!result->is_private_key)
    return Status::Success();

  status = GetJwkBigInteger(dict.get(), "d", &result->d);
  if (status.IsError())
    return status;

  // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
  // spec. However they are required by Chromium's WebCrypto implementation.

  status = GetJwkBigInteger(dict.get(), "p", &result->p);
  if (status.IsError())
    return status;

  status = GetJwkBigInteger(dict.get(), "q", &result->q);
  if (status.IsError())
    return status;

  status = GetJwkBigInteger(dict.get(), "dp", &result->dp);
  if (status.IsError())
    return status;

  status = GetJwkBigInteger(dict.get(), "dq", &result->dq);
  if (status.IsError())
    return status;

  status = GetJwkBigInteger(dict.get(), "qi", &result->qi);
  if (status.IsError())
    return status;

  return Status::Success();
}

const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) {
  switch (hash) {
    case blink::WebCryptoAlgorithmIdSha1:
      return "HS1";
    case blink::WebCryptoAlgorithmIdSha256:
      return "HS256";
    case blink::WebCryptoAlgorithmIdSha384:
      return "HS384";
    case blink::WebCryptoAlgorithmIdSha512:
      return "HS512";
    default:
      return NULL;
  }
}

// TODO(eroman): This accepts invalid inputs. http://crbug.com/378034
bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
  std::string base64_encoded_text(input);
  std::replace(
      base64_encoded_text.begin(), base64_encoded_text.end(), '-', '+');
  std::replace(
      base64_encoded_text.begin(), base64_encoded_text.end(), '_', '/');
  base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '=');
  return base::Base64Decode(base64_encoded_text, output);
}

std::string Base64EncodeUrlSafe(const base::StringPiece& input) {
  std::string output;
  base::Base64Encode(input, &output);
  std::replace(output.begin(), output.end(), '+', '-');
  std::replace(output.begin(), output.end(), '/', '_');
  output.erase(std::remove(output.begin(), output.end(), '='), output.end());
  return output;
}

std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) {
  const base::StringPiece string_piece(
      reinterpret_cast<const char*>(vector_as_array(&input)), input.size());
  return Base64EncodeUrlSafe(string_piece);
}

}  // namespace webcrypto

}  // namespace content