hopr_crypto_sphinx/
ec_groups.rs1use hopr_crypto_types::errors::Result;
2#[cfg(feature = "secp256k1")]
3use {
4 elliptic_curve::{
5 Group,
6 ops::MulByGenerator,
7 sec1::{FromEncodedPoint, ToEncodedPoint},
8 },
9 hopr_crypto_types::prelude::CryptoError,
10 k256::{AffinePoint, EncodedPoint},
11};
12
13use crate::shared_keys::{Alpha, GroupElement, Scalar, SphinxSuite};
14
15#[cfg(any(feature = "x25519", feature = "ed25519"))]
16impl Scalar for curve25519_dalek::scalar::Scalar {
17 fn random() -> Self {
18 let bytes = hopr_crypto_random::random_bytes::<32>();
19 Self::from_bytes(&bytes).unwrap()
20 }
21
22 fn from_bytes(bytes: &[u8]) -> Result<Self> {
23 hopr_crypto_types::utils::x25519_scalar_from_bytes(bytes)
24 }
25}
26
27#[cfg(feature = "secp256k1")]
28impl Scalar for k256::Scalar {
29 fn random() -> Self {
30 k256::Scalar::generate_vartime(&mut hopr_crypto_random::rng())
32 }
33
34 fn from_bytes(bytes: &[u8]) -> Result<Self> {
35 hopr_crypto_types::utils::k256_scalar_from_bytes(bytes)
36 }
37}
38
39#[cfg(feature = "x25519")]
40impl GroupElement<curve25519_dalek::scalar::Scalar> for curve25519_dalek::MontgomeryPoint {
41 type AlphaLen = typenum::U32;
42
43 fn to_alpha(&self) -> Alpha<typenum::U32> {
44 self.0.into()
45 }
46
47 fn from_alpha(alpha: Alpha<typenum::U32>) -> Result<Self> {
48 Ok(curve25519_dalek::MontgomeryPoint(alpha.into()))
49 }
50
51 fn generate(scalar: &curve25519_dalek::scalar::Scalar) -> Self {
52 curve25519_dalek::EdwardsPoint::mul_base(scalar).to_montgomery()
53 }
54
55 fn is_valid(&self) -> bool {
56 use curve25519_dalek::traits::IsIdentity;
57 !self.is_identity()
58 }
59}
60
61#[cfg(feature = "ed25519")]
62impl GroupElement<curve25519_dalek::scalar::Scalar> for curve25519_dalek::EdwardsPoint {
63 type AlphaLen = typenum::U32;
64
65 fn to_alpha(&self) -> Alpha<typenum::U32> {
66 self.compress().0.into()
67 }
68
69 fn from_alpha(alpha: Alpha<typenum::U32>) -> Result<Self> {
70 curve25519_dalek::edwards::CompressedEdwardsY(alpha.into())
71 .decompress()
72 .ok_or(hopr_crypto_types::errors::CryptoError::InvalidInputValue("alpha"))
73 }
74
75 fn generate(scalar: &curve25519_dalek::scalar::Scalar) -> Self {
76 curve25519_dalek::EdwardsPoint::mul_base(scalar)
77 }
78
79 fn is_valid(&self) -> bool {
80 use curve25519_dalek::traits::IsIdentity;
81 self.is_torsion_free() && !self.is_identity() && !self.is_small_order()
82 }
83}
84
85#[cfg(feature = "secp256k1")]
86impl GroupElement<k256::Scalar> for k256::ProjectivePoint {
87 type AlphaLen = typenum::U33;
88
89 fn to_alpha(&self) -> Alpha<typenum::U33> {
90 let mut ret = Alpha::<typenum::U33>::default();
91 ret.copy_from_slice(self.to_affine().to_encoded_point(true).as_ref());
92 ret
93 }
94
95 fn from_alpha(alpha: Alpha<typenum::U33>) -> Result<Self> {
96 EncodedPoint::from_bytes(&alpha)
97 .map_err(|_| CryptoError::InvalidInputValue("alpha"))
98 .and_then(|ep| {
99 AffinePoint::from_encoded_point(&ep)
100 .into_option()
101 .ok_or(CryptoError::InvalidInputValue("alpha"))
102 })
103 .map(k256::ProjectivePoint::from)
104 }
105
106 fn generate(scalar: &k256::Scalar) -> Self {
107 k256::ProjectivePoint::mul_by_generator(scalar)
108 }
109
110 fn is_valid(&self) -> bool {
111 !bool::from(self.is_identity())
112 }
113}
114
115#[cfg(feature = "secp256k1")]
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119pub struct Secp256k1Suite;
120
121#[cfg(feature = "secp256k1")]
122impl SphinxSuite for Secp256k1Suite {
123 type E = k256::Scalar;
124 type G = k256::ProjectivePoint;
125 type P = hopr_crypto_types::keypairs::ChainKeypair;
126 type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
127}
128
129#[cfg(feature = "ed25519")]
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133pub struct Ed25519Suite;
134
135#[cfg(feature = "ed25519")]
136impl SphinxSuite for Ed25519Suite {
137 type E = curve25519_dalek::scalar::Scalar;
138 type G = curve25519_dalek::edwards::EdwardsPoint;
139 type P = hopr_crypto_types::keypairs::OffchainKeypair;
140 type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
141}
142
143#[cfg(feature = "x25519")]
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147pub struct X25519Suite;
148
149#[cfg(feature = "x25519")]
150impl SphinxSuite for X25519Suite {
151 type E = curve25519_dalek::scalar::Scalar;
152 type G = curve25519_dalek::montgomery::MontgomeryPoint;
153 type P = hopr_crypto_types::keypairs::OffchainKeypair;
154 type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
155}
156
157#[cfg(test)]
158mod tests {
159 use parameterized::parameterized;
160
161 use crate::shared_keys::tests::generic_sphinx_suite_test;
162
163 #[cfg(feature = "secp256k1")]
164 #[test]
165 fn test_extract_key_from_group_element() {
166 use crate::shared_keys::GroupElement;
167
168 let salt = [0xde, 0xad, 0xbe, 0xef];
169 let pt = k256::ProjectivePoint::GENERATOR;
170
171 let key = pt.extract_key("test", &salt);
172 assert_eq!(
173 "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
174 hex::encode(key)
175 );
176 }
177
178 #[cfg(feature = "secp256k1")]
179 #[test]
180 fn test_expand_key_from_group_element() {
181 use crate::shared_keys::GroupElement;
182
183 let salt = [0xde, 0xad, 0xbe, 0xef];
184 let pt = k256::ProjectivePoint::GENERATOR;
185
186 let key = pt.extract_key("test", &salt);
187
188 assert_eq!(
189 "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
190 hex::encode(key)
191 );
192 }
193
194 #[cfg(feature = "secp256k1")]
195 #[parameterized(nodes = {4, 3, 2, 1})]
196 fn test_secp256k1_suite(nodes: usize) {
197 generic_sphinx_suite_test::<crate::ec_groups::Secp256k1Suite>(nodes)
198 }
199
200 #[cfg(feature = "ed25519")]
201 #[parameterized(nodes = {4, 3, 2, 1})]
202 fn test_ed25519_shared_keys(nodes: usize) {
203 generic_sphinx_suite_test::<crate::ec_groups::Ed25519Suite>(nodes)
204 }
205
206 #[cfg(feature = "x25519")]
207 #[parameterized(nodes = {4, 3, 2, 1})]
208 fn test_montgomery_shared_keys(nodes: usize) {
209 generic_sphinx_suite_test::<crate::ec_groups::X25519Suite>(nodes)
210 }
211}