Skip to content
Open
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
46 changes: 46 additions & 0 deletions doc/api_ref/ffi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,52 @@ X.509 Certificate Revocation Lists

Load a CRL from a file.

.. cpp:function:: int botan_x509_crl_create(botan_x509_crl_t* crl_obj, \
botan_rng_t rng, \
botan_x509_cert_t ca_cert, \
botan_privkey_t ca_key, \
uint64_t issue_time, \
uint32_t next_update, \
const char* hash_fn, \
const char* padding)

Create a new CRL. ``issue_time`` is expected to be a UNIX timestamp, in seconds.
``next_update`` is expected to be in seconds.
``hash_fn`` and ``padding`` may be NULL.

.. cpp:enum:: botan_x509_crl_reason_code

CRL revocation reason codes. Allowed values: `UNSPECIFIED`,
`KEY_COMPROMISE`, `CA_COMPROMISE`, `AFFILIATION_CHANGED`,
`SUPERSEDED`, `CESSATION_OF_OPERATION`, `CERTIFICATE_HOLD`,
`REMOVE_FROM_CRL`, `PRIVILEGE_WITHDRAWN`, `AA_COMPROMISE`.

.. cpp:function:: int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code)

Create a new CRL entry to be added to a CRL later.

.. cpp:function:: int botan_x509_crl_update(botan_x509_crl_t* crl_obj, \
botan_x509_crl_t last_crl, \
botan_rng_t rng, \
botan_x509_cert_t ca_cert, \
botan_privkey_t ca_key, \
uint64_t issue_time, \
uint32_t next_update, \
const botan_x509_crl_entry_t* new_entries, \
size_t new_entries_len, \
const char* hash_fn, \
const char* padding)

Revoke some certificates. This does not update the given CRL in place.
``issue_time`` is expected to be a UNIX timestamp, in seconds.
``next_update`` is expected to be in seconds.
``hash_fn`` and ``padding`` may be NULL.
``new_entries`` is an array of ``botan_x509_crl_entry_t`` objects, ``new_entries_len`` is its length.

.. cpp:function:: int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key, int* result)

Verify the signature of a CRL. Sets ``result`` to 1 if the signature is valid, otherwise to 0.

.. cpp:function:: int botan_x509_crl_destroy(botan_x509_crl_t crl)

Destroy the CRL object.
Expand Down
75 changes: 74 additions & 1 deletion src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2475,6 +2475,79 @@ int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], siz
BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_this_update(botan_x509_crl_t crl, uint64_t* time_since_epoch);
BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch);

/**
* Create a new CRL
* @param crl_obj The newly created CRL
* @param rng a random number generator object
* @param ca_cert The CA Certificate the CRL belongs to
* @param ca_key The private key of that CA
* @param issue_time The time when the CRL becomes valid
* @param next_update The time in seconds until the CRL expires
* @param hash_fn The hash function to use, may be null
* @param padding The padding to use, may be null
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_create(botan_x509_crl_t* crl_obj,
botan_rng_t rng,
botan_x509_cert_t ca_cert,
botan_privkey_t ca_key,
uint64_t issue_time,
uint32_t next_update,
const char* hash_fn,
const char* padding);

/* Must match values of CRL_Code in pkix_enums.h */
enum botan_x509_crl_reason_code /* NOLINT(*-enum-size) */ {
BOTAN_CRL_ENTRY_UNSPECIFIED = 0,
BOTAN_CRL_ENTRY_KEY_COMPROMISE = 1,
BOTAN_CRL_ENTRY_CA_COMPROMISE = 2,
BOTAN_CRL_ENTRY_AFFILIATION_CHANGED = 3,
BOTAN_CRL_ENTRY_SUPERSEDED = 4,
BOTAN_CRL_ENTRY_CESSATION_OF_OPERATION = 5,
BOTAN_CRL_ENTRY_CERTIFICATE_HOLD = 6,
BOTAN_CRL_ENTRY_REMOVE_FROM_CRL = 8,
BOTAN_CRL_ENTRY_PRIVILEGE_WITHDRAWN = 9,
BOTAN_CRL_ENTRY_AA_COMPROMISE = 10
};

/**
* Create a new CRL entry that marks @p cert as revoked
* @param entry The newly created CRL entry
* @param cert The certificate to mark as revoked
* @param reason_code The reason code for revocation
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code);

/**
* Update a CRL with new revoked entries. This does not modify the old crl, and instead creates a new one.
* @param crl_obj The newly created CRL
* @param last_crl The CRL to update
* @param rng a random number generator object
* @param ca_cert The CA Certificate the CRL belongs to
* @param ca_key The private key of that CA
* @param issue_time The time when the CRL becomes valid
* @param next_update The time in seconds until the CRL expires
* @param new_entries The entries to add to the CRL
* @param new_entries_len The number of entries
* @param hash_fn The hash function to use, may be null
* @param padding The padding to use, may be null
*/
BOTAN_FFI_EXPORT(3, 11)
int botan_x509_crl_update(botan_x509_crl_t* crl_obj,
botan_x509_crl_t last_crl,
botan_rng_t rng,
botan_x509_cert_t ca_cert,
botan_privkey_t ca_key,
uint64_t issue_time,
uint32_t next_update,
const botan_x509_crl_entry_t* new_entries,
size_t new_entries_len,
const char* hash_fn,
const char* padding);

BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key, int* result);

BOTAN_FFI_EXPORT(2, 13) int botan_x509_crl_destroy(botan_x509_crl_t crl);

/**
Expand All @@ -2499,7 +2572,7 @@ BOTAN_FFI_EXPORT(3, 11) int botan_x509_crl_entries_count(botan_x509_crl_t crl, s

/**
* Return the revocation reason code for the given CRL @p entry.
* See RFC 5280 - 5.3.1 for possible reason codes.
* See `botan_x509_crl_reason_code` and 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);

Expand Down
131 changes: 118 additions & 13 deletions src/lib/ffi/ffi_cert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

#include <botan/ffi.h>

#include <botan/internal/ffi_cert.h>
Copy link
Owner

Choose a reason for hiding this comment

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

This seems to be making FFI hard-depend on x509, which is probably fine tbh, but should be codified by updated the dependencies in info.txt

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This I don't quite understand, all the methods, including ffi_cert.h have #if defined(BOTAN_HAS_X509_CERTIFICATES) guards?

#include <botan/internal/ffi_mp.h>
#include <botan/internal/ffi_pkey.h>
#include <botan/internal/ffi_rng.h>
#include <botan/internal/ffi_util.h>
#include <memory>

Expand All @@ -19,16 +22,24 @@
#include <botan/internal/ffi_oid.h>
#endif

extern "C" {
namespace {
std::chrono::system_clock::time_point timepoint_from_timestamp(uint64_t time_since_epoch) {
return std::chrono::system_clock::time_point(std::chrono::seconds(time_since_epoch));
}

using namespace Botan_FFI;
std::string default_from_ptr(const char* value) {
std::string ret;
if(value != nullptr) {
ret = value;
}
return ret;
}

#if defined(BOTAN_HAS_X509_CERTIFICATES)
} // namespace

BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
BOTAN_FFI_DECLARE_STRUCT(botan_x509_general_name_struct, Botan::GeneralName, 0x563654FD);
extern "C" {

#endif
using namespace Botan_FFI;

int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path) {
if(cert_obj == nullptr || cert_path == nullptr) {
Expand Down Expand Up @@ -782,13 +793,6 @@ const char* botan_x509_cert_validation_status(int code) {
#endif
}

#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

int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path) {
if(crl_obj == nullptr || crl_path == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
Expand Down Expand Up @@ -859,6 +863,107 @@ int botan_x509_crl_next_update(botan_x509_crl_t crl, uint64_t* time_since_epoch)
#endif
}

int botan_x509_crl_create(botan_x509_crl_t* crl_obj,
botan_rng_t rng,
botan_x509_cert_t ca_cert,
botan_privkey_t ca_key,
uint64_t issue_time,
uint32_t next_update,
const char* hash_fn,
const char* padding) {
if(Botan::any_null_pointers(crl_obj)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
auto& rng_ = safe_get(rng);
auto ca = Botan::X509_CA(
safe_get(ca_cert), safe_get(ca_key), default_from_ptr(hash_fn), default_from_ptr(padding), rng_);
auto crl = std::make_unique<Botan::X509_CRL>(
ca.new_crl(rng_, timepoint_from_timestamp(issue_time), std::chrono::seconds(next_update)));
return ffi_new_object(crl_obj, std::move(crl));
});
#else
BOTAN_UNUSED(rng, ca_cert, ca_key, hash_fn, padding, issue_time, next_update);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_entry_create(botan_x509_crl_entry_t* entry, botan_x509_cert_t cert, int reason_code) {
if(Botan::any_null_pointers(entry)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
return ffi_new_object(
entry, std::make_unique<Botan::CRL_Entry>(safe_get(cert), static_cast<Botan::CRL_Code>(reason_code)));
});
#else
BOTAN_UNUSED(cert, reason_code);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_update(botan_x509_crl_t* crl_obj,
botan_x509_crl_t last_crl,
botan_rng_t rng,
botan_x509_cert_t ca_cert,
botan_privkey_t ca_key,
uint64_t issue_time,
uint32_t next_update,
const botan_x509_crl_entry_t* new_entries,
size_t new_entries_len,
const char* hash_fn,
const char* padding) {
if(Botan::any_null_pointers(crl_obj)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
if(new_entries_len == 0) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}
auto& rng_ = safe_get(rng);
auto ca = Botan::X509_CA(
safe_get(ca_cert), safe_get(ca_key), default_from_ptr(hash_fn), default_from_ptr(padding), rng_);

std::vector<Botan::CRL_Entry> entries;
entries.reserve(new_entries_len);
for(size_t i = 0; i < new_entries_len; i++) {
entries.push_back(safe_get(new_entries[i]));
}

auto crl = std::make_unique<Botan::X509_CRL>(ca.update_crl(
safe_get(last_crl), entries, rng_, timepoint_from_timestamp(issue_time), std::chrono::seconds(next_update)));
return ffi_new_object(crl_obj, std::move(crl));
});
#else
BOTAN_UNUSED(
last_crl, rng, ca_cert, ca_key, hash_fn, padding, issue_time, next_update, new_entries, new_entries_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_verify_signature(botan_x509_crl_t crl, botan_pubkey_t key, int* result) {
if(Botan::any_null_pointers(result)) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
const bool ok = safe_get(crl).check_signature(safe_get(key));
if(ok) {
*result = 1;
} else {
*result = 0;
}
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(crl, key);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_x509_crl_destroy(botan_x509_crl_t crl) {
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_CHECKED_DELETE(crl);
Expand Down
31 changes: 31 additions & 0 deletions src/lib/ffi/ffi_cert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* (C) 2026 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_FFI_CERT_H_
#define BOTAN_FFI_CERT_H_

#include <botan/internal/ffi_util.h>

#if defined(BOTAN_HAS_X509_CERTIFICATES)
#include <botan/data_src.h>
#include <botan/x509_ca.h>
#include <botan/x509_crl.h>
#include <botan/x509cert.h>
#include <botan/x509path.h>
#endif

extern "C" {
#if defined(BOTAN_HAS_X509_CERTIFICATES)

BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
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);
BOTAN_FFI_DECLARE_STRUCT(botan_x509_general_name_struct, Botan::GeneralName, 0x563654FD);

#endif
}

#endif
1 change: 1 addition & 0 deletions src/lib/ffi/info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ brief -> "C API for Botan's functionality"
</module_info>

<header:internal>
ffi_cert.h
ffi_ec.h
ffi_mp.h
ffi_oid.h
Expand Down
Loading
Loading