@@ -88,9 +88,17 @@ private const val DEVICE_INTEGRITY_SOFT_EXPIRATION_CHECK_PERIOD = 600L // 10 min
88
88
private const val TEMPORARY_DEVICE_KEY_VALIDITY = 64800L // 18 hours
89
89
private const val DEVICE_INTEGRITY_SOFT_EXPIRATION = 100800L // 28 hours
90
90
private const val DEVICE_INTEGRITY_HARD_EXPIRATION = 432000L // 5 day
91
-
91
+ const val INTERMEDIATE_INTEGRITY_HARD_EXPIRATION = 86400L // 1 day
92
92
private const val TAG = " IntegrityExtensions"
93
93
94
+ fun IntegrityRequestWrapper.getExpirationTime () = runCatching {
95
+ val creationTimeStamp = deviceIntegrityWrapper?.creationTime ? : Timestamp (0 , 0 )
96
+ val creationTime = (creationTimeStamp.seconds ? : 0 ) * 1000 + (creationTimeStamp.nanos ? : 0 ) / 1_000_000
97
+ val currentTimeStamp = makeTimestamp(System .currentTimeMillis())
98
+ val currentTime = (currentTimeStamp.seconds ? : 0 ) * 1000 + (currentTimeStamp.nanos ? : 0 ) / 1_000_000
99
+ return @runCatching currentTime - creationTime
100
+ }.getOrDefault(0 )
101
+
94
102
private fun Context.getProtoFile (): File {
95
103
val directory = File (filesDir, " finsky/shared" )
96
104
if (! directory.exists()) {
@@ -103,6 +111,12 @@ private fun Context.getProtoFile(): File {
103
111
return file
104
112
}
105
113
114
+ private fun getExpressFilePB (context : Context ): ExpressFilePB {
115
+ return runCatching { FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB .ADAPTER .decode(input) } }
116
+ .onFailure { Log .w(TAG , " Failed to read express cache " , it) }
117
+ .getOrDefault(ExpressFilePB ())
118
+ }
119
+
106
120
fun PackageManager.getPackageInfoCompat (packageName : String , flags : Int = 0): PackageInfo {
107
121
return runCatching {
108
122
if (Build .VERSION .SDK_INT >= 33 ) {
@@ -161,14 +175,14 @@ fun readAes128GcmBuilderFromClientKey(clientKey: ClientKey?): Aead? {
161
175
}
162
176
}
163
177
164
- suspend fun getIntegrityRequestWrapper (context : Context , expressIntegritySession : ExpressIntegritySession , accountName : String ) = withContext(Dispatchers .IO ){
178
+ suspend fun getIntegrityRequestWrapper (context : Context , expressIntegritySession : ExpressIntegritySession , accountName : String ) = withContext(Dispatchers .IO ) {
165
179
fun getUpdatedWebViewRequestMode (webViewRequestMode : Int ): Int {
166
180
return when (webViewRequestMode) {
167
181
in 0 .. 2 -> webViewRequestMode + 1
168
182
else -> 1
169
183
}
170
184
}
171
- val expressFilePB = FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB . ADAPTER .decode(input) }
185
+ val expressFilePB = getExpressFilePB (context)
172
186
expressFilePB.integrityRequestWrapper.filter { item ->
173
187
TextUtils .equals(item.packageName, expressIntegritySession.packageName) && item.cloudProjectNumber == expressIntegritySession.cloudProjectNumber && getUpdatedWebViewRequestMode(
174
188
expressIntegritySession.webViewRequestMode
@@ -237,7 +251,7 @@ fun fetchCertificateChain(context: Context, attestationChallenge: ByteArray?): L
237
251
suspend fun updateLocalExpressFilePB (context : Context , intermediateIntegrityResponseData : IntermediateIntegrityResponseData ) = withContext(Dispatchers .IO ) {
238
252
Log .d(TAG , " Writing AAR to express cache" )
239
253
val intermediateIntegrity = intermediateIntegrityResponseData.intermediateIntegrity
240
- val expressFilePB = FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB . ADAPTER .decode(input) }
254
+ val expressFilePB = getExpressFilePB (context)
241
255
242
256
val integrityResponseWrapper = IntegrityRequestWrapper .Builder ().apply {
243
257
accountName = intermediateIntegrity.accountName
@@ -285,7 +299,7 @@ suspend fun updateExpressSessionTime(context: Context, expressIntegritySession:
285
299
expressIntegritySession.packageName
286
300
}
287
301
288
- val expressFilePB = FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB . ADAPTER .decode(input) }
302
+ val expressFilePB = getExpressFilePB (context)
289
303
290
304
val clientKey = expressFilePB.integrityTokenTimeMap ? : IntegrityTokenTimeMap ()
291
305
val timeMutableMap = clientKey.newBuilder().timeMap.toMutableMap()
@@ -307,21 +321,30 @@ suspend fun updateExpressSessionTime(context: Context, expressIntegritySession:
307
321
}
308
322
309
323
suspend fun updateExpressClientKey (context : Context ) = withContext(Dispatchers .IO ) {
310
- val expressFilePB = FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB .ADAPTER .decode(input) }
311
-
324
+ val expressFilePB = getExpressFilePB(context)
312
325
val oldClientKey = expressFilePB.clientKey ? : ClientKey ()
313
- var clientKey = ClientKey .Builder ().apply {
314
- val currentTimeMillis = System .currentTimeMillis()
315
- generated = Timestamp .Builder ().seconds(currentTimeMillis / 1000 ).nanos((Math .floorMod(currentTimeMillis, 1000L ) * 1000000 ).toInt()).build()
326
+ val generated = makeTimestamp(System .currentTimeMillis())
327
+
328
+ val oldGeneratedSec = oldClientKey.generated?.seconds ? : 0
329
+ val newGeneratedSec = generated.seconds ? : 0
330
+
331
+ val useOld = oldClientKey.keySetHandle?.size != 0 && oldGeneratedSec >= newGeneratedSec - TEMPORARY_DEVICE_KEY_VALIDITY
332
+
333
+ val clientKey = if (useOld) {
334
+ Log .d(TAG , " Using existing clientKey, not expired. oldGeneratedSec=$oldGeneratedSec newGeneratedSec=$newGeneratedSec " )
335
+ oldClientKey
336
+ } else {
337
+ Log .d(TAG , " Generating new clientKey. oldKeyValid=${oldClientKey.keySetHandle?.size != 0 } expired=${oldGeneratedSec < newGeneratedSec - TEMPORARY_DEVICE_KEY_VALIDITY } " )
316
338
val keySetHandle = KeysetHandle .generateNew(AesGcmKeyManager .aes128GcmTemplate())
317
- val outputStream = ByteArrayOutputStream ()
318
- CleartextKeysetHandle .write(keySetHandle, BinaryKeysetWriter .withOutputStream(outputStream))
319
- this .keySetHandle = ByteBuffer .wrap(outputStream.toByteArray()).toByteString()
320
- }.build()
321
- if (oldClientKey.keySetHandle?.size != 0 ) {
322
- if (oldClientKey.generated?.seconds != null && clientKey.generated?.seconds != null && oldClientKey.generated.seconds < clientKey.generated?.seconds!! .minus(TEMPORARY_DEVICE_KEY_VALIDITY )) {
323
- clientKey = oldClientKey
339
+ val keyBytes = ByteArrayOutputStream ().use { output ->
340
+ CleartextKeysetHandle .write(keySetHandle, BinaryKeysetWriter .withOutputStream(output))
341
+ output.toByteArray()
324
342
}
343
+ Log .d(TAG , " New clientKey generated at timestamp: ${generated.seconds} " )
344
+ ClientKey .Builder ()
345
+ .generated(generated)
346
+ .keySetHandle(ByteBuffer .wrap(keyBytes).toByteString())
347
+ .build()
325
348
}
326
349
327
350
val newExpressFilePB = expressFilePB.newBuilder().clientKey(clientKey).build()
@@ -330,7 +353,7 @@ suspend fun updateExpressClientKey(context: Context) = withContext(Dispatchers.I
330
353
}
331
354
332
355
suspend fun updateExpressAuthTokenWrapper (context : Context , expressIntegritySession : ExpressIntegritySession , authToken : String , clientKey : ClientKey ) = withContext(Dispatchers .IO ) {
333
- var expressFilePB = FileInputStream (context.getProtoFile()).use { input -> ExpressFilePB . ADAPTER .decode(input) }
356
+ var expressFilePB = getExpressFilePB (context)
334
357
335
358
val createTimeSeconds = expressFilePB.tokenWrapper?.deviceIntegrityWrapper?.creationTime?.seconds ? : 0
336
359
val lastManualSoftRefreshTime = expressFilePB.tokenWrapper?.lastManualSoftRefreshTime?.seconds ? : 0
@@ -386,6 +409,7 @@ private suspend fun regenerateToken(
386
409
this .deviceIntegrityToken = deviceIntegrityToken ? : ByteString .EMPTY
387
410
this .creationTime = makeTimestamp(System .currentTimeMillis())
388
411
}.build()
412
+ this .lastManualSoftRefreshTime = makeTimestamp(System .currentTimeMillis())
389
413
}.build()
390
414
} catch (e: Exception ) {
391
415
Log .d(TAG , " regenerateToken: error " , e)
0 commit comments