openzeppelin_relayer/utils/
address_derivation.rs1use super::der::extract_public_key_from_der;
8use super::ed25519::extract_ed25519_public_key_from_der;
9
10#[derive(Debug, thiserror::Error)]
11pub enum AddressDerivationError {
12 #[error("Parse Error: {0}")]
13 ParseError(String),
14}
15
16pub fn derive_ethereum_address_from_der(der: &[u8]) -> Result<[u8; 20], AddressDerivationError> {
18 let pub_key = extract_public_key_from_der(der)
19 .map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
20
21 let hash = alloy::primitives::keccak256(pub_key);
22
23 let address_bytes = &hash[hash.len() - 20..];
25
26 let mut array = [0u8; 20];
27 array.copy_from_slice(address_bytes);
28
29 Ok(array)
30}
31
32pub fn derive_ethereum_address_from_pem(pem_str: &str) -> Result<[u8; 20], AddressDerivationError> {
34 let pkey =
35 pem::parse(pem_str).map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
36 let der = pkey.contents();
37 derive_ethereum_address_from_der(der)
38}
39
40pub fn derive_solana_address_from_pem(pem_str: &str) -> Result<String, AddressDerivationError> {
42 let pkey =
43 pem::parse(pem_str).map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
44 let content = pkey.contents();
45 derive_solana_address_from_der(content)
46}
47
48pub fn derive_stellar_address_from_pem(pem_str: &str) -> Result<String, AddressDerivationError> {
51 let pkey =
52 pem::parse(pem_str).map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
53 let content = pkey.contents();
54 derive_stellar_address_from_der(content)
55}
56
57pub fn derive_solana_address_from_der(der: &[u8]) -> Result<String, AddressDerivationError> {
64 let pubkey = extract_ed25519_public_key_from_der(der)
65 .map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
66 Ok(bs58::encode(pubkey).into_string())
67}
68
69pub fn derive_stellar_address_from_der(der: &[u8]) -> Result<String, AddressDerivationError> {
76 let pubkey = extract_ed25519_public_key_from_der(der)
77 .map_err(|e| AddressDerivationError::ParseError(e.to_string()))?;
78
79 use stellar_strkey::ed25519::PublicKey;
80 Ok(PublicKey(pubkey).to_string())
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 const VALID_SECP256K1_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjJaJh5wfZwvj8b3bQ4GYikqDTLXWUjMh\nkFs9lGj2N9B17zo37p4PSy99rDio0QHLadpso0rtTJDSISRW9MdOqA==\n-----END PUBLIC KEY-----\n"; const VALID_ED25519_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAnUV+ReQWxMZ3Z2pC/5aOPPjcc8jzOo0ZgSl7+j4AMLo=\n-----END PUBLIC KEY-----\n";
90
91 #[test]
92 fn test_derive_ethereum_address_from_pem_with_invalid_data() {
93 let invalid_pem = "not-a-valid-pem";
94 let result = derive_ethereum_address_from_pem(invalid_pem);
95 assert!(result.is_err());
96
97 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
99 }
100
101 #[test]
102 fn test_derive_ethereum_address_from_pem_with_valid_secp256k1() {
103 let result = derive_ethereum_address_from_pem(VALID_SECP256K1_PEM);
104 assert!(result.is_ok());
105
106 let address = result.unwrap();
107 assert_eq!(address.len(), 20); assert_eq!(
110 format!("0x{}", hex::encode(address)),
111 "0xeeb8861f51b3f3f2204d64bbf7a7eb25e1b4d6cd"
112 );
113 }
114
115 #[test]
116 fn test_derive_ethereum_address_from_der_with_invalid_data() {
117 let invalid_der = &[1, 2, 3];
118 let result = derive_ethereum_address_from_der(invalid_der);
119 assert!(result.is_err());
120
121 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
123 }
124
125 #[test]
126 fn test_derive_ethereum_address_from_der_with_valid_secp256k1() {
127 let pem = pem::parse(VALID_SECP256K1_PEM).unwrap();
128 let der = pem.contents();
129 let result = derive_ethereum_address_from_der(der);
130
131 assert!(result.is_ok());
132
133 let address = result.unwrap();
134 assert_eq!(address.len(), 20); assert_eq!(
137 format!("0x{}", hex::encode(address)),
138 "0xeeb8861f51b3f3f2204d64bbf7a7eb25e1b4d6cd"
139 );
140 }
141
142 #[test]
143 fn test_derive_solana_address_from_pem_with_invalid_data() {
144 let invalid_pem = "not-a-valid-pem";
145 let result = derive_solana_address_from_pem(invalid_pem);
146 assert!(result.is_err());
147
148 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
150 }
151
152 #[test]
153 fn test_derive_solana_address_from_pem_with_valid_ed25519() {
154 let result = derive_solana_address_from_pem(VALID_ED25519_PEM);
155 assert!(result.is_ok());
156
157 let address = result.unwrap();
158 assert!(!address.is_empty());
160 assert!(address.len() >= 32 && address.len() <= 44);
161
162 assert_eq!(address, "BavUBpkD77FABnevMkBVqV8BDHv7gX8sSoYYJY9WU9L5");
163 }
164
165 #[test]
166 fn test_derive_solana_address_from_pem_with_invalid_key_length() {
167 let invalid_ed25519_der = vec![0u8; 10]; let invalid_pem = pem::Pem::new("PUBLIC KEY", invalid_ed25519_der);
170 let invalid_pem_str = pem::encode(&invalid_pem);
171
172 let result = derive_solana_address_from_pem(&invalid_pem_str);
173 assert!(result.is_err());
174
175 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
176 }
177
178 #[test]
179 fn test_derive_stellar_address_from_pem_with_valid_ed25519() {
180 let result = derive_stellar_address_from_pem(VALID_ED25519_PEM);
181 assert!(result.is_ok());
182
183 let address = result.unwrap();
184 assert!(address.starts_with('G'));
186 assert_eq!(address.len(), 56);
188 }
189
190 #[test]
191 fn test_derive_stellar_address_from_pem_with_invalid_data() {
192 let invalid_pem = "not-a-valid-pem";
193 let result = derive_stellar_address_from_pem(invalid_pem);
194 assert!(result.is_err());
195
196 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
198 }
199
200 #[test]
201 fn test_derive_stellar_address_from_pem_with_invalid_key_length() {
202 let invalid_ed25519_der = vec![0u8; 10]; let invalid_pem = pem::Pem::new("PUBLIC KEY", invalid_ed25519_der);
205 let invalid_pem_str = pem::encode(&invalid_pem);
206
207 let result = derive_stellar_address_from_pem(&invalid_pem_str);
208 assert!(result.is_err());
209
210 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
211 }
212
213 fn create_ed25519_spki_der(public_key: &[u8; 32]) -> Vec<u8> {
215 let mut der = vec![
216 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, ];
222 der.extend_from_slice(public_key);
223 der
224 }
225
226 const TEST_ED25519_PUBLIC_KEY: [u8; 32] = [
228 0x9d, 0x45, 0x7e, 0x45, 0xe4, 0x16, 0xc4, 0xc6, 0x77, 0x67, 0x6a, 0x42, 0xff, 0x96, 0x8e,
229 0x3c, 0xf8, 0xdc, 0x73, 0xc8, 0xf3, 0x3a, 0x8d, 0x19, 0x81, 0x29, 0x7b, 0xfa, 0x3e, 0x00,
230 0x30, 0xba,
231 ];
232
233 #[test]
234 fn test_derive_solana_address_from_der_with_spki_format() {
235 let spki_der = create_ed25519_spki_der(&TEST_ED25519_PUBLIC_KEY);
236 let result = derive_solana_address_from_der(&spki_der);
237 assert!(result.is_ok());
238
239 let address = result.unwrap();
240 assert_eq!(address, "BavUBpkD77FABnevMkBVqV8BDHv7gX8sSoYYJY9WU9L5");
241 }
242
243 #[test]
244 fn test_derive_solana_address_from_der_with_raw_key() {
245 let result = derive_solana_address_from_der(&TEST_ED25519_PUBLIC_KEY);
246 assert!(result.is_ok());
247
248 let address = result.unwrap();
249 assert_eq!(address, "BavUBpkD77FABnevMkBVqV8BDHv7gX8sSoYYJY9WU9L5");
250 }
251
252 #[test]
253 fn test_derive_solana_address_from_der_with_invalid_length() {
254 let invalid_der = vec![0u8; 10];
255 let result = derive_solana_address_from_der(&invalid_der);
256 assert!(result.is_err());
257 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
258 }
259
260 #[test]
261 fn test_derive_stellar_address_from_der_with_spki_format() {
262 let spki_der = create_ed25519_spki_der(&TEST_ED25519_PUBLIC_KEY);
263 let result = derive_stellar_address_from_der(&spki_der);
264 assert!(result.is_ok());
265
266 let address = result.unwrap();
267 assert!(address.starts_with('G'));
268 assert_eq!(address.len(), 56);
269 }
270
271 #[test]
272 fn test_derive_stellar_address_from_der_with_raw_key() {
273 let result = derive_stellar_address_from_der(&TEST_ED25519_PUBLIC_KEY);
274 assert!(result.is_ok());
275
276 let address = result.unwrap();
277 assert!(address.starts_with('G'));
278 assert_eq!(address.len(), 56);
279 }
280
281 #[test]
282 fn test_derive_stellar_address_from_der_with_invalid_length() {
283 let invalid_der = vec![0u8; 50];
284 let result = derive_stellar_address_from_der(&invalid_der);
285 assert!(result.is_err());
286 assert!(matches!(result, Err(AddressDerivationError::ParseError(_))));
287 }
288
289 #[test]
290 fn test_der_and_pem_produce_same_addresses() {
291 let pem = pem::parse(VALID_ED25519_PEM).unwrap();
293 let der = pem.contents();
294
295 let solana_from_pem = derive_solana_address_from_pem(VALID_ED25519_PEM).unwrap();
296 let solana_from_der = derive_solana_address_from_der(der).unwrap();
297 assert_eq!(solana_from_pem, solana_from_der);
298
299 let stellar_from_pem = derive_stellar_address_from_pem(VALID_ED25519_PEM).unwrap();
300 let stellar_from_der = derive_stellar_address_from_der(der).unwrap();
301 assert_eq!(stellar_from_pem, stellar_from_der);
302 }
303}