Skip to main content

script/dom/webcrypto/subtlecrypto/
kmac_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 cshake::CShake;
6use cshake::digest::{ExtendableOutput, Update};
7use ctutils::CtEq;
8use js::context::JSContext;
9use rand::TryRng;
10use zeroize::Zeroizing;
11
12use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
13    CryptoKeyMethods, KeyType, KeyUsage,
14};
15use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
16use crate::dom::bindings::error::Error;
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::bindings::str::DOMString;
19use crate::dom::cryptokey::{CryptoKey, Handle};
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::subtlecrypto::{
22    CryptoAlgorithm, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
23    SubtleKmacImportParams, SubtleKmacKeyAlgorithm, SubtleKmacKeyGenParams, SubtleKmacParams,
24};
25
26/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-sign>
27pub(crate) fn sign(
28    normalized_algorithm: &SubtleKmacParams,
29    key: &CryptoKey,
30    message: &[u8],
31) -> Result<Vec<u8>, Error> {
32    // Step 1. Let customization be the customization member of normalizedAlgorithm if present or
33    // the empty octet string otherwise.
34    let customization = normalized_algorithm
35        .customization
36        .as_deref()
37        .unwrap_or_default();
38
39    // Step 2.
40    // If the name member of normalizedAlgorithm is a case-sensitive string match for "KMAC128":
41    //     Let mac be the result of performing the KMAC128 function defined in Section 4 of
42    //     [NIST-SP800-185] using the key represented by [[handle]] internal slot of key as the K
43    //     input parameter, message as the X input parameter, the outputLength member of
44    //     normalizedAlgorithm as the L input parameter, and customization as the S input parameter.
45    // If the name member of normalizedAlgorithm is a case-sensitive string match for "KMAC256":
46    //     Let mac be the result of performing the KMAC256 function defined in Section 4 of
47    //     [NIST-SP800-185] using the key represented by [[handle]] internal slot of key as the K
48    //     input parameter, message as the X input parameter, the outputLength member of
49    //     normalizedAlgorithm as the L input parameter, and customization as the S input parameter.
50    let Handle::KmacKey(kmac_key) = key.handle() else {
51        return Err(Error::Operation(Some(
52            "[[handle]] internal slot of key is not a KMAC key".into(),
53        )));
54    };
55    let KeyAlgorithmAndDerivatives::KmacKeyAlgorithm(algorithm) = key.algorithm() else {
56        return Err(Error::Operation(Some(
57            "[[algorithm]] internal slot of key is not a KmacKeyAlgorithm".into(),
58        )));
59    };
60    let mac = match normalized_algorithm.name {
61        CryptoAlgorithm::Kmac128 => kmac128(
62            kmac_key,
63            algorithm.length,
64            message,
65            normalized_algorithm.output_length,
66            customization,
67        ),
68        CryptoAlgorithm::Kmac256 => kmac256(
69            kmac_key,
70            algorithm.length,
71            message,
72            normalized_algorithm.output_length,
73            customization,
74        ),
75        algorithm_name => {
76            return Err(Error::NotSupported(Some(format!(
77                "{} is not supported",
78                algorithm_name.as_str()
79            ))));
80        },
81    };
82
83    // Step 3. Return a byte sequence containing mac.
84    Ok(mac)
85}
86
87/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-verify>
88pub(crate) fn verify(
89    normalized_algorithm: &SubtleKmacParams,
90    key: &CryptoKey,
91    message: &[u8],
92    signature: &[u8],
93) -> Result<bool, Error> {
94    // Step 1. Let customization be the customization member of normalizedAlgorithm if present or
95    // the empty octet string otherwise.
96    let customization = normalized_algorithm
97        .customization
98        .as_deref()
99        .unwrap_or_default();
100
101    // Step 2.
102    // If the name member of normalizedAlgorithm is a case-sensitive string match for "KMAC128":
103    //     Let mac be the result of performing the KMAC128 function defined in Section 4 of
104    //     [NIST-SP800-185] using the key represented by [[handle]] internal slot of key as the K
105    //     input parameter, message as the X input parameter, the outputLength member of
106    //     normalizedAlgorithm as the L input parameter, and customization as the S input parameter.
107    // If the name member of normalizedAlgorithm is a case-sensitive string match for "KMAC256":
108    //     Let mac be the result of performing the KMAC256 function defined in Section 4 of
109    //     [NIST-SP800-185] using the key represented by [[handle]] internal slot of key as the K
110    //     input parameter, message as the X input parameter, the outputLength member of
111    //     normalizedAlgorithm as the L input parameter, and customization as the S input parameter.
112    // Step 3. Let computedMac be a byte sequence containing mac.
113    let Handle::KmacKey(kmac_key) = key.handle() else {
114        return Err(Error::Operation(Some(
115            "[[handle]] internal slot of key is not a KMAC key".into(),
116        )));
117    };
118    let KeyAlgorithmAndDerivatives::KmacKeyAlgorithm(algorithm) = key.algorithm() else {
119        return Err(Error::Operation(Some(
120            "[[algorithm]] internal slot of key is not a KmacKeyAlgorithm".into(),
121        )));
122    };
123    let computed_mac = match normalized_algorithm.name {
124        CryptoAlgorithm::Kmac128 => kmac128(
125            kmac_key,
126            algorithm.length,
127            message,
128            normalized_algorithm.output_length,
129            customization,
130        ),
131        CryptoAlgorithm::Kmac256 => kmac256(
132            kmac_key,
133            algorithm.length,
134            message,
135            normalized_algorithm.output_length,
136            customization,
137        ),
138        algorithm_name => {
139            return Err(Error::NotSupported(Some(format!(
140                "{} is not supported",
141                algorithm_name.as_str()
142            ))));
143        },
144    };
145
146    // Step 4. Return true if computedMac is equal to signature and false otherwise. This comparison
147    // must be performed in constant-time.
148    Ok(computed_mac.ct_eq(signature).into())
149}
150
151/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-generate-key>
152pub(crate) fn generate_key(
153    cx: &mut JSContext,
154    global: &GlobalScope,
155    normalized_algorithm: &SubtleKmacKeyGenParams,
156    extractable: bool,
157    usages: Vec<KeyUsage>,
158) -> Result<DomRoot<CryptoKey>, Error> {
159    // Step 1. If usages contains an entry which is not "sign" or "verify", then throw a
160    // SyntaxError.
161    if usages
162        .iter()
163        .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
164    {
165        return Err(Error::Syntax(Some(
166            "Usages contains an entry which is not \"sign\" or \"verify\"".into(),
167        )));
168    }
169
170    // Step 2.
171    // If the length member of normalizedAlgorithm is present:
172    //     Let length be equal to the length member of normalizedAlgorithm.
173    // Otherwise, if the name member of normalizedAlgorithm is a case-sensitive string match for
174    // "KMAC128":
175    //     Let length be 128.
176    // Otherwise, if the name member of normalizedAlgorithm is a case-sensitive string match for
177    // "KMAC256":
178    //     Let length be 256.
179    let length = if let Some(normalized_algorithm_length) = normalized_algorithm.length {
180        normalized_algorithm_length
181    } else if normalized_algorithm.name == CryptoAlgorithm::Kmac128 {
182        128
183    } else if normalized_algorithm.name == CryptoAlgorithm::Kmac256 {
184        256
185    } else {
186        return Err(Error::Data(Some(
187            "Unable to determine the length of KMAC key".into(),
188        )));
189    };
190
191    // Step 3. Generate a key of length length bits.
192    // Step 4. If the key generation step fails, then throw an OperationError.
193    //
194    // NOTE: We store the KMAC key bits as the byte sequence containing bits.
195    // <https://w3c.github.io/webcrypto/#dfn-byte-sequence-containing>
196    let mut rng = rand::rng();
197    let mut bits = vec![0u8; length.div_ceil(8) as usize];
198    rng.try_fill_bytes(&mut bits)
199        .map_err(|_| Error::Operation(Some("Failed to generate KMAC key".into())))?;
200    if !length.is_multiple_of(8) {
201        // Clean excess bits in the last byte of result.
202        let mask = u8::MAX << (8 - length % 8);
203        if let Some(last_byte) = bits.last_mut() {
204            *last_byte &= mask;
205        }
206    }
207
208    // Step 5. Let key be a new CryptoKey object representing the generated key.
209    // Step 6. Set the [[type]] internal slot of key to "secret".
210    // Step 7. Let algorithm be a new KmacKeyAlgorithm.
211    // Step 8. Set the name attribute of algorithm to the name member of normalizedAlgorithm.
212    // Step 9. Set the length attribute of algorithm to length.
213    // Step 10. Set the [[algorithm]] internal slot of key to algorithm.
214    // Step 11. Set the [[extractable]] internal slot of key to be extractable.
215    // Step 12. Set the [[usages]] internal slot of key to be usages.
216    let algorithm = SubtleKmacKeyAlgorithm {
217        name: normalized_algorithm.name,
218        length,
219    };
220    let key = CryptoKey::new(
221        cx,
222        global,
223        KeyType::Secret,
224        extractable,
225        KeyAlgorithmAndDerivatives::KmacKeyAlgorithm(algorithm),
226        usages,
227        Handle::KmacKey(bits.into()),
228    );
229
230    // Step 13. Return key.
231    Ok(key)
232}
233
234/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-import-key>
235pub(crate) fn import_key(
236    cx: &mut JSContext,
237    global: &GlobalScope,
238    normalized_algorithm: &SubtleKmacImportParams,
239    format: KeyFormat,
240    key_data: &[u8],
241    extractable: bool,
242    usages: Vec<KeyUsage>,
243) -> Result<DomRoot<CryptoKey>, Error> {
244    // Step 1. Let keyData be the key data to be imported.
245    // NOTE: It is given as a method parameter.
246
247    // Step 2. If usages contains an entry which is not "sign" or "verify", then throw a
248    // SyntaxError.
249    if usages
250        .iter()
251        .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
252    {
253        return Err(Error::Syntax(Some(
254            "Usages contains an entry which is not \"sign\" or \"verify\"".into(),
255        )));
256    }
257
258    // Step 3.
259    let mut data: Zeroizing<Vec<u8>>;
260    match format {
261        // If format is "raw-secret":
262        KeyFormat::Raw_secret => {
263            // Step 3.1. Let data be keyData.
264            data = key_data.to_vec().into();
265        },
266        // If format is "jwk":
267        KeyFormat::Jwk => {
268            // Step 3.1.
269            // If keyData is a JsonWebKey dictionary:
270            //     Let jwk equal keyData.
271            // Otherwise:
272            //     Throw a DataError.
273            let jwk = JsonWebKey::parse(cx, key_data)?;
274
275            // Step 3.2. If the kty field of jwk is not "oct", then throw a DataError.
276            if jwk.kty.as_ref().is_none_or(|kty| kty != "oct") {
277                return Err(Error::Data(Some(
278                    "The kty field of jwk is not \"oct\"".into(),
279                )));
280            }
281
282            // Step 3.3. If jwk does not meet the requirements of Section 6.4 of JSON Web Algorithms
283            // [JWA], then throw a DataError.
284            // Step 3.4. Let data be the byte sequence obtained by decoding the k field of jwk.
285            data = jwk.decode_required_string_field(JwkStringField::K)?;
286
287            // Step 3.4.
288            // If the name member of normalizedAlgorithm is a case-sensitive string match for
289            // "KMAC128":
290            //     If the alg field of jwk is present and is not "K128", then throw a DataError.
291            // If the name member of normalizedAlgorithm is a case-sensitive string match for
292            // "KMAC256":
293            //     If the alg field of jwk is present and is not "K256", then throw a DataError.
294            if normalized_algorithm.name == CryptoAlgorithm::Kmac128 &&
295                jwk.alg.as_ref().is_some_and(|alg| alg != "K128")
296            {
297                return Err(Error::Data(Some(
298                    "The alg field of jwk is present and is not \"K128\"".into(),
299                )));
300            }
301            if normalized_algorithm.name == CryptoAlgorithm::Kmac256 &&
302                jwk.alg.as_ref().is_some_and(|alg| alg != "K256")
303            {
304                return Err(Error::Data(Some(
305                    "The alg field of jwk is present and is not \"K256\"".into(),
306                )));
307            }
308
309            // Step 3.6. If usages is non-empty and the use field of jwk is present and is not
310            // "sig", then throw a DataError.
311            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
312                return Err(Error::Data(Some(
313                    "Usages is non-empty and the use field of jwk is present and is not \
314                    equal to \"sig\""
315                        .into(),
316                )));
317            }
318
319            // Step 3.7. If the key_ops field of jwk is present, and is invalid according to the
320            // requirements of JSON Web Key [JWK] or does not contain all of the specified usages
321            // values, then throw a DataError.
322            jwk.check_key_ops(&usages)?;
323
324            // Step 3.8. If the ext field of jwk is present and has the value false and extractable
325            // is true, then throw a DataError.
326            if jwk.ext.is_some_and(|ext| !ext) && extractable {
327                return Err(Error::Data(Some(
328                    "The ext field of jwk is present and has the value false and extractable \
329                    is true"
330                        .into(),
331                )));
332            }
333        },
334        // Otherwise:
335        _ => {
336            // throw a NotSupportedError.
337            return Err(Error::NotSupported(Some(
338                "Unsupported import key format for KMAC key".into(),
339            )));
340        },
341    }
342
343    // Step 4. Let length be the length in bits of data.
344    let mut length = data.len() as u32 * 8;
345
346    // Step 5.
347    // If the length member of normalizedAlgorithm is present:
348    //     If the length member of normalizedAlgorithm is greater than length:
349    //         throw a DataError.
350    //     If the length member of normalizedAlgorithm, is less than or equal to length minus eight:
351    //         throw a DataError.
352    //     Otherwise:
353    //         Set length equal to the length member of normalizedAlgorithm.
354    if let Some(normalized_algorithm_length) = normalized_algorithm.length {
355        if normalized_algorithm_length > length {
356            return Err(Error::Data(Some("The key bit string is too short".into())));
357        } else if normalized_algorithm_length + 8 <= length {
358            return Err(Error::Data(Some("The key bit string is too long".into())));
359        } else {
360            length = normalized_algorithm_length;
361        }
362    }
363
364    // Step 6. Let key be a new CryptoKey object representing an KMAC key with the first length bits
365    // of data.
366    // Step 7. Set the [[type]] internal slot of key to "secret".
367    // Step 8. Let algorithm be a new KmacKeyAlgorithm.
368    // Step 9. Set the name attribute of algorithm to the name member of normalizedAlgorithm.
369    // Step 10. Set the length attribute of algorithm to length.
370    // Step 11. Set the [[algorithm]] internal slot of key to algorithm.
371    //
372    // NOTE: We store the first length bits of data as the byte sequence containing bits.
373    // <https://w3c.github.io/webcrypto/#dfn-byte-sequence-containing>
374    if !length.is_multiple_of(8) {
375        // Clean excess bits in the last byte of result.
376        let mask = u8::MAX << (8 - length % 8);
377        if let Some(last_byte) = data.last_mut() {
378            *last_byte &= mask;
379        }
380    }
381    let algorithm = SubtleKmacKeyAlgorithm {
382        name: normalized_algorithm.name,
383        length,
384    };
385    let key = CryptoKey::new(
386        cx,
387        global,
388        KeyType::Secret,
389        extractable,
390        KeyAlgorithmAndDerivatives::KmacKeyAlgorithm(algorithm),
391        usages,
392        Handle::KmacKey(data),
393    );
394
395    // Step 12. Return key.
396    Ok(key)
397}
398
399/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-export-key>
400pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
401    // Step 1. If the underlying cryptographic key material represented by the [[handle]] internal
402    // slot of key cannot be accessed, then throw an OperationError.
403
404    // Step 2. Let bits be the raw bits of the key represented by [[handle]] internal slot of key.
405    // Step 3. Let data be an byte sequence containing bits.
406    //
407    // NOTE: We already store KMAC key bits as the byte sequence containing bits in the [[handle]]
408    // internal slot of key.
409    let Handle::KmacKey(data) = key.handle() else {
410        return Err(Error::Operation(Some(
411            "[[handle]] internal slot of key is not a KMAC key".into(),
412        )));
413    };
414
415    // Step 4.
416    let result = match format {
417        // If format is "raw-secret":
418        KeyFormat::Raw_secret => {
419            // Step 4.1. Let result be data.
420            ExportedKey::new_bytes(data.to_vec())
421        },
422        // If format is "jwk":
423        KeyFormat::Jwk => {
424            // Step 4.1. Let jwk be a new JsonWebKey dictionary.
425            let mut jwk = JsonWebKey::default();
426
427            // Step 4.2. Set the kty attribute of jwk to the string "oct".
428            jwk.kty = Some(DOMString::from("oct"));
429
430            // Step 4.3. Set the k attribute of jwk to be a string containing data, encoded
431            // according to Section 6.4 of JSON Web Algorithms [JWA].
432            jwk.encode_string_field(JwkStringField::K, data);
433
434            // Step 4.4. Let keyAlgorithm be the [[algorithm]] internal slot of key.
435            let KeyAlgorithmAndDerivatives::KmacKeyAlgorithm(key_algorithm) = key.algorithm()
436            else {
437                return Err(Error::Operation(Some("The key is not a KMAC key".into())));
438            };
439
440            // Step 4.5.
441            // If the name member of keyAlgorithm is "KMAC128":
442            //     Set the alg attribute of jwk to the string "K128".
443            // If the name member of keyAlgorithm is "KMAC256":
444            //     Set the alg attribute of jwk to the string "K256".
445            if key_algorithm.name == CryptoAlgorithm::Kmac128 {
446                jwk.alg = Some(DOMString::from("K128"));
447            }
448            if key_algorithm.name == CryptoAlgorithm::Kmac256 {
449                jwk.alg = Some(DOMString::from("K256"));
450            }
451
452            // Step 4.6. Set the key_ops attribute of jwk to equal the usages attribute of key.
453            jwk.set_key_ops(&key.usages());
454
455            // Step 4.7. Set the ext attribute of jwk to equal the [[extractable]] internal slot of
456            // key.
457            jwk.ext = Some(key.Extractable());
458
459            // Step 4.8. Let result be jwk.
460            ExportedKey::new_jwk(jwk)
461        },
462        _ => {
463            // throw a NotSupportedError.
464            return Err(Error::NotSupported(Some(
465                "Unsupported import key format for KMAC".into(),
466            )));
467        },
468    };
469
470    // Step 5. Return result.
471    Ok(result)
472}
473
474/// <https://wicg.github.io/webcrypto-modern-algos/#kmac-operations-get-key-length>
475pub(crate) fn get_key_length(
476    normalized_algorithm: &SubtleKmacImportParams,
477) -> Result<Option<u32>, Error> {
478    // Step 1.
479    // If the length member of normalizedAlgorithm is present:
480    //     Let length be equal to the length member of normalizedAlgorithm.
481    // Otherwise, if the name member of normalizedAlgorithm is a case-sensitive string match for
482    // "KMAC128":
483    //     Let length be 128.
484    // Otherwise, if the name member of normalizedAlgorithm is a case-sensitive string match for
485    // "KMAC256":
486    //     Let length be 256.
487    let length = if let Some(normalized_algorithm_length) = normalized_algorithm.length {
488        normalized_algorithm_length
489    } else if normalized_algorithm.name == CryptoAlgorithm::Kmac128 {
490        128
491    } else if normalized_algorithm.name == CryptoAlgorithm::Kmac256 {
492        256
493    } else {
494        return Err(Error::Data(Some(
495            "Unable to determine the length of KMAC key".into(),
496        )));
497    };
498
499    // Step 2. Return length.
500    Ok(Some(length))
501}
502
503/// The KMAC128 function defined in Section 4 of [NIST-SP800-185], using `key` as the K input
504/// parameter, `message` as the X input parameter, `output_length` as the L input parameter, and
505/// `customization` as the S input parameter, where `key_length` is the key length in bits.
506/// <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf>
507///
508/// Since the key is represented as a byte sequence, and the key length in bits might not be a
509/// muliple of 8, callers need to provide the actual key length in bits via the function argument
510/// `key_length`, and the excess bits at the end of the byte sequence must be zeros.
511fn kmac128(
512    key: &[u8],
513    key_length: u32,
514    message: &[u8],
515    output_length: u32,
516    customization: &[u8],
517) -> Vec<u8> {
518    kmac::<168>(key, key_length, message, output_length, customization)
519}
520
521/// The KMAC256 function defined in Section 4 of [NIST-SP800-185], using `key` as the K input
522/// parameter, `message` as the X input parameter, `output_length` as the L input parameter, and
523/// `customization` as the S input parameter, where `key_length` is the key length in bits.
524/// <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf>
525///
526/// Since the key is represented as a byte sequence, and the key length in bits might not be a
527/// muliple of 8, callers need to provide the actual key length in bits via the function argument
528/// `key_length`, and the excess bits at the end of the byte sequence must be zeros.
529fn kmac256(
530    key: &[u8],
531    key_length: u32,
532    message: &[u8],
533    output_length: u32,
534    customization: &[u8],
535) -> Vec<u8> {
536    kmac::<136>(key, key_length, message, output_length, customization)
537}
538
539/// A shared implementation for both the KMAC128 function and KMAC256 function defined in Section 4
540/// of [NIST-SP800-185], using `key` as the K input parameter, `message` as the X input parameter,
541/// `output_length` as the L input parameter, and `customization` as the S input parameter, where
542/// `key_length` is the key length in bits.
543/// <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf>
544///
545/// To be specific, this function implements the following algorithm, where RATE must be either 168
546/// or 136 for KMAC128 and KMAC256 respectively.
547///
548/// 1. newX = bytepad(encode_string(K), RATE) || X || right_encode(L).
549/// 2. return cSHAKE128(newX, L, “KMAC”, S).
550///
551/// Since the key is represented as a byte sequence, and the key length in bits might not be a
552/// muliple of 8, callers need to provide the actual key length in bits via the function argument
553/// `key_length`, and the excess bits at the end of the byte sequence must be zeros.
554fn kmac<const RATE: usize>(
555    key: &[u8],
556    key_length: u32,
557    message: &[u8],
558    output_length: u32,
559    customization: &[u8],
560) -> Vec<u8> {
561    let mut buffer = [0u8; 5];
562    let zeros = [0u8; RATE];
563
564    // Initialize cSHAKE.
565    let mut cshake = CShake::<RATE>::new_with_function_name(b"KMAC", customization);
566
567    // Hash bytepad(encode_string(K), RATE).
568    let update_and_return_written = |cshake: &mut CShake<RATE>, input: &[u8]| -> usize {
569        cshake.update(input);
570        input.len()
571    };
572    let mut written = update_and_return_written(&mut cshake, left_encode(RATE as u32, &mut buffer));
573    written += update_and_return_written(&mut cshake, left_encode(key_length, &mut buffer));
574    written += update_and_return_written(&mut cshake, key);
575    cshake.update(&zeros[0..RATE - written % RATE]);
576
577    // Hash X.
578    cshake.update(message);
579
580    // Hash right_encode(L).
581    cshake.update(right_encode(output_length, &mut buffer));
582
583    // Finalize cSHAKE.
584    let mut result = vec![0u8; output_length.div_ceil(8) as usize];
585    cshake.finalize_xof_into(&mut result);
586    if !output_length.is_multiple_of(8) {
587        // Clean excess bits in the last byte of result.
588        let mask = u8::MAX << (8 - output_length % 8);
589        if let Some(last_byte) = result.last_mut() {
590            *last_byte &= mask;
591        }
592    }
593
594    result
595}
596
597/// The right_encode function defined in Section 2.3.1 of [NIST-SP800-185], using `value` as the x
598/// input parameter. <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf>
599///
600/// Callers need to provide `buffer` for internal operations. The buffer can be reused afterwards in
601/// separate calls of [`right_encode`] and [`left_encode`].
602fn right_encode(value: u32, buffer: &mut [u8; 5]) -> &[u8] {
603    buffer[0..4].copy_from_slice(&value.to_be_bytes());
604    let leading_zero = buffer[0..3].iter().take_while(|byte| **byte == 0).count();
605    buffer[4] = u8::try_from(4 - leading_zero).expect("The number must be 1..=4");
606    &buffer[leading_zero..5]
607}
608
609/// The left_encode function defined in Section 2.3.1 of [NIST-SP800-185], using `value` as the x
610/// input parameter. <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf>
611///
612/// Callers need to provide `buffer` for internal operations. The buffer can be reused afterwards in
613/// separate calls of [`right_encode`] and [`left_encode`].
614fn left_encode(value: u32, buffer: &mut [u8; 5]) -> &[u8] {
615    buffer[1..5].copy_from_slice(&value.to_be_bytes());
616    let leading_zero = buffer[1..4].iter().take_while(|byte| **byte == 0).count();
617    buffer[leading_zero] = u8::try_from(4 - leading_zero).expect("The number must be 1..=4");
618    &buffer[leading_zero..5]
619}