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