From 029f0f6bd37c5d46fc019245984f0497796ded03 Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Fri, 21 Jun 2024 15:14:04 +0300 Subject: [PATCH 1/4] libasignify: add _sign_reset_files To allow making many different signatures with one context - and thus one password entry. --- include/asignify.h | 6 ++++++ libasignify/sign.c | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/asignify.h b/include/asignify.h index 02a6658..a9045b1 100644 --- a/include/asignify.h +++ b/include/asignify.h @@ -150,6 +150,12 @@ bool asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf); */ const char* asignify_sign_get_error(asignify_sign_t *ctx); +/** + * Remove all files from the sign context + * @param ctx sign context + */ +void asignify_sign_reset_files(asignify_sign_t *ctx); + /** * Free sign context * @param ctx sign context diff --git a/libasignify/sign.c b/libasignify/sign.c index 23c5afe..c875aa8 100644 --- a/libasignify/sign.c +++ b/libasignify/sign.c @@ -230,13 +230,21 @@ asignify_sign_get_error(asignify_sign_t *ctx) void asignify_sign_free(asignify_sign_t *ctx) +{ + if (ctx) { + asignify_sign_reset_files(ctx); + asignify_private_data_free(ctx->privk); + free(ctx); + } +} + +void +asignify_sign_reset_files(asignify_sign_t *ctx) { struct asignify_file *f; int i; if (ctx) { - asignify_private_data_free(ctx->privk); - for (i = 0; i < kv_size(ctx->files); i ++) { f = &kv_A(ctx->files, i); if (f->digests) { @@ -246,6 +254,8 @@ asignify_sign_free(asignify_sign_t *ctx) free(f->fname); } kv_destroy(ctx->files); - free(ctx); + /* Destroy does not null out the pointer (or size). + * _init does. */ + kv_init(ctx->files); } } From 7ebf8dff2575dc2594240c79f478000dddb9febc Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Fri, 21 Jun 2024 16:07:01 +0300 Subject: [PATCH 2/4] cli: sign: do multi-signature per call (with -S .suffix) --- src/sign.c | 72 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/sign.c b/src/sign.c index 20c67a0..d276eec 100644 --- a/src/sign.c +++ b/src/sign.c @@ -69,15 +69,16 @@ cli_sign_help(bool full) const char *fullmsg = "" "asignify [global_opts] sign - creates a signature\n\n" - "Usage: asignify sign [-n] [-d ...] [file1 [file2...]]\n" + "Usage: asignify sign [-n] [-d ...] [-S ] [signature] [file1 [file2...]]\n" "\t-n Do not record files sizes\n" "\t-d Write specific digest (sha256, sha512, blake2)\n" + "\t-S Write seperate digest for each file to file\n" "\tsecretkey Path to a secret key file make a signature\n" "\tsignature Path to signature file to write\n" "\tfile A file that will be recorded in the signature digests\n"; if (!full) { - return ("sign [-n] [-d ] secretkey signature [file1 [file2...]]"); + return ("sign [-n] [-d ] [-S ] secretkey [signature] [file1 [file2...]]"); } return (fullmsg); @@ -88,10 +89,28 @@ struct digest_item { struct digest_item *next; }; +static int +cli_write_sign(asignify_sign_t *sgn, const char* sigfile, int added) +{ + if (added == 0) { + fprintf(stderr, "no digests has been added to the signature"); + return (-1); + } + + if (!asignify_sign_write_signature(sgn, sigfile)) { + fprintf(stderr, "cannot write sign file %s: %s\n", sigfile, + asignify_sign_get_error(sgn)); + return (-1); + } + + return 0; +} + int cli_sign(int argc, char **argv) { asignify_sign_t *sgn; + const char *suffix = NULL; const char *seckeyfile = NULL, *sigfile = NULL; int i; int ch; @@ -104,10 +123,11 @@ cli_sign(int argc, char **argv) static struct option long_options[] = { {"no-size", no_argument, 0, 'n' }, {"digest", required_argument, 0, 'd' }, + {"suffix", required_argument, 0, 'S' }, {0, 0, 0, 0 } }; - while ((ch = getopt_long(argc, argv, "nd:", long_options, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "nd:S:", long_options, NULL)) != -1) { switch (ch) { case 'n': no_size = true; @@ -123,6 +143,9 @@ cli_sign(int argc, char **argv) dtit->next = dt_list; dt_list = dtit; break; + case 'S': + suffix = optarg; + break; default: return (0); break; @@ -142,7 +165,7 @@ cli_sign(int argc, char **argv) } seckeyfile = argv[0]; - sigfile = argv[1]; + if (!suffix) sigfile = argv[1]; sgn = asignify_sign_init(); @@ -153,7 +176,8 @@ cli_sign(int argc, char **argv) return (-1); } - for (i = 2; i < argc; i ++) { + + for (i = suffix ? 1 : 2; i < argc; i ++) { dtit = dt_list; while(dtit != NULL) { if (!asignify_sign_add_file(sgn, argv[i], dtit->type)) { @@ -177,28 +201,38 @@ cli_sign(int argc, char **argv) ret = -1; } } - } + if (suffix) { + int r; + int fnlen = strlen(argv[i]) + strlen(suffix) + 1; + char *sigfn = malloc(fnlen); + strcpy(sigfn, argv[i]); + strcat(sigfn, suffix); + r = cli_write_sign(sgn, sigfn, added); + free(sigfn); + asignify_sign_reset_files(sgn); + added = 0; + if (r < 0) { + ret = -2; + break; + } - if (added == 0) { - fprintf(stderr, "no digests has been added to the signature"); - return (-1); + } } - if (!asignify_sign_write_signature(sgn, sigfile)) { - fprintf(stderr, "cannot write sign file %s: %s\n", sigfile, - asignify_sign_get_error(sgn)); - asignify_sign_free(sgn); - return (-1); + if (!suffix) { + if (cli_write_sign(sgn, sigfile, added)) + ret = -2; } asignify_sign_free(sgn); if (!quiet) { - if (ret == 1) { - printf("Digests file %s has been successfully signed\n", sigfile); - } - else { - printf("Digests file %s has been signed but some files were not added due to errors\n", sigfile); + if (ret == -2) { + /* We already wrote about this */ + } else if (ret == 1) { + printf("Digests file(s) have been successfully signed\n"); + } else { + printf("Digests file(s) have been signed but some files were not added due to errors\n"); } } From 3724da0b2fe8d44ade5c5d9c8872b297c9f55f08 Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Fri, 21 Jun 2024 16:35:24 +0300 Subject: [PATCH 3/4] libasignify: add support for omitting the name and for parsing omitted-name digests. This is an API change (but only to signing), so maybe we should instead have a different function for no-name signature write. Dunno. --- include/asignify.h | 3 ++- libasignify/sign.c | 29 ++++++++++++++++++++++------- libasignify/verify.c | 30 +++++++++++++++++++++++++++++- src/sign.c | 2 +- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/include/asignify.h b/include/asignify.h index a9045b1..42ca1ad 100644 --- a/include/asignify.h +++ b/include/asignify.h @@ -139,9 +139,10 @@ bool asignify_sign_add_file(asignify_sign_t *ctx, const char *f, * Write the complete signature for this context * @param ctx sign context * @param sigf file name or '-' to write to stdout + * @param omit_name if true, do not store the filename in the digest (files must be 1) * @return true if a signature has been successfully written */ -bool asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf); +bool asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf, bool omit_name); /** * Returns last error for sign context diff --git a/libasignify/sign.c b/libasignify/sign.c index c875aa8..ce2ab8f 100644 --- a/libasignify/sign.c +++ b/libasignify/sign.c @@ -145,7 +145,7 @@ asignify_sign_add_file(asignify_sign_t *ctx, const char *f, } bool -asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf) +asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf, bool omit_name) { kvec_t(char) out; char sig_pad[crypto_sign_BYTES + sizeof(unsigned int)]; @@ -161,6 +161,11 @@ asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf) return (false); } + if (omit_name && kv_size(ctx->files) > 1) { + CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); + return (false); + } + kv_init(out); kv_reserve(char, out, kv_size(ctx->files) * PATH_MAX + crypto_sign_BYTES); @@ -172,8 +177,12 @@ asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf) for (i = 0; i < kv_size(ctx->files); i ++) { f = &kv_A(ctx->files, i); if (f->size != 0) { - r = snprintf(line, sizeof(line), "SIZE (%s) = %zu\n", f->fname, - f->size); + if (omit_name) { + r = snprintf(line, sizeof(line), "SIZE = %zu\n", f->size); + } else { + r = snprintf(line, sizeof(line), "SIZE (%s) = %zu\n", + f->fname, f->size); + } if (r >= sizeof(line)) { ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); goto cleanup; @@ -182,10 +191,16 @@ asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf) else { bin2hex(hex, sizeof(hex) - 1, f->digests->digest, asignify_digest_len(f->digests->digest_type)); - r = snprintf(line, sizeof(line), "%s (%s) = %s\n", - asignify_digest_name(f->digests->digest_type), - f->fname, - hex); + if (omit_name) { + r = snprintf(line, sizeof(line), "%s = %s\n", + asignify_digest_name(f->digests->digest_type), + hex); + } else { + r = snprintf(line, sizeof(line), "%s (%s) = %s\n", + asignify_digest_name(f->digests->digest_type), + f->fname, + hex); + } if (r >= sizeof(line)) { ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); goto cleanup; diff --git a/libasignify/verify.c b/libasignify/verify.c index adf48d4..2b2ed31 100644 --- a/libasignify/verify.c +++ b/libasignify/verify.c @@ -78,6 +78,7 @@ struct asignify_pubkey_chain { struct asignify_verify_ctx { struct asignify_pubkey_chain *pk_chain; khash_t(asignify_verify_hnode) *files; + bool name_omitted; const char *error; }; @@ -249,6 +250,30 @@ asignify_verify_parse_files(struct asignify_verify_ctx *ctx, const char *data, } break; case PARSE_OBRACE: + if (*p == '=') { + ctx->name_omitted = true; + k = kh_get(asignify_verify_hnode, ctx->files, "."); + if (k != kh_end(ctx->files)) { + /* We already have the node */ + cur_file = kh_value(ctx->files, k); + } else { + cur_file = xmalloc0(sizeof(*cur_file)); + cur_file->fname = strdup("."); + k = kh_put(asignify_verify_hnode, ctx->files, + cur_file->fname, &r); + if (r == -1) { + goto parse_error; + } + + kh_value(ctx->files, k) = cur_file; + } + p++; + c = p; + state = PARSE_SPACES; + next_state = PARSE_HASH; + break; + } + if (*p != '(') { goto parse_error; } @@ -468,8 +493,11 @@ asignify_verify_file(asignify_verify_t *ctx, const char *checkf) CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); return (false); } + if (ctx->name_omitted) + k = kh_get(asignify_verify_hnode, ctx->files, "."); + else + k = kh_get(asignify_verify_hnode, ctx->files, checkf); - k = kh_get(asignify_verify_hnode, ctx->files, checkf); if (k == kh_end(ctx->files)) { ctx->error = xerr_string(ASIGNIFY_ERROR_NO_DIGEST); return (false); diff --git a/src/sign.c b/src/sign.c index d276eec..e215b35 100644 --- a/src/sign.c +++ b/src/sign.c @@ -97,7 +97,7 @@ cli_write_sign(asignify_sign_t *sgn, const char* sigfile, int added) return (-1); } - if (!asignify_sign_write_signature(sgn, sigfile)) { + if (!asignify_sign_write_signature(sgn, sigfile, false)) { fprintf(stderr, "cannot write sign file %s: %s\n", sigfile, asignify_sign_get_error(sgn)); return (-1); From 1d4f922d65a095659c97f80e055a88e25f04c375 Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Fri, 21 Jun 2024 16:39:56 +0300 Subject: [PATCH 4/4] cli: sign: add support for creation of digest without filename --- src/sign.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sign.c b/src/sign.c index e215b35..8c49afb 100644 --- a/src/sign.c +++ b/src/sign.c @@ -69,8 +69,9 @@ cli_sign_help(bool full) const char *fullmsg = "" "asignify [global_opts] sign - creates a signature\n\n" - "Usage: asignify sign [-n] [-d ...] [-S ] [signature] [file1 [file2...]]\n" + "Usage: asignify sign [-n] [-N] [-d ...] [-S ] [signature] [file1 [file2...]]\n" "\t-n Do not record files sizes\n" + "\t-N Do not record filename (only one file per digest allowed)\n" "\t-d Write specific digest (sha256, sha512, blake2)\n" "\t-S Write seperate digest for each file to file\n" "\tsecretkey Path to a secret key file make a signature\n" @@ -78,7 +79,7 @@ cli_sign_help(bool full) "\tfile A file that will be recorded in the signature digests\n"; if (!full) { - return ("sign [-n] [-d ] [-S ] secretkey [signature] [file1 [file2...]]"); + return ("sign [-n] [-N] [-d ] [-S ] secretkey [signature] [file1 [file2...]]"); } return (fullmsg); @@ -90,14 +91,14 @@ struct digest_item { }; static int -cli_write_sign(asignify_sign_t *sgn, const char* sigfile, int added) +cli_write_sign(asignify_sign_t *sgn, const char* sigfile, int added, bool no_name) { if (added == 0) { fprintf(stderr, "no digests has been added to the signature"); return (-1); } - if (!asignify_sign_write_signature(sgn, sigfile, false)) { + if (!asignify_sign_write_signature(sgn, sigfile, no_name)) { fprintf(stderr, "cannot write sign file %s: %s\n", sigfile, asignify_sign_get_error(sgn)); return (-1); @@ -117,21 +118,26 @@ cli_sign(int argc, char **argv) int ret = 1; int added = 0; bool no_size = false; + bool no_name = false; /* XXX: we do not free this list on exit */ struct digest_item *dt_list = NULL, *dtit; enum asignify_digest_type dt; static struct option long_options[] = { {"no-size", no_argument, 0, 'n' }, + {"no-name", no_argument, 0, 'N'}, {"digest", required_argument, 0, 'd' }, {"suffix", required_argument, 0, 'S' }, {0, 0, 0, 0 } }; - while ((ch = getopt_long(argc, argv, "nd:S:", long_options, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "nNd:S:", long_options, NULL)) != -1) { switch (ch) { case 'n': no_size = true; break; + case 'N': + no_name = true; + break; case 'd': dt = asignify_digest_from_str(optarg, strlen(optarg)); if (dt == ASIGNIFY_DIGEST_MAX) { @@ -207,7 +213,7 @@ cli_sign(int argc, char **argv) char *sigfn = malloc(fnlen); strcpy(sigfn, argv[i]); strcat(sigfn, suffix); - r = cli_write_sign(sgn, sigfn, added); + r = cli_write_sign(sgn, sigfn, added, no_name); free(sigfn); asignify_sign_reset_files(sgn); added = 0; @@ -220,7 +226,7 @@ cli_sign(int argc, char **argv) } if (!suffix) { - if (cli_write_sign(sgn, sigfile, added)) + if (cli_write_sign(sgn, sigfile, added, no_name)) ret = -2; }