Close Ed25519 signature-strip attack on Strict verification paths - #523
Merged
Conversation
15 tasks
Production verification now requires Ed25519 signatures on all non-Legacy paths, closing limitation 7 (signature strip → HMAC admit). Changes: - CertificationVerifyOptions.Default: Now fail-closed with RequireEd25519Signature=true and MinimumSchemaVersion=2 - CertificationVerifyOptions.Strict: Added RequireEd25519Signature=true to close the gap where production admit/load paths (FileCertificationRecordStore, CertifiedBrickHotSwapHost, SelfProducedBrickCertificationPolicy) did not enforce Ed25519 - CertificationVerifyOptions.Legacy: New option for backward compatibility, allows HMAC-only records for migration scenarios - Updated XML docs to reflect fail-closed defaults throughout (CertificationVerifyOptions remarks, MinimumSchemaVersion docs, CertificationTrustVerifier param docs, CertificationRecordSigner param docs) - Tests: Added Strict_RejectsRecordWithoutEd25519 and Default_RejectsRecordWithoutEd25519 to prove strip-attack rejection; updated IsStrict test; fixed tests that expected HMAC-only records to verify (now use Legacy) - Docs: Updated certification-evidence.md limitations 7-8 to CLOSED status with 2026-09-06 timestamp Fixes signature-strip attack (limitation 7) and schema-downgrade attack (limitation 8) on all production verification paths. Limitation 9 (CompositionCertificationRecordSigner key discard) remains open and is unaffected by this change. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
DowngradedRecord_WithRewrittenGate_VerifiesWhenNoFloorIsSet was verifying under Default (fail-closed since b90e18e), causing failure. This test demonstrates the legacy-schema hole, so it must use CertificationVerifyOptions.Legacy to accept HMAC-only downgraded records. No gate weakening: Default and Strict still RequireEd25519Signature=true. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
cursorBotforce-pushed
the
cursor/fix-strict-ed25519-requirement-fc3b
branch
from
September 6, 2026 03:14
b90e18e to
f964b41CompareAttestedStateLogBindingTests, CompositionProbeFixtures, and CompositionDogfoodHarness now create Ed25519-signed certification records to satisfy CertificationVerifyOptions.Strict (which requires Ed25519 after 75e5f41). Production code paths (StateLogVerifier, FileCertificationRecordStore, CertifiedBrickRegistry, HotSwapHost, etc.) use Strict and must verify dual-signed records. Fixes cert-gate + kernel-coverage failures on f964b41. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
FileCertificationRecordStoreConcurrencyTests (3 tests): - LoadRefusal_PersistsAVerifiableFail_AndNeverAdmits - ConcurrentSavesOfOneBrick_LeaveAVerifiableRecord_NeverAShreddedOne - AStrayStagingFile_IsNotReadAsARecord GateEmittedArtifactTests: - Loader_RecordsGateEmittedArtifact_AndStrictVerifyRequiresIt Phases14CliE2ETests: - AdaptCommand_DryRun_ExitsZero (SeedAdmittedObservationContextRecord) Root cause: FileCertificationRecordStore.Get() verifies with Default (RequireEd25519Signature=true after 75e5f41). HMAC-only fixtures fail verification and return null, breaking assertions. Fix: ephemeral Ed25519 dual-signing (same pattern as AttestedStateLogBindingTests). Added CreateEd25519Key() helpers, CertificationRecordSigner with private key, and Strict-compliant Inputs. No gate weakening. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
CertificationGateTeethTests: - CreateGate() now creates CertificationRecordSigner with Ed25519 private key - UngatedBrick_IsRejectedByRegistryAdmissionPath uses Ed25519 signer - CertifiedAdmission_OnlyExposesAdmittedBricks uses Ed25519 signer - All gate-produced records now dual-signed (HMAC + Ed25519) GateEmittedArtifactTests (additional fixes): - Gate_RefusesArtifactWhoseHashDoesNotMatchBytes uses Ed25519 signer - Gate_WitnessesActivatedPe_NotCallerBrickInstance uses Ed25519 signer Ensures all certification gate tests produce Strict-compliant records. No gate weakening. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
…ixtures HotSwap family: - CertifiedBrickHotSwapHostTests: CertifyRecord() now uses dual-signing - CertifiedBrickHotSwapAutonomyTests: CertifyRecord() now uses dual-signing - GenerationWatchWindowTests: CertifyRecord() now uses dual-signing Autonomy family: - AutonomyControlsTests: CertifyRecord() now uses dual-signing - AutonomyAdversarialCampaignTests: SignData() + inline gate creation with Ed25519 - AutonomousIterationHarnessTests: Harness() + HoldAdmission test with Ed25519 - AutonomyLedgerScanTests: FileStore test with Ed25519 signer Cross-project & adaptation: - CrossProjectReuseTests: ProjectACertifiesDamageResolverAsync with Ed25519 - DamageResolverDogfoodTests: CreateService with Ed25519 All fixtures now produce Strict-compliant dual-signed records (HMAC + Ed25519). Added CreateEd25519Key() helper to all modified test files. No Legacy usage on production paths. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
Additional gate test files updated for Ed25519 dual-signing: - CertificationGateSessionExecutionTests: Gate() helper with Ed25519 - AnalyzerGateAdversarialCampaignTests: CreateGate() helper with Ed25519 - AdversarialCorpusTests: inline gate creation with Ed25519 - DiagnosticProbeTests: all gate instantiations with Ed25519 All certification gate tests now produce Strict-compliant dual-signed records. Added CreateEd25519Key() helper to all modified test files. No Legacy usage. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
- GateEmittedArtifactTests: add missing 'using NSec.Cryptography;' - Fix SignRecord type mismatch (CS1503): change CertificationRecordData to CertificationRecord in object initializers for 5 files: * GenerationWatchWindowTests.cs (CertifyRecord helper) * CertifiedBrickHotSwapHostTests.cs (CertifyRecord helper) * CertifiedBrickHotSwapAutonomyTests.cs (CertifyRecord helper) * AutonomyControlsTests.cs (CertifyRecord helper) * AutonomyAdversarialCampaignTests.cs (SignData helper) SignRecord() expects CertificationRecord, not CertificationRecordData. All helpers now create CertificationRecord objects before signing. No gate weakening. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
Fixed all 5 failing CertifyRecord/SignData helpers by following the working pattern from AttestedStateLogBindingTests.CreateTrustedRecord: - Changed from using SignRecord() (which expects CertificationRecord) to manual dual-signing of CertificationRecordData - Create CertificationRecordData object directly - Sign with CertificationRecordSigning.Sign() for HMAC - Sign with CertificationRecordEd25519.Sign() for Ed25519 - Include Ed25519PublicKey in record before signing - Return CertificationRecordData (not CertificationRecord) Files fixed: - GenerationWatchWindowTests.cs: CertifyRecord helper - CertifiedBrickHotSwapHostTests.cs: CertifyRecord helper - CertifiedBrickHotSwapAutonomyTests.cs: CertifyRecord helper - AutonomyControlsTests.cs: CertifyRecord helper - AutonomyAdversarialCampaignTests.cs: SignData helper Build verified successful. No gate weakening. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
Fixed 2 REAL test failures (453 tests passing) by adding Ed25519 dual-signing: 1. CertifiedBrickHotSwapHostTests.Swap_MismatchedPrecompiledAssembly_RematerializesCertifiedSource - Changed HMAC-only inline fixture to dual-signed - Added CreateEd25519Key() call to generate key pair - Added Ed25519PublicKey to unsigned record - Added Ed25519Signature using CertificationRecordEd25519.Sign() - Follows AttestedStateLogBindingTests.CreateTrustedRecord pattern 2. Adaptation.GenerationSafetyTests.GoodGeneration_StrongWitness_Admits_WithZeroEscapeRate - CreateService() now creates CertificationRecordSigner with Ed25519 private key - Added 'using NSec.Cryptography;' - Added CreateEd25519Key() helper method - Registry now accepts Ed25519-signed records Both fixes mirror the working dual-sign pattern from AttestedStateLogBindingTests. Build verified successful. No Legacy. No gate weakening. Default/Strict remain fail-closed. Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
…d25519-requirement-fc3b Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Sep 6, 2026
cursorBot
pushed a commit
that referenced
this pull request
Sep 6, 2026
- Resolved docs/dogfood-ledger.md: preserved master's 2026-09-05 entry, updated 2026-09-06 entry to reflect #523 now on master - Resolved docs/DocsIndex.md: merged both versions of trust loop section - All other changes from master merged cleanly Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
cursorBot
pushed a commit
that referenced
this pull request
Sep 6, 2026
- Updated workflow comments: #523 is now on master (merge commit 966e6bf) - Updated dogfood-continuous-proof.sh: removed #523 as blocker, kept honest about stub + lim-9 - Updated dogfood-scorecard.md: marked #523 as merged, updated status section - Kept honest gaps: canary sweep script still stub, lim-9 status TBD - No weakening of cert-gate or thresholds Co-authored-by: IanFrelinger <IanFrelinger@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the security gap where production verification paths using
CertificationVerifyOptions.Strictdid not enforce Ed25519 signatures, leaving them vulnerable to signature-strip attacks (limitation 7). BothDefaultandStrictare now fail-closed, requiring Ed25519 signatures and trust-loop schema (v2+). This closes limitations 7 (signature strip) and 8 (schema downgrade) documented indocs/certification-evidence.md.Problem
Prior state (per issue description):
CertificationVerifyOptions.Defaultwas not fail-closed (contrary to claims in some documentation)CertificationVerifyOptions.StrictsetMinimumSchemaVersion=2and required gate/certifier inputs, but did NOT require Ed25519 signaturesed25519Signaturefield from a record, recomputes HMAC (using committed dev key or captured key), and the record verifies on production pathsChanges
CertificationVerifyOptions.Default: Now fail-closed withRequireEd25519Signature=trueandMinimumSchemaVersion=2CertificationVerifyOptions.Strict: AddedRequireEd25519Signature=trueto close production path gapCertificationVerifyOptions.Legacy: New option for backward compatibility; allows HMAC-only records for migration scenariosCertificationVerifyOptionsclass remarks (no longer claims "every default reproduces today's behaviour")MinimumSchemaVersiondocs (clarified default only applies to Legacy)CertificationTrustVerifier.Verifyparam docs (Default is now fail-closed)CertificationRecordSigner.Verifyparam docs (Default is now fail-closed)Strict_RejectsRecordWithoutEd25519proving strip-attack rejection under StrictDefault_RejectsRecordWithoutEd25519proving strip-attack rejection under DefaultLegacy_AllowsHmacOnlyRecordsfor migration path coverageIsStrict_ReflectsConfiguredStrictness(Default is now strict)DefaultOptions_AreFailClosed(wasReproduceTodaysBehaviour, now tests fail-closed behavior)LegacyRecord_SignsAndVerifies,V2Record_HmacOnly_Verifies, and gate tests to use Legacy where appropriatedocs/certification-evidence.mdlimitation 7: Now CLOSED as of 2026-09-06Testing
SchemaVersionFloorTests.csproving strip-attack rejection under Default and StrictTrustLoopRecordSchemaTests.csto use Legacy where HMAC-only records are intentionaled25519-signature-requiredunder Default and StrictTesting strategy (blast radius)
SchemaVersionFloorTests.csmake kernel-coverage-gate(touchingsrc/Ashlar.Certification.Contractsandsrc/Ashlar.Infrastructure/Certification)Note: Cannot run tests locally in current environment (dotnet SDK unavailable in cloud agent). Tests follow existing patterns in
SchemaVersionFloorTestsandTrustLoopRecordSchemaTests; CI will validate.Checklist
certification-evidence.mdlimitations 7-8 marked CLOSED)TODOorNotImplementedExceptionmake testpasses locally (pending CI — dotnet unavailable in agent environment)Release
Notes
DefaultandStrictfail-closed by default. Deployments that need to verify HMAC-only (pre-trust-loop) records during migration must explicitly useCertificationVerifyOptions.Legacy..Verify()with no options, defaulting toDefault, so they now reject signature-stripped records.