Add ECDSA raw/asn1 signature conversion methods 62/306962/3
authorDariusz Michaluk <d.michaluk@samsung.com>
Thu, 29 Feb 2024 16:29:06 +0000 (17:29 +0100)
committerDariusz Michaluk <d.michaluk@samsung.com>
Mon, 4 Mar 2024 16:27:26 +0000 (17:27 +0100)
Change-Id: I089c0a32b8e1f8b7bb1de063c96f531bd5946e59

CMakeLists.txt
packaging/key-manager-ta.spec
ta/CMakeLists.txt
ta/include/asn1.h [new file with mode: 0644]
ta/include/crypto_asymmetric.h
ta/src/asn1.c [new file with mode: 0644]
ta/src/cmd_exec.c
ta/src/crypto_asymmetric.c

index 94b851a829d5e1f4a74d50de5a239a65b7919c99..f905c1d9165dfb235238707613c6f0cecf1e7119 100644 (file)
@@ -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)
index af28929a035b6536d7d24daf9bc80dd4119ba774..0a97340a5b588012b312adb4248f8ed3c3b27891 100644 (file)
@@ -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
index b3cbb9477b89fba06a3ddc97a12ac3cdbf245951..61dba2f8a67128c39126b8f710434f8fe361bc4a 100644 (file)
@@ -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 (file)
index 0000000..06dd23f
--- /dev/null
@@ -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 <tee_internal_api.h>
+
+#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);
index fff8e8ddac99a11338f3b9ef59abf8ef71c3f888..dc0bde3e77cc84e100f26761f7b044051ccd1d85 100644 (file)
@@ -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 (file)
index 0000000..b788648
--- /dev/null
@@ -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 <string.h> //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
index 1d95e02a2ca393501c392b1f0a5652d28c07d975..6b7c606df54b636d46ebf182f3c03924adb546da 100644 (file)
@@ -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);
index b3002be5661cd2fc2cc3bf2a797ccac6d8c516d9..11b2a654549f6da672d6dc0dd90b358343ddb882 100644 (file)
@@ -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;