summaryrefslogtreecommitdiffstats
path: root/auth_encrypted_key_blob.cpp
blob: 655bc12760e6ef75141765002a50e9e0095029c0 (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
/*
 * Copyright 2015 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 "auth_encrypted_key_blob.h"

#include <keymaster/android_keymaster_utils.h>
#include <keymaster/authorization_set.h>
#include <keymaster/logger.h>

#include "ocb_utils.h"

namespace keymaster {

const uint32_t CURRENT_BLOB_VERSION = 0;

keymaster_error_t SerializeAuthEncryptedBlob(const KeymasterKeyBlob& encrypted_key_material,
                                             const AuthorizationSet& hw_enforced,
                                             const AuthorizationSet& sw_enforced,

                                             const Buffer& nonce, const Buffer& tag,
                                             KeymasterKeyBlob* key_blob) {
    size_t size = 1 /* version byte */ + nonce.SerializedSize() +
                  encrypted_key_material.SerializedSize() + tag.SerializedSize() +
                  hw_enforced.SerializedSize() + sw_enforced.SerializedSize();

    if (!key_blob->Reset(size))
        return KM_ERROR_MEMORY_ALLOCATION_FAILED;

    uint8_t* buf = key_blob->writable_data();
    const uint8_t* end = key_blob->key_material + key_blob->key_material_size;

    *buf++ = CURRENT_BLOB_VERSION;
    buf = nonce.Serialize(buf, end);
    buf = encrypted_key_material.Serialize(buf, end);
    buf = tag.Serialize(buf, end);
    buf = hw_enforced.Serialize(buf, end);
    buf = sw_enforced.Serialize(buf, end);
    if (buf != key_blob->key_material + key_blob->key_material_size)
        return KM_ERROR_UNKNOWN_ERROR;

    return KM_ERROR_OK;
}

static keymaster_error_t DeserializeUnversionedBlob(const KeymasterKeyBlob& key_blob,
                                                    KeymasterKeyBlob* encrypted_key_material,
                                                    AuthorizationSet* hw_enforced,
                                                    AuthorizationSet* sw_enforced, Buffer* nonce,
                                                    Buffer* tag) {
    const uint8_t* tmp = key_blob.key_material;
    const uint8_t** buf_ptr = &tmp;
    const uint8_t* end = tmp + key_blob.key_material_size;

    if (!nonce->reserve(OCB_NONCE_LENGTH) || !tag->reserve(OCB_TAG_LENGTH))
        return KM_ERROR_MEMORY_ALLOCATION_FAILED;

    if (!copy_from_buf(buf_ptr, end, nonce->peek_write(), OCB_NONCE_LENGTH) ||
        !encrypted_key_material->Deserialize(buf_ptr, end) ||
        !copy_from_buf(buf_ptr, end, tag->peek_write(), OCB_TAG_LENGTH) ||
        !hw_enforced->Deserialize(buf_ptr, end) ||  //
        !sw_enforced->Deserialize(buf_ptr, end)) {
        LOG_D("Failed to deserialize unversioned blob (may be a HW-backed key)", 0);
        return KM_ERROR_INVALID_KEY_BLOB;
    }
    if (!nonce->advance_write(OCB_NONCE_LENGTH) || !tag->advance_write(OCB_TAG_LENGTH))
        return KM_ERROR_UNKNOWN_ERROR;
    return KM_ERROR_OK;
}

keymaster_error_t DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob,
                                               KeymasterKeyBlob* encrypted_key_material,
                                               AuthorizationSet* hw_enforced,
                                               AuthorizationSet* sw_enforced, Buffer* nonce,
                                               Buffer* tag) {
    if (!key_blob.key_material || key_blob.key_material_size == 0)
        return KM_ERROR_INVALID_KEY_BLOB;

    const uint8_t* tmp = key_blob.key_material;
    const uint8_t** buf_ptr = &tmp;
    const uint8_t* end = tmp + key_blob.key_material_size;

    if (end <= *buf_ptr)
        return KM_ERROR_INVALID_KEY_BLOB;

    uint8_t version = *(*buf_ptr)++;
    if (version != CURRENT_BLOB_VERSION ||  //
        !nonce->Deserialize(buf_ptr, end) || nonce->available_read() != OCB_NONCE_LENGTH ||
        !encrypted_key_material->Deserialize(buf_ptr, end) ||  //
        !tag->Deserialize(buf_ptr, end) || tag->available_read() != OCB_TAG_LENGTH ||
        !hw_enforced->Deserialize(buf_ptr, end) ||  //
        !sw_enforced->Deserialize(buf_ptr, end)) {
        // This blob failed to parse.  Either it's corrupted or it's a blob generated by an earlier
        // version of keymaster using a previous blob format which did not include the version byte
        // or the nonce or tag length fields.  So we try to parse it as that previous version.
        //
        // Note that it's not really a problem if we erronously parse a corrupted blob, because
        // decryption will fail the authentication check.
        //
        // A bigger potential problem is: What if a valid unversioned blob appears to parse
        // correctly as a versioned blob?  It would then be rejected during decryption, causing a
        // valid key to become unusable.  If this is a disk encryption key, upgrading to a keymaster
        // version with the new format would destroy the user's data.
        //
        // What is the probability that an unversioned key could be successfully parsed as a version
        // 0 key?  The first 12 bytes of an unversioned key are the nonce, which, in the only
        // keymaster version released with unversioned keys, is chosen randomly.  In order for an
        // unversioned key to parse as a version 0 key, the following must be true about the first
        // five of those random bytes:
        //
        // 1.  The first byte must be zero.  This will happen with probability 1/2^8.
        //
        // 2.  The second through fifth bytes must contain an unsigned integer value equal to
        //     NONCE_LENGTH.  This will happen with probability 1/2^32.
        //
        // Based on those two checks alone, the probability of interpreting an unversioned blob as a
        // version 0 blob is 1/2^40.  That's small enough to be negligible, but there are additional
        // checks which lower it further.
        LOG_D("Failed to deserialize versioned key blob.  Assuming unversioned.", 0);
        return DeserializeUnversionedBlob(key_blob, encrypted_key_material, hw_enforced,
                                          sw_enforced, nonce, tag);
    }
    return KM_ERROR_OK;
}

}  // namespace keymaster