Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Use nix to run dilithium without installing and building #98

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ PQCsignKAT_Dilithium5.rsp
tvecs2
tvecs3
tvecs5
result
example/dilithium2*
example/dilithium3*
example/dilithium5*
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@

This repository contains the official reference implementation of the [Dilithium](https://www.pq-crystals.org/dilithium/) signature scheme, and an optimized implementation for x86 CPUs supporting the AVX2 instruction set. Dilithium is standardized as [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final).

## Running Dilithium

Dilithium can be used via Nix flakes. Make sure you have Nix [installed with flakes enabled](https://nixos.org/manual/nix/stable/installation/installing-binary.html#installation-with-flakes).

To try Dilithium directly on the command line without cloning this repository, run:
```sh
nix run github:pq-crystals/dilithium -- --help
```

The following commands create a signing key and a signature for a file "message.txt":
```sh
nix run github:pq-crystals/dilithium -- --keygen -v 2 -p public.key -s secret.key
nix run github:pq-crystals/dilithium -- --sign -v 2 -p public.key -s secret.key -i message.txt -S signature.bin
```

Use the `--verify` flag to verify a signature:
```sh
nix run github:pq-crystals/dilithium -- --verify -v 2 -p public.key -i message.txt -S signature.bin
```

## Build instructions

The implementations contain several test and benchmarking programs and a Makefile to facilitate compilation.
Expand Down Expand Up @@ -56,6 +76,60 @@ Please note that the reference implementation in `ref/` is not optimized for any

Our Dilithium implementations are contained in the [SUPERCOP](https://bench.cr.yp.to) benchmarking framework. See [here](http://bench.cr.yp.to/results-sign.html#amd64-kizomba) for current cycle counts on an Intel KabyLake CPU.

## Development using Nix

This repository provides a Nix flake with the following outputs:

- `packages.dilithium`: The main Dilithium CLI tools
- `packages.dilithium-lib`: The Dilithium shared libraries
- `devShells.default`: A development environment with all dependencies

### Flake Usage Examples

To use Dilithium as a dependency in your own flake:
```nix
{
inputs.dilithium.url = "github:pq-crystals/dilithium";

outputs = { self, nixpkgs, dilithium }: {
# Use the library
packages.default = pkgs.stdenv.mkDerivation {
buildInputs = [ dilithium.packages.${system}.dilithium-lib ];
# ...
};
};
}
```

The following command will enter the development shell and provide the necessary environment variables (CFLAGS and LDFLAGS required to link against Dilithium and OpenSSL):
```sh
nix develop github:pq-crystals/dilithium
```

The development shell provides:
- All build dependencies
- Dilithium libraries and headers
- Proper environment variables (CFLAGS, LDFLAGS, etc.)

To compile and run tests described above, the same make commands work in either directory:
```sh
make
```

This produces the same test executables:
```sh
test/test_dilithium$ALG
test/test_vectors$ALG
```
where `$ALG` ranges over the parameter sets 2, 3, and 5.

For performance benchmarking, you can additionally run and execute the speed tests:
```sh
make speed
test/test_speed$ALG
```
These programs measure performance using the CPU's Time Step Counter (TSC) by default, reporting median and average cycle counts over 10000 executions of key operations.

## Randomized signing

By default our code implements Dilithium's hedged signing mode. To change this to the deterministic signing mode, undefine the `DILITHIUM_RANDOMIZED_SIGNING` preprocessor macro at compilation by either commenting the line
Expand Down
60 changes: 60 additions & 0 deletions example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
CC=gcc
USE_AVX2 ?= $(shell if [ `uname -m` = "x86_64" ] && grep -q avx2 /proc/cpuinfo; then echo "1"; else echo "0"; fi)

CFLAGS=-I${DILITHIUM_INCLUDE_DIR} -Wall -Wextra
CFLAGS += $(shell if grep -q avx2 /proc/cpuinfo; then echo "-DDILITHIUM_USE_AVX2 -mavx2"; fi)

LDFLAGS ?= -L${DILITHIUM_LIB_DIR}
IMPLEMENTATION=$(shell if [ "$(USE_AVX2)" = "1" ]; then echo "avx2"; else echo "ref"; fi)

# Add all Dilithium versions to linker flags
ifeq ($(USE_AVX2),1)
LDFLAGS += -lpqcrystals_dilithium2_avx2 -lpqcrystals_dilithium3_avx2 -lpqcrystals_dilithium5_avx2 \
-lpqcrystals_fips202_avx2 -lpqcrystals_fips202x4_avx2
else
LDFLAGS += -lpqcrystals_dilithium2_ref -lpqcrystals_dilithium3_ref -lpqcrystals_dilithium5_ref -lpqcrystals_fips202_ref
endif
LDFLAGS += -lcrypto


all: dilithium2-keygen dilithium2-sign dilithium2-verify \
dilithium3-keygen dilithium3-sign dilithium3-verify \
dilithium5-keygen dilithium5-sign dilithium5-verify

dilithium2-keygen: keygen.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=2 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium2-sign: sign.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=2 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium2-verify: verify.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=2 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium3-keygen: keygen.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=3 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium3-sign: sign.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=3 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium3-verify: verify.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=3 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium5-keygen: keygen.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=5 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium5-sign: sign.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=5 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

dilithium5-verify: verify.c base64.c
$(CC) $(CFLAGS) -DDILITHIUM_MODE=5 -o $@ $< ../$(IMPLEMENTATION)/randombytes.c base64.c $(LDFLAGS)

test: all
./runtest.sh

clean:
rm -f dilithium2-keygen dilithium2-sign dilithium2-verify \
dilithium3-keygen dilithium3-sign dilithium3-verify \
dilithium5-keygen dilithium5-sign dilithium5-verify \
message*.txt public*.key secret*.key signature*.bin

.PHONY: all test clean
114 changes: 114 additions & 0 deletions example/base64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include "base64.h"
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>

#define MAX_LINE_LENGTH 1024
#define MAX_BASE64_LENGTH 8192

// Add at the top, after includes
static int debug_output = 0;

int read_from_base64_file(const char* filepath, const char* header, const char* footer,
uint8_t* output, size_t expected_size) {
FILE* file = fopen(filepath, "r");
if (!file) {
fprintf(stderr, "Failed to open file\n");
return -1;
}

char line[MAX_LINE_LENGTH];
if (!fgets(line, sizeof(line), file)) {
fprintf(stderr, "Failed to read header\n");
fclose(file);
return -3;
}
// Verify header matches expected
if (strncmp(line, header, strlen(header)) != 0) {
fprintf(stderr, "Invalid header\n");
fclose(file);
return -2;
}

// Read base64 content into a temporary buffer
char base64_data[MAX_BASE64_LENGTH] = {0};
char temp_line[MAX_LINE_LENGTH];
size_t total_len = 0;

// Read all lines until footer
while (fgets(temp_line, sizeof(temp_line), file)) {
if (strstr(temp_line, footer) != NULL) {
break;
}
// Remove trailing newline if present
size_t line_len = strlen(temp_line);
if (line_len > 0 && temp_line[line_len-1] == '\n') {
temp_line[line_len-1] = '\0';
line_len--;
}
// Append to base64_data
if (total_len + line_len < sizeof(base64_data)) {
strcat(base64_data, temp_line);
total_len += line_len;
} else {
fprintf(stderr, "Base64 buffer overflow! Need more than %zu bytes\n", sizeof(base64_data));
fclose(file);
return -5;
}
}

// Modify debug output to use debug flag
if (debug_output) {
fprintf(stderr, "Base64 data length: %zu\n", strlen(base64_data));
}

// Setup BIO chain for base64 decoding
BIO* bio_mem = BIO_new_mem_buf(base64_data, -1);
BIO* bio_b64 = BIO_new(BIO_f_base64());
BIO* bio_chain = BIO_push(bio_b64, bio_mem);

// Disable newline checking
BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);

// Read and decode
size_t total_bytes = BIO_read(bio_chain, output, expected_size);

// Modify debug output to use debug flag
if (debug_output) {
fprintf(stderr, "Decoded %zu bytes, expected %zu bytes\n", total_bytes, expected_size);
}

fclose(file);
BIO_free_all(bio_chain);

return (total_bytes == expected_size) ? 0 : -4;
}

int write_to_base64_file(const char* filepath, const char* header, const char* footer,
const uint8_t* data, size_t data_len) {
FILE* file = fopen(filepath, "w");
if (!file) {
return -1;
}

// Setup BIO chain for base64 encoding
BIO* bio_b64 = BIO_new(BIO_f_base64());
BIO* bio_mem = BIO_new(BIO_s_mem());
BIO* bio_chain = BIO_push(bio_b64, bio_mem);
BUF_MEM* buffer_ptr;

// Write data and flush
BIO_write(bio_chain, data, data_len);
BIO_flush(bio_chain);
BIO_get_mem_ptr(bio_chain, &buffer_ptr);

// Write to file with headers
fprintf(file, "%s\n", header);
fprintf(file, "%.*s", (int)buffer_ptr->length, buffer_ptr->data);
fprintf(file, "%s\n", footer);

BIO_free_all(bio_chain);
fclose(file);
return 0;
}
14 changes: 14 additions & 0 deletions example/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef BASE64_H
#define BASE64_H

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int read_from_base64_file(const char* filepath, const char* header, const char* footer,
uint8_t* output, size_t expected_size);

int write_to_base64_file(const char* filepath, const char* header, const char* footer,
const uint8_t* data, size_t data_len);

#endif // BASE64_H
82 changes: 82 additions & 0 deletions example/dilithium
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash

# Function to print usage
print_usage() {
echo "Usage:"
echo " Generate keypair: $0 --keygen -v VERSION -p pubkey -s seckey"
echo " Sign message: $0 --sign -v VERSION -s seckey -i message -o signature"
echo " Verify signature: $0 --verify -v VERSION -p pubkey -i message -S signature"
echo ""
echo "Options:"
echo " -v VERSION Dilithium version (2, 3, or 5)"
echo " -p pubkey Public key file"
echo " -s seckey Secret key file"
echo " -i message Input message file"
echo " -o signature Output signature file"
echo " -S signature Signature file (for verify)"
}

# Get the action first
ACTION=""
case "$1" in
--keygen) ACTION="keygen" ;;
--sign) ACTION="sign" ;;
--verify) ACTION="verify" ;;
*)
echo "Error: Must specify --keygen, --sign, or --verify" >&2
print_usage
exit 1
;;
esac
shift # Remove the action argument

# Parse remaining arguments
VERSION=""
while getopts ":v:p:s:i:o:S:" opt; do
case $opt in
v) VERSION="$OPTARG" ;;
p|s|i|o|S) ;; # Valid options, handled later
\?)
echo "Invalid option: -$OPTARG" >&2
print_usage
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
print_usage
exit 1
;;
esac
done

# Validate version
if [[ ! "$VERSION" =~ ^[235]$ ]]; then
echo "Error: Version must be 2, 3, or 5" >&2
print_usage
exit 1
fi

export LD_LIBRARY_PATH=LD_LIBRARY_PATH

# Get the binary path
BINARY_DIR="$(dirname "$0")"
BINARY="$BINARY_DIR/dilithium${VERSION}-${ACTION}"

if [ ! -x "$BINARY" ]; then
echo "Error: Binary not found at $BINARY" >&2
exit 1
fi

# Reset argument parsing to pass all arguments except -v and the action
OPTIND=1
args=()
while [ "$#" -gt 0 ]; do
if [ "$1" = "-v" ]; then
shift 2
else
args+=("$1")
shift
fi
done

exec "$BINARY" "${args[@]}"
Loading