blob: d689c9e263293b344444471becb5134d08447ccf [file] [log] [blame]
Shawn Willden4db3fbd2014-08-08 22:13:44 -06001/*
2 * Copyright (C) 2014 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
17#include <algorithm>
18
19#include <gtest/gtest.h>
20
21#include <openssl/engine.h>
Shawn Willden5b877622014-09-17 14:32:43 -060022#include <openssl/rand.h>
Shawn Willden4db3fbd2014-08-08 22:13:44 -060023
Shawn Willden98d9b922014-08-26 08:14:10 -060024#include <keymaster/authorization_set.h>
25#include <keymaster/google_keymaster_utils.h>
26#include <keymaster/keymaster_tags.h>
27
Shawn Willden72014ad2014-09-17 13:04:10 -060028#include "unencrypted_key_blob.h"
Shawn Willden4db3fbd2014-08-08 22:13:44 -060029
30int main(int argc, char** argv) {
31 ::testing::InitGoogleTest(&argc, argv);
32 int result = RUN_ALL_TESTS();
33 // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
34 CRYPTO_cleanup_all_ex_data();
35 ERR_free_strings();
36 return result;
37}
38
39namespace keymaster {
40
Shawn Willden4db3fbd2014-08-08 22:13:44 -060041namespace test {
42
Shawn Willdenebf627f2014-08-12 11:15:29 -060043const uint8_t master_key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
44const uint8_t key_data[5] = {21, 22, 23, 24, 25};
Shawn Willdenebf627f2014-08-12 11:15:29 -060045
Shawn Willden4db3fbd2014-08-08 22:13:44 -060046class KeyBlobTest : public testing::Test {
47 protected:
Shawn Willdenebf627f2014-08-12 11:15:29 -060048 KeyBlobTest() {
Shawn Willden4db3fbd2014-08-08 22:13:44 -060049 enforced_.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
50 enforced_.push_back(TAG_KEY_SIZE, 256);
51 enforced_.push_back(TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE);
52 enforced_.push_back(TAG_MIN_SECONDS_BETWEEN_OPS, 10);
53 enforced_.push_back(TAG_ALL_USERS);
54 enforced_.push_back(TAG_NO_AUTH_REQUIRED);
55 enforced_.push_back(TAG_ORIGIN, KM_ORIGIN_HARDWARE);
Shawn Willden4db3fbd2014-08-08 22:13:44 -060056
57 unenforced_.push_back(TAG_ACTIVE_DATETIME, 10);
58 unenforced_.push_back(TAG_ORIGINATION_EXPIRE_DATETIME, 100);
59 unenforced_.push_back(TAG_CREATION_DATETIME, 10);
60 unenforced_.push_back(TAG_CHUNK_LENGTH, 10);
Shawn Willden39b970b2014-08-11 09:11:21 -060061
62 hidden_.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
63 hidden_.push_back(TAG_APPLICATION_ID, "my_app", 6);
64
Shawn Willden5b877622014-09-17 14:32:43 -060065 EXPECT_EQ(1, RAND_bytes(nonce_, array_size(nonce_)));
66
Shawn Willden72014ad2014-09-17 13:04:10 -060067 blob_.reset(new UnencryptedKeyBlob(enforced_, unenforced_, hidden_, key_data,
68 array_size(key_data), master_key_data,
Shawn Willden5b877622014-09-17 14:32:43 -060069 array_size(master_key_data), nonce_));
Shawn Willden4db3fbd2014-08-08 22:13:44 -060070 }
71
72 AuthorizationSet enforced_;
73 AuthorizationSet unenforced_;
Shawn Willden39b970b2014-08-11 09:11:21 -060074 AuthorizationSet hidden_;
75
Shawn Willden72014ad2014-09-17 13:04:10 -060076 UniquePtr<UnencryptedKeyBlob> blob_;
Shawn Willden5b877622014-09-17 14:32:43 -060077 uint8_t nonce_[KeyBlob::NONCE_LENGTH];
Shawn Willden4db3fbd2014-08-08 22:13:44 -060078};
79
80TEST_F(KeyBlobTest, EncryptDecrypt) {
Shawn Willden39b970b2014-08-11 09:11:21 -060081 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -060082 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -060083 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -060084
85 // key_data shouldn't be anywhere in the blob.
86 uint8_t* begin = serialized_blob.get();
87 uint8_t* end = begin + size;
Shawn Willdenebf627f2014-08-12 11:15:29 -060088 EXPECT_EQ(end, std::search(begin, end, key_data, key_data + array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -060089
90 // Recover the key material.
91 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -060092 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
93 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -060094 EXPECT_EQ(KM_ERROR_OK, deserialized.error());
Shawn Willden72014ad2014-09-17 13:04:10 -060095 EXPECT_EQ(0, memcmp(deserialized.unencrypted_key_material(), key_data, array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -060096}
97
98TEST_F(KeyBlobTest, WrongKeyLength) {
Shawn Willden39b970b2014-08-11 09:11:21 -060099 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600100 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600101 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600102
Shawn Willden8d336ae2014-08-09 15:47:05 -0600103 // Modify the key length
Shawn Willden72014ad2014-09-17 13:04:10 -0600104 serialized_blob[UnencryptedKeyBlob::NONCE_LENGTH]++;
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600105
106 // Decrypting with wrong nonce should fail.
107 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600108 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
109 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600110 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
111}
112
113TEST_F(KeyBlobTest, WrongNonce) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600114 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600115 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600116 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600117
118 // Find the nonce, then modify it.
119 uint8_t* begin = serialized_blob.get();
120 uint8_t* end = begin + size;
Shawn Willden5b877622014-09-17 14:32:43 -0600121 auto nonce_ptr = std::search(begin, end, nonce_, nonce_ + array_size(nonce_));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600122 ASSERT_NE(nonce_ptr, end);
Shawn Willden5b877622014-09-17 14:32:43 -0600123 EXPECT_EQ(end, std::search(nonce_ptr + 1, end, nonce_, nonce_ + array_size(nonce_)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600124 (*nonce_ptr)++;
125
126 // Decrypting with wrong nonce should fail.
127 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600128 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
129 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600130 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden72014ad2014-09-17 13:04:10 -0600131 EXPECT_NE(0, memcmp(deserialized.unencrypted_key_material(), key_data, array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600132}
133
134TEST_F(KeyBlobTest, WrongTag) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600135 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600136 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600137 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600138
139 // Find the tag, them modify it.
140 uint8_t* begin = serialized_blob.get();
141 uint8_t* end = begin + size;
Shawn Willden72014ad2014-09-17 13:04:10 -0600142 auto tag_ptr =
143 std::search(begin, end, blob_->tag(), blob_->tag() + UnencryptedKeyBlob::TAG_LENGTH);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600144 ASSERT_NE(tag_ptr, end);
Shawn Willden72014ad2014-09-17 13:04:10 -0600145 EXPECT_EQ(end, std::search(tag_ptr + 1, end, blob_->tag(),
146 blob_->tag() + UnencryptedKeyBlob::TAG_LENGTH));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600147 (*tag_ptr)++;
148
149 // Decrypting with wrong tag should fail.
150 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600151 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
152 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600153 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden72014ad2014-09-17 13:04:10 -0600154 EXPECT_NE(0, memcmp(deserialized.unencrypted_key_material(), key_data, array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600155}
156
157TEST_F(KeyBlobTest, WrongCiphertext) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600158 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600159 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600160 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600161
162 // Find the ciphertext, them modify it.
163 uint8_t* begin = serialized_blob.get();
164 uint8_t* end = begin + size;
Shawn Willden39b970b2014-08-11 09:11:21 -0600165 auto ciphertext_ptr =
166 std::search(begin, end, blob_->encrypted_key_material(),
167 blob_->encrypted_key_material() + blob_->key_material_length());
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600168 ASSERT_NE(ciphertext_ptr, end);
Shawn Willden39b970b2014-08-11 09:11:21 -0600169 EXPECT_EQ(end, std::search(ciphertext_ptr + 1, end, blob_->encrypted_key_material(),
170 blob_->encrypted_key_material() + blob_->key_material_length()));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600171 (*ciphertext_ptr)++;
172
173 // Decrypting with wrong tag should fail.
174 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600175 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
176 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600177 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden72014ad2014-09-17 13:04:10 -0600178 EXPECT_NE(0, memcmp(deserialized.unencrypted_key_material(), key_data, array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600179}
180
181TEST_F(KeyBlobTest, WrongMasterKey) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600182 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600183 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600184 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600185
186 uint8_t wrong_master_data[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600187
188 // Decrypting with wrong master key should fail.
189 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600190 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, wrong_master_data,
191 array_size(wrong_master_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600192 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden72014ad2014-09-17 13:04:10 -0600193 EXPECT_NE(0, memcmp(deserialized.unencrypted_key_material(), key_data, array_size(key_data)));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600194}
195
196TEST_F(KeyBlobTest, WrongEnforced) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600197 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600198 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600199 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600200 uint8_t* begin = serialized_blob.get();
201 uint8_t* end = begin + size;
202
Shawn Willden8d336ae2014-08-09 15:47:05 -0600203 // Find enforced serialization data and modify it.
204 size_t enforced_size = enforced_.SerializedSize();
205 UniquePtr<uint8_t[]> enforced_data(new uint8_t[enforced_size]);
206 enforced_.Serialize(enforced_data.get(), enforced_data.get() + enforced_size);
207
208 auto enforced_ptr =
209 std::search(begin, end, enforced_data.get(), enforced_data.get() + enforced_size);
210 ASSERT_NE(end, enforced_ptr);
211 EXPECT_EQ(end, std::search(enforced_ptr + 1, end, enforced_data.get(),
212 enforced_data.get() + enforced_size));
213 (*(enforced_ptr + enforced_size - 1))++;
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600214
215 // Decrypting with wrong unenforced data should fail.
216 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600217 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
218 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600219 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600220}
221
222TEST_F(KeyBlobTest, WrongUnenforced) {
Shawn Willden39b970b2014-08-11 09:11:21 -0600223 size_t size = blob_->SerializedSize();
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600224 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
Shawn Willden39b970b2014-08-11 09:11:21 -0600225 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600226 uint8_t* begin = serialized_blob.get();
227 uint8_t* end = begin + size;
228
Shawn Willden8d336ae2014-08-09 15:47:05 -0600229 // Find unenforced serialization data and modify it.
230 size_t unenforced_size = unenforced_.SerializedSize();
231 UniquePtr<uint8_t[]> unenforced_data(new uint8_t[unenforced_size]);
232 unenforced_.Serialize(unenforced_data.get(), unenforced_data.get() + unenforced_size);
233
234 auto unenforced_ptr =
235 std::search(begin, end, unenforced_data.get(), unenforced_data.get() + unenforced_size);
236 ASSERT_NE(end, unenforced_ptr);
237 EXPECT_EQ(end, std::search(unenforced_ptr + 1, end, unenforced_data.get(),
238 unenforced_data.get() + unenforced_size));
239 (*(unenforced_ptr + unenforced_size - 1))++;
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600240
241 // Decrypting with wrong unenforced data should fail.
242 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600243 UnencryptedKeyBlob deserialized(encrypted_blob, hidden_, master_key_data,
244 array_size(master_key_data));
Shawn Willden39b970b2014-08-11 09:11:21 -0600245 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
246}
247
248TEST_F(KeyBlobTest, EmptyHidden) {
249 size_t size = blob_->SerializedSize();
250 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
251 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
252 uint8_t* begin = serialized_blob.get();
253 uint8_t* end = begin + size;
254
255 AuthorizationSet wrong_hidden;
256
257 // Decrypting with wrong hidden data should fail.
258 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600259 UnencryptedKeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_data,
260 array_size(master_key_data));
Shawn Willden39b970b2014-08-11 09:11:21 -0600261 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
262}
263
264TEST_F(KeyBlobTest, WrongRootOfTrust) {
265 size_t size = blob_->SerializedSize();
266 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
267 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
268 uint8_t* begin = serialized_blob.get();
269 uint8_t* end = begin + size;
270
271 AuthorizationSet wrong_hidden;
272 wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "bar", 3);
273 wrong_hidden.push_back(TAG_APPLICATION_ID, "my_app", 6);
274
275 // Decrypting with wrong hidden data should fail.
276 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600277 UnencryptedKeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_data,
278 array_size(master_key_data));
Shawn Willden39b970b2014-08-11 09:11:21 -0600279 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
280}
281
282TEST_F(KeyBlobTest, WrongAppId) {
283 size_t size = blob_->SerializedSize();
284 UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
285 blob_->Serialize(serialized_blob.get(), serialized_blob.get() + size);
286 uint8_t* begin = serialized_blob.get();
287 uint8_t* end = begin + size;
288
289 AuthorizationSet wrong_hidden;
290 wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
291 wrong_hidden.push_back(TAG_APPLICATION_ID, "your_app", 7);
292
293 // Decrypting with wrong hidden data should fail.
294 keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
Shawn Willden72014ad2014-09-17 13:04:10 -0600295 UnencryptedKeyBlob deserialized(encrypted_blob, wrong_hidden, master_key_data,
296 array_size(master_key_data));
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600297 EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600298}
299
Shawn Willden5b877622014-09-17 14:32:43 -0600300TEST_F(KeyBlobTest, UnversionedBlob) {
301 // Manually construct an unversioned blob serialization.
302 size_t unversioned_blob_size =
303 KeyBlob::NONCE_LENGTH + // nonce data
304 sizeof(uint32_t) + // length of key material
305 blob_->key_material_length() + // key material data
306 KeyBlob::TAG_LENGTH + // tag data
307 blob_->enforced().SerializedSize() + // serialization of enforced set
308 blob_->unenforced().SerializedSize(); // serialization of unenforced set
309 UniquePtr<uint8_t[]> unversioned_serialized_blob(new uint8_t[unversioned_blob_size]);
310 uint8_t* buf = unversioned_serialized_blob.get();
311 const uint8_t* end = buf + unversioned_blob_size;
312 buf = append_to_buf(buf, end, blob_->nonce(), KeyBlob::NONCE_LENGTH);
313 buf = append_size_and_data_to_buf(buf, end, blob_->encrypted_key_material(),
314 blob_->key_material_length());
315 buf = append_to_buf(buf, end, blob_->tag(), KeyBlob::TAG_LENGTH);
316 buf = blob_->enforced().Serialize(buf, end);
317 buf = blob_->unenforced().Serialize(buf, end);
318 EXPECT_EQ(buf, end);
319
320 keymaster_key_blob_t unversioned_blob = {unversioned_serialized_blob.get(),
321 unversioned_blob_size};
322 UnencryptedKeyBlob deserialized(unversioned_blob, hidden_, master_key_data,
323 array_size(master_key_data));
324 EXPECT_EQ(KM_ERROR_OK, deserialized.error());
325}
326
Shawn Willden4db3fbd2014-08-08 22:13:44 -0600327} // namespace test
328} // namespace keymaster