From c8da72da9a7c515ae5c5009f5091f9297917d9a3 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 26 Apr 2024 16:56:32 +0200 Subject: [PATCH] long signature certificates Signed-off-by: Dr Maxim Orlovsky --- src/lib.rs | 2 +- src/main.rs | 23 +++++++++++++++++++---- src/public.rs | 38 +++++++++++++++++++++++++++++++++++--- src/secret.rs | 1 + 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 80b678d..8942808 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ pub use ed25519::Ed25519Secret; pub use identity::{Ssi, SsiParseError, Uid}; pub use public::{ Algo, CertParseError, Chain, Fingerprint, InvalidPubkey, InvalidSig, SsiCert, SsiPub, SsiQuery, - SsiSig, UnknownAlgo, UnknownChain, + SsiSig, UnknownAlgo, UnknownChain, VerifyError, }; pub use runtime::{Error, SsiRuntime, SSI_DIR}; pub use secret::{SecretParseError, SsiPair, SsiSecret}; diff --git a/src/main.rs b/src/main.rs index 11767b5..24565e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,6 +81,10 @@ pub enum Command { /// Sign a file or a message Sign { + /// Generate signature including the full identity + #[clap(long)] + full: bool, + /// Text message to sign #[clap(short, long, conflicts_with = "file")] text: Option, @@ -177,7 +181,12 @@ fn main() { runtime.store().expect("unable to save data"); } - Command::Sign { text, file, ssi } => { + Command::Sign { + full, + text, + file, + ssi, + } => { eprintln!("Signing with {ssi} ..."); let passwd = rpassword::prompt_password("Password for private key encryption: ") @@ -199,15 +208,21 @@ fn main() { .expect("unknown signing identity"); eprintln!("Using key {signer}"); let cert = signer.sign(msg); - println!("{cert}"); + if full { + println!("{cert:#}"); + } else { + println!("{cert}"); + } } Command::Verify { signature } => { eprint!("Verifying signature for message digest {} ... ", signature.msg); - let ssi = runtime + let pk = runtime .find_identity(signature.fp) + .map(|ssi| ssi.pk) + .or(signature.pk) .expect("unknown signing identity"); - match ssi.pk.verify(signature.msg.to_byte_array(), signature.sig) { + match pk.verify(signature.msg.to_byte_array(), signature.sig) { Ok(_) => eprintln!("valid"), Err(err) => eprintln!("invalid: {err}"), } diff --git a/src/public.rs b/src/public.rs index f38f437..5c328d2 100644 --- a/src/public.rs +++ b/src/public.rs @@ -288,17 +288,38 @@ impl FromStr for Fingerprint { #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct SsiCert { pub fp: Fingerprint, + pub pk: Option, pub msg: Bytes32, pub sig: SsiSig, } +#[derive(Debug, Display, Error, From)] +#[display(inner)] +pub enum VerifyError { + #[display("the certificate has no identity, verification impossible.")] + NoIdentity, + #[from] + InvalidSig(InvalidSig), +} + +impl SsiCert { + pub fn verify(&self) -> Result<(), VerifyError> { + let Some(pk) = self.pk else { + return Err(VerifyError::NoIdentity); + }; + Ok(pk.verify(self.msg.to_byte_array(), self.sig)?) + } +} + #[derive(Debug, Display, Error, From)] #[display(doc_comments)] pub enum CertParseError { /// SSI URI lacks signature or message information. DataMissed, - /// invalid fingerprint data in private key - {0}. + /// invalid certificate identity fingerprint - {0}. InvalidFingerprint(Baid64ParseError), + /// invalid certificate identity key - {0}. + InvalidPub(Baid64ParseError), /// invalid message digest - {0}. #[from] InvalidMessage(hex::Error), @@ -320,15 +341,26 @@ impl FromStr for SsiCert { .split_once('&') .ok_or(CertParseError::DataMissed)?; let sig = rest.trim_start_matches("sig="); - let fp = Fingerprint::from_str(fp).map_err(CertParseError::InvalidFingerprint)?; + let (fp, pk) = match fp.len() { + 8 => (Fingerprint::from_str(fp).map_err(CertParseError::InvalidFingerprint)?, None), + _ => { + let pk = SsiPub::from_str(fp).map_err(CertParseError::InvalidPub)?; + (pk.fingerprint(), Some(pk)) + } + }; let msg = Bytes32::from_str(msg)?; let sig = SsiSig::from_str(sig)?; - Ok(SsiCert { fp, msg, sig }) + Ok(SsiCert { fp, pk, msg, sig }) } } impl Display for SsiCert { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if f.alternate() { + if let Some(pk) = self.pk { + return write!(f, "{pk}?msg={msg}&sig={sig}", msg = self.msg, sig = self.sig); + } + } write!(f, "ssi:{fp}?msg={msg}&sig={sig}", fp = self.fp, msg = self.msg, sig = self.sig) } } diff --git a/src/secret.rs b/src/secret.rs index c89f30f..f5fdae0 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -214,6 +214,7 @@ impl SsiPair { let sig = self.sk.sign(digest.into()); SsiCert { fp: self.pk.fingerprint(), + pk: Some(self.pk), msg: Bytes32::from_byte_array(digest), sig, }