diff --git a/Cargo.lock b/Cargo.lock index 2ad2ea39679..46a885db347 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1954,12 +1954,12 @@ dependencies = [ [[package]] name = "fd-lock" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" +checksum = "9799aefb4a2e4a01cc47610b1dd47c18ab13d991f27bbcaed9296f5a53d5cbad" dependencies = [ "cfg-if", - "rustix 0.36.11", + "rustix 0.37.5", "windows-sys 0.45.0", ] @@ -2601,7 +2601,7 @@ checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.4", + "rustix 0.37.5", "windows-sys 0.45.0", ] @@ -4348,9 +4348,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.4" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c348b5dc624ecee40108aa2922fed8bad89d7fcc2b9f8cb18f632898ac4a37f9" +checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" dependencies = [ "bitflags 1.3.2", "errno 0.3.0", @@ -5070,7 +5070,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.4", + "rustix 0.37.5", "windows-sys 0.45.0", ] diff --git a/examples/rust/file_transfer/examples/receiver.rs b/examples/rust/file_transfer/examples/receiver.rs index c638091a7cd..90f4c66aedc 100644 --- a/examples/rust/file_transfer/examples/receiver.rs +++ b/examples/rust/file_transfer/examples/receiver.rs @@ -5,10 +5,9 @@ use ockam::access_control::AllowAll; use ockam::remote::RemoteForwarderTrustOptions; use ockam::{ errcode::{Kind, Origin}, - identity::{Identity, TrustEveryonePolicy}, + identity::TrustEveryonePolicy, remote::RemoteForwarder, - vault::Vault, - Context, Error, Result, Routed, TcpConnectionTrustOptions, TcpTransport, Worker, + services, Context, Error, Result, Routed, TcpConnectionTrustOptions, TcpTransport, Worker, }; use tokio::fs::OpenOptions; use tokio::io::AsyncWriteExt; @@ -84,16 +83,15 @@ async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Receiver. - let vault = Vault::create(); - // Create an Identity to represent Receiver. - let receiver = Identity::create(&ctx, vault).await?; + let services = services(); + let receiver = services.identities_creation().create_identity().await?; // Create a secure channel listener for Receiver that will wait for requests to // initiate an Authenticated Key Exchange. - receiver - .create_secure_channel_listener("listener", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &receiver, "listener", TrustEveryonePolicy) .await?; // The computer that is running this program is likely within a private network and diff --git a/examples/rust/file_transfer/examples/sender.rs b/examples/rust/file_transfer/examples/sender.rs index f43675f5381..93ec5cd397f 100644 --- a/examples/rust/file_transfer/examples/sender.rs +++ b/examples/rust/file_transfer/examples/sender.rs @@ -1,12 +1,7 @@ // examples/sender.rs use file_transfer::{FileData, FileDescription}; -use ockam::{ - identity::{Identity, TrustEveryonePolicy}, - route, - vault::Vault, - Context, -}; +use ockam::{identity::TrustEveryonePolicy, route, services, Context}; use ockam::{TcpConnectionTrustOptions, TcpTransport}; use std::path::PathBuf; @@ -39,11 +34,9 @@ async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Sender. - let vault = Vault::create(); - // Create an Identity to represent Sender. - let sender = Identity::create(&ctx, vault).await?; + let services = services(); + let sender = services.identities_creation().create_identity().await?; // This program expects that the receiver has setup a forwarding address, // for his secure channel listener, on the Ockam node at 1.node.ockam.network:4000. @@ -62,8 +55,9 @@ async fn main(ctx: Context) -> Result<()> { // As Sender, connect to the Receiver's secure channel listener, and perform an // Authenticated Key Exchange to establish an encrypted secure channel with Receiver. - let channel = sender - .create_secure_channel(route_to_receiver_listener, TrustEveryonePolicy) + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &sender, route_to_receiver_listener, TrustEveryonePolicy) .await?; println!("\n[✓] End-to-end encrypted secure channel was established.\n"); diff --git a/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-initiator.rs b/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-initiator.rs index c38f26a18a2..d38a17d9a61 100644 --- a/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-initiator.rs +++ b/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-initiator.rs @@ -1,26 +1,27 @@ // This node creates an end-to-end encrypted secure channel over two tcp transport hops. // It then routes a message, to a worker on a different node, through this encrypted channel. -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{route, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{route, services, Context, Result, TcpConnectionTrustOptions, TcpTransport}; #[ockam::node] async fn main(mut ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Alice. - let vault = Vault::create(); - // Create an Identity to represent Alice. - let alice = Identity::create(&ctx, vault).await?; + let services = services(); + let alice = services.identities_creation().create_identity().await?; // Create a TCP connection to the middle node. let connection_to_middle_node = tcp.connect("localhost:3000", TcpConnectionTrustOptions::new()).await?; // Connect to a secure channel listener and perform a handshake. let r = route![connection_to_middle_node, "forward_to_bob", "bob_listener"]; - let channel = alice.create_secure_channel(r, TrustEveryonePolicy).await?; + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &alice, r, TrustEveryonePolicy) + .await?; // Send a message to the echoer worker via the channel. ctx.send(route![channel, "echoer"], "Hello Ockam!".to_string()).await?; diff --git a/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-responder.rs b/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-responder.rs index 75cad1d2930..b0ccbd8aec1 100644 --- a/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-responder.rs +++ b/examples/rust/get_started/examples/05-secure-channel-over-two-transport-hops-responder.rs @@ -3,8 +3,8 @@ use hello_ockam::Echoer; use ockam::access_control::AllowAll; -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{vault::Vault, Context, Result, TcpListenerTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{services, Context, Result, TcpListenerTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { @@ -13,15 +13,14 @@ async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Bob. - let vault = Vault::create(); - - // Create an Identity to represent Bob. - let bob = Identity::create(&ctx, vault).await?; + let services = services(); + let bob = services.identities_creation().create_identity().await?; // Create a secure channel listener for Bob that will wait for requests to // initiate an Authenticated Key Exchange. - bob.create_secure_channel_listener("bob_listener", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &bob, "bob_listener", TrustEveryonePolicy) .await?; // Create a TCP listener and wait for incoming connections. diff --git a/examples/rust/get_started/examples/06-credentials-exchange-client.rs b/examples/rust/get_started/examples/06-credentials-exchange-client.rs index 2396d3f98c3..5f83f91a312 100644 --- a/examples/rust/get_started/examples/06-credentials-exchange-client.rs +++ b/examples/rust/get_started/examples/06-credentials-exchange-client.rs @@ -1,9 +1,6 @@ -use ockam::authenticated_storage::AuthenticatedAttributeStorage; -use ockam::identity::credential_issuer::{CredentialIssuerApi, CredentialIssuerClient}; -use ockam::identity::{Identity, SecureChannelTrustOptions, TrustEveryonePolicy}; +use ockam::identity::{CredentialIssuerApi, CredentialIssuerClient, SecureChannelTrustOptions, TrustEveryonePolicy}; use ockam::sessions::Sessions; -use ockam::{route, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpTransport}; -use std::sync::Arc; +use ockam::{route, services, Context, Result, TcpConnectionTrustOptions, TcpTransport}; #[ockam::node] async fn main(mut ctx: Context) -> Result<()> { @@ -14,7 +11,7 @@ async fn main(mut ctx: Context) -> Result<()> { // We preload the client vault with a change history and secret key corresponding to the identity identifier // Pe92f183eb4c324804ef4d62962dea94cf095a265d4d28500c34e1a4e0d5ef638 // which is an identifier known to the credential issuer, with some preset attributes - let vault = Vault::create(); + let services = services(); // Create an Identity representing the server // Load an identity corresponding to the following public identifier @@ -24,8 +21,10 @@ async fn main(mut ctx: Context) -> Result<()> { // to the credential issuer as a member of the production cluster. let change_history = "01dcf392551f796ef1bcb368177e53f9a5875a962f67279259207d24a01e690721000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020a0d205f09cab9a9467591fcee560429aab1215d8136e5c985a6b7dc729e6f08203010140b098463a727454c0e5292390d8f4cbd4dd0cae5db95606832f3d0a138936487e1da1489c40d8a0995fce71cc1948c6bcfd67186467cdd78eab7e95c080141505"; let secret = "41b6873b20d95567bf958e6bab2808e9157720040882630b1bb37a72f4015cd2"; - let client = Identity::create_identity_with_change_history(&ctx, vault, change_history, secret).await?; - let store = client.authenticated_storage(); + let client = services + .identities_creation() + .import_private_identity(change_history, secret) + .await?; // Connect with the credential issuer and authenticate using the latest private // key of this program's hardcoded identity. @@ -40,13 +39,13 @@ async fn main(mut ctx: Context) -> Result<()> { let issuer_trust_options = SecureChannelTrustOptions::new() .with_trust_policy(TrustEveryonePolicy) .as_consumer(&sessions, &session_id); - let issuer_channel = client - .create_secure_channel(route![issuer_connection, "secure"], issuer_trust_options) + let issuer_channel = services + .secure_channels() + .create_secure_channel(&ctx, &client, route![issuer_connection, "secure"], issuer_trust_options) .await?; let issuer_client = CredentialIssuerClient::new(&ctx, route![issuer_channel]).await?; - let credential = issuer_client.get_credential(client.identifier()).await?.unwrap(); + let credential = issuer_client.get_credential(&client.identifier()).await?.unwrap(); println!("Credential:\n{credential}"); - client.set_credential(credential).await; // Create a secure channel to the node that is running the Echoer service. let server_session_id = sessions.generate_session_id(); @@ -55,15 +54,23 @@ async fn main(mut ctx: Context) -> Result<()> { let channel_trust_options = SecureChannelTrustOptions::new() .with_trust_policy(TrustEveryonePolicy) .as_consumer(&sessions, &server_session_id); - let channel = client - .create_secure_channel(route![server_connection, "secure"], channel_trust_options) + let channel = services + .secure_channels() + .create_secure_channel( + &ctx, + &client, + route![server_connection, "secure"], + channel_trust_options, + ) .await?; // Present credentials over the secure channel - let storage = Arc::new(AuthenticatedAttributeStorage::new(store.clone())); - let issuer = issuer_client.public_identity().await?; + let issuer = issuer_client.identity().await?; let r = route![channel.clone(), "credentials"]; - client.present_credential_mutual(r, &[issuer], storage, None).await?; + services + .credentials_server() + .present_credential_mutual(&ctx, r, &[issuer], credential) + .await?; // Send a message to the worker at address "echoer". ctx.send(route![channel, "echoer"], "Hello Ockam!".to_string()).await?; diff --git a/examples/rust/get_started/examples/06-credentials-exchange-issuer.rs b/examples/rust/get_started/examples/06-credentials-exchange-issuer.rs index 7f68bc02282..6f7e9fbb532 100644 --- a/examples/rust/get_started/examples/06-credentials-exchange-issuer.rs +++ b/examples/rust/get_started/examples/06-credentials-exchange-issuer.rs @@ -1,17 +1,18 @@ use ockam::access_control::AllowAll; use ockam::access_control::IdentityIdAccessControl; -use ockam::identity::credential_issuer::CredentialIssuer; -use ockam::identity::TrustEveryonePolicy; -use ockam::{Context, Result, TcpListenerTrustOptions, TcpTransport}; +use ockam::identity::{CredentialIssuer, TrustEveryonePolicy}; +use ockam::{services, Context, Result, TcpListenerTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { - let issuer = CredentialIssuer::create(&ctx).await?; - let issuer_change_history = issuer.identity().change_history().await; - let exported = issuer_change_history.export().unwrap(); - println!("Credential Issuer Identifier: {}", issuer.identity().identifier()); + let services = services(); + let issuer = services.identities_creation().create_identity().await?; + let credential_issuer = CredentialIssuer::new(services.repository(), services.credentials(), &issuer); + let exported = issuer.change_history().export().unwrap(); + println!("Credential Issuer Identifier: {}", issuer.identifier()); println!("Credential Issuer Change History: {}", hex::encode(exported)); + // credential_issuer // Tell this credential issuer about a set of public identifiers that are // known, in advance, to be members of the production cluster. let known_identifiers = vec![ @@ -30,19 +31,25 @@ async fn main(ctx: Context) -> Result<()> { // For a different application this attested attribute set can be different and // distinct for each identifier, but for this example we'll keep things simple. for identifier in known_identifiers.iter() { - issuer.put_attribute_value(identifier, "cluster", "production").await?; + credential_issuer + .put_attribute_value(identifier, "cluster", "production") + .await?; } // Start a secure channel listener that only allows channels where the identity // at the other end of the channel can authenticate with the latest private key // corresponding to one of the above known public identifiers. let p = TrustEveryonePolicy; - issuer.identity().create_secure_channel_listener("secure", p).await?; + services + .secure_channels() + .create_secure_channel_listener(&ctx, &issuer, "secure", p) + .await?; // Start a credential issuer worker that will only accept incoming requests from // authenticated secure channels with our known public identifiers. let allow_known = IdentityIdAccessControl::new(known_identifiers); - ctx.start_worker("issuer", issuer, allow_known, AllowAll).await?; + ctx.start_worker("issuer", credential_issuer, allow_known, AllowAll) + .await?; // Initialize TCP Transport, create a TCP listener, and wait for connections. let tcp = TcpTransport::create(&ctx).await?; diff --git a/examples/rust/get_started/examples/06-credentials-exchange-server.rs b/examples/rust/get_started/examples/06-credentials-exchange-server.rs index ddffb4f4389..1786f53157d 100644 --- a/examples/rust/get_started/examples/06-credentials-exchange-server.rs +++ b/examples/rust/get_started/examples/06-credentials-exchange-server.rs @@ -3,18 +3,18 @@ use hello_ockam::Echoer; use ockam::abac::AbacAccessControl; use ockam::access_control::AllowAll; -use ockam::authenticated_storage::AuthenticatedAttributeStorage; -use ockam::identity::credential_issuer::{CredentialIssuerApi, CredentialIssuerClient}; -use ockam::identity::{Identity, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, TrustEveryonePolicy}; +use ockam::identity::{ + CredentialIssuerApi, CredentialIssuerClient, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, + TrustEveryonePolicy, +}; use ockam::sessions::Sessions; -use ockam::{route, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpListenerTrustOptions, TcpTransport}; -use std::sync::Arc; +use ockam::{route, services, Context, Result, TcpConnectionTrustOptions, TcpListenerTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport let tcp = TcpTransport::create(&ctx).await?; - let vault = Vault::create(); + let services = services(); // Create an Identity representing the server // Load an identity corresponding to the following public identifier @@ -24,8 +24,10 @@ async fn main(ctx: Context) -> Result<()> { // to the credential issuer as a member of the production cluster. let change_history = "01ed8a5b1303f975c1296c990d1bd3c1946cfef328de20531e3511ec5604ce0dd9000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020e8c328bc0cc07a374762091d037e69c36fdd4d2e1a651abd4d43a1362d3f800503010140a349968063d7337d0c965969fa9c640824c01a6d37fe130d4ab963b0271b9d5bbf0923faa5e27f15359554f94f08676df01b99d997944e4feaf0caaa1189480e"; let secret = "5b2b3f2abbd1787704d8f8b363529f8e2d8f423b6dd4b96a2c462e4f0e04ee18"; - let server = Identity::create_identity_with_change_history(&ctx, vault, change_history, secret).await?; - let store = server.authenticated_storage(); + let server = services + .identities_creation() + .import_private_identity(change_history, secret) + .await?; // Connect with the credential issuer and authenticate using the latest private // key of this program's hardcoded identity. @@ -40,26 +42,32 @@ async fn main(ctx: Context) -> Result<()> { let issuer_trust_options = SecureChannelTrustOptions::new() .with_trust_policy(TrustEveryonePolicy) .as_consumer(&sessions, &session_id); - let issuer_channel = server - .create_secure_channel(route![issuer_connection, "secure"], issuer_trust_options) + let issuer_channel = services + .secure_channels() + .create_secure_channel(&ctx, &server, route![issuer_connection, "secure"], issuer_trust_options) .await?; let issuer = CredentialIssuerClient::new(&ctx, route![issuer_channel]).await?; - let credential = issuer.get_credential(server.identifier()).await?.unwrap(); + let credential = issuer.get_credential(&server.identifier()).await?.unwrap(); println!("Credential:\n{credential}"); - server.set_credential(credential).await; // Start an echoer worker that will only accept incoming requests from // identities that have authenticated credentials issued by the above credential // issuer. These credentials must also attest that requesting identity is // a member of the production cluster. - let allow_production = AbacAccessControl::create(store.clone(), "cluster", "production"); + let allow_production = AbacAccessControl::create(services.repository(), "cluster", "production"); ctx.start_worker("echoer", Echoer, allow_production, AllowAll).await?; // Start a worker which will receive credentials sent by the client and issued by the issuer node - let issuer_identity = issuer.public_identity().await?; - let storage = Arc::new(AuthenticatedAttributeStorage::new(store.clone())); - server - .start_credential_exchange_worker(vec![issuer_identity], "credentials", true, storage) + let issuer_identity = issuer.identity().await?; + services + .credentials_server() + .start( + &ctx, + Some(credential), + vec![issuer_identity], + "credentials".into(), + true, + ) .await?; // Start a secure channel listener that only allows channels with @@ -68,7 +76,10 @@ async fn main(ctx: Context) -> Result<()> { let trust_options = SecureChannelListenerTrustOptions::new() .with_trust_policy(TrustEveryonePolicy) .as_spawner(&sessions, &listener_session_id); - server.create_secure_channel_listener("secure", trust_options).await?; + services + .secure_channels() + .create_secure_channel_listener(&ctx, &server, "secure", trust_options) + .await?; // Create a TCP listener and wait for incoming connections let tcp_listener_trust_options = TcpListenerTrustOptions::new().as_spawner(&sessions, &listener_session_id); diff --git a/examples/rust/get_started/examples/10-secure-channel-via-streams-initiator.rs b/examples/rust/get_started/examples/10-secure-channel-via-streams-initiator.rs index 584ef45e49c..b9918d5c548 100644 --- a/examples/rust/get_started/examples/10-secure-channel-via-streams-initiator.rs +++ b/examples/rust/get_started/examples/10-secure-channel-via-streams-initiator.rs @@ -1,7 +1,6 @@ -use ockam::identity::{Identity, TrustEveryonePolicy}; +use ockam::identity::TrustEveryonePolicy; use ockam::{ - route, stream::Stream, vault::Vault, Context, MessageReceiveOptions, Result, TcpConnectionTrustOptions, - TcpTransport, + route, services, stream::Stream, Context, MessageReceiveOptions, Result, TcpConnectionTrustOptions, TcpTransport, }; #[ockam::node] @@ -14,11 +13,9 @@ async fn main(mut ctx: Context) -> Result<()> { .connect(hub_node_tcp_address, TcpConnectionTrustOptions::new()) .await?; - // Create a vault - let vault = Vault::create(); - // Create an Identity - let alice = Identity::create(&ctx, vault).await?; + let services = services(); + let alice = services.identities_creation().create_identity().await?; // Create a stream client let (sender, _receiver) = Stream::new(&ctx) @@ -34,8 +31,11 @@ async fn main(mut ctx: Context) -> Result<()> { .await?; // Create a secure channel - let secure_channel = alice + let secure_channel = services + .secure_channels() .create_secure_channel( + &ctx, + &alice, route![ sender.clone(), // via the "sc-initiator-to-responder" stream "secure_channel_listener" // to the "secure_channel_listener" listener diff --git a/examples/rust/get_started/examples/10-secure-channel-via-streams-responder.rs b/examples/rust/get_started/examples/10-secure-channel-via-streams-responder.rs index 9d6c1c905a9..1c46478dfd7 100644 --- a/examples/rust/get_started/examples/10-secure-channel-via-streams-responder.rs +++ b/examples/rust/get_started/examples/10-secure-channel-via-streams-responder.rs @@ -1,7 +1,7 @@ use hello_ockam::Echoer; use ockam::access_control::AllowAll; -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{route, stream::Stream, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{route, services, stream::Stream, Context, Result, TcpConnectionTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { @@ -16,14 +16,14 @@ async fn main(ctx: Context) -> Result<()> { .connect(hub_node_tcp_address, TcpConnectionTrustOptions::new()) .await?; - // Create a vault - let vault = Vault::create(); - // Create an Identity - let bob = Identity::create(&ctx, vault).await?; + let services = services(); + let bob = services.identities_creation().create_identity().await?; // Create a secure channel listener at address "secure_channel_listener" - bob.create_secure_channel_listener("secure_channel_listener", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &bob, "secure_channel_listener", TrustEveryonePolicy) .await?; // Create a stream client diff --git a/examples/rust/get_started/examples/11-attribute-based-authentication-control-plane.rs b/examples/rust/get_started/examples/11-attribute-based-authentication-control-plane.rs index da17fad5e46..b4909eebefd 100644 --- a/examples/rust/get_started/examples/11-attribute-based-authentication-control-plane.rs +++ b/examples/rust/get_started/examples/11-attribute-based-authentication-control-plane.rs @@ -1,14 +1,11 @@ use hello_ockam::{create_token, import_project}; -use ockam::identity::authenticated_storage::AuthenticatedAttributeStorage; -use ockam::identity::credential::OneTimeCode; -use ockam::identity::{Identity, SecureChannelTrustOptions, TrustEveryonePolicy, TrustMultiIdentifiersPolicy}; - use ockam::abac::AbacAccessControl; +use ockam::identity::credential::OneTimeCode; +use ockam::identity::{SecureChannelTrustOptions, TrustEveryonePolicy, TrustMultiIdentifiersPolicy}; use ockam::remote::{RemoteForwarder, RemoteForwarderTrustOptions}; -use ockam::{route, vault::Vault, Context, Result, TcpOutletTrustOptions, TcpTransport}; +use ockam::{route, services, Context, Result, TcpOutletTrustOptions, TcpTransport}; use ockam_api::authenticator::direct::{CredentialIssuerClient, RpcClient, TokenAcceptorClient}; use ockam_api::{create_tcp_session, DefaultAddress}; -use std::sync::Arc; use std::time::Duration; /// This node supports a "control" server on which several "edge" devices can connect @@ -49,14 +46,14 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime let tcp = TcpTransport::create(&ctx).await?; // Create an Identity for the control node - let vault = Vault::create(); - let control_plane = Identity::create(&ctx, vault.clone()).await?; + let services = services(); + let control_plane = services.identities_creation().create_identity().await?; // 2. create a secure channel to the authority // to retrieve the node credential // Import the authority identity and route from the information file - let project = import_project(project_information_path, vault).await?; + let project = import_project(project_information_path, services.identities_vault()).await?; let tcp_session = create_tcp_session(&project.authority_route(), &tcp).await.unwrap(); // FIXME: Handle error let trust_options = SecureChannelTrustOptions::new().with_trust_policy(TrustMultiIdentifiersPolicy::new(vec![ @@ -69,8 +66,15 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime }; // create a secure channel to the authority // when creating the channel we check that the opposite side is indeed presenting the authority identity - let secure_channel = control_plane - .create_secure_channel_extended(tcp_session.route, trust_options, Duration::from_secs(120)) + let secure_channel = services + .secure_channels() + .create_secure_channel_extended( + &ctx, + &control_plane, + tcp_session.route, + trust_options, + Duration::from_secs(120), + ) .await?; let token_acceptor = TokenAcceptorClient::new( @@ -88,21 +92,21 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime println!("{credential}"); - // store the credential and start a credential exchange worker which will be + // start a credential exchange worker which will be // later on to exchange credentials with the edge node - control_plane.set_credential(credential).await; - let storage = AuthenticatedAttributeStorage::new(control_plane.authenticated_storage().clone()); - control_plane - .start_credential_exchange_worker( + services + .credentials_server() + .start( + &ctx, + Some(credential.clone()), vec![project.authority_public_identity()], - "credential_exchange", + "credential_exchange".into(), true, - Arc::new(storage), ) .await?; // 3. create an access control policy checking the value of the "component" attribute of the caller - let access_control = AbacAccessControl::create(control_plane.authenticated_storage().clone(), "component", "edge"); + let access_control = AbacAccessControl::create(services.repository(), "component", "edge"); // 4. create a tcp outlet with the above policy tcp.create_outlet( @@ -125,8 +129,11 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime // create a secure channel to the project first // we make sure that we indeed connect to the correct project on the Orchestrator - let secure_channel_address = control_plane + let secure_channel_address = services + .secure_channels() .create_secure_channel_extended( + &ctx, + &control_plane, tcp_project_session.route, project_trust_options, Duration::from_secs(120), @@ -135,10 +142,12 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime println!("secure channel to project: {secure_channel_address:?}"); // present this node credential to the project - control_plane + services + .credentials_server() .present_credential( + &ctx, route![secure_channel_address.clone(), DefaultAddress::CREDENTIALS_SERVICE], - None, + credential, ) .await?; @@ -154,8 +163,9 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime // 6. create a secure channel listener which will allow the edge node to // start a secure channel when it is ready - control_plane - .create_secure_channel_listener("untrusted", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &control_plane, "untrusted", TrustEveryonePolicy) .await?; println!("created a secure channel listener"); diff --git a/examples/rust/get_started/examples/11-attribute-based-authentication-edge-plane.rs b/examples/rust/get_started/examples/11-attribute-based-authentication-edge-plane.rs index 5ecda1bee44..941022bae07 100644 --- a/examples/rust/get_started/examples/11-attribute-based-authentication-edge-plane.rs +++ b/examples/rust/get_started/examples/11-attribute-based-authentication-edge-plane.rs @@ -1,12 +1,11 @@ -use ockam_core::compat::sync::Arc; use std::time::Duration; use hello_ockam::{create_token, import_project}; use ockam::abac::AbacAccessControl; -use ockam::identity::authenticated_storage::AuthenticatedAttributeStorage; use ockam::identity::credential::OneTimeCode; -use ockam::identity::{Identity, SecureChannelTrustOptions, TrustEveryonePolicy, TrustMultiIdentifiersPolicy}; -use ockam::{route, vault::Vault, Context, Result, TcpInletTrustOptions, TcpTransport}; +use ockam::identity::{identities, SecureChannelTrustOptions, TrustEveryonePolicy, TrustMultiIdentifiersPolicy}; +use ockam::{route, Context, Result, TcpTransport}; +use ockam::{services, TcpInletTrustOptions}; use ockam_api::authenticator::direct::{CredentialIssuerClient, RpcClient, TokenAcceptorClient}; use ockam_api::{create_tcp_session, DefaultAddress}; @@ -47,14 +46,14 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime let tcp = TcpTransport::create(&ctx).await?; // Create an Identity for the edge plane - let vault = Vault::create(); - let edge_plane = Identity::create(&ctx, vault.clone()).await?; + let services = services(); + let edge_plane = services.identities_creation().create_identity().await?; // 2. create a secure channel to the authority // to retrieve the node credential // Import the authority identity and route from the information file - let project = import_project(project_information_path, vault).await?; + let project = import_project(project_information_path, services.identities_vault()).await?; let tcp_authority_session = create_tcp_session(&project.authority_route(), &tcp).await.unwrap(); // FIXME: Handle error let authority_trust_options = @@ -69,8 +68,15 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime // create a secure channel to the authority // when creating the channel we check that the opposite side is indeed presenting the authority identity - let secure_channel = edge_plane - .create_secure_channel_extended(tcp_authority_session.route, trust_options, Duration::from_secs(120)) + let secure_channel = services + .secure_channels() + .create_secure_channel_extended( + &ctx, + &edge_plane, + tcp_authority_session.route, + trust_options, + Duration::from_secs(120), + ) .await?; let token_acceptor = TokenAcceptorClient::new( @@ -90,20 +96,19 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime // store the credential and start a credential exchange worker which will be // later on to exchange credentials with the control node - edge_plane.set_credential(credential.to_owned()).await; - - let storage = AuthenticatedAttributeStorage::new(edge_plane.authenticated_storage().clone()); - edge_plane - .start_credential_exchange_worker( + services + .credentials_server() + .start( + &ctx, + Some(credential.clone()), vec![project.authority_public_identity()], - "credential_exchange", + "credential_exchange".into(), true, - Arc::new(storage), ) .await?; // 3. create an access control policy checking the value of the "component" attribute of the caller - let access_control = AbacAccessControl::create(edge_plane.authenticated_storage().clone(), "component", "control"); + let access_control = AbacAccessControl::create(identities().repository(), "component", "control"); // 4. create a tcp inlet with the above policy @@ -117,8 +122,11 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime }; // 4.1 first created a secure channel to the project - let secure_channel_address = edge_plane + let secure_channel_address = services + .secure_channels() .create_secure_channel_extended( + &ctx, + &edge_plane, tcp_project_session.route, project_trust_options, Duration::from_secs(120), @@ -127,17 +135,22 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime println!("secure channel address to the project: {secure_channel_address:?}"); // 4.2 and send this node credential to the project - edge_plane + services + .credentials_server() .present_credential( + &ctx, route![secure_channel_address.clone(), DefaultAddress::CREDENTIALS_SERVICE], - None, + credential.clone(), ) .await?; // 4.3 then create a secure channel to the control node (via its forwarder) let secure_channel_listener_route = route![secure_channel_address, "forward_to_control_plane1", "untrusted"]; - let secure_channel_to_control = edge_plane + let secure_channel_to_control = services + .secure_channels() .create_secure_channel_extended( + &ctx, + &edge_plane, secure_channel_listener_route.clone(), TrustEveryonePolicy, Duration::from_secs(120), @@ -147,13 +160,13 @@ async fn start_node(ctx: Context, project_information_path: &str, token: OneTime println!("secure channel address to the control node: {secure_channel_to_control:?}"); // 4.4 exchange credential with the control node - let storage = AuthenticatedAttributeStorage::new(edge_plane.authenticated_storage().clone()); - edge_plane + services + .credentials_server() .present_credential_mutual( + &ctx, route![secure_channel_to_control.clone(), "credential_exchange"], - vec![&project.authority_public_identity()], - Arc::new(storage), - None, + &[project.authority_public_identity()], + credential, ) .await?; println!("credential exchange done"); diff --git a/examples/rust/get_started/examples/alice.rs b/examples/rust/get_started/examples/alice.rs index c9f4980472a..937f37db072 100644 --- a/examples/rust/get_started/examples/alice.rs +++ b/examples/rust/get_started/examples/alice.rs @@ -1,5 +1,5 @@ -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{route, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{route, services, Context, Result, TcpConnectionTrustOptions, TcpTransport}; use std::io; #[ockam::node] @@ -7,11 +7,9 @@ async fn main(mut ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Alice. - let vault = Vault::create(); - // Create an Identity to represent Alice. - let alice = Identity::create(&ctx, vault).await?; + let services = services(); + let alice = services.identities_creation().create_identity().await?; // This program expects that Bob has setup a forwarding address, // for his secure channel listener, on the Ockam node at 1.node.ockam.network:4000. @@ -31,8 +29,9 @@ async fn main(mut ctx: Context) -> Result<()> { // As Alice, connect to Bob's secure channel listener, and perform an // Authenticated Key Exchange to establish an encrypted secure channel with Bob. - let channel = alice - .create_secure_channel(route_to_bob_listener, TrustEveryonePolicy) + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &alice, route_to_bob_listener, TrustEveryonePolicy) .await?; println!("\n[✓] End-to-end encrypted secure channel was established.\n"); diff --git a/examples/rust/get_started/examples/bob.rs b/examples/rust/get_started/examples/bob.rs index 005060fb570..92f72f15639 100644 --- a/examples/rust/get_started/examples/bob.rs +++ b/examples/rust/get_started/examples/bob.rs @@ -1,8 +1,8 @@ use ockam::access_control::AllowAll; -use ockam::identity::{Identity, TrustEveryonePolicy}; +use ockam::identity::TrustEveryonePolicy; use ockam::remote::RemoteForwarderTrustOptions; -use ockam::{remote::RemoteForwarder, Routed, TcpConnectionTrustOptions, TcpTransport, Worker}; -use ockam::{vault::Vault, Context, Result}; +use ockam::{remote::RemoteForwarder, services, Routed, TcpConnectionTrustOptions, TcpTransport, Worker}; +use ockam::{Context, Result}; struct Echoer; @@ -30,15 +30,15 @@ async fn main(ctx: Context) -> Result<()> { // This worker will echo back every message it receives, along its return route. ctx.start_worker("echoer", Echoer, AllowAll, AllowAll).await?; - // Create a Vault to safely store secret keys for Bob. - let vault = Vault::create(); - // Create an Identity to represent Bob. - let bob = Identity::create(&ctx, vault).await?; + let services = services(); + let bob = services.identities_creation().create_identity().await?; // Create a secure channel listener for Bob that will wait for requests to // initiate an Authenticated Key Exchange. - bob.create_secure_channel_listener("listener", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &bob, "listener", TrustEveryonePolicy) .await?; // The computer that is running this program is likely within a private network and diff --git a/examples/rust/get_started/examples/hello.rs b/examples/rust/get_started/examples/hello.rs index 428f85dfaec..8d90e223771 100644 --- a/examples/rust/get_started/examples/hello.rs +++ b/examples/rust/get_started/examples/hello.rs @@ -1,28 +1,27 @@ -use ockam::{ - identity::{Identity, TrustEveryonePolicy}, - route, - vault::Vault, - Context, Result, -}; +use ockam::{identity::TrustEveryonePolicy, route, services, Context, Result}; #[ockam::node] async fn main(mut ctx: Context) -> Result<()> { - // Create a Vault to safely store secret keys for Alice and Bob. - let vault = Vault::create(); - // Create an Identity to represent Bob. - let bob = Identity::create(&ctx, vault.clone()).await?; + let services = services(); + let bob = services.identities_creation().create_identity().await?; // Create a secure channel listener for Bob that will wait for requests to // initiate an Authenticated Key Exchange. - bob.create_secure_channel_listener("bob", TrustEveryonePolicy).await?; + services + .secure_channels() + .create_secure_channel_listener(&ctx, &bob, "bob", TrustEveryonePolicy) + .await?; // Create an entity to represent Alice. - let alice = Identity::create(&ctx, vault.clone()).await?; + let alice = services.identities_creation().create_identity().await?; // As Alice, connect to Bob's secure channel listener and perform an // Authenticated Key Exchange to establish an encrypted secure channel with Bob. - let channel = alice.create_secure_channel("bob", TrustEveryonePolicy).await?; + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &alice, "bob", TrustEveryonePolicy) + .await?; // Send a message, ** THROUGH ** the secure channel, // to the "app" worker on the other side. diff --git a/examples/rust/get_started/src/project.rs b/examples/rust/get_started/src/project.rs index 30b9f6a1af9..c1ba4e1dc9f 100644 --- a/examples/rust/get_started/src/project.rs +++ b/examples/rust/get_started/src/project.rs @@ -1,5 +1,5 @@ use anyhow::anyhow; -use ockam::identity::{IdentityIdentifier, IdentityVault, PublicIdentity}; +use ockam::identity::{IdentitiesCreation, IdentitiesVault, Identity, IdentityIdentifier}; use ockam_core::errcode::{Kind, Origin}; use ockam_core::{Error, Result}; use ockam_multiaddr::MultiAddr; @@ -13,7 +13,7 @@ use std::sync::Arc; /// when running `ockam project information > project.json` pub struct Project { pub project_identifier: IdentityIdentifier, - pub authority_public_identity: PublicIdentity, + pub authority_public_identity: Identity, pub authority_route: MultiAddr, pub project_route: MultiAddr, } @@ -26,13 +26,13 @@ impl Project { } /// Return the public identity of the authority - pub fn authority_public_identity(&self) -> PublicIdentity { + pub fn authority_public_identity(&self) -> Identity { self.authority_public_identity.clone() } /// Return the identifier of the authority pub fn authority_public_identifier(&self) -> IdentityIdentifier { - self.authority_public_identity.identifier().clone() + self.authority_public_identity.identifier() } /// Return the authority route @@ -48,14 +48,16 @@ impl Project { /// Import a project identity into a Vault from a project.json path /// and return a Project struct -pub async fn import_project(path: &str, vault: Arc) -> Result { +pub async fn import_project(path: &str, vault: Arc) -> Result { match read_json(path)? { Value::Object(values) => { let project_identifier = IdentityIdentifier::from_str(get_field_as_str(&values, "identity")?.as_str())?; let authority_identity = get_field_as_str(&values, "authority_identity")?; - let authority_public_identity = - PublicIdentity::import(&hex::decode(authority_identity).unwrap(), vault).await?; + let identities_creation = IdentitiesCreation::new(vault); + let authority_public_identity = identities_creation + .import_identity(&hex::decode(authority_identity).unwrap()) + .await?; let authority_access_route = get_field_as_str(&values, "authority_access_route")?; let authority_route = diff --git a/examples/rust/ockam_kafka/examples/ockam_kafka_alice.rs b/examples/rust/ockam_kafka/examples/ockam_kafka_alice.rs index de9ecf4ee85..1d55fa3cc61 100644 --- a/examples/rust/ockam_kafka/examples/ockam_kafka_alice.rs +++ b/examples/rust/ockam_kafka/examples/ockam_kafka_alice.rs @@ -1,10 +1,6 @@ use ockam::{ - identity::{Identity, TrustEveryonePolicy}, - route, - stream::Stream, - unique_with_prefix, - vault::Vault, - Context, Result, TcpConnectionTrustOptions, TcpTransport, + identity::TrustEveryonePolicy, route, services, stream::Stream, unique_with_prefix, Context, Result, + TcpConnectionTrustOptions, TcpTransport, }; use std::io; @@ -13,11 +9,9 @@ async fn main(mut ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Alice. - let vault = Vault::create(); - // Create an Identity to represent Alice. - let alice = Identity::create(&ctx, vault).await?; + let services = services(); + let alice = services.identities_creation().create_identity().await?; // This program expects that Bob has created two streams // bob_to_alice and alice_to_bob on the cloud node at 1.node.ockam.network:4000 @@ -57,7 +51,10 @@ async fn main(mut ctx: Context) -> Result<()> { // perform an Authenticated Key Exchange to establish an encrypted secure // channel with Bob. let r = route![sender.clone(), "listener"]; - let channel = alice.create_secure_channel(r, TrustEveryonePolicy).await?; + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &alice, r, TrustEveryonePolicy) + .await?; println!("\n[✓] End-to-end encrypted secure channel was established.\n"); diff --git a/examples/rust/ockam_kafka/examples/ockam_kafka_bob.rs b/examples/rust/ockam_kafka/examples/ockam_kafka_bob.rs index fe6e31d35e0..aea992f1364 100644 --- a/examples/rust/ockam_kafka/examples/ockam_kafka_bob.rs +++ b/examples/rust/ockam_kafka/examples/ockam_kafka_bob.rs @@ -1,10 +1,7 @@ use ockam::access_control::AllowAll; use ockam::{ - identity::{Identity, TrustEveryonePolicy}, - route, - stream::Stream, - vault::Vault, - Context, Result, Routed, TcpConnectionTrustOptions, TcpTransport, Worker, + identity::TrustEveryonePolicy, route, services, stream::Stream, Context, Result, Routed, TcpConnectionTrustOptions, + TcpTransport, Worker, }; struct Echoer; @@ -29,15 +26,15 @@ async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Bob. - let vault = Vault::create(); - // Create an Identity to represent Bob. - let bob = Identity::create(&ctx, vault).await?; + let services = services(); + let bob = services.identities_creation().create_identity().await?; // Create a secure channel listener for Bob that will wait for requests to // initiate an Authenticated Key Exchange. - bob.create_secure_channel_listener("listener", TrustEveryonePolicy) + services + .secure_channels() + .create_secure_channel_listener(&ctx, &bob, "listener", TrustEveryonePolicy) .await?; // Connect, over TCP, to the cloud node at `1.node.ockam.network:4000` and diff --git a/examples/rust/tcp_inlet_and_outlet/examples/03-inlet.rs b/examples/rust/tcp_inlet_and_outlet/examples/03-inlet.rs index 12c67f09426..b0ee8df7468 100644 --- a/examples/rust/tcp_inlet_and_outlet/examples/03-inlet.rs +++ b/examples/rust/tcp_inlet_and_outlet/examples/03-inlet.rs @@ -1,5 +1,5 @@ -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{route, vault::Vault, Context, Result, TcpConnectionTrustOptions, TcpInletTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{route, services, Context, Result, TcpConnectionTrustOptions, TcpInletTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { @@ -16,14 +16,17 @@ async fn main(ctx: Context) -> Result<()> { // We assume the Outlet node is listening on port 4000, unless otherwise specified // by a second command line argument. - let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; + let services = services(); + let e = services.identities_creation().create_identity().await?; let outlet_port = std::env::args().nth(2).unwrap_or_else(|| "4000".to_string()); let outlet_connection = tcp .connect(&format!("127.0.0.1:{outlet_port}"), TcpConnectionTrustOptions::new()) .await?; let r = route![outlet_connection, "secure_channel_listener"]; - let channel = e.create_secure_channel(r, TrustEveryonePolicy).await?; + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &e, r, TrustEveryonePolicy) + .await?; // We know Secure Channel address that tunnels messages to the node with an Outlet, // we also now that Outlet lives at "outlet" address at that node. diff --git a/examples/rust/tcp_inlet_and_outlet/examples/03-outlet.rs b/examples/rust/tcp_inlet_and_outlet/examples/03-outlet.rs index 2752eeba69c..45f90a518e3 100644 --- a/examples/rust/tcp_inlet_and_outlet/examples/03-outlet.rs +++ b/examples/rust/tcp_inlet_and_outlet/examples/03-outlet.rs @@ -1,5 +1,5 @@ -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{vault::Vault, Context, Result, TcpListenerTrustOptions, TcpOutletTrustOptions, TcpTransport}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{services, Context, Result, TcpListenerTrustOptions, TcpOutletTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { @@ -12,9 +12,11 @@ async fn main(ctx: Context) -> Result<()> { // 3. A Secure Channel Listener at Worker address - secure_channel_listener // that will wait for requests to start an Authenticated Key Exchange. - let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; - e.create_secure_channel_listener("secure_channel_listener", TrustEveryonePolicy) + let services = services(); + let e = services.identities_creation().create_identity().await?; + services + .secure_channels() + .create_secure_channel_listener(&ctx, &e, "secure_channel_listener", TrustEveryonePolicy) .await?; // Expect first command line argument to be the TCP address of a target TCP server. diff --git a/examples/rust/tcp_inlet_and_outlet/examples/04-inlet.rs b/examples/rust/tcp_inlet_and_outlet/examples/04-inlet.rs index c94ab4c34b0..35d544ac6fd 100644 --- a/examples/rust/tcp_inlet_and_outlet/examples/04-inlet.rs +++ b/examples/rust/tcp_inlet_and_outlet/examples/04-inlet.rs @@ -1,7 +1,5 @@ -use ockam::identity::{Identity, TrustEveryonePolicy}; -use ockam::{ - route, vault::Vault, Context, Result, Route, TcpConnectionTrustOptions, TcpInletTrustOptions, TcpTransport, -}; +use ockam::identity::TrustEveryonePolicy; +use ockam::{route, services, Context, Result, Route, TcpConnectionTrustOptions, TcpInletTrustOptions, TcpTransport}; #[ockam::node] async fn main(ctx: Context) -> Result<()> { @@ -15,8 +13,8 @@ async fn main(ctx: Context) -> Result<()> { // For this example, we know that the Outlet node is listening for Ockam Routing Messages // through a Remote Forwarder at "1.node.ockam.network:4000" and its forwarder address // points to secure channel listener. - let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; + let services = services(); + let e = services.identities_creation().create_identity().await?; // Expect second command line argument to be the Outlet node forwarder address let forwarding_address = std::env::args().nth(2).expect("no outlet forwarding address given"); @@ -24,7 +22,10 @@ async fn main(ctx: Context) -> Result<()> { .connect("1.node.ockam.network:4000", TcpConnectionTrustOptions::new()) .await?; let r = route![node_in_hub, forwarding_address, "secure_channel_listener"]; - let channel = e.create_secure_channel(r, TrustEveryonePolicy).await?; + let channel = services + .secure_channels() + .create_secure_channel(&ctx, &e, r, TrustEveryonePolicy) + .await?; // We know Secure Channel address that tunnels messages to the node with an Outlet, // we also now that Outlet lives at "outlet" address at that node. diff --git a/examples/rust/tcp_inlet_and_outlet/examples/04-outlet.rs b/examples/rust/tcp_inlet_and_outlet/examples/04-outlet.rs index e8066612ee2..63a76ef5e02 100644 --- a/examples/rust/tcp_inlet_and_outlet/examples/04-outlet.rs +++ b/examples/rust/tcp_inlet_and_outlet/examples/04-outlet.rs @@ -1,9 +1,7 @@ use ockam::remote::RemoteForwarderTrustOptions; use ockam::{ - identity::{Identity, TrustEveryonePolicy}, - remote::RemoteForwarder, - vault::Vault, - Context, Result, TcpConnectionTrustOptions, TcpOutletTrustOptions, TcpTransport, + identity::TrustEveryonePolicy, remote::RemoteForwarder, services, Context, Result, TcpConnectionTrustOptions, + TcpOutletTrustOptions, TcpTransport, }; #[ockam::node] @@ -11,9 +9,11 @@ async fn main(ctx: Context) -> Result<()> { // Initialize the TCP Transport. let tcp = TcpTransport::create(&ctx).await?; - let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; - e.create_secure_channel_listener("secure_channel_listener", TrustEveryonePolicy) + let services = services(); + let e = services.identities_creation().create_identity().await?; + services + .secure_channels() + .create_secure_channel_listener(&ctx, &e, "secure_channel_listener", TrustEveryonePolicy) .await?; // Expect first command line argument to be the TCP address of a target TCP server. diff --git a/implementations/rust/ockam/ockam/src/lib.rs b/implementations/rust/ockam/ockam/src/lib.rs index 48f26f370e0..b1c10bb7314 100644 --- a/implementations/rust/ockam/ockam/src/lib.rs +++ b/implementations/rust/ockam/ockam/src/lib.rs @@ -67,7 +67,7 @@ pub use ockam_core::{ /// Access Control pub mod access_control { pub use ockam_core::access_control::*; - pub use ockam_identity::access_control::*; + pub use ockam_identity::secure_channel::access_control::*; } /// Sessions @@ -122,14 +122,13 @@ pub mod vault { } } -/// Authenticated Storage -pub mod authenticated_storage { - pub use ockam_identity::authenticated_storage::mem::*; - pub use ockam_identity::authenticated_storage::*; -} - #[cfg(feature = "ockam_transport_tcp")] pub use ockam_transport_tcp::{ TcpConnectionTrustOptions, TcpInletTrustOptions, TcpListenerTrustOptions, TcpOutletTrustOptions, TcpTransport, }; + +/// List of all top-level services +pub mod services; + +pub use services::*; diff --git a/implementations/rust/ockam/ockam/src/services.rs b/implementations/rust/ockam/ockam/src/services.rs new file mode 100644 index 00000000000..5f66dfbac54 --- /dev/null +++ b/implementations/rust/ockam/ockam/src/services.rs @@ -0,0 +1,133 @@ +use ockam_core::compat::sync::Arc; +use ockam_identity::IdentitiesVault; +use ockam_identity::{ + secure_channels, Credentials, CredentialsServer, Identities, IdentitiesCreation, + IdentitiesKeys, IdentitiesRepository, SecureChannelRegistry, SecureChannels, + SecureChannelsBuilder, Storage, +}; + +/// This struct supports all the ockam services for managing identities +/// and creating secure channels +pub struct Services { + secure_channels: Arc, +} + +/// Create default top level services (with no persistence) +pub fn services() -> Arc { + Arc::new(Services { + secure_channels: secure_channels(), + }) +} + +/// Return a new builder for top-level services +pub fn builder() -> ServicesBuilder { + ServicesBuilder::new() +} + +impl Services { + /// Return secure channel services + pub fn secure_channels(&self) -> Arc { + self.secure_channels.clone() + } + + /// Return services to manage identities + pub fn identities(&self) -> Arc { + self.secure_channels().identities() + } + + /// Return services to create and import identities + pub fn identities_creation(&self) -> Arc { + self.identities().identities_creation() + } + + /// Return services to manage identities keys + pub fn identities_keys(&self) -> Arc { + self.identities().identities_keys() + } + + /// Return services to manage credentials + pub fn credentials(&self) -> Arc { + self.identities().credentials() + } + + /// Return services to serve credentials + pub fn credentials_server(&self) -> Arc { + self.identities().credentials_server() + } + + /// Return the repository used to store identities data + pub fn repository(&self) -> Arc { + self.identities().repository() + } + + /// Return the vault used by secure channels + pub fn secure_channels_vault(&self) -> Arc { + self.secure_channels().vault() + } + + /// Return the vault used by identities + pub fn identities_vault(&self) -> Arc { + self.identities().vault() + } +} + +/// Builder for top level services +/// It merely encapsulates a secure channel builder for now +#[derive(Clone)] +pub struct ServicesBuilder { + builder: SecureChannelsBuilder, +} + +impl ServicesBuilder { + fn new() -> ServicesBuilder { + ServicesBuilder { + builder: secure_channels::builder(), + } + } + + /// Set a specific vault storage for identities and secure channels + pub fn with_vault_storage( + &mut self, + storage: Arc, + ) -> ServicesBuilder { + self.builder = self.builder.with_vault_storage(storage); + self.clone() + } + + /// Set a specific identities vault + pub fn with_identities_vault(&mut self, vault: Arc) -> ServicesBuilder { + self.builder = self.builder.with_identities_vault(vault); + self.clone() + } + + /// Set a specific storage for identities + pub fn with_identities_storage(&mut self, storage: Arc) -> ServicesBuilder { + self.builder = self.builder.with_identities_storage(storage); + self.clone() + } + + /// Set a specific identities repository + pub fn with_identities_repository( + &mut self, + repository: Arc, + ) -> ServicesBuilder { + self.builder = self.builder.with_identities_repository(repository); + self.clone() + } + + /// Set a specific secure channels registry + pub fn with_secure_channels_registry( + &mut self, + registry: SecureChannelRegistry, + ) -> ServicesBuilder { + self.builder = self.builder.with_secure_channels_registry(registry); + self.clone() + } + + /// Build top level services + pub fn build(&self) -> Services { + Services { + secure_channels: self.builder.build(), + } + } +} diff --git a/implementations/rust/ockam/ockam/tests/forwarder.rs b/implementations/rust/ockam/ockam/tests/forwarder.rs index 2adbaf391e7..9669ecd80b8 100644 --- a/implementations/rust/ockam/ockam/tests/forwarder.rs +++ b/implementations/rust/ockam/ockam/tests/forwarder.rs @@ -4,11 +4,11 @@ use ockam::ForwardingService; use ockam_core::sessions::{SessionPolicy, Sessions}; use ockam_core::{route, Address, AllowAll, Result}; use ockam_identity::{ - Identity, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, TrustEveryonePolicy, + secure_channels, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, + TrustEveryonePolicy, }; use ockam_node::{Context, MessageReceiveOptions, MessageSendReceiveOptions}; use ockam_transport_tcp::{TcpConnectionTrustOptions, TcpListenerTrustOptions, TcpTransport}; -use ockam_vault::Vault; use std::time::Duration; // Node creates a Forwarding service and a Remote Forwarder, Echoer is reached through the Forwarder. No session @@ -189,9 +189,11 @@ async fn test4(ctx: &mut Context) -> Result<()> { // Cloud ForwardingService::create(ctx, "forwarding_service", AllowAll, AllowAll).await?; - let cloud_identity = Identity::create(ctx, Vault::create()).await?; - cloud_identity - .create_secure_channel_listener("cloud_listener", TrustEveryonePolicy) + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); + let cloud_identity = identities_creation.create_identity().await?; + secure_channels + .create_secure_channel_listener(ctx, &cloud_identity, "cloud_listener", TrustEveryonePolicy) .await?; let cloud_tcp = TcpTransport::create(ctx).await?; @@ -220,17 +222,21 @@ async fn test4(ctx: &mut Context) -> Result<()> { TcpConnectionTrustOptions::new().as_producer(&server_sessions, &server_tcp_session_id), ) .await?; - let server_identity = Identity::create(ctx, Vault::create()).await?; - let cloud_server_channel = server_identity + let server_identity = identities_creation.create_identity().await?; + let cloud_server_channel = secure_channels .create_secure_channel( + ctx, + &server_identity, route![cloud_server_connection, "cloud_listener"], SecureChannelTrustOptions::new() .as_consumer(&server_sessions, &server_tcp_session_id) .as_producer(&server_sessions, &server_channel_session_id), ) .await?; - server_identity + secure_channels .create_secure_channel_listener( + ctx, + &server_identity, "server_listener", SecureChannelListenerTrustOptions::new() .as_consumer( @@ -263,9 +269,11 @@ async fn test4(ctx: &mut Context) -> Result<()> { TcpConnectionTrustOptions::new().as_producer(&client_sessions, &client_tcp_session_id), ) .await?; - let client_identity = Identity::create(ctx, Vault::create()).await?; - let cloud_client_channel = client_identity + let client_identity = identities_creation.create_identity().await?; + let cloud_client_channel = secure_channels .create_secure_channel( + ctx, + &client_identity, route![cloud_client_connection, "cloud_listener"], SecureChannelTrustOptions::new() .as_consumer(&client_sessions, &client_tcp_session_id) @@ -273,8 +281,10 @@ async fn test4(ctx: &mut Context) -> Result<()> { ) .await?; - let tunnel_channel = client_identity + let tunnel_channel = secure_channels .create_secure_channel( + ctx, + &client_identity, route![ cloud_client_channel, remote_info.remote_address(), diff --git a/implementations/rust/ockam/ockam_abac/src/attribute_access_control.rs b/implementations/rust/ockam/ockam_abac/src/attribute_access_control.rs index 5c70c7a4c6e..30dfefc543a 100644 --- a/implementations/rust/ockam/ockam_abac/src/attribute_access_control.rs +++ b/implementations/rust/ockam/ockam_abac/src/attribute_access_control.rs @@ -1,4 +1,5 @@ use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::compat::fmt; use ockam_core::compat::fmt::Debug; use ockam_core::compat::fmt::Formatter; @@ -11,21 +12,17 @@ use tracing as log; use crate::expr::str; use crate::Expr::*; use crate::{eval, Env, Expr}; -use ockam_core::compat::boxed::Box; use ockam_core::compat::format; use ockam_core::compat::string::ToString; use ockam_core::compat::sync::Arc; -use ockam_identity::authenticated_storage::{ - AuthenticatedAttributeStorage, AuthenticatedStorage, IdentityAttributeStorage, -}; -use ockam_identity::IdentitySecureChannelLocalInfo; +use ockam_identity::{IdentitiesRepository, IdentitySecureChannelLocalInfo}; /// This AccessControl uses a storage for authenticated attributes in order /// to verify if a policy expression is valid /// A similar access control policy is available as [`crate::policy::PolicyAccessControl`] where /// as [`crate::PolicyStorage`] can be used to retrieve a specific policy for a given resource and action pub struct AbacAccessControl { - attributes: Arc, + repository: Arc, expression: Expr, environment: Env, } @@ -41,12 +38,12 @@ impl Debug for AbacAccessControl { impl AbacAccessControl { /// Create a new AccessControl using a specific policy for checking attributes pub fn new( - attributes: Arc, + repository: Arc, expression: Expr, environment: Env, ) -> Self { Self { - attributes, + repository, expression, environment, } @@ -55,7 +52,7 @@ impl AbacAccessControl { /// Create an AccessControl which will verify that the sender of /// a message has an authenticated attribute with the correct name and value pub fn create( - storage: Arc, + repository: Arc, attribute_name: &str, attribute_value: &str, ) -> AbacAccessControl @@ -65,11 +62,7 @@ where { Ident(format!("subject.{attribute_name}")), Str(attribute_value.into()), ]); - AbacAccessControl::new( - Arc::new(AuthenticatedAttributeStorage::new(storage)), - expression, - Env::new(), - ) + AbacAccessControl::new(repository, expression, Env::new()) } } @@ -91,7 +84,7 @@ impl IncomingAccessControl for AbacAccessControl { let mut environment = self.environment.clone(); // Get identity attributes and populate the environment: - if let Some(attrs) = self.attributes.get_attributes(&id).await? { + if let Some(attrs) = self.repository.get_attributes(&id).await? { for (key, value) in attrs.attrs() { if key.find(|c: char| c.is_whitespace()).is_some() { log::warn! { diff --git a/implementations/rust/ockam/ockam_abac/src/policy.rs b/implementations/rust/ockam/ockam_abac/src/policy.rs index 5bb2af37649..90f3f6be3f0 100644 --- a/implementations/rust/ockam/ockam_abac/src/policy.rs +++ b/implementations/rust/ockam/ockam_abac/src/policy.rs @@ -9,7 +9,7 @@ use ockam_core::compat::format; use ockam_core::compat::sync::Arc; use ockam_core::{async_trait, RelayMessage}; use ockam_core::{IncomingAccessControl, Result}; -use ockam_identity::authenticated_storage::IdentityAttributeStorage; +use ockam_identity::IdentitiesRepository; use tracing as log; /// Evaluates a policy expression against an environment of attributes. @@ -20,7 +20,7 @@ pub struct PolicyAccessControl { resource: Resource, action: Action, policies: Arc, - attributes: Arc, + repository: Arc, environment: Env, } @@ -44,7 +44,7 @@ impl PolicyAccessControl { /// which may already contain other resource, action or subject attributes. pub fn new( policies: Arc, - store: Arc, + repository: Arc, r: Resource, a: Action, env: Env, @@ -53,7 +53,7 @@ impl PolicyAccessControl { resource: r, action: a, policies, - attributes: store, + repository, environment: env, } } @@ -85,7 +85,7 @@ impl IncomingAccessControl for PolicyAccessControl { return Ok(false); }; - AbacAccessControl::new(self.attributes.clone(), expr, self.environment.clone()) + AbacAccessControl::new(self.repository.clone(), expr, self.environment.clone()) .is_authorized(msg) .await } diff --git a/implementations/rust/ockam/ockam_api/CHANGELOG.md b/implementations/rust/ockam/ockam_api/CHANGELOG.md index 6a83e2af0d8..764dba1f822 100644 --- a/implementations/rust/ockam/ockam_api/CHANGELOG.md +++ b/implementations/rust/ockam/ockam_api/CHANGELOG.md @@ -793,4 +793,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated dependencies - diff --git a/implementations/rust/ockam/ockam_api/src/auth.rs b/implementations/rust/ockam/ockam_api/src/auth.rs index 6e5c78c0e9d..cb709f2a693 100644 --- a/implementations/rust/ockam/ockam_api/src/auth.rs +++ b/implementations/rust/ockam/ockam_api/src/auth.rs @@ -2,19 +2,18 @@ pub mod types; use core::fmt; use minicbor::Decoder; +use ockam::identity::{AttributesEntry, IdentityAttributesReader, IdentityIdentifier}; use ockam_core::api::decode_option; use ockam_core::api::{Method, Request, Response}; use ockam_core::compat::sync::Arc; use ockam_core::{self, Address, DenyAll, Result, Route, Routed, Worker}; -use ockam_identity::authenticated_storage::{AttributesEntry, IdentityAttributeStorageReader}; -use ockam_identity::IdentityIdentifier; use ockam_node::api::request; use ockam_node::Context; use tracing::trace; /// Auth API server. pub struct Server { - store: Arc, + store: Arc, } #[ockam_core::worker] @@ -33,7 +32,7 @@ impl Worker for Server { } impl Server { - pub fn new(s: Arc) -> Self { + pub fn new(s: Arc) -> Self { Server { store: s } } diff --git a/implementations/rust/ockam/ockam_api/src/authenticator/direct.rs b/implementations/rust/ockam/ockam_api/src/authenticator/direct.rs index ba6ea37d12e..68415ce3201 100644 --- a/implementations/rust/ockam/ockam_api/src/authenticator/direct.rs +++ b/implementations/rust/ockam/ockam_api/src/authenticator/direct.rs @@ -3,11 +3,11 @@ pub mod types; use core::{fmt, str}; use lru::LruCache; use minicbor::{Decode, Decoder, Encode}; -use ockam::identity::authenticated_storage::{ - AttributesEntry, IdentityAttributeStorage, IdentityAttributeStorageReader, +use ockam::identity::{ + AttributesEntry, CredentialData, Identities, Identity, IdentityAttributesWriter, }; -use ockam::identity::credential::{Credential, OneTimeCode, SchemaId, Timestamp}; -use ockam::identity::{Identity, IdentityIdentifier, IdentitySecureChannelLocalInfo}; +use ockam::identity::{Credential, OneTimeCode, SchemaId, Timestamp}; +use ockam::identity::{IdentityIdentifier, IdentitySecureChannelLocalInfo}; use ockam_core::api::{self, Error, Method, Request, RequestBuilder, Response, Status}; use ockam_core::compat::sync::{Arc, RwLock}; use ockam_core::errcode::{Kind, Origin}; @@ -151,36 +151,48 @@ impl Worker for LegacyApiConverter { } pub struct CredentialIssuer { + identities: Arc, + issuer: Identity, project: Vec, - store: Arc, - ident: Arc, } impl CredentialIssuer { pub async fn new( + identities: Arc, + issuer: Identity, project: Vec, - store: Arc, - identity: Arc, ) -> Result { Ok(Self { + identities, + issuer, project, - store, - ident: identity, }) } async fn issue_credential(&self, from: &IdentityIdentifier) -> Result> { - match self.store.get_attributes(from).await? { + match self + .identities + .repository() + .as_attributes_reader() + .get_attributes(from) + .await? + { Some(entry) => { let crd = entry .attrs() .iter() .fold( - Credential::builder(from.clone()).with_schema(PROJECT_MEMBER_SCHEMA), + CredentialData::builder(from.clone(), self.issuer.identifier()) + .with_schema(PROJECT_MEMBER_SCHEMA), |crd, (a, v)| crd.with_attribute(a, v), ) .with_attribute(PROJECT_ID, &self.project); - Ok(Some(self.ident.issue_credential(crd).await?)) + Ok(Some( + self.identities + .credentials() + .issue_credential(&self.issuer, crd.build()?) + .await?, + )) } None => Ok(None), } @@ -208,7 +220,7 @@ impl Worker for CredentialIssuer { } let res = match (req.method(), req.path()) { (Some(Method::Post), "/") | (Some(Method::Post), "/credential") => { - match self.issue_credential(from).await { + match self.issue_credential(&from).await { Ok(Some(crd)) => Response::ok(req.id()).body(crd).to_vec()?, Ok(None) => { // Again, this has already been checked by the access control, so if we @@ -229,12 +241,18 @@ impl Worker for CredentialIssuer { pub struct DirectAuthenticator { project: Vec, - store: Arc, + attributes_writer: Arc, } impl DirectAuthenticator { - pub async fn new(project: Vec, store: Arc) -> Result { - Ok(Self { project, store }) + pub async fn new( + project: Vec, + attributes_writer: Arc, + ) -> Result { + Ok(Self { + project, + attributes_writer, + }) } async fn add_member<'a>( @@ -254,7 +272,7 @@ impl DirectAuthenticator { None, Some(enroller.clone()), ); - self.store.put_attributes(id, entry).await + self.attributes_writer.put_attributes(id, entry).await } } @@ -280,7 +298,7 @@ impl Worker for DirectAuthenticator { let res = match (req.method(), req.path()) { (Some(Method::Post), "/") | (Some(Method::Post), "/members") => { let add: AddMember = dec.decode()?; - self.add_member(from, add.member(), add.attributes()) + self.add_member(&from, add.member(), add.attributes()) .await?; Response::ok(req.id()).to_vec()? } @@ -302,13 +320,13 @@ pub struct EnrollmentTokenAuthenticator { pub struct EnrollmentTokenIssuer(EnrollmentTokenAuthenticator); pub struct EnrollmentTokenAcceptor( EnrollmentTokenAuthenticator, - Arc, + Arc, ); impl EnrollmentTokenAuthenticator { pub fn new_worker_pair( project: Vec, - storage: Arc, + attributes_writer: Arc, ) -> (EnrollmentTokenIssuer, EnrollmentTokenAcceptor) { let base = Self { project, @@ -318,7 +336,7 @@ impl EnrollmentTokenAuthenticator { }; ( EnrollmentTokenIssuer(base.clone()), - EnrollmentTokenAcceptor(base, storage), + EnrollmentTokenAcceptor(base, attributes_writer), ) } } @@ -374,7 +392,7 @@ impl Worker for EnrollmentTokenIssuer { let res = match (req.method(), req.path()) { (Some(Method::Post), "/") | (Some(Method::Post), "/tokens") => { let att: CreateToken = dec.decode()?; - match self.issue_token(from, att.into_owned_attributes()).await { + match self.issue_token(&from, att.into_owned_attributes()).await { Ok(otc) => Response::ok(req.id()).body(&otc).to_vec()?, Err(error) => api::internal_error(&req, &error.to_string()).to_vec()?, } @@ -445,7 +463,7 @@ impl Worker for EnrollmentTokenAcceptor { None, Some(tkn.generated_by), ); - self.1.put_attributes(from, entry).await?; + self.1.put_attributes(&from, entry).await?; Response::ok(req.id()).to_vec()? } Err(err) => err.to_vec()?, diff --git a/implementations/rust/ockam/ockam_api/src/authenticator/direct/types.rs b/implementations/rust/ockam/ockam_api/src/authenticator/direct/types.rs index 234f8d3780d..0f331041f3c 100644 --- a/implementations/rust/ockam/ockam_api/src/authenticator/direct/types.rs +++ b/implementations/rust/ockam/ockam_api/src/authenticator/direct/types.rs @@ -1,6 +1,6 @@ use minicbor::{Decode, Encode}; +use ockam::identity::IdentityIdentifier; use ockam_core::CowStr; -use ockam_identity::IdentityIdentifier; use std::collections::HashMap; #[cfg(feature = "tag")] diff --git a/implementations/rust/ockam/ockam_api/src/bootstrapped_identities_store.rs b/implementations/rust/ockam/ockam_api/src/bootstrapped_identities_store.rs index 77172f6253f..490c089dd59 100644 --- a/implementations/rust/ockam/ockam_api/src/bootstrapped_identities_store.rs +++ b/implementations/rust/ockam/ockam_api/src/bootstrapped_identities_store.rs @@ -1,14 +1,12 @@ +use ockam::identity::{ + AttributesEntry, IdentitiesReader, IdentitiesRepository, IdentitiesWriter, Identity, + IdentityAttributesReader, IdentityAttributesWriter, IdentityIdentifier, Timestamp, +}; use ockam_core::async_trait; use ockam_core::compat::sync::Arc; use ockam_core::compat::{collections::HashMap, string::String, vec::Vec}; use ockam_core::errcode::{Kind, Origin}; use ockam_core::Result; -use ockam_identity::authenticated_storage::{ - AttributesEntry, IdentityAttributeStorage, IdentityAttributeStorageReader, - IdentityAttributeStorageWriter, -}; -use ockam_identity::credential::Timestamp; -use ockam_identity::IdentityIdentifier; use serde::{Deserialize, Serialize}; use serde_json as json; use std::path::PathBuf; @@ -16,24 +14,24 @@ use tracing::trace; #[derive(Clone)] pub struct BootstrapedIdentityStore { - bootstrapped: Arc, - storage: Arc, + bootstrapped: Arc, + repository: Arc, } impl BootstrapedIdentityStore { pub fn new( - bootstrapped: Arc, - storage: Arc, + bootstrapped: Arc, + repository: Arc, ) -> Self { Self { bootstrapped, - storage, + repository, } } } #[async_trait] -impl IdentityAttributeStorageReader for BootstrapedIdentityStore { +impl IdentityAttributesReader for BootstrapedIdentityStore { async fn get_attributes( &self, identity_id: &IdentityIdentifier, @@ -44,13 +42,13 @@ impl IdentityAttributeStorageReader for BootstrapedIdentityStore { "get_attributes" } match self.bootstrapped.get_attributes(identity_id).await? { - None => self.storage.get_attributes(identity_id).await, + None => self.repository.get_attributes(identity_id).await, Some(x) => Ok(Some(x)), } } async fn list(&self) -> Result> { - let mut l = self.storage.list().await?; + let mut l = self.repository.list().await?; let mut l2 = self.bootstrapped.list().await?; l.append(&mut l2); Ok(l) @@ -58,7 +56,7 @@ impl IdentityAttributeStorageReader for BootstrapedIdentityStore { } #[async_trait] -impl IdentityAttributeStorageWriter for BootstrapedIdentityStore { +impl IdentityAttributesWriter for BootstrapedIdentityStore { async fn put_attributes( &self, sender: &IdentityIdentifier, @@ -70,7 +68,7 @@ impl IdentityAttributeStorageWriter for BootstrapedIdentityStore { "put_attributes" } match self.bootstrapped.get_attributes(sender).await? { - None => self.storage.put_attributes(sender, entry).await, + None => self.repository.put_attributes(sender, entry).await, Some(_) => Ok(()), /* * TODO: previous client automatically adds the project admin as a member. @@ -88,16 +86,34 @@ impl IdentityAttributeStorageWriter for BootstrapedIdentityStore { } async fn delete(&self, identity: &IdentityIdentifier) -> Result<()> { - self.storage.delete(identity).await + self.repository.delete(identity).await + } +} + +#[async_trait] +impl IdentitiesReader for BootstrapedIdentityStore { + async fn get_identity(&self, identifier: &IdentityIdentifier) -> Result> { + self.repository.get_identity(identifier).await + } +} + +#[async_trait] +impl IdentitiesWriter for BootstrapedIdentityStore { + async fn put_identity(&self, identity: &Identity) -> Result<()> { + self.repository.put_identity(identity).await + } + + async fn update_known_identity(&self, identity: &Identity) -> Result<()> { + self.repository.update_known_identity(identity).await } } -impl IdentityAttributeStorage for BootstrapedIdentityStore { - fn as_identity_attribute_storage_reader(&self) -> Arc { +impl IdentitiesRepository for BootstrapedIdentityStore { + fn as_attributes_reader(&self) -> Arc { Arc::new(self.clone()) } - fn as_identity_attribute_storage_writer(&self) -> Arc { + fn as_attributes_writer(&self) -> Arc { Arc::new(self.clone()) } } @@ -164,7 +180,7 @@ impl From> for PreTrustedIdentities } #[async_trait] -impl IdentityAttributeStorageReader for PreTrustedIdentities { +impl IdentityAttributesReader for PreTrustedIdentities { async fn get_attributes( &self, identity_id: &IdentityIdentifier, diff --git a/implementations/rust/ockam/ockam_api/src/cli_state.rs b/implementations/rust/ockam/ockam_api/src/cli_state.rs index d73b95dbb88..f2d8547658f 100644 --- a/implementations/rust/ockam/ockam_api/src/cli_state.rs +++ b/implementations/rust/ockam/ockam_api/src/cli_state.rs @@ -3,11 +3,12 @@ use crate::cloud::project::Project; use crate::nodes::models::transport::{CreateTransportJson, TransportMode, TransportType}; use nix::errno::Errno; -use ockam_core::compat::sync::Arc; -use ockam_identity::change_history::{IdentityChangeHistory, IdentityHistoryComparison}; -use ockam_identity::{ - Identity, IdentityIdentifier, IdentityVault, PublicIdentity, SecureChannelRegistry, +use ockam::identity::identity::{IdentityChangeHistory, IdentityHistoryComparison}; +use ockam::identity::{ + identities, Identities, IdentitiesRepository, IdentitiesStorage, IdentitiesVault, Identity, + IdentityIdentifier, }; +use ockam_core::compat::sync::Arc; use ockam_vault::{storage::FileStorage, Vault}; use rand::random; use serde::{Deserialize, Serialize}; @@ -18,8 +19,7 @@ use std::time::SystemTime; use sysinfo::{Pid, System, SystemExt}; use crate::lmdb::LmdbStorage; -use ockam_identity::authenticated_storage::AuthenticatedStorage; -use ockam_identity::credential::Credential; +use ockam::identity::credential::Credential; use thiserror::Error; type Result = std::result::Result; @@ -142,6 +142,72 @@ impl CliState { fn defaults_dir(dir: &Path) -> Result { Ok(dir.join("defaults")) } + + pub async fn create_vault_state(&self, vault_name: Option) -> Result { + let vault_state = if let Some(v) = vault_name { + self.vaults.get(v.as_str())? + } + // Or get the default + else if let Ok(v) = self.vaults.default() { + v + } else { + let n = hex::encode(random::<[u8; 4]>()); + let c = VaultConfig::default(); + self.vaults.create(&n, c).await? + }; + Ok(vault_state) + } + + pub async fn create_identity_state( + &self, + identity_name: Option, + vault: Vault, + ) -> Result { + // get the identity name specified in the argument, + // and don't try to recreate an identity state if it already exists + if let Some(name) = identity_name { + if let Ok(identity_state) = self.identities.get(name.as_str()) { + Ok(identity_state) + } else { + self.make_identity_state(vault, Some(name)).await + } + } + // or make a new one with a default name + else { + self.make_identity_state(vault, None).await + } + } + + async fn make_identity_state( + &self, + vault: Vault, + name: Option, + ) -> Result { + let identity = self + .get_identities(vault) + .await? + .identities_creation() + .create_identity() + .await?; + let identity_config = IdentityConfig::new(&identity).await; + let identity_name = name.unwrap_or_else(|| hex::encode(random::<[u8; 4]>())); + self.identities + .create(identity_name.as_str(), identity_config) + } + + pub async fn get_identities(&self, vault: Vault) -> Result> { + Ok(identities::builder() + .with_identities_vault(Arc::new(vault)) + .with_identities_repository(self.identities.identities_repository().await?) + .build()) + } + + pub async fn default_identities(&self) -> Result> { + Ok(identities::builder() + .with_identities_vault(self.vaults.default()?.identities_vault().await?) + .with_identities_repository(self.identities.identities_repository().await?) + .build()) + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -304,6 +370,13 @@ impl VaultState { self.data_path(&self.name) } + pub async fn identities_vault(&self) -> Result> { + let path = self.vault_file_path()?; + Ok(Arc::new(Vault::new(Some(Arc::new( + FileStorage::create(path).await?, + ))))) + } + pub fn delete(&self) -> Result<()> { std::fs::remove_file(&self.path)?; let data_path = self.data_path(&self.name)?; @@ -500,12 +573,14 @@ impl IdentitiesState { self.get(name) } - pub async fn authenticated_storage(&self) -> Result> { - let lmdb_path = self.authenticated_storage_path()?; - Ok(Arc::new(LmdbStorage::new(lmdb_path).await?)) + pub async fn identities_repository(&self) -> Result> { + let lmdb_path = self.identities_repository_path()?; + Ok(Arc::new(IdentitiesStorage::new(Arc::new( + LmdbStorage::new(lmdb_path).await?, + )))) } - pub fn authenticated_storage_path(&self) -> Result { + pub fn identities_repository_path(&self) -> Result { let lmdb_path = self.dir.join("data").join("authenticated_storage.lmdb"); Ok(lmdb_path) } @@ -552,30 +627,34 @@ impl IdentityState { self.persist() } - pub async fn get( - &self, - ctx: &ockam::Context, - vault: Arc, - ) -> Result { + pub async fn get(&self, vault: Arc) -> Result { let data = self.config.change_history.export()?; + Ok(self + .make_identities(vault) + .await? + .identities_creation() + .import_identity(&data) + .await?) + } + + pub async fn make_identities( + &self, + vault: Arc, + ) -> Result> { let cli_state_path = self .path .parent() .expect("Should have identities dir as parent") .parent() .expect("Should have CliState dir as parent"); - let storage = CliState::new(cli_state_path)? + let repository = CliState::new(cli_state_path)? .identities - .authenticated_storage() + .identities_repository() .await?; - Ok(Identity::import_ext( - ctx, - &data, - storage, - &SecureChannelRegistry::new(), - vault.clone(), - ) - .await?) + Ok(identities::builder() + .with_identities_vault(vault) + .with_identities_repository(repository) + .build()) } } @@ -625,8 +704,8 @@ pub struct IdentityConfig { impl IdentityConfig { pub async fn new(identity: &Identity) -> Self { - let identifier = identity.identifier().clone(); - let change_history = identity.change_history().await; + let identifier = identity.identifier(); + let change_history = identity.change_history(); Self { identifier, change_history, @@ -634,8 +713,8 @@ impl IdentityConfig { } } - pub fn public_identity(&self) -> PublicIdentity { - PublicIdentity::new(self.identifier.clone(), self.change_history.clone()) + pub fn identity(&self) -> Identity { + Identity::new(self.identifier.clone(), self.change_history.clone()) } } @@ -952,11 +1031,11 @@ impl NodeConfig { Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?) } - pub async fn identity(&self, ctx: &ockam::Context) -> Result { - let vault: Arc = Arc::new(self.vault().await?); + pub async fn default_identity(&self) -> Result { + let vault: Arc = Arc::new(self.vault().await?); let state_path = std::fs::canonicalize(&self.default_identity)?; let state = IdentityState::try_from(&state_path)?; - state.get(ctx, vault).await + state.get(vault).await } } @@ -1409,11 +1488,16 @@ mod tests { let identity_name = { let name = hex::encode(random::<[u8; 4]>()); let vault_state = sut.vaults.get(&vault_name).unwrap(); - let vault: Arc = Arc::new(vault_state.get().await.unwrap()); - let identity = - Identity::create_ext(ctx, sut.identities.authenticated_storage().await?, vault) - .await - .unwrap(); + let vault: Arc = Arc::new(vault_state.get().await.unwrap()); + let identities = identities::builder() + .with_identities_vault(vault) + .with_identities_repository(sut.identities.identities_repository().await?) + .build(); + let identity = identities + .identities_creation() + .create_identity() + .await + .unwrap(); let config = IdentityConfig::new(&identity).await; let state = sut.identities.create(&name, config).unwrap(); diff --git a/implementations/rust/ockam/ockam_api/src/cloud/enroll.rs b/implementations/rust/ockam/ockam_api/src/cloud/enroll.rs index 30ddb1683e3..394ee065e49 100644 --- a/implementations/rust/ockam/ockam_api/src/cloud/enroll.rs +++ b/implementations/rust/ockam/ockam_api/src/cloud/enroll.rs @@ -32,7 +32,7 @@ mod node { use crate::cloud::enroll::enrollment_token::{EnrollmentToken, RequestEnrollmentToken}; use crate::cloud::{CloudRequestWrapper, ORCHESTRATOR_RESTART_TIMEOUT}; use crate::nodes::NodeManagerWorker; - use ockam_identity::credential::Attributes; + use ockam::identity::credential::Attributes; const TARGET: &str = "ockam_api::cloud::enroll"; @@ -182,7 +182,7 @@ pub mod auth0 { } pub mod enrollment_token { - use ockam_identity::credential::Attributes; + use ockam::identity::credential::Attributes; use serde::Serialize; use super::*; diff --git a/implementations/rust/ockam/ockam_api/src/cloud/mod.rs b/implementations/rust/ockam/ockam_api/src/cloud/mod.rs index ef730dd7ef0..8a1ba661d36 100644 --- a/implementations/rust/ockam/ockam_api/src/cloud/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/cloud/mod.rs @@ -75,15 +75,14 @@ impl<'a> BareCloudRequestWrapper<'a> { mod node { use std::env; use std::str::FromStr; - use std::sync::Arc; use std::time::Duration; use minicbor::Encode; use rust_embed::EmbeddedFile; + use ockam::identity::{IdentityIdentifier, SecureChannelTrustOptions, TrustIdentifierPolicy}; use ockam_core::api::RequestBuilder; use ockam_core::{self, route, CowStr, Result}; - use ockam_identity::{IdentityIdentifier, SecureChannelTrustOptions, TrustIdentifierPolicy}; use ockam_multiaddr::MultiAddr; use ockam_node::api::request_with_timeout; use ockam_node::{Context, DEFAULT_TIMEOUT}; @@ -166,47 +165,27 @@ mod node { where T: Encode<()>, { - let identity = { - let node_manager = self.get().read().await; - match &ident { - Some(existing_identity_name) => { - let identity_state = node_manager - .cli_state - .identities - .get(existing_identity_name.as_ref())?; - match identity_state.get(ctx, node_manager.vault()?).await { - Ok(idt) => Arc::new(idt), - Err(_) => { - let vault_state = node_manager.cli_state.vaults.default()?; - Arc::new( - identity_state - .get(ctx, Arc::new(vault_state.get().await?)) - .await?, - ) - } - } - } - None => node_manager.identity.clone(), - } - }; - let sc = { - let node_manager = self.get().read().await; - let cloud_session = - crate::create_tcp_session(cloud_multiaddr, &node_manager.tcp_transport) - .await - .ok_or_else(|| ApiError::generic("Invalid Multiaddr"))?; - let trust_options = SecureChannelTrustOptions::new().with_trust_policy( - TrustIdentifierPolicy::new(node_manager.controller_identity_id()), - ); - let trust_options = if let Some((sessions, session_id)) = cloud_session.session { - trust_options.as_consumer(&sessions, &session_id) - } else { - trust_options - }; - identity - .create_secure_channel(cloud_session.route, trust_options) - .await? + let mut node_manager = self.get().write().await; + let identity_name = ident.map(|i| i.to_string()).clone(); + let secure_channels = node_manager + .get_secure_channels(None, identity_name.clone()) + .await?; + let identity = node_manager.get_identity(None, identity_name).await?; + let cloud_session = + crate::create_tcp_session(cloud_multiaddr, &node_manager.tcp_transport) + .await + .ok_or_else(|| ApiError::generic("Invalid Multiaddr"))?; + let trust_options = SecureChannelTrustOptions::new().with_trust_policy( + TrustIdentifierPolicy::new(node_manager.controller_identity_id()), + ); + let trust_options = if let Some((sessions, session_id)) = cloud_session.session { + trust_options.as_consumer(&sessions, &session_id) + } else { + trust_options }; + let sc = secure_channels + .create_secure_channel(ctx, &identity, cloud_session.route, trust_options) + .await?; let route = route![&sc.to_string(), api_service]; let res = request_with_timeout(ctx, label, schema, route, req, timeout).await; ctx.stop_worker(sc).await?; diff --git a/implementations/rust/ockam/ockam_api/src/cloud/project.rs b/implementations/rust/ockam/ockam_api/src/cloud/project.rs index 349cfbfcdd8..04047e32286 100644 --- a/implementations/rust/ockam/ockam_api/src/cloud/project.rs +++ b/implementations/rust/ockam/ockam_api/src/cloud/project.rs @@ -4,11 +4,11 @@ use minicbor::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::cloud::addon::ConfluentConfigResponse; +use ockam::identity::IdentityIdentifier; use ockam_core::CowStr; use ockam_core::Result; #[cfg(feature = "tag")] use ockam_core::TypeTag; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; use ockam_node::tokio; diff --git a/implementations/rust/ockam/ockam_api/src/config/cli.rs b/implementations/rust/ockam/ockam_api/src/config/cli.rs index 79a1637c5e3..ff58a89572d 100644 --- a/implementations/rust/ockam/ockam_api/src/config/cli.rs +++ b/implementations/rust/ockam/ockam_api/src/config/cli.rs @@ -2,9 +2,9 @@ use crate::config::{lookup::ConfigLookup, ConfigValues}; use crate::{cli_state, HexByteVec}; +use ockam::identity::{Identities, Identity, IdentityIdentifier}; use ockam_core::compat::sync::Arc; use ockam_core::Result; -use ockam_identity::{IdentityIdentifier, IdentityVault, PublicIdentity}; use ockam_multiaddr::MultiAddr; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -73,13 +73,15 @@ impl AuthoritiesConfig { self.authorities.iter() } - pub async fn to_public_identities( - &self, - vault: Arc, - ) -> Result> { + pub async fn to_identities(&self, identities: Arc) -> Result> { let mut v = Vec::new(); for a in self.authorities.values() { - v.push(PublicIdentity::import(a.identity.as_slice(), vault.clone()).await?) + v.push( + identities + .identities_creation() + .import_identity(a.identity.as_slice()) + .await?, + ) } Ok(v) } diff --git a/implementations/rust/ockam/ockam_api/src/config/lookup.rs b/implementations/rust/ockam/ockam_api/src/config/lookup.rs index 45f089ed654..f44705c86e6 100644 --- a/implementations/rust/ockam/ockam_api/src/config/lookup.rs +++ b/implementations/rust/ockam/ockam_api/src/config/lookup.rs @@ -2,11 +2,10 @@ use crate::cloud::project::{OktaAuth0, Project}; use crate::error::ApiError; use anyhow::Context as _; use bytes::Bytes; +use ockam::identity::{identities, IdentityIdentifier}; use ockam_core::compat::collections::VecDeque; use ockam_core::{CowStr, Result}; -use ockam_identity::{IdentityIdentifier, PublicIdentity}; use ockam_multiaddr::MultiAddr; -use ockam_vault::Vault; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, @@ -273,8 +272,11 @@ impl ProjectAuthority { .ok_or_else(|| ApiError::generic("Identity is not set"))?; let a = hex::decode(&**a).map_err(|_| ApiError::generic("Invalid project authority"))?; - let p = PublicIdentity::import(&a, Vault::create()).await?; - Ok(Some(ProjectAuthority::new(p.identifier().clone(), rte, a))) + let p = identities() + .identities_creation() + .import_identity(&a) + .await?; + Ok(Some(ProjectAuthority::new(p.identifier(), rte, a))) } else { Ok(None) } diff --git a/implementations/rust/ockam/ockam_api/src/identity/identity_service.rs b/implementations/rust/ockam/ockam_api/src/identity/identity_service.rs index bbb2827e1cd..28f7a36fe0d 100644 --- a/implementations/rust/ockam/ockam_api/src/identity/identity_service.rs +++ b/implementations/rust/ockam/ockam_api/src/identity/identity_service.rs @@ -1,41 +1,23 @@ -use crate::cli_state::CliState; use crate::identity::models::*; +use crate::nodes::service::NodeIdentities; use core::convert::Infallible; use minicbor::encode::Write; use minicbor::{Decoder, Encode}; +use ockam::identity::IdentityHistoryComparison; use ockam_core::api::{Error, Id, Method, Request, Response, Status}; -use ockam_core::compat::sync::Arc; use ockam_core::vault::Signature; -use ockam_core::{Address, DenyAll, Result, Routed, Worker}; -use ockam_identity::change_history::IdentityHistoryComparison; -use ockam_identity::{Identity, IdentityVault, PublicIdentity}; +use ockam_core::{Result, Routed, Worker}; use ockam_node::Context; use tracing::trace; /// Vault Service Worker pub struct IdentityService { - ctx: Context, - vault: Arc, - cli_state: CliState, + node_identities: NodeIdentities, } impl IdentityService { - pub async fn new( - ctx: &Context, - vault: Arc, - cli_state: CliState, - ) -> Result { - Ok(Self { - ctx: ctx - .new_detached( - Address::random_tagged("IdentityService.root"), - DenyAll, - DenyAll, - ) - .await?, - vault, - cli_state, - }) + pub async fn new(node_identities: NodeIdentities) -> Result { + Ok(Self { node_identities }) } } @@ -116,22 +98,33 @@ impl IdentityService { match method { Get => match req.path_segments::<2>().as_slice() { [identity_name] => { - let identity = self.cli_state.identities.get(identity_name)?; - let body = CreateResponse::new( - identity.config.change_history.export()?, - String::from(identity.config.identifier), - ); - Self::ok_response(req, Some(body), enc) + match self + .node_identities + .get_identity(identity_name.to_string(), None) + .await? + { + Some(identity) => { + let body = CreateResponse::new( + identity.export()?, + identity.identifier().to_string(), + ); + Self::ok_response(req, Some(body), enc) + } + None => Self::response_for_bad_request(req, "unknown identity", enc), + } } _ => Self::response_for_bad_request(req, "unknown path", enc), }, Post => match req.path_segments::<2>().as_slice() { [""] => { - let identity = Identity::create(&self.ctx, self.vault.clone()).await?; - let identifier = identity.identifier(); - + let identity = self + .node_identities + .get_default_identities_creation() + .await? + .create_identity() + .await?; let body = - CreateResponse::new(identity.export().await?, String::from(identifier)); + CreateResponse::new(identity.export()?, identity.identifier().to_string()); Self::ok_response(req, Some(body), enc) } @@ -141,8 +134,11 @@ impl IdentityService { } let args = dec.decode::()?; - let identity = - Identity::import(&self.ctx, args.identity(), self.vault.clone()).await?; + let identities_creation = self + .node_identities + .get_default_identities_creation() + .await?; + let identity = identities_creation.import_identity(args.identity()).await?; let body = ValidateIdentityChangeHistoryResponse::new(String::from( identity.identifier(), @@ -156,18 +152,18 @@ impl IdentityService { } let args = dec.decode::()?; - let identity = match args.vault_name() { - None => { - Identity::import(&self.ctx, args.identity(), self.vault.clone()).await? - } - - Some(vault_name) => { - let vault = self.cli_state.vaults.get(&vault_name)?.get().await?; - Identity::import(&self.ctx, args.identity(), Arc::new(vault)).await? - } - }; - - let signature = identity.create_signature(args.data(), None).await?; + let identities_creation = self + .node_identities + .get_identities_creation(args.vault_name()) + .await?; + let identity = identities_creation.import_identity(args.identity()).await?; + let identities_keys = self + .node_identities + .get_identities_keys(args.vault_name()) + .await?; + let signature = identities_keys + .create_signature(&identity, args.data(), None) + .await?; let body = CreateSignatureResponse::new(signature.as_ref()); @@ -179,15 +175,22 @@ impl IdentityService { } let args = dec.decode::()?; - let peer_identity = - PublicIdentity::import(args.signer_identity(), self.vault.clone()).await?; + let identities_creation = self + .node_identities + .get_default_identities_creation() + .await?; + let peer_identity = identities_creation + .import_identity(args.signer_identity()) + .await?; - let verified = peer_identity + let identities_keys = + self.node_identities.get_default_identities_keys().await?; + let verified = identities_keys .verify_signature( + &peer_identity, &Signature::new(args.signature().to_vec()), args.data(), None, - self.vault.clone(), ) .await?; @@ -202,16 +205,21 @@ impl IdentityService { let args = dec.decode::()?; - let current_identity = - PublicIdentity::import(args.current_identity(), self.vault.clone()).await?; + let identities_creation = self + .node_identities + .get_default_identities_creation() + .await?; + + let current_identity = identities_creation + .import_identity(args.current_identity()) + .await?; let body = if args.known_identity().is_empty() { IdentityHistoryComparison::Newer } else { - let known_identity = - PublicIdentity::import(args.known_identity(), self.vault.clone()) - .await?; - + let known_identity = identities_creation + .import_identity(args.known_identity()) + .await?; current_identity.compare(&known_identity) }; diff --git a/implementations/rust/ockam/ockam_api/src/kafka/integration_test.rs b/implementations/rust/ockam/ockam_api/src/kafka/integration_test.rs index a0682ea47bd..2589cb93e7b 100644 --- a/implementations/rust/ockam/ockam_api/src/kafka/integration_test.rs +++ b/implementations/rust/ockam/ockam_api/src/kafka/integration_test.rs @@ -19,11 +19,11 @@ mod test { Compression, RecordBatchDecoder, RecordBatchEncoder, RecordEncodeOptions, TimestampType, }; use ockam::compat::tokio::io::DuplexStream; + use ockam::identity::TrustEveryonePolicy; use ockam::Context; use ockam_core::async_trait; use ockam_core::compat::sync::Arc; use ockam_core::{route, Address, AllowAll, Route}; - use ockam_identity::TrustEveryonePolicy; use ockam_node::compat::tokio; use ockam_transport_tcp::{TcpInletTrustOptions, TcpOutletTrustOptions}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -75,6 +75,7 @@ mod test { kind: KafkaServiceKind, ) -> ockam::Result { let secure_channel_controller = KafkaSecureChannelControllerImpl::new_extended( + handle.secure_channels.clone(), handle.identity.clone(), route![], HopForwarderCreator {}, @@ -87,8 +88,10 @@ mod test { .create_consumer_listener(context) .await?; handle - .identity + .secure_channels .create_secure_channel_listener( + context, + &handle.identity, KAFKA_SECURE_CHANNEL_LISTENER_ADDRESS, TrustEveryonePolicy, ) diff --git a/implementations/rust/ockam/ockam_api/src/kafka/portal_worker.rs b/implementations/rust/ockam/ockam_api/src/kafka/portal_worker.rs index 02a439f526a..8fa8c2e0910 100644 --- a/implementations/rust/ockam/ockam_api/src/kafka/portal_worker.rs +++ b/implementations/rust/ockam/ockam_api/src/kafka/portal_worker.rs @@ -295,12 +295,11 @@ mod test { use kafka_protocol::protocol::Decodable; use kafka_protocol::protocol::Encodable as KafkaEncodable; use kafka_protocol::protocol::StrBytes; + use ockam::identity::secure_channels; use ockam_core::compat::sync::{Arc, Mutex}; use ockam_core::{route, Address, AllowAll, Routed, Worker}; - use ockam_identity::Identity; - use ockam_node::{Context, MessageReceiveOptions}; + use ockam_node::Context; use ockam_transport_tcp::{PortalMessage, MAX_PAYLOAD_SIZE}; - use ockam_vault::Vault; use std::collections::BTreeMap; use std::time::Duration; @@ -308,6 +307,7 @@ mod test { use crate::kafka::portal_worker::{KafkaPortalWorker, MAX_KAFKA_MESSAGE_SIZE}; use crate::kafka::secure_channel_map::KafkaSecureChannelControllerImpl; use crate::port_range::PortRange; + use ockam::MessageReceiveOptions; const TEST_KAFKA_API_VERSION: i16 = 13; @@ -580,10 +580,15 @@ mod test { PortRange::new(20_000, 40_000).unwrap(), ); - let vault = Vault::create(); - let identity = Identity::create(context, vault).await.unwrap(); + let secure_channels = secure_channels(); + let identity = secure_channels + .identities() + .identities_creation() + .create_identity() + .await + .unwrap(); let secure_channel_controller = - KafkaSecureChannelControllerImpl::new(Arc::new(identity), route![]).into_trait(); + KafkaSecureChannelControllerImpl::new(secure_channels, identity, route![]).into_trait(); KafkaPortalWorker::start_kafka_portal( context, @@ -630,11 +635,16 @@ mod test { ) -> ockam::Result<()> { crate::test::start_manager_for_tests(context).await?; - let vault = Vault::create(); - let identity = Identity::create(context, vault).await?; + let secure_channels = secure_channels(); + let identity = secure_channels + .identities() + .identities_creation() + .create_identity() + .await?; let secure_channel_controller = - KafkaSecureChannelControllerImpl::new(Arc::new(identity), route![]).into_trait(); + KafkaSecureChannelControllerImpl::new(secure_channels.clone(), identity, route![]) + .into_trait(); let inlet_map = KafkaInletMap::new( route![], diff --git a/implementations/rust/ockam/ockam_api/src/kafka/secure_channel_map.rs b/implementations/rust/ockam/ockam_api/src/kafka/secure_channel_map.rs index a8504bdb88c..3c23bb39b65 100644 --- a/implementations/rust/ockam/ockam_api/src/kafka/secure_channel_map.rs +++ b/implementations/rust/ockam/ockam_api/src/kafka/secure_channel_map.rs @@ -2,18 +2,19 @@ use crate::kafka::{ KAFKA_SECURE_CHANNEL_CONTROLLER_ADDRESS, KAFKA_SECURE_CHANNEL_LISTENER_ADDRESS, ORCHESTRATOR_KAFKA_CONSUMERS, }; +use ockam::identity::Identity; +use ockam::identity::{ + DecryptionRequest, DecryptionResponse, EncryptionRequest, EncryptionResponse, +}; +use ockam::identity::{ + SecureChannelRegistryEntry, SecureChannelTrustOptions, SecureChannels, TrustEveryonePolicy, +}; use ockam::remote::{RemoteForwarder, RemoteForwarderTrustOptions}; use ockam_core::compat::collections::{HashMap, HashSet}; use ockam_core::compat::sync::Arc; use ockam_core::errcode::{Kind, Origin}; use ockam_core::Message; use ockam_core::{async_trait, route, Address, AllowAll, Error, Result, Route, Routed, Worker}; -use ockam_identity::api::{ - DecryptionRequest, DecryptionResponse, EncryptionRequest, EncryptionResponse, -}; -use ockam_identity::{ - Identity, SecureChannelRegistryEntry, SecureChannelTrustOptions, TrustEveryonePolicy, -}; use ockam_node::compat::tokio::sync::Mutex; use ockam_node::Context; use serde::{Deserialize, Serialize}; @@ -122,7 +123,8 @@ struct InnerSecureChannelControllerImpl { //of the secure channel id_encryptor_map: HashMap, topic_encryptor_map: HashMap, - identity: Arc, + secure_channels: Arc, + identity: Identity, project_route: Route, topic_forwarder_set: HashSet, forwarder_creator: F, @@ -130,10 +132,12 @@ struct InnerSecureChannelControllerImpl { impl KafkaSecureChannelControllerImpl { pub(crate) fn new( - identity: Arc, + secure_channels: Arc, + identity: Identity, project_route: Route, ) -> KafkaSecureChannelControllerImpl { Self::new_extended( + secure_channels, identity, project_route.clone(), RemoteForwarderCreator { @@ -146,7 +150,8 @@ impl KafkaSecureChannelControllerImpl { impl KafkaSecureChannelControllerImpl { /// to manually specify `ForwarderCreator`, for testing purposes pub(crate) fn new_extended( - identity: Arc, + secure_channels: Arc, + identity: Identity, project_route: Route, forwarder_creator: F, ) -> KafkaSecureChannelControllerImpl { @@ -155,6 +160,7 @@ impl KafkaSecureChannelControllerImpl { id_encryptor_map: Default::default(), topic_encryptor_map: Default::default(), topic_forwarder_set: Default::default(), + secure_channels, identity, forwarder_creator, project_route, @@ -242,8 +248,10 @@ impl KafkaSecureChannelControllerImpl { let trust_options = SecureChannelTrustOptions::new().with_trust_policy(TrustEveryonePolicy); let encryptor_address = inner - .identity + .secure_channels .create_secure_channel( + context, + &inner.identity, route![ inner.project_route.clone(), topic_partition_address.clone(), @@ -284,7 +292,7 @@ impl KafkaSecureChannelControllerImpl { }; inner - .identity + .secure_channels .secure_channel_registry() .get_channel_by_encryptor_address(&encryptor_address) .map(|entry| (random_unique_id, entry)) @@ -299,7 +307,7 @@ impl KafkaSecureChannelControllerImpl { let inner = self.inner.lock().await; if let Some(encryptor_address) = inner.id_encryptor_map.get(&secure_channel_id) { inner - .identity + .secure_channels .secure_channel_registry() .get_channel_list() .iter() diff --git a/implementations/rust/ockam/ockam_api/src/lmdb.rs b/implementations/rust/ockam/ockam_api/src/lmdb.rs index 5cc954f1b02..a7ba5982999 100644 --- a/implementations/rust/ockam/ockam_api/src/lmdb.rs +++ b/implementations/rust/ockam/ockam_api/src/lmdb.rs @@ -1,11 +1,11 @@ use core::str; use lmdb::{Cursor, Database, Environment, Transaction}; use minicbor::{Decode, Encode}; +use ockam::identity::identities::storage::Storage; use ockam_abac::{Action, Expr, PolicyStorage, Resource}; use ockam_core::async_trait; use ockam_core::errcode::{Kind, Origin}; use ockam_core::{Error, Result}; -use ockam_identity::authenticated_storage::AuthenticatedStorage; use ockam_node::tokio::task::{self, JoinError}; use std::borrow::Cow; use std::fmt; @@ -15,7 +15,7 @@ use tokio_retry::strategy::{jitter, FixedInterval}; use tokio_retry::Retry; use tracing as log; -/// Lmdb AuthenticatedStorage implementation +/// Lmdb AttributesStorage implementation #[derive(Clone)] pub struct LmdbStorage { env: Arc, @@ -87,7 +87,7 @@ impl LmdbStorage { } #[async_trait] -impl AuthenticatedStorage for LmdbStorage { +impl Storage for LmdbStorage { async fn get(&self, id: &str, key: &str) -> Result>> { let d = self.clone(); let k = format!("{id}:{key}"); diff --git a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/authority.rs b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/authority.rs index af2e288b9e2..dbe6ae12319 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/authority.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/authority.rs @@ -5,19 +5,17 @@ use crate::lmdb::LmdbStorage; use crate::nodes::authority_node::authority::EnrollerCheck::{AnyMember, EnrollerOnly}; use crate::nodes::authority_node::Configuration; use crate::{actions, DefaultAddress}; +use ockam::identity::{ + secure_channels, Identities, IdentitiesRepository, IdentitiesStorage, IdentitiesVault, + Identity, IdentityAttributesWriter, SecureChannelListenerTrustOptions, SecureChannels, + TrustEveryonePolicy, +}; use ockam_abac::expr::{and, eq, ident, str}; use ockam_abac::{AbacAccessControl, Env}; use ockam_core::compat::sync::Arc; use ockam_core::errcode::{Kind, Origin}; use ockam_core::sessions::{SessionId, SessionPolicy, Sessions}; -use ockam_core::{Address, AllowAll, AsyncTryClone, Error, Message, Result, Worker}; -use ockam_identity::authenticated_storage::{ - AuthenticatedAttributeStorage, AuthenticatedStorage, IdentityAttributeStorage, -}; -use ockam_identity::{ - Identity, IdentityVault, PublicIdentity, SecureChannelListenerTrustOptions, - SecureChannelRegistry, TrustEveryonePolicy, -}; +use ockam_core::{Address, AllowAll, Error, Message, Result, Worker}; use ockam_node::{Context, WorkerBuilder}; use ockam_transport_tcp::{TcpListenerTrustOptions, TcpTransport}; use ockam_vault::storage::FileStorage; @@ -34,20 +32,7 @@ use tracing::info; // - an enrollment token acceptor pub struct Authority { identity: Identity, - attributes_storage: Arc, -} - -impl Authority { - /// Create a new Authority with a given identity - /// The list of trusted identities is used to pre-populate an attributes storage - /// In practice it contains the list of identities with the ockam-role attribute set as 'enroller' - pub(crate) fn new(identity: Identity, configuration: &Configuration) -> Self { - let attributes_storage = Self::make_attributes_storage(&identity, configuration); - Self { - identity, - attributes_storage, - } - } + secure_channels: Arc, } /// Public functions to: @@ -55,27 +40,33 @@ impl Authority { /// - start services impl Authority { /// Return the public identity for this authority - pub async fn public_identity(&self) -> Result { - self.identity.to_public().await + pub fn identity(&self) -> Identity { + self.identity.clone() } /// Create an identity for an authority from the configured public identity and configured vault - pub async fn create(ctx: &Context, configuration: &Configuration) -> Result { + /// The list of trusted identities in the configuration is used to pre-populate an attributes storage + /// In practice it contains the list of identities with the ockam-role attribute set as 'enroller' + pub async fn create(configuration: &Configuration) -> Result { info!("configuration {:?}", configuration); - let vault = Self::create_identity_vault(configuration).await?; - let storage = Self::create_authenticated_storage(configuration).await?; - - let identity = Identity::import_ext( - ctx, - configuration.identity.export()?.as_slice(), - storage.clone(), - &SecureChannelRegistry::new(), - vault, - ) - .await?; + let vault = Self::create_secure_channels_vault(configuration).await?; + let repository = Self::create_identities_repository(configuration).await?; + let secure_channels = secure_channels::builder() + .with_identities_vault(vault) + .with_identities_repository(repository) + .build(); + + let identity = secure_channels + .identities() + .identities_creation() + .import_identity(configuration.identity.export()?.as_slice()) + .await?; info!("retrieved the authority identity {}", identity.identifier()); - Ok(Authority::new(identity, configuration)) + Ok(Authority { + identity, + secure_channels, + }) } /// Start the secure channel listener service, using TCP as a transport @@ -102,8 +93,13 @@ impl Authority { .as_spawner(sessions, &secure_channel_listener_session_id); let listener_name = configuration.secure_channel_listener_name(); - self.identity - .create_secure_channel_listener(listener_name.clone(), trust_options) + self.secure_channels + .create_secure_channel_listener( + ctx, + &self.identity, + listener_name.clone(), + trust_options, + ) .await?; info!("started a secure channel listener with name '{listener_name}'"); @@ -137,7 +133,7 @@ impl Authority { let direct = crate::authenticator::direct::DirectAuthenticator::new( configuration.clone().project_identifier(), - self.attributes_storage().clone(), + self.attributes_writer(), ) .await?; @@ -169,7 +165,7 @@ impl Authority { let (issuer, acceptor) = EnrollmentTokenAuthenticator::new_worker_pair( configuration.project_identifier(), - self.attributes_storage(), + self.attributes_writer(), ); // start an enrollment token issuer with an abac policy checking that @@ -226,11 +222,9 @@ impl Authority { ) -> Result<()> { // create and start a credential issuer worker let issuer = CredentialIssuer::new( + self.identities(), + self.identity.clone(), configuration.project_identifier(), - self.attributes_storage() - .clone() - .as_identity_attribute_storage_reader(), - Arc::new(self.identity.async_try_clone().await?), ) .await?; @@ -258,9 +252,8 @@ impl Authority { ) -> Result<()> { if let Some(okta) = configuration.clone().okta { let okta_worker = crate::okta::Server::new( + self.attributes_writer(), configuration.project_identifier(), - self.attributes_storage() - .as_identity_attribute_storage_writer(), okta.tenant_base_url(), okta.certificate(), okta.attributes().as_slice(), @@ -292,15 +285,25 @@ impl Authority { /// Private Authority functions impl Authority { - /// Return the attribute storage used by the authority - fn attributes_storage(&self) -> Arc { - self.attributes_storage.clone() + /// Return the identities storage used by the authority + fn identities(&self) -> Arc { + self.secure_channels.identities() + } + + /// Return the identities repository used by the authority + fn identities_repository(&self) -> Arc { + self.identities().repository().clone() + } + + /// Return the identities repository used by the authority + fn attributes_writer(&self) -> Arc { + self.identities_repository().as_attributes_writer().clone() } /// Create an identity vault backed by a FileStorage - async fn create_identity_vault( + async fn create_secure_channels_vault( configuration: &Configuration, - ) -> Result> { + ) -> Result> { let vault_path = &configuration.vault_path; Self::create_ockam_directory_if_necessary(vault_path)?; let mut file_storage = FileStorage::new(vault_path.clone()); @@ -310,13 +313,14 @@ impl Authority { } /// Create an authenticated storage backed by a Lmdb database - async fn create_authenticated_storage( + async fn create_identities_repository( configuration: &Configuration, - ) -> Result> { + ) -> Result> { let storage_path = &configuration.storage_path; Self::create_ockam_directory_if_necessary(storage_path)?; let storage = Arc::new(LmdbStorage::new(&storage_path).await?); - Ok(storage) + let repository = Arc::new(IdentitiesStorage::new(storage)); + Ok(Self::bootstrap_repository(repository, configuration)) } /// Create a directory to save storage files if they haven't been created before @@ -328,19 +332,17 @@ impl Authority { Ok(()) } - /// Make an identity attributes storage pre-populated with the attributes of some trusted + /// Make an identities repository pre-populated with the attributes of some trusted /// identities. The values either come from the command line or are read directly from a file - /// every time the IdentityAttributeStorage tries to retrieve some attributes - fn make_attributes_storage( - authority: &Identity, + /// every time we try to retrieve some attributes + fn bootstrap_repository( + repository: Arc, configuration: &Configuration, - ) -> Arc { + ) -> Arc { let trusted_identities = &configuration.trusted_identities; Arc::new(BootstrapedIdentityStore::new( Arc::new(trusted_identities.clone()), - Arc::new(AuthenticatedAttributeStorage::new( - authority.authenticated_storage(), - )), + repository.clone(), )) } @@ -394,7 +396,11 @@ impl Authority { "resource.project_id", str(configuration.clone().project_identifier), ); - let abac = Arc::new(AbacAccessControl::new(self.attributes_storage(), rule, env)); + let abac = Arc::new(AbacAccessControl::new( + self.identities_repository(), + rule, + env, + )); abac } } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/configuration.rs b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/configuration.rs index d8e0c90b4d4..678ed4f379d 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/configuration.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/configuration.rs @@ -1,11 +1,10 @@ use crate::bootstrapped_identities_store::PreTrustedIdentities; use crate::DefaultAddress; +use ockam::identity::credential::Timestamp; +use ockam::identity::{AttributesEntry, Identity, IdentityIdentifier}; use ockam_core::compat::collections::HashMap; use ockam_core::compat::fmt; use ockam_core::compat::fmt::{Display, Formatter}; -use ockam_identity::authenticated_storage::AttributesEntry; -use ockam_identity::credential::Timestamp; -use ockam_identity::{IdentityIdentifier, PublicIdentity}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::path::PathBuf; @@ -14,7 +13,7 @@ use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Configuration { /// Authority identity or identity associated with the newly created node - pub identity: PublicIdentity, + pub identity: Identity, /// path where the storage for identity attributes should be persisted pub storage_path: PathBuf, diff --git a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/node.rs b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/node.rs index f616dd8e77a..501ee7455a4 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/authority_node/node.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/authority_node/node.rs @@ -10,7 +10,7 @@ pub async fn start_node(ctx: &Context, configuration: &Configuration) -> Result< // or retrieve it from disk if the node has already been started before // The trusted identities in the configuration are used to pre-populate an attribute storage // containing those identities and their attributes - let authority = Authority::create(ctx, configuration).await?; + let authority = Authority::create(configuration).await?; // start a secure channel listener (this also starts a TCP transport) let sessions = Sessions::default(); @@ -41,7 +41,7 @@ pub async fn start_node(ctx: &Context, configuration: &Configuration) -> Result< info!( "Authority node started with identity\n{}", - authority.public_identity().await? + authority.identity() ); Ok(()) } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/connection.rs b/implementations/rust/ockam/ockam_api/src/nodes/connection.rs index 2169205126a..dba61805608 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/connection.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/connection.rs @@ -1,5 +1,5 @@ +use ockam::identity::IdentityIdentifier; use ockam_core::CowStr; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; use ockam_node::Context; use std::time::Duration; diff --git a/implementations/rust/ockam/ockam_api/src/nodes/models/forwarder.rs b/implementations/rust/ockam/ockam_api/src/nodes/models/forwarder.rs index b0701fd0055..b6dbebac2f3 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/models/forwarder.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/models/forwarder.rs @@ -1,8 +1,8 @@ use minicbor::{Decode, Encode}; +use ockam::identity::IdentityIdentifier; use ockam::remote::RemoteForwarderInfo; use ockam_core::CowStr; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; #[cfg(feature = "tag")] diff --git a/implementations/rust/ockam/ockam_api/src/nodes/models/portal.rs b/implementations/rust/ockam/ockam_api/src/nodes/models/portal.rs index 2ff81d7afff..e15fbc52cad 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/models/portal.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/models/portal.rs @@ -5,10 +5,10 @@ use std::net::SocketAddr; use minicbor::{Decode, Encode}; use ockam_core::compat::borrow::Cow; +use ockam::identity::IdentityIdentifier; use ockam_core::CowStr; #[cfg(feature = "tag")] use ockam_core::TypeTag; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; /// Request body to create an inlet diff --git a/implementations/rust/ockam/ockam_api/src/nodes/models/secure_channel.rs b/implementations/rust/ockam/ockam_api/src/nodes/models/secure_channel.rs index 8ed7cc39778..e7bfd526452 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/models/secure_channel.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/models/secure_channel.rs @@ -3,11 +3,11 @@ use std::time::Duration; use minicbor::{Decode, Encode}; use crate::nodes::registry::SecureChannelInfo; +use ockam::identity::IdentityIdentifier; use ockam_core::compat::borrow::Cow; #[cfg(feature = "tag")] use ockam_core::TypeTag; use ockam_core::{route, Address, CowStr, Result}; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; use serde::Serialize; diff --git a/implementations/rust/ockam/ockam_api/src/nodes/registry.rs b/implementations/rust/ockam/ockam_api/src/nodes/registry.rs index eb777a5c311..352ee3547b9 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/registry.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/registry.rs @@ -1,7 +1,7 @@ use crate::nodes::service::Alias; +use ockam::identity::IdentityIdentifier; use ockam_core::compat::collections::BTreeMap; use ockam_core::{Address, Route}; -use ockam_identity::IdentityIdentifier; #[derive(Default)] pub(crate) struct SecureChannelRegistry { diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service.rs b/implementations/rust/ockam/ockam_api/src/nodes/service.rs index b430008640e..222a91b61bc 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service.rs @@ -2,7 +2,12 @@ use minicbor::Decoder; -use ockam::identity::{Identity, IdentityIdentifier, PublicIdentity}; +use ockam::identity::credential::Credential; +use ockam::identity::{ + secure_channels, Credentials, CredentialsServer, CredentialsServerModule, Identities, + IdentitiesRepository, IdentitiesVault, IdentityAttributesReader, IdentityAttributesWriter, +}; +use ockam::identity::{Identity, IdentityIdentifier, SecureChannels}; use ockam::{Address, Context, ForwardingService, Result, Routed, TcpTransport, Worker}; use ockam_abac::PolicyStorage; use ockam_core::api::{Error, Method, Request, Response, ResponseBuilder, Status}; @@ -13,11 +18,6 @@ use ockam_core::compat::{ }; use ockam_core::errcode::{Kind, Origin}; use ockam_core::{route, AllowAll, AsyncTryClone}; -use ockam_identity::authenticated_storage::{ - AuthenticatedAttributeStorage, AuthenticatedStorage, IdentityAttributeStorage, -}; -use ockam_identity::credential::Credential; -use ockam_identity::IdentityVault; use ockam_multiaddr::proto::{Project, Secure}; use ockam_multiaddr::{MultiAddr, Protocol}; use ockam_node::compat::asynchronous::RwLock; @@ -51,12 +51,15 @@ pub mod message; mod credentials; mod forwarder; +mod node_identities; +mod node_services; mod policy; mod portals; mod secure_channel; -mod services; mod transport; +pub use node_identities::*; + const TARGET: &str = "ockam_api::nodemanager::service"; pub(crate) type Alias = String; @@ -84,7 +87,7 @@ impl Authorities { Self(authorities) } - pub fn public_identities(&self) -> Vec { + pub fn public_identities(&self) -> Vec { self.0.iter().map(|x| x.identity.clone()).collect() } } @@ -97,7 +100,7 @@ impl AsRef<[AuthorityInfo]> for Authorities { #[derive(Clone)] pub(crate) struct AuthorityInfo { - identity: PublicIdentity, + identity: Identity, addr: MultiAddr, } @@ -112,8 +115,9 @@ pub struct NodeManager { pub(crate) controller_identity_id: IdentityIdentifier, skip_defaults: bool, enable_credential_checks: bool, - vault: Arc, - pub(crate) identity: Arc, + identity: Identity, + credential: Option, + pub(crate) secure_channels: Arc, project_id: Option, projects: Arc>, authorities: Option, @@ -121,7 +125,52 @@ pub struct NodeManager { sessions: Arc>, medic: JoinHandle>, policies: Arc, - attributes_storage: Arc, +} + +impl NodeManager { + pub(super) fn identity(&self) -> Identity { + self.identity.clone() + } + + pub(super) fn identities(&self) -> Arc { + self.secure_channels.identities() + } + + pub(super) fn identities_vault(&self) -> Arc { + self.identities().vault() + } + + pub(super) fn identities_repository(&self) -> Arc { + self.identities().repository().clone() + } + + pub(super) fn attributes_writer(&self) -> Arc { + self.identities_repository().as_attributes_writer() + } + + pub(super) fn attributes_reader(&self) -> Arc { + self.identities_repository().as_attributes_reader() + } + + pub(super) fn credentials(&self) -> Arc { + self.identities().credentials() + } + + pub(super) fn credentials_service(&self) -> Arc { + Arc::new(CredentialsServerModule::new(self.credentials())) + } + + pub(super) fn secure_channels_vault(&self) -> Arc { + self.secure_channels.vault().clone() + } + + pub(super) fn credential(&self) -> Option { + self.credential.clone() + } + + pub(super) fn set_credential(&mut self, credential: Credential) { + self.credential = Some(credential) + } } pub struct NodeManagerWorker { @@ -146,10 +195,6 @@ pub struct IdentityOverride { } impl NodeManager { - pub(crate) fn vault(&self) -> Result> { - Ok(self.vault.clone()) - } - pub(crate) fn authorities(&self) -> Result<&Authorities> { self.authorities .as_ref() @@ -252,35 +297,28 @@ impl NodeManager { let cli_state = general_options.cli_state; let node_state = cli_state.nodes.get(&general_options.node_name)?; - let authenticated_storage: Arc = - cli_state.identities.authenticated_storage().await?; + let repository: Arc = + cli_state.identities.identities_repository().await?; //TODO: fix this. Either don't require it to be a bootstrappedidentitystore (and use the //trait instead), or pass it from the general_options always. - let attributes_storage: Arc = + let vault: Arc = Arc::new(node_state.config.vault().await?); + let identities_repository: Arc = Arc::new(match general_options.pre_trusted_identities { None => BootstrapedIdentityStore::new( Arc::new(PreTrustedIdentities::new_from_string("{}")?), - Arc::new(AuthenticatedAttributeStorage::new( - authenticated_storage.clone(), - )), - ), - Some(f) => BootstrapedIdentityStore::new( - Arc::new(f), - Arc::new(AuthenticatedAttributeStorage::new( - authenticated_storage.clone(), - )), + repository.clone(), ), + Some(f) => BootstrapedIdentityStore::new(Arc::new(f), repository.clone()), }); - let policies: Arc = Arc::new(node_state.policies_storage().await?); - - let vault: Arc = Arc::new(node_state.config.vault().await?); - let identity = Arc::new(node_state.config.identity(ctx).await?); - if let Some(cred) = projects_options.credential { - identity.set_credential(cred.to_owned()).await; - } + let secure_channels = secure_channels::builder() + .with_identities_vault(vault) + .with_identities_repository(identities_repository) + .build(); + let policies: Arc = Arc::new(node_state.policies_storage().await?); + let identity = node_state.config.default_identity().await?; let medic = Medic::new(); let sessions = medic.sessions(); @@ -293,8 +331,9 @@ impl NodeManager { skip_defaults: general_options.skip_defaults, enable_credential_checks: projects_options.ac.is_some() && projects_options.project_id.is_some(), - vault, identity, + credential: projects_options.credential, + secure_channels, projects: Arc::new(projects_options.projects), project_id: projects_options.project_id, authorities: None, @@ -305,7 +344,6 @@ impl NodeManager { }, sessions, policies, - attributes_storage, }; if !general_options.skip_defaults { @@ -323,13 +361,15 @@ impl NodeManager { } async fn configure_authorities(&mut self, ac: &AuthoritiesConfig) -> Result<()> { - let vault = self.vault()?; - let mut v = Vec::new(); for a in ac.authorities() { v.push(AuthorityInfo { - identity: PublicIdentity::import(a.1.identity(), vault.clone()).await?, + identity: self + .identities() + .identities_creation() + .import_identity(a.1.identity()) + .await?, addr: a.1.access_route().clone(), }) } @@ -371,8 +411,12 @@ impl NodeManager { // If we've been configured with authorities, we can start Credential Exchange service if self.authorities().is_ok() { - self.start_credentials_service_impl(DefaultAddress::CREDENTIALS_SERVICE.into(), false) - .await?; + self.start_credentials_service_impl( + ctx, + DefaultAddress::CREDENTIALS_SERVICE.into(), + false, + ) + .await?; } Ok(()) @@ -415,9 +459,9 @@ impl NodeManager { i, m, timeout, - identity_name, + identity_name.map(|i| i.to_string()), ctx, - credential_name, + credential_name.map(|c| c.to_string()), tcp_session.session, ) .await?; @@ -445,9 +489,9 @@ impl NodeManager { authorized_identities.clone(), m, timeout, - identity_name, + identity_name.map(|i| i.to_string()), ctx, - credential_name, + credential_name.map(|c| c.to_string()), tcp_session.session, ) .await?; @@ -594,7 +638,7 @@ impl NodeManagerWorker { .await? .either(ResponseBuilder::to_vec, ResponseBuilder::to_vec)?, (Post, ["node", "credentials", "actions", "present"]) => { - self.present_credential(req, dec).await?.to_vec()? + self.present_credential(req, dec, ctx).await?.to_vec()? } // ==*== Secure channels ==*== @@ -613,7 +657,7 @@ impl NodeManagerWorker { self.create_secure_channel(req, dec, ctx).await?.to_vec()? } (Delete, ["node", "secure_channel"]) => { - self.delete_secure_channel(req, dec).await?.to_vec()? + self.delete_secure_channel(req, dec, ctx).await?.to_vec()? } (Get, ["node", "show_secure_channel"]) => { self.show_secure_channel(req, dec).await?.to_vec()? diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/credentials.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/credentials.rs index 3445cce9b84..74eee442638 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/credentials.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/credentials.rs @@ -7,12 +7,10 @@ use crate::nodes::NodeManager; use crate::{create_tcp_session, DefaultAddress}; use either::Either; use minicbor::Decoder; +use ockam::identity::Credential; use ockam::Result; use ockam_core::api::{Error, Request, Response, ResponseBuilder}; -use ockam_core::compat::sync::Arc; use ockam_core::route; -use ockam_identity::credential::Credential; -use ockam_identity::{Identity, IdentityVault}; use ockam_multiaddr::MultiAddr; use ockam_node::Context; use std::str::FromStr; @@ -22,12 +20,13 @@ use super::NodeManagerWorker; impl NodeManager { pub(super) async fn get_credential_impl( &mut self, - identity: &Identity, + ctx: &Context, + identity_name: Option, overwrite: bool, - ) -> Result<()> { + ) -> Result { debug!("Credential check: looking for identity"); - if identity.credential().await.is_some() && !overwrite { + if self.credential().is_some() && !overwrite { return Err(ApiError::generic("credential already exists")); } @@ -42,7 +41,7 @@ impl NodeManager { debug!("Getting credential from : {}", authority.addr); - let allowed = vec![authority.identity.identifier().clone()]; + let allowed = vec![authority.identity.identifier()]; let authority_tcp_session = match create_tcp_session(&authority.addr, &self.tcp_transport).await { @@ -53,10 +52,12 @@ impl NodeManager { } }; + let identity = self.get_identity(None, identity_name).await?; debug!("Create secure channel to project authority"); let sc = self .create_secure_channel_internal( - identity, + &identity, + ctx, authority_tcp_session.route, Some(allowed), None, @@ -69,23 +70,22 @@ impl NodeManager { let authorities = self.authorities()?; let client = CredentialIssuerClient::new( - RpcClient::new( - route![sc, DefaultAddress::CREDENTIAL_ISSUER], - identity.ctx(), - ) - .await?, + RpcClient::new(route![sc, DefaultAddress::CREDENTIAL_ISSUER], ctx).await?, ); let credential = client.credential().await?; debug!("Got credential"); - identity - .verify_self_credential(&credential, authorities.public_identities().iter()) + self.credentials() + .verify_credential( + &identity.identifier(), + authorities.public_identities().as_slice(), + credential.clone(), + ) .await?; debug!("Verified self credential"); - identity.set_credential(credential.to_owned()).await; - - Ok(()) + self.set_credential(credential.to_owned()); + Ok(credential.clone()) } } @@ -99,25 +99,11 @@ impl NodeManagerWorker { let mut node_manager = self.node_manager.write().await; let request: GetCredentialRequest = dec.decode()?; - let identity = if let Some(identity) = &request.identity_name { - let idt_state = node_manager.cli_state.identities.get(identity)?; - match idt_state.get(ctx, node_manager.vault()?).await { - Ok(idt) => Arc::new(idt), - Err(_) => { - let default_vault = &node_manager.cli_state.vaults.default()?.get().await?; - let vault: Arc = Arc::new(default_vault.clone()); - Arc::new(idt_state.get(ctx, vault).await?) - } - } - } else { - node_manager.identity.clone() - }; - node_manager - .get_credential_impl(&identity, request.is_overwrite()) + .get_credential_impl(ctx, request.clone().identity_name, request.is_overwrite()) .await?; - if let Some(c) = identity.credential().await { + if let Some(c) = node_manager.credential() { Ok(Either::Right(Response::ok(req.id()).body(c))) } else { let err = Error::default().with_message("error getting credential"); @@ -129,8 +115,9 @@ impl NodeManagerWorker { &self, req: &Request<'_>, dec: &mut Decoder<'_>, + ctx: &Context, ) -> Result { - let node_manager = self.node_manager.read().await; + let mut node_manager = self.node_manager.write().await; let request: PresentCredentialRequest = dec.decode()?; // TODO: Replace with self.connect? @@ -140,19 +127,20 @@ impl NodeManagerWorker { None => return Err(ApiError::generic("invalid credentials service route")), }; + let credential = node_manager.get_credential_if_needed(ctx, None).await?; if request.oneway { node_manager - .identity - .present_credential(route, None) + .credentials_service() + .present_credential(ctx, route, credential) .await?; } else { node_manager - .identity + .credentials_service() .present_credential_mutual( + ctx, route, &node_manager.authorities()?.public_identities(), - node_manager.attributes_storage.clone(), - None, + credential, ) .await?; } diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/forwarder.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/forwarder.rs index 8c06a5f41a2..45be2ab44f7 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/forwarder.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/forwarder.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use minicbor::Decoder; use ockam::compat::asynchronous::RwLock; +use ockam::identity::IdentityIdentifier; use ockam::remote::{RemoteForwarder, RemoteForwarderTrustOptions}; use ockam::Result; use ockam_core::api::{Id, Response, Status}; use ockam_core::AsyncTryClone; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; use ockam_node::tokio::time::timeout; use ockam_node::Context; @@ -126,7 +126,7 @@ fn replacer( let f = async { let prev = try_multiaddr_to_addr(&prev)?; let mut this = manager.write().await; - let _ = this.delete_secure_channel(&prev).await; + let _ = this.delete_secure_channel(&ctx, &prev).await; let connection = Connection::new(ctx.as_ref(), &addr) .with_authorized_identity(auth) .with_timeout(util::MAX_CONNECT_TIME); diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/node_identities.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/node_identities.rs new file mode 100644 index 00000000000..eee4dd4ea92 --- /dev/null +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/node_identities.rs @@ -0,0 +1,93 @@ +use crate::cli_state::CliState; +use ockam::compat::sync::Arc; +use ockam::identity::{identities, Identities, IdentitiesCreation, IdentitiesKeys}; +use ockam::identity::{IdentitiesVault, Identity}; +use ockam::Result; + +/// This struct supports identities operation that are either backed by +/// a specific vault or which are using the default vault +pub struct NodeIdentities { + vault: Arc, + cli_state: CliState, +} + +impl NodeIdentities { + pub fn new(vault: Arc, cli_state: CliState) -> NodeIdentities { + NodeIdentities { vault, cli_state } + } + + pub(super) fn identities_vault(&self) -> Arc { + self.vault.clone() + } + + /// Return an identity if it has been created with that name before + /// If a vault name is specified, use it to validate the identity against that vault before returning it + pub(crate) async fn get_identity( + &self, + identity_name: String, + vault_name: Option, + ) -> Result> { + let vault = self.get_identities_vault(vault_name).await?; + if let Ok(idt_state) = self.cli_state.identities.get(identity_name.as_str()) { + Ok(Some(idt_state.get(vault).await?)) + } else { + Ok(None) + } + } + + /// Return an identities creation service backed up by the default vault + pub(crate) async fn get_default_identities_creation(&self) -> Result> { + Ok(Arc::new(self.get_identities_creation(None).await?)) + } + + /// Return an identities keys service backed up by the default vault + pub(crate) async fn get_default_identities_keys(&self) -> Result> { + Ok(identities::builder() + .with_identities_vault(self.vault.clone()) + .build() + .identities_keys()) + } + + /// Return an identities service for a specific identity + pub(crate) async fn get_identities( + &self, + vault_name: Option, + identity_name: String, + ) -> Result> { + let vault = self.get_identities_vault(vault_name).await?; + let idt_state = self.cli_state.identities.get(identity_name.as_str())?; + Ok(idt_state.make_identities(vault.clone()).await?) + } + + /// Return an identities creations service + pub(crate) async fn get_identities_creation( + &self, + vault_name: Option, + ) -> Result { + let vault = self.get_identities_vault(vault_name).await?; + Ok(IdentitiesCreation::new(vault)) + } + + /// Return either the default vault or a specific one + pub(crate) async fn get_identities_vault( + &self, + vault_name: Option, + ) -> Result> { + if let Some(vault) = vault_name { + let existing_vault = self.cli_state.vaults.get(vault.as_str())?.get().await?; + Ok(Arc::new(existing_vault)) + } else { + Ok(self.identities_vault()) + } + } + + /// Return a service to perform key operations + pub(crate) async fn get_identities_keys( + &self, + vault_name: Option, + ) -> Result> { + Ok(Arc::new(IdentitiesKeys::new( + self.get_identities_vault(vault_name).await?, + ))) + } +} diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/services.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/node_services.rs similarity index 93% rename from implementations/rust/ockam/ockam_api/src/nodes/service/services.rs rename to implementations/rust/ockam/ockam_api/src/nodes/service/node_services.rs index 5383d2f8b7c..837152c34b1 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/services.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/node_services.rs @@ -1,4 +1,5 @@ use crate::auth::Server; +use crate::authenticator::direct::{CredentialIssuer, EnrollmentTokenAuthenticator}; use crate::echoer::Echoer; use crate::error::ApiError; use crate::hop::Hop; @@ -27,13 +28,12 @@ use crate::{actions, resources}; use crate::{local_multiaddr_to_route, DefaultAddress}; use core::time::Duration; use minicbor::Decoder; -use ockam::{Address, AsyncTryClone, Context, Result}; +use ockam::{Address, Context, Result}; use ockam_abac::expr::{and, eq, ident, str}; use ockam_abac::{Action, Env, Expr, PolicyAccessControl, Resource}; use ockam_core::api::{bad_request, Error, Request, Response, ResponseBuilder}; use ockam_core::compat::sync::Arc; use ockam_core::{route, AllowAll, IncomingAccessControl}; -use ockam_identity::authenticated_storage::IdentityAttributeStorageReader; use ockam_multiaddr::proto::Project; use ockam_multiaddr::MultiAddr; use ockam_node::WorkerBuilder; @@ -51,7 +51,7 @@ impl NodeManager { return Err(ApiError::generic("Vault service exists at this address")); } - let service = VaultService::new(self.vault()?.clone()); + let service = VaultService::new(self.secure_channels_vault()); ctx.start_worker( addr.clone(), @@ -77,7 +77,7 @@ impl NodeManager { return Err(ApiError::generic("Identity service exists at this address")); } - let service = IdentityService::new(ctx, self.vault.clone(), self.cli_state.clone()).await?; + let service = IdentityService::new(self.node_identities()).await?; ctx.start_worker( addr.clone(), @@ -96,6 +96,7 @@ impl NodeManager { pub(super) async fn start_credentials_service_impl<'a>( &mut self, + ctx: &Context, addr: Address, oneway: bool, ) -> Result<()> { @@ -107,12 +108,13 @@ impl NodeManager { let authorities = self.authorities()?; - self.identity - .start_credential_exchange_worker( + self.credentials_service() + .start( + ctx, + self.credential(), authorities.public_identities(), addr.clone(), !oneway, - self.attributes_storage.clone(), ) .await?; @@ -134,10 +136,7 @@ impl NodeManager { )); } - let identity_attributes_reader: Arc = self - .attributes_storage - .as_identity_attribute_storage_reader(); - let server = Server::new(identity_attributes_reader.clone()); + let server = Server::new(self.attributes_reader()); ctx.start_worker( addr.clone(), server, @@ -244,7 +243,7 @@ impl NodeManager { } Ok(Arc::new(PolicyAccessControl::new( self.policies.clone(), - self.attributes_storage.clone(), + self.identities_repository(), r.clone(), a.clone(), env, @@ -269,13 +268,8 @@ impl NodeManager { let abac = self .build_access_control(&resource, &action, project, &rule) .await?; - let issuer = crate::authenticator::direct::CredentialIssuer::new( - proj.to_vec(), - self.attributes_storage - .as_identity_attribute_storage_reader(), - self.identity.clone(), - ) - .await?; + let issuer = + CredentialIssuer::new(self.identities(), self.identity.clone(), proj.to_vec()).await?; WorkerBuilder::with_access_control(abac, Arc::new(AllowAll), addr.clone(), issuer) .start(ctx) .await @@ -312,7 +306,7 @@ impl NodeManager { let direct = crate::authenticator::direct::DirectAuthenticator::new( proj.to_vec(), - self.attributes_storage.clone(), + self.attributes_writer(), ) .await?; @@ -357,10 +351,7 @@ impl NodeManager { let resource = Resource::new(&issuer_addr.to_string()); let project = std::str::from_utf8(proj).unwrap(); let (issuer, acceptor) = - crate::authenticator::direct::EnrollmentTokenAuthenticator::new_worker_pair( - proj.to_vec(), - self.attributes_storage.async_try_clone().await?, - ); + EnrollmentTokenAuthenticator::new_worker_pair(proj.to_vec(), self.attributes_writer()); let rule = and([ eq([ident("resource.project_id"), ident("subject.project_id")]), eq([ident("subject.ockam-role"), str("enroller")]), @@ -410,11 +401,13 @@ impl NodeManager { "Okta Identity Provider service already started", )); } - let db = self - .attributes_storage - .as_identity_attribute_storage_writer(); - let au = - crate::okta::Server::new(proj.to_vec(), db, tenant_base_url, certificate, attributes)?; + let au = crate::okta::Server::new( + self.attributes_writer(), + proj.to_vec(), + tenant_base_url, + certificate, + attributes, + )?; ctx.start_worker( addr.clone(), au, @@ -441,7 +434,7 @@ impl NodeManager { kind: KafkaServiceKind, ) -> Result<()> { let connection = Connection::new(context, &project_route_multiaddr) - .with_authorized_identity(self.identity.clone().identifier().clone()) + .with_authorized_identity(self.identity().identifier()) .with_timeout(Duration::from_secs(60)); let (maybe_tunnel_multiaddr, suffix_address) = self.connect(connection).await?; @@ -487,8 +480,11 @@ impl NodeManager { ) .await?; - let secure_channel_controller = - KafkaSecureChannelControllerImpl::new(self.identity.clone(), project_route); + let secure_channel_controller = KafkaSecureChannelControllerImpl::new( + self.secure_channels.clone(), + self.identity.clone(), + project_route, + ); if let KafkaServiceKind::Consumer = kind { secure_channel_controller @@ -680,7 +676,7 @@ impl NodeManagerWorker { return Err(ApiError::generic("Verifier service exists at this address")); } - let vs = crate::verifier::Verifier::new(node_manager.vault.clone()); + let vs = crate::verifier::Verifier::new(node_manager.identities()); ctx.start_worker( addr.clone(), vs, @@ -699,7 +695,7 @@ impl NodeManagerWorker { pub(super) async fn start_credentials_service<'a>( &mut self, - _ctx: &Context, + ctx: &Context, req: &'a Request<'_>, dec: &mut Decoder<'_>, ) -> Result { @@ -709,7 +705,7 @@ impl NodeManagerWorker { let oneway = body.oneway(); node_manager - .start_credentials_service_impl(addr, oneway) + .start_credentials_service_impl(ctx, addr, oneway) .await?; Ok(Response::ok(req.id())) diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/portals.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/portals.rs index f9e83e724e4..47c2130fbc0 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/portals.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/portals.rs @@ -10,13 +10,13 @@ use crate::{actions, resources}; use crate::{local_multiaddr_to_route, try_multiaddr_to_addr}; use minicbor::Decoder; use ockam::compat::tokio::time::timeout; +use ockam::identity::IdentityIdentifier; use ockam::{Address, AsyncTryClone, Result}; use ockam_abac::expr::{eq, ident, str}; use ockam_abac::{Action, Env, PolicyAccessControl, Resource}; use ockam_core::api::{Request, Response, ResponseBuilder}; use ockam_core::compat::sync::Arc; use ockam_core::{AllowAll, IncomingAccessControl}; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::proto::{Project, Secure, Service}; use ockam_multiaddr::{MultiAddr, Protocol}; use ockam_node::compat::asynchronous::RwLock; @@ -48,11 +48,9 @@ impl NodeManager { let fallback = eq([ident("resource.project_id"), ident("subject.project_id")]); self.policies.set_policy(r, a, &fallback).await? } - let store = self.attributes_storage.async_try_clone().await?; - let policies = self.policies.clone(); Ok(Arc::new(PolicyAccessControl::new( - policies, - store, + self.policies.clone(), + self.identities_repository(), r.clone(), a.clone(), env, @@ -498,10 +496,10 @@ fn replacer( // First the previous secure channel is deleted, and -- if secure // channels were nested -- the outer one as well: - let _ = this.delete_secure_channel(&prev).await; + let _ = this.delete_secure_channel(&ctx, &prev).await; if let Some(a) = data.get::(OUTER_CHAN) { let a = try_multiaddr_to_addr(&a)?; - let _ = this.delete_secure_channel(&a).await; + let _ = this.delete_secure_channel(&ctx, &a).await; } // Now a connection attempt is made: diff --git a/implementations/rust/ockam/ockam_api/src/nodes/service/secure_channel.rs b/implementations/rust/ockam/ockam_api/src/nodes/service/secure_channel.rs index 9d41e4c1ebc..760adad08d5 100644 --- a/implementations/rust/ockam/ockam_api/src/nodes/service/secure_channel.rs +++ b/implementations/rust/ockam/ockam_api/src/nodes/service/secure_channel.rs @@ -17,33 +17,41 @@ use ockam::identity::TrustEveryonePolicy; use ockam::{Address, Result, Route}; use ockam_core::api::{Request, Response, ResponseBuilder}; use ockam_core::compat::sync::Arc; +use ockam_core::route; use ockam_core::sessions::{SessionId, Sessions}; -use ockam_core::{route, CowStr}; -use ockam_identity::{ - Identity, IdentityIdentifier, IdentityVault, SecureChannelListenerTrustOptions, - SecureChannelTrustOptions, TrustMultiIdentifiersPolicy, +use crate::cli_state::CliStateError; +use crate::nodes::service::NodeIdentities; +use ockam::identity::{ + secure_channels, Credential, Identities, IdentitiesVault, Identity, IdentityIdentifier, + SecureChannelListenerTrustOptions, SecureChannelTrustOptions, SecureChannels, + TrustMultiIdentifiersPolicy, }; use ockam_multiaddr::MultiAddr; use ockam_node::Context; impl NodeManager { - async fn get_credential_if_needed(&mut self, identity: &Identity) -> Result<()> { - if identity.credential().await.is_some() { + pub(super) async fn get_credential_if_needed( + &mut self, + ctx: &Context, + identity_name: Option, + ) -> Result { + if let Some(credential) = self.credential() { debug!("Credential check: credential already exists..."); - return Ok(()); + return Ok(credential); } debug!("Credential check: requesting..."); - self.get_credential_impl(identity, false).await?; + let credential = self.get_credential_impl(ctx, identity_name, false).await?; debug!("Credential check: got new credential..."); - Ok(()) + Ok(credential) } pub(crate) async fn create_secure_channel_internal( &mut self, identity: &Identity, + ctx: &Context, sc_route: Route, authorized_identifiers: Option>, timeout: Option, @@ -73,8 +81,9 @@ impl NodeManager { None => trust_options.with_trust_policy(TrustEveryonePolicy), }; - let sc_addr = identity - .create_secure_channel_extended(sc_route.clone(), trust_options, timeout) + let sc_addr = self + .secure_channels + .create_secure_channel_extended(ctx, identity, sc_route.clone(), trust_options, timeout) .await?; debug!(%sc_route, %sc_addr, "Created secure channel"); @@ -93,24 +102,12 @@ impl NodeManager { authorized_identifiers: Option>, credential_exchange_mode: CredentialExchangeMode, timeout: Option, - identity_name: Option>, + identity_name: Option, ctx: &Context, - credential_name: Option>, + credential_name: Option, session: Option<(Sessions, SessionId)>, ) -> Result
{ - let identity: Arc = if let Some(identity) = identity_name { - let idt_state = self.cli_state.identities.get(&identity)?; - match idt_state.get(ctx, self.vault()?).await { - Ok(idt) => Arc::new(idt), - Err(_) => { - let default_vault = &self.cli_state.vaults.default()?.get().await?; - let vault: Arc = Arc::new(default_vault.clone()); - Arc::new(idt_state.get(ctx, vault).await?) - } - } - } else { - self.identity.clone() - }; + let identity = self.get_identity(None, identity_name.clone()).await?; let provided_credential = if let Some(credential_name) = credential_name { Some( self.cli_state @@ -127,6 +124,7 @@ impl NodeManager { let sc_addr = self .create_secure_channel_internal( &identity, + ctx, sc_route, authorized_identifiers, timeout, @@ -150,31 +148,32 @@ impl NodeManager { } CredentialExchangeMode::Oneway => { debug!(%sc_addr, "One-way credential presentation"); - if provided_credential.is_none() { - self.get_credential_if_needed(&identity).await?; - } + let credential = self + .get_provided_credential_or_default(ctx, identity_name, provided_credential) + .await?; - identity + self.credentials_service() .present_credential( + ctx, route![sc_addr.clone(), DefaultAddress::CREDENTIALS_SERVICE], - provided_credential.as_ref(), + credential, ) .await?; debug!(%sc_addr, "One-way credential presentation success"); } CredentialExchangeMode::Mutual => { debug!(%sc_addr, "Mutual credential presentation"); - if provided_credential.is_none() { - self.get_credential_if_needed(&identity).await?; - } + let credential = self + .get_provided_credential_or_default(ctx, identity_name, provided_credential) + .await?; let authorities = self.authorities()?; - identity + self.credentials_service() .present_credential_mutual( + ctx, route![sc_addr.clone(), DefaultAddress::CREDENTIALS_SERVICE], &authorities.public_identities(), - self.attributes_storage.clone(), - provided_credential.as_ref(), + credential, ) .await?; debug!(%sc_addr, "Mutual credential presentation success"); @@ -185,34 +184,38 @@ impl NodeManager { Ok(sc_addr) } + async fn get_provided_credential_or_default( + &mut self, + ctx: &Context, + identity_name: Option, + provided_credential: Option, + ) -> Result { + if let Some(credential) = provided_credential { + Ok(credential) + } else { + Ok(self.get_credential_if_needed(ctx, identity_name).await?) + } + } + pub(super) async fn create_secure_channel_listener_impl( &mut self, - addr: Address, + address: Address, authorized_identifiers: Option>, - vault_name: Option>, - identity_name: Option>, + vault_name: Option, + identity_name: Option, ctx: &Context, ) -> Result<()> { info!( "Handling request to create a new secure channel listener: {}", - addr + address ); - let identity: Arc = if let Some(identity) = identity_name { - let idt_state = self.cli_state.identities.get(&identity)?; - if let Some(vault) = vault_name { - let default_vault = self.cli_state.vaults.get(&vault)?.get().await?; - let vault: Arc = Arc::new(default_vault.clone()); - Arc::new(idt_state.get(ctx, vault).await?) - } else { - Arc::new(idt_state.get(ctx, self.vault()?).await?) - } - } else { - if vault_name.is_some() { - warn!("The optional vault is ignored when an optional identity is not specified. Using the default identity."); - } - self.identity.clone() - }; + let secure_channels = self + .get_secure_channels(vault_name.clone(), identity_name.clone()) + .await?; + let identity = self + .get_identity(vault_name.clone(), identity_name.clone()) + .await?; let trust_options = SecureChannelListenerTrustOptions::new(); // FIXME: add session_id let trust_options = match authorized_identifiers { @@ -220,20 +223,93 @@ impl NodeManager { None => trust_options.with_trust_policy(TrustEveryonePolicy), }; - identity - .create_secure_channel_listener(addr.clone(), trust_options) + secure_channels + .create_secure_channel_listener(ctx, &identity, address.clone(), trust_options) .await?; self.registry .secure_channel_listeners - .insert(addr, Default::default()); + .insert(address, Default::default()); Ok(()) } - pub(super) async fn delete_secure_channel(&mut self, addr: &Address) -> Result<()> { + pub(crate) async fn get_secure_channels( + &mut self, + vault_name: Option, + identity_name: Option, + ) -> Result> { + let secure_channels = if let Some(identity) = identity_name { + let vault = self.get_secure_channels_vault(vault_name.clone()).await?; + let identities = self.get_identities(vault_name, identity).await?; + let registry = self.secure_channels.secure_channel_registry(); + secure_channels::builder() + .with_identities_vault(vault) + .with_identities(identities) + .with_secure_channels_registry(registry) + .build() + } else { + if vault_name.is_some() { + warn!("The optional vault is ignored when an optional identity is not specified. Using the default identity."); + } + self.secure_channels.clone() + }; + Ok(secure_channels) + } + + pub(super) fn node_identities(&self) -> NodeIdentities { + NodeIdentities::new(self.identities_vault(), self.cli_state.clone()) + } + + pub(crate) async fn get_identity( + &self, + vault_name: Option, + identity_name: Option, + ) -> Result { + if let Some(name) = identity_name { + if let Some(identity) = self + .node_identities() + .get_identity(name.clone(), vault_name) + .await? + { + Ok(identity) + } else { + Err(CliStateError::NotFound(format!("identity not found with name {name}")).into()) + } + } else { + Ok(self.identity.clone()) + } + } + + async fn get_identities( + &mut self, + vault_name: Option, + identity_name: String, + ) -> Result> { + self.node_identities() + .get_identities(vault_name, identity_name) + .await + } + + async fn get_secure_channels_vault( + &mut self, + vault_name: Option, + ) -> Result> { + if let Some(vault) = vault_name { + let existing_vault = self.cli_state.vaults.get(vault.as_str())?.get().await?; + Ok(Arc::new(existing_vault)) + } else { + Ok(self.secure_channels_vault()) + } + } + + pub(super) async fn delete_secure_channel( + &mut self, + ctx: &Context, + addr: &Address, + ) -> Result<()> { debug!(%addr, "deleting secure channel"); - self.identity.stop_secure_channel(addr).await?; + self.secure_channels.stop_secure_channel(ctx, addr).await?; self.registry.secure_channels.remove_by_addr(addr); Ok(()) } @@ -278,72 +354,17 @@ impl NodeManagerWorker { ) } - pub(super) async fn create_secure_channel( - &mut self, - req: &Request<'_>, - dec: &mut Decoder<'_>, - ctx: &Context, - ) -> Result>> { - let mut node_manager = self.node_manager.write().await; - let CreateSecureChannelRequest { - addr, - authorized_identifiers, - credential_exchange_mode, - timeout, - identity_name: identity, - credential_name, - .. - } = dec.decode()?; - - // credential retrieved from request - info!("Handling request to create a new secure channel: {}", addr); - - let authorized_identifiers = match authorized_identifiers { - Some(ids) => { - let ids = ids - .into_iter() - .map(|x| IdentityIdentifier::try_from(x.0.as_ref())) - .collect::>>()?; - - Some(ids) - } - None => None, - }; - - // TODO: Improve error handling + move logic into CreateSecureChannelRequest - let addr = MultiAddr::try_from(addr.as_ref()).map_err(map_multiaddr_err)?; - let tcp_session = create_tcp_session(&addr, &node_manager.tcp_transport) - .await - .ok_or_else(|| ApiError::generic("Invalid Multiaddr"))?; - - let channel = node_manager - .create_secure_channel_impl( - tcp_session.route, - authorized_identifiers, - credential_exchange_mode, - timeout, - identity, - ctx, - credential_name, - tcp_session.session, - ) - .await?; - - let response = Response::ok(req.id()).body(CreateSecureChannelResponse::new(&channel)); - - Ok(response) - } - pub(super) async fn delete_secure_channel( &mut self, req: &Request<'_>, dec: &mut Decoder<'_>, + ctx: &Context, ) -> Result>> { let body: DeleteSecureChannelRequest = dec.decode()?; let addr = Address::from(body.channel.as_ref()); info!(%addr, "Handling request to delete secure channel"); let mut node_manager = self.node_manager.write().await; - let res = match node_manager.delete_secure_channel(&addr).await { + let res = match node_manager.delete_secure_channel(ctx, &addr).await { Ok(()) => { trace!(%addr, "Removed secure channel"); Some(addr) @@ -409,7 +430,13 @@ impl NodeManagerWorker { } node_manager - .create_secure_channel_listener_impl(addr, authorized_identifiers, vault, identity, ctx) + .create_secure_channel_listener_impl( + addr, + authorized_identifiers, + vault.map(|v| v.to_string()), + identity.map(|v| v.to_string()), + ctx, + ) .await?; let response = Response::ok(req.id()); @@ -459,3 +486,61 @@ impl NodeManagerWorker { Ok(Response::ok(req.id()).body(ShowSecureChannelListenerResponse::new(&address))) } } + +impl NodeManagerWorker { + pub(super) async fn create_secure_channel( + &mut self, + req: &Request<'_>, + dec: &mut Decoder<'_>, + ctx: &Context, + ) -> Result>> { + let mut node_manager = self.node_manager.write().await; + let CreateSecureChannelRequest { + addr, + authorized_identifiers, + credential_exchange_mode, + timeout, + identity_name: identity, + credential_name, + .. + } = dec.decode()?; + + // credential retrieved from request + info!("Handling request to create a new secure channel: {}", addr); + + let authorized_identifiers = match authorized_identifiers { + Some(ids) => { + let ids = ids + .into_iter() + .map(|x| IdentityIdentifier::try_from(x.0.as_ref())) + .collect::>>()?; + + Some(ids) + } + None => None, + }; + + // TODO: Improve error handling + move logic into CreateSecureChannelRequest + let addr = MultiAddr::try_from(addr.as_ref()).map_err(map_multiaddr_err)?; + let tcp_session = create_tcp_session(&addr, &node_manager.tcp_transport) + .await + .ok_or_else(|| ApiError::generic("Invalid Multiaddr"))?; + + let channel = node_manager + .create_secure_channel_impl( + tcp_session.route, + authorized_identifiers, + credential_exchange_mode, + timeout, + identity.map(|i| i.to_string()), + ctx, + credential_name.map(|c| c.to_string()), + tcp_session.session, + ) + .await?; + + let response = Response::ok(req.id()).body(CreateSecureChannelResponse::new(&channel)); + + Ok(response) + } +} diff --git a/implementations/rust/ockam/ockam_api/src/okta/mod.rs b/implementations/rust/ockam/ockam_api/src/okta/mod.rs index 637a96fb381..7a15108462b 100644 --- a/implementations/rust/ockam/ockam_api/src/okta/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/okta/mod.rs @@ -2,20 +2,21 @@ use crate::error::ApiError; use core::str; use minicbor::Decoder; use ockam::identity::credential::Timestamp; +use ockam::identity::{ + AttributesEntry, IdentityAttributesWriter, IdentityIdentifier, IdentitySecureChannelLocalInfo, +}; use ockam_core::api; use ockam_core::api::{Method, Request, Response}; use ockam_core::compat::sync::Arc; use ockam_core::{self, Result, Routed, Worker}; -use ockam_identity::authenticated_storage::{AttributesEntry, IdentityAttributeStorageWriter}; -use ockam_identity::{IdentityIdentifier, IdentitySecureChannelLocalInfo}; use ockam_node::Context; use reqwest::StatusCode; use std::collections::HashMap; use tracing::trace; pub struct Server { + attributes_writer: Arc, project: Vec, - store: Arc, tenant_base_url: String, certificate: reqwest::Certificate, attributes: Vec, @@ -28,7 +29,7 @@ impl Worker for Server { async fn handle_message(&mut self, c: &mut Context, m: Routed) -> Result<()> { if let Ok(i) = IdentitySecureChannelLocalInfo::find_info(m.local_message()) { - let r = self.on_request(i.their_identity_id(), m.as_body()).await?; + let r = self.on_request(&i.their_identity_id(), m.as_body()).await?; c.send(m.return_route(), r).await } else { let mut dec = Decoder::new(m.as_body()); @@ -41,8 +42,8 @@ impl Worker for Server { impl Server { pub fn new( + attributes_writer: Arc, project: Vec, - store: Arc, tenant_base_url: &str, certificate: &str, attributes: &[&str], @@ -50,8 +51,8 @@ impl Server { let certificate = reqwest::Certificate::from_pem(certificate.as_bytes()) .map_err(|err| ApiError::generic(&err.to_string()))?; Ok(Server { + attributes_writer, project, - store, tenant_base_url: tenant_base_url.to_string(), certificate, attributes: attributes.iter().map(|s| s.to_string()).collect(), @@ -102,7 +103,7 @@ impl Server { None, None, ); - self.store.put_attributes(from, entry).await?; + self.attributes_writer.put_attributes(from, entry).await?; Response::ok(req.id()).to_vec()? } else { api::forbidden(&req, "Forbidden").to_vec()? diff --git a/implementations/rust/ockam/ockam_api/src/util.rs b/implementations/rust/ockam/ockam_api/src/util.rs index 5fe960bb73c..c2489f59a64 100644 --- a/implementations/rust/ockam/ockam_api/src/util.rs +++ b/implementations/rust/ockam/ockam_api/src/util.rs @@ -313,10 +313,10 @@ pub mod test { NodeManagerTransportOptions, }; use crate::nodes::{NodeManager, NodeManagerWorker, NODEMANAGER_ADDR}; + use ockam::identity::{secure_channels, Identity, SecureChannels}; use ockam::Result; use ockam_core::compat::sync::Arc; use ockam_core::AsyncTryClone; - use ockam_identity::{Identity, IdentityVault}; use ockam_node::compat::asynchronous::RwLock; use ockam_node::Context; use ockam_transport_tcp::TcpTransport; @@ -330,7 +330,8 @@ pub mod test { pub cli_state: CliState, pub node_manager: Arc>, pub tcp: TcpTransport, - pub identity: Arc, + pub secure_channels: Arc, + pub identity: Identity, } impl Drop for NodeManagerHandle { @@ -359,14 +360,16 @@ pub mod test { .await?; let identity_name = hex::encode(rand::random::<[u8; 4]>()); - let vault: Arc = Arc::new(vault); - let identity = Identity::create_ext( - context, - cli_state.identities.authenticated_storage().await?, - vault, - ) - .await - .unwrap(); + let secure_channels = secure_channels::builder() + .with_identities_vault(Arc::new(vault)) + .with_identities_repository(cli_state.identities.identities_repository().await?) + .build(); + let identity = secure_channels + .identities() + .identities_creation() + .create_identity() + .await + .unwrap(); let config = IdentityConfig::new(&identity).await; cli_state.identities.create(&identity_name, config).unwrap(); @@ -405,7 +408,8 @@ pub mod test { cli_state, node_manager, tcp: tcp.async_try_clone().await?, - identity: Arc::new(identity), + secure_channels: secure_channels.clone(), + identity: identity.clone(), }) } } diff --git a/implementations/rust/ockam/ockam_api/src/vault.rs b/implementations/rust/ockam/ockam_api/src/vault.rs index ca88415b754..c270a07d318 100644 --- a/implementations/rust/ockam/ockam_api/src/vault.rs +++ b/implementations/rust/ockam/ockam_api/src/vault.rs @@ -5,23 +5,23 @@ use core::convert::Infallible; use minicbor::encode::Write; use minicbor::{Decoder, Encode}; use models::*; +use ockam::identity::IdentitiesVault; use ockam_core::api::{Error, Id, Method, Request, Response, Status}; use ockam_core::compat::sync::Arc; use ockam_core::vault::{KeyId, Signature}; use ockam_core::CowStr; use ockam_core::{Result, Routed, Worker}; -use ockam_identity::IdentityVault; use ockam_node::Context; use tracing::trace; /// Vault Service Worker pub struct VaultService { - vault: Arc, + vault: Arc, } impl VaultService { /// Constructor - pub fn new(vault: Arc) -> Self { + pub fn new(vault: Arc) -> Self { Self { vault: vault.clone(), } diff --git a/implementations/rust/ockam/ockam_api/src/verifier.rs b/implementations/rust/ockam/ockam_api/src/verifier.rs index cedc2669049..cf0417464f3 100644 --- a/implementations/rust/ockam/ockam_api/src/verifier.rs +++ b/implementations/rust/ockam/ockam_api/src/verifier.rs @@ -2,19 +2,19 @@ pub mod types; use either::Either; use minicbor::Decoder; +use ockam::identity::credential::{Credential, CredentialData, Verified}; +use ockam::identity::Identities; use ockam_core::api::{self, Id, ResponseBuilder}; use ockam_core::api::{Error, Method, Request, Response}; use ockam_core::compat::sync::Arc; use ockam_core::{self, Result, Routed, Worker}; -use ockam_identity::credential::{Credential, CredentialData, Verified}; -use ockam_identity::{IdentityVault, PublicIdentity}; use ockam_node::Context; use tracing::trace; use self::types::{VerifyRequest, VerifyResponse}; pub struct Verifier { - vault: Arc, + identities: Arc, } #[ockam_core::worker] @@ -29,8 +29,8 @@ impl Worker for Verifier { } impl Verifier { - pub fn new(vault: Arc) -> Self { - Self { vault } + pub fn new(identities: Arc) -> Self { + Self { identities } } async fn on_request(&mut self, data: &[u8]) -> Result> { @@ -85,17 +85,22 @@ impl Verifier { req: &'a VerifyRequest<'a>, cre: &Credential, ) -> Result>, CredentialData>> { - let data = CredentialData::try_from(cre)?; + let data = CredentialData::try_from(cre.data.as_slice())?; - let ident = if let Some(ident) = req.authority(data.unverified_issuer()) { - PublicIdentity::import(ident, self.vault.clone()).await? + let authority = if let Some(ident) = req.authority(data.unverified_issuer()) { + self.identities + .identities_creation() + .import_identity(ident) + .await? } else { let err = Error::new("/verify").with_message("unauthorised issuer"); return Ok(Either::Left(Response::unauthorized(id).body(err))); }; - let data = match ident - .verify_credential(cre, req.subject(), self.vault.clone()) + let data = match self + .identities + .credentials() + .verify_credential(req.subject(), vec![authority].as_slice(), cre.clone()) .await { Ok(data) => data, diff --git a/implementations/rust/ockam/ockam_api/src/verifier/types.rs b/implementations/rust/ockam/ockam_api/src/verifier/types.rs index e8a3c7fdfc0..434c8277a18 100644 --- a/implementations/rust/ockam/ockam_api/src/verifier/types.rs +++ b/implementations/rust/ockam/ockam_api/src/verifier/types.rs @@ -1,8 +1,8 @@ use minicbor::{Decode, Encode}; +use ockam::identity::credential::{Attributes, Timestamp}; +use ockam::identity::IdentityIdentifier; use ockam_core::compat::borrow::Cow; use ockam_core::CowBytes; -use ockam_identity::credential::{Attributes, Timestamp}; -use ockam_identity::IdentityIdentifier; use std::collections::BTreeMap; #[cfg(feature = "tag")] diff --git a/implementations/rust/ockam/ockam_api/tests/auth_smoke.rs b/implementations/rust/ockam/ockam_api/tests/auth_smoke.rs index cd44f46ff3f..9d3acb42843 100644 --- a/implementations/rust/ockam/ockam_api/tests/auth_smoke.rs +++ b/implementations/rust/ockam/ockam_api/tests/auth_smoke.rs @@ -1,7 +1,7 @@ +use ockam::identity::IdentityAttributesReader; use ockam_api::auth; use ockam_api::bootstrapped_identities_store::PreTrustedIdentities; use ockam_core::{AllowAll, Result}; -use ockam_identity::authenticated_storage::IdentityAttributeStorageReader; use ockam_node::Context; use std::sync::Arc; @@ -12,7 +12,7 @@ async fn auth_smoke(ctx: &mut Context) -> Result<()> { "P624ed0b2e5a2be82e267ead6b3279f683616b66de9537a23e45343c95cbb357b":{"attr":"value2"} }"#, )?; - let s: Arc = Arc::new(s); + let s: Arc = Arc::new(s); ctx.start_worker("auth", auth::Server::new(s), AllowAll, AllowAll) .await?; diff --git a/implementations/rust/ockam/ockam_api/tests/credential_issuer.rs b/implementations/rust/ockam/ockam_api/tests/credential_issuer.rs index 1a823c8b40e..b692be5df30 100644 --- a/implementations/rust/ockam/ockam_api/tests/credential_issuer.rs +++ b/implementations/rust/ockam/ockam_api/tests/credential_issuer.rs @@ -1,16 +1,15 @@ use ockam_core::compat::collections::{BTreeMap, HashMap}; use ockam_core::compat::sync::Arc; -use ockam::authenticated_storage::AttributesEntry; -use ockam::identity::Identity; +use ockam::identity::credential::Timestamp; +use ockam::identity::{ + identities, secure_channels, AttributesEntry, IdentitiesStorage, TrustEveryonePolicy, +}; use ockam::route; -use ockam::vault::Vault; use ockam_api::authenticator::direct; -use ockam_api::bootstrapped_identities_store::PreTrustedIdentities; +use ockam_api::bootstrapped_identities_store::{BootstrapedIdentityStore, PreTrustedIdentities}; use ockam_core::compat::rand::random_string; use ockam_core::{AllowAll, Result}; -use ockam_identity::credential::Timestamp; -use ockam_identity::{PublicIdentity, TrustEveryonePolicy}; use ockam_node::Context; #[ockam_macros::test] @@ -18,11 +17,15 @@ async fn credential(ctx: &mut Context) -> Result<()> { let api_worker_addr = random_string(); let auth_worker_addr = random_string(); - let auth_identity = Arc::new(Identity::create(ctx, Vault::create()).await?); - let member_identity = Arc::new(Identity::create(ctx, Vault::create()).await?); + // create 2 identities to populate the trusted identities + let identities = identities(); + let auth_identity = identities.identities_creation().create_identity().await?; + let member_identity = identities.identities_creation().create_identity().await?; + let now = Timestamp::now().unwrap(); + let pre_trusted = HashMap::from([( - member_identity.identifier().clone(), + member_identity.identifier(), AttributesEntry::new( BTreeMap::from([("attr".to_string(), "value".as_bytes().to_vec())]), now, @@ -30,14 +33,34 @@ async fn credential(ctx: &mut Context) -> Result<()> { None, ), )]); - let store = Arc::new(PreTrustedIdentities::from(pre_trusted)); + + let boostrapped = BootstrapedIdentityStore::new( + Arc::new(PreTrustedIdentities::from(pre_trusted)), + IdentitiesStorage::create(), + ); + + // Now recreate the identities services with the previous vault + // (so that the authority can verify its signature) + // and the repository containing the trusted identities + let identities = identities::builder() + .with_identities_repository(Arc::new(boostrapped)) + .with_identities_vault(identities.clone().vault()) + .build(); + let secure_channels = secure_channels::builder() + .with_identities(identities.clone()) + .build(); + let identities_creation = identities.identities_creation(); // Create the CredentialIssuer: - auth_identity - .create_secure_channel_listener(&api_worker_addr, TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &auth_identity, &api_worker_addr, TrustEveryonePolicy) .await?; - let auth = - direct::CredentialIssuer::new(b"project42".to_vec(), store, auth_identity.clone()).await?; + let auth = direct::CredentialIssuer::new( + identities.clone(), + auth_identity.clone(), + b"project42".to_vec(), + ) + .await?; ctx.start_worker( &auth_worker_addr, auth, @@ -47,23 +70,28 @@ async fn credential(ctx: &mut Context) -> Result<()> { .await?; // Connect to the API channel from the member: - let e2a = member_identity - .create_secure_channel(&api_worker_addr, TrustEveryonePolicy) + let e2a = secure_channels + .create_secure_channel(ctx, &member_identity, &api_worker_addr, TrustEveryonePolicy) .await?; // Add the member via the enroller's connection: let c = direct::CredentialIssuerClient::new( direct::RpcClient::new(route![e2a.address(), &auth_worker_addr], ctx).await?, ); // Get a fresh member credential and verify its validity: - let cred = c.credential().await?; - let exported = auth_identity.export().await?; - let vault = Vault::create(); + let credential = c.credential().await?; + let exported = member_identity.export()?; - let pkey = PublicIdentity::import(&exported, Vault::create()) + let imported = identities_creation + .import_identity(&exported) .await .unwrap(); - let data = pkey - .verify_credential(&cred, member_identity.identifier(), vault) + let data = identities + .credentials() + .verify_credential( + &imported.identifier(), + vec![auth_identity].as_slice(), + credential, + ) .await?; assert_eq!( Some(b"project42".as_slice()), diff --git a/implementations/rust/ockam/ockam_api/tests/identity.rs b/implementations/rust/ockam/ockam_api/tests/identity.rs index f39e5e78a48..a4c8703ffb4 100644 --- a/implementations/rust/ockam/ockam_api/tests/identity.rs +++ b/implementations/rust/ockam/ockam_api/tests/identity.rs @@ -1,12 +1,13 @@ use minicbor::Decoder; +use ockam::identity::identity::IdentityHistoryComparison; use ockam_api::cli_state::CliState; use ockam_api::identity::models::*; use ockam_api::identity::IdentityService; +use ockam_api::nodes::service::NodeIdentities; use ockam_core::api::{Request, Response, Status}; use ockam_core::compat::rand::random; use ockam_core::errcode::{Kind, Origin}; use ockam_core::{route, Error, LocalOnwardOnly, LocalSourceOnly, Result}; -use ockam_identity::change_history::IdentityHistoryComparison; use ockam_node::Context; use ockam_vault::Vault; @@ -161,14 +162,14 @@ async fn full_flow(ctx: &mut Context) -> Result<()> { // Start services ctx.start_worker( "1", - IdentityService::new(ctx, vault1, cli_state.clone()).await?, + IdentityService::new(NodeIdentities::new(vault1, cli_state.clone())).await?, LocalSourceOnly, LocalOnwardOnly, ) .await?; ctx.start_worker( "2", - IdentityService::new(ctx, vault2, cli_state).await?, + IdentityService::new(NodeIdentities::new(vault2, cli_state)).await?, LocalSourceOnly, LocalOnwardOnly, ) @@ -178,8 +179,8 @@ async fn full_flow(ctx: &mut Context) -> Result<()> { let (identity2, _identity_id2) = create_identity(ctx, "2").await?; // Identity is updated here + let _identity_id1 = validate_identity_change_history(ctx, &identity1, "2").await?; let _identity_id2 = validate_identity_change_history(ctx, &identity2, "1").await?; - let _identity_id1 = validate_identity_change_history(ctx, &identity2, "2").await?; let comparison1 = compare_identity_change_history(ctx, &identity2, &[], "1").await?; let comparison2 = compare_identity_change_history(ctx, &identity1, &[], "2").await?; @@ -192,8 +193,8 @@ async fn full_flow(ctx: &mut Context) -> Result<()> { let proof1 = create_signature(ctx, &identity1, &state, "1").await?; let proof2 = create_signature(ctx, &identity2, &state, "2").await?; - let verified1 = verify_signature(ctx, &identity2, &state, &proof2, "1").await?; - let verified2 = verify_signature(ctx, &identity1, &state, &proof1, "2").await?; + let verified1 = verify_signature(ctx, &identity1, &state, &proof1, "2").await?; + let verified2 = verify_signature(ctx, &identity2, &state, &proof2, "1").await?; assert!(verified1); assert!(verified2); diff --git a/implementations/rust/ockam/ockam_command/src/authenticated.rs b/implementations/rust/ockam/ockam_command/src/authenticated.rs index e7892c9871a..3634920c3b5 100644 --- a/implementations/rust/ockam/ockam_command/src/authenticated.rs +++ b/implementations/rust/ockam/ockam_command/src/authenticated.rs @@ -5,11 +5,10 @@ use anyhow::{anyhow, Context as _}; use clap::builder::NonEmptyStringValueParser; use clap::{Args, Subcommand}; use ockam::compat::collections::HashMap; +use ockam::identity::{AttributesEntry, IdentityIdentifier}; use ockam::{Context, TcpTransport}; use ockam_api::auth; use ockam_api::is_local_node; -use ockam_identity::authenticated_storage::AttributesEntry; -use ockam_identity::IdentityIdentifier; use ockam_multiaddr::MultiAddr; use termimad::{minimad::TextTemplate, MadSkin}; diff --git a/implementations/rust/ockam/ockam_command/src/authority/create.rs b/implementations/rust/ockam/ockam_command/src/authority/create.rs index 5361d7d6aa2..d334b0d6c91 100644 --- a/implementations/rust/ockam/ockam_command/src/authority/create.rs +++ b/implementations/rust/ockam/ockam_command/src/authority/create.rs @@ -6,7 +6,7 @@ use crate::util::{embedded_node_that_is_not_stopped, exitcode}; use crate::{docs, identity, CommandGlobalOpts, Result}; use anyhow::anyhow; use clap::{ArgGroup, Args}; -use ockam::AsyncTryClone; +use ockam::identity::{AttributesEntry, IdentityIdentifier}; use ockam::Context; use ockam_api::bootstrapped_identities_store::PreTrustedIdentities; use ockam_api::nodes::authority_node; @@ -15,8 +15,6 @@ use ockam_api::nodes::models::transport::{CreateTransportJson, TransportMode, Tr use ockam_api::DefaultAddress; use ockam_core::compat::collections::HashMap; use ockam_core::compat::fmt; -use ockam_identity::authenticated_storage::AttributesEntry; -use ockam_identity::IdentityIdentifier; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::path::PathBuf; @@ -84,13 +82,9 @@ pub struct CreateCommand { /// Start an authority node by calling the `ockam` executable with the current command-line /// arguments -async fn spawn_background_node( - ctx: &Context, - opts: &CommandGlobalOpts, - cmd: &CreateCommand, -) -> crate::Result<()> { +async fn spawn_background_node(opts: &CommandGlobalOpts, cmd: &CreateCommand) -> crate::Result<()> { // Create node state, including the vault and identity if they don't exist - init_node_state(ctx, opts, &cmd.node_name, None, None).await?; + init_node_state(opts, &cmd.node_name, None, None).await?; // Construct the arguments list and re-execute the ockam // CLI in foreground mode to start the newly created node @@ -188,11 +182,11 @@ impl CreateCommand { /// Given a Context start a node in a new OS process async fn create_background_node( - ctx: Context, + _ctx: Context, (opts, cmd): (CommandGlobalOpts, CreateCommand), ) -> crate::Result<()> { // Spawn node in another, new process - spawn_background_node(&ctx, &opts, &cmd).await + spawn_background_node(&opts, &cmd).await } /// Start an authority node: @@ -213,17 +207,16 @@ async fn start_authority_node( .get_node_path(&command.node_name) .exists() { - init_node_state(&ctx, &options, &command.node_name, None, None).await?; + init_node_state(&options, &command.node_name, None, None).await?; }; // retrieve the authority identity if it has been created before // otherwise create a new one - let public_identity = match options.state.identities.default().ok() { - Some(state) => state.config.public_identity(), + let identity = match options.state.identities.default().ok() { + Some(state) => state.config.identity(), None => { let cmd = identity::CreateCommand::new("authority".into(), None); - cmd.create_identity(ctx.async_try_clone().await?, options.clone()) - .await? + cmd.create_identity(options.clone()).await? } }; @@ -258,14 +251,12 @@ async fn start_authority_node( )?), )?; - let trusted_identities = &command.trusted_identities( - &command.project_identifier.clone(), - public_identity.identifier(), - )?; + let trusted_identities = + &command.trusted_identities(&command.project_identifier.clone(), &identity.identifier())?; let configuration = authority_node::Configuration { - identity: public_identity, - storage_path: options.state.identities.authenticated_storage_path()?, + identity, + storage_path: options.state.identities.identities_repository_path()?, vault_path: options.state.vaults.default()?.vault_file_path()?, project_identifier: command.project_identifier, tcp_listener_address: command.tcp_listener_address.clone(), @@ -294,8 +285,8 @@ fn parse_trusted_identities(values: &str) -> Result { #[cfg(test)] mod tests { use super::*; + use ockam::identity::IdentityIdentifier; use ockam_core::compat::collections::HashMap; - use ockam_identity::IdentityIdentifier; use std::str::FromStr; #[test] diff --git a/implementations/rust/ockam/ockam_command/src/credential/issue.rs b/implementations/rust/ockam/ockam_command/src/credential/issue.rs index 9533accd2b6..fa24b70a6df 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/issue.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/issue.rs @@ -9,8 +9,8 @@ use crate::{ }; use anyhow::Context as _; use clap::Args; +use ockam::identity::{CredentialData, IdentityIdentifier}; use ockam::Context; -use ockam_identity::{credential::CredentialBuilder, IdentityIdentifier}; #[derive(Clone, Debug, Args)] pub struct IssueCommand { @@ -50,18 +50,21 @@ impl IssueCommand { } async fn run_impl( - ctx: Context, + _ctx: Context, (opts, cmd): (CommandGlobalOpts, IssueCommand), ) -> crate::Result<()> { - let attrs = cmd.attributes()?; - let cred_builder = CredentialBuilder::from_attributes(cmd.for_identity.clone(), attrs); - - let vault = opts.state.vaults.get(&cmd.vault)?.get().await?; + let vault = Arc::new(opts.state.vaults.get(&cmd.vault)?.get().await?); let ident_state = opts.state.identities.get(&cmd.as_identity)?; + let issuer = ident_state.get(vault.clone()).await?; + let identities = ident_state.make_identities(vault).await?; - let ident = ident_state.get(&ctx, Arc::new(vault)).await?; - - let credential = ident.issue_credential(cred_builder).await?; + let attrs = cmd.attributes()?; + let credential_data = + CredentialData::from_attributes(cmd.for_identity.clone(), issuer.identifier(), attrs)?; + let credential = identities + .credentials() + .issue_credential(&issuer, credential_data) + .await?; print_encodable(credential, &cmd.encode_format)?; diff --git a/implementations/rust/ockam/ockam_command/src/credential/list.rs b/implementations/rust/ockam/ockam_command/src/credential/list.rs index 4e840004897..057f8835c7a 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/list.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/list.rs @@ -20,13 +20,13 @@ impl ListCommand { } async fn run_impl( - ctx: Context, + _ctx: Context, (opts, cmd): (CommandGlobalOpts, ListCommand), ) -> crate::Result<()> { let cred_states = opts.state.credentials.list()?; for cred_state in cred_states { - display_credential(&opts, &ctx, &cred_state.name()?, &cmd.vault).await?; + display_credential(&opts, &cred_state.name()?, &cmd.vault).await?; } Ok(()) diff --git a/implementations/rust/ockam/ockam_command/src/credential/mod.rs b/implementations/rust/ockam/ockam_command/src/credential/mod.rs index 304cfee656a..b3443cb95e7 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/mod.rs @@ -10,12 +10,9 @@ use anyhow::anyhow; pub(crate) use get::GetCommand; pub(crate) use issue::IssueCommand; pub(crate) use list::ListCommand; -use ockam::Context; +use ockam::identity::credential::{Credential, CredentialData, Unverified}; +use ockam::identity::IdentityIdentifier; use ockam_core::compat::sync::Arc; -use ockam_identity::credential::Credential; -use ockam_identity::credential::CredentialData; -use ockam_identity::credential::Unverified; -use ockam_identity::IdentityIdentifier; pub(crate) use present::PresentCommand; pub(crate) use show::ShowCommand; pub(crate) use store::StoreCommand; @@ -69,7 +66,6 @@ pub async fn validate_encoded_cred( issuer: &IdentityIdentifier, vault: &str, opts: &CommandGlobalOpts, - ctx: &Context, ) -> Result<()> { let vault = Arc::new(opts.state.vaults.get(vault)?.get().await?); @@ -79,17 +75,19 @@ pub async fn validate_encoded_cred( }; let cred: Credential = minicbor::decode(&bytes)?; - let cred_data: CredentialData = minicbor::decode(cred.unverified_data())?; let ident_state = opts.state.identities.get_by_identifier(issuer)?; - let ident = ident_state.get(ctx, vault.clone()).await?; - - ident - .to_public() - .await? - .verify_credential(&cred, cred_data.unverified_subject(), vault) + let identity = ident_state.get(vault.clone()).await?; + let identities = ident_state.make_identities(vault.clone()).await?; + identities + .credentials() + .verify_credential( + cred_data.unverified_subject(), + vec![identity].as_slice(), + cred, + ) .await?; Ok(()) diff --git a/implementations/rust/ockam/ockam_command/src/credential/show.rs b/implementations/rust/ockam/ockam_command/src/credential/show.rs index 1bc37bd38c8..67d81878b18 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/show.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/show.rs @@ -1,7 +1,7 @@ use clap::{arg, Args}; use colorful::Colorful; +use ockam::identity::IdentityIdentifier; use ockam::Context; -use ockam_identity::IdentityIdentifier; use crate::{ credential::validate_encoded_cred, util::node_rpc, vault::default_vault_name, CommandGlobalOpts, @@ -23,35 +23,29 @@ impl ShowCommand { } async fn run_impl( - ctx: Context, + _ctx: Context, (opts, cmd): (CommandGlobalOpts, ShowCommand), ) -> crate::Result<()> { - display_credential(&opts, &ctx, &cmd.credential_name, &cmd.vault).await?; + display_credential(&opts, &cmd.credential_name, &cmd.vault).await?; Ok(()) } pub(crate) async fn display_credential( opts: &CommandGlobalOpts, - ctx: &Context, cred_name: &str, vault_name: &str, ) -> crate::Result<()> { let cred_config = opts.state.credentials.get(cred_name)?.config().await?; let issuer = IdentityIdentifier::try_from(cred_config.issuer.to_string())?; - let is_verified = match validate_encoded_cred( - &cred_config.encoded_credential, - &issuer, - vault_name, - opts, - ctx, - ) - .await - { - Ok(_) => "✔︎".light_green(), - Err(_) => "✕".light_red(), - }; + let is_verified = + match validate_encoded_cred(&cred_config.encoded_credential, &issuer, vault_name, opts) + .await + { + Ok(_) => "✔︎".light_green(), + Err(_) => "✕".light_red(), + }; let cred = cred_config.credential()?; println!("Credential: {cred_name} {is_verified}"); diff --git a/implementations/rust/ockam/ockam_command/src/credential/store.rs b/implementations/rust/ockam/ockam_command/src/credential/store.rs index 14a19968e7c..f8bbeb80600 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/store.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/store.rs @@ -8,9 +8,9 @@ use crate::{ use anyhow::anyhow; use clap::Args; +use ockam::identity::IdentityIdentifier; use ockam::Context; use ockam_api::cli_state::CredentialConfig; -use ockam_identity::IdentityIdentifier; #[derive(Clone, Debug, Args)] pub struct StoreCommand { diff --git a/implementations/rust/ockam/ockam_command/src/credential/verify.rs b/implementations/rust/ockam/ockam_command/src/credential/verify.rs index 08f35ead50c..0797a7b3839 100644 --- a/implementations/rust/ockam/ockam_command/src/credential/verify.rs +++ b/implementations/rust/ockam/ockam_command/src/credential/verify.rs @@ -7,7 +7,7 @@ use clap::Args; use colorful::Colorful; use ockam::Context; -use ockam_identity::IdentityIdentifier; +use ockam::identity::IdentityIdentifier; use super::validate_encoded_cred; @@ -33,7 +33,7 @@ impl VerifyCommand { } async fn run_impl( - ctx: Context, + _ctx: Context, (opts, cmd): (CommandGlobalOpts, VerifyCommand), ) -> crate::Result<()> { let cred_as_str = match (cmd.credential, cmd.credential_path) { @@ -42,7 +42,7 @@ async fn run_impl( _ => return Err(anyhow!("Credential or Credential Path argument must be provided").into()), }; - match validate_encoded_cred(&cred_as_str, &cmd.issuer, &cmd.vault, &opts, &ctx).await { + match validate_encoded_cred(&cred_as_str, &cmd.issuer, &cmd.vault, &opts).await { Ok(_) => { println!("{} Verified Credential", "✔︎".light_green()); } diff --git a/implementations/rust/ockam/ockam_command/src/enroll.rs b/implementations/rust/ockam/ockam_command/src/enroll.rs index 0ab8a779043..a96b0d62a2c 100644 --- a/implementations/rust/ockam/ockam_command/src/enroll.rs +++ b/implementations/rust/ockam/ockam_command/src/enroll.rs @@ -54,7 +54,7 @@ async fn run_impl(ctx: &Context, opts: CommandGlobalOpts, cmd: EnrollCommand) -> let cloud_opts = cmd.cloud_opts.clone(); let space = default_space(ctx, &opts, &cloud_opts, &node_name).await?; default_project(ctx, &opts, &cloud_opts, &node_name, &space).await?; - update_enrolled_identity(ctx, &opts, &node_name).await?; + update_enrolled_identity(&opts, &node_name).await?; delete_embedded_node(&opts, &node_name).await; Ok(()) @@ -361,18 +361,14 @@ impl Auth0Service { } } -async fn update_enrolled_identity( - ctx: &Context, - opts: &CommandGlobalOpts, - node_name: &str, -) -> Result<()> { +async fn update_enrolled_identity(opts: &CommandGlobalOpts, node_name: &str) -> Result<()> { let identities = opts.state.identities.list()?; let node_state = opts.state.nodes.get(node_name)?; - let node_identity = node_state.config.identity(ctx).await?; + let node_identity = node_state.config.default_identity().await?; for mut identity in identities { - if node_identity.identifier() == &identity.config.identifier { + if node_identity.identifier() == identity.config.identifier { identity.set_enrollment_status()?; } } diff --git a/implementations/rust/ockam/ockam_command/src/identity/create.rs b/implementations/rust/ockam/ockam_command/src/identity/create.rs index bca764a1e52..23c846343d3 100644 --- a/implementations/rust/ockam/ockam_command/src/identity/create.rs +++ b/implementations/rust/ockam/ockam_command/src/identity/create.rs @@ -1,10 +1,8 @@ use crate::util::node_rpc; use crate::{docs, CommandGlobalOpts}; use clap::Args; +use ockam::identity::Identity; use ockam::Context; -use ockam_api::cli_state::{self, VaultConfig}; -use ockam_core::compat::sync::Arc; -use ockam_identity::{Identity, PublicIdentity}; use rand::prelude::random; const LONG_ABOUT: &str = include_str!("./static/create/long_about.txt"); @@ -35,44 +33,21 @@ impl CreateCommand { } async fn run_impl( - ctx: Context, + _ctx: Context, (options, cmd): (CommandGlobalOpts, CreateCommand), ) -> crate::Result<()> { - cmd.create_identity(ctx, options).await.map(|_| ()) + cmd.create_identity(options).await.map(|_| ()) } - pub async fn create_identity( - &self, - ctx: Context, - options: CommandGlobalOpts, - ) -> crate::Result { - let vault_state = if let Some(vault_name) = self.vault.clone() { - options.state.vaults.get(&vault_name)? - } else if options.state.vaults.default().is_err() { - let vault_name = hex::encode(random::<[u8; 4]>()); - let state = options - .state - .vaults - .create(&vault_name, VaultConfig::default()) - .await?; - println!("Default vault created: {}", &vault_name); - state - } else { - options.state.vaults.default()? - }; - let vault = vault_state.get().await?; - let identity = Identity::create_ext( - &ctx, - options.state.identities.authenticated_storage().await?, - Arc::new(vault), - ) - .await?; - let identity_config = cli_state::IdentityConfig::new(&identity).await; + pub async fn create_identity(&self, options: CommandGlobalOpts) -> crate::Result { + let vault_state = options.state.create_vault_state(self.vault.clone()).await?; + let identity_state = options .state - .identities - .create(&self.name, identity_config)?; + .create_identity_state(Some(self.name.clone()), vault_state.get().await?) + .await?; + let identity = identity_state.config.identity(); println!("Identity created: {}", identity.identifier()); - Ok(identity_state.config.public_identity()) + Ok(identity) } } diff --git a/implementations/rust/ockam/ockam_command/src/identity/show.rs b/implementations/rust/ockam/ockam_command/src/identity/show.rs index e39671a31c8..13b9cd340b2 100644 --- a/implementations/rust/ockam/ockam_command/src/identity/show.rs +++ b/implementations/rust/ockam/ockam_command/src/identity/show.rs @@ -4,9 +4,9 @@ use crate::{docs, CommandGlobalOpts, EncodeFormat, Result}; use anyhow::anyhow; use clap::Args; use core::fmt::Write; +use ockam::identity::identity::IdentityChangeHistory; use ockam_api::cli_state::CliState; use ockam_api::nodes::models::identity::{LongIdentityResponse, ShortIdentityResponse}; -use ockam_identity::change_history::IdentityChangeHistory; const LONG_ABOUT: &str = include_str!("./static/show/long_about.txt"); const AFTER_LONG_HELP: &str = include_str!("./static/show/after_long_help.txt"); diff --git a/implementations/rust/ockam/ockam_command/src/message/send.rs b/implementations/rust/ockam/ockam_command/src/message/send.rs index 2e574a88743..50ff5519ad6 100644 --- a/implementations/rust/ockam/ockam_command/src/message/send.rs +++ b/implementations/rust/ockam/ockam_command/src/message/send.rs @@ -63,7 +63,7 @@ async fn rpc(mut ctx: Context, (opts, cmd): (CommandGlobalOpts, SendCommand)) -> ctx, opts, None, - cmd.cloud_opts.identity.as_ref(), + cmd.cloud_opts.identity.clone(), Some(&cmd.project_opts), ) .await?; diff --git a/implementations/rust/ockam/ockam_command/src/node/create.rs b/implementations/rust/ockam/ockam_command/src/node/create.rs index 0302317dd03..901bd0b7ced 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create.rs @@ -1,5 +1,5 @@ use clap::Args; -use ockam_identity::PublicIdentity; +use ockam::identity::IdentitiesCreation; use ockam_multiaddr::MultiAddr; use ockam_vault::Vault; use rand::prelude::random; @@ -194,7 +194,7 @@ async fn run_impl( let cmd = cmd.overwrite_addr()?; let addr = SocketAddr::from_str(&cmd.tcp_listener_address)?; - spawn_background_node(&ctx, &opts, &cmd, addr).await?; + spawn_background_node(&opts, &cmd, addr).await?; // Print node status let tcp = TcpTransport::create(&ctx).await?; @@ -228,20 +228,15 @@ async fn run_foreground_node( // This node was initially created as a foreground node // and there is no existing state for it yet. if !cmd.child_process && opts.state.nodes.get(&node_name).is_err() { - init_node_state( - &ctx, - &opts, - &node_name, - cmd.vault.as_ref(), - cmd.identity.as_ref(), - ) - .await?; + init_node_state(&opts, &node_name, cmd.vault.clone(), cmd.identity.clone()).await?; } if let Some(authority_identities) = &cmd.authority_identities { for auth in authority_identities.iter() { - let vault = Vault::create(); - let i = PublicIdentity::import(auth.identity(), vault).await?; + let i = IdentitiesCreation::new(Vault::create()) + .import_identity(auth.identity()) + .await?; + cfg.authorities(&node_name)? .add_authority(i.identifier().clone(), auth.clone())?; } @@ -475,7 +470,6 @@ async fn start_services( } async fn spawn_background_node( - ctx: &Context, opts: &CommandGlobalOpts, cmd: &CreateCommand, addr: SocketAddr, @@ -491,14 +485,7 @@ async fn spawn_background_node( let node_name = parse_node_name(&cmd.node_name)?; // Create node state, including the vault and identity if don't exist - init_node_state( - ctx, - opts, - &node_name, - cmd.vault.as_ref(), - cmd.identity.as_ref(), - ) - .await?; + init_node_state(opts, &node_name, cmd.vault.clone(), cmd.identity.clone()).await?; // Construct the arguments list and re-execute the ockam // CLI in foreground mode to start the newly created node @@ -552,12 +539,11 @@ async fn start_authority_node( // retrieve the authority identity if it has been created before // otherwise create a new one - let public_identity = match options.state.identities.default().ok() { - Some(state) => state.config.public_identity(), + let identity = match options.state.identities.default().ok() { + Some(state) => state.config.identity(), None => { let cmd = identity::CreateCommand::new("authority".into(), None); - cmd.create_identity(ctx.async_try_clone().await?, options.clone()) - .await? + cmd.create_identity(options.clone()).await? } }; @@ -566,8 +552,8 @@ async fn start_authority_node( .map_err(|e| crate::Error::new(exitcode::CONFIG, anyhow!("{e}")))?; let configuration = authority_node::Configuration { - identity: public_identity, - storage_path: options.state.identities.authenticated_storage_path()?, + identity, + storage_path: options.state.identities.identities_repository_path()?, vault_path: options.state.vaults.default()?.vault_file_path()?, project_identifier: authenticator_config.project, tcp_listener_address: command.tcp_listener_address.clone(), diff --git a/implementations/rust/ockam/ockam_command/src/node/util.rs b/implementations/rust/ockam/ockam_command/src/node/util.rs index b4f4fd92067..92039b8e587 100644 --- a/implementations/rust/ockam/ockam_command/src/node/util.rs +++ b/implementations/rust/ockam/ockam_command/src/node/util.rs @@ -1,23 +1,21 @@ use anyhow::{anyhow, Context as _}; use ockam_api::config::lookup::ProjectLookup; -use rand::random; use std::env::current_exe; use std::fs::OpenOptions; use std::path::{Path, PathBuf}; use std::process::Command; -use ockam::identity::{Identity, PublicIdentity}; +use ockam::identity::IdentitiesCreation; use ockam::{Context, TcpListenerTrustOptions, TcpTransport}; use ockam_api::cli_state; -use ockam_api::config::cli::{self, Authority}; +use ockam_api::config::cli::Authority; use ockam_api::nodes::models::transport::{TransportMode, TransportType}; use ockam_api::nodes::service::{ ApiTransport, NodeManagerGeneralOptions, NodeManagerProjectsOptions, NodeManagerTransportOptions, }; use ockam_api::nodes::{NodeManager, NodeManagerWorker, NODEMANAGER_ADDR}; -use ockam_core::compat::sync::Arc; use ockam_core::AllowAll; use ockam_multiaddr::MultiAddr; use ockam_vault::Vault; @@ -38,8 +36,8 @@ pub async fn start_embedded_node( pub async fn start_embedded_node_with_vault_and_identity( ctx: &Context, opts: &CommandGlobalOpts, - vault: Option<&String>, - identity: Option<&String>, + vault: Option, + identity: Option, project_opts: Option<&ProjectOpts>, ) -> Result { let cfg = &opts.config; @@ -47,7 +45,7 @@ pub async fn start_embedded_node_with_vault_and_identity( // This node was initially created as a foreground node if !cmd.child_process { - init_node_state(ctx, opts, &cmd.node_name, vault, identity).await?; + init_node_state(opts, &cmd.node_name, vault, identity).await?; } let project_id = if let Some(p) = project_opts { @@ -149,47 +147,19 @@ pub async fn add_project_info_to_node_state( None => Ok(None), } } + pub(crate) async fn init_node_state( - ctx: &Context, opts: &CommandGlobalOpts, node_name: &str, - vault: Option<&String>, - identity: Option<&String>, + vault_name: Option, + identity_name: Option, ) -> Result<()> { // Get vault specified in the argument, or get the default - let vault_state = if let Some(v) = vault { - opts.state.vaults.get(v)? - } - // Or get the default - else if let Ok(v) = opts.state.vaults.default() { - v - } else { - let n = hex::encode(random::<[u8; 4]>()); - let c = cli_state::VaultConfig::default(); - opts.state.vaults.create(&n, c).await? - }; - - // Get identity specified in the argument - let identity_state = if let Some(idt) = identity { - opts.state.identities.get(idt)? - } - // Or get the default - else if let Ok(idt) = opts.state.identities.default() { - idt - } else { - let vault = vault_state.get().await?; - let identity_name = hex::encode(random::<[u8; 4]>()); - let identity = Identity::create_ext( - ctx, - opts.state.identities.authenticated_storage().await?, - Arc::new(vault), - ) + let vault_state = opts.state.create_vault_state(vault_name).await?; + let identity_state = opts + .state + .create_identity_state(identity_name, vault_state.get().await?) .await?; - let identity_config = cli_state::IdentityConfig::new(&identity).await; - opts.state - .identities - .create(&identity_name, identity_config)? - }; // Create the node with the given vault and identity let node_config = cli_state::NodeConfigBuilder::default() @@ -207,10 +177,11 @@ pub(super) async fn add_project_authority( node: &str, cfg: &OckamConfig, ) -> Result<()> { - let i = PublicIdentity::import(&authority_identity, Vault::create()).await?; - let a = cli::Authority::new(authority_identity, authority_access_route); - cfg.authorities(node)? - .add_authority(i.identifier().clone(), a) + let i = IdentitiesCreation::new(Vault::create()) + .import_identity(&authority_identity) + .await?; + let a = Authority::new(authority_identity, authority_access_route); + cfg.authorities(node)?.add_authority(i.identifier(), a) } pub(super) async fn add_project_authority_from_project_info( diff --git a/implementations/rust/ockam/ockam_command/src/status.rs b/implementations/rust/ockam/ockam_command/src/status.rs index 84d428e9e0c..ad5a6c497bd 100644 --- a/implementations/rust/ockam/ockam_command/src/status.rs +++ b/implementations/rust/ockam/ockam_command/src/status.rs @@ -3,10 +3,10 @@ use crate::CommandGlobalOpts; use crate::Result; use anyhow::anyhow; use clap::Args; +use ockam::identity::Identity; use ockam::{Context, TcpTransport}; use ockam_api::cli_state::{IdentityState, NodeState}; use ockam_api::nodes::models::base::NodeStatus; -use ockam_identity::Identity; use std::time::Duration; /// Display Ockam Status @@ -43,7 +43,7 @@ async fn run_impl(ctx: &Context, opts: CommandGlobalOpts, cmd: StatusCommand) -> let tcp = TcpTransport::create(ctx).await?; for node_state in &node_states { let node_infos = NodeDetails { - identity: node_state.config.identity(ctx).await?, + identity: node_state.config.default_identity().await?, state: node_state.clone(), status: get_node_status(ctx, &opts, node_state, &tcp).await?, }; @@ -111,7 +111,7 @@ async fn print_status( println!("{:2}{}", "", line); } - node_details.retain(|nd| nd.identity.identifier() == &identity.config.identifier); + node_details.retain(|nd| nd.identity.identifier() == identity.config.identifier); if !node_details.is_empty() { println!("{:2}Linked Nodes:", ""); for (n_idx, node) in node_details.iter().enumerate() { diff --git a/implementations/rust/ockam/ockam_command/src/util/mod.rs b/implementations/rust/ockam/ockam_command/src/util/mod.rs index fec454f4e79..8205d33d4b0 100644 --- a/implementations/rust/ockam/ockam_command/src/util/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/util/mod.rs @@ -668,8 +668,6 @@ mod tests { use ockam_api::cli_state; use ockam_api::cli_state::{IdentityConfig, NodeConfig, VaultConfig}; use ockam_api::nodes::models::transport::{CreateTransportJson, TransportMode, TransportType}; - use ockam_core::compat::sync::Arc; - use ockam_identity::Identity; #[test] fn test_extract_address_value() { @@ -705,12 +703,13 @@ mod tests { let v_config = VaultConfig::default(); cli_state.vaults.create(&v_name, v_config).await?; let v = cli_state.vaults.get(&v_name)?.get().await?; - let idt = Identity::create_ext( - ctx, - cli_state.identities.authenticated_storage().await?, - Arc::new(v), - ) - .await?; + let idt = cli_state + .get_identities(v) + .await + .unwrap() + .identities_creation() + .create_identity() + .await?; let idt_config = IdentityConfig::new(&idt).await; cli_state .identities diff --git a/implementations/rust/ockam/ockam_command/src/util/orchestrator_api.rs b/implementations/rust/ockam/ockam_command/src/util/orchestrator_api.rs index cfe1b673ca7..a562e6f0533 100644 --- a/implementations/rust/ockam/ockam_command/src/util/orchestrator_api.rs +++ b/implementations/rust/ockam/ockam_command/src/util/orchestrator_api.rs @@ -79,7 +79,7 @@ impl<'a> OrchestratorApiBuilder<'a> { self.ctx, self.opts, None, - self.identity.as_ref(), + self.identity.clone(), Some(self.project_opts), ) .await?; diff --git a/implementations/rust/ockam/ockam_command/src/vault/attach_key.rs b/implementations/rust/ockam/ockam_command/src/vault/attach_key.rs index df2e3840178..f5728c3a02f 100644 --- a/implementations/rust/ockam/ockam_command/src/vault/attach_key.rs +++ b/implementations/rust/ockam/ockam_command/src/vault/attach_key.rs @@ -1,12 +1,11 @@ use anyhow::anyhow; use clap::Args; -use std::sync::Arc; use ockam::Context; use ockam_api::cli_state; use ockam_core::vault::{Secret, SecretAttributes, SecretPersistence, SecretType, SecretVault}; -use ockam_identity::{Identity, IdentityStateConst, KeyAttributes}; +use ockam_identity::{IdentityChangeConstants, KeyAttributes}; use crate::util::node_rpc; use crate::CommandGlobalOpts; @@ -28,34 +27,28 @@ impl AttachKeyCommand { } async fn rpc( - mut ctx: Context, + mut _ctx: Context, (opts, cmd): (CommandGlobalOpts, AttachKeyCommand), ) -> crate::Result<()> { - run_impl(&mut ctx, opts, cmd).await + run_impl(opts, cmd).await } -async fn run_impl( - ctx: &mut Context, - opts: CommandGlobalOpts, - cmd: AttachKeyCommand, -) -> crate::Result<()> { +async fn run_impl(opts: CommandGlobalOpts, cmd: AttachKeyCommand) -> crate::Result<()> { let v_state = opts.state.vaults.get(&cmd.vault)?; if !v_state.config.is_aws() { return Err(anyhow!("Vault {} is not an AWS KMS vault", cmd.vault).into()); } - let v = v_state.get().await?; + let vault = v_state.get().await?; let idt = { let attrs = SecretAttributes::new(SecretType::NistP256, SecretPersistence::Persistent, 32); - let kid = v.secret_import(Secret::Aws(cmd.key_id), attrs).await?; - let attrs = KeyAttributes::new(IdentityStateConst::ROOT_LABEL.to_string(), attrs); - Identity::create_with_external_key_ext( - ctx, - opts.state.identities.authenticated_storage().await?, - Arc::new(v), - &kid, - attrs, - ) - .await? + let kid = vault.secret_import(Secret::Aws(cmd.key_id), attrs).await?; + let attrs = KeyAttributes::new(IdentityChangeConstants::ROOT_LABEL.to_string(), attrs); + opts.state + .get_identities(vault) + .await? + .identities_creation() + .create_identity_with_external_key(&kid, attrs) + .await? }; let idt_name = cli_state::random_name(); let idt_config = cli_state::IdentityConfig::new(&idt).await; diff --git a/implementations/rust/ockam/ockam_core/CHANGELOG.md b/implementations/rust/ockam/ockam_core/CHANGELOG.md index d2ccb240fa4..7965d6bd451 100644 --- a/implementations/rust/ockam/ockam_core/CHANGELOG.md +++ b/implementations/rust/ockam/ockam_core/CHANGELOG.md @@ -671,4 +671,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Error` - an error type that can be returned is both `std` and `no_std` modes. - `Result` - a result type that can be returned is both `std` and `no_std` modes. - diff --git a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/02-abac-in-place.rs b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/02-abac-in-place.rs index 77d9c621fcd..da94bd5e89d 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/02-abac-in-place.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/02-abac-in-place.rs @@ -156,7 +156,7 @@ impl AbacAuthorization for SomeAbacAuthorizationImplementation { async fn mock_ockam_cloud(ctx: &Context) -> Result
{ // Create an identity for customer let customer_vault = Vault::create(); - let customer = Identity::create(ctx, &customer_vault).await?; + let customer = Identities::create(ctx, &customer_vault).await?; let customer_identity = customer.identifier()?; // Create some abac implementation @@ -183,7 +183,7 @@ async fn mock_ockam_cloud(ctx: &Context) -> Result
{ // Set up some identity secure channel for API service worker side let ockam_vault = Vault::create(); - let ockam = Identity::create(ctx, &ockam_vault).await?; + let ockam = Identities::create(ctx, &ockam_vault).await?; let ockam_storage = InMemoryStorage::new(); ockam .create_secure_channel_listener("ockam_listener", TrustEveryonePolicy, &ockam_storage) diff --git a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/03-abac-workers.rs b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/03-abac-workers.rs index ad6c27de79a..c494e9170da 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/03-abac-workers.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/03-abac-workers.rs @@ -96,7 +96,7 @@ impl Worker for EchoerServiceWorker { async fn mock_ockam_cloud(ctx: &Context) -> Result
{ // Create an identity for customer let customer_vault = Vault::create(); - let customer = Identity::create(ctx, &customer_vault).await?; + let customer = Identities::create(ctx, &customer_vault).await?; let customer_identity = customer.identifier()?; // Start some ABAC policy source @@ -133,7 +133,7 @@ async fn mock_ockam_cloud(ctx: &Context) -> Result
{ // Set up some identity secure channel for API service worker side let ockam_vault = Vault::create(); - let ockam = Identity::create(ctx, &ockam_vault).await?; + let ockam = Identities::create(ctx, &ockam_vault).await?; let ockam_storage = InMemoryStorage::new(); ockam .create_secure_channel_listener("ockam_listener", TrustEveryonePolicy, &ockam_storage) diff --git a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-inlet.rs b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-inlet.rs index 89917087421..4f51e295e43 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-inlet.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-inlet.rs @@ -19,7 +19,7 @@ async fn main(ctx: Context) -> Result<()> { // by a second command line argument. let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; + let e = Identities::create(&ctx, vault).await?; let outlet_port = std::env::args() .nth(2) .unwrap_or_else(|| "4000".to_string()); diff --git a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-outlet.rs b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-outlet.rs index 4663a3be220..ac9961a382b 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-outlet.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/access_control/examples/07-outlet.rs @@ -15,7 +15,7 @@ async fn main(ctx: Context) -> Result<()> { // that will wait for requests to start an Authenticated Key Exchange. let vault = Vault::create(); - let e = Identity::create(&ctx, vault).await?; + let e = Identities::create(&ctx, vault).await?; let storage = InMemoryStorage::new(); e.create_secure_channel_listener("secure_channel_listener", TrustEveryonePolicy, &storage) .await?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/hub_inlet/src/main.rs b/implementations/rust/ockam/ockam_examples/example_projects/hub_inlet/src/main.rs index 0ff458d25f0..844d9dddad2 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/hub_inlet/src/main.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/hub_inlet/src/main.rs @@ -188,7 +188,7 @@ async fn main(ctx: Context) -> Result<()> { let config = Config::new(); let vault = Vault::create(&ctx).await?; - let mut hub = Identity::create(&ctx, vault)?; + let mut hub = Identities::create(&ctx, vault)?; hub.create_secure_channel_listener("secure_channel_listener_service", TrustEveryonePolicy)?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/no_std/examples/hello.rs b/implementations/rust/ockam/ockam_examples/example_projects/no_std/examples/hello.rs index 8d485b223e1..4879b270f72 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/no_std/examples/hello.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/no_std/examples/hello.rs @@ -64,31 +64,27 @@ fn entry() -> ! { use ockam::{ identity::{Identity, TrustEveryonePolicy}, - route, - vault::Vault, - Context, Result, + route, Context, Result, }; #[ockam::node] async fn main(mut ctx: Context) -> Result<()> { - // Create a Vault to safely store secret keys for Alice and Bob. - let vault = Vault::create(); - - // Create an Identity to represent Bob. - let bob = Identity::create(&ctx, vault).await?; + let secure_channels = SecureChannels::create().await?; + let bob = secure_channels.identities().create_identity().await?; // Create a secure channel listener for Bob that will wait for requests to // initiate an Authenticated Key Exchange. - bob.create_secure_channel_listener("bob", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(&ctx, &bob, "bob", TrustEveryonePolicy) .await?; - // Create an Identity to represent Alice. - let alice = Identity::create(&ctx, vault).await?; + // Create an entity to represent Alice. + let alice = secure_channels.identities().create_identity().await?; // As Alice, connect to Bob's secure channel listener and perform an // Authenticated Key Exchange to establish an encrypted secure channel with Bob. - let channel = alice - .create_secure_channel("bob", TrustEveryonePolicy) + let channel = secure_channels + .create_secure_channel(&ctx, &alice, "bob", TrustEveryonePolicy) .await?; // Send a message, ** THROUGH ** the secure channel, diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/inlet_hub.rs b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/inlet_hub.rs index f725d345afd..c53bae97e40 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/inlet_hub.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/inlet_hub.rs @@ -65,7 +65,7 @@ impl Worker for InletCreatorWorker { #[ockam::node] async fn main(ctx: Context) -> Result<()> { let vault = Vault::create(&ctx).await?; - let mut hub = Identity::create(&ctx, vault)?; + let mut hub = Identities::create(&ctx, vault)?; hub.create_secure_channel_listener("secure_channel_listener", TrustEveryonePolicy)?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/main.rs b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/main.rs index 31382ece988..720432d66bb 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/main.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/main.rs @@ -6,7 +6,7 @@ async fn main(mut ctx: Context) -> Result<()> { let vault = Vault::create(&ctx).await?; // Create an Identity to represent this machine - let mut fabric_machine = Identity::create(&ctx, vault)?; + let mut fabric_machine = Identities::create(&ctx, vault)?; // Initialize the TCP Transport let tcp = TcpTransport::create(&ctx).await?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/outlet_hub.rs b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/outlet_hub.rs index 8fc858f5458..9f670025a79 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/outlet_hub.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/ports/src/bin/outlet_hub.rs @@ -3,7 +3,7 @@ use ockam::{route, Context, Identity, Result, TcpTransport, TrustEveryonePolicy, #[ockam::node] async fn main(mut ctx: Context) -> Result<()> { let vault = Vault::create(&ctx).await?; - let mut fabric_machine = Identity::create(&ctx, vault)?; + let mut fabric_machine = Identities::create(&ctx, vault)?; let tcp = TcpTransport::create(&ctx).await?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/Cargo.lock b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/Cargo.lock new file mode 100644 index 00000000000..9821c77d77f --- /dev/null +++ b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/Cargo.lock @@ -0,0 +1,1706 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", + "rand_core 0.6.4", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "serde", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "credential_example" +version = "0.1.0" +dependencies = [ + "ockam", + "ockam_core", + "ockam_vault", + "ssh-key", +] + +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", +] + +[[package]] +name = "der" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +dependencies = [ + "const-oid 0.9.2", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "ecdsa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644d3b8674a5fc5b929ae435bca85c2323d85ccb013a5509c2ac9ee11a6284ba" +dependencies = [ + "der 0.7.1", + "elliptic-curve", + "rfc6979", + "signature 2.0.0", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1 0.7.1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-io" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" + +[[package]] +name = "futures-macro" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", + "serde", +] + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minicbor" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7005aaf257a59ff4de471a9d5538ec868a21586534fff7f85dd97d4043a6139" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1154809406efdb7982841adb6311b3d095b46f78342dd646736122fe6b19e267" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "ockam" +version = "0.83.0" +dependencies = [ + "arrayref", + "dyn-clone", + "hex", + "minicbor", + "ockam_abac", + "ockam_core", + "ockam_identity", + "ockam_key_exchange_xx", + "ockam_macros", + "ockam_node", + "ockam_transport_tcp", + "ockam_vault", + "rand", + "serde", + "sha2 0.10.6", + "tracing", +] + +[[package]] +name = "ockam_abac" +version = "0.17.0" +dependencies = [ + "either", + "minicbor", + "ockam_core", + "ockam_identity", + "once_cell", + "regex", + "str-buf", + "tracing", + "wast", +] + +[[package]] +name = "ockam_core" +version = "0.77.0" +dependencies = [ + "async-trait", + "backtrace", + "cfg-if", + "core2", + "futures-util", + "hashbrown", + "heapless", + "hex", + "minicbor", + "ockam_macros", + "once_cell", + "rand", + "serde", + "serde_bare", + "subtle", + "tinyvec", + "tracing", + "tracing-error", + "zeroize", +] + +[[package]] +name = "ockam_executor" +version = "0.45.0" +dependencies = [ + "crossbeam-queue", + "futures", + "heapless", + "ockam_core", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "ockam_identity" +version = "0.71.0" +dependencies = [ + "async-trait", + "cfg-if", + "group", + "heapless", + "hex", + "minicbor", + "ockam_core", + "ockam_key_exchange_xx", + "ockam_macros", + "ockam_node", + "ockam_vault", + "rand", + "serde", + "serde-big-array", + "serde_bare", + "sha2 0.10.6", + "subtle", + "time", + "tracing", +] + +[[package]] +name = "ockam_key_exchange_xx" +version = "0.73.0" +dependencies = [ + "ockam_core", + "ockam_macros", +] + +[[package]] +name = "ockam_macros" +version = "0.27.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ockam_node" +version = "0.80.0" +dependencies = [ + "futures", + "minicbor", + "ockam_core", + "ockam_executor", + "ockam_macros", + "serde", + "serde_bare", + "tokio", + "tracing", + "tracing-error", + "tracing-subscriber", +] + +[[package]] +name = "ockam_transport_core" +version = "0.50.0" +dependencies = [ + "ockam_core", + "tracing", +] + +[[package]] +name = "ockam_transport_tcp" +version = "0.78.0" +dependencies = [ + "cfg-if", + "hashbrown", + "ockam_core", + "ockam_macros", + "ockam_node", + "ockam_transport_core", + "rand", + "serde", + "socket2 0.5.1", + "tokio", + "tracing", +] + +[[package]] +name = "ockam_vault" +version = "0.73.0" +dependencies = [ + "aes-gcm", + "arrayref", + "cfg-if", + "curve25519-dalek", + "ed25519-dalek", + "fs2", + "hex", + "hkdf", + "ockam_core", + "ockam_macros", + "ockam_node", + "p256", + "rand", + "serde", + "serde_json", + "sha2 0.10.6", + "tracing", + "x25519-dalek", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7270da3e5caa82afd3deb054cc237905853813aea3859544bc082c3fe55b8d47" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.6", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +dependencies = [ + "der 0.7.1", + "spki", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7613fdcc0831c10060fa69833ea8fa2caa94b6456f51e25356a885b530a2e3d0" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der 0.5.1", + "generic-array", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct", + "der 0.7.1", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bare" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c55386eed0f1ae957b091dc2ca8122f287b60c79c774cbe3d5f2b69fded660" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "serde_json" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8d618c6641ae355025c449427f9e96b98abf99a772be3cef6708d15c77147a" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +dependencies = [ + "base64ct", + "der 0.7.1", +] + +[[package]] +name = "ssh-key" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c85ed79c3ce171194a27b20afc7114a5ba0e416e95ed061701aa0c7941afd2" +dependencies = [ + "base64ct", + "sec1 0.2.1", + "zeroize", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "str-buf" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75b72ee54e2f93c3ea1354066162be893ee5e25773ab743de3e088cecbb4f31" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.4.9", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-encoder" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "55.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4984d3e1406571f4930ba5cf79bd70f75f41d0e87e17506e0bd19b0e5d085f05" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25588073e5216b50bca71d61cb8595cdb9745e87032a58c199730def2862c934" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.11", +] diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/alice.rs b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/alice.rs index 455d656ecae..f9c5f38c268 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/alice.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/alice.rs @@ -1,5 +1,5 @@ use credential_example::{BOB_LISTENER_ADDRESS, BOB_TCP_ADDRESS, ECHOER}; -use ockam::identity::{Identity, IdentityTrait, TrustEveryonePolicy}; +use ockam::identity::{secure_channels, Identity, IdentityTrait, TrustEveryonePolicy}; use ockam::vault::{SecretAttributes, SecretPersistence, SecretType, SecretVault, Vault}; use ockam::{route, Context, Result, TcpTransport, TCP}; use std::{env, fs}; @@ -8,8 +8,8 @@ use std::{env, fs}; async fn main(mut ctx: Context) -> Result<()> { let _tcp = TcpTransport::create(&ctx).await?; - let vault = Vault::create(); - let alice = Identity::create(&ctx, vault).await?; + let secure_channels = secure_channels(); + let alice = secure_channels.identities().create().await?; let secret_key_path = env::var("SECRET_KEY_PATH").unwrap(); let secret_key = fs::read_to_string(secret_key_path).unwrap(); @@ -31,8 +31,10 @@ async fn main(mut ctx: Context) -> Result<()> { alice.add_key("SSH".into(), &secret_key).await?; - let channel = alice + let channel = secure_channels .create_secure_channel( + &ctx, + &alice, route![(TCP, BOB_TCP_ADDRESS), BOB_LISTENER_ADDRESS], TrustEveryonePolicy, ) diff --git a/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/bob.rs b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/bob.rs index d4a7187d138..210fbb2695c 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/bob.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/ssh_credentials/src/bin/bob.rs @@ -1,8 +1,8 @@ use credential_example::{BOB_LISTENER_ADDRESS, BOB_TCP_ADDRESS, ECHOER}; use ockam::identity::access_control::IdentityAccessControlBuilder; -use ockam::identity::{Identity, TrustPublicKeyPolicy}; +use ockam::identity::{secure_channels, Identity, TrustPublicKeyPolicy}; use ockam::vault::{PublicKey, SecretType, Vault}; -use ockam::{Context, Result, Routed, TcpTransport, Worker}; +use ockam::{Context, Result, Routed, TcpTransport, Worker, WorkerBuilder}; use ockam_core::AsyncTryClone; use std::{env, fs}; @@ -23,14 +23,13 @@ impl Worker for Echoer { #[ockam::node] async fn main(ctx: Context) -> Result<()> { - let vault = Vault::create(); - let access_control = IdentityAccessControlBuilder::new_with_any_id(); WorkerBuilder::with_access_control(access_control, ECHOER, Echoer) - .start(ctx) + .start(&ctx) .await?; - let bob = Identity::create(&ctx, vault).await?; + let secure_channels = secure_channels(); + let bob = secure_channels.identities().create().await?; let public_key_path = env::var("PUBLIC_KEY_PATH").unwrap(); let public_key = fs::read_to_string(public_key_path).unwrap(); @@ -43,10 +42,10 @@ async fn main(ctx: Context) -> Result<()> { let public_key = PublicKey::new(public_key.as_ref().to_vec(), SecretType::Ed25519); - let trust_policy = - TrustPublicKeyPolicy::new(public_key, "SSH", bob.async_try_clone().await.unwrap()); + let trust_policy = TrustPublicKeyPolicy::new(public_key, "SSH", secure_channels.identities()); - bob.create_secure_channel_listener(BOB_LISTENER_ADDRESS, trust_policy) + secure_channels + .create_secure_channel_listener(&ctx, &bob, BOB_LISTENER_ADDRESS, trust_policy) .await?; let tcp = TcpTransport::create(&ctx).await?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/client.rs b/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/client.rs index 391d1842b6a..587d4f9248e 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/client.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/client.rs @@ -18,7 +18,7 @@ async fn main(mut ctx: Context) -> Result<()> { // create secure channel to the server let vault = Vault::create(&ctx).await?; - let mut me = Identity::create(&ctx, vault)?; + let mut me = Identities::create(&ctx, vault)?; let route = route![(TCP, "127.0.0.1:8000"), "secure_listener"]; let secure_channel = me.create_secure_channel(route, TrustEveryonePolicy)?; diff --git a/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/server.rs b/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/server.rs index 3d63cc7c913..ad3930cb0d5 100644 --- a/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/server.rs +++ b/implementations/rust/ockam/ockam_examples/example_projects/tunnel/src/bin/server.rs @@ -63,7 +63,7 @@ impl Worker for ConnectionBrokerWorker { async fn main(ctx: Context) -> Result<()> { // create a secure listening channel let vault = Vault::create(&ctx).await?; - let mut me = Identity::create(&ctx, vault)?; + let mut me = Identities::create(&ctx, vault)?; me.create_secure_channel_listener("secure_listener", TrustEveryonePolicy)?; // start listening over TCP and start worker diff --git a/implementations/rust/ockam/ockam_identity/CHANGELOG.md b/implementations/rust/ockam/ockam_identity/CHANGELOG.md index 7ba2576ff56..a1f38263bef 100644 --- a/implementations/rust/ockam/ockam_identity/CHANGELOG.md +++ b/implementations/rust/ockam/ockam_identity/CHANGELOG.md @@ -440,7 +440,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Create worker builder for cleaner worker access control initialisation - Identity updates -- `AuthenticatedTable` -> `AuthenticatedStorage` +- `AuthenticatedTable` -> `AttributesStorage` ## 0.49.0 - 2022-06-14 @@ -824,4 +824,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## v0.1.0 - 2021-05-17 - Initial release. - diff --git a/implementations/rust/ockam/ockam_identity/src/authenticated_storage.rs b/implementations/rust/ockam/ockam_identity/src/authenticated_storage.rs deleted file mode 100644 index dce7a6ae5fb..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/authenticated_storage.rs +++ /dev/null @@ -1,226 +0,0 @@ -use crate::alloc::borrow::ToOwned; -use crate::alloc::string::ToString; -use crate::credential::Timestamp; -use crate::{IdentityIdentifier, IdentityStateConst}; -use minicbor::{Decode, Encode}; -use ockam_core::async_trait; -use ockam_core::compat::sync::Arc; -use ockam_core::compat::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; -use ockam_core::errcode::{Kind, Origin}; -use ockam_core::Result; -use serde::{Deserialize, Serialize}; - -/// Storage for Authenticated data -#[async_trait] -pub trait AuthenticatedStorage: Send + Sync + 'static { - /// Get entry - async fn get(&self, id: &str, key: &str) -> Result>>; - - /// Set entry - async fn set(&self, id: &str, key: String, val: Vec) -> Result<()>; - - /// Delete entry - async fn del(&self, id: &str, key: &str) -> Result<()>; - - /// List all keys of a given "type". TODO: we shouldn't store different things on a single - /// store. - async fn keys(&self, namespace: &str) -> Result>; -} - -/// An entry on the AuthenticatedIdentities table. -#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, Serialize, Deserialize)] -#[rustfmt::skip] -#[cbor(map)] -pub struct AttributesEntry { - #[b(1)] attrs: BTreeMap>, - #[n(2)] added: Timestamp, - #[n(3)] expires: Option, - #[n(4)] attested_by: Option, -} - -impl AttributesEntry { - //TODO: since we are converting from HashMap to BTreeMap in different parts, - // it will make sense to have a constructor here taking a HashMap and doing - // the conversion here. Better: standarize on either of the above for attributes. - - /// Constructor - pub fn new( - attrs: BTreeMap>, - added: Timestamp, - expires: Option, - attested_by: Option, - ) -> Self { - Self { - attrs, - added, - expires, - attested_by, - } - } - - /// The entry attributes - pub fn attrs(&self) -> &BTreeMap> { - &self.attrs - } - - /// Expiration time for this entry - pub fn expires(&self) -> Option { - self.expires - } - - /// Date that the entry was added - pub fn added(&self) -> Timestamp { - self.added - } - /// Who attested this attributes for this identity identifier - pub fn attested_by(&self) -> Option { - self.attested_by.to_owned() - } -} - -/// Trait implementing read access to an AuthenticatedIdentities table -#[async_trait] -pub trait IdentityAttributeStorageReader: Send + Sync + 'static { - /// Get the attributes associated with the given identity identifier - async fn get_attributes( - &self, - identity: &IdentityIdentifier, - ) -> Result>; - - /// List all identities with their attributes - async fn list(&self) -> Result>; -} - -/// Trait implementing write access to an AuthenticatedIdentities table -#[async_trait] -pub trait IdentityAttributeStorageWriter: Send + Sync + 'static { - /// Set the attributes associated with the given identity identifier. - /// Previous values gets overridden. - async fn put_attributes( - &self, - identity: &IdentityIdentifier, - entry: AttributesEntry, - ) -> Result<()>; - - /// Remove all attributes for a given identity identifier - async fn delete(&self, identity: &IdentityIdentifier) -> Result<()>; -} - -/// Trait implementing read/write access to an AuthenticatedIdentities table -#[async_trait] -pub trait IdentityAttributeStorage: - IdentityAttributeStorageReader + IdentityAttributeStorageWriter -{ - /// Return this storage as a read storage - fn as_identity_attribute_storage_reader(&self) -> Arc; - - /// Return this storage as a write storage - fn as_identity_attribute_storage_writer(&self) -> Arc; -} - -/// Implementation of `IdentityAttributeStorage` trait based on an underling -/// `AuthenticatedStorage` store. -#[derive(Clone)] -pub struct AuthenticatedAttributeStorage { - storage: Arc, -} - -impl AuthenticatedAttributeStorage { - /// Constructor. `AttributesEntry` entries are serialized and stored on the underling - /// storage given. - pub fn new(storage: Arc) -> Self { - Self { storage } - } -} - -impl IdentityAttributeStorage for AuthenticatedAttributeStorage { - fn as_identity_attribute_storage_reader(&self) -> Arc { - Arc::new(self.clone()) - } - - fn as_identity_attribute_storage_writer(&self) -> Arc { - Arc::new(self.clone()) - } -} - -#[async_trait] -impl IdentityAttributeStorageReader for AuthenticatedAttributeStorage { - async fn list(&self) -> Result> { - let mut l = Vec::new(); - for id in self - .storage - .keys(IdentityStateConst::ATTRIBUTES_KEY) - .await? - { - let identity_identifier = IdentityIdentifier::try_from(id)?; - if let Some(attrs) = self.get_attributes(&identity_identifier).await? { - l.push((identity_identifier, attrs)) - } - } - Ok(l) - } - - async fn get_attributes( - &self, - identity_id: &IdentityIdentifier, - ) -> Result> { - let id = identity_id.to_string(); - let entry = match self - .storage - .get(&id, IdentityStateConst::ATTRIBUTES_KEY) - .await? - { - Some(e) => e, - None => return Ok(None), - }; - - let entry: AttributesEntry = minicbor::decode(&entry)?; - - let now = Timestamp::now().ok_or_else(|| { - ockam_core::Error::new(Origin::Core, Kind::Internal, "invalid system time") - })?; - match entry.expires() { - Some(exp) if exp <= now => { - self.storage - .del(&id, IdentityStateConst::ATTRIBUTES_KEY) - .await?; - Ok(None) - } - _ => Ok(Some(entry)), - } - } -} - -#[async_trait] -impl IdentityAttributeStorageWriter for AuthenticatedAttributeStorage { - async fn put_attributes( - &self, - sender: &IdentityIdentifier, - entry: AttributesEntry, - ) -> Result<()> { - // TODO: Implement expiration mechanism in Storage - let entry = minicbor::to_vec(&entry)?; - - self.storage - .set( - &sender.to_string(), - IdentityStateConst::ATTRIBUTES_KEY.to_string(), - entry, - ) - .await?; - - Ok(()) - } - - async fn delete(&self, identity: &IdentityIdentifier) -> Result<()> { - self.storage - .del( - identity.to_string().as_str(), - IdentityStateConst::ATTRIBUTES_KEY, - ) - .await - } -} - -/// In-memory impl -pub mod mem; diff --git a/implementations/rust/ockam/ockam_identity/src/change/create_key.rs b/implementations/rust/ockam/ockam_identity/src/change/create_key.rs deleted file mode 100644 index f2989566173..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/change/create_key.rs +++ /dev/null @@ -1,156 +0,0 @@ -use crate::change::{IdentityChange, IdentitySignedChange, Signature, SignatureType}; -use crate::change_history::IdentityChangeHistory; -use crate::IdentityError::InvalidInternalState; -use crate::{ - to_hasher, to_secret_vault, ChangeIdentifier, Identity, IdentityError, IdentityVault, - KeyAttributes, -}; -use core::fmt; -use ockam_core::compat::sync::Arc; -use ockam_core::vault::{KeyId, PublicKey, SecretVault}; -use ockam_core::{Encodable, Result}; -use serde::{Deserialize, Serialize}; - -/// Key change data creation -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CreateKeyChangeData { - prev_change_id: ChangeIdentifier, - key_attributes: KeyAttributes, - public_key: PublicKey, -} - -impl CreateKeyChangeData { - /// Return key attributes - pub fn key_attributes(&self) -> &KeyAttributes { - &self.key_attributes - } - /// Return public key - pub fn public_key(&self) -> &PublicKey { - &self.public_key - } - /// Previous change identifier, used to create a chain - pub fn prev_change_id(&self) -> &ChangeIdentifier { - &self.prev_change_id - } -} - -impl CreateKeyChangeData { - /// Create new CreateKeyChangeData - pub fn new( - prev_change_id: ChangeIdentifier, - key_attributes: KeyAttributes, - public_key: PublicKey, - ) -> Self { - Self { - prev_change_id, - key_attributes, - public_key, - } - } -} - -impl fmt::Display for CreateKeyChangeData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "prev_change_id:{} key attibutes:{} public key:{}", - self.prev_change_id(), - self.key_attributes(), - self.public_key() - ) - } -} - -impl Identity { - async fn generate_key_if_needed( - secret: Option<&KeyId>, - key_attributes: &KeyAttributes, - vault: &Arc, - ) -> Result { - if let Some(s) = secret { - Ok(s.clone()) - } else { - vault - .secret_generate(key_attributes.secret_attributes()) - .await - } - } - - /// Create a new key - pub(crate) async fn make_create_key_change_static( - secret: Option<&KeyId>, - prev_id: ChangeIdentifier, - key_attributes: KeyAttributes, - root_key: Option<&KeyId>, - vault: Arc, - ) -> Result { - let secret_key = - Self::generate_key_if_needed(secret, &key_attributes, &to_secret_vault(vault.clone())) - .await?; - - let public_key = vault.secret_public_key_get(&secret_key).await?; - - let data = CreateKeyChangeData::new(prev_id, key_attributes, public_key); - - let change_block = IdentityChange::CreateKey(data); - let change_block_binary = change_block - .encode() - .map_err(|_| IdentityError::BareError)?; - - let change_id = vault.sha256(&change_block_binary).await?; - let change_id = ChangeIdentifier::from_hash(change_id); - - let self_signature = vault.sign(&secret_key, change_id.as_ref()).await?; - let self_signature = Signature::new(SignatureType::SelfSign, self_signature); - - let mut signatures = vec![self_signature]; - - // If we have root_key passed we should sign using it - // If there is no root_key - we're creating new identity, so we just generated root_key - if let Some(root_key) = root_key { - let root_signature = vault.sign(root_key, change_id.as_ref()).await?; - let root_signature = Signature::new(SignatureType::RootSign, root_signature); - - signatures.push(root_signature); - } - - let signed_change = IdentitySignedChange::new(change_id, change_block, signatures); - - Ok(signed_change) - } - - /// Create a new key - pub(crate) async fn make_create_key_change( - &self, - secret: Option<&KeyId>, - key_attributes: KeyAttributes, - ) -> Result { - let change_history = self.change_history.read().await; - // Creating key after it was revoked is forbidden - if IdentityChangeHistory::find_last_key_change( - change_history.as_ref(), - key_attributes.label(), - ) - .is_ok() - { - return Err(InvalidInternalState.into()); - } - - let prev_id = match change_history.get_last_change_id() { - Ok(prev_id) => prev_id, - Err(_) => ChangeIdentifier::initial(to_hasher(self.vault.clone())).await, - }; - - let root_secret = self.get_root_secret_key().await?; - let root_key = Some(&root_secret); - - Self::make_create_key_change_static( - secret, - prev_id, - key_attributes, - root_key, - self.vault.clone(), - ) - .await - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/change/rotate_key.rs b/implementations/rust/ockam/ockam_identity/src/change/rotate_key.rs deleted file mode 100644 index 0f745f00eea..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/change/rotate_key.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::change::{IdentityChange, IdentitySignedChange, Signature, SignatureType}; -use crate::change_history::IdentityChangeHistory; -use crate::{ChangeIdentifier, Identity, IdentityError, KeyAttributes}; -use core::fmt; -use ockam_core::vault::PublicKey; -use ockam_core::{Encodable, Result}; -use serde::{Deserialize, Serialize}; - -/// RotateKeyChangeData -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RotateKeyChangeData { - prev_change_id: ChangeIdentifier, - key_attributes: KeyAttributes, - public_key: PublicKey, -} - -impl RotateKeyChangeData { - /// Return key attributes - pub fn key_attributes(&self) -> &KeyAttributes { - &self.key_attributes - } - /// Return public key - pub fn public_key(&self) -> &PublicKey { - &self.public_key - } - /// Previous change identifier, used to create a chain - pub fn prev_change_id(&self) -> &ChangeIdentifier { - &self.prev_change_id - } -} - -impl RotateKeyChangeData { - /// Create RotateKeyChangeData - pub fn new( - prev_change_id: ChangeIdentifier, - key_attributes: KeyAttributes, - public_key: PublicKey, - ) -> Self { - Self { - prev_change_id, - key_attributes, - public_key, - } - } -} - -impl fmt::Display for RotateKeyChangeData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "prev_change_id:{} key attibutes:{} public key:{}", - self.prev_change_id(), - self.key_attributes(), - self.public_key() - ) - } -} - -impl Identity { - /// Rotate key change - pub(crate) async fn make_rotate_key_change( - &self, - key_attributes: KeyAttributes, - ) -> Result { - let change_history = self.change_history.read().await; - let prev_change_id = change_history.get_last_change_id()?; - - let last_change_in_chain = IdentityChangeHistory::find_last_key_change( - change_history.as_ref(), - key_attributes.label(), - )? - .clone(); - - let last_key_in_chain = self - .get_secret_key_from_change(&last_change_in_chain) - .await?; - - let secret_attributes = key_attributes.secret_attributes(); - - let secret_key = self.vault.secret_generate(secret_attributes).await?; - let public_key = self.vault.secret_public_key_get(&secret_key).await?; - - let data = RotateKeyChangeData::new(prev_change_id, key_attributes, public_key); - - let change_block = IdentityChange::RotateKey(data); - let change_block_binary = change_block - .encode() - .map_err(|_| IdentityError::BareError)?; - - let change_id = self.vault.sha256(&change_block_binary).await?; - let change_id = ChangeIdentifier::from_hash(change_id); - - let self_signature = self.vault.sign(&secret_key, change_id.as_ref()).await?; - let self_signature = Signature::new(SignatureType::SelfSign, self_signature); - - let root_key = self.get_root_secret_key().await?; - - let root_signature = self.vault.sign(&root_key, change_id.as_ref()).await?; - let root_signature = Signature::new(SignatureType::RootSign, root_signature); - - let prev_signature = self - .vault - .sign(&last_key_in_chain, change_id.as_ref()) - .await?; - let prev_signature = Signature::new(SignatureType::PrevSign, prev_signature); - - let signed_change = IdentitySignedChange::new( - change_id, - change_block, - vec![self_signature, root_signature, prev_signature], - ); - - Ok(signed_change) - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/channel/access_control.rs b/implementations/rust/ockam/ockam_identity/src/channel/access_control.rs deleted file mode 100644 index 1da0451cb6e..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/channel/access_control.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod identity_access_control; -pub use identity_access_control::*; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/common.rs b/implementations/rust/ockam/ockam_identity/src/channel/common.rs deleted file mode 100644 index 1ddcc31ebed..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/channel/common.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::IdentityVault; -use ockam_core::async_trait; -use ockam_core::compat::boxed::Box; -use ockam_core::compat::sync::Arc; -use ockam_core::compat::vec::Vec; -use ockam_core::vault::{ - AsymmetricVault, Buffer, Hasher, KeyId, PublicKey, Secret, SecretAttributes, SecretVault, - SmallBuffer, SymmetricVault, -}; -use ockam_core::Message; -use ockam_core::{KeyExchanger, NewKeyExchanger, Result}; -use ockam_key_exchange_xx::XXVault; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Message)] -pub(crate) struct AuthenticationConfirmation; - -#[derive(Clone)] -pub(crate) enum Role { - Initiator, - Responder, -} - -impl Role { - pub fn is_initiator(&self) -> bool { - match self { - Role::Initiator => true, - Role::Responder => false, - } - } - - pub fn str(&self) -> &'static str { - match self { - Role::Initiator => "initiator", - Role::Responder => "responder", - } - } -} - -/// Vault with XX required functionality -pub trait SecureChannelVault: SymmetricVault + XXVault + Send + Sync + 'static {} - -impl SecureChannelVault for D where D: SymmetricVault + XXVault + Send + Sync + 'static {} - -/// KeyExchanger with extra constraints -pub trait SecureChannelKeyExchanger: KeyExchanger + Send + Sync + 'static {} - -impl SecureChannelKeyExchanger for D where D: KeyExchanger + Send + Sync + 'static {} - -/// NewKeyExchanger with extra constraints -pub trait SecureChannelNewKeyExchanger: NewKeyExchanger + Send + Sync + 'static {} - -impl SecureChannelNewKeyExchanger for D where D: NewKeyExchanger + Send + Sync + 'static {} - -/// SecureChannelListener message wrapper. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Message)] -pub struct CreateResponderChannelMessage { - payload: Vec, - custom_payload: Option>, -} - -impl CreateResponderChannelMessage { - /// Channel information. - pub fn payload(&self) -> &[u8] { - &self.payload - } - /// Callback Address - pub fn custom_payload(&self) -> &Option> { - &self.custom_payload - } -} - -impl CreateResponderChannelMessage { - /// Create message using payload and callback_address - pub fn new(payload: Vec, custom_payload: Option>) -> Self { - CreateResponderChannelMessage { - payload, - custom_payload, - } - } -} - -/// This struct is used to compensate for the lack of non-experimental trait upcasting in Rust -/// We encapsulate an IdentityVault and delegate the implementation of all the functions of -/// the various traits inherited by IdentityVault: SymmetricVault, SecretVault, etc... -struct CoercedIdentityVault { - vault: Arc, -} - -#[async_trait] -impl SymmetricVault for CoercedIdentityVault { - async fn aead_aes_gcm_encrypt( - &self, - key_id: &KeyId, - plaintext: &[u8], - nonce: &[u8], - aad: &[u8], - ) -> Result> { - self.vault - .aead_aes_gcm_encrypt(key_id, plaintext, nonce, aad) - .await - } - - async fn aead_aes_gcm_decrypt( - &self, - key_id: &KeyId, - cipher_text: &[u8], - nonce: &[u8], - aad: &[u8], - ) -> Result> { - self.vault - .aead_aes_gcm_decrypt(key_id, cipher_text, nonce, aad) - .await - } -} - -#[async_trait] -impl SecretVault for CoercedIdentityVault { - async fn secret_generate(&self, attributes: SecretAttributes) -> Result { - self.vault.secret_generate(attributes).await - } - - async fn secret_import(&self, secret: Secret, attributes: SecretAttributes) -> Result { - self.vault.secret_import(secret, attributes).await - } - - async fn secret_export(&self, key_id: &KeyId) -> Result { - self.vault.secret_export(key_id).await - } - - async fn secret_attributes_get(&self, key_id: &KeyId) -> Result { - self.vault.secret_attributes_get(key_id).await - } - - async fn secret_public_key_get(&self, key_id: &KeyId) -> Result { - self.vault.secret_public_key_get(key_id).await - } - - async fn secret_destroy(&self, key_id: KeyId) -> Result<()> { - self.vault.secret_destroy(key_id).await - } -} - -#[async_trait] -impl Hasher for CoercedIdentityVault { - async fn sha256(&self, data: &[u8]) -> Result<[u8; 32]> { - self.vault.sha256(data).await - } - - async fn hkdf_sha256( - &self, - salt: &KeyId, - info: &[u8], - ikm: Option<&KeyId>, - output_attributes: SmallBuffer, - ) -> Result> { - self.vault - .hkdf_sha256(salt, info, ikm, output_attributes) - .await - } -} - -#[async_trait] -impl AsymmetricVault for CoercedIdentityVault { - async fn ec_diffie_hellman( - &self, - secret: &KeyId, - peer_public_key: &PublicKey, - ) -> Result { - self.vault.ec_diffie_hellman(secret, peer_public_key).await - } - - async fn compute_key_id_for_public_key(&self, public_key: &PublicKey) -> Result { - self.vault.compute_key_id_for_public_key(public_key).await - } -} - -/// Return this vault as a symmetric vault -pub fn to_symmetric_vault(vault: Arc) -> Arc { - Arc::new(CoercedIdentityVault { - vault: vault.clone(), - }) -} - -/// Return this vault as a XX vault -pub fn to_xx_vault(vault: Arc) -> Arc { - Arc::new(CoercedIdentityVault { - vault: vault.clone(), - }) -} - -/// Return this vault as a secret vault -pub fn to_secret_vault(vault: Arc) -> Arc { - Arc::new(CoercedIdentityVault { - vault: vault.clone(), - }) -} - -/// Return this vault as a hasher -pub fn to_hasher(vault: Arc) -> Arc { - Arc::new(CoercedIdentityVault { - vault: vault.clone(), - }) -} diff --git a/implementations/rust/ockam/ockam_identity/src/credential.rs b/implementations/rust/ockam/ockam_identity/src/credential.rs deleted file mode 100644 index 8a5f69d94b5..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/credential.rs +++ /dev/null @@ -1,509 +0,0 @@ -#![allow(missing_docs)] - -mod identity; -mod public_identity; -mod worker; - -pub mod access_control; -pub mod one_time_code; - -use ockam_core::compat::collections::HashMap; -pub use one_time_code::*; - -use crate::IdentityIdentifier; -use core::fmt; -use core::marker::PhantomData; -use core::time::Duration; -use minicbor::bytes::ByteVec; -use minicbor::{Decode, Encode}; -use ockam_core::compat::{collections::BTreeMap, string::String, vec::Vec}; -use ockam_core::Result; -use serde::de::Error; -use serde::{Deserialize, Deserializer}; -use serde::{Serialize, Serializer}; -#[cfg(feature = "std")] -use time::format_description::well_known::iso8601::{Iso8601, TimePrecision}; -#[cfg(feature = "std")] -use time::{Error::Format, OffsetDateTime}; - -#[cfg(feature = "std")] -use std::ops::Deref; - -#[cfg(feature = "tag")] -use crate::TypeTag; - -pub const MAX_CREDENTIAL_VALIDITY: Duration = Duration::from_secs(30 * 24 * 3600); - -/// Type to represent data of verified credential. -#[derive(Debug, Encode)] -pub enum Verified {} - -/// Type to represent data of unverified credential. -#[derive(Debug, Decode)] -pub enum Unverified {} - -#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] -#[rustfmt::skip] -#[cbor(map)] -pub struct Credential { - #[cfg(feature = "tag")] - #[n(0)] tag: TypeTag<3796735>, - /// CBOR-encoded [`CredentialData`]. - #[cbor(with = "minicbor::bytes")] - #[b(1)] data: Vec, - /// Cryptographic signature of attributes data. - #[cbor(with = "minicbor::bytes")] - #[b(2)] signature: Vec, -} - -impl fmt::Display for Credential { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data = CredentialData::::try_from(self) - .map_err(|_| fmt::Error)? - .into_verified(); - write!(f, "{}", data)?; - writeln!(f, "Signature: {}", hex::encode(self.signature.deref())) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Credential {{ ... }}") - } -} - -impl fmt::Display for CredentialData { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use time::format_description::well_known::iso8601; - - if let Some(schema_id) = self.schema { - writeln!(f, "Schema: {schema_id}")?; - } - writeln!(f, "Subject: {}", self.subject)?; - writeln!(f, "Issuer: {} ({})", self.issuer, self.issuer_key_label)?; - - let human_readable_time = - |time: Timestamp| match OffsetDateTime::from_unix_timestamp(u64::from(time) as i64) { - Ok(time) => { - match time.format( - &Iso8601::< - { - iso8601::Config::DEFAULT - .set_time_precision(TimePrecision::Second { - decimal_digits: None, - }) - .encode() - }, - >, - ) { - Ok(now_iso) => now_iso, - Err(_) => Format(time::error::Format::InvalidComponent("timestamp error")) - .to_string(), - } - } - Err(_) => Format(time::error::Format::InvalidComponent( - "unix time is invalid", - )) - .to_string(), - }; - writeln!(f, "Created: {}", human_readable_time(self.created))?; - writeln!(f, "Expires: {}", human_readable_time(self.expires))?; - write!(f, "Attributes: ")?; - f.debug_map() - .entries( - self.attributes - .iter() - .map(|(k, v)| (k, std::str::from_utf8(v).unwrap_or("**binary**"))), - ) - .finish()?; - writeln!(f) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "{:?}", self) - } -} - -#[derive(Debug, Decode, Encode)] -#[rustfmt::skip] -#[cbor(map)] -pub struct CredentialData { - /// A schema identifier to allow distinguishing sets of attributes. - #[n(1)] schema: Option, - /// User-defined key-value pairs. - #[b(2)] attributes: Attributes, - /// The subject this credential is issued for. - #[n(3)] subject: IdentityIdentifier, - /// The entity that signed this credential. - #[n(4)] issuer: IdentityIdentifier, - /// The label of the issuer's public key. - #[b(5)] issuer_key_label: String, - /// The time when this credential was created. - #[n(6)] created: Timestamp, - /// The time this credential expires. - #[n(7)] expires: Timestamp, - /// Term to represent the verification status type. - #[n(8)] status: Option> -} - -impl CredentialData { - pub(crate) fn into_verified(self) -> CredentialData { - CredentialData { - schema: self.schema, - attributes: self.attributes, - subject: self.subject, - issuer: self.issuer, - issuer_key_label: self.issuer_key_label, - created: self.created, - expires: self.expires, - status: None::>, - } - } -} - -impl Credential { - pub fn builder(subject: IdentityIdentifier) -> CredentialBuilder { - CredentialBuilder { - schema: None, - subject, - attrs: Attributes::new(), - validity: MAX_CREDENTIAL_VALIDITY, - } - } - - pub fn signature(&self) -> &[u8] { - &self.signature - } - - pub fn unverified_data(&self) -> &[u8] { - &self.data - } - - fn new(data: Vec, signature: Vec) -> Self { - Credential { - #[cfg(feature = "tag")] - tag: TypeTag, - data, - signature, - } - } -} - -impl CredentialData { - pub fn schema(&self) -> Option { - self.schema - } - - pub fn subject(&self) -> &IdentityIdentifier { - &self.subject - } - - pub fn issuer(&self) -> &IdentityIdentifier { - &self.issuer - } - - pub fn issuer_key_label(&self) -> &str { - &self.issuer_key_label - } - - pub fn created_at(&self) -> Timestamp { - self.created - } - - pub fn expires_at(&self) -> Timestamp { - self.expires - } - - pub fn attributes(&self) -> &Attributes { - &self.attributes - } - - pub fn into_attributes(self) -> Attributes { - self.attributes - } -} - -impl CredentialData { - pub fn unverified_issuer(&self) -> &IdentityIdentifier { - &self.issuer - } - pub fn unverified_key_label(&self) -> &str { - &self.issuer_key_label - } - pub fn unverified_subject(&self) -> &IdentityIdentifier { - &self.subject - } -} - -impl TryFrom<&Credential> for CredentialData { - type Error = minicbor::decode::Error; - - fn try_from(value: &Credential) -> Result { - minicbor::decode(value.clone().data.as_slice()) - } -} - -/// User-defined key-value pairs. -#[derive(Debug, Clone, Default, Encode, Decode, PartialEq, Eq)] -#[rustfmt::skip] -#[cbor(map)] -pub struct Attributes { - #[cfg(feature = "tag")] - #[n(0)] tag: TypeTag<4724285>, - - #[b(1)] attrs: BTreeMap -} - -impl Attributes { - /// Create a new empty attribute set. - pub fn new() -> Self { - Attributes { - #[cfg(feature = "tag")] - tag: TypeTag, - attrs: BTreeMap::new(), - } - } - - pub fn is_empty(&self) -> bool { - self.attrs.is_empty() - } - - pub fn len(&self) -> usize { - self.attrs.len() - } - - /// Add a key-value pair to the attribute set. - /// - /// If an entry with the same key exists it is replaced with the new value. - pub fn put(&mut self, k: &str, v: &[u8]) -> &mut Self { - self.attrs.insert(k.into(), v.to_vec().into()); - self - } - - pub fn get(&self, k: &str) -> Option<&[u8]> { - self.attrs.get(k).map(|s| &***s) - } - - pub fn iter(&self) -> impl Iterator { - self.attrs.iter() - } -} - -/// A Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) -#[derive( - Debug, Clone, Copy, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, -)] -#[cbor(transparent)] -pub struct Timestamp(#[n(0)] u64); - -impl Timestamp { - #[cfg(feature = "std")] - pub fn now() -> Option { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .ok() - .map(|d| Timestamp(d.as_secs())) - } - - #[cfg(not(feature = "std"))] - pub fn now() -> Option { - None - } - - pub fn elapsed(&self, since: Timestamp) -> Option { - (self.0 >= since.0).then(|| Duration::from_secs(self.0 - since.0)) - } - - pub fn unix_time(&self) -> u64 { - self.0 - } -} - -impl From for u64 { - fn from(t: Timestamp) -> Self { - t.0 - } -} - -/// A schema identifier allows discriminate sets of credential attributes. -#[derive(Debug, Clone, Copy, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cbor(transparent)] -pub struct SchemaId(#[n(0)] pub u64); - -impl From for u64 { - fn from(s: SchemaId) -> Self { - s.0 - } -} - -impl fmt::Display for SchemaId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// Convenience structure to create [`Credential`]s. -pub struct CredentialBuilder { - schema: Option, - attrs: Attributes, - subject: IdentityIdentifier, - validity: Duration, -} - -impl CredentialBuilder { - pub fn from_attributes(identity: IdentityIdentifier, attrs: HashMap) -> Self { - attrs - .iter() - .fold(Credential::builder(identity), |crd, (k, v)| { - crd.with_attribute(k, v.as_bytes()) - }) - } - /// Add some key-value pair as credential attribute. - pub fn with_attribute(mut self, k: &str, v: &[u8]) -> Self { - self.attrs.put(k, v); - self - } - - /// Set the schema identifier of the credential. - pub fn with_schema(mut self, s: SchemaId) -> Self { - self.schema = Some(s); - self - } - - /// Set validity duration of the credential. - /// - /// # Panics - /// - /// If the given validity exceeds [`MAX_CREDENTIAL_VALIDITY`]. - pub fn valid_for(mut self, val: Duration) -> Self { - assert! { - val <= MAX_CREDENTIAL_VALIDITY, - "validity exceeds allowed maximum" - } - self.validity = val; - self - } -} - -impl Serialize for Credential { - fn serialize(&self, ser: S) -> Result { - let bytes = minicbor::to_vec(self).expect("encoding credential to vec never errors"); - if ser.is_human_readable() { - ser.serialize_str(&hex::encode(&bytes)) - } else { - ser.serialize_bytes(&bytes) - } - } -} - -impl<'a> Deserialize<'a> for Credential { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - let bytes: Vec = if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - hex::decode(s).map_err(D::Error::custom)? - } else { - >::deserialize(deserializer)? - }; - minicbor::decode(&bytes).map_err(D::Error::custom) - } -} - -#[cfg(test)] -mod test { - use super::*; - use quickcheck::{Arbitrary, Gen}; - use quickcheck_macros::quickcheck; - use serde_json; - - #[quickcheck] - fn test_serialization_roundtrip(credential: Credential) -> bool { - let serialized = serde_bare::to_vec(&credential).unwrap(); - let actual: Credential = serde_bare::from_slice(serialized.as_slice()).unwrap(); - actual == credential - } - - #[test] - fn test_serialization() { - // this test makes sure that we are using the minicbor Bytes encoder - // for the Credential fields - let credential = Credential::new(vec![1, 2, 3], vec![5, 6, 7]); - let serialized = serde_bare::to_vec(&credential).unwrap(); - let expected: Vec = vec![11, 162, 1, 67, 1, 2, 3, 2, 67, 5, 6, 7]; - assert_eq!(serialized, expected) - } - - #[quickcheck] - fn test_serialization_roundtrip_human_readable(credential: Credential) -> bool { - let serialized = serde_json::to_string(&credential).unwrap(); - let actual: Credential = serde_json::from_str(serialized.as_str()).unwrap(); - actual == credential - } - - impl Arbitrary for Credential { - fn arbitrary(g: &mut Gen) -> Self { - Credential::new(>::arbitrary(g), >::arbitrary(g)) - } - - /// there is no meaningful shrinking in general for a credential - fn shrink(&self) -> Box> { - Box::new(std::iter::empty()) - } - } - - #[test] - fn test_display_credential_data() { - let credential_data = make_credential_data(); - let actual = format!("{credential_data}"); - let expected = r#"Schema: 1 -Subject: P6474cfdbf547240b6d716bff89c976810859bc3f47be8ea620df12a392ea6cb7 -Issuer: P0db4fec87ff764485f1311e68d6f474e786f1fdbafcd249a5eb73dd681fd1d5d (OCKAM_RK) -Created: 1970-01-01T00:02:00Z -Expires: 1970-01-01T00:03:20Z -Attributes: {"name": "value"} -"#; - assert_eq!(actual, expected); - } - - #[test] - fn test_display_credential() { - let credential_data = make_credential_data(); - let data = minicbor::to_vec(credential_data).unwrap(); - let credential = Credential::new(data, vec![1, 2, 3]); - - let actual = format!("{credential}"); - let expected = r#"Schema: 1 -Subject: P6474cfdbf547240b6d716bff89c976810859bc3f47be8ea620df12a392ea6cb7 -Issuer: P0db4fec87ff764485f1311e68d6f474e786f1fdbafcd249a5eb73dd681fd1d5d (OCKAM_RK) -Created: 1970-01-01T00:02:00Z -Expires: 1970-01-01T00:03:20Z -Attributes: {"name": "value"} -Signature: 010203 -"#; - assert_eq!(actual, expected); - } - - fn make_credential_data() -> CredentialData { - let mut attributes = Attributes::new(); - attributes.put("name", "value".as_bytes()); - - CredentialData { - schema: Some(SchemaId(1)), - subject: IdentityIdentifier::from_key_id( - "6474cfdbf547240b6d716bff89c976810859bc3f47be8ea620df12a392ea6cb7", - ), - issuer: IdentityIdentifier::from_key_id( - "0db4fec87ff764485f1311e68d6f474e786f1fdbafcd249a5eb73dd681fd1d5d", - ), - attributes, - issuer_key_label: "OCKAM_RK".into(), - created: Timestamp(120), - expires: Timestamp(200), - status: None::>, - } - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/credential.rs b/implementations/rust/ockam/ockam_identity/src/credential/credential.rs new file mode 100644 index 00000000000..856a0e1bb7e --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credential/credential.rs @@ -0,0 +1,155 @@ +use core::fmt; +use minicbor::{Decode, Encode}; +use ockam_core::compat::{string::String, vec::Vec}; +use ockam_core::Result; +use serde::de::Error; +use serde::{Deserialize, Deserializer}; +use serde::{Serialize, Serializer}; + +#[cfg(feature = "std")] +use crate::CredentialData; +#[cfg(feature = "std")] +use std::ops::Deref; + +#[cfg(feature = "tag")] +use crate::TypeTag; + +/// Credential data + signature for that data +#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] +#[rustfmt::skip] +#[cbor(map)] +pub struct Credential { + #[cfg(feature = "tag")] + #[n(0)] tag: TypeTag<3796735>, + /// CBOR-encoded [`CredentialData`]. + #[cbor(with = "minicbor::bytes")] + #[b(1)] pub data: Vec, + /// Cryptographic signature of attributes data. + #[cbor(with = "minicbor::bytes")] + #[b(2)] pub signature: Vec, +} + +impl Credential { + /// Return the signature of a credential + pub fn signature(&self) -> &[u8] { + &self.signature + } + + /// Return the serialized data of a credential + pub fn unverified_data(&self) -> &[u8] { + &self.data + } + + pub(crate) fn new(data: Vec, signature: Vec) -> Self { + Credential { + #[cfg(feature = "tag")] + tag: TypeTag, + data, + signature, + } + } +} + +impl fmt::Display for Credential { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let credential_data: CredentialData<_> = + CredentialData::try_from(self.clone().data.as_slice()).map_err(|_| fmt::Error)?; + writeln!(f, "---")?; + writeln!(f, " Subject: {}", credential_data.subject)?; + writeln!( + f, + " Issuer: {} ({})", + credential_data.issuer, credential_data.issuer_key_label + )?; + //TODO: write timestamps on human-readable format. Should we add a dependency for this? + writeln!(f, " Created: {}", u64::from(credential_data.created))?; + writeln!(f, " Expires: {}", u64::from(credential_data.expires))?; + write!(f, " Attributes: ")?; + f.debug_map() + .entries( + credential_data + .attributes + .iter() + .map(|(k, v)| (k, std::str::from_utf8(v).unwrap_or("**binary**"))), + ) + .finish()?; + writeln!(f, "\n")?; + writeln!(f, " Signature: {}", hex::encode(self.signature.deref()))?; + writeln!(f, "---") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{:?}", self) + } +} + +impl Serialize for Credential { + fn serialize(&self, ser: S) -> Result { + let bytes = minicbor::to_vec(self).expect("encoding credential to vec never errors"); + if ser.is_human_readable() { + ser.serialize_str(&hex::encode(&bytes)) + } else { + ser.serialize_bytes(&bytes) + } + } +} + +impl<'a> Deserialize<'a> for Credential { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + let bytes: Vec = if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + hex::decode(s).map_err(D::Error::custom)? + } else { + >::deserialize(deserializer)? + }; + minicbor::decode(&bytes).map_err(D::Error::custom) + } +} + +#[cfg(test)] +mod test { + use super::*; + use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; + use serde_json; + + #[quickcheck] + fn test_serialization_roundtrip(credential: Credential) -> bool { + let serialized = serde_bare::to_vec(&credential).unwrap(); + let actual: Credential = serde_bare::from_slice(serialized.as_slice()).unwrap(); + actual == credential + } + + #[test] + fn test_serialization() { + // this test makes sure that we are using the minicbor Bytes encoder + // for the Credential fields + let credential = Credential::new(vec![1, 2, 3], vec![5, 6, 7]); + let serialized = serde_bare::to_vec(&credential).unwrap(); + let expected: Vec = vec![11, 162, 1, 67, 1, 2, 3, 2, 67, 5, 6, 7]; + assert_eq!(serialized, expected) + } + + #[quickcheck] + fn test_serialization_roundtrip_human_readable(credential: Credential) -> bool { + let serialized = serde_json::to_string(&credential).unwrap(); + let actual: Credential = serde_json::from_str(serialized.as_str()).unwrap(); + actual == credential + } + + impl Arbitrary for Credential { + fn arbitrary(g: &mut Gen) -> Self { + Credential::new(>::arbitrary(g), >::arbitrary(g)) + } + + /// there is no meaningful shrinking in general for a credential + fn shrink(&self) -> Box> { + Box::new(std::iter::empty()) + } + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/credential_builder.rs b/implementations/rust/ockam/ockam_identity/src/credential/credential_builder.rs new file mode 100644 index 00000000000..d81c96b04a5 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credential/credential_builder.rs @@ -0,0 +1,96 @@ +use core::marker::PhantomData; +use core::time::Duration; +use ockam_core::compat::collections::HashMap; +use ockam_core::compat::string::String; +use ockam_core::errcode::{Kind, Origin}; +use ockam_core::{Error, Result}; + +use crate::credential::{ + Attributes, CredentialData, SchemaId, Timestamp, Verified, MAX_CREDENTIAL_VALIDITY, +}; +use crate::identity::identity_change::IdentityChangeConstants; +use crate::identity::IdentityIdentifier; + +#[cfg(feature = "tag")] +use crate::TypeTag; + +/// Convenience structure to create [`Credential`]s. +pub struct CredentialBuilder { + pub(crate) schema: Option, + pub(crate) attrs: Attributes, + pub(crate) subject: IdentityIdentifier, + pub(crate) issuer: IdentityIdentifier, + pub(crate) validity: Duration, +} + +impl CredentialBuilder { + pub(super) fn new( + subject: IdentityIdentifier, + issuer: IdentityIdentifier, + ) -> CredentialBuilder { + Self { + schema: None, + attrs: Attributes::default(), + subject, + issuer, + validity: MAX_CREDENTIAL_VALIDITY, + } + } + + /// + pub fn from_attributes( + subject: IdentityIdentifier, + issuer: IdentityIdentifier, + attrs: HashMap, + ) -> Self { + attrs + .iter() + .fold(CredentialData::builder(subject, issuer), |crd, (k, v)| { + crd.with_attribute(k, v.as_bytes()) + }) + } + /// Add some key-value pair as credential attribute. + pub fn with_attribute(mut self, k: &str, v: &[u8]) -> Self { + self.attrs.put(k, v); + self + } + + /// Set the schema identifier of the credential. + pub fn with_schema(mut self, s: SchemaId) -> Self { + self.schema = Some(s); + self + } + + /// Set validity duration of the credential. + /// + /// # Panics + /// + /// If the given validity exceeds [`MAX_CREDENTIAL_VALIDITY`]. + pub fn valid_for(mut self, val: Duration) -> Self { + assert! { + val <= MAX_CREDENTIAL_VALIDITY, + "validity exceeds allowed maximum" + } + self.validity = val; + self + } + + /// Return a verified credential data, with a created timestamp + pub fn build(self) -> Result> { + let key_label = IdentityChangeConstants::ROOT_LABEL; + let now = Timestamp::now() + .ok_or_else(|| Error::new(Origin::Core, Kind::Internal, "invalid system time"))?; + let exp = Timestamp::add_seconds(&now, self.validity.as_secs()); + + Ok(CredentialData { + schema: self.schema, + attributes: self.attrs, + subject: self.subject, + issuer: self.issuer, + issuer_key_label: key_label.into(), + created: now, + expires: exp, + status: None::>, + }) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/credential_data.rs b/implementations/rust/ockam/ockam_identity/src/credential/credential_data.rs new file mode 100644 index 00000000000..48e576de977 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credential/credential_data.rs @@ -0,0 +1,305 @@ +use crate::alloc::string::ToString; +use core::marker::PhantomData; +use core::time::Duration; +use minicbor::bytes::ByteVec; +use minicbor::{Decode, Encode}; +use ockam_core::compat::collections::HashMap; +use ockam_core::compat::{collections::BTreeMap, string::String, vec::Vec}; +use ockam_core::{Error, Result}; +use serde::{Deserialize, Serialize}; + +use crate::identity::identity_change::IdentityChangeConstants; +use crate::identity::IdentityIdentifier; +use crate::CredentialBuilder; +use ockam_core::errcode::{Kind, Origin}; + +#[cfg(feature = "tag")] +use crate::TypeTag; + +/// Set of attributes associated to a given identity issued by another identity +#[derive(Debug, Decode, Encode, Clone)] +#[rustfmt::skip] +#[cbor(map)] +pub struct CredentialData { + /// A schema identifier to allow distinguishing sets of attributes. + #[n(1)] pub(crate) schema: Option, + /// User-defined key-value pairs. + #[b(2)] pub(crate) attributes: Attributes, + /// The subject this credential is issued for. + #[n(3)] pub(crate) subject: IdentityIdentifier, + /// The entity that signed this credential. + #[n(4)] pub(crate) issuer: IdentityIdentifier, + /// The label of the issuer's public key. + #[b(5)] pub(crate) issuer_key_label: String, + /// The time when this credential was created. + #[n(6)] pub(crate) created: Timestamp, + /// The time this credential expires. + #[n(7)] pub(crate) expires: Timestamp, + /// Term to represent the verification status type. + #[n(8)] pub(crate) status: Option> +} + +impl CredentialData { + /// Create a builder for a subject and an issuer, all other fields are optional and + /// can be set with the builder methods + pub fn builder(subject: IdentityIdentifier, issuer: IdentityIdentifier) -> CredentialBuilder { + CredentialBuilder::new(subject, issuer) + } + + /// Return a credential data struct with a fixed set of attributes + pub fn from_attributes( + subject: IdentityIdentifier, + issuer: IdentityIdentifier, + attrs: HashMap, + ) -> Result> { + CredentialBuilder::from_attributes(subject, issuer, attrs).build() + } +} + +impl CredentialData { + pub(crate) fn verify( + &self, + subject: &IdentityIdentifier, + issuer: &IdentityIdentifier, + now: Timestamp, + ) -> Result<()> { + if self.issuer_key_label != IdentityChangeConstants::ROOT_LABEL { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "invalid signing key", + )); + } + + if &self.issuer != issuer { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "unknown authority", + )); + } + + if &self.subject != subject { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "unknown subject", + )); + } + + if self.expires <= now { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "expired credential", + )); + } + + Ok(()) + } + + pub(crate) fn into_verified(self) -> CredentialData { + CredentialData { + schema: self.schema, + attributes: self.attributes, + subject: self.subject, + issuer: self.issuer, + issuer_key_label: self.issuer_key_label, + created: self.created, + expires: self.expires, + status: None::>, + } + } +} + +/// Maximum duration for a valid credential in seconds (30 days) +pub const MAX_CREDENTIAL_VALIDITY: Duration = Duration::from_secs(30 * 24 * 3600); + +/// Type to represent data of verified credential. +#[derive(Debug, Encode, Clone)] +pub enum Verified {} + +/// Type to represent data of unverified credential. +#[derive(Debug, Decode, Clone)] +pub enum Unverified {} + +impl CredentialData { + /// Return the credential schema + pub fn schema(&self) -> Option { + self.schema + } + + /// Return the credential subject + pub fn subject(&self) -> &IdentityIdentifier { + &self.subject + } + + /// Return the credential issuer + pub fn issuer(&self) -> &IdentityIdentifier { + &self.issuer + } + + /// Return the credential issuer + pub fn issuer_key_label(&self) -> &str { + &self.issuer_key_label + } + + /// Return the credential creation date + pub fn created_at(&self) -> Timestamp { + self.created + } + + /// Return the credential expiration date + pub fn expires_at(&self) -> Timestamp { + self.expires + } + + /// Return the identity attributes as a reference + pub fn attributes(&self) -> &Attributes { + &self.attributes + } + + /// Return the identity attributes + pub fn into_attributes(self) -> Attributes { + self.attributes + } +} + +impl CredentialData { + /// Return the issuer of a credential data when unverified + pub fn unverified_issuer(&self) -> &IdentityIdentifier { + &self.issuer + } + + /// Return the issuer key label of a credential data when unverified + pub fn unverified_key_label(&self) -> &str { + &self.issuer_key_label + } + + /// Return the subject of a credential data when unverified + pub fn unverified_subject(&self) -> &IdentityIdentifier { + &self.subject + } +} + +impl TryFrom<&[u8]> for CredentialData { + type Error = minicbor::decode::Error; + + fn try_from(value: &[u8]) -> Result { + minicbor::decode(value) + } +} + +/// User-defined key-value pairs. +#[derive(Debug, Clone, Default, Encode, Decode, PartialEq, Eq)] +#[rustfmt::skip] +#[cbor(map)] +pub struct Attributes { + #[cfg(feature = "tag")] + #[n(0)] tag: TypeTag<4724285>, + #[b(1)] attrs: BTreeMap +} + +impl Attributes { + /// Create a new empty attribute set. + pub fn new() -> Self { + Attributes { + #[cfg(feature = "tag")] + tag: TypeTag, + attrs: BTreeMap::new(), + } + } + + /// Return true if this set of key / value is empty + pub fn is_empty(&self) -> bool { + self.attrs.is_empty() + } + + /// Return the number of key / values + pub fn len(&self) -> usize { + self.attrs.len() + } + + /// Add a key-value pair to the attribute set. + /// + /// If an entry with the same key exists it is replaced with the new value. + pub fn put(&mut self, k: &str, v: &[u8]) -> &mut Self { + self.attrs.insert(k.into(), v.to_vec().into()); + self + } + + /// Return the value associated to a given key + pub fn get(&self, k: &str) -> Option<&[u8]> { + self.attrs.get(k).map(|s| &***s) + } + + /// Return an iterator on the list of key / values + pub fn iter(&self) -> impl Iterator { + self.attrs.iter() + } + + //TODO: review the credential' attributes types. They are references and has lifetimes, + //etc, but in reality this is always just deserizalided (either from wire or from + //storage), so imho all that just add to the complexity without gaining much + pub(crate) fn as_map_vec_u8(&self) -> BTreeMap> { + self.attrs + .iter() + .map(|(k, v)| (k.to_string(), v.to_vec())) + .collect() + } +} + +/// A Unix timestamp (seconds since 1970-01-01 00:00:00 UTC) +#[derive( + Debug, Clone, Copy, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +#[cbor(transparent)] +pub struct Timestamp(#[n(0)] u64); + +impl Timestamp { + /// Create a new timestamp using the system time + #[cfg(feature = "std")] + pub fn now() -> Option { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .ok() + .map(|d| Timestamp(d.as_secs())) + } + + pub(crate) fn add_seconds(&self, seconds: u64) -> Self { + Timestamp(self.0.saturating_add(seconds)) + } + + /// Create a new timestamp using the system time + #[cfg(not(feature = "std"))] + pub fn now() -> Option { + None + } + + /// Return the time elapsed between this timestamp and a previous one + pub fn elapsed(&self, since: Timestamp) -> Option { + (self.0 >= since.0).then(|| Duration::from_secs(self.0 - since.0)) + } + + /// Return the timestamp value as a number of seconds since the UNIX epoch time + pub fn unix_time(&self) -> u64 { + self.0 + } +} + +impl From for u64 { + fn from(t: Timestamp) -> Self { + t.0 + } +} + +/// A schema identifier allows discriminate sets of credential attributes. +#[derive(Debug, Clone, Copy, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cbor(transparent)] +pub struct SchemaId(#[n(0)] pub u64); + +impl From for u64 { + fn from(s: SchemaId) -> Self { + s.0 + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/identity.rs b/implementations/rust/ockam/ockam_identity/src/credential/identity.rs deleted file mode 100644 index 7f6e0de7f6f..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/credential/identity.rs +++ /dev/null @@ -1,263 +0,0 @@ -use crate::alloc::string::ToString; -use crate::authenticated_storage::{AttributesEntry, IdentityAttributeStorage}; -use crate::credential::worker::CredentialExchangeWorker; -use crate::credential::{ - Credential, CredentialBuilder, CredentialData, Timestamp, Unverified, Verified, -}; -use crate::{ - Identity, IdentityError, IdentityIdentifier, IdentitySecureChannelLocalInfo, - IdentityStateConst, IdentityVault, PublicIdentity, -}; -use core::marker::PhantomData; -use minicbor::Decoder; -use ockam_core::api::{Request, Response, Status}; -use ockam_core::compat::sync::Arc; -use ockam_core::compat::vec::Vec; -use ockam_core::errcode::{Kind, Origin}; -use ockam_core::vault::SignatureVec; -use ockam_core::{Address, AllowAll, AsyncTryClone, Error, Mailboxes, Result, Route}; -use ockam_node::api::{request, request_with_local_info}; -use ockam_node::WorkerBuilder; - -impl Identity { - pub async fn set_credential(&self, credential: Credential) { - // TODO: May also verify received credential calling self.verify_self_credential - *self.credential.write().await = Some(credential); - } - - pub async fn credential(&self) -> Option { - self.credential.read().await.clone() - } - - /// Create a signed credential based on the given values. - pub async fn issue_credential(&self, builder: CredentialBuilder) -> Result { - let key_label = IdentityStateConst::ROOT_LABEL; - let now = Timestamp::now() - .ok_or_else(|| Error::new(Origin::Core, Kind::Internal, "invalid system time"))?; - let exp = Timestamp(u64::from(now).saturating_add(builder.validity.as_secs())); - let dat = CredentialData { - schema: builder.schema, - attributes: builder.attrs, - subject: builder.subject, - issuer: self.identifier().clone(), - issuer_key_label: key_label.into(), - created: now, - expires: exp, - status: None::>, - }; - let bytes = minicbor::to_vec(&dat)?; - - let sig = self.create_signature(&bytes, None).await?; - Ok(Credential::new(bytes, SignatureVec::from(sig))) - } - - /// Start worker that will be available to receive others attributes and put them into storage, - /// after successful verification - pub async fn start_credential_exchange_worker( - &self, - authorities: Vec, - address: impl Into
, - present_back: bool, - attributes_storage: Arc, - ) -> Result<()> { - let s = self.async_try_clone().await?; - let worker = - CredentialExchangeWorker::new(authorities, present_back, s, attributes_storage); - - WorkerBuilder::with_mailboxes( - Mailboxes::main( - address.into(), - Arc::new(AllowAll), // We check for Identity secure channel inside the worker - Arc::new(AllowAll), // FIXME: @ac Allow to respond anywhere using return_route - ), - worker, - ) - .start(&self.ctx) - .await?; - - Ok(()) - } - - /// Present credential to other party, route shall use secure channel - pub async fn present_credential( - &self, - route: impl Into, - provided_credential: Option<&Credential>, - ) -> Result<()> { - let credential = self.get_credential_or_provided(provided_credential).await?; - - let buf = request( - &self.ctx, - "credential", - None, - route.into(), - Request::post("actions/present").body(credential), - ) - .await?; - - let res: Response = minicbor::decode(&buf)?; - match res.status() { - Some(Status::Ok) => Ok(()), - _ => Err(Error::new( - Origin::Application, - Kind::Invalid, - "credential presentation failed", - )), - } - } - - /// Present credential to other party, route shall use secure channel. Other party is expected - /// to present its credential in response, otherwise this call errors. - pub async fn present_credential_mutual( - &self, - route: impl Into, - authorities: impl IntoIterator, - attributes_storage: Arc, - provided_credential: Option<&Credential>, - ) -> Result<()> { - let credential = self.get_credential_or_provided(provided_credential).await?; - - let path = "actions/present_mutual"; - let (buf, local_info) = request_with_local_info( - &self.ctx, - "credential", - None, - route.into(), - Request::post(path).body(credential), - ) - .await?; - - let their_id = IdentitySecureChannelLocalInfo::find_info_from_list(&local_info)? - .their_identity_id() - .clone(); - - let mut dec = Decoder::new(&buf); - let res: Response = dec.decode()?; - match res.status() { - Some(Status::Ok) => {} - Some(s) => { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - format!("credential presentation failed: {}", s), - )) - } - _ => { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "credential presentation failed", - )) - } - } - - let credential: Credential = dec.decode()?; - - self.receive_presented_credential(their_id, credential, authorities, attributes_storage) - .await?; - - Ok(()) - } -} - -impl Identity { - async fn verify_credential( - sender: &IdentityIdentifier, - credential: &Credential, - authorities: impl IntoIterator, - vault: Arc, - ) -> Result> { - let credential_data: CredentialData = match minicbor::decode(&credential.data) { - Ok(c) => c, - Err(_) => return Err(IdentityError::InvalidCredentialFormat.into()), - }; - - let issuer = authorities - .into_iter() - .find(|&x| x.identifier() == &credential_data.issuer); - let issuer = match issuer { - Some(i) => i, - None => return Err(IdentityError::UnknownAuthority.into()), - }; - - let credential_data = match issuer - .verify_credential(credential, sender, vault.clone()) - .await - { - Ok(d) => d, - Err(_) => return Err(IdentityError::CredentialVerificationFailed.into()), - }; - - Ok(credential_data) - } - - pub async fn verify_self_credential( - &self, - credential: &Credential, - authorities: impl IntoIterator, - ) -> Result<()> { - let _ = Self::verify_credential( - self.identifier(), - credential, - authorities, - self.vault.clone(), - ) - .await?; - Ok(()) - } - - pub(crate) async fn receive_presented_credential( - &self, - sender: IdentityIdentifier, - credential: Credential, - authorities: impl IntoIterator, - attributes_storage: Arc, - ) -> Result<()> { - let credential_data = - Self::verify_credential(&sender, &credential, authorities, self.vault.clone()).await?; - - //TODO: review the credential' attributes types. They are references and has lifetimes, - //etc, but in reality this is always just deserizalided (either from wire or from - //storage), so imho all that just add to the complexity without gaining much - let attrs = credential_data - .attributes - .attrs - .iter() - .map(|(k, v)| (k.to_string(), v.to_vec())) - .collect(); - attributes_storage - .put_attributes( - &sender, - AttributesEntry::new( - attrs, - Timestamp::now().unwrap(), - Some(credential_data.expires), - Some(credential_data.issuer), - ), - ) - .await?; - - Ok(()) - } - - /// Gets a clone of the identities current credential - /// or uses the provided credential if one exists - async fn get_credential_or_provided( - &self, - provided_cred: Option<&Credential>, - ) -> Result { - let rw_credential = self.credential.read().await; - let credential = match provided_cred { - Some(c) => c, - None => rw_credential.as_ref().ok_or_else(|| { - Error::new( - Origin::Application, - Kind::Invalid, - "no credential to present", - ) - })?, - }; - - Ok(credential.clone()) - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/mod.rs b/implementations/rust/ockam/ockam_identity/src/credential/mod.rs new file mode 100644 index 00000000000..ccfab67668a --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credential/mod.rs @@ -0,0 +1,10 @@ +#[allow(clippy::module_inception)] +mod credential; +mod credential_builder; +mod credential_data; +mod one_time_code; + +pub use credential::*; +pub use credential_builder::*; +pub use credential_data::*; +pub use one_time_code::*; diff --git a/implementations/rust/ockam/ockam_identity/src/credential/public_identity.rs b/implementations/rust/ockam/ockam_identity/src/credential/public_identity.rs deleted file mode 100644 index a792b12647b..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/credential/public_identity.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::alloc::borrow::ToOwned; -use crate::authenticated_storage::IdentityAttributeStorage; -use crate::credential::{Credential, CredentialData, Timestamp, Verified}; -use crate::PublicIdentity; -use crate::{IdentityIdentifier, IdentityStateConst, IdentityVault}; -use ockam_core::compat::collections::BTreeMap; -use ockam_core::compat::{string::String, sync::Arc, vec::Vec}; -use ockam_core::errcode::{Kind, Origin}; -use ockam_core::vault::Signature; -use ockam_core::{Error, Result}; - -impl PublicIdentity { - /// Perform a signature check with the given identity. - /// - /// If successful, the credential data are returned. - pub async fn verify_credential( - &self, - credential: &Credential, - subject: &IdentityIdentifier, - vault: Arc, - ) -> Result> { - let dat = CredentialData::try_from(credential)?; - if dat.unverified_key_label() != IdentityStateConst::ROOT_LABEL { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "invalid signing key", - )); - } - - if &dat.issuer != self.identifier() { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "unknown authority", - )); - } - - if &dat.subject != subject { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "unknown subject", - )); - } - - let now = Timestamp::now() - .ok_or_else(|| Error::new(Origin::Application, Kind::Invalid, "invalid system time"))?; - if dat.expires <= now { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "expired credential", - )); - } - - let sig = Signature::new(credential.signature().to_vec()); - - if !self - .verify_signature( - &sig, - credential.unverified_data(), - Some(dat.unverified_key_label()), - vault, - ) - .await? - { - return Err(Error::new( - Origin::Application, - Kind::Invalid, - "invalid signature", - )); - } - Ok(dat.into_verified()) - } - - /// Return authenticated non-expired attributes attached to that Identity - pub async fn get_attributes( - &self, - authenticated_storage: &impl IdentityAttributeStorage, - ) -> Result>>> { - authenticated_storage - .get_attributes(self.identifier()) - .await - .map(|r| r.map(|e| e.attrs().to_owned())) - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/credential_issuer.rs b/implementations/rust/ockam/ockam_identity/src/credentials/credential_issuer.rs similarity index 71% rename from implementations/rust/ockam/ockam_identity/src/credential_issuer.rs rename to implementations/rust/ockam/ockam_identity/src/credentials/credential_issuer.rs index 3ca9e470908..084eb312d64 100644 --- a/implementations/rust/ockam/ockam_identity/src/credential_issuer.rs +++ b/implementations/rust/ockam/ockam_identity/src/credentials/credential_issuer.rs @@ -1,20 +1,18 @@ use crate::alloc::string::ToString; use ockam_core::compat::boxed::Box; use ockam_core::compat::collections::BTreeMap; +use ockam_core::compat::sync::Arc; use ockam_core::errcode::{Kind, Origin}; use ockam_core::{route, AsyncTryClone, Error, Message, Result, Route, Routed, Worker}; - use ockam_node::Context; -use ockam_vault::Vault; use CredentialIssuerRequest::*; use CredentialIssuerResponse::*; -use crate::authenticated_storage::{ - AttributesEntry, AuthenticatedAttributeStorage, IdentityAttributeStorageReader, - IdentityAttributeStorageWriter, -}; use crate::credential::{Credential, Timestamp}; -use crate::{Identity, IdentityIdentifier, PublicIdentity}; +use crate::credentials::Credentials; +use crate::identities::{identities, AttributesEntry, IdentitiesRepository}; +use crate::identity::{Identity, IdentityIdentifier}; +use crate::CredentialData; use serde::{Deserialize, Serialize}; /// This struct provides a simplified credential issuer which can be used in test scenarios @@ -32,31 +30,33 @@ use serde::{Deserialize, Serialize}; /// A Credential for a given identity can then be retrieved with the `get_credential` method. /// pub struct CredentialIssuer { + identity_attributes: Arc, + credentials: Arc, identity: Identity, } impl CredentialIssuer { /// Create a fully in-memory issuer for testing - pub async fn create(ctx: &Context) -> Result { - let identity = Identity::create(ctx, Vault::create()).await?; - Ok(CredentialIssuer { identity }) + pub async fn create(identity: &Identity) -> Result { + let identities = identities(); + Ok(CredentialIssuer::new( + identities.identities_repository.clone(), + identities.credentials(), + identity, + )) } /// Create a new CredentialIssuer from an Identity - pub fn new(identity: Identity) -> CredentialIssuer { - CredentialIssuer { identity } - } - - /// Return the identity holding credentials - pub fn identity(&self) -> &Identity { - &self.identity - } - - /// Return the attributes storage for the issuer identity - async fn attributes_storage(&self) -> Result { - Ok(AuthenticatedAttributeStorage::new( - self.identity.authenticated_storage().clone(), - )) + pub fn new( + identity_attributes: Arc, + credentials: Arc, + identity: &Identity, + ) -> CredentialIssuer { + CredentialIssuer { + identity_attributes, + credentials, + identity: identity.clone(), + } } /// Store an attribute name/value pair for a given identity @@ -66,8 +66,7 @@ impl CredentialIssuer { attribute_name: &str, attribute_value: &str, ) -> Result<()> { - let attributes_storage: AuthenticatedAttributeStorage = self.attributes_storage().await?; - let mut attributes = match attributes_storage.get_attributes(subject).await? { + let mut attributes = match self.identity_attributes.get_attributes(subject).await? { Some(entry) => (*entry.attrs()).clone(), None => BTreeMap::new(), }; @@ -79,24 +78,25 @@ impl CredentialIssuer { attributes, Timestamp::now().unwrap(), None, - Some(self.identity.identifier().clone()), + Some(subject.clone()), ); - attributes_storage.put_attributes(subject, entry).await + self.identity_attributes + .put_attributes(subject, entry) + .await } } #[ockam_core::async_trait] impl CredentialIssuerApi for CredentialIssuer { - /// Return the issuer public identity - async fn public_identity(&self) -> Result { - self.identity.to_public().await + /// Return the issuer identity + async fn identity(&self) -> Result { + Ok(self.identity.clone()) } /// Create an authenticated credential for an identity async fn get_credential(&self, subject: &IdentityIdentifier) -> Result> { - let mut builder = Credential::builder(subject.clone()); - let identity_attributes: AuthenticatedAttributeStorage = self.attributes_storage().await?; - if let Some(attributes) = identity_attributes.get_attributes(subject).await? { + let mut builder = CredentialData::builder(subject.clone(), self.identity.identifier()); + if let Some(attributes) = self.identity_attributes.get_attributes(subject).await? { builder = attributes .attrs() @@ -104,7 +104,10 @@ impl CredentialIssuerApi for CredentialIssuer { .fold(builder, |b, (attribute_name, attribute_value)| { b.with_attribute(attribute_name, attribute_value.as_slice()) }); - let credential = self.identity.issue_credential(builder).await?; + let credential = self + .credentials + .issue_credential(&self.identity, builder.build()?) + .await?; Ok(Some(credential)) } else { Ok(None) @@ -117,7 +120,7 @@ impl CredentialIssuerApi for CredentialIssuer { #[ockam_core::async_trait] pub trait CredentialIssuerApi { /// Return the issuer public identity - async fn public_identity(&self) -> Result; + async fn identity(&self) -> Result; /// Return an authenticated credential a given identity async fn get_credential(&self, subject: &IdentityIdentifier) -> Result>; @@ -143,31 +146,30 @@ impl Worker for CredentialIssuer { let credential = self.get_credential(&subject).await?; ctx.send(return_route, CredentialResponse(credential)).await } - GetPublicIdentity => { - let identity = self.public_identity().await?; - ctx.send(return_route, PublicIdentityResponse(identity)) - .await + GetIdentity => { + let identity = self.identity().await?; + ctx.send(return_route, IdentityResponse(identity)).await } } } } /// Requests for the CredentialIssuer worker API -#[derive(ockam_core::Message, Serialize, Deserialize)] +#[derive(Message, Serialize, Deserialize)] pub enum CredentialIssuerRequest { /// get an authenticated credential for a given identity GetCredential(IdentityIdentifier), /// get the public identity of the issuer - GetPublicIdentity, + GetIdentity, } /// Responses for the CredentialIssuer worker API -#[derive(ockam_core::Message, Serialize, Deserialize)] +#[derive(Message, Serialize, Deserialize)] pub enum CredentialIssuerResponse { /// return an authenticated credential CredentialResponse(Option), /// return the public identity of the issuer - PublicIdentityResponse(PublicIdentity), + IdentityResponse(Identity), } /// Client access to an CredentialIssuer worker @@ -189,16 +191,16 @@ impl CredentialIssuerClient { #[ockam_core::async_trait] impl CredentialIssuerApi for CredentialIssuerClient { - async fn public_identity(&self) -> Result { + async fn identity(&self) -> Result { let response = self .ctx .send_and_receive( route![self.credential_issuer_route.clone(), "issuer"], - GetPublicIdentity, + GetIdentity, ) .await?; match response { - PublicIdentityResponse(identity) => Ok(identity), + IdentityResponse(identity) => Ok(identity), _ => Err(error("missing public identity for the credential issuer")), } } diff --git a/implementations/rust/ockam/ockam_identity/src/credentials/credentials.rs b/implementations/rust/ockam/ockam_identity/src/credentials/credentials.rs new file mode 100644 index 00000000000..c297f68134e --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credentials/credentials.rs @@ -0,0 +1,120 @@ +use crate::credential::{Credential, CredentialData, Timestamp, Verified}; +use crate::identities::{AttributesEntry, Identities}; +use crate::identity::{Identity, IdentityError, IdentityIdentifier}; +use async_trait::async_trait; +use ockam_core::compat::boxed::Box; +use ockam_core::errcode::{Kind, Origin}; +use ockam_core::vault::SignatureVec; +use ockam_core::{Error, Result}; + +/// This trait provides functions to issue, accept and verify credentials +/// It can also return a CredentialsService offering a remote interface and start it as a worker +#[async_trait] +pub trait Credentials: Send + Sync + 'static { + /// Issue a credential for by having the issuer sign the serialized credential data + async fn issue_credential( + &self, + issuer: &Identity, + credential_data: CredentialData, + ) -> Result; + + /// Verify that a credential has been signed by one of the authorities + async fn verify_credential( + &self, + subject: &IdentityIdentifier, + authorities: &[Identity], + credential: Credential, + ) -> Result>; + + /// Verify and store a credential sent by a specific identity + async fn receive_presented_credential( + &self, + sender: &IdentityIdentifier, + authorities: &[Identity], + credential: Credential, + ) -> Result<()>; +} + +#[async_trait] +impl Credentials for Identities { + async fn verify_credential( + &self, + subject: &IdentityIdentifier, + authorities: &[Identity], + credential: Credential, + ) -> Result> { + let credential_data = CredentialData::try_from(credential.data.as_slice())?; + + let issuer = authorities + .iter() + .find(|&x| x.identifier() == credential_data.issuer); + let issuer = match issuer { + Some(i) => i, + None => return Err(IdentityError::UnknownAuthority.into()), + }; + + let now = Timestamp::now() + .ok_or_else(|| Error::new(Origin::Application, Kind::Invalid, "invalid system time"))?; + + credential_data.verify(subject, &issuer.identifier(), now)?; + + let sig = ockam_core::vault::Signature::new(credential.signature().to_vec()); + + if !self + .identities_keys() + .verify_signature( + issuer, + &sig, + credential.unverified_data(), + Some(credential_data.clone().unverified_key_label()), + ) + .await? + { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "invalid signature", + )); + } + Ok(credential_data.into_verified()) + } + + /// Create a signed credential based on the given values. + async fn issue_credential( + &self, + issuer: &Identity, + credential_data: CredentialData, + ) -> Result { + let bytes = minicbor::to_vec(credential_data)?; + let sig = self + .identities_keys() + .create_signature(issuer, &bytes, None) + .await?; + Ok(Credential::new(bytes, SignatureVec::from(sig))) + } + + async fn receive_presented_credential( + &self, + sender: &IdentityIdentifier, + authorities: &[Identity], + credential: Credential, + ) -> Result<()> { + let credential_data = self + .verify_credential(sender, authorities, credential) + .await?; + + self.identities_repository + .put_attributes( + sender, + AttributesEntry::new( + credential_data.attributes.as_map_vec_u8(), + Timestamp::now().unwrap(), + Some(credential_data.expires), + Some(credential_data.issuer), + ), + ) + .await?; + + Ok(()) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/credentials/credentials_server.rs b/implementations/rust/ockam/ockam_identity/src/credentials/credentials_server.rs new file mode 100644 index 00000000000..cfabb55294a --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credentials/credentials_server.rs @@ -0,0 +1,173 @@ +use crate::credential::Credential; +use crate::credentials::credentials_server_worker::CredentialsServerWorker; +use crate::credentials::Credentials; +use crate::identity::Identity; +use crate::secure_channel::IdentitySecureChannelLocalInfo; +use async_trait::async_trait; +use minicbor::Decoder; +use ockam_core::api::{Request, Response, Status}; +use ockam_core::compat::boxed::Box; +use ockam_core::compat::sync::Arc; +use ockam_core::compat::vec::Vec; +use ockam_core::errcode::{Kind, Origin}; +use ockam_core::{Address, AllowAll, Error, Mailboxes, Result, Route}; +use ockam_node::api::{request, request_with_local_info}; +use ockam_node::{Context, WorkerBuilder}; + +/// This trait allows an identity to send its credential to another identity +/// located at the end of a secure channel route +#[async_trait] +pub trait CredentialsServer: Send + Sync { + /// Present credential to other party, route shall use secure channel. Other party is expected + /// to present its credential in response, otherwise this call errors. + /// + async fn present_credential_mutual( + &self, + ctx: &Context, + route: Route, + authorities: &[Identity], + credential: Credential, + ) -> Result<()>; + + /// Present credential to other party, route shall use secure channel + async fn present_credential( + &self, + ctx: &Context, + route: Route, + credential: Credential, + ) -> Result<()>; + + /// Start this service as a worker + async fn start( + &self, + ctx: &Context, + credential: Option, + authorities: Vec, + address: Address, + present_back: bool, + ) -> Result<()>; +} + +/// Implementation of the CredentialsService +pub struct CredentialsServerModule { + credentials: Arc, +} + +#[async_trait] +impl CredentialsServer for CredentialsServerModule { + /// Present credential to other party, route shall use secure channel. Other party is expected + /// to present its credential in response, otherwise this call errors. + async fn present_credential_mutual( + &self, + ctx: &Context, + route: Route, + authorities: &[Identity], + credential: Credential, + ) -> Result<()> { + let path = "actions/present_mutual"; + let (buf, local_info) = request_with_local_info( + ctx, + "credential", + None, + route, + Request::post(path).body(credential), + ) + .await?; + + let their_id = IdentitySecureChannelLocalInfo::find_info_from_list(&local_info)? + .their_identity_id() + .clone(); + + let mut dec = Decoder::new(&buf); + let res: Response = dec.decode()?; + match res.status() { + Some(Status::Ok) => {} + Some(s) => { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + format!("credential presentation failed: {}", s), + )) + } + _ => { + return Err(Error::new( + Origin::Application, + Kind::Invalid, + "credential presentation failed", + )) + } + } + + let credential: Credential = dec.decode()?; + self.credentials + .receive_presented_credential(&their_id, authorities, credential) + .await?; + + Ok(()) + } + + /// Present credential to other party, route shall use secure channel + async fn present_credential( + &self, + ctx: &Context, + route: Route, + credential: Credential, + ) -> Result<()> { + let buf = request( + ctx, + "credential", + None, + route, + Request::post("actions/present").body(credential), + ) + .await?; + + let res: Response = minicbor::decode(&buf)?; + match res.status() { + Some(Status::Ok) => Ok(()), + _ => Err(Error::new( + Origin::Application, + Kind::Invalid, + "credential presentation failed", + )), + } + } + + /// Start worker that will be available to receive others attributes and put them into storage, + /// after successful verification + async fn start( + &self, + ctx: &Context, + credential: Option, + authorities: Vec, + address: Address, + present_back: bool, + ) -> Result<()> { + let worker = CredentialsServerWorker::new( + self.credentials.clone(), + credential.clone(), + authorities, + present_back, + ); + + WorkerBuilder::with_mailboxes( + Mailboxes::main( + address, + Arc::new(AllowAll), // We check for Identity secure channel inside the worker + Arc::new(AllowAll), // FIXME: @ac Allow to respond anywhere using return_route + ), + worker, + ) + .start(ctx) + .await?; + + Ok(()) + } +} + +impl CredentialsServerModule { + /// Create a CredentialsService. It is simply backed by the Credentials interface + pub fn new(credentials: Arc) -> Self { + Self { credentials } + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/credential/worker.rs b/implementations/rust/ockam/ockam_identity/src/credentials/credentials_server_worker.rs similarity index 78% rename from implementations/rust/ockam/ockam_identity/src/credential/worker.rs rename to implementations/rust/ockam/ockam_identity/src/credentials/credentials_server_worker.rs index 6c5cf0b1fee..096b3183b98 100644 --- a/implementations/rust/ockam/ockam_identity/src/credential/worker.rs +++ b/implementations/rust/ockam/ockam_identity/src/credentials/credentials_server_worker.rs @@ -1,10 +1,12 @@ -use crate::authenticated_storage::IdentityAttributeStorage; use crate::credential::Credential; -use crate::{Identity, IdentityIdentifier, IdentitySecureChannelLocalInfo, PublicIdentity}; +use crate::credentials::Credentials; +use crate::identity::{Identity, IdentityIdentifier}; +use crate::secure_channel::IdentitySecureChannelLocalInfo; use minicbor::Decoder; use ockam_core::api::{Error, Id, Request, Response, ResponseBuilder, Status}; use ockam_core::async_trait; -use ockam_core::compat::{boxed::Box, string::ToString, sync::Arc, vec::Vec}; +use ockam_core::compat::boxed::Box; +use ockam_core::compat::{string::ToString, sync::Arc, vec::Vec}; use ockam_core::{Result, Routed, Worker}; use ockam_node::Context; use tracing::{debug, error, info, trace, warn}; @@ -12,36 +14,30 @@ use tracing::{debug, error, info, trace, warn}; const TARGET: &str = "ockam::credential_exchange_worker::service"; /// Worker responsible for receiving and verifying other party's credential -pub struct CredentialExchangeWorker { - authorities: Vec, +pub struct CredentialsServerWorker { + credentials: Arc, + credential: Option, + authorities: Vec, present_back: bool, - identity: Identity, - attributes_storage: Arc, } -impl CredentialExchangeWorker { +impl CredentialsServerWorker { pub fn new( - authorities: Vec, + credentials: Arc, + credential: Option, + authorities: Vec, present_back: bool, - identity: Identity, - attributes_storage: Arc, ) -> Self { Self { + credentials, + credential, authorities, present_back, - identity, - attributes_storage, } } } -impl CredentialExchangeWorker { - /// Create a generic bad request response. - pub fn bad_request<'a>(id: Id, path: &'a str, msg: &'a str) -> ResponseBuilder> { - let e = Error::new(path).with_message(msg); - Response::bad_request(id).body(e) - } - +impl CredentialsServerWorker { async fn handle_request( &mut self, _ctx: &mut Context, @@ -79,13 +75,8 @@ impl CredentialExchangeWorker { let credential: Credential = dec.decode()?; let res = self - .identity - .receive_presented_credential( - sender.clone(), - credential, - self.authorities.iter(), - self.attributes_storage.clone(), - ) + .credentials + .receive_presented_credential(&sender, &self.authorities, credential) .await; match res { @@ -110,13 +101,8 @@ impl CredentialExchangeWorker { let credential: Credential = dec.decode()?; let res = self - .identity - .receive_presented_credential( - sender.clone(), - credential, - self.authorities.iter(), - self.attributes_storage.clone(), - ) + .credentials + .receive_presented_credential(&sender, &self.authorities, credential) .await; if let Err(err) = res { @@ -130,8 +116,7 @@ impl CredentialExchangeWorker { "Mutual credential presentation request processed successfully with {}", sender ); - let credential = self.identity.credential.read().await; - match credential.as_ref() { + match &self.credential { Some(p) if self.present_back => { info!("Mutual credential presentation request processed successfully with {}. Responding with own credential...", sender); Response::ok(req.id()).body(p).to_vec()? @@ -154,10 +139,16 @@ impl CredentialExchangeWorker { }; Ok(r) } + + /// Create a generic bad request response. + pub fn bad_request<'a>(id: Id, path: &'a str, msg: &'a str) -> ResponseBuilder> { + let e = Error::new(path).with_message(msg); + Response::bad_request(id).body(e) + } } #[async_trait] -impl Worker for CredentialExchangeWorker { +impl Worker for CredentialsServerWorker { type Message = Vec; type Context = Context; @@ -175,9 +166,8 @@ impl Worker for CredentialExchangeWorker { } }; - let sender = IdentitySecureChannelLocalInfo::find_info(msg.local_message())? - .their_identity_id() - .clone(); + let sender = + IdentitySecureChannelLocalInfo::find_info(msg.local_message())?.their_identity_id(); let r = match self.handle_request(ctx, &req, sender, &mut dec).await { Ok(r) => r, diff --git a/implementations/rust/ockam/ockam_identity/src/credentials/mod.rs b/implementations/rust/ockam/ockam_identity/src/credentials/mod.rs new file mode 100644 index 00000000000..e9fe4f7f342 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/credentials/mod.rs @@ -0,0 +1,9 @@ +mod credential_issuer; +#[allow(clippy::module_inception)] +mod credentials; +mod credentials_server; +mod credentials_server_worker; + +pub use credential_issuer::*; +pub use credentials::*; +pub use credentials_server::*; diff --git a/implementations/rust/ockam/ockam_identity/src/identities/identities.rs b/implementations/rust/ockam/ockam_identity/src/identities/identities.rs new file mode 100644 index 00000000000..4cfb5f2a402 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/identities.rs @@ -0,0 +1,55 @@ +use crate::identities::{IdentitiesKeys, IdentitiesRepository, IdentitiesVault}; +use crate::{Credentials, CredentialsServer, CredentialsServerModule, IdentitiesCreation}; +use ockam_core::compat::sync::Arc; + +impl Identities { + /// Return the identities vault + pub fn vault(&self) -> Arc { + self.vault.clone() + } + + /// Return the identities repository + pub fn repository(&self) -> Arc { + self.identities_repository.clone() + } + + /// Return the identities keys management service + pub fn identities_keys(&self) -> Arc { + Arc::new(IdentitiesKeys::new(self.vault.clone())) + } + + /// Return the identities creation service + pub fn identities_creation(&self) -> Arc { + Arc::new(IdentitiesCreation::new(self.vault.clone())) + } + + /// Return the identities credentials service + pub fn credentials(&self) -> Arc { + Arc::new(self.clone()) + } + + /// Return the identities credentials server + pub fn credentials_server(&self) -> Arc { + Arc::new(CredentialsServerModule::new(self.credentials())) + } +} + +/// This struct supports all the services related to identities +#[derive(Clone)] +pub struct Identities { + pub(crate) vault: Arc, + pub(crate) identities_repository: Arc, +} + +impl Identities { + /// Create a new identities module + pub(crate) fn new( + vault: Arc, + identities_repository: Arc, + ) -> Identities { + Identities { + vault, + identities_repository, + } + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/identities_builder.rs b/implementations/rust/ockam/ockam_identity/src/identities/identities_builder.rs new file mode 100644 index 00000000000..f17ede42570 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/identities_builder.rs @@ -0,0 +1,66 @@ +use crate::identities::{ + Identities, IdentitiesRepository, IdentitiesStorage, IdentitiesVault, Storage, +}; +use ockam_core::compat::sync::Arc; +use ockam_core::vault::storage::Storage as VaultStorage; +use ockam_vault::Vault; + +/// Builder for Identities services +#[derive(Clone)] +pub struct IdentitiesBuilder { + vault: Arc, + repository: Arc, +} + +/// Return a default identities +pub fn identities() -> Arc { + builder().build() +} + +/// Return a default builder for identities +pub fn builder() -> IdentitiesBuilder { + IdentitiesBuilder { + vault: Vault::create(), + repository: IdentitiesStorage::create(), + } +} + +impl IdentitiesBuilder { + /// Set a specific storage for the identities vault + pub fn with_vault_storage(&mut self, storage: Arc) -> IdentitiesBuilder { + self.with_identities_vault(Arc::new(Vault::new(Some(storage)))) + } + + /// Set a specific identities vault + pub fn with_identities_vault(&mut self, vault: Arc) -> IdentitiesBuilder { + self.vault = vault; + self.clone() + } + + /// Set a specific storage for identities + pub fn with_identities_storage(&mut self, storage: Arc) -> IdentitiesBuilder { + self.with_identities_repository(Arc::new(IdentitiesStorage::new(storage))) + } + + /// Set a specific repository + pub fn with_identities_repository( + &mut self, + repository: Arc, + ) -> IdentitiesBuilder { + self.repository = repository; + self.clone() + } + + fn vault(&self) -> Arc { + self.vault.clone() + } + + fn repository(&self) -> Arc { + self.repository.clone() + } + + /// Build identities + pub fn build(&self) -> Arc { + Arc::new(Identities::new(self.vault(), self.repository())) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/identities_creation.rs b/implementations/rust/ockam/ockam_identity/src/identities/identities_creation.rs new file mode 100644 index 00000000000..94588753efe --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/identities_creation.rs @@ -0,0 +1,108 @@ +use crate::alloc::string::ToString; +use crate::identity::IdentityError; +use crate::{ + IdentitiesKeys, IdentitiesVault, Identity, IdentityChangeConstants, IdentityChangeHistory, + IdentityIdentifier, KeyAttributes, +}; +use ockam_core::compat::sync::Arc; +use ockam_core::compat::vec::Vec; +use ockam_core::vault::Secret::Key; +use ockam_core::vault::{ + KeyId, SecretAttributes, SecretKey, SecretPersistence, SecretType, CURVE25519_SECRET_LENGTH_U32, +}; +use ockam_core::Result; + +/// This struct supports functions for the creation and import of identities using an IdentityVault +pub struct IdentitiesCreation { + vault: Arc, +} + +impl IdentitiesCreation { + /// Create a new identities import module + pub fn new(vault: Arc) -> IdentitiesCreation { + IdentitiesCreation { vault } + } + + /// Import and verify an `Identity` from its change history in a binary format + pub async fn import_identity(&self, data: &[u8]) -> Result { + let change_history = IdentityChangeHistory::import(data)?; + let identity_keys = IdentitiesKeys::new(self.vault.clone()); + identity_keys + .verify_all_existing_changes(&change_history) + .await?; + + let identifier = self.compute_identity_identifier(&change_history).await?; + Ok(Identity::new(identifier, change_history)) + } + + /// Create an identity with a vault initialized with a specific private key + /// encoded as a hex string. + /// Such a key can be obtained by running vault.secret_export and then encoding + /// the exported secret as a hex string + pub async fn import_private_identity( + &self, + identity_history: &str, + secret: &str, + ) -> Result { + let key_attributes = KeyAttributes::default_with_label(IdentityChangeConstants::ROOT_LABEL); + self.vault + .secret_import( + Key(SecretKey::new(hex::decode(secret).unwrap())), + key_attributes.secret_attributes(), + ) + .await?; + let identity_history_data: Vec = + hex::decode(identity_history).map_err(|_| IdentityError::InvalidInternalState)?; + self.import_identity(identity_history_data.as_slice()).await + } + + /// Cryptographically compute `IdentityIdentifier` + pub(super) async fn compute_identity_identifier( + &self, + change_history: &IdentityChangeHistory, + ) -> Result { + let root_public_key = change_history.get_first_root_public_key()?; + let key_id = self + .vault + .compute_key_id_for_public_key(&root_public_key) + .await?; + + Ok(IdentityIdentifier::from_key_id(&key_id)) + } + + /// Make a new identity with its key and attributes + pub(super) async fn make_identity( + &self, + key_id: Option<&KeyId>, + key_attributes: KeyAttributes, + ) -> Result { + let identity_keys = IdentitiesKeys::new(self.vault.clone()); + let change_history = identity_keys + .create_initial_key(key_id, key_attributes.clone()) + .await?; + let identifier = self.compute_identity_identifier(&change_history).await?; + Ok(Identity::new(identifier, change_history)) + } + + /// Create an `Identity` with an external key. Extended version + pub async fn create_identity_with_external_key( + &self, + kid: &KeyId, + attrs: KeyAttributes, + ) -> Result { + self.make_identity(Some(kid), attrs).await + } + + /// Create an Identity + pub async fn create_identity(&self) -> Result { + let attrs = KeyAttributes::new( + IdentityChangeConstants::ROOT_LABEL.to_string(), + SecretAttributes::new( + SecretType::Ed25519, + SecretPersistence::Persistent, + CURVE25519_SECRET_LENGTH_U32, + ), + ); + self.make_identity(None, attrs).await + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/identities_vault.rs b/implementations/rust/ockam/ockam_identity/src/identities/identities_vault.rs new file mode 100644 index 00000000000..80e672fb254 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/identities_vault.rs @@ -0,0 +1,144 @@ +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; +use ockam_core::compat::sync::Arc; +use ockam_core::vault::{AsymmetricVault, KeyId, Signature, Signer}; +use ockam_core::vault::{ + Buffer, Hasher, PublicKey, Secret, SecretAttributes, SecretVault, SmallBuffer, +}; +use ockam_core::vault::{SymmetricVault, Verifier}; +use ockam_core::Result; +use ockam_key_exchange_xx::XXVault; + +/// Traits required for a Vault implementation suitable for use in an Identity +/// Vault with XX required functionality +pub trait IdentitiesVault: XXVault + Signer + Verifier {} + +impl IdentitiesVault for D where D: XXVault + Signer + Verifier {} + +/// This struct is used to compensate for the lack of non-experimental trait upcasting in Rust +/// We encapsulate an IdentitiesVault and delegate the implementation of all the functions of +/// the various traits inherited by IdentitiesVault: SymmetricVault, SecretVault, etc... +struct CoercedIdentitiesVault { + vault: Arc, +} + +#[async_trait] +impl SymmetricVault for CoercedIdentitiesVault { + async fn aead_aes_gcm_encrypt( + &self, + key_id: &KeyId, + plaintext: &[u8], + nonce: &[u8], + aad: &[u8], + ) -> Result> { + self.vault + .aead_aes_gcm_encrypt(key_id, plaintext, nonce, aad) + .await + } + + async fn aead_aes_gcm_decrypt( + &self, + key_id: &KeyId, + cipher_text: &[u8], + nonce: &[u8], + aad: &[u8], + ) -> Result> { + self.vault + .aead_aes_gcm_decrypt(key_id, cipher_text, nonce, aad) + .await + } +} + +#[async_trait] +impl AsymmetricVault for CoercedIdentitiesVault { + async fn ec_diffie_hellman( + &self, + secret: &KeyId, + peer_public_key: &PublicKey, + ) -> Result { + self.vault.ec_diffie_hellman(secret, peer_public_key).await + } + + async fn compute_key_id_for_public_key(&self, public_key: &PublicKey) -> Result { + self.vault.compute_key_id_for_public_key(public_key).await + } +} + +#[async_trait] +impl SecretVault for CoercedIdentitiesVault { + async fn secret_generate(&self, attributes: SecretAttributes) -> Result { + self.vault.secret_generate(attributes).await + } + + async fn secret_import(&self, secret: Secret, attributes: SecretAttributes) -> Result { + self.vault.secret_import(secret, attributes).await + } + + async fn secret_export(&self, key_id: &KeyId) -> Result { + self.vault.secret_export(key_id).await + } + + async fn secret_attributes_get(&self, key_id: &KeyId) -> Result { + self.vault.secret_attributes_get(key_id).await + } + + async fn secret_public_key_get(&self, key_id: &KeyId) -> Result { + self.vault.secret_public_key_get(key_id).await + } + + async fn secret_destroy(&self, key_id: KeyId) -> Result<()> { + self.vault.secret_destroy(key_id).await + } +} + +#[async_trait] +impl Hasher for CoercedIdentitiesVault { + async fn sha256(&self, data: &[u8]) -> Result<[u8; 32]> { + self.vault.sha256(data).await + } + + async fn hkdf_sha256( + &self, + salt: &KeyId, + info: &[u8], + ikm: Option<&KeyId>, + output_attributes: SmallBuffer, + ) -> Result> { + self.vault + .hkdf_sha256(salt, info, ikm, output_attributes) + .await + } +} + +#[async_trait] +impl Signer for CoercedIdentitiesVault { + async fn sign(&self, key_id: &KeyId, data: &[u8]) -> Result { + self.vault.sign(key_id, data).await + } +} + +#[async_trait] +impl Verifier for CoercedIdentitiesVault { + async fn verify( + &self, + signature: &Signature, + public_key: &PublicKey, + data: &[u8], + ) -> Result { + self.vault.verify(signature, public_key, data).await + } +} + +/// Return this vault as a symmetric vault +pub fn to_symmetric_vault(vault: Arc) -> Arc { + Arc::new(CoercedIdentitiesVault { + vault: vault.clone(), + }) +} + +/// Return this vault as a XX vault +pub fn to_xx_vault(vault: Arc) -> Arc { + Arc::new(CoercedIdentitiesVault { + vault: vault.clone(), + }) +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/identity_keys.rs b/implementations/rust/ockam/ockam_identity/src/identities/identity_keys.rs new file mode 100644 index 00000000000..6fb763edd2f --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/identity_keys.rs @@ -0,0 +1,483 @@ +use crate::alloc::string::ToString; +use crate::identities::IdentitiesVault; +use crate::identity::IdentityChange::{CreateKey, RotateKey}; +use crate::identity::IdentityError::InvalidInternalState; +use crate::identity::{ + ChangeIdentifier, CreateKeyChangeData, Identity, IdentityChangeConstants, + IdentityChangeHistory, IdentityError, IdentitySignedChange, KeyAttributes, RotateKeyChangeData, + Signature, SignatureType, +}; +use ockam_core::compat::string::String; +use ockam_core::compat::sync::Arc; +use ockam_core::vault::KeyId; +use ockam_core::{Encodable, Result}; + +/// This module supports the key operations related to identities +pub struct IdentitiesKeys { + vault: Arc, +} + +impl IdentitiesKeys { + pub(crate) async fn create_initial_key( + &self, + key_id: Option<&KeyId>, + key_attribs: KeyAttributes, + ) -> Result { + let initial_change_id = self.make_change_identifier().await?; + let create_key_change = self + .make_create_key_change_static(key_id, initial_change_id, key_attribs.clone(), None) + .await?; + let change_history = IdentityChangeHistory::new(create_key_change); + + // Sanity checks + change_history.check_entire_consistency()?; + self.verify_all_existing_changes(&change_history).await?; + + Ok(change_history) + } + + /// Initial `ChangeIdentifier` that is used as a previous_identifier of the first change + async fn make_change_identifier(&self) -> Result { + let h = match self + .vault + .sha256(IdentityChangeConstants::INITIAL_CHANGE) + .await + { + Ok(hash) => hash, + Err(_) => panic!("failed to hash initial change"), + }; + Ok(ChangeIdentifier::from_hash(h)) + } +} + +/// Public functions +impl IdentitiesKeys { + /// Create a new identities keys module + pub fn new(vault: Arc) -> Self { + Self { vault } + } + + /// Create a new identities keys module with an in-memory vault + /// Sign some binary data with the signing key of an identity + pub async fn create_signature( + &self, + identity: &Identity, + data: &[u8], + key_label: Option<&str>, + ) -> Result { + let secret = self.get_secret_key(identity, key_label).await?; + self.vault.sign(&secret, data).await + } + + /// Verify the signature of a piece of data + pub async fn verify_signature( + &self, + identity: &Identity, + signature: &ockam_core::vault::Signature, + data: &[u8], + key_label: Option<&str>, + ) -> Result { + let public_key = identity.get_public_key(key_label)?; + self.vault.verify(signature, &public_key, data).await + } + + /// Generate and add a new key to this `Identity` with a given `label` + pub async fn create_key(&self, identity: &mut Identity, label: String) -> Result<()> { + let key_attribs = KeyAttributes::default_with_label(label); + let change = self + .make_create_key_change(identity, None, key_attribs) + .await?; + identity.add_change(change) + } + + /// Rotate an existing key with a given label + pub async fn rotate_key(&self, identity: &mut Identity, label: &str) -> Result<()> { + let change = self + .make_rotate_key_change( + identity, + KeyAttributes::default_with_label(label.to_string()), + ) + .await?; + + identity.add_change(change) + } + + /// Add a new key to this `Identity` with a given `label` + pub async fn add_key( + &self, + identity: &mut Identity, + label: String, + secret: &KeyId, + ) -> Result<()> { + let secret_attributes = self.vault.secret_attributes_get(secret).await?; + let key_attribs = KeyAttributes::new(label, secret_attributes); + + let change = self + .make_create_key_change(identity, Some(secret), key_attribs) + .await?; + + identity.add_change(change) + } + + /// Verify all changes present in current `IdentityChangeHistory` + pub(crate) async fn verify_all_existing_changes( + &self, + identity_changes: &IdentityChangeHistory, + ) -> Result<()> { + for i in 0..identity_changes.as_ref().len() { + let existing_changes = &identity_changes.as_ref()[..i]; + let new_change = &identity_changes.as_ref()[i]; + self.verify_change(existing_changes, new_change).await? + } + Ok(()) + } + + /// Return the secret key of an identity + pub async fn get_secret_key( + &self, + identity: &Identity, + key_label: Option<&str>, + ) -> Result { + let key = match key_label { + Some(label) => self.get_labelled_key(identity, label).await?, + None => self.get_root_secret_key(identity).await?, + }; + Ok(key) + } + + /// Rotate this `Identity` root key + pub async fn rotate_root_key(&self, identity: &mut Identity) -> Result<()> { + let change = self + .make_rotate_key_change( + identity, + KeyAttributes::default_with_label(IdentityChangeConstants::ROOT_LABEL.to_string()), + ) + .await?; + + identity.add_change(change) + } +} + +/// Private functions +impl IdentitiesKeys { + /// Create a new key + async fn make_create_key_change( + &self, + identity: &Identity, + secret: Option<&KeyId>, + key_attributes: KeyAttributes, + ) -> Result { + let change_history = identity.change_history(); + // Creating key after it was revoked is forbidden + if IdentityChangeHistory::find_last_key_change( + change_history.as_ref(), + key_attributes.label(), + ) + .is_ok() + { + return Err(InvalidInternalState.into()); + } + + let prev_id = match change_history.get_last_change_id() { + Ok(prev_id) => prev_id, + Err(_) => self.make_change_identifier().await?, + }; + + let root_secret = self.get_root_secret_key(identity).await?; + let root_key = Some(&root_secret); + + self.make_create_key_change_static(secret, prev_id, key_attributes, root_key) + .await + } + + /// Create a new key + async fn make_create_key_change_static( + &self, + secret: Option<&KeyId>, + prev_id: ChangeIdentifier, + key_attributes: KeyAttributes, + root_key: Option<&KeyId>, + ) -> Result { + let secret_key = self.generate_key_if_needed(secret, &key_attributes).await?; + let public_key = self.vault.secret_public_key_get(&secret_key).await?; + + let data = CreateKeyChangeData::new(prev_id, key_attributes, public_key); + + let change_block = CreateKey(data); + let change_block_binary = change_block + .encode() + .map_err(|_| IdentityError::BareError)?; + + let change_id = self.vault.sha256(&change_block_binary).await?; + let change_id = ChangeIdentifier::from_hash(change_id); + + let self_signature = self.vault.sign(&secret_key, change_id.as_ref()).await?; + let self_signature = Signature::new(SignatureType::SelfSign, self_signature); + + let mut signatures = vec![self_signature]; + + // If we have root_key passed we should sign using it + // If there is no root_key - we're creating new identity, so we just generated root_key + if let Some(root_key) = root_key { + let root_signature = self.vault.sign(root_key, change_id.as_ref()).await?; + let root_signature = Signature::new(SignatureType::RootSign, root_signature); + + signatures.push(root_signature); + } + + let signed_change = IdentitySignedChange::new(change_id, change_block, signatures); + + Ok(signed_change) + } + + async fn generate_key_if_needed( + &self, + secret: Option<&KeyId>, + key_attributes: &KeyAttributes, + ) -> Result { + if let Some(s) = secret { + Ok(s.clone()) + } else { + self.vault + .secret_generate(key_attributes.secret_attributes()) + .await + } + } + + async fn get_labelled_key(&self, identity: &Identity, label: &str) -> Result { + let change = + IdentityChangeHistory::find_last_key_change(identity.change_history().as_ref(), label)? + .clone(); + self.get_secret_key_from_change(&change).await + } + + /// Rotate key change + async fn make_rotate_key_change( + &self, + identity: &mut Identity, + key_attributes: KeyAttributes, + ) -> Result { + let prev_change_id = identity.change_history.get_last_change_id()?; + + let last_change_in_chain = IdentityChangeHistory::find_last_key_change( + identity.change_history.as_ref(), + key_attributes.label(), + )? + .clone(); + + let last_key_in_chain = self + .get_secret_key_from_change(&last_change_in_chain) + .await?; + + let secret_attributes = key_attributes.secret_attributes(); + + let secret_key = self.vault.secret_generate(secret_attributes).await?; + let public_key = self.vault.secret_public_key_get(&secret_key).await?; + + let data = RotateKeyChangeData::new(prev_change_id, key_attributes, public_key); + + let change_block = RotateKey(data); + let change_block_binary = change_block + .encode() + .map_err(|_| IdentityError::BareError)?; + + let change_id = self.vault.sha256(&change_block_binary).await?; + let change_id = ChangeIdentifier::from_hash(change_id); + + let self_signature = self.vault.sign(&secret_key, change_id.as_ref()).await?; + let self_signature = Signature::new(SignatureType::SelfSign, self_signature); + + let root_key = self.get_root_secret_key(identity).await?; + + let root_signature = self.vault.sign(&root_key, change_id.as_ref()).await?; + let root_signature = Signature::new(SignatureType::RootSign, root_signature); + + let prev_signature = self + .vault + .sign(&last_key_in_chain, change_id.as_ref()) + .await?; + let prev_signature = Signature::new(SignatureType::PrevSign, prev_signature); + + let signed_change = IdentitySignedChange::new( + change_id, + change_block, + vec![self_signature, root_signature, prev_signature], + ); + + Ok(signed_change) + } + + /// Get [`Secret`] key. Key is uniquely identified by label in [`KeyAttributes`] + async fn get_root_secret_key(&self, identity: &Identity) -> Result { + self.get_labelled_key(identity, IdentityChangeConstants::ROOT_LABEL) + .await + } + + async fn get_secret_key_from_change(&self, change: &IdentitySignedChange) -> Result { + let public_key = change.change().public_key()?; + self.vault.compute_key_id_for_public_key(&public_key).await + } + + /// Verify all changes present in current `IdentityChangeHistory` + pub async fn verify_changes(&self, identity: &Identity) -> Result<()> { + self.verify_all_existing_changes(&identity.change_history()) + .await + } + + /// WARNING: This function assumes all existing changes in chain are verified. + /// WARNING: Correctness of changes sequence is not verified here. + async fn verify_change( + &self, + existing_changes: &[IdentitySignedChange], + new_change: &IdentitySignedChange, + ) -> Result<()> { + let change_binary = new_change + .change() + .encode() + .map_err(|_| IdentityError::BareError)?; + + let change_id = self.vault.sha256(&change_binary).await?; + let change_id = ChangeIdentifier::from_hash(change_id); + + if &change_id != new_change.identifier() { + return Err(IdentityError::IdentityVerificationFailed.into()); // ChangeIdDoesNotMatch + } + + struct SignaturesCheck { + self_sign: u8, + prev_sign: u8, + root_sign: u8, + } + + let mut signatures_check = match new_change.change() { + CreateKey(_) => { + // Should have self signature and root signature + // There is no Root signature for the very first change + let root_sign = u8::from(!existing_changes.is_empty()); + + SignaturesCheck { + self_sign: 1, + prev_sign: 0, + root_sign, + } + } + RotateKey(_) => { + // Should have self signature, root signature, and previous key signature + SignaturesCheck { + self_sign: 1, + prev_sign: 1, + root_sign: 1, + } + } + }; + + for signature in new_change.signatures() { + let counter; + let public_key = match signature.stype() { + SignatureType::RootSign => { + if existing_changes.is_empty() { + return Err(IdentityError::IdentityVerificationFailed.into()); + } + + counter = &mut signatures_check.root_sign; + IdentityChangeHistory::get_current_root_public_key(existing_changes)? + } + SignatureType::SelfSign => { + counter = &mut signatures_check.self_sign; + new_change.change().public_key()? + } + SignatureType::PrevSign => { + counter = &mut signatures_check.prev_sign; + IdentityChangeHistory::get_public_key_static( + existing_changes, + new_change.change().label(), + )? + } + }; + + if *counter == 0 { + return Err(IdentityError::IdentityVerificationFailed.into()); + } + + if !self + .vault + .verify(signature.data(), &public_key, change_id.as_ref()) + .await? + { + return Err(IdentityError::IdentityVerificationFailed.into()); + } + + *counter -= 1; + } + + if signatures_check.prev_sign == 0 + && signatures_check.root_sign == 0 + && signatures_check.self_sign == 0 + { + Ok(()) + } else { + Err(IdentityError::IdentityVerificationFailed.into()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::identities; + use ockam_core::errcode::{Kind, Origin}; + use ockam_core::Error; + use ockam_node::Context; + + fn test_error>(error: S) -> Result<()> { + Err(Error::new_without_cause(Origin::Identity, Kind::Unknown).context("msg", error.into())) + } + + #[ockam_macros::test] + async fn test_basic_identity_key_ops(ctx: &mut Context) -> Result<()> { + let identities = identities(); + let identity_keys = identities.identities_keys(); + let mut identity = identities.identities_creation().create_identity().await?; + + identity_keys.verify_changes(&identity).await?; + let secret1 = identity_keys.get_root_secret_key(&identity).await?; + let public1 = identity.get_root_public_key()?; + + identity_keys + .create_key(&mut identity, "Truck management".to_string()) + .await?; + identity_keys.verify_changes(&identity).await?; + + let secret2 = identity_keys + .get_labelled_key(&identity, "Truck management") + .await?; + let public2 = identity.get_public_key(Some("Truck management"))?; + + if secret1 == secret2 { + return test_error("secret did not change after create_key"); + } + + if public1 == public2 { + return test_error("public did not change after create_key"); + } + + identity_keys.rotate_root_key(&mut identity).await?; + identity_keys.verify_changes(&identity).await?; + + let secret3 = identity_keys.get_root_secret_key(&identity).await?; + let public3 = identity.get_root_public_key()?; + + identity_keys.rotate_root_key(&mut identity).await?; + identity_keys.verify_changes(&identity).await?; + + if secret1 == secret3 { + return test_error("secret did not change after rotate_key"); + } + + if public1 == public3 { + return test_error("public did not change after rotate_key"); + } + + ctx.stop().await + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/mod.rs b/implementations/rust/ockam/ockam_identity/src/identities/mod.rs new file mode 100644 index 00000000000..557f38ccf8e --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/mod.rs @@ -0,0 +1,19 @@ +#[allow(clippy::module_inception)] +mod identities; +mod identities_builder; +mod identities_creation; +mod identities_vault; +mod identity_keys; + +/// Identities storage functions +pub mod storage; + +pub use identities::*; +pub use identities_builder::*; +pub use identities_creation::*; +pub use identities_vault::*; +pub use identity_keys::*; +pub use storage::*; + +#[cfg(test)] +mod tests; diff --git a/implementations/rust/ockam/ockam_identity/src/identities/storage/attributes_entry.rs b/implementations/rust/ockam/ockam_identity/src/identities/storage/attributes_entry.rs new file mode 100644 index 00000000000..4e52c6a1fb4 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/storage/attributes_entry.rs @@ -0,0 +1,58 @@ +use crate::alloc::borrow::ToOwned; +use crate::credential::Timestamp; +use crate::identity::IdentityIdentifier; +use minicbor::{Decode, Encode}; +use ockam_core::compat::{collections::BTreeMap, string::String, vec::Vec}; +use serde::{Deserialize, Serialize}; + +/// An entry on the AuthenticatedIdentities table. +#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, Serialize, Deserialize)] +#[rustfmt::skip] +#[cbor(map)] +pub struct AttributesEntry { + #[b(1)] attrs: BTreeMap>, + #[n(2)] added: Timestamp, + #[n(3)] expires: Option, + #[n(4)] attested_by: Option, +} + +impl AttributesEntry { + //TODO: since we are converting from HashMap to BTreeMap in different parts, + // it will make sense to have a constructor here taking a HashMap and doing + // the conversion here. Better: standarize on either of the above for attributes. + + /// Constructor + pub fn new( + attrs: BTreeMap>, + added: Timestamp, + expires: Option, + attested_by: Option, + ) -> Self { + Self { + attrs, + added, + expires, + attested_by, + } + } + + /// The entry attributes + pub fn attrs(&self) -> &BTreeMap> { + &self.attrs + } + + /// Expiration time for this entry + pub fn expires(&self) -> Option { + self.expires + } + + /// Date that the entry was added + pub fn added(&self) -> Timestamp { + self.added + } + + /// Who attested this attributes for this identity identifier + pub fn attested_by(&self) -> Option { + self.attested_by.to_owned() + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/storage/identities_repository.rs b/implementations/rust/ockam/ockam_identity/src/identities/storage/identities_repository.rs new file mode 100644 index 00000000000..53c4810f87f --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/storage/identities_repository.rs @@ -0,0 +1,240 @@ +use crate::alloc::string::ToString; +use crate::credential::Timestamp; +use crate::identities::storage::storage::{InMemoryStorage, Storage}; +use crate::identity::IdentityHistoryComparison; +use crate::identity::{Identity, IdentityChangeConstants, IdentityError, IdentityIdentifier}; +use crate::AttributesEntry; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; +use ockam_core::compat::sync::Arc; +use ockam_core::compat::vec::Vec; +use ockam_core::errcode::{Kind, Origin}; +use ockam_core::Result; + +/// Repository for data related to identities: key changes and attributes +#[async_trait] +pub trait IdentitiesRepository: + IdentityAttributesReader + IdentityAttributesWriter + IdentitiesReader + IdentitiesWriter +{ + /// Restrict this repository as a reader for attributes + fn as_attributes_reader(&self) -> Arc; + + /// Restrict this repository as a writer for attributes + fn as_attributes_writer(&self) -> Arc; +} + +#[async_trait] +impl IdentitiesRepository for IdentitiesStorage { + fn as_attributes_reader(&self) -> Arc { + Arc::new(self.clone()) + } + + fn as_attributes_writer(&self) -> Arc { + Arc::new(self.clone()) + } +} + +/// Trait implementing read access to attributes +#[async_trait] +pub trait IdentityAttributesReader: Send + Sync + 'static { + /// Get the attributes associated with the given identity identifier + async fn get_attributes( + &self, + identity: &IdentityIdentifier, + ) -> Result>; + + /// List all identities with their attributes + async fn list(&self) -> Result>; +} + +/// Trait implementing write access to attributes +#[async_trait] +pub trait IdentityAttributesWriter: Send + Sync + 'static { + /// Set the attributes associated with the given identity identifier. + /// Previous values gets overridden. + async fn put_attributes( + &self, + identity: &IdentityIdentifier, + entry: AttributesEntry, + ) -> Result<()>; + + /// Remove all attributes for a given identity identifier + async fn delete(&self, identity: &IdentityIdentifier) -> Result<()>; +} + +/// Trait implementing write access to identities +#[async_trait] +pub trait IdentitiesWriter: Send + Sync + 'static { + /// Persist an identity + async fn put_identity(&self, identity: &Identity) -> Result<()>; + + /// Store changes if there are new key changes associated to that identity + /// Return an error if the current change history conflicts with the persisted one + async fn update_known_identity(&self, identity: &Identity) -> Result<()>; +} + +/// Trait implementing read access to identiets +#[async_trait] +pub trait IdentitiesReader: Send + Sync + 'static { + /// Return a persisted identity + async fn get_identity(&self, identifier: &IdentityIdentifier) -> Result>; +} + +/// Implementation of `IdentityAttributes` trait based on an underlying `Storage` +#[derive(Clone)] +pub struct IdentitiesStorage { + storage: Arc, +} + +impl Default for IdentitiesStorage { + fn default() -> IdentitiesStorage { + IdentitiesStorage { + storage: Arc::new(InMemoryStorage::new()), + } + } +} + +impl IdentitiesStorage { + /// Create a new storage for attributes + pub fn new(storage: Arc) -> Self { + Self { storage } + } + + /// Create a new storage for attributes + pub fn create() -> Arc { + Arc::new(Self::default()) + } +} + +#[async_trait] +impl IdentityAttributesReader for IdentitiesStorage { + async fn get_attributes( + &self, + identity_id: &IdentityIdentifier, + ) -> Result> { + let id = identity_id.to_string(); + let entry = match self + .storage + .get(&id, IdentityChangeConstants::ATTRIBUTES_KEY) + .await? + { + Some(e) => e, + None => return Ok(None), + }; + + let entry: AttributesEntry = minicbor::decode(&entry)?; + + let now = Timestamp::now().ok_or_else(|| { + ockam_core::Error::new(Origin::Core, Kind::Internal, "invalid system time") + })?; + match entry.expires() { + Some(exp) if exp <= now => { + self.storage + .del(&id, IdentityChangeConstants::ATTRIBUTES_KEY) + .await?; + Ok(None) + } + _ => Ok(Some(entry)), + } + } + + async fn list(&self) -> Result> { + let mut l = Vec::new(); + for id in self + .storage + .keys(IdentityChangeConstants::ATTRIBUTES_KEY) + .await? + { + let identity_identifier = IdentityIdentifier::try_from(id)?; + if let Some(attrs) = self.get_attributes(&identity_identifier).await? { + l.push((identity_identifier, attrs)) + } + } + Ok(l) + } +} + +#[async_trait] +impl IdentityAttributesWriter for IdentitiesStorage { + async fn put_attributes( + &self, + sender: &IdentityIdentifier, + entry: AttributesEntry, + ) -> Result<()> { + // TODO: Implement expiration mechanism in Storage + let entry = minicbor::to_vec(&entry)?; + + self.storage + .set( + &sender.to_string(), + IdentityChangeConstants::ATTRIBUTES_KEY.to_string(), + entry, + ) + .await?; + + Ok(()) + } + + async fn delete(&self, identity: &IdentityIdentifier) -> Result<()> { + self.storage + .del( + identity.to_string().as_str(), + IdentityChangeConstants::ATTRIBUTES_KEY, + ) + .await + } +} + +#[async_trait] +impl IdentitiesWriter for IdentitiesStorage { + async fn put_identity(&self, identity: &Identity) -> Result<()> { + self.storage + .set( + &identity.identifier().to_string(), + IdentityChangeConstants::CHANGE_HISTORY_KEY.to_string(), + identity.export()?, + ) + .await + } + + async fn update_known_identity(&self, identity: &Identity) -> Result<()> { + let should_set = if let Some(known) = self.get_identity(&identity.identifier()).await? { + match identity.changes().compare(known.changes()) { + IdentityHistoryComparison::Equal => false, /* Do nothing */ + IdentityHistoryComparison::Conflict => { + return Err(IdentityError::ConsistencyError.into()) + } + IdentityHistoryComparison::Newer => true, /* Update */ + IdentityHistoryComparison::Older => { + return Err(IdentityError::ConsistencyError.into()) + } + } + } else { + true + }; + + if should_set { + self.put_identity(identity).await?; + } + + Ok(()) + } +} + +#[async_trait] +impl IdentitiesReader for IdentitiesStorage { + async fn get_identity(&self, identifier: &IdentityIdentifier) -> Result> { + if let Some(data) = self + .storage + .get( + &identifier.to_string(), + IdentityChangeConstants::CHANGE_HISTORY_KEY, + ) + .await? + { + Ok(Some(Identity::import(identifier, &data)?)) + } else { + Ok(None) + } + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identities/storage/mod.rs b/implementations/rust/ockam/ockam_identity/src/identities/storage/mod.rs new file mode 100644 index 00000000000..b111aea7792 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identities/storage/mod.rs @@ -0,0 +1,8 @@ +mod attributes_entry; +mod identities_repository; +#[allow(clippy::module_inception)] +mod storage; + +pub use attributes_entry::*; +pub use identities_repository::*; +pub use storage::*; diff --git a/implementations/rust/ockam/ockam_identity/src/authenticated_storage/mem.rs b/implementations/rust/ockam/ockam_identity/src/identities/storage/storage.rs similarity index 71% rename from implementations/rust/ockam/ockam_identity/src/authenticated_storage/mem.rs rename to implementations/rust/ockam/ockam_identity/src/identities/storage/storage.rs index db974c68b84..62568c2d6d3 100644 --- a/implementations/rust/ockam/ockam_identity/src/authenticated_storage/mem.rs +++ b/implementations/rust/ockam/ockam_identity/src/identities/storage/storage.rs @@ -1,4 +1,3 @@ -use super::AuthenticatedStorage; use ockam_core::async_trait; use ockam_core::compat::{ boxed::Box, @@ -9,7 +8,22 @@ use ockam_core::compat::{ }; use ockam_core::Result; -type Attributes = BTreeMap>; +/// Storage for Authenticated data +#[async_trait] +pub trait Storage: Send + Sync + 'static { + /// Get entry + async fn get(&self, id: &str, key: &str) -> Result>>; + + /// Set entry + async fn set(&self, id: &str, key: String, val: Vec) -> Result<()>; + + /// Delete entry + async fn del(&self, id: &str, key: &str) -> Result<()>; + + /// List all keys of a given "type". TODO: we shouldn't store different things on a single + /// store. + async fn keys(&self, namespace: &str) -> Result>; +} /// Non-persistent table stored in RAM #[derive(Clone, Default)] @@ -17,15 +31,22 @@ pub struct InMemoryStorage { map: Arc>>, } +type Attributes = BTreeMap>; + impl InMemoryStorage { /// Constructor pub fn new() -> Self { Default::default() } + + /// Constructor + pub fn create() -> Arc { + Arc::new(Self::new()) + } } #[async_trait] -impl AuthenticatedStorage for InMemoryStorage { +impl Storage for InMemoryStorage { async fn get(&self, id: &str, namespace: &str) -> Result>> { let m = self.map.read().unwrap(); if let Some(a) = m.get(namespace) { diff --git a/implementations/rust/ockam/ockam_identity/src/invalid_signatures_tests.rs b/implementations/rust/ockam/ockam_identity/src/identities/tests.rs similarity index 67% rename from implementations/rust/ockam/ockam_identity/src/invalid_signatures_tests.rs rename to implementations/rust/ockam/ockam_identity/src/identities/tests.rs index 73e57f98aad..65158eb359c 100644 --- a/implementations/rust/ockam/ockam_identity/src/invalid_signatures_tests.rs +++ b/implementations/rust/ockam/ockam_identity/src/identities/tests.rs @@ -1,13 +1,13 @@ -use crate::change::IdentitySignedChange; -use crate::change_history::IdentityChangeHistory; -use crate::{Identity, IdentityVault, PublicIdentity}; +use crate::identities::{self, IdentitiesKeys}; +use crate::identity::identity_change::IdentitySignedChange; +use crate::identity::{Identity, IdentityChangeHistory}; +use ockam_core::async_trait; use ockam_core::compat::sync::Arc; use ockam_core::vault::{ AsymmetricVault, Buffer, Hasher, KeyId, PublicKey, Secret, SecretAttributes, SecretVault, Signature, Signer, SmallBuffer, SymmetricVault, Verifier, }; use ockam_core::Result; -use ockam_core::{async_trait, compat::boxed::Box}; use ockam_node::Context; use ockam_vault::Vault; use rand::distributions::Standard; @@ -16,39 +16,118 @@ use rand::{thread_rng, Rng}; use std::collections::HashSet; use std::sync::atomic::{AtomicBool, Ordering}; -impl Identity { - pub async fn eject_random_signature(self) -> Result { - let mut history = self.change_history.read().await.as_ref().to_vec(); +#[ockam_macros::test] +async fn test_invalid_signature(ctx: &mut Context) -> Result<()> { + for _ in 0..100 { + let crazy_vault = Arc::new(CrazyVault::new(0.1, Vault::default())); + let identities = identities::builder() + .with_identities_vault(crazy_vault.clone()) + .build(); + let mut identity = identities.identities_creation().create_identity().await?; + let res = check_identity(&mut identity).await; + + if crazy_vault.forged_operation_occurred() { + assert!(res.is_err()); + break; + } else { + assert!(res.is_ok()) + } - let i = thread_rng().gen_range(0..history.len()); - let change = &mut history[i]; - let mut signatures = change.signatures().to_vec(); + loop { + identities + .identities_keys() + .random_change(&mut identity) + .await?; + + let res = identities::identities() + .identities_creation() + .import_identity(&identity.export()?) + .await; + if crazy_vault.forged_operation_occurred() { + assert!(res.is_err()); + break; + } else { + assert!(res.is_ok()) + } + } + } - signatures.remove(thread_rng().gen_range(0..signatures.len())); + ctx.stop().await?; - history[i] = IdentitySignedChange::new( - change.identifier().clone(), - change.change().clone(), - signatures, - ); + Ok(()) +} - let mut new_history = IdentityChangeHistory::new(history[0].clone()); +/// This function simulates an identity import to check its history +async fn check_identity(identity: &mut Identity) -> Result { + identities::identities() + .identities_creation() + .import_identity(&identity.export()?) + .await +} - for change in history.into_iter().skip(1) { - new_history.check_consistency_and_add_change(change)? +#[ockam_macros::test] +async fn test_eject_signatures(ctx: &mut Context) -> Result<()> { + let crazy_vault = CrazyVault::new(0.1, Vault::default()); + + for _ in 0..100 { + let identities = crate::identities::builder() + .with_identities_vault(Arc::new(crazy_vault.clone())) + .build(); + let mut identity = identities.identities_creation().create_identity().await?; + + let j: i32 = thread_rng().gen_range(0..10); + for _ in 0..j { + identities + .identities_keys() + .random_change(&mut identity) + .await?; } - Ok(Identity::new( - self.identifier().clone(), - new_history, - self.ctx, - self.authenticated_storage, - self.secure_channel_registry, - self.vault, - )) + let res = identities + .identities_creation() + .import_identity(&identity.export()?) + .await; + assert!(res.is_ok()); + + let identity = eject_random_signature(&identity)?; + let res = identities + .identities_creation() + .import_identity(&identity.export()?) + .await; + assert!(res.is_err()); } - async fn random_change(&self) -> Result<()> { + ctx.stop().await?; + + Ok(()) +} + +pub fn eject_random_signature(identity: &Identity) -> Result { + let mut history = identity.change_history().as_ref().to_vec(); + + let i = thread_rng().gen_range(0..history.len()); + let change = &mut history[i]; + let mut signatures = change.signatures().to_vec(); + + signatures.remove(thread_rng().gen_range(0..signatures.len())); + + history[i] = IdentitySignedChange::new( + change.identifier().clone(), + change.change().clone(), + signatures, + ); + + let mut new_history = IdentityChangeHistory::new(history[0].clone()); + + for change in history.into_iter().skip(1) { + new_history.check_consistency_and_add_change(change)? + } + + Ok(new_history) +} + +impl IdentitiesKeys { + async fn random_change(&self, identity: &mut Identity) -> Result<()> { enum Action { CreateKey, RotateKey, @@ -70,16 +149,16 @@ impl Identity { Action::CreateKey => { let label: [u8; 16] = thread_rng().gen(); let label = hex::encode(label); - self.create_key(label).await?; + self.create_key(identity, label).await?; } Action::RotateKey => { let mut present_keys = HashSet::::new(); - for change in self.change_history.read().await.as_ref() { + for change in identity.change_history().as_ref() { present_keys.insert(change.change().label().to_string()); } let present_keys: Vec = present_keys.into_iter().collect(); let index = thread_rng().gen_range(0..present_keys.len()); - self.rotate_key(&present_keys[index]).await?; + self.rotate_key(identity, &present_keys[index]).await?; } } @@ -91,7 +170,7 @@ impl Identity { struct CrazyVault { prob_to_produce_invalid_signature: f32, forged_operation_occurred: Arc, - vault: Arc, + vault: Vault, } impl CrazyVault { @@ -101,11 +180,11 @@ impl CrazyVault { } impl CrazyVault { - pub fn new(prob_to_produce_invalid_signature: f32, vault: Arc) -> Self { + pub fn new(prob_to_produce_invalid_signature: f32, vault: Vault) -> Self { Self { prob_to_produce_invalid_signature, forged_operation_occurred: Arc::new(false.into()), - vault: vault.clone(), + vault, } } } @@ -228,63 +307,3 @@ impl Verifier for CrazyVault { self.vault.verify(signature, public_key, data).await } } - -#[ockam_macros::test] -async fn test_invalid_signature(ctx: &mut Context) -> Result<()> { - for _ in 0..100 { - let vault = Vault::create(); - let crazy_vault = CrazyVault::new(0.1, vault.clone()); - let crazy_identity_vault: Arc = Arc::new(crazy_vault.clone()); - - let identity = Identity::create(ctx, crazy_identity_vault).await?; - - let res = PublicIdentity::import(&identity.export().await?, Vault::create()).await; - if crazy_vault.forged_operation_occurred() { - assert!(res.is_err()); - break; - } else { - assert!(res.is_ok()) - } - - loop { - identity.random_change().await?; - - let res = PublicIdentity::import(&identity.export().await?, Vault::create()).await; - if crazy_vault.forged_operation_occurred() { - assert!(res.is_err()); - break; - } else { - assert!(res.is_ok()) - } - } - } - - ctx.stop().await?; - - Ok(()) -} - -#[ockam_macros::test] -async fn test_eject_signatures(ctx: &mut Context) -> Result<()> { - for _ in 0..100 { - let vault = Vault::create(); - - let identity = Identity::create(ctx, vault).await?; - - let j: i32 = thread_rng().gen_range(0..10); - for _ in 0..j { - identity.random_change().await?; - } - - let res = PublicIdentity::import(&identity.export().await?, Vault::create()).await; - assert!(res.is_ok()); - - let identity = identity.eject_random_signature().await?; - let res = PublicIdentity::import(&identity.export().await?, Vault::create()).await; - assert!(res.is_err()); - } - - ctx.stop().await?; - - Ok(()) -} diff --git a/implementations/rust/ockam/ockam_identity/src/identity.rs b/implementations/rust/ockam/ockam_identity/src/identity.rs deleted file mode 100644 index 42dbfeb30d8..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/identity.rs +++ /dev/null @@ -1,564 +0,0 @@ -use crate::alloc::string::ToString; -use crate::authenticated_storage::mem::InMemoryStorage; -use crate::authenticated_storage::AuthenticatedStorage; -use crate::change::IdentitySignedChange; -use crate::change_history::{IdentityChangeHistory, IdentityHistoryComparison}; -use crate::credential::Credential; -use crate::{ - to_hasher, ChangeIdentifier, IdentityError, IdentityIdentifier, IdentityVault, KeyAttributes, - PublicIdentity, SecureChannelRegistry, -}; -use ockam_core::compat::{boxed::Box, string::String, sync::Arc, vec::Vec}; -use ockam_core::vault::Secret::Key; -use ockam_core::vault::{ - SecretKey, SecretPersistence, SecretType, Signature, CURVE25519_SECRET_LENGTH_U32, -}; -use ockam_core::{Address, Result}; -use ockam_core::{AsyncTryClone, DenyAll}; -use ockam_node::compat::asynchronous::RwLock; -use ockam_node::Context; -use ockam_vault::Hasher; -use ockam_vault::{KeyId, SecretAttributes}; - -/// Identity implementation -#[derive(AsyncTryClone)] -#[async_try_clone(crate = "ockam_core")] -pub struct Identity { - id: IdentityIdentifier, - pub(crate) credential: Arc>>, - pub(crate) change_history: Arc>, - pub(crate) ctx: Context, - pub(crate) authenticated_storage: Arc, - pub(crate) secure_channel_registry: SecureChannelRegistry, - pub(crate) vault: Arc, -} - -/// `Identity`-related constants -pub struct IdentityStateConst; - -impl IdentityStateConst { - /// Sha256 of that value is used as previous change id for first change in a - /// [`crate::Identity`] - pub const INITIAL_CHANGE: &'static [u8] = "OCKAM_INITIAL_CHANGE".as_bytes(); - /// Label for [`crate::Identity`] update key - pub const ROOT_LABEL: &'static str = "OCKAM_RK"; - /// Change history key for AuthenticatedStorage - pub const CHANGE_HISTORY_KEY: &'static str = "CHANGE_HISTORY"; - /// Attributes key for AuthenticatedStorage - pub const ATTRIBUTES_KEY: &'static str = "ATTRIBUTES"; -} - -impl Identity { - /// Identity constructor - pub(crate) fn new( - id: IdentityIdentifier, - change_history: IdentityChangeHistory, - ctx: Context, - authenticated_storage: Arc, - secure_channel_registry: SecureChannelRegistry, - vault: Arc, - ) -> Self { - Self { - id, - credential: Arc::new(RwLock::new(None)), - change_history: Arc::new(RwLock::new(change_history)), - ctx, - authenticated_storage, - secure_channel_registry, - vault, - } - } - - /// Import and verify an `Identity` from the binary format. Extended version - pub async fn import_ext( - ctx: &Context, - data: &[u8], - authenticated_storage: Arc, - secure_channel_registry: &SecureChannelRegistry, - vault: Arc, - ) -> Result { - let change_history = IdentityChangeHistory::import(data)?; - if !change_history - .verify_all_existing_changes(vault.clone()) - .await? - { - return Err(IdentityError::IdentityVerificationFailed.into()); - } - let child_ctx = ctx - .new_detached( - Address::random_tagged("Identity.import.detached"), - DenyAll, - DenyAll, - ) - .await?; - - let id = change_history.compute_identity_id(vault.clone()).await?; - - let identity = Self::new( - id, - change_history, - child_ctx, - authenticated_storage, - secure_channel_registry.clone(), - vault.clone(), - ); - - Ok(identity) - } - - async fn create_impl( - ctx: &Context, - authenticated_storage: Arc, - secure_channel_registry: SecureChannelRegistry, - vault: Arc, - kid: Option<&KeyId>, - key_attribs: KeyAttributes, - ) -> Result { - let child_ctx = ctx - .new_detached( - Address::random_tagged("Identity.create.detached"), - DenyAll, - DenyAll, - ) - .await?; - let hasher: Arc = to_hasher(vault.clone()); - let initial_change_id = ChangeIdentifier::initial(hasher).await; - - let create_key_change = Self::make_create_key_change_static( - kid, - initial_change_id, - key_attribs.clone(), - None, - vault.clone(), - ) - .await?; - - let change_history = IdentityChangeHistory::new(create_key_change); - - // Sanity check - if !change_history.check_entire_consistency() { - return Err(IdentityError::ConsistencyError.into()); - } - - // Sanity check - if !change_history - .verify_all_existing_changes(vault.clone()) - .await? - { - return Err(IdentityError::IdentityVerificationFailed.into()); - } - - let id = change_history.compute_identity_id(vault.clone()).await?; - - let identity = Self::new( - id, - change_history, - child_ctx, - authenticated_storage, - secure_channel_registry, - vault.clone(), - ); - - Ok(identity) - } - - /// Create an `Identity`. Extended version - pub async fn create_ext( - ctx: &Context, - authenticated_storage: Arc, - vault: Arc, - ) -> Result { - let attrs = KeyAttributes::new( - IdentityStateConst::ROOT_LABEL.to_string(), - SecretAttributes::new( - SecretType::Ed25519, - SecretPersistence::Persistent, - CURVE25519_SECRET_LENGTH_U32, - ), - ); - Self::create_impl( - ctx, - authenticated_storage, - SecureChannelRegistry::new(), - vault, - None, - attrs, - ) - .await - } - - /// Create an `Identity` with an external key. Extended version - pub async fn create_with_external_key_ext( - ctx: &Context, - authenticated_storage: Arc, - vault: Arc, - kid: &KeyId, - attrs: KeyAttributes, - ) -> Result { - Self::create_impl( - ctx, - authenticated_storage, - SecureChannelRegistry::new(), - vault, - Some(kid), - attrs, - ) - .await - } -} - -impl Identity { - /// Create an `Identity` with a new secret key and `InMemoryStorage` - pub async fn create(ctx: &Context, vault: Arc) -> Result { - let attrs = KeyAttributes::new( - IdentityStateConst::ROOT_LABEL.to_string(), - SecretAttributes::new( - SecretType::Ed25519, - SecretPersistence::Persistent, - CURVE25519_SECRET_LENGTH_U32, - ), - ); - Self::create_impl( - ctx, - Arc::new(InMemoryStorage::new()), - SecureChannelRegistry::new(), - vault, - None, - attrs, - ) - .await - } - - /// Create an `Identity` with an external key. - pub async fn create_with_external_key( - ctx: &Context, - vault: Arc, - kid: &KeyId, - attrs: KeyAttributes, - ) -> Result { - Self::create_impl( - ctx, - Arc::new(InMemoryStorage::new()), - SecureChannelRegistry::new(), - vault, - Some(kid), - attrs, - ) - .await - } - - /// Import and verify `Identity` from the binary format. Uses `InMemoryStorage` - pub async fn import(ctx: &Context, data: &[u8], vault: Arc) -> Result { - let storage: Arc = Arc::new(InMemoryStorage::new()); - Self::import_ext(ctx, data, storage, &SecureChannelRegistry::new(), vault).await - } - - /// Create an identity with a vault initialized with a specific private key - /// encoded as a hex string. - /// Such a key can be obtained by running vault.secret_export and then encoding - /// the exported secret as a hex string - pub async fn create_identity_with_change_history( - ctx: &Context, - vault: Arc, - identity_history: &str, - secret: &str, - ) -> Result { - let key_attributes = KeyAttributes::default_with_label(IdentityStateConst::ROOT_LABEL); - vault - .secret_import( - Key(SecretKey::new(hex::decode(secret).unwrap())), - key_attributes.secret_attributes(), - ) - .await?; - let identity_history_data: Vec = - hex::decode(identity_history).map_err(|_| IdentityError::InvalidInternalState)?; - Identity::import_ext( - ctx, - identity_history_data.as_slice(), - Arc::new(InMemoryStorage::default()), - &SecureChannelRegistry::default(), - vault, - ) - .await - } -} - -impl Identity { - /// Export an `Identity` to the binary format - pub async fn export(&self) -> Result> { - self.change_history.read().await.export() - } - - /// Vault - pub fn vault(&self) -> Arc { - self.vault.clone() - } - - /// `AuthenticatedStorage` - pub fn authenticated_storage(&self) -> Arc { - self.authenticated_storage.clone() - } - - /// `SecureChannelRegistry` with all known SecureChannels this `Identity` has created - pub fn secure_channel_registry(&self) -> &SecureChannelRegistry { - &self.secure_channel_registry - } - - /// `Identity` change history - pub async fn change_history(&self) -> IdentityChangeHistory { - self.change_history.read().await.clone() - } - - /// `Context` - pub fn ctx(&self) -> &Context { - &self.ctx - } -} - -impl Identity { - pub(crate) async fn get_secret_key_from_change( - &self, - change: &IdentitySignedChange, - ) -> Result { - let public_key = change.change().public_key()?; - self.vault.compute_key_id_for_public_key(&public_key).await - } - - async fn add_change(&self, change: IdentitySignedChange) -> Result<()> { - self.change_history - .write() - .await - .check_consistency_and_add_change(change) - } -} - -impl Identity { - /// `IdentityIdentifier` of this `Identity` - pub fn identifier(&self) -> &IdentityIdentifier { - &self.id - } - - /// Generate and add a new key to this `Identity` with a given `label` - pub async fn create_key(&self, label: String) -> Result<()> { - let key_attribs = KeyAttributes::default_with_label(label); - - let change = self.make_create_key_change(None, key_attribs).await?; - - self.add_change(change).await - } - - /// Add a new key to this `Identity` with a given `label` - pub async fn add_key(&self, label: String, secret: &KeyId) -> Result<()> { - let secret_attributes = self.vault.secret_attributes_get(secret).await?; - let key_attribs = KeyAttributes::new(label, secret_attributes); - - let change = self - .make_create_key_change(Some(secret), key_attribs) - .await?; - - self.add_change(change).await - } - - /// Rotate an existing key with a given label - pub async fn rotate_key(&self, label: &str) -> Result<()> { - let change = self - .make_rotate_key_change(KeyAttributes::default_with_label(label.to_string())) - .await?; - - self.add_change(change).await - } - - /// Rotate this `Identity` root key - pub async fn rotate_root_key(&self) -> Result<()> { - let change = self - .make_rotate_key_change(KeyAttributes::default_with_label( - IdentityStateConst::ROOT_LABEL.to_string(), - )) - .await?; - - self.add_change(change).await - } - - /// Get [`Secret`] key. Key is uniquely identified by label in [`KeyAttributes`] - pub(crate) async fn get_root_secret_key(&self) -> Result { - self.get_secret_key(IdentityStateConst::ROOT_LABEL).await - } - - pub(crate) async fn get_secret_key(&self, label: &str) -> Result { - let change = IdentityChangeHistory::find_last_key_change( - self.change_history.read().await.as_ref(), - label, - )? - .clone(); - self.get_secret_key_from_change(&change).await - } - - /// Generate Proof of possession of [`crate::Identity`]. - /// - /// channel_state should be tied to channel's cryptographical material (e.g. h value for Noise XX) - pub async fn create_signature( - &self, - data: &[u8], - key_label: Option<&str>, - ) -> Result { - let secret = match key_label { - Some(label) => self.get_secret_key(label).await?, - None => self.get_root_secret_key().await?, - }; - - self.vault.sign(&secret, data).await - } - - /// Get a `PublicIdentity` with given `IdentityIdentifier`, - /// that we already should know (e.g. after creating a SecureChannel with it) - pub async fn get_known_identity( - &self, - their_identity_id: &IdentityIdentifier, - ) -> Result> { - if let Some(known) = self - .authenticated_storage - .get( - &their_identity_id.to_string(), - IdentityStateConst::CHANGE_HISTORY_KEY, - ) - .await? - { - let known = PublicIdentity::import(&known, self.vault.clone()).await?; - - Ok(Some(known)) - } else { - Ok(None) - } - } - - /// Update previously known `Identity` given newly obtained changes in `PublicIdentity` - pub async fn update_known_identity( - &self, - their_identity_id: &IdentityIdentifier, - current_history: &PublicIdentity, - ) -> Result<()> { - let should_set = if let Some(known) = self.get_known_identity(their_identity_id).await? { - match current_history.changes().compare(known.changes()) { - IdentityHistoryComparison::Equal => false, /* Do nothing */ - IdentityHistoryComparison::Conflict => { - return Err(IdentityError::ConsistencyError.into()) - } - IdentityHistoryComparison::Newer => true, /* Update */ - IdentityHistoryComparison::Older => { - return Err(IdentityError::ConsistencyError.into()) - } - } - } else { - true - }; - - if should_set { - self.authenticated_storage - .set( - &their_identity_id.to_string(), - IdentityStateConst::CHANGE_HISTORY_KEY.to_string(), - current_history.export()?, - ) - .await?; - } - - Ok(()) - } - - /// Export to `PublicIdentity` - pub async fn to_public(&self) -> Result { - Ok(PublicIdentity::new( - self.id.clone(), - self.change_history.read().await.clone(), - )) - } -} - -#[cfg(test)] -mod test { - use super::*; - use ockam_core::errcode::{Kind, Origin}; - use ockam_core::vault::PublicKey; - use ockam_core::Error; - use ockam_vault::Vault; - - fn test_error>(error: S) -> Result<()> { - Err(Error::new_without_cause(Origin::Identity, Kind::Unknown).context("msg", error.into())) - } - - impl Identity { - pub async fn get_root_public_key(&self) -> Result { - self.change_history.read().await.get_root_public_key() - } - - pub async fn get_public_key(&self, label: &str) -> Result { - self.change_history.read().await.get_public_key(label) - } - - async fn verify_changes(&self) -> Result { - self.change_history - .read() - .await - .verify_all_existing_changes(self.vault.clone()) - .await - } - } - - #[ockam_macros::test] - async fn test_basic_identity_key_ops(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); - - let identity = Identity::create(ctx, vault).await?; - - if !identity.verify_changes().await? { - return test_error("verify_changes failed"); - } - - let secret1 = identity.get_root_secret_key().await?; - let public1 = identity.get_root_public_key().await?; - - identity.create_key("Truck management".to_string()).await?; - - if !identity.verify_changes().await? { - return test_error("verify_changes failed"); - } - - let secret2 = identity.get_secret_key("Truck management").await?; - let public2 = identity.get_public_key("Truck management").await?; - - if secret1 == secret2 { - return test_error("secret did not change after create_key"); - } - - if public1 == public2 { - return test_error("public did not change after create_key"); - } - - identity.rotate_root_key().await?; - - if !identity.verify_changes().await? { - return test_error("verify_changes failed"); - } - - let secret3 = identity.get_root_secret_key().await?; - let public3 = identity.get_root_public_key().await?; - - identity.rotate_root_key().await?; - - if !identity.verify_changes().await? { - return test_error("verify_changes failed"); - } - - if secret1 == secret3 { - return test_error("secret did not change after rotate_key"); - } - - if public1 == public3 { - return test_error("public did not change after rotate_key"); - } - - ctx.stop().await?; - - Ok(()) - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/error.rs b/implementations/rust/ockam/ockam_identity/src/identity/error.rs similarity index 100% rename from implementations/rust/ockam/ockam_identity/src/error.rs rename to implementations/rust/ockam/ockam_identity/src/identity/error.rs diff --git a/implementations/rust/ockam/ockam_identity/src/public_identity.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity.rs similarity index 56% rename from implementations/rust/ockam/ockam_identity/src/public_identity.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity.rs index 39b78771afe..e6698bfe44e 100644 --- a/implementations/rust/ockam/ockam_identity/src/public_identity.rs +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity.rs @@ -1,89 +1,86 @@ -use crate::change_history::{IdentityChangeHistory, IdentityHistoryComparison}; -use crate::{IdentityError, IdentityIdentifier, IdentityVault}; +//! Identity history +use crate::identity::identity_change::IdentitySignedChange; +use crate::identity::identity_change_history::IdentityChangeHistory; +use crate::identity::identity_identifier::IdentityIdentifier; +use crate::IdentityHistoryComparison; +use core::fmt::{Display, Formatter}; use ockam_core::compat::fmt; -use ockam_core::compat::fmt::{Display, Formatter}; -use ockam_core::compat::sync::Arc; use ockam_core::compat::vec::Vec; -use ockam_core::vault::Signature; +use ockam_core::vault::PublicKey; use ockam_core::Result; -use ockam_vault::PublicKey; use serde::{Deserialize, Serialize}; -/// Public part of an `Identity` +/// Identity implementation #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PublicIdentity { - id: IdentityIdentifier, - change_history: IdentityChangeHistory, +pub struct Identity { + pub(crate) identifier: IdentityIdentifier, + pub(crate) change_history: IdentityChangeHistory, } -impl PublicIdentity { - /// Create a new public identity - pub fn new(id: IdentityIdentifier, change_history: IdentityChangeHistory) -> Self { - Self { id, change_history } +impl Identity { + /// Create a new identity + pub fn new(identifier: IdentityIdentifier, change_history: IdentityChangeHistory) -> Self { + Self { + identifier, + change_history, + } + } + + /// Return the identity identifier + pub fn identifier(&self) -> IdentityIdentifier { + self.identifier.clone() } - /// Export to the binary format + /// Export an `Identity` to the binary format pub fn export(&self) -> Result> { self.change_history.export() } - /// Import from the binary format - pub async fn import(data: &[u8], vault: Arc) -> Result { - let change_history = IdentityChangeHistory::import(data)?; - if !change_history - .verify_all_existing_changes(vault.clone()) - .await? - { - return Err(IdentityError::IdentityVerificationFailed.into()); - } - - let id = change_history.compute_identity_id(vault.clone()).await?; - - let identity = Self::new(id, change_history); - - Ok(identity) + /// Add a new key change to the change history + pub fn add_change(&mut self, change: IdentitySignedChange) -> Result<()> { + self.change_history.add_change(change) } - pub(crate) fn changes(&self) -> &IdentityChangeHistory { - &self.change_history + /// `Identity` change history + pub fn change_history(&self) -> IdentityChangeHistory { + self.change_history.clone() } - /// Compare to a previously known state of the same `Identity` - pub fn compare(&self, known: &Self) -> IdentityHistoryComparison { - self.change_history.compare(&known.change_history) + /// Return the root public key of an identity + pub fn get_root_public_key(&self) -> Result { + self.change_history.get_root_public_key() } - /// `IdentityIdentifier` - pub fn identifier(&self) -> &IdentityIdentifier { - &self.id + pub(crate) fn get_public_key(&self, key_label: Option<&str>) -> Result { + let key = match key_label { + Some(label) => self.get_labelled_public_key(label)?, + None => self.get_root_public_key()?, + }; + Ok(key) } - pub(crate) fn get_root_public_key(&self) -> Result { - self.change_history.get_root_public_key() + pub(crate) fn get_labelled_public_key(&self, label: &str) -> Result { + self.change_history.get_public_key(label) } - pub(crate) fn get_public_key(&self, label: &str) -> Result { - self.change_history.get_public_key(label) + /// Create an Identity from serialized data + pub fn import(identifier: &IdentityIdentifier, data: &[u8]) -> Result { + let change_history = IdentityChangeHistory::import(data)?; + Ok(Identity::new(identifier.clone(), change_history)) } - /// Verify signature using key with the given label - pub async fn verify_signature( - &self, - signature: &Signature, - data: &[u8], - key_label: Option<&str>, - vault: Arc, - ) -> Result { - let public_key = match key_label { - Some(label) => self.get_public_key(label)?, - None => self.get_root_public_key()?, - }; + /// Return the list of key changes for this identity + pub(crate) fn changes(&self) -> &IdentityChangeHistory { + &self.change_history + } - vault.verify(signature, &public_key, data).await + /// Compare to a previously known state of the same `Identity` + pub fn compare(&self, known: &Self) -> IdentityHistoryComparison { + self.change_history.compare(&known.change_history) } } -impl Display for PublicIdentity { +impl Display for Identity { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let identifier = self.identifier(); writeln!(f, "Identifier: {identifier}")?; @@ -102,7 +99,7 @@ mod tests { #[test] fn test_display() { let data = hex::decode("0144c7eb72dd1e633f38e0d0521e9d5eb5072f6418176529eb1b00189e4d69ad2e000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020c6c52380125d42b0b4da922b1cff8503a258c3497ec8ac0b4a3baa0d9ca7b3780301014075064b902bda9d16db81ab5f38fbcf226a0e904e517a8c087d379ea139df1f2d7fee484ac7e1c2b7ab2da75f85adef6af7ddb05e7fa8faf180820cb9e86def02").unwrap(); - let public_identity = PublicIdentity::new( + let identity = Identity::new( IdentityIdentifier::from_str( "Pfa804b7fca12a19eed206ae180b5b576860ae6512f196c189d90661bcc434b50", ) @@ -110,7 +107,7 @@ mod tests { IdentityChangeHistory::import(data.to_vec().as_slice()).unwrap(), ); - let actual = format!("{public_identity}"); + let actual = format!("{identity}"); let expected = r#"Identifier: Pfa804b7fca12a19eed206ae180b5b576860ae6512f196c189d90661bcc434b50 Change history: 0144c7eb72dd1e633f38e0d0521e9d5eb5072f6418176529eb1b00189e4d69ad2e000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020c6c52380125d42b0b4da922b1cff8503a258c3497ec8ac0b4a3baa0d9ca7b3780301014075064b902bda9d16db81ab5f38fbcf226a0e904e517a8c087d379ea139df1f2d7fee484ac7e1c2b7ab2da75f85adef6af7ddb05e7fa8faf180820cb9e86def02 "#; diff --git a/implementations/rust/ockam/ockam_identity/src/identity/identity_change/change_identifier.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/change_identifier.rs new file mode 100644 index 00000000000..f573f96479e --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/change_identifier.rs @@ -0,0 +1,30 @@ +use core::fmt::{Display, Formatter}; +use ockam_core::compat::string::String; +use serde::{Deserialize, Serialize}; + +/// Unique [`crate::change::IdentityChange`] identifier, computed as SHA256 of the change data +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash)] +pub struct ChangeIdentifier([u8; 32]); + +impl Display for ChangeIdentifier { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +impl AsRef<[u8]> for ChangeIdentifier { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl ChangeIdentifier { + /// Create identifier from public key hash + pub fn from_hash(hash: [u8; 32]) -> Self { + Self(hash) + } + /// Human-readable form of the id + pub fn to_string_representation(&self) -> String { + format!("E_ID.{}", hex::encode(self.0)) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/identity/identity_change/create_key.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/create_key.rs new file mode 100644 index 00000000000..cef2290801d --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/create_key.rs @@ -0,0 +1,55 @@ +use crate::identity::identity_change::ChangeIdentifier; +use crate::identity::identity_change::KeyAttributes; +use core::fmt; +use ockam_core::vault::PublicKey; +use serde::{Deserialize, Serialize}; + +/// Key change data creation +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CreateKeyChangeData { + prev_change_id: ChangeIdentifier, + key_attributes: KeyAttributes, + public_key: PublicKey, +} + +impl CreateKeyChangeData { + /// Return key attributes + pub fn key_attributes(&self) -> &KeyAttributes { + &self.key_attributes + } + /// Return public key + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + /// Previous change identifier, used to create a chain + pub fn prev_change_id(&self) -> &ChangeIdentifier { + &self.prev_change_id + } +} + +impl CreateKeyChangeData { + /// Create new CreateKeyChangeData + pub fn new( + prev_change_id: ChangeIdentifier, + key_attributes: KeyAttributes, + public_key: PublicKey, + ) -> Self { + Self { + prev_change_id, + key_attributes, + public_key, + } + } +} + +impl fmt::Display for CreateKeyChangeData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "prev_change_id:{} key attibutes:{} public key:{}", + self.prev_change_id(), + self.key_attributes(), + self.public_key() + ) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/change.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change.rs similarity index 89% rename from implementations/rust/ockam/ockam_identity/src/change.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change.rs index 53326fab50d..8727967ebf8 100644 --- a/implementations/rust/ockam/ockam_identity/src/change.rs +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change.rs @@ -1,19 +1,13 @@ -use crate::ChangeIdentifier; +use crate::identity::identity_change::CreateKeyChangeData; +use crate::identity::identity_change::RotateKeyChangeData; +use crate::identity::identity_change::{ChangeIdentifier, Signature}; use core::fmt; use ockam_core::compat::vec::Vec; use ockam_core::vault::PublicKey; use ockam_core::Result; use serde::{Deserialize, Serialize}; -pub use crate::signature::*; - -mod create_key; -mod rotate_key; - -pub use create_key::*; -pub use rotate_key::*; - -/// Possible types of [`crate::Identity`] changes +/// Possible types of [`crate::SecureChannels`] changes #[derive(Serialize, Deserialize, Debug, Clone)] pub enum IdentityChange { /// Create key @@ -59,7 +53,7 @@ impl IdentityChange { } } -/// [`crate::Identity`]s are modified using a chain of changes. +/// [`crate::SecureChannels`]s are modified using a chain of changes. /// Signatures are used to check change validity. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct IdentitySignedChange { diff --git a/implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change_constants.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change_constants.rs new file mode 100644 index 00000000000..30ab2fe9961 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/identity_change_constants.rs @@ -0,0 +1,14 @@ +/// `Identity`-related constants +pub struct IdentityChangeConstants; + +impl IdentityChangeConstants { + /// Sha256 of that value is used as previous change id for first change in a + /// [`crate::SecureChannels`] + pub const INITIAL_CHANGE: &'static [u8] = "OCKAM_INITIAL_CHANGE".as_bytes(); + /// Label for [`crate::SecureChannels`] update key + pub const ROOT_LABEL: &'static str = "OCKAM_RK"; + /// Change history key for AttributesStorage + pub const CHANGE_HISTORY_KEY: &'static str = "CHANGE_HISTORY"; + /// Attributes key for AttributesStorage + pub const ATTRIBUTES_KEY: &'static str = "ATTRIBUTES"; +} diff --git a/implementations/rust/ockam/ockam_identity/src/key_attributes.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/key_attributes.rs similarity index 100% rename from implementations/rust/ockam/ockam_identity/src/key_attributes.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity_change/key_attributes.rs diff --git a/implementations/rust/ockam/ockam_identity/src/identity/identity_change/mod.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/mod.rs new file mode 100644 index 00000000000..798891e1f87 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/mod.rs @@ -0,0 +1,16 @@ +mod change_identifier; +mod create_key; +#[allow(clippy::module_inception)] +mod identity_change; +pub(crate) mod identity_change_constants; +mod key_attributes; +mod rotate_key; +mod signature; + +pub use change_identifier::*; +pub use create_key::*; +pub use identity_change::*; +pub use identity_change_constants::*; +pub use key_attributes::*; +pub use rotate_key::*; +pub use signature::*; diff --git a/implementations/rust/ockam/ockam_identity/src/identity/identity_change/rotate_key.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/rotate_key.rs new file mode 100644 index 00000000000..d469c25f6fe --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/rotate_key.rs @@ -0,0 +1,55 @@ +use crate::identity::identity_change::ChangeIdentifier; +use crate::identity::identity_change::KeyAttributes; +use core::fmt; +use ockam_core::vault::PublicKey; +use serde::{Deserialize, Serialize}; + +/// RotateKeyChangeData +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RotateKeyChangeData { + prev_change_id: ChangeIdentifier, + key_attributes: KeyAttributes, + public_key: PublicKey, +} + +impl RotateKeyChangeData { + /// Return key attributes + pub fn key_attributes(&self) -> &KeyAttributes { + &self.key_attributes + } + /// Return public key + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + /// Previous change identifier, used to create a chain + pub fn prev_change_id(&self) -> &ChangeIdentifier { + &self.prev_change_id + } +} + +impl RotateKeyChangeData { + /// Create RotateKeyChangeData + pub fn new( + prev_change_id: ChangeIdentifier, + key_attributes: KeyAttributes, + public_key: PublicKey, + ) -> Self { + Self { + prev_change_id, + key_attributes, + public_key, + } + } +} + +impl fmt::Display for RotateKeyChangeData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "prev_change_id:{} key attibutes:{} public key:{}", + self.prev_change_id(), + self.key_attributes(), + self.public_key() + ) + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/signature.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change/signature.rs similarity index 100% rename from implementations/rust/ockam/ockam_identity/src/signature.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity_change/signature.rs diff --git a/implementations/rust/ockam/ockam_identity/src/change_history.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_change_history.rs similarity index 59% rename from implementations/rust/ockam/ockam_identity/src/change_history.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity_change_history.rs index ca497a98601..e6cd90686be 100644 --- a/implementations/rust/ockam/ockam_identity/src/change_history.rs +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_change_history.rs @@ -1,15 +1,14 @@ //! Identity history -use crate::change::IdentityChange::{CreateKey, RotateKey}; -use crate::change::{IdentitySignedChange, SignatureType}; -use crate::{ - ChangeIdentifier, IdentityError, IdentityIdentifier, IdentityStateConst, IdentityVault, +use crate::identity::identity_change::IdentityChange::CreateKey; +use crate::identity::identity_change::{ + ChangeIdentifier, IdentityChangeConstants, IdentitySignedChange, }; +use crate::identity::IdentityError; use core::cmp::Ordering; use core::fmt; use minicbor::{Decode, Encode}; -use ockam_core::compat::sync::Arc; use ockam_core::compat::vec::Vec; -use ockam_core::{allow, deny, Encodable, Result}; +use ockam_core::Result; use ockam_vault::PublicKey; use serde::{Deserialize, Serialize}; @@ -32,7 +31,7 @@ pub enum IdentityHistoryComparison { Older, } -/// Full history of [`crate::identity::Identity`] changes. History and corresponding secret keys are enough to recreate [`crate::identity::Identity`] +/// Full history of [`crate::secure_channels::SecureChannels`] changes. History and corresponding secret keys are enough to recreate [`crate::secure_channels::SecureChannels`] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct IdentityChangeHistory(Vec); @@ -69,11 +68,7 @@ impl IdentityChangeHistory { /// Import `IdentityChangeHistory` from the binary format pub fn import(data: &[u8]) -> Result { let s: Self = serde_bare::from_slice(data).map_err(|_| IdentityError::ConsistencyError)?; - - if !s.check_entire_consistency() { - return Err(IdentityError::ConsistencyError.into()); - } - + s.check_entire_consistency()?; Ok(s) } } @@ -121,20 +116,6 @@ impl IdentityChangeHistory { } } - /// Cryptographically compute `IdentityIdentifier` - pub async fn compute_identity_id( - &self, - vault: Arc, - ) -> Result { - let root_public_key = self.get_first_root_public_key()?; - - let key_id = vault - .compute_key_id_for_public_key(&root_public_key) - .await?; - - Ok(IdentityIdentifier::from_key_id(&key_id)) - } - /// Get public key with the given label (name) pub fn get_public_key(&self, label: &str) -> Result { Self::get_public_key_static(self.as_ref(), label) @@ -160,29 +141,24 @@ impl IdentityChangeHistory { /// Get latest root public key pub fn get_root_public_key(&self) -> Result { - self.get_public_key(IdentityStateConst::ROOT_LABEL) - } - - /// Verify all changes present in current `IdentityChangeHistory` - pub async fn verify_all_existing_changes(&self, vault: Arc) -> Result { - for i in 0..self.0.len() { - let existing_changes = &self.as_ref()[..i]; - let new_change = &self.as_ref()[i]; - if !Self::verify_change(existing_changes, new_change, vault.clone()).await? { - return deny(); - } - } - allow() + self.get_public_key(IdentityChangeConstants::ROOT_LABEL) } /// Check consistency of changes that are being added - pub fn check_entire_consistency(&self) -> bool { - Self::check_consistency(&[], &self.0) + pub fn check_entire_consistency(&self) -> Result<()> { + if !Self::check_consistency(&[], &self.0) { + return Err(IdentityError::ConsistencyError.into()); + } + Ok(()) } } // Pub crate API impl IdentityChangeHistory { + pub(crate) fn add_change(&mut self, change: IdentitySignedChange) -> Result<()> { + self.check_consistency_and_add_change(change) + } + pub(crate) fn get_last_change_id(&self) -> Result { if let Some(e) = self.0.last() { Ok(e.identifier().clone()) @@ -214,7 +190,7 @@ impl IdentityChangeHistory { pub(crate) fn get_current_root_public_key( existing_changes: &[IdentitySignedChange], ) -> Result { - Self::find_last_key_change_public_key(existing_changes, IdentityStateConst::ROOT_LABEL) + Self::find_last_key_change_public_key(existing_changes, IdentityChangeConstants::ROOT_LABEL) } pub(crate) fn get_public_key_static( @@ -225,98 +201,6 @@ impl IdentityChangeHistory { change.change().public_key() } - /// WARNING: This function assumes all existing changes in chain are verified. - /// WARNING: Correctness of changes sequence is not verified here. - pub(crate) async fn verify_change( - existing_changes: &[IdentitySignedChange], - new_change: &IdentitySignedChange, - vault: Arc, - ) -> Result { - let change_binary = new_change - .change() - .encode() - .map_err(|_| IdentityError::BareError)?; - - let change_id = vault.sha256(&change_binary).await?; - let change_id = ChangeIdentifier::from_hash(change_id); - - if &change_id != new_change.identifier() { - return deny(); // ChangeIdDoesNotMatch - } - - struct SignaturesCheck { - self_sign: u8, - prev_sign: u8, - root_sign: u8, - } - - let mut signatures_check = match new_change.change() { - CreateKey(_) => { - // Should have self signature and root signature - // There is no Root signature for the very first change - let root_sign = u8::from(!existing_changes.is_empty()); - - SignaturesCheck { - self_sign: 1, - prev_sign: 0, - root_sign, - } - } - RotateKey(_) => { - // Should have self signature, root signature, and previous key signature - SignaturesCheck { - self_sign: 1, - prev_sign: 1, - root_sign: 1, - } - } - }; - - for signature in new_change.signatures() { - let counter; - let public_key = match signature.stype() { - SignatureType::RootSign => { - if existing_changes.is_empty() { - return Err(IdentityError::VerifyFailed.into()); - } - - counter = &mut signatures_check.root_sign; - Self::get_current_root_public_key(existing_changes)? - } - SignatureType::SelfSign => { - counter = &mut signatures_check.self_sign; - new_change.change().public_key()? - } - SignatureType::PrevSign => { - counter = &mut signatures_check.prev_sign; - Self::get_public_key_static(existing_changes, new_change.change().label())? - } - }; - - if *counter == 0 { - return Err(IdentityError::VerifyFailed.into()); - } - - if !vault - .verify(signature.data(), &public_key, change_id.as_ref()) - .await? - { - return deny(); - } - - *counter -= 1; - } - - if signatures_check.prev_sign == 0 - && signatures_check.root_sign == 0 - && signatures_check.self_sign == 0 - { - allow() - } else { - deny() - } - } - /// Check consistency of changes that are been added pub(crate) fn check_consistency( existing_changes: &[IdentitySignedChange], diff --git a/implementations/rust/ockam/ockam_identity/src/identifiers.rs b/implementations/rust/ockam/ockam_identity/src/identity/identity_identifier.rs similarity index 75% rename from implementations/rust/ockam/ockam_identity/src/identifiers.rs rename to implementations/rust/ockam/ockam_identity/src/identity/identity_identifier.rs index ae0730b5995..4e8af8cd108 100644 --- a/implementations/rust/ockam/ockam_identity/src/identifiers.rs +++ b/implementations/rust/ockam/ockam_identity/src/identity/identity_identifier.rs @@ -1,12 +1,11 @@ -use crate::{IdentityError, IdentityStateConst}; +use crate::identity::IdentityError; use core::fmt::{Display, Formatter}; use core::str::FromStr; use minicbor::decode::{self, Decoder}; use minicbor::{Decode, Encode}; use ockam_core::compat::borrow::Cow; use ockam_core::compat::string::{String, ToString}; -use ockam_core::compat::sync::Arc; -use ockam_core::vault::{Hasher, KeyId}; +use ockam_core::vault::KeyId; use ockam_core::{Error, Result}; use serde::{Deserialize, Deserializer, Serialize}; @@ -16,7 +15,7 @@ use serde::{Deserialize, Deserializer, Serialize}; #[cbor(transparent)] pub struct IdentityIdentifier(#[n(0)] KeyId); -/// Unique [`crate::Identity`] identifier, computed as SHA256 of root public key +/// Unique [`crate::SecureChannels`] identifier, computed as SHA256 of root public key impl IdentityIdentifier { const PREFIX: &'static str = "P"; @@ -109,40 +108,6 @@ impl FromStr for IdentityIdentifier { } } -/// Unique [`crate::change::IdentityChange`] identifier, computed as SHA256 of the change data -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash)] -pub struct ChangeIdentifier([u8; 32]); -impl Display for ChangeIdentifier { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", hex::encode(self.0)) - } -} - -impl AsRef<[u8]> for ChangeIdentifier { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl ChangeIdentifier { - /// Initial `ChangeIdentifier` that is used as a previous_identifier of the first change - pub async fn initial(hasher: Arc) -> Self { - let h = match hasher.sha256(IdentityStateConst::INITIAL_CHANGE).await { - Ok(hash) => hash, - Err(_) => panic!("failed to hash initial change"), - }; - ChangeIdentifier::from_hash(h) - } - /// Create identifier from public key hash - pub fn from_hash(hash: [u8; 32]) -> Self { - Self(hash) - } - /// Human-readable form of the id - pub fn to_string_representation(&self) -> String { - format!("E_ID.{}", hex::encode(self.0)) - } -} - #[cfg(test)] mod test { use super::IdentityIdentifier; diff --git a/implementations/rust/ockam/ockam_identity/src/identity/mod.rs b/implementations/rust/ockam/ockam_identity/src/identity/mod.rs new file mode 100644 index 00000000000..b936e17eb6c --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/identity/mod.rs @@ -0,0 +1,14 @@ +mod error; + +#[allow(clippy::module_inception)] +mod identity; +/// List of key changes associated to an identity +pub mod identity_change; +mod identity_change_history; +mod identity_identifier; + +pub use error::*; +pub use identity::*; +pub use identity_change::*; +pub use identity_change_history::*; +pub use identity_identifier::*; diff --git a/implementations/rust/ockam/ockam_identity/src/identity_builder.rs b/implementations/rust/ockam/ockam_identity/src/identity_builder.rs deleted file mode 100644 index 922ea43ea24..00000000000 --- a/implementations/rust/ockam/ockam_identity/src/identity_builder.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::{Identity, IdentityVault}; -use ockam_core::compat::sync::Arc; -use ockam_core::{Address, DenyAll, Result}; -use ockam_node::Context; - -/// Builder for `Identity` -pub struct IdentityBuilder { - ctx: Context, - vault: Arc, -} - -impl IdentityBuilder { - /// Constructor - pub async fn new(ctx: &Context, vault: Arc) -> Result { - let child_ctx = ctx - .new_detached( - Address::random_tagged("IdentityBuilder.detached"), - DenyAll, - DenyAll, - ) - .await?; - - Ok(Self { - ctx: child_ctx, - vault: vault.clone(), - }) - } - - /// Build an `Identity` - pub async fn build(self) -> Result { - Identity::create(&self.ctx, self.vault).await - } -} - -#[cfg(test)] -mod test { - use crate::{IdentityBuilder, IdentityVault}; - use ockam_core::Result; - use ockam_node::Context; - use ockam_vault::Vault; - use std::sync::Arc; - - #[ockam_macros::test] - async fn test_builder(ctx: &mut Context) -> Result<()> { - let vault: Arc = Vault::create(); - let builder = IdentityBuilder::new(ctx, vault).await.unwrap(); - let _ = builder.build().await.unwrap(); - - ctx.stop().await - } -} diff --git a/implementations/rust/ockam/ockam_identity/src/lib.rs b/implementations/rust/ockam/ockam_identity/src/lib.rs index fc72ddb93c3..c61d05016a1 100644 --- a/implementations/rust/ockam/ockam_identity/src/lib.rs +++ b/implementations/rust/ockam/ockam_identity/src/lib.rs @@ -1,5 +1,20 @@ -//! Identity is an abstraction over Identitys and Vaults, easing the use of these primitives in -//! authentication and authorization APIs. +//! This crate supports the domain of "identities", which is required to create secure channels: +//! +//! - the `identity` module describes an entity as a set of verified key changes and an identifier +//! uniquely representing those changes +//! +//! - the `identities` module provides services to create, update, and import identities +//! +//! - the `credential` module describes sets of attributes describing a given identity and signed by +//! another identity +//! +//! - the `credentials` module provides services to create, import and verify credentials +//! +//! - the `secure_channel` module describes the steps required to establish a secure channel +//! between 2 identities +//! +//! - the `secure_channels` module provides services to create a secure channel between 2 identities + #![deny(unsafe_code)] #![warn( // prevented by big_array @@ -10,7 +25,6 @@ unused_qualifications )] #![cfg_attr(not(feature = "std"), no_std)] - #[cfg(feature = "std")] extern crate core; @@ -18,56 +32,30 @@ extern crate core; #[macro_use] extern crate alloc; -use ockam_vault::{Hasher, SecretVault, Signer, Verifier}; - -use crate::IdentityError; - -/// Storage used for previously authenticated info about others: attributes, public identities, etc. -pub mod authenticated_storage; - -/// Possible change of an `Identity` -pub mod change; - -/// Change history of an `Identity` -pub mod change_history; - -/// Credential support +/// Data types supporting the creation of a credential pub mod credential; -/// Authority test support -pub mod credential_issuer; - -/// Errors -pub mod error; - -pub use error::*; +/// Services for creating and validating credentials +pub mod credentials; -mod channel; -mod identifiers; -mod identity; -mod identity_builder; -mod key_attributes; -mod public_identity; +/// Service for the management of identities +pub mod identities; -pub use channel::*; -pub use identifiers::*; -pub use identity::*; -pub use identity_builder::*; -pub use key_attributes::*; -pub use public_identity::*; - -mod signature; +/// Data types representing an identity +pub mod identity; -#[cfg(test)] -mod invalid_signatures_tests; +/// Data types supporting the creation of a secure channels +pub mod secure_channel; -/// Traits required for a Vault implementation suitable for use in an Identity -pub trait IdentityVault: - SecretVault + SecureChannelVault + Hasher + Signer + Verifier + Send + Sync + 'static -{ -} +/// Service supporting the creation of secure channel listener and connection to a listener +pub mod secure_channels; -impl IdentityVault for D where - D: SecretVault + SecureChannelVault + Hasher + Signer + Verifier + Send + Sync + 'static -{ -} +/// +/// Exports +/// +pub use credential::*; +pub use credentials::*; +pub use identities::*; +pub use identity::*; +pub use secure_channel::*; +pub use secure_channels::*; diff --git a/implementations/rust/ockam/ockam_identity/src/credential/access_control/credential_access_control.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/credential_access_control.rs similarity index 76% rename from implementations/rust/ockam/ockam_identity/src/credential/access_control/credential_access_control.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/credential_access_control.rs index 4629e008926..4c128cd2542 100644 --- a/implementations/rust/ockam/ockam_identity/src/credential/access_control/credential_access_control.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/credential_access_control.rs @@ -1,25 +1,27 @@ -use crate::authenticated_storage::IdentityAttributeStorage; -use crate::IdentitySecureChannelLocalInfo; +use crate::identities::IdentitiesRepository; +use crate::secure_channel::local_info::IdentitySecureChannelLocalInfo; use core::fmt::{Debug, Formatter}; use ockam_core::access_control::IncomingAccessControl; -use ockam_core::compat::{string::String, sync::Arc, vec::Vec}; +use ockam_core::compat::{boxed::Box, string::String, sync::Arc, vec::Vec}; use ockam_core::Result; -use ockam_core::{async_trait, compat::boxed::Box, RelayMessage}; +use ockam_core::{async_trait, RelayMessage}; +/// Access control checking that message senders have a specific set of attributes #[derive(Clone)] pub struct CredentialAccessControl { required_attributes: Vec<(String, Vec)>, - storage: Arc, + storage: Arc, } impl CredentialAccessControl { + /// Create a new credential access control pub fn new( required_attributes: &[(String, Vec)], - storage: impl IdentityAttributeStorage, + storage: Arc, ) -> Self { Self { required_attributes: required_attributes.to_vec(), - storage: Arc::new(storage), + storage, } } } @@ -42,7 +44,7 @@ impl IncomingAccessControl for CredentialAccessControl { { let attributes = match self .storage - .get_attributes(msg_identity_id.their_identity_id()) + .get_attributes(&msg_identity_id.their_identity_id()) .await? { Some(a) => a, diff --git a/implementations/rust/ockam/ockam_identity/src/channel/access_control/identity_access_control.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/identity_access_control.rs similarity index 90% rename from implementations/rust/ockam/ockam_identity/src/channel/access_control/identity_access_control.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/identity_access_control.rs index b858cbde40c..a88a5889313 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/access_control/identity_access_control.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/identity_access_control.rs @@ -1,7 +1,9 @@ -use crate::{IdentityIdentifier, IdentitySecureChannelLocalInfo}; +use crate::identity::IdentityIdentifier; +use crate::secure_channel::local_info::IdentitySecureChannelLocalInfo; use ockam_core::access_control::IncomingAccessControl; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::compat::vec::Vec; -use ockam_core::{async_trait, compat::boxed::Box}; use ockam_core::{RelayMessage, Result}; /// Builder for `Identity`-related AccessControls @@ -68,7 +70,7 @@ impl IncomingAccessControl for IdentityIdAccessControl { if let Ok(msg_identity_id) = IdentitySecureChannelLocalInfo::find_info(relay_msg.local_message()) { - Ok(self.contains(msg_identity_id.their_identity_id())) + Ok(self.contains(&msg_identity_id.their_identity_id())) } else { Ok(false) } diff --git a/implementations/rust/ockam/ockam_identity/src/credential/access_control.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/mod.rs similarity index 51% rename from implementations/rust/ockam/ockam_identity/src/credential/access_control.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/mod.rs index 3831a0b4240..6a423b63442 100644 --- a/implementations/rust/ockam/ockam_identity/src/credential/access_control.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/access_control/mod.rs @@ -1,2 +1,5 @@ mod credential_access_control; +mod identity_access_control; + pub use credential_access_control::*; +pub use identity_access_control::*; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/addresses.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/addresses.rs similarity index 98% rename from implementations/rust/ockam/ockam_identity/src/channel/addresses.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/addresses.rs index 91ba2b8d5af..16935088c32 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/addresses.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/addresses.rs @@ -1,4 +1,4 @@ -use crate::channel::common::Role; +use crate::secure_channel::common::Role; use ockam_core::Address; // Previously there were regular ephemeral secure channel encryptor&decryptor diff --git a/implementations/rust/ockam/ockam_identity/src/channel/api.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/api.rs similarity index 100% rename from implementations/rust/ockam/ockam_identity/src/channel/api.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/api.rs diff --git a/implementations/rust/ockam/ockam_identity/src/secure_channel/common.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/common.rs new file mode 100644 index 00000000000..ed8f8db9f28 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/common.rs @@ -0,0 +1,66 @@ +use ockam_core::compat::vec::Vec; +use ockam_core::{KeyExchanger, Message, NewKeyExchanger}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Message)] +pub(crate) struct AuthenticationConfirmation; + +#[derive(Clone)] +pub(crate) enum Role { + Initiator, + Responder, +} + +impl Role { + pub fn is_initiator(&self) -> bool { + match self { + Role::Initiator => true, + Role::Responder => false, + } + } + + pub fn str(&self) -> &'static str { + match self { + Role::Initiator => "initiator", + Role::Responder => "responder", + } + } +} + +/// KeyExchanger with extra constraints +pub trait SecureChannelKeyExchanger: KeyExchanger + Send + Sync + 'static {} + +impl SecureChannelKeyExchanger for D where D: KeyExchanger + Send + Sync + 'static {} + +/// NewKeyExchanger with extra constraints +pub trait SecureChannelNewKeyExchanger: NewKeyExchanger + Send + Sync + 'static {} + +impl SecureChannelNewKeyExchanger for D where D: NewKeyExchanger + Send + Sync + 'static {} + +/// SecureChannelListener message wrapper. +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Message)] +pub struct CreateResponderChannelMessage { + payload: Vec, + custom_payload: Option>, +} + +impl CreateResponderChannelMessage { + /// Channel information. + pub fn payload(&self) -> &[u8] { + &self.payload + } + /// Callback Address + pub fn custom_payload(&self) -> &Option> { + &self.custom_payload + } +} + +impl CreateResponderChannelMessage { + /// Create message using payload and callback_address + pub fn new(payload: Vec, custom_payload: Option>) -> Self { + CreateResponderChannelMessage { + payload, + custom_payload, + } + } +} diff --git a/implementations/rust/ockam/ockam_identity/src/channel/decryptor.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor.rs similarity index 92% rename from implementations/rust/ockam/ockam_identity/src/channel/decryptor.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor.rs index d44669715af..73e539922cb 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/decryptor.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor.rs @@ -1,5 +1,5 @@ -use crate::channel::encryptor::Encryptor; -use crate::error::IdentityError; +use crate::identity::IdentityError; +use crate::secure_channel::encryptor::Encryptor; use ockam_core::compat::sync::Arc; use ockam_core::compat::vec::Vec; use ockam_core::vault::{KeyId, SymmetricVault}; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/decryptor_state.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_state.rs similarity index 87% rename from implementations/rust/ockam/ockam_identity/src/channel/decryptor_state.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_state.rs index 6bc1bed0a99..c5d724a3d90 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/decryptor_state.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_state.rs @@ -1,8 +1,7 @@ -use crate::channel::addresses::Addresses; -use crate::channel::decryptor::Decryptor; -use crate::channel::encryptor::Encryptor; -use crate::channel::Role; -use crate::{Identity, IdentityIdentifier, TrustPolicy}; +use crate::secure_channel::decryptor::Decryptor; +use crate::secure_channel::encryptor::Encryptor; +use crate::secure_channel::{Addresses, Role}; +use crate::{Identity, IdentityIdentifier, SecureChannels, TrustPolicy}; use alloc::vec::Vec; use ockam_core::compat::boxed::Box; use ockam_core::compat::sync::Arc; @@ -11,6 +10,7 @@ use ockam_core::{Address, KeyExchanger, Route}; pub(crate) struct KeyExchangeState { pub(crate) role: Role, pub(crate) identity: Identity, + pub(crate) secure_channels: Arc, pub(crate) addresses: Addresses, pub(crate) remote_route: Route, pub(crate) key_exchanger: Box, @@ -24,6 +24,7 @@ pub(crate) struct KeyExchangeState { pub(crate) struct IdentityExchangeState { pub(crate) role: Role, pub(crate) identity: Identity, + pub(crate) secure_channels: Arc, pub(crate) encryptor: Option, pub(crate) addresses: Addresses, pub(crate) remote_route: Route, @@ -52,6 +53,7 @@ impl KeyExchangeState { IdentityExchangeState { role: self.role, identity: self.identity, + secure_channels: self.secure_channels, remote_route: self.remote_route, addresses: self.addresses, trust_policy: self.trust_policy, @@ -91,6 +93,7 @@ impl State { pub(crate) fn new( role: Role, identity: Identity, + secure_channels: Arc, addresses: Addresses, key_exchanger: Box, remote_route: Route, @@ -101,6 +104,7 @@ impl State { Self::KeyExchange(KeyExchangeState { role, identity, + secure_channels, addresses, remote_route, key_exchanger, diff --git a/implementations/rust/ockam/ockam_identity/src/channel/decryptor_worker.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_worker.rs similarity index 90% rename from implementations/rust/ockam/ockam_identity/src/channel/decryptor_worker.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_worker.rs index e7aa8a24527..1960c7bf8d6 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/decryptor_worker.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/decryptor_worker.rs @@ -1,31 +1,29 @@ -use crate::api::{DecryptionRequest, DecryptionResponse}; -use crate::channel::addresses::Addresses; -use crate::channel::common::{AuthenticationConfirmation, CreateResponderChannelMessage, Role}; -use crate::channel::decryptor::Decryptor; -use crate::channel::decryptor_state::{ +use crate::secure_channel::decryptor::Decryptor; +use crate::secure_channel::decryptor_state::{ IdentityExchangeState, InitializedState, KeyExchangeState, State, }; -use crate::channel::encryptor::Encryptor; -use crate::channel::encryptor_worker::EncryptorWorker; -use crate::channel::messages::IdentityChannelMessage; +use crate::secure_channel::encryptor::Encryptor; +use crate::secure_channel::encryptor_worker::EncryptorWorker; +use crate::secure_channel::messages::IdentityChannelMessage; +use crate::secure_channel::{ + Addresses, AuthenticationConfirmation, CreateResponderChannelMessage, Role, +}; use crate::{ - to_symmetric_vault, to_xx_vault, Identity, IdentityError, IdentityIdentifier, - IdentitySecureChannelLocalInfo, PublicIdentity, SecureChannelRegistryEntry, - SecureChannelTrustInfo, TrustPolicy, + to_symmetric_vault, to_xx_vault, DecryptionRequest, DecryptionResponse, Identity, + IdentityError, IdentityIdentifier, IdentitySecureChannelLocalInfo, SecureChannelRegistryEntry, + SecureChannelTrustInfo, SecureChannels, TrustPolicy, }; use core::time::Duration; +use ockam_core::compat::boxed::Box; +use ockam_core::compat::sync::Arc; use ockam_core::compat::vec::Vec; -use ockam_core::compat::{boxed::Box, sync::Arc}; use ockam_core::vault::Signature; +use ockam_core::Result; use ockam_core::{ - async_trait, AllowAll, AllowOnwardAddress, AllowSourceAddress, DenyAll, LocalOnwardOnly, - LocalSourceOnly, Mailbox, Mailboxes, -}; -use ockam_core::{ - route, Address, Any, Decodable, Encodable, LocalMessage, Result, Route, Routed, - TransportMessage, Worker, + async_trait, route, Address, AllowAll, AllowOnwardAddress, AllowSourceAddress, Any, Decodable, + DenyAll, Encodable, LocalMessage, LocalOnwardOnly, LocalSourceOnly, Mailbox, Mailboxes, + NewKeyExchanger, OutgoingAccessControl, Route, Routed, TransportMessage, Worker, }; -use ockam_core::{NewKeyExchanger, OutgoingAccessControl}; use ockam_key_exchange_xx::XXNewKeyExchanger; use ockam_node::{Context, MessageReceiveOptions, WorkerBuilder}; use tracing::{debug, info, warn}; @@ -36,10 +34,11 @@ pub(crate) struct DecryptorWorker { impl DecryptorWorker { #[allow(clippy::too_many_arguments)] - pub async fn create_initiator( + pub(crate) async fn create_initiator( ctx: &Context, - remote_route: Route, + secure_channels: Arc, identity: Identity, + remote_route: Route, addresses: Addresses, trust_policy: Arc, decryptor_outgoing_access_control: Arc, @@ -53,7 +52,7 @@ impl DecryptorWorker { ) .await?; - let key_exchanger = XXNewKeyExchanger::new(to_xx_vault(identity.vault.clone())) + let key_exchanger = XXNewKeyExchanger::new(to_xx_vault(secure_channels.vault())) .initiator() .await?; @@ -63,6 +62,7 @@ impl DecryptorWorker { state: Some(State::new( Role::Initiator, identity, + secure_channels.clone(), addresses.clone(), Box::new(key_exchanger), remote_route, @@ -94,8 +94,9 @@ impl DecryptorWorker { impl DecryptorWorker { pub(crate) async fn create_responder( ctx: &Context, - identity: Identity, + secure_channels: Arc, addresses: Addresses, + identity: Identity, trust_policy: Arc, decryptor_outgoing_access_control: Arc, msg: Routed, @@ -112,7 +113,7 @@ impl DecryptorWorker { let remote_backwards_compatibility_address = Address::decode(remote_backwards_compatibility_address)?; - let vault = to_xx_vault(identity.vault.clone()); + let vault = to_xx_vault(secure_channels.vault()); let key_exchanger = XXNewKeyExchanger::new(vault).responder().await?; let mailboxes = Self::mailboxes(&addresses, decryptor_outgoing_access_control); @@ -121,6 +122,7 @@ impl DecryptorWorker { state: Some(State::new( Role::Responder, identity, + secure_channels.clone(), addresses.clone(), Box::new(key_exchanger), remote_route, @@ -262,7 +264,7 @@ impl KeyExchangeState { // Key exchange completed, proceed to Identity Exchange let keys = self.key_exchanger.finalize().await?; - let vault = self.identity.vault.clone(); + let vault = &self.secure_channels.vault(); let mut identity_exchange = self.into_identity_exchange( Encryptor::new( @@ -270,7 +272,10 @@ impl KeyExchangeState { 0, to_symmetric_vault(vault.clone()), ), - Decryptor::new(keys.decrypt_key().clone(), to_symmetric_vault(vault)), + Decryptor::new( + keys.decrypt_key().clone(), + to_symmetric_vault(vault.clone()), + ), *keys.h(), ); @@ -356,11 +361,11 @@ impl IdentityExchangeState { self.addresses.decryptor_remote.clone(), self.addresses.decryptor_api.clone(), self.role.is_initiator(), - self.identity.identifier().clone(), + self.identity.identifier(), their_identity_id.clone(), ); - self.identity - .secure_channel_registry + self.secure_channels + .secure_channel_registry() .register_channel(info)?; if self.role.is_initiator() { @@ -396,16 +401,21 @@ impl IdentityExchangeState { &self.addresses.decryptor_remote ); - let their_identity = PublicIdentity::import(&identity, self.identity.vault.clone()).await?; + let identities = self.secure_channels.identities(); + let their_identity = identities + .identities_creation() + .import_identity(identity.as_slice()) + .await?; let their_identity_id = their_identity.identifier(); // Verify responder posses their Identity key - let verified = their_identity + let verified = identities + .identities_keys() .verify_signature( + &their_identity, &Signature::new(signature), &self.auth_hash, None, - self.identity.vault.clone(), ) .await?; @@ -413,8 +423,10 @@ impl IdentityExchangeState { return Err(IdentityError::SecureChannelVerificationFailed.into()); } - self.identity - .update_known_identity(their_identity_id, &their_identity) + self.secure_channels + .identities() + .repository() + .update_known_identity(&their_identity) .await?; info!( @@ -438,22 +450,23 @@ impl IdentityExchangeState { } async fn send_identity(&mut self, ctx: &mut Context, first_sender: bool) -> Result<()> { - // Send our identity - let identity = self.identity.export().await?; // Prove we posses our Identity key let signature = self - .identity - .create_signature(&self.auth_hash, None) + .secure_channels + .identities() + .identities_keys() + .create_signature(&self.identity, &self.auth_hash, None) .await?; + let exported = self.identity.export()?; let auth_msg = if first_sender { IdentityChannelMessage::Request { - identity, + identity: exported, signature: signature.as_ref().to_vec(), } } else { IdentityChannelMessage::Response { - identity, + identity: exported, signature: signature.as_ref().to_vec(), } }; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/encryptor.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor.rs similarity index 97% rename from implementations/rust/ockam/ockam_identity/src/channel/encryptor.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor.rs index b2aff5a507d..1402987e5ab 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/encryptor.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor.rs @@ -1,4 +1,4 @@ -use crate::error::IdentityError; +use crate::identity::IdentityError; use ockam_core::compat::sync::Arc; use ockam_core::compat::vec::Vec; use ockam_core::vault::{KeyId, SymmetricVault}; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/encryptor_worker.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor_worker.rs similarity index 94% rename from implementations/rust/ockam/ockam_identity/src/channel/encryptor_worker.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor_worker.rs index eed6c032d51..0a38a5e5cd0 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/encryptor_worker.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/encryptor_worker.rs @@ -1,7 +1,7 @@ -use crate::api::{EncryptionRequest, EncryptionResponse}; -use crate::channel::addresses::Addresses; -use crate::channel::encryptor::Encryptor; -use crate::error::IdentityError; +use crate::identity::IdentityError; +use crate::secure_channel::addresses::Addresses; +use crate::secure_channel::api::{EncryptionRequest, EncryptionResponse}; +use crate::secure_channel::encryptor::Encryptor; use ockam_core::compat::boxed::Box; use ockam_core::{async_trait, Address, Decodable, Encodable, Route}; use ockam_core::{Any, Result, Routed, TransportMessage, Worker}; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/listener.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/listener.rs similarity index 71% rename from implementations/rust/ockam/ockam_identity/src/channel/listener.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/listener.rs index 0fc85c245e5..e826d2b796d 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/listener.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/listener.rs @@ -1,30 +1,39 @@ -use crate::channel::addresses::Addresses; -use crate::channel::common::CreateResponderChannelMessage; -use crate::channel::decryptor_worker::DecryptorWorker; -use crate::channel::Role; -use crate::{Identity, SecureChannelListenerTrustOptions}; +use crate::identity::Identity; +use crate::secure_channel::addresses::Addresses; +use crate::secure_channel::common::{CreateResponderChannelMessage, Role}; +use crate::secure_channel::trust_options::SecureChannelListenerTrustOptions; +use crate::secure_channel::DecryptorWorker; +use crate::secure_channels::secure_channels::SecureChannels; use ockam_core::compat::boxed::Box; -use ockam_core::{Address, AllowAll, AsyncTryClone, DenyAll, Result, Routed, Worker}; +use ockam_core::compat::sync::Arc; +use ockam_core::{Address, AllowAll, DenyAll, Result, Routed, Worker}; use ockam_node::Context; pub(crate) struct IdentityChannelListener { - trust_options: SecureChannelListenerTrustOptions, + secure_channels: Arc, identity: Identity, + trust_options: SecureChannelListenerTrustOptions, } impl IdentityChannelListener { - pub fn new(trust_options: SecureChannelListenerTrustOptions, identity: Identity) -> Self { + pub fn new( + secure_channels: Arc, + identity: Identity, + trust_options: SecureChannelListenerTrustOptions, + ) -> Self { Self { - trust_options, + secure_channels, identity, + trust_options, } } pub async fn create( ctx: &Context, + secure_channels: Arc, + identity: &Identity, address: Address, trust_options: SecureChannelListenerTrustOptions, - identity: Identity, ) -> Result<()> { if let Some(ciphertext_session) = &trust_options.consumer_session { ciphertext_session.sessions.add_consumer( @@ -34,7 +43,7 @@ impl IdentityChannelListener { ); } - let listener = Self::new(trust_options, identity); + let listener = Self::new(secure_channels.clone(), identity.clone(), trust_options); ctx.start_worker( address, listener, AllowAll, // TODO: @ac allow to customize @@ -56,8 +65,6 @@ impl Worker for IdentityChannelListener { ctx: &mut Self::Context, msg: Routed, ) -> Result<()> { - let identity = self.identity.async_try_clone().await?; - // Check if the Worker that send us this message is a Producer // If yes - decryptor will be added to that session to be able to receive further messages // from that Producer @@ -78,8 +85,9 @@ impl Worker for IdentityChannelListener { DecryptorWorker::create_responder( ctx, - identity, + self.secure_channels.clone(), addresses, + self.identity.clone(), self.trust_options.trust_policy.clone(), access_control.decryptor_outgoing_access_control, msg, diff --git a/implementations/rust/ockam/ockam_identity/src/channel/local_info.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/local_info.rs similarity index 94% rename from implementations/rust/ockam/ockam_identity/src/channel/local_info.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/local_info.rs index b57de150689..c33b535e422 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/local_info.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/local_info.rs @@ -1,4 +1,4 @@ -use crate::{IdentityError, IdentityIdentifier}; +use crate::identity::{IdentityError, IdentityIdentifier}; use ockam_core::compat::vec::Vec; use ockam_core::{Decodable, Encodable, LocalInfo, LocalMessage, Result}; use serde::{Deserialize, Serialize}; @@ -54,8 +54,8 @@ impl IdentitySecureChannelLocalInfo { impl IdentitySecureChannelLocalInfo { /// Key exchange name - pub fn their_identity_id(&self) -> &IdentityIdentifier { - &self.their_identity_id + pub fn their_identity_id(&self) -> IdentityIdentifier { + self.their_identity_id.clone() } } diff --git a/implementations/rust/ockam/ockam_identity/src/channel/messages.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/messages.rs similarity index 100% rename from implementations/rust/ockam/ockam_identity/src/channel/messages.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/messages.rs diff --git a/implementations/rust/ockam/ockam_identity/src/secure_channel/mod.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/mod.rs new file mode 100644 index 00000000000..6f6749dbb8c --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/mod.rs @@ -0,0 +1,28 @@ +/// Access control data for workers +pub mod access_control; +mod addresses; +mod api; +mod common; +mod decryptor; +mod decryptor_state; +mod decryptor_worker; +mod encryptor; +mod encryptor_worker; +mod listener; +mod local_info; +mod messages; +mod registry; +mod trust_options; +/// List of trust policies to setup ABAC controls +pub mod trust_policy; + +pub use access_control::*; +pub(crate) use addresses::*; +pub use api::*; +pub(crate) use common::*; +pub(crate) use decryptor_worker::*; +pub(crate) use listener::*; +pub use local_info::*; +pub use registry::*; +pub use trust_options::*; +pub use trust_policy::*; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/registry.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/registry.rs similarity index 93% rename from implementations/rust/ockam/ockam_identity/src/channel/registry.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/registry.rs index c5d3213f631..a971b5606cd 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/registry.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/registry.rs @@ -1,5 +1,4 @@ -use crate::error::IdentityError; -use crate::IdentityIdentifier; +use crate::identity::{IdentityError, IdentityIdentifier}; use ockam_core::compat::collections::BTreeMap; use ockam_core::compat::sync::{Arc, RwLock}; use ockam_core::compat::vec::Vec; @@ -60,17 +59,17 @@ impl SecureChannelRegistryEntry { /// If we are were initiating this channel pub fn is_initiator(&self) -> bool { - self.is_initiator + self.clone().is_initiator } /// Our `IdentityIdentifier` - pub fn my_id(&self) -> &IdentityIdentifier { - &self.my_id + pub fn my_id(&self) -> IdentityIdentifier { + self.my_id.clone() } /// Their `IdentityIdentifier` - pub fn their_id(&self) -> &IdentityIdentifier { - &self.their_id + pub fn their_id(&self) -> IdentityIdentifier { + self.their_id.clone() } } diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_options.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_options.rs similarity index 97% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_options.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_options.rs index 10253326e26..99c41e0a253 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_options.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_options.rs @@ -1,6 +1,7 @@ -use crate::channel::addresses::Addresses; -use crate::error::IdentityError; -use crate::{TrustEveryonePolicy, TrustPolicy}; +use crate::secure_channel::addresses::Addresses; +use crate::secure_channel::trust_policy::TrustEveryonePolicy; +use crate::secure_channel::TrustPolicy; +use crate::IdentityError; use ockam_core::compat::sync::Arc; use ockam_core::sessions::{SessionId, SessionOutgoingAccessControl, SessionPolicy, Sessions}; use ockam_core::{AllowAll, OutgoingAccessControl, Result}; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/all_trust_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/all_trust_policy.rs similarity index 85% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/all_trust_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/all_trust_policy.rs index 46fcd439bde..7e2f1a974c4 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/all_trust_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/all_trust_policy.rs @@ -1,5 +1,6 @@ -use crate::{SecureChannelTrustInfo, TrustPolicy}; -use ockam_core::{async_trait, compat::boxed::Box}; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::{AsyncTryClone, Result}; /// Succeeds only if both `TrustPolicy` checks succeeded @@ -27,9 +28,10 @@ impl TrustPolicy for AllTrustPolicy { #[cfg(test)] mod test { - use crate::{IdentityIdentifier, SecureChannelTrustInfo, TrustPolicy}; + use crate::identity::IdentityIdentifier; + use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; + use ockam_core::async_trait; use ockam_core::Result; - use ockam_core::{async_trait, compat::boxed::Box}; #[tokio::test] async fn test() { diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/any_trust_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/any_trust_policy.rs similarity index 86% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/any_trust_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/any_trust_policy.rs index 8e5f5e1d9f0..9739dd7d79b 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/any_trust_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/any_trust_policy.rs @@ -1,5 +1,6 @@ -use crate::{SecureChannelTrustInfo, TrustPolicy}; -use ockam_core::{async_trait, compat::boxed::Box}; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::{AsyncTryClone, Result}; /// Succeeds if any or both `TrustPolicy` checks succeeded @@ -28,9 +29,10 @@ impl TrustPolicy for AnyTrustPolicy { #[cfg(test)] mod test { - use crate::{IdentityIdentifier, SecureChannelTrustInfo, TrustPolicy}; + use crate::identity::IdentityIdentifier; + use crate::secure_channel::{SecureChannelTrustInfo, TrustPolicy}; + use ockam_core::async_trait; use ockam_core::Result; - use ockam_core::{async_trait, compat::boxed::Box}; #[tokio::test] async fn test() { diff --git a/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/mod.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/mod.rs new file mode 100644 index 00000000000..43b1ed9a1ac --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/mod.rs @@ -0,0 +1,15 @@ +mod all_trust_policy; +mod any_trust_policy; +mod trust_everyone_policy; +mod trust_identifier_policy; +mod trust_multi_identifier_policy; +mod trust_policy_type; +mod trust_public_key_policy; + +pub use all_trust_policy::*; +pub use any_trust_policy::*; +pub use trust_everyone_policy::*; +pub use trust_identifier_policy::*; +pub use trust_multi_identifier_policy::*; +pub use trust_policy_type::*; +pub use trust_public_key_policy::*; diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_everyone_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_everyone_policy.rs similarity index 65% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_everyone_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_everyone_policy.rs index 5765d22049f..62f24d0d150 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_everyone_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_everyone_policy.rs @@ -1,6 +1,7 @@ -use crate::{SecureChannelTrustInfo, TrustPolicy}; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::{allow, Result}; -use ockam_core::{async_trait, compat::boxed::Box}; /// Trust any participant #[derive(Clone)] diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_identifier_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_identifier_policy.rs similarity index 75% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_identifier_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_identifier_policy.rs index b062f9d49d2..4c41060ab29 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_identifier_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_identifier_policy.rs @@ -1,6 +1,8 @@ -use crate::{IdentityIdentifier, SecureChannelTrustInfo, TrustPolicy}; +use crate::identity::IdentityIdentifier; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::Result; -use ockam_core::{async_trait, compat::boxed::Box}; /// `TrustPolicy` based on pre-known `IdentityIdentifier` of the other participant #[derive(Clone)] diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_multi_identifier_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_multi_identifier_policy.rs similarity index 86% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_multi_identifier_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_multi_identifier_policy.rs index 1612b71aa0a..517ed1a88ac 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_multi_identifier_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_multi_identifier_policy.rs @@ -1,11 +1,9 @@ -use crate::{IdentityIdentifier, SecureChannelTrustInfo, TrustPolicy}; +use crate::identity::IdentityIdentifier; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::compat::boxed::Box; use ockam_core::compat::string::String; use ockam_core::compat::string::ToString; -use ockam_core::{ - async_trait, - compat::{boxed::Box, vec::Vec}, - Result, -}; +use ockam_core::{async_trait, compat::vec::Vec, Result}; use tracing::info; /// `TrustPolicy` based on list of pre-known `IdentityIdentifier`s of the possible participants diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_policy_type.rs similarity index 80% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_policy_type.rs index 83071041e61..b9b363aef76 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_policy_type.rs @@ -1,4 +1,3 @@ -use crate::IdentityIdentifier; use ockam_core::{ async_trait, compat::{boxed::Box, sync::Arc}, @@ -6,23 +5,14 @@ use ockam_core::{ }; use serde::{Deserialize, Serialize}; -mod trust_identifier_policy; -pub use trust_identifier_policy::*; -mod trust_multi_identifier_policy; -pub use trust_multi_identifier_policy::*; -mod all_trust_policy; -pub use all_trust_policy::*; -mod any_trust_policy; -pub use any_trust_policy::*; -mod trust_everyone_policy; -pub use trust_everyone_policy::*; -mod trust_public_key_policy; -pub use trust_public_key_policy::*; +use crate::identity::IdentityIdentifier; +use crate::secure_channel::trust_policy::{AllTrustPolicy, AnyTrustPolicy}; /// Authenticated data of the newly created SecureChannel to perform `TrustPolicy` check #[derive(Clone, Serialize, Deserialize)] pub struct SecureChannelTrustInfo { - their_identity_id: IdentityIdentifier, + /// identity of the other end of the secure channel + pub their_identity_id: IdentityIdentifier, } impl SecureChannelTrustInfo { diff --git a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_public_key_policy.rs b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_public_key_policy.rs similarity index 63% rename from implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_public_key_policy.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_public_key_policy.rs index fb98347875e..4623beb1726 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel/trust_policy/trust_public_key_policy.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channel/trust_policy/trust_public_key_policy.rs @@ -1,16 +1,17 @@ -use crate::{Identity, SecureChannelTrustInfo, TrustPolicy}; +use crate::identities::Identities; +use crate::secure_channel::trust_policy::{SecureChannelTrustInfo, TrustPolicy}; +use ockam_core::async_trait; +use ockam_core::compat::boxed::Box; use ockam_core::compat::string::String; +use ockam_core::compat::sync::Arc; use ockam_core::vault::PublicKey; -use ockam_core::{async_trait, compat::boxed::Box}; -use ockam_core::{AsyncTryClone, Result}; +use ockam_core::Result; /// `TrustPolicy` based on pre-known `PublicKey` of the other participant -#[derive(AsyncTryClone)] -#[async_try_clone(crate = "ockam_core")] pub struct TrustPublicKeyPolicy { public_key: PublicKey, public_key_label: String, - identity: Identity, + identities: Arc, } impl TrustPublicKeyPolicy { @@ -18,12 +19,12 @@ impl TrustPublicKeyPolicy { pub fn new( public_key: PublicKey, public_key_label: impl Into, - identity: Identity, + identities: Arc, ) -> Self { Self { public_key, public_key_label: public_key_label.into(), - identity, + identities, } } } @@ -32,15 +33,16 @@ impl TrustPublicKeyPolicy { impl TrustPolicy for TrustPublicKeyPolicy { async fn check(&self, trust_info: &SecureChannelTrustInfo) -> Result { let their_identity = match self - .identity - .get_known_identity(trust_info.their_identity_id()) + .identities + .identities_repository + .get_identity(trust_info.their_identity_id()) .await? { Some(their_identity) => their_identity, None => return Ok(false), }; - match their_identity.get_public_key(&self.public_key_label) { + match their_identity.get_labelled_public_key(&self.public_key_label) { Ok(pub_key) => Ok(pub_key == self.public_key), _ => Ok(false), } diff --git a/implementations/rust/ockam/ockam_identity/src/secure_channels/mod.rs b/implementations/rust/ockam/ockam_identity/src/secure_channels/mod.rs new file mode 100644 index 00000000000..d0f6d14d3e9 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/secure_channels/mod.rs @@ -0,0 +1,7 @@ +/// Services for creating secure channels +#[allow(clippy::module_inception)] +pub mod secure_channels; +mod secure_channels_builder; + +pub use secure_channels::*; +pub use secure_channels_builder::*; diff --git a/implementations/rust/ockam/ockam_identity/src/channel.rs b/implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels.rs similarity index 59% rename from implementations/rust/ockam/ockam_identity/src/channel.rs rename to implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels.rs index 5a00c612a76..f5f13401049 100644 --- a/implementations/rust/ockam/ockam_identity/src/channel.rs +++ b/implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels.rs @@ -1,51 +1,66 @@ -mod addresses; -mod decryptor; -mod decryptor_state; -mod decryptor_worker; -mod encryptor; -mod encryptor_worker; -mod listener; -mod messages; - -mod common; -mod local_info; -mod registry; -mod trust_options; -mod trust_policy; - -pub use common::*; -pub use local_info::*; -pub use registry::*; -pub use trust_options::*; -pub use trust_policy::*; - -/// `AccessControl` implementation based on SecureChannel authentication guarantees -pub mod access_control; -/// SecureChannel API -pub mod api; - -use crate::channel::addresses::Addresses; -use crate::channel::decryptor_worker::DecryptorWorker; -use crate::channel::listener::IdentityChannelListener; -use crate::error::IdentityError; -use crate::Identity; +use crate::identities::Identities; +use crate::identities::IdentitiesVault; +use crate::identity::{Identity, IdentityError}; +use crate::secure_channel::{ + Addresses, DecryptorWorker, IdentityChannelListener, Role, SecureChannelListenerTrustOptions, + SecureChannelRegistry, SecureChannelTrustOptions, +}; use core::time::Duration; -use ockam_core::{Address, AsyncTryClone, Result, Route}; +use ockam_core::compat::sync::Arc; +use ockam_core::Result; +use ockam_core::{Address, Route}; +use ockam_node::Context; + +/// Identity implementation +#[derive(Clone)] +pub struct SecureChannels { + pub(crate) identities: Arc, + pub(crate) secure_channel_registry: SecureChannelRegistry, +} + +impl SecureChannels { + /// Constructor + pub(crate) fn new( + identities: Arc, + secure_channel_registry: SecureChannelRegistry, + ) -> Self { + Self { + identities, + secure_channel_registry, + } + } + + /// Return the identities services associated to this service + pub fn identities(&self) -> Arc { + self.identities.clone() + } + + /// Return the vault associated to this service + pub fn vault(&self) -> Arc { + self.identities.vault.clone() + } + + /// Return the secure channel registry + pub fn secure_channel_registry(&self) -> SecureChannelRegistry { + self.secure_channel_registry.clone() + } +} -impl Identity { +impl SecureChannels { /// Spawns a SecureChannel listener at given `Address` with given [`SecureChannelListenerTrustOptions`] pub async fn create_secure_channel_listener( &self, + ctx: &Context, + identity: &Identity, address: impl Into
, trust_options: impl Into, ) -> Result<()> { - let identity_clone = self.async_try_clone().await?; - IdentityChannelListener::create( - &self.ctx, + ctx, + Arc::new(self.clone()), + identity, address.into(), trust_options.into(), - identity_clone, ) .await?; @@ -55,11 +70,11 @@ impl Identity { /// Initiate a SecureChannel using `Route` to the SecureChannel listener and [`SecureChannelTrustOptions`] pub async fn create_secure_channel( &self, + ctx: &Context, + identity: &Identity, route: impl Into, trust_options: impl Into, ) -> Result
{ - let identity_clone = self.async_try_clone().await?; - let addresses = Addresses::generate(Role::Initiator); let trust_options = trust_options.into(); @@ -67,9 +82,10 @@ impl Identity { let access_control = trust_options.create_access_control(); DecryptorWorker::create_initiator( - &self.ctx, + ctx, + Arc::new(self.clone()), + identity.clone(), route.into(), - identity_clone, addresses, trust_options.trust_policy, access_control.decryptor_outgoing_access_control, @@ -81,12 +97,12 @@ impl Identity { /// Extended function to create a SecureChannel with [`SecureChannelTrustOptions`] pub async fn create_secure_channel_extended( &self, + ctx: &Context, + identity: &Identity, route: impl Into, trust_options: impl Into, timeout: Duration, ) -> Result
{ - let identity_clone = self.async_try_clone().await?; - let addresses = Addresses::generate(Role::Initiator); let trust_options = trust_options.into(); @@ -94,9 +110,10 @@ impl Identity { let access_control = trust_options.create_access_control(); DecryptorWorker::create_initiator( - &self.ctx, + ctx, + Arc::new(self.clone()), + identity.clone(), route.into(), - identity_clone, addresses, trust_options.trust_policy, access_control.decryptor_outgoing_access_control, @@ -106,15 +123,13 @@ impl Identity { } /// Stop a SecureChannel given an encryptor address - pub async fn stop_secure_channel(&self, channel: &Address) -> Result<()> { + pub async fn stop_secure_channel(&self, ctx: &Context, channel: &Address) -> Result<()> { if let Some(entry) = self.secure_channel_registry.unregister_channel(channel) { - let err1 = self - .ctx + let err1 = ctx .stop_worker(entry.encryptor_messaging_address().clone()) .await .err(); - let err2 = self - .ctx + let err2 = ctx .stop_worker(entry.decryptor_messaging_address().clone()) .await .err(); diff --git a/implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels_builder.rs b/implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels_builder.rs new file mode 100644 index 00000000000..767daef9c07 --- /dev/null +++ b/implementations/rust/ockam/ockam_identity/src/secure_channels/secure_channels_builder.rs @@ -0,0 +1,89 @@ +use crate::identities::{Identities, IdentitiesRepository, Storage}; +use crate::secure_channel::SecureChannelRegistry; +use crate::secure_channels::SecureChannels; +use crate::{identities, IdentitiesBuilder, IdentitiesVault}; +use ockam_core::compat::sync::Arc; + +/// This struct supports all the services related to secure channels +#[derive(Clone)] +pub struct SecureChannelsBuilder { + identities_builder: IdentitiesBuilder, + registry: SecureChannelRegistry, +} + +/// Create default, in-memory, secure channels (mostly for examples and testing) +pub fn secure_channels() -> Arc { + builder().build() +} + +/// Create a builder for secure channels +pub fn builder() -> SecureChannelsBuilder { + SecureChannelsBuilder { + identities_builder: identities::builder(), + registry: SecureChannelRegistry::new(), + } +} + +impl SecureChannelsBuilder { + /// Set a specific storage for the secure channels vault + pub fn with_vault_storage( + &mut self, + storage: Arc, + ) -> SecureChannelsBuilder { + self.identities_builder = self.identities_builder.with_vault_storage(storage); + self.clone() + } + + /// Set a specific vault for secure channels + pub fn with_identities_vault( + &mut self, + vault: Arc, + ) -> SecureChannelsBuilder { + self.identities_builder = self.identities_builder.with_identities_vault(vault); + self.clone() + } + + /// Set a specific storage for the identities repository + pub fn with_identities_storage(&mut self, storage: Arc) -> SecureChannelsBuilder { + self.identities_builder = self.identities_builder.with_identities_storage(storage); + self.clone() + } + + /// Set a specific identities repository + pub fn with_identities_repository( + &mut self, + repository: Arc, + ) -> SecureChannelsBuilder { + self.identities_builder = self + .identities_builder + .with_identities_repository(repository); + self.clone() + } + + /// Set a specific identities + pub fn with_identities(&mut self, identities: Arc) -> SecureChannelsBuilder { + self.identities_builder = self + .identities_builder + .with_identities_repository(identities.repository()) + .with_identities_vault(identities.vault()); + self.clone() + } + + /// Set a specific channel registry + pub fn with_secure_channels_registry( + &mut self, + registry: SecureChannelRegistry, + ) -> SecureChannelsBuilder { + self.registry = registry; + self.clone() + } + + /// Return the vault used by this builder + /// Build secure channels + pub fn build(&self) -> Arc { + Arc::new(SecureChannels::new( + self.identities_builder.build(), + SecureChannelRegistry::new(), + )) + } +} diff --git a/implementations/rust/ockam/ockam_identity/tests/channel.rs b/implementations/rust/ockam/ockam_identity/tests/channel.rs index cfd2aa877f6..88f216c4f6c 100644 --- a/implementations/rust/ockam/ockam_identity/tests/channel.rs +++ b/implementations/rust/ockam/ockam_identity/tests/channel.rs @@ -2,31 +2,31 @@ use core::sync::atomic::{AtomicU8, Ordering}; use core::time::Duration; use ockam_core::compat::sync::Arc; use ockam_core::{route, Address, AllowAll, Any, DenyAll, Mailboxes, Result, Routed, Worker}; -use ockam_identity::access_control::IdentityAccessControlBuilder; -use ockam_identity::api::{DecryptionResponse, EncryptionRequest, EncryptionResponse}; +use ockam_identity::secure_channels::secure_channels; use ockam_identity::{ - Identity, IdentitySecureChannelLocalInfo, TrustEveryonePolicy, TrustIdentifierPolicy, + DecryptionResponse, EncryptionRequest, EncryptionResponse, IdentityAccessControlBuilder, + IdentitySecureChannelLocalInfo, TrustEveryonePolicy, TrustIdentifierPolicy, }; use ockam_node::{Context, WorkerBuilder}; -use ockam_vault::Vault; use tokio::time::sleep; #[ockam_macros::test] async fn test_channel(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, alice_vault).await?; - let bob = Identity::create(ctx, bob_vault).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier().clone()); - let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier().clone()); + let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier()); + let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier()); - bob.create_secure_channel_listener("bob_listener", bob_trust_policy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_listener", bob_trust_policy) .await?; - let alice_channel = alice - .create_secure_channel(route!["bob_listener"], alice_trust_policy) + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, route!["bob_listener"], alice_trust_policy) .await?; let mut child_ctx = ctx @@ -68,20 +68,21 @@ async fn test_channel(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_channel_registry(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, alice_vault).await?; - let bob = Identity::create(ctx, bob_vault).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - bob.create_secure_channel_listener("bob_listener", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_listener", TrustEveryonePolicy) .await?; - let alice_channel = alice - .create_secure_channel(route!["bob_listener"], TrustEveryonePolicy) + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, route!["bob_listener"], TrustEveryonePolicy) .await?; - let alice_channel_data = alice + let alice_channel_data = secure_channels .secure_channel_registry() .get_channel_by_encryptor_address(&alice_channel) .unwrap(); @@ -111,7 +112,7 @@ async fn test_channel_registry(ctx: &mut Context) -> Result<()> { let bob_channel = return_route.next().unwrap().clone(); - let bob_channel_data = bob + let bob_channel_data = secure_channels .secure_channel_registry() .get_channel_by_encryptor_address(&bob_channel) .unwrap(); @@ -125,17 +126,18 @@ async fn test_channel_registry(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_channel_api(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, alice_vault).await?; - let bob = Identity::create(ctx, bob_vault).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - bob.create_secure_channel_listener("bob_listener", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_listener", TrustEveryonePolicy) .await?; - let alice_channel = alice - .create_secure_channel(route!["bob_listener"], TrustEveryonePolicy) + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, route!["bob_listener"], TrustEveryonePolicy) .await?; let mut bob_ctx = ctx @@ -159,12 +161,12 @@ async fn test_channel_api(ctx: &mut Context) -> Result<()> { let bob_channel = return_route.next().unwrap().clone(); - let alice_channel_data = alice + let alice_channel_data = secure_channels .secure_channel_registry() .get_channel_by_encryptor_address(&alice_channel) .unwrap(); - let bob_channel_data = bob + let bob_channel_data = secure_channels .secure_channel_registry() .get_channel_by_encryptor_address(&bob_channel) .unwrap(); @@ -221,28 +223,38 @@ async fn test_channel_api(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_tunneled_secure_channel_works(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, vault.clone()).await?; - let bob = Identity::create(ctx, vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier().clone()); - let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier().clone()); + let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier()); + let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier()); - bob.create_secure_channel_listener("bob_listener", bob_trust_policy.clone()) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_listener", bob_trust_policy.clone()) .await?; - let alice_channel = alice - .create_secure_channel(route!["bob_listener"], alice_trust_policy.clone()) + let alice_channel = secure_channels + .create_secure_channel( + ctx, + &alice, + route!["bob_listener"], + alice_trust_policy.clone(), + ) .await?; - bob.create_secure_channel_listener("bob_another_listener", bob_trust_policy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_another_listener", bob_trust_policy.clone()) .await?; - let alice_another_channel = alice + let alice_another_channel = secure_channels .create_secure_channel( + ctx, + &alice, route![alice_channel, "bob_another_listener"], - alice_trust_policy, + alice_trust_policy.clone(), ) .await?; @@ -274,38 +286,56 @@ async fn test_tunneled_secure_channel_works(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_double_tunneled_secure_channel_works(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, vault.clone()).await?; - let bob = Identity::create(ctx, vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier().clone()); - let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier().clone()); + let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier()); + let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier()); - bob.create_secure_channel_listener("bob_listener", bob_trust_policy.clone()) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_listener", bob_trust_policy.clone()) .await?; - let alice_channel = alice - .create_secure_channel(route!["bob_listener"], alice_trust_policy.clone()) + let alice_channel = secure_channels + .create_secure_channel( + ctx, + &alice, + route!["bob_listener"], + alice_trust_policy.clone(), + ) .await?; - bob.create_secure_channel_listener("bob_another_listener", bob_trust_policy.clone()) + secure_channels + .create_secure_channel_listener(ctx, &bob, "bob_another_listener", bob_trust_policy.clone()) .await?; - let alice_another_channel = alice + let alice_another_channel = secure_channels .create_secure_channel( + ctx, + &alice, route![alice_channel, "bob_another_listener"], alice_trust_policy.clone(), ) .await?; - bob.create_secure_channel_listener("bob_yet_another_listener", bob_trust_policy) + secure_channels + .create_secure_channel_listener( + ctx, + &bob, + "bob_yet_another_listener", + bob_trust_policy.clone(), + ) .await?; - let alice_yet_another_channel = alice + let alice_yet_another_channel = secure_channels .create_secure_channel( + ctx, + &alice, route![alice_another_channel, "bob_yet_another_listener"], - alice_trust_policy, + alice_trust_policy.clone(), ) .await?; @@ -337,27 +367,33 @@ async fn test_double_tunneled_secure_channel_works(ctx: &mut Context) -> Result< #[ockam_macros::test] async fn test_many_times_tunneled_secure_channel_works(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, vault.clone()).await?; - let bob = Identity::create(ctx, vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier().clone()); - let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier().clone()); + let alice_trust_policy = TrustIdentifierPolicy::new(bob.identifier()); + let bob_trust_policy = TrustIdentifierPolicy::new(alice.identifier()); let n = rand::random::() % 5 + 4; let mut channels: Vec
= vec![]; + for i in 0..n { - bob.create_secure_channel_listener(i.to_string(), bob_trust_policy.clone()) + secure_channels + .create_secure_channel_listener(ctx, &bob, i.to_string(), bob_trust_policy.clone()) .await?; + let channel_route = if i > 0 { route![channels.pop().unwrap(), i.to_string()] } else { route![i.to_string()] }; - let alice_channel = alice - .create_secure_channel(channel_route, alice_trust_policy.clone()) + + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, channel_route, alice_trust_policy.clone()) .await?; + channels.push(alice_channel); } @@ -415,12 +451,13 @@ async fn access_control__known_participant__should_pass_messages(ctx: &mut Conte received_count: received_count.clone(), }; - let vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, vault.clone()).await?; - let bob = Identity::create(ctx, vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let access_control = IdentityAccessControlBuilder::new_with_id(alice.identifier().clone()); + let access_control = IdentityAccessControlBuilder::new_with_id(alice.identifier()); WorkerBuilder::with_access_control( Arc::new(access_control), Arc::new(DenyAll), @@ -430,11 +467,12 @@ async fn access_control__known_participant__should_pass_messages(ctx: &mut Conte .start(ctx) .await?; - bob.create_secure_channel_listener("listener", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "listener", TrustEveryonePolicy) .await?; - let alice_channel = alice - .create_secure_channel("listener", TrustEveryonePolicy) + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, route!["listener"], TrustEveryonePolicy) .await?; let child_ctx = ctx @@ -466,12 +504,13 @@ async fn access_control__unknown_participant__should_not_pass_messages( received_count: received_count.clone(), }; - let vault = Vault::create(); + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); - let alice = Identity::create(ctx, vault.clone()).await?; - let bob = Identity::create(ctx, vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - let access_control = IdentityAccessControlBuilder::new_with_id(bob.identifier().clone()); + let access_control = IdentityAccessControlBuilder::new_with_id(bob.identifier()); WorkerBuilder::with_access_control( Arc::new(access_control), Arc::new(DenyAll), @@ -481,11 +520,12 @@ async fn access_control__unknown_participant__should_not_pass_messages( .start(ctx) .await?; - bob.create_secure_channel_listener("listener", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &bob, "listener", TrustEveryonePolicy) .await?; - let alice_channel = alice - .create_secure_channel("listener", TrustEveryonePolicy) + let alice_channel = secure_channels + .create_secure_channel(ctx, &alice, route!["listener"], TrustEveryonePolicy) .await?; let child_ctx = ctx diff --git a/implementations/rust/ockam/ockam_identity/tests/ciphertext_message_flow_auth.rs b/implementations/rust/ockam/ockam_identity/tests/ciphertext_message_flow_auth.rs index 363dcbc33c6..8867eee799c 100644 --- a/implementations/rust/ockam/ockam_identity/tests/ciphertext_message_flow_auth.rs +++ b/implementations/rust/ockam/ockam_identity/tests/ciphertext_message_flow_auth.rs @@ -63,8 +63,10 @@ async fn test2(ctx: &mut Context) -> Result<()> { message_should_pass(ctx, &channel_to_alice).await?; let res = channel_to_bob - .identity + .secure_channels .create_secure_channel_extended( + ctx, + &channel_to_bob.identity, route![connection_to_bob.address.clone(), "listener"], TrustEveryonePolicy, Duration::from_secs(1), @@ -103,8 +105,10 @@ async fn test3(ctx: &mut Context) -> Result<()> { message_should_pass(ctx, &channel_to_alice).await?; let res = channel_to_bob - .identity + .secure_channels .create_secure_channel_extended( + ctx, + &channel_to_bob.identity, route![connection_to_bob.address.clone(), "listener"], TrustEveryonePolicy, Duration::from_secs(1), @@ -143,8 +147,10 @@ async fn test4(ctx: &mut Context) -> Result<()> { message_should_pass(ctx, &channel_to_alice).await?; let res = channel_to_bob - .identity + .secure_channels .create_secure_channel_extended( + ctx, + &channel_to_bob.identity, route![connection_to_bob.address.clone(), "listener"], TrustEveryonePolicy, Duration::from_secs(1), @@ -183,8 +189,10 @@ async fn test5(ctx: &mut Context) -> Result<()> { message_should_pass(ctx, &channel_to_bob).await?; let res = channel_to_alice - .identity + .secure_channels .create_secure_channel_extended( + ctx, + &channel_to_alice.identity, route![connection_to_alice.address.clone(), "listener"], TrustEveryonePolicy, Duration::from_secs(1), diff --git a/implementations/rust/ockam/ockam_identity/tests/common/mod.rs b/implementations/rust/ockam/ockam_identity/tests/common/mod.rs index d693c2fa322..f3fc3944e47 100644 --- a/implementations/rust/ockam/ockam_identity/tests/common/mod.rs +++ b/implementations/rust/ockam/ockam_identity/tests/common/mod.rs @@ -2,12 +2,13 @@ use ockam_core::compat::net::SocketAddr; use ockam_core::sessions::{SessionId, SessionPolicy, Sessions}; use ockam_core::{route, Address, AllowAll, Result, Route}; use ockam_identity::{ - Identity, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, TrustEveryonePolicy, + secure_channels, Identity, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, + SecureChannels, TrustEveryonePolicy, }; use ockam_node::{Context, MessageReceiveOptions}; use ockam_transport_tcp::{TcpConnectionTrustOptions, TcpListenerTrustOptions, TcpTransport}; -use ockam_vault::Vault; use rand::random; +use std::sync::Arc; #[allow(dead_code)] pub async fn message_should_pass(ctx: &Context, address: &Address) -> Result<()> { @@ -206,12 +207,13 @@ async fn create_tcp_connection( #[allow(dead_code)] pub struct SecureChannelListenerInfo { pub identity: Identity, + pub secure_channels: Arc, } impl SecureChannelListenerInfo { #[allow(dead_code)] pub fn get_channel(&self) -> Address { - self.identity + self.secure_channels .secure_channel_registry() .get_channel_list() .first() @@ -227,7 +229,10 @@ pub async fn create_secure_channel_listener( session: &Option<(Sessions, SessionId)>, with_tcp_listener: bool, ) -> Result { - let identity = Identity::create(ctx, Vault::create()).await?; + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); + + let identity = identities_creation.create_identity().await?; let trust_options = SecureChannelListenerTrustOptions::new().with_trust_policy(TrustEveryonePolicy); @@ -243,17 +248,21 @@ pub async fn create_secure_channel_listener( trust_options }; - identity - .create_secure_channel_listener("listener", trust_options) + secure_channels + .create_secure_channel_listener(ctx, &identity, "listener", trust_options) .await?; - let info = SecureChannelListenerInfo { identity }; + let info = SecureChannelListenerInfo { + secure_channels, + identity, + }; Ok(info) } #[allow(dead_code)] pub struct SecureChannelInfo { + pub secure_channels: Arc, pub identity: Identity, pub address: Address, } @@ -263,7 +272,10 @@ pub async fn create_secure_channel( ctx: &Context, connection: &TcpConnectionInfo, ) -> Result { - let identity = Identity::create(ctx, Vault::create()).await?; + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); + + let identity = identities_creation.create_identity().await?; let trust_options = SecureChannelTrustOptions::new(); let trust_options = if let Some((sessions, session_id)) = &connection.session { @@ -272,14 +284,20 @@ pub async fn create_secure_channel( trust_options }; - let address = identity + let address = secure_channels .create_secure_channel( + ctx, + &identity, route![connection.address.clone(), "listener"], trust_options, ) .await?; - let info = SecureChannelInfo { identity, address }; + let info = SecureChannelInfo { + secure_channels, + identity, + address, + }; Ok(info) } diff --git a/implementations/rust/ockam/ockam_identity/tests/credentials.rs b/implementations/rust/ockam/ockam_identity/tests/credentials.rs index 450bcc4515d..e0d669ee212 100644 --- a/implementations/rust/ockam/ockam_identity/tests/credentials.rs +++ b/implementations/rust/ockam/ockam_identity/tests/credentials.rs @@ -1,64 +1,60 @@ -use ockam_core::compat::{boxed::Box, sync::Arc}; +use ockam_core::compat::sync::Arc; use ockam_core::{async_trait, AllowAll, Any, DenyAll, Mailboxes}; use ockam_core::{route, Result, Routed, Worker}; -use ockam_identity::authenticated_storage::{ - mem::InMemoryStorage, AuthenticatedAttributeStorage, IdentityAttributeStorage, - IdentityAttributeStorageReader, -}; -use ockam_identity::credential::access_control::CredentialAccessControl; -use ockam_identity::credential::Credential; -use ockam_identity::{Identity, TrustEveryonePolicy, TrustIdentifierPolicy}; +use ockam_identity::secure_channels::secure_channels; +use ockam_identity::{ + CredentialAccessControl, CredentialData, TrustEveryonePolicy, TrustIdentifierPolicy, +}; use ockam_node::{Context, WorkerBuilder}; -use ockam_vault::Vault; use std::sync::atomic::{AtomicI8, Ordering}; use std::time::Duration; #[ockam_macros::test] async fn full_flow_oneway(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); - - let authenticated_attribute_storage = - AuthenticatedAttributeStorage::new(Arc::new(InMemoryStorage::new())); - - let authority = Identity::create(ctx, vault.clone()).await?; - let server = Identity::create(ctx, vault.clone()).await?; - - server - .create_secure_channel_listener("listener", TrustEveryonePolicy) + let secure_channels = secure_channels(); + let identities = secure_channels.identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let credentials = identities.credentials(); + let credentials_service = identities.credentials_server(); + + let authority = identities_creation.create_identity().await?; + let server = identities_creation.create_identity().await?; + let client = identities_creation.create_identity().await?; + + secure_channels + .create_secure_channel_listener(ctx, &server, "listener", TrustEveryonePolicy) .await?; - let authorities = vec![authority.to_public().await?]; - server - .start_credential_exchange_worker( - authorities, - "credential_exchange", - false, - Arc::new(authenticated_attribute_storage.clone()), - ) + let authorities = vec![authority.clone()]; + credentials_service + .start(ctx, None, authorities, "credential_exchange".into(), false) .await?; - let client = Identity::create(ctx, vault).await?; - let channel = client + let channel = secure_channels .create_secure_channel( + ctx, + &client, route!["listener"], TrustIdentifierPolicy::new(server.identifier().clone()), ) .await?; - let credential_builder = Credential::builder(client.identifier().clone()); - let credential = credential_builder.with_attribute("is_superuser", b"true"); + let credential_data = CredentialData::builder(client.identifier(), authority.identifier()) + .with_attribute("is_superuser", b"true") + .build()?; - let credential = authority.issue_credential(credential).await?; - - client.set_credential(credential).await; + let credential = credentials + .issue_credential(&authority, credential_data) + .await?; - client - .present_credential(route![channel, "credential_exchange"], None) + credentials_service + .present_credential(ctx, route![channel, "credential_exchange"], credential) .await?; - let attrs = authenticated_attribute_storage - .get_attributes(client.identifier()) + let attrs = identities_repository + .get_attributes(&client.identifier()) .await? .unwrap(); @@ -71,132 +67,113 @@ async fn full_flow_oneway(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn full_flow_twoway(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); - let authenticated_attribute_storage_client_1 = - AuthenticatedAttributeStorage::new(Arc::new(InMemoryStorage::new())); - let storage2 = Arc::new(InMemoryStorage::new()); - let authenticated_attribute_storage_client_2 = - AuthenticatedAttributeStorage::new(storage2.clone()); - - let authority = Identity::create(ctx, vault.clone()).await?; - let client2 = Identity::create(ctx, vault.clone()).await?; - - let credential2 = - Credential::builder(client2.identifier().clone()).with_attribute("is_admin", b"true"); - - let credential2 = authority.issue_credential(credential2).await?; - client2.set_credential(credential2).await; + let secure_channels = secure_channels(); + let identities = secure_channels.identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let credentials = identities.credentials(); + let credentials_service = identities.credentials_server(); + + let authority = identities_creation.create_identity().await?; + let client1 = identities_creation.create_identity().await?; + let client2 = identities_creation.create_identity().await?; + + let credential_data = CredentialData::builder(client1.identifier(), authority.identifier()) + .with_attribute("is_admin", b"true") + .build()?; + let credential = credentials + .issue_credential(&authority, credential_data) + .await?; - client2 - .create_secure_channel_listener("listener", TrustEveryonePolicy) + secure_channels + .create_secure_channel_listener(ctx, &client1, "listener", TrustEveryonePolicy) .await?; - let authorities = vec![authority.to_public().await?]; - client2 - .start_credential_exchange_worker( + let authorities = vec![authority.clone()]; + credentials_service + .start( + ctx, + Some(credential.clone()), authorities.clone(), - "credential_exchange", + "credential_exchange".into(), true, - Arc::new(authenticated_attribute_storage_client_2), ) .await?; - let client1 = Identity::create(ctx, vault).await?; - - let credential1 = - Credential::builder(client1.identifier().clone()).with_attribute("is_user", b"true"); - - let credential1 = authority.issue_credential(credential1).await?; - client1.set_credential(credential1).await; + let credential_data = CredentialData::builder(client2.identifier(), authority.identifier()) + .with_attribute("is_user", b"true") + .build()?; + let credential = credentials + .issue_credential(&authority, credential_data) + .await?; - let channel = client1 - .create_secure_channel(route!["listener"], TrustEveryonePolicy) + let channel = secure_channels + .create_secure_channel(ctx, &client2, route!["listener"], TrustEveryonePolicy) .await?; - let storage: Arc = - Arc::new(authenticated_attribute_storage_client_1.clone()); - client1 + credentials_service .present_credential_mutual( + ctx, route![channel, "credential_exchange"], - &authorities, - storage, - None, + authorities.as_slice(), + credential, ) .await?; - let attrs1 = AuthenticatedAttributeStorage::new(storage2.clone()) - .get_attributes(client1.identifier()) + let attrs1 = identities_repository + .get_attributes(&client1.identifier()) .await? .unwrap(); - assert_eq!(attrs1.attrs().get("is_user").unwrap().as_slice(), b"true"); + assert_eq!(attrs1.attrs().get("is_admin").unwrap().as_slice(), b"true"); - let attrs2 = authenticated_attribute_storage_client_1 - .get_attributes(client2.identifier()) + let attrs2 = identities_repository + .get_attributes(&client2.identifier()) .await? .unwrap(); - assert_eq!(attrs2.attrs().get("is_admin").unwrap().as_slice(), b"true"); + assert_eq!(attrs2.attrs().get("is_user").unwrap().as_slice(), b"true"); ctx.stop().await } -struct CountingWorker { - msgs_count: Arc, -} - -#[async_trait] -impl Worker for CountingWorker { - type Context = Context; - type Message = Any; - - async fn handle_message( - &mut self, - _context: &mut Self::Context, - _msg: Routed, - ) -> Result<()> { - let _ = self.msgs_count.fetch_add(1, Ordering::Relaxed); - - Ok(()) - } -} - #[ockam_macros::test] async fn access_control(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); - let storage = Arc::new(InMemoryStorage::new()); - let authority = Identity::create(ctx, vault.clone()).await?; - let server = Identity::create(ctx, vault.clone()).await?; - - server - .create_secure_channel_listener("listener", TrustEveryonePolicy) + let secure_channels = secure_channels(); + let identities = secure_channels.identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let credentials = identities.credentials(); + let credentials_service = identities.credentials_server(); + + let authority = identities_creation.create_identity().await?; + let server = identities_creation.create_identity().await?; + let client = identities_creation.create_identity().await?; + + secure_channels + .create_secure_channel_listener(ctx, &server, "listener", TrustEveryonePolicy) .await?; - let authorities = vec![authority.to_public().await?]; - - server - .start_credential_exchange_worker( - authorities, - "credential_exchange", - false, - Arc::new(AuthenticatedAttributeStorage::new(storage.clone())), - ) + let authorities = vec![authority.clone()]; + credentials_service + .start(ctx, None, authorities, "credential_exchange".into(), false) .await?; - let client = Identity::create(ctx, vault.clone()).await?; - let channel = client + let channel = secure_channels .create_secure_channel( + ctx, + &client, route!["listener"], TrustIdentifierPolicy::new(server.identifier().clone()), ) .await?; - let credential_builder = Credential::builder(client.identifier().clone()); - let credential = credential_builder.with_attribute("is_superuser", b"true"); - - let credential = authority.issue_credential(credential).await?; - - client.set_credential(credential).await; + let credential_data = CredentialData::builder(client.identifier(), authority.identifier()) + .with_attribute("is_superuser", b"true") + .build()?; + let credential = credentials + .issue_credential(&authority, credential_data) + .await?; let counter = Arc::new(AtomicI8::new(0)); @@ -205,10 +182,8 @@ async fn access_control(ctx: &mut Context) -> Result<()> { }; let required_attributes = vec![("is_superuser".to_string(), b"true".to_vec())]; - let access_control = CredentialAccessControl::new( - &required_attributes, - AuthenticatedAttributeStorage::new(storage.clone()), - ); + let access_control = + CredentialAccessControl::new(&required_attributes, identities_repository.clone()); WorkerBuilder::with_access_control( Arc::new(access_control), @@ -235,8 +210,12 @@ async fn access_control(ctx: &mut Context) -> Result<()> { ctx.sleep(Duration::from_millis(100)).await; assert_eq!(counter.load(Ordering::Relaxed), 0); - client - .present_credential(route![channel.clone(), "credential_exchange"], None) + credentials_service + .present_credential( + ctx, + route![channel.clone(), "credential_exchange"], + credential, + ) .await?; child_ctx @@ -247,3 +226,23 @@ async fn access_control(ctx: &mut Context) -> Result<()> { ctx.stop().await } + +struct CountingWorker { + msgs_count: Arc, +} + +#[async_trait] +impl Worker for CountingWorker { + type Context = Context; + type Message = Any; + + async fn handle_message( + &mut self, + _context: &mut Self::Context, + _msg: Routed, + ) -> Result<()> { + let _ = self.msgs_count.fetch_add(1, Ordering::Relaxed); + + Ok(()) + } +} diff --git a/implementations/rust/ockam/ockam_identity/tests/plaintext_message_flow_auth.rs b/implementations/rust/ockam/ockam_identity/tests/plaintext_message_flow_auth.rs index 26d35650816..506165d1f55 100644 --- a/implementations/rust/ockam/ockam_identity/tests/plaintext_message_flow_auth.rs +++ b/implementations/rust/ockam/ockam_identity/tests/plaintext_message_flow_auth.rs @@ -3,10 +3,11 @@ use crate::common::{ }; use ockam_core::sessions::{SessionPolicy, Sessions}; use ockam_core::{route, Address, AllowAll, Result}; -use ockam_identity::{Identity, SecureChannelListenerTrustOptions, SecureChannelTrustOptions}; +use ockam_identity::{ + secure_channels, SecureChannelListenerTrustOptions, SecureChannelTrustOptions, +}; use ockam_node::Context; use ockam_transport_tcp::{TcpConnectionTrustOptions, TcpListenerTrustOptions, TcpTransport}; -use ockam_vault::Vault; use std::time::Duration; mod common; @@ -21,17 +22,26 @@ async fn test1(ctx: &mut Context) -> Result<()> { let sessions_bob = Sessions::default(); let session_id_bob_channel = sessions_bob.generate_session_id(); - let alice = Identity::create(ctx, Vault::create()).await?; - let bob = Identity::create(ctx, Vault::create()).await?; + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); + + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - bob.create_secure_channel_listener( - "listener", - SecureChannelListenerTrustOptions::new().as_spawner(&sessions_bob, &session_id_bob_channel), - ) - .await?; + secure_channels + .create_secure_channel_listener( + ctx, + &bob, + "listener", + SecureChannelListenerTrustOptions::new() + .as_spawner(&sessions_bob, &session_id_bob_channel), + ) + .await?; - let channel_to_bob = alice + let channel_to_bob = secure_channels .create_secure_channel( + ctx, + &alice, route!["listener"], SecureChannelTrustOptions::new() .as_producer(&sessions_alice, &session_id_alice_channel), @@ -39,7 +49,7 @@ async fn test1(ctx: &mut Context) -> Result<()> { .await?; ctx.sleep(Duration::from_millis(50)).await; // Wait for workers to add themselves to the registry - let channel_to_alice = bob + let channel_to_alice = secure_channels .secure_channel_registry() .get_channel_list() .first() @@ -106,23 +116,31 @@ async fn test2(ctx: &mut Context) -> Result<()> { message_should_not_pass(ctx, &connection_to_bob).await?; message_should_not_pass(ctx, &connection_to_alice).await?; - let alice = Identity::create(ctx, Vault::create()).await?; - let bob = Identity::create(ctx, Vault::create()).await?; - - bob.create_secure_channel_listener( - "listener", - SecureChannelListenerTrustOptions::new() - .as_consumer( - &sessions_bob, - &session_id_bob_tcp, - SessionPolicy::SpawnerAllowOnlyOneMessage, - ) - .as_spawner(&sessions_bob, &session_id_bob_plaintext), - ) - .await?; - - let channel_to_bob = alice + let secure_channels = secure_channels(); + let identities_creation = secure_channels.identities().identities_creation(); + + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; + + secure_channels + .create_secure_channel_listener( + ctx, + &bob, + "listener", + SecureChannelListenerTrustOptions::new() + .as_consumer( + &sessions_bob, + &session_id_bob_tcp, + SessionPolicy::SpawnerAllowOnlyOneMessage, + ) + .as_spawner(&sessions_bob, &session_id_bob_plaintext), + ) + .await?; + + let channel_to_bob = secure_channels .create_secure_channel( + ctx, + &alice, route![connection_to_bob, "listener"], SecureChannelTrustOptions::new() .as_consumer(&sessions_alice, &session_id_alice_tcp) @@ -132,7 +150,7 @@ async fn test2(ctx: &mut Context) -> Result<()> { ctx.sleep(Duration::from_millis(50)).await; // Wait for workers to add themselves to the registry - let channels = bob.secure_channel_registry().get_channel_list(); + let channels = secure_channels.secure_channel_registry().get_channel_list(); assert_eq!(channels.len(), 1); let channel_to_alice = channels .first() diff --git a/implementations/rust/ockam/ockam_identity/tests/tests.rs b/implementations/rust/ockam/ockam_identity/tests/tests.rs index 269bfee6e98..20567e67613 100644 --- a/implementations/rust/ockam/ockam_identity/tests/tests.rs +++ b/implementations/rust/ockam/ockam_identity/tests/tests.rs @@ -1,30 +1,23 @@ use ockam_core::errcode::{Kind, Origin}; -use ockam_core::vault::{SecretAttributes, SecretPersistence, SecretType, SecretVault}; +use ockam_core::vault::{SecretAttributes, SecretPersistence, SecretType}; use ockam_core::{Error, Result}; -use ockam_identity::Identity; +use ockam_identity::identities; use ockam_node::Context; -use ockam_vault::Vault; use rand::{thread_rng, RngCore}; -fn test_error>(error: S) -> Result<()> { - Err(Error::new_without_cause(Origin::Identity, Kind::Unknown).context("msg", error.into())) -} - #[ockam_macros::test] async fn test_auth_use_case(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let identities = identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let identities_keys = identities.identities_keys(); // Alice and Bob are distinct Entities. - let alice = Identity::create(ctx, alice_vault.clone()).await?; - let bob = Identity::create(ctx, bob_vault.clone()).await?; + let alice = identities_creation.create_identity().await?; + let bob = identities_creation.create_identity().await?; - alice - .update_known_identity(bob.identifier(), &bob.to_public().await?) - .await?; - - bob.update_known_identity(alice.identifier(), &alice.to_public().await?) - .await?; + identities_repository.update_known_identity(&bob).await?; + identities_repository.update_known_identity(&alice).await?; // Some state known to both parties. In Noise this would be a computed hash, for example. let state = { @@ -34,20 +27,28 @@ async fn test_auth_use_case(ctx: &mut Context) -> Result<()> { state }; - let alice_proof = alice.create_signature(&state, None).await?; - let bob_proof = bob.create_signature(&state, None).await?; + let alice_proof = identities_keys + .create_signature(&alice, &state, None) + .await?; + let bob_proof = identities_keys.create_signature(&bob, &state, None).await?; - let known_bob = alice.get_known_identity(bob.identifier()).await?.unwrap(); - if !known_bob - .verify_signature(&bob_proof, &state, None, alice_vault) + let known_bob = identities_repository + .get_identity(&bob.identifier()) + .await? + .unwrap(); + if !identities_keys + .verify_signature(&known_bob, &bob_proof, &state, None) .await? { return test_error("bob's proof was invalid"); } - let known_alice = bob.get_known_identity(alice.identifier()).await?.unwrap(); - if !known_alice - .verify_signature(&alice_proof, &state, None, bob_vault) + let known_alice = identities_repository + .get_identity(&alice.identifier()) + .await? + .unwrap(); + if !identities_keys + .verify_signature(&known_alice, &alice_proof, &state, None) .await? { return test_error("alice's proof was invalid"); @@ -60,23 +61,21 @@ async fn test_auth_use_case(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_key_rotation(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let identities = identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let identities_keys = identities.identities_keys(); // Alice and Bob are distinct Entities. - let alice = Identity::create(ctx, alice_vault).await?; - let bob = Identity::create(ctx, bob_vault).await?; + let mut alice = identities_creation.create_identity().await?; + let mut bob = identities_creation.create_identity().await?; // Both identities rotate keys. - alice.rotate_root_key().await?; - bob.rotate_root_key().await?; + identities_keys.rotate_root_key(&mut alice).await?; + identities_keys.rotate_root_key(&mut bob).await?; - alice - .update_known_identity(bob.identifier(), &bob.to_public().await?) - .await?; - - bob.update_known_identity(alice.identifier(), &alice.to_public().await?) - .await?; + identities_repository.update_known_identity(&bob).await?; + identities_repository.update_known_identity(&alice).await?; ctx.stop().await?; @@ -85,29 +84,23 @@ async fn test_key_rotation(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn test_update_contact_and_reprove(ctx: &mut Context) -> Result<()> { - let alice_vault = Vault::create(); - let bob_vault = Vault::create(); + let identities = identities(); + let identities_creation = identities.identities_creation(); + let identities_repository = identities.repository(); + let identities_keys = identities.identities_keys(); // Alice and Bob are distinct Entities. - let alice = Identity::create(ctx, alice_vault.clone()).await?; - let bob = Identity::create(ctx, bob_vault.clone()).await?; + let mut alice = identities_creation.create_identity().await?; + let mut bob = identities_creation.create_identity().await?; - alice - .update_known_identity(bob.identifier(), &bob.to_public().await?) - .await?; + identities_repository.update_known_identity(&bob).await?; + identities_repository.update_known_identity(&alice).await?; - bob.update_known_identity(alice.identifier(), &alice.to_public().await?) - .await?; - - alice.rotate_root_key().await?; - bob.rotate_root_key().await?; + identities_keys.rotate_root_key(&mut alice).await?; + identities_keys.rotate_root_key(&mut bob).await?; - alice - .update_known_identity(bob.identifier(), &bob.to_public().await?) - .await?; - - bob.update_known_identity(alice.identifier(), &alice.to_public().await?) - .await?; + identities_repository.update_known_identity(&bob).await?; + identities_repository.update_known_identity(&alice).await?; // Re-Prove let state = { @@ -117,20 +110,28 @@ async fn test_update_contact_and_reprove(ctx: &mut Context) -> Result<()> { state }; - let alice_proof = alice.create_signature(&state, None).await?; - let bob_proof = bob.create_signature(&state, None).await?; + let alice_proof = identities_keys + .create_signature(&alice, &state, None) + .await?; + let bob_proof = identities_keys.create_signature(&bob, &state, None).await?; - let known_bob = alice.get_known_identity(bob.identifier()).await?.unwrap(); - if !known_bob - .verify_signature(&bob_proof, &state, None, alice_vault) + let known_bob = identities_repository + .get_identity(&bob.identifier()) + .await? + .unwrap(); + if !identities_keys + .verify_signature(&known_bob, &bob_proof, &state, None) .await? { return test_error("bob's proof was invalid"); } - let known_alice = bob.get_known_identity(alice.identifier()).await?.unwrap(); - if !known_alice - .verify_signature(&alice_proof, &state, None, bob_vault) + let known_alice = identities_repository + .get_identity(&alice.identifier()) + .await? + .unwrap(); + if !identities_keys + .verify_signature(&known_alice, &alice_proof, &state, None) .await? { return test_error("alice's proof was invalid"); @@ -143,10 +144,13 @@ async fn test_update_contact_and_reprove(ctx: &mut Context) -> Result<()> { #[ockam_macros::test] async fn add_key(ctx: &mut Context) -> Result<()> { - let vault = Vault::create(); - let e = Identity::create(ctx, vault.clone()).await?; + let identities = identities(); + let identities_creation = identities.identities_creation(); + let identities_vault = identities.vault(); + let identities_keys = identities.identities_keys(); + let mut identity = identities_creation.create_identity().await?; - let key = vault + let key = identities_vault .secret_generate(SecretAttributes::new( SecretType::Ed25519, SecretPersistence::Ephemeral, @@ -154,7 +158,13 @@ async fn add_key(ctx: &mut Context) -> Result<()> { )) .await?; - e.add_key("test".into(), &key).await?; + identities_keys + .add_key(&mut identity, "test".into(), &key) + .await?; ctx.stop().await } + +fn test_error>(error: S) -> Result<()> { + Err(Error::new_without_cause(Origin::Identity, Kind::Unknown).context("msg", error.into())) +} diff --git a/implementations/rust/ockam/ockam_transport_ble/examples/05-secure-channel-over-ble-transport-initiator.rs b/implementations/rust/ockam/ockam_transport_ble/examples/05-secure-channel-over-ble-transport-initiator.rs index 442d03cb01d..7b38d39a0d4 100644 --- a/implementations/rust/ockam/ockam_transport_ble/examples/05-secure-channel-over-ble-transport-initiator.rs +++ b/implementations/rust/ockam/ockam_transport_ble/examples/05-secure-channel-over-ble-transport-initiator.rs @@ -1,9 +1,8 @@ // This node routes a message, to a worker on a different node, over the ble transport. use ockam_core::{route, Result}; -use ockam_identity::{Identity, TrustEveryonePolicy}; +use ockam_identity::{secure_channels, TrustEveryonePolicy}; use ockam_node::Context; -use ockam_vault::Vault; use ockam_transport_ble::driver::btleplug::BleAdapter; use ockam_transport_ble::driver::BleClient; @@ -26,18 +25,22 @@ async fn async_main(mut ctx: Context) -> Result<()> { // Initialize the BLE Transport. let ble = BleTransport::create(&ctx).await?; - // Create a Vault to safely store secret keys for Alice. - let vault = Vault::create(); - // Create an Entity to represent Alice. - let alice = Identity::create(&ctx, vault).await?; + let secure_channels = secure_channels(); + let alice = secure_channels + .identities() + .identities_creation() + .create_identity() + .await?; // Connect to BLE Server ble.connect(ble_client, "ockam_ble_1".to_string()).await?; // Connect to a secure channel listener and perform a handshake. let r = route![(BLE, "ockam_ble_1"), "bob_listener"]; - let channel = alice.create_secure_channel(r, TrustEveryonePolicy).await?; + let channel = secure_channels + .create_secure_channel(&ctx, &alice, r, TrustEveryonePolicy) + .await?; // Send a message to the "echoer" worker, on a different node, via secure channel. let r = route![channel, "echoer"];