initial commit

This commit is contained in:
weaversa committed 2026-08-07 16:39:11 +00:00
commit 7d0528bd99
241 files changed
+15594

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
#! /bin/sh
# Run `cargo build --release` on all Rust projects.
set -e
CARGO_TOML_FILES=`find . -name "Cargo.toml"`
for f in $CARGO_TOML_FILES; do cargo build --manifest-path $f --release; done;
+9
View File
@@ -0,0 +1,9 @@
#! /bin/sh
# Run `cargo saw-build` on all Rust projects.
set -e
CARGO_TOML_FILES=`find . -name "Cargo.toml"`
for f in $CARGO_TOML_FILES; do cargo saw-build --manifest-path $f --release; done
# Rename mir json files and move them next to associated `Cargo.toml`.
find . -name "*.linked-mir.json" -exec sh -c "mv '{}' \$(dirname '{}')/../../../../linked-mir.json" \;
+14
View File
@@ -0,0 +1,14 @@
import glob
import cryptol
# Create a list of all `.cry` files.
cryptol_files = glob.glob(r"**/*.cry", recursive=True)
# Connect to `cryptol-remote-api`.
c = cryptol.connect()
# Attempt to load all `.cry` files. This will raise an exception if a
# file does not exist or load properly.
for f in cryptol_files:
print(f'Loading {f}')
c.load_file(f).result()
+8
View File
@@ -0,0 +1,8 @@
#! /bin/sh
# Run all `saw.py` files in virtual environment for SAW Remote API.
set -e
export SAW_SOLVER_CACHE_PATH=${SAW_SOLVER_CACHE_PATH:=$(pwd)/saw-cache}
export VENV_SAW=${VENV_SAW:=/opt/venv/saw-remote-api}
SAWFILES=`find . -name "saw.py"`
for f in $SAWFILES; do $VENV_SAW/bin/python3 $f; done;
+7
View File
@@ -0,0 +1,7 @@
#! /bin/sh
# Run `saw` on all `all.saw` files.
set -e
export SAW_SOLVER_CACHE_PATH=${SAW_SOLVER_CACHE_PATH:=$(pwd)/saw-cache}
SAWFILES=`find . -name "all.saw"`
for f in $SAWFILES; do saw $f; done;
@@ -0,0 +1 @@
{"image":"ghcr.io/nationalsecurityagency/foundation/cryptol-dev:latest"}
+1
View File
@@ -0,0 +1 @@
{"image":"ghcr.io/nationalsecurityagency/foundation/saw-dev:latest"}
+17
View File
@@ -0,0 +1,17 @@
# See https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "cargo"
directories:
- "algorithms/AES/rust/KeyExpansion/"
- "algorithms/AES/rust/Cipher/"
- "shared/rust/ffi_utils/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: ".github/workflows"
schedule:
interval: "daily"
+44
View File
@@ -0,0 +1,44 @@
FROM public.ecr.aws/lts/ubuntu:24.04_stable
USER root
RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get dist-upgrade --yes \
&& DEBIAN_FRONTEND="noninteractive" apt-get install --yes ca-certificates curl gnupg lsb-release \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& chmod a+r /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& DEBIAN_FRONTEND="noninteractive" apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install --yes \
bash \
build-essential \
containerd.io \
docker-ce \
docker-ce-cli \
docker-compose-plugin \
dos2unix \
exiftool \
libx11-6 \
locales \
m4 \
pkg-config \
python3 \
python3-pip \
python3-venv \
software-properties-common \
sudo \
unzip \
wget \
zip \
# Install latest Git using their official PPA
&& add-apt-repository ppa:git-core/ppa \
&& DEBIAN_FRONTEND="noninteractive" apt-get install --yes git \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Enables Docker starting with systemd
RUN systemctl enable docker
# Make typing unicode characters in the terminal work.
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8
@@ -0,0 +1,11 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
COPY --from=ghcr.io/galoisinc/crux-mir:latest --chown=root:root /home/crux-mir/.cargo/* /usr/local/bin/
COPY --from=ghcr.io/galoisinc/crux-mir:latest --chown=root:root /home/crux-mir/.rustup /usr/local/rustup
COPY --from=ghcr.io/galoisinc/crux-mir:latest /usr/local/bin/* /usr/local/bin/
COPY --from=ghcr.io/galoisinc/crux-mir:latest /crux-mir/rlibs /crux-mir/rlibs
ENV CARGO_HOME=/usr/local/cargo \
RUSTUP_HOME=/usr/local/rustup \
CRUX_RUST_LIBRARY_PATH=/crux-mir/rlibs \
LD_LIBRARY_PATH=/usr/local/lib:/usr/local/rustup/lib
+21
View File
@@ -0,0 +1,21 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
# Setup global install for cargo, rustup, and kani.
RUN mkdir /usr/local/share/.rustup
ENV RUSTUP_HOME=/usr/local/share/.rustup
RUN mkdir /usr/local/share/.cargo
ENV CARGO_HOME=/usr/local/share/.cargo
RUN mkdir /usr/local/share/.kani
ENV KANI_HOME=/usr/local/share/.kani
# Add various bins to PATH
ENV PATH="${CARGO_HOME}/bin:${RUSTUP_HOME}/bin:${KANI_HOME}/bin:${PATH}"
# Get Rust, Kani, and Bolero
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y \
&& cargo install --locked kani-verifier \
&& cargo kani setup \
&& cargo install cargo-bolero \
&& chmod a+rw -R /usr/local/share/.cargo /usr/local/share/.rustup /usr/local/share/.kani
+20
View File
@@ -0,0 +1,20 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
RUN apt-get update -y \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y \
make \
cmake \
libgmp-dev \
libssl-dev \
libgomp1 \
libnuma-dev \
autoconf \
build-essential \
clang-18 \
clang-tools-18 \
clang-format-18 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Link clang-18 utils
RUN find /usr/bin/ -name "*-18" -exec basename {} \; | sed "s/\-18//" | xargs -I{} ln -s /usr/bin/'{}'-18 /usr/bin/'{}'
@@ -0,0 +1,48 @@
FROM ghcr.io/nationalsecurityagency/foundation/cryptol-tools:latest
# Install some tools to assist with development.
RUN apt-get update -y \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y \
vim \
openssh-client \
bash-completion \
htop \
man \
graphviz \
make \
cmake \
libgmp-dev \
libssl-dev \
libgomp1 \
libnuma-dev \
autoconf \
build-essential \
clang-18 \
clang-tools-18 \
clang-format-18 \
emacs \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Link clang-18 utils
RUN find /usr/bin/ -name "*-18" -exec basename {} \; | sed "s/\-18//" | xargs -I{} ln -s /usr/bin/'{}'-18 /usr/bin/'{}'
# Setup global install for cargo, rustup, and kani.
RUN mkdir /usr/local/share/.rustup
ENV RUSTUP_HOME=/usr/local/share/.rustup
RUN mkdir /usr/local/share/.cargo
ENV CARGO_HOME=/usr/local/share/.cargo
RUN mkdir /usr/local/share/.kani
ENV KANI_HOME=/usr/local/share/.kani
# Add various bins to PATH
ENV PATH="${CARGO_HOME}/bin:${RUSTUP_HOME}/bin:${KANI_HOME}/bin:${PATH}"
# Get Rust, Kani, and Bolero
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y \
&& cargo install --locked kani-verifier \
&& cargo kani setup \
&& cargo install cargo-bolero \
&& chmod a+rw -R /usr/local/share/.cargo /usr/local/share/.rustup /usr/local/share/.kani
@@ -0,0 +1,10 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
# Install Python client for Cryptol in a virtual environment
ENV VENV_CRYPTOL=/opt/venv/cryptol-remote-api
RUN python3 -m venv $VENV_CRYPTOL \
&& $VENV_CRYPTOL/bin/pip --no-cache-dir install pyparsing tabulate numpy scipy "https://github.com/GaloisInc/cryptol/archive/refs/heads/master.tar.gz#subdirectory=cryptol-remote-api/python"
RUN useradd -m cryptol && chown -R cryptol:cryptol /home/cryptol
USER cryptol
WORKDIR /home/cryptol
@@ -0,0 +1,5 @@
FROM ghcr.io/galoisinc/cryptol-remote-api:latest
CMD ["--max-occupancy", "10", "http", "--host", "0.0.0.0", "--port", "8080", "/"]
EXPOSE 8080
@@ -0,0 +1,56 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install --yes \
# cryptol and saw deps
libgmp-dev \
libssl-dev \
libgomp1 \
libffi8 \
libncurses6 \
libtinfo6 \
libreadline8 \
libnuma-dev \
openssl \
openjdk-11-jdk-headless \
# qbf-tools
libglpk40 \
libboost-iostreams1.74.0 \
libboost-program-options1.74.0 \
libreadline8 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install SAW and saw-remote-api
COPY --from=ghcr.io/galoisinc/saw:latest /usr/local/bin /usr/local/bin
COPY --from=ghcr.io/galoisinc/saw-remote-api:latest /usr/local/bin/saw-remote-api /usr/local/bin/saw-remote-api
ENV SAW_SERVER_URL=http://0.0.0.0:36691
RUN echo 'saw-remote-api --read-only http --host 0.0.0.0 --port 36691 / &' >> /usr/local/bin/start-saw-remote-api-read-only \
&& echo 'saw-remote-api http --host 0.0.0.0 --port 36691 / &' >> /usr/local/bin/start-saw-remote-api \
&& chmod a+x /usr/local/bin/start-saw-remote-api /usr/local/bin/start-saw-remote-api-read-only
# Install Cryptol and cryptol-remote-api
COPY --from=ghcr.io/galoisinc/cryptol:latest /usr/local/bin /usr/local/bin
COPY --from=ghcr.io/galoisinc/cryptol-remote-api:latest /usr/local/bin/cryptol-remote-api /usr/local/bin/cryptol-remote-api
ENV CRYPTOL_SERVER_URL=http://0.0.0.0:36681
RUN echo 'cryptol-remote-api --read-only --max-occupancy 10 http --host 0.0.0.0 --port 36681 / &' >> /usr/local/bin/start-cryptol-remote-api-read-only \
&& echo 'cryptol-remote-api --max-occupancy 10 http --host 0.0.0.0 --port 36681 / &' >> /usr/local/bin/start-cryptol-remote-api \
&& chmod a+x /usr/local/bin/start-cryptol-remote-api /usr/local/bin/start-cryptol-remote-api-read-only
# Add --no-call-stacks option to cryptol interpreter
RUN mv /usr/local/bin/cryptol /usr/local/bin/_cryptol \
&& echo '/usr/local/bin/_cryptol --no-call-stacks $@' > /usr/local/bin/cryptol \
&& chmod a+x /usr/local/bin/cryptol
# Get latest what4-solvers compiled for ubuntu
RUN wget https://github.com/GaloisInc/what4-solvers/releases/download/snapshot-20260622/ubuntu-24.04-X64-bin.zip \
&& unzip -o ubuntu-24.04-X64-bin.zip -d /usr/local/bin \
&& rm -rf ubuntu-24.04-X64-bin.zip \
&& chmod a+x /usr/local/bin/*
# Install Python clients for Cryptol and SAW in virtual environments
ENV VENV_CRYPTOL=/opt/venv/cryptol-remote-api
ENV VENV_SAW=/opt/venv/saw-remote-api
RUN python3 -m venv $VENV_CRYPTOL \
&& $VENV_CRYPTOL/bin/pip --no-cache-dir install pyparsing tabulate numpy scipy "https://github.com/GaloisInc/cryptol/archive/refs/heads/master.tar.gz#subdirectory=cryptol-remote-api/python" \
&& python3 -m venv $VENV_SAW \
&& $VENV_SAW/bin/pip --no-cache-dir install pyparsing tabulate numpy scipy "https://github.com/GaloisInc/saw-script/archive/refs/heads/master.tar.gz#subdirectory=saw-python"
@@ -0,0 +1,25 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install --yes \
libgmp10 \
libgomp1 \
libffi8 \
wget \
libncurses6 \
unzip \
libreadline-dev \
openjdk-11-jdk-headless \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/galoisinc/saw:latest /usr/local/bin /usr/local/bin
COPY --from=ghcr.io/galoisinc/saw-remote-api:latest /usr/local/bin/saw-remote-api /usr/local/bin/saw-remote-api
# Install Python client for SAW in a virtual environment
ENV VENV_SAW=/opt/venv/saw-remote-api
RUN python3 -m venv $VENV_SAW \
&& $VENV_SAW/bin/pip --no-cache-dir install pyparsing tabulate numpy scipy "https://github.com/GaloisInc/saw-script/archive/refs/heads/master.tar.gz#subdirectory=saw-python"
RUN useradd -m saw && chown -R saw:saw /home/saw
USER saw
WORKDIR /home/saw
@@ -0,0 +1,3 @@
FROM ghcr.io/nationalsecurityagency/foundation/foundation-saw-base:latest
ADD foundation.tar .
@@ -0,0 +1,3 @@
FROM ghcr.io/nationalsecurityagency/foundation/cryptol-remote-api:latest
ADD foundation.tar .
+38
View File
@@ -0,0 +1,38 @@
FROM ghcr.io/nationalsecurityagency/foundation/cryptol-tools:latest
# Install some tools to assist with development.
RUN apt-get update -y \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y \
vim \
openssh-client \
bash-completion \
htop \
man \
graphviz \
make \
cmake \
libgmp-dev \
libssl-dev \
libgomp1 \
libnuma-dev \
autoconf \
build-essential \
clang-18 \
clang-tools-18 \
clang-format-18 \
emacs \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Link clang-18 utils
RUN find /usr/bin/ -name "*-18" -exec basename {} \; | sed "s/\-18//" | xargs -I{} ln -s /usr/bin/'{}'-18 /usr/bin/'{}'
COPY --from=ghcr.io/galoisinc/crux-mir:latest --chown=root:root /home/crux-mir/.cargo/* /usr/local/bin/
COPY --from=ghcr.io/galoisinc/crux-mir:latest --chown=root:root /home/crux-mir/.rustup /usr/local/rustup
COPY --from=ghcr.io/galoisinc/crux-mir:latest /usr/local/bin/* /usr/local/bin/
COPY --from=ghcr.io/galoisinc/crux-mir:latest /crux-mir/rlibs /crux-mir/rlibs
ENV CARGO_HOME=/usr/local/cargo \
RUSTUP_HOME=/usr/local/rustup \
CRUX_RUST_LIBRARY_PATH=/crux-mir/rlibs \
LD_LIBRARY_PATH=/usr/local/lib:/usr/local/rustup/lib
@@ -0,0 +1,10 @@
FROM ghcr.io/nationalsecurityagency/foundation/base:latest
# Install Python client for SAW in a virtual environment
ENV VENV_SAW=/opt/venv/saw-remote-api
RUN python3 -m venv $VENV_SAW \
&& $VENV_SAW/bin/pip --no-cache-dir install pyparsing tabulate numpy scipy "https://github.com/GaloisInc/saw-script/archive/refs/heads/master.tar.gz#subdirectory=saw-python"
RUN useradd -m saw && chown -R saw:saw /home/saw
USER saw
WORKDIR /home/saw
@@ -0,0 +1,5 @@
FROM ghcr.io/galoisinc/saw-remote-api:latest
CMD ["http", "--host", "0.0.0.0", "--port", "8080", "/"]
EXPOSE 8080
+1
View File
@@ -0,0 +1 @@
FROM ghcr.io/galoisinc/saw:latest
+41
View File
@@ -0,0 +1,41 @@
# GitHub Workflows
## Infrastructure
Workflows prefixed with `infrastructure` contain GitHub Actions for
building the suite of Docker images needed to maintain assurance of
the various components of this repository (specifications, reference
implementations, etc.). Also included here are workflows for building
a development Docker image suitable for use with a containerized
development environment.
### Updating the Infrastructure
These workflows may be started manually, for example, when a new
version of some tool (such as Cryptol) is released. Before running
these workflows on the `main` branch, they should first be tested in a
new branch and any issues resolved in a pull request to the point that
all CI tests pass.
## Release
Workflows prefixed with `release` contain GitHub Actions for building
Docker images containing the `foundation` project as well as tooling
needed to interact with the project in the various workflows here that
create assurance artifacts.
## Assurance
Workflows prefixed with `assurance` contain GitHub Actions that levy
invariants on and create assurance artifacts from the project.
## Cleanup
Workflows prefixed with `cleanup` contain GitHub Actions for cleaning
up the repository.
### Registry
All images built here are stored in [GitHub's container registry for
this `foundation`
project](https://github.com/orgs/NationalSecurityAgency/packages?tab=packages&q=foundation).
+86
View File
@@ -0,0 +1,86 @@
name: Create assurance artifacts
on:
pull_request:
push:
branches:
- 'main'
schedule:
- cron: '0 22 * * *'
env:
CRYPTOL_SERVER_URL: 'http://foundation:8080'
SAW_SERVER_URL: 'http://localhost:8080'
SAW_SOLVER_CACHE_PATH: 'saw-cache'
VENV_CRYPTOL: '/opt/venv/cryptol-remote-api'
VENV_SAW: '/opt/venv/saw-remote-api'
jobs:
# This job builds the foundation image for this pipeline.
build-foundation-image:
uses: ./.github/workflows/release-build-foundation-image.yml
with:
sha: ${{ github.sha }}
# This job attempts to load all Cryptol files in foundation.
load-all-cry-files:
needs: build-foundation-image
runs-on: ubuntu-latest
container: ghcr.io/nationalsecurityagency/foundation/cryptol-remote-api-client:latest
services:
foundation:
image: ghcr.io/nationalsecurityagency/foundation/foundation:${{ github.sha }}
ports:
- 8080:8080
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Run python script to load all Cryptol files in virtual environment for Cryptol Remote API
run: $VENV_CRYPTOL/bin/python3 .ci/load-all-cry-files.py
# This job attempts to run top-level SAW files in foundation.
load-top-level-SAW-files:
needs: build-foundation-image
runs-on: ubuntu-latest
container: ghcr.io/nationalsecurityagency/foundation/foundation-saw:${{ github.sha }}
steps:
- name: Prefix 'pwd' onto SAW_SOLVER_CACHE_PATH
run: echo "SAW_SOLVER_CACHE_PATH=/home/saw/$SAW_SOLVER_CACHE_PATH" >> $GITHUB_ENV
- name: Restore SAW proof cache
uses: actions/cache/restore@v6
with:
path: ${{ env.SAW_SOLVER_CACHE_PATH }}
key: ${{ github.ref_name }}-saw-cache-${{ github.sha }}
restore-keys: |
${{ github.ref_name }}-saw-cache-
main-saw-cache-
- name: Clean SAW proof cache
run: saw --clean-mismatched-versions-solver-cache
- name: Run top-level SAW files.
working-directory: /home/saw
run: .ci/run-top-level-saw-files.sh
# This job attempts to run top-level SAW python files in foundation.
load-top-level-SAW-python-files:
needs: build-foundation-image
runs-on: ubuntu-latest
container: ghcr.io/nationalsecurityagency/foundation/foundation-saw:${{ github.sha }}
steps:
- name: Prefix 'pwd' onto SAW_SOLVER_CACHE_PATH
run: echo "SAW_SOLVER_CACHE_PATH=/home/saw/$SAW_SOLVER_CACHE_PATH" >> $GITHUB_ENV
- name: Restore SAW proof cache
uses: actions/cache/restore@v6
with:
path: ${{ env.SAW_SOLVER_CACHE_PATH }}
key: ${{ github.ref_name }}-saw-rust-cache-${{ github.sha }}
restore-keys: |
${{ github.ref_name }}-saw-rust-cache-
main-saw-rust-cache-
- name: Clean SAW proof cache
run: saw --clean-mismatched-versions-solver-cache
- name: Run top-level SAW python files.
working-directory: /home/saw
run: |
saw-remote-api http --host 0.0.0.0 --port 8080 / &
.ci/run-saw-python-files.sh
@@ -0,0 +1,33 @@
name: Cleanup caches when a branch is closed
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,42 @@
name: Build image template
on:
workflow_call:
inputs:
dockerfile:
type: string
required: true
jobs:
build-layer:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to ghcr.io
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push to branch
uses: docker/build-push-action@v7
if: github.ref_name != 'main'
with:
context: .
push: true
file: .github/dockerfiles/${{ inputs.dockerfile }}.Dockerfile
tags: ghcr.io/nationalsecurityagency/foundation/${{ inputs.dockerfile }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=min
- name: Build and push to main
uses: docker/build-push-action@v7
if: github.ref_name == 'main'
with:
context: .
push: true
file: .github/dockerfiles/${{ inputs.dockerfile }}.Dockerfile
tags: ghcr.io/nationalsecurityagency/foundation/${{ inputs.dockerfile }}:latest
cache-from: type=gha
cache-to: type=gha,mode=min
@@ -0,0 +1,80 @@
name: Build images
on:
workflow_dispatch:
jobs:
base:
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "base"
cryptol-remote-api:
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cryptol-remote-api"
saw:
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "saw"
saw-remote-api:
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "saw-remote-api"
cryptol-remote-api-client:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cryptol-remote-api-client"
saw-remote-api-client:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "saw-remote-api-client"
foundation-saw-base:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "foundation-saw-base"
cargo:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cargo"
cargo-saw-build:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cargo-saw-build"
clang:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "clang"
cryptol-tools:
needs: base
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cryptol-tools"
cryptol-dev:
needs: cryptol-tools
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "cryptol-dev"
saw-dev:
needs: cryptol-tools
uses: ./.github/workflows/infrastructure-build-image-template.yml
with:
dockerfile: "saw-dev"
@@ -0,0 +1,67 @@
name: Build foundation container template
on:
workflow_call:
inputs:
sha:
type: string
required: true
jobs:
build-foundation-saw-container:
runs-on: ubuntu-latest
container:
image: ghcr.io/nationalsecurityagency/foundation/cargo-saw-build:latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Build Rust artifacts for SAW
run: .ci/build-rust-saw-artifacts.sh
- name: Tar all `.cry`, `.saw`, `linked-mir.json`, and `saw.py` files
run: find . -name "*.cry" -or -name "*.saw" -or -name "linked-mir.json" -or -name "saw.py" -or -name "*.sh" | tar -cf foundation.tar -T -
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to ghcr.io
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push to branch
uses: docker/build-push-action@v7
with:
context: .
push: true
file: .github/dockerfiles/foundation-saw.Dockerfile
tags: ghcr.io/nationalsecurityagency/foundation/foundation-saw:${{ inputs.sha }}
cache-from: type=gha
cache-to: type=gha,mode=min
build-foundation-container:
runs-on: ubuntu-latest
container:
image: ghcr.io/nationalsecurityagency/foundation/cargo:latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: Build release Rust for Cryptol FFIs
run: .ci/build-rust-ffi.sh
- name: Tar all `.cry` and `.so` files
run: find . -name "*.cry" -or -name "*.so" | tar -cf foundation.tar -T -
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to ghcr.io
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push to branch
uses: docker/build-push-action@v7
with:
context: .
push: true
file: .github/dockerfiles/foundation.Dockerfile
tags: ghcr.io/nationalsecurityagency/foundation/foundation:${{ inputs.sha }}
cache-from: type=gha
cache-to: type=gha,mode=min
+54
View File
@@ -0,0 +1,54 @@
name: Light-weight Rust checking
on:
pull_request:
push:
branches:
- 'main'
jobs:
# This job runs `cargo fmt` on included Rust.
cargo-fmt:
runs-on: ubuntu-latest
container:
image: ghcr.io/nationalsecurityagency/foundation/cargo:latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: cargo fmt
run: |
set -e
CARGO_TOML_FILES=`find . -name "Cargo.toml"`
for f in $CARGO_TOML_FILES; do cargo fmt --manifest-path $f --all -- --check; done;
# This job runs `cargo clippy` on included Rust.
cargo-clippy:
runs-on: ubuntu-latest
container:
image: ghcr.io/nationalsecurityagency/foundation/cargo:latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: cargo clippy
run: |
set -e
CARGO_TOML_FILES=`find . -name "Cargo.toml"`
for f in $CARGO_TOML_FILES; do cargo clippy --manifest-path $f --all-targets --all-features -- -Dclippy::all -Dclippy::pedantic; done;
# This job runs `cargo test` and `cargo kani` on included Rust.
cargo-test:
runs-on: ubuntu-latest
container:
image: ghcr.io/nationalsecurityagency/foundation/cargo:latest
steps:
- name: Check out code
uses: actions/checkout@v7
- name: cargo test
run: |
set -e
CARGO_TOML_FILES=`find . -name "Cargo.toml"`
for f in $CARGO_TOML_FILES; do cargo test --manifest-path $f --all-targets; \
cargo test --manifest-path $f --doc; \
# cargo kani --manifest-path $f --output-format=terse; \
done;
+3
View File
@@ -0,0 +1,3 @@
*~
\#*\#
**/rust/**/target/
+164
View File
@@ -0,0 +1,164 @@
# Contributors Guide
Foundation is an open source project. If you are interested in making
it better, there are many ways you can contribute. For example, you
can:
- Submit a bug report
- Suggest a new feature
- Provide feedback by commenting on feature requests/proposals
- Propose a patch by submitting a pull request
- Suggest or submit documentation improvements
- Review outstanding pull requests
- Answer questions from other users
- Share the software with other users who are interested
- Teach others to use the software
- Package and distribute the software in a downstream community (such
as your preferred Linux distribution)
## Bugs and Feature Requests
If you believe that you have found a bug or wish to propose a new
feature, please first search the existing [issues] to see if it has
already been reported. If you are unable to find an existing issue,
consider using one of the provided templates to create a new issue and
provide as many details as you can to assist in reproducing the bug or
explaining your proposed feature.
## Patch Submission tips
Patches should be submitted in the form of Pull Requests to the
Foundation [repository] on GitHub. But first, consider the following
tips to ensure a smooth process when submitting a patch:
- Ensure that the patch does not break any continuous integration
tests.
- Be understanding, patient, and friendly; developers may need time to
review your submissions before they can take action or respond. This
does not mean your contribution is not valued. If your contribution
has not received a response in a reasonable time, consider
commenting with a polite inquiry for an update.
- Limit your patches to the smallest reasonable change to achieve your
intended goal. For example, do not make unnecessary indentation
changes; but don't go out of your way to make the patch so minimal
that it isn't easy to read, either. Consider the reviewer's
perspective.
- Unless previously authorized by the Foundation team, repackaging,
renaming, and other refactoring should not be part of any pull
request. These types of changes are difficult to review, pollute the
git history making it harder to do git forensics on regressions, and
will likely conflict with other changes that the Foundation team is
making internally.
- Avoid "find and replace" changes in your pull request. While it may
be tempting to globally replace calls to deprecated methods or
change the style of the code to fit your personal preference, these
types of seemingly trivial changes have likely not already been
performed by the Foundation team for good reason.
- Focus your patches on bug fixes that were discovered through
real-world usage and testing, and on improvements that clearly
satisfy a need in Foundation. Before you begin implementing,
consider first opening a dialogue with the Foundation team to
ensure that your efforts will align with the goals of the
project. This will significantly improve the odds that your patch
gets accepted.
- Unless it addresses a critical security update, avoid pull requests
that update the continuous integration pipeline or 3rd party
libraries. It is preferred that these changes are made internally by
the team. If you have a need for an updated library, please submit
an issue with your request instead of a pull request.
- Before submission, please squash your commits to using a message
that starts with the issue number and a description of the changes.
- Isolate multiple patches from each other. If you wish to make
several independent patches, do so in separate, smaller pull
requests that can be reviewed more easily.
- Be prepared to answer questions from reviewers. They may have
further questions before accepting your patch, and may even propose
changes. Please accept this feedback constructively, and not as a
rejection of your proposed change.
## Review
- We welcome code reviews from anyone. A contributor is required to
formally accept and merge the changes.
- Reviewers will be looking for things like readability and code
style, assurance and performance implications, API design,
duplication of existing functionality, avoidance of bloat
(scope-creep), etc.
- Reviewers will likely ask questions to better understand your
change.
- Reviewers will make comments about changes to your patch:
- MUST means that the change is required
- SHOULD means that the change is suggested, further discussion on
the subject may be required
- COULD means that the change is optional.
## Getting Started
Please see the [developer guide][devguide] for instructions to set up
a suitable development environment.
## Timeline and Managing Expectations
As we continue to engage contributors and learn best practices for
running a successful open source project, our processes and guidance
will likely evolve. We will try to communicate expectations as we are
able and to always be responsive. We hope that the community will
share their suggestions for improving this engagement. Based on the
level of initial interest we receive and the availability of resources
to evaluate contributions, we anticipate the following:
- We will initially prioritize pull requests that include small bug
fixes and code that addresses potential vulnerabilities as well as
pull requests that include existing specifications because these
require a reasonable amount of effort to evaluate and will help us
exercise and revise our process for accepting contributions. In
other words, we are going to start small in order to work out the
kinks first.
- We are committed to maintaining the integrity and security of our
code base. In addition to the careful review the maintainers will
give to code contributions to make sure they do not introduce new
bugs or vulnerabilities, we will be trying to identify best
practices to incorporate with our open source project so that
contributors can have more control over whether their contributions
are accepted. These might include things like style guides and
requirements for tests and documentation to accompany some code
contributions. As a result, it may take a long time for some
contributions to be accepted. This does not mean we are ignoring
them.
- We are committed to integrating this GitHub project with our team's
regular development work flow so that the open source project
remains dynamic and relevant. This may affect our responsiveness
and ability to accept pull requests quickly. This does not mean we
are ignoring them.
- Not all innovative ideas need to be accepted as pull requests into
this GitHub project to be valuable to the community. There may be
times when we recommend that you just share your code for some
enhancement to Foundation from your own repository. As we identify
and recognize extensions that are of general interest to the
cryptographic assurance community, we may seek to incorporate them
with our baseline.
## Legal
Consistent with Section D.6. of the GitHub Terms of Service as of
2019, and Section 5. of the Apache License, Version 2.0, the project
maintainer for this project accepts contributions using the
inbound=outbound model. When you submit a pull request to this
repository (inbound), you are agreeing to license your contribution
under the same terms as specified in [LICENSE] (outbound).
This is an open source project. Contributions you make to this public
U.S. Government ("USG") repository are completely voluntary. When you
submit an issue, bug report, question, enhancement, pull request,
etc., you are offering your contribution without expectation of
payment, you expressly waive any future pay claims against the USG
related to your contribution, and you acknowledge that this does not
create an obligation on the part of the USG of any kind. Furthermore,
your contributing to this project does not create an employer-employee
relationship between the United States ("U.S.") Government and the
contributor.
[issues]: https://github.com/NationalSecurityAgency/foundation/issues
[repository]: https://github.com/NationalSecurityAgency/foundation/
[devguide]: docs/DevGuide.md
[LICENSE]: LICENSE
+35
View File
@@ -0,0 +1,35 @@
# Disclaimer of Warranty
This Work is provided "AS IS." Any express or implied warranties,
including but not limited to, the implied warranties of
merchantability and fitness for a particular purpose are disclaimed.
In no event shall the United States Government be liable for any
direct, indirect, incidental, special, exemplary or consequential
damages (including, but not limited to, procurement of substitute
goods or services, loss of use, data or profits, or business
interruption) however caused and on any theory of liability, whether
in contract, strict liability, or tort (including negligence or
otherwise) arising in any way out of the use of this Work, even if
advised of the possibility of such damage.
The User of this Work agrees to hold harmless and indemnify the United
States Government, its agents and employees from every claim or
liability (whether in tort or in contract), including attorney's fees,
court costs, and expenses, arising in direct consequence of
Recipient's use of the item, including, but not limited to, claims or
liabilities made for injury to or death of personnel of User or third
parties, damage to or destruction of property of User or third
parties, and infringement or other violations of intellectual property
or technical data rights.
# Disclaimer of Endorsement
Nothing in this Work is intended to constitute an endorsement,
explicit or implied, by the United States Government of any particular
manufacturer's product or service.
Reference herein to any specific commercial product, process, or
service by trade name, trademark, manufacturer, or otherwise, in this
Work does not constitute an endorsement, recommendation, or favoring
by the United States Government and shall not be used for advertising
or product endorsement purposes.
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+41
View File
@@ -0,0 +1,41 @@
## Foundation
This product includes software developed at National Security Agency
(https://www.nsa.gov)
Portions of this product were created by the U.S. Government and not
subject to U.S. copyright protections under 17 U.S.C.
The remaining portions are copyright their respective authors and have
been contributed under the terms of one or more open source licenses,
and made available to you under the terms of those licenses (see
[LICENSE](LICENSE)).
## Licensing Intent
The intent is that this software and documentation ("Project") should
be treated as if it is licensed under the license associated with the
Project ("License") in the [LICENSE](LICENSE) file. However, because
we are part of the United States (U.S.) Federal Government, it is not
that simple.
The portions of this Project written by U.S. Federal Government
employees within the scope of their federal employment are ineligible
for copyright protection in the U.S.; this is generally understood to
mean that these portions of the Project are placed in the public
domain.
In countries where copyright protection is available (which does not
include the U.S.), contributions made by U.S. Federal Government
employees are released under the [LICENSE](LICENSE). Merged
contributions from private contributors are released under the
[LICENSE](LICENSE).
The Foundation software is released under the Apache License, Version
2.0 ("Apache 2.0").
Consistent with the inbound=outbound model, contributions to any
module must be made available, by the contributor, under the
[LICENSE](LICENSE). Please read the Legal section of the
[CONTRIBUTING.md](CONTRIBUTING.md) guide.
+52
View File
@@ -0,0 +1,52 @@
# Foundation
[Foundation][main] is a repository for formal specifications of
cryptographic algorithms and functional assurance artifacts created
and maintained by the [National Security Agency][nsa] Research
Directorate.
If you are a U.S. citizen interested in projects like this, to develop
Foundation and other cybersecurity tools for NSA to help protect our
nation and its allies, consider applying for a [career with
us][career].
## Workflow Status
This repository uses GitHub Actions to add assurance to its
artifacts. The badges here provide the status of those actions.
[![Build images](https://github.com/NationalSecurityAgency/foundation/actions/workflows/infrastructure-build-images.yml/badge.svg)](https://github.com/NationalSecurityAgency/foundation/actions/workflows/infrastructure-build-images.yml)
[![Create assurance artifacts](https://github.com/NationalSecurityAgency/foundation/actions/workflows/assurance-main.yml/badge.svg)](https://github.com/NationalSecurityAgency/foundation/actions/workflows/assurance-main.yml)
## Develop
Two [development container
configurations](.devcontainer/) are maintained in this
repository and provide the tooling (Cryptol, SAW, supported SAT/SMT
solvers, and more) necessary to support its development. These
configurations are compatible with GitHub's
[Codespaces](https://github.com/codespaces), a service that enables
developers to "Spin up fully configured dev environments in the
cloud", as well as [Visual Studio Code's Dev Container
extension](https://code.visualstudio.com/docs/devcontainers/containers). Using
these configurations in a supported development environment (such as
those listed above) is the preferred way to develop in this
repository.
Start developing immediately:
* [Cryptol Codespace](https://codespaces.new/NationalSecurityAgency/foundation?devcontainer_path=.devcontainer%2Fcryptol-dev%2Fdevcontainer.json)
* [SAW Codespace](https://codespaces.new/NationalSecurityAgency/foundation?devcontainer_path=.devcontainer%2Fsaw-dev%2Fdevcontainer.json)
## Contribute
If you would like to contribute bug fixes, improvements, and new
features back to Foundation, please take a look at our [Contributor
Guide][contrib] and [Developer Guide][devguide] to see how you can
participate in this open source project.
[nsa]: https://www.nsa.gov
[contrib]: CONTRIBUTING.md
[devguide]: docs/DevGuide.md
[career]: https://www.intelligencecareers.gov/nsa
[releases]: https://github.com/NationalSecurityAgency/foundation/releases
[main]: https://github.com/NationalSecurityAgency/foundation/archive/refs/heads/master.zip
+10
View File
@@ -0,0 +1,10 @@
# Bibliography
[FIPS-PUB-197], Advanced Encryption Standard (AES), FIPS Publication
197, National Institute of Standards and Technology (NIST), May 9,
2023. https://doi.org/10.6028/NIST.FIPS.197-upd1
[AES Test Vectors], National Institute of Standards and
Technology. Cryptographic Standards and Guidelines: Examples with
Intermediate Values, 2022.
https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values.
+215
View File
@@ -0,0 +1,215 @@
/*
* Description: This is a Cryptol specification of the `Cipher` and
* `InvCipher` methods in the Advanced Encryption Standard (AES)
* [FIPS-PUB-197].
*/
module algorithms::AES::Cipher where
import algorithms::AES::Types
import algorithms::AES::GF28 ((•))
import algorithms::AES::SBox (SBox, InvSBox)
/**
* This function performs one round of AES encryption [FIPS-PUB-197
* Section 5.1].
*/
Round : Block -> Block -> Block
Round xs ki = AddRoundKey (MixColumns (ShiftRows (SubBytes xs))) ki
/**
* The `Cipher` function performs AES encryption [FIPS-PUB-197
* Section 5.1].
*/
foreign Cipher : {k} (Key k) => [Nr k + 1]Block -> Block -> Block
Cipher ([w0] # ws # [wr]) in = state_final
where
state_initial = AddRoundKey in w0
state_rounds = foldl Round state_initial ws
state_final = AddRoundKey (ShiftRows (SubBytes state_rounds)) wr
/**
* This function performs one round of AES decryption [FIPS-PUB-197
* Section 5.3].
*/
InvRound : Block -> Block -> Block
InvRound xs ki = InvMixColumns (AddRoundKey (InvSubBytes (InvShiftRows xs)) ki)
/**
* This is an alternate version of the `InvRound` function that is a
* proper inverse of `Round`.
*/
InvRound' : Block -> Block -> Block
InvRound' xs ki = InvSubBytes (InvShiftRows (InvMixColumns (AddRoundKey xs ki)))
/**
* This property demonstrates that `Round` and `InvRound'` are
* inverses.
*/
RoundInverts : Block -> Block -> Bool
property RoundInverts xs ki = InvRound' (Round xs ki) ki == xs
/**
* The `InvCipher` function performs AES decryption [FIPS-PUB-197
* Section 5.3].
*/
foreign InvCipher : {k} (Key k) => [Nr k + 1]Block -> Block -> Block
InvCipher ([w0] # ws # [wr]) in = state_final
where
state_initial = AddRoundKey in wr
state_rounds = foldl InvRound state_initial (reverse ws)
state_final = AddRoundKey (InvSubBytes (InvShiftRows state_rounds)) w0
/**
* This is an alternate version of the `InvCipher` function
* [FIPS-PUB-197 Section 5.3]. The following deviates from Algorithm 3
* but is functionally equivalent and more obviously the inverse of
* `Cipher`.
*/
InvCipher' : {k} (Key k) => [Nr k + 1]Block -> Block -> Block
InvCipher' ([w0] # ws # [wr]) in = state_final
where
state_initial = InvSubBytes (InvShiftRows (AddRoundKey in wr))
state_rounds = foldl InvRound' state_initial (reverse ws)
state_final = AddRoundKey state_rounds w0
/**
* This property demonstrates that `InvCipher` and `InvCipher` are
* functionally equivalent.
*/
InvCipherEquivalence : {k} (Key k) => [Nr k + 1]Block -> Block -> Bool
property InvCipherEquivalence ws state =
InvCipher`{k} ws state == InvCipher'`{k} ws state
/**
* This property demonstrates that `Cipher` and `InvCipher` are
* inverses.
*/
CipherInverts : {k} (Key k) => [Nr k + 1]Block -> Block -> Bool
property CipherInverts ws state =
InvCipher`{k} ws (Cipher`{k} ws state) == state
/**
* This property demonstrates that `Cipher` and `InvCipher'` are
* inverses.
*/
CipherInverts' : {k} (Key k) => [Nr k + 1]Block -> Block -> Bool
property CipherInverts' ws state =
InvCipher'`{k} ws (Cipher`{k} ws state) == state
/**
* This function performs the `SubBytes` transformation [FIPS-PUB-197
* Section 5.1.1].
*/
SubBytes : Block -> Block
SubBytes bs = SBox @@ bs
/**
* This function performs the `InvSubBytes` transformation
* [FIPS-PUB-197 Section 5.3.2].
*/
InvSubBytes : Block -> Block
InvSubBytes bs = InvSBox @@ bs
/**
* This property demonstrates that `SubBytes` and `InvSubBytes` are
* inverses.
*/
SubBytesInverts : Block -> Bool
property SubBytesInverts bs = InvSubBytes (SubBytes bs) == bs
/**
* This function performs the `ShiftRows` transformation [FIPS-PUB-197
* Section 5.1.2]. Here, `ShiftRows` is treated as a permutation of a
* sequence bytes rather than a series of cyclic shifts on a matrix of
* bytes.
*/
ShiftRows : Block -> Block
ShiftRows bs = bs @@ [ 0, 5, 10, 15
, 4, 9, 14, 3
, 8, 13, 2, 7
, 12, 1, 6, 11
]
/**
* This function performs the `InvShiftRows` transformation
* [FIPS-PUB-197 Section 5.3.1]. Here, `InvShiftRows` is treated as a
* permutation of a sequence bytes rather than a series of cyclic
* shifts on a matrix of bytes.
*/
InvShiftRows : Block -> Block
InvShiftRows bs = bs @@ [ 0, 13, 10, 7
, 4, 1, 14, 11
, 8, 5, 2, 15
, 12, 9, 6, 3
]
/**
* This property demonstrates that `ShiftRows` and `InvShiftRows` are
* inverses.
*/
ShiftRowsInverts : Block -> Bool
property ShiftRowsInverts xs = InvShiftRows (ShiftRows xs) == xs
/**
* This is one step of `MixColumns` [FIPS-PUB-197 Section 5.1.3].
*/
MixColumnsStep : AESWord -> AESWord
MixColumnsStep [s0, s1, s2, s3] =
[ (0x02 • s0) ^ (0x03 • s1) ^ s2 ^ s3
, s0 ^ (0x02 • s1) ^ (0x03 • s2) ^ s3
, s0 ^ s1 ^ (0x02 • s2) ^ (0x03 • s3)
, (0x03 • s0) ^ s1 ^ s2 ^ (0x02 • s3)
]
/**
* This function performs the `MixColumns` transformation
* [FIPS-PUB-197 Section 5.1.3].
*/
MixColumns : Block -> Block
MixColumns bs = join (map MixColumnsStep (split bs))
/**
* The is one step of `InvMixColumns` [FIPS-PUB-197 Section 5.3.3].
*/
InvMixColumnsStep : AESWord -> AESWord
InvMixColumnsStep [s0, s1, s2, s3] =
[ (0x0e • s0) ^ (0x0b • s1) ^ (0x0d • s2) ^ (0x09 • s3)
, (0x09 • s0) ^ (0x0e • s1) ^ (0x0b • s2) ^ (0x0d • s3)
, (0x0d • s0) ^ (0x09 • s1) ^ (0x0e • s2) ^ (0x0b • s3)
, (0x0b • s0) ^ (0x0d • s1) ^ (0x09 • s2) ^ (0x0e • s3)
]
/**
* This function performs the `InvMixColumns` transformation
* [FIPS-PUB-197 Section 5.3.3].
*/
InvMixColumns : Block -> Block
InvMixColumns bs = join (map InvMixColumnsStep (split bs))
/**
* This property demonstrates that the step functions of `MixColumns`
* and `InvMixColumns` are inverses.
*/
MixColumnsStepInverts : AESWord -> Bool
property MixColumnsStepInverts b = InvMixColumnsStep (MixColumnsStep b) == b
/**
* This property demonstrates that `MixColumns` and `InvMixColumns` are
* inverses.
*/
MixColumnsInverts : Block -> Bool
property MixColumnsInverts bs = InvMixColumns (MixColumns bs) == bs
/**
* This function adds a round key into the current state [FIPS-PUB-197
* Section 5.1.4].
*/
AddRoundKey : Block -> Block -> Block
AddRoundKey bs w = bs ^ w
/**
* This property demonstrates that `AddRoundKey` is an involution.
*/
AddRoundKeyInvolution : Block -> Block -> Bool
property AddRoundKeyInvolution bs w = AddRoundKey (AddRoundKey bs w) w == bs
+1
View File
@@ -0,0 +1 @@
rust/Cipher/target/release/libCipher.dylib
+1
View File
@@ -0,0 +1 @@
rust/Cipher/target/release/libCipher.so
+32
View File
@@ -0,0 +1,32 @@
/*
* Description: This is a Cryptol specification of the GF(2^^8)
* routines in the Advanced Encryption Standard (AES) [FIPS-PUB-197].
*/
module algorithms::AES::GF28 where
/**
* The `•` symbol denotes multiplication in GF(2^^8) [FIPS-PUB-197
* Section 4.2].
*/
(•) : [8] -> [8] -> [8]
(•) b c = pmod (pmult b c) m
where m = <| x^^8 + x^^4 + x^^3 + x + 1 |>
/**
* This function performs the product of `b • 0x02` and is implemented
* here at the byte level as a left shift and a subsequent conditional
* bitwise XOR with `0x1b` [FIPS-PUB-197 Section 4.2].
*/
xTimes : [8] -> [8]
xTimes b = b << 1 ^ (if head b then 0x1b else 0x00)
/**
* This property demonstrates that Cryptol's built-in polynomial
* arithmetic is functionally equivalent to AES's GF(2^^8)
* multiplication routine [FIPS-PUB-197 Section 4.2].
*/
GF28MultiplicationCorrect : [8] -> [8] -> Bool
property GF28MultiplicationCorrect b c =
xTimes b == b • 2
/\ b • c == foldl (\a bi -> xTimes a ^ if bi then c else 0x00) 0x00 b
+60
View File
@@ -0,0 +1,60 @@
/*
* Description: This is a Cryptol specification of the `KeyExpansion`
* method in the Advanced Encryption Standard (AES) [FIPS-PUB-197].
*/
module algorithms::AES::KeyExpansion where
import algorithms::AES::Types
import algorithms::AES::SBox (SBox)
/**
* This table holds the word array for the round constant
* [FIPS-PUB-197 Sections 2.1 and 5.2]. Constants are used here rather
* than computing the values in place.
*/
Rcon : [11]AESWord
Rcon = [ undefined
, [ 0x01, 0x00, 0x00, 0x00 ]
, [ 0x02, 0x00, 0x00, 0x00 ]
, [ 0x04, 0x00, 0x00, 0x00 ]
, [ 0x08, 0x00, 0x00, 0x00 ]
, [ 0x10, 0x00, 0x00, 0x00 ]
, [ 0x20, 0x00, 0x00, 0x00 ]
, [ 0x40, 0x00, 0x00, 0x00 ]
, [ 0x80, 0x00, 0x00, 0x00 ]
, [ 0x1b, 0x00, 0x00, 0x00 ]
, [ 0x36, 0x00, 0x00, 0x00 ]
]
/**
* This function takes a word [a0,a1,a2,a3] as input and performs a
* cyclic permutation, returning the word [a1,a2,a3,a0] [FIPS-PUB-197
* Section 5.2].
*/
RotWord : AESWord -> AESWord
RotWord [ a0, a1, a2, a3 ] = [ a1, a2, a3, a0 ]
/**
* This function takes a four-byte input word and applies the S-box to
* each of the four bytes to produce an output word [FIPS-PUB-197
* Section 5.2].
*/
SubWord : AESWord -> AESWord
SubWord [ a0, a1, a2, a3 ] =
[ SBox @ a0, SBox @ a1, SBox @ a2, SBox @ a3 ]
/**
* This is the main AES key expansion routine [FIPS-PUB-197 Section
* 5.2].
*/
foreign KeyExpansion : {k} (Key k) => [Nk k]AESWord -> [Nr k + 1]Block
KeyExpansion key = split (split (join (join w)))
where
Nk = `(Nk k)
w = [ if i < Nk then key@i
| i % Nk == 0 then w@(i - Nk) ^ SubWord (RotWord (w@(i - 1))) ^ Rcon@(i / Nk)
| i % Nk == 4 /\ Nk > 6 then w@(i - Nk) ^ SubWord (w@(i - 1))
else w@(i - Nk) ^ w@(i - 1)
| i <- [ 0 ..< 4 * (Nr k + 1) ]
] : [4 * (Nr k + 1)]AESWord
+1
View File
@@ -0,0 +1 @@
rust/KeyExpansion/target/release/libKeyExpansion.dylib
+1
View File
@@ -0,0 +1 @@
rust/KeyExpansion/target/release/libKeyExpansion.so
+48
View File
@@ -0,0 +1,48 @@
/*
* Description: This is a Cryptol specification for the Advanced
* Encryption Standard (AES), also called Rijndael
* [FIPS-PUB-197]. This specification is specialized to Nb = 4 and
* supports the standard key sizes of 128, 192, and 256-bits.
*/
module algorithms::AES::Main where
import algorithms::AES::Types
import algorithms::AES::Cipher (Cipher, InvCipher)
import algorithms::AES::KeyExpansion (KeyExpansion)
/**
* This record holds the main AES block cipher functions [FIPS-PUB-197
* Section 5], namely, `Encrypt` and `Decrypt`.
*/
AES : {k} (Key k)
=> { Encrypt : ([k] -> [128] -> [128])
, Decrypt : ([k] -> [128] -> [128])
}
AES = {
Encrypt key in = join (Cipher`{k} (KeyExpansion`{k} (split (split key))) (split in))
, Decrypt key in = join (InvCipher`{k} (KeyExpansion`{k} (split (split key))) (split in))
}
/**
* This property demonstrates that `Encrypt` and `Decrypt` are
* inverses.
*/
EncryptInverts : {k} (Key k) => [k] -> [128] -> Bit
property EncryptInverts key in =
AES`{k}.Decrypt key (AES`{k}.Encrypt key in) == in
/**
* This is the AES-128 block cipher [FIPS-PUB-197 Section 5].
*/
AES128 = AES`{128}
/**
* This is the AES-192 block cipher [FIPS-PUB-197 Section 5].
*/
AES192 = AES`{192}
/**
* This is the AES-256 block cipher [FIPS-PUB-197 Section 5].
*/
AES256 = AES`{256}
+89
View File
@@ -0,0 +1,89 @@
/*
* Description: This is a Cryptol specification of the `S-Box`es for
* the Advanced Encryption Standard (AES) [FIPS-PUB-197].
*/
module algorithms::AES::SBox where
import shared::algebra::Polynomial::GF2 (is_irreducible, inverse, (^*), (^%), (^^^))
/** The S-box irreducible polynomial [FIPS-PUB-197 Section 4.2]. */
SBoxPolynomial = <| x^^8 + x^^4 + x^^3 + x + 1 |> : [9]
/** This property demonstrates that the S-Box polynomial is irreducible. */
SBoxPolynomialIsIrreducible : Bool
property SBoxPolynomialIsIrreducible = is_irreducible`{8} SBoxPolynomial
/**
* This property demonstrates that `inverse` is correct [FIPS-PUB-197
* Section 4.4].
*
* ```
* :exhaust inverseCorrect
* ```
*/
inverseCorrect : [8] -> Bool
property inverseCorrect b =
b == 0 ==> inverse b SBoxPolynomial == 0
/\ inverse b SBoxPolynomial == b ^^^ (254, SBoxPolynomial)
/** Affine transformation [FIPS-PUB-197 Section 5.1.1]. */
affine : [8] -> [8]
affine b = reverse b'
where
c = reverse 0b01100011
b_ = reverse (inverse b SBoxPolynomial)
b'@i = b_@i ^ b_@((i+4) % 8) ^ b_@((i+5) % 8) ^ b_@((i+6) % 8) ^ b_@((i+7) % 8) ^ c@i
/** This property demonstrates that the AES S-Box is correct. */
property SBoxCorrect = [ affine b | b <- [0..255] ] == SBox
/**
* This table is the S-box [FIPS-PUB-197 Section 5.1.1 and Table 4].
*/
SBox : [256][8]
SBox = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76
, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0
, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15
, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75
, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84
, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf
, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8
, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2
, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73
, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb
, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79
, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08
, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a
, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e
, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf
, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
/**
* This table is the inverse S-box [FIPS-PUB-197 Section 5.3.2 and Table 6].
*/
InvSBox : [256][8]
InvSBox = [ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]
/**
* This property demonstrates that `SBox` and `InvSBox` are inverses.
*/
SBoxInverts : [8] -> Bool
property SBoxInverts b = InvSBox @ (SBox @ b) == b
+299
View File
@@ -0,0 +1,299 @@
/*
* Description: This module holds tests for public functions in the
* Cryptol specification for the Advanced Encryption Standard (AES)
* [FIPS-PUB-197].
*/
module algorithms::AES::Tests where
import algorithms::AES::KeyExpansion
import algorithms::AES::Cipher
import algorithms::AES::Main
/**
* This test demonstrates the expansion of a 128-bit key [FIPS-PUB-197
* Appendix A.1].
*/
property Example128KeyExpansion =
KeyExpansion`{128} (split (split 0x2b7e151628aed2a6abf7158809cf4f3c))
== [ [ 0x2b, 0x7e, 0x15, 0x16
, 0x28, 0xae, 0xd2, 0xa6
, 0xab, 0xf7, 0x15, 0x88
, 0x09, 0xcf, 0x4f, 0x3c
]
, [ 0xa0, 0xfa, 0xfe, 0x17
, 0x88, 0x54, 0x2c, 0xb1
, 0x23, 0xa3, 0x39, 0x39
, 0x2a, 0x6c, 0x76, 0x05
]
, [ 0xf2, 0xc2, 0x95, 0xf2
, 0x7a, 0x96, 0xb9, 0x43
, 0x59, 0x35, 0x80, 0x7a
, 0x73, 0x59, 0xf6, 0x7f
]
, [ 0x3d, 0x80, 0x47, 0x7d
, 0x47, 0x16, 0xfe, 0x3e
, 0x1e, 0x23, 0x7e, 0x44
, 0x6d, 0x7a, 0x88, 0x3b
]
, [ 0xef, 0x44, 0xa5, 0x41
, 0xa8, 0x52, 0x5b, 0x7f
, 0xb6, 0x71, 0x25, 0x3b
, 0xdb, 0x0b, 0xad, 0x00
]
, [ 0xd4, 0xd1, 0xc6, 0xf8
, 0x7c, 0x83, 0x9d, 0x87
, 0xca, 0xf2, 0xb8, 0xbc
, 0x11, 0xf9, 0x15, 0xbc
]
, [ 0x6d, 0x88, 0xa3, 0x7a
, 0x11, 0x0b, 0x3e, 0xfd
, 0xdb, 0xf9, 0x86, 0x41
, 0xca, 0x00, 0x93, 0xfd
]
, [ 0x4e, 0x54, 0xf7, 0x0e
, 0x5f, 0x5f, 0xc9, 0xf3
, 0x84, 0xa6, 0x4f, 0xb2
, 0x4e, 0xa6, 0xdc, 0x4f
]
, [ 0xea, 0xd2, 0x73, 0x21
, 0xb5, 0x8d, 0xba, 0xd2
, 0x31, 0x2b, 0xf5, 0x60
, 0x7f, 0x8d, 0x29, 0x2f
]
, [ 0xac, 0x77, 0x66, 0xf3
, 0x19, 0xfa, 0xdc, 0x21
, 0x28, 0xd1, 0x29, 0x41
, 0x57, 0x5c, 0x00, 0x6e
]
, [ 0xd0, 0x14, 0xf9, 0xa8
, 0xc9, 0xee, 0x25, 0x89
, 0xe1, 0x3f, 0x0c, 0xc8
, 0xb6, 0x63, 0x0c, 0xa6
]
]
/**
* This test demonstrates the expansion of a 192-bit key [FIPS-PUB-197
* Appendix A.2].
*/
property Example192KeyExpansion =
KeyExpansion`{192} (split (split 0x8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b))
== [ [ 0x8e, 0x73, 0xb0, 0xf7
, 0xda, 0x0e, 0x64, 0x52
, 0xc8, 0x10, 0xf3, 0x2b
, 0x80, 0x90, 0x79, 0xe5
]
, [ 0x62, 0xf8, 0xea, 0xd2
, 0x52, 0x2c, 0x6b, 0x7b
, 0xfe, 0x0c, 0x91, 0xf7
, 0x24, 0x02, 0xf5, 0xa5
]
, [ 0xec, 0x12, 0x06, 0x8e
, 0x6c, 0x82, 0x7f, 0x6b
, 0x0e, 0x7a, 0x95, 0xb9
, 0x5c, 0x56, 0xfe, 0xc2
]
, [ 0x4d, 0xb7, 0xb4, 0xbd
, 0x69, 0xb5, 0x41, 0x18
, 0x85, 0xa7, 0x47, 0x96
, 0xe9, 0x25, 0x38, 0xfd
]
, [ 0xe7, 0x5f, 0xad, 0x44
, 0xbb, 0x09, 0x53, 0x86
, 0x48, 0x5a, 0xf0, 0x57
, 0x21, 0xef, 0xb1, 0x4f
]
, [ 0xa4, 0x48, 0xf6, 0xd9
, 0x4d, 0x6d, 0xce, 0x24
, 0xaa, 0x32, 0x63, 0x60
, 0x11, 0x3b, 0x30, 0xe6
]
, [ 0xa2, 0x5e, 0x7e, 0xd5
, 0x83, 0xb1, 0xcf, 0x9a
, 0x27, 0xf9, 0x39, 0x43
, 0x6a, 0x94, 0xf7, 0x67
]
, [ 0xc0, 0xa6, 0x94, 0x07
, 0xd1, 0x9d, 0xa4, 0xe1
, 0xec, 0x17, 0x86, 0xeb
, 0x6f, 0xa6, 0x49, 0x71
]
, [ 0x48, 0x5f, 0x70, 0x32
, 0x22, 0xcb, 0x87, 0x55
, 0xe2, 0x6d, 0x13, 0x52
, 0x33, 0xf0, 0xb7, 0xb3
]
, [ 0x40, 0xbe, 0xeb, 0x28
, 0x2f, 0x18, 0xa2, 0x59
, 0x67, 0x47, 0xd2, 0x6b
, 0x45, 0x8c, 0x55, 0x3e
]
, [ 0xa7, 0xe1, 0x46, 0x6c
, 0x94, 0x11, 0xf1, 0xdf
, 0x82, 0x1f, 0x75, 0x0a
, 0xad, 0x07, 0xd7, 0x53
]
, [ 0xca, 0x40, 0x05, 0x38
, 0x8f, 0xcc, 0x50, 0x06
, 0x28, 0x2d, 0x16, 0x6a
, 0xbc, 0x3c, 0xe7, 0xb5
]
, [ 0xe9, 0x8b, 0xa0, 0x6f
, 0x44, 0x8c, 0x77, 0x3c
, 0x8e, 0xcc, 0x72, 0x04
, 0x01, 0x00, 0x22, 0x02
]
]
/**
* This test demonstrates the expansion of a 256-bit key [FIPS-PUB-197
* Appendix A.3].
*/
property Example256KeyExpansion =
KeyExpansion`{256} (split (split 0x603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4))
== [ [ 0x60, 0x3d, 0xeb, 0x10
, 0x15, 0xca, 0x71, 0xbe
, 0x2b, 0x73, 0xae, 0xf0
, 0x85, 0x7d, 0x77, 0x81
]
, [ 0x1f, 0x35, 0x2c, 0x07
, 0x3b, 0x61, 0x08, 0xd7
, 0x2d, 0x98, 0x10, 0xa3
, 0x09, 0x14, 0xdf, 0xf4
]
, [ 0x9b, 0xa3, 0x54, 0x11
, 0x8e, 0x69, 0x25, 0xaf
, 0xa5, 0x1a, 0x8b, 0x5f
, 0x20, 0x67, 0xfc, 0xde
]
, [ 0xa8, 0xb0, 0x9c, 0x1a
, 0x93, 0xd1, 0x94, 0xcd
, 0xbe, 0x49, 0x84, 0x6e
, 0xb7, 0x5d, 0x5b, 0x9a
]
, [ 0xd5, 0x9a, 0xec, 0xb8
, 0x5b, 0xf3, 0xc9, 0x17
, 0xfe, 0xe9, 0x42, 0x48
, 0xde, 0x8e, 0xbe, 0x96
]
, [ 0xb5, 0xa9, 0x32, 0x8a
, 0x26, 0x78, 0xa6, 0x47
, 0x98, 0x31, 0x22, 0x29
, 0x2f, 0x6c, 0x79, 0xb3
]
, [ 0x81, 0x2c, 0x81, 0xad
, 0xda, 0xdf, 0x48, 0xba
, 0x24, 0x36, 0x0a, 0xf2
, 0xfa, 0xb8, 0xb4, 0x64
]
, [ 0x98, 0xc5, 0xbf, 0xc9
, 0xbe, 0xbd, 0x19, 0x8e
, 0x26, 0x8c, 0x3b, 0xa7
, 0x09, 0xe0, 0x42, 0x14
]
, [ 0x68, 0x00, 0x7b, 0xac
, 0xb2, 0xdf, 0x33, 0x16
, 0x96, 0xe9, 0x39, 0xe4
, 0x6c, 0x51, 0x8d, 0x80
]
, [ 0xc8, 0x14, 0xe2, 0x04
, 0x76, 0xa9, 0xfb, 0x8a
, 0x50, 0x25, 0xc0, 0x2d
, 0x59, 0xc5, 0x82, 0x39
]
, [ 0xde, 0x13, 0x69, 0x67
, 0x6c, 0xcc, 0x5a, 0x71
, 0xfa, 0x25, 0x63, 0x95
, 0x96, 0x74, 0xee, 0x15
]
, [ 0x58, 0x86, 0xca, 0x5d
, 0x2e, 0x2f, 0x31, 0xd7
, 0x7e, 0x0a, 0xf1, 0xfa
, 0x27, 0xcf, 0x73, 0xc3
]
, [ 0x74, 0x9c, 0x47, 0xab
, 0x18, 0x50, 0x1d, 0xda
, 0xe2, 0x75, 0x7e, 0x4f
, 0x74, 0x01, 0x90, 0x5a
]
, [ 0xca, 0xfa, 0xaa, 0xe3
, 0xe4, 0xd5, 0x9b, 0x34
, 0x9a, 0xdf, 0x6a, 0xce
, 0xbd, 0x10, 0x19, 0x0d
]
, [ 0xfe, 0x48, 0x90, 0xd1
, 0xe6, 0x18, 0x8d, 0x0b
, 0x04, 0x6d, 0xf3, 0x44
, 0x70, 0x6c, 0x63, 0x1e
]
]
/**
* This tests the `Cipher` and `InvCipher` functions [FIPS-PUB-197
* Appendix B].
*/
property CipherExample =
Cipher`{128} key input == output
/\ InvCipher`{128} key output == input
where
input = split 0x3243f6a8885a308d313198a2e0370734
key = KeyExpansion`{128} (split (split 0x2b7e151628aed2a6abf7158809cf4f3c))
output = split 0x3925841d02dc09fbdc118597196a0b32
/**
* An AES-128 example [AES Test Vectors].
*/
property AES128Example =
map (AES`{128}.Encrypt key) plain == cipher
/\ map (AES`{128}.Decrypt key) cipher == plain
where
key = 0x2b7e151628aed2a6abf7158809cf4f3c
plain = [ 0x6bc1bee22e409f96e93d7e117393172a
, 0xae2d8a571e03ac9c9eb76fac45af8e51
, 0x30c81c46a35ce411e5fbc1191a0a52ef
, 0xf69f2445df4f9b17ad2b417be66c3710
]
cipher = [ 0x3ad77bb40d7a3660a89ecaf32466ef97
, 0xf5d3d58503b9699de785895a96fdbaaf
, 0x43b1cd7f598ece23881b00e3ed030688
, 0x7b0c785e27e8ad3f8223207104725dd4
]
/**
* An AES-192 example [AES Test Vectors].
*/
property AES192Example =
map (AES`{192}.Encrypt key) plain == cipher
/\ map (AES`{192}.Decrypt key) cipher == plain
where
key = 0x8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
plain = [ 0x6bc1bee22e409f96e93d7e117393172a
, 0xae2d8a571e03ac9c9eb76fac45af8e51
, 0x30c81c46a35ce411e5fbc1191a0a52ef
, 0xf69f2445df4f9b17ad2b417be66c3710
]
cipher = [ 0xbd334f1d6e45f25ff712a214571fa5cc
, 0x974104846d0ad3ad7734ecb3ecee4eef
, 0xef7afd2270e2e60adce0ba2face6444e
, 0x9a4b41ba738d6c72fb16691603c18e0e
]
/**
* An AES-256 example [AES Test Vectors].
*/
property AES256Example =
map (AES`{256}.Encrypt key) plain == cipher
/\ map (AES`{256}.Decrypt key) cipher == plain
where
key = 0x603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4
plain = [ 0x6bc1bee22e409f96e93d7e117393172a
, 0xae2d8a571e03ac9c9eb76fac45af8e51
, 0x30c81c46a35ce411e5fbc1191a0a52ef
, 0xf69f2445df4f9b17ad2b417be66c3710
]
cipher = [ 0xf3eed1bdb5d2a03c064b5a7e3db181f8
, 0x591ccb10d410ed26dc5ba74a31362870
, 0xb6ed21b99ca6f4f9f153e7b1beafed1d
, 0x23304b7a39f9f3ff067d8d8f9e24ecc7
]
+39
View File
@@ -0,0 +1,39 @@
/*
* Description: This is a Cryptol specification of the types needed to
* specify the Advanced Encryption Standard (AES) [FIPS-PUB-197].
*/
module algorithms::AES::Types where
/**
* This constraint enforces the standard key sizes of 128, 192, and
* 256-bits [FIPS-PUB-197 Sections 1, 5, and 6.1].
*/
type constraint Key k = (fin k, k % 64 == 0, k / 64 >= 2, k / 64 <= 4)
/**
* This sequence of bytes comprises the input, output, State, and
* Round Key [FIPS-PUB-197 Section 2.1].
*/
type Block = [16][8]
/**
* This sequence of bytes represents a 32-bit word [FIPS-PUB-197
* Section 2.1]. `AESWord` is used to avoid conflicting with Cryptol's
* built-in `Word`.
*/
type AESWord = [4][8]
/**
* This type represents the number of 32-bit words comprising the
* key. `Nk` is assigned to 4, 6, and 8 for `AES128`, `AES192`, and
* `AES256`, respectively [FIPS-PUB-197 Sections 2.3 and 5].
*/
type Nk k = k / 32
/**
* This type represents the number of rounds to perform. `Nr` is
* assigned to 10, 12, and 14 for `AES128`, `AES192`, and `AES256`,
* respectively [FIPS-PUB-197 Sections 2.3 and 5].
*/
type Nr k = Nk k + 6
+14
View File
@@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Cipher"
version = "0.1.0"
dependencies = [
"ffi_utils",
]
[[package]]
name = "ffi_utils"
version = "0.1.0"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "Cipher"
version = "0.1.0"
edition = "2021"
[lib]
name = "Cipher"
path = "src/lib.rs"
crate-type = ["lib", "cdylib"]
[dependencies]
ffi_utils = { path = "../../../../shared/rust/ffi_utils" }
+252
View File
@@ -0,0 +1,252 @@
//! This is a Rust implementation of the `Cipher` and `InvCipher`
//! methods in the Advanced Encryption Standard (AES) [FIPS-PUB-197].
#![allow(non_snake_case)]
use ffi_utils::{get_array, get_vec, output_array};
/// This sequence of bytes comprises the input, output, State, and
/// Round Key [FIPS-PUB-197 Section 2.1].
pub type Block = [u8; 16];
/// This sequence of bytes represents a 32-bit word [FIPS-PUB-197
/// Section 2.1].
pub type Word = [u8; 4];
/// This function performs the product of `b • 0x02` and is implemented
/// here at the byte level as a left shift and a subsequent conditional
/// bitwise XOR with `0x1b` [FIPS-PUB-197 Section 4.2].
fn xTimes(x: u8) -> u8 {
(x << 1) ^ (if x > 127 { 0x1b } else { 0x00 })
}
/// Multiplication of two bytes representing coefficients of
/// polynomials in GF 2^^8 [FIPS-PUB-197 Section 4.3].
fn mul_bytes(x: u8, y: u8) -> u8 {
let mut a: u8 = 0x00;
for i in 0..=7 {
a = xTimes(a)
^ (if (x >> (7 - i)) & 0x01 == 0x01 {
y
} else {
0x00
});
}
a
}
/// S-box [FIPS-PUB-197 Section 5.1.1, Table 4].
pub static SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
/// Inverse S-box [FIPS-PUB-197 Section 5.3.2, Table 6].
static INVSBOX: [u8; 256] = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
];
/// `SubBytes` transformation [FIPS-PUB-197 Section 5.1.1].
fn subbytes(xs: Block) -> Block {
let mut ret: Block = [0; 16];
for i in 0..=15 {
ret[i] = SBOX[xs[i] as usize];
}
ret
}
/// `InvSubBytes` transformation [FIPS-PUB-197 Section 5.3.2].
fn inv_subbytes(xs: Block) -> Block {
let mut ret: Block = [0; 16];
for i in 0..=15 {
ret[i] = INVSBOX[xs[i] as usize];
}
ret
}
/// This function performs the `ShiftRows` transformation [FIPS-PUB-197
/// Section 5.1.2]. Here, `ShiftRows` is treated as a permutation of a
/// sequence bytes rather than a series of cyclic shifts on a matrix of
/// bytes.
fn shiftrows(xs: Block) -> Block {
static PERM: [usize; 16] = [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11];
let mut ret: Block = [0; 16];
for i in 0..=15 {
ret[i] = xs[PERM[i]];
}
ret
}
/// This function performs the `InvShiftRows` transformation
/// [FIPS-PUB-197 Section 5.3.1]. Here, `InvShiftRows` is treated as a
/// permutation of a sequence bytes rather than a series of cyclic
/// shifts on a matrix of bytes.
fn inv_shiftrows(xs: Block) -> Block {
static PERM: [usize; 16] = [0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3];
let mut ret: Block = [0; 16];
for i in 0..=15 {
ret[i] = xs[PERM[i]];
}
ret
}
/// This is one step of `MixColumns` [FIPS-PUB-197 Section 5.1.3].
fn mixcolumns_step([s0, s1, s2, s3]: Word) -> Word {
let t0 = mul_bytes(0x02, s0) ^ mul_bytes(0x03, s1) ^ s2 ^ s3;
let t1 = s0 ^ mul_bytes(0x02, s1) ^ mul_bytes(0x03, s2) ^ s3;
let t2 = s0 ^ s1 ^ mul_bytes(0x02, s2) ^ mul_bytes(0x03, s3);
let t3 = mul_bytes(0x03, s0) ^ s1 ^ s2 ^ mul_bytes(0x02, s3);
[t0, t1, t2, t3]
}
/// This function performs the `MixColumns` transformation
/// [FIPS-PUB-197 Section 5.1.3].
fn mixcolumns(
[s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15]: Block,
) -> Block {
let [t0, t1, t2, t3] = mixcolumns_step([s0, s1, s2, s3]);
let [t4, t5, t6, t7] = mixcolumns_step([s4, s5, s6, s7]);
let [t8, t9, t10, t11] = mixcolumns_step([s8, s9, s10, s11]);
let [t12, t13, t14, t15] = mixcolumns_step([s12, s13, s14, s15]);
[
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
]
}
/// The is one step of `InvMixColumns` [FIPS-PUB-197 Section 5.3.3].
fn inv_mixcolumns_step([s0, s1, s2, s3]: Word) -> Word {
let t0 = mul_bytes(0x0e, s0) ^ mul_bytes(0x0b, s1) ^ mul_bytes(0x0d, s2) ^ mul_bytes(0x09, s3);
let t1 = mul_bytes(0x09, s0) ^ mul_bytes(0x0e, s1) ^ mul_bytes(0x0b, s2) ^ mul_bytes(0x0d, s3);
let t2 = mul_bytes(0x0d, s0) ^ mul_bytes(0x09, s1) ^ mul_bytes(0x0e, s2) ^ mul_bytes(0x0b, s3);
let t3 = mul_bytes(0x0b, s0) ^ mul_bytes(0x0d, s1) ^ mul_bytes(0x09, s2) ^ mul_bytes(0x0e, s3);
[t0, t1, t2, t3]
}
/// This function performs the `InvMixColumns` transformation
/// [FIPS-PUB-197 Section 5.3.3].
fn inv_mixcolumns(
[s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15]: Block,
) -> Block {
let [t0, t1, t2, t3] = inv_mixcolumns_step([s0, s1, s2, s3]);
let [t4, t5, t6, t7] = inv_mixcolumns_step([s4, s5, s6, s7]);
let [t8, t9, t10, t11] = inv_mixcolumns_step([s8, s9, s10, s11]);
let [t12, t13, t14, t15] = inv_mixcolumns_step([s12, s13, s14, s15]);
[
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
]
}
/// This function performs one round of AES encryption [FIPS-PUB-197
/// Section 5.1].
fn round(mut xs: Block, ki: Block) -> Block {
xs = mixcolumns(shiftrows(subbytes(xs)));
for i in 0..=15 {
xs[i] ^= ki[i];
}
xs
}
/// This is an alternate version of the `InvRound` function that is a
/// proper inverse of `Round`.
fn inv_round(mut xs: Block, ki: Block) -> Block {
for i in 0..=15 {
xs[i] ^= ki[i];
}
inv_subbytes(inv_shiftrows(inv_mixcolumns(xs)))
}
/// Takes two `Block`s and returns the first `Block` XOR'd with the
/// second.
fn xorb(mut a: Block, b: Block) -> Block {
for i in 0..=15 {
a[i] ^= b[i];
}
a
}
/// The `Cipher` function performs AES encryption [FIPS-PUB-197
/// Section 5.1].
#[allow(clippy::needless_range_loop)]
#[must_use]
pub fn cipher(ks: &[Block], plaintext: Block) -> Block {
let pre = xorb(plaintext, ks[0]);
let mut mid = pre;
for i in 1..(ks.len() - 1) {
mid = round(mid, ks[i]);
}
xorb(shiftrows(subbytes(mid)), ks[ks.len() - 1])
}
/// FFI entrypoint for the `Cipher` function.
#[export_name = "Cipher"]
pub extern "C" fn cipher_ffi(
k: usize,
expanded_key_raw: *const Block,
pt_raw: *const u8,
out_raw: *mut u8,
) {
let Nr = k / 32 + 6;
let expanded_key = get_vec::<Block>(Nr + 1, expanded_key_raw);
let pt = get_array::<u8, 16>(pt_raw);
let ct = cipher(&expanded_key, pt);
output_array::<u8, 16>(&ct, out_raw);
}
/// This is an alternate version of the `InvCipher` function
/// [FIPS-PUB-197 Section 5.3]. The following deviates from Algorithm 3
/// but is functionally equivalent and more obviously the inverse of
/// `Cipher`.
#[allow(clippy::needless_range_loop)]
#[must_use]
pub fn inv_cipher(ks: &[Block], ciphertext: Block) -> Block {
let pre = inv_subbytes(inv_shiftrows(xorb(ciphertext, ks[ks.len() - 1])));
let mut mid = pre;
for i in (1..(ks.len() - 1)).rev() {
mid = inv_round(mid, ks[i]);
}
xorb(mid, ks[0])
}
/// FFI entrypoint for the `InvCipher` function.
#[export_name = "InvCipher"]
pub extern "C" fn inv_cipher_ffi(
k: usize,
expanded_key_raw: *const Block,
ct_raw: *const u8,
out_raw: *mut u8,
) {
let Nr = k / 32 + 6;
let expanded_key = get_vec::<Block>(Nr + 1, expanded_key_raw);
let ct = get_array::<u8, 16>(ct_raw);
let pt = inv_cipher(&expanded_key, ct);
output_array::<u8, 16>(&pt, out_raw);
}
+118
View File
@@ -0,0 +1,118 @@
from pathlib import Path
import unittest
from saw_client import *
from saw_client.crucible import *
from saw_client.mir import *
from saw_client.proofscript import *
from saw_client.mir_type import *
def ref_to_fresh(c : Contract, ty : MIRType, name : Optional[str] = None,
read_only : bool = False) -> Tuple[FreshVar, SetupVal]:
"""Add to ``Contract`` ``c`` an allocation of a reference of type ``ty`` initialized to an unknown fresh value.
If ``read_only == True`` then the allocated memory is immutable.
:returns A fresh variable bound to the reference's initial value and the newly allocated reference. (The fresh
variable will be assigned ``name`` if provided/available.)"""
var = c.fresh_var(ty, name)
ptr = c.alloc(ty, points_to = var, read_only = read_only)
return (var, ptr)
# fn F(xs: Block) -> Block {
class block_to_block_Contract(Contract):
def __init__(self, F : CryptolTerm):
super().__init__()
self.F = F
def specification(self):
xs = self.fresh_var(array_ty(16, u8), "xs")
self.execute_func(xs)
self.returns(cry_f("{self.F} {xs}"))
def block_to_block_verify(self, mod, F_rust, F_cryptol):
result = mir_verify(mod, F_rust, block_to_block_Contract(cry_f(F_cryptol)),
script=ProofScript([rme]))
self.assertIs(result.is_success(), True)
return result
# fn F(xs: Block, ki: Block) -> Block {
# Matches `round` and `inv_round`
class round_Contract(Contract):
def __init__(self, F : CryptolTerm):
super().__init__()
self.F = F
def specification(self):
xs = self.fresh_var(array_ty(16, u8), "xs")
ki = self.fresh_var(array_ty(16, u8), "ki")
self.execute_func(xs, ki)
self.returns(cry_f("{self.F} {xs} {ki}"))
def round_verify(self, mod, F_rust, F_cryptol, lemmas, script=ProofScript([rme])):
result = mir_verify(mod, F_rust, round_Contract(cry_f(F_cryptol)),
lemmas=lemmas,
script=script)
self.assertIs(result.is_success(), True)
return result
# fn F(ks: &[Block], plaintext: Block) -> Block {
# Matches `cipher` and `inv_cipher`
class cipher_Contract(Contract):
def __init__(self, k: int, F: CryptolTerm):
super().__init__()
self.k = k
self.F = F
def specification(self):
k = cry_f("{self.k} : [32]");
(ks, ks_p) = ref_to_fresh(self, array_ty(self.k/32 + 6 + 1, array_ty(16, u8)), name="ks", read_only=True)
plaintext = self.fresh_var(array_ty(16, u8), "plaintext")
self.execute_func(slice_value(ks_p), plaintext)
self.returns(cry_f("{self.F}`{{{self.k}}} {ks} {plaintext}"))
def cipher_verify(self, mod, k, F_rust, F_cryptol, lemmas, script=ProofScript([rme])):
result = mir_verify(mod, F_rust, cipher_Contract(k, cry_f(F_cryptol)),
lemmas=lemmas,
script=script)
self.assertIs(result.is_success(), True)
return result
class Cipher(unittest.TestCase):
def test_Cipher(self):
connect(reset_server=True)
if __name__ == "__main__": view(LogResults(verbose_failure=True))
basedir = Path(__file__).absolute().parents[1] # Get absolute path to `Cargo.toml`
crypath = basedir/"../../Cipher.cry"
mirpath = basedir/"linked-mir.json"
cryptol_load_file(str(crypath))
mod = mir_load_module(str(mirpath))
subbytes_result = block_to_block_verify(self, mod, 'Cipher::subbytes', 'SubBytes')
inv_subbytes_result = block_to_block_verify(self, mod, 'Cipher::inv_subbytes', 'InvSubBytes')
shiftrows_result = block_to_block_verify(self, mod, 'Cipher::shiftrows', 'ShiftRows')
inv_shiftrows_result = block_to_block_verify(self, mod, 'Cipher::inv_shiftrows', 'InvShiftRows')
mixcolums_result = block_to_block_verify(self, mod, 'Cipher::mixcolumns', 'MixColumns')
inv_mixcolums_result = block_to_block_verify(self, mod, 'Cipher::inv_mixcolumns', 'InvMixColumns')
round_result = round_verify(self, mod, 'Cipher::round', "Round",
lemmas=[subbytes_result, shiftrows_result, mixcolums_result])
inv_round_result = round_verify(self, mod, 'Cipher::inv_round', "InvRound'",
lemmas=[inv_subbytes_result, inv_shiftrows_result, inv_mixcolums_result],
script=ProofScript([z3(["InvSubBytes", "InvShiftRows", "InvMixColumns"])]))
def cipher_result(k):
return cipher_verify(self, mod, k, 'Cipher::cipher', "Cipher",
lemmas=[round_result, subbytes_result, shiftrows_result],
script=ProofScript([z3(["Round", "SubBytes", "ShiftRows"])]))
cipher_128_result = cipher_result(128)
cipher_192_result = cipher_result(192)
cipher_256_result = cipher_result(256)
def inv_cipher_result(k):
return cipher_verify(self, mod, k, 'Cipher::inv_cipher', "InvCipher'",
lemmas=[inv_round_result, inv_subbytes_result, inv_shiftrows_result],
script=ProofScript([cvc5(["InvRound", "InvSubBytes", "InvShiftRows"])]))
inv_cipher_128_result = inv_cipher_result(128)
inv_cipher_192_result = inv_cipher_result(192)
inv_cipher_256_result = inv_cipher_result(256)
if __name__ == "__main__":
unittest.main()
+22
View File
@@ -0,0 +1,22 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Cipher"
version = "0.1.0"
dependencies = [
"ffi_utils",
]
[[package]]
name = "KeyExpansion"
version = "0.1.0"
dependencies = [
"Cipher",
"ffi_utils",
]
[[package]]
name = "ffi_utils"
version = "0.1.0"
@@ -0,0 +1,13 @@
[package]
name = "KeyExpansion"
version = "0.1.0"
edition = "2021"
[lib]
name = "KeyExpansion"
path = "src/lib.rs"
crate-type = ["lib", "cdylib"]
[dependencies]
ffi_utils = { path = "../../../../shared/rust/ffi_utils" }
Cipher = { path = "../Cipher" }
@@ -0,0 +1,88 @@
//! This is a Rust implementation of the `KeyExpansion` method in
//! the Advanced Encryption Standard (AES) [FIPS-PUB-197].
#![allow(non_snake_case)]
use ffi_utils::{get_vec, output_vec};
use Cipher::{Block, Word, SBOX};
/// This table holds the word array for the round constant
/// [FIPS-PUB-197 Sections 2.1 and 5.2]. Constants are used here rather
/// than computing the values in place.
static RCON: [Word; 11] = [
[0x00, 0x00, 0x00, 0x00], // undefined
[0x01, 0x00, 0x00, 0x00],
[0x02, 0x00, 0x00, 0x00],
[0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00],
[0x10, 0x00, 0x00, 0x00],
[0x20, 0x00, 0x00, 0x00],
[0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00],
[0x1b, 0x00, 0x00, 0x00],
[0x36, 0x00, 0x00, 0x00],
];
/// This function takes a word [a0,a1,a2,a3] as input and performs a
/// cyclic permutation, returning the word [a1,a2,a3,a0] [FIPS-PUB-197
/// Section 5.2].
fn rotword([a0, a1, a2, a3]: Word) -> Word {
[a1, a2, a3, a0]
}
/// This function takes a four-byte input word and applies the S-box to
/// each of the four bytes to produce an output word [FIPS-PUB-197
/// Section 5.2].
fn subword(w: Word) -> Word {
let mut ret = [0, 0, 0, 0];
for i in 0..=3 {
ret[i] = SBOX[w[i] as usize];
}
ret
}
/// Takes two `Word`s and returns the first `Word` XOR'd with the
/// second.
fn xorw(mut a: Word, b: Word) -> Word {
for i in 0..=3 {
a[i] ^= b[i];
}
a
}
/// This is the main AES key expansion routine [FIPS-PUB-197 Section
/// 5.2].
#[must_use]
pub fn key_expansion(k: usize, key: &[Word]) -> [Block; 15] {
let nk = k / 32;
let nr = nk + 6;
let mut w: [Word; 64] = [[0; 4]; 64];
for i in 0..(4 * (nr + 1)) {
if i < nk {
w[i] = key[i];
} else if i % nk == 0 {
w[i] = xorw(w[i - nk], xorw(subword(rotword(w[i - 1])), RCON[i / nk]));
} else if (i % nk == 4) && (nk > 6) {
w[i] = xorw(w[i - nk], subword(w[i - 1]));
} else {
w[i] = xorw(w[i - nk], w[i - 1]);
}
}
let mut expanded_key: [Block; 15] = [[0u8; 16]; 15];
for i in 0..=nr {
for j in 0..=15 {
expanded_key[i][j] = w[i * 4 + j / 4][j % 4];
}
}
expanded_key
}
/// FFI entrypoint for the `KeyExpansion` function.
#[export_name = "KeyExpansion"]
pub extern "C" fn key_expansion_ffi(k: usize, key_raw: *const Word, out_raw: *mut Block) {
let key = get_vec::<Word>(k / 32, key_raw);
let expanded_key = key_expansion(k, &key);
let mut expanded_key_vec: Vec<Block> = expanded_key.to_vec();
expanded_key_vec.resize(k / 32 + 6 + 1, [0; 16]);
output_vec::<Block>(&expanded_key_vec, out_raw);
}
@@ -0,0 +1,78 @@
from pathlib import Path
import unittest
from saw_client import *
from saw_client.crucible import *
from saw_client.mir import *
from saw_client.proofscript import *
from saw_client.mir_type import *
def ref_to_fresh(c : Contract, ty : MIRType, name : Optional[str] = None,
read_only : bool = False) -> Tuple[FreshVar, SetupVal]:
"""Add to ``Contract`` ``c`` an allocation of a reference of type ``ty`` initialized to an unknown fresh value.
If ``read_only == True`` then the allocated memory is immutable.
:returns A fresh variable bound to the reference's initial value and the newly allocated reference. (The fresh
variable will be assigned ``name`` if provided/available.)"""
var = c.fresh_var(ty, name)
ptr = c.alloc(ty, points_to = var, read_only = read_only)
return (var, ptr)
# fn subword(w: Word) -> Word
class subword_Contract(Contract):
def specification(self):
a = self.fresh_var(array_ty(4, u8), "a")
self.execute_func(a)
self.returns(cry_f("SubWord {a}"))
# pub fn key_expansion(k: usize, key: &[Word]) -> Vec<Block>
class key_expansion_Contract(Contract):
def __init__(self, k : int):
super().__init__()
self.k = k
def specification(self):
k = cry_f("{self.k} : [64]");
(key, key_p) = ref_to_fresh(self, array_ty(self.k // 32, array_ty(4, u8)), name="key", read_only=True)
self.execute_func(k, slice_value(key_p))
# The use of slices means the potentially unused part of the
# 15-block array is all zeros and we need to account for them.
key_from_cryptol = cry_f("KeyExpansion`{{{self.k}}} {key} # zero : [15][16][8]")
self.returns(key_from_cryptol)
class KeyExpansion(unittest.TestCase):
def test_KeyExpansion(self):
connect(reset_server=True)
if __name__ == "__main__": view(LogResults(verbose_failure=True))
basedir = Path(__file__).absolute().parents[1] # Get absolute path to `Cargo.toml`
crypath = basedir/"../../KeyExpansion.cry"
mirpath = basedir/"linked-mir.json"
cryptol_load_file(str(crypath))
mod = mir_load_module(str(mirpath))
subword_result = mir_verify(mod,
'KeyExpansion::subword',
subword_Contract(),
script=ProofScript([rme]))
self.assertIs(subword_result.is_success(), True)
def key_expand_result(k):
result = mir_verify(mod,
'KeyExpansion::key_expansion',
key_expansion_Contract(k),
lemmas=[subword_result],
script=ProofScript([z3(["SubWord"])]))
self.assertIs(result.is_success(), True)
return result
key_expand_128_result = key_expand_result(128)
key_expand_192_result = key_expand_result(192)
key_expand_256_result = key_expand_result(256)
if __name__ == "__main__":
unittest.main()
+143
View File
@@ -0,0 +1,143 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/AES/Cipher.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the Cryptol specification for AES's `Cipher` and `InvCipher`
// routines.
import "../Cipher.cry";
let verify_SubBytesInverts : TopLevel Theorem =
proveit {{ SubBytesInverts }} rme;
let verify_ShiftRowsInverts : TopLevel Theorem =
proveit {{ ShiftRowsInverts }} rme;
let verify_MixColumnsStepInverts : TopLevel Theorem =
proveit {{ MixColumnsStepInverts }} rme;
let verify_MixColumnsInverts : TopLevel Theorem =
do {
MixColumnsStepInverts_lemma <- verify_MixColumnsStepInverts;
proveit {{ MixColumnsInverts }}
do {
goal_eval_unint ["InvMixColumnsStep", "MixColumnsStep"];
use_lemmas [ MixColumnsStepInverts_lemma ];
goal_eval; trivial;
};
};
let verify_AddRoundKeyInvolution : TopLevel Theorem =
proveit {{ AddRoundKeyInvolution }} rme;
let verify_RoundInverts : TopLevel Theorem =
do {
AddRoundKeyInvolution_lemma <- verify_AddRoundKeyInvolution;
SubBytesInverts_lemma <- verify_SubBytesInverts;
ShiftRowsInverts_lemma <- verify_ShiftRowsInverts;
MixColumnsInverts_lemma <- verify_MixColumnsInverts;
proveit {{ RoundInverts }}
do {
goal_eval_unint [ "AddRoundKey"
, "SubBytes" , "InvSubBytes"
, "ShiftRows" , "InvShiftRows"
, "MixColumns", "InvMixColumns"
];
use_lemmas [ AddRoundKeyInvolution_lemma
, SubBytesInverts_lemma
, ShiftRowsInverts_lemma
, MixColumnsInverts_lemma
];
goal_eval; trivial;
};
};
// This parameterized lemma will be proven at all 3 key sizes (128,
// 192, and 256).
let verify_InvCipherEquivalence (KeySize : Int) : TopLevel Theorem =
do {
proveit {{ InvCipherEquivalence`{KeySize} }}
do {
goal_eval_unint [ "AddRoundKey"
, "InvSubBytes"
, "InvShiftRows"
, "InvMixColumns"
];
simplify (addsimp (core_thm "bvEq_refl") empty_ss);
goal_eval; trivial;
};
};
let verify_CipherInverts (KeySize : Int) : TopLevel Theorem =
do {
AddRoundKeyInvolution_lemma <- verify_AddRoundKeyInvolution;
SubBytesInverts_lemma <- verify_SubBytesInverts;
ShiftRowsInverts_lemma <- verify_ShiftRowsInverts;
MixColumnsInverts_lemma <- verify_MixColumnsInverts;
proveit {{ CipherInverts`{KeySize} }}
do {
goal_eval_unint [ "AddRoundKey"
, "SubBytes" , "InvSubBytes"
, "ShiftRows" , "InvShiftRows"
, "MixColumns", "InvMixColumns"
];
use_lemmas [ AddRoundKeyInvolution_lemma
, SubBytesInverts_lemma
, ShiftRowsInverts_lemma
, MixColumnsInverts_lemma
];
goal_eval; trivial;
};
};
// This parameterized lemma will be proven at all 3 key sizes (128,
// 192, and 256).
let verify_CipherInverts' (KeySize : Int) : TopLevel Theorem =
do {
AddRoundKeyInvolution_lemma <- verify_AddRoundKeyInvolution;
SubBytesInverts_lemma <- verify_SubBytesInverts;
ShiftRowsInverts_lemma <- verify_ShiftRowsInverts;
RoundInverts_lemma <- verify_RoundInverts;
proveit {{ CipherInverts'`{KeySize} }}
do {
goal_eval_unint [ "AddRoundKey"
, "SubBytes" , "InvSubBytes"
, "ShiftRows", "InvShiftRows"
, "Round" , "InvRound'"
];
use_lemmas [ AddRoundKeyInvolution_lemma
, SubBytesInverts_lemma
, ShiftRowsInverts_lemma
, RoundInverts_lemma
];
goal_eval; trivial;
};
};
/*
* Verify public properties of `algorithms::AES::Cipher`.
*/
let main : TopLevel [Theorem] = do {
CipherInverts_lemmas <- for [128, 192, 256] verify_CipherInverts;
CipherInverts'_lemmas <- for [128, 192, 256] verify_CipherInverts';
InvCipherEquivalence_lemmas <- for [128, 192, 256] verify_InvCipherEquivalence;
summarize_verification;
return
(foldl concat []
[ CipherInverts_lemmas
, CipherInverts'_lemmas
, InvCipherEquivalence_lemmas
]
);
};
+24
View File
@@ -0,0 +1,24 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/AES/GF28.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the AES Cryptol GF(2^^8) functions.
import "../GF28.cry";
let verify_GF28MultiplicationCorrect : TopLevel Theorem =
proveit {{ GF28MultiplicationCorrect }} rme;
/*
* Verify public properties of `algorithms::AES::GF28`.
*/
let main : TopLevel [Theorem] = do {
GF28MultiplicationCorrect_lemma <- verify_GF28MultiplicationCorrect;
summarize_verification;
return [GF28MultiplicationCorrect_lemma];
};
+47
View File
@@ -0,0 +1,47 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/AES/Main.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Include theorems from `Cipher` for use as lemmas.
include "Cipher.saw";
// Import the Cryptol specification for AES's `Encrypt` and `Decrypt`
// routines.
import "../Main.cry";
import "../Types.cry";
// This helper lemma rewrites away the `split (join ...)` between
// `decrypt` and `encrypt` in the `encryptInverts` proof goal.
let verify_split_join : TopLevel Theorem =
proveit {{ \(xs : Block) -> split (join xs) == xs }} rme;
// This parameterized lemma will be proven at all 3 key sizes (128,
// 192, and 256).
let verify_EncryptInverts (keySize : Int) : TopLevel Theorem =
do {
split_join_lemma <- verify_split_join;
CipherInverts_lemma_keySize <- verify_CipherInverts keySize;
proveit {{ EncryptInverts`{keySize} }}
do {
goal_eval_unint [ "split", "join", "InvCipher", "Cipher" ];
use_lemmas [ split_join_lemma, CipherInverts_lemma_keySize ];
goal_eval; trivial;
};
};
/*
* Verify public properties of `algorithms::AES::Main`.
*/
let main : TopLevel [Theorem] = do {
EncryptInverts_lemmas <- for [128, 192, 256] verify_EncryptInverts;
summarize_verification;
return EncryptInverts_lemmas;
};
+24
View File
@@ -0,0 +1,24 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/AES/SBox.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the Cryptol specification for AES's `SBox`es.
import "../SBox.cry";
let verify_SBoxInverts : TopLevel Theorem =
proveit {{ SBoxInverts }} rme;
/*
* Verify public properties of `algorithms::AES::SBox`.
*/
let main : TopLevel [Theorem] = do {
SBoxInverts_lemma <- verify_SBoxInverts;
summarize_verification;
return [SBoxInverts_lemma];
};
+42
View File
@@ -0,0 +1,42 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/AES`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
include "Main.saw";
let Main_main = main;
include "GF28.saw";
let GF28_main = main;
include "SBox.saw";
let SBox_main = main;
include "Cipher.saw";
let Cipher_main = main;
/*
* Script to run on command line:
* `saw all.saw`
* Return:
* `TopLevel [Theorem]` verifying all lemmas used throughout `algorithms/AES`.
*/
let main : TopLevel [Theorem] = do {
Main_lemmas <- Main_main;
GF28_lemmas <- GF28_main;
SBox_lemmas <- SBox_main;
Cipher_lemmas <- Cipher_main;
summarize_verification;
return
(foldl concat []
[ Main_lemmas
, GF28_lemmas
, SBox_lemmas
, Cipher_lemmas
]);
};
+13
View File
@@ -0,0 +1,13 @@
# Bibliography
[NSA19] U.S. National Security Agency. Mathematical routines for the
NIST prime elliptic curves. November 19, 2019.
[FIPS186-5] National Institute of Standards and Technology. FIPS
186-5. Digital Signature Standard (DSS). February 3, 2023.
https://doi.org/10.6028/NIST.FIPS.186-5
[NISTSP800-186] Recommendations for Discrete Logarithm-based
Cryptography: Elliptic Curve Domain Parameters. NIST SP
800-186. February 2023.
https://doi.org/10.6028/NIST.SP.800-186
+206
View File
@@ -0,0 +1,206 @@
/*
* Description: This module provides ECDSA signature creation and
* verification routines for the NIST elliptic curves P-192, P-224,
* P-256, P-384, and P-521 [FIPS186-5].
*/
module algorithms::ECDSA::ECDSA where
import shared::common::utils::TypeCoercion (ZtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::Routines as Routines where
type p = _p
b = _b
mp_mod_sqrt = _mp_mod_sqrt
parameter
/**
* `p` is an odd prime number > 3 [NSA19 Section 1], used to make an
* elliptic curve over `GF(p)`.
*/
type p : #
type constraint (fin p, prime p, p > 8) // `p > 8` comes from scalar multiplications.
/**
* An elliptic curve over `GF(p)` has an order, which is the
* smallest positive integer `n` such that for any point `S`, `nS =
* O`. The order of each of the NIST elliptic curves is prime [NSA19
* Section 2.2].
*/
type n : #
type constraint (prime n, p >= n)
/**
* Elliptic curve parameter. y ^^ 2 == x ^^ 3 - 3 * x + b [NSA19
* Section 1].
*/
b : Z p
/** Base point for an elliptic curve group [NSA19 Section 1]. */
G : { x : Z p, y : Z p, z : Z p }
/**
* Modular square root function specialized on `p` [NSA19 Section
* 3.2].
*/
mp_mod_sqrt : Z p -> Z p
/* Workaround for parameter hiding */
type _p = p
_b = b
_mp_mod_sqrt = mp_mod_sqrt
/**
* ECDSA Signature Generation Algorithm [FIPS186-5 Section 6.4.1].
*
* Inputs:
* `M`: Bit string to be signed.
* `d`: Private key in the interval [1, n-1].
* `Hash`: an approved hash function [FIPS186-5 Section 2.3].
* `k`: a per-message secret number (0 < k < n).
*
* Outputs:
* `(r, s)`: A pair of integers each in the interval `[1, n-1].
*
* Types:
* `hashlen`: The digest size of `Hash` [FIPS186-5 Section 6.4.1].
*/
sign :
{ hashlen, a }
( fin hashlen, fin a )
=> [a] -> Z n -> ([a] -> [hashlen]) -> Z n -> Result (Z n, Z n) (String _)
sign M d Hash k =
if d == 0 \/ r == 0 \/ s == 0 then
Err "failure"
else Ok (r, s)
where
// 1. Compute `H = Hash(M)`
H = Hash M : [hashlen]
// 2(a). If `len(n) ≥ hashlen`, set `E = H`. Otherwise, set `E`
// equal to the leftmost `log2(n)` bits of `H`.
E = take H : [min (width n) hashlen]
// 2(b). Convert the bit string `E` to the integer `e`.
e = fromInteger (toInteger E) : Z n
// 4. Compute `k^^-1 mod n`.
k' = recip k
// 5. Compute the elliptic curve point `R = [k]G`.
R = Routines::ec_mult (ZtoZ k) G
// 6. Set `xR` to x-coordinate of the affine representation of the
// point `R = (xR, yR)`. By definition of 'order', `[k]G` is not
// the point at infinity, and so the Affine transformation of
// `[k]G` will not fail. Hence `unwrap` here is safe to use.
{x = xR, y = _} = unwrap (ec_affinify R)
// 7. Convert the field element `xR` to the integer `r1`.
r1 = fromZ xR
// 8. Set `r = r1 mod n`
r = fromInteger r1 : Z n
// 9. Compute `s = `k^^-1 * (e + r * d) mod n`
s = k' * (e + r * d)
/**
* ECDSA Signature Verification Algorithm [FIPS186-5 Section 6.4.2].
*
* Inputs:
* `M`: A message
* `(r, s)`: A pair of integers.
* `Q`: A purported signature verification key.
* `Hash`: an approved hash function [FIPS186-5 Section 2.3].
*
* Outputs:
* A Boolean value denoting "accept" (`True`) or "reject" (`False`).
*
* Types:
* `hashlen`: The digest size of `Hash` [FIPS186-5 Section 6.4.1].
*/
verify :
{ hashlen, a }
( fin hashlen, fin a )
=> [a] -> (Z n, Z n) -> ProjectivePoint -> ([a] -> [hashlen]) -> Bool
verify M (r, s) Q Hash =
// 1. Verify that both `r` and `s` are integers in the interval
// `[1, n − 1]`. Output “reject” if verification fails.
if r == 0 \/ s == 0 \/ affinifyFailed \/ r1VerificationFailed then
False
else True
where
// 2. Compute `H = Hash(M)`
H = Hash M : [hashlen]
// 3(a). If `len(n) ≥ hashlen`, set `E = H`. Otherwise, set `E`
// equal to the leftmost `log2(n)` bits of `H`.
E = take H : [min (width n) hashlen]
// 3(b). Convert the bit string `E` to the integer `e`.
e = fromInteger (toInteger E) : Z n
// 4. Compute `s^^-1 mod n`.
s' = recip s
// 5. Compute `u = e * s^^-1` and `v = r * s^^-1`.
(u, v) = (e * s', r * s')
// 6. Compute `R1 = [u]G + [v]Q`.
R1 = Routines::ec_twin_mult (ZtoZ u) G (ZtoZ v) Q
// 7. Set `xR` to x-coordinate of the affine representation of the
// point `R1 = (xR, yR)`.
({x = xR, y = _}, affinifyFailed) =
case (ec_affinify R1) of
Ok a -> (a, False)
Err _ -> (undefined, True)
// 8. Convert the field element `xR` to the integer `r1`.
r1 = fromZ xR
// 9. Verify that `r = r1 mod n`
r1VerificationFailed = r != fromInteger r1
/**
* ECDSA Key Pair Generation [FIPS186-5 Section A.2.1]. Essentially
* step 5.
*/
private_to_public : Z n -> ProjectivePoint
private_to_public d = Routines::ec_mult (ZtoZ d) G
/**
* Routine 2.2.2: Create an affine point `R` from a projective point
* `S` [NSA19 Section 2.2].
*/
ec_affinify : ProjectivePoint -> Result AffinePoint (String _)
ec_affinify = Routines::ec_affinify
/**
* "The affine representations of `S` is a pair `S = (Sx, Sy)` of
* elements of `GF(p)` which satisfy the defining equation [NSA19
* Section 2.2]."
*/
type AffinePoint = Routines::AffinePoint
/**
* A projective representation of `S` is a triple `S = (Sx, Sy, Sz) of
* elements of `GF(p)`, such that `Sy ^^ 2 == Sx ^^ 3 * Sz ^^ 4 + b *
* Sz ^^ 6 (mod p)` [NSA19 Section 2.2].
*/
type ProjectivePoint = Routines::ProjectivePoint
/**
* Routine 2.2.3: Set `Rbar` to compressed `Sbar` from an affine point
* `S` [NSA19 Section 2.2].
*/
ec_compress : AffinePoint -> [2 + width p]
ec_compress = Routines::ec_compress
/**
* Routine 2.2.4: Create an affine point `R` from compressed `Sbar`
* [NSA19 Section 2.2].
*/
ec_decompress : [2 + width p] -> Result AffinePoint (String _)
ec_decompress = Routines::ec_decompress
/**
* Use `G` as a generator to produce random affine points and test the
* compression and decompression algorithms.
*
* ```
* :check decompress_correct
* ```
*/
decompress_correct : Z n -> Bool
property decompress_correct x =
affine_decompress_correct (unwrap (ec_affinify (Routines::ec_mult (ZtoZ x) G)))
where
affine_decompress_correct : AffinePoint -> Bool
affine_decompress_correct R =
R == unwrap (ec_decompress (ec_compress R))
+313
View File
@@ -0,0 +1,313 @@
/*
* Description: This module provides "routines for implementing
* primitives for elliptic curve cryptography on the NIST elliptic
* curves P-192, P-224, P-256, P-384, P-521" [NSA19].
*/
module algorithms::ECDSA::Routines where
import shared::common::utils::TypeCoercion (BVtoZ, ZtoZ, ZtoBV)
import shared::common::utils::Enum
parameter
/**
* `p` is an odd prime number > 3 [NSA19 Section 1], used to make an
* elliptic curve over `GF(p)`.
*/
type p : #
type constraint (fin p, prime p, p > 8) // `p > 8` comes from scalar multiplications.
/**
* Elliptic curve parameter. y ^^ 2 == x ^^ 3 - 3 * x + b [NSA19
* Section 1].
*/
b : Z p
/**
* Modular square root function specialized on `p` [NSA19 Section
* 3.2].
*/
mp_mod_sqrt : Z p -> Z p
/**
* Bit-length of `p` [NSA19 Section 1].
*/
type n = width p
/**
* "The affine representations of `S` is a pair `S = (Sx, Sy)` of
* elements of `GF(p)` which satisfy the defining equation [NSA19
* Section 2.2]."
*/
type AffinePoint = { x : Z p, y : Z p }
/**
* A projective representation of `S` is a triple `S = (Sx, Sy, Sz) of
* elements of `GF(p)`, such that `Sy ^^ 2 == Sx ^^ 3 * Sz ^^ 4 + b *
* Sz ^^ 6 (mod p)` [NSA19 Section 2.2].
*/
type ProjectivePoint = { x : Z p, y : Z p, z : Z p }
/**
* "The point at infinity, or the identity [NSA19 Section 2.2."
*/
O : ProjectivePoint
O = { x = 1, y = 1, z = 0 }
/**
* Routine 2.2.1: Create a projective point `R` from an affine point
* `S` [NSA19 Section 2.2].
*/
ec_projectify : AffinePoint -> ProjectivePoint
ec_projectify S = R
where R = { x = S.x
, y = S.y
, z = 1
}
/**
* Routine 2.2.2: Create an affine point `R` from a projective point
* `S` [NSA19 Section 2.2].
*/
ec_affinify : ProjectivePoint -> Result AffinePoint (String _)
ec_affinify S =
if S.z == 0 then
Err "Cannot affinify the point at infinity"
else Ok R
where
lambda = recip S.z
R = { x = lambda ^^ 2 * S.x
, y = lambda ^^ 3 * S.y
}
/**
* This is a helper function to deterine whether a given point is on
* the curve [NSA19 Section 1].
*/
pointIsOnCurve : AffinePoint -> Bool
pointIsOnCurve { x = x, y = y } =
y ^^ 2 == x ^^ 3 - 3 * x + b
/**
* Routine 2.2.3: Set `Rbar` to compressed `Sbar` from an affine point
* `S` [NSA19 Section 2.2].
*/
ec_compress : AffinePoint -> [2 + width p]
ec_compress S = Rbar
where
Rbar = drop (2 + (Sy % 2)) # Sx
Sx = ZtoBV S.x : [width p]
Sy = ZtoBV S.y : [width p]
/**
* Routine 2.2.4: Create an affine point `R` from compressed `Sbar`
* [NSA19 Section 2.2].
*/
ec_decompress : [2 + width p] -> Result AffinePoint (String _)
ec_decompress S = R
where
c = toInteger (take S : [2])
Rx = BVtoZ (drop S : [width p]) : Z p
t0 = Rx ^^ 3 - 3 * Rx + b
t1 = mp_mod_sqrt t0
Ry = if fromZ t1 % 2 == c % 2 then
t1
else
-t1
R = if t1 ^^ 2 != t0 then
Err "no such point"
else
Ok { x = Rx, y = Ry }
/**
* Property demonstrating that `mp_mod_sqrt` is correct.
*
* ```
* :check decompress_correct
* ```
*/
mp_mod_sqrt_correct : Z p -> Bool
property mp_mod_sqrt_correct x =
(mp_mod_sqrt (x ^^ 2)) ^^ 2 == (x ^^ 2)
/**
* Routine 2.2.5: Check whether the affine point `S` is on the curve
* [NSA19 Section 2.2].
*/
ec_is_point_affine : AffinePoint -> Bool
ec_is_point_affine S = S.y ^^ 2 == t
where t = S.x ^^ 3 - 3 * S.x + b
/**
* Routine 2.2.6: Set `R` to `2S` [NSA19 Section 2.2].
*/
ec_double : ProjectivePoint -> ProjectivePoint
ec_double S =
if S.z == 0 then
{ x = 1, y = 1, z = 0 } // 5: R <- (1, 1, 0) and return */
else
{ x = r18, y = r23, z = r13 }
where
r7 = S.z ^^ 2 // 7: t4 <- t3 ^^ 2
r8 = S.x - r7 // 8: t5 <- t1 - t4
r9 = S.x + r7 // 9: t4 <- t1 + t4
r10 = r9 * r8 // 10: t5 <- t4 * t5
r11 = 3 * r10 // 11: t4 <- 3 * t5
r12 = S.z * S.y // 12: t3 <- t3 * t2
r13 = 2 * r12 // 13: t3 <- 2 * t3
r14 = S.y ^^ 2 // 14: t2 <- t2 ^^ 2
r15 = S.x * r14 // 15: t5 <- t1 * t2
r16 = 4 * r15 // 16: t5 <- 4 * t5
r17 = r11 ^^ 2 // 17: t1 <- t4 ^^ 2
r18 = r17 - 2 * r16 // 18: t1 <- t1 - 2 * t5
r19 = r14 ^^ 2 // 19: t2 <- t2 ^^ 2
r20 = 8 * r19 // 20: t2 <- 8 * t2
r21 = r16 - r18 // 21: t5 <- t5 - t1
r22 = r11 * r21 // 22: t5 <- t4 * t5
r23 = r22 - r20 // 23: t2 <- t5 - t2
/**
* Routine 2.2.7: Set `R` to `S + T`. Set `R` to `(0, 0, 0)` if `S ==
* T` [NSA19 Section 2.2].
*/
ec_add : ProjectivePoint -> ProjectivePoint -> ProjectivePoint
ec_add S T =
if r13 == 0 then
if r14 == 0 then
{ x = 0, y = 0, z = 0 } // 17: R <- (0, 0, 0) and return */
else
{ x = 1, y = 1, z = 0 } // 19: R <- (1, 1, 0) and return */
else
{ x = r32, y = r37, z = r27 }
where
r4 = T.z ^^ 2 // 4: t7 <- t6 ^^ 2
r5 = S.x * r4 // 5: t1 <- t1 * t7
r6 = T.z * r4 // 6: t7 <- t6 * t7
r7 = S.y * r6 // 7: t2 <- t2 * t7
// Covers cases on lines 2-7 and 24-26.
(t1, t2, t3) = if T.z != 1 then (r5, r7, r25) else (S.x, S.y, S.z)
r9 = S.z ^^ 2 // 9: t7 <- t3 ^^ 2
r10 = T.x * r9 // 10: t4 <- t4 * t7
r11 = S.z * r9 // 11: t7 <- t3 * t7
r12 = T.y * r11 // 12: t5 <- t5 * t7
r13 = t1 - r10 // 13: t4 <- t1 - t4
r14 = t2 - r12 // 14: t5 <- t2 - t5
r22 = 2 * t1 - r13 // 22: t1 <- 2 * t1 - t4
r23 = 2 * t2 - r14 // 23: t2 <- 2 * t2 - t5
r25 = S.z * T.z // 25: t3 <- t3 * t6
r27 = t3 * r13 // 27: t3 <- t3 * t4
r28 = r13 ^^ 2 // 28: t7 <- t4 ^^ 2
r29 = r13 * r28 // 29: t4 <- t4 * t7
r30 = r22 * r28 // 30: t7 <- t1 * t7
r31 = r14 ^^ 2 // 31: t1 <- t5 ^^ 2
r32 = r31 - r30 // 32: t1 <- t1 - t7
r33 = r30 - 2 * r32 // 33: t7 <- t7 - 2 * t1
r34 = r14 * r33 // 34: t5 <- t5 * t7
r35 = r23 * r29 // 35: t4 <- t2 * t4
r36 = r34 - r35 // 36: t2 <- t5 - t4
r37 = r36 /. 2 // 37: t2 <- t2 / 2
/**
* Routine 2.2.8: Set `R` to `S + T` [NSA19 Section 2.2].
*/
ec_full_add : ProjectivePoint -> ProjectivePoint -> ProjectivePoint
ec_full_add S T =
if S.z == 0 then T // 1-4: if Sz == 0 then R <- T, return
| T.z == 0 then S // 5-8: if Tz == 0 then R <- S, return
// 10-11: if R == (0, 0, 0) then ec_double (R, s)
| R == {x = 0, y = 0, z = 0} then ec_double S
else R
where
R = ec_add S T // 9: ec_add (R, S, T)
/**
* Routine 2.2.9: Set `R` to `S - T` [NSA19 Section 2.2].
*/
ec_full_sub : ProjectivePoint -> ProjectivePoint -> ProjectivePoint
ec_full_sub S T = R
where
U = {x = T.x, y = -T.y, z = T.z}
R = ec_full_add S U
/**
* Routine 2.2.10: Set `R` to `d * S` [NSA19 Section 2.2].
*
* ```
* :s prover=cvc5
* :safe ec_mult
* ```
*/
ec_mult : Z p -> ProjectivePoint -> ProjectivePoint
ec_mult d S =
if d == 0 then { x = 1, y = 1, z = 0 }
| d == 1 then S
| S.z == 0 then { x = 1, y = 1, z = 0 }
else Rs!1
where
S' = if S.z != 1 then ec_projectify (unwrap (ec_affinify S))
else S // `unwrap` is safe here because `Err` is guarded by
// the `S.z == 0` case above.
h = 3 * k
k = ZtoBV d : [n + 2]
Rs = [ { x = 1, y = 1, z = 0 } ] // Start with 1 instead of `S` to avoid
// needing to find the high-bit.
# [ if hi && ~ki then ec_full_add RiDouble S'
| ~hi && ki then ec_full_sub RiDouble S'
else RiDouble
where RiDouble = ec_double Ri
| ki <- k | hi <- h | Ri <- Rs ]
/**
* Routine 2.2.11: An auxiliary function for `ec_twin_mult` [NSA19
* Section 2.2].
*/
F : [5] -> [5]
F t = if 18 <= t /\ t < 22 then 9
| 14 <= t /\ t < 18 then 10
| 22 <= t /\ t < 24 then 11
| 4 <= t /\ t < 12 then 14
else 12
/**
* Routine 2.2.12: Set `R` to `d0 * S + d1 * T` where `0 <= d0, d1 <
* p` [NSA19 Section 2.2].
*/
ec_twin_mult : Z p -> ProjectivePoint -> Z p -> ProjectivePoint -> ProjectivePoint
ec_twin_mult d0 S d1 T = (last states).0
where
SpT = ec_full_add S T
SmT = ec_full_sub S T
e0 = ZtoBV d0 : [max 4 n]
e1 = ZtoBV d1 : [max 4 n]
c = [ [ False, False ] # take e0
, [ False, False ] # take e1
] : [2][6]
states = [ ( O, c ) ] #
[ (Rk', c')
where
h0 = if c0@0 then 31 - tail c0 else tail c0
h1 = if c1@0 then 31 - tail c1 else tail c1
u0 = if h0 < F h1 then 0 else if c0@0 then -1 else 1
u1 = if h1 < F h0 then 0 else if c1@0 then -1 else 1
c' = [ [ (u0 != 0) ^ c0@1 ] # (drop`{2} c0) # [e0k]
, [ (u1 != 0) ^ c1@1 ] # (drop`{2} c1) # [e1k]
]
Rk' = if (u0 == -1) && (u1 == -1) then ec_full_sub RkDouble SpT
| (u0 == -1) && (u1 == 0) then ec_full_sub RkDouble S
| (u0 == -1) && (u1 == 1) then ec_full_sub RkDouble SmT
| (u0 == 0) && (u1 == -1) then ec_full_sub RkDouble T
| (u0 == 0) && (u1 == 1) then ec_full_add RkDouble T
| (u0 == 1) && (u1 == -1) then ec_full_add RkDouble SmT
| (u0 == 1) && (u1 == 0) then ec_full_add RkDouble S
| (u0 == 1) && (u1 == 1) then ec_full_add RkDouble SpT
else RkDouble
RkDouble = ec_double Rk
| (Rk, [c0, c1]) <- states
| e0k <- drop`{4} e0 # (0 : [5])
| e1k <- drop`{4} e1 # (0 : [5])
]
+28
View File
@@ -0,0 +1,28 @@
/*
* Description: This is an instantiation of ECDSA for the NIST
* elliptic curve P-192 [NSA19, NISTSP800-186].
*/
module algorithms::ECDSA::p192 = algorithms::ECDSA::ECDSA where
type p = 6277101735386680763835789423207666416083908700390324961279
type n = 6277101735386680763835789423176059013767194773182842284081
b = 2455155546008943817740293915197451784769108058161191238065
G = {x = 602046282375688656758213480587526111916698976636884684818,
y = 174050332293622031404857552280219410364023488927386650641,
z = 1}
mp_mod_sqrt : Z p -> Z p
mp_mod_sqrt c = r''
where
t1 = c ^^ 2 ; t1' = t1 * c
t2 = t1' ^^ (2^^2) ; t2' = t2 * t1'
t3 = t2' ^^ (2^^4) ; t3' = t3 * t2'
t4 = t3' ^^ (2^^8) ; t4' = t4 * t3'
t5 = t4' ^^ (2^^16) ; t5' = t5 * t4'
t6 = t5' ^^ (2^^32) ; t6' = t6 * t5'
r = t6' ^^ (2^^64) ; r' = r * t6'
r'' = r' ^^ (2^^62)
+72
View File
@@ -0,0 +1,72 @@
/*
* Description: This is an instantiation of ECDSA for the NIST
* elliptic curve P-224 [NSA19, NISTSP800-186].
*/
module algorithms::ECDSA::p224 = algorithms::ECDSA::ECDSA where
import shared::common::utils::TypeCoercion (ZtoBV)
type p = 26959946667150639794667015087019630673557916260026308143510066298881
type n = 26959946667150639794667015087019625940457807714424391721682722368061
b = 18958286285566608000408668544493926415504680968679321075787234672564
G = {x = 19277929113566293071110308034699488026831934219452440156649784352033,
y = 19926808758034470970197974370888749184205991990603949537637343198772,
z = 1}
mp_mod_sqrt : Z p -> Z p
mp_mod_sqrt c = r
where
s = random (ZtoBV c)
(d0, e0, f0) = RP c s
(d1, e1, f1) = RS (d0, e0, f0)
state = [(d1, e1, f1)] #
[ if d1' == 0 then (d, e, f) else (d1', e1', f1')
where (d1', e1', f1') = RS (d, e, f)
| (d, e, f) <- state ]
(d0', e0', _) = state@95
r = d0' /. e0'
RS : (Z p, Z p, Z p) -> (Z p, Z p, Z p)
RS (d0, e0, f0) = (d1, e1', f1'')
where
t = d0 ^^ 2
e1 = d0 * e0
d1 = t + f0
e1' = e1 + e1
f1 = t * f0
f1' = f1 + f1
f1'' = f1' + f1'
RSS : (Z p, Z p, Z p) -> [7] -> (Z p, Z p, Z p)
RSS (d0, e0, f0) j = (d1, e1, f1)
where
(d1, e1, f1) = (iterate RS (d0, e0, f0))@j
RM : Z p -> Z p -> Z p -> Z p -> Z p -> (Z p, Z p, Z p)
RM c d0 e0 d1 e1 = (d2, e2', f2'')
where
t1 = e0 * e1
t1' = t1 * c
t1'' = -t1'
t2 = d0 * d1
t2' = t2 + t1''
t1''' = d0 * e1
e2 = d1 * e0
e2' = e2 + t1'''
f2 = e2' ^^ 2
f2' = f2 * c
f2'' = -f2'
d2 = t2'
RP : Z p -> Z p -> (Z p, Z p, Z p)
RP c r = (d1, e1, f1)
where
(d1, e1, f1) = foldl body (r, 1, -c) [0..6]
body (d0, e0, f0) i = (d1'', e1'', f1'')
where
(d1', e1', _) = RSS (d0, e0, f0) (2^^i)
(d1'', e1'', f1'') = RM c d1' e1' d0 e0
+27
View File
@@ -0,0 +1,27 @@
/*
* Description: This is an instantiation of ECDSA for the NIST
* elliptic curve P-256 [NSA19, NISTSP800-186].
*/
module algorithms::ECDSA::p256 = algorithms::ECDSA::ECDSA where
type p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
type n = 115792089210356248762697446949407573529996955224135760342422259061068512044369
b = 41058363725152142129326129780047268409114441015993725554835256314039467401291
G = {x = 48439561293906451759052585252797914202762949526041747995844080717082404635286,
y = 36134250956749795798585127919587881956611106672985015071877198253568414405109,
z = 1}
mp_mod_sqrt : Z p -> Z p
mp_mod_sqrt c = r'' ^^ (2 ^^ 94)
where
t1 = (c ^^ 2) * c
t2 = (t1 ^^ (2 ^^ 2)) * t1
t3 = (t2 ^^ (2 ^^ 4)) * t2
t4 = (t3 ^^ (2 ^^ 8)) * t3
r = (t4 ^^ (2 ^^ 16)) * t4
r' = (r ^^ (2 ^^ 32)) * c
r'' = (r' ^^ (2 ^^ 96)) * c
+35
View File
@@ -0,0 +1,35 @@
/*
* Description: This is an instantiation of ECDSA for the NIST
* elliptic curve P-384 [NSA19, NISTSP800-186].
*/
module algorithms::ECDSA::p384 = algorithms::ECDSA::ECDSA where
type p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319
type n = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643
b = 27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575
G = {x = 26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087,
y = 8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871,
z = 1}
mp_mod_sqrt : Z p -> Z p
mp_mod_sqrt c = r''' ^^ (2 ^^ 30)
where
t1 = (c ^^ 2) * c
t2 = (t1 ^^ (2 ^^ 2)) * t1
t2' = (t2 ^^ 2) * c
t3 = (t2' ^^ (2 ^^ 5)) * t2'
t4 = (t3 ^^ (2 ^^ 5)) * t2'
t2'' = (t4 ^^ (2 ^^ 15)) * t4
t3' = t2'' ^^ (2 ^^ 2)
t1' = t3' * t1
t3'' = t3' ^^ (2 ^^ 28)
t2''' = t2'' * t3''
t3''' = (t2''' ^^ (2 ^^ 60)) * t2'''
r = (t3''' ^^ (2 ^^ 120)) * t3'''
r' = (r ^^ (2 ^^ 15)) * t4
r'' = (r' ^^ (2 ^^ 33)) * t1'
r''' = (r'' ^^ (2 ^^ 64)) * c
+21
View File
@@ -0,0 +1,21 @@
/*
* Description: This is an instantiation of ECDSA for the NIST
* elliptic curve P-521 [NSA19, NISTSP800-186].
*/
module algorithms::ECDSA::p521 = algorithms::ECDSA::ECDSA where
type p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151
type n = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449
b = 1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984
G = {x = 2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846,
y = 3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784,
z = 1}
mp_mod_sqrt : Z p -> Z p
mp_mod_sqrt c = rs@519
where
rs = [c] # [ ri ^^ 2 | ri <- rs ]
+37
View File
@@ -0,0 +1,37 @@
/*
* Description: This module contains a single test from:
* `https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/digital-signatures`
*/
module algorithms::ECDSA::tests::p192 where
import shared::common::utils::TypeCoercion (BVtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::p192
import algorithms::SHA1::Main as SHA1
/** Message to sign */
M = 0x66e98a165854cd07989b1ee0ec3f8dbe0ee3c2fb0051ef53a0be03457c4f21bce7dc50ef4df37486c3207dfee26bde4ed62340cbb2da784906b1b783b4d601bdff4ae1a7e5e85a85afa3208dc60f0990c823bedddb3db663426665152ed7b093d6bda506c93a694b83ac71553f31f5cc0d6ba2fa248090e8796573c4915d1586
/** Private key */
d = BVtoZ 0x0017899949d02b55f9556846411cc9de512c6f16ecdeb1c4
/** Random value */
k = BVtoZ 0x0098e9c07e8e6adb97b77d85b0c10a265e11737a89a3e37b
/** `r` component of the signature */
r = BVtoZ 0xaf1f749e3df6220ff04efd178618a977e0838b1b9dc126e3
/** `s` component of the signature */
s = BVtoZ 0x8990a04c6cc0ff26264ecf8f7831381a9dbc6e53cc8cc860
/** Public key */
Q = { x = BVtoZ 0x14f69738599689f5706ab71343becc886ef1569a2d1137fe
, y = BVtoZ 0x0cf5a433909e33217fb4df6b9593f71d43fb1c2a5653b763
}
/** A test of p192 */
property p192Test =
unwrap(sign M d SHA1::hash k) == (r, s)
/\ verify M (r, s) (private_to_public d) SHA1::hash
/\ Q == unwrap (ec_affinify (private_to_public d))
+37
View File
@@ -0,0 +1,37 @@
/*
* Description: This module contains a single test from:
* `https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/digital-signatures`
*/
module algorithms::ECDSA::tests::p224 where
import shared::common::utils::TypeCoercion (BVtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::p224
import algorithms::SHA2::SHA384 as SHA384
/** Message to sign */
M = 0x25e4416695f77551fdce276355528ccf1ddc2483821c5d22d751d50111ca2fadc6593b52c74f4b5957494f1df25b0b2f86950d0d19229ec6506fee8581d2dd09d48418b146ff16bd84a17ca0dc83b1888eb407376da6c8a88fa1e60b8c2a2471dfde4b3996ef673d5bde3d70c434dc9f2488e9de16ae657d29e5e59ec922a1ec
/** Private key */
d = BVtoZ 0x62c572ee0d6f81b27e591d788bfc2f42b5105d2663078dfb58069ebd
/** Random value */
k = BVtoZ 0x0f0bb1e428bcdebf4dc62a5278068efc0f8ce75f89e89b3630f102b2
/** `r` component of the signature */
r = BVtoZ 0xaac0ea27e129f544abcc77f110e70bbdd5aa3e425dc39d5e8887025d
/** `s` component of the signature */
s = BVtoZ 0x10e5dd06aee6b8419a04aa33d9d5678b0039c3acc3c4b61fe106bfdc
/** Public key */
Q = { x = BVtoZ 0xbd6ba605639b98fa8113a16a3bb004ddfaec901c98a931206165f4a5
, y = BVtoZ 0xa3190b10ef39e88abd60b2293b4707512b45c6c5ed5794cc11454427
}
/** A test of p224 */
property p224Test =
unwrap(sign M d SHA384::hash k) == (r, s)
/\ verify M (r, s) (private_to_public d) SHA384::hash
/\ Q == unwrap (ec_affinify (private_to_public d))
+37
View File
@@ -0,0 +1,37 @@
/*
* Description: This module contains a single test from:
* `https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/digital-signatures`
*/
module algorithms::ECDSA::tests::p256 where
import shared::common::utils::TypeCoercion (BVtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::p256
import algorithms::SHA2::SHA384 as SHA384
/** Message to sign */
M = 0xe0b8596b375f3306bbc6e77a0b42f7469d7e83635990e74aa6d713594a3a24498feff5006790742d9c2e9b47d714bee932435db747c6e733e3d8de41f2f91311f2e9fd8e025651631ffd84f66732d3473fbd1627e63dc7194048ebec93c95c159b5039ab5e79e42c80b484a943f125de3da1e04e5bf9c16671ad55a1117d3306
/** Private key */
d = BVtoZ 0xb6faf2c8922235c589c27368a3b3e6e2f42eb6073bf9507f19eed0746c79dced
/** Random value */
k = BVtoZ 0x9980b9cdfcef3ab8e219b9827ed6afdd4dbf20bd927e9cd01f15762703487007
/** `r` component of the signature */
r = BVtoZ 0xf5087878e212b703578f5c66f434883f3ef414dc23e2e8d8ab6a8d159ed5ad83
/** `s` component of the signature */
s = BVtoZ 0x306b4c6c20213707982dffbb30fba99b96e792163dd59dbe606e734328dd7c8a
/** Public key */
Q = { x = BVtoZ 0xe0e7b99bc62d8dd67883e39ed9fa0657789c5ff556cc1fd8dd1e2a55e9e3f243
, y = BVtoZ 0x63fbfd0232b95578075c903a4dbf85ad58f8350516e1ec89b0ee1f5e1362da69
}
/** A test of p256 */
property p256Test =
unwrap(sign M d SHA384::hash k) == (r, s)
/\ verify M (r, s) (private_to_public d) SHA384::hash
/\ Q == unwrap (ec_affinify (private_to_public d))
+37
View File
@@ -0,0 +1,37 @@
/*
* Description: This module contains a single test from:
* `https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/digital-signatures`
*/
module algorithms::ECDSA::tests::p384 where
import shared::common::utils::TypeCoercion (BVtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::p384
import algorithms::SHA2::SHA384 as SHA384
/** Message to sign */
M = 0x6b45d88037392e1371d9fd1cd174e9c1838d11c3d6133dc17e65fa0c485dcca9f52d41b60161246039e42ec784d49400bffdb51459f5de654091301a09378f93464d52118b48d44b30d781eb1dbed09da11fb4c818dbd442d161aba4b9edc79f05e4b7e401651395b53bd8b5bd3f2aaa6a00877fa9b45cadb8e648550b4c6cbe
/** Private key */
d = BVtoZ 0x201b432d8df14324182d6261db3e4b3f46a8284482d52e370da41e6cbdf45ec2952f5db7ccbce3bc29449f4fb080ac97
/** Random value */
k = BVtoZ 0xdcedabf85978e090f733c6e16646fa34df9ded6e5ce28c6676a00f58a25283db8885e16ce5bf97f917c81e1f25c9c771
/** `r` component of the signature */
r = BVtoZ 0x50835a9251bad008106177ef004b091a1e4235cd0da84fff54542b0ed755c1d6f251609d14ecf18f9e1ddfe69b946e32
/** `s` component of the signature */
s = BVtoZ 0x0475f3d30c6463b646e8d3bf2455830314611cbde404be518b14464fdb195fdcc92eb222e61f426a4a592c00a6a89721
/** Public key */
Q = { x = BVtoZ 0xc2b47944fb5de342d03285880177ca5f7d0f2fcad7678cce4229d6e1932fcac11bfc3c3e97d942a3c56bf34123013dbf
, y = BVtoZ 0x37257906a8223866eda0743c519616a76a758ae58aee81c5fd35fbf3a855b7754a36d4a0672df95d6c44a81cf7620c2d
}
/** A test of p384 */
property p384Test =
unwrap(sign M d SHA384::hash k) == (r, s)
/\ verify M (r, s) (private_to_public d) SHA384::hash
/\ Q == unwrap (ec_affinify (private_to_public d))
+37
View File
@@ -0,0 +1,37 @@
/*
* Description: This module contains a single test from:
* `https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/digital-signatures`
*/
module algorithms::ECDSA::tests::p521 where
import shared::common::utils::TypeCoercion (BVtoZ)
import shared::common::utils::Enum (unwrap)
import algorithms::ECDSA::p521
import algorithms::SHA2::SHA384 as SHA384
/** Message to sign */
M = 0xdbc094402c5b559d53168c6f0c550d827499c6fb2186ae2db15b89b4e6f46220386d6f01bebde91b6ceb3ec7b4696e2cbfd14894dd0b7d656d23396ce920044f9ca514bf115cf98ecaa55b950a9e49365c2f3a05be5020e93db92c37437513044973e792af814d0ffad2c8ecc89ae4b35ccb19318f0b988a7d33ec5a4fe85dfe
/** Private key */
d = BVtoZ (`0x095976d387d814e68aeb09abecdbf4228db7232cd3229569ade537f33e07ed0da0abdee84ab057c9a00049f45250e2719d1ecaccf91c0e6fcdd4016b75bdd98a950 : [521])
/** Random value */
k = BVtoZ (`0x0a8d90686bd1104627836afe698effe22c51aa3b651737a940f2b0f9cd72c594575e550adb142e467a3f631f4429514df8296d8f5144df86faa9e3a8f13939ad5b3 : [521])
/** `r` component of the signature */
r = BVtoZ (`0x02128f77df66d16a604ffcd1a515e039d49bf6b91a215b814b2a1c88d32039521fbd142f717817b838450229025670d99c1fd5ab18bd965f093cae7accff0675aae : [521])
/** `s` component of the signature */
s = BVtoZ (`0x008dc65a243700a84619dce14e44ea8557e36631db1a55de15865497dbfd66e76a7471f78e510c04e613ced332aa563432a1017da8b81c146059ccc7930153103a6 : [521])
/** Public key */
Q = { x = BVtoZ (`0x13b4ab7bc1ddf7fd74ca6f75ac560c94169f435361e74eba1f8e759ac70ab3af138d8807aca3d8e73b5c2eb787f6dcca2718122bd94f08943a686b115d869d3f406 : [521])
, y = BVtoZ (`0x0f293c1d627b44e7954d0546270665888144a94d437679d074787959d0d944d8223b9d4b5d068b4fbbd1176a004b476810475cd2a200b83eccd226d08b444a71e71 : [521])
}
/** A test of p521 */
property p521Test =
unwrap(sign M d SHA384::hash k) == (r, s)
/\ verify M (r, s) (private_to_public d) SHA384::hash
/\ Q == unwrap (ec_affinify (private_to_public d))
+12
View File
@@ -0,0 +1,12 @@
# Bibliography
[FIPS-PUB-198-1] The Keyed-Hash Message Authentication Code (HMAC).
Federal Information Processing Standards Publication 198-1.
July 2008.
https://doi.org/10.6028/NIST.FIPS.198-1
[NIST SP 800-107r1] Recommendation for Applications Using Approved
Hash Algorithms (Withdrawn). Quynh Dang. August 2021.
https://doi.org/10.6028/NIST.SP.800-107r1
[NIST HMAC Examples] https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values#aHashing
+65
View File
@@ -0,0 +1,65 @@
/*
* Description: This is a Cryptol specification for a keyed-Hash
* Message Authentication Code (HMAC), a mechanism for message
* authentication using an Approved Cryptographic Hash Function (ACHF)
* [FIPS-PUB-198-1 Section 2.3].
*/
module algorithms::HMAC::Main where
/*
* `HMAC` and `HMAC_init` use the following types [FIPS-PUB-198-1
* Section 2.3]:
* `λ` : The number of (leftmost) bits of the output used
* [FIPS-PUB-198-1 Section 5].
* `B` : Block size (in bytes) of the input of the ACHF.
* `k` : Length of `K` in bytes.
* `n` : Length of `text` in bits, where `0 <= n < 2^^B - 8*B`.
* `L` : Block size (in bytes) of the output of the ACHF.
*/
/**
* This defines the HMAC message authentication function using the
* ACHF `Hk`, `Hn`, and `HL`. Each ACHF is technically the same but
* defined with a different input size.
*/
HMAC :
{ λ, B, k, n, L}
( λ <= L * 8
, fin B, 2 ^^ B >= 8 * B
, fin k
, fin n, n < 2 ^^ B - 8 * B
, L <= B
)
=> ([k * 8] -> [L * 8])
-> ([B * 8 + n] -> [L * 8])
-> ([(B + L) * 8] -> [L * 8])
-> [k][8]
-> [n]
-> [λ]
HMAC Hk Hn HL K text =
take (HL ((K0 ^ opad) # (Hn ((K0 ^ ipad) # text))))
where
K0 : [B * 8] // `K` after preprocessing to form
// a `B`-byte key.
K0 = HMAC_init Hk K
opad = join (repeat`{B} 0x5C) // [FIPS-PUB-198-1 Section 2.3]
ipad = join (repeat`{B} 0x36) // [FIPS-PUB-198-1 Section 2.3]
private
/**
* This function initializes the HMAC key `K` by performing step 1-3
* of [FIPS-PUB-198-1 Section 4 Table 1]. Step 2 implicitly requires
* `L <= B`.
*/
HMAC_init :
{ B, k, L }
( fin B, fin k, L <= B )
=> ([k * 8] -> [L * 8])
-> [k][8]
-> [B * 8]
HMAC_init Hk K
| k == B => join K
| k > B => Hk (join K) # (zero : [(B - L) * 8])
| k < B => join K # (zero : [(B - k) * 8])
+62
View File
@@ -0,0 +1,62 @@
/*
* Description: The following properties check test vectors for
* HMAC-SHA3-224 provided by [NIST HMAC Examples].
*/
module algorithms::HMAC::tests::SHA3_224 where
import algorithms::SHA3::SHA3_224 (HMAC)
property Sample1 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b
])
msg = join "Sample message for keylen<blocklen"
mac = join [ 0x332cfd59, 0x347fdb8e, 0x576e7726, 0x0be4aba2
, 0xd6dc5311, 0x7b3bfb52, 0xc6d18c04
]
property Sample2 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f
])
msg = join "Sample message for keylen=blocklen"
mac = join [ 0xd8b733bc, 0xf66c644a, 0x12323d56, 0x4e24dcf3
, 0xfc75f231, 0xf3b67968, 0x359100c7
]
property Sample3 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f
, 0x90919293, 0x94959697, 0x98999a9b, 0x9c9d9e9f
, 0xa0a1a2a3, 0xa4a5a6a7, 0xa8a9aaab
])
msg = join "Sample message for keylen>blocklen"
mac = join [ 0x078695ee, 0xcc227c63, 0x6ad31d06, 0x3a15dd05
, 0xa7e819a6, 0x6ec6d8de, 0x1e193e59
]
property Sample4 = take (HMAC key msg) == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b
])
msg = join "Sample message for keylen<blocklen, with truncated tag"
mac = join [0x8569c54c, 0xbb00a9b7, 0x8ff1b391] # 0xb0e5
+62
View File
@@ -0,0 +1,62 @@
/*
* Description: The following properties check test vectors for
* HMAC-SHA3-256 provided by [NIST HMAC Examples].
*/
module algorithms::HMAC::tests::SHA3_256 where
import algorithms::SHA3::SHA3_256 (HMAC)
property Sample1 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
])
msg = join "Sample message for keylen<blocklen"
mac = join [ 0x4fe8e202, 0xc4f058e8, 0xdddc23d8, 0xc34e4673
, 0x43e23555, 0xe24fc2f0, 0x25d598f5, 0x58f67205
]
property Sample2 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687
])
msg = join "Sample message for keylen=blocklen"
mac = join [ 0x68b94e2e, 0x538a9be4, 0x103bebb5, 0xaa016d47
, 0x961d4d1a, 0xa9060613, 0x13b557f8, 0xaf2c3faa
]
property Sample3 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f
, 0x90919293, 0x94959697, 0x98999a9b, 0x9c9d9e9f
, 0xa0a1a2a3, 0xa4a5a6a7
])
msg = join "Sample message for keylen>blocklen"
mac = join [ 0x9bcf2c23, 0x8e235c3c, 0xe88404e8, 0x13bd2f3a
, 0x97185ac6, 0xf238c63d, 0x6229a00b, 0x07974258
]
property Sample4 = take (HMAC key msg) == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
])
msg = join "Sample message for keylen<blocklen, with truncated tag"
mac = join [ 0xc8dc7148, 0xd8c1423a, 0xa549105d, 0xafdf9cad
]
+65
View File
@@ -0,0 +1,65 @@
/*
* Description: The following properties check test vectors for
* HMAC-SHA3-384 provided by [NIST HMAC Examples].
*/
module algorithms::HMAC::tests::SHA3_384 where
import algorithms::SHA3::SHA3_384 (HMAC)
property Sample1 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
])
msg = join "Sample message for keylen<blocklen"
mac = join [ 0xd588a3c5, 0x1f3f2d90, 0x6e8298c1, 0x199aa8ff
, 0x62962181, 0x27f6b38a, 0x90b6afe2, 0xc5617725
, 0xbc99987f, 0x79b22a55, 0x7b6520db, 0x710b7f42
]
property Sample2 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667
])
msg = join "Sample message for keylen=blocklen"
mac = join [ 0xa27d24b5, 0x92e8c8cb, 0xf6d4ce6f, 0xc5bf62d8
, 0xfc98bf2d, 0x486640d9, 0xeb8099e2, 0x4047837f
, 0x5f3bffbe, 0x92dcce90, 0xb4ed5b1e, 0x7e44fa90
]
property Sample3 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f
, 0x90919293, 0x94959697
])
msg = join "Sample message for keylen>blocklen"
mac = join [ 0xe5ae4c73, 0x9f455279, 0x368ebf36, 0xd4f5354c
, 0x95aa184c, 0x899d3870, 0xe460ebc2, 0x88ef1f94
, 0x70053f73, 0xf7c6da2a, 0x71bcaec3, 0x8ce7d6ac
]
property Sample4 = take (HMAC key msg) == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
])
msg = join "Sample message for keylen<blocklen, with truncated tag"
mac = join [ 0x25f4bf53, 0x606e91af, 0x79d24a4b, 0xb1fd6aec
, 0xd44414a3, 0x0c8ebb0a
]
+67
View File
@@ -0,0 +1,67 @@
/*
* Description: The following properties check test vectors for
* HMAC-SHA3-512 provided by [NIST HMAC Examples].
*/
module algorithms::HMAC::tests::SHA3_512 where
import algorithms::SHA3::SHA3_512 (HMAC)
property Sample1 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
])
msg = join "Sample message for keylen<blocklen"
mac = join [ 0x4efd629d, 0x6c71bf86, 0x162658f2, 0x9943b1c3
, 0x08ce27cd, 0xfa6db0d9, 0xc3ce8176, 0x3f9cbce5
, 0xf7ebe986, 0x8031db1a, 0x8f8eb7b6, 0xb95e5c5e
, 0x3f657a89, 0x96c86a2f, 0x6527e307, 0xf0213196
]
property Sample2 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647
])
msg = join "Sample message for keylen=blocklen"
mac = join [ 0x544e257e, 0xa2a3e5ea, 0x19a590e6, 0xa24b724c
, 0xe6327757, 0x723fe275, 0x1b75bf00, 0x7d80f6b3
, 0x60744bf1, 0xb7a88ea5, 0x85f9765b, 0x47911976
, 0xd3191cf8, 0x3c039f5f, 0xfab0d29c, 0xc9d9b6da
]
property Sample3 = HMAC key msg == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
, 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f
, 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f
, 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f
, 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f
, 0x80818283, 0x84858687
])
msg = join "Sample message for keylen>blocklen"
mac = join [ 0x5f464f5e, 0x5b7848e3, 0x885e49b2, 0xc385f069
, 0x4985d0e3, 0x8966242d, 0xc4a5fe3f, 0xea4b37d4
, 0x6b65cece, 0xd5dcf594, 0x38dd840b, 0xab22269f
, 0x0ba7febd, 0xb9fcf746, 0x02a35666, 0xb2a32915
]
property Sample4 = take (HMAC key msg) == mac
where
key = split (join [ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f
, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f
, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f
, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
])
msg = join "Sample message for keylen<blocklen, with truncated tag"
mac = join [ 0x7bb06d85, 0x9257b25c, 0xe73ca700, 0xdf34c5cb
, 0xef5c898b, 0xac91029e, 0x0b27975d, 0x4e526a08
]
+9
View File
@@ -0,0 +1,9 @@
# Bibliography
[FIPS-PUB-202] SHA-3 Standard: Permutation-Based Hash and
Extendable-Output Functions. August 2015.
http://dx.doi.org/10.6028/NIST.FIPS.202
[NIST-SP-800-185] NIST Special Publication 800-185. SHA-3 Derived
Functions: cSHAKE, KMAC, TupleHash and ParallelHash. December 2016.
https://doi.org/10.6028/NIST.SP.800-185
+116
View File
@@ -0,0 +1,116 @@
/*
* Description: This module provides KECCAK Message Authentication
* Code (KMAC) functions [NIST-SP-800-185 Section 4].
*/
module algorithms::KMAC::Main where
import algorithms::Keccak::derived (bytepad, encode_string, right_encode)
import algorithms::Keccak::Main (bitReordering)
import algorithms::cSHAKE::Main (cSHAKE128, cSHAKE256)
/**
* The KECCAK Message Authentication Code (KMAC128) algorithm is a PRF
* and keyed hash function based on Keccak. It provides
* variable-length output, and unlike `SHAKE128` and `cSHAKE128`,
* altering the requested output length generates a new, unrelated
* output [NIST-SP-800-185 Section 4.3].
*
* Inputs:
* `K`: A key bit string of any length, including zero.
* `X`: The main input bit string. It may be of any length, including zero.
* `S`: A customization bit string of any length, including zero. If
* no customization is desired, set `S` to the empty string.
*
* Notable types:
* `L`: An integer representing the requested output length in
* bits.
*/
KMAC128 :
{ L, k, a, s }
( L < 2 ^^ 2040, k < 2 ^^ 2040, fin a, s < 2 ^^ 2040)
=> [k] -> [a] -> [s] -> [L]
KMAC128 K X S =
cSHAKE128`{L} newX (join "KMAC") S
where
K' = bitReordering K
X' = bitReordering X
newX = bitReordering (bytepad`{168} (encode_string K') # X' # right_encode`{L})
/**
* The KECCAK Message Authentication Code (KMAC256) algorithm is a PRF
* and keyed hash function based on Keccak. It provides
* variable-length output, and unlike `SHAKE256` and `cSHAKE256`,
* altering the requested output length generates a new, unrelated
* output [NIST-SP-800-185 Section 4.3].
*
* Inputs:
* `K`: A key bit string of any length, including zero.
* `X`: The main input bit string. It may be of any length, including zero.
* `S`: A customization bit string of any length, including zero. If
* no customization is desired, set `S` to the empty string.
*
* Notable types:
* `L`: An integer representing the requested output length in
* bits.
*/
KMAC256 :
{ L, k, a, s }
( L < 2 ^^ 2040, k < 2 ^^ 2040, fin a, s < 2 ^^ 2040)
=> [k] -> [a] -> [s] -> [L]
KMAC256 K X S =
cSHAKE256`{L} newX (join "KMAC") S
where
K' = bitReordering K
X' = bitReordering X
newX = bitReordering (bytepad`{136} (encode_string K') # X' # right_encode`{L})
/**
* This is a variant of KMAC128 as an XOF [NIST-SP-800-185 Section
* 4.3.1].
*
* Inputs:
* `K`: A key bit string of any length, including zero.
* `X`: The main input bit string. It may be of any length, including zero.
* `S`: A customization bit string of any length, including zero. If
* no customization is desired, set `S` to the empty string.
*
* Notable types:
* `L`: An integer representing the requested output length in
* bits.
*/
KMACXOF128 :
{ L, k, a, s }
( k < 2 ^^ 2040, fin a, s < 2 ^^ 2040)
=> [k] -> [a] -> [s] -> [L]
KMACXOF128 K X S =
cSHAKE128`{L} newX (join "KMAC") S
where
K' = bitReordering K
X' = bitReordering X
newX = bitReordering (bytepad`{168} (encode_string K') # X' # right_encode`{0})
/**
* This is a variant of KMAC256 as an XOF [NIST-SP-800-185 Section
* 4.3.1].
*
* Inputs:
* `K`: A key bit string of any length, including zero.
* `X`: The main input bit string. It may be of any length, including zero.
* `S`: A customization bit string of any length, including zero. If
* no customization is desired, set `S` to the empty string.
*
* Notable types:
* `L`: An integer representing the requested output length in
* bits.
*/
KMACXOF256 :
{ L, k, a, s }
( k < 2 ^^ 2040, fin a, s < 2 ^^ 2040)
=> [k] -> [a] -> [s] -> [L]
KMACXOF256 K X S =
cSHAKE256`{L} newX (join "KMAC") S
where
K' = bitReordering K
X' = bitReordering X
newX = bitReordering (bytepad`{136} (encode_string K') # X' # right_encode`{0})
+15
View File
@@ -0,0 +1,15 @@
# Bibliography
[FIPS-PUB-202] SHA-3 Standard: Permutation-Based Hash and
Extendable-Output Functions. August 2015.
http://dx.doi.org/10.6028/NIST.FIPS.202
[NIST-SP-800-185] SHA-3 Derived Functions: cSHAKE, KMAC, TupleHash,
and ParallelHash. John Kelsey, Shu-jen Chang, Ray Perlnder. NIST
Special Publication 800-185. December 2016.
https://doi.org/10.6028/NIST.SP.800-185
[SHA3 Test Vectors], National Institute of Standards and
Technology. Cryptographic Standards and Guidelines: Examples with
Intermediate Values, 2022.
https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
+137
View File
@@ -0,0 +1,137 @@
/*
* Description: This is a Cryptol specification for the Secure Hash
* Algorithm-3 (SHA-3) family of functions on binary data. Each of
* the SHA-3 functions is based on an instance of the Keccak algorithm
* that NIST selected as the winner of the SHA-3 Cryptographic Hash
* Algorithm Competition. This Cryptol spec is also for the Keccak-p
* family of mathematical permutations, including the permutation that
* underlies Keccak, in order to facilite the development of
* additional permutation-based cryptographic functions
* [FIPS-PUB-202].
*/
module algorithms::Keccak::Main where
import algorithms::Keccak::Types
import algorithms::Keccak::StepMappings
import shared::sponge::Main as sponge
/**
* The conversion function from hexadecimal strings to the SHA-3
* strings that they represent [FIPS-PUB-202 Section B.1].
*/
h2b : {n, m} (n <= 8 * m) => [m * 2 * 4] -> [n]
h2b H
| fin n => take (join (map reverse Hs))
where Hs = groupBy`{8} H : [m][8]
| n == inf => join (map reverse Hs) // avoid `take`
where Hs = groupBy`{8} H : [m][8]
/** Test for `h2b` [FIPS-PUB-202 Section B.1] */
property h2bTest = h2b`{14} 0xA32E == 0b1100_0101_0111_01
/**
* The conversion function from SHA-3 bit strings to the hexadecimal
* strings that represent them [FIPS-PUB-202 Section B.1].
*/
b2h : {n} (fin n) => [n] -> [n + n %^ 8]
b2h S = H
where
T = S # zero : [n + n %^ 8]
H = h2b`{m = n /^ (2 * 4)} T
/**
* Property demonstrating that `h2b` inverts `b2h`.
*
* ```
* :prove h2bInvertsb2h`{0}
* :prove h2bInvertsb2h`{1}
* :prove h2bInvertsb2h`{10}
* ```
*/
h2bInvertsb2h : {n} (fin n) => [n] -> Bool
property h2bInvertsb2h S = h2b`{n, n /^ 8} (b2h S) == S
/**
* Property demonstrating that `b2h` inverts `h2b`.
* Essentially, this transformation preserves all the full bytes of
* `H` and zeros out the first `n %^ 8` residual bits in the last
* partial byte, if one exists.
*
* ```
* :prove b2hInvertsh2b`{0, 0}
* :prove b2hInvertsh2b`{0, 1}
* :prove b2hInvertsh2b`{1, 1}
* :prove b2hInvertsh2b`{7, 1}
* :prove b2hInvertsh2b`{8, 1}
* :prove b2hInvertsh2b`{101, 33}
* ```
*/
b2hInvertsh2b : {n, m} (fin m, n <= 8 * m) => [m * 2 * 4] -> Bool
property b2hInvertsh2b H = b2h (h2b`{n, m} H) == L # z # R
where
L = take`{n - n % 8} H
z = zero`{[n %^ 8]}
R = drop`{back=n % 8} (take`{n + n %^ 8} H)
/**
* Keccak employs a involutory bit-reordering function to swap between
* binary and hexadecimal representations. Specifically, "In binary
* representation, bytes are written with the low-order bit first,
* while in hexadecimal representation, bytes are written with the
* high-order digit first. E.g., 0x01 = 10000000 and 0x80 = 00000001."
* [NIST-SP-800-185 Section 2]. Note that `take`ing `n` bits from a
* reordered *infinte* sequence can give a different result than
* reordering a finite sequence of length `n`. Namely, the residual
* bits in any final partial byte may be different. For example:
* ```
* > take`{12} (bitReordering (0x123 # zero : [inf]))
* 0x480
* > bitReordering 0x123
* 0x483
* ```
*/
bitReordering : {a} [a] -> [a]
bitReordering x
| fin a => h2b`{m = (a - a % 8) / 8} bytes # bits
where (bytes, bits) = splitAt`{front = a - (a % 8), back = a % 8} x
| a == inf => h2b`{m = a} x
/**
* Property demonstrating that `bitReordering` is an involution.
*
* ```
* :prove bitReorderingIsAnInvolution`{0}
* :prove bitReorderingIsAnInvolution`{1}
* :prove bitReorderingIsAnInvolution`{31}
* :prove bitReorderingIsAnInvolution`{32}
* :prove bitReorderingIsAnInvolution`{33}
* :prove bitReorderingIsAnInvolution`{100}
* ```
*/
bitReorderingIsAnInvolution : {a} (fin a) => [a] -> Bool
property bitReorderingIsAnInvolution x = bitReordering (bitReordering x) == x
/**
* This property demonstrates that `take`ing `n` bits from a reordered
* *infinte* sequence can give a different result than reordering a
* finite sequence of length `n`. Namely, the residual bits in any
* final partial byte may be different. This is something to be aware
* of when working with infinite sequences, e.g. XOFs.
*/
bitReorderingInfIssue : Bool
property bitReorderingInfIssue =
take`{12} (bitReordering (0x123 # zero : [inf])) == 0x480
/\ bitReordering 0x123 == 0x483
/**
* The following defines the Keccak family of functions. In
* particular, Keccak[c] = sponge[Keccak_p[1600, 24], pad10*1, 1600 -
* c] [FIPS-PUB-202].
*/
Keccak :
{c, d, a}
(fin c, 1600 > c, fin a)
=> [a] -> [d]
Keccak N =
take`{d, inf} (sponge::sponge`{b=1600, r=1600-c} Keccak_p_w64_nr24 (sponge::pad10s1 N))
+46
View File
@@ -0,0 +1,46 @@
/*
* Description: This module contains a Cryptol specification for the
* functions used to convert between state strings and state arrays in
* the Keccak family of functions [FIPS-PUB-202].
*/
module algorithms::Keccak::StateConversions where
import algorithms::Keccak::Types as Types
/**
* The following function transforms a string state `S` into a state
* array `A` [FIPS-PUB-202 Section 3.1.2].
*/
toState : {w} (fin w) => Types::String w -> Types::State w
toState S = A
where
A@x@y = (split S : [25][w]) @ (5 * y + x)
/**
* The following function transforms a state array `A` into a string
* state `S` [FIPS-PUB-202 Section 3.1.3].
*/
toString : {w} (fin w) => Types::State w -> Types::String w
toString A = S
where
S = join [ A@i@j | j <- [0..4], i <- [0..4] ]
/**
* The following property shows that `toString` inverts `toState`.
* ```
* :prove toStatetoStringProp`{32}
* :prove toStatetoStringProp`{64}
* ```
*/
toStatetoStringProp : {w} (fin w) => Types::String w -> Bool
property toStatetoStringProp S = toString (toState S) == S
/**
* The following property confirms that `toState` inverts `toString`.
* ```
* :prove toStringtoStateProp`{32}
* :prove toStringtoStateProp`{64}
* ```
*/
toStringtoStateProp : {w} (fin w) => Types::State w -> Bool
property toStringtoStateProp S = toState (toString S) == S
+154
View File
@@ -0,0 +1,154 @@
/*
* Description: This module is a Cryptol specification for the Step
* Mapping algorithms as specified by [FIPS-PUB-202 Section 3.2].
*/
module algorithms::Keccak::StepMappings where
import algorithms::Keccak::Types as Types
import algorithms::Keccak::StateConversions
import shared::LFSR::Main as LFSR
import shared::common::utils::BitVector (parity)
/**
* The following defines "⨁" as an infix operator for the XOR
* operation and is used to match the formatting of [FIPS-PUB-202] as
* closely as possible.
*/
(⨁) = (^)
infixl 45 ⨁
/**
* [FIPS-PUB-202 Algorithm 1]
* 1. For all pairs (x,z) such that 0≤x<5 and 0≤z<w, let
* C[x,z]=A[x,0,z] ⨁ A[x,1,z] ⨁ A[x,2,z] ⨁ A[x,3,z] ⨁ A[x,4,z].
*
* 2. For all pairs (x,z) such that 0≤x<5 and 0≤z<w, let
* D[x,z] = C[(x-1) mod 5, z] ⨁ C[(x+1) mod 5, (z-1) mod w].
*
* 3. For all triples (x,y,z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
* A'[x,y,z] = A[x,y,z] ⨁ D[x,z].
*/
θ : {w} (fin w) => Types::State w -> Types::State w
θ A = A'
where
C, D : [5][w]
C@x = A@x@0 ⨁ A@x@1 ⨁ A@x@2 ⨁ A@x@3 ⨁ A@x@4
D@x = C@((x-1) % 5) ⨁ C@((x+1) % 5) >>> 1
A'@x@y = A@x@y ⨁ D@x
/**
* [FIPS-PUB-202 Algorithm 2]
* 1. For all z such that 0≤z<w, let A'[0,0,z] = A[0,0,z].
* 2. Let (x,y) = (1,0).
* 3. For t from 0 to 23:
* a. for all z such that 0≤z<w, let A'[x,y,z]=A[x,y,(z-(t+1)(t+2)/2 mod w]
* b. let (x,y) = (y,(2x+3y) mod 5).
*
* Here we specify the offset table variant.
*/
ρ : {w} (fin w) => Types::State w -> Types::State w
ρ A = A'
where
A' = groupBy`{5} [ a >>> o | a <- join A | o <- offsets ]
offsets = [ 0, 36, 3, 105, 210
, 1, 300, 10, 45, 66
, 190, 6, 171, 15, 253
, 28, 55, 153, 21, 120
, 91, 276, 231, 136, 78
]
/**
* [FIPS-PUB-202 Algorithm 3]
* For all triples (x,y,z) such that 0≤x<5, 0≤y<5, 0≤z<w, let
* A'[x,y,z] = A[(x+3y) mod 5, x, z]
*/
π : {w} (fin w) => Types::State w -> Types::State w
π A = A'
where
A'@x@y = A@((x + 3 * y) % 5)@x
/**
* [FIPS-PUB-202 Algorithm 4]
* For all triples (x,y,z) such that 0≤x<5, 0≤y<5, 0≤z<w, let
* A'[x,y,z] = A[x,y,z] ⨁ ((A[(x+1) mod 5, y, z] ⨁ 1) ⦁ A[(x+2) mod 5, y, z]).
*/
χ : {w} (fin w) => Types::State w -> Types::State w
χ A = A'
where
A'@x = A@x ⨁ ~A@((x+1) % 5) && A@((x+2) % 5)
/**
* [FIPS-PUB-202 Algorithm 5]
* The specification of ι's round constant `rc`. Each of these l + 1
* bits is generated by a function that is based on a linear feedback
* shift register. Note, l = log2(w).
*/
rc : [inf]
rc = (LFSR::fmsrRun (⨁) <| x^^8 + x^^6 + x^^5 + x^^4 + 1 |> 0b10000000).stream
/**
* [FIPS-PUB-202 Algorithm 6]
* For all triples (x,y,z) such that 0≤x<5, 0≤y<5, 0≤z<w, let
* A'[0,0,z]=A'[0,0,z] ⨁ RC[z].
* Otherwise,
* A'[x,y,z] = A[x,y,z].
* The round constant bits for `ir` are excited by
* RC[2^^j - 1] = rc(j+7*ir)
*/
ι :
{w, a}
(fin w, Integral a, Literal (max 7 (lg2 w)) a)
=> Types::State w -> a -> Types::State w
ι A ir = A'
where
type l = lg2 w // [FIPS-PUB-202 Section 2.2]
A'@x@y = if (x == 0 /\ y == 0) then A@x@y ⨁ RC else A@x@y
RC = updates zero
[ 2^^j - 1 | j <- [0 .. l] ]
[ rc @ (j + 7*ir) | j <- [0 .. l] ]
/**
* The Keccak Round Function, `Rnd`, is the transformation that
* results from applying the step functions in order [FIPS-PUB-202
* part of Algorithm 7].
*/
Rnd :
{w, a}
(fin w, Integral a, Literal (max 7 (lg2 w)) a)
=> Types::State w -> a -> Types::State w
Rnd A ir = ι (χ (π (ρ (θ A)))) ir
/**
* [FIPS-PUB-202 Algorithm 7]
* 1. Converts `S` into a state array, `A`, as described in Section 3.1.2.
* 2. For `ir` from `12+2l-nr` to `12+2l-1`, let `A = Rnd(A, ir)`.
* 3. Convert `A` into a string `S'` of length `b`, as described in Section 3.1.3.
* 4. Return `S'`.
*/
Keccak_p :
{ w, nr }
( fin w
, fin nr
, 12 + 2 * (lg2 w) >= nr
)
=> Types::String w -> Types::String w
Keccak_p S = S'
where
type l = lg2 w
A = toState S
A' = foldl Rnd A [12 + 2*l - nr .. < 12 + 2*l]
S' = toString A'
/**
* A Rust FFI for Keccak_p specialized to `w = 64` and `nr = 24`.
*/
foreign Keccak_p_FFI : Types::State 64 -> Types::State 64
Keccak_p_FFI A = toState (Keccak_p`{w = 64, nr = 24} (toString A))
/**
* A shim to rejigger types for ease of FFI integration into the main
* Keccak function.
*/
Keccak_p_w64_nr24 : Types::String 64 -> Types::String 64
Keccak_p_w64_nr24 S = toString (Keccak_p_FFI (toState S))
+1
View File
@@ -0,0 +1 @@
rust/target/release/libKeccak.dylib
+1
View File
@@ -0,0 +1 @@
rust/target/release/libKeccak.so
+20
View File
@@ -0,0 +1,20 @@
/*
* Description: This is a Cryptol specification for the types needed
* to specify the Keccak family of algorithms.
*/
module algorithms::Keccak::Types where
/**
* The `State` type is a 5-by-5-by-w array of bits that represents the
* state for a Keccak-p permutation. The indices for x, y, and z
* coordinates range from 0 to 4, 0 to 4, and 0 to `w-1`, respectively
* [FIPS-PUB-202 Section 2.1].
*/
type State w = [5][5][w]
/**
* The `String` type is a 5*5*w array of bits that also represents the
* state of a Keccak-p permutation [FIPS-PUB-202 Section 2.1].
*/
type String w = [5*5*w]
+103
View File
@@ -0,0 +1,103 @@
/*
* Description: This is a Cryptol spec for several helper functions
* used to define the SHA-3 derived hash functions `cSHAKE`, `KMAC`,
* `TupleHash`, and `ParallelHash` [NIST-SP-800-185].
*/
module algorithms::Keccak::derived where
/**
* For a natural number `i` ranging from 0 to 255, `enc8 i` is the
* byte encoding of `i`, with bit `0` being the low-order bit of the
* byte [NIST-SP-800-185 Section 2.2]. This function differs in that
* it will encode an entire sequence of bytes rather than just one.
*/
enc8 : {n} [n][8] -> [n][8]
enc8 = map reverse
/**
* This computes the number of bits needed to encode `x`
* [NIST-SP-800-185 Section 2.3.1].
*/
type EncodeSize x = 8 * (1 + max 1 (width x /^ 8))
/**
* This function encodes an integer `x` as a byte string in a way that
* can be unambiguously parsed from the end of the string by inserting
* the length of the byte string after the representation of `x`
* [NIST-SP-800-185 Section 2.3.1].
*/
right_encode : {x} (x < 2 ^^ 2040) => [EncodeSize x]
right_encode = join (𝑂 # 𝑂np1)
where
type n = max 1 (width x /^ 8)
𝑂 = enc8 (groupBy`{8, n} `x)
𝑂np1 = enc8 [fromInteger `n]
/** `right_encode` example [NIST-SP-800-185 Section 2.3.1] */
property right_encode_example =
right_encode`{0} == 0b00000000_10000000
/**
* This function encodes an integer `x` as a byte string in a way that
* can be unambiguously parsed from the beginning of the string by
* inserting the length of the byte string before the representation
* of `x` [NIST-SP-800-185 Section 2.3.1].
*/
left_encode : {x} (x < 2 ^^ 2040) => [EncodeSize x]
left_encode = join (𝑂0 # 𝑂)
where
type n = max 1 (width x /^ 8)
𝑂 = enc8 (groupBy`{8} `x)
𝑂0 = enc8 [fromInteger `n]
/** `left_encode` example [NIST-SP-800-185 Section 2.3.1] */
property left_encode_example =
left_encode`{0} == 0b10000000_00000000
/**
* This function encodes bit strings in a way that can be parsed
* unambiguously from the beginning of the string `S` [NIST-SP-800-185
* Section 2.3.2].
*/
encode_string :
{x} (x < 2 ^^ 2040)
=> [x] -> [EncodeSize x + x]
encode_string S = left_encode`{x} # S
/** `encode_string` example [NIST-SP-800-185 Section 2.3.2] */
property encode_string_example =
encode_string [] == 0b10000000_00000000
/**
* The `bytepad(X, w)` function prepends an encoding of the integer
* `w` to the input string `X`, then pads the result with zeros until
* it is a byte string whose length in bytes is a multiple of `w`. In
* general, `bytepad` is intended to be used on encoded strings. The
* byte string `bytepad(encode_string(S), w)` can be parsed
* unambiguously from its beginning, whereas bytepad does not provide
* unambiguous padding for all input strings [NIST-SP-800-185 Section
* 2.3.3].
*/
bytepad :
{w, a}
(w > 0, w < 2 ^^ 2040, fin a)
=> [a]
-> [ EncodeSize a // `left_encode`
+ a // `X`
+ (EncodeSize a + a) %^ (w * 8) // pad to next multiple of `w`
]
bytepad X = left_encode`{w} # X # zero
/**
* Let parameters `a` and `b` be non-negative integers that denote a
* specific position in a bit string `X`. Informally, the
* `substring(X, a, b)` function returns a substring from the bit
* string `X` containing the values at bit positions `a, a+1, ...,
* b−1`, inclusive. Note that all bit positions in the input and
* output strings are indexed from zero. Thus, the first bit in a
* string is in position `0`, and the last bit in an `n`-bit string is
* in position `n−1` [NIST-SP-800-185 Section 2.3.4].
*/
substring : {a, b, n} (fin a, a <= b, b <= n) => [n] -> [b - a]
substring X = X @@ [ a ..< b ]
+14
View File
@@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Keccak"
version = "0.1.0"
dependencies = [
"ffi_utils",
]
[[package]]
name = "ffi_utils"
version = "0.1.0"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "Keccak"
version = "0.1.0"
edition = "2021"
[lib]
name = "Keccak"
path = "src/lib.rs"
crate-type = ["lib", "cdylib"]
[dependencies]
ffi_utils = { path = "../../../shared/rust/ffi_utils" }
+270
View File
@@ -0,0 +1,270 @@
//! This is a Rust implementation of Keccak [FIPS-PUB-202] where `w
//! = 64`.
#![allow(non_snake_case)]
/// The `State` type is a 5-by-5-by-w array of bits that represents
/// the state for a `Keccak-p` permutation. The indices for `x`, `y`,
/// and `z` coordinates range from 0 to 4, 0 to 4, and 0 to `w-1`,
/// respectively [FIPS-PUB-202 Section 2.1].
type State = [[u64; 5]; 5];
/// # `theta` [FIPS-PUB-202 Algorithm 1]
///
/// 1. For all pairs `(x,z)` such that `0≤x<5` and `0≤z<w`, let
/// `C[x,z]=A[x,0,z] ⨁ A[x,1,z] ⨁ A[x,2,z] ⨁ A[x,3,z] ⨁ A[x,4,z]`.
///
/// 2. For all pairs `(x,z)` such that `0≤x<5` and `0≤z<w`, let
/// `D[x,z] = C[(x-1) mod 5, z] ⨁ C[(x+1) mod 5, (z-1) mod w]`.
///
/// 3. For all triples (x,y,z) such that `0≤x<5`, `0≤y<5`, and
/// `0≤z<w`, let `A'[x,y,z] = A[x,y,z] ⨁ D[x,z]`.
#[allow(clippy::needless_range_loop)]
fn theta(A: &mut State) {
let mut C: [u64; 5] = [0; 5];
let mut D: [u64; 5] = [0; 5];
for x in 0..5usize {
C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4];
}
for x in 0..5usize {
D[x] = C[(x + 5 - 1) % 5] ^ C[(x + 1) % 5].rotate_right(1);
}
for x in 0..5usize {
for y in 0..5usize {
A[x][y] ^= D[x];
}
}
}
/// # `rho` [FIPS-PUB-202 Algorithm 2]
///
/// 1. For all `z` such that `0≤z<w`, let `A'[0,0,z] = A[0,0,z]`.
/// 2. Let `(x,y) = (1,0)`.
/// 3. For `t` from 0 to 23:
/// a. for all `z` such that `0≤z<w`, let `A'[x,y,z]=A[x,y,(z-(t+1)(t+2)/2 mod w]`
/// b. let `(x,y) = (y,(2x+3y) mod 5)`.
///
/// Here we specify the offset table variant.
fn rho(A: &mut State) {
let offsets = [
[0, 36, 3, 105, 210],
[1, 300, 10, 45, 66],
[190, 6, 171, 15, 253],
[28, 55, 153, 21, 120],
[91, 276, 231, 136, 78],
];
for x in 0..5usize {
for y in 0..5usize {
A[x][y] = A[x][y].rotate_right(offsets[x][y]);
}
}
}
/// # `pi` [FIPS-PUB-202 Algorithm 3]
///
/// For all triples `(x,y,z)` such that `0≤x<5`, `0≤y<5`, `0≤z<w`, let
/// `A'[x,y,z] = A[(x+3y) mod 5, x, z]`
fn pi(A: &mut State) {
let A_p = *A;
for x in 0..5usize {
for y in 0..5usize {
A[x][y] = A_p[(x + 3 * y) % 5][x];
}
}
}
/// # `chi` [FIPS-PUB-202 Algorithm 4]
///
/// For all triples `(x,y,z)` such that `0≤x<5`, `0≤y<5`, `0≤z<w`, let
/// `A'[x,y,z] = A[x,y,z] ⨁ ((A[(x+1) mod 5, y, z] ⨁ 1) ⦁ A[(x+2) mod
/// 5, y, z])`.
fn chi(A: &mut State) {
let A_p = *A;
for x in 0..5usize {
for y in 0..5usize {
A[x][y] = A_p[x][y] ^ !A_p[(x + 1) % 5][y] & A_p[(x + 2) % 5][y];
}
}
}
/// # `rc` [FIPS-PUB-202 Algorithm 5]
///
/// The specification of `iota`'s round constant `rc`, specialized and
/// tabled given `w = 64`. The round constant bits for `ir` are
/// excited by RC[2^^j - 1] = rc(j+7*ir)
const RC: [u64; 24] = [
0x8000_0000_0000_0000,
0x4101_0000_0000_0000,
0x5101_0000_0000_0001,
0x0001_0001_0000_0001,
0xd101_0000_0000_0000,
0x8000_0001_0000_0000,
0x8101_0001_0000_0001,
0x9001_0000_0000_0001,
0x5100_0000_0000_0000,
0x1100_0000_0000_0000,
0x9001_0001_0000_0000,
0x5000_0001_0000_0000,
0xd101_0001_0000_0000,
0xd100_0000_0000_0001,
0x9101_0000_0000_0001,
0xc001_0000_0000_0001,
0x4001_0000_0000_0001,
0x0100_0000_0000_0001,
0x5001_0000_0000_0000,
0x5000_0001_0000_0001,
0x8101_0001_0000_0001,
0x0101_0000_0000_0001,
0x8000_0001_0000_0000,
0x1001_0001_0000_0001,
];
/// # `iota` [FIPS-PUB-202 Algorithm 6]
///
/// For all triples `(x,y,z)` such that `0≤x<5`, `0≤y<5`, `0≤z<w`, let
/// `A'[0,0,z]=A'[0,0,z] ⨁ RC[z]`. Otherwise, `A'[x,y,z] = A[x,y,z]`.
fn iota(A: &mut State, ir: usize) {
A[0][0] ^= RC[ir];
}
/// The Keccak Round Function, `Rnd`, is the transformation that
/// results from applying the step functions in order [FIPS-PUB-202
/// part of Algorithm 7].
fn Rnd(A: &mut State, ir: usize) {
theta(A);
rho(A);
pi(A);
chi(A);
iota(A, ir);
}
/// # `Keccak-p` [FIPS-PUB-202 Algorithm 7]
///
/// This function is specialized to `w = 64` and operates directly on
/// `State`s.
/// 1. For `ir` from `12+2l-nr` to `12+2l-1`, let `A = Rnd(A, ir)`.
/// 2. Return `A`.
fn Keccak_p(A: &mut State) {
let l: usize = 6;
let nr: usize = 24;
for ir in (12 + 2 * l - nr)..(12 + 2 * l) {
Rnd(A, ir);
}
}
/// FFI entrypoint for the `Keccak-p` function, specialized to `w = 64`.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[allow(clippy::needless_range_loop)]
#[export_name = "Keccak_p_FFI"]
pub extern "C" fn Keccak_p_FFI(A_in: *const State, A_out: *mut State) {
let A = unsafe { &mut *A_out };
for x in 0..5usize {
for y in 0..5usize {
A[x][y] = unsafe { *A_in }[x][y];
}
}
Keccak_p(A);
}
/// # `pad10s1` [CSF-0.1 Section 2.1.2].
///
/// Appends a single 1 bit followed by the minimum number of 0 bits
/// followed by a single 1 bit such that the length of the result is a
/// multiple of the block length.
fn pad10s1(rate_bytes: usize, mut message: Vec<u8>) -> Vec<u8> {
// Apply `pad10*1` padding to the message
let padding_length = rate_bytes - (message.len() % rate_bytes);
if padding_length == 1 {
message.push(0x81);
} else {
message.push(0x80); // Domain separator
message.resize(message.len() + padding_length - 2, 0x00);
message.push(0x01); // Final bit
}
message
}
/// # `absorb` [CFS-0.1 Section 2.4.1].
///
/// `r`-bit input message blocks are `XORed` into the first `r` bits
/// of the state, interleaved with applications of the function
/// `Keccak-p`.
fn absorb(rate_bytes: usize, padded_message: &[u8]) -> State {
// Initialize the state
let mut state: State = [[0u64; 5]; 5];
for block in padded_message.chunks(rate_bytes) {
// Expand block to state
for (i, chunk) in block.chunks(8).enumerate() {
let mut value = 0u64;
for (j, &byte) in chunk.iter().enumerate() {
value |= u64::from(byte) << (8 * (7 - j));
}
state[i % 5][i / 5] ^= value;
}
Keccak_p(&mut state);
}
state
}
/// # `squeeze` [CSF-0.1 Section 2.4.2]
///
/// Concatenation of the left-most `r` bits of compositions of
/// `Keccak-p`.
#[allow(clippy::needless_range_loop)]
fn squeeze(rate_bytes: usize, digest_size: usize, mut state: State) -> Vec<u8> {
assert!(
rate_bytes.is_multiple_of(8),
"Rate must be a multiple of 64."
);
let mut hash = Vec::new();
while hash.len() < digest_size {
for x in 0..5 {
#[allow(clippy::needless_range_loop)]
for y in 0..5 {
if hash.len() < digest_size && (x * 5 * 8 + y * 8) < rate_bytes {
hash.extend_from_slice(&state[y][x].to_be_bytes());
}
}
}
if hash.len() < digest_size {
Keccak_p(&mut state);
}
}
hash.truncate(digest_size);
hash
}
/// # `Keccak`
///
/// Implements the Keccak family of functions. This function uses the sponge
/// construction with the `Keccak_p` permutation, `pad10*1` padding, and a
/// specified rate `r` (1600 - capacity).
///
/// - `capacity`: The capacity of the sponge (in bits).
/// - `digest_size`: The desired hash length in bytes.
/// - `message`: The input message as a byte slice.
///
/// Returns the hash as a `Vec<u8>`.
///
/// # Panics
///
/// This function will panic is the capacity is out of range.
#[must_use]
pub fn Keccak(capacity: usize, digest_size: usize, message: &[u8]) -> Vec<u8> {
const B: usize = 1600; // State size in bits
let r = B - capacity; // Rate in bits
let rate_bytes = r / 8;
assert!(capacity < B, "Capacity must be less than 1600 bits.");
assert!(r >= 1, "Rate must be at least 1 bit.");
assert!(r.is_multiple_of(8), "Rate must be a multiple of 8.");
let padded_message = pad10s1(rate_bytes, Vec::from(message));
let state = absorb(rate_bytes, &padded_message);
squeeze(rate_bytes, digest_size, state)
}
+72
View File
@@ -0,0 +1,72 @@
from pathlib import Path
import unittest
from saw_client import *
from saw_client.crucible import *
from saw_client.mir import *
from saw_client.proofscript import *
from saw_client.mir_type import *
def ref_to_fresh(c : Contract, ty : MIRType, name : Optional[str] = None,
read_only : bool = False) -> Tuple[FreshVar, SetupVal]:
"""Add to ``Contract`` ``c`` an allocation of a reference of type ``ty`` initialized to an unknown fresh value.
If ``read_only == True`` then the allocated memory is immutable.
:returns A fresh variable bound to the reference's initial value and the newly allocated reference. (The fresh
variable will be assigned ``name`` if provided/available.)"""
var = c.fresh_var(ty, name)
ptr = c.alloc(ty, points_to = var, read_only = read_only)
return (var, ptr)
# fn F(A: &mut State) {
class mut_state_Contract(Contract):
def __init__(self, F : CryptolTerm):
super().__init__()
self.F = F
def specification(self):
(A, A_ref) = ref_to_fresh(self, array_ty(5, array_ty(5, u64)), "A")
self.execute_func(A_ref)
self.points_to(A_ref, cry_f("{self.F} {A}"))
self.returns(void)
def mut_state_verify(test, mod, F_rust, F_cryptol):
""" Verify a function of type `fn F(A: &mut State)`."""
result = mir_verify(mod, F_rust, mut_state_Contract(cry_f(F_cryptol)),
script=ProofScript([rme]))
test.assertIs(result.is_success(), True)
return result
class Keccak_p_Contract(Contract):
def specification(self):
(A, A_ref) = ref_to_fresh(self, array_ty(5, array_ty(5, u64)), "A")
self.execute_func(A_ref)
self.points_to(A_ref, cry_f("toState (Keccak_p`{{w=64, nr=24}} (toString {A}))"))
self.returns(void)
def Keccak_p_verify(test, mod, lemmas):
result = mir_verify(mod, "Keccak::Keccak_p", Keccak_p_Contract(), lemmas,
script=ProofScript([z3("θ ρ π χ".split())]))
test.assertIs(result.is_success(), True)
return result
class Keccak(unittest.TestCase):
def test_Cipher(self):
connect(reset_server=True)
if __name__ == "__main__": view(LogResults(verbose_failure=True))
basedir = Path(__file__).absolute().parents[1] # Get absolute path to `Cargo.toml`
StepMappings_crypath = basedir/"../StepMappings.cry"
StateConversions_crypath = basedir/"../StateConversions.cry"
mirpath = basedir/"linked-mir.json"
cryptol_load_file(str(StepMappings_crypath))
cryptol_load_file(str(StateConversions_crypath))
mod = mir_load_module(str(mirpath))
theta_result = mut_state_verify(self, mod, 'Keccak::theta', 'θ')
rho_result = mut_state_verify(self, mod, 'Keccak::rho', 'ρ')
pi_result = mut_state_verify(self, mod, 'Keccak::pi', 'π')
chi_result = mut_state_verify(self, mod, 'Keccak::chi', 'χ')
Keccak_p_result = Keccak_p_verify(self, mod, [theta_result, rho_result, pi_result, chi_result])
if __name__ == "__main__":
unittest.main()
+55
View File
@@ -0,0 +1,55 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/Keccak/Main.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the Cryptol specification for Keccak's main functions.
import "../Main.cry";
let verify_h2bTest : TopLevel Theorem =
proveit {{ h2bTest }} rme;
let verify_h2bInvertsb2h (n : Int) : TopLevel Theorem =
proveit {{ h2bInvertsb2h`{n} }} rme;
let verify_b2hInvertsh2b (n : Int, m : Int) : TopLevel Theorem =
proveit {{ b2hInvertsh2b`{n, m} }} rme;
let verify_bitReorderingIsAnInvolution (a : Int) : TopLevel Theorem =
proveit {{ bitReorderingIsAnInvolution`{a} }} rme;
let verify_bitReorderingInfIssue : TopLevel Theorem =
proveit {{ bitReorderingInfIssue }} rme;
/*
* Verify public properties of `algorithms::Keccak::Main`.
*/
let main : TopLevel [Theorem] = do {
h2bTest_lemma <- verify_h2bTest;
h2bInvertsb2h_lemmas <- for [ 0, 1, 10 ] verify_h2bInvertsb2h;
b2hInvertsh2b_lemmas <- for [ ( 0, 0 )
, ( 0, 1 )
, ( 1, 1 )
, ( 7, 1 )
, ( 8, 1 )
, ( 101, 33 )
] verify_b2hInvertsh2b;
bitReorderingIsAnInvolution_lemmas <-
for [ 0, 1, 31, 32, 33, 100 ] verify_bitReorderingIsAnInvolution;
bitReorderingInfIssue_lemma <- verify_bitReorderingInfIssue;
summarize_verification;
return
(foldl concat [ ]
[ [ h2bTest_lemma ]
, h2bInvertsb2h_lemmas
, b2hInvertsh2b_lemmas
, bitReorderingIsAnInvolution_lemmas
, [ bitReorderingInfIssue_lemma ]
]
);
};
@@ -0,0 +1,33 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/Keccak/StateConversions.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the Cryptol specification for Keccak's `State` conversion functions.
import "../StateConversions.cry";
let verify_toStatetoStringProp (w : Int) : TopLevel Theorem =
proveit {{ toStatetoStringProp`{w} }} rme;
let verify_toStringtoStateProp (w : Int) : TopLevel Theorem =
proveit {{ toStringtoStateProp`{w} }} rme;
/*
* Verify public properties of `algorithms::Keccak::StateConversions`.
*/
let main : TopLevel [Theorem] = do {
toStatetoStringProp_lemmas <- for [32, 64] verify_toStatetoStringProp;
toStringtoStateProp_lemmas <- for [32, 64] verify_toStatetoStringProp;
summarize_verification;
return
(foldl concat []
[ toStatetoStringProp_lemmas
, toStringtoStateProp_lemmas
]
);
};
+38
View File
@@ -0,0 +1,38 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/Keccak`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
include "Main.saw";
let Main_main = main;
include "derived.saw";
let derived_main = main;
include "StateConversions.saw";
let StateConversions_main = main;
/*
* Script to run on command line:
* `saw all.saw`
* Return:
* `TopLevel [Theorem]` verifying all lemmas used throughout `algorithms/Keccak`.
*/
let main : TopLevel [Theorem] = do {
Main_lemmas <- Main_main;
derived_lemmas <- derived_main;
StateConversions_lemmas <- StateConversions_main;
summarize_verification;
return
(foldl concat []
[ Main_lemmas
, derived_lemmas
, StateConversions_lemmas
]
);
};
+35
View File
@@ -0,0 +1,35 @@
/*
* This is a SAW script that guides SAW to prove the properties found
* in `algorithms/Keccak/derived.cry`.
*/
// Include common SAW utilities.
include "../../../shared/SAW/utils.saw";
// Import the Cryptol specification for Keccak's derived functions.
import "../derived.cry";
let verify_right_encode_example : TopLevel Theorem =
proveit {{ right_encode_example }} rme;
let verify_left_encode_example : TopLevel Theorem =
proveit {{ left_encode_example }} rme;
let verify_encode_string_example : TopLevel Theorem =
proveit {{ encode_string_example }} rme;
/*
* Verify public properties of `algorithms::Keccak::derived`.
*/
let main : TopLevel [Theorem] = do {
right_encode_example_lemma <- verify_right_encode_example;
left_encode_example_lemma <- verify_left_encode_example;
encode_string_example_lemma <- verify_encode_string_example;
summarize_verification;
return [ right_encode_example_lemma
, left_encode_example_lemma
, encode_string_example_lemma
];
};
+15
View File
@@ -0,0 +1,15 @@
/*
* Description: This is a Cryptol specification for the KWAE and KWAD
* functions [NIST-800-38F], specialized to AES-128.
*/
module algorithms::KeyWrapping::AES128 where
import algorithms::KeyWrapping::KW as KW
import algorithms::KeyWrapping::KWP as KWP
import algorithms::AES::Main (AES128)
KWAE KEK = KW::KWAE (AES128.Encrypt KEK)
KWAD KEK = KW::KWAD (AES128.Decrypt KEK)
KWPAE KEK = KWP::KWPAE (AES128.Encrypt KEK)
KWPAD KEK = KWP::KWPAD (AES128.Decrypt KEK)
+15
View File
@@ -0,0 +1,15 @@
/*
* Description: This is a Cryptol specification for the KWAE and KWAD
* functions [NIST-800-38F], specialized to AES-192.
*/
module algorithms::KeyWrapping::AES192 where
import algorithms::KeyWrapping::KW as KW
import algorithms::KeyWrapping::KWP as KWP
import algorithms::AES::Main (AES192)
KWAE KEK = KW::KWAE (AES192.Encrypt KEK)
KWAD KEK = KW::KWAD (AES192.Decrypt KEK)
KWPAE KEK = KWP::KWPAE (AES192.Encrypt KEK)
KWPAD KEK = KWP::KWPAD (AES192.Decrypt KEK)
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff. Show more