From 7cfb6e7d18c46d4d37c056601c077563e50195a4 Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 6 Jan 2022 18:52:40 -0800 Subject: [PATCH 01/11] implement webkey gpg parsing to did --- .gitignore | 2 + Cargo.toml | 17 +++-- did-webkey/Cargo.toml | 7 +- did-webkey/src/lib.rs | 141 ++++++++++++++++++++++++++++++++++---- did-webkey/tests/user.gpg | 59 ++++++++++++++++ src/did.rs | 2 + 6 files changed, 205 insertions(+), 23 deletions(-) create mode 100644 did-webkey/tests/user.gpg diff --git a/.gitignore b/.gitignore index 96ef6c0b9..c185eafdc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target Cargo.lock + +.vscode diff --git a/Cargo.toml b/Cargo.toml index 5c3054ed8..de074dfd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,12 @@ description = "Core library for Verifiable Credentials and Decentralized Identif repository = "https://github.com/spruceid/ssi/" documentation = "https://docs.rs/ssi/" -exclude = [ - "json-ld-api/*", - "json-ld-normalization/*", -] +exclude = ["json-ld-api/*", "json-ld-normalization/*"] [features] default = ["ring"] http-did = ["hyper", "hyper-tls", "http", "percent-encoding", "tokio"] -libsecp256k1 = ["secp256k1"] # backward compatibility +libsecp256k1 = ["secp256k1"] # backward compatibility secp256k1 = ["k256", "rand", "k256/keccak256"] secp256r1 = ["p256", "rand"] ripemd-160 = ["ripemd160", "secp256k1"] @@ -58,7 +55,12 @@ lazy_static = "1.4" combination = "0.1" sha2 = { version = "0.9", optional = true } sha2_old = { package = "sha2", version = "0.8" } -hyper = { version = "0.14", optional = true, features = ["server", "client", "http1", "stream"] } +hyper = { version = "0.14", optional = true, features = [ + "server", + "client", + "http1", + "stream", +] } hyper-tls = { version = "0.5", optional = true } http = { version = "0.2", optional = true } hex = "0.4" @@ -77,6 +79,7 @@ p256 = { version = "0.8", optional = true, features = ["zeroize", "ecdsa"] } ssi-contexts = { version = "0.1.2", path = "contexts/" } ripemd160 = { version = "0.9", optional = true } sshkeys = "0.3" +sequoia-openpgp = "1.7" reqwest = { version = "0.11", features = ["json"] } flate2 = "1.0" bitvec = "0.20" @@ -106,7 +109,7 @@ members = [ ] [dev-dependencies] -blake2 = "0.8" # for bbs doctest +blake2 = "0.8" # for bbs doctest uuid = { version = "0.8", features = ["v4", "serde"] } difference = "2.0" did-method-key = { path = "./did-key" } diff --git a/did-webkey/Cargo.toml b/did-webkey/Cargo.toml index 782704abc..808fc19c8 100644 --- a/did-webkey/Cargo.toml +++ b/did-webkey/Cargo.toml @@ -15,10 +15,15 @@ documentation = "https://docs.rs/did-webkey/" p256 = ["ssi/p256"] [dependencies] -ssi = { version = "0.3", path = "../", default-features = false } +ssi = { version = "0.3", path = "../", features = [ + "rand", + "ring", + "p256", +], default-features = false } async-trait = "0.1" reqwest = { version = "0.11", features = ["json"] } http = "0.2" +sequoia-openpgp = "1.7" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } sshkeys = "0.3" diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index 973ccc1c3..4defe4e8c 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -3,6 +3,12 @@ use core::str::FromStr; use async_trait::async_trait; use serde::{Deserialize, Serialize}; +use openpgp::{ + cert::prelude::*, + parse::{PacketParser, Parse}, + serialize::SerializeInto, +}; +use sequoia_openpgp as openpgp; use sshkeys::PublicKeyKind; use ssi::did::{DIDMethod, Document, VerificationMethod, VerificationMethodMap, DIDURL}; use ssi::did_resolve::{ @@ -39,11 +45,53 @@ impl FromStr for DIDWebKeyType { } fn parse_pubkeys_gpg( - _did: &str, - _bytes: Vec, + did: &str, + bytes: Vec, ) -> Result<(Vec, Vec), String> { - // TODO - Err(String::from("GPG Key Type Not Implemented")) + let mut did_urls = Vec::new(); + let mut vm_maps = Vec::new(); + + let ppr = PacketParser::from_bytes(&bytes) + .map_err(|e| format!("Unable to parse GPG keyring: {}", e))?; + for certo in CertParser::from(ppr) { + let cert = certo.map_err(|e| format!("Error reading keyring: {}", e))?; + let (vm_map, did_url) = gpg_pk_to_vm(did, cert).map_err(|e| { + format!( + "Unable to convert GPG public key to verification method: {}", + e + ) + })?; + vm_maps.push(vm_map); + did_urls.push(did_url); + } + + Ok((vm_maps, did_urls)) +} + +fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL), String> { + let vm_url = DIDURL { + did: did.to_string(), + // NOTE: should this be key_id or fingerprint? + // https://docs.rs/sequoia-openpgp/1.7.0/sequoia_openpgp/struct.Cert.html#method.keyid + fragment: Some(cert.fingerprint().to_string()), + ..Default::default() + }; + + let armored_pgp = String::from_utf8( + cert.armored() + .to_vec() + .map_err(|e| format!("Failed to re-serialize cert: {}", e))?, + ) + .map_err(|e| format!("Failed to read cert as utf8: {}", e))?; + + let vm_map = VerificationMethodMap { + id: vm_url.to_string(), + type_: "PgpVerificationKey2021".to_string(), + public_key_pgp: Some(armored_pgp), + controller: did.to_string(), + ..Default::default() + }; + Ok((vm_map, vm_url)) } fn pk_to_vm_ed25519( @@ -348,23 +396,22 @@ mod tests { ); } - // TODO: use JWK fingerprint - const DID_URL: &str = "https://localhost/user.keys"; - const PUBKEYS: &str = include_str!("../tests/ssh_keys"); // localhost web server for serving did:web DID documents. - // TODO: pass arguments here instead of using const - fn web_server() -> Result<(String, impl FnOnce() -> Result<(), ()>), hyper::Error> { + fn web_server( + did_url: &'static str, + pubkeys: &'static str, + ) -> Result<(String, impl FnOnce() -> Result<(), ()>), hyper::Error> { use http::header::{HeaderValue, CONTENT_TYPE}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Response, Server}; let addr = ([127, 0, 0, 1], 0).into(); - let make_svc = make_service_fn(|_| async move { - Ok::<_, hyper::Error>(service_fn(|req| async move { + let make_svc = make_service_fn(move |_| async move { + Ok::<_, hyper::Error>(service_fn(move |req| async move { let uri = req.uri(); // Skip leading slash let proxied_url: String = uri.path().chars().skip(1).collect(); - if proxied_url == DID_URL { - let body = Body::from(PUBKEYS); + if proxied_url == did_url { + let body = Body::from(pubkeys); let mut response = Response::new(body); response .headers_mut() @@ -392,8 +439,12 @@ mod tests { } #[tokio::test] - async fn from_did_webkey() { - let (url, shutdown) = web_server().unwrap(); + async fn from_did_webkey_ssh() { + // TODO: use JWK fingerprint + let did_url: &str = "https://localhost/user.keys"; + let pubkeys: &str = include_str!("../tests/ssh_keys"); + + let (url, shutdown) = web_server(did_url, pubkeys).unwrap(); PROXY.with(|proxy| { proxy.replace(Some(url)); }); @@ -460,4 +511,64 @@ mod tests { }); shutdown().ok(); } + + #[tokio::test] + async fn from_did_webkey_gpg() { + let did_url: &str = "https://localhost/user.gpg"; + let pubkeys: &str = include_str!("../tests/user.gpg"); + + let (url, shutdown) = web_server(did_url, pubkeys).unwrap(); + PROXY.with(|proxy| { + proxy.replace(Some(url)); + }); + let (res_meta, doc_opt, _doc_meta) = DIDWebKey + .resolve( + "did:webkey:gpg:localhost:user.gpg", + &ResolutionInputMetadata::default(), + ) + .await; + assert_eq!(res_meta.error, None); + let value_expected = json!({ + "@context": "https://www.w3.org/ns/did/v1", + "assertionMethod": [ + "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935", + "did:webkey:gpg:localhost:user.gpg#6BABBD68A84D5FE3CEEB986EB77927AE619B8EB6", + "did:webkey:gpg:localhost:user.gpg#DCB1FF1899328C0EBB5DF07BD41BBBD1FE58006E" + ], + "authentication": [ + "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935", + "did:webkey:gpg:localhost:user.gpg#6BABBD68A84D5FE3CEEB986EB77927AE619B8EB6", + "did:webkey:gpg:localhost:user.gpg#DCB1FF1899328C0EBB5DF07BD41BBBD1FE58006E" + ], + "id": "did:webkey:gpg:localhost:user.gpg", + "verificationMethod": [ + { + "controller": "did:webkey:gpg:localhost:user.gpg", + "id": "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935", + "publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nComment: 0CEE 8B84 B25C 0A3C 554A 9EC1 F8FE E972 E2A1 D935\nComment: Foobar \n\n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\n2YdBRVOAnw==\n=4Vh8\n-----END PGP PUBLIC KEY BLOCK-----\n", + "type": "PgpVerificationKey2021" + }, + { + "controller": "did:webkey:gpg:localhost:user.gpg", + "id": "did:webkey:gpg:localhost:user.gpg#6BABBD68A84D5FE3CEEB986EB77927AE619B8EB6", + "publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nComment: 6BAB BD68 A84D 5FE3 CEEB 986E B779 27AE 619B 8EB6\nComment: Foobar \n\nxlIEYd3nnBMIKoZIzj0DAQcCAwRhnJmDiD35LzJXstn4zBMfpavUCSkYzyJKIYHe\nOwW4BFe+AF/ZdczzJnx8O1xndvYOFccVNAz7HMb7xPB7MDcEzRtGb29iYXIgPGZv\nb2JhckBleGFtcGxlLm9yZz7CkAQTEwgAOBYhBGurvWioTV/jzuuYbrd5J65hm462\nBQJh3eecAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJELd5J65hm462BNgB\nAKzxt0M3BpEGlAGjz4czrWX8zRdo6XiKeby5yeORfKDEAP4uOuIwE9ics9XICXUg\n1IZhOVNB2cUS6p7Q5ApaqwE3Wc5WBGHd55wSCCqGSM49AwEHAgMEN0OVHjy6Pwyp\nfTci+EKIc486T1EGeYBs/1FErq3bB44Vqr3EsOcdscSqyj3dcxXb47d0kOkiDPKm\nKTy/6ZPWsAMBCAfCeAQYEwgAIBYhBGurvWioTV/jzuuYbrd5J65hm462BQJh3eec\nAhsMAAoJELd5J65hm462KTsA/3vbivQARQMsZfGKptW/SVaKwszMQm2SE+jOESoH\ntk3MAQCjUD7O3CzMX2rCDgLBLh6hwgB3zjn8uaHM1zO9Z48HhQ==\n=Erc7\n-----END PGP PUBLIC KEY BLOCK-----\n", + "type": "PgpVerificationKey2021" + }, + { + "controller": "did:webkey:gpg:localhost:user.gpg", + "id": "did:webkey:gpg:localhost:user.gpg#DCB1FF1899328C0EBB5DF07BD41BBBD1FE58006E", + "publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nComment: DCB1 FF18 9932 8C0E BB5D F07B D41B BBD1 FE58 006E\nComment: Foobar \n\nxjMEYd3nyxYJKwYBBAHaRw8BAQdAp756gWZbZB66yTjjn52DyUvCxUgFG7aSKqYY\n7KG2KvDNG0Zvb2JhciA8Zm9vYmFyQGV4YW1wbGUub3JnPsKQBBMWCAA4FiEE3LH/\nGJkyjA67XfB71Bu70f5YAG4FAmHd58sCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC\nF4AACgkQ1Bu70f5YAG7IMQD7BEg3vAqinv1wllBpXfQov7b4+haxcADWXgmc+06D\nx1QBAMWd6Oa71iKafJKKL3Vgk5q/Sns5+xDvMJmcGbMemckMzjgEYd3nyxIKKwYB\nBAGXVQEFAQEHQECEkuj4GJuUKC0nKvyXoEA1DxJPnASFt2GPC0trMcMoAwEIB8J4\nBBgWCAAgFiEE3LH/GJkyjA67XfB71Bu70f5YAG4FAmHd58sCGwwACgkQ1Bu70f5Y\nAG6eUAEA8vwHBMR4ownA069pQ2EqGhueMoU7YQX0IQBosDf7NrMBAJCoLmuc2dGQ\nT4/C2SFSd3mgOqJXpumOyBFj6hoYkyAI\n=LgN5\n-----END PGP PUBLIC KEY BLOCK-----\n", + "type": "PgpVerificationKey2021" + } + ] + }); + let doc = doc_opt.unwrap(); + let doc_value = serde_json::to_value(doc).unwrap(); + eprintln!("doc {}", serde_json::to_string_pretty(&doc_value).unwrap()); + assert_eq!(doc_value, value_expected); + PROXY.with(|proxy| { + proxy.replace(None); + }); + shutdown().ok(); + } } diff --git a/did-webkey/tests/user.gpg b/did-webkey/tests/user.gpg new file mode 100644 index 000000000..af6f7246a --- /dev/null +++ b/did-webkey/tests/user.gpg @@ -0,0 +1,59 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +IVJ3eaA6olem6Y7IEWPqGhiTIAg= +=yjPH +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/did.rs b/src/did.rs index 93fde1661..3c74d110d 100644 --- a/src/did.rs +++ b/src/did.rs @@ -159,6 +159,8 @@ pub struct VerificationMethodMap { // TODO: make sure this JWK does not have private key material pub public_key_jwk: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub public_key_pgp: Option, + #[serde(skip_serializing_if = "Option::is_none")] // TODO: make Base58 type like Base64urlUIntString pub public_key_base58: Option, // TODO: ensure that not both key parameters are set From 9426c8d545507d4787cb192225d41ffc6404b10f Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 14:28:56 -0800 Subject: [PATCH 02/11] install nettle and capnproto on ubuntu runner required for sequoia pgp dependency --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 15f729d4b..eaf1428d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,11 @@ jobs: with: submodules: true + - name: Install additional build dependencies + run: | + sudo apt-get update + sudo apt-get install nettle-dev capnproto + - name: Cache Cargo registry and build artifacts uses: actions/cache@v2 with: From 58ff86b5daf9d5c5bd4eda599560c8aac332a3af Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 14:32:59 -0800 Subject: [PATCH 03/11] remove sequoia dep from root crate manifest --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index de074dfd8..5e9d47c0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,6 @@ p256 = { version = "0.8", optional = true, features = ["zeroize", "ecdsa"] } ssi-contexts = { version = "0.1.2", path = "contexts/" } ripemd160 = { version = "0.9", optional = true } sshkeys = "0.3" -sequoia-openpgp = "1.7" reqwest = { version = "0.11", features = ["json"] } flate2 = "1.0" bitvec = "0.20" From ffee29982bb58ca3a390f55c68d39c2278f9faf9 Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 14:40:35 -0800 Subject: [PATCH 04/11] allow sequoia features allows users to pick between which crypto lib (cng or nettle) uses flate compression by default --- did-webkey/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/did-webkey/Cargo.toml b/did-webkey/Cargo.toml index 808fc19c8..4b0de587c 100644 --- a/did-webkey/Cargo.toml +++ b/did-webkey/Cargo.toml @@ -12,6 +12,9 @@ homepage = "https://github.com/spruceid/ssi/tree/main/did-webkey/" documentation = "https://docs.rs/did-webkey/" [features] +default = ["sequoia-openpgp/crypto-nettle"] +crypto-cng = ["sequoia-openpgp/crypto-cng"] +crypto-nettle = ["sequoia-openpgp/crypto-nettle"] p256 = ["ssi/p256"] [dependencies] @@ -23,7 +26,9 @@ ssi = { version = "0.3", path = "../", features = [ async-trait = "0.1" reqwest = { version = "0.11", features = ["json"] } http = "0.2" -sequoia-openpgp = "1.7" +sequoia-openpgp = { version = "1.7", features = [ + "compression-deflate", +], default-features = false } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } sshkeys = "0.3" From c85286b3ff868681e38b6aa8594b22c43deaf47c Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 14:42:59 -0800 Subject: [PATCH 05/11] mention additional dependencies in readme --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 21e0a0c63..deb9026ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -[![](https://img.shields.io/github/workflow/status/spruceid/ssi/ci)](https://github.com/spruceid/ssi/actions?query=workflow%3Aci+branch%3Amain) [![](https://img.shields.io/badge/Rust-v1.51.0-orange)](https://www.rust-lang.org/) [![](https://img.shields.io/badge/License-Apache--2.0-green)](https://github.com/spruceid/didkit/blob/main/LICENSE) [![](https://img.shields.io/twitter/follow/sprucesystems?label=Follow&style=social)](https://twitter.com/sprucesystems) +[![](https://img.shields.io/github/workflow/status/spruceid/ssi/ci)](https://github.com/spruceid/ssi/actions?query=workflow%3Aci+branch%3Amain) +[![](https://img.shields.io/badge/Rust-v1.51.0-orange)](https://www.rust-lang.org/) +[![](https://img.shields.io/badge/License-Apache--2.0-green)](https://github.com/spruceid/didkit/blob/main/LICENSE) +[![](https://img.shields.io/twitter/follow/sprucesystems?label=Follow&style=social)](https://twitter.com/sprucesystems) SSI's documentation is currently packaged with the DIDKit documentation [here](https://spruceid.dev/docs/didkit/). @@ -14,8 +17,9 @@ including embedded systems. This library is embedded in the the cross-platform ![DIDKit core components](https://user-images.githubusercontent.com/37127325/132885372-9cdf586e-ba6f-44c8-8b83-f72f16d86107.png) ## Maturity Disclaimer -In the v0.1 release on January 27th, 2021, SSI has not yet undergone a -formal security audit and to desired levels of confidence for suitable use in + +In the v0.1 release on January 27th, 2021, SSI has not yet undergone a formal +security audit and to desired levels of confidence for suitable use in production systems. This implementation is currently suitable for exploratory work and experimentation only. We welcome feedback on the usability, architecture, and security of this implementation and are committed to a @@ -34,6 +38,8 @@ packages. ``` clang openssl-devel +nettle-dev +capnproto ``` ## Install From 38e33d718525252797bb815c994e3406e81c68a5 Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 14:43:56 -0800 Subject: [PATCH 06/11] remove resolved note --- did-webkey/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index 4defe4e8c..b3892b03e 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -71,8 +71,6 @@ fn parse_pubkeys_gpg( fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL), String> { let vm_url = DIDURL { did: did.to_string(), - // NOTE: should this be key_id or fingerprint? - // https://docs.rs/sequoia-openpgp/1.7.0/sequoia_openpgp/struct.Cert.html#method.keyid fragment: Some(cert.fingerprint().to_string()), ..Default::default() }; From d0d964a126578a0d9544030f7437d174db65306a Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 13 Jan 2022 18:48:22 -0800 Subject: [PATCH 07/11] impl using rpgp add note regarding bug pgp::composed::signed_key::parse::from_armor_many seems to yield only one key when multiple are present use feature resolver v2 (necessary since nettle will not compile on wasm32) https://doc.rust-lang.org/cargo/reference/features.html#feature-resolver-version-2 add notes on pgp specific dependencies --- Cargo.toml | 1 + README.md | 10 ++++++ did-webkey/Cargo.toml | 17 +++++++-- did-webkey/src/lib.rs | 80 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e9d47c0d..1073469a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "Apache-2.0" description = "Core library for Verifiable Credentials and Decentralized Identifiers." repository = "https://github.com/spruceid/ssi/" documentation = "https://docs.rs/ssi/" +resolver = "2" exclude = ["json-ld-api/*", "json-ld-normalization/*"] diff --git a/README.md b/README.md index deb9026ca..25a848b0a 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,20 @@ packages. ``` clang openssl-devel +``` + +If using feature `did-webkey/sequoia-openpgp` for PGP support, the following +dependencies are also needed: + +``` nettle-dev capnproto ``` +If using feature +[`did-webkey/crypto-cng`](https://gitlab.com/sequoia-pgp/sequoia#cryptography), +only `capnproto` is needed. + ## Install ### Crates.io diff --git a/did-webkey/Cargo.toml b/did-webkey/Cargo.toml index 4b0de587c..16c67f48b 100644 --- a/did-webkey/Cargo.toml +++ b/did-webkey/Cargo.toml @@ -25,14 +25,25 @@ ssi = { version = "0.3", path = "../", features = [ ], default-features = false } async-trait = "0.1" reqwest = { version = "0.11", features = ["json"] } +hex = "0.4" http = "0.2" -sequoia-openpgp = { version = "1.7", features = [ - "compression-deflate", -], default-features = false } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } sshkeys = "0.3" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +sequoia-openpgp = { version = "1.7", features = [ + "compression-deflate", +], default-features = false, optional = true } +# HACK: temp, point to crates once pgp publishes a version that doesn't require zeroize=1.3.0 +pgp = { git = "https://github.com/rpgp/rpgp", rev = "21081b6aaaaa5750ab937cfef30bae879a740d23", optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +# HACK: same thing as above +pgp = { git = "https://github.com/rpgp/rpgp", rev = "21081b6aaaaa5750ab937cfef30bae879a740d23", features = [ + "wasm", +] } + [target.'cfg(target_os = "android")'.dependencies.reqwest] version = "0.11" features = ["json", "native-tls-vendored"] diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index b3892b03e..cc57d1b29 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -3,11 +3,19 @@ use core::str::FromStr; use async_trait::async_trait; use serde::{Deserialize, Serialize}; +#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] use openpgp::{ cert::prelude::*, parse::{PacketParser, Parse}, serialize::SerializeInto, }; +#[cfg(any(target_arch = "wasm32", feature = "pgp"))] +use pgp::{ + composed::{PublicOrSecret, SignedPublicKey}, + errors::Error as PgpError, + types::KeyTrait, +}; +#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] use sequoia_openpgp as openpgp; use sshkeys::PublicKeyKind; use ssi::did::{DIDMethod, Document, VerificationMethod, VerificationMethodMap, DIDURL}; @@ -16,6 +24,11 @@ use ssi::did_resolve::{ }; use ssi::ssh::ssh_pkk_to_jwk; +#[cfg(all(feature = "sequoia-openpgp", feature = "pgp"))] +compile_error!( + "Feature \"sequoia-openpgp\" and feature \"pgp\" cannot be enabled at the same time" +); + // For testing, enable handling requests at localhost. #[cfg(test)] use std::cell::RefCell; @@ -44,6 +57,7 @@ impl FromStr for DIDWebKeyType { } } +#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] fn parse_pubkeys_gpg( did: &str, bytes: Vec, @@ -68,6 +82,7 @@ fn parse_pubkeys_gpg( Ok((vm_maps, did_urls)) } +#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL), String> { let vm_url = DIDURL { did: did.to_string(), @@ -92,6 +107,68 @@ fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL) Ok((vm_map, vm_url)) } +#[cfg(any(target_arch = "wasm32", feature = "pgp"))] +fn parse_pubkeys_gpg( + did: &str, + bytes: Vec, +) -> Result<(Vec, Vec), String> { + use std::io::Cursor; + + let mut did_urls = Vec::new(); + let mut vm_maps = Vec::new(); + + let c = Cursor::new(bytes); + // BUG: This seems to yield only one key. + let keys = pgp::composed::signed_key::parse::from_armor_many(c) + .map_err(|e| format!("Unable to parse GPG keyring: {}", e))? + .0 + .collect::, PgpError>>() + .map_err(|e| format!("Unable to parse GPG keyring: {}", e))?; + + for key in keys { + // ignore if secret key (which shouldn't happen) + if let PublicOrSecret::Public(pk) = key { + let (vm_map, did_url) = gpg_pk_to_vm(did, pk).map_err(|e| { + format!( + "Unable to convert GPG public key to verification method: {}", + e + ) + })?; + vm_maps.push(vm_map); + did_urls.push(did_url); + } + } + + Ok((vm_maps, did_urls)) +} + +#[cfg(any(target_arch = "wasm32", feature = "pgp"))] +fn gpg_pk_to_vm( + did: &str, + key: SignedPublicKey, +) -> Result<(VerificationMethodMap, DIDURL), String> { + let fingerprint: String = hex::encode_upper(key.fingerprint()); + + let vm_url = DIDURL { + did: did.to_string(), + fragment: Some(fingerprint), + ..Default::default() + }; + + let armored_pgp = key + .to_armored_string(None) + .map_err(|e| format!("Failed to re-serialize cert: {}", e))?; + + let vm_map = VerificationMethodMap { + id: vm_url.to_string(), + type_: "PgpVerificationKey2021".to_string(), + public_key_pgp: Some(armored_pgp), + controller: did.to_string(), + ..Default::default() + }; + Ok((vm_map, vm_url)) +} + fn pk_to_vm_ed25519( did: &str, pk: sshkeys::Ed25519PublicKey, @@ -420,7 +497,7 @@ mod tests { let (mut parts, body) = Response::::default().into_parts(); parts.status = hyper::StatusCode::NOT_FOUND; let response = Response::from_parts(parts, body); - return Ok::<_, hyper::Error>(response); + Ok::<_, hyper::Error>(response) })) }); let server = Server::try_bind(&addr)?.serve(make_svc); @@ -526,6 +603,7 @@ mod tests { ) .await; assert_eq!(res_meta.error, None); + // NOTE: sequoia-pgp and rpgp will likely produce slightly different output let value_expected = json!({ "@context": "https://www.w3.org/ns/did/v1", "assertionMethod": [ From 07fc97e9f333871c1e3e6be0a587a5e510bf4c66 Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 20 Jan 2022 16:27:50 -0800 Subject: [PATCH 08/11] simplify feature flags also include test output for both pgp libs --- did-webkey/src/lib.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index cc57d1b29..4b41b426c 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -3,7 +3,7 @@ use core::str::FromStr; use async_trait::async_trait; use serde::{Deserialize, Serialize}; -#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] +#[cfg(feature = "sequoia-openpgp")] use openpgp::{ cert::prelude::*, parse::{PacketParser, Parse}, @@ -15,7 +15,7 @@ use pgp::{ errors::Error as PgpError, types::KeyTrait, }; -#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] +#[cfg(feature = "sequoia-openpgp")] use sequoia_openpgp as openpgp; use sshkeys::PublicKeyKind; use ssi::did::{DIDMethod, Document, VerificationMethod, VerificationMethodMap, DIDURL}; @@ -57,7 +57,7 @@ impl FromStr for DIDWebKeyType { } } -#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] +#[cfg(feature = "sequoia-openpgp")] fn parse_pubkeys_gpg( did: &str, bytes: Vec, @@ -82,7 +82,7 @@ fn parse_pubkeys_gpg( Ok((vm_maps, did_urls)) } -#[cfg(all(not(target_arch = "wasm32"), feature = "sequoia-openpgp"))] +#[cfg(feature = "sequoia-openpgp")] fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL), String> { let vm_url = DIDURL { did: did.to_string(), @@ -603,7 +603,8 @@ mod tests { ) .await; assert_eq!(res_meta.error, None); - // NOTE: sequoia-pgp and rpgp will likely produce slightly different output + + #[cfg(feature = "sequoia-openpgp")] let value_expected = json!({ "@context": "https://www.w3.org/ns/did/v1", "assertionMethod": [ @@ -638,6 +639,28 @@ mod tests { } ] }); + + #[cfg(feature = "pgp")] + // BUG: rpgp will only yield the first pubkey + let value_expected = json!({ + "@context": "https://www.w3.org/ns/did/v1", + "assertionMethod": [ + "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935" + ], + "authentication": [ + "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935" + ], + "id": "did:webkey:gpg:localhost:user.gpg", + "verificationMethod": [ + { + "controller": "did:webkey:gpg:localhost:user.gpg", + "id": "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935", + "publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\n2YdBRVOAnw==\n=A/sJ\n-----END PGP PUBLIC KEY BLOCK-----\n", + "type": "PgpVerificationKey2021" + } + ] + }); + let doc = doc_opt.unwrap(); let doc_value = serde_json::to_value(doc).unwrap(); eprintln!("doc {}", serde_json::to_string_pretty(&doc_value).unwrap()); From 29fdcf02df7d1a3f9da7b86583078e8a6d6242f4 Mon Sep 17 00:00:00 2001 From: Allen Date: Fri, 21 Jan 2022 08:22:26 -0800 Subject: [PATCH 09/11] rework errors include anyhow; upgrade rest of deps --- did-webkey/Cargo.toml | 27 +++++----- did-webkey/src/lib.rs | 120 +++++++++++------------------------------- 2 files changed, 44 insertions(+), 103 deletions(-) diff --git a/did-webkey/Cargo.toml b/did-webkey/Cargo.toml index 16c67f48b..7b54c7e97 100644 --- a/did-webkey/Cargo.toml +++ b/did-webkey/Cargo.toml @@ -23,16 +23,17 @@ ssi = { version = "0.3", path = "../", features = [ "ring", "p256", ], default-features = false } -async-trait = "0.1" -reqwest = { version = "0.11", features = ["json"] } -hex = "0.4" -http = "0.2" -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } -sshkeys = "0.3" +anyhow = "1.0.52" +async-trait = "0.1.52" +reqwest = { version = "0.11.9", features = ["json"] } +hex = "0.4.3" +http = "0.2.6" +serde_json = "1.0.75" +serde = { version = "1.0.134", features = ["derive"] } +sshkeys = "0.3.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -sequoia-openpgp = { version = "1.7", features = [ +sequoia-openpgp = { version = "1.7.0", features = [ "compression-deflate", ], default-features = false, optional = true } # HACK: temp, point to crates once pgp publishes a version that doesn't require zeroize=1.3.0 @@ -45,11 +46,11 @@ pgp = { git = "https://github.com/rpgp/rpgp", rev = "21081b6aaaaa5750ab937cfef30 ] } [target.'cfg(target_os = "android")'.dependencies.reqwest] -version = "0.11" +version = "0.11.9" features = ["json", "native-tls-vendored"] [dev-dependencies] -tokio = { version = "1.0", features = ["macros"] } -async-std = { version = "1.9", features = ["attributes"] } -futures = "0.3" -hyper = { version = "0.14", features = ["server", "client", "http1", "stream"] } +tokio = { version = "1.15.0", features = ["macros"] } +async-std = { version = "1.10.0", features = ["attributes"] } +futures = "0.3.19" +hyper = { version = "0.14.16", features = ["server", "client", "http1", "stream"] } diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index 4b41b426c..62fa83a76 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -3,6 +3,7 @@ use core::str::FromStr; use async_trait::async_trait; use serde::{Deserialize, Serialize}; +use anyhow::{anyhow, Result}; #[cfg(feature = "sequoia-openpgp")] use openpgp::{ cert::prelude::*, @@ -61,20 +62,14 @@ impl FromStr for DIDWebKeyType { fn parse_pubkeys_gpg( did: &str, bytes: Vec, -) -> Result<(Vec, Vec), String> { +) -> Result<(Vec, Vec)> { let mut did_urls = Vec::new(); let mut vm_maps = Vec::new(); - let ppr = PacketParser::from_bytes(&bytes) - .map_err(|e| format!("Unable to parse GPG keyring: {}", e))?; + let ppr = PacketParser::from_bytes(&bytes)?; for certo in CertParser::from(ppr) { - let cert = certo.map_err(|e| format!("Error reading keyring: {}", e))?; - let (vm_map, did_url) = gpg_pk_to_vm(did, cert).map_err(|e| { - format!( - "Unable to convert GPG public key to verification method: {}", - e - ) - })?; + let cert = certo?; + let (vm_map, did_url) = gpg_pk_to_vm(did, cert)?; vm_maps.push(vm_map); did_urls.push(did_url); } @@ -83,19 +78,14 @@ fn parse_pubkeys_gpg( } #[cfg(feature = "sequoia-openpgp")] -fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL), String> { +fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL)> { let vm_url = DIDURL { did: did.to_string(), fragment: Some(cert.fingerprint().to_string()), ..Default::default() }; - let armored_pgp = String::from_utf8( - cert.armored() - .to_vec() - .map_err(|e| format!("Failed to re-serialize cert: {}", e))?, - ) - .map_err(|e| format!("Failed to read cert as utf8: {}", e))?; + let armored_pgp = String::from_utf8(cert.armored().to_vec()?)?; let vm_map = VerificationMethodMap { id: vm_url.to_string(), @@ -111,7 +101,7 @@ fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL) fn parse_pubkeys_gpg( did: &str, bytes: Vec, -) -> Result<(Vec, Vec), String> { +) -> Result<(Vec, Vec)> { use std::io::Cursor; let mut did_urls = Vec::new(); @@ -119,21 +109,14 @@ fn parse_pubkeys_gpg( let c = Cursor::new(bytes); // BUG: This seems to yield only one key. - let keys = pgp::composed::signed_key::parse::from_armor_many(c) - .map_err(|e| format!("Unable to parse GPG keyring: {}", e))? + let keys = pgp::composed::signed_key::parse::from_armor_many(c)? .0 - .collect::, PgpError>>() - .map_err(|e| format!("Unable to parse GPG keyring: {}", e))?; + .collect::, PgpError>>()?; for key in keys { // ignore if secret key (which shouldn't happen) if let PublicOrSecret::Public(pk) = key { - let (vm_map, did_url) = gpg_pk_to_vm(did, pk).map_err(|e| { - format!( - "Unable to convert GPG public key to verification method: {}", - e - ) - })?; + let (vm_map, did_url) = gpg_pk_to_vm(did, pk)?; vm_maps.push(vm_map); did_urls.push(did_url); } @@ -143,10 +126,7 @@ fn parse_pubkeys_gpg( } #[cfg(any(target_arch = "wasm32", feature = "pgp"))] -fn gpg_pk_to_vm( - did: &str, - key: SignedPublicKey, -) -> Result<(VerificationMethodMap, DIDURL), String> { +fn gpg_pk_to_vm(did: &str, key: SignedPublicKey) -> Result<(VerificationMethodMap, DIDURL)> { let fingerprint: String = hex::encode_upper(key.fingerprint()); let vm_url = DIDURL { @@ -155,9 +135,7 @@ fn gpg_pk_to_vm( ..Default::default() }; - let armored_pgp = key - .to_armored_string(None) - .map_err(|e| format!("Failed to re-serialize cert: {}", e))?; + let armored_pgp = key.to_armored_string(None)?; let vm_map = VerificationMethodMap { id: vm_url.to_string(), @@ -172,15 +150,9 @@ fn gpg_pk_to_vm( fn pk_to_vm_ed25519( did: &str, pk: sshkeys::Ed25519PublicKey, -) -> Result<(VerificationMethodMap, DIDURL), String> { - let jwk = match ssh_pkk_to_jwk(&PublicKeyKind::Ed25519(pk)) { - Err(err) => return Err(format!("Unable to convert SSH key to JWK: {}", err)), - Ok(jwk) => jwk, - }; - let thumbprint = match jwk.thumbprint() { - Err(err) => return Err(format!("Unable to calculate JWK thumbprint: {}", err)), - Ok(t) => t, - }; +) -> Result<(VerificationMethodMap, DIDURL)> { + let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Ed25519(pk))?; + let thumbprint = jwk.thumbprint()?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint), @@ -199,15 +171,9 @@ fn pk_to_vm_ed25519( fn pk_to_vm_ecdsa( did: &str, pk: sshkeys::EcdsaPublicKey, -) -> Result<(VerificationMethodMap, DIDURL), String> { - let jwk = match ssh_pkk_to_jwk(&PublicKeyKind::Ecdsa(pk)) { - Err(err) => return Err(format!("Unable to convert SSH key to JWK: {}", err)), - Ok(jwk) => jwk, - }; - let thumbprint = match jwk.thumbprint() { - Err(err) => return Err(format!("Unable to calculate JWK thumbprint: {}", err)), - Ok(t) => t, - }; +) -> Result<(VerificationMethodMap, DIDURL)> { + let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Ecdsa(pk))?; + let thumbprint = jwk.thumbprint()?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint), @@ -223,18 +189,9 @@ fn pk_to_vm_ecdsa( Ok((vm_map, vm_url)) } -fn pk_to_vm_rsa( - did: &str, - pk: sshkeys::RsaPublicKey, -) -> Result<(VerificationMethodMap, DIDURL), String> { - let jwk = match ssh_pkk_to_jwk(&PublicKeyKind::Rsa(pk)) { - Err(err) => return Err(format!("Unable to convert SSH key to JWK: {}", err)), - Ok(jwk) => jwk, - }; - let thumbprint = match jwk.thumbprint() { - Err(err) => return Err(format!("Unable to calculate JWK thumbprint: {}", err)), - Ok(t) => t, - }; +fn pk_to_vm_rsa(did: &str, pk: sshkeys::RsaPublicKey) -> Result<(VerificationMethodMap, DIDURL)> { + let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Rsa(pk))?; + let thumbprint = jwk.thumbprint()?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint), @@ -250,14 +207,11 @@ fn pk_to_vm_rsa( Ok((vm_map, vm_url)) } -fn pk_to_vm_dsa( - _did: &str, - _pk: sshkeys::DsaPublicKey, -) -> Result<(VerificationMethodMap, DIDURL), String> { - Err(String::from("Unsupported DSA Key")) +fn pk_to_vm_dsa(_did: &str, _pk: sshkeys::DsaPublicKey) -> Result<(VerificationMethodMap, DIDURL)> { + Err(anyhow!("Unsupported DSA Key")) } -fn pk_to_vm(did: &str, pk: sshkeys::PublicKey) -> Result<(VerificationMethodMap, DIDURL), String> { +fn pk_to_vm(did: &str, pk: sshkeys::PublicKey) -> Result<(VerificationMethodMap, DIDURL)> { match pk.kind { PublicKeyKind::Rsa(pk) => pk_to_vm_rsa(did, pk), PublicKeyKind::Dsa(pk) => pk_to_vm_dsa(did, pk), @@ -269,28 +223,14 @@ fn pk_to_vm(did: &str, pk: sshkeys::PublicKey) -> Result<(VerificationMethodMap, fn parse_pubkeys_ssh( did: &str, bytes: Vec, -) -> Result<(Vec, Vec), String> { - let lines = match String::from_utf8(bytes) { - Ok(string) => string, - Err(err) => return Err(format!("Unable to parse SSH keys: {}", err)), - }; +) -> Result<(Vec, Vec)> { + let lines = String::from_utf8(bytes)?; let mut did_urls = Vec::new(); let mut vm_maps = Vec::new(); let lines = lines.trim().split('\n'); for line in lines { - let pk = match sshkeys::PublicKey::from_string(line) { - Ok(pk) => pk, - Err(err) => return Err(format!("Unable to parse SSH key: {}", err)), - }; - let (vm_map, did_url) = match pk_to_vm(did, pk) { - Ok(pk) => pk, - Err(err) => { - return Err(format!( - "Unable to convert SSH public key to verification method: {}", - err - )) - } - }; + let pk = sshkeys::PublicKey::from_string(line)?; + let (vm_map, did_url) = pk_to_vm(did, pk)?; vm_maps.push(vm_map); did_urls.push(did_url); } @@ -301,7 +241,7 @@ fn parse_pubkeys( did: &str, type_: DIDWebKeyType, bytes: Vec, -) -> Result<(Vec, Vec), String> { +) -> Result<(Vec, Vec)> { match type_ { DIDWebKeyType::Gpg => parse_pubkeys_gpg(did, bytes), DIDWebKeyType::Ssh => parse_pubkeys_ssh(did, bytes), From aaae84f0005290b035db6d052d83e3962beff4f3 Mon Sep 17 00:00:00 2001 From: Allen Date: Wed, 16 Feb 2022 10:14:31 -0800 Subject: [PATCH 10/11] remove rpgp impl --- did-webkey/Cargo.toml | 27 +++++++------- did-webkey/src/lib.rs | 84 +------------------------------------------ 2 files changed, 16 insertions(+), 95 deletions(-) diff --git a/did-webkey/Cargo.toml b/did-webkey/Cargo.toml index 7b54c7e97..56d4ed2e9 100644 --- a/did-webkey/Cargo.toml +++ b/did-webkey/Cargo.toml @@ -12,17 +12,15 @@ homepage = "https://github.com/spruceid/ssi/tree/main/did-webkey/" documentation = "https://docs.rs/did-webkey/" [features] -default = ["sequoia-openpgp/crypto-nettle"] +default = ["ssi/ring", "crypto-nettle"] crypto-cng = ["sequoia-openpgp/crypto-cng"] crypto-nettle = ["sequoia-openpgp/crypto-nettle"] p256 = ["ssi/p256"] [dependencies] -ssi = { version = "0.3", path = "../", features = [ - "rand", - "ring", - "p256", -], default-features = false } +ssi = { version = "0.3", path = "../", default-features = false, features = [ + "secp256r1", +] } anyhow = "1.0.52" async-trait = "0.1.52" reqwest = { version = "0.11.9", features = ["json"] } @@ -36,15 +34,15 @@ sshkeys = "0.3.1" sequoia-openpgp = { version = "1.7.0", features = [ "compression-deflate", ], default-features = false, optional = true } -# HACK: temp, point to crates once pgp publishes a version that doesn't require zeroize=1.3.0 -pgp = { git = "https://github.com/rpgp/rpgp", rev = "21081b6aaaaa5750ab937cfef30bae879a740d23", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -# HACK: same thing as above -pgp = { git = "https://github.com/rpgp/rpgp", rev = "21081b6aaaaa5750ab937cfef30bae879a740d23", features = [ - "wasm", +sequoia-openpgp = { version = "1.7.0", default-features = false, features = [ + "crypto-rust", + "allow-experimental-crypto", + "allow-variable-time-crypto", ] } + [target.'cfg(target_os = "android")'.dependencies.reqwest] version = "0.11.9" features = ["json", "native-tls-vendored"] @@ -53,4 +51,9 @@ features = ["json", "native-tls-vendored"] tokio = { version = "1.15.0", features = ["macros"] } async-std = { version = "1.10.0", features = ["attributes"] } futures = "0.3.19" -hyper = { version = "0.14.16", features = ["server", "client", "http1", "stream"] } +hyper = { version = "0.14.16", features = [ + "server", + "client", + "http1", + "stream", +] } diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index 62fa83a76..60ea8e45c 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -10,12 +10,6 @@ use openpgp::{ parse::{PacketParser, Parse}, serialize::SerializeInto, }; -#[cfg(any(target_arch = "wasm32", feature = "pgp"))] -use pgp::{ - composed::{PublicOrSecret, SignedPublicKey}, - errors::Error as PgpError, - types::KeyTrait, -}; #[cfg(feature = "sequoia-openpgp")] use sequoia_openpgp as openpgp; use sshkeys::PublicKeyKind; @@ -25,11 +19,6 @@ use ssi::did_resolve::{ }; use ssi::ssh::ssh_pkk_to_jwk; -#[cfg(all(feature = "sequoia-openpgp", feature = "pgp"))] -compile_error!( - "Feature \"sequoia-openpgp\" and feature \"pgp\" cannot be enabled at the same time" -); - // For testing, enable handling requests at localhost. #[cfg(test)] use std::cell::RefCell; @@ -97,56 +86,6 @@ fn gpg_pk_to_vm(did: &str, cert: Cert) -> Result<(VerificationMethodMap, DIDURL) Ok((vm_map, vm_url)) } -#[cfg(any(target_arch = "wasm32", feature = "pgp"))] -fn parse_pubkeys_gpg( - did: &str, - bytes: Vec, -) -> Result<(Vec, Vec)> { - use std::io::Cursor; - - let mut did_urls = Vec::new(); - let mut vm_maps = Vec::new(); - - let c = Cursor::new(bytes); - // BUG: This seems to yield only one key. - let keys = pgp::composed::signed_key::parse::from_armor_many(c)? - .0 - .collect::, PgpError>>()?; - - for key in keys { - // ignore if secret key (which shouldn't happen) - if let PublicOrSecret::Public(pk) = key { - let (vm_map, did_url) = gpg_pk_to_vm(did, pk)?; - vm_maps.push(vm_map); - did_urls.push(did_url); - } - } - - Ok((vm_maps, did_urls)) -} - -#[cfg(any(target_arch = "wasm32", feature = "pgp"))] -fn gpg_pk_to_vm(did: &str, key: SignedPublicKey) -> Result<(VerificationMethodMap, DIDURL)> { - let fingerprint: String = hex::encode_upper(key.fingerprint()); - - let vm_url = DIDURL { - did: did.to_string(), - fragment: Some(fingerprint), - ..Default::default() - }; - - let armored_pgp = key.to_armored_string(None)?; - - let vm_map = VerificationMethodMap { - id: vm_url.to_string(), - type_: "PgpVerificationKey2021".to_string(), - public_key_pgp: Some(armored_pgp), - controller: did.to_string(), - ..Default::default() - }; - Ok((vm_map, vm_url)) -} - fn pk_to_vm_ed25519( did: &str, pk: sshkeys::Ed25519PublicKey, @@ -528,6 +467,7 @@ mod tests { } #[tokio::test] + #[cfg(feature = "sequoia-openpgp")] async fn from_did_webkey_gpg() { let did_url: &str = "https://localhost/user.gpg"; let pubkeys: &str = include_str!("../tests/user.gpg"); @@ -544,7 +484,6 @@ mod tests { .await; assert_eq!(res_meta.error, None); - #[cfg(feature = "sequoia-openpgp")] let value_expected = json!({ "@context": "https://www.w3.org/ns/did/v1", "assertionMethod": [ @@ -580,27 +519,6 @@ mod tests { ] }); - #[cfg(feature = "pgp")] - // BUG: rpgp will only yield the first pubkey - let value_expected = json!({ - "@context": "https://www.w3.org/ns/did/v1", - "assertionMethod": [ - "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935" - ], - "authentication": [ - "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935" - ], - "id": "did:webkey:gpg:localhost:user.gpg", - "verificationMethod": [ - { - "controller": "did:webkey:gpg:localhost:user.gpg", - "id": "did:webkey:gpg:localhost:user.gpg#0CEE8B84B25C0A3C554A9EC1F8FEE972E2A1D935", - "publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\n2YdBRVOAnw==\n=A/sJ\n-----END PGP PUBLIC KEY BLOCK-----\n", - "type": "PgpVerificationKey2021" - } - ] - }); - let doc = doc_opt.unwrap(); let doc_value = serde_json::to_value(doc).unwrap(); eprintln!("doc {}", serde_json::to_string_pretty(&doc_value).unwrap()); From 1d55f001915ada8b97f8ae387f8a08db7ee1a596 Mon Sep 17 00:00:00 2001 From: Allen Date: Wed, 16 Feb 2022 10:21:14 -0800 Subject: [PATCH 11/11] add context for jwk fingerprint errors --- did-webkey/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/did-webkey/src/lib.rs b/did-webkey/src/lib.rs index 60ea8e45c..60ac5f5c1 100644 --- a/did-webkey/src/lib.rs +++ b/did-webkey/src/lib.rs @@ -3,7 +3,7 @@ use core::str::FromStr; use async_trait::async_trait; use serde::{Deserialize, Serialize}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; #[cfg(feature = "sequoia-openpgp")] use openpgp::{ cert::prelude::*, @@ -91,7 +91,9 @@ fn pk_to_vm_ed25519( pk: sshkeys::Ed25519PublicKey, ) -> Result<(VerificationMethodMap, DIDURL)> { let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Ed25519(pk))?; - let thumbprint = jwk.thumbprint()?; + let thumbprint = jwk + .thumbprint() + .context("Unable to calculate JWK thumbprint")?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint), @@ -112,7 +114,9 @@ fn pk_to_vm_ecdsa( pk: sshkeys::EcdsaPublicKey, ) -> Result<(VerificationMethodMap, DIDURL)> { let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Ecdsa(pk))?; - let thumbprint = jwk.thumbprint()?; + let thumbprint = jwk + .thumbprint() + .context("Unable to calculate JWK thumbprint")?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint), @@ -130,7 +134,9 @@ fn pk_to_vm_ecdsa( fn pk_to_vm_rsa(did: &str, pk: sshkeys::RsaPublicKey) -> Result<(VerificationMethodMap, DIDURL)> { let jwk = ssh_pkk_to_jwk(&PublicKeyKind::Rsa(pk))?; - let thumbprint = jwk.thumbprint()?; + let thumbprint = jwk + .thumbprint() + .context("Unable to calculate JWK thumbprint")?; let vm_url = DIDURL { did: did.to_string(), fragment: Some(thumbprint),