hopr_crypto_types/
utils.rs

1use crate::errors::CryptoError;
2use crate::errors::CryptoError::{CalculationError, InvalidInputValue, InvalidParameterSize};
3use crate::prelude::{HalfKey, SecretKey};
4use cipher::zeroize;
5use generic_array::{ArrayLength, GenericArray};
6use hopr_crypto_random::{random_array, Randomizable};
7use k256::elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
8use k256::elliptic_curve::{Group, PrimeField};
9use k256::Secp256k1;
10use sha3::Sha3_256;
11use subtle::{Choice, ConstantTimeEq};
12use typenum::Unsigned;
13
14/// Generates a random elliptic curve point on the secp256k1 curve (but not a point in infinity).
15/// Returns the encoded secret scalar and the corresponding point.
16pub(crate) fn random_group_element() -> ([u8; 32], crate::types::CurvePoint) {
17    let mut scalar = k256::NonZeroScalar::from_uint(1u32.into()).unwrap();
18    let mut point = k256::ProjectivePoint::IDENTITY;
19    while point.is_identity().into() {
20        scalar = k256::NonZeroScalar::random(&mut hopr_crypto_random::rng());
21        point = k256::ProjectivePoint::GENERATOR * scalar.as_ref();
22    }
23    (scalar.to_bytes().into(), point.to_affine().into())
24}
25
26/// Creates X25519 secret scalar (also compatible with Ed25519 scalar) from the given bytes.
27/// This function ensures the value is pre-multiplied by the curve's co-factor and already
28/// reduced mod 2^255-19.
29pub fn x25519_scalar_from_bytes(bytes: &[u8]) -> crate::errors::Result<curve25519_dalek::scalar::Scalar> {
30    if bytes.len() == 32 {
31        // Representation of the scalar is little-endian
32        let mut clamped = [0u8; 32];
33        clamped.copy_from_slice(&bytes[..32]);
34        clamped[00] &= 0b1111_1000; // clear the 3 LSB bits (= multiply by Curve25519's co-factor)
35        clamped[31] &= 0b0111_1111; // clear the 256-th bit
36        clamped[31] |= 0b0100_0000; // make it 255-bit number
37
38        Ok(curve25519_dalek::scalar::Scalar::from_bytes_mod_order(clamped))
39    } else {
40        Err(InvalidInputValue("bytes"))
41    }
42}
43
44/// Creates secp256k1 secret scalar from the given bytes.
45/// Note that this function allows zero scalars.
46pub fn k256_scalar_from_bytes(bytes: &[u8]) -> crate::errors::Result<k256::Scalar> {
47    if bytes.len() == k256::elliptic_curve::FieldBytesSize::<Secp256k1>::to_usize() {
48        Option::from(k256::Scalar::from_repr(*k256::FieldBytes::from_slice(bytes))).ok_or(InvalidInputValue("bytes"))
49    } else {
50        Err(InvalidInputValue("bytes"))
51    }
52}
53
54/// Sample a random secp256k1 field element that can represent a valid secp256k1 point.
55/// The implementation uses the ` hash_to_field ` function as defined in
56/// `<https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#name-hashing-to-a-finite-field>`
57/// The `secret` must be at least `SecretKey::LENGTH` long.
58/// The `tag` parameter will be used as an additional Domain Separation Tag.
59pub fn sample_secp256k1_field_element(secret: &[u8], tag: &str) -> crate::errors::Result<HalfKey> {
60    if secret.len() >= SecretKey::LENGTH {
61        let scalar = Secp256k1::hash_to_scalar::<ExpandMsgXmd<Sha3_256>>(
62            &[secret],
63            &[b"secp256k1_XMD:SHA3-256_SSWU_RO_", tag.as_bytes()],
64        )
65        .map_err(|_| CalculationError)?;
66        Ok(HalfKey::try_from(scalar.to_bytes().as_ref())?)
67    } else {
68        Err(InvalidParameterSize {
69            name: "secret",
70            expected: SecretKey::LENGTH,
71        })
72    }
73}
74
75/// Represents a secret value of a fixed length that is zeroized on drop.
76/// Secret values are always compared in constant time.
77/// The default value is all zeroes.
78#[derive(Clone, zeroize::ZeroizeOnDrop)]
79pub struct SecretValue<L: ArrayLength>(GenericArray<u8, L>);
80
81impl<L: ArrayLength> ConstantTimeEq for SecretValue<L> {
82    fn ct_eq(&self, other: &Self) -> Choice {
83        self.0.ct_eq(&other.0)
84    }
85}
86
87impl<L: ArrayLength> AsRef<[u8]> for SecretValue<L> {
88    fn as_ref(&self) -> &[u8] {
89        self.0.as_ref()
90    }
91}
92
93impl<L: ArrayLength> From<GenericArray<u8, L>> for SecretValue<L> {
94    fn from(value: GenericArray<u8, L>) -> Self {
95        Self(value)
96    }
97}
98
99impl<'a, L: ArrayLength> From<&'a SecretValue<L>> for &'a GenericArray<u8, L> {
100    fn from(value: &'a SecretValue<L>) -> Self {
101        &value.0
102    }
103}
104
105impl<L: ArrayLength> TryFrom<&[u8]> for SecretValue<L> {
106    type Error = CryptoError;
107
108    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
109        if value.len() == Self::LENGTH {
110            Ok(Self(GenericArray::from_slice(value).clone()))
111        } else {
112            Err(InvalidInputValue("value"))
113        }
114    }
115}
116
117impl<L: ArrayLength> Default for SecretValue<L> {
118    fn default() -> Self {
119        Self(GenericArray::default())
120    }
121}
122
123impl<L: ArrayLength> AsMut<[u8]> for SecretValue<L> {
124    fn as_mut(&mut self) -> &mut [u8] {
125        self.0.as_mut()
126    }
127}
128
129impl<L: ArrayLength> From<SecretValue<L>> for Box<[u8]> {
130    fn from(value: SecretValue<L>) -> Self {
131        value.as_ref().into()
132    }
133}
134
135#[cfg(feature = "serde")]
136impl<L: ArrayLength> serde::Serialize for SecretValue<L> {
137    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
138    where
139        S: serde::Serializer,
140    {
141        self.0.serialize(serializer)
142    }
143}
144
145#[cfg(feature = "serde")]
146impl<'de, L: ArrayLength> serde::Deserialize<'de> for SecretValue<L> {
147    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148    where
149        D: serde::Deserializer<'de>,
150    {
151        Ok(Self(GenericArray::deserialize(deserializer)?))
152    }
153}
154
155impl<L: ArrayLength> SecretValue<L> {
156    /// Length of the secret value in bytes.
157    pub const LENGTH: usize = L::USIZE;
158}
159
160impl<L: ArrayLength> Randomizable for SecretValue<L> {
161    /// Generates cryptographically strong random secret value.
162    fn random() -> Self {
163        Self(random_array())
164    }
165}