kms_secp256k1_api/services/
keys_service.rs

1#[cfg(test)]
2use crate::services::mocks::mock_kms_client_service::MockKmsClientService;
3use crate::services::{
4    aws_kms_client_service::AWSKmsClientService, crypto_service::CryptoService,
5    kms_client_service::KmsClientService,
6};
7use crate::{config::Config, constants::SIGNATURE_RSV_LEN};
8use serde::{Deserialize, Serialize};
9use std::sync::Arc;
10use tracing::error;
11
12#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
13pub struct KeyEntry {
14    pub address: Arc<String>,
15    pub public_key: Arc<Option<String>>,
16    pub public_key_base64: Arc<String>,
17    pub key_id: Arc<String>,
18}
19
20#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
21pub struct SigEntry {
22    pub address: Arc<String>,
23    pub public_key: Arc<String>,
24    pub signature: Arc<String>,
25}
26
27#[async_trait::async_trait]
28pub trait KeysServiceTrait: Send + Sync {
29    /// Creates a new cryptographic key based on the provided configuration.
30    ///
31    /// # Errors
32    /// Returns an error if key creation fails due to misconfiguration, internal cryptographic errors,
33    /// or if the underlying storage backend is unavailable.
34    async fn create_key(&mut self, config: &Config) -> Result<KeyEntry, String>;
35
36    /// Signs the given transaction hash using the specified public key.
37    ///
38    /// # Arguments
39    /// - `transaction_hash`: A hex-encoded hash of the transaction data.
40    /// - `key`: The key used to locate the corresponding private key for signing.
41    ///
42    /// # Errors
43    /// Returns an error if the key is not found, if signing fails, or if the inputs are malformed.
44    async fn sign_transaction_hash(
45        &mut self,
46        config: &Config,
47        transaction_hash: &str,
48        key: &str,
49    ) -> Result<SigEntry, String>;
50
51    /// Signs the raw transaction data using the specified public key.
52    ///
53    /// # Arguments
54    /// - `transaction_str`: The raw transaction string to be hashed and signed.
55    /// - `key`: The public key used to determine the signing key.
56    ///
57    /// # Errors
58    /// Returns an error if hashing, signing, or key retrieval fails.
59    async fn sign_transaction(
60        &mut self,
61        config: &Config,
62        transaction_str: &str,
63        key: &str,
64    ) -> Result<String, String>;
65
66    /// Verifies that the given signature is valid for the provided transaction hash and public key.
67    ///
68    /// # Arguments
69    /// - `transaction_hash_hex`: A hex-encoded transaction hash.
70    /// - `signature_hex`: A hex-encoded digital signature.
71    /// - `key`: The key alias to verify against.
72    ///
73    /// # Errors
74    /// Returns an error if the signature is invalid, the input format is incorrect,
75    /// or if verification logic encounters a failure.
76    async fn verify(
77        &mut self,
78        transaction_hash_hex: &str,
79        signature_hex: &str,
80        key: &str,
81    ) -> Result<bool, String>;
82
83    /// Verifies a transaction hash using an external KMS (Key Management System).
84    ///
85    /// # Arguments
86    /// - `transaction_hash_hex`: A hex-encoded hash of the transaction.
87    /// - `signature_hex`: A hex-encoded signature to verify.
88    /// - `key`: The key alias in the KMS.
89    ///
90    /// # Errors
91    /// Returns an error if KMS access fails, if the inputs are malformed,
92    /// or if verification returns an invalid result.
93    async fn verify_via_kms(
94        &mut self,
95        transaction_hash_hex: &str,
96        signature_hex: &str,
97        key: &str,
98    ) -> Result<bool, String>;
99
100    /// Deletes the cryptographic key associated with the given key.
101    ///
102    /// # Arguments
103    /// - `key`: The key whose associated private key should be deleted.
104    ///
105    /// # Errors
106    /// Returns an error if the key cannot be found or deletion fails due to internal issues or access control.
107    async fn delete_key(&mut self, key: &str) -> Result<bool, String>;
108
109    /// Lists all available public keys and their metadata.
110    ///
111    /// # Errors
112    /// Returns an error if key listing fails due to storage access problems or unexpected internal errors.
113    async fn list_keys(&mut self) -> Result<Vec<KeyEntry>, String>;
114}
115
116pub struct KeysService {
117    pub kms_client_service: Arc<dyn KmsClientService>,
118    pub crypto_service: CryptoService,
119}
120
121impl KeysService {
122    /// Creates a new `KeyService` instance with the given config and crypto service.
123    ///
124    /// Initializes the appropriate KMS client (AWS or else) based on the config.
125    ///
126    /// # Errors
127    ///
128    /// Returns an error if the AWS KMS client fails to initialize.
129    ///
130    pub async fn new(config: Config, crypto_service: CryptoService) -> Result<Self, String> {
131        let kms_client_service: Arc<dyn KmsClientService> = if config.is_aws_mode() {
132            let client = AWSKmsClientService::new(config.get_aws_config().clone())
133                .await
134                .map_err(|e| format!("Failed to initialize AWSKmsClientService: {e}"))?;
135            Arc::new(client)
136        } else if config.is_testing_mode() {
137            // TODO Implement other kms
138            Self::get_mock_kms_client_service()
139        } else {
140            return Err("Unsupported KMS mode and not in testing".to_string());
141        };
142
143        Ok(Self {
144            kms_client_service,
145            crypto_service,
146        })
147    }
148
149    #[cfg(test)]
150    fn get_mock_kms_client_service() -> Arc<dyn KmsClientService> {
151        Arc::new(MockKmsClientService)
152    }
153
154    #[cfg(not(test))]
155    fn get_mock_kms_client_service() -> Arc<dyn KmsClientService> {
156        unimplemented!("Mock KMS Client is only available in tests")
157    }
158
159    /// Signs a given transaction hash using the specified key.
160    ///
161    /// This function delegates signing to the underlying KMS client service, then converts the
162    /// resulting signature into the expected format. An optional prefix can be prepended to
163    /// the final signature string.
164    ///
165    /// # Parameters
166    /// - `transaction_hash_hex`: The transaction hash to sign, as a hex-encoded string.
167    /// - `key`: The key or alias to use for signing.
168    /// - `prefix`: An optional string slice to prepend to the signature (e.g., a format or type prefix).
169    ///
170    /// # Returns
171    /// - `Ok(String)`: The formatted signature string, optionally prefixed.
172    /// - `Err(String)`: An error message if signing or signature conversion fails.
173    ///
174    /// # Errors
175    /// Returns an error if:
176    /// - The KMS client service fails to produce a signature for the given hash and key.
177    /// - Conversion of the raw signature bytes into the desired format fails.
178    pub async fn sign(
179        &mut self,
180        transaction_hash_hex: &str,
181        key: &str,
182        prefix: Option<&str>,
183    ) -> Result<String, String> {
184        let signature = self
185            .kms_client_service
186            .sign(transaction_hash_hex, key)
187            .await
188            .map_err(|e| {
189                let msg = format!("Failed to sign transaction with KMS: {e}");
190                error!("{}", msg);
191                msg
192            })?;
193
194        let mut signature = self.crypto_service.convert(&signature).map_err(|e| {
195            let msg = format!("Failed to convert signature: {e}");
196            error!("{}", msg);
197            msg
198        })?;
199
200        if let Some(pref) = prefix {
201            signature = format!("{pref}{signature}");
202        }
203
204        Ok(signature)
205    }
206
207    /// Verifies a signature against a transaction hash and public key.
208    ///
209    /// # Arguments
210    ///
211    /// * `transaction_hash_hex` - The hex-encoded transaction hash to verify.
212    /// * `signature_hex` - The hex-encoded signature to verify.
213    /// * `public_key` - The public key to use for verification.
214    ///
215    /// # Errors
216    ///
217    /// Returns an error string if the verification process fails.
218    ///
219    /// # Returns
220    ///
221    /// `Ok(true)` if the signature is valid, `Ok(false)` if invalid.
222    pub fn verify(
223        &mut self,
224        transaction_hash_hex: &str,
225        signature_hex: &str,
226        public_key: &str,
227    ) -> Result<bool, String> {
228        self.crypto_service
229            .verify(transaction_hash_hex, signature_hex, public_key)
230            .map_err(|e| {
231                let msg = format!("Signature verification failed: {e}");
232                error!("{}", msg);
233                msg
234            })
235    }
236
237    /// Verifies an Ethereum EIP-155 signature against a transaction hash and public key.
238    ///
239    /// # Arguments
240    ///
241    /// * `transaction_hash_hex` - The hex-encoded transaction hash to verify.
242    /// * `signature_hex` - The hex-encoded signature to verify.
243    /// * `public_key` - The public key to use for verification.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error string if the verification process fails.
248    ///
249    /// # Returns
250    ///
251    /// `Ok(true)` if the signature is valid, `Ok(false)` if invalid.
252    pub fn verify_eip155(
253        &mut self,
254        transaction_hash_hex: &str,
255        signature_hex: &str,
256        public_key: &str,
257    ) -> Result<bool, String> {
258        self.crypto_service
259            .verify_eip155(transaction_hash_hex, signature_hex, public_key)
260            .map_err(|e| {
261                let msg = format!("Signature verification failed: {e}");
262                error!("{}", msg);
263                msg
264            })
265    }
266
267    /// Verifies a signature against a transaction hash and public key using both
268    /// local crypto verification and KMS verification.
269    ///
270    /// First, verifies the signature locally via the crypto service. If that succeeds,
271    /// converts the signature to ASN.1 base64 format and verifies it via the KMS client.
272    ///
273    /// # Arguments
274    ///
275    /// * `transaction_hash_hex` - Hex-encoded transaction hash to verify.
276    /// * `signature_hex` - Hex-encoded signature to verify.
277    /// * `public_key` - Public key used for verification.
278    ///
279    /// # Errors
280    ///
281    /// Returns an error string if either local or KMS verification fails.
282    ///
283    /// # Returns
284    ///
285    /// `Ok(true)` if both verifications pass, `Ok(false)` if local verification fails.
286    pub async fn verify_via_kms(
287        &mut self,
288        transaction_hash_hex: &str,
289        signature_hex: &str,
290        public_key: &str,
291    ) -> Result<bool, String> {
292        self.verify_via_kms_internal(transaction_hash_hex, signature_hex, public_key, false)
293            .await
294    }
295
296    /// Verifies an EIP-155 signature and then confirms it via KMS.
297    ///
298    /// This method first performs a cryptographic verification of the signature according to EIP-155.
299    /// If that succeeds, it proceeds to verify the signature via the KMS service.
300    ///
301    /// # Parameters
302    /// - `transaction_hash_hex`: The transaction hash to verify, as a hex-encoded string.
303    /// - `signature_hex`: The signature to verify, as a hex-encoded string.
304    /// - `public_key`: The public key corresponding to the signer, as a hex string.
305    ///
306    /// # Returns
307    /// - `Ok(true)` if both cryptographic and KMS verifications succeed.
308    /// - `Ok(false)` if the cryptographic verification fails.
309    /// - `Err(String)` if any error occurs during verification.
310    ///
311    /// # Errors
312    /// Returns an error if:
313    /// - The inputs cannot be parsed or decoded correctly.
314    /// - The cryptographic verification process encounters an unexpected error.
315    /// - The KMS verification call fails or returns an error.
316    pub async fn verify_via_kms_eip155(
317        &mut self,
318        transaction_hash_hex: &str,
319        signature_hex: &str,
320        public_key: &str,
321    ) -> Result<bool, String> {
322        self.verify_via_kms_internal(transaction_hash_hex, signature_hex, public_key, true)
323            .await
324    }
325
326    async fn verify_via_kms_internal(
327        &mut self,
328        transaction_hash_hex: &str,
329        mut signature_hex: &str,
330        public_key: &str,
331        use_eip155: bool,
332    ) -> Result<bool, String> {
333        let is_verified = if use_eip155 {
334            let verify_eip155 = self
335                .verify_eip155(transaction_hash_hex, signature_hex, public_key)
336                .map_err(|e| {
337                    let msg = format!("Signature eip155 verification failed: {e}");
338                    error!("{}", msg);
339                    msg
340                })?;
341
342            // Trim the last v byte for KMS verification if signature length matches
343            if signature_hex.len() == SIGNATURE_RSV_LEN {
344                signature_hex = &signature_hex[..signature_hex.len() - 2];
345            }
346
347            verify_eip155
348        } else {
349            self.verify(transaction_hash_hex, signature_hex, public_key)
350                .map_err(|e| {
351                    let msg = format!("Signature verification failed: {e}");
352                    error!("{}", msg);
353                    msg
354                })?
355        };
356
357        if !is_verified {
358            return Ok(false);
359        }
360
361        let signature = self.crypto_service.unconvert(signature_hex).map_err(|e| {
362            let msg = format!("Signature conversion failed: {e}");
363            error!("{}", msg);
364            msg
365        })?;
366
367        let alias = if use_eip155 {
368            self.crypto_service.address_eth(public_key).map_err(|e| {
369                let msg = format!("Failed to convert public key to address: {e:?}");
370                error!("{}", &msg);
371                msg
372            })?
373        } else {
374            public_key.to_string()
375        };
376
377        self.kms_client_service
378            .verify(transaction_hash_hex, &signature, &alias)
379            .await
380            .map_err(|e| {
381                let msg = format!("Failed to verify signature with KMS: {e}");
382                error!("{}", msg);
383                msg
384            })
385    }
386
387    /// Deletes a key identified by the given key using the KMS client.
388    ///
389    /// # Arguments
390    ///
391    /// * `key` - The key identifying the key to delete.
392    ///
393    /// # Errors
394    ///
395    /// Returns an error string if the deletion fails.
396    ///
397    /// # Returns
398    ///
399    /// `Ok(true)` if the key was successfully deleted.
400    pub async fn delete_key(&mut self, key: &str) -> Result<bool, String> {
401        self.kms_client_service.delete_key(key).await.map_err(|e| {
402            let msg = format!("Key deletion failed: {e}");
403            error!("{}", msg);
404            msg
405        })
406    }
407
408    /// Retrieves a list of keys from the KMS client.
409    ///
410    /// # Errors
411    ///
412    /// Returns an error string if the key listing fails.
413    ///
414    /// # Returns
415    ///
416    /// `Ok` with a vector of `KeyEntry`
417    pub async fn list_keys(&mut self) -> Result<Vec<KeyEntry>, String> {
418        let entries = self.kms_client_service.list_keys().await.map_err(|e| {
419            let msg = format!("Listing keys failed: {e}");
420            error!("{}", msg);
421            msg
422        })?;
423
424        let mut result = Vec::with_capacity(entries.len());
425
426        for mut entry in entries {
427            if entry.public_key.is_none() {
428                let public_key_base64 = entry.public_key_base64.to_string();
429
430                let public_key =
431                    self.crypto_service
432                        .public_key(&public_key_base64)
433                        .map_err(|e| {
434                            let msg = format!("public_key conversion failed: {e:?}");
435                            error!("{}", &msg);
436                            msg
437                        })?;
438
439                entry.public_key = Some(public_key).into();
440            }
441            result.push(entry);
442        }
443
444        Ok(result)
445    }
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    use crate::{
452        config::ConfigBuilder,
453        constants::{
454            CASPER_PUBLIC_KEY_PREFIXED, CASPER_SECP_PREFIX, ETH_PUBLIC_KEY, ETH_SIGNATURE,
455            ETH_TRANSACTION_HASH, SIGNATURE_RS_LEN, TRANSACTION_HASH, WASM_PATH,
456        },
457        services::crypto_service::CryptoService,
458        wasm_loader::WasmLoader,
459    };
460    use base64::Engine;
461    use base64::engine::general_purpose::STANDARD;
462
463    #[tokio::test]
464    async fn test_sign_successful() {
465        let config = ConfigBuilder::new().with_casper_mode().build();
466
467        let wasm_loader = WasmLoader::new(WASM_PATH)
468            .await
469            .expect("Failed to load WASM module");
470
471        let crypto_service =
472            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
473
474        // Create CasperKeysService (uses mocked KMS + real CryptoService)
475        let mut service = KeysService::new(config.clone(), crypto_service)
476            .await
477            .expect("Failed to create KeysService");
478
479        let public_key = CASPER_PUBLIC_KEY_PREFIXED;
480
481        // Call the sign method with prefix
482        let result = service
483            .sign(TRANSACTION_HASH, public_key, Some(CASPER_SECP_PREFIX))
484            .await;
485        assert!(result.is_ok(), "sign failed: {result:?}");
486        let signature = result.unwrap();
487        assert!(
488            signature.starts_with(CASPER_SECP_PREFIX),
489            "Signature missing prefix"
490        );
491        assert_eq!(
492            signature.len(),
493            CASPER_SECP_PREFIX.len() + SIGNATURE_RS_LEN,
494            "Signature length invalid"
495        );
496
497        // Call the sign method without PREFIX
498        let result = service.sign(TRANSACTION_HASH, public_key, None).await;
499        assert!(result.is_ok(), "sign failed: {result:?}");
500        let signature = result.unwrap();
501
502        assert_eq!(
503            signature.len(),
504            SIGNATURE_RS_LEN,
505            "Signature length invalid"
506        );
507    }
508
509    // #[tokio::test]
510    // async fn test_sign_eip155_successful() {
511    //     let config = ConfigBuilder::new().with_ethereum_mode().build();
512
513    //     let wasm_loader = WasmLoader::new(WASM_PATH)
514    //         .await
515    //         .expect("Failed to load WASM module");
516
517    //     let crypto_service =
518    //         CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
519
520    //     // Create CasperKeysService (uses mocked KMS + real CryptoService)
521    //     let mut service = KeysService::new(config.clone(), crypto_service)
522    //         .await
523    //         .expect("Failed to create KeysService");
524
525    //     // Call the sign method without PREFIX
526    //     let result = service
527    //         .sign(ETH_TRANSACTION_HASH, ETH_PUBLIC_KEY, None)
528    //         .await;
529    //     assert!(result.is_ok(), "sign failed: {result:?}");
530    //     let signature = result.unwrap();
531
532    //     assert_eq!(signature.to_string(), ETH_SIGNATURE, "Signature invalid");
533    //     assert_eq!(
534    //         signature.len(),
535    //         SIGNATURE_RSV_LEN,
536    //         "Signature length invalid"
537    //     );
538    // }
539
540    #[tokio::test]
541    async fn test_verify_successful() {
542        let config = ConfigBuilder::new().with_casper_mode().build();
543
544        let wasm_loader = WasmLoader::new(WASM_PATH)
545            .await
546            .expect("Failed to load WASM module");
547
548        let crypto_service =
549            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
550
551        let mut service = KeysService::new(config.clone(), crypto_service)
552            .await
553            .expect("Failed to create KeysService");
554
555        let public_key = CASPER_PUBLIC_KEY_PREFIXED;
556
557        // First, sign the transaction
558        let signature = service
559            .sign(TRANSACTION_HASH, public_key, None)
560            .await
561            .expect("Signing failed");
562
563        // Then, verify the signature
564        let result = service.verify(TRANSACTION_HASH, &signature, public_key);
565
566        assert!(result.is_ok(), "verify failed: {result:?}");
567        assert!(result.unwrap(), "signature verification returned false");
568    }
569
570    #[tokio::test]
571    async fn test_verify_eip155_successful() {
572        let config = ConfigBuilder::new().with_ethereum_mode().build();
573
574        let wasm_loader = WasmLoader::new(WASM_PATH)
575            .await
576            .expect("Failed to load WASM module");
577
578        let crypto_service =
579            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
580
581        let mut service = KeysService::new(config.clone(), crypto_service)
582            .await
583            .expect("Failed to create KeysService");
584
585        let transaction_hash = ETH_TRANSACTION_HASH;
586        let public_key = ETH_PUBLIC_KEY;
587        let signature = ETH_SIGNATURE;
588
589        // Verify the known-good Ethereum signature
590        let result = service.verify_eip155(transaction_hash, signature, public_key);
591
592        assert!(result.is_ok(), "verify_eip155 failed: {result:?}");
593        assert!(
594            result.unwrap(),
595            "EIP-155 signature verification returned false"
596        );
597    }
598
599    #[tokio::test]
600    async fn test_verify_via_kms_successful() {
601        let config = ConfigBuilder::new().with_casper_mode().build();
602
603        let wasm_loader = WasmLoader::new(WASM_PATH)
604            .await
605            .expect("Failed to load WASM module");
606
607        let crypto_service =
608            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
609
610        let mut service = KeysService::new(config, crypto_service)
611            .await
612            .expect("Failed to create KeysService");
613
614        // Sign using Casper keys
615        let signature = service
616            .sign(TRANSACTION_HASH, CASPER_PUBLIC_KEY_PREFIXED, None)
617            .await
618            .expect("Signing failed");
619
620        // Verify via KMS
621        let result = service
622            .verify_via_kms(TRANSACTION_HASH, &signature, CASPER_PUBLIC_KEY_PREFIXED)
623            .await;
624
625        assert!(result.is_ok(), "verify_via_kms failed: {result:?}");
626        assert!(result.unwrap(), "KMS verification returned false");
627    }
628
629    #[tokio::test]
630    async fn test_verify_via_kms_eip155_successful() {
631        let config = ConfigBuilder::new().with_ethereum_mode().build();
632
633        let wasm_loader = WasmLoader::new(WASM_PATH)
634            .await
635            .expect("Failed to load WASM module");
636
637        let crypto_service =
638            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
639
640        let mut service = KeysService::new(config, crypto_service)
641            .await
642            .expect("Failed to create KeysService");
643
644        // ETH_SIGNATURE is assumed valid, from constants
645        let result = service
646            .verify_via_kms_eip155(ETH_TRANSACTION_HASH, ETH_SIGNATURE, ETH_PUBLIC_KEY)
647            .await;
648
649        assert!(result.is_ok(), "verify_via_kms_eip155 failed: {result:?}");
650        assert!(result.unwrap(), "EIP-155 + KMS verification returned false");
651    }
652
653    #[tokio::test]
654    async fn test_delete_key_successful() {
655        let config = ConfigBuilder::new().build();
656
657        let wasm_loader = WasmLoader::new(WASM_PATH)
658            .await
659            .expect("Failed to load WASM module");
660
661        let crypto_service =
662            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
663
664        let mut service = KeysService::new(config, crypto_service)
665            .await
666            .expect("Failed to create KeysService");
667
668        // Key that matches mocked successful condition
669        let result = service.delete_key("known_public_key_xyz").await;
670
671        assert!(result.is_ok(), "delete_key failed unexpectedly: {result:?}");
672        assert!(result.unwrap(), "Expected key deletion to succeed");
673    }
674
675    #[tokio::test]
676    async fn test_delete_key_not_found() {
677        let config = ConfigBuilder::new().build();
678
679        let wasm_loader = WasmLoader::new(WASM_PATH)
680            .await
681            .expect("Failed to load WASM module");
682
683        let crypto_service =
684            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
685
686        let mut service = KeysService::new(config, crypto_service)
687            .await
688            .expect("Failed to create KeysService");
689
690        // Key that doesn't match mock condition
691        let result = service.delete_key("some_other_key").await;
692
693        assert!(result.is_ok(), "delete_key failed unexpectedly: {result:?}");
694        assert!(
695            !result.unwrap(),
696            "Expected deletion to return false for unknown key"
697        );
698    }
699
700    #[tokio::test]
701    async fn test_list_keys_successful() {
702        let config = ConfigBuilder::new().build();
703        let wasm_loader = WasmLoader::new(WASM_PATH)
704            .await
705            .expect("Failed to load WASM module");
706
707        let crypto_service =
708            CryptoService::new(&wasm_loader).expect("Failed to initialize CryptoService");
709
710        let mut service = KeysService::new(config, crypto_service)
711            .await
712            .expect("Failed to create KeysService");
713
714        let result = service.list_keys().await;
715
716        assert!(result.is_ok(), "list_keys failed: {result:?}");
717
718        let keys = result.unwrap();
719        assert_eq!(keys.len(), 2, "Expected two keys from mocked KMS");
720        assert_eq!(
721            keys[0],
722            KeyEntry {
723                address: "address_1".to_string().into(),
724                public_key_base64: STANDARD.encode("public_key_1_base64").into(),
725                public_key: Some("public_key_1".to_string()).into(),
726                key_id: "key_id_1".to_string().into(),
727            }
728        );
729        assert_eq!(
730            keys[1],
731            KeyEntry {
732                address: "address_2".to_string().into(),
733                public_key_base64: STANDARD.encode("public_key_2_base64").into(),
734                public_key: Some("public_key_2".to_string()).into(),
735                key_id: "key_id_2".to_string().into(),
736            }
737        );
738    }
739}