[BACK]Return to signify.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / signify

Annotation of src/usr.bin/signify/signify.c, Revision 1.11

1.11    ! tedu        1: /* $OpenBSD: signify.c,v 1.10 2014/01/03 17:13:42 tedu Exp $ */
1.1       tedu        2: /*
                      3:  * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <sys/stat.h>
                     18:
                     19: #include <netinet/in.h>
                     20: #include <resolv.h>
                     21:
                     22: #include <stdint.h>
                     23: #include <fcntl.h>
                     24: #include <string.h>
                     25: #include <stdio.h>
                     26: #include <err.h>
                     27: #include <unistd.h>
                     28: #include <readpassphrase.h>
                     29: #include <util.h>
                     30: #include <sha2.h>
                     31:
                     32: #include "crypto_api.h"
                     33:
                     34: #define streq(a, b) (strcmp(a, b) == 0)
                     35:
                     36: #define SIGBYTES crypto_sign_ed25519_BYTES
                     37: #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
                     38: #define PUBLICBYTES crypto_sign_ed25519_PUBLICKEYBYTES
                     39:
                     40: #define PKALG "Ed"
                     41: #define KDFALG "BK"
                     42:
                     43: struct enckey {
                     44:        uint8_t pkalg[2];
                     45:        uint8_t kdfalg[2];
                     46:        uint32_t kdfrounds;
                     47:        uint8_t salt[16];
                     48:        uint8_t checksum[8];
                     49:        uint8_t seckey[SECRETBYTES];
                     50: };
                     51:
                     52: struct pubkey {
                     53:        uint8_t pkalg[2];
                     54:        uint8_t pubkey[PUBLICBYTES];
                     55: };
                     56:
                     57: struct sig {
                     58:        uint8_t pkalg[2];
                     59:        uint8_t sig[SIGBYTES];
                     60: };
                     61:
                     62: extern char *__progname;
                     63:
                     64: static void
                     65: usage(void)
                     66: {
1.9       espie      67:        fprintf(stderr, "usage:"
                     68:            "\t%s [-n] -p pubkey -s seckey -G\n"
                     69:            "\t%s [-o output] -s seckey -S input\n"
                     70:            "\t%s [-o output] -p pubkey -V input\n",
                     71:            __progname, __progname, __progname);
1.1       tedu       72:        exit(1);
                     73: }
                     74:
                     75: static int
                     76: xopen(const char *fname, int flags, mode_t mode)
                     77: {
                     78:        int fd;
                     79:
                     80:        fd = open(fname, flags, mode);
                     81:        if (fd == -1)
                     82:                err(1, "open %s", fname);
                     83:        return fd;
                     84: }
                     85:
                     86: static void *
                     87: xmalloc(size_t len)
                     88: {
                     89:        void *p;
                     90:
                     91:        p = malloc(len);
                     92:        if (!p)
                     93:                err(1, "malloc %zu", len);
                     94:        return p;
                     95: }
                     96:
                     97: static void
1.7       espie      98: readall(int fd, void *buf, size_t len, const char *filename)
1.1       tedu       99: {
1.11    ! tedu      100:        ssize_t x;
        !           101:
        !           102:        x = read(fd, buf, len);
1.7       espie     103:        if (x == -1) {
                    104:                err(1, "read from %s", filename);
                    105:        } else if (x != len) {
                    106:                errx(1, "short read from %s", filename);
                    107:        }
1.1       tedu      108: }
                    109:
                    110: static void
                    111: readb64file(const char *filename, void *buf, size_t len)
                    112: {
                    113:        char b64[2048];
1.10      tedu      114:        int rv, fd;
                    115:        char *commentend;
1.1       tedu      116:
                    117:        fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
                    118:        memset(b64, 0, sizeof(b64));
                    119:        rv = read(fd, b64, sizeof(b64) - 1);
                    120:        if (rv == -1)
1.7       espie     121:                err(1, "read from %s", filename);
1.10      tedu      122:        commentend = strchr(b64, '\n');
                    123:        if (!commentend)
1.1       tedu      124:                errx(1, "no newline in %s", filename);
1.10      tedu      125:        rv = b64_pton(commentend + 1, buf, len);
1.1       tedu      126:        if (rv != len)
                    127:                errx(1, "invalid b64 encoding in %s", filename);
                    128:        memset(b64, 0, sizeof(b64));
                    129:        close(fd);
                    130:        if (memcmp(buf, PKALG, 2))
                    131:                errx(1, "unsupported file %s", filename);
                    132: }
                    133:
                    134: uint8_t *
                    135: readmsg(const char *filename, unsigned long long *msglenp)
                    136: {
                    137:        unsigned long long msglen;
                    138:        uint8_t *msg;
                    139:        struct stat sb;
                    140:        int fd;
                    141:
                    142:        fd = xopen(filename, O_RDONLY | O_NOFOLLOW, 0);
                    143:        fstat(fd, &sb);
                    144:        msglen = sb.st_size;
                    145:        if (msglen > (1UL << 30))
                    146:                errx(1, "msg too large in %s", filename);
                    147:        msg = xmalloc(msglen);
1.7       espie     148:        readall(fd, msg, msglen, filename);
1.1       tedu      149:        close(fd);
                    150:
                    151:        *msglenp = msglen;
                    152:        return msg;
                    153: }
                    154:
                    155: static void
1.7       espie     156: writeall(int fd, const void *buf, size_t len, const char *filename)
1.1       tedu      157: {
1.11    ! tedu      158:        ssize_t x;
        !           159:
        !           160:        x = write(fd, buf, len);
1.7       espie     161:        if (x == -1) {
                    162:                err(1, "write to %s", filename);
                    163:        } else if (x != len) {
                    164:                errx(1, "short write to %s", filename);
                    165:        }
1.1       tedu      166: }
                    167:
                    168: static void
                    169: writeb64file(const char *filename, const char *comment, const void *buf,
                    170:     size_t len, mode_t mode)
                    171: {
                    172:        char header[1024];
                    173:        char b64[1024];
                    174:        int fd, rv;
                    175:
                    176:        fd = xopen(filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_RDWR, mode);
                    177:        snprintf(header, sizeof(header), "signify -- %s\n", comment);
1.7       espie     178:        writeall(fd, header, strlen(header), filename);
1.8       espie     179:        if ((rv = b64_ntop(buf, len, b64, sizeof(b64)-1)) == -1)
1.1       tedu      180:                errx(1, "b64 encode failed");
1.8       espie     181:        b64[rv++] = '\n';
1.7       espie     182:        writeall(fd, b64, rv, filename);
1.1       tedu      183:        memset(b64, 0, sizeof(b64));
                    184:        close(fd);
                    185: }
                    186:
                    187: static void
                    188: kdf(uint8_t *salt, size_t saltlen, int rounds, uint8_t *key, size_t keylen)
                    189: {
                    190:        char pass[1024];
                    191:
                    192:        if (rounds == 0) {
                    193:                memset(key, 0, keylen);
                    194:                return;
                    195:        }
                    196:
                    197:        if (!readpassphrase("passphrase: ", pass, sizeof(pass), 0))
                    198:                errx(1, "readpassphrase");
                    199:        if (bcrypt_pbkdf(pass, strlen(pass), salt, saltlen, key,
                    200:            keylen, rounds) == -1)
                    201:                errx(1, "bcrypt pbkdf");
                    202:        memset(pass, 0, sizeof(pass));
                    203: }
                    204:
                    205: static void
                    206: signmsg(uint8_t *seckey, uint8_t *msg, unsigned long long msglen,
                    207:     uint8_t *sig)
                    208: {
                    209:        unsigned long long siglen;
                    210:        uint8_t *sigbuf;
                    211:
                    212:        sigbuf = xmalloc(msglen + SIGBYTES);
                    213:        crypto_sign_ed25519(sigbuf, &siglen, msg, msglen, seckey);
                    214:        memcpy(sig, sigbuf, SIGBYTES);
                    215:        free(sigbuf);
                    216: }
                    217:
                    218: static void
                    219: verifymsg(uint8_t *pubkey, uint8_t *msg, unsigned long long msglen,
                    220:     uint8_t *sig)
                    221: {
                    222:        uint8_t *sigbuf, *dummybuf;
                    223:        unsigned long long siglen, dummylen;
                    224:
                    225:        siglen = SIGBYTES + msglen;
                    226:        sigbuf = xmalloc(siglen);
                    227:        dummybuf = xmalloc(siglen);
                    228:        memcpy(sigbuf, sig, SIGBYTES);
                    229:        memcpy(sigbuf + SIGBYTES, msg, msglen);
                    230:        if (crypto_sign_ed25519_open(dummybuf, &dummylen, sigbuf, siglen,
                    231:            pubkey) == -1)
                    232:                errx(1, "signature failed");
                    233:        free(sigbuf);
                    234:        free(dummybuf);
                    235: }
                    236:
                    237: static void
                    238: generate(const char *pubkeyfile, const char *seckeyfile, int rounds)
                    239: {
                    240:        uint8_t digest[SHA512_DIGEST_LENGTH];
                    241:        struct pubkey pubkey;
                    242:        struct enckey enckey;
                    243:        uint8_t xorkey[sizeof(enckey.seckey)];
                    244:        SHA2_CTX ctx;
                    245:        int i;
                    246:
                    247:        crypto_sign_ed25519_keypair(pubkey.pubkey, enckey.seckey);
                    248:
                    249:        SHA512Init(&ctx);
                    250:        SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
                    251:        SHA512Final(digest, &ctx);
                    252:
                    253:        memcpy(enckey.pkalg, PKALG, 2);
                    254:        memcpy(enckey.kdfalg, KDFALG, 2);
                    255:        enckey.kdfrounds = htonl(rounds);
                    256:        arc4random_buf(enckey.salt, sizeof(enckey.salt));
                    257:        kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
                    258:        memcpy(enckey.checksum, digest, sizeof(enckey.checksum));
                    259:        for (i = 0; i < sizeof(enckey.seckey); i++)
                    260:                enckey.seckey[i] ^= xorkey[i];
                    261:        memset(digest, 0, sizeof(digest));
                    262:        memset(xorkey, 0, sizeof(xorkey));
                    263:
                    264:        writeb64file(seckeyfile, "secret key", &enckey,
                    265:            sizeof(enckey), 0600);
                    266:        memset(&enckey, 0, sizeof(enckey));
                    267:
                    268:        memcpy(pubkey.pkalg, PKALG, 2);
                    269:        writeb64file(pubkeyfile, "public key", &pubkey,
                    270:            sizeof(pubkey), 0666);
                    271: }
                    272:
                    273: static void
                    274: sign(const char *seckeyfile, const char *inputfile, const char *sigfile)
                    275: {
                    276:        struct sig sig;
                    277:        uint8_t digest[SHA512_DIGEST_LENGTH];
                    278:        struct enckey enckey;
                    279:        uint8_t xorkey[sizeof(enckey.seckey)];
                    280:        uint8_t *msg;
                    281:        unsigned long long msglen;
                    282:        int i, rounds;
                    283:        SHA2_CTX ctx;
                    284:
                    285:        readb64file(seckeyfile, &enckey, sizeof(enckey));
                    286:
                    287:        if (memcmp(enckey.kdfalg, KDFALG, 2))
                    288:                errx(1, "unsupported KDF");
                    289:        rounds = ntohl(enckey.kdfrounds);
                    290:        kdf(enckey.salt, sizeof(enckey.salt), rounds, xorkey, sizeof(xorkey));
                    291:        for (i = 0; i < sizeof(enckey.seckey); i++)
                    292:                enckey.seckey[i] ^= xorkey[i];
                    293:        memset(xorkey, 0, sizeof(xorkey));
                    294:        SHA512Init(&ctx);
                    295:        SHA512Update(&ctx, enckey.seckey, sizeof(enckey.seckey));
                    296:        SHA512Final(digest, &ctx);
                    297:        if (memcmp(enckey.checksum, digest, sizeof(enckey.checksum)))
                    298:            errx(1, "incorrect passphrase");
                    299:        memset(digest, 0, sizeof(digest));
                    300:
                    301:        msg = readmsg(inputfile, &msglen);
                    302:
                    303:        signmsg(enckey.seckey, msg, msglen, sig.sig);
                    304:        memset(&enckey, 0, sizeof(enckey));
                    305:
                    306:        memcpy(sig.pkalg, PKALG, 2);
                    307:        writeb64file(sigfile, "signature", &sig, sizeof(sig), 0666);
                    308:
                    309:        free(msg);
                    310: }
                    311:
                    312: static void
                    313: verify(const char *pubkeyfile, const char *inputfile, const char *sigfile)
                    314: {
                    315:        struct sig sig;
                    316:        struct pubkey pubkey;
                    317:        unsigned long long msglen;
                    318:        uint8_t *msg;
                    319:
                    320:        readb64file(pubkeyfile, &pubkey, sizeof(pubkey));
                    321:        readb64file(sigfile, &sig, sizeof(sig));
                    322:
                    323:        msg = readmsg(inputfile, &msglen);
                    324:
                    325:        verifymsg(pubkey.pubkey, msg, msglen, sig.sig);
                    326:        printf("verified\n");
                    327:
                    328:        free(msg);
                    329: }
                    330:
                    331: int
                    332: main(int argc, char **argv)
                    333: {
                    334:        const char *pubkeyfile = NULL, *seckeyfile = NULL, *inputfile = NULL,
                    335:            *sigfile = NULL;
                    336:        char sigfilebuf[1024];
                    337:        int ch, rounds;
1.6       tedu      338:        enum {
                    339:                NONE,
                    340:                GENERATE,
                    341:                SIGN,
                    342:                VERIFY
                    343:        } verb = NONE;
                    344:
1.1       tedu      345:
                    346:        rounds = 42;
                    347:
1.9       espie     348:        while ((ch = getopt(argc, argv, "GSVno:p:s:")) != -1) {
1.1       tedu      349:                switch (ch) {
1.6       tedu      350:                case 'G':
                    351:                        if (verb)
                    352:                                usage();
                    353:                        verb = GENERATE;
                    354:                        break;
                    355:                case 'S':
                    356:                        if (verb)
                    357:                                usage();
                    358:                        verb = SIGN;
                    359:                        break;
                    360:                case 'V':
                    361:                        if (verb)
                    362:                                usage();
                    363:                        verb = VERIFY;
                    364:                        break;
                    365:                case 'n':
1.1       tedu      366:                        rounds = 0;
                    367:                        break;
1.6       tedu      368:                case 'o':
1.1       tedu      369:                        sigfile = optarg;
                    370:                        break;
1.6       tedu      371:                case 'p':
1.1       tedu      372:                        pubkeyfile = optarg;
                    373:                        break;
1.6       tedu      374:                case 's':
1.1       tedu      375:                        seckeyfile = optarg;
                    376:                        break;
                    377:                default:
                    378:                        usage();
                    379:                        break;
                    380:                }
                    381:        }
1.2       tedu      382:        argc -= optind;
1.9       espie     383:        argv += optind;
                    384:
                    385:        if (verb == NONE)
1.1       tedu      386:                usage();
                    387:
1.6       tedu      388:        if (verb == GENERATE) {
1.9       espie     389:                if (!pubkeyfile || !seckeyfile || argc != 0)
1.1       tedu      390:                        usage();
                    391:                generate(pubkeyfile, seckeyfile, rounds);
1.9       espie     392:        } else {
                    393:                if (argc != 1)
1.1       tedu      394:                        usage();
1.9       espie     395:
                    396:                inputfile = argv[0];
                    397:
                    398:                if (!sigfile) {
                    399:                        if (snprintf(sigfilebuf, sizeof(sigfilebuf), "%s.sig",
                    400:                            inputfile) >= sizeof(sigfilebuf))
                    401:                                errx(1, "path too long");
                    402:                        sigfile = sigfilebuf;
                    403:                }
                    404:
                    405:                if (verb == SIGN) {
                    406:                        if (!seckeyfile)
                    407:                                usage();
                    408:                        sign(seckeyfile, inputfile, sigfile);
                    409:                } else if (verb == VERIFY) {
                    410:                        if (!pubkeyfile)
                    411:                                usage();
                    412:                        verify(pubkeyfile, inputfile, sigfile);
                    413:                }
1.1       tedu      414:        }
1.9       espie     415:
1.1       tedu      416:        return 0;
                    417: }