summaryrefslogtreecommitdiffstats
path: root/store
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-08-29 14:10:05 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-08-29 17:11:58 +0200
commit742b637a118b3b87034c224fb5146025addd41a8 (patch)
tree8fd605d35de23a428331cf6129dabcbe1e355b3e /store
parent80b0d59448feb30daa527688e73a56c5cefca92c (diff)
store: Add method to lookup keys by keyid.
Diffstat (limited to 'store')
-rw-r--r--store/src/backend/mod.rs65
-rw-r--r--store/src/lib.rs50
-rw-r--r--store/src/store_protocol.capnp1
3 files changed, 116 insertions, 0 deletions
diff --git a/store/src/backend/mod.rs b/store/src/backend/mod.rs
index 3c6051dd..a356fb19 100644
--- a/store/src/backend/mod.rs
+++ b/store/src/backend/mod.rs
@@ -302,6 +302,27 @@ impl node::store::Server for StoreServer {
Promise::ok(())
}
+ fn lookup_by_keyid(&mut self,
+ params: node::store::LookupByKeyidParams,
+ mut results: node::store::LookupByKeyidResults)
+ -> Promise<(), capnp::Error> {
+ bind_results!(results);
+ let keyid = pry!(params.get()).get_keyid();
+
+ let binding_id: ID = sry!(
+ self.c.query_row(
+ "SELECT bindings.id FROM bindings
+ JOIN key_by_keyid on bindings.key = key_by_keyid.key
+ WHERE key_by_keyid.keyid = ?1",
+ &[&(keyid as i64)], |row| row.get(0)));
+
+ pry!(pry!(results.get().get_result()).set_ok(
+ node::binding::ToClient::new(
+ BindingServer::new(self.c.clone(), binding_id))
+ .from_server::<capnp_rpc::Server>()));
+ Promise::ok(())
+ }
+
fn delete(&mut self,
_: node::store::DeleteParams,
mut results: node::store::DeleteResults)
@@ -501,6 +522,7 @@ impl node::binding::Server for BindingServer {
sry!(self.c.execute("UPDATE keys SET key = ?1 WHERE id = ?2",
&[&blob, &key_id]));
+ sry!(KeyServer::reindex_subkeys(&self.c, key_id, &new));
pry!(pry!(results.get().get_result()).set_ok(&blob[..]));
Promise::ok(())
@@ -676,10 +698,45 @@ impl KeyServer {
self.c.execute("UPDATE keys SET key = ?1 WHERE id = ?2",
&[&blob, &self.id])?;
+ KeyServer::reindex_subkeys(&self.c, self.id, &new)?;
Ok(blob)
}
+ /// Keeps the mapping of (sub)KeyIDs to keys up-to-date.
+ fn reindex_subkeys(c: &Connection, key_id: ID, tpk: &TPK) -> Result<()> {
+ for (sig, key) in tpk.keys() {
+ // Only index signing- or certification-capable subkeys.
+ if ! sig.map(|s| s.key_flags().can_sign()
+ || s.key_flags().can_certify())
+ .unwrap_or(true)
+ {
+ continue;
+ }
+
+ let keyid = key.fingerprint().to_keyid().as_u64()
+ .expect("computed keyid is valid");
+
+ let r = c.execute(
+ "INSERT INTO key_by_keyid (keyid, key) VALUES (?1, ?2)",
+ &[&(keyid as i64), &key_id]);
+
+ // The mapping might already be present. This is not an error.
+ match r {
+ Err(rusqlite::Error::SqliteFailure(f, e)) => match f.code {
+ // Already present.
+ rusqlite::ErrorCode::ConstraintViolation =>
+ Ok(()),
+ // Raise otherwise.
+ _ => Err(rusqlite::Error::SqliteFailure(f, e)),
+ },
+ Err(e) => Err(e),
+ Ok(_) => Ok(()),
+ }?;
+ }
+ Ok(())
+ }
+
/// Records a successful key update.
fn success(&self, message: &str, next: Duration) -> Result<()> {
log::message(&self.c, log::Refers::to().key(self.id),
@@ -1264,6 +1321,14 @@ CREATE TABLE keys (
UNIQUE (fingerprint));
+CREATE TABLE key_by_keyid (
+ id INTEGER PRIMARY KEY,
+ keyid INTEGER NOT NULL,
+ key INTEGER NOT NULL,
+
+ UNIQUE(keyid, key),
+ FOREIGN KEY (key) REFERENCES keys(id) ON DELETE CASCADE);
+
CREATE TABLE log (
id INTEGER PRIMARY KEY,
timestamp INTEGER NOT NULL,
diff --git a/store/src/lib.rs b/store/src/lib.rs
index 947ed5d8..22868934 100644
--- a/store/src/lib.rs
+++ b/store/src/lib.rs
@@ -79,6 +79,7 @@ extern crate sequoia_core;
extern crate sequoia_net;
use openpgp::Fingerprint;
+use openpgp::KeyID;
use openpgp::TPK;
use sequoia_core as core;
use sequoia_core::Context;
@@ -287,6 +288,55 @@ impl Store {
Ok(Binding::new(self.core.clone(), Some(label), binding))
}
+ /// Looks up a key by KeyID.
+ ///
+ /// The KeyID may also reference a signing- or
+ /// certification-capable subkey. The reason for this restriction
+ /// is that anyone can attach any subkey to her TPK, but signing-
+ /// or certification-capable subkeys require back signatures.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # extern crate openpgp;
+ /// # extern crate sequoia_core;
+ /// # extern crate sequoia_store;
+ /// # use openpgp::{TPK, KeyID};
+ /// # use sequoia_core::{Context, NetworkPolicy, IPCPolicy};
+ /// # use sequoia_store::{Store, Result};
+ /// # fn main() { f().unwrap(); }
+ /// # fn f() -> Result<()> {
+ /// # let ctx = Context::configure("org.sequoia-pgp.demo.store")
+ /// # .network_policy(NetworkPolicy::Offline)
+ /// # .ipc_policy(IPCPolicy::Internal)
+ /// # .ephemeral().build()?;
+ /// # let tpk = TPK::from_bytes(
+ /// # include_bytes!("../../openpgp/tests/data/keys/emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))
+ /// # .unwrap();
+ /// let store = Store::open(&ctx, "default")?;
+ /// store.import("Emmelie", &tpk)?;
+ ///
+ /// // Lookup by the primary key's KeyID.
+ /// let tpk_ = store.lookup_by_keyid(&KeyID::from_hex("069C0C348DD82C19")?)?
+ /// .tpk()?;
+ /// assert_eq!(tpk, tpk_);
+ ///
+ /// // Lookup by the encryption subkey's KeyID.
+ /// let tpk_ = store.lookup_by_keyid(&KeyID::from_hex("22E3FAFE96B56C32")?)?
+ /// .tpk()?;
+ /// assert_eq!(tpk, tpk_);
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn lookup_by_keyid(&self, keyid: &KeyID) -> Result<Binding> {
+ let mut request = self.store.lookup_by_keyid_request();
+ request.get().set_keyid(keyid.as_u64()?);
+ let binding = make_request!(self.core.borrow_mut(), request)?;
+ let mut binding = Binding::new(self.core.clone(), None, binding);
+ binding.label = binding.label().ok();
+ Ok(binding)
+ }
+
/// Deletes this store.
///
/// # Example
diff --git a/store/src/store_protocol.capnp b/store/src/store_protocol.capnp
index 11ef362a..c94ee275 100644
--- a/store/src/store_protocol.capnp
+++ b/store/src/store_protocol.capnp
@@ -13,6 +13,7 @@ interface Node {
delete @2 () -> (result: Result(Unit));
iter @3 () -> (result: Result(BindingIter));
log @4 () -> (result: Result(LogIter));
+ lookupByKeyid @5 (keyid: UInt64) -> (result: Result(Binding));
}
interface Binding {