Skip to main content

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