+/*
+ * GOST CMS processing functions
+ */
+/* FIXME reaarange declarations */
+static int pub_decode_gost_ec(EVP_PKEY *pk, X509_PUBKEY *pub);
+
+static int gost_cms_set_kari_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
+{
+ int ret = 0;
+ X509_ALGOR *alg;
+ ASN1_OCTET_STRING *ukm;
+
+ /* Deal with originator */
+ X509_ALGOR *pubalg = NULL;
+ ASN1_BIT_STRING *pubkey = NULL;
+
+ EVP_PKEY *peer_key = NULL;
+ X509_PUBKEY *tmp = NULL;
+
+ int nid;
+ unsigned char shared_key[64];
+ size_t shared_key_size = 64;
+ const EVP_CIPHER *cipher = NULL;
+
+ if (CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm) == 0)
+ goto err;
+
+ if (CMS_RecipientInfo_kari_get0_orig_id(ri, &pubalg, &pubkey, NULL, NULL, NULL) == 0)
+ goto err;
+
+ nid = OBJ_obj2nid(alg->algorithm);
+ if (alg->parameter->type != V_ASN1_SEQUENCE)
+ goto err;
+
+ switch (nid) {
+ case NID_kuznyechik_kexp15:
+ case NID_magma_kexp15:
+ cipher = EVP_get_cipherbynid(nid);
+ break;
+ }
+
+ if (cipher == NULL) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, GOST_R_CIPHER_NOT_FOUND);
+ goto err;
+ }
+
+ if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_SET_IV,
+ ASN1_STRING_length(ukm), (void *)ASN1_STRING_get0_data(ukm)) <= 0)
+ goto err;
+
+ if (pubkey != NULL && pubalg != NULL) {
+ const ASN1_OBJECT *paobj = NULL;
+ int ptype = 0;
+ const void *param = NULL;
+
+ peer_key = EVP_PKEY_new();
+ tmp = X509_PUBKEY_new();
+
+ if ((peer_key == NULL) || (tmp == NULL)) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ X509_ALGOR_get0(&paobj, &ptype, ¶m, pubalg);
+
+ if (X509_PUBKEY_set0_param(tmp, (ASN1_OBJECT *)paobj,
+ ptype, (void *)param,
+ (unsigned char *)ASN1_STRING_get0_data(pubkey),
+ ASN1_STRING_length(pubkey) ) == 0) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, GOST_R_PUBLIC_KEY_UNDEFINED);
+ goto err;
+ }
+
+ if (pub_decode_gost_ec(peer_key, tmp) <= 0) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, GOST_R_ERROR_DECODING_PUBLIC_KEY);
+ goto err;
+ }
+
+ if (EVP_PKEY_derive_set_peer(pctx, peer_key) <= 0) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, GOST_R_ERROR_SETTING_PEER_KEY);
+ goto err;
+ }
+ }
+
+ if (EVP_PKEY_derive(pctx, shared_key, &shared_key_size) <= 0) {
+ GOSTerr(GOST_F_GOST_CMS_SET_KARI_SHARED_INFO, GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ goto err;
+ }
+
+ EVP_CIPHER_CTX_set_flags(CMS_RecipientInfo_kari_get0_ctx(ri), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ if (EVP_DecryptInit_ex(CMS_RecipientInfo_kari_get0_ctx(ri), cipher, NULL,
+ shared_key, ukm->data+24) == 0)
+ goto err;
+
+ ret = 1;
+err:
+ EVP_PKEY_free(peer_key);
+ if (ret == 0) {
+ X509_PUBKEY_free(tmp);
+ }
+
+ return ret;
+}
+
+static int gost_cms_set_ktri_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
+{
+ X509_ALGOR *alg;
+ struct gost_pmeth_data *gctx = EVP_PKEY_CTX_get_data(pctx);
+
+ CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg);
+
+ switch (OBJ_obj2nid(alg->algorithm)) {
+ case NID_kuznyechik_kexp15:
+ gctx->cipher_nid = NID_kuznyechik_ctr;
+ break;
+
+ case NID_magma_kexp15:
+ gctx->cipher_nid = NID_magma_ctr;
+ break;
+
+ case NID_id_GostR3410_2001:
+ case NID_id_GostR3410_2001DH:
+ case NID_id_GostR3410_2012_256:
+ case NID_id_GostR3410_2012_512:
+ gctx->cipher_nid = NID_id_Gost28147_89;
+ break;
+
+ default:
+ GOSTerr(GOST_F_GOST_CMS_SET_KTRI_SHARED_INFO, GOST_R_UNSUPPORTED_RECIPIENT_INFO);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int gost_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
+{
+ switch(CMS_RecipientInfo_type(ri)) {
+ case CMS_RECIPINFO_AGREE:
+ return gost_cms_set_kari_shared_info(pctx, ri);
+ break;
+ case CMS_RECIPINFO_TRANS:
+ return gost_cms_set_ktri_shared_info(pctx, ri);
+ break;
+ }
+
+ GOSTerr(GOST_F_GOST_CMS_SET_SHARED_INFO, GOST_R_UNSUPPORTED_RECIPIENT_INFO);
+ return 0;
+}
+
+static ASN1_STRING *gost_encode_cms_params(int ka_nid)
+{
+ ASN1_STRING *ret = NULL;
+ ASN1_STRING *params = ASN1_STRING_new();
+
+ /* It's a hack. We have only one OID here, so we can use
+ * GOST_KEY_PARAMS which is a sequence of 3 OIDs,
+ * the 1st one is mandatory and the rest are optional */
+ GOST_KEY_PARAMS *gkp = GOST_KEY_PARAMS_new();
+
+ if (params == NULL || gkp == NULL) {
+ GOSTerr(GOST_F_GOST_ENCODE_CMS_PARAMS, ERR_R_MALLOC_FAILURE);
+ goto end;
+ }
+
+ gkp->key_params = OBJ_nid2obj(ka_nid);
+ params->length = i2d_GOST_KEY_PARAMS(gkp, ¶ms->data);
+
+ if (params->length < 0) {
+ GOSTerr(GOST_F_GOST_ENCODE_CMS_PARAMS, ERR_R_MALLOC_FAILURE);
+ goto end;
+ }
+
+ params->type = V_ASN1_SEQUENCE;
+ ret = params;
+
+end:
+ GOST_KEY_PARAMS_free(gkp);
+
+ if (ret == NULL)
+ ASN1_STRING_free(params);
+
+ return ret;
+}
+
+/*
+ * Control function
+ */