xref: /dpdk/lib/pdcp/pdcp_crypto.c (revision 7917b0d38e92e8b9ec5a870415b791420e10f11a)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2023 Marvell.
3  */
4 
5 #include <rte_crypto.h>
6 #include <rte_crypto_sym.h>
7 #include <rte_cryptodev.h>
8 #include <rte_errno.h>
9 #include <rte_pdcp.h>
10 #include <rte_pdcp_hdr.h>
11 
12 #include "pdcp_crypto.h"
13 #include "pdcp_entity.h"
14 
15 static int
16 pdcp_crypto_caps_cipher_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *c_xfrm)
17 {
18 	const struct rte_cryptodev_symmetric_capability *cap;
19 	struct rte_cryptodev_sym_capability_idx cap_idx;
20 	int ret;
21 
22 	cap_idx.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
23 	cap_idx.algo.cipher = c_xfrm->cipher.algo;
24 
25 	cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx);
26 	if (cap == NULL)
27 		return -1;
28 
29 	ret = rte_cryptodev_sym_capability_check_cipher(cap, c_xfrm->cipher.key.length,
30 							c_xfrm->cipher.iv.length);
31 
32 	return ret;
33 }
34 
35 static int
36 pdcp_crypto_caps_auth_verify(uint8_t dev_id, const struct rte_crypto_sym_xform *a_xfrm)
37 {
38 	const struct rte_cryptodev_symmetric_capability *cap;
39 	struct rte_cryptodev_sym_capability_idx cap_idx;
40 	int ret;
41 
42 	cap_idx.type = RTE_CRYPTO_SYM_XFORM_AUTH;
43 	cap_idx.algo.auth = a_xfrm->auth.algo;
44 
45 	cap = rte_cryptodev_sym_capability_get(dev_id, &cap_idx);
46 	if (cap == NULL)
47 		return -1;
48 
49 	ret = rte_cryptodev_sym_capability_check_auth(cap, a_xfrm->auth.key.length,
50 						      a_xfrm->auth.digest_length,
51 						      a_xfrm->auth.iv.length);
52 
53 	return ret;
54 }
55 
56 static int
57 pdcp_crypto_xfrm_validate(const struct rte_pdcp_entity_conf *conf,
58 				 const struct rte_crypto_sym_xform *c_xfrm,
59 				 const struct rte_crypto_sym_xform *a_xfrm,
60 				 bool is_auth_then_cipher)
61 {
62 	uint16_t cipher_iv_len, auth_digest_len, auth_iv_len;
63 	int ret;
64 
65 	/*
66 	 * Uplink means PDCP entity is configured for transmit. Downlink means PDCP entity is
67 	 * configured for receive. When integrity protection is enabled, PDCP always performs
68 	 * digest-encrypted or auth-gen-encrypt for uplink (and decrypt-auth-verify for downlink).
69 	 * So for uplink, crypto chain would be auth-cipher while for downlink it would be
70 	 * cipher-auth.
71 	 *
72 	 * When integrity protection is not required, xform would be cipher only.
73 	 */
74 
75 	if (c_xfrm == NULL)
76 		return -EINVAL;
77 
78 	if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK) {
79 
80 		/* With UPLINK, if auth is enabled, it should be before cipher */
81 		if (a_xfrm != NULL && !is_auth_then_cipher)
82 			return -EINVAL;
83 
84 		/* With UPLINK, cipher operation must be encrypt */
85 		if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_ENCRYPT)
86 			return -EINVAL;
87 
88 		/* With UPLINK, auth operation (if present) must be generate */
89 		if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_GENERATE)
90 			return -EINVAL;
91 
92 	} else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK) {
93 
94 		/* With DOWNLINK, if auth is enabled, it should be after cipher */
95 		if (a_xfrm != NULL && is_auth_then_cipher)
96 			return -EINVAL;
97 
98 		/* With DOWNLINK, cipher operation must be decrypt */
99 		if (c_xfrm->cipher.op != RTE_CRYPTO_CIPHER_OP_DECRYPT)
100 			return -EINVAL;
101 
102 		/* With DOWNLINK, auth operation (if present) must be verify */
103 		if (a_xfrm != NULL && a_xfrm->auth.op != RTE_CRYPTO_AUTH_OP_VERIFY)
104 			return -EINVAL;
105 
106 	} else {
107 		return -EINVAL;
108 	}
109 
110 	if ((c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_NULL) &&
111 	    (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_AES_CTR) &&
112 	    (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_ZUC_EEA3) &&
113 	    (c_xfrm->cipher.algo != RTE_CRYPTO_CIPHER_SNOW3G_UEA2))
114 		return -EINVAL;
115 
116 	if (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_NULL)
117 		cipher_iv_len = 0;
118 	else
119 		cipher_iv_len = PDCP_IV_LEN;
120 
121 	if (cipher_iv_len != c_xfrm->cipher.iv.length)
122 		return -EINVAL;
123 
124 	if (a_xfrm != NULL) {
125 		if ((a_xfrm->auth.algo != RTE_CRYPTO_AUTH_NULL) &&
126 		    (a_xfrm->auth.algo != RTE_CRYPTO_AUTH_AES_CMAC) &&
127 		    (a_xfrm->auth.algo != RTE_CRYPTO_AUTH_ZUC_EIA3) &&
128 		    (a_xfrm->auth.algo != RTE_CRYPTO_AUTH_SNOW3G_UIA2))
129 			return -EINVAL;
130 
131 		/* For AUTH NULL, lib PDCP would add 4 byte 0s */
132 		if (a_xfrm->auth.algo == RTE_CRYPTO_AUTH_NULL)
133 			auth_digest_len = 0;
134 		else
135 			auth_digest_len = RTE_PDCP_MAC_I_LEN;
136 
137 		if (auth_digest_len != a_xfrm->auth.digest_length)
138 			return -EINVAL;
139 
140 		if ((a_xfrm->auth.algo == RTE_CRYPTO_AUTH_ZUC_EIA3) ||
141 		    (a_xfrm->auth.algo == RTE_CRYPTO_AUTH_SNOW3G_UIA2))
142 			auth_iv_len = PDCP_IV_LEN;
143 		else
144 			auth_iv_len = 0;
145 
146 		if (a_xfrm->auth.iv.length != auth_iv_len)
147 			return -EINVAL;
148 	}
149 
150 	if (!rte_cryptodev_is_valid_dev(conf->dev_id))
151 		return -EINVAL;
152 
153 	ret = pdcp_crypto_caps_cipher_verify(conf->dev_id, c_xfrm);
154 	if (ret)
155 		return -ENOTSUP;
156 
157 	if (a_xfrm != NULL) {
158 		ret = pdcp_crypto_caps_auth_verify(conf->dev_id, a_xfrm);
159 		if (ret)
160 			return -ENOTSUP;
161 	}
162 
163 	return 0;
164 }
165 
166 int
167 pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf)
168 {
169 	struct rte_crypto_sym_xform *c_xfrm, *a_xfrm;
170 	struct entity_priv *en_priv;
171 	bool is_auth_then_cipher;
172 	int ret;
173 
174 	if (entity == NULL || conf == NULL || conf->crypto_xfrm == NULL)
175 		return -EINVAL;
176 
177 	en_priv = entity_priv_get(entity);
178 
179 	en_priv->dev_id = conf->dev_id;
180 
181 	if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_CIPHER) {
182 		c_xfrm = conf->crypto_xfrm;
183 		a_xfrm = conf->crypto_xfrm->next;
184 		is_auth_then_cipher = false;
185 	} else if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_AUTH) {
186 		a_xfrm = conf->crypto_xfrm;
187 		c_xfrm = conf->crypto_xfrm->next;
188 		is_auth_then_cipher = true;
189 	} else {
190 		return -EINVAL;
191 	}
192 
193 	ret = pdcp_crypto_xfrm_validate(conf, c_xfrm, a_xfrm, is_auth_then_cipher);
194 	if (ret)
195 		return ret;
196 
197 	if (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_NULL)
198 		c_xfrm->cipher.iv.offset = 0;
199 	else
200 		c_xfrm->cipher.iv.offset = PDCP_IV_OFFSET;
201 
202 	if (a_xfrm != NULL) {
203 		if (a_xfrm->auth.algo == RTE_CRYPTO_AUTH_NULL)
204 			a_xfrm->auth.iv.offset = 0;
205 		else
206 			if (c_xfrm->cipher.iv.offset)
207 				a_xfrm->auth.iv.offset = PDCP_IV_OFFSET + PDCP_IV_LEN;
208 			else
209 				a_xfrm->auth.iv.offset = PDCP_IV_OFFSET;
210 	}
211 
212 	if (conf->sess_mpool == NULL)
213 		return -EINVAL;
214 
215 	en_priv->crypto_sess = rte_cryptodev_sym_session_create(conf->dev_id, conf->crypto_xfrm,
216 								conf->sess_mpool);
217 	if (en_priv->crypto_sess == NULL) {
218 		/* rte_errno is set as positive values of error codes */
219 		return -rte_errno;
220 	}
221 
222 	rte_cryptodev_sym_session_opaque_data_set(en_priv->crypto_sess, (uint64_t)entity);
223 
224 	return 0;
225 }
226 
227 void
228 pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity)
229 {
230 	struct entity_priv *en_priv;
231 
232 	en_priv = entity_priv_get(entity);
233 
234 	if (en_priv->crypto_sess != NULL) {
235 		rte_cryptodev_sym_session_free(en_priv->dev_id, en_priv->crypto_sess);
236 		en_priv->crypto_sess = NULL;
237 	}
238 }
239