hopr_crypto_sphinx/
ec_groups.rs

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