Ring instead of libsodium

This commit is contained in:
Dennis Schwerdel 2019-01-04 13:53:12 +01:00
parent 81c18fcd83
commit 66d7ba53c4
4 changed files with 64 additions and 146 deletions

31
Cargo.lock generated
View File

@ -6,16 +6,6 @@ dependencies = [
"memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "aligned_alloc"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.4.10" version = "0.4.10"
@ -574,6 +564,17 @@ dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "ring"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.2.3" version = "0.2.3"
@ -938,6 +939,11 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "untrusted"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "utf8-ranges" name = "utf8-ranges"
version = "1.0.2" version = "1.0.2"
@ -957,7 +963,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "vpncloud" name = "vpncloud"
version = "0.8.2" version = "0.8.2"
dependencies = [ dependencies = [
"aligned_alloc 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -969,6 +974,7 @@ dependencies = [
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1052,7 +1058,6 @@ dependencies = [
[metadata] [metadata]
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
"checksum aligned_alloc 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9dcebfb002ccde769c15bc841d0d5548a90e80fcd2ffed5131339e8074746f0a"
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
@ -1118,6 +1123,7 @@ dependencies = [
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
"checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" "checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
@ -1158,6 +1164,7 @@ dependencies = [
"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" "checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

View File

@ -19,7 +19,6 @@ serde_yaml = "0.8"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
signal = "0.6" signal = "0.6"
libc = "0.2" libc = "0.2"
aligned_alloc = "0.1"
rand = "0.6" rand = "0.6"
fnv = "1" fnv = "1"
net2 = "0.2" net2 = "0.2"
@ -28,6 +27,7 @@ yaml-rust = "0.4"
igd = "0.7" igd = "0.7"
siphasher = "0.3" siphasher = "0.3"
daemonize = "0.3" daemonize = "0.3"
ring = "0.13"
[build-dependencies] [build-dependencies]
cc = "^1" cc = "^1"

View File

@ -5,11 +5,11 @@
use std::ptr; use std::ptr;
use std::ffi::CStr; use std::ffi::CStr;
use std::sync::{Once, ONCE_INIT}; use std::sync::{Once, ONCE_INIT};
use ring::aead::*;
static CRYPTO_INIT: Once = ONCE_INIT; static CRYPTO_INIT: Once = ONCE_INIT;
use libc::{size_t, c_char, c_ulonglong, c_int}; use libc::{size_t, c_char, c_ulonglong, c_int};
use aligned_alloc::{aligned_alloc, aligned_free};
use super::types::Error; use super::types::Error;
@ -42,22 +42,6 @@ const crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE: usize = 524_288;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE: usize = 16_777_216; const crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE: usize = 16_777_216;
pub struct Aes256State(*mut [u8; crypto_aead_aes256gcm_STATEBYTES]);
impl Aes256State {
fn new() -> Aes256State {
let ptr = aligned_alloc(crypto_aead_aes256gcm_STATEBYTES, 16)
as *mut [u8; crypto_aead_aes256gcm_STATEBYTES];
Aes256State(ptr)
}
}
impl Drop for Aes256State {
fn drop(&mut self) {
unsafe { aligned_free(self.0 as *mut ()) }
}
}
#[link(name="sodium", kind="static")] #[link(name="sodium", kind="static")]
extern { extern {
@ -127,23 +111,24 @@ pub enum CryptoMethod {
AES256 AES256
} }
pub enum Crypto { pub struct CryptoData {
None, sealing_key: SealingKey,
ChaCha20Poly1305{ opening_key: OpeningKey,
key: [u8; crypto_aead_chacha20poly1305_ietf_KEYBYTES], nonce: Vec<u8>
nonce: [u8; crypto_aead_chacha20poly1305_ietf_NPUBBYTES]
},
AES256GCM{
state: Aes256State,
nonce: [u8; crypto_aead_aes256gcm_NPUBBYTES]
}
} }
fn inc_nonce_12(nonce: &mut [u8; 12]) { pub enum Crypto {
for i in 0..12 { None,
let mut num = nonce[11-i]; ChaCha20Poly1305(CryptoData),
AES256GCM(CryptoData)
}
fn inc_nonce(nonce: &mut [u8]) {
let l = nonce.len();
for i in (0..l).rev() {
let mut num = nonce[i];
num = num.wrapping_add(1); num = num.wrapping_add(1);
nonce[11-i] = num; nonce[i] = num;
if num > 0 { if num > 0 {
break break
} }
@ -170,9 +155,7 @@ impl Crypto {
#[inline] #[inline]
pub fn aes256_available() -> bool { pub fn aes256_available() -> bool {
unsafe { true
crypto_aead_aes256gcm_is_available() == 1
}
} }
#[inline] #[inline]
@ -188,7 +171,7 @@ impl Crypto {
pub fn nonce_bytes(&self) -> usize { pub fn nonce_bytes(&self) -> usize {
match *self { match *self {
Crypto::None => 0, Crypto::None => 0,
Crypto::ChaCha20Poly1305{ref nonce, ..} | Crypto::AES256GCM{ref nonce, ..} => nonce.len(), Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.sealing_key.algorithm().nonce_len()
} }
} }
@ -218,69 +201,30 @@ impl Crypto {
if res != 0 { if res != 0 {
fail!("Key derivation failed"); fail!("Key derivation failed");
} }
let algo = match method {
CryptoMethod::ChaCha20 => &CHACHA20_POLY1305,
CryptoMethod::AES256 => &AES_256_GCM
};
let sealing_key = SealingKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
let opening_key = OpeningKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
let mut nonce: Vec<u8> = Vec::with_capacity(algo.nonce_len());
for _ in 0..algo.nonce_len() {
nonce.push(0);
}
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
let data = CryptoData { sealing_key, opening_key, nonce };
match method { match method {
CryptoMethod::ChaCha20 => { CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
let mut crypto_key = [0; crypto_aead_chacha20poly1305_ietf_KEYBYTES]; CryptoMethod::AES256 => Crypto::AES256GCM(data)
crypto_key.clone_from_slice(&key[..crypto_aead_chacha20poly1305_ietf_KEYBYTES]);
let mut nonce = [0u8; crypto_aead_chacha20poly1305_ietf_NPUBBYTES];
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
Crypto::ChaCha20Poly1305{key: crypto_key, nonce}
},
CryptoMethod::AES256 => {
if ! Crypto::aes256_available() {
fail!("AES256 is not supported by this processor, use ChaCha20 instead");
}
let mut nonce = [0u8; crypto_aead_aes256gcm_NPUBBYTES];
unsafe { randombytes_buf(nonce.as_mut_ptr(), nonce.len()) };
let state = Aes256State::new();
let res = unsafe { crypto_aead_aes256gcm_beforenm(
state.0,
key[..crypto_aead_aes256gcm_KEYBYTES].as_ptr() as *const [u8; crypto_aead_aes256gcm_KEYBYTES]
) };
assert_eq!(res, 0);
Crypto::AES256GCM{state, nonce}
}
} }
} }
pub fn decrypt(&self, buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> { pub fn decrypt(&self, buf: &mut [u8], nonce: &[u8], header: &[u8]) -> Result<usize, Error> {
match *self { match *self {
Crypto::None => Ok(buf.len()), Crypto::None => Ok(buf.len()),
Crypto::ChaCha20Poly1305{ref key, ..} => { Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => {
let mut mlen: u64 = buf.len() as u64; let plaintext = open_in_place(&data.opening_key, nonce, header, 0, buf).expect("error");
let res = unsafe { crypto_aead_chacha20poly1305_ietf_decrypt( Ok(plaintext.len())
buf.as_mut_ptr(), // Base pointer to buffer
&mut mlen, // Mutable size of buffer (will be set to used size)
ptr::null_mut::<[u8; 0]>(), // Mutable base pointer to secret nonce (always NULL)
buf.as_ptr(), // Base pointer to message
buf.len() as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
nonce.as_ptr() as *const [u8; crypto_aead_chacha20poly1305_ietf_NPUBBYTES], // Base pointer to public nonce
key.as_ptr() as *const [u8; crypto_aead_chacha20poly1305_ietf_KEYBYTES] // Base pointer to key
) };
match res {
0 => Ok(mlen as usize),
_ => Err(Error::Crypto("Failed to decrypt"))
}
},
Crypto::AES256GCM{ref state, ..} => {
let mut mlen: u64 = buf.len() as u64;
let res = unsafe { crypto_aead_aes256gcm_decrypt_afternm(
buf.as_mut_ptr(), // Base pointer to buffer
&mut mlen, // Mutable size of buffer (will be set to used size)
ptr::null_mut::<[u8; 0]>(), // Mutable base pointer to secret nonce (always NULL)
buf.as_ptr(), // Base pointer to message
buf.len() as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
nonce.as_ptr() as *const [u8; crypto_aead_aes256gcm_NPUBBYTES], // Base pointer to public nonce
state.0 // Base pointer to state
) };
match res {
0 => Ok(mlen as usize),
_ => Err(Error::Crypto("Failed to decrypt"))
}
} }
} }
} }
@ -288,49 +232,16 @@ impl Crypto {
pub fn encrypt(&mut self, buf: &mut [u8], mlen: usize, nonce_bytes: &mut [u8], header: &[u8]) -> usize { pub fn encrypt(&mut self, buf: &mut [u8], mlen: usize, nonce_bytes: &mut [u8], header: &[u8]) -> usize {
match *self { match *self {
Crypto::None => mlen, Crypto::None => mlen,
Crypto::ChaCha20Poly1305{ref key, ref mut nonce} => { Crypto::ChaCha20Poly1305(ref mut data) | Crypto::AES256GCM(ref mut data) => {
inc_nonce_12(nonce); inc_nonce(&mut data.nonce);
let mut clen: u64 = buf.len() as u64; let tag_len = data.sealing_key.algorithm().tag_len();
assert_eq!(nonce_bytes.len(), nonce.len()); assert!(buf.len() - mlen >= tag_len);
assert!(clen as usize >= mlen + crypto_aead_chacha20poly1305_ietf_ABYTES); let buf = &mut buf[.. mlen + tag_len];
let res = unsafe { crypto_aead_chacha20poly1305_ietf_encrypt( let new_len = seal_in_place(&data.sealing_key, &data.nonce, header, buf, tag_len).expect("error");
buf.as_mut_ptr(), // Base pointer to buffer
&mut clen, // Mutable size of buffer (will be set to used size)
buf.as_ptr(), // Base pointer to message
mlen as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
ptr::null::<[u8; 0]>(), // Base pointer to secret nonce (always NULL)
nonce.as_ptr() as *const [u8; crypto_aead_chacha20poly1305_ietf_NPUBBYTES], // Base pointer to public nonce
key.as_ptr() as *const [u8; crypto_aead_chacha20poly1305_ietf_KEYBYTES] // Base pointer to key
) };
assert_eq!(res, 0);
unsafe { unsafe {
ptr::copy_nonoverlapping(nonce.as_ptr(), nonce_bytes.as_mut_ptr(), nonce.len()); ptr::copy_nonoverlapping(data.nonce.as_ptr(), nonce_bytes.as_mut_ptr(), data.nonce.len());
} }
clen as usize new_len
},
Crypto::AES256GCM{ref state, ref mut nonce} => {
inc_nonce_12(nonce);
let mut clen: u64 = buf.len() as u64;
assert_eq!(nonce_bytes.len(), nonce.len());
assert!(clen as usize >= mlen + crypto_aead_aes256gcm_ABYTES);
let res = unsafe { crypto_aead_aes256gcm_encrypt_afternm(
buf.as_mut_ptr(), // Base pointer to buffer
&mut clen, // Mutable size of buffer (will be set to used size)
buf.as_ptr(), // Base pointer to message
mlen as u64, // Size of message
header.as_ptr(), // Base pointer to additional data
header.len() as u64, // Size of additional data
ptr::null::<[u8; 0]>(), // Base pointer to secret nonce (always NULL)
nonce.as_ptr() as *const [u8; crypto_aead_aes256gcm_NPUBBYTES], // Base pointer to public nonce
state.0 // Base pointer to state
) };
assert_eq!(res, 0);
unsafe {
ptr::copy_nonoverlapping(nonce.as_ptr(), nonce_bytes.as_mut_ptr(), nonce.len());
}
clen as usize
} }
} }
} }

View File

@ -13,7 +13,6 @@ extern crate serde;
extern crate serde_yaml; extern crate serde_yaml;
extern crate signal; extern crate signal;
extern crate libc; extern crate libc;
extern crate aligned_alloc;
extern crate rand; extern crate rand;
extern crate fnv; extern crate fnv;
extern crate net2; extern crate net2;
@ -21,6 +20,7 @@ extern crate yaml_rust;
extern crate igd; extern crate igd;
extern crate siphasher; extern crate siphasher;
extern crate daemonize; extern crate daemonize;
extern crate ring;
#[cfg(feature = "bench")] extern crate test; #[cfg(feature = "bench")] extern crate test;
#[macro_use] pub mod util; #[macro_use] pub mod util;