"Fossies" - the Fresh Open Source Software Archive

Member "cryptsetup-2.4.3/lib/loopaes/loopaes.c" (13 Jan 2022, 6410 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "loopaes.c" see the Fossies "Dox" file reference documentation.

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