iota_keys/
keypair_file.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::path::PathBuf;
6
7use anyhow::anyhow;
8use fastcrypto::{
9    encoding::{Encoding, Hex},
10    secp256k1::Secp256k1KeyPair,
11    traits::EncodeDecodeBase64,
12};
13use iota_types::crypto::{AuthorityKeyPair, IotaKeyPair, NetworkKeyPair, ToFromBytes};
14
15/// Write Bech32 encoded `flag || privkey` to file.
16pub fn write_keypair_to_file<P: AsRef<std::path::Path>>(
17    keypair: &IotaKeyPair,
18    path: P,
19) -> anyhow::Result<()> {
20    let contents = keypair.encode().map_err(|e| anyhow!(e))?;
21    std::fs::write(path, contents)?;
22    Ok(())
23}
24
25/// Write Base64 encoded `privkey` to file.
26pub fn write_authority_keypair_to_file<P: AsRef<std::path::Path>>(
27    keypair: &AuthorityKeyPair,
28    path: P,
29) -> anyhow::Result<()> {
30    let contents = keypair.encode_base64();
31    std::fs::write(path, contents)?;
32    Ok(())
33}
34
35/// Read from file as Base64 encoded `privkey` and return a AuthorityKeyPair.
36pub fn read_authority_keypair_from_file<P: AsRef<std::path::Path>>(
37    path: P,
38) -> anyhow::Result<AuthorityKeyPair> {
39    let contents = std::fs::read_to_string(path)?;
40    AuthorityKeyPair::decode_base64(contents.as_str().trim()).map_err(|e| anyhow!(e))
41}
42
43/// Read from file as Bech32 encoded `flag || privkey` and return a IotaKeypair.
44pub fn read_keypair_from_file<P: AsRef<std::path::Path>>(path: P) -> anyhow::Result<IotaKeyPair> {
45    let contents = std::fs::read_to_string(path)?;
46    IotaKeyPair::decode(contents.as_str().trim()).map_err(|e| anyhow!(e))
47}
48
49/// Read from file as Base64 encoded `flag || privkey` and return a
50/// NetworkKeyPair.
51pub fn read_network_keypair_from_file<P: AsRef<std::path::Path>>(
52    path: P,
53) -> anyhow::Result<NetworkKeyPair> {
54    let kp = read_keypair_from_file(path)?;
55    if let IotaKeyPair::Ed25519(kp) = kp {
56        Ok(kp)
57    } else {
58        Err(anyhow!("Invalid scheme for network keypair"))
59    }
60}
61
62/// Read an IotaKeyPair from a file. The content could be any of the following:
63/// - Base64 encoded `flag || privkey` for ECDSA key
64/// - Base64 encoded `privkey` for Raw key
65/// - Bech32 encoded private key prefixed with `iotaprivkey`
66/// - Hex encoded `privkey` for Raw key
67///
68/// If `require_secp256k1` is true, it will return an error if the key is not
69/// Secp256k1.
70pub fn read_key(path: &PathBuf, require_secp256k1: bool) -> Result<IotaKeyPair, anyhow::Error> {
71    if !path.exists() {
72        return Err(anyhow::anyhow!("Key file not found at path: {:?}", path));
73    }
74    let file_contents = std::fs::read_to_string(path)?;
75    let contents = file_contents.as_str().trim();
76
77    // Try base64 encoded IotaKeyPair `flag || privkey`
78    if let Ok(key) = IotaKeyPair::decode_base64(contents) {
79        if require_secp256k1 && !matches!(key, IotaKeyPair::Secp256k1(_)) {
80            return Err(anyhow!("Key is not Secp256k1"));
81        }
82        return Ok(key);
83    }
84
85    // Try base64 encoded Raw Secp256k1 key `privkey`
86    if let Ok(key) = Secp256k1KeyPair::decode_base64(contents) {
87        return Ok(IotaKeyPair::Secp256k1(key));
88    }
89
90    // Try Bech32 encoded 33-byte `flag || private key` starting with `iotaprivkey`A
91    // prefix. This is the format of a private key exported from IOTA Wallet or
92    // iota.keystore.
93    if let Ok(key) = IotaKeyPair::decode(contents) {
94        if require_secp256k1 && !matches!(key, IotaKeyPair::Secp256k1(_)) {
95            return Err(anyhow!("Key is not Secp256k1"));
96        }
97        return Ok(key);
98    }
99
100    // Try hex encoded Raw key `privkey`
101    if let Ok(bytes) = Hex::decode(contents).map_err(|e| anyhow!("Error decoding hex: {:?}", e)) {
102        if let Ok(key) = Secp256k1KeyPair::from_bytes(&bytes) {
103            return Ok(IotaKeyPair::Secp256k1(key));
104        }
105    }
106
107    Err(anyhow!("Error decoding key from {:?}", path))
108}