From 2c4a3995362a2803c3c2d7e28ce5d7f8e105a44e Mon Sep 17 00:00:00 2001
From: Mirek Kratochvil <exa.exa@gmail.com>
Date: Mon, 1 Apr 2013 17:49:58 +0200
Subject: [PATCH] keyring: better structure

---
 src/keyring.cpp |  77 +++++++++++++++++++++-------------
 src/keyring.h   | 108 ++++++++++++++++++++++++++++++++++++++++++------
 src/message.cpp |  29 ++++++-------
 3 files changed, 158 insertions(+), 56 deletions(-)

diff --git a/src/keyring.cpp b/src/keyring.cpp
index 64bf126..c0c3da7 100644
--- a/src/keyring.cpp
+++ b/src/keyring.cpp
@@ -18,34 +18,46 @@
 
 #include "keyring.h"
 
-sencode* keyring::get_pubkey (const std::string&key_id)
+void keyring::clear()
 {
+	for (std::map<std::string, pubkey_entry>::iterator
+	     i = pubs.begin(), e = pubs.end(); i != e; ++i)
+		sencode_destroy (i->second.key);
+	pubs.clear();
 
+	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);
+	}
+	pairs.clear();
 }
 
-void keyring::remove_pubkey (const std::string&key_id)
+/*
+ * KeyID is SHA256 of pubkey string representation. Also serves as a
+ * simple fingerprint.
+ */
+
+#include "sha2.h"
+#include <stdint.h>
+
+std::string keyring::get_keyid (const std::string&pubkey)
 {
+	SHA256_CTX ctx;
+	uint8_t t;
 
-}
+	SHA256_Init (&ctx);
 
-bool keyring::store_pubkey (const std::string&key_id, sencode*)
-{
+	for (size_t i = 0; i < pubkey.length(); ++i) {
+		t = pubkey[i];
+		SHA256_Update (&ctx, &t, 1);
+	}
 
-}
-
-sencode* keyring::get_privkey (const std::string&key_id)
-{
-
-}
-
-void keyring::remove_privkey (const std::string&key_id)
-{
-
-}
-
-bool keyring::store_privkey (const std::string&key_id, sencode*)
-{
+	std::string r;
+	r.resize (64, ' ');
+	SHA256_End (&ctx, & (r[0]) );
 
+	return r;
 }
 
 /*
@@ -53,8 +65,10 @@ bool keyring::store_privkey (const std::string&key_id, sencode*)
  *
  * Whole thing is stored in two files just like in GnuPG:
  *
- * ~/.ccr/pubkeys
- * ~/.ccr/private_keyring
+ * ${CCR_DIR}/pubring
+ * ${CCR_DIR}/secrets
+ *
+ * CCR_DIR is taken from environment, and defaults to ${HOME}/.ccr
  *
  * format of the files is raw sencode.
  *
@@ -62,9 +76,9 @@ bool keyring::store_privkey (const std::string&key_id, sencode*)
  *
  * (
  *   "ccr public key storage"
- *   ( "public-key-id" pubkey_as_embedded_sencode )
- *   ( "public-key-id" pubkey_as_embedded_sencode )
- *   ( "public-key-id" pubkey_as_embedded_sencode )
+ *   ( "key-name" pubkey_as_embedded_sencode )
+ *   ( "key-name" pubkey_as_embedded_sencode )
+ *   ( "key-name" pubkey_as_embedded_sencode )
  *   ...
  * )
  *
@@ -73,15 +87,22 @@ bool keyring::store_privkey (const std::string&key_id, sencode*)
  *
  * (
  *   "ccr private keyring"
- *   ( "public-key-id" privkey pubkey )
- *   ( "public-key-id" privkey pubkey )
- *   ( "public-key-id" privkey pubkey )
+ *   ( "key-name" privkey pubkey )
+ *   ( "key-name" privkey pubkey )
+ *   ( "key-name" privkey pubkey )
  *   ...
  * )
  *
  */
 
-bool keyring::disk_sync()
+bool keyring::load()
 {
+
+	return false;
+}
+
+bool keyring::save()
+{
+
 	return false;
 }
diff --git a/src/keyring.h b/src/keyring.h
index c174a98..27151f0 100644
--- a/src/keyring.h
+++ b/src/keyring.h
@@ -24,23 +24,107 @@
 
 #include "sencode.h"
 
-/* TODO privkeys are actually keypairs! */
-
 class keyring
 {
-	std::multimap<std::string, sencode*>
-	priv_cache, priv_dirty,
-	            pub_cache, pub_dirty;
 public:
-	bool disk_sync();
+	struct pubkey_entry {
+		sencode *key;
+		std::string name, keyid;
 
-	sencode* get_pubkey (const std::string&key_id);
-	void remove_pubkey (const std::string&key_id);
-	bool store_pubkey (const std::string&key_id, sencode*);
+		pubkey_entry() {
+			key = NULL;
+		}
 
-	sencode* get_privkey (const std::string&key_id);
-	void remove_privkey (const std::string&key_id);
-	bool store_privkey (const std::string&key_id, sencode*);
+		pubkey_entry (const std::string& KID,
+		              const std::string& N,
+		              sencode*K) {
+			key = K;
+			name = N;
+			keyid = KID;
+		}
+	};
+
+	struct keypair_entry {
+		sencode *privkey;
+		pubkey_entry pub;
+
+		keypair_entry() {
+			privkey = NULL;
+		}
+
+		keypair_entry (const std::string&KID,
+		               const std::string& N,
+		               sencode*PubK,
+		               sencode*PrivK)
+			: pub (KID, N, PubK) {
+			privkey = PrivK;
+		}
+	};
+
+	std::map<std::string, pubkey_entry> pubs;
+	std::map<std::string, keypair_entry> pairs;
+
+	explicit keyring() {
+	}
+
+	~keyring() {
+		clear();
+	}
+
+	bool load();
+	bool save();
+
+	void clear();
+
+	static std::string get_keyid (const std::string& pubkey);
+
+	static std::string get_keyid (sencode* pubkey) {
+		return get_keyid (pubkey->encode() );
+	}
+
+	pubkey_entry* get_pubkey (const std::string&keyid) {
+		// "own first", but there should not be collisions.
+		if (pairs.count (keyid) ) return & (pairs[keyid].pub);
+		if (pubs.count (keyid) ) return & (pubs[keyid]);
+		return NULL;
+	}
+
+	bool store_pubkey (const std::string&keyid,
+	                   const std::string&name, sencode*key) {
+
+		if (pairs.count (keyid) ) return false;
+		if (pubs.count (keyid) ) return false;
+		pubs[keyid] = pubkey_entry (keyid, name, key);
+	}
+
+	void remove_pubkey (const std::string&keyid) {
+		if (pubs.count (keyid) ) {
+			sencode_destroy (pubs[keyid].key);
+			pubs.erase (keyid);
+		}
+	}
+
+	keypair_entry* get_keypair (const std::string&keyid) {
+		if (pairs.count (keyid) ) return & (pairs[keyid]);
+		return NULL;
+	}
+
+	bool store_keypair (const std::string&keyid,
+	                    const std::string&name,
+	                    sencode*pubkey, sencode*privkey) {
+
+		if (pairs.count (keyid) ) return false;
+		if (pubs.count (keyid) ) return false;
+		pairs[keyid] = keypair_entry (keyid, name, pubkey, privkey);
+	}
+
+	void remove_keypair (const std::string&keyid) {
+		if (pairs.count (keyid) ) {
+			sencode_destroy (pairs[keyid].pub.key);
+			sencode_destroy (pairs[keyid].privkey);
+			pairs.erase (keyid);
+		}
+	}
 };
 
 #endif
diff --git a/src/message.cpp b/src/message.cpp
index 8994b30..29a1f7f 100644
--- a/src/message.cpp
+++ b/src/message.cpp
@@ -35,10 +35,10 @@ int encrypted_msg::encrypt (const bvector&msg,
 
 	if (!alg) return 1;
 
-	sencode*pubkey = kr.get_pubkey (key_id);
-	if (!pubkey) return 2; //PK not found
+	keyring::pubkey_entry*pk = kr.get_pubkey (key_id);
+	if (!pk) return 2; //PK not found
 
-	return alg->encrypt (msg, ciphertext, pubkey, rng);
+	return alg->encrypt (msg, ciphertext, pk->key, rng);
 }
 
 int encrypted_msg::decrypt (bvector& msg, algorithm_suite&algs, keyring& kr)
@@ -52,10 +52,10 @@ int encrypted_msg::decrypt (bvector& msg, algorithm_suite&algs, keyring& kr)
 
 	if (!alg) return 1;
 
-	sencode*privkey = kr.get_privkey (key_id);
-	if (!privkey) return 2;
+	keyring::keypair_entry*k = kr.get_keypair (key_id);
+	if (!k) return 2;
 
-	return alg->decrypt (ciphertext, msg, privkey);
+	return alg->decrypt (ciphertext, msg, k->privkey);
 }
 
 int signed_msg::sign (const bvector&msg,
@@ -76,22 +76,19 @@ int signed_msg::sign (const bvector&msg,
 
 	if (!alg) return 1;
 
-	sencode*privkey = kr.get_privkey (key_id);
-	if (!privkey) return 2;
+	keyring::keypair_entry *k = kr.get_keypair (key_id);
+	if (!k) return 2;
 
 	bool privkey_dirty = false;
 	int r;
 
-	r = alg->sign (message, signature, &privkey, privkey_dirty, rng);
+	r = alg->sign (message, signature, & (k->privkey), privkey_dirty, rng);
 
 	if (r) return r;
 
 	if (privkey_dirty) {
-		kr.remove_privkey (key_id);
-		//this actually shouldn't fail, key_id is not present
-		kr.store_privkey (key_id, privkey);
 		//we can't output a signature without storing privkey changes!
-		if (!kr.disk_sync() ) return 3;
+		if (!kr.save() ) return 3;
 	}
 
 	return 0;
@@ -108,9 +105,9 @@ int signed_msg::verify (algorithm_suite&algs, keyring&kr)
 
 	if (!alg) return 1;
 
-	sencode*pubkey = kr.get_pubkey (key_id);
-	if (!pubkey) return 2;
+	keyring::pubkey_entry*pk = kr.get_pubkey (key_id);
+	if (!pk) return 2;
 
-	return alg->verify (signature, message, pubkey);
+	return alg->verify (signature, message, pk->key);
 }