ic_auth_client/
storage.rs1use base64::prelude::{BASE64_STANDARD_NO_PAD, Engine as _};
7use thiserror::Error;
8
9#[cfg(feature = "wasm-js")]
10pub mod async_storage;
11#[cfg(feature = "native")]
12pub mod sync_storage;
13
14pub const KEY_STORAGE_KEY: &str = "identity";
16pub const KEY_STORAGE_DELEGATION: &str = "delegation";
18pub(crate) const KEY_VECTOR: &str = "iv";
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum StoredKey {
23 String(String),
25 Raw([u8; 32]),
27}
28
29impl StoredKey {
30 pub fn decode(&self) -> Result<[u8; 32], DecodeError> {
39 match self {
40 StoredKey::String(s) => {
41 let bytes = BASE64_STANDARD_NO_PAD
42 .decode(s)
43 .map_err(DecodeError::Base64)?;
44 let bytes: [u8; 32] = bytes
45 .try_into()
46 .map_err(|_| DecodeError::Ed25519("Invalid slice length".to_string()))?;
47 Ok(bytes)
48 }
49 StoredKey::Raw(bytes) => Ok(*bytes),
50 }
51 }
52
53 pub fn encode(&self) -> String {
57 match self {
58 StoredKey::String(s) => s.clone(),
59 StoredKey::Raw(bytes) => BASE64_STANDARD_NO_PAD.encode(bytes),
60 }
61 }
62}
63
64impl From<[u8; 32]> for StoredKey {
65 fn from(value: [u8; 32]) -> Self {
66 StoredKey::Raw(value)
67 }
68}
69
70impl TryFrom<Vec<u8>> for StoredKey {
71 type Error = DecodeError;
72
73 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
74 let bytes: [u8; 32] = value
75 .try_into()
76 .map_err(|_| DecodeError::Ed25519("Invalid slice length".to_string()))?;
77 Ok(StoredKey::Raw(bytes))
78 }
79}
80
81impl From<String> for StoredKey {
82 fn from(value: String) -> Self {
83 StoredKey::String(value)
84 }
85}
86
87#[derive(Debug, Clone, thiserror::Error)]
92pub enum DecodeError {
93 #[error("Ed25519 error: {0}")]
98 Ed25519(String),
99 #[error("Base64 error: {0}")]
104 Base64(base64::DecodeError),
105}
106
107#[derive(Error, Debug)]
109pub enum StorageError {
110 #[error("Keyring error: {0}")]
112 Keyring(String),
113 #[error("Web Sys error: {0}")]
115 WebSys(String),
116 #[error("File storage error: {0}")]
118 File(String),
119 #[error("Decode error: {0}")]
121 Decode(#[from] DecodeError),
122}
123
124#[cfg(feature = "keyring")]
125impl From<keyring::Error> for StorageError {
126 fn from(err: keyring::Error) -> Self {
127 StorageError::Keyring(err.to_string())
128 }
129}
130
131#[cfg(feature = "wasm-js")]
132impl From<web_sys::wasm_bindgen::JsValue> for StorageError {
133 fn from(err: web_sys::wasm_bindgen::JsValue) -> Self {
134 StorageError::WebSys(
135 err.as_string()
136 .unwrap_or_else(|| "unknown websys error".to_string()),
137 )
138 }
139}
140
141impl From<std::io::Error> for StorageError {
142 fn from(err: std::io::Error) -> Self {
143 StorageError::File(err.to_string())
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use ed25519_dalek::SigningKey;
151
152 #[test]
153 fn test_stored_key_encode_decode() {
154 let mut rng = rand::thread_rng();
155 let signing_key = SigningKey::generate(&mut rng);
156 let raw_key = signing_key.to_bytes();
157
158 let encoded = StoredKey::Raw(raw_key).encode();
159 let key = StoredKey::String(encoded);
160 let decoded = key.decode().unwrap();
161 assert_eq!(raw_key, decoded);
162 }
163}