ripple_address_codec/
lib.rs1#![deny(
10 warnings,
11 clippy::all,
12 missing_debug_implementations,
13 missing_copy_implementations,
14 missing_docs,
15 missing_crate_level_docs,
16 missing_doc_code_examples,
17 non_ascii_idents,
18 unreachable_pub
19)]
20#![doc(test(attr(deny(warnings))))]
21#![doc(html_root_url = "https://docs.rs/ripple-address-codec/0.1.1")]
22
23use std::{convert::TryInto, result};
24
25use base_x;
26use ring::digest::{digest, SHA256};
27
28mod error;
29
30pub use self::error::{Error, Error::DecodeError};
31pub use self::Algorithm::{Ed25519, Secp256k1};
32
33const ALPHABET: &str = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
34const CHECKSUM_LENGTH: usize = 4;
35const ENTROPY_LEN: usize = 16;
36
37pub type Entropy = [u8; ENTROPY_LEN];
41
42pub type Result<T> = result::Result<T, Error>;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub enum Algorithm {
49 Secp256k1,
51 Ed25519,
53}
54
55impl Default for Algorithm {
56 fn default() -> Self {
57 Self::Secp256k1
58 }
59}
60
61pub fn encode_seed(entropy: &Entropy, algorithm: &Algorithm) -> String {
80 let prefix = match algorithm {
81 Secp256k1 => SeedSecP256K1.prefix(),
82 Ed25519 => SeedEd25519.prefix(),
83 };
84
85 encode_bytes_with_prefix(prefix, entropy)
86}
87
88pub fn decode_seed(seed: &str) -> Result<(Entropy, &'static Algorithm)> {
103 decode_seed_secp256k1(seed).or(decode_seed_ed25519(seed))
104}
105
106pub fn encode_account_id(bytes: &[u8; Address::PAYLOAD_LEN]) -> String {
116 encode_bytes_with_prefix(Address.prefix(), bytes)
117}
118
119pub fn decode_account_id(account_id: &str) -> Result<[u8; Address::PAYLOAD_LEN]> {
133 let decoded_bytes = decode_with_xrp_alphabet(account_id)?;
134
135 let payload = get_payload(decoded_bytes, Address)?;
136
137 Ok(payload.try_into().unwrap())
138}
139
140trait Settings {
141 const PAYLOAD_LEN: usize;
142 const PREFIX: &'static [u8] = &[];
143
144 fn prefix(&self) -> &'static [u8] {
145 Self::PREFIX
146 }
147
148 fn prefix_len(&self) -> usize {
149 Self::PREFIX.len()
150 }
151
152 fn payload_len(&self) -> usize {
153 Self::PAYLOAD_LEN
154 }
155}
156
157struct Address;
158
159impl Settings for Address {
160 const PREFIX: &'static [u8] = &[0x00];
161 const PAYLOAD_LEN: usize = 20;
162}
163
164struct SeedSecP256K1;
165
166impl SeedSecP256K1 {
167 const ALG: Algorithm = Secp256k1;
168}
169
170impl Settings for SeedSecP256K1 {
171 const PREFIX: &'static [u8] = &[0x21];
172 const PAYLOAD_LEN: usize = ENTROPY_LEN;
173}
174
175struct SeedEd25519;
176
177impl SeedEd25519 {
178 const ALG: Algorithm = Ed25519;
179}
180
181impl Settings for SeedEd25519 {
182 const PREFIX: &'static [u8] = &[0x01, 0xE1, 0x4B];
183 const PAYLOAD_LEN: usize = ENTROPY_LEN;
184}
185
186fn decode_seed_secp256k1(s: &str) -> Result<(Entropy, &'static Algorithm)> {
187 let decoded_bytes = decode_with_xrp_alphabet(s)?;
188
189 let payload = get_payload(decoded_bytes, SeedSecP256K1)?;
190
191 Ok((payload.try_into().unwrap(), &SeedSecP256K1::ALG))
192}
193
194fn decode_seed_ed25519(s: &str) -> Result<(Entropy, &'static Algorithm)> {
195 let decoded_bytes = decode_with_xrp_alphabet(s)?;
196
197 let payload = get_payload(decoded_bytes, SeedEd25519)?;
198
199 Ok((payload.try_into().unwrap(), &SeedEd25519::ALG))
200}
201
202fn encode_bytes_with_prefix(prefix: &[u8], bytes: &[u8]) -> String {
203 encode_bytes(&[prefix, bytes].concat())
204}
205
206fn encode_bytes(bytes: &[u8]) -> String {
207 let checked_bytes = [bytes, &calc_checksum(bytes)].concat();
208 base_x::encode(ALPHABET, &checked_bytes)
209}
210
211fn decode_with_xrp_alphabet(s: &str) -> Result<Vec<u8>> {
212 Ok(base_x::decode(ALPHABET, s)?)
213}
214
215fn get_payload(bytes: Vec<u8>, settings: impl Settings) -> Result<Vec<u8>> {
216 verify_payload_len(&bytes, settings.prefix_len(), settings.payload_len())?;
217 verify_prefix(settings.prefix(), &bytes)?;
218 let checked_bytes = get_checked_bytes(bytes)?;
219
220 Ok(checked_bytes[settings.prefix_len()..].try_into().unwrap())
221}
222
223fn verify_prefix(prefix: &[u8], bytes: &[u8]) -> Result<()> {
224 if bytes.starts_with(prefix) {
225 return Ok(());
226 }
227
228 Err(DecodeError)
229}
230
231fn verify_payload_len(bytes: &[u8], prefix_len: usize, expected_len: usize) -> Result<()> {
232 if bytes[prefix_len..bytes.len() - CHECKSUM_LENGTH].len() == expected_len {
233 return Ok(());
234 }
235
236 Err(DecodeError)
237}
238
239fn get_checked_bytes(mut bytes_with_checksum: Vec<u8>) -> Result<Vec<u8>> {
240 verify_checksum_lenght(&bytes_with_checksum)?;
241
242 let checksum = bytes_with_checksum.split_off(bytes_with_checksum.len() - CHECKSUM_LENGTH);
244 let bytes = bytes_with_checksum;
245
246 verify_checksum(&bytes, &checksum)?;
247
248 Ok(bytes)
249}
250
251fn verify_checksum(input: &[u8], checksum: &[u8]) -> Result<()> {
252 if calc_checksum(input) == checksum {
253 Ok(())
254 } else {
255 Err(DecodeError)
256 }
257}
258
259fn verify_checksum_lenght(bytes: &[u8]) -> Result<()> {
260 let len = bytes.len();
261
262 if len < CHECKSUM_LENGTH + 1 {
263 return Err(DecodeError);
264 }
265
266 Ok(())
267}
268
269fn calc_checksum(bytes: &[u8]) -> [u8; CHECKSUM_LENGTH] {
270 sha256_digest(&sha256_digest(bytes))[..CHECKSUM_LENGTH]
271 .try_into()
272 .unwrap()
273}
274
275fn sha256_digest(data: &[u8]) -> Vec<u8> {
276 digest(&SHA256, data).as_ref().to_vec()
277}