make keychain records append-only

This commit is contained in:
Eric Wendland 2026-07-18 20:25:45 +02:00
commit 9e5871d02a
3 changed files with 95 additions and 14 deletions

View file

@ -1145,7 +1145,7 @@ impl Store {
pub fn insert_keychain_op(&self, op: &StoredKeychainOp) -> Result<(), StoreError> {
self.conn.execute(
r#"INSERT OR REPLACE INTO keychain_ops(op_id, op_json, created_at_ms)
r#"INSERT OR IGNORE INTO keychain_ops(op_id, op_json, created_at_ms)
VALUES (?1, ?2, ?3)"#,
params![op.op_id, op.op_json, op.created_at_ms],
)?;
@ -1196,7 +1196,7 @@ impl Store {
signature: &StoredKeychainSignature,
) -> Result<(), StoreError> {
self.conn.execute(
r#"INSERT OR REPLACE INTO keychain_signatures(
r#"INSERT OR IGNORE INTO keychain_signatures(
op_id, signer, signer_public_key, namespace, signature, created_at_ms
)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)"#,
@ -1265,13 +1265,13 @@ impl Store {
signatures: &[StoredKeychainSignature],
) -> Result<(), StoreError> {
tx.execute(
r#"INSERT OR REPLACE INTO keychain_ops(op_id, op_json, created_at_ms)
r#"INSERT OR IGNORE INTO keychain_ops(op_id, op_json, created_at_ms)
VALUES (?1, ?2, ?3)"#,
params![op.op_id, op.op_json, op.created_at_ms],
)?;
for signature in signatures {
tx.execute(
r#"INSERT OR REPLACE INTO keychain_signatures(
r#"INSERT OR IGNORE INTO keychain_signatures(
op_id, signer, signer_public_key, namespace, signature, created_at_ms
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"#,
params![
@ -2331,6 +2331,54 @@ mod tests {
);
}
#[test]
fn keychain_records_are_append_only_by_operation_and_signature_identity() {
let store = Store::open_memory().expect("open");
let original = StoredKeychainOp {
op_id: "op:keychain:immutable".to_owned(),
op_json: r#"{"id":"op:keychain:immutable","name":"original"}"#.to_owned(),
created_at_ms: 1,
};
let replacement = StoredKeychainOp {
op_id: original.op_id.clone(),
op_json: r#"{"id":"op:keychain:immutable","name":"replacement"}"#.to_owned(),
created_at_ms: 2,
};
let original_signature = StoredKeychainSignature {
op_id: original.op_id.clone(),
signer: "key:admin".to_owned(),
signer_public_key: "ssh-ed25519 AAAA original".to_owned(),
namespace: "geth.keychain.v1@geth.local".to_owned(),
signature: b"original-signature".to_vec(),
created_at_ms: 1,
};
let replacement_signature = StoredKeychainSignature {
signer_public_key: "ssh-ed25519 AAAA replacement".to_owned(),
signature: b"replacement-signature".to_vec(),
created_at_ms: 2,
..original_signature.clone()
};
store
.insert_keychain_op_with_signatures(
&original,
std::slice::from_ref(&original_signature),
)
.expect("insert original");
store
.insert_keychain_op_with_signatures(
&replacement,
std::slice::from_ref(&replacement_signature),
)
.expect("attempt replacement");
assert_eq!(store.list_keychain_ops().expect("list ops"), vec![original]);
assert_eq!(
store.list_keychain_signatures().expect("list signatures"),
vec![original_signature]
);
}
#[test]
fn keychain_ops_with_signatures_batch_commits_multiple_ops() {
let store = Store::open_memory().expect("open");