diff --git a/doc/api_ref/ffi.rst b/doc/api_ref/ffi.rst index 5673815f176..b2590f86d62 100644 --- a/doc/api_ref/ffi.rst +++ b/doc/api_ref/ffi.rst @@ -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 @@ -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) @@ -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 + 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 `_. + +.. 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) ---------------------------------------- diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index b45c270c233..07aa4c9edb2 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -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"; diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index 5b61f0dac2e..e9e48fddb23 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -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, @@ -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) @@ -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); + +/** +* 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); + +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 diff --git a/src/lib/ffi/ffi_cert.cpp b/src/lib/ffi/ffi_cert.cpp index 0a918f56c34..6da2d9f3651 100644 --- a/src/lib/ffi/ffi_cert.cpp +++ b/src/lib/ffi/ffi_cert.cpp @@ -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 @@ -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) { +#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; + } + + return ffi_new_object(entry, std::make_unique(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(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, diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 66e7af8b344..18bf2b7f1c6 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -17,6 +17,7 @@ #include #include #include + #include #include #include #include @@ -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 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)); + + 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(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));