Skip to main content

coven_storage/
restore_storage.rs

1//! The storage part of a restore code (format.md D13).
2
3use crate::{CloudProvider, S3Credentials, StorageConfig, StorageError, StorageFailure};
4use coven_crypto::SecretBytes;
5use coven_format::code_fields::{pack, FieldReader};
6
7/// What a restore code tells a new device about storage: the store's
8/// location and, on S3, the member's key when the person's own device sends
9/// the one it holds (§13.1). Never OAuth tokens or a share token.
10#[derive(Clone)]
11pub struct RestoreStorage {
12    /// The store's location.
13    pub location: StorageConfig,
14    /// The member's S3 key; only an S3 location carries one.
15    pub s3_key: Option<S3Credentials>,
16}
17
18impl RestoreStorage {
19    /// The location with no key: what the code a person writes down, and the
20    /// copy saved for the person's next devices, carry (§13.1).
21    pub fn location_only(location: StorageConfig) -> Self {
22        Self { location, s3_key: None }
23    }
24
25    /// Pack the location and, on S3, whether a key follows and the key
26    /// (format.md D13), in zeroizing storage.
27    pub fn encode(&self) -> Result<SecretBytes, StorageError> {
28        self.location.validate()?;
29        let s3 = self.location.provider() == CloudProvider::S3;
30        match &self.s3_key {
31            Some(_) if !s3 => {
32                return Err(StorageFailure::InvalidConfiguration
33                    .with_source("an S3 key restores only an S3 location"))
34            }
35            Some(key) => key.validate()?,
36            None => {}
37        }
38        Ok(pack(|out| {
39            self.location.pack(out)?;
40            if s3 {
41                out.option(self.s3_key.as_ref(), |out, key| key.pack(out))?;
42            }
43            Ok(())
44        })?)
45    }
46
47    /// Decode a packed payload, checking the location and any key.
48    pub fn decode(bytes: &[u8]) -> Result<Self, StorageError> {
49        let mut input = FieldReader::new(bytes)?;
50        let location = StorageConfig::unpack(&mut input)?;
51        let s3_key = match location.provider() {
52            CloudProvider::S3 => input.option(S3Credentials::unpack)?,
53            _ => None,
54        };
55        input.finish()?;
56        Ok(Self { location, s3_key })
57    }
58}
59
60#[cfg(test)]
61#[path = "restore_storage_tests.rs"]
62mod tests;