Skip to main content

sentc_crypto_core/
user.rs

1use alloc::vec::Vec;
2
3use sha2::{Digest, Sha256};
4
5use crate::cryptomat::{
6	ClientRandomValue,
7	CryptoAlg,
8	DeriveAuthKeyForAuth,
9	DeriveMasterKeyForAuth,
10	HashedAuthenticationKey,
11	Pk,
12	PwHash,
13	SignK,
14	SignKeyComposer,
15	SignKeyPair,
16	Sk,
17	SkComposer,
18	StaticKeyPair,
19	SymKeyGen,
20	VerifyK,
21};
22use crate::error::Error;
23
24pub struct RegisterOutPut<P: Pk, V: VerifyK, CRV: ClientRandomValue, HAK: HashedAuthenticationKey>
25{
26	//info about the raw master key (not the encrypted by the password!)
27	pub master_key_alg: &'static str,
28
29	//from key derived
30	pub client_random_value: CRV,
31	pub hashed_authentication_key_bytes: HAK,
32	pub encrypted_master_key: Vec<u8>,
33	pub encrypted_master_key_alg: &'static str,
34	pub derived_alg: &'static str,
35
36	//the key pairs incl. the encrypted secret keys
37	pub public_key: P,
38	pub encrypted_private_key: Vec<u8>,
39	pub keypair_encrypt_alg: &'static str,
40	pub verify_key: V,
41	pub encrypted_sign_key: Vec<u8>,
42	pub keypair_sign_alg: &'static str,
43}
44
45pub struct LoginDoneOutput<S: Sk, Si: SignK>
46{
47	pub private_key: S,
48	pub sign_key: Si,
49}
50
51pub struct ChangePasswordOutput<CRV: ClientRandomValue, HAK: HashedAuthenticationKey, DAK: DeriveAuthKeyForAuth>
52{
53	pub client_random_value: CRV,
54	pub hashed_authentication_key_bytes: HAK,
55	pub encrypted_master_key: Vec<u8>,
56	pub encrypted_master_key_alg: &'static str,
57	pub derived_alg: &'static str,
58	pub old_auth_key: DAK,
59}
60
61pub struct ResetPasswordOutput<CRV: ClientRandomValue, HAK: HashedAuthenticationKey>
62{
63	pub master_key_alg: &'static str,
64	pub client_random_value: CRV,
65	pub hashed_authentication_key_bytes: HAK,
66	pub encrypted_master_key: Vec<u8>,
67	pub encrypted_master_key_alg: &'static str,
68	pub derived_alg: &'static str,
69	pub encrypted_private_key: Vec<u8>,
70	pub encrypted_sign_key: Vec<u8>,
71}
72
73pub struct PrepareLoginOutput<DMK: DeriveMasterKeyForAuth, DAK: DeriveAuthKeyForAuth>
74{
75	pub master_key_encryption_key: DMK,
76	pub auth_key: DAK,
77}
78
79/**
80# Register a new user
81
82Every user got a master key, every other user key is encrypted by this master key.
83
84The master key is encrypted by a derived key from password. The password turned into two different keys via a password hash.
85The first key is the derived key and encrypts the master key. The second key is the authentication for the server.
86Only this key gets send to the server not the password.
87
88A public / private encryption key pair and a sign / verify key pair get created too.
89
90Both private and sign key gets encrypted by the master key.
91
92<br>
93
94## For key update:
95Just register the user again with a password, it should be the same password for usability, but it can a different password too.
96Then login again and encrypt everything what was encrypted by the old keys with the new keys
97(e.g. group keys (encrypted by public key), or direct encrypted data).
98*/
99#[allow(clippy::type_complexity)]
100pub fn register<S: SymKeyGen, St: StaticKeyPair, Sign: SignKeyPair, H: PwHash>(
101	password: &str,
102) -> Result<RegisterOutPut<St::PublicKey, Sign::VerifyKey, H::CRV, H::HAK>, Error>
103{
104	//1. create master key
105	let master_key = S::generate()?;
106
107	//2. create static pub/pri key pair for encrypt and decrypt
108	let (sk, public_key) = St::generate_static_keypair()?;
109
110	//3. create sign key pair for sign and verify
111	let (sign_k, verify_key) = Sign::generate_key_pair()?;
112
113	let encrypted_private_key = sk.encrypt_by_master_key(&master_key)?;
114	let encrypted_sign_key = sign_k.encrypt_by_master_key(&master_key)?;
115
116	let (client_random_value, hashed_authentication_key_bytes, encrypted_master_key, encrypted_master_key_alg) =
117		H::derived_keys_from_password(password.as_bytes(), &master_key, None)?;
118
119	Ok(RegisterOutPut {
120		master_key_alg: master_key.get_alg_str(),
121		derived_alg: client_random_value.get_alg_str(),
122		client_random_value,
123		hashed_authentication_key_bytes,
124		encrypted_master_key,
125		encrypted_master_key_alg,
126		encrypted_sign_key,
127		verify_key,
128		keypair_sign_alg: sign_k.get_alg_str(),
129		encrypted_private_key,
130		public_key,
131		keypair_encrypt_alg: sk.get_alg_str(),
132	})
133}
134
135/**
136# Start the login process
137
138The salt was generated by the server when the user first interact with the server.
139*/
140pub fn prepare_login<H: PwHash>(password: &str, salt: &[u8], derived_encryption_key_alg: &str) -> Result<PrepareLoginOutput<H::DMK, H::DAK>, Error>
141{
142	let (master_key_encryption_key, auth_key) = H::derive_keys_for_auth(password.as_bytes(), salt, derived_encryption_key_alg)?;
143
144	Ok(PrepareLoginOutput {
145		master_key_encryption_key,
146		auth_key,
147	})
148}
149
150/**
151# End the login process
152
153Get all information about the current used login keys
154*/
155pub fn done_login<SkC: SkComposer, SiC: SignKeyComposer>(
156	derived_encryption_key: &impl DeriveMasterKeyForAuth, //the value from prepare_login
157	encrypted_master_key: &[u8],                          //as base64 encoded string from the server
158	encrypted_private_key: &[u8],
159	keypair_encrypt_alg: &str,
160	encrypted_sign_key: &[u8],
161	keypair_sign_alg: &str,
162) -> Result<LoginDoneOutput<SkC::SecretKey, SiC::Key>, Error>
163{
164	//decrypt the master key from the derived key from password
165	let master_key = derived_encryption_key.get_master_key(encrypted_master_key)?;
166
167	//decode the private keys to the enums to use them later
168	let private_key = SkC::decrypt_by_master_key(&master_key, encrypted_private_key, keypair_encrypt_alg)?;
169	let sign_key = SiC::decrypt_by_master_key(&master_key, encrypted_sign_key, keypair_sign_alg)?;
170
171	Ok(LoginDoneOutput {
172		private_key,
173		sign_key,
174	})
175}
176
177/**
178# Prepare Password change
179
180before calling this function make a request to the login api endpoint with the username
181to get the salt from the api
182if the old pw was wrong this will check in the api later after this function
183
184get the old auth key from @see prepare_login()
185with the old pw and the old salt
186
187decrypt the master key with the old pw (because it is stored in the client but encrypted)
188
189use the function @see derivedKeysFromPassword with the new pw and the decrypted master key
190to create the new client random value, the new encrypted master key and the new hashed authkey
191
192send the return data back to the server
193*/
194#[allow(clippy::type_complexity)]
195pub fn change_password<H: PwHash>(
196	old_pw: &str,
197	new_pw: &str,
198	old_salt: &[u8],
199	encrypted_master_key: &[u8],
200	derived_encryption_key_alg: &str,
201) -> Result<ChangePasswordOutput<H::CRV, H::HAK, H::DAK>, Error>
202{
203	//first make a request to login endpoint -> prepareLogin() with the username to get the salt
204	//get the old auth key
205	let prepare_login_output = prepare_login::<H>(old_pw, old_salt, derived_encryption_key_alg)?;
206
207	//decrypt the master key with the old pw.
208	let master_key = prepare_login_output
209		.master_key_encryption_key
210		.get_master_key(encrypted_master_key)?;
211
212	//encrypt the master key with the new pw and create a new salt with a new random value
213	//the 2nd check is necessary because master key from different alg can have different length
214	let (client_random_value, hashed_authentication_key_bytes, encrypted_master_key, encrypted_master_key_alg) =
215		H::derived_keys_from_password(new_pw.as_bytes(), &master_key, Some(derived_encryption_key_alg))?;
216
217	Ok(ChangePasswordOutput {
218		derived_alg: client_random_value.get_alg_str(),
219		client_random_value,
220		hashed_authentication_key_bytes,
221		encrypted_master_key,
222		encrypted_master_key_alg,
223		old_auth_key: prepare_login_output.auth_key,
224	})
225}
226
227/**
228# Reset the users password
229
230Only works if the user still got access to the decrypted private key and the decrypted sign key (which are encrypted by the old password).
231
2321. create a new master key like register()
2332. encrypt the private and sign keys with the new master key. Use here the alg from the actual selected feature
2343. finally encrypt the new master key by the new password
235
236## Security hint:
237- the server still awaits a valid auth token when sending the reset password request
238- an attacker needs access to the decrypted private key, decrypted sign key and the auth token
239*/
240pub fn password_reset<S: SymKeyGen, H: PwHash>(
241	new_pw: &str,
242	decrypted_private_key: &impl Sk,
243	decrypted_sign_key: &impl SignK,
244) -> Result<ResetPasswordOutput<H::CRV, H::HAK>, Error>
245{
246	//1. create a new master key (because the old key was encrypted by the forgotten password and can't be restored)
247	let master_key = S::generate()?;
248
249	//2. encrypt the private and the sign key with the new master key
250	let encrypted_private_key = decrypted_private_key.encrypt_by_master_key(&master_key)?;
251	let encrypted_sign_key = decrypted_sign_key.encrypt_by_master_key(&master_key)?;
252
253	//3. encrypt the new master key with the new password
254	let (client_random_value, hashed_authentication_key_bytes, encrypted_master_key, encrypted_master_key_alg) =
255		H::derived_keys_from_password(new_pw.as_bytes(), &master_key, None)?;
256
257	Ok(ResetPasswordOutput {
258		master_key_alg: master_key.get_alg_str(),
259		derived_alg: client_random_value.get_alg_str(),
260		client_random_value,
261		hashed_authentication_key_bytes,
262		encrypted_master_key,
263		encrypted_master_key_alg,
264		encrypted_private_key,
265		encrypted_sign_key,
266	})
267}
268
269/**
270Creates a safety number in byte of a given verify key and additional user information like the user id or username.
271
272## combined number
273
274To create a combination of two identities set for user_2 another SafetyNumberUser struct.
275Make sure to keep the order of user_1 and user_2 on the other user too, otherwise the number will not be the same.
276 */
277pub fn safety_number<Vk: VerifyK>(
278	user_1_verify_key: &Vk,
279	user_1_user_info: &str,
280	user_2_verify_key: Option<&Vk>,
281	user_2_user_info: Option<&str>,
282) -> Vec<u8>
283{
284	let mut hasher = Sha256::new();
285
286	user_1_verify_key.create_hash(&mut hasher);
287
288	hasher.update(user_1_user_info.as_bytes());
289
290	if let (Some(u_2), Some(u_2_i)) = (user_2_verify_key, user_2_user_info) {
291		u_2.create_hash(&mut hasher);
292
293		hasher.update(u_2_i.as_bytes());
294	}
295
296	let number_bytes = hasher.finalize();
297
298	number_bytes.to_vec()
299}