nettext 0.4.1

A text-based data format for cryptographic network protocols
Documentation
//! Helpers to use cryptographic data types in nettext

pub use dryoc;

use dryoc::types::Bytes;
use dryoc::*;

use crate::dec;
use crate::enc;

const BM_HASH: &str = "h.b2";

const BM_SIGNATURE: &str = "sig.ed25519";
const BM_SIGN_KEYPAIR: &str = "sk.ed25519";
const BM_SIGN_PUBKEY: &str = "pk.ed25519";

// ---- types ----

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Hash(pub generichash::Hash);

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Signature(pub sign::Signature);
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct SigningPublicKey(pub sign::PublicKey);
#[derive(PartialEq, Clone, Debug)]
pub struct SigningKeyPair(pub sign::SigningKeyPair<sign::PublicKey, sign::SecretKey>);

impl SigningKeyPair {
    /// Return the public key of this keypair
    pub fn public_key(&self) -> SigningPublicKey {
        SigningPublicKey(self.0.public_key.clone())
    }
}

// ---- encoding ----

impl enc::Encode for Hash {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_HASH, self.0.as_slice())
    }
}

impl enc::Encode for Signature {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGNATURE, self.0.as_slice())
    }
}

impl enc::Encode for SigningPublicKey {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGN_PUBKEY, self.0.as_slice())
    }
}

impl enc::Encode for SigningKeyPair {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGN_KEYPAIR, self.0.secret_key.as_slice())
    }
}

// ---- calculating hashes, signatures, etc ----

/// Compute the hash of a payload with default dryoc parameters and optionnal key
pub fn compute_hash(bytes: &[u8], key: Option<&[u8; 32]>) -> Hash {
    Hash(generichash::GenericHash::hash_with_defaults(bytes, key).unwrap())
}

/// Generate a new signing keypair
pub fn gen_signing_keypair() -> SigningKeyPair {
    SigningKeyPair(sign::SigningKeyPair::gen_with_defaults())
}

/// Compute the ed25519 signature of a message using a secret key
pub fn compute_signature(message: &[u8], keypair: &SigningKeyPair) -> Signature {
    Signature(
        keypair
            .0
            .sign_with_defaults(message)
            .unwrap()
            .into_parts()
            .0,
    )
}

/// Verify the ed25519 signature of a message using a public key
pub fn verify_signature(
    signature: &Signature,
    message: &[u8],
    public_key: &SigningPublicKey,
) -> bool {
    sign::SignedMessage::from_parts(signature.0.clone(), message.to_vec())
        .verify(&public_key.0)
        .is_ok()
}

// ---- decode helpers ----

pub trait CryptoDec {
    /// Try to interpret this string as a Blake2b512 digest
    /// (32-bytes base64 encoded, prefixed by `h.b2:`)
    ///
    /// Example:
    ///
    /// ```
    /// use nettext::dec::decode;
    /// use nettext::crypto::{compute_hash, CryptoDec};
    ///
    /// let term = decode(b"{
    ///     message = hello;
    ///     hash = h.b2:Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8;
    /// }").unwrap();
    /// let [msg, hash] = term.dict_of(["message", "hash"], false).unwrap();
    /// let expected_hash = compute_hash(msg.raw(), None);
    /// assert_eq!(hash.hash().unwrap(), expected_hash);
    /// ```
    fn hash(&self) -> Result<Hash, dec::TypeError>;

    /// Try to interpret this string as an ed25519 signature
    /// (64 bytes base64 encoded, prefixed by `sig.ed25519:`)
    fn signature(&self) -> Result<Signature, dec::TypeError>;

    /// Try to interpret this string as an ed25519 keypair
    /// (64 bytes base64 encoded, prefixed by `sk.ed25519:`)
    fn keypair(&self) -> Result<SigningKeyPair, dec::TypeError>;

    /// Try to interpret this string as an ed25519 public key
    /// (32 bytes base64 encoded, prefixed by `pk.ed25519:`)
    fn public_key(&self) -> Result<SigningPublicKey, dec::TypeError>;
}

impl<'a, 'b> CryptoDec for dec::Term<'a, 'b> {
    fn hash(&self) -> Result<Hash, dec::TypeError> {
        Ok(Hash(generichash::Hash::from(
            self.marked_bytes_exact(BM_HASH)?,
        )))
    }

    /// Try to interpret this string as an ed25519 signature (64 bytes base64 encoded)
    fn signature(&self) -> Result<Signature, dec::TypeError> {
        Ok(Signature(sign::Signature::from(
            self.marked_bytes_exact(BM_SIGNATURE)?,
        )))
    }

    fn keypair(&self) -> Result<SigningKeyPair, dec::TypeError> {
        let secret_key = sign::SecretKey::from(self.marked_bytes_exact(BM_SIGN_KEYPAIR)?);
        Ok(SigningKeyPair(sign::SigningKeyPair::from_secret_key(
            secret_key,
        )))
    }

    fn public_key(&self) -> Result<SigningPublicKey, dec::TypeError> {
        Ok(SigningPublicKey(sign::PublicKey::from(
            self.marked_bytes_exact(BM_SIGN_PUBKEY)?,
        )))
    }
}