diff --git a/crypto/pkcs7/pkcs7_internal_bio_test.cc b/crypto/pkcs7/pkcs7_internal_bio_test.cc index 16debd15b27..cbd9c2e483a 100644 --- a/crypto/pkcs7/pkcs7_internal_bio_test.cc +++ b/crypto/pkcs7/pkcs7_internal_bio_test.cc @@ -123,9 +123,10 @@ TEST(PKCS7Test, CipherBIO) { ASSERT_TRUE(bio_mem); ASSERT_TRUE(BIO_push(bio_cipher.get(), bio_mem.get())); std::vector pt_vec, ct_vec, decrypted_pt_vec; - uint8_t buff[1024*1024]; - for (size_t wsize : (size_t[]){1, 3, 7, 8, 64, 7, 0, 923, sizeof(buff), 1, 8}) { - ASSERT_TRUE(RAND_bytes(buff, wsize)); + uint8_t buff[1024 * 1024]; + ASSERT_TRUE(RAND_bytes(buff, sizeof(buff))); + for (size_t wsize : + (size_t[]){1, 3, 7, 8, 64, 7, 0, 923, sizeof(buff), 1, 8}) { pt_vec.insert(pt_vec.end(), buff, buff + wsize); EXPECT_TRUE(BIO_write(bio_cipher.get(), buff, wsize) || wsize == 0); } @@ -136,14 +137,17 @@ TEST(PKCS7Test, CipherBIO) { ct_vec.insert(ct_vec.end(), buff, buff + bytes_read); } EXPECT_TRUE(BIO_reset(bio_cipher.get())); // also resets owned |bio_mem| - EXPECT_TRUE(BIO_write(bio_mem.get(), ct_vec.data(), ct_vec.size())); // replace ct + EXPECT_TRUE( + BIO_write(bio_mem.get(), ct_vec.data(), ct_vec.size())); // replace ct bio_mem.release(); // |bio_cipher| took ownership EXPECT_TRUE(BIO_get_cipher_ctx(bio_cipher.get(), &ctx)); ASSERT_TRUE( EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv, /*enc*/ 0)); - decrypted_pt_vec.resize(pt_vec.size()); - // TODO [childw] variable read sizes? - EXPECT_TRUE(BIO_read(bio_cipher.get(), decrypted_pt_vec.data(), decrypted_pt_vec.size())); + for (size_t rsize : + (size_t[]){1, 3, 7, 8, 64, 7, 0, 923, sizeof(buff), 1, 8}) { + EXPECT_TRUE(BIO_read(bio_cipher.get(), buff, rsize) || rsize == 0); + decrypted_pt_vec.insert(decrypted_pt_vec.end(), buff, buff + rsize); + } EXPECT_TRUE(BIO_get_cipher_status(bio_cipher.get())); EXPECT_EQ(pt_vec.size(), decrypted_pt_vec.size()); EXPECT_EQ(Bytes(pt_vec.data(), pt_vec.size()), @@ -151,40 +155,74 @@ TEST(PKCS7Test, CipherBIO) { // TODO [childw] explain induce write failure pt_vec.clear(); + ct_vec.clear(); decrypted_pt_vec.clear(); bio_cipher.reset(BIO_new(BIO_f_cipher())); ASSERT_TRUE(bio_cipher); EXPECT_TRUE(BIO_get_cipher_ctx(bio_cipher.get(), &ctx)); ASSERT_TRUE( EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv, /*enc*/ 1)); - bio_mem.reset(BIO_new(BIO_s_mem())); + bio_mem.reset(BIO_new(BIO_s_mock())); ASSERT_TRUE(bio_mem); ASSERT_TRUE(BIO_push(bio_cipher.get(), bio_mem.get())); - const int wsize = 16; - pt_vec.insert(pt_vec.end(), buff, buff + wsize); - EXPECT_TRUE(BIO_write(bio_cipher.get(), buff, wsize)); + const int io_size = 64; + pt_vec.insert(pt_vec.end(), buff, buff + io_size); + EXPECT_EQ(io_size, BIO_write(bio_cipher.get(), buff, io_size)); + // |bio_mem| is writeable, so shouldn't have any buffered data EXPECT_EQ(0UL, BIO_wpending(bio_cipher.get())); + // Set underlying BIO to r/o to induce buffering in |bio_cipher| BIO_set_flags(bio_mem.get(), BIO_FLAGS_MEM_RDONLY); - pt_vec.insert(pt_vec.end(), buff, buff + wsize); - EXPECT_TRUE(BIO_write(bio_cipher.get(), buff, wsize)); + pt_vec.insert(pt_vec.end(), buff, buff + io_size); + // Write to |bio_cipher| should still succeed while it can buffer + EXPECT_LT(0, BIO_write(bio_cipher.get(), buff, io_size)); BIO_clear_flags(bio_mem.get(), BIO_FLAGS_MEM_RDONLY); + // Now that we there's buffered data, |BIO_wpending| should be positive EXPECT_LT(0UL, BIO_wpending(bio_cipher.get())); EXPECT_TRUE(BIO_flush(bio_cipher.get())); + // Flush should empty the buffered data EXPECT_EQ(0UL, BIO_wpending(bio_cipher.get())); EXPECT_TRUE(BIO_get_cipher_status(bio_cipher.get())); EXPECT_TRUE(BIO_get_cipher_ctx(bio_cipher.get(), &ctx)); + + // TODO [childw] reset BIOs before testing read failures? + while (!BIO_eof(bio_mem.get())) { + size_t bytes_read = BIO_read(bio_mem.get(), buff, sizeof(buff)); + ct_vec.insert(ct_vec.end(), buff, buff + bytes_read); + } + EXPECT_TRUE(BIO_reset(bio_cipher.get())); // also resets owned |bio_mem| + EXPECT_TRUE( + BIO_write(bio_mem.get(), ct_vec.data(), ct_vec.size())); // replace ct + auto disable_reads = [](BIO *bio, int oper, const char *argp, size_t len, + int argi, long argl, int bio_ret, + size_t *processed) -> long { + return !(oper & BIO_CB_RETURN) && (oper & BIO_CB_READ); + }; ASSERT_TRUE( EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv, /*enc*/ 0)); decrypted_pt_vec.resize(pt_vec.size()); - // Must seek back to beginning of file before reading - ASSERT_EQ(0, BIO_seek(bio_mem.get(), 0)); // 0 indicates success here - EXPECT_TRUE(BIO_read(bio_cipher.get(), decrypted_pt_vec.data(), decrypted_pt_vec.size())); + EXPECT_EQ(decrypted_pt_vec.size(), BIO_pending(bio_cipher.get())); + //BIO_set_callback_ex(bio_mem.get(), disable_reads); + //EXPECT_EQ(0, BIO_read(bio_cipher.get(), decrypted_pt_vec.data() + io_size, + //io_size)); + //BIO_set_callback_ex(bio_mem.get(), nullptr); + EXPECT_EQ(io_size, + BIO_read(bio_cipher.get(), decrypted_pt_vec.data(), io_size)); + EXPECT_EQ(decrypted_pt_vec.size() - io_size, BIO_pending(bio_cipher.get())); + // Disable reads from underlying BIO + BIO_set_callback_ex(bio_mem.get(), disable_reads); + // BIO_set_flags(bio_mem.get(), BIO_FLAGS_MEM_RDONLY); + EXPECT_GE(io_size, BIO_read(bio_cipher.get(), decrypted_pt_vec.data() + io_size, + io_size)); + EXPECT_EQ(0UL, BIO_pending(bio_cipher.get())); + // Re-enable reads from underlying BIO + BIO_set_callback_ex(bio_mem.get(), nullptr); + // BIO_clear_flags(bio_mem.get(), BIO_FLAGS_MEM_RDONLY); + EXPECT_EQ(-1, BIO_read(bio_cipher.get(), + decrypted_pt_vec.data() + io_size, io_size)); + EXPECT_EQ(0UL, BIO_pending(bio_cipher.get())); EXPECT_TRUE(BIO_get_cipher_status(bio_cipher.get())); EXPECT_EQ(pt_vec.size(), decrypted_pt_vec.size()); EXPECT_EQ(Bytes(pt_vec.data(), pt_vec.size()), Bytes(decrypted_pt_vec.data(), decrypted_pt_vec.size())); bio_mem.release(); // |bio_cipher| took ownership - - - // TODO [childw] induce read failures? } diff --git a/crypto/test/test_util.cc b/crypto/test/test_util.cc index a0e923eb923..01ba420b1e9 100644 --- a/crypto/test/test_util.cc +++ b/crypto/test/test_util.cc @@ -156,3 +156,47 @@ void CustomDataFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, free(ptr); } +static int bio_mock_new(BIO *bio) { + BIO *ret = BIO_new(BIO_s_mem()); + if (ret) { + *bio = *ret; + return 1; + } + return 0; +} + +static int bio_mock_free(BIO *bio) { return BIO_free(bio); } + +static int bio_mock_read(BIO *bio, char *out, int outl) { + printf("FOOBAR\n"); + if (BIO_test_flags(bio, BIO_FLAGS_MEM_RDONLY)) { + return 0; + } + return BIO_read(bio, out, outl); +} + +static int bio_mock_write(BIO *bio, const char *in, int inl) { + printf("BARFOO\n"); + if (BIO_test_flags(bio, BIO_FLAGS_MEM_RDONLY)) { + return 0; + } + return BIO_write(bio, in, inl); +} + +static int bio_mock_gets(BIO *bio, char *buf, int size) { + return BIO_gets(bio, buf, size); +} + +static long bio_mock_ctrl(BIO *bio, int cmd, long num, void *ptr) { + return BIO_ctrl(bio, cmd, num, ptr); +} + +static const BIO_METHOD bio_mock_method = { + BIO_TYPE_MEM, "mock with memory buffer", + bio_mock_write, bio_mock_read, + NULL /* puts */, bio_mock_gets, + bio_mock_ctrl, bio_mock_new, + bio_mock_free, NULL /* callback_ctrl */, +}; + +const BIO_METHOD *BIO_s_mock(void) { return &bio_mock_method; } diff --git a/crypto/test/test_util.h b/crypto/test/test_util.h index 4f531496025..2b073267796 100644 --- a/crypto/test/test_util.h +++ b/crypto/test/test_util.h @@ -111,4 +111,6 @@ typedef struct { void CustomDataFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int index, long argl, void *argp); +const BIO_METHOD *BIO_s_mock(void); + #endif // OPENSSL_HEADER_CRYPTO_TEST_TEST_UTIL_H