summaryrefslogtreecommitdiff
path: root/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
blob: df6b0f8339d713ce44f4d2110058bd7c3a460783 (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
/*
 * Copyright (C) 2020 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 <errno.h>
#include <getopt.h>
#include <libxml/xmlreader.h>
#include <openssl/pem.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string>

using std::string;

#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>

static const char* _sopts = "h";
static const struct option _lopts[] = {
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0},
};

static const char* usage =
        "Usage: %s [options] xml-file\n"
        "\n"
        "options:\n"
        "  -h, --help            prints this message and exit\n"
        "\n";

static void print_usage_and_exit(const char* prog, int code) {
    fprintf(stderr, usage, prog);
    exit(code);
}

static void parse_options(int argc, char** argv) {
    int c;
    int oidx = 0;

    while (1) {
        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
        if (c == -1) {
            break; /* done */
        }

        switch (c) {
            case 'h':
                print_usage_and_exit(argv[0], EXIT_SUCCESS);
                break;

            default:
                print_usage_and_exit(argv[0], EXIT_FAILURE);
        }
    }
}

struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
    explicit SetAttestationKeyRequest(int32_t ver = keymaster::kDefaultMessageVersion)
        : KeymasterMessage(ver) {}

    size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
        buf = keymaster::append_uint32_to_buf(buf, end, algorithm);
        return key_data.Serialize(buf, end);
    }
    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
        return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm) &&
               key_data.Deserialize(buf_ptr, end);
    }

    keymaster_algorithm_t algorithm;
    keymaster::Buffer key_data;
};

struct KeymasterNoResponse : public keymaster::KeymasterResponse {
    explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)
        : keymaster::KeymasterResponse(ver) {}

    size_t NonErrorSerializedSize() const override { return 0; }
    uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }
    bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }
};

struct SetAttestationKeyResponse : public KeymasterNoResponse {};

struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
    explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::kDefaultMessageVersion)
        : KeymasterMessage(ver) {}

    size_t SerializedSize() const override { return sizeof(uint32_t); }
    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
        return keymaster::append_uint32_to_buf(buf, end, algorithm);
    }
    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
        return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm);
    }

    keymaster_algorithm_t algorithm;
};

struct ClearAttestationCertChainResponse : public KeymasterNoResponse {};

static int set_attestation_key_or_cert_bin(uint32_t cmd, keymaster_algorithm_t algorithm,
                                           const void* key_data, size_t key_data_size) {
    int ret;

    SetAttestationKeyRequest req;
    req.algorithm = algorithm;
    req.key_data.Reinitialize(key_data, key_data_size);
    SetAttestationKeyResponse rsp;

    ret = trusty_keymaster_send(cmd, req, &rsp);
    if (ret) {
        fprintf(stderr, "trusty_keymaster_send cmd 0x%x failed %d\n", cmd, ret);
        return ret;
    }

    return 0;
}

static int set_attestation_key_or_cert_pem(uint32_t cmd, keymaster_algorithm_t algorithm,
                                           const xmlChar* pemkey) {
    int ret;
    int sslret;

    /* Convert from pem to binary */
    BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));
    if (!bio) {
        fprintf(stderr, "BIO_new_mem_buf failed\n");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    char* key_name;
    char* key_header;
    uint8_t* key;
    long keylen;
    sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);
    BIO_free(bio);

    if (!sslret) {
        fprintf(stderr, "PEM_read_bio failed\n");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    /* Send key in binary format to trusty */
    ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);

    OPENSSL_free(key_name);
    OPENSSL_free(key_header);
    OPENSSL_free(key);

    return ret;
}

static int set_attestation_key_or_cert_iecs(uint32_t cmd, keymaster_algorithm_t algorithm,
                                            const xmlChar* key_base64) {
    int ret;
    int sslret;

    /* Remove all whitespace. EVP_DecodeBase64 does not support whitespace. */
    string key_base64_str((const char*)key_base64);
    key_base64_str.erase(remove_if(key_base64_str.begin(), key_base64_str.end(), isspace),
                         key_base64_str.end());

    /* Convert from base64 to binary */
    uint8_t* key;
    size_t keylen;
    size_t key_base64_len = key_base64_str.length();

    sslret = EVP_DecodedLength(&keylen, key_base64_len);
    if (!sslret) {
        fprintf(stderr, "invalid input length, %zu\n", key_base64_len);
        return -1;
    }
    key = (uint8_t*)malloc(keylen);
    if (!key) {
        fprintf(stderr, "failed to allocate key, size %zu\n", key_base64_len);
        return -1;
    }
    sslret = EVP_DecodeBase64(key, &keylen, keylen, (const uint8_t*)key_base64_str.data(),
                              key_base64_len);
    if (!sslret) {
        fprintf(stderr, "EVP_DecodeBase64 failed\n");
        ERR_print_errors_fp(stderr);
        free(key);
        return -1;
    }

    /* Send key in binary format to trusty */
    ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);

    free(key);

    return ret;
}

static int str_to_algorithm(keymaster_algorithm_t* algorithm, const xmlChar* algorithm_str) {
    if (xmlStrEqual(algorithm_str, BAD_CAST "rsa")) {
        *algorithm = KM_ALGORITHM_RSA;
    } else if (xmlStrEqual(algorithm_str, BAD_CAST "ecdsa")) {
        *algorithm = KM_ALGORITHM_EC;
    } else {
        printf("unsupported algorithm: %s\n", algorithm_str);
        return -1;
    }
    return 0;
}

static int set_attestation_key_or_cert(uint32_t cmd, const xmlChar* algorithm_str,
                                       const xmlChar* format, const xmlChar* str) {
    int ret;
    keymaster_algorithm_t algorithm;

    ret = str_to_algorithm(&algorithm, algorithm_str);
    if (ret) {
        return ret;
    }

    if (xmlStrEqual(format, BAD_CAST "pem")) {
        ret = set_attestation_key_or_cert_pem(cmd, algorithm, str);
    } else if (xmlStrEqual(format, BAD_CAST "iecs")) {
        ret = set_attestation_key_or_cert_iecs(cmd, algorithm, str);
    } else {
        printf("unsupported key/cert format: %s\n", format);
        return -1;
    }
    return ret;
}

static int clear_cert_chain(const xmlChar* algorithm_str) {
    int ret;
    ClearAttestationCertChainRequest req;
    ClearAttestationCertChainResponse rsp;

    ret = str_to_algorithm(&req.algorithm, algorithm_str);
    if (ret) {
        return ret;
    }

    ret = trusty_keymaster_send(KM_CLEAR_ATTESTATION_CERT_CHAIN, req, &rsp);
    if (ret) {
        fprintf(stderr, "%s: trusty_keymaster_send failed %d\n", __func__, ret);
        return ret;
    }
    return 0;
}

static int process_xml(xmlTextReaderPtr xml) {
    int ret;
    const xmlChar* algorithm = NULL;
    const xmlChar* element = NULL;
    const xmlChar* element_format = NULL;

    while ((ret = xmlTextReaderRead(xml)) == 1) {
        int nodetype = xmlTextReaderNodeType(xml);
        const xmlChar *name, *value;
        name = xmlTextReaderConstName(xml);
        switch (nodetype) {
            case XML_READER_TYPE_ELEMENT:
                element = name;
                element_format = xmlTextReaderGetAttribute(xml, BAD_CAST "format");
                if (xmlStrEqual(name, BAD_CAST "Key")) {
                    algorithm = xmlTextReaderGetAttribute(xml, BAD_CAST "algorithm");
                } else if (xmlStrEqual(name, BAD_CAST "CertificateChain")) {
                    ret = clear_cert_chain(algorithm);
                    if (ret) {
                        fprintf(stderr, "%s, algorithm %s: Clear cert chain cmd failed, %d\n",
                                element, algorithm, ret);
                        return ret;
                    }
                    printf("%s, algorithm %s: Clear cert chain cmd done\n", element, algorithm);
                }
                break;
            case XML_READER_TYPE_TEXT:
                value = xmlTextReaderConstValue(xml);
                uint32_t cmd;
                if (xmlStrEqual(element, BAD_CAST "PrivateKey")) {
                    if (xmlStrEqual(element_format, BAD_CAST "pem")) {
                        cmd = KM_SET_ATTESTATION_KEY;
                    } else if (xmlStrEqual(element_format, BAD_CAST "iecs")) {
                        cmd = KM_SET_WRAPPED_ATTESTATION_KEY;
                    } else {
                        printf("unsupported key format: %s\n", element_format);
                        return -1;
                    }
                } else if (xmlStrEqual(element, BAD_CAST "Certificate")) {
                    cmd = KM_APPEND_ATTESTATION_CERT_CHAIN;
                } else {
                    break;
                }

                ret = set_attestation_key_or_cert(cmd, algorithm, element_format, value);
                if (ret) {
                    fprintf(stderr, "%s, algorithm %s, format %s: Cmd 0x%x failed, %d\n", element,
                            algorithm, element_format, cmd, ret);
                    return ret;
                }
                printf("%s, algorithm %s, format %s: Cmd 0x%x done\n", element, algorithm,
                       element_format, cmd);
                break;
            case XML_READER_TYPE_END_ELEMENT:
                element = NULL;
                break;
        }
    }
    return ret;
}

static int parse_xml_file(const char* filename) {
    int ret;
    xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);
    if (!xml) {
        fprintf(stderr, "failed to open %s\n", filename);
        return -1;
    }

    ret = process_xml(xml);

    xmlFreeTextReader(xml);
    if (ret != 0) {
        fprintf(stderr, "Failed to parse or process %s\n", filename);
        return -1;
    }

    return 0;
}

int main(int argc, char** argv) {
    int ret = 0;

    parse_options(argc, argv);
    if (optind + 1 != argc) {
        print_usage_and_exit(argv[0], EXIT_FAILURE);
    }

    ret = trusty_keymaster_connect();
    if (ret) {
        fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
    } else {
        ret = parse_xml_file(argv[optind]);
        trusty_keymaster_disconnect();
    }

    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}