Skip to main content

script/dom/webcrypto/subtlecrypto/
turboshake_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use seq_macro::seq;
6use turboshake::digest::{ExtendableOutput, Update};
7use turboshake::{CTurboShake128, CTurboShake256};
8
9use crate::dom::bindings::error::Error;
10use crate::dom::subtlecrypto::{CryptoAlgorithm, SubtleTurboShakeParams};
11
12/// <https://wicg.github.io/webcrypto-modern-algos/#turboshake-operations-digest>
13pub(crate) fn digest(
14    normalized_algorithm: &SubtleTurboShakeParams,
15    message: &[u8],
16) -> Result<Vec<u8>, Error> {
17    // Step 1. Let outputLength be the outputLength member of normalizedAlgorithm.
18    let output_length = normalized_algorithm.output_length;
19
20    // Step 2. If outputLength is zero or is not a multiple of 8, then throw an OperationError.
21    if output_length == 0 || !output_length.is_multiple_of(8) {
22        return Err(Error::Operation(Some(
23            "The outputLength is zero or is not a multiple of 8".to_string(),
24        )));
25    }
26
27    // Step 3. Let domainSeparation be the domainSeparation member of normalizedAlgorithm if
28    // present, or 0x1F otherwise.
29    let domain_separation = normalized_algorithm.domain_separation.unwrap_or(0x1f);
30
31    // Step 4. If domainSeparation is less than 0x01 or greater than 0x7F, then throw an
32    // OperationError.
33    if !(0x01..=0x7f).contains(&domain_separation) {
34        return Err(Error::Operation(Some(
35            "The domainSeparation is less than 0x01 or greater than 0x7F".to_string(),
36        )));
37    }
38
39    // Step 5.
40    // If the name member of normalizedAlgorithm is a case-sensitive string match for
41    // "TurboSHAKE128":
42    //     Let result be the result of performing the TurboSHAKE128 function defined in Section 2
43    //     of [RFC9861] using message as the M input parameter, domainSeparation as the D input
44    //     parameter, and outputLength divided by 8 as the L input parameter.
45    // If the name member of normalizedAlgorithm is a case-sensitive string match for
46    // "TurboSHAKE256":
47    //     Let result be the result of performing the TurboSHAKE256 function defined in Section 2
48    //     of [RFC9861] using message as the M input parameter, domainSeparation as the D input
49    //     parameter, and outputLength divided by 8 as the L input parameter.
50    // Step 6. If performing the operation results in an error, then throw an OperationError.
51    let mut result = vec![0u8; output_length as usize / 8];
52    match normalized_algorithm.name {
53        CryptoAlgorithm::TurboShake128 => {
54            seq!(DS in 0x01..=0x7f {
55                match domain_separation {
56                    #(
57                        DS => hash_message::<CTurboShake128<DS>>(message, &mut result),
58                    )*
59                    _ => unreachable!("Step 4 guarantees domainSeparation lies in 0x01..=0x7f"),
60                }
61            });
62        },
63        CryptoAlgorithm::TurboShake256 => {
64            seq!(DS in 0x01..=0x7f {
65                match domain_separation {
66                    #(
67                        DS => hash_message::<CTurboShake256<DS>>(message, &mut result),
68                    )*
69                    _ => unreachable!("Step 4 guarantees domainSeparation lies in 0x01..=0x7f"),
70                }
71            });
72        },
73        algorithm_name => {
74            return Err(Error::NotSupported(Some(format!(
75                "{} is not a TurboSHAKE algorithm",
76                algorithm_name.as_str()
77            ))));
78        },
79    }
80
81    // Step 7. Return result.
82    Ok(result)
83}
84
85fn hash_message<T: ExtendableOutput + Update + Default>(message: &[u8], result: &mut [u8]) {
86    let mut hasher = T::default();
87    hasher.update(message);
88    hasher.finalize_xof_into(result);
89}