diff --git a/crates/sargon/fixtures/vector/wallet_interactions_dapp_to_wallet.json b/crates/sargon/fixtures/vector/wallet_interactions_dapp_to_wallet.json index 61181ac65..c2c8d2407 100644 --- a/crates/sargon/fixtures/vector/wallet_interactions_dapp_to_wallet.json +++ b/crates/sargon/fixtures/vector/wallet_interactions_dapp_to_wallet.json @@ -153,5 +153,43 @@ "origin" : "https://dev-sandbox.rdx-works-main.extratools.works/", "dAppDefinitionAddress" : "account_tdx_2_12xd46c22d6m696lv565t9afn088htudtq275px3qs925ywwty8axze" } + }, + { + "interactionId": "2916ad16-52a0-4564-a611-4971883c1322", + "items": { + "discriminator": "unauthorizedRequest", + "proofOfOwnership": { + "challenge": "4c85e4a903ab97450ef83763f8d4ca55a43efe843e1d2ced78a4940e5c397c9c", + "accountAddresses": [ + "account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90" + ] + } + }, + "metadata": { + "version": 2, + "dAppDefinitionAddress": "account_tdx_2_12xd46c22d6m696lv565t9afn088htudtq275px3qs925ywwty8axze", + "networkId": 2, + "origin": "https://dev-sandbox.rdx-works-main.extratools.works/" + } + }, + { + "interactionId": "17d530f6-0cb6-4122-8540-64e46a2e0f84", + "items": { + "discriminator": "unauthorizedRequest", + "proofOfOwnership": { + "challenge": "e280cfa39e1499f2862e59759cc2fc990cce28b70a7989324fe91c47814d0630", + "accountAddresses": [ + "account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90", + "account_tdx_2_129qeystv8tufmkmjrry2g6kadhhfh4f7rd0x3t9yagcvfhspt62paz" + ], + "identityAddress": "identity_tdx_2_12fat0nh0gymw9j4rqka5344p3h3r86x4z0hkw2v78r03pt0kfv0qva" + } + }, + "metadata": { + "version": 2, + "dAppDefinitionAddress": "account_tdx_2_12xd46c22d6m696lv565t9afn088htudtq275px3qs925ywwty8axze", + "networkId": 2, + "origin": "https://dev-sandbox.rdx-works-main.extratools.works/" + } } ] diff --git a/crates/sargon/fixtures/vector/wallet_interactions_wallet_to_dapp.json b/crates/sargon/fixtures/vector/wallet_interactions_wallet_to_dapp.json index d18f8a9ce..a7a994888 100644 --- a/crates/sargon/fixtures/vector/wallet_interactions_wallet_to_dapp.json +++ b/crates/sargon/fixtures/vector/wallet_interactions_wallet_to_dapp.json @@ -95,21 +95,69 @@ ] } } - }, - { - "discriminator" : "failure", - "interactionId" : "278608e0-e5ca-416e-8339-f2d2695651c4", - "error" : "rejectedByUser", - "message" : "User rejected the request" - }, - { - "discriminator" : "success", - "interactionId" : "c42f8825-4bbb-4ce2-a646-776b529e2f51", - "items" : { - "send" : { - "transactionIntentHash" : "txid_tdx_2_1mwuvufnewv6qkxdaesx0gcwap7n79knhkn0crsc8dg9g9k7qknjs6vkd3n" - }, - "discriminator" : "transaction" + }, + { + "discriminator" : "failure", + "interactionId" : "278608e0-e5ca-416e-8339-f2d2695651c4", + "error" : "rejectedByUser", + "message" : "User rejected the request" + }, + { + "discriminator" : "success", + "interactionId" : "c42f8825-4bbb-4ce2-a646-776b529e2f51", + "items" : { + "send" : { + "transactionIntentHash" : "txid_tdx_2_1mwuvufnewv6qkxdaesx0gcwap7n79knhkn0crsc8dg9g9k7qknjs6vkd3n" + }, + "discriminator" : "transaction" + } + }, + { + "discriminator" : "success", + "interactionId" : "2916ad16-52a0-4564-a611-4971883c1322", + "items" : { + "discriminator" : "unauthorizedRequest", + "proofOfOwnership": { + "challenge": "4c85e4a903ab97450ef83763f8d4ca55a43efe843e1d2ced78a4940e5c397c9c", + "proofs": [ + { + "accountAddress": "account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90", + "proof": { + "publicKey": "ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699", + "curve": "curve25519", + "signature": "10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05" + } + } + ] + } + } + }, + { + "discriminator" : "success", + "interactionId" : "17d530f6-0cb6-4122-8540-64e46a2e0f84", + "items" : { + "discriminator" : "unauthorizedRequest", + "proofOfOwnership": { + "challenge": "e280cfa39e1499f2862e59759cc2fc990cce28b70a7989324fe91c47814d0630", + "proofs": [ + { + "accountAddress": "account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90", + "proof": { + "publicKey": "ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699", + "curve": "curve25519", + "signature": "10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05" + } + }, + { + "identityAddress": "identity_tdx_2_12fat0nh0gymw9j4rqka5344p3h3r86x4z0hkw2v78r03pt0kfv0qva", + "proof": { + "publicKey": "ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699", + "curve": "curve25519", + "signature": "10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05" + } + } + ] } } + } ] \ No newline at end of file diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/mod.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/mod.rs index 16b7d403c..dffb38df2 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/mod.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/mod.rs @@ -1,7 +1,9 @@ mod accounts; mod persona_data; +mod proof_of_ownership; mod reset; pub use accounts::*; pub use persona_data::*; +pub use proof_of_ownership::*; pub use reset::*; diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/proof_of_ownership.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/proof_of_ownership.rs new file mode 100644 index 000000000..ef0c4cbe0 --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/entity/proof_of_ownership.rs @@ -0,0 +1,85 @@ +use crate::prelude::*; + +/// A request to prove ownership of `Accounts` and/or a `Persona`. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, uniffi::Record)] +#[serde(rename_all = "camelCase")] +pub struct DappToWalletInteractionProofOfOwnershipRequestItem { + /// The challenge that must be signed to prove ownership. + pub challenge: DappToWalletInteractionAuthChallengeNonce, + + /// The list of `AccountAddress`es for which the wallet must prove ownership. + #[serde(skip_serializing_if = "Option::is_none")] + pub account_addresses: Option>, + + /// The `IdentityAddress` for which the wallet must prove ownership. + #[serde(skip_serializing_if = "Option::is_none")] + pub identity_address: Option, +} + +impl DappToWalletInteractionProofOfOwnershipRequestItem { + pub fn new( + challenge: impl Into, + account_addresses: impl Into>>, + identity_address: impl Into>, + ) -> Self { + Self { + challenge: challenge.into(), + account_addresses: account_addresses.into(), + identity_address: identity_address.into(), + } + } +} + +impl HasSampleValues for DappToWalletInteractionProofOfOwnershipRequestItem { + fn sample() -> Self { + Self::new( + DappToWalletInteractionAuthChallengeNonce::sample(), + vec![AccountAddress::sample(), AccountAddress::sample_other()], + IdentityAddress::sample(), + ) + } + + fn sample_other() -> Self { + Self::new( + DappToWalletInteractionAuthChallengeNonce::sample_other(), + vec![AccountAddress::sample_other()], + IdentityAddress::sample_other(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = DappToWalletInteractionProofOfOwnershipRequestItem; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn json_roundtrip() { + assert_eq_after_json_roundtrip( + &SUT::sample(), + r#" + { + "challenge": "deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "accountAddresses": [ + "account_rdx128y6j78mt0aqv6372evz28hrxp8mn06ccddkr7xppc88hyvynvjdwr", + "account_rdx12xkzynhzgtpnnd02tudw2els2g9xl73yk54ppw8xekt2sdrlaer264" + ], + "identityAddress": "identity_rdx122yy9pkfdrkam4evxcwh235c4qc52wujkwnt52q7vqxefhnlen489g" + } + "#, + ); + } +} diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/unauthorized_request.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/unauthorized_request.rs index f4c007097..563259007 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/unauthorized_request.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/dapp_to_wallet/interaction_items/request/unauthorized_request.rs @@ -9,6 +9,10 @@ pub struct DappToWalletInteractionUnauthorizedRequestItems { #[serde(skip_serializing_if = "Option::is_none")] pub one_time_persona_data: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub proof_of_ownership: + Option, } impl DappToWalletInteractionUnauthorizedRequestItems { @@ -19,10 +23,14 @@ impl DappToWalletInteractionUnauthorizedRequestItems { one_time_persona_data: impl Into< Option, >, + proof_of_ownership: impl Into< + Option, + >, ) -> Self { Self { one_time_accounts: one_time_accounts.into(), one_time_persona_data: one_time_persona_data.into(), + proof_of_ownership: proof_of_ownership.into(), } } } @@ -32,6 +40,7 @@ impl HasSampleValues for DappToWalletInteractionUnauthorizedRequestItems { Self::new( DappToWalletInteractionAccountsRequestItem::sample(), DappToWalletInteractionPersonaDataRequestItem::sample(), + DappToWalletInteractionProofOfOwnershipRequestItem::sample(), ) } @@ -39,6 +48,7 @@ impl HasSampleValues for DappToWalletInteractionUnauthorizedRequestItems { Self::new( DappToWalletInteractionAccountsRequestItem::sample_other(), DappToWalletInteractionPersonaDataRequestItem::sample_other(), + DappToWalletInteractionProofOfOwnershipRequestItem::sample_other(), ) } } diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/failure_response/error_type.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/failure_response/error_type.rs index 9d8cf5f33..5cb93fcbb 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/failure_response/error_type.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/failure_response/error_type.rs @@ -24,6 +24,7 @@ pub enum DappWalletInteractionErrorType { InvalidRequest, IncompatibleVersion, FailedToSignAuthChallenge, + InvalidPersonaOrAccounts, } impl HasSampleValues for DappWalletInteractionErrorType { diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/authorized_request.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/authorized_request.rs index ea7e4be87..c9df733a6 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/authorized_request.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/authorized_request.rs @@ -4,15 +4,19 @@ use crate::prelude::*; #[serde(rename_all = "camelCase")] pub struct WalletToDappInteractionAuthorizedRequestResponseItems { pub auth: WalletToDappInteractionAuthRequestResponseItem, + #[serde(skip_serializing_if = "Option::is_none")] pub ongoing_accounts: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub ongoing_persona_data: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub one_time_accounts: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub one_time_persona_data: Option, diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/mod.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/mod.rs index aa3a3bfeb..698f2d468 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/mod.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/mod.rs @@ -2,7 +2,9 @@ mod account; mod auth; mod authorized_request; mod items; +mod persona; mod persona_data; +mod proof_of_ownership; mod success; mod transaction; mod unauthorized_request; @@ -11,7 +13,9 @@ pub use account::*; pub use auth::*; pub use authorized_request::*; pub use items::*; +pub use persona::*; pub use persona_data::*; +pub use proof_of_ownership::*; pub use success::*; pub use transaction::*; pub use unauthorized_request::*; diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/mod.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/mod.rs new file mode 100644 index 000000000..39fa7809d --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/mod.rs @@ -0,0 +1,3 @@ +mod persona_proof; + +pub use persona_proof::*; diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/persona_proof.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/persona_proof.rs new file mode 100644 index 000000000..6a2892692 --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/persona/persona_proof.rs @@ -0,0 +1,72 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, uniffi::Record)] +#[serde(rename_all = "camelCase")] +pub struct WalletToDappInteractionPersonaProof { + pub identity_address: IdentityAddress, + pub proof: WalletToDappInteractionAuthProof, +} + +impl WalletToDappInteractionPersonaProof { + pub fn new( + identity_address: impl Into, + proof: WalletToDappInteractionAuthProof, + ) -> Self { + Self { + identity_address: identity_address.into(), + proof, + } + } +} + +impl HasSampleValues for WalletToDappInteractionPersonaProof { + fn sample() -> Self { + Self::new( + IdentityAddress::sample(), + WalletToDappInteractionAuthProof::sample(), + ) + } + + fn sample_other() -> Self { + Self::new( + IdentityAddress::sample_other(), + WalletToDappInteractionAuthProof::sample_other(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = WalletToDappInteractionPersonaProof; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn json_roundtrip() { + assert_eq_after_json_roundtrip( + &SUT::sample(), + r#" + { + "identityAddress": "identity_rdx122yy9pkfdrkam4evxcwh235c4qc52wujkwnt52q7vqxefhnlen489g", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + } + "#, + ); + } +} diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/mod.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/mod.rs new file mode 100644 index 000000000..6ebdb470a --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/mod.rs @@ -0,0 +1,5 @@ +mod proof; +mod proof_of_ownership; + +pub use proof::*; +pub use proof_of_ownership::*; diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof.rs new file mode 100644 index 000000000..21b0fce52 --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof.rs @@ -0,0 +1,103 @@ +use crate::prelude::*; + +/// A proof of ownership of either an `Account` or a `Persona`. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, uniffi::Enum)] +#[serde(rename_all = "camelCase")] +#[serde(untagged)] +pub enum WalletToDappInteractionProofOfOwnership { + Account(WalletToDappInteractionAccountProof), + + Persona(WalletToDappInteractionPersonaProof), +} + +impl From + for WalletToDappInteractionProofOfOwnership +{ + fn from(value: WalletToDappInteractionAccountProof) -> Self { + Self::Account(value) + } +} + +impl From + for WalletToDappInteractionProofOfOwnership +{ + fn from(value: WalletToDappInteractionPersonaProof) -> Self { + Self::Persona(value) + } +} + +impl HasSampleValues for WalletToDappInteractionProofOfOwnership { + fn sample() -> Self { + Self::Account(WalletToDappInteractionAccountProof::sample()) + } + + fn sample_other() -> Self { + Self::Persona(WalletToDappInteractionPersonaProof::sample()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = WalletToDappInteractionProofOfOwnership; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn from_account() { + assert_eq!( + SUT::sample(), + WalletToDappInteractionAccountProof::sample().into() + ) + } + + #[test] + fn from_persona() { + assert_eq!( + SUT::sample_other(), + WalletToDappInteractionPersonaProof::sample().into() + ) + } + + #[test] + fn json_roundtrip() { + assert_eq_after_json_roundtrip( + &SUT::sample(), + r#" + { + "accountAddress": "account_rdx128y6j78mt0aqv6372evz28hrxp8mn06ccddkr7xppc88hyvynvjdwr", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + } + "#, + ); + + assert_eq_after_json_roundtrip( + &SUT::sample_other(), + r#" + { + "identityAddress": "identity_rdx122yy9pkfdrkam4evxcwh235c4qc52wujkwnt52q7vqxefhnlen489g", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + } + "#, + ); + } +} diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof_of_ownership.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof_of_ownership.rs new file mode 100644 index 000000000..6b65be574 --- /dev/null +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/proof_of_ownership/proof_of_ownership.rs @@ -0,0 +1,111 @@ +use crate::prelude::*; + +/// A response with the list of proofs of ownership for `Accounts`/`Personas` +/// and the challenge that was signed. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, uniffi::Record)] +pub struct WalletToDappInteractionProofOfOwnershipRequestResponseItem { + pub challenge: DappToWalletInteractionAuthChallengeNonce, + + pub proofs: Vec, +} + +impl WalletToDappInteractionProofOfOwnershipRequestResponseItem { + pub fn new( + challenge: impl Into, + proofs: impl Into>, + ) -> Self { + Self { + challenge: challenge.into(), + proofs: proofs.into(), + } + } +} + +impl HasSampleValues + for WalletToDappInteractionProofOfOwnershipRequestResponseItem +{ + fn sample() -> Self { + Self::new( + DappToWalletInteractionAuthChallengeNonce::sample(), + vec![ + WalletToDappInteractionProofOfOwnership::sample(), + WalletToDappInteractionProofOfOwnership::sample_other(), + ], + ) + } + + fn sample_other() -> Self { + Self::new( + DappToWalletInteractionAuthChallengeNonce::sample_other(), + vec![WalletToDappInteractionProofOfOwnership::sample_other()], + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = WalletToDappInteractionProofOfOwnershipRequestResponseItem; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn json_roundtrip() { + assert_eq_after_json_roundtrip( + &SUT::sample(), + r#" + { + "challenge": "deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "proofs": [ + { + "accountAddress": "account_rdx128y6j78mt0aqv6372evz28hrxp8mn06ccddkr7xppc88hyvynvjdwr", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + }, + { + "identityAddress": "identity_rdx122yy9pkfdrkam4evxcwh235c4qc52wujkwnt52q7vqxefhnlen489g", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + } + ] + } + "#, + ); + + assert_eq_after_json_roundtrip( + &SUT::sample_other(), + r#" + { + "challenge": "fadefadefadefadefadefadefadefadefadefadefadefadefadefadefadefade", + "proofs": [ + { + "identityAddress": "identity_rdx122yy9pkfdrkam4evxcwh235c4qc52wujkwnt52q7vqxefhnlen489g", + "proof": { + "publicKey": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", + "curve": "curve25519", + "signature": "fc6a4a15516b886b10f26777094cb1abdccb213c9ebdea7a4bceb83b6fcba50fea181b0136ee5659c3dfae5f771e5b6e6f9abbaa3f0435df0be1f732be965103" + } + } + ] + } + "#, + ); + } +} diff --git a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/unauthorized_request.rs b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/unauthorized_request.rs index 2459067b9..6a6a2a65a 100644 --- a/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/unauthorized_request.rs +++ b/crates/sargon/src/radix_connect/wallet_interaction/dapp_wallet_interaction/wallet_to_dapp/success_response/unauthorized_request.rs @@ -10,6 +10,10 @@ pub struct WalletToDappInteractionUnauthorizedRequestResponseItems { #[serde(skip_serializing_if = "Option::is_none")] pub one_time_persona_data: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub proof_of_ownership: + Option, } impl WalletToDappInteractionUnauthorizedRequestResponseItems { @@ -20,10 +24,14 @@ impl WalletToDappInteractionUnauthorizedRequestResponseItems { one_time_persona_data: impl Into< Option, >, + proof_of_ownership: impl Into< + Option, + >, ) -> Self { Self { one_time_accounts: one_time_accounts.into(), one_time_persona_data: one_time_persona_data.into(), + proof_of_ownership: proof_of_ownership.into(), } } } @@ -35,6 +43,8 @@ impl HasSampleValues Self::new( WalletToDappInteractionAccountsRequestResponseItem::sample(), WalletToDappInteractionPersonaDataRequestResponseItem::sample(), + WalletToDappInteractionProofOfOwnershipRequestResponseItem::sample( + ), ) } @@ -43,6 +53,7 @@ impl HasSampleValues WalletToDappInteractionAccountsRequestResponseItem::sample_other(), WalletToDappInteractionPersonaDataRequestResponseItem::sample_other( ), + WalletToDappInteractionProofOfOwnershipRequestResponseItem::sample_other(), ) } } diff --git a/crates/sargon/tests/vectors/main.rs b/crates/sargon/tests/vectors/main.rs index 078d6fbfd..cacee4c6c 100644 --- a/crates/sargon/tests/vectors/main.rs +++ b/crates/sargon/tests/vectors/main.rs @@ -615,7 +615,8 @@ mod dapp_to_wallet_interaction_tests { true, RequestedQuantity::exactly(1), RequestedQuantity::exactly(1), - ) + ), + None ) ); @@ -638,7 +639,8 @@ mod dapp_to_wallet_interaction_tests { true, RequestedQuantity::at_least(1), RequestedQuantity::at_least(1), - ) + ), + None, ) ); @@ -651,12 +653,61 @@ mod dapp_to_wallet_interaction_tests { metadata.clone(), ); + let account_proof_request_items = DappToWalletInteractionItems::UnauthorizedRequest( + DappToWalletInteractionUnauthorizedRequestItems::new( + None, + None, + DappToWalletInteractionProofOfOwnershipRequestItem::new( + DappToWalletInteractionAuthChallengeNonce(Exactly32Bytes::from_hex("4c85e4a903ab97450ef83763f8d4ca55a43efe843e1d2ced78a4940e5c397c9c").unwrap()), + vec![ + AccountAddress::from_str("account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90").unwrap(), + ], + None, + ), + ) + ); + + let account_proof_request = DappToWalletInteraction::new( + WalletInteractionId::from_str( + "2916ad16-52a0-4564-a611-4971883c1322", + ) + .unwrap(), + account_proof_request_items, + metadata.clone(), + ); + + let accounts_and_persona_proof_request_items = DappToWalletInteractionItems::UnauthorizedRequest( + DappToWalletInteractionUnauthorizedRequestItems::new( + None, + None, + DappToWalletInteractionProofOfOwnershipRequestItem::new( + DappToWalletInteractionAuthChallengeNonce(Exactly32Bytes::from_hex("e280cfa39e1499f2862e59759cc2fc990cce28b70a7989324fe91c47814d0630").unwrap()), + vec![ + AccountAddress::from_str("account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90").unwrap(), + AccountAddress::from_str("account_tdx_2_129qeystv8tufmkmjrry2g6kadhhfh4f7rd0x3t9yagcvfhspt62paz").unwrap(), + ], + IdentityAddress::from_str("identity_tdx_2_12fat0nh0gymw9j4rqka5344p3h3r86x4z0hkw2v78r03pt0kfv0qva").unwrap(), + ), + ) + ); + + let accounts_and_persona_proof_request = DappToWalletInteraction::new( + WalletInteractionId::from_str( + "17d530f6-0cb6-4122-8540-64e46a2e0f84", + ) + .unwrap(), + accounts_and_persona_proof_request_items, + metadata.clone(), + ); + let interactions = vec![ authorized_request_with_challenge, authorized_request_without_challenge, transaction, unauthorized_request_1, unauthorized_request_2, + account_proof_request, + accounts_and_persona_proof_request, ]; for (fixture, expected) in @@ -794,6 +845,7 @@ mod wallet_to_dapp_interaction_tests { None, ), persona_data.clone(), + None, ), ); @@ -830,11 +882,88 @@ mod wallet_to_dapp_interaction_tests { ) )); + let account_proof_response_items = + WalletToDappInteractionResponseItems::UnauthorizedRequest( + WalletToDappInteractionUnauthorizedRequestResponseItems::new( + None, + None, + WalletToDappInteractionProofOfOwnershipRequestResponseItem::new( + DappToWalletInteractionAuthChallengeNonce(Exactly32Bytes::from_hex("4c85e4a903ab97450ef83763f8d4ca55a43efe843e1d2ced78a4940e5c397c9c").unwrap()), + vec![WalletToDappInteractionProofOfOwnership::Account(WalletToDappInteractionAccountProof::new( + AccountAddress::from_str("account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90").unwrap(), + WalletToDappInteractionAuthProof::new( + PublicKey::from_str("ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699") + .unwrap(), + SLIP10Curve::Curve25519, + Signature::from_str("10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05") + .unwrap() + ), + ))] + ), + ), + ); + + let account_proof_response = WalletToDappInteractionResponse::Success( + WalletToDappInteractionSuccessResponse::new( + WalletInteractionId::from_str( + "2916ad16-52a0-4564-a611-4971883c1322", + ) + .unwrap(), + account_proof_response_items, + ), + ); + + let accounts_and_persona_proof_response_items = + WalletToDappInteractionResponseItems::UnauthorizedRequest( + WalletToDappInteractionUnauthorizedRequestResponseItems::new( + None, + None, + WalletToDappInteractionProofOfOwnershipRequestResponseItem::new( + DappToWalletInteractionAuthChallengeNonce(Exactly32Bytes::from_hex("e280cfa39e1499f2862e59759cc2fc990cce28b70a7989324fe91c47814d0630").unwrap()), + vec![ + WalletToDappInteractionProofOfOwnership::Account(WalletToDappInteractionAccountProof::new( + AccountAddress::from_str("account_tdx_2_12ytkalad6hfxamsz4a7r8tevz7ahurfj58dlp4phl4nca5hs0hpu90").unwrap(), + WalletToDappInteractionAuthProof::new( + PublicKey::from_str("ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699") + .unwrap(), + SLIP10Curve::Curve25519, + Signature::from_str("10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05") + .unwrap() + ), + )), + WalletToDappInteractionProofOfOwnership::Persona(WalletToDappInteractionPersonaProof::new( + IdentityAddress::from_str("identity_tdx_2_12fat0nh0gymw9j4rqka5344p3h3r86x4z0hkw2v78r03pt0kfv0qva").unwrap(), + WalletToDappInteractionAuthProof::new( + PublicKey::from_str("ff8aee4c625738e35d837edb11e33b8abe0d6f40849ca1451edaba84d04d0699") + .unwrap(), + SLIP10Curve::Curve25519, + Signature::from_str("10177ac7d486691777133ffe59d46d55529d86cb1c4ce66aa82f432372f33e24d803d8498f42e26fe113c030fce68c526aeacff94334ba5a7f7ef84c2936eb05") + .unwrap() + ), + )), + ] + ), + ), + ); + + let accounts_and_persona_proof_response = + WalletToDappInteractionResponse::Success( + WalletToDappInteractionSuccessResponse::new( + WalletInteractionId::from_str( + "17d530f6-0cb6-4122-8540-64e46a2e0f84", + ) + .unwrap(), + accounts_and_persona_proof_response_items, + ), + ); + let responses = vec![ authorized_request_response, unauthorized_request_response, failure_response, transaction_response, + account_proof_response, + accounts_and_persona_proof_response, ]; let encoded = serde_json::to_string(&responses).unwrap();