src/bin/proxmox-backup-client.rs - key API: pass kdf parameter
Allow to store keys without password.
This commit is contained in:
parent
9b06db4563
commit
181f097af9
@ -57,13 +57,44 @@ impl KeyDerivationConfig {
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct KeyConfig {
|
pub struct KeyConfig {
|
||||||
kdf: Option<KeyDerivationConfig>,
|
pub kdf: Option<KeyDerivationConfig>,
|
||||||
#[serde(with = "proxmox::tools::serde::date_time_as_rfc3339")]
|
#[serde(with = "proxmox::tools::serde::date_time_as_rfc3339")]
|
||||||
created: DateTime<Local>,
|
pub created: DateTime<Local>,
|
||||||
#[serde(with = "proxmox::tools::serde::bytes_as_base64")]
|
#[serde(with = "proxmox::tools::serde::bytes_as_base64")]
|
||||||
data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn store_key_config(
|
||||||
|
path: &std::path::Path,
|
||||||
|
replace: bool,
|
||||||
|
key_config: KeyConfig,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let data = serde_json::to_string(&key_config)?;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
try_block!({
|
||||||
|
if replace {
|
||||||
|
let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
|
||||||
|
crate::tools::file_set_contents(&path, data.as_bytes(), Some(mode))?;
|
||||||
|
} else {
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
|
||||||
|
let mut file = std::fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.mode(0o0600)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&path)?;
|
||||||
|
|
||||||
|
file.write_all(data.as_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}).map_err(|err: Error| format_err!("Unable to create file {:?} - {}", path, err))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn store_key_with_passphrase(
|
pub fn store_key_with_passphrase(
|
||||||
path: &std::path::Path,
|
path: &std::path::Path,
|
||||||
@ -104,37 +135,11 @@ pub fn store_key_with_passphrase(
|
|||||||
|
|
||||||
let created = Local.timestamp(Local::now().timestamp(), 0);
|
let created = Local.timestamp(Local::now().timestamp(), 0);
|
||||||
|
|
||||||
|
store_key_config(path, replace, KeyConfig {
|
||||||
let key_config = KeyConfig {
|
|
||||||
kdf: Some(kdf),
|
kdf: Some(kdf),
|
||||||
created,
|
created,
|
||||||
data: enc_data,
|
data: enc_data,
|
||||||
};
|
})
|
||||||
|
|
||||||
let data = serde_json::to_string(&key_config)?;
|
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
try_block!({
|
|
||||||
if replace {
|
|
||||||
let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
|
|
||||||
crate::tools::file_set_contents(&path, data.as_bytes(), Some(mode))?;
|
|
||||||
} else {
|
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
|
||||||
|
|
||||||
let mut file = std::fs::OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.mode(0o0600)
|
|
||||||
.create_new(true)
|
|
||||||
.open(&path)?;
|
|
||||||
|
|
||||||
file.write_all(data.as_bytes())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}).map_err(|err: Error| format_err!("Unable to create file {:?} - {}", path, err))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_and_decrtypt_key(path: &std::path::Path, passphrase: fn() -> Result<Vec<u8>, Error>) -> Result<Vec<u8>, Error> {
|
pub fn load_and_decrtypt_key(path: &std::path::Path, passphrase: fn() -> Result<Vec<u8>, Error>) -> Result<Vec<u8>, Error> {
|
||||||
|
@ -812,18 +812,34 @@ fn key_create(
|
|||||||
let path = tools::required_string_param(¶m, "path")?;
|
let path = tools::required_string_param(¶m, "path")?;
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
|
|
||||||
// always read from tty
|
let kdf = param["kdf"].as_str().unwrap_or("scrypt");
|
||||||
if !crate::tools::tty::stdin_isatty() {
|
|
||||||
bail!("unable to read passphrase - no tty");
|
|
||||||
}
|
|
||||||
|
|
||||||
let password = crate::tools::tty::read_password("Encryption Key Password: ")?;
|
|
||||||
|
|
||||||
let key = proxmox::sys::linux::random_data(32)?;
|
let key = proxmox::sys::linux::random_data(32)?;
|
||||||
|
|
||||||
store_key_with_passphrase(&path, &key, &password, false)?;
|
if kdf == "scrypt" {
|
||||||
|
// always read passphrase from tty
|
||||||
|
if !crate::tools::tty::stdin_isatty() {
|
||||||
|
bail!("unable to read passphrase - no tty");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Null)
|
let password = crate::tools::tty::read_password("Encryption Key Password: ")?;
|
||||||
|
|
||||||
|
store_key_with_passphrase(&path, &key, &password, false)?;
|
||||||
|
|
||||||
|
Ok(Value::Null)
|
||||||
|
} else if kdf == "none" {
|
||||||
|
let created = Local.timestamp(Local::now().timestamp(), 0);
|
||||||
|
|
||||||
|
store_key_config(&path, false, KeyConfig {
|
||||||
|
kdf: None,
|
||||||
|
created,
|
||||||
|
data: key,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::Null)
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -836,6 +852,8 @@ fn key_change_passphrase(
|
|||||||
let path = tools::required_string_param(¶m, "path")?;
|
let path = tools::required_string_param(¶m, "path")?;
|
||||||
let path = PathBuf::from(path);
|
let path = PathBuf::from(path);
|
||||||
|
|
||||||
|
let kdf = param["kdf"].as_str().unwrap_or("scrypt");
|
||||||
|
|
||||||
// we need a TTY to query the new password
|
// we need a TTY to query the new password
|
||||||
if !crate::tools::tty::stdin_isatty() {
|
if !crate::tools::tty::stdin_isatty() {
|
||||||
bail!("unable to change passphrase - no tty");
|
bail!("unable to change passphrase - no tty");
|
||||||
@ -843,30 +861,54 @@ fn key_change_passphrase(
|
|||||||
|
|
||||||
let key = load_and_decrtypt_key(&path, get_encryption_key_password)?;
|
let key = load_and_decrtypt_key(&path, get_encryption_key_password)?;
|
||||||
|
|
||||||
let new_pw = String::from_utf8(crate::tools::tty::read_password("New Password: ")?)?;
|
if kdf == "scrypt" {
|
||||||
let verify_pw = String::from_utf8(crate::tools::tty::read_password("Verify Password: ")?)?;
|
|
||||||
|
|
||||||
if new_pw != verify_pw {
|
let new_pw = String::from_utf8(crate::tools::tty::read_password("New Password: ")?)?;
|
||||||
bail!("Password verification fail!");
|
let verify_pw = String::from_utf8(crate::tools::tty::read_password("Verify Password: ")?)?;
|
||||||
|
|
||||||
|
if new_pw != verify_pw {
|
||||||
|
bail!("Password verification fail!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_pw.len() < 5 {
|
||||||
|
bail!("Password is too short!");
|
||||||
|
}
|
||||||
|
|
||||||
|
store_key_with_passphrase(&path, &key, new_pw.as_bytes(), true)?;
|
||||||
|
|
||||||
|
Ok(Value::Null)
|
||||||
|
} else if kdf == "none" {
|
||||||
|
// fixme: keep original creation time, add modified timestamp ??
|
||||||
|
let created = Local.timestamp(Local::now().timestamp(), 0);
|
||||||
|
|
||||||
|
store_key_config(&path, true, KeyConfig {
|
||||||
|
kdf: None,
|
||||||
|
created,
|
||||||
|
data: key,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::Null)
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_pw.len() < 5 {
|
|
||||||
bail!("Password is too short!");
|
|
||||||
}
|
|
||||||
|
|
||||||
store_key_with_passphrase(&path, &key, new_pw.as_bytes(), true)?;
|
|
||||||
|
|
||||||
Ok(Value::Null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_mgmt_cli() -> CliCommandMap {
|
fn key_mgmt_cli() -> CliCommandMap {
|
||||||
|
|
||||||
|
let kdf_schema: Arc<Schema> = Arc::new(
|
||||||
|
StringSchema::new("Key derivation function. Choose 'none' to store the key unecrypted.")
|
||||||
|
.format(Arc::new(ApiStringFormat::Enum(&["scrypt", "none"])))
|
||||||
|
.default("scrypt")
|
||||||
|
.into()
|
||||||
|
);
|
||||||
|
|
||||||
// fixme: change-passphrase, import, export, list
|
// fixme: change-passphrase, import, export, list
|
||||||
let key_create_cmd_def = CliCommand::new(
|
let key_create_cmd_def = CliCommand::new(
|
||||||
ApiMethod::new(
|
ApiMethod::new(
|
||||||
key_create,
|
key_create,
|
||||||
ObjectSchema::new("Create a new encryption key.")
|
ObjectSchema::new("Create a new encryption key.")
|
||||||
.required("path", StringSchema::new("File system path."))
|
.required("path", StringSchema::new("File system path."))
|
||||||
|
.optional("kdf", kdf_schema.clone())
|
||||||
))
|
))
|
||||||
.arg_param(vec!["path"])
|
.arg_param(vec!["path"])
|
||||||
.completion_cb("path", tools::complete_file_name);
|
.completion_cb("path", tools::complete_file_name);
|
||||||
@ -876,6 +918,7 @@ fn key_mgmt_cli() -> CliCommandMap {
|
|||||||
key_change_passphrase,
|
key_change_passphrase,
|
||||||
ObjectSchema::new("Change the passphrase required to decrypt the key.")
|
ObjectSchema::new("Change the passphrase required to decrypt the key.")
|
||||||
.required("path", StringSchema::new("File system path."))
|
.required("path", StringSchema::new("File system path."))
|
||||||
|
.optional("kdf", kdf_schema.clone())
|
||||||
))
|
))
|
||||||
.arg_param(vec!["path"])
|
.arg_param(vec!["path"])
|
||||||
.completion_cb("path", tools::complete_file_name);
|
.completion_cb("path", tools::complete_file_name);
|
||||||
|
Loading…
Reference in New Issue
Block a user