Skip to content

Commit

Permalink
age: Remove EncryptorType
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Jul 29, 2024
1 parent 219ac41 commit 118d9d0
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 29 deletions.
4 changes: 4 additions & 0 deletions age/i18n/en-US/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down Expand Up @@ -57,6 +59,8 @@ err-header-mac-invalid = Header MAC is invalid
err-key-decryption = Failed to decrypt an encrypted key
err-mixed-recipient-passphrase = {-scrypt-recipient} can't be used with other recipients.
err-no-matching-keys = No matching keys found
err-unknown-format = Unknown {-age} format.
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/es-AR/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/fr/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/it/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/ru/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/zh-CN/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
2 changes: 2 additions & 0 deletions age/i18n/zh-TW/age.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
-age = age
-rage = rage
-scrypt-recipient = scrypt::Recipient
-openssh = OpenSSH
-ssh-keygen = ssh-keygen
-ssh-rsa = ssh-rsa
Expand Down
8 changes: 8 additions & 0 deletions age/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ pub enum EncryptError {
/// The plugin's binary name.
binary_name: String,
},
/// [`scrypt::Recipient`] was mixed with other recipient types.
///
/// [`scrypt::Recipient`]: crate::scrypt::Recipient
MixedRecipientAndPassphrase,
/// Errors from a plugin.
#[cfg(feature = "plugin")]
#[cfg_attr(docsrs, doc(cfg(feature = "plugin")))]
Expand All @@ -131,6 +135,7 @@ impl Clone for EncryptError {
Self::MissingPlugin { binary_name } => Self::MissingPlugin {
binary_name: binary_name.clone(),
},
Self::MixedRecipientAndPassphrase => Self::MixedRecipientAndPassphrase,
#[cfg(feature = "plugin")]
Self::Plugin(e) => Self::Plugin(e.clone()),
}
Expand All @@ -147,6 +152,9 @@ impl fmt::Display for EncryptError {
wlnfl!(f, "err-missing-plugin", plugin_name = binary_name.as_str())?;
wfl!(f, "rec-missing-plugin")
}
EncryptError::MixedRecipientAndPassphrase => {
wfl!(f, "err-mixed-recipient-passphrase")
}
#[cfg(feature = "plugin")]
EncryptError::Plugin(errors) => match &errors[..] {
[] => unreachable!(),
Expand Down
18 changes: 14 additions & 4 deletions age/src/format.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! The age file format.
use age_core::format::Stanza;
use std::io::{self, BufRead, Read, Write};

use age_core::format::{grease_the_joint, Stanza};

use crate::{
error::DecryptError,
primitives::{HmacKey, HmacWriter},
scrypt,
scrypt, EncryptError,
};

#[cfg(feature = "async")]
Expand All @@ -33,21 +34,30 @@ pub(crate) struct HeaderV1 {
}

impl HeaderV1 {
pub(crate) fn new(recipients: Vec<Stanza>, mac_key: HmacKey) -> Self {
pub(crate) fn new(recipients: Vec<Stanza>, mac_key: HmacKey) -> Result<Self, EncryptError> {
let mut header = HeaderV1 {
recipients,
mac: [0; 32],
encoded_bytes: None,
};

if header.no_scrypt() {
// Keep the joint well oiled!
header.recipients.push(grease_the_joint());
}

if !header.is_valid() {
return Err(EncryptError::MixedRecipientAndPassphrase);
}

let mut mac = HmacWriter::new(mac_key);
cookie_factory::gen(write::header_v1_minus_mac(&header), &mut mac)
.expect("can serialize Header into HmacWriter");
header
.mac
.copy_from_slice(mac.finalize().into_bytes().as_slice());

header
Ok(header)
}

pub(crate) fn verify_mac(&self, mac_key: HmacKey) -> Result<(), hmac::digest::MacError> {
Expand Down
39 changes: 14 additions & 25 deletions age/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Encryption and decryption routines for age.
use age_core::{format::grease_the_joint, secrecy::SecretString};
use age_core::secrecy::SecretString;
use rand::{rngs::OsRng, RngCore};
use std::io::{self, BufRead, Read, Write};

Expand Down Expand Up @@ -45,24 +45,18 @@ impl Nonce {
}
}

/// Handles the various types of age encryption.
enum EncryptorType {
/// Encryption to a list of recipients identified by keys.
Keys(Vec<Box<dyn Recipient + Send>>),
/// Encryption to a passphrase.
Passphrase(SecretString),
}

/// Encryptor for creating an age file.
pub struct Encryptor(EncryptorType);
pub struct Encryptor {
recipients: Vec<Box<dyn Recipient + Send>>,
}

impl Encryptor {
/// Constructs an `Encryptor` that will create an age file encrypted to a list of
/// recipients.
///
/// Returns `None` if no recipients were provided.
pub fn with_recipients(recipients: Vec<Box<dyn Recipient + Send>>) -> Option<Self> {
(!recipients.is_empty()).then_some(Encryptor(EncryptorType::Keys(recipients)))
(!recipients.is_empty()).then_some(Encryptor { recipients })
}

/// Returns an `Encryptor` that will create an age file encrypted with a passphrase.
Expand All @@ -74,29 +68,24 @@ impl Encryptor {
///
/// [`x25519::Identity`]: crate::x25519::Identity
pub fn with_user_passphrase(passphrase: SecretString) -> Self {
Encryptor(EncryptorType::Passphrase(passphrase))
Encryptor {
recipients: vec![Box::new(scrypt::Recipient::new(passphrase))],
}
}

/// Creates the header for this age file.
fn prepare_header(self) -> Result<(Header, Nonce, PayloadKey), EncryptError> {
let file_key = new_file_key();

let recipients = match self.0 {
EncryptorType::Keys(recipients) => {
let mut stanzas = Vec::with_capacity(recipients.len() + 1);
for recipient in recipients {
stanzas.append(&mut recipient.wrap_file_key(&file_key)?);
}
// Keep the joint well oiled!
stanzas.push(grease_the_joint());
stanzas
}
EncryptorType::Passphrase(passphrase) => {
scrypt::Recipient::new(passphrase).wrap_file_key(&file_key)?
let recipients = {
let mut stanzas = Vec::with_capacity(self.recipients.len() + 1);
for recipient in self.recipients {
stanzas.append(&mut recipient.wrap_file_key(&file_key)?);
}
stanzas
};

let header = HeaderV1::new(recipients, mac_key(&file_key));
let header = HeaderV1::new(recipients, mac_key(&file_key))?;
let nonce = Nonce::random();
let payload_key = v1_payload_key(&file_key, &header, &nonce).expect("MAC is correct");

Expand Down

0 comments on commit 118d9d0

Please sign in to comment.