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
;
69 static uint64_t fingerprint_u64(uint8_t *data
)
73 #define ADD(_v) val = (val << 8) | _v
88 file_error(const char *filename
, bool _read
)
90 if (!quiet
|| cmd
!= CMD_VERIFY
)
91 fprintf(stderr
, "Cannot open file '%s' for %s\n", filename
,
92 _read
? "reading" : "writing");
97 open_file(const char *filename
, bool _read
)
101 if (!strcmp(filename
, "-"))
102 return _read
? stdin
: stdout
;
104 f
= fopen(filename
, _read
? "r" : "w");
106 file_error(filename
, _read
);
112 get_file(const char *filename
, char *buf
, int buflen
)
114 FILE *f
= open_file(filename
, true);
118 char *cur
= fgets(buf
, buflen
, f
);
121 fprintf(stderr
, "Premature end of file\n");
125 if (strchr(buf
, '\n'))
129 len
= fread(buf
, 1, buflen
- 1, f
);
134 get_base64_file(const char *file
, void *dest
, int size
, void *buf
, int buflen
)
136 get_file(file
, buf
, buflen
- 1);
137 return b64_pton(buf
, dest
, size
) == size
;
140 static int verify(const char *msgfile
)
144 struct edsign_verify_state vst
;
148 f
= open_file(msgfile
, true);
150 fprintf(stderr
, "Cannot open message file\n");
154 if (!get_base64_file(sigfile
, &sig
, sizeof(sig
), buf
, sizeof(buf
)) ||
155 memcmp(sig
.pkalg
, "Ed", 2) != 0) {
156 fprintf(stderr
, "Failed to decode signature\n");
161 snprintf(buf
, sizeof(buf
), "%s/%"PRIx64
, pubkeydir
,
162 fingerprint_u64(sig
.fingerprint
));
166 if (!get_base64_file(pubkeyfile
, &pkey
, sizeof(pkey
), buf
, sizeof(buf
)) ||
167 memcmp(pkey
.pkalg
, "Ed", 2) != 0) {
168 fprintf(stderr
, "Failed to decode public key\n");
172 edsign_verify_init(&vst
, sig
.sig
, pkey
.pubkey
);
175 int len
= fread(buf
, 1, sizeof(buf
), f
);
176 edsign_verify_add(&vst
, buf
, len
);
180 if (!edsign_verify(&vst
, sig
.sig
, pkey
.pubkey
)) {
182 fprintf(stderr
, "verification failed\n");
187 fprintf(stderr
, "OK\n");
191 static int sign(const char *msgfile
)
205 if (!get_base64_file(seckeyfile
, &skey
, sizeof(skey
), buf
, sizeof(buf
)) ||
206 memcmp(skey
.pkalg
, "Ed", 2) != 0) {
207 fprintf(stderr
, "Failed to decode secret key\n");
211 if (skey
.kdfrounds
) {
212 fprintf(stderr
, "Password protected secret keys are not supported\n");
216 mfd
= open(msgfile
, O_RDONLY
, 0);
217 if (mfd
< 0 || fstat(mfd
, &st
) < 0 ||
218 (m
= mmap(0, st
.st_size
, PROT_READ
, MAP_PRIVATE
, mfd
, 0)) == MAP_FAILED
) {
221 perror("Cannot open message file");
226 memcpy(sig
.fingerprint
, skey
.fingerprint
, sizeof(sig
.fingerprint
));
227 edsign_sec_to_pub(pubkey
, skey
.seckey
);
228 edsign_sign(sig
.sig
, pubkey
, skey
.seckey
, m
, mlen
);
232 if (b64_ntop(&sig
, sizeof(sig
), buf
, sizeof(buf
)) < 0)
235 out
= open_file(sigfile
, false);
236 fprintf(out
, "untrusted comment: signed by key %"PRIx64
"\n%s\n", fingerprint_u64(sig
.fingerprint
), buf
);
242 static int fingerprint(void)
251 get_base64_file(seckeyfile
, &skey
, sizeof(skey
), buf
, sizeof(buf
)))
252 fp
= skey
.fingerprint
;
253 else if (pubkeyfile
&&
254 get_base64_file(pubkeyfile
, &pkey
, sizeof(pkey
), buf
, sizeof(buf
)))
255 fp
= pkey
.fingerprint
;
257 get_base64_file(sigfile
, &sig
, sizeof(sig
), buf
, sizeof(buf
)))
258 fp
= sig
.fingerprint
;
262 fprintf(stdout
, "%"PRIx64
"\n", fingerprint_u64(fp
));
266 static int generate(void)
268 struct seckey skey
= {
273 struct pubkey pkey
= {
276 struct sha512_state s
;
280 f
= fopen("/dev/urandom", "r");
282 fread(skey
.fingerprint
, sizeof(skey
.fingerprint
), 1, f
) != 1 ||
283 fread(skey
.seckey
, EDSIGN_SECRET_KEY_SIZE
, 1, f
) != 1 ||
284 fread(skey
.salt
, sizeof(skey
.salt
), 1, f
) != 1) {
285 fprintf(stderr
, "Can't read data from /dev/urandom\n");
291 ed25519_prepare(skey
.seckey
);
292 edsign_sec_to_pub(skey
.seckey
+ 32, skey
.seckey
);
295 sha512_add(&s
, skey
.seckey
, sizeof(skey
.seckey
));
296 memcpy(skey
.checksum
, sha512_final_get(&s
), sizeof(skey
.checksum
));
298 if (b64_ntop(&skey
, sizeof(skey
), buf
, sizeof(buf
)) < 0)
301 f
= open_file(seckeyfile
, false);
302 fprintf(f
, "untrusted comment: secret key %"PRIx64
"\n%s\n", fingerprint_u64(skey
.fingerprint
), buf
);
305 memcpy(pkey
.fingerprint
, skey
.fingerprint
, sizeof(pkey
.fingerprint
));
306 memcpy(pkey
.pubkey
, skey
.seckey
+ 32, sizeof(pkey
.pubkey
));
308 if (b64_ntop(&pkey
, sizeof(pkey
), buf
, sizeof(buf
)) < 0)
311 f
= open_file(pubkeyfile
, false);
312 fprintf(f
, "untrusted comment: public key %"PRIx64
"\n%s\n", fingerprint_u64(pkey
.fingerprint
), buf
);
318 static int usage(const char *cmd
)
321 "Usage: %s <command> <options>\n"
323 " -V: verify (needs at least -m and -p|-P)\n"
324 " -S: sign (needs at least -m and -s)\n"
325 " -F: print key fingerprint of public/secret key or signature\n"
326 " -G: generate a new keypair\n"
328 " -m <file>: message file\n"
329 " -p <file>: public key file (verify/fingerprint only)\n"
330 " -P <path>: public key directory (verify only)\n"
331 " -q: quiet (do not print verification result, use return code only)\n"
332 " -s <file>: secret key file (sign/fingerprint only)\n"
333 " -x <file>: signature file (defaults to <message file>.sig)\n"
339 static void set_cmd(const char *prog
, int val
)
347 int main(int argc
, char **argv
)
349 const char *msgfile
= NULL
;
352 while ((ch
= getopt(argc
, argv
, "FGSVm:P:p:qs:x:")) != -1) {
355 set_cmd(argv
[0], CMD_VERIFY
);
358 set_cmd(argv
[0], CMD_SIGN
);
361 set_cmd(argv
[0], CMD_FINGERPRINT
);
364 set_cmd(argv
[0], CMD_GENERATE
);
385 return usage(argv
[0]);
389 if (!sigfile
&& msgfile
) {
390 char *buf
= alloca(strlen(msgfile
) + 5);
392 if (!strcmp(msgfile
, "-")) {
393 fprintf(stderr
, "Need signature file when reading message from stdin\n");
397 sprintf(buf
, "%s.sig", msgfile
);
403 if ((!pubkeyfile
&& !pubkeydir
) || !msgfile
)
404 return usage(argv
[0]);
405 return verify(msgfile
);
407 if (!seckeyfile
|| !msgfile
|| !sigfile
)
408 return usage(argv
[0]);
409 return sign(msgfile
);
410 case CMD_FINGERPRINT
:
411 if (!!seckeyfile
+ !!pubkeyfile
+ !!sigfile
!= 1) {
412 fprintf(stderr
, "Need one secret/public key or signature\n");
413 return usage(argv
[0]);
415 return fingerprint();
417 if (!seckeyfile
|| !pubkeyfile
)
418 return usage(argv
[0]);
421 return usage(argv
[0]);