]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/ed25519.rs
spake2: refactor internals (#91)
[PAKEs.git] / spake2 / src / ed25519.rs
1 //! "Edwards25519" elliptic curve group.
2
3 use crate::{c2_Element, c2_Scalar, Group};
4 use alloc::vec::Vec;
5 use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY};
6 use hkdf::Hkdf;
7 use rand_core::{CryptoRng, RngCore};
8 use sha2::{Digest, Sha256};
9
10 /// Ed25519 elliptic curve group.
11 #[derive(Debug, PartialEq, Eq)]
12 pub struct Ed25519Group;
13
14 impl Group for Ed25519Group {
15     type Scalar = c2_Scalar;
16     type Element = c2_Element;
17     type TranscriptHash = Sha256;
18
19     fn name() -> &'static str {
20         "Ed25519"
21     }
22
23     fn const_m() -> c2_Element {
24         // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.M.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
25         // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
26         CompressedEdwardsY([
27             0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
28             0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
29             0x81, 0xa0, 0x23, 0x12,
30         ])
31         .decompress()
32         .unwrap()
33     }
34
35     fn const_n() -> c2_Element {
36         // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.N.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
37         // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
38         CompressedEdwardsY([
39             0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
40             0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
41             0x00, 0xe5, 0x83, 0xc3,
42         ])
43         .decompress()
44         .unwrap()
45     }
46
47     fn const_s() -> c2_Element {
48         // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.S.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
49         // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
50         CompressedEdwardsY([
51             0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
52             0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
53             0x6a, 0xb8, 0xe6, 0xf1,
54         ])
55         .decompress()
56         .unwrap()
57     }
58
59     fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
60         ed25519_hash_to_scalar(s)
61     }
62
63     fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
64     where
65         T: RngCore + CryptoRng,
66     {
67         c2_Scalar::random(cspring)
68     }
69
70     fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
71         -s
72     }
73
74     fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
75         s.compress().as_bytes().to_vec()
76     }
77
78     fn element_length() -> usize {
79         32
80     }
81
82     fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
83         if b.len() != 32 {
84             return None;
85         }
86
87         let mut bytes = [0u8; 32];
88         bytes.copy_from_slice(b);
89
90         let cey = CompressedEdwardsY(bytes);
91         cey.decompress()
92     }
93
94     fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
95         ED25519_BASEPOINT_POINT * s
96     }
97     fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
98         e * s
99     }
100
101     fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
102         a + b
103     }
104 }
105
106 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
107     //c2_Scalar::hash_from_bytes::<Sha512>(&s)
108     // spake2.py does:
109     //  h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
110     //  i = int(h, 16)
111     //  i % q
112
113     let mut okm = [0u8; 32 + 16];
114     Hkdf::<Sha256>::new(Some(b""), s)
115         .expand(b"SPAKE2 pw", &mut okm)
116         .unwrap();
117     //println!("expanded:   {}{}", "................................", okm.iter().to_hex()); // ok
118
119     let mut reducible = [0u8; 64]; // little-endian
120     for (i, x) in okm.iter().enumerate().take(32 + 16) {
121         reducible[32 + 16 - 1 - i] = *x;
122     }
123     //println!("reducible:  {}", reducible.iter().to_hex());
124     c2_Scalar::from_bytes_mod_order_wide(&reducible)
125     //let reduced = c2_Scalar::reduce(&reducible);
126     //println!("reduced:    {}", reduced.as_bytes().to_hex());
127     //println!("done");
128     //reduced
129 }
130
131 /// Hash `idA` and `idB` identities.
132 pub(crate) fn hash_ab(
133     password_vec: &[u8],
134     id_a: &[u8],
135     id_b: &[u8],
136     first_msg: &[u8],
137     second_msg: &[u8],
138     key_bytes: &[u8],
139 ) -> Vec<u8> {
140     assert_eq!(first_msg.len(), 32);
141     assert_eq!(second_msg.len(), 32);
142     // the transcript is fixed-length, made up of 6 32-byte values:
143     // byte 0-31   : sha256(pw)
144     // byte 32-63  : sha256(idA)
145     // byte 64-95  : sha256(idB)
146     // byte 96-127 : X_msg
147     // byte 128-159: Y_msg
148     // byte 160-191: K_bytes
149     let mut transcript = [0u8; 6 * 32];
150
151     let mut pw_hash = Sha256::new();
152     pw_hash.update(password_vec);
153     transcript[0..32].copy_from_slice(&pw_hash.finalize());
154
155     let mut ida_hash = Sha256::new();
156     ida_hash.update(id_a);
157     transcript[32..64].copy_from_slice(&ida_hash.finalize());
158
159     let mut idb_hash = Sha256::new();
160     idb_hash.update(id_b);
161     transcript[64..96].copy_from_slice(&idb_hash.finalize());
162
163     transcript[96..128].copy_from_slice(first_msg);
164     transcript[128..160].copy_from_slice(second_msg);
165     transcript[160..192].copy_from_slice(key_bytes);
166
167     //println!("transcript: {:?}", transcript.iter().to_hex());
168
169     //let mut hash = G::TranscriptHash::default();
170     let mut hash = Sha256::new();
171     hash.update(transcript.to_vec());
172     hash.finalize().to_vec()
173 }
174
175 /// Hash symmetric identities.
176 pub(crate) fn hash_symmetric(
177     password_vec: &[u8],
178     id_s: &[u8],
179     msg_u: &[u8],
180     msg_v: &[u8],
181     key_bytes: &[u8],
182 ) -> Vec<u8> {
183     assert_eq!(msg_u.len(), 32);
184     assert_eq!(msg_v.len(), 32);
185     // # since we don't know which side is which, we must sort the messages
186     // first_msg, second_msg = sorted([msg1, msg2])
187     // transcript = b"".join([sha256(pw).digest(),
188     //                        sha256(idSymmetric).digest(),
189     //                        first_msg, second_msg, K_bytes])
190
191     // the transcript is fixed-length, made up of 5 32-byte values:
192     // byte 0-31   : sha256(pw)
193     // byte 32-63  : sha256(idSymmetric)
194     // byte 64-95  : X_msg
195     // byte 96-127 : Y_msg
196     // byte 128-159: K_bytes
197     let mut transcript = [0u8; 5 * 32];
198
199     let mut pw_hash = Sha256::new();
200     pw_hash.update(password_vec);
201     transcript[0..32].copy_from_slice(&pw_hash.finalize());
202
203     let mut ids_hash = Sha256::new();
204     ids_hash.update(id_s);
205     transcript[32..64].copy_from_slice(&ids_hash.finalize());
206
207     if msg_u < msg_v {
208         transcript[64..96].copy_from_slice(msg_u);
209         transcript[96..128].copy_from_slice(msg_v);
210     } else {
211         transcript[64..96].copy_from_slice(msg_v);
212         transcript[96..128].copy_from_slice(msg_u);
213     }
214     transcript[128..160].copy_from_slice(key_bytes);
215
216     let mut hash = Sha256::new();
217     hash.update(transcript.to_vec());
218     hash.finalize().to_vec()
219 }