hopr_crypto_sphinx/
ec_groups.rs

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