Revision control
Copy as Markdown
Other Tools
/*
* (C) 2015,2017,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include <botan/ffi.h>
#include <botan/internal/ffi_util.h>
#include <botan/internal/ffi_pkey.h>
#if defined(BOTAN_HAS_X509_CERTIFICATES)
#include <botan/x509cert.h>
#include <botan/x509path.h>
#include <botan/x509_crl.h>
#include <botan/data_src.h>
#endif
extern "C" {
using namespace Botan_FFI;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
#endif
int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path)
{
if(!cert_obj || !cert_path)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
return ffi_guard_thunk(__func__, [=]() -> int {
std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(cert_path));
*cert_obj = new botan_x509_cert_struct(c.release());
return BOTAN_FFI_SUCCESS;
});
#else
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert)
{
if(!cert_obj)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
return ffi_guard_thunk(__func__, [=]() -> int {
std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(safe_get(cert)));
*cert_obj = new botan_x509_cert_struct(c.release());
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(cert);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert_bits[], size_t cert_bits_len)
{
if(!cert_obj || !cert_bits)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
Botan::DataSource_Memory bits(cert_bits, cert_bits_len);
std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(bits));
*cert_obj = new botan_x509_cert_struct(c.release());
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(cert_bits_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key)
{
if(key == nullptr)
return BOTAN_FFI_ERROR_NULL_POINTER;
*key = nullptr;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
std::unique_ptr<Botan::Public_Key> publicKey = safe_get(cert).load_subject_public_key();
*key = new botan_pubkey_struct(publicKey.release());
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(cert);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_issuer_dn(botan_x509_cert_t cert,
const char* key, size_t index,
uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.issuer_info(key).at(index)); });
#else
BOTAN_UNUSED(cert, key, index, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_subject_dn(botan_x509_cert_t cert,
const char* key, size_t index,
uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.subject_info(key).at(index)); });
#else
BOTAN_UNUSED(cert, key, index, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.to_string()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_RETURNING(Botan::X509_Certificate, cert, c, {
const Botan::Key_Constraints k = static_cast<Botan::Key_Constraints>(key_usage);
if(c.allowed_usage(k))
return BOTAN_FFI_SUCCESS;
return 1;
});
#else
BOTAN_UNUSED(cert, key_usage);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_destroy(botan_x509_cert_t cert)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_CHECKED_DELETE(cert);
#else
BOTAN_UNUSED(cert);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_before().to_string()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_after().to_string()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
*time_since_epoch = c.not_before().time_since_epoch();
});
#else
BOTAN_UNUSED(cert, time_since_epoch);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
*time_since_epoch = c.not_after().time_since_epoch();
});
#else
BOTAN_UNUSED(cert, time_since_epoch);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.serial_number()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.fingerprint(hash)); });
#else
BOTAN_UNUSED(cert, hash, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.authority_key_id()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_key_id()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_public_key_bits()); });
#else
BOTAN_UNUSED(cert, out, out_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname)
{
if(hostname == nullptr)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c,
{ return c.matches_dns_name(hostname) ? 0 : -1; });
#else
BOTAN_UNUSED(cert);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_cert_verify(int* result_code,
botan_x509_cert_t cert,
const botan_x509_cert_t* intermediates,
size_t intermediates_len,
const botan_x509_cert_t* trusted,
size_t trusted_len,
const char* trusted_path,
size_t required_strength,
const char* hostname_cstr,
uint64_t reference_time)
{
if(required_strength == 0)
required_strength = 110;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
const auto validation_time = reference_time == 0 ?
std::chrono::system_clock::now() :
std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
std::vector<Botan::X509_Certificate> end_certs;
end_certs.push_back(safe_get(cert));
for(size_t i = 0; i != intermediates_len; ++i)
end_certs.push_back(safe_get(intermediates[i]));
std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
std::vector<Botan::Certificate_Store*> trusted_roots;
if(trusted_path && *trusted_path)
{
trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
trusted_roots.push_back(trusted_from_path.get());
}
if(trusted_len > 0)
{
trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
for(size_t i = 0; i != trusted_len; ++i)
{
trusted_extra->add_certificate(safe_get(trusted[i]));
}
trusted_roots.push_back(trusted_extra.get());
}
Botan::Path_Validation_Restrictions restrictions(false, required_strength);
auto validation_result = Botan::x509_path_validate(end_certs,
restrictions,
trusted_roots,
hostname,
usage,
validation_time);
if(result_code)
*result_code = static_cast<int>(validation_result.result());
if(validation_result.successful_validation())
return 0;
else
return 1;
});
#else
BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
const char* botan_x509_cert_validation_status(int code)
{
if(code < 0)
return nullptr;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
Botan::Certificate_Status_Code sc = static_cast<Botan::Certificate_Status_Code>(code);
return Botan::to_string(sc);
#else
return nullptr;
#endif
}
#if defined(BOTAN_HAS_X509_CERTIFICATES)
BOTAN_FFI_DECLARE_STRUCT(botan_x509_crl_struct, Botan::X509_CRL, 0x2C628910);
#endif
int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path)
{
if(!crl_obj || !crl_path)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
return ffi_guard_thunk(__func__, [=]() -> int {
std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(crl_path));
*crl_obj = new botan_x509_crl_struct(c.release());
return BOTAN_FFI_SUCCESS;
});
#else
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len)
{
if(!crl_obj || !crl_bits)
return BOTAN_FFI_ERROR_NULL_POINTER;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
Botan::DataSource_Memory bits(crl_bits, crl_bits_len);
std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(bits));
*crl_obj = new botan_x509_crl_struct(c.release());
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(crl_bits_len);
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);
#else
BOTAN_UNUSED(crl);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert)
{
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return BOTAN_FFI_RETURNING(Botan::X509_CRL, crl, c, {
return c.is_revoked(safe_get(cert)) ? 0 : -1;
});
#else
BOTAN_UNUSED(cert);
BOTAN_UNUSED(crl);
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,
size_t intermediates_len,
const botan_x509_cert_t* trusted,
size_t trusted_len,
const botan_x509_crl_t* crls,
size_t crls_len,
const char* trusted_path,
size_t required_strength,
const char* hostname_cstr,
uint64_t reference_time)
{
if(required_strength == 0)
required_strength = 110;
#if defined(BOTAN_HAS_X509_CERTIFICATES)
return ffi_guard_thunk(__func__, [=]() -> int {
const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
const auto validation_time = reference_time == 0 ?
std::chrono::system_clock::now() :
std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
std::vector<Botan::X509_Certificate> end_certs;
end_certs.push_back(safe_get(cert));
for(size_t i = 0; i != intermediates_len; ++i)
end_certs.push_back(safe_get(intermediates[i]));
std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
std::vector<Botan::Certificate_Store*> trusted_roots;
if(trusted_path && *trusted_path)
{
trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
trusted_roots.push_back(trusted_from_path.get());
}
if(trusted_len > 0)
{
trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
for(size_t i = 0; i != trusted_len; ++i)
{
trusted_extra->add_certificate(safe_get(trusted[i]));
}
trusted_roots.push_back(trusted_extra.get());
}
if(crls_len > 0)
{
trusted_crls.reset(new Botan::Certificate_Store_In_Memory);
for(size_t i = 0; i != crls_len; ++i)
{
trusted_crls->add_crl(safe_get(crls[i]));
}
trusted_roots.push_back(trusted_crls.get());
}
Botan::Path_Validation_Restrictions restrictions(false, required_strength);
auto validation_result = Botan::x509_path_validate(end_certs,
restrictions,
trusted_roots,
hostname,
usage,
validation_time);
if(result_code)
*result_code = static_cast<int>(validation_result.result());
if(validation_result.successful_validation())
return 0;
else
return 1;
});
#else
BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time, crls, crls_len);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
}