mirror of
https://github.com/chatmail/core.git
synced 2026-04-28 19:06:35 +03:00
Add API to change database passphrase
New API is: - dc_context_is_encrypted() - dc_context_change_passphrase()
This commit is contained in:
@@ -146,6 +146,12 @@ impl Context {
|
||||
self.sql.is_open().await
|
||||
}
|
||||
|
||||
/// Returns true if database is encrypted. Returns false if database is not open yet or not
|
||||
/// encrypted.
|
||||
pub async fn is_encrypted(&self) -> bool {
|
||||
self.sql.is_encrypted().await.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Tests the database passphrase.
|
||||
///
|
||||
/// Returns true if passphrase is correct.
|
||||
@@ -155,6 +161,14 @@ impl Context {
|
||||
self.sql.check_passphrase(passphrase).await
|
||||
}
|
||||
|
||||
/// Changes the database passphrase.
|
||||
///
|
||||
/// Works only for encrypted databases. Encrypted database can only be converted to unencrypted
|
||||
/// one and backwards via import/export.
|
||||
pub async fn change_passphrase(&self, passphrase: String) -> Result<()> {
|
||||
self.sql.change_passphrase(self, passphrase).await
|
||||
}
|
||||
|
||||
pub(crate) async fn with_blobdir(
|
||||
dbfile: PathBuf,
|
||||
blobdir: PathBuf,
|
||||
@@ -1069,4 +1083,34 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_change_passphrase() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
|
||||
let id = 1;
|
||||
let context = Context::new_closed(dbfile.clone().into(), id)
|
||||
.await
|
||||
.context("failed to create context")?;
|
||||
assert_eq!(context.open("foo".to_string()).await?, true);
|
||||
assert_eq!(context.is_open().await, true);
|
||||
assert_eq!(context.is_encrypted().await, true);
|
||||
|
||||
context.change_passphrase("bar".to_string()).await?;
|
||||
drop(context);
|
||||
|
||||
let id = 2;
|
||||
let context = Context::new(dbfile.into(), id)
|
||||
.await
|
||||
.context("failed to create context")?;
|
||||
assert_eq!(context.is_open().await, false);
|
||||
assert_eq!(context.check_passphrase("foo".to_string()).await?, false);
|
||||
assert_eq!(context.check_passphrase("bar".to_string()).await?, true);
|
||||
assert_eq!(context.open("foo".to_string()).await?, false);
|
||||
assert_eq!(context.open("bar".to_string()).await?, true);
|
||||
assert_eq!(context.is_encrypted().await, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
34
src/sql.rs
34
src/sql.rs
@@ -97,6 +97,40 @@ impl Sql {
|
||||
*self.is_encrypted.read().await
|
||||
}
|
||||
|
||||
/// Changes the database passpharse.
|
||||
///
|
||||
/// The database must be open and encrypted already.
|
||||
pub(crate) async fn change_passphrase(
|
||||
&self,
|
||||
context: &Context,
|
||||
passphrase: String,
|
||||
) -> Result<()> {
|
||||
// Take the whole pool so nobody opens another connection in parallel.
|
||||
let pool = self
|
||||
.pool
|
||||
.write()
|
||||
.await
|
||||
.take()
|
||||
.context("the database must be open before rekeying")?;
|
||||
|
||||
// Get one connection and rekey the database.
|
||||
// All other connections will stop working after that.
|
||||
let connection = pool
|
||||
.get()
|
||||
.context("failed to get connection from the pool")?;
|
||||
connection
|
||||
.pragma_update(None, "rekey", &passphrase)
|
||||
.context("failed to set PRAGMA rekey")?;
|
||||
drop(pool);
|
||||
|
||||
// Reopen the database with new passphrase.
|
||||
self.open(context, passphrase)
|
||||
.await
|
||||
.context("failed to reopen the database after rekeying")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Closes all underlying Sqlite connections.
|
||||
async fn close(&self) {
|
||||
let _ = self.pool.write().await.take();
|
||||
|
||||
Reference in New Issue
Block a user