From d011ca543b469d13bc967458b6fdc59a7b0f061d Mon Sep 17 00:00:00 2001 From: Bruce Date: Tue, 13 Feb 2024 15:19:21 -0500 Subject: [PATCH] Add Marvin toolkit container (#400) This contains a Docker image which can be used for testing for the Marvin Attack: https://people.redhat.com/~hkario/marvin/ --- marvin-toolkit/Cargo.toml | 13 ++++ marvin-toolkit/Dockerfile | 23 ++++++ marvin-toolkit/README.md | 66 +++++++++++++++++ marvin-toolkit/entrypoint.sh | 133 +++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 marvin-toolkit/Cargo.toml create mode 100644 marvin-toolkit/Dockerfile create mode 100644 marvin-toolkit/README.md create mode 100644 marvin-toolkit/entrypoint.sh diff --git a/marvin-toolkit/Cargo.toml b/marvin-toolkit/Cargo.toml new file mode 100644 index 00000000..7ad3142b --- /dev/null +++ b/marvin-toolkit/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust-crypto" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +clap = { version = "4", features = ["derive"] } +rsa = "0.9" + +[patch.crates-io] +rsa = { git = "https://github.com/RustCrypto/RSA", branch = "const-crypto-biguint" } +crypto-bigint = { git = "https://github.com/RustCrypto/crypto-bigint", branch = "master" } \ No newline at end of file diff --git a/marvin-toolkit/Dockerfile b/marvin-toolkit/Dockerfile new file mode 100644 index 00000000..afabec4f --- /dev/null +++ b/marvin-toolkit/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.12-bookworm + +# Create non-root user +RUN adduser rustcrypto --disabled-password --gecos "" + +USER rustcrypto + +# Install Rust +RUN curl -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/home/rustcrypto/.cargo/bin:${PATH}" + +# Clone the marvin-toolkit repository +RUN cd $HOME \ + && git clone https://github.com/tomato42/marvin-toolkit.git \ + && cd marvin-toolkit \ + && chmod +x *.sh \ + && ./step0.sh +WORKDIR "/home/rustcrypto/marvin-toolkit" + +# Generating private keys, ciphertexts, building RustCrypto/RSA, should all be done at runtime +COPY --chmod=777 entrypoint.sh ./entrypoint.sh + +ENTRYPOINT ["./entrypoint.sh"] \ No newline at end of file diff --git a/marvin-toolkit/README.md b/marvin-toolkit/README.md new file mode 100644 index 00000000..aa54b3d7 --- /dev/null +++ b/marvin-toolkit/README.md @@ -0,0 +1,66 @@ +# Marvin tool-kit integration +This document describes the procedure for replicating the analysis for the Marvin attack. This analysis is best done on a container for reproducibility. + +**TL;DR**: +```bash +# Build the image +docker build -t marvin:latest . + +# Create the output directory and allow container to write to it +mkdir -p outputs +chmod a+rw outputs + +# Run the analysis +docker run -d --rm \ + --name marvin \ + -v $(pwd)/outputs:/home/rustcrypto/marvin-toolkit/outputs \ + -v $(pwd)/Cargo.toml:/home/rustcrypto/marvin-toolkit/example/rust-crypto/Cargo.toml \ + marvin:latest + +# Use "docker logs -f marvin" to read live output + +# Read the output +cat outputs/results/report.txt +``` + +## Adjusting analysis parameters +For more help on the options pass in the `-h` flag in the `docker run` command: + +``` +docker run ... marvin:latest -h +``` + +There are two main parameters of the analysis: RSA key size and the number of repetitions during ciphertext generation. + +RSA key size is specified through `-s <1024|2048|4096>`. The number of repetition is specified through `-n `. A larger repetition number will increase the confidence of the analysis, but will make the analysis take longer. The default key size is 2048 and the default repetition count is 100,000. + +```bash +# Run analysis for RSA 4096 with 1 million repetition +docker run -d --rm \ + --name marvin \ + marvin:latest -s 4096 -n 1000000 +``` + +## Extracting keys, ciphertexts, and analysis results (WIP) +After the analysis is done, the generate keys, ciphertexts, and the analysis outputs are all copied into the directory `/home/rustcrypto/marvin-toolkit/outputs`. To extract and preserve these artifacts, mount a volume into this directory, such as using a bind mount: + +```bash +mkdir -p outputs +chmod a+rw outputs + +# Mount +docker run -d --rm --name "marvin" \ + -v $(pwd)/outputs:/home/rustcrypto/marvin-toolkit/outputs \ + marvin:latest +``` + +## Compile test harness with custom `Cargo.toml` +The test harness is compiled at container run-time, so a custom `Cargo.toml` can be passed into the container at runtime to compile the test harness using custom versions of `RustCrypto/RSA` and/or `RustCrypto/crypto-bigint`: + +```bash +docker run -d --rm --name "marvin" \ + -v $(pwd)/Cargo.toml:/home/rustcrypto/marvin-toolkit/example/rust-crypto/Cargo.toml \ + marvin:latest +``` + +If no `Cargo.toml` is specified, the default one will use `rsa = 0.9` \ No newline at end of file diff --git a/marvin-toolkit/entrypoint.sh b/marvin-toolkit/entrypoint.sh new file mode 100644 index 00000000..3f71564e --- /dev/null +++ b/marvin-toolkit/entrypoint.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# Build the test harness +cd example/rust-crypto +cargo update --quiet +cargo build --profile release --quiet +cd ~/marvin-toolkit + +# Parse CLI inputs to $size and $repeat +size=2048 +repeat=100000 + +# Function to display help message +display_help() { + echo "Usage: $0 [-s SIZE] [-n NUMBER] [-h]" + echo " -s SIZE Set the RSA key size (1024, 2048, or 4096; default: 2048)" + echo " -n NUMBER Set the repeat number (integer; default: 100000)" + echo " -h Display this help message" +} + +# Parse command-line arguments using getopts +while getopts ":s:n:h" opt; do + case $opt in + s) + size=$OPTARG + if [[ ! "$size" =~ ^(1024|2048|4096)$ ]]; then + echo "Error: Invalid size. Please choose 1024, 2048, or 4096." + exit 1 + fi + ;; + n) + repeat=$OPTARG + if ! [[ "$repeat" =~ ^[0-9]+$ ]]; then + echo "Error: Invalid number. Please specify a valid integer." + exit 1 + fi + ;; + h) + display_help + exit 0 + ;; + \?) + echo "Error: Invalid option -$OPTARG" + display_help + exit 1 + ;; + :) + echo "Error: Option -$OPTARG requires an argument." + display_help + exit 1 + ;; + esac +done +size_bytes=$(($size / 8)) + +# Step 1: Generate key pairs +. ./certgen/certgen/lib.sh +name="rsa${size}" +tmp_file="$(mktemp)" +if ! x509KeyGen -s $size $name &> "$tmp_file"; then + echo "ERROR $size bit key generation failed" >&2 + cat "$tmp_file" >&2 + exit 1 +fi +if ! x509SelfSign $name &> "$tmp_file"; then + echo "ERROR: $size bit key self-signing failed" >&2 + cat "$tmp_file" >&2 + exit 1 +fi + +echo "RSA $size bit private key in old OpenSSL PEM format is in" $(x509Key $name) +echo "RSA $size bit private key in old OpenSSL DER format is in" $(x509Key --der $name) +echo "RSA $size bit private key in PKCS#8 PEM format is in" $(x509Key --pkcs8 $name) +echo "RSA $size bit private key in PKCS#8 DER format is in" $(x509Key --der --pkcs8 $name) +echo "RSA $size bit private key in PKCS#12 format is in" $(x509Key --with-cert --pkcs12 $name) +echo "RSA $size bit self-signed certificate is in" $(x509Cert $name) +echo + +# Generate ciphertexts +case $size in + 1024) + PYTHONPATH=tlsfuzzer ./marvin-venv/bin/python ./step2.py \ + -c rsa1024/cert.pem -o rsa1024_repeat \ + --repeat ${repeat} --verbose \ + no_structure no_padding=48 signature_padding=8 \ + valid_repeated_byte_payload="118 0xff" \ + valid_repeated_byte_payload="118 0x01" \ + valid=48 header_only \ + no_header_with_payload=48 zero_byte_in_padding="48 4" \ + valid=0 valid=118 + ;; + 2048) + PYTHONPATH=tlsfuzzer ./marvin-venv/bin/python ./step2.py \ + -c rsa2048/cert.pem -o rsa2048_repeat \ + --repeat ${repeat} --verbose \ + no_structure no_padding=48 signature_padding=8 \ + valid_repeated_byte_payload="246 0xff" \ + valid_repeated_byte_payload="246 0x01" \ + valid=48 header_only \ + no_header_with_payload=48 zero_byte_in_padding="48 4" \ + valid=0 valid=192 valid=246 + ;; + 4096) + PYTHONPATH=tlsfuzzer ./marvin-venv/bin/python ./step2.py \ + -c rsa4096/cert.pem -o rsa4096_repeat \ + --repeat ${repeat} --verbose \ + no_structure no_padding=48 signature_padding=8 \ + valid_repeated_byte_payload="502 0xff" \ + valid_repeated_byte_payload="502 0x01" \ + valid=48 header_only \ + no_header_with_payload=48 zero_byte_in_padding="48 4" \ + valid=0 valid=192 valid=502 + ;; +esac + +# Run decryptions and analyze data +echo "Starting decryption" +./example/rust-crypto/target/release/rust-crypto \ + -i rsa${size}_repeat/ciphers.bin \ + -o rsa${size}_repeat/raw_times.csv -k rsa${size}/pkcs8.pem -n $size_bytes +echo "Decryptions finished" +PYTHONPATH=tlsfuzzer marvin-venv/bin/python3 tlsfuzzer/tlsfuzzer/extract.py \ +-l rsa${size}_repeat/log.csv --raw-times rsa${size}_repeat/raw_times.csv \ +-o rsa${size}_repeat/ \ +--clock-frequency 1000 +PYTHONPATH=tlsfuzzer marvin-venv/bin/python3 tlsfuzzer/tlsfuzzer/analysis.py \ +-o rsa${size}_repeat/ --verbose + +# Copy over the keys and the results, if the results directory exists +if [[ -d ~/marvin-toolkit/outputs ]]; then + cp -r rsa${size} ~/marvin-toolkit/outputs/keys + cp -r rsa${size}_repeat ~/marvin-toolkit/outputs/results +fi \ No newline at end of file