Support init_by_name for verity.
[platform/upstream/cryptsetup.git] / lib / loopaes / loopaes.c
1 /*
2  * loop-AES compatible volume handling
3  *
4  * Copyright (C) 2011-2012, Red Hat, Inc. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "libcryptsetup.h"
26 #include "loopaes.h"
27 #include "internal.h"
28
29 static const char *get_hash(unsigned int key_size)
30 {
31         const char *hash;
32
33         switch (key_size) {
34                 case 16: hash = "sha256"; break;
35                 case 24: hash = "sha384"; break;
36                 case 32: hash = "sha512"; break;
37                 default: hash = NULL;
38         }
39
40         return hash;
41 }
42
43 static unsigned char get_tweak(unsigned int keys_count)
44 {
45         switch (keys_count) {
46                 case 64: return 0x55;
47                 case 65: return 0xF4;
48                 default: break;
49         }
50         return 0x00;
51 }
52
53 static int hash_key(const char *src, size_t src_len,
54                     char *dst, size_t dst_len,
55                     const char *hash_name)
56 {
57         struct crypt_hash *hd = NULL;
58         int r;
59
60         if (crypt_hash_init(&hd, hash_name))
61                 return -EINVAL;
62
63         r = crypt_hash_write(hd, src, src_len);
64         if (!r)
65                 r = crypt_hash_final(hd, dst, dst_len);
66
67         crypt_hash_destroy(hd);
68         return r;
69 }
70
71 static int hash_keys(struct crypt_device *cd,
72                      struct volume_key **vk,
73                      const char *hash_override,
74                      const char **input_keys,
75                      unsigned int keys_count,
76                      unsigned int key_len_output)
77 {
78         const char *hash_name;
79         char tweak, *key_ptr;
80         unsigned i, key_len_input;
81         int r;
82
83         hash_name = hash_override ?: get_hash(key_len_output);
84         tweak = get_tweak(keys_count);
85         key_len_input = strlen(input_keys[0]);
86
87         if (!keys_count || !key_len_output || !hash_name || !key_len_input) {
88                 log_err(cd, _("Key processing error (using hash %s).\n"),
89                         hash_name ?: "[none]");
90                 return -EINVAL;
91         }
92
93         *vk = crypt_alloc_volume_key(key_len_output * keys_count, NULL);
94         if (!*vk)
95                 return -ENOMEM;
96
97         for (i = 0; i < keys_count; i++) {
98                 key_ptr = &(*vk)->key[i * key_len_output];
99                 r = hash_key(input_keys[i], key_len_input, key_ptr,
100                              key_len_output, hash_name);
101                 if (r < 0)
102                         break;
103
104                 key_ptr[0] ^= tweak;
105         }
106
107         if (r < 0 && *vk) {
108                 crypt_free_volume_key(*vk);
109                 *vk = NULL;
110         }
111         return r;
112 }
113
114 static int keyfile_is_gpg(char *buffer, size_t buffer_len)
115 {
116         int r = 0;
117         int index = buffer_len < 100 ? buffer_len - 1 : 100;
118         char eos = buffer[index];
119
120         buffer[index] = '\0';
121         if (strstr(buffer, "BEGIN PGP MESSAGE"))
122                 r = 1;
123         buffer[index] = eos;
124         return r;
125 }
126
127 int LOOPAES_parse_keyfile(struct crypt_device *cd,
128                           struct volume_key **vk,
129                           const char *hash,
130                           unsigned int *keys_count,
131                           char *buffer,
132                           size_t buffer_len)
133 {
134         const char *keys[LOOPAES_KEYS_MAX];
135         unsigned i, key_index, key_len, offset;
136
137         log_dbg("Parsing loop-AES keyfile of size %d.", buffer_len);
138
139         if (!buffer_len)
140                 return -EINVAL;
141
142         if (keyfile_is_gpg(buffer, buffer_len)) {
143                 log_err(cd, _("Detected not yet supported GPG encrypted keyfile.\n"));
144                 log_std(cd, _("Please use gpg --decrypt <KEYFILE> | cryptsetup --keyfile=- ...\n"));
145                 return -EINVAL;
146         }
147
148         /* Remove EOL in buffer */
149         for (i = 0; i < buffer_len; i++)
150                 if (buffer[i] == '\n' || buffer[i] == '\r')
151                         buffer[i] = '\0';
152
153         offset = 0;
154         key_index = 0;
155         while (offset < buffer_len && key_index < LOOPAES_KEYS_MAX) {
156                 keys[key_index++] = &buffer[offset];
157                 while (offset < buffer_len && buffer[offset])
158                         offset++;
159                 while (offset < buffer_len && !buffer[offset])
160                         offset++;
161         }
162
163         /* All keys must be the same length */
164         key_len = key_index ? strlen(keys[0]) : 0;
165         for (i = 0; i < key_index; i++)
166                 if (key_len != strlen(keys[i])) {
167                         log_dbg("Unexpected length %d of key #%d (should be %d).",
168                                 strlen(keys[i]), i, key_len);
169                         key_len = 0;
170                         break;
171                 }
172
173         log_dbg("Keyfile: %d keys of length %d.", key_index, key_len);
174         if (offset != buffer_len || key_len == 0 ||
175            (key_index != 1 && key_index !=64 && key_index != 65)) {
176                 log_err(cd, _("Incompatible loop-AES keyfile detected.\n"));
177                 return -EINVAL;
178         }
179
180         *keys_count = key_index;
181         return hash_keys(cd, vk, hash, keys, key_index, crypt_get_volume_key_size(cd));
182 }
183
184 int LOOPAES_activate(struct crypt_device *cd,
185                      const char *name,
186                      const char *base_cipher,
187                      unsigned int keys_count,
188                      struct volume_key *vk,
189                      uint32_t flags)
190 {
191         char *cipher = NULL;
192         uint32_t req_flags;
193         int r;
194         struct crypt_dm_active_device dmd = {
195                 .target = DM_CRYPT,
196                 .uuid   = crypt_get_uuid(cd),
197                 .size   = 0,
198                 .flags  = flags,
199                 .data_device = crypt_get_device_name(cd),
200                 .u.crypt  = {
201                         .cipher = NULL,
202                         .vk     = vk,
203                         .offset = crypt_get_data_offset(cd),
204                         .iv_offset = crypt_get_iv_offset(cd),
205                 }
206         };
207
208
209         r = device_check_and_adjust(cd, dmd.data_device, DEV_EXCL,
210                                     &dmd.size, &dmd.u.crypt.offset, &flags);
211         if (r)
212                 return r;
213
214         if (keys_count == 1) {
215                 req_flags = DM_PLAIN64_SUPPORTED;
216                 r = asprintf(&cipher, "%s-%s", base_cipher, "cbc-plain64");
217         } else {
218                 req_flags = DM_LMK_SUPPORTED;
219                 r = asprintf(&cipher, "%s:%d-%s", base_cipher, 64, "cbc-lmk");
220         }
221         if (r < 0)
222                 return -ENOMEM;
223
224         dmd.u.crypt.cipher = cipher;
225         log_dbg("Trying to activate loop-AES device %s using cipher %s.",
226                 name, dmd.u.crypt.cipher);
227
228         r = dm_create_device(name, CRYPT_LOOPAES, &dmd, 0);
229
230         if (!r && !(dm_flags() & req_flags)) {
231                 log_err(cd, _("Kernel doesn't support loop-AES compatible mapping.\n"));
232                 r = -ENOTSUP;
233         }
234
235         free(cipher);
236         return r;
237 }