nettext/src/lib.rs

103 lines
3.5 KiB
Rust

//! A text-based data format for cryptographic network protocols.
//!
//! ```
//! use nettext::enc::*;
//! use nettext::dec::*;
//! use nettext::crypto::{self, Signer, Verifier};
//!
//! let keypair = crypto::generate_keypair();
//!
//! // Encode a fist object that represents a payload that will be hashed and signed
//! let text1 = encode(list([
//! string("CALL"),
//! string("myfunction"),
//! dict([
//! ("a", string("hello")),
//! ("b", string("world")),
//! ("c", raw(b"{ a = 12, b = 42 }")),
//! ("d", bytes_split(&((0..128u8).collect::<Vec<_>>()))),
//! ]),
//! keypair.public.term(),
//! ])).unwrap();
//! eprintln!("{}", std::str::from_utf8(&text1).unwrap());
//!
//! let hash = crypto::Blake2Sum::compute(&text1);
//! let sign = keypair.sign(&text1);
//!
//! // Encode a second object that represents the signed and hashed payload
//! let text2 = encode(dict([
//! ("hash", hash.term()),
//! ("signature", sign.term()),
//! ("payload", raw(&text1)),
//! ])).unwrap();
//! eprintln!("{}", std::str::from_utf8(&text2).unwrap());
//!
//! // Decode and check everything is fine
//! let object1 = decode(&text2).unwrap();
//! let [hash, signature, payload] = object1.dict_of(["hash", "signature", "payload"], false).unwrap();
//! assert!(hash.b2sum().unwrap().verify(payload.raw()).is_ok());
//! assert_eq!(payload.raw(), text1);
//!
//! let object2 = decode(payload.raw()).unwrap();
//!
//! let [verb, arg1, arg2, pubkey] = object2.list_of().unwrap();
//! let pubkey = pubkey.public_key().unwrap();
//! assert!(pubkey.verify(payload.raw(), &signature.signature().unwrap()).is_ok());
//!
//! assert_eq!(verb.string().unwrap(), "CALL");
//! assert_eq!(arg1.string().unwrap(), "myfunction");
//! assert_eq!(pubkey, keypair.public);
//! ```
//!
//! The value of `text1` would be as follows:
//!
//! ```raw
//! CALL myfunction {
//! a = hello,
//! b = world,
//! c = { a = 12, b = 42 },
//! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
//! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f
//! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8,
//! } 1hUAS2C0lzHXHWIvXqwuhUYVPlu3BbZ7ANLUMH_OYjo
//! ```
//!
//! And the value of `text2` would be as follows:
//! ```raw
//! {
//! hash = Se6Wmbh3fbFQ9_ilE6zGbxNaEd9v5CHAb30p46Fxpi74iblRb9fXmGAiMkXnSe4DePTwb16zGAz_Ux4ZAG9s3w,
//! payload = CALL myfunction {
//! a = hello,
//! b = world,
//! c = { a = 12, b = 42 },
//! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
//! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f
//! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8,
//! } 1hUAS2C0lzHXHWIvXqwuhUYVPlu3BbZ7ANLUMH_OYjo,
//! signature = 8mo3aeQD7JAdqbDcm7oVdaU0XamDwg03JtC3mfsWhEy_ZkNmWBFZefIDlzBR3XpnF0szTzEwtoPFfnR1fz6fAA,
//! }
//! ```
//!
//! Note that the value of `text1` is embedded as-is inside `text2`. This is what allows us
//! to check the hash and the signature: the raw representation of the term hasn't changed.
pub mod crypto;
pub mod dec;
pub mod enc;
// ---- syntactic elements of the data format ----
pub(crate) const DICT_OPEN: u8 = b'{';
pub(crate) const DICT_CLOSE: u8 = b'}';
pub(crate) const DICT_ASSIGN: u8 = b'=';
pub(crate) const DICT_DELIM: u8 = b',';
pub(crate) const STR_EXTRA_CHARS: &[u8] = b"._-+*?";
pub(crate) fn is_string_char(c: u8) -> bool {
c.is_ascii_alphanumeric() || STR_EXTRA_CHARS.contains(&c)
}
pub(crate) fn is_whitespace(c: u8) -> bool {
c.is_ascii_whitespace()
}