|
| 1 | +# CryptoNet - AI Coding Agent Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +CryptoNet is a cross-platform .NET cryptography library providing RSA, AES, and DSA implementations. The core library targets .NET Standard 2.0 for maximum compatibility, while tests and examples use .NET 8.0. |
| 5 | + |
| 6 | +## Architecture |
| 7 | + |
| 8 | +### Project Structure |
| 9 | +- **CryptoNet** (netstandard2.0): Core library with `CryptoNetRsa`, `CryptoNetAes`, `CryptoNetDsa` classes |
| 10 | +- **CryptoNet.Models** (netstandard2.0): Shared models (`CryptoNetInfo`, `RsaDetail`, `AesDetail`, `DsaDetail`, `KeyType`, `EncryptionType`) |
| 11 | +- **CryptoNet.ExtShared** (netstandard2.0): Internal utilities for certificate handling and byte operations |
| 12 | +- **CryptoNet.ExtPack** (net8.0): Extension methods for MD5 hashing, PEM export, and content validation |
| 13 | +- **CryptoNet.UnitTests** (net8.0): NUnit tests with NUnit, Moq, Shouldly assertions |
| 14 | + |
| 15 | +### Key Dependencies |
| 16 | +All projects use **Central Package Management** via `Directory.Packages.props`. Never add package versions directly to `.csproj` files—only add `<PackageReference Include="PackageName" />` without versions. |
| 17 | + |
| 18 | +### NuGet Packaging Pattern |
| 19 | +The main `CryptoNet.csproj` uses `PrivateAssets="all"` for project references to `ExtShared` and `Models`, then explicitly includes their DLLs in the package via `IncludeReferencedDllsInPackage` target. This creates a self-contained NuGet package. |
| 20 | + |
| 21 | +## Development Workflows |
| 22 | + |
| 23 | +### Building & Testing |
| 24 | +```powershell |
| 25 | +# Standard build and test |
| 26 | +dotnet build |
| 27 | +dotnet test --configuration Release --no-build |
| 28 | +
|
| 29 | +# Code coverage with HTML report |
| 30 | +.\Scripts\run_codecoverage.ps1 # Runs tests, generates coverage, opens in Edge |
| 31 | +
|
| 32 | +# Docker build |
| 33 | +.\Scripts\run_docker_build.ps1 |
| 34 | +# or: docker build . --file .\Dockerfile --tag cryptonet-service:latest |
| 35 | +``` |
| 36 | + |
| 37 | +### Documentation Generation |
| 38 | +Documentation uses **DocFX** to generate API docs from XML comments: |
| 39 | +```powershell |
| 40 | +.\Scripts\run_docs.ps1 # Cleans, builds, serves on localhost:8080 |
| 41 | +
|
| 42 | +# Setup (one-time): dotnet tool install -g docfx |
| 43 | +``` |
| 44 | + |
| 45 | +The `index.md` is kept in sync with `README.md`: |
| 46 | +```powershell |
| 47 | +.\Scripts\run_update_index.ps1 # Adds YAML header and appends README content |
| 48 | +``` |
| 49 | + |
| 50 | +### Release Process |
| 51 | +Releases are tag-based with version format `vX.Y.Z` or `vX.Y.Z-previewYYYYMMDDHmm`: |
| 52 | +```powershell |
| 53 | +# Preview release |
| 54 | +.\Scripts\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $true |
| 55 | +
|
| 56 | +# Production release |
| 57 | +.\Scripts\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $false |
| 58 | +``` |
| 59 | + |
| 60 | +Pushing tags triggers GitHub Actions workflows (`cd-release.yml`, `cd-release-preview.yml`) that build, test, pack, and publish to NuGet. |
| 61 | + |
| 62 | +## Coding Conventions |
| 63 | + |
| 64 | +### Code Quality Standards |
| 65 | +- **Nullable reference types enabled**: Use `?` for nullable types, handle nulls explicitly |
| 66 | +- **TreatWarningsAsErrors**: All warnings are errors—code must be warning-free |
| 67 | +- **XML documentation required**: Public APIs must have `<summary>` tags (enforced by `GenerateDocumentationFile`) |
| 68 | +- **Deterministic builds**: `<Deterministic>true</Deterministic>` in `Directory.Build.Props` |
| 69 | + |
| 70 | +### Interface Pattern |
| 71 | +All main crypto classes implement interfaces (`ICryptoNetRsa`, `ICryptoNetAes`, `ICryptoNetDsa`) which inherit from `ICryptoNet`. This base interface defines: |
| 72 | +```csharp |
| 73 | +CryptoNetInfo Info { get; } |
| 74 | +byte[] EncryptFromString(string content); |
| 75 | +string DecryptToString(byte[] bytes); |
| 76 | +byte[] EncryptFromBytes(byte[] bytes); |
| 77 | +byte[] DecryptToBytes(byte[] bytes); |
| 78 | +``` |
| 79 | + |
| 80 | +### Constructor Overloading Pattern |
| 81 | +Crypto classes follow a consistent constructor pattern: |
| 82 | +```csharp |
| 83 | +// Self-generated keys |
| 84 | +public CryptoNetRsa(int keySize = 2048) |
| 85 | + |
| 86 | +// String key (XML or PEM) |
| 87 | +public CryptoNetRsa(string key, int keySize = 2048) |
| 88 | + |
| 89 | +// File-based key |
| 90 | +public CryptoNetRsa(FileInfo fileInfo, int keySize = 2048) |
| 91 | + |
| 92 | +// X509 certificate |
| 93 | +public CryptoNetRsa(X509Certificate2? certificate, KeyType keyType, int keySize = 2048) |
| 94 | +``` |
| 95 | + |
| 96 | +### Testing Patterns |
| 97 | +Tests use **NUnit** with **Shouldly** for assertions: |
| 98 | +```csharp |
| 99 | +result.ShouldBe(expected); // NOT Assert.AreEqual |
| 100 | +ExtensionPack.CheckContent(original, decrypted).ShouldBeTrue(); // MD5-based content comparison |
| 101 | +``` |
| 102 | + |
| 103 | +Test resource files are in `Resources/` with `<CopyToOutputDirectory>Always</CopyToOutputDirectory>`. |
| 104 | + |
| 105 | +## Common Pitfalls |
| 106 | + |
| 107 | +### Version Management |
| 108 | +- **Never** hardcode versions in individual `.csproj` files |
| 109 | +- Update versions in `Directory.Build.Props` (global) and `Directory.Packages.props` (package versions) |
| 110 | +- Release versions must match the format `^\d+\.\d+\.\d+$` in `run_release.ps1` |
| 111 | + |
| 112 | +### Cross-Platform Compatibility |
| 113 | +- Core library must remain .NET Standard 2.0 compatible |
| 114 | +- Avoid .NET 8+ specific APIs in `CryptoNet`, `CryptoNet.Models`, `CryptoNet.ExtShared` |
| 115 | +- Use `System.Security.Cryptography` types from .NET Standard 2.0 |
| 116 | + |
| 117 | +### GitHub Actions Workflows |
| 118 | +- **Never add `runs-on` to caller jobs** when using reusable workflows (see `CONTRIBUTING.md`) |
| 119 | +- Reusable workflows declare `on: workflow_call` and define their own `runs-on` |
| 120 | +- Example: `cd-build-test-pack.yml` is a reusable workflow called by release workflows |
| 121 | + |
| 122 | +### File Header Conventions |
| 123 | +All C# files include copyright headers: |
| 124 | +```csharp |
| 125 | +// <copyright file="FileName.cs" company="itbackyard" year="2021"> |
| 126 | +// Copyright (c) 2021 All Rights Reserved |
| 127 | +// </copyright> |
| 128 | +// <author>Maytham Fahmi</author> |
| 129 | +// <date>DD-MM-YYYY HH:MM:SS</date> |
| 130 | +// <summary>part of CryptoNet project</summary> |
| 131 | +``` |
| 132 | + |
| 133 | +## Key Files |
| 134 | +- `Directory.Build.Props`: Global MSBuild properties (version, warnings, symbols) |
| 135 | +- `Directory.Packages.props`: Central package version management |
| 136 | +- `docfx.json`: DocFX configuration for API documentation |
| 137 | +- `RELEASE-NOTES`: Changelog file read during NuGet packing (see `PrepareReleaseNotes` target) |
| 138 | +- `coverlet.runsettings`: Code coverage configuration |
| 139 | + |
| 140 | +## Development Tips |
| 141 | +- Use `ExtShared.LoadFileToString()` for reading key files consistently |
| 142 | +- Use `ExtensionPack.CheckContent()` for MD5-based content validation in tests |
| 143 | +- Key files are stored/loaded using `SaveKey(FileInfo, bool isPrivate)` and constructor overloads |
| 144 | +- The `Info` property exposes algorithm details, key types, and loaded keys for inspection |
| 145 | + |
| 146 | +## Migration Guide: Old API → Latest CryptoNet v3+ |
| 147 | + |
| 148 | +### API Changes Summary |
| 149 | +- **Removed**: `ExportKey()`, `ExportKeyAndSave()` methods |
| 150 | +- **Replaced with**: `GetKey()` and `SaveKey()` methods |
| 151 | +- **Namespace change**: `CryptoNetUtils` → `ExtShared.ExtShared` |
| 152 | +- All crypto classes now expose `Info` property with algorithm details |
| 153 | + |
| 154 | +### AES Examples |
| 155 | + |
| 156 | +#### Old: Encrypt/Decrypt with Symmetric Key |
| 157 | +```csharp |
| 158 | +// OLD API (v2.x) |
| 159 | +ICryptoNet cryptoNet = new CryptoNetAes(); |
| 160 | +var key = cryptoNet.ExportKey(); |
| 161 | +ICryptoNet encryptClient = new CryptoNetAes(key); |
| 162 | +var encrypt = encryptClient.EncryptFromString(data); |
| 163 | +ICryptoNet decryptClient = new CryptoNetAes(key); |
| 164 | +var decrypt = decryptClient.DecryptToString(encrypt); |
| 165 | +``` |
| 166 | + |
| 167 | +#### New: Encrypt/Decrypt with Symmetric Key |
| 168 | +```csharp |
| 169 | +// NEW API (v3.x) |
| 170 | +ICryptoNetAes cryptoNet = new CryptoNetAes(); |
| 171 | +var key = cryptoNet.GetKey(); // Changed from ExportKey() |
| 172 | +
|
| 173 | +ICryptoNetAes encryptClient = new CryptoNetAes(key); |
| 174 | +var encrypt = encryptClient.EncryptFromString(data); |
| 175 | + |
| 176 | +ICryptoNetAes decryptClient = new CryptoNetAes(key); |
| 177 | +var decrypt = decryptClient.DecryptToString(encrypt); |
| 178 | +``` |
| 179 | + |
| 180 | +#### Old: Export and Import Symmetric Key from File |
| 181 | +```csharp |
| 182 | +// OLD API (v2.x) |
| 183 | +ICryptoNet cryptoNet = new CryptoNetAes(); |
| 184 | +var file = new FileInfo("symmetric.key"); |
| 185 | +cryptoNet.ExportKeyAndSave(file); |
| 186 | +var encrypt = cryptoNet.EncryptFromString(data); |
| 187 | +ICryptoNet cryptoNetImport = new CryptoNetAes(file); |
| 188 | +var decrypt = cryptoNetImport.DecryptToString(encrypt); |
| 189 | +``` |
| 190 | + |
| 191 | +#### New: Save and Load Symmetric Key from File |
| 192 | +```csharp |
| 193 | +// NEW API (v3.x) |
| 194 | +ICryptoNetAes cryptoNet = new CryptoNetAes(); |
| 195 | +var file = new FileInfo("symmetric.key"); |
| 196 | +cryptoNet.SaveKey(file); // Changed from ExportKeyAndSave() |
| 197 | +
|
| 198 | +var encrypt = cryptoNet.EncryptFromString(data); |
| 199 | + |
| 200 | +ICryptoNetAes cryptoNetImport = new CryptoNetAes(file); |
| 201 | +var decrypt = cryptoNetImport.DecryptToString(encrypt); |
| 202 | +``` |
| 203 | + |
| 204 | +### RSA Examples |
| 205 | + |
| 206 | +#### Old: Generate RSA Key Pair and Save |
| 207 | +```csharp |
| 208 | +// OLD API (v2.x) |
| 209 | +ICryptoNet cryptoNet = new CryptoNetRsa(); |
| 210 | +cryptoNet.ExportKeyAndSave(new FileInfo("private.key"), true); |
| 211 | +cryptoNet.ExportKeyAndSave(new FileInfo("public.key"), false); |
| 212 | +ICryptoNet pubKeyCrypto = new CryptoNetRsa(new FileInfo("public.key")); |
| 213 | +var encrypt = pubKeyCrypto.EncryptFromString(data); |
| 214 | +ICryptoNet priKeyCrypto = new CryptoNetRsa(new FileInfo("private.key")); |
| 215 | +var decrypt = priKeyCrypto.DecryptToString(encrypt); |
| 216 | +``` |
| 217 | + |
| 218 | +#### New: Generate RSA Key Pair and Save |
| 219 | +```csharp |
| 220 | +// NEW API (v3.x) |
| 221 | +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); |
| 222 | +cryptoNet.SaveKey(new FileInfo("private.key"), true); // Changed from ExportKeyAndSave() |
| 223 | +cryptoNet.SaveKey(new FileInfo("public.key"), false); // Changed from ExportKeyAndSave() |
| 224 | +
|
| 225 | +ICryptoNetRsa pubKeyCrypto = new CryptoNetRsa(new FileInfo("public.key")); |
| 226 | +var encrypt = pubKeyCrypto.EncryptFromString(data); |
| 227 | + |
| 228 | +ICryptoNetRsa priKeyCrypto = new CryptoNetRsa(new FileInfo("private.key")); |
| 229 | +var decrypt = priKeyCrypto.DecryptToString(encrypt); |
| 230 | +``` |
| 231 | + |
| 232 | +### X509 Certificate Examples |
| 233 | + |
| 234 | +#### Old: Use X509 Certificate for Encryption |
| 235 | +```csharp |
| 236 | +// OLD API (v2.x) |
| 237 | +X509Certificate2? cert = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); |
| 238 | +ICryptoNet publicKeyCrypto = new CryptoNetRsa(cert, KeyType.PublicKey); |
| 239 | +var encrypt = publicKeyCrypto.EncryptFromString(data); |
| 240 | +ICryptoNet privateKeyCrypto = new CryptoNetRsa(cert, KeyType.PrivateKey); |
| 241 | +var decrypt = privateKeyCrypto.DecryptToString(encrypt); |
| 242 | +``` |
| 243 | + |
| 244 | +#### New: Use X509 Certificate for Encryption |
| 245 | +```csharp |
| 246 | +// NEW API (v3.x) |
| 247 | +using CryptoNet.ExtShared; // Namespace change: CryptoNetUtils → ExtShared |
| 248 | +
|
| 249 | +X509Certificate2? cert = ExtShared.GetCertificateFromStore("CN=Maytham"); // Changed from CryptoNetUtils |
| 250 | +
|
| 251 | +ICryptoNetRsa publicKeyCrypto = new CryptoNetRsa(cert, KeyType.PublicKey); |
| 252 | +var encrypt = publicKeyCrypto.EncryptFromString(data); |
| 253 | + |
| 254 | +ICryptoNetRsa privateKeyCrypto = new CryptoNetRsa(cert, KeyType.PrivateKey); |
| 255 | +var decrypt = privateKeyCrypto.DecryptToString(encrypt); |
| 256 | +``` |
| 257 | + |
| 258 | +### Key Migration Points |
| 259 | +1. **Method renames**: `ExportKey()` → `GetKey()`, `ExportKeyAndSave()` → `SaveKey()` |
| 260 | +2. **Namespace**: `CryptoNetUtils` → `CryptoNet.ExtShared.ExtShared` |
| 261 | +3. **Interface specificity**: Prefer `ICryptoNetRsa`, `ICryptoNetAes`, `ICryptoNetDsa` over generic `ICryptoNet` |
| 262 | +4. **Info property**: Access algorithm details via `cryptoNet.Info` (includes key types, sizes, loaded keys) |
| 263 | +5. **File operations**: Both `FileInfo` and `string` filename overloads available for `SaveKey()` |
0 commit comments