Skip to content

Add Windows/MSVC port - #464

Open
ColtonWilley wants to merge 2 commits into
masterfrom
windows-msvc-port-v2
Open

Add Windows/MSVC port#464
ColtonWilley wants to merge 2 commits into
masterfrom
windows-msvc-port-v2

Conversation

@ColtonWilley

Copy link
Copy Markdown
Contributor

Adds a Visual Studio solution under IDE/WINVS and the source changes needed to build wolfProvider with MSVC, FIPS and non-FIPS. Four configurations, x64.

user_settings.h is hand-written and committed rather than generated. options.h can't be reused here: it's an autotools output that bakes in results that are wrong under MSVC. wolfSSL and wolfProvider have to be built against the same one.

include/wolfprovider/version.h is now tracked. It was gitignored, so a fresh clone couldn't build on Windows at all.

Built and tested FIPS and non-FIPS, in the VS IDE.

Builds libwolfprov.dll on Windows with Visual Studio 2022 (v143, x64) against a
user_settings.h-configured wolfSSL and OpenSSL 3.x, for both non-FIPS and FIPS.

Build files live under IDE/WINVS/, laid out like the rest of the wolfSSL family
(wolfssh/ide/winvs): solution, property sheet and user_settings.h at the top,
projects one directory down. Four configurations:

  DLL Release|x64, DLL Debug|x64        the shipped provider
  Static Release|x64, Static Debug|x64  unit tests only, never distributed

The static configurations exist solely so the unit test can link wolfProvider's
internals. The DLL exports exactly one symbol, OSSL_provider_init, via a .def
rather than adding __declspec to the sources. A static library cannot serve as a
provider at all -- OpenSSL loads one only through LoadLibraryA plus
DSO_bind_func("OSSL_provider_init") -- so this costs nothing and keeps internal
crypto symbols off the shipped ABI.

Naming diverges from wolfSSH deliberately. There, unprefixed means static; here
the unprefixed names would be the test-only ones, and Visual Studio's default
selection sorts to them. "DLL" sorts before "Static" under either ordering, so
the default selection is always a customer artifact.

Three latent defects fixed, none of them Windows-specific:

  - wolfProvider never called wolfCrypt_Init(). wolfSSL's drbgStateMutex is
    declared with a static-initializer clause that expands to nothing off
    pthreads, and a Win32 CRITICAL_SECTION has no static-initializer form, so
    the mutex stayed zeroed and wc_InitRng() faulted inside ntdll. Only
    wolfCrypt_Init() runs wc_DrbgState_MutexInit(). Paired with
    wolfCrypt_Cleanup() in teardown and on the init failure path, since OpenSSL
    does not call teardown when init returns 0.

  - wolfProvider never called wc_curve25519_set_rng(), so blinded curve25519 was
    broken: X25519 derive, pkey -pubout, and the default TLS 1.3 group. Blinding
    is on whenever USE_INTEL_SPEEDUP is off, so this breaks any
    --disable-intelasm build on any platform. wp_ecx_gen was worse than a NULL
    pointer: wc_curve25519_make_key attaches the generation context's RNG, which
    is then freed. Now a per-key WC_RNG, mirroring wp_ecc_kmgmt.c.

  - wp_fips.h gated WP_FIPS_CHECKS_DEFAULT on HAVE_FIPS but included no wolfSSL
    configuration under WOLFSSL_USER_SETTINGS, so HAVE_FIPS was invisible and the
    mask silently evaluated to 0. src/wp_fips.c includes only that header, so
    fipsChecks initialised to zero and every wolfProvider FIPS check was
    disabled -- the provider still claimed P-192, RSA-SHA1 and small RSA keys but
    no longer rejected them. Now includes wolfssl/wolfcrypt/settings.h.

Portability: options.h guarded behind WOLFSSL_USER_SETTINGS (it is an autotools
output that does not exist in such a build); strcasecmp -> XSTRCASECMP;
XSTRLEN("literal") -> sizeof("literal") - 1 in two static tables, which GCC
constant-folds and MSVC does not; the FIPS CAST mutex initialiser moved off
__attribute__((constructor)), which MSVC has no equivalent for, to a
CRYPTO_THREAD_run_once from wolfssl_provider_init(); and a local byte-swap
replacing ByteReverseWord32, which is static in misc.c and never linkable.

include/wolfprovider/version.h is now tracked. It is an AC_CONFIG_FILES output
that was gitignored, so a fresh clone had no way to produce it without running
configure -- which Windows does not have. wolfSSL and wolfSSH both track theirs.

Distribution: IDE is enumerated file by file through nested include.am rather
than named as a directory, because automake's distdir rule is a plain recursive
cp that is not gitignore-aware and the Visual Studio projects write build output
inside the repo. The same reasoning removes EXTRA_DIST+=examples, which shipped
a built binary. Two source-list gates run from make check.

Verified in a Windows Server 2025 guest, MSVC v143 x64:

  non-FIPS   199/199 unit tests; 11/11 shipped-artifact checks
  FIPS       the same, against four bundles --
             5.9.2+v5.2.4, 5.8.4+v5.2.3, 5.9.2+v5.2.1, 5.9.1+FIPS-Ready

wolfSSL is built SHARED in every case, including FIPS, and the in-core integrity
check passes. IDE/WIN10/README.txt says FIPS "must be built as a static library,
for the moment", but specifies the linker settings required for a DLL twenty
lines later; the blocker is that their project never applies them to x64 and
never disables Whole Program Optimization for a DLL configuration. A static
wolfSSL would put the FIPS boundary inside libwolfprov.dll, so every wolfProvider
rebuild would invalidate the module hash.

USE_INTEL_SPEEDUP is deliberately omitted: wolfSSL ships x86-64 assembly for nine
features as GAS .S only, with no MASM equivalent, so enabling it fails to link.
WOLFSSL_AESNI and WOLFSSL_SP_X86_64_ASM are kept.

See IDE/WINVS/README.md for the build procedure, including what a FIPS build
requires beyond wolfSSL's own documented steps and why each of those is not
optional. The Linux autotools build is unaffected and still compiles clean under
-Werror.
@ColtonWilley ColtonWilley added the ci:all PR OSP toggle: run all label Aug 10, 2026

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #464

Scan targets checked: wolfprovider-bugs, wolfprovider-src

Findings: 5
5 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread test/test_ecc.c
* _WIN32 so builds without blinding keep the coverage. */
PRINT_MSG("X25519 ECDH KAT skipped - curve25519 blinding enabled");
(void)data;
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] X25519 tests return success instead of running under WOLFSSL_CURVE25519_BLINDING · Weak or missing assertions

The new guard makes test_ecdh_x25519_vector (and test_ecx_x25519_get_params_stale_ret at test/test_ecx.c:830) return 0 — a pass — whenever curve25519 blinding is enabled, which is wolfSSL's default without USE_INTEL_SPEEDUP. The RFC 7748 KAT and the get_params stale-return_size regression check therefore stop running on ordinary Linux builds too, and no wc_curve25519_set_rng() call exists anywhere in src/, so the condition the skip works around is still present.

Fix: Attach an RNG with wc_curve25519_set_rng() in the X25519 key paths so the tests can run, or return a distinct skip status the harness reports separately from a pass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional, curve25519 blinding breaks on linux as well with same build config. Proper X25519 RNG handling for blinding will be a follow up PR.

#endif

#define LIBWOLFPROV_VERSION_STRING "1.2.1"
#define LIBWOLFPROV_VERSION_HEX 0x01002001

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Generated version.h committed with hardcoded version that configure overwrites · Copy-paste errors

include/wolfprovider/version.h is an AC_CONFIG_FILES output (configure.ac:230) generated from version.h.in, yet it is now tracked with the version literal duplicated from AC_INIT([wolfprov], [1.2.1]). Running configure rewrites the tracked file, make distclean deletes it, and a version bump in configure.ac leaves the Windows build reporting the old value.

Fix: Track a Windows-only header (e.g. IDE/WINVS/version.h) instead of the configure output, or drive both values from a single committed source.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

version.h structure is intentional and mirrors wolfssl proper

Comment thread src/wp_wolfprov.c
#if defined(HAVE_FIPS) && !defined(WP_SINGLE_THREADED)
/* FIPS CAST tests are run lazily per-algorithm via wp_init_cast();
* their mutexes must exist before the first one runs. */
if (!wp_init_cast_mutexes()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] wp_init_cast_mutexes() error check cannot detect mutex initialization failure · Dead error handling

wolfprov_init_cast_mutex() (src/wp_internal.c:105) returns void and discards every wc_InitMutex() return, so wp_init_cast_mutexes() reports success whenever CRYPTO_THREAD_run_once succeeds. The new "Failed to initialize FIPS CAST mutexes" branch can never fire for the failure it names, leaving wp_init_cast() to lock uninitialized mutexes.

Fix: Record wc_InitMutex() failures in a static flag inside wolfprov_init_cast_mutex() and have wp_init_cast_mutexes() return 0 when it is set.

Comment thread test/test_ecc.c
* _WIN32 so builds without blinding keep the coverage. */
PRINT_MSG("X25519 ECDH KAT skipped - curve25519 blinding enabled");
(void)data;
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] X25519 ECDH known-answer test unconditionally passes when curve25519 blinding is enabled · Weak or missing assertions

Under WOLFSSL_CURVE25519_BLINDING the function returns 0 (pass) before running the RFC 7748 KAT, and the wc_curve25519_set_rng() call it says is missing is not added anywhere in src/. The X25519 shared-secret KAT therefore reports success in every blinded build, including the new MSVC configuration where the derive path is known broken.

Fix: Attach an RNG in the X25519 keymgmt so the KAT can run, or return a distinct non-pass skip status instead of 0.

Comment thread test/test_ecx.c
* _WIN32 so builds without blinding keep the coverage. */
PRINT_MSG("X25519 get_params test skipped - curve25519 blinding enabled");
(void)data;
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] X25519 get_params regression test unconditionally passes under blinding · Weak or missing assertions

The early return 0 under WOLFSSL_CURVE25519_BLINDING skips all three assertions (private, public and encoded-public export with a stale zero return_size), so the buffer-sizing regression this test guards is unverified in blinded builds while still counting as a pass.

Fix: Restrict the skip to the public-key sections that need an RNG, keeping the private-key stale-return_size assertions active.

@@ -0,0 +1,39 @@
/* version.h.in
*
* Copyright (C) 2006-2025 wolfSSL Inc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than check this in, can we put this in IDE/WINVS?

If not, AI says we need to update configure.ac, remove version.hin, etc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think thats a good idea actually, that setup is practically begging for a version.h clash if include paths are off even a little. AI is just overly upset, wolfSSL proper uses this exact same setup.

Comment thread src/wp_logging.c
{ "WP_LOG_LEVEL_VERBOSE", XSTRLEN("WP_LOG_LEVEL_VERBOSE"),WP_LOG_LEVEL_VERBOSE },
{ "WP_LOG_LEVEL_DEBUG", XSTRLEN("WP_LOG_LEVEL_DEBUG"), WP_LOG_LEVEL_DEBUG },
{ "WP_LOG_LEVEL_TRACE", XSTRLEN("WP_LOG_LEVEL_TRACE"), WP_LOG_LEVEL_TRACE },
{ "WP_LOG_LEVEL_ERROR", sizeof("WP_LOG_LEVEL_ERROR") - 1, WP_LOG_LEVEL_ERROR },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could be nice to macro-ize this for DRYness and compactness with a macro to define each entry

Comment thread include/wolfprovider/internal.h
Comment thread test/test_ecx.c
* which wolfProvider does not do yet. Gated on the macro rather than
* _WIN32 so builds without blinding keep the coverage. */
PRINT_MSG("X25519 get_params test skipped - curve25519 blinding enabled");
(void)data;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the return before the declarations is not C99. Could add the #else below

Use #else rather than an early return followed by declarations, which
C90 rejects. Review feedback from padelsbach on PR 464.
@ColtonWilley ColtonWilley added ci:all PR OSP toggle: run all and removed ci:all PR OSP toggle: run all labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:all PR OSP toggle: run all

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants