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