static int ep11_wrapkey()

in crypto/zcrypt_ep11misc.c [1068:1199]


static int ep11_wrapkey(u16 card, u16 domain,
			const u8 *key, size_t keysize,
			u32 mech, const u8 *iv,
			u8 *databuf, size_t *datasize)
{
	struct wk_req_pl {
		struct pl_head head;
		u8  var_tag;
		u8  var_len;
		u32 var;
		u8  mech_tag;
		u8  mech_len;
		u32 mech;
		/*
		 * followed by iv data
		 * followed by key tag + key blob
		 * followed by dummy kek param
		 * followed by dummy mac param
		 */
	} __packed * req_pl;
	struct wk_rep_pl {
		struct pl_head head;
		u8  rc_tag;
		u8  rc_len;
		u32 rc;
		u8  data_tag;
		u8  data_lenfmt;
		u16 data_len;
		u8  data[1024];
	} __packed * rep_pl;
	struct ep11_cprb *req = NULL, *rep = NULL;
	struct ep11_target_dev target;
	struct ep11_urb *urb = NULL;
	struct ep11keyblob *kb;
	size_t req_pl_size;
	int api, rc = -ENOMEM;
	bool has_header = false;
	u8 *p;

	/* maybe the session field holds a header with key info */
	kb = (struct ep11keyblob *) key;
	if (kb->head.type == TOKTYPE_NON_CCA &&
	    kb->head.version == TOKVER_EP11_AES) {
		has_header = true;
		keysize = kb->head.len < keysize ? kb->head.len : keysize;
	}

	/* request cprb and payload */
	req_pl_size = sizeof(struct wk_req_pl) + (iv ? 16 : 0)
		+ ASN1TAGLEN(keysize) + 4;
	req = alloc_cprb(req_pl_size);
	if (!req)
		goto out;
	if (!mech || mech == 0x80060001)
		req->flags |= 0x20; /* CPACF_WRAP needs special bit */
	req_pl = (struct wk_req_pl *) (((u8 *) req) + sizeof(*req));
	api = (!mech || mech == 0x80060001) ? 4 : 1; /* CKM_IBM_CPACF_WRAP */
	prep_head(&req_pl->head, req_pl_size, api, 33); /* WrapKey */
	req_pl->var_tag = 0x04;
	req_pl->var_len = sizeof(u32);
	/* mech is mech + mech params (iv here) */
	req_pl->mech_tag = 0x04;
	req_pl->mech_len = sizeof(u32) + (iv ? 16 : 0);
	req_pl->mech = (mech ? mech : 0x80060001); /* CKM_IBM_CPACF_WRAP */
	p = ((u8 *) req_pl) + sizeof(*req_pl);
	if (iv) {
		memcpy(p, iv, 16);
		p += 16;
	}
	/* key blob */
	p += asn1tag_write(p, 0x04, key, keysize);
	/* maybe the key argument needs the head data cleaned out */
	if (has_header) {
		kb = (struct ep11keyblob *)(p - keysize);
		memset(&kb->head, 0, sizeof(kb->head));
	}
	/* empty kek tag */
	*p++ = 0x04;
	*p++ = 0;
	/* empty mac tag */
	*p++ = 0x04;
	*p++ = 0;

	/* reply cprb and payload */
	rep = alloc_cprb(sizeof(struct wk_rep_pl));
	if (!rep)
		goto out;
	rep_pl = (struct wk_rep_pl *) (((u8 *) rep) + sizeof(*rep));

	/* urb and target */
	urb = kmalloc(sizeof(struct ep11_urb), GFP_KERNEL);
	if (!urb)
		goto out;
	target.ap_id = card;
	target.dom_id = domain;
	prep_urb(urb, &target, 1,
		 req, sizeof(*req) + req_pl_size,
		 rep, sizeof(*rep) + sizeof(*rep_pl));

	rc = zcrypt_send_ep11_cprb(urb);
	if (rc) {
		DEBUG_ERR(
			"%s zcrypt_send_ep11_cprb(card=%d dom=%d) failed, rc=%d\n",
			__func__, (int) card, (int) domain, rc);
		goto out;
	}

	rc = check_reply_pl((u8 *)rep_pl, __func__);
	if (rc)
		goto out;
	if (rep_pl->data_tag != 0x04 || rep_pl->data_lenfmt != 0x82) {
		DEBUG_ERR("%s unknown reply data format\n", __func__);
		rc = -EIO;
		goto out;
	}
	if (rep_pl->data_len > *datasize) {
		DEBUG_ERR("%s mismatch reply data len / data buffer len\n",
			  __func__);
		rc = -ENOSPC;
		goto out;
	}

	/* copy the data from the cprb to the data buffer */
	memcpy(databuf, rep_pl->data, rep_pl->data_len);
	*datasize = rep_pl->data_len;

out:
	kfree(req);
	kfree(rep);
	kfree(urb);
	return rc;
}