sentc_crypto_utils/
user.rs

1use alloc::string::{String, ToString};
2
3use base64ct::{Base64, Encoding};
4use sentc_crypto_common::user::{
5	ChangePasswordData,
6	DoneLoginServerInput,
7	DoneLoginServerKeysOutput,
8	DoneLoginServerOutput,
9	DoneLoginServerReturn,
10	JwtRefreshInput,
11	OtpInput,
12	PrepareLoginSaltServerOutput,
13	PrepareLoginServerInput,
14	UserPublicKeyData,
15	UserUpdateServerInput,
16	UserVerifyKeyData,
17	VerifyLoginInput,
18};
19use sentc_crypto_common::{DeviceId, UserId};
20use sentc_crypto_core::cryptomat::{DeriveMasterKeyForAuth, PwHash, Sk};
21use serde::{Deserialize, Serialize};
22
23use crate::cryptomat::{PkWrapper, SignComposerWrapper, SignKWrapper, SkWrapper, StaticKeyComposerWrapper, VerifyKWrapper};
24use crate::error::SdkUtilError;
25use crate::{client_random_value_to_string, derive_auth_key_for_auth_to_string, handle_server_response, hashed_authentication_key_to_string};
26
27/**
28# key storage structure for the rust feature
29
30It can be used with other rust programs.
31
32The different to the internally DoneLoginOutput ist that,
33the KeyFormat is sued for each where, were the key id is saved too
34 */
35pub struct DeviceKeyDataInt<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper>
36{
37	pub private_key: Sk,
38	pub sign_key: SiK,
39	pub public_key: Pk,
40	pub verify_key: Vk,
41	pub exported_public_key: UserPublicKeyData,
42	pub exported_verify_key: UserVerifyKeyData,
43}
44
45#[derive(Serialize, Deserialize)]
46pub struct DeviceKeyDataExport
47{
48	pub private_key: String, //Base64 exported keys
49	pub public_key: String,
50	pub sign_key: String,
51	pub verify_key: String,
52	pub exported_public_key: String,
53	pub exported_verify_key: String,
54}
55
56impl<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper> TryFrom<DeviceKeyDataInt<Sk, Pk, SiK, Vk>> for DeviceKeyDataExport
57{
58	type Error = SdkUtilError;
59
60	fn try_from(value: DeviceKeyDataInt<Sk, Pk, SiK, Vk>) -> Result<Self, Self::Error>
61	{
62		Ok(Self {
63			private_key: value.private_key.to_string()?,
64			public_key: value.public_key.to_string()?,
65			sign_key: value.sign_key.to_string()?,
66			verify_key: value.verify_key.to_string()?,
67			exported_public_key: value
68				.exported_public_key
69				.to_string()
70				.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
71			exported_verify_key: value
72				.exported_verify_key
73				.to_string()
74				.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
75		})
76	}
77}
78
79pub struct UserPreVerifyLogin<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper>
80{
81	pub challenge: String,
82	pub device_keys: DeviceKeyDataInt<Sk, Pk, SiK, Vk>,
83	pub user_id: UserId,
84	pub device_id: DeviceId,
85}
86
87fn decrypt_login_challenge(private_key: &impl SkWrapper, challenge: &str) -> Result<String, SdkUtilError>
88{
89	//moved to util crate because this must be done for light and normal sdk
90
91	let challenge = Base64::decode_vec(challenge).map_err(|_| SdkUtilError::DecryptingLoginChallengeFailed)?;
92
93	let decrypted = private_key.get_key().decrypt(&challenge)?;
94
95	String::from_utf8(decrypted).map_err(|_| SdkUtilError::DecryptingLoginChallengeFailed)
96}
97
98/**
99# prepare the data for the server req
100
101 */
102pub fn prepare_login_start(user_identifier: &str) -> Result<String, SdkUtilError>
103{
104	PrepareLoginServerInput {
105		user_identifier: user_identifier.to_string(),
106	}
107	.to_string()
108	.map_err(|_| SdkUtilError::JsonToStringFailed)
109}
110
111/**
112# Starts the login process
113
1141. Get the auth key and the master key encryption key from the password.
1152. Send the auth key to the server to get the DoneLoginInput back
116 */
117pub fn prepare_login<H: PwHash>(user_identifier: &str, password: &str, server_output: &str) -> Result<(String, String, H::DMK), SdkUtilError>
118{
119	let server_output: PrepareLoginSaltServerOutput = handle_server_response(server_output)?;
120
121	let salt = Base64::decode_vec(server_output.salt_string.as_str()).map_err(|_| SdkUtilError::DecodeSaltFailed)?;
122	let result = sentc_crypto_core::user::prepare_login::<H>(password, &salt, server_output.derived_encryption_key_alg.as_str())?;
123
124	//for the server
125	let auth_key = derive_auth_key_for_auth_to_string(&result.auth_key);
126
127	let input = DoneLoginServerInput {
128		auth_key: auth_key.clone(),
129		device_identifier: user_identifier.to_string(),
130	}
131	.to_string()
132	.map_err(|_| SdkUtilError::JsonToStringFailed)?;
133
134	Ok((input, auth_key, result.master_key_encryption_key))
135}
136
137pub fn check_done_login(server_output: &str) -> Result<DoneLoginServerReturn, SdkUtilError>
138{
139	let server_output: DoneLoginServerReturn = handle_server_response(server_output)?;
140
141	Ok(server_output)
142}
143
144/**
145If user enabled mfa then prepare the input here. the token is from the mfa auth like totp, or from the recover keys
146*/
147pub fn prepare_validate_mfa(auth_key: String, device_identifier: String, token: String) -> Result<String, SdkUtilError>
148{
149	serde_json::to_string(&OtpInput {
150		token,
151		auth_key,
152		device_identifier,
153	})
154	.map_err(|_| SdkUtilError::JsonToStringFailed)
155}
156
157/**
158If the user enables mfa, do the done login here
159*/
160pub fn done_validate_mfa<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
161	master_key_encryption: &impl DeriveMasterKeyForAuth,
162	auth_key: String,
163	device_identifier: String,
164	server_output: &str,
165) -> Result<
166	UserPreVerifyLogin<
167		<SkC as StaticKeyComposerWrapper>::SkWrapper,
168		<SkC as StaticKeyComposerWrapper>::PkWrapper,
169		<SiKC as SignComposerWrapper>::SignKWrapper,
170		<SiKC as SignComposerWrapper>::VerifyKWrapper,
171	>,
172	SdkUtilError,
173>
174{
175	let server_output: DoneLoginServerOutput = handle_server_response(server_output)?;
176
177	done_login::<SkC, SiKC>(master_key_encryption, auth_key, device_identifier, server_output)
178}
179
180/**
181# finalize the login process
182
1831. extract the DoneLoginInput from the server. It includes the encrypted master key, encrypted private and sign keys, in pem exported public and verify keys
1842. decrypt the master key with the encryption key from @see prepare_login
1853. import the public and verify keys to the internal format
186 */
187pub fn done_login<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
188	master_key_encryption: &impl DeriveMasterKeyForAuth,
189	auth_key: String,
190	device_identifier: String,
191	server_output: DoneLoginServerOutput,
192) -> Result<
193	UserPreVerifyLogin<
194		<SkC as StaticKeyComposerWrapper>::SkWrapper,
195		<SkC as StaticKeyComposerWrapper>::PkWrapper,
196		<SiKC as SignComposerWrapper>::SignKWrapper,
197		<SiKC as SignComposerWrapper>::VerifyKWrapper,
198	>,
199	SdkUtilError,
200>
201{
202	let device_data = server_output.device_keys;
203
204	let device_keys = done_login_internally_with_device_out::<SkC, SiKC>(master_key_encryption, &device_data)?;
205
206	let challenge = decrypt_login_challenge(&device_keys.private_key, &server_output.challenge)?;
207
208	Ok(UserPreVerifyLogin {
209		device_keys,
210		challenge: serde_json::to_string(&VerifyLoginInput {
211			auth_key,
212			device_identifier,
213			challenge,
214		})
215		.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
216		user_id: device_data.user_id,
217		device_id: device_data.device_id,
218	})
219}
220
221fn done_login_internally_with_device_out<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
222	master_key_encryption: &impl DeriveMasterKeyForAuth,
223	server_output: &DoneLoginServerKeysOutput,
224) -> Result<
225	DeviceKeyDataInt<
226		<SkC as StaticKeyComposerWrapper>::SkWrapper,
227		<SkC as StaticKeyComposerWrapper>::PkWrapper,
228		<SiKC as SignComposerWrapper>::SignKWrapper,
229		<SiKC as SignComposerWrapper>::VerifyKWrapper,
230	>,
231	SdkUtilError,
232>
233{
234	let encrypted_master_key = Base64::decode_vec(server_output.encrypted_master_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
235	let encrypted_private_key = Base64::decode_vec(server_output.encrypted_private_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
236	let encrypted_sign_key = Base64::decode_vec(server_output.encrypted_sign_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
237
238	let out = sentc_crypto_core::user::done_login::<SkC::Composer, SiKC::Composer>(
239		master_key_encryption,
240		&encrypted_master_key,
241		&encrypted_private_key,
242		server_output.keypair_encrypt_alg.as_str(),
243		&encrypted_sign_key,
244		server_output.keypair_sign_alg.as_str(),
245	)?;
246
247	//now prepare the public and verify key for use
248	let public_key = SkC::pk_from_pem(
249		&server_output.public_key_string,
250		&server_output.keypair_encrypt_alg,
251		server_output.keypair_encrypt_id.clone(),
252	)?;
253
254	let verify_key = SiKC::vk_from_pem(
255		&server_output.verify_key_string,
256		&server_output.keypair_sign_alg,
257		server_output.keypair_sign_id.clone(),
258	)?;
259
260	let private_key = SkC::sk_from_inner(out.private_key, server_output.keypair_encrypt_id.clone());
261
262	let sign_key = SiKC::sk_from_inner(out.sign_key, server_output.keypair_sign_id.clone());
263
264	//export this too, so the user can verify the own data
265	let exported_public_key = UserPublicKeyData {
266		public_key_pem: server_output.public_key_string.to_string(),
267		public_key_alg: server_output.keypair_encrypt_alg.to_string(),
268		public_key_id: server_output.keypair_encrypt_id.clone(),
269		public_key_sig: None, //no sig for device keys
270		public_key_sig_key_id: None,
271	};
272
273	let exported_verify_key = UserVerifyKeyData {
274		verify_key_pem: server_output.verify_key_string.to_string(),
275		verify_key_alg: server_output.keypair_sign_alg.to_string(),
276		verify_key_id: server_output.keypair_sign_id.clone(),
277	};
278
279	Ok(DeviceKeyDataInt {
280		private_key,
281		sign_key,
282		public_key,
283		verify_key,
284		exported_public_key,
285		exported_verify_key,
286	})
287}
288
289/**
290Make the prepare and done login req.
291
292- prep login to get the salt
293- done login to get the encrypted master key, because this key is never stored on the device
294 */
295pub fn change_password<H: PwHash>(
296	old_pw: &str,
297	new_pw: &str,
298	server_output_prep_login: &str,
299	server_output_done_login: DoneLoginServerOutput,
300) -> Result<String, SdkUtilError>
301{
302	let server_output_prep_login: PrepareLoginSaltServerOutput = handle_server_response(server_output_prep_login)?;
303
304	let encrypted_master_key = Base64::decode_vec(
305		server_output_done_login
306			.device_keys
307			.encrypted_master_key
308			.as_str(),
309	)
310	.map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
311	let old_salt = Base64::decode_vec(server_output_prep_login.salt_string.as_str()).map_err(|_| SdkUtilError::DecodeSaltFailed)?;
312
313	let output = sentc_crypto_core::user::change_password::<H>(
314		old_pw,
315		new_pw,
316		&old_salt,
317		&encrypted_master_key,
318		server_output_prep_login.derived_encryption_key_alg.as_str(),
319	)?;
320
321	//prepare for the server
322	let new_encrypted_master_key = Base64::encode_string(&output.encrypted_master_key);
323
324	let new_client_random_value = client_random_value_to_string(&output.client_random_value);
325
326	//the 16 bytes of the org. hashed key
327	let new_hashed_authentication_key = hashed_authentication_key_to_string(&output.hashed_authentication_key_bytes);
328
329	let old_auth_key = derive_auth_key_for_auth_to_string(&output.old_auth_key);
330
331	let pw_change_out = ChangePasswordData {
332		new_derived_alg: output.derived_alg.to_string(),
333		new_encrypted_master_key,
334		new_client_random_value,
335		new_hashed_authentication_key,
336		new_encrypted_master_key_alg: output.encrypted_master_key_alg.to_string(),
337		old_auth_key,
338	};
339
340	pw_change_out
341		.to_string()
342		.map_err(|_| SdkUtilError::JsonToStringFailed)
343}
344
345pub fn prepare_refresh_jwt(refresh_token: String) -> Result<String, SdkUtilError>
346{
347	JwtRefreshInput {
348		refresh_token,
349	}
350	.to_string()
351	.map_err(|_| SdkUtilError::JsonToStringFailed)
352}
353
354pub fn prepare_user_identifier_update(user_identifier: String) -> Result<String, SdkUtilError>
355{
356	let input = UserUpdateServerInput {
357		user_identifier,
358	};
359
360	input
361		.to_string()
362		.map_err(|_| SdkUtilError::JsonToStringFailed)
363}