diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-08-29 14:10:05 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-08-29 17:11:58 +0200 |
commit | 742b637a118b3b87034c224fb5146025addd41a8 (patch) | |
tree | 8fd605d35de23a428331cf6129dabcbe1e355b3e /store | |
parent | 80b0d59448feb30daa527688e73a56c5cefca92c (diff) |
store: Add method to lookup keys by keyid.
Diffstat (limited to 'store')
-rw-r--r-- | store/src/backend/mod.rs | 65 | ||||
-rw-r--r-- | store/src/lib.rs | 50 | ||||
-rw-r--r-- | store/src/store_protocol.capnp | 1 |
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 { |