/* * (C) 2015,2017,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #if defined(BOTAN_HAS_X509_CERTIFICATES) #include #include #include #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 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 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 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 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_DO(Botan::X509_Certificate, cert, c, { const Botan::Key_Constraints k = static_cast(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(reference_time)); std::vector 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 trusted_from_path; std::unique_ptr trusted_extra; std::vector 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(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(code); return Botan::to_string(sc); #else return nullptr; #endif } }