X-Git-Url: https://pd.if.org/git/?p=zpackage;a=blobdiff_plain;f=crypto%2Flibeddsa%2Fsign.c;h=593f993c76e0c17366606179ddd49eafe35513a7;hp=723c1f06b45cb9811614a214d1fc304f955a6f18;hb=0c2216d1e0dc8565a6bf61c9572e47bb1ae1c1fb;hpb=27f7fa2a8566beb45fc813fcd8ff3f1e7c738883 diff --git a/crypto/libeddsa/sign.c b/crypto/libeddsa/sign.c index 723c1f0..593f993 100644 --- a/crypto/libeddsa/sign.c +++ b/crypto/libeddsa/sign.c @@ -22,9 +22,12 @@ * -h message is hex encoded */ +/* chacha20 encryption: 256 bit key = 32 bytes */ + #include #include #include +#include #include #include #include @@ -37,22 +40,468 @@ #include "eddsa.h" #include "sha512.h" +#include "crypto/chacha.h" +#include "lib/blake2/ref/blake2.h" + +char *readpass(char *prompt); #define VERIFY 1 #define SIGN 2 -#define GENKEY 3 +#define KEYGEN 3 #define EXTRACT 4 +#define KEYSIGN 5 +#define LIST 6 +#define ENCRYPT 7 +#define DECRYPT 8 +#define SIGNKEY 9 +#define IMPORT 10 -//#define MARK do { fprintf(stderr, "%s %s:%d\n", __FILE__, __func__, __LINE__); } while (0) +/* output flags */ +#define RAW 0x1 +#define HUMANREADABLE 0x2 -static void hexdump(void *src, size_t len) { - unsigned char *b = src; - while (len--) { - printf("%02x", *b++); +#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) { @@ -64,14 +513,116 @@ 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 = bytes; i < 256; i++) { + buf[i] = 0; + } + + if (bytes == 248 && memcmp(buf, "ZPMS", 4) == 0) { + type = buf[5]; + } else { + return 0; + } - for (i=0; ikey, 32); + outlen = 64; + } else { + seckey_bytea(skh, sk); + serialize(serialized, skh); + outlen = sizeof serialized; } - if (n > fs) { - n = fs; + + 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; } - 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_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; } - 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_object(int fd, void *o, int outputflags) { + uint8_t bytes[256]; + char serialized[512]; + size_t outlen; + int type; + union signobject *obj = o; -/* 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 - */ + type = ((struct secret_key *)obj)->type; -/* 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 - */ + 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); + } -/* 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 - */ + write(fd, serialized, outlen); + memset(serialized, 0, outlen); -/* signature items from openpgp */ -/* revokable flag - * 0-255 trust level - * - */ + return 1; +} -/* 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. - */ +/* TODO work on strings, not file output */ +void write_to_512(int fd, int output) { + char trailer[65]; + memset(trailer, '-', 64); + trailer[64] = '\n'; -/* 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]; -}; + 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; + } +} -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 */ -}; +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); -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 }; +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); - 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); + 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); - ed25519_sign(pk->sig, sk->key, pk->sig, sk->info, 52); 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; } -/* 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. +/* 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; + } -int read_secret_key(uint8_t *sec, uint8_t *pub, char *file) { - uint8_t sk[64]; + 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; - 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; } - memcpy(sec, sk+8, 32); - //printf("sec: "); - //hexdump(sec, 32); - if (pub) { - ed25519_genpub(pub, sec); - // printf("pub: "); - // hexdump(pub, 32); + 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; } -/* 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 +#include + 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 == EXTRACT) { - /* 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; }