1use codec::Encode;
7
8use crate::crypto::{DeriveJunction, SecretUri, seed_from_entropy};
9use core::str::FromStr;
10use hex::FromHex;
11use secp256k1::{Message, Secp256k1, SecretKey, ecdsa::RecoverableSignature};
12use secrecy::ExposeSecret;
13
14use thiserror::Error as DeriveError;
15
16const SECRET_KEY_LENGTH: usize = 32;
17
18pub type SecretKeyBytes = [u8; SECRET_KEY_LENGTH];
20
21#[derive(Clone, Copy, PartialEq, Eq)]
24pub struct Signature(pub [u8; 65]);
25
26impl AsRef<[u8]> for Signature {
27 fn as_ref(&self) -> &[u8] {
28 &self.0
29 }
30}
31
32#[derive(Debug, Clone)]
34pub struct PublicKey(pub [u8; 33]);
35
36impl AsRef<[u8]> for PublicKey {
37 fn as_ref(&self) -> &[u8] {
38 &self.0
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct Keypair(pub secp256k1::Keypair);
45
46impl Keypair {
47 pub fn from_uri(uri: &SecretUri) -> Result<Self, Error> {
61 let SecretUri {
62 junctions,
63 phrase,
64 password,
65 } = uri;
66
67 let key = if let Some(hex_str) = phrase.expose_secret().strip_prefix("0x") {
71 let seed = SecretKeyBytes::from_hex(hex_str)?;
72 Self::from_secret_key(seed)?
73 } else {
74 let phrase = bip39::Mnemonic::from_str(phrase.expose_secret())?;
75 let pass_str = password.as_ref().map(|p| p.expose_secret());
76 Self::from_phrase(&phrase, pass_str)?
77 };
78
79 key.derive(junctions.iter().copied())
81 }
82
83 pub fn from_phrase(mnemonic: &bip39::Mnemonic, password: Option<&str>) -> Result<Self, Error> {
97 let (arr, len) = mnemonic.to_entropy_array();
98 let big_seed =
99 seed_from_entropy(&arr[0..len], password.unwrap_or("")).ok_or(Error::InvalidSeed)?;
100
101 let secret_key_bytes: SecretKeyBytes = big_seed[..SECRET_KEY_LENGTH]
102 .try_into()
103 .expect("should be valid Seed");
104
105 Self::from_secret_key(secret_key_bytes)
106 }
107
108 pub fn from_secret_key(secret_key: SecretKeyBytes) -> Result<Self, Error> {
114 let secret = SecretKey::from_slice(&secret_key).map_err(|_| Error::InvalidSeed)?;
115 Ok(Self(secp256k1::Keypair::from_secret_key(
116 &Secp256k1::signing_only(),
117 &secret,
118 )))
119 }
120
121 pub fn derive<Js: IntoIterator<Item = DeriveJunction>>(
139 &self,
140 junctions: Js,
141 ) -> Result<Self, Error> {
142 let mut acc = self.0.secret_key().clone().secret_bytes();
143 for junction in junctions {
144 match junction {
145 DeriveJunction::Soft(_) => return Err(Error::SoftJunction),
146 DeriveJunction::Hard(junction_bytes) => {
147 acc = ("Secp256k1HDKD", acc, junction_bytes)
148 .using_encoded(sp_crypto_hashing::blake2_256)
149 }
150 }
151 }
152 Self::from_secret_key(acc)
153 }
154
155 pub fn public_key(&self) -> PublicKey {
159 PublicKey(self.0.public_key().serialize())
160 }
161
162 pub fn secret_key(&self) -> SecretKeyBytes {
164 *self.0.secret_key().as_ref()
165 }
166
167 pub fn sign(&self, message: &[u8]) -> Signature {
169 self.sign_prehashed(&sp_crypto_hashing::blake2_256(message))
170 }
171
172 pub fn sign_prehashed(&self, message_hash: &[u8; 32]) -> Signature {
174 let wrapped = Message::from_digest_slice(message_hash).expect("Message is 32 bytes; qed");
175 Signature(internal::sign(&self.0.secret_key(), &wrapped))
176 }
177}
178
179pub fn verify<M: AsRef<[u8]>>(sig: &Signature, message: M, pubkey: &PublicKey) -> bool {
192 let message_hash = sp_crypto_hashing::blake2_256(message.as_ref());
193 let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed");
194
195 internal::verify(&sig.0, &wrapped, pubkey)
196}
197
198pub(crate) mod internal {
199 use super::*;
200
201 pub fn sign(secret_key: &secp256k1::SecretKey, message: &Message) -> [u8; 65] {
202 let recsig: RecoverableSignature =
203 Secp256k1::signing_only().sign_ecdsa_recoverable(message, secret_key);
204 let (recid, sig): (_, [u8; 64]) = recsig.serialize_compact();
205 let mut signature_bytes: [u8; 65] = [0; 65];
206 signature_bytes[..64].copy_from_slice(&sig);
207 signature_bytes[64] = (i32::from(recid) & 0xFF) as u8;
208 signature_bytes
209 }
210
211 pub fn verify(sig: &[u8; 65], message: &Message, pubkey: &PublicKey) -> bool {
212 let Ok(signature) = secp256k1::ecdsa::Signature::from_compact(&sig[..64]) else {
213 return false;
214 };
215 let Ok(public) = secp256k1::PublicKey::from_slice(&pubkey.0) else {
216 return false;
217 };
218
219 Secp256k1::verification_only()
220 .verify_ecdsa(message, &signature, &public)
221 .is_ok()
222 }
223}
224
225#[derive(Debug, PartialEq, DeriveError)]
227pub enum Error {
228 #[error("Invalid seed (was it the wrong length?)")]
230 InvalidSeed,
231 #[error("Invalid seed for ECDSA, contained soft junction")]
233 SoftJunction,
234 #[error("Cannot parse phrase: {0}")]
236 Phrase(bip39::Error),
237 #[error("Cannot parse hex string: {0}")]
239 Hex(hex::FromHexError),
240}
241
242impl From<hex::FromHexError> for Error {
243 fn from(err: hex::FromHexError) -> Self {
244 Error::Hex(err)
245 }
246}
247
248impl From<bip39::Error> for Error {
249 fn from(err: bip39::Error) -> Self {
250 Error::Phrase(err)
251 }
252}
253
254pub mod dev {
257 use super::*;
258
259 once_static_cloned! {
260 pub fn alice() -> Keypair {
262 Keypair::from_uri(&SecretUri::from_str("//Alice").unwrap()).unwrap()
263 }
264 pub fn bob() -> Keypair {
266 Keypair::from_uri(&SecretUri::from_str("//Bob").unwrap()).unwrap()
267 }
268 pub fn charlie() -> Keypair {
270 Keypair::from_uri(&SecretUri::from_str("//Charlie").unwrap()).unwrap()
271 }
272 pub fn dave() -> Keypair {
274 Keypair::from_uri(&SecretUri::from_str("//Dave").unwrap()).unwrap()
275 }
276 pub fn eve() -> Keypair {
278 Keypair::from_uri(&SecretUri::from_str("//Eve").unwrap()).unwrap()
279 }
280 pub fn ferdie() -> Keypair {
282 Keypair::from_uri(&SecretUri::from_str("//Ferdie").unwrap()).unwrap()
283 }
284 pub fn one() -> Keypair {
286 Keypair::from_uri(&SecretUri::from_str("//One").unwrap()).unwrap()
287 }
288 pub fn two() -> Keypair {
290 Keypair::from_uri(&SecretUri::from_str("//Two").unwrap()).unwrap()
291 }
292 }
293}
294
295#[cfg(feature = "subxt")]
298mod subxt_compat {
299 use super::*;
300 use subxt::config::Config;
301 use subxt::transactions::Signer as SignerT;
302 use subxt::utils::{AccountId32, MultiAddress, MultiSignature};
303
304 impl From<Signature> for MultiSignature {
305 fn from(value: Signature) -> Self {
306 MultiSignature::Ecdsa(value.0)
307 }
308 }
309
310 impl From<PublicKey> for AccountId32 {
311 fn from(value: PublicKey) -> Self {
312 value.to_account_id()
313 }
314 }
315
316 impl<T> From<PublicKey> for MultiAddress<AccountId32, T> {
317 fn from(value: PublicKey) -> Self {
318 value.to_address()
319 }
320 }
321
322 impl PublicKey {
323 pub fn to_account_id(self) -> AccountId32 {
327 AccountId32(sp_crypto_hashing::blake2_256(&self.0))
328 }
329 pub fn to_address<T>(self) -> MultiAddress<AccountId32, T> {
333 MultiAddress::Id(self.to_account_id())
334 }
335 }
336
337 impl<T: Config> SignerT<T> for Keypair
338 where
339 T::AccountId: From<PublicKey>,
340 T::Address: From<PublicKey>,
341 T::Signature: From<Signature>,
342 {
343 fn account_id(&self) -> T::AccountId {
344 self.public_key().into()
345 }
346
347 fn sign(&self, signer_payload: &[u8]) -> T::Signature {
348 self.sign(signer_payload).into()
349 }
350 }
351}
352
353#[cfg(test)]
354mod test {
355 use std::str::FromStr;
356
357 use super::*;
358
359 use sp_core::{self, crypto::Pair as _, ecdsa::Pair as SpPair};
360
361 #[test]
362 fn check_from_phrase_matches() {
363 for _ in 0..20 {
364 let (sp_pair, phrase, _seed) = SpPair::generate_with_phrase(None);
365 let phrase = bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
366 let pair = Keypair::from_phrase(&phrase, None).expect("should be valid");
367
368 assert_eq!(sp_pair.public().0, pair.public_key().0);
369 }
370 }
371
372 #[test]
373 fn check_from_phrase_with_password_matches() {
374 for _ in 0..20 {
375 let (sp_pair, phrase, _seed) = SpPair::generate_with_phrase(Some("Testing"));
376 let phrase = bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
377 let pair = Keypair::from_phrase(&phrase, Some("Testing")).expect("should be valid");
378
379 assert_eq!(sp_pair.public().0, pair.public_key().0);
380 }
381 }
382
383 #[test]
384 fn check_from_secret_uri_matches() {
385 let uri_paths = ["//bar", "//0001", "//1", "//0001", "//foo//bar//wibble"];
387
388 for i in 0..2 {
389 for path in &uri_paths {
390 let password = format!("Testing{i}");
392 let (_sp_pair, phrase, _seed) = SpPair::generate_with_phrase(Some(&password));
393 let uri = format!("{phrase}{path}///{password}");
394 let sp_pair = SpPair::from_string(&uri, None).expect("should be valid");
395
396 let uri = SecretUri::from_str(&uri).expect("should be valid secret URI");
398 let pair = Keypair::from_uri(&uri).expect("should be valid");
399
400 assert_eq!(sp_pair.public().0, pair.public_key().0);
402 }
403 }
404 }
405
406 #[test]
407 fn check_derive_errs_with_soft_junction() {
408 let uri_paths = ["/bar", "/1", "//foo//bar/wibble"];
409 for path in &uri_paths {
410 let (_sp_pair, phrase, _seed) = SpPair::generate_with_phrase(None);
411 let uri = format!("{phrase}{path}");
412 let uri = SecretUri::from_str(&uri).expect("should be valid secret URI");
413 let result = Keypair::from_uri(&uri);
414 assert_eq!(result.err(), Some(Error::SoftJunction));
415 }
416 }
417
418 #[test]
419 fn check_signing_and_verifying_matches() {
420 use sp_core::ecdsa::Signature as SpSignature;
421
422 for _ in 0..20 {
423 let (sp_pair, phrase, _seed) = SpPair::generate_with_phrase(Some("Testing"));
424 let phrase = bip39::Mnemonic::parse(phrase).expect("valid phrase expected");
425 let pair = Keypair::from_phrase(&phrase, Some("Testing")).expect("should be valid");
426
427 let message = b"Hello world";
428 let sp_sig = sp_pair.sign(message).0;
429 let sig: [u8; 65] = pair.sign(message).0;
430
431 assert!(SpPair::verify(
432 &SpSignature::from(sig),
433 message,
434 &sp_pair.public(),
435 ));
436 assert!(verify(&Signature(sp_sig), message, &pair.public_key()));
437 }
438 }
439
440 #[test]
441 fn check_hex_uris() {
442 let uri_str =
444 "0x1122334455667788112233445566778811223344556677881122334455667788///SomePassword";
445
446 let uri = SecretUri::from_str(uri_str).expect("should be valid");
447 let pair = Keypair::from_uri(&uri).expect("should be valid");
448 let sp_pair = SpPair::from_string(uri_str, None).expect("should be valid");
449
450 assert_eq!(pair.public_key().0, sp_pair.public().0);
451 }
452}