[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