Skip to content

FFI parity: verification introspection, key certifications, security profiles#15

Open
ronaldtse wants to merge 8 commits into
mainfrom
ffi-parity
Open

FFI parity: verification introspection, key certifications, security profiles#15
ronaldtse wants to merge 8 commits into
mainfrom
ffi-parity

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

Summary

FFI-parity pass against librnp 0.18.1/main. The extension exposed ~30 PHP functions of librnp's 296-symbol FFI; this PR closes the highest-value gaps — verification/signature introspection, the 0.18 key-certification API, and security profiles — with new phpt coverage. rnp_arginfo.h was regenerated with PHP 8.5's gen_stub.php; the build stays warning-free.

New/changed PHP API

  • Verification introspection: rnp_op_verify()/rnp_op_verify_detached() accept optional int $flags = 0 (BC); per-signature arrays now include alg, keyid, key_fprint, features, key_flags, key_expiration, primary_uid, trust_level/amount, revoker, revocation_code/reason, key_server(+prefs), preferred_algs/hashes/zalgs, errors (via rnp_signature_error_count/at — covers invalid-signature reporting), and subpackets (type/hashed/critical/data). Top level adds recipients, symencs, used_recipient, used_symenc.
  • New functions: rnp_key_get_signatures(), rnp_uid_get_signatures() (with validity_status and exported packet data), rnp_key_certification_create()/rnp_key_direct_signature_create()/rnp_key_revocation_signature_create() returning the new final class RnpKeySignature (all setters folded into an options array, idiomatic for this extension), rnp_key_signature_sign(), rnp_key_remove_signatures(), rnp_add/get/remove_security_rule(), rnp_set_timestamp(), rnp_request_password() (buffer cleared after copy). rnp_key_get_info() gains version and revokers.
  • New constants: RNP_VERIFY_*, RNP_SIGNATURE_REVALIDATE, RNP_KEY_USAGE_*, RNP_KEY_FEATURE_*, RNP_CERTIFICATION_*, RNP_KEY_SERVER_NO_MODIFY, RNP_REVOKER_SENSITIVE, RNP_KEY_SIGNATURE_*, RNP_SECURITY_*.

Tests

  • New tests/019.phpt (verify flags, invalid-signature error codes, subpacket + recipient/symenc enumeration), 020.phpt (security-rule roundtrip: SHA-1 invalid by default → allowed via override → invalid after removal), 021.phpt (certification/direct/revocation create+sign read-back, error paths, double-sign exception), 022.phpt (request_password); 009/013 updated for the extended array shapes; package.xml updated.
  • 22/22 phpt pass against librnp v0.18.1 and against main; zero compiler warnings.

Remaining scope (noted for follow-ups)

Most of the ~240 still-unbound symbols are either already covered by options arrays (the granular rnp_op_* setters), or out of this extension's idioms (stream/callback APIs). Notable candidates for later: key protect/lock/unlock, rnp_key_add_uid, rnp_uid_get_*, rnp_enarmor/rnp_dearmor, JSON key dumps, remaining key getters foldable into rnp_key_get_info. Upstream gap found: librnp's rnp_result_to_string returns "Unsupported error code" for the RNP_ERROR_SIG_* range, so errors[] entries are only partially descriptive.

Extend rnp_op_verify()/rnp_op_verify_detached() results with full
per-signature information backed by the rnp_signature_get_*() family:
signature algorithm, signer keyid and fingerprint, features, key flags
and expiration, primary uid flag, trust level, revoker, revocation
reason, key server and preferences, preferred algorithm lists,
validation error codes (rnp_signature_error_count()/at()) and
subpacket enumeration (rnp_signature_subpacket_*()). Both functions
also gained an optional flags parameter (rnp_op_verify_set_flags())
and report recipients and symencs of encrypted data together with the
ones used for decryption.

New functions rnp_key_get_signatures() and rnp_uid_get_signatures()
return the same signature information for key direct/revocation
signatures and userid certifications, including the exported signature
packet (rnp_signature_export()).

New constants: RNP_VERIFY_*, RNP_SIGNATURE_REVALIDATE.

Also make the sub_alg parameter of rnp_op_generate_key() explicitly
nullable in the stub as required by the PHP 8.5 gen_stub.php.
Back the new fields with rnp_key_get_version(),
rnp_key_get_revoker_count() and rnp_key_get_revoker_at().
Wrap rnp_key_certification_create(), rnp_key_direct_signature_create()
and rnp_key_revocation_signature_create(). All the
rnp_key_signature_set_*()/add_preferred_*() customization calls are
folded into an options array of the create functions, which return an
opaque RnpKeySignature object finalized with rnp_key_signature_sign().
Also add rnp_key_remove_signatures().

New constants: RNP_KEY_USAGE_*, RNP_KEY_FEATURE_*, RNP_CERTIFICATION_*,
RNP_KEY_SERVER_NO_MODIFY, RNP_REVOKER_SENSITIVE, RNP_KEY_SIGNATURE_*.
Wrap rnp_add_security_rule(), rnp_get_security_rule(),
rnp_remove_security_rule() and rnp_set_timestamp(), allowing to
override default algorithm security rules (i.e. allow SHA1
signatures) and to set a fixed timestamp for all operations.

Also wrap rnp_request_password(), which requests a password via the
callback configured with rnp_ffi_set_pass_provider().

New constants: RNP_SECURITY_*.
Wrap rnp_key_protect()/rnp_key_unprotect() (password plus an options
array with cipher, hash, iterations and mode), rnp_key_lock(),
rnp_key_unlock() and rnp_key_set_expiration(). rnp_key_get_info()
now reports the expiration time and provides rich uid entries
(data, type, is_primary, is_valid, is_revoked, signature_count)
instead of plain strings.

Add the user-ID API: rnp_key_add_uid() with hash/expiration/
key_flags/primary options, rnp_uid_get_info() returning the uid
model together with the revocation signature when present, and
rnp_uid_remove(). Uid handles are always released via
rnp_uid_handle_destroy() as required by the FFI.

New constants: RNP_USER_ID, RNP_USER_ATTR.
Wrap rnp_enarmor()/rnp_dearmor() as string in/out functions with an
optional armor type parameter (guessed from the contents when NULL),
and rnp_calculate_iterations() for time-based s2k iteration counts.
Wrap rnp_key_to_json() and rnp_key_packets_to_json(), and change
rnp_dump_packets_to_json() to return the decoded JSON as PHP arrays
(json_decode(..., true) semantics) instead of opaque strings, matching
the extension's array-shaped API. Signature listings produced by
rnp_key_get_signatures()/rnp_uid_get_signatures() now also carry the
decoded rnp_signature_packet_to_json() dump under the json key.
JSON result buffers are wiped with rnp_buffer_clear() before release
since they may contain secret key material.

New constants: RNP_JSON_PUBLIC_MPIS, RNP_JSON_SECRET_MPIS,
RNP_JSON_SIGNATURE_MPIS.
Exercise the file name/modification time, signature expiration time
and AEAD chunk size options of the encrypt/sign options arrays in
packet dumps and verification results (tests/028.phpt).

Also list the recently added test files in package.xml.
ronaldtse added a commit to rnpgp/rnp that referenced this pull request Jul 22, 2026
The signature validation error range (RNP_ERROR_SIG_*, starting at
0x14000000 and added in 1c616df) had no cases in
rnp_result_to_string(), so all 28 codes fell through to
"Unsupported error code". These codes reach FFI callers via
rnp_signature_error_at() and rnp_op_verify_signature_get_status(),
leaving downstream bindings without meaningful messages; surfaced
by rnpgp/php-rnp#15.

Add one case with a descriptive string per code, and extend
test_result_to_string to cover the new range and to assert the
exact string of every defined error code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant