//! A text-based data format for cryptographic network protocols. //! //! ``` //! use nettext::enc::*; //! use nettext::dec::*; //! use nettext::crypto::*; //! //! let final_payload = { //! let keypair = gen_signing_keypair(); //! //! // Encode a fist object that represents a payload that will be hashed and signed //! let signed_payload = seq([ //! string("CALL").unwrap(), //! string("myfunction").unwrap(), //! dict([ //! ("a", string("hello").unwrap()), //! ("b", string("world").unwrap()), //! ("c", raw(b"{ a = 12; b = 42 }").unwrap()), //! ("d", bytes_split(&((0..128u8).collect::>()))), //! ]).unwrap(), //! keypair.public_key().term().unwrap(), //! ]).unwrap().encode(); //! eprintln!("{}", std::str::from_utf8(&signed_payload).unwrap()); //! //! let hash = compute_hash(&signed_payload, None); //! let sign = compute_signature(&signed_payload[..], &keypair); //! //! // Encode a second object that represents the signed and hashed payload //! dict([ //! ("hash", hash.term().unwrap()), //! ("signature", sign.term().unwrap()), //! ("payload", raw(&signed_payload).unwrap()), //! ]).unwrap().encode() //! }; //! eprintln!("{}", std::str::from_utf8(&final_payload).unwrap()); //! //! // Decode and check everything is fine //! let signed_object = decode(&final_payload).unwrap(); //! let [hash, signature, payload] = signed_object.dict_of(["hash", "signature", "payload"], false).unwrap(); //! let hash = hash.hash().unwrap(); //! let signature = signature.signature().unwrap(); //! let expected_hash = compute_hash(payload.raw(), None); //! assert_eq!(hash, expected_hash); //! //! let object2 = decode(payload.raw()).unwrap(); //! //! let [verb, arg1, arg2, pubkey] = object2.seq_of().unwrap(); //! let pubkey = pubkey.public_key().unwrap(); //! assert!(verify_signature(&signature, payload.raw(), &pubkey)); //! //! assert_eq!(verb.string().unwrap(), "CALL"); //! assert_eq!(arg1.string().unwrap(), "myfunction"); //! ``` //! //! The value of `signed_payload` would be as follows: //! //! ```raw //! CALL myfunction { //! a = hello; //! b = world; //! c = { a = 12; b = 42 }; //! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v //! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f //! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8; //! } pk.ed25519:inYgWFyL_BzZTsXNKp71r2aVct_3Izi_bkerbzOiz94 //! ``` //! //! And the value of `final_payload` would be as follows: //! ```raw //! { //! hash = h.b2:B1AnRocS90DmqxynGyvvBNuh-brucNO7-5hrsGplJr0; //! payload = CALL myfunction { //! a = hello; //! b = world; //! c = { a = 12; b = 42 }; //! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v //! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f //! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8; //! } pk.ed25519:inYgWFyL_BzZTsXNKp71r2aVct_3Izi_bkerbzOiz94; //! signature = sig.ed25519:LvLC1gHxNxUH44HHQRO-zWtLM4WyXhiYLFr94qTdI311Wa-kmgZsaWqSWe3jcjkS4PnsWSNt5apgbhR68cWWCg; //! } //! ``` //! //! 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 dec; pub mod enc; #[cfg(feature = "dryoc")] pub mod crypto; #[cfg(feature = "serde")] pub mod serde; // ---- 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 LIST_OPEN: u8 = b'['; pub(crate) const LIST_CLOSE: u8 = b']'; pub(crate) const LIST_DELIM: u8 = b';'; const BASE_EXTRA_CHARS: &[u8] = b".,:?!@$^<>|&#'_-+*/%"; const STR_EXTRA_CHARS: &[u8] = b"\\"; #[inline] pub(crate) fn is_string_char(c: u8) -> bool { c.is_ascii_alphanumeric() || BASE_EXTRA_CHARS.contains(&c) || STR_EXTRA_CHARS.contains(&c) } #[inline] pub(crate) fn is_whitespace(c: u8) -> bool { c.is_ascii_whitespace() } pub(crate) fn debug(x: &[u8]) -> &str { std::str::from_utf8(x).unwrap_or("") }