summaryrefslogtreecommitdiff
path: root/trusty_secure_deletion_secret_storage.cpp
blob: 33274239c329ecd935aa786893af50a797b4abbd (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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
/*
 * Copyright (C) 2021 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.
 */

#include "trusty_secure_deletion_secret_storage.h"

#include <inttypes.h>

#include <array>
#include <optional>
#include <vector>

#include <lib/storage/storage.h>
#include <uapi/err.h>

#include <keymaster/logger.h>
#include <keymaster/random_source.h>

namespace keymaster {

namespace {

// Maximum number of attempts to perform a secure storage transaction to read or
// delete a secure deletion secret.  Because the storageproxy may be restarted
// while this code is running, it may be necessary to retry.  But because it's
// unclear exactly what error codes may be returned when the proxy is shut down,
// we conservatively retry all unexpected errors.  To avoid an infinite loop, we
// set a limit on the number of retries (though hitting the limit and returning
// an error will likely break the boot anyway).  Ideally, we should never need
// more than one retry.  We allow three.
constexpr size_t kMaxTries = 3;

// Name of the file to store secrets. The "_1" suffix is to allow for new file
// formats/versions in the future.
constexpr char kSecureDeletionSecretFileName[] = "SecureDeletionSecrets_1";

// Each secret is 16 bytes.
constexpr storage_off_t kSecretSize = 16;

//  The factory reset secret is composed of two secrets, so 32 bytes, and it's
//  stored at offset 0.
constexpr storage_off_t kFactoryResetSecretSize = kSecretSize * 2;
constexpr storage_off_t kFactoryResetSecretPos = 0;
constexpr storage_off_t kFirstSecureDeletionSecretPos =
        kFactoryResetSecretPos + kFactoryResetSecretSize;

// We read secrets in blocks of 32, so 512 bytes.
constexpr storage_off_t kBlockSize = kSecretSize * 32;

// Limit file size to 16 KiB (except for key upgrades, see
// kMaxSecretFileSizeForUpgrades).
constexpr storage_off_t kMaxSecretFileSize = kBlockSize * 32;

// This is a higher file size limit, with the space above kMaxSecretFileSize
// usable only for key IDs that need to be written as part of a key upgrade.
// This is to reduce the probability that keys are degraded as a result of
// upgrading.
constexpr storage_off_t kMaxSecretFileSizeForUpgrades =
        kMaxSecretFileSize + 8 * kBlockSize;

// We set a bit in the first byte of each slot to indicate that the slot is in
// use.  This reduces the maximum entropy of each slot to 127 bits.
constexpr uint8_t kInUseFlag = 0x80;

/**
 * StorageFile represents a secure storage file, and provides operations on it.
 * Use StorageSession::OpenFile to create a StorageFile.
 */
class StorageFile {
public:
    StorageFile(const StorageFile&) = delete;
    StorageFile& operator=(const StorageFile&) = delete;

    StorageFile(StorageFile&& rhs)
            : fileHandle_(std::move(rhs.fileHandle_)),
              fileSize_(rhs.fileSize_) {
        rhs.fileHandle_ = std::nullopt;
        rhs.fileSize_ = 0;
    }

    ~StorageFile() { CloseFile(); }

    /**
     * Close the file.
     *
     * Note that normally it's not necessary to call this, because the dtor
     * will.
     */
    void CloseFile() {
        if (!fileHandle_) {
            return;
        }

        LOG_D("Closing file handle %" PRIu64, *fileHandle_);
        storage_close_file(*fileHandle_);
        fileHandle_ = std::nullopt;
    }

    /**
     * Return size of file.
     */
    storage_off_t size() const { return fileSize_; }

    /**
     * Reads a block of bytes from the file.  On error returns std::nullopt.
     */
    std::optional<Buffer> ReadBlock(storage_off_t readPos,
                                    storage_off_t bytesToRead) const {
        if (!fileHandle_) {
            return std::nullopt;
        }

        Buffer buf(bytesToRead);
        if (buf.buffer_size() < bytesToRead) {
            LOG_E("Error memory allocation failed trying to allocate ReadBlock buffer.");
            return std::nullopt;
        }

        ssize_t bytesRead = storage_read(
                *fileHandle_, readPos, buf.peek_write(), buf.available_write());
        if (bytesRead < 0) {
            LOG_E("Error %zd reading file", bytesRead);
            return std::nullopt;
        } else if (static_cast<size_t>(bytesRead) < bytesToRead) {
            LOG_E("Error attempt to read %" PRIu64
                  " bytes returned only %zd bytes",
                  bytesToRead, bytesRead);
            return std::nullopt;
        }

        if (!buf.advance_write(bytesRead)) {
            LOG_E("Failed to update buffer write position. Code error.");
            return std::nullopt;
        }
        return buf;
    }

    /**
     * Write a block of bytes from `data` of size `size` to storage at offset
     * `pos`.  Will not extend the file.
     *
     * Returns false if the write would go past the end of the file, or if an
     * error occurs (all errors are logged).
     */
    bool WriteBlock(storage_off_t pos, const uint8_t* data, size_t size) const {
        if (!fileHandle_) {
            LOG_E("Attempt to write to invalid file handle");
            return false;
        }

        storage_off_t end;
        if (__builtin_add_overflow(pos, size, &end) || end > fileSize_) {
            LOG_E("Attempt to write past EOF");
            return false;
        }

        ssize_t bytesWritten =
                storage_write(*fileHandle_, pos, data, size, 0 /* opflags */);
        if (bytesWritten < 0) {
            LOG_E("Error %zd writing rollback record at offset %" PRIu64,
                  bytesWritten, pos);
            return false;
        } else if (static_cast<size_t>(bytesWritten) < size) {
            LOG_E("Wrote %zd of %zu bytes", bytesWritten, size);
            return false;
        }

        return true;
    }

    /**
     * Resize the file to `newSize` bytes.
     *
     * Returns error code from uapi/err.h.  Errors are logged.
     */
    int Resize(storage_off_t newSize) {
        if (!fileHandle_) {
            LOG_E("Attempt to resize invalid file handle");
            return ERR_NOT_VALID;
        }

        int rc = storage_set_file_size(*fileHandle_, newSize, 0 /* opflags */);
        if (rc) {
            LOG_E("Error %d resizing file from %" PRIu64 " to %" PRIu64, rc,
                  fileSize_, newSize);
            return rc;
        }

        fileSize_ = newSize;
        return NO_ERROR;
    }

private:
    friend class StorageSession;

    StorageFile(file_handle_t fileHandle, storage_off_t fileSize)
            : fileHandle_(fileHandle), fileSize_(fileSize) {}

    std::optional<file_handle_t> fileHandle_;
    storage_off_t fileSize_;
};

/**
 * StorageSession represents a secure storage session, and provides methods to
 * manipulate files within the session, and to finalize a storage transaction.
 */
class StorageSession {
public:
    // Error codes used by DeleteFile.
    enum class Error : uint32_t {
        OK = 0,
        NOT_FOUND = 1,
        UNKNOWN = 2,
    };

    StorageSession(const StorageSession&) = delete;
    StorageSession(StorageSession&& rhs) : session_(rhs.session_) {
        rhs.session_ = STORAGE_INVALID_SESSION;
    }
    ~StorageSession() {
        if (session_ != STORAGE_INVALID_SESSION) {
            LOG_D("Closing storage session %" PRIu32, session_);
            storage_close_session(session_);
        }
    }

    /**
     * Creates a session, connected to the specified port.
     *
     * There is a possibility that the port has not been created when this
     * method is called.  In this case its behavior is determined by the
     * `waitForPort` argument:
     *
     * - if true, the method will block until the port is available and the
     *   connection is completed.
     *
     * - if false, the method will return std::nullopt immediately.
     *
     * If the port exists, this method will block until the connection is
     * completed.
     */
    static std::optional<StorageSession> CreateSession(
            bool waitForPort = true,
            const char* port = STORAGE_CLIENT_TP_PORT) {
        LOG_D("Opening storage session on port %s (wait: %s)", port,
              waitForPort ? "true" : "false");
        // We use connect rather than storage_open_session because the latter
        // always waits for the port.
        long rc = connect(port, waitForPort ? IPC_CONNECT_WAIT_FOR_PORT : 0);
        if (rc < 0) {
            LOG_E("Error %ld opening storage session on port %s.", rc, port);
            return std::nullopt;
        }
        storage_session_t session = static_cast<storage_session_t>(rc);
        LOG_D("Opened storage session %" PRIu32, session);
        return StorageSession(session);
    }

    /**
     * Open a file.  Returns std::nullopt on failure (after logging error code).
     */
    std::optional<StorageFile> OpenFile(
            const char* fileName,
            uint32_t flags = STORAGE_FILE_OPEN_CREATE) const {
        file_handle_t fileHandle;
        int err = storage_open_file(session_, &fileHandle, fileName, flags,
                                    0 /* opflags */);
        if (err) {
            LOG_E("Error %d opening file %s", err, fileName);
            return std::nullopt;
        }
        LOG_D("Opened file %s with handle %" PRIu64, fileName, fileHandle);

        storage_off_t fileSize;
        err = storage_get_file_size(fileHandle, &fileSize);
        if (err) {
            LOG_E("Error %d reading size of file %s", err, fileName);
            storage_close_file(fileHandle);
            return std::nullopt;
        }
        return StorageFile(fileHandle, fileSize);
    }

    /**
     * Delete a file.
     *
     * Returns Error::OK on success, Error::NOT_FOUND, if the file did not
     * exist, Error::UNKNOWN in any other case.
     */
    Error DeleteFile(const char* fileName) const {
        int rc = storage_delete_file(session_, fileName, 0 /* opflags */);
        if (rc < 0) {
            LOG_E("Error (%d) deleting file %s", rc, fileName);
            if (rc == ERR_NOT_FOUND) {
                return Error::NOT_FOUND;
            } else {
                return Error::UNKNOWN;
            }
        }
        return Error::OK;
    }

    /**
     * Ends current transaction (any uncommitted changes in this session),
     * either committing or aborting (rolling back) the changes, as specified by
     * `commit`.
     *
     * Note failing to commit the transaction will result in it being lost.
     *
     * Returns true on success, false on failure.
     */
    bool EndTransaction(bool commit) {
        int rc = storage_end_transaction(session_, commit);
        if (rc < 0) {
            LOG_E("Error (%d) committing transaction", rc);
            return false;
        }
        return true;
    }

private:
    StorageSession(storage_session_t session) : session_(session) {}

    storage_session_t session_;
};

/**
 * Zeros each secret from position `begin` to position `end`.
 */
bool zero_entries(const StorageFile& file,
                  storage_off_t begin,
                  storage_off_t end) {
    if (begin % kSecretSize != 0) {
        LOG_S("zero_entries called with invalid offset %" PRIu64, begin);
        return false;
    }

    for (storage_off_t pos = begin; pos < end; pos += kSecretSize) {
        uint8_t zero_buf[kSecretSize] = {0x00, 0x00, 0x00, 0x00,  //
                                         0x00, 0x00, 0x00, 0x00,  //
                                         0x00, 0x00, 0x00, 0x00,  //
                                         0x00, 0x00, 0x00, 0x00};
        if (!file.WriteBlock(pos, zero_buf, sizeof(zero_buf))) {
            LOG_E("Failed to zero secret at offset %" PRIu64, pos);
            return false;
        }
    }

    return true;
}

/**
 * Finds an empty slot in file.  Returns empty on error, 0 on failure to
 * find an empty slot and a valid (>0) slot number otherwise.
 */
std::optional<uint32_t /* keySlot */> find_empty_slot(const StorageFile& file,
                                                      bool isUpgrade) {
    storage_off_t end =
            std::min(file.size(), isUpgrade ? kMaxSecretFileSizeForUpgrades
                                            : kMaxSecretFileSize);

    uint32_t retval = 0;
    for (storage_off_t filePos = 0; filePos < end; filePos += kBlockSize) {
        std::optional<Buffer> block = file.ReadBlock(filePos, kBlockSize);
        if (!block) {
            LOG_E("Failed to read block of secrets");
            return std::nullopt;
        }

        size_t blockPos =
                filePos == kFactoryResetSecretPos ? kFactoryResetSecretSize : 0;
        for (; blockPos < block->available_read(); blockPos += kSecretSize) {
            uint8_t first_byte = *(block->begin() + blockPos);
            if ((first_byte & kInUseFlag) == 0 && retval == 0) {
                static_assert(kBlockSize % kSecretSize == 0 &&
                              kFactoryResetSecretSize % kSecretSize == 0);
                retval = static_cast<uint32_t>((filePos + blockPos) /
                                               kSecretSize);
            }
        }
    }

    return retval;
}

// Helper class that calls the provided function (probably a lambda) on
// destruction if not disarmed.
template <typename F>
class OnExit {
public:
    OnExit(F f) : f_(f) {}
    ~OnExit() {
        if (!disarmed_) {
            f_();
        }
    }

    void disarm() { disarmed_ = true; }

private:
    F f_;
    bool disarmed_ = false;
};

}  // namespace

bool TrustySecureDeletionSecretStorage::LoadOrCreateFactoryResetSecret(
        bool wait_for_port) const {
    if (factory_reset_secret_) {
        // Already read.
        return true;
    }

    LOG_D("Trying to open a session to read factory reset secret");
    std::optional<StorageSession> session =
            StorageSession::CreateSession(wait_for_port);
    if (!session) {
        return false;
    }

    LOG_D("Trying to open secure secrets file");
    std::optional<StorageFile> file =
            session->OpenFile(kSecureDeletionSecretFileName);
    if (!file) {
        // This shouldn't be possible, unless maybe the session just went away?
        LOG_E("Can't open secure secrets file.");
        return false;
    }

    if (file->size() > 0) {
        LOG_D("Opened non-empty secure secrets file.");
        std::optional<Buffer> block = file->ReadBlock(kFactoryResetSecretPos,
                                                      kFactoryResetSecretSize);
        if (!block) {
            LOG_E("Failed to read factory reset secret");
            return false;
        }

        LOG_D("Read factory-reset secret, size %zu", block->available_read());
        factory_reset_secret_ = std::move(block);
        return true;
    }

    // The file was just created.  Need to create the factory reset secret.
    LOG_I("Created new secure secrets file, size %" PRIu64, file->size());
    if (file->Resize(kBlockSize) != NO_ERROR) {
        LOG_E("Failed to grow new file from 0 to %" PRIu64 " bytes",
              kBlockSize);
        return false;
    }
    LOG_D("Resized secure secrets file to size %" PRIu64, file->size());

    static_assert(kBlockSize >= kFactoryResetSecretSize);
    Buffer buf(kFactoryResetSecretSize);
    keymaster_error_t error =
            random_.GenerateRandom(buf.peek_write(), buf.available_write());
    if (error != KM_ERROR_OK || !buf.advance_write(kFactoryResetSecretSize)) {
        LOG_E("Failed to generate %" PRIu64
              " random bytes for factory reset secret",
              kFactoryResetSecretSize);
        return false;
    }

    if (!file->WriteBlock(kFactoryResetSecretPos, buf.peek_read(),
                          buf.available_read())) {
        LOG_E("Failed to write factory reset secret");
        return false;
    }
    LOG_D("Wrote new factory reset secret.");

    if (!zero_entries(*file, kFirstSecureDeletionSecretPos /* begin */,
                      kBlockSize /* end */)) {
        LOG_E("Failed to zero secure deletion secret entries in first block");
        return false;
    }
    LOG_D("Zeroed secrets.");

    if (!session->EndTransaction(true /* commit */)) {
        LOG_E("Failed to commit transaction creating secure secrets file");
        return false;
    }
    LOG_D("Committed new secrets file.");

    LOG_I("Got factory reset secret of size %zu", buf.buffer_size());
    factory_reset_secret_ = std::move(buf);
    return true;
}

std::optional<SecureDeletionData>
TrustySecureDeletionSecretStorage::CreateDataForNewKey(bool secure_deletion,
                                                       bool is_upgrade) const {
    if (!LoadOrCreateFactoryResetSecret(false /* wait_for_port */) ||
        !factory_reset_secret_) {
        // Unable to get factory reset secret from secure storage.
        LOG_I("Unable to get factory reset secret");
        return std::nullopt;
    }

    SecureDeletionData retval;
    retval.factory_reset_secret.Reinitialize(*factory_reset_secret_);

    if (!secure_deletion) {
        LOG_D("Secure deletion not requested.");
        return retval;
    }

    retval.secure_deletion_secret.reserve(kSecretSize);
    if (retval.secure_deletion_secret.buffer_size() == 0) {
        return std::nullopt;
    }

    keymaster_error_t error = random_.GenerateRandom(
            retval.secure_deletion_secret.peek_write(),
            retval.secure_deletion_secret.available_write());
    if (error != KM_ERROR_OK) {
        // Ths really shouldn't be possible.  Perhaps we should abort()?
        LOG_E("Failed to create secure deletion secret");
        return std::nullopt;
    }
    retval.secure_deletion_secret.peek_write()[0] |= kInUseFlag;
    retval.secure_deletion_secret.advance_write(
            retval.secure_deletion_secret.available_write());

    auto sds_cleanup = OnExit([&]() { retval.secure_deletion_secret.Clear(); });

    std::optional<StorageSession> session =
            StorageSession::CreateSession();  // Will block

    if (!session) {
        LOG_E("Failed to open session in CreateDateForNewKey");
        return retval;
    }
    LOG_D("Opened session to store secure deletion secret.");

    std::optional<StorageFile> file =
            session->OpenFile(kSecureDeletionSecretFileName);
    if (!file) {
        LOG_E("Failed to open file in CreateDateForNewKey");
        return retval;
    }
    LOG_D("Opened file to store secure deletion secret.");

    std::optional<uint32_t> keySlot = find_empty_slot(*file, is_upgrade);
    if (!keySlot) {
        LOG_E("Error while searching for key slot");
        return retval;
    }

    if (*keySlot == 0) {
        bool can_resize =
                file->size() < kMaxSecretFileSize ||
                (is_upgrade && file->size() < kMaxSecretFileSizeForUpgrades);

        if (!can_resize) {
            LOG_E("Didn't find a slot and can't grow the file larger than %" PRIu64,
                  file->size());
            return retval;
        }

        storage_off_t old_size = file->size();
        LOG_D("Attempting to resize file from %" PRIu64 " to %" PRIu64,
              file->size(), file->size() + kBlockSize);
        int rc = file->Resize(old_size + kBlockSize);
        if (rc != NO_ERROR) {
            LOG_E("Failed (%d) to grow file to make room for a key slot", rc);
            return retval;
        }
        LOG_D("Resized file to %" PRIu64, file->size());

        if (!zero_entries(*file, old_size, file->size())) {
            LOG_E("Error zeroing space in extended file");
            return retval;
        }

        keySlot = old_size / kSecretSize;
    }

    LOG_D("Writing new deletion secret to key slot %u", *keySlot);
    if (!file->WriteBlock(*keySlot * kSecretSize,
                          retval.secure_deletion_secret.peek_read(),
                          retval.secure_deletion_secret.available_read())) {
        LOG_E("Failed to write new deletion secret to key slot %u", *keySlot);
        return retval;
    }

    if (!session->EndTransaction(true /* commit */)) {
        LOG_E("Failed to commit transaction writing new deletion secret to slot %u",
              *keySlot);
        return retval;
    }
    LOG_D("Committed new secret.");

    sds_cleanup.disarm();  // Secure deletion secret written; no need to wipe.
    retval.key_slot = *keySlot;
    return retval;
}

SecureDeletionData TrustySecureDeletionSecretStorage::GetDataForKey(
        const uint32_t key_slot) const {
    for (size_t tries = 0; tries < kMaxTries && !factory_reset_secret_;
         ++tries) {
        LoadOrCreateFactoryResetSecret(true /* waitForPort */);
    }
    if (!factory_reset_secret_) {
        return SecureDeletionData{};
    }

    SecureDeletionData retval;
    retval.key_slot = key_slot;
    retval.factory_reset_secret.Reinitialize(*factory_reset_secret_);

    bool secureDeletionSecretRequested = (key_slot != 0);
    if (!secureDeletionSecretRequested) {
        LOG_D("Secure deletion not requested.");
        return retval;
    }

    LOG_D("Need to read secure deletion secret from slot %u", retval.key_slot);

    for (size_t tries = 0; tries < kMaxTries; ++tries) {
        std::optional<StorageSession> session =
                StorageSession::CreateSession();  // Will block
        if (!session) {
            LOG_E("Failed to open session to get secure deletion data.");
            continue;
        }

        std::optional<StorageFile> file =
                session->OpenFile(kSecureDeletionSecretFileName);
        if (!file) {
            LOG_E("Failed to open file to get secure deletion data.");
            continue;
        }

        storage_off_t keySlotBegin = retval.key_slot * kSecretSize;
        storage_off_t keySlotEnd = keySlotBegin + kSecretSize;
        if (keySlotEnd > file->size()) {
            LOG_E("Invalid key slot %u would read past end of file of size %" PRIu64,
                  retval.key_slot, file->size());
            return retval;  // Empty secure_deletion_secret, key decryption will
                            // fail.
        }

        std::optional<Buffer> secret =
                file->ReadBlock(retval.key_slot * kSecretSize, kSecretSize);
        if (!secret) {
            LOG_E("Failed to read secret from slot %u", retval.key_slot);
            continue;
        }

        LOG_D("Read secure deletion secret, size: %zu",
              secret->available_read());
        retval.secure_deletion_secret = std::move(*secret);
        break;
    }

    return retval;
}

void TrustySecureDeletionSecretStorage::DeleteKey(uint32_t key_slot) const {
    if (key_slot == 0) {
        LOG_D("key_slot == 0, nothing to delete");
        return;
    }

    for (;;) {
        std::optional<StorageSession> session =
                StorageSession::CreateSession();  // Will block
        if (!session) {
            LOG_E("Failed to open session to retrieve secure deletion data.");
            continue;
        }

        std::optional<StorageFile> file =
                session->OpenFile(kSecureDeletionSecretFileName);
        if (!file) {
            LOG_E("Failed to open file to retrieve secure deletion data.");
            continue;
        }

        storage_off_t key_slot_begin = key_slot * kSecretSize;
        storage_off_t key_slot_end = key_slot_begin + kSecretSize;
        if (key_slot_begin <
                    kFactoryResetSecretPos + kFactoryResetSecretSize  //
            || key_slot_end > file->size()) {
            LOG_E("Attempted to delete invalid key slot %u", key_slot);
            return;
        }

        if (!zero_entries(*file, key_slot_begin, key_slot_end)) {
            continue;
        }
        LOG_D("Deleted secure key slot %u, zeroing %" PRIu64 " to %" PRIu64,
              key_slot, key_slot_begin, key_slot_end);

        if (!session->EndTransaction(true /* commit */)) {
            LOG_E("Failed to commit transaction deleting key at slot %u",
                  key_slot);
            continue;
        }
        LOG_D("Committed deletion");

        return;
    }
}

void TrustySecureDeletionSecretStorage::DeleteAllKeys() const {
    for (;;) {
        std::optional<StorageSession> session =
                StorageSession::CreateSession();  // Will block
        if (!session) {
            LOG_E("Failed to open session to delete secrets file.");
            continue;
        }
        LOG_D("Opened session to delete secrets file.");

        auto error = session->DeleteFile(kSecureDeletionSecretFileName);
        if (error == StorageSession::Error::OK) {
            LOG_D("Deleted secrets file");

            if (!session->EndTransaction(true /* commit */)) {
                LOG_E("Failed to commit deletion of secrets file.");
            }
            LOG_D("Committed deletion of secrets file.");
        } else if (error == StorageSession::Error::NOT_FOUND) {
            // File does not exist, may as well abandon the session.
            LOG_D("No secrets file existed.");
        } else {
            // Assuming transient error. Log and retry.
            LOG_E("Failed to delete secrets file");
            continue;
        }

        // Success
        factory_reset_secret_ = {};
        return;
    }
}

}  // namespace keymaster