1 //! Store Tape encryptions keys
3 //! This module can store 256bit encryption keys for tape backups,
4 //! indexed by key fingerprint.
6 //! We store the plain key (unencrypted), as well as a encrypted
7 //! version protected by password (see struct `KeyConfig`)
9 //! Tape backups store the password protected version on tape, so that
10 //! it is possible to restore the key from tape if you know the
13 use std
::collections
::HashMap
;
15 use anyhow
::{bail, Error}
;
16 use serde
::{Deserialize, Serialize}
;
18 use proxmox
::tools
::fs
::file_read_optional_string
;
19 use pbs_api_types
::Fingerprint
;
21 use crate::key_config
::KeyConfig
;
22 use crate::{open_backup_lockfile, replace_secret_config, replace_backup_config}
;
25 use serde
::{self, Deserialize, Serializer, Deserializer}
;
30 ) -> Result
<S
::Ok
, S
::Error
>
34 let s
= proxmox
::tools
::digest_to_hex(csum
);
35 serializer
.serialize_str(&s
)
38 pub fn deserialize
<'de
, D
>(
40 ) -> Result
<[u8; 32], D
::Error
>
44 let s
= String
::deserialize(deserializer
)?
;
45 proxmox
::tools
::hex_to_digest(&s
).map_err(serde
::de
::Error
::custom
)
49 /// Store Hardware Encryption keys (plain, unprotected keys)
50 #[derive(Deserialize, Serialize)]
51 pub struct EncryptionKeyInfo
{
52 /// Key fingerprint (we verify the fingerprint on load)
53 pub fingerprint
: Fingerprint
,
54 /// The plain encryption key
55 #[serde(with = "hex_key")]
59 impl EncryptionKeyInfo
{
60 pub fn new(key
: [u8; 32], fingerprint
: Fingerprint
) -> Self {
61 Self { fingerprint, key }
65 pub const TAPE_KEYS_FILENAME
: &str = "/etc/proxmox-backup/tape-encryption-keys.json";
66 pub const TAPE_KEY_CONFIG_FILENAME
: &str = "/etc/proxmox-backup/tape-encryption-key-config.json";
67 pub const TAPE_KEYS_LOCKFILE
: &str = "/etc/proxmox-backup/.tape-encryption-keys.lck";
69 /// Load tape encryption keys (plain, unprotected keys)
70 pub fn load_keys() -> Result
<(HashMap
<Fingerprint
, EncryptionKeyInfo
>, [u8;32]), Error
> {
72 let content
= file_read_optional_string(TAPE_KEYS_FILENAME
)?
;
73 let content
= content
.unwrap_or_else(|| String
::from("[]"));
75 let digest
= openssl
::sha
::sha256(content
.as_bytes());
77 let key_list
: Vec
<EncryptionKeyInfo
> = serde_json
::from_str(&content
)?
;
79 let mut map
= HashMap
::new();
81 for item
in key_list
{
82 let key_config
= KeyConfig
::without_password(item
.key
)?
; // to compute fingerprint
83 let expected_fingerprint
= key_config
.fingerprint
.unwrap();
84 if item
.fingerprint
!= expected_fingerprint
{
86 "inconsistent fingerprint ({} != {})",
92 if map
.insert(item
.fingerprint
.clone(), item
).is_some() {
93 bail
!("found duplicate fingerprint");
100 /// Load tape encryption key configurations (password protected keys)
101 pub fn load_key_configs() -> Result
<(HashMap
<Fingerprint
, KeyConfig
>, [u8;32]), Error
> {
103 let content
= file_read_optional_string(TAPE_KEY_CONFIG_FILENAME
)?
;
104 let content
= content
.unwrap_or_else(|| String
::from("[]"));
106 let digest
= openssl
::sha
::sha256(content
.as_bytes());
108 let key_list
: Vec
<KeyConfig
> = serde_json
::from_str(&content
)?
;
110 let mut map
= HashMap
::new();
112 for key_config
in key_list
{
113 match key_config
.fingerprint
{
114 Some(ref fingerprint
) => {
115 if map
.insert(fingerprint
.clone(), key_config
).is_some() {
116 bail
!("found duplicate fingerprint");
119 None
=> bail
!("missing fingerprint"),
126 /// Store tape encryption keys (plain, unprotected keys)
128 /// The file is only accessible by user root (mode 0600).
129 pub fn save_keys(map
: HashMap
<Fingerprint
, EncryptionKeyInfo
>) -> Result
<(), Error
> {
131 let mut list
= Vec
::new();
133 for (_fp
, item
) in map
{
137 let raw
= serde_json
::to_string_pretty(&list
)?
;
138 replace_secret_config(TAPE_KEYS_FILENAME
, raw
.as_bytes())
141 /// Store tape encryption key configurations (password protected keys)
142 pub fn save_key_configs(map
: HashMap
<Fingerprint
, KeyConfig
>) -> Result
<(), Error
> {
144 let mut list
= Vec
::new();
146 for (_fp
, item
) in map
{
150 let raw
= serde_json
::to_string_pretty(&list
)?
;
151 replace_backup_config(TAPE_KEY_CONFIG_FILENAME
, raw
.as_bytes())
156 /// Get the lock, load both files, insert the new key, store files.
157 pub fn insert_key(key
: [u8;32], key_config
: KeyConfig
, force
: bool
) -> Result
<(), Error
> {
159 let _lock
= open_backup_lockfile(TAPE_KEYS_LOCKFILE
, None
, true)?
;
161 let (mut key_map
, _
) = load_keys()?
;
162 let (mut config_map
, _
) = load_key_configs()?
;
164 let fingerprint
= match key_config
.fingerprint
.clone() {
165 Some(fingerprint
) => fingerprint
,
166 None
=> bail
!("missing encryption key fingerprint - internal error"),
170 if config_map
.get(&fingerprint
).is_some() {
171 bail
!("encryption key '{}' already exists.", fingerprint
);
175 let item
= EncryptionKeyInfo
::new(key
, fingerprint
.clone());
176 key_map
.insert(fingerprint
.clone(), item
);
179 config_map
.insert(fingerprint
.clone(), key_config
);
180 save_key_configs(config_map
)?
;
185 // shell completion helper
186 /// Complete tape encryption key fingerprints
187 pub fn complete_key_fingerprint(_arg
: &str, _param
: &HashMap
<String
, String
>) -> Vec
<String
> {
188 let data
= match load_key_configs() {
189 Ok((data
, _digest
)) => data
,
190 Err(_
) => return Vec
::new(),
193 data
.keys().map(|fp
| fp
.signature()).collect()