Index: kpw.d/db.md ================================================================== --- kpw.d/db.md +++ kpw.d/db.md @@ -10,9 +10,9 @@ each record takes the form of 1. account name length (1 byte) 2. account name - 3. password length (4 bytes) + 3. password length (1 byte) 4. password records are added simply by encrypting them with the public key and appending them to the end of the file. thus, adding a new password does not require the decryption password. Index: kpw.d/kpw.c ================================================================== --- kpw.d/kpw.c +++ kpw.d/kpw.c @@ -341,10 +341,40 @@ db = open(dbpath, flags, 0600); } return db; } + +void bytedump(uint8_t* bytes, size_t sz) { + for (size_t i = 0; i < sz; ++i) { + char tpl[] ="\x1b[35m \x1b[m"; + char* c = tpl + 5; + *c = bytes[i]; + if (*c < ' ' || *c > '~') *c='.', write(2, tpl, sz(tpl)); + else write(2, c, 1); + } +} +void hexdump(uint8_t* bytes, size_t sz) { + if(!_g_debug_msgs) return; + alert(a_debug, "printing hex dump"); + uint8_t* st = bytes; + for (size_t i = 0; i < sz; ++i) { + char hex[5] = " "; + kitoa(16, bytes[i], hex, hex + 2, NULL, true); + write(2, hex, 4); + if(!((i+1)%8)) { + write(2, _str("│ ")); + bytedump(st, 8); + write(2, "\n", 1); + st += 8; + } else if (i == sz - 1) { + write(2, _str("│ ")); + bytedump(st, (bytes + sz) - st); + write(2, "\n", 1); + } + } +} int kpw(int argc, const char** argv) { if (argc == 0) return bad_insane; _g_binary_name = argv[0]; @@ -456,28 +486,104 @@ if (e != ok) return bad_num; } else alert(a_debug, "using default password length"), len = default_pw_len; alert(a_debug, "generating new password"); - mkpw(mode, pw, len); + if (mkpw(mode, pw, len) == bad_entropy) return bad_entropy; if (print || !tty_out) { write(1, pw, len); if(tty_out) write(1, "\n", 1); } pwlen = len; } if (copy_pw) copy(pw, pwlen); - alert(a_debug, "enciphering password"); + alert(a_debug, "encoding database entry"); + uint8_t acctlen = strlen(acct); + uint8_t plaintext[1 + acctlen + + 1 + pwlen]; + plaintext[0] = acctlen; + strncpy(plaintext + 1, acct, acctlen); + plaintext[1 + acctlen] = pwlen; + strncpy(plaintext + acctlen + 2, pw, pwlen); + hexdump(plaintext, sz(plaintext)); + alert(a_debug, "enciphering database entry"); + + uint8_t ciphertext[sz(plaintext) + crypto_box_SEALBYTES]; + crypto_box_seal(ciphertext, plaintext, sz(plaintext), key); + hexdump(ciphertext, sz(ciphertext)); + + alert(a_debug, "writing ciphertext to db"); + uint8_t ciphertext_len = sz(ciphertext); + write(db, &ciphertext_len, 1); + write(db, &ciphertext, ciphertext_len); + + close(db); break; } case delpw:{ /* kpw -d */ break; } case lspw: { /* kpw -t[p] [] */ + alert(a_debug, "opening database for reading"); + int db = dbopen(O_RDONLY); + if (db == -1) return bad_db_load; + password dbpw; size_t pwlen; + bad e = pwread(!print, dbpw, &pwlen,_str("database key: ")); + + uint8_t salt [crypto_pwhash_SALTBYTES], + key [db_privkey_len], + priv_enc [db_privkey_len], + priv [db_privkey_len], + pub [db_pubkey_len]; + uint8_t salt_enc [crypto_box_SEALBYTES + sz(salt)], + salt_dec [sz(salt)]; + bzero(salt_dec, sz(salt_dec)); + + alert(a_debug, "loading public key"); + ssize_t sr = read(db, pub, sz(pub)); + if (sr != sz(pub)) return bad_db_corrupt; + hexdump(pub, sz(pub)); + + alert(a_debug, "loading password salt"); + sr = read(db, salt, sz(salt)); + if (sr != sz(salt)) return bad_db_corrupt; + hexdump(salt, sz(salt)); + + alert(a_debug, "deriving secret"); + if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, + crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + return bad_mem; + } + hexdump(key, sz(key)); + + alert(a_debug, "loading encrypted private key"); + read(db, priv_enc, sz(priv_enc)); + hexdump(priv_enc, sz(priv_enc)); + + alert(a_debug, "decrypting private key"); + for (size_t i = 0; i < sz(key); ++i) { + priv[i] = priv_enc[i] ^ key[i]; + } + hexdump(priv, sz(priv)); + + alert(a_debug, "loading verification hash"); + read(db, salt_enc, sz(salt_enc)); + hexdump(salt_enc, sz(salt_enc)); + + alert(a_debug, "decrypting verification hash"); + hexdump(pub, sz(pub)); + hexdump(priv, sz(priv)); + printf("sz salt_enc = %zu\n / crypto_box bytes = %zu", sz(salt_enc), crypto_box_SEALBYTES); + int r = crypto_box_seal_open(salt_dec, salt_enc, + sz(salt_enc), pub, priv); + printf("result code: %d\n",r); + hexdump(salt_dec, sz(salt_dec)); break; } case createdb: { /* kpw -C [] */ alert(a_debug, "creating new database"); @@ -506,11 +612,11 @@ */ alert(a_notice, "database keypair generated, encrypting"); password dbpw; size_t pwlen; - bad e = pwread(!print, dbpw, &pwlen, _str("- database passphrase: ")); + bad e = pwread(!print, dbpw, &pwlen, _str("- new database key: ")); if (e != ok) return e; if (!print && isatty(0)) { password dbpw_conf; e = pwread(!print, dbpw_conf, NULL, _str("- confirm: ")); if (e != ok) return e; @@ -521,30 +627,40 @@ uint8_t salt[crypto_pwhash_SALTBYTES], key[db_privkey_len]; uint8_t salt_enc[crypto_box_SEALBYTES + sz(salt)]; + alert(a_debug, "generating salt"); if (syscall(SYS_getrandom, salt, sz(salt), 0) == -1) return bad_entropy; + hexdump(salt, sz(salt)); + alert(a_debug, "hashing database keyphrase"); if(crypto_pwhash(key, sz(key), dbpw, pwlen, salt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) { return bad_mem; } + alert(a_debug, "encrypting private key"); + hexdump(priv, sz(priv)); for (size_t i = 0; i < sz(key); ++i) { priv_enc[i] = priv[i] ^ key[i]; } + alert(a_debug, "private key encrypted"); + hexdump(priv_enc, sz(priv_enc)); + alert(a_debug, "encrypting salt"); crypto_box_seal(salt_enc, salt, sz(salt), priv); + hexdump(salt_enc, sz(salt_enc)); /* we have everything we need. now we create the * file, failing if it already exists so as not * to clobber anyone's passwords. */ + alert(a_debug, "creating new database on disk"); int db; const int flags = O_CREAT | O_WRONLY | (clobber ? O_TRUNC : O_EXCL); if(param == 0) db = dbopen(flags); @@ -556,10 +672,13 @@ /* the file is safely created; all that's left to * do is write out the header and then we can call * it a day. */ + alert(a_debug, "writing public key"); + hexdump(pub, sz(pub)); + write(db, pub, sz(pub)); write(db, salt, sz(salt)); write(db, priv_enc, sz(priv_enc)); write(db, salt_enc, sz(salt_enc)); close(db); @@ -567,17 +686,10 @@ alert(a_debug, "database created"); break; } } - - - /* char buf[len+1]; */ - /* *buf = 0; */ - /* mkpw(mode,buf,len); */ - /* write(1, buf, len + 1); */ - # ifdef _CLIPBOARD if (copy_pw) { /* copy(buf,len); */ } # endif