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