use crate::did::document::{DidDocument, VerificationRelationship};
use crate::proof::ed25519::Ed25519Signer;
use crate::{Did, DidError, DidResult, VerificationMethod};
use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyRotationRecord {
pub old_key_id: String,
pub new_key_id: String,
pub rotation_date: DateTime<Utc>,
pub revocation_date: Option<DateTime<Utc>>,
pub reason: Option<KeyRotationReason>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyRotationReason {
Scheduled,
Compromise,
AlgorithmUpgrade,
OrganizationalChange,
Expiration,
Other,
}
impl KeyRotationReason {
pub fn as_str(&self) -> &'static str {
match self {
Self::Scheduled => "scheduled",
Self::Compromise => "compromise",
Self::AlgorithmUpgrade => "algorithm_upgrade",
Self::OrganizationalChange => "organizational_change",
Self::Expiration => "expiration",
Self::Other => "other",
}
}
}
#[derive(Debug, Clone)]
pub struct KeyRotation {
pub new_key: VerificationMethod,
pub new_secret_key: Vec<u8>,
pub rotation_date: DateTime<Utc>,
pub transition_period_days: u32,
pub reason: Option<KeyRotationReason>,
}
impl KeyRotation {
pub fn new(new_key: VerificationMethod, new_secret_key: Vec<u8>) -> Self {
Self {
new_key,
new_secret_key,
rotation_date: Utc::now(),
transition_period_days: 30,
reason: None,
}
}
pub fn with_transition_period(mut self, days: u32) -> Self {
self.transition_period_days = days;
self
}
pub fn with_reason(mut self, reason: KeyRotationReason) -> Self {
self.reason = Some(reason);
self
}
pub fn with_rotation_date(mut self, date: DateTime<Utc>) -> Self {
self.rotation_date = date;
self
}
pub fn revocation_date(&self) -> DateTime<Utc> {
self.rotation_date + Duration::days(self.transition_period_days as i64)
}
}
pub struct KeyRotationRegistry {
rotations: Vec<KeyRotationRecord>,
current_keys: HashMap<String, String>,
key_map: HashMap<String, VerificationMethod>,
}
impl Default for KeyRotationRegistry {
fn default() -> Self {
Self::new()
}
}
impl KeyRotationRegistry {
pub fn new() -> Self {
Self {
rotations: Vec::new(),
current_keys: HashMap::new(),
key_map: HashMap::new(),
}
}
pub fn register_initial_key(&mut self, did: &Did, key: VerificationMethod) {
let key_id = key.id.clone();
self.current_keys
.insert(did.as_str().to_string(), key_id.clone());
self.key_map.insert(key_id, key);
}
pub fn rotate_key(
&mut self,
did_doc: &mut DidDocument,
rotation: KeyRotation,
) -> DidResult<KeyRotationRecord> {
let did_str = did_doc.id.as_str().to_string();
let old_key_id = self
.current_keys
.get(&did_str)
.cloned()
.or_else(|| {
did_doc.authentication.first().map(|rel| match rel {
VerificationRelationship::Reference(id) => id.clone(),
VerificationRelationship::Embedded(vm) => vm.id.clone(),
})
})
.ok_or_else(|| {
DidError::KeyNotFound(format!("No current key found for DID: {}", did_str))
})?;
let new_key_id = rotation.new_key.id.clone();
if old_key_id == new_key_id {
return Err(DidError::InvalidKey(
"New key ID must differ from old key ID".to_string(),
));
}
if did_doc.get_verification_method(&new_key_id).is_some() {
return Err(DidError::InvalidKey(format!(
"Key {} already exists in DID Document",
new_key_id
)));
}
let revocation_date = if rotation.transition_period_days > 0 {
Some(rotation.revocation_date())
} else {
None
};
let new_key_vm = rotation.new_key.clone();
did_doc.verification_method.push(new_key_vm.clone());
update_verification_relationships(&mut did_doc.authentication, &old_key_id, &new_key_id);
update_verification_relationships(&mut did_doc.assertion_method, &old_key_id, &new_key_id);
update_verification_relationships(
&mut did_doc.capability_invocation,
&old_key_id,
&new_key_id,
);
update_verification_relationships(
&mut did_doc.capability_delegation,
&old_key_id,
&new_key_id,
);
update_verification_relationships(&mut did_doc.key_agreement, &old_key_id, &new_key_id);
if rotation.transition_period_days == 0 {
did_doc.verification_method.retain(|vm| vm.id != old_key_id);
}
let record = KeyRotationRecord {
old_key_id: old_key_id.clone(),
new_key_id: new_key_id.clone(),
rotation_date: rotation.rotation_date,
revocation_date,
reason: rotation.reason,
};
self.rotations.push(record.clone());
self.current_keys.insert(did_str, new_key_id.clone());
self.key_map.insert(new_key_id, new_key_vm);
Ok(record)
}
pub fn is_key_valid(&self, key_id: &str, at_time: Option<DateTime<Utc>>) -> bool {
let check_time = at_time.unwrap_or_else(Utc::now);
for record in &self.rotations {
if record.old_key_id == key_id {
match record.revocation_date {
Some(revoke_date) => {
if check_time > revoke_date {
return false;
}
}
None => {
if check_time >= record.rotation_date {
return false;
}
}
}
}
}
self.key_map.contains_key(key_id)
}
pub fn get_current_key_id(&self, did: &Did) -> Option<&String> {
self.current_keys.get(did.as_str())
}
pub fn get_current_key(&self, did: &Did) -> Option<&VerificationMethod> {
self.current_keys
.get(did.as_str())
.and_then(|id| self.key_map.get(id))
}
pub fn get_rotation_history(&self) -> &[KeyRotationRecord] {
&self.rotations
}
pub fn get_key_rotation_chain(&self, initial_key_id: &str) -> Vec<&KeyRotationRecord> {
let mut chain = Vec::new();
let mut current = initial_key_id;
loop {
let record = self.rotations.iter().find(|r| r.old_key_id == current);
match record {
Some(r) => {
chain.push(r);
current = &r.new_key_id;
}
None => break,
}
}
chain
}
pub fn apply_pending_revocations(
&self,
did_doc: &mut DidDocument,
at_time: Option<DateTime<Utc>>,
) {
let check_time = at_time.unwrap_or_else(Utc::now);
let keys_to_remove: Vec<String> = self
.rotations
.iter()
.filter(|r| {
if let Some(revoke_date) = r.revocation_date {
check_time > revoke_date
} else {
false
}
})
.map(|r| r.old_key_id.clone())
.collect();
did_doc
.verification_method
.retain(|vm| !keys_to_remove.contains(&vm.id));
}
pub fn rotation_count(&self) -> usize {
self.rotations.len()
}
}
pub fn generate_rotation_key(
did: &Did,
key_fragment: &str,
) -> DidResult<(VerificationMethod, Vec<u8>)> {
let signer = Ed25519Signer::generate();
let secret_key = signer.secret_key_bytes().to_vec();
let public_key = signer.public_key_bytes();
let key_id = format!("{}#{}", did.as_str(), key_fragment);
let vm = VerificationMethod::ed25519(&key_id, did.as_str(), &public_key);
Ok((vm, secret_key))
}
fn update_verification_relationships(
relationships: &mut [VerificationRelationship],
old_key_id: &str,
new_key_id: &str,
) {
for rel in relationships.iter_mut() {
match rel {
VerificationRelationship::Reference(id) if id == old_key_id => {
*id = new_key_id.to_string();
}
VerificationRelationship::Embedded(vm) if vm.id == old_key_id => {
vm.id = new_key_id.to_string();
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::did::DidDocument;
fn create_test_did_doc() -> (DidDocument, Vec<u8>) {
let signer = Ed25519Signer::generate();
let secret_key = signer.secret_key_bytes().to_vec();
let public_key = signer.public_key_bytes();
let doc = DidDocument::from_key_ed25519(&public_key).unwrap();
(doc, secret_key)
}
#[test]
fn test_key_rotation_basic() {
let (mut doc, _old_secret) = create_test_did_doc();
let did = doc.id.clone();
let old_key_id = doc.verification_method[0].id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (new_vm, new_secret) = generate_rotation_key(&did, "key-2").unwrap();
let rotation = KeyRotation::new(new_vm, new_secret)
.with_transition_period(30)
.with_reason(KeyRotationReason::Scheduled);
let record = registry.rotate_key(&mut doc, rotation).unwrap();
assert_eq!(record.old_key_id, old_key_id);
assert!(record.new_key_id.contains("key-2"));
assert!(record.revocation_date.is_some());
assert_eq!(record.reason, Some(KeyRotationReason::Scheduled));
assert!(doc.get_verification_method(&record.new_key_id).is_some());
let auth_refs: Vec<&str> = doc
.authentication
.iter()
.filter_map(|rel| match rel {
VerificationRelationship::Reference(id) => Some(id.as_str()),
_ => None,
})
.collect();
assert!(auth_refs.contains(&record.new_key_id.as_str()));
}
#[test]
fn test_key_rotation_no_transition_period() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let old_key_id = doc.verification_method[0].id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (new_vm, new_secret) = generate_rotation_key(&did, "key-2").unwrap();
let rotation = KeyRotation::new(new_vm, new_secret).with_transition_period(0);
let _record = registry.rotate_key(&mut doc, rotation).unwrap();
assert!(doc.get_verification_method(&old_key_id).is_none());
}
#[test]
fn test_is_key_valid_during_transition() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let old_key_id = doc.verification_method[0].id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (new_vm, new_secret) = generate_rotation_key(&did, "key-2").unwrap();
let rotation = KeyRotation::new(new_vm, new_secret).with_transition_period(30);
let _record = registry.rotate_key(&mut doc, rotation).unwrap();
assert!(registry.is_key_valid(&old_key_id, None));
let past_transition = Utc::now() + Duration::days(31);
assert!(!registry.is_key_valid(&old_key_id, Some(past_transition)));
}
#[test]
fn test_rotation_history() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (vm1, secret1) = generate_rotation_key(&did, "key-2").unwrap();
registry
.rotate_key(&mut doc, KeyRotation::new(vm1, secret1))
.unwrap();
let (vm2, secret2) = generate_rotation_key(&did, "key-3").unwrap();
registry
.rotate_key(&mut doc, KeyRotation::new(vm2, secret2))
.unwrap();
assert_eq!(registry.rotation_count(), 2);
assert_eq!(registry.get_rotation_history().len(), 2);
}
#[test]
fn test_get_current_key() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (new_vm, new_secret) = generate_rotation_key(&did, "key-2").unwrap();
let new_key_id = new_vm.id.clone();
registry
.rotate_key(&mut doc, KeyRotation::new(new_vm, new_secret))
.unwrap();
let current = registry.get_current_key(&did).unwrap();
assert_eq!(current.id, new_key_id);
}
#[test]
fn test_apply_pending_revocations() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let old_key_id = doc.verification_method[0].id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (new_vm, new_secret) = generate_rotation_key(&did, "key-2").unwrap();
let rotation = KeyRotation::new(new_vm, new_secret).with_transition_period(30);
registry.rotate_key(&mut doc, rotation).unwrap();
assert!(doc.get_verification_method(&old_key_id).is_some());
let future_time = Utc::now() + Duration::days(31);
registry.apply_pending_revocations(&mut doc, Some(future_time));
assert!(doc.get_verification_method(&old_key_id).is_none());
}
#[test]
fn test_rotation_chain() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let initial_key_id = doc.verification_method[0].id.clone();
let mut registry = KeyRotationRegistry::new();
registry.register_initial_key(&did, doc.verification_method[0].clone());
let (vm1, s1) = generate_rotation_key(&did, "key-2").unwrap();
registry
.rotate_key(&mut doc, KeyRotation::new(vm1, s1))
.unwrap();
let (vm2, s2) = generate_rotation_key(&did, "key-3").unwrap();
registry
.rotate_key(&mut doc, KeyRotation::new(vm2, s2))
.unwrap();
let chain = registry.get_key_rotation_chain(&initial_key_id);
assert_eq!(chain.len(), 2);
}
#[test]
fn test_generate_rotation_key() {
let did = Did::new("did:key:z6Mk123").unwrap();
let (vm, secret) = generate_rotation_key(&did, "key-2").unwrap();
assert!(vm.id.contains("key-2"));
assert_eq!(vm.controller, did.as_str());
assert_eq!(secret.len(), 32);
assert!(vm.public_key_multibase.is_some());
}
#[test]
fn test_duplicate_key_rotation_error() {
let (mut doc, _) = create_test_did_doc();
let did = doc.id.clone();
let mut registry = KeyRotationRegistry::new();
let original_vm = doc.verification_method[0].clone();
registry.register_initial_key(&did, original_vm.clone());
let rotation = KeyRotation::new(original_vm, vec![0u8; 32]);
let result = registry.rotate_key(&mut doc, rotation);
assert!(result.is_err());
}
}