diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..18039bc157 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,59 @@ +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +name: Bug report +description: Create a report to help us improve +labels: [bug] +body: + - type: markdown + attributes: + value: | + **Security reports** + + DO NOT report security issues through GitHub issues - instead use GitHub's [private vulnerability reporting](https://github.com/pq-code-package/mldsa-native/security). + - type: input + id: platform + attributes: + label: Platform + placeholder: e.g., x86_64 Linux + validations: + required: true + - type: input + id: compiler + attributes: + label: Compiler + placeholder: e.g., gcc 13.2.0 (installed through nix) + validations: + required: true + - type: input + id: version + attributes: + label: mldsa-native version + placeholder: e.g., v1.0.0 or main-branch + validations: + required: true + - type: textarea + id: steps + attributes: + label: What did you do? + description: Steps to reproduce the behavior. + render: shell + validations: + required: true + - type: textarea + id: expected + attributes: + label: What did you expect to see? + validations: + required: true + - type: textarea + id: actual + attributes: + label: What did you actually see? + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..149bcb96cc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,43 @@ +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +name: Feature request +description: Suggest an idea for this project +labels: [enhancement] +body: + - type: checkboxes + id: platform + attributes: + label: Platform + description: Which platform does this concern? + options: + - label: platform independent + - label: aarch64 + - label: x86_64 + - label: rv32 + - label: other + - type: checkboxes + id: category + attributes: + label: Issue category + description: Which part(s) of mldsa-native does this issue concern? + options: + - label: "core crypto: ML-DSA" + - label: "core crypto: FIPS202" + - label: documentation + - label: integration + - label: verification + - label: CI + - label: testing + - type: textarea + id: feature + attributes: + label: Describe the feature + description: What would you like mldsa-native to support, and why? + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93af0dafee..b537949ce2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -287,39 +287,42 @@ jobs: darwin: True opt: all examples: true - # CPU flags are not correctly passed to the zig assembler - # https://github.com/ziglang/zig/issues/23576 - # We therefore only test the C backend - # - # We omit all examples since there is currently no way to run - # only those examples not involving native code. - # - # zig 0.16 still appears affected on Linux (CPU-feature - # predefines look to be dropped in assembler-with-cpp mode). + # zig: + # - Up to 0.16, CPU feature flags are not passed to the assembler + # (https://github.com/ziglang/zig/issues/23576). We therefore + # only test the C backend and exclude the native examples. + # - 0.17 fixes this, so we test the native backends as well. + # - The ABI check is not supported with zig: on AArch64 it passes + # -march=armv8-a+simd and -march=armv8.4-a+sha3, which zig rejects + # since it parses -march as a CPU name. - family: zig version: "0.10" shell: zig0_10 darwin: False opt: no_opt - examples: False - - family: zig - version: "0.14" - shell: zig0_14 - darwin: True - opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.15" shell: zig0_15 darwin: True opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.16" shell: zig0_16 darwin: True opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" + - family: zig + version: "0.17" + shell: zig0_17 + darwin: True + opt: all + examples: True + extra_args: "--no-abicheck" runs-on: ${{ matrix.target.runner }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -338,6 +341,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "${{ matrix.cflags }}" - name: native build+functest (C90) if: ${{ matrix.compiler.darwin || matrix.target.runner != 'macos-latest' }} @@ -352,6 +356,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c90 ${{ matrix.cflags }}" - name: native build+functest (C99) if: ${{ matrix.compiler.darwin || matrix.target.runner != 'macos-latest' }} @@ -366,6 +371,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c99 ${{ matrix.cflags }}" - name: native build+functest (C11) if: ${{ matrix.compiler.darwin || matrix.target.runner != 'macos-latest' }} @@ -380,6 +386,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c11 ${{ matrix.cflags }}" - name: native build+functest (C17) if: ${{ (matrix.compiler.darwin || matrix.target.runner != 'macos-latest') && @@ -395,6 +402,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c17 ${{ matrix.cflags }}" - name: native build+functest (C23) if: ${{ (matrix.compiler.darwin || matrix.target.runner != 'macos-latest') && @@ -410,6 +418,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c23 ${{ matrix.cflags }}" stack_analysis: name: Stack analysis (${{ matrix.target.name }}, ${{ matrix.cflags }}) diff --git a/.github/workflows/legacy-compilers.yml b/.github/workflows/legacy-compilers.yml index 3298cbbf14..f394d6cce6 100644 --- a/.github/workflows/legacy-compilers.yml +++ b/.github/workflows/legacy-compilers.yml @@ -189,46 +189,62 @@ jobs: shell: clang23 opt: all examples: true - # CPU flags are not correctly passed to the zig assembler - # https://github.com/ziglang/zig/issues/23576 - # We therefore only test the C backend, which is why opt is set to - # no_opt and examples are omitted (there is currently no way to run - # only those examples not involving native code). + # zig: + # - Up to 0.16, CPU feature flags are not passed to the assembler + # (https://github.com/ziglang/zig/issues/23576). We therefore + # only test the C backend and exclude the native examples. + # - 0.17 fixes this, so we test the native backends as well. + # - The ABI check is not supported with zig: on AArch64 it passes + # -march=armv8-a+simd and -march=armv8.4-a+sha3, which zig rejects + # since it parses -march as a CPU name. - family: zig version: "0.10" shell: zig0_10 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.11" shell: zig0_11 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.12" shell: zig0_12 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.13" shell: zig0_13 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.14" shell: zig0_14 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.15" shell: zig0_15 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.16" shell: zig0_16 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" + - family: zig + version: "0.17" + shell: zig0_17 + opt: all + examples: True + extra_args: "--no-abicheck" runs-on: ${{ matrix.target.runner }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -246,6 +262,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "${{ matrix.cflags }}" - name: native build+functest (C90) uses: ./.github/actions/multi-functest @@ -259,6 +276,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c90 ${{ matrix.cflags }}" - name: native build+functest (C99) uses: ./.github/actions/multi-functest @@ -272,6 +290,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c99 ${{ matrix.cflags }}" - name: native build+functest (C11) uses: ./.github/actions/multi-functest @@ -285,6 +304,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c11 ${{ matrix.cflags }}" - name: native build+functest (C17) if: ${{ matrix.compiler.family == 'zig' || (matrix.compiler.family == 'gcc' && matrix.compiler.version >= 8) || (matrix.compiler.family == 'clang' && matrix.compiler.version >= 7) }} @@ -299,6 +319,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c17 ${{ matrix.cflags }}" - name: native build+functest (C23) if: ${{ (matrix.compiler.family == 'zig' && matrix.compiler.version >= 0.14) || (matrix.compiler.family == 'gcc' && matrix.compiler.version >= 14) || (matrix.compiler.family == 'clang' && matrix.compiler.version >= 18) }} @@ -313,6 +334,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c23 ${{ matrix.cflags }}" compiler_tests_macos: @@ -382,27 +404,38 @@ jobs: version: "0.12" shell: zig0_12 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.13" shell: zig0_13 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.14" shell: zig0_14 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.15" shell: zig0_15 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" - family: zig version: "0.16" shell: zig0_16 opt: no_opt - examples: False + examples: True + extra_args: "--exclude-example monolithic_build_native --exclude-example monolithic_build_multilevel_native --exclude-example multilevel_build_native" + - family: zig + version: "0.17" + shell: zig0_17 + opt: all + examples: True + extra_args: "--no-abicheck" runs-on: macos-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -420,6 +453,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "${{ matrix.cflags }}" - name: native build+functest (C90) uses: ./.github/actions/multi-functest @@ -433,6 +467,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c90 ${{ matrix.cflags }}" - name: native build+functest (C99) uses: ./.github/actions/multi-functest @@ -446,6 +481,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c99 ${{ matrix.cflags }}" - name: native build+functest (C11) uses: ./.github/actions/multi-functest @@ -459,6 +495,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c11 ${{ matrix.cflags }}" - name: native build+functest (C17) if: ${{ matrix.compiler.family == 'zig' || (matrix.compiler.family == 'gcc' && matrix.compiler.version >= 8) || (matrix.compiler.family == 'clang' && matrix.compiler.version >= 7) }} @@ -473,6 +510,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c17 ${{ matrix.cflags }}" - name: native build+functest (C23) if: ${{ (matrix.compiler.family == 'zig' && matrix.compiler.version >= 0.14) || (matrix.compiler.family == 'gcc' && matrix.compiler.version >= 14) || (matrix.compiler.family == 'clang' && matrix.compiler.version >= 18) }} @@ -487,6 +525,7 @@ jobs: examples: ${{ matrix.compiler.examples }} opt: ${{ matrix.compiler.opt }} nix-shell: ${{ matrix.compiler.shell }} + extra_args: ${{ matrix.compiler.extra_args }} cflags: "-std=c23 ${{ matrix.cflags }}" compiler_tests_windows_mingw: diff --git a/API-CONVENTIONS.md b/API-CONVENTIONS.md index 61b379d0e3..24de631435 100644 --- a/API-CONVENTIONS.md +++ b/API-CONVENTIONS.md @@ -18,6 +18,8 @@ All pointers to non-empty buffers are assumed to be valid and non-NULL, and ever The exception is a pointer paired with a length: it may be NULL when that length is `0` — for example, the message `m` when `mlen == 0`, or `ctx` when `ctxlen == 0`. Such cases are called out in the per-function documentation. +Buffers passed to the same function must not overlap. + ## Output buffers on error When a function returns an error, each caller-owned output buffer is left either unchanged or fully zeroized. An output buffer is never left holding partially computed or otherwise stale data that could be mistaken for a valid result. diff --git a/README.md b/README.md index f916a13cc7..bb37e0b95e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ mldsa-native is used in ## Formal Verification -We use the [C Bounded Model Checker (CBMC)](https://github.com/diffblue/cbmc) to prove absence of various classes of undefined behaviour in C, including out of bounds memory accesses and integer overflows. The proofs cover all C code in [mldsa/src/*](mldsa) and [mldsa/src/fips202/*](mldsa/src/fips202) involved in running mldsa-native with its C backend. See [proofs/cbmc](proofs/cbmc) for details. +We use the [C Bounded Model Checker (CBMC)](https://github.com/diffblue/cbmc) to prove absence of various classes of undefined behaviour in C, including out of bounds memory accesses and integer overflows. The proofs cover all C code in [mldsa/src/*](mldsa) and [mldsa/src/fips202/*](mldsa/src/fips202), except for the Armv8.1-M backend and the debug code. See [proofs/cbmc](proofs/cbmc) for details. All AArch64 and x86_64 assembly, and the RV32IM arithmetic backend, are proved functionally correct and memory-safe at the object-code level using @@ -81,7 +81,8 @@ pointwise-multiplication assembly; see its [README](proofs/isabelle/neon_ntt/REA All assembly in mldsa-native is constant-time in the sense that it is free of secret-dependent control flow, memory access, and instructions that are commonly variable-time, thwarting most timing side channels. C code is hardened against compiler-introduced -timing side channels through suitable barriers and constant-time patterns. +timing side channels through suitable barriers and constant-time patterns. The C code assumes constant-time 32x32->32 and 32x32->64 multiplication, +which some targets do not provide, e.g., cores without a hardware multiplier or with early-terminating multiply instructions. Absence of secret-dependent branches, memory-access patterns and variable-latency instructions is also tested using `valgrind` with various combinations of compilers and compilation options. diff --git a/flake.nix b/flake.nix index 8c52dadf22..95703d7595 100644 --- a/flake.nix +++ b/flake.nix @@ -208,6 +208,7 @@ devShells.zig0_14 = util.mkShellWithCC' (zigWrapCC pkgs.zig_0_14); devShells.zig0_15 = util.mkShellWithCC' (zigWrapCC pkgs.zig_0_15); devShells.zig0_16 = util.mkShellWithCC' (zigWrapCC pkgs.zig_0_16); + devShells.zig0_17 = util.mkShellWithCC' (zigWrapCC pkgs-unstable.zig_0_17); devShells.gcc13 = util.mkShellWithCC' pkgs.gcc13; devShells.gcc14 = util.mkShellWithCC' pkgs.gcc14; diff --git a/mldsa/mldsa_native.h b/mldsa/mldsa_native.h index d42095c87c..837078b153 100644 --- a/mldsa/mldsa_native.h +++ b/mldsa/mldsa_native.h @@ -26,7 +26,9 @@ * # API conventions * * Conventions shared by all functions below (return values, pointer validity, - * output buffers on error) are documented in API-CONVENTIONS.md. + * buffer overlap, output buffers on error) are documented in + * API-CONVENTIONS.md. In particular, buffers passed to the same function must + * not overlap. * * # Multi-level builds * diff --git a/test/wycheproof/wycheproof_client.py b/test/wycheproof/wycheproof_client.py index 6b2a9e486e..904f06cac6 100755 --- a/test/wycheproof/wycheproof_client.py +++ b/test/wycheproof/wycheproof_client.py @@ -20,8 +20,8 @@ exec_prefix = os.environ.get("EXEC_WRAPPER", "") exec_prefix = exec_prefix.split(" ") if exec_prefix != "" else [] -# Pinned to a specific commit (2026-08-18). -WYCHEPROOF_COMMIT = "dac1dd4729fd1f8dd9e1e9f3dce51d783da6c166" +# Pinned to a specific commit (2026-10-12). +WYCHEPROOF_COMMIT = "7a325932231bd7a724c791c8b0d65ea4d6d2f435" WYCHEPROOF_BASE_URL = ( "https://api.github.com/repos/C2SP/wycheproof/contents/testvectors_v1" )