hopr_crypto_sphinx/
ec_groups.rs

1use 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        // Beware, this is not constant-time
31        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        // Ed25519 scalars always come clamped (pre-multiplied by the curve's co-factor)
81        // and therefore cannot result into points of small order.
82        // See `x25519_scalar_from_bytes` for more details.
83        use curve25519_dalek::traits::IsIdentity;
84        !self.is_identity()
85    }
86}
87
88#[cfg(feature = "secp256k1")]
89impl GroupElement<k256::Scalar> for k256::ProjectivePoint {
90    type AlphaLen = typenum::U33;
91
92    fn to_alpha(&self) -> Alpha<typenum::U33> {
93        let mut ret = Alpha::<typenum::U33>::default();
94        ret.copy_from_slice(self.to_affine().to_encoded_point(true).as_ref());
95        ret
96    }
97
98    fn from_alpha(alpha: Alpha<typenum::U33>) -> Result<Self> {
99        EncodedPoint::from_bytes(&alpha)
100            .map_err(|_| CryptoError::InvalidInputValue("alpha"))
101            .and_then(|ep| {
102                AffinePoint::from_encoded_point(&ep)
103                    .into_option()
104                    .ok_or(CryptoError::InvalidInputValue("alpha"))
105            })
106            .map(k256::ProjectivePoint::from)
107    }
108
109    fn generate(scalar: &k256::Scalar) -> Self {
110        k256::ProjectivePoint::mul_by_generator(scalar)
111    }
112
113    fn is_valid(&self) -> bool {
114        !bool::from(self.is_identity())
115    }
116}
117
118/// Represents an instantiation of the Sphinx protocol using secp256k1 elliptic curve and `ChainKeypair`
119#[cfg(feature = "secp256k1")]
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct Secp256k1Suite;
123
124#[cfg(feature = "secp256k1")]
125impl SphinxSuite for Secp256k1Suite {
126    type E = k256::Scalar;
127    type G = k256::ProjectivePoint;
128    type P = hopr_crypto_types::keypairs::ChainKeypair;
129    type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
130}
131
132/// Represents an instantiation of the Sphinx protocol using the ed25519 curve and `OffchainKeypair`
133#[cfg(feature = "ed25519")]
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct Ed25519Suite;
137
138#[cfg(feature = "ed25519")]
139impl SphinxSuite for Ed25519Suite {
140    type E = curve25519_dalek::scalar::Scalar;
141    type G = curve25519_dalek::edwards::EdwardsPoint;
142    type P = hopr_crypto_types::keypairs::OffchainKeypair;
143    type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
144}
145
146/// Represents an instantiation of the Sphinx protocol using the Curve25519 curve and `OffchainKeypair`
147#[cfg(feature = "x25519")]
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
149#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
150pub struct X25519Suite;
151
152#[cfg(feature = "x25519")]
153impl SphinxSuite for X25519Suite {
154    type E = curve25519_dalek::scalar::Scalar;
155    type G = curve25519_dalek::montgomery::MontgomeryPoint;
156    type P = hopr_crypto_types::keypairs::OffchainKeypair;
157    type PRP = hopr_crypto_types::lioness::LionessBlake3ChaCha20<typenum::U1022>;
158}
159
160#[cfg(test)]
161mod tests {
162    use parameterized::parameterized;
163
164    use crate::shared_keys::tests::generic_sphinx_suite_test;
165
166    #[cfg(feature = "secp256k1")]
167    #[test]
168    fn test_extract_key_from_group_element() {
169        use crate::shared_keys::GroupElement;
170
171        let salt = [0xde, 0xad, 0xbe, 0xef];
172        let pt = k256::ProjectivePoint::GENERATOR;
173
174        let key = pt.extract_key("test", &salt);
175        assert_eq!(
176            "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
177            hex::encode(key)
178        );
179    }
180
181    #[cfg(feature = "secp256k1")]
182    #[test]
183    fn test_expand_key_from_group_element() {
184        use crate::shared_keys::GroupElement;
185
186        let salt = [0xde, 0xad, 0xbe, 0xef];
187        let pt = k256::ProjectivePoint::GENERATOR;
188
189        let key = pt.extract_key("test", &salt);
190
191        assert_eq!(
192            "08112a22609819a4c698d6c92f404628ca925f3d731d53594126ffdf19ef6fa9",
193            hex::encode(key)
194        );
195    }
196
197    #[cfg(feature = "secp256k1")]
198    #[parameterized(nodes = {4, 3, 2, 1})]
199    fn test_secp256k1_suite(nodes: usize) {
200        generic_sphinx_suite_test::<crate::ec_groups::Secp256k1Suite>(nodes)
201    }
202
203    #[cfg(feature = "ed25519")]
204    #[parameterized(nodes = {4, 3, 2, 1})]
205    fn test_ed25519_shared_keys(nodes: usize) {
206        generic_sphinx_suite_test::<crate::ec_groups::Ed25519Suite>(nodes)
207    }
208
209    #[cfg(feature = "x25519")]
210    #[parameterized(nodes = {4, 3, 2, 1})]
211    fn test_montgomery_shared_keys(nodes: usize) {
212        generic_sphinx_suite_test::<crate::ec_groups::X25519Suite>(nodes)
213    }
214}