root/content/child/webcrypto/jwk.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateRsaSsaImportAlgorithm
  2. CreateRsaOaepImportAlgorithm
  3. required_key_length_bytes_
  4. required_key_length_bytes_
  5. required_key_length_bytes_
  6. CreateImportAlgorithm
  7. IsInvalidKeyByteLength
  8. GetAlgorithmInfo
  9. BindAlgorithmId
  10. ImportAlgorithmsConsistent
  11. GetJwkString
  12. GetOptionalJwkString
  13. GetOptionalJwkList
  14. GetJwkBytes
  15. GetOptionalJwkBool
  16. ContainsKeyUsages
  17. WriteSecretKey
  18. WriteRsaPublicKey
  19. WriteKeyOps
  20. WriteExt
  21. WriteAlg
  22. IsRsaPublicKey
  23. ToPlatformPublicKey
  24. ImportKeyJwk
  25. ExportKeyJwk

// 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 <algorithm>
#include <functional>
#include <map>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/strings/stringprintf.h"
#include "content/child/webcrypto/crypto_data.h"
#include "content/child/webcrypto/platform_crypto.h"
#include "content/child/webcrypto/shared_crypto.h"
#include "content/child/webcrypto/status.h"
#include "content/child/webcrypto/webcrypto_util.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"

namespace content {

namespace webcrypto {

namespace {

// Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a
// hash_id that is not a SHA*.
blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm(
    blink::WebCryptoAlgorithmId hash_id) {
  return CreateRsaHashedImportAlgorithm(
      blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id);
}

// Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id
// that is not a SHA*.
blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm(
    blink::WebCryptoAlgorithmId hash_id) {
  return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep,
                                        hash_id);
}

// Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
// TODO(padolph): Add 'deriveBits' once supported by Blink.
const blink::WebCryptoKeyUsageMask kJwkEncUsage =
    blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
    blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
    blink::WebCryptoKeyUsageDeriveKey;
// Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
const blink::WebCryptoKeyUsageMask kJwkSigUsage =
    blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;

typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();

class JwkAlgorithmInfo {
 public:
  JwkAlgorithmInfo()
      : creation_func_(NULL),
        required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}

  explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
      : creation_func_(algorithm_creation_func),
        required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}

  JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func,
                   unsigned int required_key_length_bits)
      : creation_func_(algorithm_creation_func),
        required_key_length_bytes_(required_key_length_bits / 8) {
    DCHECK_EQ(0u, required_key_length_bits % 8);
  }

  bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const {
    *algorithm = creation_func_();
    return !algorithm->isNull();
  }

  bool IsInvalidKeyByteLength(size_t byte_length) const {
    if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT)
      return false;
    return required_key_length_bytes_ != byte_length;
  }

 private:
  enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX };

  AlgorithmCreationFunc creation_func_;

  // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT.
  unsigned int required_key_length_bytes_;
};

typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap;

class JwkAlgorithmRegistry {
 public:
  JwkAlgorithmRegistry() {
    // TODO(eroman):
    // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20
    // says HMAC with SHA-2 should have a key size at least as large as the
    // hash output.
    alg_to_info_["HS1"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha1>);
    alg_to_info_["HS256"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha256>);
    alg_to_info_["HS384"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha384>);
    alg_to_info_["HS512"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha512>);
    alg_to_info_["RS1"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha1>);
    alg_to_info_["RS256"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha256>);
    alg_to_info_["RS384"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha384>);
    alg_to_info_["RS512"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha512>);
    alg_to_info_["RSA1_5"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm,
                         blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>);
    alg_to_info_["RSA-OAEP"] =
        JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
                                          blink::WebCryptoAlgorithmIdSha1>);
    alg_to_info_["A128KW"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
        128);
    alg_to_info_["A192KW"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
        192);
    alg_to_info_["A256KW"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
        256);
    alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
        128);
    alg_to_info_["A192GCM"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
        192);
    alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
        256);
    alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
        128);
    alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
        192);
    alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
        &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
        256);
  }

  // Returns NULL if the algorithm name was not registered.
  const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const {
    const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg);
    if (pos == alg_to_info_.end())
      return NULL;
    return &pos->second;
  }

 private:
  // Binds a WebCryptoAlgorithmId value to a compatible factory function.
  typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)(
      blink::WebCryptoAlgorithmId);
  template <FuncWithWebCryptoAlgIdArg func,
            blink::WebCryptoAlgorithmId algorithm_id>
  static blink::WebCryptoAlgorithm BindAlgorithmId() {
    return func(algorithm_id);
  }

  JwkAlgorithmInfoMap alg_to_info_;
};

base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry =
    LAZY_INSTANCE_INITIALIZER;

bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1,
                                const blink::WebCryptoAlgorithm& alg2) {
  DCHECK(!alg1.isNull());
  DCHECK(!alg2.isNull());
  if (alg1.id() != alg2.id())
    return false;
  if (alg1.paramsType() != alg2.paramsType())
    return false;
  switch (alg1.paramsType()) {
    case blink::WebCryptoAlgorithmParamsTypeNone:
      return true;
    case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams:
      return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(),
                                        alg2.rsaHashedImportParams()->hash());
    case blink::WebCryptoAlgorithmParamsTypeHmacImportParams:
      return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(),
                                        alg2.hmacImportParams()->hash());
    default:
      return false;
  }
}

// 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 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();
}

// Returns true if the set bits in b make up a subset of the set bits in a.
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
                       blink::WebCryptoKeyUsageMask b) {
  return (a & b) == b;
}

// Writes a secret/symmetric key to a JWK dictionary.
void WriteSecretKey(const blink::WebArrayBuffer& raw_key,
                    base::DictionaryValue* jwk_dict) {
  DCHECK(jwk_dict);
  jwk_dict->SetString("kty", "oct");
  // For a secret/symmetric key, the only extra JWK field is 'k', containing the
  // base64url encoding of the raw key.
  DCHECK(!raw_key.isNull());
  DCHECK(raw_key.data());
  DCHECK(raw_key.byteLength());
  unsigned int key_length_bytes = raw_key.byteLength();
  const base::StringPiece key_str(static_cast<const char*>(raw_key.data()),
                                  key_length_bytes);
  jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str));
}

// Writes an RSA public key to a JWK dictionary
void WriteRsaPublicKey(const std::vector<uint8>& modulus,
                       const std::vector<uint8>& public_exponent,
                       base::DictionaryValue* jwk_dict) {
  DCHECK(jwk_dict);
  DCHECK(modulus.size());
  DCHECK(public_exponent.size());
  jwk_dict->SetString("kty", "RSA");
  jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus));
  jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
}

// Writes a Web Crypto usage mask to a JWK dictionary.
void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages,
                 base::DictionaryValue* jwk_dict) {
  jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages));
}

// Writes a Web Crypto extractable value to a JWK dictionary.
void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) {
  jwk_dict->SetBoolean("ext", extractable);
}

// Writes a Web Crypto algorithm to a JWK dictionary.
Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm,
                base::DictionaryValue* jwk_dict) {
  switch (algorithm.paramsType()) {
    case blink::WebCryptoKeyAlgorithmParamsTypeAes: {
      DCHECK(algorithm.aesParams());
      const char* aes_prefix = "";
      switch (algorithm.aesParams()->lengthBits()) {
        case 128:
          aes_prefix = "A128";
          break;
        case 192:
          aes_prefix = "A192";
          break;
        case 256:
          aes_prefix = "A256";
          break;
        default:
          NOTREACHED();  // bad key length means algorithm was built improperly
          return Status::ErrorUnexpected();
      }
      const char* aes_suffix = "";
      switch (algorithm.id()) {
        case blink::WebCryptoAlgorithmIdAesCbc:
          aes_suffix = "CBC";
          break;
        case blink::WebCryptoAlgorithmIdAesCtr:
          aes_suffix = "CTR";
          break;
        case blink::WebCryptoAlgorithmIdAesGcm:
          aes_suffix = "GCM";
          break;
        case blink::WebCryptoAlgorithmIdAesKw:
          aes_suffix = "KW";
          break;
        default:
          return Status::ErrorUnsupported();
      }
      jwk_dict->SetString("alg",
                          base::StringPrintf("%s%s", aes_prefix, aes_suffix));
      break;
    }
    case blink::WebCryptoKeyAlgorithmParamsTypeHmac: {
      DCHECK(algorithm.hmacParams());
      switch (algorithm.hmacParams()->hash().id()) {
        case blink::WebCryptoAlgorithmIdSha1:
          jwk_dict->SetString("alg", "HS1");
          break;
        case blink::WebCryptoAlgorithmIdSha256:
          jwk_dict->SetString("alg", "HS256");
          break;
        case blink::WebCryptoAlgorithmIdSha384:
          jwk_dict->SetString("alg", "HS384");
          break;
        case blink::WebCryptoAlgorithmIdSha512:
          jwk_dict->SetString("alg", "HS512");
          break;
        default:
          NOTREACHED();
          return Status::ErrorUnexpected();
      }
      break;
    }
    case blink::WebCryptoKeyAlgorithmParamsTypeRsa:
      switch (algorithm.id()) {
        case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
          jwk_dict->SetString("alg", "RSA1_5");
          break;
        default:
          NOTREACHED();
          return Status::ErrorUnexpected();
      }
      break;
    case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed:
      switch (algorithm.rsaHashedParams()->hash().id()) {
        case blink::WebCryptoAlgorithmIdRsaOaep:
          jwk_dict->SetString("alg", "RSA-OAEP");
          break;
        case blink::WebCryptoAlgorithmIdSha1:
          jwk_dict->SetString("alg", "RS1");
          break;
        case blink::WebCryptoAlgorithmIdSha256:
          jwk_dict->SetString("alg", "RS256");
          break;
        case blink::WebCryptoAlgorithmIdSha384:
          jwk_dict->SetString("alg", "RS384");
          break;
        case blink::WebCryptoAlgorithmIdSha512:
          jwk_dict->SetString("alg", "RS512");
          break;
        default:
          NOTREACHED();
          return Status::ErrorUnexpected();
      }
      break;
    default:
      return Status::ErrorUnsupported();
  }
  return Status::Success();
}

bool IsRsaPublicKey(const blink::WebCryptoKey& key) {
  if (key.type() != blink::WebCryptoKeyTypePublic)
    return false;
  const blink::WebCryptoAlgorithmId algorithm_id = key.algorithm().id();
  return algorithm_id == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
         algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
         algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep;
}

// TODO(padolph): This function is duplicated in shared_crypto.cc
Status ToPlatformPublicKey(const blink::WebCryptoKey& key,
                           platform::PublicKey** out) {
  *out = static_cast<platform::Key*>(key.handle())->AsPublicKey();
  if (!*out)
    return Status::ErrorUnexpectedKeyType();
  return Status::Success();
}

}  // namespace

Status ImportKeyJwk(const CryptoData& key_data,
                    const blink::WebCryptoAlgorithm& algorithm,
                    bool extractable,
                    blink::WebCryptoKeyUsageMask usage_mask,
                    blink::WebCryptoKey* key) {
  // TODO(padolph): Generalize this comment to include export, and move to top
  // of file.

  // The goal of this method is to extract key material and meta data from the
  // incoming JWK, combine them with the input parameters, and ultimately import
  // a Web Crypto Key.
  //
  // 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. A special case is that the method
  // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg'
  // value (if present) is used as a fallback.
  //
  // 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 TODO(padolph): not currently supported |
  //   +-------+--------------------------------------------------------------+
  //
  // - 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                                             |
  //   +--------------+-------------------------------------------------------+
  //   | "RSA1_5"     | RSAES-PKCS1-V1_5 [RFC3447]                            |
  //   | "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. Currently only two possibilities are
  // supported: a raw key or an RSA public key. RSA private keys are not
  // supported because typical applications seldom need to import a private key,
  // and the large number of JWK parameters required to describe one.
  // - 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.     |
  //   +-------+--------------------------------------------------------------+
  //
  // 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.
  //

  if (!key_data.byte_length())
    return Status::ErrorImportEmptyKeyData();
  DCHECK(key);

  // Parse the incoming JWK JSON.
  base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()),
                                key_data.byte_length());
  scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
  // Note, bare pointer dict_value is ok since it points into scoped value.
  base::DictionaryValue* dict_value = NULL;
  if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
    return Status::ErrorJwkNotDictionary();

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

  // JWK "ext" (optional) --> extractable parameter
  {
    bool jwk_ext_value = false;
    bool has_jwk_ext;
    status =
        GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext);
    if (status.IsError())
      return status;
    if (has_jwk_ext && !jwk_ext_value && extractable)
      return Status::ErrorJwkExtInconsistent();
  }

  // JWK "alg" --> algorithm parameter
  // 1. JWK alg present but unrecognized: error
  // 2. JWK alg valid and inconsistent with input algorithm: error
  // 3. JWK alg valid and consistent with input algorithm: use input value
  // 4. JWK alg is missing: use input value
  const JwkAlgorithmInfo* algorithm_info = NULL;
  std::string jwk_alg_value;
  bool has_jwk_alg;
  status =
      GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg);
  if (status.IsError())
    return status;

  if (has_jwk_alg) {
    // JWK alg present

    // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
    // only be from the RSA family.

    blink::WebCryptoAlgorithm jwk_algorithm =
        blink::WebCryptoAlgorithm::createNull();
    algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value);
    if (!algorithm_info ||
        !algorithm_info->CreateImportAlgorithm(&jwk_algorithm))
      return Status::ErrorJwkUnrecognizedAlgorithm();

    if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm))
      return Status::ErrorJwkAlgorithmInconsistent();
  }
  DCHECK(!algorithm.isNull());

  // JWK "key_ops" (optional) --> usage_mask parameter
  base::ListValue* jwk_key_ops_value = NULL;
  bool has_jwk_key_ops;
  status = GetOptionalJwkList(
      dict_value, "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, usage_mask))
      return Status::ErrorJwkKeyopsInconsistent();
  }

  // JWK "use" (optional) --> usage_mask parameter
  std::string jwk_use_value;
  bool has_jwk_use;
  status =
      GetOptionalJwkString(dict_value, "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, 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();

  // JWK keying material --> ImportKeyInternal()
  if (jwk_kty_value == "oct") {
    std::string jwk_k_value;
    status = GetJwkBytes(dict_value, "k", &jwk_k_value);
    if (status.IsError())
      return status;

    // Some JWK alg ID's embed information about the key length in the alg ID
    // string. For example "A128CBC" implies the JWK carries 128 bits
    // of key material. For such keys validate that enough bytes were provided.
    // If this validation is not done, then it would be possible to select a
    // different algorithm by passing a different lengthed key, since that is
    // how WebCrypto interprets things.
    if (algorithm_info &&
        algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) {
      return Status::ErrorJwkIncorrectKeyLength();
    }

    return ImportKey(blink::WebCryptoKeyFormatRaw,
                     CryptoData(jwk_k_value),
                     algorithm,
                     extractable,
                     usage_mask,
                     key);
  }
  if (jwk_kty_value == "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.

    // RSA private key import is not currently supported, so fail here if a "d"
    // entry is found.
    // TODO(padolph): Support RSA private key import.
    if (dict_value->HasKey("d"))
      return Status::ErrorJwkRsaPrivateKeyUnsupported();

    std::string jwk_n_value;
    status = GetJwkBytes(dict_value, "n", &jwk_n_value);
    if (status.IsError())
      return status;
    std::string jwk_e_value;
    status = GetJwkBytes(dict_value, "e", &jwk_e_value);
    if (status.IsError())
      return status;

    return platform::ImportRsaPublicKey(algorithm,
                                        extractable,
                                        usage_mask,
                                        CryptoData(jwk_n_value),
                                        CryptoData(jwk_e_value),
                                        key);

  }

  return Status::ErrorJwkUnrecognizedKty();
}

Status ExportKeyJwk(const blink::WebCryptoKey& key,
                    blink::WebArrayBuffer* buffer) {
  DCHECK(key.extractable());
  base::DictionaryValue jwk_dict;
  Status status = Status::Error();

  switch (key.type()) {
    case blink::WebCryptoKeyTypeSecret: {
      blink::WebArrayBuffer exported_key;
      status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key);
      if (status.IsError())
        return status;
      WriteSecretKey(exported_key, &jwk_dict);
      break;
    }
    case blink::WebCryptoKeyTypePublic: {
      // Currently only RSA public key export is supported.
      if (!IsRsaPublicKey(key))
        return Status::ErrorUnsupported();
      platform::PublicKey* public_key;
      status = ToPlatformPublicKey(key, &public_key);
      if (status.IsError())
        return status;
      std::vector<uint8> modulus;
      std::vector<uint8> public_exponent;
      status =
          platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent);
      if (status.IsError())
        return status;
      WriteRsaPublicKey(modulus, public_exponent, &jwk_dict);
      break;
    }
    case blink::WebCryptoKeyTypePrivate:  // TODO(padolph)
    default:
      return Status::ErrorUnsupported();
  }

  WriteKeyOps(key.usages(), &jwk_dict);
  WriteExt(key.extractable(), &jwk_dict);
  status = WriteAlg(key.algorithm(), &jwk_dict);
  if (status.IsError())
    return status;

  std::string json;
  base::JSONWriter::Write(&jwk_dict, &json);
  *buffer = CreateArrayBuffer(reinterpret_cast<const uint8*>(json.data()),
                              json.size());
  return Status::Success();
}

}  // namespace webcrypto

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */