identity_verification/verification_method/
material.rs

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