1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/child/webcrypto/webcrypto_util.h" 6 7 #include "base/base64.h" 8 #include "base/logging.h" 9 #include "base/strings/stringprintf.h" 10 #include "content/child/webcrypto/status.h" 11 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" 12 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 13 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" 14 15 namespace content { 16 17 namespace webcrypto { 18 19 const uint8* Uint8VectorStart(const std::vector<uint8>& data) { 20 if (data.empty()) 21 return NULL; 22 return &data[0]; 23 } 24 25 uint8* Uint8VectorStart(std::vector<uint8>* data) { 26 if (data->empty()) 27 return NULL; 28 return &(*data)[0]; 29 } 30 31 // This function decodes unpadded 'base64url' encoded data, as described in 32 // RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. To do this, first 33 // change the incoming data to 'base64' encoding by applying the appropriate 34 // transformation including adding padding if required, and then call a base64 35 // decoder. 36 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { 37 std::string base64EncodedText(input); 38 std::replace(base64EncodedText.begin(), base64EncodedText.end(), '-', '+'); 39 std::replace(base64EncodedText.begin(), base64EncodedText.end(), '_', '/'); 40 base64EncodedText.append((4 - base64EncodedText.size() % 4) % 4, '='); 41 return base::Base64Decode(base64EncodedText, output); 42 } 43 44 // Returns an unpadded 'base64url' encoding of the input data, using the 45 // inverse of the process above. 46 std::string Base64EncodeUrlSafe(const base::StringPiece& input) { 47 std::string output; 48 base::Base64Encode(input, &output); 49 std::replace(output.begin(), output.end(), '+', '-'); 50 std::replace(output.begin(), output.end(), '/', '_'); 51 output.erase(std::remove(output.begin(), output.end(), '='), output.end()); 52 return output; 53 } 54 55 std::string Base64EncodeUrlSafe(const std::vector<uint8>& input) { 56 const base::StringPiece string_piece( 57 reinterpret_cast<const char*>(Uint8VectorStart(input)), input.size()); 58 return Base64EncodeUrlSafe(string_piece); 59 } 60 61 struct JwkToWebCryptoUsage { 62 const char* const jwk_key_op; 63 const blink::WebCryptoKeyUsage webcrypto_usage; 64 }; 65 66 // Keep this ordered according to the definition 67 // order of WebCrypto's "recognized key usage 68 // values". 69 // 70 // This is not required for spec compliance, 71 // however it makes the ordering of key_ops match 72 // that of WebCrypto's Key.usages. 73 const JwkToWebCryptoUsage kJwkWebCryptoUsageMap[] = { 74 {"encrypt", blink::WebCryptoKeyUsageEncrypt}, 75 {"decrypt", blink::WebCryptoKeyUsageDecrypt}, 76 {"sign", blink::WebCryptoKeyUsageSign}, 77 {"verify", blink::WebCryptoKeyUsageVerify}, 78 {"deriveKey", blink::WebCryptoKeyUsageDeriveKey}, 79 {"deriveBits", blink::WebCryptoKeyUsageDeriveBits}, 80 {"wrapKey", blink::WebCryptoKeyUsageWrapKey}, 81 {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}}; 82 83 // Modifies the input usage_mask by according to the key_op value. 84 bool JwkKeyOpToWebCryptoUsage(const std::string& key_op, 85 blink::WebCryptoKeyUsageMask* usage_mask) { 86 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { 87 if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) { 88 *usage_mask |= kJwkWebCryptoUsageMap[i].webcrypto_usage; 89 return true; 90 } 91 } 92 return false; 93 } 94 95 // Composes a Web Crypto usage mask from an array of JWK key_ops values. 96 Status GetWebCryptoUsagesFromJwkKeyOps( 97 const base::ListValue* jwk_key_ops_value, 98 blink::WebCryptoKeyUsageMask* usage_mask) { 99 *usage_mask = 0; 100 for (size_t i = 0; i < jwk_key_ops_value->GetSize(); ++i) { 101 std::string key_op; 102 if (!jwk_key_ops_value->GetString(i, &key_op)) { 103 return Status::ErrorJwkPropertyWrongType( 104 base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string"); 105 } 106 // Unrecognized key_ops are silently skipped. 107 ignore_result(JwkKeyOpToWebCryptoUsage(key_op, usage_mask)); 108 } 109 return Status::Success(); 110 } 111 112 // Composes a JWK key_ops List from a Web Crypto usage mask. 113 // Note: Caller must assume ownership of returned instance. 114 base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages( 115 blink::WebCryptoKeyUsageMask usage_mask) { 116 base::ListValue* jwk_key_ops = new base::ListValue(); 117 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { 118 if (usage_mask & kJwkWebCryptoUsageMap[i].webcrypto_usage) 119 jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op); 120 } 121 return jwk_key_ops; 122 } 123 124 blink::WebCryptoAlgorithm GetInnerHashAlgorithm( 125 const blink::WebCryptoAlgorithm& algorithm) { 126 DCHECK(!algorithm.isNull()); 127 switch (algorithm.paramsType()) { 128 case blink::WebCryptoAlgorithmParamsTypeHmacImportParams: 129 return algorithm.hmacImportParams()->hash(); 130 case blink::WebCryptoAlgorithmParamsTypeHmacKeyGenParams: 131 return algorithm.hmacKeyGenParams()->hash(); 132 case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: 133 return algorithm.rsaHashedImportParams()->hash(); 134 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: 135 return algorithm.rsaHashedKeyGenParams()->hash(); 136 default: 137 return blink::WebCryptoAlgorithm::createNull(); 138 } 139 } 140 141 blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) { 142 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL); 143 } 144 145 blink::WebCryptoAlgorithm CreateHmacImportAlgorithm( 146 blink::WebCryptoAlgorithmId hash_id) { 147 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); 148 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( 149 blink::WebCryptoAlgorithmIdHmac, 150 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id))); 151 } 152 153 blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm( 154 blink::WebCryptoAlgorithmId id, 155 blink::WebCryptoAlgorithmId hash_id) { 156 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); 157 DCHECK(id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || 158 id == blink::WebCryptoAlgorithmIdRsaOaep); 159 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( 160 id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id))); 161 } 162 163 bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, 164 unsigned int keylen_bytes, 165 blink::WebCryptoKeyAlgorithm* key_algorithm) { 166 switch (algorithm.id()) { 167 case blink::WebCryptoAlgorithmIdHmac: { 168 blink::WebCryptoAlgorithm hash = GetInnerHashAlgorithm(algorithm); 169 if (hash.isNull()) 170 return false; 171 if (keylen_bytes > UINT_MAX / 8) 172 return false; 173 *key_algorithm = 174 blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bytes * 8); 175 return true; 176 } 177 case blink::WebCryptoAlgorithmIdAesKw: 178 case blink::WebCryptoAlgorithmIdAesCbc: 179 case blink::WebCryptoAlgorithmIdAesCtr: 180 case blink::WebCryptoAlgorithmIdAesGcm: 181 *key_algorithm = blink::WebCryptoKeyAlgorithm::createAes( 182 algorithm.id(), keylen_bytes * 8); 183 return true; 184 default: 185 return false; 186 } 187 } 188 189 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, 190 blink::WebCryptoKeyUsageMask b) { 191 return (a & b) == b; 192 } 193 194 bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) { 195 return alg_id == blink::WebCryptoAlgorithmIdRsaOaep || 196 alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; 197 } 198 199 bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) { 200 // TODO(padolph): include all other asymmetric algorithms once they are 201 // defined, e.g. EC and DH. 202 return IsAlgorithmRsa(alg_id); 203 } 204 205 } // namespace webcrypto 206 207 } // namespace content 208