-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathProofOfConceptAdvertisingIDV4.java
More file actions
156 lines (130 loc) · 7.47 KB
/
ProofOfConceptAdvertisingIDV4.java
File metadata and controls
156 lines (130 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.uid2.operator;
import com.uid2.operator.service.EncodingUtils;
import com.uid2.operator.service.TokenUtils;
import io.vertx.core.buffer.Buffer;
import org.junit.jupiter.api.Test;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.*;
public class ProofOfConceptAdvertisingIDV4 {
private byte[] generateIV(String salt, byte[] firstLevelHashLast16Bytes, String metadata, int keyId) {
int iv_length = 12;
String iv_base = salt
.concat(Arrays.toString(firstLevelHashLast16Bytes))
.concat(String.valueOf(metadata))
.concat(String.valueOf(keyId));
return Arrays.copyOfRange(EncodingUtils.getSha256Bytes(iv_base), 0, iv_length);
}
private byte[] padIV16Bytes(byte[] iv12Bytes) {
// Pad the 12-byte IV to 16 bytes for AES-CTR (standard block size)
byte[] ctrIV = new byte[16];
System.arraycopy(iv12Bytes, 0, ctrIV, 0, 12); // Copy 12-byte IV
// Remaining 4 bytes are already zero-initialized (counter starts at 0)
System.out.println("Padded IV for AES-CTR (16 bytes): " + EncodingUtils.toBase64String(ctrIV));
return ctrIV;
}
private byte[] encryptFirstLevelHash(String encryptionKey, byte[] firstLevelHashLast16Bytes, byte[] iv) throws Exception {
// AES256-CTR Encryption
// Set up AES256-CTR cipher
Cipher aesCtr = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(encryptionKey.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(padIV16Bytes(iv));
// Encrypt the 16-byte first level hash
aesCtr.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
return aesCtr.doFinal(firstLevelHashLast16Bytes);
}
private byte[] decryptEncryptedFirstLevelHash(String encryptionKey, byte[] encryptedFirstLevelHash, byte[] iv) throws Exception {
Cipher aesCtr = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(encryptionKey.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(padIV16Bytes(iv));
aesCtr.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
return aesCtr.doFinal(encryptedFirstLevelHash);
}
private byte generateChecksum(byte[] data) {
// Simple XOR checksum of all bytes
byte checksum = 0;
for (byte b : data) {
checksum ^= b;
}
System.out.println("Checksum: 0x" + String.format("%02X", checksum));
return checksum;
}
private byte[] constructUIDv4(byte metadata, int keyId, byte[] iv, byte[] encryptedFirstLevelHash) {
Buffer buffer = Buffer.buffer();
buffer.appendByte(metadata);
buffer.appendBytes(new byte[] {
(byte) (keyId & 0xFF), // LSB
(byte) ((keyId >> 8) & 0xFF), // Middle
(byte) ((keyId >> 16) & 0xFF) // MSB
});
buffer.appendBytes(iv);
buffer.appendBytes(encryptedFirstLevelHash);
byte checksum = generateChecksum(buffer.getBytes());
buffer.appendByte(checksum);
return buffer.getBytes();
}
@Test
public void encryptDecryptAdvertisingID() throws Exception {
String salt = "salt1234salt1234salt1234salt1234";
String key = "key12345key12345key12345key12345";
int encryptionKeyId = 1000000;
byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromIdentity("test@example.com", salt);
byte[] firstLevelHashLast16Bytes = Arrays.copyOfRange(firstLevelHash, firstLevelHash.length - 16, firstLevelHash.length);
byte metadata = (byte) 0b10110101;
// generating advertising ID
byte[] iv = generateIV(salt, firstLevelHashLast16Bytes, key, encryptionKeyId);
byte[] encryptedFirstLevelHash = encryptFirstLevelHash(key, firstLevelHashLast16Bytes, iv);
// Construct v4 UID: metadata + key id + iv + encrypted first level hash + checksum
byte[] v4UID = constructUIDv4(metadata, encryptionKeyId, iv, encryptedFirstLevelHash);
assertEquals(33, v4UID.length);
// Test extraction of components
byte extractedMetadata = v4UID[0];
byte[] keyIdBytes = Arrays.copyOfRange(v4UID, 1, 4);
int extractedKeyId = (keyIdBytes[0] & 0xFF) | ((keyIdBytes[1] & 0xFF) << 8) | ((keyIdBytes[2] & 0xFF) << 16);
byte[] extractedIV = Arrays.copyOfRange(v4UID, 4, 16);
byte[] extractedEncryptedHash = Arrays.copyOfRange(v4UID, 16, 32);
byte extractedChecksum = v4UID[32];
assertEquals(metadata, extractedMetadata);
assertEquals(encryptionKeyId, extractedKeyId);
assertArrayEquals(iv, extractedIV);
assertArrayEquals(encryptedFirstLevelHash, extractedEncryptedHash);
// Verify checksum
byte recomputedChecksum = generateChecksum(Arrays.copyOfRange(v4UID, 0, 32));
assertEquals(extractedChecksum, recomputedChecksum);
// Test decryption to verify correctness
byte[] decryptedFirstLevelHash = decryptEncryptedFirstLevelHash(key, extractedEncryptedHash, extractedIV);
assertEquals(16, decryptedFirstLevelHash.length);
assertArrayEquals(firstLevelHashLast16Bytes, decryptedFirstLevelHash);
// Recalculate IV
byte[] recalculatedIV = generateIV(salt, decryptedFirstLevelHash, key, extractedKeyId);
assertArrayEquals(extractedIV, recalculatedIV);
// Rebuild UID
byte[] reconstructedUID = constructUIDv4(metadata, encryptionKeyId, recalculatedIV, encryptFirstLevelHash(key, decryptedFirstLevelHash, recalculatedIV));
assertArrayEquals(reconstructedUID, v4UID);
// Print results for verification
System.out.println("Original metadata: 0x" + String.format("%02X", extractedMetadata));
System.out.println("Original key ID: " + encryptionKeyId);
System.out.println("Original IV (12 bytes): " + EncodingUtils.toBase64String(iv));
System.out.println("Original 16-byte first level hash: " + EncodingUtils.toBase64String(firstLevelHashLast16Bytes));
System.out.println("Encrypted hash: " + EncodingUtils.toBase64String(encryptedFirstLevelHash));
System.out.println("V4 UID (33 bytes): " + EncodingUtils.toBase64String(v4UID));
System.out.println();
System.out.println("=== EXTRACTION VERIFICATION ===");
System.out.println("Extracted metadata: 0x" + String.format("%02X", extractedMetadata));
System.out.println("Extracted key ID: " + extractedKeyId);
System.out.println("Extracted IV (12 bytes): " + EncodingUtils.toBase64String(extractedIV));
System.out.println("Extracted encrypted hash: " + EncodingUtils.toBase64String(extractedEncryptedHash));
System.out.println("Extracted checksum: 0x" + String.format("%02X", extractedChecksum));
System.out.println("Decrypted 16-byte first level hash: " + EncodingUtils.toBase64String(decryptedFirstLevelHash));
System.out.println("Reconstructed V4 UID (33 bytes): " + EncodingUtils.toBase64String(reconstructedUID));
System.out.println("✓ All verifications passed - V4 UID format is correct and functional!");
}
}