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>
46 static void hexdump(void *src, size_t len) {
47 unsigned char *b = src;
54 static char hexchars[] = "0123456789abcdefABCDEF";
56 static void hex(char *dst, uint8_t *src, size_t len) {
58 dst[0] = hexchars[(src[0]>>4)&0xf];
59 dst[1] = hexchars[src[0]&0xf];
65 static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
69 for (i=0; i<len; i+=2) {
70 sscanf((const char *)src+i, "%02x", &x);
75 void *map(char *file, size_t *size) {
80 fd = open(file, O_RDONLY);
91 m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
92 if (m == MAP_FAILED) {
101 ssize_t readbytes(char *dest, char *file, size_t n) {
113 size_t pk = strspn(m, hexchars);
119 hexbin(dest, m, n*2);
125 /* private key format is:
126 * "ZPMS" 4 byte magic "ZPMS"
127 * byte 0x01 = version 1
128 * byte 0x01 = private key
129 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
130 * byte 0x00 reserved, probably "key usage"
131 * 32 bytes private key
133 * sub keys, public keys? No point to the public key, since it's
134 * always derivable. could have 16 bytes of IV for chacha to encrypt.
136 * entire thing is then hexencoded, but
139 /* public key format is:
140 * "ZPMS" 4 byte magic "ZPMS"
141 * 0x01 1 byte version
142 * 0x02 1 byte "public key packet"
143 * 0x00 1 byte reserved
144 * 0x00 1 byte reserved
145 * 0x.. 32 bytes public key
146 * 0x.. 24 bytes reserved
147 * 0x.. revocation signatures
148 * 0x.. user id packets
149 * 0x.. certification signatures
150 * 0x.. sub key packets
153 /* timestamps are 8 byte little endian integers of seconds since
154 * the epoch. or unix time, or some such. Times are a mess.
155 * Unix time is leap second unaware, and ambiguous during leap seconds.
156 * UTC requires a database of leap seconds for past time, and the
157 * specified second in the future possibly changes.
158 * TAI is good, but currently 37 seconds behind UTC, which is just weird
161 /* signature items from openpgp */
168 * "ZPMS" 4 byte magic "ZPMS"
169 * 0x01 1 byte version
170 * 0x03 1 byte "signature packet"
171 * 0x00 1 byte reserved
172 * 0x00 1 byte reserved
173 * 0x.. 8 bytes timestamp of signature time
174 * 0x.. 8 bytes timestamp of signature expiration, 0 if none
175 * 0x.. 64 bytes of actual signature
176 * 0x.. 32 bytes of public key making the signature
177 * 0x.. 8 bytes reserved
178 * Total is 128 bytes.
181 /* expires time could probably be 4 bytes as an offset from created.
182 * That would free up 4 bytes. Sign time could probably be four
183 * bytes with an epoch of say 2019-01-01, since negative times don't
184 * really make any sense, that give more than 100 years, and frees
187 * version is zero until this proof of concept is better validated
190 char magic[4]; /* magic ZPMS */
191 char info[4]; /* version = 0, type=3, 1 byte trust, 1 byte reserved */
192 uint64_t signtime; /* unix time */
193 uint64_t expires; /* unix time, 0 = none */
200 char magic[4]; /* "ZPMS" */
201 char info[4]; /* version, type = 0x02, reserved, reserved */
206 char sig[64]; /* zeroes for a private key, or fill in, doesn't matter*/
207 /* could fill in the first 32 bytes of sig with the public key
208 * if this is a private key, then you don't need another structure */
211 int create_key(struct key *sk) {
212 char info[] = { 0, 1, 0, 0 };
213 memcpy(sk->magic, "ZPMS", 4);
214 memcpy(sk->info, info, 4);
215 getrandom(sk->key, sizeof sk->key, 0);
216 ed25519_genpub(sk->sig, sk->key);
217 sk->created = time(NULL);
218 memset(sk->reserved, 0, 8);
219 memset(sk->sig+32, 0, 32);
223 int derive_pubkey(struct key *pk, struct key *sk) {
224 char info[] = { 0, 2, 0, 0 };
226 memcpy(sk->magic, "ZPMS", 4);
227 memcpy(sk->info, info, 4);
228 ed25519_genpub(pk->key, sk->key);
229 pk->created = sk->created;
230 pk->expires = sk->expires;
231 memset(sk->reserved, 0, 8);
233 ed25519_sign(pk->sig, sk->key, pk->sig, sk->info, 52);
238 /* if reserved used 16 bytes for signtime and expires, and we added
239 * 64 bytes as a signature, a public key could include it's own self
240 * signature, and a key would be 128 bytes, as would a signature. This
241 * would make all structures 128 bytes.
244 int read_secret_key(uint8_t *sec, uint8_t *pub, char *file) {
247 readbytes(sk, file, sizeof sk);
248 if (sk[4] != 1 || sk[5] != 1) {
253 memcpy(sec, sk+8, 32);
258 ed25519_genpub(pub, sec);
266 /* private key format is:
267 * "ZPMS" 4 byte magic "ZPMS"
268 * byte 0x01 = version 1
269 * byte 0x01 = private key
270 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
271 * byte 0x00 reserved, probably "key usage"
272 * 32 bytes private key
274 * sub keys, public keys? No point to the public key, since it's
275 * always derivable. could have 16 bytes of IV for chacha to encrypt.
277 * entire thing is then hexencoded, but
280 int write_secret_key(uint8_t *sec, int flags, int fd) {
281 uint8_t skh[128] = "5A504D5301010000"; /* ZPMS 1 1 0 0 */
287 hex(skh + 16, sec, 32);
288 memset(skh + 80, '0', 48);
289 write(fd, skh, sizeof skh);
290 memset(skh, 0, sizeof skh);
295 int main(int ac, char *av[]) {
299 uint8_t pub[ED25519_KEY_LEN];
300 /* TODO should use mlock()ed memory for the secret key */
301 uint8_t sec[ED25519_KEY_LEN];
302 uint8_t sig[ED25519_SIG_LEN];
304 char *messagefile = 0;
305 char *messagestring = 0;
306 char messagehash[SHA512_HASH_LENGTH];
307 char *keystring = 0, *keyfile = 0;
308 unsigned char *sigstring = 0, *sigfile = 0;
309 char *passphrase = 0;
311 int rawsig = 0, hexencoded = 0;
314 while ((option = getopt(ac, av, "o:vS:f:sgek:K:rm:hd")) != -1) {
316 case 'o': outfile = optarg; break;
317 case 'v': mode = VERIFY; break;
318 case 'S': sigstring = optarg; break;
319 case 'f': sigfile = optarg; break;
320 case 's': mode = SIGN; break;
321 case 'g': mode = GENKEY; break;
322 case 'e': mode = EXTRACT; break;
323 case 'k': keystring = optarg; break;
324 case 'K': keyfile = optarg; break;
325 case 'p': passphrase = optarg; break;
326 case 'm': messagestring = optarg; break;
327 case 'h': hexencoded = 1; break;
328 case 'r': rawsig = 1; break;
329 case 'd': debug++; break;
338 memset(sig, 0, sizeof sig);
339 r = readbytes(sig, sigfile, sizeof sig);
343 if ((size_t)r < sizeof sig) {
346 } else if (sigstring) {
347 memset(sig, 0, sizeof sig);
348 hexbin(sig, sigstring, sizeof sig);
352 memset(sec, 0, sizeof sec);
353 hexbin(sec, keystring, strlen(keystring));
354 ed25519_genpub(pub, sec);
356 printf("sec:\n"); hexdump(sec, sizeof sec);
358 } else if (keyfile) {
359 read_secret_key(sec, pub, keyfile);
360 //hexdump(sec, sizeof sec);
361 //hexdump(pub, sizeof pub);
364 if (mode == EXTRACT) {
365 /* want to print the pubkey only */
366 hexdump(pub, sizeof pub);
370 /* need to be able to encrypt the private keys */
372 if (mode == GENKEY) {
373 /* a private key is just 32 random bytes */
374 getrandom(sec, sizeof sec, 0);
375 ed25519_genpub(pub, sec);
376 ed25519_sign(sig, sec, pub, sec, sizeof sec);
377 //int rv = ed25519_verify(sig, pub, sec, sizeof sec);
380 fd = open(outfile, O_WRONLY|O_CREAT, 0600);
382 perror("can't open outfile");
385 write_secret_key(sec, 0, fd);
388 write_secret_key(sec, 0, 1);
392 /* set up the message data */
393 if (mode == SIGN || mode == VERIFY) {
394 messagefile = av[optind];
396 message = map(messagefile, &mlen);
400 } else if (messagestring) {
401 message = messagestring;
403 mlen = strlen(messagestring)/2;
404 message = malloc(mlen);
408 hexbin(message, messagestring, mlen*2);
410 mlen = strlen(messagestring);
430 s.signtime = time(NULL);
432 memset(s.reserved, 0, 8);
436 sha512_add(&hash, message, mlen);
437 sha512_add(&hash, (uint8_t *)&s.signtime, sizeof s.signtime);
438 sha512_add(&hash, (uint8_t *)&s.expires, sizeof s.expires);
439 sha512_add(&hash, s.info, sizeof s.info);
440 sha512_final(&hash, messagehash);
441 message = messagehash;
448 ed25519_sign(sig, sec, pub, message, mlen);
449 memcpy(s.sig, sig, sizeof sig);
450 memcpy(s.pub, pub, sizeof pub);
452 hexdump(sig, sizeof sig);
454 hexdump(&s, sizeof s);
458 if (mode == VERIFY) {
459 fprintf(stderr, "sig: ");
460 hexdump(sig, sizeof sig);
461 int rv = ed25519_verify(sig, pub, message, mlen);
463 fprintf(stderr, "verified\n");
466 fprintf(stderr, "not verified\n");
471 if (message && messagefile) {
472 munmap(message, mlen);