diff options
author | Julian Hall <julian.hall@arm.com> | 2021-07-15 14:31:07 +0100 |
---|---|---|
committer | Gyorgy Szing <Gyorgy.Szing@arm.com> | 2021-10-06 00:48:23 +0200 |
commit | 0ed3d4538b2836fdac44886a6b4e7828cfb6bbe0 (patch) | |
tree | add78e0668a3a3abb185549cc9248daf62891695 | |
parent | 7bfb18e215aae621fe5f331b20c8ea0e329c42ef (diff) | |
download | trusted-services-0ed3d4538b2836fdac44886a6b4e7828cfb6bbe0.tar.gz |
Extend hash operation support
Remaining hash operations for aborting, verifying and cloning hash
operations added.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: If40483fe1be095145bb046d8d9fae4d0ad030931
6 files changed, 426 insertions, 27 deletions
diff --git a/components/service/crypto/client/psa/psa_hash.c b/components/service/crypto/client/psa/psa_hash.c index 55970ce3a..61bcf233f 100644 --- a/components/service/crypto/client/psa/psa_hash.c +++ b/components/service/crypto/client/psa/psa_hash.c @@ -179,20 +179,135 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, psa_status_t psa_hash_abort(psa_hash_operation_t *operation) { - return PSA_ERROR_NOT_SUPPORTED; + psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR; + struct ts_crypto_hash_abort_in req_msg; + size_t req_fixed_len = sizeof(struct ts_crypto_hash_abort_in); + size_t req_len = req_fixed_len; + + req_msg.op_handle = operation->handle; + + rpc_call_handle call_handle; + uint8_t *req_buf; + + call_handle = rpc_caller_begin(psa_crypto_client_instance.caller, &req_buf, req_len); + + if (call_handle) { + + uint8_t *resp_buf; + size_t resp_len; + int opstatus; + + memcpy(req_buf, &req_msg, req_fixed_len); + + psa_crypto_client_instance.rpc_status = + rpc_caller_invoke(psa_crypto_client_instance.caller, call_handle, + TS_CRYPTO_OPCODE_HASH_ABORT, &opstatus, &resp_buf, &resp_len); + + if (psa_crypto_client_instance.rpc_status == TS_RPC_CALL_ACCEPTED) psa_status = opstatus; + + rpc_caller_end(psa_crypto_client_instance.caller, call_handle); + } + + return psa_status; } psa_status_t psa_hash_verify(psa_hash_operation_t *operation, const uint8_t *hash, size_t hash_length) { - return PSA_ERROR_NOT_SUPPORTED; + psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR; + struct ts_crypto_hash_verify_in req_msg; + size_t req_fixed_len = sizeof(struct ts_crypto_hash_verify_in); + size_t req_len = req_fixed_len; + + req_msg.op_handle = operation->handle; + + /* Mandatory input data parameter */ + struct tlv_record data_record; + data_record.tag = TS_CRYPTO_HASH_VERIFY_IN_TAG_HASH; + data_record.length = hash_length; + data_record.value = hash; + req_len += tlv_required_space(data_record.length); + + rpc_call_handle call_handle; + uint8_t *req_buf; + + call_handle = rpc_caller_begin(psa_crypto_client_instance.caller, &req_buf, req_len); + + if (call_handle) { + + uint8_t *resp_buf; + size_t resp_len; + int opstatus; + struct tlv_iterator req_iter; + + memcpy(req_buf, &req_msg, req_fixed_len); + + tlv_iterator_begin(&req_iter, &req_buf[req_fixed_len], req_len - req_fixed_len); + tlv_encode(&req_iter, &data_record); + + psa_crypto_client_instance.rpc_status = + rpc_caller_invoke(psa_crypto_client_instance.caller, call_handle, + TS_CRYPTO_OPCODE_HASH_VERIFY, &opstatus, &resp_buf, &resp_len); + + if (psa_crypto_client_instance.rpc_status == TS_RPC_CALL_ACCEPTED) psa_status = opstatus; + + rpc_caller_end(psa_crypto_client_instance.caller, call_handle); + } + + return psa_status; } psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, psa_hash_operation_t *target_operation) { - return PSA_ERROR_NOT_SUPPORTED; + psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR; + struct ts_crypto_hash_clone_in req_msg; + size_t req_fixed_len = sizeof(struct ts_crypto_hash_clone_in); + size_t req_len = req_fixed_len; + + req_msg.source_op_handle = source_operation->handle; + + rpc_call_handle call_handle; + uint8_t *req_buf; + + call_handle = rpc_caller_begin(psa_crypto_client_instance.caller, &req_buf, req_len); + + if (call_handle) { + + uint8_t *resp_buf; + size_t resp_len; + int opstatus; + + memcpy(req_buf, &req_msg, req_fixed_len); + + psa_crypto_client_instance.rpc_status = + rpc_caller_invoke(psa_crypto_client_instance.caller, call_handle, + TS_CRYPTO_OPCODE_HASH_CLONE, &opstatus, &resp_buf, &resp_len); + + if (psa_crypto_client_instance.rpc_status == TS_RPC_CALL_ACCEPTED) { + + psa_status = opstatus; + + if (psa_status == PSA_SUCCESS) { + + if (resp_len >= sizeof(struct ts_crypto_hash_clone_out)) { + + struct ts_crypto_hash_clone_out resp_msg; + memcpy(&resp_msg, resp_buf, sizeof(struct ts_crypto_hash_clone_out)); + target_operation->handle = resp_msg.target_op_handle; + } + else { + /* Failed to decode response message */ + psa_status = PSA_ERROR_GENERIC_ERROR; + } + } + } + + rpc_caller_end(psa_crypto_client_instance.caller, call_handle); + } + + return psa_status; } psa_status_t psa_hash_compare(psa_algorithm_t alg, diff --git a/components/service/crypto/provider/extension/hash/hash_provider.c b/components/service/crypto/provider/extension/hash/hash_provider.c index 5a7ae3c28..2c560513f 100644 --- a/components/service/crypto/provider/extension/hash/hash_provider.c +++ b/components/service/crypto/provider/extension/hash/hash_provider.c @@ -14,12 +14,18 @@ static rpc_status_t hash_setup_handler(void *context, struct call_req* req); static rpc_status_t hash_update_handler(void *context, struct call_req* req); static rpc_status_t hash_finish_handler(void *context, struct call_req* req); +static rpc_status_t hash_abort_handler(void *context, struct call_req* req); +static rpc_status_t hash_verify_handler(void *context, struct call_req* req); +static rpc_status_t hash_clone_handler(void *context, struct call_req* req); /* Handler mapping table for service */ static const struct service_handler handler_table[] = { {TS_CRYPTO_OPCODE_HASH_SETUP, hash_setup_handler}, {TS_CRYPTO_OPCODE_HASH_UPDATE, hash_update_handler}, - {TS_CRYPTO_OPCODE_HASH_FINISH, hash_finish_handler} + {TS_CRYPTO_OPCODE_HASH_FINISH, hash_finish_handler}, + {TS_CRYPTO_OPCODE_HASH_ABORT, hash_abort_handler}, + {TS_CRYPTO_OPCODE_HASH_VERIFY, hash_verify_handler}, + {TS_CRYPTO_OPCODE_HASH_CLONE, hash_clone_handler} }; void hash_provider_init(struct hash_provider *context) @@ -123,6 +129,8 @@ static rpc_status_t hash_update_handler(void *context, struct call_req* req) if (rpc_status == TS_RPC_CALL_ACCEPTED) { + psa_status_t psa_status = PSA_ERROR_BAD_STATE; + struct crypto_context *crypto_context = crypto_context_pool_find(&this_instance->context_pool, CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), @@ -130,13 +138,10 @@ static rpc_status_t hash_update_handler(void *context, struct call_req* req) if (crypto_context) { - psa_status_t psa_status = psa_hash_update(&crypto_context->op.hash, data, data_len); - call_req_set_opstatus(req, psa_status); - } - else { - /* Requested context doesn't exist */ - rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE; + psa_status = psa_hash_update(&crypto_context->op.hash, data, data_len); } + + call_req_set_opstatus(req, psa_status); } return rpc_status; @@ -156,6 +161,8 @@ static rpc_status_t hash_finish_handler(void *context, struct call_req* req) if (rpc_status == TS_RPC_CALL_ACCEPTED) { + psa_status_t psa_status = PSA_ERROR_BAD_STATE; + struct crypto_context *crypto_context = crypto_context_pool_find(&this_instance->context_pool, CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), @@ -163,7 +170,6 @@ static rpc_status_t hash_finish_handler(void *context, struct call_req* req) if (crypto_context) { - psa_status_t psa_status; size_t hash_len; uint8_t hash[PSA_HASH_MAX_SIZE]; @@ -176,13 +182,130 @@ static rpc_status_t hash_finish_handler(void *context, struct call_req* req) } crypto_context_pool_free(&this_instance->context_pool, crypto_context); + } - call_req_set_opstatus(req, psa_status); + call_req_set_opstatus(req, psa_status); + } + + return rpc_status; +} + +static rpc_status_t hash_abort_handler(void *context, struct call_req* req) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED; + struct call_param_buf *req_buf = call_req_get_req_buf(req); + const struct hash_provider_serializer *serializer = get_serializer(context, req); + struct hash_provider *this_instance = (struct hash_provider*)context; + + uint32_t op_handle; + + if (serializer) + rpc_status = serializer->deserialize_hash_abort_req(req_buf, &op_handle); + + if (rpc_status == TS_RPC_CALL_ACCEPTED) { + + /* Return success if operation is no longer active and + * doesn't need aborting. + */ + psa_status_t psa_status = PSA_SUCCESS; + + struct crypto_context *crypto_context = + crypto_context_pool_find(&this_instance->context_pool, + CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), + op_handle); + + if (crypto_context) { + + psa_status = psa_hash_abort(&crypto_context->op.hash); + crypto_context_pool_free(&this_instance->context_pool, crypto_context); } - else { - /* Requested context doesn't exist */ - rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE; + + call_req_set_opstatus(req, psa_status); + } + + return rpc_status; +} + +static rpc_status_t hash_verify_handler(void *context, struct call_req* req) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED; + struct call_param_buf *req_buf = call_req_get_req_buf(req); + const struct hash_provider_serializer *serializer = get_serializer(context, req); + struct hash_provider *this_instance = (struct hash_provider*)context; + + uint32_t op_handle; + const uint8_t *hash; + size_t hash_len; + + if (serializer) + rpc_status = serializer->deserialize_hash_verify_req(req_buf, &op_handle, &hash, &hash_len); + + if (rpc_status == TS_RPC_CALL_ACCEPTED) { + + psa_status_t psa_status = PSA_ERROR_BAD_STATE; + + struct crypto_context *crypto_context = + crypto_context_pool_find(&this_instance->context_pool, + CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), + op_handle); + + if (crypto_context) { + + psa_status = psa_hash_verify(&crypto_context->op.hash, hash, hash_len); + } + + call_req_set_opstatus(req, psa_status); + } + + return rpc_status; +} + +static rpc_status_t hash_clone_handler(void *context, struct call_req* req) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED; + struct call_param_buf *req_buf = call_req_get_req_buf(req); + const struct hash_provider_serializer *serializer = get_serializer(context, req); + struct hash_provider *this_instance = (struct hash_provider*)context; + + uint32_t source_op_handle; + + if (serializer) + rpc_status = serializer->deserialize_hash_clone_req(req_buf, &source_op_handle); + + if (rpc_status == TS_RPC_CALL_ACCEPTED) { + + psa_status_t psa_status = PSA_ERROR_BAD_STATE; + + struct crypto_context *source_crypto_context = + crypto_context_pool_find(&this_instance->context_pool, + CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), + source_op_handle); + + if (source_crypto_context) { + + uint32_t target_op_handle; + + struct crypto_context *target_crypto_context = crypto_context_pool_alloc( + &this_instance->context_pool, + CRYPTO_CONTEXT_OP_ID_HASH, call_req_get_caller_id(req), + &target_op_handle); + + if (target_crypto_context) { + + target_crypto_context->op.hash = psa_hash_operation_init(); + + psa_status = psa_hash_clone(&source_crypto_context->op.hash, + &target_crypto_context->op.hash); + + if (psa_status == PSA_SUCCESS) { + + struct call_param_buf *resp_buf = call_req_get_resp_buf(req); + rpc_status = serializer->serialize_hash_clone_resp(resp_buf, target_op_handle); + } + } } + + call_req_set_opstatus(req, psa_status); } return rpc_status; diff --git a/components/service/crypto/provider/extension/hash/serializer/hash_provider_serializer.h b/components/service/crypto/provider/extension/hash/serializer/hash_provider_serializer.h index 5aa3c95e2..1611e6e63 100644 --- a/components/service/crypto/provider/extension/hash/serializer/hash_provider_serializer.h +++ b/components/service/crypto/provider/extension/hash/serializer/hash_provider_serializer.h @@ -35,6 +35,22 @@ struct hash_provider_serializer { rpc_status_t (*serialize_hash_finish_resp)(struct call_param_buf *resp_buf, const uint8_t *hash, size_t hash_len); + + /* Operation: hash_abort */ + rpc_status_t (*deserialize_hash_abort_req)(const struct call_param_buf *req_buf, + uint32_t *op_handle); + + /* Operation: hash_verify */ + rpc_status_t (*deserialize_hash_verify_req)(const struct call_param_buf *req_buf, + uint32_t *op_handle, + const uint8_t **hash, size_t *hash_len); + + /* Operation: hash_clone */ + rpc_status_t (*deserialize_hash_clone_req)(const struct call_param_buf *req_buf, + uint32_t *source_op_handle); + + rpc_status_t (*serialize_hash_clone_resp)(struct call_param_buf *resp_buf, + uint32_t target_op_handle); }; #endif /* HASH_PROVIDER_SERIALIZER_H */ diff --git a/components/service/crypto/provider/extension/hash/serializer/packed-c/packedc_hash_provider_serializer.c b/components/service/crypto/provider/extension/hash/serializer/packed-c/packedc_hash_provider_serializer.c index 2006f38fb..ac0c35e47 100644 --- a/components/service/crypto/provider/extension/hash/serializer/packed-c/packedc_hash_provider_serializer.c +++ b/components/service/crypto/provider/extension/hash/serializer/packed-c/packedc_hash_provider_serializer.c @@ -45,7 +45,6 @@ static rpc_status_t serialize_hash_setup_resp(struct call_param_buf *resp_buf, } return rpc_status; - } /* Operation: hash_update */ @@ -126,6 +125,99 @@ static rpc_status_t serialize_hash_finish_resp(struct call_param_buf *resp_buf, return rpc_status; } +/* Operation: hash_abort */ +static rpc_status_t deserialize_hash_abort_req(const struct call_param_buf *req_buf, + uint32_t *op_handle) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + struct ts_crypto_hash_abort_in recv_msg; + size_t expected_fixed_len = sizeof(struct ts_crypto_hash_abort_in); + + if (expected_fixed_len <= req_buf->data_len) { + + memcpy(&recv_msg, req_buf->data, expected_fixed_len); + *op_handle = recv_msg.op_handle; + rpc_status = TS_RPC_CALL_ACCEPTED; + } + + return rpc_status; +} + +/* Operation: hash_verify */ +static rpc_status_t deserialize_hash_verify_req(const struct call_param_buf *req_buf, + uint32_t *op_handle, + const uint8_t **hash, size_t *hash_len) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + struct ts_crypto_hash_verify_in recv_msg; + size_t expected_fixed_len = sizeof(struct ts_crypto_hash_verify_in); + + if (expected_fixed_len <= req_buf->data_len) { + + struct tlv_const_iterator req_iter; + struct tlv_record decoded_record; + + rpc_status = TS_RPC_CALL_ACCEPTED; + + memcpy(&recv_msg, req_buf->data, expected_fixed_len); + + *op_handle = recv_msg.op_handle; + + tlv_const_iterator_begin(&req_iter, + (uint8_t*)req_buf->data + expected_fixed_len, + req_buf->data_len - expected_fixed_len); + + if (tlv_find_decode(&req_iter, TS_CRYPTO_HASH_VERIFY_IN_TAG_HASH, &decoded_record)) { + + *hash = decoded_record.value; + *hash_len = decoded_record.length; + } + else { + /* Default to a zero length data */ + *hash_len = 0; + } + } + + return rpc_status; +} + +/* Operation: hash_clone */ +static rpc_status_t deserialize_hash_clone_req(const struct call_param_buf *req_buf, + uint32_t *source_op_handle) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_INVALID_REQ_BODY; + struct ts_crypto_hash_clone_in recv_msg; + size_t expected_fixed_len = sizeof(struct ts_crypto_hash_clone_in); + + if (expected_fixed_len <= req_buf->data_len) { + + memcpy(&recv_msg, req_buf->data, expected_fixed_len); + *source_op_handle = recv_msg.source_op_handle; + rpc_status = TS_RPC_CALL_ACCEPTED; + } + + return rpc_status; +} + +static rpc_status_t serialize_hash_clone_resp(struct call_param_buf *resp_buf, + uint32_t target_op_handle) +{ + rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL; + struct ts_crypto_hash_clone_out resp_msg; + size_t fixed_len = sizeof(struct ts_crypto_hash_clone_out); + + resp_msg.target_op_handle = target_op_handle; + + if (fixed_len <= resp_buf->size) { + + memcpy(resp_buf->data, &resp_msg, fixed_len); + resp_buf->data_len = fixed_len; + rpc_status = TS_RPC_CALL_ACCEPTED; + } + + return rpc_status; +} + /* Singleton method to provide access to the serializer instance */ const struct hash_provider_serializer *packedc_hash_provider_serializer_instance(void) { @@ -134,7 +226,11 @@ const struct hash_provider_serializer *packedc_hash_provider_serializer_instance serialize_hash_setup_resp, deserialize_hash_update_req, deserialize_hash_finish_req, - serialize_hash_finish_resp + serialize_hash_finish_resp, + deserialize_hash_abort_req, + deserialize_hash_verify_req, + deserialize_hash_clone_req, + serialize_hash_clone_resp }; return &instance; diff --git a/protocols/service/crypto/packed-c/hash.h b/protocols/service/crypto/packed-c/hash.h index 13ae3b9dd..36ed3f64e 100644 --- a/protocols/service/crypto/packed-c/hash.h +++ b/protocols/service/crypto/packed-c/hash.h @@ -15,7 +15,7 @@ */ -/******** +/**************************************** * hash_setup operation definition */ @@ -31,7 +31,7 @@ struct __attribute__ ((__packed__)) ts_crypto_hash_setup_out uint32_t op_handle; }; -/********* +/**************************************** * hash_update operation definition */ @@ -47,7 +47,7 @@ enum TS_CRYPTO_HASH_UPDATE_IN_TAG_DATA = 1 }; -/********* +/**************************************** * hash_finish operation definition */ @@ -63,5 +63,46 @@ enum TS_CRYPTO_HASH_FINISH_OUT_TAG_HASH = 1 }; +/**************************************** + * hash_abort operation definition + */ + +/* Mandatory fixed sized input parameters */ +struct __attribute__ ((__packed__)) ts_crypto_hash_abort_in +{ + uint32_t op_handle; +}; + +/**************************************** + * hash_verify operation definition + */ + +/* Mandatory fixed sized input parameters */ +struct __attribute__ ((__packed__)) ts_crypto_hash_verify_in +{ + uint32_t op_handle; +}; + +/* Variable length input parameter tags */ +enum +{ + TS_CRYPTO_HASH_VERIFY_IN_TAG_HASH = 1 +}; + +/**************************************** + * hash_clone operation definition + */ + +/* Mandatory fixed sized input parameters */ +struct __attribute__ ((__packed__)) ts_crypto_hash_clone_in +{ + uint32_t source_op_handle; +}; + +/* Mandatory fixed sized output parameters */ +struct __attribute__ ((__packed__)) ts_crypto_hash_clone_out +{ + uint32_t target_op_handle; +}; #endif /* TS_CRYPTO_HASH_H */ diff --git a/protocols/service/crypto/packed-c/opcodes.h b/protocols/service/crypto/packed-c/opcodes.h index fa23175da..b0ccff33e 100644 --- a/protocols/service/crypto/packed-c/opcodes.h +++ b/protocols/service/crypto/packed-c/opcodes.h @@ -9,8 +9,10 @@ /* C/C++ definition of crypto service opcodes */ -#define TS_CRYPTO_OPCODE_BASE (0x0100) + +/* Base operations */ #define TS_CRYPTO_OPCODE_NOP (0x0000) +#define TS_CRYPTO_OPCODE_BASE (0x0100) #define TS_CRYPTO_OPCODE_GENERATE_KEY (TS_CRYPTO_OPCODE_BASE + 1) #define TS_CRYPTO_OPCODE_DESTROY_KEY (TS_CRYPTO_OPCODE_BASE + 2) #define TS_CRYPTO_OPCODE_EXPORT_KEY (TS_CRYPTO_OPCODE_BASE + 5) @@ -21,11 +23,17 @@ #define TS_CRYPTO_OPCODE_ASYMMETRIC_DECRYPT (TS_CRYPTO_OPCODE_BASE + 10) #define TS_CRYPTO_OPCODE_ASYMMETRIC_ENCRYPT (TS_CRYPTO_OPCODE_BASE + 11) #define TS_CRYPTO_OPCODE_GENERATE_RANDOM (TS_CRYPTO_OPCODE_BASE + 12) -#define TS_CRYPTO_OPCODE_HASH_SETUP (TS_CRYPTO_OPCODE_BASE + 13) -#define TS_CRYPTO_OPCODE_HASH_UPDATE (TS_CRYPTO_OPCODE_BASE + 14) -#define TS_CRYPTO_OPCODE_HASH_FINISH (TS_CRYPTO_OPCODE_BASE + 15) -#define TS_CRYPTO_OPCODE_COPY_KEY (TS_CRYPTO_OPCODE_BASE + 16) -#define TS_CRYPTO_OPCODE_PURGE_KEY (TS_CRYPTO_OPCODE_BASE + 17) -#define TS_CRYPTO_OPCODE_GET_KEY_ATTRIBUTES (TS_CRYPTO_OPCODE_BASE + 18) +#define TS_CRYPTO_OPCODE_COPY_KEY (TS_CRYPTO_OPCODE_BASE + 13) +#define TS_CRYPTO_OPCODE_PURGE_KEY (TS_CRYPTO_OPCODE_BASE + 14) +#define TS_CRYPTO_OPCODE_GET_KEY_ATTRIBUTES (TS_CRYPTO_OPCODE_BASE + 15) + +/* Hash operations */ +#define TS_CRYPTO_OPCODE_HASH_BASE (0x0200) +#define TS_CRYPTO_OPCODE_HASH_SETUP (TS_CRYPTO_OPCODE_HASH_BASE + 1) +#define TS_CRYPTO_OPCODE_HASH_UPDATE (TS_CRYPTO_OPCODE_HASH_BASE + 2) +#define TS_CRYPTO_OPCODE_HASH_FINISH (TS_CRYPTO_OPCODE_HASH_BASE + 3) +#define TS_CRYPTO_OPCODE_HASH_ABORT (TS_CRYPTO_OPCODE_HASH_BASE + 4) +#define TS_CRYPTO_OPCODE_HASH_VERIFY (TS_CRYPTO_OPCODE_HASH_BASE + 5) +#define TS_CRYPTO_OPCODE_HASH_CLONE (TS_CRYPTO_OPCODE_HASH_BASE + 6) #endif /* TS_CRYPTO_OPCODES_H */ |