Update OPAQUE credential profile handling
This commit is contained in:
parent
8aa2890ed4
commit
8ad56b938d
17 changed files with 2113 additions and 973 deletions
168
omikron-connector/src/password/protected.rs
Normal file
168
omikron-connector/src/password/protected.rs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
use iota_storage::{users::user_manager, util::protected_replay};
|
||||
use mtp::{
|
||||
codec::{
|
||||
CommunicationType, CommunicationValue, DataValue, ProtectedMessageBuilder,
|
||||
ProtectedOpenOptions, ProtectionPolicy, ReplayError, ReplayGuard, VerifiedProtectedMessage,
|
||||
open_protected_with_checked,
|
||||
},
|
||||
crypto::{DualSigner, PublicKeyBundle},
|
||||
};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
|
||||
use super::{
|
||||
OmikronConnection,
|
||||
app_protection::{PASSWORD_MANAGEMENT_ENCRYPTION, PASSWORD_MANAGEMENT_SIGNATURE, purpose},
|
||||
wire::now_millis,
|
||||
};
|
||||
|
||||
struct CommandReplayGuard;
|
||||
|
||||
impl ReplayGuard for CommandReplayGuard {
|
||||
fn accept(
|
||||
&mut self,
|
||||
signer_id: u64,
|
||||
message_id: mtp::common::MessageId,
|
||||
created_at: u64,
|
||||
) -> Result<bool, ReplayError> {
|
||||
let now = now_millis();
|
||||
if created_at < now.saturating_sub(7 * 24 * 60 * 60 * 1000)
|
||||
|| created_at > now.saturating_add(5 * 60 * 1000)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
let signer =
|
||||
i64::try_from(signer_id).map_err(|error| ReplayError::Store(error.to_string()))?;
|
||||
let time =
|
||||
i64::try_from(created_at).map_err(|error| ReplayError::Store(error.to_string()))?;
|
||||
protected_replay::accept(signer, &message_id.to_string(), time)
|
||||
.map_err(|error| ReplayError::Store(error.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl OmikronConnection {
|
||||
pub(super) async fn open_password_command(
|
||||
&self,
|
||||
frame: &CommunicationValue,
|
||||
kind: CommunicationType,
|
||||
) -> Result<VerifiedProtectedMessage, String> {
|
||||
let iota_id = iota_storage::util::config_util::CONFIG
|
||||
.load()
|
||||
.iota_id
|
||||
.ok_or("Iota has no identity")?;
|
||||
let keyring = self
|
||||
.keyring
|
||||
.read()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or("Iota has no keyring")?;
|
||||
let mut replay = CommandReplayGuard;
|
||||
let opened = open_protected_with_checked(
|
||||
frame,
|
||||
&[keyring.as_ref()],
|
||||
None,
|
||||
|signer_id| {
|
||||
let id = i64::try_from(signer_id).ok()?;
|
||||
let profile = user_manager::get_user(id).ok()??;
|
||||
Some(vec![
|
||||
PublicKeyBundle::from_base64(&profile.public_key).ok()?,
|
||||
])
|
||||
},
|
||||
ProtectedOpenOptions::new(
|
||||
Some(iota_id),
|
||||
purpose(PASSWORD_MANAGEMENT_SIGNATURE),
|
||||
purpose(PASSWORD_MANAGEMENT_ENCRYPTION),
|
||||
ProtectionPolicy::dual(),
|
||||
),
|
||||
&mut replay,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
if opened.message_type() != kind {
|
||||
return Err("wrong protected command type".into());
|
||||
}
|
||||
Ok(opened)
|
||||
}
|
||||
|
||||
pub(super) async fn protected_response(
|
||||
&self,
|
||||
frame: &CommunicationValue,
|
||||
kind: CommunicationType,
|
||||
user_id: i64,
|
||||
content: DataValue,
|
||||
signature: u8,
|
||||
encryption: u8,
|
||||
) -> Result<CommunicationValue, String> {
|
||||
let iota_id = iota_storage::util::config_util::CONFIG
|
||||
.load()
|
||||
.iota_id
|
||||
.ok_or("Iota has no identity")?;
|
||||
let keyring = self
|
||||
.keyring
|
||||
.read()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or("Iota has no keyring")?;
|
||||
let recipient = PublicKeyBundle::from_base64(
|
||||
&user_manager::get_user(user_id)
|
||||
.map_err(|error| error.to_string())?
|
||||
.ok_or("account not hosted")?
|
||||
.public_key,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
self.build_protected(
|
||||
frame,
|
||||
kind,
|
||||
iota_id,
|
||||
user_id as u64,
|
||||
content,
|
||||
recipient,
|
||||
signature,
|
||||
encryption,
|
||||
&keyring,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn build_protected(
|
||||
&self,
|
||||
frame: &CommunicationValue,
|
||||
kind: CommunicationType,
|
||||
signer_id: u64,
|
||||
recipient_id: u64,
|
||||
content: DataValue,
|
||||
recipient: PublicKeyBundle,
|
||||
signature: u8,
|
||||
encryption: u8,
|
||||
keyring: &mtp::crypto::Keyring,
|
||||
) -> Result<CommunicationValue, String> {
|
||||
let signer = DualSigner::new(
|
||||
&keyring.sig_cl_secret_key,
|
||||
&keyring.sig_pq_secret_key,
|
||||
&keyring.sig_pq_public_key,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
let mut rng = OsRng;
|
||||
let id = ((rng.next_u64() as u128) << 64) | rng.next_u64() as u128;
|
||||
ProtectedMessageBuilder::new(
|
||||
kind,
|
||||
content,
|
||||
signer_id,
|
||||
recipient_id,
|
||||
&signer,
|
||||
purpose(signature),
|
||||
purpose(encryption),
|
||||
)
|
||||
.message_id(id)
|
||||
.created_at(now_millis())
|
||||
.recipients(vec![recipient])
|
||||
.frame_id(frame.id().ok_or("missing request ID")?)
|
||||
.build()
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
pub(super) async fn password_error(&self, frame: &CommunicationValue, kind: CommunicationType) {
|
||||
let mut response = CommunicationValue::new(kind);
|
||||
if let Some(id) = frame.id() {
|
||||
response = response.with_id(id);
|
||||
}
|
||||
let _ = self.send_message(&response).await;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue