chacha20poly1305: migrate to workspace lint
What changed, and why it matters
This is a routine code cleanup in the ChaCha20-Poly1305 cryptography module. It moves linting rules from an individual crate to the shared workspace configuration, updates documentation formatting, and refactors how the Poly1305 key is copied from a keystream slice. The commit explicitly states the refactored code is semantically equivalent and produces the same machine code. There is no security-relevant behavior change.
No action required. This is a non-security refactoring commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit migrates the chacha20_poly1305 crate to use workspace-level lints, removes redundant per-crate lint configuration, and makes minor style changes (hex literal underscores, backticks in docs, u64::from casts). The only functional-looking change is replacing keystream[..32].try_into().expect(...) with a fixed-size array and copy_from_slice for Poly1305 key initialization. The commit message asserts this is semantically equivalent and compiles to identical machine code. No cryptographic constants, clamping masks, or algorithm logic were changed in a security-relevant way.
Changed components
chacha20_poly1305/src/chacha20.rschacha20_poly1305/src/lib.rschacha20_poly1305/src/poly1305.rschacha20_poly1305/Cargo.tomlCargo.tomlInspect captured patch +52 / −49
diff --git a/Cargo.toml b/Cargo.toml
index 720cd642..f111ab52 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["benches"]
resolver = "2"
[workspace.lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = ['cfg(kani)'] }
+unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(kani)'] }
[workspace.lints.clippy]
# Exclude lints we don't think are valuable.
diff --git a/chacha20_poly1305/Cargo.toml b/chacha20_poly1305/Cargo.toml
index 8abd82a8..c096ae06 100644
--- a/chacha20_poly1305/Cargo.toml
+++ b/chacha20_poly1305/Cargo.toml
@@ -24,9 +24,5 @@ hex = { package = "hex-conservative", version = "0.3.0", default-features = fals
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
-[lints.rust]
-unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)'] }
-
-[lints.clippy]
-redundant_clone = "warn"
-use_self = "warn"
+[lints]
+workspace = true
diff --git a/chacha20_poly1305/src/chacha20.rs b/chacha20_poly1305/src/chacha20.rs
index 13c4f5f4..d3970d4f 100644
--- a/chacha20_poly1305/src/chacha20.rs
+++ b/chacha20_poly1305/src/chacha20.rs
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: CC0-1.0
-//! The ChaCha20 stream cipher from RFC8439.
+//! The `ChaCha20` stream cipher from RFC8439.
use core::ops::BitXor;
-/// The first four words (32-bit) of the ChaCha stream cipher state are constants.
-const WORD_1: u32 = 0x61707865;
-const WORD_2: u32 = 0x3320646e;
-const WORD_3: u32 = 0x79622d32;
-const WORD_4: u32 = 0x6b206574;
+/// The first four words (32-bit) of the `ChaCha` stream cipher state are constants.
+const WORD_1: u32 = 0x6170_7865;
+const WORD_2: u32 = 0x3320_646e;
+const WORD_3: u32 = 0x7962_2d32;
+const WORD_4: u32 = 0x6b20_6574;
/// The cipher's block size is 64 bytes.
const CHACHA_BLOCKSIZE: usize = 64;
@@ -198,7 +198,7 @@ impl State {
/// The column quarter rounds are made up of indexes: `[0,4,8,12]`, `[1,5,9,13]`, `[2,6,10,14]`, `[3,7,11,15]`.
/// The diagonals quarter rounds are made up of indexes: `[0,5,10,15]`, `[1,6,11,12]`, `[2,7,8,13]`, `[3,4,9,14]`.
///
- /// The underlying quarter_round function is vectorized using the
+ /// The underlying `quarter_round` function is vectorized using the
/// u32x4 type in order to perform 4 quarter round functions at the same time.
/// This is a little more difficult to read, but it gives the compiler
/// a strong hint to use the performant SIMD instructions.
@@ -222,7 +222,7 @@ impl State {
[a, b, c, d]
}
- /// Transforms the state by performing the ChaCha block function.
+ /// Transforms the state by performing the `ChaCha` block function.
#[inline(always)]
fn chacha_block(&mut self) {
let mut working_state = self.matrix;
@@ -248,7 +248,7 @@ impl State {
}
}
-/// The ChaCha20 stream cipher from RFC8439.
+/// The `ChaCha20` stream cipher from RFC8439.
///
/// The 20-round IETF version uses a 96-bit nonce and 32-bit block counter. This is the
/// variant used in the Bitcoin ecosystem, including BIP-0324.
@@ -259,19 +259,19 @@ pub struct ChaCha20 {
nonce: Nonce,
/// Internal block index of keystream.
block_count: u32,
- /// Internal byte offset index of the block_count.
+ /// Internal byte offset index of the `block_count`.
seek_offset_bytes: usize,
}
impl ChaCha20 {
- /// Make a new instance of ChaCha20 from an index in the keystream.
+ /// Make a new instance of `ChaCha20` from an index in the keystream.
pub const fn new(key: Key, nonce: Nonce, seek: u32) -> Self {
let block_count = seek / 64;
let seek_offset_bytes = (seek % 64) as usize;
Self { key, nonce, block_count, seek_offset_bytes }
}
- /// Make a new instance of ChaCha20 from a block in the keystream.
+ /// Make a new instance of `ChaCha20` from a block in the keystream.
pub const fn new_from_block(key: Key, nonce: Nonce, block: u32) -> Self {
Self { key, nonce, block_count: block, seek_offset_bytes: 0 }
}
@@ -360,19 +360,19 @@ mod tests {
fn chacha_block() {
let mut state = State {
matrix: [
- U32x4([0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]),
- U32x4([0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c]),
- U32x4([0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c]),
- U32x4([0x00000001, 0x09000000, 0x4a000000, 0x00000000]),
+ U32x4([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]),
+ U32x4([0x0302_0100, 0x0706_0504, 0x0b0a_0908, 0x0f0e_0d0c]),
+ U32x4([0x1312_1110, 0x1716_1514, 0x1b1a_1918, 0x1f1e_1d1c]),
+ U32x4([0x0000_0001, 0x0900_0000, 0x4a00_0000, 0x0000_0000]),
],
};
state.chacha_block();
let expected = [
- U32x4([0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3]),
- U32x4([0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3]),
- U32x4([0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9]),
- U32x4([0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2]),
+ U32x4([0xe4e7_f110, 0x1559_3bd1, 0x1fdd_0f50, 0xc471_20a3]),
+ U32x4([0xc7f4_d1c7, 0x0368_c033, 0x9aaa_2204, 0x4e6c_d4c3]),
+ U32x4([0x4664_82d2, 0x09aa_9f07, 0x05d7_c214, 0xa202_8bd9]),
+ U32x4([0xd19c_12b5, 0xb94e_16de, 0xe883_d0cb, 0x4e3c_50a2]),
];
for (actual, expected) in state.matrix.iter().zip(expected.iter()) {
diff --git a/chacha20_poly1305/src/lib.rs b/chacha20_poly1305/src/lib.rs
index 88283d4b..f2c8e164 100644
--- a/chacha20_poly1305/src/lib.rs
+++ b/chacha20_poly1305/src/lib.rs
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: CC0-1.0
-//! ChaCha20 - Poly1305
+//! `ChaCha20` - `Poly1305`
//!
-//! Combine the ChaCha20 stream cipher with the Poly1305 message authentication code
+//! Combine the `ChaCha20` stream cipher with the `Poly1305` message authentication code
//! to form an authenticated encryption with additional data (AEAD) algorithm.
#![no_std]
@@ -11,9 +11,7 @@
#![warn(deprecated_in_future)]
#![doc(test(attr(warn(unused))))]
// Exclude lints we don't think are valuable.
-#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
-#![allow(clippy::manual_range_contains)] // More readable than clippy's format.
-#![allow(clippy::uninlined_format_args)] // Allow `format!("{}", x)` instead of enforcing `format!("{x}")`
+#![allow(clippy::inline_always)] // Not sure yet if we should give up the inline always, possible that the LLVM knows better.
#[cfg(feature = "alloc")]
extern crate alloc;
@@ -33,7 +31,7 @@ pub use self::chacha20::{Key, Nonce};
/// Zero array for padding slices.
const ZEROES: [u8; 16] = [0u8; 16];
-/// Errors encrypting and decrypting messages with ChaCha20 and Poly1305 authentication tags.
+/// Errors encrypting and decrypting messages with `ChaCha20` and `Poly1305` authentication tags.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
/// Additional data showing up when it is not expected.
@@ -64,10 +62,12 @@ pub struct ChaCha20Poly1305 {
}
impl ChaCha20Poly1305 {
- /// Make a new instance of a ChaCha20Poly1305 AEAD.
- pub const fn new(key: Key, nonce: Nonce) -> Self { Self { key, nonce } }
+ /// Make a new instance of a `ChaCha20Poly1305` AEAD.
+ pub const fn new(key: Key, nonce: Nonce) -> Self {
+ Self { key, nonce }
+ }
- /// Encrypt content in place and return the Poly1305 16-byte authentication tag.
+ /// Encrypt content in place and return the `Poly1305` 16-byte authentication tag.
///
/// # Parameters
///
@@ -81,8 +81,9 @@ impl ChaCha20Poly1305 {
let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 1);
chacha.apply_keystream(content);
let keystream = chacha.get_keystream(0);
- let mut poly =
- Poly1305::new(keystream[..32].try_into().expect("slicing produces 32-byte slice"));
+ let mut poly_key = [0u8; 32];
+ poly_key.copy_from_slice(&keystream[..32]);
+ let mut poly = Poly1305::new(poly_key);
let aad = aad.unwrap_or(&[]);
// AAD and ciphertext are padded if not 16-byte aligned.
poly.input(aad);
@@ -109,6 +110,11 @@ impl ChaCha20Poly1305 {
/// - `content` - Ciphertext to be decrypted in place.
/// - `tag` - 16-byte authentication tag.
/// - `aad` - Optional metadata covered by the authentication tag.
+ ///
+ /// # Errors
+ ///
+ /// Returns [`Error::UnauthenticatedAdditionalData`] if the computed authentication tag does
+ /// not match the provided tag.
pub fn decrypt(
self,
content: &mut [u8],
@@ -117,8 +123,9 @@ impl ChaCha20Poly1305 {
) -> Result<(), Error> {
let chacha = ChaCha20::new_from_block(self.key, self.nonce, 0);
let keystream = chacha.get_keystream(0);
- let mut poly =
- Poly1305::new(keystream[..32].try_into().expect("slicing produces 32-byte slice"));
+ let mut poly_key = [0u8; 32];
+ poly_key.copy_from_slice(&keystream[..32]);
+ let mut poly = Poly1305::new(poly_key);
let aad = aad.unwrap_or(&[]);
poly.input(aad);
// AAD and ciphertext are padded if not 16-byte aligned.
diff --git a/chacha20_poly1305/src/poly1305.rs b/chacha20_poly1305/src/poly1305.rs
index cf560bd0..675d0db6 100644
--- a/chacha20_poly1305/src/poly1305.rs
+++ b/chacha20_poly1305/src/poly1305.rs
@@ -2,11 +2,11 @@
//! Poly1305 one-time message authenticator from RFC8439.
//!
-//! Heavily inspired by the ["Donna"](https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna-32.h) implementation in C
+//! Heavily inspired by the [`Donna`](https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna-32.h) implementation in C
//! and Loup Vaillant's [Poly1305 design article](https://loup-vaillant.fr/tutorials/poly1305-design).
/// 2^26 for the 26-bit limbs.
-const BITMASK: u32 = 0x03ffffff;
+const BITMASK: u32 = 0x03ff_ffff;
/// Number is encoded in five 26-bit limbs.
const CARRY: u32 = 26;
@@ -30,11 +30,11 @@ impl Poly1305 {
/// Initializes authenticator with a 32-byte one-time secret key.
pub const fn new(key: [u8; 32]) -> Self {
// Taken from Donna. Assigns r to a 26-bit 5-limb number while simultaneously 'clamping' r.
- let r0 = u32::from_le_bytes([key[0], key[1], key[2], key[3]]) & 0x3ffffff;
- let r1 = (u32::from_le_bytes([key[3], key[4], key[5], key[6]]) >> 2) & 0x03ffff03;
- let r2 = (u32::from_le_bytes([key[6], key[7], key[8], key[9]]) >> 4) & 0x03ffc0ff;
- let r3 = (u32::from_le_bytes([key[9], key[10], key[11], key[12]]) >> 6) & 0x03f03fff;
- let r4 = (u32::from_le_bytes([key[12], key[13], key[14], key[15]]) >> 8) & 0x000fffff;
+ let r0 = u32::from_le_bytes([key[0], key[1], key[2], key[3]]) & 0x03ff_ffff;
+ let r1 = (u32::from_le_bytes([key[3], key[4], key[5], key[6]]) >> 2) & 0x03ff_ff03;
+ let r2 = (u32::from_le_bytes([key[6], key[7], key[8], key[9]]) >> 4) & 0x03ff_c0ff;
+ let r3 = (u32::from_le_bytes([key[9], key[10], key[11], key[12]]) >> 6) & 0x03f0_3fff;
+ let r4 = (u32::from_le_bytes([key[12], key[13], key[14], key[15]]) >> 8) & 0x000f_ffff;
let s0 = u32::from_le_bytes([key[16], key[17], key[18], key[19]]);
let s1 = u32::from_le_bytes([key[20], key[21], key[22], key[23]]);
@@ -142,7 +142,7 @@ impl Poly1305 {
// a + s
let mut tag: [u64; 4] = [0; 4];
for i in 0..4 {
- tag[i] = a[i] as u64 + self.s[i] as u64;
+ tag[i] = u64::from(a[i]) + u64::from(self.s[i]);
}
// Carry.
@@ -168,7 +168,7 @@ impl Poly1305 {
for (j, t) in t.iter_mut().enumerate() {
let modulus: u64 = if i > j { 5 } else { 1 };
let start = (5 - i) % 5;
- *t += modulus * self.r[i] as u64 * self.acc[(start + j) % 5] as u64;
+ *t += modulus * u64::from(self.r[i]) * u64::from(self.acc[(start + j) % 5]);
}
}
// Carry.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.