stealth_address_kit/
stealth_addresses.rs

1use ark_ec::{AffineRepr, CurveGroup, Group};
2use ark_ff::{Fp, FpConfig, PrimeField};
3use ark_serialize::CanonicalSerialize;
4use ark_std::rand::rngs::OsRng;
5use ark_std::UniformRand;
6use std::fmt::Display;
7use std::ops::Add;
8use tiny_keccak::{Hasher, Keccak};
9
10/// A trait for types that have a view tag.
11pub trait HasViewTag {
12    /// Returns the view tag.
13    fn get_view_tag(&self) -> u64;
14}
15
16impl<P: FpConfig<N>, const N: usize> HasViewTag for Fp<P, N>
17where
18    Fp<P, N>: PrimeField,
19{
20    fn get_view_tag(&self) -> u64 {
21        self.0 .0[0]
22    }
23}
24
25/// A trait for converting projective points to bytes.
26pub trait ToBytesFromProjective {
27    /// Converts the projective point to a byte vector.
28    fn to_bytes(&self) -> Vec<u8>;
29}
30
31impl<G: CurveGroup> ToBytesFromProjective for G
32where
33    G::Affine: CanonicalSerialize,
34{
35    fn to_bytes(&self) -> Vec<u8> {
36        let affine = self.into_affine();
37        let mut bytes = Vec::with_capacity(affine.compressed_size());
38        affine.serialize_compressed(&mut bytes).unwrap();
39        bytes
40    }
41}
42
43// we want to route through CurveGroup -> Config -> ScalarField
44type FrOf<P> =
45    <<<P as StealthAddressOnCurve>::Projective as CurveGroup>::Affine as AffineRepr>::ScalarField;
46
47/// A trait for implementing stealth addresses on elliptic curves.
48pub trait StealthAddressOnCurve {
49    /// The projective representation of the elliptic curve point.
50    type Projective: Display
51        + Add<Output = Self::Projective>
52        + From<<Self::Projective as CurveGroup>::Affine>
53        + CurveGroup;
54
55    /// Derives a public key from a given private key.
56    ///
57    /// # Arguments
58    ///
59    /// * `private_key` - A reference to the private key.
60    ///
61    /// # Returns
62    ///
63    /// The derived public key.
64    #[inline]
65    fn derive_public_key(private_key: &FrOf<Self>) -> Self::Projective {
66        Self::Projective::generator() * *private_key
67    }
68
69    /// Generates a random keypair.
70    ///
71    /// # Returns
72    ///
73    /// A tuple containing the private key and the derived public key.
74    #[inline]
75    fn random_keypair() -> (FrOf<Self>, Self::Projective) {
76        let private_key = Self::generate_random_fr();
77        let public_key = Self::derive_public_key(&private_key);
78        (private_key, public_key)
79    }
80
81    /// Generates a random scalar field element.
82    ///
83    /// # Returns
84    ///
85    /// A random scalar field element.
86    #[inline]
87    fn generate_random_fr() -> FrOf<Self> {
88        FrOf::<Self>::rand(&mut OsRng)
89    }
90
91    /// Hashes an input byte slice to a scalar field element.
92    ///
93    /// # Arguments
94    ///
95    /// * `input` - A byte slice to be hashed.
96    ///
97    /// # Returns
98    ///
99    /// A scalar field element derived from the hash of the input.
100    #[inline]
101    fn hash_to_fr(input: &[u8]) -> FrOf<Self>
102    where
103        FrOf<Self>: HasViewTag,
104    {
105        let mut hash = [0; 32];
106        let mut hasher = Keccak::v256();
107        hasher.update(input);
108        hasher.finalize(&mut hash);
109
110        // We export the hash as a field element
111        FrOf::<Self>::from_le_bytes_mod_order(hash.as_slice())
112    }
113
114    /// Computes a shared elliptic curve point given a private key and a public key.
115    ///
116    /// # Arguments
117    ///
118    /// * `private_key` - The private key.
119    /// * `public_key` - The public key.
120    ///
121    /// # Returns
122    ///
123    /// The computed shared elliptic curve point.
124    #[inline]
125    fn compute_shared_point(
126        private_key: FrOf<Self>,
127        public_key: Self::Projective,
128    ) -> Self::Projective {
129        public_key * private_key
130    }
131
132    /// Generates a stealth address.
133    ///
134    /// # Arguments
135    ///
136    /// * `viewing_public_key` - The viewing public key.
137    /// * `spending_public_key` - The spending public key.
138    /// * `ephemeral_private_key` - The ephemeral private key.
139    ///
140    /// # Returns
141    ///
142    /// A tuple containing the stealth address and the view tag.
143    #[inline]
144    fn generate_stealth_address(
145        viewing_public_key: Self::Projective,
146        spending_public_key: Self::Projective,
147        ephemeral_private_key: FrOf<Self>,
148    ) -> (Self::Projective, u64)
149    where
150        FrOf<Self>: HasViewTag,
151    {
152        let q = Self::compute_shared_point(ephemeral_private_key, viewing_public_key);
153        let q_hashed = Self::hash_to_fr(&q.to_bytes());
154        let q_hashed_in_g1 = Self::derive_public_key(&q_hashed);
155        let view_tag = q_hashed.get_view_tag();
156        (q_hashed_in_g1 + spending_public_key, view_tag)
157    }
158
159    /// Generates a stealth private key.
160    ///
161    /// # Arguments
162    ///
163    /// * `ephemeral_public_key` - The ephemeral public key.
164    /// * `viewing_key` - The viewing key.
165    /// * `spending_key` - The spending key.
166    /// * `expected_view_tag` - The expected view tag.
167    ///
168    /// # Returns
169    ///
170    /// An optional stealth private key.
171    #[inline]
172    fn generate_stealth_private_key(
173        ephemeral_public_key: Self::Projective,
174        viewing_key: FrOf<Self>,
175        spending_key: FrOf<Self>,
176        expected_view_tag: u64,
177    ) -> Option<FrOf<Self>>
178    where
179        FrOf<Self>: HasViewTag,
180    {
181        let q_receiver = Self::compute_shared_point(viewing_key, ephemeral_public_key);
182        let q_receiver_hashed = Self::hash_to_fr(&q_receiver.to_bytes());
183        if q_receiver_hashed.get_view_tag() == expected_view_tag {
184            Some(spending_key + q_receiver_hashed)
185        } else {
186            None
187        }
188    }
189}