hopr_crypto_types/
utils.rs

1use cipher::zeroize;
2use generic_array::{ArrayLength, GenericArray};
3use hopr_crypto_random::{Randomizable, random_array};
4use k256::{
5    Secp256k1,
6    elliptic_curve::{
7        Group, PrimeField,
8        hash2curve::{ExpandMsgXmd, GroupDigest},
9    },
10};
11use sha3::Sha3_256;
12use subtle::{Choice, ConstantTimeEq};
13use typenum::Unsigned;
14
15use crate::{
16    errors::{
17        CryptoError,
18        CryptoError::{CalculationError, InvalidInputValue, InvalidParameterSize},
19    },
20    prelude::{HalfKey, SecretKey},
21};
22
23/// Generates a random elliptic curve point on the secp256k1 curve (but not a point in infinity).
24/// Returns the encoded secret scalar and the corresponding point.
25pub(crate) fn random_group_element() -> ([u8; 32], crate::types::CurvePoint) {
26    let mut scalar = k256::NonZeroScalar::from_uint(1u32.into()).unwrap();
27    let mut point = k256::ProjectivePoint::IDENTITY;
28    while point.is_identity().into() {
29        scalar = k256::NonZeroScalar::random(&mut hopr_crypto_random::rng());
30        point = k256::ProjectivePoint::GENERATOR * scalar.as_ref();
31    }
32    (scalar.to_bytes().into(), point.to_affine().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 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/// The implementation uses the ` hash_to_field ` function as defined in
65/// `<https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#name-hashing-to-a-finite-field>`
66/// The `secret` must be at least `SecretKey::LENGTH` long.
67/// The `tag` parameter will be used as an additional Domain Separation Tag.
68pub fn sample_secp256k1_field_element(secret: &[u8], tag: &str) -> crate::errors::Result<HalfKey> {
69    if secret.len() >= SecretKey::LENGTH {
70        let scalar = Secp256k1::hash_to_scalar::<ExpandMsgXmd<Sha3_256>>(
71            &[secret],
72            &[b"secp256k1_XMD:SHA3-256_SSWU_RO_", tag.as_bytes()],
73        )
74        .map_err(|_| CalculationError)?;
75        Ok(HalfKey::try_from(scalar.to_bytes().as_ref())?)
76    } else {
77        Err(InvalidParameterSize {
78            name: "secret",
79            expected: SecretKey::LENGTH,
80        })
81    }
82}
83
84/// Represents a secret value of a fixed length that is zeroized on drop.
85/// Secret values are always compared in constant time.
86/// The default value is all zeroes.
87#[derive(Clone, zeroize::ZeroizeOnDrop)]
88pub struct SecretValue<L: ArrayLength>(GenericArray<u8, L>);
89
90impl<L: ArrayLength> ConstantTimeEq for SecretValue<L> {
91    fn ct_eq(&self, other: &Self) -> Choice {
92        self.0.ct_eq(&other.0)
93    }
94}
95
96impl<L: ArrayLength> AsRef<[u8]> for SecretValue<L> {
97    fn as_ref(&self) -> &[u8] {
98        self.0.as_ref()
99    }
100}
101
102impl<L: ArrayLength> From<GenericArray<u8, L>> for SecretValue<L> {
103    fn from(value: GenericArray<u8, L>) -> Self {
104        Self(value)
105    }
106}
107
108impl<'a, L: ArrayLength> From<&'a SecretValue<L>> for &'a GenericArray<u8, L> {
109    fn from(value: &'a SecretValue<L>) -> Self {
110        &value.0
111    }
112}
113
114impl<L: ArrayLength> TryFrom<&[u8]> for SecretValue<L> {
115    type Error = CryptoError;
116
117    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
118        if value.len() == Self::LENGTH {
119            Ok(Self(GenericArray::from_slice(value).clone()))
120        } else {
121            Err(InvalidInputValue("value"))
122        }
123    }
124}
125
126impl<L: ArrayLength> Default for SecretValue<L> {
127    fn default() -> Self {
128        Self(GenericArray::default())
129    }
130}
131
132impl<L: ArrayLength> AsMut<[u8]> for SecretValue<L> {
133    fn as_mut(&mut self) -> &mut [u8] {
134        self.0.as_mut()
135    }
136}
137
138impl<L: ArrayLength> From<SecretValue<L>> for Box<[u8]> {
139    fn from(value: SecretValue<L>) -> Self {
140        value.as_ref().into()
141    }
142}
143
144#[cfg(feature = "serde")]
145impl<L: ArrayLength> serde::Serialize for SecretValue<L> {
146    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147    where
148        S: serde::Serializer,
149    {
150        self.0.serialize(serializer)
151    }
152}
153
154#[cfg(feature = "serde")]
155impl<'de, L: ArrayLength> serde::Deserialize<'de> for SecretValue<L> {
156    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157    where
158        D: serde::Deserializer<'de>,
159    {
160        Ok(Self(GenericArray::deserialize(deserializer)?))
161    }
162}
163
164impl<L: ArrayLength> SecretValue<L> {
165    /// Length of the secret value in bytes.
166    pub const LENGTH: usize = L::USIZE;
167}
168
169impl<L: ArrayLength> Randomizable for SecretValue<L> {
170    /// Generates cryptographically strong random secret value.
171    fn random() -> Self {
172        Self(random_array())
173    }
174}