Files asks for your Flamenet passphrase, not a second secret #96
@@ -773,7 +773,7 @@ fun ColumnScope.AccountScreen(
|
||||
Text("Files", fontWeight = FontWeight.Medium, fontSize = 16.sp)
|
||||
Spacer(Modifier.height(Space.xs))
|
||||
Text(
|
||||
"Files encrypted on this phone, with a passphrase of their own. "
|
||||
"Files encrypted on this phone, opened with your Flamenet passphrase. "
|
||||
+ "Nothing is stored on a server.",
|
||||
fontSize = 13.sp,
|
||||
color = Palette.SecondaryText,
|
||||
|
||||
@@ -118,10 +118,22 @@ private fun ColumnScope.FilesSetUp(
|
||||
var passphrase by remember { mutableStateOf("") }
|
||||
var again by remember { mutableStateOf("") }
|
||||
|
||||
// 🔴 **"A passphrase of their own" told people to invent a second secret,
|
||||
// and iOS never did.** iOS says "your passphrase" and takes whatever is
|
||||
// typed, so a person naturally uses their Flamenet one and it simply
|
||||
// works. Android's copy sent them off to make up something else, then
|
||||
// asked for it again weeks later — and forgetting it is the one-way door
|
||||
// described above this function.
|
||||
//
|
||||
// ⚠️ **Nothing about the derivation changes.** This is still a free-choice
|
||||
// string protecting the identity record, so anyone who already set a
|
||||
// different one keeps opening their files with it. Making the account
|
||||
// passphrase the ACTUAL key would be a migration with permanent data loss
|
||||
// on the far side of any mistake, and it is not what this fixes.
|
||||
Text(
|
||||
"Files on this phone are encrypted with a passphrase of their own. "
|
||||
+ "Flamenet doesn't keep it, so nobody can open them by picking up "
|
||||
+ "your unlocked phone.",
|
||||
"Use your Flamenet passphrase — the one you sign in with. Files on "
|
||||
+ "this phone are encrypted with it, and Flamenet keeps no copy, so "
|
||||
+ "nobody can open them by picking up your unlocked phone.",
|
||||
fontSize = 14.sp, color = Palette.SecondaryText,
|
||||
)
|
||||
Spacer(Modifier.height(Space.sm))
|
||||
@@ -134,7 +146,7 @@ private fun ColumnScope.FilesSetUp(
|
||||
|
||||
OutlinedTextField(
|
||||
value = passphrase, onValueChange = { passphrase = it },
|
||||
label = { Text("Choose a passphrase") },
|
||||
label = { Text("Your Flamenet passphrase") },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
singleLine = true,
|
||||
modifier = Modifier.fillMaxWidth().testTag("files-new-passphrase"),
|
||||
@@ -168,7 +180,10 @@ private fun ColumnScope.FilesUnlock(
|
||||
) {
|
||||
var passphrase by remember { mutableStateOf("") }
|
||||
|
||||
Text("Enter your Files passphrase to open them.",
|
||||
// ⚠️ Not "your Files passphrase". That name implied a second secret even
|
||||
// for somebody who had used their Flamenet one, and left them hunting for
|
||||
// a passphrase they never set.
|
||||
Text("Enter your Flamenet passphrase to open them.",
|
||||
fontSize = 14.sp, color = Palette.SecondaryText)
|
||||
Spacer(Modifier.height(Space.md))
|
||||
OutlinedTextField(
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.flamenet.messenger.app
|
||||
|
||||
import java.io.File
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Files asks for the Flamenet passphrase, not a second secret.
|
||||
*
|
||||
* 🔴 **The screen used to say the files were encrypted "with a passphrase of
|
||||
* their own", and iOS never said anything like it.** iOS says "your
|
||||
* passphrase" and takes whatever is typed, so people use their Flamenet one
|
||||
* and it works. Android's copy sent them off to invent something else — and
|
||||
* forgetting it is a one-way door: the label keys are derived from the
|
||||
* identity it protects and exist nowhere else, so every vaulted file becomes
|
||||
* permanently unreadable.
|
||||
*
|
||||
* ⚠️ **This is about the words, and the words are the whole feature here.**
|
||||
* Nothing in the derivation changed: it is still a free-choice string
|
||||
* protecting the identity record, so anyone who already set a different one
|
||||
* keeps opening their files with it. A guard on the copy is therefore the only
|
||||
* guard there can be.
|
||||
*/
|
||||
class FilesUsesTheAccountPassphraseTest {
|
||||
|
||||
private fun source(name: String): String {
|
||||
var dir = File(System.getProperty("user.dir"))
|
||||
while (!File(dir, "app/src/main/kotlin").isDirectory && dir.parentFile != null) {
|
||||
dir = dir.parentFile
|
||||
}
|
||||
val f = File(dir, "app/src/main/kotlin/io/flamenet/messenger/app/$name")
|
||||
assertTrue(f.isFile, "could not find $name — this guard is reading nothing")
|
||||
return f.readText()
|
||||
}
|
||||
|
||||
/** Lines that are shown, not lines that explain. */
|
||||
private fun copyLines(body: String): List<String> =
|
||||
body.lines().filterNot {
|
||||
val t = it.trimStart()
|
||||
t.startsWith("//") || t.startsWith("*") || t.startsWith("/*")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the setup screen names the Flamenet passphrase`() {
|
||||
val shown = copyLines(source("FilesScreen.kt")).joinToString("\n")
|
||||
assertTrue(
|
||||
shown.contains("Use your Flamenet passphrase"),
|
||||
"the Files setup screen no longer tells people to use the passphrase " +
|
||||
"they already have, which is the whole of android's parity gap with iOS",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ⚠️ The exact phrase that caused it. Kept as a literal because a person
|
||||
* rewording this screen should have to read the comment above the string
|
||||
* and decide deliberately, not discover the consequence from a user.
|
||||
*/
|
||||
@Test
|
||||
fun `no screen tells somebody to invent a second secret`() {
|
||||
for (name in listOf("FilesScreen.kt", "ChatScreens.kt")) {
|
||||
val shown = copyLines(source(name)).joinToString("\n")
|
||||
assertTrue(
|
||||
!shown.contains("passphrase of their own"),
|
||||
"$name tells people the files have a passphrase of their own — " +
|
||||
"iOS asks for the one they already have, and forgetting a " +
|
||||
"second one destroys every vaulted file",
|
||||
)
|
||||
assertTrue(
|
||||
!shown.contains("Choose a passphrase"),
|
||||
"$name still asks somebody to choose a new passphrase for Files",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user