diff --git a/src/config.rs b/src/config.rs index d0fe13925..c2bcd42fc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,7 +18,7 @@ use crate::log::LogExt; use crate::mimefactory::RECOMMENDED_FILE_SIZE; use crate::provider::{get_provider_by_id, Provider}; use crate::sync::{self, Sync::*, SyncData}; -use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress}; +use crate::tools::{get_abs_path, improve_single_line_input}; /// The available configuration keys. #[derive( @@ -343,6 +343,10 @@ pub enum Config { /// until `chat_id.accept()` is called. #[strum(props(default = "0"))] VerifiedOneOnOneChats, + + /// Row ID of the key in the `keypairs` table + /// used for signatures, encryption to self and included in `Autocrypt` header. + KeyId, } impl Config { @@ -627,8 +631,6 @@ impl Context { /// /// This should only be used by test code and during configure. pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> { - let old_addr = self.get_config(Config::ConfiguredAddr).await?; - // add old primary address (if exists) to secondary addresses let mut secondary_addrs = self.get_all_self_addrs().await?; // never store a primary address also as a secondary @@ -642,17 +644,6 @@ impl Context { self.set_config(Config::ConfiguredAddr, Some(primary_new)) .await?; - if let Some(old_addr) = old_addr { - let old_addr = EmailAddress::new(&old_addr)?; - let old_keypair = crate::key::load_keypair(self, &old_addr).await?; - - if let Some(mut old_keypair) = old_keypair { - old_keypair.addr = EmailAddress::new(primary_new)?; - crate::key::store_self_keypair(self, &old_keypair, crate::key::KeyPairUse::Default) - .await?; - } - } - Ok(()) } diff --git a/src/context.rs b/src/context.rs index 1ca193d90..57ecff7a3 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1318,6 +1318,7 @@ mod tests { "socks5_port", "socks5_user", "socks5_password", + "key_id", ]; let t = TestContext::new().await; let info = t.get_info().await.unwrap(); diff --git a/src/imex.rs b/src/imex.rs index 16f9231f3..6fa69ad55 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -670,7 +670,7 @@ async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> { let keys = context .sql .query_map( - "SELECT id, public_key, private_key, is_default FROM keypairs;", + "SELECT id, public_key, private_key, id=(SELECT value FROM config WHERE keyname='key_id') FROM keypairs;", (), |row| { let id = row.get(0)?; diff --git a/src/key.rs b/src/key.rs index 365726a37..c5d50f853 100644 --- a/src/key.rs +++ b/src/key.rs @@ -18,7 +18,7 @@ use crate::constants::KeyGenType; use crate::context::Context; use crate::log::LogExt; use crate::pgp::KeyPair; -use crate::tools::{time, EmailAddress}; +use crate::tools::EmailAddress; /// Convenience trait for working with keys. /// @@ -82,10 +82,9 @@ pub(crate) async fn load_self_public_key(context: &Context) -> Result = row.get(0)?; @@ -106,10 +105,9 @@ pub(crate) async fn load_self_secret_key(context: &Context) -> Result = row.get(0)?; @@ -132,8 +130,7 @@ pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result>(0), |keys| keys.collect::, _>>().map_err(Into::into), @@ -233,13 +230,10 @@ pub(crate) async fn load_keypair( let res = context .sql .query_row_optional( - r#" - SELECT public_key, private_key - FROM keypairs - WHERE addr=?1 - AND is_default=1; - "#, - (addr,), + "SELECT public_key, private_key + FROM keypairs + WHERE id=(SELECT value FROM config WHERE keyname='key_id')", + (), |row| { let pub_bytes: Vec = row.get(0)?; let sec_bytes: Vec = row.get(1)?; @@ -288,42 +282,44 @@ pub async fn store_self_keypair( keypair: &KeyPair, default: KeyPairUse, ) -> Result<()> { - context + let mut config_cache_lock = context.sql.config_cache.write().await; + let new_key_id = context .sql .transaction(|transaction| { let public_key = DcKey::to_bytes(&keypair.public); let secret_key = DcKey::to_bytes(&keypair.secret); - transaction - .execute( - "DELETE FROM keypairs WHERE public_key=? OR private_key=?;", - (&public_key, &secret_key), - ) - .context("failed to remove old use of key")?; - if default == KeyPairUse::Default { - transaction - .execute("UPDATE keypairs SET is_default=0;", ()) - .context("failed to clear default")?; - } + let is_default = match default { - KeyPairUse::Default => i32::from(true), - KeyPairUse::ReadOnly => i32::from(false), + KeyPairUse::Default => true, + KeyPairUse::ReadOnly => false, }; - let addr = keypair.addr.to_string(); - let t = time(); - transaction .execute( - "INSERT INTO keypairs (addr, is_default, public_key, private_key, created) - VALUES (?,?,?,?,?);", - (addr, is_default, &public_key, &secret_key, t), + "INSERT OR REPLACE INTO keypairs (public_key, private_key) + VALUES (?,?)", + (&public_key, &secret_key), ) - .context("failed to insert keypair")?; + .context("Failed to insert keypair")?; - Ok(()) + if is_default { + let new_key_id = transaction.last_insert_rowid(); + transaction.execute( + "INSERT OR REPLACE INTO config (keyname, value) VALUES ('key_id', ?)", + (new_key_id,), + )?; + Ok(Some(new_key_id)) + } else { + Ok(None) + } }) .await?; + if let Some(new_key_id) = new_key_id { + // Update config cache if transaction succeeded and changed current default key. + config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string())); + } + Ok(()) } diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index d0cf51b5b..c0ab7074c 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -804,6 +804,36 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid); .await?; } + if dbversion < 107 { + sql.execute_migration( + "CREATE TABLE new_keypairs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + private_key UNIQUE NOT NULL, + public_key UNIQUE NOT NULL + ); + INSERT OR IGNORE INTO new_keypairs SELECT id, private_key, public_key FROM keypairs; + + INSERT OR IGNORE + INTO config (keyname, value) + VALUES + ('key_id', (SELECT id FROM new_keypairs + WHERE private_key= + (SELECT private_key FROM keypairs + WHERE addr=(SELECT value FROM config WHERE keyname='configured_addr') + AND is_default=1))); + + -- We do not drop the old `keypairs` table for now, + -- but move it to `old_keypairs`. We can remove it later + -- in next migrations. This may be needed for recovery + -- in case something is wrong with the migration. + ALTER TABLE keypairs RENAME TO old_keypairs; + ALTER TABLE new_keypairs RENAME TO keypairs; + ", + 107, + ) + .await?; + } + let new_version = sql .get_raw_config_int(VERSION_CFG) .await?