Skip to content

필사 모드: Passkey Portability Only Half Arrived — CXF Became a Standard, CXP Is Still a 2024 Working Draft

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

Introduction — What "you can export your passkeys" actually covers

In June 2026, Android got passkey import/export. iOS 26 had done the same thing the year before. Read only the headlines and the story sounds settled — passkeys are portable now, and the lock-in worry is solved.

But open the spec directory itself and the picture is different. FIDO's credential exchange work is split not into one document but two, and the two are separated by an order of magnitude in maturity. One became a standard. The other has been a working draft for 21 months. And the finished one depends on the unfinished one for security.

This post works out what that asymmetry means in practice. The short version: what has actually shipped as of July 2026 is not "cross-ecosystem passkey portability" but "OS-mediated, app-to-app transfer within the same device." That difference disappears in marketing copy, but it does not disappear in the architecture.

Two specs, two speeds

Read the FIDO Alliance spec directory (fidoalliance.org/specs/cx/) as-is and this is what you get.

cxf-v1.0-wd-20240522.html          CXF working draft     2024-05-22
cxf-v1.0-wd-20241003.html          CXF working draft     2024-10-03
cxf-v1.0-rd-20250313.html          CXF review draft      2025-03-13
cxf-v1.0-ps-20250814.html          CXF Proposed Standard 2025-08-14
cxf-v1.0-errata-20260309.html      CXF errata            2026-03-09
cxf-v1.0-ps-errata-20260309.html   CXF PS + errata folded 2026-03-09

cxp-v1.0-wd-20240522.html          CXP working draft     2024-05-22
cxp-v1.0-wd-20241003.html          CXP working draft     2024-10-03
                                   ...stops here

CXF (Credential Exchange Format) defines what gets exchanged — the data structures and format. It went from working draft, through review draft, to Proposed Standard on August 14, 2025, and got an errata on March 9, 2026. That's a normal standardization trajectory.

CXP (Credential Exchange Protocol) defines how it gets exchanged — the actual transport protocol between two providers. The last thing it has is an October 3, 2024 working draft. No review draft, no Proposed Standard. As of today (July 16, 2026) it has sat unchanged for 21 months.

For what it's worth, the March 2026 errata is an editorial fix, not a content change. Appendix A's example payload had a hmacSecret key that had drifted out of sync with the normative body; the errata corrects it to hmacCredentials and fills in example-file content that had gone missing. The errata document itself says as much — "The contents of example-file were missing and cfx example was updated." No normative behavior changed, so don't read this as "CXF got a major revision in 2026."

What the CXP working draft says about itself

Here is the "Status of This Document" section from the CXP working draft, carried over verbatim.

This is a Working Draft Specification and is not intended to be a basis for any
implementations as the Specification may change. This document is merely a FIDO
Alliance working group internal and member-confidential document. It has no
official standing of any kind and does not represent consensus of the FIDO Alliance.

No rights are granted to prepare derivative works of this Specification.

It reads like boilerplate you'd skim past, but given that this is the transport half of the passkey-portability story, every sentence carries weight. "Not intended to be a basis for any implementations." "Merely a FIDO Alliance working group internal and member-confidential document." "No official standing of any kind... does not represent consensus."

The document's unfinished state shows up in the table of contents too. CXP §5, "Implementation Requirements" — the section that's supposed to define which algorithms and features of this spec are mandatory — contains exactly two sentences: "This section defines which algorithms and features of this specification are mandatory to implement. Applications using this specification can impose additional requirements upon implementations that they use." Then it jumps straight to §6 Security Considerations. The set of mandatory-to-implement algorithms is empty. The interoperability floor hasn't been laid yet.

The protocol does have a skeleton. A Diffie-Hellman key exchange establishes a secure channel or data payload between the two providers, HPKE parameters are negotiated (mode is base / psk / auth / auth-psk), and the archive uses DEFLATE. The flow is five steps: the importer creates an Export Request → the authorizing party decides to migrate → the exporter encrypts the credential data → it sends an Export Response → the importer decrypts the bundle. The design is reasonable. But "there's a design" and "there's a standard" are different claims.

What CXF actually carries — a plaintext private key

This is where it matters. In the CXF spec's Passkey entry, the definition of the key member reads:

key
  The private key associated to this passkey instance.
  The value MUST be PKCS#8 ASN.1 DER formatted byte string which is then
  Base64url encoded.
  The value MUST give the same public key value that was provided by the
  original authenticator during registration.

The spec's Appendix A example payload makes it concrete. (This is carried over verbatim from the CXF PS + errata document's example.)

{
  "id": "akKA3Y0jQRuK7sKplB0Y9w",
  "title": "WebAuthn.io",
  "credentials": [
    {
      "type": "passkey",
      "credentialId": "Y3JlZGVudGlhbElkRXhhbXBsZQ",
      "rpId": "webauthn.io",
      "username": "johndoe",
      "userDisplayName": "John Doe",
      "userHandle": "cnEzaNHWcYK3coWZjvoaV1Hj9gnI12mKe2dL2HZVFlY",
      "key": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgARu_0sCt20EpgVxb4Puq3Ga5VVLpuTY75ngvZlyq3X6hRANCAASmdk1xLsK0oOlhxIPp0d1ZuS0sT9nf6BZtSelhqvLBW0fOL33l_bXgsr_STUHjCLn8l6gcRJwe7OQvbQubZ1dY"
    }
  ]
}

The string in the key field is a P-256 private key. No wrapping, no encryption, no passphrase. In the same example, the password sits right next to it as "value": "securepassword123", and the TOTP seed as "secret": "JBSWY3DPEHPK3PXP" — both plaintext.

This isn't a bug, it's the design. CXF is only a format, and it draws the line explicitly in its own §1.2 Scope — "This document outlines the data structures and format needed to exchange credentials and does not make any assumptions about the protocol used for the transfer, such as the protocol outlined by CXP." It makes no assumptions about transport. Which means: taken on its own, a CXF document's output is a plaintext JSON blob containing all of your private keys, passwords, and TOTP seeds.

So who's responsible for confidentiality?

CXF's §5 Security Considerations answers this question as follows.

Note: [CXP] ensures that the credential exchange remains confidential between
the exporting provider and importing provider.

In other words, CXF says "confidentiality is guaranteed by CXP," and CXP says "I'm not intended to be a basis for any implementations, and I have no official standing." A finished spec delegating security to an unfinished one — that's the core of this post.

CXF is aware of the gap and has left one escape hatch. The same §5.1 states that the orchestrating party (the entity mediating the exchange) MAY implement only CXF without implementing CXP. But if it doesn't implement CXP, the developer MUST document the security mechanism of that credential exchange and MUST publish that documentation on a public website. The published documentation must state that the intermediary only relays the data, does not retain it or use it for other purposes, and passes on received data without tampering with it.

Read plainly, this substitutes a standardized cryptographic protocol with each vendor's own documented promise. Better than nothing, but not the same thing as an interoperable security guarantee. Instead of a verifiable protocol, you get a public statement of "this is how we do it."

What actually shipped — OS-mediated, same-device transfer

So what did Apple and Google actually ship in 2026? This is where the tension above gets resolved.

Apple. ASCredentialExportManager and ASCredentialImportManager landed in iOS 26.0, iPadOS 26.0, macOS 26.0, Mac Catalyst 26.0, and visionOS 26.0. Apple's developer documentation states the behavior plainly.

The operating system acts as an intermediary to establish the identities of the
password manager apps involved and performs the exchange; this process doesn't
write any data to the file system.

The OS acts as intermediary, verifies the identities of both apps, and performs the exchange directly — and in the process, it writes no data to the file system at all. An app must declare SupportsCredentialExchange in its extension plist and list supported versions in SupportedCredentialExchangeVersions, though per Apple's documentation the only value currently possible is 1.0.

This design explains why CXP wasn't needed. When the OS acts as a trusted intermediary on the same device, the two providers don't need to authenticate each other or negotiate a channel — a plaintext CXF payload never touches disk; it stays inside the OS boundary. In other words, Apple didn't implement CXP — it sidestepped the problem CXP was meant to solve. Platform control was substituted for a protocol.

Google. The Security & Privacy section of the Google Play services v26.21 (2026-06-01) release notes reads: "[Phone] You can now import and export passwords and passkeys between Google Password Manager and third-party password managers with the Credential Exchange standard."

Precision matters here. Google's official release notes say only "Credential Exchange standard" — they do not name CXP. Some outlets have written that "Android supports CXP and CXF," but that's a secondhand interpretation. Google's own wording is vaguer than that, and the tag is just [Phone]. And the related SDK artifact, com.google.android.gms:play-services-identity-credentials, is still at 16.0.0-alpha11 (last updated 2026-02-09) on Google's Maven repository. It's gone from alpha01 through alpha11 and still hasn't reached beta.

Put together: what both companies actually shipped is app-to-app transfer mediated by the OS, on the same device. Transfer that crosses devices, or crosses ecosystems — exactly the part CXP is supposed to own — isn't here yet.

The passkeys that don't move

CXF has two normative constraints that practitioners tend to miss. Both amount to "some passkeys can't be exported at all."

First, passkeys with a non-zero signature counter. Here is the Note from the CXF spec's Passkey section, carried over verbatim.

Note: Passkeys using a non-zero signature counter MUST be excluded from the export
and the exporter SHOULD inform the user that such passkeys are excluded from the
export. Importers MUST set a zero value for the imported passkey signature counters
and MUST NOT increment them after the fact.

The exporter MUST exclude passkeys with a non-zero signature counter, and the importer MUST set the counter to zero and MUST NOT increment it afterward. The signature counter exists to detect authenticator cloning, and that purpose conflicts with portability on principle — a mechanism for detecting cloning and a feature for legitimately cloning credentials can't coexist. The spec resolved that conflict in favor of portability, at the cost of excluding counter-using credentials from the exchange.

The spec needs to be read precisely here. Per WebAuthn Level 3 §6.1.1, an RP compares its stored counter against the newly received signCount, and cloning is only suspected "if either is non-zero, and the new value is less than or equal to the stored value." A passkey whose counter stays at zero never triggers this check in the first place. The spec also states that an authenticator that doesn't implement counters pins signCount at zero, so zero is a normal value the spec anticipates.

So, stated honestly, what happens to an imported passkey isn't "getting locked out by a false positive" — it's simply losing the clone-detection signal. CXF traded away clone detection in exchange for portability. That said, if you're running logic stricter than the spec — for instance, an implementation that directly enforces "the counter must always increase" — the story is different: that logic will trip on a value the spec defines as normal and block a legitimate user. For reference, the spec explicitly says it does not distinguish whether a counter mismatch is cloning, malfunction, or a race condition, and leaves the response up to each RP's risk tolerance.

Second, hardware security keys. This one is more fundamental. In FIDO Alliance's official public feedback repository (fido-alliance/credential-exchange-feedback), a CTAP implementer asked which standard covers moving a credential off a hardware authenticator and onto another authenticator (issue #33, 2025-08-02). A member of the fido-alliance GitHub organization answered:

No certified FIDO hardware authenticator allows a credential to leave its secure
element, so what you're asking for does not exist, by design.

No certified FIDO hardware authenticator lets a credential leave its secure element, and that is by design. In the same thread, he stated plainly that credential exchange has nothing to do with CTAP, and the target is "migration between credential managers."

Put the two constraints together and the picture sharpens. Credential exchange is a migration tool between software credential managers. It is not, and won't become, a story about moving a passkey off a YubiKey. Any organization using hardware keys that sees a "passkeys are portable now" headline and builds a backup strategy on top of it is building on the wrong premise. The backup strategy for hardware keys is still "enroll more than one."

What's still unresolved — export refusal and lock-in

There's an open question of whether the portability spec actually prevents lock-in. Issue #24, "Risk of lock-in from export blocking", opened January 21, 2025, raises this head-on. The gist: CXP appears to leave room for a provider to refuse export requests or approve them selectively, which would let provider FOO, after raising prices, selectively block exports to competitor BAR — holding the user's credentials hostage. The person who filed it framed it as a question of whether "permissionlessly exportable" (exportable without the current provider's consent) should be in scope for the spec.

The response is worth noting. A FIDO-side participant credited as a CXF contributor said in March 2025 that "the concern has been heard plenty and there's been a lot of internal Alliance discussion," and added this in May:

The general vibe is supportive and language has been added to this effect, though
it looks like we haven't done a public working draft in some time so I don't think
that's externally visible yet.

So language addressing this was added internally, but no public working draft has gone out in a while, so it isn't externally visible yet. And as of July 2026, it still isn't — the latest public CXP is, then and now, the October 3, 2024 working draft. The issue remains open.

This isn't a knock against the spec process. Working under member confidentiality is FIDO's long-standing way of doing things, and standards are inherently slow. But stated honestly from a practitioner's seat: we cannot read how the anti-lock-in language was actually applied, and a guarantee you can't read is not one you can plan around.

A counterargument from the same issue thread is worth recording too. One side argued that once you've chosen to rely on a third-party service, this is no different from any other login method; the person who filed the issue pushed back that local open-source software like Firefox lets you install an older version or patch the code even if an update blocks export, whereas a credential sitting in a secure element inherently requires the manufacturer's or operator's permission — and drew an analogy to the relationship between a domain registrar and ICANN. This post won't adjudicate which side is right. But the fact that this dispute has sat unresolved since January 2025 is itself evidence that portability is a governance problem before it's a technical one.

So what should you do right now

Translated into practical terms:

What you can rely on today

  • Switching credential managers on the same device. This actually works on iOS/iPadOS/macOS/visionOS 26, and on Android (Play services 26.21+, currently [Phone]).
  • Replacing CSV export. This is CXF's real achievement. The CXP spec's own preamble names CSV as the problem it's defining — an unsafe format, and migration failures from the lack of a standard structure. A structured JSON schema alone improves both of those.
  • Moving passwords, TOTP, and other item types alongside passkeys. CXF isn't passkey-only; it defines multiple item types within a credential manager.

What you should not plan around yet

  • Cross-device or cross-ecosystem transfer. Handing a passkey directly from an iPhone to Android is CXP's job, and CXP is still a public working draft.
  • Migration or backup of hardware security keys. By design, it doesn't exist. Enrolling more than one is still the only answer.
  • A file-based export you keep yourself. What's shipped today is OS-mediated app-to-app transfer, and per Apple's documentation, nothing is written to the file system. As one participant pointed out in issue #24, wanting "a copy I hold myself" points in a different direction than the current design.
  • A guarantee that a provider won't refuse to export. The published spec doesn't have that guarantee yet.

If you run an RP, check this

  • Whether your signature-counter logic is stricter than the spec. WebAuthn's clone detection only fires when the value is non-zero, so if you implemented it to spec, an imported passkey passes through silently. But if you've directly implemented "the counter must always increase," it will block legitimate users. And either way, the fact remains: counter-based clone detection no longer provides a signal for imported passkeys.
  • How you use the backup state (BE/BS) flags. As the FIDO-side answer in the issue #33 thread pointed out, BE/BS signal only whether a passkey is synced versus device-bound, and whether the authenticator currently believes it's backed up — not "shareability." If you have a policy that reads this flag as a shareability signal, that premise is wrong.
  • Account recovery paths. Better portability doesn't solve recovery. Being able to switch managers and being able to get back in after losing all your devices are different problems, and the latter is still homework each RP has to do on its own.

Closing

To sum up: credential exchange is two specs, and they're at different levels of completeness. CXF, which defines what gets exchanged, became a Proposed Standard — a real achievement, and something that will end the CSV era. CXP, which is supposed to define how it gets exchanged, is stuck at an October 2024 working draft, and that document states of itself that it is not a basis for implementation and has no official standing. Yet the payload CXF carries contains a plaintext PKCS#8 private key, and CXF's own security considerations delegate confidentiality to CXP.

The platforms filled that gap with control, not protocol. The OS becomes the intermediary, performs the exchange within a single device, and writes nothing to disk. It's a clever solution and it genuinely works — but it isn't "passkeys became portable." It's "you can now switch managers within one device." The distance between those two is exactly the space CXP is supposed to fill, and that space is still empty.

So look at the filename in the spec directory, not the headline. cxp-v1.0-wd-20241003.html — until that date changes, cross-ecosystem passkey portability is a roadmap item, not a shipped feature.

References

현재 단락 (1/119)

In June 2026, Android got passkey import/export. iOS 26 had done the same thing the year before. Rea...

작성 글자: 0원문 글자: 19,052작성 단락: 0/119