Skip to content

aes: shrink AES round keys for AES fixslice backend - #594

Open
Freax13 wants to merge 5 commits into
RustCrypto:masterfrom
Freax13:compressed-bitslice
Open

aes: shrink AES round keys for AES fixslice backend#594
Freax13 wants to merge 5 commits into
RustCrypto:masterfrom
Freax13:compressed-bitslice

Conversation

@Freax13

@Freax13 Freax13 commented Sep 10, 2026

Copy link
Copy Markdown

This PR does two things:

  1. Add support for using u16 as the native word size for the AES fixslice backend.
  2. Round keys are now always store as u16s. They are expanded into the full native words when the backend is created.
    a. On a targets with a 64-bit native word size, this reduces the size of the Aes128, Aes192, and Aes256 types from 704, 832, and 960 to 176, 208, and 240 bytes respectively.

Closes #191

@Freax13 Freax13 changed the title aes: shrink AES round keys for AES fixslice aes: shrink AES round keys for AES fixslice backend Sep 10, 2026
@Freax13
Freax13 marked this pull request as ready for review September 10, 2026 16:42
Comment thread aes/src/backends/fixslice/mod.rs
@newpavlov

Copy link
Copy Markdown
Member

I don't think we need to introduce u16-based backend for compact keys. Instead it would be better to store compact round keys in the Aes state and perform their expansion during backend creation (see the AVX-512 backend for reference).

@Freax13

Freax13 commented Sep 10, 2026

Copy link
Copy Markdown
Author

I don't think we need to introduce u16-based backend for compact keys.

Just to be clear, even with the compact backend, the fixslice implementation still runs on the native word size. Enabling the compact backend does not force it into the u16 path for encryption/decryption.

Instead it would be better to store compact round keys in the Aes state and perform their expansion during backend creation (see the AVX-512 backend for reference).

The proposed changes are very similar to this except that we expand/broadcast the round keys one by one when they're needed instead of doing so at the start of the encrypt/decrypt function. Expanding them at the beginning of encrypt/decrypt would be possible, but it would require saving all the expanded state on the stack and negate the space savings that motivated this change in the first place.

@newpavlov

newpavlov commented Sep 10, 2026

Copy link
Copy Markdown
Member

it would require saving all the expanded state on the stack and negate the space savings that motivated this change in the first place.

Yes, broadcasted keys are likely to be spilled to the stack (at least on register-starved x86), but it would reduce size of the Aes state (so we would not need bother with something like #593) and would have much less runtime overhead since broadcast has to be done only once during backend selection.

@Freax13
Freax13 force-pushed the compressed-bitslice branch 2 times, most recently from 5ad8541 to ed6b822 Compare September 11, 2026 10:16
@Freax13

Freax13 commented Sep 11, 2026

Copy link
Copy Markdown
Author

it would require saving all the expanded state on the stack and negate the space savings that motivated this change in the first place.

Yes, broadcasted keys are likely to be spilled to the stack (at least on register-starved x86), but it would reduce size of the Aes state (so we would not need bother with something like #593) and would have much less runtime overhead since broadcast has to be done only once during backend selection.

Done.

@Freax13
Freax13 requested a review from tarcieri September 11, 2026 10:25

@newpavlov newpavlov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked the changes in fixslice internals, but it looks mostly good otherwise.

Ideally, it would be nice to keep aes192, mix_columns, and utils intact, but I am fine with the changes if there is a good reason for them.

Comment thread .github/workflows/aes.yml Outdated
Comment thread aes/src/backends/fixslice/mod.rs Outdated
Comment thread aes/src/backends/soft.rs Outdated
Comment thread aes/src/backends/soft.rs Outdated
@Freax13
Freax13 force-pushed the compressed-bitslice branch from ed6b822 to 2c7ef09 Compare September 11, 2026 14:40
Up until now, u32 was the smallest supported word size. With u32, the
bitsliced soft backend can work on two blocks at the same time. A side-
effect of this is that up until now our code assumes that two blocks is
the smallest supported batch size and some of the constants are
centered around that. In order to support a batch of 1, we need adjust
accordingly by deduplicating the bits in the input for uniform_row and
doubling or quadrupling the bits for u32 and u64 respectively.
Same as previous commit. Instead of assuming two blocks as the smallest
common denominator, assume 1 and duplicate/quadruple accordingly.
Previously, we could use the same type for both of these because they
contained the same data, but in the future, this will no longer be the
case.
This matches the VAES256 and VAES512 backends.
The round keys are expanded to the native word size when multiple
blocks are processed in parallel.
@Freax13
Freax13 force-pushed the compressed-bitslice branch from 2c7ef09 to a07e3ac Compare September 12, 2026 06:19

@newpavlov newpavlov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity, could you add a comment (in the PR discussion) explaining why the aes192, mix_columns, and utils changes were necessary?

@Freax13

Freax13 commented Sep 13, 2026

Copy link
Copy Markdown
Author

For posterity, could you add a comment (in the PR discussion) explaining why the aes192, mix_columns, and utils changes were necessary?

The fixslice implementation uses the words like SIMD registers to encrypt multiple blocks in different lanes each at once. u32 can fit two lanes and u64 can fit four lanes. Because previously u32 with two lanes was the smallest denominator, some of the constants passed to uniform_row and pack_rows were already duplicated to fit that smallest denominator. This caused problems when adding the u16 implementation because a u16 can only fit a single lane.

There were two possible solutions: Either deduplicate the constants in the u16 implementation or adjust the code so that a single lane is the new common denominator and duplicate/quadruple the constants for the u32 and u64 implementations respectively. I chose the later approach.

If you look at the constants passed to uniform_row and pack_rows, you'll note that they previously all contained bit patterns with duplicated bits (e.g. W::pack_rows(0x00, 0x03, 0x0f, 0x0c) is equal to W::pack_rows(0b00_00_00_00, 0b00_00_00_11, 0b00_00_11_11, 0b00_00_11_00) and now those bit patterns have been deduplicated (the same line was changed to W::pack_rows(0b0_0_0_0, 0b0_0_0_1, 0b0_0_1_1, 0b0_0_1_0) or W::pack_rows(0x0, 0x1, 0x3, 0x2) in hex).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optional compact round keys for AES fixslice

3 participants