ebfdfb0809c4d8394b2254625d47649975d72000
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
);
136 get_base64_file(const char *file
, void *dest
, int size
, void *buf
, int buflen
)
138 get_file(file
, buf
, buflen
- 1);
139 return b64_decode(buf
, dest
, size
) == size
;
142 static void write_file(const char *name
, const uint8_t *fingerprint
,
143 const char *prefix
, char *buf
)
147 f
= open_file(name
, false);
148 fputs("untrusted comment: ", f
);
152 fprintf(f
, "%s %016"PRIx64
, prefix
,
153 fingerprint_u64(fingerprint
));
154 fprintf(f
, "\n%s\n", buf
);
158 static int verify(const char *msgfile
)
162 struct edsign_verify_state vst
;
166 f
= open_file(msgfile
, true);
168 fprintf(stderr
, "Cannot open message file\n");
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");
180 snprintf(buf
, sizeof(buf
), "%s/%016"PRIx64
, pubkeydir
,
181 fingerprint_u64(sig
.fingerprint
));
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");
192 edsign_verify_init(&vst
, sig
.sig
, pkey
.pubkey
);
195 int len
= fread(buf
, 1, sizeof(buf
), f
);
196 edsign_verify_add(&vst
, buf
, len
);
200 if (!edsign_verify(&vst
, sig
.sig
, pkey
.pubkey
)) {
202 fprintf(stderr
, "verification failed\n");
207 fprintf(stderr
, "OK\n");
211 static int sign(const char *msgfile
)
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");
230 if (skey
.kdfrounds
) {
231 fprintf(stderr
, "Password protected secret keys are not supported\n");
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
) {
240 perror("Cannot open message file");
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
);
251 if (b64_encode(&sig
, sizeof(sig
), buf
, sizeof(buf
)) < 0)
254 write_file(sigfile
, sig
.fingerprint
, "signed by key", buf
);
259 static int fingerprint(void)
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
;
274 get_base64_file(sigfile
, &sig
, sizeof(sig
), buf
, sizeof(buf
)))
275 fp
= sig
.fingerprint
;
279 fprintf(stdout
, "%016"PRIx64
"\n", fingerprint_u64(fp
));
283 static int generate(void)
285 struct seckey skey
= {
290 struct pubkey pkey
= {
293 struct sha512_state s
;
297 f
= fopen("/dev/urandom", "r");
299 fprintf(stderr
, "Can't open /dev/urandom\n");
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");
313 ed25519_prepare(skey
.seckey
);
314 edsign_sec_to_pub(skey
.seckey
+ 32, skey
.seckey
);
317 sha512_add(&s
, skey
.seckey
, sizeof(skey
.seckey
));
318 memcpy(skey
.checksum
, sha512_final_get(&s
), sizeof(skey
.checksum
));
320 if (b64_encode(&skey
, sizeof(skey
), buf
, sizeof(buf
)) < 0)
323 write_file(seckeyfile
, skey
.fingerprint
, "private key", buf
);
325 memcpy(pkey
.fingerprint
, skey
.fingerprint
, sizeof(pkey
.fingerprint
));
326 memcpy(pkey
.pubkey
, skey
.seckey
+ 32, sizeof(pkey
.pubkey
));
328 if (b64_encode(&pkey
, sizeof(pkey
), buf
, sizeof(buf
)) < 0)
331 write_file(pubkeyfile
, pkey
.fingerprint
, "public key", buf
);
336 static int usage(const char *cmd
)
339 "Usage: %s <command> <options>\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"
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"
358 static void set_cmd(const char *prog
, int val
)
366 int main(int argc
, char **argv
)
368 const char *msgfile
= NULL
;
371 while ((ch
= getopt(argc
, argv
, "FGSVc:m:P:p:qs:x:")) != -1) {
374 set_cmd(argv
[0], CMD_VERIFY
);
377 set_cmd(argv
[0], CMD_SIGN
);
380 set_cmd(argv
[0], CMD_FINGERPRINT
);
383 set_cmd(argv
[0], CMD_GENERATE
);
407 return usage(argv
[0]);
411 if (!sigfile
&& msgfile
) {
412 char *buf
= alloca(strlen(msgfile
) + 5);
414 if (!strcmp(msgfile
, "-")) {
415 fprintf(stderr
, "Need signature file when reading message from stdin\n");
419 sprintf(buf
, "%s.sig", msgfile
);
425 if ((!pubkeyfile
&& !pubkeydir
) || !msgfile
)
426 return usage(argv
[0]);
427 return verify(msgfile
);
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]);
437 return fingerprint();
439 if (!seckeyfile
|| !pubkeyfile
)
440 return usage(argv
[0]);
443 return usage(argv
[0]);