• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/usign/main.c

  1 /*
  2  * usign - tiny signify replacement
  3  *
  4  * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
  5  *
  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.
  9  *
 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.
 17  */
 18 #include <sys/mman.h>
 19 #include <sys/stat.h>
 20 #include <stdio.h>
 21 #include <stdbool.h>
 22 #include <stdlib.h>
 23 #include <string.h>
 24 #include <getopt.h>
 25 #include <stdint.h>
 26 #include <fcntl.h>
 27 #include <unistd.h>
 28 #include <inttypes.h>
 29 
 30 #include "base64.h"
 31 #include "edsign.h"
 32 #include "ed25519.h"
 33 
 34 struct pubkey {
 35         char pkalg[2];
 36         uint8_t fingerprint[8];
 37         uint8_t pubkey[EDSIGN_PUBLIC_KEY_SIZE];
 38 };
 39 
 40 struct seckey {
 41         char pkalg[2];
 42         char kdfalg[2];
 43         uint32_t kdfrounds;
 44         uint8_t salt[16];
 45         uint8_t checksum[8];
 46         uint8_t fingerprint[8];
 47         uint8_t seckey[64];
 48 };
 49 
 50 struct sig {
 51         char pkalg[2];
 52         uint8_t fingerprint[8];
 53         uint8_t sig[EDSIGN_SIGNATURE_SIZE];
 54 };
 55 
 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;
 61 static bool quiet;
 62 static enum {
 63         CMD_NONE,
 64         CMD_VERIFY,
 65         CMD_SIGN,
 66         CMD_FINGERPRINT,
 67         CMD_GENERATE,
 68 } cmd = CMD_NONE;
 69 
 70 static uint64_t fingerprint_u64(const uint8_t *data)
 71 {
 72         uint64_t val = 0;
 73 
 74 #define ADD(_v) val = (val << 8) | _v
 75         ADD(data[0]);
 76         ADD(data[1]);
 77         ADD(data[2]);
 78         ADD(data[3]);
 79         ADD(data[4]);
 80         ADD(data[5]);
 81         ADD(data[6]);
 82         ADD(data[7]);
 83 #undef ADD
 84 
 85         return val;
 86 }
 87 
 88 static void
 89 file_error(const char *filename, bool _read)
 90 {
 91         if (!quiet || cmd != CMD_VERIFY)
 92                 fprintf(stderr, "Cannot open file '%s' for %s\n", filename,
 93                         _read ? "reading" : "writing");
 94         exit(1);
 95 }
 96 
 97 static FILE *
 98 open_file(const char *filename, bool _read)
 99 {
100         FILE *f;
101 
102         if (!strcmp(filename, "-"))
103                 return _read ? stdin : stdout;
104 
105         f = fopen(filename, _read ? "r" : "w");
106         if (!f)
107                 file_error(filename, _read);
108 
109         return f;
110 }
111 
112 static void
113 get_file(const char *filename, char *buf, int buflen)
114 {
115         FILE *f = open_file(filename, true);
116         int len;
117 
118         while (1) {
119                 char *cur = fgets(buf, buflen, f);
120 
121                 if (!cur) {
122                         fprintf(stderr, "Premature end of file\n");
123                         exit(1);
124                 }
125 
126                 if (strchr(buf, '\n'))
127                         break;
128         }
129 
130         len = fread(buf, 1, buflen - 1, f);
131         buf[len] = 0;
132         fclose(f);
133 }
134 
135 static bool
136 get_base64_file(const char *file, void *dest, int size, void *buf, int buflen)
137 {
138         get_file(file, buf, buflen - 1);
139         return b64_decode(buf, dest, size) == size;
140 }
141 
142 static void write_file(const char *name, const uint8_t *fingerprint,
143                        const char *prefix, char *buf)
144 {
145         FILE *f;
146 
147         f = open_file(name, false);
148         fputs("untrusted comment: ", f);
149         if (comment)
150                 fputs(comment, f);
151         else
152                 fprintf(f, "%s %"PRIx64, prefix,
153                         fingerprint_u64(fingerprint));
154         fprintf(f, "\n%s\n", buf);
155         fclose(f);
156 }
157 
158 static int verify(const char *msgfile)
159 {
160         struct pubkey pkey;
161         struct sig sig;
162         struct edsign_verify_state vst;
163         FILE *f;
164         char buf[512];
165 
166         f = open_file(msgfile, true);
167         if (!f) {
168                 fprintf(stderr, "Cannot open message file\n");
169                 return 1;
170         }
171 
172         if (!get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)) ||
173             memcmp(sig.pkalg, "Ed", 2) != 0) {
174                 fprintf(stderr, "Failed to decode signature\n");
175                 fclose(f);
176                 return 1;
177         }
178 
179         if (!pubkeyfile) {
180                 snprintf(buf, sizeof(buf), "%s/%"PRIx64, pubkeydir,
181                          fingerprint_u64(sig.fingerprint));
182                 pubkeyfile = buf;
183         }
184 
185         if (!get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)) ||
186             memcmp(pkey.pkalg, "Ed", 2) != 0) {
187                 fprintf(stderr, "Failed to decode public key\n");
188                 fclose(f);
189                 return 1;
190         }
191 
192         edsign_verify_init(&vst, sig.sig, pkey.pubkey);
193 
194         while (!feof(f)) {
195                 int len = fread(buf, 1, sizeof(buf), f);
196                 edsign_verify_add(&vst, buf, len);
197         }
198         fclose(f);
199 
200         if (!edsign_verify(&vst, sig.sig, pkey.pubkey)) {
201                 if (!quiet)
202                         fprintf(stderr, "verification failed\n");
203                 return 1;
204         }
205 
206         if (!quiet)
207                 fprintf(stderr, "OK\n");
208         return 0;
209 }
210 
211 static int sign(const char *msgfile)
212 {
213         struct seckey skey;
214         struct sig sig = {
215                 .pkalg = "Ed",
216         };
217         struct stat st;
218         char buf[512];
219         void *pubkey = buf;
220         long mlen;
221         void *m;
222         int mfd;
223 
224         if (!get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)) ||
225             memcmp(skey.pkalg, "Ed", 2) != 0) {
226                 fprintf(stderr, "Failed to decode secret key\n");
227                 return 1;
228         }
229 
230         if (skey.kdfrounds) {
231                 fprintf(stderr, "Password protected secret keys are not supported\n");
232                 return 1;
233         }
234 
235         mfd = open(msgfile, O_RDONLY, 0);
236         if (mfd < 0 || fstat(mfd, &st) < 0 ||
237                 (m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, mfd, 0)) == MAP_FAILED) {
238                 if (mfd >= 0)
239                         close(mfd);
240                 perror("Cannot open message file");
241                 return 1;
242         }
243         mlen = st.st_size;
244 
245         memcpy(sig.fingerprint, skey.fingerprint, sizeof(sig.fingerprint));
246         edsign_sec_to_pub(pubkey, skey.seckey);
247         edsign_sign(sig.sig, pubkey, skey.seckey, m, mlen);
248         munmap(m, mlen);
249         close(mfd);
250 
251         if (b64_encode(&sig, sizeof(sig), buf, sizeof(buf)) < 0)
252                 return 1;
253 
254         write_file(sigfile, sig.fingerprint, "signed by key", buf);
255 
256         return 0;
257 }
258 
259 static int fingerprint(void)
260 {
261         struct seckey skey;
262         struct pubkey pkey;
263         struct sig sig;
264         char buf[512];
265         uint8_t *fp;
266 
267         if (seckeyfile &&
268             get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)))
269                 fp = skey.fingerprint;
270         else if (pubkeyfile &&
271                  get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)))
272                 fp = pkey.fingerprint;
273         else if (sigfile &&
274                  get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)))
275                 fp = sig.fingerprint;
276         else
277                 return 1;
278 
279         fprintf(stdout, "%"PRIx64"\n", fingerprint_u64(fp));
280         return 0;
281 }
282 
283 static int generate(void)
284 {
285         struct seckey skey = {
286                 .pkalg = "Ed",
287                 .kdfalg = "BK",
288                 .kdfrounds = 0,
289         };
290         struct pubkey pkey = {
291                 .pkalg = "Ed",
292         };
293         struct sha512_state s;
294         char buf[512];
295         FILE *f;
296 
297         f = fopen("/dev/urandom", "r");
298         if (!f) {
299                 fprintf(stderr, "Can't open /dev/urandom\n");
300                 return 1;
301         }
302 
303         if (fread(skey.fingerprint, sizeof(skey.fingerprint), 1, f) != 1 ||
304             fread(skey.seckey, EDSIGN_SECRET_KEY_SIZE, 1, f) != 1 ||
305             fread(skey.salt, sizeof(skey.salt), 1, f) != 1) {
306                 fprintf(stderr, "Can't read data from /dev/urandom\n");
307                 fclose(f);
308                 return 1;
309         }
310         if (f)
311                 fclose(f);
312 
313         ed25519_prepare(skey.seckey);
314         edsign_sec_to_pub(skey.seckey + 32, skey.seckey);
315 
316         sha512_init(&s);
317         sha512_add(&s, skey.seckey, sizeof(skey.seckey));
318         memcpy(skey.checksum, sha512_final_get(&s), sizeof(skey.checksum));
319 
320         if (b64_encode(&skey, sizeof(skey), buf, sizeof(buf)) < 0)
321                 return 1;
322 
323         write_file(seckeyfile, skey.fingerprint, "private key", buf);
324 
325         memcpy(pkey.fingerprint, skey.fingerprint, sizeof(pkey.fingerprint));
326         memcpy(pkey.pubkey, skey.seckey + 32, sizeof(pkey.pubkey));
327 
328         if (b64_encode(&pkey, sizeof(pkey), buf, sizeof(buf)) < 0)
329                 return 1;
330 
331         write_file(pubkeyfile, pkey.fingerprint, "public key", buf);
332 
333         return 0;
334 }
335 
336 static int usage(const char *cmd)
337 {
338         fprintf(stderr,
339                 "Usage: %s <command> <options>\n"
340                 "Commands:\n"
341                 "  -V:                  verify (needs at least -m and -p|-P)\n"
342                 "  -S:                  sign (needs at least -m and -s)\n"
343                 "  -F:                  print key fingerprint of public/secret key or signature\n"
344                 "  -G:                  generate a new keypair (needs at least -p and -s)\n"
345                 "Options:\n"
346                 "  -c <comment>:        add comment to keys\n"
347                 "  -m <file>:           message file\n"
348                 "  -p <file>:           public key file (verify/fingerprint only)\n"
349                 "  -P <path>:           public key directory (verify only)\n"
350                 "  -q:                  quiet (do not print verification result, use return code only)\n"
351                 "  -s <file>:           secret key file (sign/fingerprint only)\n"
352                 "  -x <file>:           signature file (defaults to <message file>.sig)\n"
353                 "\n",
354                 cmd);
355         return 1;
356 }
357 
358 static void set_cmd(const char *prog, int val)
359 {
360         if (cmd != CMD_NONE)
361                 exit(usage(prog));
362 
363         cmd = val;
364 }
365 
366 int main(int argc, char **argv)
367 {
368         const char *msgfile = NULL;
369         int ch;
370 
371         while ((ch = getopt(argc, argv, "FGSVc:m:P:p:qs:x:")) != -1) {
372                 switch (ch) {
373                 case 'V':
374                         set_cmd(argv[0], CMD_VERIFY);
375                         break;
376                 case 'S':
377                         set_cmd(argv[0], CMD_SIGN);
378                         break;
379                 case 'F':
380                         set_cmd(argv[0], CMD_FINGERPRINT);
381                         break;
382                 case 'G':
383                         set_cmd(argv[0], CMD_GENERATE);
384                         break;
385                 case 'c':
386                         comment = optarg;
387                         break;
388                 case 'm':
389                         msgfile = optarg;
390                         break;
391                 case 'P':
392                         pubkeydir = optarg;
393                         break;
394                 case 'p':
395                         pubkeyfile = optarg;
396                         break;
397                 case 's':
398                         seckeyfile = optarg;
399                         break;
400                 case 'x':
401                         sigfile = optarg;
402                         break;
403                 case 'q':
404                         quiet = true;
405                         break;
406                 default:
407                         return usage(argv[0]);
408                 }
409         }
410 
411         if (!sigfile && msgfile) {
412                 char *buf = alloca(strlen(msgfile) + 5);
413 
414                 if (!strcmp(msgfile, "-")) {
415                         fprintf(stderr, "Need signature file when reading message from stdin\n");
416                         return 1;
417                 }
418 
419                 sprintf(buf, "%s.sig", msgfile);
420                 sigfile = buf;
421         }
422 
423         switch (cmd) {
424         case CMD_VERIFY:
425                 if ((!pubkeyfile && !pubkeydir) || !msgfile)
426                         return usage(argv[0]);
427                 return verify(msgfile);
428         case CMD_SIGN:
429                 if (!seckeyfile || !msgfile || !sigfile)
430                         return usage(argv[0]);
431                 return sign(msgfile);
432         case CMD_FINGERPRINT:
433                 if (!!seckeyfile + !!pubkeyfile + !!sigfile != 1) {
434                         fprintf(stderr, "Need one secret/public key or signature\n");
435                         return usage(argv[0]);
436                 }
437                 return fingerprint();
438         case CMD_GENERATE:
439                 if (!seckeyfile || !pubkeyfile)
440                         return usage(argv[0]);
441                 return generate();
442         default:
443                 return usage(argv[0]);
444         }
445 }
446 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt