# GeoVLog.Core **Shared domain‑model & utility library for the GeoV airborne‑survey suite** ``` GeoVLog.sln │ ├─ GeoVLog.Core ← you are here ├─ GeoVLogSvc (aircraft Windows Service – writes encrypted logs) ├─ GeoVLogReader (CLI – decrypts & extracts logs) └─ GeoVLogViewer (planned WPF GUI – visualises logs) ``` `GeoVLog.Core` contains all logic that **must be identical** across writer, reader and future tools: * **Data contracts** – `Record`, `FlightManifest`, `SensorId`, enumerations. * **Cryptography helpers** – AES‑GCM chunk encrypt/decrypt, RSA key wrap, random‑key generator. * **HDF5 constants** – dataset names, attribute keys, compression & filter IDs. * **Embedded public key** – hard‑wired RSA public key used by the logger to wrap the per‑file AES key (see §4). * **Utility extensions** – CRC16, `Span` decoders, NMEA time helpers. * **Versioning & schema migrations** – one source of truth so all apps read / write the same file format. > **Never** add UI, service‑host or CLI parsing code here—keep Core UI‑agnostic > and platform‑agnostic. --- ## 1 · Project basics | Setting | Value | |---------|-------| | Target framework | **`net8.0`** | | Nullable refs | `enable` | | Style / analyzers| `Microsoft.CodeAnalysis.NetAnalyzers` (warnings‐as‑errors) | | NuGet packages | `HDF5.NET`, `BouncyCastle.NetCore`, `Microsoft.Extensions.Logging.Abstractions` | | Output | Class‑library DLL (no EXE) | --- ## 2 · Folder layout ``` GeoVLog.Core/ │ ├─ Models/ │ ├─ Record.cs ← time‑series row │ ├─ FlightManifest.cs ← YAML‑serialisable struct │ ├─ SensorId.cs ← enum (Gamma, Mag, VLF, GPS) │ └─ Units.cs │ ├─ Crypto/ │ ├─ CryptoUtils.cs ← AES‑GCM, RSA wrap/unwrap, RNG │ ├─ EmbeddedKeys.cs ← public key PEM string │ └─ Crc16Ccitt.cs │ ├─ Hdf5/ │ ├─ H5Constants.cs ← dataset names, filter IDs │ ├─ H5Writer.cs ← append‑row helper used by logger │ └─ H5Reader.cs ← low‑level decrypt+parse (used by ReaderLib) │ └─ GeoVLog.Core.csproj ``` *(Feel free to reorganise; keep “no UI / no host dependencies” rule.)* --- ## 3 · Data model ### 3.1 `Record` | Column | Type | Description | |--------|------|-------------| | `TimestampUtc` | `long` (μs since Unix epoch) | Recorded using the system clock | | `Sensor` | `SensorId` enum | gamma, mag, vlf, gps, … | | `Channel` | `byte` | axis or energy window | | `Value` | `double` | raw measurement | | `Unit` | `Units` enum (string attr) | cps, nT, pT/√Hz … | | `Quality` | `ushort` bitmask | CRC flag, saturation, gap‑fill | ### 3.2 `FlightManifest` YAML‑friendly POCO: ```csharp public sealed record FlightManifest ( string FlightId, string Aircraft, DateTimeOffset TakeOff, DateTimeOffset Landing, Dictionary Sensors, string Crew, string Notes ); ``` Serialised via `YamlDotNet` in the service (not referenced here). --- ## 4 · Embedded RSA public key ```csharp internal static class EmbeddedKeys { /// RSA‑2048 SubjectPublicKeyInfo PEM internal const string PublicPem = """-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9...snip... -----END PUBLIC KEY-----"""; } ``` * Used **only by GeoVLogSvc** to call `CryptoUtils.WrapAesKey(aesKey, EmbeddedKeys.PublicPem)`. * If you rotate keys: replace the PEM here, increment `FileFormatVersion`, rebuild all apps, re‑sign logger. --- ## 5 · Crypto utilities ```csharp public static class CryptoUtils { public static byte[] WrapAesKey(ReadOnlySpan aesKey, string publicPem); public static byte[] UnwrapAesKey(ReadOnlySpan wrapped, string privatePem); public static void EncryptChunk(ReadOnlySpan plain, Span cipher, Span tag, ReadOnlySpan nonce, ReadOnlySpan key); public static void DecryptChunk(ReadOnlySpan cipher, ReadOnlySpan tag, Span plain, ReadOnlySpan nonce, ReadOnlySpan key); } ``` * AES‑256‑GCM via `System.Security.Cryptography.AesGcm` (hardware AES‑NI). * RSA‑OAEP (SHA‑256) via `RSA.ImportFromPem`. * Nonces = 96‑bit random per chunk. * Self‑test vectors in `CryptoTests` project. --- ## 6 · HDF5 helper overview | Helper | Key responsibilities | |--------|---------------------| | **H5Constants** | `DataSetName = "/measurements"`; filter ID for `H5Z_AES`; attribute keys (`file_version`, `aes_wrapped_key`). | | **H5Writer** | `BeginFile(wrappedKey)` → creates file, dataset, writes header.
`AppendRecords(ReadOnlySpan)` → buffer→chunk→flush. | | **H5Reader** | `ReadHeader()` (returns metadata + wrapped key)
`Rows()` → `IAsyncEnumerable` decrypting on the fly. | The logger uses **H5Writer**; ReaderLib and tests use **H5Reader**. --- ## 7 · Versioning & compatibility * `FileFormatVersion` (ushort) stored as a root attribute; initial value **1**. * Bump only on **breaking** schema or crypto changes. * E.g. key‑length change, new mandatory column. * `H5Reader` throws `UnsupportedVersionException` if it encounters a newer major. --- ## 8 · Unit tests (in GeoVLog.Tests) | Category | Tests | |----------|-------| | **Models** | `Record_SerializationRoundTrip` | | **Crypto** | `AESGcm_RoundTrip`, `RSA_WrapUnwrap`, `TamperedTag_Throws` | | **HDF5** | `WriteRead_SameRows`, `VersionMismatch_Throws` | Run all: ```bash dotnet test ..\.. ests\GeoVLog.Tests --filter Category=Core ``` --- ## 9 · Contribution rules 1. **No UI** – keep Core headless. 2. **No direct file paths** – expose `Stream` APIs; let callers decide the FS. 3. **Add unit tests** for every public method. 4. **Run `dotnet format`** before PR. 5. Keep public APIs `readonly` / immutable where practical. --- ## 10 · License MIT – see repository root `LICENSE`. --- *Questions / proposals?* Open a PR or issue labelled **core**.