core: crypto: add support for SM2 KEP

Adds SM2 Key Exchange Protocol [1] using LibTomCrypt. The TA interface
complies with the GlobalPlatform TEE Internal Core API version 1.2.

SM2 KEP is enabled with CFG_CRYPTO_SM2_KEP=y (default y) wich currently
requires that CFG_CRYPTOLIB_NAME=tomcrypt. An Mbed TLS implementation
could be added later if needed.

[1] http://www.gmbz.org.cn/main/postDetail.html?id=20180724110812

Signed-off-by: Jerome Forissier <jerome@forissier.org>
Acked-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/core/crypto.mk b/core/crypto.mk
index 0fdbac6..4727045 100644
--- a/core/crypto.mk
+++ b/core/crypto.mk
@@ -42,6 +42,7 @@
 ifeq ($(CFG_CRYPTOLIB_NAME),tomcrypt)
 CFG_CRYPTO_SM2_PKE ?= y
 CFG_CRYPTO_SM2_DSA ?= y
+CFG_CRYPTO_SM2_KEP ?= y
 endif
 ifeq ($(CFG_CRYPTOLIB_NAME)-$(CFG_CRYPTO_SM2_PKE),mbedtls-y)
 $(error Error: CFG_CRYPTO_SM2_PKE=y requires CFG_CRYPTOLIB_NAME=tomcrypt)
@@ -49,6 +50,9 @@
 ifeq ($(CFG_CRYPTOLIB_NAME)-$(CFG_CRYPTO_SM2_DSA),mbedtls-y)
 $(error Error: CFG_CRYPTO_SM2_DSA=y requires CFG_CRYPTOLIB_NAME=tomcrypt)
 endif
+ifeq ($(CFG_CRYPTOLIB_NAME)-$(CFG_CRYPTO_SM2_KEP),mbedtls-y)
+$(error Error: CFG_CRYPTO_SM2_KEP=y requires CFG_CRYPTOLIB_NAME=tomcrypt)
+endif
 
 # Authenticated encryption
 CFG_CRYPTO_CCM ?= y
@@ -142,6 +146,7 @@
 # SM2 is Elliptic Curve Cryptography, it uses some generic ECC functions
 $(eval $(call cryp-dep-one, SM2_PKE, ECC))
 $(eval $(call cryp-dep-one, SM2_DSA, ECC))
+$(eval $(call cryp-dep-one, SM2_KEP, ECC))
 
 ###############################################################
 # libtomcrypt (LTC) specifics, phase #1
@@ -174,6 +179,7 @@
 core-ltc-vars += SIZE_OPTIMIZATION
 core-ltc-vars += SM2_PKE
 core-ltc-vars += SM2_DSA
+core-ltc-vars += SM2_KEP
 # Assigned selected CFG_CRYPTO_xxx as _CFG_CORE_LTC_xxx
 $(foreach v, $(core-ltc-vars), $(eval _CFG_CORE_LTC_$(v) := $(CFG_CRYPTO_$(v))))
 _CFG_CORE_LTC_MPI := $(CFG_CORE_MBEDTLS_MPI)
diff --git a/core/crypto/crypto.c b/core/crypto/crypto.c
index 578514d..7a4f22c 100644
--- a/core/crypto/crypto.c
+++ b/core/crypto/crypto.c
@@ -733,3 +733,16 @@
 	return TEE_ERROR_NOT_IMPLEMENTED;
 }
 #endif /* !CFG_CRYPTO_SM2_DSA */
+#if !defined(CFG_CRYPTO_SM2_KEP)
+TEE_Result crypto_acipher_sm2_kep_derive(struct ecc_keypair *my_key __unused,
+					 struct ecc_keypair *my_eph_key
+								__unused,
+					 struct ecc_public_key *peer_key
+								__unused,
+					 struct ecc_public_key *peer_eph_key
+								__unused,
+					 struct sm2_kep_parms *p __unused)
+{
+	return TEE_ERROR_NOT_IMPLEMENTED;
+}
+#endif
diff --git a/core/include/crypto/crypto.h b/core/include/crypto/crypto.h
index e913875..7f9b14a 100644
--- a/core/include/crypto/crypto.h
+++ b/core/include/crypto/crypto.h
@@ -252,6 +252,26 @@
 					 const uint8_t *msg, size_t msg_len,
 					 const uint8_t *sig, size_t sig_len);
 
+struct sm2_kep_parms {
+	uint8_t *out;
+	size_t out_len;
+	bool is_initiator;
+	const uint8_t *initiator_id;
+	size_t initiator_id_len;
+	const uint8_t *responder_id;
+	size_t responder_id_len;
+	const uint8_t *conf_in;
+	size_t conf_in_len;
+	uint8_t *conf_out;
+	size_t conf_out_len;
+};
+
+TEE_Result crypto_acipher_sm2_kep_derive(struct ecc_keypair *my_key,
+					 struct ecc_keypair *my_eph_key,
+					 struct ecc_public_key *peer_key,
+					 struct ecc_public_key *peer_eph_key,
+					 struct sm2_kep_parms *p);
+
 /*
  * Verifies a SHA-256 hash, doesn't require crypto_init() to be called in
  * advance and has as few dependencies as possible.
diff --git a/core/lib/libtomcrypt/ecc.c b/core/lib/libtomcrypt/ecc.c
index 57632d2..97f6b39 100644
--- a/core/lib/libtomcrypt/ecc.c
+++ b/core/lib/libtomcrypt/ecc.c
@@ -129,7 +129,8 @@
 		size_bytes = 32;
 		name = "SM2";
 		if ((algo != 0) && (algo != TEE_ALG_SM2_PKE) &&
-		    (algo != TEE_ALG_SM2_DSA_SM3))
+		    (algo != TEE_ALG_SM2_DSA_SM3) &&
+		    (algo != TEE_ALG_SM2_KEP))
 			return TEE_ERROR_BAD_PARAMETERS;
 		break;
 	default:
diff --git a/core/lib/libtomcrypt/sm2-kep.c b/core/lib/libtomcrypt/sm2-kep.c
index 8e47ebd..12a25f8 100644
--- a/core/lib/libtomcrypt/sm2-kep.c
+++ b/core/lib/libtomcrypt/sm2-kep.c
@@ -440,4 +440,3 @@
 	ecc_free(&ltc_my_key);
 	return res;
 }
-
diff --git a/core/tee/tee_svc_cryp.c b/core/tee/tee_svc_cryp.c
index 17d8dec..00c85de 100644
--- a/core/tee/tee_svc_cryp.c
+++ b/core/tee/tee_svc_cryp.c
@@ -339,7 +339,7 @@
 
 	{
 	.attr_id = TEE_ATTR_ECC_CURVE,
-	.flags = TEE_TYPE_ATTR_REQUIRED,
+	.flags = TEE_TYPE_ATTR_REQUIRED | TEE_TYPE_ATTR_SIZE_INDICATOR,
 	.ops_index = ATTR_OPS_INDEX_VALUE,
 	RAW_DATA(struct ecc_public_key, curve)
 	},
@@ -502,6 +502,14 @@
 	PROP(TEE_TYPE_SM2_PKE_KEYPAIR, 1, 256, 256,
 	     sizeof(struct ecc_keypair),
 	     tee_cryp_obj_ecc_keypair_attrs),
+
+	PROP(TEE_TYPE_SM2_KEP_PUBLIC_KEY, 1, 256, 256,
+	     sizeof(struct ecc_public_key),
+	     tee_cryp_obj_ecc_pub_key_attrs),
+
+	PROP(TEE_TYPE_SM2_KEP_KEYPAIR, 1, 256, 256,
+	     sizeof(struct ecc_keypair),
+	     tee_cryp_obj_ecc_keypair_attrs),
 };
 
 struct attr_ops {
@@ -1159,6 +1167,9 @@
 		} else if (o->info.objectType == TEE_TYPE_SM2_PKE_PUBLIC_KEY) {
 			if (src->info.objectType != TEE_TYPE_SM2_PKE_KEYPAIR)
 				return TEE_ERROR_BAD_PARAMETERS;
+		} else if (o->info.objectType == TEE_TYPE_SM2_KEP_PUBLIC_KEY) {
+			if (src->info.objectType != TEE_TYPE_SM2_KEP_KEYPAIR)
+				return TEE_ERROR_BAD_PARAMETERS;
 		} else {
 			return TEE_ERROR_BAD_PARAMETERS;
 		}
@@ -1249,6 +1260,7 @@
 	case TEE_TYPE_ECDH_PUBLIC_KEY:
 	case TEE_TYPE_SM2_DSA_PUBLIC_KEY:
 	case TEE_TYPE_SM2_PKE_PUBLIC_KEY:
+	case TEE_TYPE_SM2_KEP_PUBLIC_KEY:
 		res = crypto_acipher_alloc_ecc_public_key(o->attr,
 							  max_key_size);
 		break;
@@ -1256,6 +1268,7 @@
 	case TEE_TYPE_ECDH_KEYPAIR:
 	case TEE_TYPE_SM2_DSA_KEYPAIR:
 	case TEE_TYPE_SM2_PKE_KEYPAIR:
+	case TEE_TYPE_SM2_KEP_KEYPAIR:
 		res = crypto_acipher_alloc_ecc_keypair(o->attr, max_key_size);
 		break;
 	default:
@@ -2052,6 +2065,12 @@
 		else
 			req_key_type = TEE_TYPE_SM2_DSA_KEYPAIR;
 		break;
+#if defined(CFG_CRYPTO_SM2_KEP)
+	case TEE_MAIN_ALGO_SM2_KEP:
+		req_key_type = TEE_TYPE_SM2_KEP_KEYPAIR;
+		req_key_type2 = TEE_TYPE_SM2_KEP_PUBLIC_KEY;
+		break;
+#endif
 #if defined(CFG_CRYPTO_HKDF)
 	case TEE_MAIN_ALGO_HKDF:
 		req_key_type = TEE_TYPE_HKDF_IKM;
@@ -2174,8 +2193,13 @@
 			res = TEE_ERROR_BAD_PARAMETERS;
 		break;
 	case TEE_OPERATION_KEY_DERIVATION:
-		if (key1 == 0 || key2 != 0)
-			res = TEE_ERROR_BAD_PARAMETERS;
+		if (algo == TEE_ALG_SM2_KEP) {
+			if (key1 == 0 || key2 == 0)
+				res = TEE_ERROR_BAD_PARAMETERS;
+		} else {
+			if (key1 == 0 || key2 != 0)
+				res = TEE_ERROR_BAD_PARAMETERS;
+		}
 		break;
 	default:
 		res = TEE_ERROR_NOT_SUPPORTED;
@@ -2756,6 +2780,110 @@
 }
 #endif
 
+#if defined(CFG_CRYPTO_SM2_KEP)
+static TEE_Result get_sm2_kep_params(const TEE_Attribute *params,
+				     uint32_t param_count,
+				     struct ecc_public_key *peer_key,
+				     struct ecc_public_key *peer_eph_key,
+				     struct sm2_kep_parms *kep_parms)
+{
+	TEE_Result res = TEE_ERROR_GENERIC;
+	size_t n;
+	enum {
+		IS_INITIATOR,
+		PEER_KEY_X,
+		PEER_KEY_Y,
+		PEER_EPH_KEY_X,
+		PEER_EPH_KEY_Y,
+		INITIATOR_ID,
+		RESPONDER_ID,
+	};
+	uint8_t mandatory = BIT(IS_INITIATOR) | BIT(PEER_KEY_X) |
+		BIT(PEER_KEY_Y) | BIT(PEER_EPH_KEY_X) | BIT(PEER_EPH_KEY_Y) |
+		BIT(INITIATOR_ID) | BIT(RESPONDER_ID);
+	uint8_t found = 0;
+
+	res = crypto_acipher_alloc_ecc_public_key(peer_key, 256);
+	if (res)
+		goto out;
+
+	res = crypto_acipher_alloc_ecc_public_key(peer_eph_key, 256);
+	if (res)
+		goto out;
+
+	peer_key->curve = TEE_ECC_CURVE_SM2;
+	peer_eph_key->curve = TEE_ECC_CURVE_SM2;
+
+	for (n = 0; n < param_count; n++) {
+		const TEE_Attribute *p = &params[n];
+
+		switch (p->attributeID) {
+		case TEE_ATTR_SM2_KEP_USER:
+			kep_parms->is_initiator = !p->content.value.a;
+			found |= BIT(IS_INITIATOR);
+			break;
+		case TEE_ATTR_ECC_PUBLIC_VALUE_X:
+			crypto_bignum_bin2bn(p->content.ref.buffer,
+					     p->content.ref.length,
+					     peer_key->x);
+			found |= BIT(PEER_KEY_X);
+			break;
+		case TEE_ATTR_ECC_PUBLIC_VALUE_Y:
+			crypto_bignum_bin2bn(p->content.ref.buffer,
+					     p->content.ref.length,
+					     peer_key->y);
+			found |= BIT(PEER_KEY_Y);
+			break;
+		case TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_X:
+			crypto_bignum_bin2bn(p->content.ref.buffer,
+					     p->content.ref.length,
+					     peer_eph_key->x);
+			found |= BIT(PEER_EPH_KEY_X);
+			break;
+		case TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_Y:
+			crypto_bignum_bin2bn(p->content.ref.buffer,
+					     p->content.ref.length,
+					     peer_eph_key->y);
+			found |= BIT(PEER_EPH_KEY_Y);
+			break;
+		case TEE_ATTR_SM2_ID_INITIATOR:
+			kep_parms->initiator_id = p->content.ref.buffer;
+			kep_parms->initiator_id_len = p->content.ref.length;
+			found |= BIT(INITIATOR_ID);
+			break;
+		case TEE_ATTR_SM2_ID_RESPONDER:
+			kep_parms->responder_id = p->content.ref.buffer;
+			kep_parms->responder_id_len = p->content.ref.length;
+			found |= BIT(RESPONDER_ID);
+			break;
+		case TEE_ATTR_SM2_KEP_CONFIRMATION_IN:
+			kep_parms->conf_in = p->content.ref.buffer;
+			kep_parms->conf_in_len = p->content.ref.length;
+			break;
+		case TEE_ATTR_SM2_KEP_CONFIRMATION_OUT:
+			kep_parms->conf_out = p->content.ref.buffer;
+			kep_parms->conf_out_len = p->content.ref.length;
+			break;
+		default:
+			/* Unexpected attribute */
+			res = TEE_ERROR_BAD_PARAMETERS;
+			goto out;
+		}
+	}
+
+	if ((found & mandatory) != mandatory) {
+		res = TEE_ERROR_BAD_PARAMETERS;
+		goto out;
+	}
+
+	return TEE_SUCCESS;
+out:
+	crypto_acipher_free_ecc_public_key(peer_key);
+	crypto_acipher_free_ecc_public_key(peer_eph_key);
+	return res;
+}
+#endif
+
 TEE_Result syscall_cryp_derive_key(unsigned long state,
 			const struct utee_attribute *usr_params,
 			unsigned long param_count, unsigned long derived_key)
@@ -2995,6 +3123,42 @@
 		}
 	}
 #endif
+#if defined(CFG_CRYPTO_SM2_KEP)
+	else if (cs->algo == TEE_ALG_SM2_KEP) {
+		struct ecc_public_key peer_eph_key = { };
+		struct ecc_public_key peer_key = { };
+		struct sm2_kep_parms kep_parms = {
+			.out = (uint8_t *)(sk + 1),
+			.out_len = so->info.maxKeySize,
+		};
+		struct tee_obj *ko2 = NULL;
+
+		res = tee_obj_get(utc, cs->key2, &ko2);
+		if (res != TEE_SUCCESS)
+			goto out;
+
+		res = get_sm2_kep_params(params, param_count, &peer_key,
+					 &peer_eph_key, &kep_parms);
+		if (res != TEE_SUCCESS)
+			goto out;
+
+		/*
+		 * key1 is our private keypair, key2 is our ephemeral public key
+		 */
+		res = crypto_acipher_sm2_kep_derive(ko->attr, /* key1 */
+						    ko2->attr, /* key2 */
+						    &peer_key, &peer_eph_key,
+						    &kep_parms);
+
+		if (res == TEE_SUCCESS) {
+			sk->key_size = kep_parms.out_len;
+			so->info.handleFlags |= TEE_HANDLE_FLAG_INITIALIZED;
+			set_attribute(so, type_props, TEE_ATTR_SECRET_VALUE);
+		}
+		crypto_acipher_free_ecc_public_key(&peer_key);
+		crypto_acipher_free_ecc_public_key(&peer_eph_key);
+	}
+#endif
 	else
 		res = TEE_ERROR_NOT_SUPPORTED;
 
diff --git a/lib/libutee/include/tee_api_defines.h b/lib/libutee/include/tee_api_defines.h
index fa64d3a..6ebd34e 100644
--- a/lib/libutee/include/tee_api_defines.h
+++ b/lib/libutee/include/tee_api_defines.h
@@ -163,6 +163,7 @@
 #define TEE_ALG_DSA_SHA256                      0x70004131
 #define TEE_ALG_SM2_DSA_SM3                     0x70006045
 #define TEE_ALG_DH_DERIVE_SHARED_SECRET         0x80000032
+#define TEE_ALG_SM2_KEP                         0x60000045
 #define TEE_ALG_MD5                             0x50000001
 #define TEE_ALG_SHA1                            0x50000002
 #define TEE_ALG_SHA224                          0x50000003
@@ -224,6 +225,8 @@
 #define TEE_TYPE_ECDH_KEYPAIR               0xA1000042
 #define TEE_TYPE_SM2_DSA_PUBLIC_KEY         0xA0000045
 #define TEE_TYPE_SM2_DSA_KEYPAIR            0xA1000045
+#define TEE_TYPE_SM2_KEP_PUBLIC_KEY         0xA0000046
+#define TEE_TYPE_SM2_KEP_KEYPAIR            0xA1000046
 #define TEE_TYPE_SM2_PKE_PUBLIC_KEY         0xA0000047
 #define TEE_TYPE_SM2_PKE_KEYPAIR            0xA1000047
 #define TEE_TYPE_GENERIC_SECRET             0xA0000000
@@ -258,6 +261,13 @@
 #define TEE_ATTR_ECC_PUBLIC_VALUE_Y         0xD0000241
 #define TEE_ATTR_ECC_PRIVATE_VALUE          0xC0000341
 #define TEE_ATTR_ECC_CURVE                  0xF0000441
+#define TEE_ATTR_SM2_ID_INITIATOR           0xD0000446
+#define TEE_ATTR_SM2_ID_RESPONDER           0xD0000546
+#define TEE_ATTR_SM2_KEP_USER               0xF0000646
+#define TEE_ATTR_SM2_KEP_CONFIRMATION_IN    0xD0000746
+#define TEE_ATTR_SM2_KEP_CONFIRMATION_OUT   0xD0000846
+#define TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_X 0xD0000946 /* Missing in 1.2.1 */
+#define TEE_ATTR_ECC_EPHEMERAL_PUBLIC_VALUE_Y 0xD0000A46 /* Missing in 1.2.1 */
 
 #define TEE_ATTR_BIT_PROTECTED		(1 << 28)
 #define TEE_ATTR_BIT_VALUE		(1 << 29)
diff --git a/lib/libutee/include/utee_defines.h b/lib/libutee/include/utee_defines.h
index e442cb3..7aa1c84 100644
--- a/lib/libutee/include/utee_defines.h
+++ b/lib/libutee/include/utee_defines.h
@@ -31,6 +31,7 @@
 #define TEE_MAIN_ALGO_ECDSA      0x41
 #define TEE_MAIN_ALGO_ECDH       0x42
 #define TEE_MAIN_ALGO_SM2_DSA_SM3 0x45 /* Not in v1.2 spec */
+#define TEE_MAIN_ALGO_SM2_KEP    0x46 /* Not in v1.2 spec */
 #define TEE_MAIN_ALGO_SM2_PKE    0x47 /* Not in v1.2 spec */
 #define TEE_MAIN_ALGO_HKDF       0xC0 /* OP-TEE extension */
 #define TEE_MAIN_ALGO_CONCAT_KDF 0xC1 /* OP-TEE extension */
@@ -53,6 +54,8 @@
 {
 	if (algo == TEE_ALG_SM2_PKE)
 		return TEE_OPERATION_ASYMMETRIC_CIPHER;
+	if (algo == TEE_ALG_SM2_KEP)
+		return TEE_OPERATION_KEY_DERIVATION;
 
 	return (algo >> 28) & 0xF; /* Bits [31:28] */
 }
@@ -64,6 +67,8 @@
 	switch (algo) {
 	case TEE_ALG_SM2_PKE:
 		return TEE_MAIN_ALGO_SM2_PKE;
+	case TEE_ALG_SM2_KEP:
+		return TEE_MAIN_ALGO_SM2_KEP;
 	default:
 		break;
 	}
diff --git a/lib/libutee/tee_api_operations.c b/lib/libutee/tee_api_operations.c
index e8a9efb..56ea2e1 100644
--- a/lib/libutee/tee_api_operations.c
+++ b/lib/libutee/tee_api_operations.c
@@ -46,7 +46,7 @@
 	if (!operation)
 		TEE_Panic(0);
 
-	if (algorithm == TEE_ALG_AES_XTS)
+	if (algorithm == TEE_ALG_AES_XTS || algorithm == TEE_ALG_SM2_KEP)
 		handle_state = TEE_HANDLE_FLAG_EXPECT_TWO_KEYS;
 
 	/* Check algorithm max key size */
@@ -90,6 +90,12 @@
 			return TEE_ERROR_NOT_SUPPORTED;
 		break;
 
+	case TEE_ALG_SM2_KEP:
+		/* Two 256-bit keys */
+		if (maxKeySize != 512)
+			return TEE_ERROR_NOT_SUPPORTED;
+		break;
+
 	case TEE_ALG_ECDSA_P384:
 	case TEE_ALG_ECDH_P384:
 		if (maxKeySize != 384)
@@ -218,6 +224,7 @@
 	case TEE_ALG_CONCAT_KDF_SHA384_DERIVE_KEY:
 	case TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY:
 	case TEE_ALG_PBKDF2_HMAC_SHA1_DERIVE_KEY:
+	case TEE_ALG_SM2_KEP:
 		if (mode != TEE_MODE_DERIVE)
 			return TEE_ERROR_NOT_SUPPORTED;
 		with_private_key = true;
@@ -623,7 +630,7 @@
 		goto out;
 	}
 
-	/* Two keys flag expected (TEE_ALG_AES_XTS only) */
+	/* Two keys flag expected (TEE_ALG_AES_XTS and TEE_ALG_SM2_KEP only) */
 	if ((operation->info.handleState & TEE_HANDLE_FLAG_EXPECT_TWO_KEYS) ==
 	    0) {
 		res = TEE_ERROR_BAD_PARAMETERS;
@@ -658,11 +665,10 @@
 	}
 
 	/*
-	 * AES-XTS (the only multi key algorithm supported, requires the
-	 * keys to be of equal size.
+	 * All the multi key algorithm currently supported requires the keys to
+	 * be of equal size.
 	 */
-	if (operation->info.algorithm == TEE_ALG_AES_XTS &&
-	    key_info1.keySize != key_info2.keySize) {
+	if (key_info1.keySize != key_info2.keySize) {
 		res = TEE_ERROR_BAD_PARAMETERS;
 		goto out;
 
@@ -686,7 +692,6 @@
 	res = TEE_CopyObjectAttributes1(operation->key1, key1);
 	if (res != TEE_SUCCESS)
 		goto out;
-
 	res = TEE_CopyObjectAttributes1(operation->key2, key2);
 	if (res != TEE_SUCCESS) {
 		if (res == TEE_ERROR_CORRUPT_OBJECT)