| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/webcrypto/webcrypto_impl.h" | 5 #include "content/renderer/webcrypto/webcrypto_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 #include <functional> | |
| 9 #include <map> | |
| 10 #include "base/json/json_reader.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "base/logging.h" | 7 #include "base/logging.h" |
| 13 #include "base/memory/scoped_ptr.h" | 8 #include "content/renderer/webcrypto/crypto_data.h" |
| 14 #include "base/strings/string_piece.h" | 9 #include "content/renderer/webcrypto/shared_crypto.h" |
| 15 #include "base/values.h" | |
| 16 #include "content/renderer/webcrypto/webcrypto_util.h" | 10 #include "content/renderer/webcrypto/webcrypto_util.h" |
| 17 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | |
| 18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
| 19 #include "third_party/WebKit/public/platform/WebCryptoKey.h" | |
| 20 #include "third_party/WebKit/public/platform/WebString.h" | 11 #include "third_party/WebKit/public/platform/WebString.h" |
| 21 | 12 |
| 22 namespace content { | 13 namespace content { |
| 23 | 14 |
| 24 using webcrypto::Status; | 15 using webcrypto::Status; |
| 25 | 16 |
| 26 namespace { | 17 namespace { |
| 27 | 18 |
| 28 void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { | 19 void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { |
| 29 DCHECK(status.IsError()); | 20 DCHECK(status.IsError()); |
| 30 if (status.HasErrorDetails()) | 21 if (status.HasErrorDetails()) |
| 31 result->completeWithError(blink::WebString::fromUTF8(status.ToString())); | 22 result->completeWithError(blink::WebString::fromUTF8(status.ToString())); |
| 32 else | 23 else |
| 33 result->completeWithError(); | 24 result->completeWithError(); |
| 34 } | 25 } |
| 35 | 26 |
| 36 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) { | 27 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) { |
| 37 // TODO(padolph): include all other asymmetric algorithms once they are | 28 // TODO(padolph): include all other asymmetric algorithms once they are |
| 38 // defined, e.g. EC and DH. | 29 // defined, e.g. EC and DH. |
| 39 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || | 30 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || |
| 40 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || | 31 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || |
| 41 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep); | 32 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep); |
| 42 } | 33 } |
| 43 | 34 |
| 44 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); | |
| 45 | |
| 46 class JwkAlgorithmInfo { | |
| 47 public: | |
| 48 JwkAlgorithmInfo() | |
| 49 : creation_func_(NULL), | |
| 50 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) { | |
| 51 | |
| 52 } | |
| 53 | |
| 54 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) | |
| 55 : creation_func_(algorithm_creation_func), | |
| 56 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) { | |
| 57 } | |
| 58 | |
| 59 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, | |
| 60 unsigned int required_key_length_bits) | |
| 61 : creation_func_(algorithm_creation_func), | |
| 62 required_key_length_bytes_(required_key_length_bits / 8) { | |
| 63 DCHECK((required_key_length_bits % 8) == 0); | |
| 64 } | |
| 65 | |
| 66 bool CreateAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { | |
| 67 *algorithm = creation_func_(); | |
| 68 return !algorithm->isNull(); | |
| 69 } | |
| 70 | |
| 71 bool IsInvalidKeyByteLength(size_t byte_length) const { | |
| 72 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) | |
| 73 return false; | |
| 74 return required_key_length_bytes_ != byte_length; | |
| 75 } | |
| 76 | |
| 77 private: | |
| 78 enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX }; | |
| 79 | |
| 80 AlgorithmCreationFunc creation_func_; | |
| 81 | |
| 82 // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT. | |
| 83 unsigned int required_key_length_bytes_; | |
| 84 | |
| 85 }; | |
| 86 | |
| 87 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; | |
| 88 | |
| 89 class JwkAlgorithmRegistry { | |
| 90 public: | |
| 91 JwkAlgorithmRegistry() { | |
| 92 // TODO(eroman): | |
| 93 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 | |
| 94 // says HMAC with SHA-2 should have a key size at least as large as the | |
| 95 // hash output. | |
| 96 alg_to_info_["HS256"] = JwkAlgorithmInfo( | |
| 97 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
| 98 blink::WebCryptoAlgorithmIdSha256>); | |
| 99 alg_to_info_["HS384"] = JwkAlgorithmInfo( | |
| 100 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
| 101 blink::WebCryptoAlgorithmIdSha384>); | |
| 102 alg_to_info_["HS512"] = JwkAlgorithmInfo( | |
| 103 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
| 104 blink::WebCryptoAlgorithmIdSha512>); | |
| 105 alg_to_info_["RS256"] = JwkAlgorithmInfo( | |
| 106 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
| 107 blink::WebCryptoAlgorithmIdSha256>); | |
| 108 alg_to_info_["RS384"] = JwkAlgorithmInfo( | |
| 109 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
| 110 blink::WebCryptoAlgorithmIdSha384>); | |
| 111 alg_to_info_["RS512"] = JwkAlgorithmInfo( | |
| 112 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
| 113 blink::WebCryptoAlgorithmIdSha512>); | |
| 114 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo( | |
| 115 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 116 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>); | |
| 117 alg_to_info_["RSA-OAEP"] = JwkAlgorithmInfo( | |
| 118 &BindAlgorithmId<webcrypto::CreateRsaOaepAlgorithm, | |
| 119 blink::WebCryptoAlgorithmIdSha1>); | |
| 120 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet | |
| 121 alg_to_info_["A128KW"] = | |
| 122 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 128); | |
| 123 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet | |
| 124 alg_to_info_["A256KW"] = | |
| 125 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 256); | |
| 126 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( | |
| 127 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 128 blink::WebCryptoAlgorithmIdAesGcm>, 128); | |
| 129 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( | |
| 130 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 131 blink::WebCryptoAlgorithmIdAesGcm>, 256); | |
| 132 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( | |
| 133 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 134 blink::WebCryptoAlgorithmIdAesCbc>, 128); | |
| 135 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( | |
| 136 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 137 blink::WebCryptoAlgorithmIdAesCbc>, 192); | |
| 138 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( | |
| 139 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
| 140 blink::WebCryptoAlgorithmIdAesCbc>, 256); | |
| 141 } | |
| 142 | |
| 143 // Returns NULL if the algorithm name was not registered. | |
| 144 const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const { | |
| 145 const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg); | |
| 146 if (pos == alg_to_info_.end()) | |
| 147 return NULL; | |
| 148 return &pos->second; | |
| 149 } | |
| 150 | |
| 151 private: | |
| 152 // Binds a WebCryptoAlgorithmId value to a compatible factory function. | |
| 153 typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)( | |
| 154 blink::WebCryptoAlgorithmId); | |
| 155 template <FuncWithWebCryptoAlgIdArg func, | |
| 156 blink::WebCryptoAlgorithmId algorithm_id> | |
| 157 static blink::WebCryptoAlgorithm BindAlgorithmId() { | |
| 158 return func(algorithm_id); | |
| 159 } | |
| 160 | |
| 161 JwkAlgorithmInfoMap alg_to_info_; | |
| 162 }; | |
| 163 | |
| 164 base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry = | |
| 165 LAZY_INSTANCE_INITIALIZER; | |
| 166 | |
| 167 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, | |
| 168 const blink::WebCryptoAlgorithm& alg2) { | |
| 169 DCHECK(!alg1.isNull()); | |
| 170 DCHECK(!alg2.isNull()); | |
| 171 if (alg1.id() != alg2.id()) | |
| 172 return false; | |
| 173 switch (alg1.id()) { | |
| 174 case blink::WebCryptoAlgorithmIdHmac: | |
| 175 case blink::WebCryptoAlgorithmIdRsaOaep: | |
| 176 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: | |
| 177 if (WebCryptoAlgorithmsConsistent( | |
| 178 webcrypto::GetInnerHashAlgorithm(alg1), | |
| 179 webcrypto::GetInnerHashAlgorithm(alg2))) { | |
| 180 return true; | |
| 181 } | |
| 182 break; | |
| 183 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: | |
| 184 case blink::WebCryptoAlgorithmIdSha1: | |
| 185 case blink::WebCryptoAlgorithmIdSha224: | |
| 186 case blink::WebCryptoAlgorithmIdSha256: | |
| 187 case blink::WebCryptoAlgorithmIdSha384: | |
| 188 case blink::WebCryptoAlgorithmIdSha512: | |
| 189 case blink::WebCryptoAlgorithmIdAesCbc: | |
| 190 case blink::WebCryptoAlgorithmIdAesGcm: | |
| 191 case blink::WebCryptoAlgorithmIdAesCtr: | |
| 192 return true; | |
| 193 default: | |
| 194 NOTREACHED(); // Not a supported algorithm. | |
| 195 break; | |
| 196 } | |
| 197 return false; | |
| 198 } | |
| 199 | |
| 200 // Extracts the required string property with key |path| from |dict| and saves | |
| 201 // the result to |*result|. If the property does not exist or is not a string, | |
| 202 // returns an error. | |
| 203 Status GetJwkString(base::DictionaryValue* dict, | |
| 204 const std::string& path, | |
| 205 std::string* result) { | |
| 206 base::Value* value = NULL; | |
| 207 if (!dict->Get(path, &value)) | |
| 208 return Status::ErrorJwkPropertyMissing(path); | |
| 209 if (!value->GetAsString(result)) | |
| 210 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
| 211 return Status::Success(); | |
| 212 } | |
| 213 | |
| 214 // Extracts the optional string property with key |path| from |dict| and saves | |
| 215 // the result to |*result| if it was found. If the property exists and is not a | |
| 216 // string, returns an error. Otherwise returns success, and sets | |
| 217 // |*property_exists| if it was found. | |
| 218 Status GetOptionalJwkString(base::DictionaryValue* dict, | |
| 219 const std::string& path, | |
| 220 std::string* result, | |
| 221 bool* property_exists) { | |
| 222 *property_exists = false; | |
| 223 base::Value* value = NULL; | |
| 224 if (!dict->Get(path, &value)) | |
| 225 return Status::Success(); | |
| 226 | |
| 227 if (!value->GetAsString(result)) | |
| 228 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
| 229 | |
| 230 *property_exists = true; | |
| 231 return Status::Success(); | |
| 232 } | |
| 233 | |
| 234 // Extracts the required string property with key |path| from |dict| and saves | |
| 235 // the base64-decoded bytes to |*result|. If the property does not exist or is | |
| 236 // not a string, or could not be base64-decoded, returns an error. | |
| 237 Status GetJwkBytes(base::DictionaryValue* dict, | |
| 238 const std::string& path, | |
| 239 std::string* result) { | |
| 240 std::string base64_string; | |
| 241 Status status = GetJwkString(dict, path, &base64_string); | |
| 242 if (status.IsError()) | |
| 243 return status; | |
| 244 | |
| 245 if (!webcrypto::Base64DecodeUrlSafe(base64_string, result)) | |
| 246 return Status::ErrorJwkBase64Decode(path); | |
| 247 | |
| 248 return Status::Success(); | |
| 249 } | |
| 250 | |
| 251 // Extracts the optional boolean property with key |path| from |dict| and saves | |
| 252 // the result to |*result| if it was found. If the property exists and is not a | |
| 253 // boolean, returns an error. Otherwise returns success, and sets | |
| 254 // |*property_exists| if it was found. | |
| 255 Status GetOptionalJwkBool(base::DictionaryValue* dict, | |
| 256 const std::string& path, | |
| 257 bool* result, | |
| 258 bool* property_exists) { | |
| 259 *property_exists = false; | |
| 260 base::Value* value = NULL; | |
| 261 if (!dict->Get(path, &value)) | |
| 262 return Status::Success(); | |
| 263 | |
| 264 if (!value->GetAsBoolean(result)) | |
| 265 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | |
| 266 | |
| 267 *property_exists = true; | |
| 268 return Status::Success(); | |
| 269 } | |
| 270 | 35 |
| 271 } // namespace | 36 } // namespace |
| 272 | 37 |
| 273 WebCryptoImpl::WebCryptoImpl() { | 38 WebCryptoImpl::WebCryptoImpl() { |
| 274 Init(); | 39 webcrypto::Init(); |
| 275 } | 40 } |
| 276 | 41 |
| 42 WebCryptoImpl::~WebCryptoImpl() {} |
| 43 |
| 277 void WebCryptoImpl::encrypt( | 44 void WebCryptoImpl::encrypt( |
| 278 const blink::WebCryptoAlgorithm& algorithm, | 45 const blink::WebCryptoAlgorithm& algorithm, |
| 279 const blink::WebCryptoKey& key, | 46 const blink::WebCryptoKey& key, |
| 280 const unsigned char* data, | 47 const unsigned char* data, |
| 281 unsigned int data_size, | 48 unsigned int data_size, |
| 282 blink::WebCryptoResult result) { | 49 blink::WebCryptoResult result) { |
| 283 DCHECK(!algorithm.isNull()); | 50 DCHECK(!algorithm.isNull()); |
| 284 blink::WebArrayBuffer buffer; | 51 blink::WebArrayBuffer buffer; |
| 285 Status status = EncryptInternal(algorithm, key, data, data_size, &buffer); | 52 Status status = webcrypto::Encrypt( |
| 53 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
| 286 if (status.IsError()) | 54 if (status.IsError()) |
| 287 CompleteWithError(status, &result); | 55 CompleteWithError(status, &result); |
| 288 else | 56 else |
| 289 result.completeWithBuffer(buffer); | 57 result.completeWithBuffer(buffer); |
| 290 } | 58 } |
| 291 | 59 |
| 292 void WebCryptoImpl::decrypt( | 60 void WebCryptoImpl::decrypt( |
| 293 const blink::WebCryptoAlgorithm& algorithm, | 61 const blink::WebCryptoAlgorithm& algorithm, |
| 294 const blink::WebCryptoKey& key, | 62 const blink::WebCryptoKey& key, |
| 295 const unsigned char* data, | 63 const unsigned char* data, |
| 296 unsigned int data_size, | 64 unsigned int data_size, |
| 297 blink::WebCryptoResult result) { | 65 blink::WebCryptoResult result) { |
| 298 DCHECK(!algorithm.isNull()); | 66 DCHECK(!algorithm.isNull()); |
| 299 blink::WebArrayBuffer buffer; | 67 blink::WebArrayBuffer buffer; |
| 300 Status status = DecryptInternal(algorithm, key, data, data_size, &buffer); | 68 Status status = webcrypto::Decrypt( |
| 69 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
| 301 if (status.IsError()) | 70 if (status.IsError()) |
| 302 CompleteWithError(status, &result); | 71 CompleteWithError(status, &result); |
| 303 else | 72 else |
| 304 result.completeWithBuffer(buffer); | 73 result.completeWithBuffer(buffer); |
| 305 } | 74 } |
| 306 | 75 |
| 307 void WebCryptoImpl::digest( | 76 void WebCryptoImpl::digest( |
| 308 const blink::WebCryptoAlgorithm& algorithm, | 77 const blink::WebCryptoAlgorithm& algorithm, |
| 309 const unsigned char* data, | 78 const unsigned char* data, |
| 310 unsigned int data_size, | 79 unsigned int data_size, |
| 311 blink::WebCryptoResult result) { | 80 blink::WebCryptoResult result) { |
| 312 DCHECK(!algorithm.isNull()); | 81 DCHECK(!algorithm.isNull()); |
| 313 blink::WebArrayBuffer buffer; | 82 blink::WebArrayBuffer buffer; |
| 314 Status status = DigestInternal(algorithm, data, data_size, &buffer); | 83 Status status = webcrypto::Digest( |
| 84 algorithm, webcrypto::CryptoData(data, data_size), &buffer); |
| 315 if (status.IsError()) | 85 if (status.IsError()) |
| 316 CompleteWithError(status, &result); | 86 CompleteWithError(status, &result); |
| 317 else | 87 else |
| 318 result.completeWithBuffer(buffer); | 88 result.completeWithBuffer(buffer); |
| 319 } | 89 } |
| 320 | 90 |
| 321 void WebCryptoImpl::generateKey( | 91 void WebCryptoImpl::generateKey( |
| 322 const blink::WebCryptoAlgorithm& algorithm, | 92 const blink::WebCryptoAlgorithm& algorithm, |
| 323 bool extractable, | 93 bool extractable, |
| 324 blink::WebCryptoKeyUsageMask usage_mask, | 94 blink::WebCryptoKeyUsageMask usage_mask, |
| 325 blink::WebCryptoResult result) { | 95 blink::WebCryptoResult result) { |
| 326 DCHECK(!algorithm.isNull()); | 96 DCHECK(!algorithm.isNull()); |
| 327 if (IsAlgorithmAsymmetric(algorithm)) { | 97 if (IsAlgorithmAsymmetric(algorithm)) { |
| 328 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | 98 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
| 329 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | 99 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
| 330 Status status = GenerateKeyPairInternal( | 100 Status status = webcrypto::GenerateKeyPair( |
| 331 algorithm, extractable, usage_mask, &public_key, &private_key); | 101 algorithm, extractable, usage_mask, &public_key, &private_key); |
| 332 if (status.IsError()) { | 102 if (status.IsError()) { |
| 333 CompleteWithError(status, &result); | 103 CompleteWithError(status, &result); |
| 334 } else { | 104 } else { |
| 335 DCHECK(public_key.handle()); | 105 DCHECK(public_key.handle()); |
| 336 DCHECK(private_key.handle()); | 106 DCHECK(private_key.handle()); |
| 337 DCHECK_EQ(algorithm.id(), public_key.algorithm().id()); | 107 DCHECK_EQ(algorithm.id(), public_key.algorithm().id()); |
| 338 DCHECK_EQ(algorithm.id(), private_key.algorithm().id()); | 108 DCHECK_EQ(algorithm.id(), private_key.algorithm().id()); |
| 339 DCHECK_EQ(true, public_key.extractable()); | 109 DCHECK_EQ(true, public_key.extractable()); |
| 340 DCHECK_EQ(extractable, private_key.extractable()); | 110 DCHECK_EQ(extractable, private_key.extractable()); |
| 341 DCHECK_EQ(usage_mask, public_key.usages()); | 111 DCHECK_EQ(usage_mask, public_key.usages()); |
| 342 DCHECK_EQ(usage_mask, private_key.usages()); | 112 DCHECK_EQ(usage_mask, private_key.usages()); |
| 343 result.completeWithKeyPair(public_key, private_key); | 113 result.completeWithKeyPair(public_key, private_key); |
| 344 } | 114 } |
| 345 } else { | 115 } else { |
| 346 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 116 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
| 347 Status status = GenerateSecretKeyInternal( | 117 Status status = |
| 348 algorithm, extractable, usage_mask, &key); | 118 webcrypto::GenerateSecretKey(algorithm, extractable, usage_mask, &key); |
| 349 if (status.IsError()) { | 119 if (status.IsError()) { |
| 350 CompleteWithError(status, &result); | 120 CompleteWithError(status, &result); |
| 351 } else { | 121 } else { |
| 352 DCHECK(key.handle()); | 122 DCHECK(key.handle()); |
| 353 DCHECK_EQ(algorithm.id(), key.algorithm().id()); | 123 DCHECK_EQ(algorithm.id(), key.algorithm().id()); |
| 354 DCHECK_EQ(extractable, key.extractable()); | 124 DCHECK_EQ(extractable, key.extractable()); |
| 355 DCHECK_EQ(usage_mask, key.usages()); | 125 DCHECK_EQ(usage_mask, key.usages()); |
| 356 result.completeWithKey(key); | 126 result.completeWithKey(key); |
| 357 } | 127 } |
| 358 } | 128 } |
| 359 } | 129 } |
| 360 | 130 |
| 361 void WebCryptoImpl::importKey( | 131 void WebCryptoImpl::importKey( |
| 362 blink::WebCryptoKeyFormat format, | 132 blink::WebCryptoKeyFormat format, |
| 363 const unsigned char* key_data, | 133 const unsigned char* key_data, |
| 364 unsigned int key_data_size, | 134 unsigned int key_data_size, |
| 365 const blink::WebCryptoAlgorithm& algorithm_or_null, | 135 const blink::WebCryptoAlgorithm& algorithm_or_null, |
| 366 bool extractable, | 136 bool extractable, |
| 367 blink::WebCryptoKeyUsageMask usage_mask, | 137 blink::WebCryptoKeyUsageMask usage_mask, |
| 368 blink::WebCryptoResult result) { | 138 blink::WebCryptoResult result) { |
| 369 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 139 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
| 370 Status status = Status::Error(); | 140 Status status = |
| 371 if (format == blink::WebCryptoKeyFormatJwk) { | 141 webcrypto::ImportKey(format, |
| 372 status = ImportKeyJwk(key_data, | 142 webcrypto::CryptoData(key_data, key_data_size), |
| 373 key_data_size, | 143 algorithm_or_null, |
| 374 algorithm_or_null, | 144 extractable, |
| 375 extractable, | 145 usage_mask, |
| 376 usage_mask, | 146 &key); |
| 377 &key); | |
| 378 } else { | |
| 379 status = ImportKeyInternal(format, | |
| 380 key_data, | |
| 381 key_data_size, | |
| 382 algorithm_or_null, | |
| 383 extractable, | |
| 384 usage_mask, | |
| 385 &key); | |
| 386 } | |
| 387 if (status.IsError()) { | 147 if (status.IsError()) { |
| 388 CompleteWithError(status, &result); | 148 CompleteWithError(status, &result); |
| 389 } else { | 149 } else { |
| 390 DCHECK(key.handle()); | 150 DCHECK(key.handle()); |
| 391 DCHECK(!key.algorithm().isNull()); | 151 DCHECK(!key.algorithm().isNull()); |
| 392 DCHECK_EQ(extractable, key.extractable()); | 152 DCHECK_EQ(extractable, key.extractable()); |
| 393 result.completeWithKey(key); | 153 result.completeWithKey(key); |
| 394 } | 154 } |
| 395 } | 155 } |
| 396 | 156 |
| 397 void WebCryptoImpl::exportKey( | 157 void WebCryptoImpl::exportKey( |
| 398 blink::WebCryptoKeyFormat format, | 158 blink::WebCryptoKeyFormat format, |
| 399 const blink::WebCryptoKey& key, | 159 const blink::WebCryptoKey& key, |
| 400 blink::WebCryptoResult result) { | 160 blink::WebCryptoResult result) { |
| 401 blink::WebArrayBuffer buffer; | 161 blink::WebArrayBuffer buffer; |
| 402 Status status = ExportKeyInternal(format, key, &buffer); | 162 Status status = webcrypto::ExportKey(format, key, &buffer); |
| 403 if (status.IsError()) | 163 if (status.IsError()) |
| 404 CompleteWithError(status, &result); | 164 CompleteWithError(status, &result); |
| 405 else | 165 else |
| 406 result.completeWithBuffer(buffer); | 166 result.completeWithBuffer(buffer); |
| 407 } | 167 } |
| 408 | 168 |
| 409 void WebCryptoImpl::sign( | 169 void WebCryptoImpl::sign( |
| 410 const blink::WebCryptoAlgorithm& algorithm, | 170 const blink::WebCryptoAlgorithm& algorithm, |
| 411 const blink::WebCryptoKey& key, | 171 const blink::WebCryptoKey& key, |
| 412 const unsigned char* data, | 172 const unsigned char* data, |
| 413 unsigned int data_size, | 173 unsigned int data_size, |
| 414 blink::WebCryptoResult result) { | 174 blink::WebCryptoResult result) { |
| 415 DCHECK(!algorithm.isNull()); | 175 DCHECK(!algorithm.isNull()); |
| 416 blink::WebArrayBuffer buffer; | 176 blink::WebArrayBuffer buffer; |
| 417 Status status = SignInternal(algorithm, key, data, data_size, &buffer); | 177 Status status = webcrypto::Sign( |
| 178 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
| 418 if (status.IsError()) | 179 if (status.IsError()) |
| 419 CompleteWithError(status, &result); | 180 CompleteWithError(status, &result); |
| 420 else | 181 else |
| 421 result.completeWithBuffer(buffer); | 182 result.completeWithBuffer(buffer); |
| 422 } | 183 } |
| 423 | 184 |
| 424 void WebCryptoImpl::verifySignature( | 185 void WebCryptoImpl::verifySignature( |
| 425 const blink::WebCryptoAlgorithm& algorithm, | 186 const blink::WebCryptoAlgorithm& algorithm, |
| 426 const blink::WebCryptoKey& key, | 187 const blink::WebCryptoKey& key, |
| 427 const unsigned char* signature, | 188 const unsigned char* signature, |
| 428 unsigned int signature_size, | 189 unsigned int signature_size, |
| 429 const unsigned char* data, | 190 const unsigned char* data, |
| 430 unsigned int data_size, | 191 unsigned int data_size, |
| 431 blink::WebCryptoResult result) { | 192 blink::WebCryptoResult result) { |
| 432 DCHECK(!algorithm.isNull()); | 193 DCHECK(!algorithm.isNull()); |
| 433 bool signature_match = false; | 194 bool signature_match = false; |
| 434 Status status = VerifySignatureInternal(algorithm, | 195 Status status = webcrypto::VerifySignature( |
| 435 key, | 196 algorithm, |
| 436 signature, | 197 key, |
| 437 signature_size, | 198 webcrypto::CryptoData(signature, signature_size), |
| 438 data, | 199 webcrypto::CryptoData(data, data_size), |
| 439 data_size, | 200 &signature_match); |
| 440 &signature_match); | |
| 441 if (status.IsError()) | 201 if (status.IsError()) |
| 442 CompleteWithError(status, &result); | 202 CompleteWithError(status, &result); |
| 443 else | 203 else |
| 444 result.completeWithBoolean(signature_match); | 204 result.completeWithBoolean(signature_match); |
| 445 } | 205 } |
| 446 | 206 |
| 447 Status WebCryptoImpl::ImportKeyJwk( | |
| 448 const unsigned char* key_data, | |
| 449 unsigned int key_data_size, | |
| 450 const blink::WebCryptoAlgorithm& algorithm_or_null, | |
| 451 bool extractable, | |
| 452 blink::WebCryptoKeyUsageMask usage_mask, | |
| 453 blink::WebCryptoKey* key) { | |
| 454 | |
| 455 // The goal of this method is to extract key material and meta data from the | |
| 456 // incoming JWK, combine them with the input parameters, and ultimately import | |
| 457 // a Web Crypto Key. | |
| 458 // | |
| 459 // JSON Web Key Format (JWK) | |
| 460 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16 | |
| 461 // TODO(padolph): Not all possible values are handled by this code right now | |
| 462 // | |
| 463 // A JWK is a simple JSON dictionary with the following entries | |
| 464 // - "kty" (Key Type) Parameter, REQUIRED | |
| 465 // - <kty-specific parameters, see below>, REQUIRED | |
| 466 // - "use" (Key Use) Parameter, OPTIONAL | |
| 467 // - "alg" (Algorithm) Parameter, OPTIONAL | |
| 468 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE, | |
| 469 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796] | |
| 470 // (all other entries are ignored) | |
| 471 // | |
| 472 // OPTIONAL here means that this code does not require the entry to be present | |
| 473 // in the incoming JWK, because the method input parameters contain similar | |
| 474 // information. If the optional JWK entry is present, it will be validated | |
| 475 // against the corresponding input parameter for consistency and combined with | |
| 476 // it according to rules defined below. A special case is that the method | |
| 477 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg' | |
| 478 // value (if present) is used as a fallback. | |
| 479 // | |
| 480 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK | |
| 481 // values are parsed out and combined with the method input parameters to | |
| 482 // build a Web Crypto Key: | |
| 483 // Web Crypto Key type <-- (deduced) | |
| 484 // Web Crypto Key extractable <-- JWK extractable + input extractable | |
| 485 // Web Crypto Key algorithm <-- JWK alg + input algorithm | |
| 486 // Web Crypto Key keyUsage <-- JWK use + input usage_mask | |
| 487 // Web Crypto Key keying material <-- kty-specific parameters | |
| 488 // | |
| 489 // Values for each JWK entry are case-sensitive and defined in | |
| 490 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16. | |
| 491 // Note that not all values specified by JOSE are handled by this code. Only | |
| 492 // handled values are listed. | |
| 493 // - kty (Key Type) | |
| 494 // +-------+--------------------------------------------------------------+ | |
| 495 // | "RSA" | RSA [RFC3447] | | |
| 496 // | "oct" | Octet sequence (used to represent symmetric keys) | | |
| 497 // +-------+--------------------------------------------------------------+ | |
| 498 // - use (Key Use) | |
| 499 // +-------+--------------------------------------------------------------+ | |
| 500 // | "enc" | encrypt and decrypt operations | | |
| 501 // | "sig" | sign and verify (MAC) operations | | |
| 502 // | "wrap"| key wrap and unwrap [not yet part of JOSE] | | |
| 503 // +-------+--------------------------------------------------------------+ | |
| 504 // - extractable (Key Exportability) | |
| 505 // +-------+--------------------------------------------------------------+ | |
| 506 // | true | Key may be exported from the trusted environment | | |
| 507 // | false | Key cannot exit the trusted environment | | |
| 508 // +-------+--------------------------------------------------------------+ | |
| 509 // - alg (Algorithm) | |
| 510 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16 | |
| 511 // +--------------+-------------------------------------------------------+ | |
| 512 // | Digital Signature or MAC Algorithm | | |
| 513 // +--------------+-------------------------------------------------------+ | |
| 514 // | "HS256" | HMAC using SHA-256 hash algorithm | | |
| 515 // | "HS384" | HMAC using SHA-384 hash algorithm | | |
| 516 // | "HS512" | HMAC using SHA-512 hash algorithm | | |
| 517 // | "RS256" | RSASSA using SHA-256 hash algorithm | | |
| 518 // | "RS384" | RSASSA using SHA-384 hash algorithm | | |
| 519 // | "RS512" | RSASSA using SHA-512 hash algorithm | | |
| 520 // +--------------+-------------------------------------------------------| | |
| 521 // | Key Management Algorithm | | |
| 522 // +--------------+-------------------------------------------------------+ | |
| 523 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] | | |
| 524 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | | |
| 525 // | | (OAEP) [RFC3447], with the default parameters | | |
| 526 // | | specified by RFC3447 in Section A.2.1 | | |
| 527 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | | |
| 528 // | | [RFC3394] using 128 bit keys | | |
| 529 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | | |
| 530 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | | |
| 531 // | | 128 bit keys | | |
| 532 // | "A256GCM" | AES GCM using 256 bit keys | | |
| 533 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | | |
| 534 // | | padding [NIST.800-38A] [not yet part of JOSE, see | | |
| 535 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 | | |
| 536 // | "A192CBC" | AES CBC using 192 bit keys [not yet part of JOSE] | | |
| 537 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] | | |
| 538 // +--------------+-------------------------------------------------------+ | |
| 539 // | |
| 540 // kty-specific parameters | |
| 541 // The value of kty determines the type and content of the keying material | |
| 542 // carried in the JWK to be imported. Currently only two possibilities are | |
| 543 // supported: a raw key or an RSA public key. RSA private keys are not | |
| 544 // supported because typical applications seldom need to import a private key, | |
| 545 // and the large number of JWK parameters required to describe one. | |
| 546 // - kty == "oct" (symmetric or other raw key) | |
| 547 // +-------+--------------------------------------------------------------+ | |
| 548 // | "k" | Contains the value of the symmetric (or other single-valued) | | |
| 549 // | | key. It is represented as the base64url encoding of the | | |
| 550 // | | octet sequence containing the key value. | | |
| 551 // +-------+--------------------------------------------------------------+ | |
| 552 // - kty == "RSA" (RSA public key) | |
| 553 // +-------+--------------------------------------------------------------+ | |
| 554 // | "n" | Contains the modulus value for the RSA public key. It is | | |
| 555 // | | represented as the base64url encoding of the value's | | |
| 556 // | | unsigned big endian representation as an octet sequence. | | |
| 557 // +-------+--------------------------------------------------------------+ | |
| 558 // | "e" | Contains the exponent value for the RSA public key. It is | | |
| 559 // | | represented as the base64url encoding of the value's | | |
| 560 // | | unsigned big endian representation as an octet sequence. | | |
| 561 // +-------+--------------------------------------------------------------+ | |
| 562 // | |
| 563 // Consistency and conflict resolution | |
| 564 // The 'algorithm_or_null', 'extractable', and 'usage_mask' input parameters | |
| 565 // may be different than the corresponding values inside the JWK. The Web | |
| 566 // Crypto spec says that if a JWK value is present but is inconsistent with | |
| 567 // the input value, it is an error and the operation must fail. If no | |
| 568 // inconsistency is found, the input and JWK values are combined as follows: | |
| 569 // | |
| 570 // algorithm | |
| 571 // If an algorithm is provided by both the input parameter and the JWK, | |
| 572 // consistency between the two is based only on algorithm ID's (including an | |
| 573 // inner hash algorithm if present). In this case if the consistency | |
| 574 // check is passed, the input algorithm is used. If only one of either the | |
| 575 // input algorithm and JWK alg is provided, it is used as the final | |
| 576 // algorithm. | |
| 577 // | |
| 578 // extractable | |
| 579 // If the JWK extractable is true but the input parameter is false, make the | |
| 580 // Web Crypto Key non-extractable. Conversely, if the JWK extractable is | |
| 581 // false but the input parameter is true, it is an inconsistency. If both | |
| 582 // are true or both are false, use that value. | |
| 583 // | |
| 584 // usage_mask | |
| 585 // The input usage_mask must be a strict subset of the interpreted JWK use | |
| 586 // value, else it is judged inconsistent. In all cases the input usage_mask | |
| 587 // is used as the final usage_mask. | |
| 588 // | |
| 589 | |
| 590 if (!key_data_size) | |
| 591 return Status::ErrorImportEmptyKeyData(); | |
| 592 DCHECK(key); | |
| 593 | |
| 594 // Parse the incoming JWK JSON. | |
| 595 base::StringPiece json_string(reinterpret_cast<const char*>(key_data), | |
| 596 key_data_size); | |
| 597 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); | |
| 598 // Note, bare pointer dict_value is ok since it points into scoped value. | |
| 599 base::DictionaryValue* dict_value = NULL; | |
| 600 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) | |
| 601 return Status::ErrorJwkNotDictionary(); | |
| 602 | |
| 603 // JWK "kty". Exit early if this required JWK parameter is missing. | |
| 604 std::string jwk_kty_value; | |
| 605 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); | |
| 606 if (status.IsError()) | |
| 607 return status; | |
| 608 | |
| 609 // JWK "extractable" (optional) --> extractable parameter | |
| 610 { | |
| 611 bool jwk_extractable_value = false; | |
| 612 bool has_jwk_extractable; | |
| 613 status = GetOptionalJwkBool(dict_value, | |
| 614 "extractable", | |
| 615 &jwk_extractable_value, | |
| 616 &has_jwk_extractable); | |
| 617 if (status.IsError()) | |
| 618 return status; | |
| 619 if (has_jwk_extractable && !jwk_extractable_value && extractable) | |
| 620 return Status::ErrorJwkExtractableInconsistent(); | |
| 621 } | |
| 622 | |
| 623 // JWK "alg" (optional) --> algorithm parameter | |
| 624 // Note: input algorithm is also optional, so we have six cases to handle. | |
| 625 // 1. JWK alg present but unrecognized: error | |
| 626 // 2. JWK alg valid AND input algorithm isNull: use JWK value | |
| 627 // 3. JWK alg valid AND input algorithm specified, but JWK value | |
| 628 // inconsistent with input: error | |
| 629 // 4. JWK alg valid AND input algorithm specified, both consistent: use | |
| 630 // input value (because it has potentially more details) | |
| 631 // 5. JWK alg missing AND input algorithm isNull: error | |
| 632 // 6. JWK alg missing AND input algorithm specified: use input value | |
| 633 blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm::createNull(); | |
| 634 const JwkAlgorithmInfo* algorithm_info = NULL; | |
| 635 std::string jwk_alg_value; | |
| 636 bool has_jwk_alg; | |
| 637 status = | |
| 638 GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg); | |
| 639 if (status.IsError()) | |
| 640 return status; | |
| 641 | |
| 642 if (has_jwk_alg) { | |
| 643 // JWK alg present | |
| 644 | |
| 645 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can | |
| 646 // only be from the RSA family. | |
| 647 | |
| 648 blink::WebCryptoAlgorithm jwk_algorithm = | |
| 649 blink::WebCryptoAlgorithm::createNull(); | |
| 650 algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value); | |
| 651 if (!algorithm_info || !algorithm_info->CreateAlgorithm(&jwk_algorithm)) | |
| 652 return Status::ErrorJwkUnrecognizedAlgorithm(); // case 1 | |
| 653 | |
| 654 // JWK alg valid | |
| 655 if (algorithm_or_null.isNull()) { | |
| 656 // input algorithm not specified | |
| 657 algorithm = jwk_algorithm; // case 2 | |
| 658 } else { | |
| 659 // input algorithm specified | |
| 660 if (!WebCryptoAlgorithmsConsistent(jwk_algorithm, algorithm_or_null)) | |
| 661 return Status::ErrorJwkAlgorithmInconsistent(); // case 3 | |
| 662 algorithm = algorithm_or_null; // case 4 | |
| 663 } | |
| 664 } else { | |
| 665 // JWK alg missing | |
| 666 if (algorithm_or_null.isNull()) | |
| 667 return Status::ErrorJwkAlgorithmMissing(); // case 5 | |
| 668 algorithm = algorithm_or_null; // case 6 | |
| 669 } | |
| 670 DCHECK(!algorithm.isNull()); | |
| 671 | |
| 672 // JWK "use" (optional) --> usage_mask parameter | |
| 673 std::string jwk_use_value; | |
| 674 bool has_jwk_use; | |
| 675 status = | |
| 676 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); | |
| 677 if (status.IsError()) | |
| 678 return status; | |
| 679 if (has_jwk_use) { | |
| 680 blink::WebCryptoKeyUsageMask jwk_usage_mask = 0; | |
| 681 if (jwk_use_value == "enc") { | |
| 682 jwk_usage_mask = | |
| 683 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; | |
| 684 } else if (jwk_use_value == "sig") { | |
| 685 jwk_usage_mask = | |
| 686 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | |
| 687 } else if (jwk_use_value == "wrap") { | |
| 688 jwk_usage_mask = | |
| 689 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey; | |
| 690 } else { | |
| 691 return Status::ErrorJwkUnrecognizedUsage(); | |
| 692 } | |
| 693 if ((jwk_usage_mask & usage_mask) != usage_mask) { | |
| 694 // A usage_mask must be a subset of jwk_usage_mask. | |
| 695 return Status::ErrorJwkUsageInconsistent(); | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 // JWK keying material --> ImportKeyInternal() | |
| 700 if (jwk_kty_value == "oct") { | |
| 701 | |
| 702 std::string jwk_k_value; | |
| 703 status = GetJwkBytes(dict_value, "k", &jwk_k_value); | |
| 704 if (status.IsError()) | |
| 705 return status; | |
| 706 | |
| 707 // Some JWK alg ID's embed information about the key length in the alg ID | |
| 708 // string. For example "A128CBC" implies the JWK carries 128 bits | |
| 709 // of key material. For such keys validate that enough bytes were provided. | |
| 710 // If this validation is not done, then it would be possible to select a | |
| 711 // different algorithm by passing a different lengthed key, since that is | |
| 712 // how WebCrypto interprets things. | |
| 713 if (algorithm_info && | |
| 714 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { | |
| 715 return Status::ErrorJwkIncorrectKeyLength(); | |
| 716 } | |
| 717 | |
| 718 return ImportKeyInternal(blink::WebCryptoKeyFormatRaw, | |
| 719 reinterpret_cast<const uint8*>(jwk_k_value.data()), | |
| 720 jwk_k_value.size(), | |
| 721 algorithm, | |
| 722 extractable, | |
| 723 usage_mask, | |
| 724 key); | |
| 725 } else if (jwk_kty_value == "RSA") { | |
| 726 | |
| 727 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | |
| 728 // in the JWK, while an RSA private key must have those, plus at least a "d" | |
| 729 // (private exponent) entry. | |
| 730 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
| 731 // section 6.3. | |
| 732 | |
| 733 // RSA private key import is not currently supported, so fail here if a "d" | |
| 734 // entry is found. | |
| 735 // TODO(padolph): Support RSA private key import. | |
| 736 if (dict_value->HasKey("d")) | |
| 737 return Status::ErrorJwkRsaPrivateKeyUnsupported(); | |
| 738 | |
| 739 std::string jwk_n_value; | |
| 740 status = GetJwkBytes(dict_value, "n", &jwk_n_value); | |
| 741 if (status.IsError()) | |
| 742 return status; | |
| 743 std::string jwk_e_value; | |
| 744 status = GetJwkBytes(dict_value, "e", &jwk_e_value); | |
| 745 if (status.IsError()) | |
| 746 return status; | |
| 747 | |
| 748 return ImportRsaPublicKeyInternal( | |
| 749 reinterpret_cast<const uint8*>(jwk_n_value.data()), | |
| 750 jwk_n_value.size(), | |
| 751 reinterpret_cast<const uint8*>(jwk_e_value.data()), | |
| 752 jwk_e_value.size(), | |
| 753 algorithm, | |
| 754 extractable, | |
| 755 usage_mask, | |
| 756 key); | |
| 757 | |
| 758 } else { | |
| 759 return Status::ErrorJwkUnrecognizedKty(); | |
| 760 } | |
| 761 | |
| 762 return Status::Success(); | |
| 763 } | |
| 764 | |
| 765 } // namespace content | 207 } // namespace content |
| OLD | NEW |