Skip to content

Add linux example project.#94

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

Add linux example project.#94
werwurm wants to merge 14 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 f0ee7c1 during lcov-test-coverage-report #177

Summary coverage rate:
  lines......: 95.7% (3013 of 3147 lines)
  functions..: 99.1% (230 of 232 functions)
  branches...: 87.2% (1646 of 1887 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 12 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.
The nat20sw module is an implementation of a nat20device character
device class. It uses the functionality implemented in nat20lib and
nat20crypto to implement a fully fledged DICE service with embedded CA
(ECA).
The root secret is hard coded and thus not useful for production
applications. But it serves as inspirational reference implementation
and as a suitable environment to develop user space tools against.
This adds libnat20 to the example buildroot environment.
It is a dependency for developing nat20 userspace tools.
@werwurm werwurm force-pushed the werwurm/example_linux branch from 9075c35 to b1d69fa Compare May 4, 2026 23:01
werwurm added 2 commits May 4, 2026 16:38
This commandline tool provides a primitive interface to communicate with
a nat20 device.
@werwurm werwurm force-pushed the werwurm/example_linux branch from b1d69fa to f0ee7c1 Compare May 4, 2026 23:39
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