diff --git a/Cargo.toml b/Cargo.toml index 61f4322..93c95a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,12 @@ rand = "0.5" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -base64 = "0.9" time = "0.1" tempfile = "3.0" parking_lot = "0.6" structopt = "0.2" +url = "1.6" +hex = "0.3" [dependencies.rocket_contrib] version = "0" diff --git a/dist/templates/index.html.hbs b/dist/templates/index.html.hbs index a2c1715..a8da66b 100644 --- a/dist/templates/index.html.hbs +++ b/dist/templates/index.html.hbs @@ -3,8 +3,9 @@

The verifying PGP key server. Powered by p≡pnology!

Search for keys

-
- + + +
diff --git a/src/database/fs.rs b/src/database/fs.rs index 4fa41d4..e083f28 100644 --- a/src/database/fs.rs +++ b/src/database/fs.rs @@ -6,11 +6,11 @@ use std::os::unix::fs::symlink; use tempfile; use serde_json; -use openpgp::packet::UserID; -use base64; +use url; -use database::{Verify, Delete, Fingerprint, Database}; +use database::{Verify, Delete, Database}; use Result; +use types::{Email, Fingerprint}; pub struct Filesystem { base: PathBuf, @@ -48,7 +48,7 @@ impl Filesystem { create_dir_all(base.join("deletion_tokens"))?; create_dir_all(base.join("scratch_pad"))?; create_dir_all(base.join("public").join("by-fpr"))?; - create_dir_all(base.join("public").join("by-uid"))?; + create_dir_all(base.join("public").join("by-email"))?; info!("Opened base dir '{}'", base.display()); Ok(Filesystem{ @@ -127,10 +127,10 @@ impl Database for Filesystem { } } - fn link_userid(&self, uid: &UserID, fpr: &Fingerprint) { - let uid = base64::encode_config(uid.userid(), base64::URL_SAFE); + fn link_email(&self, email: &Email, fpr: &Fingerprint) { + let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); let target = self.base.join("public").join("by-fpr").join(fpr.to_string()); - let link = self.base.join("public").join("by-uid").join(uid); + let link = self.base.join("public").join("by-email").join(email); if link.exists() { let _ = remove_file(link.clone()); @@ -139,9 +139,9 @@ impl Database for Filesystem { let _ = symlink(target, link); } - fn unlink_userid(&self, uid: &UserID, fpr: &Fingerprint) { - let uid = base64::encode_config(uid.userid(), base64::URL_SAFE); - let link = self.base.join("public").join("by-uid").join(uid); + fn unlink_email(&self, email: &Email, fpr: &Fingerprint) { + let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); + let link = self.base.join("public").join("by-email").join(email); match read_link(link.clone()) { Ok(target) => { @@ -187,10 +187,11 @@ impl Database for Filesystem { } // XXX: slow - fn by_uid(&self, uid: &str) -> Option> { + fn by_email(&self, email: &Email) -> Option> { use std::fs; - let path = self.base.join("public").join("by-uid").join(uid); + let email = url::form_urlencoded::byte_serialize(email.to_string().as_bytes()).collect::(); + let path = self.base.join("public").join("by-email").join(email); fs::canonicalize(path).ok() .and_then(|p| { diff --git a/src/database/memory.rs b/src/database/memory.rs index d6ba79a..a1600ec 100644 --- a/src/database/memory.rs +++ b/src/database/memory.rs @@ -1,15 +1,13 @@ use std::collections::HashMap; use parking_lot::Mutex; -use openpgp::packet::UserID; -use base64; - -use database::{Verify, Delete, Fingerprint, Database}; +use database::{Verify, Delete, Database}; +use types::{Email, Fingerprint}; use Result; pub struct Memory { fpr: Mutex>>, - userid: Mutex>, + email: Mutex>, verify_token: Mutex>, delete_token: Mutex>, } @@ -18,7 +16,7 @@ impl Default for Memory { fn default() -> Self { Memory{ fpr: Mutex::new(HashMap::default()), - userid: Mutex::new(HashMap::default()), + email: Mutex::new(HashMap::default()), verify_token: Mutex::new(HashMap::default()), delete_token: Mutex::new(HashMap::default()), } @@ -56,14 +54,12 @@ impl Database for Memory { } } - fn link_userid(&self, uid: &UserID, fpr: &Fingerprint) { - let uid = base64::encode_config(uid.userid(), base64::URL_SAFE); - self.userid.lock().insert(uid.to_string(), fpr.clone()); + fn link_email(&self, email: &Email, fpr: &Fingerprint) { + self.email.lock().insert(email.clone(), fpr.clone()); } - fn unlink_userid(&self, uid: &UserID, _: &Fingerprint) { - let uid = base64::encode_config(uid.userid(), base64::URL_SAFE); - self.userid.lock().remove(&uid.to_string()); + fn unlink_email(&self, email: &Email, _: &Fingerprint) { + self.email.lock().remove(email); } // (verified uid, fpr) @@ -80,11 +76,11 @@ impl Database for Memory { self.fpr.lock().get(fpr).map(|x| x.clone()) } - fn by_uid(&self, uid: &str) -> Option> { - let userid = self.userid.lock(); + fn by_email(&self, email: &Email) -> Option> { + let by_email = self.email.lock(); let fprs = self.fpr.lock(); - userid.get(uid).and_then(|fpr| fprs.get(fpr).map(|x| x.clone())) + by_email.get(email).and_then(|fpr| fprs.get(fpr).map(|x| x.clone())) } } diff --git a/src/database/mod.rs b/src/database/mod.rs index d5b3a90..1625caa 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,14 +1,10 @@ -use std::result; use std::io::Cursor; -use std::str::FromStr; use std::convert::TryFrom; -use std::fmt; -use serde::{Serializer, Deserializer, de}; use time; -use openpgp::{self, packet::Signature, TPK, packet::UserID, Packet, PacketPile, constants::SignatureType}; -use base64; -use {Error, Result}; +use openpgp::{packet::Signature, TPK, packet::UserID, Packet, PacketPile, constants::SignatureType}; +use Result; +use types::{Fingerprint, Email}; mod fs; pub use self::fs::Filesystem; @@ -16,53 +12,16 @@ mod memory; pub use self::memory::Memory; mod poly; pub use self::poly::Polymorphic; + +#[cfg(test)] mod test; - -#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] -pub struct Fingerprint([u8; 20]); - -impl TryFrom for Fingerprint { - type Error = Error; - - fn try_from(fpr: openpgp::Fingerprint) -> Result { - match fpr { - openpgp::Fingerprint::V4(a) => Ok(Fingerprint(a)), - openpgp::Fingerprint::Invalid(_) => Err("invalid fingerprint".into()), - } - } -} - -impl ToString for Fingerprint { - fn to_string(&self) -> String { - base64::encode_config(&self.0[..], base64::URL_SAFE) - } -} - -impl FromStr for Fingerprint { - type Err = Error; - - fn from_str(s: &str) -> Result { - let vec = base64::decode_config(s, base64::URL_SAFE) - .map_err(|e| format!("'{}' is not a valid fingerprint: {}", s, e))?; - if vec.len() == 20 { - let mut arr = [0u8; 20]; - - arr.copy_from_slice(&vec[..]); - Ok(Fingerprint(arr)) - } else { - Err(format!("'{}' is not a valid fingerprint", s).into()) - } - } -} - #[derive(Serialize,Deserialize,Clone,Debug)] pub struct Verify { created: i64, packets: Box<[u8]>, fpr: Fingerprint, - #[serde(deserialize_with = "Verify::deserialize_userid", serialize_with = "Verify::serialize_userid")] - uid: UserID, + email: Email, } impl Verify { @@ -70,7 +29,6 @@ impl Verify { use openpgp::serialize::Serialize; let mut cur = Cursor::new(Vec::default()); - let res: Result<()> = uid.serialize(&mut cur) .map_err(|e| format!("openpgp: {}", e).into()); res?; @@ -85,50 +43,9 @@ impl Verify { created: time::now().to_timespec().sec, packets: cur.into_inner().into(), fpr: fpr, - uid: uid.clone(), + email: Email::try_from(uid.clone())?, }) } - fn deserialize_userid<'de, D>(de: D) -> result::Result where D: Deserializer<'de> { - de.deserialize_bytes(UserIDVisitor) - } - - fn serialize_userid(uid: &UserID, ser: S) -> result::Result where S: Serializer { - ser.serialize_bytes(uid.userid()) - } -} - -struct UserIDVisitor; - -impl<'de> de::Visitor<'de> for UserIDVisitor { - type Value = UserID; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a OpenPGP User ID") - } - - fn visit_bytes(self, s: &[u8]) -> result::Result - where - E: de::Error, - { - let mut uid = UserID::new(); - uid.set_userid_from_bytes(s); - Ok(uid) - } - - fn visit_seq(self, mut seq: A) -> result::Result - where - A: de::SeqAccess<'de> - { - let mut buf = Vec::default(); - - while let Some(x) = seq.next_element()? { - buf.push(x); - } - - let mut uid = UserID::new(); - uid.set_userid_from_bytes(&buf); - Ok(uid) - } } #[derive(Serialize,Deserialize,Clone,Debug)] @@ -155,8 +72,8 @@ pub trait Database: Sync + Send { fn compare_and_swap(&self, fpr: &Fingerprint, present: Option<&[u8]>, new: Option<&[u8]>) -> Result; - fn link_userid(&self, uid: &UserID, fpr: &Fingerprint); - fn unlink_userid(&self, uid: &UserID, fpr: &Fingerprint); + fn link_email(&self, email: &Email, fpr: &Fingerprint); + fn unlink_email(&self, email: &Email, fpr: &Fingerprint); // (verified uid, fpr) fn pop_verify_token(&self, token: &str) -> Option; @@ -164,7 +81,7 @@ pub trait Database: Sync + Send { fn pop_delete_token(&self, token: &str) -> Option; fn by_fpr(&self, fpr: &Fingerprint) -> Option>; - fn by_uid(&self, uid: &str) -> Option>; + fn by_email(&self, email: &Email) -> Option>; // fn by_kid<'a>(&self, fpr: &str) -> Option<&[u8]>; fn strip_userids(tpk: TPK) -> Result { @@ -187,18 +104,22 @@ pub trait Database: Sync + Send { tpk.serialize(&mut cur).map(|_| cur.into_inner()).map_err(|e| format!("{}", e).into()) } - fn merge_or_publish(&self, mut tpk: TPK) -> Result> { + fn merge_or_publish(&self, mut tpk: TPK) -> Result> { let fpr = Fingerprint::try_from(tpk.primary().fingerprint())?; let mut ret = Vec::default(); // update verify tokens for uid in tpk.userids() { - let enc = base64::encode_config(&format!("{}", uid.userid()), base64::URL_SAFE); - if self.by_uid(&enc).is_none() { - let payload = Verify::new(uid.userid(), &uid.selfsigs().collect::>(), fpr.clone())?; + let email = Email::try_from(uid.userid().clone())?; + + if self.by_email(&email).is_none() { + let payload = Verify::new( + uid.userid(), + &uid.selfsigs().collect::>(), + fpr.clone())?; // XXX: send mail - ret.push((uid.userid().clone(),self.new_verify_token(payload)?)); + ret.push((email, self.new_verify_token(payload)?)); } } @@ -238,9 +159,9 @@ pub trait Database: Sync + Send { // cas(tpk, merged) // } // } - fn verify_token(&self, token: &str) -> Result> { + fn verify_token(&self, token: &str) -> Result> { match self.pop_verify_token(token) { - Some(Verify{ created, packets, fpr, uid }) => { + Some(Verify{ created, packets, fpr, email }) => { let now = time::now().to_timespec().sec; if created > now || now - created > 3 * 3600 { return Ok(None); } @@ -251,8 +172,8 @@ pub trait Database: Sync + Send { new.extend(packets.into_iter()); if self.compare_and_swap(&fpr, Some(&old), Some(&new))? { - self.link_userid(&uid, &fpr); - return Ok(Some((uid.clone(), fpr.clone()))); + self.link_email(&email, &fpr); + return Ok(Some((email.clone(), fpr.clone()))); } } None => { @@ -296,7 +217,7 @@ pub trait Database: Sync + Send { }; for uid in tpk.userids() { - self.unlink_userid(uid.userid(), &fpr); + self.unlink_email(&Email::try_from(uid.userid().clone())?, &fpr); } while !self.compare_and_swap(&fpr, Some(&old), None)? {} diff --git a/src/database/poly.rs b/src/database/poly.rs index 58c5676..9d30287 100644 --- a/src/database/poly.rs +++ b/src/database/poly.rs @@ -1,7 +1,6 @@ use errors::Result; -use database::{Verify, Delete, Database, Fingerprint, Filesystem, Memory}; - -use openpgp::packet::UserID; +use database::{Verify, Delete, Database, Filesystem, Memory}; +use types::{Fingerprint, Email}; pub enum Polymorphic { Memory(Memory), @@ -30,17 +29,17 @@ impl Database for Polymorphic { } } - fn link_userid(&self, uid: &UserID, fpr: &Fingerprint) { + fn link_email(&self, email: &Email, fpr: &Fingerprint) { match self { - &Polymorphic::Memory(ref db) => db.link_userid(uid, fpr), - &Polymorphic::Filesystem(ref db) => db.link_userid(uid, fpr), + &Polymorphic::Memory(ref db) => db.link_email(email, fpr), + &Polymorphic::Filesystem(ref db) => db.link_email(email, fpr), } } - fn unlink_userid(&self, uid: &UserID, fpr: &Fingerprint) { + fn unlink_email(&self, email: &Email, fpr: &Fingerprint) { match self { - &Polymorphic::Memory(ref db) => db.unlink_userid(uid, fpr), - &Polymorphic::Filesystem(ref db) => db.unlink_userid(uid, fpr), + &Polymorphic::Memory(ref db) => db.unlink_email(email, fpr), + &Polymorphic::Filesystem(ref db) => db.unlink_email(email, fpr), } } @@ -65,10 +64,10 @@ impl Database for Polymorphic { } } - fn by_uid(&self, uid: &str) -> Option> { + fn by_email(&self, email: &Email) -> Option> { match self { - &Polymorphic::Memory(ref db) => db.by_uid(uid), - &Polymorphic::Filesystem(ref db) => db.by_uid(uid), + &Polymorphic::Memory(ref db) => db.by_email(email), + &Polymorphic::Filesystem(ref db) => db.by_email(email), } } } diff --git a/src/database/test.rs b/src/database/test.rs index a1a0e86..eb8f246 100644 --- a/src/database/test.rs +++ b/src/database/test.rs @@ -17,11 +17,12 @@ use std::convert::TryFrom; use std::thread; use std::sync::atomic::{Ordering, AtomicBool}; +use std::str::FromStr; -use database::{Fingerprint, Database}; +use database::Database; use openpgp::tpk::{TPKBuilder, UserIDBinding}; use openpgp::{Packet, packet::UserID, TPK, PacketPile}; -use base64; +use types::{Email, Fingerprint}; pub fn test_uid_verification(db: &mut D) { let str_uid1 = "Test A "; @@ -36,8 +37,8 @@ pub fn test_uid_verification(db: &mut D) { uid1.set_userid_from_bytes(str_uid1.as_bytes()); uid2.set_userid_from_bytes(str_uid2.as_bytes()); - let b64_uid1 = base64::encode_config(str_uid1, base64::URL_SAFE); - let b64_uid2 = base64::encode_config(str_uid2, base64::URL_SAFE); + let email1 = Email::from_str(str_uid1).unwrap(); + let email2 = Email::from_str(str_uid2).unwrap(); // upload key let tokens = db.merge_or_publish(tpk.clone()).unwrap(); @@ -56,8 +57,8 @@ pub fn test_uid_verification(db: &mut D) { } // fail to fetch by uid - assert!(db.by_uid(&b64_uid1).is_none()); - assert!(db.by_uid(&b64_uid2).is_none()); + assert!(db.by_email(&email1).is_none()); + assert!(db.by_email(&email2).is_none()); // verify 1st uid assert!(db.verify_token(&tokens[0].1).unwrap().is_some()); @@ -74,13 +75,13 @@ pub fn test_uid_verification(db: &mut D) { let uid = key.userids().next().unwrap().userid().clone(); assert!((uid == uid1) ^ (uid == uid2)); - let b64_uid = base64::encode_config(&String::from_utf8(uid.userid().to_vec()).unwrap(), base64::URL_SAFE); - assert_eq!(db.by_uid(&b64_uid).unwrap(), raw); + let email = Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()).unwrap(); + assert_eq!(db.by_email(&email).unwrap(), raw); - if b64_uid1 == b64_uid { - assert!(db.by_uid(&b64_uid2).is_none()); - } else if b64_uid2 == b64_uid { - assert!(db.by_uid(&b64_uid1).is_none()); + if email1 == email { + assert!(db.by_email(&email2).is_none()); + } else if email2 == email { + assert!(db.by_email(&email1).is_none()); } else { unreachable!() } @@ -101,13 +102,13 @@ pub fn test_uid_verification(db: &mut D) { let uid = key.userids().next().unwrap().userid().clone(); assert!((uid == uid1) ^ (uid == uid2)); - let b64_uid = base64::encode_config(&String::from_utf8(uid.userid().to_vec()).unwrap(), base64::URL_SAFE); - assert_eq!(db.by_uid(&b64_uid).unwrap(), raw); + let email = Email::from_str(&String::from_utf8(uid.userid().to_vec()).unwrap()).unwrap(); + assert_eq!(db.by_email(&email).unwrap(), raw); - if b64_uid1 == b64_uid { - assert!(db.by_uid(&b64_uid2).is_none()); - } else if b64_uid2 == b64_uid { - assert!(db.by_uid(&b64_uid1).is_none()); + if email1 == email { + assert!(db.by_email(&email2).is_none()); + } else if email2 == email { + assert!(db.by_email(&email1).is_none()); } else { unreachable!() } @@ -128,13 +129,13 @@ pub fn test_uid_verification(db: &mut D) { let myuid1 = key.userids().next().unwrap().userid().clone(); let myuid2 = key.userids().skip(1).next().unwrap().userid().clone(); - assert_eq!(db.by_uid(&b64_uid1).unwrap(), raw); - assert_eq!(db.by_uid(&b64_uid2).unwrap(), raw); + assert_eq!(db.by_email(&email1).unwrap(), raw); + assert_eq!(db.by_email(&email2).unwrap(), raw); assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); } // upload again - assert_eq!(db.merge_or_publish(tpk.clone()).unwrap(), Vec::<(UserID,String)>::default()); + assert_eq!(db.merge_or_publish(tpk.clone()).unwrap(), Vec::<(Email,String)>::default()); // publish w/ one uid less { @@ -150,7 +151,7 @@ pub fn test_uid_verification(db: &mut D) { let pile = PacketPile::from_packets(packets.collect()); let short_tpk = TPK::from_packet_pile(pile).unwrap(); - assert_eq!(db.merge_or_publish(short_tpk.clone()).unwrap(), Vec::<(UserID,String)>::default()); + assert_eq!(db.merge_or_publish(short_tpk.clone()).unwrap(), Vec::<(Email,String)>::default()); // fetch by fpr let raw = db.by_fpr(&fpr).unwrap(); @@ -163,8 +164,8 @@ pub fn test_uid_verification(db: &mut D) { let myuid1 = key.userids().next().unwrap().userid().clone(); let myuid2 = key.userids().skip(1).next().unwrap().userid().clone(); - assert_eq!(db.by_uid(&b64_uid1).unwrap(), raw); - assert_eq!(db.by_uid(&b64_uid2).unwrap(), raw); + assert_eq!(db.by_email(&email1).unwrap(), raw); + assert_eq!(db.by_email(&email2).unwrap(), raw); assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); } @@ -183,7 +184,7 @@ pub fn test_uid_verification(db: &mut D) { let mut uid3 = UserID::new(); uid3.set_userid_from_bytes(str_uid3.as_bytes()); - let b64_uid3 = base64::encode_config(str_uid3, base64::URL_SAFE); + let email3 = Email::from_str(str_uid3).unwrap(); let key = tpk.primary(); let bind = UserIDBinding::new(key, uid3.clone(), key).unwrap(); @@ -207,10 +208,10 @@ pub fn test_uid_verification(db: &mut D) { let myuid1 = key.userids().next().unwrap().userid().clone(); let myuid2 = key.userids().skip(1).next().unwrap().userid().clone(); - assert_eq!(db.by_uid(&b64_uid1).unwrap(), raw); - assert_eq!(db.by_uid(&b64_uid2).unwrap(), raw); + assert_eq!(db.by_email(&email1).unwrap(), raw); + assert_eq!(db.by_email(&email2).unwrap(), raw); assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); - assert!(db.by_uid(&b64_uid3).is_none()); + assert!(db.by_email(&email3).is_none()); } } @@ -227,8 +228,8 @@ pub fn test_uid_deletion(db: &mut D) { uid1.set_userid_from_bytes(str_uid1.as_bytes()); uid2.set_userid_from_bytes(str_uid2.as_bytes()); - let b64_uid1 = base64::encode_config(str_uid1, base64::URL_SAFE); - let b64_uid2 = base64::encode_config(str_uid2, base64::URL_SAFE); + let email1 = Email::from_str(str_uid1).unwrap(); + let email2 = Email::from_str(str_uid2).unwrap(); // upload key and verify uids let tokens = db.merge_or_publish(tpk.clone()).unwrap(); @@ -255,8 +256,8 @@ pub fn test_uid_deletion(db: &mut D) { let myuid1 = key.userids().next().unwrap().userid().clone(); let myuid2 = key.userids().skip(1).next().unwrap().userid().clone(); - assert_eq!(db.by_uid(&b64_uid1).unwrap(), raw); - assert_eq!(db.by_uid(&b64_uid2).unwrap(), raw); + assert_eq!(db.by_email(&email1).unwrap(), raw); + assert_eq!(db.by_email(&email2).unwrap(), raw); assert!(((myuid1 == uid1) & (myuid2 == uid2)) ^ ((myuid1 == uid2) & (myuid2 == uid1))); } @@ -265,14 +266,14 @@ pub fn test_uid_deletion(db: &mut D) { // check it's gone assert!(db.by_fpr(&fpr).is_none()); - assert!(db.by_uid(&b64_uid1).is_none()); - assert!(db.by_uid(&b64_uid2).is_none()); + assert!(db.by_email(&email1).is_none()); + assert!(db.by_email(&email2).is_none()); // confirm deletion again assert!(!db.confirm_deletion(&del).unwrap()); // check it's still gone assert!(db.by_fpr(&fpr).is_none()); - assert!(db.by_uid(&b64_uid1).is_none()); - assert!(db.by_uid(&b64_uid2).is_none()); + assert!(db.by_email(&email1).is_none()); + assert!(db.by_email(&email2).is_none()); } diff --git a/src/main.rs b/src/main.rs index 11ebe91..78a3f67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,10 @@ extern crate serde; extern crate serde_json; extern crate time; -extern crate base64; +extern crate url; +extern crate hex; -#[cfg(not(test))] #[macro_use] extern crate rocket; +#[cfg(not(test))] extern crate rocket; #[cfg(test)] extern crate rocket; extern crate rocket_contrib; extern crate multipart; @@ -21,10 +22,11 @@ extern crate openpgp; extern crate rand; extern crate tempfile; extern crate parking_lot; -#[macro_use] extern crate structopt; +extern crate structopt; mod web; mod database; +mod types; mod errors { error_chain!{ @@ -33,10 +35,10 @@ mod errors { Io(::std::io::Error); Json(::serde_json::Error); Persist(::tempfile::PersistError); - Base64(::base64::DecodeError); RktConfig(::rocket::config::ConfigError); StringUtf8Error(::std::string::FromUtf8Error); StrUtf8Error(::std::str::Utf8Error); + HexError(::hex::FromHexError); } } } diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..e6e0fa4 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,76 @@ +use std::str::FromStr; +use std::convert::TryFrom; + +use openpgp::{self, packet::UserID}; +use {Error, Result}; + +#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] +pub struct Email(String); + +impl TryFrom for Email { + type Error = Error; + + fn try_from(uid: UserID) -> Result { + let email = String::from_utf8_lossy(uid.userid()); + + Self::from_str(&email) + } +} + +impl ToString for Email { + fn to_string(&self) -> String { self.0.clone() } +} + +impl FromStr for Email { + type Err = Error; + + fn from_str(s: &str) -> Result { + let segs = s.split(|c| c == '<' || c == '>').collect::>(); + + if segs.len() == 3 { + Ok(Email(segs[1].to_string())) + } else { + Ok(Email(s.to_string())) + } + } +} + +#[derive(Serialize,Deserialize,Clone,Debug,Hash,PartialEq,Eq)] +pub struct Fingerprint([u8; 20]); + +impl TryFrom for Fingerprint { + type Error = Error; + + fn try_from(fpr: openpgp::Fingerprint) -> Result { + match fpr { + openpgp::Fingerprint::V4(a) => Ok(Fingerprint(a)), + openpgp::Fingerprint::Invalid(_) => Err("invalid fingerprint".into()), + } + } +} + +impl ToString for Fingerprint { + fn to_string(&self) -> String { + format!("0x{}", hex::encode(&self.0[..])) + } +} + +impl FromStr for Fingerprint { + type Err = Error; + + fn from_str(s: &str) -> Result { + if !s.starts_with("0x") || s.len() != 20 + 2 { + return Err(format!("'{}' is not a valid fingerprint", s).into()); + } + + let vec = hex::decode(&s[2..])?; + if vec.len() == 20 { + let mut arr = [0u8; 20]; + + arr.copy_from_slice(&vec[..]); + Ok(Fingerprint(arr)) + } else { + Err(format!("'{}' is not a valid fingerprint", s).into()) + } + } +} diff --git a/src/web/mod.rs b/src/web/mod.rs index 58c0645..5646304 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -2,7 +2,6 @@ use rocket; use rocket::{State, Outcome}; use rocket::http::Status; use rocket::request::{self, Request, FromRequest}; -use rocket::response::content; use rocket::response::status::Custom; use rocket::http::uri::URI; use rocket::response::NamedFile; @@ -13,7 +12,8 @@ use std::path::{Path, PathBuf}; mod upload; -use database::{Polymorphic, Fingerprint, Database}; +use database::{Polymorphic, Database}; +use types::{Fingerprint, Email}; use errors::Result; use errors; use Opt; @@ -22,11 +22,17 @@ use std::str::FromStr; use std::result; mod queries { - use database::Fingerprint; + use types::{Fingerprint, Email}; pub enum Key { Fingerprint(Fingerprint), - UserID(String), + Email(Email), + } + + #[derive(Debug)] + pub enum Hkp { + Fingerprint(Fingerprint), + Email(Email), } } @@ -127,11 +133,10 @@ fn get_key(db: rocket::State, key: Option) { use std::io::Write; use openpgp::armor::{Writer, Kind}; - use std::str::FromStr; let maybe_key = match key { Some(queries::Key::Fingerprint(ref fpr)) => db.by_fpr(fpr), - Some(queries::Key::UserID(ref uid)) => db.by_uid(uid), + Some(queries::Key::Email(ref email)) => db.by_email(email), None => { return Ok("nothing to do".to_string()); } };