{"record":{"id":"8109688de2082bf4","repo":"passbolt/passbolt_api","slug":"could-not-validate-key-revocation","errorCode":null,"errorMessage":"Could not validate key revocation.","messagePattern":"Could not validate key revocation\\.","errorType":"validation","errorClass":"CustomValidationException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AbstractAccountRecoveryOrganizationPolicySetService.php","lineNumber":310,"sourceCode":"     * Check user provided valid valid account_recovery_organization_revoked_key\n     * Return patched entity corresponding to the key to revoke (e.g. to update in DB)\n     *\n     * @param \\App\\Utility\\UserAccessControl $uac user access control\n     * @throws \\App\\Error\\Exception\\CustomValidationException if any of the check on fingerprint or armored key data fails\n     * @return \\Passbolt\\AccountRecovery\\Model\\Entity\\AccountRecoveryOrganizationPublicKey currently in use key patched with new revoked armored_key\n     */\n    public function buildRevokedKeyEntityFromDataOrFail(UserAccessControl $uac): AccountRecoveryOrganizationPublicKey\n    {\n        try {\n            $data = $this->getData('account_recovery_organization_revoked_key');\n            $entity = $this->AccountRecoveryOrganizationPublicKeys->buildAndValidateEntity($uac, $data);\n            $oldEntity = $this->findActiveKeyByFingerprintOrFail($entity->fingerprint);\n            PublicKeyValidationService::parseAndValidatePublicKey(\n                $entity->armored_key,\n                PublicKeyValidationService::getRevokedKeyRules()\n            );\n        } catch (ValidationException | CustomValidationException $exception) {\n            throw new CustomValidationException(__('Could not validate key revocation.'), [\n                'account_recovery_organization_revoked_key' => $exception->getErrors(),\n            ]);\n        } catch (Exception $exception) {\n            throw new CustomValidationException(__('Could not validate key revocation.'), [\n                'account_recovery_organization_revoked_key' => [\n                    'armored_key' => [\n                        'invalidArmoredKey' => $exception->getMessage(),\n                    ],\n                ],\n            ]);\n        }\n\n        // Check revocation cryptographically\n        // parseAndValidatePublicKey only do superficial signature check\n        if (!(new PublicKeyRevocationCheckService())->check($entity->armored_key)) {\n            throw new CustomValidationException(__('Could not validate key revocation.'), [\n                'account_recovery_organization_revoked_key' => __('Could not validate key revocation.'),\n            ]);","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AbstractAccountRecoveryOrganizationPolicySetService.php#L292-L328","documentation":"Wrapper error thrown by buildRevokedKeyEntityFromDataOrFail when a ValidationException/CustomValidationException occurs while validating the revoked organization key — either the original active key lookup (findActiveKeyByFingerprintOrFail) or PublicKeyValidationService::parseAndValidatePublicKey with the revoked-key rules fails. Errors are nested under 'account_recovery_organization_revoked_key'.","triggerScenarios":"Calling set() or disablePolicy() (policy disable flow) where the submitted revocation certificate's armored_key fails revoked-key validation rules, or the fingerprint doesn't correspond to an active organization key.","commonSituations":"Admin submits the current public key instead of a revocation certificate; revocation key has a fingerprint matching no active key; malformed or truncated revocation armor during policy disable.","solutions":["Inspect errors.account_recovery_organization_revoked_key for the nested rule failure","Ensure the armored_key is a proper revocation certificate generated with `gpg --gen-revoke <fingerprint>` (armored)","Ensure the fingerprint matches the currently active organization recovery key","Validate locally that the certificate parses: `gpg --import --import-options show-only <revoke.asc>`"],"exampleFix":"// before\n{\"armored_key\": \"<normal public key>\", \"fingerprint\": \"<fp>\"}\n// after\ngpg --armor --gen-revoke <fp> > revoke.asc\n{\"armored_key\": \"<contents of revoke.asc>\", \"fingerprint\": \"<fp>\"}","handlingStrategy":"validation","validationCode":"// A revocation cert looks like a public key block; ensure it parses and matches the active fp\nif (!armored_key.includes('-----BEGIN PGP PUBLIC KEY BLOCK-----')) {\n  throw new Error('Provide an armored revocation certificate');\n}\nif (fingerprint !== activeOrgKeyFingerprint) {\n  throw new Error('Fingerprint must match the active organization key');\n}","typeGuard":"function isRevocationPayloadForActiveKey(p, activeFp) {\n  return typeof p.armored_key === 'string'\n    && p.armored_key.includes('BEGIN PGP PUBLIC KEY BLOCK')\n    && p.fingerprint === activeFp;\n}","tryCatchPattern":"try {\n  await api.disableOrganizationPolicy(revocationPayload);\n} catch (e) {\n  const nested = e.body?.errors?.account_recovery_organization_revoked_key;\n  console.error(nested); // shows which rule (or key lookup) failed\n}","preventionTips":["Generate revocation certificates at key-creation time and store them safely","Ensure the fingerprint matches the currently active organization key","Verify the certificate locally with gpg show-only import options"],"tags":["openpgp","account-recovery","revocation","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}