summaryrefslogtreecommitdiff
path: root/cryptfs.cpp
blob: be8ba3c90f5a5523d1521a7b904fe5d81484655a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// This file contains the implementation of the dm-crypt volume metadata
// encryption method, which is deprecated.  Devices that launched with Android
// 11 or higher use a different method instead.  For details, see
// https://source.android.com/security/encryption/metadata#configuration-on-adoptable-storage
//

#define LOG_TAG "Cryptfs"

#include "cryptfs.h"

#include "CryptoType.h"
#include "Utils.h"

#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <cutils/properties.h>
#include <libdm/dm.h>
#include <log/log.h>

#include <chrono>

using android::base::ParseUint;
using android::vold::CryptoType;
using android::vold::KeyBuffer;
using android::vold::KeyGeneration;
using namespace android::dm;
using namespace android::vold;
using namespace std::chrono_literals;

#define MAX_KEY_LEN 48

#define TABLE_LOAD_RETRIES 10

constexpr CryptoType aes_128_cbc = CryptoType()
                                           .set_config_name("AES-128-CBC")
                                           .set_kernel_name("aes-cbc-essiv:sha256")
                                           .set_keysize(16);

constexpr CryptoType supported_crypto_types[] = {aes_128_cbc, android::vold::adiantum};

static_assert(validateSupportedCryptoTypes(MAX_KEY_LEN, supported_crypto_types,
                                           array_length(supported_crypto_types)),
              "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
              "incompletely constructed.");

static const CryptoType& get_crypto_type() {
    // We only want to parse this read-only property once.  But we need to wait
    // until the system is initialized before we can read it.  So we use a static
    // scoped within this function to get it only once.
    static CryptoType crypto_type =
            lookup_crypto_algorithm(supported_crypto_types, array_length(supported_crypto_types),
                                    aes_128_cbc, "ro.crypto.fde_algorithm");
    return crypto_type;
}

const KeyGeneration cryptfs_get_keygen() {
    return KeyGeneration{get_crypto_type().get_keysize(), true, false};
}

/* Convert a binary key of specified length into an ascii hex string equivalent,
 * without the leading 0x and with null termination
 */
static void convert_key_to_hex_ascii(const KeyBuffer& key, char* key_ascii) {
    unsigned int i, a;
    unsigned char nibble;

    for (i = 0, a = 0; i < key.size(); i++, a += 2) {
        /* For each byte, write out two ascii hex digits */
        nibble = (key[i] >> 4) & 0xf;
        key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);

        nibble = key[i] & 0xf;
        key_ascii[a + 1] = nibble + (nibble > 9 ? 0x37 : 0x30);
    }

    /* Add the null termination */
    key_ascii[a] = '\0';
}

/*
 * Called by vold when it's asked to mount an encrypted external
 * storage volume. The incoming partition has no crypto header/footer,
 * as any metadata is been stored in a separate, small partition.  We
 * assume it must be using our same crypt type and keysize.
 */
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const KeyBuffer& key,
                             std::string* out_crypto_blkdev) {
    auto crypto_type = get_crypto_type();
    if (key.size() != crypto_type.get_keysize()) {
        SLOGE("Raw keysize %zu does not match crypt keysize %zu", key.size(),
              crypto_type.get_keysize());
        return -1;
    }
    uint64_t nr_sec = 0;
    if (android::vold::GetBlockDev512Sectors(real_blkdev, &nr_sec) != android::OK) {
        SLOGE("Failed to get size of %s: %s", real_blkdev, strerror(errno));
        return -1;
    }

    constexpr char DM_CRYPT_SECTOR_SIZE[] = "ro.crypto.fde_sector_size";
    char value[PROPERTY_VALUE_MAX];
    unsigned int sector_size = 0;

    if (property_get(DM_CRYPT_SECTOR_SIZE, value, "") > 0) {
        if (!ParseUint(value, &sector_size) || sector_size < 512 || sector_size > 4096 ||
            (sector_size & (sector_size - 1)) != 0) {
            SLOGE("Invalid value for %s: %s.  Must be >= 512, <= 4096, and a power of 2\n",
                  DM_CRYPT_SECTOR_SIZE, value);
            return -1;
        }
    }

    // Round the crypto device size down to a crypto sector boundary.
    if (sector_size > 0) {
        nr_sec &= ~((sector_size / 512) - 1);
    }

    auto& dm = DeviceMapper::Instance();
    // We need two ASCII characters to represent each byte, and need space for
    // the '\0' terminator.
    char key_ascii[MAX_KEY_LEN * 2 + 1];
    convert_key_to_hex_ascii(key, key_ascii);

    auto target = std::make_unique<DmTargetCrypt>(0, nr_sec, crypto_type.get_kernel_name(),
                                                  key_ascii, 0, real_blkdev, 0);
    target->AllowDiscards();

    if (IsFbeEnabled() &&
        android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false)) {
        target->AllowEncryptOverride();
    }

    // Append the parameters to make dm-crypt use the specified crypto sector size.
    if (sector_size > 0) {
        target->SetSectorSize(sector_size);
        // With this option, IVs will match the sector numbering, instead
        // of being hard-coded to being based on 512-byte sectors.
        target->SetIvLargeSectors();
    }

    DmTable table;
    table.AddTarget(std::move(target));

    int load_count = 1;
    while (load_count < TABLE_LOAD_RETRIES) {
        if (dm.CreateDevice(label, table)) {
            break;
        }
        load_count++;
    }

    if (load_count >= TABLE_LOAD_RETRIES) {
        SLOGE("Cannot load dm-crypt mapping table.\n");
        return -1;
    }
    if (load_count > 1) {
        SLOGI("Took %d tries to load dmcrypt table.\n", load_count);
    }

    if (!dm.GetDmDevicePathByName(label, out_crypto_blkdev)) {
        SLOGE("Cannot determine dm-crypt path for %s.\n", label);
        return -1;
    }

    /* Ensure the dm device has been created before returning. */
    if (android::vold::WaitForFile(out_crypto_blkdev->c_str(), 1s) < 0) {
        // WaitForFile generates a suitable log message
        return -1;
    }
    return 0;
}