From 88f901cf77a53901082d4bea19768ff10369145f Mon Sep 17 00:00:00 2001 From: Mikhail Komarov Date: Sun, 5 Jun 2022 11:45:52 +0300 Subject: [PATCH] Initial FFI implementation done #65 --- CMakeLists.txt | 19 +- include/nil/crypto3/block/extern/block.h | 1009 +++++++++++++++++ .../crypto3/block/extern/detail/allocator.hpp | 104 ++ .../block/extern/detail/cpuid/cpuid.hpp | 1 + .../block/extern/detail/cpuid/cpuid_arm.hpp | 214 ++++ .../block/extern/detail/cpuid/cpuid_ppc.hpp | 122 ++ .../block/extern/detail/cpuid/cpuid_x86.hpp | 174 +++ .../block/extern/detail/locking_allocator.hpp | 88 ++ .../crypto3/block/extern/detail/memops.hpp | 329 ++++++ .../crypto3/block/extern/detail/mempool.hpp | 200 ++++ .../nil/crypto3/block/extern/detail/wraps.hpp | 136 +++ src/extern.cpp | 79 ++ 12 files changed, 2474 insertions(+), 1 deletion(-) create mode 100644 include/nil/crypto3/block/extern/block.h create mode 100644 include/nil/crypto3/block/extern/detail/allocator.hpp create mode 100644 include/nil/crypto3/block/extern/detail/cpuid/cpuid.hpp create mode 100644 include/nil/crypto3/block/extern/detail/cpuid/cpuid_arm.hpp create mode 100644 include/nil/crypto3/block/extern/detail/cpuid/cpuid_ppc.hpp create mode 100644 include/nil/crypto3/block/extern/detail/cpuid/cpuid_x86.hpp create mode 100644 include/nil/crypto3/block/extern/detail/locking_allocator.hpp create mode 100644 include/nil/crypto3/block/extern/detail/memops.hpp create mode 100644 include/nil/crypto3/block/extern/detail/mempool.hpp create mode 100644 include/nil/crypto3/block/extern/detail/wraps.hpp create mode 100644 src/extern.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ce1aa44..981e707 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,8 @@ list(APPEND ${CURRENT_PROJECT_NAME}_PUBLIC_HEADERS include/nil/crypto3/block/detail/state_adder.hpp include/nil/crypto3/block/detail/unbounded_shift.hpp) -list(APPEND ${CURRENT_PROJECT_NAME}_UNGROUPED_SOURCES) +list(APPEND ${CURRENT_PROJECT_NAME}_UNGROUPED_SOURCES + src/extern.cpp) if(CRYPTO3_BLOCK_AES) list(APPEND ${CURRENT_PROJECT_NAME}_RIJNDAEL_HEADERS @@ -470,9 +471,14 @@ list(APPEND ${CURRENT_PROJECT_NAME}_SOURCES cm_setup_version(VERSION 0.1.0 PREFIX ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}) add_library(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE) +add_library(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_extern ${${CURRENT_PROJECT_NAME}_SOURCES}) set_target_properties(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} PROPERTIES EXPORT_NAME ${CURRENT_PROJECT_NAME}) +set_target_properties(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_extern PROPERTIES + EXPORT_NAME ${CURRENT_PROJECT_NAME} + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE) if(${CMAKE_TARGET_ARCHITECTURE} STREQUAL "x86_64" OR ${CMAKE_TARGET_ARCHITECTURE} STREQUAL "x86") if(SSE2_FOUND) @@ -495,13 +501,24 @@ endif() target_link_libraries(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE ${Boost_LIBRARIES}) +target_link_libraries(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_extern + ${Boost_LIBRARIES}) + target_include_directories(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE $ $ ${Boost_INCLUDE_DIRS}) +target_include_directories(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_extern PRIVATE + $ + $ + + ${Boost_INCLUDE_DIRS}) + cm_deploy(TARGETS ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} + ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_extern + INCLUDE include NAMESPACE ${CMAKE_WORKSPACE_NAME}::) diff --git a/include/nil/crypto3/block/extern/block.h b/include/nil/crypto3/block/extern/block.h new file mode 100644 index 0000000..12c56a9 --- /dev/null +++ b/include/nil/crypto3/block/extern/block.h @@ -0,0 +1,1009 @@ +#ifndef CRYPTO3_FFI_HPP +#define CRYPTO3_FFI_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +/* +This header exports some of library functionality via a C89 +interface. This API is uesd by the Python and OCaml bindings via those +languages respective ctypes libraries. + +The API is intended to be as easy as possible to call from other +languages, which often have easy ways to call C, because C. But some C +code is easier to deal with than others, so to make things easy this +API follows a few simple rules: + +- All interactions are via pointers to opaque structs. No need to worry about + structure padding issues and the like. + +- All functions return an int error code (except the version calls, which are + assumed to always have something to say). + +- Use simple types: size_t for lengths, const char* NULL terminated strings, + uint8_t for binary. + +- No ownership of memory transfers across the API boundary. The API will + consume data from const pointers, and will produce output by writing to + variables provided by the caller. + +- If exporting a value (a string or a blob) the function takes a pointer to the + output array and a read/write pointer to the length. If the length is insufficient, an + error is returned. So passing nullptr/0 allows querying the final value. + + Note this does not apply to all functions, like `nil_crypto3_hash_final` + which is not idempotent and are documented specially. But it's a + general theory of operation. + +The API is not currently documented, nor should it be considered +stable. It is buggy as heck, most likely, and error handling is a +mess. However the goal is to provide a long term API usable for +language bindings, or for use by systems written in C. Suggestions on +how to provide the cleanest API for such users would be most welcome. + +* TODO: +* - Better error reporting +* - User callback for exception logging? +* - Doxygen comments for all functions/params +*/ + +#include +#include + +/** + * Error codes + */ +enum CRYPTO3_FFI_ERROR { + CRYPTO3_FFI_SUCCESS = 0, + CRYPTO3_FFI_INVALID_VERIFIER = 1, + + CRYPTO3_FFI_ERROR_INVALID_INPUT = -1, + CRYPTO3_FFI_ERROR_BAD_MAC = -2, + + CRYPTO3_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10, + CRYPTO3_FFI_ERROR_EXCEPTION_THROWN = -20, + CRYPTO3_FFI_ERROR_BAD_FLAG = -30, + CRYPTO3_FFI_ERROR_NULL_POINTER = -31, + CRYPTO3_FFI_ERROR_BAD_PARAMETER = -32, + CRYPTO3_FFI_ERROR_NOT_IMPLEMENTED = -40, + CRYPTO3_FFI_ERROR_INVALID_OBJECT = -50, + + CRYPTO3_FFI_ERROR_UNKNOWN_ERROR = -100, +}; + +/** + * Convert an error code into a string. Returns "Unknown error" + * if the error code is not a known one. + */ +const char *nil_crypto3_error_description(int err); + +/** + * Return the version of the currently supported FFI API. This is + * expressed in the form YYYYMMDD of the release date of this version + * of the API. + */ + +uint32_t nil_crypto3_ffi_api_version(); + +/** + * Return 0 (ok) if the version given is one this library supports. + * nil_crypto3_ffi_supports_api(nil_crypto3_ffi_api_version()) will always return 0. + */ + +int nil_crypto3_ffi_supports_api(uint32_t api_version); + +/** + * Return a free-form version string, e.g., 2.0.0 + */ + +const char *nil_crypto3_version_string(); + +/** + * Return the major version of the library + */ + +uint32_t nil_crypto3_version_major(); + +/** + * Return the minor version of the library + */ + +uint32_t nil_crypto3_version_minor(); + +/** + * Return the patch version of the library + */ + +uint32_t nil_crypto3_version_patch(); + +/** + * Return the date this version was released as + * an integer, or 0 if an unreleased version + */ + +uint32_t nil_crypto3_version_datestamp(); + +/** + * RNG type + */ +typedef struct nil_crypto3_rng_struct *nil_crypto3_rng_t; + +/** + * Initialize a random number generator object + * @param rng rng object + * @param rng_type type of the rng, possible values: + * "system": System_RNG, "user": AutoSeeded_RNG + * Set rng_type to null or empty string to let the library choose + * + * TODO: replace rng_type with simple flags? + */ + +int nil_crypto3_rng_init(nil_crypto3_rng_t *rng, const char *rng_type); + +/** + * Get random bytes from a random number generator + * @param rng rng object + * @param out output buffer of size out_len + * @param out_len number of requested bytes + * @return 0 on success, negative on failure + * + * TODO: better name + */ + +int nil_crypto3_rng_get(nil_crypto3_rng_t rng, uint8_t *out, size_t out_len); + +/** + * Reseed a random number generator + * Uses the System_RNG as a seed generator. + * + * @param rng rng object + * @param bits number of bits to to reseed with + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_rng_reseed(nil_crypto3_rng_t rng, size_t bits); + +/** + * Frees all resources of the random number generator object + * @param rng rng object + * @return always returns 0 + */ + +int nil_crypto3_rng_destroy(nil_crypto3_rng_t rng); + +/* + * Hash type + */ +typedef struct nil_crypto3_hash_struct *nil_crypto3_hash_t; + +/** + * Initialize a hash function object + * @param hash hash object + * @param hash_name name of the hash function, e.g., "SHA-384" + * @param flags should be 0 in current API revision, all other uses are reserved + * and return CRYPTO3_FFI_ERROR_BAD_FLAG + * + * TODO: since output_length is effectively required to use this API, + * return it from init as an output parameter + */ + +int nil_crypto3_hash_init(nil_crypto3_hash_t *hash, const char *hash_name, uint32_t flags); + +/** + * Copy the state of a hash function object + * @param dest destination hash object + * @param source source hash object + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_copy_state(nil_crypto3_hash_t *dest, const nil_crypto3_hash_t source); + +/** + * Writes the output length of the hash function to *output_length + * @param hash hash object + * @param output_length output buffer to hold the hash function output length + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_output_length(nil_crypto3_hash_t hash, size_t *output_length); + +/** + * Writes the block size of the hash function to *block_size + * @param hash hash object + * @param block_size output buffer to hold the hash function output length + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_block_size(nil_crypto3_hash_t hash, size_t *block_size); + +/** + * Send more input to the hash function + * @param hash hash object + * @param in input buffer + * @param in_len number of bytes to read from the input buffer + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_update(nil_crypto3_hash_t hash, const uint8_t *in, size_t in_len); + +/** + * Finalizes the hash computation and writes the output to + * out[0:nil_crypto3_hash_output_length()] then reinitializes for computing + * another digest as if nil_crypto3_hash_clear had been called. + * @param hash hash object + * @param out output buffer + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_final(nil_crypto3_hash_t hash, uint8_t out[]); + +/** + * Reinitializes the state of the hash computation. A hash can + * be computed (with update/final) immediately. + * @param hash hash object + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_hash_clear(nil_crypto3_hash_t hash); + +/** + * Frees all resources of the hash object + * @param hash hash object + * @return always returns 0 + */ + +int nil_crypto3_hash_destroy(nil_crypto3_hash_t hash); + +/** + * TODO has no implementation + */ + +int nil_crypto3_hash_name(nil_crypto3_hash_t hash, char *name, size_t name_len); + +/* + * Message Authentication type + */ +typedef struct nil_crypto3_mac_struct *nil_crypto3_mac_t; + +/** + * Initialize a message authentication code object + * @param mac mac object + * @param mac_name name of the hash function, e.g., "HMAC(SHA-384)" + * @param flags should be 0 in current API revision, all other uses are reserved + * and return a negative value (error code) + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_init(nil_crypto3_mac_t *mac, const char *mac_name, uint32_t flags); + +/** + * Writes the output length of the message authentication code to *output_length + * @param mac mac object + * @param output_length output buffer to hold the MAC output length + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_output_length(nil_crypto3_mac_t mac, size_t *output_length); + +/** + * Sets the key on the MAC + * @param mac mac object + * @param key buffer holding the key + * @param key_len size of the key buffer in bytes + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_set_key(nil_crypto3_mac_t mac, const uint8_t *key, size_t key_len); + +/** + * Send more input to the message authentication code + * @param mac mac object + * @param buf input buffer + * @param len number of bytes to read from the input buffer + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_update(nil_crypto3_mac_t mac, const uint8_t *buf, size_t len); + +/** + * Finalizes the MAC computation and writes the output to + * out[0:nil_crypto3_mac_output_length()] then reinitializes for computing + * another MAC as if nil_crypto3_mac_clear had been called. + * @param mac mac object + * @param out output buffer + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_final(nil_crypto3_mac_t mac, uint8_t out[]); + +/** + * Reinitializes the state of the MAC computation. A MAC can + * be computed (with update/final) immediately. + * @param mac mac object + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_mac_clear(nil_crypto3_mac_t mac); + +/** + * Frees all resources of the MAC object + * @param mac mac object + * @return always returns 0 + */ + +int nil_crypto3_mac_destroy(nil_crypto3_mac_t mac); + +/* + * Cipher modes + */ +typedef struct nil_crypto3_cipher_struct *nil_crypto3_cipher_t; + +#define CRYPTO3_CIPHER_INIT_FLAG_MASK_DIRECTION 1 +#define CRYPTO3_CIPHER_INIT_FLAG_ENCRYPT 0 +#define CRYPTO3_CIPHER_INIT_FLAG_DECRYPT 1 + +int nil_crypto3_cipher_init(nil_crypto3_cipher_t *cipher, const char *name, uint32_t flags); + +int nil_crypto3_cipher_valid_nonce_length(nil_crypto3_cipher_t cipher, size_t nl); + +int nil_crypto3_cipher_get_tag_length(nil_crypto3_cipher_t cipher, size_t *tag_size); + +int nil_crypto3_cipher_get_default_nonce_length(nil_crypto3_cipher_t cipher, size_t *nl); + +int nil_crypto3_cipher_get_update_granularity(nil_crypto3_cipher_t cipher, size_t *ug); + +int nil_crypto3_cipher_query_keylen(nil_crypto3_cipher_t, size_t *out_minimum_keylength, size_t *out_maximum_keylength); + +int nil_crypto3_cipher_set_key(nil_crypto3_cipher_t cipher, const uint8_t *key, size_t key_len); + +int nil_crypto3_cipher_set_associated_data(nil_crypto3_cipher_t cipher, const uint8_t *ad, size_t ad_len); + +int nil_crypto3_cipher_start(nil_crypto3_cipher_t cipher, const uint8_t *nonce, size_t nonce_len); + +#define CRYPTO3_CIPHER_UPDATE_FLAG_FINAL (1U << 0) + +int nil_crypto3_cipher_update(nil_crypto3_cipher_t cipher, uint32_t flags, uint8_t output[], size_t output_size, + size_t *output_written, const uint8_t input_bytes[], size_t input_size, + size_t *input_consumed); + +int nil_crypto3_cipher_clear(nil_crypto3_cipher_t hash); + +int nil_crypto3_cipher_destroy(nil_crypto3_cipher_t cipher); + +/* + * Derive a key from a passphrase for a number of iterations + * @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2" + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_pbkdf(const char *pbkdf_algo, uint8_t out[], size_t out_len, const char *passphrase, + const uint8_t salt[], size_t salt_len, size_t iterations); + +/** + * Derive a key from a passphrase, running until msec time has elapsed. + * @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2" + * @param out buffer to store the derived key, must be of out_len bytes + * @param out_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param milliseconds_to_run if iterations is zero, then instead the PBKDF is + * run until milliseconds_to_run milliseconds has passed + * @param out_iterations_used set to the number iterations executed + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_pbkdf_timed(const char *pbkdf_algo, uint8_t out[], size_t out_len, const char *passphrase, + const uint8_t salt[], size_t salt_len, size_t milliseconds_to_run, + size_t *out_iterations_used); + +/** + * Derive a key + * @param kdf_algo KDF algorithm, e.g., "SP800-56C" + * @param out buffer holding the derived key, must be of length out_len + * @param out_len the desired output length in bytes + * @param secret the secret input + * @param secret_len size of secret in bytes + * @param salt a diversifier + * @param salt_len size of salt in bytes + * @param label purpose for the derived keying material + * @param label_len size of label in bytes + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_kdf(const char *kdf_algo, uint8_t out[], size_t out_len, const uint8_t secret[], size_t secret_len, + const uint8_t salt[], size_t salt_len, const uint8_t label[], size_t label_len); + +/** +* Create a password hash using Bcrypt +* @param out buffer holding the password hash, should be of length 64 bytes +* @param out_len the desired output length in bytes +* @param password the password +* @param rng a random number generator +* @param work_factor how much work to do to slow down guessing attacks +* @param flags should be 0 in current API revision, all other uses are reserved +* and return CRYPTO3_FFI_ERROR_BAD_FLAG +* @return 0 on success, a negative value on failure + +* Output is formatted bcrypt $2a$... +*/ + +int nil_crypto3_bcrypt_generate(uint8_t *out, size_t *out_len, const char *password, nil_crypto3_rng_t rng, + size_t work_factor, uint32_t flags); + +/* + * Raw Block Cipher (PRP) interface + */ +typedef struct nil_crypto3_block_cipher_struct *nil_crypto3_block_cipher_t; + +/** + * Initialize a block cipher object + */ + +int nil_crypto3_block_cipher_init(nil_crypto3_block_cipher_t *bc, const char *cipher_name); + +/** + * Destroy a block cipher object + */ + +int nil_crypto3_block_cipher_destroy(nil_crypto3_block_cipher_t bc); + +/** + * Reinitializes the block cipher + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_block_cipher_clear(nil_crypto3_block_cipher_t bc); + +/** + * Set the key for a block cipher instance + */ + +int nil_crypto3_block_cipher_set_key(nil_crypto3_block_cipher_t bc, const uint8_t key[], size_t len); + +/** + * Return the positive block size of this block cipher, or negative to + * indicate an error + */ + +int nil_crypto3_block_cipher_block_size(nil_crypto3_block_cipher_t bc); + +int nil_crypto3_block_cipher_encrypt_blocks(nil_crypto3_block_cipher_t bc, const uint8_t in[], uint8_t out[], + size_t blocks); + +int nil_crypto3_block_cipher_decrypt_blocks(nil_crypto3_block_cipher_t bc, const uint8_t in[], uint8_t out[], + size_t blocks); + +/* + * Multiple precision integers + */ +typedef struct nil_crypto3_mp_struct *nil_crypto3_mp_t; + +int nil_crypto3_mp_init(nil_crypto3_mp_t *mp); + +int nil_crypto3_mp_destroy(nil_crypto3_mp_t mp); + +// writes nil_crypto3_mp_num_bytes(multiprecision)*2 + 1 bytes to out[] + +int nil_crypto3_mp_to_hex(const nil_crypto3_mp_t mp, char *out); + +int nil_crypto3_mp_to_str(const nil_crypto3_mp_t mp, uint8_t base, char *out, size_t *out_len); + +int nil_crypto3_mp_clear(nil_crypto3_mp_t mp); + +int nil_crypto3_mp_set_from_int(nil_crypto3_mp_t mp, int initial_value); + +int nil_crypto3_mp_set_from_mp(nil_crypto3_mp_t dest, const nil_crypto3_mp_t source); + +int nil_crypto3_mp_set_from_str(nil_crypto3_mp_t dest, const char *str); + +int nil_crypto3_mp_set_from_radix_str(nil_crypto3_mp_t dest, const char *str, size_t radix); + +int nil_crypto3_mp_num_bits(const nil_crypto3_mp_t n, size_t *bits); + +int nil_crypto3_mp_num_bytes(const nil_crypto3_mp_t n, size_t *bytes); + +// Writes nil_crypto3_mp_num_bytes(multiprecision) to vec + +int nil_crypto3_mp_to_bin(const nil_crypto3_mp_t mp, uint8_t vec[]); + +int nil_crypto3_mp_from_bin(const nil_crypto3_mp_t mp, const uint8_t vec[], size_t vec_len); + +int nil_crypto3_mp_to_uint32(const nil_crypto3_mp_t mp, uint32_t *val); + +/** + * This function is not well named. Returns 1 iff mp is greater than + * *or equal to* zero. Use nil_crypto3_mp_is_negative to detect negative + * numbers, nil_crypto3_mp_is_zero to check for zero. + */ + +int nil_crypto3_mp_is_positive(const nil_crypto3_mp_t mp); + +/** + * Return 1 iff mp is less than 0 + */ + +int nil_crypto3_mp_is_negative(const nil_crypto3_mp_t mp); + +int nil_crypto3_mp_flip_sign(nil_crypto3_mp_t mp); +// CRYPTO3_PUBLIC_API(2,1) int nil_crypto3_mp_set_negative(nil_crypto3_mp_t multiprecision); + +int nil_crypto3_mp_is_zero(const nil_crypto3_mp_t mp); + +int nil_crypto3_mp_is_odd(const nil_crypto3_mp_t mp); + +int nil_crypto3_mp_is_even(const nil_crypto3_mp_t mp); + +int nil_crypto3_mp_add(nil_crypto3_mp_t result, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +int nil_crypto3_mp_sub(nil_crypto3_mp_t result, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +int nil_crypto3_mp_mul(nil_crypto3_mp_t result, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +int nil_crypto3_mp_div(nil_crypto3_mp_t quotient, nil_crypto3_mp_t remainder, const nil_crypto3_mp_t x, + const nil_crypto3_mp_t y); + +int nil_crypto3_mp_mod_mul(nil_crypto3_mp_t result, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y, + const nil_crypto3_mp_t mod); + +/* + * Returns 0 if x != y + * Returns 1 if x == y + * Returns negative number on error + */ + +int nil_crypto3_mp_equal(const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +/* + * Sets *result to comparison result: + * -1 if x < y, 0 if x == y, 1 if x > y + * Returns negative number on error or zero on success + */ + +int nil_crypto3_mp_cmp(int *result, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +/* + * Swap two nil_crypto3_mp_t + */ + +int nil_crypto3_mp_swap(nil_crypto3_mp_t x, nil_crypto3_mp_t y); + +// Return (core^exponent) % modulus + +int nil_crypto3_mp_powmod(nil_crypto3_mp_t out, const nil_crypto3_mp_t base, const nil_crypto3_mp_t exponent, + const nil_crypto3_mp_t modulus); + +int nil_crypto3_mp_lshift(nil_crypto3_mp_t out, const nil_crypto3_mp_t in, size_t shift); + +int nil_crypto3_mp_rshift(nil_crypto3_mp_t out, const nil_crypto3_mp_t in, size_t shift); + +int nil_crypto3_mp_mod_inverse(nil_crypto3_mp_t out, const nil_crypto3_mp_t in, const nil_crypto3_mp_t modulus); + +int nil_crypto3_mp_rand_bits(nil_crypto3_mp_t rand_out, nil_crypto3_rng_t rng, size_t bits); + +int nil_crypto3_mp_rand_range(nil_crypto3_mp_t rand_out, nil_crypto3_rng_t rng, const nil_crypto3_mp_t lower_bound, + const nil_crypto3_mp_t upper_bound); + +int nil_crypto3_mp_gcd(nil_crypto3_mp_t out, const nil_crypto3_mp_t x, const nil_crypto3_mp_t y); + +/** + * Returns 0 if n is not prime + * Returns 1 if n is prime + * Returns negative number on error + */ + +int nil_crypto3_mp_is_prime(const nil_crypto3_mp_t n, nil_crypto3_rng_t rng, size_t test_prob); + +/** + * Returns 0 if specified bit of n is not set + * Returns 1 if specified bit of n is set + * Returns negative number on error + */ + +int nil_crypto3_mp_get_bit(const nil_crypto3_mp_t n, size_t bit); + +/** + * Set the specified bit + */ + +int nil_crypto3_mp_set_bit(nil_crypto3_mp_t n, size_t bit); + +/** + * Clear the specified bit + */ + +int nil_crypto3_mp_clear_bit(nil_crypto3_mp_t n, size_t bit); + +/* Bcrypt password hashing */ + +/** + * Check a previously created password hash + * @param pass the password to check against + * @param hash the stored hash to check against + * @return 0 if if this password/hash combination is valid, + * 1 if the combination is not valid (but otherwise well formed), + * negative on error + */ + +int nil_crypto3_bcrypt_is_valid(const char *pass, const char *hash); + +/* + * Public/private key creation, import, ... + */ +typedef struct nil_crypto3_privkey_struct *nil_crypto3_privkey_t; + +int nil_crypto3_privkey_create(nil_crypto3_privkey_t *key, const char *algo_name, const char *algo_params, + nil_crypto3_rng_t rng); + +#define CRYPTO3_CHECK_KEY_EXPENSIVE_TESTS 1 + +int nil_crypto3_privkey_check_key(nil_crypto3_privkey_t key, nil_crypto3_rng_t rng, uint32_t flags); + +int nil_crypto3_privkey_create_rsa(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, size_t n_bits); + +int nil_crypto3_privkey_create_ecdsa(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, const char *params); + +int nil_crypto3_privkey_create_ecdh(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, const char *params); + +int nil_crypto3_privkey_create_mceliece(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, size_t n, size_t t); + +int nil_crypto3_privkey_create_dh(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, const char *param); + +/* + * Generates DSA key pair. Gives to a caller control over key length + * and order of a subgroup 'q'. + * + * @param key handler to the resulting key + * @param random initialized PRNG + * @param pbits length of the key in bits. Must be between in range (1024, 3072) + * and multiple of 64. Bit size of the prime 'p' + * @param qbits order of the subgroup. Must be in range (160, 256) and multiple + * of 8 + * + * @returns CRYPTO3_FFI_SUCCESS Success, `key' initialized with DSA key + * @returns CRYPTO3_FFI_ERROR_NULL_POINTER either `key' or `random' is NULL + * @returns CRYPTO3_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or + * `qbits' + * @returns CRYPTO3_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented + * +-------------------------------------------------------------------------------- */ + +int nil_crypto3_privkey_create_dsa(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, size_t pbits, size_t qbits); + +/* + * Generates ElGamal key pair. Caller has a control over key length + * and order of a subgroup 'q'. Function is able to use two types of + * primes: + * * if pbits-1 == qbits then safe primes are used for key generation + * * otherwise generation uses group of prime order + * + * @param key handler to the resulting key + * @param random initialized PRNG + * @param pbits length of the key in bits. Must be at least 1024 + * @param qbits order of the subgroup. Must be at least 160 + * + * @returns CRYPTO3_FFI_SUCCESS Success, `key' initialized with DSA key + * @returns CRYPTO3_FFI_ERROR_NULL_POINTER either `key' or `random' is NULL + * @returns CRYPTO3_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or + * `qbits' + * @returns CRYPTO3_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented + * +-------------------------------------------------------------------------------- */ + +int nil_crypto3_privkey_create_elgamal(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, size_t pbits, size_t qbits); + +/* + * Input currently assumed to be PKCS #8 structure; + * Set password to NULL to indicate no encryption expected + */ + +int nil_crypto3_privkey_load(nil_crypto3_privkey_t *key, nil_crypto3_rng_t rng, const uint8_t bits[], size_t len, + const char *password); + +int nil_crypto3_privkey_destroy(nil_crypto3_privkey_t key); + +#define CRYPTO3_PRIVKEY_EXPORT_FLAG_DER 0 +#define CRYPTO3_PRIVKEY_EXPORT_FLAG_PEM 1 + +/* + * On input *out_len is number of bytes in out[] + * On output *out_len is number of bytes written (or required) + * If out is not big enough no output is written, *out_len is set and 1 is returned + * Returns 0 on success and sets + * If some other error occurs a negative integer is returned. + */ + +int nil_crypto3_privkey_export(nil_crypto3_privkey_t key, uint8_t out[], size_t *out_len, uint32_t flags); + +/* + * Export a private key, running PBKDF for specified amount of time + * @param key the private key to export + */ + +int nil_crypto3_privkey_export_encrypted_pbkdf_msec(nil_crypto3_privkey_t key, uint8_t out[], size_t *out_len, + nil_crypto3_rng_t rng, const char *passphrase, + uint32_t pbkdf_msec_runtime, size_t *pbkdf_iterations_out, + const char *cipher_algo, const char *pbkdf_algo, uint32_t flags); + +/* + * Export a private key using the specified number of iterations. + */ + +int nil_crypto3_privkey_export_encrypted_pbkdf_iter(nil_crypto3_privkey_t key, uint8_t out[], size_t *out_len, + nil_crypto3_rng_t rng, const char *passphrase, + size_t pbkdf_iterations, const char *cipher_algo, + const char *pbkdf_algo, uint32_t flags); + +typedef struct nil_crypto3_pubkey_struct *nil_crypto3_pubkey_t; + +int nil_crypto3_pubkey_load(nil_crypto3_pubkey_t *key, const uint8_t bits[], size_t len); + +int nil_crypto3_privkey_export_pubkey(nil_crypto3_pubkey_t *out, nil_crypto3_privkey_t in); + +int nil_crypto3_pubkey_export(nil_crypto3_pubkey_t key, uint8_t out[], size_t *out_len, uint32_t flags); + +int nil_crypto3_pubkey_algo_name(nil_crypto3_pubkey_t key, char out[], size_t *out_len); + +/** + * Returns 0 if key is valid, negative if invalid key or some other error + */ + +int nil_crypto3_pubkey_check_key(nil_crypto3_pubkey_t key, nil_crypto3_rng_t rng, uint32_t flags); + +int nil_crypto3_pubkey_estimated_strength(nil_crypto3_pubkey_t key, size_t *estimate); + +int nil_crypto3_pubkey_fingerprint(nil_crypto3_pubkey_t key, const char *hash, uint8_t out[], size_t *out_len); + +int nil_crypto3_pubkey_destroy(nil_crypto3_pubkey_t key); + +/* + * Get arbitrary named fields from public or privat keys + */ + +int nil_crypto3_pubkey_get_field(nil_crypto3_mp_t output, nil_crypto3_pubkey_t key, const char *field_name); + +int nil_crypto3_privkey_get_field(nil_crypto3_mp_t output, nil_crypto3_privkey_t key, const char *field_name); + +/* + * Algorithm specific key operations: RSA + */ + +int nil_crypto3_privkey_load_rsa(nil_crypto3_privkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t q, nil_crypto3_mp_t e); + +int nil_crypto3_privkey_rsa_get_p(nil_crypto3_mp_t p, nil_crypto3_privkey_t rsa_key); + +int nil_crypto3_privkey_rsa_get_q(nil_crypto3_mp_t q, nil_crypto3_privkey_t rsa_key); + +int nil_crypto3_privkey_rsa_get_d(nil_crypto3_mp_t d, nil_crypto3_privkey_t rsa_key); + +int nil_crypto3_privkey_rsa_get_n(nil_crypto3_mp_t n, nil_crypto3_privkey_t rsa_key); + +int nil_crypto3_privkey_rsa_get_e(nil_crypto3_mp_t e, nil_crypto3_privkey_t rsa_key); + +int nil_crypto3_pubkey_load_rsa(nil_crypto3_pubkey_t *key, nil_crypto3_mp_t n, nil_crypto3_mp_t e); + +int nil_crypto3_pubkey_rsa_get_e(nil_crypto3_mp_t e, nil_crypto3_pubkey_t rsa_key); + +int nil_crypto3_pubkey_rsa_get_n(nil_crypto3_mp_t n, nil_crypto3_pubkey_t rsa_key); + +/* + * Algorithm specific key operations: DSA + */ + +int nil_crypto3_privkey_load_dsa(nil_crypto3_privkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t q, nil_crypto3_mp_t g, + nil_crypto3_mp_t x); + +int nil_crypto3_pubkey_load_dsa(nil_crypto3_pubkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t q, nil_crypto3_mp_t g, + nil_crypto3_mp_t y); + +int nil_crypto3_privkey_dsa_get_x(nil_crypto3_mp_t n, nil_crypto3_privkey_t key); + +int nil_crypto3_pubkey_dsa_get_p(nil_crypto3_mp_t p, nil_crypto3_pubkey_t key); + +int nil_crypto3_pubkey_dsa_get_q(nil_crypto3_mp_t q, nil_crypto3_pubkey_t key); + +int nil_crypto3_pubkey_dsa_get_g(nil_crypto3_mp_t d, nil_crypto3_pubkey_t key); + +int nil_crypto3_pubkey_dsa_get_y(nil_crypto3_mp_t y, nil_crypto3_pubkey_t key); + +/* + * Loads Diffie Hellman private key + * + * @param key variable populated with key material + * @param p prime order of a Z_p group + * @param g group generator + * @param x private key + * + * @pre key is NULL on input + * @post function allocates memory and assigns to `key' + * + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_privkey_load_dh(nil_crypto3_privkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t g, nil_crypto3_mp_t x); + +/* + * Loads Diffie Hellman public key + * + * @param key variable populated with key material + * @param p prime order of a Z_p group + * @param g group generator + * @param y public key + * + * @pre key is NULL on input + * @post function allocates memory and assigns to `key' + * + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_pubkey_load_dh(nil_crypto3_pubkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t g, nil_crypto3_mp_t y); + +/* + * Algorithm specific key operations: ElGamal + */ + +/* + * Loads ElGamal public key + * @param key variable populated with key material + * @param p prime order of a Z_p group + * @param g group generator + * @param y public key + * + * @pre key is NULL on input + * @post function allocates memory and assigns to `key' + * + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_pubkey_load_elgamal(nil_crypto3_pubkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t g, nil_crypto3_mp_t y); + +/* + * Loads ElGamal private key + * + * @param key variable populated with key material + * @param p prime order of a Z_p group + * @param g group generator + * @param x private key + * + * @pre key is NULL on input + * @post function allocates memory and assigns to `key' + * + * @return 0 on success, a negative value on failure + */ + +int nil_crypto3_privkey_load_elgamal(nil_crypto3_privkey_t *key, nil_crypto3_mp_t p, nil_crypto3_mp_t g, nil_crypto3_mp_t x); + +/* + * Algorithm specific key operations: Ed25519 + */ + +int nil_crypto3_privkey_load_ed25519(nil_crypto3_privkey_t *key, const uint8_t privkey[32]); + +int nil_crypto3_pubkey_load_ed25519(nil_crypto3_pubkey_t *key, const uint8_t pubkey[32]); + +int nil_crypto3_privkey_ed25519_get_privkey(nil_crypto3_privkey_t key, uint8_t output[64]); + +int nil_crypto3_pubkey_ed25519_get_pubkey(nil_crypto3_pubkey_t key, uint8_t pubkey[32]); + +/* + * Algorithm specific key operations: ECDSA and ECDH + */ + +int nil_crypto3_privkey_load_ecdsa(nil_crypto3_privkey_t *key, const nil_crypto3_mp_t scalar, const char *curve_name); + +int nil_crypto3_pubkey_load_ecdsa(nil_crypto3_pubkey_t *key, const nil_crypto3_mp_t public_x, + const nil_crypto3_mp_t public_y, const char *curve_name); + +int nil_crypto3_pubkey_load_ecdh(nil_crypto3_pubkey_t *key, const nil_crypto3_mp_t public_x, + const nil_crypto3_mp_t public_y, const char *curve_name); + +int nil_crypto3_privkey_load_ecdh(nil_crypto3_privkey_t *key, const nil_crypto3_mp_t scalar, const char *curve_name); + +int nil_crypto3_pubkey_load_sm2(nil_crypto3_pubkey_t *key, const nil_crypto3_mp_t public_x, const nil_crypto3_mp_t public_y, + const char *curve_name); + +int nil_crypto3_privkey_load_sm2(nil_crypto3_privkey_t *key, const nil_crypto3_mp_t scalar, const char *curve_name); + +int nil_crypto3_pubkey_load_sm2_enc(nil_crypto3_pubkey_t *key, const nil_crypto3_mp_t public_x, + const nil_crypto3_mp_t public_y, const char *curve_name); + +int nil_crypto3_privkey_load_sm2_enc(nil_crypto3_privkey_t *key, const nil_crypto3_mp_t scalar, const char *curve_name); + +int nil_crypto3_pubkey_sm2_compute_za(uint8_t out[], size_t *out_len, const char *ident, const char *hash_algo, + const nil_crypto3_pubkey_t key); + +/* + * Public Key encryption + */ +typedef struct nil_crypto3_pk_op_encrypt_struct *nil_crypto3_pk_op_encrypt_t; + +int nil_crypto3_pk_op_encrypt_create(nil_crypto3_pk_op_encrypt_t *op, nil_crypto3_pubkey_t key, const char *padding, + uint32_t flags); + +int nil_crypto3_pk_op_encrypt_destroy(nil_crypto3_pk_op_encrypt_t op); + +int nil_crypto3_pk_op_encrypt(nil_crypto3_pk_op_encrypt_t op, nil_crypto3_rng_t rng, uint8_t out[], size_t *out_len, + const uint8_t plaintext[], size_t plaintext_len); + +/* + * Public Key decryption + */ +typedef struct nil_crypto3_pk_op_decrypt_struct *nil_crypto3_pk_op_decrypt_t; + +int nil_crypto3_pk_op_decrypt_create(nil_crypto3_pk_op_decrypt_t *op, nil_crypto3_privkey_t key, const char *padding, + uint32_t flags); + +int nil_crypto3_pk_op_decrypt_destroy(nil_crypto3_pk_op_decrypt_t op); + +int nil_crypto3_pk_op_decrypt(nil_crypto3_pk_op_decrypt_t op, uint8_t out[], size_t *out_len, const uint8_t ciphertext[], + size_t ciphertext_len); + +/* + * signature Generation + */ +typedef struct nil_crypto3_pk_op_sign_struct *nil_crypto3_pk_op_sign_t; + +int nil_crypto3_pk_op_sign_create(nil_crypto3_pk_op_sign_t *op, nil_crypto3_privkey_t key, const char *hash_and_padding, + uint32_t flags); + +int nil_crypto3_pk_op_sign_destroy(nil_crypto3_pk_op_sign_t op); + +int nil_crypto3_pk_op_sign_update(nil_crypto3_pk_op_sign_t op, const uint8_t in[], size_t in_len); + +int nil_crypto3_pk_op_sign_finish(nil_crypto3_pk_op_sign_t op, nil_crypto3_rng_t rng, uint8_t sig[], size_t *sig_len); + +/* + * signature verification + */ +typedef struct nil_crypto3_pk_op_verify_struct *nil_crypto3_pk_op_verify_t; + +int nil_crypto3_pk_op_verify_create(nil_crypto3_pk_op_verify_t *op, nil_crypto3_pubkey_t key, const char *hash_and_padding, + uint32_t flags); + +int nil_crypto3_pk_op_verify_destroy(nil_crypto3_pk_op_verify_t op); + +int nil_crypto3_pk_op_verify_update(nil_crypto3_pk_op_verify_t op, const uint8_t in[], size_t in_len); + +int nil_crypto3_pk_op_verify_finish(nil_crypto3_pk_op_verify_t op, const uint8_t sig[], size_t sig_len); + +/* + * Key Agreement + */ +typedef struct nil_crypto3_pk_op_ka_struct *nil_crypto3_pk_op_ka_t; + +int nil_crypto3_pk_op_key_agreement_create(nil_crypto3_pk_op_ka_t *op, nil_crypto3_privkey_t key, const char *kdf, + uint32_t flags); + +int nil_crypto3_pk_op_key_agreement_destroy(nil_crypto3_pk_op_ka_t op); + +int nil_crypto3_pk_op_key_agreement_export_public(nil_crypto3_privkey_t key, uint8_t out[], size_t *out_len); + +int nil_crypto3_pk_op_key_agreement(nil_crypto3_pk_op_ka_t op, uint8_t out[], size_t *out_len, const uint8_t other_key[], + size_t other_key_len, const uint8_t salt[], size_t salt_len); + +int nil_crypto3_pkcs_hash_id(const char *hash_name, uint8_t pkcs_id[], size_t *pkcs_id_len); + +/* + * + * @param mce_key must be a McEliece key + * ct_len should be pt_len + n/8 + a few? + */ + +int nil_crypto3_mceies_encrypt(nil_crypto3_pubkey_t mce_key, nil_crypto3_rng_t rng, const char *aead, const uint8_t pt[], + size_t pt_len, const uint8_t ad[], size_t ad_len, uint8_t ct[], size_t *ct_len); + +int nil_crypto3_mceies_decrypt(nil_crypto3_privkey_t mce_key, const char *aead, const uint8_t ct[], size_t ct_len, + const uint8_t ad[], size_t ad_len, uint8_t pt[], size_t *pt_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/nil/crypto3/block/extern/detail/allocator.hpp b/include/nil/crypto3/block/extern/detail/allocator.hpp new file mode 100644 index 0000000..7ce174c --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/allocator.hpp @@ -0,0 +1,104 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_SECURE_ALLOCATOR_HPP +#define CRYPTO3_SECURE_ALLOCATOR_HPP + +#include + +namespace nil { + namespace crypto3 { + template + class secure_allocator { + public: + /* + * Assert exists to prevent someone from doing something that will + * probably crash anyway (like secure_vector where ~non_POD_t + * deletes a member pointer which was zeroed before it ran). + * MSVC in debug mode uses non-integral proxy types in container types + * like std::vector, thus we disable the check there. + */ +#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0 + static_assert(std::is_integral::value, "secure_allocator supports only integer types"); +#endif + + typedef T value_type; + typedef std::size_t size_type; + +#ifdef CRYPTO3_BUILD_COMPILER_IS_MSVC_2013 + secure_allocator() = default; + secure_allocator(const secure_allocator &) = default; + secure_allocator &operator=(const secure_allocator &) = default; + ~secure_allocator() = default; + + template + struct rebind { + typedef secure_allocator other; + }; + + void construct(value_type *mem, const value_type &value) { + std::_Construct(mem, value); + } + + void destroy(value_type *mem) { + std::_Destroy(mem); + } +#else + + secure_allocator() BOOST_NOEXCEPT = default; + + secure_allocator(const secure_allocator &) BOOST_NOEXCEPT = default; + + secure_allocator &operator=(const secure_allocator &) BOOST_NOEXCEPT = default; + + ~secure_allocator() BOOST_NOEXCEPT = default; + +#endif + + template + secure_allocator(const secure_allocator &) BOOST_NOEXCEPT { + } + + T *allocate(std::size_t n) { + return static_cast(allocate_memory(n, sizeof(T))); + } + + void deallocate(T *p, std::size_t n) { + deallocate_memory(p, n, sizeof(T)); + } + }; + + template + inline bool operator==(const secure_allocator &, const secure_allocator &) { + return true; + } + + template + inline bool operator!=(const secure_allocator &, const secure_allocator &) { + return false; + } + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_SECURE_ALLOCATOR_HPP diff --git a/include/nil/crypto3/block/extern/detail/cpuid/cpuid.hpp b/include/nil/crypto3/block/extern/detail/cpuid/cpuid.hpp new file mode 100644 index 0000000..a3ead2d --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/cpuid/cpuid.hpp @@ -0,0 +1 @@ +#ifndef CRYPTO3_CPUID_HPP #define CRYPTO3_CPUID_HPP #include #include #include /* * If no way of dynamically determining the cache line size for the * system exists, this value is used as the default. Used by the side * channel countermeasures rather than for alignment purposes, so it is * better to be on the smaller side if the exact value cannot be * determined. Typically 32 or 64 bytes on modern CPUs. */ #if !defined(CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE) #define CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE 32 #endif namespace nil { namespace crypto3 { /*! * A class handling runtime CPU feature detection. It is limited to * just the features necessary to implement CPU specific code in library, * rather than being a general purpose utility. * * This class supports: * * - x86 features using CPUID. x86 is also the only processor with * accurate cache line detection currently. * * - PowerPC AltiVec detection on Linux, NetBSD, OpenBSD, and Darwin * * - ARM NEON and crypto extensions detection. On Linux and Android * systems which support getauxval, that is used to access CPU * feature information. Otherwise a relatively portable but * thread-unsafe mechanism involving executing probe functions which * catching SIGILL signal is used. */ class cpuid final { public: /** * Probe the CPU and see what extensions are supported */ static void initialize() { g_processor_features = 0; #if defined(BOOST_ARCH_PPC) || defined(BOOST_ARCH_ARM) || defined(BOOST_ARCH_X86) g_processor_features = cpuid::detect_cpu_features(&g_cache_line_size); #endif g_endian_status = runtime_check_endian(); g_processor_features |= cpuid::CPUID_INITIALIZED_BIT; } static bool has_simd_32() { #if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE2_VERSION return cpuid::has_sse2(); #elif BOOST_HW_SIMD_ARM >= BOOST_HW_SIMD_ARM_NEON_VERSION return cpuid::has_altivec(); #elif BOOST_HW_SIMD_PPC >= BOOST_HW_SIMD_PPC_VMX_VERSION return cpuid::has_neon(); #else return true; #endif } /** * Return a possibly empty string containing list of known CPU * extensions. Each name will be seperated by a space, and the ordering * will be arbitrary. This list only contains values that are useful for * the library (for example FMA instructions are not checked). * * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec" */ static std::string to_string() { std::vector flags; #define CPUID_PRINT(flag) \ do { \ if (has_##flag()) { \ flags.push_back(#flag); \ } \ } while (0) #if defined(BOOST_ARCH_X86) CPUID_PRINT(sse2); CPUID_PRINT(ssse3); CPUID_PRINT(sse41); CPUID_PRINT(sse42); CPUID_PRINT(avx2); CPUID_PRINT(avx512f); CPUID_PRINT(rdtsc); CPUID_PRINT(bmi2); CPUID_PRINT(adx); CPUID_PRINT(aes_ni); CPUID_PRINT(clmul); CPUID_PRINT(rdrand); CPUID_PRINT(rdseed); CPUID_PRINT(intel_sha); #endif #if defined(BOOST_ARCH_PPC) CPUID_PRINT(altivec); CPUID_PRINT(ppc_crypto); #endif #if defined(BOOST_ARCH_ARM) CPUID_PRINT(neon); CPUID_PRINT(arm_sha1); CPUID_PRINT(arm_sha2); CPUID_PRINT(arm_aes); CPUID_PRINT(arm_pmull); #endif #undef CPUID_PRINT std::string out; for (const std::string &c : flags) { out.push_back(' '); out.insert(out.end(), c.begin(), c.end()); } return out; } /** * Return a best guess of the cache line size */ static size_t cache_line_size() { if (g_processor_features == 0) { initialize(); } return g_cache_line_size; } static bool is_little_endian() { return get_endian_status() == ENDIAN_LITTLE; } static bool is_big_endian() { return get_endian_status() == ENDIAN_BIG; } enum CPUID_bits : uint64_t { #if defined(BOOST_ARCH_X86) // These values have no relation to cpuid bitfields // SIMD instruction sets CPUID_SSE2_BIT = (1ULL << 0), CPUID_SSSE3_BIT = (1ULL << 1), CPUID_SSE41_BIT = (1ULL << 2), CPUID_SSE42_BIT = (1ULL << 3), CPUID_AVX2_BIT = (1ULL << 4), CPUID_AVX512F_BIT = (1ULL << 5), // Misc useful instructions CPUID_RDTSC_BIT = (1ULL << 10), CPUID_BMI2_BIT = (1ULL << 11), CPUID_ADX_BIT = (1ULL << 12), CPUID_BMI1_BIT = (1ULL << 13), // Crypto-specific ISAs CPUID_AESNI_BIT = (1ULL << 16), CPUID_CLMUL_BIT = (1ULL << 17), CPUID_RDRAND_BIT = (1ULL << 18), CPUID_RDSEED_BIT = (1ULL << 19), CPUID_SHA_BIT = (1ULL << 20), #endif #if defined(BOOST_ARCH_PPC) CPUID_ALTIVEC_BIT = (1ULL << 0), CPUID_PPC_CRYPTO3_BIT = (1ULL << 1), #endif #if defined(BOOST_ARCH_ARM) CPUID_ARM_NEON_BIT = (1ULL << 0), CPUID_ARM_RIJNDAEL_BIT = (1ULL << 16), CPUID_ARM_PMULL_BIT = (1ULL << 17), CPUID_ARM_SHA1_BIT = (1ULL << 18), CPUID_ARM_SHA2_BIT = (1ULL << 19), #endif CPUID_INITIALIZED_BIT = (1ULL << 63) }; #if defined(BOOST_ARCH_PPC) /** * Check if the processor supports AltiVec/VMX */ static bool has_altivec() { return has_cpuid_bit(CPUID_ALTIVEC_BIT); } /** * Check if the processor supports POWER8 crypto3 extensions */ static bool has_ppc_crypto() { return has_cpuid_bit(CPUID_PPC_CRYPTO3_BIT); } #endif #if defined(BOOST_ARCH_ARM) /** * Check if the processor supports NEON SIMD */ static bool has_neon() { return has_cpuid_bit(CPUID_ARM_NEON_BIT); } /** * Check if the processor supports ARMv8 SHA1 */ static bool has_arm_sha1() { return has_cpuid_bit(CPUID_ARM_SHA1_BIT); } /** * Check if the processor supports ARMv8 SHA2 */ static bool has_arm_sha2() { return has_cpuid_bit(CPUID_ARM_SHA2_BIT); } /** * Check if the processor supports ARMv8 AES */ static bool has_arm_aes() { return has_cpuid_bit(CPUID_ARM_RIJNDAEL_BIT); } /** * Check if the processor supports ARMv8 PMULL */ static bool has_arm_pmull() { return has_cpuid_bit(CPUID_ARM_PMULL_BIT); } #endif #if defined(BOOST_ARCH_X86) /** * Check if the processor supports RDTSC */ static bool has_rdtsc() { return has_cpuid_bit(CPUID_RDTSC_BIT); } /** * Check if the processor supports SSE2 */ static bool has_sse2() { return has_cpuid_bit(CPUID_SSE2_BIT); } /** * Check if the processor supports SSSE3 */ static bool has_ssse3() { return has_cpuid_bit(CPUID_SSSE3_BIT); } /** * Check if the processor supports SSE4.1 */ static bool has_sse41() { return has_cpuid_bit(CPUID_SSE41_BIT); } /** * Check if the processor supports SSE4.2 */ static bool has_sse42() { return has_cpuid_bit(CPUID_SSE42_BIT); } /** * Check if the processor supports AVX2 */ static bool has_avx2() { return has_cpuid_bit(CPUID_AVX2_BIT); } /** * Check if the processor supports AVX-512F */ static bool has_avx512f() { return has_cpuid_bit(CPUID_AVX512F_BIT); } /** * Check if the processor supports BMI1 */ static bool has_bmi1() { return has_cpuid_bit(CPUID_BMI1_BIT); } /** * Check if the processor supports BMI2 */ static bool has_bmi2() { return has_cpuid_bit(CPUID_BMI2_BIT); } /** * Check if the processor supports AES-NI */ static bool has_aes_ni() { return has_cpuid_bit(CPUID_AESNI_BIT); } /** * Check if the processor supports CLMUL */ static bool has_clmul() { return has_cpuid_bit(CPUID_CLMUL_BIT); } /** * Check if the processor supports Intel SHA extension */ static bool has_intel_sha() { return has_cpuid_bit(CPUID_SHA_BIT); } /** * Check if the processor supports ADX extension */ static bool has_adx() { return has_cpuid_bit(CPUID_ADX_BIT); } /** * Check if the processor supports RDRAND */ static bool has_rdrand() { return has_cpuid_bit(CPUID_RDRAND_BIT); } /** * Check if the processor supports RDSEED */ static bool has_rdseed() { return has_cpuid_bit(CPUID_RDSEED_BIT); } #endif /* * Clear a cpuid bit * Call cpuid::initialize to reset * * This is only exposed for testing, don't use unless you know * what you are doing. */ static void clear_cpuid_bit(CPUID_bits bit) { const uint64_t mask = ~(static_cast(bit)); g_processor_features &= mask; } /* * Don't call this function, use cpuid::has_xxx above * It is only exposed for the tests. */ static bool has_cpuid_bit(CPUID_bits elem) { if (g_processor_features == 0) { initialize(); } const uint64_t elem64 = static_cast(elem); return ((g_processor_features & elem64) == elem64); } static std::vector bit_from_string(const std::string &tok) { #if defined(BOOST_ARCH_X86) if (tok == "sse2" || tok == "simd") { return {nil::crypto3::cpuid::CPUID_SSE2_BIT}; } if (tok == "ssse3") { return {nil::crypto3::cpuid::CPUID_SSSE3_BIT}; } if (tok == "aesni") { return {nil::crypto3::cpuid::CPUID_AESNI_BIT}; } if (tok == "clmul") { return {nil::crypto3::cpuid::CPUID_CLMUL_BIT}; } if (tok == "avx2") { return {nil::crypto3::cpuid::CPUID_AVX2_BIT}; } if (tok == "sha") { return {nil::crypto3::cpuid::CPUID_SHA_BIT}; } #elif defined(BOOST_ARCH_PPC) if (tok == "altivec" || tok == "simd") return {nil::crypto3::cpuid::CPUID_ALTIVEC_BIT}; #elif defined(BOOST_ARCH_ARM) if (tok == "neon" || tok == "simd") return {nil::crypto3::cpuid::CPUID_ARM_NEON_BIT}; if (tok == "armv8sha1") return {nil::crypto3::cpuid::CPUID_ARM_SHA1_BIT}; if (tok == "armv8sha2") return {nil::crypto3::cpuid::CPUID_ARM_SHA2_BIT}; if (tok == "armv8aes") return {nil::crypto3::cpuid::CPUID_ARM_RIJNDAEL_BIT}; if (tok == "armv8pmull") return {nil::crypto3::cpuid::CPUID_ARM_PMULL_BIT}; #else CRYPTO3_UNUSED(tok); #endif return {}; } private: enum endian_status : uint32_t { ENDIAN_UNKNOWN = 0x00000000, ENDIAN_BIG = 0x01234567, ENDIAN_LITTLE = 0x67452301, }; #if defined(BOOST_ARCH_PPC) || defined(BOOST_ARCH_ARM) || defined(BOOST_ARCH_X86) static uint64_t detect_cpu_features(size_t *cache_line_size); #endif static endian_status runtime_check_endian() { // Check runtime endian const uint32_t endian32 = 0x01234567; const uint8_t *e8 = reinterpret_cast(&endian32); endian_status endian = ENDIAN_UNKNOWN; if (e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67) { endian = ENDIAN_BIG; } else if (e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01) { endian = ENDIAN_LITTLE; } else { throw std::exception(); } // If we were compiled with a known endian, verify it matches at runtime #if defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE) BOOST_ASSERT_MSG(endian == ENDIAN_LITTLE, "Build and runtime endian match"); #elif defined(BOOST_ENDIAN_BIG_BYTE_AVAILABLE) BOOST_ASSERT_MSG(endian == ENDIAN_BIG, "Build and runtime endian match"); #endif return endian; } static endian_status get_endian_status() { if (g_endian_status == ENDIAN_UNKNOWN) { g_endian_status = runtime_check_endian(); } return g_endian_status; } static uint64_t g_processor_features; static size_t g_cache_line_size; static enum endian_status g_endian_status; }; uint64_t cpuid::g_processor_features = 0; size_t cpuid::g_cache_line_size = CRYPTO3_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE; cpuid::endian_status cpuid::g_endian_status = ENDIAN_UNKNOWN; } // namespace crypto3 } // namespace nil #endif \ No newline at end of file diff --git a/include/nil/crypto3/block/extern/detail/cpuid/cpuid_arm.hpp b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_arm.hpp new file mode 100644 index 0000000..f12e3ba --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_arm.hpp @@ -0,0 +1,214 @@ +#include + +#if defined(BOOST_ARCH_ARM) + +#if defined(CRYPTO3_TARGET_OS_HAS_GETAUXVAL) +#include + +#elif defined(CRYPTO3_TARGET_OS_IS_IOS) +#include +#include + +#else +#include + +#endif + +#endif + +namespace nil { + namespace crypto3 { + +#if defined(BOOST_ARCH_ARM) + +#if defined(CRYPTO3_TARGET_OS_IS_IOS) + + namespace { + + uint64_t flags_by_ios_machine_type(const std::string &machine) { + /* + * This relies on a map of known machine names to features. This + * will quickly grow out of date as new products are introduced, but + * is apparently the best we can do for iOS. + */ + + struct version_info { + std::string name; + size_t min_version_neon; + size_t min_version_armv8; + }; + + static const version_info min_versions[] = { + {"iPhone", 2, 6}, + {"iPad", 1, 4}, + {"iPod", 4, 7}, + {"AppleTV", 2, 5}, + }; + + if (machine.size() < 3) + return 0; + + auto comma = machine.find(','); + + // Simulator, or something we don't know about + if (comma == std::string::npos) + return 0; + + std::string product = machine.substr(0, comma); + + size_t version = 0; + size_t place = 1; + while (product.size() > 1 && ::isdigit(product.back())) { + const size_t digit = product.back() - '0'; + version += digit * place; + place *= 10; + product.pop_back(); + } + + if (version == 0) + return 0; + + for (const version_info &info : min_versions) { + if (info.name != product) + continue; + + if (version >= info.min_version_armv8) { + return cpuid::CPUID_ARM_RIJNDAEL_BIT | cpuid::CPUID_ARM_PMULL_BIT | cpuid::CPUID_ARM_SHA1_BIT | + cpuid::CPUID_ARM_SHA2_BIT | cpuid::CPUID_ARM_NEON_BIT; + } + + if (version >= info.min_version_neon) + return cpuid::CPUID_ARM_NEON_BIT; + } + + // Some other product we don't know about + return 0; + } + + } // namespace + +#endif + + uint64_t cpuid::detect_cpu_features(size_t *cache_line_size) { + uint64_t detected_features = 0; + +#if defined(CRYPTO3_TARGET_OS_HAS_GETAUXVAL) + /* + * On systems with getauxval these bits should normally be defined + * in bits/auxv.h but some buggy? glibc installs seem to miss them. + * These following values are all fixed, for the Linux ELF format, + * so we just hardcode them in ARM_hwcap_bit enum. + */ + + enum ARM_hwcap_bit { +#if defined(CRYPTO3_TARGET_ARCHITECTURE_IS_ARM32) + NEON_bit = (1 << 12), + AES_bit = (1 << 0), + PMULL_bit = (1 << 1), + SHA1_bit = (1 << 2), + SHA2_bit = (1 << 3), + + ARCH_hwcap_neon = 16, // AT_HWCAP + ARCH_hwcap_crypto = 26, // AT_HWCAP2 +#elif defined(CRYPTO3_TARGET_ARCHITECTURE_IS_ARM64) + NEON_bit = (1 << 1), + AES_bit = (1 << 3), + PMULL_bit = (1 << 4), + SHA1_bit = (1 << 5), + SHA2_bit = (1 << 6), + + ARCH_hwcap_neon = 16, // AT_HWCAP + ARCH_hwcap_crypto = 16, // AT_HWCAP +#endif + }; + +#if defined(AT_DCACHEBSIZE) + const unsigned long dcache_line = ::getauxval(AT_DCACHEBSIZE); + + // plausibility check + if (dcache_line == 32 || dcache_line == 64 || dcache_line == 128) + *cache_line_size = static_cast(dcache_line); +#endif + + const unsigned long hwcap_neon = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_neon); + if (hwcap_neon & ARM_hwcap_bit::NEON_bit) + detected_features |= cpuid::CPUID_ARM_NEON_BIT; + + /* + On aarch64 this ends up calling getauxval twice with AT_HWCAP + It doesn't seem worth optimizing this out, since getauxval is + just reading a field in the ELF header. + */ + const unsigned long hwcap_crypto = ::getauxval(ARM_hwcap_bit::ARCH_hwcap_crypto); + if (hwcap_crypto & ARM_hwcap_bit::AES_bit) + detected_features |= cpuid::CPUID_ARM_RIJNDAEL_BIT; + if (hwcap_crypto & ARM_hwcap_bit::PMULL_bit) + detected_features |= cpuid::CPUID_ARM_PMULL_BIT; + if (hwcap_crypto & ARM_hwcap_bit::SHA1_bit) + detected_features |= cpuid::CPUID_ARM_SHA1_BIT; + if (hwcap_crypto & ARM_hwcap_bit::SHA2_bit) + detected_features |= cpuid::CPUID_ARM_SHA2_BIT; + +#elif defined(CRYPTO3_TARGET_OS_IS_IOS) + + char machine[64] = {0}; + size_t size = sizeof(machine) - 1; + ::sysctlbyname("hw.machine", machine, &size, nullptr, 0); + + detected_features = flags_by_ios_machine_type(machine); + +#elif defined(CRYPTO3_USE_GCC_INLINE_ASM) && defined(CRYPTO3_TARGET_ARCHITECTURE_IS_ARM64) + + /* + No getauxval API available, fall back on probe functions. We only + bother with Aarch64 here to simplify the code and because going to + extreme contortions to support detect NEON on devices that probably + don't support it doesn't seem worthwhile. + + NEON registers v0-v7 are caller saved in Aarch64 + */ + + auto neon_probe = []() -> int { + asm("and v0.16b, v0.16b, v0.16b"); + return 1; + }; + auto aes_probe = []() -> int { + asm(".word 0x4e284800"); + return 1; + }; + auto pmull_probe = []() -> int { + asm(".word 0x0ee0e000"); + return 1; + }; + auto sha1_probe = []() -> int { + asm(".word 0x5e280800"); + return 1; + }; + auto sha2_probe = []() -> int { + asm(".word 0x5e282800"); + return 1; + }; + + // Only bother running the crypto3 detection if we found NEON + + if (run_cpu_instruction_probe(neon_probe) == 1) { + detected_features |= cpuid::CPUID_ARM_NEON_BIT; + + if (run_cpu_instruction_probe(aes_probe) == 1) + detected_features |= cpuid::CPUID_ARM_RIJNDAEL_BIT; + if (run_cpu_instruction_probe(pmull_probe) == 1) + detected_features |= cpuid::CPUID_ARM_PMULL_BIT; + if (run_cpu_instruction_probe(sha1_probe) == 1) + detected_features |= cpuid::CPUID_ARM_SHA1_BIT; + if (run_cpu_instruction_probe(sha2_probe) == 1) + detected_features |= cpuid::CPUID_ARM_SHA2_BIT; + } + +#endif + + return detected_features; + } + +#endif + } // namespace crypto3 +} // namespace nil diff --git a/include/nil/crypto3/block/extern/detail/cpuid/cpuid_ppc.hpp b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_ppc.hpp new file mode 100644 index 0000000..f8a9d33 --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_ppc.hpp @@ -0,0 +1,122 @@ +#include +#include + +#if defined(BOOST_ARCH_PPC) + +/* + * On Darwin and OpenBSD ppc, use sysctl to detect AltiVec + */ +#if defined(CRYPTO3_TARGET_OS_IS_DARWIN) +#include +#elif defined(CRYPTO3_TARGET_OS_IS_OPENBSD) +#include +#include +#include +#elif defined(CRYPTO3_TARGET_OS_HAS_GETAUXVAL) +#include +#endif + +#endif + +namespace nil { + namespace crypto3 { + +#if defined(BOOST_ARCH_PPC) + + /* + * PowerPC specific block: check for AltiVec using either + * sysctl or by reading processor version number register. + */ + uint64_t cpuid::detect_cpu_features(size_t *cache_line_size) { + CRYPTO3_UNUSED(cache_line_size); + +#if defined(CRYPTO3_TARGET_OS_IS_DARWIN) || defined(CRYPTO3_TARGET_OS_IS_OPENBSD) + // On Darwin/OS X and OpenBSD, use sysctl + + int sels[2] = { +#if defined(CRYPTO3_TARGET_OS_IS_OPENBSD) + CTL_MACHDEP, + CPU_ALTIVEC +#else + CTL_HW, + HW_VECTORUNIT +#endif + }; + + int vector_type = 0; + size_t length = sizeof(vector_type); + int error = ::sysctl(sels, 2, &vector_type, &length, NULL, 0); + + if (error == 0 && vector_type > 0) + return cpuid::CPUID_ALTIVEC_BIT; + +#elif defined(CRYPTO3_TARGET_OS_HAS_GETAUXVAL) && defined(CRYPTO3_TARGET_ARCHITECTURE_IS_PPC64) + + enum PPC_hwcap_bit { + ALTIVEC_bit = (1 << 28), + CRYPTO3_bit = (1 << 25), + + ARCH_hwcap_altivec = 16, // AT_HWCAP + ARCH_hwcap_crypto = 26, // AT_HWCAP2 + }; + + uint64_t detected_features = 0; + + const unsigned long hwcap_altivec = ::getauxval(PPC_hwcap_bit::ARCH_hwcap_altivec); + if (hwcap_altivec & PPC_hwcap_bit::ALTIVEC_bit) + detected_features |= cpuid::CPUID_ALTIVEC_BIT; + + const unsigned long hwcap_crypto = ::getauxval(PPC_hwcap_bit::ARCH_hwcap_crypto); + if (hwcap_crypto & PPC_hwcap_bit::CRYPTO3_bit) + detected_features |= cpuid::CPUID_PPC_CRYPTO3_BIT; + + return detected_features; + +#else + + /* + On PowerPC, MSR 287 is PVR, the Processor Version Number + Normally it is only accessible to ring 0, but Linux and NetBSD + (others, too, maybe?) will trap and emulate it for us. + */ + + int pvr = run_cpu_instruction_probe([]() -> int { + uint32_t pvr = 0; + asm volatile("mfspr %0, 287" : "=r"(pvr)); + // Top 16 bits suffice to identify the model + return static_cast(pvr >> 16); + }); + + if (pvr > 0) { + const uint16_t ALTIVEC_PVR[] = { + 0x003E, // IBM POWER6 + 0x003F, // IBM POWER7 + 0x004A, // IBM POWER7p + 0x004D, // IBM POWER8 + 0x004B, // IBM POWER8E + 0x000C, // G4-7400 + 0x0039, // G5 970 + 0x003C, // G5 970FX + 0x0044, // G5 970MP + 0x0070, // Cell PPU + 0, // end + }; + + for (size_t i = 0; ALTIVEC_PVR[i]; ++i) { + if (pvr == ALTIVEC_PVR[i]) + return cpuid::CPUID_ALTIVEC_BIT; + } + + return 0; + } + + // TODO try direct instruction probing + +#endif + + return 0; + } + +#endif + } // namespace crypto3 +} // namespace nil diff --git a/include/nil/crypto3/block/extern/detail/cpuid/cpuid_x86.hpp b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_x86.hpp new file mode 100644 index 0000000..5cdcf35 --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/cpuid/cpuid_x86.hpp @@ -0,0 +1,174 @@ +#include +#include +#include + +#if defined(BOOST_ARCH_X86) + +#if defined(CRYPTO3_BUILD_COMPILER_IS_MSVC) +#include +#elif defined(CRYPTO3_BUILD_COMPILER_IS_INTEL) +#include +#elif defined(CRYPTO3_BUILD_COMPILER_IS_GCC) || defined(CRYPTO3_BUILD_COMPILER_IS_CLANG) +#include +#endif + +#endif + +namespace nil { + namespace crypto3 { + +#if defined(BOOST_ARCH_X86) + + uint64_t cpuid::detect_cpu_features(size_t *cache_line_size) { +#if defined(CRYPTO3_BUILD_COMPILER_IS_MSVC) +#define X86_CPUID(type, out) \ + do { \ + __cpuid((int *)out, type); \ + } while (0) +#define X86_CPUID_SUBLEVEL(type, level, out) \ + do { \ + __cpuidex((int *)out, type, level); \ + } while (0) + +#elif defined(CRYPTO3_BUILD_COMPILER_IS_INTEL) +#define X86_CPUID(type, out) \ + do { \ + __cpuid(out, type); \ + } while (0) +#define X86_CPUID_SUBLEVEL(type, level, out) \ + do { \ + __cpuidex((int *)out, type, level); \ + } while (0) + +#elif defined(CRYPTO3_TARGET_ARCHITECTURE_IS_X86_64) && defined(CRYPTO3_USE_GCC_INLINE_ASM) +#define X86_CPUID(type, out) asm("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type)) + +#define X86_CPUID_SUBLEVEL(type, level, out) \ + asm("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type), "2"(level)) + +#elif defined(CRYPTO3_BUILD_COMPILER_IS_GCC) || defined(CRYPTO3_BUILD_COMPILER_IS_CLANG) +#define X86_CPUID(type, out) \ + do { \ + __get_cpuid(type, out, out + 1, out + 2, out + 3); \ + } while (0) + +#define X86_CPUID_SUBLEVEL(type, level, out) \ + do { \ + __cpuid_count(type, level, out[0], out[1], out[2], out[3]); \ + } while (0) +#else +#warning "No way of calling x86 cpuid instruction for this compiler" +#define X86_CPUID(type, out) \ + do { \ + clear_mem(out, 4); \ + } while (0) +#define X86_CPUID_SUBLEVEL(type, level, out) \ + do { \ + clear_mem(out, 4); \ + } while (0) +#endif + + uint64_t features_detected = 0; + uint32_t cpuid[4] = {0}; + + // cpuid 0: vendor identification, max sublevel + X86_CPUID(0, cpuid); + + const uint32_t max_supported_sublevel = cpuid[0]; + + const uint32_t INTEL_CPUID[3] = {0x756E6547, 0x6C65746E, 0x49656E69}; + const uint32_t AMD_CPUID[3] = {0x68747541, 0x444D4163, 0x69746E65}; + const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3); + const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3); + + if (max_supported_sublevel >= 1) { + // cpuid 1: feature bits + X86_CPUID(1, cpuid); + const uint64_t flags0 = (static_cast(cpuid[2]) << 32) | cpuid[3]; + + enum x86_CPUID_1_bits : uint64_t { + RDTSC = (1ULL << 4), + SSE2 = (1ULL << 26), + CLMUL = (1ULL << 33), + SSSE3 = (1ULL << 41), + SSE41 = (1ULL << 51), + SSE42 = (1ULL << 52), + AESNI = (1ULL << 57), + RDRAND = (1ULL << 62) + }; + + if (flags0 & x86_CPUID_1_bits::RDTSC) + features_detected |= cpuid::CPUID_RDTSC_BIT; + if (flags0 & x86_CPUID_1_bits::SSE2) + features_detected |= cpuid::CPUID_SSE2_BIT; + if (flags0 & x86_CPUID_1_bits::CLMUL) + features_detected |= cpuid::CPUID_CLMUL_BIT; + if (flags0 & x86_CPUID_1_bits::SSSE3) + features_detected |= cpuid::CPUID_SSSE3_BIT; + if (flags0 & x86_CPUID_1_bits::SSE41) + features_detected |= cpuid::CPUID_SSE41_BIT; + if (flags0 & x86_CPUID_1_bits::SSE42) + features_detected |= cpuid::CPUID_SSE42_BIT; + if (flags0 & x86_CPUID_1_bits::AESNI) + features_detected |= cpuid::CPUID_AESNI_BIT; + if (flags0 & x86_CPUID_1_bits::RDRAND) + features_detected |= cpuid::CPUID_RDRAND_BIT; + } + + if (is_intel) { + // Intel cache line size is in cpuid(1) output + *cache_line_size = 8 * extract_uint_t(cpuid[1], 2); + } else if (is_amd) { + // AMD puts it in vendor zone + X86_CPUID(0x80000005, cpuid); + *cache_line_size = extract_uint_t(cpuid[2], 3); + } + + if (max_supported_sublevel >= 7) { + clear_mem(cpuid, 4); + X86_CPUID_SUBLEVEL(7, 0, cpuid); + + enum x86_CPUID_7_bits : uint64_t { + AVX2 = (1ULL << 5), + BMI2 = (1ULL << 8), + AVX512F = (1ULL << 16), + RDSEED = (1ULL << 18), + ADX = (1ULL << 19), + SHA = (1ULL << 29), + }; + uint64_t flags7 = (static_cast(cpuid[2]) << 32) | cpuid[1]; + + if (flags7 & x86_CPUID_7_bits::AVX2) + features_detected |= cpuid::CPUID_AVX2_BIT; + if (flags7 & x86_CPUID_7_bits::BMI2) + features_detected |= cpuid::CPUID_BMI2_BIT; + if (flags7 & x86_CPUID_7_bits::AVX512F) + features_detected |= cpuid::CPUID_AVX512F_BIT; + if (flags7 & x86_CPUID_7_bits::RDSEED) + features_detected |= cpuid::CPUID_RDSEED_BIT; + if (flags7 & x86_CPUID_7_bits::ADX) + features_detected |= cpuid::CPUID_ADX_BIT; + if (flags7 & x86_CPUID_7_bits::SHA) + features_detected |= cpuid::CPUID_SHA_BIT; + } + +#undef X86_CPUID +#undef X86_CPUID_SUBLEVEL + + /* + * If we don't have access to cpuid, we can still safely assume that + * any x86-64 processor has SSE2 and RDTSC + */ +#if defined(CRYPTO3_TARGET_ARCHITECTURE_IS_X86_64) + if (features_detected == 0) { + features_detected |= cpuid::CPUID_SSE2_BIT; + features_detected |= cpuid::CPUID_RDTSC_BIT; + } +#endif + + return features_detected; + } + +#endif + } // namespace crypto3 +} // namespace nil diff --git a/include/nil/crypto3/block/extern/detail/locking_allocator.hpp b/include/nil/crypto3/block/extern/detail/locking_allocator.hpp new file mode 100644 index 0000000..2d7e11e --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/locking_allocator.hpp @@ -0,0 +1,88 @@ +#ifndef CRYPTO3_MLOCK_ALLOCATOR_HPP +#define CRYPTO3_MLOCK_ALLOCATOR_HPP + +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + + class memory_pool; + + class mlock_allocator final { + public: + static mlock_allocator &instance() { + static mlock_allocator mlock; + return mlock; + } + + void *allocate(size_t num_elems, size_t elem_size) { + if (!m_pool) { + return nullptr; + } + + const size_t n = num_elems * elem_size; + if (n / elem_size != num_elems) { + return nullptr; + } // overflow! + + return m_pool->allocate(n); + } + + bool deallocate(void *p, size_t num_elems, size_t elem_size) BOOST_NOEXCEPT { + if (!m_pool) { + return false; + } + + size_t n = num_elems * elem_size; + + /* + We return nullptr in allocate if there was an overflow, so if an + overflow occurs here we know the pointer was not allocated by this pool. + */ + if (n / elem_size != num_elems) { + return false; + } + + return m_pool->deallocate(p, n); + } + + mlock_allocator(const mlock_allocator &) = delete; + + mlock_allocator &operator=(const mlock_allocator &) = delete; + + private: + mlock_allocator() { + const size_t mem_to_lock = get_memory_locking_limit(); + + if (mem_to_lock) { + m_locked_pages = static_cast(allocate_locked_pages(mem_to_lock)); + + if (m_locked_pages) { + m_locked_pages_size = mem_to_lock; + m_pool = std::make_unique(m_locked_pages, m_locked_pages_size, system_page_size(), + CRYPTO3_MLOCK_ALLOCATOR_MIN_ALLOCATION, + CRYPTO3_MLOCK_ALLOCATOR_MAX_ALLOCATION, 4); + } + } + } + + ~mlock_allocator() { + if (m_pool) { + m_pool.reset(); + // free_locked_pages scrubs the memory before free + free_locked_pages(m_locked_pages, m_locked_pages_size); + } + } + + std::unique_ptr m_pool; + uint8_t *m_locked_pages = nullptr; + size_t m_locked_pages_size = 0; + }; + } // namespace crypto3 +} // namespace nil + +#endif \ No newline at end of file diff --git a/include/nil/crypto3/block/extern/detail/memops.hpp b/include/nil/crypto3/block/extern/detail/memops.hpp new file mode 100644 index 0000000..c588854 --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/memops.hpp @@ -0,0 +1,329 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_MEMOPS_HPP +#define CRYPTO3_MEMOPS_HPP + +#include +#include + +#ifdef CRYPTO3_HAS_LOCKING_ALLOCATOR + +#include + +#endif + +namespace nil { + namespace crypto3 { + + /** + * Allocate a memory buffer by some method. This should only be used for + * primitive types (uint8_t, uint32_t, etc). + * + * @param elems the number of elements + * @param elem_size the size of each element + * @return pointer to allocated and zeroed memory, or throw std::bad_alloc on failure + */ + __attribute__((malloc)) void *allocate_memory(size_t elems, size_t elem_size) { +#if defined(CRYPTO3_HAS_LOCKING_ALLOCATOR) + if (void *p = mlock_allocator::instance().allocate(elems, elem_size)) { + return p; + } +#endif + + void *ptr = std::calloc(elems, elem_size); + if (!ptr) { + throw std::bad_alloc(); + } + return ptr; + } + + /** + * Scrub memory contents in a way that a compiler should not elide, + * using some system specific technique. Note that this function might + * not zero the memory (for example, in some hypothetical + * implementation it might combine the memory contents with the output + * of a system PRNG), but if you can detect any difference in behavior + * at runtime then the clearing is side-effecting and you can just + * use `clear_mem`. + * + * Use this function to scrub memory just before deallocating it, or on + * a stack buffer before returning from the function. + * + * @param ptr a pointer to memory to scrub + * @param n the number of bytes pointed to by ptr + */ + void secure_scrub_memory(void *ptr, size_t n) { +#if defined(CRYPTO3_TARGET_OS_HAS_RTLSECUREZEROMEMORY) + ::RtlSecureZeroMemory(ptr, n); + +#elif defined(CRYPTO3_TARGET_OS_HAS_EXPLICIT_BZERO) + ::explicit_bzero(ptr, n); + +#elif defined(CRYPTO3_USE_VOLATILE_MEMSET_FOR_ZERO) && (CRYPTO3_USE_VOLATILE_MEMSET_FOR_ZERO == 1) + /* + * Call memset through a static volatile pointer, which the compiler + * should not elide. This construct should be safe in conforming + * compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and + * Clang 3.8 both create code that saves the memset address in the + * data segment and uncondtionally loads and jumps to that address. + */ + static void *(*const volatile memset_ptr)(void *, int, size_t) = std::memset; + (memset_ptr)(ptr, 0, n); +#else + + volatile uint8_t *p = reinterpret_cast(ptr); + + for (size_t i = 0; i != n; ++i) { + p[i] = 0; + } +#endif + } + + /** + * Free a pointer returned by allocate_memory + * @param p the pointer returned by allocate_memory + * @param elems the number of elements, as passed to allocate_memory + * @param elem_size the size of each element, as passed to allocate_memory + */ + void deallocate_memory(void *p, size_t elems, size_t elem_size) { + if (p == nullptr) { + return; + } + + secure_scrub_memory(p, elems * elem_size); + +#if defined(CRYPTO3_HAS_LOCKING_ALLOCATOR) + if (mlock_allocator::instance().deallocate(p, elems, elem_size)) { + return; + } +#endif + + std::free(p); + } + + /** + * Ensure the allocator is initialized + */ + void initialize_allocator() { +#if defined(CRYPTO3_HAS_LOCKING_ALLOCATOR) + mlock_allocator::instance(); +#endif + } + + /** + * Memory comparison, input insensitive + * @param x a pointer to an array + * @param y a pointer to another array + * @param len the number of Ts in x and y + * @return true iff x[i] == y[i] forall i in [0...n) + */ + + bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len) { + volatile uint8_t difference = 0; + + for (size_t i = 0; i != len; ++i) { + difference |= (x[i] ^ y[i]); + } + + return difference == 0; + } + + /** + * Zero out some bytes + * @param ptr a pointer to memory to zero + * @param bytes the number of bytes to zero in ptr + */ + inline void clear_bytes(void *ptr, size_t bytes) { + if (bytes > 0) { + std::memset(ptr, 0, bytes); + } + } + + /** + * Zero memory before use. This simply calls memset and should not be + * used in cases where the compiler cannot see the call as a + * side-effecting operation (for example, if calling clear_mem before + * deallocating memory, the compiler would be allowed to omit the call + * to memset entirely under the as-if rule.) + * + * @param ptr a pointer to an array of Ts to zero + * @param n the number of Ts pointed to by ptr + */ + template + inline void clear_mem(T *ptr, size_t n) { + clear_bytes(ptr, sizeof(T) * n); + } + + /** + * Copy memory + * @param out the destination array + * @param in the source array + * @param n the number of elements of in/out + */ + template + inline void copy_mem(T *out, const T *in, size_t n) { + if (n > 0) { + std::memmove(out, in, sizeof(T) * n); + } + } + + /** + * Set memory to a fixed value + * @param ptr a pointer to an array + * @param n the number of Ts pointed to by ptr + * @param val the value to set each byte to + */ + template + inline void set_mem(T *ptr, size_t n, uint8_t val) { + if (n > 0) { + std::memset(ptr, val, sizeof(T) * n); + } + } + + inline const uint8_t *cast_char_ptr_to_uint8(const char *s) { + return reinterpret_cast(s); + } + + inline const char *cast_uint8_ptr_to_char(const uint8_t *b) { + return reinterpret_cast(b); + } + + inline uint8_t *cast_char_ptr_to_uint8(char *s) { + return reinterpret_cast(s); + } + + inline char *cast_uint8_ptr_to_char(uint8_t *b) { + return reinterpret_cast(b); + } + + /** + * Memory comparison, input insensitive + * @param p1 a pointer to an array + * @param p2 a pointer to another array + * @param n the number of Ts in p1 and p2 + * @return true iff p1[i] == p2[i] forall i in [0...n) + */ + template + inline bool same_mem(const T *p1, const T *p2, size_t n) { + volatile T difference = 0; + + for (size_t i = 0; i != n; ++i) { + difference |= (p1[i] ^ p2[i]); + } + + return difference == 0; + } + + /** + * XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length + * @param out the input/output buffer + * @param in the read-only input buffer + * @param length the length of the buffers + */ + inline void xor_buf(uint8_t out[], const uint8_t in[], size_t length) { + while (length >= 16) { + uint64_t x0, x1, y0, y1; + std::memcpy(&x0, in, 8); + std::memcpy(&x1, in + 8, 8); + std::memcpy(&y0, out, 8); + std::memcpy(&y1, out + 8, 8); + + y0 ^= x0; + y1 ^= x1; + std::memcpy(out, &y0, 8); + std::memcpy(out + 8, &y1, 8); + out += 16; + in += 16; + length -= 16; + } + + while (length > 0) { + out[0] ^= in[0]; + out += 1; + in += 1; + length -= 1; + } + } + + /** + * XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length + * @param out the output buffer + * @param in the first input buffer + * @param in2 the second output buffer + * @param length the length of the three buffers + */ + inline void xor_buf(uint8_t out[], const uint8_t in[], const uint8_t in2[], size_t length) { + while (length >= 16) { + uint64_t x0, x1, y0, y1; + std::memcpy(&x0, in, 8); + std::memcpy(&x1, in + 8, 8); + std::memcpy(&y0, in2, 8); + std::memcpy(&y1, in2 + 8, 8); + + x0 ^= y0; + x1 ^= y1; + std::memcpy(out, &x0, 8); + std::memcpy(out + 8, &x1, 8); + out += 16; + in += 16; + in2 += 16; + length -= 16; + } + + for (size_t i = 0; i != length; ++i) { + out[i] = in[i] ^ in2[i]; + } + } + + template + void xor_buf(std::vector &out, const std::vector &in, size_t n) { + xor_buf(out.data(), in.data(), n); + } + + template + void xor_buf(std::vector &out, const uint8_t *in, size_t n) { + xor_buf(out.data(), in, n); + } + + template + void xor_buf(std::vector &out, const uint8_t *in, const std::vector &in2, + size_t n) { + xor_buf(out.data(), in, in2.data(), n); + } + + template + std::vector &operator^=(std::vector &out, + const std::vector &in) { + if (out.size() < in.size()) { + out.resize(in.size()); + } + + xor_buf(out.data(), in.data(), in.size()); + return out; + } + } // namespace crypto3 +} // namespace nil + +#endif \ No newline at end of file diff --git a/include/nil/crypto3/block/extern/detail/mempool.hpp b/include/nil/crypto3/block/extern/detail/mempool.hpp new file mode 100644 index 0000000..b1599c4 --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/mempool.hpp @@ -0,0 +1,200 @@ +#ifndef CRYPTO3_MEM_POOL_HPP +#define CRYPTO3_MEM_POOL_HPP + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace detail { + inline bool ptr_in_pool(const void *pool_ptr, size_t poolsize, const void *buf_ptr, size_t bufsize) { + const uintptr_t pool = reinterpret_cast(pool_ptr); + const uintptr_t buf = reinterpret_cast(buf_ptr); + return (buf >= pool) && (buf + bufsize <= pool + poolsize); + } + + inline size_t padding_for_alignment(size_t n, size_t alignment) { + const size_t mod = n % alignment; + if (mod == 0) { + return 0; + } + return alignment - mod; + } + } // namespace detail + + class memory_pool final { + public: + /** + * Initialize a memory pool. The memory is not owned by *this, + * it must be freed by the caller. + * @param pool the pool + * @param pool_size size of pool + * @param page_size some nominal page size (does not need to match + * the system page size) + * @param min_allocation return null for allocs for smaller amounts + * @param max_allocation return null for allocs of larger amounts + * @param align_bit align all returned memory to (1< m_max_alloc) { + throw std::invalid_argument("memory_pool min_alloc > max_alloc"); + } + + if (m_align_bit > 6) { + throw std::invalid_argument("memory_pool invalid align_bit"); + } + + // This is basically just to verify that the range is valid + clear_mem(pool, pool_size); + + m_pool = pool; + m_pool_size = pool_size; + m_freelist.emplace_back(0, m_pool_size); + } + + void *allocate(size_t req) { + const size_t alignment = (1 << m_align_bit); + + if (req > m_pool_size) { + return nullptr; + } + if (req < m_min_alloc || req > m_max_alloc) { + return nullptr; + } + + std::lock_guard lock(m_mutex); + + auto best_fit = m_freelist.end(); + + for (auto i = m_freelist.begin(); i != m_freelist.end(); ++i) { + // If we have a perfect fit, use it immediately + if (i->second == req && (i->first % alignment) == 0) { + const size_t offset = i->first; + m_freelist.erase(i); + clear_mem(m_pool + offset, req); + + BOOST_ASSERT_MSG((reinterpret_cast(m_pool) + offset) % alignment == 0, + "Returning correctly aligned pointer"); + + return m_pool + offset; + } + + if (((best_fit == m_freelist.end()) || (best_fit->second > i->second)) && + (i->second >= (req + detail::padding_for_alignment(i->first, alignment)))) { + best_fit = i; + } + } + + if (best_fit != m_freelist.end()) { + const size_t offset = best_fit->first; + + const size_t alignment_padding = detail::padding_for_alignment(offset, alignment); + + best_fit->first += req + alignment_padding; + best_fit->second -= req + alignment_padding; + + // Need to realign, split the block + if (alignment_padding) { + /* + If we used the entire block except for small piece used for + alignment at the beginning, so just update the entry already + in place (as it is in the correct location), rather than + deleting the empty range and inserting the new one in the + same location. + */ + if (best_fit->second == 0) { + best_fit->first = offset; + best_fit->second = alignment_padding; + } else { + m_freelist.insert(best_fit, std::make_pair(offset, alignment_padding)); + } + } + + clear_mem(m_pool + offset + alignment_padding, req); + + BOOST_ASSERT_MSG((reinterpret_cast(m_pool) + offset + alignment_padding) % alignment == + 0, + "Returning correctly aligned pointer"); + + return m_pool + offset + alignment_padding; + } + + return nullptr; + } + + bool deallocate(void *p, std::size_t n) BOOST_NOEXCEPT { + if (!detail::ptr_in_pool(m_pool, m_pool_size, p, n)) { + return false; + } + + std::memset(p, 0, n); + + std::lock_guard lock(m_mutex); + + const size_t start = static_cast(p) - m_pool; + + auto comp = [](std::pair x, std::pair y) { return x.first < y.first; }; + + auto i = std::lower_bound(m_freelist.begin(), m_freelist.end(), std::make_pair(start, 0), comp); + + // try to merge with later block + if (i != m_freelist.end() && + + start + n == i->first) { + i->first = start; + i->second += n; + n = 0; + } + + // try to merge with previous block + if (i != m_freelist.begin()) { + auto prev = std::prev(i); + + if (prev->first + prev->second == start) { + if (n) { + prev->second += n; + n = 0; + } else { + // merge adjoining + prev->second += i->second; + m_freelist.erase(i); + } + } + } + + if (n != 0) { // no merge possible? + m_freelist.insert(i, std::make_pair(start, n)); + } + + return true; + } + + memory_pool(const memory_pool &) = delete; + + memory_pool &operator=(const memory_pool &) = delete; + + private: + const size_t m_page_size = 0; + const size_t m_min_alloc = 0; + const size_t m_max_alloc = 0; + const uint8_t m_align_bit = 0; + + std::mutex m_mutex; + + std::vector> m_freelist; + uint8_t *m_pool = nullptr; + size_t m_pool_size = 0; + }; + } // namespace crypto3 +} // namespace nil + +#endif \ No newline at end of file diff --git a/include/nil/crypto3/block/extern/detail/wraps.hpp b/include/nil/crypto3/block/extern/detail/wraps.hpp new file mode 100644 index 0000000..1b7deda --- /dev/null +++ b/include/nil/crypto3/block/extern/detail/wraps.hpp @@ -0,0 +1,136 @@ +#ifndef CRYPTO3_FFI_UTILS_HPP +#define CRYPTO3_FFI_UTILS_HPP + +#include +#include +#include +#include + +namespace nil { + namespace crypto3 { + namespace ffi { + template + struct nil_crypto3_struct { + public: + nil_crypto3_struct(T *obj) : m_magic(MAGIC), m_obj(obj) { + } + + virtual ~nil_crypto3_struct() { + m_magic = 0; + m_obj.reset(); + } + + bool magic_ok() const { + return (m_magic == MAGIC); + } + + T *unsafe_get() const { + return m_obj.get(); + } + + private: + uint32_t m_magic; + std::unique_ptr m_obj; + }; + +#define CRYPTO3_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \ + struct NAME final : public nil::crypto3::ffi::nil_crypto3_struct { \ + explicit NAME(TYPE *x) : nil_crypto3_struct(x) { } \ + } + + // Declared in ffi.cpp + int ffi_error_exception_thrown(const char *func_name, const char *exn); + + template + T &safe_get(nil_crypto3_struct *p) { + if (!p) { + throw std::invalid_argument("Null pointer argument"); + } + if (!p->magic_ok()) { + throw std::invalid_argument("Bad magic in ffi object"); + } + + T *t = p->unsafe_get(); + if (t) { + return *t; + } else { + throw std::invalid_argument("Invalid object pointer"); + } + } + + template + int ffi_guard_thunk(const char *func_name, Thunk thunk) { + try { + return thunk(); + } catch (std::bad_alloc &) { + return ffi_error_exception_thrown(func_name, "bad_alloc"); + } catch (std::exception &e) { + return ffi_error_exception_thrown(func_name, e.what()); + } catch (...) { + return ffi_error_exception_thrown(func_name, "unknown exception"); + } + + return CRYPTO3_FFI_ERROR_UNKNOWN_ERROR; + } + + template + int apply_fn(nil_crypto3_struct *o, const char *func_name, F func) { + if (!o) { + return CRYPTO3_FFI_ERROR_NULL_POINTER; + } + + if (!o->magic_ok()) { + return CRYPTO3_FFI_ERROR_INVALID_OBJECT; + } + + return ffi_guard_thunk(func_name, [&]() { return func(*o->unsafe_get()); }); + } + +#define CRYPTO3_FFI_DO(T, obj, param, block) \ + apply_fn(obj, BOOST_CURRENT_FUNCTION, [=](T ¶m) -> int { \ + do { \ + block \ + } while (0); \ + return CRYPTO3_FFI_SUCCESS; \ + }) + + template + int ffi_delete_object(nil_crypto3_struct *obj, const char *func_name) { + try { + if (obj == nullptr) { + return CRYPTO3_FFI_SUCCESS; + } // ignore delete of null objects + + if (!obj->magic_ok()) { + return CRYPTO3_FFI_ERROR_INVALID_OBJECT; + } + + delete obj; + return CRYPTO3_FFI_SUCCESS; + } catch (std::exception &e) { + return ffi_error_exception_thrown(func_name, e.what()); + } catch (...) { + return ffi_error_exception_thrown(func_name, "unknown exception"); + } + } + +#define CRYPTO3_FFI_CHECKED_DELETE(o) ffi_delete_object(o, BOOST_CURRENT_FUNCTION) + + inline int write_output(uint8_t out[], size_t *out_len, const uint8_t buf[], size_t buf_len); + + template + int write_vec_output(uint8_t out[], size_t *out_len, const std::vector &buf) { + return write_output(out, out_len, buf.data(), buf.size()); + } + + inline int write_str_output(uint8_t out[], size_t *out_len, const std::string &str); + + inline int write_str_output(char out[], size_t *out_len, const std::string &str); + + inline int write_str_output(char out[], size_t *out_len, const std::vector &str_vec); + + } // namespace ffi + } // namespace crypto3 +} // namespace nil + +#endif diff --git a/src/extern.cpp b/src/extern.cpp new file mode 100644 index 0000000..773da4b --- /dev/null +++ b/src/extern.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include + +#include +#include + +extern "C" { + +using namespace nil::crypto3::ffi; + +// CRYPTO3_FFI_DECLARE_STRUCT(nil_crypto3_block_cipher_struct, nil::crypto3::BlockCipher, 0x64C29716); + +int nil_crypto3_block_cipher_init(nil_crypto3_block_cipher_t *bc, const char *bc_name) { + return ffi_guard_thunk(BOOST_CURRENT_FUNCTION, [=]() -> int { + if (bc == NULL || bc_name == NULL || *bc_name == 0) { + return CRYPTO3_FFI_ERROR_NULL_POINTER; + } + + *bc = NULL; + + // std::unique_ptr cipher(nil::crypto3::BlockCipher::create(bc_name)); + // if (cipher == NULL) { + // return CRYPTO3_FFI_ERROR_NOT_IMPLEMENTED; + // } + // + // *bc = new nil_crypto3_block_cipher_struct(cipher.release()); + return CRYPTO3_FFI_SUCCESS; + }); +} + +/** + * Destroy a block cipher object + */ +int nil_crypto3_block_cipher_destroy(nil_crypto3_block_cipher_t bc) { + // return CRYPTO3_FFI_CHECKED_DELETE(bc); +} + +int nil_crypto3_block_cipher_clear(nil_crypto3_block_cipher_t bc) { + // return CRYPTO3_FFI_DO(nil::crypto3::BlockCipher, bc, b, { + // b.clear(); + // }); +} + +/** + * Set the key for a block cipher instance + */ +int nil_crypto3_block_cipher_set_key(nil_crypto3_block_cipher_t bc, const uint8_t key[], size_t len) { + // return CRYPTO3_FFI_DO(nil::crypto3::BlockCipher, bc, b, { + // b.set_key(key, len); + // }); +} + +/** + * Return the positive block size of this block cipher, or negative to + * indicate an error + */ +int nil_crypto3_block_cipher_block_size(nil_crypto3_block_cipher_t bc) { + // return CRYPTO3_FFI_DO(nil::crypto3::BlockCipher, bc, b, { + // return static_cast(b.block_size()); + // }); +} + +int nil_crypto3_block_cipher_encrypt_blocks(nil_crypto3_block_cipher_t bc, const uint8_t in[], uint8_t out[], + size_t blocks) { + // return CRYPTO3_FFI_DO(nil::crypto3::BlockCipher, bc, b, { + // b.encrypt_n(in, out, blocks); + // }); +} + +int nil_crypto3_block_cipher_decrypt_blocks(nil_crypto3_block_cipher_t bc, const uint8_t in[], uint8_t out[], + size_t blocks) { + // return CRYPTO3_FFI_DO(nil::crypto3::BlockCipher, bc, b, { + // b.decrypt_n(in, out, blocks); + // }); +} +} \ No newline at end of file