Skip to content

Commit 9a7a819

Browse files
committed
refactor: migrate to spec-compliant KERI SAID algorithm: replace empty-string placeholder with 44-char # placeholder, fix field-ordering for absent d fields under preserve_order, and update all call sites across the workspace
1 parent 252dddb commit 9a7a819

29 files changed

Lines changed: 1946 additions & 272 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/auths-core/src/witness/server.rs

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,10 @@ impl WitnessServerState {
226226
sig: vec![],
227227
};
228228

229-
let payload_for_said = receipt
230-
.signing_payload()
229+
let receipt_value = serde_json::to_value(&receipt)
230+
.map_err(|e| WitnessError::Serialization(e.to_string()))?;
231+
receipt.d = crate::crypto::said::compute_said(&receipt_value)
231232
.map_err(|e| WitnessError::Serialization(e.to_string()))?;
232-
receipt.d = crate::crypto::said::compute_said(&payload_for_said);
233233

234234
let signing_payload = receipt
235235
.signing_payload()
@@ -314,17 +314,10 @@ fn verify_event_said(event: &serde_json::Value) -> Result<(), String> {
314314
.and_then(|v| v.as_str())
315315
.ok_or("missing 'd' (SAID) field")?;
316316

317-
let mut zeroed = event.clone();
318-
zeroed
319-
.as_object_mut()
320-
.ok_or("event must be a JSON object")?
321-
.insert("d".to_string(), serde_json::Value::String(String::new()));
317+
let computed = crate::crypto::said::compute_said(event)
318+
.map_err(|e| format!("failed to compute SAID: {}", e))?;
322319

323-
let canonical =
324-
serde_json::to_vec(&zeroed).map_err(|e| format!("failed to serialize event: {}", e))?;
325-
let computed = crate::crypto::said::compute_said(&canonical);
326-
327-
if computed != claimed_d {
320+
if computed.as_str() != claimed_d {
328321
return Err(format!(
329322
"SAID mismatch: claimed {} but computed {}",
330323
claimed_d, computed
@@ -678,13 +671,9 @@ mod tests {
678671
let sig = kp.sign(&payload);
679672
event["x"] = serde_json::Value::String(hex::encode(sig.as_ref()));
680673

681-
// Compute SAID (with empty d, but final x)
682-
let mut for_said = event.clone();
683-
for_said["d"] = serde_json::Value::String(String::new());
684-
let said_payload = serde_json::to_vec(&for_said).unwrap();
685-
event["d"] = serde_json::Value::String(
686-
crate::crypto::said::compute_said(&said_payload).into_inner(),
687-
);
674+
// Compute SAID (x is already set; compute_said ignores x and injects d placeholder)
675+
let said = crate::crypto::said::compute_said(&event).unwrap();
676+
event["d"] = serde_json::Value::String(said.into_inner());
688677

689678
event
690679
}
@@ -794,10 +783,8 @@ mod tests {
794783
"x": "not_valid_hex!!!"
795784
});
796785
// Set proper SAID for the event as-is
797-
let said_payload = serde_json::to_vec(&event).unwrap();
798-
event["d"] = serde_json::Value::String(
799-
crate::crypto::said::compute_said(&said_payload).into_inner(),
800-
);
786+
let said = crate::crypto::said::compute_said(&event).unwrap();
787+
event["d"] = serde_json::Value::String(said.into_inner());
801788

802789
let response = app
803790
.oneshot(
@@ -839,13 +826,9 @@ mod tests {
839826
let sig = wrong_kp.sign(&payload);
840827
event["x"] = serde_json::Value::String(hex::encode(sig.as_ref()));
841828

842-
// Compute SAID
843-
let mut for_said = event.clone();
844-
for_said["d"] = serde_json::Value::String(String::new());
845-
let said_payload = serde_json::to_vec(&for_said).unwrap();
846-
event["d"] = serde_json::Value::String(
847-
crate::crypto::said::compute_said(&said_payload).into_inner(),
848-
);
829+
// Compute SAID (x is already set; compute_said ignores x and injects d placeholder)
830+
let said = crate::crypto::said::compute_said(&event).unwrap();
831+
event["d"] = serde_json::Value::String(said.into_inner());
849832

850833
let response = app
851834
.oneshot(

crates/auths-core/tests/cases/witness.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async fn start_test_server() -> (SocketAddr, WitnessServerState) {
2929

3030
/// Build a valid KERI inception event with proper SAID and self-signature.
3131
/// Returns (event_json_bytes, computed_said).
32-
fn make_test_event(prefix: &str, seq: u64) -> (Vec<u8>, String) {
32+
fn make_test_event(prefix: &str, seq: u64) -> (Vec<u8>, auths_keri::Said) {
3333
let rng = ring::rand::SystemRandom::new();
3434
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
3535
let kp = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref()).unwrap();
@@ -51,12 +51,9 @@ fn make_test_event(prefix: &str, seq: u64) -> (Vec<u8>, String) {
5151
let sig = kp.sign(&payload);
5252
event["x"] = serde_json::Value::String(hex::encode(sig.as_ref()));
5353

54-
// Compute SAID (with empty d, but final x)
55-
let mut for_said = event.clone();
56-
for_said["d"] = serde_json::Value::String(String::new());
57-
let said_payload = serde_json::to_vec(&for_said).unwrap();
58-
let said = auths_core::crypto::said::compute_said(&said_payload);
59-
event["d"] = serde_json::Value::String(said.clone());
54+
// Compute SAID (x is already set; compute_said ignores x and injects d placeholder)
55+
let said = auths_core::crypto::said::compute_said(&event).unwrap();
56+
event["d"] = serde_json::Value::String(said.as_str().to_string());
6057

6158
(serde_json::to_vec(&event).unwrap(), said)
6259
}

crates/auths-id/src/identity/rotate.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ use std::sync::Arc;
2828
use crate::storage::layout::StorageLayoutConfig;
2929
use crate::storage::registry::RegistryBackend;
3030
use crate::witness_config::WitnessConfig;
31-
use auths_core::crypto::said::{compute_next_commitment, compute_said, verify_commitment};
31+
use auths_core::crypto::said::{compute_next_commitment, verify_commitment};
3232
use auths_core::crypto::signer::{decrypt_keypair, encrypt_keypair};
3333
use auths_core::signing::PassphraseProvider;
3434
use auths_core::storage::keychain::{IdentityDID, KeyAlias, KeyRole, KeyStorage};
35+
use auths_keri::compute_said;
3536

3637
/// Result of a rotation operation with keychain-specific info.
3738
pub struct RotationKeyInfo {
@@ -262,9 +263,10 @@ pub fn rotate_registry_identity(
262263
x: String::new(),
263264
};
264265

265-
let rot_json = serde_json::to_vec(&Event::Rot(rot.clone()))
266+
let rot_value = serde_json::to_value(Event::Rot(rot.clone()))
266267
.map_err(|e| InitError::Keri(format!("Serialization failed: {}", e)))?;
267-
rot.d = compute_said(&rot_json);
268+
rot.d = compute_said(&rot_value)
269+
.map_err(|e| InitError::Keri(format!("SAID computation failed: {}", e)))?;
268270

269271
let canonical = serialize_for_signing(&Event::Rot(rot.clone()))
270272
.map_err(|e| InitError::Keri(e.to_string()))?;

crates/auths-id/src/keri/anchor.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
77
use git2::Repository;
88
use ring::signature::Ed25519KeyPair;
99

10-
use auths_core::crypto::said::compute_said;
10+
use auths_keri::compute_said;
1111

1212
use super::event::KeriSequence;
1313
use super::seal::SealType;
@@ -105,9 +105,10 @@ pub fn anchor_data<T: serde::Serialize>(
105105
let state = validate_kel(&events)?;
106106

107107
// Compute data digest
108-
let data_json =
109-
serde_json::to_vec(data).map_err(|e| AnchorError::Serialization(e.to_string()))?;
110-
let data_digest = compute_said(&data_json);
108+
let data_value =
109+
serde_json::to_value(data).map_err(|e| AnchorError::Serialization(e.to_string()))?;
110+
let data_digest =
111+
compute_said(&data_value).map_err(|e| AnchorError::Serialization(e.to_string()))?;
111112

112113
// Create seal
113114
let seal = Seal::new(data_digest, seal_type);
@@ -125,9 +126,9 @@ pub fn anchor_data<T: serde::Serialize>(
125126
};
126127

127128
// Compute SAID
128-
let ixn_json = serde_json::to_vec(&Event::Ixn(ixn.clone()))
129+
let ixn_value = serde_json::to_value(Event::Ixn(ixn.clone()))
129130
.map_err(|e| AnchorError::Serialization(e.to_string()))?;
130-
ixn.d = compute_said(&ixn_json);
131+
ixn.d = compute_said(&ixn_value).map_err(|e| AnchorError::Serialization(e.to_string()))?;
131132

132133
// Sign the event with the current key
133134
let canonical = super::serialize_for_signing(&Event::Ixn(ixn.clone()))?;
@@ -231,9 +232,10 @@ pub fn verify_anchor<T: serde::Serialize>(
231232
data: &T,
232233
) -> Result<AnchorVerification, AnchorError> {
233234
// Compute data digest
234-
let data_json =
235-
serde_json::to_vec(data).map_err(|e| AnchorError::Serialization(e.to_string()))?;
236-
let data_digest = compute_said(&data_json);
235+
let data_value =
236+
serde_json::to_value(data).map_err(|e| AnchorError::Serialization(e.to_string()))?;
237+
let data_digest =
238+
compute_said(&data_value).map_err(|e| AnchorError::Serialization(e.to_string()))?;
237239

238240
verify_anchor_by_digest(repo, prefix, data_digest.as_str())
239241
}
@@ -412,8 +414,8 @@ mod tests {
412414
.unwrap();
413415

414416
// Compute the digest we're looking for
415-
let att_json = serde_json::to_vec(&attestation).unwrap();
416-
let att_digest = compute_said(&att_json);
417+
let att_value = serde_json::to_value(&attestation).unwrap();
418+
let att_digest = compute_said(&att_value).unwrap();
417419

418420
let found = find_anchor_event(&repo, &init.prefix, att_digest.as_str()).unwrap();
419421
assert!(found.is_some());

crates/auths-id/src/keri/rotation.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use git2::Repository;
1313
use ring::rand::SystemRandom;
1414
use ring::signature::{Ed25519KeyPair, KeyPair};
1515

16-
use auths_core::crypto::said::{compute_next_commitment, compute_said, verify_commitment};
16+
use auths_core::crypto::said::{compute_next_commitment, verify_commitment};
17+
use auths_keri::compute_said;
1718

1819
use super::event::KeriSequence;
1920
use super::types::{Prefix, Said};
@@ -207,9 +208,9 @@ pub fn rotate_keys(
207208
};
208209

209210
// Compute SAID
210-
let rot_json = serde_json::to_vec(&Event::Rot(rot.clone()))
211+
let rot_value = serde_json::to_value(Event::Rot(rot.clone()))
211212
.map_err(|e| RotationError::Serialization(e.to_string()))?;
212-
rot.d = compute_said(&rot_json);
213+
rot.d = compute_said(&rot_value).map_err(|e| RotationError::Serialization(e.to_string()))?;
213214

214215
// Sign with the new current key (next_keypair is now the active key)
215216
let canonical = super::serialize_for_signing(&Event::Rot(rot.clone()))?;
@@ -319,9 +320,9 @@ pub fn abandon_identity(
319320
};
320321

321322
// Compute SAID
322-
let rot_json = serde_json::to_vec(&Event::Rot(rot.clone()))
323+
let rot_value = serde_json::to_value(Event::Rot(rot.clone()))
323324
.map_err(|e| RotationError::Serialization(e.to_string()))?;
324-
rot.d = compute_said(&rot_json);
325+
rot.d = compute_said(&rot_value).map_err(|e| RotationError::Serialization(e.to_string()))?;
325326

326327
// Sign with the new current key (next_keypair is now the active key)
327328
let canonical = super::serialize_for_signing(&Event::Rot(rot.clone()))?;
@@ -408,9 +409,9 @@ pub fn rotate_keys_with_backend(
408409
x: String::new(),
409410
};
410411

411-
let rot_json = serde_json::to_vec(&Event::Rot(rot.clone()))
412+
let rot_value = serde_json::to_value(Event::Rot(rot.clone()))
412413
.map_err(|e| RotationError::Serialization(e.to_string()))?;
413-
rot.d = compute_said(&rot_json);
414+
rot.d = compute_said(&rot_value).map_err(|e| RotationError::Serialization(e.to_string()))?;
414415

415416
let canonical = super::serialize_for_signing(&Event::Rot(rot.clone()))
416417
.map_err(|e| RotationError::Serialization(e.to_string()))?;

crates/auths-id/tests/cases/keri.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ fn verify_anchor_by_digest_works() {
332332
.unwrap();
333333

334334
// Compute digest
335-
let att_json = serde_json::to_vec(&attestation).unwrap();
336-
let digest = compute_said(&att_json);
335+
let att_value = serde_json::to_value(&attestation).unwrap();
336+
let digest = compute_said(&att_value).unwrap();
337337

338338
// Verify by digest
339339
let verification = verify_anchor_by_digest(&repo, &init.prefix, digest.as_str()).unwrap();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Seeds for failure cases proptest has generated in the past. It is
2+
# automatically read and these particular cases re-run before any
3+
# novel cases are generated.
4+
#
5+
# It is recommended to check this file in to source control so that
6+
# everyone who runs the test benefits from these saved cases.
7+
cc 324d6750120f8135b6e66f4af19922d87ddf081902ce35bfbb0051af2d245dc8 # shrinks to ixn_count = 1

crates/auths-id/tests/cases/proptest_keri.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ fn make_signed_ixn(
6262
x: String::new(),
6363
};
6464

65-
let json = serde_json::to_vec(&Event::Ixn(ixn.clone())).unwrap();
66-
ixn.d = compute_said(&json);
65+
let value = serde_json::to_value(Event::Ixn(ixn.clone())).unwrap();
66+
ixn.d = compute_said(&value).unwrap();
6767
ixn.x = sign_event(&Event::Ixn(ixn.clone()), kp);
6868
ixn
6969
}
@@ -91,8 +91,8 @@ fn make_signed_rot(
9191
x: String::new(),
9292
};
9393

94-
let json = serde_json::to_vec(&Event::Rot(rot.clone())).unwrap();
95-
rot.d = compute_said(&json);
94+
let value = serde_json::to_value(Event::Rot(rot.clone())).unwrap();
95+
rot.d = compute_said(&value).unwrap();
9696
rot.x = sign_event(&Event::Rot(rot.clone()), new_kp);
9797
rot
9898
}

crates/auths-infra-http/tests/cases/witness.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ fn make_test_event(prefix: &str, seq: u64) -> (Vec<u8>, Said) {
4646
let sig = kp.sign(&payload);
4747
event["x"] = serde_json::Value::String(hex::encode(sig.as_ref()));
4848

49-
let mut for_said = event.clone();
50-
for_said["d"] = serde_json::Value::String(String::new());
51-
let said_payload = serde_json::to_vec(&for_said).unwrap();
52-
let said = auths_core::crypto::said::compute_said(&said_payload);
53-
event["d"] = serde_json::Value::String(said.to_string());
49+
// Compute SAID (x is already set; compute_said ignores x and injects d placeholder)
50+
let said = auths_core::crypto::said::compute_said(&event).unwrap();
51+
event["d"] = serde_json::Value::String(said.as_str().to_string());
5452

5553
(serde_json::to_vec(&event).unwrap(), said)
5654
}

0 commit comments

Comments
 (0)