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