Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/tw-api/msg-codes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ pack.need_key
pack.need_passphrase
pack.no_such_item
pack.not_base64
pack.not_encrypted
pack.passphrase_short
pack.schema_too_new
pack.too_large
Expand Down
26 changes: 23 additions & 3 deletions crates/tw-pack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@ pub enum PackError {
MissingEntry { name: String },
#[error("{}", self.msg())]
TooLarge { limit: usize },
/// 要求加密的地方读到了一个没加密的包([`read_strict`])
#[error("{}", self.msg())]
NotEncrypted,
}

impl PackError {
Expand Down Expand Up @@ -347,6 +350,10 @@ impl PackError {
"pack.missing_entry", name = name =>
"This configuration pack has no {name}."
),
PackError::NotEncrypted => msg!(
"pack.not_encrypted" =>
"This configuration pack is not encrypted, though it has to be, so it was not opened."
),
PackError::TooLarge { limit } => msg!(
"pack.too_large", limit = limit / (1024 * 1024) =>
"This configuration pack is larger than {limit} MiB."
Expand Down Expand Up @@ -452,8 +459,9 @@ fn work_factor() -> u8 {
}
}

/// 解开时最多接受多大的工作量。比写的时候大一些:将来调高了,旧版本照样读得了新包
const MAX_WORK_FACTOR: u8 = 22;
/// 解开时最多接受多大的工作量:**和写的时候一样**(同步位置的 keyring 也是这个上限)。一个
/// 不可信的包能指定工作量,放宽了就是让它一打开就占掉几个 GB 内存(`2^22` 约 4 GiB)
const MAX_WORK_FACTOR: u8 = 18;

/// **只给测试用**:把口令加密的工作量调低,测试不用每次等一秒。0 是不改
#[doc(hidden)]
Expand All @@ -466,7 +474,19 @@ pub fn is_encrypted(bytes: &[u8]) -> bool {
bytes.starts_with(b"age-encryption.org/")
}

/// 读一个配置包。
/// 同 [`read`],**加密得和给的钥匙对得上**:给了口令或密钥([`Unseal::Passphrase`] /
/// [`Unseal::Identity`])时,没加密的包不收([`PackError::NotEncrypted`])。同步位置开着加密
/// 时用它:一个照收的明文版本,等于对那个位置有写权限的谁都能把配置写进这台设备。不给
/// ([`Unseal::None`])时和 [`read`] 一样
pub fn read_strict(bytes: &[u8], unseal: Unseal) -> Result<Pack, PackError> {
if !matches!(unseal, Unseal::None) && !is_encrypted(bytes) {
return Err(PackError::NotEncrypted);
}
read(bytes, unseal)
}

/// 读一个配置包。**给了钥匙也照收明文的包**:导入用户自己挑的文件时,加没加密都行;要求加密
/// 的地方用 [`read_strict`]
pub fn read(bytes: &[u8], unseal: Unseal) -> Result<Pack, PackError> {
if bytes.len() > MAX_BYTES {
return Err(PackError::TooLarge { limit: MAX_BYTES });
Expand Down
31 changes: 31 additions & 0 deletions crates/tw-pack/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,34 @@ fn origins_keep_their_words() {
}
assert_eq!(PackOrigin::from_slug("nope"), PackOrigin::Other);
}

/// 要求加密的地方:给了钥匙就不收明文;加密的照常解;没给钥匙时和 `read` 一样
#[test]
fn a_strict_read_refuses_a_plain_pack_when_a_key_is_given() {
fast();
let pack = sample();
let plain = write(&pack, Seal::None).unwrap();
let id = age::x25519::Identity::generate();
let sealed = write(&pack, Seal::Recipient(&id.to_public())).unwrap();
assert_eq!(
read_strict(&plain, Unseal::Identity(&id)).unwrap_err(),
PackError::NotEncrypted
);
assert_eq!(
read_strict(&plain, Unseal::Passphrase("whatever it is")).unwrap_err(),
PackError::NotEncrypted
);
assert_eq!(PackError::NotEncrypted.msg().code, "pack.not_encrypted");
assert_eq!(
read_strict(&sealed, Unseal::Identity(&id))
.unwrap()
.config_yaml,
pack.config_yaml
);
assert_eq!(
read_strict(&plain, Unseal::None).unwrap().config_yaml,
pack.config_yaml
);
// `read` 照收:导入的是用户自己挑的文件
assert!(read(&plain, Unseal::Identity(&id)).is_ok());
}
Loading