allow lookup by keyid and subkey fpr
This commit is contained in:
parent
a5e9d8fa3c
commit
6d3ccd9762
|
@ -28,6 +28,11 @@ http {
|
|||
try_files /$request_uri =404;
|
||||
}
|
||||
|
||||
location ^~ /by-kid/ {
|
||||
default_type application/pgp-keys;
|
||||
try_files /$request_uri =404;
|
||||
}
|
||||
|
||||
location = / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use url;
|
|||
|
||||
use database::{Verify, Delete, Database};
|
||||
use Result;
|
||||
use types::{Email, Fingerprint};
|
||||
use types::{Email, Fingerprint, KeyID};
|
||||
|
||||
pub struct Filesystem {
|
||||
base: PathBuf,
|
||||
|
@ -49,6 +49,7 @@ impl Filesystem {
|
|||
create_dir_all(base.join("scratch_pad"))?;
|
||||
create_dir_all(base.join("public").join("by-fpr"))?;
|
||||
create_dir_all(base.join("public").join("by-email"))?;
|
||||
create_dir_all(base.join("public").join("by-kid"))?;
|
||||
|
||||
info!("Opened base dir '{}'", base.display());
|
||||
Ok(Filesystem{
|
||||
|
@ -155,6 +156,64 @@ impl Database for Filesystem {
|
|||
}
|
||||
}
|
||||
|
||||
fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint) {
|
||||
let target = self.base.join("public").join("by-fpr").join(fpr.to_string());
|
||||
let link = self.base.join("public").join("by-kid").join(kid.to_string());
|
||||
|
||||
if link.exists() {
|
||||
let _ = remove_file(link.clone());
|
||||
}
|
||||
|
||||
let _ = symlink(target, link);
|
||||
}
|
||||
|
||||
fn unlink_kid(&self, kid: &KeyID, fpr: &Fingerprint) {
|
||||
let link = self.base.join("public").join("by-kid").join(kid.to_string());
|
||||
|
||||
match read_link(link.clone()) {
|
||||
Ok(target) => {
|
||||
let expected = self.base.join("public").join("by-fpr").join(fpr.to_string());
|
||||
|
||||
if target == expected {
|
||||
let _ = remove_file(link);
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn link_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) {
|
||||
let target = self.base.join("public").join("by-fpr").join(fpr.to_string());
|
||||
let link = self.base.join("public").join("by-fpr").join(from.to_string());
|
||||
|
||||
if link == target { return; }
|
||||
if link.exists() {
|
||||
match link.metadata() {
|
||||
Ok(ref meta) if meta.file_type().is_symlink() => {
|
||||
let _ = remove_file(link.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = symlink(target, link);
|
||||
}
|
||||
|
||||
fn unlink_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) {
|
||||
let link = self.base.join("public").join("by-fpr").join(from.to_string());
|
||||
|
||||
match read_link(link.clone()) {
|
||||
Ok(target) => {
|
||||
let expected = self.base.join("public").join("by-fpr").join(fpr.to_string());
|
||||
|
||||
if target == expected {
|
||||
let _ = remove_file(link);
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_verify_token(&self, token: &str) -> Option<Verify> {
|
||||
self.pop_token("verification_tokens", token).ok().and_then(|raw| {
|
||||
str::from_utf8(&raw).ok().map(|s| s.to_string())
|
||||
|
@ -211,6 +270,31 @@ impl Database for Filesystem {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// XXX: slow
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<Box<[u8]>> {
|
||||
use std::fs;
|
||||
|
||||
let path = self.base.join("public").join("by-kid").join(kid.to_string());
|
||||
|
||||
fs::canonicalize(path).ok()
|
||||
.and_then(|p| {
|
||||
if p.starts_with(&self.base) {
|
||||
Some(p)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).and_then(|p| {
|
||||
File::open(p).ok()
|
||||
}).and_then(|mut fd| {
|
||||
let mut buf = Vec::default();
|
||||
if fd.read_to_end(&mut buf).is_ok() {
|
||||
Some(buf.into_boxed_slice())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -257,4 +341,20 @@ mod tests {
|
|||
|
||||
test::test_uid_deletion(&mut db);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subkey_lookup() {
|
||||
let tmpdir = TempDir::new().unwrap();
|
||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
||||
|
||||
test::test_subkey_lookup(&mut db);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kid_lookup() {
|
||||
let tmpdir = TempDir::new().unwrap();
|
||||
let mut db = Filesystem::new(tmpdir.path()).unwrap();
|
||||
|
||||
test::test_kid_lookup(&mut db);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@ use std::collections::HashMap;
|
|||
use parking_lot::Mutex;
|
||||
|
||||
use database::{Verify, Delete, Database};
|
||||
use types::{Email, Fingerprint};
|
||||
use types::{Email, Fingerprint, KeyID};
|
||||
use Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Memory {
|
||||
fpr: Mutex<HashMap<Fingerprint, Box<[u8]>>>,
|
||||
fpr_links: Mutex<HashMap<Fingerprint, Fingerprint>>,
|
||||
email: Mutex<HashMap<Email, Fingerprint>>,
|
||||
kid: Mutex<HashMap<KeyID, Fingerprint>>,
|
||||
verify_token: Mutex<HashMap<String, Verify>>,
|
||||
delete_token: Mutex<HashMap<String, Delete>>,
|
||||
}
|
||||
|
@ -16,6 +19,8 @@ impl Default for Memory {
|
|||
fn default() -> Self {
|
||||
Memory{
|
||||
fpr: Mutex::new(HashMap::default()),
|
||||
fpr_links: Mutex::new(HashMap::default()),
|
||||
kid: Mutex::new(HashMap::default()),
|
||||
email: Mutex::new(HashMap::default()),
|
||||
verify_token: Mutex::new(HashMap::default()),
|
||||
delete_token: Mutex::new(HashMap::default()),
|
||||
|
@ -54,6 +59,14 @@ impl Database for Memory {
|
|||
}
|
||||
}
|
||||
|
||||
fn link_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) {
|
||||
self.fpr_links.lock().insert(from.clone(), fpr.clone());
|
||||
}
|
||||
|
||||
fn unlink_fpr(&self, from: &Fingerprint, _: &Fingerprint) {
|
||||
self.fpr_links.lock().remove(from);
|
||||
}
|
||||
|
||||
fn link_email(&self, email: &Email, fpr: &Fingerprint) {
|
||||
self.email.lock().insert(email.clone(), fpr.clone());
|
||||
}
|
||||
|
@ -62,6 +75,14 @@ impl Database for Memory {
|
|||
self.email.lock().remove(email);
|
||||
}
|
||||
|
||||
fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint) {
|
||||
self.kid.lock().insert(kid.clone(), fpr.clone());
|
||||
}
|
||||
|
||||
fn unlink_kid(&self, kid: &KeyID, _: &Fingerprint) {
|
||||
self.kid.lock().remove(kid);
|
||||
}
|
||||
|
||||
// (verified uid, fpr)
|
||||
fn pop_verify_token(&self, token: &str) -> Option<Verify> {
|
||||
self.verify_token.lock().remove(token)
|
||||
|
@ -73,15 +94,27 @@ impl Database for Memory {
|
|||
}
|
||||
|
||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<Box<[u8]>> {
|
||||
self.fpr.lock().get(fpr).map(|x| x.clone())
|
||||
let fprs = self.fpr.lock();
|
||||
let links = self.fpr_links.lock();
|
||||
|
||||
fprs.get(fpr).map(|x| x.clone()).or_else(|| {
|
||||
links.get(fpr).and_then(|fpr| fprs.get(fpr).map(|x| x.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
fn by_email(&self, email: &Email) -> Option<Box<[u8]>> {
|
||||
let by_email = self.email.lock();
|
||||
let fprs = self.fpr.lock();
|
||||
let by_email = self.email.lock();
|
||||
|
||||
by_email.get(email).and_then(|fpr| fprs.get(fpr).map(|x| x.clone()))
|
||||
}
|
||||
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<Box<[u8]>> {
|
||||
let fprs = self.fpr.lock();
|
||||
let by_kid = self.kid.lock();
|
||||
|
||||
by_kid.get(kid).and_then(|fpr| fprs.get(fpr).map(|x| x.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
|
@ -123,4 +156,18 @@ mod tests {
|
|||
|
||||
test::test_uid_verification(&mut db);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subkey_lookup() {
|
||||
let mut db = Memory::default();
|
||||
|
||||
test::test_subkey_lookup(&mut db);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kid_lookup() {
|
||||
let mut db = Memory::default();
|
||||
|
||||
test::test_kid_lookup(&mut db);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::convert::TryFrom;
|
|||
use time;
|
||||
use sequoia_openpgp::{packet::Signature, TPK, packet::UserID, Packet, PacketPile, constants::SignatureType, parse::Parse};
|
||||
use Result;
|
||||
use types::{Fingerprint, Email};
|
||||
use types::{Fingerprint, Email, KeyID};
|
||||
|
||||
mod fs;
|
||||
pub use self::fs::Filesystem;
|
||||
|
@ -75,29 +75,35 @@ pub trait Database: Sync + Send {
|
|||
fn link_email(&self, email: &Email, fpr: &Fingerprint);
|
||||
fn unlink_email(&self, email: &Email, fpr: &Fingerprint);
|
||||
|
||||
fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint);
|
||||
fn unlink_kid(&self, kid: &KeyID, fpr: &Fingerprint);
|
||||
|
||||
fn link_fpr(&self, from: &Fingerprint, to: &Fingerprint);
|
||||
fn unlink_fpr(&self, from: &Fingerprint, to: &Fingerprint);
|
||||
|
||||
// (verified uid, fpr)
|
||||
fn pop_verify_token(&self, token: &str) -> Option<Verify>;
|
||||
// fpr
|
||||
fn pop_delete_token(&self, token: &str) -> Option<Delete>;
|
||||
|
||||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<Box<[u8]>>;
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<Box<[u8]>>;
|
||||
fn by_email(&self, email: &Email) -> Option<Box<[u8]>>;
|
||||
// fn by_kid<'a>(&self, fpr: &str) -> Option<&[u8]>;
|
||||
|
||||
fn strip_userids(tpk: TPK) -> Result<TPK> {
|
||||
let pile = tpk.to_packet_pile().into_children().filter(|pkt| {
|
||||
match pkt {
|
||||
&Packet::PublicKey(_) | &Packet::PublicSubkey(_) => true,
|
||||
&Packet::Signature(ref sig) =>
|
||||
&Packet::Signature(ref sig) => {
|
||||
sig.sigtype() == SignatureType::DirectKey
|
||||
|| sig.sigtype() == SignatureType::SubkeyBinding
|
||||
|| sig.sigtype() == SignatureType::PrimaryKeyBinding,
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
TPK::from_packet_pile(PacketPile::from_packets(pile))
|
||||
.map_err(|e| format!("sequoia_openpgp: {}", e).into())
|
||||
.map_err(|e| format!("openpgp: {}", e).into())
|
||||
}
|
||||
|
||||
fn tpk_into_bytes(tpk: &TPK) -> Result<Vec<u8>> {
|
||||
|
@ -108,25 +114,48 @@ pub trait Database: Sync + Send {
|
|||
tpk.serialize(&mut cur).map(|_| cur.into_inner()).map_err(|e| format!("{}", e).into())
|
||||
}
|
||||
|
||||
fn link_subkeys(&self, fpr: &Fingerprint, subkeys: Vec<sequoia_openpgp::Fingerprint>) -> Result<()> {
|
||||
// link (subkey) kid & and subkey fpr
|
||||
self.link_kid(&fpr.clone().into(), &fpr);
|
||||
|
||||
for sub_fpr in subkeys {
|
||||
let sub_fpr = Fingerprint::try_from(sub_fpr)?;
|
||||
|
||||
self.link_kid(&sub_fpr.clone().into(), &fpr);
|
||||
self.link_fpr(&sub_fpr, &fpr);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge_or_publish(&self, mut tpk: TPK) -> Result<Vec<(Email, String)>> {
|
||||
use sequoia_openpgp::RevocationStatus;
|
||||
|
||||
let fpr = Fingerprint::try_from(tpk.primary().fingerprint())?;
|
||||
let mut ret = Vec::default();
|
||||
|
||||
// update verify tokens
|
||||
for uid in tpk.userids() {
|
||||
let email = Email::try_from(uid.userid().clone())?;
|
||||
match uid.revoked() {
|
||||
RevocationStatus::Revoked(_) => { /* skip */ }
|
||||
RevocationStatus::CouldBe(_) |
|
||||
RevocationStatus::NotAsFarAsWeKnow => {
|
||||
let email = Email::try_from(uid.userid().clone())?;
|
||||
|
||||
if self.by_email(&email).is_none() {
|
||||
let payload = Verify::new(
|
||||
uid.userid(),
|
||||
&uid.selfsigs().collect::<Vec<_>>(),
|
||||
fpr.clone())?;
|
||||
if self.by_email(&email).is_none() {
|
||||
let payload = Verify::new(
|
||||
uid.userid(),
|
||||
&uid.selfsigs().collect::<Vec<_>>(),
|
||||
fpr.clone())?;
|
||||
|
||||
// XXX: send mail
|
||||
ret.push((email, self.new_verify_token(payload)?));
|
||||
ret.push((email, self.new_verify_token(payload)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let subkeys = tpk.subkeys().map(|s| s.subkey().fingerprint()).collect::<Vec<_>>();
|
||||
|
||||
tpk = Self::strip_userids(tpk)?;
|
||||
|
||||
for _ in 0..100 /* while cas failed */ {
|
||||
|
@ -134,10 +163,11 @@ pub trait Database: Sync + Send {
|
|||
match self.by_fpr(&fpr).map(|x| x.to_vec()) {
|
||||
Some(old) => {
|
||||
let new = TPK::from_bytes(&old).unwrap();
|
||||
let new = new.merge(tpk.clone()).unwrap();
|
||||
let new = Self::tpk_into_bytes(&new)?;
|
||||
let tpk = new.merge(tpk.clone()).unwrap();
|
||||
let new = Self::tpk_into_bytes(&tpk)?;
|
||||
|
||||
if self.compare_and_swap(&fpr, Some(&old), Some(&new))? {
|
||||
self.link_subkeys(&fpr, subkeys)?;
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +176,7 @@ pub trait Database: Sync + Send {
|
|||
let fresh = Self::tpk_into_bytes(&tpk)?;
|
||||
|
||||
if self.compare_and_swap(&fpr, None, Some(&fresh))? {
|
||||
self.link_subkeys(&fpr, subkeys)?;
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use errors::Result;
|
||||
use database::{Verify, Delete, Database, Filesystem, Memory};
|
||||
use types::{Fingerprint, Email};
|
||||
use types::{Fingerprint, Email, KeyID};
|
||||
|
||||
pub enum Polymorphic {
|
||||
Memory(Memory),
|
||||
|
@ -29,6 +29,34 @@ impl Database for Polymorphic {
|
|||
}
|
||||
}
|
||||
|
||||
fn link_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.link_fpr(from, fpr),
|
||||
&Polymorphic::Filesystem(ref db) => db.link_fpr(from, fpr),
|
||||
}
|
||||
}
|
||||
|
||||
fn unlink_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.unlink_fpr(from, fpr),
|
||||
&Polymorphic::Filesystem(ref db) => db.unlink_fpr(from, fpr),
|
||||
}
|
||||
}
|
||||
|
||||
fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint) {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.link_kid(kid, fpr),
|
||||
&Polymorphic::Filesystem(ref db) => db.link_kid(kid, fpr),
|
||||
}
|
||||
}
|
||||
|
||||
fn unlink_kid(&self, kid: &KeyID, fpr: &Fingerprint) {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.unlink_kid(kid, fpr),
|
||||
&Polymorphic::Filesystem(ref db) => db.unlink_kid(kid, fpr),
|
||||
}
|
||||
}
|
||||
|
||||
fn link_email(&self, email: &Email, fpr: &Fingerprint) {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.link_email(email, fpr),
|
||||
|
@ -70,4 +98,11 @@ impl Database for Polymorphic {
|
|||
&Polymorphic::Filesystem(ref db) => db.by_email(email),
|
||||
}
|
||||
}
|
||||
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<Box<[u8]>> {
|
||||
match self {
|
||||
&Polymorphic::Memory(ref db) => db.by_kid(kid),
|
||||
&Polymorphic::Filesystem(ref db) => db.by_kid(kid),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ use std::str::FromStr;
|
|||
|
||||
use database::Database;
|
||||
use sequoia_openpgp::tpk::{TPKBuilder, UserIDBinding};
|
||||
use sequoia_openpgp::{Packet, packet::UserID, TPK, PacketPile};
|
||||
use types::{Email, Fingerprint};
|
||||
use sequoia_openpgp::{Packet, packet::UserID, TPK, PacketPile, parse::Parse};
|
||||
use types::{KeyID, Email, Fingerprint};
|
||||
|
||||
pub fn test_uid_verification<D: Database>(db: &mut D) {
|
||||
let str_uid1 = "Test A <test_a@example.com>";
|
||||
|
@ -275,3 +275,45 @@ pub fn test_uid_deletion<D: Database>(db: &mut D) {
|
|||
assert!(db.by_email(&email1).is_none());
|
||||
assert!(db.by_email(&email2).is_none());
|
||||
}
|
||||
|
||||
pub fn test_subkey_lookup<D: Database>(db: &mut D) {
|
||||
let tpk = TPKBuilder::default()
|
||||
.add_userid("Testy <test@example.com>")
|
||||
.add_signing_subkey()
|
||||
.add_encryption_subkey()
|
||||
.generate().unwrap().0;
|
||||
|
||||
// upload key
|
||||
let _ = db.merge_or_publish(tpk.clone()).unwrap();
|
||||
let primary_fpr = Fingerprint::try_from(tpk.fingerprint()).unwrap();
|
||||
let sub1_fpr = Fingerprint::try_from(tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap();
|
||||
let sub2_fpr = Fingerprint::try_from(tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap();
|
||||
|
||||
let raw1 = db.by_fpr(&primary_fpr).unwrap();
|
||||
let raw2 = db.by_fpr(&sub1_fpr).unwrap();
|
||||
let raw3 = db.by_fpr(&sub2_fpr).unwrap();
|
||||
|
||||
assert_eq!(raw1, raw2);
|
||||
assert_eq!(raw1, raw3);
|
||||
}
|
||||
|
||||
pub fn test_kid_lookup<D: Database>(db: &mut D) {
|
||||
let tpk = TPKBuilder::default()
|
||||
.add_userid("Testy <test@example.com>")
|
||||
.add_signing_subkey()
|
||||
.add_encryption_subkey()
|
||||
.generate().unwrap().0;
|
||||
|
||||
// upload key
|
||||
let _ = db.merge_or_publish(tpk.clone()).unwrap();
|
||||
let primary_kid = KeyID::try_from(tpk.fingerprint()).unwrap();
|
||||
let sub1_kid = KeyID::try_from(tpk.subkeys().next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap();
|
||||
let sub2_kid = KeyID::try_from(tpk.subkeys().skip(1).next().map(|x| x.subkey().fingerprint()).unwrap()).unwrap();
|
||||
|
||||
let raw1 = db.by_kid(&primary_kid).unwrap();
|
||||
let raw2 = db.by_kid(&sub1_kid).unwrap();
|
||||
let raw3 = db.by_kid(&sub2_kid).unwrap();
|
||||
|
||||
assert_eq!(raw1, raw2);
|
||||
assert_eq!(raw1, raw3);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,7 @@ extern crate time;
|
|||
extern crate url;
|
||||
extern crate hex;
|
||||
|
||||
#[cfg(not(test))] #[macro_use] extern crate rocket;
|
||||
#[cfg(test)] #[macro_use] extern crate rocket;
|
||||
#[macro_use] extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
extern crate multipart;
|
||||
|
||||
|
|
49
src/types.rs
49
src/types.rs
|
@ -74,3 +74,52 @@ impl FromStr for Fingerprint {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)]
|
||||
pub struct KeyID([u8; 8]);
|
||||
|
||||
impl TryFrom<sequoia_openpgp::Fingerprint> for KeyID {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(fpr: sequoia_openpgp::Fingerprint) -> Result<Self> {
|
||||
match fpr {
|
||||
sequoia_openpgp::Fingerprint::V4(a) => Ok(Fingerprint(a).into()),
|
||||
sequoia_openpgp::Fingerprint::Invalid(_) => Err("invalid fingerprint".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fingerprint> for KeyID {
|
||||
fn from(fpr: Fingerprint) -> KeyID {
|
||||
let mut arr = [0u8; 8];
|
||||
|
||||
arr.copy_from_slice(&fpr.0[12..20]);
|
||||
KeyID(arr)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for KeyID {
|
||||
fn to_string(&self) -> String {
|
||||
format!("0x{}", hex::encode(&self.0[..]))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for KeyID {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<KeyID> {
|
||||
if !s.starts_with("0x") || s.len() != 16 + 2 {
|
||||
return Err(format!("'{}' is not a valid long key ID", s).into());
|
||||
}
|
||||
|
||||
let vec = hex::decode(&s[2..])?;
|
||||
if vec.len() == 8 {
|
||||
let mut arr = [0u8; 8];
|
||||
|
||||
arr.copy_from_slice(&vec[..]);
|
||||
Ok(KeyID(arr))
|
||||
} else {
|
||||
Err(format!("'{}' is not a valid long key ID", s).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::path::{Path, PathBuf};
|
|||
mod upload;
|
||||
|
||||
use database::{Polymorphic, Database};
|
||||
use types::{Fingerprint, Email};
|
||||
use types::{Fingerprint, Email, KeyID};
|
||||
use errors::Result;
|
||||
use Opt;
|
||||
|
||||
|
@ -140,6 +140,21 @@ fn by_email(db: rocket::State<Polymorphic>, email: String)
|
|||
}
|
||||
}
|
||||
|
||||
#[get("/by-kid/<kid>")]
|
||||
fn by_kid(db: rocket::State<Polymorphic>, kid: String)
|
||||
-> result::Result<String, Custom<String>>
|
||||
{
|
||||
let maybe_key = match KeyID::from_str(&kid) {
|
||||
Ok(ref key) => db.by_kid(key),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
match maybe_key {
|
||||
Some(ref bytes) => process_key(bytes),
|
||||
None => Ok("No such key :-(".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/vks/verify/<token>")]
|
||||
fn verify(db: rocket::State<Polymorphic>, token: String)
|
||||
-> result::Result<Template, Custom<String>>
|
||||
|
@ -305,6 +320,7 @@ pub fn serve(opt: &Opt, db: Polymorphic) -> Result<()> {
|
|||
// nginx-supported lookup
|
||||
by_email,
|
||||
by_fpr,
|
||||
by_kid,
|
||||
// HKP
|
||||
lookup,
|
||||
upload::multipart_upload,
|
||||
|
|
Loading…
Reference in New Issue