1 #define _POSIX_C_SOURCE 200809L
3 /* general zpm sign function */
5 /* read key, export key, import key, sign, export signature, import
6 * signature, verify signature
9 /* import and export is base64 encoded */
14 * -e extract public key
18 * -p read key from file
19 * -P read public key from argument to -P
20 * -S read secret key from argument
21 * -m message from argument
22 * -h message is hex encoded
29 #include <sys/types.h>
35 /* linux specific for getrandom */
36 #include <sys/random.h>
47 static void hexdump(void *src, size_t len) {
48 unsigned char *b = src;
55 static char hexchars[] = "0123456789abcdefABCDEF";
57 static void hex(char *dst, uint8_t *src, size_t len) {
59 dst[0] = hexchars[(src[0]>>4)&0xf];
60 dst[1] = hexchars[src[0]&0xf];
66 static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
70 for (i=0; i<len; i+=2) {
71 sscanf((const char *)src+i, "%02x", &x);
76 void *map(char *file, size_t *size) {
81 fd = open(file, O_RDONLY);
92 m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
93 if (m == MAP_FAILED) {
102 ssize_t readbytes(char *dest, char *file, size_t n) {
114 size_t pk = strspn(m, hexchars);
120 hexbin(dest, m, n*2);
126 /* private key format is:
127 * "ZPMS" 4 byte magic "ZPMS"
128 * byte 0x01 = version 1
129 * byte 0x01 = private key
130 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
131 * byte 0x00 reserved, probably "key usage"
132 * 32 bytes private key
134 * sub keys, public keys? No point to the public key, since it's
135 * always derivable. could have 16 bytes of IV for chacha to encrypt.
137 * entire thing is then hexencoded, but
140 /* public key format is:
141 * "ZPMS" 4 byte magic "ZPMS"
142 * 0x01 1 byte version
143 * 0x02 1 byte "public key packet"
144 * 0x00 1 byte reserved
145 * 0x00 1 byte reserved
146 * 0x.. 32 bytes public key
147 * 0x.. 24 bytes reserved
148 * 0x.. revocation signatures
149 * 0x.. user id packets
150 * 0x.. certification signatures
151 * 0x.. sub key packets
154 /* timestamps are 8 byte little endian integers of seconds since
155 * the epoch. or unix time, or some such. Times are a mess.
156 * Unix time is leap second unaware, and ambiguous during leap seconds.
157 * UTC requires a database of leap seconds for past time, and the
158 * specified second in the future possibly changes.
159 * TAI is good, but currently 37 seconds behind UTC, which is just weird
162 /* signature items from openpgp */
169 * "ZPMS" 4 byte magic "ZPMS"
170 * 0x01 1 byte version
171 * 0x03 1 byte "signature packet"
172 * 0x00 1 byte reserved
173 * 0x00 1 byte reserved
174 * 0x.. 8 bytes timestamp of signature time
175 * 0x.. 8 bytes timestamp of signature expiration, 0 if none
176 * 0x.. 64 bytes of actual signature
177 * 0x.. 32 bytes of public key making the signature
178 * 0x.. 8 bytes reserved
179 * Total is 128 bytes.
182 /* expires time could probably be 4 bytes as an offset from created.
183 * That would free up 4 bytes. Sign time could probably be four
184 * bytes with an epoch of say 2019-01-01, since negative times don't
185 * really make any sense, that give more than 100 years, and frees
188 * version is zero until this proof of concept is better validated
191 char magic[4]; /* magic ZPMS */
192 char info[4]; /* version = 0, type=3, 1 byte trust, 1 byte reserved */
193 uint64_t signtime; /* unix time */
194 uint64_t expires; /* unix time, 0 = none */
201 char magic[4]; /* "ZPMS" */
202 char info[4]; /* version, type = 0x02, reserved, reserved */
207 char sig[64]; /* zeroes for a private key, or fill in, doesn't matter*/
208 /* could fill in the first 32 bytes of sig with the public key
209 * if this is a private key, then you don't need another structure */
212 int create_key(struct key *sk) {
213 char info[] = { 0, 1, 0, 0 };
214 memcpy(sk->magic, "ZPMS", 4);
215 memcpy(sk->info, info, 4);
216 getrandom(sk->key, sizeof sk->key, 0);
217 ed25519_genpub(sk->sig, sk->key);
218 sk->created = time(NULL);
219 memset(sk->reserved, 0, 8);
220 memset(sk->sig+32, 0, 32);
224 int derive_pubkey(struct key *pk, struct key *sk) {
225 char info[] = { 0, 2, 0, 0 };
227 memcpy(sk->magic, "ZPMS", 4);
228 memcpy(sk->info, info, 4);
229 ed25519_genpub(pk->key, sk->key);
230 pk->created = sk->created;
231 pk->expires = sk->expires;
232 memset(sk->reserved, 0, 8);
234 ed25519_sign(pk->sig, sk->key, pk->sig, sk->info, 52);
239 /* if reserved used 16 bytes for signtime and expires, and we added
240 * 64 bytes as a signature, a public key could include it's own self
241 * signature, and a key would be 128 bytes, as would a signature. This
242 * would make all structures 128 bytes.
245 int read_secret_key(uint8_t *sec, uint8_t *pub, char *file) {
248 readbytes(sk, file, sizeof sk);
249 if (sk[4] != 1 || sk[5] != 1) {
254 memcpy(sec, sk+8, 32);
259 ed25519_genpub(pub, sec);
267 /* private key format is:
268 * "ZPMS" 4 byte magic "ZPMS"
269 * byte 0x01 = version 1
270 * byte 0x01 = private key
271 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
272 * byte 0x00 reserved, probably "key usage"
273 * 32 bytes private key
275 * sub keys, public keys? No point to the public key, since it's
276 * always derivable. could have 16 bytes of IV for chacha to encrypt.
278 * entire thing is then hexencoded, but
281 int write_secret_key(uint8_t *sec, int flags, int fd) {
282 uint8_t skh[128] = "5A504D5301010000"; /* ZPMS 1 1 0 0 */
288 hex(skh + 16, sec, 32);
289 memset(skh + 80, '0', 48);
290 write(fd, skh, sizeof skh);
291 memset(skh, 0, sizeof skh);
296 int main(int ac, char *av[]) {
300 uint8_t pub[ED25519_KEY_LEN];
301 /* TODO should use mlock()ed memory for the secret key */
302 uint8_t sec[ED25519_KEY_LEN];
303 uint8_t sig[ED25519_SIG_LEN];
305 char *messagefile = 0;
306 char *messagestring = 0;
307 char messagehash[SHA512_HASH_LENGTH];
308 char *keystring = 0, *keyfile = 0;
309 unsigned char *sigstring = 0, *sigfile = 0;
310 char *passphrase = 0;
312 int rawsig = 0, hexencoded = 0;
315 while ((option = getopt(ac, av, "o:vS:f:sgek:K:rm:hd")) != -1) {
317 case 'o': outfile = optarg; break;
318 case 'v': mode = VERIFY; break;
319 case 'S': sigstring = optarg; break;
320 case 'f': sigfile = optarg; break;
321 case 's': mode = SIGN; break;
322 case 'g': mode = GENKEY; break;
323 case 'e': mode = EXTRACT; break;
324 case 'k': keystring = optarg; break;
325 case 'K': keyfile = optarg; break;
326 case 'p': passphrase = optarg; break;
327 case 'm': messagestring = optarg; break;
328 case 'h': hexencoded = 1; break;
329 case 'r': rawsig = 1; break;
330 case 'd': debug++; break;
339 memset(sig, 0, sizeof sig);
340 r = readbytes(sig, sigfile, sizeof sig);
344 if ((size_t)r < sizeof sig) {
347 } else if (sigstring) {
348 memset(sig, 0, sizeof sig);
349 hexbin(sig, sigstring, sizeof sig);
353 memset(sec, 0, sizeof sec);
354 hexbin(sec, keystring, strlen(keystring));
355 ed25519_genpub(pub, sec);
357 printf("sec:\n"); hexdump(sec, sizeof sec);
359 } else if (keyfile) {
360 read_secret_key(sec, pub, keyfile);
361 //hexdump(sec, sizeof sec);
362 //hexdump(pub, sizeof pub);
365 if (mode == PUBKEY) {
366 /* want to print the pubkey only */
367 hexdump(pub, sizeof pub);
371 /* need to be able to encrypt the private keys */
373 if (mode == GENKEY) {
374 /* a private key is just 32 random bytes */
375 getrandom(sec, sizeof sec, 0);
376 ed25519_genpub(pub, sec);
377 ed25519_sign(sig, sec, pub, sec, sizeof sec);
378 //int rv = ed25519_verify(sig, pub, sec, sizeof sec);
381 fd = open(outfile, O_WRONLY|O_CREAT, 0600);
383 perror("can't open outfile");
386 write_secret_key(sec, 0, fd);
389 write_secret_key(sec, 0, 1);
393 /* set up the message data */
394 if (mode == SIGN || mode == VERIFY) {
395 messagefile = av[optind];
397 message = map(messagefile, &mlen);
401 } else if (messagestring) {
402 message = messagestring;
404 mlen = strlen(messagestring)/2;
405 message = malloc(mlen);
409 hexbin(message, messagestring, mlen*2);
411 mlen = strlen(messagestring);
431 s.signtime = time(NULL);
433 memset(s.reserved, 0, 8);
437 sha512_add(&hash, message, mlen);
438 sha512_add(&hash, (uint8_t *)&s.signtime, sizeof s.signtime);
439 sha512_add(&hash, (uint8_t *)&s.expires, sizeof s.expires);
440 sha512_add(&hash, s.info, sizeof s.info);
441 sha512_final(&hash, messagehash);
442 message = messagehash;
449 ed25519_sign(sig, sec, pub, message, mlen);
450 memcpy(s.sig, sig, sizeof sig);
451 memcpy(s.pub, pub, sizeof pub);
453 hexdump(sig, sizeof sig);
455 hexdump(&s, sizeof s);
459 if (mode == VERIFY) {
460 fprintf(stderr, "sig: ");
461 hexdump(sig, sizeof sig);
462 int rv = ed25519_verify(sig, pub, message, mlen);
464 fprintf(stderr, "verified\n");
467 fprintf(stderr, "not verified\n");
472 if (message && messagefile) {
473 munmap(message, mlen);