Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/ecdsa-verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
wulfraem committed May 13, 2024
2 parents 719a524 + 149bfac commit a9c3997
Show file tree
Hide file tree
Showing 26 changed files with 254 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .github/actions/iota-sandbox/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ runs:
# Start Tangle
sudo ./bootstrap.sh
docker compose up -d
docker compose --profile inx-faucet up -d
- name: Wait for tangle to start
shell: bash
run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/$WAIT_FOR_VERSION/wait-for | sh -s -- -t 60 http://localhost/health -- echo "Tangle is up"
Expand Down
19 changes: 17 additions & 2 deletions .github/actions/rust/rust-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/grpc-publish-to-dockerhub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}

26 changes: 13 additions & 13 deletions bindings/grpc/README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions bindings/grpc/proto/utils.proto
Original file line number Diff line number Diff line change
@@ -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);
}

22 changes: 19 additions & 3 deletions bindings/grpc/src/main.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -29,11 +30,18 @@ async fn main() -> anyhow::Result<()> {

#[tracing::instrument]
fn init_stronghold() -> anyhow::Result<StrongholdStorage> {
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));
}
Expand All @@ -45,3 +53,11 @@ fn init_stronghold() -> anyhow::Result<StrongholdStorage> {
.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
}
2 changes: 2 additions & 0 deletions bindings/grpc/src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()
}
67 changes: 67 additions & 0 deletions bindings/grpc/src/services/utils.rs
Original file line number Diff line number Diff line change
@@ -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<Error> 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<DataSigningRequest>) -> Result<Response<DataSigningResponse>, 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<SigningService> {
SigningServer::new(SigningService::new(stronghold))
}
1 change: 1 addition & 0 deletions bindings/grpc/tests/api/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ mod helpers;
mod jwt;
mod sd_jwt_validation;
mod status_list_2021;
mod utils;
48 changes: 48 additions & 0 deletions bindings/grpc/tests/api/utils.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
5 changes: 5 additions & 0 deletions bindings/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,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"
4 changes: 2 additions & 2 deletions identity_credential/src/credential/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ impl StatusList2021CredentialSubject {
return Err(StatusList2021CredentialError::MultipleCredentialSubject);
};
if let Some(subject_type) = subject.properties.get("type") {
if !subject_type.as_str().is_some_and(|t| t == CREDENTIAL_SUBJECT_TYPE) {
if subject_type.as_str() != Some(CREDENTIAL_SUBJECT_TYPE) {
return Err(StatusList2021CredentialError::InvalidProperty("credentialSubject.type"));
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion identity_credential/src/validator/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion identity_iota_core/src/document/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit a9c3997

Please sign in to comment.