From 8a63e14aeeb217e55ce35072677bc184bc11e669 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 29 Sep 2021 16:54:45 +0200 Subject: [PATCH] Add default signing key as a generic verification method (#421) * Rename auth to int chain everywhere * Create verification method with attached auth * Fix generations in tests * Update remaining generations in tests * Remove outdated TODO * Impl generic verif method in low level API * Use 'integration' instead of 'int' * Add test that verifies number of verif. methods * Assert the authentication relationship --- identity-account/src/account/account.rs | 18 ++++---- identity-account/src/events/command.rs | 11 +++-- identity-account/src/events/event.rs | 10 ++-- .../src/identity/identity_state.rs | 46 +++++++++---------- identity-account/src/storage/stronghold.rs | 2 +- identity-account/src/tests/commands.rs | 32 ++++++------- identity-account/src/types/key_location.rs | 12 ++--- identity-core/src/common/fragment.rs | 3 +- identity-iota/src/did/doc/iota_document.rs | 27 ++++++++++- identity/benches/benchmark.rs | 6 +-- 10 files changed, 99 insertions(+), 68 deletions(-) diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index 3f0e335e07..ca38ae1be8 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -209,7 +209,7 @@ impl Account { let snapshot: IdentitySnapshot = self.load_snapshot(identity).await?; let state: &IdentityState = snapshot.identity(); - let fragment: Fragment = Fragment::new(fragment.into()); + let fragment: Fragment = Fragment::new(fragment); let method: &TinyMethod = state.methods().fetch(fragment.name())?; let location: &KeyLocation = method.location(); @@ -282,7 +282,7 @@ impl Account { new_state: &IdentityState, document: &mut IotaDocument, ) -> Result<()> { - if new_state.auth_generation() == Generation::new() { + if new_state.integration_generation() == Generation::new() { let method: &TinyMethod = new_state.authentication()?; let location: &KeyLocation = method.location(); @@ -299,7 +299,7 @@ impl Account { Ok(()) } - async fn process_auth_change(&self, old_root: IdentitySnapshot) -> Result<()> { + async fn process_integration_change(&self, old_root: IdentitySnapshot) -> Result<()> { let new_root: IdentitySnapshot = self.load_snapshot(old_root.id()).await?; let old_state: &IdentityState = old_root.identity(); @@ -315,7 +315,7 @@ impl Account { self.state.clients.publish_document(&new_doc).await?.into() }; - let events: [Event; 1] = [Event::new(EventData::AuthMessage(message))]; + let events: [Event; 1] = [Event::new(EventData::IntegrationMessage(message))]; self.commit_events(&new_root, &events).await?; @@ -478,7 +478,7 @@ impl Account { let id: IdentityId = snapshot.id(); match Publish::new(&commits) { - Publish::Auth => self.process_auth_change(snapshot).await?, + Publish::Integration => self.process_integration_change(snapshot).await?, Publish::Diff => self.process_diff_change(snapshot).await?, Publish::None => {} } @@ -635,7 +635,7 @@ impl State { #[derive(Clone, Copy, Debug)] enum Publish { None, - Auth, + Integration, Diff, } @@ -646,9 +646,9 @@ impl Publish { const fn apply(self, commit: &Commit) -> Self { match (self, commit.event().data()) { - (Self::Auth, _) => Self::Auth, - (_, EventData::IdentityCreated(..)) => Self::Auth, - (_, EventData::AuthMessage(_)) => self, + (Self::Integration, _) => Self::Integration, + (_, EventData::IdentityCreated(..)) => Self::Integration, + (_, EventData::IntegrationMessage(_)) => self, (_, EventData::DiffMessage(_)) => self, (_, _) => Self::Diff, } diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index cac9740dd9..3a61edac2e 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -89,7 +89,7 @@ impl Command { UpdateError::InvalidMethodType(authentication) ); - let generation: Generation = state.auth_generation(); + let generation: Generation = state.integration_generation(); let location: KeyLocation = KeyLocation::new_authentication(authentication, generation); // The key location must be available @@ -112,10 +112,15 @@ impl Command { let network: Option<&str> = network.as_deref(); let document: IotaDID = IotaDID::from_components(public.as_ref(), network)?; + let method_fragment = Fragment::new(method.location().fragment()); + Ok(Some(vec![ Event::new(EventData::IdentityCreated(document)), - // TODO: MethodScope::VerificationMethod when possible - Event::new(EventData::MethodCreated(MethodScope::Authentication, method)), + Event::new(EventData::MethodCreated(MethodScope::VerificationMethod, method)), + Event::new(EventData::MethodAttached( + method_fragment, + vec![MethodScope::Authentication], + )), ])) } Self::CreateMethod { diff --git a/identity-account/src/events/event.rs b/identity-account/src/events/event.rs index b59ca71374..2a4e9ca778 100644 --- a/identity-account/src/events/event.rs +++ b/identity-account/src/events/event.rs @@ -45,9 +45,9 @@ impl Event { trace!("[Event::apply] State = {:?}", state); match self.data { - EventData::AuthMessage(message) => { - state.set_auth_message_id(message); - state.increment_auth_generation()?; + EventData::IntegrationMessage(message) => { + state.set_integration_message_id(message); + state.increment_integration_generation()?; } EventData::DiffMessage(message) => { state.set_diff_message_id(message); @@ -104,8 +104,8 @@ impl Event { #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(tag = "type", content = "data")] pub enum EventData { - /// Emitted when a new auth message is published to the IOTA Tangle. - AuthMessage(MessageId), + /// Emitted when a new int message is published to the IOTA Tangle. + IntegrationMessage(MessageId), /// Emitted when a new diff message is published to the IOTA Tangle. DiffMessage(MessageId), /// Emitted when a new identity state is created. diff --git a/identity-account/src/identity/identity_state.rs b/identity-account/src/identity/identity_state.rs index cf34c8be40..3523c00ae3 100644 --- a/identity-account/src/identity/identity_state.rs +++ b/identity-account/src/identity/identity_state.rs @@ -47,12 +47,12 @@ pub struct IdentityState { // Chain State // // =========== // id: IdentityId, - auth_generation: Generation, + integration_generation: Generation, diff_generation: Generation, #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] this_message_id: MessageId, #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] - last_auth_message_id: MessageId, + last_integration_message_id: MessageId, #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] last_diff_message_id: MessageId, @@ -79,10 +79,10 @@ impl IdentityState { pub fn new(id: IdentityId) -> Self { Self { id, - auth_generation: Generation::new(), + integration_generation: Generation::new(), diff_generation: Generation::new(), this_message_id: MessageId::null(), - last_auth_message_id: MessageId::null(), + last_integration_message_id: MessageId::null(), last_diff_message_id: MessageId::null(), did: None, controller: None, @@ -103,9 +103,9 @@ impl IdentityState { self.id } - /// Returns the current generation of the identity auth chain. - pub fn auth_generation(&self) -> Generation { - self.auth_generation + /// Returns the current generation of the identity integration chain. + pub fn integration_generation(&self) -> Generation { + self.integration_generation } /// Returns the current generation of the identity diff chain. @@ -113,9 +113,9 @@ impl IdentityState { self.diff_generation } - /// Increments the generation of the identity auth chain. - pub fn increment_auth_generation(&mut self) -> Result<()> { - self.auth_generation = self.auth_generation.try_increment()?; + /// Increments the generation of the identity integration chain. + pub fn increment_integration_generation(&mut self) -> Result<()> { + self.integration_generation = self.integration_generation.try_increment()?; self.diff_generation = Generation::new(); Ok(()) @@ -132,17 +132,17 @@ impl IdentityState { // Tangle State // =========================================================================== - /// Returns the current auth Tangle message id of the identity. + /// Returns the current integration Tangle message id of the identity. pub fn this_message_id(&self) -> &MessageId { &self.this_message_id } - /// Returns the previous auth Tangle message id of the identity. + /// Returns the previous integration Tangle message id of the identity. pub fn last_message_id(&self) -> &MessageId { - &self.last_auth_message_id + &self.last_integration_message_id } - /// Returns the previous diff Tangle message id, or the current auth message id. + /// Returns the previous diff Tangle message id, or the current integration message id. pub fn diff_message_id(&self) -> &MessageId { if self.last_diff_message_id.is_null() { &self.this_message_id @@ -151,15 +151,15 @@ impl IdentityState { } } - /// Sets the current Tangle auth message id of the identity. - pub fn set_auth_message_id(&mut self, message: MessageId) { - // Set the current auth message id as the previous auth message. - self.last_auth_message_id = self.this_message_id; + /// Sets the current Tangle integration message id of the identity. + pub fn set_integration_message_id(&mut self, message: MessageId) { + // Set the current integration message id as the previous integration message. + self.last_integration_message_id = self.this_message_id; // Clear the diff message id self.last_diff_message_id = MessageId::null(); - // Set the new auth message id + // Set the new integration message id self.this_message_id = message; } @@ -237,7 +237,7 @@ impl IdentityState { .methods() .iter() .filter(|method| method.is_authentication()) - .max_by_key(|method| method.location().auth_generation()) + .max_by_key(|method| method.location().integration_generation()) .ok_or(Error::MethodNotFound) } @@ -246,7 +246,7 @@ impl IdentityState { Ok(KeyLocation { method, fragment: Fragment::new(fragment), - auth_generation: self.auth_generation(), + integration_generation: self.integration_generation(), diff_generation: self.diff_generation(), }) } @@ -312,8 +312,8 @@ impl IdentityState { document.set_message_id(self.this_message_id); } - if !self.last_auth_message_id.is_null() { - document.set_previous_message_id(self.last_auth_message_id); + if !self.last_integration_message_id.is_null() { + document.set_previous_message_id(self.last_integration_message_id); } document.set_created(self.created.into()); diff --git a/identity-account/src/storage/stronghold.rs b/identity-account/src/storage/stronghold.rs index 0481e42c06..7f2a720a52 100644 --- a/identity-account/src/storage/stronghold.rs +++ b/identity-account/src/storage/stronghold.rs @@ -428,7 +428,7 @@ fn fmt_key(prefix: &str, location: &KeyLocation) -> Vec { format!( "{}:{}:{}:{}", prefix, - location.auth_generation(), + location.integration_generation(), location.diff_generation(), location.fragment(), ) diff --git a/identity-account/src/tests/commands.rs b/identity-account/src/tests/commands.rs index 496ad2c2db..6f74103c32 100644 --- a/identity-account/src/tests/commands.rs +++ b/identity-account/src/tests/commands.rs @@ -51,7 +51,7 @@ async fn test_create_identity() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(3)); + assert_eq!(snapshot.sequence(), Generation::from_u32(4)); assert_eq!(snapshot.id(), identity); assert!(snapshot.identity().did().is_some()); assert_ne!(snapshot.identity().created(), UnixTimestamp::EPOCH); @@ -147,8 +147,8 @@ async fn test_create_identity_already_exists() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - // version is now 3 - assert_eq!(snapshot.sequence(), Generation::from(3)); + // version is now 4 + assert_eq!(snapshot.sequence(), Generation::from(4)); let output: Result<()> = account.process(identity, command, false).await; @@ -159,8 +159,8 @@ async fn test_create_identity_already_exists() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - // version is still 3, no events have been committed - assert_eq!(snapshot.sequence(), Generation::from(3)); + // version is still 4, no events have been committed + assert_eq!(snapshot.sequence(), Generation::from(4)); Ok(()) } @@ -233,7 +233,7 @@ async fn test_create_method() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(5)); + assert_eq!(snapshot.sequence(), Generation::from_u32(6)); assert_eq!(snapshot.id(), identity); assert!(snapshot.identity().did().is_some()); assert_ne!(snapshot.identity().created(), UnixTimestamp::EPOCH); @@ -270,8 +270,8 @@ async fn test_create_method_reserved_fragment() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - // version is now 3 - assert_eq!(snapshot.sequence(), Generation::from_u32(3)); + // version is now 4 + assert_eq!(snapshot.sequence(), Generation::from_u32(4)); let output: _ = account.process(identity, command, false).await; @@ -282,8 +282,8 @@ async fn test_create_method_reserved_fragment() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - // version is still 3, no new events have been committed - assert_eq!(snapshot.sequence(), Generation::from_u32(3)); + // version is still 4, no new events have been committed + assert_eq!(snapshot.sequence(), Generation::from_u32(4)); Ok(()) } @@ -309,12 +309,12 @@ async fn test_create_method_duplicate_fragment() -> Result<()> { }; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(3)); + assert_eq!(snapshot.sequence(), Generation::from_u32(4)); account.process(identity, command.clone(), false).await?; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(5)); + assert_eq!(snapshot.sequence(), Generation::from_u32(6)); let output: _ = account.process(identity, command, false).await; @@ -324,7 +324,7 @@ async fn test_create_method_duplicate_fragment() -> Result<()> { )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(5)); + assert_eq!(snapshot.sequence(), Generation::from_u32(6)); Ok(()) } @@ -458,13 +458,13 @@ async fn test_delete_method() -> Result<()> { }; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(3)); + assert_eq!(snapshot.sequence(), Generation::from_u32(4)); account.process(identity, command, false).await?; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(5)); + assert_eq!(snapshot.sequence(), Generation::from_u32(6)); assert_eq!(snapshot.identity().methods().len(), 2); assert!(snapshot.identity().methods().contains("key-1")); assert!(snapshot.identity().methods().get("key-1").is_some()); @@ -478,7 +478,7 @@ async fn test_delete_method() -> Result<()> { let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; - assert_eq!(snapshot.sequence(), Generation::from_u32(7)); + assert_eq!(snapshot.sequence(), Generation::from_u32(8)); assert_eq!(snapshot.identity().methods().len(), 1); assert!(!snapshot.identity().methods().contains("key-1")); assert!(snapshot.identity().methods().get("key-1").is_none()); diff --git a/identity-account/src/types/key_location.rs b/identity-account/src/types/key_location.rs index e1b7a36572..c62b632871 100644 --- a/identity-account/src/types/key_location.rs +++ b/identity-account/src/types/key_location.rs @@ -15,7 +15,7 @@ use crate::types::Generation; pub struct KeyLocation { pub(crate) method: MethodType, pub(crate) fragment: Fragment, - pub(crate) auth_generation: Generation, + pub(crate) integration_generation: Generation, pub(crate) diff_generation: Generation, } @@ -29,7 +29,7 @@ impl KeyLocation { Self { method, fragment: Fragment::new(fragment), - auth_generation: generation, + integration_generation: generation, diff_generation: Generation::new(), } } @@ -44,9 +44,9 @@ impl KeyLocation { self.fragment.name() } - /// Returns the auth generation when this key was created. - pub fn auth_generation(&self) -> Generation { - self.auth_generation + /// Returns the integration generation when this key was created. + pub fn integration_generation(&self) -> Generation { + self.integration_generation } /// Returns the diff generation when this key was created. @@ -69,7 +69,7 @@ impl Display for KeyLocation { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_fmt(format_args!( "({}:{}:{}:{})", - self.auth_generation, + self.integration_generation, self.diff_generation, self.fragment, self.method.as_u32() diff --git a/identity-core/src/common/fragment.rs b/identity-core/src/common/fragment.rs index 2de5e57c6c..ad510cb032 100644 --- a/identity-core/src/common/fragment.rs +++ b/identity-core/src/common/fragment.rs @@ -16,7 +16,8 @@ impl Fragment { const PREFIX: char = '#'; /// Creates a new Fragment from the given `value`. - pub fn new(value: String) -> Self { + pub fn new(value: impl Into) -> Self { + let value = value.into(); if value.starts_with(Self::PREFIX) { Self(value) } else { diff --git a/identity-iota/src/did/doc/iota_document.rs b/identity-iota/src/did/doc/iota_document.rs index 01a12e6109..42ca90e4c3 100644 --- a/identity-iota/src/did/doc/iota_document.rs +++ b/identity-iota/src/did/doc/iota_document.rs @@ -8,6 +8,7 @@ use core::fmt::Display; use core::fmt::Formatter; use core::fmt::Result as FmtResult; +use identity_did::did::DID; use serde::Serialize; use identity_core::common::Object; @@ -113,9 +114,12 @@ impl IotaDocument { /// /// This must be guaranteed safe by the caller. pub unsafe fn from_authentication_unchecked(method: IotaVerificationMethod) -> Self { + let verification_method_did: DID = method.id().as_ref().clone(); + CoreDocument::builder(Default::default()) .id(method.controller().clone().into()) - .authentication(method) + .verification_method(method.into()) + .authentication(MethodRef::Refer(verification_method_did)) .build() .map(CoreDocument::into_verifiable) .map(TryInto::try_into) @@ -1070,4 +1074,25 @@ mod tests { let diff_index = IotaDocument::diff_index(&message_id).expect("failed to generate diff_index"); assert_eq!(diff_index, "2g45GsCAmkvQfcrHGUgqwQJLbYY3Gic8f23wf71sGGGP"); } + + #[test] + fn test_new_document_has_verification_method_with_authentication_relationship() { + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::from_keypair(&keypair).unwrap(); + + let verif_method = document.resolve("#authentication").unwrap(); + let auth_method = document.authentication(); + + // `methods` returns all embedded verification methods, so only one is expected. + assert_eq!(document.methods().count(), 1); + + // Assert that the verification method and the authentication method are the same + assert_eq!(verif_method, auth_method); + + // Assert that the fragment of the authentication method reference is `authentication` + match document.document.authentication().first().unwrap().clone().into_inner() { + MethodRef::Refer(did) => assert_eq!(did.fragment().unwrap_or_default(), "authentication"), + MethodRef::Embed(_) => panic!("authentication method should be a reference"), + } + } } diff --git a/identity/benches/benchmark.rs b/identity/benches/benchmark.rs index a29e858a2b..0efaaa11b6 100644 --- a/identity/benches/benchmark.rs +++ b/identity/benches/benchmark.rs @@ -64,12 +64,12 @@ fn bench_diff_chain_updates(c: &mut Criterion) { group.finish(); } -fn bench_auth_chain_updates(c: &mut Criterion) { +fn bench_integration_chain_updates(c: &mut Criterion) { static ITERATIONS: &[usize] = &[1, 10, 100, 1000]; let (doc, keys) = setup_diff_chain_bench(); - let mut group = c.benchmark_group("update auth chain"); + let mut group = c.benchmark_group("update integration chain"); for size in ITERATIONS.iter() { let mut chain: DocumentChain = DocumentChain::new(IntegrationChain::new(doc.clone()).unwrap()); @@ -89,6 +89,6 @@ criterion_group!( bench_generate_did, bench_generate_doc_chain, bench_diff_chain_updates, - bench_auth_chain_updates, + bench_integration_chain_updates, ); criterion_main!(benches);