diff --git a/.agents/docs/2026-10-20-add-wolfssl-plan.md b/.agents/docs/2026-10-20-add-wolfssl-plan.md new file mode 100644 index 00000000..5ecbf6d9 --- /dev/null +++ b/.agents/docs/2026-10-20-add-wolfssl-plan.md @@ -0,0 +1,89 @@ +# Add wolfSSL 5.9.4 as compat.wolfssl + +## Source and shape + +- Upstream: . +- Version: `5.9.4`, released as tag `v5.9.4-stable` on 2026-09-27; this is + the newest release on the upstream releases page at the time of writing. +- License: GPLv3, with a GPLv2 exception for a listed set of upstream + projects (`LICENSING` in the archive); a commercial license is available. + The descriptor records `GPL-3.0-only`. +- Shape: Form B, C-source compat with a generated config header. wolfSSL's + own build is autotools or CMake and both write `wolfssl/options.h` before + compiling. An inline descriptor cannot run a configure step, so the two + outputs of that step are carried literally: + - `sources` is upstream CMake's default library list, transcribed from + `generate_lib_src_list()` in `cmake/functions.cmake` with the default + options. `src/ssl.c` represents `src/`: the `src/ssl_*.c`, `src/bio.c`, + `src/x509*.c`, `src/pk*.c` and `src/conf.c` files in 5.9.4 are + `#include`d by it. The same holds for + `wolfcrypt/src/{evp,evp_pk,asn_tsp,asm,ecc_fp}.c`, which therefore are + not separate translation units. + - `generated_files` writes `mcpp_generated/wolfssl/options.h`, a snapshot + of the header CMake generates with default options, with platform probes + expressed as branches (`_WIN32`, `__linux__`, `__SIZEOF_INT128__`, + x86_64/aarch64). Both the library and its consumers compile it because + the descriptor defines `WOLFSSL_USE_OPTIONS_H` in `cflags` and in a + default-implied feature (the `compat.curl` `CURL_STATICLIB` pattern). +- Default feature set: TLS 1.3 + 1.2, AES-GCM, ChaCha20-Poly1305, ECC, + DH, RSA-PSS, SHA-2/SHA-3/SHAKE, ML-KEM; hardened defaults `NO_OLD_TLS`, + `NO_DES3`, `NO_RC4`, `NO_MD5`, `NO_MD4`, `NO_DSA`, `NO_PSK`. + Curve25519/Ed25519 and system CA-store loading are off in CMake's default + configuration and stay off here. + +## Automatic updates + +`extensions.autoupdate` is declared with `tag = "v${version}-stable"` and +`url = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/${tag}.tar.gz"`. +The suffix is upstream's actual release-tag shape. The recipe also pins +upstream's default source list and an options.h snapshot, so a release can +add or remove translation units or change CMake defaults; candidate CI is +what detects that, and the bump PR then needs a manual recipe update (the +"upgrades requiring recipe changes" case in `docs/automatic-updates.md`). + +## Feature evaluation + +No source-gated `features` are offered. CMake's optional components would +need different `options.h` values, and a feature's `defines` cannot be +depended on to rebuild the config header; adding one would silently produce +a library and consumers that disagree about its layout. TLS 1.3, which is a +define-only feature, is always on for the same reason. + +## Download and mirror + +- GLOBAL: + `https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.9.4-stable.tar.gz` +- SHA-256: + `7256bfc89b183a75183806c7debfa203443873b0b4a562e1b80d68e01b45ac57` +- The archive was hashed twice; both computations agree. +- No verified `mcpp-res/wolfssl` release is available (the gitcode probe + returns 401), so the descriptor uses the repository's plain-string upstream + URL fallback. A maintainer can add a byte-identical GitCode release later; + the sha256 stays the same. + +## Test member + +`tests/examples/wolfssl/` is registered in the root workspace. It asserts: + +- compile-time: the consumer inherited `WOLFSSL_TLS13`, `HAVE_AESGCM` and + `HAVE_ECC` (a configuration mismatch fails at compile time, not on the + wire); +- runtime: `wolfSSL_lib_version()`/`wolfSSL_lib_version_hex()` match the + header; the SHA-256 known-answer vector for `"abc"`; an AES-256-GCM + round trip and rejection of a modified tag; two distinct RNG blocks; and a + P-256 key pair whose exported public key is the expected uncompressed + 65-byte point. + +## Verification + +- `mcpp xpkg parse --all-os pkgs/c/compat.wolfssl.lua` reports + `parse OK` for linux, macosx and windows with 38 sources, 2 include dirs + and 1 generated file. +- A stock CMake build of the same tag with the default options + (`BUILD_SHARED_LIBS=OFF`, no examples/tests) compiles this exact source + list, which is what the descriptor transcribes. +- Cold `mcpp test -p wolfssl` on Windows (mcpp 2026.10.17.1, LLVM + toolchain, `MCPP_INDEX_MIRROR=GLOBAL`) reports + `test result ok. 1 passed; 0 failed` (build 8.61 s + run 0.42 s); the + package itself was downloaded and compiled cold in that run. Linux/macOS + legs are left to CI. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a09290b..eb9d2032 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,26 @@ ### Added +- **`compat.wolfssl` 5.9.4。** wolfSSL(TLS/crypto)以 C 源码 compat 形态收录,上游最新稳定 + tag 为 `v5.9.4-stable`。上游的 autotools/CMake 都会在编译前生成 `wolfssl/options.h`,而内联 + 描述符无法运行 configure,因此把该步骤的两项输出原样带入:`sources` 逐条抄自上游 CMake 默认 + 的 `generate_lib_src_list()`(5.9.4 的 `src/ssl_*.c`、`src/bio.c`、`src/x509*.c`、 + `src/pk*.c`、`src/conf.c` 由 `src/ssl.c` `#include`,故不单列;`wolfcrypt/src/{evp, + evp_pk,asn_tsp,asm,ecc_fp}.c` 同理);`generated_files` 写入 + `mcpp_generated/wolfssl/options.h`,即 CMake 默认选项生成的配置头,平台探测改写为 + `_WIN32`/`__linux__`/`__SIZEOF_INT128__`/x86_64/aarch64 分支。描述符通过 `cflags` 与 + default-implied feature 同时下发 `WOLFSSL_USE_OPTIONS_H`,库与消费者编译同一份配置, + 结构体布局和功能集不会分叉。默认功能集:TLS 1.3/1.2、AES-GCM、ChaCha20-Poly1305、ECC、 + DH、RSA-PSS、SHA-2/3/SHAKE、ML-KEM,并保留 CMake 的加固默认值(`NO_OLD_TLS`/`NO_DES3`/ + `NO_RC4`/`NO_MD5`/`NO_MD4`/`NO_DSA`/`NO_PSK`);Curve25519/Ed25519 与系统 CA 读取在 + CMake 默认配置里本就关闭,本包保持一致。CN 镜像尚未发布到 `mcpp-res`,按先例用纯字符串 + url 回退 GLOBAL。实测(Windows / mcpp 2026.10.17.1 / LLVM): + `mcpp xpkg parse --all-os pkgs/c/compat.wolfssl.lua` 三平台 `parse OK`(38 sources, + 2 include dirs,1 generated file);冷 `mcpp test -p wolfssl` → + `test result ok. 1 passed; 0 failed`(头文件版本串、SHA-256 KAT、AES-256-GCM 往返及 + 坏 tag 拒绝、RNG、P-256 密钥导出)。新增成员 `tests/examples/wolfssl`;linux/macOS + 两腿交 CI 裁决。 + - **`compat.soci` 4.1.4。** SOCI(C++ 数据库访问层)以 C++ 源码 compat 形态收录:上游 `src/core` 的 22 个 TU 与 sqlite3 后端的 10 个 TU 编入**同一个 lib** —— 上游 CMake 把它们拆成 `soci_core`/`soci_sqlite3` 两个库,但 mcpp 的 `sources` 是每包一张表,且没有后端的 core 连 diff --git a/mcpp.toml b/mcpp.toml index a25889b3..290044d1 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -171,6 +171,7 @@ members = [ "tests/examples/wil", "tests/examples/websocket-features", "tests/examples/websocket-tls", + "tests/examples/wolfssl", "tests/examples/tray", "tests/examples/xxhash", "tests/examples/yaml-cpp", diff --git a/pkgs/c/compat.wolfssl.lua b/pkgs/c/compat.wolfssl.lua new file mode 100644 index 00000000..bd79fba5 --- /dev/null +++ b/pkgs/c/compat.wolfssl.lua @@ -0,0 +1,292 @@ +-- compat.wolfssl — wolfSSL 5.9.4, built from source as a portable C +-- TLS/crypto library. +-- +-- WHY A DIRECT BUILD. wolfSSL's own build is autotools or CMake, and either +-- one writes `wolfssl/options.h` before compiling. An inline descriptor +-- cannot run a configure step, so this package carries two things instead: +-- +-- * `sources` — upstream CMake's default library source list, transcribed +-- from `generate_lib_src_list()` in cmake/functions.cmake with the +-- default option values (TLS 1.3 on; DTLS, CRL, OCSP, PSK and the +-- sniffer are off, so their sources are omitted or compile to empty +-- translation units). `src/ssl.c` alone represents `src/`: the +-- `src/ssl_*.c`, `src/bio.c`, `src/x509*.c`, `src/pk*.c`, `src/conf.c` +-- files in 5.9.4 are `#include`d by it and warn if compiled separately. +-- The same applies to `wolfcrypt/src/{evp,evp_pk,asn_tsp,asm,ecc_fp}.c`, +-- which is why they are absent. +-- +-- * `generated_files` — the feature defines CMake would have written to +-- options.h, recorded once and made platform-portable. Defining +-- WOLFSSL_USE_OPTIONS_H makes settings.h include it for the library's +-- own TUs and for every consumer, so a consumer including +-- cannot silently compile against a different feature +-- set than the archive was built with. Including +-- explicitly also works, as with an upstream install. +-- +-- What the default feature set contains: TLS 1.3 + TLS 1.2, AES-GCM, +-- ChaCha20-Poly1305, ECC (P-256 etc.), DH, RSA (PSS), SHA-224/256/384/512, +-- SHA-3/SHAKE, ML-KEM, and the hardened defaults CMake applies (NO_OLD_TLS, +-- NO_DES3, NO_RC4, NO_MD5, NO_MD4, NO_DSA, NO_PSK). Curve25519/Ed25519 and +-- system-CA-store loading are OFF in CMake's default configuration, so they +-- stay off here; consumers can still load a CA bundle with +-- wolfSSL_CTX_load_verify_locations(). +-- +-- wolfSSL's license is GPLv3 with a GPLv2 exception for a listed set of +-- upstream projects (see LICENSING in the archive); a commercial license is +-- also available. The SPDX identifier below records the default grant. +package = { + spec = "1", + namespace = "compat", + name = "wolfssl", + description = "wolfSSL — portable TLS/crypto library (static source build, TLS 1.3)", + licenses = {"GPL-3.0-only"}, + repo = "https://github.com/wolfSSL/wolfssl", + type = "package", + + -- Upstream's release tags carry a suffix: v5.9.4-stable, not v5.9.4. + -- The updater substitutes ${version} into `tag` and then uses ${tag} in + -- the URL. Candidate CI is what catches a release that changes the + -- transcribed source list or CMake defaults; when that happens the + -- descriptor needs a manual recipe update before the bump PR can pass. + extensions = { + autoupdate = { + tag = "v${version}-stable", + url = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/${tag}.tar.gz", + }, + }, + + xpm = { + -- No CN mirror has been published to gitcode/mcpp-res yet, so this + -- uses the repository's plain-string upstream fallback rather than + -- inventing one. A maintainer can add a byte-identical release later; + -- the sha256 does not change. + linux = { + ["5.9.4"] = { + url = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.9.4-stable.tar.gz", + sha256 = "7256bfc89b183a75183806c7debfa203443873b0b4a562e1b80d68e01b45ac57", + }, + }, + macosx = { + ["5.9.4"] = { + url = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.9.4-stable.tar.gz", + sha256 = "7256bfc89b183a75183806c7debfa203443873b0b4a562e1b80d68e01b45ac57", + }, + }, + windows = { + ["5.9.4"] = { + url = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.9.4-stable.tar.gz", + sha256 = "7256bfc89b183a75183806c7debfa203443873b0b4a562e1b80d68e01b45ac57", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + c_standard = "c11", + + -- Upstream's include root is the repository root: public headers are + -- , . mcpp_generated supplies the + -- options.h described above (and comes second so it can never shadow + -- a checked-in header). + include_dirs = { "*", "mcpp_generated" }, + + sources = { + -- src/: only ssl.c, because every other src/*.c is included by it. + "*/src/internal.c", + "*/src/wolfio.c", + "*/src/keys.c", + "*/src/ssl.c", + "*/src/ocsp.c", + "*/src/tls.c", + "*/src/tls13.c", + + -- wolfcrypt/src/: upstream's default set (cmake/functions.cmake). + "*/wolfcrypt/src/hmac.c", + "*/wolfcrypt/src/hash.c", + "*/wolfcrypt/src/cpuid.c", + "*/wolfcrypt/src/kdf.c", + "*/wolfcrypt/src/random.c", + "*/wolfcrypt/src/sha256.c", + "*/wolfcrypt/src/sha.c", + "*/wolfcrypt/src/sha512.c", + "*/wolfcrypt/src/sha3.c", + "*/wolfcrypt/src/rsa.c", + "*/wolfcrypt/src/sp_int.c", + "*/wolfcrypt/src/integer.c", -- guarded no-op under SP math; kept for parity with upstream + "*/wolfcrypt/src/ecc.c", + "*/wolfcrypt/src/aes.c", + "*/wolfcrypt/src/logging.c", + "*/wolfcrypt/src/wc_port.c", + "*/wolfcrypt/src/error.c", + "*/wolfcrypt/src/wc_encrypt.c", + "*/wolfcrypt/src/signature.c", + "*/wolfcrypt/src/wolfmath.c", + "*/wolfcrypt/src/memory.c", + "*/wolfcrypt/src/dh.c", + "*/wolfcrypt/src/asn.c", + "*/wolfcrypt/src/coding.c", + "*/wolfcrypt/src/poly1305.c", + "*/wolfcrypt/src/pwdbased.c", + "*/wolfcrypt/src/pkcs12.c", + "*/wolfcrypt/src/chacha.c", + "*/wolfcrypt/src/chacha20_poly1305.c", + "*/wolfcrypt/src/wc_mlkem.c", + "*/wolfcrypt/src/wc_mlkem_poly.c", + }, + + targets = { ["wolfssl"] = { kind = "lib" } }, + deps = {}, + + -- The archive is a static library; everything else follows from the + -- options.h snapshot. WOLFSSL_USE_OPTIONS_H is needed by the + -- package's own TUs AND by every consumer, so it is delivered twice: + -- `cflags` for this package, and a default-implied feature for + -- consumers (the compat.curl CURL_STATICLIB pattern). + cflags = { "-DWOLFSSL_USE_OPTIONS_H" }, + features = { + ["default"] = { implies = { "configured" } }, + ["configured"] = { defines = { "WOLFSSL_USE_OPTIONS_H" } }, + }, + + generated_files = { + ["mcpp_generated/wolfssl/options.h"] = [==[ +/* options.h — wolfSSL 5.9.4 build configuration. + * + * wolfSSL's CMake writes this header just before compiling the library. This + * is a snapshot of the values CMake produces with its default options, with + * the probe results expressed as branches so that one file is correct for + * every target this package is built for. Both the library and its consumers + * compile this same header (WOLFSSL_USE_OPTIONS_H in settings.h), so a + * consumer cannot disagree with the archive about struct layouts or feature + * availability. + * + * Source: CMakeLists.txt + cmake/functions.cmake + cmake/options.h.in at tag + * v5.9.4-stable. Legacy algorithms CMake disables by default stay disabled. + */ +#ifndef WOLFSSL_OPTIONS_H +#define WOLFSSL_OPTIONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WOLFSSL_OPTIONS_IGNORE_SYS +#undef _GNU_SOURCE +#if defined(__linux__) +#define _GNU_SOURCE 1 +#endif +#undef _POSIX_THREADS +#endif + +/* types.h depends on HAVE_LIMITS_H, so it must be defined here. */ +#undef HAVE_LIMITS_H +#define HAVE_LIMITS_H 1 + +/* callbacks.h sizes WOLFSSL_TIMEVAL with HAVE_SYS_TIME_H. */ +#if !defined(_WIN32) +#undef HAVE_SYS_TIME_H +#define HAVE_SYS_TIME_H 1 +#undef HAVE_SYS_UN_H +#define HAVE_SYS_UN_H 1 +#undef HAVE_PTHREAD +#define HAVE_PTHREAD 1 +#undef HAVE_GETPID +#define HAVE_GETPID 1 +#endif + +#undef HAVE___UINT128_T +#if defined(__SIZEOF_INT128__) +#define HAVE___UINT128_T 1 +#endif + +#undef WOLFSSL_X86_64_BUILD +#if defined(__x86_64__) || defined(_M_X64) +#define WOLFSSL_X86_64_BUILD +#endif + +#undef WOLFSSL_AARCH64_BUILD +#if defined(__aarch64__) || defined(_M_ARM64) +#define WOLFSSL_AARCH64_BUILD +#endif + +/* Math and constant-time choices. */ +#define ECC_SHAMIR +#define ECC_TIMING_RESISTANT +#define TFM_ECC256 +#define TFM_TIMING_RESISTANT +#define WC_NO_ASYNC_THREADING +#define WC_RSA_BLINDING +#define ECC_MIN_KEY_SZ 224 + +/* Crypto features enabled by CMake defaults. */ +#define GCM_TABLE_4BIT +#define HAVE_AESGCM +#define HAVE_CHACHA +#define HAVE_DH_DEFAULT_PARAMS +#define HAVE_ECC +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_EXTENDED_MASTER +#define HAVE_FFDHE_2048 +#define HAVE_HASHDRBG +#define HAVE_HKDF +#define HAVE_ONE_TIME_AUTH +#define HAVE_POLY1305 +#define WOLFSSL_BASE64_ENCODE +#define WOLFSSL_SHA224 +#define WOLFSSL_SHA3 +#define WOLFSSL_SHA384 +#define WOLFSSL_SHA512 +#define WOLFSSL_SHAKE128 +#define WOLFSSL_SHAKE256 +#define WOLFSSL_HAVE_MLKEM +#define WOLFSSL_PQC_HYBRIDS +#define WC_RSA_PSS +#define WOLFSSL_PSS_LONG_SALT + +/* TLS features. */ +#define HAVE_SNI +#define HAVE_SUPPORTED_CURVES +#define HAVE_TLS_EXTENSIONS +#define WOLFSSL_TLS13 +#define WOLFSSL_TLS_NO_MLKEM_STANDALONE +#define WOLFSSL_ASN_PRINT + +/* Upstream's hardened defaults: no legacy TLS or algorithms, no DSA/PSK. */ +#define NO_DES3 +#define NO_DES3_TLS_SUITES +#define NO_DSA +#define NO_MD4 +#define NO_MD5 +#define NO_OLD_TLS +#define NO_PSK +#define NO_RC4 + +/* Miscellaneous defaults from the same generated header. */ +#define HAVE_THREAD_LS +#define WOLFSSL_USE_ALIGN +#define NO_DO178 + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_OPTIONS_H */ +]==], + }, + + linux = { + cflags = { "-D_GNU_SOURCE" }, + ldflags = { "-lpthread", "-lm" }, + }, + macosx = { + cflags = { "-D_GNU_SOURCE" }, + ldflags = { "-lpthread", "-lm" }, + }, + windows = { + -- wolfio.c uses Winsock; random.c's entropy source uses + -- CryptAcquireContext/CryptGenRandom from advapi32. + ldflags = { "-lws2_32", "-ladvapi32" }, + }, + }, +} diff --git a/tests/examples/wolfssl/mcpp.toml b/tests/examples/wolfssl/mcpp.toml new file mode 100644 index 00000000..5d6c7fe1 --- /dev/null +++ b/tests/examples/wolfssl/mcpp.toml @@ -0,0 +1,11 @@ +# compat.wolfssl test project. It asserts the configuration surface a consumer +# cannot see without the options.h snapshot (TLS 1.3 and AES-GCM present), then +# exercises real crypto: a SHA-256 known-answer vector, an AES-256-GCM +# round trip (including tag rejection), the RNG, and an ECC P-256 key pair. +# `compat` redirect inherited from the workspace root. +[package] +name = "wolfssl-tests" +version = "0.1.0" + +[dependencies.compat] +wolfssl = "5.9.4" diff --git a/tests/examples/wolfssl/tests/wolfssl.cpp b/tests/examples/wolfssl/tests/wolfssl.cpp new file mode 100644 index 00000000..5aab521c --- /dev/null +++ b/tests/examples/wolfssl/tests/wolfssl.cpp @@ -0,0 +1,134 @@ +// compat.wolfssl: the configuration reaches the consumer, and the library +// behaves. Every assertion affects the exit status. + +// Included explicitly because this is the file that states the feature set; +// the package's default-implied define also makes settings.h pull it in. +#include + +#include +#include +#include +#include +#include +#include + +// Compile-time assertions: a consumer that did not inherit the package's +// configuration would fail here rather than at run time. +#ifndef WOLFSSL_TLS13 +#error "compat.wolfssl: the consumer did not inherit WOLFSSL_TLS13" +#endif +#ifndef HAVE_AESGCM +#error "compat.wolfssl: the consumer did not inherit HAVE_AESGCM" +#endif +#ifndef HAVE_ECC +#error "compat.wolfssl: the consumer did not inherit HAVE_ECC" +#endif + +#include +#include + +namespace { + +int failures = 0; + +void check(bool held, const char* what) { + if (!held) { std::printf("FAIL: %s\n", what); ++failures; } +} + +constexpr unsigned char kAbcSha256[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad, +}; + +void check_sha256() { + unsigned char digest[32]; + const char* msg = "abc"; + check(wc_Sha256Hash(reinterpret_cast(msg), 3, digest) == 0, + "wc_Sha256Hash succeeds"); + check(std::memcmp(digest, kAbcSha256, sizeof digest) == 0, + "SHA-256(\"abc\") is the published known-answer vector"); +} + +void check_aes_gcm() { + Aes aes; + check(wc_AesInit(&aes, nullptr, INVALID_DEVID) == 0, "AES context initializes"); + + unsigned char key[32]; + unsigned char iv[12]; + unsigned char aad[] = "wolfssl-header"; + unsigned char plain[] = "AES-256-GCM round trip through compat.wolfssl"; + unsigned char sealed[sizeof plain]; + unsigned char tag[16]; + unsigned char opened[sizeof plain]; + + for (unsigned i = 0; i < sizeof key; ++i) key[i] = static_cast(i * 7 + 1); + for (unsigned i = 0; i < sizeof iv; ++i) iv[i] = static_cast(0xa0 + i); + + check(wc_AesGcmSetKey(&aes, key, sizeof key) == 0, "AES-256 key is accepted"); + check(wc_AesGcmEncrypt(&aes, sealed, plain, sizeof plain, iv, sizeof iv, + tag, sizeof tag, aad, sizeof aad) == 0, + "AES-256-GCM encryption succeeds"); + check(std::memcmp(sealed, plain, sizeof plain) != 0, + "the sealed buffer is not the plaintext"); + check(wc_AesGcmDecrypt(&aes, opened, sealed, sizeof sealed, iv, sizeof iv, + tag, sizeof tag, aad, sizeof aad) == 0, + "AES-256-GCM decryption succeeds"); + check(std::memcmp(opened, plain, sizeof plain) == 0, + "decryption restores the plaintext"); + + tag[0] ^= 0x01; + check(wc_AesGcmDecrypt(&aes, opened, sealed, sizeof sealed, iv, sizeof iv, + tag, sizeof tag, aad, sizeof aad) != 0, + "a modified authentication tag is rejected"); + + wc_AesFree(&aes); +} + +void check_rng_and_ecc() { + WC_RNG rng; + check(wc_InitRng(&rng) == 0, "RNG initializes"); + + unsigned char a[32]; + unsigned char b[32]; + check(wc_RNG_GenerateBlock(&rng, a, sizeof a) == 0, "RNG produces a block"); + check(wc_RNG_GenerateBlock(&rng, b, sizeof b) == 0, "RNG produces a second block"); + check(std::memcmp(a, b, sizeof a) != 0, "two RNG blocks differ"); + + ecc_key key; + check(wc_ecc_init(&key) == 0, "ECC key initializes"); + check(wc_ecc_make_key_ex(&rng, 32, &key, ECC_SECP256R1) == 0, + "a P-256 key pair is generated"); + + unsigned char pub[65]; // uncompressed P-256 point: 0x04 || X || Y + word32 pubLen = sizeof pub; + check(wc_ecc_export_x963(&key, pub, &pubLen) == 0, "the public key exports"); + check(pubLen == sizeof pub && pub[0] == 0x04, + "the exported key is an uncompressed 65-byte P-256 point"); + + wc_ecc_free(&key); + wc_FreeRng(&rng); +} + +} // namespace + +int main() { + check(std::strcmp(wolfSSL_lib_version(), LIBWOLFSSL_VERSION_STRING) == 0, + "the runtime version matches the header's version string"); + check(wolfSSL_lib_version_hex() == LIBWOLFSSL_VERSION_HEX, + "the runtime version matches the header's version number"); + check(wolfSSL_Init() == WOLFSSL_SUCCESS, "wolfSSL_Init succeeds"); + + check_sha256(); + check_aes_gcm(); + check_rng_and_ecc(); + + wolfSSL_Cleanup(); + if (failures == 0) { + std::printf("wolfssl OK\n"); + } else { + std::printf("wolfssl FAILED: %d\n", failures); + } + return failures == 0 ? 0 : 1; +}