use crate::models::{Keys, OnlyZero}; use core::fmt; use diesel::{ expression::TypedExpressionType, sql_types::SqlType, Connection, RunQueryDsl, SqliteConnection, }; use ed25519_dalek::{ ed25519::{signature::rand_core::OsRng, SignatureEncoding}, SignatureError, Signer, Verifier, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_with::{ base64::{Base64, UrlSafe}, formats::Unpadded, serde_as, }; use std::marker::PhantomData; #[derive(Copy, Clone)] pub struct Key(T); impl fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Key([redacted])") } } impl Key { pub fn new(v: T) -> Self { Self(v) } pub fn secret(&self) -> &T { &self.0 } } impl diesel::Queryable for Key where Vec: diesel::Queryable, { type Row = as diesel::Queryable>::Row; fn build(row: Self::Row) -> diesel::deserialize::Result { let v = as diesel::Queryable>::build(row)?; Ok(Key(ed25519_dalek::SigningKey::try_from(&*v)?)) } } impl diesel::expression::AsExpression for Key where Vec: diesel::expression::AsExpression, { type Expression = as diesel::expression::AsExpression>::Expression; fn as_expression(self) -> Self::Expression { Vec::::from(self.0.to_bytes()).as_expression() } } impl<'a, T: SqlType + TypedExpressionType> diesel::expression::AsExpression for &'a Key where &'a [u8]: diesel::expression::AsExpression, { type Expression = <&'a [u8] as diesel::expression::AsExpression>::Expression; fn as_expression(self) -> Self::Expression { self.0.as_bytes()[..].as_expression() } } impl<'a, T: SqlType + TypedExpressionType> diesel::expression::AsExpression for &'a &'_ Key where &'a [u8]: diesel::expression::AsExpression, { type Expression = <&'a [u8] as diesel::expression::AsExpression>::Expression; fn as_expression(self) -> Self::Expression { self.0.as_bytes()[..].as_expression() } } pub fn get_or_make_keys(db: &mut SqliteConnection) -> eyre::Result { db.transaction(|db| -> eyre::Result { let keys: Option = crate::schema::keys::dsl::keys.load(db)?.get(0).cloned(); if let Some(keys) = keys { return Ok(keys); } let keys = Keys { id: OnlyZero::Zero, email_unsubscribe_url: Key::new(ed25519_dalek::SigningKey::generate(&mut OsRng)), }; diesel::insert_into(crate::schema::keys::dsl::keys) .values(keys.clone()) .execute(db)?; Ok(keys) }) } #[serde_as] #[derive(Serialize, Deserialize)] pub struct Signed { #[serde(rename = "v")] value: String, #[serde(rename = "s", bound(deserialize = "S::Repr: TryFrom>"))] #[serde_as(as = "Base64")] signature: S::Repr, #[serde(skip)] _phantom: PhantomData<(fn() -> T, S)>, } impl Signed { pub fn sign(value: &T, key: &K) -> Result where K: Signer, { let value = serde_json::to_string(value)?; let signature = key.sign(value.as_bytes()).to_bytes(); Ok(Self { value, signature, _phantom: PhantomData, }) } pub fn verify(&self, key: &K) -> eyre::Result> where K: Verifier, for<'a> eyre::Report: From<>::Error>, { if let Err(e) = key.verify( &self.value.as_bytes(), &S::try_from(self.signature.as_ref())?, ) { return Ok(Err(e)); } Ok(Ok(serde_json::from_str(&self.value)?)) } }