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

Implement parsing GPG keys #373

Merged
merged 11 commits into from
Feb 24, 2022
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/target
Cargo.lock

.vscode
17 changes: 10 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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"
Expand All @@ -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"
fairingrey marked this conversation as resolved.
Show resolved Hide resolved
reqwest = { version = "0.11", features = ["json"] }
flate2 = "1.0"
bitvec = "0.20"
Expand Down Expand Up @@ -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" }
Expand Down
7 changes: 6 additions & 1 deletion did-webkey/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
141 changes: 126 additions & 15 deletions did-webkey/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -39,11 +45,53 @@ impl FromStr for DIDWebKeyType {
}

fn parse_pubkeys_gpg(
_did: &str,
_bytes: Vec<u8>,
did: &str,
bytes: Vec<u8>,
) -> Result<(Vec<VerificationMethodMap>, Vec<DIDURL>), 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(
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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));
});
Expand Down Expand Up @@ -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 <foobar@example.org>\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 <foobar@example.org>\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 <foobar@example.org>\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();
}
}
59 changes: 59 additions & 0 deletions did-webkey/tests/user.gpg
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
IVJ3eaA6olem6Y7IEWPqGhiTIAg=
=yjPH
-----END PGP PUBLIC KEY BLOCK-----
2 changes: 2 additions & 0 deletions src/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ pub struct VerificationMethodMap {
// TODO: make sure this JWK does not have private key material
pub public_key_jwk: Option<JWK>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key_pgp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
// TODO: make Base58 type like Base64urlUIntString
pub public_key_base58: Option<String>,
// TODO: ensure that not both key parameters are set
Expand Down