* -h message is hex encoded
*/
+/* chacha20 encryption: 256 bit key = 32 bytes */
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#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 GENKEY 3
+#define KEYGEN 3
#define EXTRACT 4
-#define PUBKEY 5
+#define KEYSIGN 5
+#define LIST 6
+#define ENCRYPT 7
+#define DECRYPT 8
+#define SIGNKEY 9
+#define IMPORT 10
-static void hexdump(void *src, size_t len) {
- unsigned char *b = src;
- while (len--) {
- printf("%02x", *b++);
+/* 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);
}
- printf("\n");
+
+ 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) {
}
}
-static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
- size_t i;
- int x;
+/* 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=0; i<len; i+=2) {
- sscanf((const char *)src+i, "%02x", &x);
- dst[i/2] = x;
+ 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) {
return m;
}
-ssize_t readbytes(char *dest, char *file, size_t n) {
- char *m;
- size_t fs;
+/* 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;
- m = map(file, &fs);
- if (!m) {
- return -1;
+ bytes = read512(fd, buffer);
+ if (bytes != 512) {
+ return 0;
}
- if (n > fs) {
- n = fs;
+ 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;
}
- size_t pk = strspn(m, hexchars);
- pk /= 2;
- if (n > pk) {
- n = pk;
+ 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;
}
- hexbin(dest, m, n*2);
- munmap(m, fs);
- return n;
+ 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;
-/* private key format is:
- * "ZPMS" 4 byte magic "ZPMS"
- * byte 0x01 = version 1
- * byte 0x01 = private key
- * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
- * byte 0x00 reserved, probably "key usage"
- * 32 bytes private key
- * 24 bytes reserved
- * sub keys, public keys? No point to the public key, since it's
- * always derivable. could have 16 bytes of IV for chacha to encrypt.
- *
- * entire thing is then hexencoded, but
- */
+ if (outputflags & RAW) {
+ hex(serialized, sig->signature, 64);
+ outlen = 128;
+ } else {
+ signature_bytea(skh, sig);
+ serialize(serialized, skh);
+ outlen = sizeof serialized;
+ }
-/* public key format is:
- * "ZPMS" 4 byte magic "ZPMS"
- * 0x01 1 byte version
- * 0x02 1 byte "public key packet"
- * 0x00 1 byte reserved
- * 0x00 1 byte reserved
- * 0x.. 32 bytes public key
- * 0x.. 24 bytes reserved
- * 0x.. revocation signatures
- * 0x.. user id packets
- * 0x.. certification signatures
- * 0x.. sub key packets
- */
+ write(fd, serialized, outlen);
+ /* clear memory */
+ memset(skh, 0, sizeof skh);
+ memset(serialized, 0, sizeof serialized);
-/* 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
- */
+ return 1;
+}
-/* signature items from openpgp */
-/* revokable flag
- * 0-255 trust level
- *
- */
+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;
-/* signature format:
- * "ZPMS" 4 byte magic "ZPMS"
- * 0x01 1 byte version
- * 0x03 1 byte "signature packet"
- * 0x00 1 byte reserved
- * 0x00 1 byte reserved
- * 0x.. 8 bytes timestamp of signature time
- * 0x.. 8 bytes timestamp of signature expiration, 0 if none
- * 0x.. 64 bytes of actual signature
- * 0x.. 32 bytes of public key making the signature
- * 0x.. 8 bytes reserved
- * Total is 128 bytes.
- */
+ type = ((struct secret_key *)obj)->type;
-/* expires time could probably be 4 bytes as an offset from created.
- * That would free up 4 bytes. Sign time could probably be four
- * bytes with an epoch of say 2019-01-01, since negative times don't
- * really make any sense, that give more than 100 years, and frees
- * up another 4 bytes
- *
- * version is zero until this proof of concept is better validated
- */
-struct signature {
- char magic[4]; /* magic ZPMS */
- char info[4]; /* version = 0, type=3, 1 byte trust, 1 byte reserved */
- uint64_t signtime; /* unix time */
- uint64_t expires; /* unix time, 0 = none */
- char sig[64];
- char pub[32];
- char reserved[8];
-};
+ 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);
+ }
-struct key {
- char magic[4]; /* "ZPMS" */
- char info[4]; /* version, type = 0x02, reserved, reserved */
- char key[32];
- uint64_t created;
- uint64_t expires;
- char reserved[8];
- char sig[64]; /* zeroes for a private key, or fill in, doesn't matter*/
- /* could fill in the first 32 bytes of sig with the public key
- * if this is a private key, then you don't need another structure */
-};
+ write(fd, serialized, outlen);
+ memset(serialized, 0, outlen);
-int create_key(struct key *sk) {
- char info[] = { 0, 1, 0, 0 };
- memcpy(sk->magic, "ZPMS", 4);
- memcpy(sk->info, info, 4);
- getrandom(sk->key, sizeof sk->key, 0);
- ed25519_genpub(sk->sig, sk->key);
- sk->created = time(NULL);
- memset(sk->reserved, 0, 8);
- memset(sk->sig+32, 0, 32);
return 1;
}
-int derive_pubkey(struct key *pk, struct key *sk) {
- char info[] = { 0, 2, 0, 0 };
+/* TODO work on strings, not file output */
+void write_to_512(int fd, int output) {
+ char trailer[65];
+ memset(trailer, '-', 64);
+ trailer[64] = '\n';
- memcpy(sk->magic, "ZPMS", 4);
- memcpy(sk->info, info, 4);
- ed25519_genpub(pk->key, sk->key);
- pk->created = sk->created;
- pk->expires = sk->expires;
- memset(sk->reserved, 0, 8);
+ 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);
- ed25519_sign(pk->sig, sk->key, pk->sig, sk->info, 52);
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;
}
-/* if reserved used 16 bytes for signtime and expires, and we added
- * 64 bytes as a signature, a public key could include it's own self
- * signature, and a key would be 128 bytes, as would a signature. This
- * would make all structures 128 bytes.
+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.
*/
-int read_secret_key(uint8_t *sec, uint8_t *pub, char *file) {
- uint8_t sk[64];
+/* can probably just call read_item */
+int read_object(int fd, union signobject *obj) {
+ char buf[512];
+ ssize_t bytes;
- readbytes(sk, file, sizeof sk);
- if (sk[4] != 1 || sk[5] != 1) {
- printf("magic: ");
- hexdump(sk, 16);
+ 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;
}
- memcpy(sec, sk+8, 32);
- //printf("sec: ");
- //hexdump(sec, 32);
- if (pub) {
- ed25519_genpub(pub, sec);
- // printf("pub: ");
- // hexdump(pub, 32);
+ 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;
}
-/* private key format is:
- * "ZPMS" 4 byte magic "ZPMS"
- * byte 0x01 = version 1
- * byte 0x01 = private key
- * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
- * byte 0x00 reserved, probably "key usage"
- * 32 bytes private key
- * 24 bytes reserved
- * sub keys, public keys? No point to the public key, since it's
- * always derivable. could have 16 bytes of IV for chacha to encrypt.
- *
- * entire thing is then hexencoded, but
+/* 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;
+ }
-int write_secret_key(uint8_t *sec, int flags, int fd) {
- uint8_t skh[128] = "5A504D5301010000"; /* ZPMS 1 1 0 0 */
+ return ed25519_verify(sig->signature, sig->key, message, mlen);
+}
- if (flags & 0x01) {
- skh[13] = '1';
+int generate_key(struct secret_key *sk, char *keystring, char *idstring) {
+ size_t len;
+
+ if (!sk) {
+ return 0;
}
- //hexdump(sec, 32);
- hex(skh + 16, sec, 32);
- memset(skh + 80, '0', 48);
- write(fd, skh, sizeof skh);
- memset(skh, 0, sizeof skh);
+ 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 <sys/time.h>
+#include <sys/resource.h>
+
int main(int ac, char *av[]) {
- int option;
- int mode = 0;
- char *outfile = 0;
- uint8_t pub[ED25519_KEY_LEN];
- /* TODO should use mlock()ed memory for the secret key */
- uint8_t sec[ED25519_KEY_LEN];
- uint8_t sig[ED25519_SIG_LEN];
- void *message = 0;
+ /*
+ * 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 messagehash[SHA512_HASH_LENGTH];
- char *keystring = 0, *keyfile = 0;
- unsigned char *sigstring = 0, *sigfile = 0;
+
+ char *timestring = 0, *expirestring = 0;
+ int64_t timestamp = 0, expires = INT64_MAX;
+
char *passphrase = 0;
- size_t mlen;
- int rawsig = 0, hexencoded = 0;
- int debug = 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, "o:vS:f:sgek:K:rm:hd")) != -1) {
+ while ((option = getopt(ac, av, "vsgelxyirdHo:hbT:E:n:w:m:N:k:K:p:P:f:F:c:C:")) != -1) {
switch (option) {
- case 'o': outfile = optarg; break;
+ /* modes */
case 'v': mode = VERIFY; break;
- case 'S': sigstring = optarg; break;
- case 'f': sigfile = optarg; break;
case 's': mode = SIGN; break;
- case 'g': mode = GENKEY; break;
+ case 'g': mode = KEYGEN; break;
case 'e': mode = EXTRACT; break;
- case 'k': keystring = optarg; break;
- case 'K': keyfile = optarg; break;
- case 'p': passphrase = optarg; break;
- case 'm': messagestring = optarg; break;
- case 'h': hexencoded = 1; break;
- case 'r': rawsig = 1; 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;
}
}
- if (sigfile) {
- ssize_t r;
- memset(sig, 0, sizeof sig);
- r = readbytes(sig, sigfile, sizeof sig);
- if (r == -1) {
- return 8;
- }
- if ((size_t)r < sizeof sig) {
- return 7;
- }
- } else if (sigstring) {
- memset(sig, 0, sizeof sig);
- hexbin(sig, sigstring, sizeof sig);
- }
+ argn = optind;
- if (keystring) {
- memset(sec, 0, sizeof sec);
- hexbin(sec, keystring, strlen(keystring));
- ed25519_genpub(pub, sec);
- if (debug) {
- printf("sec:\n"); hexdump(sec, sizeof sec);
- }
- } else if (keyfile) {
- read_secret_key(sec, pub, keyfile);
- //hexdump(sec, sizeof sec);
- //hexdump(pub, sizeof pub);
+ /*
+ * 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 (mode == PUBKEY) {
- /* want to print the pubkey only */
- hexdump(pub, sizeof pub);
- exit(0);
+ if (expirestring) {
+ expires = strtoull(expirestring, 0, 10);
}
- /* need to be able to encrypt the private keys */
- /* use chacha */
- if (mode == GENKEY) {
- /* a private key is just 32 random bytes */
- getrandom(sec, sizeof sec, 0);
- ed25519_genpub(pub, sec);
- ed25519_sign(sig, sec, pub, sec, sizeof sec);
- //int rv = ed25519_verify(sig, pub, sec, sizeof sec);
- if (outfile) {
- int fd;
- fd = open(outfile, O_WRONLY|O_CREAT, 0600);
- if (fd == -1) {
- perror("can't open outfile");
- exit(EXIT_FAILURE);
+ 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);
+ }
}
- write_secret_key(sec, 0, fd);
- close(fd);
+ //dump_signature(2, &sig);
+
+ message = construct_message(messagestring, av[argn], &mlen, hexencoded);
+ rv = verify(&sig, message, mlen, bareinput);
} else {
- write_secret_key(sec, 0, 1);
+ rv = 0;
}
- }
-
- /* set up the message data */
- if (mode == SIGN || mode == VERIFY) {
- messagefile = av[optind];
- if (messagefile) {
- message = map(messagefile, &mlen);
- if (!message) {
- return 9;
+ } 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);
}
- } else if (messagestring) {
- message = messagestring;
- if (hexencoded) {
- mlen = strlen(messagestring)/2;
- message = malloc(mlen);
- if (!message) {
- return 10;
+ 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);
}
- hexbin(message, messagestring, mlen*2);
- } else {
- mlen = strlen(messagestring);
}
- } else {
- return 2;
- }
- if (!message) {
- return 3;
- }
- }
-
- if (mode == SIGN) {
- struct signature s;
- s.magic[0] = 'Z';
- s.magic[1] = 'P';
- s.magic[2] = 'M';
- s.magic[3] = 'S';
- s.info[0] = 1;
- s.info[1] = 1;
- s.info[2] = 0;
- s.info[3] = 0;
- s.signtime = time(NULL);
- s.expires = 0;
- memset(s.reserved, 0, 8);
- if (!rawsig) {
- struct sha512 hash;
- sha512_init(&hash);
- sha512_add(&hash, message, mlen);
- sha512_add(&hash, (uint8_t *)&s.signtime, sizeof s.signtime);
- sha512_add(&hash, (uint8_t *)&s.expires, sizeof s.expires);
- sha512_add(&hash, s.info, sizeof s.info);
- sha512_final(&hash, messagehash);
- message = messagehash;
- }
-
- if (message == 0) {
- return 5;
- }
-
- ed25519_sign(sig, sec, pub, message, mlen);
- memcpy(s.sig, sig, sizeof sig);
- memcpy(s.pub, pub, sizeof pub);
- if (rawsig) {
- hexdump(sig, sizeof sig);
- } else {
- hexdump(&s, sizeof s);
+ 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);
}
- if (mode == VERIFY) {
- fprintf(stderr, "sig: ");
- hexdump(sig, sizeof sig);
- int rv = ed25519_verify(sig, pub, message, mlen);
- if (rv) {
- fprintf(stderr, "verified\n");
- return 0;
- } else {
- fprintf(stderr, "not verified\n");
- return 6;
+ /*
+ * 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;
}