PQC migration: from use case to profile
This section takes one use case and develops it in full, from the decision a consumer is trying to make through to a complete profile definition. It applies the twelve steps set out in Method, and is the reference example for how the remaining use cases would be worked up.
The decision being supported
An operator runs a product it did not build and cannot inspect. It needs to answer three questions about every interface that product exposes, and it needs the answers in a form it can aggregate across hundreds of instances.
- Can this interface be made quantum-safe at all, and if so with which algorithms?
- What would it take: a configuration change, a software update, or new hardware?
- What breaks when we do it, and can we do it while the other end is still classical?
None of these is an inventory question. Knowing which algorithms an interface uses today leaves all three unanswered, because they concern capability, the route to enabling it, and what enabling it costs in interoperability. A migration profile therefore has to require the facts those answers rest on.
The black-box constraint
The defining constraint is that the consumer cannot see inside the product, and the vendor will not expose its internals. Everything the profile asks for therefore has to be declarable at the boundary: observable at an interface, or stated by the vendor as a property of the product it ships. Anything requiring disclosure of source, design or internal architecture is out of scope, and would not be supplied in any case.
This is also why the profile is interface-driven rather than product-driven. A single answer at product level, of the kind "this product supports ML-KEM", is not actionable: a product may support a post-quantum key exchange on its service interface and not on its management interface, and the operator has to migrate both. Migration is planned and executed one interface at a time.
From questions to attributes
Each planning question implies particular facts. Working from the question to the attribute, rather than from the format's available fields to whatever they can express, is what keeps a profile purposeful.
| What the consumer needs to know | Attribute that supplies it |
|---|---|
| Which interface are we talking about, and does it matter? | interfaceId, interfaceType |
| What protocol, and which versions can it speak? | protocol, protocolVersionsSupported |
| What is it doing now? | keyExchange, authentication |
| What else could it do without changing the product? | keyExchangeSupported, authenticationSupported |
| How do we switch it on? | enablementMethod, minimumProductVersion |
| Is this a software change or a hardware one? | providerLocation, implementationPurl |
| Can we migrate while the far end is still classical? | coexistence, negotiationControl |
| What is likely to break? | integrationConstraints |
| If it cannot do this yet, what is stopping it, and who can change that? | capabilityStatus, blockedBy, roadmapRef |
| How much should we trust this record? | lifecycleStage |
The profile definition
Keywords follow BCP 14. The profile is product-independent: no rule names a product, a vendor or an interface instance. It builds on the interface disclosure baseline described in the Profile section and adds the capability and enablement attributes migration requires.
Attribute names follow the conventions in Method. Where an attribute also appears in the baseline profile it keeps the same name and meaning: keyExchange and authentication denote what the interface uses now, and the Supported suffix denotes what it is able to use.
This profile extends the interface disclosure baseline at version 0.7, under the composition rules in Profile. It inherits every baseline rule, adds the capability and enablement attributes below, and tightens one inherited rule. Because extension is monotonic, a CBOM conforming to this profile also conforms to the baseline.
| Relationship to the baseline | Rules |
|---|---|
| Inherited unchanged | P1 to P4 at product level; I1 to I8 per interface, covering protocol, protocol version, present-state key exchange, encryption and authentication, endpoint roles, interface type and lifecycle stage. Note that no rule constrains interfaceId: it is an instance label chosen by the producer, so a profile cannot require a particular value for it. |
| Tightened | interface-disclosure#I9 implementationPurl is no longer withholdable. The baseline already requires it at MUST and accepts a withheld marker in place of a value; this profile removes that option. Migration planning depends on knowing which library implements an interface, since that determines whether an update is available at all. |
| Added | One product rule, nine per-interface rules and one group rule, listed below. They are numbered in this profile's own space, from P1, I1 and G1, and cited against this profile's tag: pqc-migration#I1 is a different rule from interface-disclosure#I1, and a failure report shows both in full. See Profile for the scheme. |
The tables that follow list the added and tightened rules. Inherited rules are not restated, and the baseline remains authoritative for them.
Product-level rules
| # | Requirement | Level | Note |
|---|---|---|---|
interface-disclosure#P1 | Declare at least one cryptographic interface | MUST | Inherited from the baseline. |
interface-disclosure#P2 | Declare at least one interface of type management, or state why there is none | MUST | Inherited. Administrative access is migrated too, and is the interface most often omitted. |
interface-disclosure#P3 | Identify the subject the document describes | MUST | Inherited. A plan is drawn against a deployment, so the record has to name which product and version it is about. |
interface-disclosure#P4 | State the completeness of the interface set | MUST | Inherited since baseline v0.6. coverage is one of all-external, all, or partial. A planner needs to know whether the list is exhaustive; a partial list that looks complete produces a plan with silent gaps. |
pqc-migration#P1 | Identify the cryptographic providers the product uses | SHOULD | This profile's own first product rule, and the only one it adds. Lets a consumer reason about update paths shared across interfaces. |
Per-interface rules added here
Applied to every declared interface, alongside the inherited baseline rules interface-disclosure#I1 to #I9. This profile numbers its own from I1; the two sets are told apart by the tag they are cited against, not by the letter.
| # | Attribute | Level | Values and purpose |
|---|---|---|---|
| I1 | protocolVersionsSupported | MUST | All versions the interface can negotiate, not only the default. Post-quantum key exchange is unavailable on older versions of some protocols, so the version set bounds what is possible. |
| I2 | keyExchangeSupported | MUST | The full set the interface can negotiate, including hybrid constructions. This is the attribute that answers whether the interface can be made quantum-safe. |
| I3 | authenticationSupported | MUST | Authentication migrates separately from key exchange and usually later, because it depends on what a certificate authority will issue. |
| I4 | enablementMethod | MUST | already-enabled · configuration · software-update · hardware-change · licence · not-available. This single attribute separates the cheap changes from the expensive ones and drives sequencing more than any other. |
| I5 | minimumProductVersion | conditional | MUST where enablementMethod is software-update. Without it the consumer knows an update is needed but not to what. |
| I6 | providerLocation | MUST | software · hsm · tee · unspecified. Where key operations execute determines whether a new algorithm is a software matter at all. |
| I7 | coexistence | MUST | simultaneous · exclusive · unknown. Whether the interface can serve classical and post-quantum peers at the same time. An exclusive interface forces a coordinated cutover with every counterparty. |
| I8 | negotiationControl | SHOULD | Whether the interface can be configured to require post-quantum key exchange and refuse a classical fallback. Determines whether migration can be enforced or only offered. |
| I9 | integrationConstraints | SHOULD | Known effects of enabling the capability: handshake or record size limits, fragmentation behaviour, certificate size ceilings, intermediary incompatibilities, measured performance impact. |
| G1.1 | capabilityStatus | MUST | available · committed · planned · under-evaluation · not-planned. A commitment class rather than a date. Most sequencing decisions turn on the difference between planned and not-planned, which needs no date at all. |
| G1.2 | blockedBy | conditional | MUST where that purpose's capabilityStatus is not available. One of provider · product · hardware · standard · certification. Identifies what has to change and therefore who can change it. |
| G1.3 | roadmapRef | MAY | Reference to a roadmap entry carrying the target release and, if offered, a date. Interfaces sharing a blocker reference the same entry. |
The one tightening
| Inherited rule | Baseline | Here |
|---|---|---|
I9 implementationPurl | MUST, withholdable | MUST, not withholdable |
Migration planning depends on knowing which library implements an interface, since that determines whether an update exists and when it arrived. A vendor withholding it leaves the consumer unable to sequence the work. This is the only inherited rule this profile changes, and raising a level is permitted because it tightens.
Conditional rules
Two of the rules above apply only in certain states, which is worth noting in itself. A profile is not limited to a flat list of mandatory fields; a requirement can depend on the value of another attribute. Here, I5 obliges a producer claiming that a capability needs a software update to say which version provides it, and G1.2 obliges one declaring anything other than present capability for a given purpose to say what is in the way of that purpose. Both dependencies exist because the unconditional alternatives are unhelpful: requiring a version from every interface would produce noise, and requiring none would leave the useful case unstated.
Availability, and why it is not a date on an interface
An earlier draft of this profile put a plannedAvailability date on each interface. It is the obvious design, and it does not hold up. The reason is structural.
Interfaces do not gain capability independently. A product with six TLS interfaces built on one cryptographic provider gains post-quantum key exchange on all six at the moment that provider is updated and the product integrates it. A date on each interface duplicates one underlying event six times, and the six copies drift apart as they are maintained. Availability belongs to the change that delivers the capability; the interfaces merely inherit it.
What a planner needs first is therefore not the date but the obstacle, because the obstacle determines whether any date is credible and who is able to move it.
blockedBy | What has to happen | Who can move it | Predictability |
|---|---|---|---|
provider | A dependency is upgraded to a version offering the algorithm. | The vendor, by taking the upgrade. | Reasonable |
product | The product's own code must change to expose or negotiate it. | The vendor, in a named release. | Reasonable |
hardware | A module, firmware or platform must support the algorithm. | The vendor together with the module supplier. | Poor |
standard | No specification yet defines the algorithm for this protocol. | Nobody, until the standards process concludes. | None |
certification | A validated implementation is required, for example under a scheme such as FIPS. | An external laboratory or scheme. | Externally determined |
The distinction that matters most is the fourth row. An interface blocked on a protocol standard cannot be given a credible date by any vendor, and a consumer who understands that stops waiting and considers compensating controls instead. Recording it as "planned, date to follow" alongside a case that is genuinely scheduled for the next release conflates two situations that call for opposite responses.
Where a date or release is offered, the profile keeps it out of the interface and out of the CBOM. Target release and date sit in a roadmap entry that interfaces reference through roadmapRef, so that interfaces sharing a blocker point at one entry. Two reasons: a CBOM revision is an immutable point-in-time record, so a slipping date either goes stale inside an authoritative-looking document or forces CBOM republication when nothing about the product has changed; and roadmap data has a different owner and a different cadence from the rest of the document, which is the same argument the Governance section makes for keeping separately-lifecycled artifacts apart.
A target release is also preferable to a calendar date. Releases are the vendor's own unit of change, and the statement "this lands in 9.x" survives slippage in a way that a date does not.
A worked interface
One interface of a hypothetical product, declared under this profile. The values are illustrative.
| Attribute | Value |
|---|---|
interfaceId | svc-api-tls |
interfaceType | service |
protocol / protocolVersionsSupported | TLS / 1.2, 1.3 |
keyExchange | X25519 |
keyExchangeSupported | X25519, secp256r1, X25519MLKEM768 |
authentication | RSA-2048 |
authenticationSupported | RSA-2048, ECDSA-P256 |
enablementMethod | software-update |
minimumProductVersion | 8.2.0 |
providerLocation | software |
implementationPurl | pkg:generic/openssl@3.5.0 |
coexistence | simultaneous |
negotiationControl | configurable to require hybrid |
integrationConstraints | larger ClientHello may exceed some middlebox limits; no measured throughput impact |
lifecycleStage | implemented |
Capability, per purpose
These attributes were a single value per interface until v0.4, and the values below are why that could not hold. Key agreement is done here and identity proof is not, which is the ordinary position rather than an awkward corner, and one status per interface would have to report the worse of the two. Rule G1 now requires an entry per cryptographic purpose, and the conditional blocker in G1.2 is evaluated inside an entry, so the blocker belongs to the purpose it blocks.
| Purpose | capabilityStatus | blockedBy | roadmapRef | In scope |
|---|---|---|---|---|
key-establishment | available | not applicable | — | yes |
encryption | available | not applicable | — | yes |
entity-authentication | committed | certification | rm:pqc-auth-2027 | yes |
data-integrity | available | not applicable | — | status only |
non-repudiation | not-planned | product | — | status only |
key-protection | planned | hardware | — | status only |
key-derivation | available | not applicable | — | status only |
The last four are outside this profile's declared scope and still carry a status. That is the whole of what a deferred purpose owes: one enumerated value, and a blocker where the status is not available. It costs a producer almost nothing and it is what keeps a staged profile from becoming a permanent floor — the key-protection row above tells a planner that this interface's authentication migration will involve hardware, which is a fact worth having in the first document rather than the third.
What a planner can now conclude, without seeing inside the product: this interface can be made quantum-safe for key exchange but not yet for authentication; the key-exchange change requires an upgrade to 8.2.0 rather than new hardware; the upgrade can be rolled out before counterparties are ready, because classical and hybrid peers can be served at once; there is a specific interoperability risk to test for; and the authentication half is waiting on certification, so a scheme controls that date and pressing the vendor for it in a tender will achieve little. An inventory of the algorithms in use today yields none of these conclusions.
A purpose is not an operation, and the distinction is what makes the vocabulary worth having. CycloneDX already records operations: this interface's certificate signature carries cryptoFunctions: ["sign", "verify"], and so would a firmware signature on the same product. One is entity authentication that migrates when the PKI does; the other must still verify years after it was written and migrates when the last verifier in the field is upgraded. Same operation, different decade, different work. A vocabulary of operations would restate a field the format already has and answer no planning question. See Terms for the four-part test an entry has to pass, and decision 0010 for the reasoning.
What the profile deliberately excludes
The profile omits three kinds of information, in each case deliberately.
It carries no readiness or posture verdict. Whether a declared algorithm set counts as quantum-safe is a judgement against criteria still under revision, so an external policy computes it from these facts at the time of asking and dates the result. Policy Evaluation sets out the reasoning, which is also why pqcPosture was dropped from the baseline profile.
Maturity scores are excluded on the same grounds. Collapsing heterogeneous attributes into one number has a further drawback: it tends to hide whichever attribute is actually blocking the migration.
Internal implementation is out of scope, so no source references, design detail or component structure are requested. This follows from the black-box constraint and is not a concession to it. A vendor asked to expose internals will decline, and a profile nobody satisfies achieves nothing.
One further omission is worth explaining, since a reader comparing this with the baseline will notice it. The baseline's encryption attribute is inherited and still required, so an interface states the bulk algorithm it uses. What this profile does not add is an encryptionSupported counterpart. Symmetric encryption at adequate key lengths is not the pressing concern in a post-quantum migration, and the interfaces needing work are identified by their key exchange and authentication. A profile combining migration with a broader agility objective would add it.
Expressing it in a CBOM
The attributes above are format-independent. Several map onto native CycloneDX fields, and the remainder are carried as properties under the pkic:profile: namespace, in the same manner as the baseline profile. The capability attributes hold lists where the baseline holds single values, which is the main structural difference between the two and the point at which the mapping needs care: a consumer must be able to distinguish an empty list from an absent one, and both from a withheld one. See Formats for the encoding conventions and Challenges for why that distinction matters.
Where an attribute is unavailable, the four disclosure outcomes apply. A vendor may not withhold implementationPurl under this profile and still conform: the baseline permits it, and this profile removes that permission, because migration sequencing depends on knowing which library implements an interface. Withholding it here fails interface-disclosure#I9 — the rule keeps the id of the profile that introduced it, even though this profile is the one that tightened it. A vendor that does not know whether an interface can coexist should record coexistence as unknown instead of guessing, because a wrong answer here produces a migration plan that fails at cutover.
Recorded decisions
Two choices in this profile were made against the more obvious alternative. Each is written up as a decision record in the repository, with the options considered, the rationale and what reversing it would involve. The records are the authoritative statement; the summaries below are for orientation.
| Record | Decision | Aspect |
|---|---|---|
| 0002 | Capability is stated as facts. The profile requires the algorithm sets an interface supports and does not ask whether they amount to being quantum-safe. That judgement is made by an external policy at the time of asking, for the reasons given in Policy Evaluation. | 3.3 |
| 0007 | Availability is expressed as a status and a blocker. Dates and target releases sit in a referenced roadmap entry, away from the interface. | 3.10 |
The rules above also depend on decisions taken elsewhere in the methodology: 0004 for what this profile is permitted to change in the baseline it extends, and 0005 for the Supported suffix. The full index is in decisions/.
Open questions for the working group
These are also carried in the open questions register, which collects the unresolved items from every section and maps each to the methodology aspect that tracks it.
- Whether raising
implementationPurlto a non-withholdable MUST is acceptable to vendors. It is the one place this profile tightens the baseline, and it removes an option the baseline deliberately offered. - Whether
enablementMethodshould be a single value or a set, since some capabilities need both an update and a configuration change. - Whether
integrationConstraintscan be structured usefully or should remain prose. Structured constraints can be checked automatically, though prose is what vendors are more likely to supply. - Whether
capabilityStatusshould be stated once per interface or separately for key exchange and authentication. The worked example does the latter, because the two migrate on different schedules, but it complicates the rule. - Whether a CBOM should carry forward-looking data at all. This one is a substantive disagreement, not a detail to be tidied up. A procurement profile has a defensible claim on it, since a buyer is entitled to ask what a product will support before committing; a runtime assurance profile arguably has none, because a statement about the future says nothing about what is running. The answer may legitimately differ between profiles, and need not be settled once for all of them.
- How this profile relates to a sector baseline. A telecommunications or finance profile would most likely add required interface types and constrain the acceptable algorithm sets, leaving the attribute list intact.