/* * Secure eXtended Message Passing framework * Secure eXtended Transport layer implementation: (libsxt) * - very similar to SSH2/TLS * - using already proven and tested crypto algos * - better than TLS for message passing * * PublicPrivateKeyPairs operation API * * (c) Alexander Vdolainen 2016 * * libsxmp is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libsxmp is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see ."; * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #define blub(a) printf("%s:%d -> %d\n", __FUNCTION__, __LINE__, (a)) #endif /* locals */ static int sxtkey_export_priv_ed25519(const sxtkey_t *key, const char *pass, sxtsafebuffer_t **bin); static int sxt_public_ed25519_2sb(const sxtkey_t *key, sxtsafebuffer_t **buf); static int sxt_public_ed25519_2rdb(const sxtkey_t *key, ndbuf_t **out); static int _sxtkey_check_fmtrawbuf(const char *buffer); static int _sxtkey_privkey_decrypt(ndbuf_t *privkey, ndbuf_t *kdfopt, const char *passkey); int sxtkey_generate(sxtkey_t *key, int type, int opt) { int r = 0; if(!key) return SXT_EINVAL; /* set defaults */ key->type = type; key->flags = SXT_PPKP_PRIVATE | SXT_PPKP_PUBLIC; switch(type) { case PPKP_ED25519: key->pubkey = malloc(sizeof(ed25519_pubkey)); if(!key->pubkey) { r = SXT_ENOMEM; goto __fall; } key->privkey = malloc(sizeof(ed25519_privkey)); if(!key->privkey) { r = SXT_ENOMEM; goto __safefall0; } r = crypto_sign_ed25519_keypair(*key->pubkey, *key->privkey); if(r) { r = SXT_ECRYPTO; goto __safefall0; } break; default: return SXT_EINVAL; } if(!r) return SXT_SUCCESS; __safefall0: if(key->pubkey) free(key->pubkey); if(key->privkey) free(key->privkey); __fall: return r; } sxtkey_t *sxtkey_alloc(void) { sxtkey_t *key = malloc(sizeof(sxtkey_t)); if(!key) return NULL; else memset(key, 0, sizeof(sxtkey_t)); return key; } void sxtkey_burn(sxtkey_t *key) { if(!key) return; key->priv = NULL; switch(key->type) { case PPKP_ED25519: if(key->pubkey) memset(key->pubkey, 0, sizeof(ed25519_pubkey)); if(key->privkey) memset(key->privkey, 0, sizeof(ed25519_privkey)); break; default: return; } key->type = 0; key->flags = 0; return; } void sxtkey_free(sxtkey_t *key) { if(!key) return; switch(key->type) { case PPKP_ED25519: if(key->pubkey) free(key->pubkey); if(key->privkey) free(key->privkey); break; default: return; /* cannot free unrecognized key due to the * potential memleak */ } free(key); return; } const char *sxtkey_name(int type) { switch(type) { case PPKP_ED25519: return "ppkp-ed25519"; default: return NULL; } return NULL; } uint8_t sxtkey_type_fname(const char *name) { if(!name) return 0; if(!strcmp(name, "ppkp-ed25519")) return PPKP_ED25519; else return 0; } int sxtkey_public(const sxtkey_t *key) { if(!key) return 0; return (key->flags & SXT_PPKP_PUBLIC) == SXT_PPKP_PUBLIC; } int sxtkey_private(const sxtkey_t *key) { if(!key) return 0; return (key->flags & SXT_PPKP_PRIVATE) == SXT_PPKP_PRIVATE; } int sxtkey_assign_hash(sxtkey_t *key, uint64_t hash) { if(!key) return SXT_EINVAL; // if(!sxtkey_private(key)) return SXT_EINVAL; //if(key->hash) return SXT_EKEY; key->hash = hash; return SXT_SUCCESS; } uint64_t sxtkey_hash(const sxtkey_t *key) { if(!key) return 0; return key->hash; } /* will duplicate a key depends on it's kind */ int sxtkey_dup(const sxtkey_t *key, sxtkey_t **dest) { int r = SXT_EKEY; if(!key) return SXT_EINVAL; if(sxtkey_public(key)) return sxtkey_dup_public(key, dest); else if(sxtkey_private(key)) return sxtkey_dup_private(key, dest); else return sxtkey_dup_private(key, dest); /* never riched */ return r; } /* will duplicate public key, if key was private or pair - it becomes public */ int sxtkey_dup_public(const sxtkey_t *key, sxtkey_t **dest) { sxtkey_t *nkey = NULL; int r = SXT_SUCCESS; if(!key) return SXT_EINVAL; if(!sxtkey_private(key)) return SXT_EKEY; if(!(nkey = sxtkey_alloc())) return SXT_ENOMEM; else nkey->flags = 0; switch(key->type) { case PPKP_ED25519: /* actually the same still, but in case of other key types will differ */ /* check first */ if(!key->pubkey || !key->privkey) { r = SXT_EKEY; goto __failed; } /* allocate new values */ if(!(nkey->pubkey = malloc(ED25519_PK_LEN))) { r = SXT_ENOMEM; goto __failed; } if(!(nkey->privkey = malloc(ED25519_SK_LEN))) { free(nkey->pubkey); r = SXT_ENOMEM; goto __failed; } /* copy that */ memcpy(nkey->pubkey, key->pubkey, ED25519_PK_LEN); memcpy(nkey->privkey, key->privkey, ED25519_SK_LEN); break; default: /* don't now about it */ sxtkey_free(nkey); return SXT_EKEY; } /* copy other values */ nkey->flags |= SXT_PPKP_PUBLIC; nkey->type = key->type; nkey->hash = key->hash; nkey->priv = key->priv; *dest = nkey; return r; __failed: sxtkey_free(nkey); return r; } /* will duplicate private key, if key public error will returns */ int sxtkey_dup_private(const sxtkey_t *key, sxtkey_t **dest) { sxtkey_t *nkey = NULL; int r = SXT_SUCCESS; if(!key) return SXT_EINVAL; //if(sxt_key_public(key)) return SXT_EKEY; /* cannot duplicate private key from public */ if(!(nkey = sxtkey_alloc())) return SXT_ENOMEM; else nkey->flags = 0; switch(key->type) { case PPKP_ED25519: /* check first */ if(!key->pubkey || !key->privkey) { r = SXT_EKEY; goto __failed; } /* allocate new values */ if(!(nkey->pubkey = malloc(ED25519_PK_LEN))) { r = SXT_ENOMEM; goto __failed; } if(!(nkey->privkey = malloc(ED25519_SK_LEN))) { free(nkey->pubkey); r = SXT_ENOMEM; goto __failed; } /* copy that */ memcpy(nkey->pubkey, key->pubkey, ED25519_PK_LEN); memcpy(nkey->privkey, key->privkey, ED25519_SK_LEN); break; default: /* don't now about it */ sxtkey_free(nkey); return SXT_EKEY; } /* copy other values */ nkey->flags = SXT_PPKP_PRIVATE; nkey->type = key->type; nkey->hash = key->hash; nkey->priv = key->priv; *dest = nkey; return r; __failed: sxtkey_free(nkey); return r; } int sxtkey_import_priv_file(const char *file, const char *passkey, int (*ask_passkey)(char *pkbuf, size_t length, int confirm, void *priv), void *priv, sxtkey_t **ik) { FILE *stream = NULL; char *rawfbuf = NULL, *tbuf, *tebuf, *tuple = NULL, *b64pk = NULL; struct stat stbuf; uint8_t keytype = 0; int r = SXT_SUCCESS, tcc = 0, len; *ik = NULL; /* check input file */ if(!file) return SXT_EINVAL; if(stat(file, &stbuf) != 0) return SXT_EIO; else if(!S_ISREG(stbuf.st_mode)) return SXT_EINVAL; if(stbuf.st_size >= SXT_PPKP_MAXCSIZE) return SXT_EINVAL; if((stream = fopen(file, "r")) == NULL) return SXT_EIO; if(!(rawfbuf = malloc(stbuf.st_size + sizeof(char)))) goto __failed; else rawfbuf[stbuf.st_size] = '\0'; if(fread(rawfbuf, stbuf.st_size, 1, stream) != 1) { r = SXT_EIO; goto __failed; } /* check general format */ r = _sxtkey_check_fmtrawbuf(rawfbuf); if(r != SXT_SUCCESS) goto __failed; /* now we need to get a keytype and decode b64blob */ for(tbuf = rawfbuf, tcc = 0, tebuf = NULL; *tbuf != '\0'; tbuf++) { switch(tcc) { case 0: /* just entering */ if(*tbuf == '(') tcc++; break; case 1: /* first e.g. key type plain text description */ if(!tebuf) tebuf = tbuf; else if(tebuf && *tbuf == ' ') { len = (tbuf - tebuf) + sizeof(char); if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); /* check keytype */ memcpy(tuple, tebuf, len - sizeof(char)); keytype = sxtkey_type_fname(tuple); free(tuple); if(keytype == 0) { r = SXT_EKEY; /* unsupported key type */ goto __failed; } /* going further */ tebuf = NULL; tcc++; } break; case 2: /* private key contents */ if(!tebuf && *tbuf != ' ') tebuf = tbuf; else if(tebuf && *tbuf == '"') { tebuf++; len = (tbuf - tebuf) + sizeof(char); if(len <= 1) { r = SXT_EKEYFMT; goto __failed; } if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); memcpy(tuple, tebuf, len - sizeof(char)); b64pk = tuple; tuple = tebuf = NULL; tcc++; } break; case 3: if(*tbuf == ')') tcc++; else if(*tbuf != ' ') { r = SXT_EKEYFMT; goto __failed; } break; default: break; } } if(tcc < 3) { r = SXT_EKEYFMT; goto __failed; } r = sxtkey_privkey_import_fbase64blob(b64pk, keytype, ask_passkey, priv, ik); __failed: if(stream) fclose(stream); if(rawfbuf) free(rawfbuf); if(b64pk) free(b64pk); return r; } int sxtkey_privkey_import_rdbuf(ndbuf_t *buf, uint8_t keytype, int (*ask_passkey)(char *pkbuf, size_t length, int confirm, void *priv), void *priv, sxtkey_t **ik) { uint8_t flags = 0, chk = 0; char *passkey = NULL; ndbuf_t *kdfopt = NULL, *ctrl_pubkey = NULL, *privkey = NULL; ndbuf_t *ed25519_pubbuf = NULL, *ed25519_privbuf = NULL; sxtkey_t *key = NULL; char *keyname = NULL, *ciphername = NULL, *kdfname = NULL, *magic = NULL; uint64_t hash = 0; uint32_t r1 = 0, r2 = 0, ctrl_pklen = 0; int r = SXT_SUCCESS, decrypt = 0; if(!buf) return SXT_EINVAL; if(!ndbuf_length(buf)) return SXT_EINVAL; if(keytype == 0) return SXT_EKEY; r = ndbuf_escan(buf, "sbsssRdRR", &magic, &flags, &keyname, &ciphername, &kdfname, &kdfopt, &ctrl_pklen, &ctrl_pubkey, &privkey); if(r != SXT_SUCCESS) goto __failed; if(strcmp(PPKP_MAGIC, magic)) { /* magic should be right */ __eekey: r = SXT_EKEY; goto __failed; } if(strcmp(sxtkey_name(keytype), keyname)) goto __eekey; /* check for auth function */ if(flags & SXT_PPKP_ENCRYPT) decrypt++; if(decrypt && !ask_passkey) { r = SXT_EAUTH; goto __failed; } switch(keytype) { case PPKP_ED25519: /* check public key length first */ if(ctrl_pklen != ED25519_PK_LEN + sizeof(uint32_t)) { r = SXT_EKEY; goto __failed; } /* check for encryption details */ if(decrypt) { if(strcmp(ciphername, "aes128-cbc") || strcmp(kdfname, "bcrypt")) { r = SXT_EKEY; goto __failed; } /* get passkey */ passkey = malloc(64*sizeof(char)); if(!passkey) { r = SXT_ENOMEM; goto __failed; } r = ask_passkey(passkey, 63, 0, priv); if(r != SXT_SUCCESS) { /* FIXME: might be return SXT_EAUTH here ? */ goto __failed; } /* decrypt it */ r = _sxtkey_privkey_decrypt(privkey, kdfopt, passkey); if(r != SXT_SUCCESS) goto __failed; } else { /* nil expected */ if(strcmp(ciphername, "nil") || strcmp(kdfname, "nil")) { r = SXT_EKEY; goto __failed; } } /* here we have privkey decrypted */ r = ndbuf_escan(privkey, "ddRRqb", &r1, &r2, &ed25519_pubbuf, &ed25519_privbuf, &hash, &chk); if(r != SXT_SUCCESS) { hash = 0; chk = 0; r = SXT_EKEY; goto __failed; } /* control check for random numbers */ if(r1 != r2) goto __invalid; /* control check of public keys */ if(ndbuf_cmp(ctrl_pubkey, ed25519_pubbuf)) { __invalid: ndbuf_free(ed25519_pubbuf); ndbuf_free(ed25519_privbuf); r = SXT_EKEY; goto __failed; } /* alloc a key and copy data */ if(!(key = sxtkey_alloc())) { __enomem: ndbuf_free(ed25519_pubbuf); ndbuf_free(ed25519_privbuf); r = SXT_ENOMEM; goto __failed; } /* allocate space */ if(!(key->pubkey = malloc(ED25519_PK_LEN)) || !(key->privkey = malloc(ED25519_SK_LEN))) { if(key->pubkey) free(key->pubkey); if(key->privkey) free(key->privkey); sxtkey_free(key); goto __enomem; } /* copy and set all required data */ memcpy(key->pubkey, ndbuf_rdata(ed25519_pubbuf), ED25519_PK_LEN); memcpy(key->privkey, ndbuf_rdata(ed25519_privbuf), ED25519_SK_LEN); /* free it */ ndbuf_free(ed25519_pubbuf); ndbuf_free(ed25519_privbuf); key->type = keytype; key->hash = hash; key->flags = SXT_PPKP_PRIVATE; /* and the rest */ *ik = key; r = SXT_SUCCESS; break; default: /* unsupported key type ... */ r = SXT_EKEY; goto __failed; break; } __failed: /* free temproary stuff */ if(magic) free(magic); if(keyname) free(keyname); if(ciphername) free(ciphername); if(kdfname) free(kdfname); if(passkey) { memset(passkey, 0, 64); free(passkey); } if(kdfopt) ndbuf_free(kdfopt); if(ctrl_pubkey) ndbuf_free(ctrl_pubkey); if(privkey) ndbuf_free(privkey); return r; } int sxtkey_privkey_import_fbase64blob(const char *b64pk, uint8_t keytype, int (*ask_passkey)(char *pkbuf, size_t length, int confirm, void *priv), void *priv, sxtkey_t **ik) { ndbuf_t *fullkeybuf = NULL; char *decoded = NULL; int len, r = SXT_SUCCESS; if(!b64pk) return SXT_EINVAL; else len = strlen(b64pk); if(keytype == 0) return SXT_EKEY; if(!len) return SXT_EINVAL; if(!(decoded = malloc(len + sizeof(char)))) return SXT_ENOMEM; /* decode */ len = sxt_b64decode_in(b64pk, len, decoded, len); if(!len) { r = SXT_EKEYFMT; goto __failed; } /* allocate a buffer and insert raw data */ if(!(fullkeybuf = ndbuf_new())) { r = SXT_ENOMEM; goto __failed; } else ndbuf_setflags(fullkeybuf, NDBUF_BURN); ndbuf_write_raw_head(fullkeybuf, decoded, len); r = sxtkey_privkey_import_rdbuf(fullkeybuf, keytype, ask_passkey, priv, ik); __failed: if(decoded) { memset(decoded, 0, len); free(decoded); } if(fullkeybuf) ndbuf_free(fullkeybuf); return r; } int sxtkey_pubkey_import_fbase64blob(const char *b64b, uint8_t keytype, uint64_t ctrl_hash, sxtkey_t **ik) { sxtkey_t *key = NULL; ndbuf_t *keybuf = NULL; char *blob = NULL, *ed25519pk = NULL; uint64_t hash; uint32_t klen = 0; int len, r = SXT_SUCCESS; if(!b64b) return SXT_EINVAL; if(!sxtkey_name(keytype)) return SXT_EKEY; /* ok, first decode */ len = strlen(b64b) + sizeof(char); if(!(blob = malloc(len))) return SXT_ENOMEM; else memset(blob, 0, len); len = sxt_b64decode_in(b64b, len - 1, blob, len); if(!(keybuf = ndbuf_new())) { r = SXT_ENOMEM; goto __failed; } switch(keytype) { case PPKP_ED25519: if(!(ed25519pk = malloc(ED25519_PK_LEN))) { r = SXT_ENOMEM; goto __failed; } /* */ ndbuf_write_raw(keybuf, blob, len); ndbuf_resetcur(keybuf); ndbuf_escan(keybuf, "dpq", &klen, ED25519_PK_LEN, ed25519pk, &hash); if(klen != ED25519_PK_LEN) { r = SXT_EKEY; goto __failed; } if(hash != ctrl_hash) { r = SXT_EKEY; goto __failed; } if(!(key = sxtkey_alloc())) { r = SXT_ENOMEM; goto __failed; } key->type = keytype; if(!(key->pubkey = malloc(ED25519_PK_LEN))) { sxtkey_free(key); r = SXT_ENOMEM; goto __failed; } key->hash = hash; key->flags = SXT_PPKP_PUBLIC; memcpy(key->pubkey, ed25519pk, ED25519_PK_LEN); r = SXT_SUCCESS; *ik = key; break; default: r = SXT_EKEYFMT; break; } __failed: if(blob) free(blob); if(keybuf) ndbuf_free(keybuf); if(ed25519pk) free(ed25519pk); return r; } int sxtkey_import_public_file(const char *file, sxtkey_t **ik) { FILE *stream = NULL; char *rawfbuf = NULL, *tbuf, *tebuf, *tuple = NULL, *b64pk = NULL; ndbuf_t *chbuf = NULL; int tc = 0, tcc = 0, len; struct stat stbuf; int r = SXT_ENOMEM; uint64_t ctrl_hash; uint8_t keytype = 0; *ik = NULL; if(!file) return SXT_EINVAL; if(stat(file, &stbuf) != 0) return SXT_EIO; else if(!S_ISREG(stbuf.st_mode)) return SXT_EINVAL; if(stbuf.st_size >= SXT_PPKP_MAXCSIZE) return SXT_EINVAL; if((stream = fopen(file, "r")) == NULL) return SXT_EIO; if(!(rawfbuf = malloc(stbuf.st_size + sizeof(char)))) goto __failed; else rawfbuf[stbuf.st_size] = '\0'; if(fread(rawfbuf, stbuf.st_size, 1, stream) != 1) { r = SXT_EIO; goto __failed; } /* check general format */ r = _sxtkey_check_fmtrawbuf(rawfbuf); if(r != SXT_SUCCESS) goto __failed; /* ok, now we can parse it, avoid to use libsexpr here, * to make sxt inpedendent as possible */ for(tbuf = rawfbuf, tc = 0, tcc = 0, tebuf = NULL; *tbuf != '\0'; tbuf++) { switch(tcc) { case 0: /* just entering */ if(*tbuf == '(') tcc++; break; case 1: /* first e.g. key type plain text description */ if(!tebuf) tebuf = tbuf; else if(tebuf && *tbuf == ' ') { if(!tebuf) goto __failed; len = (tbuf - tebuf) + sizeof(char); if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); /* check keytype */ memcpy(tuple, tebuf, len - sizeof(char)); keytype = sxtkey_type_fname(tuple); free(tuple); if(keytype == 0) { r = SXT_EKEY; /* unsupported key type */ goto __failed; } /* going further */ tebuf = NULL; tcc++; } break; case 2: /* public sign keyword */ if(!tebuf && *tbuf != ' ') tebuf = tbuf; else if(tebuf && *tbuf == ' ') { if(*tebuf != '\'') { /* invalid format */ r = SXT_EKEYFMT; goto __failed; } /* get tuple */ len = (tbuf - tebuf) + sizeof(char); if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); memcpy(tuple, tebuf, len - sizeof(char)); if(strcmp(tuple, "'public")) tc = SXT_EKEYFMT; else tc = 0; free(tuple); if(tc) { r = tc; goto __failed; } tebuf = NULL; tcc++; } break; case 3: /* key contents */ if(!tebuf && *tbuf != ' ') tebuf = tbuf; else if(tebuf && *tbuf == ' ') { len = (tbuf - tebuf) + sizeof(char); /* check format */ if(*tebuf != '"') tc = SXT_EKEYFMT; if(!tc && tebuf[len - 2] != '"') tc = SXT_EKEYFMT; len -= 2; if(tc || len <= 0) { r = tc; goto __failed; } tebuf++; if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); memcpy(tuple, tebuf, len - sizeof(char)); b64pk = tuple; tuple = tebuf = NULL; tcc++; } break; case 4: /* base64 encoded hash to verify contents */ if(!tebuf && *tbuf == '"') tebuf = tbuf; else if(tebuf && *tbuf == '"') { tebuf++; len = (tbuf - tebuf) + sizeof(char); if(len <= 1) tc = SXT_EKEYFMT; if(tc) {r = tc; goto __failed;} if(!(tuple = malloc(len))) { r = SXT_ENOMEM; goto __failed; } else memset(tuple, 0, len); memcpy(tuple, tebuf, len - 1); /* decode hash */ tebuf = NULL; if(!(tebuf = malloc(len))) { free(tuple); r = SXT_ENOMEM; goto __failed; } else memset(tebuf, 0, len); len = sxt_b64decode_in(tuple, strlen(tuple), tebuf, len); free(tuple); if(!(chbuf = ndbuf_new())) { free(tebuf); r = SXT_ENOMEM; goto __failed; } ndbuf_write_raw(chbuf, tebuf, len); ndbuf_resetcur(chbuf); ndbuf_read_u64(chbuf, &ctrl_hash); ndbuf_free(chbuf); free(tebuf); tebuf = tuple = NULL; tcc++; } break; case 5: if(*tbuf == ')') tcc++; else if(*tbuf != ' ') { r = SXT_EKEYFMT; goto __failed; } break; default: break; } } /* ok check fmt last time */ if(tcc < 6) { r = SXT_EKEYFMT; goto __failed; } /* import from base64 encoded blob */ r = sxtkey_pubkey_import_fbase64blob(b64pk, keytype, ctrl_hash, ik); __failed: if(stream) fclose(stream); if(rawfbuf) free(rawfbuf); if(b64pk) free(b64pk); return r; } int sxtkey_export_priv_file(const sxtkey_t *key, const char *file, const char *passkey, int (*ask_passkey)(char *pkbuf, size_t length, int confirm, void *priv), void *priv) { FILE *out = NULL; char *pass = NULL; sxtsafebuffer_t *bin = NULL; int r = 0; if(!key) return SXT_EINVAL; if(!sxtkey_private(key)) return SXT_EKEY; if((out = fopen(file, "wb")) == NULL) return SXT_EIO; /* passkey challenge */ if(passkey) pass = (char *)passkey; else if(ask_passkey) { if((pass = malloc(sizeof(char)*64)) == NULL) { r = SXT_ENOMEM; goto __failed; } r = ask_passkey(pass, 64, 1, priv); if(r != SXT_SUCCESS) goto __failed; } /* ok, get the base64 encoded data for the key */ switch(key->type) { case PPKP_ED25519: r = sxtkey_export_priv_ed25519(key, pass, &bin); if(!bin) goto __failed; break; default: r = SXT_EKEY; goto __failed; } /* sxt key container as follows: ( "")\n */ fprintf(out, "(%s \"%s\")\n", sxtkey_name(key->type), (char *)sxtsafebuffer_getdata(bin)); sxtsafebuffer_destroy(bin); __failed: if(pass && pass != passkey) { memset(pass, 0, sizeof(char)*64); /* clean to disallow passkeys found in coredumps */ free(pass); } fclose(out); return r; } static int sxtkey_publickey2blob_whash(const sxtkey_t *key, sxtsafebuffer_t **o) { sxtsafebuffer_t *buf = NULL, *pk = NULL; ndbuf_t *b = NULL; int r = SXT_SUCCESS; if(!key) return SXT_EINVAL; /* get a public key blob without hash */ switch(key->type) { case PPKP_ED25519: if((r = sxt_public_ed25519_2sb(key, &pk)) != SXT_SUCCESS) return r; break; default: return SXT_EKEY; } if(!(b = ndbuf_new())) { r = SXT_ENOMEM; goto __failed; } ndbuf_print(b, "pq", sxtsafebuffer_length(pk), sxtsafebuffer_getdata(pk), key->hash); if(!(buf = sxtsafebuffer_new(ndbuf_length(b)))) { r = SXT_ENOMEM; goto __failed; } memcpy(sxtsafebuffer_getdata(buf), ndbuf_rdata(b), ndbuf_length(b)); *o = buf; __failed: if(pk) sxtsafebuffer_destroy(pk); if(b) ndbuf_free(b); return r; } static int sxtkey_hash2blob(const sxtkey_t *key, sxtsafebuffer_t **o) { sxtsafebuffer_t *buf = NULL; ndbuf_t *b = NULL; int r = SXT_SUCCESS; if(!key) return SXT_EINVAL; if(!(b = ndbuf_new())) return SXT_ENOMEM; ndbuf_print(b, "q", key->hash); if(!(buf = sxtsafebuffer_new(ndbuf_length(b)))) { r = SXT_ENOMEM; goto __failed; } memcpy(sxtsafebuffer_getdata(buf), ndbuf_rdata(b), ndbuf_length(b)); *o = buf; __failed: if(b) ndbuf_free(b); return r; } int sxtkey_export_public_file(const sxtkey_t *key, const char *file) { FILE *out = NULL; sxtsafebuffer_t *hashbuf = NULL, *pubkeybuf = NULL; sxtsafebuffer_t *pubkey_sb = NULL, *hash_sb = NULL; char *pckey = NULL, *pchash = NULL; int r = SXT_SUCCESS; if(!key) return SXT_EINVAL; if(!sxtkey_public(key)) return SXT_EKEY; if((out = fopen(file, "wb")) == NULL) return SXT_EIO; /* * format described as follows: * ( 'public "" "")\n * all scoped values are base64 encoded */ /* get a public key with hash blob */ if((r = sxtkey_publickey2blob_whash(key, &pubkey_sb)) != SXT_SUCCESS) goto __failed; /* get key hash blob */ if((r = sxtkey_hash2blob(key, &hash_sb)) != SXT_SUCCESS) goto __failed; /* allocate buffers for base64 encoded data */ if(!(pubkeybuf = sxtsafebuffer_new(sxt_rawlen2b64len(sxtsafebuffer_length(pubkey_sb))))) { r = SXT_ENOMEM; goto __failed; } if(!(hashbuf = sxtsafebuffer_new(sxt_rawlen2b64len(sxtsafebuffer_length(hash_sb))))) { r = SXT_ENOMEM; goto __failed; } /* do base64 encode */ sxt_b64encode_in(sxtsafebuffer_getdata(pubkey_sb), sxtsafebuffer_getdata(pubkeybuf), sxtsafebuffer_length(pubkey_sb)); /* pubkey */ sxt_b64encode_in(sxtsafebuffer_getdata(hash_sb), sxtsafebuffer_getdata(hashbuf), sxtsafebuffer_length(hash_sb)); /* hash */ /* get cstring to output */ if(!(pckey = sxtsafebuffer_getcstr(pubkeybuf))) { r = SXT_ENOMEM; goto __failed; } if(!(pchash = sxtsafebuffer_getcstr(hashbuf))) { r = SXT_ENOMEM; sxtsafebuffer_freecstr(pckey); goto __failed; } /* output this */ fprintf(out, "(%s 'public \"%s\" \"%s\")\n", sxtkey_name(key->type), pckey, pchash); /* free all stuff */ sxtsafebuffer_freecstr(pckey); sxtsafebuffer_freecstr(pchash); __failed: if(pubkey_sb) sxtsafebuffer_destroy(pubkey_sb); if(hash_sb) sxtsafebuffer_destroy(hash_sb); if(hashbuf) sxtsafebuffer_destroy(hashbuf); if(pubkeybuf) sxtsafebuffer_destroy(pubkeybuf); fclose(out); return r; } static int sxtkey_export_priv_ed25519(const sxtkey_t *key, const char *pass, sxtsafebuffer_t **bin) { int r = 0; sxtsafebuffer_t *b64 = NULL, *pubkey_sb = NULL; ndbuf_t *keybuf = NULL, *privbuf = NULL; ndbuf_t *kdfopts = NULL; uint32_t urand; uint8_t flags = 0; if(!key) return SXT_EINVAL; if(key->type != PPKP_ED25519) return SXT_EKEY; /* allocate a buffer for the key at all */ if(!(keybuf = ndbuf_new())) return SXT_ENOMEM; else ndbuf_setflags(keybuf, NDBUF_BURN); /* ok firstly get private-key-data according to SXT key container doc */ if((r = sxt_public_ed25519_2sb(key, &pubkey_sb)) != SXT_SUCCESS) goto __failed; /* now we need random number */ sxt_get_random(&urand, sizeof(uint32_t), 1); if(!(privbuf = ndbuf_new())) { r = SXT_ENOMEM; goto __failed; } else ndbuf_setflags(privbuf, NDBUF_BURN); /* two random numbers */ if(!ndbuf_print(privbuf, "dd", urand, urand)) { r = SXT_ENOMEM; goto __failed; } /* pub, priv, hash, zero padding */ if(!ndbuf_print(privbuf, "dpdpqb", (uint32_t)ED25519_PK_LEN, (size_t)ED25519_PK_LEN, key->pubkey, (uint32_t)ED25519_SK_LEN, (size_t)ED25519_SK_LEN, key->privkey, key->hash, (uint8_t)'\0')) { r = SXT_ENOMEM; goto __failed; } /* if hash is non-zero - include it i.e. set flag */ if(key->hash) flags |= SXT_PPKP_IHASH; if(pass) { /* we need to encrypt the key i.e. privbuf *must* be encrypted */ /* to avoid yet another wheel invention, * the following was taken from ssh implementation, i mean * kdf and crypto in use to encrypt the key */ uint8_t pad = 1; size_t keystub_len; sxtsafebuffer_t *salt; sxt_cipher_t *cipher = sxt_cipher_get("aes128-cbc"); uint8_t keystub[128]; /* set flags */ flags |= SXT_PPKP_ENCRYPT; if(!cipher) { /* it might be in case of forgotten initialization function */ r = SXT_EKEY; goto __failed; } /* get the salt */ if(!(salt = sxtsafebuffer_new(16))) { __enomem1: sxt_cipher_free(cipher); r = SXT_ENOMEM; goto __failed; } else sxt_get_random(sxtsafebuffer_getdata(salt), 16, 1); /* generate kdf description */ if(!(kdfopts = ndbuf_new())) { sxtsafebuffer_destroy(salt); goto __enomem1; } ndbuf_print(kdfopts, "Pd", 16, sxtsafebuffer_getdata(salt), (uint32_t)32); /* ok, now we need to get a padding, just to be sure to be aligned for the cipher block size */ while(ndbuf_length(privbuf) % cipher->blksize != 0) { if(ndbuf_write_u8(privbuf, pad) != sizeof(uint8_t)) { sxt_cipher_free(cipher); r = SXT_ENOMEM; goto __failed; } pad++; } /* key stub */ keystub_len = cipher->keysize/8 + cipher->blksize; if(keystub_len > sizeof(keystub)) { r = SXT_EKEY; sxt_cipher_free(cipher); goto __failed; } /* bcrypt those stuff */ if(bcrypt_pbkdf(pass, strlen(pass), sxtsafebuffer_getdata(salt), sxtsafebuffer_length(salt), keystub, keystub_len, 32)) { r = SXT_ERROR; sxt_cipher_free(cipher); goto __failed; } /* encrypt with cipher */ sxt_cipher_set_encrypt_key(cipher, keystub, keystub + cipher->keysize/8); sxt_cipher_encrypt(cipher, ndbuf_rdata(privbuf), ndbuf_rdata(privbuf), ndbuf_length(privbuf)); /* clean up all */ memset(keystub, 0, sizeof(keystub)); sxt_cipher_free(cipher); sxtsafebuffer_destroy(salt); } /* in case of non-crypted key we anyways should include encrypt options */ if(!kdfopts) { if(!(kdfopts = ndbuf_new())) { r = SXT_ENOMEM; goto __failed; } ndbuf_print(kdfopts, "Pd", 3, "nil", (uint32_t)0); } /* there are a time to print all required content to the buffer */ /* <8bit flags> * * * * according to the documentation */ ndbuf_print(keybuf, "sbsssPPP", PPKP_MAGIC, flags, "ppkp-ed25519", pass ? "aes128-cbc" : "nil", pass ? "bcrypt" : "nil", ndbuf_length(kdfopts), ndbuf_rdata(kdfopts), sxtsafebuffer_length(pubkey_sb), sxtsafebuffer_getdata(pubkey_sb), ndbuf_length(privbuf), ndbuf_rdata(privbuf)); /* as described - encode all with base64 */ b64 = sxtsafebuffer_new(sxt_rawlen2b64len(ndbuf_length(keybuf))); if(!b64) { r = SXT_ENOMEM; goto __failed; } /* encode buffer */ sxt_b64encode_in((const char *)ndbuf_rdata(keybuf), (char *)sxtsafebuffer_getdata(b64), ndbuf_length(keybuf)); /* the last ... */ *bin = b64; r = SXT_SUCCESS; __failed: if(keybuf) ndbuf_free(keybuf); /* container free */ if(pubkey_sb) sxtsafebuffer_destroy(pubkey_sb); /* public key bin free */ if(privbuf) ndbuf_free(privbuf); /* private key buffer free */ return r; } static int sxt_public_ed25519_2sb(const sxtkey_t *key, sxtsafebuffer_t **buf) { ndbuf_t *rbuf = NULL; sxtsafebuffer_t *o = NULL; int r = SXT_SUCCESS; if(!key->pubkey) return SXT_EKEY; r = sxt_public_ed25519_2rdb(key, &rbuf); if(!rbuf) return r; if(!(o = sxtsafebuffer_new((size_t)ndbuf_length(rbuf)))) { r = SXT_ENOMEM; goto __failed; } sxtsafebuffer_setdata(o, ndbuf_rdata(rbuf), (size_t)ndbuf_length(rbuf)); *buf = o; __failed: ndbuf_free(rbuf); return r; } static int sxt_public_ed25519_2rdb(const sxtkey_t *key, ndbuf_t **out) { ndbuf_t *buf = NULL; if(!key->pubkey) return SXT_EKEY; if(!(buf = ndbuf_new())) return SXT_ENOMEM; else ndbuf_setflags(buf, NDBUF_BURN); if(!ndbuf_print(buf, "dp", (uint32_t)ED25519_PK_LEN, (size_t)ED25519_PK_LEN, key->pubkey)) { ndbuf_free(buf); return SXT_EKEY; } *out = buf; return SXT_SUCCESS; } static int _sxtkey_check_fmtrawbuf(const char *buffer) { char *tbuf; int r = SXT_SUCCESS, tc = 0, tcc = 0; /* currently only own container supported, * let's checkout format: * = () and "" */ for(tbuf = (char *)buffer; *tbuf != '\0'; tbuf++) { switch(*tbuf) { case '(': tc++; break; case ')': tc--; break; case '"': if(!tc) { r = SXT_EKEYFMT; goto __failed; } else tcc++; break; default: break; /* just ignore */ } } r = SXT_EKEYFMT; if(tc) goto __failed; if(tcc%2) goto __failed; r = SXT_SUCCESS; __failed: return r; } static int _sxtkey_privkey_decrypt(ndbuf_t *privkey, ndbuf_t *kdfopt, const char *passkey) { int r = SXT_SUCCESS; ndbuf_t *salt = NULL; sxt_cipher_t *cipher = sxt_cipher_get("aes128-cbc"); uint32_t rounds, keystub_len; uint8_t keystub[128]; /* default check */ if(!privkey || !kdfopt) return SXT_EINVAL; if(!passkey) return SXT_EAUTH; /* cipher */ if(!(cipher = sxt_cipher_get("aes128-cbc"))) return SXT_EKEY; /* get salt and rounds */ if((r = ndbuf_escan(kdfopt, "Rd", &salt, &rounds)) != SXT_SUCCESS) goto __failed; /* check blob length and cipher block size */ if(ndbuf_length(privkey) % cipher->blksize != 0) { r = SXT_EKEY; goto __failed; } /* bcrypt */ keystub_len = cipher->keysize/8 + cipher->blksize; if(keystub_len > sizeof(keystub)) { r = SXT_EKEY; goto __failed; } if(bcrypt_pbkdf(passkey, strlen(passkey), ndbuf_rdata(salt), ndbuf_length(salt), keystub, keystub_len, rounds) < 0) { memset(keystub, 0, keystub_len); r = SXT_EAUTH; goto __failed; } /* set decrypt key and decrypt */ cipher->f->set_decrypt_key(cipher, keystub, keystub + cipher->keysize/8); cipher->f->decrypt(cipher, ndbuf_rdata(privkey), ndbuf_rdata(privkey), ndbuf_length(privkey)); /* free/burn unused data */ memset(keystub, 0, keystub_len); __failed: if(cipher) sxt_cipher_free(cipher); if(salt) ndbuf_free(salt); return r; }