eth_stealth_addresses/
lib.rs1use rand;
2use generic_array::GenericArray;
3use k256;
4use k256::elliptic_curve::group::GroupEncoding;
5use secp256k1::PublicKey;
6use sha3::{Digest, Keccak256};
7
8type Address = [u8; 20];
9type StealthMetaAddress = [u8; 66];
10type PublicKeyUncompressed = [u8; 65];
11type PublicKeyCompressed = [u8; 33];
12type PrivateKey = [u8; 32];
13
14pub fn generate_stealth_address(
15 stealth_meta_address: &StealthMetaAddress,
16) -> (Address, PublicKeyCompressed, u8) {
17 let r = k256::Scalar::generate_biased(&mut rand::thread_rng());
18 let (spend_pk_point, view_pk_point) = decode_stealth_meta_address(stealth_meta_address);
19 let (stealth_pk_point, view_tag) = get_stealth_pubkey(spend_pk_point, view_pk_point, r);
20 let stealth_address: Address = get_address_from_pubkey(stealth_pk_point);
21 let ephemeral_pubkey: PublicKeyCompressed = encode_pubkey(get_pubkey_from_priv(r));
22
23 (stealth_address, ephemeral_pubkey, view_tag)
24}
25
26pub fn check_stealth_address(
27 stealth_address: &Address,
28 ephemeral_pubkey: &PublicKeyCompressed,
29 viewing_key: &PrivateKey,
30 spending_pubkey: &PublicKeyCompressed,
31) -> bool {
32 let (shared_secret, _) = get_shared_secret(
33 decode_pubkey(&ephemeral_pubkey),
34 decode_priv(&viewing_key),
35 );
36 let check: Address = get_address_from_pubkey(
37 decode_pubkey(&spending_pubkey) + get_pubkey_from_priv(shared_secret),
38 );
39
40 return check == *stealth_address;
41}
42
43pub fn check_stealth_address_fast(
44 stealth_address: &Address,
45 ephemeral_pubkey: &PublicKeyCompressed,
46 viewing_key: &PrivateKey,
47 spending_pubkey: &PublicKeyCompressed,
48 view_tag: u8,
49) -> bool {
50 let (shared_secret, view_tag_check) = get_shared_secret(
51 decode_pubkey(&ephemeral_pubkey),
52 decode_priv(&viewing_key),
53 );
54 if view_tag_check == view_tag {
55 let check = get_address_from_pubkey(
56 decode_pubkey(&spending_pubkey) + get_pubkey_from_priv(shared_secret),
57 );
58
59 return check == *stealth_address;
60 } else {
61 return false;
62 }
63}
64
65pub fn compute_stealth_key(
66 stealth_address: &Address,
67 ephemeral_pubkey: &PublicKeyCompressed,
68 viewing_key: &PrivateKey,
69 spending_key: &PrivateKey,
70) -> PrivateKey {
71 let (shared_secret, _) = get_shared_secret(
72 decode_pubkey(&ephemeral_pubkey),
73 decode_priv(&viewing_key),
74 );
75 let stealth_key_scalar = shared_secret + decode_priv(&spending_key);
76 let stealth_address_check = get_address_from_pubkey(get_pubkey_from_priv(stealth_key_scalar));
77 if stealth_address_check != *stealth_address {
78 panic!("keys do not generate stealth address");
79 }
80 return encode_priv(stealth_key_scalar);
81}
82
83pub fn generate_stealth_meta_address() -> (StealthMetaAddress, PrivateKey, PrivateKey) {
84 let rng = &mut rand::thread_rng();
85 let s = k256::Scalar::generate_biased(rng);
86 let v = k256::Scalar::generate_biased(rng);
87 let pks = get_pubkey_from_priv(s);
88 let pkv = get_pubkey_from_priv(v);
89
90 return (encode_stealth_meta_address(pks, pkv), encode_priv(s), encode_priv(v));
91}
92
93pub fn split_stealth_meta_address(
94 encoded: &StealthMetaAddress,
95) -> (PublicKeyCompressed, PublicKeyCompressed) {
96 let mut front = encoded.to_vec();
97 let back = front.split_off(33);
98 let front_arr = front.as_slice().try_into().unwrap();
99 let back_arr = back.as_slice().try_into().unwrap();
100
101 (front_arr, back_arr)
102}
103
104pub fn encode_stealth_meta_address(
105 pks: k256::ProjectivePoint,
106 pkv: k256::ProjectivePoint,
107) -> StealthMetaAddress {
108 let b = [pks.to_bytes(), pkv.to_bytes()].concat();
109 let arr = b.as_slice().try_into().unwrap();
110
111 arr
112}
113
114pub fn decode_stealth_meta_address(
115 encoded: &StealthMetaAddress,
116) -> (k256::ProjectivePoint, k256::ProjectivePoint) {
117 let (front_arr, back_arr) = split_stealth_meta_address(encoded);
118
119 return (decode_pubkey(&front_arr), decode_pubkey(&back_arr));
120}
121
122pub fn encode_pubkey(x: k256::ProjectivePoint) -> PublicKeyCompressed {
123 let b = x.to_bytes();
124 let arr = b.as_slice().try_into().unwrap();
125
126 arr
127}
128
129pub fn decode_pubkey(encoded: &PublicKeyCompressed) -> k256::ProjectivePoint {
130 return k256::ProjectivePoint::from_bytes(GenericArray::from_slice(encoded)).unwrap();
131}
132
133pub fn encode_priv(x: k256::Scalar) -> PrivateKey {
134 let b = x.to_bytes();
135 let arr: PrivateKey = b.as_slice().try_into().unwrap();
136
137 arr
138}
139
140pub fn decode_priv(encoded: &PrivateKey) -> k256::Scalar {
141 let result = k256::elliptic_curve::scalar::ScalarPrimitive::from_slice(encoded);
142 let primitive = match result {
143 Ok(val) => val,
144 Err(error) => panic!("problem generating scalar: {:?}", error),
145 };
146 return k256::Scalar::from(primitive);
147}
148
149pub fn get_pubkey_from_priv(x: k256::Scalar) -> k256::ProjectivePoint {
150 return k256::ProjectivePoint::GENERATOR * x;
151}
152
153pub fn get_address_from_pubkey(pubkey: k256::ProjectivePoint) -> Address {
154 let result = PublicKey::from_slice(&pubkey.to_bytes());
155 let pub_reformat = match result {
156 Ok(val) => val,
157 Err(error) => panic!("problem generating PublicKey: {:?}", error),
158 };
159 let pub_uncompressed = pub_reformat.serialize_uncompressed();
160 let pub_hashable = strip_uncompressed_pubkey(&pub_uncompressed);
161 let mut hasher = Keccak256::new();
162 hasher.update(pub_hashable);
163 let address_raw = hasher.finalize();
164 let address: Address = address_raw[12..].try_into().unwrap();
165
166 address
167}
168
169fn strip_uncompressed_pubkey(a: &PublicKeyUncompressed) -> [u8; 64] {
170 let arr: [u8; 64] = a[1..].try_into().unwrap();
171
172 arr
173}
174
175fn get_stealth_pubkey(
176 pks: k256::ProjectivePoint,
177 pkv: k256::ProjectivePoint,
178 r: k256::Scalar,
179) -> (k256::ProjectivePoint, u8) {
180 let (shared_secret, view_tag) = get_shared_secret(pkv, r);
181 let stealth_pub = pks + get_pubkey_from_priv(shared_secret);
182
183 (stealth_pub, view_tag)
184}
185
186fn get_shared_secret(pubkey: k256::ProjectivePoint, k: k256::Scalar) -> (k256::Scalar, u8) {
187 let shared_secret_raw = pubkey * k;
188 let mut hasher = Keccak256::new();
189 hasher.update(shared_secret_raw.to_bytes());
190 let hashed_s = hasher.finalize();
191 let result = k256::elliptic_curve::scalar::ScalarPrimitive::from_slice(&hashed_s);
192 let primitive = match result {
193 Ok(val) => val,
194 Err(error) => panic!("problem generating scalar: {:?}", error),
195 };
196
197 return (k256::Scalar::from(primitive), hashed_s[0]);
198}