|
| 1 | +package com.uid2.operator.service; |
| 2 | + |
| 3 | +import javax.crypto.Cipher; |
| 4 | +import javax.crypto.spec.IvParameterSpec; |
| 5 | +import javax.crypto.spec.SecretKeySpec; |
| 6 | +import io.vertx.core.buffer.Buffer; |
| 7 | +import java.util.Arrays; |
| 8 | + |
| 9 | +public class V4TokenUtils { |
| 10 | + public static byte[] generateIV(String salt, byte[] firstLevelHashLast16Bytes, byte metadata, int keyId) { |
| 11 | + int iv_length = 12; |
| 12 | + String iv_base = salt |
| 13 | + .concat(Arrays.toString(firstLevelHashLast16Bytes)) |
| 14 | + .concat(Byte.toString(metadata)) |
| 15 | + .concat(String.valueOf(keyId)); |
| 16 | + return Arrays.copyOfRange(EncodingUtils.getSha256Bytes(iv_base), 0, iv_length); |
| 17 | + } |
| 18 | + |
| 19 | + private static byte[] padIV16Bytes(byte[] iv) { |
| 20 | + // Pad the 12-byte IV to 16 bytes for AES-CTR (standard block size) |
| 21 | + byte[] paddedIV = new byte[16]; |
| 22 | + System.arraycopy(iv, 0, paddedIV, 0, 12); |
| 23 | + // Remaining 4 bytes are already zero-initialized (counter starts at 0) |
| 24 | + return paddedIV; |
| 25 | + } |
| 26 | + |
| 27 | + private static byte[] encryptHash(String encryptionKey, byte[] hash, byte[] iv) throws Exception { |
| 28 | + // Set up AES256-CTR cipher |
| 29 | + Cipher aesCtr = Cipher.getInstance("AES/CTR/NoPadding"); |
| 30 | + SecretKeySpec secretKey = new SecretKeySpec(encryptionKey.getBytes(), "AES"); |
| 31 | + IvParameterSpec ivSpec = new IvParameterSpec(padIV16Bytes(iv)); |
| 32 | + |
| 33 | + aesCtr.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); |
| 34 | + return aesCtr.doFinal(hash); |
| 35 | + } |
| 36 | + |
| 37 | + public static byte generateChecksum(byte[] data) { |
| 38 | + // Simple XOR checksum of all bytes |
| 39 | + byte checksum = 0; |
| 40 | + for (byte b : data) { |
| 41 | + checksum ^= b; |
| 42 | + } |
| 43 | + System.out.println("Checksum: 0x" + String.format("%02X", checksum)); |
| 44 | + return checksum; |
| 45 | + } |
| 46 | + |
| 47 | + public static byte[] buildAdvertisingIdV4(byte metadata, byte[] firstLevelHash, int keyId, String key, String salt) throws Exception { |
| 48 | + byte[] hash16Bytes = Arrays.copyOfRange(firstLevelHash, 0, 16); |
| 49 | + byte[] iv = V4TokenUtils.generateIV(salt, hash16Bytes, metadata, keyId); |
| 50 | + byte[] encryptedFirstLevelHash = V4TokenUtils.encryptHash(key, hash16Bytes, iv); |
| 51 | + |
| 52 | + Buffer buffer = Buffer.buffer(); |
| 53 | + buffer.appendByte(metadata); |
| 54 | + buffer.appendBytes(new byte[] { |
| 55 | + (byte) (keyId & 0xFF), // LSB |
| 56 | + (byte) ((keyId >> 8) & 0xFF), // Middle |
| 57 | + (byte) ((keyId >> 16) & 0xFF) // MSB |
| 58 | + }); |
| 59 | + buffer.appendBytes(iv); |
| 60 | + buffer.appendBytes(encryptedFirstLevelHash); |
| 61 | + |
| 62 | + byte checksum = generateChecksum(buffer.getBytes()); |
| 63 | + buffer.appendByte(checksum); |
| 64 | + |
| 65 | + return buffer.getBytes(); |
| 66 | + } |
| 67 | +} |
0 commit comments