Skip to content

Commit 49872ce

Browse files
committed
CLAUDE didn't exist when I created this project :)
1 parent 3425508 commit 49872ce

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Overview
6+
7+
CAS .NET SDK is a managed cryptographic toolkit published as the [`cas-dotnet-sdk`](https://www.nuget.org/packages/cas-dotnet-sdk) NuGet package. It is a thin, high-level .NET API over a native Rust FFI layer (`cas-core-lib`), which itself wraps the RustCrypto and Dalek-Cryptography suites. The .NET code does almost no cryptography itself — it marshals data to/from the native library.
8+
9+
## Repository Layout
10+
11+
- `cas-dotnet-sdk/` — the SDK class library (NuGet package source). Multi-targets `net6.0``net10.0`.
12+
- `cas-dotnet-sdk-tests/` — xUnit test project, references the SDK directly.
13+
- `cas-core-lib/`**git submodule** containing the Rust source compiled to `cas_core_lib.dll` (Windows) / `libcas_core_lib.so` (Linux). Clone with `--recursive` or run `git submodule update --init --recursive`.
14+
- `docs/EXAMPLES.md` — usage samples.
15+
16+
## Build & Test
17+
18+
The Rust native library must be built and present alongside the managed assemblies, or all P/Invoke calls fail at runtime. The SDK `.csproj` automates this via MSBuild targets (`BuildRust` / `StageRustArtifacts`) that run `cargo build --release` before each build — so a plain `dotnet build` builds Rust too. This requires a working Rust toolchain (`cargo`) on PATH.
19+
20+
```bash
21+
# Full build (also compiles the Rust submodule via the BuildRust target)
22+
dotnet build cas-dotnet-sdk.sln
23+
24+
# Skip the Rust build when the native lib is already staged (much faster)
25+
dotnet build cas-dotnet-sdk.sln -p:BuildNativeRust=false
26+
27+
# Build the Rust lib manually
28+
cd cas-core-lib && cargo build --release
29+
30+
# Run all tests for one framework (must specify -f; the project multi-targets)
31+
dotnet test ./cas-dotnet-sdk-tests/cas-dotnet-sdk-tests.csproj -c Release -f net8.0
32+
33+
# Run a single test class or method
34+
dotnet test ./cas-dotnet-sdk-tests/cas-dotnet-sdk-tests.csproj -f net8.0 --filter "FullyQualifiedName~SHAWrapperTests"
35+
dotnet test ./cas-dotnet-sdk-tests/cas-dotnet-sdk-tests.csproj -f net8.0 --filter "DisplayName~Hash512"
36+
```
37+
38+
Note: CI (`.github/workflows/pr-tests-*.yml`) builds `cargo build --release` separately, then copies the native lib into the test `bin/Release/<tfm>` directory before running `dotnet test`. If you build the Rust lib by hand and the MSBuild target is skipped, the native artifact must end up next to the test assembly the same way.
39+
40+
## Architecture: the three-layer wrapper pattern
41+
42+
Every cryptographic capability is grouped into a top-level folder by category (`Hashers`, `Symmetric`, `Asymmetric`, `Signatures`, `PasswordHashers`, `KeyExchange`, `Hybrid`, `Compression`, `Sponges`, `PQC`). Within each category folder, the same three-part structure repeats — understand it once and it applies everywhere:
43+
44+
1. **Public wrapper** (e.g. `Hashers/SHAWrapper.cs`) — the user-facing class. Inherits `BaseWrapper` and implements a category interface (e.g. `IHasherBase`). It contains the real logic: choose the platform DLL import, call it, copy the returned bytes out of the native pointer with `Marshal.Copy`, then free the native memory.
45+
46+
2. **Platform P/Invoke wrappers** (`<Category>/Windows/*.cs` and `<Category>/Linux/*.cs`) — `internal static` classes containing only `[DllImport]` declarations. Windows imports target `cas_core_lib.dll`, Linux imports target `libcas_core_lib.so`. The two files are otherwise identical signatures. The public wrapper dispatches between them with `this._platform == OSPlatform.Linux ? LinuxWrapper.fn(...) : WindowsWrapper.fn(...)`.
47+
48+
3. **Types** (`<Category>/Types/*.cs`) — `internal struct`s mirroring the C ABI structs returned by the Rust FFI (e.g. `SHAHashByteResult { IntPtr result_bytes_ptr; int length; }`), plus category interfaces and enums.
49+
50+
### Cross-cutting helpers (`Helpers/`)
51+
52+
- `BaseWrapper` — base class for all public wrappers; resolves the current OS into `_platform` (`OSPlatform`) at construction via `OperatingSystemDeterminator`. Throws on any OS that is not Windows or Linux.
53+
- `FreeMemoryHelper` — frees native heap memory returned by the Rust layer. **Any FFI call that returns a pointer-bearing struct must have its pointer freed via `FreeMemoryHelper` after the bytes are marshaled out**, or memory leaks. This is the single most important invariant when adding new wrappers.
54+
55+
### Factories
56+
57+
Categories with interchangeable algorithms expose a `*Factory` (e.g. `HasherFactory.Get(IHasherType.SHA)`) returning the category interface. When adding a new algorithm to such a category, wire it into both the enum and the factory switch.
58+
59+
## Adding a new algorithm
60+
61+
1. Add the Rust FFI function in `cas-core-lib` (separate submodule/repo) and rebuild it.
62+
2. Add matching `[DllImport]` declarations in **both** the `Windows` and `Linux` P/Invoke files of the relevant category.
63+
3. Add any new return struct to `Types/`.
64+
4. Implement the public method on the wrapper: platform-dispatch the call, marshal out the result, free native memory.
65+
5. If the category has a factory/enum, register the new type there.
66+
6. Add an xUnit test. NIST test vectors live as `.rsp` files under the test project's `AES/Data` and `SHA/Data` folders (copied to output via `Content` items in the test `.csproj`).
67+
68+
## Cross-platform constraint
69+
70+
Only **Windows x64** and **Linux x64** are supported (no macOS). All wrappers must keep Windows and Linux paths in sync — a function added to one platform wrapper without the other will throw `DllNotFoundException` / `EntryPointNotFoundException` on the missing platform. Tests run against .NET 6–10 on both OSes in CI on every PR.

0 commit comments

Comments
 (0)