1 /*
2  * Copyright (C) 2017 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 package com.android.server.locksettings.recoverablekeystore;
18 
19 import static junit.framework.Assert.fail;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 
25 import android.util.Pair;
26 
27 import androidx.test.filters.SmallTest;
28 import androidx.test.runner.AndroidJUnit4;
29 
30 import com.android.internal.util.ArrayUtils;
31 import com.android.security.SecureBox;
32 
33 import com.google.common.collect.ImmutableMap;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.nio.ByteBuffer;
39 import java.nio.ByteOrder;
40 import java.nio.charset.StandardCharsets;
41 import java.security.KeyPair;
42 import java.security.MessageDigest;
43 import java.security.PublicKey;
44 import java.util.Arrays;
45 import java.util.Map;
46 import java.util.Random;
47 
48 import javax.crypto.AEADBadTagException;
49 import javax.crypto.KeyGenerator;
50 import javax.crypto.SecretKey;
51 
52 @SmallTest
53 @RunWith(AndroidJUnit4.class)
54 public class KeySyncUtilsTest {
55     private static final int RECOVERY_KEY_LENGTH_BITS = 256;
56     private static final int THM_KF_HASH_SIZE = 256;
57     private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
58     private static final byte[] TEST_VAULT_HANDLE =
59             new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
60     private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
61     private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
62     private static final String SHA_256_ALGORITHM = "SHA-256";
63     private static final String APPLICATION_KEY_ALGORITHM = "AES";
64     private static final byte[] LOCK_SCREEN_HASH_1 =
65             utf8Bytes("g09TEvo6XqVdNaYdRggzn5w2C5rCeE1F");
66     private static final byte[] LOCK_SCREEN_HASH_2 =
67             utf8Bytes("snQzsbvclkSsG6PwasAp1oFLzbq3KtFe");
68     private static final byte[] RECOVERY_CLAIM_HEADER =
69             "V1 KF_claim".getBytes(StandardCharsets.UTF_8);
70     private static final byte[] RECOVERY_RESPONSE_HEADER =
71             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
72     private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
73     private static final byte[] NULL_METADATA = null;
74     private static final byte[] NON_NULL_METADATA = "somemetadata".getBytes(StandardCharsets.UTF_8);
75 
76     @Test
calculateThmKfHash_isShaOfLockScreenHashWithPrefix()77     public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
78         byte[] lockScreenHash = utf8Bytes("012345678910");
79 
80         byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(lockScreenHash);
81 
82         assertArrayEquals(calculateSha256(utf8Bytes("THM_KF_hash012345678910")), thmKfHash);
83     }
84 
85     @Test
calculateThmKfHash_is256BitsLong()86     public void calculateThmKfHash_is256BitsLong() throws Exception {
87         byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(utf8Bytes("1234"));
88 
89         assertEquals(THM_KF_HASH_SIZE / Byte.SIZE, thmKfHash.length);
90     }
91 
92     @Test
generateRecoveryKey_returnsA256BitKey()93     public void generateRecoveryKey_returnsA256BitKey() throws Exception {
94         SecretKey key = KeySyncUtils.generateRecoveryKey();
95 
96         assertEquals(RECOVERY_KEY_LENGTH_BITS / Byte.SIZE, key.getEncoded().length);
97     }
98 
99     @Test
generateRecoveryKey_generatesANewKeyEachTime()100     public void generateRecoveryKey_generatesANewKeyEachTime() throws Exception {
101         SecretKey a = KeySyncUtils.generateRecoveryKey();
102         SecretKey b = KeySyncUtils.generateRecoveryKey();
103 
104         assertFalse(Arrays.equals(a.getEncoded(), b.getEncoded()));
105     }
106 
107     @Test
generateKeyClaimant_returns16Bytes()108     public void generateKeyClaimant_returns16Bytes() throws Exception {
109         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
110 
111         assertEquals(KEY_CLAIMANT_LENGTH_BYTES, keyClaimant.length);
112     }
113 
114     @Test
generateKeyClaimant_generatesANewClaimantEachTime()115     public void generateKeyClaimant_generatesANewClaimantEachTime() {
116         byte[] a = KeySyncUtils.generateKeyClaimant();
117         byte[] b = KeySyncUtils.generateKeyClaimant();
118 
119         assertFalse(Arrays.equals(a, b));
120     }
121 
122     @Test
decryptApplicationKey_decryptsAnApplicationKey_nullMetadata()123     public void decryptApplicationKey_decryptsAnApplicationKey_nullMetadata() throws Exception {
124         String alias = "phoebe";
125         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
126         SecretKey applicationKey = generateApplicationKey();
127         Map<String, byte[]> encryptedKeys =
128                 KeySyncUtils.encryptKeysWithRecoveryKey(
129                         recoveryKey,
130                         ImmutableMap.of(alias, Pair.create(applicationKey, NULL_METADATA)));
131         byte[] encryptedKey = encryptedKeys.get(alias);
132 
133         byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
134                 encryptedKey, NULL_METADATA);
135 
136         assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
137     }
138 
139     @Test
decryptApplicationKey_decryptsAnApplicationKey_nonNullMetadata()140     public void decryptApplicationKey_decryptsAnApplicationKey_nonNullMetadata() throws Exception {
141         String alias = "phoebe";
142         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
143         SecretKey applicationKey = generateApplicationKey();
144         Map<String, byte[]> encryptedKeys =
145                 KeySyncUtils.encryptKeysWithRecoveryKey(
146                         recoveryKey,
147                         ImmutableMap.of(alias, Pair.create(applicationKey, NON_NULL_METADATA)));
148         byte[] encryptedKey = encryptedKeys.get(alias);
149 
150         byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
151                 encryptedKey, NON_NULL_METADATA);
152 
153         assertArrayEquals(applicationKey.getEncoded(), keyMaterial);
154     }
155 
156     @Test
decryptApplicationKey_throwsIfUnableToDecrypt()157     public void decryptApplicationKey_throwsIfUnableToDecrypt() throws Exception {
158         String alias = "casper";
159         Map<String, byte[]> encryptedKeys =
160                 KeySyncUtils.encryptKeysWithRecoveryKey(
161                         KeySyncUtils.generateRecoveryKey(),
162                         ImmutableMap.of("casper",
163                                 Pair.create(generateApplicationKey(), NULL_METADATA)));
164         byte[] encryptedKey = encryptedKeys.get(alias);
165 
166         try {
167             KeySyncUtils.decryptApplicationKey(KeySyncUtils.generateRecoveryKey().getEncoded(),
168                     encryptedKey, NULL_METADATA);
169             fail("Did not throw decrypting with bad key.");
170         } catch (AEADBadTagException error) {
171             // expected
172         }
173     }
174 
175     @Test
decryptApplicationKey_throwsIfWrongMetadata()176     public void decryptApplicationKey_throwsIfWrongMetadata() throws Exception {
177         String alias1 = "casper1";
178         String alias2 = "casper2";
179         String alias3 = "casper3";
180         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
181 
182         Map<String, byte[]> encryptedKeys =
183                 KeySyncUtils.encryptKeysWithRecoveryKey(
184                         recoveryKey,
185                         ImmutableMap.of(
186                                 alias1,
187                                 Pair.create(generateApplicationKey(), NULL_METADATA),
188                                 alias2,
189                                 Pair.create(generateApplicationKey(), NON_NULL_METADATA),
190                                 alias3,
191                                 Pair.create(generateApplicationKey(), NON_NULL_METADATA)));
192 
193         try {
194             KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
195                     encryptedKeys.get(alias1), NON_NULL_METADATA);
196             fail("Did not throw decrypting with wrong metadata.");
197         } catch (AEADBadTagException error) {
198             // expected
199         }
200         try {
201             KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
202                     encryptedKeys.get(alias2), NULL_METADATA);
203             fail("Did not throw decrypting with wrong metadata.");
204         } catch (AEADBadTagException error) {
205             // expected
206         }
207         try {
208             KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(),
209                     encryptedKeys.get(alias3), "different".getBytes(StandardCharsets.UTF_8));
210             fail("Did not throw decrypting with wrong metadata.");
211         } catch (AEADBadTagException error) {
212             // expected
213         }
214     }
215 
216     @Test
decryptRecoveryKey_decryptsALocallyEncryptedKey()217     public void decryptRecoveryKey_decryptsALocallyEncryptedKey() throws Exception {
218         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
219         byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey(
220                 LOCK_SCREEN_HASH_1, recoveryKey);
221 
222         byte[] keyMaterial = KeySyncUtils.decryptRecoveryKey(LOCK_SCREEN_HASH_1, encrypted);
223 
224         assertArrayEquals(recoveryKey.getEncoded(), keyMaterial);
225     }
226 
227     @Test
decryptRecoveryKey_throwsIfCannotDecrypt()228     public void decryptRecoveryKey_throwsIfCannotDecrypt() throws Exception {
229         SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey();
230         byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey(LOCK_SCREEN_HASH_1, recoveryKey);
231 
232         try {
233             KeySyncUtils.decryptRecoveryKey(LOCK_SCREEN_HASH_2, encrypted);
234             fail("Did not throw decrypting with bad key.");
235         } catch (AEADBadTagException error) {
236             // expected
237         }
238     }
239 
240     @Test
decryptRecoveryClaimResponse_decryptsAValidResponse()241     public void decryptRecoveryClaimResponse_decryptsAValidResponse() throws Exception {
242         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
243         byte[] vaultParams = randomBytes(100);
244         byte[] recoveryKey = randomBytes(32);
245         byte[] encryptedPayload = SecureBox.encrypt(
246                 /*theirPublicKey=*/ null,
247                 /*sharedSecret=*/ keyClaimant,
248                 /*header=*/ ArrayUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
249                 /*payload=*/ recoveryKey);
250 
251         byte[] decrypted = KeySyncUtils.decryptRecoveryClaimResponse(
252                 keyClaimant, vaultParams, encryptedPayload);
253 
254         assertArrayEquals(recoveryKey, decrypted);
255     }
256 
257     @Test
decryptRecoveryClaimResponse_throwsIfCannotDecrypt()258     public void decryptRecoveryClaimResponse_throwsIfCannotDecrypt() throws Exception {
259         byte[] vaultParams = randomBytes(100);
260         byte[] recoveryKey = randomBytes(32);
261         byte[] encryptedPayload = SecureBox.encrypt(
262                 /*theirPublicKey=*/ null,
263                 /*sharedSecret=*/ KeySyncUtils.generateKeyClaimant(),
264                 /*header=*/ ArrayUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
265                 /*payload=*/ recoveryKey);
266 
267         try {
268             KeySyncUtils.decryptRecoveryClaimResponse(
269                     KeySyncUtils.generateKeyClaimant(), vaultParams, encryptedPayload);
270             fail("Did not throw decrypting with bad keyClaimant");
271         } catch (AEADBadTagException error) {
272             // expected
273         }
274     }
275 
276     @Test
encryptRecoveryClaim_encryptsLockScreenAndKeyClaimant()277     public void encryptRecoveryClaim_encryptsLockScreenAndKeyClaimant() throws Exception {
278         KeyPair keyPair = SecureBox.genKeyPair();
279         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
280         byte[] challenge = randomBytes(32);
281         byte[] vaultParams = randomBytes(100);
282 
283         byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim(
284                 keyPair.getPublic(),
285                 vaultParams,
286                 challenge,
287                 LOCK_SCREEN_HASH_1,
288                 keyClaimant);
289 
290         byte[] decrypted = SecureBox.decrypt(
291                 keyPair.getPrivate(),
292                 /*sharedSecret=*/ null,
293                 /*header=*/ ArrayUtils.concat(RECOVERY_CLAIM_HEADER, vaultParams, challenge),
294                 encryptedRecoveryClaim);
295         assertArrayEquals(ArrayUtils.concat(LOCK_SCREEN_HASH_1, keyClaimant), decrypted);
296     }
297 
298     @Test
encryptRecoveryClaim_cannotBeDecryptedWithoutChallenge()299     public void encryptRecoveryClaim_cannotBeDecryptedWithoutChallenge() throws Exception {
300         KeyPair keyPair = SecureBox.genKeyPair();
301         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
302         byte[] vaultParams = randomBytes(100);
303 
304         byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim(
305                 keyPair.getPublic(),
306                 vaultParams,
307                 /*challenge=*/ randomBytes(32),
308                 LOCK_SCREEN_HASH_1,
309                 keyClaimant);
310 
311         try {
312             SecureBox.decrypt(
313                     keyPair.getPrivate(),
314                     /*sharedSecret=*/ null,
315                     /*header=*/ ArrayUtils.concat(
316                             RECOVERY_CLAIM_HEADER, vaultParams, randomBytes(32)),
317                     encryptedRecoveryClaim);
318             fail("Should throw if challenge is incorrect.");
319         } catch (AEADBadTagException e) {
320             // expected
321         }
322     }
323 
324     @Test
encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectSecretKey()325     public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectSecretKey() throws Exception {
326         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
327         byte[] challenge = randomBytes(32);
328         byte[] vaultParams = randomBytes(100);
329 
330         byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim(
331                 SecureBox.genKeyPair().getPublic(),
332                 vaultParams,
333                 challenge,
334                 LOCK_SCREEN_HASH_1,
335                 keyClaimant);
336 
337         try {
338             SecureBox.decrypt(
339                     SecureBox.genKeyPair().getPrivate(),
340                     /*sharedSecret=*/ null,
341                     /*header=*/ ArrayUtils.concat(
342                             RECOVERY_CLAIM_HEADER, vaultParams, challenge),
343                     encryptedRecoveryClaim);
344             fail("Should throw if secret key is incorrect.");
345         } catch (AEADBadTagException e) {
346             // expected
347         }
348     }
349 
350     @Test
encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectVaultParams()351     public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectVaultParams() throws Exception {
352         KeyPair keyPair = SecureBox.genKeyPair();
353         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
354         byte[] challenge = randomBytes(32);
355 
356         byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim(
357                 keyPair.getPublic(),
358                 /*vaultParams=*/ randomBytes(100),
359                 challenge,
360                 LOCK_SCREEN_HASH_1,
361                 keyClaimant);
362 
363         try {
364             SecureBox.decrypt(
365                     keyPair.getPrivate(),
366                     /*sharedSecret=*/ null,
367                     /*header=*/ ArrayUtils.concat(
368                             RECOVERY_CLAIM_HEADER, randomBytes(100), challenge),
369                     encryptedRecoveryClaim);
370             fail("Should throw if vault params is incorrect.");
371         } catch (AEADBadTagException e) {
372             // expected
373         }
374     }
375 
376     @Test
encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectHeader()377     public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectHeader() throws Exception {
378         KeyPair keyPair = SecureBox.genKeyPair();
379         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
380         byte[] challenge = randomBytes(32);
381         byte[] vaultParams = randomBytes(100);
382 
383         byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim(
384                 keyPair.getPublic(),
385                 vaultParams,
386                 challenge,
387                 LOCK_SCREEN_HASH_1,
388                 keyClaimant);
389 
390         try {
391             SecureBox.decrypt(
392                     keyPair.getPrivate(),
393                     /*sharedSecret=*/ null,
394                     /*header=*/ ArrayUtils.concat(randomBytes(10), vaultParams, challenge),
395                     encryptedRecoveryClaim);
396             fail("Should throw if header is incorrect.");
397         } catch (AEADBadTagException e) {
398             // expected
399         }
400     }
401 
402     @Test
packVaultParams_returnsCorrectSize()403     public void packVaultParams_returnsCorrectSize() throws Exception {
404         PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
405 
406         byte[] packedForm = KeySyncUtils.packVaultParams(
407                 thmPublicKey,
408                 /*counterId=*/ 1001L,
409                 /*maxAttempts=*/ 10,
410                 TEST_VAULT_HANDLE);
411 
412         assertEquals(VAULT_PARAMS_LENGTH_BYTES, packedForm.length);
413     }
414 
415     @Test
packVaultParams_encodesPublicKeyInFirst65Bytes()416     public void packVaultParams_encodesPublicKeyInFirst65Bytes() throws Exception {
417         PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
418 
419         byte[] packedForm = KeySyncUtils.packVaultParams(
420                 thmPublicKey,
421                 /*counterId=*/ 1001L,
422                 /*maxAttempts=*/ 10,
423                 TEST_VAULT_HANDLE);
424 
425         assertArrayEquals(
426                 SecureBox.encodePublicKey(thmPublicKey),
427                 Arrays.copyOf(packedForm, PUBLIC_KEY_LENGTH_BYTES));
428     }
429 
430     @Test
packVaultParams_encodesCounterIdAsSecondParam()431     public void packVaultParams_encodesCounterIdAsSecondParam() throws Exception {
432         long counterId = 103502L;
433 
434         byte[] packedForm = KeySyncUtils.packVaultParams(
435                 SecureBox.genKeyPair().getPublic(),
436                 counterId,
437                 /*maxAttempts=*/ 10,
438                 TEST_VAULT_HANDLE);
439 
440         ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
441                 .order(ByteOrder.LITTLE_ENDIAN);
442         byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES);
443         assertEquals(counterId, byteBuffer.getLong());
444     }
445 
446     @Test
packVaultParams_encodesMaxAttemptsAsThirdParam()447     public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception {
448         int maxAttempts = 10;
449 
450         byte[] packedForm = KeySyncUtils.packVaultParams(
451                 SecureBox.genKeyPair().getPublic(),
452                 /*counterId=*/ 1001L,
453                 maxAttempts,
454                 TEST_VAULT_HANDLE);
455 
456         ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
457                 .order(ByteOrder.LITTLE_ENDIAN);
458         byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
459         assertEquals(maxAttempts, byteBuffer.getInt());
460     }
461 
462     @Test
packVaultParams_encodesVaultHandleAsLastParam()463     public void packVaultParams_encodesVaultHandleAsLastParam() throws Exception {
464         byte[] packedForm = KeySyncUtils.packVaultParams(
465                 SecureBox.genKeyPair().getPublic(),
466                 /*counterId=*/ 10021L,
467                 /*maxAttempts=*/ 10,
468                 TEST_VAULT_HANDLE);
469 
470         ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
471                 .order(ByteOrder.LITTLE_ENDIAN);
472         byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
473         byte[] vaultHandle = new byte[VAULT_HANDLE_LENGTH_BYTES];
474         byteBuffer.get(vaultHandle);
475         assertArrayEquals(TEST_VAULT_HANDLE, vaultHandle);
476     }
477 
478     @Test
packVaultParams_encodesVaultHandleWithLength8AsLastParam()479     public void packVaultParams_encodesVaultHandleWithLength8AsLastParam() throws Exception {
480         byte[] vaultHandleWithLenght8 = new byte[] {1, 2, 3, 4, 1, 2, 3, 4};
481         byte[] packedForm = KeySyncUtils.packVaultParams(
482                 SecureBox.genKeyPair().getPublic(),
483                 /*counterId=*/ 10021L,
484                 /*maxAttempts=*/ 10,
485                 vaultHandleWithLenght8);
486 
487         ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
488                 .order(ByteOrder.LITTLE_ENDIAN);
489         assertEquals(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES + 8, packedForm.length);
490         byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
491         byte[] vaultHandle = new byte[8];
492         byteBuffer.get(vaultHandle);
493         assertArrayEquals(vaultHandleWithLenght8, vaultHandle);
494     }
495 
randomBytes(int n)496     private static byte[] randomBytes(int n) {
497         byte[] bytes = new byte[n];
498         new Random().nextBytes(bytes);
499         return bytes;
500     }
501 
utf8Bytes(String s)502     private static byte[] utf8Bytes(String s) {
503         return s.getBytes(StandardCharsets.UTF_8);
504     }
505 
calculateSha256(byte[] bytes)506     private static byte[] calculateSha256(byte[] bytes) throws Exception {
507         MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM);
508         messageDigest.update(bytes);
509         return messageDigest.digest();
510     }
511 
generateApplicationKey()512     private static SecretKey generateApplicationKey() throws Exception {
513         KeyGenerator keyGenerator = KeyGenerator.getInstance(APPLICATION_KEY_ALGORITHM);
514         keyGenerator.init(/*keySize=*/ 256);
515         return keyGenerator.generateKey();
516     }
517 }
518