1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::{fmt, io};

use byteorder::{BigEndian, ByteOrder};

use crate::crypto::checksum;
use crate::crypto::public_key::PublicKeyAlgorithm;
use crate::crypto::sym::SymmetricKeyAlgorithm;
use crate::errors::Result;
use crate::ser::Serialize;
use crate::types::*;

#[derive(Clone, PartialEq, Eq)]
pub struct EncryptedSecretParams {
    /// The encrypted data.
    data: Vec<u8>,
    /// IV.
    iv: Vec<u8>,
    /// The encryption algorithm used.
    encryption_algorithm: SymmetricKeyAlgorithm,
    /// The string-to-key method and its parameters.
    string_to_key: StringToKey,
    /// The identifier for how this data is stored.
    string_to_key_id: u8,
}

impl EncryptedSecretParams {
    pub fn new(
        data: Vec<u8>,
        iv: Vec<u8>,
        alg: SymmetricKeyAlgorithm,
        s2k: StringToKey,
        id: u8,
    ) -> Self {
        assert_ne!(id, 0, "invalid string to key id");
        EncryptedSecretParams {
            data,
            iv,
            encryption_algorithm: alg,
            string_to_key: s2k,
            string_to_key_id: id,
        }
    }

    pub fn data(&self) -> &[u8] {
        &self.data
    }

    pub fn iv(&self) -> &[u8] {
        &self.iv
    }

    pub fn encryption_algorithm(&self) -> SymmetricKeyAlgorithm {
        self.encryption_algorithm
    }

    pub fn string_to_key(&self) -> &StringToKey {
        &self.string_to_key
    }

    pub fn string_to_key_id(&self) -> u8 {
        self.string_to_key_id
    }

    pub fn compare_checksum(&self, other: Option<&[u8]>) -> Result<()> {
        if self.string_to_key_id < 254 {
            if let Some(other) = other {
                ensure_eq!(
                    BigEndian::read_u16(other),
                    checksum::calculate_simple(self.data()),
                    "Invalid checksum"
                );
            } else {
                bail!("Missing checksum");
            }
        } else {
            ensure!(other.is_none(), "Expected no checksum, but found one");
        }

        Ok(())
    }

    pub fn checksum(&self) -> Option<Vec<u8>> {
        if self.string_to_key_id < 254 {
            Some(
                checksum::calculate_simple(self.data())
                    .to_be_bytes()
                    .to_vec(),
            )
        } else {
            None
        }
    }

    pub fn unlock<F>(&self, pw: F, alg: PublicKeyAlgorithm) -> Result<PlainSecretParams>
    where
        F: FnOnce() -> String,
    {
        let key = self
            .string_to_key
            .derive_key(&pw(), self.encryption_algorithm.key_size())?;

        // Actual decryption
        let mut plaintext = self.data.clone();
        self.encryption_algorithm
            .decrypt_with_iv_regular(&key, &self.iv, &mut plaintext)?;

        PlainSecretParams::from_slice(&plaintext, alg)
    }
}

impl Serialize for EncryptedSecretParams {
    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
        writer.write_all(&[self.string_to_key_id])?;

        match self.string_to_key_id {
            0 => panic!("encrypted secret params should not have an unecrypted identifier"),
            1..=253 => {
                writer.write_all(&self.iv)?;
            }
            254..=255 => {
                let s2k = &self.string_to_key;

                writer.write_all(&[self.encryption_algorithm as u8])?;
                s2k.to_writer(writer)?;
                writer.write_all(&self.iv)?;
            }
        }

        writer.write_all(&self.data)?;
        if let Some(cs) = self.checksum() {
            writer.write_all(&cs)?;
        }

        Ok(())
    }
}

impl fmt::Debug for EncryptedSecretParams {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EncryptedSecretParams")
            .field("data", &hex::encode(&self.data))
            .field("checksum", &self.checksum().map(hex::encode))
            .field("iv", &hex::encode(&self.iv))
            .field("encryption_algorithm", &self.encryption_algorithm)
            .field("string_to_key", &self.string_to_key)
            .field("string_to_key_id", &self.string_to_key_id)
            .finish()
    }
}