fix: read back sent copies after the sec 5.6 upstream fix, pinned at 2d65d66

This commit is contained in:
randogoth 2026-09-28 23:49:39 +03:00
parent 52947e153d
commit d6f4c434fa
36 changed files with 4223 additions and 3118 deletions

View file

@ -5,8 +5,9 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:smol_mail/data/providers/providers.dart";
import "package:smol_mail/shared/utils/format.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/address.dart";
import "package:smol_mail/smol/errors.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/ui.dart";
/// The full key, the address to hand out, and any key this one replaced —
/// the row can only afford a fingerprint, and §8 turns on the user being able
@ -125,7 +126,7 @@ class _ContactDetailScreenState extends ConsumerState<ContactDetailScreen> {
const Divider(height: 1),
const SizedBox(height: 20),
_field(context, "fingerprint", fingerprint(contact.key)),
_field(context, "public key", b32encode(contact.key)),
_field(context, "public key", contact.key),
Row(
children: [
Expanded(
@ -162,7 +163,7 @@ class _ContactDetailScreenState extends ConsumerState<ContactDetailScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(fingerprint(entry.key)),
Text(b32encode(entry.key),
Text(entry.key,
style: Theme.of(context).textTheme.labelSmall),
Text(
"replaced ${formatTime(entry.until ~/ 1000)}",

View file

@ -7,7 +7,7 @@ import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/errors.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/ui.dart";
/// Contacts: the addresses bound to keys, with the trust badge and the number
/// of keys each has replaced — the §8 surface for noticing rotations.

View file

@ -1,4 +1,3 @@
import "dart:typed_data";
import "package:auto_route/auto_route.dart";
import "package:flutter/material.dart";
@ -40,7 +39,7 @@ class MessageDetailScreen extends ConsumerWidget {
}
Future<void> _nameSender(BuildContext context, WidgetRef ref,
Uint8List senderKey) async {
String senderKey) async {
final client = ref.read(clientProvider);
final controller = TextEditingController();
final address = await showDialog<String>(

View file

@ -8,9 +8,9 @@ import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/widgets/primary_button.dart";
import "package:smol_mail/presentation/widgets/secondary_button.dart";
import "package:smol_mail/presentation/widgets/small_loading_spinner.dart";
import "package:smol_mail/smol/crypto.dart";
import "package:smol_mail/smol/address.dart";
import "package:smol_mail/smol/errors.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/ui.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
@ -49,6 +49,10 @@ class _OnboardingScreenState extends ConsumerState<OnboardingScreen> {
@override
void dispose() {
// The backup step's copy of the master is overwritten, not left to the
// garbage collector — the honest zeroization Dart allows.
createdMaster?.fillRange(0, createdMaster!.length, 0);
createdMaster = null;
seedController.dispose();
restoreAddressController.dispose();
addressController.dispose();
@ -81,11 +85,12 @@ class _OnboardingScreenState extends ConsumerState<OnboardingScreen> {
}
}
void _createIdentity() {
Future<void> _createIdentity() async {
_clearAbandonedIdentity();
final client = ref.read(clientProvider);
try {
final master = client.createIdentity();
final master = await client.createIdentity();
if (!mounted) return;
setState(() {
createdMaster = master;
step = _Step.backup;

View file

@ -12,9 +12,8 @@ import "package:smol_mail/data/providers/providers.dart";
import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/crypto.dart";
import "package:smol_mail/smol/errors.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/ui.dart";
/// Identity card, server pins, rotation and the wipe. The seed is the only
/// secret; revealing it is always an explicit action.
@ -69,10 +68,11 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
// pinned servers into one shareable file; never the seed.
Future<void> _export() async {
final store = ref.read(storeProvider);
final data = store.exportData();
// exportData is the sealed gsmol container itself; the file body is its
// JSON, already sealed to the master-derived key.
final bytes = Uint8List.fromList(utf8.encode(await store.exportData()));
final stamp = DateTime.now().toIso8601String().substring(0, 10);
final fileName = "kirakira-backup-$stamp.json";
final bytes = Uint8List.fromList(utf8.encode(jsonEncode(data)));
try {
// share_plus has no file-sharing implementation on desktop platforms —
// it throws UnimplementedError for Linux/Windows/macOS — so those save
@ -108,8 +108,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
final path = files.isEmpty ? null : files.single.path;
if (path == null) return;
try {
final data = jsonDecode(File(path).readAsStringSync()) as Map;
final summary = await ref.read(storeProvider).importData(data);
final summary =
await ref.read(storeProvider).importData(File(path).readAsStringSync());
ref.read(revisionProvider.notifier).bump();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@ -165,7 +165,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
ref.read(revisionProvider.notifier).bump();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("rotated; new key ${b32encode(fresh.publicKey)}")),
SnackBar(content: Text("rotated; new key ${fresh.publicKey}")),
);
}
} catch (err) {
@ -313,7 +313,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
contentPadding: EdgeInsets.zero,
title: Text(host),
subtitle: Text(
b32encode(key),
key,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelSmall,
),

View file

@ -7,7 +7,6 @@ import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/avatar_color.dart";
import "package:smol_mail/shared/utils/format.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/store.dart";
class MessageTile extends ConsumerWidget {
@ -27,7 +26,7 @@ class MessageTile extends ConsumerWidget {
who = record.recipient ?? "";
} else if (opened.sender != null) {
final known = store.addressForKey(opened.sender!);
who = known ?? "unknown · ${b32encode(opened.sender!).substring(0, 4)}";
who = known ?? "unknown · ${opened.sender!.substring(0, 4)}";
} else {
who = "?";
}

View file

@ -1,4 +1,3 @@
import "dart:typed_data";
import "package:auto_route/auto_route.dart";
import "package:flutter/material.dart";
@ -9,8 +8,8 @@ import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/shared/utils/format.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/client.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/store.dart";
import "package:smol_mail/smol/ui.dart";
/// The opened message: trust row (fingerprint and how the sender's key is
/// known), frontmatter fields, and the plain body — smol mail has no HTML.
@ -18,7 +17,7 @@ class MessageView extends ConsumerWidget {
final String folder;
final MailRecord record;
final OpenedRecord opened;
final void Function(Uint8List senderKey)? onNameSender;
final void Function(String senderKey)? onNameSender;
const MessageView({
super.key,
@ -78,7 +77,7 @@ class MessageView extends ConsumerWidget {
],
if (!isSent && senderKey != null) ...[
const SizedBox(height: 10),
_keyBlock(context, "${fingerprint(senderKey)}\n${b32encode(senderKey)}"),
_keyBlock(context, "${fingerprint(senderKey)}\n$senderKey"),
],
const SizedBox(height: 18),
_field(context, isSent ? "To" : "From",