Skip to content

Commit

Permalink
Support JWK public key VM processing into Key (#1297)
Browse files Browse the repository at this point in the history
* move to latest vdr

Signed-off-by: gmulhearn <[email protected]>

* move other common git deps to use workspace

Signed-off-by: gmulhearn <[email protected]>

* cargo update

Signed-off-by: gmulhearn <[email protected]>

* add decode from jwk with tests

Signed-off-by: gmulhearn <[email protected]>

* support in VM processing to public key

Signed-off-by: gmulhearn <[email protected]>

* enable jwk in all unit tests

Signed-off-by: gmulhearn <[email protected]>

---------

Signed-off-by: gmulhearn <[email protected]>
Signed-off-by: George Mulhearn <[email protected]>
  • Loading branch information
gmulhearn authored Oct 11, 2024
1 parent 4d57c79 commit 7fc8b79
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ let_underscore_drop = "allow"
indy-vdr = { git = "https://github.com/hyperledger/indy-vdr.git", tag = "v0.4.3", default-features = false, features = [
"log",
] }
indy-vdr-proxy-client = { git = "https://github.com/hyperledger/indy-vdr.git", tag = "v0.4.3" }
indy-vdr-proxy-client = { git = "https://github.com/hyperledger/indy-vdr.git", tag = "v0.4.3" }
indy-credx = { git = "https://github.com/hyperledger/indy-shared-rs", tag = "v1.1.0" }
anoncreds = { git = "https://github.com/hyperledger/anoncreds-rs.git", tag = "v0.2.0" }
anoncreds = { git = "https://github.com/hyperledger/anoncreds-rs.git", tag = "v0.2.0" }
aries-askar = { version = "0.3.1" }
askar-crypto = { version = "0.3.1", default-features = false }
6 changes: 3 additions & 3 deletions aries/aries_vcx_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ edition.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
vdrtools_wallet = [ "dep:libvdrtools", "dep:indy-api-types"]
vdrtools_wallet = ["dep:libvdrtools", "dep:indy-api-types"]
askar_wallet = ["dep:aries-askar"]

[dependencies]
anyhow = "1.0"
aries-askar = { version = "0.3.1", optional = true }
aries-askar = { workspace = true, optional = true }
async-trait = "0.1.68"
bs58 = { version = "0.5" }
base64 = "0.22.1"
Expand All @@ -22,7 +22,7 @@ log = "0.4.17"
indy-api-types = { path = "../misc/legacy/libvdrtools/indy-api-types", optional = true }
serde = { version = "1.0.159", features = ["derive"] }
serde_json = "1.0.95"
public_key = { path = "../../did_core/public_key"}
public_key = { path = "../../did_core/public_key" }
rand = "0.8.5"
thiserror = "1.0.40"
tokio = { version = "1.38" }
Expand Down
3 changes: 3 additions & 0 deletions did_core/did_doc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "did_doc"
version = "0.1.0"
edition = "2021"

[features]
jwk = ["public_key/jwk"]

[dependencies]
base64 = "0.22.1"
bs58 = "0.5.0"
Expand Down
52 changes: 52 additions & 0 deletions did_core/did_doc/src/schema/verification_method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ impl VerificationMethod {
PublicKeyField::Multibase {
public_key_multibase,
} => Key::from_fingerprint(public_key_multibase)?,
#[cfg(feature = "jwk")]
PublicKeyField::Jwk { public_key_jwk } => Key::from_jwk(&public_key_jwk.to_string())?,
// TODO - FUTURE - other key types could do with some special handling, i.e.
// those where the key_type is encoded within the key field (multibase, jwk, etc)
_ => Key::new(
Expand Down Expand Up @@ -152,3 +154,53 @@ mod tests {
assert!(vm.is_err());
}
}

#[cfg(feature = "jwk")]
#[cfg(test)]
mod jwk_tests {
use ::public_key::KeyType;
use serde_json::json;

use super::*;

#[test]
fn test_public_key_from_ed25519_jwk_vm() {
let vm: VerificationMethod = serde_json::from_value(json!({
"id": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"type": "Ed25519VerificationKey2018",
"controller": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
"publicKeyJwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "lJZrfAjkBXdfjebMHEUI9usidAPhAlssitLXR3OYxbI"
}
})).unwrap();
let pk = vm.public_key().unwrap();
assert!(matches!(pk.key_type(), KeyType::Ed25519));
assert_eq!(
pk.fingerprint(),
"z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
)
}

#[test]
fn test_public_key_from_p256_jwk_vm() {
let vm: VerificationMethod = serde_json::from_value(json!({
"id": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
"type": "JsonWebKey2020",
"controller": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
}
})).unwrap();
let pk = vm.public_key().unwrap();
assert!(matches!(pk.key_type(), KeyType::P256));
assert_eq!(
pk.fingerprint(),
"zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ impl TryFrom<VerificationMethodType> for KeyType {
VerificationMethodType::Bls12381G2Key2020 => Ok(KeyType::Bls12381g2),
VerificationMethodType::X25519KeyAgreementKey2019
| VerificationMethodType::X25519KeyAgreementKey2020 => Ok(KeyType::X25519),
// The verification method type does not map directly to a key type.
// This may occur when the VM type is a multikey (JsonWebKey, Multikey, etc)
_ => Err(DidDocumentBuilderError::UnsupportedVerificationMethodType(
value,
)),
Expand Down
10 changes: 10 additions & 0 deletions did_core/public_key/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "public_key"
version = "0.1.0"
edition = "2021"

[features]
jwk = ["dep:askar-crypto"]

[dependencies]
thiserror = "1.0.40"
serde = { version = "1.0.164", features = ["derive"] }
Expand All @@ -11,3 +14,10 @@ base64 = "0.22.1"
bs58 = "0.5.0"
multibase = "0.9.1"
unsigned-varint = "0.8.0"
# askar-crypto used for jwk conversion. maintain minimal feature set
askar-crypto = { workspace = true, features = [
"std",
"any_key",
"ec_curves",
"ed25519",
], optional = true }
6 changes: 6 additions & 0 deletions did_core/public_key/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error::Error;

use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -10,8 +12,12 @@ pub enum PublicKeyError {
MultibaseDecodingError(#[from] multibase::Error),
#[error("Varint decoding error")]
VarintDecodingError(#[from] VarintDecodingError),
#[error("JWK decoding error")]
JwkDecodingError(#[from] Box<dyn Error + Send + Sync>),
#[error("Unsupported multicodec descriptor: {0}")]
UnsupportedMulticodecDescriptor(u64),
#[error("Unsupported multicodec descriptor: {0}")]
UnsupportedKeyType(String),
}

#[derive(Debug, Error)]
Expand Down
100 changes: 100 additions & 0 deletions did_core/public_key/src/jwk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use askar_crypto::{
alg::{AnyKey, BlsCurves, EcCurves},
jwk::FromJwk,
repr::ToPublicBytes,
};

use crate::{Key, KeyType, PublicKeyError};

impl Key {
pub fn from_jwk(jwk: &str) -> Result<Key, PublicKeyError> {
let askar_key: Box<AnyKey> =
FromJwk::from_jwk(jwk).map_err(|e| PublicKeyError::JwkDecodingError(Box::new(e)))?;

let askar_alg = askar_key.algorithm();
let pub_key_bytes = askar_key
.to_public_bytes()
.map_err(|e| PublicKeyError::JwkDecodingError(Box::new(e)))?
.to_vec();

let key_type = match askar_alg {
askar_crypto::alg::KeyAlg::Ed25519 => KeyType::Ed25519,
askar_crypto::alg::KeyAlg::Bls12_381(BlsCurves::G1G2) => KeyType::Bls12381g1g2,
askar_crypto::alg::KeyAlg::Bls12_381(BlsCurves::G1) => KeyType::Bls12381g1,
askar_crypto::alg::KeyAlg::Bls12_381(BlsCurves::G2) => KeyType::Bls12381g2,
askar_crypto::alg::KeyAlg::X25519 => KeyType::X25519,
askar_crypto::alg::KeyAlg::EcCurve(EcCurves::Secp256r1) => KeyType::P256,
askar_crypto::alg::KeyAlg::EcCurve(EcCurves::Secp384r1) => KeyType::P384,
_ => return Err(PublicKeyError::UnsupportedKeyType(askar_alg.to_string())),
};

Key::new(pub_key_bytes, key_type)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_from_ed25519_jwk() {
// vector from https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
let jwk = r#"{
"kty": "OKP",
"crv": "Ed25519",
"x": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
}"#;
let key = Key::from_jwk(jwk).unwrap();
assert_eq!(
key.fingerprint(),
"z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
);
}

#[test]
fn test_from_x25519_jwk() {
// vector from https://w3c-ccg.github.io/did-method-key/#ed25519-x25519
let jwk = r#"{
"kty": "OKP",
"crv": "X25519",
"x": "W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0"
}"#;
let key = Key::from_jwk(jwk).unwrap();
assert_eq!(
key.fingerprint(),
"z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
);
}

#[test]
fn test_from_p256_jwk() {
// vector from https://dev.uniresolver.io/
let jwk = r#"{
"kty": "EC",
"crv": "P-256",
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
}"#;
let key = Key::from_jwk(jwk).unwrap();
assert_eq!(
key.fingerprint(),
"zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
);
}

#[test]
fn test_from_p384_jwk() {
// vector from https://dev.uniresolver.io/
let jwk = r#"{
"kty": "EC",
"crv": "P-384",
"x": "bKq-gg3sJmfkJGrLl93bsumOTX1NubBySttAV19y5ClWK3DxEmqPy0at5lLqBiiv",
"y": "PJQtdHnInU9SY3e8Nn9aOPoP51OFbs-FWJUsU0TGjRtZ4bnhoZXtS92wdzuAotL9"
}"#;
let key = Key::from_jwk(jwk).unwrap();
assert_eq!(
key.fingerprint(),
"z82Lkytz3HqpWiBmt2853ZgNgNG8qVoUJnyoMvGw6ZEBktGcwUVdKpUNJHct1wvp9pXjr7Y"
);
}
}
2 changes: 2 additions & 0 deletions did_core/public_key/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod error;
#[cfg(feature = "jwk")]
mod jwk;
mod key;
mod key_type;

Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ check-aries-vcx-credx:
cargo test --manifest-path="aries/aries_vcx/Cargo.toml" -F vdrtools_wallet,credx --tests

test-unit test_name="":
RUST_TEST_THREADS=1 cargo test --workspace --lib --exclude aries-vcx-agent --exclude libvdrtools --exclude wallet_migrator --exclude mediator {{test_name}}
RUST_TEST_THREADS=1 cargo test --workspace --lib --exclude aries-vcx-agent --exclude libvdrtools --exclude wallet_migrator --exclude mediator {{test_name}} -F did_doc/jwk -F public_key/jwk

test-compatibility-aries-vcx-wallet:
cargo test --manifest-path="aries/aries_vcx_wallet/Cargo.toml" -F vdrtools_wallet,askar_wallet wallet_compatibility_
Expand All @@ -44,4 +44,4 @@ test-integration-aries-vcx-vdrproxy test_name="":
cargo test --manifest-path="aries/aries_vcx/Cargo.toml" -F vdr_proxy_ledger,credx -- --ignored {{test_name}}

test-integration-did-crate test_name="":
cargo test --examples -p did_doc -p did_parser_nom -p did_resolver -p did_resolver_registry -p did_resolver_sov -p did_resolver_web -p did_key -p did_peer --test "*"
cargo test --examples -p did_doc -p did_parser_nom -p did_resolver -p did_resolver_registry -p did_resolver_sov -p did_resolver_web -p did_key -p did_peer -F did_doc/jwk --test "*"

0 comments on commit 7fc8b79

Please sign in to comment.