Skip to content

Commit

Permalink
state: add test for format stability
Browse files Browse the repository at this point in the history
This should have been added before the matrix sdk upgrade, but it is
extra work to update the test for old serializing format too.
That part was tested manually
  • Loading branch information
martinetd committed Sep 8, 2024
1 parent 0f38c13 commit 6f31bb7
Showing 1 changed file with 67 additions and 15 deletions.
82 changes: 67 additions & 15 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ base64_serde_type!(Base64, base64::engine::general_purpose::STANDARD);
use crate::args::args;

/// data we want to keep around
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Session {
pub homeserver: String,
pub matrix_session: SerializedMatrixSession,
}

/// matrix-rust-sdk's "Session" struct as we used to serialize it
/// as of matrix-rust-sdk commit 0b9c082e11955f49f99acd21542f62b40f11c418
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct SerializedMatrixSession {
/// The access token used for this session.
pub access_token: String,
Expand Down Expand Up @@ -54,11 +54,13 @@ struct Blob {

/// try to decrypt session and return it
fn check_pass(session_file: PathBuf, pass: &str) -> Result<Session> {
let blob = {
let content = fs::read(session_file).context("Could not read user session file")?;
serde_json::from_slice::<Blob>(&content)
.context("Could not deserialize session file content.")?
};
let blob_text = fs::read(session_file).context("Could not read user session file")?;
decrypt_blob(pass, &blob_text)
}

fn decrypt_blob(pass: &str, blob_text: &[u8]) -> Result<Session> {
let blob = serde_json::from_slice::<Blob>(blob_text)
.context("Could not deserialize session file content.")?;
if blob.version != "argon2+chacha20poly1305" {
return Err(Error::msg(
"This version only supports argon2+chacha20poly1305",
Expand All @@ -79,13 +81,7 @@ fn check_pass(session_file: PathBuf, pass: &str) -> Result<Session> {
Ok(session)
}

/// encrypt session and store it
pub fn create_user(
nick: &str,
pass: &str,
homeserver: &str,
auth_session: AuthSession,
) -> Result<()> {
fn encrypt_blob(pass: &str, homeserver: &str, auth_session: AuthSession) -> Result<Vec<u8>> {
let session_meta = auth_session.meta();
let session = Session {
homeserver: homeserver.into(),
Expand Down Expand Up @@ -118,6 +114,17 @@ pub fn create_user(
salt,
nonce,
};
serde_json::to_vec(&blob).context("could not serialize blob")
}

/// encrypt session and store it
pub fn create_user(
nick: &str,
pass: &str,
homeserver: &str,
auth_session: AuthSession,
) -> Result<()> {
let blob_text = encrypt_blob(pass, homeserver, auth_session)?;

let user_dir = Path::new(&args().state_dir).join(nick);
if !user_dir.is_dir() {
Expand All @@ -133,7 +140,7 @@ pub fn create_user(
.create_new(true)
.open(user_dir.join("session"))
.context("creating user session file failed")?;
file.write_all(&serde_json::to_vec(&blob).context("could not serialize blob")?)
file.write_all(&blob_text)
.context("Writing to user session file failed")?;
Ok(())
}
Expand All @@ -150,3 +157,48 @@ pub fn login(nick: &str, pass: &str) -> Result<Option<Session>> {
Err(Error::msg(format!("unknown user {}", nick)))
}
}

#[cfg(test)]
mod tests {
use super::*;
use matrix_sdk::{
matrix_auth::{MatrixSession, MatrixSessionTokens},
SessionMeta,
};

/// ensure on disk format is stable
#[test]
fn check_state_storage() -> Result<()> {
//{"homeserver":"https://matrix.codewreck.org","matrix_session":{"access_token":"syt_dGVzdDI_MsvRmWOsfnSDZMCycFUK_3UNGcT","user_id":"@test2:codewreck.org","device_id":"MSPYQMJBVG"}}
let session = AuthSession::Matrix(MatrixSession {
meta: SessionMeta {
user_id: "@test:domain.tld".try_into()?,
device_id: "ABCDEFGHIJ".try_into()?,
},
tokens: MatrixSessionTokens {
access_token: "abc_abcdefg_abcdefgh_abcdef".into(),
refresh_token: None,
},
});
// can serialize/encrypt
let blob_string = &encrypt_blob("pass", "domain.tld", session)?;

// can decrypt what we just encrypted
let session = decrypt_blob("pass", &blob_string)?;
assert_eq!(session.homeserver, "domain.tld");
assert_eq!(session.matrix_session.user_id, "@test:domain.tld");
assert_eq!(session.matrix_session.device_id, "ABCDEFGHIJ");
assert_eq!(
session.matrix_session.access_token,
"abc_abcdefg_abcdefgh_abcdef"
);
assert!(session.matrix_session.refresh_token.is_none());

// can decrypt something we encrypted ages ago (format stability check)
let old_blob = r#"{"version":"argon2+chacha20poly1305","ciphertext":"jTMm0N+nAl9jTD6sdppn+9w5B93QpGzng7YNyR+oDcFdHs3EEAUYKKBPTQlkJovthypQ+eDSrS9Vd9WJAdsa9NqGgyx+XoijMPL4LG+K88CnlKE/0GbNbGLH4r1QqGif5aimVJOmgI5rTgRAb+ZhfEGx5nmk1CNmCW5nCzLmWfdvjHJssMJt4JJFN82hJoVn2RHNwFY3q+MQ08E0zTvG1CA=","salt":"c9fUuFFl0Q1bzaBKAyvOcy+x1alIJ2mr/eZow4ut+58=","nonce":"QgY2eb3OGc7VCzw76t4b9kSPWx4pmZCG"}"#;
let old_session = decrypt_blob("pass", old_blob.as_bytes())?;
assert_eq!(session, old_session);

Ok(())
}
}

0 comments on commit 6f31bb7

Please sign in to comment.