Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Support hex encoded secret key for --node-key #7052

Merged
merged 3 commits into from
Sep 11, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 50 additions & 30 deletions client/cli/src/params/node_key_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use sc_network::config::NodeKeyConfig;
use sc_network::{config::identity::ed25519, config::NodeKeyConfig};
use sp_core::H256;
use std::{path::PathBuf, str::FromStr};
use std::{path::{PathBuf, Path}, str::FromStr, fs::File, io::Read};
use structopt::StructOpt;

use crate::arg_enums::NodeKeyType;
Expand Down Expand Up @@ -83,7 +83,7 @@ pub struct NodeKeyParams {
/// as follows:
///
/// `ed25519`:
/// The file must contain an unencoded 32 byte Ed25519 secret key.
/// The file must contain an unencoded 32 byte or hex encoded Ed25519 secret key.
///
/// If the file does not exist, it is created with a newly generated secret key of
/// the chosen type.
Expand All @@ -99,13 +99,10 @@ impl NodeKeyParams {
NodeKeyType::Ed25519 => {
let secret = if let Some(node_key) = self.node_key.as_ref() {
parse_ed25519_secret(node_key)?
} else if let Some(path) = &self.node_key_file {
open_ed25519_secret(&path)?
} else {
let path = self
.node_key_file
.clone()
.unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE));

sc_network::config::Secret::File(path)
sc_network::config::Secret::File(net_config_dir.join(NODE_KEY_ED25519_FILE))
};

NodeKeyConfig::Ed25519(secret)
Expand All @@ -124,16 +121,36 @@ fn parse_ed25519_secret(hex: &str) -> error::Result<sc_network::config::Ed25519S
H256::from_str(&hex)
.map_err(invalid_node_key)
.and_then(|bytes| {
sc_network::config::identity::ed25519::SecretKey::from_bytes(bytes)
ed25519::SecretKey::from_bytes(bytes)
.map(sc_network::config::Secret::Input)
.map_err(invalid_node_key)
})
}

/// Open a file and try to parse it as hex or raw bytes Ed25519 secret key into a `sc_network::Secret`.
fn open_ed25519_secret(file: &Path) -> error::Result<sc_network::config::Ed25519Secret> {
let mut file = File::open(file)
.map_err(|e| format!("Failed to open ed25519 secret: {:?}", e))?;

let mut content = Vec::new();
file.read_to_end(&mut content).map_err(|e| format!("Failed to read ed25519 secret: {:?}", e))?;

let secret_key = match String::from_utf8(content.clone())
.ok()
.and_then(|s| H256::from_str(&s).ok())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please check content.len() to be either 32 or 64 instead? I don't really trust H256::from_str to be future-proof.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is under our "control" and the docs already say that they do exactly these checks: https://docs.rs/fixed-hash/0.6.1/src/fixed_hash/hash.rs.html#595

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is under our "control"

But we're not an omniscient collective brain.
I totally imagine in a scenario where in a year from now someone changes H256 to automatically add 0s if the value is not long enough, or something like that, and suddenly private keys that only contain valid hexadecimal characters will parse incorrectly.

{
Some(bytes) => ed25519::SecretKey::from_bytes(bytes),
None => ed25519::SecretKey::from_bytes(content),
Copy link
Contributor

@tomaka tomaka Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about something like this, then?

Suggested change
Some(bytes) => ed25519::SecretKey::from_bytes(bytes),
None => ed25519::SecretKey::from_bytes(content),
Some(bytes) => { debug_assert_eq!(content.len(), 64); ed25519::SecretKey::from_bytes(bytes) },
None => { debug_assert_eq!(content.len(), 32); ed25519::SecretKey::from_bytes(content) },

};

secret_key.map(sc_network::config::Secret::Input).map_err(invalid_node_key)
}

#[cfg(test)]
mod tests {
use super::*;
use sc_network::config::identity::ed25519;
use std::fs;

#[test]
fn test_node_key_config_input() {
Expand Down Expand Up @@ -164,28 +181,31 @@ mod tests {

#[test]
fn test_node_key_config_file() {
fn secret_file(net_config_dir: &PathBuf) -> error::Result<()> {
NodeKeyType::variants().iter().try_for_each(|t| {
let node_key_type = NodeKeyType::from_str(t).unwrap();
let tmp = tempfile::Builder::new().prefix("alice").tempdir()?;
let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf();
let params = NodeKeyParams {
node_key_type,
node_key: None,
node_key_file: Some(file.clone()),
};
params.node_key(net_config_dir).and_then(|c| match c {
NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f))
if node_key_type == NodeKeyType::Ed25519 && f == &file =>
{
Ok(())
}
_ => Err(error::Error::Input("Unexpected node key config".into())),
})
})
fn check_key(file: PathBuf, key: &ed25519::SecretKey) {
let params = NodeKeyParams {
node_key_type: NodeKeyType::Ed25519,
node_key: None,
node_key_file: Some(file),
};

let node_key = params.node_key(&PathBuf::from("not-used")).expect("Creates node key");

match node_key {
NodeKeyConfig::Ed25519(sc_network::config::Secret::Input(ref secret))
if secret.as_ref() == key.as_ref() => {}
_ => panic!("Invalid key"),
}
}

assert!(secret_file(&PathBuf::from_str("x").unwrap()).is_ok());
let tmp = tempfile::Builder::new().prefix("alice").tempdir().expect("Creates tempfile");
let file = tmp.path().join("mysecret").to_path_buf();
let key = ed25519::SecretKey::generate();

fs::write(&file, hex::encode(key.as_ref())).expect("Writes secret key");
check_key(file.clone(), &key);

fs::write(&file, &key).expect("Writes secret key");
check_key(file.clone(), &key);
}

#[test]
Expand Down