/src/cryptsetup/lib/loopaes/loopaes.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: LGPL-2.1-or-later |
2 | | /* |
3 | | * loop-AES compatible volume handling |
4 | | * |
5 | | * Copyright (C) 2011-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2011-2025 Milan Broz |
7 | | */ |
8 | | |
9 | | #include <errno.h> |
10 | | #include <stdio.h> |
11 | | #include <stdlib.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "libcryptsetup.h" |
15 | | #include "loopaes.h" |
16 | | #include "internal.h" |
17 | | |
18 | | static const char *get_hash(unsigned int key_size) |
19 | 0 | { |
20 | 0 | const char *hash; |
21 | |
|
22 | 0 | switch (key_size) { |
23 | 0 | case 16: hash = "sha256"; break; |
24 | 0 | case 24: hash = "sha384"; break; |
25 | 0 | case 32: hash = "sha512"; break; |
26 | 0 | default: hash = NULL; |
27 | 0 | } |
28 | | |
29 | 0 | return hash; |
30 | 0 | } |
31 | | |
32 | | static unsigned char get_tweak(unsigned int keys_count) |
33 | 0 | { |
34 | 0 | switch (keys_count) { |
35 | 0 | case 64: return 0x55; |
36 | 0 | case 65: return 0xF4; |
37 | 0 | default: break; |
38 | 0 | } |
39 | 0 | return 0x00; |
40 | 0 | } |
41 | | |
42 | | static int hash_key(const char *src, size_t src_len, |
43 | | char *dst, size_t dst_len, |
44 | | const char *hash_name) |
45 | 0 | { |
46 | 0 | struct crypt_hash *hd = NULL; |
47 | 0 | int r; |
48 | |
|
49 | 0 | if (crypt_hash_init(&hd, hash_name)) |
50 | 0 | return -EINVAL; |
51 | | |
52 | 0 | r = crypt_hash_write(hd, src, src_len); |
53 | 0 | if (!r) |
54 | 0 | r = crypt_hash_final(hd, dst, dst_len); |
55 | |
|
56 | 0 | crypt_hash_destroy(hd); |
57 | 0 | return r; |
58 | 0 | } |
59 | | |
60 | | static int hash_keys(struct crypt_device *cd, |
61 | | struct volume_key **vk, |
62 | | const char *hash_override, |
63 | | const char **input_keys, |
64 | | unsigned int keys_count, |
65 | | unsigned int key_len_output, |
66 | | unsigned int key_len_input) |
67 | 0 | { |
68 | 0 | const char *hash_name; |
69 | 0 | char tweak, *key_ptr; |
70 | 0 | unsigned int i; |
71 | 0 | int r = 0; |
72 | 0 | void *key = NULL; |
73 | |
|
74 | 0 | hash_name = hash_override ?: get_hash(key_len_output); |
75 | 0 | tweak = get_tweak(keys_count); |
76 | |
|
77 | 0 | if (!keys_count || !key_len_output || !hash_name || !key_len_input) { |
78 | 0 | log_err(cd, _("Key processing error (using hash %s)."), |
79 | 0 | hash_name ?: "[none]"); |
80 | 0 | return -EINVAL; |
81 | 0 | } |
82 | | |
83 | 0 | key = crypt_safe_alloc((size_t)key_len_output * keys_count); |
84 | 0 | if (!key) |
85 | 0 | return -ENOMEM; |
86 | | |
87 | 0 | for (i = 0; i < keys_count; i++) { |
88 | 0 | key_ptr = &((char *)key)[i * key_len_output]; |
89 | 0 | r = hash_key(input_keys[i], key_len_input, key_ptr, |
90 | 0 | key_len_output, hash_name); |
91 | 0 | if (r < 0) |
92 | 0 | goto err; |
93 | | |
94 | 0 | key_ptr[0] ^= tweak; |
95 | 0 | } |
96 | | |
97 | 0 | *vk = crypt_alloc_volume_key_by_safe_alloc(&key); |
98 | 0 | if (!*vk) { |
99 | 0 | r = -ENOMEM; |
100 | 0 | goto err; |
101 | 0 | } |
102 | | |
103 | 0 | return 0; |
104 | 0 | err: |
105 | 0 | crypt_safe_free(key); |
106 | 0 | *vk = NULL; |
107 | 0 | return r; |
108 | 0 | } |
109 | | |
110 | | static int keyfile_is_gpg(char *buffer, size_t buffer_len) |
111 | 0 | { |
112 | 0 | int r = 0; |
113 | 0 | int index = buffer_len < 100 ? buffer_len - 1 : 100; |
114 | 0 | char eos = buffer[index]; |
115 | |
|
116 | 0 | buffer[index] = '\0'; |
117 | 0 | if (strstr(buffer, "BEGIN PGP MESSAGE")) |
118 | 0 | r = 1; |
119 | 0 | buffer[index] = eos; |
120 | 0 | return r; |
121 | 0 | } |
122 | | |
123 | | int LOOPAES_parse_keyfile(struct crypt_device *cd, |
124 | | struct volume_key **vk, |
125 | | const char *hash, |
126 | | unsigned int *keys_count, |
127 | | char *buffer, |
128 | | size_t buffer_len) |
129 | 0 | { |
130 | 0 | const char *keys[LOOPAES_KEYS_MAX]; |
131 | 0 | unsigned int key_lengths[LOOPAES_KEYS_MAX]; |
132 | 0 | unsigned int i, key_index, key_len, offset; |
133 | |
|
134 | 0 | log_dbg(cd, "Parsing loop-AES keyfile of size %zu.", buffer_len); |
135 | |
|
136 | 0 | if (!buffer_len) |
137 | 0 | return -EINVAL; |
138 | | |
139 | 0 | if (keyfile_is_gpg(buffer, buffer_len)) { |
140 | 0 | log_err(cd, _("Detected not yet supported GPG encrypted keyfile.")); |
141 | 0 | log_std(cd, _("Please use gpg --decrypt <KEYFILE> | cryptsetup --keyfile=- ...\n")); |
142 | 0 | return -EINVAL; |
143 | 0 | } |
144 | | |
145 | | /* Remove EOL in buffer */ |
146 | 0 | for (i = 0; i < buffer_len; i++) |
147 | 0 | if (buffer[i] == '\n' || buffer[i] == '\r') |
148 | 0 | buffer[i] = '\0'; |
149 | |
|
150 | 0 | offset = 0; |
151 | 0 | key_index = 0; |
152 | 0 | key_lengths[0] = 0; |
153 | 0 | while (offset < buffer_len && key_index < LOOPAES_KEYS_MAX) { |
154 | 0 | keys[key_index] = &buffer[offset]; |
155 | 0 | key_lengths[key_index] = 0; |
156 | 0 | while (offset < buffer_len && buffer[offset]) { |
157 | 0 | offset++; |
158 | 0 | key_lengths[key_index]++; |
159 | 0 | } |
160 | 0 | if (offset == buffer_len) { |
161 | 0 | log_dbg(cd, "Unterminated key #%d in keyfile.", key_index); |
162 | 0 | log_err(cd, _("Incompatible loop-AES keyfile detected.")); |
163 | 0 | return -EINVAL; |
164 | 0 | } |
165 | 0 | while (offset < buffer_len && !buffer[offset]) |
166 | 0 | offset++; |
167 | 0 | key_index++; |
168 | 0 | } |
169 | | |
170 | | /* All keys must be the same length */ |
171 | 0 | key_len = key_lengths[0]; |
172 | 0 | for (i = 0; i < key_index; i++) |
173 | 0 | if (!key_lengths[i] || (key_lengths[i] != key_len)) { |
174 | 0 | log_dbg(cd, "Unexpected length %d of key #%d (should be %d).", |
175 | 0 | key_lengths[i], i, key_len); |
176 | 0 | key_len = 0; |
177 | 0 | break; |
178 | 0 | } |
179 | |
|
180 | 0 | if (offset != buffer_len || key_len == 0 || |
181 | 0 | (key_index != 1 && key_index !=64 && key_index != 65)) { |
182 | 0 | log_err(cd, _("Incompatible loop-AES keyfile detected.")); |
183 | 0 | return -EINVAL; |
184 | 0 | } |
185 | | |
186 | 0 | log_dbg(cd, "Keyfile: %d keys of length %d.", key_index, key_len); |
187 | |
|
188 | 0 | *keys_count = key_index; |
189 | 0 | return hash_keys(cd, vk, hash, keys, key_index, |
190 | 0 | crypt_get_volume_key_size(cd), key_len); |
191 | 0 | } |
192 | | |
193 | | int LOOPAES_activate(struct crypt_device *cd, |
194 | | const char *name, |
195 | | const char *base_cipher, |
196 | | unsigned int keys_count, |
197 | | struct volume_key *vk, |
198 | | uint32_t flags) |
199 | 0 | { |
200 | 0 | int r; |
201 | 0 | uint64_t req_flags, dmc_flags; |
202 | 0 | char *cipher = NULL; |
203 | 0 | struct crypt_dm_active_device dmd = { |
204 | 0 | .flags = flags, |
205 | 0 | }; |
206 | |
|
207 | 0 | r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL, |
208 | 0 | crypt_get_data_offset(cd), &dmd.size, &dmd.flags); |
209 | 0 | if (r) |
210 | 0 | return r; |
211 | | |
212 | 0 | if (keys_count == 1) { |
213 | 0 | req_flags = DM_PLAIN64_SUPPORTED; |
214 | 0 | r = asprintf(&cipher, "%s-%s", base_cipher, "cbc-plain64"); |
215 | 0 | } else { |
216 | 0 | req_flags = DM_LMK_SUPPORTED; |
217 | 0 | r = asprintf(&cipher, "%s:%d-%s", base_cipher, 64, "cbc-lmk"); |
218 | 0 | } |
219 | 0 | if (r < 0) |
220 | 0 | return -ENOMEM; |
221 | | |
222 | 0 | r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), |
223 | 0 | vk, cipher, crypt_get_iv_offset(cd), crypt_get_data_offset(cd), |
224 | 0 | NULL, 0, 0, crypt_get_sector_size(cd)); |
225 | |
|
226 | 0 | if (r) { |
227 | 0 | free(cipher); |
228 | 0 | return r; |
229 | 0 | } |
230 | | |
231 | 0 | log_dbg(cd, "Trying to activate loop-AES device %s using cipher %s.", |
232 | 0 | name, cipher); |
233 | |
|
234 | 0 | r = dm_create_device(cd, name, CRYPT_LOOPAES, &dmd); |
235 | |
|
236 | 0 | if (r < 0 && !dm_flags(cd, DM_CRYPT, &dmc_flags) && |
237 | 0 | (dmc_flags & req_flags) != req_flags) { |
238 | 0 | log_err(cd, _("Kernel does not support loop-AES compatible mapping.")); |
239 | 0 | r = -ENOTSUP; |
240 | 0 | } |
241 | |
|
242 | 0 | dm_targets_free(cd, &dmd); |
243 | 0 | free(cipher); |
244 | |
|
245 | 0 | return r; |
246 | 0 | } |