From 4cf455d62deae9a05d5f727508ef6241771ddbcd Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Tue, 21 May 2024 16:55:10 +0200 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9abdb3868d76ccb39da2145346e201640448870a Author: Sven Date: Tue May 14 09:16:09 2024 +0200 Add EcDSA verifier (#1353) * add ecdsa verifier * add identity_ecdsa_verifier to workspace, add license headers * Update identity_ecdsa_verifier/Cargo.toml Co-authored-by: wulfraem * Update identity_ecdsa_verifier/src/secp256k1.rs Co-authored-by: wulfraem * Update identity_ecdsa_verifier/Cargo.toml Co-authored-by: wulfraem * Update identity_ecdsa_verifier/src/secp256k1.rs Co-authored-by: wulfraem * Update identity_ecdsa_verifier/src/secp256r1.rs Co-authored-by: wulfraem * add feedback * add OpenSSL installation to windows runner in CI * update license headers and authors for ecdsa verifier * update license template to allow multiple contributors --------- Co-authored-by: Sebastian Wolfram commit 149bfac98e8d9d8ca3d890e413291e384447c62b Author: wulfraem Date: Mon May 13 10:44:09 2024 +0200 Fix findings after clippy update (#1365) * fix clippy findings * fix formatting * refactor .clone_into calls into .to_string * fix previous edit * disable empty_docs for wasm binding for now * fix missing newline * disable self update from rust setup in ci for now * update self update skip to skip only for windows build commit 51aedd51be086e333744b020e867c0348833a083 Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Apr 30 16:16:36 2024 +0200 Use STRONGHOLD_PWD_FILE env variable to pass stronghold's password (#1363) commit edec26c18782ad75a20ca6bebd7c66959eadb91d Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Apr 30 15:40:55 2024 +0200 Arbitrary data signing service (#1350) commit f59e75a57df05971aef00549c8880b83c6600f2b Author: Eike Haß Date: Tue Apr 30 15:34:40 2024 +0200 Fix dockerhub workflow (#1343) commit 993cfec8a698f668f2891f46e6c94f2584c50c05 Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Fri Apr 26 13:39:29 2024 +0200 add inx-faucet profile (#1356) --- .github/actions/rust/rust-setup/action.yml | 19 ++- .github/workflows/build-and-test.yml | 6 + .../workflows/grpc-publish-to-dockerhub.yml | 5 +- .license_template | 2 +- Cargo.toml | 1 + bindings/grpc/README.md | 26 ++-- bindings/grpc/proto/utils.proto | 23 ++++ bindings/grpc/src/main.rs | 22 +++- bindings/grpc/src/services/mod.rs | 2 + bindings/grpc/src/services/utils.rs | 67 +++++++++++ bindings/grpc/tests/api/main.rs | 1 + bindings/grpc/tests/api/utils.rs | 48 ++++++++ bindings/wasm/Cargo.toml | 5 + identity_credential/src/credential/proof.rs | 4 +- .../revocation/status_list_2021/credential.rs | 6 +- .../src/validator/test_utils.rs | 2 +- identity_ecdsa_verifier/Cargo.toml | 32 +++++ identity_ecdsa_verifier/README.md | 3 + .../src/ecdsa_jws_verifier.rs | 34 ++++++ identity_ecdsa_verifier/src/lib.rs | 29 +++++ identity_ecdsa_verifier/src/secp256k1.rs | 93 +++++++++++++++ identity_ecdsa_verifier/src/secp256r1.rs | 89 ++++++++++++++ identity_ecdsa_verifier/src/tests/mod.rs | 5 + identity_ecdsa_verifier/src/tests/secp256.rs | 77 ++++++++++++ identity_ecdsa_verifier/src/tests/secp256k.rs | 112 ++++++++++++++++++ identity_iota_core/src/document/test_utils.rs | 2 +- identity_storage/src/key_storage/ed25519.rs | 2 +- identity_storage/src/key_storage/memstore.rs | 8 +- .../src/key_storage/tests/utils.rs | 8 +- .../src/storage/tests/test_utils.rs | 2 +- identity_stronghold/src/ed25519.rs | 2 +- .../src/storage/stronghold_jwk_storage.rs | 2 +- .../src/tests/test_jwk_storage.rs | 8 +- identity_stronghold/src/tests/utils.rs | 2 +- 34 files changed, 702 insertions(+), 47 deletions(-) create mode 100644 bindings/grpc/proto/utils.proto create mode 100644 bindings/grpc/src/services/utils.rs create mode 100644 bindings/grpc/tests/api/utils.rs create mode 100644 identity_ecdsa_verifier/Cargo.toml create mode 100644 identity_ecdsa_verifier/README.md create mode 100644 identity_ecdsa_verifier/src/ecdsa_jws_verifier.rs create mode 100644 identity_ecdsa_verifier/src/lib.rs create mode 100644 identity_ecdsa_verifier/src/secp256k1.rs create mode 100644 identity_ecdsa_verifier/src/secp256r1.rs create mode 100644 identity_ecdsa_verifier/src/tests/mod.rs create mode 100644 identity_ecdsa_verifier/src/tests/secp256.rs create mode 100644 identity_ecdsa_verifier/src/tests/secp256k.rs diff --git a/.github/actions/rust/rust-setup/action.yml b/.github/actions/rust/rust-setup/action.yml index b7b16a352a..5f783a98cc 100644 --- a/.github/actions/rust/rust-setup/action.yml +++ b/.github/actions/rust/rust-setup/action.yml @@ -48,7 +48,16 @@ runs: shell: bash run: | - if ! rustup self update; then + # self update is currently broken on Windows runners: + # https://github.com/rust-lang/rustup/issues/3709 + # so we'll skip self update for windows + OS=${{ inputs.os }} + IS_WINDOWS=false; [[ $OS =~ ^[wW]indows ]] && IS_WINDOWS=true + + if [[ $IS_WINDOWS = true ]] ; + then + echo "skipping self update on windows runner due to https://github.com/rust-lang/rustup/issues/3709" + elif ! rustup self update; then echo "rustup self update failed" fi @@ -57,7 +66,13 @@ runs: rustup target add $TARGET fi - rustup update + if [[ $IS_WINDOWS = true ]] ; + then + echo "skipping self update on windows runner due to https://github.com/rust-lang/rustup/issues/3709" + rustup update --no-self-update + else + rustup update + fi TOOLCHAIN=${{ inputs.toolchain }} if [[ $TOOLCHAIN != 'stable' ]]; then diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a58316790f..206c534962 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -82,6 +82,12 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Ensure, OpenSSL is available in Windows + if: matrix.os == 'windows-latest' + run: | + echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + vcpkg install openssl:x64-windows-static-md + - name: Setup Rust and cache uses: './.github/actions/rust/rust-setup' with: diff --git a/.github/workflows/grpc-publish-to-dockerhub.yml b/.github/workflows/grpc-publish-to-dockerhub.yml index d72fe20702..348bf8c564 100644 --- a/.github/workflows/grpc-publish-to-dockerhub.yml +++ b/.github/workflows/grpc-publish-to-dockerhub.yml @@ -39,14 +39,15 @@ jobs: context: . file: bindings/grpc/Dockerfile push: ${{ !inputs.dry-run }} - labels: iotaledger/identity-grpc:${{ inputs.tag }} + tags: iotaledger/identity-grpc:${{ inputs.tag }} - name: Docker Hub Description uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae + if: ${{ !inputs.dry-run }} with: username: ${{ secrets.IOTALEDGER_DOCKER_USERNAME }} password: ${{ secrets.IOTALEDGER_DOCKER_PASSWORD }} repository: iotaledger/identity-grpc - readme-filepath: ./bindigns/grpc/README.md + readme-filepath: ./bindings/grpc/README.md short-description: ${{ github.event.repository.description }} diff --git a/.license_template b/.license_template index 30334ddc0c..a437281e00 100644 --- a/.license_template +++ b/.license_template @@ -1,2 +1,2 @@ -// Copyright {20\d{2}(-20\d{2})?} IOTA Stiftung +// Copyright {20\d{2}(-20\d{2})?} IOTA Stiftung{(?:, .+)?} // SPDX-License-Identifier: Apache-2.0 diff --git a/Cargo.toml b/Cargo.toml index e9aab70af4..e7eda3efc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "identity_verification", "identity_stronghold", "identity_jose", + "identity_ecdsa_verifier", "identity_eddsa_verifier", "examples", ] diff --git a/bindings/grpc/README.md b/bindings/grpc/README.md index 814e82a7f8..f94f0add17 100644 --- a/bindings/grpc/README.md +++ b/bindings/grpc/README.md @@ -1,7 +1,7 @@ # Identity.rs gRPC Bindings This project provides the functionalities of [Identity.rs](https://github.com/iotaledger/identity.rs) in a language-agnostic way through a [gRPC](https://grpc.io) server. -The server can easily be run with docker using [this dockerfile](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/Dockerfile). +The server can easily be run with docker using [this dockerfile](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/Dockerfile). ## Build Run `docker build -f bindings/grpc/Dockerfile -t iotaleger/identity-grpc .` from the project root. @@ -17,17 +17,17 @@ Make sure to provide a valid stronghold snapshot at the provided `SNAPSHOT_PATH` ### Available services | Service description | Service Id | Proto File | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------| -| Credential Revocation Checking | `credentials/CredentialRevocation.check` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) | -| SD-JWT Validation | `sd_jwt/Verification.verify` | [sd_jwt.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/sd_jwt.proto) | -| Credential JWT creation | `credentials/Jwt.create` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) | -| Credential JWT validation | `credentials/VcValidation.validate` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/credentials.proto) | -| DID Document Creation | `document/DocumentService.create` | [document.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/document.proto) | -| Domain Linkage - validate domain, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_domain` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) | -| Domain Linkage - validate domain, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_domain_against_did_configuration` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) | -| Domain Linkage - validate endpoints in DID, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_did` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) | -| Domain Linkage - validate endpoints in DID, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_did_against_did_configurations` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/domain_linkage.proto) | -| `StatusList2021Credential` creation | `status_list_2021/StatusList2021Svc.create` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/status_list_2021.proto) | -| `StatusList2021Credential` update | `status_list_2021/StatusList2021Svc.update` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/grpc-bindings/bindings/grpc/proto/status_list_2021.proto) | +| Credential Revocation Checking | `credentials/CredentialRevocation.check` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/credentials.proto) | +| SD-JWT Validation | `sd_jwt/Verification.verify` | [sd_jwt.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/sd_jwt.proto) | +| Credential JWT creation | `credentials/Jwt.create` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/credentials.proto) | +| Credential JWT validation | `credentials/VcValidation.validate` | [credentials.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/credentials.proto) | +| DID Document Creation | `document/DocumentService.create` | [document.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/document.proto) | +| Domain Linkage - validate domain, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_domain` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/domain_linkage.proto) | +| Domain Linkage - validate domain, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_domain_against_did_configuration` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/domain_linkage.proto) | +| Domain Linkage - validate endpoints in DID, let server fetch did-configuration | `domain_linkage/DomainLinkage.validate_did` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/domain_linkage.proto) | +| Domain Linkage - validate endpoints in DID, pass did-configuration to service | `domain_linkage/DomainLinkage.validate_did_against_did_configurations` | [domain_linkage.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/domain_linkage.proto) | +| `StatusList2021Credential` creation | `status_list_2021/StatusList2021Svc.create` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/status_list_2021.proto) | +| `StatusList2021Credential` update | `status_list_2021/StatusList2021Svc.update` | [status_list_2021.proto](https://github.com/iotaledger/identity.rs/blob/main/bindings/grpc/proto/status_list_2021.proto) | ## Testing @@ -62,7 +62,7 @@ In order to test domain linkage, you need access to a server that is reachable v 1. for convenience, you can find a script to start the HTTP server, that you can adjust in `tooling/start-http-server.sh`, don't forget to insert your static domain or to remove the `--domain` parameter #### Domain linkage credential -1. copy the public url and insert it into [6_domain_linkage.rs](../../examples/1_advanced/6_domain_linkage.rs) as domain 1, e.g. `let domain_1: Url = Url::parse("https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app")?;` +1. copy the public url and insert it into [6_domain_linkage.rs](https://github.com/iotaledger/identity.rs/blob/main/examples/1_advanced/6_domain_linkage.rs) as domain 1, e.g. `let domain_1: Url = Url::parse("https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app")?;` .1 run the example with `cargo run --release --example 6_domain_linkage` #### GRPC server diff --git a/bindings/grpc/proto/utils.proto b/bindings/grpc/proto/utils.proto new file mode 100644 index 0000000000..87ea3f7054 --- /dev/null +++ b/bindings/grpc/proto/utils.proto @@ -0,0 +1,23 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +syntax = "proto3"; +package utils; + +message DataSigningRequest { + // Raw data that will be signed. + bytes data = 1; + // Signing key's ID. + string key_id = 2; +} + +message DataSigningResponse { + // Raw data signature. + bytes signature = 1; +} + +// Service that handles signing operations on raw data. +service Signing { + rpc sign(DataSigningRequest) returns (DataSigningResponse); +} + diff --git a/bindings/grpc/src/main.rs b/bindings/grpc/src/main.rs index 4e6e3e11fa..04927b1c9c 100644 --- a/bindings/grpc/src/main.rs +++ b/bindings/grpc/src/main.rs @@ -1,6 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use anyhow::Context; use identity_grpc::server::GRpcServer; use identity_stronghold::StrongholdStorage; use iota_sdk::client::stronghold::StrongholdAdapter; @@ -29,11 +30,18 @@ async fn main() -> anyhow::Result<()> { #[tracing::instrument] fn init_stronghold() -> anyhow::Result { - let stronghold_password = std::env::var("STRONGHOLD_PWD")?; - let snapshot_path = std::env::var("SNAPSHOT_PATH")?; + use std::env; + use std::fs; + let stronghold_password = env::var("STRONGHOLD_PWD_FILE") + .context("Unset \"STRONGHOLD_PWD_FILE\" env variable") + .and_then(|path| fs::read_to_string(&path).context(format!("{path} does not exists"))) + .map(sanitize_pwd) + .or(env::var("STRONGHOLD_PWD")) + .context("No password for stronghold was provided")?; + let snapshot_path = env::var("SNAPSHOT_PATH")?; // Check for snapshot file at specified path - let metadata = std::fs::metadata(&snapshot_path)?; + let metadata = fs::metadata(&snapshot_path)?; if !metadata.is_file() { return Err(anyhow::anyhow!("No snapshot at provided path \"{}\"", &snapshot_path)); } @@ -45,3 +53,11 @@ fn init_stronghold() -> anyhow::Result { .map(StrongholdStorage::new)?, ) } + +/// Remove any trailing whitespace in-place. +fn sanitize_pwd(mut pwd: String) -> String { + let trimmed = pwd.trim_end(); + pwd.truncate(trimmed.len()); + pwd.shrink_to_fit(); + pwd +} diff --git a/bindings/grpc/src/services/mod.rs b/bindings/grpc/src/services/mod.rs index f632feb91a..00abe17ce1 100644 --- a/bindings/grpc/src/services/mod.rs +++ b/bindings/grpc/src/services/mod.rs @@ -7,6 +7,7 @@ pub mod domain_linkage; pub mod health_check; pub mod sd_jwt; pub mod status_list_2021; +pub mod utils; use identity_stronghold::StrongholdStorage; use iota_sdk::client::Client; @@ -21,6 +22,7 @@ pub fn routes(client: &Client, stronghold: &StrongholdStorage) -> Routes { routes.add_service(domain_linkage::service(client)); routes.add_service(document::service(client, stronghold)); routes.add_service(status_list_2021::service()); + routes.add_service(utils::service(stronghold)); routes.routes() } diff --git a/bindings/grpc/src/services/utils.rs b/bindings/grpc/src/services/utils.rs new file mode 100644 index 0000000000..0e7d2fc570 --- /dev/null +++ b/bindings/grpc/src/services/utils.rs @@ -0,0 +1,67 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use _utils::signing_server::Signing as SigningSvc; +use _utils::signing_server::SigningServer; +use _utils::DataSigningRequest; +use _utils::DataSigningResponse; +use identity_iota::storage::JwkStorage; +use identity_iota::storage::KeyId; +use identity_iota::storage::KeyStorageError; +use identity_stronghold::StrongholdStorage; +use tonic::Request; +use tonic::Response; +use tonic::Status; + +mod _utils { + tonic::include_proto!("utils"); +} + +#[derive(Debug, thiserror::Error)] +#[error("Key storage error: {0}")] +pub struct Error(#[from] KeyStorageError); + +impl From for Status { + fn from(value: Error) -> Self { + Status::internal(value.to_string()) + } +} + +pub struct SigningService { + storage: StrongholdStorage, +} + +impl SigningService { + pub fn new(stronghold: &StrongholdStorage) -> Self { + Self { + storage: stronghold.clone(), + } + } +} + +#[tonic::async_trait] +impl SigningSvc for SigningService { + #[tracing::instrument( + name = "utils/sign", + skip_all, + fields(request = ?req.get_ref()) + ret, + err, + )] + async fn sign(&self, req: Request) -> Result, Status> { + let DataSigningRequest { data, key_id } = req.into_inner(); + let key_id = KeyId::new(key_id); + let public_key_jwk = self.storage.get_public_key(&key_id).await.map_err(Error)?; + let signature = self + .storage + .sign(&key_id, &data, &public_key_jwk) + .await + .map_err(Error)?; + + Ok(Response::new(DataSigningResponse { signature })) + } +} + +pub fn service(stronghold: &StrongholdStorage) -> SigningServer { + SigningServer::new(SigningService::new(stronghold)) +} diff --git a/bindings/grpc/tests/api/main.rs b/bindings/grpc/tests/api/main.rs index e187cf7f1c..af4929bfae 100644 --- a/bindings/grpc/tests/api/main.rs +++ b/bindings/grpc/tests/api/main.rs @@ -10,3 +10,4 @@ mod helpers; mod jwt; mod sd_jwt_validation; mod status_list_2021; +mod utils; diff --git a/bindings/grpc/tests/api/utils.rs b/bindings/grpc/tests/api/utils.rs new file mode 100644 index 0000000000..9c863bf3de --- /dev/null +++ b/bindings/grpc/tests/api/utils.rs @@ -0,0 +1,48 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use _utils::signing_client::SigningClient; +use _utils::DataSigningRequest; +use identity_iota::verification::jws::JwsAlgorithm; +use identity_storage::JwkStorage; +use identity_storage::KeyType; +use identity_stronghold::StrongholdStorage; + +use crate::helpers::make_stronghold; +use crate::helpers::TestServer; + +mod _utils { + tonic::include_proto!("utils"); +} + +const SAMPLE_SIGNING_DATA: &'static [u8] = b"I'm just some random data to be signed :)"; + +#[tokio::test] +async fn raw_data_signing_works() -> anyhow::Result<()> { + let stronghold = StrongholdStorage::new(make_stronghold()); + let server = TestServer::new_with_stronghold(stronghold.clone()).await; + + let key_id = stronghold + .generate(KeyType::from_static_str("Ed25519"), JwsAlgorithm::EdDSA) + .await? + .key_id; + + let expected_signature = { + let public_key_jwk = stronghold.get_public_key(&key_id).await?; + stronghold.sign(&key_id, SAMPLE_SIGNING_DATA, &public_key_jwk).await? + }; + + let mut grpc_client = SigningClient::connect(server.endpoint()).await?; + let signature = grpc_client + .sign(DataSigningRequest { + data: SAMPLE_SIGNING_DATA.to_owned(), + key_id: key_id.to_string(), + }) + .await? + .into_inner() + .signature; + + assert_eq!(signature, expected_signature); + + Ok(()) +} diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 819da13d7b..74bee6d945 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -47,3 +47,8 @@ instant = { version = "0.1", default-features = false, features = ["wasm-bindgen [profile.release] opt-level = 's' lto = true + +[lints.clippy] +# can be removed as soon as fix has been added to clippy +# see https://github.com/rust-lang/rust-clippy/issues/12377 +empty_docs = "allow" diff --git a/identity_credential/src/credential/proof.rs b/identity_credential/src/credential/proof.rs index 03e4bca663..ab779014a2 100644 --- a/identity_credential/src/credential/proof.rs +++ b/identity_credential/src/credential/proof.rs @@ -52,7 +52,7 @@ mod tests { assert_eq!(proof.type_, "test-proof"); let value = proof .properties - .get(&"signature".to_owned()) + .get("signature") .expect("property in proof doesn't exist"); assert_eq!(value, "abc123"); } @@ -88,7 +88,7 @@ mod tests { assert_eq!(proof.type_, "RsaSignature2018"); let value = proof .properties - .get(&"proofPurpose".to_owned()) + .get("proofPurpose") .expect("property in proof doesn't exist"); assert_eq!(value, "assertionMethod"); assert_eq!(proof.properties.len(), 4); diff --git a/identity_credential/src/revocation/status_list_2021/credential.rs b/identity_credential/src/revocation/status_list_2021/credential.rs index e2ef0950cd..4402283e1a 100644 --- a/identity_credential/src/revocation/status_list_2021/credential.rs +++ b/identity_credential/src/revocation/status_list_2021/credential.rs @@ -279,11 +279,7 @@ impl StatusList2021CredentialSubject { return Err(StatusList2021CredentialError::MultipleCredentialSubject); }; if let Some(subject_type) = subject.properties.get("type") { - if !subject_type - .as_str() - .map(|t| t == CREDENTIAL_SUBJECT_TYPE) - .unwrap_or(false) - { + if subject_type.as_str() != Some(CREDENTIAL_SUBJECT_TYPE) { return Err(StatusList2021CredentialError::InvalidProperty("credentialSubject.type")); } } else { diff --git a/identity_credential/src/validator/test_utils.rs b/identity_credential/src/validator/test_utils.rs index 9febb41a1f..22a18a7605 100644 --- a/identity_credential/src/validator/test_utils.rs +++ b/identity_credential/src/validator/test_utils.rs @@ -19,7 +19,7 @@ pub(crate) fn encode_public_ed25519_jwk(public_key: &PublicKey) -> Jwk { let mut params = JwkParamsOkp::new(); params.x = x; params.d = None; - params.crv = EdCurve::Ed25519.name().to_owned(); + params.crv = EdCurve::Ed25519.name().to_string(); let mut jwk = Jwk::from_params(params); jwk.set_alg(JwsAlgorithm::EdDSA.name()); jwk diff --git a/identity_ecdsa_verifier/Cargo.toml b/identity_ecdsa_verifier/Cargo.toml new file mode 100644 index 0000000000..c72ffa089c --- /dev/null +++ b/identity_ecdsa_verifier/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "identity_ecdsa_verifier" +version = "0.1.0" +authors = ["IOTA Stiftung", "Filancore GmbH"] +edition.workspace = true +homepage.workspace = true +keywords = ["iota", "identity", "jose", "jwk", "jws"] +license.workspace = true +readme = "./README.md" +repository.workspace = true +rust-version.workspace = true +description = "JWS ECDSA signature verification for IOTA Identity" + +[lints] +workspace = true + +[dependencies] +identity_verification = { version = "=1.2.0", path = "../identity_verification", default-features = false } +k256 = { version = "0.13.3", default-features = false, features = ["std", "ecdsa", "ecdsa-core"], optional = true } +p256 = { version = "0.13.2", default-features = false, features = ["std", "ecdsa", "ecdsa-core"], optional = true } +signature = { version = "2", default-features = false } + +[dev-dependencies] +josekit = "0.8.6" +serde_json.workspace = true + +[features] +default = ["es256", "es256k"] +# Enables the EcDSAJwsVerifier to verify JWS with alg = ES256. +es256 = ["dep:p256"] +# Enables the EcDSAJwsVerifier to verify JWS with alg = ES256K. +es256k = ["dep:k256"] diff --git a/identity_ecdsa_verifier/README.md b/identity_ecdsa_verifier/README.md new file mode 100644 index 0000000000..4ccb0f36b9 --- /dev/null +++ b/identity_ecdsa_verifier/README.md @@ -0,0 +1,3 @@ +# ECDSA Verifier + +This crate implements a `JwsVerifier` capable of verifying EcDSA signatures with algorithms `ES256` and `ES256K`. diff --git a/identity_ecdsa_verifier/src/ecdsa_jws_verifier.rs b/identity_ecdsa_verifier/src/ecdsa_jws_verifier.rs new file mode 100644 index 0000000000..6371b40b78 --- /dev/null +++ b/identity_ecdsa_verifier/src/ecdsa_jws_verifier.rs @@ -0,0 +1,34 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +use identity_verification::jws::JwsAlgorithm; +use identity_verification::jws::JwsVerifier; +use identity_verification::jws::SignatureVerificationErrorKind; + +/// An implementor of [`JwsVerifier`](identity_verification::jws::JwsVerifier) +/// that can handle a selection of EcDSA algorithms. +/// +/// The following algorithms are supported, if the respective feature on the +/// crate is activated: +/// +/// - [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256). +/// - [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K). +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct EcDSAJwsVerifier {} + +impl JwsVerifier for EcDSAJwsVerifier { + fn verify( + &self, + input: identity_verification::jws::VerificationInput, + public_key: &identity_verification::jwk::Jwk, + ) -> Result<(), identity_verification::jws::SignatureVerificationError> { + match input.alg { + #[cfg(feature = "es256")] + JwsAlgorithm::ES256 => crate::Secp256R1Verifier::verify(&input, public_key), + #[cfg(feature = "es256k")] + JwsAlgorithm::ES256K => crate::Secp256K1Verifier::verify(&input, public_key), + _ => Err(SignatureVerificationErrorKind::UnsupportedAlg.into()), + } + } +} diff --git a/identity_ecdsa_verifier/src/lib.rs b/identity_ecdsa_verifier/src/lib.rs new file mode 100644 index 0000000000..6136a3eae1 --- /dev/null +++ b/identity_ecdsa_verifier/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +#![doc = include_str!("./../README.md")] +#![warn( + rust_2018_idioms, + unreachable_pub, + missing_docs, + rustdoc::missing_crate_level_docs, + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::private_doc_tests, + clippy::missing_safety_doc +)] + +mod ecdsa_jws_verifier; +#[cfg(feature = "es256k")] +mod secp256k1; +#[cfg(feature = "es256")] +mod secp256r1; + +pub use ecdsa_jws_verifier::*; +#[cfg(feature = "es256k")] +pub use secp256k1::*; +#[cfg(feature = "es256")] +pub use secp256r1::*; + +#[cfg(test)] +mod tests; diff --git a/identity_ecdsa_verifier/src/secp256k1.rs b/identity_ecdsa_verifier/src/secp256k1.rs new file mode 100644 index 0000000000..9c77412cc8 --- /dev/null +++ b/identity_ecdsa_verifier/src/secp256k1.rs @@ -0,0 +1,93 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; + +use identity_verification::jwk::JwkParamsEc; +use identity_verification::jws::SignatureVerificationError; +use identity_verification::jws::SignatureVerificationErrorKind; +use identity_verification::jwu::{self}; +use k256::ecdsa::Signature; +use k256::ecdsa::VerifyingKey; +use k256::elliptic_curve::sec1::FromEncodedPoint; +use k256::elliptic_curve::subtle::CtOption; +use k256::EncodedPoint; +use k256::PublicKey; + +/// A verifier that can handle the +/// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) +/// algorithm. +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct Secp256K1Verifier {} + +impl Secp256K1Verifier { + /// Verify a JWS signature secured with the + /// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) + /// algorithm. + /// + /// This function is useful when one is building a + /// [`JwsVerifier`](identity_verification::jws::JwsVerifier) that + /// handles the + /// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) + /// in the same manner as the [`Secp256K1Verifier`] hence extending its + /// capabilities. + /// + /// # Warning + /// + /// This function does not check whether `alg = ES256K` in the protected + /// header. Callers are expected to assert this prior to calling the + /// function. + pub fn verify( + input: &identity_verification::jws::VerificationInput, + public_key: &identity_verification::jwk::Jwk, + ) -> Result<(), SignatureVerificationError> { + // Obtain a K256 public key. + let params: &JwkParamsEc = public_key + .try_ec_params() + .map_err(|_| SignatureVerificationErrorKind::UnsupportedKeyType)?; + + // Concatenate x and y coordinates as required by + // EncodedPoint::from_untagged_bytes. + let public_key_bytes = jwu::decode_b64(¶ms.x) + .map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err) + })? + .into_iter() + .chain(jwu::decode_b64(¶ms.y).map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err) + })?) + .collect(); + + // The JWK contains the uncompressed x and y coordinates, so we can create the + // encoded point directly without prefixing an SEC1 tag. + let encoded_point: EncodedPoint = EncodedPoint::from_untagged_bytes(&public_key_bytes); + let public_key: PublicKey = { + let opt_public_key: CtOption = PublicKey::from_encoded_point(&encoded_point); + if opt_public_key.is_none().into() { + return Err(SignatureVerificationError::new( + SignatureVerificationErrorKind::KeyDecodingFailure, + )); + } else { + opt_public_key.unwrap() + } + }; + + let verifying_key: VerifyingKey = VerifyingKey::from(public_key); + + let mut signature: Signature = Signature::try_from(input.decoded_signature.deref()).map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err) + })?; + + if let Some(normalized) = signature.normalize_s() { + signature = normalized; + } + + match signature::Verifier::verify(&verifying_key, &input.signing_input, &signature) { + Ok(()) => Ok(()), + Err(err) => { + Err(SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err)) + } + } + } +} diff --git a/identity_ecdsa_verifier/src/secp256r1.rs b/identity_ecdsa_verifier/src/secp256r1.rs new file mode 100644 index 0000000000..09201570d0 --- /dev/null +++ b/identity_ecdsa_verifier/src/secp256r1.rs @@ -0,0 +1,89 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; + +use identity_verification::jwk::JwkParamsEc; +use identity_verification::jws::SignatureVerificationError; +use identity_verification::jws::SignatureVerificationErrorKind; +use identity_verification::jwu::{self}; +use p256::ecdsa::Signature; +use p256::ecdsa::VerifyingKey; +use p256::elliptic_curve::sec1::FromEncodedPoint; +use p256::elliptic_curve::subtle::CtOption; +use p256::EncodedPoint; +use p256::PublicKey; + +/// A verifier that can handle the +/// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) +/// algorithm. +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct Secp256R1Verifier {} + +impl Secp256R1Verifier { + /// Verify a JWS signature secured with the + /// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) + /// algorithm. + /// + /// This function is useful when one is building a + /// [`JwsVerifier`](identity_verification::jws::JwsVerifier) that + /// handles the + /// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) + /// in the same manner as the [`Secp256R1Verifier`] hence extending its + /// capabilities. + /// + /// # Warning + /// + /// This function does not check whether `alg = ES256` in the protected + /// header. Callers are expected to assert this prior to calling the + /// function. + pub fn verify( + input: &identity_verification::jws::VerificationInput, + public_key: &identity_verification::jwk::Jwk, + ) -> Result<(), SignatureVerificationError> { + // Obtain a P256 public key. + let params: &JwkParamsEc = public_key + .try_ec_params() + .map_err(|_| SignatureVerificationErrorKind::UnsupportedKeyType)?; + + // Concatenate x and y coordinates as required by + // EncodedPoint::from_untagged_bytes. + let public_key_bytes = jwu::decode_b64(¶ms.x) + .map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err) + })? + .into_iter() + .chain(jwu::decode_b64(¶ms.y).map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err) + })?) + .collect(); + + // The JWK contains the uncompressed x and y coordinates, so we can create the + // encoded point directly without prefixing an SEC1 tag. + let encoded_point: EncodedPoint = EncodedPoint::from_untagged_bytes(&public_key_bytes); + let public_key: PublicKey = { + let opt_public_key: CtOption = PublicKey::from_encoded_point(&encoded_point); + if opt_public_key.is_none().into() { + return Err(SignatureVerificationError::new( + SignatureVerificationErrorKind::KeyDecodingFailure, + )); + } else { + opt_public_key.unwrap() + } + }; + + let verifying_key: VerifyingKey = VerifyingKey::from(public_key); + + let signature: Signature = Signature::try_from(input.decoded_signature.deref()).map_err(|err| { + SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err) + })?; + + match signature::Verifier::verify(&verifying_key, &input.signing_input, &signature) { + Ok(()) => Ok(()), + Err(err) => { + Err(SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err)) + } + } + } +} diff --git a/identity_ecdsa_verifier/src/tests/mod.rs b/identity_ecdsa_verifier/src/tests/mod.rs new file mode 100644 index 0000000000..63e508fa33 --- /dev/null +++ b/identity_ecdsa_verifier/src/tests/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +mod secp256; +mod secp256k; diff --git a/identity_ecdsa_verifier/src/tests/secp256.rs b/identity_ecdsa_verifier/src/tests/secp256.rs new file mode 100644 index 0000000000..c6700a85e4 --- /dev/null +++ b/identity_ecdsa_verifier/src/tests/secp256.rs @@ -0,0 +1,77 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +mod es256 { + use identity_verification::jwk::EcCurve; + use identity_verification::jwk::Jwk; + use identity_verification::jwk::JwkParamsEc; + use identity_verification::jwu; + use p256::ecdsa::Signature; + use p256::ecdsa::SigningKey; + use p256::SecretKey; + + pub(crate) fn expand_p256_jwk(jwk: &Jwk) -> SecretKey { + let params: &JwkParamsEc = jwk.try_ec_params().unwrap(); + + if params.try_ec_curve().unwrap() != EcCurve::P256 { + panic!("expected a P256 curve"); + } + + let sk_bytes = params.d.as_ref().map(jwu::decode_b64).unwrap().unwrap(); + SecretKey::from_slice(&sk_bytes).unwrap() + } + + pub(crate) fn sign(message: &[u8], private_key: &Jwk) -> impl AsRef<[u8]> { + let sk: SecretKey = expand_p256_jwk(private_key); + let signing_key: SigningKey = SigningKey::from(sk); + let signature: Signature = signature::Signer::sign(&signing_key, message); + signature.to_bytes() + } +} + +use identity_verification::jwk::Jwk; +use identity_verification::jws; +use identity_verification::jws::JwsHeader; + +use crate::EcDSAJwsVerifier; + +#[test] +fn test_es256_rfc7515() { + // Test Vector taken from https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.3. + let tv_header: &str = r#"{"alg":"ES256"}"#; + let tv_claims: &[u8] = &[ + 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, + 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, + 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125, + ]; + let tv_encoded: &[u8] = b"eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.e4ZrhZdbFQ7630Tq51E6RQiJaae9bFNGJszIhtusEwzvO21rzH76Wer6yRn2Zb34VjIm3cVRl0iQctbf4uBY3w"; + let tv_private_key: &str = r#" + { + "kty": "EC", + "crv": "P-256", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "d": "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" + } + "#; + + let header: JwsHeader = serde_json::from_str(tv_header).unwrap(); + let jwk: Jwk = serde_json::from_str(tv_private_key).unwrap(); + let encoder: jws::CompactJwsEncoder<'_> = jws::CompactJwsEncoder::new(tv_claims, &header).unwrap(); + let signing_input: &[u8] = encoder.signing_input(); + let encoded: String = { + let signature = es256::sign(signing_input, &jwk); + encoder.into_jws(signature.as_ref()) + }; + assert_eq!(encoded.as_bytes(), tv_encoded); + + let jws_verifier = EcDSAJwsVerifier::default(); + let decoder = jws::Decoder::new(); + let token = decoder + .decode_compact_serialization(tv_encoded, None) + .and_then(|decoded| decoded.verify(&jws_verifier, &jwk)) + .unwrap(); + + assert_eq!(token.protected, header); + assert_eq!(token.claims, tv_claims); +} diff --git a/identity_ecdsa_verifier/src/tests/secp256k.rs b/identity_ecdsa_verifier/src/tests/secp256k.rs new file mode 100644 index 0000000000..49f234a3c7 --- /dev/null +++ b/identity_ecdsa_verifier/src/tests/secp256k.rs @@ -0,0 +1,112 @@ +// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH +// SPDX-License-Identifier: Apache-2.0 + +mod es256k1 { + use identity_verification::jwk::EcCurve; + use identity_verification::jwk::Jwk; + use identity_verification::jwk::JwkParamsEc; + use identity_verification::jwu; + use k256::ecdsa::Signature; + use k256::ecdsa::SigningKey; + use k256::SecretKey; + + pub(crate) fn expand_k256_jwk(jwk: &Jwk) -> SecretKey { + let params: &JwkParamsEc = jwk.try_ec_params().unwrap(); + + if params.try_ec_curve().unwrap() != EcCurve::Secp256K1 { + panic!("expected a Secp256K1 curve"); + } + + let sk_bytes = params.d.as_ref().map(jwu::decode_b64).unwrap().unwrap(); + SecretKey::from_slice(&sk_bytes).unwrap() + } + + pub(crate) fn sign(message: &[u8], private_key: &Jwk) -> impl AsRef<[u8]> { + let sk: SecretKey = expand_k256_jwk(private_key); + let signing_key: SigningKey = SigningKey::from(sk); + let signature: Signature = signature::Signer::sign(&signing_key, message); + signature.to_bytes() + } +} + +use identity_verification::jwk::Jwk; +use identity_verification::jws; +use identity_verification::jws::JwsHeader; + +use crate::EcDSAJwsVerifier; + +#[test] +fn test_es256k_verifier() { + let tv_header: &str = r#"{ + "typ": "JWT", + "alg":"ES256K" + }"#; + let tv_private_key: &str = r#" + { + "kty":"EC", + "crv":"secp256k1", + "d":"y0zUV7bLeUG_kDOvACFHnSmtH7j8MSJek25R2wJbWWg", + "x":"BBobbZkiC8E4C4EYekPNJkcXFCsMNHhh0AV2USy_xSs", + "y":"VQcPHjIQClX0b5TLluFl6jpIf9U-norWC0oEvIQRNyU" + }"#; + let tv_claims: &[u8] = br#"{"key":"value"}"#; + + let header: JwsHeader = serde_json::from_str(tv_header).unwrap(); + let jwk: Jwk = serde_json::from_str(tv_private_key).unwrap(); + let encoder: jws::CompactJwsEncoder<'_> = jws::CompactJwsEncoder::new(tv_claims, &header).unwrap(); + let signing_input: &[u8] = encoder.signing_input(); + let encoded: String = { + let signature = es256k1::sign(signing_input, &jwk); + encoder.into_jws(signature.as_ref()) + }; + + let jws_verifier = EcDSAJwsVerifier::default(); + let jwk: Jwk = serde_json::from_str(tv_private_key).unwrap(); + let decoder = jws::Decoder::new(); + assert!(decoder + .decode_compact_serialization(encoded.as_bytes(), None) + .and_then(|decoded| decoded.verify(&jws_verifier, &jwk)) + .is_ok()); +} + +/// In the absence of official test vectors for secp256k1, +/// this ensures we can verify JWTs created by other libraries. +mod test_es256k_josekit { + use identity_verification::jws; + use josekit::jwk::alg::ec::EcKeyPair; + use josekit::jwk::Jwk; + use josekit::jws::JwsHeader; + use josekit::jwt::JwtPayload; + + use crate::EcDSAJwsVerifier; + + #[test] + fn test_es256k_josekit() { + let alg = josekit::jws::ES256K; + + let private_key: &str = r#" + { + "kty":"EC", + "crv":"secp256k1", + "d":"y0zUV7bLeUG_kDOvACFHnSmtH7j8MSJek25R2wJbWWg", + "x":"BBobbZkiC8E4C4EYekPNJkcXFCsMNHhh0AV2USy_xSs", + "y":"VQcPHjIQClX0b5TLluFl6jpIf9U-norWC0oEvIQRNyU" + }"#; + let josekit_jwk: Jwk = serde_json::from_str(private_key).unwrap(); + let mut src_header = JwsHeader::new(); + src_header.set_token_type("JWT"); + let mut src_payload = JwtPayload::new(); + src_payload.set_claim("key", Some("value".into())).unwrap(); + let eckp = EcKeyPair::from_jwk(&josekit_jwk).unwrap(); + let signer = alg.signer_from_jwk(&eckp.to_jwk_key_pair()).unwrap(); + let jwt_string = josekit::jwt::encode_with_signer(&src_payload, &src_header, &signer).unwrap(); + + let jws_verifier = EcDSAJwsVerifier::default(); + let decoder = jws::Decoder::new(); + let jwk: identity_verification::jwk::Jwk = serde_json::from_str(private_key).unwrap(); + assert!(decoder + .decode_compact_serialization(jwt_string.as_bytes(), None) + .and_then(|decoded| decoded.verify(&jws_verifier, &jwk)) + .is_ok()); + } +} diff --git a/identity_iota_core/src/document/test_utils.rs b/identity_iota_core/src/document/test_utils.rs index b8c48cadf4..b45d418751 100644 --- a/identity_iota_core/src/document/test_utils.rs +++ b/identity_iota_core/src/document/test_utils.rs @@ -24,7 +24,7 @@ fn encode_public_ed25519_jwk(public_key: &[u8]) -> Jwk { let mut params = JwkParamsOkp::new(); params.x = x; params.d = None; - params.crv = EdCurve::Ed25519.name().to_owned(); + params.crv = EdCurve::Ed25519.name().to_string(); let mut jwk = Jwk::from_params(params); jwk.set_alg(JwsAlgorithm::EdDSA.name()); jwk diff --git a/identity_storage/src/key_storage/ed25519.rs b/identity_storage/src/key_storage/ed25519.rs index 8e807af6c9..619493c35d 100644 --- a/identity_storage/src/key_storage/ed25519.rs +++ b/identity_storage/src/key_storage/ed25519.rs @@ -53,6 +53,6 @@ pub(crate) fn encode_jwk(private_key: &SecretKey, public_key: &crypto::signature let mut params = JwkParamsOkp::new(); params.x = x; params.d = Some(d); - EdCurve::Ed25519.name().clone_into(&mut params.crv); + params.crv = EdCurve::Ed25519.name().to_string(); Jwk::from_params(params) } diff --git a/identity_storage/src/key_storage/memstore.rs b/identity_storage/src/key_storage/memstore.rs index 203ced2e64..9bf4e6ea9a 100644 --- a/identity_storage/src/key_storage/memstore.rs +++ b/identity_storage/src/key_storage/memstore.rs @@ -513,10 +513,10 @@ mod tests { let store: JwkMemStore = JwkMemStore::new(); let mut ec_params = JwkParamsEc::new(); - ec_params.crv = EcCurve::P256.name().to_owned(); - ec_params.x = "".to_owned(); - ec_params.y = "".to_owned(); - ec_params.d = Some("".to_owned()); + ec_params.crv = EcCurve::P256.name().to_string(); + ec_params.x = String::new(); + ec_params.y = String::new(); + ec_params.d = Some(String::new()); let jwk_ec = Jwk::from_params(ec_params); let err = store.insert(jwk_ec).await.unwrap_err(); diff --git a/identity_storage/src/key_storage/tests/utils.rs b/identity_storage/src/key_storage/tests/utils.rs index 379df562b4..b5ca210301 100644 --- a/identity_storage/src/key_storage/tests/utils.rs +++ b/identity_storage/src/key_storage/tests/utils.rs @@ -45,10 +45,10 @@ pub(crate) async fn test_incompatible_key_alg(store: impl JwkStorage) { pub(crate) async fn test_incompatible_key_type(store: impl JwkStorage) { let mut ec_params = JwkParamsEc::new(); - ec_params.crv = EcCurve::P256.name().to_owned(); - ec_params.x = "".to_owned(); - ec_params.y = "".to_owned(); - ec_params.d = Some("".to_owned()); + ec_params.crv = EcCurve::P256.name().to_string(); + ec_params.x = String::new(); + ec_params.y = String::new(); + ec_params.d = Some(String::new()); let jwk_ec = Jwk::from_params(ec_params); let err = store.insert(jwk_ec).await.unwrap_err(); diff --git a/identity_storage/src/storage/tests/test_utils.rs b/identity_storage/src/storage/tests/test_utils.rs index ebc0660147..77b1a92072 100644 --- a/identity_storage/src/storage/tests/test_utils.rs +++ b/identity_storage/src/storage/tests/test_utils.rs @@ -192,7 +192,7 @@ pub(crate) fn encode_public_ed25519_jwk(public_key: &PublicKey) -> Jwk { let mut params = JwkParamsOkp::new(); params.x = x; params.d = None; - params.crv = EdCurve::Ed25519.name().to_owned(); + params.crv = EdCurve::Ed25519.name().to_string(); let mut jwk = Jwk::from_params(params); jwk.set_alg(JwsAlgorithm::EdDSA.name()); jwk diff --git a/identity_stronghold/src/ed25519.rs b/identity_stronghold/src/ed25519.rs index 13c3135bb0..933983cdfc 100644 --- a/identity_stronghold/src/ed25519.rs +++ b/identity_stronghold/src/ed25519.rs @@ -53,6 +53,6 @@ pub(crate) fn encode_jwk(private_key: &SecretKey, public_key: &crypto::signature let mut params = JwkParamsOkp::new(); params.x = x; params.d = Some(d); - params.crv = EdCurve::Ed25519.name().to_owned(); + params.crv = EdCurve::Ed25519.name().to_string(); Jwk::from_params(params) } diff --git a/identity_stronghold/src/storage/stronghold_jwk_storage.rs b/identity_stronghold/src/storage/stronghold_jwk_storage.rs index 63535293b1..b0400c8f65 100644 --- a/identity_stronghold/src/storage/stronghold_jwk_storage.rs +++ b/identity_stronghold/src/storage/stronghold_jwk_storage.rs @@ -85,7 +85,7 @@ impl JwkStorage for StrongholdStorage { let mut params = JwkParamsOkp::new(); params.x = jwu::encode_b64(public_key); - EdCurve::Ed25519.name().clone_into(&mut params.crv); + params.crv = EdCurve::Ed25519.name().to_string(); let mut jwk: Jwk = Jwk::from_params(params); jwk.set_alg(alg.name()); jwk.set_kid(jwk.thumbprint_sha256_b64()); diff --git a/identity_stronghold/src/tests/test_jwk_storage.rs b/identity_stronghold/src/tests/test_jwk_storage.rs index ca775c95aa..e7ccbb2a05 100644 --- a/identity_stronghold/src/tests/test_jwk_storage.rs +++ b/identity_stronghold/src/tests/test_jwk_storage.rs @@ -171,10 +171,10 @@ mod jwk_storage_tests { pub(crate) async fn test_incompatible_key_type(store: impl JwkStorage) { let mut ec_params = JwkParamsEc::new(); - ec_params.crv = EcCurve::P256.name().to_owned(); - ec_params.x = "".to_owned(); - ec_params.y = "".to_owned(); - ec_params.d = Some("".to_owned()); + ec_params.crv = EcCurve::P256.name().to_string(); + ec_params.x = String::new(); + ec_params.y = String::new(); + ec_params.d = Some(String::new()); let jwk_ec = Jwk::from_params(ec_params); let err = store.insert(jwk_ec).await.unwrap_err(); diff --git a/identity_stronghold/src/tests/utils.rs b/identity_stronghold/src/tests/utils.rs index 9fec954f0f..5113c95f28 100644 --- a/identity_stronghold/src/tests/utils.rs +++ b/identity_stronghold/src/tests/utils.rs @@ -28,7 +28,7 @@ pub(crate) fn encode_public_ed25519_jwk(public_key: &PublicKey) -> Jwk { let mut params = JwkParamsOkp::new(); params.x = x; params.d = None; - params.crv = EdCurve::Ed25519.name().to_owned(); + params.crv = EdCurve::Ed25519.name().to_string(); let mut jwk = Jwk::from_params(params); jwk.set_alg(JwsAlgorithm::EdDSA.name()); jwk