mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-20 05:39:52 -07:00
Add commands for importing and exporting room keys (#233)
This commit is contained in:
parent
b4e9c213e6
commit
2327658e8c
4 changed files with 116 additions and 3 deletions
14
docs/iamb.1
14
docs/iamb.1
|
@ -61,12 +61,22 @@ Log out of
|
||||||
View a list of joined rooms.
|
View a list of joined rooms.
|
||||||
.It Sy ":spaces"
|
.It Sy ":spaces"
|
||||||
View a list of joined spaces.
|
View a list of joined spaces.
|
||||||
.It Sy ":verify"
|
|
||||||
View a list of ongoing E2EE verifications.
|
|
||||||
.It Sy ":welcome"
|
.It Sy ":welcome"
|
||||||
View the startup Welcome window.
|
View the startup Welcome window.
|
||||||
.El
|
.El
|
||||||
|
|
||||||
|
.Sh "E2EE COMMANDS"
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Sy ":keys export [path] [passphrase]"
|
||||||
|
Export and encrypt keys to
|
||||||
|
.Pa path .
|
||||||
|
.It Sy ":keys import [path] [passphrase]"
|
||||||
|
Import and decrypt keys from
|
||||||
|
.Pa path .
|
||||||
|
.It Sy ":verify"
|
||||||
|
View a list of ongoing E2EE verifications.
|
||||||
|
.El
|
||||||
|
|
||||||
.Sh "MESSAGE COMMANDS"
|
.Sh "MESSAGE COMMANDS"
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Sy ":download"
|
.It Sy ":download"
|
||||||
|
|
21
src/base.rs
21
src/base.rs
|
@ -420,6 +420,15 @@ pub enum HomeserverAction {
|
||||||
Logout(String, bool),
|
Logout(String, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An action performed against the user's room keys.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum KeysAction {
|
||||||
|
/// Export room keys to a file, encrypted with a passphrase.
|
||||||
|
Export(String, String),
|
||||||
|
/// Import room keys from a file, encrypted with a passphrase.
|
||||||
|
Import(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
/// An action that the main program loop should.
|
/// An action that the main program loop should.
|
||||||
///
|
///
|
||||||
/// See [the commands module][super::commands] for where these are usually created.
|
/// See [the commands module][super::commands] for where these are usually created.
|
||||||
|
@ -428,6 +437,9 @@ pub enum IambAction {
|
||||||
/// Perform an action against the homeserver.
|
/// Perform an action against the homeserver.
|
||||||
Homeserver(HomeserverAction),
|
Homeserver(HomeserverAction),
|
||||||
|
|
||||||
|
/// Perform an action over room keys.
|
||||||
|
Keys(KeysAction),
|
||||||
|
|
||||||
/// Perform an action on the currently selected message.
|
/// Perform an action on the currently selected message.
|
||||||
Message(MessageAction),
|
Message(MessageAction),
|
||||||
|
|
||||||
|
@ -485,6 +497,7 @@ impl ApplicationAction for IambAction {
|
||||||
fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
|
fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Break,
|
IambAction::Homeserver(..) => SequenceStatus::Break,
|
||||||
|
IambAction::Keys(..) => SequenceStatus::Break,
|
||||||
IambAction::Message(..) => SequenceStatus::Break,
|
IambAction::Message(..) => SequenceStatus::Break,
|
||||||
IambAction::Room(..) => SequenceStatus::Break,
|
IambAction::Room(..) => SequenceStatus::Break,
|
||||||
IambAction::OpenLink(..) => SequenceStatus::Break,
|
IambAction::OpenLink(..) => SequenceStatus::Break,
|
||||||
|
@ -498,6 +511,7 @@ impl ApplicationAction for IambAction {
|
||||||
fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
|
fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Atom,
|
IambAction::Homeserver(..) => SequenceStatus::Atom,
|
||||||
|
IambAction::Keys(..) => SequenceStatus::Atom,
|
||||||
IambAction::Message(..) => SequenceStatus::Atom,
|
IambAction::Message(..) => SequenceStatus::Atom,
|
||||||
IambAction::OpenLink(..) => SequenceStatus::Atom,
|
IambAction::OpenLink(..) => SequenceStatus::Atom,
|
||||||
IambAction::Room(..) => SequenceStatus::Atom,
|
IambAction::Room(..) => SequenceStatus::Atom,
|
||||||
|
@ -511,6 +525,7 @@ impl ApplicationAction for IambAction {
|
||||||
fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
|
fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Ignore,
|
IambAction::Homeserver(..) => SequenceStatus::Ignore,
|
||||||
|
IambAction::Keys(..) => SequenceStatus::Ignore,
|
||||||
IambAction::Message(..) => SequenceStatus::Ignore,
|
IambAction::Message(..) => SequenceStatus::Ignore,
|
||||||
IambAction::Room(..) => SequenceStatus::Ignore,
|
IambAction::Room(..) => SequenceStatus::Ignore,
|
||||||
IambAction::OpenLink(..) => SequenceStatus::Ignore,
|
IambAction::OpenLink(..) => SequenceStatus::Ignore,
|
||||||
|
@ -526,6 +541,7 @@ impl ApplicationAction for IambAction {
|
||||||
IambAction::Homeserver(..) => false,
|
IambAction::Homeserver(..) => false,
|
||||||
IambAction::Message(..) => false,
|
IambAction::Message(..) => false,
|
||||||
IambAction::Room(..) => false,
|
IambAction::Room(..) => false,
|
||||||
|
IambAction::Keys(..) => false,
|
||||||
IambAction::Send(..) => false,
|
IambAction::Send(..) => false,
|
||||||
IambAction::OpenLink(..) => false,
|
IambAction::OpenLink(..) => false,
|
||||||
IambAction::ToggleScrollbackFocus => false,
|
IambAction::ToggleScrollbackFocus => false,
|
||||||
|
@ -585,6 +601,9 @@ pub enum IambError {
|
||||||
#[error("Cryptographic storage error: {0}")]
|
#[error("Cryptographic storage error: {0}")]
|
||||||
CryptoStore(#[from] matrix_sdk::encryption::CryptoStoreError),
|
CryptoStore(#[from] matrix_sdk::encryption::CryptoStoreError),
|
||||||
|
|
||||||
|
#[error("Failed to import room keys: {0}")]
|
||||||
|
FailedKeyImport(#[from] matrix_sdk::encryption::RoomKeyImportError),
|
||||||
|
|
||||||
/// A failure related to the cryptographic store.
|
/// A failure related to the cryptographic store.
|
||||||
#[error("Cannot export keys from sled: {0}")]
|
#[error("Cannot export keys from sled: {0}")]
|
||||||
UpgradeSled(#[from] crate::sled_export::SledMigrationError),
|
UpgradeSled(#[from] crate::sled_export::SledMigrationError),
|
||||||
|
@ -1767,7 +1786,7 @@ fn complete_cmdarg(
|
||||||
match cmd.name.as_str() {
|
match cmd.name.as_str() {
|
||||||
"cancel" | "dms" | "edit" | "redact" | "reply" => vec![],
|
"cancel" | "dms" | "edit" | "redact" | "reply" => vec![],
|
||||||
"members" | "rooms" | "spaces" | "welcome" => vec![],
|
"members" | "rooms" | "spaces" | "welcome" => vec![],
|
||||||
"download" | "open" | "upload" => complete_path(text, cursor),
|
"download" | "keys" | "open" | "upload" => complete_path(text, cursor),
|
||||||
"react" | "unreact" => complete_emoji(text, cursor, store),
|
"react" | "unreact" => complete_emoji(text, cursor, store),
|
||||||
|
|
||||||
"invite" => complete_users(text, cursor, store),
|
"invite" => complete_users(text, cursor, store),
|
||||||
|
|
|
@ -19,6 +19,7 @@ use crate::base::{
|
||||||
HomeserverAction,
|
HomeserverAction,
|
||||||
IambAction,
|
IambAction,
|
||||||
IambId,
|
IambId,
|
||||||
|
KeysAction,
|
||||||
MessageAction,
|
MessageAction,
|
||||||
ProgramCommand,
|
ProgramCommand,
|
||||||
ProgramCommands,
|
ProgramCommands,
|
||||||
|
@ -102,6 +103,29 @@ fn iamb_invite(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
return Ok(step);
|
return Ok(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iamb_keys(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
|
let mut args = desc.arg.strings()?;
|
||||||
|
|
||||||
|
if args.len() != 3 {
|
||||||
|
return Err(CommandError::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
let act = args.remove(0);
|
||||||
|
let path = args.remove(0);
|
||||||
|
let passphrase = args.remove(0);
|
||||||
|
|
||||||
|
let act = match act.as_str() {
|
||||||
|
"export" => KeysAction::Export(path, passphrase),
|
||||||
|
"import" => KeysAction::Import(path, passphrase),
|
||||||
|
_ => return Err(CommandError::InvalidArgument),
|
||||||
|
};
|
||||||
|
|
||||||
|
let vact = IambAction::Keys(act);
|
||||||
|
let step = CommandStep::Continue(vact.into(), ctx.context.clone());
|
||||||
|
|
||||||
|
return Ok(step);
|
||||||
|
}
|
||||||
|
|
||||||
fn iamb_verify(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
fn iamb_verify(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
let mut args = desc.arg.strings()?;
|
let mut args = desc.arg.strings()?;
|
||||||
|
|
||||||
|
@ -523,6 +547,7 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
|
||||||
f: iamb_invite,
|
f: iamb_invite,
|
||||||
});
|
});
|
||||||
cmds.add_command(ProgramCommand { name: "join".into(), aliases: vec![], f: iamb_join });
|
cmds.add_command(ProgramCommand { name: "join".into(), aliases: vec![], f: iamb_join });
|
||||||
|
cmds.add_command(ProgramCommand { name: "keys".into(), aliases: vec![], f: iamb_keys });
|
||||||
cmds.add_command(ProgramCommand {
|
cmds.add_command(ProgramCommand {
|
||||||
name: "leave".into(),
|
name: "leave".into(),
|
||||||
aliases: vec![],
|
aliases: vec![],
|
||||||
|
@ -959,4 +984,31 @@ mod tests {
|
||||||
let res = cmds.input_cmd("redact Removed Removed", ctx.clone());
|
let res = cmds.input_cmd("redact Removed Removed", ctx.clone());
|
||||||
assert_eq!(res, Err(CommandError::InvalidArgument));
|
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmd_keys() {
|
||||||
|
let mut cmds = setup_commands();
|
||||||
|
let ctx = EditContext::default();
|
||||||
|
|
||||||
|
let res = cmds.input_cmd("keys import /a/b/c pword", ctx.clone()).unwrap();
|
||||||
|
let act = IambAction::Keys(KeysAction::Import("/a/b/c".into(), "pword".into()));
|
||||||
|
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||||
|
|
||||||
|
let res = cmds.input_cmd("keys export /a/b/c pword", ctx.clone()).unwrap();
|
||||||
|
let act = IambAction::Keys(KeysAction::Export("/a/b/c".into(), "pword".into()));
|
||||||
|
assert_eq!(res, vec![(act.into(), ctx.clone())]);
|
||||||
|
|
||||||
|
// Invalid invocations.
|
||||||
|
let res = cmds.input_cmd("keys", ctx.clone());
|
||||||
|
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||||
|
|
||||||
|
let res = cmds.input_cmd("keys import", ctx.clone());
|
||||||
|
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||||
|
|
||||||
|
let res = cmds.input_cmd("keys import foo", ctx.clone());
|
||||||
|
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||||
|
|
||||||
|
let res = cmds.input_cmd("keys import foo bar baz", ctx.clone());
|
||||||
|
assert_eq!(res, Err(CommandError::InvalidArgument));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -87,6 +87,7 @@ use crate::{
|
||||||
IambId,
|
IambId,
|
||||||
IambInfo,
|
IambInfo,
|
||||||
IambResult,
|
IambResult,
|
||||||
|
KeysAction,
|
||||||
ProgramAction,
|
ProgramAction,
|
||||||
ProgramContext,
|
ProgramContext,
|
||||||
ProgramStore,
|
ProgramStore,
|
||||||
|
@ -529,6 +530,7 @@ impl Application {
|
||||||
|
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
IambAction::Keys(act) => self.keys_command(act, ctx, store).await?,
|
||||||
IambAction::Message(act) => {
|
IambAction::Message(act) => {
|
||||||
self.screen.current_window_mut()?.message_command(act, ctx, store).await?
|
self.screen.current_window_mut()?.message_command(act, ctx, store).await?
|
||||||
},
|
},
|
||||||
|
@ -603,6 +605,36 @@ impl Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn keys_command(
|
||||||
|
&mut self,
|
||||||
|
action: KeysAction,
|
||||||
|
_: ProgramContext,
|
||||||
|
store: &mut ProgramStore,
|
||||||
|
) -> IambResult<EditInfo> {
|
||||||
|
let encryption = store.application.worker.client.encryption();
|
||||||
|
|
||||||
|
match action {
|
||||||
|
KeysAction::Export(path, passphrase) => {
|
||||||
|
encryption
|
||||||
|
.export_room_keys(path.into(), &passphrase, |_| true)
|
||||||
|
.await
|
||||||
|
.map_err(IambError::from)?;
|
||||||
|
|
||||||
|
Ok(Some("Successfully exported room keys".into()))
|
||||||
|
},
|
||||||
|
KeysAction::Import(path, passphrase) => {
|
||||||
|
let res = encryption
|
||||||
|
.import_room_keys(path.into(), &passphrase)
|
||||||
|
.await
|
||||||
|
.map_err(IambError::from)?;
|
||||||
|
|
||||||
|
let msg = format!("Imported {} of {} keys", res.imported_count, res.total_count);
|
||||||
|
|
||||||
|
Ok(Some(msg.into()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_info(&mut self, info: InfoMessage) {
|
fn handle_info(&mut self, info: InfoMessage) {
|
||||||
match info {
|
match info {
|
||||||
InfoMessage::Message(info) => {
|
InfoMessage::Message(info) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue