blob: 7b6671d73db595b2ae88f9ee6601817e4aa39801 [file] [log] [blame]
Benoit Gobyd5fcafa2012-04-12 12:23:49 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Yabin Cuiaed3c612015-09-22 15:52:57 -070017#define TRACE_TAG AUTH
Dan Albert33134262015-03-19 15:21:08 -070018
19#include "sysdeps.h"
20#include "adb_auth.h"
Yurii Zubrytskyia9e2b992016-05-25 15:17:10 -070021#include "adb_utils.h"
Dan Albert33134262015-03-19 15:21:08 -070022
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070023#include <stdio.h>
Christopher Ferris67a7a4a2014-11-06 14:34:24 -080024#include <stdlib.h>
Dan Albert33134262015-03-19 15:21:08 -070025#include <string.h>
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070026
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070027#include "adb.h"
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070028
29/* HACK: we need the RSAPublicKey struct
30 * but RSA_verify conflits with openssl */
31#define RSA_verify RSA_verify_mincrypt
32#include "mincrypt/rsa.h"
33#undef RSA_verify
34
David Pursell5f787ed2016-01-27 08:52:53 -080035#include <android-base/errors.h>
Yurii Zubrytskyi049ebb82016-05-26 09:46:10 -070036#include <android-base/stringprintf.h>
Elliott Hughes4f713192015-12-04 22:00:26 -080037#include <android-base/strings.h>
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070038#include <cutils/list.h>
39
40#include <openssl/evp.h>
41#include <openssl/objects.h>
42#include <openssl/pem.h>
43#include <openssl/rsa.h>
44#include <openssl/sha.h>
45
Adam Langley179d9d62014-09-03 14:34:47 -070046#if defined(OPENSSL_IS_BORINGSSL)
47#include <openssl/base64.h>
48#endif
49
Dan Albert286bb6d2015-07-09 20:35:09 +000050#define ANDROID_PATH ".android"
51#define ADB_KEY_FILE "adbkey"
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070052
Benoit Gobyd5fcafa2012-04-12 12:23:49 -070053struct adb_private_key {
54 struct listnode node;
55 RSA *rsa;
56};
57
58static struct listnode key_list;
59
60
61/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
62static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
63{
64 int ret = 1;
65 unsigned int i;
66
67 BN_CTX* ctx = BN_CTX_new();
68 BIGNUM* r32 = BN_new();
69 BIGNUM* rr = BN_new();
70 BIGNUM* r = BN_new();
71 BIGNUM* rem = BN_new();
72 BIGNUM* n = BN_new();
73 BIGNUM* n0inv = BN_new();
74
75 if (RSA_size(rsa) != RSANUMBYTES) {
76 ret = 0;
77 goto out;
78 }
79
80 BN_set_bit(r32, 32);
81 BN_copy(n, rsa->n);
82 BN_set_bit(r, RSANUMWORDS * 32);
83 BN_mod_sqr(rr, r, n, ctx);
84 BN_div(NULL, rem, n, r32, ctx);
85 BN_mod_inverse(n0inv, rem, r32, ctx);
86
87 pkey->len = RSANUMWORDS;
88 pkey->n0inv = 0 - BN_get_word(n0inv);
89 for (i = 0; i < RSANUMWORDS; i++) {
90 BN_div(rr, rem, rr, r32, ctx);
91 pkey->rr[i] = BN_get_word(rem);
92 BN_div(n, rem, n, r32, ctx);
93 pkey->n[i] = BN_get_word(rem);
94 }
95 pkey->exponent = BN_get_word(rsa->e);
96
97out:
98 BN_free(n0inv);
99 BN_free(n);
100 BN_free(rem);
101 BN_free(r);
102 BN_free(rr);
103 BN_free(r32);
104 BN_CTX_free(ctx);
105
106 return ret;
107}
108
109static void get_user_info(char *buf, size_t len)
110{
111 char hostname[1024], username[1024];
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800112 int ret = -1;
113
114 if (getenv("HOSTNAME") != NULL) {
115 strncpy(hostname, getenv("HOSTNAME"), sizeof(hostname));
116 hostname[sizeof(hostname)-1] = '\0';
117 ret = 0;
118 }
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700119
120#ifndef _WIN32
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700121 if (ret < 0)
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800122 ret = gethostname(hostname, sizeof(hostname));
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700123#endif
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800124 if (ret < 0)
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700125 strcpy(hostname, "unknown");
126
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800127 ret = -1;
128
129 if (getenv("LOGNAME") != NULL) {
130 strncpy(username, getenv("LOGNAME"), sizeof(username));
131 username[sizeof(username)-1] = '\0';
132 ret = 0;
133 }
134
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700135#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700136 if (ret < 0)
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800137 ret = getlogin_r(username, sizeof(username));
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700138#endif
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800139 if (ret < 0)
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700140 strcpy(username, "unknown");
141
142 ret = snprintf(buf, len, " %s@%s", username, hostname);
143 if (ret >= (signed)len)
144 buf[len - 1] = '\0';
145}
146
147static int write_public_keyfile(RSA *private_key, const char *private_key_path)
148{
149 RSAPublicKey pkey;
Adam Langley179d9d62014-09-03 14:34:47 -0700150 FILE *outfile = NULL;
Tamas Berghammer3d2904c2015-07-13 19:12:28 +0100151 char path[PATH_MAX], info[MAX_PAYLOAD_V1];
Dan Albertbac34742015-02-25 17:51:28 -0800152 uint8_t* encoded = nullptr;
Adam Langley179d9d62014-09-03 14:34:47 -0700153 size_t encoded_length;
154 int ret = 0;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700155
Adam Langley179d9d62014-09-03 14:34:47 -0700156 if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
157 (int)sizeof(path)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700158 D("Path too long while writing public key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700159 return 0;
Adam Langley179d9d62014-09-03 14:34:47 -0700160 }
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700161
Adam Langley179d9d62014-09-03 14:34:47 -0700162 if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700163 D("Failed to convert to publickey");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700164 return 0;
165 }
166
Spencer Low9b960312015-05-07 19:08:29 -0700167 outfile = fopen(path, "w");
Adam Langley179d9d62014-09-03 14:34:47 -0700168 if (!outfile) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700169 D("Failed to open '%s'", path);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700170 return 0;
171 }
172
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700173 D("Writing public key to '%s'", path);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700174
Adam Langley179d9d62014-09-03 14:34:47 -0700175#if defined(OPENSSL_IS_BORINGSSL)
176 if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700177 D("Public key too large to base64 encode");
Adam Langley179d9d62014-09-03 14:34:47 -0700178 goto out;
179 }
180#else
181 /* While we switch from OpenSSL to BoringSSL we have to implement
182 * |EVP_EncodedLength| here. */
183 encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
184#endif
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700185
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700186 encoded = new uint8_t[encoded_length];
Dan Albertbac34742015-02-25 17:51:28 -0800187 if (encoded == nullptr) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700188 D("Allocation failure");
Adam Langley179d9d62014-09-03 14:34:47 -0700189 goto out;
190 }
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700191
Adam Langley179d9d62014-09-03 14:34:47 -0700192 encoded_length = EVP_EncodeBlock(encoded, (uint8_t*) &pkey, sizeof(pkey));
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700193 get_user_info(info, sizeof(info));
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700194
Adam Langley179d9d62014-09-03 14:34:47 -0700195 if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
196 fwrite(info, strlen(info), 1, outfile) != 1) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700197 D("Write error while writing public key");
Adam Langley179d9d62014-09-03 14:34:47 -0700198 goto out;
199 }
200
201 ret = 1;
202
203 out:
204 if (outfile != NULL) {
205 fclose(outfile);
206 }
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700207 delete[] encoded;
Adam Langley179d9d62014-09-03 14:34:47 -0700208 return ret;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700209}
210
211static int generate_key(const char *file)
212{
213 EVP_PKEY* pkey = EVP_PKEY_new();
214 BIGNUM* exponent = BN_new();
215 RSA* rsa = RSA_new();
Benoit Goby64b31032012-08-31 12:14:21 -0700216 mode_t old_mask;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700217 FILE *f = NULL;
218 int ret = 0;
219
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700220 D("generate_key '%s'", file);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700221
222 if (!pkey || !exponent || !rsa) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700223 D("Failed to allocate key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700224 goto out;
225 }
226
227 BN_set_word(exponent, RSA_F4);
228 RSA_generate_key_ex(rsa, 2048, exponent, NULL);
229 EVP_PKEY_set1_RSA(pkey, rsa);
230
Benoit Goby64b31032012-08-31 12:14:21 -0700231 old_mask = umask(077);
232
Spencer Low9b960312015-05-07 19:08:29 -0700233 f = fopen(file, "w");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700234 if (!f) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700235 D("Failed to open '%s'", file);
Benoit Goby64b31032012-08-31 12:14:21 -0700236 umask(old_mask);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700237 goto out;
238 }
239
Benoit Goby64b31032012-08-31 12:14:21 -0700240 umask(old_mask);
241
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700242 if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700243 D("Failed to write key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700244 goto out;
245 }
246
247 if (!write_public_keyfile(rsa, file)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700248 D("Failed to write public key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700249 goto out;
250 }
251
252 ret = 1;
253
254out:
255 if (f)
256 fclose(f);
257 EVP_PKEY_free(pkey);
258 RSA_free(rsa);
259 BN_free(exponent);
260 return ret;
261}
262
263static int read_key(const char *file, struct listnode *list)
264{
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700265 D("read_key '%s'", file);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700266
Spencer Low9b960312015-05-07 19:08:29 -0700267 FILE* fp = fopen(file, "r");
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700268 if (!fp) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700269 D("Failed to open '%s': %s", file, strerror(errno));
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700270 return 0;
271 }
272
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700273 adb_private_key* key = new adb_private_key;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700274 key->rsa = RSA_new();
275
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700276 if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700277 D("Failed to read key");
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700278 fclose(fp);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700279 RSA_free(key->rsa);
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700280 delete key;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700281 return 0;
282 }
283
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700284 fclose(fp);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700285 list_add_tail(list, &key->node);
286 return 1;
287}
288
Dan Albert286bb6d2015-07-09 20:35:09 +0000289static int get_user_keyfilepath(char *filename, size_t len)
290{
Yurii Zubrytskyi049ebb82016-05-26 09:46:10 -0700291 const std::string home = adb_get_homedir_path(true);
Yurii Zubrytskyia9e2b992016-05-25 15:17:10 -0700292 D("home '%s'", home.c_str());
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700293
Yurii Zubrytskyi049ebb82016-05-26 09:46:10 -0700294 const std::string android_dir =
295 android::base::StringPrintf("%s%c%s", home.c_str(),
296 OS_PATH_SEPARATOR, ANDROID_PATH);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700297
Yurii Zubrytskyi049ebb82016-05-26 09:46:10 -0700298 struct stat buf;
299 if (stat(android_dir.c_str(), &buf)) {
300 if (adb_mkdir(android_dir.c_str(), 0750) < 0) {
301 D("Cannot mkdir '%s'", android_dir.c_str());
Dan Albert286bb6d2015-07-09 20:35:09 +0000302 return -1;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700303 }
304 }
305
Yurii Zubrytskyi049ebb82016-05-26 09:46:10 -0700306 return snprintf(filename, len, "%s%c%s",
307 android_dir.c_str(), OS_PATH_SEPARATOR, ADB_KEY_FILE);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700308}
309
310static int get_user_key(struct listnode *list)
311{
Dan Albert286bb6d2015-07-09 20:35:09 +0000312 struct stat buf;
313 char path[PATH_MAX];
314 int ret;
315
316 ret = get_user_keyfilepath(path, sizeof(path));
317 if (ret < 0 || ret >= (signed)sizeof(path)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700318 D("Error getting user key filename");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700319 return 0;
320 }
321
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700322 D("user key '%s'", path);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700323
Dan Albert286bb6d2015-07-09 20:35:09 +0000324 if (stat(path, &buf) == -1) {
325 if (!generate_key(path)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700326 D("Failed to generate new key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700327 return 0;
328 }
329 }
330
Dan Albert286bb6d2015-07-09 20:35:09 +0000331 return read_key(path, list);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700332}
333
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700334static void get_vendor_keys(struct listnode* key_list) {
335 const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
336 if (adb_keys_path == nullptr) {
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700337 return;
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700338 }
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700339
Elliott Hughes65fe2512015-10-07 15:59:35 -0700340 for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700341 if (!read_key(path.c_str(), key_list)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700342 D("Failed to read '%s'", path.c_str());
Elliott Hughes8d5fa6d2015-04-24 23:02:00 -0700343 }
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700344 }
345}
346
Dan Albertbac34742015-02-25 17:51:28 -0800347int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
348 unsigned char* sig)
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700349{
350 unsigned int len;
351 struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
352
Sami Tolvanen7b9c20d2015-01-27 16:48:35 +0000353 if (token_size != TOKEN_SIZE) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700354 D("Unexpected token size %zd", token_size);
Sami Tolvanen7b9c20d2015-01-27 16:48:35 +0000355 return 0;
356 }
357
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700358 if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
359 return 0;
360 }
361
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700362 D("adb_auth_sign len=%d", len);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700363 return (int)len;
364}
365
366void *adb_auth_nextkey(void *current)
367{
368 struct listnode *item;
369
370 if (list_empty(&key_list))
371 return NULL;
372
373 if (!current)
374 return list_head(&key_list);
375
376 list_for_each(item, &key_list) {
377 if (item == current) {
378 /* current is the last item, we tried all the keys */
379 if (item->next == &key_list)
380 return NULL;
381 return item->next;
382 }
383 }
384
385 return NULL;
386}
387
388int adb_auth_get_userkey(unsigned char *data, size_t len)
389{
Dan Albert286bb6d2015-07-09 20:35:09 +0000390 char path[PATH_MAX];
391 int ret = get_user_keyfilepath(path, sizeof(path) - 4);
392 if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700393 D("Error getting user key filename");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700394 return 0;
395 }
Dan Albert286bb6d2015-07-09 20:35:09 +0000396 strcat(path, ".pub");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700397
Dan Albertbac34742015-02-25 17:51:28 -0800398 // TODO(danalbert): ReadFileToString
Spencer Low6ac5d7d2015-05-22 20:09:06 -0700399 // Note that on Windows, load_file() does not do CR/LF translation, but
400 // ReadFileToString() uses the C Runtime which uses CR/LF translation by
401 // default (by is overridable with _setmode()).
Dan Albertbac34742015-02-25 17:51:28 -0800402 unsigned size;
Dan Albert286bb6d2015-07-09 20:35:09 +0000403 char* file_data = reinterpret_cast<char*>(load_file(path, &size));
Dan Albertbac34742015-02-25 17:51:28 -0800404 if (file_data == nullptr) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700405 D("Can't load '%s'", path);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700406 return 0;
407 }
408
Dan Albertbac34742015-02-25 17:51:28 -0800409 if (len < (size_t)(size + 1)) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700410 D("%s: Content too large ret=%d", path, size);
Dan Albertbac34742015-02-25 17:51:28 -0800411 free(file_data);
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700412 return 0;
413 }
414
Dan Albertbac34742015-02-25 17:51:28 -0800415 memcpy(data, file_data, size);
416 free(file_data);
417 file_data = nullptr;
418 data[size] = '\0';
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700419
Dan Albertbac34742015-02-25 17:51:28 -0800420 return size + 1;
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700421}
422
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800423int adb_auth_keygen(const char* filename) {
Nick Kralevichbea3f9c2014-11-13 15:17:29 -0800424 return (generate_key(filename) == 0);
425}
426
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700427void adb_auth_init(void)
428{
429 int ret;
430
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700431 D("adb_auth_init");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700432
433 list_init(&key_list);
434
435 ret = get_user_key(&key_list);
436 if (!ret) {
Yabin Cui7a3f8d62015-09-02 17:44:28 -0700437 D("Failed to get user key");
Benoit Gobyd5fcafa2012-04-12 12:23:49 -0700438 return;
439 }
440
441 get_vendor_keys(&key_list);
442}