Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 49 additions & 20 deletions src/librepgp/stream-dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1651,19 +1651,32 @@ DumpContextDst::dump(bool raw_only)
}
}

/* check whether source is armored */
/* check whether source is armored; concatenated armored messages are all
* walked (see issue #2036) */
if (!raw_only && src.is_armored()) {
std::unique_ptr<Source> armor(new Source());
auto ret = init_armored_src(&armor->src(), &src);
if (ret) {
RNP_LOG("failed to parse armored data");
return ret;
rnp::ArmoredSource armor(
src, rnp::ArmoredSource::AllowBinary | rnp::ArmoredSource::AllowMultiple);
rnp_result_t ret = RNP_SUCCESS;
bool first = true;
while (true) {
if (armor.eof() && armor.multiple()) {
armor.restart();
}
if (armor.eof()) {
break;
}
if (first) {
dst_printf(dst, ":armored input\n");
first = false;
}
DumpContextDst ctx(armor.src(), dst);
ctx.copy_params(*this);
ret = ctx.dump(true);
if (ret) {
break;
}
}
dst_printf(dst, ":armored input\n");

std::unique_ptr<DumpContextDst> ctx(new DumpContextDst(armor->src(), dst));
ctx->copy_params(*this);
return ctx->dump(true);
return ret;
}

if (src.eof()) {
Expand Down Expand Up @@ -2706,17 +2719,33 @@ DumpContextJson::dump(bool raw_only)
return RNP_ERROR_BAD_FORMAT;
}
}
/* check whether source is armored */
/* check whether source is armored; concatenated armored messages are
* walked and all of their packets collected into a single array (#2036) */
if (!raw_only && src.is_armored()) {
std::unique_ptr<Source> armor(new Source());
rnp_result_t ret = init_armored_src(&armor->src(), &src);
if (ret) {
RNP_LOG("failed to parse armored data");
return ret;
rnp::ArmoredSource armor(
src, rnp::ArmoredSource::AllowBinary | rnp::ArmoredSource::AllowMultiple);
rnp_result_t ret = RNP_SUCCESS;
nlohmann::ordered_json res = nlohmann::ordered_json::array();
while (true) {
if (armor.eof() && armor.multiple()) {
armor.restart();
}
if (armor.eof()) {
break;
}
nlohmann::ordered_json block;
DumpContextJson ctx(armor.src(), &block);
ctx.copy_params(*this);
ret = ctx.dump(true);
if (ret) {
break;
}
if (block.is_array()) {
res.insert(res.end(), block.begin(), block.end());
}
}
DumpContextJson ctx(armor->src(), json);
ctx.copy_params(*this);
return ctx.dump(true);
*json = std::move(res);
return ret;
}

if (src.eof()) {
Expand Down
46 changes: 46 additions & 0 deletions src/tests/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,52 @@ TEST_F(rnp_tests, test_ffi_key_dump_edge_cases)
rnp_ffi_destroy(ffi);
}

TEST_F(rnp_tests, test_ffi_dump_multiple_armored_messages)
{
/* Regression for issue #2036: a single input file containing multiple
* armored OpenPGP messages must dump all of them, not just the first.
* Fixture has public keys in the first armor block and secret keys in
* the second. Before the fix the secret-key packets were silently
* dropped from --list-packets output. */
rnp_ffi_t ffi = NULL;
assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));

rnp_input_t input = NULL;
assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-both.asc"));
rnp_output_t output = NULL;
assert_rnp_success(rnp_output_to_memory(&output, 0));
assert_rnp_success(rnp_dump_packets_to_output(input, output, 0));
rnp_input_destroy(input);

uint8_t *buf = NULL;
size_t len = 0;
assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false));
std::string dstr(buf, buf + len);
rnp_output_destroy(output);

/* first armor block: public key + 2 userids + 2 public subkeys */
assert_true(dstr.find("Public key packet") != std::string::npos);
assert_true(dstr.find("Public subkey packet") != std::string::npos);
/* second armor block: secret key + secret subkeys -- the bit that
* used to be dropped because ArmoredSource without AllowMultiple stops
* at the first -----END...----- */
assert_true(dstr.find("Secret key packet") != std::string::npos);
assert_true(dstr.find("Secret subkey packet") != std::string::npos);

/* Same coverage via the JSON dumper, which has the same multi-armor
* bug class. */
assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-both.asc"));
char *json = NULL;
assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json));
rnp_input_destroy(input);
std::string jstr(json);
rnp_buffer_destroy(json);
assert_true(jstr.find("\"tag\":5") != std::string::npos); /* secret key */
assert_true(jstr.find("\"tag\":7") != std::string::npos); /* secret subkey */

rnp_ffi_destroy(ffi);
}

TEST_F(rnp_tests, test_ffi_key_userid_dump_has_no_special_chars)
{
rnp_ffi_t ffi = NULL;
Expand Down
Loading