eth_stealth_addresses/
lib.rs

1use 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}