Skip to content

Add linux example project.#94

Draft
werwurm wants to merge 71 commits into
mainfrom
werwurm/example_linux
Draft

Add linux example project.#94
werwurm wants to merge 71 commits into
mainfrom
werwurm/example_linux

Conversation

@werwurm
Copy link
Copy Markdown
Contributor

@werwurm werwurm commented Apr 7, 2026

No description provided.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

LCOV of commit cee9a77 during lcov-test-coverage-report #231

Summary coverage rate:
  lines......: 95.6% (3048 of 3188 lines)
  functions..: 99.1% (232 of 234 functions)
  branches...: 87.1% (1658 of 1904 branches)

Files changed coverage rate: n/a

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Linux-based example stack for libnat20, including kernel modules (device framework, crypto backend, software service) plus a userspace CLI and a Buildroot external tree to build/run the example (QEMU-focused).

Changes:

  • Introduces kernel modules: nat20device (char-dev framework), nat20crypto (Linux kernel crypto backend), nat20sw (software NAT20 service), and nat20lib (exports libnat20 symbols).
  • Adds nat20cli userspace tool + a shell script to exercise the kernel stack and verify cert chains.
  • Adds Buildroot br_external integration (packages, configs, bootstrap/env scripts, QEMU runner) and expands license-check coverage to include these file types.

Reviewed changes

Copilot reviewed 32 out of 34 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
examples/linux/nat20sw/nat20sw.c Software NAT20 service kernel module implementing dispatch + certificate read via nat20device.
examples/linux/nat20sw/Kbuild Kbuild for building nat20sw against nat20 modules/symvers.
examples/linux/nat20lib/mod.c Kernel module wrapper exporting selected libnat20 symbols for other modules.
examples/linux/nat20lib/Kbuild Kbuild for compiling core libnat20 sources into a kernel module.
examples/linux/nat20device/nat20device.c NAT20 device framework kernel module (char device + securityfs cert file).
examples/linux/nat20device/Kbuild Kbuild for nat20device.
examples/linux/nat20device/include/nat20device.h Public API for registering/unregistering NAT20 device drivers.
examples/linux/nat20crypto/nat20crypto.c Linux-kernel-crypto-based implementation of nat20 crypto context.
examples/linux/nat20crypto/Kbuild Kbuild for nat20crypto with include paths and symvers linkage.
examples/linux/nat20crypto/include/nat20crypto.h Public header for opening/closing Linux crypto context + secret creation.
examples/linux/nat20cli/src/main.c Userspace CLI to issue requests via /dev/nat20X and decode responses.
examples/linux/nat20cli/nat20clitest.sh Script to load modules, run CLI flows, and verify produced cert chains.
examples/linux/nat20cli/CMakeLists.txt CMake build for nat20cli against LibNat20 components.
examples/linux/br_external/utils/envsetup.sh Helper functions/env exports for the Buildroot out-of-tree build dir.
examples/linux/br_external/run-qemu.sh QEMU runner for the Buildroot-produced kernel + rootfs.
examples/linux/br_external/package/nat20sw/nat20sw.mk Buildroot package definition for nat20sw kernel module.
examples/linux/br_external/package/nat20sw/Config.in Buildroot menu entry for nat20sw.
examples/linux/br_external/package/nat20lib/nat20lib.mk Buildroot package definition for nat20lib kernel module.
examples/linux/br_external/package/nat20lib/Config.in Buildroot menu entry for nat20lib.
examples/linux/br_external/package/nat20device/nat20device.mk Buildroot package definition for nat20device kernel module.
examples/linux/br_external/package/nat20device/Config.in Buildroot menu entry + help text for nat20device.
examples/linux/br_external/package/nat20crypto/nat20crypto.mk Buildroot package definition for nat20crypto kernel module.
examples/linux/br_external/package/nat20crypto/Config.in Buildroot menu entry for nat20crypto.
examples/linux/br_external/package/nat20cli/nat20cli.mk Buildroot package definition for installing nat20cli.
examples/linux/br_external/package/nat20cli/Config.in Buildroot menu entry for nat20cli.
examples/linux/br_external/package/libnat20/libnat20.mk Buildroot package definition for building/installing libnat20.
examples/linux/br_external/package/libnat20/Config.in Buildroot menu entry for libnat20.
examples/linux/br_external/external.mk Buildroot external-tree make include of all package .mks.
examples/linux/br_external/external.desc Buildroot external-tree metadata.
examples/linux/br_external/Config.in Top-level Buildroot external-tree menu sources for NAT20 packages.
examples/linux/br_external/bootstrap.sh Bootstrap script to create out-of-tree Buildroot build directory and seed configs.
.github/license-check/license-config.json Extends license header checking to additional Buildroot/script file types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +325 to +333
void nat20device_unregister_driver(struct nat20device_driver* driver) {
struct nat20device_driver_instance* instance;

if (!driver) return;

instance = to_nat20device_instance(driver);

pr_info("NAT20: Unregistering driver instance %s%d\n", NAT20DEVICE_DEVICE_NAME, instance->id);

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

nat20device_unregister_driver() frees instance unconditionally, but open file descriptors keep a raw pointer to instance in file_priv->instance (set in nat20device_open). This can lead to use-after-free in subsequent read/write/release on those FDs after unregister. Consider reference counting the instance (e.g., kref) and only freeing after the last release, while making unregister mark the instance as inactive (e.g., set instance->ops = NULL) and remove device nodes/cdev.

Copilot uses AI. Check for mistakes.
/* Dispatch the request */
ret = instance->ops->dispatch(instance->ctx, request_buf, count, &file_priv->response);
if (ret < 0) goto out;

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

nat20device_write() replaces the response buffer but never resets the file position. If the previous response was fully read, *f_pos will remain at the old end offset and the next read() will immediately return 0 (EOF). Reset *f_pos (and/or filp->f_pos) to 0 when a new response is produced.

Suggested change
/*
* A new response buffer is now available for this file; rewind the
* file position so the next read starts from the beginning.
*/
if (f_pos) *f_pos = 0;
filp->f_pos = 0;

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +66
* The driver must allocate the response buffer, which will be freed by
* the framework using kfree after the read operation completes.
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Header comment says the framework frees the response buffer "after the read operation completes", but the implementation only frees the buffer on the next write or on release() (it is not freed when read() reaches EOF). Please either update the documentation to match the actual lifetime or implement freeing when the response has been fully consumed.

Suggested change
* The driver must allocate the response buffer, which will be freed by
* the framework using kfree after the read operation completes.
* The driver must allocate the response buffer and must not free it after
* a successful return. The framework takes ownership of the buffer and
* frees it with kfree during later request/file cleanup (for example,
* before replacing it with a new response or when the device is released).

Copilot uses AI. Check for mistakes.
kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(md_tfm), GFP_KERNEL);
if (md_ctx == NULL) {
crypto_free_shash(md_tfm);
printk(KERN_ERR "Failed to allocate hash descriptor: %ld\n", PTR_ERR(md_ctx));
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

On allocation failure md_ctx == NULL, the log uses PTR_ERR(md_ctx), but md_ctx is not an ERR_PTR (it's NULL). This prints a meaningless value and may confuse debugging. Log -ENOMEM or omit the error code here.

Suggested change
printk(KERN_ERR "Failed to allocate hash descriptor: %ld\n", PTR_ERR(md_ctx));
printk(KERN_ERR "Failed to allocate hash descriptor: %d\n", -ENOMEM);

Copilot uses AI. Check for mistakes.
Comment on lines +406 to +407
n20_rfc6979_k_generation(
&ctx->digest_ctx, digest_algorithm, dicemod_key->type, &key_slice, &gather_list, &k_bn, 0);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The return value from n20_rfc6979_k_generation() is ignored. If k-generation fails, subsequent operations (including modular inversion of k) may operate on an invalid/zero key and produce incorrect signatures or trigger errors. Capture and check the return value and fail early on error.

Suggested change
n20_rfc6979_k_generation(
&ctx->digest_ctx, digest_algorithm, dicemod_key->type, &key_slice, &gather_list, &k_bn, 0);
err = n20_rfc6979_k_generation(
&ctx->digest_ctx, digest_algorithm, dicemod_key->type, &key_slice, &gather_list, &k_bn, 0);
if (err) {
printk(KERN_ERR "Failed to generate signing nonce: %d\n", err);
return err;
}

Copilot uses AI. Check for mistakes.
Comment thread examples/linux/nat20cli/src/main.c Outdated
exit(EXIT_FAILURE);
}

// Stage 1: Determine command
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

argv[optind] is used without checking that optind < argc. Invoking nat20cli with no command (or after consuming options) will read past argv bounds and likely crash. Add an argc/optind check and print usage when no command is provided.

Suggested change
// Stage 1: Determine command
// Stage 1: Determine command
if (optind >= argc) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}

Copilot uses AI. Check for mistakes.
Comment thread examples/linux/nat20cli/src/main.c Outdated
size_t pos = 0;
if ((len & 1) != 0) {
// Odd length, assume leading zero
*out_pos++ = nibble2bits(hex[0]);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

For odd-length hex strings, hex_string_to_bytes_in_place() writes nibble2bits(hex[0]) without validating it. If the first character is not valid hex, the function will write 0xFF (from -1) and continue instead of failing. Check nibble2bits(hex[0]) < 0 and return an error like the even-length path does.

Suggested change
*out_pos++ = nibble2bits(hex[0]);
int8_t low = nibble2bits(hex[0]);
if (low < 0) {
return -1; // Invalid hex character
}
*out_pos++ = low;

Copilot uses AI. Check for mistakes.
# along with this program; if not, see
# <https://www.gnu.org/licenses/>.

LIBNAT20_VERSION = origin/main
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Using *_VERSION = origin/main for a Buildroot git package makes builds non-reproducible and can unexpectedly change when the branch moves. Prefer pinning to a specific tag or commit hash (and updating intentionally), especially since this external tree lives inside the same repository.

Suggested change
LIBNAT20_VERSION = origin/main
# Pin to an immutable revision for reproducible Buildroot builds.
LIBNAT20_VERSION = 0123456789abcdef0123456789abcdef01234567

Copilot uses AI. Check for mistakes.
Comment thread examples/linux/br_external/package/nat20device/Config.in Outdated
@werwurm werwurm force-pushed the werwurm/example_linux branch 6 times, most recently from be3d097 to a978a54 Compare April 8, 2026 16:09
werwurm added 10 commits April 29, 2026 09:34
Add a kernel module that provides libnat20 functionality to linux kernel
modules. Also add a configuration to build a minimal linux image with
buildroot and run in on qemu and a workflow to test build nat20lib.ko
This module creates a new character device class intended to implement
the nat20 service protocol implementing DICE based device state
attestation and an embedded CA.
The nat20crypto module implements the libnat20 crypto interface
in terms of linux kernel crypto primitives.
The module implements
- deterministic ECDSA with curves P256 and P384.
- Bytewise SHA-2 224/256/384/512
- HMAC
- HKDF

ED25519 is currently not supported.
@werwurm werwurm force-pushed the werwurm/example_linux branch 2 times, most recently from b1d69fa to f0ee7c1 Compare May 4, 2026 23:39
@werwurm werwurm force-pushed the werwurm/example_linux branch from f0ee7c1 to d461087 Compare May 14, 2026 13:46
werwurm and others added 16 commits May 14, 2026 09:15
  Adds a C integration test binary (nat20_integration_test) that exercises
  the full DICE service stack via /dev/nat200. The test generates certificate
  chains across all supported key type (P-256, P-384) and format (X.509,
  COSE) permutations, verifies cryptographic signatures at each link, and
  confirms that parent_path-based issuance produces identical results to
  direct issuance after promote.

  Test structure:
  - Phase 1 (level 1): Generate CDI1, CDI2, ECA, ECA_EE certs and
    signatures using parent paths of varying depth from the UDS level.
    Verify all X.509 and COSE chains cryptographically.
  - Phase 2 (level 2): After one promote, regenerate CDI2/ECA/ECA_EE/sign
    with reduced parent path depth and assert byte-for-byte equality.
  - Phase 3 (level 3): After second promote, regenerate ECA/ECA_EE/sign
    with no parent path and assert equality.

  Also includes:
  - test_helpers.c: OpenSSL-based X.509 signature verification, public key
    extraction, COSE_Sign1 parsing and verification, CWT subject public
    key extraction, and compressed input computation.
  - nat20_qemu_init.sh: init wrapper for running tests in QEMU CI.
  - GitHub Action steps to build the rootfs and run the test suite in QEMU.
  - Buildroot package (nat20test) with OpenSSL dependency.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@werwurm werwurm force-pushed the werwurm/example_linux branch from d461087 to cee9a77 Compare May 16, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants