diff --git a/docs/05_features/50_crypto-extensions.md b/docs/05_features/50_crypto-extensions.md new file mode 100644 index 0000000000..414517be80 --- /dev/null +++ b/docs/05_features/50_crypto-extensions.md @@ -0,0 +1,241 @@ +--- +content_title: Crypto Extensions +--- + +As of `v3.0` crypto host functions were extended to include +- `mod_exp`: Big integer modular exponentiation +- `alt_bn128_add`, `alt_bn128_mul`, `alt_bn128_pair`: Add, multiply, and pairing check functions for the `alt_bn128` elliptic curve +- `blake2_f`: `BLAKE2b F` compression function +- `sha3`: sha3` hash function using `SHA3 NIST` +- `Keccak256`: `sha3` hash function using `SHA3 Keccak` +- `k1_recover`: Safe `ECDSA` uncompressed pubkey recover for the `secp256k1` curve + +In `v3.0`, `C` format was supported; in `v3.1`, `C++` format was added for better data abstraction. + +## Prerequisites + +- In `nodeos`, activate protocol feature `CRYPTO_PRIMITIVES` (`68d6405cb8df3de95bd834ebb408196578500a9f818ff62ccc68f60b932f7d82`) +- In smart contract code, include `crypto_ext.hpp` + +## C Format + +- `int32_t alt_bn128_add( const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* result, uint32_t result_len )` +Perform addition operation on the elliptic curve `alt_bn128`, store the result in `result`, and return `0` if success otherwise `-1` +- `int32_t alt_bn128_mul( const char* g1, uint32_t g1_len, const char* scalar, uint32_t scalar_len, char* result, uint32_t result_len )` +Perform scalar multiplication operation on the elliptic curve `alt_bn128` +- `int32_t alt_bn128_pair( const char* pairs, uint32_t pairs_len )` +Perform Optimal-Ate pairing check elliptic curve `alt_bn128`, and return `0` if `true` and successful, `1` if `false`, otherwise `-1` +- `int32_t mod_exp( const char* base, uint32_t base_len, const char* exp, uint32_t exp_len, const char* mod, uint32_t mod_len, char* result, uint32_t result_len )` +Calculate `( BASE^EXP ) % MOD`, store in `result`, and return `0` if successful, otherwise `-1` +- `int32_t blake2_f( uint32_t rounds, const char* state, uint32_t state_len, const char* msg, uint32_t msg_len, const char* t0_offset, uint32_t t0_len, const char* t1_offset, uint32_t t1_len, int32_t final, char* result, uint32_t result_len)` +Implement BLAKE2 compression function `F`. Return `0` if success otherwise `-1` +- `eosio::checksum256 sha3(const char* data, uint32_t length)` +Return hash of `data` using `SHA3 NIST` +- `void assert_sha3(const char* data, uint32_t length, const eosio::checksum256& hash)` +Test if the SHA3 hash generated from data matches the provided digest +- `eosio::checksum256 keccak(const char* data, uint32_t length)` +Return hash of `data` using `SHA3 Keccak` +- `void assert_keccak(const char* data, uint32_t length, const eosio::checksum256& hash) +Test if the SHA3 keccak hash generated from data matches the provided digest +- `int32_t k1_recover( const char* sig, uint32_t sig_len, const char* dig, uint32_t dig_len, char* pub, uint32_t pub_len )` +Calculates the uncompressed public key used for a given signature on a given digest. Return `0` if success otherwise `-1` + +## C++ Format + +C++ types were added to represent `G1` and `G2` points (read and write) and views (read-only), and represent big integers. Their definitions are + +### Types + +```c++ + /** + * Abstracts mutable G1 and G2 points + * + */ + template + struct ec_point { + /** + * Bytes of the x coordinate + */ + std::vector x; + + /** + * Bytes of the y coordinate + */ + std::vector y; + + /** + * Construct a point given x and y + * + * @param x_ - The x coordinate, a vector of chars + * @param y_ - The y coordinate, a vector of chars + */ + ec_point(std::vector& x_, std::vector& y_); + + /** + * Construct a point given a serialized point + * + * @param p - The serialized point + */ + ec_point(std::vector& p); + + /** + * Return serialzed point containing only x and y + */ + std::vector serialized() const; + }; + + /** + * Abstracts read-only G1 and G2 points + */ + template + struct ec_point_view { + /** + * Pointer to the x coordinate + */ + const char* x; + + /** + * Pointer to the y coordinate + */ + const char* y; + + /** + * Number of bytes in each of x and y + */ + uint32_t size; + + /** + * Construct a point view from x and y + * + * @param x_ - The x coordinate, poiter to chars + * @param x_size - x's size + * @param y_ - The y coordinate, poiter to chars + * @param y_size - y's size + */ + ec_point_view(const char* x_, uint32_t x_size, const char* y_, uint32_t y_size); + + /** + * Construct a point view from a serialized point + * + * @param p - The serialized point + */ + ec_point_view(const std::vector& p); + + /** + * Construct a point view from a point + * + * @param p - The point + */ + ec_point_view(const ec_point& p); + + /** + * Return serialzed point containing only x and y + */ + std::vector serialized() const; + }; + + static constexpr size_t g1_coordinate_size = 32; + static constexpr size_t g2_coordinate_size = 64; + + using g1_point = ec_point; + using g2_point = ec_point; + using g1_point_view = ec_point_view; + using g2_point_view = ec_point_view; + + /** + * Big integer. + * + * @ingroup crypto + */ + using bigint = std::vector; +``` + +### Methods + +- `alt_bn128_add` +```c++ + template + g1_point alt_bn128_add( const T& op1, const T& op2 ) +``` +Take two G1 points or G1 views as input and return a G1 point. +- `alt_bn128_mul` +```c++ + template + g1_point alt_bn128_mul( const T& g1, const bigint& scalar) +``` +Take a G1 point or view and a bigint as input and return a G1 point +- `alt_bn128_pair` +```c++ + template + int32_t alt_bn128_pair( const std::vector>& pairs ) +``` +Take a pair of G1 and G2 as input. +- `mod_exp` +```c++ + int32_t mod_exp( const bigint& base, const bigint& exp, const bigint& mod, bigint& result ) +``` +Take bigints as input + +### Examples + +- `alt_bn128_add` +```c++ + std::vector x1, y1, x2, y2; + + // point + eosio::g1_point point1 {x1, y1}; + eosio::g1_point point2 {x2, y2}; + auto result = eosio::alt_bn128_add(point1, point2); + + // view + eosio::g1_point_view point_view1 {x1.data(), x1.size(), y1.data(), y1.size()}; + eosio::g1_point_view point_view2 {x2.data(), x2.size(), y2.data(), y2.size()}; + result = eosio::alt_bn128_add(point_view1, point_view2); +``` + +- `alt_bn128_mul` +```c++ + std::vector x, y, scaler; + eosio::bigint s {scalar}; + + // point + eosio::g1_point g1_point {x, y}; + auto result = eosio::alt_bn128_mul(g1_point, s); + + // view + eosio::g1_point_view g1_view {x.data(), x.size(), y.data(), y.size()}; + result = eosio::alt_bn128_mul(g1_view, s); +``` + +- `alt_bn128_pair` +```c++ + std::vector g1_a_x, g1_a_y, g2_a_x, g2_a_y, g1_b_x, g1_b_y, g2_b_x, g2_b_y; + + + // point + eosio::g1_point g1_a {g1_a_x, g1_a_y}; + eosio::g2_point g2_a {g2_a_x, g2_a_y}; + eosio::g1_point g1_b {g1_b_x, g1_b_y}; + eosio::g2_point g2_b {g2_b_x, g2_b_y}; + std::vector> pairs { {g1_a, g2_a}, {g1_b, g2_b} }; + auto result = eosio::alt_bn128_pair(pairs); + + // view + eosio::g1_point_view g1_view_a {g1_a_x.data(), g1_a_x.size(), g1_a_y.data(), g1_a_y.size()}; + eosio::g2_point_view g2_view_a {g2_a_x.data(), g2_a_x.size(), g2_a_y.data(), g2_a_y.size()}; + eosio::g1_point_view g1_view_b {g1_b_x.data(), g1_b_x.size(), g1_b_y.data(), g1_b_y.size()}; + eosio::g2_point_view g2_view_b {g2_b_x.data(), g2_b_x.size(), g2_b_y.data(), g2_b_y.size()}; + std::vector> view_pairs { {g1_a, g2_a}, {g1_b, g2_b} }; + result = eosio::alt_bn128_pair(view_pairs); +``` + +- `mod_exp` +```c++ + std::vector base, exp, modulo; + eosio::bigint base_val {base}; + eosio::bigint exp_val {exp}; + eosio::bigint modulo_val {modulo}; + eosio::bigint result( modulo.size(), '\0' ); + + auto rc = eosio::mod_exp(base_val, exp_val, modulo_val, result); +```