hopr_crypto_types/
utils.rs

1use crate::errors::CryptoError;
2use crate::errors::CryptoError::InvalidInputValue;
3use generic_array::{ArrayLength, GenericArray};
4use hopr_crypto_random::random_array;
5use k256::elliptic_curve::{Group, PrimeField};
6use subtle::{Choice, ConstantTimeEq};
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9/// Convenience method to XOR one slice onto other.
10pub fn xor_inplace(a: &mut [u8], b: &[u8]) {
11    let bound = a.len().min(b.len());
12
13    // TODO: use portable_simd here
14    for i in 0..bound {
15        a[i] ^= b[i];
16    }
17}
18
19/// Generates a random elliptic curve point on secp256k1 curve (but not a point in infinity).
20/// Returns the encoded secret scalar and the corresponding point.
21pub(crate) fn random_group_element() -> ([u8; 32], crate::types::CurvePoint) {
22    let mut scalar = k256::NonZeroScalar::from_uint(1u32.into()).unwrap();
23    let mut point = k256::ProjectivePoint::IDENTITY;
24    while point.is_identity().into() {
25        scalar = k256::NonZeroScalar::random(&mut hopr_crypto_random::rng());
26        point = k256::ProjectivePoint::GENERATOR * scalar.as_ref();
27    }
28    (scalar.to_bytes().into(), point.to_affine().into())
29}
30
31/// Creates X25519 secret scalar (also compatible with Ed25519 scalar) from the given bytes.
32/// This function ensures the value is pre-multiplied by the curve's co-factor and already
33/// reduced mod 2^255-19.
34pub fn x25519_scalar_from_bytes(bytes: &[u8]) -> crate::errors::Result<curve25519_dalek::scalar::Scalar> {
35    if bytes.len() == 32 {
36        // Representation of the scalar is little-endian
37        let mut clamped = [0u8; 32];
38        clamped.copy_from_slice(&bytes[..32]);
39        clamped[00] &= 0b1111_1000; // clear the 3 LSB bits (= multiply by Curve25519's co-factor)
40        clamped[31] &= 0b0111_1111; // clear the 256-th bit
41        clamped[31] |= 0b0100_0000; // make it 255-bit number
42
43        Ok(curve25519_dalek::scalar::Scalar::from_bytes_mod_order(clamped))
44    } else {
45        Err(InvalidInputValue)
46    }
47}
48
49/// Creates secp256k1 secret scalar from the given bytes.
50/// Note that this function allows zero scalars.
51pub fn k256_scalar_from_bytes(bytes: &[u8]) -> crate::errors::Result<k256::Scalar> {
52    Option::from(k256::Scalar::from_repr(*k256::FieldBytes::from_slice(bytes))).ok_or(InvalidInputValue)
53}
54
55/// Represents a secret value of a fixed length that is zeroized on drop.
56/// Secret values are always compared in constant time.
57/// The default value is all zeroes.
58#[derive(Clone, Zeroize, ZeroizeOnDrop)]
59pub struct SecretValue<L: ArrayLength<u8>>(GenericArray<u8, L>);
60
61impl<L: ArrayLength<u8>> ConstantTimeEq for SecretValue<L> {
62    fn ct_eq(&self, other: &Self) -> Choice {
63        self.0.as_ref().ct_eq(other.0.as_ref())
64    }
65}
66
67impl<L: ArrayLength<u8>> AsRef<[u8]> for SecretValue<L> {
68    fn as_ref(&self) -> &[u8] {
69        self.0.as_ref()
70    }
71}
72
73impl<L: ArrayLength<u8>> From<GenericArray<u8, L>> for SecretValue<L> {
74    fn from(value: GenericArray<u8, L>) -> Self {
75        Self(value)
76    }
77}
78
79impl<'a, L: ArrayLength<u8>> From<&'a SecretValue<L>> for &'a GenericArray<u8, L> {
80    fn from(value: &'a SecretValue<L>) -> Self {
81        &value.0
82    }
83}
84
85impl<L: ArrayLength<u8>> TryFrom<&[u8]> for SecretValue<L> {
86    type Error = CryptoError;
87
88    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
89        if value.len() == Self::LENGTH {
90            Ok(Self(GenericArray::from_slice(value).clone()))
91        } else {
92            Err(InvalidInputValue)
93        }
94    }
95}
96
97impl<L: ArrayLength<u8>> Default for SecretValue<L> {
98    fn default() -> Self {
99        // Ensure the default value is zeroized
100        let mut ret = Self(GenericArray::default());
101        ret.zeroize();
102        ret
103    }
104}
105
106impl<L: ArrayLength<u8>> AsMut<[u8]> for SecretValue<L> {
107    fn as_mut(&mut self) -> &mut [u8] {
108        self.0.as_mut()
109    }
110}
111
112impl<L: ArrayLength<u8>> From<SecretValue<L>> for Box<[u8]> {
113    fn from(value: SecretValue<L>) -> Self {
114        value.as_ref().into()
115    }
116}
117
118impl<L: ArrayLength<u8>> SecretValue<L> {
119    /// Length of the secret value in bytes.
120    pub const LENGTH: usize = L::USIZE;
121
122    /// Generates cryptographically strong random secret value.
123    pub fn random() -> Self {
124        Self(random_array())
125    }
126}