Public Access
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.
This commit is contained in:
@@ -62,6 +62,26 @@ dotnet_naming_symbols.any_interface.applicable_kinds = interface
|
||||
dotnet_naming_style.starts_with_i.required_prefix = I
|
||||
dotnet_naming_style.starts_with_i.capitalization = pascal_case
|
||||
|
||||
# Constants and static readonly fields are PascalCase, per .NET convention. These rules must
|
||||
# come before the camelCase rule below: the first matching rule wins, and a rule matching all
|
||||
# private fields would otherwise force `const int Foo` to be named `foo`.
|
||||
dotnet_naming_rule.constants_are_pascal_case.severity = warning
|
||||
dotnet_naming_rule.constants_are_pascal_case.symbols = any_const_field
|
||||
dotnet_naming_rule.constants_are_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.any_const_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.any_const_field.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.any_const_field.required_modifiers = const
|
||||
|
||||
dotnet_naming_rule.static_readonly_fields_are_pascal_case.severity = warning
|
||||
dotnet_naming_rule.static_readonly_fields_are_pascal_case.symbols = static_readonly_field
|
||||
dotnet_naming_rule.static_readonly_fields_are_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.static_readonly_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_readonly_field.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.static_readonly_field.required_modifiers = static, readonly
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# Private instance fields are camelCase.
|
||||
dotnet_naming_rule.private_fields_are_camel_case.severity = warning
|
||||
dotnet_naming_rule.private_fields_are_camel_case.symbols = private_field
|
||||
dotnet_naming_rule.private_fields_are_camel_case.style = camel_case_style
|
||||
|
||||
Reference in New Issue
Block a user