From 2116c75fc1cd83893bc97afaffdd8e0b4a3209af Mon Sep 17 00:00:00 2001 From: Infi Date: Sun, 4 Aug 2024 21:03:37 +0200 Subject: [PATCH] feat(labs): CryptographicAgeVerificationSandbox Signed-off-by: Infi --- .../internals/CryptographicAgeVerification.kt | 41 ++++++++++ .../revolt/screens/labs/LabsHomeScreen.kt | 31 ++++++++ .../revolt/screens/labs/LabsRootScreen.kt | 5 ++ .../CryptographicAgeVerificationSandbox.kt | 78 +++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 app/src/main/java/chat/revolt/internals/CryptographicAgeVerification.kt create mode 100644 app/src/main/java/chat/revolt/screens/labs/ui/sandbox/CryptographicAgeVerificationSandbox.kt diff --git a/app/src/main/java/chat/revolt/internals/CryptographicAgeVerification.kt b/app/src/main/java/chat/revolt/internals/CryptographicAgeVerification.kt new file mode 100644 index 00000000..7cee54ff --- /dev/null +++ b/app/src/main/java/chat/revolt/internals/CryptographicAgeVerification.kt @@ -0,0 +1,41 @@ +package chat.revolt.internals + +import java.security.MessageDigest + +class CryptographicAgeVerification { + fun getProofHash( + seed: String, + userAge: Int, + minAllowedAge: Int + ): Pair { + var proof = MessageDigest.getInstance("SHA-256").digest(seed.toByteArray()) + + for (i in 1 until userAge - minAllowedAge) { + proof = MessageDigest.getInstance("SHA-256").digest(proof) + } + + var ageHash = + MessageDigest.getInstance("SHA-256").digest(seed.toByteArray()) + + for (i in 1 until userAge - 1) { + ageHash = MessageDigest.getInstance("SHA-256").digest(ageHash) + } + + return ageHash to proof + } + + fun proveAge( + minAllowedAge: Int, + proofHash: ByteArray, + ageHash: ByteArray, + ): Boolean { + var verifyHash = + MessageDigest.getInstance("SHA-256").digest(proofHash.toString().toByteArray()) + + for (i in 1 until minAllowedAge - 1) { + verifyHash = MessageDigest.getInstance("SHA-256").digest(verifyHash) + } + + return verifyHash.contentEquals(ageHash) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/labs/LabsHomeScreen.kt b/app/src/main/java/chat/revolt/screens/labs/LabsHomeScreen.kt index e4eee9c9..b28787b6 100644 --- a/app/src/main/java/chat/revolt/screens/labs/LabsHomeScreen.kt +++ b/app/src/main/java/chat/revolt/screens/labs/LabsHomeScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -36,6 +37,7 @@ import androidx.navigation.NavController enum class LabsHomeScreenTab { Home, Mockups, + Sandboxes, } @OptIn(ExperimentalMaterial3Api::class) @@ -80,6 +82,19 @@ fun LabsHomeScreen(navController: NavController) { Text("UI Mockups") } ) + NavigationBarItem( + selected = currentTab.value == LabsHomeScreenTab.Sandboxes, + onClick = { currentTab.value = LabsHomeScreenTab.Sandboxes }, + icon = { + Icon( + imageVector = Icons.Default.PlayArrow, + contentDescription = null, + ) + }, + label = { + Text("Sandboxes") + } + ) } } ) { @@ -128,6 +143,22 @@ fun LabsHomeScreen(navController: NavController) { HorizontalDivider() } } + + LabsHomeScreenTab.Sandboxes -> { + Column( + modifier = Modifier.verticalScroll(rememberScrollState()) + ) { + ListItem( + headlineContent = { + Text("Cryptographic Age Verification") + }, + modifier = Modifier.clickable { + navController.navigate("sandboxes/cryptoageverif") + } + ) + HorizontalDivider() + } + } } } } diff --git a/app/src/main/java/chat/revolt/screens/labs/LabsRootScreen.kt b/app/src/main/java/chat/revolt/screens/labs/LabsRootScreen.kt index 626768b3..49402468 100644 --- a/app/src/main/java/chat/revolt/screens/labs/LabsRootScreen.kt +++ b/app/src/main/java/chat/revolt/screens/labs/LabsRootScreen.kt @@ -13,6 +13,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import chat.revolt.api.settings.FeatureFlags import chat.revolt.screens.labs.ui.mockups.CallScreenMockup +import chat.revolt.screens.labs.ui.sandbox.CryptographicAgeVerificationSandbox annotation class LabsFeature @@ -62,6 +63,10 @@ fun LabsRootScreen(topNav: NavController) { composable("mockups/call") { CallScreenMockup() } + + composable("sandboxes/cryptoageverif") { + CryptographicAgeVerificationSandbox(labsNav) + } } } } diff --git a/app/src/main/java/chat/revolt/screens/labs/ui/sandbox/CryptographicAgeVerificationSandbox.kt b/app/src/main/java/chat/revolt/screens/labs/ui/sandbox/CryptographicAgeVerificationSandbox.kt new file mode 100644 index 00000000..244c6394 --- /dev/null +++ b/app/src/main/java/chat/revolt/screens/labs/ui/sandbox/CryptographicAgeVerificationSandbox.kt @@ -0,0 +1,78 @@ +package chat.revolt.screens.labs.ui.sandbox + +import android.util.Log +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.navigation.NavController +import chat.revolt.api.REVOLT_KJBOOK +import chat.revolt.internals.CryptographicAgeVerification + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CryptographicAgeVerificationSandbox(navController: NavController) { + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + "Crypto Age Verify Sandbox", + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + navigationIcon = { + IconButton(onClick = { navController.popBackStack() }) { + Icon(Icons.AutoMirrored.Default.ArrowBack, "Back") + } + } + ) + } + ) { pv -> + Column( + Modifier + .padding(pv) + .fillMaxSize() + ) { + Button(onClick = { + val cav = CryptographicAgeVerification() + val (ageHash, verifyHash) = cav.getProofHash( + seed = REVOLT_KJBOOK, + userAge = 21, + minAllowedAge = 18 + ) + + Log.d( + "CryptographicAgeVerificationSandbox", + "ageHash: ${ageHash.contentToString()}" + ) + Log.d( + "CryptographicAgeVerificationSandbox", + "verifyHash: ${verifyHash.contentToString()}" + ) + + val verification = cav.proveAge( + minAllowedAge = 18, + proofHash = verifyHash, + ageHash = ageHash + ) + + Log.d("CryptographicAgeVerificationSandbox", "Verification result: $verification") + }) { + Text("Run Cryptographic Age Verification") + } + } + } +} \ No newline at end of file