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}