miden_crypto/ecdh/ecgfp5/
mod.rs1use alloc::vec::Vec;
28
29mod advice;
30mod direct;
31mod native;
32mod tables;
33
34#[cfg(test)]
35mod tests;
36
37pub use advice::AgreementAdvice;
38use native::{curve::Point, field::GFp5, scalar::Scalar};
39use rand::CryptoRng;
40pub use tables::PreparedPublicKey;
41
42use crate::{
43 Felt, SequentialCommit, Word,
44 hash::eidos::{Eidos, domains::ECGFP5_PUBLIC_KEY},
45 utils::{
46 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
47 read_sensitive_array,
48 zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing},
49 },
50};
51
52pub struct SecretKey(Zeroizing<Scalar>);
54
55impl SecretKey {
56 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
58 let mut bytes = Zeroizing::new([0; 40]);
59 loop {
60 rng.fill_bytes(bytes.as_mut());
61 bytes[39] &= 0x7f;
63 if let Ok(key) = Self::from_bytes(&bytes) {
64 return key;
65 }
66 }
67 }
68
69 pub fn from_bytes(bytes: &[u8; 40]) -> Result<Self, InvalidSecretKey> {
72 let (scalar, valid) = Scalar::decode(bytes);
73 let scalar = Zeroizing::new(scalar);
74 if valid == 0 || scalar.iszero() != 0 {
75 return Err(InvalidSecretKey);
76 }
77 Ok(Self(scalar))
78 }
79
80 pub fn to_bytes(&self) -> Zeroizing<[u8; 40]> {
82 Zeroizing::new(self.0.encode())
83 }
84
85 pub fn public_key(&self) -> PublicKey {
87 PublicKey(Point::mulgen(*self.0).encode().encode())
88 }
89
90 pub fn diffie_hellman(&self, public_key: &PublicKey) -> SharedSecret {
92 SharedSecret((public_key.point() * *self.0).encode().encode())
93 }
94}
95
96impl ZeroizeOnDrop for SecretKey {}
97
98impl Serializable for SecretKey {
99 fn write_into<W: ByteWriter>(&self, target: &mut W) {
100 target.write_bytes(self.to_bytes().as_ref());
101 }
102}
103
104impl Deserializable for SecretKey {
105 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
106 let bytes = read_sensitive_array::<40, _>(source)?;
107 Self::from_bytes(&bytes)
108 .map_err(|_| DeserializationError::InvalidValue("invalid EcGFp5 secret key".into()))
109 }
110}
111
112#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
114pub struct PublicKey([u8; 40]);
115
116impl PublicKey {
117 pub fn to_commitment(&self) -> Word {
119 <Self as SequentialCommit>::to_commitment(self)
120 }
121
122 pub fn from_bytes(bytes: [u8; 40]) -> Result<Self, InvalidPublicKey> {
124 let (encoding, canonical) = GFp5::decode(&bytes);
125 let (_, valid) = Point::decode(encoding);
126 if canonical == 0 || valid == 0 || encoding.iszero() != 0 {
127 return Err(InvalidPublicKey);
128 }
129 Ok(Self(bytes))
130 }
131
132 pub fn from_elements(elements: [Felt; 5]) -> Result<Self, InvalidPublicKey> {
134 let mut bytes = [0; 40];
135 for (chunk, element) in bytes.chunks_exact_mut(8).zip(elements) {
136 chunk.copy_from_slice(&element.as_canonical_u64().to_le_bytes());
137 }
138 Self::from_bytes(bytes)
139 }
140
141 pub fn to_bytes(self) -> [u8; 40] {
143 self.0
144 }
145
146 pub fn to_elements(self) -> [Felt; 5] {
148 elements_from_bytes(&self.0)
149 }
150
151 fn point(self) -> Point {
152 let (encoding, _) = GFp5::decode(&self.0);
153 Point::decode(encoding).0
154 }
155}
156
157impl SequentialCommit for PublicKey {
158 type Commitment = Word;
159
160 fn to_commitment(&self) -> Word {
161 Eidos::hash_elements_in_domain(&PublicKey::to_elements(*self), ECGFP5_PUBLIC_KEY)
162 }
163
164 fn to_elements(&self) -> Vec<Felt> {
165 PublicKey::to_elements(*self).to_vec()
166 }
167}
168
169impl Serializable for PublicKey {
170 fn write_into<W: ByteWriter>(&self, target: &mut W) {
171 target.write_bytes(&self.0);
172 }
173}
174
175impl Deserializable for PublicKey {
176 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
177 Self::from_bytes(source.read_array()?)
178 .map_err(|_| DeserializationError::InvalidValue("invalid EcGFp5 public key".into()))
179 }
180}
181
182pub struct SharedSecret([u8; 40]);
185
186impl SharedSecret {
187 pub fn to_elements(&self) -> [Felt; 5] {
189 elements_from_bytes(&self.0)
190 }
191}
192
193impl AsRef<[u8]> for SharedSecret {
194 fn as_ref(&self) -> &[u8] {
195 &self.0
196 }
197}
198
199impl Zeroize for SharedSecret {
200 fn zeroize(&mut self) {
201 self.0.zeroize();
202 }
203}
204
205impl Drop for SharedSecret {
206 fn drop(&mut self) {
207 self.zeroize();
208 }
209}
210
211impl ZeroizeOnDrop for SharedSecret {}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
215#[error("invalid EcGFp5 secret scalar")]
216pub struct InvalidSecretKey;
217
218#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
220#[error("invalid EcGFp5 public key")]
221pub struct InvalidPublicKey;
222
223fn elements_from_bytes(bytes: &[u8; 40]) -> [Felt; 5] {
224 core::array::from_fn(|i| {
225 Felt::new_unchecked(u64::from_le_bytes(bytes[8 * i..8 * i + 8].try_into().unwrap()))
226 })
227}