use crate::device::{Device, DeviceWrapper, Librem, Pro, Storage};
use crate::error::{CommandError, Error, LibraryError};
use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};
const SLOT_COUNT: u8 = 16;
#[derive(Debug)]
pub struct PasswordSafe<'a, 'b> {
_device: &'a dyn Device<'b>,
}
#[derive(Clone, Copy, Debug)]
pub struct PasswordSlot<'p, 'a, 'b> {
slot: u8,
_pws: &'p PasswordSafe<'a, 'b>,
}
pub trait GetPasswordSafe<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error>;
}
fn get_password_safe<'a, 'b>(
device: &'a dyn Device<'b>,
user_pin: &str,
) -> Result<PasswordSafe<'a, 'b>, Error> {
let user_pin_string = get_cstring(user_pin)?;
get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) })
.map(|_| PasswordSafe { _device: device })
}
fn get_pws_result(s: String) -> Result<String, Error> {
if s.is_empty() {
Err(CommandError::SlotNotProgrammed.into())
} else {
Ok(s)
}
}
impl<'a, 'b> PasswordSafe<'a, 'b> {
pub fn get_slot_count(&self) -> u8 {
SLOT_COUNT
}
#[deprecated(since = "0.9.0", note = "Use get_slots() instead")]
pub fn get_slot_status(&self) -> Result<[bool; 16], Error> {
let status_ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_status() };
if status_ptr.is_null() {
return Err(get_last_error());
}
let status_array_ptr = status_ptr as *const [u8; SLOT_COUNT as usize];
let status_array = unsafe { *status_array_ptr };
let mut result = [false; SLOT_COUNT as usize];
for i in 0..SLOT_COUNT {
result[i as usize] = status_array[i as usize] == 1;
}
unsafe {
nitrokey_sys::NK_free_password_safe_slot_status(status_ptr);
}
Ok(result)
}
pub fn get_slots(&self) -> Result<Vec<Option<PasswordSlot<'_, 'a, 'b>>>, Error> {
let mut slots = Vec::new();
let status_ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_status() };
if status_ptr.is_null() {
return Err(get_last_error());
}
let status_array_ptr = status_ptr as *const [u8; SLOT_COUNT as usize];
let status_array = unsafe { *status_array_ptr };
for slot in 0..SLOT_COUNT {
if status_array[usize::from(slot)] == 1 {
slots.push(Some(PasswordSlot { slot, _pws: self }));
} else {
slots.push(None);
}
}
unsafe {
nitrokey_sys::NK_free_password_safe_slot_status(status_ptr);
}
Ok(slots)
}
pub fn get_slot(&self, slot: u8) -> Result<PasswordSlot<'_, 'a, 'b>, Error> {
let slot = usize::from(slot);
let slots = self.get_slots()?;
if slot < slots.len() {
slots[slot].ok_or_else(|| CommandError::SlotNotProgrammed.into())
} else {
Err(LibraryError::InvalidSlot.into())
}
}
pub fn get_slot_unchecked(&self, slot: u8) -> Result<PasswordSlot<'_, 'a, 'b>, Error> {
if slot < self.get_slot_count() {
Ok(PasswordSlot { slot, _pws: self })
} else {
Err(LibraryError::InvalidSlot.into())
}
}
#[deprecated(since = "0.9.0", note = "Use get_slot(slot)?.get_name() instead")]
pub fn get_slot_name(&self, slot: u8) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) })
.and_then(get_pws_result)
}
#[deprecated(since = "0.9.0", note = "Use get_slot(slot)?.get_login() instead")]
pub fn get_slot_login(&self, slot: u8) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_login(slot) })
.and_then(get_pws_result)
}
#[deprecated(since = "0.9.0", note = "Use get_slot(slot)?.get_password() instead")]
pub fn get_slot_password(&self, slot: u8) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_password(slot) })
.and_then(get_pws_result)
}
pub fn write_slot(
&mut self,
slot: u8,
name: &str,
login: &str,
password: &str,
) -> Result<(), Error> {
let name_string = get_cstring(name)?;
let login_string = get_cstring(login)?;
let password_string = get_cstring(password)?;
get_command_result(unsafe {
nitrokey_sys::NK_write_password_safe_slot(
slot,
name_string.as_ptr(),
login_string.as_ptr(),
password_string.as_ptr(),
)
})
}
pub fn erase_slot(&mut self, slot: u8) -> Result<(), Error> {
get_command_result(unsafe { nitrokey_sys::NK_erase_password_safe_slot(slot) })
}
}
impl<'a, 'b> Drop for PasswordSafe<'a, 'b> {
fn drop(&mut self) {
}
}
impl<'p, 'a, 'b> PasswordSlot<'p, 'a, 'b> {
pub fn index(&self) -> u8 {
self.slot
}
pub fn get_name(&self) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_name(self.slot) })
}
pub fn get_login(&self) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_login(self.slot) })
}
pub fn get_password(&self) -> Result<String, Error> {
result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_password(self.slot) })
}
}
impl<'a> GetPasswordSafe<'a> for Librem<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
impl<'a> GetPasswordSafe<'a> for Pro<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
impl<'a> GetPasswordSafe<'a> for Storage<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}