Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identity v2 #60

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 89 additions & 48 deletions reference/libraries/rust/credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,37 @@ In a later guide, we'll explore how Ockam enables you to define various pluggabl
// examples/06-credentials-exchange-issuer.rs
use ockam::access_control::AllowAll;
use ockam::access_control::IdentityIdAccessControl;
use ockam::identity::CredentialsIssuer;
use ockam::identity::SecureChannelListenerOptions;
use ockam::TcpTransportExtension;
use ockam::{node, Context, Result, TcpListenerOptions};
use ockam::identity::{CredentialsIssuer, SecureChannelListenerOptions, Vault};
use ockam::vault::{Secret, SecretAttributes, SoftwareSigningVault};
use ockam::{Context, Result, TcpListenerOptions};
use ockam::{Node, TcpTransportExtension};

#[ockam::node]
async fn main(ctx: Context) -> Result<()> {
// Create a node with default implementations
let node = node(ctx);
let identity_vault = SoftwareSigningVault::create();
// Import the signing secret key to the Vault
let secret = identity_vault
.import_key(
Secret::new(hex::decode("0127359911708ef4de9adaaf27c357501473c4a10a5326a69c1f7f874a0cd82e").unwrap()),
SecretAttributes::Ed25519,
)
.await?;

// Create a default Vault but use the signing vault with our secret in it
let mut vault = Vault::create();
vault.identity_vault = identity_vault;

let node = Node::builder().with_vault(vault).build(ctx).await?;

let issuer_identity = "0180370b91c5d0aa4af34580a9ab4b8fb2a28351bed061525c96b4f07e75c0ee18000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020236f79490d3f683e0c3bf458a7381c366c99a8f2b2ac406db1ef8c130111f12703010140b23fddceb11cea25602aa681b6ef6abda036722c27a6dee291f1d6b2234a127af21cc79de2252201f27e7e34e0bf5064adbf3d01eb355aff4bf5c90b8f1fd80a";
let secret = "9278735d525efceef16bfd9143d3534759f3d388e460e6002134b9541e06489f";
let issuer = node.import_private_identity(issuer_identity, secret).await?;
let issuer_identity = hex::decode("81a201583ba20101025835a4028201815820afbca9cf5d440147450f9f0d0a038a337b3fe5c17086163f2c54509558b62ef403f4041a64dd404a051a77a9434a0282018158407754214545cda6e7ff49136f67c9c7973ec309ca4087360a9f844aac961f8afe3f579a72c0c9530f3ff210f02b7c5f56e96ce12ee256b01d7628519800723805").unwrap();
let issuer = node.import_private_identity(&issuer_identity, &secret).await?;
println!("issuer identifier {}", issuer.identifier());

// Tell the credential issuer about a set of public identifiers that are
// known, in advance, to be members of the production cluster.
let known_identifiers = vec![
"Pe92f183eb4c324804ef4d62962dea94cf095a265d4d28500c34e1a4e0d5ef638".try_into()?,
"Pada09e0f96e56580f6a0cb54f55ecbde6c973db6732e30dfb39b178760aed041".try_into()?,
"I6342c580429b9a0733880bea4fa18f8055871130".try_into()?, // Client Identifier
"I2c3b0ef15c12fe43d405497fcfc46318da46d0f5".try_into()?, // Server Identifier
];

// Tell this credential issuer about the attributes to include in credentials
Expand All @@ -77,12 +88,17 @@ 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.
let credential_issuer =
CredentialsIssuer::new(node.identities(), issuer.identifier(), "trust_context".into()).await?;
let credential_issuer = CredentialsIssuer::new(
node.identities().repository(),
node.credentials(),
issuer.identifier(),
"trust_context".into(),
)
.await?;
for identifier in known_identifiers.iter() {
node.identities()
.repository()
.put_attribute_value(identifier, "cluster", "production")
.put_attribute_value(identifier, b"cluster".to_vec(), b"production".to_vec())
.await?;
}

Expand All @@ -94,7 +110,7 @@ async fn main(ctx: Context) -> Result<()> {
// 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.
node.create_secure_channel_listener(&issuer.identifier(), "secure", sc_listener_options)
node.create_secure_channel_listener(issuer.identifier(), "secure", sc_listener_options)
.await?;

// Start a credential issuer worker that will only accept incoming requests from
Expand Down Expand Up @@ -136,27 +152,40 @@ use hello_ockam::Echoer;
use ockam::abac::AbacAccessControl;
use ockam::access_control::AllowAll;
use ockam::identity::{
AuthorityService, CredentialsIssuerClient, SecureChannelListenerOptions, SecureChannelOptions, TrustContext,
AuthorityService, CredentialsIssuerClient, SecureChannelListenerOptions, SecureChannelOptions, TrustContext, Vault,
};
use ockam::TcpTransportExtension;
use ockam::{node, route, Context, Result, TcpConnectionOptions, TcpListenerOptions};
use ockam::vault::{Secret, SecretAttributes, SoftwareSigningVault};
use ockam::{route, Context, Result, TcpConnectionOptions, TcpListenerOptions};
use ockam::{Node, TcpTransportExtension};

#[ockam::node]
async fn main(ctx: Context) -> Result<()> {
// Create a node with default implementations
let node = node(ctx);
let identity_vault = SoftwareSigningVault::create();
// Import the signing secret key to the Vault
let secret = identity_vault
.import_key(
Secret::new(hex::decode("5FB3663DF8405379981462BABED7507E3D53A8D061188105E3ADBD70E0A74B8A").unwrap()),
SecretAttributes::Ed25519,
)
.await?;

// Create a default Vault but use the signing vault with our secret in it
let mut vault = Vault::create();
vault.identity_vault = identity_vault;

let node = Node::builder().with_vault(vault).build(ctx).await?;

// Initialize the TCP Transport
let tcp = node.create_tcp_transport().await?;

// Create an Identity representing the server
// Load an identity corresponding to the following public identifier
// Pe92f183eb4c324804ef4d62962dea94cf095a265d4d28500c34e1a4e0d5ef638
// I2c3b0ef15c12fe43d405497fcfc46318da46d0f5
//
// We're hard coding this specific identity because its public identifier is known
// to the credential issuer as a member of the production cluster.
let change_history = "01ed8a5b1303f975c1296c990d1bd3c1946cfef328de20531e3511ec5604ce0dd9000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020e8c328bc0cc07a374762091d037e69c36fdd4d2e1a651abd4d43a1362d3f800503010140a349968063d7337d0c965969fa9c640824c01a6d37fe130d4ab963b0271b9d5bbf0923faa5e27f15359554f94f08676df01b99d997944e4feaf0caaa1189480e";
let secret = "5b2b3f2abbd1787704d8f8b363529f8e2d8f423b6dd4b96a2c462e4f0e04ee18";
let server = node.import_private_identity(change_history, secret).await?;
let change_history = hex::decode("81a201583ba20101025835a40282018158201d387ce453816d91159740a55e9a62ad3b58be9ecf7ef08760c42c0d885b6c2e03f4041a64dd4074051a77a9437402820181584053de69d82c9c4b12476c889b437be1d9d33bd0041655c4836a3a57ac5a67703e7f500af5bacaed291cfd6783d255fe0f0606638577d087a5612bfb4671f2b70a").unwrap();
let server = node.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.
Expand All @@ -167,32 +196,31 @@ async fn main(ctx: Context) -> Result<()> {
let issuer_connection = tcp.connect("127.0.0.1:5000", TcpConnectionOptions::new()).await?;
let issuer_channel = node
.create_secure_channel(
&server.identifier(),
server.identifier(),
route![issuer_connection, "secure"],
SecureChannelOptions::new(),
)
.await?;

let issuer_client = CredentialsIssuerClient::new(route![issuer_channel, "issuer"], node.context()).await?;
let credential = issuer_client.credential().await?;
println!("Credential:\n{credential}");

// Verify that the received credential has indeed be signed by the issuer.
// The issuer identity must be provided out-of-band from a trusted source
// and match the identity used to start the issuer node
let issuer_identity = "0180370b91c5d0aa4af34580a9ab4b8fb2a28351bed061525c96b4f07e75c0ee18000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020236f79490d3f683e0c3bf458a7381c366c99a8f2b2ac406db1ef8c130111f12703010140b23fddceb11cea25602aa681b6ef6abda036722c27a6dee291f1d6b2234a127af21cc79de2252201f27e7e34e0bf5064adbf3d01eb355aff4bf5c90b8f1fd80a";
let issuer_identity = "81a201583ba20101025835a4028201815820afbca9cf5d440147450f9f0d0a038a337b3fe5c17086163f2c54509558b62ef403f4041a64dd404a051a77a9434a0282018158407754214545cda6e7ff49136f67c9c7973ec309ca4087360a9f844aac961f8afe3f579a72c0c9530f3ff210f02b7c5f56e96ce12ee256b01d7628519800723805";
let issuer = node.import_identity_hex(issuer_identity).await?;
node.credentials()
.verify_credential(&server.identifier(), &[issuer.clone()], credential.clone())
.credentials_verification()
.verify_credential(Some(server.identifier()), &[issuer.identifier().clone()], &credential)
.await?;

// Create a trust context that will be used to authenticate credential exchanges
let trust_context = TrustContext::new(
"trust_context_id".to_string(),
Some(AuthorityService::new(
node.identities().identities_reader(),
node.credentials(),
issuer.identifier(),
issuer.identifier().clone(),
None,
)),
);
Expand All @@ -209,13 +237,13 @@ async fn main(ctx: Context) -> Result<()> {

node.flow_controls()
.add_consumer("echoer", &sc_listener_options.spawner_flow_control_id());
let allow_production = AbacAccessControl::create(node.repository(), "cluster", "production");
let allow_production = AbacAccessControl::create(node.identities_repository(), "cluster", "production");
node.start_worker_with_access_control("echoer", Echoer, allow_production, AllowAll)
.await?;

// Start a secure channel listener that only allows channels with
// authenticated identities.
node.create_secure_channel_listener(&server.identifier(), "secure", sc_listener_options)
node.create_secure_channel_listener(server.identifier(), "secure", sc_listener_options)
.await?;

// Create a TCP listener and wait for incoming connections
Expand All @@ -242,26 +270,40 @@ touch examples/06-credential-exchange-client.rs
{% code lineNumbers="true" %}
```rust
// examples/06-credentials-exchange-client.rs
use ockam::identity::{AuthorityService, CredentialsIssuerClient, SecureChannelOptions, TrustContext};
use ockam::TcpTransportExtension;
use ockam::{node, route, Context, Result, TcpConnectionOptions};
use ockam::identity::{AuthorityService, CredentialsIssuerClient, SecureChannelOptions, TrustContext, Vault};
use ockam::vault::{Secret, SecretAttributes, SoftwareSigningVault};
use ockam::{route, Context, Result, TcpConnectionOptions};
use ockam::{Node, TcpTransportExtension};

#[ockam::node]
async fn main(ctx: Context) -> Result<()> {
// Create a node with default implementations
let mut node = node(ctx);
let identity_vault = SoftwareSigningVault::create();
// Import the signing secret key to the Vault
let secret = identity_vault
.import_key(
Secret::new(hex::decode("31FF4E1CD55F17735A633FBAB4B838CF88D1252D164735CB3185A6E315438C2C").unwrap()),
SecretAttributes::Ed25519,
)
.await?;

// Create a default Vault but use the signing vault with our secret in it
let mut vault = Vault::create();
vault.identity_vault = identity_vault;

let mut node = Node::builder().with_vault(vault).build(ctx).await?;
// Initialize the TCP Transport
let tcp = node.create_tcp_transport().await?;

// Create an Identity representing the client
// We preload the client vault with a change history and secret key corresponding to the identity identifier
// Pe92f183eb4c324804ef4d62962dea94cf095a265d4d28500c34e1a4e0d5ef638
// I6342c580429b9a0733880bea4fa18f8055871130
// which is an identifier known to the credential issuer, with some preset attributes
//
// We're hard coding this specific identity because its public identifier is known
// to the credential issuer as a member of the production cluster.
let change_history = "01dcf392551f796ef1bcb368177e53f9a5875a962f67279259207d24a01e690721000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020a0d205f09cab9a9467591fcee560429aab1215d8136e5c985a6b7dc729e6f08203010140b098463a727454c0e5292390d8f4cbd4dd0cae5db95606832f3d0a138936487e1da1489c40d8a0995fce71cc1948c6bcfd67186467cdd78eab7e95c080141505";
let secret = "41b6873b20d95567bf958e6bab2808e9157720040882630b1bb37a72f4015cd2";
let client = node.import_private_identity(change_history, secret).await?;
let change_history = hex::decode("81a201583ba20101025835a4028201815820530d1c2e9822433b679a66a60b9c2ed47c370cd0ce51cbe1a7ad847b5835a96303f4041a64dd4060051a77a94360028201815840042fff8f6c80603fb1cec4a3cf1ff169ee36889d3ed76184fe1dfbd4b692b02892df9525c61c2f1286b829586d13d5abf7d18973141f734d71c1840520d40a0e").unwrap();
let client = node.import_private_identity(&change_history, &secret).await?;
println!("issuer identifier {}", client.identifier());

// Connect with the credential issuer and authenticate using the latest private
// key of this program's hardcoded identity.
Expand All @@ -272,32 +314,31 @@ async fn main(ctx: Context) -> Result<()> {
let issuer_connection = tcp.connect("127.0.0.1:5000", TcpConnectionOptions::new()).await?;
let issuer_channel = node
.create_secure_channel(
&client.identifier(),
client.identifier(),
route![issuer_connection, "secure"],
SecureChannelOptions::new(),
)
.await?;

let issuer_client = CredentialsIssuerClient::new(route![issuer_channel, "issuer"], node.context()).await?;
let credential = issuer_client.credential().await?;
println!("Credential:\n{credential}");

// Verify that the received credential has indeed be signed by the issuer.
// The issuer identity must be provided out-of-band from a trusted source
// and match the identity used to start the issuer node
let issuer_identity = "0180370b91c5d0aa4af34580a9ab4b8fb2a28351bed061525c96b4f07e75c0ee18000547c93239ba3d818ec26c9cdadd2a35cbdf1fa3b6d1a731e06164b1079fb7b8084f434b414d5f524b03012000000020236f79490d3f683e0c3bf458a7381c366c99a8f2b2ac406db1ef8c130111f12703010140b23fddceb11cea25602aa681b6ef6abda036722c27a6dee291f1d6b2234a127af21cc79de2252201f27e7e34e0bf5064adbf3d01eb355aff4bf5c90b8f1fd80a";
let issuer_identity = "81a201583ba20101025835a4028201815820afbca9cf5d440147450f9f0d0a038a337b3fe5c17086163f2c54509558b62ef403f4041a64dd404a051a77a9434a0282018158407754214545cda6e7ff49136f67c9c7973ec309ca4087360a9f844aac961f8afe3f579a72c0c9530f3ff210f02b7c5f56e96ce12ee256b01d7628519800723805";
let issuer = node.import_identity_hex(issuer_identity).await?;
node.credentials()
.verify_credential(&client.identifier(), &[issuer.clone()], credential.clone())
.credentials_verification()
.verify_credential(Some(client.identifier()), &[issuer.identifier().clone()], &credential)
.await?;

// Create a trust context that will be used to authenticate credential exchanges
let trust_context = TrustContext::new(
"trust_context_id".to_string(),
Some(AuthorityService::new(
node.identities().identities_reader(),
node.credentials(),
issuer.identifier(),
issuer.identifier().clone(),
None,
)),
);
Expand All @@ -306,7 +347,7 @@ async fn main(ctx: Context) -> Result<()> {
let server_connection = tcp.connect("127.0.0.1:4000", TcpConnectionOptions::new()).await?;
let channel = node
.create_secure_channel(
&client.identifier(),
client.identifier(),
route![server_connection, "secure"],
SecureChannelOptions::new()
.with_trust_context(trust_context)
Expand Down