use std::sync::Arc;
use crate::keri::inception::create_keri_identity_with_curve;
use git2::Repository;
use std::path::Path;
use crate::error::InitError;
use crate::keri::{
CesrKey, Event, IcpEvent, KeriSequence, Prefix, Said, Threshold, VersionString,
finalize_icp_event, serialize_for_signing,
};
use crate::storage::identity::IdentityStorage;
use crate::storage::registry::RegistryBackend;
use crate::witness_config::WitnessConfig;
use auths_core::{
crypto::said::compute_next_commitment,
crypto::signer::encrypt_keypair,
signing::PassphraseProvider,
storage::keychain::{IdentityDID, KeyAlias, KeyRole, KeyStorage},
};
#[allow(clippy::too_many_arguments)]
pub fn initialize_keri_identity(
repo_path: &Path,
local_key_alias: &KeyAlias,
metadata: Option<serde_json::Value>,
passphrase_provider: &dyn PassphraseProvider,
identity_storage: &dyn IdentityStorage,
keychain: &(dyn KeyStorage + Send + Sync),
now: chrono::DateTime<chrono::Utc>,
curve: auths_crypto::CurveType,
) -> Result<(IdentityDID, KeyAlias), InitError> {
let repo = Repository::open(repo_path)?;
let result = create_keri_identity_with_curve(&repo, None, now, curve)
.map_err(|e| InitError::Keri(e.to_string()))?;
#[allow(clippy::disallowed_methods)]
let controller_did = IdentityDID::new_unchecked(result.did());
let is_hardware_backend = keychain.is_hardware_backend();
if is_hardware_backend {
keychain.store_key(
local_key_alias,
&controller_did,
KeyRole::Primary,
&[], )?;
let next_alias = KeyAlias::new_unchecked(format!("{}--next-0", local_key_alias));
keychain.store_key(
&next_alias,
&controller_did,
KeyRole::NextRotation,
&[], )?;
} else {
let passphrase = passphrase_provider
.get_passphrase(&format!("Enter passphrase for key '{}':", local_key_alias))?;
let encrypted_current =
encrypt_keypair(result.current_keypair_pkcs8.as_ref(), &passphrase)?;
let encrypted_next = encrypt_keypair(result.next_keypair_pkcs8.as_ref(), &passphrase)?;
keychain.store_key(
local_key_alias,
&controller_did,
KeyRole::Primary,
&encrypted_current,
)?;
let next_alias = KeyAlias::new_unchecked(format!("{}--next-0", local_key_alias));
keychain.store_key(
&next_alias,
&controller_did,
KeyRole::NextRotation,
&encrypted_next,
)?;
}
identity_storage.create_identity(controller_did.as_str(), metadata)?;
Ok((controller_did, local_key_alias.clone()))
}
pub fn initialize_registry_identity(
backend: Arc<dyn RegistryBackend + Send + Sync>,
local_key_alias: &KeyAlias,
passphrase_provider: &dyn PassphraseProvider,
keychain: &(dyn KeyStorage + Send + Sync),
witness_config: Option<&WitnessConfig>,
curve: auths_crypto::CurveType,
) -> Result<(IdentityDID, KeyAlias), InitError> {
backend
.init_if_needed()
.map_err(|e| InitError::Registry(e.to_string()))?;
if keychain.is_hardware_backend() {
return initialize_hardware_registry_identity(
backend,
local_key_alias,
passphrase_provider,
keychain,
witness_config,
curve,
);
}
let current = crate::keri::inception::generate_keypair_for_init(curve)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let next = crate::keri::inception::generate_keypair_for_init(curve)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let current_pub_encoded = current.cesr_encoded.clone();
let next_commitment = compute_next_commitment(&next.verkey());
let (bt, b) = match witness_config {
Some(cfg) if cfg.is_enabled() => (
Threshold::Simple(cfg.threshold as u64),
cfg.aids().cloned().collect(),
),
_ => (Threshold::Simple(0), vec![]),
};
let icp = IcpEvent {
v: VersionString::placeholder(),
d: Said::default(),
i: Prefix::default(),
s: KeriSequence::new(0),
kt: Threshold::Simple(1),
k: vec![CesrKey::new_unchecked(current_pub_encoded)],
nt: Threshold::Simple(1),
n: vec![next_commitment],
bt,
b,
c: vec![],
a: vec![],
};
let finalized = finalize_icp_event(icp).map_err(|e| InitError::Keri(e.to_string()))?;
let prefix = finalized.i.clone();
let canonical = serialize_for_signing(&Event::Icp(finalized.clone()))
.map_err(|e| InitError::Keri(e.to_string()))?;
let sig_bytes =
crate::keri::inception::sign_with_pkcs8_for_init(curve, ¤t.pkcs8, &canonical)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let attachment = auths_keri::serialize_attachment(&[auths_keri::IndexedSignature {
index: 0,
prior_index: None,
sig: sig_bytes,
}])
.map_err(|e| InitError::Keri(format!("attachment serialization: {e}")))?;
backend
.append_signed_event(&prefix, &Event::Icp(finalized), &attachment)
.map_err(|e| InitError::Registry(e.to_string()))?;
#[allow(clippy::disallowed_methods)]
let controller_did = IdentityDID::new_unchecked(format!("did:keri:{}", prefix));
let passphrase = passphrase_provider
.get_passphrase(&format!("Enter passphrase for key '{}':", local_key_alias))?;
let encrypted_current = encrypt_keypair(current.pkcs8.as_ref(), &passphrase)?;
let encrypted_next = encrypt_keypair(next.pkcs8.as_ref(), &passphrase)?;
keychain.store_key(
local_key_alias,
&controller_did,
KeyRole::Primary,
&encrypted_current,
)?;
let next_alias = KeyAlias::new_unchecked(format!("{}--next-0", local_key_alias));
keychain.store_key(
&next_alias,
&controller_did,
KeyRole::NextRotation,
&encrypted_next,
)?;
Ok((controller_did, local_key_alias.clone()))
}
fn initialize_hardware_registry_identity(
backend: Arc<dyn RegistryBackend + Send + Sync>,
local_key_alias: &KeyAlias,
passphrase_provider: &dyn PassphraseProvider,
keychain: &(dyn KeyStorage + Send + Sync),
witness_config: Option<&WitnessConfig>,
curve: auths_crypto::CurveType,
) -> Result<(IdentityDID, KeyAlias), InitError> {
if curve != auths_crypto::CurveType::P256 {
return Err(InitError::Crypto(format!(
"hardware-backed inception supports P-256 only, got {curve:?}"
)));
}
#[allow(clippy::disallowed_methods)]
let placeholder = IdentityDID::new_unchecked("did:keri:pending");
let next_alias = KeyAlias::new_unchecked(format!("{}--next-0", local_key_alias));
keychain.store_key(local_key_alias, &placeholder, KeyRole::Primary, &[])?;
keychain.store_key(&next_alias, &placeholder, KeyRole::NextRotation, &[])?;
let _ = passphrase_provider;
let current_pub = keychain.export_public_key(local_key_alias)?;
let next_pub = keychain.export_public_key(&next_alias)?;
let current_norm = auths_crypto::normalize_verkey(¤t_pub, curve)
.map_err(|e| InitError::Crypto(format!("hardware current key: {e}")))?;
let next_norm = auths_crypto::normalize_verkey(&next_pub, curve)
.map_err(|e| InitError::Crypto(format!("hardware next key: {e}")))?;
let current_verkey = auths_keri::KeriPublicKey::from_verkey_bytes(¤t_norm, curve)
.map_err(|e| InitError::Crypto(format!("hardware current key: {e}")))?;
let next_verkey = auths_keri::KeriPublicKey::from_verkey_bytes(&next_norm, curve)
.map_err(|e| InitError::Crypto(format!("hardware next key: {e}")))?;
let current_cesr = current_verkey
.to_qb64()
.map_err(|e| InitError::Crypto(e.to_string()))?;
let (bt, b) = match witness_config {
Some(cfg) if cfg.is_enabled() => (
Threshold::Simple(cfg.threshold as u64),
cfg.aids().cloned().collect(),
),
_ => (Threshold::Simple(0), vec![]),
};
let icp = IcpEvent {
v: VersionString::placeholder(),
d: Said::default(),
i: Prefix::default(),
s: KeriSequence::new(0),
kt: Threshold::Simple(1),
k: vec![CesrKey::new_unchecked(current_cesr)],
nt: Threshold::Simple(1),
n: vec![compute_next_commitment(&next_verkey)],
bt,
b,
c: vec![],
a: vec![],
};
let finalized = finalize_icp_event(icp).map_err(|e| InitError::Keri(e.to_string()))?;
let prefix = finalized.i.clone();
let canonical = serialize_for_signing(&Event::Icp(finalized.clone()))
.map_err(|e| InitError::Keri(e.to_string()))?;
let sig_bytes = keychain.sign_raw(local_key_alias, &canonical)?;
let attachment = auths_keri::serialize_attachment(&[auths_keri::IndexedSignature {
index: 0,
prior_index: None,
sig: sig_bytes,
}])
.map_err(|e| InitError::Keri(format!("attachment serialization: {e}")))?;
backend
.append_signed_event(&prefix, &Event::Icp(finalized), &attachment)
.map_err(|e| InitError::Registry(e.to_string()))?;
#[allow(clippy::disallowed_methods)]
let controller_did = IdentityDID::new_unchecked(format!("did:keri:{}", prefix));
keychain.rebind_identity(local_key_alias, &controller_did)?;
keychain.rebind_identity(&next_alias, &controller_did)?;
Ok((controller_did, local_key_alias.clone()))
}
#[allow(clippy::too_many_arguments)]
pub fn initialize_registry_identity_multi(
backend: Arc<dyn RegistryBackend + Send + Sync>,
local_key_alias: &KeyAlias,
passphrase_provider: &dyn PassphraseProvider,
keychain: &(dyn KeyStorage + Send + Sync),
witness_config: Option<&WitnessConfig>,
curves: &[auths_crypto::CurveType],
kt: Threshold,
nt: Threshold,
) -> Result<(IdentityDID, KeyAlias), InitError> {
if curves.is_empty() {
return Err(InitError::Crypto(
"initialize_registry_identity_multi requires at least one curve".to_string(),
));
}
crate::keri::inception::validate_threshold_for_key_count(&kt, curves.len())
.map_err(|e| InitError::Crypto(e.to_string()))?;
crate::keri::inception::validate_threshold_for_key_count(&nt, curves.len())
.map_err(|e| InitError::Crypto(e.to_string()))?;
backend
.init_if_needed()
.map_err(|e| InitError::Registry(e.to_string()))?;
let current_kps = crate::keri::inception::generate_keypairs_for_init(curves)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let next_kps = crate::keri::inception::generate_keypairs_for_init(curves)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let k: Vec<CesrKey> = current_kps
.iter()
.map(|kp| CesrKey::new_unchecked(kp.cesr_encoded.clone()))
.collect();
let n: Vec<Said> = next_kps
.iter()
.map(|kp| compute_next_commitment(&kp.verkey()))
.collect();
let (bt, b) = match witness_config {
Some(cfg) if cfg.is_enabled() => (
Threshold::Simple(cfg.threshold as u64),
cfg.aids().cloned().collect(),
),
_ => (Threshold::Simple(0), vec![]),
};
let icp = IcpEvent {
v: VersionString::placeholder(),
d: Said::default(),
i: Prefix::default(),
s: KeriSequence::new(0),
kt,
k,
nt,
n,
bt,
b,
c: vec![],
a: vec![],
};
let finalized = finalize_icp_event(icp).map_err(|e| InitError::Keri(e.to_string()))?;
let prefix = finalized.i.clone();
let canonical = serialize_for_signing(&Event::Icp(finalized.clone()))
.map_err(|e| InitError::Keri(e.to_string()))?;
let sig_bytes = crate::keri::inception::sign_with_pkcs8_for_init(
curves[0],
¤t_kps[0].pkcs8,
&canonical,
)
.map_err(|e| InitError::Crypto(e.to_string()))?;
let attachment = auths_keri::serialize_attachment(&[auths_keri::IndexedSignature {
index: 0,
prior_index: None,
sig: sig_bytes,
}])
.map_err(|e| InitError::Keri(format!("attachment serialization: {e}")))?;
backend
.append_signed_event(&prefix, &Event::Icp(finalized), &attachment)
.map_err(|e| InitError::Registry(e.to_string()))?;
#[allow(clippy::disallowed_methods)]
let controller_did = IdentityDID::new_unchecked(format!("did:keri:{}", prefix));
let is_hardware_backend = keychain.is_hardware_backend();
for (idx, (cur, nxt)) in current_kps.iter().zip(next_kps.iter()).enumerate() {
let cur_alias = KeyAlias::new_unchecked(format!("{}--{}", local_key_alias, idx));
let nxt_alias = KeyAlias::new_unchecked(format!("{}--next-0-{}", local_key_alias, idx));
if is_hardware_backend {
keychain.store_key(&cur_alias, &controller_did, KeyRole::Primary, &[])?;
keychain.store_key(&nxt_alias, &controller_did, KeyRole::NextRotation, &[])?;
} else {
let passphrase = passphrase_provider
.get_passphrase(&format!("Enter passphrase for key '{}':", local_key_alias))?;
let encrypted_current = encrypt_keypair(cur.pkcs8.as_ref(), &passphrase)?;
let encrypted_next = encrypt_keypair(nxt.pkcs8.as_ref(), &passphrase)?;
keychain.store_key(
&cur_alias,
&controller_did,
KeyRole::Primary,
&encrypted_current,
)?;
keychain.store_key(
&nxt_alias,
&controller_did,
KeyRole::NextRotation,
&encrypted_next,
)?;
}
}
Ok((controller_did, local_key_alias.clone()))
}