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}