up:: Migration Architecture MOC
The Strip Test
The strip test is a pre-deployment check where you run the downgrade attack on your own hybrid rollout before shipping it: you remove the post-quantum option from the negotiation, offer only classical suites, and confirm the deployment either refuses the connection or trips its downgrade protection rather than quietly completing on classical-only. It exists because a hybrid deployment that silently falls back to classical key exchange under pressure looks exactly like a healthy connection while running the cryptography you were trying to retire, and nothing in the logs flags it. The strip test forces that behavior out into the open in a lab, where a failure is a finding, before an attacker or a middlebox forces it in production, where the same failure is a quietly harvested recording.
The short version:
- You attack your own deployment: strip the post-quantum key-exchange option out of the offer, present only classical suites, and watch what the deployment does.
- A safe deployment fails loud. It either refuses the connection or trips a downgrade tripwire that aborts the handshake. An unsafe one fails quiet, completing on classical-only with a green padlock and no error.
- TLS 1.3 ships a downgrade tripwire, a sentinel the server plants in its handshake random when pushed to an older version, and the strip test verifies both halves are live, because the server can plant the marker while the client skips the check.
- The rule underneath is the minimum-floor rule: a system that negotiates its cryptography is only safe if its policy carries a floor it refuses to drop below. A negotiated deployment with no floor is downgradeable by construction.
- The pass or fail is read the same way a good inventory reads: off what was actually negotiated on the wire, never off what the configuration permits, which is the Wire Over Config instrument applied to an attack you staged.
Think of testing a smoke alarm by holding a smoldering match under it. Nobody trusts an alarm because it is installed and its light is green. You make smoke on purpose and confirm it screams. The failure you are really hunting is the alarm that stays silent, because a silent alarm during a real fire is indistinguishable from no fire at all, right up until the building is gone. The strip test holds smoke under a hybrid deployment. It manufactures the exact downgrade an adversary would attempt and confirms the deployment screams instead of swallowing it, so a silent fallback gets caught in the lab rather than discovered from a decrypted archive years later.
What is the strip test?
The strip test is a named pre-ship verification move for any deployment that negotiates its cryptography, run against your own systems in a controlled environment before the rollout reaches real traffic. It has three components.
- The attack you run. You act as the adversary you are defending against. You remove the post-quantum or hybrid key-exchange group from what the client offers, or strip it from the path, so the negotiation is left with only classical options, exactly the state a machine-in-the-middle or a hostile middlebox would engineer.
- The behavior you require. The deployment has to fail in a way an operator can see. Acceptable outcomes are a refused connection or a downgrade protection that fires and aborts the handshake. The unacceptable outcome is a session that completes on classical-only cryptography and reports success.
- The evidence you read. You judge the result on the negotiated outcome captured on the wire, never on what the configuration says is permitted, because the whole failure mode being hunted is a config that permits a safe handshake while a real negotiation quietly produces an unsafe one. This is the Wire Over Config rule turned on a test you staged.
The test applies to every protocol that chooses its cryptography per connection during the transition, which is nearly all of them: TLS, SSH, IPsec, QUIC, anything running hybrid or dual-mode while classical peers still exist. It applies most sharply to links carrying long-lived secrets, where a silent classical fallback reopens the exact harvest-now-decrypt-later window the migration was meant to close.
It does not apply where there is nothing to strip. A protocol with a single fixed algorithm and no negotiation has no menu an attacker can edit, so the strip test has no move to make there, and its job passes to a configuration review instead. That limit is worked out in full below.
The strip test is one instrument inside a larger pre-ship exercise that also builds the interoperability matrix, sets the floor, and instruments the wire across a staged rollout. The portable, reusable piece of that exercise, the part a stranger can carry to their own systems without any of the rest, is this test.
Why does a hybrid deployment have to fail loud rather than quiet?
Because a downgrade’s entire danger is that the downgraded session succeeds normally, so the only way it announces itself is if you build it to. Hybrid key exchange runs a classical algorithm and a post-quantum one together, and the connection is protected as long as either half genuinely contributes to the session key. The security property holds only when the failure of that combination is visible. A hybrid that quietly drops to a classical group when the post-quantum half is removed has failed open: the endpoint believes it negotiated hybrid while running classical-only, with no way to know the difference.
The two failure directions are worth stating side by side, because the whole test is built to prefer one over the other.
- Fail loud (the safe direction). When the post-quantum option is stripped, the deployment refuses the connection or aborts with a visible error. A human sees a failure, opens a ticket, and fixes the cause. A loud failure on an HNDL-critical link is a good day, because it means the floor held.
- Fail quiet (the dangerous direction). When the post-quantum option is stripped, the deployment completes the handshake on classical-only cryptography. The user sees a padlock, the dashboard reads healthy, and the traffic is now harvestable by anyone recording the wire for a future quantum computer. There is no alarm and no attacker to catch, because the endpoint made the downgrade decision itself.
From a harvest-now-decrypt-later standpoint the quiet fallback and a successful active strip leave the identical recording on the wire, so they belong in the same threat model even though only one involves an adversary. The reason the quiet path is the expensive one is that it lies to every layer above it. A refused connection costs you an availability incident you can measure and schedule. A silent fallback costs you a false migration report and an exposure that persists precisely because it looks like success. The strip test exists to convert the second into the first before you ship.
How does the TLS 1.3 downgrade tripwire actually work?
TLS 1.3 carries a built-in downgrade-protection sentinel, and the strip test’s job is to confirm both halves of it are live, because the mechanism only protects a connection when the server plants the marker and the client checks it. Either half dead means no protection, and implementations have shipped with the client-side check missing.
The mechanism, from RFC 8446 §4.1.3, works by having the server leave a fingerprint when it is forced down to an older protocol version.
- The server plants a sentinel. A TLS 1.3-capable server that ends up negotiating an older version writes a fixed marker into the last 8 bytes of its ServerHello random value, the field otherwise filled with random bytes. The marker is the ASCII string
DOWNGRDfollowed by a version byte. - The client checks for it. A client that supports TLS 1.3 but is told it landed on TLS 1.2 or below must inspect those last 8 bytes, and if it finds the sentinel it must abort the handshake with an
illegal_parameteralert, because the sentinel means a real TLS 1.3 server was pushed downhill by something on the path.
| Server negotiates | Last 8 bytes of ServerHello.random | ASCII | A TLS 1.3 client seeing this must |
|---|---|---|---|
| TLS 1.2 | 44 4F 57 4E 47 52 44 01 | DOWNGRD + 01 | Abort with illegal_parameter |
| TLS 1.1 or below | 44 4F 57 4E 47 52 44 00 | DOWNGRD + 00 | Abort with illegal_parameter |
Source: E. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.3,” IETF, RFC 8446 §4.1.3, rfc-editor.org, August 2018.
The reason the strip test checks both halves independently is that they fail independently. A server library that plants the sentinel correctly is defeated entirely by a client library that never reads it, and that exact combination has shipped: code that emits the marker paired with a peer that silently accepts the downgrade. Confirming the server plants it proves nothing about whether your client aborts, so the test forces a version rollback and watches for the abort on the client you actually deploy, not on a reference implementation.
Two boundaries keep this honest.
- The sentinel protects against a forced rollback of the protocol version. The related protection against an active attacker stripping a strong group inside a genuine TLS 1.3 handshake is the Finished-message transcript MAC, covered in Downgrade and Rollback Attacks, which breaks the connection if the negotiation was edited in flight.
- Neither mechanism catches a client that voluntarily retries classical-only after a middlebox drops an oversized post-quantum ClientHello, because that transcript is honest and no version was rolled back. That voluntary fallback is exactly the case the strip test surfaces by policy, which is where the floor comes in.
What is the minimum-floor rule?
The minimum-floor rule is the principle the strip test proves: a system that negotiates its cryptography is only safe if its policy defines a floor below which it refuses to operate, and a negotiated system without such a floor is downgradeable by construction. Negotiation is a menu, and a menu with no bottom will eventually serve the bottom, because some client, middlebox, or attacker will always ask for it.
A crypto-agile deployment gets its agility from being willing to speak more than one algorithm, and that same willingness is what a downgrade exploits. The floor is what reconciles the two. It says the deployment may negotiate freely above a defined line and must refuse everything below it, which turns a silent fallback into a visible refusal on the paths where that matters.
The floor is a per-path policy decision, made by data class rather than set once for the whole estate.
- On links carrying long-lived secrets, where harvest-now-decrypt-later is the whole threat, the floor is hard: refuse a connection that cannot establish post-quantum or hybrid key exchange, so a downgrade becomes an actionable error rather than a quiet exposure.
- On general public traffic, where reachability by every legacy client is the priority and the data has a short secrecy life, a deliberate decision to allow classical fallback is defensible, provided it is a decision on the record and not an accident the strip test happened to reveal.
The strip test is how you find out which one you actually have. A deployment that refuses under the strip has a floor. A deployment that completes classical-only under the strip has none, whatever the intent was, and the fix is to set the floor and re-run the test until the refusal is real.
What are the three things people call “tested”?
The word “tested” hides three separate claims, and only one of them is about your estate, so the strip test lives specifically inside that third claim. Keeping them straight is the difference between “a lab somewhere ran a test” and “my deployment was validated.”
| What “tested” means | Who establishes it | What it proves | Whose fact it is |
|---|---|---|---|
| Algorithm-validated | NIST ACVP | The primitive computes correct outputs on known inputs | A product’s, inherited when you buy it |
| Module-validated | NIST CMVP | The whole cryptographic module meets FIPS 140-3 | A product’s, the certificate a federal buyer checks |
| Your-deployment-tested | Only you | Your systems negotiate the algorithm end to end, across your real paths, and refuse to be downgraded | Yours, and no external certificate covers it |
Source: NIST, “Cryptographic Algorithm Validation Program,” csrc.nist.gov; NIST, “Cryptographic Module Validation Program,” csrc.nist.gov.
The first two are real and necessary, and they are established by someone else and handed to you when you procure the product. Neither says a word about whether your particular deployment can be forced back onto classical cryptography, because that depends on your configuration, your client population, and your paths. The strip test is a your-deployment test, the third row, and it is the row nobody else can run for you. Treating a CMVP certificate as if it answered the third question is the most common way a team convinces itself a downgradeable rollout was validated. The full stack of validation layers and what each does and does not cover is in Testing as the Control Surface.
What does the strip test read off the wire?
The strip test passes or fails on the negotiated outcome captured on the wire, never on the configuration, for the same reason a cryptographic inventory does: the configuration states what the deployment is permitted to do, and the wire records what it did under the attack you staged. This is the Wire Over Config rule, and the strip test is the case where it matters most, because the config will almost always look correct. A config that lists the hybrid group first and retains classical suites for compatibility is a perfectly ordinary, review-passing config, and it is also a config that permits exactly the silent fallback the strip test is designed to catch.
So the observable that decides the result is the negotiated key-exchange group in the handshake, read from a packet capture or from termination-tier logs. Under a genuine strip, a passing deployment produces either no completed handshake at all, or an aborted one with the downgrade sentinel fired. A failing deployment produces a completed handshake whose negotiated group is a classical curve. The config predicted a safe handshake in every case, which is precisely why reading the config is not the test.
How do you run the strip test on a real endpoint?
Run it end to end on a generic case. Take a public API edge that has enabled X25519MLKEM768, listed the hybrid group first, retained TLS 1.2 for compatibility, installed an ECDSA certificate, and passed both its internal configuration review and an external scan. On paper it is a migrated endpoint. The strip test asks whether it stays migrated when the post-quantum option is taken away.
You run three probes and read each one off the wire.
| Probe | What you send | The passing outcome | This endpoint before a floor |
|---|---|---|---|
| 1. Strip the hybrid group | A client offer whose supported_groups lists classical curves only, no hybrid group | Refuse the connection on an HNDL-critical path, or complete only if this path is a documented classical-fallback path | Completes on classical x25519, TLS 1.3, no error, a silent classical downgrade |
| 2. Force a version rollback | A client that offers a maximum version of TLS 1.2 | Server plants the DOWNGRD sentinel and the deployed client aborts with illegal_parameter | Server plants it correctly; the deployed client library has to be confirmed to actually check it |
| 3. Re-test with a floor set | The probe 1 offer again, after adding a minimum-group policy to the listener class | Server refuses with handshake_failure, no acceptable group | Refuses, the fail-loud outcome you were after |
Probe 1 is the heart of it. The endpoint that passed everything completes a classical-only handshake the moment the hybrid group is absent, because compatibility fallback is the default behavior of a TLS server and nothing in the config forbade it. On a link carrying long-lived secrets, that completed handshake is a fail, and it was invisible to every check that came before, because the config permitted a safe handshake and the scanner, being a modern client, always offered the hybrid group and always got one.
Probe 2 exposes the tripwire’s live-ness. The server plants the sentinel correctly, which tells you the server half works, and tells you nothing about the client half. You then point the client library you actually deploy at a server forced to 1.2 and confirm it aborts, because the client that skips the check is the shipped bug the mechanism keeps producing.
Probe 3 closes the loop. You set a minimum-group floor on that listener class, declaring that connections which cannot establish hybrid key exchange are refused, and you re-run probe 1. The refusal is the receipt that the floor is real, established on the wire rather than asserted in a policy document. The endpoint that “passed everything” is now genuinely fail-safe on that path, and you have the captures to prove it, which is a claim the original config review could never make.
Common misconceptions
- “We tested it and it passed, so the rollout is safe.” Three different things wear the word tested, and only your-deployment-tested is about your estate. A NIST CAVP or CMVP pass proves the product’s algorithm and module are correct, and says nothing about whether your configuration and client population let the connection be forced back to classical. The strip test is the one that answers your question, and only you can run it.
- “A green test proves the deployment is secure.” A test proves the configurations, endpoints, and paths you exercised, and every estate has the ones you could not reach: the vendor product you could not instrument, the firmware you could not touch, the middlebox you never routed through. A passing strip test shrinks the untested surface, and the paths outside it are still untested rather than safe. Path coverage is the limit, and it is why the strip test rides on top of discovery rather than replacing it.
- “A refused connection is worse than one that completes.” This is the reversal the whole framework fights. A refusal is a loud, diagnosable, single incident that gets a ticket and a fix. A silent classical completion is an invisible, indefinite exposure that reports success to every dashboard above it while the traffic it carries is harvested. On an HNDL-critical link, the connection that completes is the failure and the one that refuses is the pass.
- “If my server plants the downgrade sentinel, I’m protected.” The sentinel is two halves, and the server planting it is one. A client that never checks the last 8 bytes of the server random accepts the rollback silently, and that omission has shipped in real code. Protection exists only when both halves are confirmed live, which is why probe 2 tests the client you deploy rather than trusting a reference implementation.
- “Enabling the hybrid group means the connection uses it.” Enabling it means the endpoint is willing to negotiate it. A peer that never offers the group, an attacker who strips it, or a middlebox that drops the oversized ClientHello can each leave you on a completed classical handshake. The strip test manufactures that condition deliberately so you learn the answer in a lab instead of from an archive.
- “The config review already covers this.” A configuration review verifies the menu, and the failure the strip test hunts is a correct menu producing an unsafe order. The config that lists hybrid first and keeps classical for compatibility passes review and still completes classical-only under a strip. The two are different artifacts answering different questions, and the wire holds the answer the config cannot.
Pro tips
- Judge on the negotiated group, and capture it every run. The pass condition is a fact on the wire: no completed handshake, an aborted one with the sentinel fired, or a
handshake_failure. Keep the packet capture or the termination-tier log for each probe, because “we ran the strip test” is a claim and the capture is the evidence, and the same capture becomes the baseline you diff against after the next config change. - Test both halves of the tripwire on the code you actually ship. Server-side sentinel behavior is easy to confirm and gives false comfort. Point your deployed client library, in the version you run, at a server pinned to TLS 1.2 and confirm it aborts. The client that skips the check is the recurring bug, so the client half is where the test earns its keep.
- Set the floor by data class before you test, so probe 3 has something to prove. Decide which listener classes carry long-lived secrets and give those a hard floor, then let the strip test confirm the refusal is real. A floor asserted in a policy doc and never exercised is a floor you do not have yet.
- Ask the follow-up when a team says “it fails safe.” The evasive version of that answer describes intent. The real version produces a capture of a refused or aborted handshake under a strip, on the specific path in question. “Show me the strip-test capture for this link” is the two-second way to tell a tested floor from a hoped-for one.
- Re-run after every change that touches the negotiation. A library upgrade, a cipher-policy edit, a new load balancer, or a vendor middlebox swap can all move a passing endpoint back to a silent fallback, because the negotiated reality is a moving target. The strip test is a gate you re-open on change, and its captures are what make a regression visible.
- Watch the retry rate as a companion tell. The voluntary classical-only fallback after an oversized ClientHello shows up as a raised handshake-failure or retry rate from specific network paths. A strip test proves the policy, and the retry rate tells you which real paths are triggering the fallback the floor now refuses, so you know which client populations to chase.
Where does the strip test break down?
The strip test needs a negotiation to attack, so it goes quiet wherever the cryptography is fixed rather than chosen. On a protocol or link that offers a single algorithm with no per-connection negotiation, there is no post-quantum option to strip out, no menu an attacker can edit, and therefore no downgrade behavior to observe. Pre-shared-key links, statically configured IPsec associations pinned to one suite, and any transport where the algorithm is compiled in all fall here. On those surfaces the floor is not a runtime policy the test can probe; the floor is the configuration itself, and the correct instrument is a configuration and key-management review that confirms the fixed algorithm is the strong one and cannot be changed by an unauthenticated peer.
Two narrower limits are worth naming so the test is used honestly.
- Coverage. The strip test validates the paths and endpoints you ran it against. A vendor-hosted termination point you cannot instrument, a device fleet you cannot reach, and a partner-run verifier you have never seen the source of are all outside what any lab run can exercise, so their downgrade behavior stays a written attestation rather than a measured fact. The Wire Over Config limit applies here too: where there is no wire you can read, the evidence degrades to a dated attestation, labeled as the weaker thing it is.
- Point-in-time. A strip test validates the deployment as it stood the moment you ran it. The negotiated behavior drifts as libraries, policies, and middleboxes change, which is why the test is a gate you re-open on change rather than a certificate you earn once.
Named plainly, those limits are the reason the strip test is one move inside a larger verification practice rather than the whole of it, and the reason a single pass is confidence rather than a guarantee.
How do you use the strip test in a boardroom?
Deploy it the moment a rollout is reported as tested and ready, which in most organizations is every time hybrid is reported. The slide says the post-quantum deployment was tested and validated, and the wield-upward form of the framework is one question: “When we strip the post-quantum option out, does this refuse the connection or quietly complete on classical, and can we see the capture?”
The question sorts the room. A team that answers with a captured refusal or a fired downgrade sentinel on the paths that carry long-lived data is running the transition on evidence, and the conversation moves to coverage, which paths were exercised and which are still on attestation. A team that answers by re-describing the algorithm’s NIST validation has confused a product certificate for a deployment test, and the board should understand that shipping on that basis means finding the downgrade behavior in production, where a silent fallback is a false migration report and an archive an adversary keeps. The question costs a sentence to ask and converts “we tested it” from a reassurance into a floor with a receipt, which is the unit a board can actually govern with.
Questions people ask
Is the strip test the same as a downgrade attack? It is a downgrade attack you run on yourself, in a lab, before an adversary runs it on you in production. The mechanics are identical: remove the strong option from the negotiation and see what the deployment settles for. The difference is that you control the outcome, so a failure is a finding you fix rather than a recording someone keeps.
How is this different from just reviewing the config? A configuration review verifies what the deployment is permitted to do, and the failure the strip test hunts is a correct configuration that still completes an unsafe handshake under a real strip. The config that lists hybrid first and keeps classical for compatibility passes review and fails the strip. The strip test reads the negotiated outcome on the wire, which is the only place the actual behavior appears.
What counts as a pass? On a link carrying long-lived secrets, a pass is the deployment refusing the connection or aborting with the downgrade sentinel fired when the post-quantum option is stripped, confirmed on the wire. On a documented classical-fallback path for short-lived data, a completed classical handshake can be an accepted pass, provided the fallback was a decision on the record rather than an accident the test revealed.
Do I have to check the client, or is the server enough? You have to check both. The TLS 1.3 downgrade tripwire only protects a connection when the server plants the sentinel and the client checks it, and client libraries have shipped that skip the check. Confirming the server half proves nothing about the client half, so the test forces a version rollback and confirms the client you deploy actually aborts.
Where does the minimum floor come from? You set it, by data class. Links carrying long-lived secrets get a hard floor that refuses anything below hybrid or post-quantum key exchange; short-lived public traffic can carry a deliberate classical-fallback allowance. The floor is a policy decision, and the strip test is what proves the policy is enforced rather than merely written down.
What if my protocol doesn’t negotiate at all? Then there is nothing to strip, and the strip test does not apply. A fixed-algorithm link, a pre-shared-key channel, or a compiled-in suite has its floor set by the configuration itself, so the right check is a configuration and key-management review confirming the fixed algorithm is strong and cannot be changed by an unauthenticated peer.
Does a passing strip test mean my migration is secure? It means the paths you exercised fail safe under a downgrade, which is a real and important result and not a guarantee about the paths you could not reach. Coverage is the standing limit, the deployment drifts over time, and the posture is that a strip test raises confidence and layered verification raises it further. It rides on top of discovery, not in place of it.
How often should I run it? On change rather than on a calendar. Any edit to the cipher policy, a library upgrade, a new termination point, or a swapped middlebox can move a passing endpoint back to a silent fallback, so the strip test is a gate you re-open whenever the negotiation surface changes, with each run’s capture kept as the baseline for the next.
Everything here is the map, given freely. Running a strip test across a whole estate, building the matrix of every counterpart and path a rollout has to survive, setting the floor per data class, and quantifying the coverage of what you can and cannot reach, is the version of this work that gets done against your own systems, and that is the work I do. Request the workshop.
Last verified 2026-07-26 · Updated 2026-08-25 · Maintained by Addie LaMarr, LaMarr Labs.