Move to dryoc crate for crypto primitives

This commit is contained in:
Alex 2022-11-22 14:56:58 +01:00
parent 5651210a83
commit fd957e7ef1
Signed by: lx
GPG key ID: 0E496D15096376BE
6 changed files with 71 additions and 148 deletions

View file

@ -2,7 +2,7 @@
name = "nettext"
description = "A text-based data format for cryptographic network protocols"
authors = ["Alex Auvolat <alex@adnab.me>"]
version = "0.1.1"
version = "0.2.0"
edition = "2021"
license = "AGPL-3.0"
readme = "README.md"
@ -13,10 +13,9 @@ readme = "README.md"
nom = "7.1"
base64 = "0.13"
blake2 = { version = "0.10", optional = true }
rand = "0.7"
ed25519-dalek = { version = "1.0", optional = true }
dryoc = { version = "0.4", optional = true }
serde = { version = "1.0", optional = true, features = ["derive"] }
[features]
default = [ "blake2", "ed25519-dalek", "serde" ]
default = [ "dryoc", "serde" ]

View file

@ -1,42 +0,0 @@
use blake2::{Blake2b512, Digest};
use crate::crypto::CryptoError;
use crate::enc;
/// A Blake2b512 digest
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Blake2Sum([u8; 64]);
impl Blake2Sum {
/// Create a Blake2Sum object by passing the digest as bytes directly
pub fn from_bytes(bytes: [u8; 64]) -> Self {
Self(bytes)
}
/// Compute the Blake2b512 digest of a byte slice
pub fn compute(buf: &[u8]) -> Self {
let mut hasher = Blake2b512::new();
hasher.update(buf);
Self(hasher.finalize()[..].try_into().unwrap())
}
/// Check that this digest corresponds to a given slice
pub fn verify(&self, buf: &[u8]) -> Result<(), CryptoError> {
if Self::compute(buf) == *self {
Ok(())
} else {
Err(CryptoError::InvalidHash)
}
}
/// Return a reference to the inner byte slice
pub fn as_bytes(&self) -> &[u8] {
&self.0[..]
}
}
impl enc::Encode for Blake2Sum {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(self.as_bytes()))
}
}

View file

@ -1,35 +0,0 @@
use rand::prelude::*;
use crate::enc;
pub use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature, Signer, Verifier};
/// Generate a public/secret Ed25519 keypair
pub fn generate_keypair() -> Keypair {
let mut csprng = thread_rng();
Keypair::generate(&mut csprng)
}
impl enc::Encode for Keypair {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(&self.to_bytes()))
}
}
impl enc::Encode for PublicKey {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(self.as_bytes()))
}
}
impl enc::Encode for SecretKey {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(self.as_bytes()))
}
}
impl enc::Encode for Signature {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(&self.to_bytes()))
}
}

View file

@ -1,21 +1,21 @@
//! Helpers to use cryptographic data types in nettext
#[cfg(feature = "blake2")]
mod b2;
pub use dryoc::*;
#[cfg(feature = "blake2")]
pub use b2::*;
use dryoc::types::{Bytes, StackByteArray};
#[cfg(feature = "ed25519-dalek")]
mod ed25519;
use crate::enc;
#[cfg(feature = "ed25519-dalek")]
pub use ed25519::*;
pub type SigningKeyPair = sign::SigningKeyPair<sign::PublicKey, sign::SecretKey>;
/// An error corresponding to a cryptographic check that failed
pub enum CryptoError {
/// A hash verification failed
InvalidHash,
/// A signature verification failed
InvalidSignature,
impl<const N: usize> enc::Encode for StackByteArray<N> {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(self.as_slice()))
}
}
impl enc::Encode for sign::SigningKeyPair<sign::PublicKey, sign::SecretKey> {
fn term(&self) -> enc::Result<'_> {
Ok(enc::bytes(self.secret_key.as_slice()))
}
}

View file

@ -5,7 +5,7 @@ mod error;
use std::collections::HashMap;
#[cfg(any(feature = "blake2", feature = "ed25519-dalek"))]
#[cfg(any(feature = "dryoc"))]
use crate::crypto;
pub use decode::*;
@ -442,7 +442,7 @@ impl<'a, 'b> Term<'a, 'b> {
// ---- CRYPTO HELPERS ----
#[cfg(feature = "blake2")]
#[cfg(feature = "dryoc")]
impl<'a, 'b> Term<'a, 'b> {
/// Try to interpret this string as a Blake2b512 digest (32-bytes base64 encoded)
///
@ -450,43 +450,39 @@ impl<'a, 'b> Term<'a, 'b> {
///
/// ```
/// use nettext::dec::decode;
/// use nettext::crypto::generichash::GenericHash;
///
/// let term = decode(b"{
/// message = hello,
/// hash = 5M-jmj03vjHFlgnoB5cHmcqmihm_qhUTXxZQheAdQaZboeGxRq62vQCStJ6sIUwQPM-jo2WVS7vlL3Sis2IMlA
/// hash = Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8,
/// }").unwrap();
/// let [msg, hash] = term.dict_of(["message", "hash"], false).unwrap();
/// assert!(hash.b2sum().unwrap().verify(msg.raw()).is_ok());
/// let expected_hash = GenericHash::hash_with_defaults(msg.raw(), None::<&Vec<u8>>).unwrap();
/// assert_eq!(hash.b2sum().unwrap(), expected_hash);
/// ```
pub fn b2sum(&self) -> Result<crypto::Blake2Sum, TypeError> {
Ok(crypto::Blake2Sum::from_bytes(self.bytes_exact()?))
}
pub fn b2sum(&self) -> Result<crypto::generichash::Hash, TypeError> {
Ok(crypto::generichash::Hash::from(self.bytes_exact()?))
}
#[cfg(feature = "ed25519-dalek")]
impl<'a, 'b> Term<'a, 'b> {
/// Try to interpret this string as an ed25519 keypair (64 bytes base64 encoded)
pub fn keypair(&self) -> Result<crypto::Keypair, TypeError> {
let bytes = self.bytes_exact::<64>()?;
crypto::Keypair::from_bytes(&bytes).map_err(|_| TypeError::WrongType("KEYPAIR"))
pub fn keypair(&self) -> Result<crypto::SigningKeyPair, TypeError> {
let secret_key = crypto::sign::SecretKey::from(self.bytes_exact()?);
Ok(crypto::SigningKeyPair::from_secret_key(secret_key))
}
/// Try to interpret this string as an ed25519 public key (32 bytes base64 encoded)
pub fn public_key(&self) -> Result<crypto::PublicKey, TypeError> {
let bytes = self.bytes_exact::<32>()?;
crypto::PublicKey::from_bytes(&bytes).map_err(|_| TypeError::WrongType("PUBLICKEY"))
pub fn public_key(&self) -> Result<crypto::sign::PublicKey, TypeError> {
Ok(crypto::sign::PublicKey::from(self.bytes_exact()?))
}
/// Try to interpret this string as an ed25519 secret key (32 bytes base64 encoded)
pub fn secret_key(&self) -> Result<crypto::SecretKey, TypeError> {
let bytes = self.bytes_exact::<32>()?;
crypto::SecretKey::from_bytes(&bytes).map_err(|_| TypeError::WrongType("SECRETKEY"))
pub fn secret_key(&self) -> Result<crypto::sign::SecretKey, TypeError> {
Ok(crypto::sign::SecretKey::from(self.bytes_exact()?))
}
/// Try to interpret this string as an ed25519 signature (64 bytes base64 encoded)
pub fn signature(&self) -> Result<crypto::Signature, TypeError> {
let bytes = self.bytes_exact::<64>()?;
crypto::Signature::from_bytes(&bytes).map_err(|_| TypeError::WrongType("SIGNATURE"))
pub fn signature(&self) -> Result<crypto::sign::Signature, TypeError> {
Ok(crypto::sign::Signature::from(self.bytes_exact()?))
}
}

View file

@ -3,12 +3,13 @@
//! ```
//! use nettext::enc::*;
//! use nettext::dec::*;
//! use nettext::crypto::{self, Signer, Verifier};
//! use nettext::crypto::{SigningKeyPair, generichash::{Hash, GenericHash}, sign::{Signature, SignedMessage}};
//!
//! let keypair = crypto::generate_keypair();
//! let final_payload = {
//! let keypair = SigningKeyPair::gen_with_defaults();
//!
//! // Encode a fist object that represents a payload that will be hashed and signed
//! let text1 = list([
//! let signed_payload = list([
//! string("CALL").unwrap(),
//! string("myfunction").unwrap(),
//! dict([
@ -17,36 +18,38 @@
//! ("c", raw(b"{ a = 12, b = 42 }").unwrap()),
//! ("d", bytes_split(&((0..128u8).collect::<Vec<_>>()))),
//! ]).unwrap(),
//! keypair.public.term().unwrap(),
//! keypair.public_key.term().unwrap(),
//! ]).unwrap().encode();
//! eprintln!("{}", std::str::from_utf8(&text1).unwrap());
//! eprintln!("{}", std::str::from_utf8(&signed_payload).unwrap());
//!
//! let hash = crypto::Blake2Sum::compute(&text1);
//! let sign = keypair.sign(&text1);
//! let hash: Hash = GenericHash::hash_with_defaults(&signed_payload, None::<&Vec<u8>>).unwrap();
//! let (sign, _) = keypair.sign_with_defaults(&signed_payload[..]).unwrap().into_parts();
//!
//! // Encode a second object that represents the signed and hashed payload
//! let text2 = dict([
//! dict([
//! ("hash", hash.term().unwrap()),
//! ("signature", sign.term().unwrap()),
//! ("payload", raw(&text1).unwrap()),
//! ]).unwrap().encode();
//! eprintln!("{}", std::str::from_utf8(&text2).unwrap());
//! ("payload", raw(&signed_payload).unwrap()),
//! ]).unwrap().encode()
//! };
//! eprintln!("{}", std::str::from_utf8(&final_payload).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 signed_object = decode(&final_payload).unwrap();
//! let [hash, signature, payload] = signed_object.dict_of(["hash", "signature", "payload"], false).unwrap();
//! let hash = hash.b2sum().unwrap();
//! let signature = signature.signature().unwrap();
//! let expected_hash = GenericHash::hash_with_defaults(payload.raw(), None::<&Vec<u8>>).unwrap();
//! assert_eq!(hash, expected_hash);
//!
//! 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!(SignedMessage::from_parts(signature, payload.raw()).verify(&pubkey).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:
@ -81,10 +84,12 @@
//! 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;
#[cfg(feature = "dryoc")]
pub mod crypto;
#[cfg(feature = "serde")]
pub mod serde;