Skip to content

Commit

Permalink
KbSdJwt validation and fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
UMR1352 committed Jan 23, 2024
1 parent a8caa78 commit 620e5d8
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 80 deletions.
10 changes: 10 additions & 0 deletions bindings/grpc/proto/sd_jwt.proto
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
syntax = "proto3";
package sd_jwt;

message KeyBindingOptions {
optional string nonce = 1;
optional string aud = 2;
// TODO: add JWS validation options
optional string earliest_issuance_date = 3;
optional string latest_issuance_date = 4;
string holder_did = 5;
}

message VerificationRequest {
string jwt = 1;
optional KeyBindingOptions kb_options = 2;
}

message VerificationResponse {
Expand Down
3 changes: 1 addition & 2 deletions bindings/grpc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use iota_sdk::client::Client;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_endpoint = std::env::var("API_ENDPOINT")
.context("Missing environmental variable API_ENDPOINT")?;
let api_endpoint = std::env::var("API_ENDPOINT").context("Missing environmental variable API_ENDPOINT")?;

let client: Client = Client::builder()
.with_primary_node(&api_endpoint, None)?
Expand Down
3 changes: 2 additions & 1 deletion bindings/grpc/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::net::SocketAddr;

use iota_sdk::client::Client;
use tonic::transport::server::{Router, Server};
use tonic::transport::server::Router;
use tonic::transport::server::Server;

use crate::services;

Expand Down
32 changes: 20 additions & 12 deletions bindings/grpc/src/services/credential.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
use credential_verification::{
credential_revocation_server::{CredentialRevocation, CredentialRevocationServer},
RevocationCheckRequest, RevocationCheckResponse, RevocationStatus,
};
use identity_iota::{
credential::{self, JwtCredentialValidatorUtils, JwtValidationError, RevocationBitmapStatus},
prelude::{IotaDocument, Resolver},
};
use credential_verification::credential_revocation_server::CredentialRevocation;
use credential_verification::credential_revocation_server::CredentialRevocationServer;
use credential_verification::RevocationCheckRequest;
use credential_verification::RevocationCheckResponse;
use credential_verification::RevocationStatus;
use identity_iota::credential::JwtCredentialValidatorUtils;
use identity_iota::credential::JwtValidationError;
use identity_iota::credential::RevocationBitmapStatus;
use identity_iota::credential::{self};
use identity_iota::prelude::IotaDocument;
use identity_iota::prelude::Resolver;
use iota_sdk::client::Client;
use prost::bytes::Bytes;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use serde::Serialize;

use thiserror::Error;
use tonic::{self, Request, Response};
use tonic::Request;
use tonic::Response;
use tonic::{self};

mod credential_verification {
use super::RevocationCheckError;
use identity_iota::credential::{RevocationBitmapStatus, Status};
use identity_iota::credential::RevocationBitmapStatus;
use identity_iota::credential::Status;

tonic::include_proto!("credentials");

impl TryFrom<RevocationCheckRequest> for Status {
type Error = RevocationCheckError;
fn try_from(req: RevocationCheckRequest) -> Result<Self, Self::Error> {
use identity_iota::core::{Object, Url};
use identity_iota::core::Object;
use identity_iota::core::Url;

if req.r#type.as_str() != RevocationBitmapStatus::TYPE {
Err(Self::Error::UnknownRevocationType(req.r#type))
Expand Down
12 changes: 7 additions & 5 deletions bindings/grpc/src/services/health_check.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use health_check::{
health_check_server::{HealthCheck, HealthCheckServer},
HealthCheckRequest, HealthCheckResponse,
};
use tonic::{Request, Response, Status};
use health_check::health_check_server::HealthCheck;
use health_check::health_check_server::HealthCheckServer;
use health_check::HealthCheckRequest;
use health_check::HealthCheckResponse;
use tonic::Request;
use tonic::Response;
use tonic::Status;

mod health_check {
tonic::include_proto!("health_check");
Expand Down
3 changes: 2 additions & 1 deletion bindings/grpc/src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pub mod health_check;
pub mod sd_jwt;

use iota_sdk::client::Client;
use tonic::transport::server::{Routes, RoutesBuilder};
use tonic::transport::server::Routes;
use tonic::transport::server::RoutesBuilder;

pub fn routes(client: Client) -> Routes {
let mut routes = RoutesBuilder::default();
Expand Down
90 changes: 65 additions & 25 deletions bindings/grpc/src/services/sd_jwt.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
use _sd_jwt::{
verification_server::{Verification, VerificationServer},
VerificationRequest, VerificationResponse,
};
use _sd_jwt::verification_server::Verification;
use _sd_jwt::verification_server::VerificationServer;
use _sd_jwt::VerificationRequest;
use _sd_jwt::VerificationResponse;
use identity_eddsa_verifier::EdDSAJwsVerifier;
use identity_iota::{
core::{Object, ToJson},
credential::{FailFast, Jwt, JwtCredentialValidationOptions, JwtCredentialValidatorUtils, SdJwtCredentialValidator},
iota::{IotaDID, IotaDocument},
resolver::Resolver,
sd_jwt_payload::{Error as SdJwtError, SdJwt, SdObjectDecoder},
};
use identity_iota::core::Object;
use identity_iota::core::Timestamp;
use identity_iota::core::ToJson;
use identity_iota::credential::FailFast;
use identity_iota::credential::Jwt;
use identity_iota::credential::JwtCredentialValidationOptions;
use identity_iota::credential::JwtCredentialValidatorUtils;
use identity_iota::credential::KeyBindingJWTValidationOptions;
use identity_iota::credential::SdJwtCredentialValidator;
use identity_iota::iota::IotaDID;
use identity_iota::iota::IotaDocument;
use identity_iota::resolver::Resolver;
use identity_iota::sd_jwt_payload::SdJwt;
use identity_iota::sd_jwt_payload::SdObjectDecoder;
use iota_sdk::client::Client;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use serde::Serialize;
use thiserror::Error;

use self::_sd_jwt::KeyBindingOptions;

mod _sd_jwt {
tonic::include_proto!("sd_jwt");
}

impl From<KeyBindingOptions> for KeyBindingJWTValidationOptions {
fn from(value: KeyBindingOptions) -> Self {
let mut kb_options = Self::default();
kb_options.nonce = value.nonce;
kb_options.aud = value.aud;
kb_options.earliest_issuance_date = value
.earliest_issuance_date
.and_then(|t| Timestamp::parse(t.as_str()).ok());
kb_options.latest_issuance_date = value
.latest_issuance_date
.and_then(|t| Timestamp::parse(t.as_str()).ok());

kb_options
}
}

#[derive(Debug, Error, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "error", content = "reason")]
Expand All @@ -30,15 +56,12 @@ enum SdJwtVerificationError {
VerificationError(String),
#[error("Failed to resolve DID Document: {0}")]
DidResolutionError(String),
}

impl From<SdJwtError> for SdJwtVerificationError {
fn from(value: SdJwtError) -> Self {
match value {
SdJwtError::DeserializationError(e) => Self::DeserializationError(e),
_ => unreachable!(),
}
}
#[error("Missing \"kb_options\".")]
MissingKbOptions,
#[error("{0}")]
KeyBindingJwtError(String),
#[error("Provided an invalid holder's id.")]
InvalidHolderDid,
}

impl From<SdJwtVerificationError> for tonic::Status {
Expand All @@ -48,6 +71,9 @@ impl From<SdJwtVerificationError> for tonic::Status {
SdJwtVerificationError::JwtError(_) => tonic::Code::InvalidArgument,
SdJwtVerificationError::VerificationError(_) => tonic::Code::InvalidArgument,
SdJwtVerificationError::DidResolutionError(_) => tonic::Code::NotFound,
SdJwtVerificationError::MissingKbOptions => tonic::Code::InvalidArgument,
SdJwtVerificationError::KeyBindingJwtError(_) => tonic::Code::Internal,
SdJwtVerificationError::InvalidHolderDid => tonic::Code::InvalidArgument,
};
let message = value.to_string();
let error_json = serde_json::to_vec(&value).expect("plenty of memory!");
Expand Down Expand Up @@ -75,8 +101,8 @@ impl Verification for SdJwtService {
&self,
request: tonic::Request<VerificationRequest>,
) -> Result<tonic::Response<VerificationResponse>, tonic::Status> {
let VerificationRequest { jwt } = request.into_inner();
let mut sd_jwt = SdJwt::parse(&jwt).map_err(SdJwtVerificationError::from)?;
let VerificationRequest { jwt, kb_options } = request.into_inner();
let mut sd_jwt = SdJwt::parse(&jwt).map_err(|e| SdJwtVerificationError::DeserializationError(e.to_string()))?;
let jwt = Jwt::new(sd_jwt.jwt);

let issuer_did = JwtCredentialValidatorUtils::extract_issuer_from_jwt::<IotaDID>(&jwt)
Expand All @@ -99,8 +125,22 @@ impl Verification for SdJwtService {
)
.map_err(|e| SdJwtVerificationError::VerificationError(e.to_string()))?;

if let Some(kb_jwt) = sd_jwt.key_binding_jwt {
todo!();
if sd_jwt.key_binding_jwt.is_some() {
let Some(kb_options) = kb_options else {
return Err(SdJwtVerificationError::MissingKbOptions.into());
};
let holder = {
let did =
IotaDID::parse(kb_options.holder_did.as_str()).map_err(|_| SdJwtVerificationError::InvalidHolderDid)?;
self
.resolver
.resolve(&did)
.await
.map_err(|e| SdJwtVerificationError::DidResolutionError(e.to_string()))?
};
let _ = validator
.validate_key_binding_jwt(&sd_jwt, &holder, &kb_options.into())
.map_err(|e| SdJwtVerificationError::KeyBindingJwtError(e.to_string()))?;
}

Ok(tonic::Response::new(VerificationResponse {
Expand Down
18 changes: 9 additions & 9 deletions bindings/grpc/tests/api/credential_revocation_check.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use credentials::{credential_revocation_client::CredentialRevocationClient, RevocationStatus};
use identity_iota::{
credential::{self, RevocationBitmap, RevocationBitmapStatus},
did::DID,
};
use credentials::credential_revocation_client::CredentialRevocationClient;
use credentials::RevocationStatus;
use identity_iota::credential::RevocationBitmap;
use identity_iota::credential::RevocationBitmapStatus;
use identity_iota::credential::{self};
use identity_iota::did::DID;
use serde_json::json;

use crate::{
credential_revocation_check::credentials::RevocationCheckRequest,
helpers::{Entity, TestServer},
};
use crate::credential_revocation_check::credentials::RevocationCheckRequest;
use crate::helpers::Entity;
use crate::helpers::TestServer;

mod credentials {
tonic::include_proto!("credentials");
Expand Down
4 changes: 3 additions & 1 deletion bindings/grpc/tests/api/health_check.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use health_check::{health_check_client::HealthCheckClient, HealthCheckRequest, HealthCheckResponse};
use health_check::health_check_client::HealthCheckClient;
use health_check::HealthCheckRequest;
use health_check::HealthCheckResponse;

use crate::helpers::TestServer;

Expand Down
52 changes: 28 additions & 24 deletions bindings/grpc/tests/api/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
use anyhow::Context;
use identity_iota::{
iota::{IotaClientExt, IotaDocument, IotaIdentityClientExt, NetworkName},
verification::{jws::JwsAlgorithm, MethodScope},
};
use identity_storage::{key_id_storage::KeyIdMemstore, key_storage::JwkMemStore, JwkDocumentExt, Storage};
use iota_sdk::{
client::{
api::GetAddressesOptions,
node_api::indexer::query_parameters::QueryParameter,
secret::{stronghold::StrongholdSecretManager, SecretManager},
Client, Password,
},
crypto::keys::bip39,
types::block::{
address::{Address, Bech32Address, Hrp},
output::AliasOutputBuilder,
},
};
use rand::{
distributions::{Alphanumeric, DistString},
thread_rng,
};
use std::{net::SocketAddr, path::PathBuf};
use tokio::{net::TcpListener, task::JoinHandle};
use identity_iota::iota::IotaClientExt;
use identity_iota::iota::IotaDocument;
use identity_iota::iota::IotaIdentityClientExt;
use identity_iota::iota::NetworkName;
use identity_iota::verification::jws::JwsAlgorithm;
use identity_iota::verification::MethodScope;
use identity_storage::key_id_storage::KeyIdMemstore;
use identity_storage::key_storage::JwkMemStore;
use identity_storage::JwkDocumentExt;
use identity_storage::Storage;
use iota_sdk::client::api::GetAddressesOptions;
use iota_sdk::client::node_api::indexer::query_parameters::QueryParameter;
use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::client::secret::SecretManager;
use iota_sdk::client::Client;
use iota_sdk::client::Password;
use iota_sdk::crypto::keys::bip39;
use iota_sdk::types::block::address::Address;
use iota_sdk::types::block::address::Bech32Address;
use iota_sdk::types::block::address::Hrp;
use iota_sdk::types::block::output::AliasOutputBuilder;
use rand::distributions::Alphanumeric;
use rand::distributions::DistString;
use rand::thread_rng;
use std::net::SocketAddr;
use std::path::PathBuf;
use tokio::net::TcpListener;
use tokio::task::JoinHandle;
use tonic::transport::Uri;

pub type MemStorage = Storage<JwkMemStore, KeyIdMemstore>;
Expand Down

0 comments on commit 620e5d8

Please sign in to comment.