identity_storage/key_id_storage/
method_digest.rs

1// Copyright 2020-2025 IOTA Stiftung, Fondazione LINKS
2// SPDX-License-Identifier: Apache-2.0
3
4use identity_core::convert::ToJson;
5use identity_verification::MethodData;
6use identity_verification::VerificationMethod;
7use seahash::SeaHasher;
8use std::fmt::Display;
9use std::hash::Hasher;
10
11use super::KeyIdStorageError;
12
13/// Error that may occur when constructing a [`MethodDigest`].
14pub type MethodDigestConstructionError = identity_core::common::SingleStructError<MethodDigestConstructionErrorKind>;
15
16/// Characterization of the underlying cause of a [`MethodDigestConstructionError`].
17#[derive(Debug)]
18#[non_exhaustive]
19pub enum MethodDigestConstructionErrorKind {
20  /// Caused by a missing id on a verification method.
21  ///
22  /// This error should be impossible but exists for safety reasons.
23  MissingIdFragment,
24  /// Caused by a failure to decode a method's [key material](identity_verification::MethodData).
25  DataDecodingFailure,
26}
27
28impl Display for MethodDigestConstructionErrorKind {
29  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30    f.write_str("method digest construction failure: ")?;
31    match self {
32      MethodDigestConstructionErrorKind::MissingIdFragment => f.write_str("missing id fragment"),
33      MethodDigestConstructionErrorKind::DataDecodingFailure => f.write_str("data decoding failure"),
34    }
35  }
36}
37
38/// Unique identifier of a [`VerificationMethod`].
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub struct MethodDigest {
41  /// Version of hashing.
42  version: u8,
43  /// Hash value.
44  value: u64,
45}
46
47impl MethodDigest {
48  /// Creates a new [`MethodDigest`].
49  pub fn new(verification_method: &VerificationMethod) -> Result<Self, MethodDigestConstructionError> {
50    // Method digest version 0 formula: SeaHash(<fragment><JWK thumbprint if JWK else decoded public key>)
51    use MethodDigestConstructionErrorKind::*;
52    let mut hasher: SeaHasher = SeaHasher::new();
53    let fragment: &str = verification_method.id().fragment().ok_or(MissingIdFragment)?;
54    let method_data: &MethodData = verification_method.data();
55
56    hasher.write(fragment.as_bytes());
57
58    match method_data {
59      MethodData::PublicKeyJwk(jwk) => hasher.write(jwk.thumbprint_sha256().as_ref()),
60      MethodData::CompositeJwk(composite) => {
61        let algid = composite
62          .alg_id()
63          .to_json_vec()
64          .map_err(|err| MethodDigestConstructionError::new(DataDecodingFailure).with_source(err))?;
65        hasher.write(&algid);
66        hasher.write(composite.traditional_public_key().thumbprint_sha256().as_ref());
67        hasher.write(composite.pq_public_key().thumbprint_sha256().as_ref());
68      }
69      _ => hasher.write(
70        &method_data
71          .try_decode()
72          .map_err(|err| MethodDigestConstructionError::new(DataDecodingFailure).with_source(err))?,
73      ),
74    };
75
76    let key_hash: u64 = hasher.finish();
77
78    Ok(Self {
79      version: 0,
80      value: key_hash,
81    })
82  }
83
84  /// Packs [`MethodDigest`] into bytes.
85  pub fn pack(&self) -> Vec<u8> {
86    let mut pack: Vec<u8> = vec![self.version];
87    pack.append(&mut self.value.to_le_bytes().to_vec());
88    pack
89  }
90
91  /// Unpacks bytes into [`MethodDigest`].
92  pub fn unpack(bytes: Vec<u8>) -> crate::key_id_storage::KeyIdStorageResult<Self> {
93    if bytes.len() != 9 {
94      return Err(KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError));
95    }
96    let version: u8 = bytes[0];
97    if version != 0 {
98      return Err(KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError));
99    }
100    let value_le_bytes: [u8; 8] = bytes[1..9]
101      .try_into()
102      .map_err(|_| KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError))?;
103    let value: u64 = u64::from_le_bytes(value_le_bytes);
104    Ok(Self { version, value })
105  }
106}
107
108#[cfg(test)]
109mod test {
110  use crate::key_id_storage::KeyIdStorageError;
111  use crate::key_id_storage::KeyIdStorageErrorKind;
112  use identity_core::convert::FromJson;
113  use identity_core::json;
114  use identity_verification::VerificationMethod;
115  use serde_json::Value;
116
117  use super::MethodDigest;
118
119  #[test]
120  fn hash() {
121    // These values should be tested in the bindings too.
122    let a: Value = json!(
123      {
124        "id": "did:example:HHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u#frag_1",
125        "controller": "did:example:HHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u",
126        "type": "Ed25519VerificationKey2018",
127        "publicKeyMultibase": "zHHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u"
128      }
129    );
130    let verification_method: VerificationMethod = VerificationMethod::from_json_value(a).unwrap();
131    let method_digest: MethodDigest = MethodDigest::new(&verification_method).unwrap();
132    let method_digest_expected: MethodDigest = MethodDigest {
133      version: 0,
134      value: 9634551232492878922,
135    };
136    assert_eq!(method_digest, method_digest_expected);
137
138    let packed: Vec<u8> = method_digest.pack();
139    let packed_expected: Vec<u8> = vec![0, 74, 60, 10, 199, 76, 205, 180, 133];
140    assert_eq!(packed, packed_expected);
141  }
142
143  #[test]
144  fn pack() {
145    let verification_method: VerificationMethod = crate::storage::tests::test_utils::create_verification_method();
146    let method_digest: MethodDigest = MethodDigest::new(&verification_method).unwrap();
147    let packed: Vec<u8> = method_digest.pack();
148    let method_digest_unpacked: MethodDigest = MethodDigest::unpack(packed).unwrap();
149    assert_eq!(method_digest, method_digest_unpacked);
150  }
151
152  #[test]
153  fn unpack() {
154    let packed: Vec<u8> = vec![0, 255, 212, 82, 63, 57, 19, 134, 193];
155    let method_digest_unpacked: MethodDigest = MethodDigest::unpack(packed).unwrap();
156    let method_digest_expected: MethodDigest = MethodDigest {
157      version: 0,
158      value: 13944854432795776255,
159    };
160    assert_eq!(method_digest_unpacked, method_digest_expected);
161  }
162
163  #[test]
164  fn invalid_unpack() {
165    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134, 193];
166    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
167    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
168    assert!(matches!(method_digest_unpacked, _expected_error));
169
170    // Vec size > 9.
171    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134, 193, 200];
172    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
173    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
174    assert!(matches!(method_digest_unpacked, _expected_error));
175
176    // Vec size < 9.
177    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134];
178    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
179    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
180    assert!(matches!(method_digest_unpacked, _expected_error));
181
182    // Vec size 0;
183    let packed: Vec<u8> = vec![];
184    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
185    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
186    assert!(matches!(method_digest_unpacked, _expected_error));
187  }
188}