stealth_address_kit/
stealth_addresses.rs1use 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
10pub trait HasViewTag {
12 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
25pub trait ToBytesFromProjective {
27 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
43type FrOf<P> =
45 <<<P as StealthAddressOnCurve>::Projective as CurveGroup>::Affine as AffineRepr>::ScalarField;
46
47pub trait StealthAddressOnCurve {
49 type Projective: Display
51 + Add<Output = Self::Projective>
52 + From<<Self::Projective as CurveGroup>::Affine>
53 + CurveGroup;
54
55 #[inline]
65 fn derive_public_key(private_key: &FrOf<Self>) -> Self::Projective {
66 Self::Projective::generator() * *private_key
67 }
68
69 #[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 #[inline]
87 fn generate_random_fr() -> FrOf<Self> {
88 FrOf::<Self>::rand(&mut OsRng)
89 }
90
91 #[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 FrOf::<Self>::from_le_bytes_mod_order(hash.as_slice())
112 }
113
114 #[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 #[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 #[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}