Skip to content

Commit d231bf8

Browse files
committed
arithmetic: Rewrite limbs_reduce_once.
Move the function to `arithmetic` from `limb`. This is step towards moving all arithmetic out of `limb`. Change the signature so that the reduction is done separately instead of in-place. It was already being done separately in `bigint` and it costs very little, if anything, to do the same in the other caller in `ec`. Optimize the implementation to take advantage of the fact that `r` and `a` do not alias each other. To do so, replace `LIMBS_reduce_once` with two separate helper functions, `LIMBS_sub` and `LIMBS_cmov`. As part of doing this, ensure we're not passing any empty slices to the relevant C code. ``` git difftool HEAD^1:src/limb.rs src/arithmetic/limbs/reduce_once.rs ```
1 parent a5220ab commit d231bf8

File tree

12 files changed

+171
-52
lines changed

12 files changed

+171
-52
lines changed

build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,9 +878,10 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
878878
"LIMBS_are_zero",
879879
"LIMBS_equal",
880880
"LIMBS_less_than",
881-
"LIMBS_reduce_once",
881+
"LIMBS_select",
882882
"LIMBS_select_512_32",
883883
"LIMBS_shl_mod",
884+
"LIMBS_sub",
884885
"LIMBS_sub_mod",
885886
"LIMBS_window5_split_window",
886887
"LIMBS_window5_unsplit_window",

crypto/limbs/limbs.c

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,6 @@ Limb LIMBS_less_than(const Limb a[], const Limb b[], size_t num_limbs) {
6161
return constant_time_is_nonzero_w(borrow);
6262
}
6363

64-
/* if (r >= m) { r -= m; } */
65-
void LIMBS_reduce_once(Limb r[], const Limb m[], size_t num_limbs) {
66-
debug_assert_nonsecret(num_limbs >= 1);
67-
/* This could be done more efficiently if we had |num_limbs| of extra space
68-
* available, by storing |r - m| and then doing a conditional copy of either
69-
* |r| or |r - m|. But, in order to operate in constant space, with an eye
70-
* towards this function being used in RSA in the future, we do things a
71-
* slightly less efficient way. */
72-
Limb lt = LIMBS_less_than(r, m, num_limbs);
73-
Carry borrow =
74-
limb_sub(&r[0], r[0], constant_time_select_w(lt, 0, m[0]));
75-
for (size_t i = 1; i < num_limbs; ++i) {
76-
/* XXX: This is probably particularly inefficient because the operations in
77-
* constant_time_select affect the carry flag, so there will likely be
78-
* loads and stores of |borrow|. */
79-
borrow =
80-
limb_sbb(&r[i], r[i], constant_time_select_w(lt, 0, m[i]), borrow);
81-
}
82-
dev_assert_secret(borrow == 0);
83-
}
84-
8564
void LIMBS_add_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],
8665
size_t num_limbs) {
8766
Limb overflow1 =
@@ -94,6 +73,14 @@ void LIMBS_add_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],
9473
}
9574
}
9675

76+
// r, `a` and/or `b` may alias.
77+
Limb LIMBS_sub(Limb a_high, Limb r[], const Limb a[], const Limb b[], size_t num_limbs) {
78+
debug_assert_nonsecret(num_limbs >= 1);
79+
Carry underflow = limbs_sub(r, a, b, num_limbs);
80+
limb_sbb(&a_high, a_high, 0, underflow);
81+
return a_high;
82+
}
83+
9784
void LIMBS_sub_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],
9885
size_t num_limbs) {
9986
Limb underflow =
@@ -122,6 +109,13 @@ void LIMBS_shl_mod(Limb r[], const Limb a[], const Limb m[], size_t num_limbs) {
122109
}
123110
}
124111

112+
// r, a, and/or b may alias.
113+
void LIMBS_select(Limb cond, Limb r[], const Limb a[], const Limb b[], size_t num_limbs) {
114+
for (size_t i = 0; i < num_limbs; ++i) {
115+
r[i] = constant_time_select_w(cond, a[i], b[i]);
116+
}
117+
}
118+
125119
int LIMBS_select_512_32(Limb r[], const Limb table[], size_t num_limbs,
126120
crypto_word_t index) {
127121
if (num_limbs % (512 / LIMB_BITS) != 0) {

crypto/limbs/limbs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ typedef crypto_word_t Limb;
2727

2828
Limb LIMBS_are_zero(const Limb a[], size_t num_limbs);
2929
Limb LIMBS_equal(const Limb a[], const Limb b[], size_t num_limbs);
30-
void LIMBS_reduce_once(Limb r[], const Limb m[], size_t num_limbs);
3130
void LIMBS_add_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],
3231
size_t num_limbs);
3332
void LIMBS_sub_mod(Limb r[], const Limb a[], const Limb b[], const Limb m[],

src/arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mod constant;
2424
pub mod bigint;
2525

2626
pub(crate) mod inout;
27-
mod limbs;
27+
pub mod limbs;
2828
mod limbs512;
2929
pub mod montgomery;
3030

src/arithmetic/bigint.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub(crate) use self::{
4242
modulusvalue::OwnedModulusValue,
4343
private_exponent::PrivateExponent,
4444
};
45-
use super::{inout::AliasingSlices3, limbs512, montgomery::*, LimbSliceError, MAX_LIMBS};
45+
use super::{inout::AliasingSlices3, limbs::*, limbs512, montgomery::*, LimbSliceError, MAX_LIMBS};
4646
use crate::{
4747
bits::BitLength,
4848
c,
@@ -213,8 +213,8 @@ pub fn elem_reduced_once<A, M>(
213213
other_modulus_len_bits: BitLength,
214214
) -> Elem<M, Unencoded> {
215215
assert_eq!(m.len_bits(), other_modulus_len_bits);
216-
r.limbs.copy_from_slice(&a.limbs);
217-
limb::limbs_reduce_once(&mut r.limbs, m.limbs())
216+
217+
limbs_reduce_once(&mut r.limbs, &a.limbs, m.limbs())
218218
.unwrap_or_else(unwrap_impossible_len_mismatch_error);
219219
Elem {
220220
limbs: r.limbs,

src/arithmetic/limbs/fallback/cmov.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2025 Brian Smith.
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
use super::super::super::inout::AliasingSlices3;
16+
use crate::{c, error::LenMismatchError, limb::*};
17+
use core::num::NonZeroUsize;
18+
19+
// `if cond { r = a; }`, assuming `cond` is 0 (false) or 0xff..ff (true).
20+
pub fn limbs_cmov(
21+
cond: Limb,
22+
r: &mut [Limb],
23+
a: &[Limb],
24+
num_limbs: NonZeroUsize,
25+
) -> Result<(), LenMismatchError> {
26+
prefixed_extern! {
27+
// r, a, and/or b may alias.
28+
fn LIMBS_select(
29+
cond: Limb,
30+
r: *mut Limb,
31+
a: *const Limb,
32+
b: *const Limb,
33+
num_limbs: c::NonZero_size_t);
34+
}
35+
(r, a).with_non_dangling_non_null_pointers_rab(num_limbs, |r, a, b| unsafe {
36+
LIMBS_select(cond, r, b, a, num_limbs)
37+
})
38+
}

src/arithmetic/limbs/fallback/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2025 Brian Smith.
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
pub(super) mod cmov;
16+
pub(super) mod sub;

src/arithmetic/limbs/fallback/sub.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 Brian Smith.
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
use super::super::super::inout::AliasingSlices3;
16+
use crate::{c, error::LenMismatchError, limb::*};
17+
use core::num::NonZeroUsize;
18+
19+
/// r = a - b, where `a` is considered to have `a_high` appended to it as its most
20+
/// significant word. The new value of `a_high` is returned.
21+
#[inline]
22+
pub fn limbs_sub(
23+
a_high: Limb,
24+
in_out: impl AliasingSlices3<Limb>,
25+
num_limbs: NonZeroUsize,
26+
) -> Result<Limb, LenMismatchError> {
27+
prefixed_extern! {
28+
// `r`, 'a', and/or `b` may alias.
29+
fn LIMBS_sub(
30+
a_high: Limb,
31+
r: *mut Limb,
32+
a: *const Limb,
33+
b: *const Limb,
34+
num_limbs: c::NonZero_size_t)
35+
-> Limb;
36+
}
37+
in_out.with_non_dangling_non_null_pointers_rab(num_limbs, |r, a, b| unsafe {
38+
LIMBS_sub(a_high, r, a, b, num_limbs)
39+
})
40+
}

src/arithmetic/limbs/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,11 @@
1212
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1313
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1414

15+
mod fallback;
16+
mod reduce_once;
17+
1518
pub(super) mod aarch64;
1619
pub(super) mod x86_64;
20+
21+
pub(crate) use self::reduce_once::limbs_reduce_once;
22+
use fallback::{cmov::limbs_cmov, sub::limbs_sub};

src/arithmetic/limbs/reduce_once.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2025 Brian Smith.
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
use super::*;
16+
use crate::{error::LenMismatchError, limb::*};
17+
use core::num::NonZeroUsize;
18+
19+
/// Equivalent to `if (r >= m) { r -= m; }`
20+
#[inline]
21+
pub fn limbs_reduce_once(r: &mut [Limb], a: &[Limb], m: &[Limb]) -> Result<(), LenMismatchError> {
22+
let num_limbs = NonZeroUsize::new(m.len()).ok_or_else(|| LenMismatchError::new(m.len()))?;
23+
reduce_once(0, r, a, m, num_limbs)
24+
}
25+
26+
fn reduce_once(
27+
a_high: Limb,
28+
r: &mut [Limb],
29+
a: &[Limb],
30+
m: &[Limb],
31+
num_limbs: NonZeroUsize,
32+
) -> Result<(), LenMismatchError> {
33+
#[allow(clippy::useless_asref)]
34+
let borrow = limbs_sub(a_high, (r.as_mut(), a, m), num_limbs)?;
35+
limbs_cmov(borrow, r, a, num_limbs)
36+
}

src/ec/suite_b/ops.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
use crate::{
1616
arithmetic::limbs_from_hex,
17-
arithmetic::montgomery::*,
17+
arithmetic::{limbs::*, montgomery::*},
1818
bb::LeakyWord,
1919
cpu,
2020
error::{self, LenMismatchError},
@@ -491,11 +491,15 @@ fn twin_mul_inefficient(
491491

492492
// This assumes n < q < 2*n.
493493
impl Modulus<N> {
494-
pub fn elem_reduced_to_scalar(&self, elem: &Elem<Unencoded>) -> Scalar<Unencoded> {
494+
pub fn elem_reduced_to_scalar(&self, a: &Elem<Unencoded>) -> Scalar<Unencoded> {
495495
let num_limbs = self.num_limbs.into();
496-
let mut r_limbs = elem.limbs;
497-
limbs_reduce_once(&mut r_limbs[..num_limbs], &self.limbs[..num_limbs])
498-
.unwrap_or_else(unwrap_impossible_len_mismatch_error);
496+
let mut r_limbs = a.limbs;
497+
limbs_reduce_once(
498+
&mut r_limbs[..num_limbs],
499+
&a.limbs[..num_limbs],
500+
&self.limbs[..num_limbs],
501+
)
502+
.unwrap_or_else(unwrap_impossible_len_mismatch_error);
499503
Scalar {
500504
limbs: r_limbs,
501505
m: PhantomData,
@@ -573,14 +577,12 @@ pub(super) fn scalar_parse_big_endian_partially_reduced_variable_consttime(
573577
bytes: untrusted::Input,
574578
) -> Result<Scalar, error::Unspecified> {
575579
let num_limbs = n.num_limbs.into();
580+
let mut unreduced = [0; elem::NumLimbs::MAX];
581+
let unreduced = &mut unreduced[..num_limbs];
582+
parse_big_endian_and_pad_consttime(bytes, unreduced)?;
576583
let mut r = Scalar::zero();
577-
{
578-
let r = &mut r.limbs[..num_limbs];
579-
parse_big_endian_and_pad_consttime(bytes, r)?;
580-
limbs_reduce_once(r, &n.limbs[..num_limbs])
581-
.unwrap_or_else(unwrap_impossible_len_mismatch_error);
582-
}
583-
584+
limbs_reduce_once(&mut r.limbs[..num_limbs], unreduced, &n.limbs[..num_limbs])
585+
.map_err(error::erase::<LenMismatchError>)?;
584586
Ok(r)
585587
}
586588

src/limb.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,6 @@ pub fn limbs_minimal_bits(a: &[Limb]) -> bits::BitLength {
152152
bits::BitLength::from_bits(0)
153153
}
154154

155-
/// Equivalent to `if (r >= m) { r -= m; }`
156-
#[inline]
157-
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-
let num_limbs = NonZeroUsize::new(r.len()).ok_or_else(|| LenMismatchError::new(m.len()))?;
162-
let r = r.as_mut_ptr(); // Non-dangling because num_limbs is non-zero.
163-
let m = m.as_ptr(); // Non-dangling because num_limbs is non-zero.
164-
unsafe { LIMBS_reduce_once(r, m, num_limbs) };
165-
Ok(())
166-
}
167-
168155
#[derive(Clone, Copy, PartialEq)]
169156
pub enum AllowZero {
170157
No,

0 commit comments

Comments
 (0)