From 2a803e45597c9fb82e342e0cc32c1eef71c45c57 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Wed, 2 Oct 2024 10:03:38 +0300 Subject: [PATCH] Use verify account signature function from SDK --- README.md | 2 +- compliant-reward-distribution/README.md | 2 +- .../indexer-and-server/CHANGELOG.md | 4 + .../indexer-and-server/Cargo.lock | 12 +-- .../indexer-and-server/Cargo.toml | 2 +- .../indexer-and-server/src/bin/server.rs | 79 +++---------------- .../indexer-and-server/src/error.rs | 6 +- deps/concordium-rust-sdk | 2 +- 8 files changed, 32 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index a0d51727..c25d1ef7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The following examples are available. interactions of a store that sells restricted items, and supports payment in EUROe tokens (or generally any CIS2 token). -- [track-and-trace](./track-and-trace/) demonstrates an example frontend, backend, smart contract and event indexer to track items along the supply chain. It also has a [CIS-3](https://proposals.concordium.software/CIS/cis-3.html) sponsored transactions service that is compatible with any CIS-3 contract. +- [trackAndTrace](./trackAndTrace/) demonstrates an example frontend, backend, smart contract and event indexer to track items along the supply chain. It also has a [CIS-3](https://proposals.concordium.software/CIS/cis-3.html) sponsored transactions service that is compatible with any CIS-3 contract. - [compliant-reward-distribution](./compliant-reward-distribution/) demonstrates an airdrop example with an indexer, a frontend, and a backend with signature and ZK proof verification at the backend. The ZK proof is used to ensure `Sybil-resistance/uniqueness` as each identity on the chain can interact with the service with only one of its accounts. diff --git a/compliant-reward-distribution/README.md b/compliant-reward-distribution/README.md index f9552440..297e9217 100644 --- a/compliant-reward-distribution/README.md +++ b/compliant-reward-distribution/README.md @@ -6,7 +6,7 @@ ## Hosted front end -[Soon](https://github.com/Concordium/concordium-dapp-examples) +[here](https://compliant-reward-distribution.testnet.concordium.com/) ## Overview diff --git a/compliant-reward-distribution/indexer-and-server/CHANGELOG.md b/compliant-reward-distribution/indexer-and-server/CHANGELOG.md index 0006cbc1..90932408 100644 --- a/compliant-reward-distribution/indexer-and-server/CHANGELOG.md +++ b/compliant-reward-distribution/indexer-and-server/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased changes +## 0.1.1 + +- Change to use the new `verify_single_account_signature` function from the SDK. + ## 0.1.0 - Add initial `server`. diff --git a/compliant-reward-distribution/indexer-and-server/Cargo.lock b/compliant-reward-distribution/indexer-and-server/Cargo.lock index 1e53f404..6ef4fcc7 100644 --- a/compliant-reward-distribution/indexer-and-server/Cargo.lock +++ b/compliant-reward-distribution/indexer-and-server/Cargo.lock @@ -691,7 +691,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "concordium-contracts-common" -version = "9.1.0" +version = "9.2.0" dependencies = [ "base64", "bs58", @@ -720,7 +720,7 @@ dependencies = [ [[package]] name = "concordium-rust-sdk" -version = "4.3.0" +version = "5.0.0" dependencies = [ "aes-gcm", "anyhow", @@ -751,7 +751,7 @@ dependencies = [ [[package]] name = "concordium-smart-contract-engine" -version = "5.0.0" +version = "6.0.0" dependencies = [ "anyhow", "byteorder", @@ -774,7 +774,7 @@ dependencies = [ [[package]] name = "concordium-wasm" -version = "4.0.0" +version = "5.0.0" dependencies = [ "anyhow", "concordium-contracts-common", @@ -785,7 +785,7 @@ dependencies = [ [[package]] name = "concordium_base" -version = "5.0.0" +version = "6.0.0" dependencies = [ "aes", "anyhow", @@ -1515,7 +1515,7 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexer" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "axum", diff --git a/compliant-reward-distribution/indexer-and-server/Cargo.toml b/compliant-reward-distribution/indexer-and-server/Cargo.toml index 6883f9d1..29a024ca 100644 --- a/compliant-reward-distribution/indexer-and-server/Cargo.toml +++ b/compliant-reward-distribution/indexer-and-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indexer" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dependencies] diff --git a/compliant-reward-distribution/indexer-and-server/src/bin/server.rs b/compliant-reward-distribution/indexer-and-server/src/bin/server.rs index 2cd06c3f..362ac1fa 100644 --- a/compliant-reward-distribution/indexer-and-server/src/bin/server.rs +++ b/compliant-reward-distribution/indexer-and-server/src/bin/server.rs @@ -8,13 +8,14 @@ use axum::{ }; use chrono::Utc; use clap::Parser; -use concordium_rust_sdk::smart_contracts::common::to_bytes; use concordium_rust_sdk::{ id::{ constants::ArCurve, id_proof_types::Statement, types::{AccountAddress, AccountCredentialWithoutProofs}, }, + signatures::verify_single_account_signature, + smart_contracts::common::to_bytes, v2::{AccountIdentifier, BlockIdentifier, Client}, web3id::{ did::Network, @@ -533,19 +534,10 @@ where let SigningData { signer, message, - signature, + signature, block_height, } = param.signing_data(); - let signer_account_info = state - .node_client - .get_account_info( - &AccountIdentifier::Address(*signer), - BlockIdentifier::LastFinal, - ) - .await - .map_err(ServerError::QueryError)?; - let block_hash = state .node_client .get_block_info(block_height) @@ -565,63 +557,18 @@ where message, }; - // The message signed in the Concordium wallet is prepended with the - // `account` address (signer) and 8 zero bytes. Accounts in the Concordium - // wallet can either sign a regular transaction (in that case the - // prepend is `account` address and the nonce of the account which is by - // design >= 1) or sign a message (in that case the prepend is `account` - // address and 8 zero bytes). Hence, the 8 zero bytes ensure that the user - // does not accidentally sign a transaction. The account nonce is of type - // u64 (8 bytes). - // Add the prepend to the message and calculate the final message hash. - let final_message_hash = sha2::Sha256::digest( - [ - &signer.as_ref() as &[u8], - &[0u8; 8], - to_bytes(&message_signed_in_wallet).as_slice(), - ] - .concat(), - ); - - // Get the public key of the signer. + let message_bytes = to_bytes(&message_signed_in_wallet); - // The intention is to only use/support regular accounts (no multi-sig - // accounts). While it works for some (but not all) multi-sig accounts, to - // reduce complexity we will communicate that multi-sig accounts are not - // supported. Regular accounts have only one public-private key pair at - // index 0 in the credential map. - if signer_account_info.response.account_credentials.len() != 1 { - return Err(ServerError::OnlyRegularAccounts); - } - let signer_account_credential = signer_account_info - .response - .account_credentials - .get(&0.into()) - .ok_or(ServerError::OnlyRegularAccounts)?; - - let signer_public_key = match &signer_account_credential.value { - // `Initial` accounts were created by identity providers in the past - // without a Pedersen commitment deployed on chain. As such we should not verify ZK proofs - // on them so that we exclude them from this service. - AccountCredentialWithoutProofs::Initial { .. } => { - return Err(ServerError::NoCredentialCommitment) - } - // We use/support regular accounts. Regular accounts have only one - // public-private key pair at index 0 in the key map. - AccountCredentialWithoutProofs::Normal { cdv, .. } => { - if cdv.cred_key_info.keys.len() != 1 { - return Err(ServerError::OnlyRegularAccounts); - } - - cdv.cred_key_info - .keys - .get(&0.into()) - .ok_or(ServerError::OnlyRegularAccounts)? - } - }; + // Verify the signature. + let is_valid = verify_single_account_signature( + state.node_client.clone(), + *signer, + signature.clone(), + message_bytes, + BlockIdentifier::Best, + ) + .await?; - // Verify the signature. - let is_valid = signer_public_key.verify(final_message_hash, signature); if !is_valid { return Err(ServerError::InvalidSignature); } diff --git a/compliant-reward-distribution/indexer-and-server/src/error.rs b/compliant-reward-distribution/indexer-and-server/src/error.rs index 90c58d13..7f510789 100644 --- a/compliant-reward-distribution/indexer-and-server/src/error.rs +++ b/compliant-reward-distribution/indexer-and-server/src/error.rs @@ -6,6 +6,7 @@ use axum::{ use concordium_rust_sdk::{ base::{contracts_common::AccountAddressParseError, hashes::IncorrectLength}, common::types::AccountAddress, + signatures::SignatureError, types::AbsoluteBlockHeight, v2::QueryError, web3id::{did::Network, CredentialLookupError, PresentationVerificationError}, @@ -120,6 +121,8 @@ pub enum ServerError { AccountAddressParse(#[from] AccountAddressParseError), #[error("Not a valid tweet URL (expected format: https://x.com/JohnDoe/status/1818198789817077916 or https://twitter.com/JohnDoe/status/1818198789817077916)")] NotValidTweetURL, + #[error("Signature verification error: {0}")] + SignatureVerificationError(#[from] SignatureError), } impl IntoResponse for ServerError { @@ -163,7 +166,8 @@ impl IntoResponse for ServerError { | ServerError::NoCredentialCommitment | ServerError::IdentityReUsed { .. } | ServerError::AccountAddressParse(_) - | ServerError::NotValidTweetURL => { + | ServerError::NotValidTweetURL + | ServerError::SignatureVerificationError(..) => { let error_message = format!("Bad request: {self}"); tracing::info!(error_message); (StatusCode::BAD_REQUEST, error_message.into()) diff --git a/deps/concordium-rust-sdk b/deps/concordium-rust-sdk index 432dbffb..9b9f22b8 160000 --- a/deps/concordium-rust-sdk +++ b/deps/concordium-rust-sdk @@ -1 +1 @@ -Subproject commit 432dbffb95e6d534df07ec3375715c056d602445 +Subproject commit 9b9f22b8251f5f9a4bde53d8dc0873a9c3fa4e6b