identity_credential/sd_jwt_vc/metadata/
integrity.rs

1// Copyright 2020-2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fmt::Display;
5use std::str::FromStr;
6
7use anyhow::anyhow;
8use identity_core::convert::Base;
9use identity_core::convert::BaseEncoding;
10use serde::Deserialize;
11use serde::Serialize;
12
13/// An integrity metadata string as defined in [W3C SRI](https://www.w3.org/TR/SRI/#integrity-metadata).
14#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(try_from = "String")]
16pub struct IntegrityMetadata(String);
17
18impl IntegrityMetadata {
19  /// Parses an [`IntegrityMetadata`] from a string.
20  /// ## Example
21  /// ```rust
22  /// use identity_credential::sd_jwt_vc::metadata::IntegrityMetadata;
23  ///
24  /// let integrity_data = IntegrityMetadata::parse(
25  ///   "sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd",
26  /// )
27  /// .unwrap();
28  /// ```
29  pub fn parse(s: &str) -> Result<Self, anyhow::Error> {
30    s.parse()
31  }
32
33  /// Returns the digest algorithm's identifier string.
34  /// ## Example
35  /// ```rust
36  /// use identity_credential::sd_jwt_vc::metadata::IntegrityMetadata;
37  ///
38  /// let integrity_data: IntegrityMetadata =
39  ///   "sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd"
40  ///     .parse()
41  ///     .unwrap();
42  /// assert_eq!(integrity_data.alg(), "sha384");
43  /// ```
44  pub fn alg(&self) -> &str {
45    self.0.split_once('-').unwrap().0
46  }
47
48  /// Returns the base64 encoded digest part.
49  /// ## Example
50  /// ```rust
51  /// use identity_credential::sd_jwt_vc::metadata::IntegrityMetadata;
52  ///
53  /// let integrity_data: IntegrityMetadata =
54  ///   "sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd"
55  ///     .parse()
56  ///     .unwrap();
57  /// assert_eq!(
58  ///   integrity_data.digest(),
59  ///   "dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd"
60  /// );
61  /// ```
62  pub fn digest(&self) -> &str {
63    self.0.split('-').nth(1).unwrap()
64  }
65
66  /// Returns the digest's bytes.
67  pub fn digest_bytes(&self) -> Vec<u8> {
68    BaseEncoding::decode(self.digest(), Base::Base64).unwrap()
69  }
70
71  /// Returns the option part.
72  /// ## Example
73  /// ```rust
74  /// use identity_credential::sd_jwt_vc::metadata::IntegrityMetadata;
75  ///
76  /// let integrity_data: IntegrityMetadata =
77  ///   "sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd"
78  ///     .parse()
79  ///     .unwrap();
80  /// assert!(integrity_data.options().is_none());
81  /// ```
82  pub fn options(&self) -> Option<&str> {
83    self.0.splitn(3, '-').nth(2)
84  }
85}
86
87impl AsRef<str> for IntegrityMetadata {
88  fn as_ref(&self) -> &str {
89    self.0.as_str()
90  }
91}
92
93impl Display for IntegrityMetadata {
94  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95    write!(f, "{}", &self.0)
96  }
97}
98
99impl FromStr for IntegrityMetadata {
100  type Err = anyhow::Error;
101  fn from_str(s: &str) -> Result<Self, Self::Err> {
102    Self::try_from(s.to_owned())
103  }
104}
105
106impl TryFrom<String> for IntegrityMetadata {
107  type Error = anyhow::Error;
108  fn try_from(value: String) -> Result<Self, Self::Error> {
109    let mut metadata_parts = value.splitn(3, '-');
110    let _alg = metadata_parts
111      .next()
112      .ok_or_else(|| anyhow!("invalid integrity metadata"))?;
113    let _digest = metadata_parts
114      .next()
115      .and_then(|digest| BaseEncoding::decode(digest, Base::Base64).ok())
116      .ok_or_else(|| anyhow!("invalid integrity metadata"))?;
117    let _options = metadata_parts.next();
118
119    Ok(Self(value))
120  }
121}