Skip to content

Commit f88326d

Browse files
committed
refactor(sql): recreate keypairs table
Removed unused `addr` and `created` field. `is_default` boolean flag is moved into `config` row pointing to the current default key.
1 parent 025e0b5 commit f88326d

File tree

5 files changed

+73
-55
lines changed

5 files changed

+73
-55
lines changed

src/config.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ pub enum Config {
343343
/// until `chat_id.accept()` is called.
344344
#[strum(props(default = "0"))]
345345
VerifiedOneOnOneChats,
346+
347+
/// Row ID of the key in the `keypairs` table
348+
/// used for signatures, encryption to self and included in `Autocrypt` header.
349+
KeyId,
346350
}
347351

348352
impl Config {
@@ -627,8 +631,6 @@ impl Context {
627631
///
628632
/// This should only be used by test code and during configure.
629633
pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> {
630-
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
631-
632634
// add old primary address (if exists) to secondary addresses
633635
let mut secondary_addrs = self.get_all_self_addrs().await?;
634636
// never store a primary address also as a secondary
@@ -642,17 +644,6 @@ impl Context {
642644
self.set_config(Config::ConfiguredAddr, Some(primary_new))
643645
.await?;
644646

645-
if let Some(old_addr) = old_addr {
646-
let old_addr = EmailAddress::new(&old_addr)?;
647-
let old_keypair = crate::key::load_keypair(self, &old_addr).await?;
648-
649-
if let Some(mut old_keypair) = old_keypair {
650-
old_keypair.addr = EmailAddress::new(primary_new)?;
651-
crate::key::store_self_keypair(self, &old_keypair, crate::key::KeyPairUse::Default)
652-
.await?;
653-
}
654-
}
655-
656647
Ok(())
657648
}
658649

src/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ mod tests {
13181318
"socks5_port",
13191319
"socks5_user",
13201320
"socks5_password",
1321+
"key_id",
13211322
];
13221323
let t = TestContext::new().await;
13231324
let info = t.get_info().await.unwrap();

src/imex.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> {
670670
let keys = context
671671
.sql
672672
.query_map(
673-
"SELECT id, public_key, private_key, is_default FROM keypairs;",
673+
"SELECT id, public_key, private_key, id=(SELECT value FROM config WHERE keyname='key_id') FROM keypairs;",
674674
(),
675675
|row| {
676676
let id = row.get(0)?;

src/key.rs

+37-41
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::constants::KeyGenType;
1818
use crate::context::Context;
1919
use crate::log::LogExt;
2020
use crate::pgp::KeyPair;
21-
use crate::tools::{time, EmailAddress};
21+
use crate::tools::EmailAddress;
2222

2323
/// Convenience trait for working with keys.
2424
///
@@ -82,10 +82,9 @@ pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPubl
8282
match context
8383
.sql
8484
.query_row_optional(
85-
r#"SELECT public_key
86-
FROM keypairs
87-
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
88-
AND is_default=1"#,
85+
"SELECT public_key
86+
FROM keypairs
87+
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
8988
(),
9089
|row| {
9190
let bytes: Vec<u8> = row.get(0)?;
@@ -106,10 +105,9 @@ pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecr
106105
match context
107106
.sql
108107
.query_row_optional(
109-
r#"SELECT private_key
110-
FROM keypairs
111-
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
112-
AND is_default=1"#,
108+
"SELECT private_key
109+
FROM keypairs
110+
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
113111
(),
114112
|row| {
115113
let bytes: Vec<u8> = row.get(0)?;
@@ -132,8 +130,7 @@ pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result<Vec<Si
132130
.query_map(
133131
r#"SELECT private_key
134132
FROM keypairs
135-
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
136-
ORDER BY is_default DESC"#,
133+
ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
137134
(),
138135
|row| row.get::<_, Vec<u8>>(0),
139136
|keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
@@ -233,13 +230,10 @@ pub(crate) async fn load_keypair(
233230
let res = context
234231
.sql
235232
.query_row_optional(
236-
r#"
237-
SELECT public_key, private_key
238-
FROM keypairs
239-
WHERE addr=?1
240-
AND is_default=1;
241-
"#,
242-
(addr,),
233+
"SELECT public_key, private_key
234+
FROM keypairs
235+
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
236+
(),
243237
|row| {
244238
let pub_bytes: Vec<u8> = row.get(0)?;
245239
let sec_bytes: Vec<u8> = row.get(1)?;
@@ -288,42 +282,44 @@ pub async fn store_self_keypair(
288282
keypair: &KeyPair,
289283
default: KeyPairUse,
290284
) -> Result<()> {
291-
context
285+
let mut config_cache_lock = context.sql.config_cache.write().await;
286+
let new_key_id = context
292287
.sql
293288
.transaction(|transaction| {
294289
let public_key = DcKey::to_bytes(&keypair.public);
295290
let secret_key = DcKey::to_bytes(&keypair.secret);
296-
transaction
297-
.execute(
298-
"DELETE FROM keypairs WHERE public_key=? OR private_key=?;",
299-
(&public_key, &secret_key),
300-
)
301-
.context("failed to remove old use of key")?;
302-
if default == KeyPairUse::Default {
303-
transaction
304-
.execute("UPDATE keypairs SET is_default=0;", ())
305-
.context("failed to clear default")?;
306-
}
291+
307292
let is_default = match default {
308-
KeyPairUse::Default => i32::from(true),
309-
KeyPairUse::ReadOnly => i32::from(false),
293+
KeyPairUse::Default => true,
294+
KeyPairUse::ReadOnly => false,
310295
};
311296

312-
let addr = keypair.addr.to_string();
313-
let t = time();
314-
315297
transaction
316298
.execute(
317-
"INSERT INTO keypairs (addr, is_default, public_key, private_key, created)
318-
VALUES (?,?,?,?,?);",
319-
(addr, is_default, &public_key, &secret_key, t),
299+
"INSERT OR REPLACE INTO keypairs (public_key, private_key)
300+
VALUES (?,?)",
301+
(&public_key, &secret_key),
320302
)
321-
.context("failed to insert keypair")?;
322-
323-
Ok(())
303+
.context("Failed to insert keypair")?;
304+
305+
if is_default {
306+
let new_key_id = transaction.last_insert_rowid();
307+
transaction.execute(
308+
"INSERT OR REPLACE INTO config (keyname, value) VALUES ('key_id', ?)",
309+
(new_key_id,),
310+
)?;
311+
Ok(Some(new_key_id))
312+
} else {
313+
Ok(None)
314+
}
324315
})
325316
.await?;
326317

318+
if let Some(new_key_id) = new_key_id {
319+
// Update config cache if transaction succeeded and changed current default key.
320+
config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string()));
321+
}
322+
327323
Ok(())
328324
}
329325

src/sql/migrations.rs

+30
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,36 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
804804
.await?;
805805
}
806806

807+
if dbversion < 107 {
808+
sql.execute_migration(
809+
"CREATE TABLE new_keypairs (
810+
id INTEGER PRIMARY KEY AUTOINCREMENT,
811+
private_key UNIQUE NOT NULL,
812+
public_key UNIQUE NOT NULL
813+
);
814+
INSERT OR IGNORE INTO new_keypairs SELECT id, private_key, public_key FROM keypairs;
815+
816+
INSERT OR IGNORE
817+
INTO config (keyname, value)
818+
VALUES
819+
('key_id', (SELECT id FROM new_keypairs
820+
WHERE private_key=
821+
(SELECT private_key FROM keypairs
822+
WHERE addr=(SELECT value FROM config WHERE keyname='configured_addr')
823+
AND is_default=1)));
824+
825+
-- We do not drop the old `keypairs` table for now,
826+
-- but move it to `old_keypairs`. We can remove it later
827+
-- in next migrations. This may be needed for recovery
828+
-- in case something is wrong with the migration.
829+
ALTER TABLE keypairs RENAME TO old_keypairs;
830+
ALTER TABLE new_keypairs RENAME TO keypairs;
831+
",
832+
107,
833+
)
834+
.await?;
835+
}
836+
807837
let new_version = sql
808838
.get_raw_config_int(VERSION_CFG)
809839
.await?

0 commit comments

Comments
 (0)