Skip to content

Commit

Permalink
Add support for v5 document signatures.
Browse files Browse the repository at this point in the history
  • Loading branch information
ni4 committed Sep 27, 2023
1 parent 1202389 commit 514662b
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 27 deletions.
38 changes: 26 additions & 12 deletions src/lib/crypto/signatures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@
* @param sig populated or loaded signature
* @param hbuf buffer to store the resulting hash. Must be large enough for hash output.
* @param hlen on success will be filled with the hash size, otherwise zeroed
* @param hdr literal packet header for attached signatures or NULL otherwise.
* @return RNP_SUCCESS on success or some error otherwise
*/
static void
signature_hash_finish(const pgp_signature_t &sig, rnp::Hash &hash, uint8_t *hbuf, size_t &hlen)
signature_hash_finish(const pgp_signature_t & sig,
rnp::Hash & hash,
uint8_t * hbuf,
size_t & hlen,
const pgp_literal_hdr_t *hdr)
{
hash.add(sig.hashed_data, sig.hashed_len);
switch (sig.version) {
Expand All @@ -58,13 +63,20 @@ signature_hash_finish(const pgp_signature_t &sig, rnp::Hash &hash, uint8_t *hbuf
break;
}
case PGP_V5: {
/* TODO: support for literal packet metadata for attached signatures, see
* draft-koch, 5.2.4. */
uint64_t hash_len = sig.hashed_len;
if (sig.is_document()) {
uint8_t doc_trailer[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
hash.add(doc_trailer, 6);
hash_len += 6;
/* This data is not added to the hash_len as per spec */
if (hdr) {
doc_trailer[0] = hdr->format;
doc_trailer[1] = hdr->fname_len;
write_uint32(&doc_trailer[2], hdr->timestamp);
hash.add(doc_trailer, 2);
hash.add(hdr->fname, hdr->fname_len);
hash.add(&doc_trailer[2], 4);
} else {
hash.add(doc_trailer, 6);
}
}
uint8_t trailer[10] = {0x05, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
write_uint64(&trailer[2], hash_len);
Expand Down Expand Up @@ -104,10 +116,11 @@ signature_init(const pgp_key_pkt_t &key, const pgp_signature_t &sig)
}

void
signature_calculate(pgp_signature_t & sig,
pgp_key_material_t & seckey,
rnp::Hash & hash,
rnp::SecurityContext &ctx)
signature_calculate(pgp_signature_t & sig,
pgp_key_material_t & seckey,
rnp::Hash & hash,
rnp::SecurityContext & ctx,
const pgp_literal_hdr_t *hdr)
{
uint8_t hval[PGP_MAX_HASH_SIZE];
size_t hlen = 0;
Expand All @@ -116,7 +129,7 @@ signature_calculate(pgp_signature_t & sig,

/* Finalize hash first, since function is required to do this */
try {
signature_hash_finish(sig, hash, hval, hlen);
signature_hash_finish(sig, hash, hval, hlen, hdr);
} catch (const std::exception &e) {
RNP_LOG("Failed to finalize hash: %s", e.what());
throw;
Expand Down Expand Up @@ -255,7 +268,8 @@ rnp_result_t
signature_validate(const pgp_signature_t & sig,
const pgp_key_material_t & key,
rnp::Hash & hash,
const rnp::SecurityContext &ctx)
const rnp::SecurityContext &ctx,
const pgp_literal_hdr_t * hdr)
{
if (sig.palg != key.alg) {
RNP_LOG("Signature and key do not agree on algorithm type: %d vs %d",
Expand Down Expand Up @@ -297,7 +311,7 @@ signature_validate(const pgp_signature_t & sig,
uint8_t hval[PGP_MAX_HASH_SIZE];
size_t hlen = 0;
try {
signature_hash_finish(sig, hash, hval, hlen);
signature_hash_finish(sig, hash, hval, hlen, hdr);
} catch (const std::exception &e) {
RNP_LOG("Failed to finalize signature hash.");
return RNP_ERROR_GENERIC;
Expand Down
17 changes: 11 additions & 6 deletions src/lib/crypto/signatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ std::unique_ptr<rnp::Hash> signature_init(const pgp_key_pkt_t & key,
* @param seckey signing secret key material
* @param hash pre-populated with signed data hash context. It is finalized and destroyed
* during the execution. Signature fields and trailer are hashed in this function.
* @param rng random number generator
* @param ctx security context
* @param hdr literal packet header for attached document signatures or NULL otherwise.
*/
void signature_calculate(pgp_signature_t & sig,
pgp_key_material_t & seckey,
rnp::Hash & hash,
rnp::SecurityContext &ctx);
void signature_calculate(pgp_signature_t & sig,
pgp_key_material_t & seckey,
rnp::Hash & hash,
rnp::SecurityContext & ctx,
const pgp_literal_hdr_t *hdr = NULL);

/**
* @brief Validate a signature with pre-populated hash. This method just checks correspondence
Expand All @@ -59,11 +61,14 @@ void signature_calculate(pgp_signature_t & sig,
* @param key public key material of the verifying key
* @param hash pre-populated with signed data hash context. It is finalized
* during the execution. Signature fields and trailer are hashed in this function.
* @param ctx security context
* @param hdr literal packet header for attached document signatures or NULL otherwise.
* @return RNP_SUCCESS if signature was successfully validated or error code otherwise.
*/
rnp_result_t signature_validate(const pgp_signature_t & sig,
const pgp_key_material_t & key,
rnp::Hash & hash,
const rnp::SecurityContext &ctx);
const rnp::SecurityContext &ctx,
const pgp_literal_hdr_t * hdr = NULL);

#endif
5 changes: 3 additions & 2 deletions src/lib/pgp-key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2015,15 +2015,16 @@ pgp_key_t::validate_sig(const pgp_key_t & key,
void
pgp_key_t::validate_sig(pgp_signature_info_t & sinfo,
rnp::Hash & hash,
const rnp::SecurityContext &ctx) const noexcept
const rnp::SecurityContext &ctx,
const pgp_literal_hdr_t * hdr) const noexcept
{
sinfo.no_signer = false;
sinfo.valid = false;
sinfo.expired = false;

/* Validate signature itself */
if (sinfo.signer_valid || valid_at(sinfo.sig->creation())) {
sinfo.valid = !signature_validate(*sinfo.sig, pkt_.material, hash, ctx);
sinfo.valid = !signature_validate(*sinfo.sig, pkt_.material, hash, ctx, hdr);
} else {
sinfo.valid = false;
RNP_LOG("invalid or untrusted key");
Expand Down
4 changes: 3 additions & 1 deletion src/lib/pgp-key.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,12 @@ struct pgp_key_t {
* @param sinfo populated signature info. Validation results will be stored here.
* @param hash hash, feed with all signed data except signature trailer.
* @param ctx Populated security context.
* @param hdr literal packet header for attached document signatures or NULL otherwise.
*/
void validate_sig(pgp_signature_info_t & sinfo,
rnp::Hash & hash,
const rnp::SecurityContext &ctx) const noexcept;
const rnp::SecurityContext &ctx,
const pgp_literal_hdr_t * hdr = NULL) const noexcept;

/**
* @brief Validate certification.
Expand Down
34 changes: 28 additions & 6 deletions src/librepgp/stream-parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ typedef struct pgp_source_signed_param_t {
size_t text_line_len; /* length of a current line in a text document */
long stripped_crs; /* number of trailing CR characters stripped from the end of the last
processed chunk */
pgp_literal_hdr_t lhdr{};
bool has_lhdr = false;

std::vector<pgp_one_pass_sig_t> onepasses; /* list of one-pass singatures */
std::list<pgp_signature_t> sigs; /* list of signatures */
Expand Down Expand Up @@ -892,7 +894,8 @@ signed_validate_signature(pgp_source_signed_param_t &param, pgp_signature_info_t
return;
}
auto shash = hash->clone();
key->validate_sig(sinfo, *shash, *param.handler->ctx->ctx);
key->validate_sig(
sinfo, *shash, *param.handler->ctx->ctx, param.has_lhdr ? &param.lhdr : NULL);
} catch (const std::exception &e) {
RNP_LOG("Signature validation failed: %s", e.what());
sinfo.valid = false;
Expand All @@ -911,6 +914,14 @@ stripped_line_len(uint8_t *begin, uint8_t *end)
return stripped_end - begin + 1;
}

static void
signed_src_set_literal_hdr(pgp_source_t &src, const pgp_literal_hdr_t &hdr)
{
auto param = static_cast<pgp_source_signed_param_t *>(src.param);
param->lhdr = hdr;
param->has_lhdr = true;
}

static void
signed_src_update(pgp_source_t *src, const void *buf, size_t len)
{
Expand Down Expand Up @@ -1131,6 +1142,14 @@ signed_src_finish(pgp_source_t *src)
return ret;
}

/* See https://dev.gnupg.org/T6615 for the details */
if (param->cleartext) {
param->lhdr.format = 't';
param->lhdr.fname_len = 0;
param->lhdr.timestamp = 0;
param->has_lhdr = true;
}

if (!src_eof(src)) {
RNP_LOG("warning: unexpected data on the stream end");
}
Expand Down Expand Up @@ -2900,13 +2919,16 @@ process_pgp_source(pgp_parse_handler_t *handler, pgp_source_t &src)
}
/* file processing case */
decsrc = &ctx.sources.back();
char * filename = NULL;
uint32_t mtime = 0;
const char *filename = NULL;
uint32_t mtime = 0;

if (ctx.literal_src) {
auto *param = static_cast<pgp_source_literal_param_t *>(ctx.literal_src->param);
filename = param->hdr.fname;
mtime = param->hdr.timestamp;
auto &hdr = get_literal_src_hdr(*ctx.literal_src);
filename = hdr.fname;
mtime = hdr.timestamp;
if (ctx.signed_src) {
signed_src_set_literal_hdr(*ctx.signed_src, hdr);
}
}

if (!handler->dest_provider ||
Expand Down
20 changes: 20 additions & 0 deletions src/tests/data/test_messages/message.txt.signed.v5-clear-rsa
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

This is test message to be signed, and/or encrypted, cleartext signed and detached signed.
It will use keys from keyrings/1.
End of message.
-----BEGIN PGP SIGNATURE-----

iQG1BQEBCAApIiEFuFakGXET1DGSe5JSSPAmYV+fOQsmvBZ26B8HLHD1OekFAmS5
INYAABcDC/9lYOIe0QmlAewi4+vL7qmu4U5O9JRyKPIx9p/l5uiJ5KyyzcugL1EC
gsoeYBftOVWqzelF6ndDy3ke3qqSdOdUfGdT3HeiYzV+icfIhwbPsa0ZzH/nM7Df
V+iQ9hlMtLqpxHbx9WaIbKQ4gBzcgmQQtfqbfBz29bC84g8tfzAORdIs8nrbjb79
UhW9uftMs8opABEuoTKpzCG7VOgwb6NATHCSsB6UtQmE4vNGVNM9m/TOp9d7EvVq
TdZtdyRL7Ji/fH3QgSZkwFOn3uDlbZrjRyKRuWMQm63SPkFiLtGXgx570jm/JCWh
uTXrfz9UFjwYC+Wh4LQCebajbdnfjLaBG3MF1HKTaI6ep4sL1Qy4GZv3kuqsZzd2
FoaFwPNxAeTN4ONQ0q40vIgoBIn3Huu650jIrFXLSMCj97sDE1GE331prxHzDEMw
JBWPx/FJ1OmP6zaAb46UZM4p5ALfcf5KAIVjJ7Fx/Hp9bDGRgZUmpD4gNtRk3NM8
3Fk3IxgrcBI=
=CPPf
-----END PGP SIGNATURE-----
Binary file not shown.
Binary file not shown.

0 comments on commit 514662b

Please sign in to comment.