1use base64::{engine::general_purpose, Engine as _};
2use bip39::Mnemonic;
3use schnorrkel::{PublicKey, SecretKey};
4use scrypt::{scrypt, Params as ScryptParams};
5use serde::{Deserialize, Serialize};
6use sodiumoxide::crypto::sealedbox;
7use sodiumoxide::crypto::secretbox;
8use sodiumoxide::crypto::secretbox::{Key, Nonce};
9use sodiumoxide::crypto::sign::ed25519 as sign_ed25519;
10use sp_core::crypto::{AccountId32, Ss58Codec};
11use sp_core::{ed25519, sr25519, ByteArray, Pair};
12use std::fmt;
13
14use crate::constants::{CRYPTO_ED25519, CRYPTO_SR25519};
15
16const PKCS8_HEADER: &[u8] = &[48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32];
17const PKCS8_DIVIDER: &[u8] = &[161, 35, 3, 33, 0];
18const SEC_LENGTH: usize = 64;
19const PUB_LENGTH: usize = 32;
20
21#[derive(Clone)]
22#[allow(clippy::large_enum_variant)]
23pub(crate) enum PairInner {
24 Sr25519(sr25519::Pair),
25 Ed25519(ed25519::Pair),
26}
27
28impl PairInner {
29 pub fn public_bytes(&self) -> [u8; 32] {
30 match self {
31 Self::Sr25519(p) => p.public().0,
32 Self::Ed25519(p) => p.public().0,
33 }
34 }
35
36 pub fn ss58_address(&self) -> String {
37 AccountId32::from(self.public_bytes()).to_ss58check()
38 }
39
40 pub fn sign(&self, data: &[u8]) -> Vec<u8> {
41 match self {
42 Self::Sr25519(p) => p.sign(data).0.to_vec(),
43 Self::Ed25519(p) => p.sign(data).0.to_vec(),
44 }
45 }
46
47 pub fn to_raw_vec(&self) -> Vec<u8> {
48 match self {
49 Self::Sr25519(p) => p.to_raw_vec(),
50 Self::Ed25519(p) => p.to_raw_vec(),
51 }
52 }
53
54 pub fn crypto_type(&self) -> u8 {
55 match self {
56 Self::Sr25519(_) => CRYPTO_SR25519,
57 Self::Ed25519(_) => CRYPTO_ED25519,
58 }
59 }
60}
61
62fn verify_signature(
63 crypto_type: u8,
64 public_key_bytes: &[u8; 32],
65 data: &[u8],
66 signature_bytes: &[u8],
67) -> Result<bool, String> {
68 match crypto_type {
69 CRYPTO_SR25519 => {
70 let public = sr25519::Public::from_raw(*public_key_bytes);
71 let signature = sr25519::Signature::from_slice(signature_bytes)
72 .map_err(|_| "Invalid SR25519 signature.".to_string())?;
73 Ok(sr25519::Pair::verify(&signature, data, &public))
74 }
75 CRYPTO_ED25519 => {
76 let public = ed25519::Public::from_raw(*public_key_bytes);
77 let signature = ed25519::Signature::from_slice(signature_bytes)
78 .map_err(|_| "Invalid ED25519 signature.".to_string())?;
79 Ok(ed25519::Pair::verify(&signature, data, &public))
80 }
81 _ => Err(format!("Unsupported crypto type: {}.", crypto_type)),
82 }
83}
84
85#[derive(Serialize, Deserialize, Debug)]
86struct Encoding {
87 content: Vec<String>,
88 #[serde(rename = "type")]
89 enc_type: Vec<String>,
90 version: String,
91}
92
93#[derive(Serialize, Deserialize, Debug)]
94struct Meta {
95 #[serde(rename = "genesisHash")]
96 genesis_hash: Option<String>,
97 name: String,
98 #[serde(rename = "whenCreated")]
99 when_created: Option<u64>,
100}
101
102#[derive(Serialize, Deserialize, Debug)]
103struct JsonStructure {
104 encoded: String,
105 encoding: Encoding,
106 address: String,
107 meta: Meta,
108}
109
110#[derive(Clone)]
111pub struct Keypair {
112 ss58_address: Option<String>,
113 public_key: Option<String>,
114 private_key: Option<String>,
115 ss58_format: u8,
116 seed_hex: Option<Vec<u8>>,
117 crypto_type: u8,
118 mnemonic: Option<String>,
119 pair: Option<PairInner>,
120}
121
122impl Keypair {
123 pub fn pair_is_some(&self) -> bool {
125 self.pair.is_some()
126 }
127
128 #[allow(dead_code)]
130 pub(crate) fn set_crypto_type_field(&mut self, crypto_type: u8) {
131 self.crypto_type = crypto_type;
132 }
133
134 pub fn new(
148 ss58_address: Option<String>,
149 public_key: Option<String>,
150 private_key: Option<String>,
151 ss58_format: u8,
152 seed_hex: Option<Vec<u8>>,
153 crypto_type: u8,
154 ) -> Result<Self, String> {
155 match crypto_type {
156 CRYPTO_SR25519 | CRYPTO_ED25519 => {}
157 _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
158 }
159
160 let mut ss58_address_res = ss58_address.clone();
161 let mut public_key_res = public_key;
162
163 if let Some(private_key_str) = &private_key {
164 let private_key_bytes =
165 hex::decode(private_key_str.trim_start_matches("0x")).expect("");
166
167 let expected_len = match crypto_type {
168 CRYPTO_SR25519 => 64,
169 CRYPTO_ED25519 => 32,
170 _ => unreachable!(),
171 };
172 if private_key_bytes.len() != expected_len {
173 return Err(format!("Secret key should be {} bytes long.", expected_len));
174 }
175 }
176
177 if let Some(public_key_str) = &public_key_res {
178 let public_key_vec = hex::decode(public_key_str.trim_start_matches("0x"))
179 .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
180
181 let public_key_array: [u8; 32] = public_key_vec
182 .try_into()
183 .map_err(|_| "Public key must be 32 bytes long.")?;
184
185 let account_id = AccountId32::from(public_key_array);
186 ss58_address_res = Some(account_id.to_ss58check());
187 }
188
189 if let Some(ss58_address_str) = ss58_address.clone() {
190 let (account_id, _) = AccountId32::from_ss58check_with_version(&ss58_address_str)
191 .map_err(|e| format!("Invalid SS58 address: {:?}", e))?;
192 public_key_res = Some(hex::encode(<AccountId32 as AsRef<[u8; 32]>>::as_ref(
193 &account_id,
194 )));
195 }
196
197 let kp = Keypair {
198 ss58_address: ss58_address_res,
199 public_key: public_key_res,
200 private_key,
201 ss58_format,
202 seed_hex,
203 crypto_type,
204 mnemonic: None,
205 pair: None,
206 };
207
208 if kp.public_key.is_none() {
209 return Err("No SS58 formatted address or public key provided.".to_string());
210 }
211 Ok(kp)
212 }
213
214 fn __str__(&self) -> Result<String, String> {
215 match self.ss58_address() {
216 Some(address) => Ok(format!("<Keypair (address={})>", address)),
217 None => Ok("<Keypair (address=None)>".to_string()),
218 }
219 }
220
221 fn __repr__(&self) -> Result<String, String> {
222 self.__str__()
223 }
224
225 pub fn generate_mnemonic(n_words: usize) -> Result<String, String> {
234 let mnemonic = Mnemonic::generate(n_words).map_err(|e| e.to_string())?;
235 Ok(mnemonic.to_string())
236 }
237
238 pub fn create_from_mnemonic(mnemonic: &str, crypto_type: u8) -> Result<Self, String> {
248 let (pair_inner, seed_vec) = match crypto_type {
249 CRYPTO_SR25519 => {
250 let (pair, seed) =
251 sr25519::Pair::from_phrase(mnemonic, None).map_err(|e| e.to_string())?;
252 (PairInner::Sr25519(pair), seed.to_vec())
253 }
254 CRYPTO_ED25519 => {
255 let (pair, seed) =
256 ed25519::Pair::from_phrase(mnemonic, None).map_err(|e| e.to_string())?;
257 (PairInner::Ed25519(pair), seed.to_vec())
258 }
259 _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
260 };
261
262 Ok(Keypair {
263 mnemonic: Some(mnemonic.to_string()),
264 seed_hex: Some(seed_vec),
265 pair: Some(pair_inner),
266 crypto_type,
267 ..Default::default()
268 })
269 }
270
271 pub fn create_from_seed(seed: Vec<u8>, crypto_type: u8) -> Result<Self, String> {
281 let pair_inner = match crypto_type {
282 CRYPTO_SR25519 => {
283 let pair = sr25519::Pair::from_seed_slice(&seed)
284 .map_err(|e| format!("Failed to create SR25519 pair from seed: {}", e))?;
285 PairInner::Sr25519(pair)
286 }
287 CRYPTO_ED25519 => {
288 let pair = ed25519::Pair::from_seed_slice(&seed)
289 .map_err(|e| format!("Failed to create ED25519 pair from seed: {}", e))?;
290 PairInner::Ed25519(pair)
291 }
292 _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
293 };
294
295 Ok(Keypair {
296 seed_hex: Some(seed),
297 pair: Some(pair_inner),
298 crypto_type,
299 ..Default::default()
300 })
301 }
302
303 pub fn create_from_private_key(private_key: &str, crypto_type: u8) -> Result<Self, String> {
313 let private_key_vec = hex::decode(private_key.trim_start_matches("0x"))
314 .map_err(|e| format!("Invalid `private_key` string: {}", e))?;
315
316 let pair_inner = match crypto_type {
317 CRYPTO_SR25519 => {
318 let pair = sr25519::Pair::from_seed_slice(&private_key_vec).map_err(|e| {
319 format!("Failed to create SR25519 pair from private key: {}", e)
320 })?;
321 PairInner::Sr25519(pair)
322 }
323 CRYPTO_ED25519 => {
324 let pair = ed25519::Pair::from_seed_slice(&private_key_vec).map_err(|e| {
325 format!("Failed to create ED25519 pair from private key: {}", e)
326 })?;
327 PairInner::Ed25519(pair)
328 }
329 _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
330 };
331
332 Ok(Keypair {
333 pair: Some(pair_inner),
334 crypto_type,
335 ..Default::default()
336 })
337 }
338
339 pub fn create_from_encrypted_json(
349 json_data: &str,
350 passphrase: &str,
351 ) -> Result<Keypair, String> {
352 fn pad_right(mut data: Vec<u8>, total_len: usize, pad_byte: u8) -> Vec<u8> {
354 if data.len() < total_len {
355 let pad_len = total_len - data.len();
356 data.extend(vec![pad_byte; pad_len]);
357 }
358 data
359 }
360
361 pub fn pair_from_ed25519_secret_key(secret: &[u8], pubkey: &[u8]) -> ([u8; 64], [u8; 32]) {
375 match (
376 SecretKey::from_ed25519_bytes(secret),
377 PublicKey::from_bytes(pubkey),
378 ) {
379 (Ok(s), Ok(k)) => (s.to_bytes(), k.to_bytes()),
380 _ => panic!("Invalid secret or pubkey provided."),
381 }
382 }
383
384 fn decode_pkcs8(
387 ciphertext: &[u8],
388 ) -> Result<([u8; SEC_LENGTH], [u8; PUB_LENGTH]), &'static str> {
389 let mut current_offset = 0;
390 let header = &ciphertext[current_offset..current_offset + PKCS8_HEADER.len()];
391 if header != PKCS8_HEADER {
392 return Err("Invalid Pkcs8 header found in body");
393 }
394 current_offset += PKCS8_HEADER.len();
395 let secret_key = &ciphertext[current_offset..current_offset + SEC_LENGTH];
396 let mut secret_key_array = [0u8; SEC_LENGTH];
397 secret_key_array.copy_from_slice(secret_key);
398 current_offset += SEC_LENGTH;
399 let divider = &ciphertext[current_offset..current_offset + PKCS8_DIVIDER.len()];
400 if divider != PKCS8_DIVIDER {
401 return Err("Invalid Pkcs8 divider found in body");
402 }
403 current_offset += PKCS8_DIVIDER.len();
404 let public_key = &ciphertext[current_offset..current_offset + PUB_LENGTH];
405 let mut public_key_array = [0u8; PUB_LENGTH];
406 public_key_array.copy_from_slice(public_key);
407 Ok((secret_key_array, public_key_array))
408 }
409
410 let json_data: JsonStructure = serde_json::from_str(json_data).unwrap();
411
412 if json_data.encoding.version != "3" {
413 return Err("Unsupported JSON format".to_string());
414 }
415
416 let mut encrypted = general_purpose::STANDARD
417 .decode(json_data.encoded)
418 .map_err(|e| e.to_string())?;
419
420 let password = if json_data.encoding.enc_type.contains(&"scrypt".to_string()) {
421 let salt = &encrypted[0..32];
422 let n = u32::from_le_bytes(encrypted[32..36].try_into().unwrap());
423 let p = u32::from_le_bytes(encrypted[36..40].try_into().unwrap());
424 let r = u32::from_le_bytes(encrypted[40..44].try_into().unwrap());
425 let log_n: u8 = n.ilog2() as u8;
426
427 let params = ScryptParams::new(log_n, r, p, 32).map_err(|e| e.to_string())?;
428 let mut derived_key = vec![0u8; 32];
429 scrypt(passphrase.as_bytes(), salt, ¶ms, &mut derived_key)
430 .map_err(|e| e.to_string())?;
431 encrypted = encrypted[44..].to_vec();
432 derived_key
433 } else {
434 let mut derived_key = passphrase.as_bytes().to_vec();
435 derived_key = pad_right(derived_key, 32, 0x00);
436 derived_key
437 };
438
439 let nonce_bytes = &encrypted[0..24];
440 let nonce = Nonce::from_slice(nonce_bytes)
441 .ok_or("Invalid nonce length")
442 .map_err(|e| e.to_string())?;
443 let message = &encrypted[24..];
444
445 let key = Key::from_slice(&password).ok_or("Invalid key length")?;
446 let decrypted_data = secretbox::open(message, &nonce, &key)
447 .map_err(|_| "Failed to decrypt data".to_string())?;
448 let (private_key, public_key) =
449 decode_pkcs8(&decrypted_data).map_err(|_| "Failed to decode PKCS8 data".to_string())?;
450
451 let (secret, converted_public_key) =
452 pair_from_ed25519_secret_key(&private_key[..], &public_key[..]);
453
454 if json_data.encoding.content.iter().any(|c| c == "sr25519") {
455 assert_eq!(public_key, converted_public_key);
456 Keypair::create_from_private_key(&hex::encode(secret), CRYPTO_SR25519)
457 } else if json_data.encoding.content.iter().any(|c| c == "ed25519") {
458 let seed = &private_key[..32];
459 let pair = ed25519::Pair::from_seed_slice(seed)
460 .map_err(|e| format!("Failed to create ED25519 pair: {}", e))?;
461 if pair.public().0 != public_key {
462 return Err("ED25519 public key mismatch in JSON.".to_string());
463 }
464 Ok(Keypair {
465 pair: Some(PairInner::Ed25519(pair)),
466 crypto_type: CRYPTO_ED25519,
467 ..Default::default()
468 })
469 } else {
470 Err("Unsupported keypair type.".to_string())
471 }
472 }
473
474 pub fn create_from_uri(uri: &str, crypto_type: u8) -> Result<Self, String> {
484 let pair_inner = match crypto_type {
485 CRYPTO_SR25519 => {
486 let pair = sr25519::Pair::from_string(uri, None).map_err(|e| e.to_string())?;
487 PairInner::Sr25519(pair)
488 }
489 CRYPTO_ED25519 => {
490 let pair = ed25519::Pair::from_string(uri, None).map_err(|e| e.to_string())?;
491 PairInner::Ed25519(pair)
492 }
493 _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
494 };
495
496 Ok(Keypair {
497 pair: Some(pair_inner),
498 crypto_type,
499 ..Default::default()
500 })
501 }
502
503 pub fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>, String> {
512 let pair = self
513 .pair
514 .as_ref()
515 .ok_or_else(|| "No private key set to create signatures.".to_string())?;
516
517 Ok(pair.sign(&data))
518 }
519
520 pub fn verify(&self, data: Vec<u8>, signature: Vec<u8>) -> Result<bool, String> {
530 let public_key_bytes = self.public_key_bytes()?;
531 let ct = self.crypto_type();
532
533 let verified = verify_signature(ct, &public_key_bytes, &data, &signature)?;
534 if verified {
535 return Ok(true);
536 }
537
538 let wrapped_data = [b"<Bytes>", data.as_slice(), b"</Bytes>"].concat();
539 verify_signature(ct, &public_key_bytes, &wrapped_data, &signature)
540 }
541
542 pub fn encrypt(&self, message: &[u8]) -> Result<Vec<u8>, String> {
551 if self.crypto_type() != CRYPTO_ED25519 {
552 return Err("Encrypt/decrypt is only supported for ED25519 keypairs.".to_string());
553 }
554 let x25519_pk = self.ed25519_to_x25519_pk()?;
555 Ok(sealedbox::seal(message, &x25519_pk))
556 }
557
558 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, String> {
567 if self.crypto_type() != CRYPTO_ED25519 {
568 return Err("Encrypt/decrypt is only supported for ED25519 keypairs.".to_string());
569 }
570 let pair = self
571 .pair
572 .as_ref()
573 .ok_or_else(|| "Decryption requires a keypair with a private key.".to_string())?;
574 let (x25519_pk, x25519_sk) = Self::ed25519_x25519_keypair_from_pair(pair)?;
575 sealedbox::open(ciphertext, &x25519_pk, &x25519_sk)
576 .map_err(|_| "Decryption failed: invalid ciphertext or wrong key.".to_string())
577 }
578
579 fn ed25519_to_x25519_pk(&self) -> Result<sodiumoxide::crypto::box_::PublicKey, String> {
580 let pubkey_bytes = self.public_key_bytes()?;
581 let ed25519_pk = sign_ed25519::PublicKey::from_slice(&pubkey_bytes)
582 .ok_or_else(|| "No public key available for encryption.".to_string())?;
583 sign_ed25519::to_curve25519_pk(&ed25519_pk)
584 .map_err(|_| "Failed to convert ED25519 public key to X25519.".to_string())
585 }
586
587 fn ed25519_x25519_keypair_from_pair(
588 pair: &PairInner,
589 ) -> Result<
590 (
591 sodiumoxide::crypto::box_::PublicKey,
592 sodiumoxide::crypto::box_::SecretKey,
593 ),
594 String,
595 > {
596 let seed_bytes = pair.to_raw_vec();
597 let seed = sign_ed25519::Seed::from_slice(&seed_bytes)
598 .ok_or_else(|| "Failed to derive X25519 keypair for decryption.".to_string())?;
599 let (pk, sk) = sign_ed25519::keypair_from_seed(&seed);
600 let x25519_pk = sign_ed25519::to_curve25519_pk(&pk)
601 .map_err(|_| "Failed to derive X25519 keypair for decryption.".to_string())?;
602 let x25519_sk = sign_ed25519::to_curve25519_sk(&sk)
603 .map_err(|_| "Failed to derive X25519 keypair for decryption.".to_string())?;
604 Ok((x25519_pk, x25519_sk))
605 }
606
607 fn public_key_bytes(&self) -> Result<[u8; 32], String> {
608 if let Some(pair) = &self.pair {
609 Ok(pair.public_bytes())
610 } else if let Some(public_key_str) = &self.public_key {
611 let bytes = hex::decode(public_key_str.trim_start_matches("0x"))
612 .map_err(|e| format!("Invalid `public_key` string: {:?}", e))?;
613 <[u8; 32]>::try_from(bytes).map_err(|_| "Public key must be 32 bytes.".to_string())
614 } else {
615 Err("No public key or pair available.".to_string())
616 }
617 }
618
619 pub fn ss58_address(&self) -> Option<String> {
621 match &self.pair {
622 Some(pair) => Some(pair.ss58_address()),
623 None => self.ss58_address.clone(),
624 }
625 }
626
627 pub fn public_key(&self) -> Result<Option<Vec<u8>>, String> {
629 if let Some(pair) = &self.pair {
630 Ok(Some(pair.public_bytes().to_vec()))
631 } else if let Some(public_key) = &self.public_key {
632 let public_key_vec = hex::decode(public_key.trim_start_matches("0x"))
633 .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
634 Ok(Some(public_key_vec))
635 } else {
636 Ok(None)
637 }
638 }
639
640 pub fn ss58_format(&self) -> u8 {
642 self.ss58_format
643 }
644
645 pub fn seed_hex(&self) -> Option<Vec<u8>> {
647 self.seed_hex.clone()
648 }
649
650 pub fn crypto_type(&self) -> u8 {
653 match &self.pair {
654 Some(pair) => pair.crypto_type(),
655 None => self.crypto_type,
656 }
657 }
658
659 pub fn mnemonic(&self) -> Option<String> {
661 self.mnemonic.clone()
662 }
663
664 pub fn private_key(&self) -> Result<Option<Vec<u8>>, String> {
666 match &self.pair {
667 Some(pair) => {
668 let seed = pair.to_raw_vec();
669 Ok(Some(seed))
670 }
671 None => {
672 if let Some(private_key) = &self.private_key {
673 Ok(Some(private_key.as_bytes().to_vec()))
674 } else {
675 Ok(None)
676 }
677 }
678 }
679 }
680}
681
682impl Default for Keypair {
683 fn default() -> Self {
684 Keypair {
685 ss58_address: None,
686 public_key: None,
687 private_key: None,
688 ss58_format: 42,
689 seed_hex: None,
690 crypto_type: CRYPTO_SR25519,
691 mnemonic: None,
692 pair: None,
693 }
694 }
695}
696
697impl fmt::Display for Keypair {
698 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699 let address = self.ss58_address();
700 match address {
701 Some(addr) => write!(f, "<Keypair (address={})>", addr),
702 None => write!(f, "<Keypair (address=None)>"),
703 }
704 }
705}
706
707impl fmt::Debug for Keypair {
708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709 let address = self.ss58_address();
710 match address {
711 Some(addr) => write!(f, "<Keypair (address={})>", addr),
712 None => write!(f, "<Keypair (address=None)>"),
713 }
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720
721 fn test_mnemonic() -> String {
722 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
723 .to_string()
724 }
725
726 #[test]
729 fn test_sr25519_from_mnemonic_produces_valid_keypair() {
730 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
731 assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
732 let pk = kp.public_key().unwrap().unwrap();
733 assert_eq!(pk.len(), 32);
734 assert!(kp.ss58_address().is_some());
735 assert!(kp.ss58_address().unwrap().starts_with('5'));
736 }
737
738 #[test]
739 fn test_ed25519_from_mnemonic_produces_valid_keypair() {
740 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
741 assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
742 let pk = kp.public_key().unwrap().unwrap();
743 assert_eq!(pk.len(), 32);
744 let priv_key = kp.private_key().unwrap().unwrap();
745 assert_eq!(priv_key.len(), 32);
746 assert!(kp.ss58_address().is_some());
747 assert!(kp.ss58_address().unwrap().starts_with('5'));
748 }
749
750 #[test]
751 fn test_same_mnemonic_different_crypto_produces_different_addresses() {
752 let sr = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
753 let ed = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
754 assert_ne!(sr.ss58_address(), ed.ss58_address());
755 assert_ne!(
756 sr.public_key().unwrap().unwrap(),
757 ed.public_key().unwrap().unwrap()
758 );
759 }
760
761 #[test]
762 fn test_ed25519_from_seed_accepts_32_bytes() {
763 let result = Keypair::create_from_seed([1u8; 32].to_vec(), CRYPTO_ED25519);
764 assert!(result.is_ok());
765 }
766
767 #[test]
768 fn test_ed25519_from_seed_rejects_64_bytes() {
769 let result = Keypair::create_from_seed([1u8; 64].to_vec(), CRYPTO_ED25519);
770 assert!(result.is_err());
771 }
772
773 #[test]
774 fn test_sr25519_from_seed_accepts_32_and_64_bytes() {
775 assert!(Keypair::create_from_seed([1u8; 32].to_vec(), CRYPTO_SR25519).is_ok());
776 assert!(Keypair::create_from_seed([1u8; 64].to_vec(), CRYPTO_SR25519).is_ok());
777 }
778
779 #[test]
780 fn test_ed25519_from_uri_hard_derivation() {
781 let kp = Keypair::create_from_uri("//Alice", CRYPTO_ED25519).unwrap();
782 assert!(kp.ss58_address().is_some());
783 assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
784 }
785
786 #[test]
787 fn test_invalid_crypto_type_rejected() {
788 assert!(Keypair::create_from_mnemonic(&test_mnemonic(), 2).is_err());
789 assert!(Keypair::create_from_mnemonic(&test_mnemonic(), 255).is_err());
790 assert!(Keypair::new(
791 Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
792 None,
793 None,
794 42,
795 None,
796 5
797 )
798 .is_err());
799 }
800
801 #[test]
802 fn test_sr25519_alice_known_address() {
803 let kp = Keypair::create_from_uri("//Alice", CRYPTO_SR25519).unwrap();
804 assert_eq!(
805 kp.ss58_address().unwrap(),
806 "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
807 );
808 }
809
810 #[test]
813 fn test_ed25519_sign_verify_roundtrip() {
814 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
815 let sig = kp.sign(b"hello".to_vec()).unwrap();
816 assert_eq!(sig.len(), 64);
817 assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
818 }
819
820 #[test]
821 fn test_ed25519_verify_wrong_data() {
822 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
823 let sig = kp.sign(b"hello".to_vec()).unwrap();
824 assert!(!kp.verify(b"world".to_vec(), sig).unwrap());
825 }
826
827 #[test]
828 fn test_ed25519_verify_bytes_wrapping() {
829 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
830 let sig = kp.sign(b"<Bytes>hello</Bytes>".to_vec()).unwrap();
831 assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
832 }
833
834 #[test]
835 fn test_sr25519_sign_verify_unchanged() {
836 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
837 let sig = kp.sign(b"hello".to_vec()).unwrap();
838 assert_eq!(sig.len(), 64);
839 assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
840 }
841
842 #[test]
843 fn test_cross_type_verification_fails() {
844 let sr = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
845 let ed = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
846 let sig = sr.sign(b"hello".to_vec()).unwrap();
847 assert!(!ed.verify(b"hello".to_vec(), sig).unwrap());
848 }
849
850 #[test]
853 fn test_new_ed25519_pubonly_from_ss58() {
854 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
855 let addr = kp.ss58_address().unwrap();
856
857 let pub_kp =
858 Keypair::new(Some(addr.clone()), None, None, 42, None, CRYPTO_ED25519).unwrap();
859 assert_eq!(pub_kp.ss58_address().unwrap(), addr);
860 assert_eq!(pub_kp.crypto_type(), CRYPTO_ED25519);
861 }
862
863 #[test]
864 fn test_pair_is_some() {
865 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
866 assert!(kp.pair_is_some());
867
868 let pub_kp = Keypair::new(
869 Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
870 None,
871 None,
872 42,
873 None,
874 CRYPTO_SR25519,
875 )
876 .unwrap();
877 assert!(!pub_kp.pair_is_some());
878 }
879
880 #[test]
883 fn test_crypto_type_derived_from_pair() {
884 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
885 assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
886
887 let kp2 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
888 assert_eq!(kp2.crypto_type(), CRYPTO_SR25519);
889 }
890
891 #[test]
892 fn test_crypto_type_from_field_for_pubonly() {
893 let pub_kp = Keypair::new(
894 Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
895 None,
896 None,
897 42,
898 None,
899 CRYPTO_ED25519,
900 )
901 .unwrap();
902 assert_eq!(pub_kp.crypto_type(), CRYPTO_ED25519);
903 }
904
905 #[test]
908 fn test_ed25519_from_private_key_32_bytes() {
909 let hex_key = hex::encode([1u8; 32]);
910 let kp = Keypair::create_from_private_key(&hex_key, CRYPTO_ED25519).unwrap();
911 assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
912 assert!(kp.ss58_address().is_some());
913 }
914
915 #[test]
916 fn test_ed25519_from_private_key_64_bytes_rejected() {
917 let hex_key = hex::encode([1u8; 64]);
918 let result = Keypair::create_from_private_key(&hex_key, CRYPTO_ED25519);
919 assert!(result.is_err());
920 }
921
922 #[test]
923 fn test_sr25519_from_private_key_64_bytes() {
924 let hex_key = hex::encode([1u8; 64]);
925 let kp = Keypair::create_from_private_key(&hex_key, CRYPTO_SR25519).unwrap();
926 assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
927 assert!(kp.ss58_address().is_some());
928 }
929
930 #[test]
933 fn test_ed25519_mnemonic_determinism() {
934 let kp1 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
935 let kp2 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
936 assert_eq!(kp1.ss58_address(), kp2.ss58_address());
937 assert_eq!(
938 kp1.public_key().unwrap().unwrap(),
939 kp2.public_key().unwrap().unwrap()
940 );
941 }
942
943 #[test]
946 fn test_ed25519_pubonly_can_verify() {
947 let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
948 let sig = full.sign(b"test-message".to_vec()).unwrap();
949 let addr = full.ss58_address().unwrap();
950
951 let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
952 assert!(pub_only.verify(b"test-message".to_vec(), sig).unwrap());
953 }
954
955 #[test]
958 fn test_sr25519_verify_bytes_wrapping() {
959 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
960 let sig = kp.sign(b"<Bytes>hello</Bytes>".to_vec()).unwrap();
961 assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
962 }
963
964 #[test]
967 fn test_default_crypto_type_is_sr25519() {
968 let kp = Keypair::default();
969 assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
970 }
971
972 #[test]
975 fn test_sp_core_and_sodiumoxide_same_pubkey_from_seed() {
976 let seed = [42u8; 32];
977 let sp_pair = ed25519::Pair::from_seed_slice(&seed).unwrap();
978 let sp_pubkey = sp_pair.public().0;
979
980 let sodium_seed = sign_ed25519::Seed::from_slice(&seed).unwrap();
981 let (sodium_pk, _) = sign_ed25519::keypair_from_seed(&sodium_seed);
982
983 assert_eq!(sp_pubkey, sodium_pk.0);
984 }
985
986 #[test]
987 fn test_encrypt_decrypt_roundtrip() {
988 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
989 let message = b"secret message for testing";
990 let ciphertext = kp.encrypt(message).unwrap();
991 let plaintext = kp.decrypt(&ciphertext).unwrap();
992 assert_eq!(plaintext, message);
993 }
994
995 #[test]
996 fn test_encrypt_produces_correct_length() {
997 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
998 let message = b"hello world";
999 let ciphertext = kp.encrypt(message).unwrap();
1000 assert_eq!(ciphertext.len(), message.len() + 48);
1001 }
1002
1003 #[test]
1004 fn test_encrypt_is_nondeterministic() {
1005 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1006 let message = b"same message";
1007 let ct1 = kp.encrypt(message).unwrap();
1008 let ct2 = kp.encrypt(message).unwrap();
1009 assert_ne!(ct1, ct2);
1010 }
1011
1012 #[test]
1013 fn test_encrypt_works_on_pubonly_keypair() {
1014 let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1015 let addr = full.ss58_address().unwrap();
1016 let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1017 let ciphertext = pub_only.encrypt(b"hello").unwrap();
1018 assert_eq!(ciphertext.len(), 5 + 48);
1019 }
1020
1021 #[test]
1022 fn test_decrypt_fails_on_pubonly_keypair() {
1023 let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1024 let ciphertext = full.encrypt(b"hello").unwrap();
1025 let addr = full.ss58_address().unwrap();
1026 let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1027 let result = pub_only.decrypt(&ciphertext);
1028 assert!(result.is_err());
1029 assert!(result.unwrap_err().contains("private key"));
1030 }
1031
1032 #[test]
1033 fn test_encrypt_fails_on_sr25519() {
1034 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
1035 let result = kp.encrypt(b"hello");
1036 assert!(result.is_err());
1037 assert!(result.unwrap_err().contains("ED25519"));
1038 }
1039
1040 #[test]
1041 fn test_decrypt_fails_on_sr25519() {
1042 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
1043 let result = kp.decrypt(b"fake ciphertext that is long enough for sealbytes padding!!");
1044 assert!(result.is_err());
1045 assert!(result.unwrap_err().contains("ED25519"));
1046 }
1047
1048 #[test]
1049 fn test_decrypt_fails_with_wrong_key() {
1050 let alice = Keypair::create_from_uri("//Alice", CRYPTO_ED25519).unwrap();
1051 let bob = Keypair::create_from_uri("//Bob", CRYPTO_ED25519).unwrap();
1052 let ciphertext = alice.encrypt(b"for alice only").unwrap();
1053 let result = bob.decrypt(&ciphertext);
1054 assert!(result.is_err());
1055 }
1056
1057 #[test]
1058 fn test_decrypt_fails_with_tampered_ciphertext() {
1059 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1060 let mut ciphertext = kp.encrypt(b"hello").unwrap();
1061 ciphertext[10] ^= 0xFF;
1062 let result = kp.decrypt(&ciphertext);
1063 assert!(result.is_err());
1064 }
1065
1066 #[test]
1067 fn test_encrypt_empty_message() {
1068 let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1069 let ciphertext = kp.encrypt(b"").unwrap();
1070 assert_eq!(ciphertext.len(), 48);
1071 let plaintext = kp.decrypt(&ciphertext).unwrap();
1072 assert_eq!(plaintext, b"");
1073 }
1074
1075 #[test]
1076 fn test_pubonly_can_encrypt_fullkey_can_decrypt() {
1077 let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1078 let addr = full.ss58_address().unwrap();
1079 let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1080
1081 let ciphertext = pub_only.encrypt(b"encrypted by pub-only").unwrap();
1082 let plaintext = full.decrypt(&ciphertext).unwrap();
1083 assert_eq!(plaintext, b"encrypted by pub-only");
1084 }
1085}