+{
+ int ret;
+ size_t outlen;
+ const EC_POINT *pubkey = NULL;
+ EC_KEY *eckey;
+ EC_PKEY_CTX *dctx = ctx->data;
+ if (!ctx->pkey || !ctx->peerkey) {
+ ECerr(EC_F_PKEY_EC_DERIVE, EC_R_KEYS_NOT_SET);
+ return 0;
+ }
+
+ eckey = dctx->co_key ? dctx->co_key : ctx->pkey->pkey.ec;
+
+ if (!key) {
+ const EC_GROUP *group;
+ group = EC_KEY_get0_group(eckey);
+ *keylen = (EC_GROUP_get_degree(group) + 7) / 8;
+ return 1;
+ }
+ pubkey = EC_KEY_get0_public_key(ctx->peerkey->pkey.ec);
+
+ /*
+ * NB: unlike PKCS#3 DH, if *outlen is less than maximum size this is not
+ * an error, the result is truncated.
+ */
+
+ outlen = *keylen;
+
+ ret = ECDH_compute_key(key, outlen, pubkey, eckey, 0);
+ if (ret <= 0)
+ return 0;
+ *keylen = ret;
+ return 1;
+}
+
+static int pkey_ecies_encrypt(EVP_PKEY_CTX *ctx,
+ unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ int ret, md_type;
+ EC_PKEY_CTX *dctx = ctx->data;
+ EC_KEY *ec = ctx->pkey->pkey.ec;
+ const int ec_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+
+ if (dctx->md)
+ md_type = EVP_MD_type(dctx->md);
+ else if (ec_nid == NID_sm2)
+ md_type = NID_sm3;
+ else
+ md_type = NID_sha256;
+
+ if (ec_nid == NID_sm2) {
+# if defined(OPENSSL_NO_SM2)
+ ret = -1;
+# else
+ if (out == NULL) {
+ *outlen = SM2_ciphertext_size(ec, EVP_get_digestbynid(md_type), inlen);
+ ret = 1;
+ }
+ else {
+ ret = SM2_encrypt(ec, EVP_get_digestbynid(md_type),
+ in, inlen, out, outlen);
+ }
+# endif
+ } else {
+ /* standard ECIES not implemented */
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int pkey_ecies_decrypt(EVP_PKEY_CTX *ctx,
+ unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ int ret, md_type;
+ EC_PKEY_CTX *dctx = ctx->data;
+ EC_KEY *ec = ctx->pkey->pkey.ec;
+ const int ec_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+
+ if (dctx->md)
+ md_type = EVP_MD_type(dctx->md);
+ else if (ec_nid == NID_sm2)
+ md_type = NID_sm3;
+ else
+ md_type = NID_sha256;
+
+ if (ec_nid == NID_sm2) {
+# if defined(OPENSSL_NO_SM2)
+ ret = -1;
+# else
+ if (out == NULL) {
+ *outlen = SM2_plaintext_size(ec, EVP_get_digestbynid(md_type), inlen);
+ ret = 1;
+ }
+ else {
+ ret = SM2_decrypt(ec, EVP_get_digestbynid(md_type),
+ in, inlen, out, outlen);
+ }
+# endif
+ } else {
+ /* standard ECIES not implemented */
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int pkey_ec_kdf_derive(EVP_PKEY_CTX *ctx,
+ unsigned char *key, size_t *keylen)
+{
+ EC_PKEY_CTX *dctx = ctx->data;
+ unsigned char *ktmp = NULL;
+ size_t ktmplen;
+ int rv = 0;
+ if (dctx->kdf_type == EVP_PKEY_ECDH_KDF_NONE)
+ return pkey_ec_derive(ctx, key, keylen);
+ if (!key) {
+ *keylen = dctx->kdf_outlen;
+ return 1;
+ }
+ if (*keylen != dctx->kdf_outlen)
+ return 0;
+ if (!pkey_ec_derive(ctx, NULL, &ktmplen))
+ return 0;
+ ktmp = OPENSSL_malloc(ktmplen);
+ if (ktmp == NULL)
+ return 0;
+ if (!pkey_ec_derive(ctx, ktmp, &ktmplen))
+ goto err;
+ /* Do KDF stuff */
+ if (!ECDH_KDF_X9_62(key, *keylen, ktmp, ktmplen,
+ dctx->kdf_ukm, dctx->kdf_ukmlen, dctx->kdf_md))
+ goto err;
+ rv = 1;
+
+ err:
+ OPENSSL_clear_free(ktmp, ktmplen);
+ return rv;
+}
+#endif