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
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#[cfg(feature = "sha")]
use crypto::hashes::sha::SHA256;

#[cfg(feature = "sha")]
use crypto::hashes::sha::SHA256_LEN;

pub const SHA_ALG_NAME: &str = "sha-256";

/// Used to implement hash functions to be used for encoding/decoding.
///
/// ## Note
///
/// Implementations of this trait are expected only for algorithms listed in
/// the IANA "Named Information Hash Algorithm" registry.
/// See [Hash Function Claim](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#name-hash-function-claim)
pub trait Hasher: Sync + Send {
  /// Digests input to produce unique fixed-size hash value in bytes.
  fn digest(&self, input: &[u8]) -> Vec<u8>;

  /// Returns the name of hash function used.
  ///
  /// ## Note
  ///
  /// The hash algorithm identifier MUST be a hash algorithm value from the
  /// "Hash Name String" column in the IANA "Named Information Hash Algorithm"  
  fn alg_name(&self) -> &'static str;

  /// Returns the base64url-encoded digest of a `disclosure`.
  fn encoded_digest(&self, disclosure: &str) -> String {
    let hash = self.digest(disclosure.as_bytes());
    multibase::Base::Base64Url.encode(hash)
  }
}

/// An implementation of [`Hasher`] that uses the `sha-256` hash function.
#[derive(Default, Clone, Copy)]
#[cfg(feature = "sha")]
pub struct Sha256Hasher;

#[cfg(feature = "sha")]
impl Sha256Hasher {
  /// Creates a new [`ShaHasher`]
  pub fn new() -> Self {
    Sha256Hasher {}
  }
}
#[cfg(feature = "sha")]
impl Hasher for Sha256Hasher {
  fn digest(&self, input: &[u8]) -> Vec<u8> {
    let mut digest: [u8; SHA256_LEN] = Default::default();
    SHA256(input, &mut digest);
    digest.to_vec()
  }

  fn alg_name(&self) -> &'static str {
    SHA_ALG_NAME
  }
}

// Some test values taken from https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#name-disclosures
#[cfg(test)]
mod test {
  use crate::Hasher;
  use crate::Sha256Hasher;

  #[test]
  fn test1() {
    let disclosure = "WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNw7ZiaXVzIl0";
    let hasher = Sha256Hasher::new();
    let hash = hasher.encoded_digest(disclosure);
    assert_eq!("uutlBuYeMDyjLLTpf6Jxi7yNkEF35jdyWMn9U7b_RYY", hash);
  }

  #[test]
  fn test2() {
    let disclosure =
      "WyJlSThaV205UW5LUHBOUGVOZW5IZGhRIiwgImVtYWlsIiwgIlwidW51c3VhbCBlbWFpbCBhZGRyZXNzXCJAZXhhbXBsZS5qcCJd";
    let hasher = Sha256Hasher::new();
    let hash = hasher.encoded_digest(disclosure);
    assert_eq!("Kuet1yAa0HIQvYnOVd59hcViO9Ug6J2kSfqYRBeowvE", hash);
  }

  #[test]
  fn test3() {
    let disclosure = "WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIkZSIl0";
    let hasher = Sha256Hasher::new();
    let hash = hasher.encoded_digest(disclosure);
    assert_eq!("w0I8EKcdCtUPkGCNUrfwVp2xEgNjtoIDlOxc9-PlOhs", hash);
  }
}