Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI and FFI with VC/VP issuing and verifying #2

Merged
merged 3 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[target.i686-linux-android]
linker = "i686-linux-android16-clang"

[target.armv7-linux-androideabi]
linker = "armv7a-linux-androideabi16-clang"

[target.aarch64-linux-android]
linker = "aarch64-linux-android21-clang"

[target.x86_64-linux-android]
linker = "x86_64-linux-android21-clang"
91 changes: 91 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: ci

on:
pull_request:
branches:
- main
push:
branches:
- main

env:
CARGO_TERM_COLOR: always

defaults:
run:
working-directory: didkit

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout DIDKit repository
uses: actions/checkout@v2
with:
path: didkit

- name: Checkout SSI library
uses: actions/checkout@v2
with:
repository: spruceid/ssi
token: ${{ secrets.GH_ACCESS_TOKEN_CEL }}
path: ssi

- name: Cache Cargo registry and build artifacts
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', 'lib/Makefile', '**.rs') }}
restore-keys: |
${{ runner.os }}-cargo-

- name: Build
run: cargo build --verbose --workspace

- name: Test
run: cargo test --verbose --workspace

- name: Install Rust Android targets
run: make -C lib install-rustup-android

- name: Install JDK
uses: actions/setup-java@v1
with:
java-version: 1.8

- name: Install Flutter
uses: subosito/flutter-action@v1

- name: Opt out of Dart/Flutter analytics
run: |
dart --disable-analytics
flutter --suppress-analytics config --no-analytics

- name: Install Android SDK
uses: android-actions/setup-android@v2

- name: Cache Android NDK
id: ndk-cache
uses: actions/cache@v2
with:
path: ${{ env.ANDROID_SDK_ROOT }}/ndk-bundle
key: ${{ runner.os }}-ndk-bundle

- name: Install Android NDK
if: steps.ndk-cache.outputs.cache-hit != 'true'
run: $ANDROID_SDK_ROOT/tools/bin/sdkmanager ndk-bundle

- name: Test C FFI
run: make -C lib ../target/test/c.stamp

- name: Test JNI
run: make -C lib ../target/test/java.stamp

- name: Test Dart/Flutter plugin
run: make -C lib ../target/test/flutter.stamp

- name: Build Android Archive
run: make -C lib ../target/test/aar.stamp
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[workspace]
members = [
"http",
"cli",
"lib",
"lib/cbindings"
]
15 changes: 15 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "didkit_cli"
version = "0.0.1"
authors = ["Charles E. Lehner <[email protected]>"]
edition = "2018"

[dependencies]
chrono = { version = "0.4", features = ["serde"] }
didkit = { path = "../lib" }
serde_json = "1.0"
structopt = "0.3"

[[bin]]
path = "src/main.rs"
name = "didkit"
186 changes: 186 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter};
use std::path::PathBuf;

use chrono::prelude::*;
use structopt::StructOpt;

use didkit::{
LinkedDataProofOptions, ProofPurpose, VerifiableCredential, VerifiablePresentation, JWK,
};

#[derive(StructOpt, Debug)]
pub enum DIDKit {
/// Generate and output a Ed25519 keypair in JWK format
GenerateEd25519Key,
/// Output a did:key DID for a JWK
KeyToDIDKey {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
},

/*
// DID Functionality
/// Create new DID Document.
DIDCreate {},
/// Resolve a DID to a DID Document.
DIDResolve {},
/// Dereference a DID URL to a resource.
DIDDereference {},
/// Update a DID Document’s authentication.
DIDUpdateAuthentication {},
/// Update a DID Document’s service endpoint(s).
DIDUpdateServiceEndpoints {},
/// Deactivate a DID.
DIDDeactivate {},
/// Create a Signed IETF JSON Patch to update a DID document.
DIDPatch {},
*/
// VC Functionality
/// Issue Credential
VCIssueCredential {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
proof_options: ProofOptions,
},
/// Verify Credential
VCVerifyCredential {
#[structopt(flatten)]
proof_options: ProofOptions,
},
/// Issue Presentation
VCIssuePresentation {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
proof_options: ProofOptions,
},
/// Verify Presentation
VCVerifyPresentation {
#[structopt(flatten)]
proof_options: ProofOptions,
},
/*
/// Revoke Credential
VCRevokeCredential {},
*/

/*
// DIDComm Functionality (???)
/// Discover a messaging endpoint from a DID which supports DIDComm.
DIDCommDiscover {},
/// Send a DIDComm message.
DIDCommSend {},
/// Receive a DIDComm message.
DIDCommReceive {},
*/
}

#[derive(StructOpt, Debug)]
pub struct ProofOptions {
#[structopt(short, long)]
pub verification_method: Option<String>,
#[structopt(short, long)]
pub proof_purpose: Option<ProofPurpose>,
#[structopt(short, long)]
pub created: Option<DateTime<Utc>>,
#[structopt(short = "C", long)]
pub challenge: Option<String>,
#[structopt(short, long)]
pub domain: Option<String>,
}

impl From<ProofOptions> for LinkedDataProofOptions {
fn from(options: ProofOptions) -> LinkedDataProofOptions {
LinkedDataProofOptions {
verification_method: options.verification_method,
proof_purpose: options.proof_purpose,
created: options.created,
challenge: options.challenge,
domain: options.domain,
}
}
}

fn read_key(key_path: PathBuf) -> JWK {
let key_file = File::open(key_path).unwrap();
let key_reader = BufReader::new(key_file);
let key: JWK = serde_json::from_reader(key_reader).unwrap();
key
}

fn main() {
let opt = DIDKit::from_args();
match opt {
DIDKit::GenerateEd25519Key => {
let jwk = JWK::generate_ed25519().unwrap();
let jwk_str = serde_json::to_string(&jwk).unwrap();
println!("{}", jwk_str);
}

DIDKit::KeyToDIDKey { key: key_path } => {
let key = read_key(key_path);
let did = key.to_did().unwrap();
println!("{}", did);
}

DIDKit::VCIssueCredential {
key: key_path,
proof_options,
} => {
let key: JWK = read_key(key_path);
let credential_reader = BufReader::new(stdin());
let mut credential: VerifiableCredential =
serde_json::from_reader(credential_reader).unwrap();
let options = LinkedDataProofOptions::from(proof_options);
let proof = credential.generate_proof(&key, &options).unwrap();
credential.add_proof(proof);
let stdout_writer = BufWriter::new(stdout());
serde_json::to_writer(stdout_writer, &credential).unwrap();
}

DIDKit::VCVerifyCredential { proof_options } => {
let credential_reader = BufReader::new(stdin());
let credential: VerifiableCredential =
serde_json::from_reader(credential_reader).unwrap();
let options = LinkedDataProofOptions::from(proof_options);
credential.validate_unsigned().unwrap();
let result = credential.verify(Some(options));
let stdout_writer = BufWriter::new(stdout());
serde_json::to_writer(stdout_writer, &result).unwrap();
if result.errors.len() > 0 {
std::process::exit(2);
}
}

DIDKit::VCIssuePresentation {
key: key_path,
proof_options,
} => {
let key: JWK = read_key(key_path);
let presentation_reader = BufReader::new(stdin());
let mut presentation: VerifiablePresentation =
serde_json::from_reader(presentation_reader).unwrap();
let options = LinkedDataProofOptions::from(proof_options);
let proof = presentation.generate_proof(&key, &options).unwrap();
presentation.add_proof(proof);
let stdout_writer = BufWriter::new(stdout());
serde_json::to_writer(stdout_writer, &presentation).unwrap();
}

DIDKit::VCVerifyPresentation { proof_options } => {
let presentation_reader = BufReader::new(stdin());
let presentation: VerifiablePresentation =
serde_json::from_reader(presentation_reader).unwrap();
let options = LinkedDataProofOptions::from(proof_options);
presentation.validate_unsigned().unwrap();
let result = presentation.verify(Some(options));
let stdout_writer = BufWriter::new(stdout());
serde_json::to_writer(stdout_writer, &result).unwrap();
if result.errors.len() > 0 {
std::process::exit(2);
}
}
}
}
Loading