Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicitly export public API #458

Merged
merged 7 commits into from
Apr 16, 2019
Merged

Explicitly export public API #458

merged 7 commits into from
Apr 16, 2019

Conversation

ilammy
Copy link
Collaborator

@ilammy ilammy commented Apr 11, 2019

Historically, Themis did not manage public API exports at all. We exported all non-static functions and that's it. WebAssembly builds with Emscripten will require marking exported functions with EMSCRIPTEN_KEEPALIVE attribute so now is a good moment to explicitly define our public API.

Only functions explicitly marked as THEMIS_API and SOTER_API will be exported from libraries and available to users. Everything else will be hidden and not available (even if the users include some private header and try calling a function).

Here are details on functions that are removed from publicly visible exports with these changes:

Soter
Function
🚫 algid_to_evp
🚫 algid_to_evp_aead
clip_random_32
🚫 crypto_sign_ed25519_ref10_fe_0
🚫 crypto_sign_ed25519_ref10_fe_1
🚫 crypto_sign_ed25519_ref10_fe_add
🚫 crypto_sign_ed25519_ref10_fe_cmov
🚫 crypto_sign_ed25519_ref10_fe_copy
🚫 crypto_sign_ed25519_ref10_fe_frombytes
🚫 crypto_sign_ed25519_ref10_fe_invert
🚫 crypto_sign_ed25519_ref10_fe_isnegative
crypto_sign_ed25519_ref10_fe_isnonzero
🚫 crypto_sign_ed25519_ref10_fe_mul
crypto_sign_ed25519_ref10_fe_neg
🚫 crypto_sign_ed25519_ref10_fe_pow22523
🚫 crypto_sign_ed25519_ref10_fe_sq
🚫 crypto_sign_ed25519_ref10_fe_sq2
🚫 crypto_sign_ed25519_ref10_fe_sub
crypto_sign_ed25519_ref10_fe_tobytes
🚫 crypto_sign_ed25519_ref10_ge_add
crypto_sign_ed25519_ref10_ge_double_scalarmult_vartime
🚫 crypto_sign_ed25519_ref10_ge_frombytes_negate_vartime
🚫 crypto_sign_ed25519_ref10_ge_madd
🚫 crypto_sign_ed25519_ref10_ge_msub
🚫 crypto_sign_ed25519_ref10_ge_p1p1_to_p2
🚫 crypto_sign_ed25519_ref10_ge_p1p1_to_p3
🚫 crypto_sign_ed25519_ref10_ge_p2_0
🚫 crypto_sign_ed25519_ref10_ge_p2_dbl
🚫 crypto_sign_ed25519_ref10_ge_p3_0
🚫 crypto_sign_ed25519_ref10_ge_p3_dbl
crypto_sign_ed25519_ref10_ge_p3_tobytes
🚫 crypto_sign_ed25519_ref10_ge_p3_to_cached
🚫 crypto_sign_ed25519_ref10_ge_p3_to_p2
🚫 crypto_sign_ed25519_ref10_ge_precomp_0
crypto_sign_ed25519_ref10_ge_scalarmult_base
🚫 crypto_sign_ed25519_ref10_ge_sub
crypto_sign_ed25519_ref10_ge_tobytes
crypto_sign_ed25519_ref10_sc_muladd
crypto_sign_ed25519_ref10_sc_reduce
crypto_verify_32
ge_cmp
ge_frombytes_vartime
generate_random_32
ge_p2_to_p3
ge_p3_sub
ge_scalarmult_blinded
soter_asym_cipher_cleanup
soter_asym_cipher_create
soter_asym_cipher_decrypt
soter_asym_cipher_destroy
soter_asym_cipher_encrypt
🚫 soter_asym_cipher_import_key
soter_asym_cipher_init
soter_asym_ka_cleanup
soter_asym_ka_create
soter_asym_ka_derive
soter_asym_ka_destroy
soter_asym_ka_export_key
soter_asym_ka_gen_key
soter_asym_ka_import_key
soter_asym_ka_init
🚫 soter_crc32
🚫 soter_crc32_create
🚫 soter_crc32_final
🚫 soter_crc32_update
🚫 soter_ec_export_key
🚫 soter_ec_gen_key
🚫 soter_ec_import_key
🚫 soter_ec_priv_key_to_engine_specific
🚫 soter_ec_pub_key_to_engine_specific
🚫 soter_engine_specific_to_ec_priv_key
🚫 soter_engine_specific_to_ec_pub_key
🚫 soter_engine_specific_to_rsa_priv_key
🚫 soter_engine_specific_to_rsa_pub_key
soter_hash_cleanup
soter_hash_create
soter_hash_destroy
soter_hash_final
soter_hash_init
soter_hash_update
soter_hmac_cleanup
soter_hmac_create
soter_hmac_destroy
soter_hmac_final
soter_hmac_init
soter_hmac_update
soter_kdf
🚫 soter_nokdf
🚫 soter_pbkdf2
soter_rand
🚫 soter_rsa_export_key
🚫 soter_rsa_gen_key
🚫 soter_rsa_import_key
🚫 soter_rsa_key_pair_gen_cleanup
soter_rsa_key_pair_gen_create
soter_rsa_key_pair_gen_destroy
soter_rsa_key_pair_gen_export_key
soter_rsa_key_pair_gen_init
🚫 soter_rsa_priv_key_to_engine_specific
🚫 soter_rsa_pub_key_to_engine_specific
🚫 soter_sign_cleanup
🚫 soter_sign_cleanup_ecdsa_none_pkcs8
🚫 soter_sign_cleanup_rsa_pss_pkcs8
soter_sign_create
soter_sign_destroy
soter_sign_export_key
🚫 soter_sign_export_key_ecdsa_none_pkcs8
🚫 soter_sign_export_key_rsa_pss_pkcs8
soter_sign_final
🚫 soter_sign_final_ecdsa_none_pkcs8
🚫 soter_sign_final_rsa_pss_pkcs8
soter_sign_get_alg_id
soter_sign_init
🚫 soter_sign_init_ecdsa_none_pkcs8
🚫 soter_sign_init_rsa_pss_pkcs8
soter_sign_update
🚫 soter_sign_update_ecdsa_none_pkcs8
🚫 soter_sign_update_rsa_pss_pkcs8
🚫 soter_sym_aead_ctx_final
🚫 soter_sym_aead_ctx_init
soter_sym_aead_decrypt_aad
soter_sym_aead_decrypt_create
soter_sym_aead_decrypt_destroy
soter_sym_aead_decrypt_final
soter_sym_aead_decrypt_update
soter_sym_aead_encrypt_aad
soter_sym_aead_encrypt_create
soter_sym_aead_encrypt_destroy
soter_sym_aead_encrypt_final
soter_sym_aead_encrypt_update
🚫 soter_sym_ctx_destroy
🚫 soter_sym_ctx_final
🚫 soter_sym_ctx_init
🚫 soter_sym_ctx_update
soter_sym_decrypt_create
soter_sym_decrypt_destroy
soter_sym_decrypt_final
soter_sym_decrypt_update
soter_sym_encrypt_create
soter_sym_encrypt_destroy
soter_sym_encrypt_final
soter_sym_encrypt_update
soter_update_container_checksum
🚫 soter_verify_cleanup
🚫 soter_verify_cleanup_ecdsa_none_pkcs8
🚫 soter_verify_cleanup_rsa_pss_pkcs8
soter_verify_container_checksum
soter_verify_create
soter_verify_destroy
soter_verify_final
🚫 soter_verify_final_ecdsa_none_pkcs8
🚫 soter_verify_final_rsa_pss_pkcs8
soter_verify_get_alg_id
soter_verify_init
🚫 soter_verify_init_ecdsa_none_pkcs8
🚫 soter_verify_init_rsa_pss_pkcs8
soter_verify_update
🚫 soter_verify_update_ecdsa_none_pkcs8
🚫 soter_verify_update_rsa_pss_pkcs8
🚫 soter_withkdf
Themis
Function
🚫 compute_mac
🚫 compute_signature
🚫 encrypt_gcm
🚫 get_alg_id
🚫 get_key_sign_type
🚫 get_peer_key_sign_type
secure_comparator_append_secret
secure_comparator_begin_compare
🚫 secure_comparator_cleanup
secure_comparator_create
secure_comparator_destroy
secure_comparator_get_result
🚫 secure_comparator_init
secure_comparator_proceed_compare
🚫 secure_session_cleanup
secure_session_connect
secure_session_create
🚫 secure_session_derive_message_keys
secure_session_destroy
secure_session_generate_connect_request
secure_session_get_remote_id
🚫 secure_session_init
secure_session_is_established
secure_session_load
🚫 secure_session_peer_cleanup
🚫 secure_session_peer_init
secure_session_receive
secure_session_save
secure_session_send
secure_session_unwrap
secure_session_wrap
🚫 themis_auth_sym_decrypt_message
🚫 themis_auth_sym_decrypt_message_
🚫 themis_auth_sym_encrypt_message
🚫 themis_auth_sym_encrypt_message_
🚫 themis_auth_sym_plain_decrypt
🚫 themis_auth_sym_plain_encrypt
themis_gen_ec_key_pair
🚫 themis_gen_key_pair
themis_gen_rsa_key_pair
themis_get_asym_key_kind
themis_is_valid_asym_key
🚫 themis_message_destroy
🚫 themis_message_get_data
🚫 themis_message_get_length
🚫 themis_message_init
🚫 themis_message_set
themis_secure_cell_decrypt_context_imprint
themis_secure_cell_decrypt_seal
themis_secure_cell_decrypt_token_protect
themis_secure_cell_encrypt_context_imprint
themis_secure_cell_encrypt_seal
themis_secure_cell_encrypt_token_protect
themis_secure_message_decrypt
🚫 themis_secure_message_decrypter_destroy
🚫 themis_secure_message_decrypter_init
🚫 themis_secure_message_decrypter_proceed
🚫 themis_secure_message_ec_decrypter_destroy
🚫 themis_secure_message_ec_decrypter_init
🚫 themis_secure_message_ec_decrypter_proceed
🚫 themis_secure_message_ec_encrypter_destroy
🚫 themis_secure_message_ec_encrypter_init
🚫 themis_secure_message_ec_encrypter_proceed
themis_secure_message_encrypt
🚫 themis_secure_message_encrypter_destroy
🚫 themis_secure_message_encrypter_init
🚫 themis_secure_message_encrypter_proceed
🚫 themis_secure_message_rsa_decrypter_destroy
🚫 themis_secure_message_rsa_decrypter_init
🚫 themis_secure_message_rsa_decrypter_proceed
🚫 themis_secure_message_rsa_encrypter_destroy
🚫 themis_secure_message_rsa_encrypter_init
🚫 themis_secure_message_rsa_encrypter_proceed
themis_secure_message_sign
🚫 themis_secure_message_signer_destroy
🚫 themis_secure_message_signer_init
🚫 themis_secure_message_signer_proceed
themis_secure_message_unwrap
🚫 themis_secure_message_verifier_destroy
🚫 themis_secure_message_verifier_init
🚫 themis_secure_message_verifier_proceed
themis_secure_message_verify
themis_secure_message_wrap
🚫 themis_sym_decrypt_message_u
🚫 themis_sym_decrypt_message_u_
🚫 themis_sym_encrypt_message_u
🚫 themis_sym_encrypt_message_u_
🚫 themis_sym_kdf
🚫 themis_sym_plain_decrypt
🚫 themis_sym_plain_encrypt
🚫 verify_mac
🚫 verify_signature

All these functions are not accessible normally, via <soter/soter.h> or <themis/themis.h>.

Note that some functions from private headers are required to be exported due to hysterical raisins. These ones are marked with SOTER_PRIVATE_API and are exceptional by all means.


  • Explicitly mark public API of Themis

Introduce a new header <themis/themis_api.h> which defines a function attribute marker THEMIS_API. It is used to explicitly mark API that is considered public. These functions will be exported from the library.

Themis does not really provide separate public header files so it's not immediately obvious which functions have to be marked public. We go with functions visible when including <themis/themis.h>.

Currently we support only UNIX-like systems so we can limit ourselves to just Clang and GCC. MSVC requires slightly different syntax but we'll get back to that when we start supporting Windows again.

  • Explicitly mark public API of Soter

Just like with Themis, introduce <soter/soter_api.h> with SOTER_API and mark all public declarations as such.

Note that Soter also has a... complicated story with header files. It's not apparent which headers are public and which are private. We consider public those ones included from <soter/soter.h>.

  • Export additional private-use API from Soter

Due to historically sloppy export management, many functions in Themis (as well as in unit-tests) rely on not-so-public API to be available. Normally you cannot get access to these functions, but you can if you import <soter/soter_t.h> or some other internal header files.

Introduce a new marker SOTER_PRIVATE_API to distinguish such functions from normal exports, and mark all of them exported. There are quite a few of functions, yeah...

Note that while public functions are marked in header files, private export functions are marked at definition site. This is required because some of the source files do not actually include corresponding headers. Plus, this way this definitions are ugly to look at and discourage from adding new 'private' exports.

  • Set default symbol visibility to hidden

Finally, compile all source code with default symbol visibility set to "hidden". With this only symbols marked with THEMIS_API, SOTER_API, or SOTER_PRIVATE_API will get exported from libraries. static functions will never be exported, and non-static functions are also not exported by default unless explicitly marked as public.


Before merge:

  • Add new headers to Xcode project for Carthage
  • Make sure API mistakes are caught by automatic tests (will do later)

Introduce a new header <themis/themis_api.h> which defines a function
attribute marker THEMIS_API. It is used to explicitly mark API that is
considered public. These functions will be exported from the library.

Themis does not really provide separate public header files so it's not
immediately obvious which functions have to be marked public. We go with
functions visible when including <themis/themis.h>.

Currently we support only UNIX-like systems so we can limit ourselves
to just Clang and GCC. MSVC requires slightly different syntax but we'll
get back to that when we start supporting Windows again.
Just like with Themis, introduce <soter/soter_api.h> with SOTER_API and
mark all public declarations as such.

Note that Soter also has a... complicated story with header files. It's
not apparent which headers are public and which are private. We consider
public those ones included from <soter/soter.h>.
Due to historically sloppy export management, many functions in Themis
(as well as in unit-tests) rely on not-so-public API to be available.
Normally you cannot get access to these functions, but you can if you
import <soter/soter_t.h> or some other internal header files.

Introduce a new marker SOTER_PRIVATE_API to distinguish such functions
from normal exports, and mark all of them exported. There are quite a
few of functions, yeah...

Note that while public functions are marked in header files, private
export functions are marked at definition site. This is required because
some of the source files do not actually include corresponding headers.
Plus, this way this definitions are ugly to look at and discourage from
adding new 'private' exports.
Finally, compile all source code with default symbol visibility set to
"hidden". With this only symbols marked with THEMIS_API, SOTER_API, or
SOTER_PRIVATE_API will get exported from libraries. "static" functions
will never be exported, and non-static functions are also not exported
by default unless explicitly marked as public.
@ilammy ilammy added the core Themis Core written in C, its packages label Apr 11, 2019
DEPRECATED macro expands into [[deprecated]] attribute when compiling
C++14. Make sure this attribute goes before GCC-like __attribute__
specifiers, or else the compiler will not recognize them properly.

Unfortunately, there is no standard equivalent to visibility attributes.
(And I doubt there'll ever be, based on how C++ standards committee
operates.)
@Lagovas
Copy link
Collaborator

Lagovas commented Apr 11, 2019

is it hard to add windows support and extend macros? found an example when read about visibility attribute - https://gcc.gnu.org/wiki/Visibility

Add [experimental and untested] support for Windows to the API macros.
Note that Windows uses slightly different approach to DLL linkage. Just
like on Linux and macOS, exported functions should be marked on Windows.
However, _imported_ functions should be explicitly marked as well.

Effectively, this means that we should mark exported functions with
 __declspec(dllexport) when compiling DLL object files, but users of
the DLL should see functions marked with __declspec(dllimport).
We achieve that by using a special macro like THEMIS_EXPORT which is
defined only when compling Themis source code, but not when using it.
@ilammy
Copy link
Collaborator Author

ilammy commented Apr 11, 2019

@Lagovas, here you go, but please note that this is a blind change. Our build system does not support Windows, our CI does not do Windows builds and does not run any test, and I do not have a Windows dev box available at the moment. But something like that should do for Windows export support, assuming that we'll get the Makefile build on Windows.

@vixentael
Copy link
Contributor

vixentael commented Apr 12, 2019

Introduce a new header <themis/themis_api.h>
introduce <soter/soter_api.h>

⚠️ Note to everyone: do not forget to update Xcode project that builds Themis Carthage to include new header

Copy link
Contributor

@vixentael vixentael left a comment

Choose a reason for hiding this comment

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

Overall looks great for me, but how we understand if some other functions should be private too?

@ilammy
Copy link
Collaborator Author

ilammy commented Apr 12, 2019

how we understand if some other functions should be private too?

My initial idea was that developers shouldn't use this often. If ones does add SOTER_PRIVATE_API then they should know exactly what they are doing, why they are doing it, and feel bad about doing it. Arguably, we should strive to remove all these private exports from the code base and replace then with appropriate public functions.

And as for SOTER_API and THEMIS_API the usage is simple: if you add a new public function to public headers, mark it with one of those macros. If you add a helper function to a private header to be used internally then don't mark it.

Getting back to your question... Bad news: unit tests (as they are now) do not catch exporting mistakes. The tests are linked against Themis statically, and visibility is ignored for the most part with static linkage. That is, if you add a new public function but do not mark it as exported then the tests will still link and run fine, because of the way static linkage works. However, any user of the dynamic library (e.g., our wrappers) will see weird linkage errors about missing symbols. This is true for any private dependencies as well. So I'd assume that wrapper tests will catch these mistakes, but in a late and not obvious way.

I guess we can improve this by linking Soter and Themis Core tests dynamically against the libraries in the build directory. That way make test will fail if you forget to mark something as exported (publicly or privately). That's how I determined the list of these not-so-private exports for this PR, actually...

@ilammy
Copy link
Collaborator Author

ilammy commented Apr 12, 2019

I've experimented with dynamic linkage for tests a bit and recalled why I made tests link statically. On Linux dynamic linkage can be more or less easily hacked with rpath, but on macOS the install name thingy causes troubles: the loader wants the libraries to reside in /usr/local. Plus, all of this interferes with other PRs:

@vixentael, let's leave it as is in this PR. I'll test all these things above and send a separate PR to make our tests link against libraries dynamically. That should give us nice compilation errors if we miss something.

While not strictly required for build, let's add the headers so that
they are visible in Xcode when browsing.
@ilammy
Copy link
Collaborator Author

ilammy commented Apr 12, 2019

Note to everyone: do not forget to update Xcode project that builds Themis Carthage to include new header

The header files are not strictly necessary for the build to be successful (they are not compiled). But they were missing from the project file list in Xcode. I've added them there and verified that the framework can be built, and that our Carthage examples work with that result.

@vixentael, thanks for reminding me about this. I think we should work on adding Carthage to CI in order to relieve you from this duty...

@ilammy ilammy merged commit 14686bd into master Apr 16, 2019
@ilammy ilammy deleted the ilammy/explicit-exports branch April 16, 2019 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Themis Core written in C, its packages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants