use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;
use crate::error::Error;
#[cfg(doc)]
use crate::error::{didkit_error_code, didkit_error_message};
use crate::get_verification_method;
use crate::runtime;
use crate::Source;
use crate::VerifiableCredential;
use crate::VerifiablePresentation;
use crate::DID_METHODS;
use crate::JWK;
use crate::{dereference, DereferencingInputMetadata, ResolutionInputMetadata, ResolutionResult};
use crate::{JWTOrLDPOptions, ProofFormat};
pub static VERSION_C: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
#[no_mangle]
pub extern "C" fn didkit_get_version() -> *const c_char {
VERSION_C.as_ptr() as *const c_char
}
fn ccchar_or_error(result: Result<*const c_char, Error>) -> *const c_char {
match result {
Ok(ccchar) => ccchar,
Err(error) => {
error.stash();
ptr::null()
}
}
}
fn generate_ed25519_key() -> Result<*const c_char, Error> {
let jwk = JWK::generate_ed25519()?;
Ok(CString::new(serde_json::to_string(&jwk)?)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_vc_generate_ed25519_key() -> *const c_char {
ccchar_or_error(generate_ed25519_key())
}
fn key_to_did(
method_pattern_ptr: *const c_char,
key_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let method_pattern = unsafe { CStr::from_ptr(method_pattern_ptr) }.to_str()?;
let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?;
let key: JWK = serde_json::from_str(key_json)?;
let did = DID_METHODS
.generate(&Source::KeyAndPattern(&key, &method_pattern))
.ok_or(Error::UnableToGenerateDID)?;
Ok(CString::new(did)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_key_to_did(
method_pattern: *const c_char,
jwk: *const c_char,
) -> *const c_char {
ccchar_or_error(key_to_did(method_pattern, jwk))
}
fn key_to_verification_method(
method_pattern_ptr: *const c_char,
key_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let method_pattern = unsafe { CStr::from_ptr(method_pattern_ptr) }.to_str()?;
let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?;
let key: JWK = serde_json::from_str(key_json)?;
let did_method = DID_METHODS
.get(&method_pattern)
.ok_or(Error::UnknownDIDMethod)?;
let did = did_method
.generate(&Source::Key(&key))
.ok_or(Error::UnableToGenerateDID)?;
let did_resolver = did_method.to_resolver();
let rt = runtime::get()?;
let vm = rt
.block_on(get_verification_method(&did, did_resolver))
.ok_or(Error::UnableToGetVerificationMethod)?;
Ok(CString::new(vm)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_key_to_verification_method(
method_pattern: *const c_char,
jwk: *const c_char,
) -> *const c_char {
ccchar_or_error(key_to_verification_method(method_pattern, jwk))
}
fn issue_credential(
credential_json_ptr: *const c_char,
proof_options_json_ptr: *const c_char,
key_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let resolver = DID_METHODS.to_resolver();
let mut context_loader = ssi::jsonld::ContextLoader::default();
let credential_json = unsafe { CStr::from_ptr(credential_json_ptr) }.to_str()?;
let proof_options_json = unsafe { CStr::from_ptr(proof_options_json_ptr) }.to_str()?;
let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?;
let mut credential = VerifiableCredential::from_json_unsigned(credential_json)?;
let key: JWK = serde_json::from_str(key_json)?;
let options: JWTOrLDPOptions = serde_json::from_str(proof_options_json)?;
let proof_format = options.proof_format.unwrap_or_default();
let rt = runtime::get()?;
let out = match proof_format {
ProofFormat::JWT => {
rt.block_on(credential.generate_jwt(Some(&key), &options.ldp_options, resolver))?
}
ProofFormat::LDP => {
let proof = rt.block_on(credential.generate_proof(
&key,
&options.ldp_options,
resolver,
&mut context_loader,
))?;
credential.add_proof(proof);
serde_json::to_string(&credential)?
}
};
Ok(CString::new(out)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_vc_issue_credential(
credential_json: *const c_char,
proof_options_json: *const c_char,
key_json: *const c_char,
) -> *const c_char {
ccchar_or_error(issue_credential(
credential_json,
proof_options_json,
key_json,
))
}
fn verify_credential(
credential_ptr: *const c_char,
proof_options_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let vc_str = unsafe { CStr::from_ptr(credential_ptr) }.to_str()?;
let proof_options_json = unsafe { CStr::from_ptr(proof_options_json_ptr) }.to_str()?;
let options: JWTOrLDPOptions = serde_json::from_str(proof_options_json)?;
let proof_format = options.proof_format.unwrap_or_default();
let rt = runtime::get()?;
let resolver = DID_METHODS.to_resolver();
let mut context_loader = ssi::jsonld::ContextLoader::default();
let result = match proof_format {
ProofFormat::JWT => rt.block_on(VerifiableCredential::verify_jwt(
&vc_str,
Some(options.ldp_options),
resolver,
&mut context_loader,
)),
ProofFormat::LDP => {
let vc = VerifiableCredential::from_json_unsigned(vc_str)?;
rt.block_on(vc.verify(Some(options.ldp_options), resolver, &mut context_loader))
}
};
Ok(CString::new(serde_json::to_string(&result)?)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_vc_verify_credential(
credential: *const c_char,
proof_options_json: *const c_char,
) -> *const c_char {
ccchar_or_error(verify_credential(credential, proof_options_json))
}
fn issue_presentation(
presentation_json_ptr: *const c_char,
proof_options_json_ptr: *const c_char,
key_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let resolver = DID_METHODS.to_resolver();
let mut context_loader = ssi::jsonld::ContextLoader::default();
let presentation_json = unsafe { CStr::from_ptr(presentation_json_ptr) }.to_str()?;
let proof_options_json = unsafe { CStr::from_ptr(proof_options_json_ptr) }.to_str()?;
let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?;
let mut presentation = VerifiablePresentation::from_json_unsigned(presentation_json)?;
let key: JWK = serde_json::from_str(key_json)?;
let options: JWTOrLDPOptions = serde_json::from_str(proof_options_json)?;
let proof_format = options.proof_format.unwrap_or_default();
let rt = runtime::get()?;
let out = match proof_format {
ProofFormat::JWT => {
rt.block_on(presentation.generate_jwt(Some(&key), &options.ldp_options, resolver))?
}
ProofFormat::LDP => {
let proof = rt.block_on(presentation.generate_proof(
&key,
&options.ldp_options,
resolver,
&mut context_loader,
))?;
presentation.add_proof(proof);
serde_json::to_string(&presentation)?
}
};
Ok(CString::new(out)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_vc_issue_presentation(
presentation_json: *const c_char,
proof_options_json: *const c_char,
key_json: *const c_char,
) -> *const c_char {
ccchar_or_error(issue_presentation(
presentation_json,
proof_options_json,
key_json,
))
}
fn did_auth(
holder_ptr: *const c_char,
proof_options_json_ptr: *const c_char,
key_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let resolver = DID_METHODS.to_resolver();
let mut context_loader = ssi::jsonld::ContextLoader::default();
let holder = unsafe { CStr::from_ptr(holder_ptr) }.to_str()?;
let proof_options_json = unsafe { CStr::from_ptr(proof_options_json_ptr) }.to_str()?;
let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?;
let mut presentation = VerifiablePresentation::default();
presentation.holder = Some(ssi::vc::URI::String(holder.to_string()));
let key: JWK = serde_json::from_str(key_json)?;
let options: JWTOrLDPOptions = serde_json::from_str(proof_options_json)?;
let proof_format = options.proof_format.unwrap_or_default();
let rt = runtime::get()?;
let out = match proof_format {
ProofFormat::JWT => {
rt.block_on(presentation.generate_jwt(Some(&key), &options.ldp_options, resolver))?
}
ProofFormat::LDP => {
let proof = rt.block_on(presentation.generate_proof(
&key,
&options.ldp_options,
resolver,
&mut context_loader,
))?;
presentation.add_proof(proof);
serde_json::to_string(&presentation)?
}
};
Ok(CString::new(out)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_did_auth(
holder: *const c_char,
proof_options_json: *const c_char,
key_json: *const c_char,
) -> *const c_char {
ccchar_or_error(did_auth(holder, proof_options_json, key_json))
}
fn verify_presentation(
presentation_ptr: *const c_char,
proof_options_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let vp_str = unsafe { CStr::from_ptr(presentation_ptr) }.to_str()?;
let proof_options_json = unsafe { CStr::from_ptr(proof_options_json_ptr) }.to_str()?;
let options: JWTOrLDPOptions = serde_json::from_str(proof_options_json)?;
let proof_format = options.proof_format.unwrap_or_default();
let mut context_loader = ssi::jsonld::ContextLoader::default();
let rt = runtime::get()?;
let resolver = DID_METHODS.to_resolver();
let result = match proof_format {
ProofFormat::JWT => rt.block_on(VerifiablePresentation::verify_jwt(
&vp_str,
Some(options.ldp_options),
resolver,
&mut context_loader,
)),
ProofFormat::LDP => {
let vp = VerifiablePresentation::from_json_unsigned(vp_str)?;
rt.block_on(vp.verify(
Some(options.ldp_options),
DID_METHODS.to_resolver(),
&mut context_loader,
))
}
};
Ok(CString::new(serde_json::to_string(&result)?)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_vc_verify_presentation(
presentation: *const c_char,
proof_options_json: *const c_char,
) -> *const c_char {
ccchar_or_error(verify_presentation(presentation, proof_options_json))
}
fn resolve_did(
did_ptr: *const c_char,
input_metadata_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let did = unsafe { CStr::from_ptr(did_ptr) }.to_str()?;
let input_metadata_json = if input_metadata_json_ptr.is_null() {
"{}"
} else {
unsafe { CStr::from_ptr(input_metadata_json_ptr) }.to_str()?
};
let input_metadata: ResolutionInputMetadata = serde_json::from_str(input_metadata_json)?;
let resolver = DID_METHODS.to_resolver();
let rt = runtime::get()?;
let (res_meta, doc_opt, doc_meta_opt) = rt.block_on(resolver.resolve(did, &input_metadata));
let result = ResolutionResult {
did_document: doc_opt,
did_resolution_metadata: Some(res_meta),
did_document_metadata: doc_meta_opt,
..Default::default()
};
Ok(CString::new(serde_json::to_string(&result)?)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_did_resolve(
did: *const c_char,
input_metadata_json: *const c_char,
) -> *const c_char {
ccchar_or_error(resolve_did(did, input_metadata_json))
}
fn dereference_did_url(
did_url_ptr: *const c_char,
input_metadata_json_ptr: *const c_char,
) -> Result<*const c_char, Error> {
let did_url = unsafe { CStr::from_ptr(did_url_ptr) }.to_str()?;
let input_metadata_json = if input_metadata_json_ptr.is_null() {
"{}"
} else {
unsafe { CStr::from_ptr(input_metadata_json_ptr) }.to_str()?
};
let input_metadata: DereferencingInputMetadata = serde_json::from_str(input_metadata_json)?;
let resolver = DID_METHODS.to_resolver();
let rt = runtime::get()?;
let deref_result = rt.block_on(dereference(resolver, did_url, &input_metadata));
use serde_json::json;
let result = json!(deref_result);
Ok(CString::new(serde_json::to_string(&result)?)?.into_raw())
}
#[no_mangle]
pub extern "C" fn didkit_did_url_dereference(
did_url: *const c_char,
input_metadata_json: *const c_char,
) -> *const c_char {
ccchar_or_error(dereference_did_url(did_url, input_metadata_json))
}
#[no_mangle]
pub extern "C" fn didkit_free_string(string: *const c_char) {
if string.is_null() {
return;
}
unsafe {
drop(CString::from_raw(string as *mut c_char));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_version() {
let version = didkit_get_version();
let version_cstr = unsafe { CStr::from_ptr(version) };
let version_str = version_cstr.to_str().unwrap();
assert!(version_str.len() > 0);
}
}