• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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.nearby.common.bluetooth.fastpair;
18 
19 import static com.google.common.primitives.Bytes.concat;
20 
21 import androidx.annotation.VisibleForTesting;
22 
23 import java.security.GeneralSecurityException;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.SecureRandom;
26 import java.util.Arrays;
27 
28 /**
29  * AES-CTR utilities used for encrypting and decrypting Fast Pair packets that contain multiple
30  * blocks. Encrypts input data by:
31  *
32  * <ol>
33  *   <li>encryptedBlock[i] = clearBlock[i] ^ AES(counter), and
34  *   <li>concat(encryptedBlock[0], encryptedBlock[1],...) to create the encrypted result, where
35  *   <li>counter: the 16-byte input of AES. counter = iv + block_index.
36  *   <li>iv: extend 8-byte nonce to 16 bytes with zero padding. i.e. concat(0x0000000000000000,
37  *       nonce).
38  *   <li>nonce: the cryptographically random 8 bytes, must never be reused with the same key.
39  * </ol>
40  */
41 final class AesCtrMultipleBlockEncryption {
42 
43     /** Length for AES-128 key. */
44     static final int KEY_LENGTH = AesEcbSingleBlockEncryption.KEY_LENGTH;
45 
46     @VisibleForTesting
47     static final int AES_BLOCK_LENGTH = AesEcbSingleBlockEncryption.AES_BLOCK_LENGTH;
48 
49     /** Length of the nonce, a byte array of cryptographically random bytes. */
50     static final int NONCE_SIZE = 8;
51 
52     private static final int IV_SIZE = AES_BLOCK_LENGTH;
53     private static final int MAX_NUMBER_OF_BLOCKS = 4;
54 
AesCtrMultipleBlockEncryption()55     private AesCtrMultipleBlockEncryption() {}
56 
57     /** Generates a 16-byte AES key. */
generateKey()58     static byte[] generateKey() throws NoSuchAlgorithmException {
59         return AesEcbSingleBlockEncryption.generateKey();
60     }
61 
62     /**
63      * Encrypts data using AES-CTR by the given secret.
64      *
65      * @param secret AES-128 key.
66      * @param data the plaintext to be encrypted.
67      * @return the encrypted data with the 8-byte nonce appended to the front.
68      */
encrypt(byte[] secret, byte[] data)69     static byte[] encrypt(byte[] secret, byte[] data) throws GeneralSecurityException {
70         byte[] nonce = generateNonce();
71         return concat(nonce, doAesCtr(secret, data, nonce));
72     }
73 
74     /**
75      * Decrypts data using AES-CTR by the given secret and nonce.
76      *
77      * @param secret AES-128 key.
78      * @param data the first 8 bytes is the nonce, and the remaining is the encrypted data to be
79      *     decrypted.
80      * @return the decrypted data.
81      */
decrypt(byte[] secret, byte[] data)82     static byte[] decrypt(byte[] secret, byte[] data) throws GeneralSecurityException {
83         if (data == null || data.length <= NONCE_SIZE) {
84             throw new GeneralSecurityException(
85                     "Incorrect data length "
86                             + (data == null ? "NULL" : data.length)
87                             + " to decrypt, the data should contain nonce.");
88         }
89         byte[] nonce = Arrays.copyOf(data, NONCE_SIZE);
90         byte[] encryptedData = Arrays.copyOfRange(data, NONCE_SIZE, data.length);
91         return doAesCtr(secret, encryptedData, nonce);
92     }
93 
94     /**
95      * Generates cryptographically random NONCE_SIZE bytes nonce. This nonce can be used only once.
96      * Always call this function to generate a new nonce before a new encryption.
97      */
98     // Suppression for a warning for potentially insecure random numbers on Android 4.3 and older.
99     // Fast Pair service is only for Android 6.0+ devices.
generateNonce()100     static byte[] generateNonce() {
101         SecureRandom random = new SecureRandom();
102         byte[] nonce = new byte[NONCE_SIZE];
103         random.nextBytes(nonce);
104 
105         return nonce;
106     }
107 
108     // AES-CTR implementation.
109     @VisibleForTesting
doAesCtr(byte[] secret, byte[] data, byte[] nonce)110     static byte[] doAesCtr(byte[] secret, byte[] data, byte[] nonce)
111             throws GeneralSecurityException {
112         if (secret.length != KEY_LENGTH) {
113             throw new IllegalArgumentException(
114                     "Incorrect key length for encryption, only supports 16-byte AES Key.");
115         }
116         if (nonce.length != NONCE_SIZE) {
117             throw new IllegalArgumentException(
118                     "Incorrect nonce length for encryption, "
119                             + "Fast Pair naming scheme only supports 8-byte nonce.");
120         }
121 
122         // Keeps the following operations on this byte[], returns it as the final AES-CTR result.
123         byte[] aesCtrResult = new byte[data.length];
124         System.arraycopy(data, /*srcPos=*/ 0, aesCtrResult, /*destPos=*/ 0, data.length);
125 
126         // Initializes counter as IV.
127         byte[] counter = createIv(nonce);
128         // The length of the given data is permitted to non-align block size.
129         int numberOfBlocks =
130                 (data.length / AES_BLOCK_LENGTH) + ((data.length % AES_BLOCK_LENGTH == 0) ? 0 : 1);
131 
132         if (numberOfBlocks > MAX_NUMBER_OF_BLOCKS) {
133             throw new IllegalArgumentException(
134                     "Incorrect data size, Fast Pair naming scheme only supports 4 blocks.");
135         }
136 
137         for (int i = 0; i < numberOfBlocks; i++) {
138             // Performs the operation: encryptedBlock[i] = clearBlock[i] ^ AES(counter).
139             counter[0] = (byte) (i & 0xFF);
140             byte[] aesOfCounter = doAesSingleBlock(secret, counter);
141             int start = i * AES_BLOCK_LENGTH;
142             // The size of the last block of data may not be 16 bytes. If not, still do xor to the
143             // last byte of data.
144             int end = Math.min(start + AES_BLOCK_LENGTH, data.length);
145             for (int j = 0; start < end; j++, start++) {
146                 aesCtrResult[start] ^= aesOfCounter[j];
147             }
148         }
149         return aesCtrResult;
150     }
151 
doAesSingleBlock(byte[] secret, byte[] counter)152     private static byte[] doAesSingleBlock(byte[] secret, byte[] counter)
153             throws GeneralSecurityException {
154         return AesEcbSingleBlockEncryption.encrypt(secret, counter);
155     }
156 
157     /** Extends 8-byte nonce to 16 bytes with zero padding to create IV. */
createIv(byte[] nonce)158     private static byte[] createIv(byte[] nonce) {
159         return concat(new byte[IV_SIZE - NONCE_SIZE], nonce);
160     }
161 }
162