Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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/child/webcrypto/platform_crypto.h" | 5 #include "content/child/webcrypto/platform_crypto.h" |
| 6 | 6 |
| 7 #include <cryptohi.h> | 7 #include <cryptohi.h> |
| 8 #include <pk11pub.h> | 8 #include <pk11pub.h> |
| 9 #include <sechash.h> | 9 #include <sechash.h> |
| 10 | 10 |
| (...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 483 *mechanism = CKM_AES_GCM; | 483 *mechanism = CKM_AES_GCM; |
| 484 *flags = CKF_ENCRYPT | CKF_DECRYPT; | 484 *flags = CKF_ENCRYPT | CKF_DECRYPT; |
| 485 break; | 485 break; |
| 486 } | 486 } |
| 487 default: | 487 default: |
| 488 return Status::ErrorUnsupported(); | 488 return Status::ErrorUnsupported(); |
| 489 } | 489 } |
| 490 return Status::Success(); | 490 return Status::Success(); |
| 491 } | 491 } |
| 492 | 492 |
| 493 Status AesKwUnwrapSymKey(const CryptoData& wrapped_key_data, | |
|
eroman
2014/03/14 22:14:53
I don't like this name as it is a subtle re-orderi
padolph
2014/03/17 03:24:36
Chose UnwrapSymKeyAesKw. Thank you - I agree it wa
| |
| 494 SymKey* wrapping_key, | |
| 495 CK_MECHANISM_TYPE mechanism, | |
| 496 CK_FLAGS flags, | |
| 497 crypto::ScopedPK11SymKey* unwrapped_key) { | |
| 498 DCHECK_GE(wrapped_key_data.byte_length(), 24u); | |
| 499 DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); | |
| 500 | |
| 501 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); | |
| 502 crypto::ScopedSECItem param_item( | |
| 503 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); | |
| 504 if (!param_item) | |
| 505 return Status::ErrorUnexpected(); | |
| 506 | |
| 507 SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); | |
| 508 | |
| 509 // The plaintext length is always 64 bits less than the data size. | |
| 510 const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; | |
| 511 | |
| 512 crypto::ScopedPK11SymKey new_key(PK11_UnwrapSymKey(wrapping_key->key(), | |
| 513 CKM_NSS_AES_KEY_WRAP, | |
| 514 param_item.get(), | |
| 515 &cipher_text, | |
| 516 mechanism, | |
| 517 flags, | |
| 518 plaintext_length)); | |
| 519 // TODO(padolph): Use NSS PORT_GetError() and friends to report a more | |
| 520 // accurate error, providing if doesn't leak any information to web pages | |
| 521 // about other web crypto users, key details, etc. | |
| 522 if (!new_key) | |
| 523 return Status::Error(); | |
| 524 | |
| 525 // TODO(padolph): Change to "defined(USE_NSS)" once the NSS fix for | |
| 526 // https://bugzilla.mozilla.org/show_bug.cgi?id=981170 rolls into chromium. | |
| 527 #if 1 | |
| 528 // ------- Start NSS bug workaround | |
| 529 // Workaround for https://code.google.com/p/chromium/issues/detail?id=349939 | |
| 530 // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, with | |
| 531 // a reasonable length but with key data pointing to uninitialized memory. | |
| 532 // This workaround re-wraps the key and compares the result with the incoming | |
| 533 // data, and fails if there is a difference. This prevents returning a bad key | |
| 534 // to the caller. | |
| 535 const unsigned int output_length = wrapped_key_data.byte_length(); | |
| 536 std::vector<unsigned char> buffer(output_length, 0); | |
| 537 SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(buffer)); | |
| 538 if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, | |
| 539 param_item.get(), | |
| 540 wrapping_key->key(), | |
| 541 new_key.get(), | |
| 542 &wrapped_key_item)) { | |
| 543 return Status::Error(); | |
| 544 } | |
| 545 if (wrapped_key_item.len != wrapped_key_data.byte_length() || | |
| 546 memcmp(wrapped_key_item.data, | |
| 547 wrapped_key_data.bytes(), | |
| 548 wrapped_key_item.len) != 0) { | |
| 549 return Status::Error(); | |
| 550 } | |
| 551 // ------- End NSS bug workaround | |
| 552 #endif | |
| 553 | |
| 554 *unwrapped_key = new_key.Pass(); | |
| 555 return Status::Success(); | |
| 556 } | |
| 557 | |
| 493 } // namespace | 558 } // namespace |
| 494 | 559 |
| 495 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, | 560 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, |
| 496 const CryptoData& key_data, | 561 const CryptoData& key_data, |
| 497 bool extractable, | 562 bool extractable, |
| 498 blink::WebCryptoKeyUsageMask usage_mask, | 563 blink::WebCryptoKeyUsageMask usage_mask, |
| 499 blink::WebCryptoKey* key) { | 564 blink::WebCryptoKey* key) { |
| 500 | 565 |
| 501 DCHECK(!algorithm.isNull()); | 566 DCHECK(!algorithm.isNull()); |
| 502 | 567 |
| (...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1148 | 1213 |
| 1149 return Status::Success(); | 1214 return Status::Success(); |
| 1150 } | 1215 } |
| 1151 | 1216 |
| 1152 Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, | 1217 Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, |
| 1153 SymKey* wrapping_key, | 1218 SymKey* wrapping_key, |
| 1154 const blink::WebCryptoAlgorithm& algorithm, | 1219 const blink::WebCryptoAlgorithm& algorithm, |
| 1155 bool extractable, | 1220 bool extractable, |
| 1156 blink::WebCryptoKeyUsageMask usage_mask, | 1221 blink::WebCryptoKeyUsageMask usage_mask, |
| 1157 blink::WebCryptoKey* key) { | 1222 blink::WebCryptoKey* key) { |
| 1158 DCHECK_GE(wrapped_key_data.byte_length(), 24u); | |
| 1159 DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); | |
| 1160 | |
| 1161 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); | |
| 1162 crypto::ScopedSECItem param_item( | |
| 1163 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); | |
| 1164 if (!param_item) | |
| 1165 return Status::ErrorUnexpected(); | |
| 1166 | |
| 1167 SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); | |
| 1168 | |
| 1169 // The plaintext length is always 64 bits less than the data size. | |
| 1170 const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; | |
| 1171 | |
| 1172 // Determine the proper NSS key properties from the input algorithm. | 1223 // Determine the proper NSS key properties from the input algorithm. |
| 1173 CK_MECHANISM_TYPE mechanism; | 1224 CK_MECHANISM_TYPE mechanism; |
| 1174 CK_FLAGS flags; | 1225 CK_FLAGS flags; |
| 1175 Status status = | 1226 Status status = |
| 1176 WebCryptoAlgorithmToNssMechFlags(algorithm, &mechanism, &flags); | 1227 WebCryptoAlgorithmToNssMechFlags(algorithm, &mechanism, &flags); |
| 1177 if (status.IsError()) | 1228 if (status.IsError()) |
| 1178 return status; | 1229 return status; |
| 1179 | 1230 |
| 1180 crypto::ScopedPK11SymKey unwrapped_key(PK11_UnwrapSymKey(wrapping_key->key(), | 1231 crypto::ScopedPK11SymKey unwrapped_key; |
| 1181 CKM_NSS_AES_KEY_WRAP, | 1232 status = AesKwUnwrapSymKey( |
| 1182 param_item.get(), | 1233 wrapped_key_data, wrapping_key, mechanism, flags, &unwrapped_key); |
| 1183 &cipher_text, | 1234 if (status.IsError()) |
| 1184 mechanism, | 1235 return status; |
| 1185 flags, | |
| 1186 plaintext_length)); | |
| 1187 // TODO(padolph): Use NSS PORT_GetError() and friends to report a more | |
| 1188 // accurate error, providing if doesn't leak any information to web pages | |
| 1189 // about other web crypto users, key details, etc. | |
| 1190 if (!unwrapped_key) | |
| 1191 return Status::Error(); | |
| 1192 | |
| 1193 // TODO(padolph): Change to "defined(USE_NSS)" once the NSS fix for | |
| 1194 // https://bugzilla.mozilla.org/show_bug.cgi?id=981170 rolls into chromium. | |
| 1195 #if 1 | |
| 1196 // ------- Start NSS bug workaround | |
| 1197 // Workaround for https://code.google.com/p/chromium/issues/detail?id=349939 | |
| 1198 // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, with | |
| 1199 // a reasonable length but with key data pointing to uninitialized memory. | |
| 1200 // This workaround re-wraps the key and compares the result with the incoming | |
| 1201 // data, and fails if there is a difference. This prevents returning a bad key | |
| 1202 // to the caller. | |
| 1203 const unsigned int output_length = wrapped_key_data.byte_length(); | |
| 1204 std::vector<unsigned char> buffer(output_length, 0); | |
| 1205 SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(buffer)); | |
| 1206 if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, | |
| 1207 param_item.get(), | |
| 1208 wrapping_key->key(), | |
| 1209 unwrapped_key.get(), | |
| 1210 &wrapped_key_item)) { | |
| 1211 return Status::Error(); | |
| 1212 } | |
| 1213 if (wrapped_key_item.len != wrapped_key_data.byte_length() || | |
| 1214 memcmp(wrapped_key_item.data, | |
| 1215 wrapped_key_data.bytes(), | |
| 1216 wrapped_key_item.len) != 0) { | |
| 1217 return Status::Error(); | |
| 1218 } | |
| 1219 // ------- End NSS bug workaround | |
| 1220 #endif | |
| 1221 | 1236 |
| 1222 blink::WebCryptoKeyAlgorithm key_algorithm; | 1237 blink::WebCryptoKeyAlgorithm key_algorithm; |
| 1223 if (!CreateSecretKeyAlgorithm(algorithm, plaintext_length, &key_algorithm)) | 1238 if (!CreateSecretKeyAlgorithm( |
| 1239 algorithm, PK11_GetKeyLength(unwrapped_key.get()), &key_algorithm)) | |
| 1224 return Status::ErrorUnexpected(); | 1240 return Status::ErrorUnexpected(); |
| 1225 | 1241 |
| 1226 *key = blink::WebCryptoKey::create(new SymKey(unwrapped_key.Pass()), | 1242 *key = blink::WebCryptoKey::create(new SymKey(unwrapped_key.Pass()), |
| 1227 blink::WebCryptoKeyTypeSecret, | 1243 blink::WebCryptoKeyTypeSecret, |
| 1228 extractable, | 1244 extractable, |
| 1229 key_algorithm, | 1245 key_algorithm, |
| 1230 usage_mask); | 1246 usage_mask); |
| 1231 return Status::Success(); | 1247 return Status::Success(); |
| 1232 } | 1248 } |
| 1233 | 1249 |
| 1250 Status DecryptAesKw(SymKey* wrapping_key, | |
| 1251 const CryptoData& data, | |
| 1252 blink::WebArrayBuffer* buffer) { | |
| 1253 // Due to limitations in the NSS API for the AES-KW algorithm, |data| must be | |
| 1254 // temporarily viewed as a symmetric key to be unwrapped (decrypted). | |
|
eroman
2014/03/13 00:17:18
Deferring to @rsleevi. I had the impression from R
padolph
2014/03/13 01:26:34
I tried that method earlier but could not make it
padolph
2014/03/17 03:24:36
After a round of discussions with Ryan and experim
| |
| 1255 crypto::ScopedPK11SymKey decrypted; | |
| 1256 Status status = AesKwUnwrapSymKey( | |
| 1257 data, wrapping_key, CKK_GENERIC_SECRET, CKA_ENCRYPT, &decrypted); | |
| 1258 if (status.IsError()) | |
| 1259 return status; | |
| 1260 | |
| 1261 // Once the decrypt is complete, extract the resultant raw bytes from NSS and | |
| 1262 // return them to the caller. | |
| 1263 if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) | |
| 1264 return Status::Error(); | |
| 1265 SECItem* key_data = PK11_GetKeyData(decrypted.get()); | |
|
eroman
2014/03/14 22:14:53
Can you make this const?
padolph
2014/03/17 03:24:36
Done.
| |
| 1266 if (!key_data) | |
| 1267 return Status::Error(); | |
| 1268 *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); | |
| 1269 | |
| 1270 return Status::Success(); | |
| 1271 } | |
| 1272 | |
| 1234 Status WrapSymKeyRsaEs(PublicKey* wrapping_key, | 1273 Status WrapSymKeyRsaEs(PublicKey* wrapping_key, |
| 1235 SymKey* key, | 1274 SymKey* key, |
| 1236 blink::WebArrayBuffer* buffer) { | 1275 blink::WebArrayBuffer* buffer) { |
| 1237 // Check the raw length of the key to be wrapped against the max size allowed | 1276 // Check the raw length of the key to be wrapped against the max size allowed |
| 1238 // by the RSA wrapping key. With PKCS#1 v1.5 padding used in this function, | 1277 // by the RSA wrapping key. With PKCS#1 v1.5 padding used in this function, |
| 1239 // the maximum data length that can be encrypted is the wrapping_key's modulus | 1278 // the maximum data length that can be encrypted is the wrapping_key's modulus |
| 1240 // byte length minus eleven bytes. | 1279 // byte length minus eleven bytes. |
| 1241 const unsigned int input_length_bytes = PK11_GetKeyLength(key->key()); | 1280 const unsigned int input_length_bytes = PK11_GetKeyLength(key->key()); |
| 1242 const unsigned int modulus_length_bytes = | 1281 const unsigned int modulus_length_bytes = |
| 1243 SECKEY_PublicKeyStrength(wrapping_key->key()); | 1282 SECKEY_PublicKeyStrength(wrapping_key->key()); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1308 key_algorithm, | 1347 key_algorithm, |
| 1309 usage_mask); | 1348 usage_mask); |
| 1310 return Status::Success(); | 1349 return Status::Success(); |
| 1311 } | 1350 } |
| 1312 | 1351 |
| 1313 } // namespace platform | 1352 } // namespace platform |
| 1314 | 1353 |
| 1315 } // namespace webcrypto | 1354 } // namespace webcrypto |
| 1316 | 1355 |
| 1317 } // namespace content | 1356 } // namespace content |
| OLD | NEW |