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.
55pub fn k256_scalar_from_bytes(bytes: &[u8]) -> crate::errors::Result<k256::Scalar> {
56    if bytes.len() == k256::elliptic_curve::FieldBytesSize::<Secp256k1>::to_usize() {
57        Option::from(k256::Scalar::from_repr(*k256::FieldBytes::from_slice(bytes))).ok_or(InvalidInputValue("bytes"))
58    } else {
59        Err(InvalidInputValue("bytes"))
60    }
61}
62
63/// Sample a random secp256k1 field element that can represent a valid secp256k1 point.
64///
65/// The implementation uses the ` hash_to_field ` function as defined in
66/// `<https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#name-hashing-to-a-finite-field>`
67/// The `secret` must be at least `SecretKey::LENGTH` long.
68/// The `tag` parameter will be used as an additional Domain Separation Tag.
69pub fn sample_secp256k1_field_element(secret: &[u8], tag: &str) -> crate::errors::Result<HalfKey> {
70    if secret.len() >= SecretKey::LENGTH {
71        let scalar = Secp256k1::hash_to_scalar::<ExpandMsgXmd<Sha3_256>>(
72            &[secret],
73            &[b"secp256k1_XMD:SHA3-256_SSWU_RO_", tag.as_bytes()],
74        )
75        .map_err(|_| CalculationError)?;
76        Ok(HalfKey::try_from(scalar.to_bytes().as_ref())?)
77    } else {
78        Err(InvalidParameterSize {
79            name: "secret",
80            expected: SecretKey::LENGTH,
81        })
82    }
83}
84
85/// Represents a secret value of a fixed length that is zeroized on drop.
86/// Secret values are always compared in constant time.
87/// The default value is all zeroes.
88#[derive(Clone, zeroize::ZeroizeOnDrop)]
89pub struct SecretValue<L: ArrayLength>(GenericArray<u8, L>);
90
91impl<L: ArrayLength> ConstantTimeEq for SecretValue<L> {
92    fn ct_eq(&self, other: &Self) -> Choice {
93        self.0.ct_eq(&other.0)
94    }
95}
96
97impl<L: ArrayLength> AsRef<[u8]> for SecretValue<L> {
98    fn as_ref(&self) -> &[u8] {
99        self.0.as_ref()
100    }
101}
102
103impl<L: ArrayLength> From<GenericArray<u8, L>> for SecretValue<L> {
104    fn from(value: GenericArray<u8, L>) -> Self {
105        Self(value)
106    }
107}
108
109impl<'a, L: ArrayLength> From<&'a SecretValue<L>> for &'a GenericArray<u8, L> {
110    fn from(value: &'a SecretValue<L>) -> Self {
111        &value.0
112    }
113}
114
115impl<L: ArrayLength> TryFrom<&[u8]> for SecretValue<L> {
116    type Error = CryptoError;
117
118    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
119        if value.len() == Self::LENGTH {
120            Ok(Self(GenericArray::from_slice(value).clone()))
121        } else {
122            Err(InvalidInputValue("value"))
123        }
124    }
125}
126
127impl<L: ArrayLength> Default for SecretValue<L> {
128    fn default() -> Self {
129        Self(GenericArray::default())
130    }
131}
132
133impl<L: ArrayLength> AsMut<[u8]> for SecretValue<L> {
134    fn as_mut(&mut self) -> &mut [u8] {
135        self.0.as_mut()
136    }
137}
138
139impl<L: ArrayLength> From<SecretValue<L>> for Box<[u8]> {
140    fn from(value: SecretValue<L>) -> Self {
141        value.as_ref().into()
142    }
143}
144
145impl From<SecretValue<typenum::U32>> for [u8; 32] {
146    fn from(value: SecretValue<typenum::U32>) -> Self {
147        value.0.into_array()
148    }
149}
150
151#[cfg(feature = "serde")]
152impl<L: ArrayLength> serde::Serialize for SecretValue<L> {
153    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154    where
155        S: serde::Serializer,
156    {
157        self.0.serialize(serializer)
158    }
159}
160
161#[cfg(feature = "serde")]
162impl<'de, L: ArrayLength> serde::Deserialize<'de> for SecretValue<L> {
163    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
164    where
165        D: serde::Deserializer<'de>,
166    {
167        Ok(Self(GenericArray::deserialize(deserializer)?))
168    }
169}
170
171impl<L: ArrayLength> SecretValue<L> {
172    /// Length of the secret value in bytes.
173    pub const LENGTH: usize = L::USIZE;
174}
175
176impl<L: ArrayLength> Randomizable for SecretValue<L> {
177    /// Generates cryptographically strong random secret value.
178    fn random() -> Self {
179        Self(random_array())
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn test_sample_field_element() {
189        let secret = [1u8; SecretKey::LENGTH];
190        assert!(sample_secp256k1_field_element(&secret, "TEST_TAG").is_ok());
191    }
192}