Skip to content

Commit fc36a41

Browse files
committed
rsa: Allocate public key on the stack in one-shot verification.
1 parent 57bbdbf commit fc36a41

File tree

9 files changed

+129
-29
lines changed

9 files changed

+129
-29
lines changed

src/arithmetic/bigint.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
use crate::polyfill::prelude::*;
4141

4242
use self::boxed_limbs::BoxedLimbs;
43-
use super::{montgomery::*, LimbSliceError};
43+
use super::{montgomery::*, LimbSliceError, MAX_LIMBS};
4444
use crate::{
4545
error::{self, LenMismatchError},
4646
limb::{self, Limb},
@@ -54,6 +54,7 @@ pub(crate) use {
5454
},
5555
exp::elem_exp_consttime,
5656
modulus::{BoxedIntoMont, IntoMont, Mont, One},
57+
oversized_uninit::OversizedUninit,
5758
private_exponent::PrivateExponent,
5859
},
5960
super::exp_vartime::elem_exp_vartime,
@@ -63,6 +64,7 @@ mod boxed_limbs;
6364
mod elem;
6465
mod exp;
6566
pub mod modulus;
67+
mod oversized_uninit;
6668
mod private_exponent;
6769

6870
pub trait PublicModulus {}

src/arithmetic/bigint/modulus/mont.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{
1919
super::{
2020
super::montgomery::{limbs_square_mont, Unencoded, RR, RRR},
2121
modulus::value::Value,
22-
unwrap_impossible_limb_slice_error, Elem, One, PublicModulus, Uninit, N0,
22+
unwrap_impossible_limb_slice_error, Elem, One, OversizedUninit, PublicModulus, Uninit, N0,
2323
},
2424
ValidatedInput,
2525
};
@@ -106,6 +106,15 @@ impl ValidatedInput<'_> {
106106
}
107107
}
108108

109+
pub(crate) fn build_into_mont<'o, M>(
110+
&self,
111+
uninit: &'o mut OversizedUninit<2>,
112+
cpu: cpu::Features,
113+
) -> IntoMont<'o, M, RR> {
114+
self.write_into_mont(&mut uninit.as_uninit().into_cursor(), cpu)
115+
.unwrap_or_else(|LenMismatchError { .. }| unreachable!())
116+
}
117+
109118
fn write_into_mont<'o, M>(
110119
&self,
111120
out: &mut Cursor<'o, Limb>,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
#[allow(unused_imports)]
16+
use crate::polyfill::prelude::*;
17+
18+
use super::{Limb, MAX_LIMBS};
19+
use crate::polyfill;
20+
use core::mem::MaybeUninit;
21+
22+
/// A buffer that has enough space to hold `N` values of the maximum size,
23+
/// hiding the representation of values from the user.
24+
pub struct OversizedUninit<const N: usize>([[MaybeUninit<Limb>; MAX_LIMBS]; N]);
25+
26+
impl<const N: usize> OversizedUninit<N> {
27+
pub fn new() -> Self {
28+
Self(unsafe { MaybeUninit::uninit().assume_init() })
29+
}
30+
31+
pub(super) fn as_uninit(&mut self) -> polyfill::slice::Uninit<'_, Limb> {
32+
self.0.as_flattened_mut().into()
33+
}
34+
}

src/rsa/base/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,4 @@ mod public_exponent;
1616
pub(super) mod public_key;
1717
mod public_modulus;
1818

19-
pub(super) use self::{
20-
public_exponent::PublicExponent, public_key::PublicKey, public_modulus::PublicModulus,
21-
};
19+
pub(super) use self::{public_exponent::PublicExponent, public_modulus::PublicModulus};

src/rsa/base/public_key.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ use super::{
1616
super::{PublicKeyComponents, N, PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN},
1717
public_modulus, PublicExponent, PublicModulus,
1818
};
19-
use crate::{arithmetic::bigint, bits, cpu, error, limb::LIMB_BYTES};
19+
use crate::{
20+
arithmetic::{bigint, montgomery::RR},
21+
bits, cpu, error,
22+
limb::LIMB_BYTES,
23+
};
2024
use core::num::NonZeroU64;
2125

2226
/// An RSA Public Key.
2327
#[derive(Clone)]
24-
pub(crate) struct PublicKey {
25-
n: PublicModulus,
28+
pub(crate) struct PublicKey<S> {
29+
n: PublicModulus<S>,
2630
e: PublicExponent,
2731
}
2832

@@ -70,18 +74,41 @@ impl<'a> ValidatedInput<'a> {
7074
self.e_input
7175
}
7276

73-
pub(in super::super) fn build(&self, cpu_features: cpu::Features) -> PublicKey {
77+
pub(in super::super) fn build_boxed(
78+
&self,
79+
cpu: cpu::Features,
80+
) -> PublicKey<bigint::BoxedIntoMont<N, RR>> {
81+
PublicKey {
82+
n: self.n.build_boxed_into_mont(cpu),
83+
e: self.e,
84+
}
85+
}
86+
87+
pub(in super::super) fn build<'o>(
88+
&self,
89+
out: &'o mut bigint::OversizedUninit<2>,
90+
cpu: cpu::Features,
91+
) -> PublicKey<bigint::IntoMont<'o, N, RR>> {
92+
PublicKey {
93+
n: self.n.build(out, cpu),
94+
e: self.e,
95+
}
96+
}
97+
}
98+
99+
impl PublicKey<bigint::BoxedIntoMont<N, RR>> {
100+
pub fn reborrow(&self) -> PublicKey<bigint::IntoMont<'_, N, RR>> {
74101
PublicKey {
75-
n: self.n.build(cpu_features),
102+
n: self.n.reborrow(),
76103
e: self.e,
77104
}
78105
}
79106
}
80107

81-
impl PublicKey {
108+
impl PublicKey<bigint::IntoMont<'_, N, RR>> {
82109
/// The public modulus.
83110
#[inline]
84-
pub(in super::super) fn n(&self) -> &PublicModulus {
111+
pub(in super::super) fn n(&self) -> &PublicModulus<bigint::IntoMont<'_, N, RR>> {
85112
&self.n
86113
}
87114

src/rsa/base/public_modulus.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use core::ops::RangeInclusive;
99

1010
/// The modulus (n) of an RSA public key.
1111
#[derive(Clone)]
12-
pub struct PublicModulus {
13-
value: bigint::BoxedIntoMont<N, RR>,
12+
pub struct PublicModulus<S> {
13+
value: S,
1414
}
1515

1616
/*
@@ -68,27 +68,48 @@ impl<'a> ValidatedInput<'a> {
6868
self.input.len_bits()
6969
}
7070

71-
pub(super) fn build(&self, cpu_features: cpu::Features) -> PublicModulus {
71+
pub(super) fn build_boxed_into_mont(
72+
&self,
73+
cpu_features: cpu::Features,
74+
) -> PublicModulus<bigint::BoxedIntoMont<N, RR>> {
7275
PublicModulus {
7376
value: self.input.build_boxed_into_mont(cpu_features),
7477
}
7578
}
79+
80+
pub(super) fn build<'o>(
81+
&self,
82+
out: &'o mut bigint::OversizedUninit<2>,
83+
cpu_features: cpu::Features,
84+
) -> PublicModulus<bigint::IntoMont<'o, N, RR>> {
85+
PublicModulus {
86+
value: self.input.build_into_mont(out, cpu_features),
87+
}
88+
}
89+
}
90+
91+
impl PublicModulus<bigint::BoxedIntoMont<N, RR>> {
92+
pub fn reborrow(&self) -> PublicModulus<bigint::IntoMont<'_, N, RR>> {
93+
PublicModulus {
94+
value: self.value.reborrow(),
95+
}
96+
}
7697
}
7798

78-
impl PublicModulus {
99+
impl PublicModulus<bigint::IntoMont<'_, N, RR>> {
79100
/// The big-endian encoding of the modulus.
80101
///
81102
/// There are no leading zeros.
82103
pub fn be_bytes(&self) -> impl ExactSizeIterator<Item = u8> + Clone + '_ {
83-
self.value.reborrow().be_bytes()
104+
self.value.be_bytes()
84105
}
85106

86107
/// The length of the modulus in bits.
87108
pub fn len_bits(&self) -> bits::BitLength {
88-
self.value.reborrow().len_bits()
109+
self.value.len_bits()
89110
}
90111

91-
pub(in super::super) fn value(&self) -> bigint::IntoMont<'_, N, RR> {
92-
self.value.reborrow()
112+
pub(in super::super) fn value(&self) -> &bigint::IntoMont<'_, N, RR> {
113+
&self.value
93114
}
94115
}

src/rsa/keypair.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ impl KeyPair {
330330
// checking p * q == 0 (mod n) is equivalent to checking p * q == n.
331331
let public_key = PublicKey::new(public_key, cpu_features)?;
332332

333-
let n = &public_key.inner().n().value();
333+
let borrowed_public_key = public_key.inner();
334+
let n = borrowed_public_key.n().value();
334335
let nm = &n.modulus(cpu_features);
335336

336337
let q = q.build(cpu_features);
@@ -607,7 +608,8 @@ impl KeyPair {
607608
// RFC 8017 Section 5.1.2: RSADP, using the Chinese Remainder Theorem
608609
// with Garner's algorithm.
609610

610-
let n = &self.public.inner().n().value();
611+
let borrowed_public = self.public.inner();
612+
let n = borrowed_public.n().value();
611613
let nm = &n.modulus(cpu_features);
612614

613615
// Step 1. The value zero is also rejected.

src/rsa/public_key.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
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-
use super::base;
15+
use super::{base, N};
1616
use crate::{
17+
arithmetic::{bigint, montgomery::RR},
1718
cpu, error,
1819
io::{self, der, der_writer},
1920
};
@@ -24,7 +25,7 @@ pub(super) use base::public_key::ValidatedInput;
2425
/// An RSA Public Key.
2526
#[derive(Clone)]
2627
pub struct PublicKey {
27-
inner: base::PublicKey,
28+
inner: base::public_key::PublicKey<bigint::BoxedIntoMont<N, RR>>,
2829
serialized: Box<[u8]>,
2930
}
3031

@@ -35,7 +36,7 @@ impl PublicKey {
3536
input: ValidatedInput<'_>,
3637
cpu_features: cpu::Features,
3738
) -> Result<Self, error::KeyRejected> {
38-
let inner = input.build(cpu_features);
39+
let inner = input.build_boxed(cpu_features);
3940

4041
let n_bytes = input.n().input();
4142
let e_bytes = input.e_input();
@@ -62,11 +63,15 @@ impl PublicKey {
6263
/// The modulus length is rounded up to a whole number of bytes if its
6364
/// bit length isn't a multiple of 8.
6465
pub fn modulus_len(&self) -> usize {
65-
self.inner.n().len_bits().as_usize_bytes_rounded_up()
66+
self.inner
67+
.reborrow()
68+
.n()
69+
.len_bits()
70+
.as_usize_bytes_rounded_up()
6671
}
6772

68-
pub(super) fn inner(&self) -> &base::PublicKey {
69-
&self.inner
73+
pub(super) fn inner(&self) -> base::public_key::PublicKey<bigint::IntoMont<'_, N, RR>> {
74+
self.inner.reborrow()
7075
}
7176
}
7277

src/rsa/verification.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::{
1919
parse_public_key, PublicKeyComponents, RsaParameters, PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN,
2020
};
2121
use crate::{
22+
arithmetic::bigint,
2223
bits::{self, FromByteLen as _},
2324
cpu, digest,
2425
error::{self, InputTooLongError},
@@ -207,7 +208,8 @@ fn verify(
207208
// exponent value is 2**16 + 1, but it isn't clear if this is just for
208209
// signing or also for verification. We support exponents of 3 and larger
209210
// for compatibility with other commonly-used crypto libraries.
210-
let key = validated.build(cpu_features);
211+
let mut out = bigint::OversizedUninit::<2>::new();
212+
let key = validated.build(&mut out, cpu_features);
211213

212214
// RFC 8017 Section 5.2.2: RSAVP1.
213215
let mut decoded = [0u8; PUBLIC_KEY_PUBLIC_MODULUS_MAX_LEN];

0 commit comments

Comments
 (0)