use base64::prelude::*;
use ed25519_dalek::{
Signer as Ed25519Signer, SigningKey as Ed25519SigningKey, Verifier as Ed25519Verifier,
VerifyingKey as Ed25519VerifyingKey,
};
use rand_core::CryptoRngCore;
use sha2::{Digest, Sha256};
use std::collections::{BTreeSet, HashMap};
use thiserror::Error;
const MAX_NOTE_SIZE: usize = 1_000_000;
const MAX_NOTE_SIGNATURES: usize = 100;
pub trait Verifier {
fn name(&self) -> &str;
fn key_id(&self) -> u32;
fn verify(&self, msg: &[u8], sig: &[u8]) -> bool;
}
pub trait Signer {
fn name(&self) -> &str;
fn key_id(&self) -> u32;
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, signature::Error>;
}
pub fn key_id(name: &str, key: &[u8]) -> u32 {
let mut hasher = Sha256::new();
hasher.update(name.as_bytes());
hasher.update(b"\n");
hasher.update(key);
let result = hasher.finalize();
u32::from_be_bytes(result[0..4].try_into().unwrap())
}
#[derive(Error, Debug)]
pub enum VerifierError {
#[error("malformed verifier key")]
Format,
#[error("unknown verifier algorithm")]
Alg,
#[error("invalid verifier ID")]
Id,
}
const ALG_ED25519: u8 = 1;
pub fn is_key_name_valid(name: &str) -> bool {
!(name.is_empty() || name.chars().any(char::is_whitespace) || name.contains('+'))
}
#[derive(Clone)]
pub struct StandardVerifier {
name: String,
id: u32,
verifying_key: Ed25519VerifyingKey,
}
impl Verifier for StandardVerifier {
fn name(&self) -> &str {
&self.name
}
fn key_id(&self) -> u32 {
self.id
}
fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
let sig_bytes: [u8; ed25519_dalek::SIGNATURE_LENGTH] = match sig.try_into() {
Ok(ok) => ok,
Err(_) => return false,
};
self.verifying_key
.verify(msg, &ed25519_dalek::Signature::from_bytes(&sig_bytes))
.is_ok()
}
}
impl StandardVerifier {
pub fn new(vkey: &str) -> Result<Self, VerifierError> {
let (name, vkey) = vkey.split_once('+').ok_or(VerifierError::Format)?;
let (id16, key64) = vkey.split_once('+').ok_or(VerifierError::Format)?;
let id = u32::from_str_radix(id16, 16).map_err(|_| VerifierError::Format)?;
let key = BASE64_STANDARD
.decode(key64)
.map_err(|_| VerifierError::Format)?;
if id16.len() != 8 || !is_key_name_valid(name) || key.is_empty() {
return Err(VerifierError::Format);
}
if id != key_id(name, &key) {
return Err(VerifierError::Id);
}
let alg = key[0];
let key = &key[1..];
match alg {
ALG_ED25519 => {
let key_bytes: &[u8; ed25519_dalek::PUBLIC_KEY_LENGTH] =
&key.try_into().map_err(|_| VerifierError::Format)?;
let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(key_bytes)
.map_err(|_| VerifierError::Format)?;
Ok(Self {
name: name.to_owned(),
id,
verifying_key,
})
}
_ => Err(VerifierError::Alg),
}
}
}
#[derive(Error, Debug)]
pub enum SignerError {
#[error("malformed verifier key")]
Format,
#[error("unknown verifier algorithm")]
Alg,
#[error("invalid verifier ID")]
Id,
}
#[derive(Clone)]
pub struct StandardSigner {
name: String,
id: u32,
signing_key: Ed25519SigningKey,
}
impl Signer for StandardSigner {
fn name(&self) -> &str {
&self.name
}
fn key_id(&self) -> u32 {
self.id
}
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, signature::Error> {
let sig = self.signing_key.try_sign(msg)?;
Ok(sig.to_vec())
}
}
impl StandardSigner {
pub fn new(skey: &str) -> Result<Self, SignerError> {
let (priv1, skey) = skey.split_once('+').ok_or(SignerError::Format)?;
let (priv2, skey) = skey.split_once('+').ok_or(SignerError::Format)?;
let (name, skey) = skey.split_once('+').ok_or(SignerError::Format)?;
let (id16, key64) = skey.split_once('+').ok_or(SignerError::Format)?;
let id = u32::from_str_radix(id16, 16).map_err(|_| SignerError::Format)?;
let key = BASE64_STANDARD
.decode(key64)
.map_err(|_| SignerError::Format)?;
if priv1 != "PRIVATE"
|| priv2 != "KEY"
|| id16.len() != 8
|| !is_key_name_valid(name)
|| key.is_empty()
{
return Err(SignerError::Format);
}
let signer: StandardSigner;
let pubkey: Vec<u8>;
let alg = key[0];
let key = &key[1..];
match alg {
ALG_ED25519 => {
let signing_key =
ed25519_dalek::SigningKey::try_from(key).map_err(|_| SignerError::Format)?;
pubkey = [
&[ALG_ED25519],
ed25519_dalek::VerifyingKey::from(&signing_key)
.to_bytes()
.as_slice(),
]
.concat();
signer = Self {
name: name.to_owned(),
id,
signing_key,
};
}
_ => {
return Err(SignerError::Alg);
}
}
if id != key_id(name, &pubkey) {
return Err(SignerError::Id);
}
Ok(signer)
}
}
pub fn generate_key<R: CryptoRngCore + ?Sized>(csprng: &mut R, name: &str) -> (String, String) {
let signing_key = ed25519_dalek::SigningKey::generate(csprng);
let pubkey = [
&[ALG_ED25519],
signing_key.verifying_key().to_bytes().as_slice(),
]
.concat();
let privkey = [&[ALG_ED25519], signing_key.to_bytes().as_slice()].concat();
let skey = format!(
"PRIVATE+KEY+{}+{:08x}+{}",
name,
key_id(name, &pubkey),
BASE64_STANDARD.encode(privkey)
);
let vkey = new_ed25519_verifier_key(name, &signing_key.verifying_key());
(skey, vkey)
}
pub fn new_ed25519_verifier_key(name: &str, key: &ed25519_dalek::VerifyingKey) -> String {
let pubkey = [&[ALG_ED25519], key.to_bytes().as_slice()].concat();
format!(
"{}+{:08x}+{}",
name,
key_id(name, &pubkey),
BASE64_STANDARD.encode(&pubkey)
)
}
pub trait Verifiers {
fn verifier(&self, name: &str, id: u32) -> Result<&dyn Verifier, VerificationError>;
}
type VerifierMap = HashMap<(String, u32), Vec<Box<dyn Verifier>>>;
pub struct VerifierList {
map: VerifierMap,
}
#[derive(Error, Debug)]
pub enum VerificationError {
#[error("unknown key {name}+{id:08x}")]
UnknownKey { name: String, id: u32 },
#[error("ambiguous key {name}+{id:08x}")]
AmbiguousKey { name: String, id: u32 },
}
impl Verifiers for VerifierList {
fn verifier(&self, name: &str, id: u32) -> Result<&dyn Verifier, VerificationError> {
match self.map.get(&(name.to_owned(), id)) {
Some(verifiers) => {
if verifiers.len() > 1 {
return Err(VerificationError::AmbiguousKey {
name: name.to_owned(),
id,
});
}
Ok(&*verifiers[0])
}
None => Err(VerificationError::UnknownKey {
name: name.to_owned(),
id,
}),
}
}
}
impl VerifierList {
pub fn new(list: Vec<Box<dyn Verifier>>) -> Self {
let mut map: VerifierMap = HashMap::new();
for verifier in list {
map.entry((verifier.name().to_owned(), verifier.key_id()))
.or_default()
.push(verifier);
}
VerifierList { map }
}
}
#[derive(Debug, PartialEq)]
pub struct Note {
text: Vec<u8>,
sigs: Vec<Signature>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Signature {
name: String,
id: u32,
sig: Vec<u8>,
}
impl Signature {
pub fn new(name: String, id: u32, sig: Vec<u8>) -> Result<Self, NoteError> {
if !is_key_name_valid(&name) {
return Err(NoteError::MalformedNote);
}
Ok(Self { name, id, sig })
}
pub fn from_bytes(line: &[u8]) -> Result<Self, NoteError> {
let line = std::str::from_utf8(line).map_err(|_| NoteError::MalformedNote)?;
let line = line.strip_prefix("— ").ok_or(NoteError::MalformedNote)?;
let (name, b64) = line.split_once(' ').ok_or(NoteError::MalformedNote)?;
let sig = BASE64_STANDARD
.decode(b64)
.map_err(|_| NoteError::MalformedNote)?;
if b64.is_empty() || sig.len() < 5 {
return Err(NoteError::MalformedNote);
}
let id = u32::from_be_bytes(sig[..4].try_into().unwrap());
let sig = &sig[4..];
Signature::new(name.to_owned(), id, sig.to_owned())
}
pub fn name(&self) -> &str {
&self.name
}
pub fn id(&self) -> u32 {
self.id
}
pub fn signature(&self) -> &[u8] {
&self.sig
}
pub fn to_bytes(&self) -> Vec<u8> {
let hbuf = self.id.to_be_bytes();
let base64 = BASE64_STANDARD.encode([&hbuf, self.sig.as_slice()].concat());
format!("— {} {}\n", self.name, base64).into()
}
}
#[derive(Error, Debug)]
pub enum NoteError {
#[error("malformed note")]
MalformedNote,
#[error("invalid signer")]
InvalidSigner,
#[error("invalid signature for key {name}+{id:08x}")]
InvalidSignature { name: String, id: u32 },
#[error("verifier name or id doesn't match signature")]
MismatchedVerifier,
#[error("note has no verifiable signatures")]
UnverifiedNote,
#[error(transparent)]
VerificationError(#[from] VerificationError),
#[error(transparent)]
SignatureError(#[from] signature::Error),
}
impl Note {
pub fn new(text: &[u8], existing_sigs: &[Signature]) -> Result<Self, NoteError> {
if text.len() > MAX_NOTE_SIZE {
return Err(NoteError::MalformedNote);
}
let text_str = std::str::from_utf8(text).map_err(|_| NoteError::MalformedNote)?;
for ch in text_str.chars() {
if ch < '\u{0020}' && ch != '\n' {
return Err(NoteError::MalformedNote);
}
}
if !text_str.ends_with('\n') {
return Err(NoteError::MalformedNote);
}
Ok(Self {
text: text.to_owned(),
sigs: existing_sigs.into(),
})
}
pub fn from_bytes(msg: &[u8]) -> Result<Self, NoteError> {
if msg.len() > MAX_NOTE_SIZE {
return Err(NoteError::MalformedNote);
}
let msg_str = std::str::from_utf8(msg).map_err(|_| NoteError::MalformedNote)?;
for ch in msg_str.chars() {
if ch < '\u{0020}' && ch != '\n' {
return Err(NoteError::MalformedNote);
}
}
let (text, sigs) = msg_str
.rsplit_once("\n\n")
.ok_or(NoteError::MalformedNote)?;
let text = format!("{text}\n");
let sigs = sigs.strip_suffix("\n").ok_or(NoteError::MalformedNote)?;
let mut parsed_sigs: Vec<Signature> = Vec::new();
let mut num_sig = 0;
for line in sigs.split('\n') {
let sig = Signature::from_bytes(line.as_bytes())?;
num_sig += 1;
if num_sig > MAX_NOTE_SIGNATURES {
return Err(NoteError::MalformedNote);
}
parsed_sigs.push(sig);
}
Self::new(text.as_bytes(), &parsed_sigs)
}
pub fn verify(
&self,
known: &impl Verifiers,
) -> Result<(Vec<Signature>, Vec<Signature>), NoteError> {
let mut verified_sigs = Vec::new();
let mut unverified_sigs = Vec::new();
let mut seen = BTreeSet::new();
let mut seen_unverified = BTreeSet::new();
for sig in &self.sigs {
match known.verifier(&sig.name, sig.id) {
Ok(verifier) => {
if verifier.name() != sig.name || verifier.key_id() != sig.id {
return Err(NoteError::MismatchedVerifier);
}
if seen.contains(&(&sig.name, sig.id)) {
continue;
}
seen.insert((&sig.name, sig.id));
if !verifier.verify(&self.text, &sig.sig) {
return Err(NoteError::InvalidSignature {
name: sig.name.clone(),
id: sig.id,
});
}
verified_sigs.push(sig.clone());
}
Err(VerificationError::UnknownKey { name: _, id: _ }) => {
if seen_unverified.contains(&sig.to_bytes()) {
continue;
}
seen_unverified.insert(sig.to_bytes());
unverified_sigs.push(sig.clone());
}
Err(e) => return Err(e.into()),
}
}
if verified_sigs.is_empty() {
return Err(NoteError::UnverifiedNote);
}
Ok((verified_sigs, unverified_sigs))
}
pub fn add_sigs(&mut self, signers: &[&dyn Signer]) -> Result<(), NoteError> {
let mut new_sigs = Vec::new();
let mut have = BTreeSet::new();
for s in signers {
let name = s.name();
let id = s.key_id();
have.insert((name, id));
if !is_key_name_valid(name) {
return Err(NoteError::InvalidSigner);
}
let sig = s.sign(&self.text)?;
new_sigs.push(Signature::new(name.to_owned(), id, sig)?);
}
self.sigs.retain(|sig| !have.contains(&(&sig.name, sig.id)));
self.sigs.extend(new_sigs);
Ok(())
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = self.text.clone();
buf.push(b'\n');
for sig in &self.sigs {
buf.extend(&sig.to_bytes());
}
buf
}
pub fn text(&self) -> &[u8] {
&self.text
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
static NAME: &str = "EnochRoot";
fn test_signer_and_verifier(name: &str, signer: &dyn Signer, verifier: &dyn Verifier) {
assert_eq!(&name, &signer.name());
assert_eq!(&name, &verifier.name());
assert_eq!(signer.key_id(), verifier.key_id());
let msg: &[u8] = b"hi";
let sig = signer.sign(msg).unwrap();
assert!(verifier.verify(msg, &sig));
}
#[test]
fn test_generate_key() {
let (skey, vkey) = generate_key(&mut OsRng, NAME);
let signer = StandardSigner::new(&skey).unwrap();
let verifier = StandardVerifier::new(&vkey).unwrap();
test_signer_and_verifier(NAME, &signer, &verifier);
}
#[test]
fn test_from_ed25519() {
let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng);
let pubkey = [
&[ALG_ED25519],
signing_key.verifying_key().to_bytes().as_slice(),
]
.concat();
let id = key_id(NAME, &pubkey);
let vkey = new_ed25519_verifier_key(NAME, &signing_key.verifying_key());
let verifier = StandardVerifier::new(&vkey).unwrap();
let signer = StandardSigner {
name: NAME.to_owned(),
id,
signing_key,
};
test_signer_and_verifier(NAME, &signer, &verifier);
}
struct BadSigner {
s: Box<dyn Signer>,
}
impl Signer for BadSigner {
fn name(&self) -> &'static str {
"bad name"
}
fn key_id(&self) -> u32 {
self.s.key_id()
}
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, signature::Error> {
self.s.sign(msg)
}
}
struct ErrSigner {
s: Box<dyn Signer>,
}
impl Signer for ErrSigner {
fn name(&self) -> &str {
self.s.name()
}
fn key_id(&self) -> u32 {
self.s.key_id()
}
fn sign(&self, _msg: &[u8]) -> Result<Vec<u8>, signature::Error> {
Err(signature::Error::new())
}
}
#[test]
fn test_sign() {
let skey = "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz";
let text = b"If you think cryptography is the answer to your problem,\n\
then you don't know what your problem is.\n";
let signer = StandardSigner::new(skey).unwrap();
let mut n = Note::new(text, &[]).unwrap();
n.add_sigs(&[&signer]).unwrap();
let want = "If you think cryptography is the answer to your problem,\n\
then you don't know what your problem is.\n\
\n\
— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n";
assert_eq!(n.to_bytes(), want.as_bytes());
let mut n = Note::new(
text,
&[Signature::new("PeterNeumann".into(), 0xc74f_20a3, vec![]).unwrap()],
)
.unwrap();
n.add_sigs(&[&signer]).unwrap();
assert_eq!(n.to_bytes(), want.as_bytes());
let err = Note::new(b"abc", &[]).unwrap_err();
assert!(matches!(err, NoteError::MalformedNote));
let err = Signature::new("a+b".into(), 0, vec![]).unwrap_err();
assert!(matches!(err, NoteError::MalformedNote));
let err = Note::new(text, &[])
.unwrap()
.add_sigs(&[&BadSigner {
s: Box::new(signer.clone()),
}])
.unwrap_err();
assert!(matches!(err, NoteError::InvalidSigner));
let err = Note::new(text, &[])
.unwrap()
.add_sigs(&[&ErrSigner {
s: Box::new(signer.clone()),
}])
.unwrap_err();
assert!(matches!(err, NoteError::SignatureError(_)));
}
struct FixedVerifier {
v: Box<dyn Verifier>,
}
impl Verifiers for FixedVerifier {
fn verifier(&self, _name: &str, _id: u32) -> Result<&dyn Verifier, VerificationError> {
Ok(&*self.v)
}
}
#[test]
fn test_open() {
let peter_key = "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW";
let peter_verifier = StandardVerifier::new(peter_key).unwrap();
let enoch_key = "EnochRoot+af0cfe78+ATtqJ7zOtqQtYqOo0CpvDXNlMhV3HeJDpjrASKGLWdop";
let enoch_verifier = StandardVerifier::new(enoch_key).unwrap();
let text = "If you think cryptography is the answer to your problem,\n\
then you don't know what your problem is.\n";
let peter_sig = "— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n";
let enoch_sig = "— EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ=\n";
let peter = Signature::from_bytes(peter_sig.trim_end().as_bytes()).unwrap();
let enoch = Signature::from_bytes(enoch_sig.trim_end().as_bytes()).unwrap();
let n = Note::from_bytes(format!("{text}\n{peter_sig}{enoch_sig}").as_bytes()).unwrap();
let (verified_sigs, unverified_sigs) = n
.verify(&VerifierList::new(vec![Box::new(peter_verifier.clone())]))
.unwrap();
assert_eq!(n.text(), text.as_bytes());
assert_eq!(verified_sigs, vec![peter.clone()]);
assert_eq!(unverified_sigs, vec![enoch.clone()]);
let (verified_sigs, unverified_sigs) = n
.verify(&VerifierList::new(vec![
Box::new(peter_verifier.clone()),
Box::new(enoch_verifier.clone()),
]))
.unwrap();
assert_eq!(verified_sigs, vec![peter.clone(), enoch.clone()]);
assert!(unverified_sigs.is_empty());
let err = n.verify(&VerifierList::new(vec![])).unwrap_err();
assert!(matches!(err, NoteError::UnverifiedNote));
let err = Note::from_bytes(format!("{text}\n{enoch_sig}").as_bytes())
.unwrap()
.verify(&VerifierList::new(vec![
Box::new(enoch_verifier.clone()),
Box::new(peter_verifier.clone()),
Box::new(enoch_verifier.clone()),
]))
.unwrap_err();
assert_eq!(err.to_string(), "ambiguous key EnochRoot+af0cfe78");
let _ = Note::from_bytes(format!("{text}\n{peter_sig}").as_bytes())
.unwrap()
.verify(&VerifierList::new(vec![
Box::new(enoch_verifier.clone()),
Box::new(peter_verifier.clone()),
Box::new(enoch_verifier.clone()),
]))
.unwrap();
let err = Note::from_bytes(format!("{}\n{}", text, peter_sig.repeat(101)).as_bytes())
.unwrap_err();
assert!(matches!(err, NoteError::MalformedNote));
let invalid_sig = format!("{}ABCD{}", &peter_sig[..60], &peter_sig[60..]);
let err = Note::from_bytes(format!("{text}\n{invalid_sig}").as_bytes())
.unwrap()
.verify(&VerifierList::new(vec![Box::new(peter_verifier.clone())]))
.unwrap_err();
assert_eq!(
err.to_string(),
"invalid signature for key PeterNeumann+c74f20a3"
);
let enoch_abcd = Signature::from_bytes("— EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5nABCD2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ="
.as_bytes(),
)
.unwrap();
let sigs = format!(
"{peter_sig}{peter_sig}{peter_sig}{enoch_sig}{}ABCD{}",
&enoch_sig[..60],
&enoch_sig[60..]
);
let n = Note::from_bytes(format!("{text}\n{sigs}").as_bytes()).unwrap();
let (verified_sigs, unverified_sigs) = n
.verify(&VerifierList::new(vec![Box::new(peter_verifier.clone())]))
.unwrap();
assert_eq!(verified_sigs, vec![peter.clone()]);
assert_eq!(unverified_sigs, vec![enoch.clone(), enoch_abcd.clone()]);
let bad_msgs: Vec<Vec<u8>> = vec![
text.as_bytes().to_vec(),
format!("\n{text}").as_bytes().to_vec(),
format!("{text}\n{}", &peter_sig[..peter_sig.len() - 1]).as_bytes().to_vec(),
format!("\x01{text}\n{peter_sig}").as_bytes().to_vec(),
[&[0xff], format!("{text}\n{peter_sig}").as_bytes()].concat(),
format!("{text}\n— Bad Name x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=").as_bytes().to_vec(),
];
for msg in bad_msgs {
let err = Note::from_bytes(&msg).unwrap_err();
assert!(matches!(err, NoteError::MalformedNote));
}
let misnamed_sig = peter_sig.replace("PeterNeumann", "CarmenSandiego");
let err = Note::from_bytes(format!("{text}\n{misnamed_sig}").as_bytes())
.unwrap()
.verify(&FixedVerifier {
v: Box::new(peter_verifier.clone()),
})
.unwrap_err();
assert!(matches!(err, NoteError::MismatchedVerifier));
let wrong_id = peter_sig.replace("x08g", "xxxx");
let err = Note::from_bytes(format!("{text}\n{wrong_id}").as_bytes())
.unwrap()
.verify(&FixedVerifier {
v: Box::new(peter_verifier.clone()),
})
.unwrap_err();
assert!(matches!(err, NoteError::MismatchedVerifier));
}
}