-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
45 changed files
with
3,144 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
target/ | ||
bindings/wasm/ | ||
bindings/grpc/target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
name: Build and run grpc tests | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
types: [ opened, synchronize, reopened, ready_for_review ] | ||
branches: | ||
- main | ||
- 'epic/**' | ||
- 'support/**' | ||
paths: | ||
- '.github/workflows/build-and-test.yml' | ||
- '.github/actions/**' | ||
- '**.rs' | ||
- '**.toml' | ||
- 'bindings/grpc/**' | ||
|
||
jobs: | ||
check-for-run-condition: | ||
runs-on: ubuntu-latest | ||
outputs: | ||
should-run: ${{ !github.event.pull_request || github.event.pull_request.draft == false }} | ||
steps: | ||
- run: | | ||
# this run step does nothing, but is needed to get the job output | ||
build-and-test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out the repo | ||
uses: actions/checkout@v4 | ||
|
||
- name: Build Docker image | ||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 | ||
with: | ||
context: . | ||
file: bindings/grpc/Dockerfile | ||
push: false | ||
labels: iotaledger/identity-grpc:latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
name: gRPC publish to dockerhub | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
tag: | ||
description: 'Tag to publish under, defaults to latest' | ||
required: false | ||
default: latest | ||
branch: | ||
description: 'Branch to run publish from' | ||
required: true | ||
dry-run: | ||
description: 'Run in dry-run mode' | ||
type: boolean | ||
required: false | ||
default: true | ||
|
||
jobs: | ||
push_to_registry: | ||
environment: release | ||
name: Push Docker image to Docker Hub | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out the repo | ||
uses: actions/checkout@v4 | ||
with: | ||
ref: ${{ github.event.inputs.branch }} | ||
|
||
- name: Log in to Docker Hub | ||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a | ||
with: | ||
username: ${{ secrets.IOTALEDGER_DOCKER_USERNAME }} | ||
password: ${{ secrets.IOTALEDGER_DOCKER_PASSWORD }} | ||
|
||
- name: Build and push Docker image | ||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 | ||
with: | ||
context: . | ||
file: bindings/grpc/Dockerfile | ||
push: ${{ !inputs.dry-run }} | ||
labels: iotaledger/identity-grpc:${{ inputs.tag }} | ||
|
||
- name: Docker Hub Description | ||
uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae | ||
with: | ||
username: ${{ secrets.IOTALEDGER_DOCKER_USERNAME }} | ||
password: ${{ secrets.IOTALEDGER_DOCKER_PASSWORD }} | ||
repository: iotaledger/identity-grpc | ||
readme-filepath: ./bindigns/grpc/README.md | ||
short-description: ${{ github.event.repository.description }} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
[package] | ||
name = "identity-grpc" | ||
version = "0.1.0" | ||
authors = ["IOTA Stiftung"] | ||
edition = "2021" | ||
homepage = "https://www.iota.org" | ||
license = "Apache-2.0" | ||
repository = "https://github.com/iotaledger/identity.rs" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[lib] | ||
path = "src/lib.rs" | ||
|
||
[[bin]] | ||
name = "identity-grpc" | ||
path = "src/main.rs" | ||
|
||
[dependencies] | ||
anyhow = "1.0.75" | ||
futures = { version = "0.3" } | ||
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier" } | ||
identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch", "status-list-2021"] } | ||
identity_stronghold = { path = "../../identity_stronghold", features = ["send-sync-storage"] } | ||
iota-sdk = { version = "1.1.2", features = ["stronghold"] } | ||
openssl = { version = "0.10", features = ["vendored"] } | ||
prost = "0.12" | ||
rand = "0.8.5" | ||
serde = { version = "1.0.193", features = ["derive", "alloc"] } | ||
serde_json = { version = "1.0.108", features = ["alloc"] } | ||
thiserror = "1.0.50" | ||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } | ||
tokio-stream = { version = "0.1.14", features = ["net"] } | ||
tonic = "0.10" | ||
tracing = { version = "0.1.40", features = ["async-await"] } | ||
tracing-subscriber = "0.3.18" | ||
url = { version = "2.5", default-features = false } | ||
|
||
[dev-dependencies] | ||
identity_storage = { path = "../../identity_storage", features = ["memstore"] } | ||
|
||
[build-dependencies] | ||
tonic-build = "0.10" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
FROM rust:bookworm as builder | ||
|
||
# install protobuf | ||
RUN apt-get update && apt-get install -y protobuf-compiler libprotobuf-dev musl-tools | ||
|
||
COPY . /usr/src/app/ | ||
WORKDIR /usr/src/app/bindings/grpc | ||
RUN rustup target add x86_64-unknown-linux-musl | ||
RUN cargo build --target x86_64-unknown-linux-musl --release --bin identity-grpc | ||
|
||
FROM gcr.io/distroless/static-debian11 as runner | ||
|
||
# get binary | ||
COPY --from=builder /usr/src/app/bindings/grpc/target/x86_64-unknown-linux-musl/release/identity-grpc / | ||
|
||
# set run env | ||
EXPOSE 50051 | ||
|
||
# run it | ||
CMD ["/identity-grpc"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
# 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). | ||
|
||
## Build | ||
Run `docker build -f bindings/grpc/Dockerfile -t iotaleger/identity-grpc .` from the project root. | ||
|
||
### Dockerimage env variables and volume binds | ||
The provided docker image requires the following variables to be set in order to properly work: | ||
- `API_ENDPOINT`: IOTA node address. | ||
- `STRONGHOLD_PWD`: Stronghold password. | ||
- `SNAPSHOT_PATH`: Stronghold's snapshot location. | ||
|
||
Make sure to provide a valid stronghold snapshot at the provided `SNAPSHOT_PATH` prefilled with all the needed key material. | ||
|
||
### 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) | | ||
|
||
## Testing | ||
|
||
### Domain Linkage | ||
|
||
#### Http server | ||
In order to test domain linkage, you need access to a server that is reachable via HTTPS. If you already have one, you can ignore the server setup steps here and and provide the `did-configuration.json` on your server. | ||
|
||
1. create a folder with did configuration in it, e.g. (you can also use the template in `./tooling/domain-linkage-test-server`) | ||
```raw | ||
test-server/ | ||
└── .well-known | ||
└── did-configuration.json | ||
``` | ||
|
||
the `did-configuration` should look like this for now: | ||
|
||
```json | ||
{ | ||
"@context": "https://identity.foundation/.well-known/did-configuration/v1", | ||
"linked_dids": [ | ||
"add your domain linkage credential here" | ||
] | ||
} | ||
``` | ||
1. start a server that will serve this folder, e.g. with a NodeJs "http-server": `http-server ./test-server/`, in this example the server should now be running on local port 8080 | ||
1. tunnel your server's port (here 8080) to a public domain with https, e.g. with ngrok: | ||
`ngrok http http://127.0.0.1:8080` | ||
the output should now have a line like | ||
`Forwarding https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app -> http://127.0.0.1:8080` | ||
check that the https url is reachable, this will be used in the next step. You can also start ngrok with a static domain, which means you don't have to update credentials after each http server restart | ||
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 run the example with `cargo run --release --example 6_domain_linkage` | ||
|
||
#### GRPC server | ||
1. grab the configuration resource from the log and replace the contents of your `did-configuration.json` with it | ||
1. you now have a publicly reachable (sub)domain, that serves a `did-configuration` file containing a credential pointing to your DID | ||
1. to verify this, run the server via Docker or with the following command, remember to replace the placeholders ;) `API_ENDPOINT=replace_me STRONGHOLD_PWD=replace_me SNAPSHOT_PATH=replace_me cargo run --release` | ||
The arguments can be taken from examples, e.g. after running a `6_domain_linkage.rs`, which also logs snapshot path passed to secret manager (`let snapshot_path = random_stronghold_path(); dbg!(&snapshot_path.to_str());`), for example | ||
- API_ENDPOINT: `"http://localhost"` | ||
- STRONGHOLD_PWD: `"secure_password"` | ||
- SNAPSHOT_PATH: `"/var/folders/41/s1sm86jx0xl4x435t81j81440000gn/T/test_strongholds/8o2Nyiv5ENBi7Ik3dEDq9gNzSrqeUdqi.stronghold"` | ||
1. for convenience, you can find a script to start the GRPC server, that you can adjust in `tooling/start-rpc-server.sh`, don't forget to insert the env variables as described above | ||
|
||
#### Calling the endpoints | ||
1. call the `validate_domain` endpoint with your domain, e.g with: | ||
|
||
```json | ||
{ | ||
"domain": "https://0d40-2003-d3-2710-e200-485f-e8bb-7431-79a7.ngrok-free.app" | ||
} | ||
``` | ||
|
||
you should now receive a response like this: | ||
|
||
```json | ||
{ | ||
"linked_dids": [ | ||
{ | ||
"document": "... (compact JWT domain linkage credential)", | ||
"status": "ok" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
1. to call the `validate_did` endpoint, you need a DID to check, you can find a testable in you domain linkage credential. for this just decode it (e.g. on jwt.io) and get the `iss` value, then you can submit as "did" like following | ||
|
||
```json | ||
{ | ||
"did": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9" | ||
} | ||
``` | ||
|
||
you should not receive a response like this: | ||
|
||
```json | ||
{ | ||
"service": [ | ||
{ | ||
"service_endpoint": [ | ||
{ | ||
"valid": true, | ||
"document": "eyJraWQiOiJkaWQ6aW90YTpzbmQ6MHg5NjdiZjhmMGM3NDg3ZjYxMzc4NjExYjZhMWM2YTU5Y2I5OWU2NWI4Mzk2ODFlZTcwYmU2OTFiMDlhMDI0YWI5IzA3QjVWRkxBa0FabkRhaC1OTnYwYUN3TzJ5ZnRzX09ZZ0YzNFNudUloMlUiLCJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJleHAiOjE3NDE2NzgyNzUsImlzcyI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJuYmYiOjE3MTAxNDIyNzUsInN1YiI6ImRpZDppb3RhOnNuZDoweDk2N2JmOGYwYzc0ODdmNjEzNzg2MTFiNmExYzZhNTljYjk5ZTY1YjgzOTY4MWVlNzBiZTY5MWIwOWEwMjRhYjkiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vaWRlbnRpdHkuZm91bmRhdGlvbi8ud2VsbC1rbm93bi9kaWQtY29uZmlndXJhdGlvbi92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiRG9tYWluTGlua2FnZUNyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsib3JpZ2luIjoiaHR0cHM6Ly9ob3QtYnVsbGRvZy1wcm9mb3VuZC5uZ3Jvay1mcmVlLmFwcC8ifX19.69e7T0DbRw9Kz7eEQ96P9E5HWbEo5F1fLuMjyQN6_Oa1lwBdbfj0wLlhS1j_d8AuNmvu60lMdLVixjMZJLQ5AA" | ||
}, | ||
{ | ||
"valid": false, | ||
"error": "domain linkage error: error sending request for url (https://bar.example.com/.well-known/did-configuration.json): error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known" | ||
} | ||
], | ||
"id": "did:iota:snd:0x967bf8f0c7487f61378611b6a1c6a59cb99e65b839681ee70be691b09a024ab9" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Which tells us that it found a DID document with one matching service with a serviceEndpoint, that contains two domains. Out of these domains one links back to the given DID, the other domain could not be resolved. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2020-2024 IOTA Stiftung | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let proto_files = std::fs::read_dir("./proto")? | ||
.filter_map(|entry| entry.ok().map(|e| e.path())) | ||
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("proto")); | ||
|
||
for proto in proto_files { | ||
tonic_build::compile_protos(proto)?; | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2020-2024 IOTA Stiftung | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
syntax = "proto3"; | ||
package credentials; | ||
|
||
// -- CREDENTIALS REVOCATION --------------------------------------------- | ||
|
||
// The States a credential can be in. | ||
enum RevocationStatus { | ||
REVOKED = 0; | ||
SUSPENDED = 1; | ||
VALID = 2; | ||
} | ||
|
||
message RevocationCheckRequest { | ||
string type = 1; | ||
string url = 2; | ||
map<string, string> properties = 3; | ||
} | ||
|
||
message RevocationCheckResponse { | ||
RevocationStatus status = 1; | ||
} | ||
|
||
service CredentialRevocation { | ||
// Checks whether a credential has been revoked with `RevocationBitmap2022`. | ||
rpc check(RevocationCheckRequest) returns (RevocationCheckResponse); | ||
} | ||
|
||
message JwtCreationRequest { | ||
string credential_json = 1; | ||
string issuer_fragment = 2; | ||
} | ||
|
||
message JwtCreationResponse { | ||
string jwt = 1; | ||
} | ||
|
||
service Jwt { | ||
// Encodes a given JSON credential into JWT, using the issuer's fragment to fetch the key from stronghold. | ||
rpc create(JwtCreationRequest) returns (JwtCreationResponse); | ||
} | ||
|
||
message VcValidationRequest { | ||
// JWT encoded credential. | ||
string credential_jwt = 1; | ||
// JSON encoded `StatusList2021Credential`, used for status checking. | ||
// If missing, status checking will be performed with `RevocationBitmap2022`. | ||
optional string status_list_credential_json = 2; | ||
} | ||
|
||
message VcValidationResponse { | ||
// JSON encoded credential (extracted from request's JWT). | ||
string credential_json = 1; | ||
} | ||
|
||
service VcValidation { | ||
// Performs encoding, syntax, signature, time constraints and status checking on the provided credential. | ||
rpc validate(VcValidationRequest) returns (VcValidationResponse); | ||
} |
Oops, something went wrong.