139 lines
4 KiB
Rust
139 lines
4 KiB
Rust
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>(T);
|
|
|
|
impl<T> fmt::Debug for Key<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "Key([redacted])")
|
|
}
|
|
}
|
|
|
|
impl<T> Key<T> {
|
|
pub fn new(v: T) -> Self {
|
|
Self(v)
|
|
}
|
|
pub fn secret(&self) -> &T {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<ST, DB: diesel::backend::Backend> diesel::Queryable<ST, DB> for Key<ed25519_dalek::SigningKey>
|
|
where
|
|
Vec<u8>: diesel::Queryable<ST, DB>,
|
|
{
|
|
type Row = <Vec<u8> as diesel::Queryable<ST, DB>>::Row;
|
|
|
|
fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
|
|
let v = <Vec<u8> as diesel::Queryable<ST, DB>>::build(row)?;
|
|
Ok(Key(ed25519_dalek::SigningKey::try_from(&*v)?))
|
|
}
|
|
}
|
|
|
|
impl<T: SqlType + TypedExpressionType> diesel::expression::AsExpression<T>
|
|
for Key<ed25519_dalek::SigningKey>
|
|
where
|
|
Vec<u8>: diesel::expression::AsExpression<T>,
|
|
{
|
|
type Expression = <Vec<u8> as diesel::expression::AsExpression<T>>::Expression;
|
|
|
|
fn as_expression(self) -> Self::Expression {
|
|
Vec::<u8>::from(self.0.to_bytes()).as_expression()
|
|
}
|
|
}
|
|
|
|
impl<'a, T: SqlType + TypedExpressionType> diesel::expression::AsExpression<T>
|
|
for &'a Key<ed25519_dalek::SigningKey>
|
|
where
|
|
&'a [u8]: diesel::expression::AsExpression<T>,
|
|
{
|
|
type Expression = <&'a [u8] as diesel::expression::AsExpression<T>>::Expression;
|
|
|
|
fn as_expression(self) -> Self::Expression {
|
|
self.0.as_bytes()[..].as_expression()
|
|
}
|
|
}
|
|
|
|
impl<'a, T: SqlType + TypedExpressionType> diesel::expression::AsExpression<T>
|
|
for &'a &'_ Key<ed25519_dalek::SigningKey>
|
|
where
|
|
&'a [u8]: diesel::expression::AsExpression<T>,
|
|
{
|
|
type Expression = <&'a [u8] as diesel::expression::AsExpression<T>>::Expression;
|
|
|
|
fn as_expression(self) -> Self::Expression {
|
|
self.0.as_bytes()[..].as_expression()
|
|
}
|
|
}
|
|
|
|
pub fn get_or_make_keys(db: &mut SqliteConnection) -> eyre::Result<Keys> {
|
|
db.transaction(|db| -> eyre::Result<Keys> {
|
|
let keys: Option<Keys> = 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<T, S: SignatureEncoding> {
|
|
#[serde(rename = "v")]
|
|
value: String,
|
|
#[serde(rename = "s", bound(deserialize = "S::Repr: TryFrom<Vec<u8>>"))]
|
|
#[serde_as(as = "Base64<UrlSafe, Unpadded>")]
|
|
signature: S::Repr,
|
|
#[serde(skip)]
|
|
_phantom: PhantomData<(fn() -> T, S)>,
|
|
}
|
|
|
|
impl<T: Serialize + DeserializeOwned, S: SignatureEncoding> Signed<T, S> {
|
|
pub fn sign<K>(value: &T, key: &K) -> Result<Self, serde_json::Error>
|
|
where
|
|
K: Signer<S>,
|
|
{
|
|
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<K>(&self, key: &K) -> eyre::Result<Result<T, SignatureError>>
|
|
where
|
|
K: Verifier<S>,
|
|
for<'a> eyre::Report: From<<S as TryFrom<&'a [u8]>>::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)?))
|
|
}
|
|
}
|