/src/cryptsetup/lib/crypto_backend/crypto_cipher_kernel.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: LGPL-2.1-or-later |
2 | | /* |
3 | | * Linux kernel userspace API crypto backend implementation (skcipher) |
4 | | * |
5 | | * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2012-2025 Milan Broz |
7 | | */ |
8 | | |
9 | | #include <stdlib.h> |
10 | | #include <stdio.h> |
11 | | #include <errno.h> |
12 | | #include <unistd.h> |
13 | | #include <sys/socket.h> |
14 | | #include <sys/stat.h> |
15 | | #include "crypto_backend_internal.h" |
16 | | |
17 | | #if ENABLE_AF_ALG |
18 | | |
19 | | #include <linux/if_alg.h> |
20 | | |
21 | | #ifndef AF_ALG |
22 | | #define AF_ALG 38 |
23 | | #endif |
24 | | #ifndef SOL_ALG |
25 | | #define SOL_ALG 279 |
26 | | #endif |
27 | | |
28 | | #ifndef ALG_SET_AEAD_AUTHSIZE |
29 | | #define ALG_SET_AEAD_AUTHSIZE 5 |
30 | | #endif |
31 | | |
32 | | /* |
33 | | * ciphers |
34 | | * |
35 | | * ENOENT - algorithm not available |
36 | | * ENOTSUP - AF_ALG family not available |
37 | | * (but cannot check specifically for skcipher API) |
38 | | */ |
39 | | static int _crypt_cipher_init(struct crypt_cipher_kernel *ctx, |
40 | | const void *key, size_t key_length, |
41 | | size_t tag_length, struct sockaddr_alg *sa) |
42 | 0 | { |
43 | 0 | void *optval = NULL; |
44 | |
|
45 | 0 | if (!ctx) |
46 | 0 | return -EINVAL; |
47 | | |
48 | 0 | ctx->opfd = -1; |
49 | 0 | ctx->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); |
50 | 0 | if (ctx->tfmfd < 0) { |
51 | 0 | crypt_cipher_destroy_kernel(ctx); |
52 | 0 | return -ENOTSUP; |
53 | 0 | } |
54 | | |
55 | 0 | if (bind(ctx->tfmfd, (struct sockaddr *)sa, sizeof(*sa)) < 0) { |
56 | 0 | crypt_cipher_destroy_kernel(ctx); |
57 | 0 | return -ENOENT; |
58 | 0 | } |
59 | | |
60 | 0 | if (setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_KEY, key, key_length) < 0) { |
61 | 0 | crypt_cipher_destroy_kernel(ctx); |
62 | 0 | return -EINVAL; |
63 | 0 | } |
64 | | |
65 | 0 | if (tag_length && setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, &optval, tag_length) < 0) { |
66 | 0 | crypt_cipher_destroy_kernel(ctx); |
67 | 0 | return -EINVAL; |
68 | 0 | } |
69 | | |
70 | 0 | ctx->opfd = accept(ctx->tfmfd, NULL, 0); |
71 | 0 | if (ctx->opfd < 0) { |
72 | 0 | crypt_cipher_destroy_kernel(ctx); |
73 | 0 | return -EINVAL; |
74 | 0 | } |
75 | | |
76 | 0 | return 0; |
77 | 0 | } |
78 | | |
79 | | int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name, |
80 | | const char *mode, const void *key, size_t key_length) |
81 | 0 | { |
82 | 0 | struct sockaddr_alg sa = { |
83 | 0 | .salg_family = AF_ALG, |
84 | 0 | .salg_type = "skcipher", |
85 | 0 | }; |
86 | 0 | int r; |
87 | |
|
88 | 0 | if (!strcmp(name, "cipher_null")) |
89 | 0 | key_length = 0; |
90 | |
|
91 | 0 | if (!strncmp(name, "capi:", 5)) |
92 | 0 | strncpy((char *)sa.salg_name, &name[5], sizeof(sa.salg_name) - 1); |
93 | 0 | else { |
94 | 0 | r = snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(%s)", mode, name); |
95 | 0 | if (r < 0 || (size_t)r >= sizeof(sa.salg_name)) |
96 | 0 | return -EINVAL; |
97 | 0 | } |
98 | | |
99 | 0 | return _crypt_cipher_init(ctx, key, key_length, 0, &sa); |
100 | 0 | } |
101 | | |
102 | | /* musl has broken CMSG_NXTHDR macro in system headers */ |
103 | | static inline struct cmsghdr *_CMSG_NXTHDR(struct msghdr* mhdr, struct cmsghdr* cmsg) |
104 | 0 | { |
105 | | #if !defined(__GLIBC__) && defined(__clang__) |
106 | | #pragma clang diagnostic push |
107 | | #pragma clang diagnostic ignored "-Wcast-align" |
108 | | #pragma clang diagnostic ignored "-Wsign-compare" |
109 | | return CMSG_NXTHDR(mhdr, cmsg); |
110 | | #pragma clang diagnostic pop |
111 | | #else |
112 | 0 | return CMSG_NXTHDR(mhdr, cmsg); |
113 | 0 | #endif |
114 | 0 | } |
115 | | |
116 | | /* The in/out should be aligned to page boundary */ |
117 | | /* coverity[ -taint_source : arg-3 ] */ |
118 | | static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, |
119 | | const char *in, size_t in_length, |
120 | | char *out, size_t out_length, |
121 | | const char *iv, size_t iv_length, |
122 | | uint32_t direction) |
123 | 0 | { |
124 | 0 | int r = 0; |
125 | 0 | ssize_t len; |
126 | 0 | struct af_alg_iv *alg_iv; |
127 | 0 | struct cmsghdr *header; |
128 | 0 | uint32_t *type; |
129 | 0 | struct iovec iov = { |
130 | 0 | .iov_base = (void*)(uintptr_t)in, |
131 | 0 | .iov_len = in_length, |
132 | 0 | }; |
133 | 0 | int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0; |
134 | 0 | char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size]; |
135 | 0 | struct msghdr msg = { |
136 | 0 | .msg_control = buffer, |
137 | 0 | .msg_controllen = sizeof(buffer), |
138 | 0 | .msg_iov = &iov, |
139 | 0 | .msg_iovlen = 1, |
140 | 0 | }; |
141 | |
|
142 | 0 | if (!in || !out || !in_length) |
143 | 0 | return -EINVAL; |
144 | | |
145 | 0 | if ((!iv && iv_length) || (iv && !iv_length)) |
146 | 0 | return -EINVAL; |
147 | | |
148 | 0 | memset(buffer, 0, sizeof(buffer)); |
149 | | |
150 | | /* Set encrypt/decrypt operation */ |
151 | 0 | header = CMSG_FIRSTHDR(&msg); |
152 | 0 | if (!header) |
153 | 0 | return -EINVAL; |
154 | | |
155 | 0 | header->cmsg_level = SOL_ALG; |
156 | 0 | header->cmsg_type = ALG_SET_OP; |
157 | 0 | header->cmsg_len = CMSG_LEN(sizeof(*type)); |
158 | 0 | type = (void*)CMSG_DATA(header); |
159 | 0 | *type = direction; |
160 | | |
161 | | /* Set IV */ |
162 | 0 | if (iv) { |
163 | 0 | header = _CMSG_NXTHDR(&msg, header); |
164 | 0 | if (!header) |
165 | 0 | return -EINVAL; |
166 | | |
167 | 0 | header->cmsg_level = SOL_ALG; |
168 | 0 | header->cmsg_type = ALG_SET_IV; |
169 | 0 | header->cmsg_len = iv_msg_size; |
170 | 0 | alg_iv = (void*)CMSG_DATA(header); |
171 | 0 | alg_iv->ivlen = iv_length; |
172 | 0 | crypt_backend_memcpy(alg_iv->iv, iv, iv_length); |
173 | 0 | } |
174 | | |
175 | 0 | len = sendmsg(ctx->opfd, &msg, 0); |
176 | 0 | if (len != (ssize_t)(in_length)) |
177 | 0 | r = -EIO; |
178 | 0 | else { |
179 | 0 | len = read(ctx->opfd, out, out_length); |
180 | 0 | if (len != (ssize_t)out_length) |
181 | 0 | r = -EIO; |
182 | 0 | } |
183 | |
|
184 | 0 | crypt_backend_memzero(buffer, sizeof(buffer)); |
185 | 0 | return r; |
186 | 0 | } |
187 | | |
188 | | int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx, |
189 | | const char *in, char *out, size_t length, |
190 | | const char *iv, size_t iv_length) |
191 | 0 | { |
192 | 0 | return _crypt_cipher_crypt(ctx, in, length, out, length, |
193 | 0 | iv, iv_length, ALG_OP_ENCRYPT); |
194 | 0 | } |
195 | | |
196 | | int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx, |
197 | | const char *in, char *out, size_t length, |
198 | | const char *iv, size_t iv_length) |
199 | 0 | { |
200 | 0 | return _crypt_cipher_crypt(ctx, in, length, out, length, |
201 | 0 | iv, iv_length, ALG_OP_DECRYPT); |
202 | 0 | } |
203 | | |
204 | | void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx) |
205 | 0 | { |
206 | 0 | if (ctx->tfmfd >= 0) |
207 | 0 | close(ctx->tfmfd); |
208 | 0 | if (ctx->opfd >= 0) |
209 | 0 | close(ctx->opfd); |
210 | |
|
211 | 0 | ctx->tfmfd = -1; |
212 | 0 | ctx->opfd = -1; |
213 | 0 | } |
214 | | |
215 | | int crypt_cipher_check_kernel(const char *name, const char *mode, |
216 | | const char *integrity, size_t key_length) |
217 | 0 | { |
218 | 0 | struct crypt_cipher_kernel c; |
219 | 0 | char mode_name[64], tmp_salg_name[180], *cipher_iv = NULL, *key; |
220 | 0 | const char *salg_type, *real_mode; |
221 | 0 | bool aead; |
222 | 0 | int r; |
223 | 0 | struct sockaddr_alg sa = { |
224 | 0 | .salg_family = AF_ALG, |
225 | 0 | }; |
226 | |
|
227 | 0 | aead = integrity && strcmp(integrity, "none"); |
228 | 0 | real_mode = NULL; |
229 | | |
230 | | /* Remove IV if present */ |
231 | 0 | if (mode) { |
232 | 0 | strncpy(mode_name, mode, sizeof(mode_name)); |
233 | 0 | mode_name[sizeof(mode_name) - 1] = 0; |
234 | 0 | cipher_iv = strchr(mode_name, '-'); |
235 | 0 | if (cipher_iv) { |
236 | 0 | *cipher_iv = '\0'; |
237 | 0 | real_mode = mode_name; |
238 | 0 | } |
239 | 0 | } |
240 | |
|
241 | 0 | salg_type = aead ? "aead" : "skcipher"; |
242 | 0 | r = snprintf((char *)sa.salg_type, sizeof(sa.salg_type), "%s", salg_type); |
243 | 0 | if (r < 0 || (size_t)r >= sizeof(sa.salg_name)) |
244 | 0 | return -EINVAL; |
245 | | |
246 | 0 | memset(tmp_salg_name, 0, sizeof(tmp_salg_name)); |
247 | | |
248 | | /* FIXME: this is duplicating a part of devmapper backend */ |
249 | 0 | if (aead) { |
250 | | /* In AEAD, mode parameter can be just IV like "random" */ |
251 | 0 | if (!strcmp(integrity, "poly1305")) |
252 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity); |
253 | 0 | else if (!real_mode) |
254 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); |
255 | 0 | else if (!strcmp(real_mode, "ccm")) |
256 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name); |
257 | 0 | else |
258 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name); |
259 | 0 | } else { |
260 | 0 | if (!mode) |
261 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); |
262 | 0 | else |
263 | 0 | r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode ?: mode_name, name); |
264 | 0 | } |
265 | |
|
266 | 0 | if (r < 0 || (size_t)r >= sizeof(tmp_salg_name)) |
267 | 0 | return -EINVAL; |
268 | | |
269 | 0 | memcpy(sa.salg_name, tmp_salg_name, sizeof(sa.salg_name)); |
270 | |
|
271 | 0 | key = malloc(key_length); |
272 | 0 | if (!key) |
273 | 0 | return -ENOMEM; |
274 | | |
275 | | /* We cannot use RNG yet, any key works here, tweak the first part if it is split key (XTS). */ |
276 | 0 | memset(key, 0xab, key_length); |
277 | 0 | *key = 0xef; |
278 | |
|
279 | 0 | r = _crypt_cipher_init(&c, key, key_length, 0, &sa); |
280 | 0 | crypt_cipher_destroy_kernel(&c); |
281 | 0 | free(key); |
282 | |
|
283 | 0 | return r; |
284 | 0 | } |
285 | | |
286 | | int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length, |
287 | | const char *in, char *out, size_t length, |
288 | | const char *iv, size_t iv_length, |
289 | | const char *tag, size_t tag_length) |
290 | 0 | { |
291 | 0 | struct crypt_cipher_kernel c; |
292 | 0 | struct sockaddr_alg sa = { |
293 | 0 | .salg_family = AF_ALG, |
294 | 0 | .salg_type = "aead", |
295 | 0 | .salg_name = "ccm(aes)", |
296 | 0 | }; |
297 | 0 | int r; |
298 | 0 | char buffer[128], ccm_iv[16]; |
299 | |
|
300 | 0 | if (length + tag_length > sizeof(buffer)) |
301 | 0 | return -EINVAL; |
302 | | |
303 | 0 | if (iv_length > sizeof(ccm_iv) - 2) |
304 | 0 | return -EINVAL; |
305 | | |
306 | 0 | r = _crypt_cipher_init(&c, key, key_length, tag_length, &sa); |
307 | 0 | if (r < 0) |
308 | 0 | return r; |
309 | | |
310 | 0 | memcpy(buffer, in, length); |
311 | 0 | memcpy(buffer + length, tag, tag_length); |
312 | | |
313 | | /* CCM IV - RFC3610 */ |
314 | 0 | memset(ccm_iv, 0, sizeof(ccm_iv)); |
315 | 0 | ccm_iv[0] = 15 - iv_length - 1; |
316 | 0 | memcpy(ccm_iv + 1, iv, iv_length); |
317 | 0 | memset(ccm_iv + 1 + iv_length, 0, ccm_iv[0] + 1); |
318 | 0 | iv_length = sizeof(ccm_iv); |
319 | |
|
320 | 0 | r = _crypt_cipher_crypt(&c, buffer, length + tag_length, out, length, |
321 | 0 | ccm_iv, iv_length, ALG_OP_DECRYPT); |
322 | |
|
323 | 0 | crypt_cipher_destroy_kernel(&c); |
324 | 0 | crypt_backend_memzero(buffer, sizeof(buffer)); |
325 | |
|
326 | 0 | return r; |
327 | 0 | } |
328 | | |
329 | | #else /* ENABLE_AF_ALG */ |
330 | | #pragma GCC diagnostic ignored "-Wunused-parameter" |
331 | | |
332 | | int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name, |
333 | | const char *mode, const void *key, size_t key_length) |
334 | | { |
335 | | return -ENOTSUP; |
336 | | } |
337 | | |
338 | | void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx) |
339 | | { |
340 | | return; |
341 | | } |
342 | | |
343 | | int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx, |
344 | | const char *in, char *out, size_t length, |
345 | | const char *iv, size_t iv_length) |
346 | | { |
347 | | return -EINVAL; |
348 | | } |
349 | | int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx, |
350 | | const char *in, char *out, size_t length, |
351 | | const char *iv, size_t iv_length) |
352 | | { |
353 | | return -EINVAL; |
354 | | } |
355 | | int crypt_cipher_check_kernel(const char *name, const char *mode, |
356 | | const char *integrity, size_t key_length) |
357 | | { |
358 | | /* Cannot check, expect success. */ |
359 | | return 0; |
360 | | } |
361 | | int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length, |
362 | | const char *in, char *out, size_t length, |
363 | | const char *iv, size_t iv_length, |
364 | | const char *tag, size_t tag_length) |
365 | | { |
366 | | return -ENOTSUP; |
367 | | } |
368 | | #endif |