You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
platform-wallet: contact xpub decryption fails for a canonical ENCRYPTION key that dashj decrypts, and the failure permanently breaks the channel #4407
On a mainnet DashPay wallet with 120 contacts, one contact's encrypted xpub cannot be decrypted by platform-wallet, and the channel is marked permanently broken. The same xpub decrypts successfully in dashj on the same wallet, so the ciphertext is valid and a correct key exists.
This is not the legacy key-purpose cohort fixed by #4372 (that fix is present in the build under test). Both referenced keys are canonical.
Evidence
The drain: building external account diagnostic, immediately before the failure:
drain: external register permanent fault; marking channel broken
error=Invalid identity data: Failed to decrypt contact xpub (Decryption failed);
ciphertext 96 bytes. PKCS7 rejected the plaintext — most likely the ECDH shared
secret did not match the sender's, though a corrupted ciphertext produces the
same symptom
Everything here is canonical:
both keys are ENCRYPTION purpose and ECDSA_SECP256K1 type — purposes we mint ourselves
the ECDH path matches identity_auth_derivation_path_for_type's documented format m/9'/COIN'/5'/0'/key_derivation_type'/identity_index'/key_index' with key_index=2,
which agrees with our_key_id=2
Why this is a platform-wallet bug, not bad data
On the same wallet, the same device and the same contact, dashj derives the friendship chains successfully — both account references resolve, the receiving chain yields addresses, and the sending path is present. There are zero decryption failures on the dashj side across the whole session.
So one implementation reads this contact's xpub and the other cannot, from identical inputs. The remaining explanation is that platform-wallet derives a different private key (or a different shared secret) for this identity key slot than dashj does.
Scope on the affected wallet: 119 of 120 contacts register fine; only this one fails.
Second defect: the failure is unappealable
register_external_contact_account classifies a decrypt failure as Permanent, which marks the channel broken with no retry:
A wrong shared secret is not proof that the document is bad — as this case shows, the same ciphertext is decryptable. Treating it as permanent means one bad key guess disables a contact for the life of the wallet, with no user-visible cause and no way to recover. The user can neither pay that contact nor match payments from them.
Suggested split:
root cause — reconcile the identity-key derivation / ECDH secret against dashj for ENCRYPTION-purpose identity keys, ideally with a cross-implementation vector test
independently — stop charging a decrypt failure to the document as Permanent; a shared-secret mismatch should leave the channel retryable
Reproduction
Not reproducible on demand yet — it is one contact on one field wallet. Device logs are available privately. A cross-implementation test vector (dashj-produced encrypted xpub decrypted by platform-wallet) would pin it deterministically and guard the regression.
Summary
On a mainnet DashPay wallet with 120 contacts, one contact's encrypted xpub cannot be decrypted by platform-wallet, and the channel is marked permanently broken. The same xpub decrypts successfully in dashj on the same wallet, so the ciphertext is valid and a correct key exists.
This is not the legacy key-purpose cohort fixed by #4372 (that fix is present in the build under test). Both referenced keys are canonical.
Evidence
The
drain: building external accountdiagnostic, immediately before the failure:and the failure ~80 ms later:
Everything here is canonical:
ENCRYPTIONpurpose andECDSA_SECP256K1type — purposes we mint ourselveslegacy_widened=false, so the fix(platform-wallet): accept legacy dashj key purposes on inbound contact requests #4372 receive-side widening played no partciphertext_len=96, the expected sizeidentity_auth_derivation_path_for_type's documented formatm/9'/COIN'/5'/0'/key_derivation_type'/identity_index'/key_index'withkey_index=2,which agrees with
our_key_id=2Why this is a platform-wallet bug, not bad data
On the same wallet, the same device and the same contact, dashj derives the friendship chains successfully — both account references resolve, the receiving chain yields addresses, and the sending path is present. There are zero decryption failures on the dashj side across the whole session.
So one implementation reads this contact's xpub and the other cannot, from identical inputs. The remaining explanation is that platform-wallet derives a different private key (or a different shared secret) for this identity key slot than dashj does.
Scope on the affected wallet: 119 of 120 contacts register fine; only this one fails.
Second defect: the failure is unappealable
register_external_contact_accountclassifies a decrypt failure asPermanent, which marks the channel broken with no retry:A wrong shared secret is not proof that the document is bad — as this case shows, the same ciphertext is decryptable. Treating it as permanent means one bad key guess disables a contact for the life of the wallet, with no user-visible cause and no way to recover. The user can neither pay that contact nor match payments from them.
Suggested split:
ENCRYPTION-purpose identity keys, ideally with a cross-implementation vector testPermanent; a shared-secret mismatch should leave the channel retryableReproduction
Not reproducible on demand yet — it is one contact on one field wallet. Device logs are available privately. A cross-implementation test vector (dashj-produced encrypted xpub decrypted by platform-wallet) would pin it deterministically and guard the regression.