xrpl_mithril_wallet/
algorithm.rs1#[cfg(not(any(feature = "pure-rust-crypto", feature = "native-crypto")))]
10compile_error!(
11 "xrpl-wallet: enable either `pure-rust-crypto` (default) or `native-crypto` feature \
12 to provide a secp256k1 backend"
13);
14
15use crate::error::WalletError;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40#[non_exhaustive]
41pub enum Algorithm {
42 Secp256k1,
44 Ed25519,
46}
47
48impl core::fmt::Display for Algorithm {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 match self {
51 Self::Secp256k1 => write!(f, "secp256k1"),
52 Self::Ed25519 => write!(f, "ed25519"),
53 }
54 }
55}
56
57#[cfg(all(feature = "pure-rust-crypto", not(feature = "native-crypto")))]
62pub(crate) mod secp256k1_impl {
63 use super::*;
64 use k256::ecdsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
65 use k256::ecdsa::signature::SignatureEncoding;
66 use k256::ecdsa::{SigningKey, VerifyingKey};
67 use k256::elliptic_curve::ops::Reduce;
68 use k256::{Scalar, U256};
69
70 pub(crate) fn is_valid_secret(bytes: &[u8; 32]) -> bool {
72 SigningKey::from_slice(bytes).is_ok()
73 }
74
75 pub(crate) fn public_key(secret: &[u8; 32]) -> Result<[u8; 33], WalletError> {
77 let sk = SigningKey::from_slice(secret)
78 .map_err(|e| WalletError::InvalidSecretKey(e.to_string()))?;
79 let vk = sk.verifying_key();
80 let point = vk.to_encoded_point(true);
81 let mut pubkey = [0u8; 33];
82 pubkey.copy_from_slice(point.as_bytes());
83 Ok(pubkey)
84 }
85
86 pub(crate) fn scalar_add(a: &[u8; 32], b: &[u8; 32]) -> Result<[u8; 32], WalletError> {
90 let sa = <Scalar as Reduce<U256>>::reduce_bytes(&(*a).into());
91 let sb = <Scalar as Reduce<U256>>::reduce_bytes(&(*b).into());
92 let sum = sa + sb;
93 if sum.is_zero().into() {
94 return Err(WalletError::KeyDerivationFailed(
95 "scalar addition produced zero".into(),
96 ));
97 }
98 Ok(sum.to_bytes().into())
99 }
100
101 pub(crate) fn sign(secret: &[u8; 32], hash: &[u8; 32]) -> Result<Vec<u8>, WalletError> {
106 let sk = SigningKey::from_slice(secret)
107 .map_err(|e| WalletError::InvalidSecretKey(e.to_string()))?;
108 let sig: k256::ecdsa::Signature = sk
109 .sign_prehash(hash)
110 .map_err(|e| WalletError::SigningFailed(e.to_string()))?;
111 Ok(sig.to_der().to_vec())
112 }
113
114 pub(crate) fn verify(
116 pubkey: &[u8],
117 hash: &[u8; 32],
118 signature: &[u8],
119 ) -> Result<(), WalletError> {
120 let vk = VerifyingKey::from_sec1_bytes(pubkey)
121 .map_err(|e| WalletError::InvalidPublicKey(e.to_string()))?;
122 let sig = k256::ecdsa::Signature::from_der(signature)
123 .map_err(|e| WalletError::InvalidSignature(e.to_string()))?;
124 vk.verify_prehash(hash, &sig)
125 .map_err(|e| WalletError::VerificationFailed(e.to_string()))
126 }
127}
128
129#[cfg(feature = "native-crypto")]
134pub(crate) mod secp256k1_impl {
135 use super::*;
136 use secp256k1::{Message, PublicKey, Secp256k1, SecretKey};
137
138 pub(crate) fn is_valid_secret(bytes: &[u8; 32]) -> bool {
140 SecretKey::from_byte_array(*bytes).is_ok()
141 }
142
143 pub(crate) fn public_key(secret: &[u8; 32]) -> Result<[u8; 33], WalletError> {
145 let secp = Secp256k1::new();
146 let sk = SecretKey::from_byte_array(*secret)
147 .map_err(|e| WalletError::InvalidSecretKey(e.to_string()))?;
148 let pk = PublicKey::from_secret_key(&secp, &sk);
149 Ok(pk.serialize())
150 }
151
152 pub(crate) fn scalar_add(a: &[u8; 32], b: &[u8; 32]) -> Result<[u8; 32], WalletError> {
154 let mut sk = SecretKey::from_byte_array(*a)
155 .map_err(|e| WalletError::KeyDerivationFailed(e.to_string()))?;
156 let tweak = secp256k1::Scalar::from_be_bytes(*b)
157 .map_err(|e| WalletError::KeyDerivationFailed(e.to_string()))?;
158 sk = sk
159 .add_tweak(&tweak)
160 .map_err(|e| WalletError::KeyDerivationFailed(e.to_string()))?;
161 Ok(sk.secret_bytes())
162 }
163
164 pub(crate) fn sign(secret: &[u8; 32], hash: &[u8; 32]) -> Result<Vec<u8>, WalletError> {
166 let secp = Secp256k1::new();
167 let sk = SecretKey::from_byte_array(*secret)
168 .map_err(|e| WalletError::InvalidSecretKey(e.to_string()))?;
169 let msg = Message::from_digest(*hash);
170 let sig = secp.sign_ecdsa(msg, &sk);
171 Ok(sig.serialize_der().to_vec())
172 }
173
174 pub(crate) fn verify(
176 pubkey: &[u8],
177 hash: &[u8; 32],
178 signature: &[u8],
179 ) -> Result<(), WalletError> {
180 let secp = Secp256k1::new();
181 let pk = PublicKey::from_slice(pubkey)
182 .map_err(|e| WalletError::InvalidPublicKey(e.to_string()))?;
183 let msg = Message::from_digest(*hash);
184 let sig = secp256k1::ecdsa::Signature::from_der(signature)
185 .map_err(|e| WalletError::InvalidSignature(e.to_string()))?;
186 secp.verify_ecdsa(msg, &sig, &pk)
187 .map_err(|e| WalletError::VerificationFailed(e.to_string()))
188 }
189}
190
191pub(crate) use secp256k1_impl as secp256k1_ops;
193
194pub(crate) mod ed25519_ops {
199 use super::*;
200 use ed25519_dalek::{Signer, Verifier};
201
202 pub(crate) fn public_key(secret: &[u8; 32]) -> [u8; 32] {
204 let sk = ed25519_dalek::SigningKey::from_bytes(secret);
205 sk.verifying_key().to_bytes()
206 }
207
208 pub(crate) fn sign(secret: &[u8; 32], message: &[u8]) -> Vec<u8> {
214 let sk = ed25519_dalek::SigningKey::from_bytes(secret);
215 let sig = sk.sign(message);
216 sig.to_bytes().to_vec()
217 }
218
219 pub(crate) fn verify(
221 pubkey: &[u8; 32],
222 message: &[u8],
223 signature: &[u8],
224 ) -> Result<(), WalletError> {
225 let vk = ed25519_dalek::VerifyingKey::from_bytes(pubkey)
226 .map_err(|e| WalletError::InvalidPublicKey(e.to_string()))?;
227 let sig = ed25519_dalek::Signature::from_slice(signature)
228 .map_err(|e| WalletError::InvalidSignature(e.to_string()))?;
229 vk.verify(message, &sig)
230 .map_err(|e| WalletError::VerificationFailed(e.to_string()))
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn secp256k1_public_key_is_33_bytes() {
240 let secret = [1u8; 32];
242 let pubkey = secp256k1_ops::public_key(&secret).expect("valid secret");
243 assert_eq!(pubkey.len(), 33);
244 assert!(pubkey[0] == 0x02 || pubkey[0] == 0x03);
246 }
247
248 #[test]
249 fn secp256k1_sign_verify_round_trip() {
250 let secret = [42u8; 32];
251 let hash = [0xABu8; 32];
252
253 let pubkey = secp256k1_ops::public_key(&secret).expect("valid secret");
254 let signature = secp256k1_ops::sign(&secret, &hash).expect("sign");
255 secp256k1_ops::verify(&pubkey, &hash, &signature).expect("verify should succeed");
256 }
257
258 #[test]
259 fn secp256k1_verify_wrong_hash_fails() {
260 let secret = [42u8; 32];
261 let hash = [0xABu8; 32];
262 let wrong_hash = [0xCDu8; 32];
263
264 let pubkey = secp256k1_ops::public_key(&secret).expect("valid secret");
265 let signature = secp256k1_ops::sign(&secret, &hash).expect("sign");
266 assert!(secp256k1_ops::verify(&pubkey, &wrong_hash, &signature).is_err());
267 }
268
269 #[test]
270 fn secp256k1_scalar_add_non_zero() {
271 let a = [1u8; 32];
272 let b = [2u8; 32];
273 let sum = secp256k1_ops::scalar_add(&a, &b).expect("scalar add");
274 assert_ne!(sum, a);
276 assert_ne!(sum, b);
277 }
278
279 #[test]
280 fn secp256k1_zero_secret_rejected() {
281 let zero = [0u8; 32];
282 assert!(!secp256k1_ops::is_valid_secret(&zero));
283 }
284
285 #[test]
286 fn ed25519_public_key_is_32_bytes() {
287 let secret = [1u8; 32];
288 let pubkey = ed25519_ops::public_key(&secret);
289 assert_eq!(pubkey.len(), 32);
290 }
291
292 #[test]
293 fn ed25519_sign_verify_round_trip() {
294 let secret = [42u8; 32];
295 let message = b"test message for Ed25519 signing";
296
297 let pubkey = ed25519_ops::public_key(&secret);
298 let signature = ed25519_ops::sign(&secret, message);
299 assert_eq!(signature.len(), 64);
300 ed25519_ops::verify(&pubkey, message, &signature).expect("verify should succeed");
301 }
302
303 #[test]
304 fn ed25519_verify_wrong_message_fails() {
305 let secret = [42u8; 32];
306 let message = b"correct message";
307 let wrong_message = b"wrong message";
308
309 let pubkey = ed25519_ops::public_key(&secret);
310 let signature = ed25519_ops::sign(&secret, message);
311 assert!(ed25519_ops::verify(&pubkey, wrong_message, &signature).is_err());
312 }
313
314 #[test]
315 fn algorithm_display() {
316 assert_eq!(format!("{}", Algorithm::Secp256k1), "secp256k1");
317 assert_eq!(format!("{}", Algorithm::Ed25519), "ed25519");
318 }
319}