Skip to content

Commit f044253

Browse files
authored
Merge branch 'main' into ocsp-tighten-url-parsing
2 parents fbd15ac + c6104fa commit f044253

3 files changed

Lines changed: 109 additions & 0 deletions

File tree

.github/workflows/zig.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
concurrency:
88
group: ${{ github.workflow }}-${{ github.ref_name }}
99
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
1014
jobs:
1115
zig:
1216
if: github.repository_owner == 'aws'

crypto/cipher_extra/cipher_test.cc

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <openssl/sha.h>
2121
#include <openssl/span.h>
2222

23+
#include "../fipsmodule/cipher/internal.h"
2324
#include "../internal.h"
2425
#include "../test/file_test.h"
2526
#include "../test/test_util.h"
@@ -1406,6 +1407,106 @@ TEST(CipherTest, Empty_EVP_CIPHER_CTX_V1187459157) {
14061407
CHECK_ERROR(EVP_DecryptFinal(ctx.get(), out_vec.data(), &out_len), ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
14071408
}
14081409

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+
14091510
struct CipherInfo {
14101511
const char *name;
14111512
const EVP_CIPHER *(*func)(void);

crypto/fipsmodule/cipher/cipher.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) {
7676

7777
if (in->cipher->flags & EVP_CIPH_CUSTOM_COPY) {
7878
if (!in->cipher->ctrl((EVP_CIPHER_CTX *)in, EVP_CTRL_COPY, 0, out)) {
79+
OPENSSL_free(out->cipher_data);
80+
out->cipher_data = NULL;
7981
out->cipher = NULL;
8082
return 0;
8183
}
@@ -130,6 +132,8 @@ int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
130132

131133
if (ctx->cipher->flags & EVP_CIPH_CTRL_INIT) {
132134
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_INIT, 0, NULL)) {
135+
OPENSSL_free(ctx->cipher_data);
136+
ctx->cipher_data = NULL;
133137
ctx->cipher = NULL;
134138
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INITIALIZATION_ERROR);
135139
return 0;

0 commit comments

Comments
 (0)