Restructure preferences. Use B+ Trees for indices. Decouple common blocks.
This commit is contained in:
parent
4ec212dd4b
commit
444e15e3ca
5 changed files with 358 additions and 139 deletions
|
@ -1,10 +1,12 @@
|
|||
use std::{fs::File, io::Read, path::Path};
|
||||
|
||||
use crate::store::HashType;
|
||||
|
||||
pub struct Preferences {
|
||||
_store_type: String,
|
||||
_store_path: String,
|
||||
_pref_hash_algo: String,
|
||||
_pref_hash_algo: HashType,
|
||||
_pref_enc_algo: String,
|
||||
_pref_sign_algo: String,
|
||||
}
|
||||
|
||||
pub fn try_load_preferences() -> Option<Preferences> {
|
||||
|
|
|
@ -11,7 +11,7 @@ fn main() {
|
|||
//options
|
||||
}
|
||||
|
||||
try_load_preferences();
|
||||
let _preferences = try_load_preferences();
|
||||
|
||||
let store = match store::local::load(
|
||||
"/tmp/store.db".to_string(),
|
||||
|
@ -24,5 +24,5 @@ fn main() {
|
|||
Ok(x) => x,
|
||||
};
|
||||
|
||||
store.get_creds_by_key("meow".to_string());
|
||||
store.get_creds("nya".to_string(), "meow".to_string());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::{
|
||||
arch::x86_64::_mm_crc32_u32,
|
||||
collections::{hash_map::DefaultHasher, HashMap},
|
||||
fs::File,
|
||||
io::{BufReader, Read, Write},
|
||||
hash::{Hash, Hasher},
|
||||
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
|
@ -10,12 +12,14 @@ use argon2::PasswordHasher;
|
|||
use password_hash::SaltString;
|
||||
use rand::{rngs::StdRng, RngCore, SeedableRng};
|
||||
|
||||
use super::{Store, StoreRecord};
|
||||
use super::{EncryptionType, HashType, RecordType, Store, StoreRecord};
|
||||
|
||||
pub struct LocalStore {
|
||||
path: String,
|
||||
header: LocalStoreHeader,
|
||||
meta: LocalStoreMeta,
|
||||
enc_ctx: EncryptionContext,
|
||||
cache: HashMap<String, LocalRecord>,
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -39,13 +43,20 @@ pub struct LocalStore {
|
|||
--Block 0
|
||||
Superblock:
|
||||
Hash:
|
||||
0x00 - master password hash
|
||||
0x40 - master password hash
|
||||
Metadata:
|
||||
0x20 - record count
|
||||
0x24 - specifier count
|
||||
0x08 -
|
||||
0x60 - record count
|
||||
0x64 - specifier count
|
||||
0x68 - padding with 0
|
||||
Auth tag: 0x80
|
||||
--Block 1
|
||||
Index:
|
||||
Root node:
|
||||
0x90 - root node or invalid node if empty db
|
||||
*/
|
||||
|
||||
//Proposal: Initialize IV to 0?
|
||||
|
||||
const MAGIC_NUM: u32 = 0x6d656f77;
|
||||
const HEADER_SIZE: usize = 32;
|
||||
|
||||
|
@ -78,14 +89,15 @@ impl LocalStoreHeader {
|
|||
|
||||
fn is_checksum_valid(&self) -> bool {
|
||||
let chk = self.get_checksum();
|
||||
/*println!(
|
||||
"Checksum: {:x}, Expected: {:x}, Size:: {}",
|
||||
self.chksum_crc, chk, HEADER_SIZE
|
||||
);*/
|
||||
chk == self.chksum_crc
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalStoreMeta {
|
||||
record_count: u32,
|
||||
specifier_count: u32,
|
||||
}
|
||||
|
||||
struct EncryptionContext {
|
||||
enc_type: EncryptionType,
|
||||
hash_type: HashType,
|
||||
|
@ -94,44 +106,6 @@ struct EncryptionContext {
|
|||
iv_root: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
enum EncryptionType {
|
||||
Invalid = 0,
|
||||
AesGcm = 1,
|
||||
Chacha20Poly1305 = 2,
|
||||
}
|
||||
|
||||
impl EncryptionType {
|
||||
fn from(n: u8) -> EncryptionType {
|
||||
match n {
|
||||
0 => EncryptionType::Invalid,
|
||||
1 => EncryptionType::AesGcm,
|
||||
2 => EncryptionType::Chacha20Poly1305,
|
||||
_ => panic!("Invalid value '{}'.", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
enum HashType {
|
||||
Invalid = 0,
|
||||
Argon2 = 1,
|
||||
Bcrypt = 2,
|
||||
Pbkdf2 = 4,
|
||||
}
|
||||
|
||||
impl HashType {
|
||||
fn from(n: u8) -> HashType {
|
||||
match n {
|
||||
0 => HashType::Invalid,
|
||||
1 => HashType::Argon2,
|
||||
2 => HashType::Bcrypt,
|
||||
4 => HashType::Pbkdf2,
|
||||
_ => panic!("Invalid value '{}'.", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_deserialize_header(file: &mut BufReader<File>) -> Result<LocalStoreHeader, &'static str> {
|
||||
let mut header: LocalStoreHeader = LocalStoreHeader {
|
||||
magic_h: 0,
|
||||
|
@ -176,16 +150,18 @@ fn try_deserialize_superblock(
|
|||
file: &mut BufReader<File>,
|
||||
enc: &EncryptionContext,
|
||||
header: &LocalStoreHeader,
|
||||
) -> Result<String, &'static str> {
|
||||
let mut buf: Vec<u8> = vec![0; header.enc_sup_block_size as usize];
|
||||
) -> Result<LocalStoreMeta, &'static str> {
|
||||
let data = enc
|
||||
.decrypt_block_from_file(header.enc_sup_block_size as usize, 0, file)
|
||||
.unwrap();
|
||||
|
||||
if file.read_exact(&mut buf).is_err() {
|
||||
return Err("Read error.");
|
||||
if data[0..enc.key.len()].ne(&enc.key) {
|
||||
return Err("Key mismatch.");
|
||||
}
|
||||
|
||||
let dat = enc.decrypt_block(&buf).unwrap();
|
||||
println!("Superblocc: {:?}", &dat);
|
||||
Ok(String::from_utf8_lossy(&dat).try_into().unwrap())
|
||||
let meta = deserialize_store_meta(&data[31..]);
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
fn serialize_header(header: &LocalStoreHeader) -> Vec<u8> {
|
||||
|
@ -208,8 +184,8 @@ fn serialize_header(header: &LocalStoreHeader) -> Vec<u8> {
|
|||
);
|
||||
buf.append(u32::to_be_bytes(header.chksum_crc).to_vec().as_mut());
|
||||
|
||||
let padlen = 32 - buf.len();
|
||||
let mut pad: Vec<u8> = vec![0; padlen];
|
||||
let padding_len = 32 - buf.len();
|
||||
let mut pad: Vec<u8> = vec![0; padding_len];
|
||||
buf.append(&mut pad);
|
||||
|
||||
return buf;
|
||||
|
@ -240,21 +216,28 @@ fn try_create_store(path: &String, passphrase: &Vec<u8>) -> bool {
|
|||
iv_root: iv.to_vec(),
|
||||
};
|
||||
|
||||
let mut enc_buf: Vec<u8> = Vec::new();
|
||||
let mut superblock_buf: Vec<u8> = Vec::new();
|
||||
|
||||
enc_buf.extend_from_slice(&enc.key);
|
||||
if enc_buf.len() != 32 {
|
||||
enc_buf.append([b'\0'].repeat(32 - enc_buf.len()).as_mut());
|
||||
superblock_buf.extend_from_slice(&enc.key);
|
||||
if superblock_buf.len() != 32 {
|
||||
superblock_buf.append([b'\0'].repeat(32 - superblock_buf.len()).as_mut());
|
||||
}
|
||||
|
||||
let enc_buf = enc.encrypt_block(&enc_buf).unwrap();
|
||||
let meta = LocalStoreMeta {
|
||||
record_count: 0,
|
||||
specifier_count: 0,
|
||||
};
|
||||
|
||||
superblock_buf.append(&mut serialize_store_meta(&meta));
|
||||
|
||||
let enc_buf = enc.encrypt_block(&superblock_buf, 0).unwrap();
|
||||
|
||||
let mut header: LocalStoreHeader = LocalStoreHeader {
|
||||
magic_h: MAGIC_NUM,
|
||||
last_access: timestamp,
|
||||
last_write: timestamp,
|
||||
enc_type: enc.enc_type,
|
||||
enc_sup_block_size: enc_buf.len() as u16,
|
||||
enc_sup_block_size: (enc_buf.len() - 16) as u16,
|
||||
enc_key_size: 256,
|
||||
hash_type: enc.hash_type,
|
||||
encrypted_block_offset: 32,
|
||||
|
@ -319,7 +302,25 @@ fn derive_key(
|
|||
Ok(hash)
|
||||
}
|
||||
|
||||
fn get_specifier_map(file: &mut BufReader<File>, header: &LocalStoreHeader) {}
|
||||
fn deserialize_store_meta(data: &[u8]) -> LocalStoreMeta {
|
||||
let records = u32::from_be_bytes(data[0..4].try_into().unwrap());
|
||||
let specifiers = u32::from_be_bytes(data[4..8].try_into().unwrap());
|
||||
|
||||
LocalStoreMeta {
|
||||
record_count: records,
|
||||
specifier_count: specifiers,
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_store_meta(meta: &LocalStoreMeta) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(32);
|
||||
|
||||
buf.append(u32::to_be_bytes(meta.record_count).to_vec().as_mut());
|
||||
buf.append(u32::to_be_bytes(meta.specifier_count).to_vec().as_mut());
|
||||
buf.append([b'\0'].repeat(32 - buf.len()).as_mut());
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn load(path: String, passphrase: Vec<u8>) -> Result<LocalStore, &'static str> {
|
||||
let path_p = Path::new(&path);
|
||||
|
@ -362,112 +363,192 @@ pub fn load(path: String, passphrase: Vec<u8>) -> Result<LocalStore, &'static st
|
|||
iv_root: iv,
|
||||
};
|
||||
|
||||
let sup_data = match try_deserialize_superblock(&mut reader, &enc_ctx, &header) {
|
||||
let meta = match try_deserialize_superblock(&mut reader, &enc_ctx, &header) {
|
||||
Ok(d) => d,
|
||||
Err(x) => return Err(x),
|
||||
};
|
||||
|
||||
println!("Data: {}", &sup_data);
|
||||
|
||||
let spec_map = get_specifier_map(&mut reader, &header);
|
||||
|
||||
let store = LocalStore {
|
||||
path: path,
|
||||
header,
|
||||
enc_ctx,
|
||||
meta,
|
||||
cache: HashMap::new(),
|
||||
};
|
||||
|
||||
return Ok(store);
|
||||
}
|
||||
|
||||
struct CredentialRecord {
|
||||
id: u64,
|
||||
key: String,
|
||||
value: String,
|
||||
specifier: Option<String>,
|
||||
meta: Option<String>,
|
||||
struct LocalRecord {
|
||||
record: StoreRecord,
|
||||
position: usize,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
const CRED_FLAG_SPECIFIER: u8 = 0b0000010;
|
||||
const CRED_FLAG_METADATA: u8 = 0b0000100;
|
||||
|
||||
impl CredentialRecord {
|
||||
impl LocalRecord {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::<u8>::new();
|
||||
let mut buf = Vec::<u8>::new();
|
||||
|
||||
buf.push(self.record.r#type as u8);
|
||||
|
||||
let mut flags: u8 = 0;
|
||||
|
||||
if self.specifier.is_some() {
|
||||
flags |= CRED_FLAG_SPECIFIER;
|
||||
//put specifier here for better indexing
|
||||
//use hashtable in superblock
|
||||
}
|
||||
|
||||
if self.meta.is_some() {
|
||||
if self.record.meta.is_some() {
|
||||
flags |= CRED_FLAG_METADATA;
|
||||
}
|
||||
bytes.push(flags);
|
||||
buf.push(flags);
|
||||
|
||||
bytes.append(u64::to_be_bytes(self.id).to_vec().as_mut());
|
||||
let spec_bytes = self.record.specifier.bytes();
|
||||
let key_bytes = self.record.key.bytes();
|
||||
let value_bytes = &self.record.value;
|
||||
let meta_len = if self.record.meta.is_none() {
|
||||
0
|
||||
} else {
|
||||
self.record.meta.as_ref().unwrap().len()
|
||||
};
|
||||
|
||||
let key_bytes = self.key.bytes();
|
||||
let value_bytes = self.value.bytes();
|
||||
buf.append(u32::to_be_bytes(spec_bytes.len() as u32).to_vec().as_mut());
|
||||
buf.append(u32::to_be_bytes(key_bytes.len() as u32).to_vec().as_mut());
|
||||
buf.append(u32::to_be_bytes(value_bytes.len() as u32).to_vec().as_mut());
|
||||
buf.append(u32::to_be_bytes(meta_len as u32).to_vec().as_mut());
|
||||
|
||||
bytes.append(u32::to_be_bytes(key_bytes.len() as u32).to_vec().as_mut());
|
||||
bytes.append(u32::to_be_bytes(value_bytes.len() as u32).to_vec().as_mut());
|
||||
buf.extend(spec_bytes);
|
||||
buf.extend(key_bytes);
|
||||
buf.extend(value_bytes);
|
||||
|
||||
bytes.extend(key_bytes);
|
||||
bytes.extend(value_bytes);
|
||||
if meta_len > 0 {
|
||||
buf.extend(self.record.meta.as_ref().unwrap().bytes());
|
||||
}
|
||||
|
||||
bytes
|
||||
buf
|
||||
}
|
||||
|
||||
fn from_block(bytes: &[u8]) {
|
||||
let flags = bytes[0];
|
||||
fn from_block(bytes: &[u8]) -> Result<LocalRecord, &'static str> {
|
||||
let r#type = bytes[0];
|
||||
let flags = bytes[1];
|
||||
let spec_len = u32::from_be_bytes(bytes[2..6].try_into().unwrap()) as usize;
|
||||
let key_len = u32::from_be_bytes(bytes[6..10].try_into().unwrap()) as usize;
|
||||
let val_len = u32::from_be_bytes(bytes[10..14].try_into().unwrap()) as usize;
|
||||
let meta_len = u32::from_be_bytes(bytes[14..18].try_into().unwrap()) as usize;
|
||||
|
||||
let id = u64::from_be_bytes(bytes[1..5].try_into().unwrap());
|
||||
todo!()
|
||||
if key_len < 1 {
|
||||
return Err("Invalid structure: key length is 0.");
|
||||
}
|
||||
|
||||
if spec_len < 1 {
|
||||
return Err("Invalid structure: specifier length is 0.");
|
||||
}
|
||||
|
||||
if val_len < 1 {
|
||||
return Err("Invalid structure: value length is 0.");
|
||||
}
|
||||
|
||||
let key_offset: usize = 18 + spec_len;
|
||||
let val_offset: usize = key_offset + key_len;
|
||||
|
||||
let specifier = std::str::from_utf8(&bytes[18..(18 + spec_len)])
|
||||
.expect("Cannot deserialize the specifier.");
|
||||
let key = std::str::from_utf8(&bytes[key_offset..key_offset + key_len])
|
||||
.expect("Cannot deserialize the key.");
|
||||
let value = &bytes[val_offset..val_offset + val_len];
|
||||
let meta = if meta_len == 0 {
|
||||
None
|
||||
} else {
|
||||
let meta_offset = val_offset + val_len;
|
||||
Some(
|
||||
std::str::from_utf8(&bytes[meta_offset..meta_offset + meta_len])
|
||||
.expect("Cannot deserialize metadata.")
|
||||
.to_owned(),
|
||||
)
|
||||
};
|
||||
|
||||
let record = LocalRecord {
|
||||
record: StoreRecord {
|
||||
specifier: specifier.to_owned(),
|
||||
key: key.to_owned(),
|
||||
r#type: RecordType::from(r#type),
|
||||
value: value.to_vec(),
|
||||
meta: meta,
|
||||
},
|
||||
position: 0,
|
||||
size: val_offset + val_len + meta_len,
|
||||
};
|
||||
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn encrypt(&self, typ: EncryptionType) {}
|
||||
}
|
||||
|
||||
impl Store for LocalStore {
|
||||
fn get_creds(&self, id: u64) -> Option<StoreRecord> {
|
||||
fn get_creds(&self, specifier: String, key: String) -> Option<StoreRecord> {
|
||||
self.btree_find_key(&key);
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_creds_by_key(&self, key: String) -> Vec<StoreRecord> {
|
||||
fn get_creds_by_specifier(&self, specifier: String) -> Vec<StoreRecord> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn store_creds(
|
||||
&self,
|
||||
specifier: String,
|
||||
key: String,
|
||||
value: String,
|
||||
specifier: Option<String>,
|
||||
value: Vec<u8>,
|
||||
r#type: RecordType,
|
||||
meta: Option<String>,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalStore {
|
||||
fn btree_find_key(&self, key: &String) {
|
||||
let mut file: BufReader<File> =
|
||||
BufReader::new(File::open(&self.path).expect("Cannot open file"));
|
||||
|
||||
let hash = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
key.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
};
|
||||
|
||||
let pos = file.seek(SeekFrom::Start(0x90)).unwrap();
|
||||
println!("Pos: {:x}", pos);
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce_offset(nonce_root: &Vec<u8>, offset: usize) -> Vec<u8> {
|
||||
let len = nonce_root.len();
|
||||
if len < (usize::BITS / 8) as usize {
|
||||
panic!("Nonce length less than {}", usize::BITS / 8);
|
||||
}
|
||||
|
||||
let mut nonce_copy = vec![0; len];
|
||||
nonce_copy.clone_from_slice(&nonce_root);
|
||||
|
||||
for i in 0..(usize::BITS / 8) {
|
||||
println!(
|
||||
"{}, {:x}",
|
||||
len - (i as usize + 1),
|
||||
(offset & (0xff << (8 * i))) >> (8 * i)
|
||||
);
|
||||
nonce_copy[len - (i as usize + 1)] += ((offset & (0xff << (8 * i))) >> (8 * i)) as u8;
|
||||
}
|
||||
|
||||
nonce_copy
|
||||
}
|
||||
|
||||
impl EncryptionContext {
|
||||
fn decrypt_block(&self, ciphertext: &Vec<u8>) -> Result<Vec<u8>, &'static str> {
|
||||
fn decrypt_block(&self, ciphertext: &Vec<u8>, offset: usize) -> Result<Vec<u8>, &'static str> {
|
||||
let plaintext = match self.enc_type {
|
||||
EncryptionType::Invalid => unreachable!(),
|
||||
EncryptionType::AesGcm => {
|
||||
println!(
|
||||
"Aes dec: len: {} key: {:?}: iv: {:?}",
|
||||
ciphertext.len(),
|
||||
&self.key,
|
||||
&self.iv_root
|
||||
);
|
||||
|
||||
let aes_key = aes_gcm::aead::generic_array::GenericArray::from_slice(&self.key);
|
||||
let aes = Aes256Gcm::new(aes_key);
|
||||
|
||||
let nonce = Nonce::from_slice(&self.iv_root);
|
||||
let nonce = nonce_offset(&self.iv_root, offset);
|
||||
let nonce = Nonce::from_slice(&nonce);
|
||||
|
||||
aes.decrypt(nonce, ciphertext.as_ref())
|
||||
}
|
||||
|
@ -481,21 +562,15 @@ impl EncryptionContext {
|
|||
Ok(plaintext.unwrap())
|
||||
}
|
||||
|
||||
fn encrypt_block(&self, plaintext: &Vec<u8>) -> Result<Vec<u8>, &'static str> {
|
||||
fn encrypt_block(&self, plaintext: &Vec<u8>, offset: usize) -> Result<Vec<u8>, &'static str> {
|
||||
let ciphertext = match self.enc_type {
|
||||
EncryptionType::Invalid => unreachable!(),
|
||||
EncryptionType::AesGcm => {
|
||||
println!(
|
||||
"Aes enc: len: {} key: {:?}: iv: {:?}",
|
||||
plaintext.len(),
|
||||
&self.key,
|
||||
&self.iv_root
|
||||
);
|
||||
|
||||
let aes_key = aes_gcm::aead::generic_array::GenericArray::from_slice(&self.key);
|
||||
let aes = Aes256Gcm::new(aes_key);
|
||||
|
||||
let nonce = Nonce::from_slice(&self.iv_root);
|
||||
let nonce = nonce_offset(&self.iv_root, offset);
|
||||
let nonce = Nonce::from_slice(&nonce);
|
||||
|
||||
aes.encrypt(nonce, plaintext.as_ref())
|
||||
}
|
||||
|
@ -508,13 +583,32 @@ impl EncryptionContext {
|
|||
|
||||
Ok(ciphertext.unwrap())
|
||||
}
|
||||
|
||||
fn decrypt_block_from_file(
|
||||
&self,
|
||||
len: usize,
|
||||
offset: usize,
|
||||
file: &mut BufReader<File>,
|
||||
) -> Result<Vec<u8>, &'static str> {
|
||||
let mut buf: Vec<u8> = vec![0; len + 16];
|
||||
if file.read_exact(&mut buf).is_err() {
|
||||
return Err("Read error.");
|
||||
}
|
||||
|
||||
self.decrypt_block(&buf, offset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::store::local::{LocalStoreHeader, HEADER_SIZE};
|
||||
use crate::store::{
|
||||
local::{nonce_offset, LocalStoreHeader, HEADER_SIZE},
|
||||
StoreRecord,
|
||||
};
|
||||
|
||||
use super::LocalRecord;
|
||||
|
||||
#[test]
|
||||
fn header_size_const_greater_than_realsize() {
|
||||
|
@ -523,4 +617,48 @@ mod tests {
|
|||
|
||||
assert!(c > r, "const = {}, real = {}", c, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonce_offset_increment() {
|
||||
let nonce: Vec<u8> = vec![0; 12];
|
||||
|
||||
let nonce_off = nonce_offset(&nonce, 0xfeeddeadbeef);
|
||||
|
||||
assert!(
|
||||
nonce_off[11] == 0xef
|
||||
&& nonce_off[10] == 0xbe
|
||||
&& nonce_off[9] == 0xad
|
||||
&& nonce_off[8] == 0xde
|
||||
&& nonce_off[7] == 0xed
|
||||
&& nonce_off[6] == 0xfe,
|
||||
"\nn1 = {:x?}\nn2 = {:x?}",
|
||||
nonce,
|
||||
nonce_off
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn localstore_record_serialize() {
|
||||
let rec = LocalRecord {
|
||||
position: 0,
|
||||
size: 0,
|
||||
record: StoreRecord {
|
||||
key: "meow".to_owned(),
|
||||
meta: None,
|
||||
specifier: "test".to_owned(),
|
||||
r#type: crate::store::RecordType::LOGIN_PASSWORD,
|
||||
value: vec![1, 2, 3, 4],
|
||||
},
|
||||
};
|
||||
|
||||
let deser = rec.serialize();
|
||||
|
||||
println!("D: {:x?}", deser);
|
||||
|
||||
let reser = LocalRecord::from_block(&deser).unwrap();
|
||||
|
||||
let redes = reser.serialize();
|
||||
|
||||
assert_eq!(deser, redes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,86 @@ pub(crate) mod local;
|
|||
pub(crate) mod server;
|
||||
|
||||
pub struct StoreRecord {
|
||||
id: u64,
|
||||
specifier: String,
|
||||
key: String,
|
||||
value: String,
|
||||
specifier: Option<String>,
|
||||
r#type: RecordType,
|
||||
value: Vec<u8>,
|
||||
meta: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum RecordType {
|
||||
LOGIN_PASSWORD = 0,
|
||||
PASSWORD = 1,
|
||||
SSH_KEY = 2,
|
||||
PGP_KEY = 3,
|
||||
SYM_KEY = 4,
|
||||
ASYM_KEY_PAIR = 5,
|
||||
ASYM_PRIV_KEY = 6,
|
||||
}
|
||||
|
||||
impl RecordType {
|
||||
pub fn from(n: u8) -> Self {
|
||||
match n {
|
||||
0 => RecordType::LOGIN_PASSWORD,
|
||||
1 => RecordType::PASSWORD,
|
||||
2 => RecordType::SSH_KEY,
|
||||
3 => RecordType::PGP_KEY,
|
||||
4 => RecordType::SYM_KEY,
|
||||
5 => RecordType::ASYM_KEY_PAIR,
|
||||
6 => RecordType::ASYM_PRIV_KEY,
|
||||
_ => panic!("Invalid value '{}'.", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Store {
|
||||
fn get_creds(&self, id: u64) -> Option<StoreRecord>;
|
||||
fn get_creds_by_key(&self, key: String) -> Vec<StoreRecord>;
|
||||
fn get_creds(&self, specifier: String, key: String) -> Option<StoreRecord>;
|
||||
fn get_creds_by_specifier(&self, specifier: String) -> Vec<StoreRecord>;
|
||||
fn store_creds(
|
||||
&self,
|
||||
specifier: String,
|
||||
key: String,
|
||||
value: String,
|
||||
specifier: Option<String>,
|
||||
value: Vec<u8>,
|
||||
r#type: RecordType,
|
||||
meta: Option<String>,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum EncryptionType {
|
||||
Invalid = 0,
|
||||
AesGcm = 1,
|
||||
Chacha20Poly1305 = 2,
|
||||
}
|
||||
|
||||
impl EncryptionType {
|
||||
pub fn from(n: u8) -> Self {
|
||||
match n {
|
||||
0 => EncryptionType::Invalid,
|
||||
1 => EncryptionType::AesGcm,
|
||||
2 => EncryptionType::Chacha20Poly1305,
|
||||
_ => panic!("Invalid value '{}'.", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum HashType {
|
||||
Invalid = 0,
|
||||
Argon2 = 1,
|
||||
Bcrypt = 2,
|
||||
Pbkdf2 = 4,
|
||||
}
|
||||
|
||||
impl HashType {
|
||||
pub fn from(n: u8) -> Self {
|
||||
match n {
|
||||
0 => HashType::Invalid,
|
||||
1 => HashType::Argon2,
|
||||
2 => HashType::Bcrypt,
|
||||
4 => HashType::Pbkdf2,
|
||||
_ => panic!("Invalid value '{}'.", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use reqwest::header::HeaderValue;
|
||||
|
||||
use super::{Store, StoreRecord};
|
||||
use super::{Store, StoreRecord, RecordType};
|
||||
|
||||
pub struct ServerStore {
|
||||
address: String,
|
||||
|
@ -27,20 +27,34 @@ pub fn load(address: String) -> Result<ServerStore, &'static str> {
|
|||
return Ok(ServerStore { address: address });
|
||||
}
|
||||
|
||||
impl ServerStore {
|
||||
fn get_endpoint(&self, path: String) -> String {
|
||||
let base = self.address.trim();
|
||||
let base = if base.ends_with('/') {
|
||||
&base[..base.len() - 2]
|
||||
} else {
|
||||
base
|
||||
};
|
||||
|
||||
format!("{}{}", base, path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Store for ServerStore {
|
||||
fn get_creds(&self, id: u64) -> Option<StoreRecord> {
|
||||
fn get_creds(&self, specifier: String, key: String) -> Option<StoreRecord> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_creds_by_key(&self, key: String) -> Vec<StoreRecord> {
|
||||
fn get_creds_by_specifier(&self, specifier: String) -> Vec<StoreRecord> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn store_creds(
|
||||
&self,
|
||||
specifier: String,
|
||||
key: String,
|
||||
value: String,
|
||||
specifier: Option<String>,
|
||||
value: Vec<u8>,
|
||||
r#type: RecordType,
|
||||
meta: Option<String>,
|
||||
) {
|
||||
todo!()
|
||||
|
|
Loading…
Reference in a new issue