Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions doc/api_ref/ffi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ The following enum values are defined in the FFI header:
An operation was invoked that makes sense for the object, but it is in the
wrong state to perform it.

.. cpp:enumerator:: BOTAN_FFI_ERROR_OUT_OF_RANGE = -36

Querying an enumerable value resulted in an "out of range" error. This error
code may be used as the marker for the end of a value enumeration.

.. cpp:enumerator:: BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40

This is returned if the functionality is not available for some reason. For
Expand Down Expand Up @@ -1808,6 +1813,10 @@ X.509 Certificate Revocation Lists

An opaque data type for an X.509 CRL.

.. cpp:type:: opaque* botan_x509_crl_entry_t

An opaque data type for an X.509 CRL entry.

.. cpp:function:: int botan_x509_crl_load(botan_x509_crl_t* crl_obj, \
const uint8_t crl[], size_t crl_len)

Expand Down Expand Up @@ -1836,6 +1845,31 @@ X.509 Certificate Revocation Lists
Check whether a given ``crl`` contains a given ``cert``.
Return ``0`` when the certificate is revoked, ``-1`` otherwise.

.. cpp:function:: int botan_x509_crl_entries(botan_x509_crl_t crl, \
size_t index, \
botan_x509_crl_entry_t *entry)

List the entries in the CRL. Using the `index` parameter applications can
Comment thread
reneme marked this conversation as resolved.
enumerate all entries in the CRL. If the list of entries is exhausted, this
will return :cpp:enumerator:`BOTAN_FFI_ERROR_OUT_OF_RANGE`.

.. cpp:function:: int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code)

Get the revocation reason code for the given CRL entry. The reason code is
according to `RFC 5280 - 5.3.1 <https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1>`_.

.. cpp:function:: int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch)

Get the revocation date for the given CRL entry, as seconds since epoch.

.. cpp:function:: int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view)

View the serial number for the given CRL entry.

.. cpp:function:: int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry)

Destroy the CRL entry object.

ZFEC (Forward Error Correction)
----------------------------------------

Expand Down
3 changes: 3 additions & 0 deletions src/lib/ffi/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ const char* botan_error_description(int err) {
case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE:
return "Invalid object state";

case BOTAN_FFI_ERROR_OUT_OF_RANGE:
return "Index out of range";

case BOTAN_FFI_ERROR_NOT_IMPLEMENTED:
return "Not implemented";

Expand Down
36 changes: 36 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ enum BOTAN_FFI_ERROR /* NOLINT(*-enum-size,*-use-enum-class) */ {
BOTAN_FFI_ERROR_KEY_NOT_SET = -33,
BOTAN_FFI_ERROR_INVALID_KEY_LENGTH = -34,
BOTAN_FFI_ERROR_INVALID_OBJECT_STATE = -35,
BOTAN_FFI_ERROR_OUT_OF_RANGE = -36,

BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40,
BOTAN_FFI_ERROR_INVALID_OBJECT = -50,
Expand Down Expand Up @@ -2238,6 +2239,7 @@ BOTAN_FFI_EXPORT(2, 8) const char* botan_x509_cert_validation_status(int code);
**************************/

typedef struct botan_x509_crl_struct* botan_x509_crl_t;
typedef struct botan_x509_crl_entry_struct* botan_x509_crl_entry_t;

BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path);
BOTAN_FFI_EXPORT(2, 13)
Expand All @@ -2254,6 +2256,40 @@ BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_destroy(botan_x509_crl_t crl);
*/
BOTAN_FFI_EXPORT(2, 13) int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert);

/**
* Allows iterating all entries of the CRL.
*
* @param crl the CRL whose entries should be listed
* @param index the index of the CRL entry to return
* @param entry an object handle containing the CRL entry data
*
* @returns BOTAN_FFI_ERROR_OUT_OF_RANGE if the given @p index is out of range of
* the CRL entry list.
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit: botan-rs has this neat macro, to simplify calling methods that write to a new object. It expects that new object to be the first parameter though. (used e.g. like so)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion in #5230 (comment)


/**
Comment thread
reneme marked this conversation as resolved.
* Return the revocation reason code for the given CRL @p entry.
* See RFC 5280 - 5.3.1 for possible reason codes.
*/
BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code);

/**
* Return the revocation date for the given CRL @p entry as time since epoch
* in seconds.
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch);

/**
* View the serial number associated with the given CRL @p entry.
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view);
Comment on lines +2285 to +2289

@reneme reneme Jan 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the same argument as in #5221 regarding OIDs and also this explicit remark by @arckoor, we should consider adding an "overload" to export the CRL entry serial as a botan_mp_t object. I.e. provide botan_x509_crl_entry_serial_number(..., botan_mp_t* serial) along with this byte-oriented view function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be an argument for also adding an MPI view function for the existing X509 cert serial

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll combine those in a follow-up PR after merging the current stack of changes.


BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry);

/**
* Different flavor of `botan_x509_cert_verify`, supports revocation lists.
* CRLs are passed as an array, same as intermediates and trusted CAs
Expand Down
72 changes: 72 additions & 0 deletions src/lib/ffi/ffi_cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ const char* botan_x509_cert_validation_status(int code) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)

BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910);
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_entry_struct, Botan::CRL_Entry, 0x4EAA5346);

#endif

Expand Down Expand Up @@ -450,6 +451,77 @@ int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert) {
#endif
}

int botan_x509_crl_entries(botan_x509_crl_t crl, size_t index, botan_x509_crl_entry_t* entry) {
Comment thread
reneme marked this conversation as resolved.
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_VISIT(crl, [=](const Botan::X509_CRL& c) -> int {
const auto& entries = c.get_revoked();
if(index >= entries.size()) {
return BOTAN_FFI_ERROR_OUT_OF_RANGE;
}

if(Botan::any_null_pointers(entry)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

Comment thread
reneme marked this conversation as resolved.
return ffi_new_object(entry, std::make_unique<Botan::CRL_Entry>(entries[index]));
});
#else
BOTAN_UNUSED(crl, index, entry);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_entry_destroy(botan_x509_crl_entry_t entry) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_CHECKED_DELETE(entry);
#else
BOTAN_UNUSED(entry);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_entry_reason(botan_x509_crl_entry_t entry, int* reason_code) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
if(Botan::any_null_pointers(reason_code)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*reason_code = static_cast<int>(e.reason_code());
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(entry, reason_code);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_entry_view_serial_number(botan_x509_crl_entry_t entry, botan_view_ctx ctx, botan_view_bin_fn view) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_VISIT(
entry, [=](const Botan::CRL_Entry& e) { return invoke_view_callback(view, ctx, e.serial_number()); });
#else
BOTAN_UNUSED(entry, ctx, view);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_entry_revocation_date(botan_x509_crl_entry_t entry, uint64_t* time_since_epoch) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_VISIT(entry, [=](const Botan::CRL_Entry& e) {
if(Botan::any_null_pointers(time_since_epoch)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*time_since_epoch = e.expire_time().time_since_epoch();
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(entry, time_since_epoch);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_cert_verify_with_crl(int* result_code,
botan_x509_cert_t cert,
const botan_x509_cert_t* intermediates,
Expand Down
28 changes: 28 additions & 0 deletions src/tests/test_ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <botan/ffi.h>
#include <botan/hex.h>
#include <botan/mem_ops.h>
#include <botan/pkix_enums.h>
#include <botan/internal/calendar.h>
#include <botan/internal/fmt.h>
#include <botan/internal/stl_util.h>
Expand Down Expand Up @@ -727,11 +728,38 @@ class FFI_CRL_Test final : public FFI_Test {
TEST_FFI_OK(botan_x509_cert_destroy, (cert1));

botan_x509_cert_t cert2;
std::vector<uint8_t> cert2_serial;
size_t cert2_serial_len = 0;
REQUIRE_FFI_OK(botan_x509_cert_load_file, (&cert2, Test::data_file("x509/nist/test20/int.crt").c_str()));
TEST_FFI_RC(0, botan_x509_is_revoked, (crl, cert2));
TEST_FFI_RC(-1, botan_x509_is_revoked, (bytecrl, cert2));
TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE,
botan_x509_cert_get_serial_number,
(cert2, nullptr, &cert2_serial_len));
cert2_serial.resize(cert2_serial_len);
TEST_FFI_OK(botan_x509_cert_get_serial_number, (cert2, cert2_serial.data(), &cert2_serial_len));
TEST_FFI_OK(botan_x509_cert_destroy, (cert2));

botan_x509_crl_entry_t entry;
TEST_FFI_OK(botan_x509_crl_entries, (crl, 0, &entry));
Comment thread
reneme marked this conversation as resolved.

ViewBytesSink serial;
uint64_t ts;
int reason;

TEST_FFI_OK(botan_x509_crl_entry_view_serial_number, (entry, serial.delegate(), serial.callback()));
TEST_FFI_OK(botan_x509_crl_entry_revocation_date, (entry, &ts));
TEST_FFI_OK(botan_x509_crl_entry_reason, (entry, &reason));
TEST_FFI_OK(botan_x509_crl_entry_destroy, (entry));

result.test_is_eq(
"Reason", static_cast<uint8_t>(reason), Botan::to_underlying(Botan::CRL_Code::KeyCompromise));
result.test_is_eq("Revocation time", ts, Botan::calendar_point(1999, 1, 1, 12, 0, 0).seconds_since_epoch());
result.test_eq("Revoked cert serial", serial.get(), cert2_serial);

TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (crl, 1, &entry));
TEST_FFI_RC(BOTAN_FFI_ERROR_OUT_OF_RANGE, botan_x509_crl_entries, (bytecrl, 0, &entry));

TEST_FFI_OK(botan_x509_crl_destroy, (crl));
TEST_FFI_OK(botan_x509_crl_destroy, (bytecrl));
TEST_FFI_OK(botan_x509_crl_destroy, (crl_without_next_update));
Expand Down
Loading