#![warn(
missing_debug_implementations,
missing_docs,
unsafe_code,
bare_trait_objects
)]
#![warn(clippy::pedantic, clippy::nursery)]
#![allow(
// Next `cast_*` lints don't give alternatives.
clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
// Next lints produce too much noise/false positives.
clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
clippy::pub_enum_variant_names,
// '... may panic' lints.
clippy::indexing_slicing,
// Too much work to fix.
clippy::missing_errors_doc, clippy::missing_const_for_fn
)]
#[macro_use]
extern crate serde_derive;
#[doc(inline)]
pub use self::crypto_impl::{
HASH_SIZE, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SEED_LENGTH, SIGNATURE_LENGTH,
};
#[cfg(feature = "sodiumoxide-crypto")]
pub use self::crypto_lib::sodiumoxide::x25519;
#[cfg(feature = "with-protobuf")]
#[doc(hidden)]
pub mod proto;
use hex::{encode as encode_hex, FromHex, FromHexError, ToHex};
use serde::{
de::{self, Deserialize, Deserializer, Visitor},
Serialize, Serializer,
};
use std::{
default::Default,
fmt,
ops::{Index, Range, RangeFrom, RangeFull, RangeTo},
};
#[cfg(feature = "sodiumoxide-crypto")]
use self::crypto_lib::sodiumoxide as crypto_impl;
#[macro_use]
mod macros;
pub(crate) mod crypto_lib;
const BYTES_IN_DEBUG: usize = 4;
fn write_short_hex(f: &mut fmt::Formatter<'_>, slice: &[u8]) -> fmt::Result {
for byte in slice.iter().take(BYTES_IN_DEBUG) {
write!(f, "{:02x}", byte)?;
}
if slice.len() > BYTES_IN_DEBUG {
write!(f, "...")?;
}
Ok(())
}
pub fn sign(data: &[u8], secret_key: &SecretKey) -> Signature {
let impl_signature = crypto_impl::sign(data, &secret_key.0);
Signature(impl_signature)
}
pub fn gen_keypair_from_seed(seed: &Seed) -> (PublicKey, SecretKey) {
let (impl_pub_key, impl_secret_key) = crypto_impl::gen_keypair_from_seed(&seed.0);
(PublicKey(impl_pub_key), SecretKey(impl_secret_key))
}
pub fn gen_keypair() -> (PublicKey, SecretKey) {
let (pubkey, secret_key) = crypto_impl::gen_keypair();
(PublicKey(pubkey), SecretKey(secret_key))
}
pub fn verify(sig: &Signature, data: &[u8], pubkey: &PublicKey) -> bool {
crypto_impl::verify(&sig.0, data, &pubkey.0)
}
pub fn hash(data: &[u8]) -> Hash {
let dig = crypto_impl::hash(data);
Hash(dig)
}
pub fn init() {
if !crypto_impl::init() {
panic!("Cryptographic library initialization failed.");
}
}
#[derive(Debug, Default)]
pub struct HashStream(crypto_impl::HashState);
impl HashStream {
pub fn new() -> Self {
Self(crypto_impl::HashState::init())
}
pub fn update(mut self, chunk: &[u8]) -> Self {
self.0.update(chunk);
self
}
pub fn hash(self) -> Hash {
let dig = self.0.finalize();
Hash(dig)
}
}
#[derive(Debug, Default)]
pub struct SignStream(crypto_impl::SignState);
impl SignStream {
pub fn new() -> Self {
Self(crypto_impl::SignState::init())
}
pub fn update(mut self, chunk: &[u8]) -> Self {
self.0.update(chunk);
self
}
pub fn sign(&mut self, secret_key: &SecretKey) -> Signature {
Signature(self.0.finalize(&secret_key.0))
}
pub fn verify(&mut self, sig: &Signature, public_key: &PublicKey) -> bool {
self.0.verify(&sig.0, &public_key.0)
}
}
implement_public_crypto_wrapper! {
struct PublicKey, PUBLIC_KEY_LENGTH
}
implement_private_crypto_wrapper! {
struct SecretKey, SECRET_KEY_LENGTH
}
implement_public_crypto_wrapper! {
struct Hash, HASH_SIZE
}
implement_public_crypto_wrapper! {
struct Signature, SIGNATURE_LENGTH
}
implement_private_crypto_wrapper! {
struct Seed, SEED_LENGTH
}
implement_serde! {Hash}
implement_serde! {PublicKey}
implement_serde! {SecretKey}
implement_serde! {Seed}
implement_serde! {Signature}
implement_index_traits! {Hash}
implement_index_traits! {PublicKey}
implement_index_traits! {SecretKey}
implement_index_traits! {Seed}
implement_index_traits! {Signature}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct KeyPair {
public_key: PublicKey,
secret_key: SecretKey,
}
impl KeyPair {
pub fn from_keys(public_key: PublicKey, secret_key: SecretKey) -> Self {
assert!(
verify_keys_match(&public_key, &secret_key),
"Public key does not match the secret key."
);
Self {
public_key,
secret_key,
}
}
pub fn random() -> Self {
let (public_key, secret_key) = gen_keypair();
Self {
public_key,
secret_key,
}
}
pub fn from_seed(seed: &Seed) -> Self {
let (public_key, secret_key) = gen_keypair_from_seed(seed);
Self {
public_key,
secret_key,
}
}
pub fn public_key(&self) -> PublicKey {
self.public_key
}
pub fn secret_key(&self) -> &SecretKey {
&self.secret_key
}
}
impl From<(PublicKey, SecretKey)> for KeyPair {
fn from(keys: (PublicKey, SecretKey)) -> Self {
Self::from_keys(keys.0, keys.1)
}
}
fn verify_keys_match(public_key: &PublicKey, secret_key: &SecretKey) -> bool {
crypto_impl::verify_keys_match(&public_key.0, &secret_key.0)
}
#[cfg(test)]
mod tests {
use super::*;
use serde::de::DeserializeOwned;
use hex::FromHex;
#[test]
fn to_from_hex_hash() {
let original = hash(&[]);
let from_hex = Hash::from_hex(original.to_hex()).unwrap();
assert_eq!(original, from_hex);
}
#[test]
fn zero_hash() {
let hash = Hash::zero();
assert_eq!(hash.as_ref(), [0; HASH_SIZE]);
}
#[test]
fn to_from_hex_keys() {
let (p, s) = gen_keypair();
let ph = PublicKey::from_hex(p.to_hex()).unwrap();
assert_eq!(p, ph);
let sh = SecretKey::from_hex(s.to_hex()).unwrap();
assert_eq!(s, sh);
}
#[test]
fn serialize_deserialize_hash() {
assert_serialize_deserialize(&Hash::new([207; HASH_SIZE]));
}
#[test]
fn serialize_deserialize_public_key() {
assert_serialize_deserialize(&PublicKey::new([208; PUBLIC_KEY_LENGTH]));
}
#[test]
fn serialize_deserialize_signature() {
assert_serialize_deserialize(&Signature::new([209; SIGNATURE_LENGTH]));
}
#[test]
fn serialize_deserialize_seed() {
assert_serialize_deserialize(&Seed::new([210; SEED_LENGTH]));
}
#[test]
fn serialize_deserialize_secret_key() {
assert_serialize_deserialize(&SecretKey::new([211; SECRET_KEY_LENGTH]));
}
#[test]
fn debug_format() {
let hash = Hash::new([1; HASH_SIZE]);
assert_eq!(format!("{:?}", &hash), "Hash(01010101...)");
let pk = PublicKey::new([15; PUBLIC_KEY_LENGTH]);
assert_eq!(format!("{:?}", &pk), "PublicKey(0f0f0f0f...)");
let sk = SecretKey::new([8; SECRET_KEY_LENGTH]);
assert_eq!(format!("{:?}", &sk), "SecretKey(08080808...)");
let signature = Signature::new([10; SIGNATURE_LENGTH]);
assert_eq!(format!("{:?}", &signature), "Signature(0a0a0a0a...)");
let seed = Seed::new([4; SEED_LENGTH]);
assert_eq!(format!("{:?}", &seed), "Seed(04040404...)");
let hash = Hash::new([128; HASH_SIZE]);
assert_eq!(format!("{:?}", &hash), "Hash(80808080...)");
let sk = SecretKey::new([255; SECRET_KEY_LENGTH]);
assert_eq!(format!("{:?}", &sk), "SecretKey(ffffffff...)");
}
#[test]
fn display_format() {
let hash = Hash::new([1; HASH_SIZE]);
assert_eq!(format!("{}", &hash), "01010101...");
let pk = PublicKey::new([15; PUBLIC_KEY_LENGTH]);
assert_eq!(format!("{}", &pk), "0f0f0f0f...");
let signature = Signature::new([10; SIGNATURE_LENGTH]);
assert_eq!(format!("{}", &signature), "0a0a0a0a...");
let hash = Hash::new([128; HASH_SIZE]);
assert_eq!(format!("{}", &hash), "80808080...");
}
#[test]
fn hash_streaming_zero() {
let h1 = hash(&[]);
let state = HashStream::new();
let h2 = state.update(&[]).hash();
assert_eq!(h1, h2);
}
#[test]
fn hash_streaming_chunks() {
let data: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
let h1 = hash(&data);
let state = HashStream::new();
let h2 = state.update(&data[..5]).update(&data[5..]).hash();
assert_eq!(h1, h2);
}
#[test]
fn sign_streaming_zero() {
let (pk, sk) = gen_keypair();
let mut creation_stream = SignStream::new().update(&[]);
let sig = creation_stream.sign(&sk);
let mut verified_stream = SignStream::new().update(&[]);
assert!(verified_stream.verify(&sig, &pk));
}
#[test]
fn sign_streaming_chunks() {
let data: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
let (pk, sk) = gen_keypair();
let mut creation_stream = SignStream::new().update(&data[..5]).update(&data[5..]);
let sig = creation_stream.sign(&sk);
let mut verified_stream = SignStream::new().update(&data[..5]).update(&data[5..]);
assert!(verified_stream.verify(&sig, &pk));
}
fn assert_serialize_deserialize<T>(original_value: &T)
where
T: Serialize + DeserializeOwned + PartialEq + fmt::Debug,
{
let json = serde_json::to_string(original_value).unwrap();
let deserialized_value: T = serde_json::from_str(&json).unwrap();
assert_eq!(*original_value, deserialized_value);
}
#[test]
fn valid_keypair() {
let (pk, sk) = gen_keypair();
let _ = KeyPair::from_keys(pk, sk);
}
#[test]
#[should_panic]
fn not_valid_keypair() {
let (pk, _) = gen_keypair();
let (_, sk) = gen_keypair();
let _ = KeyPair::from_keys(pk, sk);
}
}