Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AES backend for riscv using crypto extensions #399

Closed
wants to merge 1 commit into from
Closed
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
108 changes: 108 additions & 0 deletions .github/workflows/aes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defaults:
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"
QEMU_FULL_VERSION: 8.2.0
LLVM_MAJOR_VERSION: 17

jobs:
# Builds for no_std platforms
Expand Down Expand Up @@ -234,6 +236,112 @@ jobs:
- run: cross test --package aes --target ${{ matrix.target }} --features hazmat
- run: cross test --package aes --target ${{ matrix.target }} --all-features

# Build and cache latest QEMUs; needed for RISC-V features
qemu-build-and-cache:
runs-on: ubuntu-latest
defaults:
run:
working-directory: /home/runner
steps:
- uses: silvanshade/rustcrypto-actions/qemu-cache-build@master
with:
qemu-full-version: ${{ env.QEMU_FULL_VERSION }}
qemu-target-archs: riscv64

# RISC-V rv64 cross-compiled tests for AES intrinsics
riscv64:
needs: qemu-build-and-cache
strategy:
matrix:
include:
- target: riscv64gc-unknown-linux-gnu
rust: nightly-2024-01-27 # TODO(silvanshade): stable MSRV once available
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# NOTE: Install a recent QEMU for RISC-V support
- uses: silvanshade/rustcrypto-actions/qemu-cache-install@master
with:
qemu-full-version: ${{ env.QEMU_FULL_VERSION }}
qemu-target-archs: riscv64
# NOTE: Install a recent LLVM/GNU toolchain configured for RISC-V multiarch cross-compilation
- uses: silvanshade/rustcrypto-actions/llvm-gnu-multiarch-install@master
with:
llvm-major-version: ${{ env.LLVM_MAJOR_VERSION }}
ubuntu-archs: riscv64
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
# NOTE: Write a rust-toolchain.toml to override the default toolchain
- name: write rust-toolchain.toml
shell: bash
run: |
cd ../aes/..
echo '[toolchain]' > rust-toolchain.toml
echo 'channel = "${{ matrix.rust }}"' >> rust-toolchain.toml
echo '' >> rust-toolchain.toml
# NOTE: Write a `.cargo/config.toml` to configure the RISC-V target for scalar tests
- name: write .cargo/config.toml (for scalar tests)
shell: bash
run: |
cd ../aes/..
mkdir -p .cargo
echo '[target.${{ matrix.target }}]' > .cargo/config.toml
echo 'runner = "qemu-riscv64 -cpu rv64,zkne=true,zknd=true"' >> .cargo/config.toml
echo 'linker = "clang-${{ env.LLVM_MAJOR_VERSION }}"' >> .cargo/config.toml
echo 'rustflags = [' >> .cargo/config.toml
echo ' "-C", "link-arg=-fuse-ld=lld-${{ env.LLVM_MAJOR_VERSION }}",' >> .cargo/config.toml
echo ' "-C", "link-arg=-march=rv64gc_zkne_zknd",' >> .cargo/config.toml
echo ' "-C", "link-arg=--target=${{ matrix.target }}",' >> .cargo/config.toml
echo ' "-C", "target-feature=+zkne,+zknd"' >> .cargo/config.toml
echo ']' >> .cargo/config.toml
- name: riscv64 scalar tests
run: unset RUSTFLAGS && cargo test --package aes
- name: riscv64 scalar tests (all features)
run: unset RUSTFLAGS && cargo test --package aes --all-features
# NOTE: Write a `.cargo/config.toml` to configure the RISC-V target for vector tests
- name: write .cargo/config.toml (for vector tests)
shell: bash
run: |
cd ../aes/..
mkdir -p .cargo
echo '[target.${{ matrix.target }}]' > .cargo/config.toml
echo 'runner = "qemu-riscv64 -cpu rv64,v=true,vext_spec=v1.0,zvkned=true"' >> .cargo/config.toml
echo 'linker = "clang-${{ env.LLVM_MAJOR_VERSION }}"' >> .cargo/config.toml
echo 'rustflags = [' >> .cargo/config.toml
echo ' "-C", "link-arg=-fuse-ld=lld-${{ env.LLVM_MAJOR_VERSION }}",' >> .cargo/config.toml
echo ' "-C", "link-arg=-march=rv64gc_v1p0_zvkned1p0",' >> .cargo/config.toml
echo ' "-C", "link-arg=--target=riscv64-unknown-linux-gnu",' >> .cargo/config.toml
echo ' "-C", "target-feature=+v",' >> .cargo/config.toml
echo ' "--cfg", "target_feature_zvkned"' >> .cargo/config.toml
echo ']' >> .cargo/config.toml
- name: riscv64 vector tests
run: unset RUSTFLAGS && cargo test --package aes --target ${{ matrix.target }}
- name: riscv64 vector tests (all features)
run: unset RUSTFLAGS && cargo test --package aes --target ${{ matrix.target }} --all-features
# NOTE: Write a `.cargo/config.toml` to configure the RISC-V target for scalar AND vector tests
- name: write .cargo/config.toml (for vector tests)
shell: bash
run: |
cd ../aes/..
mkdir -p .cargo
echo '[target.${{ matrix.target }}]' > .cargo/config.toml
echo 'runner = "qemu-riscv64 -cpu rv64,zkne=true,zknd=true,v=true,vext_spec=v1.0,zvkned=true"' >> .cargo/config.toml
echo 'linker = "clang-${{ env.LLVM_MAJOR_VERSION }}"' >> .cargo/config.toml
echo 'rustflags = [' >> .cargo/config.toml
echo ' "-C", "link-arg=-fuse-ld=lld-${{ env.LLVM_MAJOR_VERSION }}",' >> .cargo/config.toml
echo ' "-C", "link-arg=-march=rv64gc_zkne_zknd_v1p0_zvkned1p0",' >> .cargo/config.toml
echo ' "-C", "link-arg=--target=riscv64-unknown-linux-gnu",' >> .cargo/config.toml
echo ' "-C", "target-feature=+zkne,+zknd,+v",' >> .cargo/config.toml
echo ' "--cfg", "target_feature_zvkned"' >> .cargo/config.toml
echo ']' >> .cargo/config.toml
- name: riscv64 vector tests
run: unset RUSTFLAGS && cargo test --package aes --target ${{ matrix.target }}
- name: riscv64 vector tests (all features)
run: unset RUSTFLAGS && cargo test --package aes --target ${{ matrix.target }} --all-features

clippy:
env:
RUSTFLAGS: "-Dwarnings --cfg aes_compact"
Expand Down
24 changes: 24 additions & 0 deletions aes/benches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,27 @@ block_decryptor_bench!(
aes128_decrypt_block,
aes128_decrypt_blocks,
);
#[cfg(any(
not(target_arch = "riscv64"),
all(
target_arch = "riscv64",
target_feature = "zknd",
target_feature = "zkne"
)
))]
block_encryptor_bench!(
Key: aes::Aes192,
aes192_encrypt_block,
aes192_encrypt_blocks,
);
#[cfg(any(
not(target_arch = "riscv64"),
all(
target_arch = "riscv64",
target_feature = "zknd",
target_feature = "zkne"
)
))]
block_decryptor_bench!(
Key: aes::Aes192,
aes192_decrypt_block,
Expand All @@ -43,6 +59,14 @@ fn aes128_new(bh: &mut test::Bencher) {
});
}

#[cfg(any(
not(target_arch = "riscv64"),
all(
target_arch = "riscv64",
target_feature = "zknd",
target_feature = "zkne"
)
))]
#[bench]
fn aes192_new(bh: &mut test::Bencher) {
bh.iter(|| {
Expand Down
36 changes: 36 additions & 0 deletions aes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@
//! runtime. On other platforms the `aes` target feature must be enabled via
//! RUSTFLAGS.
//!
//!
//! ## RISC-V rv64 (scalar) {Zkne, ZKnd} extensions
//!
//! Support is available for the RISC-V rv64 scalar crypto extensions for AES. This
//! is not currently autodetected at runtime. In order to enable, you need to
//! enable the appropriate target features at compile time. For example:
//! `RUSTFLAGS=-C target-feature=+zkne,+zknd`.
//!
//! ## RISC-V rvv (vector) {Zvkned} extensions
//!
//! Support is available for the RISC-V vector crypto extensions for AES. This is
//! not currently autodetected at runtime. In order to enable, you need to enable
//! the appropriate target features at compile time. For example:
//! `RUSTFLAGS=-C target-feature=+v --cfg target_feature_zvkned`.
//!
//! NOTE: Hardware accelerated vector key-schedule routines for AES-192 are not
//! available for the RISC-V vector crypto extensions. It is still possible to
//! fall back to using the scalar key-schedule routines for AES-192 in this case
//! if the appropriate target features are enabled. For example:
//! `RUSTFLAGS=-C target-feature=+zkne,+zknd,+v --cfg target_feature_zvkned`.
//!
//! ## `x86`/`x86_64` intrinsics (AES-NI)
//! By default this crate uses runtime detection on `i686`/`x86_64` targets
//! in order to determine if AES-NI is available, and if it is not, it will
Expand Down Expand Up @@ -118,6 +139,14 @@
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(
all(
any(target_arch = "riscv32", target_arch = "riscv64"),
target_feature = "zknd",
target_feature = "zkne"
),
feature(riscv_ext_intrinsics, stdsimd)
)]

#[cfg(feature = "hazmat")]
#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
Expand All @@ -132,6 +161,13 @@ cfg_if! {
mod armv8;
mod autodetect;
pub use autodetect::*;
// TODO(silvanshade): switch to target_feature for `zvkned` when available
} else if #[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "v", target_feature_zvkned))] {
mod riscv;
pub use riscv::rvv::*;
} else if #[cfg(all(target_arch = "riscv64", target_feature = "zknd", target_feature = "zkne"))] {
mod riscv;
pub use riscv::rv64::*;
} else if #[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
not(aes_force_soft)
Expand Down
145 changes: 145 additions & 0 deletions aes/src/riscv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//! AES block cipher implementations for RISC-V using the Cryptography Extensions
//!
//! Supported targets: rv64 (scalar), rvv
//!
//! NOTE: rv32 (scalar) is not currently implemented, primarily due to the difficulty in obtaining a
//! suitable development environment (lack of distro support and lack of precompiled toolchains),
//! the effort required for maintaining a test environment as 32-bit becomes less supported, and the
//! overall scarcity of relevant hardware. If someone has a specific need for such an
//! implementation, please open an issue. Theoretically, the rvv implementation should work for
//! riscv32, for a hypothetical rv32 implementation satisfying the vector feature requirements.
//!
//! NOTE: These implementations are currently not enabled through auto-detection. In order to use
//! this implementation, you must enable the appropriate target-features.
//!
//! Additionally, for the vector implementation, since the `zvkned` target-feature is not yet
//! defined in Rust, you must pass `--cfg target_feature_zvkned` to the compiler (through
//! `RUSTFLAGS` or some other means). However, you still must enable the `v` target-feature.
//!
//! Examining the module structure for this implementation should give you an idea of how to specify
//! these features in your own code.
//!
//! NOTE: AES-128, AES-192, and AES-256 are supported for both the scalar and vector
//! implementations.
//!
//! However, key expansion is not vector-accelerated for the AES-192 case (because RISC-V does not
//! provide vector instructions for this case). Users concerned with vector performance are advised
//! to select AES-129 or AES-256 instead. Nevertheless, the AES-192 vector implementation will still
//! fall back to the scalar AES-192 key-schedule implementation, if the appropriate scalar
//! target-features are enabled.

#[cfg(all(
target_arch = "riscv64",
target_feature = "zknd",
target_feature = "zkne"
))]
pub(crate) mod rv64;
#[cfg(all(
any(target_arch = "riscv32", target_arch = "riscv64"),
target_feature = "v",
target_feature_zvkned
))]
pub(crate) mod rvv;

#[cfg(test)]
mod test {
use hex_literal::hex;

pub(crate) const AES128_KEY: [u8; 16] = hex!("2b7e151628aed2a6abf7158809cf4f3c");
pub(crate) const AES128_EXP_KEYS: [[u8; 16]; 11] = [
AES128_KEY,
hex!("a0fafe1788542cb123a339392a6c7605"),
hex!("f2c295f27a96b9435935807a7359f67f"),
hex!("3d80477d4716fe3e1e237e446d7a883b"),
hex!("ef44a541a8525b7fb671253bdb0bad00"),
hex!("d4d1c6f87c839d87caf2b8bc11f915bc"),
hex!("6d88a37a110b3efddbf98641ca0093fd"),
hex!("4e54f70e5f5fc9f384a64fb24ea6dc4f"),
hex!("ead27321b58dbad2312bf5607f8d292f"),
hex!("ac7766f319fadc2128d12941575c006e"),
hex!("d014f9a8c9ee2589e13f0cc8b6630ca6"),
];
pub(crate) const AES128_EXP_INVKEYS: [[u8; 16]; 11] = [
AES128_KEY,
hex!("2b3708a7f262d405bc3ebdbf4b617d62"),
hex!("cc7505eb3e17d1ee82296c51c9481133"),
hex!("7c1f13f74208c219c021ae480969bf7b"),
hex!("90884413d280860a12a128421bc89739"),
hex!("6ea30afcbc238cf6ae82a4b4b54a338d"),
hex!("6efcd876d2df54807c5df034c917c3b9"),
hex!("12c07647c01f22c7bc42d2f37555114a"),
hex!("df7d925a1f62b09da320626ed6757324"),
hex!("0c7b5a631319eafeb0398890664cfbb4"),
hex!("d014f9a8c9ee2589e13f0cc8b6630ca6"),
];

pub(crate) const AES192_KEY: [u8; 24] =
hex!("8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b");
pub(crate) const AES192_EXP_KEYS: [[u8; 16]; 13] = [
hex!("8e73b0f7da0e6452c810f32b809079e5"),
hex!("62f8ead2522c6b7bfe0c91f72402f5a5"),
hex!("ec12068e6c827f6b0e7a95b95c56fec2"),
hex!("4db7b4bd69b5411885a74796e92538fd"),
hex!("e75fad44bb095386485af05721efb14f"),
hex!("a448f6d94d6dce24aa326360113b30e6"),
hex!("a25e7ed583b1cf9a27f939436a94f767"),
hex!("c0a69407d19da4e1ec1786eb6fa64971"),
hex!("485f703222cb8755e26d135233f0b7b3"),
hex!("40beeb282f18a2596747d26b458c553e"),
hex!("a7e1466c9411f1df821f750aad07d753"),
hex!("ca4005388fcc5006282d166abc3ce7b5"),
hex!("e98ba06f448c773c8ecc720401002202"),
];
pub(crate) const AES192_EXP_INVKEYS: [[u8; 16]; 13] = [
hex!("8e73b0f7da0e6452c810f32b809079e5"),
hex!("9eb149c479d69c5dfeb4a27ceab6d7fd"),
hex!("659763e78c817087123039436be6a51e"),
hex!("41b34544ab0592b9ce92f15e421381d9"),
hex!("5023b89a3bc51d84d04b19377b4e8b8e"),
hex!("b5dc7ad0f7cffb09a7ec43939c295e17"),
hex!("c5ddb7f8be933c760b4f46a6fc80bdaf"),
hex!("5b6cfe3cc745a02bf8b9a572462a9904"),
hex!("4d65dfa2b1e5620dea899c312dcc3c1a"),
hex!("f3b42258b59ebb5cf8fb64fe491e06f3"),
hex!("a3979ac28e5ba6d8e12cc9e654b272ba"),
hex!("ac491644e55710b746c08a75c89b2cad"),
hex!("e98ba06f448c773c8ecc720401002202"),
];

pub(crate) const AES256_KEY: [u8; 32] =
hex!("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
pub(crate) const AES256_EXP_KEYS: [[u8; 16]; 15] = [
hex!("603deb1015ca71be2b73aef0857d7781"),
hex!("1f352c073b6108d72d9810a30914dff4"),
hex!("9ba354118e6925afa51a8b5f2067fcde"),
hex!("a8b09c1a93d194cdbe49846eb75d5b9a"),
hex!("d59aecb85bf3c917fee94248de8ebe96"),
hex!("b5a9328a2678a647983122292f6c79b3"),
hex!("812c81addadf48ba24360af2fab8b464"),
hex!("98c5bfc9bebd198e268c3ba709e04214"),
hex!("68007bacb2df331696e939e46c518d80"),
hex!("c814e20476a9fb8a5025c02d59c58239"),
hex!("de1369676ccc5a71fa2563959674ee15"),
hex!("5886ca5d2e2f31d77e0af1fa27cf73c3"),
hex!("749c47ab18501ddae2757e4f7401905a"),
hex!("cafaaae3e4d59b349adf6acebd10190d"),
hex!("fe4890d1e6188d0b046df344706c631e"),
];
pub(crate) const AES256_EXP_INVKEYS: [[u8; 16]; 15] = [
hex!("603deb1015ca71be2b73aef0857d7781"),
hex!("8ec6bff6829ca03b9e49af7edba96125"),
hex!("42107758e9ec98f066329ea193f8858b"),
hex!("4a7459f9c8e8f9c256a156bc8d083799"),
hex!("6c3d632985d1fbd9e3e36578701be0f3"),
hex!("54fb808b9c137949cab22ff547ba186c"),
hex!("25ba3c22a06bc7fb4388a28333934270"),
hex!("d669a7334a7ade7a80c8f18fc772e9e3"),
hex!("c440b289642b757227a3d7f114309581"),
hex!("32526c367828b24cf8e043c33f92aa20"),
hex!("34ad1e4450866b367725bcc763152946"),
hex!("b668b621ce40046d36a047ae0932ed8e"),
hex!("57c96cf6074f07c0706abb07137f9241"),
hex!("ada23f4963e23b2455427c8a5c709104"),
hex!("fe4890d1e6188d0b046df344706c631e"),
];
}
Loading
Loading