From: Dariusz Michaluk Date: Thu, 29 Feb 2024 16:29:06 +0000 (+0100) Subject: Add ECDSA raw/asn1 signature conversion methods X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3dfec5e4d7fdd062959bf6b074f6e8c05ec8049e;p=platform%2Fcore%2Fsecurity%2Ftrusted%2Fkey-manager-ta.git Add ECDSA raw/asn1 signature conversion methods Change-Id: I089c0a32b8e1f8b7bb1de063c96f531bd5946e59 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b851a..f905c1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,5 +44,9 @@ INCLUDE_DIRECTORIES( ADD_DEFINITIONS("-D${CMAKE_BUILD_TYPE}") +IF(FORMAT_RAW_ENABLED) +ADD_DEFINITIONS("-DFORMAT_RAW_ENABLED") +ENDIF(FORMAT_RAW_ENABLED) + ADD_SUBDIRECTORY(ta) ADD_SUBDIRECTORY(serialization) diff --git a/packaging/key-manager-ta.spec b/packaging/key-manager-ta.spec index af28929..0a97340 100644 --- a/packaging/key-manager-ta.spec +++ b/packaging/key-manager-ta.spec @@ -90,7 +90,8 @@ cd build -DINCLUDE_DIR=%{include_dir} \ -DLIB_DIR=%{lib_dir} \ -DTA_NAME=%{ta_name} \ - -DSECRET_KEY_ENABLE=%{secret_ta_key_enable} + -DSECRET_KEY_ENABLE=%{secret_ta_key_enable} \ + -DFORMAT_RAW_ENABLED=%{?format_raw:%format_raw}%{!?format_raw:OFF} make %{?jobs:-j%jobs} %install diff --git a/ta/CMakeLists.txt b/ta/CMakeLists.txt index b3cbb94..61dba2f 100644 --- a/ta/CMakeLists.txt +++ b/ta/CMakeLists.txt @@ -36,6 +36,7 @@ SET(KEY_MANAGER_TA_SOURCES ${KEY_MANAGER_TA_PATH}/src/internal.c ${KEY_MANAGER_TA_PATH}/src/km_ta.c ${KEY_MANAGER_TA_PATH}/src/base64.c + ${KEY_MANAGER_TA_PATH}/src/asn1.c ${KEY_MANAGER_TA_COMMON_PATH}/src/log.c ${KEY_MANAGER_TA_SERIALIZATION_PATH}/src/km_serialization.c ) diff --git a/ta/include/asn1.h b/ta/include/asn1.h new file mode 100644 index 0000000..06dd23f --- /dev/null +++ b/ta/include/asn1.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include + +#pragma once + +TEE_Result ecdsa_asn1_to_raw(void *sig, uint32_t sig_len, void **out, uint32_t *out_len); +TEE_Result ecdsa_raw_to_asn1(void *sig, uint32_t sig_len, void **out, uint32_t *out_len); diff --git a/ta/include/crypto_asymmetric.h b/ta/include/crypto_asymmetric.h index fff8e8d..dc0bde3 100644 --- a/ta/include/crypto_asymmetric.h +++ b/ta/include/crypto_asymmetric.h @@ -32,9 +32,9 @@ TEE_Result KM_AsymmetricDecrypt(TEE_OperationHandle hndl, void *input, uint32_t void *output, uint32_t *output_size); TEE_Result KM_AsymmetricSign(TEE_OperationHandle hndl, void *digest, uint32_t digest_size, - void *signature, uint32_t *sig_size); + void *signature, uint32_t *sig_size, bool raw); TEE_Result KM_AsymmetricVerify(TEE_OperationHandle hndl, void *digest, uint32_t digest_size, - void *signature, uint32_t sig_size); + void *signature, uint32_t sig_size, bool raw); #endif // __CRYPTO_ASYMMETRIC_H__ diff --git a/ta/src/asn1.c b/ta/src/asn1.c new file mode 100644 index 0000000..b788648 --- /dev/null +++ b/ta/src/asn1.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include //memcpy + +#include "asn1.h" + +/* + * Compute ASN.1 encoded length for the provided integer. The ASN.1 + * encoding is signed, so its leading bit must have value 0; it must + * also be of minimal length (so leading bytes of value 0 must be + * removed, except if that would contradict the rule about the sign + * bit). + */ + +static size_t asn1_int_length(const unsigned char *x, size_t xlen) +{ + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + if (xlen == 0 || *x >= 0x80) { + xlen ++; + } + return xlen; +} + +TEE_Result ecdsa_raw_to_asn1(void *sig, uint32_t sig_len, void **out, uint32_t *out_len) +{ + TEE_Result ret = TEE_SUCCESS; + unsigned char *buf; + uint32_t hlen, rlen, slen, zlen, off; + + /* + * Internal buffer is large enough to accommodate a signature + * such that r and s fit on 125 bytes each (signed encoding), + * meaning a curve order of up to 999 bits. This is the limit + * that ensures "simple" length encodings. + */ + uint8_t *tmp = NULL; + tmp = (uint8_t *) TEE_Malloc(257, 0); + if (tmp == NULL) { + ret = TEE_ERROR_OUT_OF_MEMORY; + goto clean; + } + + buf = sig; + if ((sig_len & 1) != 0) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + + /* + * Compute lengths for the two integers. + */ + hlen = sig_len >> 1; + rlen = asn1_int_length(buf, hlen); + slen = asn1_int_length(buf + hlen, hlen); + if (rlen > 125 || slen > 125) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + + /* + * SEQUENCE header. + */ + tmp[0] = 0x30; + zlen = rlen + slen + 4; + if (zlen >= 0x80) { + tmp[1] = 0x81; + tmp[2] = zlen; + off = 3; + } else { + tmp[1] = zlen; + off = 2; + } + + /* + * First INTEGER (r). + */ + tmp[off ++] = 0x02; + tmp[off ++] = rlen; + if (rlen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf, hlen); + } else { + memcpy(tmp + off, buf + hlen - rlen, rlen); + } + off += rlen; + + /* + * Second INTEGER (s). + */ + tmp[off ++] = 0x02; + tmp[off ++] = slen; + if (slen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf + hlen, hlen); + } else { + memcpy(tmp + off, buf + sig_len - slen, slen); + } + off += slen; + + /* + * Return ASN.1 signature. + */ + *out = TEE_Realloc(tmp, off); + *out_len = off; + +clean: + if (ret != TEE_SUCCESS) + TEE_Free(tmp); + + return ret; +} + +TEE_Result ecdsa_asn1_to_raw(void *sig, uint32_t sig_len, void **out, uint32_t *out_len) +{ + TEE_Result ret = TEE_SUCCESS; + unsigned char *buf, *r, *s; + uint32_t zlen, rlen, slen, off; + + /* + * Signature format is a SEQUENCE of two INTEGER values. We + * support only integers of less than 127 bytes each (signed + * encoding) so the resulting raw signature will have length + * at most 254 bytes. + */ + + uint8_t *tmp = NULL; + tmp = (uint8_t *) TEE_Malloc(254, 0); + if (tmp == NULL) { + ret = TEE_ERROR_OUT_OF_MEMORY; + goto clean; + } + + buf = sig; + if (sig_len < 8) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + + /* + * First byte is SEQUENCE tag. + */ + if (buf[0] != 0x30) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + + /* + * The SEQUENCE length will be encoded over one or two bytes. We + * limit the total SEQUENCE contents to 255 bytes, because it + * makes things simpler; this is enough for subgroup orders up + * to 999 bits. + */ + zlen = buf[1]; + if (zlen > 0x80) { + if (zlen != 0x81) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + zlen = buf[2]; + if (zlen != sig_len - 3) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + off = 3; + } else { + if (zlen != sig_len - 2) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + off = 2; + } + + /* + * First INTEGER (r). + */ + if (buf[off ++] != 0x02) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + rlen = buf[off ++]; + if (rlen >= 0x80) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + r = buf + off; + off += rlen; + + /* + * Second INTEGER (s). + */ + if (off + 2 > sig_len) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + if (buf[off ++] != 0x02) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + slen = buf[off ++]; + if (slen >= 0x80 || slen != sig_len - off) { + ret = TEE_ERROR_GENERIC; + goto clean; + } + s = buf + off; + + /* + * Removing leading zeros from r and s. + */ + while (rlen > 0 && *r == 0) { + rlen --; + r ++; + } + while (slen > 0 && *s == 0) { + slen --; + s ++; + } + + /* + * Compute common length for the two integers, then copy integers + * into the temporary buffer, and finally copy it back over the + * signature buffer. + */ + zlen = rlen > slen ? rlen : slen; + sig_len = zlen << 1; + + memset(tmp, 0, sig_len); + memcpy(tmp + zlen - rlen, r, rlen); + memcpy(tmp + sig_len - slen, s, slen); + *out = TEE_Realloc(tmp, sig_len); + *out_len = sig_len; + +clean: + if (ret != TEE_SUCCESS) + TEE_Free(tmp); + + return ret; +} \ No newline at end of file diff --git a/ta/src/cmd_exec.c b/ta/src/cmd_exec.c index 1d95e02..6b7c606 100644 --- a/ta/src/cmd_exec.c +++ b/ta/src/cmd_exec.c @@ -2217,6 +2217,7 @@ TEE_Result KM_ExecCmdSign(TEE_Param param[4]) uint32_t digestSize = 0; uint32_t algo = 0; uint32_t key_size_in_bits = 0; + bool raw = false; void *in_buffer = param[1].memref.buffer; void *out_buffer = param[2].memref.buffer; @@ -2282,8 +2283,16 @@ TEE_Result KM_ExecCmdSign(TEE_Param param[4]) } outSize = KM_MaxObjectSizeBytes(&info); - if (param[0].value.a == ALGO_ECDSA_SV) - outSize *= 2; + if (param[0].value.a == ALGO_ECDSA_SV) { + // ECDSA-Sig-Value ::= SEQUENCE { + // r INTEGER, + // s INTEGER + // } + outSize = 8 + outSize *2; + #ifdef FORMAT_RAW_ENABLED + raw = true; + #endif + } out = TEE_Malloc(outSize, 0); if (out == NULL) { @@ -2293,7 +2302,7 @@ TEE_Result KM_ExecCmdSign(TEE_Param param[4]) } // create signature - ret = KM_AsymmetricSign(operation, digest, digestSize, out, &outSize); + ret = KM_AsymmetricSign(operation, digest, digestSize, out, &outSize, raw); if (TEE_SUCCESS != ret) { LOG("Failed to sign digested message"); goto clean; @@ -2332,6 +2341,7 @@ TEE_Result KM_ExecCmdVerify(TEE_Param param[4]) uint32_t digestSize = 0; uint32_t algo = 0; uint32_t key_size_in_bits = 0; + bool raw = false; void *in_buffer = param[1].memref.buffer; uint32_t in_size_guard = param[1].memref.size; @@ -2400,9 +2410,15 @@ TEE_Result KM_ExecCmdVerify(TEE_Param param[4]) goto clean; } + if (param[0].value.a == ALGO_ECDSA_SV) { + #ifdef FORMAT_RAW_ENABLED + raw = true; + #endif + } + // no check over here - return anything that TEE_AsymmetricVerifyDigest gives us // return code will contain information whether the signature was correct or not - ret = KM_AsymmetricVerify(operation, digest, digestSize, signature.data, signature.data_size); + ret = KM_AsymmetricVerify(operation, digest, digestSize, signature.data, signature.data_size, raw); clean: TEE_CloseObject(key); diff --git a/ta/src/crypto_asymmetric.c b/ta/src/crypto_asymmetric.c index b3002be..11b2a65 100644 --- a/ta/src/crypto_asymmetric.c +++ b/ta/src/crypto_asymmetric.c @@ -20,6 +20,7 @@ * @brief Implementation of Global Platform Internal API usage (asymmetric operations) */ +#include "asn1.h" #include "crypto_asymmetric.h" #include "log.h" @@ -52,9 +53,11 @@ TEE_Result KM_AsymmetricDecrypt(TEE_OperationHandle hndl, void *input, uint32_t } TEE_Result KM_AsymmetricSign(TEE_OperationHandle hndl, void *digest, uint32_t digest_size, - void *signature, uint32_t *sig_size) + void *signature, uint32_t *sig_size, bool raw) { TEE_Result ret = TEE_SUCCESS; + void *out; + uint32_t out_len; ret = TEE_AsymmetricSignDigest(hndl, NULL, 0, digest, digest_size, signature, sig_size); if (TEE_SUCCESS != ret) { @@ -62,15 +65,70 @@ TEE_Result KM_AsymmetricSign(TEE_OperationHandle hndl, void *digest, uint32_t di return ret; } +#if 0 + if (raw) { + void *in; + uint32_t in_len; + ret = ecdsa_asn1_to_raw(signature, *sig_size, &in, &in_len); + if (TEE_SUCCESS != ret) { + LOG("ecdsa_asn1_to_raw has failed with=%x.", ret); + return ret; + } + signature = in; + *sig_size = in_len; + } +#endif + + if (raw) { + ret = ecdsa_raw_to_asn1(signature, *sig_size, &out, &out_len); + if (TEE_SUCCESS != ret) { + LOG("ecdsa_raw_to_asn1 has failed with=%x.", ret); + return ret; + } + memcpy(signature, out, out_len); + TEE_Free(out); + *sig_size = out_len; + } + return ret; } TEE_Result KM_AsymmetricVerify(TEE_OperationHandle hndl, void *digest, uint32_t digest_size, - void *signature, uint32_t sig_size) + void *signature, uint32_t sig_size, bool raw) { TEE_Result ret = TEE_SUCCESS; + void *out; + uint32_t out_len; + + if (raw) { + ret = ecdsa_asn1_to_raw(signature, sig_size, &out, &out_len); + if (TEE_SUCCESS != ret) { + LOG("ecdsa_asn1_to_raw has failed with=%x.", ret); + return ret; + } + } + +#if 0 + if (raw) { + void *in; + uint32_t in_len; + ret = ecdsa_raw_to_asn1(out, out_len, &in, &in_len); + if (TEE_SUCCESS != ret) { + LOG("ecdsa_raw_to_asn1 has failed with=%x.", ret); + return ret; + } + out = in; + out_len = in_len; + } +#endif + + if (raw) { + ret = TEE_AsymmetricVerifyDigest(hndl, NULL, 0, digest, digest_size, out, out_len); + TEE_Free(out); + } else { + ret = TEE_AsymmetricVerifyDigest(hndl, NULL, 0, digest, digest_size, signature, sig_size); + } - ret = TEE_AsymmetricVerifyDigest(hndl, NULL, 0, digest, digest_size, signature, sig_size); if (TEE_SUCCESS != ret && TEE_ERROR_SIGNATURE_INVALID != ret) { LOG("TEE_AsymmetricVerifyDigest has failed with=%x.", ret); return ret;