nettext/lib.rs
1//! A text-based data format for cryptographic network protocols.
2//!
3//! ```
4//! use nettext::enc::*;
5//! use nettext::dec::*;
6//! use nettext::crypto::*;
7//!
8//! let final_payload = {
9//! let keypair = gen_signing_keypair();
10//!
11//! // Encode a fist object that represents a payload that will be hashed and signed
12//! let signed_payload = seq([
13//! string("CALL").unwrap(),
14//! string("myfunction").unwrap(),
15//! dict([
16//! ("a", string("hello").unwrap()),
17//! ("b", string("world").unwrap()),
18//! ("c", raw(b"{ a = 12; b = 42 }").unwrap()),
19//! ("d", bytes_split(&((0..128u8).collect::<Vec<_>>()))),
20//! ]).unwrap(),
21//! keypair.public_key().term().unwrap(),
22//! ]).unwrap().encode();
23//! eprintln!("{}", std::str::from_utf8(&signed_payload).unwrap());
24//!
25//! let hash = compute_hash(&signed_payload, None);
26//! let sign = compute_signature(&signed_payload[..], &keypair);
27//!
28//! // Encode a second object that represents the signed and hashed payload
29//! dict([
30//! ("hash", hash.term().unwrap()),
31//! ("signature", sign.term().unwrap()),
32//! ("payload", raw(&signed_payload).unwrap()),
33//! ]).unwrap().encode()
34//! };
35//! eprintln!("{}", std::str::from_utf8(&final_payload).unwrap());
36//!
37//! // Decode and check everything is fine
38//! let signed_object = decode(&final_payload).unwrap();
39//! let [hash, signature, payload] = signed_object.dict_of(["hash", "signature", "payload"], false).unwrap();
40//! let hash = hash.hash().unwrap();
41//! let signature = signature.signature().unwrap();
42//! let expected_hash = compute_hash(payload.raw(), None);
43//! assert_eq!(hash, expected_hash);
44//!
45//! let object2 = decode(payload.raw()).unwrap();
46//!
47//! let [verb, arg1, arg2, pubkey] = object2.seq_of().unwrap();
48//! let pubkey = pubkey.public_key().unwrap();
49//! assert!(verify_signature(&signature, payload.raw(), &pubkey));
50//!
51//! assert_eq!(verb.string().unwrap(), "CALL");
52//! assert_eq!(arg1.string().unwrap(), "myfunction");
53//! ```
54//!
55//! The value of `signed_payload` would be as follows:
56//!
57//! ```raw
58//! CALL myfunction {
59//! a = hello;
60//! b = world;
61//! c = { a = 12; b = 42 };
62//! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
63//! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f
64//! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8;
65//! } pk.ed25519:inYgWFyL_BzZTsXNKp71r2aVct_3Izi_bkerbzOiz94
66//! ```
67//!
68//! And the value of `final_payload` would be as follows:
69//! ```raw
70//! {
71//! hash = h.b2:B1AnRocS90DmqxynGyvvBNuh-brucNO7-5hrsGplJr0;
72//! payload = CALL myfunction {
73//! a = hello;
74//! b = world;
75//! c = { a = 12; b = 42 };
76//! d = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v
77//! MDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f
78//! YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8;
79//! } pk.ed25519:inYgWFyL_BzZTsXNKp71r2aVct_3Izi_bkerbzOiz94;
80//! signature = sig.ed25519:LvLC1gHxNxUH44HHQRO-zWtLM4WyXhiYLFr94qTdI311Wa-kmgZsaWqSWe3jcjkS4PnsWSNt5apgbhR68cWWCg;
81//! }
82//! ```
83//!
84//! Note that the value of `text1` is embedded as-is inside `text2`. This is what allows us
85//! to check the hash and the signature: the raw representation of the term hasn't changed.
86
87pub mod dec;
88pub mod enc;
89
90#[cfg(feature = "dryoc")]
91pub mod crypto;
92
93#[cfg(feature = "serde")]
94pub mod serde;
95
96// ---- syntactic elements of the data format ----
97
98pub(crate) const DICT_OPEN: u8 = b'{';
99pub(crate) const DICT_CLOSE: u8 = b'}';
100pub(crate) const DICT_ASSIGN: u8 = b'=';
101pub(crate) const DICT_DELIM: u8 = b';';
102pub(crate) const LIST_OPEN: u8 = b'[';
103pub(crate) const LIST_CLOSE: u8 = b']';
104pub(crate) const LIST_DELIM: u8 = b';';
105const BASE_EXTRA_CHARS: &[u8] = b".,:?!@$^<>|&#'_-+*/%";
106const STR_EXTRA_CHARS: &[u8] = b"\\";
107
108#[inline]
109pub(crate) fn is_string_char(c: u8) -> bool {
110 c.is_ascii_alphanumeric() || BASE_EXTRA_CHARS.contains(&c) || STR_EXTRA_CHARS.contains(&c)
111}
112
113#[inline]
114pub(crate) fn is_whitespace(c: u8) -> bool {
115 c.is_ascii_whitespace()
116}
117
118pub(crate) fn debug(x: &[u8]) -> &str {
119 std::str::from_utf8(x).unwrap_or("<invalid ascii>")
120}