]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-datastore/src/crypt_config.rs
move Kdf and KeyInfo to pbs_api_types workspace
[proxmox-backup.git] / pbs-datastore / src / crypt_config.rs
CommitLineData
48b4b40b
DM
1//! Wrappers for OpenSSL crypto functions
2//!
f323e906 3//! We use this to encrypt and decrypt data chunks. Cipher is
48b4b40b
DM
4//! AES_256_GCM, which is fast and provides authenticated encryption.
5//!
6//! See the Wikipedia Artikel for [Authenticated
7//! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)
8//! for a short introduction.
f28d9088
WB
9
10use std::io::Write;
11
8acfd15d 12use anyhow::{Error};
48b4b40b 13use openssl::hash::MessageDigest;
f28d9088 14use openssl::pkcs5::pbkdf2_hmac;
ef27200c 15use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode};
f28d9088 16
ea584a75 17pub use pbs_api_types::{CryptMode, Fingerprint};
770a36e5 18
05cdc053 19// openssl::sha::sha256(b"Proxmox Backup Encryption Key Fingerprint")
feb1645f
DM
20/// This constant is used to compute fingerprints.
21const FINGERPRINT_INPUT: [u8; 32] = [
22 110, 208, 239, 119, 71, 31, 255, 77,
23 85, 199, 168, 254, 74, 157, 182, 33,
24 97, 64, 127, 19, 76, 114, 93, 223,
25 48, 153, 45, 37, 236, 69, 237, 38,
26];
84cbdb35 27
48b4b40b
DM
28/// Encryption Configuration with secret key
29///
30/// This structure stores the secret key and provides helpers for
31/// authenticated encryption.
32pub struct CryptConfig {
33 // the Cipher
34 cipher: Cipher,
35 // A secrect key use to provide the chunk digest name space.
cb0eea29
DM
36 id_key: [u8; 32],
37 // Openssl hmac PKey of id_key
38 id_pkey: openssl::pkey::PKey<openssl::pkey::Private>,
48b4b40b
DM
39 // The private key used by the cipher.
40 enc_key: [u8; 32],
41}
42
43impl CryptConfig {
44
45 /// Create a new instance.
46 ///
47 /// We compute a derived 32 byte key using pbkdf2_hmac. This second
48 /// key is used in compute_digest.
49 pub fn new(enc_key: [u8; 32]) -> Result<Self, Error> {
50
cb0eea29 51 let mut id_key = [0u8; 32];
48b4b40b
DM
52
53 pbkdf2_hmac(
54 &enc_key,
55 b"_id_key",
56 10,
57 MessageDigest::sha256(),
58 &mut id_key)?;
59
cb0eea29
DM
60 let id_pkey = openssl::pkey::PKey::hmac(&id_key).unwrap();
61
62 Ok(Self { id_key, id_pkey, enc_key, cipher: Cipher::aes_256_gcm() })
48b4b40b
DM
63 }
64
2aa0bfff
DM
65 /// Expose Cipher
66 pub fn cipher(&self) -> &Cipher {
67 &self.cipher
68 }
69
48b4b40b
DM
70 /// Compute a chunk digest using a secret name space.
71 ///
72 /// Computes an SHA256 checksum over some secret data (derived
73 /// from the secret key) and the provided data. This ensures that
74 /// chunk digest values do not clash with values computed for
75 /// other sectret keys.
76 pub fn compute_digest(&self, data: &[u8]) -> [u8; 32] {
77 let mut hasher = openssl::sha::Sha256::new();
48b4b40b 78 hasher.update(data);
c1ff544e 79 hasher.update(&self.id_key); // at the end, to avoid length extensions attacks
834a2f95 80 hasher.finish()
48b4b40b
DM
81 }
82
cb0eea29
DM
83 pub fn data_signer(&self) -> openssl::sign::Signer {
84 openssl::sign::Signer::new(MessageDigest::sha256(), &self.id_pkey).unwrap()
85 }
86
93205f94
DM
87 /// Compute authentication tag (hmac/sha256)
88 ///
89 /// Computes an SHA256 HMAC using some secret data (derived
90 /// from the secret key) and the provided data.
91 pub fn compute_auth_tag(&self, data: &[u8]) -> [u8; 32] {
cb0eea29 92 let mut signer = self.data_signer();
93205f94
DM
93 signer.update(data).unwrap();
94 let mut tag = [0u8; 32];
95 signer.sign(&mut tag).unwrap();
96 tag
97 }
98
05cdc053 99 pub fn fingerprint(&self) -> Fingerprint {
a303e002 100 Fingerprint::new(self.compute_digest(&FINGERPRINT_INPUT))
05cdc053
FG
101 }
102
a32bd8a5
DM
103 pub fn data_crypter(&self, iv: &[u8; 16], mode: Mode) -> Result<Crypter, Error> {
104 let mut crypter = openssl::symm::Crypter::new(self.cipher, mode, &self.enc_key, Some(iv))?;
c57ec43a
DM
105 crypter.aad_update(b"")?; //??
106 Ok(crypter)
107 }
108
ee8a7e80
DM
109 /// Encrypt data using a random 16 byte IV.
110 ///
111 /// Writes encrypted data to ``output``, Return the used IV and computed MAC.
112 pub fn encrypt_to<W: Write>(
113 &self,
114 data: &[u8],
115 mut output: W,
116 ) -> Result<([u8;16], [u8;16]), Error> {
117
118 let mut iv = [0u8; 16];
119 proxmox::sys::linux::fill_with_random_data(&mut iv)?;
120
121 let mut tag = [0u8; 16];
122
a32bd8a5 123 let mut c = self.data_crypter(&iv, Mode::Encrypt)?;
ee8a7e80
DM
124
125 const BUFFER_SIZE: usize = 32*1024;
126
127 let mut encr_buf = [0u8; BUFFER_SIZE];
128 let max_encoder_input = BUFFER_SIZE - self.cipher.block_size();
129
130 let mut start = 0;
131 loop {
132 let mut end = start + max_encoder_input;
133 if end > data.len() { end = data.len(); }
134 if end > start {
135 let count = c.update(&data[start..end], &mut encr_buf)?;
136 output.write_all(&encr_buf[..count])?;
137 start = end;
138 } else {
139 break;
140 }
141 }
142
143 let rest = c.finalize(&mut encr_buf)?;
144 if rest > 0 { output.write_all(&encr_buf[..rest])?; }
145
146 output.flush()?;
147
148 c.get_tag(&mut tag)?;
149
150 Ok((iv, tag))
151 }
152
c68d2170 153 /// Decompress and decrypt data, verify MAC.
9f83e0f7
DM
154 pub fn decode_compressed_chunk(
155 &self,
156 data: &[u8],
157 iv: &[u8; 16],
158 tag: &[u8; 16],
159 ) -> Result<Vec<u8>, Error> {
bec8498a 160
077a8cae 161 let dec = Vec::with_capacity(1024*1024);
bec8498a 162
077a8cae 163 let mut decompressor = zstd::stream::write::Decoder::new(dec)?;
bec8498a 164
a32bd8a5 165 let mut c = self.data_crypter(iv, Mode::Decrypt)?;
bec8498a 166
077a8cae
DM
167 const BUFFER_SIZE: usize = 32*1024;
168
169 let mut decr_buf = [0u8; BUFFER_SIZE];
170 let max_decoder_input = BUFFER_SIZE - self.cipher.block_size();
171
9f83e0f7 172 let mut start = 0;
077a8cae
DM
173 loop {
174 let mut end = start + max_decoder_input;
175 if end > data.len() { end = data.len(); }
176 if end > start {
177 let count = c.update(&data[start..end], &mut decr_buf)?;
178 decompressor.write_all(&decr_buf[0..count])?;
179 start = end;
180 } else {
181 break;
182 }
183 }
bec8498a 184
9f83e0f7 185 c.set_tag(tag)?;
077a8cae
DM
186 let rest = c.finalize(&mut decr_buf)?;
187 if rest > 0 { decompressor.write_all(&decr_buf[..rest])?; }
bec8498a 188
077a8cae 189 decompressor.flush()?;
bec8498a 190
077a8cae
DM
191 Ok(decompressor.into_inner())
192 }
193
9f83e0f7
DM
194 /// Decrypt data, verify tag.
195 pub fn decode_uncompressed_chunk(
196 &self,
197 data: &[u8],
198 iv: &[u8; 16],
199 tag: &[u8; 16],
200 ) -> Result<Vec<u8>, Error> {
077a8cae
DM
201
202 let decr_data = decrypt_aead(
203 self.cipher,
204 &self.enc_key,
205 Some(iv),
206 b"", //??
9f83e0f7
DM
207 data,
208 tag,
077a8cae
DM
209 )?;
210
211 Ok(decr_data)
48b4b40b
DM
212 }
213}