use uuencode for userids and hex for fprs
This commit is contained in:
parent
c097e963fa
commit
7844c81774
|
@ -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"
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
<p class="lead">The verifying PGP key server. Powered by p≡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>
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)? {}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
10
src/main.rs
10
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()); }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue