1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use identity_verification::MethodData;
use identity_verification::VerificationMethod;
use seahash::SeaHasher;
use std::fmt::Display;
use std::hash::Hasher;

use super::KeyIdStorageError;

/// Error that may occur when constructing a [`MethodDigest`].
pub type MethodDigestConstructionError = identity_core::common::SingleStructError<MethodDigestConstructionErrorKind>;

/// Characterization of the underlying cause of a [`MethodDigestConstructionError`].
#[derive(Debug)]
#[non_exhaustive]
pub enum MethodDigestConstructionErrorKind {
  /// Caused by a missing id on a verification method.
  ///
  /// This error should be impossible but exists for safety reasons.
  MissingIdFragment,
  /// Caused by a failure to decode a method's [key material](identity_verification::MethodData).
  DataDecodingFailure,
}

impl Display for MethodDigestConstructionErrorKind {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.write_str("method digest construction failure: ")?;
    match self {
      MethodDigestConstructionErrorKind::MissingIdFragment => f.write_str("missing id fragment"),
      MethodDigestConstructionErrorKind::DataDecodingFailure => f.write_str("data decoding failure"),
    }
  }
}

/// Unique identifier of a [`VerificationMethod`].
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MethodDigest {
  /// Version of hashing.
  version: u8,
  /// Hash value.
  value: u64,
}

impl MethodDigest {
  /// Creates a new [`MethodDigest`].
  pub fn new(verification_method: &VerificationMethod) -> Result<Self, MethodDigestConstructionError> {
    // Method digest version 0 formula: SeaHash(<fragment><JWK thumbprint if JWK else decoded public key>)
    use MethodDigestConstructionErrorKind::*;
    let mut hasher: SeaHasher = SeaHasher::new();
    let fragment: &str = verification_method.id().fragment().ok_or(MissingIdFragment)?;
    let method_data: &MethodData = verification_method.data();

    hasher.write(fragment.as_bytes());

    match method_data {
      MethodData::PublicKeyJwk(jwk) => hasher.write(jwk.thumbprint_sha256().as_ref()),
      _ => hasher.write(
        &method_data
          .try_decode()
          .map_err(|err| MethodDigestConstructionError::new(DataDecodingFailure).with_source(err))?,
      ),
    };

    let key_hash: u64 = hasher.finish();

    Ok(Self {
      version: 0,
      value: key_hash,
    })
  }

  /// Packs [`MethodDigest`] into bytes.
  pub fn pack(&self) -> Vec<u8> {
    let mut pack: Vec<u8> = vec![self.version];
    pack.append(&mut self.value.to_le_bytes().to_vec());
    pack
  }

  /// Unpacks bytes into [`MethodDigest`].
  pub fn unpack(bytes: Vec<u8>) -> crate::key_id_storage::KeyIdStorageResult<Self> {
    if bytes.len() != 9 {
      return Err(KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError));
    }
    let version: u8 = bytes[0];
    if version != 0 {
      return Err(KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError));
    }
    let value_le_bytes: [u8; 8] = bytes[1..9]
      .try_into()
      .map_err(|_| KeyIdStorageError::new(super::KeyIdStorageErrorKind::SerializationError))?;
    let value: u64 = u64::from_le_bytes(value_le_bytes);
    Ok(Self { version, value })
  }
}

#[cfg(test)]
mod test {
  use crate::key_id_storage::KeyIdStorageError;
  use crate::key_id_storage::KeyIdStorageErrorKind;
  use identity_core::convert::FromJson;
  use identity_core::json;
  use identity_verification::VerificationMethod;
  use serde_json::Value;

  use super::MethodDigest;

  #[test]
  fn hash() {
    // These values should be tested in the bindings too.
    let a: Value = json!(
      {
        "id": "did:example:HHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u#frag_1",
        "controller": "did:example:HHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u",
        "type": "Ed25519VerificationKey2018",
        "publicKeyMultibase": "zHHoh9NQC9AUsK15Jyyq53VTujxEUizKDXRXd7zbT1B5u"
      }
    );
    let verification_method: VerificationMethod = VerificationMethod::from_json_value(a).unwrap();
    let method_digest: MethodDigest = MethodDigest::new(&verification_method).unwrap();
    let method_digest_expected: MethodDigest = MethodDigest {
      version: 0,
      value: 9634551232492878922,
    };
    assert_eq!(method_digest, method_digest_expected);

    let packed: Vec<u8> = method_digest.pack();
    let packed_expected: Vec<u8> = vec![0, 74, 60, 10, 199, 76, 205, 180, 133];
    assert_eq!(packed, packed_expected);
  }

  #[test]
  fn pack() {
    let verification_method: VerificationMethod = crate::storage::tests::test_utils::create_verification_method();
    let method_digest: MethodDigest = MethodDigest::new(&verification_method).unwrap();
    let packed: Vec<u8> = method_digest.pack();
    let method_digest_unpacked: MethodDigest = MethodDigest::unpack(packed).unwrap();
    assert_eq!(method_digest, method_digest_unpacked);
  }

  #[test]
  fn unpack() {
    let packed: Vec<u8> = vec![0, 255, 212, 82, 63, 57, 19, 134, 193];
    let method_digest_unpacked: MethodDigest = MethodDigest::unpack(packed).unwrap();
    let method_digest_expected: MethodDigest = MethodDigest {
      version: 0,
      value: 13944854432795776255,
    };
    assert_eq!(method_digest_unpacked, method_digest_expected);
  }

  #[test]
  fn invalid_unpack() {
    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134, 193];
    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
    assert!(matches!(method_digest_unpacked, _expected_error));

    // Vec size > 9.
    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134, 193, 200];
    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
    assert!(matches!(method_digest_unpacked, _expected_error));

    // Vec size < 9.
    let packed: Vec<u8> = vec![1, 255, 212, 82, 63, 57, 19, 134];
    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
    assert!(matches!(method_digest_unpacked, _expected_error));

    // Vec size 0;
    let packed: Vec<u8> = vec![];
    let method_digest_unpacked = MethodDigest::unpack(packed).unwrap_err();
    let _expected_error = KeyIdStorageError::new(KeyIdStorageErrorKind::SerializationError);
    assert!(matches!(method_digest_unpacked, _expected_error));
  }
}