Skip to content

Commit 81ef371

Browse files
test: test credential deduplication
1 parent e7c33a9 commit 81ef371

File tree

2 files changed

+151
-2
lines changed

2 files changed

+151
-2
lines changed

keystore/src/connection/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ pub mod platform {
1414
mod generic;
1515
pub use self::generic::SqlCipherConnection as KeystoreDatabaseConnection;
1616
pub use self::generic::TransactionWrapper;
17+
#[cfg(test)]
18+
pub(crate) use generic::MigrationTarget;
19+
20+
1721
}
1822
}
1923
}
@@ -229,6 +233,22 @@ impl Database {
229233
})
230234
}
231235

236+
#[cfg(all(test, not(target_family = "wasm")))]
237+
pub(crate) async fn open_at_schema_version(
238+
name: &str,
239+
key: &DatabaseKey,
240+
version: MigrationTarget,
241+
) -> CryptoKeystoreResult<Self> {
242+
let conn = KeystoreDatabaseConnection::init_with_key_at_schema_version(name, key, version)?;
243+
let conn = Mutex::new(Some(conn));
244+
let conn = Arc::new(conn);
245+
Ok(Self {
246+
conn,
247+
transaction: Default::default(),
248+
transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
249+
})
250+
}
251+
232252
/// Get direct exclusive access to the connection.
233253
pub async fn conn(&self) -> CryptoKeystoreResult<ConnectionGuard<'_>> {
234254
self.conn.lock().await.try_into()

keystore/src/connection/platform/generic/mod.rs

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ unsafe impl Send for SqlCipherConnection {}
7979
unsafe impl Sync for SqlCipherConnection {}
8080

8181
#[derive(Default)]
82-
enum MigrationTarget {
82+
pub(crate) enum MigrationTarget {
8383
#[default]
8484
Latest,
8585
Version(u16),
@@ -114,6 +114,29 @@ impl SqlCipherConnection {
114114
Ok(conn)
115115
}
116116

117+
#[cfg(test)]
118+
pub(crate) fn init_with_key_at_schema_version(
119+
path: &str,
120+
key: &DatabaseKey,
121+
version: MigrationTarget,
122+
) -> CryptoKeystoreResult<Self> {
123+
let mut conn = rusqlite::Connection::open(path)?;
124+
125+
Self::set_key(&mut conn, key)?;
126+
127+
// Disable FOREIGN KEYs - The 2 step blob writing process invalidates foreign key checks unfortunately
128+
conn.pragma_update(None, "foreign_keys", "OFF")?;
129+
130+
Self::run_migrations(&mut conn, version)?;
131+
132+
let conn = Self {
133+
path: path.into(),
134+
conn: Mutex::new(conn),
135+
};
136+
137+
Ok(conn)
138+
}
139+
117140
#[cfg(feature = "log-queries")]
118141
fn log_query(event: TraceEvent) {
119142
if let TraceEvent::Stmt(_, sql) = event {
@@ -295,9 +318,14 @@ impl<'a> DatabaseConnection<'a> for SqlCipherConnection {
295318
mod migration_test {
296319
use std::io::Write;
297320

321+
use openmls::prelude::Ciphersuite;
298322
use tempfile::NamedTempFile;
299323

300-
use crate::{ConnectionType, Database, DatabaseKey};
324+
use crate::{
325+
ConnectionType, Database, DatabaseKey,
326+
connection::{FetchFromDatabase, MigrationTarget},
327+
entities::{EntityFindParams, StoredCredential},
328+
};
301329

302330
const DB: &[u8] = include_bytes!("../../../../../crypto-ffi/bindings/jvm/src/test/resources/db-v10002003.sqlite");
303331
const OLD_KEY: &str = "secret";
@@ -317,4 +345,105 @@ mod migration_test {
317345

318346
let _db = smol::block_on(Database::open(ConnectionType::Persistent(path), &new_key)).unwrap();
319347
}
348+
349+
#[test]
350+
fn deduplicating_credentials() {
351+
let mut db_file = NamedTempFile::new().unwrap();
352+
db_file.write_all(DB).unwrap();
353+
let path = db_file
354+
.path()
355+
.to_str()
356+
.expect("tmpfile path is representable in unicode");
357+
358+
let new_key = DatabaseKey::generate();
359+
smol::block_on(Database::migrate_db_key_type_to_bytes(path, OLD_KEY, &new_key)).unwrap();
360+
361+
smol::block_on(async {
362+
let db = Database::open_at_schema_version(path, &new_key, MigrationTarget::Version(18))
363+
.await
364+
.unwrap();
365+
366+
let conn_guard = db.conn().await.unwrap();
367+
let conn = conn_guard.conn.lock().await;
368+
let mut stmt = conn
369+
.prepare(&format!(
370+
"SELECT
371+
id,
372+
credential,
373+
unixepoch(created_at) AS created_at,
374+
ciphersuite,
375+
public_key,
376+
secret_key
377+
FROM {credential_table}",
378+
credential_table = "mls_credentials_new",
379+
))
380+
.expect("preparing statement");
381+
382+
let credential = stmt
383+
.query_one([], |row| {
384+
Ok(StoredCredential {
385+
id: row.get("id")?,
386+
credential: row.get("credential")?,
387+
created_at: row.get("created_at")?,
388+
ciphersuite: row.get("ciphersuite")?,
389+
public_key: row.get("public_key")?,
390+
secret_key: row.get("secret_key")?,
391+
})
392+
})
393+
.expect("credential from row");
394+
395+
// Ciphersuites need to be ambiguous w.r.t their signature scheme to be a relevant duplicate
396+
conn.execute(
397+
"UPDATE mls_credentials_new SET ciphersuite = ?1",
398+
[Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 as u16],
399+
)
400+
.expect("updating ciphersuite");
401+
402+
// Create a duplicate from this credential
403+
conn.execute(
404+
"INSERT INTO mls_credentials_new (
405+
id,
406+
credential,
407+
created_at,
408+
ciphersuite,
409+
public_key,
410+
secret_key
411+
)
412+
VALUES (?1, ?2, datetime(?3, 'unixepoch'), ?4, ?5, ?6)",
413+
(
414+
credential.id.clone(),
415+
credential.credential.clone(),
416+
credential.created_at,
417+
Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 as u16,
418+
credential.public_key.clone(),
419+
credential.secret_key.clone(),
420+
),
421+
)
422+
.expect("inserting duplicate");
423+
424+
let count = conn
425+
.query_row("SELECT COUNT(*) FROM mls_credentials_new", [], |row| {
426+
row.get::<_, i32>(0)
427+
})
428+
.unwrap();
429+
430+
assert_eq!(count, 2);
431+
432+
drop(stmt);
433+
drop(conn);
434+
drop(conn_guard);
435+
drop(db);
436+
437+
let db = Database::open(ConnectionType::Persistent(path), &new_key)
438+
.await
439+
.unwrap();
440+
let deduplicated_count = db
441+
.find_all::<StoredCredential>(EntityFindParams::default())
442+
.await
443+
.expect("deduplicated credentials")
444+
.len();
445+
446+
assert_eq!(deduplicated_count, 1);
447+
});
448+
}
320449
}

0 commit comments

Comments
 (0)