1
0
mirror of https://github.com/biergaizi/codecrypt synced 2024-06-16 03:48:19 +00:00

implement private key locking

Included:
- gazillion changes in actions
- keyring decodes privkey structures lazily, if not needed they will
  pass by as strings
This commit is contained in:
Mirek Kratochvil 2017-10-22 22:56:44 +02:00
parent 7b0bc06d45
commit 104ee12951
8 changed files with 331 additions and 67 deletions

@ -210,8 +210,10 @@ int action_gen_key (const std::string& p_algspec, const std::string&name,
* that has a colliding KeyID with anyone else. This is highly
* improbable, so apologize nicely in that case.
*/
if (!KR.store_keypair (keyring::get_keyid (pub),
name, algname, pub, priv)) {
keyring::keypair_entry*
kp = KR.store_keypair (keyring::get_keyid (pub),
name, algname, pub, priv);
if (!kp) {
err ("error: new key cannot be saved into the keyring.");
err ("notice: produced KeyID @" << keyring::get_keyid (pub)
@ -222,7 +224,12 @@ int action_gen_key (const std::string& p_algspec, const std::string&name,
}
//note that pub&priv sencode data will get destroyed along with keyring
if (!KR.save()) {
if (force_lock && !kp->lock (withlock)) {
err ("error: locking the key failed");
return 1;
}
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -400,6 +407,11 @@ int action_decrypt (bool armor, const std::string&symmetric,
return 2; //missing key flag
}
if (!kpe->decode_privkey (withlock)) {
err ("error: could not decrypt required private key");
return 1;
}
//and the algorithm
if ( (!AS.count (msg.alg_id))
|| (!AS[msg.alg_id]->provides_encryption())) {
@ -563,6 +575,12 @@ int action_sign (const std::string&user, bool armor, const std::string&detach,
return 1;
}
//decode it for message.h
if (!u->decode_privkey (withlock)) {
err ("error: could not decrypt required private key");
return 1;
}
//signature production part
signed_msg msg;
ccr_rng r;
@ -1006,6 +1024,12 @@ int action_sign_encrypt (const std::string&user, const std::string&recipient,
return 1;
}
//decode the signing key for message.h
if (!u->decode_privkey (withlock)) {
err ("error: could not decrypt required private key");
return 1;
}
//make a signature
signed_msg smsg;
ccr_rng r;
@ -1101,6 +1125,11 @@ int action_decrypt_verify (bool armor, bool yes,
return 2; //missing key flag
}
if (!kpe->decode_privkey (withlock)) {
err ("error: could not decrypt required private key");
return 1;
}
if ( (!AS.count (emsg.alg_id))
|| (!AS[emsg.alg_id]->provides_encryption())) {
err ("error: decryption algorithm unsupported");
@ -1359,7 +1388,9 @@ int action_import (bool armor, bool no_action, bool yes, bool fp,
}
}
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1450,7 +1481,9 @@ int action_delete (bool yes, const std::string & filter, keyring & KR)
i = todel.begin(), e = todel.end(); i != e; ++i)
KR.remove_pubkey (*i);
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1497,7 +1530,9 @@ int action_rename (bool yes,
i->second.name = name;
}
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1615,11 +1650,13 @@ int action_import_sec (bool armor, bool no_action, bool yes, bool fp,
name.length() ?
name : i->second.pub.name,
i->second.pub.alg,
i->second.pub.key, i->second.privkey);
i->second.pub.key, i->second.privkey_raw);
}
}
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1657,7 +1694,9 @@ int action_export_sec (bool armor, bool yes,
if (!okay) return 0;
}
sencode*S = keyring::serialize_keypairs (s);
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
sencode*S = keyring::serialize_keypairs (s, r);
if (!S) return 1; //weird.
std::string data = S->encode();
sencode_destroy (S);
@ -1666,8 +1705,6 @@ int action_export_sec (bool armor, bool yes,
std::vector<std::string> parts;
parts.resize (1);
base64_encode (data, parts[0]);
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
data = envelope_format (ENVELOPE_SECRETS, parts, r);
}
@ -1707,7 +1744,9 @@ int action_delete_sec (bool yes, const std::string & filter, keyring & KR)
i = todel.begin(), e = todel.end(); i != e; ++i)
KR.remove_keypair (*i);
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1754,7 +1793,9 @@ int action_rename_sec (bool yes,
i->second.pub.name = name;
}
if (!KR.save()) {
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
@ -1777,15 +1818,53 @@ static int action_lock_symkey (const std::string&symmetric,
return 0;
}
int action_lock_sec (const std::string&filter,
int action_lock_sec (bool yes,
const std::string&filter,
const std::string&symmetric,
const std::string&withlock,
bool armor,
keyring&)
keyring&KR)
{
if (!symmetric.empty())
return action_lock_symkey (symmetric, withlock, armor);
return 1;
PREPARE_KEYRING;
int kc = 0;
for (keyring::keypair_storage::iterator
i = KR.pairs.begin(), e = KR.pairs.end();
i != e; ++i) {
if (keyspec_matches (filter, i->second.pub.name, i->first))
++kc;
}
if (!kc) {
err ("error: no such key");
return 0;
}
if (!yes) {
bool okay = false;
ask_for_yes (okay, "This will protect " << kc
<< " secrets from your keyring. Continue?");
if (!okay) return 0;
}
for (keyring::keypair_storage::iterator
i = KR.pairs.begin(), e = KR.pairs.end();
i != e; ++i) {
if (keyspec_matches (filter, i->second.pub.name, i->first))
if(!i->second.lock (withlock)) {
err("error: key locking failed");
return false;
}
}
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
return 0;
}
static int action_unlock_symkey (const std::string&symmetric,
@ -1800,13 +1879,51 @@ static int action_unlock_symkey (const std::string&symmetric,
return 0;
}
int action_unlock_sec (const std::string&filter,
int action_unlock_sec (bool yes,
const std::string&filter,
const std::string&symmetric,
const std::string&withlock,
bool armor,
keyring&)
keyring&KR)
{
if (!symmetric.empty())
return action_unlock_symkey (symmetric, withlock, armor);
return 1;
PREPARE_KEYRING;
int kc = 0;
for (keyring::keypair_storage::iterator
i = KR.pairs.begin(), e = KR.pairs.end();
i != e; ++i) {
if (keyspec_matches (filter, i->second.pub.name, i->first))
++kc;
}
if (!kc) {
err ("error: no such key");
return 0;
}
if (!yes) {
bool okay = false;
ask_for_yes (okay, "This will remove protection from " << kc
<< " secrets from your keyring. Continue?");
if (!okay) return 0;
}
for (keyring::keypair_storage::iterator
i = KR.pairs.begin(), e = KR.pairs.end();
i != e; ++i) {
if (keyspec_matches (filter, i->second.pub.name, i->first))
if(!i->second.unlock (withlock)) {
err("error: key unlocking failed");
return false;
}
}
ccr_rng r;
if (!r.seed (256)) SEED_FAILED;
if (!KR.save (r)) {
err ("error: couldn't save keyring");
return 1;
}
return 0;
}

@ -98,13 +98,15 @@ int action_rename_sec (bool yes,
const std::string&filter, const std::string&name,
keyring&);
int action_lock_sec (const std::string&filter,
int action_lock_sec (bool yes,
const std::string&filter,
const std::string&symmetric,
const std::string&withlock,
bool armor,
keyring&);
int action_unlock_sec (const std::string&filter,
int action_unlock_sec (bool yes,
const std::string&filter,
const std::string&symmetric,
const std::string&withlock,
bool armor,

@ -104,7 +104,8 @@ void keyring::clear_keypairs (keypair_storage&pairs)
for (std::map<std::string, keypair_entry>::iterator
i = pairs.begin(), e = pairs.end(); i != e; ++i) {
sencode_destroy (i->second.pub.key);
sencode_destroy (i->second.privkey);
if (i->second.privkey)
sencode_destroy (i->second.privkey);
}
pairs.clear();
}
@ -149,19 +150,13 @@ bool keyring::parse_keypairs (sencode*keypairs, keypair_storage&pairs)
if (! (ident && alg && privkey && pubkey)) goto failure;
std::string keyid = get_keyid (pubkey->b);
sencode *priv, *pub;
priv = sencode_decode (privkey->b);
if (!priv) goto failure;
sencode *pub;
pub = sencode_decode (pubkey->b);
if (!pub) {
sencode_destroy (priv);
goto failure;
}
if (!pub) goto failure;
pairs[keyid] = keypair_entry (keyid, ident->b, alg->b,
pub, priv);
pub, privkey->b);
}
return true;
@ -170,8 +165,12 @@ failure:
return false;
}
sencode* keyring::serialize_keypairs (const keypair_storage&pairs)
sencode* keyring::serialize_keypairs (keypair_storage&pairs, prng&rng)
{
for (std::map<std::string, keypair_entry>::iterator
i = pairs.begin(), e = pairs.end(); i != e; ++i)
if (!i->second.fix_dirty (rng)) return NULL;
sencode_list*L = new sencode_list();
L->items.push_back (new sencode_bytes (KEYPAIRS_ID));
@ -182,7 +181,7 @@ sencode* keyring::serialize_keypairs (const keypair_storage&pairs)
a->items.resize (4);
a->items[0] = new sencode_bytes (i->second.pub.name);
a->items[1] = new sencode_bytes (i->second.pub.alg);
a->items[2] = new sencode_bytes (i->second.privkey->encode());
a->items[2] = new sencode_bytes (i->second.privkey_raw);
a->items[3] = new sencode_bytes (i->second.pub.key->encode());
L->items.push_back (a);
}
@ -443,7 +442,7 @@ static void ignore_term_signals (bool ignore)
}
#endif
bool keyring::save()
bool keyring::save (prng&rng)
{
std::string dir, fn, bfn;
sencode*S;
@ -466,7 +465,9 @@ bool keyring::save()
/*
* keypairs
*/
S = serialize_keypairs (pairs);
S = serialize_keypairs (pairs, rng);
if (!S) return false;
fn = dir + SECRETS_FILENAME;
bfn = fn + BAK_SUFFIX;
res = file_put_sencode_with_backup (fn, S, bfn, backup_pairs);
@ -559,3 +560,82 @@ bool keyring::close()
return true;
}
/*
* keypair_entry loads the privkeys lazily so that it's not necessary to have
* all the secrets all the time
*/
#include "seclock.h"
#include "iohelpers.h"
bool keyring::keypair_entry::lock (const std::string&withlock)
{
//withlock here is useful for just re-encrypting,
//possibly with different password
if (!decode_privkey (withlock)) return false;
err ("notice: locking key @" + pub.keyid);
if (!load_lock_secret (sk, withlock,
"protecting key `"
+ escape_output (pub.name)
+ "'",
"KEYRING", true))
return false;
dirty = true;
locked = true;
return true;
}
bool keyring::keypair_entry::unlock (const std::string&withlock)
{
if (!decode_privkey (withlock)) return false;
if (locked) {
locked = false;
dirty = true;
}
return true;
}
bool keyring::keypair_entry::decode_privkey (const std::string&withlock)
{
if (privkey) return true; //already done
std::string encoded;
if (looks_like_locked_secret (privkey_raw)) {
err ("notice: unlocking key @" + pub.keyid);
if (!unlock_secret_sk (privkey_raw, encoded,
withlock,
"loading key `"
+ escape_output (pub.name)
+ "'",
"KEYRING", sk))
return false;
locked = true;
} else {
encoded = privkey_raw;
locked = false;
}
privkey = sencode_decode (encoded);
if (!privkey)
return false;
dirty = false;
return true;
}
#include <sstream>
bool keyring::keypair_entry::fix_dirty (prng&rng)
{
if (!privkey || !dirty) return true; //nothing to do!
if (locked) {
std::string encoded = privkey->encode();
if (!lock_secret_sk (encoded, privkey_raw, sk, rng))
return false;
} else {
privkey_raw = privkey->encode();
}
dirty = false;
return true;
}

@ -25,6 +25,7 @@
#include <map>
#include "sencode.h"
#include "symkey.h"
class keyring
{
@ -50,10 +51,22 @@ public:
struct keypair_entry {
pubkey_entry pub;
sencode *privkey;
bool locked; //store encrypted
symkey sk;
bool dirty; //privkey_raw needs to be updated
std::string privkey_raw;
bool decode_privkey (const std::string&withlock);
bool lock (const std::string&withlock);
bool unlock (const std::string&withlock);
bool fix_dirty (prng&rng);
keypair_entry() {
privkey = NULL;
dirty = false;
}
keypair_entry (const std::string&KID,
@ -61,7 +74,22 @@ public:
const std::string& A,
sencode*PubK,
sencode*PrivK)
: pub (KID, N, A, PubK), privkey (PrivK) {}
: pub (KID, N, A, PubK),
privkey (PrivK),
locked (false),
dirty (true)
{}
keypair_entry (const std::string&KID,
const std::string& N,
const std::string& A,
sencode*PubK,
const std::string&PrivK_raw)
: pub (KID, N, A, PubK),
privkey (NULL),
dirty (false),
privkey_raw (PrivK_raw)
{}
};
typedef std::map<std::string, pubkey_entry> pubkey_storage;
@ -84,7 +112,7 @@ public:
bool open();
bool close();
bool save();
bool save (prng&rng);
static std::string get_keyid (const std::string& pubkey);
@ -96,7 +124,7 @@ public:
static void clear_pubkeys (pubkey_storage&);
static bool parse_keypairs (sencode*, keypair_storage&);
static sencode* serialize_keypairs (const keypair_storage&);
static sencode* serialize_keypairs (keypair_storage&, prng&rng);
static bool parse_pubkeys (sencode*, pubkey_storage&);
static sencode* serialize_pubkeys (const pubkey_storage&);
@ -107,15 +135,14 @@ public:
return NULL;
}
bool store_pubkey (const std::string&keyid,
const std::string&name,
const std::string&alg,
sencode*key) {
pubkey_entry* store_pubkey (const std::string&keyid,
const std::string&name,
const std::string&alg,
sencode*key) {
if (pairs.count (keyid)) return false;
if (pubs.count (keyid)) return false;
pubs[keyid] = pubkey_entry (keyid, name, alg, key);
return true;
if (pairs.count (keyid)) return NULL;
if (pubs.count (keyid)) return NULL;
return & (pubs[keyid] = pubkey_entry (keyid, name, alg, key));
}
void remove_pubkey (const std::string&keyid) {
@ -130,16 +157,27 @@ public:
return NULL;
}
bool store_keypair (const std::string&keyid,
const std::string&name,
const std::string&alg,
sencode*pubkey, sencode*privkey) {
keypair_entry* store_keypair (const std::string&keyid,
const std::string&name,
const std::string&alg,
sencode*pubkey, sencode*privkey) {
if (pairs.count (keyid)) return false;
if (pubs.count (keyid)) return false;
pairs[keyid] = keypair_entry (keyid, name, alg,
pubkey, privkey);
return true;
if (pairs.count (keyid)) return NULL;
if (pubs.count (keyid)) return NULL;
return & (pairs[keyid] = keypair_entry (keyid, name, alg,
pubkey, privkey));
}
keypair_entry* store_keypair (const std::string&keyid,
const std::string&name,
const std::string&alg,
sencode*pubkey,
const std::string&privkey_raw) {
if (pairs.count (keyid)) return NULL;
if (pubs.count (keyid)) return NULL;
return & (pairs[keyid] = keypair_entry (keyid, name, alg,
pubkey, privkey_raw));
}
void remove_keypair (const std::string&keyid) {

@ -477,12 +477,14 @@ int main (int argc, char**argv)
break;
case 'L':
exitval = action_lock_sec (filter, symmetric, withlock,
exitval = action_lock_sec (opt_yes, filter,
symmetric, withlock,
opt_armor, KR);
break;
case 'U':
exitval = action_unlock_sec (filter, symmetric, withlock,
exitval = action_unlock_sec (opt_yes, filter,
symmetric, withlock,
opt_armor, KR);
break;

@ -84,19 +84,18 @@ int signed_msg::sign (const bvector&msg,
keyring::keypair_entry *k = kr.get_keypair (key_id);
if (!k) return 2;
//note that someone has to prepare the k->privkey in advance!
if (k->pub.alg != alg_id) return 3;
bool privkey_dirty = false;
int r;
r = alg->sign (message, signature, & (k->privkey), privkey_dirty, rng);
r = alg->sign (message, signature, & (k->privkey), k->dirty, rng);
if (r) return r;
if (privkey_dirty) {
if (k->dirty) {
//we can't output a signature without storing privkey changes!
if (!kr.save()) return 4;
if (!kr.save (rng)) return 4;
}
return 0;

@ -84,6 +84,12 @@ bool lock_secret (const std::string &secret, std::string &locked,
if (!load_lock_secret (sk, withlock, reason, secret_type, true))
return false;
return lock_secret_sk (secret, locked, sk, rng);
}
bool lock_secret_sk (const std::string &secret, std::string &locked,
symkey&sk, prng&rng)
{
std::istringstream i (secret);
std::ostringstream o;
o << LOCKED_PREFIX;
@ -92,12 +98,13 @@ bool lock_secret (const std::string &secret, std::string &locked,
return ret;
}
bool unlock_secret (const std::string &locked, std::string &secret,
const std::string &withlock,
const std::string &reason,
const std::string &secret_type)
bool unlock_secret_sk (const std::string &locked, std::string &secret,
const std::string &withlock,
const std::string &reason,
const std::string &secret_type,
symkey&sk)
{
symkey sk;
if (!looks_like_locked_secret (locked)) {
err ("seclock: malformed locked secret");
return false;
@ -112,5 +119,17 @@ bool unlock_secret (const std::string &locked, std::string &secret,
std::ostringstream o;
bool ret = !sk.decrypt (i, o); //returns int!
secret = o.str();
if (!ret) err ("error: unlocking a secret failed,"
" double check you password/symkey");
return ret;
}
bool unlock_secret (const std::string &locked, std::string &secret,
const std::string &withlock,
const std::string &reason,
const std::string &secret_type)
{
symkey sk;
return unlock_secret_sk (locked, secret, withlock,
reason, secret_type, sk);
}

@ -37,9 +37,16 @@ bool lock_secret (const std::string&secret, std::string&locked,
const std::string&reason,
const std::string&secret_type,
prng&rng);
bool lock_secret_sk (const std::string&secret, std::string&locked,
symkey&sk, prng&rng);
bool unlock_secret (const std::string&locked, std::string&secret,
const std::string&withlock,
const std::string&reason,
const std::string&secret_type);
bool unlock_secret_sk (const std::string&locked, std::string&secret,
const std::string&withlock,
const std::string&reason,
const std::string&secret_type,
symkey&sk);
#endif