Skip to content

Commit

Permalink
Add CLI argument to pass a JWK directly and env vars
Browse files Browse the repository at this point in the history
  • Loading branch information
sbihel committed Dec 2, 2020
1 parent 00c7bd7 commit 9c1ebd3
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 56 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ $ docker run ghcr.io/spruceid/didkit-cli:latest --help
$ docker run --init -p 8080 ghcr.io/spruceid/didkit-http:latest --port 8080
```

> You can pass JWKs either by sharing a volume with `docker run --volume`, or by passing the JWK directly with `docker run -e JWK=$MY_JWK` or `docker run didkit-http --jwk $MY_JWK`.
### Manual

DIDKit is written in [Rust][]. To get Rust, you can use [Rustup][].
Expand Down
8 changes: 5 additions & 3 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Given a Ed25519 [JWK][], output the corresponding [did:key][] [verificationMetho

#### Options

- `-k, --key <file>` (required) - Name of JWK file
- `-k, --key-path <file>` (required) - Filename of JWK file
- `-j, --jwk <jwk>` (required) - JWK.

### `didkit vc-issue-credential`

Expand All @@ -47,13 +48,14 @@ The proof type is set automatically based on the key file provided. JWK paramete

#### Options

Options besides `--key` correspond to linked data [proof options][] as specified in [ld-proofs][] and [vc-http-api][].
Options besides `--key-path` correspond to linked data [proof options][] as specified in [ld-proofs][] and [vc-http-api][].

- `-C, --challenge <challenge>` - [challenge][] property of the proof
- `-c, --created <created>` - [created][] property of the proof. ISO8601 datetime. Defaults to the current time.
time.
- `-d, --domain <domain>` - [domain][] property of the proof
- `-k, --key <key>` (required) - Filename of JWK for signing.
- `-k, --key-path <key>` (required, conflicts with jwk) - Filename of JWK for signing.
- `-j, --jwk <jwk>` (required, conflicts with key-path) - JWK for signing.
- `-p, --proof-purpose <proof-purpose>` [proofPurpose][] property of the proof.
- `-v, --verification-method <verification-method>` [verificationMethod][]
property of the proof. URI for proof verification information, e.g. a public key identifier.
Expand Down
95 changes: 58 additions & 37 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::io::{stdin, stdout, BufReader, BufWriter};
use std::path::PathBuf;

use chrono::prelude::*;
use structopt::StructOpt;
use structopt::{clap::ArgGroup, StructOpt};

use didkit::{
LinkedDataProofOptions, ProofPurpose, VerifiableCredential, VerifiablePresentation, JWK,
Expand All @@ -15,13 +15,13 @@ pub enum DIDKit {
GenerateEd25519Key,
/// Output a did:key DID for a JWK
KeyToDIDKey {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
key: KeyArg,
},
/// Output a verificationMethod for a JWK
KeyToVerificationMethod {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
key: KeyArg,
},

/*
Expand All @@ -44,8 +44,8 @@ pub enum DIDKit {
// VC Functionality
/// Issue Credential
VCIssueCredential {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
key: KeyArg,
#[structopt(flatten)]
proof_options: ProofOptions,
},
Expand All @@ -56,8 +56,8 @@ pub enum DIDKit {
},
/// Issue Presentation
VCIssuePresentation {
#[structopt(short, long, parse(from_os_str))]
key: PathBuf,
#[structopt(flatten)]
key: KeyArg,
#[structopt(flatten)]
proof_options: ProofOptions,
},
Expand All @@ -84,18 +84,54 @@ pub enum DIDKit {

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

#[derive(StructOpt, Debug)]
#[structopt(group = ArgGroup::with_name("key_group").required(true))]
pub struct KeyArg {
#[structopt(
env,
short,
long,
parse(from_os_str),
conflicts_with = "jwk",
group = "key_group"
)]
key_path: Option<PathBuf>,
#[structopt(
env,
short,
long,
parse(try_from_str = serde_json::from_str),
conflicts_with = "key_path",
group = "key_group"
)]
jwk: Option<JWK>,
}

impl KeyArg {
fn get_jwk(&self) -> JWK {
match &self.key_path {
Some(p) => {
let key_file = File::open(p).unwrap();
let key_reader = BufReader::new(key_file);
serde_json::from_reader(key_reader).unwrap()
}
None => self.jwk.clone().unwrap(),
}
}
}

impl From<ProofOptions> for LinkedDataProofOptions {
fn from(options: ProofOptions) -> LinkedDataProofOptions {
LinkedDataProofOptions {
Expand All @@ -109,13 +145,6 @@ impl From<ProofOptions> for LinkedDataProofOptions {
}
}

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 {
Expand All @@ -125,23 +154,18 @@ fn main() {
println!("{}", jwk_str);
}

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

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

DIDKit::VCIssueCredential {
key: key_path,
proof_options,
} => {
let key: JWK = read_key(key_path);
DIDKit::VCIssueCredential { key, proof_options } => {
let key: JWK = key.get_jwk();
let credential_reader = BufReader::new(stdin());
let mut credential: VerifiableCredential =
serde_json::from_reader(credential_reader).unwrap();
Expand All @@ -166,11 +190,8 @@ fn main() {
}
}

DIDKit::VCIssuePresentation {
key: key_path,
proof_options,
} => {
let key: JWK = read_key(key_path);
DIDKit::VCIssuePresentation { key, proof_options } => {
let key: JWK = key.get_jwk();
let presentation_reader = BufReader::new(stdin());
let mut presentation: VerifiablePresentation =
serde_json::from_reader(presentation_reader).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn generate_key() {
fn issue_verify_credential_presentation() {
// Get DID for key
let did_output = Command::new(BIN)
.args(&["key-to-did-key", "-k", "tests/ed25519-key.jwk"])
.args(&["key-to-did-key", "--key-path", "tests/ed25519-key.jwk"])
.stderr(Stdio::inherit())
.output()
.unwrap();
Expand Down
3 changes: 2 additions & 1 deletion http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ and runs until interrupted.
- `-p, --port <port>` - Port to listen on. Default is a random OS-chosen port.
- `-k, --key <key>` - Filename of a JWK to use for issuing credentials and
presentations.
- `-j, --jwk <jwk>` - JWK to use for issuing credentials and presentations.

#### Issuer keys

Provide issuer keys using the `-k`/`--key` option. If none are provided, issuance functionality will be unavailable. If one is provided, that one will be used to sign all credentials and presentations, regardless of the proof options in the issuance request. If more than one key is provided, the issuance request may identify which key to use for signing by its DID in the `verificationMethod` property of the proof options; if none is identified in that property, the first key is used.
Provide issuer keys using the `-k`/`--key-path` or `-j`/`--jwk` options. If none are provided, issuance functionality will be unavailable. If one is provided, that one will be used to sign all credentials and presentations, regardless of the proof options in the issuance request. If more than one key is provided, the issuance request may identify which key to use for signing by its DID in the `verificationMethod` property of the proof options; if none is identified in that property, the first key is used.

## Rust library

Expand Down
55 changes: 41 additions & 14 deletions http/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,59 @@ use didkit_http::Error;
#[derive(StructOpt, Debug)]
pub struct DIDKitHttpOpts {
/// Port to listen on
#[structopt(short, long)]
#[structopt(env, short, long)]
port: Option<u16>,
/// Hostname to listen on
#[structopt(short = "s", long)]
#[structopt(env, short = "s", long)]
host: Option<std::net::IpAddr>,
/// JWK to use for issuing
#[structopt(short, long, parse(from_os_str))]
key: Option<Vec<PathBuf>>,
#[structopt(flatten)]
key: KeyArg,
}

fn read_key(path: &PathBuf) -> JWK {
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
serde_json::from_reader(reader).unwrap()
#[derive(StructOpt, Debug)]
pub struct KeyArg {
#[structopt(
env,
short,
long,
parse(from_os_str),
conflicts_with = "jwk",
group = "key_group"
)]
key_path: Option<Vec<PathBuf>>,
#[structopt(
env,
short,
long,
parse(try_from_str = serde_json::from_str),
conflicts_with = "key_path",
group = "key_group"
)]
jwk: Option<Vec<JWK>>,
}

impl KeyArg {
fn get_jwks(&self) -> Vec<JWK> {
match self.key_path.clone() {
Some(paths) => paths
.iter()
.map(|filename| {
let key_file = File::open(filename).unwrap();
let key_reader = BufReader::new(key_file);
serde_json::from_reader(key_reader).unwrap()
})
.collect(),
None => self.jwk.clone().unwrap_or_default(),
}
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
let opt = DIDKitHttpOpts::from_args();

let keys = opt
.key
.unwrap_or_default()
.iter()
.map(|filename| read_key(filename))
.collect();
let keys = opt.key.get_jwks();
let makesvc = DIDKitHTTPMakeSvc::new(keys);
let host = opt.host.unwrap_or([127, 0, 0, 1].into());
let addr = (host, opt.port.unwrap_or(0)).into();
Expand Down

0 comments on commit 9c1ebd3

Please sign in to comment.