Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple signatures with one call (-S suffix) (and support for omitting filename in digest) #25

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 8 additions & 1 deletion include/asignify.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -150,6 +151,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
Expand Down
45 changes: 35 additions & 10 deletions libasignify/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)];
Expand All @@ -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);

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -230,13 +245,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) {
Expand All @@ -246,6 +269,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);
}
}
30 changes: 29 additions & 1 deletion libasignify/verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
78 changes: 59 additions & 19 deletions src/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,17 @@ cli_sign_help(bool full)

const char *fullmsg = ""
"asignify [global_opts] sign - creates a signature\n\n"
"Usage: asignify sign [-n] [-d <digest>...] <secretkey> <signature> [file1 [file2...]]\n"
"Usage: asignify sign [-n] [-N] [-d <digest>...] [-S <suffix>] <secretkey> [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 <suffix> Write seperate digest for each file to file<suffix>\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 <digest>] secretkey signature [file1 [file2...]]");
return ("sign [-n] [-N] [-d <digest>] [-S <suffix>] secretkey [signature] [file1 [file2...]]");
}

return (fullmsg);
Expand All @@ -88,30 +90,54 @@ struct digest_item {
struct digest_item *next;
};

static int
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, no_name)) {
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;
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:", 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) {
Expand All @@ -123,6 +149,9 @@ cli_sign(int argc, char **argv)
dtit->next = dt_list;
dt_list = dtit;
break;
case 'S':
suffix = optarg;
break;
default:
return (0);
break;
Expand All @@ -142,7 +171,7 @@ cli_sign(int argc, char **argv)
}

seckeyfile = argv[0];
sigfile = argv[1];
if (!suffix) sigfile = argv[1];

sgn = asignify_sign_init();

Expand All @@ -153,7 +182,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)) {
Expand All @@ -177,28 +207,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, no_name);
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, no_name))
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");
}
}

Expand Down