diff --git a/src/storage/aci.rs b/src/storage/aci.rs index 33f6c0d..08cf9ff 100644 --- a/src/storage/aci.rs +++ b/src/storage/aci.rs @@ -390,6 +390,7 @@ fn decrypt_data( const MAX_LOGIN_ATTEMPTS: CK_ULONG = 10; +#[derive(Clone, Debug)] pub struct StorageAuthInfo { pub default_pin: bool, pub user_data: Option>, diff --git a/src/storage/format.rs b/src/storage/format.rs index 750834c..188836e 100644 --- a/src/storage/format.rs +++ b/src/storage/format.rs @@ -60,8 +60,8 @@ fn checked_pin(pin: &[u8]) -> &[u8] { pin } -const SO_ID: &str = "SO"; -const USER_ID: &str = "USER"; +pub const SO_ID: &str = "SO"; +pub const USER_ID: &str = "USER"; fn get_pin_uid(user_type: CK_USER_TYPE) -> Result<&'static str> { match user_type { diff --git a/src/storage/json.rs b/src/storage/json.rs index e2322d2..d9d336b 100644 --- a/src/storage/json.rs +++ b/src/storage/json.rs @@ -4,8 +4,8 @@ use crate::error::Result; use crate::interface::*; use crate::object::Object; -use crate::storage::aci::StorageACI; -use crate::storage::format::{StdStorageFormat, StorageRaw}; +use crate::storage::aci; +use crate::storage::format; use crate::storage::{memory, Storage, StorageDBInfo, StorageTokenInfo}; mod objects { @@ -16,10 +16,10 @@ use objects::*; #[derive(Debug)] pub struct JsonStorage { filename: String, - cache: Box, + cache: Box, } -impl StorageRaw for JsonStorage { +impl format::StorageRaw for JsonStorage { fn is_initialized(&self) -> Result<()> { self.cache.is_initialized() } @@ -57,7 +57,19 @@ impl StorageRaw for JsonStorage { self.cache.fetch_token_info() } fn store_token_info(&mut self, info: &StorageTokenInfo) -> Result<()> { - self.cache.store_token_info(info) + self.cache.store_token_info(info)?; + self.flush() + } + fn fetch_user(&self, uid: &str) -> Result { + self.cache.fetch_user(uid) + } + fn store_user( + &mut self, + uid: &str, + data: &aci::StorageAuthInfo, + ) -> Result<()> { + self.cache.store_user(uid, data)?; + self.flush() } } @@ -75,9 +87,9 @@ impl StorageDBInfo for JsonDBInfo { }, cache: memory::raw_store(), }); - Ok(Box::new(StdStorageFormat::new( + Ok(Box::new(format::StdStorageFormat::new( raw_store, - StorageACI::new(true), + aci::StorageACI::new(true), ))) } diff --git a/src/storage/json_objects.rs b/src/storage/json_objects.rs index ebb8b53..ee5e2a2 100644 --- a/src/storage/json_objects.rs +++ b/src/storage/json_objects.rs @@ -2,8 +2,11 @@ use crate::attribute::string_to_ck_date; use crate::attribute::{AttrType, Attribute}; use crate::error::{Error, Result}; use crate::interface::*; +use crate::misc::copy_sized_string; use crate::object::Object; -use crate::storage::format::StorageRaw; +use crate::storage::aci; +use crate::storage::format; +use crate::storage::StorageTokenInfo; use data_encoding::BASE64; use serde::{Deserialize, Serialize}; @@ -56,11 +59,42 @@ impl JsonObject { } jo } + fn from_user(uid: &str, info: &aci::StorageAuthInfo) -> JsonObject { + let mut ju = JsonObject { + attributes: Map::new(), + }; + ju.attributes + .insert("name".to_string(), Value::String(uid.to_string())); + ju.attributes + .insert("default_pin".to_string(), Value::Bool(info.default_pin)); + ju.attributes.insert( + "attempts".to_string(), + Value::Number(Number::from(info.cur_attempts)), + ); + if let Some(data) = &info.user_data { + ju.attributes.insert( + "data".to_string(), + Value::String(BASE64.encode(data.as_slice())), + ); + } + ju + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonTokenInfo { + label: String, + manufacturer: String, + model: String, + serial: String, + flags: CK_ULONG, } #[derive(Debug, Serialize, Deserialize)] pub struct JsonObjects { objects: Vec, + users: Option>, + token: Option, } impl JsonObjects { @@ -71,7 +105,69 @@ impl JsonObjects { } } - pub fn prime_store(&self, store: &mut Box) -> Result<()> { + pub fn prime_store( + &self, + store: &mut Box, + ) -> Result<()> { + if let Some(t) = &self.token { + let mut info = StorageTokenInfo { + label: [0; 32], + manufacturer: [0; 32], + model: [0; 16], + serial: [0; 16], + flags: 0, + }; + copy_sized_string(t.label.as_bytes(), &mut info.label); + copy_sized_string( + t.manufacturer.as_bytes(), + &mut info.manufacturer, + ); + copy_sized_string(t.model.as_bytes(), &mut info.model); + copy_sized_string(t.serial.as_bytes(), &mut info.serial); + info.flags = t.flags; + store.store_token_info(&info)?; + } + if let Some(users) = &self.users { + for ju in users { + let mut uid = String::new(); + let mut info = aci::StorageAuthInfo::default(); + for (key, val) in &ju.attributes { + match key.as_str() { + "name" => match val.as_str() { + Some(s) => uid.push_str(s), + None => return Err(CKR_DEVICE_ERROR)?, + }, + "default_pin" => match val.as_bool() { + Some(b) => info.default_pin = b, + None => return Err(CKR_DEVICE_ERROR)?, + }, + "attempts" => match val.as_u64() { + Some(u) => { + info.cur_attempts = CK_ULONG::try_from(u)? + } + None => return Err(CKR_DEVICE_ERROR)?, + }, + "data" => match val.as_str() { + Some(s) => { + let len = match BASE64.decode_len(s.len()) { + Ok(l) => l, + Err(_) => return Err(CKR_DEVICE_ERROR)?, + }; + let mut v = vec![0; len]; + match BASE64.decode_mut(s.as_bytes(), &mut v) { + Ok(l) => v.resize(l, 0), + Err(_) => return Err(CKR_DEVICE_ERROR)?, + } + info.user_data = Some(v); + } + None => return Err(CKR_DEVICE_ERROR)?, + }, + _ => (), /* ignore unknown */ + } + } + store.store_user(uid.as_str(), &info)?; + } + } for jo in &self.objects { let mut obj = Object::new(); for (key, val) in &jo.attributes { @@ -127,19 +223,39 @@ impl JsonObjects { Ok(()) } - pub fn from_store(store: &mut Box) -> JsonObjects { + pub fn from_store(store: &mut Box) -> JsonObjects { let objs = store.search(&[]).unwrap(); - let mut jt = JsonObjects { - objects: Vec::with_capacity(objs.len()), - }; + let mut jobjs = Vec::with_capacity(objs.len()); for o in objs { if !o.is_token() { continue; } - jt.objects.push(JsonObject::from_object(&o)); + jobjs.push(JsonObject::from_object(&o)); + } + + let info = store.fetch_token_info().unwrap(); + let jtoken = JsonTokenInfo { + label: String::from_utf8(info.label.to_vec()).unwrap(), + manufacturer: String::from_utf8(info.manufacturer.to_vec()) + .unwrap(), + model: String::from_utf8(info.model.to_vec()).unwrap(), + serial: String::from_utf8(info.serial.to_vec()).unwrap(), + flags: info.flags, + }; + + let mut jusers = Vec::new(); + for id in [format::SO_ID, format::USER_ID] { + match store.fetch_user(id) { + Ok(u) => jusers.push(JsonObject::from_user(id, &u)), + Err(_) => (), + } } - jt + JsonObjects { + objects: jobjs, + users: Some(jusers), + token: Some(jtoken), + } } pub fn save(&self, filename: &str) -> Result<()> { diff --git a/src/storage/memory.rs b/src/storage/memory.rs index 264c731..53b5020 100644 --- a/src/storage/memory.rs +++ b/src/storage/memory.rs @@ -4,24 +4,23 @@ use std::collections::HashMap; use std::fmt::Debug; -use crate::attribute::Attribute; use crate::error::{Error, Result}; use crate::interface::*; -use crate::misc::copy_sized_string; use crate::object::Object; -use crate::storage::aci::StorageACI; -use crate::storage::format::{StdStorageFormat, StorageRaw}; +use crate::storage::aci; +use crate::storage::format; use crate::storage::{Storage, StorageDBInfo, StorageTokenInfo}; #[derive(Debug)] struct MemoryStorage { objects: HashMap, token_info: StorageTokenInfo, + users: HashMap, } -impl StorageRaw for MemoryStorage { +impl format::StorageRaw for MemoryStorage { fn is_initialized(&self) -> Result<()> { - if self.objects.len() != 0 { + if self.token_info.flags & CKF_TOKEN_INITIALIZED != 0 { Ok(()) } else { Err(CKR_CRYPTOKI_NOT_INITIALIZED)? @@ -75,13 +74,31 @@ impl StorageRaw for MemoryStorage { self.token_info.model = info.model; self.token_info.serial = info.serial; self.token_info.flags = info.flags; + Ok(()) + } + + fn fetch_user(&self, uid: &str) -> Result { + match self.users.get(uid) { + Some(u) => Ok(u.clone()), + None => Err(CKR_USER_PIN_NOT_INITIALIZED)?, + } + } + + fn store_user( + &mut self, + uid: &str, + data: &aci::StorageAuthInfo, + ) -> Result<()> { + self.users.insert(uid.to_string(), data.clone()); + Ok(()) } } -pub fn raw_store() -> Box { +pub fn raw_store() -> Box { Box::new(MemoryStorage { objects: HashMap::new(), token_info: StorageTokenInfo::default(), + users: HashMap::new(), }) } @@ -100,9 +117,9 @@ impl StorageDBInfo for MemoryDBInfo { None => false, }; let raw_store = raw_store(); - Ok(Box::new(StdStorageFormat::new( + Ok(Box::new(format::StdStorageFormat::new( raw_store, - StorageACI::new(encrypt), + aci::StorageACI::new(encrypt), ))) } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 460dbe7..1ea3f54 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -23,6 +23,7 @@ const TOKEN_MODEL: &str = "FIPS-140-3 v1"; #[cfg(not(feature = "fips"))] const TOKEN_MODEL: &str = "v1"; +#[derive(Clone, Debug)] pub struct StorageTokenInfo { pub label: [CK_UTF8CHAR; 32usize], pub manufacturer: [CK_UTF8CHAR; 32usize], @@ -105,7 +106,7 @@ pub trait Storage: Debug + Send + Sync { ) -> Result<()>; } -mod aci; +pub mod aci; pub mod format; #[cfg(feature = "jsondb")]