use alloc::string::{String, ToString};
use base64ct::{Base64, Encoding};
use sentc_crypto_common::user::{
ChangePasswordData,
DoneLoginServerInput,
DoneLoginServerKeysOutput,
DoneLoginServerOutput,
DoneLoginServerReturn,
JwtRefreshInput,
OtpInput,
PrepareLoginSaltServerOutput,
PrepareLoginServerInput,
UserPublicKeyData,
UserUpdateServerInput,
UserVerifyKeyData,
VerifyLoginInput,
};
use sentc_crypto_common::{DeviceId, UserId};
use sentc_crypto_core::cryptomat::{DeriveMasterKeyForAuth, PwHash, Sk};
use serde::{Deserialize, Serialize};
use crate::cryptomat::{PkWrapper, SignComposerWrapper, SignKWrapper, SkWrapper, StaticKeyComposerWrapper, VerifyKWrapper};
use crate::error::SdkUtilError;
use crate::{client_random_value_to_string, derive_auth_key_for_auth_to_string, handle_server_response, hashed_authentication_key_to_string};
pub struct DeviceKeyDataInt<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper>
{
pub private_key: Sk,
pub sign_key: SiK,
pub public_key: Pk,
pub verify_key: Vk,
pub exported_public_key: UserPublicKeyData,
pub exported_verify_key: UserVerifyKeyData,
}
#[derive(Serialize, Deserialize)]
pub struct DeviceKeyDataExport
{
pub private_key: String, pub public_key: String,
pub sign_key: String,
pub verify_key: String,
pub exported_public_key: String,
pub exported_verify_key: String,
}
impl<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper> TryFrom<DeviceKeyDataInt<Sk, Pk, SiK, Vk>> for DeviceKeyDataExport
{
type Error = SdkUtilError;
fn try_from(value: DeviceKeyDataInt<Sk, Pk, SiK, Vk>) -> Result<Self, Self::Error>
{
Ok(Self {
private_key: value.private_key.to_string()?,
public_key: value.public_key.to_string()?,
sign_key: value.sign_key.to_string()?,
verify_key: value.verify_key.to_string()?,
exported_public_key: value
.exported_public_key
.to_string()
.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
exported_verify_key: value
.exported_verify_key
.to_string()
.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
})
}
}
pub struct UserPreVerifyLogin<Sk: SkWrapper, Pk: PkWrapper, SiK: SignKWrapper, Vk: VerifyKWrapper>
{
pub challenge: String,
pub device_keys: DeviceKeyDataInt<Sk, Pk, SiK, Vk>,
pub user_id: UserId,
pub device_id: DeviceId,
}
fn decrypt_login_challenge(private_key: &impl SkWrapper, challenge: &str) -> Result<String, SdkUtilError>
{
let challenge = Base64::decode_vec(challenge).map_err(|_| SdkUtilError::DecryptingLoginChallengeFailed)?;
let decrypted = private_key.get_key().decrypt(&challenge)?;
String::from_utf8(decrypted).map_err(|_| SdkUtilError::DecryptingLoginChallengeFailed)
}
pub fn prepare_login_start(user_identifier: &str) -> Result<String, SdkUtilError>
{
PrepareLoginServerInput {
user_identifier: user_identifier.to_string(),
}
.to_string()
.map_err(|_| SdkUtilError::JsonToStringFailed)
}
pub fn prepare_login<H: PwHash>(user_identifier: &str, password: &str, server_output: &str) -> Result<(String, String, H::DMK), SdkUtilError>
{
let server_output: PrepareLoginSaltServerOutput = handle_server_response(server_output)?;
let salt = Base64::decode_vec(server_output.salt_string.as_str()).map_err(|_| SdkUtilError::DecodeSaltFailed)?;
let result = sentc_crypto_core::user::prepare_login::<H>(password, &salt, server_output.derived_encryption_key_alg.as_str())?;
let auth_key = derive_auth_key_for_auth_to_string(&result.auth_key);
let input = DoneLoginServerInput {
auth_key: auth_key.clone(),
device_identifier: user_identifier.to_string(),
}
.to_string()
.map_err(|_| SdkUtilError::JsonToStringFailed)?;
Ok((input, auth_key, result.master_key_encryption_key))
}
pub fn check_done_login(server_output: &str) -> Result<DoneLoginServerReturn, SdkUtilError>
{
let server_output: DoneLoginServerReturn = handle_server_response(server_output)?;
Ok(server_output)
}
pub fn prepare_validate_mfa(auth_key: String, device_identifier: String, token: String) -> Result<String, SdkUtilError>
{
serde_json::to_string(&OtpInput {
token,
auth_key,
device_identifier,
})
.map_err(|_| SdkUtilError::JsonToStringFailed)
}
pub fn done_validate_mfa<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
master_key_encryption: &impl DeriveMasterKeyForAuth,
auth_key: String,
device_identifier: String,
server_output: &str,
) -> Result<
UserPreVerifyLogin<
<SkC as StaticKeyComposerWrapper>::SkWrapper,
<SkC as StaticKeyComposerWrapper>::PkWrapper,
<SiKC as SignComposerWrapper>::SignKWrapper,
<SiKC as SignComposerWrapper>::VerifyKWrapper,
>,
SdkUtilError,
>
{
let server_output: DoneLoginServerOutput = handle_server_response(server_output)?;
done_login::<SkC, SiKC>(master_key_encryption, auth_key, device_identifier, server_output)
}
pub fn done_login<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
master_key_encryption: &impl DeriveMasterKeyForAuth,
auth_key: String,
device_identifier: String,
server_output: DoneLoginServerOutput,
) -> Result<
UserPreVerifyLogin<
<SkC as StaticKeyComposerWrapper>::SkWrapper,
<SkC as StaticKeyComposerWrapper>::PkWrapper,
<SiKC as SignComposerWrapper>::SignKWrapper,
<SiKC as SignComposerWrapper>::VerifyKWrapper,
>,
SdkUtilError,
>
{
let device_data = server_output.device_keys;
let device_keys = done_login_internally_with_device_out::<SkC, SiKC>(master_key_encryption, &device_data)?;
let challenge = decrypt_login_challenge(&device_keys.private_key, &server_output.challenge)?;
Ok(UserPreVerifyLogin {
device_keys,
challenge: serde_json::to_string(&VerifyLoginInput {
auth_key,
device_identifier,
challenge,
})
.map_err(|_e| SdkUtilError::JsonToStringFailed)?,
user_id: device_data.user_id,
device_id: device_data.device_id,
})
}
fn done_login_internally_with_device_out<SkC: StaticKeyComposerWrapper, SiKC: SignComposerWrapper>(
master_key_encryption: &impl DeriveMasterKeyForAuth,
server_output: &DoneLoginServerKeysOutput,
) -> Result<
DeviceKeyDataInt<
<SkC as StaticKeyComposerWrapper>::SkWrapper,
<SkC as StaticKeyComposerWrapper>::PkWrapper,
<SiKC as SignComposerWrapper>::SignKWrapper,
<SiKC as SignComposerWrapper>::VerifyKWrapper,
>,
SdkUtilError,
>
{
let encrypted_master_key = Base64::decode_vec(server_output.encrypted_master_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
let encrypted_private_key = Base64::decode_vec(server_output.encrypted_private_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
let encrypted_sign_key = Base64::decode_vec(server_output.encrypted_sign_key.as_str()).map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
let out = sentc_crypto_core::user::done_login::<SkC::Composer, SiKC::Composer>(
master_key_encryption,
&encrypted_master_key,
&encrypted_private_key,
server_output.keypair_encrypt_alg.as_str(),
&encrypted_sign_key,
server_output.keypair_sign_alg.as_str(),
)?;
let public_key = SkC::pk_from_pem(
&server_output.public_key_string,
&server_output.keypair_encrypt_alg,
server_output.keypair_encrypt_id.clone(),
)?;
let verify_key = SiKC::vk_from_pem(
&server_output.verify_key_string,
&server_output.keypair_sign_alg,
server_output.keypair_sign_id.clone(),
)?;
let private_key = SkC::sk_from_inner(out.private_key, server_output.keypair_encrypt_id.clone());
let sign_key = SiKC::sk_from_inner(out.sign_key, server_output.keypair_sign_id.clone());
let exported_public_key = UserPublicKeyData {
public_key_pem: server_output.public_key_string.to_string(),
public_key_alg: server_output.keypair_encrypt_alg.to_string(),
public_key_id: server_output.keypair_encrypt_id.clone(),
public_key_sig: None, public_key_sig_key_id: None,
};
let exported_verify_key = UserVerifyKeyData {
verify_key_pem: server_output.verify_key_string.to_string(),
verify_key_alg: server_output.keypair_sign_alg.to_string(),
verify_key_id: server_output.keypair_sign_id.clone(),
};
Ok(DeviceKeyDataInt {
private_key,
sign_key,
public_key,
verify_key,
exported_public_key,
exported_verify_key,
})
}
pub fn change_password<H: PwHash>(
old_pw: &str,
new_pw: &str,
server_output_prep_login: &str,
server_output_done_login: DoneLoginServerOutput,
) -> Result<String, SdkUtilError>
{
let server_output_prep_login: PrepareLoginSaltServerOutput = handle_server_response(server_output_prep_login)?;
let encrypted_master_key = Base64::decode_vec(
server_output_done_login
.device_keys
.encrypted_master_key
.as_str(),
)
.map_err(|_| SdkUtilError::DerivedKeyWrongFormat)?;
let old_salt = Base64::decode_vec(server_output_prep_login.salt_string.as_str()).map_err(|_| SdkUtilError::DecodeSaltFailed)?;
let output = sentc_crypto_core::user::change_password::<H>(
old_pw,
new_pw,
&old_salt,
&encrypted_master_key,
server_output_prep_login.derived_encryption_key_alg.as_str(),
)?;
let new_encrypted_master_key = Base64::encode_string(&output.encrypted_master_key);
let new_client_random_value = client_random_value_to_string(&output.client_random_value);
let new_hashed_authentication_key = hashed_authentication_key_to_string(&output.hashed_authentication_key_bytes);
let old_auth_key = derive_auth_key_for_auth_to_string(&output.old_auth_key);
let pw_change_out = ChangePasswordData {
new_derived_alg: output.derived_alg.to_string(),
new_encrypted_master_key,
new_client_random_value,
new_hashed_authentication_key,
new_encrypted_master_key_alg: output.encrypted_master_key_alg.to_string(),
old_auth_key,
};
pw_change_out
.to_string()
.map_err(|_| SdkUtilError::JsonToStringFailed)
}
pub fn prepare_refresh_jwt(refresh_token: String) -> Result<String, SdkUtilError>
{
JwtRefreshInput {
refresh_token,
}
.to_string()
.map_err(|_| SdkUtilError::JsonToStringFailed)
}
pub fn prepare_user_identifier_update(user_identifier: String) -> Result<String, SdkUtilError>
{
let input = UserUpdateServerInput {
user_identifier,
};
input
.to_string()
.map_err(|_| SdkUtilError::JsonToStringFailed)
}