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}