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:
Vendored
+190
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"_comment": "Generated by DodoSSH.Crypto.Tests.GoldenVectors. Normative source: docs/crypto.md.",
|
||||
"_warning": "A failing assertion here is a regression or an intentional versioned format change. Do not regenerate to make it pass.",
|
||||
"specVersion": 1,
|
||||
"aad": [
|
||||
{
|
||||
"name": "item-payload",
|
||||
"purpose": 4,
|
||||
"resourceType": 5,
|
||||
"resourceId": "0192f0c8-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
|
||||
"keyId": "0192f0c8-9999-7aaa-8bbb-cccccccccccc",
|
||||
"keyGeneration": 7,
|
||||
"itemVersion": 3,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0104050192f0c81a2b7c3d8e4f5a6b7c8d9e0f0192f0c899997aaa8bbbcccccccccccc000000070000000300010000000000000000000000000000",
|
||||
"aad": "bb106e753e2fd9ac31142889a4356cbf9fc1f8db3777a1921ecbb5481bd4379e"
|
||||
},
|
||||
{
|
||||
"name": "item-payload-other-resource",
|
||||
"purpose": 4,
|
||||
"resourceType": 5,
|
||||
"resourceId": "0192f0c8-1a2b-7c3d-8e4f-5a6b7c8d9e10",
|
||||
"keyId": "0192f0c8-9999-7aaa-8bbb-cccccccccccc",
|
||||
"keyGeneration": 7,
|
||||
"itemVersion": 3,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0104050192f0c81a2b7c3d8e4f5a6b7c8d9e100192f0c899997aaa8bbbcccccccccccc000000070000000300010000000000000000000000000000",
|
||||
"aad": "ae87f8da1a55b36286ed103a11fb51145adff18a222557db30422918eba6c29f"
|
||||
},
|
||||
{
|
||||
"name": "item-metadata",
|
||||
"purpose": 5,
|
||||
"resourceType": 4,
|
||||
"resourceId": "0192f0c8-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
|
||||
"keyId": "00000000-0000-0000-0000-000000000000",
|
||||
"keyGeneration": 1,
|
||||
"itemVersion": 1,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0105040192f0c81a2b7c3d8e4f5a6b7c8d9e0f00000000000000000000000000000000000000010000000100010000000000000000000000000000",
|
||||
"aad": "cfb042451484a4f484b45e812a2c7667d12592bcb8ec47be5b5ef95c38b1d3ad"
|
||||
},
|
||||
{
|
||||
"name": "vault-key-grant",
|
||||
"purpose": 2,
|
||||
"resourceType": 3,
|
||||
"resourceId": "0192f0c8-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
|
||||
"keyId": "00000000-0000-0000-0000-000000000000",
|
||||
"keyGeneration": 2,
|
||||
"itemVersion": 0,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0102030192f0c81a2b7c3d8e4f5a6b7c8d9e0f00000000000000000000000000000000000000020000000000010000000000000000000000000000",
|
||||
"aad": "5c9444daa7f74193b04abefb57d5a49a8782e8e1a7fd97771865f9e038f8c957"
|
||||
},
|
||||
{
|
||||
"name": "user-secret-bundle",
|
||||
"purpose": 1,
|
||||
"resourceType": 1,
|
||||
"resourceId": "0192f0c8-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
|
||||
"keyId": "00000000-0000-0000-0000-000000000000",
|
||||
"keyGeneration": 1,
|
||||
"itemVersion": 0,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0101010192f0c81a2b7c3d8e4f5a6b7c8d9e0f00000000000000000000000000000000000000010000000000010000000000000000000000000000",
|
||||
"aad": "9f73034823c49cdfcad4fcc75e67ae22be151a92afed72ab7548097ef5a99f68"
|
||||
},
|
||||
{
|
||||
"name": "all-zero-ids",
|
||||
"purpose": 6,
|
||||
"resourceType": 0,
|
||||
"resourceId": "00000000-0000-0000-0000-000000000000",
|
||||
"keyId": "00000000-0000-0000-0000-000000000000",
|
||||
"keyGeneration": 0,
|
||||
"itemVersion": 0,
|
||||
"aadVersion": 1,
|
||||
"schemaVersion": 1,
|
||||
"canonicalEncoding": "647368310a0106000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000",
|
||||
"aad": "cebc8d57709c0ebe47874c85fc39aa538e17c4202b767673c8636ef181cd1664"
|
||||
}
|
||||
],
|
||||
"envelope": [
|
||||
{
|
||||
"name": "xchacha20poly1305",
|
||||
"algId": 1,
|
||||
"nonce": "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7",
|
||||
"ciphertext": "000102030405060708090a0b0c0d0e0f10111213",
|
||||
"prefixSize": 30,
|
||||
"envelope": "445348310100a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7000102030405060708090a0b0c0d0e0f10111213"
|
||||
},
|
||||
{
|
||||
"name": "aes256gcm",
|
||||
"algId": 2,
|
||||
"nonce": "b0b1b2b3b4b5b6b7b8b9babb",
|
||||
"ciphertext": "000102030405060708090a0b0c0d0e0f10111213",
|
||||
"prefixSize": 18,
|
||||
"envelope": "445348310200b0b1b2b3b4b5b6b7b8b9babb000102030405060708090a0b0c0d0e0f10111213"
|
||||
},
|
||||
{
|
||||
"name": "sealto-x25519",
|
||||
"algId": 3,
|
||||
"nonce": "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7",
|
||||
"ephemeralPublicKey": "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf",
|
||||
"ciphertext": "000102030405060708090a0b0c0d0e0f10111213",
|
||||
"prefixSize": 62,
|
||||
"envelope": "445348310300c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7000102030405060708090a0b0c0d0e0f10111213"
|
||||
}
|
||||
],
|
||||
"aead": [
|
||||
{
|
||||
"name": "xchacha20poly1305-with-canonical-aad",
|
||||
"key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"nonce": "101112131415161718191a1b1c1d1e1f2021222324252627",
|
||||
"aad": "bb106e753e2fd9ac31142889a4356cbf9fc1f8db3777a1921ecbb5481bd4379e",
|
||||
"plaintext": "636f727265637420686f727365206261747465727920737461706c65",
|
||||
"ciphertext": "4793718431eb55f3feed50be98b0416d7bff929d804d53a7873495132465b6b1da6e73e042821964543ecd90",
|
||||
"envelope": "445348310100101112131415161718191a1b1c1d1e1f20212223242526274793718431eb55f3feed50be98b0416d7bff929d804d53a7873495132465b6b1da6e73e042821964543ecd90"
|
||||
}
|
||||
],
|
||||
"hkdf": [
|
||||
{
|
||||
"name": "passphrase-kek",
|
||||
"algorithm": "HKDF-SHA512-Expand",
|
||||
"prk": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"info": "dsh1/kek/passphrase/v1",
|
||||
"outputLength": 32,
|
||||
"output": "652b3a4a3ce03b235095ad32f1eed2cfdae915b5b0a98cc9f96face30853f4c7"
|
||||
},
|
||||
{
|
||||
"name": "local-cache",
|
||||
"algorithm": "HKDF-SHA512-Expand",
|
||||
"prk": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"info": "dsh1/localcache/v1",
|
||||
"outputLength": 32,
|
||||
"output": "5b69ed9266ff5f297f11667ca693b0049b805365ee34d54d6e60b843e414b1f5"
|
||||
}
|
||||
],
|
||||
"argon2id": [
|
||||
{
|
||||
"name": "passphrase-default",
|
||||
"memoryMebibytes": 256,
|
||||
"memoryKibibytes": 262144,
|
||||
"passes": 4,
|
||||
"parallelism": 1
|
||||
},
|
||||
{
|
||||
"name": "passphrase-reduced",
|
||||
"memoryMebibytes": 128,
|
||||
"memoryKibibytes": 131072,
|
||||
"passes": 3,
|
||||
"parallelism": 1
|
||||
},
|
||||
{
|
||||
"name": "passphrase-high",
|
||||
"memoryMebibytes": 512,
|
||||
"memoryKibibytes": 524288,
|
||||
"passes": 4,
|
||||
"parallelism": 1
|
||||
},
|
||||
{
|
||||
"name": "random-secret",
|
||||
"memoryMebibytes": 64,
|
||||
"memoryKibibytes": 65536,
|
||||
"passes": 3,
|
||||
"parallelism": 1
|
||||
},
|
||||
{
|
||||
"name": "random-secret-output",
|
||||
"memoryMebibytes": 64,
|
||||
"memoryKibibytes": 65536,
|
||||
"passes": 3,
|
||||
"parallelism": 1,
|
||||
"passphrase": "correct horse battery staple",
|
||||
"salt": "202122232425262728292a2b2c2d2e2f",
|
||||
"outputLength": 32,
|
||||
"output": "3573a601a50874c6c4222082d040f039ba4f557a0151e0357e8abb66fed7b29e"
|
||||
}
|
||||
],
|
||||
"fingerprint": [
|
||||
{
|
||||
"name": "identity-fingerprint",
|
||||
"x25519PublicKey": "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
|
||||
"ed25519PublicKey": "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f",
|
||||
"fingerprint": "fe8d8673f517688bf0d5d9b812327619a303c765af1f47dbd6a777db193c36e5"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user