[nearby] Update encryption methods according to the spec
Test: -m
Fix: 290294425
Ignore-AOSP-First: nearby_not_in_aosp_yet
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:297c193dede82ca662a708bb7f3eeb90ce9e968b)
Merged-In: I24ead1adfd0928a877fa1441501c3b6e462bdbd6
Change-Id: I24ead1adfd0928a877fa1441501c3b6e462bdbd6
diff --git a/nearby/service/java/com/android/server/nearby/presence/EncryptionInfo.java b/nearby/service/java/com/android/server/nearby/presence/EncryptionInfo.java
index 1f9f70b..ac1e18f 100644
--- a/nearby/service/java/com/android/server/nearby/presence/EncryptionInfo.java
+++ b/nearby/service/java/com/android/server/nearby/presence/EncryptionInfo.java
@@ -17,11 +17,13 @@
package com.android.server.nearby.presence;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.nearby.DataElement;
import androidx.annotation.NonNull;
import com.android.internal.util.Preconditions;
+import com.android.server.nearby.util.ArrayUtils;
import java.util.Arrays;
@@ -40,7 +42,7 @@
// 1st byte : encryption scheme
// 2nd to 17th bytes: salt
- private static final int ENCRYPTION_INFO_LENGTH = 17;
+ public static final int ENCRYPTION_INFO_LENGTH = 17;
private static final int ENCODING_SCHEME_MASK = 0b01111000;
private static final int ENCODING_SCHEME_OFFSET = 3;
@EncodingScheme
@@ -69,4 +71,20 @@
public byte[] getSalt() {
return mSalt;
}
+
+ /** Combines the encoding scheme and salt to a byte array
+ * that represents an {@link EncryptionInfo}.
+ */
+ @Nullable
+ public static byte[] toByte(@EncodingScheme int encodingScheme, byte[] salt) {
+ if (ArrayUtils.isEmpty(salt)) {
+ return null;
+ }
+ if (salt.length != ENCRYPTION_INFO_LENGTH - 1) {
+ return null;
+ }
+ byte schemeByte =
+ (byte) ((encodingScheme << ENCODING_SCHEME_OFFSET) & ENCODING_SCHEME_MASK);
+ return ArrayUtils.append(schemeByte, salt);
+ }
}
diff --git a/nearby/service/java/com/android/server/nearby/util/ArrayUtils.java b/nearby/service/java/com/android/server/nearby/util/ArrayUtils.java
index bb5461c..e69d004 100644
--- a/nearby/service/java/com/android/server/nearby/util/ArrayUtils.java
+++ b/nearby/service/java/com/android/server/nearby/util/ArrayUtils.java
@@ -22,6 +22,10 @@
* ArrayUtils class that help manipulate array.
*/
public class ArrayUtils {
+ private static final char[] HEX_UPPERCASE = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
/** Concatenate N arrays of bytes into a single array. */
public static byte[] concatByteArrays(byte[]... arrays) {
// Degenerate case - no input provided.
@@ -65,4 +69,54 @@
System.arraycopy(b, 0, result, 1, length);
return result;
}
+
+ /**
+ * Converts an Integer to a 2-byte array.
+ */
+ public static byte[] intToByteArray(int value) {
+ return new byte[] {(byte) (value >> 8), (byte) value};
+ }
+
+ /** Appends a byte to a byte array. */
+ public static byte[] append(byte[] a, byte b) {
+ if (a == null) {
+ return new byte[]{b};
+ }
+
+ int length = a.length;
+ byte[] result = new byte[length + 1];
+ System.arraycopy(a, 0, result, 0, length);
+ result[length] = b;
+ return result;
+ }
+
+ /** Convert an hex string to a byte array. */
+
+ public static byte[] stringToBytes(String hex) throws IllegalArgumentException {
+ int length = hex.length();
+ if (length % 2 != 0) {
+ throw new IllegalArgumentException("Hex string has odd number of characters");
+ }
+ byte[] out = new byte[length / 2];
+ for (int i = 0; i < length; i += 2) {
+ // Byte.parseByte() doesn't work here because it expects a hex value in -128, 127, and
+ // our hex values are in 0, 255.
+ out[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
+ }
+ return out;
+ }
+
+ /** Encodes a byte array as a hexadecimal representation of bytes. */
+ public static String bytesToStringUppercase(byte[] bytes) {
+ int length = bytes.length;
+ StringBuilder out = new StringBuilder(length * 2);
+ for (int i = 0; i < length; i++) {
+ if (i == length - 1 && (bytes[i] & 0xff) == 0) {
+ break;
+ }
+ out.append(HEX_UPPERCASE[(bytes[i] & 0xf0) >>> 4]);
+ out.append(HEX_UPPERCASE[bytes[i] & 0x0f]);
+ }
+ return out.toString();
+ }
}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java b/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java
index 3c5132d..ba9ca41 100644
--- a/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java
@@ -22,6 +22,10 @@
import androidx.annotation.Nullable;
+import com.google.common.hash.Hashing;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -30,22 +34,28 @@
/** Class for encryption/decryption functionality. */
public abstract class Cryptor {
+ /**
+ * In the form of "algorithm/mode/padding". Must be the same across broadcast and scan devices.
+ */
+ public static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
+
+ public static final byte[] NP_HKDF_SALT = "Google Nearby".getBytes(StandardCharsets.US_ASCII);
/** AES only supports key sizes of 16, 24 or 32 bytes. */
static final int AUTHENTICITY_KEY_BYTE_SIZE = 16;
- private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
+ public static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
/**
* Encrypt the provided data blob.
*
* @param data data blob to be encrypted.
- * @param salt used for IV
+ * @param iv advertisement nonce
* @param secretKeyBytes secrete key accessed from credentials
* @return encrypted data, {@code null} if failed to encrypt.
*/
@Nullable
- public byte[] encrypt(byte[] data, byte[] salt, byte[] secretKeyBytes) {
+ public byte[] encrypt(byte[] data, byte[] iv, byte[] secretKeyBytes) {
return data;
}
@@ -53,12 +63,12 @@
* Decrypt the original data blob from the provided byte array.
*
* @param encryptedData data blob to be decrypted.
- * @param salt used for IV
+ * @param iv advertisement nonce
* @param secretKeyBytes secrete key accessed from credentials
* @return decrypted data, {@code null} if failed to decrypt.
*/
@Nullable
- public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] secretKeyBytes) {
+ public byte[] decrypt(byte[] encryptedData, byte[] iv, byte[] secretKeyBytes) {
return encryptedData;
}
@@ -90,7 +100,7 @@
* A HAMC sha256 based HKDF algorithm to pseudo randomly hash data and salt into a byte array of
* given size.
*/
- // Based on google3/third_party/tink/java/src/main/java/com/google/crypto/tink/subtle/Hkdf.java
+ // Based on crypto/tink/subtle/Hkdf.java
@Nullable
static byte[] computeHkdf(byte[] ikm, byte[] salt, int size) {
Mac mac;
@@ -146,4 +156,66 @@
return result;
}
+
+ /**
+ * Computes an HKDF.
+ *
+ * @param macAlgorithm the MAC algorithm used for computing the Hkdf. I.e., "HMACSHA1" or
+ * "HMACSHA256".
+ * @param ikm the input keying material.
+ * @param salt optional salt. A possibly non-secret random value.
+ * (If no salt is provided i.e. if salt has length 0)
+ * then an array of 0s of the same size as the hash
+ * digest is used as salt.
+ * @param info optional context and application specific information.
+ * @param size The length of the generated pseudorandom string in bytes.
+ * @throws GeneralSecurityException if the {@code macAlgorithm} is not supported or if {@code
+ * size} is too large or if {@code salt} is not a valid key for
+ * macAlgorithm (which should not
+ * happen since HMAC allows key sizes up to 2^64).
+ */
+ public static byte[] computeHkdf(
+ String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size)
+ throws GeneralSecurityException {
+ Mac mac = Mac.getInstance(macAlgorithm);
+ if (size > 255 * mac.getMacLength()) {
+ throw new GeneralSecurityException("size too large");
+ }
+ if (salt == null || salt.length == 0) {
+ // According to RFC 5869, Section 2.2 the salt is optional. If no salt is provided
+ // then HKDF uses a salt that is an array of zeros of the same length as the hash
+ // digest.
+ mac.init(new SecretKeySpec(new byte[mac.getMacLength()], macAlgorithm));
+ } else {
+ mac.init(new SecretKeySpec(salt, macAlgorithm));
+ }
+ byte[] prk = mac.doFinal(ikm);
+ byte[] result = new byte[size];
+ int ctr = 1;
+ int pos = 0;
+ mac.init(new SecretKeySpec(prk, macAlgorithm));
+ byte[] digest = new byte[0];
+ while (true) {
+ mac.update(digest);
+ mac.update(info);
+ mac.update((byte) ctr);
+ digest = mac.doFinal();
+ if (pos + digest.length < size) {
+ System.arraycopy(digest, 0, result, pos, digest.length);
+ pos += digest.length;
+ ctr++;
+ } else {
+ System.arraycopy(digest, 0, result, pos, size - pos);
+ break;
+ }
+ }
+ return result;
+ }
+
+ /** Generates the HMAC bytes. */
+ public static byte[] generateHmac(String algorithm, byte[] input, byte[] key) {
+ return Hashing.hmacSha256(new SecretKeySpec(key, algorithm))
+ .hashBytes(input)
+ .asBytes();
+ }
}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java
deleted file mode 100644
index 1c0ec9e..0000000
--- a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.util.encryption;
-
-import androidx.annotation.Nullable;
-
-/**
- * A Cryptor that returns the original data without actual encryption
- */
-public class CryptorImpFake extends Cryptor {
- // Lazily instantiated when {@link #getInstance()} is called.
- @Nullable
- private static CryptorImpFake sCryptor;
-
- /** Returns an instance of CryptorImpFake. */
- public static CryptorImpFake getInstance() {
- if (sCryptor == null) {
- sCryptor = new CryptorImpFake();
- }
- return sCryptor;
- }
-
- private CryptorImpFake() {
- }
-}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java
deleted file mode 100644
index b0e19b4..0000000
--- a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.util.encryption;
-
-import static com.android.server.nearby.NearbyService.TAG;
-
-import android.security.keystore.KeyProperties;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * {@link android.nearby.BroadcastRequest#PRESENCE_VERSION_V1} for identity
- * encryption and decryption.
- */
-public class CryptorImpIdentityV1 extends Cryptor {
-
- // 3 16 byte arrays known by both the encryptor and decryptor.
- private static final byte[] EK_IV =
- new byte[] {14, -123, -39, 42, 109, 127, 83, 27, 27, 11, 91, -38, 92, 17, -84, 66};
- private static final byte[] ESALT_IV =
- new byte[] {46, 83, -19, 10, -127, -31, -31, 12, 31, 76, 63, -9, 33, -66, 15, -10};
- private static final byte[] KTAG_IV =
- {-22, -83, -6, 67, 16, -99, -13, -9, 8, -3, -16, 37, -75, 47, 1, -56};
-
- /** Length of encryption key required by AES/GCM encryption. */
- private static final int ENCRYPTION_KEY_SIZE = 32;
-
- /** Length of salt required by AES/GCM encryption. */
- private static final int AES_CTR_IV_SIZE = 16;
-
- /** Length HMAC tag */
- public static final int HMAC_TAG_SIZE = 8;
-
- /**
- * In the form of "algorithm/mode/padding". Must be the same across broadcast and scan devices.
- */
- private static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
-
- @VisibleForTesting
- static final String ENCRYPT_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
-
- // Lazily instantiated when {@link #getInstance()} is called.
- @Nullable private static CryptorImpIdentityV1 sCryptor;
-
- /** Returns an instance of CryptorImpIdentityV1. */
- public static CryptorImpIdentityV1 getInstance() {
- if (sCryptor == null) {
- sCryptor = new CryptorImpIdentityV1();
- }
- return sCryptor;
- }
-
- @Nullable
- @Override
- public byte[] encrypt(byte[] data, byte[] salt, byte[] authenticityKey) {
- if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
- Log.w(TAG, "Illegal authenticity key size");
- return null;
- }
-
- // Generates a 32 bytes encryption key from authenticity_key
- byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, EK_IV, ENCRYPTION_KEY_SIZE);
- if (encryptionKey == null) {
- Log.e(TAG, "Failed to generate encryption key.");
- return null;
- }
-
- // Encrypts the data using the encryption key
- SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
- Log.e(TAG, "Failed to encrypt with secret key.", e);
- return null;
- }
- byte[] esalt = Cryptor.computeHkdf(salt, ESALT_IV, AES_CTR_IV_SIZE);
- if (esalt == null) {
- Log.e(TAG, "Failed to generate salt.");
- return null;
- }
- try {
- cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(esalt));
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- Log.e(TAG, "Failed to initialize cipher.", e);
- return null;
- }
- try {
- return cipher.doFinal(data);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- Log.e(TAG, "Failed to encrypt with secret key.", e);
- return null;
- }
- }
-
- @Nullable
- @Override
- public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] authenticityKey) {
- if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
- Log.w(TAG, "Illegal authenticity key size");
- return null;
- }
-
- // Generates a 32 bytes encryption key from authenticity_key
- byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, EK_IV, ENCRYPTION_KEY_SIZE);
- if (encryptionKey == null) {
- Log.e(TAG, "Failed to generate encryption key.");
- return null;
- }
-
- // Decrypts the data using the encryption key
- SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
- Log.e(TAG, "Failed to get cipher instance.", e);
- return null;
- }
- byte[] esalt = Cryptor.computeHkdf(salt, ESALT_IV, AES_CTR_IV_SIZE);
- if (esalt == null) {
- return null;
- }
- try {
- cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(esalt));
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- Log.e(TAG, "Failed to initialize cipher.", e);
- return null;
- }
-
- try {
- return cipher.doFinal(encryptedData);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- Log.e(TAG, "Failed to decrypt bytes with secret key.", e);
- return null;
- }
- }
-
- /**
- * Generates a digital signature for the data.
- *
- * @return signature {@code null} if failed to sign
- */
- @Nullable
- @Override
- public byte[] sign(byte[] data, byte[] salt) {
- if (data == null) {
- Log.e(TAG, "Not generate HMAC tag because of invalid data input.");
- return null;
- }
-
- // Generates a 8 bytes HMAC tag
- return Cryptor.computeHkdf(data, salt, HMAC_TAG_SIZE);
- }
-
- /**
- * Generates a digital signature for the data.
- * Uses KTAG_IV as salt value.
- */
- @Nullable
- public byte[] sign(byte[] data) {
- // Generates a 8 bytes HMAC tag
- return sign(data, KTAG_IV);
- }
-
- @Override
- public boolean verify(byte[] data, byte[] key, byte[] signature) {
- return Arrays.equals(sign(data, key), signature);
- }
-
- /**
- * Verifies the signature generated by data and key, with the original signed data. Uses
- * KTAG_IV as salt value.
- */
- public boolean verify(byte[] data, byte[] signature) {
- return verify(data, KTAG_IV, signature);
- }
-}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java
deleted file mode 100644
index 15073fb..0000000
--- a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.util.encryption;
-
-import static com.android.server.nearby.NearbyService.TAG;
-
-import android.security.keystore.KeyProperties;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * {@link android.nearby.BroadcastRequest#PRESENCE_VERSION_V1} for encryption and decryption.
- */
-public class CryptorImpV1 extends Cryptor {
-
- /**
- * In the form of "algorithm/mode/padding". Must be the same across broadcast and scan devices.
- */
- private static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
-
- @VisibleForTesting
- static final String ENCRYPT_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
-
- /** Length of encryption key required by AES/GCM encryption. */
- private static final int ENCRYPTION_KEY_SIZE = 32;
-
- /** Length of salt required by AES/GCM encryption. */
- private static final int AES_CTR_IV_SIZE = 16;
-
- /** Length HMAC tag */
- public static final int HMAC_TAG_SIZE = 16;
-
- // 3 16 byte arrays known by both the encryptor and decryptor.
- private static final byte[] AK_IV =
- new byte[] {12, -59, 19, 23, 96, 57, -59, 19, 117, -31, -116, -61, 86, -25, -33, -78};
- private static final byte[] ASALT_IV =
- new byte[] {111, 48, -83, -79, -10, -102, -16, 73, 43, 55, 102, -127, 58, -19, -113, 4};
- private static final byte[] HK_IV =
- new byte[] {12, -59, 19, 23, 96, 57, -59, 19, 117, -31, -116, -61, 86, -25, -33, -78};
-
- // Lazily instantiated when {@link #getInstance()} is called.
- @Nullable private static CryptorImpV1 sCryptor;
-
- /** Returns an instance of CryptorImpV1. */
- public static CryptorImpV1 getInstance() {
- if (sCryptor == null) {
- sCryptor = new CryptorImpV1();
- }
- return sCryptor;
- }
-
- private CryptorImpV1() {
- }
-
- @Nullable
- @Override
- public byte[] encrypt(byte[] data, byte[] salt, byte[] authenticityKey) {
- if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
- Log.w(TAG, "Illegal authenticity key size");
- return null;
- }
-
- // Generates a 32 bytes encryption key from authenticity_key
- byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, AK_IV, ENCRYPTION_KEY_SIZE);
- if (encryptionKey == null) {
- Log.e(TAG, "Failed to generate encryption key.");
- return null;
- }
-
- // Encrypts the data using the encryption key
- SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
- Log.e(TAG, "Failed to encrypt with secret key.", e);
- return null;
- }
- byte[] asalt = Cryptor.computeHkdf(salt, ASALT_IV, AES_CTR_IV_SIZE);
- if (asalt == null) {
- Log.e(TAG, "Failed to generate salt.");
- return null;
- }
- try {
- cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(asalt));
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- Log.e(TAG, "Failed to initialize cipher.", e);
- return null;
- }
- try {
- return cipher.doFinal(data);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- Log.e(TAG, "Failed to encrypt with secret key.", e);
- return null;
- }
- }
-
- @Nullable
- @Override
- public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] authenticityKey) {
- if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
- Log.w(TAG, "Illegal authenticity key size");
- return null;
- }
-
- // Generates a 32 bytes encryption key from authenticity_key
- byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, AK_IV, ENCRYPTION_KEY_SIZE);
- if (encryptionKey == null) {
- Log.e(TAG, "Failed to generate encryption key.");
- return null;
- }
-
- // Decrypts the data using the encryption key
- SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
- Log.e(TAG, "Failed to get cipher instance.", e);
- return null;
- }
- byte[] asalt = Cryptor.computeHkdf(salt, ASALT_IV, AES_CTR_IV_SIZE);
- if (asalt == null) {
- return null;
- }
- try {
- cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(asalt));
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- Log.e(TAG, "Failed to initialize cipher.", e);
- return null;
- }
-
- try {
- return cipher.doFinal(encryptedData);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- Log.e(TAG, "Failed to decrypt bytes with secret key.", e);
- return null;
- }
- }
-
- @Override
- @Nullable
- public byte[] sign(byte[] data, byte[] key) {
- return generateHmacTag(data, key);
- }
-
- @Override
- public int getSignatureLength() {
- return HMAC_TAG_SIZE;
- }
-
- @Override
- public boolean verify(byte[] data, byte[] key, byte[] signature) {
- return Arrays.equals(sign(data, key), signature);
- }
-
- /** Generates a 16 bytes HMAC tag. This is used for decryptor to verify if the computed HMAC tag
- * is equal to HMAC tag in advertisement to see data integrity. */
- @Nullable
- @VisibleForTesting
- byte[] generateHmacTag(byte[] data, byte[] authenticityKey) {
- if (data == null || authenticityKey == null) {
- Log.e(TAG, "Not generate HMAC tag because of invalid data input.");
- return null;
- }
-
- if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
- Log.e(TAG, "Illegal authenticity key size");
- return null;
- }
-
- // Generates a 32 bytes HMAC key from authenticity_key
- byte[] hmacKey = Cryptor.computeHkdf(authenticityKey, HK_IV, AES_CTR_IV_SIZE);
- if (hmacKey == null) {
- Log.e(TAG, "Failed to generate HMAC key.");
- return null;
- }
-
- // Generates a 16 bytes HMAC tag from authenticity_key
- return Cryptor.computeHkdf(data, hmacKey, HMAC_TAG_SIZE);
- }
-}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorMicImp.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorMicImp.java
new file mode 100644
index 0000000..3dbf85c
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorMicImp.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util.encryption;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.util.ArrayUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * MIC encryption and decryption for {@link android.nearby.BroadcastRequest#PRESENCE_VERSION_V1}
+ * advertisement
+ */
+public class CryptorMicImp extends Cryptor {
+
+ public static final int MIC_LENGTH = 16;
+
+ private static final String ENCRYPT_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
+ private static final byte[] AES_KEY_INFO_BYTES = "Unsigned Section AES key".getBytes(
+ StandardCharsets.US_ASCII);
+ private static final byte[] ADV_NONCE_INFO_BYTES_SALT_DE = "Unsigned Section IV".getBytes(
+ StandardCharsets.US_ASCII);
+ private static final byte[] ADV_NONCE_INFO_BYTES_ENCRYPTION_INFO_DE =
+ "V1 derived salt".getBytes(StandardCharsets.US_ASCII);
+ private static final byte[] METADATA_KEY_HMAC_KEY_INFO_BYTES =
+ "Unsigned Section metadata key HMAC key".getBytes(StandardCharsets.US_ASCII);
+ private static final byte[] MIC_HMAC_KEY_INFO_BYTES = "Unsigned Section HMAC key".getBytes(
+ StandardCharsets.US_ASCII);
+ private static final int AES_KEY_SIZE = 16;
+ private static final int ADV_NONCE_SIZE_SALT_DE = 16;
+ private static final int ADV_NONCE_SIZE_ENCRYPTION_INFO_DE = 12;
+ private static final int HMAC_KEY_SIZE = 32;
+
+ // Lazily instantiated when {@link #getInstance()} is called.
+ @Nullable
+ private static CryptorMicImp sCryptor;
+
+ private CryptorMicImp() {
+ }
+
+ /** Returns an instance of CryptorImpV1. */
+ public static CryptorMicImp getInstance() {
+ if (sCryptor == null) {
+ sCryptor = new CryptorMicImp();
+ }
+ return sCryptor;
+ }
+
+ /**
+ * Generate the meta data encryption key tag
+ * @param metadataEncryptionKey used as identity
+ * @param keySeed authenticity key saved in local and shared credential
+ * @return bytes generated by hmac or {@code null} when there is an error
+ */
+ @Nullable
+ public static byte[] generateMetadataEncryptionKeyTag(byte[] metadataEncryptionKey,
+ byte[] keySeed) {
+ try {
+ byte[] metadataKeyHmacKey = generateMetadataKeyHmacKey(keySeed);
+ return Cryptor.generateHmac(/* algorithm= */ HMAC_SHA256_ALGORITHM, /* input= */
+ metadataEncryptionKey, /* key= */ metadataKeyHmacKey);
+ } catch (GeneralSecurityException e) {
+ Log.e(TAG, "Failed to generate Metadata encryption key tag.", e);
+ return null;
+ }
+ }
+
+ /**
+ * @param salt from the 2 bytes Salt Data Element
+ */
+ @Nullable
+ public static byte[] generateAdvNonce(byte[] salt) throws GeneralSecurityException {
+ return Cryptor.computeHkdf(
+ /* macAlgorithm= */ HMAC_SHA256_ALGORITHM,
+ /* ikm = */ salt,
+ /* salt= */ NP_HKDF_SALT,
+ /* info= */ ADV_NONCE_INFO_BYTES_SALT_DE,
+ /* size= */ ADV_NONCE_SIZE_SALT_DE);
+ }
+
+ /** Generates the 12 bytes nonce with salt from the 2 bytes Salt Data Element */
+ @Nullable
+ public static byte[] generateAdvNonce(byte[] salt, int deIndex)
+ throws GeneralSecurityException {
+ // go/nearby-specs-working-doc
+ // Indices are encoded as big-endian unsigned 32-bit integers, starting at 1.
+ // Index 0 is reserved
+ byte[] indexBytes = new byte[4];
+ indexBytes[3] = (byte) deIndex;
+ byte[] info =
+ ArrayUtils.concatByteArrays(ADV_NONCE_INFO_BYTES_ENCRYPTION_INFO_DE, indexBytes);
+ return Cryptor.computeHkdf(
+ /* macAlgorithm= */ HMAC_SHA256_ALGORITHM,
+ /* ikm = */ salt,
+ /* salt= */ NP_HKDF_SALT,
+ /* info= */ info,
+ /* size= */ ADV_NONCE_SIZE_ENCRYPTION_INFO_DE);
+ }
+
+ @Nullable
+ @Override
+ public byte[] encrypt(byte[] input, byte[] iv, byte[] keySeed) {
+ if (input == null || iv == null || keySeed == null) {
+ return null;
+ }
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ Log.e(TAG, "Failed to encrypt with secret key.", e);
+ return null;
+ }
+
+ byte[] aesKey;
+ try {
+ aesKey = generateAesKey(keySeed);
+ } catch (GeneralSecurityException e) {
+ Log.e(TAG, "Encryption failed because failed to generate the AES key.", e);
+ return null;
+ }
+ if (aesKey == null) {
+ Log.i(TAG, "Failed to generate the AES key.");
+ return null;
+ }
+ SecretKey secretKey = new SecretKeySpec(aesKey, ENCRYPT_ALGORITHM);
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ Log.e(TAG, "Failed to initialize cipher.", e);
+ return null;
+
+ }
+ try {
+ return cipher.doFinal(input);
+ } catch (IllegalBlockSizeException | BadPaddingException e) {
+ Log.e(TAG, "Failed to encrypt with secret key.", e);
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public byte[] decrypt(byte[] encryptedData, byte[] iv, byte[] keySeed) {
+ if (encryptedData == null || iv == null || keySeed == null) {
+ return null;
+ }
+
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ Log.e(TAG, "Failed to get cipher instance.", e);
+ return null;
+ }
+ byte[] aesKey;
+ try {
+ aesKey = generateAesKey(keySeed);
+ } catch (GeneralSecurityException e) {
+ Log.e(TAG, "Decryption failed because failed to generate the AES key.", e);
+ return null;
+ }
+ SecretKey secretKey = new SecretKeySpec(aesKey, ENCRYPT_ALGORITHM);
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ Log.e(TAG, "Failed to initialize cipher.", e);
+ return null;
+ }
+
+ try {
+ return cipher.doFinal(encryptedData);
+ } catch (IllegalBlockSizeException | BadPaddingException e) {
+ Log.e(TAG, "Failed to decrypt bytes with secret key.", e);
+ return null;
+ }
+ }
+
+ @Override
+ @Nullable
+ public byte[] sign(byte[] data, byte[] key) {
+ byte[] res = generateHmacTag(data, key);
+ return res;
+ }
+
+ @Override
+ public int getSignatureLength() {
+ return MIC_LENGTH;
+ }
+
+ @Override
+ public boolean verify(byte[] data, byte[] key, byte[] signature) {
+ return Arrays.equals(sign(data, key), signature);
+ }
+
+ /**
+ * Generates a 16 bytes HMAC tag. This is used for decryptor to verify if the computed HMAC tag
+ * is equal to HMAC tag in advertisement to see data integrity.
+ *
+ * @param input concatenated advertisement UUID, header, section header, derived salt, and
+ * section content
+ * @param keySeed the MIC HMAC key is calculated using the derived key
+ * @return the first 16 bytes of HMAC-SHA256 result
+ */
+ @Nullable
+ @VisibleForTesting
+ byte[] generateHmacTag(byte[] input, byte[] keySeed) {
+ try {
+ if (input == null || keySeed == null) {
+ return null;
+ }
+ byte[] micHmacKey = generateMicHmacKey(keySeed);
+ byte[] hmac = Cryptor.generateHmac(/* algorithm= */ HMAC_SHA256_ALGORITHM, /* input= */
+ input, /* key= */ micHmacKey);
+ if (ArrayUtils.isEmpty(hmac)) {
+ return null;
+ }
+ return Arrays.copyOf(hmac, MIC_LENGTH);
+ } catch (GeneralSecurityException e) {
+ Log.e(TAG, "Failed to generate mic hmac key.", e);
+ return null;
+ }
+ }
+
+ @Nullable
+ private static byte[] generateAesKey(byte[] keySeed) throws GeneralSecurityException {
+ return Cryptor.computeHkdf(
+ /* macAlgorithm= */ HMAC_SHA256_ALGORITHM,
+ /* ikm = */ keySeed,
+ /* salt= */ NP_HKDF_SALT,
+ /* info= */ AES_KEY_INFO_BYTES,
+ /* size= */ AES_KEY_SIZE);
+ }
+
+ private static byte[] generateMetadataKeyHmacKey(byte[] keySeed)
+ throws GeneralSecurityException {
+ return generateHmacKey(keySeed, METADATA_KEY_HMAC_KEY_INFO_BYTES);
+ }
+
+ private static byte[] generateMicHmacKey(byte[] keySeed) throws GeneralSecurityException {
+ return generateHmacKey(keySeed, MIC_HMAC_KEY_INFO_BYTES);
+ }
+
+ private static byte[] generateHmacKey(byte[] keySeed, byte[] info)
+ throws GeneralSecurityException {
+ return Cryptor.computeHkdf(
+ /* macAlgorithm= */ HMAC_SHA256_ALGORITHM,
+ /* ikm = */ keySeed,
+ /* salt= */ NP_HKDF_SALT,
+ /* info= */ info,
+ /* size= */ HMAC_KEY_SIZE);
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/EncryptionInfoTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/EncryptionInfoTest.java
index a1eb57b..6ec7c57 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/EncryptionInfoTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/EncryptionInfoTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertThrows;
+import com.android.server.nearby.presence.EncryptionInfo.EncodingScheme;
import com.android.server.nearby.util.ArrayUtils;
import org.junit.Test;
@@ -50,7 +51,7 @@
public void test_getMethods_signature() {
byte[] data = ArrayUtils.append((byte) 0b10001000, SALT);
EncryptionInfo info = new EncryptionInfo(data);
- assertThat(info.getEncodingScheme()).isEqualTo(EncryptionInfo.EncodingScheme.SIGNATURE);
+ assertThat(info.getEncodingScheme()).isEqualTo(EncodingScheme.SIGNATURE);
assertThat(info.getSalt()).isEqualTo(SALT);
}
@@ -58,7 +59,14 @@
public void test_getMethods_mic() {
byte[] data = ArrayUtils.append((byte) 0b10000000, SALT);
EncryptionInfo info = new EncryptionInfo(data);
- assertThat(info.getEncodingScheme()).isEqualTo(EncryptionInfo.EncodingScheme.MIC);
+ assertThat(info.getEncodingScheme()).isEqualTo(EncodingScheme.MIC);
+ assertThat(info.getSalt()).isEqualTo(SALT);
+ }
+ @Test
+ public void test_toBytes() {
+ byte[] data = EncryptionInfo.toByte(EncodingScheme.MIC, SALT);
+ EncryptionInfo info = new EncryptionInfo(data);
+ assertThat(info.getEncodingScheme()).isEqualTo(EncodingScheme.MIC);
assertThat(info.getSalt()).isEqualTo(SALT);
}
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java
index a759baf..455c432 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java
@@ -18,8 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import androidx.test.filters.SdkSuppress;
-
import org.junit.Test;
public final class ArrayUtilsTest {
@@ -30,51 +28,58 @@
private static final byte[] BYTES_ALL = new byte[] {7, 9, 8};
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConcatByteArraysNoInput() {
assertThat(ArrayUtils.concatByteArrays().length).isEqualTo(0);
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConcatByteArraysOneEmptyArray() {
assertThat(ArrayUtils.concatByteArrays(BYTES_EMPTY).length).isEqualTo(0);
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConcatByteArraysOneNonEmptyArray() {
assertThat(ArrayUtils.concatByteArrays(BYTES_ONE)).isEqualTo(BYTES_ONE);
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConcatByteArraysMultipleNonEmptyArrays() {
assertThat(ArrayUtils.concatByteArrays(BYTES_ONE, BYTES_TWO)).isEqualTo(BYTES_ALL);
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testConcatByteArraysMultipleArrays() {
assertThat(ArrayUtils.concatByteArrays(BYTES_ONE, BYTES_EMPTY, BYTES_TWO))
.isEqualTo(BYTES_ALL);
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testIsEmptyNull_returnsTrue() {
assertThat(ArrayUtils.isEmpty(null)).isTrue();
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testIsEmpty_returnsTrue() {
assertThat(ArrayUtils.isEmpty(new byte[]{})).isTrue();
}
@Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testIsEmpty_returnsFalse() {
assertThat(ArrayUtils.isEmpty(BYTES_ALL)).isFalse();
}
+
+ @Test
+ public void testAppendByte() {
+ assertThat(ArrayUtils.append((byte) 2, BYTES_ONE)).isEqualTo(new byte[]{2, 7, 9});
+ }
+
+ @Test
+ public void testAppendByteNull() {
+ assertThat(ArrayUtils.append((byte) 2, null)).isEqualTo(new byte[]{2});
+ }
+
+ @Test
+ public void testAppendByteToArray() {
+ assertThat(ArrayUtils.append(BYTES_ONE, (byte) 2)).isEqualTo(new byte[]{7, 9, 2});
+ }
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java
deleted file mode 100644
index f0294fc..0000000
--- a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.util.encryption;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.util.Log;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-public class CryptorImpIdentityV1Test {
- private static final String TAG = "CryptorImpIdentityV1Test";
- private static final byte[] SALT = new byte[] {102, 22};
- private static final byte[] DATA =
- new byte[] {107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
- private static final byte[] AUTHENTICITY_KEY =
- new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
-
- @Test
- public void test_encrypt_decrypt() {
- Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
- byte[] encryptedData = identityCryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
-
- assertThat(identityCryptor.decrypt(encryptedData, SALT, AUTHENTICITY_KEY)).isEqualTo(DATA);
- }
-
- @Test
- public void test_encryption() {
- Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
- byte[] encryptedData = identityCryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
-
- // for debugging
- Log.d(TAG, "encrypted data is: " + Arrays.toString(encryptedData));
-
- assertThat(encryptedData).isEqualTo(getEncryptedData());
- }
-
- @Test
- public void test_decryption() {
- Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
- byte[] decryptedData =
- identityCryptor.decrypt(getEncryptedData(), SALT, AUTHENTICITY_KEY);
- // for debugging
- Log.d(TAG, "decrypted data is: " + Arrays.toString(decryptedData));
-
- assertThat(decryptedData).isEqualTo(DATA);
- }
-
- @Test
- public void generateHmacTag() {
- CryptorImpIdentityV1 identityCryptor = CryptorImpIdentityV1.getInstance();
- byte[] generatedTag = identityCryptor.sign(DATA);
- byte[] expectedTag = new byte[]{50, 116, 95, -87, 63, 123, -79, -43};
- assertThat(generatedTag).isEqualTo(expectedTag);
- }
-
- private static byte[] getEncryptedData() {
- return new byte[]{6, -31, -32, -123, 43, -92, -47, -110, -65, 126, -15, -51, -19, -43};
- }
-}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java
deleted file mode 100644
index 3ca2575..0000000
--- a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.util.encryption;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.util.Log;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-/**
- * Unit test for {@link CryptorImpV1}
- */
-public final class CryptorImpV1Test {
- private static final String TAG = "CryptorImpV1Test";
- private static final byte[] SALT = new byte[] {102, 22};
- private static final byte[] DATA =
- new byte[] {107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
- private static final byte[] AUTHENTICITY_KEY =
- new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
-
- @Test
- public void test_encryption() {
- Cryptor v1Cryptor = CryptorImpV1.getInstance();
- byte[] encryptedData = v1Cryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
-
- // for debugging
- Log.d(TAG, "encrypted data is: " + Arrays.toString(encryptedData));
-
- assertThat(encryptedData).isEqualTo(getEncryptedData());
- }
-
- @Test
- public void test_encryption_invalidInput() {
- Cryptor v1Cryptor = CryptorImpV1.getInstance();
- assertThat(v1Cryptor.encrypt(DATA, SALT, new byte[]{1, 2, 3, 4, 6})).isNull();
- }
-
- @Test
- public void test_decryption() {
- Cryptor v1Cryptor = CryptorImpV1.getInstance();
- byte[] decryptedData =
- v1Cryptor.decrypt(getEncryptedData(), SALT, AUTHENTICITY_KEY);
- // for debugging
- Log.d(TAG, "decrypted data is: " + Arrays.toString(decryptedData));
-
- assertThat(decryptedData).isEqualTo(DATA);
- }
-
- @Test
- public void test_decryption_invalidInput() {
- Cryptor v1Cryptor = CryptorImpV1.getInstance();
- assertThat(v1Cryptor.decrypt(getEncryptedData(), SALT, new byte[]{1, 2, 3, 4, 6})).isNull();
- }
-
- @Test
- public void generateSign() {
- CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
- byte[] generatedTag = v1Cryptor.sign(DATA, AUTHENTICITY_KEY);
- byte[] expectedTag = new byte[]{
- 100, 88, -104, 80, -66, 107, -38, 95, 34, 40, -56, -23, -90, 90, -87, 12};
- assertThat(generatedTag).isEqualTo(expectedTag);
- }
-
- @Test
- public void test_verify() {
- CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
- byte[] expectedTag = new byte[]{
- 100, 88, -104, 80, -66, 107, -38, 95, 34, 40, -56, -23, -90, 90, -87, 12};
-
- assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, expectedTag)).isTrue();
- assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, DATA)).isFalse();
- }
-
- @Test
- public void test_generateHmacTag_sameResult() {
- CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
- byte[] res1 = v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY);
- assertThat(res1)
- .isEqualTo(v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY));
- }
-
- @Test
- public void test_generateHmacTag_nullData() {
- CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
- assertThat(v1Cryptor.generateHmacTag(/* data= */ null, AUTHENTICITY_KEY)).isNull();
- }
-
- @Test
- public void test_generateHmacTag_nullKey() {
- CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
- assertThat(v1Cryptor.generateHmacTag(DATA, /* authenticityKey= */ null)).isNull();
- }
-
- private static byte[] getEncryptedData() {
- return new byte[]{-92, 94, -99, -97, 81, -48, -7, 119, -64, -22, 45, -49, -50, 92};
- }
-}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorMicImpTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorMicImpTest.java
new file mode 100644
index 0000000..b6d2333
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorMicImpTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.util.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+/**
+ * Unit test for {@link CryptorMicImp}
+ */
+public final class CryptorMicImpTest {
+ private static final String TAG = "CryptorImpV1Test";
+ private static final byte[] SALT = new byte[]{102, 22};
+ private static final byte[] DATA =
+ new byte[]{107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
+ private static final byte[] AUTHENTICITY_KEY =
+ new byte[]{-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
+
+ private static byte[] getEncryptedData() {
+ return new byte[]{112, 23, -111, 87, 122, -27, 45, -25, -35, 84, -89, 115, 61, 113};
+ }
+
+ @Test
+ public void test_encryption() throws Exception {
+ Cryptor v1Cryptor = CryptorMicImp.getInstance();
+ byte[] encryptedData =
+ v1Cryptor.encrypt(DATA, CryptorMicImp.generateAdvNonce(SALT), AUTHENTICITY_KEY);
+ assertThat(encryptedData).isEqualTo(getEncryptedData());
+ }
+
+ @Test
+ public void test_decryption() throws Exception {
+ Cryptor v1Cryptor = CryptorMicImp.getInstance();
+ byte[] decryptedData =
+ v1Cryptor.decrypt(getEncryptedData(), CryptorMicImp.generateAdvNonce(SALT),
+ AUTHENTICITY_KEY);
+ assertThat(decryptedData).isEqualTo(DATA);
+ }
+
+ @Test
+ public void test_verify() {
+ CryptorMicImp v1Cryptor = CryptorMicImp.getInstance();
+ byte[] expectedTag = new byte[]{
+ -80, -51, -101, -7, -65, 110, 37, 68, 122, -128, 57, -90, -115, -59, -61, 46};
+ assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, expectedTag)).isTrue();
+ assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, DATA)).isFalse();
+ }
+
+ @Test
+ public void test_generateHmacTag_sameResult() {
+ CryptorMicImp v1Cryptor = CryptorMicImp.getInstance();
+ byte[] res1 = v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY);
+ assertThat(res1)
+ .isEqualTo(v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY));
+ }
+
+ @Test
+ public void test_generateHmacTag_nullData() {
+ CryptorMicImp v1Cryptor = CryptorMicImp.getInstance();
+ assertThat(v1Cryptor.generateHmacTag(/* data= */ null, AUTHENTICITY_KEY)).isNull();
+ }
+
+ @Test
+ public void test_generateHmacTag_nullKey() {
+ CryptorMicImp v1Cryptor = CryptorMicImp.getInstance();
+ assertThat(v1Cryptor.generateHmacTag(DATA, /* authenticityKey= */ null)).isNull();
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java
index ca612e3..1fb2236 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java
@@ -42,7 +42,7 @@
assertThat(res2).hasLength(outputSize);
assertThat(res1).isNotEqualTo(res2);
assertThat(res1)
- .isEqualTo(CryptorImpV1.computeHkdf(DATA, AUTHENTICITY_KEY, outputSize));
+ .isEqualTo(CryptorMicImp.computeHkdf(DATA, AUTHENTICITY_KEY, outputSize));
}
@Test