use crate::macros::entity;
use crate::macros::pattern;
use crate::value::TryToValue;
use ed25519::Signature;
use ed25519_dalek::SignatureError;
use ed25519_dalek::SigningKey;
use ed25519_dalek::Verifier;
use ed25519_dalek::VerifyingKey;
use itertools::Itertools;
use ed25519::signature::Signer;
use crate::blob::schemas::longstring::LongString;
use crate::blob::schemas::simplearchive::SimpleArchive;
use crate::blob::Blob;
use crate::prelude::valueschemas::Handle;
use crate::query::find;
use crate::trible::TribleSet;
use crate::value::Value;
use crate::value::schemas::hash::Blake3;
use hifitime::Epoch;
pub enum ValidationError {
AmbiguousSignature,
MissingSignature,
FailedValidation,
}
impl From<SignatureError> for ValidationError {
fn from(_: SignatureError) -> Self {
ValidationError::FailedValidation
}
}
pub fn commit_metadata(
signing_key: &SigningKey,
parents: impl IntoIterator<Item = Value<Handle<Blake3, SimpleArchive>>>,
msg: Option<Value<Handle<Blake3, LongString>>>,
content: Option<Blob<SimpleArchive>>,
metadata: Option<Value<Handle<Blake3, SimpleArchive>>>,
) -> TribleSet {
let (content_handle, signed_by, signature, created_at) = match content.as_ref() {
Some(blob) => {
let now = Epoch::now().expect("system time");
let timestamp: Value<_> =
(now, now).try_to_value().expect("point interval");
(
Some(blob.get_handle()),
Some(signing_key.verifying_key()),
Some(signing_key.sign(&blob.bytes)),
Some(timestamp),
)
}
None => (None, None, None, None),
};
let parents: Vec<_> = parents.into_iter().collect();
let fragment = entity! {
crate::metadata::created_at?: created_at,
super::content?: content_handle,
super::signed_by?: signed_by,
super::signature_r?: signature,
super::signature_s?: signature,
super::message?: msg,
super::metadata?: metadata,
super::parent*: parents,
};
fragment.into()
}
pub fn verify(content: Blob<SimpleArchive>, metadata: TribleSet) -> Result<(), ValidationError> {
let handle = content.get_handle();
let (pubkey, r, s) = match find!(
(pubkey: Value<_>, r, s),
pattern!(&metadata, [
{
super::content: handle,
super::signed_by: ?pubkey,
super::signature_r: ?r,
super::signature_s: ?s
}]))
.at_most_one()
{
Ok(Some(result)) => result,
Ok(None) => return Err(ValidationError::MissingSignature),
Err(_) => return Err(ValidationError::AmbiguousSignature),
};
let pubkey: VerifyingKey = pubkey.try_from_value()?;
let signature = Signature::from_components(r, s);
pubkey.verify(&content.bytes, &signature)?;
Ok(())
}