feat(app): an authenticator's key copied, marked sensitive, and its code six digits

A phone without a camera whose authenticator doesn't open otpauth: had no
way but typing the key. It is copied again, the key alone (ADR 0017
amended, the developer's decision of 3 Nov 2026): on Android through each
app's SensitiveClipboard.kt, marked EXTRA_IS_SENSITIVE, so Gboard and the
system show no preview and keep it out of their histories; elsewhere through
Flutter's clipboard. Emptied 60 seconds later if the clipboard still holds
it. The recovery phrase and codes stay never copied.

The code's field counted "/7", taking an optional space for a seventh
character: it now takes six digits alone, a pasted space dropped, and counts
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 22:07:35 +01:00
commit 8c70ad98a1
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 470 additions and 36 deletions

View file

@ -69,6 +69,7 @@ class MainActivity : FlutterActivity() {
val messenger = flutterEngine.dartExecutor.binaryMessenger
// Secrets halkyon_app shows kept out of screenshots and recents.
ScreenGuard.attach(this, messenger)
SensitiveClipboard.attach(this, messenger)
// The recording's side of the system (RecorderService).
MethodChannel(messenger, "app.halkyon.train/recorder").setMethodCallHandler { call, result ->
when (call.method) {

View file

@ -0,0 +1,46 @@
package app.halkyon.train
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.PersistableBundle
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
/**
* Copies an authenticator's key for halkyon_app's PlatformSecretClipboard,
* marked sensitive (ClipDescription.EXTRA_IS_SENSITIVE, read by the system
* from Android 13 on and by Gboard before it), so it shows no preview and
* stays out of clipboard histories; and empties the clipboard again if it
* still holds the key, which Android lets an app read only while in front.
*/
object SensitiveClipboard {
private const val IS_SENSITIVE = "android.content.extra.IS_SENSITIVE"
fun attach(context: Context, messenger: BinaryMessenger) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
MethodChannel(messenger, "app.halkyon/clipboard").setMethodCallHandler { call, result ->
when (call.method) {
"copySensitive" -> {
val clip = ClipData.newPlainText("", call.arguments as String)
clip.description.extras = PersistableBundle().apply { putBoolean(IS_SENSITIVE, true) }
clipboard.setPrimaryClip(clip)
result.success(null)
}
"clearIf" -> {
val held = clipboard.primaryClip?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.text
if (held?.toString() == call.arguments as String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
clipboard.clearPrimaryClip()
} else {
clipboard.setPrimaryClip(ClipData.newPlainText("", ""))
}
}
result.success(null)
}
else -> result.notImplemented()
}
}
}
}