static int ep11_cryptsingle()

in crypto/zcrypt_ep11misc.c [801:926]


static int ep11_cryptsingle(u16 card, u16 domain,
			    u16 mode, u32 mech, const u8 *iv,
			    const u8 *key, size_t keysize,
			    const u8 *inbuf, size_t inbufsize,
			    u8 *outbuf, size_t *outbufsize)
{
	struct crypt_req_pl {
		struct pl_head head;
		u8  var_tag;
		u8  var_len;
		u32 var;
		u8  mech_tag;
		u8  mech_len;
		u32 mech;
		/*
		 * maybe followed by iv data
		 * followed by key tag + key blob
		 * followed by plaintext tag + plaintext
		 */
	} __packed * req_pl;
	struct crypt_rep_pl {
		struct pl_head head;
		u8  rc_tag;
		u8  rc_len;
		u32 rc;
		u8  data_tag;
		u8  data_lenfmt;
		/* data follows */
	} __packed * rep_pl;
	struct ep11_cprb *req = NULL, *rep = NULL;
	struct ep11_target_dev target;
	struct ep11_urb *urb = NULL;
	size_t req_pl_size, rep_pl_size;
	int n, api = 1, rc = -ENOMEM;
	u8 *p;

	/* the simple asn1 coding used has length limits */
	if (keysize > 0xFFFF || inbufsize > 0xFFFF)
		return -EINVAL;

	/* request cprb and payload */
	req_pl_size = sizeof(struct crypt_req_pl) + (iv ? 16 : 0)
		+ ASN1TAGLEN(keysize) + ASN1TAGLEN(inbufsize);
	req = alloc_cprb(req_pl_size);
	if (!req)
		goto out;
	req_pl = (struct crypt_req_pl *) (((u8 *) req) + sizeof(*req));
	prep_head(&req_pl->head, req_pl_size, api, (mode ? 20 : 19));
	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 : 0x00001085); /* CKM_AES_CBC_PAD */
	p = ((u8 *) req_pl) + sizeof(*req_pl);
	if (iv) {
		memcpy(p, iv, 16);
		p += 16;
	}
	/* key and input data */
	p += asn1tag_write(p, 0x04, key, keysize);
	p += asn1tag_write(p, 0x04, inbuf, inbufsize);

	/* reply cprb and payload, assume out data size <= in data size + 32 */
	rep_pl_size = sizeof(struct crypt_rep_pl) + ASN1TAGLEN(inbufsize + 32);
	rep = alloc_cprb(rep_pl_size);
	if (!rep)
		goto out;
	rep_pl = (struct crypt_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) + rep_pl_size);

	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) {
		DEBUG_ERR("%s unknown reply data format\n", __func__);
		rc = -EIO;
		goto out;
	}
	p = ((u8 *) rep_pl) + sizeof(*rep_pl);
	if (rep_pl->data_lenfmt <= 127)
		n = rep_pl->data_lenfmt;
	else if (rep_pl->data_lenfmt == 0x81)
		n = *p++;
	else if (rep_pl->data_lenfmt == 0x82) {
		n = *((u16 *) p);
		p += 2;
	} else {
		DEBUG_ERR("%s unknown reply data length format 0x%02hhx\n",
			  __func__, rep_pl->data_lenfmt);
		rc = -EIO;
		goto out;
	}
	if (n > *outbufsize) {
		DEBUG_ERR("%s mismatch reply data len %d / output buffer %zu\n",
			  __func__, n, *outbufsize);
		rc = -ENOSPC;
		goto out;
	}

	memcpy(outbuf, p, n);
	*outbufsize = n;

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