// 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