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 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
479 *mechanism = CKM_AES_GCM; | 479 *mechanism = CKM_AES_GCM; |
480 *flags = CKF_ENCRYPT | CKF_DECRYPT; | 480 *flags = CKF_ENCRYPT | CKF_DECRYPT; |
481 break; | 481 break; |
482 } | 482 } |
483 default: | 483 default: |
484 return Status::ErrorUnsupported(); | 484 return Status::ErrorUnsupported(); |
485 } | 485 } |
486 return Status::Success(); | 486 return Status::Success(); |
487 } | 487 } |
488 | 488 |
| 489 Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, |
| 490 SymKey* wrapping_key, |
| 491 CK_MECHANISM_TYPE mechanism, |
| 492 CK_FLAGS flags, |
| 493 crypto::ScopedPK11SymKey* unwrapped_key) { |
| 494 DCHECK_GE(wrapped_key_data.byte_length(), 24u); |
| 495 DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); |
| 496 |
| 497 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); |
| 498 crypto::ScopedSECItem param_item( |
| 499 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); |
| 500 if (!param_item) |
| 501 return Status::ErrorUnexpected(); |
| 502 |
| 503 SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); |
| 504 |
| 505 // The plaintext length is always 64 bits less than the data size. |
| 506 const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; |
| 507 |
| 508 crypto::ScopedPK11SymKey new_key(PK11_UnwrapSymKey(wrapping_key->key(), |
| 509 CKM_NSS_AES_KEY_WRAP, |
| 510 param_item.get(), |
| 511 &cipher_text, |
| 512 mechanism, |
| 513 flags, |
| 514 plaintext_length)); |
| 515 // TODO(padolph): Use NSS PORT_GetError() and friends to report a more |
| 516 // accurate error, providing if doesn't leak any information to web pages |
| 517 // about other web crypto users, key details, etc. |
| 518 if (!new_key) |
| 519 return Status::Error(); |
| 520 |
| 521 // TODO(padolph): Change to "defined(USE_NSS)" once the NSS fix for |
| 522 // https://bugzilla.mozilla.org/show_bug.cgi?id=981170 rolls into chromium. |
| 523 #if 1 |
| 524 // ------- Start NSS bug workaround |
| 525 // Workaround for https://code.google.com/p/chromium/issues/detail?id=349939 |
| 526 // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, with |
| 527 // a reasonable length but with key data pointing to uninitialized memory. |
| 528 // This workaround re-wraps the key and compares the result with the incoming |
| 529 // data, and fails if there is a difference. This prevents returning a bad key |
| 530 // to the caller. |
| 531 const unsigned int output_length = wrapped_key_data.byte_length(); |
| 532 std::vector<unsigned char> buffer(output_length, 0); |
| 533 SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(buffer)); |
| 534 if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, |
| 535 param_item.get(), |
| 536 wrapping_key->key(), |
| 537 new_key.get(), |
| 538 &wrapped_key_item)) { |
| 539 return Status::Error(); |
| 540 } |
| 541 if (wrapped_key_item.len != wrapped_key_data.byte_length() || |
| 542 memcmp(wrapped_key_item.data, |
| 543 wrapped_key_data.bytes(), |
| 544 wrapped_key_item.len) != 0) { |
| 545 return Status::Error(); |
| 546 } |
| 547 // ------- End NSS bug workaround |
| 548 #endif |
| 549 |
| 550 *unwrapped_key = new_key.Pass(); |
| 551 return Status::Success(); |
| 552 } |
| 553 |
489 } // namespace | 554 } // namespace |
490 | 555 |
491 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, | 556 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, |
492 const CryptoData& key_data, | 557 const CryptoData& key_data, |
493 bool extractable, | 558 bool extractable, |
494 blink::WebCryptoKeyUsageMask usage_mask, | 559 blink::WebCryptoKeyUsageMask usage_mask, |
495 blink::WebCryptoKey* key) { | 560 blink::WebCryptoKey* key) { |
496 | 561 |
497 DCHECK(!algorithm.isNull()); | 562 DCHECK(!algorithm.isNull()); |
498 | 563 |
(...skipping 639 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1138 | 1203 |
1139 return Status::Success(); | 1204 return Status::Success(); |
1140 } | 1205 } |
1141 | 1206 |
1142 Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, | 1207 Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, |
1143 SymKey* wrapping_key, | 1208 SymKey* wrapping_key, |
1144 const blink::WebCryptoAlgorithm& algorithm, | 1209 const blink::WebCryptoAlgorithm& algorithm, |
1145 bool extractable, | 1210 bool extractable, |
1146 blink::WebCryptoKeyUsageMask usage_mask, | 1211 blink::WebCryptoKeyUsageMask usage_mask, |
1147 blink::WebCryptoKey* key) { | 1212 blink::WebCryptoKey* key) { |
1148 DCHECK_GE(wrapped_key_data.byte_length(), 24u); | |
1149 DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); | |
1150 | |
1151 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); | |
1152 crypto::ScopedSECItem param_item( | |
1153 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); | |
1154 if (!param_item) | |
1155 return Status::ErrorUnexpected(); | |
1156 | |
1157 SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); | |
1158 | |
1159 // The plaintext length is always 64 bits less than the data size. | |
1160 const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; | |
1161 | |
1162 // Determine the proper NSS key properties from the input algorithm. | 1213 // Determine the proper NSS key properties from the input algorithm. |
1163 CK_MECHANISM_TYPE mechanism; | 1214 CK_MECHANISM_TYPE mechanism; |
1164 CK_FLAGS flags; | 1215 CK_FLAGS flags; |
1165 Status status = | 1216 Status status = |
1166 WebCryptoAlgorithmToNssMechFlags(algorithm, &mechanism, &flags); | 1217 WebCryptoAlgorithmToNssMechFlags(algorithm, &mechanism, &flags); |
1167 if (status.IsError()) | 1218 if (status.IsError()) |
1168 return status; | 1219 return status; |
1169 | 1220 |
1170 crypto::ScopedPK11SymKey unwrapped_key(PK11_UnwrapSymKey(wrapping_key->key(), | 1221 crypto::ScopedPK11SymKey unwrapped_key; |
1171 CKM_NSS_AES_KEY_WRAP, | 1222 status = DoUnwrapSymKeyAesKw( |
1172 param_item.get(), | 1223 wrapped_key_data, wrapping_key, mechanism, flags, &unwrapped_key); |
1173 &cipher_text, | 1224 if (status.IsError()) |
1174 mechanism, | 1225 return status; |
1175 flags, | |
1176 plaintext_length)); | |
1177 // TODO(padolph): Use NSS PORT_GetError() and friends to report a more | |
1178 // accurate error, providing if doesn't leak any information to web pages | |
1179 // about other web crypto users, key details, etc. | |
1180 if (!unwrapped_key) | |
1181 return Status::Error(); | |
1182 | |
1183 // TODO(padolph): Change to "defined(USE_NSS)" once the NSS fix for | |
1184 // https://bugzilla.mozilla.org/show_bug.cgi?id=981170 rolls into chromium. | |
1185 #if 1 | |
1186 // ------- Start NSS bug workaround | |
1187 // Workaround for https://code.google.com/p/chromium/issues/detail?id=349939 | |
1188 // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, with | |
1189 // a reasonable length but with key data pointing to uninitialized memory. | |
1190 // This workaround re-wraps the key and compares the result with the incoming | |
1191 // data, and fails if there is a difference. This prevents returning a bad key | |
1192 // to the caller. | |
1193 const unsigned int output_length = wrapped_key_data.byte_length(); | |
1194 std::vector<unsigned char> buffer(output_length, 0); | |
1195 SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(buffer)); | |
1196 if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, | |
1197 param_item.get(), | |
1198 wrapping_key->key(), | |
1199 unwrapped_key.get(), | |
1200 &wrapped_key_item)) { | |
1201 return Status::Error(); | |
1202 } | |
1203 if (wrapped_key_item.len != wrapped_key_data.byte_length() || | |
1204 memcmp(wrapped_key_item.data, | |
1205 wrapped_key_data.bytes(), | |
1206 wrapped_key_item.len) != 0) { | |
1207 return Status::Error(); | |
1208 } | |
1209 // ------- End NSS bug workaround | |
1210 #endif | |
1211 | 1226 |
1212 blink::WebCryptoKeyAlgorithm key_algorithm; | 1227 blink::WebCryptoKeyAlgorithm key_algorithm; |
1213 if (!CreateSecretKeyAlgorithm(algorithm, plaintext_length, &key_algorithm)) | 1228 if (!CreateSecretKeyAlgorithm( |
| 1229 algorithm, PK11_GetKeyLength(unwrapped_key.get()), &key_algorithm)) |
1214 return Status::ErrorUnexpected(); | 1230 return Status::ErrorUnexpected(); |
1215 | 1231 |
1216 *key = blink::WebCryptoKey::create(new SymKey(unwrapped_key.Pass()), | 1232 *key = blink::WebCryptoKey::create(new SymKey(unwrapped_key.Pass()), |
1217 blink::WebCryptoKeyTypeSecret, | 1233 blink::WebCryptoKeyTypeSecret, |
1218 extractable, | 1234 extractable, |
1219 key_algorithm, | 1235 key_algorithm, |
1220 usage_mask); | 1236 usage_mask); |
1221 return Status::Success(); | 1237 return Status::Success(); |
1222 } | 1238 } |
1223 | 1239 |
| 1240 Status DecryptAesKw(SymKey* wrapping_key, |
| 1241 const CryptoData& data, |
| 1242 blink::WebArrayBuffer* buffer) { |
| 1243 // Due to limitations in the NSS API for the AES-KW algorithm, |data| must be |
| 1244 // temporarily viewed as a symmetric key to be unwrapped (decrypted). |
| 1245 crypto::ScopedPK11SymKey decrypted; |
| 1246 Status status = DoUnwrapSymKeyAesKw( |
| 1247 data, wrapping_key, CKK_GENERIC_SECRET, CKA_ENCRYPT, &decrypted); |
| 1248 if (status.IsError()) |
| 1249 return status; |
| 1250 |
| 1251 // Once the decrypt is complete, extract the resultant raw bytes from NSS and |
| 1252 // return them to the caller. |
| 1253 if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) |
| 1254 return Status::Error(); |
| 1255 const SECItem* const key_data = PK11_GetKeyData(decrypted.get()); |
| 1256 if (!key_data) |
| 1257 return Status::Error(); |
| 1258 *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); |
| 1259 |
| 1260 return Status::Success(); |
| 1261 } |
| 1262 |
1224 Status WrapSymKeyRsaEs(PublicKey* wrapping_key, | 1263 Status WrapSymKeyRsaEs(PublicKey* wrapping_key, |
1225 SymKey* key, | 1264 SymKey* key, |
1226 blink::WebArrayBuffer* buffer) { | 1265 blink::WebArrayBuffer* buffer) { |
1227 // Check the raw length of the key to be wrapped against the max size allowed | 1266 // Check the raw length of the key to be wrapped against the max size allowed |
1228 // by the RSA wrapping key. With PKCS#1 v1.5 padding used in this function, | 1267 // by the RSA wrapping key. With PKCS#1 v1.5 padding used in this function, |
1229 // the maximum data length that can be encrypted is the wrapping_key's modulus | 1268 // the maximum data length that can be encrypted is the wrapping_key's modulus |
1230 // byte length minus eleven bytes. | 1269 // byte length minus eleven bytes. |
1231 const unsigned int input_length_bytes = PK11_GetKeyLength(key->key()); | 1270 const unsigned int input_length_bytes = PK11_GetKeyLength(key->key()); |
1232 const unsigned int modulus_length_bytes = | 1271 const unsigned int modulus_length_bytes = |
1233 SECKEY_PublicKeyStrength(wrapping_key->key()); | 1272 SECKEY_PublicKeyStrength(wrapping_key->key()); |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1298 key_algorithm, | 1337 key_algorithm, |
1299 usage_mask); | 1338 usage_mask); |
1300 return Status::Success(); | 1339 return Status::Success(); |
1301 } | 1340 } |
1302 | 1341 |
1303 } // namespace platform | 1342 } // namespace platform |
1304 | 1343 |
1305 } // namespace webcrypto | 1344 } // namespace webcrypto |
1306 | 1345 |
1307 } // namespace content | 1346 } // namespace content |
OLD | NEW |