wlan_sae/
boringssl.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Wrappers for BoringSSL primitive types needed for SAE. Mundane does not provide implementations
6// for these types. All calls into BoringSSL are unsafe.
7
8use anyhow::{format_err, Error};
9use bssl_sys::{
10    BN_CTX_free, BN_CTX_new, BN_add, BN_asc2bn, BN_bin2bn, BN_bn2bin, BN_bn2dec, BN_cmp, BN_copy,
11    BN_equal_consttime, BN_free, BN_is_odd, BN_is_one, BN_is_zero, BN_mod_add, BN_mod_exp,
12    BN_mod_inverse, BN_mod_mul, BN_mod_sqr, BN_mod_sqrt, BN_new, BN_nnmod, BN_num_bits,
13    BN_num_bytes, BN_one, BN_rand_range, BN_rshift1, BN_set_negative, BN_set_u64, BN_sub, BN_zero,
14    EC_GROUP_free, EC_GROUP_get_curve_GFp, EC_GROUP_get_order, EC_GROUP_new_by_curve_name,
15    EC_POINT_add, EC_POINT_free, EC_POINT_get_affine_coordinates_GFp, EC_POINT_invert,
16    EC_POINT_is_at_infinity, EC_POINT_mul, EC_POINT_new, EC_POINT_set_affine_coordinates_GFp,
17    ERR_get_error, ERR_reason_error_string, NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
18    OPENSSL_free, BIGNUM, BN_CTX, EC_GROUP, EC_POINT,
19};
20use num_derive::{FromPrimitive, ToPrimitive};
21use std::cmp::Ordering;
22use std::convert::TryInto;
23use std::ffi::CString;
24use std::fmt;
25use std::ptr::NonNull;
26
27fn ptr_or_error<T>(ptr: *mut T) -> Result<NonNull<T>, Error> {
28    match NonNull::new(ptr) {
29        Some(non_null) => Ok(non_null),
30        None => return Err(format_err!("Found null pointer from BoringSSL")),
31    }
32}
33
34fn one_or_error(res: std::os::raw::c_int) -> Result<(), Error> {
35    match res {
36        1 => Ok(()),
37        _ => unsafe {
38            let error_code = ERR_get_error();
39            let error_reason_ptr = ERR_reason_error_string(error_code);
40            if error_reason_ptr.is_null() {
41                return Err(format_err!("BoringSSL failed to perform an operation."));
42            }
43            let error_reason = std::ffi::CStr::from_ptr(error_reason_ptr).to_string_lossy();
44            return Err(format_err!("BoringSSL failed to perform an operation: {}", error_reason));
45            // ERR_reason_error_string returns a static ptr, so we don't need to free anything.
46        },
47    }
48}
49
50/// An arbitrary precision integral value.
51pub struct Bignum(NonNull<BIGNUM>);
52
53impl Drop for Bignum {
54    fn drop(&mut self) {
55        unsafe { BN_free(self.0.as_mut()) }
56    }
57}
58
59/// A context structure to speed up Bignum operations.
60pub struct BignumCtx(NonNull<BN_CTX>);
61
62impl Drop for BignumCtx {
63    fn drop(&mut self) {
64        unsafe { BN_CTX_free(self.0.as_mut()) }
65    }
66}
67
68impl BignumCtx {
69    pub fn new() -> Result<Self, Error> {
70        ptr_or_error(unsafe { BN_CTX_new() }).map(Self)
71    }
72}
73
74impl Bignum {
75    pub fn new() -> Result<Self, Error> {
76        ptr_or_error(unsafe { BN_new() }).map(Self)
77    }
78
79    /// Returns a new Bignum with value zero.
80    // TODO(https://fxbug.dev/335283785): Remove or document unused code
81    #[allow(dead_code)]
82    pub fn zero() -> Result<Self, Error> {
83        let result = Self::new()?;
84        unsafe {
85            BN_zero(result.0.as_ptr());
86        }
87        Ok(result)
88    }
89
90    /// Returns a new Bignum with value one.
91    pub fn one() -> Result<Self, Error> {
92        let result = Self::new()?;
93        one_or_error(unsafe { BN_one(result.0.as_ptr()) })?;
94        Ok(result)
95    }
96
97    /// Returns a new Bignum with a cryptographically random value in the range [0, max).
98    pub fn rand(max: &Bignum) -> Result<Self, Error> {
99        let result = Self::new()?;
100        one_or_error(unsafe { BN_rand_range(result.0.as_ptr(), max.0.as_ptr()) })?;
101        Ok(result)
102    }
103
104    /// Returns a new Bignum constructed from the given bytes. Bytes are given in order of
105    /// decreasing significance.
106    pub fn new_from_slice(bytes: &[u8]) -> Result<Self, Error> {
107        if bytes.is_empty() {
108            Self::new_from_u64(0)
109        } else {
110            // Don't panic while unsafe.
111            let bytes_len = bytes.len().try_into().unwrap();
112            ptr_or_error(unsafe {
113                BN_bin2bn(&bytes[0] as *const u8, bytes_len, std::ptr::null_mut())
114            })
115            .map(Self)
116        }
117    }
118
119    /// Returns a new Bignum parsed from the given ascii string. The string may be given as a
120    /// decimal value, or as a hex value preceded by '0x'.
121    // TODO(https://fxbug.dev/335283785): Remove or document unused code
122    #[allow(dead_code)]
123    pub fn new_from_string(ascii: &str) -> Result<Self, Error> {
124        let mut bignum = std::ptr::null_mut();
125        let ascii = CString::new(ascii)?;
126        one_or_error(unsafe { BN_asc2bn(&mut bignum as *mut *mut BIGNUM, ascii.as_ptr()) })?;
127        ptr_or_error(bignum).map(Bignum)
128    }
129
130    pub fn new_from_u64(value: u64) -> Result<Self, Error> {
131        let mut bignum = Self::new()?;
132        one_or_error(unsafe { BN_set_u64(bignum.0.as_mut(), value) })?;
133        Ok(bignum)
134    }
135
136    /// Sets the sign of Bignum to negative, iff it is nonzero.  Consumes and returns itself.
137    pub fn set_negative(self) -> Self {
138        unsafe { BN_set_negative(self.0.as_ptr(), 1) };
139        self
140    }
141
142    pub fn copy(&self) -> Result<Self, Error> {
143        let mut copy = Self::new()?;
144        ptr_or_error(unsafe { BN_copy(copy.0.as_mut(), self.0.as_ptr()) })?;
145        Ok(copy)
146    }
147
148    /// Returns the sum of this Bignum and b.
149    /// Recycles b for the output to avoid unnecessary heap allocations.
150    pub fn add(&self, mut b: Self) -> Result<Self, Error> {
151        one_or_error(unsafe { BN_add(b.0.as_mut(), self.0.as_ptr(), b.0.as_ptr()) })?;
152        Ok(b)
153    }
154
155    /// Returns this Bignum minus b.
156    /// Recycles b for the output to avoid unecessary heap allocations.
157    pub fn sub(&self, mut b: Self) -> Result<Self, Error> {
158        one_or_error(unsafe { BN_sub(b.0.as_mut(), self.0.as_ptr(), b.0.as_ptr()) })?;
159        Ok(b)
160    }
161
162    /// Returns the result of `self mod m`, where both m and the result are non-negative.
163    pub fn mod_nonnegative(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
164        let mut result = Self::new()?;
165        one_or_error(unsafe {
166            BN_nnmod(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
167        })?;
168        Ok(result)
169    }
170
171    /// Returns the result of `self + b mod m`.
172    pub fn mod_add(&self, b: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
173        let mut result = Self::new()?;
174        one_or_error(unsafe {
175            BN_mod_add(
176                result.0.as_mut(),
177                self.0.as_ptr(),
178                b.0.as_ptr(),
179                m.0.as_ptr(),
180                ctx.0.as_ptr(),
181            )
182        })?;
183        Ok(result)
184    }
185
186    /// Returns the result of `self * b mod m`.
187    pub fn mod_mul(&self, b: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
188        let mut result = Self::new()?;
189        one_or_error(unsafe {
190            BN_mod_mul(
191                result.0.as_mut(),
192                self.0.as_ptr(),
193                b.0.as_ptr(),
194                m.0.as_ptr(),
195                ctx.0.as_ptr(),
196            )
197        })?;
198        Ok(result)
199    }
200
201    /// Returns the result of `self^-1 mod m`.
202    pub fn mod_inverse(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
203        let mut result = Self::new()?;
204        ptr_or_error(unsafe {
205            BN_mod_inverse(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
206        })?;
207        Ok(result)
208    }
209
210    /// Returns the result of `self^p mod m`.
211    pub fn mod_exp(&self, p: &Self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
212        let mut result = Self::new()?;
213        one_or_error(unsafe {
214            BN_mod_exp(
215                result.0.as_mut(),
216                self.0.as_ptr(),
217                p.0.as_ptr(),
218                m.0.as_ptr(),
219                ctx.0.as_ptr(),
220            )
221        })?;
222        Ok(result)
223    }
224
225    /// Returns the result of `self^2 mod m`.
226    pub fn mod_square(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
227        let mut result = Self::new()?;
228        one_or_error(unsafe {
229            BN_mod_sqr(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
230        })?;
231        Ok(result)
232    }
233
234    /// Returns a value `ret` such that `self mod m = ret^2 mod m`. Returns an error if no such
235    /// value exists.
236    pub fn mod_sqrt(&self, m: &Self, ctx: &BignumCtx) -> Result<Self, Error> {
237        let mut result = Self::new()?;
238        ptr_or_error(unsafe {
239            BN_mod_sqrt(result.0.as_mut(), self.0.as_ptr(), m.0.as_ptr(), ctx.0.as_ptr())
240        })?;
241        Ok(result)
242    }
243
244    /// Returns `self >> 1`, aka `self / 2`.
245    pub fn rshift1(&self) -> Result<Self, Error> {
246        let mut result = Self::new()?;
247        one_or_error(unsafe { BN_rshift1(result.0.as_mut(), self.0.as_ptr()) })?;
248        Ok(result)
249    }
250
251    pub fn is_one(&self) -> bool {
252        unsafe { BN_is_one(self.0.as_ptr()) == 1 }
253    }
254
255    pub fn is_zero(&self) -> bool {
256        unsafe { BN_is_zero(self.0.as_ptr()) == 1 }
257    }
258
259    pub fn is_odd(&self) -> bool {
260        unsafe { BN_is_odd(self.0.as_ptr()) == 1 }
261    }
262
263    /// Returns the length in bytes of the underlying byte array.
264    pub fn len(&self) -> usize {
265        unsafe { BN_num_bytes(self.0.as_ptr()) as usize }
266    }
267
268    /// Returns the number of significant bits in this Bignum, excluding leading zeroes. This is
269    /// *not* equal to `len() * 8`.
270    pub fn bits(&self) -> usize {
271        unsafe { BN_num_bits(self.0.as_ptr()) as usize }
272    }
273
274    /// Converts this bignum into a big-endian vector of at least the given minimum bytes, padded
275    /// with leading zeroes if necessary.
276    pub fn to_be_vec(&self, min_length: usize) -> Vec<u8> {
277        let len = self.len();
278        let padded_len = std::cmp::max(len, min_length);
279        let mut out = vec![0; padded_len];
280        if len != 0 {
281            unsafe {
282                BN_bn2bin(self.0.as_ptr(), &mut out[padded_len - len] as *mut u8);
283            }
284        }
285        out
286    }
287}
288
289impl PartialEq for Bignum {
290    fn eq(&self, other: &Self) -> bool {
291        unsafe { BN_equal_consttime(self.0.as_ptr(), other.0.as_ptr()) == 1 }
292    }
293}
294impl Eq for Bignum {}
295
296impl std::cmp::Ord for Bignum {
297    fn cmp(&self, other: &Self) -> Ordering {
298        unsafe { BN_cmp(self.0.as_ptr(), other.0.as_ptr()) }.cmp(&0)
299    }
300}
301
302impl PartialOrd for Bignum {
303    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
304        Some(self.cmp(other))
305    }
306}
307
308impl fmt::Display for Bignum {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        unsafe {
311            let ptr = BN_bn2dec(self.0.as_ptr());
312            // BoringSSL requires that we use its special free function, so we can't use CString.
313            let res = std::ffi::CStr::from_ptr(ptr).to_string_lossy().fmt(f);
314            OPENSSL_free(ptr as *mut ::std::os::raw::c_void);
315            res
316        }
317    }
318}
319
320impl fmt::Debug for Bignum {
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        write!(f, "Bignum({})", self)
323    }
324}
325
326/// Supported elliptic curve groups.
327#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive)]
328pub enum EcGroupId {
329    P256 = 19,
330    P384 = 20,
331    P521 = 21,
332}
333
334impl EcGroupId {
335    fn nid(&self) -> i32 {
336        match self {
337            EcGroupId::P256 => NID_X9_62_prime256v1,
338            EcGroupId::P384 => NID_secp384r1,
339            EcGroupId::P521 => NID_secp521r1,
340        }
341    }
342}
343
344/// An elliptic curve group, i.e. an equation of the form:
345/// `y^2 = x^3 + ax + b  (mod p)`.
346pub struct EcGroup(NonNull<EC_GROUP>);
347
348pub struct EcGroupParams {
349    // Prime modulo
350    pub p: Bignum,
351
352    // Elliptic curve parameters: y^2 = x^3 + ax + b
353    pub a: Bignum,
354    pub b: Bignum,
355}
356
357impl Drop for EcGroup {
358    fn drop(&mut self) {
359        unsafe { EC_GROUP_free(self.0.as_mut()) }
360    }
361}
362
363impl EcGroup {
364    pub fn new(id: EcGroupId) -> Result<Self, Error> {
365        ptr_or_error(unsafe { EC_GROUP_new_by_curve_name(id.nid()) }).map(Self)
366    }
367
368    /// Returns the prime and curve parameters that constitute this group.
369    pub fn get_params(&self, ctx: &BignumCtx) -> Result<EcGroupParams, Error> {
370        let p = Bignum::new()?;
371        let a = Bignum::new()?;
372        let b = Bignum::new()?;
373
374        one_or_error(unsafe {
375            EC_GROUP_get_curve_GFp(
376                self.0.as_ptr(),
377                p.0.as_ptr(),
378                a.0.as_ptr(),
379                b.0.as_ptr(),
380                ctx.0.as_ptr(),
381            )
382        })?;
383        Ok(EcGroupParams { p, a, b })
384    }
385
386    /// Returns the order of this group, i.e. the number of integer points on the curve (mod p).
387    pub fn get_order(&self, ctx: &BignumCtx) -> Result<Bignum, Error> {
388        let order = Bignum::new()?;
389        one_or_error(unsafe {
390            EC_GROUP_get_order(self.0.as_ptr(), order.0.as_ptr(), ctx.0.as_ptr())
391        })?;
392        Ok(order)
393    }
394}
395
396/// A single point on an elliptic curve.
397pub struct EcPoint(NonNull<EC_POINT>);
398
399impl Drop for EcPoint {
400    fn drop(&mut self) {
401        unsafe { EC_POINT_free(self.0.as_mut()) }
402    }
403}
404
405impl EcPoint {
406    pub fn new(group: &EcGroup) -> Result<Self, Error> {
407        ptr_or_error(unsafe { EC_POINT_new(group.0.as_ptr()) }).map(Self)
408    }
409
410    /// Converts the given affine coordinates to a point on the given curve.
411    pub fn new_from_affine_coords(
412        x: Bignum,
413        y: Bignum,
414        group: &EcGroup,
415        ctx: &BignumCtx,
416    ) -> Result<Self, Error> {
417        let point = Self::new(group)?;
418        one_or_error(unsafe {
419            EC_POINT_set_affine_coordinates_GFp(
420                group.0.as_ptr(),
421                point.0.as_ptr(),
422                x.0.as_ptr(),
423                y.0.as_ptr(),
424                ctx.0.as_ptr(),
425            )
426        })?;
427        Ok(point)
428    }
429
430    /// Converts a point on a curve to its affine coordinates.
431    pub fn to_affine_coords(
432        &self,
433        group: &EcGroup,
434        ctx: &BignumCtx,
435    ) -> Result<(Bignum, Bignum), Error> {
436        let x = Bignum::new()?;
437        let y = Bignum::new()?;
438        one_or_error(unsafe {
439            EC_POINT_get_affine_coordinates_GFp(
440                group.0.as_ptr(),
441                self.0.as_ptr(),
442                x.0.as_ptr(),
443                y.0.as_ptr(),
444                ctx.0.as_ptr(),
445            )
446        })?;
447        Ok((x, y))
448    }
449
450    /// Multiplies the given point on the curve by m. This is equivalent to adding this point
451    /// to itself m times.
452    pub fn mul(&self, group: &EcGroup, m: &Bignum, ctx: &BignumCtx) -> Result<EcPoint, Error> {
453        let result = Self::new(group)?;
454        one_or_error(unsafe {
455            EC_POINT_mul(
456                group.0.as_ptr(),
457                result.0.as_ptr(),
458                std::ptr::null_mut(),
459                self.0.as_ptr(),
460                m.0.as_ptr(),
461                ctx.0.as_ptr(),
462            )
463        })?;
464        Ok(result)
465    }
466
467    /// Computes `self + b`. In visual terms, this means drawing a line between this point and b
468    /// and finding the third point where this line intersects the curve.
469    pub fn add(&self, group: &EcGroup, b: &EcPoint, ctx: &BignumCtx) -> Result<EcPoint, Error> {
470        let result = Self::new(group)?;
471        one_or_error(unsafe {
472            EC_POINT_add(
473                group.0.as_ptr(),
474                result.0.as_ptr(),
475                self.0.as_ptr(),
476                b.0.as_ptr(),
477                ctx.0.as_ptr(),
478            )
479        })?;
480        Ok(result)
481    }
482
483    /// Inverts this point on the given curve. Mathematically this means `(x, -y)`.
484    pub fn invert(self, group: &EcGroup, ctx: &BignumCtx) -> Result<EcPoint, Error> {
485        one_or_error(unsafe {
486            EC_POINT_invert(group.0.as_ptr(), self.0.as_ptr(), ctx.0.as_ptr())
487        })?;
488        Ok(self)
489    }
490
491    /// Returns true if this is the point at infinity (i.e. the zero element for point addition).
492    pub fn is_point_at_infinity(&self, group: &EcGroup) -> bool {
493        unsafe { EC_POINT_is_at_infinity(group.0.as_ptr(), self.0.as_ptr()) == 1 }
494    }
495}
496
497#[cfg(test)]
498mod tests {
499    use super::*;
500
501    fn bn(value: &str) -> Bignum {
502        Bignum::new_from_string(value).unwrap()
503    }
504
505    #[test]
506    fn bignum_lifetime() {
507        // Create and drop some bignums to test that we don't crash.
508        for _ in 0..10 {
509            let bignum = Bignum::new().unwrap();
510            std::mem::drop(bignum);
511        }
512    }
513
514    #[test]
515    fn bignum_new() {
516        assert_eq!(Bignum::new_from_string("100").unwrap(), bn("100"));
517        assert_eq!(Bignum::new_from_u64(100).unwrap(), bn("100"));
518        assert_eq!(Bignum::new_from_slice(&[0xff, 0xff][..]).unwrap(), bn("65535"));
519    }
520
521    #[test]
522    fn bignum_set_negative() {
523        assert_eq!(bn("100").set_negative(), bn("-100"));
524        assert_eq!(bn("-100").set_negative(), bn("-100"));
525        assert_eq!(bn("0").set_negative(), bn("0"));
526    }
527
528    #[test]
529    fn bignum_format() {
530        // Bignum::from_string should support both decimal and hex formats.
531        assert_eq!(format!("{}", bn("100")), "100");
532        assert_eq!(format!("{}", bn("0x100")), "256");
533    }
534
535    #[test]
536    fn bignum_add() {
537        let bn1 = bn("1000000000000000000000");
538        let bn2 = bn("1000000000001234567890");
539        let sum = bn1.add(bn2).unwrap();
540        assert_eq!(sum, bn("2000000000001234567890"));
541
542        let bn1 = bn("-1000000000000000000000");
543        let bn2 = bn("1000000000001234567890");
544        let sum = bn1.add(bn2).unwrap();
545        assert_eq!(sum, bn("1234567890"));
546    }
547
548    #[test]
549    fn bignum_sub() {
550        let bn1 = bn("3000000000000987654321");
551        let bn2 = bn("2000000000000000000000");
552        let diff = bn1.sub(bn2).unwrap();
553        assert_eq!(diff, bn("1000000000000987654321"));
554
555        let bn1 = bn("2000000000000012345678");
556        let bn2 = bn("-3000000000000987654321");
557        let diff = bn1.sub(bn2).unwrap();
558        assert_eq!(diff, bn("5000000000000999999999"));
559    }
560
561    #[test]
562    fn bignum_mod_nonnegative() {
563        let ctx = BignumCtx::new().unwrap();
564        let bn1 = bn("12");
565        let bn2 = bn("5");
566        let mod_nonnegative = bn1.mod_nonnegative(&bn2, &ctx).unwrap();
567        assert_eq!(mod_nonnegative, bn("2"));
568
569        let bn1 = bn("-12");
570        let bn2 = bn("5");
571        let mod_nonnegative = bn1.mod_nonnegative(&bn2, &ctx).unwrap();
572        assert_eq!(mod_nonnegative, bn("3"));
573    }
574
575    #[test]
576    fn bignum_mod_add() {
577        let ctx = BignumCtx::new().unwrap();
578        let bn1 = bn("1000000000000000000000");
579        let bn2 = bn("1000000000001234567890");
580        let m = bn("2000000000000000000000");
581        let value = bn1.mod_add(&bn2, &m, &ctx).unwrap();
582        assert_eq!(value, bn("1234567890"));
583    }
584
585    #[test]
586    fn bignum_mod_mul() {
587        let ctx = BignumCtx::new().unwrap();
588        let value = bn("4").mod_mul(&bn("5"), &bn("12"), &ctx).unwrap();
589        assert_eq!(value, bn("8"));
590    }
591
592    #[test]
593    fn bignum_mod_inverse() {
594        let ctx = BignumCtx::new().unwrap();
595        assert_eq!(bn("3").mod_inverse(&bn("7"), &ctx).unwrap(), bn("5"));
596    }
597
598    #[test]
599    fn bignum_mod_exp() {
600        let ctx = BignumCtx::new().unwrap();
601        let value = bn("4").mod_exp(&bn("2"), &bn("10"), &ctx).unwrap();
602        assert_eq!(value, bn("6"));
603    }
604
605    #[test]
606    fn bigum_mod_square() {
607        let ctx = BignumCtx::new().unwrap();
608        assert_eq!(bn("11").mod_square(&bn("17"), &ctx).unwrap(), bn("2"));
609    }
610
611    #[test]
612    fn bignum_mod_sqrt() {
613        let ctx = BignumCtx::new().unwrap();
614        let m = bn("13"); // base must be prime.
615                          // https://en.wikipedia.org/wiki/Quadratic_residue#Table_of_quadratic_residues
616        let quadratic_residues = [1, 3, 4, 9, 10, 12];
617        for i in 1..12 {
618            let i_bn = Bignum::new_from_u64(i).unwrap();
619            let sqrt = i_bn.mod_sqrt(&m, &ctx);
620            if quadratic_residues.contains(&i) {
621                assert!(sqrt.is_ok());
622                assert_eq!(sqrt.unwrap().mod_exp(&bn("2"), &m, &ctx).unwrap(), i_bn);
623            } else {
624                assert!(sqrt.is_err());
625            }
626        }
627    }
628
629    #[test]
630    fn bignum_mod_sqrt_non_prime() {
631        let ctx = BignumCtx::new().unwrap();
632        let m = bn("100");
633        // 16 is a sqrt, but 100 is not prime so BoringSSL complains.
634        assert!(bn("16").mod_sqrt(&m, &ctx).is_err())
635    }
636
637    #[test]
638    fn bignum_rshift1() {
639        assert_eq!(bn("100").rshift1().unwrap(), bn("50"));
640        assert_eq!(bn("101").rshift1().unwrap(), bn("50"));
641    }
642
643    #[test]
644    fn bignum_simple_fns() {
645        assert!(bn("1").is_one());
646        assert!(!bn("100000").is_one());
647        assert!(Bignum::one().unwrap().is_one());
648
649        assert!(bn("0").is_zero());
650        assert!(!bn("1").is_zero());
651        assert!(Bignum::zero().unwrap().is_zero());
652
653        assert!(bn("1000001").is_odd());
654        assert!(!bn("1000002").is_odd());
655    }
656
657    #[test]
658    fn bignum_ord() {
659        let neg = bn("-100");
660        let zero = bn("0");
661        let pos = bn("100");
662
663        assert!(neg < zero);
664        assert!(pos > neg);
665        assert_eq!(neg, neg);
666        assert_eq!(zero, zero);
667        assert_eq!(pos, pos);
668    }
669
670    #[test]
671    fn bignum_to_be_vec() {
672        assert_eq!(bn("0xff00").to_be_vec(1), vec![0xff, 0x00]);
673        assert_eq!(bn("0xff00").to_be_vec(4), vec![0x00, 0x00, 0xff, 0x00]);
674        assert_eq!(bn("0").to_be_vec(4), vec![0x00, 0x00, 0x00, 0x00]);
675    }
676
677    // RFC 5903 section 3.1
678    const P: &'static str = "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF";
679    const B: &'static str = "0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B";
680    const ORDER: &'static str =
681        "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551";
682    const GX: &'static str = "0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
683    const GY: &'static str = "0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5";
684
685    // RFC 5903 section 8.1
686    const I: &'static str = "0xC88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433";
687    const GIX: &'static str = "0xDAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C3772581180";
688    const GIY: &'static str = "0x5271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3";
689
690    #[test]
691    fn ec_group_params() {
692        let group = EcGroup::new(EcGroupId::P256).unwrap();
693        let ctx = BignumCtx::new().unwrap();
694        let params = group.get_params(&ctx).unwrap();
695        let order = group.get_order(&ctx).unwrap();
696
697        // RFC 5903 section 3.1
698        assert_eq!(params.p, bn(P));
699        // 'a' is specified as -3, but boringssl returns the positive value congruent to -3 mod p.
700        assert_eq!(params.a, params.p.sub(bn("3")).unwrap());
701        assert_eq!(params.b, bn(B));
702        assert_eq!(order, bn(ORDER));
703    }
704
705    #[test]
706    fn ec_point_to_coords() {
707        let group = EcGroup::new(EcGroupId::P256).unwrap();
708        let ctx = BignumCtx::new().unwrap();
709        let point = EcPoint::new_from_affine_coords(bn(GIX), bn(GIY), &group, &ctx).unwrap();
710        let (x, y) = point.to_affine_coords(&group, &ctx).unwrap();
711        assert_eq!(x, bn(GIX));
712        assert_eq!(y, bn(GIY));
713    }
714
715    #[test]
716    fn ec_wrong_coords_to_point_err() {
717        let group = EcGroup::new(EcGroupId::P256).unwrap();
718        let ctx = BignumCtx::new().unwrap();
719        let result =
720            EcPoint::new_from_affine_coords(bn(GIX).add(bn("1")).unwrap(), bn(GIY), &group, &ctx);
721        let Err(err) = result else { panic!("Expected error") };
722        assert!(format!("{:?}", err).contains("POINT_IS_NOT_ON_CURVE"));
723    }
724
725    #[test]
726    fn ec_point_mul() {
727        let group = EcGroup::new(EcGroupId::P256).unwrap();
728        let ctx = BignumCtx::new().unwrap();
729        let g = EcPoint::new_from_affine_coords(bn(GX), bn(GY), &group, &ctx).unwrap();
730        let gi = g.mul(&group, &bn(I), &ctx).unwrap();
731        let (gix, giy) = gi.to_affine_coords(&group, &ctx).unwrap();
732        assert_eq!(gix, bn(GIX));
733        assert_eq!(giy, bn(GIY));
734    }
735}