Skip to main content

hopr_crypto_sphinx/
ec_groups.rs

1use hopr_types::crypto::errors::Result;
2#[cfg(feature = "secp256k1")]
3use {
4    elliptic_curve::{
5        Group,
6        ops::MulByGenerator,
7        sec1::{FromEncodedPoint, ToEncodedPoint},
8    },
9    hopr_types::crypto::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_types::crypto_random::random_bytes::<32>();
19        Self::from_bytes(&bytes).unwrap()
20    }
21
22    fn from_bytes(bytes: &[u8]) -> Result<Self> {
23        hopr_types::crypto::utils::x25519_scalar_from_bytes(bytes)
24    }
25}
26
27#[cfg(feature = "secp256k1")]
28impl Scalar for k256::Scalar {
29    fn random() -> Self {
30        // Beware, this is not constant-time
31        let mut rng = hopr_types::crypto_random::rng();
32        let mut bytes = k256::FieldBytes::default();
33        use elliptic_curve::PrimeField;
34        use hopr_types::crypto_random::Rng;
35        // Needs manual implementation due to incompatible rand crates
36        // k256::Scalar::generate_vartime(&mut hopr_types::crypto_random::rng())
37
38        loop {
39            rng.fill_bytes(&mut bytes);
40            if let Some(scalar) = k256::Scalar::from_repr(bytes).into() {
41                return scalar;
42            }
43        }
44    }
45
46    fn from_bytes(bytes: &[u8]) -> Result<Self> {
47        hopr_types::crypto::utils::k256_scalar_from_bytes(bytes)
48    }
49}
50
51#[cfg(feature = "x25519")]
52impl GroupElement<curve25519_dalek::scalar::Scalar> for curve25519_dalek::MontgomeryPoint {
53    type AlphaLen = typenum::U32;
54
55    fn to_alpha(&self) -> Alpha<typenum::U32> {
56        self.0.into()
57    }
58
59    fn from_alpha(alpha: Alpha<typenum::U32>) -> Result<Self> {
60        Ok(curve25519_dalek::MontgomeryPoint(alpha.into()))
61    }
62
63    fn generate(scalar: &curve25519_dalek::scalar::Scalar) -> Self {
64        curve25519_dalek::EdwardsPoint::mul_base(scalar).to_montgomery()
65    }
66
67    fn is_valid(&self) -> bool {
68        use curve25519_dalek::traits::IsIdentity;
69        !self.is_identity()
70    }
71}
72
73#[cfg(feature = "ed25519")]
74impl GroupElement<curve25519_dalek::scalar::Scalar> for curve25519_dalek::EdwardsPoint {
75    type AlphaLen = typenum::U32;
76
77    fn to_alpha(&self) -> Alpha<typenum::U32> {
78        self.compress().0.into()
79    }
80
81    fn from_alpha(alpha: Alpha<typenum::U32>) -> Result<Self> {
82        curve25519_dalek::edwards::CompressedEdwardsY(alpha.into())
83            .decompress()
84            .ok_or(hopr_types::crypto::errors::CryptoError::InvalidInputValue("alpha"))
85    }
86
87    fn generate(scalar: &curve25519_dalek::scalar::Scalar) -> Self {
88        curve25519_dalek::EdwardsPoint::mul_base(scalar)
89    }
90
91    fn is_valid(&self) -> bool {
92        // Ed25519 scalars always come clamped (pre-multiplied by the curve's co-factor)
93        // and therefore cannot result into points of small order.
94        // See `x25519_scalar_from_bytes` for more details.
95        use curve25519_dalek::traits::IsIdentity;
96        !self.is_identity()
97    }
98}
99
100#[cfg(feature = "secp256k1")]
101impl GroupElement<k256::Scalar> for k256::ProjectivePoint {
102    type AlphaLen = typenum::U33;
103
104    fn to_alpha(&self) -> Alpha<typenum::U33> {
105        let mut ret = Alpha::<typenum::U33>::default();
106        ret.copy_from_slice(self.to_affine().to_encoded_point(true).as_ref());
107        ret
108    }
109
110    fn from_alpha(alpha: Alpha<typenum::U33>) -> Result<Self> {
111        EncodedPoint::from_bytes(alpha)
112            .map_err(|_| CryptoError::InvalidInputValue("alpha"))
113            .and_then(|ep| {
114                AffinePoint::from_encoded_point(&ep)
115                    .into_option()
116                    .ok_or(CryptoError::InvalidInputValue("alpha"))
117            })
118            .map(k256::ProjectivePoint::from)
119    }
120
121    fn generate(scalar: &k256::Scalar) -> Self {
122        k256::ProjectivePoint::mul_by_generator(scalar)
123    }
124
125    fn is_valid(&self) -> bool {
126        !bool::from(self.is_identity())
127    }
128}
129
130// TODO: invert this, so that each SphinxSuite takes this as a type argument
131/// Default packet block size for the Sphinx protocol.
132///
133/// Currently, 1038 bytes.
134pub type DefaultSphinxPacketSize = typenum::Sum<typenum::U1024, typenum::U14>;
135
136pub use typenum::Unsigned;
137
138/// Represents an instantiation of the Sphinx protocol using secp256k1 elliptic curve and `ChainKeypair`
139#[cfg(feature = "secp256k1")]
140#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142pub struct Secp256k1Suite;
143
144#[cfg(feature = "secp256k1")]
145impl SphinxSuite for Secp256k1Suite {
146    type E = k256::Scalar;
147    type G = k256::ProjectivePoint;
148    type P = hopr_types::crypto::keypairs::ChainKeypair;
149    type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
150}
151
152/// Represents an instantiation of the Sphinx protocol using the ed25519 curve and `OffchainKeypair`
153#[cfg(feature = "ed25519")]
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156pub struct Ed25519Suite;
157
158#[cfg(feature = "ed25519")]
159impl SphinxSuite for Ed25519Suite {
160    type E = curve25519_dalek::scalar::Scalar;
161    type G = curve25519_dalek::edwards::EdwardsPoint;
162    type P = hopr_types::crypto::keypairs::OffchainKeypair;
163    type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
164}
165
166/// Represents an instantiation of the Sphinx protocol using the Curve25519 curve and `OffchainKeypair`
167#[cfg(feature = "x25519")]
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub struct X25519Suite;
171
172#[cfg(feature = "x25519")]
173impl SphinxSuite for X25519Suite {
174    type E = curve25519_dalek::scalar::Scalar;
175    type G = curve25519_dalek::montgomery::MontgomeryPoint;
176    type P = hopr_types::crypto::keypairs::OffchainKeypair;
177    type PRP = hopr_types::crypto::lioness::LionessBlake3ChaCha20<DefaultSphinxPacketSize>;
178}
179
180#[cfg(test)]
181mod tests {
182    use parameterized::parameterized;
183
184    use crate::shared_keys::tests::generic_sphinx_suite_test;
185
186    #[cfg(feature = "secp256k1")]
187    #[test]
188    fn test_extract_key_from_group_element() {
189        use crate::shared_keys::GroupElement;
190
191        let salt = [0xde, 0xad, 0xbe, 0xef];
192        let pt = k256::ProjectivePoint::GENERATOR;
193
194        let key = pt.extract_key("test", &salt);
195        assert_eq!(
196            "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
197            hex::encode(key)
198        );
199    }
200
201    #[cfg(feature = "secp256k1")]
202    #[test]
203    fn test_expand_key_from_group_element() {
204        use crate::shared_keys::GroupElement;
205
206        let salt = [0xde, 0xad, 0xbe, 0xef];
207        let pt = k256::ProjectivePoint::GENERATOR;
208
209        let key = pt.extract_key("test", &salt);
210
211        assert_eq!(
212            "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
213            hex::encode(key)
214        );
215    }
216
217    #[cfg(feature = "secp256k1")]
218    #[parameterized(nodes = {4, 3, 2, 1})]
219    fn test_secp256k1_suite(nodes: usize) {
220        generic_sphinx_suite_test::<crate::ec_groups::Secp256k1Suite>(nodes)
221    }
222
223    #[cfg(feature = "ed25519")]
224    #[parameterized(nodes = {4, 3, 2, 1})]
225    fn test_ed25519_shared_keys(nodes: usize) {
226        generic_sphinx_suite_test::<crate::ec_groups::Ed25519Suite>(nodes)
227    }
228
229    #[cfg(feature = "x25519")]
230    #[parameterized(nodes = {4, 3, 2, 1})]
231    fn test_montgomery_shared_keys(nodes: usize) {
232        generic_sphinx_suite_test::<crate::ec_groups::X25519Suite>(nodes)
233    }
234}