Files
DodoSSH/Directory.Packages.props
T
jaap-jan b15af836a3 Freeze DSH1 crypto specification and implement the core (M1)
docs/crypto.md is now the normative, frozen specification. This had to land before
anything else in M1: the server holds ciphertext and no keys, so it can never
re-encrypt, and a format change after users hold data is a coordinated client rewrite
with no rollback.

Specification:
- DSH1 envelope layout, canonical 64-byte AAD encoding, SealTo construction, key
  hierarchy, Argon2id profiles, fingerprints, and the change rules for each version field.
- AAD encoding is fixed-width binary rather than delimited string concatenation, so no
  field value can forge a field boundary. This supersedes the illustrative form sketched
  in ADR 0001, which now points here.
- UUIDs are RFC 4122 big-endian. Guid.ToByteArray() emits the first three groups
  little-endian and would have made our ciphertext unreadable by any other implementation
  of this spec, failing only at a cross-implementation boundary.

Verified rather than assumed:
- PrimitiveAvailabilityTests proves X25519, Ed25519, XChaCha20-Poly1305, Argon2id and
  HKDF-SHA512 all function on net10.0. NSec 26.4.0 targets net9.0 and is consumed by
  forward compatibility; this closes one of the two package questions the plan flagged.
- Argon2Profile exists because NSec's MemorySize is in KIBIBYTES, not bytes. Passing bytes
  gives either a 256 GiB allocation or a 256 KiB KDF that cracks instantly. The type takes
  mebibytes so the unit cannot be got wrong at a call site. Found by benchmarking: the
  first measurements were ~1000x too slow, which turned out to be 19 GiB of work.
- Parameters measured, not guessed: 256 MiB/t=4 is 323 ms on this machine; the table of
  candidates is in the spec.

Implementation and tests (83 total, up from 17):
- AadDescriptor, DshEnvelope, DshCrypto (Seal/Open/SealTo/OpenSealed/fingerprints).
- Decryption returns null rather than throwing: ciphertext comes from a server that is
  explicitly not trusted, so a failed tag is an expected outcome.
- Envelope readers reject unknown algorithms and any non-zero flag bit, so an envelope
  that is not fully understood fails closed.
- Executable form of the spec's substitution claims: a server cannot move ciphertext
  between resources, roll back a key generation or item version, repurpose a payload as
  metadata, or confuse the two constructions.
- Golden vectors in tests/fixtures/crypto/vectors.json guard the format. Mutation-checked:
  a one-byte schema version change trips four tests including the guard.

Two build-infrastructure bugs found and fixed along the way:
- .editorconfig forced camelCase on const and static readonly fields. PascalCase is the
  .NET convention for both; the config was wrong, not the code.
- The golden fixture was resolved with [CallerFilePath], which ContinuousIntegrationBuild
  rewrites to /_/... under deterministic source paths. It passed locally and would have
  failed only in CI. Now copied to the output directory and read from there.
2026-07-28 13:18:29 +02:00

70 lines
3.2 KiB
XML

<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<!--
Versions are pinned here for the whole solution. Packages are added per milestone
rather than all at once, so that every entry is one we have actually verified and
restored. See docs/adr/ for the choices behind the notable ones.
-->
<ItemGroup Label="ASP.NET Core">
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
</ItemGroup>
<ItemGroup Label="Pinned transitive dependencies">
<!--
Microsoft.AspNetCore.OpenApi 10.0.10 resolves Microsoft.OpenApi 2.0.0, which is
covered by GHSA-v5pm-xwqc-g5wc (high: circular schema references can terminate
OpenAPI parsing; vulnerable <= 2.7.4, patched in 2.7.5). Pinned forward within the
2.x major that ASP.NET Core 10 targets. Revisit when the ASP.NET Core package
itself moves off 2.0.0.
-->
<PackageVersion Include="Microsoft.OpenApi" Version="2.11.0" />
</ItemGroup>
<ItemGroup Label="Cryptography">
<!--
NSec wraps libsodium. Chosen over the BCL because .NET has no X25519 or Ed25519, and
because ChaCha20Poly1305.IsSupported is false on macOS, which rules out the in-box
AEAD for a cross-platform client. NSec also holds key material in libsodium's
guarded, non-swappable memory, which a byte[] cannot do. See docs/crypto.md.
26.4.0 targets net9.0; net10.0 consumes it by forward compatibility. Native binaries
arrive via the libsodium package, pinned here because central transitive pinning
requires it to be declared.
-->
<PackageVersion Include="NSec.Cryptography" Version="26.4.0" />
<PackageVersion Include="libsodium" Version="1.0.22" />
<!-- Managed differential oracle for the crypto test suite only. -->
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
</ItemGroup>
<ItemGroup Label="Analyzers">
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="5.6.0" />
<PackageVersion Include="Meziantou.Analyzer" Version="3.0.134" />
</ItemGroup>
<ItemGroup Label="Testing">
<!--
xunit.v3 runs on Microsoft.Testing.Platform, not VSTest. Microsoft.NET.Test.Sdk and
coverlet.collector are VSTest components: referencing them alongside MTP raises
MTP0001 and their collector never runs, so neither is referenced.
No coverage collector yet. Microsoft.Testing.Extensions.CodeCoverage 18.9.0 pulls
Microsoft.Testing.Platform.MSBuild 1.9.1, which is built against MTP 1.x and throws
TypeLoadException on IDataConsumer against the MTP 2.3.x that xunit.v3 3.2.2 brings.
Coverage gates are an M3 concern (90% on Domain and Authorization); pick a version
aligned with MTP 2.x then rather than carrying a broken dependency until it matters.
-->
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="NSubstitute" Version="6.0.0" />
</ItemGroup>
</Project>