Compare commits

...

2 commits

Author SHA1 Message Date
02c01ffa2c
Update crates 2023-02-02 00:43:04 +01:00
9db07297ac
Index deserialization and traversal. Tests. 2023-02-02 00:42:55 +01:00
3 changed files with 242 additions and 93 deletions

72
Cargo.lock generated
View file

@ -132,15 +132,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.78"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
@ -241,9 +241,9 @@ dependencies = [
[[package]]
name = "encoding_rs"
version = "0.8.31"
version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if",
]
@ -289,36 +289,36 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
[[package]]
name = "futures-sink"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
[[package]]
name = "futures-task"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
[[package]]
name = "futures-util"
version = "0.3.25"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
dependencies = [
"futures-core",
"futures-task",
@ -514,9 +514,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
version = "0.3.60"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
@ -920,9 +920,9 @@ dependencies = [
[[package]]
name = "security-framework"
version = "2.8.0"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645926f31b250a2dca3c232496c2d898d91036e45ca0e97e0e2390c54e11be36"
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
dependencies = [
"bitflags",
"core-foundation",
@ -1058,9 +1058,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.24.2"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg",
"bytes",
@ -1206,9 +1206,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1216,9 +1216,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
@ -1231,9 +1231,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.33"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
dependencies = [
"cfg-if",
"js-sys",
@ -1243,9 +1243,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1253,9 +1253,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
@ -1266,15 +1266,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.60"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",

View file

@ -4,7 +4,8 @@ use std::{
fs::File,
hash::{Hash, Hasher},
io::{BufReader, Read, Seek, SeekFrom, Write},
path::Path,
iter::TakeWhile,
path::{Iter, Path},
};
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
@ -12,7 +13,7 @@ use argon2::PasswordHasher;
use password_hash::SaltString;
use rand::{rngs::StdRng, RngCore, SeedableRng};
use super::{EncryptionType, HashType, RecordType, Store, StoreRecord};
use super::{EncryptionStream, EncryptionType, HashType, RecordType, Store, StoreRecord};
pub struct LocalStore {
path: String,
@ -158,11 +159,11 @@ fn try_deserialize_superblock(
.decrypt_block_from_file(header.enc_sup_block_size as usize, 0, file)
.unwrap();
if data[0..enc.key.len()].ne(&enc.key) {
if data.data[0..enc.key.len()].ne(&enc.key) {
return Err("Key mismatch.");
}
let meta = deserialize_store_meta(&data[31..]);
let meta = deserialize_store_meta(&&data.data[32..]);
Ok(meta)
}
@ -252,12 +253,15 @@ fn try_create_store(path: &String, passphrase: &Vec<u8>) -> bool {
header.chksum_crc = header.get_checksum();
let mut index_block: Vec<u8> = vec![0; meta.data_block_size as usize];
let mut buf = serialize_header(&header);
buf.extend_from_slice(&salt_buf);
buf.extend_from_slice(&iv);
buf.append([b'\0'].repeat(16 - iv.len()).as_mut());
buf.extend_from_slice(&enc_buf);
buf.extend_from_slice(&enc_buf.data);
buf.extend_from_slice(&enc.encrypt_block(&index_block, 0x90).unwrap().data);
match std::fs::File::create(path).unwrap().write(&buf) {
Ok(x) => return x == buf.len(),
@ -384,7 +388,7 @@ pub fn load(path: String, passphrase: Vec<u8>) -> Result<LocalStore, &'static st
};
let store = LocalStore {
path: path,
path,
header,
enc_ctx,
meta,
@ -485,7 +489,7 @@ impl LocalRecord {
key: key.to_owned(),
r#type: RecordType::from(r#type),
value: value.to_vec(),
meta: meta,
meta,
},
position: 0,
size: val_offset + val_len + meta_len,
@ -517,6 +521,7 @@ impl Store for LocalStore {
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum IndexNodeType {
Internal,
Leaf,
@ -534,12 +539,62 @@ impl From<u8> for IndexNodeType {
}
}
struct IndexNodeEntry {
key: u64,
pointer: u64,
}
struct IndexNode {
node_type: IndexNodeType,
children: [u64],
children: Vec<IndexNodeEntry>,
}
impl IndexNode {
fn children(&self) -> &Vec<IndexNodeEntry> {
&self.children
}
fn len(&self) -> usize {
self.children.len()
}
}
impl LocalStore {
fn read_node(&self, data: &mut EncryptionStream) -> IndexNode {
let node_type = {
let mut buf: [u8; 2] = [0; 2];
data.read_exact(&mut buf).expect("Cannot read node type.");
IndexNodeType::from(buf[0])
};
let mut children = Vec::with_capacity(self.meta.index_node_arity as usize);
for _ in 0..self.meta.index_node_arity {
let mut buf: [u8; 16] = [0; 16];
data.read_exact(&mut buf)
.expect("Cannot read child node content.");
let key = u64::from_be_bytes(buf[0..8].try_into().unwrap());
if key == 0 {
break;
}
let pointer = u64::from_be_bytes(buf[0..8].try_into().unwrap());
let entry = IndexNodeEntry { key, pointer };
children.push(entry);
}
let node = IndexNode {
node_type,
children,
};
node
}
fn btree_find_key(&self, key: &String) -> Option<u64> {
let mut file: BufReader<File> =
BufReader::new(File::open(&self.path).expect("Cannot open file"));
@ -551,34 +606,37 @@ impl LocalStore {
};
let pos = file.seek(SeekFrom::Start(0x90)).unwrap();
let mut block = self
.enc_ctx
.decrypt_block_from_file(self.meta.data_block_size as usize, 0x90, &mut file)
.unwrap();
let mut read_node_type = || {
let mut buf: [u8; 2] = [0; 2];
file.read_exact(&mut buf).expect("Cannot read node type.");
IndexNodeType::from(buf[0])
};
let mut read_key_pos = || {
let mut buf: [u8; 16] = [0; 16];
file.read_exact(&mut buf).expect("Read error");
(
u64::from_be_bytes(buf[0..8].try_into().unwrap()),
u64::from_be_bytes(buf[8..16].try_into().unwrap()),
)
};
let mut subnode_pos = 0;
loop {
let (key, offset) = read_key_pos();
let node = self.read_node(&mut block);
if hash > key {
if subnode_pos > self.meta.index_node_arity {
if node.node_type == IndexNodeType::Invalid {
panic!(
"Invalid node detected: {}",
block.stream_position().unwrap()
);
}
if node.node_type == IndexNodeType::Leaf {
for child in node.children() {
if child.key == hash {
return Some(child.pointer);
}
}
return None;
}
subnode_pos += 1;
for child in node.children {
if child.key < hash {
continue;
}
block.seek(SeekFrom::Start(child.pointer)).unwrap();
}
}
}
}
@ -588,19 +646,24 @@ fn nonce_offset(nonce_root: &Vec<u8>, offset: usize) -> Vec<u8> {
if len < (usize::BITS / 8) as usize {
panic!("Nonce length less than {}", usize::BITS / 8);
}
let start = len - (usize::BITS / 8) as usize;
let mut nonce_copy = vec![0; len];
nonce_copy.clone_from_slice(&nonce_root);
for i in 0..(usize::BITS / 8) {
nonce_copy[len - (i as usize + 1)] += ((offset & (0xff << (8 * i))) >> (8 * i)) as u8;
}
let mut num = u64::from_be_bytes(nonce_copy[start..len].try_into().unwrap());
num += offset as u64;
nonce_copy[start..len].copy_from_slice(&num.to_be_bytes());
nonce_copy
}
impl EncryptionContext {
fn decrypt_block(&self, ciphertext: &Vec<u8>, offset: usize) -> Result<Vec<u8>, &'static str> {
fn decrypt_block(
&self,
ciphertext: &Vec<u8>,
offset: usize,
) -> Result<EncryptionStream, String> {
let plaintext = match self.enc_type {
EncryptionType::Invalid => unreachable!(),
EncryptionType::AesGcm => {
@ -616,13 +679,20 @@ impl EncryptionContext {
};
if plaintext.is_err() {
return Err("Decryption error");
return Err(format!("Decryption error: {}", plaintext.unwrap_err()));
}
Ok(plaintext.unwrap())
Ok(EncryptionStream {
data: plaintext.unwrap(),
pos: 0,
})
}
fn encrypt_block(&self, plaintext: &Vec<u8>, offset: usize) -> Result<Vec<u8>, &'static str> {
fn encrypt_block(
&self,
plaintext: &Vec<u8>,
offset: usize,
) -> Result<EncryptionStream, &'static str> {
let ciphertext = match self.enc_type {
EncryptionType::Invalid => unreachable!(),
EncryptionType::AesGcm => {
@ -641,7 +711,10 @@ impl EncryptionContext {
return Err("Encryption error");
}
Ok(ciphertext.unwrap())
Ok(EncryptionStream {
data: ciphertext.unwrap(),
pos: 0,
})
}
fn decrypt_block_from_file(
@ -649,13 +722,12 @@ impl EncryptionContext {
len: usize,
offset: usize,
file: &mut BufReader<File>,
) -> Result<Vec<u8>, &'static str> {
) -> Result<EncryptionStream, String> {
let mut buf: Vec<u8> = vec![0; len + 16];
if file.read_exact(&mut buf).is_err() {
return Err("Read error.");
match file.read_exact(&mut buf) {
Err(err) => Err(format!("Read error: {}", err.to_string())),
Ok(_) => self.decrypt_block(&buf, offset),
}
self.decrypt_block(&buf, offset)
}
}
@ -680,6 +752,7 @@ mod tests {
#[test]
fn nonce_offset_increment() {
{
let nonce: Vec<u8> = vec![0; 12];
let nonce_off = nonce_offset(&nonce, 0xfeeddeadbeef);
@ -691,12 +764,32 @@ mod tests {
&& nonce_off[8] == 0xde
&& nonce_off[7] == 0xed
&& nonce_off[6] == 0xfe,
"\nn1 = {:x?}\nn2 = {:x?}",
"\ninitial = {:x?}\nnew = {:x?}",
nonce,
nonce_off
);
}
//Carry test
{
let nonce: Vec<u8> = vec![0xfe; 12];
let nonce_off = nonce_offset(&nonce, 0x2);
assert!(
nonce_off[11] == 0x00
&& nonce_off[10] == 0xff
&& nonce_off[9] == 0xfe
&& nonce_off[8] == 0xfe
&& nonce_off[7] == 0xfe
&& nonce_off[6] == 0xfe,
"\ninitial = {:x?}\nnew = {:x?}",
nonce,
nonce_off
);
}
}
#[test]
fn localstore_record_serialize() {
let record = LocalRecord {

View file

@ -99,6 +99,7 @@ impl Read for EncryptionStream {
let range = self.pos..(self.pos + buf.len()).clamp(0, self.data.len() - buf.len());
let size = range.len();
buf.copy_from_slice(&self.data[range]);
self.pos += size;
Ok(size)
}
}
@ -146,3 +147,58 @@ impl Seek for EncryptionStream {
Ok(cur as u64)
}
}
impl EncryptionStream {
pub fn len(&self) -> usize {
self.data.len()
}
}
#[cfg(test)]
mod tests {
use std::io::{Read, Seek, SeekFrom};
use super::EncryptionStream;
fn make_enc_stream() -> EncryptionStream {
EncryptionStream {
data: vec![0; 128],
pos: 0,
}
}
#[test]
fn encryption_stream_seek() {
let mut stream = make_enc_stream();
{
{
let pos = stream.seek(SeekFrom::Start(64));
assert!(pos.is_ok());
assert_eq!(pos.unwrap(), 0);
}
{
let pos = stream.seek(SeekFrom::Start(128));
assert!(pos.is_ok());
assert_eq!(pos.unwrap(), 64);
}
{
let pos = stream.seek(SeekFrom::Start(256));
assert!(pos.is_err());
}
}
}
#[test]
fn encryption_stream_read() {
let mut stream = make_enc_stream();
let mut buf: [u8; 8] = [1; 8];
assert!(stream.read_exact(&mut buf).is_ok());
assert_eq!(&buf, &[0; 8]);
assert_eq!(stream.stream_position().unwrap(), 8);
}
}