|
20 | 20 | #include <openssl/sha.h> |
21 | 21 | #include <openssl/span.h> |
22 | 22 |
|
| 23 | +#include "../fipsmodule/cipher/internal.h" |
23 | 24 | #include "../internal.h" |
24 | 25 | #include "../test/file_test.h" |
25 | 26 | #include "../test/test_util.h" |
@@ -1406,6 +1407,106 @@ TEST(CipherTest, Empty_EVP_CIPHER_CTX_V1187459157) { |
1406 | 1407 | CHECK_ERROR(EVP_DecryptFinal(ctx.get(), out_vec.data(), &out_len), ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
1407 | 1408 | } |
1408 | 1409 |
|
| 1410 | +// Mock cipher whose |EVP_CTRL_INIT| handler always fails. It's needed to |
| 1411 | +// exercise the error paths in |EVP_CipherInit_ex| and |EVP_CIPHER_CTX_copy|. |
| 1412 | +// In addition, the mock needs |ctx_size| != 0, otherwise |cipher_data| is |
| 1413 | +// not allocated before the ctrl callback runs. This is needed below for |
| 1414 | +// InitErrorPathReleasesCipherData, to test |cipher_data| is free'd. |
| 1415 | +static int mock_failing_init(EVP_CIPHER_CTX *, const uint8_t *, const uint8_t *, |
| 1416 | + int) { |
| 1417 | + return 1; |
| 1418 | +} |
| 1419 | + |
| 1420 | +static int mock_failing_cipher(EVP_CIPHER_CTX *, uint8_t *, const uint8_t *, |
| 1421 | + size_t) { |
| 1422 | + return 1; |
| 1423 | +} |
| 1424 | + |
| 1425 | +static int mock_failing_ctrl(EVP_CIPHER_CTX *, int type, int, void *) { |
| 1426 | + if (type == EVP_CTRL_INIT) { |
| 1427 | + return 0; |
| 1428 | + } |
| 1429 | + return -1; |
| 1430 | +} |
| 1431 | + |
| 1432 | +static int mock_failing_copy_ctrl(EVP_CIPHER_CTX *, int type, int, void *) { |
| 1433 | + if (type == EVP_CTRL_INIT) { |
| 1434 | + return 1; |
| 1435 | + } |
| 1436 | + if (type == EVP_CTRL_COPY) { |
| 1437 | + return 0; |
| 1438 | + } |
| 1439 | + return -1; |
| 1440 | +} |
| 1441 | + |
| 1442 | +static const EVP_CIPHER kFailingInitCipher = { |
| 1443 | + NID_undef, |
| 1444 | + 1, |
| 1445 | + 16, |
| 1446 | + 0, |
| 1447 | + 128, |
| 1448 | + EVP_CIPH_STREAM_CIPHER | EVP_CIPH_CTRL_INIT, |
| 1449 | + mock_failing_init, |
| 1450 | + mock_failing_cipher, |
| 1451 | + nullptr, |
| 1452 | + mock_failing_ctrl, |
| 1453 | +}; |
| 1454 | + |
| 1455 | +static const EVP_CIPHER kFailingCopyCipher = { |
| 1456 | + NID_undef, |
| 1457 | + 1, |
| 1458 | + 16, |
| 1459 | + 0, |
| 1460 | + 128, |
| 1461 | + EVP_CIPH_STREAM_CIPHER | EVP_CIPH_CTRL_INIT | EVP_CIPH_CUSTOM_COPY, |
| 1462 | + mock_failing_init, |
| 1463 | + mock_failing_cipher, |
| 1464 | + nullptr, |
| 1465 | + mock_failing_copy_ctrl, |
| 1466 | +}; |
| 1467 | + |
| 1468 | +// On |EVP_CipherInit_ex| error path at |EVP_CTRL_INIT|, the context must not be |
| 1469 | +// left with an orphaned |cipher_data|. Otherwise a subsequent |
| 1470 | +// |EVP_CipherInit_ex| call on the same context would overwrite and leak it. Not |
| 1471 | +// exactly how one would probably use this API, but who knows. |
| 1472 | +TEST(CipherTest, InitErrorPathReleasesCipherData) { |
| 1473 | + bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new()); |
| 1474 | + ASSERT_TRUE(ctx); |
| 1475 | + |
| 1476 | + std::vector<uint8_t> key(16, 0); |
| 1477 | + EXPECT_FALSE(EVP_CipherInit_ex(ctx.get(), &kFailingInitCipher, nullptr, |
| 1478 | + key.data(), nullptr, 1)); |
| 1479 | + EXPECT_EQ(ctx->cipher, nullptr); |
| 1480 | + EXPECT_EQ(ctx->cipher_data, nullptr); |
| 1481 | + |
| 1482 | + // A follow-up init with a real cipher must still work and must not depend on |
| 1483 | + // leaked state from the failed attempt. |
| 1484 | + EXPECT_TRUE(EVP_CipherInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, |
| 1485 | + key.data(), nullptr, 1)); |
| 1486 | +} |
| 1487 | + |
| 1488 | +// |EVP_CIPHER_CTX_copy| shares the same shape of error path for |
| 1489 | +// |EVP_CTRL_COPY| failures as |EVP_CTRL_INIT| above. The destination's |
| 1490 | +// |cipher_data| must not be orphaned when the custom copy callback fails. |
| 1491 | +TEST(CipherTest, CopyErrorPathReleasesCipherData) { |
| 1492 | + bssl::UniquePtr<EVP_CIPHER_CTX> src(EVP_CIPHER_CTX_new()); |
| 1493 | + ASSERT_TRUE(src); |
| 1494 | + std::vector<uint8_t> key(16, 0); |
| 1495 | + ASSERT_TRUE(EVP_CipherInit_ex(src.get(), &kFailingCopyCipher, nullptr, |
| 1496 | + key.data(), nullptr, 1)); |
| 1497 | + |
| 1498 | + bssl::UniquePtr<EVP_CIPHER_CTX> dst(EVP_CIPHER_CTX_new()); |
| 1499 | + ASSERT_TRUE(dst); |
| 1500 | + EXPECT_FALSE(EVP_CIPHER_CTX_copy(dst.get(), src.get())); |
| 1501 | + EXPECT_EQ(dst->cipher, nullptr); |
| 1502 | + EXPECT_EQ(dst->cipher_data, nullptr); |
| 1503 | + |
| 1504 | + // A follow-up init with a real cipher on |dst| must still work and must not |
| 1505 | + // depend on leaked state from the failed copy. |
| 1506 | + EXPECT_TRUE(EVP_CipherInit_ex(dst.get(), EVP_aes_128_ecb(), nullptr, |
| 1507 | + key.data(), nullptr, 1)); |
| 1508 | +} |
| 1509 | + |
1409 | 1510 | struct CipherInfo { |
1410 | 1511 | const char *name; |
1411 | 1512 | const EVP_CIPHER *(*func)(void); |
|
0 commit comments