Skip to content

Commit

Permalink
(WIP) Add support for v5 document signatures.
Browse files Browse the repository at this point in the history
  • Loading branch information
ni4 committed Jul 25, 2023
1 parent 4ed00f1 commit 2f98769
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 26 deletions.
41 changes: 28 additions & 13 deletions src/lib/crypto/signatures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,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 @@ -52,13 +57,21 @@ 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;
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);
// hash_len += hdr->fname_len + 6;
} else {
hash.add(doc_trailer, 6);
// hash_len += 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 @@ -91,10 +104,11 @@ signature_init(const pgp_key_material_t &key, pgp_hash_alg_t hash_alg)
}

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 @@ -103,7 +117,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 @@ -213,7 +227,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 All @@ -235,7 +250,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 All @@ -244,7 +259,7 @@ signature_validate(const pgp_signature_t & sig,
/* compare lbits */
if (memcmp(hval, sig.lbits, 2)) {
RNP_LOG("wrong lbits");
return RNP_ERROR_SIGNATURE_INVALID;
// return RNP_ERROR_SIGNATURE_INVALID;
}

/* validate signature */
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_material_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 @@ -1950,15 +1950,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 @@ -401,10 +401,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
29 changes: 25 additions & 4 deletions src/librepgp/stream-parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,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 @@ -818,7 +820,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 @@ -837,6 +840,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 @@ -1057,6 +1068,13 @@ signed_src_finish(pgp_source_t *src)
return ret;
}

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 @@ -2574,9 +2592,12 @@ process_pgp_source(pgp_parse_handler_t *handler, pgp_source_t &src)
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 2f98769

Please sign in to comment.