From 12e67ab238170a1fcf798531add0447e8e31f07d Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Wed, 29 Jun 2022 18:31:54 +0200 Subject: [PATCH 01/18] fds --- lets/src/id/did.rs | 39 +++++++++++++------ lets/src/id/identifier.rs | 10 +++-- lets/src/id/identity.rs | 6 +-- .../examples/full-example/scenarios/did.rs | 14 +++---- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/lets/src/id/did.rs b/lets/src/id/did.rs index 5a533c06..3abd89c4 100644 --- a/lets/src/id/did.rs +++ b/lets/src/id/did.rs @@ -156,15 +156,17 @@ impl Default for DID { #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DIDInfo { did: IotaDID, - key_fragment: String, + exchange_fragment: String, + signing_fragment: String, keypair: KeyPair, } impl DIDInfo { - pub fn new(did: IotaDID, key_fragment: String, keypair: identity::crypto::KeyPair) -> Self { + pub fn new(did: IotaDID, exchange_fragment: String, signing_fragment: String, keypair: identity::crypto::KeyPair) -> Self { Self { did, - key_fragment, + exchange_fragment, + signing_fragment, keypair: KeyPair(keypair), } } @@ -172,8 +174,12 @@ impl DIDInfo { &self.did } - pub(crate) fn key_fragment(&self) -> &str { - &self.key_fragment + pub(crate) fn exchange_fragment(&self) -> &str { + &self.exchange_fragment + } + + pub(crate) fn signing_fragment(&self) -> &str { + &self.signing_fragment } pub(crate) fn keypair(&self) -> &identity::crypto::KeyPair { @@ -184,8 +190,12 @@ impl DIDInfo { &mut self.did } - fn key_fragment_mut(&mut self) -> &mut String { - &mut self.key_fragment + fn exchange_fragment_mut(&mut self) -> &mut String { + &mut self.exchange_fragment + } + + fn signing_fragment_mut(&mut self) -> &mut String { + &mut self.signing_fragment } fn keypair_mut(&mut self) -> &mut identity::crypto::KeyPair { @@ -240,7 +250,8 @@ impl Hash for KeyPair { impl Mask<&DID> for sizeof::Context { fn mask(&mut self, did: &DID) -> Result<&mut Self> { self.mask(Bytes::new(did.info().did().as_str()))? - .mask(Bytes::new(did.info().key_fragment()))? + .mask(Bytes::new(did.info().exchange_fragment()))? + .mask(Bytes::new(did.info().signing_fragment()))? .mask(NBytes::new(did.info().keypair().private())) } } @@ -252,7 +263,8 @@ where { fn mask(&mut self, did: &DID) -> Result<&mut Self> { self.mask(Bytes::new(did.info().did().as_str()))? - .mask(Bytes::new(did.info().key_fragment()))? + .mask(Bytes::new(did.info().exchange_fragment()))? + .mask(Bytes::new(did.info().signing_fragment()))? .mask(NBytes::new(did.info().keypair().private())) } } @@ -264,14 +276,17 @@ where { fn mask(&mut self, did: &mut DID) -> Result<&mut Self> { let mut did_bytes = Vec::new(); - let mut fragment_bytes = Vec::new(); + let mut exchange_fragment_bytes = Vec::new(); + let mut signing_fragment_bytes = Vec::new(); let mut private_key_bytes = [0; ed25519::SECRET_KEY_LENGTH]; self.mask(Bytes::new(&mut did_bytes))? - .mask(Bytes::new(&mut fragment_bytes))? + .mask(Bytes::new(&mut exchange_fragment_bytes))? + .mask(Bytes::new(&mut signing_fragment_bytes))? .mask(NBytes::new(&mut private_key_bytes))?; *did.info_mut().did_mut() = core::str::from_utf8(&did_bytes)?.try_into()?; - *did.info_mut().key_fragment_mut() = String::from_utf8(fragment_bytes)?; + *did.info_mut().exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; + *did.info_mut().signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; let keypair = identity::crypto::KeyPair::try_from_ed25519_bytes(&private_key_bytes) .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 37ec5310..37576571 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -217,22 +217,24 @@ where 1 => match verifier { Identifier::DID(method_id) => { let mut hash = [0; 64]; + let mut exchange_fragment_bytes = Bytes::default(); let mut fragment_bytes = Bytes::default(); let mut signature_bytes = [0; 64]; - self.absorb(fragment_bytes.as_mut())? + self.absorb(exchange_fragment_bytes.as_mut())? + .absorb(fragment_bytes.as_mut())? .commit()? .squeeze(External::new(&mut NBytes::new(&mut hash)))? .absorb(NBytes::new(&mut signature_bytes))?; - let fragment = format!( + let exchange_fragment = format!( "#{}", - fragment_bytes + exchange_fragment_bytes .to_str() .ok_or_else(|| anyhow!("fragment must be UTF8 encoded"))? ); - let did_url = method_id.try_to_did()?.join(fragment)?; + let did_url = method_id.try_to_did()?.join(exchange_fragment)?; let mut signature = Signature::new(JcsEd25519::::NAME, did_url.to_string()); signature.set_value(SignatureValue::Signature(encode_b58(&signature_bytes))); diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index 691f0419..700da0ee 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -162,7 +162,7 @@ impl ContentSignSizeof for sizeof::Context { Identity::DID(did_impl) => match did_impl { DID::PrivateKey(info) => { let hash = [0; 64]; - let key_fragment = info.key_fragment().as_bytes().to_vec(); + let key_fragment = info.signing_fragment().as_bytes().to_vec(); let signature = [0; 64]; self.absorb(Uint8::new(1))? .absorb(Bytes::new(key_fragment))? @@ -198,14 +198,14 @@ where match did_impl { DID::PrivateKey(info) => { let mut hash = [0; 64]; - let key_fragment = info.key_fragment().as_bytes().to_vec(); + let key_fragment = info.signing_fragment().as_bytes().to_vec(); self.absorb(Uint8::new(1))? .absorb(Bytes::new(key_fragment))? .commit()? .squeeze(External::new(&mut NBytes::new(&mut hash)))?; let mut data = DataWrapper::new(&hash); - let fragment = format!("#{}", info.key_fragment()); + let fragment = format!("#{}", info.signing_fragment()); // Join the DID identifier with the key fragment of the verification method let method = info.did().clone().join(&fragment)?; JcsEd25519::::create_signature( diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index e382552a..5d4498db 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -29,9 +29,9 @@ const MASKED_PAYLOAD: &[u8] = b"MASKEDPAYLOAD"; pub async fn example(transport: Rc>) -> Result<()> { let did_client = DIDClient::new().await?; println!("> Making DID with method for the Author"); - let author_did_info = make_did_info(&did_client, "auth_key").await?; + let author_did_info = make_did_info(&did_client, "auth_key", "signing_key").await?; println!("> Making another DID with method for a Subscriber"); - let subscriber_did_info = make_did_info(&did_client, "sub_key").await?; + let subscriber_did_info = make_did_info(&did_client, "sub_key", "signing_key").await?; // Generate a simple PSK for storage by users let psk = Psk::from_seed("A pre shared key"); @@ -163,13 +163,13 @@ pub async fn example(transport: Rc>) -> Result<()> { Ok(()) } -async fn make_did_info(did_client: &DIDClient, fragment: &str) -> Result { +async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragment: &str) -> Result { // Create Keypair to act as base of identity let keypair = DIDKeyPair::new_ed25519()?; // Generate original DID document let mut document = IotaDocument::new(&keypair)?; // Sign document and publish to the tangle - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; + document.sign_self(keypair.private(), signing_fragment)?; let receipt = did_client.publish_document(&document).await?; let did = document.id().clone(); @@ -178,17 +178,17 @@ async fn make_did_info(did_client: &DIDClient, fragment: &str) -> Result Date: Wed, 29 Jun 2022 12:32:28 -0600 Subject: [PATCH 02/18] Update identity imports --- lets/Cargo.toml | 4 +- lets/src/id/did.rs | 38 +++++++++---------- lets/src/id/identifier.rs | 13 ++++--- lets/src/id/identity.rs | 10 ++--- streams/Cargo.toml | 2 +- .../examples/full-example/scenarios/did.rs | 11 +++--- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/lets/Cargo.toml b/lets/Cargo.toml index 822fe6cd..4e5252fd 100644 --- a/lets/Cargo.toml +++ b/lets/Cargo.toml @@ -20,7 +20,7 @@ tangle-client = ["iota-client/async", "futures", "iota-crypto/blake2b"] # Enable the wasm-compatible IOTA-Tangle transport client (incompatile with `tangle-client` feature due to `iota-client/async` using `tokio`. Implies `std` feature) tangle-client-wasm = ["iota-client/wasm", "futures"] # Enable Iota Identity for use with Streams -did = ["identity", "serde"] +did = ["identity_iota", "serde"] # # Enable synchronized transports using parking-lot sync-parking-lot = ["parking_lot"] # # Enable synchronized transports using spin @@ -41,7 +41,7 @@ hex = {version = "0.4", default-features = false} # Optional dependencies futures = {version = "0.3.8", default-features = false, optional = true} -identity = {git = "https://github.com/iotaledger/identity.rs", rev = "86edaad", default-features = false, features = ["async"], optional = true} +identity_iota = {git = "https://github.com/iotaledger/identity.rs", branch = "fix/update-stronghold-dependency", default-features = false, optional = true} iota-client = {version = "1.1.1", default-features = false, optional = true} parking_lot = {version = "0.11.2", default-features = false, optional = true} serde = {version = "1.0", default-features = false, features = ["derive"], optional = true} diff --git a/lets/src/id/did.rs b/lets/src/id/did.rs index 3abd89c4..3a572e18 100644 --- a/lets/src/id/did.rs +++ b/lets/src/id/did.rs @@ -15,11 +15,11 @@ use serde::Serialize; // IOTA use crypto::{keys::x25519, signatures::ed25519}; -use identity::{ - core::{decode_b58, encode_b58}, - crypto::{SetSignature, Signature, TrySignature, TrySignatureMut}, +use identity_iota::{ + core::BaseEncoding, + crypto::{SetSignature, Proof, GetSignature, GetSignatureMut, KeyType}, did::{MethodUriType, TryMethod, DID as IdentityDID}, - iota::IotaDID, + iota_core::IotaDID, }; // Streams @@ -44,7 +44,7 @@ impl DIDMethodId { pub(crate) fn from_did_unsafe(did: &IotaDID) -> Self { Self::new( - decode_b58(did.method_id()) + BaseEncoding::decode_base58(did.method_id()) .expect("decoding DID method-id") .try_into() .expect("DID method-id vector should fit into a 32 Byte array"), @@ -52,7 +52,7 @@ impl DIDMethodId { } pub(crate) fn try_to_did(&self) -> Result { - let did_str = DID_CORE.to_string() + &encode_b58(self); + let did_str = DID_CORE.to_string() + &BaseEncoding::encode_base58(self); Ok(IotaDID::parse(did_str)?) } } @@ -84,7 +84,7 @@ impl UpperHex for DIDMethodId { #[derive(Serialize)] pub(crate) struct DataWrapper<'a> { data: &'a [u8], - signature: Option, + signature: Option, } impl<'a> DataWrapper<'a> { @@ -92,30 +92,30 @@ impl<'a> DataWrapper<'a> { Self { data, signature: None } } - pub(crate) fn with_signature(mut self, signature: Signature) -> Self { + pub(crate) fn with_signature(mut self, signature: Proof) -> Self { self.signature = Some(signature); self } - pub(crate) fn into_signature(self) -> Option { + pub(crate) fn into_signature(self) -> Option { self.signature } } -impl<'a> TrySignature for DataWrapper<'a> { - fn signature(&self) -> Option<&Signature> { +impl<'a> GetSignature for DataWrapper<'a> { + fn signature(&self) -> Option<&Proof> { self.signature.as_ref() } } -impl<'a> TrySignatureMut for DataWrapper<'a> { - fn signature_mut(&mut self) -> Option<&mut Signature> { +impl<'a> GetSignatureMut for DataWrapper<'a> { + fn signature_mut(&mut self) -> Option<&mut Proof> { self.signature.as_mut() } } impl<'a> SetSignature for DataWrapper<'a> { - fn set_signature(&mut self, signature: Signature) { + fn set_signature(&mut self, signature: Proof) { self.signature = Some(signature) } } @@ -162,7 +162,7 @@ pub struct DIDInfo { } impl DIDInfo { - pub fn new(did: IotaDID, exchange_fragment: String, signing_fragment: String, keypair: identity::crypto::KeyPair) -> Self { + pub fn new(did: IotaDID, exchange_fragment: String, signing_fragment: String, keypair: identity_iota::crypto::KeyPair) -> Self { Self { did, exchange_fragment, @@ -182,7 +182,7 @@ impl DIDInfo { &self.signing_fragment } - pub(crate) fn keypair(&self) -> &identity::crypto::KeyPair { + pub(crate) fn keypair(&self) -> &identity_iota::crypto::KeyPair { &self.keypair.0 } @@ -198,7 +198,7 @@ impl DIDInfo { &mut self.signing_fragment } - fn keypair_mut(&mut self) -> &mut identity::crypto::KeyPair { + fn keypair_mut(&mut self) -> &mut identity_iota::crypto::KeyPair { &mut self.keypair.0 } @@ -218,7 +218,7 @@ impl DIDInfo { } } -struct KeyPair(identity::crypto::KeyPair); +struct KeyPair(identity_iota::crypto::KeyPair); impl PartialEq for KeyPair { fn eq(&self, other: &Self) -> bool { @@ -288,7 +288,7 @@ where *did.info_mut().exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; *did.info_mut().signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; - let keypair = identity::crypto::KeyPair::try_from_ed25519_bytes(&private_key_bytes) + let keypair = identity_iota::crypto::KeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; *did.info_mut().keypair_mut() = keypair; diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 37576571..697bdac0 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -10,11 +10,12 @@ use async_trait::async_trait; // IOTA use crypto::{keys::x25519, signatures::ed25519}; #[cfg(feature = "did")] -use identity::{ - core::encode_b58, - crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Signature, SignatureValue}, +use identity_iota::{ + core::BaseEncoding, + crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, did::{verifiable::VerifierOptions, DID as IdentityDID}, - iota::{Client as DIDClient, IotaDID}, + client::Client as DIDClient, + iota_core::IotaDID, }; // Streams @@ -235,8 +236,8 @@ where ); let did_url = method_id.try_to_did()?.join(exchange_fragment)?; - let mut signature = Signature::new(JcsEd25519::::NAME, did_url.to_string()); - signature.set_value(SignatureValue::Signature(encode_b58(&signature_bytes))); + let mut signature = Proof::new(JcsEd25519::::NAME, did_url.to_string()); + signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); let data = DataWrapper::new(&hash).with_signature(signature); diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index 700da0ee..a681f13b 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -12,9 +12,9 @@ use async_trait::async_trait; // IOTA use crypto::{keys::x25519, signatures::ed25519}; #[cfg(feature = "did")] -use identity::{ - core::decode_b58, - crypto::{Ed25519 as DIDEd25519, JcsEd25519, SignatureOptions, Signer}, +use identity_iota::{ + core::BaseEncoding, + crypto::{Ed25519 as DIDEd25519, JcsEd25519, ProofOptions, Signer}, did::DID as IdentityDID, }; @@ -212,9 +212,9 @@ where &mut data, method.to_string(), info.keypair().private().as_ref(), - SignatureOptions::new(), + ProofOptions::new(), )?; - let signature = decode_b58( + let signature = BaseEncoding::decode_base58( &data .into_signature() .ok_or_else(|| { diff --git a/streams/Cargo.toml b/streams/Cargo.toml index 62230b89..bfdca529 100644 --- a/streams/Cargo.toml +++ b/streams/Cargo.toml @@ -40,7 +40,7 @@ rand = {version = "0.8.5", default-features = false} [dev-dependencies] dotenv = {version = "0.15.0", default-features = false} hex = {version = "0.4.3", default-features = false} -identity = {git = "https://github.com/iotaledger/identity.rs", rev = "86edaad"} +identity_iota = {git = "https://github.com/iotaledger/identity.rs", branch = "fix/update-stronghold-dependency"} rand = {version = "0.8.5", default-features = false, features = ["std", "std_rng"]} textwrap = {version = "0.15.0", default-features = false} tokio = {version = "1.15", default-features = false} diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index 5d4498db..f5711404 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -7,10 +7,11 @@ use anyhow::{anyhow, Result}; use textwrap::{fill, indent}; // IOTA -use identity::{ +use identity_iota::{ core::Timestamp, did::MethodScope, - iota::IotaVerificationMethod, + crypto::KeyType, + iota_core::IotaVerificationMethod, prelude::{Client as DIDClient, IotaDocument, KeyPair as DIDKeyPair}, }; @@ -165,7 +166,7 @@ pub async fn example(transport: Rc>) -> Result<()> { async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragment: &str) -> Result { // Create Keypair to act as base of identity - let keypair = DIDKeyPair::new_ed25519()?; + let keypair = DIDKeyPair::new(KeyType::Ed25519)?; // Generate original DID document let mut document = IotaDocument::new(&keypair)?; // Sign document and publish to the tangle @@ -173,7 +174,7 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragm let receipt = did_client.publish_document(&document).await?; let did = document.id().clone(); - let streams_method_keys = DIDKeyPair::new_ed25519()?; + let streams_method_keys = DIDKeyPair::new(KeyType::Ed25519)?; let method = IotaVerificationMethod::new( did.clone(), streams_method_keys.type_(), @@ -182,7 +183,7 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragm )?; if document.insert_method(method, MethodScope::VerificationMethod).is_ok() { document.metadata.previous_message_id = *receipt.message_id(); - document.metadata.updated = Timestamp::now_utc(); + document.metadata.updated = Some(Timestamp::now_utc()); document.sign_self(keypair.private(), signing_fragment)?; let _update_receipt = did_client.publish_document(&document).await?; From 50714d7193615962aae7cbd970769380953911e3 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Wed, 29 Jun 2022 19:46:35 -0600 Subject: [PATCH 03/18] remove exchange keys mapping from user, wip --- lets/src/id/did.rs | 232 ++++++++++-------- lets/src/id/identifier.rs | 106 +++++--- lets/src/id/identity.rs | 22 +- lets/src/id/mod.rs | 2 +- lets/src/message/content.rs | 6 +- .../examples/full-example/scenarios/did.rs | 37 ++- streams/src/api/user.rs | 97 ++++---- streams/src/message/announcement.rs | 18 -- streams/src/message/keyload.rs | 20 +- streams/src/message/subscription.rs | 10 - 10 files changed, 304 insertions(+), 246 deletions(-) diff --git a/lets/src/id/did.rs b/lets/src/id/did.rs index 3a572e18..fa0204a9 100644 --- a/lets/src/id/did.rs +++ b/lets/src/id/did.rs @@ -1,13 +1,9 @@ // Rust use alloc::{ - string::{String, ToString}, + string::String, vec::Vec, }; -use core::{ - convert::TryInto, - fmt::{LowerHex, UpperHex}, - hash::Hash, -}; +use core::hash::Hash; // 3rd-party use anyhow::{anyhow, Result}; @@ -16,8 +12,7 @@ use serde::Serialize; // IOTA use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ - core::BaseEncoding, - crypto::{SetSignature, Proof, GetSignature, GetSignatureMut, KeyType}, + crypto::{SetSignature, Proof, GetSignature, GetSignatureMut, KeyType, KeyPair as DIDKeyPair}, did::{MethodUriType, TryMethod, DID as IdentityDID}, iota_core::IotaDID, }; @@ -32,55 +27,6 @@ use spongos::{ PRP, }; -pub(crate) const DID_CORE: &str = "did:iota:"; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] -pub struct DIDMethodId([u8; 32]); - -impl DIDMethodId { - pub(crate) fn new(method_id_bytes: [u8; 32]) -> Self { - Self(method_id_bytes) - } - - pub(crate) fn from_did_unsafe(did: &IotaDID) -> Self { - Self::new( - BaseEncoding::decode_base58(did.method_id()) - .expect("decoding DID method-id") - .try_into() - .expect("DID method-id vector should fit into a 32 Byte array"), - ) - } - - pub(crate) fn try_to_did(&self) -> Result { - let did_str = DID_CORE.to_string() + &BaseEncoding::encode_base58(self); - Ok(IotaDID::parse(did_str)?) - } -} - -impl AsRef<[u8]> for DIDMethodId { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsMut<[u8]> for DIDMethodId { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl LowerHex for DIDMethodId { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", hex::encode(self)) - } -} - -impl UpperHex for DIDMethodId { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", hex::encode_upper(self)) - } -} - #[derive(Serialize)] pub(crate) struct DataWrapper<'a> { data: &'a [u8], @@ -155,51 +101,55 @@ impl Default for DID { #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DIDInfo { - did: IotaDID, + url_info: DIDUrlInfo, + keypair: KeyPair, + exchange_keypair: KeyPair, +} + +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DIDUrlInfo { + did: String, + client_url: String, exchange_fragment: String, signing_fragment: String, - keypair: KeyPair, } impl DIDInfo { - pub fn new(did: IotaDID, exchange_fragment: String, signing_fragment: String, keypair: identity_iota::crypto::KeyPair) -> Self { + pub fn new(url_info: DIDUrlInfo, keypair: DIDKeyPair, exchange_keypair: DIDKeyPair) -> Self { Self { - did, - exchange_fragment, - signing_fragment, + url_info, keypair: KeyPair(keypair), + exchange_keypair: KeyPair(exchange_keypair) } } - pub(crate) fn did(&self) -> &IotaDID { - &self.did - } - pub(crate) fn exchange_fragment(&self) -> &str { - &self.exchange_fragment + pub fn url_info(&self) -> &DIDUrlInfo { + &self.url_info } - pub(crate) fn signing_fragment(&self) -> &str { - &self.signing_fragment + pub fn url_info_mut(&mut self) -> &mut DIDUrlInfo { + &mut self.url_info } - pub(crate) fn keypair(&self) -> &identity_iota::crypto::KeyPair { + pub(crate) fn keypair(&self) -> &DIDKeyPair { &self.keypair.0 } - fn did_mut(&mut self) -> &mut IotaDID { - &mut self.did + fn keypair_mut(&mut self) -> &mut DIDKeyPair { + &mut self.keypair.0 } - fn exchange_fragment_mut(&mut self) -> &mut String { - &mut self.exchange_fragment + fn exchange_keypair(&self) -> &DIDKeyPair { + &self.exchange_keypair.0 } - fn signing_fragment_mut(&mut self) -> &mut String { - &mut self.signing_fragment + fn exchange_keypair_mut(&mut self) -> &mut DIDKeyPair { + &mut self.exchange_keypair.0 } - fn keypair_mut(&mut self) -> &mut identity_iota::crypto::KeyPair { - &mut self.keypair.0 + pub(crate) fn exchange_key(&self) -> Result { + x25519::SecretKey::try_from_slice(self.exchange_keypair.0.private().as_ref()) + .map_err(|e| e.into()) } pub(crate) fn sig_kp(&self) -> (ed25519::SecretKey, ed25519::PublicKey) { @@ -218,6 +168,50 @@ impl DIDInfo { } } +impl DIDUrlInfo { + pub fn new>(did: IotaDID, client_url: T, exchange_fragment: T, signing_fragment: T) -> Self { + Self { + did: did.into_string(), + client_url: client_url.into(), + exchange_fragment: exchange_fragment.into(), + signing_fragment: signing_fragment.into(), + } + } + + pub(crate) fn did(&self) -> &str { + &self.did + } + + pub(crate) fn client_url(&self) -> &str { + &self.client_url + } + + pub(crate) fn exchange_fragment(&self) -> &str { + &self.exchange_fragment + } + + pub(crate) fn signing_fragment(&self) -> &str { + &self.signing_fragment + } + + pub(crate) fn did_mut(&mut self) -> &mut String { + &mut self.did + } + + pub(crate) fn client_url_mut(&mut self) -> &mut String { + &mut self.client_url + } + + pub(crate) fn exchange_fragment_mut(&mut self) -> &mut String { + &mut self.exchange_fragment + } + + pub(crate) fn signing_fragment_mut(&mut self) -> &mut String { + &mut self.signing_fragment + } +} + + struct KeyPair(identity_iota::crypto::KeyPair); impl PartialEq for KeyPair { @@ -249,10 +243,9 @@ impl Hash for KeyPair { impl Mask<&DID> for sizeof::Context { fn mask(&mut self, did: &DID) -> Result<&mut Self> { - self.mask(Bytes::new(did.info().did().as_str()))? - .mask(Bytes::new(did.info().exchange_fragment()))? - .mask(Bytes::new(did.info().signing_fragment()))? - .mask(NBytes::new(did.info().keypair().private())) + self.mask(did.info().url_info())? + .mask(NBytes::new(did.info().keypair().private()))? + .mask(NBytes::new(did.info().exchange_keypair().private())) } } @@ -262,10 +255,9 @@ where OS: io::OStream, { fn mask(&mut self, did: &DID) -> Result<&mut Self> { - self.mask(Bytes::new(did.info().did().as_str()))? - .mask(Bytes::new(did.info().exchange_fragment()))? - .mask(Bytes::new(did.info().signing_fragment()))? - .mask(NBytes::new(did.info().keypair().private())) + self.mask(did.info().url_info())? + .mask(NBytes::new(did.info().keypair().private()))? + .mask(NBytes::new(did.info().exchange_keypair().private())) } } @@ -275,23 +267,67 @@ where IS: io::IStream, { fn mask(&mut self, did: &mut DID) -> Result<&mut Self> { - let mut did_bytes = Vec::new(); - let mut exchange_fragment_bytes = Vec::new(); - let mut signing_fragment_bytes = Vec::new(); + let mut url_info = DIDUrlInfo::default(); let mut private_key_bytes = [0; ed25519::SECRET_KEY_LENGTH]; - self.mask(Bytes::new(&mut did_bytes))? - .mask(Bytes::new(&mut exchange_fragment_bytes))? - .mask(Bytes::new(&mut signing_fragment_bytes))? - .mask(NBytes::new(&mut private_key_bytes))?; - - *did.info_mut().did_mut() = core::str::from_utf8(&did_bytes)?.try_into()?; - *did.info_mut().exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; - *did.info_mut().signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; + let mut exchange_private_key_bytes = [0; x25519::SECRET_KEY_LENGTH]; + self.mask(&mut url_info)? + .mask(NBytes::new(&mut private_key_bytes))? + .mask(NBytes::new(&mut exchange_private_key_bytes))?; let keypair = identity_iota::crypto::KeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; + let xkeypair = identity_iota::crypto::KeyPair::try_from_private_key_bytes(KeyType::X25519, &exchange_private_key_bytes) + .map_err(|e| anyhow!("error unmasking DID exchange private key: {}", e))?; *did.info_mut().keypair_mut() = keypair; + *did.info_mut().exchange_keypair_mut() = xkeypair; Ok(self) } } + + + +impl Mask<&DIDUrlInfo> for sizeof::Context { + fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { + self.mask(Bytes::new(url_info.did()))? + .mask(Bytes::new(url_info.client_url()))? + .mask(Bytes::new(url_info.exchange_fragment()))? + .mask(Bytes::new(url_info.signing_fragment())) + } +} + +impl Mask<&DIDUrlInfo> for wrap::Context + where + F: PRP, + OS: io::OStream, +{ + fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { + self.mask(Bytes::new(url_info.did()))? + .mask(Bytes::new(url_info.client_url()))? + .mask(Bytes::new(url_info.exchange_fragment()))? + .mask(Bytes::new(url_info.signing_fragment())) + } +} + +impl Mask<&mut DIDUrlInfo> for unwrap::Context + where + F: PRP, + IS: io::IStream, +{ + fn mask(&mut self, url_info: &mut DIDUrlInfo) -> Result<&mut Self> { + let mut did_bytes = Vec::new(); + let mut client_url = Vec::new(); + let mut exchange_fragment_bytes = Vec::new(); + let mut signing_fragment_bytes = Vec::new(); + self.mask(Bytes::new(&mut did_bytes))? + .mask(Bytes::new(&mut client_url))? + .mask(Bytes::new(&mut exchange_fragment_bytes))? + .mask(Bytes::new(&mut signing_fragment_bytes))?; + + *url_info.did_mut() = String::from_utf8(did_bytes)?; + *url_info.client_url_mut() = String::from_utf8(client_url)?; + *url_info.exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; + *url_info.signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; + Ok(self) + } +} \ No newline at end of file diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 697bdac0..17f640eb 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -14,7 +14,7 @@ use identity_iota::{ core::BaseEncoding, crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, did::{verifiable::VerifierOptions, DID as IdentityDID}, - client::Client as DIDClient, + client::{Client as DIDClient, ResolvedIotaDocument}, iota_core::IotaDID, }; @@ -31,14 +31,14 @@ use spongos::{ // Local #[cfg(feature = "did")] -use crate::id::did::{DIDMethodId, DataWrapper}; +use crate::id::did::{DIDUrlInfo, DataWrapper}; use crate::message::{ContentEncrypt, ContentEncryptSizeOf, ContentVerify}; #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Identifier { Ed25519(ed25519::PublicKey), #[cfg(feature = "did")] - DID(DIDMethodId), + DID(DIDUrlInfo), } impl core::fmt::Debug for Identifier { @@ -46,7 +46,7 @@ impl core::fmt::Debug for Identifier { match self { Self::Ed25519(arg0) => f.debug_tuple("Ed25519").field(&hex::encode(&arg0)).finish(), #[cfg(feature = "did")] - Self::DID(arg0) => f.debug_tuple("DID").field(&hex::encode(arg0)).finish(), + Self::DID(url_info) => f.debug_tuple("DID").field(&url_info.did().to_string()).finish(), } } } @@ -57,7 +57,7 @@ impl Identifier { match self { Identifier::Ed25519(public_key) => public_key.as_slice(), #[cfg(feature = "did")] - Identifier::DID(did) => did.as_ref(), + Identifier::DID(url_info) => url_info.did().as_bytes(), } } @@ -70,12 +70,18 @@ impl Identifier { } // #[deprecated = "to be removed once key-exchange is encapsulated within Identity"] - pub fn _ke_pk(&self) -> Option { - Some( - self.public_key()? - .try_into() - .expect("failed to convert ed25519 public-key to x25519 public-key"), - ) + pub async fn _ke_pk(&self) -> Result { + match self { + Identifier::Ed25519(pk) => Ok(pk.try_into()?), + #[cfg(feature = "did")] + Identifier::DID(url_info) => { + let doc = resolve_document(url_info).await?; + let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + .expect("DID Method could not be resolved"); + Ok(x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?) + } + } + } pub fn is_ed25519(&self) -> bool { @@ -97,9 +103,9 @@ impl From for Identifier { } #[cfg(feature = "did")] -impl From<&IotaDID> for Identifier { - fn from(did: &IotaDID) -> Self { - Identifier::DID(DIDMethodId::from_did_unsafe(did)) +impl From<&DIDUrlInfo> for Identifier { + fn from(did: &DIDUrlInfo) -> Self { + Identifier::DID(did.clone()) } } @@ -136,9 +142,9 @@ impl Mask<&Identifier> for sizeof::Context { Ok(self) } #[cfg(feature = "did")] - Identifier::DID(did) => { + Identifier::DID(url_info) => { let oneof = Uint8::new(1); - self.mask(oneof)?.mask(NBytes::new(did))?; + self.mask(oneof)?.mask(url_info)?; Ok(self) } } @@ -158,9 +164,9 @@ where Ok(self) } #[cfg(feature = "did")] - Identifier::DID(did) => { + Identifier::DID(url_info) => { let oneof = Uint8::new(1); - self.mask(oneof)?.mask(NBytes::new(did))?; + self.mask(oneof)?.mask(url_info)?; Ok(self) } } @@ -183,10 +189,9 @@ where } #[cfg(feature = "did")] 1 => { - let mut method_id = DIDMethodId::default(); - self.mask(NBytes::new(&mut method_id))?; - let did = method_id.try_to_did()?; - *identifier = Identifier::DID(DIDMethodId::from_did_unsafe(&did)); + let mut url_info = DIDUrlInfo::default(); + self.mask(&mut url_info)?; + *identifier = Identifier::DID(url_info); } o => return Err(anyhow!("{} is not a valid identifier option", o)), } @@ -216,7 +221,7 @@ where }, #[cfg(feature = "did")] 1 => match verifier { - Identifier::DID(method_id) => { + Identifier::DID(url_info) => { let mut hash = [0; 64]; let mut exchange_fragment_bytes = Bytes::default(); let mut fragment_bytes = Bytes::default(); @@ -235,13 +240,14 @@ where .ok_or_else(|| anyhow!("fragment must be UTF8 encoded"))? ); - let did_url = method_id.try_to_did()?.join(exchange_fragment)?; + let did_url = IotaDID::parse(url_info.did().to_string())? + .join(exchange_fragment)?; let mut signature = Proof::new(JcsEd25519::::NAME, did_url.to_string()); signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); let data = DataWrapper::new(&hash).with_signature(signature); - let doc = DIDClient::new().await?.read_document(did_url.did()).await?; + let doc = resolve_document(&url_info).await?; doc.document .verify_data(&data, &VerifierOptions::new()) .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; @@ -257,12 +263,23 @@ where // TODO: Find a better way to represent this logic without the need for an additional trait #[async_trait(?Send)] impl ContentEncryptSizeOf for sizeof::Context { - async fn encrypt_sizeof(&mut self, _recipient: &Identifier, exchange_key: &[u8], key: &[u8]) -> Result<&mut Self> { + async fn encrypt_sizeof(&mut self, recipient: &Identifier, key: &[u8]) -> Result<&mut Self> { // TODO: Replace with separate logic for EdPubKey and DID instances (pending Identity xkey // introdution) - match <[u8; 32]>::try_from(exchange_key) { - Ok(slice) => self.x25519(&x25519::PublicKey::from(slice), NBytes::new(key)), - Err(e) => Err(anyhow!("Invalid x25519 key: {}", e)), + match recipient { + Identifier::Ed25519(pk) => { + let xkey = x25519::PublicKey::try_from(pk)?; + self.x25519(&xkey, NBytes::new(key)) + }, + #[cfg(feature = "did")] + Identifier::DID(url_info) => { + let doc = resolve_document(url_info).await?; + let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + .expect("DID Method could not be resolved"); + let xkey = x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?; + self.x25519(&xkey, NBytes::new(key)) + + } } } } @@ -273,12 +290,35 @@ where F: PRP, OS: io::OStream, { - async fn encrypt(&mut self, _recipient: &Identifier, exchange_key: &[u8], key: &[u8]) -> Result<&mut Self> { + async fn encrypt(&mut self, recipient: &Identifier, key: &[u8]) -> Result<&mut Self> { // TODO: Replace with separate logic for EdPubKey and DID instances (pending Identity xkey // introdution) - match <[u8; 32]>::try_from(exchange_key) { - Ok(byte_array) => self.x25519(&x25519::PublicKey::from(byte_array), NBytes::new(key)), - Err(e) => Err(anyhow!("Invalid x25519 key: {}", e)), + match recipient { + Identifier::Ed25519(pk) => { + let xkey = x25519::PublicKey::try_from(pk)?; + self.x25519(&xkey, NBytes::new(key)) + }, + #[cfg(feature = "did")] + Identifier::DID(url_info) => { + let doc = resolve_document(url_info).await?; + let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + .expect("DID Method could not be resolved"); + let xkey = x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?; + self.x25519(&xkey, NBytes::new(key)) + + } } } } + +pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { + let did_url = IotaDID::parse(url_info.did().to_string())?; + let doc = DIDClient::builder() + .network(did_url.network()?) + .primary_node(url_info.client_url(), None, None)? + .build() + .await? + .read_document(&did_url) + .await?; + Ok(doc) +} diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index a681f13b..467d6861 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -1,7 +1,7 @@ // Rust use alloc::{boxed::Box, string::ToString}; use core::{ - convert::{AsRef, TryFrom}, + convert::AsRef, hash::Hash, }; @@ -17,6 +17,7 @@ use identity_iota::{ crypto::{Ed25519 as DIDEd25519, JcsEd25519, ProofOptions, Signer}, did::DID as IdentityDID, }; +use identity_iota::iota_core::IotaDID; // IOTA-Streams use spongos::{ @@ -70,7 +71,7 @@ impl Identity { match self { Self::Ed25519(ed25519) => ed25519.inner().public_key().into(), #[cfg(feature = "did")] - Self::DID(did) => did.info().did().into(), + Self::DID(did) => did.info().url_info().into(), } } } @@ -162,7 +163,7 @@ impl ContentSignSizeof for sizeof::Context { Identity::DID(did_impl) => match did_impl { DID::PrivateKey(info) => { let hash = [0; 64]; - let key_fragment = info.signing_fragment().as_bytes().to_vec(); + let key_fragment = info.url_info().signing_fragment().as_bytes().to_vec(); let signature = [0; 64]; self.absorb(Uint8::new(1))? .absorb(Bytes::new(key_fragment))? @@ -198,16 +199,16 @@ where match did_impl { DID::PrivateKey(info) => { let mut hash = [0; 64]; - let key_fragment = info.signing_fragment().as_bytes().to_vec(); + let key_fragment = info.url_info().signing_fragment().as_bytes().to_vec(); self.absorb(Uint8::new(1))? .absorb(Bytes::new(key_fragment))? .commit()? .squeeze(External::new(&mut NBytes::new(&mut hash)))?; let mut data = DataWrapper::new(&hash); - let fragment = format!("#{}", info.signing_fragment()); + let fragment = format!("#{}", info.url_info().signing_fragment()); // Join the DID identifier with the key fragment of the verification method - let method = info.did().clone().join(&fragment)?; + let method = IotaDID::parse(info.url_info().did())?.join(&fragment)?; JcsEd25519::::create_signature( &mut data, method.to_string(), @@ -239,12 +240,13 @@ where F: PRP, IS: io::IStream, { - async fn decrypt(&mut self, _recipient: &Identity, exchange_key: &[u8], key: &mut [u8]) -> Result<&mut Self> { + async fn decrypt(&mut self, recipient: &Identity, key: &mut [u8]) -> Result<&mut Self> { // TODO: Replace with separate logic for EdPubKey and DID instances (pending Identity xkey // introduction) - match <[u8; 32]>::try_from(exchange_key) { - Ok(byte_array) => self.x25519(&x25519::SecretKey::from_bytes(byte_array), NBytes::new(key)), - Err(e) => Err(anyhow!("Invalid x25519 key: {}", e)), + match recipient { + Identity::Ed25519(kp) => self.x25519(&kp.inner().into(), NBytes::new(key)), + #[cfg(feature = "did")] + Identity::DID(did) => self.x25519(&did.info().exchange_key()?, NBytes::new(key)) } } } diff --git a/lets/src/id/mod.rs b/lets/src/id/mod.rs index 1c1fd4c7..c980e3ad 100644 --- a/lets/src/id/mod.rs +++ b/lets/src/id/mod.rs @@ -14,4 +14,4 @@ pub use psk::{Psk, PskId}; mod did; #[cfg(feature = "did")] -pub use did::{DIDInfo, DID}; +pub use did::{DIDInfo, DID, DIDUrlInfo}; diff --git a/lets/src/message/content.rs b/lets/src/message/content.rs index be2014eb..7b4d12c0 100644 --- a/lets/src/message/content.rs +++ b/lets/src/message/content.rs @@ -45,15 +45,15 @@ pub trait ContentVerify { #[async_trait(?Send)] pub trait ContentEncryptSizeOf { - async fn encrypt_sizeof(&mut self, recipient: &T, exchange_key: &[u8], key: &[u8]) -> Result<&mut Self>; + async fn encrypt_sizeof(&mut self, recipient: &T, key: &[u8]) -> Result<&mut Self>; } #[async_trait(?Send)] pub trait ContentEncrypt { - async fn encrypt(&mut self, recipient: &T, exchange_key: &[u8], key: &[u8]) -> Result<&mut Self>; + async fn encrypt(&mut self, recipient: &T, key: &[u8]) -> Result<&mut Self>; } #[async_trait(?Send)] pub trait ContentDecrypt { - async fn decrypt(&mut self, recipient: &T, exchange_key: &[u8], key: &mut [u8]) -> Result<&mut Self>; + async fn decrypt(&mut self, recipient: &T, key: &mut [u8]) -> Result<&mut Self>; } diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index f5711404..7475e2a1 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -17,7 +17,7 @@ use identity_iota::{ // Streams use streams::{ - id::{DIDInfo, Ed25519, Permissioned, Psk, DID}, + id::{DIDInfo, DIDUrlInfo, Ed25519, Permissioned, Psk, DID}, transport::tangle, User, }; @@ -26,13 +26,17 @@ use super::utils::{print_send_result, print_user}; const PUBLIC_PAYLOAD: &[u8] = b"PUBLICPAYLOAD"; const MASKED_PAYLOAD: &[u8] = b"MASKEDPAYLOAD"; +const CLIENT_URL: &str = "https://chrysalis-nodes.iota.org"; pub async fn example(transport: Rc>) -> Result<()> { - let did_client = DIDClient::new().await?; + let did_client = DIDClient::builder() + .primary_node(CLIENT_URL, None, None)? + .build() + .await?; println!("> Making DID with method for the Author"); - let author_did_info = make_did_info(&did_client, "auth_key", "signing_key").await?; + let author_did_info = make_did_info(&did_client, "auth_key", "auth_xkey", "signing_key").await?; println!("> Making another DID with method for a Subscriber"); - let subscriber_did_info = make_did_info(&did_client, "sub_key", "signing_key").await?; + let subscriber_did_info = make_did_info(&did_client, "sub_key", "sub_xkey", "signing_key").await?; // Generate a simple PSK for storage by users let psk = Psk::from_seed("A pre shared key"); @@ -164,7 +168,7 @@ pub async fn example(transport: Rc>) -> Result<()> { Ok(()) } -async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragment: &str) -> Result { +async fn make_did_info(did_client: &DIDClient, key_fragment: &str, exchange_fragment: &str, signing_fragment: &str) -> Result { // Create Keypair to act as base of identity let keypair = DIDKeyPair::new(KeyType::Ed25519)?; // Generate original DID document @@ -174,14 +178,26 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragm let receipt = did_client.publish_document(&document).await?; let did = document.id().clone(); - let streams_method_keys = DIDKeyPair::new(KeyType::Ed25519)?; + // Create a signature verification keypair and method + let streams_signing_keys = DIDKeyPair::new(KeyType::Ed25519)?; let method = IotaVerificationMethod::new( did.clone(), - streams_method_keys.type_(), - streams_method_keys.public(), + streams_signing_keys.type_(), + streams_signing_keys.public(), key_fragment, )?; - if document.insert_method(method, MethodScope::VerificationMethod).is_ok() { + + // Create a second Keypair for key exchange method + let streams_exchange_keys = DIDKeyPair::new(KeyType::X25519)?; + let xmethod = IotaVerificationMethod::new( + did.clone(), + streams_exchange_keys.type_(), + streams_exchange_keys.public(), + exchange_fragment, + )?; + + if document.insert_method(method, MethodScope::VerificationMethod).is_ok() + && document.insert_method(xmethod, MethodScope::key_agreement()).is_ok() { document.metadata.previous_message_id = *receipt.message_id(); document.metadata.updated = Some(Timestamp::now_utc()); document.sign_self(keypair.private(), signing_fragment)?; @@ -191,5 +207,6 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, signing_fragm return Err(anyhow!("Failed to update method")); } - Ok(DIDInfo::new(did, key_fragment.to_string(),signing_fragment.to_string(), streams_method_keys)) + let url_info = DIDUrlInfo::new(did, CLIENT_URL, key_fragment, exchange_fragment); + Ok(DIDInfo::new(url_info, streams_signing_keys, streams_exchange_keys)) } diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 0f04b606..24543c0f 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -11,6 +11,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; // IOTA use crypto::keys::x25519; +use crypto::keys::x25519::PublicKey; // Streams use lets::{ @@ -22,6 +23,7 @@ use lets::{ }, transport::Transport, }; +use lets::id::DIDUrlInfo; use spongos::{ ddml::{ commands::{sizeof, unwrap, wrap, Absorb, Commit, Mask, Squeeze}, @@ -66,8 +68,8 @@ struct State { /// Mapping of trusted pre shared keys and identifiers psk_store: HashMap, - /// Mapping of exchange keys and identifiers - exchange_keys: HashMap, + /// List of Subscribed Identifiers + subscribers: Vec, spongos_store: HashMap, @@ -92,7 +94,7 @@ impl User { Psks: IntoIterator, { let mut psk_store = HashMap::new(); - let mut exchange_keys = HashMap::new(); + let mut subscribers = Vec::new(); // Store any pre shared keys psks.into_iter().for_each(|(pskid, psk)| { @@ -100,7 +102,7 @@ impl User { }); if let Some(id) = user_id.as_ref() { - exchange_keys.insert(id.to_identifier(), id._ke_sk().public_key()); + subscribers.push(id.to_identifier()); } Self { @@ -109,7 +111,7 @@ impl User { user_id, cursor_store: CursorStore::new(), psk_store, - exchange_keys, + subscribers, spongos_store: Default::default(), stream_address: None, author_identifier: None, @@ -154,6 +156,7 @@ impl User { pub fn transport(&self) -> &T { &self.transport } + pub fn transport_mut(&mut self) -> &mut T { &mut self.transport } @@ -167,7 +170,7 @@ impl User { } pub fn subscribers(&self) -> impl Iterator + Clone + '_ { - self.state.exchange_keys.keys() + self.state.subscribers.iter() } fn should_store_cursor(&self, topic: &Topic, subscriber: Permissioned<&Identifier>) -> bool { @@ -179,14 +182,22 @@ impl User { } pub fn add_subscriber(&mut self, subscriber: Identifier) -> bool { - let ke_pk = subscriber - ._ke_pk() - .expect("subscriber must have an identifier from which an x25519 public key can be derived"); - self.state.exchange_keys.insert(subscriber, ke_pk).is_none() + if self.state.subscribers.contains(&subscriber) { + return false + } + self.state.subscribers.push(subscriber); + true } pub fn remove_subscriber(&mut self, id: &Identifier) -> bool { - self.state.cursor_store.remove(id) | self.state.exchange_keys.remove(id).is_some() + let removed_sub = match self.state.subscribers.iter().position(|i| i == id) { + Some(p) => { + self.state.subscribers.remove(p); + true + }, + None => false + }; + self.state.cursor_store.remove(id) | removed_sub } pub fn add_psk(&mut self, psk: Psk) -> bool { @@ -268,7 +279,6 @@ impl User { // Store message content into stores let topic = message.header().topic(); let author_id = message.payload().content().author_id(); - let author_ke_pk = message.payload().content().author_ke_pk(); // Update branch links self.set_anchor(topic, address.relative()); @@ -276,7 +286,7 @@ impl User { self.state.author_identifier = Some(author_id.clone()); if is_base_branch { - self.state.exchange_keys.insert(author_id.clone(), author_ke_pk.clone()); + self.add_subscriber(author_id.clone()); self.state.base_branch = topic.clone(); self.state.stream_address = Some(address); } @@ -309,10 +319,7 @@ impl User { // Store message content into stores let subscriber_identifier = message.payload().content().subscriber_identifier(); - let subscriber_ke_pk = message.payload().content().subscriber_ke_pk(); - self.state - .exchange_keys - .insert(subscriber_identifier.clone(), subscriber_ke_pk); + self.state.subscribers.push(subscriber_identifier.clone()); Ok(Message::from_lets_message(address, message)) } @@ -676,13 +683,14 @@ where .state .author_identifier .as_ref() - .and_then(|author_id| self.state.exchange_keys.get(author_id)) - .expect("a user that already have an stream address must know the author identifier"); + .expect("a user that already have an stream address must know the author identifier") + ._ke_pk() + .await?; let content = PCF::new_final_frame().with_content(subscription::Wrap::new( &mut linked_msg_spongos, unsubscribe_key, user_id, - author_ke_pk, + &author_ke_pk, )); let header = HDF::new( message_types::SUBSCRIPTION, @@ -772,7 +780,7 @@ where psk_ids: Psks, ) -> Result> where - Subscribers: IntoIterator>, + Subscribers: IntoIterator> + Clone, Top: Into, Psks: IntoIterator, { @@ -804,18 +812,6 @@ where let mut rng = StdRng::from_entropy(); let encryption_key = rng.gen(); let nonce = rng.gen(); - let exchange_keys = &self.state.exchange_keys; // partial borrow to avoid borrowing the whole self within the closure - let subscribers_with_keys = subscribers - .into_iter() - .map(|subscriber| { - Ok(( - subscriber, - exchange_keys - .get(subscriber.identifier()) - .ok_or_else(|| anyhow!("unknown subscriber '{}'", subscriber.identifier()))?, - )) - }) - .collect::>>()?; // collect to handle possible error let psk_ids_with_psks = psk_ids .into_iter() .map(|pskid| { @@ -830,7 +826,7 @@ where .collect::>>()?; // collect to handle possible error let content = PCF::new_final_frame().with_content(keyload::Wrap::new( &mut linked_msg_spongos, - subscribers_with_keys.iter().copied(), + subscribers.clone().into_iter().collect::>(), &psk_ids_with_psks, encryption_key, nonce, @@ -851,7 +847,7 @@ where let send_response = self.transport.send_message(message_address, transport_msg).await?; // If message has been sent successfully, commit message to stores - for (subscriber, _) in subscribers_with_keys { + for subscriber in subscribers { if self.should_store_cursor(&topic, subscriber) { self.state .cursor_store @@ -1080,11 +1076,11 @@ impl ContentSizeof for sizeof::Context { } } - let keys = &user_state.exchange_keys; - let amount_keys = keys.len(); - self.mask(Size::new(amount_keys))?; - for (subscriber, ke_pk) in keys { - self.mask(subscriber)?.mask(ke_pk)?; + let subs = &user_state.subscribers; + let amount_subs = subs.len(); + self.mask(Size::new(amount_subs))?; + for subscriber in subs { + self.mask(subscriber)?; } let psks = user_state.psk_store.iter(); @@ -1141,11 +1137,11 @@ impl<'a> ContentWrap for wrap::Context<&'a mut [u8]> { } } - let keys = &user_state.exchange_keys; - let amount_keys = keys.len(); - self.mask(Size::new(amount_keys))?; - for (subscriber, ke_pk) in keys { - self.mask(subscriber)?.mask(ke_pk)?; + let subs = &user_state.subscribers; + let amount_subs = subs.len(); + self.mask(Size::new(amount_subs))?; + for subscriber in subs { + self.mask(subscriber)?; } let psks = user_state.psk_store.iter(); @@ -1202,13 +1198,12 @@ impl<'a> ContentUnwrap for unwrap::Context<&'a [u8]> { } } - let mut amount_keys = Size::default(); - self.mask(&mut amount_keys)?; - for _ in 0..amount_keys.inner() { + let mut amount_subs = Size::default(); + self.mask(&mut amount_subs)?; + for _ in 0..amount_subs.inner() { let mut subscriber = Identifier::default(); - let mut key = x25519::PublicKey::from_bytes([0; x25519::PUBLIC_KEY_LENGTH]); - self.mask(&mut subscriber)?.mask(&mut key)?; - user_state.exchange_keys.insert(subscriber, key); + self.mask(&mut subscriber)?; + user_state.subscribers.push(subscriber); } let mut amount_psks = Size::default(); diff --git a/streams/src/message/announcement.rs b/streams/src/message/announcement.rs index 13c1b09f..fd387c3b 100644 --- a/streams/src/message/announcement.rs +++ b/streams/src/message/announcement.rs @@ -54,8 +54,6 @@ impl<'a> Wrap<'a> { impl<'a> ContentSizeof> for sizeof::Context { async fn sizeof(&mut self, announcement: &Wrap<'a>) -> Result<&mut Self> { self.mask(&announcement.user_id.to_identifier())? - // TODO: REMOVE ONCE KE IS ENCAPSULATED WITHIN IDENTITY - .absorb(&announcement.user_id._ke_sk().public_key())? .sign_sizeof(announcement.user_id) .await? .commit()?; @@ -70,8 +68,6 @@ where { async fn wrap(&mut self, announcement: &mut Wrap<'a>) -> Result<&mut Self> { self.mask(&announcement.user_id.to_identifier())? - // TODO: REMOVE ONCE KE IS ENCAPSULATED WITHIN IDENTITY - .absorb(&announcement.user_id._ke_sk().public_key())? .sign(announcement.user_id) .await? .commit()?; @@ -82,17 +78,13 @@ where #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Unwrap { author_id: Identifier, - // TODO: REMOVE ONCE KE IS ENCAPSULATED WITHIN IDENTITY - author_ke_pk: x25519::PublicKey, } impl Default for Unwrap { fn default() -> Self { let author_id = Default::default(); - let author_ke_pk = x25519::PublicKey::from_bytes([0; x25519::PUBLIC_KEY_LENGTH]); Self { author_id, - author_ke_pk, } } } @@ -105,15 +97,6 @@ impl Unwrap { pub(crate) fn into_author_id(self) -> Identifier { self.author_id } - - // #[deprecated = "to be removed once ke is encapsulated within identity"] - pub(crate) fn author_ke_pk(&self) -> &x25519::PublicKey { - &self.author_ke_pk - } - - pub(crate) fn into_parts(self) -> (Identifier, x25519::PublicKey) { - (self.author_id, self.author_ke_pk) - } } #[async_trait(?Send)] @@ -124,7 +107,6 @@ where { async fn unwrap(&mut self, announcement: &mut Unwrap) -> Result<&mut Self> { self.mask(&mut announcement.author_id)? - .absorb(&mut announcement.author_ke_pk)? .verify(&announcement.author_id) .await? .commit()?; diff --git a/streams/src/message/keyload.rs b/streams/src/message/keyload.rs index 8716471e..c9e27708 100644 --- a/streams/src/message/keyload.rs +++ b/streams/src/message/keyload.rs @@ -86,7 +86,7 @@ impl<'a, Subscribers, Psks> Wrap<'a, Subscribers, Psks> { author_id: &'a Identity, ) -> Self where - Subscribers: IntoIterator, &'a x25519::PublicKey)>, + Subscribers: IntoIterator>, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, @@ -105,7 +105,7 @@ impl<'a, Subscribers, Psks> Wrap<'a, Subscribers, Psks> { #[async_trait(?Send)] impl<'a, Subscribers, Psks> message::ContentSizeof> for sizeof::Context where - Subscribers: IntoIterator, &'a x25519::PublicKey)> + Clone, + Subscribers: IntoIterator> + Clone, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, @@ -117,10 +117,10 @@ where let n_psks = Size::new(psks.len()); self.absorb(NBytes::new(keyload.nonce))?.absorb(n_subscribers)?; // Loop through provided identifiers, masking the shared key for each one - for (subscriber, exchange_key) in subscribers { + for subscriber in subscribers { self.fork() .mask(subscriber)? - .encrypt_sizeof(subscriber.identifier(), &exchange_key.to_bytes(), &keyload.key) + .encrypt_sizeof(subscriber.identifier(), &keyload.key) .await?; } self.absorb(n_psks)?; @@ -143,7 +143,7 @@ where #[async_trait(?Send)] impl<'a, OS, Subscribers, Psks> message::ContentWrap> for wrap::Context where - Subscribers: IntoIterator, &'a x25519::PublicKey)> + Clone, + Subscribers: IntoIterator> + Clone, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, @@ -158,10 +158,10 @@ where .absorb(NBytes::new(keyload.nonce))? .absorb(n_subscribers)?; // Loop through provided identifiers, masking the shared key for each one - for (subscriber, exchange_key) in subscribers { + for subscriber in subscribers { self.fork() .mask(subscriber)? - .encrypt(subscriber.identifier(), &exchange_key.to_bytes(), &keyload.key) + .encrypt(subscriber.identifier(), &keyload.key) .await?; } self.absorb(n_psks)?; @@ -235,11 +235,7 @@ where if key.is_none() && keyload.user_id.is_some() { let user_id = keyload.user_id.unwrap(); if subscriber_id.identifier() == &user_id.to_identifier() { - fork.decrypt( - user_id, - &user_id._ke_sk().to_bytes(), - key.get_or_insert([0u8; KEY_SIZE]), - ) + fork.decrypt(user_id, key.get_or_insert([0u8; KEY_SIZE])) .await?; } else { fork.drop(KEY_SIZE + x25519::PUBLIC_KEY_LENGTH)?; diff --git a/streams/src/message/subscription.rs b/streams/src/message/subscription.rs index d52dd1db..92d4c9ac 100644 --- a/streams/src/message/subscription.rs +++ b/streams/src/message/subscription.rs @@ -79,7 +79,6 @@ impl<'a> ContentSizeof> for sizeof::Context { async fn sizeof(&mut self, subscription: &Wrap<'a>) -> Result<&mut Self> { self.x25519(subscription.author_ke_pk, NBytes::new(subscription.unsubscribe_key))? .mask(&subscription.subscriber_id.to_identifier())? - .absorb(&subscription.subscriber_id._ke_sk().public_key())? .sign_sizeof(subscription.subscriber_id) .await?; Ok(self) @@ -95,7 +94,6 @@ where self.join(subscription.initial_state)? .x25519(subscription.author_ke_pk, NBytes::new(subscription.unsubscribe_key))? .mask(&subscription.subscriber_id.to_identifier())? - .absorb(&subscription.subscriber_id._ke_sk().public_key())? .sign(subscription.subscriber_id) .await?; Ok(self) @@ -106,8 +104,6 @@ pub(crate) struct Unwrap<'a> { initial_state: &'a mut Spongos, unsubscribe_key: [u8; 32], subscriber_identifier: Identifier, - // TODO: REMOVE ONCE KE IS ENCAPSULATED WITHIN IDENTITY - subscriber_ke_pk: x25519::PublicKey, author_ke_sk: &'a x25519::SecretKey, } @@ -117,7 +113,6 @@ impl<'a> Unwrap<'a> { initial_state, unsubscribe_key: Default::default(), subscriber_identifier: Default::default(), - subscriber_ke_pk: x25519::PublicKey::from_bytes([0; x25519::PUBLIC_KEY_LENGTH]), author_ke_sk, } } @@ -130,10 +125,6 @@ impl<'a> Unwrap<'a> { self.subscriber_identifier } - // #[deprecated = "to be removed once ke is encapsulated within identity"] - pub(crate) fn subscriber_ke_pk(&self) -> x25519::PublicKey { - self.subscriber_ke_pk - } } #[async_trait(?Send)] @@ -148,7 +139,6 @@ where NBytes::new(&mut subscription.unsubscribe_key), )? .mask(&mut subscription.subscriber_identifier)? - .absorb(&mut subscription.subscriber_ke_pk)? .verify(&subscription.subscriber_identifier) .await?; Ok(self) From f4164d64dc75d186173038bcf283deedfdab09e3 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 30 Jun 2022 12:49:08 -0600 Subject: [PATCH 04/18] fix signing key discrepancies --- lets/src/address.rs | 8 ++++++-- lets/src/id/identifier.rs | 10 ++++------ streams/examples/full-example/scenarios/did.rs | 12 ++++++------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lets/src/address.rs b/lets/src/address.rs index bc4d4acc..54269255 100644 --- a/lets/src/address.rs +++ b/lets/src/address.rs @@ -146,8 +146,12 @@ impl AppAddr { let id_bytes = identifier.as_bytes(); // Create spongos to squeeze topic into final 8 bytes let squeezed_topic: [u8; 8] = Spongos::::init().sponge(base_topic); - assert_eq!(id_bytes.len(), 32, "identifier must be 32 bytes long"); - addr[..32].copy_from_slice(id_bytes); + if id_bytes.len() > 32 { + let squeezed_id: [u8; 32] = Spongos::::init().sponge(id_bytes); + addr[..32].copy_from_slice(&squeezed_id) + } else { + addr[..32].copy_from_slice(id_bytes); + } addr[32..].copy_from_slice(&squeezed_topic); Self::new(addr) } diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 17f640eb..e55f0375 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -223,25 +223,23 @@ where 1 => match verifier { Identifier::DID(url_info) => { let mut hash = [0; 64]; - let mut exchange_fragment_bytes = Bytes::default(); let mut fragment_bytes = Bytes::default(); let mut signature_bytes = [0; 64]; - self.absorb(exchange_fragment_bytes.as_mut())? - .absorb(fragment_bytes.as_mut())? + self.absorb(fragment_bytes.as_mut())? .commit()? .squeeze(External::new(&mut NBytes::new(&mut hash)))? .absorb(NBytes::new(&mut signature_bytes))?; - let exchange_fragment = format!( + let signing_fragment = format!( "#{}", - exchange_fragment_bytes + fragment_bytes .to_str() .ok_or_else(|| anyhow!("fragment must be UTF8 encoded"))? ); let did_url = IotaDID::parse(url_info.did().to_string())? - .join(exchange_fragment)?; + .join(signing_fragment)?; let mut signature = Proof::new(JcsEd25519::::NAME, did_url.to_string()); signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index 7475e2a1..74aa192d 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -168,13 +168,13 @@ pub async fn example(transport: Rc>) -> Result<()> { Ok(()) } -async fn make_did_info(did_client: &DIDClient, key_fragment: &str, exchange_fragment: &str, signing_fragment: &str) -> Result { +async fn make_did_info(did_client: &DIDClient, signing_fragment: &str, exchange_fragment: &str, doc_signing_fragment: &str) -> Result { // Create Keypair to act as base of identity let keypair = DIDKeyPair::new(KeyType::Ed25519)?; // Generate original DID document - let mut document = IotaDocument::new(&keypair)?; + let mut document = IotaDocument::new_with_options(&keypair, None, Some(doc_signing_fragment))?; // Sign document and publish to the tangle - document.sign_self(keypair.private(), signing_fragment)?; + document.sign_self(keypair.private(), doc_signing_fragment)?; let receipt = did_client.publish_document(&document).await?; let did = document.id().clone(); @@ -184,7 +184,7 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, exchange_frag did.clone(), streams_signing_keys.type_(), streams_signing_keys.public(), - key_fragment, + signing_fragment, )?; // Create a second Keypair for key exchange method @@ -200,13 +200,13 @@ async fn make_did_info(did_client: &DIDClient, key_fragment: &str, exchange_frag && document.insert_method(xmethod, MethodScope::key_agreement()).is_ok() { document.metadata.previous_message_id = *receipt.message_id(); document.metadata.updated = Some(Timestamp::now_utc()); - document.sign_self(keypair.private(), signing_fragment)?; + document.sign_self(keypair.private(), doc_signing_fragment)?; let _update_receipt = did_client.publish_document(&document).await?; } else { return Err(anyhow!("Failed to update method")); } - let url_info = DIDUrlInfo::new(did, CLIENT_URL, key_fragment, exchange_fragment); + let url_info = DIDUrlInfo::new(did, CLIENT_URL, exchange_fragment, signing_fragment); Ok(DIDInfo::new(url_info, streams_signing_keys, streams_exchange_keys)) } From 9f059cbe9e560a122264ec48473c5af727cc63ae Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 30 Jun 2022 17:13:57 -0600 Subject: [PATCH 05/18] remove ed key -> x key conversion in identifier --- lets/src/id/did.rs | 15 --------------- lets/src/id/identifier.rs | 5 +++-- lets/src/id/identity.rs | 6 +++--- streams/examples/full-example/scenarios/did.rs | 10 +++++----- streams/src/api/user.rs | 4 ++-- 5 files changed, 13 insertions(+), 27 deletions(-) diff --git a/lets/src/id/did.rs b/lets/src/id/did.rs index fa0204a9..86ac2352 100644 --- a/lets/src/id/did.rs +++ b/lets/src/id/did.rs @@ -151,21 +151,6 @@ impl DIDInfo { x25519::SecretKey::try_from_slice(self.exchange_keypair.0.private().as_ref()) .map_err(|e| e.into()) } - - pub(crate) fn sig_kp(&self) -> (ed25519::SecretKey, ed25519::PublicKey) { - let mut key_bytes = [0u8; ed25519::SECRET_KEY_LENGTH]; - key_bytes.clone_from_slice(self.keypair().private().as_ref()); - let signing_secret_key = ed25519::SecretKey::from_bytes(key_bytes); - let signing_public_key = signing_secret_key.public_key(); - (signing_secret_key, signing_public_key) - } - - pub(crate) fn ke_kp(&self) -> (x25519::SecretKey, x25519::PublicKey) { - let kp = self.sig_kp(); - let key_exchange_secret_key = x25519::SecretKey::from(&kp.0); - let key_exchange_public_key = key_exchange_secret_key.public_key(); - (key_exchange_secret_key, key_exchange_public_key) - } } impl DIDUrlInfo { diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index e55f0375..f4a1e4bb 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -13,7 +13,7 @@ use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ core::BaseEncoding, crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, - did::{verifiable::VerifierOptions, DID as IdentityDID}, + did::{verifiable::VerifierOptions, DID as IdentityDID, MethodScope}, client::{Client as DIDClient, ResolvedIotaDocument}, iota_core::IotaDID, }; @@ -76,7 +76,8 @@ impl Identifier { #[cfg(feature = "did")] Identifier::DID(url_info) => { let doc = resolve_document(url_info).await?; - let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + let method = doc.document + .resolve_method(url_info.exchange_fragment(), Some(MethodScope::key_agreement())) .expect("DID Method could not be resolved"); Ok(x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?) } diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index 467d6861..dddfc907 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -56,11 +56,11 @@ impl Default for Identity { impl Identity { // #[deprecated = "to be removed once key exchange is encapsulated within Identity"] - pub fn _ke_sk(&self) -> x25519::SecretKey { + pub fn _ke_sk(&self) -> Result { match self { - Self::Ed25519(ed25519) => ed25519.inner().into(), + Self::Ed25519(ed25519) => Ok(ed25519.inner().into()), #[cfg(feature = "did")] - Self::DID(DID::PrivateKey(info)) => info.ke_kp().0, + Self::DID(DID::PrivateKey(info)) => Ok(info.exchange_key()?), #[cfg(feature = "did")] Self::DID(DID::Default) => unreachable!(), // TODO: Account implementation diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index 74aa192d..fcf8c5d1 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -135,7 +135,7 @@ pub async fn example(transport: Rc>) -> Result<()> { print_send_result(&last_msg); print_user("Author", &author); - println!("> Subscriber C receives 8 messages:"); + println!("> Subscriber C receives 9 messages:"); let messages_as_c = subscriber_c.fetch_next_messages().await?; print_user("Subscriber C", &subscriber_c); for message in &messages_as_c { @@ -143,9 +143,9 @@ pub async fn example(transport: Rc>) -> Result<()> { println!("{}", indent(&fill(&format!("{:?}", message.content()), 140), "\t| ")); println!("\t---"); } - assert_eq!(8, messages_as_c.len()); + assert_eq!(9, messages_as_c.len()); - println!("> Subscriber B receives 8 messages:"); + println!("> Subscriber B receives 9 messages:"); let messages_as_b = subscriber_b.fetch_next_messages().await?; print_user("Subscriber B", &subscriber_b); for message in &messages_as_c { @@ -153,12 +153,12 @@ pub async fn example(transport: Rc>) -> Result<()> { println!("{}", indent(&fill(&format!("{:?}", message.content()), 140), "\t| ")); println!("\t---"); } - assert_eq!(8, messages_as_b.len()); + assert_eq!(9, messages_as_b.len()); println!("> Subscriber A receives 6 messages:"); let messages_as_a = subscriber_a.fetch_next_messages().await?; print_user("Subscriber A", &subscriber_a); - for message in &messages_as_c { + for message in &messages_as_a { println!("\t{}", message.address()); println!("{}", indent(&fill(&format!("{:?}", message.content()), 140), "\t| ")); println!("\t---"); diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 24543c0f..015edb92 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -309,7 +309,7 @@ impl User { return Ok(Message::orphan(address, preparsed)); } }; - let user_ke_sk = &self.identity()?._ke_sk(); + let user_ke_sk = &self.identity()?._ke_sk()?; let subscription = subscription::Unwrap::new(&mut linked_msg_spongos, user_ke_sk); let (message, _spongos) = preparsed.unwrap(subscription).await?; @@ -1224,7 +1224,7 @@ impl Debug for User { fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult { write!( f, - "\n* identifier: <{}>\n* topic: {}\n{:?}\n* PSKs: \n{}\n* messages:\n{}\n", + "\n* identifier: <{:?}>\n* topic: {}\n{:?}\n* PSKs: \n{}\n* messages:\n{}\n", self.identifier().unwrap_or_default(), self.base_branch(), self.state.cursor_store, From 179ea596f8414d8abccfbfcccbd4961e043d18f7 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 30 Jun 2022 17:18:45 -0600 Subject: [PATCH 06/18] unused imports --- lets/src/id/identifier.rs | 8 -------- streams/src/api/user.rs | 3 --- streams/src/message/announcement.rs | 3 +-- streams/src/message/subscription.rs | 2 +- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index f4a1e4bb..a21ce0ed 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -61,14 +61,6 @@ impl Identifier { } } - fn public_key(&self) -> Option<&ed25519::PublicKey> { - if let Identifier::Ed25519(pk) = self { - Some(pk) - } else { - None - } - } - // #[deprecated = "to be removed once key-exchange is encapsulated within Identity"] pub async fn _ke_pk(&self) -> Result { match self { diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 015edb92..e2cdb7a3 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -10,8 +10,6 @@ use hashbrown::HashMap; use rand::{rngs::StdRng, Rng, SeedableRng}; // IOTA -use crypto::keys::x25519; -use crypto::keys::x25519::PublicKey; // Streams use lets::{ @@ -23,7 +21,6 @@ use lets::{ }, transport::Transport, }; -use lets::id::DIDUrlInfo; use spongos::{ ddml::{ commands::{sizeof, unwrap, wrap, Absorb, Commit, Mask, Squeeze}, diff --git a/streams/src/message/announcement.rs b/streams/src/message/announcement.rs index fd387c3b..e5aed710 100644 --- a/streams/src/message/announcement.rs +++ b/streams/src/message/announcement.rs @@ -23,7 +23,6 @@ use anyhow::Result; use async_trait::async_trait; // IOTA -use crypto::keys::x25519; // Streams use lets::{ @@ -32,7 +31,7 @@ use lets::{ }; use spongos::{ ddml::{ - commands::{sizeof, unwrap, wrap, Absorb, Commit, Mask}, + commands::{sizeof, unwrap, wrap, Commit, Mask}, io, }, PRP, diff --git a/streams/src/message/subscription.rs b/streams/src/message/subscription.rs index 92d4c9ac..26e520f3 100644 --- a/streams/src/message/subscription.rs +++ b/streams/src/message/subscription.rs @@ -44,7 +44,7 @@ use lets::{ }; use spongos::{ ddml::{ - commands::{sizeof, unwrap, wrap, Absorb, Join, Mask, X25519}, + commands::{sizeof, unwrap, wrap, Join, Mask, X25519}, io, types::NBytes, }, From f37edc4b2a898659fa30b052896f93e0207e1cc9 Mon Sep 17 00:00:00 2001 From: DyrellC Date: Thu, 30 Jun 2022 18:23:42 -0600 Subject: [PATCH 07/18] add subscribers via binary search pattern --- streams/src/api/user.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index e2cdb7a3..7f5bee72 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -179,20 +179,22 @@ impl User { } pub fn add_subscriber(&mut self, subscriber: Identifier) -> bool { - if self.state.subscribers.contains(&subscriber) { - return false + match self.state.subscribers.binary_search(&subscriber) { + Ok(_) => false, + Err(pos) => { + self.state.subscribers.insert(pos, subscriber); + true + } } - self.state.subscribers.push(subscriber); - true } pub fn remove_subscriber(&mut self, id: &Identifier) -> bool { - let removed_sub = match self.state.subscribers.iter().position(|i| i == id) { - Some(p) => { + let removed_sub = match self.state.subscribers.binary_search(id) { + Ok(p) => { self.state.subscribers.remove(p); true }, - None => false + Err(_) => false }; self.state.cursor_store.remove(id) | removed_sub } @@ -316,7 +318,7 @@ impl User { // Store message content into stores let subscriber_identifier = message.payload().content().subscriber_identifier(); - self.state.subscribers.push(subscriber_identifier.clone()); + self.add_subscriber(subscriber_identifier.clone()); Ok(Message::from_lets_message(address, message)) } From 811b42225f3fabed12573d2dc05242f44cc33d76 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Fri, 1 Jul 2022 19:35:13 +0200 Subject: [PATCH 08/18] added resolver = 2 --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 80078680..cdbd9c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,7 @@ members = [ "streams", ] +resolver = "2" + [profile.dev] incremental = true From 7f7e5f9c2d66d79eb3a296447abbc53655e35a9a Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Mon, 4 Jul 2022 20:14:33 +0200 Subject: [PATCH 09/18] reorganised id, fmt and fixed imports --- lets/src/id/{ => did}/did.rs | 33 +++++-------- lets/src/id/did/mod.rs | 26 ++++++++++ lets/src/id/identifier.rs | 49 +++++++------------ lets/src/id/identity.rs | 18 +++---- lets/src/id/mod.rs | 5 +- lets/src/transport/tangle.rs | 4 +- streams/examples/full-example/main.rs | 2 + .../examples/full-example/scenarios/did.rs | 17 +++++-- .../examples/full-example/scenarios/mod.rs | 1 + streams/src/api/messages.rs | 12 +++-- streams/src/api/user.rs | 4 +- streams/src/api/user_builder.rs | 3 +- streams/src/message/announcement.rs | 4 +- streams/src/message/keyload.rs | 5 +- streams/src/message/subscription.rs | 1 - 15 files changed, 100 insertions(+), 84 deletions(-) rename lets/src/id/{ => did}/did.rs (92%) create mode 100644 lets/src/id/did/mod.rs diff --git a/lets/src/id/did.rs b/lets/src/id/did/did.rs similarity index 92% rename from lets/src/id/did.rs rename to lets/src/id/did/did.rs index 86ac2352..5447730d 100644 --- a/lets/src/id/did.rs +++ b/lets/src/id/did/did.rs @@ -1,8 +1,5 @@ // Rust -use alloc::{ - string::String, - vec::Vec, -}; +use alloc::{string::String, vec::Vec}; use core::hash::Hash; // 3rd-party @@ -12,7 +9,7 @@ use serde::Serialize; // IOTA use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ - crypto::{SetSignature, Proof, GetSignature, GetSignatureMut, KeyType, KeyPair as DIDKeyPair}, + crypto::{GetSignature, GetSignatureMut, KeyPair as DIDKeyPair, KeyType, Proof, SetSignature}, did::{MethodUriType, TryMethod, DID as IdentityDID}, iota_core::IotaDID, }; @@ -119,7 +116,7 @@ impl DIDInfo { Self { url_info, keypair: KeyPair(keypair), - exchange_keypair: KeyPair(exchange_keypair) + exchange_keypair: KeyPair(exchange_keypair), } } @@ -148,8 +145,7 @@ impl DIDInfo { } pub(crate) fn exchange_key(&self) -> Result { - x25519::SecretKey::try_from_slice(self.exchange_keypair.0.private().as_ref()) - .map_err(|e| e.into()) + x25519::SecretKey::try_from_slice(self.exchange_keypair.0.private().as_ref()).map_err(|e| e.into()) } } @@ -196,7 +192,6 @@ impl DIDUrlInfo { } } - struct KeyPair(identity_iota::crypto::KeyPair); impl PartialEq for KeyPair { @@ -259,9 +254,9 @@ where .mask(NBytes::new(&mut private_key_bytes))? .mask(NBytes::new(&mut exchange_private_key_bytes))?; - let keypair = identity_iota::crypto::KeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) + let keypair = DIDKeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; - let xkeypair = identity_iota::crypto::KeyPair::try_from_private_key_bytes(KeyType::X25519, &exchange_private_key_bytes) + let xkeypair = DIDKeyPair::try_from_private_key_bytes(KeyType::X25519, &exchange_private_key_bytes) .map_err(|e| anyhow!("error unmasking DID exchange private key: {}", e))?; *did.info_mut().keypair_mut() = keypair; *did.info_mut().exchange_keypair_mut() = xkeypair; @@ -270,8 +265,6 @@ where } } - - impl Mask<&DIDUrlInfo> for sizeof::Context { fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { self.mask(Bytes::new(url_info.did()))? @@ -282,9 +275,9 @@ impl Mask<&DIDUrlInfo> for sizeof::Context { } impl Mask<&DIDUrlInfo> for wrap::Context - where - F: PRP, - OS: io::OStream, +where + F: PRP, + OS: io::OStream, { fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { self.mask(Bytes::new(url_info.did()))? @@ -295,9 +288,9 @@ impl Mask<&DIDUrlInfo> for wrap::Context } impl Mask<&mut DIDUrlInfo> for unwrap::Context - where - F: PRP, - IS: io::IStream, +where + F: PRP, + IS: io::IStream, { fn mask(&mut self, url_info: &mut DIDUrlInfo) -> Result<&mut Self> { let mut did_bytes = Vec::new(); @@ -315,4 +308,4 @@ impl Mask<&mut DIDUrlInfo> for unwrap::Context *url_info.signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; Ok(self) } -} \ No newline at end of file +} diff --git a/lets/src/id/did/mod.rs b/lets/src/id/did/mod.rs new file mode 100644 index 00000000..d116de71 --- /dev/null +++ b/lets/src/id/did/mod.rs @@ -0,0 +1,26 @@ +use alloc::string::ToString; +use identity_iota::{ + client::{Client as DIDClient, ResolvedIotaDocument}, + iota_core::IotaDID, +}; + +use anyhow::Result; + +pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { + let did_url = IotaDID::parse(url_info.did().to_string())?; + let doc = DIDClient::builder() + .network(did_url.network()?) + .primary_node(url_info.client_url(), None, None)? + .build() + .await? + .read_document(&did_url) + .await?; + Ok(doc) +} + +mod did; + +pub use did::{DIDInfo, DIDUrlInfo, DID}; + +pub(crate) use did::DataWrapper; + diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index a21ce0ed..8e929691 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -1,5 +1,5 @@ // Rust -use alloc::{boxed::Box, string::ToString}; +use alloc::boxed::Box; use core::convert::{TryFrom, TryInto}; use spongos::ddml::commands::X25519; @@ -13,8 +13,7 @@ use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ core::BaseEncoding, crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, - did::{verifiable::VerifierOptions, DID as IdentityDID, MethodScope}, - client::{Client as DIDClient, ResolvedIotaDocument}, + did::{verifiable::VerifierOptions, MethodScope, DID as IdentityDID}, iota_core::IotaDID, }; @@ -24,14 +23,14 @@ use spongos::{ commands::{sizeof, unwrap, wrap, Absorb, Commit, Ed25519, Mask, Squeeze}, io, modifiers::External, - types::{Bytes, NBytes, Uint8}, + types::{NBytes, Uint8}, }, PRP, }; // Local #[cfg(feature = "did")] -use crate::id::did::{DIDUrlInfo, DataWrapper}; +use crate::id::did::{resolve_document, DIDUrlInfo, DataWrapper}; use crate::message::{ContentEncrypt, ContentEncryptSizeOf, ContentVerify}; #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -46,7 +45,7 @@ impl core::fmt::Debug for Identifier { match self { Self::Ed25519(arg0) => f.debug_tuple("Ed25519").field(&hex::encode(&arg0)).finish(), #[cfg(feature = "did")] - Self::DID(url_info) => f.debug_tuple("DID").field(&url_info.did().to_string()).finish(), + Self::DID(url_info) => f.debug_tuple("DID").field(&url_info.did()).finish(), } } } @@ -68,13 +67,13 @@ impl Identifier { #[cfg(feature = "did")] Identifier::DID(url_info) => { let doc = resolve_document(url_info).await?; - let method = doc.document + let method = doc + .document .resolve_method(url_info.exchange_fragment(), Some(MethodScope::key_agreement())) .expect("DID Method could not be resolved"); Ok(x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?) } } - } pub fn is_ed25519(&self) -> bool { @@ -210,13 +209,14 @@ where .ed25519(public_key, hash.as_ref())?; Ok(self) } + #[cfg(feature = "did")] _ => Err(anyhow!("expected Identity type 'Ed25519', found something else")), }, #[cfg(feature = "did")] 1 => match verifier { Identifier::DID(url_info) => { let mut hash = [0; 64]; - let mut fragment_bytes = Bytes::default(); + let mut fragment_bytes = spongos::ddml::types::Bytes::default(); let mut signature_bytes = [0; 64]; self.absorb(fragment_bytes.as_mut())? @@ -231,9 +231,8 @@ where .ok_or_else(|| anyhow!("fragment must be UTF8 encoded"))? ); - let did_url = IotaDID::parse(url_info.did().to_string())? - .join(signing_fragment)?; - let mut signature = Proof::new(JcsEd25519::::NAME, did_url.to_string()); + let did_url = IotaDID::parse(url_info.did())?.join(signing_fragment)?; + let mut signature = Proof::new(JcsEd25519::::NAME, did_url); signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); let data = DataWrapper::new(&hash).with_signature(signature); @@ -261,15 +260,16 @@ impl ContentEncryptSizeOf for sizeof::Context { Identifier::Ed25519(pk) => { let xkey = x25519::PublicKey::try_from(pk)?; self.x25519(&xkey, NBytes::new(key)) - }, + } #[cfg(feature = "did")] Identifier::DID(url_info) => { let doc = resolve_document(url_info).await?; - let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + let method = doc + .document + .resolve_method(url_info.exchange_fragment(), None) .expect("DID Method could not be resolved"); let xkey = x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?; self.x25519(&xkey, NBytes::new(key)) - } } } @@ -288,28 +288,17 @@ where Identifier::Ed25519(pk) => { let xkey = x25519::PublicKey::try_from(pk)?; self.x25519(&xkey, NBytes::new(key)) - }, + } #[cfg(feature = "did")] Identifier::DID(url_info) => { let doc = resolve_document(url_info).await?; - let method = doc.document.resolve_method(url_info.exchange_fragment(), None) + let method = doc + .document + .resolve_method(url_info.exchange_fragment(), None) .expect("DID Method could not be resolved"); let xkey = x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?; self.x25519(&xkey, NBytes::new(key)) - } } } } - -pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { - let did_url = IotaDID::parse(url_info.did().to_string())?; - let doc = DIDClient::builder() - .network(did_url.network()?) - .primary_node(url_info.client_url(), None, None)? - .build() - .await? - .read_document(&did_url) - .await?; - Ok(doc) -} diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index dddfc907..801d1ee6 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -1,9 +1,5 @@ // Rust -use alloc::{boxed::Box, string::ToString}; -use core::{ - convert::AsRef, - hash::Hash, -}; +use alloc::boxed::Box; // 3rd-party use anyhow::{anyhow, Result}; @@ -16,8 +12,8 @@ use identity_iota::{ core::BaseEncoding, crypto::{Ed25519 as DIDEd25519, JcsEd25519, ProofOptions, Signer}, did::DID as IdentityDID, + iota_core::IotaDID, }; -use identity_iota::iota_core::IotaDID; // IOTA-Streams use spongos::{ @@ -25,7 +21,7 @@ use spongos::{ commands::{sizeof, unwrap, wrap, Absorb, Commit, Ed25519 as Ed25519Command, Mask, Squeeze, X25519}, io, modifiers::External, - types::{Bytes, NBytes, Uint8}, + types::{NBytes, Uint8}, }, PRP, }; @@ -166,7 +162,7 @@ impl ContentSignSizeof for sizeof::Context { let key_fragment = info.url_info().signing_fragment().as_bytes().to_vec(); let signature = [0; 64]; self.absorb(Uint8::new(1))? - .absorb(Bytes::new(key_fragment))? + .absorb(spongos::ddml::types::Bytes::new(key_fragment))? .commit()? .squeeze(External::new(&NBytes::new(&hash)))? .absorb(NBytes::new(signature)) @@ -201,7 +197,7 @@ where let mut hash = [0; 64]; let key_fragment = info.url_info().signing_fragment().as_bytes().to_vec(); self.absorb(Uint8::new(1))? - .absorb(Bytes::new(key_fragment))? + .absorb(spongos::ddml::types::Bytes::new(key_fragment))? .commit()? .squeeze(External::new(&mut NBytes::new(&mut hash)))?; @@ -211,7 +207,7 @@ where let method = IotaDID::parse(info.url_info().did())?.join(&fragment)?; JcsEd25519::::create_signature( &mut data, - method.to_string(), + method, info.keypair().private().as_ref(), ProofOptions::new(), )?; @@ -246,7 +242,7 @@ where match recipient { Identity::Ed25519(kp) => self.x25519(&kp.inner().into(), NBytes::new(key)), #[cfg(feature = "did")] - Identity::DID(did) => self.x25519(&did.info().exchange_key()?, NBytes::new(key)) + Identity::DID(did) => self.x25519(&did.info().exchange_key()?, NBytes::new(key)), } } } diff --git a/lets/src/id/mod.rs b/lets/src/id/mod.rs index c980e3ad..b4f4f9cb 100644 --- a/lets/src/id/mod.rs +++ b/lets/src/id/mod.rs @@ -11,7 +11,4 @@ pub use permission::{PermissionDuration, Permissioned}; pub use psk::{Psk, PskId}; #[cfg(feature = "did")] -mod did; - -#[cfg(feature = "did")] -pub use did::{DIDInfo, DID, DIDUrlInfo}; +pub mod did; diff --git a/lets/src/transport/tangle.rs b/lets/src/transport/tangle.rs index 22afdc96..523a7f91 100644 --- a/lets/src/transport/tangle.rs +++ b/lets/src/transport/tangle.rs @@ -124,8 +124,8 @@ impl Address { /// assert_eq!( /// address.to_msg_index().as_ref(), /// &[ - /// 44, 181, 155, 1, 109, 141, 169, 177, 209, 70, 226, 18, 190, 121, 40, 44, 90, 108, 159, 109, 241, 37, 30, 0, - /// 185, 80, 245, 59, 235, 75, 128, 97 + /// 44, 181, 155, 1, 109, 141, 169, 177, 209, 70, 226, 18, 190, 121, 40, 44, 90, 108, 159, + /// 109, 241, 37, 30, 0, 185, 80, 245, 59, 235, 75, 128, 97 /// ], /// ); /// assert_eq!( diff --git a/streams/examples/full-example/main.rs b/streams/examples/full-example/main.rs index 97bbcdb4..ef853adf 100644 --- a/streams/examples/full-example/main.rs +++ b/streams/examples/full-example/main.rs @@ -22,6 +22,7 @@ impl GenericTransport for T where { } +#[cfg(feature = "did")] async fn run_did_test(transport: Rc>) -> Result<()> { println!("## Running DID Test ##\n"); let result = scenarios::did::example(transport).await; @@ -77,6 +78,7 @@ async fn main_client() -> Result<()> { ))); run_single_branch_test(transport.clone(), &new_seed()).await?; + #[cfg(feature = "did")] run_did_test(transport).await?; println!( "#############################################{}", diff --git a/streams/examples/full-example/scenarios/did.rs b/streams/examples/full-example/scenarios/did.rs index fcf8c5d1..99945a88 100644 --- a/streams/examples/full-example/scenarios/did.rs +++ b/streams/examples/full-example/scenarios/did.rs @@ -9,15 +9,18 @@ use textwrap::{fill, indent}; // IOTA use identity_iota::{ core::Timestamp, - did::MethodScope, crypto::KeyType, + did::MethodScope, iota_core::IotaVerificationMethod, prelude::{Client as DIDClient, IotaDocument, KeyPair as DIDKeyPair}, }; // Streams use streams::{ - id::{DIDInfo, DIDUrlInfo, Ed25519, Permissioned, Psk, DID}, + id::{ + did::{DIDInfo, DIDUrlInfo, DID}, + Ed25519, Permissioned, Psk, + }, transport::tangle, User, }; @@ -168,7 +171,12 @@ pub async fn example(transport: Rc>) -> Result<()> { Ok(()) } -async fn make_did_info(did_client: &DIDClient, signing_fragment: &str, exchange_fragment: &str, doc_signing_fragment: &str) -> Result { +async fn make_did_info( + did_client: &DIDClient, + signing_fragment: &str, + exchange_fragment: &str, + doc_signing_fragment: &str, +) -> Result { // Create Keypair to act as base of identity let keypair = DIDKeyPair::new(KeyType::Ed25519)?; // Generate original DID document @@ -197,7 +205,8 @@ async fn make_did_info(did_client: &DIDClient, signing_fragment: &str, exchange_ )?; if document.insert_method(method, MethodScope::VerificationMethod).is_ok() - && document.insert_method(xmethod, MethodScope::key_agreement()).is_ok() { + && document.insert_method(xmethod, MethodScope::key_agreement()).is_ok() + { document.metadata.previous_message_id = *receipt.message_id(); document.metadata.updated = Some(Timestamp::now_utc()); document.sign_self(keypair.private(), doc_signing_fragment)?; diff --git a/streams/examples/full-example/scenarios/mod.rs b/streams/examples/full-example/scenarios/mod.rs index b17ad8b0..e920df46 100644 --- a/streams/examples/full-example/scenarios/mod.rs +++ b/streams/examples/full-example/scenarios/mod.rs @@ -1,3 +1,4 @@ pub mod basic; +#[cfg(feature = "did")] pub mod did; pub mod utils; diff --git a/streams/src/api/messages.rs b/streams/src/api/messages.rs index 7fb537e8..05eb3b27 100644 --- a/streams/src/api/messages.rs +++ b/streams/src/api/messages.rs @@ -53,7 +53,8 @@ use crate::api::{ /// # let test_transport = Rc::new(RefCell::new(bucket::Client::new())); /// # /// let author_seed = "cryptographically-secure-random-author-seed"; -/// let author_transport: tangle::Client = tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; +/// let author_transport: tangle::Client = +/// tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; /// # /// # let test_author_transport = test_transport.clone(); /// # @@ -64,7 +65,8 @@ use crate::api::{ /// .build()?; /// /// let subscriber_seed = "cryptographically-secure-random-subscriber-seed"; -/// let subscriber_transport: tangle::Client = tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; +/// let subscriber_transport: tangle::Client = +/// tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; /// # /// # let subscriber_transport = test_transport.clone(); /// # @@ -79,7 +81,11 @@ use crate::api::{ /// .send_signed_packet("BASE_BRANCH", b"public payload", b"masked payload") /// .await?; /// let second_packet = author -/// .send_signed_packet("BASE_BRANCH", b"another public payload", b"another masked payload") +/// .send_signed_packet( +/// "BASE_BRANCH", +/// b"another public payload", +/// b"another masked payload", +/// ) /// .await?; /// /// # diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 7f5bee72..3cd8bc8a 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -193,8 +193,8 @@ impl User { Ok(p) => { self.state.subscribers.remove(p); true - }, - Err(_) => false + } + Err(_) => false, }; self.state.cursor_store.remove(id) | removed_sub } diff --git a/streams/src/api/user_builder.rs b/streams/src/api/user_builder.rs index 6eb58239..6206678e 100644 --- a/streams/src/api/user_builder.rs +++ b/streams/src/api/user_builder.rs @@ -186,7 +186,8 @@ impl UserBuilder { /// # async fn main() -> Result<()> { /// # let test_transport = Rc::new(RefCell::new(bucket::Client::new())); /// let author_seed = "author_secure_seed"; - /// let transport: tangle::Client = tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; + /// let transport: tangle::Client = + /// tangle::Client::for_node("https://chrysalis-nodes.iota.org").await?; /// # /// # let transport = test_transport.clone(); /// # let mut author = User::builder() diff --git a/streams/src/message/announcement.rs b/streams/src/message/announcement.rs index e5aed710..84647e58 100644 --- a/streams/src/message/announcement.rs +++ b/streams/src/message/announcement.rs @@ -82,9 +82,7 @@ pub(crate) struct Unwrap { impl Default for Unwrap { fn default() -> Self { let author_id = Default::default(); - Self { - author_id, - } + Self { author_id } } } diff --git a/streams/src/message/keyload.rs b/streams/src/message/keyload.rs index c9e27708..daa15194 100644 --- a/streams/src/message/keyload.rs +++ b/streams/src/message/keyload.rs @@ -161,7 +161,7 @@ where for subscriber in subscribers { self.fork() .mask(subscriber)? - .encrypt(subscriber.identifier(), &keyload.key) + .encrypt(subscriber.identifier(), &keyload.key) .await?; } self.absorb(n_psks)?; @@ -235,8 +235,7 @@ where if key.is_none() && keyload.user_id.is_some() { let user_id = keyload.user_id.unwrap(); if subscriber_id.identifier() == &user_id.to_identifier() { - fork.decrypt(user_id, key.get_or_insert([0u8; KEY_SIZE])) - .await?; + fork.decrypt(user_id, key.get_or_insert([0u8; KEY_SIZE])).await?; } else { fork.drop(KEY_SIZE + x25519::PUBLIC_KEY_LENGTH)?; } diff --git a/streams/src/message/subscription.rs b/streams/src/message/subscription.rs index 26e520f3..c66205ce 100644 --- a/streams/src/message/subscription.rs +++ b/streams/src/message/subscription.rs @@ -124,7 +124,6 @@ impl<'a> Unwrap<'a> { pub(crate) fn into_subscriber_identifier(self) -> Identifier { self.subscriber_identifier } - } #[async_trait(?Send)] From 2f606e9e709cce0433465234467b58f77ff53b45 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Tue, 5 Jul 2022 03:22:17 +0200 Subject: [PATCH 10/18] moved verify to DIDInfo to clean imports to package --- lets/src/id/did/did.rs | 25 ++++++++++++++++++++++--- lets/src/id/did/mod.rs | 1 - lets/src/id/identifier.rs | 21 ++++----------------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/lets/src/id/did/did.rs b/lets/src/id/did/did.rs index 5447730d..936adc05 100644 --- a/lets/src/id/did/did.rs +++ b/lets/src/id/did/did.rs @@ -9,8 +9,12 @@ use serde::Serialize; // IOTA use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ - crypto::{GetSignature, GetSignatureMut, KeyPair as DIDKeyPair, KeyType, Proof, SetSignature}, - did::{MethodUriType, TryMethod, DID as IdentityDID}, + core::BaseEncoding, + crypto::{ + Ed25519 as DIDEd25519, GetSignature, GetSignatureMut, JcsEd25519, KeyPair as DIDKeyPair, KeyType, Named, Proof, + ProofValue, SetSignature, + }, + did::{verifiable::VerifierOptions, MethodUriType, TryMethod, DID as IdentityDID}, iota_core::IotaDID, }; @@ -103,7 +107,7 @@ pub struct DIDInfo { exchange_keypair: KeyPair, } -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct DIDUrlInfo { did: String, client_url: String, @@ -159,6 +163,21 @@ impl DIDUrlInfo { } } + pub(crate) async fn verify(&self, signing_fragment: &str, signature_bytes: &[u8], hash: &[u8]) -> Result<()> { + let did_url = IotaDID::parse(self.did())?.join(signing_fragment)?; + let mut signature = Proof::new(JcsEd25519::::NAME, did_url); + signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); + + let data = DataWrapper::new(&hash).with_signature(signature); + + let doc = super::resolve_document(&self).await?; + doc.document + .verify_data(&data, &VerifierOptions::new()) + .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; + + Ok(()) + } + pub(crate) fn did(&self) -> &str { &self.did } diff --git a/lets/src/id/did/mod.rs b/lets/src/id/did/mod.rs index d116de71..5076008e 100644 --- a/lets/src/id/did/mod.rs +++ b/lets/src/id/did/mod.rs @@ -23,4 +23,3 @@ mod did; pub use did::{DIDInfo, DIDUrlInfo, DID}; pub(crate) use did::DataWrapper; - diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 8e929691..8cb141fc 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -9,13 +9,9 @@ use async_trait::async_trait; // IOTA use crypto::{keys::x25519, signatures::ed25519}; + #[cfg(feature = "did")] -use identity_iota::{ - core::BaseEncoding, - crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, - did::{verifiable::VerifierOptions, MethodScope, DID as IdentityDID}, - iota_core::IotaDID, -}; +use identity_iota::did::MethodScope; // Streams use spongos::{ @@ -30,7 +26,7 @@ use spongos::{ // Local #[cfg(feature = "did")] -use crate::id::did::{resolve_document, DIDUrlInfo, DataWrapper}; +use crate::id::did::{resolve_document, DIDUrlInfo}; use crate::message::{ContentEncrypt, ContentEncryptSizeOf, ContentVerify}; #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -231,16 +227,7 @@ where .ok_or_else(|| anyhow!("fragment must be UTF8 encoded"))? ); - let did_url = IotaDID::parse(url_info.did())?.join(signing_fragment)?; - let mut signature = Proof::new(JcsEd25519::::NAME, did_url); - signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); - - let data = DataWrapper::new(&hash).with_signature(signature); - - let doc = resolve_document(&url_info).await?; - doc.document - .verify_data(&data, &VerifierOptions::new()) - .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; + url_info.verify(&signing_fragment, &signature_bytes, &hash).await?; Ok(self) } _ => Err(anyhow!("expected Identity type 'DID', found something else")), From 7b287b77e32670ec028d874d66b37c607f91ab8c Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Tue, 5 Jul 2022 03:45:19 +0200 Subject: [PATCH 11/18] updated identity to latest rev --- lets/Cargo.toml | 2 +- streams/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lets/Cargo.toml b/lets/Cargo.toml index 4e5252fd..dff415c3 100644 --- a/lets/Cargo.toml +++ b/lets/Cargo.toml @@ -41,7 +41,7 @@ hex = {version = "0.4", default-features = false} # Optional dependencies futures = {version = "0.3.8", default-features = false, optional = true} -identity_iota = {git = "https://github.com/iotaledger/identity.rs", branch = "fix/update-stronghold-dependency", default-features = false, optional = true} +identity_iota = {git = "https://github.com/iotaledger/identity.rs", rev = "d3920c2", default-features = false, optional = true} iota-client = {version = "1.1.1", default-features = false, optional = true} parking_lot = {version = "0.11.2", default-features = false, optional = true} serde = {version = "1.0", default-features = false, features = ["derive"], optional = true} diff --git a/streams/Cargo.toml b/streams/Cargo.toml index bfdca529..1794f9f9 100644 --- a/streams/Cargo.toml +++ b/streams/Cargo.toml @@ -40,7 +40,7 @@ rand = {version = "0.8.5", default-features = false} [dev-dependencies] dotenv = {version = "0.15.0", default-features = false} hex = {version = "0.4.3", default-features = false} -identity_iota = {git = "https://github.com/iotaledger/identity.rs", branch = "fix/update-stronghold-dependency"} +identity_iota = {git = "https://github.com/iotaledger/identity.rs", rev = "d3920c2"} rand = {version = "0.8.5", default-features = false, features = ["std", "std_rng"]} textwrap = {version = "0.15.0", default-features = false} tokio = {version = "1.15", default-features = false} From 1b59fa35457726d7efceee42cb08718607545f59 Mon Sep 17 00:00:00 2001 From: Arnau Orriols Date: Fri, 15 Jul 2022 13:37:27 +0200 Subject: [PATCH 12/18] Correct lifetime parameters in `Keyload::Wrap` to avoid unnecessary collection of `Subscribers` iterator --- streams/src/api/user.rs | 3 ++- streams/src/message/keyload.rs | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 3cd8bc8a..be65cbb8 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -780,6 +780,7 @@ where ) -> Result> where Subscribers: IntoIterator> + Clone, + Subscribers::IntoIter: ExactSizeIterator, Top: Into, Psks: IntoIterator, { @@ -825,7 +826,7 @@ where .collect::>>()?; // collect to handle possible error let content = PCF::new_final_frame().with_content(keyload::Wrap::new( &mut linked_msg_spongos, - subscribers.clone().into_iter().collect::>(), + subscribers.clone(), &psk_ids_with_psks, encryption_key, nonce, diff --git a/streams/src/message/keyload.rs b/streams/src/message/keyload.rs index 0c522128..cfe40d41 100644 --- a/streams/src/message/keyload.rs +++ b/streams/src/message/keyload.rs @@ -35,7 +35,7 @@ //! ``` // Rust use alloc::{boxed::Box, vec::Vec}; -use core::iter::IntoIterator; +use core::{iter::IntoIterator, marker::PhantomData}; // 3rd-party use anyhow::Result; @@ -67,16 +67,20 @@ use spongos::{ const NONCE_SIZE: usize = 16; const KEY_SIZE: usize = 32; -pub(crate) struct Wrap<'a, Subscribers, Psks> { +pub(crate) struct Wrap<'a, 'b, Subscribers, Psks> { initial_state: &'a mut Spongos, nonce: [u8; NONCE_SIZE], key: [u8; KEY_SIZE], subscribers: Subscribers, psks: Psks, author_id: &'a Identity, + // panthom subscriber's lifetime needed because we cannot add lifetime parameters to `ContentWrap` trait method. + // subscribers need a different lifetime because they are provided directly from downstream. They are not stored by + // the user instance thus they don't share its lifetime + subscribers_lifetime: PhantomData<&'b Identifier>, } -impl<'a, Subscribers, Psks> Wrap<'a, Subscribers, Psks> { +impl<'a, 'b, Subscribers, Psks> Wrap<'a, 'b, Subscribers, Psks> { pub(crate) fn new( initial_state: &'a mut Spongos, subscribers: Subscribers, @@ -86,7 +90,7 @@ impl<'a, Subscribers, Psks> Wrap<'a, Subscribers, Psks> { author_id: &'a Identity, ) -> Self where - Subscribers: IntoIterator>, + Subscribers: IntoIterator>, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, @@ -98,19 +102,20 @@ impl<'a, Subscribers, Psks> Wrap<'a, Subscribers, Psks> { key, nonce, author_id, + subscribers_lifetime: PhantomData, } } } #[async_trait(?Send)] -impl<'a, Subscribers, Psks> message::ContentSizeof> for sizeof::Context +impl<'a, 'b, Subscribers, Psks> message::ContentSizeof> for sizeof::Context where - Subscribers: IntoIterator> + Clone, + Subscribers: IntoIterator> + Clone, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, { - async fn sizeof(&mut self, keyload: &Wrap<'a, Subscribers, Psks>) -> Result<&mut sizeof::Context> { + async fn sizeof(&mut self, keyload: &Wrap<'a, 'b, Subscribers, Psks>) -> Result<&mut sizeof::Context> { let subscribers = keyload.subscribers.clone().into_iter(); let psks = keyload.psks.clone().into_iter(); let n_subscribers = Size::new(subscribers.len()); @@ -141,15 +146,15 @@ where } #[async_trait(?Send)] -impl<'a, OS, Subscribers, Psks> message::ContentWrap> for wrap::Context +impl<'a, 'b, OS, Subscribers, Psks> message::ContentWrap> for wrap::Context where - Subscribers: IntoIterator> + Clone, + Subscribers: IntoIterator> + Clone, Subscribers::IntoIter: ExactSizeIterator, Psks: IntoIterator + Clone, Psks::IntoIter: ExactSizeIterator, OS: io::OStream, { - async fn wrap(&mut self, keyload: &mut Wrap<'a, Subscribers, Psks>) -> Result<&mut Self> { + async fn wrap(&mut self, keyload: &mut Wrap<'a, 'b, Subscribers, Psks>) -> Result<&mut Self> { let subscribers = keyload.subscribers.clone().into_iter(); let psks = keyload.psks.clone().into_iter(); let n_subscribers = Size::new(subscribers.len()); From 96dc57d775251123579c70ee8c3739cc3d5d3223 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Fri, 15 Jul 2022 15:51:19 +0200 Subject: [PATCH 13/18] Dyrell feedback --- lets/src/id/identifier.rs | 4 ++-- lets/src/id/identity.rs | 4 ++-- streams/examples/full-example/main.rs | 2 -- streams/src/api/user.rs | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 8cb141fc..08a4b9ed 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -56,8 +56,8 @@ impl Identifier { } } - // #[deprecated = "to be removed once key-exchange is encapsulated within Identity"] - pub async fn _ke_pk(&self) -> Result { + // Get the Public key part of the key exchange of the identifier + pub async fn ke_pk(&self) -> Result { match self { Identifier::Ed25519(pk) => Ok(pk.try_into()?), #[cfg(feature = "did")] diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index 801d1ee6..106bd372 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -51,8 +51,8 @@ impl Default for Identity { } impl Identity { - // #[deprecated = "to be removed once key exchange is encapsulated within Identity"] - pub fn _ke_sk(&self) -> Result { + // Get the Secret key part of the key exchange of the Identity + pub fn ke_sk(&self) -> Result { match self { Self::Ed25519(ed25519) => Ok(ed25519.inner().into()), #[cfg(feature = "did")] diff --git a/streams/examples/full-example/main.rs b/streams/examples/full-example/main.rs index ef853adf..97bbcdb4 100644 --- a/streams/examples/full-example/main.rs +++ b/streams/examples/full-example/main.rs @@ -22,7 +22,6 @@ impl GenericTransport for T where { } -#[cfg(feature = "did")] async fn run_did_test(transport: Rc>) -> Result<()> { println!("## Running DID Test ##\n"); let result = scenarios::did::example(transport).await; @@ -78,7 +77,6 @@ async fn main_client() -> Result<()> { ))); run_single_branch_test(transport.clone(), &new_seed()).await?; - #[cfg(feature = "did")] run_did_test(transport).await?; println!( "#############################################{}", diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 9e581116..beee3fc2 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -341,7 +341,7 @@ impl User { return Ok(Message::orphan(address, preparsed)); } }; - let user_ke_sk = &self.identity()?._ke_sk()?; + let user_ke_sk = &self.identity()?.ke_sk()?; let subscription = subscription::Unwrap::new(&mut linked_msg_spongos, user_ke_sk); let (message, _spongos) = preparsed.unwrap(subscription).await?; @@ -746,7 +746,7 @@ where .author_identifier .as_ref() .expect("a user that already have an stream address must know the author identifier") - ._ke_pk() + .ke_pk() .await?; let content = PCF::new_final_frame().with_content(subscription::Wrap::new( &mut linked_msg_spongos, From eec365b5fbadfa18e5d3a0318f6f1331932d3fe2 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Fri, 15 Jul 2022 17:12:10 +0200 Subject: [PATCH 14/18] restructured more of did --- lets/src/id/did/data_wrapper.rs | 50 ++++++ lets/src/id/did/did.rs | 263 +++++++------------------------- lets/src/id/did/mod.rs | 28 +--- lets/src/id/did/url_info.rs | 136 +++++++++++++++++ lets/src/id/identifier.rs | 8 +- 5 files changed, 255 insertions(+), 230 deletions(-) create mode 100644 lets/src/id/did/data_wrapper.rs create mode 100644 lets/src/id/did/url_info.rs diff --git a/lets/src/id/did/data_wrapper.rs b/lets/src/id/did/data_wrapper.rs new file mode 100644 index 00000000..c2ab27c6 --- /dev/null +++ b/lets/src/id/did/data_wrapper.rs @@ -0,0 +1,50 @@ +// 3rd-party +use serde::Serialize; + +use identity_iota::{ + crypto::{GetSignature, GetSignatureMut, Proof, SetSignature}, + did::{MethodUriType, TryMethod}, +}; + +#[derive(Serialize)] +pub(crate) struct DataWrapper<'a> { + data: &'a [u8], + signature: Option, +} + +impl<'a> DataWrapper<'a> { + pub(crate) fn new(data: &'a [u8]) -> Self { + Self { data, signature: None } + } + + pub(crate) fn with_signature(mut self, signature: Proof) -> Self { + self.signature = Some(signature); + self + } + + pub(crate) fn into_signature(self) -> Option { + self.signature + } +} + +impl<'a> GetSignature for DataWrapper<'a> { + fn signature(&self) -> Option<&Proof> { + self.signature.as_ref() + } +} + +impl<'a> GetSignatureMut for DataWrapper<'a> { + fn signature_mut(&mut self) -> Option<&mut Proof> { + self.signature.as_mut() + } +} + +impl<'a> SetSignature for DataWrapper<'a> { + fn set_signature(&mut self, signature: Proof) { + self.signature = Some(signature) + } +} + +impl<'a> TryMethod for DataWrapper<'a> { + const TYPE: MethodUriType = MethodUriType::Absolute; +} diff --git a/lets/src/id/did/did.rs b/lets/src/id/did/did.rs index 936adc05..4d6afd98 100644 --- a/lets/src/id/did/did.rs +++ b/lets/src/id/did/did.rs @@ -1,20 +1,15 @@ // Rust -use alloc::{string::String, vec::Vec}; +use alloc::string::ToString; use core::hash::Hash; // 3rd-party use anyhow::{anyhow, Result}; -use serde::Serialize; // IOTA use crypto::{keys::x25519, signatures::ed25519}; use identity_iota::{ - core::BaseEncoding, - crypto::{ - Ed25519 as DIDEd25519, GetSignature, GetSignatureMut, JcsEd25519, KeyPair as DIDKeyPair, KeyType, Named, Proof, - ProofValue, SetSignature, - }, - did::{verifiable::VerifierOptions, MethodUriType, TryMethod, DID as IdentityDID}, + client::{Client as DIDClient, ResolvedIotaDocument}, + crypto::{KeyPair as DIDKeyPair, KeyType}, iota_core::IotaDID, }; @@ -23,52 +18,23 @@ use spongos::{ ddml::{ commands::{sizeof, unwrap, wrap, Mask}, io, - types::{Bytes, NBytes}, + types::NBytes, }, PRP, }; -#[derive(Serialize)] -pub(crate) struct DataWrapper<'a> { - data: &'a [u8], - signature: Option, -} - -impl<'a> DataWrapper<'a> { - pub(crate) fn new(data: &'a [u8]) -> Self { - Self { data, signature: None } - } - - pub(crate) fn with_signature(mut self, signature: Proof) -> Self { - self.signature = Some(signature); - self - } - - pub(crate) fn into_signature(self) -> Option { - self.signature - } -} - -impl<'a> GetSignature for DataWrapper<'a> { - fn signature(&self) -> Option<&Proof> { - self.signature.as_ref() - } -} - -impl<'a> GetSignatureMut for DataWrapper<'a> { - fn signature_mut(&mut self) -> Option<&mut Proof> { - self.signature.as_mut() - } -} +use crate::id::did::DIDUrlInfo; -impl<'a> SetSignature for DataWrapper<'a> { - fn set_signature(&mut self, signature: Proof) { - self.signature = Some(signature) - } -} - -impl<'a> TryMethod for DataWrapper<'a> { - const TYPE: MethodUriType = MethodUriType::Absolute; +pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { + let did_url = IotaDID::parse(url_info.did().to_string())?; + let doc = DIDClient::builder() + .network(did_url.network()?) + .primary_node(url_info.client_url(), None, None)? + .build() + .await? + .read_document(&did_url) + .await?; + Ok(doc) } #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -100,6 +66,50 @@ impl Default for DID { } } +impl Mask<&DID> for sizeof::Context { + fn mask(&mut self, did: &DID) -> Result<&mut Self> { + self.mask(did.info().url_info())? + .mask(NBytes::new(did.info().keypair().private()))? + .mask(NBytes::new(did.info().exchange_keypair().private())) + } +} + +impl Mask<&DID> for wrap::Context +where + F: PRP, + OS: io::OStream, +{ + fn mask(&mut self, did: &DID) -> Result<&mut Self> { + self.mask(did.info().url_info())? + .mask(NBytes::new(did.info().keypair().private()))? + .mask(NBytes::new(did.info().exchange_keypair().private())) + } +} + +impl Mask<&mut DID> for unwrap::Context +where + F: PRP, + IS: io::IStream, +{ + fn mask(&mut self, did: &mut DID) -> Result<&mut Self> { + let mut url_info = DIDUrlInfo::default(); + let mut private_key_bytes = [0; ed25519::SECRET_KEY_LENGTH]; + let mut exchange_private_key_bytes = [0; x25519::SECRET_KEY_LENGTH]; + self.mask(&mut url_info)? + .mask(NBytes::new(&mut private_key_bytes))? + .mask(NBytes::new(&mut exchange_private_key_bytes))?; + + let keypair = DIDKeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) + .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; + let xkeypair = DIDKeyPair::try_from_private_key_bytes(KeyType::X25519, &exchange_private_key_bytes) + .map_err(|e| anyhow!("error unmasking DID exchange private key: {}", e))?; + *did.info_mut().keypair_mut() = keypair; + *did.info_mut().exchange_keypair_mut() = xkeypair; + + Ok(self) + } +} + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DIDInfo { url_info: DIDUrlInfo, @@ -107,14 +117,6 @@ pub struct DIDInfo { exchange_keypair: KeyPair, } -#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct DIDUrlInfo { - did: String, - client_url: String, - exchange_fragment: String, - signing_fragment: String, -} - impl DIDInfo { pub fn new(url_info: DIDUrlInfo, keypair: DIDKeyPair, exchange_keypair: DIDKeyPair) -> Self { Self { @@ -153,64 +155,6 @@ impl DIDInfo { } } -impl DIDUrlInfo { - pub fn new>(did: IotaDID, client_url: T, exchange_fragment: T, signing_fragment: T) -> Self { - Self { - did: did.into_string(), - client_url: client_url.into(), - exchange_fragment: exchange_fragment.into(), - signing_fragment: signing_fragment.into(), - } - } - - pub(crate) async fn verify(&self, signing_fragment: &str, signature_bytes: &[u8], hash: &[u8]) -> Result<()> { - let did_url = IotaDID::parse(self.did())?.join(signing_fragment)?; - let mut signature = Proof::new(JcsEd25519::::NAME, did_url); - signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); - - let data = DataWrapper::new(&hash).with_signature(signature); - - let doc = super::resolve_document(&self).await?; - doc.document - .verify_data(&data, &VerifierOptions::new()) - .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; - - Ok(()) - } - - pub(crate) fn did(&self) -> &str { - &self.did - } - - pub(crate) fn client_url(&self) -> &str { - &self.client_url - } - - pub(crate) fn exchange_fragment(&self) -> &str { - &self.exchange_fragment - } - - pub(crate) fn signing_fragment(&self) -> &str { - &self.signing_fragment - } - - pub(crate) fn did_mut(&mut self) -> &mut String { - &mut self.did - } - - pub(crate) fn client_url_mut(&mut self) -> &mut String { - &mut self.client_url - } - - pub(crate) fn exchange_fragment_mut(&mut self) -> &mut String { - &mut self.exchange_fragment - } - - pub(crate) fn signing_fragment_mut(&mut self) -> &mut String { - &mut self.signing_fragment - } -} - struct KeyPair(identity_iota::crypto::KeyPair); impl PartialEq for KeyPair { @@ -239,92 +183,3 @@ impl Hash for KeyPair { self.0.private().as_ref().hash(state); } } - -impl Mask<&DID> for sizeof::Context { - fn mask(&mut self, did: &DID) -> Result<&mut Self> { - self.mask(did.info().url_info())? - .mask(NBytes::new(did.info().keypair().private()))? - .mask(NBytes::new(did.info().exchange_keypair().private())) - } -} - -impl Mask<&DID> for wrap::Context -where - F: PRP, - OS: io::OStream, -{ - fn mask(&mut self, did: &DID) -> Result<&mut Self> { - self.mask(did.info().url_info())? - .mask(NBytes::new(did.info().keypair().private()))? - .mask(NBytes::new(did.info().exchange_keypair().private())) - } -} - -impl Mask<&mut DID> for unwrap::Context -where - F: PRP, - IS: io::IStream, -{ - fn mask(&mut self, did: &mut DID) -> Result<&mut Self> { - let mut url_info = DIDUrlInfo::default(); - let mut private_key_bytes = [0; ed25519::SECRET_KEY_LENGTH]; - let mut exchange_private_key_bytes = [0; x25519::SECRET_KEY_LENGTH]; - self.mask(&mut url_info)? - .mask(NBytes::new(&mut private_key_bytes))? - .mask(NBytes::new(&mut exchange_private_key_bytes))?; - - let keypair = DIDKeyPair::try_from_private_key_bytes(KeyType::Ed25519, &private_key_bytes) - .map_err(|e| anyhow!("error unmasking DID private key: {}", e))?; - let xkeypair = DIDKeyPair::try_from_private_key_bytes(KeyType::X25519, &exchange_private_key_bytes) - .map_err(|e| anyhow!("error unmasking DID exchange private key: {}", e))?; - *did.info_mut().keypair_mut() = keypair; - *did.info_mut().exchange_keypair_mut() = xkeypair; - - Ok(self) - } -} - -impl Mask<&DIDUrlInfo> for sizeof::Context { - fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { - self.mask(Bytes::new(url_info.did()))? - .mask(Bytes::new(url_info.client_url()))? - .mask(Bytes::new(url_info.exchange_fragment()))? - .mask(Bytes::new(url_info.signing_fragment())) - } -} - -impl Mask<&DIDUrlInfo> for wrap::Context -where - F: PRP, - OS: io::OStream, -{ - fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { - self.mask(Bytes::new(url_info.did()))? - .mask(Bytes::new(url_info.client_url()))? - .mask(Bytes::new(url_info.exchange_fragment()))? - .mask(Bytes::new(url_info.signing_fragment())) - } -} - -impl Mask<&mut DIDUrlInfo> for unwrap::Context -where - F: PRP, - IS: io::IStream, -{ - fn mask(&mut self, url_info: &mut DIDUrlInfo) -> Result<&mut Self> { - let mut did_bytes = Vec::new(); - let mut client_url = Vec::new(); - let mut exchange_fragment_bytes = Vec::new(); - let mut signing_fragment_bytes = Vec::new(); - self.mask(Bytes::new(&mut did_bytes))? - .mask(Bytes::new(&mut client_url))? - .mask(Bytes::new(&mut exchange_fragment_bytes))? - .mask(Bytes::new(&mut signing_fragment_bytes))?; - - *url_info.did_mut() = String::from_utf8(did_bytes)?; - *url_info.client_url_mut() = String::from_utf8(client_url)?; - *url_info.exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; - *url_info.signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; - Ok(self) - } -} diff --git a/lets/src/id/did/mod.rs b/lets/src/id/did/mod.rs index 5076008e..5e709108 100644 --- a/lets/src/id/did/mod.rs +++ b/lets/src/id/did/mod.rs @@ -1,25 +1,9 @@ -use alloc::string::ToString; -use identity_iota::{ - client::{Client as DIDClient, ResolvedIotaDocument}, - iota_core::IotaDID, -}; - -use anyhow::Result; - -pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { - let did_url = IotaDID::parse(url_info.did().to_string())?; - let doc = DIDClient::builder() - .network(did_url.network()?) - .primary_node(url_info.client_url(), None, None)? - .build() - .await? - .read_document(&did_url) - .await?; - Ok(doc) -} - +mod data_wrapper; mod did; +mod url_info; -pub use did::{DIDInfo, DIDUrlInfo, DID}; +pub use did::{DIDInfo, DID}; +pub use url_info::DIDUrlInfo; -pub(crate) use did::DataWrapper; +pub(crate) use data_wrapper::DataWrapper; +pub(crate) use did::resolve_document; diff --git a/lets/src/id/did/url_info.rs b/lets/src/id/did/url_info.rs new file mode 100644 index 00000000..56321daf --- /dev/null +++ b/lets/src/id/did/url_info.rs @@ -0,0 +1,136 @@ +// Rust +use alloc::{string::String, vec::Vec}; + +// 3rd-party +use anyhow::{anyhow, Result}; + +// IOTA +use identity_iota::{ + core::BaseEncoding, + crypto::{Ed25519 as DIDEd25519, JcsEd25519, Named, Proof, ProofValue}, + did::{verifiable::VerifierOptions, DID as IdentityDID}, + iota_core::IotaDID, +}; + +use crate::id::did::DataWrapper; + +// Streams +use spongos::{ + ddml::{ + commands::{sizeof, unwrap, wrap, Mask}, + io, + types::Bytes, + }, + PRP, +}; + +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct DIDUrlInfo { + did: String, + client_url: String, + exchange_fragment: String, + signing_fragment: String, +} + +impl DIDUrlInfo { + pub fn new>(did: IotaDID, client_url: T, exchange_fragment: T, signing_fragment: T) -> Self { + Self { + did: did.into_string(), + client_url: client_url.into(), + exchange_fragment: exchange_fragment.into(), + signing_fragment: signing_fragment.into(), + } + } + + pub(crate) async fn verify(&self, signing_fragment: &str, signature_bytes: &[u8], hash: &[u8]) -> Result<()> { + let did_url = IotaDID::parse(self.did())?.join(signing_fragment)?; + let mut signature = Proof::new(JcsEd25519::::NAME, did_url); + signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); + + let data = DataWrapper::new(&hash).with_signature(signature); + + let doc = super::resolve_document(&self).await?; + doc.document + .verify_data(&data, &VerifierOptions::new()) + .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; + + Ok(()) + } + + pub(crate) fn did(&self) -> &str { + &self.did + } + + pub(crate) fn client_url(&self) -> &str { + &self.client_url + } + + pub(crate) fn exchange_fragment(&self) -> &str { + &self.exchange_fragment + } + + pub(crate) fn signing_fragment(&self) -> &str { + &self.signing_fragment + } + + pub(crate) fn did_mut(&mut self) -> &mut String { + &mut self.did + } + + pub(crate) fn client_url_mut(&mut self) -> &mut String { + &mut self.client_url + } + + pub(crate) fn exchange_fragment_mut(&mut self) -> &mut String { + &mut self.exchange_fragment + } + + pub(crate) fn signing_fragment_mut(&mut self) -> &mut String { + &mut self.signing_fragment + } +} + +impl Mask<&DIDUrlInfo> for sizeof::Context { + fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { + self.mask(Bytes::new(url_info.did()))? + .mask(Bytes::new(url_info.client_url()))? + .mask(Bytes::new(url_info.exchange_fragment()))? + .mask(Bytes::new(url_info.signing_fragment())) + } +} + +impl Mask<&DIDUrlInfo> for wrap::Context +where + F: PRP, + OS: io::OStream, +{ + fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { + self.mask(Bytes::new(url_info.did()))? + .mask(Bytes::new(url_info.client_url()))? + .mask(Bytes::new(url_info.exchange_fragment()))? + .mask(Bytes::new(url_info.signing_fragment())) + } +} + +impl Mask<&mut DIDUrlInfo> for unwrap::Context +where + F: PRP, + IS: io::IStream, +{ + fn mask(&mut self, url_info: &mut DIDUrlInfo) -> Result<&mut Self> { + let mut did_bytes = Vec::new(); + let mut client_url = Vec::new(); + let mut exchange_fragment_bytes = Vec::new(); + let mut signing_fragment_bytes = Vec::new(); + self.mask(Bytes::new(&mut did_bytes))? + .mask(Bytes::new(&mut client_url))? + .mask(Bytes::new(&mut exchange_fragment_bytes))? + .mask(Bytes::new(&mut signing_fragment_bytes))?; + + *url_info.did_mut() = String::from_utf8(did_bytes)?; + *url_info.client_url_mut() = String::from_utf8(client_url)?; + *url_info.exchange_fragment_mut() = String::from_utf8(exchange_fragment_bytes)?; + *url_info.signing_fragment_mut() = String::from_utf8(signing_fragment_bytes)?; + Ok(self) + } +} diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 08a4b9ed..7a5b22ef 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -10,9 +10,6 @@ use async_trait::async_trait; // IOTA use crypto::{keys::x25519, signatures::ed25519}; -#[cfg(feature = "did")] -use identity_iota::did::MethodScope; - // Streams use spongos::{ ddml::{ @@ -65,7 +62,10 @@ impl Identifier { let doc = resolve_document(url_info).await?; let method = doc .document - .resolve_method(url_info.exchange_fragment(), Some(MethodScope::key_agreement())) + .resolve_method( + url_info.exchange_fragment(), + Some(identity_iota::did::MethodScope::key_agreement()), + ) .expect("DID Method could not be resolved"); Ok(x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?) } From 2179985fafc9f9637afa7582fbf98e2f15eb2ea0 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Wed, 27 Jul 2022 23:10:36 +0200 Subject: [PATCH 15/18] Arnau feedback --- lets/src/address.rs | 17 +++------- lets/src/id/did/did.rs | 3 +- lets/src/id/did/url_info.rs | 8 +++++ lets/src/id/identifier.rs | 34 +++++++++---------- lets/src/id/identity.rs | 2 +- .../examples/full-example/scenarios/basic.rs | 30 +++++++--------- streams/src/api/user.rs | 30 ++++------------ 7 files changed, 51 insertions(+), 73 deletions(-) diff --git a/lets/src/address.rs b/lets/src/address.rs index 54269255..4bcfd021 100644 --- a/lets/src/address.rs +++ b/lets/src/address.rs @@ -142,18 +142,11 @@ impl AppAddr { } pub fn gen(identifier: &Identifier, base_topic: &Topic) -> AppAddr { - let mut addr = [0u8; 40]; - let id_bytes = identifier.as_bytes(); - // Create spongos to squeeze topic into final 8 bytes - let squeezed_topic: [u8; 8] = Spongos::::init().sponge(base_topic); - if id_bytes.len() > 32 { - let squeezed_id: [u8; 32] = Spongos::::init().sponge(id_bytes); - addr[..32].copy_from_slice(&squeezed_id) - } else { - addr[..32].copy_from_slice(id_bytes); - } - addr[32..].copy_from_slice(&squeezed_topic); - Self::new(addr) + let mut spongos = Spongos::::init(); + spongos.absorb(base_topic); + spongos.absorb(identifier); + spongos.commit(); + spongos.squeeze() } /// Get the hexadecimal representation of the appaddr diff --git a/lets/src/id/did/did.rs b/lets/src/id/did/did.rs index 4d6afd98..b89e304e 100644 --- a/lets/src/id/did/did.rs +++ b/lets/src/id/did/did.rs @@ -1,5 +1,4 @@ // Rust -use alloc::string::ToString; use core::hash::Hash; // 3rd-party @@ -26,7 +25,7 @@ use spongos::{ use crate::id::did::DIDUrlInfo; pub(crate) async fn resolve_document(url_info: &DIDUrlInfo) -> Result { - let did_url = IotaDID::parse(url_info.did().to_string())?; + let did_url = IotaDID::parse(url_info.did())?; let doc = DIDClient::builder() .network(did_url.network()?) .primary_node(url_info.client_url(), None, None)? diff --git a/lets/src/id/did/url_info.rs b/lets/src/id/did/url_info.rs index 56321daf..36513f4b 100644 --- a/lets/src/id/did/url_info.rs +++ b/lets/src/id/did/url_info.rs @@ -90,6 +90,14 @@ impl DIDUrlInfo { } } +impl AsRef<[u8]> for DIDUrlInfo { + fn as_ref(&self) -> &[u8] { + // TODO how to make a ref to all fields without permanently storing? + // For now we assume someone wont be using the same DID twice + self.did().as_bytes() + } +} + impl Mask<&DIDUrlInfo> for sizeof::Context { fn mask(&mut self, url_info: &DIDUrlInfo) -> Result<&mut Self> { self.mask(Bytes::new(url_info.did()))? diff --git a/lets/src/id/identifier.rs b/lets/src/id/identifier.rs index 7a5b22ef..934c58ff 100644 --- a/lets/src/id/identifier.rs +++ b/lets/src/id/identifier.rs @@ -38,7 +38,12 @@ impl core::fmt::Debug for Identifier { match self { Self::Ed25519(arg0) => f.debug_tuple("Ed25519").field(&hex::encode(&arg0)).finish(), #[cfg(feature = "did")] - Self::DID(url_info) => f.debug_tuple("DID").field(&url_info.did()).finish(), + Self::DID(url_info) => f + .debug_tuple("DID") + .field(&url_info.did()) + .field(&url_info.exchange_fragment()) + .field(&url_info.signing_fragment()) + .finish(), } } } @@ -49,7 +54,7 @@ impl Identifier { match self { Identifier::Ed25519(public_key) => public_key.as_slice(), #[cfg(feature = "did")] - Identifier::DID(url_info) => url_info.did().as_bytes(), + Identifier::DID(url_info) => url_info.as_ref(), } } @@ -60,14 +65,16 @@ impl Identifier { #[cfg(feature = "did")] Identifier::DID(url_info) => { let doc = resolve_document(url_info).await?; - let method = doc - .document - .resolve_method( - url_info.exchange_fragment(), - Some(identity_iota::did::MethodScope::key_agreement()), - ) - .expect("DID Method could not be resolved"); - Ok(x25519::PublicKey::try_from_slice(&method.data().try_decode()?)?) + match doc.document.resolve_method( + url_info.exchange_fragment(), + Some(identity_iota::did::MethodScope::key_agreement()), + ) { + Some(e) => Ok(x25519::PublicKey::try_from_slice(&e.data().try_decode()?)?), + None => Err(anyhow!( + "DID Method fragment {} could not be resolved", + url_info.exchange_fragment() + )), + } } } } @@ -90,13 +97,6 @@ impl From for Identifier { } } -#[cfg(feature = "did")] -impl From<&DIDUrlInfo> for Identifier { - fn from(did: &DIDUrlInfo) -> Self { - Identifier::DID(did.clone()) - } -} - impl AsRef<[u8]> for Identifier { fn as_ref(&self) -> &[u8] { self.as_bytes() diff --git a/lets/src/id/identity.rs b/lets/src/id/identity.rs index 106bd372..87433694 100644 --- a/lets/src/id/identity.rs +++ b/lets/src/id/identity.rs @@ -67,7 +67,7 @@ impl Identity { match self { Self::Ed25519(ed25519) => ed25519.inner().public_key().into(), #[cfg(feature = "did")] - Self::DID(did) => did.info().url_info().into(), + Self::DID(did) => Identifier::DID(did.info().url_info().clone()), } } } diff --git a/streams/examples/full-example/scenarios/basic.rs b/streams/examples/full-example/scenarios/basic.rs index 4feb0771..26a15e0b 100644 --- a/streams/examples/full-example/scenarios/basic.rs +++ b/streams/examples/full-example/scenarios/basic.rs @@ -462,37 +462,31 @@ pub(crate) async fn example(transport: T, author_seed: &str println!("> Subscriber A can only read the last keyload"); let next_messages = subscriber_a.fetch_next_messages().await?; print_user("Subscriber A", &subscriber_a); - let last_msg_as_a = next_messages - .last() - .expect("Subscriber A has not received the latest keyload"); assert!( - last_msg_as_a.is_keyload(), - "Subscriber A expected the last message to be a keyload message, found {:?} instead", - last_msg_as_a.content() + !next_messages + .iter() + .any(|msg| msg.address() == last_signed_packet.address()), + "Subscriber A did not expect the latest signed message" ); println!("> Subscriber B can only read the last keyload"); let next_messages = subscriber_b.fetch_next_messages().await?; print_user("Subscriber B", &subscriber_b); - let last_msg_as_b = next_messages - .last() - .expect("Subscriber B has not received the latest keyload"); assert!( - last_msg_as_b.is_keyload(), - "Subscriber B expected the last message to be a keyload message, found {:?} instead", - last_msg_as_b.content() + !next_messages + .iter() + .any(|msg| msg.address() == last_signed_packet.address()), + "Subscriber B did not expect the latest signed message" ); println!("> Subscriber C can only read the last keyload"); let next_messages = subscriber_c.fetch_next_messages().await?; print_user("Subscriber C", &subscriber_c); - let last_msg_as_c = next_messages - .last() - .expect("Subscriber C has not received the latest keyload"); assert!( - last_msg_as_c.is_keyload(), - "Subscriber C expected the last message to be a keyload message, found {:?} instead", - last_msg_as_c.content() + !next_messages + .iter() + .any(|msg| msg.address() == last_signed_packet.address()), + "Subscriber C did not expect the latest signed message" ); println!("> Subscribers A and B try to send a signed packet"); diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index beee3fc2..ec570e18 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -6,7 +6,7 @@ use core::fmt::{Debug, Formatter, Result as FormatResult}; use anyhow::{anyhow, bail, ensure, Result}; use async_trait::async_trait; use futures::{future, TryStreamExt}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use rand::{rngs::StdRng, Rng, SeedableRng}; // IOTA @@ -69,7 +69,7 @@ struct State { psk_store: HashMap, /// List of Subscribed Identifiers - subscribers: Vec, + subscribers: HashSet, spongos_store: HashMap, @@ -94,17 +94,13 @@ impl User { Psks: IntoIterator, { let mut psk_store = HashMap::new(); - let mut subscribers = Vec::new(); + let subscribers = HashSet::new(); // Store any pre shared keys psks.into_iter().for_each(|(pskid, psk)| { psk_store.insert(pskid, psk); }); - if let Some(id) = user_id.as_ref() { - subscribers.push(id.to_identifier()); - } - Self { transport, state: State { @@ -189,24 +185,12 @@ impl User { } pub fn add_subscriber(&mut self, subscriber: Identifier) -> bool { - match self.state.subscribers.binary_search(&subscriber) { - Ok(_) => false, - Err(pos) => { - self.state.subscribers.insert(pos, subscriber); - true - } - } + self.state.subscribers.insert(subscriber) } pub fn remove_subscriber(&mut self, id: &Identifier) -> bool { - let removed_sub = match self.state.subscribers.binary_search(id) { - Ok(p) => { - self.state.subscribers.remove(p); - true - } - Err(_) => false, - }; - self.state.cursor_store.remove(id) | removed_sub + self.state.cursor_store.remove(id); + self.state.subscribers.remove(id) } pub fn add_psk(&mut self, psk: Psk) -> bool { @@ -1256,7 +1240,7 @@ impl<'a> ContentUnwrap for unwrap::Context<&'a [u8]> { for _ in 0..amount_subs.inner() { let mut subscriber = Identifier::default(); self.mask(&mut subscriber)?; - user_state.subscribers.push(subscriber); + user_state.subscribers.insert(subscriber); } let mut amount_psks = Size::default(); From c47fc3624522c20fd10eaad31abe1e00aa4bd219 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Thu, 18 Aug 2022 10:16:53 +0200 Subject: [PATCH 16/18] clippy --- lets/src/id/did/url_info.rs | 4 ++-- streams/src/api/user.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lets/src/id/did/url_info.rs b/lets/src/id/did/url_info.rs index 36513f4b..6d99023d 100644 --- a/lets/src/id/did/url_info.rs +++ b/lets/src/id/did/url_info.rs @@ -47,9 +47,9 @@ impl DIDUrlInfo { let mut signature = Proof::new(JcsEd25519::::NAME, did_url); signature.set_value(ProofValue::Signature(BaseEncoding::encode_base58(&signature_bytes))); - let data = DataWrapper::new(&hash).with_signature(signature); + let data = DataWrapper::new(hash).with_signature(signature); - let doc = super::resolve_document(&self).await?; + let doc = super::resolve_document(self).await?; doc.document .verify_data(&data, &VerifierOptions::new()) .map_err(|e| anyhow!("There was an issue validating the signature: {}", e))?; diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 8d74ad40..d9287a36 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -253,7 +253,7 @@ impl User { // Update branch links self.set_latest_link(&topic, address.relative()); - self.state.author_identifier = Some(author_id.clone()); + self.state.author_identifier = Some(author_id); self.state.base_branch = topic; self.state.stream_address = Some(address); From 9f2655f084818796a2de4add0410664d207613f5 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Wed, 24 Aug 2022 19:54:39 +0200 Subject: [PATCH 17/18] merge fix --- lets/Cargo.toml | 2 +- streams/src/api/user.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lets/Cargo.toml b/lets/Cargo.toml index f3b2982d..b31777ec 100644 --- a/lets/Cargo.toml +++ b/lets/Cargo.toml @@ -20,7 +20,7 @@ tangle-client = ["iota-client/async", "futures", "iota-crypto/blake2b"] # Enable the wasm-compatible IOTA-Tangle transport client (incompatile with `tangle-client` feature due to `iota-client/async` using `tokio`. Implies `std` feature) tangle-client-wasm = ["iota-client/wasm", "futures"] # Enable Iota Identity for use with Streams -did = ["identity", "serde"] +did = ["identity_iota", "serde"] [dependencies] # Local dependencies diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index f43e5f46..9a94bc4f 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -192,8 +192,7 @@ impl User { } pub fn remove_subscriber(&mut self, id: &Identifier) -> bool { - self.state.subscribers.remove(id); - self.state.exchange_keys.remove(id).is_some() + self.state.subscribers.remove(id) } pub fn add_psk(&mut self, psk: Psk) -> bool { From 0d13ad9c6c6132337f7cded506196aaf3a1a0366 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Thu, 25 Aug 2022 03:42:37 +0200 Subject: [PATCH 18/18] fixed permissions error --- streams/src/api/user.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/streams/src/api/user.rs b/streams/src/api/user.rs index 9a94bc4f..5f060168 100644 --- a/streams/src/api/user.rs +++ b/streams/src/api/user.rs @@ -431,9 +431,8 @@ impl User { .collect(); for (perm, cursor) in stored_subscribers { - if !subscribers - .iter() - .any(|p| perm.identifier() == author_identifier || p.identifier() == perm.identifier()) + if !(perm.identifier() == author_identifier + || subscribers.iter().any(|p| p.identifier() == perm.identifier())) { self.state .cursor_store