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 //#define MARK do { fprintf(stderr, "%s %s:%d\n", __FILE__, __func__, __LINE__); } while (0)
48 static void hexdump(void *src, size_t len) {
49 unsigned char *b = src;
56 static char hexchars[] = "0123456789abcdefABCDEF";
58 static void hex(char *dst, uint8_t *src, size_t len) {
60 dst[0] = hexchars[(src[0]>>4)&0xf];
61 dst[1] = hexchars[src[0]&0xf];
67 static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
71 for (i=0; i<len; i+=2) {
72 sscanf((const char *)src+i, "%02x", &x);
77 void *map(char *file, size_t *size) {
82 fd = open(file, O_RDONLY);
93 m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
94 if (m == MAP_FAILED) {
103 ssize_t readbytes(char *dest, char *file, size_t n) {
115 size_t pk = strspn(m, hexchars);
121 hexbin(dest, m, n*2);
127 /* private key format is:
128 * "ZPMS" 4 byte magic "ZPMS"
129 * byte 0x01 = version 1
130 * byte 0x01 = private key
131 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
132 * byte 0x00 reserved, probably "key usage"
133 * 32 bytes private key
135 * sub keys, public keys? No point to the public key, since it's
136 * always derivable. could have 16 bytes of IV for chacha to encrypt.
138 * entire thing is then hexencoded, but
141 /* public key format is:
142 * "ZPMS" 4 byte magic "ZPMS"
143 * 0x01 1 byte version
144 * 0x02 1 byte "public key packet"
145 * 0x00 1 byte reserved
146 * 0x00 1 byte reserved
147 * 0x.. 32 bytes public key
148 * 0x.. 24 bytes reserved
149 * 0x.. revocation signatures
150 * 0x.. user id packets
151 * 0x.. certification signatures
152 * 0x.. sub key packets
155 /* timestamps are 8 byte little endian integers of seconds since
156 * the epoch. or unix time, or some such. Times are a mess.
157 * Unix time is leap second unaware, and ambiguous during leap seconds.
158 * UTC requires a database of leap seconds for past time, and the
159 * specified second in the future possibly changes.
160 * TAI is good, but currently 37 seconds behind UTC, which is just weird
163 /* signature items from openpgp */
170 * "ZPMS" 4 byte magic "ZPMS"
171 * 0x01 1 byte version
172 * 0x03 1 byte "signature packet"
173 * 0x00 1 byte reserved
174 * 0x00 1 byte reserved
175 * 0x.. 8 bytes timestamp of signature time
176 * 0x.. 8 bytes timestamp of signature expiration, 0 if none
177 * 0x.. 64 bytes of actual signature
178 * 0x.. 32 bytes of public key making the signature
179 * 0x.. 8 bytes reserved
180 * Total is 128 bytes.
183 /* expires time could probably be 4 bytes as an offset from created.
184 * That would free up 4 bytes. Sign time could probably be four
185 * bytes with an epoch of say 2019-01-01, since negative times don't
186 * really make any sense, that give more than 100 years, and frees
189 * version is zero until this proof of concept is better validated
192 char magic[4]; /* magic ZPMS */
193 char info[4]; /* version = 0, type=3, 1 byte trust, 1 byte reserved */
194 uint64_t signtime; /* unix time */
195 uint64_t expires; /* unix time, 0 = none */
202 char magic[4]; /* "ZPMS" */
203 char info[4]; /* version, type = 0x02, reserved, reserved */
208 char sig[64]; /* zeroes for a private key, or fill in, doesn't matter*/
209 /* could fill in the first 32 bytes of sig with the public key
210 * if this is a private key, then you don't need another structure */
213 int create_key(struct key *sk) {
214 char info[] = { 0, 1, 0, 0 };
215 memcpy(sk->magic, "ZPMS", 4);
216 memcpy(sk->info, info, 4);
217 getrandom(sk->key, sizeof sk->key, 0);
218 ed25519_genpub(sk->sig, sk->key);
219 sk->created = time(NULL);
220 memset(sk->reserved, 0, 8);
221 memset(sk->sig+32, 0, 32);
225 int derive_pubkey(struct key *pk, struct key *sk) {
226 char info[] = { 0, 2, 0, 0 };
228 memcpy(sk->magic, "ZPMS", 4);
229 memcpy(sk->info, info, 4);
230 ed25519_genpub(pk->key, sk->key);
231 pk->created = sk->created;
232 pk->expires = sk->expires;
233 memset(sk->reserved, 0, 8);
235 ed25519_sign(pk->sig, sk->key, pk->sig, sk->info, 52);
240 /* if reserved used 16 bytes for signtime and expires, and we added
241 * 64 bytes as a signature, a public key could include it's own self
242 * signature, and a key would be 128 bytes, as would a signature. This
243 * would make all structures 128 bytes.
246 int read_secret_key(uint8_t *sec, uint8_t *pub, char *file) {
249 readbytes(sk, file, sizeof sk);
250 if (sk[4] != 1 || sk[5] != 1) {
255 memcpy(sec, sk+8, 32);
260 ed25519_genpub(pub, sec);
268 /* private key format is:
269 * "ZPMS" 4 byte magic "ZPMS"
270 * byte 0x01 = version 1
271 * byte 0x01 = private key
272 * byte 0x01 = chacha encrypted, 0x00 = plain/raw key bytes
273 * byte 0x00 reserved, probably "key usage"
274 * 32 bytes private key
276 * sub keys, public keys? No point to the public key, since it's
277 * always derivable. could have 16 bytes of IV for chacha to encrypt.
279 * entire thing is then hexencoded, but
282 int write_secret_key(uint8_t *sec, int flags, int fd) {
283 uint8_t skh[128] = "5A504D5301010000"; /* ZPMS 1 1 0 0 */
289 hex(skh + 16, sec, 32);
290 memset(skh + 80, '0', 48);
291 write(fd, skh, sizeof skh);
292 memset(skh, 0, sizeof skh);
297 int main(int ac, char *av[]) {
301 uint8_t pub[ED25519_KEY_LEN];
302 /* TODO should use mlock()ed memory for the secret key */
303 uint8_t sec[ED25519_KEY_LEN];
304 uint8_t sig[ED25519_SIG_LEN];
306 char *messagefile = 0;
307 char *messagestring = 0;
308 char messagehash[SHA512_HASH_LENGTH];
309 char *keystring = 0, *keyfile = 0;
310 unsigned char *sigstring = 0, *sigfile = 0;
311 char *passphrase = 0;
313 int rawsig = 0, hexencoded = 0;
316 while ((option = getopt(ac, av, "o:vS:f:sgek:K:rm:hd")) != -1) {
318 case 'o': outfile = optarg; break;
319 case 'v': mode = VERIFY; break;
320 case 'S': sigstring = optarg; break;
321 case 'f': sigfile = optarg; break;
322 case 's': mode = SIGN; break;
323 case 'g': mode = GENKEY; break;
324 case 'e': mode = EXTRACT; break;
325 case 'k': keystring = optarg; break;
326 case 'K': keyfile = optarg; break;
327 case 'p': passphrase = optarg; break;
328 case 'm': messagestring = optarg; break;
329 case 'h': hexencoded = 1; break;
330 case 'r': rawsig = 1; break;
331 case 'd': debug++; break;
340 memset(sig, 0, sizeof sig);
341 r = readbytes(sig, sigfile, sizeof sig);
345 if ((size_t)r < sizeof sig) {
348 } else if (sigstring) {
349 memset(sig, 0, sizeof sig);
350 hexbin(sig, sigstring, sizeof sig);
354 memset(sec, 0, sizeof sec);
355 hexbin(sec, keystring, strlen(keystring));
356 ed25519_genpub(pub, sec);
358 printf("sec:\n"); hexdump(sec, sizeof sec);
360 } else if (keyfile) {
361 read_secret_key(sec, pub, keyfile);
362 //hexdump(sec, sizeof sec);
363 //hexdump(pub, sizeof pub);
366 if (mode == EXTRACT) {
367 /* want to print the pubkey only */
368 hexdump(pub, sizeof pub);
372 /* need to be able to encrypt the private keys */
374 if (mode == GENKEY) {
375 /* a private key is just 32 random bytes */
376 getrandom(sec, sizeof sec, 0);
377 ed25519_genpub(pub, sec);
378 ed25519_sign(sig, sec, pub, sec, sizeof sec);
379 //int rv = ed25519_verify(sig, pub, sec, sizeof sec);
382 fd = open(outfile, O_WRONLY|O_CREAT, 0600);
384 perror("can't open outfile");
387 write_secret_key(sec, 0, fd);
390 write_secret_key(sec, 0, 1);
394 /* set up the message data */
395 if (mode == SIGN || mode == VERIFY) {
396 messagefile = av[optind];
398 message = map(messagefile, &mlen);
402 } else if (messagestring) {
403 message = messagestring;
405 mlen = strlen(messagestring)/2;
406 message = malloc(mlen);
410 hexbin(message, messagestring, mlen*2);
412 mlen = strlen(messagestring);
432 s.signtime = time(NULL);
434 memset(s.reserved, 0, 8);
438 sha512_add(&hash, message, mlen);
439 sha512_add(&hash, (uint8_t *)&s.signtime, sizeof s.signtime);
440 sha512_add(&hash, (uint8_t *)&s.expires, sizeof s.expires);
441 sha512_add(&hash, s.info, sizeof s.info);
442 sha512_final(&hash, messagehash);
443 message = messagehash;
450 ed25519_sign(sig, sec, pub, message, mlen);
451 memcpy(s.sig, sig, sizeof sig);
452 memcpy(s.pub, pub, sizeof pub);
454 hexdump(sig, sizeof sig);
456 hexdump(&s, sizeof s);
460 if (mode == VERIFY) {
461 fprintf(stderr, "sig: ");
462 hexdump(sig, sizeof sig);
463 int rv = ed25519_verify(sig, pub, message, mlen);
465 fprintf(stderr, "verified\n");
468 fprintf(stderr, "not verified\n");
473 if (message && messagefile) {
474 munmap(message, mlen);