Coverage Report

Created: 2025-07-12 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/limb.rs
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2016 David Judd.
2
// Copyright 2016 Brian Smith.
3
//
4
// Permission to use, copy, modify, and/or distribute this software for any
5
// purpose with or without fee is hereby granted, provided that the above
6
// copyright notice and this permission notice appear in all copies.
7
//
8
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16
//! Unsigned multi-precision integer arithmetic.
17
//!
18
//! Limbs ordered least-significant-limb to most-significant-limb. The bits
19
//! limbs use the native endianness.
20
21
use crate::{
22
    arithmetic::inout::{AliasingSlices2, AliasingSlices3},
23
    bb, c,
24
    error::{self, LenMismatchError},
25
    polyfill::{sliceutil, usize_from_u32, ArrayFlatMap},
26
};
27
use core::{iter, num::NonZeroUsize};
28
29
#[cfg(any(test, feature = "alloc"))]
30
use crate::bits;
31
32
#[cfg(feature = "alloc")]
33
use core::num::Wrapping;
34
35
// XXX: Not correct for x32 ABIs.
36
pub type Limb = bb::Word;
37
pub type LeakyLimb = bb::LeakyWord;
38
pub const LIMB_BITS: usize = usize_from_u32(Limb::BITS);
39
pub const LIMB_BYTES: usize = (LIMB_BITS + 7) / 8;
40
41
pub type LimbMask = bb::BoolMask;
42
43
#[inline]
44
0
pub fn limbs_equal_limbs_consttime(a: &[Limb], b: &[Limb]) -> Result<LimbMask, LenMismatchError> {
45
0
    if a.len() != b.len() {
46
0
        return Err(LenMismatchError::new(a.len()));
47
0
    }
48
0
    let all = a.iter().zip(b).fold(0, |running, (a, b)| running | (a ^ b));
49
0
    Ok(limb_is_zero(all))
50
0
}
51
52
#[inline]
53
0
fn limbs_less_than_limbs(a: &[Limb], b: &[Limb]) -> Result<LimbMask, LenMismatchError> {
54
    prefixed_extern! {
55
        fn LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::NonZero_size_t)
56
            -> LimbMask;
57
    }
58
    // Use `b.len` because usually `b` will be the modulus which is likely to
59
    // have had its length checked already so this can be elided by the
60
    // optimizer.
61
    // XXX: Questionable whether `LenMismatchError` is appropriate.
62
0
    let len = NonZeroUsize::new(b.len()).ok_or_else(|| LenMismatchError::new(a.len()))?;
63
0
    if a.len() != len.get() {
64
0
        return Err(LenMismatchError::new(a.len()));
65
0
    }
66
0
    Ok(unsafe { LIMBS_less_than(a.as_ptr(), b.as_ptr(), len) })
67
0
}
68
69
#[inline]
70
0
pub(crate) fn verify_limbs_less_than_limbs_leak_bit(
71
0
    a: &[Limb],
72
0
    b: &[Limb],
73
0
) -> Result<(), error::Unspecified> {
74
0
    let r = limbs_less_than_limbs(a, b).map_err(error::erase::<LenMismatchError>)?;
75
0
    if r.leak() {
76
0
        Ok(())
77
    } else {
78
0
        Err(error::Unspecified)
79
    }
80
0
}
81
82
#[inline]
83
0
pub fn limbs_less_than_limbs_vartime(a: &[Limb], b: &[Limb]) -> Result<bool, LenMismatchError> {
84
0
    let r = limbs_less_than_limbs(a, b)?;
85
0
    Ok(r.leak())
86
0
}
87
88
#[inline]
89
0
fn limb_is_zero(limb: Limb) -> LimbMask {
90
0
    prefixed_extern! {
91
0
        fn LIMB_is_zero(limb: Limb) -> LimbMask;
92
0
    }
93
0
    unsafe { LIMB_is_zero(limb) }
94
0
}
95
96
#[inline]
97
0
pub fn limbs_are_zero(limbs: &[Limb]) -> LimbMask {
98
0
    limb_is_zero(limbs.iter().fold(0, |a, b| a | b))
99
0
}
100
101
/// Leaks one bit of information (other than the lengths of the inputs):
102
/// Whether the given limbs are even.
103
#[cfg(any(test, feature = "alloc"))]
104
#[inline]
105
0
pub fn limbs_reject_even_leak_bit(limbs: &[Limb]) -> Result<(), error::Unspecified> {
106
0
    let bottom = *limbs.first().ok_or(error::Unspecified)?;
107
0
    if limb_is_zero(bottom & 1).leak() {
108
0
        return Err(error::Unspecified);
109
0
    }
110
0
    Ok(())
111
0
}
112
113
#[cfg(any(test, feature = "alloc"))]
114
#[inline]
115
0
pub fn verify_limbs_equal_1_leak_bit(a: &[Limb]) -> Result<(), error::Unspecified> {
116
0
    if let [bottom, ref rest @ ..] = *a {
117
0
        let equal = limb_is_zero(bottom ^ 1) & limbs_are_zero(rest);
118
0
        if equal.leak() {
119
0
            return Ok(());
120
0
        }
121
0
    }
122
0
    Err(error::Unspecified)
123
0
}
124
125
/// Returns the number of bits in `a`.
126
//
127
// This strives to be constant-time with respect to the values of all bits
128
// except the most significant bit. This does not attempt to be constant-time
129
// with respect to `a.len()` or the value of the result or the value of the
130
// most significant bit (It's 1, unless the input is zero, in which case it's
131
// zero.)
132
#[cfg(any(test, feature = "alloc"))]
133
0
pub fn limbs_minimal_bits(a: &[Limb]) -> bits::BitLength {
134
0
    for num_limbs in (1..=a.len()).rev() {
135
0
        let high_limb = a[num_limbs - 1];
136
137
        // Find the number of set bits in |high_limb| by a linear scan from the
138
        // most significant bit to the least significant bit. This works great
139
        // for the most common inputs because usually the most significant bit
140
        // it set.
141
0
        for high_limb_num_bits in (1..=LIMB_BITS).rev() {
142
0
            let shifted = unsafe { LIMB_shr(high_limb, high_limb_num_bits - 1) };
143
0
            if shifted != 0 {
144
0
                return bits::BitLength::from_bits(
145
0
                    ((num_limbs - 1) * LIMB_BITS) + high_limb_num_bits,
146
0
                );
147
0
            }
148
        }
149
    }
150
151
    // No bits were set.
152
0
    bits::BitLength::from_bits(0)
153
0
}
154
155
/// Equivalent to `if (r >= m) { r -= m; }`
156
#[inline]
157
0
pub fn limbs_reduce_once(r: &mut [Limb], m: &[Limb]) -> Result<(), LenMismatchError> {
158
    prefixed_extern! {
159
        fn LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::NonZero_size_t);
160
    }
161
0
    let num_limbs = NonZeroUsize::new(r.len()).ok_or_else(|| LenMismatchError::new(m.len()))?;
162
0
    let r = r.as_mut_ptr(); // Non-dangling because num_limbs is non-zero.
163
0
    let m = m.as_ptr(); // Non-dangling because num_limbs is non-zero.
164
0
    unsafe { LIMBS_reduce_once(r, m, num_limbs) };
165
0
    Ok(())
166
0
}
167
168
#[derive(Clone, Copy, PartialEq)]
169
pub enum AllowZero {
170
    No,
171
    Yes,
172
}
173
174
/// Parses `input` into `result`, verifies that the value is less than
175
/// `max_exclusive`, and pads `result` with zeros to its length. If `allow_zero`
176
/// is not `AllowZero::Yes`, zero values are rejected.
177
///
178
/// This attempts to be constant-time with respect to the actual value *only if*
179
/// the value is actually in range. In other words, this won't leak anything
180
/// about a valid value, but it might leak small amounts of information about an
181
/// invalid value (which constraint it failed).
182
0
pub fn parse_big_endian_in_range_and_pad_consttime(
183
0
    input: untrusted::Input,
184
0
    allow_zero: AllowZero,
185
0
    max_exclusive: &[Limb],
186
0
    result: &mut [Limb],
187
0
) -> Result<(), error::Unspecified> {
188
0
    parse_big_endian_and_pad_consttime(input, result)?;
189
0
    verify_limbs_less_than_limbs_leak_bit(result, max_exclusive)?;
190
0
    if allow_zero != AllowZero::Yes {
191
0
        if limbs_are_zero(result).leak() {
192
0
            return Err(error::Unspecified);
193
0
        }
194
0
    }
195
0
    Ok(())
196
0
}
197
198
/// Parses `input` into `result`, padding `result` with zeros to its length.
199
/// This attempts to be constant-time with respect to the value but not with
200
/// respect to the length; it is assumed that the length is public knowledge.
201
0
pub fn parse_big_endian_and_pad_consttime(
202
0
    input: untrusted::Input,
203
0
    result: &mut [Limb],
204
0
) -> Result<(), error::Unspecified> {
205
0
    if input.is_empty() {
206
0
        return Err(error::Unspecified);
207
0
    }
208
0
    let input_limbs = input.as_slice_less_safe().rchunks(LIMB_BYTES).map(|chunk| {
209
0
        let mut padded = [0; LIMB_BYTES];
210
0
        sliceutil::overwrite_at_start(&mut padded[(LIMB_BYTES - chunk.len())..], chunk);
211
0
        Limb::from_be_bytes(padded)
212
0
    });
213
0
    if input_limbs.len() > result.len() {
214
0
        return Err(error::Unspecified);
215
0
    }
216
0
217
0
    result
218
0
        .iter_mut()
219
0
        .zip(input_limbs.chain(iter::repeat(0)))
220
0
        .for_each(|(r, i)| *r = i);
221
0
222
0
    Ok(())
223
0
}
224
225
0
pub fn big_endian_from_limbs(limbs: &[Limb], out: &mut [u8]) {
226
0
    let be_bytes = unstripped_be_bytes(limbs);
227
0
    assert_eq!(out.len(), be_bytes.len());
228
0
    out.iter_mut().zip(be_bytes).for_each(|(o, i)| {
229
0
        *o = i;
230
0
    });
231
0
}
232
233
/// Returns an iterator of the big-endian encoding of `limbs`.
234
///
235
/// The number of bytes returned will be a multiple of `LIMB_BYTES`
236
/// and thus may be padded with leading zeros.
237
0
pub fn unstripped_be_bytes(limbs: &[Limb]) -> impl ExactSizeIterator<Item = u8> + Clone + '_ {
238
0
    // The unwrap is safe because a slice can never be larger than `usize` bytes.
239
0
    ArrayFlatMap::new(limbs.iter().rev().copied(), Limb::to_be_bytes).unwrap()
240
0
}
241
242
// Used in FFI
243
pub type Window = bb::Word;
244
245
// Used in FFI
246
pub type LeakyWindow = bb::LeakyWord;
247
248
/// Processes `limbs` as a sequence of 5-bit windows, folding the windows from
249
/// most significant to least significant and returning the accumulated result.
250
/// The first window will be mapped by `init` to produce the initial value for
251
/// the accumulator. Then `f` will be called to fold the accumulator and the
252
/// next window until all windows are processed. When the input's bit length
253
/// isn't divisible by 5, the window passed to `init` will be partial; all
254
/// windows passed to `fold` will be full.
255
///
256
/// This is designed to avoid leaking the contents of `limbs` through side
257
/// channels as long as `init` and `fold` are side-channel free.
258
///
259
/// Panics if `limbs` is empty.
260
#[cfg(feature = "alloc")]
261
0
pub fn fold_5_bit_windows<R, I: FnOnce(Window) -> R, F: Fn(R, Window) -> R>(
262
0
    limbs: &[Limb],
263
0
    init: I,
264
0
    fold: F,
265
0
) -> R {
266
    #[derive(Clone, Copy)]
267
    #[repr(transparent)]
268
    struct BitIndex(Wrapping<c::size_t>);
269
270
    const WINDOW_BITS: Wrapping<c::size_t> = Wrapping(5);
271
272
    prefixed_extern! {
273
        fn LIMBS_window5_split_window(
274
            lower_limb: Limb,
275
            higher_limb: Limb,
276
            index_within_word: BitIndex,
277
        ) -> Window;
278
        fn LIMBS_window5_unsplit_window(limb: Limb, index_within_word: BitIndex) -> Window;
279
    }
280
281
0
    let num_limbs = limbs.len();
282
0
    let mut window_low_bit = {
283
0
        let num_whole_windows = (num_limbs * LIMB_BITS) / 5;
284
0
        let mut leading_bits = (num_limbs * LIMB_BITS) - (num_whole_windows * 5);
285
0
        if leading_bits == 0 {
286
0
            leading_bits = WINDOW_BITS.0;
287
0
        }
288
0
        BitIndex(Wrapping(LIMB_BITS - leading_bits))
289
0
    };
290
0
291
0
    let initial_value = {
292
0
        let leading_partial_window =
293
0
            unsafe { LIMBS_window5_split_window(*limbs.first().unwrap(), 0, window_low_bit) };
294
0
        window_low_bit.0 -= WINDOW_BITS;
295
0
        init(leading_partial_window)
296
0
    };
297
0
298
0
    let mut low_limb = Limb::from(0 as LeakyWindow);
299
0
    limbs.iter().fold(initial_value, |mut acc, current_limb| {
300
0
        let higher_limb = low_limb;
301
0
        low_limb = *current_limb;
302
0
303
0
        if window_low_bit.0 > Wrapping(LIMB_BITS) - WINDOW_BITS {
304
0
            let window =
305
0
                unsafe { LIMBS_window5_split_window(low_limb, higher_limb, window_low_bit) };
306
0
            window_low_bit.0 -= WINDOW_BITS;
307
0
            acc = fold(acc, window);
308
0
        };
309
0
        while window_low_bit.0 < Wrapping(LIMB_BITS) {
310
0
            let window = unsafe { LIMBS_window5_unsplit_window(low_limb, window_low_bit) };
311
0
            // The loop exits when this subtraction underflows, causing `window_low_bit` to
312
0
            // wrap around to a very large value.
313
0
            window_low_bit.0 -= WINDOW_BITS;
314
0
            acc = fold(acc, window);
315
0
        }
316
0
        window_low_bit.0 += Wrapping(LIMB_BITS); // "Fix" the underflow.
317
0
318
0
        acc
319
0
    })
Unexecuted instantiation: ring::limb::fold_5_bit_windows::<ring::polyfill::slice::as_chunks_mut::AsChunksMut<u64, 8>, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::P, 1120>::{closure#0}, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::P, 1120>::{closure#1}>::{closure#0}
Unexecuted instantiation: ring::limb::fold_5_bit_windows::<ring::polyfill::slice::as_chunks_mut::AsChunksMut<u64, 8>, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::Q, 1120>::{closure#0}, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::Q, 1120>::{closure#1}>::{closure#0}
320
0
}
Unexecuted instantiation: ring::limb::fold_5_bit_windows::<ring::polyfill::slice::as_chunks_mut::AsChunksMut<u64, 8>, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::P, 1120>::{closure#0}, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::P, 1120>::{closure#1}>
Unexecuted instantiation: ring::limb::fold_5_bit_windows::<ring::polyfill::slice::as_chunks_mut::AsChunksMut<u64, 8>, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::Q, 1120>::{closure#0}, ring::arithmetic::bigint::elem_exp_consttime_inner<ring::rsa::N, ring::rsa::keypair::Q, 1120>::{closure#1}>
321
322
#[inline]
323
0
pub(crate) fn limbs_add_assign_mod(
324
0
    a: &mut [Limb],
325
0
    b: &[Limb],
326
0
    m: &[Limb],
327
0
) -> Result<(), LenMismatchError> {
328
    prefixed_extern! {
329
        // `r` and `a` may alias.
330
        fn LIMBS_add_mod(
331
            r: *mut Limb,
332
            a: *const Limb,
333
            b: *const Limb,
334
            m: *const Limb,
335
            num_limbs: c::NonZero_size_t,
336
        );
337
    }
338
0
    let num_limbs = NonZeroUsize::new(m.len()).ok_or_else(|| LenMismatchError::new(m.len()))?;
339
0
    (a, b).with_non_dangling_non_null_pointers_rab(num_limbs, |r, a, b| {
340
0
        let m = m.as_ptr(); // Also non-dangling because `num_limbs` is non-zero.
341
0
        unsafe { LIMBS_add_mod(r, a, b, m, num_limbs) }
342
0
    })
343
0
}
344
345
// r *= 2 (mod m).
346
0
pub(crate) fn limbs_double_mod(r: &mut [Limb], m: &[Limb]) -> Result<(), LenMismatchError> {
347
    prefixed_extern! {
348
        // `r` and `a` may alias.
349
        fn LIMBS_shl_mod(
350
            r: *mut Limb,
351
            a: *const Limb,
352
            m: *const Limb,
353
            num_limbs: c::NonZero_size_t);
354
    }
355
0
    let num_limbs = NonZeroUsize::new(m.len()).ok_or_else(|| LenMismatchError::new(m.len()))?;
356
0
    r.with_non_dangling_non_null_pointers_ra(num_limbs, |r, a| {
357
0
        let m = m.as_ptr(); // Also non-dangling because num_limbs > 0.
358
0
        unsafe {
359
0
            LIMBS_shl_mod(r, a, m, num_limbs);
360
0
        }
361
0
    })
362
0
}
363
364
// *r = -a, assuming a is odd.
365
0
pub(crate) fn limbs_negative_odd(r: &mut [Limb], a: &[Limb]) {
366
0
    debug_assert_eq!(r.len(), a.len());
367
    // Two's complement step 1: flip all the bits.
368
    // The compiler should optimize this to vectorized (a ^ !0).
369
0
    r.iter_mut().zip(a.iter()).for_each(|(r, &a)| {
370
0
        *r = !a;
371
0
    });
372
0
    // Two's complement step 2: Add one. Since `a` is odd, `r` is even. Thus we
373
0
    // can use a bitwise or for addition.
374
0
    r[0] |= 1;
375
0
}
376
377
#[cfg(any(test, feature = "alloc"))]
378
prefixed_extern! {
379
    fn LIMB_shr(a: Limb, shift: c::size_t) -> Limb;
380
}
381
382
#[allow(clippy::useless_conversion)]
383
#[cfg(test)]
384
mod tests {
385
    use super::*;
386
    use alloc::vec::Vec;
387
    use cfg_if::cfg_if;
388
389
    const MAX: LeakyLimb = LeakyLimb::MAX;
390
391
    fn leak_in_test(a: LimbMask) -> bool {
392
        a.leak()
393
    }
394
395
    #[test]
396
    fn test_limbs_are_even() {
397
        static EVENS: &[&[LeakyLimb]] = &[
398
            &[],
399
            &[0],
400
            &[2],
401
            &[0, 0],
402
            &[2, 0],
403
            &[0, 1],
404
            &[0, 2],
405
            &[0, 3],
406
            &[0, 0, 0, 0, MAX],
407
        ];
408
        for even in EVENS {
409
            let even = &Vec::from_iter(even.iter().copied().map(Limb::from));
410
            assert!(matches!(
411
                limbs_reject_even_leak_bit(even),
412
                Err(error::Unspecified)
413
            ));
414
        }
415
        static ODDS: &[&[LeakyLimb]] = &[
416
            &[1],
417
            &[3],
418
            &[1, 0],
419
            &[3, 0],
420
            &[1, 1],
421
            &[1, 2],
422
            &[1, 3],
423
            &[1, 0, 0, 0, MAX],
424
        ];
425
        for odd in ODDS {
426
            let odd = &Vec::from_iter(odd.iter().copied().map(Limb::from));
427
            assert!(matches!(limbs_reject_even_leak_bit(odd), Ok(())));
428
        }
429
    }
430
431
    static ZEROES: &[&[LeakyLimb]] = &[
432
        &[],
433
        &[0],
434
        &[0, 0],
435
        &[0, 0, 0],
436
        &[0, 0, 0, 0],
437
        &[0, 0, 0, 0, 0],
438
        &[0, 0, 0, 0, 0, 0, 0],
439
        &[0, 0, 0, 0, 0, 0, 0, 0],
440
        &[0, 0, 0, 0, 0, 0, 0, 0, 0],
441
    ];
442
443
    static NONZEROES: &[&[LeakyLimb]] = &[
444
        &[1],
445
        &[0, 1],
446
        &[1, 1],
447
        &[1, 0, 0, 0],
448
        &[0, 1, 0, 0],
449
        &[0, 0, 1, 0],
450
        &[0, 0, 0, 1],
451
    ];
452
453
    #[test]
454
    fn test_limbs_are_zero() {
455
        for zero in ZEROES {
456
            let zero = &Vec::from_iter(zero.iter().copied().map(Limb::from));
457
            assert!(leak_in_test(limbs_are_zero(zero)));
458
        }
459
        for nonzero in NONZEROES {
460
            let nonzero = &Vec::from_iter(nonzero.iter().copied().map(Limb::from));
461
            assert!(!leak_in_test(limbs_are_zero(nonzero)));
462
        }
463
    }
464
465
    #[test]
466
    fn test_limbs_equal_limb() {
467
        // Equal
468
        static EQUAL: &[&[LeakyLimb]] = &[&[1], &[1, 0], &[1, 0, 0], &[1, 0, 0, 0, 0, 0, 0]];
469
        for a in EQUAL {
470
            let a = &Vec::from_iter(a.iter().copied().map(Limb::from));
471
            assert!(matches!(verify_limbs_equal_1_leak_bit(a), Ok(())));
472
        }
473
474
        // Unequal
475
        static UNEQUAL: &[&[LeakyLimb]] = &[
476
            &[0],
477
            &[2],
478
            &[3],
479
            &[MAX],
480
            &[0, 1],
481
            &[1, 1],
482
            &[0, 0, 0, 0, 0, 0, 0, 1],
483
            &[0, 0, 0, 0, 1, 0, 0, 0],
484
            &[0, 0, 0, 0, 1, 0, 0, 1],
485
            &[MAX, 1],
486
        ];
487
        for a in UNEQUAL {
488
            let a = &Vec::from_iter(a.iter().copied().map(Limb::from));
489
            assert!(matches!(
490
                verify_limbs_equal_1_leak_bit(a),
491
                Err(error::Unspecified)
492
            ));
493
        }
494
    }
495
496
    #[test]
497
    fn test_parse_big_endian_and_pad_consttime() {
498
        const LIMBS: usize = 4;
499
500
        {
501
            // Empty input.
502
            let inp = untrusted::Input::from(&[]);
503
            let mut result = [0; LIMBS].map(From::<LeakyLimb>::from);
504
            assert!(parse_big_endian_and_pad_consttime(inp, &mut result).is_err());
505
        }
506
507
        // The input is longer than will fit in the given number of limbs.
508
        {
509
            let inp = [1, 2, 3, 4, 5, 6, 7, 8, 9];
510
            let inp = untrusted::Input::from(&inp);
511
            let mut result = [0; 8 / LIMB_BYTES].map(From::<LeakyLimb>::from);
512
            assert!(parse_big_endian_and_pad_consttime(inp, &mut result[..]).is_err());
513
        }
514
515
        // Less than a full limb.
516
        {
517
            let inp = [0xfe];
518
            let inp = untrusted::Input::from(&inp);
519
            let mut result = [0; LIMBS].map(From::<LeakyLimb>::from);
520
            assert_eq!(
521
                Ok(()),
522
                parse_big_endian_and_pad_consttime(inp, &mut result[..])
523
            );
524
            assert_eq!(&[0xfe, 0, 0, 0], &result);
525
        }
526
527
        // A whole limb for 32-bit, half a limb for 64-bit.
528
        {
529
            let inp = [0xbe, 0xef, 0xf0, 0x0d];
530
            let inp = untrusted::Input::from(&inp);
531
            let mut result = [0; LIMBS].map(From::<LeakyLimb>::from);
532
            assert_eq!(Ok(()), parse_big_endian_and_pad_consttime(inp, &mut result));
533
            assert_eq!(&[0xbeeff00d, 0, 0, 0], &result);
534
        }
535
536
        cfg_if! {
537
            if #[cfg(target_pointer_width = "64")] {
538
                static TEST_CASES: &[(&[u8], &[Limb])] = &[
539
                    (&[1], &[1, 0]),
540
                    (&[1, 2], &[0x102, 0]),
541
                    (&[1, 2, 3], &[0x10203, 0]),
542
                    (&[1, 2, 3, 4], &[0x102_0304, 0]),
543
                    (&[1, 2, 3, 4, 5], &[0x1_0203_0405, 0]),
544
                    (&[1, 2, 3, 4, 5, 6], &[0x102_0304_0506, 0]),
545
                    (&[1, 2, 3, 4, 5, 6, 7], &[0x1_0203_0405_0607, 0]),
546
                    (&[1, 2, 3, 4, 5, 6, 7, 8], &[0x102_0304_0506_0708, 0]),
547
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9], &[0x0203_0405_0607_0809, 0x1]),
548
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa], &[0x0304_0506_0708_090a, 0x102]),
549
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb], &[0x0405_0607_0809_0a0b, 0x1_0203]),
550
                ];
551
                for (be_bytes, limbs) in TEST_CASES {
552
                    let mut buf = [0; 2];
553
                    parse_big_endian_and_pad_consttime(untrusted::Input::from(be_bytes), &mut buf)
554
                        .unwrap();
555
                    assert_eq!(limbs, &buf, "({be_bytes:x?}, {limbs:x?}");
556
                }
557
            } else if #[cfg(target_pointer_width = "32")] {
558
                static TEST_CASES: &[(&[u8], &[Limb])] = &[
559
                    (&[1], &[1, 0, 0]),
560
                    (&[1, 2], &[0x102, 0, 0]),
561
                    (&[1, 2, 3], &[0x10203, 0, 0]),
562
                    (&[1, 2, 3, 4], &[0x102_0304, 0, 0]),
563
                    (&[1, 2, 3, 4, 5], &[0x0203_0405, 0x1, 0]),
564
                    (&[1, 2, 3, 4, 5, 6], &[0x0304_0506, 0x102, 0]),
565
                    (&[1, 2, 3, 4, 5, 6, 7], &[0x0405_0607, 0x1_0203, 0]),
566
                    (&[1, 2, 3, 4, 5, 6, 7, 8], &[0x0506_0708, 0x102_0304, 0]),
567
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9], &[0x0607_0809, 0x0203_0405, 0x1]),
568
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa], &[0x0708_090a, 0x0304_0506, 0x102]),
569
                    (&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb], &[0x0809_0a0b, 0x0405_0607, 0x1_0203]),
570
                ];
571
                for (be_bytes, limbs) in TEST_CASES {
572
                    let mut buf = [0; 3];
573
                    parse_big_endian_and_pad_consttime(untrusted::Input::from(be_bytes), &mut buf)
574
                        .unwrap();
575
                    assert_eq!(limbs, &buf, "({be_bytes:x?}, {limbs:x?}");
576
                }
577
            } else {
578
                panic!("Unsupported target_pointer_width");
579
            }
580
581
            // XXX: This is a weak set of tests. TODO: expand it.
582
        }
583
    }
584
585
    #[test]
586
    fn test_big_endian_from_limbs_same_length() {
587
        #[cfg(target_pointer_width = "32")]
588
        let limbs = [
589
            0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc, 0x55667788,
590
            0x11223344,
591
        ];
592
593
        #[cfg(target_pointer_width = "64")]
594
        let limbs = [
595
            0x8990_0aab_bccd_deef,
596
            0x0112_2334_4556_6778,
597
            0x99aa_bbcc_ddee_ff00,
598
            0x1122_3344_5566_7788,
599
        ];
600
601
        let limbs = limbs.map(From::<LeakyLimb>::from);
602
603
        let expected = [
604
            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
605
            0xff, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0x0a, 0xab,
606
            0xbc, 0xcd, 0xde, 0xef,
607
        ];
608
609
        let mut out = [0xabu8; 32];
610
        big_endian_from_limbs(&limbs[..], &mut out);
611
        assert_eq!(&out[..], &expected[..]);
612
    }
613
614
    #[should_panic]
615
    #[test]
616
    fn test_big_endian_from_limbs_fewer_limbs() {
617
        #[cfg(target_pointer_width = "32")]
618
        // Two fewer limbs.
619
        let limbs = [
620
            0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc,
621
        ];
622
623
        // One fewer limb.
624
        #[cfg(target_pointer_width = "64")]
625
        let limbs = [
626
            0x8990_0aab_bccd_deef,
627
            0x0112_2334_4556_6778,
628
            0x99aa_bbcc_ddee_ff00,
629
        ];
630
631
        let limbs = limbs.map(From::<LeakyLimb>::from);
632
633
        let mut out = [0xabu8; 32];
634
635
        big_endian_from_limbs(&limbs[..], &mut out);
636
    }
637
638
    #[test]
639
    fn test_limbs_minimal_bits() {
640
        const ALL_ONES: LeakyLimb = LeakyLimb::MAX;
641
        static CASES: &[(&[LeakyLimb], usize)] = &[
642
            (&[], 0),
643
            (&[0], 0),
644
            (&[ALL_ONES], LIMB_BITS),
645
            (&[ALL_ONES, 0], LIMB_BITS),
646
            (&[ALL_ONES, 1], LIMB_BITS + 1),
647
            (&[0, 0], 0),
648
            (&[1, 0], 1),
649
            (&[0, 1], LIMB_BITS + 1),
650
            (&[0, ALL_ONES], 2 * LIMB_BITS),
651
            (&[ALL_ONES, ALL_ONES], 2 * LIMB_BITS),
652
            (&[ALL_ONES, ALL_ONES >> 1], 2 * LIMB_BITS - 1),
653
            (&[ALL_ONES, 0b100_0000], LIMB_BITS + 7),
654
            (&[ALL_ONES, 0b101_0000], LIMB_BITS + 7),
655
            (&[ALL_ONES, ALL_ONES >> 1], LIMB_BITS + (LIMB_BITS) - 1),
656
        ];
657
        for (limbs, bits) in CASES {
658
            let limbs = &Vec::from_iter(limbs.iter().copied().map(Limb::from));
659
            assert_eq!(limbs_minimal_bits(limbs).as_bits(), *bits);
660
        }
661
    }
662
}