hopr_crypto_types/
utils.rs

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