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 %016"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/%016"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, "%016"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