├── .github └── dependabot.yml ├── .gitignore ├── Cargo.toml ├── README.md └── src ├── errors.rs ├── hash.rs ├── kdf.rs ├── kx.rs ├── lib.rs ├── random.rs ├── secretbox.rs ├── sign.rs ├── utils.rs └── version.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libhydrogen" 3 | version = "0.4.5" 4 | authors = ["Frank Denis "] 5 | description = "A modern and easy to use cryptography library" 6 | license = "ISC" 7 | homepage = "https://github.com/jedisct1/rust-libhydrogen" 8 | repository = "https://github.com/jedisct1/rust-libhydrogen" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | anyhow = "1.0" 13 | thiserror = "2.0" 14 | libhydrogen-sys = "0.9" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![dependency status](https://deps.rs/repo/github/jedisct1/rust-libhydrogen/status.svg)](https://deps.rs/repo/github/jedisct1/rust-libhydrogen) 2 | 3 | ![libhydrogen for rust](https://raw.github.com/jedisct1/libhydrogen/master/logo.png) 4 | ==================== 5 | 6 | The [Hydrogen](https://github.com/jedisct1/libhydrogen) library is a small, easy-to-use, hard-to-misuse cryptographic library. 7 | 8 | Features: 9 | - Consistent high-level API, inspired by libsodium. Instead of low-level primitives, it exposes simple functions to solve common problems that cryptography can solve. 10 | - 100% built using just two cryptographic building blocks: the [Curve25519](https://cr.yp.to/ecdh.html) elliptic curve, and the [Gimli](https://gimli.cr.yp.to/) permutation. 11 | - Small and easy to audit. Implemented as one tiny file for every set of operation, and adding a single `.c` file to your project is all it takes to use libhydrogen in your project. 12 | - The whole code is released under a single, very liberal license (ISC). 13 | - Zero dynamic memory allocations and low stack requirements (median: 32 bytes, max: 128 bytes). This makes it usable in constrained environments such as microcontrollers. 14 | - Portable. Supports Linux, *BSD, MacOS, Windows, and the Arduino IDE out of the box. 15 | - Can generate cryptographically-secure random numbers, even on Arduino boards. 16 | - Attempts to mitigate the implications of accidental misuse, even on systems with an unreliable PRG and/or no clock. 17 | 18 | This crate implement high-level Rust bindings. 19 | 20 | # Documentation 21 | 22 | * [Rust API documentation](https://docs.rs/libhydrogen) 23 | * [Original libhydrogen documentation](https://github.com/jedisct1/libhydrogen/wiki) 24 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | pub use anyhow::{anyhow, bail, ensure, Error}; 2 | 3 | #[derive(Debug, thiserror::Error)] 4 | pub enum HydroError { 5 | #[error("Invalid input")] 6 | InvalidInput, 7 | #[error("Invalid key")] 8 | InvalidKey, 9 | #[error("Invalid padding")] 10 | InvalidPadding, 11 | #[error("Invalid probe")] 12 | InvalidProbe, 13 | #[error("Invalid signature")] 14 | InvalidSignature, 15 | #[error("Unable to decrypt the ciphertext")] 16 | DecryptionError, 17 | #[error("Unable to initialized the hydrogen library")] 18 | InitError, 19 | #[error("Unsupported output length")] 20 | UnsupportedOutputLength, 21 | } 22 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | use core::{mem::MaybeUninit, ptr}; 2 | 3 | use super::ensure_initialized; 4 | use crate::errors::*; 5 | use crate::ffi; 6 | use crate::utils; 7 | 8 | pub const CONTEXTBYTES: usize = ffi::hydro_hash_CONTEXTBYTES as usize; 9 | pub const KEYBYTES: usize = ffi::hydro_hash_KEYBYTES as usize; 10 | pub const BYTES: usize = ffi::hydro_hash_BYTES as usize; 11 | pub const BYTES_MAX: usize = ffi::hydro_hash_BYTES_MAX as usize; 12 | pub const BYTES_MIN: usize = ffi::hydro_hash_BYTES_MIN as usize; 13 | 14 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 15 | pub struct Context([u8; CONTEXTBYTES]); 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct Key([u8; KEYBYTES]); 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct State(ffi::hydro_hash_state); 22 | 23 | pub struct DefaultHasher { 24 | state: State, 25 | } 26 | 27 | impl DefaultHasher { 28 | #[inline] 29 | fn new(key: Option<&Key>, context: &Context) -> DefaultHasher { 30 | unsafe { 31 | let mut state = MaybeUninit::::uninit(); 32 | let key = match key { 33 | None => ptr::null(), 34 | Some(key) => key.0.as_ptr(), 35 | }; 36 | ffi::hydro_hash_init( 37 | &mut (*state.as_mut_ptr()).0, 38 | context.0.as_ptr() as *const _, 39 | key, 40 | ); 41 | DefaultHasher { 42 | state: state.assume_init(), 43 | } 44 | } 45 | } 46 | 47 | #[inline] 48 | pub fn update(&mut self, input: &[u8]) { 49 | unsafe { 50 | ffi::hydro_hash_update(&mut self.state.0, input.as_ptr() as *const _, input.len()); 51 | } 52 | } 53 | 54 | pub fn finish_into(mut self, out: &mut [u8]) -> Result<(), HydroError> { 55 | unsafe { 56 | if ffi::hydro_hash_final(&mut self.state.0, out.as_mut_ptr(), out.len()) == 0 { 57 | Ok(()) 58 | } else { 59 | Err(HydroError::UnsupportedOutputLength) 60 | } 61 | } 62 | } 63 | 64 | pub fn finish(self, out_len: usize) -> Result, HydroError> { 65 | let mut out = vec![0u8; out_len]; 66 | self.finish_into(&mut out)?; 67 | Ok(out) 68 | } 69 | } 70 | 71 | #[inline] 72 | pub fn init(context: &Context, key: Option<&Key>) -> DefaultHasher { 73 | DefaultHasher::new(key, context) 74 | } 75 | 76 | pub fn hash_into( 77 | out: &mut [u8], 78 | input: &[u8], 79 | context: &Context, 80 | key: Option<&Key>, 81 | ) -> Result<(), HydroError> { 82 | let mut hasher = init(context, key); 83 | hasher.update(input); 84 | hasher.finish_into(out)?; 85 | Ok(()) 86 | } 87 | 88 | pub fn hash( 89 | out_len: usize, 90 | input: &[u8], 91 | context: &Context, 92 | key: Option<&Key>, 93 | ) -> Result, HydroError> { 94 | let mut out = vec![0u8; out_len]; 95 | hash_into(&mut out, input, context, key)?; 96 | Ok(out) 97 | } 98 | 99 | impl Drop for State { 100 | fn drop(&mut self) { 101 | utils::memzero(self) 102 | } 103 | } 104 | 105 | impl Drop for Key { 106 | fn drop(&mut self) { 107 | utils::memzero(self) 108 | } 109 | } 110 | 111 | impl From<[u8; KEYBYTES]> for Key { 112 | #[inline] 113 | fn from(key: [u8; KEYBYTES]) -> Key { 114 | Key(key) 115 | } 116 | } 117 | 118 | impl From for [u8; KEYBYTES] { 119 | #[inline] 120 | fn from(val: Key) -> Self { 121 | val.0 122 | } 123 | } 124 | 125 | impl AsRef<[u8]> for Key { 126 | fn as_ref(&self) -> &[u8] { 127 | &self.0 as &[u8] 128 | } 129 | } 130 | 131 | impl PartialEq for Key { 132 | fn eq(&self, other: &Self) -> bool { 133 | utils::equal(self, other) 134 | } 135 | } 136 | 137 | impl Eq for Key {} 138 | 139 | impl Key { 140 | pub fn gen() -> Key { 141 | ensure_initialized(); 142 | unsafe { 143 | let mut key = MaybeUninit::::uninit(); 144 | ffi::hydro_hash_keygen((*key.as_mut_ptr()).0.as_mut_ptr()); 145 | key.assume_init() 146 | } 147 | } 148 | } 149 | 150 | impl From<&'static str> for Context { 151 | fn from(context_str: &'static str) -> Context { 152 | let context_str_u8 = context_str.as_bytes(); 153 | let context_str_u8_len = context_str_u8.len(); 154 | if context_str_u8_len > CONTEXTBYTES { 155 | panic!("Context too long"); 156 | } 157 | let mut context = Context::default(); 158 | context.0[..context_str_u8_len].copy_from_slice(context_str_u8); 159 | context 160 | } 161 | } 162 | 163 | impl From<[u8; CONTEXTBYTES]> for Context { 164 | #[inline] 165 | fn from(context: [u8; CONTEXTBYTES]) -> Context { 166 | Context(context) 167 | } 168 | } 169 | 170 | impl From for [u8; CONTEXTBYTES] { 171 | #[inline] 172 | fn from(val: Context) -> Self { 173 | val.0 174 | } 175 | } 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use crate::*; 180 | 181 | #[test] 182 | fn test_hash() { 183 | init().unwrap(); 184 | 185 | let context = "tests".into(); 186 | let key = hash::Key::gen(); 187 | assert_ne!(key, hash::Key::gen()); 188 | 189 | let mut h = hash::init(&context, Some(&key)); 190 | h.update(b"test message"); 191 | h.finish(hash::BYTES).unwrap(); 192 | 193 | hash::hash(hash::BYTES_MIN, b"test message", &context, Some(&key)).unwrap(); 194 | 195 | let keyx: [u8; hash::KEYBYTES] = key.clone().into(); 196 | let keyy: hash::Key = keyx.into(); 197 | assert_eq!(key, keyy); 198 | 199 | let contextx: [u8; hash::CONTEXTBYTES] = context.into(); 200 | let contexty: hash::Context = contextx.into(); 201 | assert_eq!(context, contexty); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/kdf.rs: -------------------------------------------------------------------------------- 1 | //! Key derivation 2 | //! 3 | //! Multiple secret subkeys can be derived from a single, high-entropy master 4 | //! key. 5 | //! 6 | //! With the master key and a key identifier, a subkey can be deterministically 7 | //! computed. However, given a subkey, an attacker cannot compute the master key 8 | //! nor any other subkeys. 9 | //! 10 | //! The derive_from_key API can derive up to 2^64 keys from a single master key 11 | //! and context, and individual subkeys can have an arbitrary length between 128 12 | //! (16 bytes) and 524,280 bits (65535 bytes). 13 | //! 14 | //! # Examples 15 | //! ``` 16 | //! // these must come from a high entropy source such as a hardware RNG. 17 | //! // A password is not ok. 18 | //! let master_key_data = [ 19 | //! 64, 33, 195, 234, 107, 63, 107, 237, 113, 199, 183, 130, 203, 194, 247, 31, 76, 51, 203, 20 | //! 163, 126, 238, 206, 125, 225, 74, 103, 105, 133, 181, 61, 189, 21 | //! ]; 22 | //! 23 | //! let master = libhydrogen::kdf::Key::from(master_key_data); 24 | //! let context = libhydrogen::kdf::Context::default(); 25 | //! 26 | //! let subkey1 = libhydrogen::kdf::derive_from_key(32, 1, &context, &master).unwrap(); 27 | //! let subkey2 = libhydrogen::kdf::derive_from_key(32, 2, &context, &master).unwrap(); 28 | //! ``` 29 | 30 | use core::mem::MaybeUninit; 31 | 32 | use super::ensure_initialized; 33 | use crate::errors::*; 34 | use crate::ffi; 35 | use crate::utils; 36 | 37 | pub const CONTEXTBYTES: usize = ffi::hydro_kdf_CONTEXTBYTES as usize; 38 | pub const KEYBYTES: usize = ffi::hydro_kdf_KEYBYTES as usize; 39 | pub const BYTES_MAX: usize = ffi::hydro_kdf_BYTES_MAX as usize; 40 | pub const BYTES_MIN: usize = ffi::hydro_kdf_BYTES_MIN as usize; 41 | 42 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 43 | pub struct Context([u8; CONTEXTBYTES]); 44 | 45 | #[derive(Debug, Clone)] 46 | pub struct Key([u8; KEYBYTES]); 47 | 48 | /// Derives a subkey_id-th subkey of length subkey_len bytes using the master 49 | /// key and the context. 50 | pub fn derive_from_key( 51 | subkey_len: usize, 52 | subkey_id: u64, 53 | context: &Context, 54 | key: &Key, 55 | ) -> Result, HydroError> { 56 | let mut subkey = vec![0u8; subkey_len]; 57 | if unsafe { 58 | ffi::hydro_kdf_derive_from_key( 59 | subkey.as_mut_ptr(), 60 | subkey_len, 61 | subkey_id, 62 | context.0.as_ptr() as *const _, 63 | key.0.as_ptr(), 64 | ) 65 | } != 0 66 | { 67 | return Err(HydroError::UnsupportedOutputLength); 68 | } 69 | Ok(subkey) 70 | } 71 | 72 | impl Drop for Key { 73 | fn drop(&mut self) { 74 | utils::memzero(self) 75 | } 76 | } 77 | 78 | impl From<[u8; KEYBYTES]> for Key { 79 | #[inline] 80 | fn from(key: [u8; KEYBYTES]) -> Key { 81 | Key(key) 82 | } 83 | } 84 | 85 | impl From for [u8; KEYBYTES] { 86 | #[inline] 87 | fn from(val: Key) -> Self { 88 | val.0 89 | } 90 | } 91 | 92 | impl AsRef<[u8]> for Key { 93 | fn as_ref(&self) -> &[u8] { 94 | &self.0 as &[u8] 95 | } 96 | } 97 | 98 | impl PartialEq for Key { 99 | fn eq(&self, other: &Self) -> bool { 100 | utils::equal(self, other) 101 | } 102 | } 103 | 104 | impl Eq for Key {} 105 | 106 | impl Key { 107 | pub fn gen() -> Key { 108 | ensure_initialized(); 109 | unsafe { 110 | let mut key = MaybeUninit::::uninit(); 111 | ffi::hydro_kdf_keygen((*key.as_mut_ptr()).0.as_mut_ptr()); 112 | key.assume_init() 113 | } 114 | } 115 | } 116 | 117 | impl From<&'static str> for Context { 118 | fn from(context_str: &'static str) -> Context { 119 | let context_str_u8 = context_str.as_bytes(); 120 | let context_str_u8_len = context_str_u8.len(); 121 | if context_str_u8_len > CONTEXTBYTES { 122 | panic!("Context too long"); 123 | } 124 | let mut context = Context::default(); 125 | context.0[..context_str_u8_len].copy_from_slice(context_str_u8); 126 | context 127 | } 128 | } 129 | 130 | impl From<[u8; CONTEXTBYTES]> for Context { 131 | #[inline] 132 | fn from(context: [u8; CONTEXTBYTES]) -> Context { 133 | Context(context) 134 | } 135 | } 136 | 137 | impl From for [u8; CONTEXTBYTES] { 138 | #[inline] 139 | fn from(val: Context) -> Self { 140 | val.0 141 | } 142 | } 143 | 144 | #[cfg(test)] 145 | mod tests { 146 | use crate::*; 147 | 148 | #[test] 149 | fn test_kdf() { 150 | init().unwrap(); 151 | 152 | let context = "tests".into(); 153 | let key = kdf::Key::gen(); 154 | let subkey = kdf::derive_from_key(50, 1, &context, &key).unwrap(); 155 | assert_eq!(subkey.len(), 50); 156 | 157 | let contextx: [u8; kdf::CONTEXTBYTES] = context.into(); 158 | let contexty: kdf::Context = contextx.into(); 159 | assert_eq!(context, contexty); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/kx.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | convert::TryFrom, 3 | fmt, 4 | mem::{size_of_val, MaybeUninit}, 5 | ptr, 6 | }; 7 | 8 | use super::ensure_initialized; 9 | use crate::{errors::*, ffi, random, utils}; 10 | 11 | pub const PUBLICKEYBYTES: usize = ffi::hydro_kx_PUBLICKEYBYTES as usize; 12 | pub const SECRETKEYBYTES: usize = ffi::hydro_kx_SECRETKEYBYTES as usize; 13 | pub const SESSIONKEYBYTES: usize = ffi::hydro_kx_SESSIONKEYBYTES as usize; 14 | pub const SEEDBYTES: usize = ffi::hydro_kx_SEEDBYTES as usize; 15 | pub const PSKBYTES: usize = ffi::hydro_kx_PSKBYTES as usize; 16 | pub const N_PACKET1BYTES: usize = ffi::hydro_kx_N_PACKET1BYTES as usize; 17 | pub const KK_PACKET1BYTES: usize = ffi::hydro_kx_KK_PACKET1BYTES as usize; 18 | pub const KK_PACKET2BYTES: usize = ffi::hydro_kx_KK_PACKET2BYTES as usize; 19 | pub const NK_PACKET1BYTES: usize = ffi::hydro_kx_NK_PACKET1BYTES as usize; 20 | pub const NK_PACKET2BYTES: usize = ffi::hydro_kx_NK_PACKET2BYTES as usize; 21 | pub const XX_PACKET1BYTES: usize = ffi::hydro_kx_XX_PACKET1BYTES as usize; 22 | pub const XX_PACKET2BYTES: usize = ffi::hydro_kx_XX_PACKET2BYTES as usize; 23 | pub const XX_PACKET3BYTES: usize = ffi::hydro_kx_XX_PACKET3BYTES as usize; 24 | 25 | #[derive(Debug, Copy, PartialEq, Eq, Clone)] 26 | pub struct PublicKey([u8; PUBLICKEYBYTES]); 27 | 28 | #[derive(Clone)] 29 | pub struct SecretKey([u8; SECRETKEYBYTES]); 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct Seed([u8; SEEDBYTES]); 33 | 34 | #[derive(Clone)] 35 | pub struct Psk([u8; PSKBYTES]); 36 | 37 | #[derive(Clone)] 38 | pub struct NPacket1([u8; N_PACKET1BYTES]); 39 | 40 | #[derive(Clone)] 41 | pub struct KKPacket1([u8; KK_PACKET1BYTES]); 42 | 43 | #[derive(Clone)] 44 | pub struct KKPacket2([u8; KK_PACKET2BYTES]); 45 | 46 | #[derive(Clone)] 47 | pub struct NKPacket1([u8; NK_PACKET1BYTES]); 48 | 49 | #[derive(Clone)] 50 | pub struct NKPacket2([u8; NK_PACKET2BYTES]); 51 | 52 | #[derive(Clone)] 53 | pub struct XXPacket1([u8; XX_PACKET1BYTES]); 54 | 55 | #[derive(Clone)] 56 | pub struct XXPacket2([u8; XX_PACKET2BYTES]); 57 | 58 | #[derive(Clone)] 59 | pub struct XXPacket3([u8; XX_PACKET3BYTES]); 60 | 61 | #[derive(Clone)] 62 | pub struct KeyPair { 63 | pub public_key: PublicKey, 64 | pub secret_key: SecretKey, 65 | } 66 | 67 | #[derive(Clone)] 68 | pub struct SessionKey([u8; SESSIONKEYBYTES]); 69 | 70 | #[derive(Clone)] 71 | #[repr(C)] 72 | pub struct SessionKeyPair { 73 | pub rx: SessionKey, 74 | pub tx: SessionKey, 75 | } 76 | 77 | #[derive(Debug, Clone)] 78 | #[repr(C)] 79 | pub struct State(ffi::hydro_kx_state); 80 | 81 | pub fn n_1( 82 | npacket1: &mut NPacket1, 83 | psk: Option<&Psk>, 84 | server_static_keypair_public_key: &PublicKey, 85 | ) -> Result { 86 | ensure_initialized(); 87 | let psk = match psk { 88 | None => ptr::null(), 89 | Some(psk) => psk.0.as_ptr(), 90 | }; 91 | unsafe { 92 | let mut session_keypair_c = MaybeUninit::::uninit(); 93 | if ffi::hydro_kx_n_1( 94 | session_keypair_c.as_mut_ptr(), 95 | npacket1.0.as_mut_ptr(), 96 | psk, 97 | server_static_keypair_public_key.0.as_ptr(), 98 | ) != 0 99 | { 100 | Err(HydroError::InvalidInput) 101 | } else { 102 | Ok(SessionKeyPair::from(session_keypair_c)) 103 | } 104 | } 105 | } 106 | 107 | pub fn n_2( 108 | npacket1: &NPacket1, 109 | psk: Option<&Psk>, 110 | server_static_keypair: &KeyPair, 111 | ) -> Result { 112 | ensure_initialized(); 113 | let psk = match psk { 114 | None => ptr::null(), 115 | Some(psk) => psk.0.as_ptr(), 116 | }; 117 | unsafe { 118 | let mut session_keypair_c = MaybeUninit::::uninit(); 119 | if ffi::hydro_kx_n_2( 120 | session_keypair_c.as_mut_ptr(), 121 | npacket1.0.as_ptr(), 122 | psk, 123 | server_static_keypair as *const _ as *const _, 124 | ) != 0 125 | { 126 | Err(HydroError::InvalidInput) 127 | } else { 128 | Ok(SessionKeyPair::from(session_keypair_c)) 129 | } 130 | } 131 | } 132 | 133 | pub fn kk_1( 134 | client_state: &mut State, 135 | kkpacket1: &mut KKPacket1, 136 | server_static_keypair_public_key: &PublicKey, 137 | client_static_keypair: &KeyPair, 138 | ) -> Result<(), HydroError> { 139 | ensure_initialized(); 140 | unsafe { 141 | if ffi::hydro_kx_kk_1( 142 | client_state as *mut _ as *mut _, 143 | kkpacket1.0.as_mut_ptr(), 144 | server_static_keypair_public_key.0.as_ptr(), 145 | client_static_keypair as *const _ as *const _, 146 | ) != 0 147 | { 148 | Err(HydroError::InvalidInput) 149 | } else { 150 | Ok(()) 151 | } 152 | } 153 | } 154 | 155 | pub fn kk_2( 156 | kkpacket2: &mut KKPacket2, 157 | kkpacket1: &KKPacket1, 158 | client_static_keypair_public_key: &PublicKey, 159 | server_static_keypair: &KeyPair, 160 | ) -> Result { 161 | ensure_initialized(); 162 | unsafe { 163 | let mut session_keypair_c = MaybeUninit::::uninit(); 164 | if ffi::hydro_kx_kk_2( 165 | session_keypair_c.as_mut_ptr(), 166 | kkpacket2.0.as_mut_ptr(), 167 | kkpacket1.0.as_ptr(), 168 | client_static_keypair_public_key.0.as_ptr(), 169 | server_static_keypair as *const _ as *const _, 170 | ) != 0 171 | { 172 | Err(HydroError::InvalidInput) 173 | } else { 174 | Ok(SessionKeyPair::from(session_keypair_c)) 175 | } 176 | } 177 | } 178 | 179 | pub fn kk_3( 180 | client_state: &mut State, 181 | kkpacket2: &KKPacket2, 182 | client_static_keypair: &KeyPair, 183 | ) -> Result { 184 | ensure_initialized(); 185 | unsafe { 186 | let mut session_keypair_c = MaybeUninit::::uninit(); 187 | if ffi::hydro_kx_kk_3( 188 | client_state as *mut _ as *mut _, 189 | session_keypair_c.as_mut_ptr(), 190 | kkpacket2.0.as_ptr(), 191 | client_static_keypair as *const _ as *const _, 192 | ) != 0 193 | { 194 | Err(HydroError::InvalidInput) 195 | } else { 196 | Ok(SessionKeyPair::from(session_keypair_c)) 197 | } 198 | } 199 | } 200 | 201 | pub fn nk_1( 202 | client_state: &mut State, 203 | nkpacket1: &mut NKPacket1, 204 | psk: Option<&Psk>, 205 | server_static_keypair_public_key: &PublicKey, 206 | ) -> Result<(), HydroError> { 207 | ensure_initialized(); 208 | let psk = match psk { 209 | None => ptr::null(), 210 | Some(psk) => psk.0.as_ptr(), 211 | }; 212 | unsafe { 213 | if ffi::hydro_kx_nk_1( 214 | client_state as *mut _ as *mut _, 215 | nkpacket1.0.as_mut_ptr(), 216 | psk, 217 | server_static_keypair_public_key.0.as_ptr(), 218 | ) != 0 219 | { 220 | Err(HydroError::InvalidInput) 221 | } else { 222 | Ok(()) 223 | } 224 | } 225 | } 226 | 227 | pub fn nk_2( 228 | nkpacket2: &mut NKPacket2, 229 | nkpacket1: &NKPacket1, 230 | psk: Option<&Psk>, 231 | server_static_keypair: &KeyPair, 232 | ) -> Result { 233 | ensure_initialized(); 234 | let psk = match psk { 235 | None => ptr::null(), 236 | Some(psk) => psk.0.as_ptr(), 237 | }; 238 | unsafe { 239 | let mut session_keypair_c = MaybeUninit::::uninit(); 240 | if ffi::hydro_kx_nk_2( 241 | session_keypair_c.as_mut_ptr(), 242 | nkpacket2.0.as_mut_ptr(), 243 | nkpacket1.0.as_ptr(), 244 | psk, 245 | server_static_keypair as *const _ as *const _, 246 | ) != 0 247 | { 248 | Err(HydroError::InvalidInput) 249 | } else { 250 | Ok(SessionKeyPair::from(session_keypair_c)) 251 | } 252 | } 253 | } 254 | 255 | pub fn nk_3(client_state: &mut State, nkpacket2: &NKPacket2) -> Result { 256 | ensure_initialized(); 257 | unsafe { 258 | let mut session_keypair_c = MaybeUninit::::uninit(); 259 | if ffi::hydro_kx_nk_3( 260 | client_state as *mut _ as *mut _, 261 | session_keypair_c.as_mut_ptr(), 262 | nkpacket2.0.as_ptr(), 263 | ) != 0 264 | { 265 | Err(HydroError::InvalidInput) 266 | } else { 267 | Ok(SessionKeyPair::from(session_keypair_c)) 268 | } 269 | } 270 | } 271 | 272 | pub fn xx_1( 273 | client_state: &mut State, 274 | xxpacket1: &mut XXPacket1, 275 | psk: Option<&Psk>, 276 | ) -> Result<(), HydroError> { 277 | ensure_initialized(); 278 | let psk = match psk { 279 | None => ptr::null(), 280 | Some(psk) => psk.0.as_ptr(), 281 | }; 282 | unsafe { 283 | if ffi::hydro_kx_xx_1( 284 | client_state as *mut _ as *mut _, 285 | xxpacket1.0.as_mut_ptr(), 286 | psk, 287 | ) != 0 288 | { 289 | Err(HydroError::InvalidInput) 290 | } else { 291 | Ok(()) 292 | } 293 | } 294 | } 295 | 296 | pub fn xx_2( 297 | server_state: &mut State, 298 | xxpacket2: &mut XXPacket2, 299 | xxpacket1: &XXPacket1, 300 | psk: Option<&Psk>, 301 | server_static_keypair: &KeyPair, 302 | ) -> Result<(), HydroError> { 303 | ensure_initialized(); 304 | let psk = match psk { 305 | None => ptr::null(), 306 | Some(psk) => psk.0.as_ptr(), 307 | }; 308 | unsafe { 309 | if ffi::hydro_kx_xx_2( 310 | server_state as *mut _ as *mut _, 311 | xxpacket2.0.as_mut_ptr(), 312 | xxpacket1.0.as_ptr(), 313 | psk, 314 | server_static_keypair as *const _ as *const _, 315 | ) != 0 316 | { 317 | Err(HydroError::InvalidInput) 318 | } else { 319 | Ok(()) 320 | } 321 | } 322 | } 323 | 324 | pub fn xx_3( 325 | client_state: &mut State, 326 | xxpacket3: &mut XXPacket3, 327 | peer_static_public_key: Option<&mut PublicKey>, 328 | xxpacket2: &XXPacket2, 329 | psk: Option<&Psk>, 330 | client_static_keypair: &KeyPair, 331 | ) -> Result { 332 | ensure_initialized(); 333 | let psk = match psk { 334 | None => ptr::null(), 335 | Some(psk) => psk.0.as_ptr(), 336 | }; 337 | let peer_static_public_key = match peer_static_public_key { 338 | None => ptr::null(), 339 | Some(peer_static_public_key) => peer_static_public_key.0.as_mut_ptr(), 340 | }; 341 | unsafe { 342 | let mut session_keypair_c = MaybeUninit::::uninit(); 343 | if ffi::hydro_kx_xx_3( 344 | client_state as *mut _ as *mut _, 345 | session_keypair_c.as_mut_ptr(), 346 | xxpacket3.0.as_mut_ptr(), 347 | peer_static_public_key as *mut _, 348 | xxpacket2.0.as_ptr(), 349 | psk, 350 | client_static_keypair as *const _ as *const _, 351 | ) != 0 352 | { 353 | Err(HydroError::InvalidInput) 354 | } else { 355 | Ok(SessionKeyPair::from(session_keypair_c)) 356 | } 357 | } 358 | } 359 | 360 | pub fn xx_4( 361 | server_state: &mut State, 362 | peer_static_public_key: Option<&mut PublicKey>, 363 | xxpacket3: &XXPacket3, 364 | psk: Option<&Psk>, 365 | ) -> Result { 366 | ensure_initialized(); 367 | let psk = match psk { 368 | None => ptr::null(), 369 | Some(psk) => psk.0.as_ptr(), 370 | }; 371 | let peer_static_public_key = match peer_static_public_key { 372 | None => ptr::null(), 373 | Some(peer_static_public_key) => peer_static_public_key.0.as_mut_ptr(), 374 | }; 375 | unsafe { 376 | let mut session_keypair_c = MaybeUninit::::uninit(); 377 | if ffi::hydro_kx_xx_4( 378 | server_state as *mut _ as *mut _, 379 | session_keypair_c.as_mut_ptr(), 380 | peer_static_public_key as *mut _, 381 | xxpacket3.0.as_ptr(), 382 | psk, 383 | ) != 0 384 | { 385 | Err(HydroError::InvalidInput) 386 | } else { 387 | Ok(SessionKeyPair::from(session_keypair_c)) 388 | } 389 | } 390 | } 391 | 392 | impl Drop for State { 393 | fn drop(&mut self) { 394 | utils::memzero(self) 395 | } 396 | } 397 | 398 | impl Drop for SecretKey { 399 | fn drop(&mut self) { 400 | utils::memzero(self) 401 | } 402 | } 403 | 404 | impl From<[u8; SECRETKEYBYTES]> for SecretKey { 405 | #[inline] 406 | fn from(key: [u8; SECRETKEYBYTES]) -> SecretKey { 407 | SecretKey(key) 408 | } 409 | } 410 | 411 | impl From for [u8; SECRETKEYBYTES] { 412 | #[inline] 413 | fn from(val: SecretKey) -> Self { 414 | val.0 415 | } 416 | } 417 | 418 | impl AsRef<[u8]> for SecretKey { 419 | fn as_ref(&self) -> &[u8] { 420 | &self.0 as &[u8] 421 | } 422 | } 423 | 424 | impl PartialEq for SecretKey { 425 | fn eq(&self, other: &Self) -> bool { 426 | utils::equal(self, other) 427 | } 428 | } 429 | 430 | impl Eq for SecretKey {} 431 | 432 | impl From<[u8; PUBLICKEYBYTES]> for PublicKey { 433 | #[inline] 434 | fn from(key: [u8; PUBLICKEYBYTES]) -> PublicKey { 435 | PublicKey(key) 436 | } 437 | } 438 | 439 | impl From for [u8; PUBLICKEYBYTES] { 440 | #[inline] 441 | fn from(val: PublicKey) -> Self { 442 | val.0 443 | } 444 | } 445 | 446 | impl AsRef<[u8]> for PublicKey { 447 | fn as_ref(&self) -> &[u8] { 448 | &self.0 as &[u8] 449 | } 450 | } 451 | 452 | impl Drop for Seed { 453 | fn drop(&mut self) { 454 | utils::memzero(self) 455 | } 456 | } 457 | 458 | impl From<[u8; SEEDBYTES]> for Seed { 459 | #[inline] 460 | fn from(seed: [u8; SEEDBYTES]) -> Seed { 461 | Seed(seed) 462 | } 463 | } 464 | 465 | impl From for [u8; SEEDBYTES] { 466 | #[inline] 467 | fn from(val: Seed) -> Self { 468 | val.0 469 | } 470 | } 471 | 472 | impl AsRef<[u8]> for Seed { 473 | fn as_ref(&self) -> &[u8] { 474 | &self.0 as &[u8] 475 | } 476 | } 477 | 478 | impl PartialEq for Seed { 479 | fn eq(&self, other: &Self) -> bool { 480 | utils::equal(self, other) 481 | } 482 | } 483 | 484 | impl Eq for Seed {} 485 | 486 | impl From<[u8; PSKBYTES]> for Psk { 487 | #[inline] 488 | fn from(key: [u8; PSKBYTES]) -> Psk { 489 | Psk(key) 490 | } 491 | } 492 | 493 | impl From for [u8; PSKBYTES] { 494 | #[inline] 495 | fn from(val: Psk) -> Self { 496 | val.0 497 | } 498 | } 499 | 500 | impl AsRef<[u8]> for Psk { 501 | fn as_ref(&self) -> &[u8] { 502 | &self.0 as &[u8] 503 | } 504 | } 505 | 506 | impl TryFrom<&'static str> for Psk { 507 | type Error = HydroError; 508 | 509 | fn try_from(psk_str: &'static str) -> Result { 510 | let psk_str_u8 = psk_str.as_bytes(); 511 | if psk_str_u8.len() != PSKBYTES { 512 | Err(HydroError::InvalidInput) 513 | } else { 514 | let mut arr: [u8; PSKBYTES] = [0u8; PSKBYTES]; 515 | arr.copy_from_slice(&psk_str_u8[..PSKBYTES]); 516 | Ok(Psk::from(arr)) 517 | } 518 | } 519 | } 520 | 521 | impl From<[u8; N_PACKET1BYTES]> for NPacket1 { 522 | #[inline] 523 | fn from(npacket1: [u8; N_PACKET1BYTES]) -> NPacket1 { 524 | NPacket1(npacket1) 525 | } 526 | } 527 | 528 | impl From for [u8; N_PACKET1BYTES] { 529 | #[inline] 530 | fn from(val: NPacket1) -> Self { 531 | val.0 532 | } 533 | } 534 | 535 | impl AsRef<[u8]> for NPacket1 { 536 | fn as_ref(&self) -> &[u8] { 537 | &self.0 as &[u8] 538 | } 539 | } 540 | 541 | impl From<[u8; KK_PACKET1BYTES]> for KKPacket1 { 542 | #[inline] 543 | fn from(kkpacket1: [u8; KK_PACKET1BYTES]) -> KKPacket1 { 544 | KKPacket1(kkpacket1) 545 | } 546 | } 547 | 548 | impl From for [u8; KK_PACKET1BYTES] { 549 | #[inline] 550 | fn from(val: KKPacket1) -> Self { 551 | val.0 552 | } 553 | } 554 | 555 | impl AsRef<[u8]> for KKPacket1 { 556 | fn as_ref(&self) -> &[u8] { 557 | &self.0 as &[u8] 558 | } 559 | } 560 | 561 | impl From<[u8; KK_PACKET2BYTES]> for KKPacket2 { 562 | #[inline] 563 | fn from(kkpacket2: [u8; KK_PACKET2BYTES]) -> KKPacket2 { 564 | KKPacket2(kkpacket2) 565 | } 566 | } 567 | 568 | impl From for [u8; KK_PACKET2BYTES] { 569 | #[inline] 570 | fn from(val: KKPacket2) -> Self { 571 | val.0 572 | } 573 | } 574 | 575 | impl AsRef<[u8]> for KKPacket2 { 576 | fn as_ref(&self) -> &[u8] { 577 | &self.0 as &[u8] 578 | } 579 | } 580 | 581 | impl From<[u8; NK_PACKET1BYTES]> for NKPacket1 { 582 | #[inline] 583 | fn from(nkpacket1: [u8; NK_PACKET1BYTES]) -> NKPacket1 { 584 | NKPacket1(nkpacket1) 585 | } 586 | } 587 | 588 | impl From for [u8; NK_PACKET1BYTES] { 589 | #[inline] 590 | fn from(val: NKPacket1) -> Self { 591 | val.0 592 | } 593 | } 594 | 595 | impl AsRef<[u8]> for NKPacket1 { 596 | fn as_ref(&self) -> &[u8] { 597 | &self.0 as &[u8] 598 | } 599 | } 600 | 601 | impl From<[u8; NK_PACKET2BYTES]> for NKPacket2 { 602 | #[inline] 603 | fn from(nkpacket2: [u8; NK_PACKET2BYTES]) -> NKPacket2 { 604 | NKPacket2(nkpacket2) 605 | } 606 | } 607 | 608 | impl From for [u8; NK_PACKET2BYTES] { 609 | #[inline] 610 | fn from(val: NKPacket2) -> Self { 611 | val.0 612 | } 613 | } 614 | 615 | impl AsRef<[u8]> for NKPacket2 { 616 | fn as_ref(&self) -> &[u8] { 617 | &self.0 as &[u8] 618 | } 619 | } 620 | 621 | impl From<[u8; XX_PACKET1BYTES]> for XXPacket1 { 622 | #[inline] 623 | fn from(xxpacket1: [u8; XX_PACKET1BYTES]) -> XXPacket1 { 624 | XXPacket1(xxpacket1) 625 | } 626 | } 627 | 628 | impl From for [u8; XX_PACKET1BYTES] { 629 | #[inline] 630 | fn from(val: XXPacket1) -> Self { 631 | val.0 632 | } 633 | } 634 | 635 | impl AsRef<[u8]> for XXPacket1 { 636 | fn as_ref(&self) -> &[u8] { 637 | &self.0 as &[u8] 638 | } 639 | } 640 | 641 | impl From<[u8; XX_PACKET2BYTES]> for XXPacket2 { 642 | #[inline] 643 | fn from(xxpacket2: [u8; XX_PACKET2BYTES]) -> XXPacket2 { 644 | XXPacket2(xxpacket2) 645 | } 646 | } 647 | 648 | impl From for [u8; XX_PACKET2BYTES] { 649 | #[inline] 650 | fn from(val: XXPacket2) -> Self { 651 | val.0 652 | } 653 | } 654 | 655 | impl AsRef<[u8]> for XXPacket2 { 656 | fn as_ref(&self) -> &[u8] { 657 | &self.0 as &[u8] 658 | } 659 | } 660 | 661 | impl From<[u8; XX_PACKET3BYTES]> for XXPacket3 { 662 | #[inline] 663 | fn from(xxpacket3: [u8; XX_PACKET3BYTES]) -> XXPacket3 { 664 | XXPacket3(xxpacket3) 665 | } 666 | } 667 | 668 | impl From for [u8; XX_PACKET3BYTES] { 669 | #[inline] 670 | fn from(val: XXPacket3) -> Self { 671 | val.0 672 | } 673 | } 674 | 675 | impl AsRef<[u8]> for XXPacket3 { 676 | fn as_ref(&self) -> &[u8] { 677 | &self.0 as &[u8] 678 | } 679 | } 680 | 681 | impl Drop for SessionKey { 682 | fn drop(&mut self) { 683 | utils::memzero(self) 684 | } 685 | } 686 | 687 | impl From<[u8; SESSIONKEYBYTES]> for SessionKey { 688 | #[inline] 689 | fn from(key: [u8; SESSIONKEYBYTES]) -> SessionKey { 690 | SessionKey(key) 691 | } 692 | } 693 | 694 | impl From for [u8; SESSIONKEYBYTES] { 695 | #[inline] 696 | fn from(val: SessionKey) -> Self { 697 | val.0 698 | } 699 | } 700 | 701 | impl AsRef<[u8]> for SessionKey { 702 | fn as_ref(&self) -> &[u8] { 703 | &self.0 as &[u8] 704 | } 705 | } 706 | 707 | impl PartialEq for SessionKey { 708 | fn eq(&self, other: &Self) -> bool { 709 | utils::equal(self, other) 710 | } 711 | } 712 | 713 | impl Eq for SessionKey {} 714 | 715 | impl fmt::Debug for NPacket1 { 716 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 717 | let mut builder = f.debug_tuple("NPacket1"); 718 | for byte in self.as_ref().iter() { 719 | builder.field(byte); 720 | } 721 | builder.finish() 722 | } 723 | } 724 | 725 | impl fmt::Debug for KKPacket1 { 726 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 727 | let mut builder = f.debug_tuple("KKPacket1"); 728 | for byte in self.as_ref().iter() { 729 | builder.field(byte); 730 | } 731 | builder.finish() 732 | } 733 | } 734 | 735 | impl fmt::Debug for KKPacket2 { 736 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 737 | let mut builder = f.debug_tuple("KKPacket2"); 738 | for byte in self.as_ref().iter() { 739 | builder.field(byte); 740 | } 741 | builder.finish() 742 | } 743 | } 744 | 745 | impl fmt::Debug for NKPacket1 { 746 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 747 | let mut builder = f.debug_tuple("NKPacket1"); 748 | for byte in self.as_ref().iter() { 749 | builder.field(byte); 750 | } 751 | builder.finish() 752 | } 753 | } 754 | 755 | impl fmt::Debug for NKPacket2 { 756 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 757 | let mut builder = f.debug_tuple("NKPacket2"); 758 | for byte in self.as_ref().iter() { 759 | builder.field(byte); 760 | } 761 | builder.finish() 762 | } 763 | } 764 | 765 | impl fmt::Debug for XXPacket1 { 766 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 767 | let mut builder = f.debug_tuple("XXPacket1"); 768 | for byte in self.as_ref().iter() { 769 | builder.field(byte); 770 | } 771 | builder.finish() 772 | } 773 | } 774 | 775 | impl fmt::Debug for XXPacket2 { 776 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 777 | let mut builder = f.debug_tuple("XXPacket2"); 778 | for byte in self.as_ref().iter() { 779 | builder.field(byte); 780 | } 781 | builder.finish() 782 | } 783 | } 784 | 785 | impl fmt::Debug for XXPacket3 { 786 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 787 | let mut builder = f.debug_tuple("XXPacket3"); 788 | for byte in self.as_ref().iter() { 789 | builder.field(byte); 790 | } 791 | builder.finish() 792 | } 793 | } 794 | 795 | impl From> for KeyPair { 796 | fn from(keypair_c: MaybeUninit) -> KeyPair { 797 | unsafe { 798 | let mut keypair_c = keypair_c.assume_init(); 799 | let mut keypair = MaybeUninit::::uninit(); 800 | (*keypair.as_mut_ptr()) 801 | .public_key 802 | .0 803 | .copy_from_slice(&keypair_c.pk); 804 | (*keypair.as_mut_ptr()) 805 | .secret_key 806 | .0 807 | .copy_from_slice(&keypair_c.sk); 808 | ffi::hydro_memzero(&mut keypair_c as *mut _ as *mut _, size_of_val(&keypair_c)); 809 | keypair.assume_init() 810 | } 811 | } 812 | } 813 | 814 | impl From> for SessionKeyPair { 815 | fn from(session_keypair_c: MaybeUninit) -> SessionKeyPair { 816 | unsafe { 817 | let mut session_keypair_c = session_keypair_c.assume_init(); 818 | let mut session_keypair = MaybeUninit::::uninit(); 819 | (*session_keypair.as_mut_ptr()) 820 | .tx 821 | .0 822 | .copy_from_slice(&session_keypair_c.tx); 823 | (*session_keypair.as_mut_ptr()) 824 | .rx 825 | .0 826 | .copy_from_slice(&session_keypair_c.rx); 827 | ffi::hydro_memzero( 828 | &mut session_keypair_c as *mut _ as *mut _, 829 | size_of_val(&session_keypair_c), 830 | ); 831 | session_keypair.assume_init() 832 | } 833 | } 834 | } 835 | 836 | impl Seed { 837 | pub fn gen() -> Seed { 838 | let mut seed_inner = [0u8; SEEDBYTES]; 839 | random::buf_into(&mut seed_inner); 840 | Seed(seed_inner) 841 | } 842 | } 843 | 844 | impl State { 845 | pub fn new() -> State { 846 | unsafe { MaybeUninit::::zeroed().assume_init() } 847 | } 848 | } 849 | 850 | impl Default for State { 851 | fn default() -> Self { 852 | Self::new() 853 | } 854 | } 855 | 856 | impl NPacket1 { 857 | pub fn new() -> NPacket1 { 858 | NPacket1::from([0u8; N_PACKET1BYTES]) 859 | } 860 | } 861 | 862 | impl Default for NPacket1 { 863 | fn default() -> Self { 864 | Self::new() 865 | } 866 | } 867 | 868 | impl KKPacket1 { 869 | pub fn new() -> KKPacket1 { 870 | KKPacket1::from([0u8; KK_PACKET1BYTES]) 871 | } 872 | } 873 | 874 | impl Default for KKPacket1 { 875 | fn default() -> Self { 876 | Self::new() 877 | } 878 | } 879 | 880 | impl KKPacket2 { 881 | pub fn new() -> KKPacket2 { 882 | KKPacket2::from([0u8; KK_PACKET2BYTES]) 883 | } 884 | } 885 | 886 | impl Default for KKPacket2 { 887 | fn default() -> Self { 888 | Self::new() 889 | } 890 | } 891 | 892 | impl NKPacket1 { 893 | pub fn new() -> NKPacket1 { 894 | NKPacket1::from([0u8; NK_PACKET1BYTES]) 895 | } 896 | } 897 | 898 | impl Default for NKPacket1 { 899 | fn default() -> Self { 900 | Self::new() 901 | } 902 | } 903 | 904 | impl NKPacket2 { 905 | pub fn new() -> NKPacket2 { 906 | NKPacket2::from([0u8; NK_PACKET2BYTES]) 907 | } 908 | } 909 | 910 | impl Default for NKPacket2 { 911 | fn default() -> Self { 912 | Self::new() 913 | } 914 | } 915 | 916 | impl XXPacket1 { 917 | pub fn new() -> XXPacket1 { 918 | XXPacket1::from([0u8; XX_PACKET1BYTES]) 919 | } 920 | } 921 | 922 | impl Default for XXPacket1 { 923 | fn default() -> Self { 924 | Self::new() 925 | } 926 | } 927 | 928 | impl XXPacket2 { 929 | pub fn new() -> XXPacket2 { 930 | XXPacket2::from([0u8; XX_PACKET2BYTES]) 931 | } 932 | } 933 | 934 | impl Default for XXPacket2 { 935 | fn default() -> Self { 936 | Self::new() 937 | } 938 | } 939 | 940 | impl XXPacket3 { 941 | pub fn new() -> XXPacket3 { 942 | XXPacket3::from([0u8; XX_PACKET3BYTES]) 943 | } 944 | } 945 | 946 | impl Default for XXPacket3 { 947 | fn default() -> Self { 948 | Self::new() 949 | } 950 | } 951 | 952 | impl KeyPair { 953 | pub fn gen() -> KeyPair { 954 | ensure_initialized(); 955 | unsafe { 956 | let mut keypair_c = MaybeUninit::::uninit(); 957 | ffi::hydro_kx_keygen(keypair_c.as_mut_ptr()); 958 | KeyPair::from(keypair_c) 959 | } 960 | } 961 | 962 | pub fn gen_deterministic(seed: &Seed) -> KeyPair { 963 | ensure_initialized(); 964 | unsafe { 965 | let mut keypair_c = MaybeUninit::::uninit(); 966 | ffi::hydro_kx_keygen_deterministic(keypair_c.as_mut_ptr(), seed.0.as_ptr()); 967 | KeyPair::from(keypair_c) 968 | } 969 | } 970 | } 971 | 972 | #[rustfmt::skip::macros(assert)] 973 | #[cfg(test)] 974 | mod tests { 975 | use core::convert::TryFrom; 976 | 977 | use crate::*; 978 | 979 | #[test] 980 | fn test_kx_deterministic_keygen() { 981 | init().unwrap(); 982 | 983 | let seed = kx::Seed::gen(); 984 | 985 | let a = kx::KeyPair::gen_deterministic(&seed); 986 | let b = kx::KeyPair::gen_deterministic(&seed); 987 | 988 | assert!(utils::equal(a.public_key, b.public_key)); 989 | assert!(utils::equal(a.secret_key, b.secret_key)); 990 | } 991 | 992 | #[test] 993 | fn test_kx_n() { 994 | init().unwrap(); 995 | 996 | let server_static_keypair = kx::KeyPair::gen(); 997 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 998 | let mut npacket1 = kx::NPacket1::new(); 999 | 1000 | let client_session_keypair = 1001 | kx::n_1(&mut npacket1, Some(&psk), &server_static_keypair.public_key).unwrap(); 1002 | 1003 | let server_session_keypair = 1004 | kx::n_2(&npacket1, Some(&psk), &server_static_keypair).unwrap(); 1005 | 1006 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1007 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1008 | } 1009 | 1010 | #[test] 1011 | fn test_kx_n_without_psk() { 1012 | init().unwrap(); 1013 | 1014 | let server_static_keypair = kx::KeyPair::gen(); 1015 | let mut npacket1 = kx::NPacket1::new(); 1016 | 1017 | let client_session_keypair = 1018 | kx::n_1(&mut npacket1, None, &server_static_keypair.public_key).unwrap(); 1019 | 1020 | let server_session_keypair = kx::n_2(&npacket1, None, &server_static_keypair).unwrap(); 1021 | 1022 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1023 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1024 | } 1025 | 1026 | #[test] 1027 | fn test_kx_n_fails_with_bogus_psk() { 1028 | init().unwrap(); 1029 | 1030 | let server_static_keypair = kx::KeyPair::gen(); 1031 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1032 | let bogus_psk = kx::Psk::try_from("ffffffffffffffffffffffffffffffff").unwrap(); 1033 | let mut npacket1 = kx::NPacket1::new(); 1034 | 1035 | let _client_session_keypair = 1036 | kx::n_1(&mut npacket1, Some(&psk), &server_static_keypair.public_key).unwrap(); 1037 | 1038 | let result = kx::n_2(&npacket1, Some(&bogus_psk), &server_static_keypair); 1039 | 1040 | assert!(result.is_err()); 1041 | } 1042 | 1043 | #[test] 1044 | fn test_kx_n_fails_with_bogus_pk() { 1045 | init().unwrap(); 1046 | 1047 | let server_static_keypair = kx::KeyPair::gen(); 1048 | let bogus_keypair = kx::KeyPair::gen(); 1049 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1050 | let mut npacket1 = kx::NPacket1::new(); 1051 | 1052 | let _client_session_keypair = 1053 | kx::n_1(&mut npacket1, Some(&psk), &bogus_keypair.public_key).unwrap(); 1054 | 1055 | let result = kx::n_2(&npacket1, Some(&psk), &server_static_keypair); 1056 | 1057 | assert!(result.is_err()); 1058 | } 1059 | 1060 | #[test] 1061 | fn test_kx_kk() { 1062 | init().unwrap(); 1063 | 1064 | let server_static_keypair = kx::KeyPair::gen(); 1065 | let client_static_keypair = kx::KeyPair::gen(); 1066 | let mut kkpacket1 = kx::KKPacket1::new(); 1067 | let mut kkpacket2 = kx::KKPacket2::new(); 1068 | let mut client_state = kx::State::new(); 1069 | 1070 | kx::kk_1( 1071 | &mut client_state, 1072 | &mut kkpacket1, 1073 | &server_static_keypair.public_key, 1074 | &client_static_keypair, 1075 | ) 1076 | .unwrap(); 1077 | 1078 | let server_session_keypair = kx::kk_2( 1079 | &mut kkpacket2, 1080 | &kkpacket1, 1081 | &client_static_keypair.public_key, 1082 | &server_static_keypair, 1083 | ) 1084 | .unwrap(); 1085 | 1086 | let client_session_keypair = 1087 | kx::kk_3(&mut client_state, &kkpacket2, &client_static_keypair).unwrap(); 1088 | 1089 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1090 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1091 | } 1092 | 1093 | #[test] 1094 | fn test_kx_kk_fails_with_bogus_pk() { 1095 | init().unwrap(); 1096 | 1097 | let server_static_keypair = kx::KeyPair::gen(); 1098 | let client_static_keypair = kx::KeyPair::gen(); 1099 | let bogus_keypair = kx::KeyPair::gen(); 1100 | let mut kkpacket1 = kx::KKPacket1::new(); 1101 | let mut kkpacket2 = kx::KKPacket2::new(); 1102 | let mut client_state = kx::State::new(); 1103 | 1104 | kx::kk_1( 1105 | &mut client_state, 1106 | &mut kkpacket1, 1107 | &bogus_keypair.public_key, 1108 | &client_static_keypair, 1109 | ) 1110 | .unwrap(); 1111 | 1112 | let result = kx::kk_2( 1113 | &mut kkpacket2, 1114 | &kkpacket1, 1115 | &client_static_keypair.public_key, 1116 | &server_static_keypair, 1117 | ); 1118 | 1119 | assert!(result.is_err()); 1120 | } 1121 | 1122 | #[test] 1123 | fn test_kx_nk() { 1124 | init().unwrap(); 1125 | 1126 | let server_static_keypair = kx::KeyPair::gen(); 1127 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1128 | let mut nkpacket1 = kx::NKPacket1::new(); 1129 | let mut nkpacket2 = kx::NKPacket2::new(); 1130 | let mut client_state = kx::State::new(); 1131 | 1132 | kx::nk_1( 1133 | &mut client_state, 1134 | &mut nkpacket1, 1135 | Some(&psk), 1136 | &server_static_keypair.public_key, 1137 | ) 1138 | .unwrap(); 1139 | 1140 | let server_session_keypair = kx::nk_2( 1141 | &mut nkpacket2, 1142 | &nkpacket1, 1143 | Some(&psk), 1144 | &server_static_keypair, 1145 | ) 1146 | .unwrap(); 1147 | 1148 | let client_session_keypair = kx::nk_3(&mut client_state, &nkpacket2).unwrap(); 1149 | 1150 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1151 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1152 | } 1153 | 1154 | #[test] 1155 | fn test_kx_nk_without_psk() { 1156 | init().unwrap(); 1157 | 1158 | let server_static_keypair = kx::KeyPair::gen(); 1159 | let mut nkpacket1 = kx::NKPacket1::new(); 1160 | let mut nkpacket2 = kx::NKPacket2::new(); 1161 | let mut client_state = kx::State::new(); 1162 | 1163 | kx::nk_1( 1164 | &mut client_state, 1165 | &mut nkpacket1, 1166 | None, 1167 | &server_static_keypair.public_key, 1168 | ) 1169 | .unwrap(); 1170 | 1171 | let server_session_keypair = 1172 | kx::nk_2(&mut nkpacket2, &nkpacket1, None, &server_static_keypair).unwrap(); 1173 | 1174 | let client_session_keypair = kx::nk_3(&mut client_state, &nkpacket2).unwrap(); 1175 | 1176 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1177 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1178 | } 1179 | 1180 | #[test] 1181 | fn test_kx_nk_fails_with_bogus_psk() { 1182 | init().unwrap(); 1183 | 1184 | let server_static_keypair = kx::KeyPair::gen(); 1185 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1186 | let bogus_psk = kx::Psk::try_from("ffffffffffffffffffffffffffffffff").unwrap(); 1187 | let mut nkpacket1 = kx::NKPacket1::new(); 1188 | let mut nkpacket2 = kx::NKPacket2::new(); 1189 | let mut client_state = kx::State::new(); 1190 | 1191 | kx::nk_1( 1192 | &mut client_state, 1193 | &mut nkpacket1, 1194 | Some(&bogus_psk), 1195 | &server_static_keypair.public_key, 1196 | ) 1197 | .unwrap(); 1198 | 1199 | let _server_session_keypair = kx::nk_2( 1200 | &mut nkpacket2, 1201 | &nkpacket1, 1202 | Some(&psk), 1203 | &server_static_keypair, 1204 | ); 1205 | 1206 | let result = kx::nk_3(&mut client_state, &nkpacket2); 1207 | 1208 | assert!(result.is_err()); 1209 | } 1210 | 1211 | #[test] 1212 | fn test_kx_nk_fails_with_bogus_pk() { 1213 | init().unwrap(); 1214 | 1215 | let server_static_keypair = kx::KeyPair::gen(); 1216 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1217 | let bogus_keypair = kx::KeyPair::gen(); 1218 | let mut nkpacket1 = kx::NKPacket1::new(); 1219 | let mut nkpacket2 = kx::NKPacket2::new(); 1220 | let mut client_state = kx::State::new(); 1221 | 1222 | kx::nk_1( 1223 | &mut client_state, 1224 | &mut nkpacket1, 1225 | Some(&psk), 1226 | &bogus_keypair.public_key, 1227 | ) 1228 | .unwrap(); 1229 | 1230 | let result = kx::nk_2( 1231 | &mut nkpacket2, 1232 | &nkpacket1, 1233 | Some(&psk), 1234 | &server_static_keypair, 1235 | ); 1236 | 1237 | assert!(result.is_err()); 1238 | } 1239 | 1240 | #[test] 1241 | fn test_kx_xx() { 1242 | init().unwrap(); 1243 | 1244 | let server_static_keypair = kx::KeyPair::gen(); 1245 | let client_static_keypair = kx::KeyPair::gen(); 1246 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1247 | let mut client_peer_public_key = kx::PublicKey::from([0u8; kx::PUBLICKEYBYTES]); 1248 | let mut server_peer_public_key = kx::PublicKey::from([0u8; kx::PUBLICKEYBYTES]); 1249 | let mut xxpacket1 = kx::XXPacket1::new(); 1250 | let mut xxpacket2 = kx::XXPacket2::new(); 1251 | let mut xxpacket3 = kx::XXPacket3::new(); 1252 | let mut client_state = kx::State::new(); 1253 | let mut server_state = kx::State::new(); 1254 | 1255 | kx::xx_1(&mut client_state, &mut xxpacket1, Some(&psk)).unwrap(); 1256 | 1257 | kx::xx_2( 1258 | &mut server_state, 1259 | &mut xxpacket2, 1260 | &xxpacket1, 1261 | Some(&psk), 1262 | &server_static_keypair, 1263 | ) 1264 | .unwrap(); 1265 | 1266 | let client_session_keypair = kx::xx_3( 1267 | &mut client_state, 1268 | &mut xxpacket3, 1269 | Some(&mut client_peer_public_key), 1270 | &xxpacket2, 1271 | Some(&psk), 1272 | &client_static_keypair, 1273 | ) 1274 | .unwrap(); 1275 | 1276 | let server_session_keypair = kx::xx_4( 1277 | &mut server_state, 1278 | Some(&mut server_peer_public_key), 1279 | &xxpacket3, 1280 | Some(&psk), 1281 | ) 1282 | .unwrap(); 1283 | 1284 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1285 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1286 | assert!(utils::equal(client_peer_public_key, server_static_keypair.public_key)); 1287 | assert!(utils::equal(server_peer_public_key, client_static_keypair.public_key)); 1288 | } 1289 | 1290 | #[test] 1291 | fn test_kx_xx_without_peer_pk() { 1292 | init().unwrap(); 1293 | 1294 | let server_static_keypair = kx::KeyPair::gen(); 1295 | let client_static_keypair = kx::KeyPair::gen(); 1296 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1297 | let mut xxpacket1 = kx::XXPacket1::new(); 1298 | let mut xxpacket2 = kx::XXPacket2::new(); 1299 | let mut xxpacket3 = kx::XXPacket3::new(); 1300 | let mut client_state = kx::State::new(); 1301 | let mut server_state = kx::State::new(); 1302 | 1303 | kx::xx_1(&mut client_state, &mut xxpacket1, Some(&psk)).unwrap(); 1304 | 1305 | kx::xx_2( 1306 | &mut server_state, 1307 | &mut xxpacket2, 1308 | &xxpacket1, 1309 | Some(&psk), 1310 | &server_static_keypair, 1311 | ) 1312 | .unwrap(); 1313 | 1314 | let client_session_keypair = kx::xx_3( 1315 | &mut client_state, 1316 | &mut xxpacket3, 1317 | None, 1318 | &xxpacket2, 1319 | Some(&psk), 1320 | &client_static_keypair, 1321 | ) 1322 | .unwrap(); 1323 | 1324 | let server_session_keypair = 1325 | kx::xx_4(&mut server_state, None, &xxpacket3, Some(&psk)).unwrap(); 1326 | 1327 | assert!(utils::equal(client_session_keypair.tx, server_session_keypair.rx)); 1328 | assert!(utils::equal(client_session_keypair.rx, server_session_keypair.tx)); 1329 | } 1330 | 1331 | #[test] 1332 | fn test_kx_xx_fails_with_bogus_psk() { 1333 | init().unwrap(); 1334 | 1335 | let server_static_keypair = kx::KeyPair::gen(); 1336 | let client_static_keypair = kx::KeyPair::gen(); 1337 | let psk = kx::Psk::try_from("deadbeefdeadbeefdeadbeefdeadbeef").unwrap(); 1338 | let bogus_psk = kx::Psk::try_from("ffffffffffffffffffffffffffffffff").unwrap(); 1339 | let mut xxpacket1 = kx::XXPacket1::new(); 1340 | let mut xxpacket2 = kx::XXPacket2::new(); 1341 | let mut client_state = kx::State::new(); 1342 | let mut server_state = kx::State::new(); 1343 | 1344 | kx::xx_1(&mut client_state, &mut xxpacket1, Some(&psk)).unwrap(); 1345 | 1346 | let result = kx::xx_2( 1347 | &mut server_state, 1348 | &mut xxpacket2, 1349 | &xxpacket1, 1350 | Some(&bogus_psk), 1351 | &server_static_keypair, 1352 | ); 1353 | 1354 | assert!(result.is_err()); 1355 | 1356 | let mut client_peer_public_key = kx::PublicKey::from([0u8; kx::PUBLICKEYBYTES]); 1357 | let mut server_peer_public_key = kx::PublicKey::from([0u8; kx::PUBLICKEYBYTES]); 1358 | let mut xxpacket1 = kx::XXPacket1::new(); 1359 | let mut xxpacket2 = kx::XXPacket2::new(); 1360 | let mut xxpacket3 = kx::XXPacket3::new(); 1361 | let mut client_state = kx::State::new(); 1362 | let mut server_state = kx::State::new(); 1363 | 1364 | kx::xx_1(&mut client_state, &mut xxpacket1, Some(&psk)).unwrap(); 1365 | 1366 | kx::xx_2( 1367 | &mut server_state, 1368 | &mut xxpacket2, 1369 | &xxpacket1, 1370 | Some(&psk), 1371 | &server_static_keypair, 1372 | ) 1373 | .unwrap(); 1374 | 1375 | let _client_session_keypair = kx::xx_3( 1376 | &mut client_state, 1377 | &mut xxpacket3, 1378 | Some(&mut client_peer_public_key), 1379 | &xxpacket2, 1380 | Some(&psk), 1381 | &client_static_keypair, 1382 | ) 1383 | .unwrap(); 1384 | 1385 | let result = kx::xx_4( 1386 | &mut server_state, 1387 | Some(&mut server_peer_public_key), 1388 | &xxpacket3, 1389 | Some(&bogus_psk), 1390 | ); 1391 | 1392 | assert!(result.is_err()); 1393 | } 1394 | } 1395 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::trivially_copy_pass_by_ref)] 2 | use libhydrogen_sys as ffi; 3 | 4 | pub mod errors; 5 | pub mod hash; 6 | pub mod kdf; 7 | pub mod kx; 8 | pub mod random; 9 | pub mod secretbox; 10 | pub mod sign; 11 | pub mod utils; 12 | pub mod version; 13 | 14 | use std::sync::{ 15 | atomic::{AtomicBool, Ordering}, 16 | Once, 17 | }; 18 | 19 | use crate::errors::*; 20 | 21 | static INIT: Once = Once::new(); 22 | static INITIALIZED: AtomicBool = AtomicBool::new(false); 23 | 24 | pub fn init() -> Result<(), HydroError> { 25 | INIT.call_once(|| { 26 | if unsafe { ffi::hydro_init() } >= 0 { 27 | INITIALIZED.store(true, Ordering::Release); 28 | } 29 | }); 30 | if INITIALIZED.load(Ordering::Acquire) { 31 | Ok(()) 32 | } else { 33 | Err(HydroError::InitError) 34 | } 35 | } 36 | 37 | pub fn ensure_initialized() { 38 | assert!( 39 | INITIALIZED.load(Ordering::Acquire), 40 | "Hydrogen library not initialized" 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/random.rs: -------------------------------------------------------------------------------- 1 | use super::ensure_initialized; 2 | use crate::ffi; 3 | use crate::utils; 4 | 5 | pub const SEEDBYTES: usize = ffi::hydro_random_SEEDBYTES as usize; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Seed([u8; SEEDBYTES]); 9 | 10 | #[inline] 11 | pub fn u32() -> u32 { 12 | ensure_initialized(); 13 | unsafe { ffi::hydro_random_u32() } 14 | } 15 | 16 | #[inline] 17 | pub fn uniform(upper_bound: u32) -> u32 { 18 | ensure_initialized(); 19 | unsafe { ffi::hydro_random_uniform(upper_bound) } 20 | } 21 | 22 | #[inline] 23 | pub fn buf_into(out: &mut [u8]) { 24 | ensure_initialized(); 25 | unsafe { 26 | ffi::hydro_random_buf(out.as_mut_ptr() as *mut _, out.len()); 27 | } 28 | } 29 | 30 | pub fn buf(out_len: usize) -> Vec { 31 | let mut out = vec![0u8; out_len]; 32 | buf_into(&mut out); 33 | out 34 | } 35 | 36 | #[inline] 37 | pub fn buf_deterministic_into(out: &mut [u8], seed: &Seed) { 38 | ensure_initialized(); 39 | unsafe { 40 | ffi::hydro_random_buf_deterministic(out.as_mut_ptr() as *mut _, out.len(), seed.0.as_ptr()) 41 | } 42 | } 43 | 44 | #[inline] 45 | pub fn buf_deterministic(out_len: usize, seed: &Seed) -> Vec { 46 | let mut out = vec![0u8; out_len]; 47 | buf_deterministic_into(&mut out, seed); 48 | out 49 | } 50 | 51 | #[inline] 52 | pub fn ratchet() { 53 | ensure_initialized(); 54 | unsafe { 55 | ffi::hydro_random_ratchet(); 56 | } 57 | } 58 | 59 | #[inline] 60 | pub fn reseed() { 61 | ensure_initialized(); 62 | unsafe { 63 | ffi::hydro_random_reseed(); 64 | } 65 | } 66 | 67 | impl Drop for Seed { 68 | fn drop(&mut self) { 69 | utils::memzero(self) 70 | } 71 | } 72 | 73 | impl From<[u8; SEEDBYTES]> for Seed { 74 | #[inline] 75 | fn from(seed: [u8; SEEDBYTES]) -> Seed { 76 | Seed(seed) 77 | } 78 | } 79 | 80 | impl From for [u8; SEEDBYTES] { 81 | #[inline] 82 | fn from(val: Seed) -> Self { 83 | val.0 84 | } 85 | } 86 | 87 | impl AsRef<[u8]> for Seed { 88 | fn as_ref(&self) -> &[u8] { 89 | &self.0 as &[u8] 90 | } 91 | } 92 | 93 | impl PartialEq for Seed { 94 | fn eq(&self, other: &Self) -> bool { 95 | utils::equal(self, other) 96 | } 97 | } 98 | 99 | impl Eq for Seed {} 100 | 101 | impl Seed { 102 | pub fn gen() -> Seed { 103 | let mut seed_inner = [0u8; SEEDBYTES]; 104 | buf_into(&mut seed_inner); 105 | Seed(seed_inner) 106 | } 107 | } 108 | 109 | #[cfg(test)] 110 | mod tests { 111 | use crate::*; 112 | 113 | #[test] 114 | fn test_randombytes() { 115 | init().unwrap(); 116 | assert_ne!(random::u32() | random::u32() | random::u32(), 0); 117 | 118 | for _ in 0..100 { 119 | let max = random::u32(); 120 | assert!(random::uniform(max) < max) 121 | } 122 | 123 | let len = random::uniform(100) as usize + 1; 124 | let mut buf = random::buf(len); 125 | random::buf_into(&mut buf); 126 | 127 | let seed = random::Seed::gen(); 128 | let buf = random::buf_deterministic(len, &seed); 129 | let mut buf2 = vec![0u8; len]; 130 | random::buf_deterministic_into(&mut buf2, &seed); 131 | assert_eq!(buf, buf2); 132 | 133 | let seedx: [u8; random::SEEDBYTES] = seed.clone().into(); 134 | let seedy: random::Seed = seedx.into(); 135 | assert_eq!(seed, seedy); 136 | 137 | random::ratchet(); 138 | 139 | random::reseed(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/secretbox.rs: -------------------------------------------------------------------------------- 1 | //! Secret-key authenticated encryption 2 | //! 3 | //! A single key is used both to encrypt/sign and verify/decrypt messages. For 4 | //! this reason, it is critical to keep the key confidential. 5 | //! 6 | //! # Examples 7 | //! ``` 8 | //! let key_data = [ 9 | //! 64, 33, 195, 234, 107, 63, 107, 237, 113, 199, 183, 130, 203, 194, 247, 31, 76, 51, 203, 10 | //! 163, 126, 238, 206, 125, 225, 74, 103, 105, 133, 181, 61, 189, 11 | //! ]; 12 | //! 13 | //! let key = libhydrogen::secretbox::Key::from(key_data); 14 | //! let context = libhydrogen::secretbox::Context::default(); 15 | //! let ciphertext = libhydrogen::secretbox::encrypt(b"hello world", 1, &context, &key); 16 | //! 17 | //! let decrypted = libhydrogen::secretbox::decrypt(&ciphertext, 1, &context, &key).unwrap(); 18 | //! 19 | //! println!("{}", String::from_utf8(decrypted).unwrap()); 20 | //! ``` 21 | 22 | use core::mem::MaybeUninit; 23 | 24 | use super::ensure_initialized; 25 | use crate::errors::*; 26 | use crate::ffi; 27 | use crate::utils; 28 | 29 | pub const CONTEXTBYTES: usize = ffi::hydro_secretbox_CONTEXTBYTES as usize; 30 | pub const HEADERBYTES: usize = ffi::hydro_secretbox_HEADERBYTES as usize; 31 | pub const KEYBYTES: usize = ffi::hydro_secretbox_KEYBYTES as usize; 32 | pub const PROBEBYTES: usize = ffi::hydro_secretbox_PROBEBYTES as usize; 33 | 34 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 35 | pub struct Context([u8; CONTEXTBYTES]); 36 | 37 | #[derive(Debug, Clone)] 38 | pub struct Key([u8; KEYBYTES]); 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct Probe([u8; PROBEBYTES]); 42 | 43 | pub fn encrypt(input: &[u8], msg_id: u64, context: &Context, key: &Key) -> Vec { 44 | let out_len = HEADERBYTES + input.len(); 45 | let mut out = Vec::with_capacity(out_len); 46 | unsafe { 47 | out.set_len(out_len); 48 | ffi::hydro_secretbox_encrypt( 49 | out.as_mut_ptr(), 50 | input.as_ptr() as *const _, 51 | input.len(), 52 | msg_id, 53 | context.0.as_ptr() as *const _, 54 | key.0.as_ptr(), 55 | ); 56 | } 57 | out 58 | } 59 | 60 | pub fn decrypt( 61 | input: &[u8], 62 | msg_id: u64, 63 | context: &Context, 64 | key: &Key, 65 | ) -> Result, HydroError> { 66 | if input.len() < HEADERBYTES { 67 | return Err(HydroError::DecryptionError); 68 | } 69 | let out_len = input.len() - HEADERBYTES; 70 | let mut out: Vec = Vec::with_capacity(out_len); 71 | unsafe { 72 | out.set_len(out_len); 73 | if ffi::hydro_secretbox_decrypt( 74 | out.as_mut_ptr() as *mut _, 75 | input.as_ptr(), 76 | input.len(), 77 | msg_id, 78 | context.0.as_ptr() as *const _, 79 | key.0.as_ptr(), 80 | ) != 0 81 | { 82 | return Err(HydroError::DecryptionError); 83 | } 84 | } 85 | Ok(out) 86 | } 87 | 88 | impl Probe { 89 | pub fn create(input: &[u8], context: &Context, key: &Key) -> Probe { 90 | if input.len() < HEADERBYTES { 91 | panic!("A probe cannot be created for an impossible ciphertext") 92 | } 93 | unsafe { 94 | let mut probe = MaybeUninit::::uninit(); 95 | ffi::hydro_secretbox_probe_create( 96 | (*probe.as_mut_ptr()).0.as_mut_ptr(), 97 | input.as_ptr(), 98 | input.len(), 99 | context.0.as_ptr() as *const _, 100 | key.0.as_ptr(), 101 | ); 102 | probe.assume_init() 103 | } 104 | } 105 | 106 | pub fn verify(&self, input: &[u8], context: &Context, key: &Key) -> Result<(), HydroError> { 107 | if unsafe { 108 | ffi::hydro_secretbox_probe_verify( 109 | self.0.as_ptr(), 110 | input.as_ptr(), 111 | input.len(), 112 | context.0.as_ptr() as *const _, 113 | key.0.as_ptr(), 114 | ) 115 | } != 0 116 | { 117 | return Err(HydroError::InvalidProbe); 118 | } 119 | Ok(()) 120 | } 121 | } 122 | 123 | impl Drop for Key { 124 | fn drop(&mut self) { 125 | utils::memzero(self) 126 | } 127 | } 128 | 129 | impl From<[u8; KEYBYTES]> for Key { 130 | #[inline] 131 | fn from(key: [u8; KEYBYTES]) -> Key { 132 | Key(key) 133 | } 134 | } 135 | 136 | impl From for [u8; KEYBYTES] { 137 | #[inline] 138 | fn from(val: Key) -> Self { 139 | val.0 140 | } 141 | } 142 | 143 | impl AsRef<[u8]> for Key { 144 | fn as_ref(&self) -> &[u8] { 145 | &self.0 as &[u8] 146 | } 147 | } 148 | 149 | impl PartialEq for Key { 150 | fn eq(&self, other: &Self) -> bool { 151 | utils::equal(self, other) 152 | } 153 | } 154 | 155 | impl Eq for Key {} 156 | 157 | impl Key { 158 | pub fn gen() -> Key { 159 | ensure_initialized(); 160 | unsafe { 161 | let mut key = MaybeUninit::::uninit(); 162 | ffi::hydro_kdf_keygen((*key.as_mut_ptr()).0.as_mut_ptr()); 163 | key.assume_init() 164 | } 165 | } 166 | } 167 | 168 | impl Drop for Probe { 169 | fn drop(&mut self) { 170 | utils::memzero(self) 171 | } 172 | } 173 | 174 | impl From<[u8; PROBEBYTES]> for Probe { 175 | #[inline] 176 | fn from(probe: [u8; PROBEBYTES]) -> Probe { 177 | Probe(probe) 178 | } 179 | } 180 | 181 | impl From for [u8; PROBEBYTES] { 182 | #[inline] 183 | fn from(val: Probe) -> Self { 184 | val.0 185 | } 186 | } 187 | 188 | impl AsRef<[u8]> for Probe { 189 | fn as_ref(&self) -> &[u8] { 190 | &self.0 as &[u8] 191 | } 192 | } 193 | 194 | impl PartialEq for Probe { 195 | fn eq(&self, other: &Self) -> bool { 196 | utils::equal(self, other) 197 | } 198 | } 199 | 200 | impl Eq for Probe {} 201 | 202 | impl From<&'static str> for Context { 203 | fn from(context_str: &'static str) -> Context { 204 | let context_str_u8 = context_str.as_bytes(); 205 | let context_str_u8_len = context_str_u8.len(); 206 | if context_str_u8_len > CONTEXTBYTES { 207 | panic!("Context too long"); 208 | } 209 | let mut context = Context::default(); 210 | context.0[..context_str_u8_len].copy_from_slice(context_str_u8); 211 | context 212 | } 213 | } 214 | 215 | impl From<[u8; CONTEXTBYTES]> for Context { 216 | #[inline] 217 | fn from(context: [u8; CONTEXTBYTES]) -> Context { 218 | Context(context) 219 | } 220 | } 221 | 222 | impl From for [u8; CONTEXTBYTES] { 223 | #[inline] 224 | fn from(val: Context) -> Self { 225 | val.0 226 | } 227 | } 228 | 229 | #[cfg(test)] 230 | mod tests { 231 | use crate::*; 232 | 233 | #[test] 234 | fn test_secretbox() { 235 | init().unwrap(); 236 | 237 | let context = "tests".into(); 238 | let key = secretbox::Key::gen(); 239 | 240 | let ciphertext = secretbox::encrypt(b"test message", 1, &context, &key); 241 | let decrypted = secretbox::decrypt(&ciphertext, 1, &context, &key).unwrap(); 242 | assert_eq!(decrypted, b"test message"); 243 | 244 | let probe = secretbox::Probe::create(&ciphertext, &context, &key); 245 | probe.verify(&ciphertext, &context, &key).unwrap(); 246 | 247 | let contextx: [u8; secretbox::CONTEXTBYTES] = context.into(); 248 | let contexty: secretbox::Context = contextx.into(); 249 | assert_eq!(context, contexty); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/sign.rs: -------------------------------------------------------------------------------- 1 | use core::mem::{self, size_of_val, MaybeUninit}; 2 | 3 | use super::ensure_initialized; 4 | use crate::ffi; 5 | use crate::utils; 6 | use crate::{errors::*, random}; 7 | 8 | pub const BYTES: usize = ffi::hydro_sign_BYTES as usize; 9 | pub const CONTEXTBYTES: usize = ffi::hydro_sign_CONTEXTBYTES as usize; 10 | pub const PUBLICKEYBYTES: usize = ffi::hydro_sign_PUBLICKEYBYTES as usize; 11 | pub const SECRETKEYBYTES: usize = ffi::hydro_sign_SECRETKEYBYTES as usize; 12 | pub const SEEDBYTES: usize = ffi::hydro_sign_SEEDBYTES as usize; 13 | 14 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 15 | pub struct Context([u8; CONTEXTBYTES]); 16 | 17 | #[derive(Debug, Copy, PartialEq, Eq, Clone)] 18 | pub struct PublicKey([u8; PUBLICKEYBYTES]); 19 | 20 | #[derive(Clone)] 21 | pub struct SecretKey([u8; SECRETKEYBYTES]); 22 | 23 | #[derive(Clone)] 24 | pub struct KeyPair { 25 | pub public_key: PublicKey, 26 | pub secret_key: SecretKey, 27 | } 28 | 29 | #[derive(Debug, Clone)] 30 | pub struct Seed([u8; SEEDBYTES]); 31 | 32 | #[derive(Debug, Clone)] 33 | pub struct State(ffi::hydro_sign_state); 34 | 35 | #[derive(Copy, Clone)] 36 | pub struct Signature([u8; BYTES]); 37 | 38 | pub struct Sign { 39 | state: State, 40 | } 41 | 42 | impl Drop for Seed { 43 | fn drop(&mut self) { 44 | utils::memzero(self) 45 | } 46 | } 47 | 48 | impl From<[u8; SEEDBYTES]> for Seed { 49 | #[inline] 50 | fn from(seed: [u8; SEEDBYTES]) -> Seed { 51 | Seed(seed) 52 | } 53 | } 54 | 55 | impl From for [u8; SEEDBYTES] { 56 | #[inline] 57 | fn from(val: Seed) -> Self { 58 | val.0 59 | } 60 | } 61 | 62 | impl AsRef<[u8]> for Seed { 63 | fn as_ref(&self) -> &[u8] { 64 | &self.0 as &[u8] 65 | } 66 | } 67 | 68 | impl PartialEq for Seed { 69 | fn eq(&self, other: &Self) -> bool { 70 | utils::equal(self, other) 71 | } 72 | } 73 | 74 | impl Eq for Seed {} 75 | 76 | impl Seed { 77 | pub fn gen() -> Seed { 78 | let mut seed_inner = [0u8; SEEDBYTES]; 79 | random::buf_into(&mut seed_inner); 80 | Seed(seed_inner) 81 | } 82 | } 83 | 84 | impl Sign { 85 | fn new(context: &Context) -> Sign { 86 | unsafe { 87 | let mut state = MaybeUninit::::uninit(); 88 | ffi::hydro_sign_init(&mut (*state.as_mut_ptr()).0, context.0.as_ptr() as *const _); 89 | Sign { 90 | state: state.assume_init(), 91 | } 92 | } 93 | } 94 | 95 | #[inline] 96 | pub fn update(&mut self, input: &[u8]) { 97 | unsafe { 98 | ffi::hydro_sign_update(&mut self.state.0, input.as_ptr() as *const _, input.len()); 99 | } 100 | } 101 | 102 | pub fn finish_create(mut self, secret_key: &SecretKey) -> Result { 103 | unsafe { 104 | let mut signature = MaybeUninit::::uninit(); 105 | if ffi::hydro_sign_final_create( 106 | &mut self.state.0, 107 | (*signature.as_mut_ptr()).0.as_mut_ptr(), 108 | secret_key.0.as_ptr(), 109 | ) != 0 110 | { 111 | return Err(HydroError::InvalidKey); 112 | } 113 | Ok(signature.assume_init()) 114 | } 115 | } 116 | 117 | pub fn finish_verify( 118 | mut self, 119 | signature: &Signature, 120 | public_key: &PublicKey, 121 | ) -> Result<(), HydroError> { 122 | if unsafe { 123 | ffi::hydro_sign_final_verify( 124 | &mut self.state.0, 125 | signature.0.as_ptr(), 126 | public_key.0.as_ptr(), 127 | ) 128 | } != 0 129 | { 130 | return Err(HydroError::InvalidSignature); 131 | } 132 | Ok(()) 133 | } 134 | } 135 | 136 | #[inline] 137 | pub fn init(context: &Context) -> Sign { 138 | Sign::new(context) 139 | } 140 | 141 | pub fn create( 142 | input: &[u8], 143 | context: &Context, 144 | secret_key: &SecretKey, 145 | ) -> Result { 146 | let mut sign = init(context); 147 | sign.update(input); 148 | sign.finish_create(secret_key) 149 | } 150 | 151 | pub fn verify( 152 | signature: &Signature, 153 | input: &[u8], 154 | context: &Context, 155 | public_key: &PublicKey, 156 | ) -> Result<(), HydroError> { 157 | let mut sign = init(context); 158 | sign.update(input); 159 | sign.finish_verify(signature, public_key) 160 | } 161 | 162 | impl Drop for State { 163 | fn drop(&mut self) { 164 | utils::memzero(self) 165 | } 166 | } 167 | 168 | impl Drop for SecretKey { 169 | fn drop(&mut self) { 170 | utils::memzero(self) 171 | } 172 | } 173 | 174 | impl From<[u8; SECRETKEYBYTES]> for SecretKey { 175 | #[inline] 176 | fn from(key: [u8; SECRETKEYBYTES]) -> SecretKey { 177 | SecretKey(key) 178 | } 179 | } 180 | 181 | impl From for [u8; SECRETKEYBYTES] { 182 | #[inline] 183 | fn from(val: SecretKey) -> Self { 184 | val.0 185 | } 186 | } 187 | 188 | impl AsRef<[u8]> for SecretKey { 189 | fn as_ref(&self) -> &[u8] { 190 | &self.0 as &[u8] 191 | } 192 | } 193 | 194 | impl PartialEq for SecretKey { 195 | fn eq(&self, other: &Self) -> bool { 196 | utils::equal(self, other) 197 | } 198 | } 199 | 200 | impl Eq for SecretKey {} 201 | 202 | impl From<[u8; BYTES]> for Signature { 203 | #[inline] 204 | fn from(key: [u8; BYTES]) -> Signature { 205 | Signature(key) 206 | } 207 | } 208 | 209 | impl From for [u8; BYTES] { 210 | #[inline] 211 | fn from(val: Signature) -> Self { 212 | val.0 213 | } 214 | } 215 | 216 | impl AsRef<[u8]> for Signature { 217 | fn as_ref(&self) -> &[u8] { 218 | &self.0 as &[u8] 219 | } 220 | } 221 | 222 | impl PartialEq for Signature { 223 | fn eq(&self, other: &Self) -> bool { 224 | utils::equal(self, other) 225 | } 226 | } 227 | 228 | impl Eq for Signature {} 229 | 230 | impl From<[u8; PUBLICKEYBYTES]> for PublicKey { 231 | #[inline] 232 | fn from(key: [u8; PUBLICKEYBYTES]) -> PublicKey { 233 | PublicKey(key) 234 | } 235 | } 236 | 237 | impl From for [u8; PUBLICKEYBYTES] { 238 | #[inline] 239 | fn from(val: PublicKey) -> Self { 240 | val.0 241 | } 242 | } 243 | 244 | impl AsRef<[u8]> for PublicKey { 245 | fn as_ref(&self) -> &[u8] { 246 | &self.0 as &[u8] 247 | } 248 | } 249 | 250 | impl KeyPair { 251 | pub fn gen() -> KeyPair { 252 | ensure_initialized(); 253 | unsafe { 254 | let mut keypair_c = MaybeUninit::::uninit(); 255 | ffi::hydro_sign_keygen(keypair_c.as_mut_ptr()); 256 | let mut keypair_c = keypair_c.assume_init(); 257 | let mut keypair = MaybeUninit::::uninit(); 258 | (*keypair.as_mut_ptr()) 259 | .public_key 260 | .0 261 | .copy_from_slice(&keypair_c.pk); 262 | (*keypair.as_mut_ptr()) 263 | .secret_key 264 | .0 265 | .copy_from_slice(&keypair_c.sk); 266 | ffi::hydro_memzero( 267 | &mut keypair_c as *mut _ as *mut _, 268 | mem::size_of_val(&keypair_c), 269 | ); 270 | keypair.assume_init() 271 | } 272 | } 273 | 274 | pub fn gen_deterministic(seed: &Seed) -> KeyPair { 275 | ensure_initialized(); 276 | unsafe { 277 | let mut keypair_c = MaybeUninit::::uninit(); 278 | ffi::hydro_sign_keygen_deterministic(keypair_c.as_mut_ptr(), seed.0.as_ptr()); 279 | KeyPair::from(keypair_c) 280 | } 281 | } 282 | } 283 | 284 | impl From<&'static str> for Context { 285 | fn from(context_str: &'static str) -> Context { 286 | let context_str_u8 = context_str.as_bytes(); 287 | let context_str_u8_len = context_str_u8.len(); 288 | if context_str_u8_len > CONTEXTBYTES { 289 | panic!("Context too long"); 290 | } 291 | let mut context = Context::default(); 292 | context.0[..context_str_u8_len].copy_from_slice(context_str_u8); 293 | context 294 | } 295 | } 296 | 297 | impl From<[u8; CONTEXTBYTES]> for Context { 298 | #[inline] 299 | fn from(context: [u8; CONTEXTBYTES]) -> Context { 300 | Context(context) 301 | } 302 | } 303 | 304 | impl From for [u8; CONTEXTBYTES] { 305 | #[inline] 306 | fn from(val: Context) -> Self { 307 | val.0 308 | } 309 | } 310 | 311 | impl From> for KeyPair { 312 | fn from(keypair_c: MaybeUninit) -> KeyPair { 313 | unsafe { 314 | let mut keypair_c = keypair_c.assume_init(); 315 | let mut keypair = MaybeUninit::::uninit(); 316 | (*keypair.as_mut_ptr()) 317 | .public_key 318 | .0 319 | .copy_from_slice(&keypair_c.pk); 320 | (*keypair.as_mut_ptr()) 321 | .secret_key 322 | .0 323 | .copy_from_slice(&keypair_c.sk); 324 | ffi::hydro_memzero(&mut keypair_c as *mut _ as *mut _, size_of_val(&keypair_c)); 325 | keypair.assume_init() 326 | } 327 | } 328 | } 329 | 330 | #[cfg(test)] 331 | mod tests { 332 | use crate::*; 333 | 334 | #[test] 335 | fn test_signature() { 336 | init().unwrap(); 337 | 338 | let context = "tests".into(); 339 | let keypair = sign::KeyPair::gen(); 340 | 341 | let mut s = sign::init(&context); 342 | s.update(b"test message"); 343 | let signature = s.finish_create(&keypair.secret_key).unwrap(); 344 | 345 | let mut s = sign::init(&context); 346 | s.update(b"test message"); 347 | s.finish_verify(&signature, &keypair.public_key).unwrap(); 348 | 349 | let signature = sign::create(b"test message", &context, &keypair.secret_key).unwrap(); 350 | sign::verify(&signature, b"test message", &context, &keypair.public_key).unwrap(); 351 | 352 | let contextx: [u8; sign::CONTEXTBYTES] = context.into(); 353 | let contexty: sign::Context = contextx.into(); 354 | assert_eq!(context, contexty); 355 | 356 | let keypair = sign::KeyPair::gen_deterministic(&sign::Seed::gen()); 357 | let s = sign::init(&context); 358 | let signature = s.finish_create(&keypair.secret_key).unwrap(); 359 | let s = sign::init(&context); 360 | s.finish_verify(&signature, &keypair.public_key).unwrap(); 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use core::{mem, ptr}; 3 | use std::ffi::CString; 4 | 5 | use crate::errors::*; 6 | use crate::ffi; 7 | 8 | #[inline] 9 | pub fn memzero(mut obj: T) 10 | where 11 | T: Sized, 12 | { 13 | unsafe { ffi::hydro_memzero(&mut obj as *mut _ as *mut _, mem::size_of::()) } 14 | } 15 | 16 | #[inline] 17 | pub fn increment(n: &mut [u8]) { 18 | unsafe { 19 | ffi::hydro_increment(n.as_mut_ptr(), n.len()); 20 | } 21 | } 22 | 23 | pub fn equal(b1: T, b2: U) -> bool 24 | where 25 | T: AsRef<[u8]>, 26 | U: AsRef<[u8]>, 27 | { 28 | let b1 = b1.as_ref(); 29 | let b2 = b2.as_ref(); 30 | let len = b1.len(); 31 | if b2.len() != len { 32 | return false; 33 | } 34 | unsafe { ffi::hydro_equal(b1.as_ptr() as *const _, b2.as_ptr() as *const _, len) as bool } 35 | } 36 | 37 | pub fn compare(b1: &[u8], b2: &[u8]) -> Ordering { 38 | let len = b1.len(); 39 | if b2.len() != len { 40 | panic!("Comparison of vectors with different lengths") 41 | } 42 | match unsafe { ffi::hydro_compare(b1.as_ptr(), b2.as_ptr(), len) } { 43 | -1 => Ordering::Less, 44 | 0 => Ordering::Equal, 45 | 1 => Ordering::Greater, 46 | _ => unreachable!(), 47 | } 48 | } 49 | 50 | pub fn bin2hex(bin: T) -> String 51 | where 52 | T: AsRef<[u8]>, 53 | { 54 | let bin = bin.as_ref(); 55 | let len = bin.len(); 56 | let hex_len = len * 2 + 1; 57 | let mut hex = vec![0u8; hex_len]; 58 | unsafe { 59 | ffi::hydro_bin2hex(hex.as_mut_ptr() as *mut _, hex_len, bin.as_ptr(), len); 60 | hex.truncate(hex_len - 1); 61 | String::from_utf8_unchecked(hex) 62 | } 63 | } 64 | 65 | pub fn hex2bin(hex: &str, ignore: Option<&[u8]>) -> Result, HydroError> { 66 | let hex = hex.as_bytes(); 67 | let hex_len = hex.len(); 68 | let max_bin_len = hex_len / 2; 69 | let mut bin = vec![0u8; max_bin_len]; 70 | let ignore_p = match ignore { 71 | Some(ignore) => CString::new(ignore) 72 | .map_err(|_| HydroError::InvalidInput)? 73 | .into_raw(), 74 | None => ptr::null_mut(), // doesn't make sense semantically, but required for `CString::from_raw()`. 75 | }; 76 | let bin_len = unsafe { 77 | ffi::hydro_hex2bin( 78 | bin.as_mut_ptr(), 79 | max_bin_len, 80 | hex.as_ptr() as *const _, 81 | hex_len, 82 | ignore_p, 83 | ptr::null_mut(), 84 | ) 85 | }; 86 | if !ignore_p.is_null() { 87 | unsafe { 88 | // Even though `into_raw()` is not unsafe, Rust will leak the string. 89 | // Manually freeing the string is required, and the documented way 90 | // to do it is by recreating a CString using the pointer, and discarding it. 91 | _ = CString::from_raw(ignore_p); 92 | } 93 | } 94 | if bin_len < 0 { 95 | return Err(HydroError::InvalidInput); 96 | } 97 | bin.truncate(bin_len as usize); 98 | Ok(bin) 99 | } 100 | 101 | pub fn pad(buf: &mut Vec, blocksize: usize) { 102 | buf.reserve(blocksize); 103 | let unpadded_buflen = buf.len(); 104 | let max_padded_buflen = unpadded_buflen + blocksize; 105 | let padded_buflen = unsafe { 106 | buf.set_len(max_padded_buflen); 107 | ffi::hydro_pad( 108 | buf.as_mut_ptr(), 109 | unpadded_buflen, 110 | blocksize, 111 | max_padded_buflen, 112 | ) 113 | }; 114 | if padded_buflen < 0 { 115 | panic!("Padding failed") 116 | } 117 | buf.truncate(padded_buflen as usize); 118 | } 119 | 120 | pub fn unpad(buf: &mut Vec, blocksize: usize) -> Result<(), HydroError> { 121 | let unpadded_buflen = unsafe { ffi::hydro_unpad(buf.as_ptr(), buf.len(), blocksize) }; 122 | if unpadded_buflen < 0 { 123 | return Err(HydroError::InvalidPadding); 124 | } 125 | buf.truncate(unpadded_buflen as usize); 126 | Ok(()) 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | use crate::*; 132 | 133 | #[test] 134 | fn test_utils() { 135 | let bin = [69u8, 42]; 136 | let hex = utils::bin2hex(bin); 137 | assert_eq!(hex, "452a"); 138 | let bin2: Vec = utils::hex2bin(&hex, None).unwrap(); 139 | assert_eq!(bin, &bin2[..]); 140 | let mut bin2: Vec = utils::hex2bin("#452a#", Some(b"#")).unwrap(); 141 | assert_eq!(bin, &bin2[..]); 142 | 143 | utils::pad(&mut bin2, 100); 144 | assert_eq!(bin2.len(), 100); 145 | utils::unpad(&mut bin2, 100).unwrap(); 146 | assert_eq!(bin, &bin2[..]); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | 3 | #[inline] 4 | pub fn major() -> u32 { 5 | ffi::HYDRO_VERSION_MAJOR 6 | } 7 | 8 | #[inline] 9 | pub fn minor() -> u32 { 10 | ffi::HYDRO_VERSION_MINOR 11 | } 12 | 13 | pub fn string() -> String { 14 | format!("{}.{}", major(), minor()) 15 | } 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use crate::*; 20 | 21 | #[test] 22 | fn test_version() { 23 | assert!(version::major() + version::minor() > 0); 24 | assert!(!version::string().is_empty()); 25 | } 26 | } 27 | --------------------------------------------------------------------------------