nuts_container/
header.rs

1// MIT License
2//
3// Copyright (c) 2022-2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23mod plain_secret;
24mod revision;
25#[cfg(test)]
26mod tests;
27
28use log::{debug, error};
29use nuts_backend::Backend;
30use openssl::error::ErrorStack;
31use plain_secret::PlainSecret;
32use std::fmt;
33use std::ops::DerefMut;
34use thiserror::Error;
35
36use crate::buffer::{BufferError, ToBuffer};
37use crate::cipher::{Cipher, CipherContext, CipherError};
38use crate::header::revision::{Data, Revision};
39use crate::kdf::{Kdf, KdfError};
40use crate::migrate::{MigrationError, Migrator};
41use crate::options::CreateOptions;
42use crate::ossl;
43use crate::password::{PasswordError, PasswordStore};
44use crate::svec::SecureVec;
45
46pub const LATEST_REVISION: u32 = 2;
47
48/// Header related errors.
49#[derive(Debug, Error)]
50pub enum HeaderError {
51    /// A cipher related error
52    #[error(transparent)]
53    Cipher(#[from] CipherError),
54
55    /// A KDF related error
56    #[error(transparent)]
57    Kdf(#[from] KdfError),
58
59    /// A password related error.
60    #[error(transparent)]
61    Password(#[from] PasswordError),
62
63    /// The password is wrong.
64    #[error("the password is wrong")]
65    WrongPassword,
66
67    /// Invalid header revision
68    #[error("invalid header revision, expected {0} but got {1}")]
69    InvalidRevision(u32, u32),
70
71    /// Unknown header revision (from the future)
72    #[error("unknown header revision {0}")]
73    UnknownRevision(u32),
74
75    /// Invalid header, could not validate magic
76    #[error("invalid header")]
77    InvalidHeader,
78
79    /// Invalid service identifeir (sid)
80    #[error("invalid sid")]
81    InvalidSid,
82
83    /// Unexpected service identifier (sid)
84    #[error("unexpected sid, expected {} but got {}",
85        .expected.map_or_else(|| "none".to_string(), |n| n.to_string()),
86        .got.map_or_else(|| "none".to_string(), |n| n.to_string()))]
87    UnexpectedSid {
88        expected: Option<u32>,
89        got: Option<u32>,
90    },
91
92    /// Invalid settings, could not parse backend settings from header.
93    #[error("invalid settings")]
94    InvalidSettings,
95
96    /// Invalid top-id, could not parse top-id from header.
97    #[error("invalid top-id")]
98    InvalidTopId,
99
100    /// Error while (de-) serializing binary data.
101    #[error(transparent)]
102    Buffer(#[from] BufferError),
103
104    /// An error in the OpenSSL library occured.
105    #[error(transparent)]
106    OpenSSL(#[from] ErrorStack),
107
108    /// Errors coming from a migration
109    #[error(transparent)]
110    Migration(#[from] MigrationError),
111}
112
113pub struct Header<'a, B: Backend> {
114    revision: u32,
115    migrator: Migrator<'a>,
116    cipher: Cipher,
117    kdf: Kdf,
118    data: PlainSecret<B>,
119}
120
121impl<'a, B: Backend> Header<'a, B> {
122    pub fn create(
123        options: &CreateOptions,
124        settings: B::Settings,
125    ) -> Result<Header<'a, B>, HeaderError> {
126        let cipher = options.cipher;
127        let mut key = vec![0; cipher.key_len()];
128        let mut iv = vec![0; cipher.iv_len()];
129
130        ossl::rand_bytes(&mut key)?;
131        ossl::rand_bytes(&mut iv)?;
132
133        let kdf = options.kdf.build()?;
134        let (revision, plain_secret) = PlainSecret::create_latest(key.into(), iv.into(), settings)?;
135
136        Ok(Header {
137            revision,
138            migrator: Migrator::default(),
139            cipher,
140            kdf,
141            data: plain_secret,
142        })
143    }
144
145    pub fn read(
146        buf: &[u8],
147        migrator: Migrator<'a>,
148        store: &mut PasswordStore,
149    ) -> Result<Header<'a, B>, HeaderError> {
150        match Revision::get_from_buffer(&mut &buf[..])? {
151            Revision::Rev0(data) => Self::read_rev0(data, migrator, store),
152            Revision::Rev1(data) => Self::read_rev1(data, migrator, store),
153            Revision::Rev2(data) => Self::read_rev2(data, migrator, store),
154        }
155    }
156
157    fn read_rev0(
158        data: Data,
159        migrator: Migrator<'a>,
160        store: &mut PasswordStore,
161    ) -> Result<Header<'a, B>, HeaderError> {
162        let key = Self::create_key(data.cipher, &data.kdf, store)?;
163        let mut ctx = Self::prepare_cipher_ctx(data.cipher, &data.secret);
164
165        let pbuf = ctx.decrypt(&key, &data.iv)?;
166        let plain_secret = PlainSecret::from_buffer_rev0(&mut &pbuf[..])?;
167
168        Ok(Header {
169            revision: 0,
170            migrator,
171            cipher: data.cipher,
172            kdf: data.kdf,
173            data: plain_secret,
174        })
175    }
176
177    fn read_rev1(
178        data: Data,
179        migrator: Migrator<'a>,
180        store: &mut PasswordStore,
181    ) -> Result<Header<'a, B>, HeaderError> {
182        let key = Self::create_key(data.cipher, &data.kdf, store)?;
183        let mut ctx = Self::prepare_cipher_ctx(data.cipher, &data.secret);
184
185        let pbuf = ctx.decrypt(&key, &data.iv)?;
186        let plain_secret = PlainSecret::from_buffer_rev1(&mut &pbuf[..])?;
187
188        Ok(Header {
189            revision: 1,
190            migrator,
191            cipher: data.cipher,
192            kdf: data.kdf,
193            data: plain_secret,
194        })
195    }
196
197    fn read_rev2(
198        data: Data,
199        migrator: Migrator<'a>,
200        store: &mut PasswordStore,
201    ) -> Result<Header<'a, B>, HeaderError> {
202        let key = Self::create_key(data.cipher, &data.kdf, store)?;
203        let mut ctx = Self::prepare_cipher_ctx(data.cipher, &data.secret);
204
205        let pbuf = ctx.decrypt(&key, &data.iv)?;
206        let plain_secret = PlainSecret::from_buffer_rev2(&mut &pbuf[..])?;
207
208        Ok(Header {
209            revision: 2,
210            migrator,
211            cipher: data.cipher,
212            kdf: data.kdf,
213            data: plain_secret,
214        })
215    }
216
217    pub fn write(&self, buf: &mut [u8], store: &mut PasswordStore) -> Result<(), HeaderError> {
218        let mut iv = vec![0; self.cipher.iv_len()];
219        ossl::rand_bytes(&mut iv)?;
220
221        let mut pbuf: SecureVec = vec![].into();
222        self.data.to_buffer(pbuf.deref_mut())?;
223
224        let key = Self::create_key(self.cipher, &self.kdf, store)?;
225        let mut ctx = Self::prepare_cipher_ctx(self.cipher, &pbuf);
226
227        let cbuf = ctx.encrypt(&key, &iv)?;
228        let secret = cbuf.to_vec();
229
230        let rev = match self.data {
231            PlainSecret::Rev0(_) => Revision::new_rev0(self.cipher, iv, self.kdf.clone(), secret),
232            PlainSecret::Rev1(_) => Revision::new_rev1(self.cipher, iv, self.kdf.clone(), secret),
233            PlainSecret::Rev2(_) => Revision::new_rev2(self.cipher, iv, self.kdf.clone(), secret),
234        };
235
236        rev.put_into_buffer(&mut &mut buf[..])
237    }
238
239    pub fn migrate(&mut self) -> Result<(), HeaderError> {
240        if let PlainSecret::Rev0(rev0) = &mut self.data {
241            rev0.migrate(&self.migrator)
242        } else {
243            Ok(())
244        }
245    }
246
247    pub fn revision(&self) -> u32 {
248        self.revision
249    }
250
251    pub fn latest_revision_or_err(&self) -> Result<(), HeaderError> {
252        if self.revision == LATEST_REVISION {
253            Ok(())
254        } else {
255            Err(HeaderError::InvalidRevision(LATEST_REVISION, self.revision))
256        }
257    }
258
259    pub fn cipher(&self) -> Cipher {
260        self.cipher
261    }
262
263    pub fn kdf(&self) -> &Kdf {
264        &self.kdf
265    }
266
267    pub fn set_kdf(&mut self, kdf: Kdf) -> bool {
268        if self.cipher != Cipher::None && kdf != Kdf::None {
269            self.kdf = kdf;
270            true
271        } else {
272            false
273        }
274    }
275
276    pub fn settings(&self) -> &B::Settings {
277        match &self.data {
278            PlainSecret::Rev0(rev0) => &rev0.settings,
279            PlainSecret::Rev1(rev1) => &rev1.settings,
280            PlainSecret::Rev2(rev2) => &rev2.settings,
281        }
282    }
283
284    pub fn key(&self) -> &[u8] {
285        match &self.data {
286            PlainSecret::Rev0(rev0) => &rev0.key,
287            PlainSecret::Rev1(rev1) => &rev1.key,
288            PlainSecret::Rev2(rev2) => &rev2.key,
289        }
290    }
291
292    pub fn iv(&self) -> &[u8] {
293        match &self.data {
294            PlainSecret::Rev0(rev0) => &rev0.iv,
295            PlainSecret::Rev1(rev1) => &rev1.iv,
296            PlainSecret::Rev2(rev2) => &rev2.iv,
297        }
298    }
299
300    pub fn accept_sid_for_create(&self) -> Result<(), HeaderError> {
301        let sid_opt = match &self.data {
302            PlainSecret::Rev0(rev0) => rev0.sid,
303            PlainSecret::Rev1(_) => None,
304            PlainSecret::Rev2(rev2) => rev2.sid,
305        };
306
307        if sid_opt.is_none() {
308            Ok(())
309        } else {
310            Err(HeaderError::UnexpectedSid {
311                expected: None,
312                got: sid_opt,
313            })
314        }
315    }
316
317    pub fn accept_sid_for_open(&self, sid: u32) -> Result<(), HeaderError> {
318        let accecpt = |header_sid| match header_sid {
319            Some(hsid) if hsid == sid => {
320                debug!("sid {} match", sid);
321
322                Ok(())
323            }
324            _ => {
325                error!("sid mismatch, sid: {}, header sid: {:?}", sid, header_sid);
326
327                Err(HeaderError::UnexpectedSid {
328                    expected: Some(sid),
329                    got: header_sid,
330                })
331            }
332        };
333
334        match &self.data {
335            PlainSecret::Rev0(rev0) => accecpt(rev0.sid),
336            PlainSecret::Rev1(_) => {
337                // There is no way to identify a rev 1 service. This is the reason we
338                // put the sid (service identifier) into the header.
339                // Assume that the service rejects invalid data from its super-block.
340                // It's ok to say ok here.
341
342                debug!("rev1 has no sid, say ok");
343
344                Ok(())
345            }
346            PlainSecret::Rev2(rev2) => accecpt(rev2.sid),
347        }
348    }
349
350    pub fn set_sid(&mut self, sid: u32) -> Result<(), HeaderError> {
351        match &mut self.data {
352            PlainSecret::Rev0(_) => panic!("storing a sid into a rev0 header is not supported"),
353            PlainSecret::Rev1(_) => panic!("storing a sid into a rev1 header is not supported"),
354            PlainSecret::Rev2(rev2) => {
355                if sid > 0 {
356                    rev2.sid = Some(sid);
357                    Ok(())
358                } else {
359                    Err(HeaderError::InvalidSid)
360                }
361            }
362        }
363    }
364
365    pub fn top_id(&self) -> Option<&B::Id> {
366        match &self.data {
367            PlainSecret::Rev0(rev0) => rev0.top_id.as_ref(),
368            PlainSecret::Rev1(rev1) => rev1.top_id.as_ref(),
369            PlainSecret::Rev2(rev2) => rev2.top_id.as_ref(),
370        }
371    }
372
373    pub fn set_top_id(&mut self, id: B::Id) {
374        match &mut self.data {
375            PlainSecret::Rev0(_) => panic!("storing a top-id into a rev0 header is not supported"),
376            PlainSecret::Rev1(_) => panic!("storing a top-id into a rev1 header is not supported"),
377            PlainSecret::Rev2(rev2) => rev2.top_id = Some(id),
378        }
379    }
380
381    pub fn set_migrator(&mut self, migrator: Migrator<'a>) {
382        self.migrator = migrator;
383    }
384
385    pub fn convert_to_latest(&mut self, sid: u32) -> bool {
386        self.data.convert_to_latest(sid)
387    }
388
389    fn prepare_cipher_ctx(cipher: Cipher, input: &[u8]) -> CipherContext {
390        let mut ctx = CipherContext::new(cipher);
391
392        ctx.copy_from_slice(input.len(), input);
393
394        ctx
395    }
396
397    fn create_key(
398        cipher: Cipher,
399        kdf: &Kdf,
400        store: &mut PasswordStore,
401    ) -> Result<SecureVec, HeaderError> {
402        if cipher.key_len() > 0 {
403            let password = store.value()?;
404            Ok(kdf.create_key(password, cipher.key_len())?)
405        } else {
406            Ok(vec![].into())
407        }
408    }
409}
410
411impl<'a, B: Backend> fmt::Debug for Header<'a, B> {
412    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
413        fmt.debug_struct("Header")
414            .field("revision", &self.revision)
415            .field("migrator", &self.migrator)
416            .field("cipher", &self.cipher)
417            .field("kdf", &self.kdf)
418            .field("data", &self.data)
419            .finish()
420    }
421}