diff --git a/Cargo.lock b/Cargo.lock index b45297d5..ebeb8312 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,6 +113,7 @@ dependencies = [ "axum 0.7.5", "did_manager", "identity_document", + "identity_iota", "serde_json", "tokio", "tower-http 0.5.2", @@ -215,6 +216,7 @@ dependencies = [ "identity_credential", "identity_did", "identity_document", + "identity_iota", "identity_storage", "is_empty", "jsonwebtoken", @@ -1363,7 +1365,7 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "consumer" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "did_iota", "did_jwk", @@ -1929,8 +1931,9 @@ dependencies = [ [[package]] name = "did_iota" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ + "bls12_381_plus 0.8.15", "identity_iota", "identity_stronghold", "iota-sdk", @@ -1942,7 +1945,7 @@ dependencies = [ [[package]] name = "did_jwk" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "did-jwk", "identity_iota", @@ -1959,9 +1962,8 @@ dependencies = [ [[package]] name = "did_key" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ - "did-key", "did-method-key", "identity_iota", "identity_stronghold", @@ -1978,7 +1980,7 @@ dependencies = [ [[package]] name = "did_manager" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "consumer", "producer", @@ -2006,7 +2008,7 @@ dependencies = [ [[package]] name = "did_web" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "did-web", "identity_iota", @@ -2260,6 +2262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", + "base64ct", "crypto-bigint 0.5.5", "digest 0.10.7", "ff 0.13.0", @@ -2269,6 +2272,7 @@ dependencies = [ "pkcs8 0.10.2", "rand_core 0.6.4", "sec1 0.7.3", + "serde_json", "serdect", "subtle", "zeroize", @@ -3404,6 +3408,25 @@ dependencies = [ "zeroize", ] +[[package]] +name = "identity_stronghold_ext" +version = "0.1.0" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" +dependencies = [ + "async-trait", + "elliptic-curve 0.13.8", + "identity_storage", + "identity_verification", + "iota-sdk", + "iota_stronghold", + "k256", + "log", + "p256 0.13.2", + "serde_json", + "stronghold_ext", + "tokio", +] + [[package]] name = "identity_verification" version = "1.3.1" @@ -5355,14 +5378,16 @@ dependencies = [ [[package]] name = "producer" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "did_iota", "did_jwk", "did_key", "did_web", "identity_iota", + "identity_storage", "identity_stronghold", + "identity_stronghold_ext", "iota-sdk", "iota_stronghold", "log", @@ -6514,14 +6539,17 @@ dependencies = [ [[package]] name = "shared" version = "0.1.0" -source = "git+https://git@github.com/impierce/did-manager.git?rev=b9e5594#b9e5594c05f52c197ce6a84d285ab97d1305179b" +source = "git+https://git@github.com/impierce/did-manager.git?rev=2bda2b8#2bda2b8682f08cedafe72cd136b64bbe4510d812" dependencies = [ "identity_iota", + "identity_storage", "identity_stronghold", + "identity_stronghold_ext", "iota-sdk", "iota_stronghold", "log", "rand 0.8.5", + "serde_json", "thiserror", ] @@ -7232,6 +7260,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "stronghold_ext" +version = "0.1.0" +source = "git+https://github.com/tensor-programming/stronghold_ext.git#f51123bd502a943b3f719a954cabaa2be973b707" +dependencies = [ + "ecdsa 0.16.9", + "iota_stronghold", + "k256", + "p256 0.13.2", + "rand 0.8.5", + "serde", + "sha2 0.10.8", + "stronghold-utils", + "stronghold_engine", + "thiserror", + "zeroize", +] + [[package]] name = "strsim" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 78efde3e..6c185cbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ edition = "2021" rust-version = "1.76.0" [workspace.dependencies] -did_manager = { git = "https://git@github.com/impierce/did-manager.git", rev = "b9e5594" } +did_manager = { git = "https://git@github.com/impierce/did-manager.git", rev = "2bda2b8" } siopv2 = { git = "https://git@github.com/impierce/openid4vc.git", rev = "12fed14" } oid4vci = { git = "https://git@github.com/impierce/openid4vc.git", rev = "12fed14" } oid4vc-core = { git = "https://git@github.com/impierce/openid4vc.git", rev = "12fed14" } @@ -35,6 +35,7 @@ identity_credential = { version = "1.3", default-features = false, features = [ "presentation", "domain-linkage" ] } +identity_iota = { version = "1.3" } jsonwebtoken = "9.3" lazy_static = "1.4" rstest = "0.19" diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 13df35c8..9cda7ee3 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -155,6 +155,7 @@ mod tests { use crate::issuance::credentials::tests::credentials; use crate::API_VERSION; use agent_event_publisher_http::EventPublisherHttp; + use agent_issuance::services::test_utils::test_issuance_services; use agent_issuance::{offer::event::OfferEvent, startup_commands::startup_commands, state::initialize}; use agent_shared::config::{set_config, Events}; use agent_store::{in_memory, EventPublisher}; @@ -297,7 +298,7 @@ mod tests { (None, Default::default(), Default::default()) }; - let issuance_state = in_memory::issuance_state(issuance_event_publishers).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), issuance_event_publishers).await; let verification_state = in_memory::verification_state(test_verification_services(), verification_event_publishers).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/token.rs b/agent_api_rest/src/issuance/credential_issuer/token.rs index b225a2c3..91ad4279 100644 --- a/agent_api_rest/src/issuance/credential_issuer/token.rs +++ b/agent_api_rest/src/issuance/credential_issuer/token.rs @@ -67,7 +67,9 @@ pub mod tests { }; use super::*; - use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_issuance::{ + services::test_utils::test_issuance_services, startup_commands::startup_commands, state::initialize, + }; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -110,7 +112,7 @@ pub mod tests { #[tokio::test] async fn test_token_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs index 62585fe5..c690064d 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs @@ -26,7 +26,9 @@ mod tests { use crate::{app, tests::BASE_URL}; use super::*; - use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_issuance::{ + services::test_utils::test_issuance_services, startup_commands::startup_commands, state::initialize, + }; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -70,7 +72,7 @@ mod tests { #[tokio::test] async fn test_oauth_authorization_server_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index 4878c11f..46948a6e 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -28,7 +28,9 @@ mod tests { use crate::{app, tests::BASE_URL}; use super::*; - use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_issuance::{ + services::test_utils::test_issuance_services, startup_commands::startup_commands, state::initialize, + }; use agent_shared::UrlAppendHelpers; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; @@ -131,7 +133,7 @@ mod tests { #[tokio::test] async fn test_openid_credential_issuer_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credentials.rs b/agent_api_rest/src/issuance/credentials.rs index 8136d010..2689f27c 100644 --- a/agent_api_rest/src/issuance/credentials.rs +++ b/agent_api_rest/src/issuance/credentials.rs @@ -158,6 +158,7 @@ pub mod tests { app, tests::{BASE_URL, CREDENTIAL_CONFIGURATION_ID, OFFER_ID}, }; + use agent_issuance::services::test_utils::test_issuance_services; use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; @@ -252,7 +253,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_credentials_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/offers.rs b/agent_api_rest/src/issuance/offers.rs index 795d0c05..be3ea8d7 100644 --- a/agent_api_rest/src/issuance/offers.rs +++ b/agent_api_rest/src/issuance/offers.rs @@ -91,7 +91,9 @@ pub mod tests { use super::*; use crate::API_VERSION; - use agent_issuance::{startup_commands::startup_commands, state::initialize}; + use agent_issuance::{ + services::test_utils::test_issuance_services, startup_commands::startup_commands, state::initialize, + }; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -154,7 +156,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_offers_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index 42ab78b4..d2d72f1d 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -126,6 +126,7 @@ fn get_base_path() -> Result { mod tests { use std::collections::HashMap; + use agent_issuance::services::test_utils::test_issuance_services; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::routing::post; @@ -182,7 +183,7 @@ mod tests { #[tokio::test] #[should_panic] async fn test_base_path_routes() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; std::env::set_var("UNICORE__BASE_PATH", "unicore"); let router = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/authorization_requests.rs b/agent_api_rest/src/verification/authorization_requests.rs index 6e0b6cb7..ad79ff15 100644 --- a/agent_api_rest/src/verification/authorization_requests.rs +++ b/agent_api_rest/src/verification/authorization_requests.rs @@ -139,6 +139,7 @@ pub(crate) async fn authorization_requests( pub mod tests { use super::*; use crate::app; + use agent_issuance::services::test_utils::test_issuance_services; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -220,7 +221,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_authorization_requests_endpoint(#[case] by_value: bool) { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/relying_party/redirect.rs b/agent_api_rest/src/verification/relying_party/redirect.rs index 968920d9..53e475b3 100644 --- a/agent_api_rest/src/verification/relying_party/redirect.rs +++ b/agent_api_rest/src/verification/relying_party/redirect.rs @@ -62,6 +62,7 @@ pub mod tests { verification::{authorization_requests::tests::authorization_requests, relying_party::request::tests::request}, }; use agent_event_publisher_http::EventPublisherHttp; + use agent_issuance::services::test_utils::test_issuance_services; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::config::{set_config, Events}; use agent_store::{in_memory, EventPublisher}; @@ -161,7 +162,7 @@ pub mod tests { let event_publishers = vec![Box::new(EventPublisherHttp::load().unwrap()) as Box]; - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), event_publishers).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/relying_party/request.rs b/agent_api_rest/src/verification/relying_party/request.rs index fd2ca8ed..4ddcb24b 100644 --- a/agent_api_rest/src/verification/relying_party/request.rs +++ b/agent_api_rest/src/verification/relying_party/request.rs @@ -34,6 +34,7 @@ pub(crate) async fn request( pub mod tests { use super::*; use crate::{app, verification::authorization_requests::tests::authorization_requests}; + use agent_issuance::services::test_utils::test_issuance_services; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -69,7 +70,7 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_request_endpoint() { - let issuance_state = in_memory::issuance_state(Default::default()).await; + let issuance_state = in_memory::issuance_state(test_issuance_services(), Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_application/Cargo.toml b/agent_application/Cargo.toml index 6dc41cbb..9763b505 100644 --- a/agent_application/Cargo.toml +++ b/agent_application/Cargo.toml @@ -16,6 +16,7 @@ agent_verification = { path = "../agent_verification" } axum.workspace = true did_manager.workspace = true identity_document = { version = "1.3" } +identity_iota.workspace = true serde_json.workspace = true tokio.workspace = true tower-http.workspace = true diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 716a4c9c..e2ccbb52 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -3,7 +3,7 @@ log_format: "json" event_store: type: "postgres" # connection_string: "" <== Should be injected through the env variable `UNICORE__EVENT_STORE__CONNECTION_STRING` -url: "https://ssi-agent.example.org" +url: "http://10.48.72.1:3033" # base_path: "unicore" <== Runs all endpoints with a base path such as `https://ssi-agent.example.org/unicore` # cors_enabled: false <== Only applicable for browser-based wallets that require CORS external_server_response_timeout_ms: 500 @@ -22,8 +22,9 @@ did_methods: domain_linkage_enabled: false signing_algorithms_supported: - eddsa: + es256: enabled: true + preferred: true # TODO: required to be stated explicitly? vp_formats: @@ -72,6 +73,7 @@ secret_manager: stronghold_path: "/tmp/local.stronghold" # stronghold_password: "" <== Should be injected through the env variable `UNICORE__SECRET_MANAGER__STRONGHOLD_PASSWORD` # stronghold_password_file: "" - # issuer_key_id: "ed25519-0" + issuer_eddsa_key_id: "ed25519-0" + issuer_es256_key_id: "es256-0" # issuer_did: "did:iota:rms:0x0000000000000000000000000000000000000000000000000000000000000000" # issuer_fragment: "key-0" diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 2f3c57cc..8a7f51fc 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -2,7 +2,7 @@ use agent_api_rest::app; use agent_event_publisher_http::EventPublisherHttp; -use agent_issuance::{startup_commands::startup_commands, state::initialize}; +use agent_issuance::{services::IssuanceServices, startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ config::{config, LogFormat, SupportedDidMethod, ToggleOptions}, @@ -12,7 +12,8 @@ use agent_store::{in_memory, postgres, EventPublisher}; use agent_verification::services::VerificationServices; use axum::{routing::get, Json}; use identity_document::service::{Service, ServiceEndpoint}; -use std::sync::Arc; +use identity_iota::verification::jws::JwsAlgorithm; +use std::{str::FromStr, sync::Arc}; use tokio::{fs, io}; use tower_http::cors::CorsLayer; use tracing::info; @@ -29,9 +30,12 @@ async fn main() -> io::Result<()> { LogFormat::Text => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } - let verification_services = Arc::new(VerificationServices::new(Arc::new(Subject { + let subject = Arc::new(Subject { secret_manager: secret_manager().await, - }))); + }); + + let issuance_services = Arc::new(IssuanceServices::new(subject.clone())); + let verification_services = Arc::new(VerificationServices::new(subject.clone())); // TODO: Currently `issuance_event_publishers` and `verification_event_publishers` are exactly the same, which is // weird. We need some sort of layer between `agent_application` and `agent_store` that will provide a cleaner way @@ -42,11 +46,11 @@ async fn main() -> io::Result<()> { let (issuance_state, verification_state) = match agent_shared::config::config().event_store.type_ { agent_shared::config::EventStoreType::Postgres => ( - postgres::issuance_state(issuance_event_publishers).await, + postgres::issuance_state(issuance_services, issuance_event_publishers).await, postgres::verification_state(verification_services, verification_event_publishers).await, ), agent_shared::config::EventStoreType::InMemory => ( - in_memory::issuance_state(issuance_event_publishers).await, + in_memory::issuance_state(issuance_services, issuance_event_publishers).await, in_memory::verification_state(verification_services, verification_event_publishers).await, ), }; @@ -77,15 +81,19 @@ async fn main() -> io::Result<()> { .enabled; let did_document = if enable_did_web { - let subject = Subject { - secret_manager: secret_manager().await, - }; Some( subject .secret_manager .produce_document( did_manager::DidMethod::Web, Some(did_manager::MethodSpecificParameters::Web { origin: url.origin() }), + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), ) .await .unwrap(), @@ -101,7 +109,7 @@ async fn main() -> io::Result<()> { did_document .clone() .expect("No DID document found to create a DID Configuration Resource for"), - secret_manager().await, + &subject.secret_manager, ) .await .expect("Failed to create DID Configuration Resource"), diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 4cfa302f..433d8862 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -1,5 +1,5 @@ -use agent_secret_manager::services::SecretManagerServices; -use agent_shared::config::config; +// use agent_secret_manager::services::SecretManagerServices; +use agent_shared::config::{config, get_preferred_did_method}; use async_trait::async_trait; use cqrs_es::Aggregate; use derivative::Derivative; @@ -8,7 +8,7 @@ use identity_credential::credential::{ Credential as W3CVerifiableCredential, CredentialBuilder as W3CVerifiableCredentialBuilder, Issuer, }; use jsonwebtoken::{Algorithm, Header}; -use oid4vc_core::{jwt, Subject as _}; +use oid4vc_core::jwt; use oid4vci::credential_format_profiles::w3c_verifiable_credentials::jwt_vc_json::{ CredentialDefinition, JwtVcJson, JwtVcJsonParameters, }; @@ -27,7 +27,7 @@ use types_ob_v3::prelude::{ use crate::credential::command::CredentialCommand; use crate::credential::error::CredentialError::{self}; use crate::credential::event::CredentialEvent; -use crate::credential::services::CredentialServices; +use crate::services::IssuanceServices; use super::entity::Data; @@ -44,17 +44,13 @@ impl Aggregate for Credential { type Command = CredentialCommand; type Event = CredentialEvent; type Error = CredentialError; - type Services = CredentialServices; + type Services = Arc; fn aggregate_type() -> String { "credential".to_string() } - async fn handle( - &self, - command: Self::Command, - _services: &Self::Services, - ) -> Result, Self::Error> { + async fn handle(&self, command: Self::Command, services: &Self::Services) -> Result, Self::Error> { use CredentialCommand::*; use CredentialError::*; use CredentialEvent::*; @@ -179,12 +175,11 @@ impl Aggregate for Credential { if self.signed.is_some() && !overwrite { return Ok(vec![]); } - let (issuer, default_did_method) = { - let mut services = SecretManagerServices::new(None); - services.init().await.unwrap(); - (Arc::new(services.subject.unwrap()), services.default_did_method.clone()) - }; - let issuer_did = issuer + + let default_did_method = get_preferred_did_method(); + + let issuer_did = services + .issuer .identifier(&default_did_method.to_string(), Algorithm::EdDSA) .await .unwrap(); @@ -216,7 +211,7 @@ impl Aggregate for Credential { .as_secs() as i64; json!(jwt::encode( - issuer.clone(), + services.issuer.clone(), Header::new(Algorithm::EdDSA), VerifiableCredentialJwt::builder() .sub(subject_id) @@ -278,6 +273,7 @@ pub mod credential_tests { use crate::credential::aggregate::Credential; use crate::credential::event::CredentialEvent; use crate::offer::aggregate::tests::SUBJECT_KEY_DID; + use crate::services::test_utils::test_issuance_services; type CredentialTestFramework = TestFramework; @@ -298,7 +294,7 @@ pub mod credential_tests { #[case] credential_configuration: CredentialConfigurationsSupportedObject, #[case] unsigned_credential: serde_json::Value, ) { - CredentialTestFramework::with(CredentialServices) + CredentialTestFramework::with(test_issuance_services()) .given_no_previous_events() .when(CredentialCommand::CreateUnsignedCredential { data: Data { @@ -331,7 +327,7 @@ pub mod credential_tests { #[case] credential_configuration: CredentialConfigurationsSupportedObject, #[case] verifiable_credential_jwt: String, ) { - CredentialTestFramework::with(CredentialServices) + CredentialTestFramework::with(test_issuance_services()) .given(vec![CredentialEvent::UnsignedCredentialCreated { data: Data { raw: unsigned_credential, diff --git a/agent_issuance/src/lib.rs b/agent_issuance/src/lib.rs index 682c149f..0cd8087f 100644 --- a/agent_issuance/src/lib.rs +++ b/agent_issuance/src/lib.rs @@ -7,6 +7,7 @@ pub mod credential; pub mod offer; pub mod server_config; +pub mod services; pub mod startup_commands; pub mod state; diff --git a/agent_issuance/src/offer/aggregate.rs b/agent_issuance/src/offer/aggregate.rs index 3e9545c8..85e24572 100644 --- a/agent_issuance/src/offer/aggregate.rs +++ b/agent_issuance/src/offer/aggregate.rs @@ -1,4 +1,4 @@ -use agent_secret_manager::services::SecretManagerServices; +// use agent_secret_manager::services::SecretManagerServices; use agent_shared::generate_random_string; use async_trait::async_trait; use cqrs_es::Aggregate; @@ -15,7 +15,7 @@ use tracing::info; use crate::offer::command::OfferCommand; use crate::offer::error::OfferError::{self, *}; use crate::offer::event::OfferEvent; -use crate::offer::services::OfferServices; +use crate::services::IssuanceServices; #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Offer { @@ -33,17 +33,13 @@ impl Aggregate for Offer { type Command = OfferCommand; type Event = OfferEvent; type Error = OfferError; - type Services = OfferServices; + type Services = Arc; fn aggregate_type() -> String { "offer".to_string() } - async fn handle( - &self, - command: Self::Command, - _services: &Self::Services, - ) -> Result, Self::Error> { + async fn handle(&self, command: Self::Command, services: &Self::Services) -> Result, Self::Error> { use OfferCommand::*; use OfferEvent::*; @@ -126,14 +122,8 @@ impl Aggregate for Offer { authorization_server_metadata, credential_request, } => { - let issuer = { - let mut services = SecretManagerServices::new(None); - services.init().await.unwrap(); - Arc::new(services.subject.unwrap()) - }; - let credential_issuer = CredentialIssuer { - subject: issuer.clone(), + subject: services.issuer.clone(), metadata: credential_issuer_metadata, authorization_server_metadata: *authorization_server_metadata, }; @@ -141,7 +131,7 @@ impl Aggregate for Offer { let proof = credential_issuer .validate_proof( credential_request.proof.ok_or(MissingProofError)?, - Validator::Subject(issuer.clone()), + Validator::Subject(services.issuer.clone()), ) .await .map_err(|e| InvalidProofError(e.to_string()))?; @@ -224,11 +214,9 @@ impl Aggregate for Offer { pub mod tests { use super::*; - use agent_secret_manager::subject::Subject; use cqrs_es::test::TestFramework; use jsonwebtoken::Algorithm; use lazy_static::lazy_static; - use oid4vc_core::Subject as _; use oid4vci::{ credential_format_profiles::{ w3c_verifiable_credentials::jwt_vc_json::CredentialDefinition, CredentialFormats, Parameters, @@ -243,6 +231,7 @@ pub mod tests { use crate::{ credential::aggregate::credential_tests::OPENBADGE_VERIFIABLE_CREDENTIAL_JWT, server_config::aggregate::server_config_tests::{AUTHORIZATION_SERVER_METADATA, CREDENTIAL_ISSUER_METADATA}, + services::test_utils::test_issuance_services, }; type OfferTestFramework = TestFramework; @@ -255,7 +244,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given_no_previous_events() .when(OfferCommand::CreateCredentialOffer { offer_id: Default::default(), @@ -275,7 +264,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given(vec![OfferEvent::CredentialOfferCreated { offer_id: Default::default(), pre_authorized_code: subject.pre_authorized_code.clone(), @@ -299,7 +288,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given(vec![ OfferEvent::CredentialOfferCreated { offer_id: Default::default(), @@ -329,7 +318,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given(vec![ OfferEvent::CredentialOfferCreated { offer_id: Default::default(), @@ -363,7 +352,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given(vec![ OfferEvent::CredentialOfferCreated { offer_id: Default::default(), @@ -403,7 +392,7 @@ pub mod tests { *C_NONCES.lock().unwrap() = vec![generate_random_string()].into(); let subject = test_subject(); - OfferTestFramework::with(OfferServices) + OfferTestFramework::with(test_issuance_services()) .given(vec![ OfferEvent::CredentialOfferCreated { offer_id: Default::default(), @@ -439,7 +428,7 @@ pub mod tests { #[derive(Clone)] struct TestSubject { - subject: Arc, + subject: Arc, credential: String, access_token: String, pre_authorized_code: String, @@ -451,7 +440,7 @@ pub mod tests { pub static ref PRE_AUTHORIZED_CODES: Mutex> = Mutex::new(vec![].into()); pub static ref ACCESS_TOKENS: Mutex> = Mutex::new(vec![].into()); pub static ref C_NONCES: Mutex> = Mutex::new(vec![].into()); - pub static ref SUBJECT_KEY_DID: Arc = Arc::new(subject()); + pub static ref SUBJECT_KEY_DID: Arc = test_issuance_services().issuer.clone(); } fn test_subject() -> TestSubject { @@ -525,12 +514,4 @@ pub mod tests { c_nonce_expires_in: None, } } - - fn subject() -> Subject { - futures::executor::block_on(async { - let mut services = SecretManagerServices::new(None); - services.init().await.unwrap(); - services.subject.unwrap() - }) - } } diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index a9fe2042..f1093093 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -87,6 +87,7 @@ impl Aggregate for ServerConfig { .into_iter() .map(|algorithm| match algorithm { jsonwebtoken::Algorithm::EdDSA => "EdDSA".to_string(), + jsonwebtoken::Algorithm::ES256 => "ES256".to_string(), _ => unimplemented!("Unsupported algorithm: {:?}", algorithm), }) .collect(), diff --git a/agent_issuance/src/services.rs b/agent_issuance/src/services.rs new file mode 100644 index 00000000..49830325 --- /dev/null +++ b/agent_issuance/src/services.rs @@ -0,0 +1,29 @@ +use oid4vc_core::Subject; +use std::sync::Arc; + +/// Issuance services. This struct is used to sign credentials and validate credential requests. +pub struct IssuanceServices { + pub issuer: Arc, +} + +impl IssuanceServices { + pub fn new(issuer: Arc) -> Self { + Self { issuer } + } +} + +#[cfg(feature = "test_utils")] +pub mod test_utils { + use agent_secret_manager::secret_manager; + use agent_secret_manager::subject::Subject; + + use super::*; + + pub fn test_issuance_services() -> Arc { + Arc::new(IssuanceServices::new(Arc::new(futures::executor::block_on(async { + Subject { + secret_manager: secret_manager().await, + } + })))) + } +} diff --git a/agent_secret_manager/Cargo.toml b/agent_secret_manager/Cargo.toml index 4396249c..632f9522 100644 --- a/agent_secret_manager/Cargo.toml +++ b/agent_secret_manager/Cargo.toml @@ -12,7 +12,7 @@ async-trait = "0.1" base64.workspace = true cqrs-es = "0.4.2" did_manager.workspace = true -identity_iota = { version = "1.3" } +identity_iota.workspace = true jsonwebtoken = "9.3" log = "0.4" oid4vc-core.workspace = true diff --git a/agent_secret_manager/src/aggregate.rs b/agent_secret_manager/src/aggregate.rs index 2b8f3868..14d863be 100644 --- a/agent_secret_manager/src/aggregate.rs +++ b/agent_secret_manager/src/aggregate.rs @@ -1,3 +1,5 @@ +use identity_iota::verification::jws::JwsAlgorithm; +use std::str::FromStr; use std::sync::Arc; use tokio::sync::Mutex; @@ -7,98 +9,108 @@ use serde::{Deserialize, Serialize}; use crate::commands::SecretManagerCommand; use crate::events::SecretManagerEvent; -use crate::services::SecretManagerServices; - -/// An aggregate that uses services to interact with `did_manager::SecretManager`. -#[derive(Serialize, Deserialize, Default)] -pub struct AgentSecretManager {} - -#[async_trait] -impl Aggregate for AgentSecretManager { - type Command = SecretManagerCommand; - type Event = SecretManagerEvent; - type Error = std::io::Error; - type Services = Arc>; - - fn aggregate_type() -> String { - "secret_manager".to_string() - } - - async fn handle(&self, command: Self::Command, services: &Self::Services) -> Result, Self::Error> { - match command { - SecretManagerCommand::Initialize => { - let mut guard = services.lock().await; - assert!(guard.subject.is_none()); - guard.init().await.unwrap(); - assert!(guard.subject.is_some()); - - Ok(vec![SecretManagerEvent::Initialized {}]) - } - SecretManagerCommand::EnableDidMethod { method } => { - let guard = services.lock().await; - assert!(guard.subject.is_some()); - let result = guard - .subject - .as_ref() - .unwrap() - .secret_manager - .produce_document(method.clone(), None) - .await; - - if result.is_ok() { - Ok(vec![SecretManagerEvent::DidMethodEnabled { method }]) - } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to enable DID method", - )) - } - } - } - } - - fn apply(&mut self, _event: Self::Event) {} -} - -#[cfg(test)] -mod aggregate_tests { - use super::*; - - use cqrs_es::test::TestFramework; - use did_manager::DidMethod; - - use crate::aggregate::AgentSecretManager; - use crate::commands::SecretManagerCommand; - use crate::events::SecretManagerEvent; - use crate::services::SecretManagerServices; - - type SecretManagerTestFramework = TestFramework; - - #[test] - fn successfully_initializes_secret_manager() { - let expected = SecretManagerEvent::Initialized {}; - let command = SecretManagerCommand::Initialize; - let services = Arc::new(Mutex::new(SecretManagerServices::new(None))); - - SecretManagerTestFramework::with(services) - .given_no_previous_events() - .when(command) - .then_expect_events(vec![expected]) - } - - #[test] - fn successfully_enables_did_method() { - let expected = SecretManagerEvent::DidMethodEnabled { method: DidMethod::Key }; - let command = SecretManagerCommand::EnableDidMethod { method: DidMethod::Key }; - let services = futures::executor::block_on(async { - let mut services = SecretManagerServices::new(None); - services.init().await.unwrap(); - Arc::new(Mutex::new(services)) - }); - - SecretManagerTestFramework::with(services) - .given_no_previous_events() - .when(command) - .then_expect_events(vec![expected]) - } -} +// use crate::services::SecretManagerServices; + +// /// An aggregate that uses services to interact with `did_manager::SecretManager`. +// #[derive(Serialize, Deserialize, Default)] +// pub struct AgentSecretManager {} + +// #[async_trait] +// impl Aggregate for AgentSecretManager { +// type Command = SecretManagerCommand; +// type Event = SecretManagerEvent; +// type Error = std::io::Error; +// type Services = Arc>; + +// fn aggregate_type() -> String { +// "secret_manager".to_string() +// } + +// async fn handle(&self, command: Self::Command, services: &Self::Services) -> Result, Self::Error> { +// match command { +// SecretManagerCommand::Initialize => { +// let mut guard = services.lock().await; +// assert!(guard.subject.is_none()); +// guard.init().await.unwrap(); +// assert!(guard.subject.is_some()); + +// Ok(vec![SecretManagerEvent::Initialized {}]) +// } +// SecretManagerCommand::EnableDidMethod { method } => { +// let guard = services.lock().await; +// assert!(guard.subject.is_some()); +// let result = guard +// .subject +// .as_ref() +// .unwrap() +// .secret_manager +// .produce_document( +// method.clone(), +// None, +// JwsAlgorithm::from_str( +// &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) +// .as_str() +// .unwrap() +// .to_string(), +// ) +// .unwrap(), +// ) +// .await; + +// if result.is_ok() { +// Ok(vec![SecretManagerEvent::DidMethodEnabled { method }]) +// } else { +// Err(std::io::Error::new( +// std::io::ErrorKind::Other, +// "Failed to enable DID method", +// )) +// } +// } +// } +// } + +// fn apply(&mut self, _event: Self::Event) {} +// } + +// #[cfg(test)] +// mod aggregate_tests { +// use super::*; + +// use cqrs_es::test::TestFramework; +// use did_manager::DidMethod; + +// use crate::aggregate::AgentSecretManager; +// use crate::commands::SecretManagerCommand; +// use crate::events::SecretManagerEvent; +// use crate::services::SecretManagerServices; + +// type SecretManagerTestFramework = TestFramework; + +// #[test] +// fn successfully_initializes_secret_manager() { +// let expected = SecretManagerEvent::Initialized {}; +// let command = SecretManagerCommand::Initialize; +// let services = Arc::new(Mutex::new(SecretManagerServices::new(None))); + +// SecretManagerTestFramework::with(services) +// .given_no_previous_events() +// .when(command) +// .then_expect_events(vec![expected]) +// } + +// #[test] +// fn successfully_enables_did_method() { +// let expected = SecretManagerEvent::DidMethodEnabled { method: DidMethod::Key }; +// let command = SecretManagerCommand::EnableDidMethod { method: DidMethod::Key }; +// let services = futures::executor::block_on(async { +// let mut services = SecretManagerServices::new(None); +// services.init().await.unwrap(); +// Arc::new(Mutex::new(services)) +// }); + +// SecretManagerTestFramework::with(services) +// .given_no_previous_events() +// .when(command) +// .then_expect_events(vec![expected]) +// } +// } diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index 3c83a632..28cd888e 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -12,17 +12,36 @@ pub async fn secret_manager() -> SecretManager { let SecretManagerConfig { stronghold_path: snapshot_path, stronghold_password: password, - issuer_key_id: key_id, + issuer_eddsa_key_id, + issuer_es256_key_id, issuer_did, issuer_fragment, } = config().secret_manager.clone(); - match (snapshot_path, password, key_id, issuer_did, issuer_fragment) { - (snapshot_path, password, Some(key_id), issuer_did, issuer_fragment) => { - SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) - .await - .unwrap() + match ( + snapshot_path, + password, + issuer_eddsa_key_id, + issuer_es256_key_id, + issuer_did, + issuer_fragment, + ) { + (snapshot_path, password, issuer_eddsa_key_id, issuer_es256_key_id, issuer_did, issuer_fragment) + if issuer_eddsa_key_id.is_some() || issuer_es256_key_id.is_some() => + { + SecretManager::load( + snapshot_path, + password, + issuer_eddsa_key_id, + issuer_es256_key_id, + None, + issuer_did, + issuer_fragment, + ) + .await + .unwrap() } - (snapshot_path, password, None, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), + (snapshot_path, password, None, None, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), + _ => panic!(), } } diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index 79512b4a..bba224ff 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -3,37 +3,46 @@ use agent_shared::config::{config, get_preferred_did_method, SecretManagerConfig use anyhow::Result; use did_manager::SecretManager; -pub struct SecretManagerServices { - pub subject: Option, - pub default_did_method: SupportedDidMethod, -} +// pub struct SecretManagerServices { +// pub subject: Option, +// pub default_did_method: SupportedDidMethod, +// } -impl SecretManagerServices { - pub fn new(subject: Option) -> Self { - let default_did_method = get_preferred_did_method(); - Self { - subject, - default_did_method, - } - } +// impl SecretManagerServices { +// pub fn new(subject: Option) -> Self { +// let default_did_method = get_preferred_did_method(); +// Self { +// subject, +// default_did_method, +// } +// } - pub async fn init(&mut self) -> Result<(), std::io::Error> { - let SecretManagerConfig { - stronghold_path: snapshot_path, - stronghold_password: password, - issuer_key_id, - issuer_did, - issuer_fragment, - } = config().secret_manager.clone(); +// pub async fn init(&mut self) -> Result<(), std::io::Error> { +// let SecretManagerConfig { +// stronghold_path: snapshot_path, +// stronghold_password: password, +// issuer_eddsa_key_id, +// issuer_es256_key_id, +// issuer_did, +// issuer_fragment, +// } = config().secret_manager.clone(); - let key_id = issuer_key_id.expect("Missing configuration: secret_manager.issuer_key_id"); +// // let key_id = issuer_key_id.expect("Missing configuration: secret_manager.issuer_key_id"); - let secret_manager = SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) - .await - .unwrap(); +// let secret_manager = SecretManager::load( +// snapshot_path, +// password, +// issuer_eddsa_key_id, +// issuer_es256_key_id, +// None, +// issuer_did, +// issuer_fragment, +// ) +// .await +// .unwrap(); - self.subject.replace(Subject { secret_manager }); +// self.subject.replace(Subject { secret_manager }); - Ok(()) - } -} +// Ok(()) +// } +// } diff --git a/agent_secret_manager/src/subject.rs b/agent_secret_manager/src/subject.rs index 6de8d7f8..5c4d19e7 100644 --- a/agent_secret_manager/src/subject.rs +++ b/agent_secret_manager/src/subject.rs @@ -2,10 +2,14 @@ use agent_shared::config::config; use async_trait::async_trait; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use did_manager::{DidMethod, Resolver, SecretManager}; -use identity_iota::{did::DID, document::DIDUrlQuery, verification::jwk::JwkParams}; +use identity_iota::{ + did::DID, + document::DIDUrlQuery, + verification::{jwk::JwkParams, jws::JwsAlgorithm}, +}; use jsonwebtoken::Algorithm; use oid4vc_core::{authentication::sign::ExternalSign, Sign, Verify}; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; /// Reponsible for signing and verifying data. pub struct Subject { @@ -58,6 +62,13 @@ impl Sign for Subject { .produce_document( method, Some(did_manager::MethodSpecificParameters::Web { origin: origin() }), + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), ) .await .ok() @@ -68,7 +79,17 @@ impl Sign for Subject { // TODO: refactor: https://github.com/impierce/ssi-agent/pull/31#discussion_r1634590990 self.secret_manager - .produce_document(method, None) + .produce_document( + method, + None, + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), + ) .await .ok() .and_then(|document| document.verification_method().first().cloned()) @@ -76,7 +97,19 @@ impl Sign for Subject { } async fn sign(&self, message: &str, _subject_syntax_type: &str, _algorithm: Algorithm) -> anyhow::Result> { - Ok(self.secret_manager.sign(message.as_bytes()).await?) + Ok(self + .secret_manager + .sign( + message.as_bytes(), + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), + ) + .await?) } fn external_signer(&self) -> Option> { @@ -95,6 +128,13 @@ impl oid4vc_core::Subject for Subject { .produce_document( method, Some(did_manager::MethodSpecificParameters::Web { origin: origin() }), + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), ) .await .map(|document| document.id().to_string())?); @@ -102,7 +142,17 @@ impl oid4vc_core::Subject for Subject { Ok(self .secret_manager - .produce_document(method, None) + .produce_document( + method, + None, + JwsAlgorithm::from_str( + &serde_json::json!(agent_shared::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap() + .to_string(), + ) + .unwrap(), + ) .await .map(|document| document.id().to_string())?) } diff --git a/agent_secret_manager/tests/res/temp.stronghold b/agent_secret_manager/tests/res/temp.stronghold new file mode 100644 index 00000000..0ea2706d Binary files /dev/null and b/agent_secret_manager/tests/res/temp.stronghold differ diff --git a/agent_shared/Cargo.toml b/agent_shared/Cargo.toml index 1de0eb8b..a3084be3 100644 --- a/agent_shared/Cargo.toml +++ b/agent_shared/Cargo.toml @@ -19,7 +19,7 @@ identity_storage = { version = "1.3" } is_empty = "0.2" jsonwebtoken.workspace = true # TODO: replace all identity_* with identity_iota? -# identity_iota = { version = "1.3" } +identity_iota.workspace = true oid4vc-core.workspace = true oid4vci.workspace = true oid4vp.workspace = true diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 12ff89a2..c0c93b2c 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -29,49 +29,6 @@ pub struct ApplicationConfiguration { pub vp_formats: HashMap, } -impl ApplicationConfiguration { - pub fn set_preferred_did_method(&mut self, preferred_did_method: SupportedDidMethod) { - // Set the current preferred did_method to false if available. - if let Some((_, options)) = self.did_methods.iter_mut().find(|(_, v)| v.preferred == Some(true)) { - options.preferred = Some(false); - } - - // Set the current preferred did_method to true if available. - self.did_methods - .entry(preferred_did_method) - .or_insert_with(|| ToggleOptions { - enabled: true, - preferred: Some(true), - }) - .preferred = Some(true); - } - - // TODO: make generic: set_enabled(enabled: bool) - pub fn enable_event_publisher_http(&mut self) { - if let Some(event_publishers) = &mut self.event_publishers { - if let Some(http) = &mut event_publishers.http { - http.enabled = true; - } - } - } - - pub fn set_event_publisher_http_target_url(&mut self, target_url: String) { - if let Some(event_publishers) = &mut self.event_publishers { - if let Some(http) = &mut event_publishers.http { - http.target_url = target_url; - } - } - } - - pub fn set_event_publisher_http_target_events(&mut self, events: Events) { - if let Some(event_publishers) = &mut self.event_publishers { - if let Some(http) = &mut event_publishers.http { - http.events = events; - } - } - } -} - #[derive(Debug, Deserialize, Clone, Default)] #[serde(rename_all = "lowercase")] pub enum LogFormat { @@ -104,7 +61,8 @@ pub struct EventStorePostgresConfig { pub struct SecretManagerConfig { pub stronghold_path: String, pub stronghold_password: String, - pub issuer_key_id: Option, + pub issuer_eddsa_key_id: Option, + pub issuer_es256_key_id: Option, pub issuer_did: Option, pub issuer_fragment: Option, } @@ -263,6 +221,47 @@ impl ApplicationConfiguration { config.try_deserialize() } + + pub fn set_preferred_did_method(&mut self, preferred_did_method: SupportedDidMethod) { + // Set the current preferred did_method to false if available. + if let Some((_, options)) = self.did_methods.iter_mut().find(|(_, v)| v.preferred == Some(true)) { + options.preferred = Some(false); + } + + // Set the current preferred did_method to true if available. + self.did_methods + .entry(preferred_did_method) + .or_insert_with(|| ToggleOptions { + enabled: true, + preferred: Some(true), + }) + .preferred = Some(true); + } + + // TODO: make generic: set_enabled(enabled: bool) + pub fn enable_event_publisher_http(&mut self) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.enabled = true; + } + } + } + + pub fn set_event_publisher_http_target_url(&mut self, target_url: String) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.target_url = target_url; + } + } + } + + pub fn set_event_publisher_http_target_events(&mut self, events: Events) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.events = events; + } + } + } } /// Returns the application configuration or loads it, if it hasn't been loaded already. @@ -303,3 +302,16 @@ pub fn get_preferred_did_method() -> SupportedDidMethod { .cloned() .expect("Please set a DID method as `preferred` in the configuration") } + +pub fn get_preferred_signing_algorithm() -> jsonwebtoken::Algorithm { + config() + .signing_algorithms_supported + .iter() + .filter(|(_, v)| v.enabled) + .filter(|(_, v)| v.preferred.unwrap_or(false)) + .map(|(k, _)| *k) + .collect::>() + .first() + .cloned() + .expect("Please set a signing algorithm as `preferred` in the configuration") +} diff --git a/agent_shared/src/domain_linkage/mod.rs b/agent_shared/src/domain_linkage/mod.rs index 6d7140af..d2721962 100644 --- a/agent_shared/src/domain_linkage/mod.rs +++ b/agent_shared/src/domain_linkage/mod.rs @@ -1,6 +1,8 @@ pub mod verifiable_credential_jwt; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use identity_iota::verification::jws::JwsAlgorithm; +use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use crate::error::SharedError; @@ -18,7 +20,7 @@ use verifiable_credential_jwt::VerifiableCredentialJwt; pub async fn create_did_configuration_resource( url: url::Url, did_document: CoreDocument, - secret_manager: SecretManager, + secret_manager: &SecretManager, ) -> Result { let url = if cfg!(feature = "local_development") { url::Url::parse("http://local.example.org:8080").unwrap() @@ -97,7 +99,18 @@ pub async fn create_did_configuration_resource( ] .join("."); - let proof_value = secret_manager.sign(message.as_bytes()).await.unwrap(); + let proof_value = secret_manager + .sign( + message.as_bytes(), + JwsAlgorithm::from_str( + serde_json::json!(crate::config::get_preferred_signing_algorithm()) + .as_str() + .unwrap(), + ) + .unwrap(), + ) + .await + .unwrap(); let signature = URL_SAFE_NO_PAD.encode(proof_value.as_slice()); let message = [message, signature].join("."); diff --git a/agent_shared/tests/test-config.yaml b/agent_shared/tests/test-config.yaml index 0b8a9810..26d0a32f 100644 --- a/agent_shared/tests/test-config.yaml +++ b/agent_shared/tests/test-config.yaml @@ -20,6 +20,7 @@ domain_linkage_enabled: false signing_algorithms_supported: eddsa: enabled: true + preferred: true vp_formats: jwt_vc_json: @@ -58,6 +59,6 @@ credential_configurations: secret_manager: stronghold_path: "../agent_secret_manager/tests/res/selv.stronghold" stronghold_password: "VNvRtH4tKyWwvJDpL6Vuc2aoLiKAecGQ" - issuer_key_id: "UVDxWhG2rB39FkaR7I27mHeUNrGtUgcr" + issuer_eddsa_key_id: "UVDxWhG2rB39FkaR7I27mHeUNrGtUgcr" issuer_did: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" issuer_fragment: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" diff --git a/agent_store/src/in_memory.rs b/agent_store/src/in_memory.rs index ed596478..f57862fc 100644 --- a/agent_store/src/in_memory.rs +++ b/agent_store/src/in_memory.rs @@ -1,14 +1,13 @@ use agent_issuance::{ - credential::services::CredentialServices, offer::{ aggregate::Offer, queries::{ access_token::{AccessTokenQuery, AccessTokenView}, pre_authorized_code::{PreAuthorizedCodeQuery, PreAuthorizedCodeView}, }, - services::OfferServices, }, server_config::services::ServerConfigServices, + services::IssuanceServices, state::{CommandHandlers, IssuanceState, ViewRepositories}, SimpleLoggingQuery, }; @@ -117,7 +116,10 @@ where } } -pub async fn issuance_state(event_publishers: Vec>) -> IssuanceState { +pub async fn issuance_state( + issuance_services: Arc, + event_publishers: Vec>, +) -> IssuanceState { // Initialize the in-memory repositories. let server_config = Arc::new(MemRepository::default()); let credential = Arc::new(MemRepository::default()); @@ -145,7 +147,7 @@ pub async fn issuance_state(event_publishers: Vec>) -> I ), credential: Arc::new( credential_event_publishers.into_iter().fold( - AggregateHandler::new(CredentialServices) + AggregateHandler::new(issuance_services.clone()) .append_query(SimpleLoggingQuery {}) .append_query(generic_query(credential.clone())), |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher), @@ -153,7 +155,7 @@ pub async fn issuance_state(event_publishers: Vec>) -> I ), offer: Arc::new( offer_event_publishers.into_iter().fold( - AggregateHandler::new(OfferServices) + AggregateHandler::new(issuance_services) .append_query(SimpleLoggingQuery {}) .append_query(generic_query(offer.clone())) .append_query(pre_authorized_code_query) diff --git a/agent_store/src/postgres.rs b/agent_store/src/postgres.rs index c60a14cd..484be97a 100644 --- a/agent_store/src/postgres.rs +++ b/agent_store/src/postgres.rs @@ -1,10 +1,7 @@ use agent_issuance::{ - credential::services::CredentialServices, - offer::{ - queries::{access_token::AccessTokenQuery, pre_authorized_code::PreAuthorizedCodeQuery}, - services::OfferServices, - }, + offer::queries::{access_token::AccessTokenQuery, pre_authorized_code::PreAuthorizedCodeQuery}, server_config::services::ServerConfigServices, + services::IssuanceServices, state::{CommandHandlers, IssuanceState, ViewRepositories}, SimpleLoggingQuery, }; @@ -67,7 +64,10 @@ where } } -pub async fn issuance_state(event_publishers: Vec>) -> IssuanceState { +pub async fn issuance_state( + issuance_services: Arc, + event_publishers: Vec>, +) -> IssuanceState { let connection_string = config().event_store.connection_string.clone().expect( "Missing config parameter `event_store.connection_string` or `UNICORE__EVENT_STORE__CONNECTION_STRING`", ); @@ -100,7 +100,7 @@ pub async fn issuance_state(event_publishers: Vec>) -> I ), credential: Arc::new( credential_event_publishers.into_iter().fold( - AggregateHandler::new(pool.clone(), CredentialServices) + AggregateHandler::new(pool.clone(), issuance_services.clone()) .append_query(SimpleLoggingQuery {}) .append_query(generic_query(credential.clone())), |aggregate_handler, event_publisher| aggregate_handler.append_event_publisher(event_publisher), @@ -108,7 +108,7 @@ pub async fn issuance_state(event_publishers: Vec>) -> I ), offer: Arc::new( offer_event_publishers.into_iter().fold( - AggregateHandler::new(pool.clone(), OfferServices) + AggregateHandler::new(pool.clone(), issuance_services) .append_query(SimpleLoggingQuery {}) .append_query(generic_query(offer.clone())) .append_query(pre_authorized_code_query)