identity_verification/verification_method/
material.rs

1// Copyright 2020-2025 IOTA Stiftung, Fondazione LINKS
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::jose::jwk::CompositeJwk;
5use crate::jose::jwk::Jwk;
6use core::fmt::Debug;
7use core::fmt::Formatter;
8use identity_core::convert::BaseEncoding;
9use serde::de::Visitor;
10use serde::ser::SerializeMap;
11use serde::Deserialize;
12use serde::Serialize;
13use serde::Serializer;
14use serde_json::Value;
15
16use crate::error::Error;
17use crate::error::Result;
18
19/// Supported verification method data formats.
20#[allow(clippy::large_enum_variant)]
21#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)]
22#[serde(rename_all = "camelCase")]
23#[non_exhaustive]
24pub enum MethodData {
25  /// Verification Material in multibase encoding.
26  PublicKeyMultibase(String),
27  /// Verification Material in base58 encoding.
28  PublicKeyBase58(String),
29  /// Verification Material in the JSON Web Key format.
30  PublicKeyJwk(Jwk),
31  /// Verification Material containing two keys in JSON Web Key format, one traditional and one PQ.
32  CompositeJwk(CompositeJwk),
33  /// Arbitrary verification material.
34  #[serde(untagged)]
35  Custom(CustomMethodData),
36}
37
38impl MethodData {
39  /// Creates a new `MethodData` variant with base58-encoded content.
40  pub fn new_base58(data: impl AsRef<[u8]>) -> Self {
41    Self::PublicKeyBase58(BaseEncoding::encode_base58(&data))
42  }
43
44  /// Creates a new `MethodData` variant with [Multibase]-encoded content.
45  ///
46  /// [Multibase]: https://datatracker.ietf.org/doc/html/draft-multiformats-multibase-03
47  pub fn new_multibase(data: impl AsRef<[u8]>) -> Self {
48    Self::PublicKeyMultibase(BaseEncoding::encode_multibase(&data, None))
49  }
50
51  /// Creates a new `MethodData` variant from custom data.
52  pub fn new_custom(data: impl Into<CustomMethodData>) -> Self {
53    Self::Custom(data.into())
54  }
55
56  /// Returns a `Vec<u8>` containing the decoded bytes of the `MethodData`.
57  ///
58  /// This is generally a public key identified by a `MethodType` value.
59  ///
60  /// # Errors
61  /// Decoding will fail if `MethodData` is a [`Jwk`], has invalid content or cannot be
62  /// represented as a vector of bytes.
63  pub fn try_decode(&self) -> Result<Vec<u8>> {
64    match self {
65      Self::PublicKeyJwk(_) | Self::Custom(_) | Self::CompositeJwk(_) => Err(Error::InvalidMethodDataTransformation(
66        "method data is not base encoded",
67      )),
68      Self::PublicKeyMultibase(input) => {
69        BaseEncoding::decode_multibase(input).map_err(|_| Error::InvalidKeyDataMultibase)
70      }
71      Self::PublicKeyBase58(input) => BaseEncoding::decode_base58(input).map_err(|_| Error::InvalidKeyDataBase58),
72    }
73  }
74
75  /// Returns the wrapped `CompositePublicKey` if the format is [`MethodData::CompositePublicKey`].
76  pub fn composite_public_key(&self) -> Option<&CompositeJwk> {
77    if let Self::CompositeJwk(ref c) = self {
78      Some(c)
79    } else {
80      None
81    }
82  }
83
84  /// Fallible version of [`Self::composite_public_key`](Self::composite_public_key).
85  pub fn try_composite_public_key(&self) -> Result<&CompositeJwk> {
86    self.composite_public_key().ok_or(Error::NotCompositePublicKey)
87  }
88
89  /// Returns the wrapped `Jwk` if the format is [`MethodData::PublicKeyJwk`].
90  pub fn public_key_jwk(&self) -> Option<&Jwk> {
91    if let Self::PublicKeyJwk(ref jwk) = self {
92      Some(jwk)
93    } else {
94      None
95    }
96  }
97
98  /// Fallible version of [`Self::public_key_jwk`](Self::public_key_jwk()).
99  pub fn try_public_key_jwk(&self) -> Result<&Jwk> {
100    self.public_key_jwk().ok_or(Error::NotPublicKeyJwk)
101  }
102
103  /// Returns the custom method data, if any.
104  pub fn custom(&self) -> Option<&CustomMethodData> {
105    if let Self::Custom(method_data) = self {
106      Some(method_data)
107    } else {
108      None
109    }
110  }
111}
112
113impl Debug for MethodData {
114  fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
115    match self {
116      Self::PublicKeyJwk(inner) => f.write_fmt(format_args!("PublicKeyJwk({inner:#?})")),
117      Self::PublicKeyMultibase(inner) => f.write_fmt(format_args!("PublicKeyMultibase({inner})")),
118      Self::PublicKeyBase58(inner) => f.write_fmt(format_args!("PublicKeyBase58({inner})")),
119      Self::CompositeJwk(inner) => f.write_fmt(format_args!("CompositePublicKey({inner:#?})")),
120      Self::Custom(CustomMethodData { name, data }) => f.write_fmt(format_args!("{name}({data})")),
121    }
122  }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq)]
126/// Custom verification method.
127pub struct CustomMethodData {
128  /// Verification method's name.
129  pub name: String,
130  /// Verification method's data.
131  pub data: Value,
132}
133
134impl Serialize for CustomMethodData {
135  fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
136  where
137    S: Serializer,
138  {
139    let mut map = serializer.serialize_map(Some(1))?;
140    map.serialize_entry(&self.name, &self.data)?;
141    map.end()
142  }
143}
144
145impl<'de> Deserialize<'de> for CustomMethodData {
146  fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
147  where
148    D: serde::Deserializer<'de>,
149  {
150    deserializer.deserialize_map(CustomMethodDataVisitor)
151  }
152}
153
154struct CustomMethodDataVisitor;
155
156impl<'de> Visitor<'de> for CustomMethodDataVisitor {
157  type Value = CustomMethodData;
158  fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159    formatter.write_str("\"<any property name>\": <any json value>")
160  }
161  fn visit_map<A>(self, mut map: A) -> std::prelude::v1::Result<Self::Value, A::Error>
162  where
163    A: serde::de::MapAccess<'de>,
164  {
165    let mut custom_method_data = CustomMethodData {
166      name: String::default(),
167      data: Value::Null,
168    };
169    while let Some((name, data)) = map.next_entry::<String, Value>()? {
170      custom_method_data = CustomMethodData { name, data };
171    }
172
173    Ok(custom_method_data)
174  }
175}
176
177#[cfg(test)]
178mod tests {
179  use super::*;
180  use serde_json::json;
181
182  #[test]
183  fn serialize_custom_method_data() {
184    let custom = MethodData::Custom(CustomMethodData {
185      name: "anArbitraryMethod".to_owned(),
186      data: json!({"a": 1, "b": 2}),
187    });
188    let target_str = json!({
189      "anArbitraryMethod": {"a": 1, "b": 2},
190    })
191    .to_string();
192    assert_eq!(serde_json::to_string(&custom).unwrap(), target_str);
193  }
194  #[test]
195  fn deserialize_custom_method_data() {
196    let inner_data = json!({
197        "firstCustomField": "a random string",
198        "secondCustomField": 420,
199    });
200    let json_method_data = json!({
201      "myCustomVerificationMethod": &inner_data,
202    });
203    let custom = serde_json::from_value::<MethodData>(json_method_data.clone()).unwrap();
204    let target_method_data = MethodData::Custom(CustomMethodData {
205      name: "myCustomVerificationMethod".to_owned(),
206      data: inner_data,
207    });
208    assert_eq!(custom, target_method_data);
209  }
210}