Coverage Report

Created: 2026-11-12 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-bigint-0.7.10/src/modular/sqrt.rs
Line
Count
Source
1
use crate::{
2
    Choice, CtOption, Uint,
3
    modular::{FixedMontyForm, FixedMontyParams, prime_params::PrimeParams},
4
};
5
6
/// Compute a modular square root (if it exists) given [`MontyParams`]
7
/// and [`PrimeParams`] corresponding to `monty_value`, in Montgomery form.
8
#[must_use]
9
0
pub const fn sqrt_montgomery_form<const LIMBS: usize>(
10
0
    monty_value: &Uint<LIMBS>,
11
0
    monty_params: &FixedMontyParams<LIMBS>,
12
0
    prime_params: &PrimeParams<LIMBS>,
13
0
) -> CtOption<Uint<LIMBS>> {
14
0
    let value = FixedMontyForm::from_montgomery(*monty_value, monty_params);
15
0
    let b = value.pow_vartime(&prime_params.sqrt_exp);
16
17
    // Constant-time versions of modular square root algorithms based on:
18
    // Koo, N., Cho, G.H. and Kwon, S. (2013), "Square root algorithm in 𝔽q for q ≡ 2s + 1 (mod 2s+1)".
19
    // Electron. Lett., 49: 467-469. https://doi.org/10.1049/el.2012.4239
20
21
0
    let x = match prime_params.s.get() {
22
        1 => {
23
            // Shanks algorithm: sqrt = x^((p+1)/4) = x^(t+1)
24
0
            b
25
        }
26
        2 => {
27
            // Algorithm 3: p = 5 mod 8 (Atkins variant)
28
0
            let ru = FixedMontyForm::from_montgomery(prime_params.monty_root_unity, monty_params);
29
0
            let cb = value.mul(&b);
30
0
            let zeta = cb.mul(&b);
31
0
            let is_one = Uint::eq(zeta.as_montgomery(), monty_params.one());
32
0
            monty_select(&cb.mul(&ru), &cb, is_one)
33
        }
34
        3 => {
35
            // Algorithm 4: p = 9 mod 16
36
0
            let ru = FixedMontyForm::from_montgomery(prime_params.monty_root_unity, monty_params);
37
0
            let ru_2 =
38
0
                FixedMontyForm::from_montgomery(prime_params.monty_root_unity_p2, monty_params);
39
0
            let ru_3 = ru.mul(&ru_2);
40
0
            let cb = value.mul(&b);
41
0
            let zeta = cb.mul(&b);
42
43
0
            let mut m = monty_select(
44
0
                &ru,
45
0
                &FixedMontyForm::one(ru.params()),
46
0
                Uint::eq(zeta.as_montgomery(), monty_params.one()),
47
            );
48
            // m = ru^2 if zeta = -1
49
0
            m = monty_select(
50
0
                &m,
51
0
                &ru_2,
52
0
                Uint::eq(zeta.neg().as_montgomery(), monty_params.one()),
53
0
            );
54
            // m = ru^3 if zeta = ru^2
55
0
            m = monty_select(&m, &ru_3, monty_eq(&zeta, &ru_2));
56
57
0
            cb.mul(&m)
58
        }
59
        4 => {
60
            // Algorithm 5: p = 17 mod 32
61
0
            let ru = FixedMontyForm::from_montgomery(prime_params.monty_root_unity, monty_params);
62
0
            let ru_2 =
63
0
                FixedMontyForm::from_montgomery(prime_params.monty_root_unity_p2, monty_params);
64
0
            let ru_4 = ru_2.square();
65
0
            let ru_6 = ru_2.mul(&ru_4);
66
0
            let cb = value.mul(&b);
67
0
            let zeta = cb.mul(&b);
68
69
0
            let neg_zeta = zeta.neg();
70
0
            let zeta_b = monty_eq(&zeta, &ru_2);
71
0
            let neg_zeta_b = monty_eq(&neg_zeta, &ru_2);
72
0
            let zeta_d = monty_eq(&zeta, &ru_6);
73
74
            // m = B if -zeta in {B, C}, else 1
75
0
            let mut m = monty_select(
76
0
                &FixedMontyForm::one(ru.params()),
77
0
                &ru_2,
78
0
                neg_zeta_b.or(monty_eq(&neg_zeta, &ru_4)),
79
            );
80
            // m = C if zeta in {-1, D}
81
0
            m = monty_select(
82
0
                &m,
83
0
                &ru_4,
84
0
                Uint::eq(neg_zeta.as_montgomery(), monty_params.one()).or(zeta_d),
85
0
            );
86
            // m = D if zeta in {B, C}
87
0
            m = monty_select(&m, &ru_6, zeta_b.or(monty_eq(&zeta, &ru_4)));
88
            // m = m•ru if zeta or -zeta in {B, D}
89
0
            m = monty_select(
90
0
                &m,
91
0
                &m.mul(&ru),
92
0
                zeta_b
93
0
                    .or(zeta_d)
94
0
                    .or(neg_zeta_b)
95
0
                    .or(monty_eq(&neg_zeta, &ru_6)),
96
0
            );
97
98
0
            cb.mul(&m)
99
        }
100
        _ => {
101
            // Tonelli-Shanks
102
0
            let mut x = value.mul(&b);
103
0
            let mut d = x.mul(&b);
104
0
            let mut z =
105
0
                FixedMontyForm::from_montgomery(prime_params.monty_root_unity, monty_params);
106
0
            let mut v = prime_params.s.get();
107
0
            let mut max_v = v;
108
109
0
            while max_v >= 1 {
110
0
                let mut k = 1;
111
0
                let mut tmp = d.square();
112
0
                let mut j_less_than_v = Choice::TRUE;
113
114
0
                let mut j = 2;
115
0
                while j < max_v {
116
0
                    let tmp_is_one = Uint::eq(tmp.as_montgomery(), monty_params.one());
117
0
                    let squared = monty_select(&tmp, &z, tmp_is_one).square();
118
0
                    tmp = monty_select(&squared, &tmp, tmp_is_one);
119
0
                    j_less_than_v = j_less_than_v.and(Choice::from_u32_eq(j, v).not());
120
0
                    z = monty_select(&z, &squared, tmp_is_one.and(j_less_than_v));
121
0
                    k = tmp_is_one.select_u32(j, k);
122
0
                    j += 1;
123
0
                }
124
125
0
                let b_is_one = Uint::eq(d.as_montgomery(), monty_params.one());
126
0
                x = monty_select(&x.mul(&z), &x, b_is_one);
127
0
                z = z.square();
128
0
                d = d.mul(&z);
129
0
                v = k;
130
0
                max_v -= 1;
131
            }
132
133
0
            x
134
        }
135
    };
136
137
0
    CtOption::new(x.to_montgomery(), monty_eq(&x.square(), &value))
138
0
}
Unexecuted instantiation: crypto_bigint::modular::sqrt::sqrt_montgomery_form::<4>
Unexecuted instantiation: crypto_bigint::modular::sqrt::sqrt_montgomery_form::<_>
139
140
0
const fn monty_eq<const LIMBS: usize>(
141
0
    a: &FixedMontyForm<LIMBS>,
142
0
    b: &FixedMontyForm<LIMBS>,
143
0
) -> Choice {
144
0
    Uint::eq(a.as_montgomery(), b.as_montgomery())
145
0
}
Unexecuted instantiation: crypto_bigint::modular::sqrt::monty_eq::<4>
Unexecuted instantiation: crypto_bigint::modular::sqrt::monty_eq::<_>
146
147
0
const fn monty_select<const LIMBS: usize>(
148
0
    a: &FixedMontyForm<LIMBS>,
149
0
    b: &FixedMontyForm<LIMBS>,
150
0
    c: Choice,
151
0
) -> FixedMontyForm<LIMBS> {
152
0
    FixedMontyForm::from_montgomery(
153
0
        Uint::select(a.as_montgomery(), b.as_montgomery(), c),
154
0
        a.params(),
155
    )
156
0
}
Unexecuted instantiation: crypto_bigint::modular::sqrt::monty_select::<4>
Unexecuted instantiation: crypto_bigint::modular::sqrt::monty_select::<_>
157
158
#[cfg(test)]
159
mod tests {
160
    use super::sqrt_montgomery_form;
161
    use crate::{
162
        Odd, U256, U576, Uint,
163
        modular::{FixedMontyForm, FixedMontyParams, PrimeParams},
164
    };
165
166
    fn root_of_unity<const LIMBS: usize>(
167
        monty_params: &FixedMontyParams<LIMBS>,
168
        prime_params: &PrimeParams<LIMBS>,
169
    ) -> Uint<LIMBS> {
170
        FixedMontyForm::from_montgomery(prime_params.monty_root_unity, monty_params).retrieve()
171
    }
172
173
    fn test_monty_sqrt<const LIMBS: usize>(
174
        monty_params: FixedMontyParams<LIMBS>,
175
        prime_params: PrimeParams<LIMBS>,
176
    ) {
177
        let modulus = monty_params.modulus.get();
178
        let rounds = if cfg!(miri) { 1..=2 } else { 0..=256 };
179
        for i in rounds {
180
            let s = i * i;
181
            let s_monty = FixedMontyForm::new(&Uint::from_u32(s), &monty_params);
182
            let rt_monty =
183
                sqrt_montgomery_form(s_monty.as_montgomery(), &monty_params, &prime_params)
184
                    .expect("no sqrt found");
185
            let rt = FixedMontyForm::from_montgomery(rt_monty, &monty_params).retrieve();
186
            let i = Uint::from_u32(i);
187
            assert!(
188
                Uint::eq(&rt, &i)
189
                    .or(Uint::eq(&rt, &modulus.wrapping_sub(&i)))
190
                    .to_bool()
191
            );
192
        }
193
194
        // generator must be non-residue
195
        let generator = Uint::from_u32(prime_params.generator.get());
196
        let gen_monty = FixedMontyForm::new(&generator, &monty_params);
197
        assert!(
198
            sqrt_montgomery_form(gen_monty.as_montgomery(), &monty_params, &prime_params)
199
                .is_none()
200
                .to_bool()
201
        );
202
    }
203
204
    #[test]
205
    fn mod_sqrt_s_1() {
206
        // p = 3 mod 4, s = 1
207
        // P-256 field modulus
208
        let monty_params = FixedMontyParams::new_vartime(Odd::<U256>::from_be_hex(
209
            "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
210
        ));
211
        let prime_params = PrimeParams::new_vartime(&monty_params, 6);
212
        assert_eq!(prime_params.s.get(), 1);
213
        assert_eq!(prime_params.generator.get(), 6);
214
        assert_eq!(
215
            root_of_unity(&monty_params, &prime_params),
216
            U256::from_be_hex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFE")
217
        );
218
219
        test_monty_sqrt(monty_params, prime_params);
220
    }
221
222
    #[test]
223
    fn mod_sqrt_s_2() {
224
        // p = 5 mod 8, s = 2
225
        // ed25519 base field
226
        let monty_params = FixedMontyParams::new_vartime(Odd::<U256>::from_be_hex(
227
            "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
228
        ));
229
        let prime_params = PrimeParams::new_vartime(&monty_params, 2);
230
        assert_eq!(prime_params.s.get(), 2);
231
        assert_eq!(prime_params.generator.get(), 2);
232
        assert_eq!(
233
            root_of_unity(&monty_params, &prime_params),
234
            U256::from_be_hex("2B8324804FC1DF0B2B4D00993DFBD7A72F431806AD2FE478C4EE1B274A0EA0B0")
235
        );
236
237
        test_monty_sqrt(monty_params, prime_params);
238
    }
239
240
    #[test]
241
    fn mod_sqrt_s_3() {
242
        // p = 9 mod 16, s = 3
243
        // brainpoolP384 scalar field
244
        let monty_params = FixedMontyParams::new_vartime(Odd::<U576>::from_be_hex(
245
            "00000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
246
        ));
247
        let prime_params = PrimeParams::new_vartime(&monty_params, 3);
248
        assert_eq!(prime_params.s.get(), 3);
249
        assert_eq!(prime_params.generator.get(), 3);
250
        assert_eq!(
251
            root_of_unity(&monty_params, &prime_params),
252
            U576::from_be_hex(
253
                "000000000000009a0a650d44b28c17f3d708ad2fa8c4fbc7e6000d7c12dafa92fcc5673a3055276d535f79ff391dcdbcd998b7836647d3a72472b3da861ac810a7f9c7b7b63e2205"
254
            )
255
        );
256
257
        test_monty_sqrt(monty_params, prime_params);
258
    }
259
260
    #[test]
261
    fn mod_sqrt_s_4() {
262
        // p = 17 mod 32, s = 4
263
        // P-256 scalar field
264
        let monty_params = FixedMontyParams::new_vartime(Odd::<U256>::from_be_hex(
265
            "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
266
        ));
267
        let prime_params = PrimeParams::new_vartime(&monty_params, 7);
268
        assert_eq!(prime_params.s.get(), 4);
269
        assert_eq!(prime_params.generator.get(), 7);
270
        assert_eq!(
271
            root_of_unity(&monty_params, &prime_params),
272
            U256::from_be_hex("ffc97f062a770992ba807ace842a3dfc1546cad004378daf0592d7fbb41e6602")
273
        );
274
275
        test_monty_sqrt(monty_params, prime_params);
276
    }
277
278
    #[test]
279
    fn mod_sqrt_s_6() {
280
        // s = 6
281
        // K-256 scalar field
282
        let monty_params = FixedMontyParams::new_vartime(Odd::<U256>::from_be_hex(
283
            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
284
        ));
285
        let prime_params = PrimeParams::new_vartime(&monty_params, 7);
286
        assert_eq!(prime_params.s.get(), 6);
287
        assert_eq!(prime_params.generator.get(), 7);
288
        assert_eq!(
289
            root_of_unity(&monty_params, &prime_params),
290
            U256::from_be_hex("0C1DC060E7A91986DF9879A3FBC483A898BDEAB680756045992F4B5402B052F2")
291
        );
292
293
        test_monty_sqrt(monty_params, prime_params);
294
    }
295
}