Skip to content

Commit

Permalink
Merge pull request #114 from alan-turing-institute/56-http-verifier
Browse files Browse the repository at this point in the history
Verifier for http crate and https support (#56)
  • Loading branch information
sgreenbury authored Oct 6, 2023
2 parents d523744 + b3df12b commit e4e836b
Show file tree
Hide file tree
Showing 14 changed files with 687 additions and 160 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ members = [
"trustchain-cli",
"trustchain-ffi"
]
resolver = "2"
6 changes: 3 additions & 3 deletions trustchain-core/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,13 @@ pub enum VerifierError {
#[error("A commitment error during verification: {0}")]
CommitmentFailure(CommitmentError),
/// Wrapped resolver error.
#[error("A resolver error during verification.")]
#[error("A resolver error during verification: {0}")]
ResolverFailure(ResolverError),
/// Wrapped chain error.
#[error("A chain error during verification.")]
#[error("A chain error during verification: {0}")]
ChainFailure(ChainError),
/// Wrapped serde JSON deserialization error.
#[error("Failed to deserialize.")]
#[error("Failed to deserialize: {0}")]
FailedToDeserialize(serde_json::Error),
}

Expand Down
3 changes: 3 additions & 0 deletions trustchain-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ path = "src/bin/main.rs"
[dependencies]
trustchain-core = { path = "../trustchain-core" }
trustchain-ion = { path = "../trustchain-ion" }
trustchain-api = { path = "../trustchain-api" }
async-trait = "0.1"
axum = "0.6"
axum-server = {version="0.5.1", features = ["tls-rustls"] }
base64 = "0.21.0"
chrono = "^0.4"
clap = { version = "^4", features=["derive", "env", "cargo"] }
Expand All @@ -30,6 +32,7 @@ reqwest = "0.11.16"
serde = { version = "1.0", features = ["derive"] }
serde_jcs = "0.1.0"
serde_json = "1.0"
shellexpand = "3.1.0"
ssi = {git="https://github.com/alan-turing-institute/ssi.git", branch="modify-encode-sign-jwt", features = ["http-did", "secp256k1"]}
thiserror="1.0"
tokio = {version = "1.20.1", features = ["full"]}
Expand Down
13 changes: 6 additions & 7 deletions trustchain-http/src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use log::info;
use trustchain_http::config::HTTP_CONFIG;
use trustchain_http::config::{http_config, HTTP_CONFIG};
use trustchain_http::server;

#[tokio::main]
Expand All @@ -12,13 +12,12 @@ async fn main() -> std::io::Result<()> {

// Print config
info!("{}", config);
let addr = config.to_address();

// Init server
server::server(config).await.unwrap();

// Logging
tracing::debug!("listening on {}", addr);
// Run server
match http_config().https {
false => server::http_server(config).await.unwrap(),
true => server::https_server(config).await.unwrap(),
}

Ok(())
}
25 changes: 18 additions & 7 deletions trustchain-http/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,29 @@ use std::{
str::FromStr,
};
use toml;
use trustchain_core::verifier::Timestamp;
use trustchain_core::TRUSTCHAIN_CONFIG;

const DEFAULT_HOST: &str = "127.0.0.1";
const DEFAULT_PORT: u16 = 8081;

/// Server config.
/// HTTP configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct HTTPConfig {
/// Hostname for server
pub host: IpAddr,
/// Hostname reference. For example, Android emulator 10.0.2.2 refers to 127.0.0.1 of machine running emulator.
pub host_reference: IpAddr,
pub host_display: String,
/// Port for server
pub port: u16,
/// Optional issuer DID
pub issuer_did: Option<String>,
/// Flag indicating whether server uses https
pub https: bool,
/// Path containing certificate and key necessary for https
pub https_path: Option<String>,
/// Root event time for verifier.
pub root_event_time: Option<Timestamp>,
}

impl std::fmt::Display for HTTPConfig {
Expand All @@ -34,9 +41,12 @@ impl Default for HTTPConfig {
fn default() -> Self {
Self {
host: IpAddr::from_str(DEFAULT_HOST).unwrap(),
host_reference: IpAddr::from_str(DEFAULT_HOST).unwrap(),
host_display: DEFAULT_HOST.to_string(),
port: DEFAULT_PORT,
issuer_did: None,
https: false,
https_path: None,
root_event_time: None,
}
}
}
Expand Down Expand Up @@ -73,7 +83,7 @@ pub fn http_config() -> &'static HTTP_CONFIG {
&HTTP_CONFIG
}

/// Wrapper struct for parsing the `http` table.
/// Wrapper struct for parsing the `http` config table.
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Config {
/// HTTP configuration data.
Expand All @@ -86,16 +96,17 @@ mod tests {

#[test]
fn test_deserialize() {
let config_string = r##"
let config_string = r#"
[http]
host = "127.0.0.1"
host_reference = "127.0.0.1"
host_display = "127.0.0.1"
port = 8081
issuer_did = "did:ion:test:EiBcLZcELCKKtmun_CUImSlb2wcxK5eM8YXSq3MrqNe5wA"
https = false
[non_http]
key = "value"
"##;
"#;

let config: HTTPConfig = parse_toml(config_string);

Expand Down
52 changes: 46 additions & 6 deletions trustchain-http/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use hyper::StatusCode;
use serde_json::json;
use thiserror::Error;
use trustchain_core::{
commitment::CommitmentError, issuer::IssuerError, resolver::ResolverError,
verifier::VerifierError,
commitment::CommitmentError, issuer::IssuerError, resolver::ResolverError, vc::CredentialError,
verifier::VerifierError, vp::PresentationError,
};

// TODO: refine and add doc comments for error variants
Expand All @@ -20,10 +20,22 @@ pub enum TrustchainHTTPError {
ResolverError(ResolverError),
#[error("Trustchain issuer error: {0}")]
IssuerError(IssuerError),
#[error("Trustchain presentation error: {0}")]
PresentationError(PresentationError),
#[error("Credential does not exist.")]
CredentialDoesNotExist,
#[error("No issuer available.")]
NoCredentialIssuer,
#[error("Failed to verify credential.")]
FailedToVerifyCredential,
#[error("Invalid signature.")]
InvalidSignature,
#[error("Request does not exist.")]
RequestDoesNotExist,
#[error("Could not deserialize data: {0}")]
FailedToDeserialize(serde_json::Error),
#[error("Root event time not configured for verification.")]
RootEventTimeNotSet,
}

impl From<ResolverError> for TrustchainHTTPError {
Expand All @@ -49,6 +61,12 @@ impl From<IssuerError> for TrustchainHTTPError {
}
}

impl From<PresentationError> for TrustchainHTTPError {
fn from(err: PresentationError) -> Self {
TrustchainHTTPError::PresentationError(err)
}
}

// See axum IntoRespone example:
// https://github.com/tokio-rs/axum/blob/main/examples/jwt/src/main.rs#L147-L160

Expand All @@ -59,10 +77,8 @@ impl IntoResponse for TrustchainHTTPError {
err @ TrustchainHTTPError::InternalError => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::VerifierError(VerifierError::InvalidRoot(_)) => {
(StatusCode::OK, err.to_string())
}
err @ TrustchainHTTPError::VerifierError(VerifierError::CommitmentFailure(_)) => {
err @ TrustchainHTTPError::VerifierError(VerifierError::InvalidRoot(_))
| err @ TrustchainHTTPError::VerifierError(VerifierError::CommitmentFailure(_)) => {
(StatusCode::OK, err.to_string())
}
err @ TrustchainHTTPError::VerifierError(_) => {
Expand All @@ -77,12 +93,36 @@ impl IntoResponse for TrustchainHTTPError {
err @ TrustchainHTTPError::ResolverError(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::PresentationError(PresentationError::CredentialError(
CredentialError::VerifierError(VerifierError::CommitmentFailure(_)),
))
| err @ TrustchainHTTPError::PresentationError(PresentationError::CredentialError(
CredentialError::VerifierError(VerifierError::InvalidRoot(_)),
)) => (StatusCode::OK, err.to_string()),
err @ TrustchainHTTPError::PresentationError(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::CredentialDoesNotExist => {
(StatusCode::BAD_REQUEST, err.to_string())
}
err @ TrustchainHTTPError::NoCredentialIssuer => {
(StatusCode::BAD_REQUEST, err.to_string())
}
err @ TrustchainHTTPError::FailedToVerifyCredential => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::InvalidSignature => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::RequestDoesNotExist => {
(StatusCode::BAD_REQUEST, err.to_string())
}
err @ TrustchainHTTPError::FailedToDeserialize(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
err @ TrustchainHTTPError::RootEventTimeNotSet => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
};
let body = Json(json!({ "error": err_message }));
(status, body).into_response()
Expand Down
21 changes: 15 additions & 6 deletions trustchain-http/src/issuer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config::http_config;
use crate::errors::TrustchainHTTPError;
use crate::qrcode::str_to_qr_code_html;
use crate::state::AppState;
Expand Down Expand Up @@ -123,13 +124,19 @@ impl TrustchainIssuerHTTP for TrustchainIssuerHTTPHandler {

impl TrustchainIssuerHTTPHandler {
/// Generates QR code to display to holder to receive requested credential.
pub async fn get_issuer_qrcode(State(app_state): State<Arc<AppState>>) -> Html<String> {
// TODO: update to take query param entered by user.
let id = "7426a2e8-f932-11ed-968a-4bb02079f142".to_string();
pub async fn get_issuer_qrcode(
State(app_state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Html<String> {
let http_str = if !http_config().https {
"http"
} else {
"https"
};
// Generate a QR code for server address and combination of name and UUID
let address_str = format!(
"http://{}:{}/vc/issuer/{id}",
app_state.config.host_reference, app_state.config.port
"{}://{}:{}/vc/issuer/{id}",
http_str, app_state.config.host_display, app_state.config.port
);
// Respond with the QR code as a png embedded in html
Html(str_to_qr_code_html(&address_str, "Issuer"))
Expand Down Expand Up @@ -203,7 +210,7 @@ mod tests {
one_or_many::OneOrMany,
vc::{Credential, CredentialSubject, Issuer, URI},
};
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use trustchain_core::{utils::canonicalize, verifier::Verifier};
use trustchain_ion::{get_ion_resolver, verifier::IONVerifier};

Expand Down Expand Up @@ -245,6 +252,7 @@ mod tests {
let state = Arc::new(AppState::new_with_cache(
TEST_HTTP_CONFIG.to_owned(),
serde_json::from_str(CREDENTIALS).unwrap(),
HashMap::new(),
));
let app = TrustchainRouter::from(state.clone()).into_router();
// Get offer for valid credential
Expand Down Expand Up @@ -289,6 +297,7 @@ mod tests {
let app = TrustchainRouter::from(Arc::new(AppState::new_with_cache(
TEST_HTTP_CONFIG.to_owned(),
serde_json::from_str(CREDENTIALS).unwrap(),
HashMap::new(),
)))
.into_router();
let uid = "46cb84e2-fa10-11ed-a0d4-bbb4e61d1556".to_string();
Expand Down
21 changes: 0 additions & 21 deletions trustchain-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,3 @@ pub mod server;
pub mod state;
pub mod static_handlers;
pub mod verifier;

/// Example VP request.
pub const EXAMPLE_VP_REQUEST: &str = r#"{
"type": "VerifiablePresentationRequest",
"query": [
{
"type": "QueryByExample",
"credentialQuery": {
"reason": "Request credential",
"example": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": "VerifiableCredential"
}
}
}
],
"challenge": "a877fb0a-11dd-11ee-9df7-9be7abdeee2d",
"domain": "https://alan-turing-institute.github.io/trustchain"
}"#;
Loading

0 comments on commit e4e836b

Please sign in to comment.