diff --git a/.gitignore b/.gitignore index b04ab2a..7ad6928 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ build/* tests/bin tests/.crates.toml tests/.crates2.json + +.vscode + diff --git a/Makefile b/Makefile index b8c5985..f8fc78c 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ build/libed25519.a: build/ed25519/sign.o build/ed25519/verify.o build/ed25519/sh build/ed25519/key_exchange.o build/ed25519/ge.o build/ed25519/fe.o build/ed25519/add_scalar.o $(AR) cr $@ $^ -build/auth: c/auth.c c/cardano/cardano_lock_inc.h c/ripple.h deps/mbedtls/library/libmbedcrypto.a build/libed25519.a build/libnanocbor.a +build/auth: c/auth.c c/ckb_auth.h c/elf_setup.h c/cardano/cardano_lock_inc.h c/ripple.h deps/mbedtls/library/libmbedcrypto.a build/libed25519.a build/libnanocbor.a $(CC) $(AUTH_CFLAGS) $(LDFLAGS) -fPIE -pie -Wl,--dynamic-list c/auth.syms -o $@ $^ cp $@ $@.debug $(OBJCOPY) --strip-debug --strip-all $@ @@ -99,7 +99,9 @@ ALL_C_SOURCE := c/always_success.c \ c/auth.c \ c/auth_libecc.c \ c/ckb_hex.h \ - c/ripple.h + c/ripple.h \ + c/elf_setup.h \ + tests/auth-c-lock/auth_c_lock.c fmt: clang-format -i $(ALL_C_SOURCE) diff --git a/c/auth.c b/c/auth.c index c5b8725..a02849f 100644 --- a/c/auth.c +++ b/c/auth.c @@ -1,4 +1,5 @@ // clang-format off +#define CKB_C_STDLIB_PRINTF #include "mbedtls/md.h" #include "mbedtls/md_internal.h" #include "mbedtls/memory_buffer_alloc.h" @@ -15,29 +16,20 @@ #define __builtin_ctzl secp256k1_ctz64_var_debruijn #include "ckb_consts.h" -#if defined(CKB_USE_SIM) -// exclude ckb_dlfcn.h -#define CKB_C_STDLIB_CKB_DLFCN_H_ -#include "ckb_syscall_auth_sim.h" -#else #include "ckb_syscalls.h" -#endif - #include "ckb_keccak256.h" #include "secp256k1_helper_20210801.h" #include "include/secp256k1_schnorrsig.h" +// Must be the last to include, as secp256k1 and this header file both define +// the macros CHECK and CHECK2. #include "ckb_auth.h" -#undef CKB_SUCCESS #include "ckb_hex.h" #include "blake2b.h" - -// Must be the last to include, as secp256k1 and this header file both define -// the macros CHECK and CHECK2. -// clang-format on - +#include "elf_setup.h" #include "cardano/cardano_lock_inc.h" #include "ripple.h" +// clang-format on #define SECP256K1_PUBKEY_SIZE 33 #define UNCOMPRESSED_SECP256K1_PUBKEY_SIZE 65 @@ -240,11 +232,12 @@ static int _recover_secp256k1_pubkey_btc(const uint8_t *sig, size_t sig_len, return ret; } -int validate_signature_ckb(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, size_t msg_len, - uint8_t *output, size_t *output_len) { +int validate_signature_ckb(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, size_t pubkey_hash_len) { int ret = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } uint8_t out_pubkey[SECP256K1_PUBKEY_SIZE]; @@ -259,17 +252,17 @@ int validate_signature_ckb(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, out_pubkey, out_pubkey_size); blake2b_final(&ctx, out_pubkey, BLAKE2B_BLOCK_SIZE); - memcpy(output, out_pubkey, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, out_pubkey, AUTH160_SIZE); return ret; } -int validate_signature_eth(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, size_t msg_len, - uint8_t *output, size_t *output_len) { +int validate_signature_eth(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, size_t pubkey_hash_len) { int ret = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } uint8_t out_pubkey[UNCOMPRESSED_SECP256K1_PUBKEY_SIZE]; @@ -303,17 +296,17 @@ int validate_signature_eth(void *prefilled_data, const uint8_t *sig, keccak_update(&sha3_ctx, &out_pubkey[1], out_pubkey_size - 1); keccak_final(&sha3_ctx, out_pubkey); - memcpy(output, &out_pubkey[12], AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, &out_pubkey[12], AUTH160_SIZE); return ret; } -int validate_signature_eos(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, size_t msg_len, - uint8_t *output, size_t *output_len) { +int validate_signature_eos(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } uint8_t out_pubkey[UNCOMPRESSED_SECP256K1_PUBKEY_SIZE]; @@ -327,17 +320,17 @@ int validate_signature_eos(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, out_pubkey, out_pubkey_size); blake2b_final(&ctx, out_pubkey, BLAKE2B_BLOCK_SIZE); - memcpy(output, out_pubkey, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, out_pubkey, AUTH160_SIZE); exit: return err; } -int validate_signature_btc(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, size_t msg_len, - uint8_t *output, size_t *output_len) { +int validate_signature_btc(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } uint8_t out_pubkey[UNCOMPRESSED_SECP256K1_PUBKEY_SIZE]; @@ -349,21 +342,21 @@ int validate_signature_btc(void *prefilled_data, const uint8_t *sig, unsigned char temp[AUTH160_SIZE]; err = bitcoin_hash160(out_pubkey, out_pubkey_size, temp); - memcpy(output, temp, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, temp, AUTH160_SIZE); exit: return err; } -int validate_signature_schnorr(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_schnorr(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; int success = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } if (sig_len != SCHNORR_SIGNATURE_SIZE || msg_len != 32) { @@ -387,19 +380,19 @@ int validate_signature_schnorr(void *prefilled_data, const uint8_t *sig, blake2b_update(&blake2b_ctx, sig, SCHNORR_PUBKEY_SIZE); blake2b_final(&blake2b_ctx, temp, BLAKE2B_BLOCK_SIZE); - memcpy(output, temp, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, temp, AUTH160_SIZE); return 0; } -int validate_signature_cardano(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_cardano(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } @@ -422,18 +415,18 @@ int validate_signature_cardano(void *prefilled_data, const uint8_t *sig, sizeof(cardano_data.public_key)); blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash)); - memcpy(output, pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, pubkey_hash, AUTH160_SIZE); exit: return err; } -int validate_signature_ripple(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_ripple(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } @@ -448,8 +441,7 @@ int validate_signature_ripple(void *prefilled_data, const uint8_t *sig, ERROR_INVALID_ARG); CHECK(verify_ripple(&sign_data)); - get_ripple_pubkey_hash(sign_data.public_key, output); - *output_len = AUTH160_SIZE; + get_ripple_pubkey_hash(sign_data.public_key, out_pubkey_hash); exit: return err; } @@ -582,14 +574,18 @@ int ed25519_verify_monero(const unsigned char *signature, return sc_isnonzero((const uint8_t *)c) == 0; } -int validate_signature_monero(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_monero(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; CHECK2(msg_len == BLAKE2B_BLOCK_SIZE, ERROR_INVALID_ARG); CHECK2(sig_len == MONERO_DATA_SIZE, ERROR_INVALID_ARG); + if (pubkey_hash_len < AUTH160_SIZE) { + return ERROR_INVALID_ARG; + } uint8_t *mode_ptr = (uint8_t *)sig + MONERO_SIGNATURE_SIZE; // We only support using spend key to sign transactions. @@ -613,8 +609,7 @@ int validate_signature_monero(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, mode_ptr, 1 + MONERO_PUBKEY_SIZE * 2); blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash)); - memcpy(output, pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, pubkey_hash, AUTH160_SIZE); exit: return err; } @@ -659,12 +654,16 @@ int validate_solana_signed_message(const uint8_t *signed_msg, return err; } -int validate_signature_solana(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_solana(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; + if (pubkey_hash_len < AUTH160_SIZE) { + return ERROR_INVALID_ARG; + } CHECK2(sig_len == SOLANA_WRAPPED_SIGNATURE_SIZE, ERROR_INVALID_ARG); CHECK2(msg_len == SOLANA_BLOCKHASH_SIZE, ERROR_INVALID_ARG); sig_len = (size_t)sig[0] | ((size_t)sig[1] << 8); @@ -689,8 +688,7 @@ int validate_signature_solana(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, pub_key_ptr, SOLANA_PUBKEY_SIZE); blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash)); - memcpy(output, pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, pubkey_hash, AUTH160_SIZE); exit: return err; } @@ -736,10 +734,11 @@ int get_toncoin_message(const uint8_t *signed_msg, size_t signed_msg_len, return err; } -int validate_signature_toncoin(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_toncoin(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; CHECK2(sig_len == TONCOIN_WRAPPED_SIGNATURE_SIZE, ERROR_INVALID_ARG); @@ -768,8 +767,7 @@ int validate_signature_toncoin(void *prefilled_data, const uint8_t *sig, uint8_t test_pubkey_hash[AUTH160_SIZE] = {0}; // memcpy(output, pubkey_hash, AUTH160_SIZE); - memcpy(output, test_pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, test_pubkey_hash, AUTH160_SIZE); exit: return err; } @@ -932,9 +930,8 @@ bool is_lock_script_hash_present(uint8_t *lock_script_hash) { return false; } -static int verify(uint8_t *pubkey_hash, const uint8_t *sig, uint32_t sig_len, - const uint8_t *msg, uint32_t msg_len, - validate_signature_t func, convert_msg_t convert) { +static int verify(CkbAuthValidatorType *validator, ckb_auth_validate_t func, + convert_msg_t convert) { int err = 0; uint8_t new_msg[BLAKE2B_BLOCK_SIZE]; @@ -942,16 +939,16 @@ static int verify(uint8_t *pubkey_hash, const uint8_t *sig, uint32_t sig_len, unsigned char alloc_buff[1024]; mbedtls_memory_buffer_alloc_init(alloc_buff, sizeof(alloc_buff)); - err = convert(msg, msg_len, new_msg, sizeof(new_msg)); + err = convert(validator->msg, validator->msg_len, new_msg, sizeof(new_msg)); CHECK(err); uint8_t output_pubkey_hash[AUTH160_SIZE]; - size_t output_len = AUTH160_SIZE; - err = func(NULL, sig, sig_len, new_msg, sizeof(new_msg), output_pubkey_hash, - &output_len); + err = func(validator->prefilled_data, validator->algorithm_id, + validator->sig, validator->sig_len, new_msg, sizeof(new_msg), + output_pubkey_hash, sizeof(output_pubkey_hash)); CHECK(err); - int same = memcmp(pubkey_hash, output_pubkey_hash, AUTH160_SIZE); + int same = memcmp(validator->pubkey_hash, output_pubkey_hash, AUTH160_SIZE); CHECK2(same == 0, ERROR_MISMATCHED); exit: @@ -977,8 +974,9 @@ static int verify(uint8_t *pubkey_hash, const uint8_t *sig, uint32_t sig_len, #define SIGNATURE_SIZE 65 #define PUBKEY_SIZE 33 -int verify_multisig(const uint8_t *lock_bytes, size_t lock_bytes_len, - const uint8_t *message, const uint8_t *hash) { +int verify_multisig(uint8_t *prefilled_data, const uint8_t *lock_bytes, + size_t lock_bytes_len, const uint8_t *message, + const uint8_t *hash) { int ret; uint8_t temp[PUBKEY_SIZE]; @@ -1111,79 +1109,74 @@ int verify_multisig(const uint8_t *lock_bytes, size_t lock_bytes_len, // dynamic linking entry __attribute__((visibility("default"))) int ckb_auth_validate( - uint8_t auth_algorithm_id, const uint8_t *signature, - uint32_t signature_size, const uint8_t *message, uint32_t message_size, - uint8_t *pubkey_hash, uint32_t pubkey_hash_size) { + uint8_t *prefilled_data, uint8_t algorithm_id, const uint8_t *sig, + size_t sig_len, const uint8_t *msg, size_t msg_len, uint8_t *pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - CHECK2(signature != NULL, ERROR_INVALID_ARG); - CHECK2(message != NULL, ERROR_INVALID_ARG); - CHECK2(message_size > 0, ERROR_INVALID_ARG); - CHECK2(pubkey_hash_size == AUTH160_SIZE, ERROR_INVALID_ARG); - - if (auth_algorithm_id == AuthAlgorithmIdCkb) { - CHECK2(signature_size == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_ckb, convert_copy); + + CkbAuthValidatorType validator = {.prefilled_data = prefilled_data, + .algorithm_id = algorithm_id, + .sig = sig, + .sig_len = sig_len, + .msg = msg, + .msg_len = msg_len, + .pubkey_hash = pubkey_hash, + .pubkey_hash_len = pubkey_hash_len}; + + CHECK2(sig != NULL, ERROR_INVALID_ARG); + CHECK2(msg != NULL, ERROR_INVALID_ARG); + CHECK2(msg_len > 0, ERROR_INVALID_ARG); + CHECK2(pubkey_hash_len == AUTH160_SIZE, ERROR_INVALID_ARG); + + if (algorithm_id == AuthAlgorithmIdCkb) { + CHECK2(sig_len == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); + err = verify(&validator, validate_signature_ckb, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdEthereum) { - CHECK2(signature_size == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_eth, convert_eth_message); + } else if (algorithm_id == AuthAlgorithmIdEthereum) { + CHECK2(sig_len == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); + err = verify(&validator, validate_signature_eth, convert_eth_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdEos) { - CHECK2(signature_size == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_eos, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdEos) { + CHECK2(sig_len == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); + err = verify(&validator, validate_signature_eos, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdTron) { - CHECK2(signature_size == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); - err = - verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_eth, convert_tron_message); + } else if (algorithm_id == AuthAlgorithmIdTron) { + CHECK2(sig_len == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG); + err = verify(&validator, validate_signature_eth, convert_tron_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdBitcoin) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_btc, convert_btc_message); + } else if (algorithm_id == AuthAlgorithmIdBitcoin) { + err = verify(&validator, validate_signature_btc, convert_btc_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdDogecoin) { - err = - verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_btc, convert_doge_message); + } else if (algorithm_id == AuthAlgorithmIdDogecoin) { + err = verify(&validator, validate_signature_btc, convert_doge_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdLitecoin) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_btc, + } else if (algorithm_id == AuthAlgorithmIdLitecoin) { + err = verify(&validator, validate_signature_btc, convert_litecoin_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdCkbMultisig) { - err = verify_multisig(signature, signature_size, message, pubkey_hash); + } else if (algorithm_id == AuthAlgorithmIdCkbMultisig) { + err = verify_multisig(prefilled_data, sig, sig_len, msg, pubkey_hash); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdSchnorr) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_schnorr, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdSchnorr) { + err = verify(&validator, validate_signature_schnorr, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdCardano) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_cardano, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdCardano) { + err = verify(&validator, validate_signature_cardano, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdMonero) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_monero, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdMonero) { + err = verify(&validator, validate_signature_monero, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdSolana) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_solana, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdSolana) { + err = verify(&validator, validate_signature_solana, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdRipple) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_ripple, + } else if (algorithm_id == AuthAlgorithmIdRipple) { + err = verify(&validator, validate_signature_ripple, convert_ripple_message); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdToncoin) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_toncoin, convert_copy); + } else if (algorithm_id == AuthAlgorithmIdToncoin) { + err = verify(&validator, validate_signature_toncoin, convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdOwnerLock) { + } else if (algorithm_id == AuthAlgorithmIdOwnerLock) { CHECK2(is_lock_script_hash_present(pubkey_hash), ERROR_MISMATCHED); err = 0; } else { diff --git a/c/auth_libecc.c b/c/auth_libecc.c index aaf0c51..53175b1 100644 --- a/c/auth_libecc.c +++ b/c/auth_libecc.c @@ -1,28 +1,20 @@ -#include "ckb_auth.h" - // clang-format off +#include "ckb_auth.h" #include "secp256r1.h" - #include "ckb_consts.h" -#if defined(CKB_USE_SIM) -// exclude ckb_dlfcn.h -#define CKB_C_STDLIB_CKB_DLFCN_H_ -#include "ckb_syscall_auth_sim.h" -#else #include "ckb_syscalls.h" -#endif - #include "blake2b.h" -#undef CKB_SUCCESS +#include "elf_setup.h" // clang-format on -int validate_signature_secp256r1(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { +int validate_signature_secp256r1(uint8_t *prefilled_data, uint8_t algorithm_id, + const uint8_t *sig, size_t sig_len, + const uint8_t *msg, size_t msg_len, + uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } CHECK2(msg_len == BLAKE2B_BLOCK_SIZE, ERROR_INVALID_ARG); @@ -40,19 +32,20 @@ int validate_signature_secp256r1(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, pub_key_ptr, SECP256R1_PUBKEY_SIZE); blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash)); - memcpy(output, pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, pubkey_hash, AUTH160_SIZE); + exit: return err; } -int validate_signature_secp256r1_raw(void *prefilled_data, const uint8_t *sig, +int validate_signature_secp256r1_raw(uint8_t *prefilled_data, + uint8_t algorithm_id, const uint8_t *sig, size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len) { + size_t msg_len, uint8_t *out_pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - if (*output_len < AUTH160_SIZE) { + if (pubkey_hash_len < AUTH160_SIZE) { return ERROR_INVALID_ARG; } CHECK2(msg_len == BLAKE2B_BLOCK_SIZE, ERROR_INVALID_ARG); @@ -70,8 +63,7 @@ int validate_signature_secp256r1_raw(void *prefilled_data, const uint8_t *sig, blake2b_update(&ctx, pub_key_ptr, SECP256R1_PUBKEY_SIZE); blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash)); - memcpy(output, pubkey_hash, AUTH160_SIZE); - *output_len = AUTH160_SIZE; + memcpy(out_pubkey_hash, pubkey_hash, AUTH160_SIZE); exit: return err; } @@ -84,22 +76,21 @@ int convert_copy(const uint8_t *msg, size_t msg_len, uint8_t *new_msg, return 0; } -static int verify(uint8_t *pubkey_hash, const uint8_t *sig, uint32_t sig_len, - const uint8_t *msg, uint32_t msg_len, - validate_signature_t func, convert_msg_t convert) { +static int verify_secp256r1(CkbAuthValidatorType *validator, + ckb_auth_validate_t func, convert_msg_t convert) { int err = 0; uint8_t new_msg[BLAKE2B_BLOCK_SIZE]; - err = convert(msg, msg_len, new_msg, sizeof(new_msg)); + err = convert(validator->msg, validator->msg_len, new_msg, sizeof(new_msg)); CHECK(err); uint8_t output_pubkey_hash[AUTH160_SIZE]; - size_t output_len = AUTH160_SIZE; - err = func(NULL, sig, sig_len, new_msg, sizeof(new_msg), output_pubkey_hash, - &output_len); + err = func(validator->prefilled_data, validator->algorithm_id, + validator->sig, validator->sig_len, new_msg, sizeof(new_msg), + output_pubkey_hash, AUTH160_SIZE); CHECK(err); - int same = memcmp(pubkey_hash, output_pubkey_hash, AUTH160_SIZE); + int same = memcmp(validator->pubkey_hash, output_pubkey_hash, AUTH160_SIZE); CHECK2(same == 0, ERROR_MISMATCHED); exit: @@ -108,23 +99,31 @@ static int verify(uint8_t *pubkey_hash, const uint8_t *sig, uint32_t sig_len, // dynamic linking entry __attribute__((visibility("default"))) int ckb_auth_validate( - uint8_t auth_algorithm_id, const uint8_t *signature, - uint32_t signature_size, const uint8_t *message, uint32_t message_size, - uint8_t *pubkey_hash, uint32_t pubkey_hash_size) { + uint8_t *prefilled_data, uint8_t algorithm_id, const uint8_t *sig, + size_t sig_len, const uint8_t *msg, size_t msg_len, uint8_t *pubkey_hash, + size_t pubkey_hash_len) { int err = 0; - CHECK2(signature != NULL, ERROR_INVALID_ARG); - CHECK2(message != NULL, ERROR_INVALID_ARG); - CHECK2(message_size > 0, ERROR_INVALID_ARG); - CHECK2(pubkey_hash_size == AUTH160_SIZE, ERROR_INVALID_ARG); - - if (auth_algorithm_id == AuthAlgorithmIdSecp256R1) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_secp256r1, convert_copy); + CkbAuthValidatorType validator = {.prefilled_data = prefilled_data, + .algorithm_id = algorithm_id, + .sig = sig, + .sig_len = sig_len, + .msg = msg, + .msg_len = msg_len, + .pubkey_hash = pubkey_hash, + .pubkey_hash_len = pubkey_hash_len}; + + CHECK2(sig != NULL, ERROR_INVALID_ARG); + CHECK2(msg != NULL, ERROR_INVALID_ARG); + CHECK2(msg_len > 0, ERROR_INVALID_ARG); + CHECK2(pubkey_hash_len == AUTH160_SIZE, ERROR_INVALID_ARG); + + if (algorithm_id == AuthAlgorithmIdSecp256R1) { + err = verify_secp256r1(&validator, validate_signature_secp256r1, + convert_copy); CHECK(err); - } else if (auth_algorithm_id == AuthAlgorithmIdSecp256R1Raw) { - err = verify(pubkey_hash, signature, signature_size, message, - message_size, validate_signature_secp256r1_raw, - convert_copy); + } else if (algorithm_id == AuthAlgorithmIdSecp256R1Raw) { + err = verify_secp256r1(&validator, validate_signature_secp256r1_raw, + convert_copy); CHECK(err); } else { CHECK2(false, ERROR_NOT_IMPLEMENTED); diff --git a/c/cardano/cardano_lock_inc.h b/c/cardano/cardano_lock_inc.h index f1e18d2..218189b 100644 --- a/c/cardano/cardano_lock_inc.h +++ b/c/cardano/cardano_lock_inc.h @@ -1,14 +1,5 @@ #include "ckb_consts.h" -#if defined(CKB_USE_SIM) -// exclude ckb_dlfcn.h -#define CKB_C_STDLIB_CKB_DLFCN_H_ -#include "ckb_syscall_auth_sim.h" -#else - #include "ckb_syscalls.h" -#endif - -// #include "blake2b.h" #include "nanocbor.h" diff --git a/c/ckb_auth.h b/c/ckb_auth.h index 2221c59..728aa1f 100644 --- a/c/ckb_auth.h +++ b/c/ckb_auth.h @@ -11,39 +11,28 @@ // secp256k1 also defines this macros #undef CHECK2 #undef CHECK -#define CHECK2(cond, code) \ - do { \ - if (!(cond)) { \ - err = code; \ - goto exit; \ - } \ +#define CHECK2(cond, code) \ + do { \ + if (!(cond)) { \ + printf("%s:%d, error code = %d", __FILE__, __LINE__, code); \ + err = code; \ + goto exit; \ + } \ } while (0) -#define CHECK(code) \ - do { \ - if (code != 0) { \ - err = code; \ - goto exit; \ - } \ +#define CHECK(code) \ + do { \ + if (code != 0) { \ + printf("%s:%d, error code = %d", __FILE__, __LINE__, code); \ + err = code; \ + goto exit; \ + } \ } while (0) #define CKB_AUTH_LEN 21 #define AUTH160_SIZE 20 #define BLAKE2B_BLOCK_SIZE 32 -#define OFFSETOF(TYPE, ELEMENT) ((size_t) & (((TYPE *)0)->ELEMENT)) -#define PT_DYNAMIC 2 - -/* See https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-42444.html - * for details */ -#define DT_RELA 7 -#define DT_RELACOUNT 0x6ffffff9 -#define DT_JMPREL 23 -#define DT_PLTRELSZ 2 -#define DT_PLTREL 20 -#define DT_SYMTAB 6 -#define DT_SYMENT 11 - enum AuthErrorCodeType { ERROR_NOT_IMPLEMENTED = 100, ERROR_MISMATCHED, @@ -60,12 +49,6 @@ enum AuthErrorCodeType { ERROR_SCHNORR, }; -typedef struct { - uint64_t type; - uint64_t value; -} Elf64_Dynamic; - -// TODO: when ready, move it into ckb-c-stdlib typedef struct CkbAuthType { uint8_t algorithm_id; uint8_t content[AUTH160_SIZE]; @@ -105,20 +88,25 @@ enum AuthAlgorithmIdType { AuthAlgorithmIdOwnerLock = 0xFC, }; -typedef int (*validate_signature_t)(void *prefilled_data, const uint8_t *sig, - size_t sig_len, const uint8_t *msg, - size_t msg_len, uint8_t *output, - size_t *output_len); - typedef int (*convert_msg_t)(const uint8_t *msg, size_t msg_len, uint8_t *new_msg, size_t new_msg_len); -typedef int (*ckb_auth_validate_t)(uint8_t auth_algorithm_id, - const uint8_t *signature, - uint32_t signature_size, - const uint8_t *message, - uint32_t message_size, uint8_t *pubkey_hash, - uint32_t pubkey_hash_size); +typedef int (*ckb_auth_validate_t)(uint8_t *prefilled_data, + uint8_t algorithm_id, const uint8_t *sig, + size_t sig_len, const uint8_t *msg, + size_t msg_len, uint8_t *pubkey_hash, + size_t pubkey_hash_len); + +typedef struct CkbAuthValidatorType { + uint8_t *prefilled_data; + uint8_t algorithm_id; + const uint8_t *sig; + size_t sig_len; + const uint8_t *msg; + size_t msg_len; + uint8_t *pubkey_hash; + size_t pubkey_hash_len; +} CkbAuthValidatorType; #ifndef CKB_AUTH_DISABLE_DYNAMIC_LIB @@ -196,8 +184,9 @@ int get_dl_func_by_code_hash(const uint8_t *code_hash, uint8_t hash_type, #endif // CKB_AUTH_DISABLE_DYNAMIC_LIB -int ckb_auth(CkbEntryType *entry, CkbAuthType *id, const uint8_t *signature, - uint32_t signature_size, const uint8_t *message32) { +int ckb_auth(uint8_t *prefilled_data, CkbEntryType *entry, CkbAuthType *id, + const uint8_t *signature, uint32_t signature_size, + const uint8_t *message32) { int err = 0; if (entry->entry_category == EntryCategoryDynamicLibrary) { #ifdef CKB_AUTH_DISABLE_DYNAMIC_LIB @@ -210,8 +199,8 @@ int ckb_auth(CkbEntryType *entry, CkbAuthType *id, const uint8_t *signature, if (err) { return err; } - return func(id->algorithm_id, signature, signature_size, message32, - BLAKE2B_BLOCK_SIZE, id->content, AUTH160_SIZE); + return func(prefilled_data, id->algorithm_id, signature, signature_size, + message32, BLAKE2B_BLOCK_SIZE, id->content, AUTH160_SIZE); #endif // CKB_AUTH_DISABLE_DYNAMIC_LIB } else if (entry->entry_category == EntryCategoryExec || entry->entry_category == EntryCategorySpawn) { @@ -265,98 +254,6 @@ int ckb_auth(CkbEntryType *entry, CkbAuthType *id, const uint8_t *signature, } } -int setup_elf() { -// fix error: -// c/auth.c:810:50: error: array subscript 0 is outside array bounds of -// 'uint64_t[0]' {aka 'long unsigned int[]'} [-Werror=array-bounds] -// 810 | Elf64_Phdr *program_headers = (Elf64_Phdr *)(*phoff); -// | ~^~~~~~~ -#if defined(__GNUC__) && (__GNUC__ >= 12) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif - uint64_t *phoff = (uint64_t *)OFFSETOF(Elf64_Ehdr, e_phoff); - uint16_t *phnum = (uint16_t *)OFFSETOF(Elf64_Ehdr, e_phnum); - Elf64_Phdr *program_headers = (Elf64_Phdr *)(*phoff); - - for (int i = 0; i < *phnum; i++) { - Elf64_Phdr *program_header = &program_headers[i]; - if (program_header->p_type == PT_DYNAMIC) { - Elf64_Dynamic *d = (Elf64_Dynamic *)program_header->p_vaddr; - uint64_t rela_address = 0; - uint64_t rela_count = 0; - uint64_t jmprel_address = 0; - uint64_t pltrel_size = 0; - uint64_t pltrel = 0; - uint64_t symtab_address = 0; - uint64_t symtab_entry_size = 0; - while (d->type != 0) { - switch (d->type) { - case DT_RELA: - rela_address = d->value; - break; - case DT_RELACOUNT: - rela_count = d->value; - break; - case DT_JMPREL: - jmprel_address = d->value; - break; - case DT_PLTRELSZ: - pltrel_size = d->value; - break; - case DT_PLTREL: - pltrel = d->value; - break; - case DT_SYMTAB: - symtab_address = d->value; - break; - case DT_SYMENT: - symtab_entry_size = d->value; - break; - } - d++; - } - if (rela_address > 0 && rela_count > 0) { - Elf64_Rela *relocations = (Elf64_Rela *)rela_address; - for (int j = 0; j < rela_count; j++) { - Elf64_Rela *relocation = &relocations[j]; - if (relocation->r_info != R_RISCV_RELATIVE) { - return ERROR_INVALID_ELF; - } - *((uint64_t *)(relocation->r_offset)) = - (uint64_t)(relocation->r_addend); - } - } - if (jmprel_address > 0 && pltrel_size > 0 && pltrel == DT_RELA && - symtab_address > 0) { - if (pltrel_size % sizeof(Elf64_Rela) != 0) { - return ERROR_INVALID_ELF; - } - if (symtab_entry_size != sizeof(Elf64_Sym)) { - return ERROR_INVALID_ELF; - } - Elf64_Rela *relocations = (Elf64_Rela *)jmprel_address; - Elf64_Sym *symbols = (Elf64_Sym *)symtab_address; - for (int j = 0; j < pltrel_size / sizeof(Elf64_Rela); j++) { - Elf64_Rela *relocation = &relocations[j]; - uint32_t idx = (uint32_t)(relocation->r_info >> 32); - uint32_t t = (uint32_t)relocation->r_info; - if (t != R_RISCV_JUMP_SLOT) { - return ERROR_INVALID_ELF; - } - Elf64_Sym *sym = &symbols[idx]; - *((uint64_t *)(relocation->r_offset)) = sym->st_value; - } - } - } - } - - return 0; -#if defined(__GNUC__) && (__GNUC__ >= 12) -#pragma GCC diagnostic pop -#endif -} - static int ckb_auth_validate_with_func(int argc, char *argv[], ckb_auth_validate_t validate_func) { int err = 0; @@ -413,8 +310,10 @@ static int ckb_auth_validate_with_func(int argc, char *argv[], pubkey_hash_len == AUTH160_SIZE, ERROR_SPAWN_INVALID_PUBKEY); - err = validate_func(algorithm_id, signature, signature_len, message, - message_len, pubkey_hash, pubkey_hash_len); + // TODO: load prefilled data when in entry of exec/spawn + err = validate_func(NULL, algorithm_id, signature, (size_t)signature_len, + message, (size_t)message_len, pubkey_hash, + (size_t)pubkey_hash_len); CHECK(err); exit: diff --git a/c/elf_setup.h b/c/elf_setup.h new file mode 100644 index 0000000..77f3712 --- /dev/null +++ b/c/elf_setup.h @@ -0,0 +1,118 @@ +#ifndef __ELF_SETUP_H__ +#define __ELF_SETUP_H__ + +#include +#include +#include "ckb_dlfcn.h" + +#define OFFSETOF(TYPE, ELEMENT) ((size_t) & (((TYPE *)0)->ELEMENT)) +#define PT_DYNAMIC 2 + +/* See https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-42444.html + * for details */ +#define DT_RELA 7 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_JMPREL 23 +#define DT_PLTRELSZ 2 +#define DT_PLTREL 20 +#define DT_SYMTAB 6 +#define DT_SYMENT 11 + +typedef struct { + uint64_t type; + uint64_t value; +} Elf64_Dynamic; + +int setup_elf() { +// fix error: +// c/auth.c:810:50: error: array subscript 0 is outside array bounds of +// 'uint64_t[0]' {aka 'long unsigned int[]'} [-Werror=array-bounds] +// 810 | Elf64_Phdr *program_headers = (Elf64_Phdr *)(*phoff); +// | ~^~~~~~~ +#if defined(__GNUC__) && (__GNUC__ >= 12) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + uint64_t *phoff = (uint64_t *)OFFSETOF(Elf64_Ehdr, e_phoff); + uint16_t *phnum = (uint16_t *)OFFSETOF(Elf64_Ehdr, e_phnum); + Elf64_Phdr *program_headers = (Elf64_Phdr *)(*phoff); + + for (int i = 0; i < *phnum; i++) { + Elf64_Phdr *program_header = &program_headers[i]; + if (program_header->p_type == PT_DYNAMIC) { + Elf64_Dynamic *d = (Elf64_Dynamic *)program_header->p_vaddr; + uint64_t rela_address = 0; + uint64_t rela_count = 0; + uint64_t jmprel_address = 0; + uint64_t pltrel_size = 0; + uint64_t pltrel = 0; + uint64_t symtab_address = 0; + uint64_t symtab_entry_size = 0; + while (d->type != 0) { + switch (d->type) { + case DT_RELA: + rela_address = d->value; + break; + case DT_RELACOUNT: + rela_count = d->value; + break; + case DT_JMPREL: + jmprel_address = d->value; + break; + case DT_PLTRELSZ: + pltrel_size = d->value; + break; + case DT_PLTREL: + pltrel = d->value; + break; + case DT_SYMTAB: + symtab_address = d->value; + break; + case DT_SYMENT: + symtab_entry_size = d->value; + break; + } + d++; + } + if (rela_address > 0 && rela_count > 0) { + Elf64_Rela *relocations = (Elf64_Rela *)rela_address; + for (int j = 0; j < rela_count; j++) { + Elf64_Rela *relocation = &relocations[j]; + if (relocation->r_info != R_RISCV_RELATIVE) { + return ERROR_INVALID_ELF; + } + *((uint64_t *)(relocation->r_offset)) = + (uint64_t)(relocation->r_addend); + } + } + if (jmprel_address > 0 && pltrel_size > 0 && pltrel == DT_RELA && + symtab_address > 0) { + if (pltrel_size % sizeof(Elf64_Rela) != 0) { + return ERROR_INVALID_ELF; + } + if (symtab_entry_size != sizeof(Elf64_Sym)) { + return ERROR_INVALID_ELF; + } + Elf64_Rela *relocations = (Elf64_Rela *)jmprel_address; + Elf64_Sym *symbols = (Elf64_Sym *)symtab_address; + for (int j = 0; j < pltrel_size / sizeof(Elf64_Rela); j++) { + Elf64_Rela *relocation = &relocations[j]; + uint32_t idx = (uint32_t)(relocation->r_info >> 32); + uint32_t t = (uint32_t)relocation->r_info; + if (t != R_RISCV_JUMP_SLOT) { + return ERROR_INVALID_ELF; + } + Elf64_Sym *sym = &symbols[idx]; + *((uint64_t *)(relocation->r_offset)) = sym->st_value; + } + } + } + } + + return 0; +#if defined(__GNUC__) && (__GNUC__ >= 12) +#pragma GCC diagnostic pop +#endif +} + +#endif // __ELF_SETUP_H__ diff --git a/c/ripple.h b/c/ripple.h index d3a7365..920d81b 100644 --- a/c/ripple.h +++ b/c/ripple.h @@ -11,20 +11,22 @@ #undef CHECK2 #undef CHECK -#define CHECK2(cond, code) \ - do { \ - if (!(cond)) { \ - err = code; \ - goto exit; \ - } \ +#define CHECK2(cond, code) \ + do { \ + if (!(cond)) { \ + printf("%s:%d, error code = %d", __FILE__, __LINE__, code); \ + err = code; \ + goto exit; \ + } \ } while (0) -#define CHECK(code) \ - do { \ - if (code != 0) { \ - err = code; \ - goto exit; \ - } \ +#define CHECK(code) \ + do { \ + if (code != 0) { \ + printf("%s:%d, error code = %d", __FILE__, __LINE__, code); \ + err = code; \ + goto exit; \ + } \ } while (0) #define RIPPLE_SIGN_DATA_MAX_SIZE 72 diff --git a/ckb-auth-rs/src/ckb_auth_dl.rs b/ckb-auth-rs/src/ckb_auth_dl.rs index 0eb44e4..ce20061 100644 --- a/ckb-auth-rs/src/ckb_auth_dl.rs +++ b/ckb-auth-rs/src/ckb_auth_dl.rs @@ -22,13 +22,14 @@ type DLContext = CKBDLContext<[u8; 600 * 1024]>; const RISCV_PGSIZE: usize = 4096; type CkbAuthValidate = unsafe extern "C" fn( + prefilled_data: *const u8, auth_algorithm_id: u8, signature: *const u8, - signature_size: u32, + signature_size: usize, message: *const u8, - message_size: u32, + message_size: usize, pubkey_hash: *mut u8, - pubkey_hash_size: u32, + pubkey_hash_size: usize, ) -> i32; const EXPORTED_FUNC_NAME: &str = "ckb_auth_validate"; @@ -126,15 +127,17 @@ pub fn ckb_auth_dl( )?; let mut pub_key = id.pubkey_hash.clone(); + // TODO: let rc_code = unsafe { func( + 0 as *const u8, id.algorithm_id.clone().into(), signature.as_ptr(), - signature.len() as u32, + signature.len() as usize, message.as_ptr(), - message.len() as u32, + message.len() as usize, pub_key.as_mut_ptr(), - pub_key.len() as u32, + pub_key.len() as usize, ) }; diff --git a/docs/auth.md b/docs/auth.md index d7ab11b..9deb4a9 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -4,7 +4,7 @@ Omnilock](https://github.com/nervosnetwork/rfcs/pull/343). It's used for authentication by validating signature for different blockchains. ### Compile dependencies -Before using the following APIs, it is necessary to compile CKB contracts. +Before using the following APIs, it is necessary to compile CKB scripts. To compile, use the following commands in the root directory. The generated files will be located in the `build` directory. @@ -157,10 +157,9 @@ The whole length of the witness must be exactly 512. If there are any space left ### Low Level APIs -We define some low level APIs to auth (Authentication), which can be also used for other purposes. +We define some low level APIs to auth libraries, which can be also used for other purposes. It is based on the following idea: * [RFC: Swappable Signature Verification Protocol Spec](https://talk.nervos.org/t/rfc-swappable-signature-verification-protocol-spec/4802) -* [Ideas on chained locks](https://talk.nervos.org/t/ideas-on-chained-locks/5887) First we define the "EntryType": ```C @@ -176,24 +175,56 @@ typedef struct CkbEntryType { the cell which contains the code binary * entry_category - The entry to the algorithm. Now there are 2 categories: + The entry to the algorithm. Now there are 3 categories: - dynamic library - - spawn (activated after hardfork 2023) + - exec + - spawn (will be activated after hardfork 2023/2024) ### Entry Category: Dynamic Library -We define the follow functions when entry category is `dynamic library`: +We should export the follow function from dynamic library when entry category is +`dynamic library`: ```C -int ckb_auth_validate(uint8_t auth_algorithm_id, const uint8_t *signature, +int ckb_auth_load_prefilled_data(uint8_t auth_algorithm_id, void *prefilled_data, size_t *len); +``` +The first argument denotes the `algorithm_id` in `CkbAuthType`. The `prefilled` +and `len` will be described below. + +It gives a chance for different algorithms to load necessary data. For example, +a [precomputed table](https://github.com/bitcoin-core/secp256k1) is necessary +for secp256k1. So far, this is the only data should be loaded. In the full +lifetime of a CKB script, it is expected to only call this function once to +initialize the data. All latter invocations can share the same prefilled data. + +This function should support 2 invocation modes: + +- When `data` is NULL, and `len` is an address for a variable with value 0, the + function is expected to fill in the length required by the prefilled data into + the address denoted by `len`. This can be used by the caller to allocate + enough prefilled data for the library. + +- When `data` is not NULL, and the variable denoted by `len` contains enough +length, the function is expected to fill prefilled data in the memory buffer +started from data, and then fill the actual length of the prefilled data in `len` +field. The `len` value is suggested to be 1048576 for secp256k1. + +In either mode, a return value of 0 denoting success, other values denote +failures, and should immediately trigger a script failure. + +We should also export the following important function from dynamic library: +```C +int ckb_auth_validate(void *prefilled_data, uint8_t auth_algorithm_id, const uint8_t *signature, uint32_t signature_size, const uint8_t *message, uint32_t message_size, uint8_t *pubkey_hash, uint32_t pubkey_hash_size); ``` -The first argument denotes the `algorithm_id` in `CkbAuthType` described above. The arguments `signature` and -`pubkey_hash` are described in `key parameters` mentioned above. - -A valid dynamic library denoted by `EntryType` should provide `ckb_auth_validate` exported function. +The first argument denotes the `prefilled_data` returned from +`ckb_auth_load_prefilled_data`. The second argument denotes the `algorithm_id` in +`CkbAuthType` described above. The arguments `signature` and `pubkey_hash` are +described in `key parameters` mentioned above. A return value of 0 denoting +success, other values denote failures, and should immediately trigger a script +failure. -A dynamic library will create a cache in static memory for loading ckb-auth. This cache is initially set to 200k, and if adjustments are necessary, you can modify it by defining the macro CKB_AUTH_DL_BUFF_SIZE. However, it's important to note that the ckb-auth default is around 100k, and setting it too small may result in execution failure. CKB-VM allocates a maximum of 4M memory, and setting it too large may lead to insufficient memory. -By default, you can load up to 8 different ckb-auth libraries. If this is insufficient, you can modify it by defining CKB_AUTH_DL_MAX_COUNT. If you prefer not to use this feature, you can disable it by including CKB_AUTH_DISABLE_DYNAMIC_LIB. This will help conserve memory and reduce the size of the contract. +A valid dynamic library denoted by `EntryType` should provide +`ckb_auth_load_prefilled_data` and `ckb_auth_validate` exported functions. ### Entry Category: Spawn This category shares same arguments and behavior to dynamic library. It uses `spawn` instead of `dynamic library`. When @@ -219,9 +250,10 @@ The invocation method is the same as that of `Spawn`. ### High Level APIs The following API can combine the low level APIs together: ```C +int ckb_auth_load_prefilled_data(uint8_t auth_algorithm_id, void *prefilled_data, size_t *len); int ckb_auth(EntryType* entry, CkbAuthType *id, uint8_t *signature, uint32_t signature_size, const uint8_t *message32) ``` -Most of developers only need to use this function without knowing the low level APIs. +Most of developers only need to use these functions without knowing the low level APIs. ### Rust High Level APIs @@ -231,6 +263,7 @@ Dependencies name: `ckb-auth-rs` #### API Description ``` rust +pub fn ckb_auth_load_prefilled_data(auth_algorithm_id: u8, prefilled_data: &mut[u8]); pub fn ckb_auth( entry: &CkbEntryType, id: &CkbAuthType, @@ -247,3 +280,16 @@ pub fn ckb_auth( `message` : Participate in the message data of the signature. +#### Other Issues for High Level C APIs +A dynamic library will create a cache in static memory for loading ckb-auth. +This cache is initially set to 200k, and if adjustments are necessary, you can +modify it by defining the macro CKB_AUTH_DL_BUFF_SIZE in C. However, it's +important to note that the ckb-auth default is around 100k, and setting it too +small may result in execution failure. CKB-VM allocates a maximum of 4M memory, +and setting it too large may lead to insufficient memory. + +By default, you can load up to 8 different ckb-auth libraries. If this is +insufficient, you can modify it by defining CKB_AUTH_DL_MAX_COUNT. If you prefer +not to use this feature, you can disable it by including +CKB_AUTH_DISABLE_DYNAMIC_LIB. This will help conserve memory and reduce the size +of the script. diff --git a/tests/auth-c-lock/auth_c_lock.c b/tests/auth-c-lock/auth_c_lock.c index c4063b2..3b9e2f9 100644 --- a/tests/auth-c-lock/auth_c_lock.c +++ b/tests/auth-c-lock/auth_c_lock.c @@ -19,217 +19,220 @@ static int extract_witness_lock(uint8_t *witness, uint64_t len, mol_seg_t *lock_bytes_seg) { - if (len < 20) { - return CKB_INVALID_DATA; - } - uint32_t lock_length = *((uint32_t *)(&witness[16])); - if (len < 20 + lock_length) { - return CKB_INVALID_DATA; - } else { - lock_bytes_seg->ptr = &witness[20]; - lock_bytes_seg->size = lock_length; - } - return CKB_SUCCESS; + if (len < 20) { + return CKB_INVALID_DATA; + } + uint32_t lock_length = *((uint32_t *)(&witness[16])); + if (len < 20 + lock_length) { + return CKB_INVALID_DATA; + } else { + lock_bytes_seg->ptr = &witness[20]; + lock_bytes_seg->size = lock_length; + } + return CKB_SUCCESS; } int load_and_hash_witness(blake2b_state *ctx, size_t start, size_t index, size_t source, bool hash_length) { - uint8_t temp[ONE_BATCH_SIZE]; - uint64_t len = ONE_BATCH_SIZE; - int ret = ckb_load_witness(temp, &len, start, index, source); - if (ret != CKB_SUCCESS) { - return ret; - } - if (hash_length) { - blake2b_update(ctx, (char *)&len, sizeof(uint64_t)); - } - uint64_t offset = (len > ONE_BATCH_SIZE) ? ONE_BATCH_SIZE : len; - blake2b_update(ctx, temp, offset); - while (offset < len) { - uint64_t current_len = ONE_BATCH_SIZE; - ret = ckb_load_witness(temp, ¤t_len, start + offset, index, source); + uint8_t temp[ONE_BATCH_SIZE]; + uint64_t len = ONE_BATCH_SIZE; + int ret = ckb_load_witness(temp, &len, start, index, source); if (ret != CKB_SUCCESS) { - return ret; - } - uint64_t current_read = - (current_len > ONE_BATCH_SIZE) ? ONE_BATCH_SIZE : current_len; - blake2b_update(ctx, temp, current_read); - offset += current_read; - } - return CKB_SUCCESS; + return ret; + } + if (hash_length) { + blake2b_update(ctx, (char *)&len, sizeof(uint64_t)); + } + uint64_t offset = (len > ONE_BATCH_SIZE) ? ONE_BATCH_SIZE : len; + blake2b_update(ctx, temp, offset); + while (offset < len) { + uint64_t current_len = ONE_BATCH_SIZE; + ret = + ckb_load_witness(temp, ¤t_len, start + offset, index, source); + if (ret != CKB_SUCCESS) { + return ret; + } + uint64_t current_read = + (current_len > ONE_BATCH_SIZE) ? ONE_BATCH_SIZE : current_len; + blake2b_update(ctx, temp, current_read); + offset += current_read; + } + return CKB_SUCCESS; } int generate_sighash_all(uint8_t *msg, size_t msg_len) { - int ret; - uint64_t len = 0; - unsigned char temp[MAX_WITNESS_SIZE]; - uint64_t read_len = MAX_WITNESS_SIZE; - uint64_t witness_len = MAX_WITNESS_SIZE; - - if (msg_len < BLAKE2B_BLOCK_SIZE) { - return CKB_INVALID_DATA; - } - - /* Load witness of first input */ - ret = ckb_load_witness(temp, &read_len, 0, 0, CKB_SOURCE_GROUP_INPUT); - if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; - } - witness_len = read_len; - if (read_len > MAX_WITNESS_SIZE) { - read_len = MAX_WITNESS_SIZE; - } - - /* load signature */ - mol_seg_t lock_bytes_seg; - ret = extract_witness_lock(temp, read_len, &lock_bytes_seg); - if (ret != 0) { - return CKB_INVALID_DATA; - } - - /* Load tx hash */ - unsigned char tx_hash[BLAKE2B_BLOCK_SIZE]; - len = BLAKE2B_BLOCK_SIZE; - ret = ckb_load_tx_hash(tx_hash, &len, 0); - if (ret != CKB_SUCCESS) { - return ret; - } - if (len != BLAKE2B_BLOCK_SIZE) { - return CKB_INVALID_DATA; - } - - /* Prepare sign message */ - blake2b_state blake2b_ctx; - blake2b_init(&blake2b_ctx, BLAKE2B_BLOCK_SIZE); - blake2b_update(&blake2b_ctx, tx_hash, BLAKE2B_BLOCK_SIZE); - - /* Clear lock field to zero, then digest the first witness - * lock_bytes_seg.ptr actually points to the memory in temp buffer - * */ - memset((void *)lock_bytes_seg.ptr, 0, lock_bytes_seg.size); - blake2b_update(&blake2b_ctx, (char *)&witness_len, sizeof(uint64_t)); - blake2b_update(&blake2b_ctx, temp, read_len); - - // remaining of first witness - if (read_len < witness_len) { - ret = load_and_hash_witness(&blake2b_ctx, read_len, 0, - CKB_SOURCE_GROUP_INPUT, false); + int ret; + uint64_t len = 0; + unsigned char temp[MAX_WITNESS_SIZE]; + uint64_t read_len = MAX_WITNESS_SIZE; + uint64_t witness_len = MAX_WITNESS_SIZE; + + if (msg_len < BLAKE2B_BLOCK_SIZE) { + return CKB_INVALID_DATA; + } + + /* Load witness of first input */ + ret = ckb_load_witness(temp, &read_len, 0, 0, CKB_SOURCE_GROUP_INPUT); if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; + return CKB_INVALID_DATA; + } + witness_len = read_len; + if (read_len > MAX_WITNESS_SIZE) { + read_len = MAX_WITNESS_SIZE; } - } - // Digest same group witnesses - size_t i = 1; - while (1) { - ret = - load_and_hash_witness(&blake2b_ctx, 0, i, CKB_SOURCE_GROUP_INPUT, true); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; + /* load signature */ + mol_seg_t lock_bytes_seg; + ret = extract_witness_lock(temp, read_len, &lock_bytes_seg); + if (ret != 0) { + return CKB_INVALID_DATA; } + + /* Load tx hash */ + unsigned char tx_hash[BLAKE2B_BLOCK_SIZE]; + len = BLAKE2B_BLOCK_SIZE; + ret = ckb_load_tx_hash(tx_hash, &len, 0); if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; + return ret; + } + if (len != BLAKE2B_BLOCK_SIZE) { + return CKB_INVALID_DATA; } - i += 1; - } - // Digest witnesses that not covered by inputs - i = (size_t)ckb_calculate_inputs_len(); - while (1) { - ret = load_and_hash_witness(&blake2b_ctx, 0, i, CKB_SOURCE_INPUT, true); - if (ret == CKB_INDEX_OUT_OF_BOUND) { - break; + /* Prepare sign message */ + blake2b_state blake2b_ctx; + blake2b_init(&blake2b_ctx, BLAKE2B_BLOCK_SIZE); + blake2b_update(&blake2b_ctx, tx_hash, BLAKE2B_BLOCK_SIZE); + + /* Clear lock field to zero, then digest the first witness + * lock_bytes_seg.ptr actually points to the memory in temp buffer + * */ + memset((void *)lock_bytes_seg.ptr, 0, lock_bytes_seg.size); + blake2b_update(&blake2b_ctx, (char *)&witness_len, sizeof(uint64_t)); + blake2b_update(&blake2b_ctx, temp, read_len); + + // remaining of first witness + if (read_len < witness_len) { + ret = load_and_hash_witness(&blake2b_ctx, read_len, 0, + CKB_SOURCE_GROUP_INPUT, false); + if (ret != CKB_SUCCESS) { + return CKB_INVALID_DATA; + } } - if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; + + // Digest same group witnesses + size_t i = 1; + while (1) { + ret = load_and_hash_witness(&blake2b_ctx, 0, i, CKB_SOURCE_GROUP_INPUT, + true); + if (ret == CKB_INDEX_OUT_OF_BOUND) { + break; + } + if (ret != CKB_SUCCESS) { + return CKB_INVALID_DATA; + } + i += 1; } - i += 1; - } - blake2b_final(&blake2b_ctx, msg, BLAKE2B_BLOCK_SIZE); + // Digest witnesses that not covered by inputs + i = (size_t)ckb_calculate_inputs_len(); + while (1) { + ret = load_and_hash_witness(&blake2b_ctx, 0, i, CKB_SOURCE_INPUT, true); + if (ret == CKB_INDEX_OUT_OF_BOUND) { + break; + } + if (ret != CKB_SUCCESS) { + return CKB_INVALID_DATA; + } + i += 1; + } + + blake2b_final(&blake2b_ctx, msg, BLAKE2B_BLOCK_SIZE); - return 0; + return 0; } int main() { - int ret; - uint64_t len = 0; - unsigned char temp[TEMP_SIZE]; - - unsigned char script[SCRIPT_SIZE]; - len = SCRIPT_SIZE; - ret = ckb_load_script(script, &len, 0); - if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; - } - if (len > SCRIPT_SIZE) { - return CKB_INVALID_DATA; - } - mol_seg_t script_seg; - script_seg.ptr = (uint8_t *)script; - script_seg.size = len; - - if (MolReader_Script_verify(&script_seg, false) != MOL_OK) { - return CKB_INVALID_DATA; - } - - mol_seg_t args_seg = MolReader_Script_get_args(&script_seg); - mol_seg_t args_bytes_seg = MolReader_Bytes_raw_bytes(&args_seg); - if (args_bytes_seg.size != (21 + 32 + 1 + 1)) { - return CKB_INVALID_DATA; - } - - // Load the first witness, or the witness of the same index as the first input - // using current script. - uint64_t witness_len = MAX_WITNESS_SIZE; - ret = ckb_load_witness(temp, &witness_len, 0, 0, CKB_SOURCE_GROUP_INPUT); - if (ret != CKB_SUCCESS) { - return CKB_INVALID_DATA; - } - - mol_seg_t lock_bytes_seg; - ret = extract_witness_lock(temp, witness_len, &lock_bytes_seg); - if (ret != 0) { - return CKB_INVALID_DATA; - } - uint8_t msg32[32]; - ret = generate_sighash_all(msg32, 32); - if (ret != 0) - return CKB_INVALID_DATA; - - CkbEntryType entry; - memcpy(entry.code_hash, args_bytes_seg.ptr + 21, 32); - entry.hash_type = *(args_bytes_seg.ptr + 21 + 32); - entry.entry_category = *(args_bytes_seg.ptr + 21 + 32 + 1); - - CkbAuthType auth; - auth.algorithm_id = *args_bytes_seg.ptr; - memcpy(auth.content, args_bytes_seg.ptr + 1, 20); - - // Due to the variable length of certain Lock signs, - // it becomes difficult to determine the CKB sign message. - // Therefore, additional data needs to be appended later. - // It is necessary to remove this data at this point. - switch (auth.algorithm_id) { - case AuthAlgorithmIdRipple: - if (lock_bytes_seg.ptr[lock_bytes_seg.size - 1] >= lock_bytes_seg.size) { - return 102; // ERROR_INVALID_ARG - } - lock_bytes_seg.size -= lock_bytes_seg.ptr[lock_bytes_seg.size - 1]; - break; - default: - break; - } - - // ckb_auth can be invoked multiple times for different signatures. - // Here we use the same one to demo the usages. - ret = ckb_auth(&entry, &auth, lock_bytes_seg.ptr, lock_bytes_seg.size, msg32); - if (ret) { - return ret; - } - ret = ckb_auth(&entry, &auth, lock_bytes_seg.ptr, lock_bytes_seg.size, msg32); - if (ret) { - return ret; - } - return 0; + int ret; + uint64_t len = 0; + unsigned char temp[TEMP_SIZE]; + + unsigned char script[SCRIPT_SIZE]; + len = SCRIPT_SIZE; + ret = ckb_load_script(script, &len, 0); + if (ret != CKB_SUCCESS) { + return CKB_INVALID_DATA; + } + if (len > SCRIPT_SIZE) { + return CKB_INVALID_DATA; + } + mol_seg_t script_seg; + script_seg.ptr = (uint8_t *)script; + script_seg.size = len; + + if (MolReader_Script_verify(&script_seg, false) != MOL_OK) { + return CKB_INVALID_DATA; + } + + mol_seg_t args_seg = MolReader_Script_get_args(&script_seg); + mol_seg_t args_bytes_seg = MolReader_Bytes_raw_bytes(&args_seg); + if (args_bytes_seg.size != (21 + 32 + 1 + 1)) { + return CKB_INVALID_DATA; + } + + // Load the first witness, or the witness of the same index as the first + // input using current script. + uint64_t witness_len = MAX_WITNESS_SIZE; + ret = ckb_load_witness(temp, &witness_len, 0, 0, CKB_SOURCE_GROUP_INPUT); + if (ret != CKB_SUCCESS) { + return CKB_INVALID_DATA; + } + + mol_seg_t lock_bytes_seg; + ret = extract_witness_lock(temp, witness_len, &lock_bytes_seg); + if (ret != 0) { + return CKB_INVALID_DATA; + } + uint8_t msg32[32]; + ret = generate_sighash_all(msg32, 32); + if (ret != 0) return CKB_INVALID_DATA; + + CkbEntryType entry; + memcpy(entry.code_hash, args_bytes_seg.ptr + 21, 32); + entry.hash_type = *(args_bytes_seg.ptr + 21 + 32); + entry.entry_category = *(args_bytes_seg.ptr + 21 + 32 + 1); + + CkbAuthType auth; + auth.algorithm_id = *args_bytes_seg.ptr; + memcpy(auth.content, args_bytes_seg.ptr + 1, 20); + + // Due to the variable length of certain Lock signs, + // it becomes difficult to determine the CKB sign message. + // Therefore, additional data needs to be appended later. + // It is necessary to remove this data at this point. + switch (auth.algorithm_id) { + case AuthAlgorithmIdRipple: + if (lock_bytes_seg.ptr[lock_bytes_seg.size - 1] >= + lock_bytes_seg.size) { + return 102; // ERROR_INVALID_ARG + } + lock_bytes_seg.size -= lock_bytes_seg.ptr[lock_bytes_seg.size - 1]; + break; + default: + break; + } + + // ckb_auth can be invoked multiple times for different signatures. + // Here we use the same one to demo the usages. + ret = ckb_auth(0, &entry, &auth, lock_bytes_seg.ptr, lock_bytes_seg.size, + msg32); + if (ret) { + return ret; + } + ret = ckb_auth(0, &entry, &auth, lock_bytes_seg.ptr, lock_bytes_seg.size, + msg32); + if (ret) { + return ret; + } + return 0; }