Skip to main content

iota_keys/
keystore.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeMap, HashSet},
7    fmt::{Display, Formatter, Write},
8    fs,
9    fs::File,
10    io::BufReader,
11    path::{Path, PathBuf},
12};
13
14use anyhow::{Context, anyhow, bail, ensure};
15use bip32::DerivationPath;
16use bip39::{Language, Mnemonic, Seed};
17use iota_sdk_types::{
18    Address,
19    crypto::{Intent, IntentMessage},
20};
21use iota_types::crypto::{
22    EncodeDecodeBase64, IotaKeyPair, PublicKey, Signature, SignatureScheme, enum_dispatch,
23    get_key_pair_from_rng,
24};
25use rand::{SeedableRng, rngs::StdRng};
26use regex::Regex;
27use serde::{Deserialize, Deserializer, Serialize, Serializer};
28use serde_with::{DisplayFromStr, serde_as};
29use tracing::{debug, info};
30
31use crate::{
32    key_derive::{derive_key_pair_from_path, generate_new_key},
33    random_names::{random_name, random_names},
34    serde_iota_keypair, serde_public_key,
35};
36
37#[derive(Serialize, Deserialize)]
38#[enum_dispatch(AccountKeystore)]
39pub enum Keystore {
40    File(FileBasedKeystore),
41    InMem(InMemKeystore),
42}
43
44#[enum_dispatch]
45pub trait AccountKeystore: Send + Sync {
46    fn add_key(
47        &mut self,
48        alias: Option<String>,
49        key: impl Into<StoredKey>,
50    ) -> Result<(), anyhow::Error>;
51    fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error>;
52    fn keys(&self) -> Vec<&StoredKey>;
53    fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error>;
54
55    fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error>;
56
57    fn sign_secure<T>(
58        &self,
59        address: &Address,
60        msg: &T,
61        intent: Intent,
62    ) -> Result<Signature, signature::Error>
63    where
64        T: Serialize;
65    fn addresses(&self) -> Vec<Address> {
66        self.keys().into_iter().map(|k| k.address()).collect()
67    }
68    fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)>;
69    fn aliases(&self) -> Vec<&Alias>;
70    fn aliases_mut(&mut self) -> Vec<&mut Alias>;
71    fn alias_names(&self) -> Vec<&str> {
72        self.aliases()
73            .into_iter()
74            .map(|a| a.alias.as_str())
75            .collect()
76    }
77    /// Get alias of address
78    fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error>;
79    fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error>;
80    /// Check if an alias exists by its name
81    fn alias_exists(&self, alias: &str) -> bool {
82        self.alias_names().contains(&alias)
83    }
84
85    fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error>;
86
87    fn update_alias(
88        &mut self,
89        old_alias: &str,
90        new_alias: Option<&str>,
91    ) -> Result<String, anyhow::Error>;
92
93    // Internal function. Use update_alias instead
94    fn update_alias_value(
95        &mut self,
96        old_alias: &str,
97        new_alias: Option<&str>,
98    ) -> Result<String, anyhow::Error> {
99        if !self.alias_exists(old_alias) {
100            bail!("The provided alias {old_alias} does not exist");
101        }
102
103        let new_alias_name = self.create_alias(new_alias.map(str::to_string))?;
104
105        for a in self.aliases_mut() {
106            if a.alias == old_alias {
107                *a = Alias {
108                    alias: new_alias_name.clone(),
109                };
110            }
111        }
112        Ok(new_alias_name)
113    }
114
115    fn generate_and_add_new_key(
116        &mut self,
117        key_scheme: SignatureScheme,
118        alias: Option<String>,
119        derivation_path: Option<DerivationPath>,
120        word_length: Option<String>,
121    ) -> Result<(Address, String, SignatureScheme), anyhow::Error> {
122        let (address, kp, scheme, phrase) =
123            generate_new_key(key_scheme, derivation_path, word_length)?;
124        self.add_key(alias, kp)?;
125        Ok((address, phrase, scheme))
126    }
127
128    fn import_from_mnemonic(
129        &mut self,
130        phrase: &str,
131        key_scheme: SignatureScheme,
132        derivation_path: Option<DerivationPath>,
133        alias: Option<String>,
134    ) -> Result<Address, anyhow::Error> {
135        let mnemonic = Mnemonic::from_phrase(phrase, Language::English)
136            .map_err(|e| anyhow::anyhow!("Invalid mnemonic phrase: {e:?}"))?;
137        let seed = Seed::new(&mnemonic, "");
138        self.import_from_seed(seed.as_bytes(), key_scheme, derivation_path, alias)
139    }
140
141    fn import_from_seed(
142        &mut self,
143        seed: &[u8],
144        key_scheme: SignatureScheme,
145        derivation_path: Option<DerivationPath>,
146        alias: Option<String>,
147    ) -> Result<Address, anyhow::Error> {
148        match derive_key_pair_from_path(seed, derivation_path, &key_scheme) {
149            Ok((address, kp)) => {
150                self.add_key(alias, kp)?;
151                Ok(address)
152            }
153            Err(e) => Err(anyhow!("error getting keypair {e:?}")),
154        }
155    }
156
157    fn import_from_external(
158        &mut self,
159        source: &str,
160        public_key: PublicKey,
161        derivation_path: Option<DerivationPath>,
162        alias: Option<String>,
163    ) -> Result<(), anyhow::Error> {
164        self.add_key(
165            alias,
166            StoredKey::External {
167                derivation_path,
168                public_key,
169                source: source.to_string(),
170            },
171        )
172    }
173}
174
175impl Display for Keystore {
176    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
177        let mut writer = String::new();
178        match self {
179            Keystore::File(file) => {
180                writeln!(writer, "Keystore Type: File")?;
181                write!(writer, "Keystore Path : {:?}", file.path)?;
182                write!(f, "{writer}")
183            }
184            Keystore::InMem(_) => {
185                writeln!(writer, "Keystore Type: InMem")?;
186                write!(f, "{writer}")
187            }
188        }
189    }
190}
191
192// Used to migrate from keystore v1 to v2
193#[derive(Serialize, Deserialize, Clone, Debug)]
194pub struct LegacyAlias {
195    pub alias: String,
196    pub public_key_base64: String,
197}
198
199#[derive(Serialize, Deserialize, Clone, Debug)]
200pub struct Alias {
201    pub alias: String,
202}
203
204#[expect(clippy::large_enum_variant)]
205#[serde_as]
206#[derive(Serialize, Deserialize, Debug, Clone)]
207#[serde(
208    tag = "type",            // this makes {"type": "...", "value": …}
209    content = "value",       // name the payload field "value"
210    rename_all = "snake_case"
211)]
212pub enum StoredKey {
213    #[serde(with = "serde_iota_keypair")]
214    KeyPair(IotaKeyPair),
215    Account(Address),
216    External {
217        source: String,
218        #[serde_as(as = "Option<DisplayFromStr>")]
219        #[serde(skip_serializing_if = "Option::is_none")]
220        derivation_path: Option<DerivationPath>,
221        #[serde(rename = "public_key_base64_with_flag", with = "serde_public_key")]
222        public_key: PublicKey,
223    },
224}
225
226impl From<IotaKeyPair> for StoredKey {
227    fn from(keypair: IotaKeyPair) -> Self {
228        StoredKey::KeyPair(keypair)
229    }
230}
231
232impl StoredKey {
233    pub fn address(&self) -> Address {
234        match self {
235            StoredKey::KeyPair(key) => (&key.public()).into(),
236            StoredKey::Account(address) => *address,
237            StoredKey::External { public_key, .. } => public_key.into(),
238        }
239    }
240
241    pub fn public(&self) -> PublicKey {
242        match self {
243            StoredKey::KeyPair(keypair) => keypair.public(),
244            StoredKey::Account(_) => panic!("Account addresses are not backed by key pairs."),
245            StoredKey::External { public_key, .. } => public_key.clone(),
246        }
247    }
248
249    pub fn derivation_path(&self) -> Option<DerivationPath> {
250        match self {
251            StoredKey::KeyPair(_) => None,
252            StoredKey::Account(_) => None,
253            StoredKey::External {
254                derivation_path, ..
255            } => derivation_path.clone(),
256        }
257    }
258
259    pub fn external_source(&self) -> Option<String> {
260        match self {
261            StoredKey::KeyPair(_) => None,
262            StoredKey::Account(_) => None,
263            StoredKey::External { source, .. } => Some(source.clone()),
264        }
265    }
266
267    pub fn as_keypair(&self) -> Result<&IotaKeyPair, anyhow::Error> {
268        match self {
269            StoredKey::KeyPair(keypair) => Ok(keypair),
270            StoredKey::Account(_) => bail!("Account addresses are not backed by key pairs."),
271            StoredKey::External { .. } => bail!("Cannot get key pair for External keys."),
272        }
273    }
274
275    pub fn source(&self) -> &str {
276        match self {
277            StoredKey::KeyPair(_) => "keypair",
278            StoredKey::Account(_) => "account",
279            StoredKey::External { source, .. } => source,
280        }
281    }
282}
283
284#[derive(Default)]
285pub struct FileBasedKeystore {
286    keys: BTreeMap<Address, StoredKey>,
287    aliases: BTreeMap<Address, Alias>,
288    path: PathBuf,
289}
290
291impl Serialize for FileBasedKeystore {
292    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293    where
294        S: Serializer,
295    {
296        serializer.serialize_str(self.path.to_str().unwrap_or(""))
297    }
298}
299
300impl<'de> Deserialize<'de> for FileBasedKeystore {
301    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302    where
303        D: Deserializer<'de>,
304    {
305        use serde::de::Error;
306        FileBasedKeystore::new(&PathBuf::from(String::deserialize(deserializer)?))
307            .map_err(D::Error::custom)
308    }
309}
310#[derive(Serialize, Deserialize, Debug)]
311pub struct FileBasedKeystoreFile {
312    pub version: u8,
313    pub keys: Vec<AliasedKey>,
314}
315
316#[derive(Serialize, Deserialize, Debug)]
317pub struct AliasedKey {
318    pub alias: String,
319    pub address: Address,
320    pub key: StoredKey,
321}
322
323impl AccountKeystore for FileBasedKeystore {
324    fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error> {
325        let stored_key = self.keys.get(address).ok_or_else(|| {
326            signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
327        })?;
328
329        match stored_key {
330            StoredKey::KeyPair(keypair) => Ok(Signature::new_hashed(msg, keypair)),
331            StoredKey::Account(_) => Err(signature::Error::from_source(
332                "sign_hashed is not supported for account type",
333            )),
334            StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
335                "sign_hashed is not supported for external type: {source} [{address}]"
336            ))),
337        }
338    }
339    fn sign_secure<T>(
340        &self,
341        address: &Address,
342        msg: &T,
343        intent: Intent,
344    ) -> Result<Signature, signature::Error>
345    where
346        T: Serialize,
347    {
348        let stored_key = self.keys.get(address).ok_or_else(|| {
349            signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
350        })?;
351
352        let intent_msg = &IntentMessage::new(intent, msg);
353        match stored_key {
354            StoredKey::KeyPair(keypair) => Ok(Signature::new_secure(intent_msg, keypair)),
355            StoredKey::Account(_) => Err(signature::Error::from_source(
356                "sign_secure is not supported for account type",
357            )),
358            StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
359                "sign_secure is not supported for external type: {source} [{address}]",
360            ))),
361        }
362    }
363
364    fn add_key(
365        &mut self,
366        alias: Option<String>,
367        key: impl Into<StoredKey>,
368    ) -> Result<(), anyhow::Error> {
369        let key = key.into();
370        let address = key.address();
371
372        let alias = self.create_alias(alias)?;
373        self.aliases.insert(address, Alias { alias });
374        self.keys.insert(address, key);
375        self.save()?;
376        Ok(())
377    }
378
379    fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error> {
380        self.aliases.remove(address);
381        self.keys.remove(address);
382        self.save()?;
383        Ok(())
384    }
385
386    /// Return an array of `Alias`, consisting of every alias and its
387    /// corresponding public key.
388    fn aliases(&self) -> Vec<&Alias> {
389        self.aliases.values().collect()
390    }
391
392    fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)> {
393        self.aliases.iter().collect::<Vec<_>>()
394    }
395
396    /// Return an array of `Alias`, consisting of every alias and its
397    /// corresponding public key.
398    fn aliases_mut(&mut self) -> Vec<&mut Alias> {
399        self.aliases.values_mut().collect()
400    }
401
402    fn keys(&self) -> Vec<&StoredKey> {
403        self.keys.values().collect()
404    }
405
406    /// This function returns an error if the provided alias already exists. If
407    /// the alias has not already been used, then it returns the alias.
408    /// If no alias has been passed, it will generate a new alias.
409    fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error> {
410        match alias {
411            Some(a) if self.alias_exists(&a) => {
412                bail!("Alias {a} already exists. Please choose another alias.")
413            }
414            Some(a) => validate_alias(&a),
415            None => Ok(random_name(
416                &self
417                    .alias_names()
418                    .into_iter()
419                    .map(|x| x.to_string())
420                    .collect::<HashSet<_>>(),
421            )),
422        }
423    }
424
425    /// Get the address by its alias
426    fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error> {
427        self.addresses_with_alias()
428            .iter()
429            .find(|x| x.1.alias == alias)
430            .ok_or_else(|| anyhow!("Cannot resolve alias {alias} to an address"))
431            .map(|x| x.0)
432    }
433
434    /// Get the alias if it exists, or return an error if it does not exist.
435    fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error> {
436        match self.aliases.get(address) {
437            Some(alias) => Ok(alias.alias.clone()),
438            None => bail!("Cannot find alias for address {address}"),
439        }
440    }
441
442    fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error> {
443        match self.keys.get(address) {
444            Some(key) => Ok(key),
445            None => Err(anyhow!("Cannot find key for address: [{address}]")),
446        }
447    }
448
449    /// Updates an old alias to the new alias and saves it to the alias file.
450    /// If the new_alias is None, it will generate a new random alias.
451    fn update_alias(
452        &mut self,
453        old_alias: &str,
454        new_alias: Option<&str>,
455    ) -> Result<String, anyhow::Error> {
456        let new_alias_name = self.update_alias_value(old_alias, new_alias)?;
457        self.save()?;
458        Ok(new_alias_name)
459    }
460}
461
462impl FileBasedKeystore {
463    pub fn new_from_v1(path: &PathBuf) -> Result<Self, anyhow::Error> {
464        let keys = if path.exists() {
465            let reader =
466                BufReader::new(File::open(path).with_context(|| {
467                    format!("Cannot open the keystore file: {}", path.display())
468                })?);
469            let kp_strings: Vec<String> = serde_json::from_reader(reader).with_context(|| {
470                format!("Cannot deserialize the keystore file: {}", path.display(),)
471            })?;
472            kp_strings
473                .iter()
474                .map(|kpstr| {
475                    let key = IotaKeyPair::decode(kpstr);
476                    key.map(|k| (Address::from(&k.public()), StoredKey::KeyPair(k)))
477                })
478                .collect::<Result<BTreeMap<_, _>, _>>()
479                .map_err(|e| anyhow!("Invalid keystore file: {}. {}", path.display(), e))?
480        } else {
481            BTreeMap::new()
482        };
483
484        // check aliases
485        let mut aliases_path = path.clone();
486        aliases_path.set_extension("aliases");
487
488        let aliases = if aliases_path.exists() {
489            let reader = BufReader::new(File::open(&aliases_path).with_context(|| {
490                format!(
491                    "Cannot open aliases file in keystore: {}",
492                    aliases_path.display()
493                )
494            })?);
495
496            let legacy_aliases: Vec<LegacyAlias> =
497                serde_json::from_reader(reader).with_context(|| {
498                    format!(
499                        "Cannot deserialize aliases file in keystore: {}",
500                        aliases_path.display(),
501                    )
502                })?;
503
504            legacy_aliases
505                .into_iter()
506                .map(|legacy_alias| {
507                    let key = PublicKey::decode_base64(&legacy_alias.public_key_base64);
508                    key.map(|k| {
509                        (
510                            Into::<Address>::into(&k),
511                            Alias {
512                                alias: legacy_alias.alias,
513                            },
514                        )
515                    })
516                })
517                .collect::<Result<BTreeMap<_, _>, _>>()
518                .map_err(|e| {
519                    anyhow!(
520                        "Invalid aliases file in keystore: {}. {}",
521                        aliases_path.display(),
522                        e
523                    )
524                })?
525        } else if keys.is_empty() {
526            BTreeMap::new()
527        } else {
528            let names: Vec<String> = random_names(HashSet::new(), keys.len());
529            let aliases = keys
530                .iter()
531                .zip(names)
532                .map(|((iota_address, _ikp), alias)| (*iota_address, Alias { alias }))
533                .collect::<BTreeMap<_, _>>();
534            let aliases_store = serde_json::to_string_pretty(&aliases.values().collect::<Vec<_>>())
535                .with_context(|| {
536                    format!(
537                        "Cannot serialize aliases to file in keystore: {}",
538                        aliases_path.display()
539                    )
540                })?;
541            fs::write(aliases_path, aliases_store)?;
542            aliases
543        };
544
545        Ok(Self {
546            keys,
547            aliases,
548            path: path.to_path_buf(),
549        })
550    }
551
552    fn needs_migration(path: &PathBuf) -> Result<bool, anyhow::Error> {
553        let mut aliases_path = path.clone();
554        aliases_path.set_extension("aliases");
555        if aliases_path.exists() {
556            // If the aliases file exists, we assume that the keystore is in v1 format
557            debug!(
558                "An alias file exists at {}, assuming keystore is in v1 format",
559                aliases_path.display()
560            );
561            return Ok(true);
562        }
563        // If the aliases file does not exist, we check if the keystore file exists and
564        // it has the old format
565        if path.exists() {
566            let reader =
567                BufReader::new(File::open(path).with_context(|| {
568                    format!("Cannot open the keystore file: {}", path.display())
569                })?);
570            // If we can deserialize the keystore file as a Vec<String>, it is in v1 format
571            // If it fails, it is in v2 format or invalid
572            let is_v1_format = serde_json::from_reader::<_, Vec<String>>(reader).is_ok();
573            debug!(
574                "Keystore file at {} is in v1 format: {}",
575                path.display(),
576                is_v1_format,
577            );
578            Ok(is_v1_format)
579        } else {
580            // If the keystore file does not exist, no migration is needed
581            Ok(false)
582        }
583    }
584
585    fn migrate_v1_to_v2(path: &PathBuf) -> Result<Self, anyhow::Error> {
586        let migrated = Self::new_from_v1(path)?;
587        // If the migration was successful, we rename the <path> and <path>.aliases
588        // files as a backup and append .migrated
589        let mut backup_path = path.clone();
590        backup_path.set_extension(
591            // We append .migrated to the original file extension
592            // add_extension is still experimental, so we do it manually
593            path.extension()
594                .and_then(|ext| Some(ext.to_str()?.to_owned() + ".migrated"))
595                .unwrap_or(String::from("migrated")),
596        );
597        fs::rename(path, &backup_path).with_context(|| {
598            format!(
599                "Failed to rename the old keystore file to {}",
600                backup_path.display()
601            )
602        })?;
603        let mut aliases_path = path.clone();
604        aliases_path.set_extension("aliases");
605        let mut backup_aliases_path = aliases_path.clone();
606        backup_aliases_path.set_extension("aliases.migrated");
607        fs::rename(&aliases_path, &backup_aliases_path).with_context(|| {
608            format!(
609                "Failed to rename the old aliases file to {}",
610                backup_aliases_path.display()
611            )
612        })?;
613
614        info!(
615            "Migrated {} keys in keystore from v1 to v2 format. Old files have been renamed to {} and {}",
616            migrated.keys.len(),
617            backup_path.display(),
618            backup_aliases_path.display()
619        );
620
621        // Now we save the migrated keystore to the original path
622        migrated.save()?;
623
624        Ok(migrated)
625    }
626
627    pub fn new(path: &PathBuf) -> Result<Self, anyhow::Error> {
628        if Self::needs_migration(path)? {
629            return Self::migrate_v1_to_v2(path);
630        }
631
632        let (keys, aliases) = if path.exists() {
633            let reader =
634                BufReader::new(File::open(path).with_context(|| {
635                    format!("Cannot open the keystore file: {}", path.display())
636                })?);
637
638            let file: FileBasedKeystoreFile = serde_json::from_reader(reader).map_err(|e| {
639                anyhow!(
640                    "Cannot deserialize the keystore file: {}. {e}",
641                    path.display()
642                )
643            })?;
644
645            let aliases = file
646                .keys
647                .iter()
648                .map(|aliased| {
649                    (
650                        aliased.key.address(),
651                        Alias {
652                            alias: aliased.alias.clone(),
653                        },
654                    )
655                })
656                .collect::<BTreeMap<_, _>>();
657
658            let keys = file
659                .keys
660                .into_iter()
661                .map(|aliased| (aliased.key.address(), aliased.key))
662                .collect::<BTreeMap<_, _>>();
663
664            (keys, aliases)
665        } else {
666            (BTreeMap::new(), BTreeMap::new())
667        };
668
669        Ok(Self {
670            keys,
671            aliases,
672            path: path.to_path_buf(),
673        })
674    }
675
676    pub fn set_path(&mut self, path: &Path) {
677        self.path = path.to_path_buf();
678    }
679
680    pub fn save(&self) -> Result<(), anyhow::Error> {
681        let file = FileBasedKeystoreFile {
682            version: 2,
683            keys: self
684                .keys
685                .iter()
686                .map(|(address, key)| AliasedKey {
687                    alias: self
688                        .aliases
689                        .get(address)
690                        .map_or_else(|| self.create_alias(None).unwrap(), |a| a.alias.clone()),
691                    address: *address,
692                    key: key.clone(),
693                })
694                .collect(),
695        };
696
697        let store = serde_json::to_string_pretty(&file).with_context(|| {
698            format!("Cannot serialize keystore to file: {}", self.path.display())
699        })?;
700        fs::write(&self.path, store)
701            .map_err(|e| anyhow!("Couldn't save keystore to {}: {e}", self.path.display()))?;
702        Ok(())
703    }
704}
705
706#[derive(Default, Serialize, Deserialize)]
707pub struct InMemKeystore {
708    aliases: BTreeMap<Address, Alias>,
709    keys: BTreeMap<Address, StoredKey>,
710}
711
712impl AccountKeystore for InMemKeystore {
713    fn sign_hashed(&self, address: &Address, msg: &[u8]) -> Result<Signature, signature::Error> {
714        let stored_key = self.keys.get(address).ok_or_else(|| {
715            signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
716        })?;
717
718        match stored_key {
719            StoredKey::KeyPair(keypair) => Ok(Signature::new_hashed(msg, keypair)),
720            StoredKey::Account(_) => Err(signature::Error::from_source(
721                "sign_hashed is not supported for account type",
722            )),
723            StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
724                "sign_hashed is not supported for external type: {source} [{address}]"
725            ))),
726        }
727    }
728    fn sign_secure<T>(
729        &self,
730        address: &Address,
731        msg: &T,
732        intent: Intent,
733    ) -> Result<Signature, signature::Error>
734    where
735        T: Serialize,
736    {
737        let stored_key = self.keys.get(address).ok_or_else(|| {
738            signature::Error::from_source(format!("Cannot find key for address: [{address}]"))
739        })?;
740
741        let intent_msg = &IntentMessage::new(intent, msg);
742        match stored_key {
743            StoredKey::KeyPair(keypair) => Ok(Signature::new_secure(intent_msg, keypair)),
744            StoredKey::Account(_) => Err(signature::Error::from_source(
745                "sign_secure is not supported for account type",
746            )),
747            StoredKey::External { source, .. } => Err(signature::Error::from_source(format!(
748                "sign_secure is not supported for external type: {source} [{address}]",
749            ))),
750        }
751    }
752
753    fn add_key(
754        &mut self,
755        alias: Option<String>,
756        key: impl Into<StoredKey>,
757    ) -> Result<(), anyhow::Error> {
758        let key = key.into();
759        let address: Address = (&key.public()).into();
760        let alias = alias.unwrap_or_else(|| {
761            random_name(
762                &self
763                    .aliases()
764                    .iter()
765                    .map(|x| x.alias.clone())
766                    .collect::<HashSet<_>>(),
767            )
768        });
769
770        let alias = Alias { alias };
771        self.aliases.insert(address, alias);
772        self.keys.insert(address, key);
773        Ok(())
774    }
775
776    fn remove_key(&mut self, address: &Address) -> Result<(), anyhow::Error> {
777        self.aliases.remove(address);
778        self.keys.remove(address);
779        Ok(())
780    }
781
782    /// Get all aliases objects
783    fn aliases(&self) -> Vec<&Alias> {
784        self.aliases.values().collect()
785    }
786
787    fn addresses_with_alias(&self) -> Vec<(&Address, &Alias)> {
788        self.aliases.iter().collect::<Vec<_>>()
789    }
790
791    fn keys(&self) -> Vec<&StoredKey> {
792        self.keys.values().collect()
793    }
794
795    fn get_key(&self, address: &Address) -> Result<&StoredKey, anyhow::Error> {
796        match self.keys.get(address) {
797            Some(key) => Ok(key),
798            None => Err(anyhow!("Cannot find key for address: [{address}]")),
799        }
800    }
801
802    /// Get alias of address
803    fn get_alias_by_address(&self, address: &Address) -> Result<String, anyhow::Error> {
804        match self.aliases.get(address) {
805            Some(alias) => Ok(alias.alias.clone()),
806            None => bail!("Cannot find alias for address {address}"),
807        }
808    }
809
810    /// Get the address by its alias
811    fn get_address_by_alias(&self, alias: String) -> Result<&Address, anyhow::Error> {
812        self.addresses_with_alias()
813            .iter()
814            .find(|x| x.1.alias == alias)
815            .ok_or_else(|| anyhow!("Cannot resolve alias {alias} to an address"))
816            .map(|x| x.0)
817    }
818
819    /// This function returns an error if the provided alias already exists. If
820    /// the alias has not already been used, then it returns the alias.
821    /// If no alias has been passed, it will generate a new alias.
822    fn create_alias(&self, alias: Option<String>) -> Result<String, anyhow::Error> {
823        match alias {
824            Some(a) if self.alias_exists(&a) => {
825                bail!("Alias {a} already exists. Please choose another alias.")
826            }
827            Some(a) => validate_alias(&a),
828            None => Ok(random_name(
829                &self
830                    .alias_names()
831                    .into_iter()
832                    .map(|x| x.to_string())
833                    .collect::<HashSet<_>>(),
834            )),
835        }
836    }
837
838    fn aliases_mut(&mut self) -> Vec<&mut Alias> {
839        self.aliases.values_mut().collect()
840    }
841
842    /// Updates an old alias to the new alias. If the new_alias is None,
843    /// it will generate a new random alias.
844    fn update_alias(
845        &mut self,
846        old_alias: &str,
847        new_alias: Option<&str>,
848    ) -> Result<String, anyhow::Error> {
849        self.update_alias_value(old_alias, new_alias)
850    }
851}
852
853impl InMemKeystore {
854    pub fn new_insecure_for_tests(initial_key_number: usize) -> Self {
855        let mut rng = StdRng::from_seed([0; 32]);
856        let keys = (0..initial_key_number)
857            .map(|_| get_key_pair_from_rng(&mut rng))
858            .map(|(ad, k)| (ad, IotaKeyPair::Ed25519(k).into()))
859            .collect::<BTreeMap<Address, StoredKey>>();
860
861        let aliases = keys
862            .iter()
863            .zip(random_names(HashSet::new(), keys.len()))
864            .map(|((iota_address, _ikp), alias)| (*iota_address, Alias { alias }))
865            .collect::<BTreeMap<_, _>>();
866
867        Self { aliases, keys }
868    }
869}
870
871fn validate_alias(alias: &str) -> Result<String, anyhow::Error> {
872    let re = Regex::new(r"^[A-Za-z][A-Za-z0-9-_\.]*$")
873        .map_err(|_| anyhow!("Cannot build the regex needed to validate the alias naming"))?;
874    let alias = alias.trim();
875    ensure!(
876        re.is_match(alias),
877        "Invalid alias. A valid alias must start with a letter and can contain only letters, digits, hyphens (-), dots (.), or underscores (_)."
878    );
879    Ok(alias.to_string())
880}
881
882#[cfg(test)]
883mod tests {
884    use crate::keystore::validate_alias;
885
886    #[test]
887    fn validate_alias_test() {
888        // OK
889        assert!(validate_alias("A.B_dash").is_ok());
890        assert!(validate_alias("A.B-C1_dash").is_ok());
891        assert!(validate_alias("abc_123.iota").is_ok());
892        // Not allowed
893        assert!(validate_alias("A.B-C_dash!").is_err());
894        assert!(validate_alias(".B-C_dash!").is_err());
895        assert!(validate_alias("_test").is_err());
896        assert!(validate_alias("123").is_err());
897        assert!(validate_alias("@@123").is_err());
898        assert!(validate_alias("@_Ab").is_err());
899        assert!(validate_alias("_Ab").is_err());
900        assert!(validate_alias("^A").is_err());
901        assert!(validate_alias("-A").is_err());
902    }
903}