hopr_crypto_packet/sphinx/
ec_groups.rs1use hopr_types::crypto::errors::Result;
2#[cfg(feature = "x25519")]
3use hopr_types::crypto::primitives::Curve25519MontgomeryPoint;
4#[cfg(any(feature = "x25519", feature = "ed25519"))]
5use hopr_types::crypto::primitives::{Curve25519CompressedPoint, Curve25519Point, Curve25519Scalar, IsIdentity};
6#[cfg(feature = "secp256k1")]
7use hopr_types::crypto::{
8 crypto_traits::elliptic_curve::{
9 self, Group,
10 field::FieldBytes,
11 sec1::{FromSec1Point, Sec1Point, ToSec1Point},
12 },
13 keypairs::ChainKeypair,
14 prelude::{CryptoError, Secp256k1},
15 types::PublicKey,
16};
17
18use super::shared_keys::{Alpha, GroupElement, Scalar, SphinxSuite};
19
20#[cfg(feature = "secp256k1")]
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub struct Secp256k1Scalar(pub elliptic_curve::Scalar<Secp256k1>);
25
26#[cfg(feature = "secp256k1")]
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct Secp256k1Point(pub elliptic_curve::ProjectivePoint<Secp256k1>);
31
32#[cfg(feature = "secp256k1")]
33impl Default for Secp256k1Point {
34 fn default() -> Self {
35 Self(elliptic_curve::ProjectivePoint::<Secp256k1>::IDENTITY)
36 }
37}
38
39#[cfg(feature = "secp256k1")]
41impl std::ops::Mul for Secp256k1Scalar {
42 type Output = Self;
43
44 fn mul(self, rhs: Self) -> Self::Output {
45 Self(self.0 * rhs.0)
46 }
47}
48
49#[cfg(feature = "secp256k1")]
50impl<'a> std::ops::Mul<&'a Secp256k1Scalar> for Secp256k1Point {
51 type Output = Self;
52
53 fn mul(self, rhs: &'a Secp256k1Scalar) -> Self::Output {
54 Self(self.0 * rhs.0)
55 }
56}
57
58#[cfg(feature = "secp256k1")]
59impl From<&ChainKeypair> for Secp256k1Scalar {
60 fn from(value: &ChainKeypair) -> Self {
61 Self(elliptic_curve::Scalar::<Secp256k1>::from(value))
62 }
63}
64
65#[cfg(feature = "secp256k1")]
66impl From<&PublicKey> for Secp256k1Point {
67 fn from(value: &PublicKey) -> Self {
68 Self(elliptic_curve::ProjectivePoint::<Secp256k1>::from(value))
69 }
70}
71
72#[cfg(any(feature = "x25519", feature = "ed25519"))]
73impl Scalar for Curve25519Scalar {
74 fn random() -> Self {
75 let bytes = hopr_types::crypto_random::random_bytes::<32>();
76 Self::from_bytes(&bytes).unwrap()
77 }
78
79 fn from_bytes(bytes: &[u8]) -> Result<Self> {
80 hopr_types::crypto::utils::x25519_scalar_from_bytes(bytes)
81 }
82}
83
84#[cfg(feature = "secp256k1")]
85impl Scalar for Secp256k1Scalar {
86 fn random() -> Self {
87 let mut rng = hopr_types::crypto_random::rng();
89 let mut bytes = FieldBytes::<Secp256k1>::default();
90 use elliptic_curve::PrimeField;
91 use hopr_types::crypto_random::Rng;
92 loop {
96 rng.fill_bytes(&mut bytes);
97 if let Some(scalar) = elliptic_curve::Scalar::<Secp256k1>::from_repr(bytes).into() {
98 return Secp256k1Scalar(scalar);
99 }
100 }
101 }
102
103 fn from_bytes(bytes: &[u8]) -> Result<Self> {
104 Ok(Secp256k1Scalar(hopr_types::crypto::utils::k256_scalar_from_bytes(
105 bytes,
106 )?))
107 }
108}
109
110#[cfg(feature = "x25519")]
111impl GroupElement<Curve25519Scalar> for Curve25519MontgomeryPoint {
112 type AlphaLen = hopr_types::primitive::typenum::U32;
113
114 fn to_alpha(&self) -> Alpha<hopr_types::primitive::typenum::U32> {
115 self.0.into()
116 }
117
118 fn from_alpha(alpha: Alpha<hopr_types::primitive::typenum::U32>) -> Result<Self> {
119 Ok(Curve25519MontgomeryPoint(alpha.into()))
120 }
121
122 fn generate(scalar: &Curve25519Scalar) -> Self {
123 Curve25519Point::mul_base(scalar).to_montgomery()
124 }
125
126 fn is_valid(&self) -> bool {
127 use IsIdentity;
128 !self.is_identity()
129 }
130}
131
132#[cfg(feature = "ed25519")]
133impl GroupElement<Curve25519Scalar> for Curve25519Point {
134 type AlphaLen = hopr_types::primitive::typenum::U32;
135
136 fn to_alpha(&self) -> Alpha<hopr_types::primitive::typenum::U32> {
137 self.compress().0.into()
138 }
139
140 fn from_alpha(alpha: Alpha<hopr_types::primitive::typenum::U32>) -> Result<Self> {
141 Curve25519CompressedPoint(alpha.into())
142 .decompress()
143 .ok_or(hopr_types::crypto::errors::CryptoError::InvalidInputValue("alpha"))
144 }
145
146 fn generate(scalar: &Curve25519Scalar) -> Self {
147 Curve25519Point::mul_base(scalar)
148 }
149
150 fn is_valid(&self) -> bool {
151 use IsIdentity;
155 !self.is_identity()
156 }
157}
158
159#[cfg(feature = "secp256k1")]
160impl GroupElement<Secp256k1Scalar> for Secp256k1Point {
161 type AlphaLen = hopr_types::primitive::typenum::U33;
162
163 fn to_alpha(&self) -> Alpha<hopr_types::primitive::typenum::U33> {
164 debug_assert!(self.is_valid(), "to_alpha called on identity point");
165 let mut ret = Alpha::<hopr_types::primitive::typenum::U33>::default();
166 if !bool::from(self.0.is_identity()) {
168 ret.copy_from_slice(self.0.to_affine().to_sec1_point(true).as_ref());
169 }
170 ret
171 }
172
173 fn from_alpha(alpha: Alpha<hopr_types::primitive::typenum::U33>) -> Result<Self> {
174 Sec1Point::<Secp256k1>::from_bytes(alpha)
175 .map_err(|_| CryptoError::InvalidInputValue("alpha"))
176 .and_then(|ep| {
177 <Secp256k1 as elliptic_curve::CurveArithmetic>::AffinePoint::from_sec1_point(&ep)
178 .into_option()
179 .ok_or(CryptoError::InvalidInputValue("alpha"))
180 })
181 .map(|ap| Secp256k1Point(elliptic_curve::ProjectivePoint::<Secp256k1>::from(ap)))
182 }
183
184 fn generate(scalar: &Secp256k1Scalar) -> Self {
185 Secp256k1Point(elliptic_curve::ProjectivePoint::<Secp256k1>::mul_by_generator(
186 &scalar.0,
187 ))
188 }
189
190 fn is_valid(&self) -> bool {
191 !bool::from(self.0.is_identity())
192 }
193}
194
195pub type DefaultSphinxPacketSize = hopr_types::primitive::hybrid_array::sizes::U1040;
200
201pub use hopr_types::primitive::typenum::Unsigned;
202
203#[cfg(feature = "secp256k1")]
205#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
207pub struct Secp256k1Suite;
208
209#[cfg(feature = "secp256k1")]
210impl SphinxSuite for Secp256k1Suite {
211 type E = Secp256k1Scalar;
212 type G = Secp256k1Point;
213 type P = hopr_types::crypto::keypairs::ChainKeypair;
214 type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
215}
216
217#[cfg(feature = "ed25519")]
219#[derive(Clone, Copy, Debug, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub struct Ed25519Suite;
222
223#[cfg(feature = "ed25519")]
224impl SphinxSuite for Ed25519Suite {
225 type E = Curve25519Scalar;
226 type G = Curve25519Point;
227 type P = hopr_types::crypto::keypairs::OffchainKeypair;
228 type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
229}
230
231#[cfg(feature = "x25519")]
233#[derive(Clone, Copy, Debug, PartialEq, Eq)]
234#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235pub struct X25519Suite;
236
237#[cfg(feature = "x25519")]
238impl SphinxSuite for X25519Suite {
239 type E = Curve25519Scalar;
240 type G = Curve25519MontgomeryPoint;
241 type P = hopr_types::crypto::keypairs::OffchainKeypair;
242 type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
243}
244
245#[cfg(test)]
246mod tests {
247 use parameterized::parameterized;
248
249 use super::super::shared_keys::tests::generic_sphinx_suite_test;
250
251 #[cfg(feature = "secp256k1")]
252 #[test]
253 fn test_extract_key_from_group_element() {
254 use hopr_types::crypto::{crypto_traits::elliptic_curve, prelude::Secp256k1};
255
256 use super::{super::shared_keys::GroupElement, Secp256k1Point};
257
258 let salt = [0xde, 0xad, 0xbe, 0xef];
259 let pt = Secp256k1Point(elliptic_curve::ProjectivePoint::<Secp256k1>::GENERATOR);
260
261 let key = pt.extract_key("test", &salt);
262 assert_eq!(
263 "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
264 const_hex::encode(key)
265 );
266 }
267
268 #[cfg(feature = "secp256k1")]
269 #[test]
270 fn test_expand_key_from_group_element() {
271 use hopr_types::crypto::{crypto_traits::elliptic_curve, prelude::Secp256k1};
272
273 use super::{super::shared_keys::GroupElement, Secp256k1Point};
274
275 let salt = [0xde, 0xad, 0xbe, 0xef];
276 let pt = Secp256k1Point(elliptic_curve::ProjectivePoint::<Secp256k1>::GENERATOR);
277
278 let key = pt.extract_key("test", &salt);
279
280 assert_eq!(
281 "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
282 const_hex::encode(key)
283 );
284 }
285
286 #[cfg(feature = "secp256k1")]
287 #[parameterized(nodes = {4, 3, 2, 1})]
288 fn test_secp256k1_suite(nodes: usize) {
289 generic_sphinx_suite_test::<super::Secp256k1Suite>(nodes)
290 }
291
292 #[cfg(feature = "ed25519")]
293 #[parameterized(nodes = {4, 3, 2, 1})]
294 fn test_ed25519_shared_keys(nodes: usize) {
295 generic_sphinx_suite_test::<crate::Ed25519Suite>(nodes)
296 }
297
298 #[cfg(feature = "x25519")]
299 #[parameterized(nodes = {4, 3, 2, 1})]
300 fn test_montgomery_shared_keys(nodes: usize) {
301 generic_sphinx_suite_test::<super::X25519Suite>(nodes)
302 }
303}