multiversx_sdk/crypto/edwards25519/
extended_group_element.rs

1use super::{
2    completed_group_element::CompletedGroupElement, field_element::FieldElement,
3    pre_computed_group_element::PreComputedGroupElement,
4    projective_group_element::ProjectiveGroupElement,
5};
6
7#[derive(Default, Copy, Clone, Debug)]
8pub struct ExtendedGroupElement {
9    pub x: FieldElement,
10    pub y: FieldElement,
11    pub z: FieldElement,
12    pub t: FieldElement,
13}
14
15impl ExtendedGroupElement {
16    pub fn to_projective(self, r: &mut ProjectiveGroupElement) {
17        r.x.fe_copy(&self.x);
18        r.y.fe_copy(&self.y);
19        r.z.fe_copy(&self.z);
20    }
21
22    pub fn double(&self, r: &mut CompletedGroupElement) {
23        let mut q = ProjectiveGroupElement::default();
24
25        self.to_projective(&mut q);
26        q.double(r);
27    }
28
29    pub fn zero(&mut self) {
30        self.x.fe_zero();
31        self.y.fe_one();
32        self.z.fe_one();
33        self.t.fe_zero();
34    }
35
36    // ge_scalar_mult_base computes h = a*B, where
37    //   a = a[0]+256*a[1]+...+256^31 a[31]
38    //   B is the Ed25519 base point (x,4/5) with x positive.
39    //
40    // Preconditions:
41    //   a[31] <= 127
42    #[allow(clippy::needless_range_loop)]
43    pub fn ge_scalar_mult_base(&mut self, a: [u8; 32]) {
44        let mut e = [0i8; 64];
45        for (i, v) in a.iter().enumerate() {
46            e[2 * i] = (v & 15) as i8;
47            e[2 * i + 1] = ((v >> 4) & 15) as i8;
48        }
49
50        // each e[i] is between 0 and 15 and e[63] is between 0 and 7.
51
52        let mut carry: i8 = 0;
53        for i in 0..63 {
54            e[i] += carry;
55            carry = (e[i] + 8) >> 4;
56            e[i] -= carry << 4;
57        }
58
59        e[63] += carry;
60        // each e[i] is between -8 and 8.
61
62        self.zero();
63        let mut t = PreComputedGroupElement::default();
64        let mut r = CompletedGroupElement::default();
65        for i in (1..64).step_by(2) {
66            t.select_point(i / 2, e[i as usize] as i32);
67            r.ge_mixed_add(self, &t);
68            r.to_extended(self);
69        }
70
71        let mut s = ProjectiveGroupElement::default();
72
73        self.double(&mut r);
74        r.to_projective(&mut s);
75        s.double(&mut r);
76        r.to_projective(&mut s);
77        s.double(&mut r);
78        r.to_projective(&mut s);
79        s.double(&mut r);
80        r.to_extended(self);
81
82        for i in (0..64).step_by(2) {
83            t.select_point(i / 2, e[i as usize] as i32);
84            r.ge_mixed_add(self, &t);
85            r.to_extended(self);
86        }
87    }
88
89    pub fn to_bytes(self) -> [u8; 32] {
90        let mut recip = FieldElement::default();
91        let mut x = FieldElement::default();
92        let mut y = FieldElement::default();
93
94        recip.fe_invert(&self.z);
95        x.fe_mul(&self.x, &recip);
96        y.fe_mul(&self.y, &recip);
97        let mut s = y.to_bytes();
98
99        s[31] ^= x.fe_is_negative() << 7;
100
101        s
102    }
103}