2022-11-17 22:58:44 +00:00
|
|
|
//! A text-based data format for cryptographic network protocols.
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! use nettext::enc::*;
|
|
|
|
//! use nettext::dec::*;
|
2022-11-17 23:01:23 +00:00
|
|
|
//! use nettext::crypto::{self, Signer, Verifier};
|
2022-11-17 22:58:44 +00:00
|
|
|
//!
|
2022-11-17 23:01:23 +00:00
|
|
|
//! let keypair = crypto::generate_keypair();
|
2022-11-17 22:58:44 +00:00
|
|
|
//!
|
|
|
|
//! // Encode a fist object that represents a payload that will be hashed and signed
|
|
|
|
//! let text1 = encode(list([
|
2022-11-17 23:01:23 +00:00
|
|
|
//! string("CALL"),
|
|
|
|
//! string("myfunction"),
|
|
|
|
//! dict([
|
|
|
|
//! ("a", string("hello")),
|
|
|
|
//! ("b", string("world")),
|
|
|
|
//! ("c", raw(b"{ a = 12, b = 42 }")),
|
|
|
|
//! ]),
|
|
|
|
//! keypair.public.term(),
|
|
|
|
//! ])).unwrap();
|
|
|
|
//! eprintln!("{}", std::str::from_utf8(&text1).unwrap());
|
|
|
|
//!
|
|
|
|
//! let hash = crypto::Blake2Sum::compute(&text1);
|
|
|
|
//! let sign = keypair.sign(&text1);
|
2022-11-17 22:58:44 +00:00
|
|
|
//!
|
|
|
|
//! // Encode a second object that represents the signed and hashed payload
|
2022-11-17 23:01:23 +00:00
|
|
|
//! 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);
|
2022-11-17 22:58:44 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! The value of `text1` would be as follows:
|
|
|
|
//!
|
|
|
|
//! ```raw
|
|
|
|
//! CALL myfunction {
|
|
|
|
//! a = hello,
|
|
|
|
//! b = world,
|
|
|
|
//! c = { a = 12, b = 42 },
|
2022-11-17 23:20:42 +00:00
|
|
|
//! } YutjCfgXXYNkNR1IQiNi3pFKpvqfwICkLc3EJOekcq4
|
2022-11-17 22:58:44 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! And the value of `text2` would be as follows:
|
|
|
|
//! ```raw
|
|
|
|
//! {
|
2022-11-17 23:20:42 +00:00
|
|
|
//! hash = IT4ay3XM4SycgYjxV8_Ioxqqt9JwdFK0sZqd-TOhOl9IGxbTQwK8vPy409h59xCV
|
|
|
|
//! NrMjDC1YIS7bXIrrv_Tvbw,
|
2022-11-17 22:58:44 +00:00
|
|
|
//! payload = CALL myfunction {
|
|
|
|
//! a = hello,
|
|
|
|
//! b = world,
|
|
|
|
//! c = { a = 12, b = 42 },
|
2022-11-17 23:20:42 +00:00
|
|
|
//! } YutjCfgXXYNkNR1IQiNi3pFKpvqfwICkLc3EJOekcq4,
|
|
|
|
//! signature = UFje_N6vnrN23-ygB1yr8LwSipSwxrMLEB2ov6bvU4rR9BmfLjxyq8zTzKxb_VNw
|
|
|
|
//! UABMRcy-KiITwpY_b3UdBg,
|
2022-11-17 22:58:44 +00:00
|
|
|
//! }
|
|
|
|
//! ```
|
2022-11-17 23:20:42 +00:00
|
|
|
//!
|
|
|
|
//! 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.
|
2022-11-17 22:58:44 +00:00
|
|
|
|
2022-11-17 15:35:06 +00:00
|
|
|
pub mod crypto;
|
2022-11-17 10:48:43 +00:00
|
|
|
pub mod dec;
|
2022-11-17 16:55:50 +00:00
|
|
|
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()
|
|
|
|
}
|