From ec82fbd365b6698b9dc6d096750e03e3ee38200a Mon Sep 17 00:00:00 2001 From: silvanshade Date: Tue, 9 Jan 2024 12:27:50 -0700 Subject: [PATCH] Implement AES for riscv64 using Zkned scalar crypto extensions --- .github/workflows/aes.yml | 516 +++++++++++++++++++-------------- aes/src/lib.rs | 12 + aes/src/riscv64.rs | 336 +++++++++++++++++++++ aes/src/riscv64/encdec.rs | 219 ++++++++++++++ aes/src/riscv64/expand.rs | 206 +++++++++++++ aes/src/riscv64/test_expand.rs | 159 ++++++++++ 6 files changed, 1231 insertions(+), 217 deletions(-) create mode 100644 aes/src/riscv64.rs create mode 100644 aes/src/riscv64/encdec.rs create mode 100644 aes/src/riscv64/expand.rs create mode 100644 aes/src/riscv64/test_expand.rs diff --git a/.github/workflows/aes.yml b/.github/workflows/aes.yml index f9edc7cb..cef0c66d 100644 --- a/.github/workflows/aes.yml +++ b/.github/workflows/aes.yml @@ -19,230 +19,312 @@ env: jobs: # Builds for no_std platforms - build: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - 1.72.0 # MSRV - - stable - target: - - thumbv7em-none-eabi - - wasm32-unknown-unknown - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - run: cargo check - - run: | - cargo build --target ${{ matrix.target }} - cargo build --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" - run: | - cargo build --target ${{ matrix.target }} - cargo build --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_compact" - run: | - cargo build --target ${{ matrix.target }} - cargo build --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_force_soft --cfg aes_compact" - run: | - cargo build --target ${{ matrix.target }} - cargo build --target ${{ matrix.target }} --features hazmat + # build: + # runs-on: ubuntu-latest + # strategy: + # matrix: + # rust: + # - 1.72.0 # MSRV + # - stable + # target: + # - thumbv7em-none-eabi + # - wasm32-unknown-unknown + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - run: cargo check + # - run: | + # cargo build --target ${{ matrix.target }} + # cargo build --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" + # run: | + # cargo build --target ${{ matrix.target }} + # cargo build --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_compact" + # run: | + # cargo build --target ${{ matrix.target }} + # cargo build --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_force_soft --cfg aes_compact" + # run: | + # cargo build --target ${{ matrix.target }} + # cargo build --target ${{ matrix.target }} --features hazmat - minimal-versions: - uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master - with: - working-directory: ${{ github.workflow }} + # minimal-versions: + # uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + # with: + # working-directory: ${{ github.workflow }} - # Tests for the AES-NI backend - aesni: - runs-on: ubuntu-latest - env: - CARGO_INCREMENTAL: 0 - RUSTDOCFLAGS: "-C target-feature=+aes,+ssse3" - RUSTFLAGS: "-Dwarnings -C target-feature=+aes,+ssse3" - strategy: - matrix: - include: - # 32-bit Linux - - target: i686-unknown-linux-gnu - rust: 1.72.0 # MSRV - deps: sudo apt update && sudo apt install gcc-multilib - - target: i686-unknown-linux-gnu - rust: stable - deps: sudo apt update && sudo apt install gcc-multilib - - # 64-bit Linux - - target: x86_64-unknown-linux-gnu - rust: 1.72.0 # MSRV - - target: x86_64-unknown-linux-gnu - rust: stable - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} - - run: cargo test --target ${{ matrix.target }} --features hazmat - - run: cargo test --target ${{ matrix.target }} --all-features - - # Tests for CPU feature autodetection with fallback to portable software implementation - autodetect: - runs-on: ubuntu-latest - strategy: - matrix: - include: - # 32-bit Linux - - target: i686-unknown-linux-gnu - rust: 1.72.0 # MSRV - deps: sudo apt update && sudo apt install gcc-multilib - - target: i686-unknown-linux-gnu - rust: stable - deps: sudo apt update && sudo apt install gcc-multilib - - # 64-bit Linux - - target: x86_64-unknown-linux-gnu - rust: 1.72.0 # MSRV - - target: x86_64-unknown-linux-gnu - rust: stable - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} - - run: cargo test --target ${{ matrix.target }} --features hazmat - - run: cargo test --target ${{ matrix.target }} --all-features - - # Tests for the portable software backend (i.e. `aes_force_soft`-only) - soft: - runs-on: ubuntu-latest - env: - RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" - strategy: - matrix: - include: - # 32-bit Linux - - target: i686-unknown-linux-gnu - rust: 1.72.0 # MSRV - deps: sudo apt update && sudo apt install gcc-multilib - - target: i686-unknown-linux-gnu - rust: stable - deps: sudo apt update && sudo apt install gcc-multilib - - # 64-bit Linux - - target: x86_64-unknown-linux-gnu - rust: 1.72.0 # MSRV - - target: x86_64-unknown-linux-gnu - rust: stable - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} - - run: cargo test --target ${{ matrix.target }} --all-features - - - # Cross-compiled tests - cross: - strategy: - matrix: - include: - # ARM64 - - target: aarch64-unknown-linux-gnu - rust: 1.72.0 # MSRV - - target: aarch64-unknown-linux-gnu - rust: stable - - # PPC32 - - target: powerpc-unknown-linux-gnu - rust: 1.72.0 # MSRV - - target: powerpc-unknown-linux-gnu - rust: stable + # # Tests for the AES-NI backend + # aesni: + # runs-on: ubuntu-latest + # env: + # CARGO_INCREMENTAL: 0 + # RUSTDOCFLAGS: "-C target-feature=+aes,+ssse3" + # RUSTFLAGS: "-Dwarnings -C target-feature=+aes,+ssse3" + # strategy: + # matrix: + # include: + # # 32-bit Linux + # - target: i686-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # deps: sudo apt update && sudo apt install gcc-multilib + # - target: i686-unknown-linux-gnu + # rust: stable + # deps: sudo apt update && sudo apt install gcc-multilib + + # # 64-bit Linux + # - target: x86_64-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # - target: x86_64-unknown-linux-gnu + # rust: stable + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - run: ${{ matrix.deps }} + # - run: cargo test --target ${{ matrix.target }} + # - run: cargo test --target ${{ matrix.target }} --features hazmat + # - run: cargo test --target ${{ matrix.target }} --all-features + + # # Tests for CPU feature autodetection with fallback to portable software implementation + # autodetect: + # runs-on: ubuntu-latest + # strategy: + # matrix: + # include: + # # 32-bit Linux + # - target: i686-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # deps: sudo apt update && sudo apt install gcc-multilib + # - target: i686-unknown-linux-gnu + # rust: stable + # deps: sudo apt update && sudo apt install gcc-multilib + + # # 64-bit Linux + # - target: x86_64-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # - target: x86_64-unknown-linux-gnu + # rust: stable + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - run: ${{ matrix.deps }} + # - run: cargo test --target ${{ matrix.target }} + # - run: cargo test --target ${{ matrix.target }} --features hazmat + # - run: cargo test --target ${{ matrix.target }} --all-features + + # # Tests for the portable software backend (i.e. `aes_force_soft`-only) + # soft: + # runs-on: ubuntu-latest + # env: + # RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" + # strategy: + # matrix: + # include: + # # 32-bit Linux + # - target: i686-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # deps: sudo apt update && sudo apt install gcc-multilib + # - target: i686-unknown-linux-gnu + # rust: stable + # deps: sudo apt update && sudo apt install gcc-multilib + + # # 64-bit Linux + # - target: x86_64-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # - target: x86_64-unknown-linux-gnu + # rust: stable + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - run: ${{ matrix.deps }} + # - run: cargo test --target ${{ matrix.target }} + # - run: cargo test --target ${{ matrix.target }} --all-features + + + # # Cross-compiled tests + # cross: + # strategy: + # matrix: + # include: + # # ARM64 + # - target: aarch64-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # - target: aarch64-unknown-linux-gnu + # rust: stable + + # # PPC32 + # - target: powerpc-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # - target: powerpc-unknown-linux-gnu + # rust: stable + # runs-on: ubuntu-latest + # # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml + # defaults: + # run: + # working-directory: . + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - uses: RustCrypto/actions/cross-install@master + # - run: ${{ matrix.deps }} + # - run: | + # cross test --package aes --target ${{ matrix.target }} + # cross test --package aes --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" + # run: | + # cross test --package aes --target ${{ matrix.target }} + # cross test --package aes --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_compact" + # run: | + # cross test --package aes --target ${{ matrix.target }} + # cross test --package aes --target ${{ matrix.target }} --features hazmat + # - env: + # RUSTFLAGS: "-Dwarnings --cfg aes_force_soft --cfg aes_compact" + # run: | + # cross test --package aes --target ${{ matrix.target }} + # cross test --package aes --target ${{ matrix.target }} --features hazmat + + # # ARMv8 cross-compiled tests for AES intrinsics + # armv8: + # strategy: + # matrix: + # include: + # - target: aarch64-unknown-linux-gnu + # rust: 1.72.0 # MSRV + # runs-on: ubuntu-latest + # # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml + # defaults: + # run: + # working-directory: . + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - run: ${{ matrix.deps }} + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - uses: RustCrypto/actions/cross-install@master + # - run: cross test --package aes --target ${{ matrix.target }} + # - run: cross test --package aes --target ${{ matrix.target }} --features hazmat + # - run: cross test --package aes --target ${{ matrix.target }} --all-features + + # RISC-V 64 cross-compiled tests for AES intrinsics + riscv64: runs-on: ubuntu-latest - # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml - defaults: - run: - working-directory: . + container: + image: ubuntu:mantic + env: + UBUNTU_CODENAME: mantic + LLVM_MAJOR_VERSION: 17 steps: - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - uses: RustCrypto/actions/cross-install@master - - run: ${{ matrix.deps }} - run: | - cross test --package aes --target ${{ matrix.target }} - cross test --package aes --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_force_soft" - run: | - cross test --package aes --target ${{ matrix.target }} - cross test --package aes --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_compact" - run: | - cross test --package aes --target ${{ matrix.target }} - cross test --package aes --target ${{ matrix.target }} --features hazmat - - env: - RUSTFLAGS: "-Dwarnings --cfg aes_force_soft --cfg aes_compact" + sed -i'' -E 's/^(deb|deb-src) /\1 [arch=amd64,i386] /' /etc/apt/sources.list + - run: | + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME} main restricted" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME} universe" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-updates universe" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME} multiverse" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-updates multiverse" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-backports main restricted universe multiverse" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-security universe" >> /etc/apt/sources.list.d/riscv64.list + echo "deb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ ${UBUNTU_CODENAME}-security multiverse" >> /etc/apt/sources.list.d/riscv64.list + - run: | + dpkg --add-architecture riscv64 + - run: | + apt update + apt --assume-yes install curl gnupg + - shell: bash run: | - cross test --package aes --target ${{ matrix.target }} - cross test --package aes --target ${{ matrix.target }} --features hazmat - - # ARMv8 cross-compiled tests for AES intrinsics - armv8: - strategy: - matrix: - include: - - target: aarch64-unknown-linux-gnu - rust: 1.72.0 # MSRV - runs-on: ubuntu-latest - # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml - defaults: - run: - working-directory: . - steps: - - uses: actions/checkout@v3 + apt install --assume-yes \ + binfmt-support build-essential qemu-user-static clang-${LLVM_MAJOR_VERSION} clang-tools-${LLVM_MAJOR_VERSION} lld-${LLVM_MAJOR_VERSION} \ + libc6:{amd64,riscv64} \ + libc6-dev:{amd64,riscv64} \ + libgcc-12-dev:{amd64,riscv64} \ + libgcc-s1:{amd64,riscv64} \ + libstdc++-12-dev:{amd64,riscv64} \ + linux-libc-dev:{amd64,riscv64} - uses: RustCrypto/actions/cargo-cache@master - - run: ${{ matrix.deps }} - uses: dtolnay/rust-toolchain@master with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - uses: RustCrypto/actions/cross-install@master - - run: cross test --package aes --target ${{ matrix.target }} - - run: cross test --package aes --target ${{ matrix.target }} --features hazmat - - run: cross test --package aes --target ${{ matrix.target }} --all-features - - clippy: - env: - RUSTFLAGS: "-Dwarnings --cfg aes_compact" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.72.0 # MSRV - components: clippy - - run: cargo clippy --features hazmat -- -D warnings + toolchain: nightly-2024-01-08 + targets: x86_64-unknown-linux-gnu, riscv64gc-unknown-linux-gnu + - shell: bash + run: | + mkdir -p .cargo + echo '[target.riscv64gc-unknown-linux-gnu]' >> .cargo/config + echo 'runner = "qemu-riscv64-static -cpu rv64,zkne=true,zknd=true"' >> .cargo/config + echo 'linker = "clang-17"' >> .cargo/config + echo 'rustflags = [' >> .cargo/config + echo ' "-C", "link-arg=--target=riscv64-unknown-linux-gnu",' >> .cargo/config + echo ' "-C", "link-arg=-fuse-ld=lld-17",' >> .cargo/config + echo ' "-C", "target-feature=+zkne,+zknd"' >> .cargo/config + echo ']' >> .cargo/config + - run: cargo +nightly-2024-01-08 test --package aes --target riscv64gc-unknown-linux-gnu + env: + RUSTFLAGS: -C linker=clang-17 -C link-arg=-fuse-ld=lld-17 -C link-arg=--target=riscv64-unknown-linux-gnu -C target-feature=+zkne,+zknd + - run: cargo +nightly-2024-01-08 test --package aes --target riscv64gc-unknown-linux-gnu --all-features + env: + RUSTFLAGS: -C linker=clang-17 -C link-arg=-fuse-ld=lld-17 -C link-arg=--target=riscv64-unknown-linux-gnu -C target-feature=+zkne,+zknd + # strategy: + # matrix: + # include: + # - target: riscv64gc-unknown-linux-gnu + # rust: nightly-2024-01-08 + # runs-on: ubuntu-latest + # # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml + # defaults: + # run: + # working-directory: . + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - run: ${{ matrix.deps }} + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: ${{ matrix.rust }} + # targets: ${{ matrix.target }} + # - uses: RustCrypto/actions/cross-install@master + # - run: cross test --verbose --verbose --package aes --target ${{ matrix.target }} + # - run: cross test --verbose --verbose --package aes --target ${{ matrix.target }} --all-features + + # clippy: + # env: + # RUSTFLAGS: "-Dwarnings --cfg aes_compact" + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - uses: RustCrypto/actions/cargo-cache@master + # - uses: dtolnay/rust-toolchain@master + # with: + # toolchain: 1.72.0 # MSRV + # components: clippy + # - run: cargo clippy --features hazmat -- -D warnings diff --git a/aes/src/lib.rs b/aes/src/lib.rs index f47557dc..f2d68739 100644 --- a/aes/src/lib.rs +++ b/aes/src/lib.rs @@ -1,3 +1,12 @@ +#![cfg_attr( + all( + target_arch = "riscv64", + target_feature = "zknd", + target_feature = "zkne" + ), + feature(riscv_ext_intrinsics, stdsimd) +)] + //! Pure Rust implementation of the [Advanced Encryption Standard][AES] //! (AES, a.k.a. Rijndael). //! @@ -132,6 +141,9 @@ cfg_if! { mod armv8; mod autodetect; pub use autodetect::*; + } else if #[cfg(all(target_arch = "riscv64", target_feature = "zknd", target_feature = "zkne"))] { + mod riscv64; + pub use riscv64::*; } else if #[cfg(all( any(target_arch = "x86", target_arch = "x86_64"), not(aes_force_soft) diff --git a/aes/src/riscv64.rs b/aes/src/riscv64.rs new file mode 100644 index 00000000..c00bc1eb --- /dev/null +++ b/aes/src/riscv64.rs @@ -0,0 +1,336 @@ +//! AES block cipher implementation using the RISC-V Cryptography Extensions. +//! +//! RISC-V Scalar Cryptography Extension v1.0.1: +//! https://github.com/riscv/riscv-crypto/releases/download/v1.0.1-scalar/riscv-crypto-spec-scalar-v1.0.1.pdf +//! +//! RISC-V Vector Cryptography Extension v1.0.0: +//! https://github.com/riscv/riscv-crypto/releases/download/v1.0.0/riscv-crypto-spec-vector.pdf +//! +//! Implementation Status: +//! +//! Currently only the RISC-V scalar extensions are utilized in this implementation. +//! +//! For reference, see the following other implementations: +//! +//! 1. The RISC-V Cryptography Extensions "benchmarks" reference for RISC-V 64 with zkn{de}: +//! https://github.com/riscv/riscv-crypto/tree/main/benchmarks/aes/zscrypto_rv64 +//! +//! 2. The OpenSSL assembly implementation for RISC-V 64 with zkn{de}: +//! https://github.com/openssl/openssl/blob/master/crypto/aes/asm/aes-riscv64-zkn.pl + +mod encdec; +mod expand; +#[cfg(test)] +mod test_expand; + +use self::{ + encdec::{decrypt1, decrypt8, encrypt1, encrypt8}, + expand::{inv_expanded_keys, KeyScheduleState}, +}; +use crate::{Block, Block8}; +use cipher::{ + consts::{U16, U24, U32, U8}, + inout::InOut, + AlgorithmName, BlockBackend, BlockCipher, BlockClosure, BlockDecrypt, BlockEncrypt, + BlockSizeUser, Key, KeyInit, KeySizeUser, ParBlocksSizeUser, +}; +use core::fmt; + +type RoundKey = [u64; 2]; +type RoundKeys = [RoundKey; N]; + +macro_rules! define_aes_impl { + ( + $name:ident, + $name_enc:ident, + $name_dec:ident, + $name_back_enc:ident, + $name_back_dec:ident, + $key_size:ty, + $rounds:tt, + $doc:expr $(,)? + ) => { + #[doc=$doc] + #[doc = "block cipher"] + #[derive(Clone)] + pub struct $name { + encrypt: $name_enc, + decrypt: $name_dec, + } + + impl BlockCipher for $name {} + + impl KeySizeUser for $name { + type KeySize = $key_size; + } + + impl KeyInit for $name { + #[inline] + fn new(key: &Key) -> Self { + let encrypt = $name_enc::new(key); + let decrypt = $name_dec::from(&encrypt); + Self { encrypt, decrypt } + } + } + + impl From<$name_enc> for $name { + #[inline] + fn from(encrypt: $name_enc) -> $name { + let decrypt = (&encrypt).into(); + Self { encrypt, decrypt } + } + } + + impl From<&$name_enc> for $name { + #[inline] + fn from(encrypt: &$name_enc) -> $name { + let decrypt = encrypt.into(); + let encrypt = encrypt.clone(); + Self { encrypt, decrypt } + } + } + + impl BlockSizeUser for $name { + type BlockSize = U16; + } + + impl BlockEncrypt for $name { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + self.encrypt.encrypt_with_backend(f) + } + } + + impl BlockDecrypt for $name { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + self.decrypt.decrypt_with_backend(f) + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str(concat!(stringify!($name), " { .. }")) + } + } + + impl AlgorithmName for $name { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(stringify!($name)) + } + } + + #[cfg(feature = "zeroize")] + impl zeroize::ZeroizeOnDrop for $name {} + + #[doc=$doc] + #[doc = "block cipher (encrypt-only)"] + #[derive(Clone)] + pub struct $name_enc { + round_keys: RoundKeys<$rounds>, + } + + impl $name_enc { + #[inline(always)] + pub(crate) fn get_enc_backend(&self) -> $name_back_enc<'_> { + $name_back_enc(self) + } + } + + impl BlockCipher for $name_enc {} + + impl KeySizeUser for $name_enc { + type KeySize = $key_size; + } + + impl KeyInit for $name_enc { + #[inline] + fn new(key: &Key) -> Self { + Self { + round_keys: KeyScheduleState::<$rounds>::expand_key(key.as_ref()), + } + } + } + + impl BlockSizeUser for $name_enc { + type BlockSize = U16; + } + + impl BlockEncrypt for $name_enc { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut self.get_enc_backend()) + } + } + + impl fmt::Debug for $name_enc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str(concat!(stringify!($name_enc), " { .. }")) + } + } + + impl AlgorithmName for $name_enc { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(stringify!($name_enc)) + } + } + + impl Drop for $name_enc { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + zeroize::Zeroize::zeroize(&mut self.round_keys); + } + } + + #[cfg(feature = "zeroize")] + impl zeroize::ZeroizeOnDrop for $name_enc {} + + #[doc=$doc] + #[doc = "block cipher (decrypt-only)"] + #[derive(Clone)] + pub struct $name_dec { + round_keys: RoundKeys<$rounds>, + } + + impl $name_dec { + #[inline(always)] + pub(crate) fn get_dec_backend(&self) -> $name_back_dec<'_> { + $name_back_dec(self) + } + } + + impl BlockCipher for $name_dec {} + + impl KeySizeUser for $name_dec { + type KeySize = $key_size; + } + + impl KeyInit for $name_dec { + #[inline] + fn new(key: &Key) -> Self { + $name_enc::new(key).into() + } + } + + impl From<$name_enc> for $name_dec { + #[inline] + fn from(enc: $name_enc) -> $name_dec { + Self::from(&enc) + } + } + + impl From<&$name_enc> for $name_dec { + fn from(enc: &$name_enc) -> $name_dec { + let mut round_keys = enc.round_keys; + inv_expanded_keys(&mut round_keys); + Self { round_keys } + } + } + + impl BlockSizeUser for $name_dec { + type BlockSize = U16; + } + + impl BlockDecrypt for $name_dec { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + f.call(&mut self.get_dec_backend()); + } + } + + impl fmt::Debug for $name_dec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str(concat!(stringify!($name_dec), " { .. }")) + } + } + + impl AlgorithmName for $name_dec { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(stringify!($name_dec)) + } + } + + impl Drop for $name_dec { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + zeroize::Zeroize::zeroize(&mut self.round_keys); + } + } + + #[cfg(feature = "zeroize")] + impl zeroize::ZeroizeOnDrop for $name_dec {} + + pub(crate) struct $name_back_enc<'a>(&'a $name_enc); + + impl<'a> BlockSizeUser for $name_back_enc<'a> { + type BlockSize = U16; + } + + impl<'a> ParBlocksSizeUser for $name_back_enc<'a> { + type ParBlocksSize = U8; + } + + impl<'a> BlockBackend for $name_back_enc<'a> { + #[inline(always)] + fn proc_block(&mut self, block: InOut<'_, '_, Block>) { + encrypt1(&self.0.round_keys, block); + } + + #[inline(always)] + fn proc_par_blocks(&mut self, blocks: InOut<'_, '_, Block8>) { + encrypt8(&self.0.round_keys, blocks) + } + } + + pub(crate) struct $name_back_dec<'a>(&'a $name_dec); + + impl<'a> BlockSizeUser for $name_back_dec<'a> { + type BlockSize = U16; + } + + impl<'a> ParBlocksSizeUser for $name_back_dec<'a> { + type ParBlocksSize = U8; + } + + impl<'a> BlockBackend for $name_back_dec<'a> { + #[inline(always)] + fn proc_block(&mut self, block: InOut<'_, '_, Block>) { + decrypt1(&self.0.round_keys, block); + } + + #[inline(always)] + fn proc_par_blocks(&mut self, blocks: InOut<'_, '_, Block8>) { + decrypt8(&self.0.round_keys, blocks) + } + } + }; +} + +define_aes_impl!( + Aes128, + Aes128Enc, + Aes128Dec, + Aes128BackEnc, + Aes128BackDec, + U16, + 11, + "AES-128", +); +define_aes_impl!( + Aes192, + Aes192Enc, + Aes192Dec, + Aes192BackEnc, + Aes192BackDec, + U24, + 13, + "AES-192", +); +define_aes_impl!( + Aes256, + Aes256Enc, + Aes256Dec, + Aes256BackEnc, + Aes256BackDec, + U32, + 15, + "AES-256", +); diff --git a/aes/src/riscv64/encdec.rs b/aes/src/riscv64/encdec.rs new file mode 100644 index 00000000..90163a83 --- /dev/null +++ b/aes/src/riscv64/encdec.rs @@ -0,0 +1,219 @@ +//! AES encryption support + +use super::{RoundKey, RoundKeys}; +use crate::{Block, Block8}; +use cipher::inout::InOut; + +#[inline(always)] +pub(super) fn encrypt1(keys: &RoundKeys, mut block1: InOut<'_, '_, Block>) { + let rounds = N - 1; + let mut state1 = utils::CipherState1::load1(block1.get_in()); + for i in 0..rounds / 2 - 1 { + state1.enc1_two_more(keys[2 * i + 0], keys[2 * i + 1]); + } + state1.enc1_two_last(keys[rounds - 2], keys[rounds - 1]); + state1.xor1(&keys[rounds]); + state1.save1(block1.get_out()); +} + +#[inline(always)] +pub(super) fn encrypt8(keys: &RoundKeys, mut block8: InOut<'_, '_, Block8>) { + let rounds = N - 1; + let mut state8 = utils::CipherState8::load8(block8.get_in()); + for i in 0..rounds / 2 - 1 { + state8.enc8_two_more(keys[2 * i + 0], keys[2 * i + 1]); + } + state8.enc8_two_last(keys[rounds - 2], keys[rounds - 1]); + state8.xor8(&keys[rounds]); + state8.save8(block8.get_out()); +} + +#[inline(always)] +pub(super) fn decrypt1(keys: &RoundKeys, mut block1: InOut<'_, '_, Block>) { + let rounds = N - 1; + let mut state1 = utils::CipherState1::load1(block1.get_in()); + state1.xor1(&keys[rounds]); + for i in (1..rounds / 2).rev() { + state1.dec1_two_more(keys[2 * i + 0], keys[2 * i + 1]); + } + state1.dec1_two_last(keys[0], keys[1]); + state1.save1(block1.get_out()); +} + +#[inline(always)] +pub(super) fn decrypt8(keys: &RoundKeys, mut block8: InOut<'_, '_, Block8>) { + let rounds = N - 1; + let mut state8 = utils::CipherState8::load8(block8.get_in()); + state8.xor8(&keys[rounds]); + for i in (1..rounds / 2).rev() { + state8.dec8_two_more(keys[2 * i + 0], keys[2 * i + 1]); + } + state8.dec8_two_last(keys[0], keys[1]); + state8.save8(block8.get_out()); +} + +mod utils { + use super::*; + use core::arch::riscv64::*; + + pub(super) struct CipherState1 { + data: [u64; 2], + } + + impl CipherState1 { + #[inline(always)] + pub(super) fn load1(block: &Block) -> Self { + let ptr = block.as_ptr().cast::(); + let s0 = unsafe { ptr.add(0).read_unaligned() }; + let s1 = unsafe { ptr.add(1).read_unaligned() }; + Self { data: [s0, s1] } + } + + #[inline(always)] + pub(super) fn save1(self, block: &mut Block) { + let b0 = self.data[0].to_ne_bytes(); + let b1 = self.data[1].to_ne_bytes(); + block[00..08].copy_from_slice(&b0); + block[08..16].copy_from_slice(&b1); + } + + #[inline(always)] + pub(super) fn xor1(&mut self, key: &RoundKey) { + self.data[0] ^= key[0]; + self.data[1] ^= key[1]; + } + + #[inline(always)] + pub(super) fn enc1_two_more(&mut self, k0: RoundKey, k1: RoundKey) { + self.data[0] ^= k0[0]; + self.data[1] ^= k0[1]; + let mut n0 = unsafe { aes64esm(self.data[0], self.data[1]) }; + let mut n1 = unsafe { aes64esm(self.data[1], self.data[0]) }; + n0 ^= k1[0]; + n1 ^= k1[1]; + self.data[0] = unsafe { aes64esm(n0, n1) }; + self.data[1] = unsafe { aes64esm(n1, n0) }; + } + + #[inline(always)] + pub(super) fn enc1_two_last(&mut self, k0: RoundKey, k1: RoundKey) { + self.data[0] ^= k0[0]; + self.data[1] ^= k0[1]; + let mut n0 = unsafe { aes64esm(self.data[0], self.data[1]) }; + let mut n1 = unsafe { aes64esm(self.data[1], self.data[0]) }; + n0 ^= k1[0]; + n1 ^= k1[1]; + self.data[0] = unsafe { aes64es(n0, n1) }; + self.data[1] = unsafe { aes64es(n1, n0) }; + } + + #[inline(always)] + pub(super) fn dec1_two_more(&mut self, k0: RoundKey, k1: RoundKey) { + let mut n0 = unsafe { aes64dsm(self.data[0], self.data[1]) }; + let mut n1 = unsafe { aes64dsm(self.data[1], self.data[0]) }; + self.data[0] = n0 ^ k1[0]; + self.data[1] = n1 ^ k1[1]; + n0 = unsafe { aes64dsm(self.data[0], self.data[1]) }; + n1 = unsafe { aes64dsm(self.data[1], self.data[0]) }; + self.data[0] = n0 ^ k0[0]; + self.data[1] = n1 ^ k0[1]; + } + + #[inline(always)] + pub(super) fn dec1_two_last(&mut self, k0: RoundKey, k1: RoundKey) { + let mut n0 = unsafe { aes64dsm(self.data[0], self.data[1]) }; + let mut n1 = unsafe { aes64dsm(self.data[1], self.data[0]) }; + self.data[0] = n0 ^ k1[0]; + self.data[1] = n1 ^ k1[1]; + n0 = unsafe { aes64ds(self.data[0], self.data[1]) }; + n1 = unsafe { aes64ds(self.data[1], self.data[0]) }; + self.data[0] = n0 ^ k0[0]; + self.data[1] = n1 ^ k0[1]; + } + } + + pub(super) struct CipherState8 { + data: [CipherState1; 8], + } + + impl core::ops::Index for CipherState8 + where + [CipherState1]: core::ops::Index, + { + type Output = <[CipherState1] as core::ops::Index>::Output; + + #[inline(always)] + fn index(&self, index: I) -> &Self::Output { + &self.data[index] + } + } + + impl core::ops::IndexMut for CipherState8 + where + [CipherState1]: core::ops::IndexMut, + { + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.data[index] + } + } + + impl CipherState8 { + #[inline(always)] + pub(super) fn load8(blocks: &Block8) -> Self { + Self { + data: [ + CipherState1::load1(&blocks[0]), + CipherState1::load1(&blocks[1]), + CipherState1::load1(&blocks[2]), + CipherState1::load1(&blocks[3]), + CipherState1::load1(&blocks[4]), + CipherState1::load1(&blocks[5]), + CipherState1::load1(&blocks[6]), + CipherState1::load1(&blocks[7]), + ], + } + } + + #[inline(always)] + pub(super) fn save8(self, blocks: &mut Block8) { + for (i, state) in self.data.into_iter().enumerate() { + state.save1(&mut blocks[i]); + } + } + + #[inline(always)] + pub(super) fn xor8(&mut self, key: &RoundKey) { + for state in &mut self.data { + state.xor1(key); + } + } + + #[inline(always)] + pub(super) fn enc8_two_more(&mut self, k0: RoundKey, k1: RoundKey) { + for state in &mut self.data { + state.enc1_two_more(k0, k1); + } + } + + #[inline(always)] + pub(super) fn enc8_two_last(&mut self, k0: RoundKey, k1: RoundKey) { + for state in &mut self.data { + state.enc1_two_last(k0, k1); + } + } + + #[inline(always)] + pub(super) fn dec8_two_more(&mut self, k0: RoundKey, k1: RoundKey) { + for state in &mut self.data { + state.dec1_two_more(k0, k1); + } + } + + #[inline(always)] + pub(super) fn dec8_two_last(&mut self, k0: RoundKey, k1: RoundKey) { + for state in &mut self.data { + state.dec1_two_last(k0, k1); + } + } + } +} diff --git a/aes/src/riscv64/expand.rs b/aes/src/riscv64/expand.rs new file mode 100644 index 00000000..fb66f856 --- /dev/null +++ b/aes/src/riscv64/expand.rs @@ -0,0 +1,206 @@ +use super::{RoundKey, RoundKeys}; +use core::{ + arch::riscv64::*, + mem::{transmute, MaybeUninit}, + ptr::addr_of_mut, +}; + +pub(super) struct KeyScheduleState { + data: [u64; 4], + expanded_keys: [MaybeUninit; N], +} + +impl KeyScheduleState<11> { + #[inline(always)] + fn load(user_key: &[u8; 16]) -> Self { + let user_key = user_key.as_ptr().cast::(); + let mut data: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; + unsafe { data[0].write(user_key.add(0).read_unaligned()) }; + unsafe { data[1].write(user_key.add(1).read_unaligned()) }; + let mut state = Self { + data: unsafe { transmute(data) }, + expanded_keys: unsafe { MaybeUninit::uninit().assume_init() }, + }; + state.save_one_keys(0); + state + } + + #[inline(always)] + fn save_one_keys(&mut self, i: u8) { + let i = usize::from(i); + let expanded_keys = self.expanded_keys[i].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0]).write(self.data[0]) }; + unsafe { addr_of_mut!((*expanded_keys)[1]).write(self.data[1]) }; + } + + #[inline(always)] + fn one_key_rounds(&mut self) { + let s = unsafe { aes64ks1i(self.data[1], RNUM) }; + self.data[0] = unsafe { aes64ks2(s, self.data[0]) }; + self.data[1] = unsafe { aes64ks2(self.data[0], self.data[1]) }; + self.save_one_keys(RNUM + 1) + } + + #[inline(always)] + pub(super) fn expand_key(user_key: &[u8; 16]) -> RoundKeys<11> { + let mut state = Self::load(user_key); + state.one_key_rounds::<0>(); + state.one_key_rounds::<1>(); + state.one_key_rounds::<2>(); + state.one_key_rounds::<3>(); + state.one_key_rounds::<4>(); + state.one_key_rounds::<5>(); + state.one_key_rounds::<6>(); + state.one_key_rounds::<7>(); + state.one_key_rounds::<8>(); + state.one_key_rounds::<9>(); + unsafe { transmute(state.expanded_keys) } + } +} + +impl KeyScheduleState<13> { + #[inline(always)] + fn load(user_key: &[u8; 24]) -> Self { + let user_key = user_key.as_ptr().cast::(); + let mut data: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; + unsafe { data[0].write(user_key.add(0).read_unaligned()) }; + unsafe { data[1].write(user_key.add(1).read_unaligned()) }; + unsafe { data[2].write(user_key.add(2).read_unaligned()) }; + let mut state = Self { + data: unsafe { transmute(data) }, + expanded_keys: unsafe { MaybeUninit::uninit().assume_init() }, + }; + state.save_one_and_one_half_keys(0); + state + } + + #[inline(always)] + fn save_one_keys(&mut self, i: u8) { + let n = usize::from(i) * 3 / 2; + let k = usize::from(i) % 2; + let expanded_keys = self.expanded_keys[n + 0].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0 + k]).write(self.data[0]) }; + let expanded_keys = self.expanded_keys[n + k].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[1 - k]).write(self.data[1]) }; + } + + #[inline(always)] + fn save_one_and_one_half_keys(&mut self, i: u8) { + let n = usize::from(i) * 3 / 2; + let k = usize::from(i) % 2; + let expanded_keys = self.expanded_keys[n + 0].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0 + k]).write(self.data[0]) }; + let expanded_keys = self.expanded_keys[n + k].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[1 - k]).write(self.data[1]) }; + let expanded_keys = self.expanded_keys[n + 1].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0 + k]).write(self.data[2]) }; + } + + #[inline(always)] + fn one_key_rounds(&mut self) { + let s = unsafe { aes64ks1i(self.data[2], RNUM) }; + self.data[0] = unsafe { aes64ks2(s, self.data[0]) }; + self.data[1] = unsafe { aes64ks2(self.data[0], self.data[1]) }; + self.save_one_keys(RNUM + 1) + } + + #[inline(always)] + fn one_and_one_half_key_rounds(&mut self) { + let s = unsafe { aes64ks1i(self.data[2], RNUM) }; + self.data[0] = unsafe { aes64ks2(s, self.data[0]) }; + self.data[1] = unsafe { aes64ks2(self.data[0], self.data[1]) }; + self.data[2] = unsafe { aes64ks2(self.data[1], self.data[2]) }; + self.save_one_and_one_half_keys(RNUM + 1) + } + + #[inline(always)] + pub(super) fn expand_key(user_key: &[u8; 24]) -> RoundKeys<13> { + let mut state = Self::load(user_key); + state.one_and_one_half_key_rounds::<0>(); + state.one_and_one_half_key_rounds::<1>(); + state.one_and_one_half_key_rounds::<2>(); + state.one_and_one_half_key_rounds::<3>(); + state.one_and_one_half_key_rounds::<4>(); + state.one_and_one_half_key_rounds::<5>(); + state.one_and_one_half_key_rounds::<6>(); + state.one_key_rounds::<7>(); + unsafe { transmute(state.expanded_keys) } + } +} + +impl KeyScheduleState<15> { + #[inline(always)] + fn load(user_key: &[u8; 32]) -> Self { + let user_key = user_key.as_ptr().cast::(); + let mut data: [MaybeUninit; 4] = unsafe { MaybeUninit::uninit().assume_init() }; + unsafe { data[0].write(user_key.add(0).read_unaligned()) }; + unsafe { data[1].write(user_key.add(1).read_unaligned()) }; + unsafe { data[2].write(user_key.add(2).read_unaligned()) }; + unsafe { data[3].write(user_key.add(3).read_unaligned()) }; + let mut state = Self { + data: unsafe { transmute(data) }, + expanded_keys: unsafe { MaybeUninit::uninit().assume_init() }, + }; + state.save_two_keys(0); + state + } + + #[inline(always)] + fn save_one_keys(&mut self, i: u8) { + let i = usize::from(i); + let expanded_keys = self.expanded_keys[2 * i + 0].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0]).write(self.data[0]) }; + unsafe { addr_of_mut!((*expanded_keys)[1]).write(self.data[1]) }; + } + + #[inline(always)] + fn save_two_keys(&mut self, i: u8) { + let i = usize::from(i); + let expanded_keys = self.expanded_keys[2 * i + 0].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0]).write(self.data[0]) }; + unsafe { addr_of_mut!((*expanded_keys)[1]).write(self.data[1]) }; + let expanded_keys = self.expanded_keys[2 * i + 1].as_mut_ptr(); + unsafe { addr_of_mut!((*expanded_keys)[0]).write(self.data[2]) }; + unsafe { addr_of_mut!((*expanded_keys)[1]).write(self.data[3]) }; + } + + #[inline(always)] + fn two_key_rounds(&mut self) { + let s = unsafe { aes64ks1i(self.data[3], RNUM) }; + self.data[0] = unsafe { aes64ks2(s, self.data[0]) }; + self.data[1] = unsafe { aes64ks2(self.data[0], self.data[1]) }; + let s = unsafe { aes64ks1i(self.data[1], 0xA) }; + self.data[2] = unsafe { aes64ks2(s, self.data[2]) }; + self.data[3] = unsafe { aes64ks2(self.data[2], self.data[3]) }; + self.save_two_keys(RNUM + 1); + } + + #[inline(always)] + fn one_key_rounds(&mut self) { + let s = unsafe { aes64ks1i(self.data[3], RNUM) }; + self.data[0] = unsafe { aes64ks2(s, self.data[0]) }; + self.data[1] = unsafe { aes64ks2(self.data[0], self.data[1]) }; + self.save_one_keys(RNUM + 1); + } + + #[inline(always)] + pub(super) fn expand_key(user_key: &[u8; 32]) -> RoundKeys<15> { + let mut state = Self::load(user_key); + state.two_key_rounds::<0>(); + state.two_key_rounds::<1>(); + state.two_key_rounds::<2>(); + state.two_key_rounds::<3>(); + state.two_key_rounds::<4>(); + state.two_key_rounds::<5>(); + state.one_key_rounds::<6>(); + unsafe { transmute(state.expanded_keys) } + } +} + +#[inline(always)] +pub(super) fn inv_expanded_keys(keys: &mut RoundKeys) { + for i in 1..N - 1 { + keys[i][0] = unsafe { aes64im(keys[i][0]) }; + keys[i][1] = unsafe { aes64im(keys[i][1]) }; + } +} diff --git a/aes/src/riscv64/test_expand.rs b/aes/src/riscv64/test_expand.rs new file mode 100644 index 00000000..3aa5d0f3 --- /dev/null +++ b/aes/src/riscv64/test_expand.rs @@ -0,0 +1,159 @@ +use super::{inv_expanded_keys, KeyScheduleState, RoundKeys}; +use hex_literal::hex; + +const AES128_KEY: [u8; 16] = hex!("2b7e151628aed2a6abf7158809cf4f3c"); +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"), +]; +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"), +]; + +const AES192_KEY: [u8; 24] = hex!("8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"); +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"), +]; +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"), +]; + +const AES256_KEY: [u8; 32] = + hex!("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); +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"), +]; +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"), +]; + +fn load_expanded_keys(input: [[u8; 16]; N]) -> RoundKeys { + let mut output = [[0u64; 2]; N]; + for (src, dst) in input.iter().zip(output.iter_mut()) { + let ptr = src.as_ptr().cast::(); + dst[0] = unsafe { core::ptr::read(ptr.add(0)) }; + dst[1] = unsafe { core::ptr::read(ptr.add(1)) }; + } + output +} + +fn store_expanded_keys(input: RoundKeys) -> [[u8; 16]; N] { + let mut output = [[0u8; 16]; N]; + for (src, dst) in input.iter().zip(output.iter_mut()) { + let b0 = src[0].to_ne_bytes(); + let b1 = src[1].to_ne_bytes(); + dst[00..08].copy_from_slice(&b0); + dst[08..16].copy_from_slice(&b1); + } + output +} + +#[test] +fn aes128_key_expansion() { + let ek = KeyScheduleState::<11>::expand_key(&AES128_KEY); + assert_eq!(store_expanded_keys(ek), AES128_EXP_KEYS); +} + +#[test] +fn aes128_key_expansion_inv() { + let mut ek = load_expanded_keys(AES128_EXP_KEYS); + inv_expanded_keys(&mut ek); + assert_eq!(store_expanded_keys(ek), AES128_EXP_INVKEYS); +} + +#[test] +fn aes192_key_expansion() { + let ek = KeyScheduleState::<13>::expand_key(&AES192_KEY); + assert_eq!(store_expanded_keys(ek), AES192_EXP_KEYS); +} + +#[test] +fn aes192_key_expansion_inv() { + let mut ek = load_expanded_keys(AES192_EXP_KEYS); + inv_expanded_keys(&mut ek); + assert_eq!(store_expanded_keys(ek), AES192_EXP_INVKEYS); +} + +#[test] +fn aes256_key_expansion() { + let ek = KeyScheduleState::<15>::expand_key(&AES256_KEY); + assert_eq!(store_expanded_keys(ek), AES256_EXP_KEYS); +} + +#[test] +fn aes256_key_expansion_inv() { + let mut ek = load_expanded_keys(AES256_EXP_KEYS); + inv_expanded_keys(&mut ek); + assert_eq!(store_expanded_keys(ek), AES256_EXP_INVKEYS); +}