From 7cfb6e7d18c46d4d37c056601c077563e50195a4 Mon Sep 17 00:00:00 2001 From: Allen Date: Thu, 6 Jan 2022 18:52:40 -0800 Subject: [PATCH] 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