mirror of
https://github.com/chatmail/core.git
synced 2026-05-17 05:46:30 +03:00
api: add dc_context_change_passphrase()
This commit is contained in:
@@ -301,6 +301,19 @@ dc_context_t* dc_context_new_closed (const char* dbfile);
|
|||||||
int dc_context_open (dc_context_t *context, const char* passphrase);
|
int dc_context_open (dc_context_t *context, const char* passphrase);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the passphrase on the open database.
|
||||||
|
* Existing database must already be encrypted and the passphrase cannot be NULL or empty.
|
||||||
|
* It is impossible to encrypt unencrypted database with this method and vice versa.
|
||||||
|
*
|
||||||
|
* @memberof dc_context_t
|
||||||
|
* @param context The context object.
|
||||||
|
* @param passphrase The new passphrase.
|
||||||
|
* @return 1 on success, 0 on error.
|
||||||
|
*/
|
||||||
|
int dc_context_change_passphrase (dc_context_t* context, const char* passphrase);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns 1 if database is open.
|
* Returns 1 if database is open.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -167,6 +167,24 @@ pub unsafe extern "C" fn dc_context_open(
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_context_change_passphrase(
|
||||||
|
context: *mut dc_context_t,
|
||||||
|
passphrase: *const libc::c_char,
|
||||||
|
) -> libc::c_int {
|
||||||
|
if context.is_null() {
|
||||||
|
eprintln!("ignoring careless call to dc_context_change_passphrase()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = &*context;
|
||||||
|
let passphrase = to_string_lossy(passphrase);
|
||||||
|
block_on(ctx.change_passphrase(passphrase))
|
||||||
|
.context("dc_context_change_passphrase() failed")
|
||||||
|
.log_err(ctx)
|
||||||
|
.is_ok() as libc::c_int
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc::c_int {
|
pub unsafe extern "C" fn dc_context_is_open(context: *mut dc_context_t) -> libc::c_int {
|
||||||
if context.is_null() {
|
if context.is_null() {
|
||||||
|
|||||||
@@ -332,6 +332,12 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changes encrypted database passphrase.
|
||||||
|
pub async fn change_passphrase(&self, passphrase: String) -> Result<()> {
|
||||||
|
self.sql.change_passphrase(passphrase).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if database is open.
|
/// Returns true if database is open.
|
||||||
pub async fn is_open(&self) -> bool {
|
pub async fn is_open(&self) -> bool {
|
||||||
self.sql.is_open().await
|
self.sql.is_open().await
|
||||||
|
|||||||
57
src/sql.rs
57
src/sql.rs
@@ -304,6 +304,20 @@ impl Sql {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changes the passphrase of encrypted database.
|
||||||
|
///
|
||||||
|
/// The database must already be encrypted and the passphrase cannot be empty.
|
||||||
|
/// It is impossible to turn encrypted database into unencrypted
|
||||||
|
/// and vice versa this way, use import/export for this.
|
||||||
|
pub async fn change_passphrase(&self, passphrase: String) -> Result<()> {
|
||||||
|
self.call_write(move |conn| {
|
||||||
|
conn.pragma_update(None, "rekey", passphrase)
|
||||||
|
.context("failed to set PRAGMA rekey")?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Locks the write transactions mutex in order to make sure that there never are
|
/// Locks the write transactions mutex in order to make sure that there never are
|
||||||
/// multiple write transactions at once.
|
/// multiple write transactions at once.
|
||||||
///
|
///
|
||||||
@@ -1246,6 +1260,49 @@ mod tests {
|
|||||||
sql.open(&t, "foo".to_string())
|
sql.open(&t, "foo".to_string())
|
||||||
.await
|
.await
|
||||||
.context("failed to open the database second time")?;
|
.context("failed to open the database second time")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_change_passphrase() -> Result<()> {
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
// The context is used only for logging.
|
||||||
|
let t = TestContext::new().await;
|
||||||
|
|
||||||
|
// Create a separate empty database for testing.
|
||||||
|
let dir = tempdir()?;
|
||||||
|
let dbfile = dir.path().join("testdb.sqlite");
|
||||||
|
let sql = Sql::new(dbfile.clone());
|
||||||
|
|
||||||
|
sql.open(&t, "foo".to_string())
|
||||||
|
.await
|
||||||
|
.context("failed to open the database first time")?;
|
||||||
|
sql.close().await;
|
||||||
|
|
||||||
|
// Change the passphrase from "foo" to "bar".
|
||||||
|
let sql = Sql::new(dbfile.clone());
|
||||||
|
sql.open(&t, "foo".to_string())
|
||||||
|
.await
|
||||||
|
.context("failed to open the database second time")?;
|
||||||
|
sql.change_passphrase("bar".to_string())
|
||||||
|
.await
|
||||||
|
.context("failed to change passphrase")?;
|
||||||
|
sql.close().await;
|
||||||
|
|
||||||
|
let sql = Sql::new(dbfile);
|
||||||
|
|
||||||
|
// Test that old passphrase is not working.
|
||||||
|
assert!(sql.open(&t, "foo".to_string()).await.is_err());
|
||||||
|
|
||||||
|
// Open the database with the new passphrase.
|
||||||
|
sql.check_passphrase("bar".to_string()).await?;
|
||||||
|
sql.open(&t, "bar".to_string())
|
||||||
|
.await
|
||||||
|
.context("failed to open the database third time")?;
|
||||||
|
sql.close().await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user