Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6746a41
core: KeyMaterialError::WeakKey, for a key an algorithm's specificati…
dghgit Sep 8, 2026
4e573e6
cli: block-mode plumbing takes the block length as a const parameter …
dghgit Sep 8, 2026
3ce0987
core: ElectronicCodeBook::ENCRYPTION_APPROVED (default true) marks a …
dghgit Sep 8, 2026
7a0e425
modes: the Encrypting direction of Ecb, Cbc, Cfb, Cfb8 and Ctr assert…
dghgit Sep 8, 2026
6d1656c
tdes: add bouncycastle-tdes, a constant-time table-free TDEA engine (…
dghgit Sep 8, 2026
b2d327e
tdes: drop the summary.md reviewer-orientation doc
dghgit Sep 19, 2026
eeebbf2
mem_usage_benches: fence bench_tdes_mem_usage's shell recipes as ```text
dghgit Sep 19, 2026
b931d49
tdes: fix the three CFB/CFB8/CTR doctests broken by StreamCipherEncry…
dghgit Sep 20, 2026
84015ab
tdes: adapt to feature/simple-ciphers -- the API changes this branch …
dghgit Sep 28, 2026
af64b54
tdes: adapt to feature/simple-ciphers 0fb4e79 -- the TDES_CFB/CFB8/CT…
dghgit Sep 29, 2026
ae476ea
tdes: adopt the hazmat layout (#156 step 5)
dghgit Oct 1, 2026
27a5856
tdes: project TDES_CBC and TDES_ECB through core's sealed Direction::…
dghgit Oct 1, 2026
7d74ee3
tdes: the adaptations the dropped merges carried -- bouncycastle-ciph…
dghgit Oct 24, 2026
cb57079
tdes: onto the key objects -- TDESKey<P, M> hands out the mode worker…
dghgit Oct 24, 2026
7d1e7cc
tdes: the two engines and the keys compare in constant time
dghgit Oct 25, 2026
3604a59
tdes: review fixes -- the keys' redundant key-type check goes, the st…
dghgit Oct 28, 2026
a9fb3dc
cli: the tdes commands take the aes contract -- PKCS7, --iv/--iv-file…
dghgit Oct 28, 2026
0b195ae
tdes: the stack table sums the frame chain, as the AES table does
dghgit Oct 30, 2026
620b86e
tdes: a key checks the key type its mode asks for before the engine does
dghgit Oct 30, 2026
ec4eb5c
tdes: the clippy nits from the camellia round
dghgit Oct 30, 2026
3b920bb
tdes: the PR #142 findings that apply here -- the mode and key docs s…
dghgit Nov 3, 2026
a83f737
tdes: the merge-time adaptations to feature/key-rework-memory, carrie…
dghgit Nov 3, 2026
f3f1515
core, cipher, tdes: the decryption-only gate is BlockPermutation::DEC…
dghgit Nov 4, 2026
657f758
tdes: spread goes through ct::value_barrier off x86-64 and aarch64, s…
dghgit Nov 4, 2026
fd55fc5
cli: the tdes-* encrypt commands refuse input past SP 800-67 Rev 2 Se…
dghgit Nov 4, 2026
79a2c34
Merge release/0.1.4alpha 1bee29d8 (cipher workers no longer implement…
dghgit Nov 4, 2026
a4289b2
Merge release/0.1.4alpha 8ea9d6c2 (PR #157: ChaCha20, Poly1305 and Ch…
dghgit Nov 5, 2026
1b83492
Merge release/0.1.4alpha 27b83ede (PR #160: AES Key Wrap, KW and KWP)…
dghgit Nov 5, 2026
9a00e1a
cipher: TKW, the TDEA key wrap of SP 800-38F Sec 7, as a mode over an…
dghgit Nov 5, 2026
e3622a3
tdes, cli: TDES3Key<TKW> wraps keys and TDES2Key<TKW> unwraps them; t…
dghgit Nov 5, 2026
becb91a
Merge release/0.1.4alpha bbb31d5f (PR #143: ECDSA, SM2 and the ec cra…
dghgit Nov 6, 2026
cf6e5a4
cipher, aes: Kw and Kwp carry their direction in the type
dghgit Nov 6, 2026
3509db5
cipher, tdes: Tkw carries its direction in the type, so a two-key unw…
dghgit Nov 6, 2026
608642a
cli, tdes: tdes-tkw and tdes-cfb8 encrypt count the blocks TDEA proce…
dghgit Nov 6, 2026
c028af9
cipher, core, cli, tdes, dev_scripts: the round-3 nits of the PR #139…
dghgit Nov 6, 2026
e719c94
Release notes: TDES
dghgit Nov 6, 2026
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ bouncycastle-sha2 = { path = "./crypto/sha2" }
bouncycastle-sha3 = { path = "./crypto/sha3" }
bouncycastle-sm2 = { path = "./crypto/sm2" }
bouncycastle-sm3 = { path = "./crypto/sm3" }
bouncycastle-tdes = { path = "./crypto/tdes" }
bouncycastle-utils = { path = "./crypto/utils" }


Expand Down Expand Up @@ -86,3 +87,4 @@ bouncycastle-sha2.workspace = true
bouncycastle-sha3.workspace = true
bouncycastle-sm2.workspace = true
bouncycastle-sm3.workspace = true
bouncycastle-tdes.workspace = true
10 changes: 10 additions & 0 deletions alpha_0.1.4_release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
## Major features

* CHACHA20 -- ChaCha20, Poly1305 and ChaCha20-Poly1305 (RFC 8439).
* TDES -- three-key TDEA (NIST SP 800-67 Rev 2) as a constant-time, table-free bit-sliced engine under `hazmat`,
with ECB, CBC, CFB64, CFB8 and CTR (SP 800-38A Appendix E) and TDEA Key Wrap (SP 800-38F TKW) through the key
role traits; two-key TDEA decrypts and unwraps only, which `BlockPermutation::DECRYPT_ONLY` makes a compile
error for every encrypting route; the `tdes-*` and `tdes2-*` CLI commands, whose `encrypt` refuses input past
SP 800-67 Rev 2 Sec 3.4's 2^20 blocks counted as the blocks TDEA processes (8 MiB; 1 MiB for CFB8, 699,048
bytes for TKW).
* AES Key Wrap -- AES_KW and AES_KWP (NIST SP 800-38F; RFC 3394 / RFC 5649) over any 128-bit block permutation,
with the `KeyWrapper` / `KeyUnwrapper` core traits and the `aes*-kw` / `aes*-kwp` CLI commands.
* ECDSA -- FIPS 186-5 signatures over P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1 and
Expand All @@ -15,6 +21,10 @@

## Minor features / bug fixes

* The key-wrap workers `Kw`, `Kwp` and `Tkw` carry their direction in the type, as the cipher modes do:
`KeyWrapper` is implemented for the encrypting worker only, so an unwrapper has no wrapping method and a
permutation that decrypts only is refused a wrapper at compile time by every route. The `AES_KW_*`, `AES_KWP_*`
and `TDES_TKW` aliases gain the `Dir` parameter; `TDES2_TKW` is the decrypting one.
* Cipher and MAC workers no longer implement `SuspendableKeyed` (#173): a resumed worker is a second copy of a
nonce, counter or keystream position. The AES modes, CMAC and Ascon-AEAD128 lose their suspended-state
constants and `hazmat::ModeKey` goes with them; HMAC and HKDF still suspend, and hashes, XOFs and KMAC keep
Expand Down
33 changes: 20 additions & 13 deletions cli/src/aes_cbc_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
//! separately.

use crate::helpers::block_mode_helpers::{
BLOCK_LEN, CipherDirection, decrypt_stream, encrypt_stream, load_iv, load_key_object,
start_encryptor,
CipherDirection, decrypt_stream, encrypt_stream, load_iv, load_key_object, start_encryptor,
};
use bouncycastle::aes::{AES128Key, AES192Key, AES256Key};
use bouncycastle::aes::{AES_BLOCK_LEN, AES128Key, AES192Key, AES256Key};
use bouncycastle::cipher::modes::CBC;
use bouncycastle::cipher::padding::PKCS7;
use bouncycastle::core::symmetric_key::{SymmetricDecryptionKey, SymmetricEncryptionKey};
Expand All @@ -29,7 +28,7 @@ pub(crate) fn aes128_cbc_cmd(
run::<_, 16>(
action,
&load_key_object::<AES128Key<CBC<PKCS7>>, 16>(key, key_file, "AES-128"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -45,7 +44,7 @@ pub(crate) fn aes192_cbc_cmd(
run::<_, 24>(
action,
&load_key_object::<AES192Key<CBC<PKCS7>>, 24>(key, key_file, "AES-192"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -61,7 +60,7 @@ pub(crate) fn aes256_cbc_cmd(
run::<_, 32>(
action,
&load_key_object::<AES256Key<CBC<PKCS7>>, 32>(key, key_file, "AES-256"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -70,19 +69,27 @@ pub(crate) fn aes256_cbc_cmd(
fn run<K, const KEY_LEN: usize>(
action: &CipherDirection,
key: &K,
iv: Option<[u8; BLOCK_LEN]>,
iv: Option<[u8; AES_BLOCK_LEN]>,
output_hex: bool,
) where
K: SymmetricEncryptionKey<KEY_LEN, BLOCK_LEN, BLOCK_LEN>
+ SymmetricDecryptionKey<KEY_LEN, BLOCK_LEN, BLOCK_LEN>,
K: SymmetricEncryptionKey<KEY_LEN, AES_BLOCK_LEN, AES_BLOCK_LEN>
+ SymmetricDecryptionKey<KEY_LEN, AES_BLOCK_LEN, AES_BLOCK_LEN>,
{
match action {
CipherDirection::Encrypt => {
let (enc, generated_iv) = start_encryptor(iv, |rng| key.create_encryptor(rng));
encrypt_stream::<_, BLOCK_LEN>(enc, generated_iv.as_ref(), output_hex, None)
}
CipherDirection::Decrypt => {
decrypt_stream::<_, BLOCK_LEN>(iv, |iv| key.create_decryptor(iv), output_hex, None)
encrypt_stream::<_, AES_BLOCK_LEN, AES_BLOCK_LEN>(
enc,
generated_iv.as_ref(),
output_hex,
None,
)
}
CipherDirection::Decrypt => decrypt_stream::<_, AES_BLOCK_LEN, AES_BLOCK_LEN>(
iv,
|iv| key.create_decryptor(iv),
output_hex,
None,
),
}
}
22 changes: 11 additions & 11 deletions cli/src/aes_cfb8_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
//! plaintext byte, corrupts the following 16 bytes, and then decryption resynchronises. Do not
//! decrypt data you have not authenticated separately.

use crate::helpers::block_mode_helpers::{BLOCK_LEN, CipherDirection, load_iv, load_key_object};
use crate::helpers::block_mode_helpers::{CipherDirection, load_iv, load_key_object};
use crate::helpers::stream_mode_helpers::run_stream_mode;
use bouncycastle::aes::{AES128Key, AES192Key, AES256Key};
use bouncycastle::aes::{AES_BLOCK_LEN, AES128Key, AES192Key, AES256Key};
use bouncycastle::cipher::modes::CFB8;
use bouncycastle::core::symmetric_key::{SymmetricDecryptionKey, SymmetricEncryptionKey};
use bouncycastle::core::traits::{StreamCipherDecryptor, StreamCipherEncryptor};
Expand All @@ -43,7 +43,7 @@ pub(crate) fn aes128_cfb8_cmd(
run::<_, 16>(
action,
&load_key_object::<AES128Key<CFB8>, 16>(key, key_file, "AES-128"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -59,7 +59,7 @@ pub(crate) fn aes192_cfb8_cmd(
run::<_, 24>(
action,
&load_key_object::<AES192Key<CFB8>, 24>(key, key_file, "AES-192"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -75,7 +75,7 @@ pub(crate) fn aes256_cfb8_cmd(
run::<_, 32>(
action,
&load_key_object::<AES256Key<CFB8>, 32>(key, key_file, "AES-256"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -84,13 +84,13 @@ pub(crate) fn aes256_cfb8_cmd(
fn run<'k, K, const KEY_LEN: usize>(
action: &CipherDirection,
key: &'k K,
iv: Option<[u8; BLOCK_LEN]>,
iv: Option<[u8; AES_BLOCK_LEN]>,
output_hex: bool,
) where
K: SymmetricEncryptionKey<KEY_LEN, BLOCK_LEN, 0>
+ SymmetricDecryptionKey<KEY_LEN, BLOCK_LEN, 0>,
K::Encryptor<'k>: StreamCipherEncryptor<BLOCK_LEN>,
K::Decryptor<'k>: StreamCipherDecryptor<BLOCK_LEN>,
K: SymmetricEncryptionKey<KEY_LEN, AES_BLOCK_LEN, 0>
+ SymmetricDecryptionKey<KEY_LEN, AES_BLOCK_LEN, 0>,
K::Encryptor<'k>: StreamCipherEncryptor<AES_BLOCK_LEN>,
K::Decryptor<'k>: StreamCipherDecryptor<AES_BLOCK_LEN>,
{
run_stream_mode::<K, KEY_LEN, BLOCK_LEN>(action, key, iv, output_hex)
run_stream_mode::<K, KEY_LEN, AES_BLOCK_LEN>(action, key, iv, output_hex)
}
22 changes: 11 additions & 11 deletions cli/src/aes_cfb_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
//! of the plaintext in the *same* block, so an attacker edits the block they aimed at, at the cost
//! of randomising the next one. Do not decrypt data you have not authenticated separately.

use crate::helpers::block_mode_helpers::{BLOCK_LEN, CipherDirection, load_iv, load_key_object};
use crate::helpers::block_mode_helpers::{CipherDirection, load_iv, load_key_object};
use crate::helpers::stream_mode_helpers::run_stream_mode;
use bouncycastle::aes::{AES128Key, AES192Key, AES256Key};
use bouncycastle::aes::{AES_BLOCK_LEN, AES128Key, AES192Key, AES256Key};
use bouncycastle::cipher::modes::CFB;
use bouncycastle::core::symmetric_key::{SymmetricDecryptionKey, SymmetricEncryptionKey};
use bouncycastle::core::traits::{StreamCipherDecryptor, StreamCipherEncryptor};
Expand All @@ -44,7 +44,7 @@ pub(crate) fn aes128_cfb_cmd(
run::<_, 16>(
action,
&load_key_object::<AES128Key<CFB>, 16>(key, key_file, "AES-128"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -60,7 +60,7 @@ pub(crate) fn aes192_cfb_cmd(
run::<_, 24>(
action,
&load_key_object::<AES192Key<CFB>, 24>(key, key_file, "AES-192"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -76,7 +76,7 @@ pub(crate) fn aes256_cfb_cmd(
run::<_, 32>(
action,
&load_key_object::<AES256Key<CFB>, 32>(key, key_file, "AES-256"),
load_iv::<BLOCK_LEN>(iv, iv_file),
load_iv::<AES_BLOCK_LEN>(iv, iv_file),
output_hex,
);
}
Expand All @@ -85,13 +85,13 @@ pub(crate) fn aes256_cfb_cmd(
fn run<'k, K, const KEY_LEN: usize>(
action: &CipherDirection,
key: &'k K,
iv: Option<[u8; BLOCK_LEN]>,
iv: Option<[u8; AES_BLOCK_LEN]>,
output_hex: bool,
) where
K: SymmetricEncryptionKey<KEY_LEN, BLOCK_LEN, 0>
+ SymmetricDecryptionKey<KEY_LEN, BLOCK_LEN, 0>,
K::Encryptor<'k>: StreamCipherEncryptor<BLOCK_LEN>,
K::Decryptor<'k>: StreamCipherDecryptor<BLOCK_LEN>,
K: SymmetricEncryptionKey<KEY_LEN, AES_BLOCK_LEN, 0>
+ SymmetricDecryptionKey<KEY_LEN, AES_BLOCK_LEN, 0>,
K::Encryptor<'k>: StreamCipherEncryptor<AES_BLOCK_LEN>,
K::Decryptor<'k>: StreamCipherDecryptor<AES_BLOCK_LEN>,
{
run_stream_mode::<K, KEY_LEN, BLOCK_LEN>(action, key, iv, output_hex)
run_stream_mode::<K, KEY_LEN, AES_BLOCK_LEN>(action, key, iv, output_hex)
}
12 changes: 6 additions & 6 deletions cli/src/aes_ecb_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
//! `aes*-cbc` or `aes*-cfb` under separate authentication, or better an AEAD.

use crate::helpers::block_mode_helpers::{
BLOCK_LEN, CipherDirection, decrypt_stream, encrypt_stream, load_key_object, start_encryptor,
CipherDirection, decrypt_stream, encrypt_stream, load_key_object, start_encryptor,
};
use bouncycastle::aes::{AES128Key, AES192Key, AES256Key};
use bouncycastle::aes::{AES_BLOCK_LEN, AES128Key, AES192Key, AES256Key};
use bouncycastle::cipher::modes::hazmat::ecb::ECB;
use bouncycastle::cipher::padding::NoPadding;
use bouncycastle::core::symmetric_key::{SymmetricDecryptionKey, SymmetricEncryptionKey};
Expand Down Expand Up @@ -69,16 +69,16 @@ pub(crate) fn aes256_ecb_cmd(
/// the loops write and read no IV, and `NoPadding` makes them refuse a partial final block.
fn run<K, const KEY_LEN: usize>(action: &CipherDirection, key: &K, output_hex: bool)
where
K: SymmetricEncryptionKey<KEY_LEN, 0, BLOCK_LEN>
+ SymmetricDecryptionKey<KEY_LEN, 0, BLOCK_LEN>,
K: SymmetricEncryptionKey<KEY_LEN, 0, AES_BLOCK_LEN>
+ SymmetricDecryptionKey<KEY_LEN, 0, AES_BLOCK_LEN>,
{
match action {
CipherDirection::Encrypt => {
// ECB has no IV: the constructor's RNG draws nothing, and nothing is written.
let (enc, _) = start_encryptor(None, |rng| key.create_encryptor(rng));
encrypt_stream::<_, 0>(enc, None, output_hex, Some(MODE))
encrypt_stream::<_, 0, AES_BLOCK_LEN>(enc, None, output_hex, Some(MODE))
}
CipherDirection::Decrypt => decrypt_stream::<_, 0>(
CipherDirection::Decrypt => decrypt_stream::<_, 0, AES_BLOCK_LEN>(
None,
|no_iv| key.create_decryptor(no_iv),
output_hex,
Expand Down
48 changes: 38 additions & 10 deletions cli/src/aes_kw_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
//! | bc-rust aes128-kw -d encrypt --key 000102030405060708090a0b0c0d0e0f -x
//! ```

use crate::helpers::block_mode_helpers::{CipherDirection, load_key_object};
use crate::helpers::block_mode_helpers::{CipherDirection, InputLimit, load_key_object};
use crate::helpers::write_bytes_or_hex;
use bouncycastle::aes::{AES128Key, AES192Key, AES256Key};
use bouncycastle::cipher::modes::{KW, KWP};
Expand All @@ -62,6 +62,7 @@ pub(crate) fn aes128_kw_cmd(
run::<_, 16>(
direction,
&load_key_object::<AES128Key<KW>, 16>(key, key_file, "AES-128-KW"),
None,
output_hex,
"AES-128-KW",
);
Expand All @@ -76,6 +77,7 @@ pub(crate) fn aes192_kw_cmd(
run::<_, 24>(
direction,
&load_key_object::<AES192Key<KW>, 24>(key, key_file, "AES-192-KW"),
None,
output_hex,
"AES-192-KW",
);
Expand All @@ -90,6 +92,7 @@ pub(crate) fn aes256_kw_cmd(
run::<_, 32>(
direction,
&load_key_object::<AES256Key<KW>, 32>(key, key_file, "AES-256-KW"),
None,
output_hex,
"AES-256-KW",
);
Expand All @@ -104,6 +107,7 @@ pub(crate) fn aes128_kwp_cmd(
run::<_, 16>(
direction,
&load_key_object::<AES128Key<KWP>, 16>(key, key_file, "AES-128-KWP"),
None,
output_hex,
"AES-128-KWP",
);
Expand All @@ -118,6 +122,7 @@ pub(crate) fn aes192_kwp_cmd(
run::<_, 24>(
direction,
&load_key_object::<AES192Key<KWP>, 24>(key, key_file, "AES-192-KWP"),
None,
output_hex,
"AES-192-KWP",
);
Expand All @@ -132,29 +137,52 @@ pub(crate) fn aes256_kwp_cmd(
run::<_, 32>(
direction,
&load_key_object::<AES256Key<KWP>, 32>(key, key_file, "AES-256-KWP"),
None,
output_hex,
"AES-256-KWP",
);
}

/// Reads all of stdin, wraps or unwraps it under `kek`, and writes the result. `alg` names the
/// algorithm in error messages.
fn run<K, const KEK_LEN: usize>(direction: &CipherDirection, kek: &K, output_hex: bool, alg: &str)
where
/// algorithm in error messages. Shared with the other block ciphers' key-wrap commands.
pub(crate) fn run<K, const KEK_LEN: usize>(
direction: &CipherDirection,
kek: &K,
limit: Option<&InputLimit>,
output_hex: bool,
alg: &str,
) where
K: KeyWrappingKey<KEK_LEN> + KeyUnwrappingKey<KEK_LEN>,
{
match direction {
CipherDirection::Encrypt => run_with(|input| kek.wrap(input), limit, output_hex, alg),
CipherDirection::Decrypt => run_with(|input| kek.unwrap(input), None, output_hex, alg),
}
}

/// Reads all of stdin, applies `op` to it and writes the result; the unwrap-only commands pass
/// their key's `unwrap` alone, so that no wrapper is ever instantiated for their key type.
/// `limit`, for a wrapping command whose standard bounds what one key may protect, is checked
/// against the whole input before `op` runs.
pub(crate) fn run_with(
op: impl FnOnce(&[u8]) -> Result<Vec<u8>, SymmetricCipherError>,
limit: Option<&InputLimit>,
output_hex: bool,
alg: &str,
) {
let mut input = Vec::new();
if let Err(e) = std::io::stdin().read_to_end(&mut input) {
eprintln!("Error: couldn't read stdin: {e}");
exit(-1);
}
if let Some(limit) = limit
&& input.len() as u64 > limit.max_bytes
{
eprintln!("Error: {}", limit.message);
exit(-1);
}

let result = match direction {
CipherDirection::Encrypt => kek.wrap(&input),
CipherDirection::Decrypt => kek.unwrap(&input),
};

match result {
match op(&input) {
Ok(output) => write_bytes_or_hex(&output, output_hex),
Err(SymmetricCipherError::DecryptionFailed) => {
eprintln!(
Expand Down
Loading
Loading