1use crate::{Error, Result};
3use secrecy::ExposeSecret;
4use sos_backend::{
5 database::entity::{AccountEntity, FolderEntity, FolderRow},
6 AccessPoint, BackendTarget,
7};
8use sos_core::{
9 crypto::AccessKey,
10 device::{DeviceMetaData, DevicePublicKey, TrustedDevice},
11 encode, AccountId, SecretId, VaultFlags,
12};
13use sos_filesystem::write_exclusive;
14use sos_signer::ed25519::{self, BoxedEd25519Signer, SingleParty};
15use sos_vault::{
16 secret::{Secret, SecretSigner},
17 BuilderCredentials, SecretAccess, Vault, VaultBuilder,
18};
19use urn::Urn;
20
21#[derive(Clone)]
23pub struct DeviceSigner(pub(crate) BoxedEd25519Signer);
24
25impl DeviceSigner {
26 pub fn random() -> Self {
28 let key = SingleParty::new_random();
29 Self(Box::new(key))
30 }
31
32 pub fn signing_key(&self) -> &BoxedEd25519Signer {
34 &self.0
35 }
36
37 pub fn public_key(&self) -> DevicePublicKey {
39 self.0.verifying_key().as_bytes().into()
40 }
41
42 pub fn to_bytes(&self) -> [u8; 32] {
44 self.0.to_bytes().try_into().unwrap()
45 }
46}
47
48impl TryFrom<[u8; 32]> for DeviceSigner {
49 type Error = Error;
50
51 fn try_from(value: [u8; 32]) -> Result<Self> {
52 let signer: SingleParty = value.try_into()?;
53 Ok(Self(Box::new(signer)))
54 }
55}
56
57impl From<DeviceSigner> for BoxedEd25519Signer {
58 fn from(value: DeviceSigner) -> Self {
59 value.0
60 }
61}
62
63impl From<SingleParty> for DeviceSigner {
64 fn from(value: SingleParty) -> Self {
65 Self(Box::new(value))
66 }
67}
68
69pub struct DeviceManager {
73 signer: DeviceSigner,
75 access_point: AccessPoint,
78}
79
80impl DeviceManager {
81 fn init(signer: DeviceSigner, access_point: AccessPoint) -> Self {
86 Self {
87 signer,
88 access_point,
89 }
90 }
91
92 pub async fn new(
94 target: &BackendTarget,
95 account_id: &AccountId,
96 signer: DeviceSigner,
97 password: &AccessKey,
98 ) -> Result<Self> {
99 let device_vault = Self::create_device_vault(password).await?;
100 let access_point = match target {
101 BackendTarget::FileSystem(paths) => {
102 let buffer = encode(&device_vault).await?;
103 write_exclusive(paths.device_file(), &buffer).await?;
104 AccessPoint::from_path(paths.device_file(), device_vault)
105 }
106 BackendTarget::Database(_, client) => {
107 let account_id = *account_id;
108 let folder_row = FolderRow::new_insert(&device_vault).await?;
109 client
110 .conn(move |conn| {
111 let account = AccountEntity::new(&conn);
112 let folder = FolderEntity::new(&conn);
113 let account_row = account.find_one(&account_id)?;
114 let folder_id = folder
115 .insert_folder(account_row.row_id, &folder_row)?;
116 account.insert_device_folder(
117 account_row.row_id,
118 folder_id,
119 )
120 })
121 .await
122 .map_err(sos_backend::database::Error::from)?;
123 AccessPoint::new(target.clone(), device_vault).await
124 }
125 };
126 Ok(Self::init(signer, access_point))
127 }
128
129 pub async fn open_vault(
131 target: BackendTarget,
132 vault: Vault,
133 access_key: &AccessKey,
134 ) -> Result<(Self, SecretId)> {
135 let device_key_urn = Self::device_urn()?;
136 tracing::debug!(
137 urn = %device_key_urn,
138 backend_target = %target,
139 "device::open_vault");
140
141 let mut device_keeper = AccessPoint::new(target, vault).await;
142 device_keeper.unlock(access_key).await?;
143
144 let mut device_signer_secret: Option<(SecretId, Secret)> = None;
146 for id in device_keeper.vault().keys() {
147 if let Some((meta, secret, _)) =
148 device_keeper.read_secret(id).await?
149 {
150 if let Some(urn) = meta.urn() {
151 if urn == &device_key_urn {
152 device_signer_secret = Some((*id, secret));
153 break;
154 }
155 }
156 }
157 }
158
159 if let Some((
160 id,
161 Secret::Signer {
162 private_key: SecretSigner::SinglePartyEd25519(data),
163 ..
164 },
165 )) = device_signer_secret
166 {
167 let key: ed25519::SingleParty =
168 data.expose_secret().as_slice().try_into()?;
169 Ok((DeviceManager::init(key.into(), device_keeper), id))
170 } else {
171 Err(Error::VaultEntryKind(device_key_urn.to_string()))
172 }
173 }
174
175 pub(crate) fn device_urn() -> Result<Urn> {
176 use sos_core::constants::DEVICE_KEY_URN;
177 Ok(DEVICE_KEY_URN.parse()?)
178 }
179
180 async fn create_device_vault(password: &AccessKey) -> Result<Vault> {
181 let vault = VaultBuilder::new()
183 .public_name("Device".to_string())
184 .flags(
185 VaultFlags::SYSTEM | VaultFlags::DEVICE | VaultFlags::NO_SYNC,
186 )
187 .build(BuilderCredentials::Password(
188 password.clone().into(),
189 None,
190 ))
191 .await?;
192
193 Ok(vault)
194 }
195
196 pub(crate) fn access_point(&self) -> &AccessPoint {
198 &self.access_point
199 }
200
201 pub(crate) fn access_point_mut(&mut self) -> &mut AccessPoint {
203 &mut self.access_point
204 }
205
206 pub(crate) fn signer(&self) -> &DeviceSigner {
208 &self.signer
209 }
210
211 pub async fn into_vault_buffer(self) -> Result<Vec<u8>> {
213 let vault: Vault = self.access_point.into();
214 let buffer = encode(&vault).await?;
215 Ok(buffer)
216 }
217
218 pub fn current_device(
220 &self,
221 extra_info: Option<DeviceMetaData>,
222 ) -> TrustedDevice {
223 TrustedDevice::new(self.signer.public_key(), extra_info, None)
224 }
225
226 pub fn sign_out(&mut self) {
228 self.access_point.lock();
229 }
230}