#define _POSIX_C_SOURCE 200809L /* general zpm sign function */ /* read key, export key, import key, sign, export signature, import * signature, verify signature */ /* import and export is base64 encoded */ /* -v verify * -s sign * -g generate key * -e extract public key * * -o output file * * -p read key from file * -P read public key from argument to -P * -S read secret key from argument * -m message from argument * -h message is hex encoded */ /* chacha20 encryption: 256 bit key = 32 bytes */ #include #include #include #include #include #include #include #include #include #include /* linux specific for getrandom */ #include #include "eddsa.h" #include "sha512.h" #include "crypto/chacha.h" #include "lib/blake2.h" char *readpass(char *prompt); #define VERIFY 1 #define SIGN 2 #define KEYGEN 3 #define EXTRACT 4 #define KEYSIGN 5 #define LIST 6 #define ENCRYPT 7 #define DECRYPT 8 #define SIGNKEY 9 #define IMPORT 10 /* output flags */ #define RAW 0x1 #define HUMANREADABLE 0x2 #define SECRET_KEY 1 #define PUBLIC_KEY 2 #define SIGNATURE 3 #define UNKNOWN 4 #define VERSION 1 /* possible additional types: * designated revoker * revocation certificate */ /* timestamps are 8 byte little endian integers of seconds since * the epoch. or unix time, or some such. Times are a mess. * Unix time is leap second unaware, and ambiguous during leap seconds. * UTC requires a database of leap seconds for past time, and the * specified second in the future possibly changes. * TAI is good, but currently 37 seconds behind UTC, which is just weird */ /* size of structs would have to be increased for 57/64 byte keys * and key*2 size signatures */ /* need 25 more bytes for ed448 */ struct secret_key { char magic[4]; /* "ZPMS" */ uint8_t version; /* 0x01 */ uint8_t type; /* 0x01 */ uint8_t encryption; /* 0x01 == chacha */ uint8_t reserved1; /* MBZ */ char key[32]; /* private key data */ int64_t created; /* time in unix epoch seconds, little endian */ int64_t expires; /* time in unix epoch seconds, little endian, = is none, could do uint64max */ char salt[8]; /* password salt, nist wants 16, but we're not using nist approved hash algorithms anyway. */ char reserved2[64]; /* could put the pubkey here could have 16 bytes of IV for chacha to encrypt. */ char id[120]; /* 1 byte len, id bytes, zero bytes remainder */ char trailer[8]; /* room for a pointer */ }; struct public_key { char magic[4]; /* "ZPMS" */ uint8_t version; /* 0x01 */ uint8_t type; /* 0x02 */ uint8_t reserved0; /* MBZ */ uint8_t reserved1; /* MBZ */ char key[32]; /* public key data */ int64_t created; /* time in unix epoch seconds, little endian */ int64_t expires; /* time in unix epoch seconds, little endian */ char reserved2[8]; /* MBZ */ char signature[64]; /* self sig data */ char id[120]; /* 1 byte len, id bytes, zero bytes remainder */ char trailer[8]; /* room for a pointer */ }; struct signature { char magic[4]; /* "ZPMS" */ uint8_t version; /* 0x01 */ uint8_t type; /* 0x03 */ uint8_t reserved0; /* MBZ */ /* trust level? revokable? */ uint8_t reserved1; /* MBZ */ char key[32]; /* signing public key data?, can be zero */ int64_t signtime; /* time in unix epoch seconds, little endian */ int64_t expires; /* time in unix epoch seconds, little endian */ char reserved2[8]; /* MBZ */ char signature[64]; /* sig data */ char reserved3[120]; /* some indication of what you're signing ? */ /* 512 bits is 64 bytes, could put the hash of the data signed */ char trailer[8]; /* room for a pointer */ }; struct keysig { char magic[4]; /* "ZPMS" */ uint8_t version; /* 0x01 */ uint8_t type; uint8_t encryption; /* trust level for pk? revokable for sig? */ uint8_t reserved1; /* MBZ */ char key[32]; /* signing public key data?, can be zero */ /* should these be signed? */ int64_t created; /* time in unix epoch seconds */ int64_t expires; /* time in unix epoch seconds */ char salt[8]; /* reserved for pk and sig, could be IV for sig */ char signature[64]; /* sig data, reserved for sk */ char id[120]; /* signing hash for sig? otherwise reserved for sig */ /* 512 bits is 64 bytes, could put the hash of the data signed */ char trailer[8]; /* room for a pointer */ }; struct signing_context { char *message; size_t mlen; char *keyfile, *keystring, *keyid, *keyprefix, *passphrase; unsigned char passkey[32]; }; void putsle8(unsigned char *dst, int64_t inval) { int i; union { uint64_t out; int64_t in; } uv; uint64_t val; uv.in = inval; val = uv.out; for (i=0; i<8; i++) { dst[i] = val & 0xff; val >>= 8; } } int64_t getsle8(unsigned char *dst) { uint64_t val = 0; int i; union { int64_t out; uint64_t in; } uv; for (i=0; i<8; i++) { val += (uint64_t)dst[i] << (8*i); } uv.in = val; return uv.out; } void seckey_bytea(unsigned char *bytes, struct secret_key *sk) { memset(bytes, 0, 256); memcpy(bytes, "ZPMS", 4); bytes[4] = sk->version; bytes[5] = sk->type; bytes[6] = sk->encryption; /* 7 reserved */ memcpy(bytes + 8, sk->key, 32); putsle8(bytes+40, sk->created); putsle8(bytes+48, sk->expires); /* @56 8 salt, not implemented */ /* memcpy(bytes + 64, sk->reserved2, 64); should probably put the pk */ memcpy(bytes + 128, sk->id, 120); /* 8 reserved */ } void bytea_seckey(struct secret_key *sk, unsigned char *bytes) { memcpy(sk->magic, bytes, 4); sk->version = bytes[4]; sk->type = bytes[5]; sk->encryption = bytes[6]; sk->reserved1 = 0; memcpy(sk->key, bytes + 8, 32); sk->created = getsle8(bytes+40); sk->expires = getsle8(bytes+48); memset(sk->salt, 0, 8); /* salt not implemented */ memset(sk->reserved2, 0, 64); /* salt not implemented */ memcpy(sk->id, bytes + 128, 120); memset(sk->trailer, 0, 8); } int create_key(struct secret_key *sk, int generate) { memcpy(sk->magic, "ZPMS", 4); sk->version = VERSION; sk->type = SECRET_KEY; sk->encryption = 0; sk->reserved1 = 0; if (generate) { /* a private key is just 32 random bytes */ getrandom(sk->key, sizeof sk->key, 0); } sk->created = time(NULL); sk->expires = INT64_MAX; memset(sk->salt, 0, 8); memset(sk->reserved2, 0, 64); memset(sk->id, 0, 120); memset(sk->trailer, 0, 8); ed25519_genpub(sk->reserved2, sk->key); return 1; } /* hash password into 32 bytes for chacha encryption */ int collect_key(unsigned char *key, const char *pass) { struct blake2b_state__ h; if (!pass) { pass = readpass("Passphrase: "); } if (!pass) { return 0; } blake2b_init(&h, 32); blake2b_update(&h, pass, strlen(pass)); blake2b_final(&h, key, 32); return 1; } int encrypt_key(struct secret_key *sk, unsigned char *key) { int i; if (sk->encryption) { return 1; } /* TODO chacha encrypt sk->key */ for (i=0; i < 32; i++) { sk->key[i] ^= key[i]; } sk->encryption = 1; return 1; } int encrypt_sk(struct secret_key *sk, unsigned char *pass) { unsigned char key[32]; if (sk->encryption == 1) { return 1; } collect_key(key, pass); encrypt_key(sk, key); memset(key, 0, 32); memset(pass, 0, strlen(pass)); return 1; } int decrypt_sk(struct secret_key *sk, unsigned char *pass) { unsigned char key[32]; int i; if (sk->encryption == 0) { return 1; } collect_key(key, pass); for (i=0; i < 32; i++) { sk->key[i] ^= key[i]; } memset(key, 0, 32); memset(pass, 0, strlen(pass)); sk->encryption = 0; return 1; } int decrypt_key(struct secret_key *sk, unsigned char *key) { int i; if (sk->encryption == 0) { return 1; } /* TODO chacha decrypt sk->key */ for (i=0; i < 32; i++) { sk->key[i] ^= key[i]; } sk->encryption = 0; return 1; } void pubkey_init(struct public_key *pk) { memcpy(pk->magic, "ZPMS", 4); pk->version = VERSION; pk->type = PUBLIC_KEY; pk->reserved0 = 0; pk->reserved1 = 0; memset(pk->key, 0, sizeof pk->key); pk->created = 0; pk->expires = 0; memset(pk->reserved2, 0, 8); memset(pk->signature, 0, 64); memset(pk->id, 0, 120); memset(pk->trailer, 0, 8); } void pubkey_bytea(unsigned char *bytes, struct public_key *pk) { memset(bytes, 0, 256); memcpy(bytes, "ZPMS", 4); bytes[4] = pk->version; bytes[5] = pk->type; /* 6 7 reserved */ memcpy(bytes + 8, pk->key, 32); putsle8(bytes+40, pk->created); putsle8(bytes+48, pk->expires); /* @56 8 reserved */ memcpy(bytes + 64, pk->signature, 64); memcpy(bytes + 128, pk->id, 120); /* 8 reserved */ } void bytea_pubkey(struct public_key *pk, unsigned char *bytes) { memcpy(pk->magic, bytes, 4); pk->version = bytes[4]; pk->type = bytes[5]; pk->reserved0 = 0; pk->reserved1 = 0; memcpy(pk->key, bytes + 8, 32); pk->created = getsle8(bytes+40); pk->expires = getsle8(bytes+48); memset(pk->reserved2, 0, 8); memcpy(pk->signature, bytes + 64, 64); memcpy(pk->id, bytes + 128, 120); memset(pk->trailer, 0, 8); } int derive_pubkey(struct public_key *pk, struct secret_key *sk) { char bytes[256]; memcpy(pk->magic, "ZPMS", 4); pk->version = VERSION; pk->type = PUBLIC_KEY; pk->reserved0 = 0; pk->reserved1 = 0; ed25519_genpub(pk->key, sk->key); pk->created = sk->created; pk->expires = sk->expires; memset(pk->reserved2, 0, 8); memset(pk->signature, 0, 64); memset(pk->id, 0, 120); strncpy(pk->id, sk->id, 119); memset(pk->trailer, 0, 8); /* serialize and sign */ pubkey_bytea(bytes, pk); ed25519_sign(pk->signature, sk->key, pk->key, bytes, 256); return 1; } void bytea_signature(struct signature *sig, unsigned char *bytes) { memcpy(sig->magic, bytes, 4); sig->version = bytes[4]; sig->type = bytes[5]; sig->reserved0 = 0; sig->reserved1 = 0; memcpy(sig->key, bytes + 8, 32); sig->signtime = getsle8(bytes+40); sig->expires = getsle8(bytes+48); memset(sig->reserved2, 0, 8); memcpy(sig->signature, bytes + 64, 64); memcpy(sig->reserved3, bytes + 128, 120); memset(sig->trailer, 0, 8); } void signature_bytea(unsigned char *bytes, struct signature *sig) { memset(bytes, 0, 256); memcpy(bytes, "ZPMS", 4); bytes[4] = sig->version; bytes[5] = sig->type; /* 6 7 reserved */ memcpy(bytes + 8, sig->key, 32); putsle8(bytes+40, sig->signtime); putsle8(bytes+48, sig->expires); /* @56 8 reserved */ memcpy(bytes + 64, sig->signature, 64); /* 120 reserved */ /* 8 reserved */ } union signobject { struct signature sig; struct public_key pk; struct secret_key sk; char bytes[256]; }; int object_type(union signobject *obj) { return obj ? obj->sk.type : 0; } void sighash(unsigned char *dst, struct signature *sig, unsigned char *data, size_t len) { unsigned char signtime[8], expires[8]; struct sha512 hash; /* need to put these in as little endian */ putsle8(signtime, sig->signtime); putsle8(expires, sig->expires); sha512_init(&hash); sha512_add(&hash, signtime, sizeof signtime); sha512_add(&hash, expires, sizeof expires); sha512_add(&hash, data, len); sha512_final(&hash, dst); } void init_signature(struct signature *sig) { memcpy(sig->magic, "ZPMS", 4); sig->version = VERSION; sig->type = SIGNATURE; sig->reserved0 = 0; sig->reserved1 = 0; memset(sig->key, 0, sizeof sig->key); sig->signtime = 0; sig->expires = 0; memset(sig->reserved2, 0, 8); memset(sig->signature, 0, 64); memset(sig->reserved3, 0, 120); memset(sig->trailer, 0, 8); } int create_signature(struct signature *sig, struct secret_key *sk, int baresign, unsigned char *data, size_t len) { unsigned char messagehash[64]; if (!sk || !data || !sig) { return 0; } memcpy(sig->magic, "ZPMS", 4); sig->version = VERSION; sig->type = SIGNATURE; sig->reserved0 = 0; sig->reserved1 = 0; ed25519_genpub(sig->key, sk->key); #if 0 sig->signtime = 0; sig->expires = 0; #endif memset(sig->reserved2, 0, 8); memset(sig->signature, 0, 64); memset(sig->reserved3, 0, 120); memset(sig->trailer, 0, 8); if (!baresign) { sighash(messagehash, sig, data, len); data = messagehash; len = sizeof messagehash; } ed25519_sign(sig->signature, sk->key, sig->key, data, len); return 1; } //#define MARK do { fprintf(stderr, "%s %s:%d\n", __FILE__, __func__, __LINE__); } while (0) static char hexchars[] = "0123456789abcdefABCDEF"; static void hex(char *dst, uint8_t *src, size_t len) { while (len--) { dst[0] = hexchars[(src[0]>>4)&0xf]; dst[1] = hexchars[src[0]&0xf]; dst+=2; src++; } } /* always 512 bytes dst, 248 bytes src, last 8 bytes * are ignored and set to zero, last 4 bytes not output */ void serialize(uint8_t *dst, unsigned char *src) { int i; uint8_t byte, hi, lo; for (i = 0; i < 248; i++) { byte = src[i]; hi = byte >> 4; lo = byte & 0xf; if (i % 32 == 0 && i > 0) { *dst++ = '\n'; } *dst++ = hexchars[hi]; *dst++ = hexchars[lo]; } for (; i < 252; i++) { *dst++ = '0'; *dst++ = '0'; } *dst++ = '\n'; } int hexval(int digit) { if (digit >= '0' && digit <= '9') { return digit - '0'; } else if (digit == 'A' || digit == 'a') { return 10; } else if (digit == 'B' || digit == 'b') { return 11; } else if (digit == 'C' || digit == 'c') { return 12; } else if (digit == 'D' || digit == 'd') { return 13; } else if (digit == 'E' || digit == 'e') { return 14; } else if (digit == 'F' || digit == 'f') { return 15; } return 0; } /* convert at most dlen hex bytes from srclen, returns * number of bytes read, src may contain zero bytes, * so use strlen in the caller if needed */ size_t hex2bin(uint8_t *dst, size_t dlen, char *src, size_t slen) { size_t i, n = 0; uint8_t val = 0; for (i = 0; i < slen && n < dlen; i++) { if (!isxdigit(src[i])) { continue; } val = hexval(src[i++]) << 4; /* look for next hex digit */ while (i < slen && !isxdigit(src[i])) { i++; } if (i == slen || !isxdigit(src[i])) { break; /* tests redundant, but make code clearer */ } val += hexval(src[i]); /* low nibble */ dst[n++] = val; } return n; } /* read in up to 248 bytes, set remaining to zero * returns byte 5 if the bytes begin with "ZPMS" and 248 bytes were converted * src must be at least 512 bytes */ int deserialize(void *obj, uint8_t *src, size_t slen) { int bytes, i, type; char buf[256]; bytes = hex2bin(buf, 248, src, slen); for (i = bytes; i < 256; i++) { buf[i] = 0; } if (bytes == 248 && memcmp(buf, "ZPMS", 4) == 0) { type = buf[5]; } else { return 0; } switch(type) { case SECRET_KEY: bytea_seckey(obj, buf); memset(buf, 0, sizeof buf); break; case PUBLIC_KEY: bytea_pubkey(obj, buf); break; case SIGNATURE: bytea_signature(obj, buf); break; default: type = UNKNOWN; break; } return type; } void *map(char *file, size_t *size) { int fd, rv; void *m; struct stat st; fd = open(file, O_RDONLY); if (fd == -1) { return NULL; } rv = fstat(fd, &st); if (rv == -1) { close(fd); return NULL; } m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (m == MAP_FAILED) { return 0; } close(fd); *size = st.st_size; return m; } /* read 512 bytes from fd, returns -1 for error, 512 for full read, * < 512 for end of file */ ssize_t read512(int fd, void *dst) { ssize_t bytes = 0; ssize_t need = 512; while (need) { bytes = read(fd, dst + 512 - need, need); if (bytes == -1) { return -1; } if (bytes == 0) { return 512 - need; } need -= bytes; } return 512; } int read_item(int fd, void *item) { char buffer[512]; ssize_t bytes; int type; bytes = read512(fd, buffer); if (bytes != 512) { return 0; } type = deserialize(item, buffer, sizeof buffer); return type; } int write_secret_key(int fd, struct secret_key *sk, int outputflags) { uint8_t skh[256]; char serialized[512]; size_t outlen; if (outputflags & RAW) { hex(serialized, sk->key, 32); outlen = 64; } else { seckey_bytea(skh, sk); serialize(serialized, skh); outlen = sizeof serialized; } write(fd, serialized, outlen); /* clear memory */ memset(skh, 0, sizeof skh); memset(serialized, 0, sizeof serialized); return 1; } int write_public_key(int fd, struct public_key *pk, int outputflags) { uint8_t skh[256]; char serialized[512]; size_t outlen; if (outputflags & RAW) { hex(serialized, pk->key, 32); outlen = 64; } else { pubkey_bytea(skh, pk); serialize(serialized, skh); outlen = sizeof serialized; } write(fd, serialized, outlen); /* clear memory */ memset(skh, 0, sizeof skh); memset(serialized, 0, sizeof serialized); return 1; } int write_signature(int fd, struct signature *sig, int outputflags) { uint8_t skh[256]; char serialized[512]; size_t outlen; if (outputflags & RAW) { hex(serialized, sig->signature, 64); outlen = 128; } else { signature_bytea(skh, sig); serialize(serialized, skh); outlen = sizeof serialized; } write(fd, serialized, outlen); /* clear memory */ memset(skh, 0, sizeof skh); memset(serialized, 0, sizeof serialized); return 1; } int write_object(int fd, void *o, int outputflags) { uint8_t bytes[256]; char serialized[512]; size_t outlen; int type; union signobject *obj = o; type = ((struct secret_key *)obj)->type; if (outputflags & RAW) { switch (type) { case 0x01: hex(serialized, obj->sk.key, 32); outlen = 64; break; case 0x02: hex(serialized, obj->pk.key, 32); outlen = 64; break; case 0x03: hex(serialized, obj->sig.signature, 64); outlen = 128; break; default: return 0; } } else { outlen = 512; switch (type) { case 0x01: seckey_bytea(bytes, &obj->sk); break; case 0x02: pubkey_bytea(bytes, &obj->pk); break; case 0x03: signature_bytea(bytes, &obj->sig); break; memset(bytes, 0, sizeof bytes); default: return 0; } serialize(serialized, bytes); } write(fd, serialized, outlen); memset(serialized, 0, outlen); return 1; } /* TODO work on strings, not file output */ void write_to_512(int fd, int output) { char trailer[65]; memset(trailer, '-', 64); trailer[64] = '\n'; while (output < 512) { int shortfall = 512 - output; if (shortfall >= 65) { write(fd, trailer, 65); output += 65; continue; } write(fd, trailer + (65-shortfall), shortfall); output += shortfall; } } int dump_signature(int fd, struct signature *sig) { char as_str[1024], key[64], sigstr[128]; int output; int64_t curtime = time(0); hex(key, sig->key, 32); hex(sigstr, sig->signature, 64); output = sprintf(as_str, "Type: %s\nSigned: %ld\n%s: %ld\nPublic Key: %s\nSignature:\n%.64s\n%.64s\n", "signature", sig->signtime, curtime > sig->expires ? "Expired" : "Expires", sig->expires, key, sigstr, sigstr+64); write(fd, as_str, strlen(as_str)); write_to_512(fd, output); return 1; } int dump_public_key(int fd, struct public_key *pk) { char as_str[1024], key[64], sig[128]; int output; int64_t curtime = time(0); hex(key, pk->key, 32); hex(sig, pk->signature, 64); output = sprintf(as_str, "Type: %s\nId: \"%s\"\nCreated: %ld\n%s: %ld\nKey: %.64s\nSignature:\n%.64s\n%.64s\n", "public key", pk->id, pk->created, curtime > pk->expires ? "Expired" : "Expires", pk->expires, key, sig, sig+64); write(fd, as_str, strlen(as_str)); write_to_512(fd, output); return 1; } int dump_secret_key(int fd, struct secret_key *sk) { char as_str[1024]; char key[64]; int output; int64_t curtime = time(0); char exprep[64]; hex(key, sk->key, 32); if (sk->expires == INT64_MAX) { sprintf(exprep, "never"); } else { sprintf(exprep, "%ld", sk->expires); } output = sprintf(as_str, "Type: %s\nEncrypted: %s\nId: \"%s\"\nCreated: %ld\n%s: %s\nKey: %.64s\n", "secret key", sk->encryption ? "yes" : "no", sk->id, sk->created, curtime > sk->expires ? "Expired" : "Expires", exprep, key); write(fd, as_str, strlen(as_str)); write_to_512(fd, output); return 1; } int dump_object(int fd, void *obj) { int rv; switch (((struct secret_key *)obj)->type) { case 0x01: rv = dump_secret_key(fd, obj); break; case 0x02: rv = dump_public_key(fd, obj); break; case 0x03: rv = dump_signature(fd, obj); break; default: rv = 0; break; } return rv; } /* returns a malloced keyfile name. no check is made to see if the * file actually exists */ char *find_keyfile(char *file) { char *f = 0; if (file) { f = strdup(file); } else if (getenv("ZPM_KEYFILE")) { f = strdup(getenv("ZPM_KEYFILE")); } else { char *home = getenv("HOME"); if (home) { size_t need; need = snprintf(0, 0, "%s/.zpm/key", home); char *freefile = malloc(need + 1); if (freefile) { snprintf(freefile, need+1, "%s/.zpm/key", home); } f = freefile; } } return f; } int obj_id_match(union signobject *obj, char *id) { if (!obj || !id) { return 1; } switch(object_type(obj)) { case SECRET_KEY: return !strcmp(obj->sk.id, id); break; case PUBLIC_KEY: return !strcmp(obj->pk.id, id); break; default: return 1; break; } return 1; } int key_id_match(struct secret_key *sk, char *id) { if (!sk && !id) { return 1; } /* TODO look for substring */ return !strcmp(sk->id, id); } /* try to populate a secret key structure. * preference order: * direct specified string, won't have any additional info added * string from ZPM_KEY * file, if id, try to match * file from ZPM_KEYFILE * file at ~/.zpm/key * otherwise fail * if direct from string, encrypted? assume no. need option then. */ /* can probably just call read_item */ int read_object(int fd, union signobject *obj) { char buf[512]; ssize_t bytes; bytes = read512(fd, buf); if (bytes < 512) { return 0; } return deserialize(obj, buf, bytes); } int find_secret_key(struct secret_key *sk, char *id, char *file, char *direct) { char *path = 0; int fd, bytes; size_t len; if (direct || (direct = getenv("ZPM_KEY"))) { /* TODO check strlen == 64 */ len = strlen(direct); if (len < 2 * sizeof sk->key) { return 2; } memset(sk->key, 0, sizeof sk->key); bytes = hex2bin(sk->key, sizeof sk->key, direct, len); if (bytes != sizeof sk->key) { return 0; } create_key(sk, 0); return 1; } else if (file) { path = find_keyfile(file); if (!path) { return 0; } fd = open(path, O_RDONLY); free(path); if (fd == -1) { return 0; } int64_t curtime = time(0); int type; do { type = read_object(fd, (union signobject *)sk); if (type != SECRET_KEY) { continue; } if (curtime > sk->expires) { continue; } if (!id || key_id_match(sk, id)) { close(fd); return 1; } } while (type != 0); close(fd); } return 0; } int open_output(char *path) { int fd = 1; if (path && strcmp(path, "-") != 0) { fd = open(path, O_WRONLY|O_CREAT, 0600); } return fd; } void set_key_id(struct secret_key *sk, char *id) { memset(sk->id, 0, 120); if (id) { strncpy(sk->id, id, 119); } } void set_pub_id(struct public_key *sk, char *id) { memset(sk->id, 0, 120); if (id) { strncpy(sk->id, id, 119); } } int object_filter(union signobject *obj, int type, char *id, char *prefix) { int otype; size_t len; otype = object_type(obj); if (otype == 0) { return 0; } if (type && otype != type) { return 0; } if (otype != SECRET_KEY) { return 0; } if (id) { /* TODO memmem */ if (!key_id_match(&obj->sk, id)) { return 0; } } if (prefix) { char buf[32]; len = strlen(prefix); len = hex2bin(buf, sizeof buf, prefix, len); if (memcmp(obj->sk.key, buf, len) != 0) { return 0; } } return 1; } /* read or construct a secret key */ int find_key(struct secret_key *sk, struct signing_context *ctx) { char *keystring, *keyfile, *idstring, *keyprefix; size_t len; keystring = ctx->keystring; keyfile = ctx->keyfile; idstring = ctx->keyid; keyprefix = ctx->keyprefix; //fprintf(stderr, "looking for key, str = \"%s\", file = \"%s\", id = \"%s\", prefix = \"%s\"\n", keystring, keyfile, idstring, keyprefix); if (keystring) { len = strlen(keystring); if (len < 64) { return 0; } hex2bin(sk->key, sizeof sk->key, keystring, len); create_key(sk, 0); set_key_id(sk, idstring); return 1; } else { char *keyringfile = find_keyfile(keyfile); if (!keyringfile) { return 0; } int keyring = open(keyringfile, O_RDONLY); union signobject obj; int rv; while ((rv = read_object(keyring, &obj))) { if (rv != SECRET_KEY) { continue; } if (object_filter(&obj, SECRET_KEY, idstring, keyprefix)) { break; } } close(keyring); if (rv == 1) { return 1; } } if (sk->encryption && ctx->passphrase) { collect_key(ctx->passkey, ctx->passphrase); decrypt_key(sk, ctx->passkey); } return 0; } /* flags: 1 = hex encoded, 2 = input is path */ char *create_message(char *in, size_t *len, int flags) { char *msg = 0; if (in) { if (flags == 2) { msg = map(in, len); } else if (flags == 1) { *len = strlen(in); msg = malloc(*len/2 + 2); *len = hex2bin(msg, *len, in, *len); } else { *len = strlen(in); msg = in; } } else { /* would need to read from stdin and buffer */ } return msg; } char *construct_message(char *ms, char *path, size_t *mlen, int flags) { char *msg = 0, *mapped = 0; *mlen = 0; if (ms) { *mlen = strlen(ms); msg = ms; } else if (path) { mapped = msg = map(path, mlen); } if (flags) { /* input is hex encoded */ char *new; new = malloc(*mlen/2+1); *mlen = hex2bin(new, *mlen/2+1, msg, *mlen); msg = new; } return msg; } /* TODO should use mlock()ed memory for the secret key */ #if 0 #if _POSIX_ADVISORY_INFO > 0 long pagesize = sysconf(_SC_PAGESIZE); int rv = posix_memalign(&sk, pagesize, sizeof *sk); if (rv) { mlock(sk, sizeof sk); /* not able to lock, use stack memory */ } else { /* not able to allocate, use stack memory */ } /* TODO if the allocate or lock fails, try mlockall(), * if that fails, abort or perhaps continue if insecure mode */ /* TODO also mlock buffers */ #endif #endif /* if flags == 0, then need to hash the message and the signature info * before verifying, otherwise, bare sign, so just verify */ int verify(struct signature *sig, char *message, size_t mlen, int flags) { char hash[64]; if (flags == 0) { sighash(hash, sig, message, mlen); message = hash; mlen = sizeof hash; } return ed25519_verify(sig->signature, sig->key, message, mlen); } int generate_key(struct secret_key *sk, char *keystring, char *idstring) { size_t len; if (!sk) { return 0; } if (keystring) { len = strlen(keystring); if (len < 64) { return 0; } hex2bin(sk->key, sizeof sk->key, keystring, len); create_key(sk, 0); } else { create_key(sk, 1); } if (idstring) { strncpy(sk->id, idstring, 119); } return 1; } int list_object(int fd, union signobject *obj) { if (fd >= 0 && obj) { dump_object(fd, obj); } return 1; } /* TODO can be just a find_key and take an arg */ int find_public_key(struct public_key *pk, char *id, char *pstr, char *pfile) { char *path = 0, buf[32]; int fd, bytes, type = 0; size_t len; if (!pstr && !pfile) { pstr = getenv("ZPM_PUBLIC_KEY"); } if (pstr) { /* TODO check strlen == 64 */ len = strlen(pstr); if (len >= 512) { type = deserialize(pk, pstr, len); } else if (len >= 64) { bytes = hex2bin(buf, sizeof buf, pstr, len); if (bytes == 32) { pubkey_init(pk); memcpy(pk->key, buf, 32); if (id) { set_pub_id(pk, id); } type = PUBLIC_KEY; } } } else if (pfile) { path = find_keyfile(pfile); if (!path) { return 0; } fd = open(path, O_RDONLY); free(path); if (fd == -1) { return 0; } int64_t curtime = time(0); int type; do { type = read_object(fd, (union signobject *)pk); if (type != PUBLIC_KEY) { continue; } if (curtime > pk->expires) { /* TODO warn? */ continue; } if (!id || obj_id_match((union signobject *)pk, id)) { close(fd); return type; } } while (type != 0); close(fd); } return type; } int find_signature(struct signature *sig, char *sigstring, char *sigfile) { size_t len; int type = 0; char *mapped = 0; char buf[64]; if (sigstring) { len = strlen(sigstring); if (len >= 512) { type = deserialize(sig, sigstring, len); } else if (len >= 128) { len = hex2bin(buf, sizeof buf, sigstring, len); if (len == 64) { init_signature(sig); memcpy(sig->signature, buf, 64); type = SIGNATURE; } } } else if (sigfile) { mapped = map(sigfile, &len); if (mapped && len >= 512) { type = deserialize(sig, mapped, len); } } if (mapped) { munmap(mapped, len); } return type; } int object_match(union signobject *a, union signobject *b) { if (!a && !b) { return 1; } if (!a || !b) { return 0; } if (object_type(a) != object_type(b)) { return 0; } if (object_type(a) == PUBLIC_KEY) { return memcmp(a->pk.key, b->pk.key, 32) == 0; } else if (object_type(a) == SECRET_KEY) { return memcmp(a->sk.key, b->sk.key, 32) == 0; } else if (object_type(a) == SIGNATURE) { if (memcmp(a->sig.key, b->sig.key, 32) != 0) { return 0; } return memcmp(a->sig.signature, b->sig.signature, 64) == 0; } return 0; } /* return true if we find something that matches obj */ int find_object(int fd, union signobject *lookfor) { union signobject obj; while (read_object(fd, &obj) > 0) { if (object_match(lookfor, &obj)) { return 1; } } return 0; } #include #include int main(int ac, char *av[]) { /* * M = message, P = public key, K = secret key, S = signature * T = timestamp, E = expires, I = identity, H = message hash * C = ciphertext, W = password */ struct signing_context ctx = { 0 }; unsigned char *message = 0; size_t mlen; char *messagefile = 0; char *messagestring = 0; char *timestring = 0, *expirestring = 0; int64_t timestamp = 0, expires = INT64_MAX; char *passphrase = 0; unsigned char passkey[32]; char *keyid = 0, *keyprefix = 0; struct secret_key sk = { 0 }; char *keyfile = 0, *keystring = 0; struct public_key pk = { 0 }; char *pubfile = 0, *pubstring = 0; struct signature sig = { 0 }; char *sigfile = 0, *sigstring = 0; char *encfile = 0, *encstring = 0; char *outfile = 0; int output = 1; /* file descriptor */ int option, argn; int mode = 0; int hexencoded = 0, outputflags = 0; int debug = 0, quiet = 0, bareinput = 0; int outputmsghash = 0; int rv; #if 0 #if _POSIX_ADVISORY_INFO > 0 fprintf(stderr, "locking memory\n"); struct rlimit lim; getrlimit(RLIMIT_MEMLOCK, &lim); fprintf(stderr, "memlock limit %llu/%llu\n", lim.rlim_cur, lim.rlim_max); struct public_key *pkp; posix_memalign(&pkp, rv = mlockall(MCL_CURRENT|MCL_FUTURE); /* can't happen, have to just lock sk and maybe ctx */ if (rv == -1) { perror("unable to lock memory"); } #endif #endif while ((option = getopt(ac, av, "vsgelxyirdHo:hbT:E:n:w:m:N:k:K:p:P:f:F:c:C:")) != -1) { switch (option) { /* modes */ case 'v': mode = VERIFY; break; case 's': mode = SIGN; break; case 'g': mode = KEYGEN; break; case 'e': mode = EXTRACT; break; case 'l': mode = LIST; break; case 'x': mode = ENCRYPT; break; case 'y': mode = DECRYPT; break; case 'i': mode = IMPORT; break; /* more modes: * remove pw, add pw, export, delete * need a manage mode to delete * or update keys */ #if 0 /* trust level */ case 't': trust = strtoul(optarg, 0, 10); break; #endif /* outputs */ case 'r': outputflags |= RAW; break; case 'd': debug++; break; case 'H': outputmsghash = 1; break; case 'o': outfile = optarg; break; case 'q': quiet++; break; /* inputs */ case 'h': hexencoded = 1; break; case 'b': bareinput = 1; break; case 'T': timestring = optarg; break; /* T */ case 'E': expirestring = optarg; break; /* E */ case 'n': keyid = optarg; break; /* I */ case 'w': passphrase = optarg; break; /* W */ case 'm': messagestring = optarg; break; /* M */ case 'N': keyprefix = optarg; break; /* K ish */ case 'k': keyfile = optarg; break; /* K */ case 'K': keystring = optarg; break; /* K */ case 'p': pubfile = optarg; break; /* P */ case 'P': pubstring = optarg; break; /* P */ case 'f': sigfile = optarg; break; /* S */ case 'F': sigstring = optarg; break; /* S */ case 'c': encfile = optarg; break; /* C */ case 'C': encstring = optarg; break; /* C */ default: exit(EXIT_FAILURE); break; } } argn = optind; /* * M = message, P = public key, K = secret key, S = signature * T = timestamp, E = expires, I = identity, H = message hash * C = ciphertext, W = password * * -sb bare data sign (M, K) -> S * -s full data sign ( (M,T,E) -> H, K) -> S * -s key sign ( (P,T,E) -> H, K) -> S * -e extract (K) -> P * -g generate (T, E, I) -> K (default to keyring append) * -g generate (K, T, E, I) -> K (default to keyring append) * self sign ( (K->P,T,E,I) -> H, M) -> S * import (K/P/S) -> keyring file append, stdout daft, unless redir * show (K/P/S) -> output input * -l list keys (K/P/S) -> key list, get from -k * -v verify (M, S) -> boolean, via exit value, no output * -x encrypt (M, K, T, E, P...) -> C * -y decrypt (C, K) -> M * -H generate signing hash for message or key * * output style: serialized, -d human readable, -r raw * input style: serialized, -b bare, -h hexencoded * * possible inputs: M K T E P I S C * M -> from -m, non-option arg, stdin * K -> from -K, ZPM_KEY, -k, ZPM_KEYFILE, ~/.zpm/key * P -> from -P, -p * S -> from -F, -f, second arg? * C -> from -C, -c, non-option arg, stdin * T -> from -T, time(NULL) * E -> from -E, never/-1 * I -> from -n, ZPM_KEYID, K, S, null * W -> from -w, ZPM_PASS, ~/.zpm/pass, terminal */ if (timestring) { timestamp = strtoull(timestring, 0, 10); } else { timestamp = time(NULL); } if (expirestring) { expires = strtoull(expirestring, 0, 10); } ctx.keystring = keystring; ctx.keyfile = keyfile; ctx.keyid = keyid; ctx.keyprefix = keyprefix; ctx.passphrase = passphrase; if (mode == KEYGEN) { rv = generate_key(&sk, keystring, keyid); if (!rv) { exit(EXIT_FAILURE); } sk.created = timestamp; sk.expires = expires; } else if (mode == SIGN) { char *firstarg = av[argn]; rv = find_key(&sk, &ctx); if (!rv) { fprintf(stderr, "unable to find signing key\n"); exit(EXIT_FAILURE); } char *message = construct_message(messagestring, firstarg, &mlen, hexencoded); // dump_secret_key(2, &sk); sig.signtime = timestamp; sig.expires = expires; rv = create_signature(&sig, &sk, bareinput, message, mlen); if (!rv) { exit(EXIT_FAILURE); } // dump_signature(2, &sig); } else if (mode == SIGNKEY) { find_key(&sk, &ctx); find_public_key(&pk, keyid, pubstring, pubfile); /* TODO construct a message out of a public key */ mlen = 0; sig.signtime = timestamp; sig.expires = expires; rv = create_signature(&sig, &sk, 0, message, mlen); if (!rv) { exit(EXIT_FAILURE); } } else if (mode == EXTRACT) { rv = find_key(&sk, &ctx); if (passphrase) { decrypt_sk(&sk, passphrase); } if (!rv) { exit(EXIT_FAILURE); } derive_pubkey(&pk, &sk); } else if (mode == VERIFY) { rv = find_signature(&sig, sigstring, sigfile); if (rv == SIGNATURE) { /* if you've specified the public key in a verify op, * you probably know what you're doing */ if (pubstring || pubfile) { rv = find_public_key(&pk, keyid, pubstring, pubfile); if (rv == PUBLIC_KEY) { memcpy(sig.key, pk.key, 32); } } //dump_signature(2, &sig); message = construct_message(messagestring, av[argn], &mlen, hexencoded); rv = verify(&sig, message, mlen, bareinput); } else { rv = 0; } } else if (mode == LIST) { union signobject obj; char *keyringfile = find_keyfile(keyfile); if (!keyringfile) { fprintf(stderr, "can't find keyring file\n"); exit(1); } int keyring = open(keyringfile, O_RDWR|O_APPEND); if (keyring == -1) { fprintf(stderr, "can't open keyring file %s\n", keyringfile); perror(""); exit(EXIT_FAILURE); } while ((rv = read_object(keyring, &obj))) { list_object(output, &obj); } close(keyring); } else if (mode == IMPORT) { int fd, i, rv; union signobject obj; char *keyringfile = find_keyfile(keyfile); int keyring = open(keyringfile, O_RDWR|O_APPEND); if (keyring == -1) { fprintf(stderr, "%s ", keyringfile); perror(""); exit(EXIT_FAILURE); } for (i = argn; i < ac; i++) { fd = open(av[i], O_RDONLY); if (fd == -1) { fprintf(stderr, "%s ", av[i]); perror(""); rv = 0; close(keyring); exit(EXIT_FAILURE); } while ((rv = read_object(fd, &obj))) { fprintf(stderr, "importing type %d\n", object_type(&obj)); /* TODO probably a more efficient way to do * this. Perhaps read in the keyring to * memory. */ lseek(keyring, 0, SEEK_SET); if (!find_object(keyring, &obj)) { write_object(keyring, &obj, outputflags); } } close(fd); } close(keyring); } else if (mode == ENCRYPT) { fprintf(stderr, "encryption not supported\n"); exit(EXIT_FAILURE); } else if (mode == DECRYPT) { fprintf(stderr, "decryption not supported\n"); exit(EXIT_FAILURE); } /* * now, do the action */ if (mode == KEYGEN) { if (!outfile) { outfile = find_keyfile(keyfile); } if (outfile && strcmp(outfile, "-") != 0) { output = open(outfile, O_RDWR|O_CREAT|O_APPEND, 0600); } /* TODO encrypt key */ if (passphrase) { fprintf(stderr, "encrypting key\n"); collect_key(passkey, passphrase); encrypt_key(&sk, passkey); //memset(passphrase, 0, strlen(passphrase)); memset(passkey, 0, 32); } // dump_secret_key(2, &sk); write_secret_key(output, &sk, outputflags); } else if (mode == SIGN || mode == SIGNKEY) { if (outfile && strcmp(outfile, "-") != 0) { output = open(outfile, O_RDWR|O_CREAT|O_APPEND, 0600); } //dump_signature(2, &sig); write_signature(output, &sig, outputflags); } else if (mode == EXTRACT) { if (outfile && strcmp(outfile, "-") != 0) { output = open(outfile, O_RDWR|O_CREAT|O_APPEND, 0600); } // dump_public_key(2, &pk); write_public_key(output, &pk, outputflags); } else if (mode == VERIFY) { exit(rv ? 0 : 1); } else if (mode == LIST) { /* handled above */ } else if (mode == IMPORT) { /* handled above */ } else if (mode == ENCRYPT) { fprintf(stderr, "encryption not supported\n"); exit(EXIT_FAILURE); } else if (mode == DECRYPT) { fprintf(stderr, "decryption not supported\n"); exit(EXIT_FAILURE); } if (message && messagefile) { munmap(message, mlen); } memset(sk.key, 0, sizeof sk.key); return 0; }