2 * usign - tiny signify replacement
4 * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
36 uint8_t fingerprint[8];
37 uint8_t pubkey[EDSIGN_PUBLIC_KEY_SIZE];
46 uint8_t fingerprint[8];
52 uint8_t fingerprint[8];
53 uint8_t sig[EDSIGN_SIGNATURE_SIZE];
56 static const char *pubkeyfile;
57 static const char *pubkeydir;
58 static const char *sigfile;
59 static const char *seckeyfile;
60 static const char *comment;
70 static uint64_t fingerprint_u64(const uint8_t *data)
74 #define ADD(_v) val = (val << 8) | _v
89 file_error(const char *filename, bool _read)
91 if (!quiet || cmd != CMD_VERIFY)
92 fprintf(stderr, "Cannot open file '%s' for %s\n", filename,
93 _read ? "reading" : "writing");
98 open_file(const char *filename, bool _read)
102 if (!strcmp(filename, "-"))
103 return _read ? stdin : stdout;
105 f = fopen(filename, _read ? "r" : "w");
107 file_error(filename, _read);
113 get_file(const char *filename, char *buf, int buflen)
115 FILE *f = open_file(filename, true);
119 char *cur = fgets(buf, buflen, f);
122 fprintf(stderr, "Premature end of file\n");
126 if (strchr(buf, '\n'))
130 len = fread(buf, 1, buflen - 1, f);
135 get_base64_file(const char *file, void *dest, int size, void *buf, int buflen)
137 get_file(file, buf, buflen - 1);
138 return b64_decode(buf, dest, size) == size;
141 static void write_file(const char *name, const uint8_t *fingerprint,
142 const char *prefix, char *buf)
146 f = open_file(name, false);
147 fputs("untrusted comment: ", f);
151 fprintf(f, "%s %"PRIx64, prefix,
152 fingerprint_u64(fingerprint));
153 fprintf(f, "\n%s\n", buf);
157 static int verify(const char *msgfile)
161 struct edsign_verify_state vst;
165 f = open_file(msgfile, true);
167 fprintf(stderr, "Cannot open message file\n");
171 if (!get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)) ||
172 memcmp(sig.pkalg, "Ed", 2) != 0) {
173 fprintf(stderr, "Failed to decode signature\n");
178 snprintf(buf, sizeof(buf), "%s/%"PRIx64, pubkeydir,
179 fingerprint_u64(sig.fingerprint));
183 if (!get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)) ||
184 memcmp(pkey.pkalg, "Ed", 2) != 0) {
185 fprintf(stderr, "Failed to decode public key\n");
189 edsign_verify_init(&vst, sig.sig, pkey.pubkey);
192 int len = fread(buf, 1, sizeof(buf), f);
193 edsign_verify_add(&vst, buf, len);
197 if (!edsign_verify(&vst, sig.sig, pkey.pubkey)) {
199 fprintf(stderr, "verification failed\n");
204 fprintf(stderr, "OK\n");
208 static int sign(const char *msgfile)
221 if (!get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)) ||
222 memcmp(skey.pkalg, "Ed", 2) != 0) {
223 fprintf(stderr, "Failed to decode secret key\n");
227 if (skey.kdfrounds) {
228 fprintf(stderr, "Password protected secret keys are not supported\n");
232 mfd = open(msgfile, O_RDONLY, 0);
233 if (mfd < 0 || fstat(mfd, &st) < 0 ||
234 (m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, mfd, 0)) == MAP_FAILED) {
237 perror("Cannot open message file");
242 memcpy(sig.fingerprint, skey.fingerprint, sizeof(sig.fingerprint));
243 edsign_sec_to_pub(pubkey, skey.seckey);
244 edsign_sign(sig.sig, pubkey, skey.seckey, m, mlen);
248 if (b64_encode(&sig, sizeof(sig), buf, sizeof(buf)) < 0)
251 write_file(sigfile, sig.fingerprint, "signed by key", buf);
256 static int fingerprint(void)
265 get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)))
266 fp = skey.fingerprint;
267 else if (pubkeyfile &&
268 get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)))
269 fp = pkey.fingerprint;
271 get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)))
272 fp = sig.fingerprint;
276 fprintf(stdout, "%"PRIx64"\n", fingerprint_u64(fp));
280 static int generate(void)
282 struct seckey skey = {
287 struct pubkey pkey = {
290 struct sha512_state s;
294 f = fopen("/dev/urandom", "r");
296 fread(skey.fingerprint, sizeof(skey.fingerprint), 1, f) != 1 ||
297 fread(skey.seckey, EDSIGN_SECRET_KEY_SIZE, 1, f) != 1 ||
298 fread(skey.salt, sizeof(skey.salt), 1, f) != 1) {
299 fprintf(stderr, "Can't read data from /dev/urandom\n");
305 ed25519_prepare(skey.seckey);
306 edsign_sec_to_pub(skey.seckey + 32, skey.seckey);
309 sha512_add(&s, skey.seckey, sizeof(skey.seckey));
310 memcpy(skey.checksum, sha512_final_get(&s), sizeof(skey.checksum));
312 if (b64_encode(&skey, sizeof(skey), buf, sizeof(buf)) < 0)
315 write_file(seckeyfile, skey.fingerprint, "private key", buf);
317 memcpy(pkey.fingerprint, skey.fingerprint, sizeof(pkey.fingerprint));
318 memcpy(pkey.pubkey, skey.seckey + 32, sizeof(pkey.pubkey));
320 if (b64_encode(&pkey, sizeof(pkey), buf, sizeof(buf)) < 0)
323 write_file(pubkeyfile, pkey.fingerprint, "public key", buf);
328 static int usage(const char *cmd)
331 "Usage: %s <command> <options>\n"
333 " -V: verify (needs at least -m and -p|-P)\n"
334 " -S: sign (needs at least -m and -s)\n"
335 " -F: print key fingerprint of public/secret key or signature\n"
336 " -G: generate a new keypair\n"
338 " -c <comment>: add comment to keys\n"
339 " -m <file>: message file\n"
340 " -p <file>: public key file (verify/fingerprint only)\n"
341 " -P <path>: public key directory (verify only)\n"
342 " -q: quiet (do not print verification result, use return code only)\n"
343 " -s <file>: secret key file (sign/fingerprint only)\n"
344 " -x <file>: signature file (defaults to <message file>.sig)\n"
350 static void set_cmd(const char *prog, int val)
358 int main(int argc, char **argv)
360 const char *msgfile = NULL;
363 while ((ch = getopt(argc, argv, "FGSVc:m:P:p:qs:x:")) != -1) {
366 set_cmd(argv[0], CMD_VERIFY);
369 set_cmd(argv[0], CMD_SIGN);
372 set_cmd(argv[0], CMD_FINGERPRINT);
375 set_cmd(argv[0], CMD_GENERATE);
399 return usage(argv[0]);
403 if (!sigfile && msgfile) {
404 char *buf = alloca(strlen(msgfile) + 5);
406 if (!strcmp(msgfile, "-")) {
407 fprintf(stderr, "Need signature file when reading message from stdin\n");
411 sprintf(buf, "%s.sig", msgfile);
417 if ((!pubkeyfile && !pubkeydir) || !msgfile)
418 return usage(argv[0]);
419 return verify(msgfile);
421 if (!seckeyfile || !msgfile || !sigfile)
422 return usage(argv[0]);
423 return sign(msgfile);
424 case CMD_FINGERPRINT:
425 if (!!seckeyfile + !!pubkeyfile + !!sigfile != 1) {
426 fprintf(stderr, "Need one secret/public key or signature\n");
427 return usage(argv[0]);
429 return fingerprint();
431 if (!seckeyfile || !pubkeyfile)
432 return usage(argv[0]);
435 return usage(argv[0]);