From a585e0ccabd624bf0fa24b61221bf5546086fcf0 Mon Sep 17 00:00:00 2001 From: ianhamilton87 Date: Wed, 18 Sep 2024 12:07:53 -0400 Subject: [PATCH] Add Support To Resolve JWK_JCS-PUB encoded did:key DIDs (#600) --- crates/jwk/src/lib.rs | 20 ++++++++++++++++++ crates/jwk/src/multicodec.rs | 41 +++++++++++++++++++++++++++++++++++- tests/jwk_jcs-pub.json | 6 ++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/jwk_jcs-pub.json diff --git a/crates/jwk/src/lib.rs b/crates/jwk/src/lib.rs index fb087a315..f92380431 100644 --- a/crates/jwk/src/lib.rs +++ b/crates/jwk/src/lib.rs @@ -103,6 +103,14 @@ impl FromStr for JWK { } } +impl TryFrom<&[u8]> for JWK { + type Error = serde_json::Error; + + fn try_from(bytes: &[u8]) -> Result { + serde_json::from_slice(bytes) + } +} + impl TryFrom for JWK { type Error = serde_json::Error; @@ -1319,6 +1327,7 @@ mod tests { const RSA_DER: &[u8] = include_bytes!("../../../tests/rsa2048-2020-08-25.der"); const RSA_PK_DER: &[u8] = include_bytes!("../../../tests/rsa2048-2020-08-25-pk.der"); const ED25519_JSON: &str = include_str!("../../../tests/ed25519-2020-10-18.json"); + const JWK_JCS_JSON: &[u8] = include_bytes!("../../../tests/jwk_jcs-pub.json"); #[test] fn jwk_to_from_der_rsa() { @@ -1330,6 +1339,17 @@ mod tests { assert_eq!(key.to_public().params, Params::RSA(rsa_params)); } + #[test] + fn jwk_try_from_bytes() { + let actual_jwk: JWK = JWK::try_from(JWK_JCS_JSON).unwrap(); + let actual_params: Params = actual_jwk.params; + if let Params::EC(ref ec_params) = actual_params { + assert_eq!(ec_params.curve.as_deref(), Some("P-256")); + } else { + panic!("actual_params is not of type Params::EC"); + } + } + #[test] fn rsa_from_str() { let _key: JWK = serde_json::from_str(RSA_JSON).unwrap(); diff --git a/crates/jwk/src/multicodec.rs b/crates/jwk/src/multicodec.rs index de8532ca6..9e12ac8b2 100644 --- a/crates/jwk/src/multicodec.rs +++ b/crates/jwk/src/multicodec.rs @@ -1,4 +1,5 @@ -use ssi_multicodec::{MultiEncoded, MultiEncodedBuf}; +use ssi_multicodec::{Codec, MultiEncoded, MultiEncodedBuf}; +use std::borrow::Cow; use crate::{Error, Params, JWK}; @@ -47,6 +48,9 @@ impl JWK { ssi_multicodec::BLS12_381_G2_PUB => { crate::bls12381g2_parse(k).map_err(FromMulticodecError::Bls12381G2Pub) } + ssi_multicodec::JWK_JCS_PUB => { + JWK::from_bytes(k).map_err(FromMulticodecError::JwkJcsPub) + } _ => Err(FromMulticodecError::UnsupportedCodec(codec)), } } @@ -123,6 +127,18 @@ impl JWK { } } +impl Codec for JWK { + const CODEC: u64 = ssi_multicodec::JWK_JCS_PUB; + + fn to_bytes(&self) -> Cow<[u8]> { + Cow::Owned(serde_jcs::to_vec(self).unwrap()) + } + + fn from_bytes(bytes: &[u8]) -> Result { + Self::try_from(bytes).map_err(|_| ssi_multicodec::Error::InvalidData) + } +} + #[derive(Debug, thiserror::Error)] pub enum FromMulticodecError { #[cfg(feature = "rsa")] @@ -165,6 +181,9 @@ pub enum FromMulticodecError { #[error(transparent)] Bls12381G2Pub(ssi_bbs::Error), + #[error(transparent)] + JwkJcsPub(ssi_multicodec::Error), + /// Unexpected multibase (multicodec) key prefix multicodec #[error("Unsupported multicodec key type 0x{0:x}")] UnsupportedCodec(u64), @@ -184,3 +203,23 @@ pub enum ToMulticodecError { #[error("invalid input key: {0}")] InvalidInputKey(Error), } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(feature = "secp256r1")] + fn test_multicodec_jwk_jcs_pub() { + let jwk = JWK::generate_p256(); + // Note: can't use JWK::to_multicodec() because it's based on the particular key within the JWK + // it will see the P256 key and assign a multicodec of 0x1200. + // For jwk_jcs_pub multicodecs, we can only decode them + let jwk_buf = MultiEncodedBuf::encode(&jwk); + let (codec, data) = jwk_buf.parts(); + assert_eq!(codec, ssi_multicodec::JWK_JCS_PUB); + assert_eq!(*data, jwk.to_bytes().into_owned()); + let jwk2 = JWK::from_multicodec(&jwk_buf).unwrap(); + assert_eq!(jwk, jwk2); + } +} diff --git a/tests/jwk_jcs-pub.json b/tests/jwk_jcs-pub.json new file mode 100644 index 000000000..ee0545982 --- /dev/null +++ b/tests/jwk_jcs-pub.json @@ -0,0 +1,6 @@ +{ + "crv": "P-256", + "kty": "EC", + "x": "g3fsv1xpWPH099LIUn_zJoOF5Ur8xobyzZwX9m_dJ4E", + "y": "9304UAFl55xQMfrnB-zKEjjXEC4OFWSuYnr7W6hdkVA" +}