use uuencode for userids and hex for fprs

This commit is contained in:
seu 2018-10-24 19:45:11 +02:00
parent c097e963fa
commit 7844c81774
10 changed files with 194 additions and 191 deletions

View File

@ -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"

View File

@ -3,8 +3,9 @@
<p class="lead">The verifying PGP key server. Powered by p&equiv;pnology!
<h2>Search for keys</h2>
<form action="/search" method=POST>
<input type="search" id="query" name="query" placeholder="Email">
<form action="/pks/lookup" method=GET>
<input type="search" id="search" name="search" placeholder="Email">
<input type="hidden" id="op" name="op" value="get">
<input type="submit" value="Search">
</form>

View File

@ -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::<String>();
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::<String>();
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<Box<[u8]>> {
fn by_email(&self, email: &Email) -> Option<Box<[u8]>> {
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::<String>();
let path = self.base.join("public").join("by-email").join(email);
fs::canonicalize(path).ok()
.and_then(|p| {

View File

@ -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<HashMap<Fingerprint, Box<[u8]>>>,
userid: Mutex<HashMap<String, Fingerprint>>,
email: Mutex<HashMap<Email, Fingerprint>>,
verify_token: Mutex<HashMap<String, Verify>>,
delete_token: Mutex<HashMap<String, Delete>>,
}
@ -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<Box<[u8]>> {
let userid = self.userid.lock();
fn by_email(&self, email: &Email) -> Option<Box<[u8]>> {
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()))
}
}

View File

@ -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<openpgp::Fingerprint> for Fingerprint {
type Error = Error;
fn try_from(fpr: openpgp::Fingerprint) -> Result<Self> {
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<Fingerprint> {
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<UserID, D::Error> where D: Deserializer<'de> {
de.deserialize_bytes(UserIDVisitor)
}
fn serialize_userid<S>(uid: &UserID, ser: S) -> result::Result<S::Ok, S::Error> 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<E>(self, s: &[u8]) -> result::Result<Self::Value, E>
where
E: de::Error,
{
let mut uid = UserID::new();
uid.set_userid_from_bytes(s);
Ok(uid)
}
fn visit_seq<A>(self, mut seq: A) -> result::Result<Self::Value, A::Error>
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<bool>;
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<Verify>;
@ -164,7 +81,7 @@ pub trait Database: Sync + Send {
fn pop_delete_token(&self, token: &str) -> Option<Delete>;
fn by_fpr(&self, fpr: &Fingerprint) -> Option<Box<[u8]>>;
fn by_uid(&self, uid: &str) -> 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> {
@ -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<Vec<(UserID,String)>> {
fn merge_or_publish(&self, mut tpk: TPK) -> Result<Vec<(Email,String)>> {
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::<Vec<_>>(), 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::<Vec<_>>(),
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<Option<(UserID, Fingerprint)>> {
fn verify_token(&self, token: &str) -> Result<Option<(Email, Fingerprint)>> {
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)? {}

View File

@ -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<Box<[u8]>> {
fn by_email(&self, email: &Email) -> Option<Box<[u8]>> {
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),
}
}
}

View File

@ -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<D: Database>(db: &mut D) {
let str_uid1 = "Test A <test_a@example.com>";
@ -36,8 +37,8 @@ pub fn test_uid_verification<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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<D: Database>(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());
}

View File

@ -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);
}
}
}

76
src/types.rs Normal file
View File

@ -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<UserID> for Email {
type Error = Error;
fn try_from(uid: UserID) -> Result<Self> {
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<Email> {
let segs = s.split(|c| c == '<' || c == '>').collect::<Vec<_>>();
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<openpgp::Fingerprint> for Fingerprint {
type Error = Error;
fn try_from(fpr: openpgp::Fingerprint) -> Result<Self> {
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<Fingerprint> {
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())
}
}
}

View File

@ -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<Polymorphic>, key: Option<queries::Key>)
{
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()); }
};