From 81d2e8b116e6128b6ece273198c8f43622b12d06 Mon Sep 17 00:00:00 2001 From: Davie Li Date: Sun, 21 Apr 2024 14:26:47 +0000 Subject: [PATCH] lib: Add Obligation and Cert types with construction --- Cargo.toml | 4 +++ src/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index d739e57..ed17f89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" [[bin]] name = "credit5000" path = "src/main.rs" + +[dependencies] +ed25519-dalek = "2.1.1" +rand = "0.8.5" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f9f8cef --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,102 @@ +use dalek::Verifier; +use ed25519_dalek as dalek; +use rand::prelude as rand; + +pub type Address = [u8; 32]; +pub type Signature = [u8; 64]; +pub type Salt = [u8; 32]; + +/// TODO: Use hashes instead of public addresses. +/// TODO: Don't forget document endiannes. +/// TODO: Allow doubly signed obligations or implement abstract interfaces for Cert (see below). + +/// Implement abstract Cert datatype to allow attestation of hyper-relations: +/// TODO: Use serializable trait to generalize over the subject field. +/// TODO: Allow per subject type verification trait. Hard not to go +/// full crypto-conditions but better not to complicate things too +/// much. + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct Obligation { + pub from: Address, + pub to: Address, + pub value: u32, +} + +impl Obligation { + fn serialize(self) -> Vec { + [&self.from[..], &self.value.to_le_bytes()[..], &self.to[..]].concat() + } +} + +#[derive(Copy, Clone)] +pub struct Cert { + pub subject: Obligation, + pub signature: Signature, + pub salt: Salt, +} + +impl Cert { + /// TODO: add optional salt argument + pub fn new(sub: Obligation, sig: Signature) -> Result { + match dalek::VerifyingKey::from_bytes(&sub.from) + .unwrap() + .verify(&sub.serialize()[..], &dalek::Signature::from_bytes(&sig)) + { + Ok(_) => Ok(Self { + subject: sub, + signature: sig, + salt: rand::random(), + }), + Err(_) => Err("Invalid signature"), + } + } +} + +#[cfg(test)] +mod tests { + use crate::{Cert, Obligation}; + use ed25519_dalek::{Signer, SigningKey}; + + #[test] + fn serialize_obligation() { + assert_eq!( + Obligation { + from: [1; 32], + to: [2; 32], + value: 4_294_967_295, // Max u32 (255 255 255 255) + } + .serialize(), + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 255, 255, 255, 255, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 + ] + ); + } + + #[test] + fn cert_new() { + let sk_gen = || { + let skobj = SigningKey::from_bytes(&rand::random()); + ( + skobj.clone(), + skobj.to_bytes(), + skobj.verifying_key().to_bytes(), + ) + }; + let (f, _, fpk) = sk_gen(); + let (_, _, tpk) = sk_gen(); + let sub = Obligation { + from: fpk, + to: tpk, + value: 1000, + }; + let sig = f.sign(&sub.serialize()[..]).to_bytes(); + let cert = Cert::new(sub, sig); + assert!(cert.is_ok()); + assert_eq!(cert.unwrap().subject, sub); + assert_eq!(cert.unwrap().signature, sig); + assert!(Cert::new(sub, [0; 64]).is_err()); + } +}