Skip to main content

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