login using login token

This commit is contained in:
otsmr 2026-05-10 00:09:41 +02:00
parent 105129023a
commit f735070a7c
25 changed files with 431 additions and 211 deletions

View file

@ -0,0 +1,22 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
class FlutterKeyManager {
const FlutterKeyManager();
static Future<Uint8List> getLoginToken() => RustLib.instance.api
.crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken();
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FlutterKeyManager && runtimeType == other.runtimeType;
}

View file

@ -5,6 +5,7 @@
import 'bridge.dart';
import 'bridge/callbacks.dart';
import 'bridge/wrapper/key_manager.dart';
import 'bridge/wrapper/user_discovery.dart';
import 'dart:async';
import 'dart:convert';
@ -70,7 +71,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.12.0';
@override
int get rustContentHash => 1680338106;
int get rustContentHash => 1007286393;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
@ -82,6 +83,9 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
}
abstract class RustLibApi extends BaseApi {
Future<Uint8List>
crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken();
Future<Uint8List>
crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion();
@ -165,7 +169,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
@override
Future<Uint8List>
crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion() {
crateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginToken() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
@ -181,6 +185,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
decodeSuccessData: sse_decode_list_prim_u_8_strict,
decodeErrorData: sse_decode_AnyhowException,
),
constMeta:
kCrateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginTokenConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta
get kCrateBridgeWrapperKeyManagerFlutterKeyManagerGetLoginTokenConstMeta =>
const TaskConstMeta(
debugName: "flutter_key_manager_get_login_token",
argNames: [],
);
@override
Future<Uint8List>
crateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersion() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 2,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_list_prim_u_8_strict,
decodeErrorData: sse_decode_AnyhowException,
),
constMeta:
kCrateBridgeWrapperUserDiscoveryFlutterUserDiscoveryGetCurrentVersionConstMeta,
argValues: [],
@ -211,7 +248,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 2,
funcId: 3,
port: port_,
);
},
@ -254,7 +291,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 3,
funcId: 4,
port: port_,
);
},
@ -296,7 +333,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 4,
funcId: 5,
port: port_,
);
},
@ -334,7 +371,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 5,
funcId: 6,
port: port_,
);
},
@ -375,7 +412,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 6,
funcId: 7,
port: port_,
);
},
@ -499,7 +536,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 7,
funcId: 8,
port: port_,
);
},
@ -564,7 +601,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 8,
funcId: 9,
port: port_,
);
},
@ -1183,6 +1220,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return dco_decode_init_config(raw);
}
@protected
FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 0)
throw Exception('unexpected arr length: expect 0 but see ${arr.length}');
return FlutterKeyManager();
}
@protected
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@ -1378,6 +1424,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return (sse_decode_init_config(deserializer));
}
@protected
FlutterKeyManager sse_decode_flutter_key_manager(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
return FlutterKeyManager();
}
@protected
FlutterUserDiscovery sse_decode_flutter_user_discovery(
SseDeserializer deserializer,
@ -1821,6 +1875,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_init_config(self, serializer);
}
@protected
void sse_encode_flutter_key_manager(
FlutterKeyManager self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
}
@protected
void sse_encode_flutter_user_discovery(
FlutterUserDiscovery self,

View file

@ -5,6 +5,7 @@
import 'bridge.dart';
import 'bridge/callbacks.dart';
import 'bridge/wrapper/key_manager.dart';
import 'bridge/wrapper/user_discovery.dart';
import 'dart:async';
import 'dart:convert';
@ -119,6 +120,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
InitConfig dco_decode_box_autoadd_init_config(dynamic raw);
@protected
FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw);
@protected
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
@ -204,6 +208,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer);
@protected
FlutterKeyManager sse_decode_flutter_key_manager(
SseDeserializer deserializer,
);
@protected
FlutterUserDiscovery sse_decode_flutter_user_discovery(
SseDeserializer deserializer,
@ -397,6 +406,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_flutter_key_manager(
FlutterKeyManager self,
SseSerializer serializer,
);
@protected
void sse_encode_flutter_user_discovery(
FlutterUserDiscovery self,

View file

@ -8,6 +8,7 @@
import 'bridge.dart';
import 'bridge/callbacks.dart';
import 'bridge/wrapper/key_manager.dart';
import 'bridge/wrapper/user_discovery.dart';
import 'dart:async';
import 'dart:convert';
@ -121,6 +122,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
InitConfig dco_decode_box_autoadd_init_config(dynamic raw);
@protected
FlutterKeyManager dco_decode_flutter_key_manager(dynamic raw);
@protected
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
@ -206,6 +210,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer);
@protected
FlutterKeyManager sse_decode_flutter_key_manager(
SseDeserializer deserializer,
);
@protected
FlutterUserDiscovery sse_decode_flutter_user_discovery(
SseDeserializer deserializer,
@ -399,6 +408,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer,
);
@protected
void sse_encode_flutter_key_manager(
FlutterKeyManager self,
SseSerializer serializer,
);
@protected
void sse_encode_flutter_user_discovery(
FlutterUserDiscovery self,

View file

@ -35,7 +35,7 @@ class AppState {
static bool isInBackgroundTask = false;
static bool allowErrorTrackingViaSentry = false;
static bool gotMessageFromServer = false;
static int latestAppVersionId = 110;
static int latestAppVersionId = 111;
}
class AppGlobalKeys {

View file

@ -80,7 +80,7 @@ void main() async {
unawaited(StartupGuard.markAppStartup());
var storageError = await twonlyMinimumInitialization();
unawaited(initFCMService());
await initFCMService();
var userExists = false;
@ -185,6 +185,13 @@ Future<void> runMigrations() async {
}
});
}
if (userService.currentUser.appVersion < 111) {
await UserService.update((u) {
u
..appVersion = 111
..canUseLoginTokenForAuth = false;
});
}
}
Future<void> postStartupTasks() async {

View file

@ -128,6 +128,9 @@ class UserData {
@JsonKey(defaultValue: true)
bool updateFCMToken = true;
@JsonKey(defaultValue: true)
bool canUseLoginTokenForAuth = true;
// --- BACKUP ---
DateTime? nextTimeToShowBackupNotice;

View file

@ -1248,66 +1248,6 @@ class ApplicationData_GetUserById extends $pb.GeneratedMessage {
void clearUserId() => $_clearField(1);
}
class ApplicationData_UpdatePlanOptions extends $pb.GeneratedMessage {
factory ApplicationData_UpdatePlanOptions({
$core.bool? autoRenewal,
}) {
final result = create();
if (autoRenewal != null) result.autoRenewal = autoRenewal;
return result;
}
ApplicationData_UpdatePlanOptions._();
factory ApplicationData_UpdatePlanOptions.fromBuffer(
$core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory ApplicationData_UpdatePlanOptions.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'ApplicationData.UpdatePlanOptions',
package:
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
createEmptyInstance: create)
..aOB(1, _omitFieldNames ? '' : 'autoRenewal')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_UpdatePlanOptions clone() => deepCopy();
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
ApplicationData_UpdatePlanOptions copyWith(
void Function(ApplicationData_UpdatePlanOptions) updates) =>
super.copyWith((message) =>
updates(message as ApplicationData_UpdatePlanOptions))
as ApplicationData_UpdatePlanOptions;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_UpdatePlanOptions create() =>
ApplicationData_UpdatePlanOptions._();
@$core.override
ApplicationData_UpdatePlanOptions createEmptyInstance() => create();
@$core.pragma('dart2js:noInline')
static ApplicationData_UpdatePlanOptions getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<ApplicationData_UpdatePlanOptions>(
create);
static ApplicationData_UpdatePlanOptions? _defaultInstance;
@$pb.TagNumber(1)
$core.bool get autoRenewal => $_getBF(0);
@$pb.TagNumber(1)
set autoRenewal($core.bool value) => $_setBool(0, value);
@$pb.TagNumber(1)
$core.bool hasAutoRenewal() => $_has(0);
@$pb.TagNumber(1)
void clearAutoRenewal() => $_clearField(1);
}
class ApplicationData_GetAvailablePlans extends $pb.GeneratedMessage {
factory ApplicationData_GetAvailablePlans() => create();
@ -2178,7 +2118,7 @@ enum ApplicationData_ApplicationData {
deprecated16,
deprecated17,
removeAdditionalUser,
updatePlanOptions,
deprecated19,
downloadDone,
getSignedPrekeyByUserid,
updateSignedPrekey,
@ -2209,7 +2149,7 @@ class ApplicationData extends $pb.GeneratedMessage {
ApplicationData_Deprecated? deprecated16,
ApplicationData_Deprecated? deprecated17,
ApplicationData_RemoveAdditionalUser? removeAdditionalUser,
ApplicationData_UpdatePlanOptions? updatePlanOptions,
ApplicationData_Deprecated? deprecated19,
ApplicationData_DownloadDone? downloadDone,
ApplicationData_GetSignedPreKeyByUserId? getSignedPrekeyByUserid,
ApplicationData_UpdateSignedPreKey? updateSignedPrekey,
@ -2241,7 +2181,7 @@ class ApplicationData extends $pb.GeneratedMessage {
if (deprecated17 != null) result.deprecated17 = deprecated17;
if (removeAdditionalUser != null)
result.removeAdditionalUser = removeAdditionalUser;
if (updatePlanOptions != null) result.updatePlanOptions = updatePlanOptions;
if (deprecated19 != null) result.deprecated19 = deprecated19;
if (downloadDone != null) result.downloadDone = downloadDone;
if (getSignedPrekeyByUserid != null)
result.getSignedPrekeyByUserid = getSignedPrekeyByUserid;
@ -2283,7 +2223,7 @@ class ApplicationData extends $pb.GeneratedMessage {
16: ApplicationData_ApplicationData.deprecated16,
17: ApplicationData_ApplicationData.deprecated17,
18: ApplicationData_ApplicationData.removeAdditionalUser,
19: ApplicationData_ApplicationData.updatePlanOptions,
19: ApplicationData_ApplicationData.deprecated19,
20: ApplicationData_ApplicationData.downloadDone,
22: ApplicationData_ApplicationData.getSignedPrekeyByUserid,
23: ApplicationData_ApplicationData.updateSignedPrekey,
@ -2380,10 +2320,9 @@ class ApplicationData extends $pb.GeneratedMessage {
18, _omitFieldNames ? '' : 'removeAdditionalUser',
protoName: 'removeAdditionalUser',
subBuilder: ApplicationData_RemoveAdditionalUser.create)
..aOM<ApplicationData_UpdatePlanOptions>(
19, _omitFieldNames ? '' : 'updatePlanOptions',
protoName: 'updatePlanOptions',
subBuilder: ApplicationData_UpdatePlanOptions.create)
..aOM<ApplicationData_Deprecated>(19, _omitFieldNames ? '' : 'deprecated19',
protoName: 'deprecated_19',
subBuilder: ApplicationData_Deprecated.create)
..aOM<ApplicationData_DownloadDone>(
20, _omitFieldNames ? '' : 'downloadDone',
protoName: 'downloadDone',
@ -2672,16 +2611,15 @@ class ApplicationData extends $pb.GeneratedMessage {
$_ensure(14);
@$pb.TagNumber(19)
ApplicationData_UpdatePlanOptions get updatePlanOptions => $_getN(15);
ApplicationData_Deprecated get deprecated19 => $_getN(15);
@$pb.TagNumber(19)
set updatePlanOptions(ApplicationData_UpdatePlanOptions value) =>
$_setField(19, value);
set deprecated19(ApplicationData_Deprecated value) => $_setField(19, value);
@$pb.TagNumber(19)
$core.bool hasUpdatePlanOptions() => $_has(15);
$core.bool hasDeprecated19() => $_has(15);
@$pb.TagNumber(19)
void clearUpdatePlanOptions() => $_clearField(19);
void clearDeprecated19() => $_clearField(19);
@$pb.TagNumber(19)
ApplicationData_UpdatePlanOptions ensureUpdatePlanOptions() => $_ensure(15);
ApplicationData_Deprecated ensureDeprecated19() => $_ensure(15);
@$pb.TagNumber(20)
ApplicationData_DownloadDone get downloadDone => $_getN(16);

View file

@ -447,13 +447,13 @@ const ApplicationData$json = {
'10': 'deprecated17'
},
{
'1': 'updatePlanOptions',
'1': 'deprecated_19',
'3': 19,
'4': 1,
'5': 11,
'6': '.client_to_server.ApplicationData.UpdatePlanOptions',
'6': '.client_to_server.ApplicationData.Deprecated',
'9': 0,
'10': 'updatePlanOptions'
'10': 'deprecated19'
},
{
'1': 'downloadDone',
@ -561,7 +561,6 @@ const ApplicationData$json = {
ApplicationData_ChangeUsername$json,
ApplicationData_UpdateGoogleFcmToken$json,
ApplicationData_GetUserById$json,
ApplicationData_UpdatePlanOptions$json,
ApplicationData_GetAvailablePlans$json,
ApplicationData_GetAddAccountsInvites$json,
ApplicationData_GetCurrentPlanInfos$json,
@ -636,14 +635,6 @@ const ApplicationData_GetUserById$json = {
],
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_UpdatePlanOptions$json = {
'1': 'UpdatePlanOptions',
'2': [
{'1': 'auto_renewal', '3': 1, '4': 1, '5': 8, '10': 'autoRenewal'},
],
};
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_GetAvailablePlans$json = {
'1': 'GetAvailablePlans',
@ -788,48 +779,47 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'dGVkSABSDGRlcHJlY2F0ZWQxNRJTCg1kZXByZWNhdGVkXzE2GBAgASgLMiwuY2xpZW50X3RvX3'
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRGVwcmVjYXRlZEgAUgxkZXByZWNhdGVkMTYSUwoNZGVw'
'cmVjYXRlZF8xNxgRIAEoCzIsLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlcH'
'JlY2F0ZWRIAFIMZGVwcmVjYXRlZDE3EmMKEXVwZGF0ZVBsYW5PcHRpb25zGBMgASgLMjMuY2xp'
'ZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlUGxhbk9wdGlvbnNIAFIRdXBkYX'
'RlUGxhbk9wdGlvbnMSVAoMZG93bmxvYWREb25lGBQgASgLMi4uY2xpZW50X3RvX3NlcnZlci5B'
'cHBsaWNhdGlvbkRhdGEuRG93bmxvYWREb25lSABSDGRvd25sb2FkRG9uZRJ1ChdnZXRTaWduZW'
'RQcmVrZXlCeVVzZXJpZBgWIAEoCzI5LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRh'
'LkdldFNpZ25lZFByZUtleUJ5VXNlcklkSABSF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkEmYKEn'
'VwZGF0ZVNpZ25lZFByZWtleRgXIAEoCzI0LmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25E'
'YXRhLlVwZGF0ZVNpZ25lZFByZUtleUgAUhJ1cGRhdGVTaWduZWRQcmVrZXkSVwoNZGVsZXRlQW'
'Njb3VudBgYIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlbGV0ZUFj'
'Y291bnRIAFINZGVsZXRlQWNjb3VudBJOCgpyZXBvcnRVc2VyGBkgASgLMiwuY2xpZW50X3RvX3'
'NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVwb3J0VXNlckgAUgpyZXBvcnRVc2VyEloKDmNoYW5n'
'ZVVzZXJuYW1lGBogASgLMjAuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuQ2hhbm'
'dlVXNlcm5hbWVIAFIOY2hhbmdlVXNlcm5hbWUSUQoLaXBhUHVyY2hhc2UYGyABKAsyLS5jbGll'
'bnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFQdXJjaGFzZUgAUgtpcGFQdXJjaGFzZR'
'JXCg1pcGFGb3JjZUNoZWNrGBwgASgLMi8uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRh'
'dGEuSVBBRm9yY2VDaGVja0gAUg1pcGFGb3JjZUNoZWNrEmwKFHJlbW92ZUFkZGl0aW9uYWxVc2'
'VyGBIgASgLMjYuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVtb3ZlQWRkaXRp'
'b25hbFVzZXJIAFIUcmVtb3ZlQWRkaXRpb25hbFVzZXISYwoRYWRkQWRkaXRpb25hbFVzZXIYHS'
'ABKAsyMy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5BZGRBZGRpdGlvbmFsVXNl'
'ckgAUhFhZGRBZGRpdGlvbmFsVXNlchJZCg9zZXRfbG9naW5fdG9rZW4YHiABKAsyLy5jbGllbn'
'RfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5TZXRMb2dpblRva2VuSABSDXNldExvZ2luVG9r'
'ZW4aagoLVGV4dE1lc3NhZ2USFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhIKBGJvZHkYAyABKA'
'xSBGJvZHkSIAoJcHVzaF9kYXRhGAQgASgMSABSCHB1c2hEYXRhiAEBQgwKCl9wdXNoX2RhdGEa'
'LwoRR2V0VXNlckJ5VXNlcm5hbWUSGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lGiwKDkNoYW'
'5nZVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEgASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVG'
'Y21Ub2tlbhIdCgpnb29nbGVfZmNtGAEgASgJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFw'
'oHdXNlcl9pZBgBIAEoA1IGdXNlcklkGjYKEVVwZGF0ZVBsYW5PcHRpb25zEiEKDGF1dG9fcmVu'
'ZXdhbBgBIAEoCFILYXV0b1JlbmV3YWwaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQW'
'Njb3VudHNJbnZpdGVzGhUKE0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25h'
'bFVzZXISFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCg'
'd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNl'
'cl9pZBgBIAEoA1IGdXNlcklkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZW'
'tleV9pZBgBIAEoA1IOc2lnbmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2ln'
'bmVkUHJla2V5EjYKF3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZX'
'lTaWduYXR1cmUaNQoMRG93bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3du'
'bG9hZFRva2VuGk4KClJlcG9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3'
'J0ZWRVc2VySWQSFgoGcmVhc29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJv'
'ZHVjdF9pZBgBIAEoCVIJcHJvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcm'
'lmaWNhdGlvbl9kYXRhGAMgASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2sa'
'DwoNRGVsZXRlQWNjb3VudBosChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUg'
'Z1c2VySWQaMAoNU2V0TG9naW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tl'
'bhoMCgpEZXByZWNhdGVkQhEKD0FwcGxpY2F0aW9uRGF0YQ==');
'JlY2F0ZWRIAFIMZGVwcmVjYXRlZDE3ElMKDWRlcHJlY2F0ZWRfMTkYEyABKAsyLC5jbGllbnRf'
'dG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5EZXByZWNhdGVkSABSDGRlcHJlY2F0ZWQxORJUCg'
'xkb3dubG9hZERvbmUYFCABKAsyLi5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5E'
'b3dubG9hZERvbmVIAFIMZG93bmxvYWREb25lEnUKF2dldFNpZ25lZFByZWtleUJ5VXNlcmlkGB'
'YgASgLMjkuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuR2V0U2lnbmVkUHJlS2V5'
'QnlVc2VySWRIAFIXZ2V0U2lnbmVkUHJla2V5QnlVc2VyaWQSZgoSdXBkYXRlU2lnbmVkUHJla2'
'V5GBcgASgLMjQuY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuVXBkYXRlU2lnbmVk'
'UHJlS2V5SABSEnVwZGF0ZVNpZ25lZFByZWtleRJXCg1kZWxldGVBY2NvdW50GBggASgLMi8uY2'
'xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuRGVsZXRlQWNjb3VudEgAUg1kZWxldGVB'
'Y2NvdW50Ek4KCnJlcG9ydFVzZXIYGSABKAsyLC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW'
'9uRGF0YS5SZXBvcnRVc2VySABSCnJlcG9ydFVzZXISWgoOY2hhbmdlVXNlcm5hbWUYGiABKAsy'
'MC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5DaGFuZ2VVc2VybmFtZUgAUg5jaG'
'FuZ2VVc2VybmFtZRJRCgtpcGFQdXJjaGFzZRgbIAEoCzItLmNsaWVudF90b19zZXJ2ZXIuQXBw'
'bGljYXRpb25EYXRhLklQQVB1cmNoYXNlSABSC2lwYVB1cmNoYXNlElcKDWlwYUZvcmNlQ2hlY2'
'sYHCABKAsyLy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5JUEFGb3JjZUNoZWNr'
'SABSDWlwYUZvcmNlQ2hlY2sSbAoUcmVtb3ZlQWRkaXRpb25hbFVzZXIYEiABKAsyNi5jbGllbn'
'RfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5SZW1vdmVBZGRpdGlvbmFsVXNlckgAUhRyZW1v'
'dmVBZGRpdGlvbmFsVXNlchJjChFhZGRBZGRpdGlvbmFsVXNlchgdIAEoCzIzLmNsaWVudF90b1'
'9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkFkZEFkZGl0aW9uYWxVc2VySABSEWFkZEFkZGl0aW9u'
'YWxVc2VyElkKD3NldF9sb2dpbl90b2tlbhgeIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG'
'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhpqCgtUZXh0TWVzc2Fn'
'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2'
'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy'
'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX'
'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds'
'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg'
'Z1c2VySWQaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQWNjb3VudHNJbnZpdGVzGhUK'
'E0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZB'
'gBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1'
'c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlck'
'lkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2ln'
'bmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ2'
'5lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUaNQoMRG93'
'bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuGk4KClJlcG'
'9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3J0ZWRVc2VySWQSFgoGcmVh'
'c29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJvZHVjdF9pZBgBIAEoCVIJcH'
'JvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcmlmaWNhdGlvbl9kYXRhGAMg'
'ASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2saDwoNRGVsZXRlQWNjb3VudB'
'osChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMAoNU2V0TG9n'
'aW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tlbhoMCgpEZXByZWNhdGVkQh'
'EKD0FwcGxpY2F0aW9uRGF0YQ==');
@$core.Deprecated('Use responseDescriptor instead')
const Response$json = {

View file

@ -15,6 +15,7 @@ import 'package:flutter/foundation.dart';
import 'package:libsignal_protocol_dart/src/ecc/ed25519.dart';
import 'package:mutex/mutex.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:twonly/core/bridge/wrapper/key_manager.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
@ -450,6 +451,21 @@ class ApiService {
await onAuthenticated();
} else {
unawaited(onAuthenticated());
try {
Log.info('Switching authentication to login token');
final loginToken = await FlutterKeyManager.getLoginToken();
final res = await _setLoginToken(loginToken);
if (res.isSuccess) {
Log.info('Switch was successfully.');
await UserService.update((u) => u.canUseLoginTokenForAuth = true);
await SecureStorage.instance.delete(
key: SecureStorageKeys.apiAuthToken,
);
}
} catch (e) {
Log.error(e);
}
}
return true;
}
@ -466,9 +482,51 @@ class ApiService {
return false;
}
Future<bool> tryAuthenticateWithLoginToken() async {
try {
final loginToken = await FlutterKeyManager.getLoginToken();
final authenticate = Handshake_AuthenticateWithLoginToken()
..userId = Int64(userService.currentUser.userId)
..appVersion = (await PackageInfo.fromPlatform()).version
..deviceId = Int64(userService.currentUser.deviceId)
..inBackground = AppState.isInBackgroundTask
..secretLoginToken = loginToken.toList();
final handshake = Handshake()..authenticateWithLoginToken = authenticate;
final req = createClientToServerFromHandshake(handshake);
final result = await sendRequestSync(req, authenticated: false);
if (result.isSuccess) {
Log.info('websocket is authenticated');
isAuthenticated = true;
if (AppState.isInBackgroundTask) {
await onAuthenticated();
} else {
unawaited(onAuthenticated());
}
return true;
}
if (result.isError) {
if (result.error != ErrorCode.AuthTokenNotValid &&
result.error != ErrorCode.ForegroundSessionConnected) {
Log.error(
'got error while authenticating to the server: ${result.error}',
);
return false;
}
}
} catch (e) {
Log.error(e);
}
return false;
}
Future<void> authenticate() async {
return lockAuthentication.protect(() async {
if (isAuthenticated) return;
if (await getSignalIdentity() == null) {
Log.error('Signal identity not found.');
return;
@ -476,6 +534,11 @@ class ApiService {
if (!userService.isUserCreated) return;
if (userService.currentUser.canUseLoginTokenForAuth) {
await tryAuthenticateWithLoginToken();
return;
}
if (await tryAuthenticateWithToken()) {
return;
}
@ -542,6 +605,8 @@ class ApiService {
final signedPreKey = (await signalStore.loadSignedPreKeys())[0];
final loginToken = await FlutterKeyManager.getLoginToken();
final register = Handshake_Register()
..username = username
..publicIdentityKey = (await signalStore.getIdentityKeyPair())
@ -552,6 +617,7 @@ class ApiService {
..signedPrekeySignature = signedPreKey.signature
..signedPrekeyId = Int64(signedPreKey.id)
..langCode = ui.PlatformDispatcher.instance.locale.languageCode
..loginToken = loginToken
..proofOfWork = Int64(proofOfWorkResult)
..isIos = Platform.isIOS;
@ -617,6 +683,13 @@ class ApiService {
return sendRequestSync(req, ensureRetransmission: true);
}
Future<Result> _setLoginToken(List<int> token) async {
final get = ApplicationData_SetLoginToken()..loginToken = token;
final appData = ApplicationData()..setLoginToken = get;
final req = createClientToServerFromApplicationData(appData);
return sendRequestSync(req);
}
Future<Response_UserData?> getUserData(String username) async {
final get = ApplicationData_GetUserByUsername()..username = username;
final appData = ApplicationData()..getUserByUsername = get;
@ -645,13 +718,6 @@ class ApiService {
return null;
}
Future<Result> updatePlanOptions(bool autoRenewal) async {
final get = ApplicationData_UpdatePlanOptions()..autoRenewal = autoRenewal;
final appData = ApplicationData()..updatePlanOptions = get;
final req = createClientToServerFromApplicationData(appData);
return sendRequestSync(req);
}
Future<Result> removeAdditionalUser(Int64 userId) async {
final get = ApplicationData_RemoveAdditionalUser()..userId = userId;
final appData = ApplicationData()..removeAdditionalUser = get;

View file

@ -1,6 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:background_downloader/background_downloader.dart';
import 'package:clock/clock.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
@ -12,7 +10,6 @@ import 'package:http/http.dart' as http;
import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
@ -21,12 +18,12 @@ import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/mediafiles/media_background.api.dart';
import 'package:twonly/src/services/api/messages.api.dart';
import 'package:twonly/src/services/api/utils.api.dart';
import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/exclusive_access.utils.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/secure_storage.dart';
import 'package:workmanager/workmanager.dart' hide TaskStatus;
final lockRetransmission = Mutex();
@ -620,22 +617,17 @@ Future<void> _uploadUploadRequest(MediaFileService media) async {
return null;
}
final apiAuthTokenRaw = await SecureStorage.instance.read(
key: SecureStorageKeys.apiAuthToken,
);
if (apiAuthTokenRaw == null) {
Log.error('api auth token not defined.');
return null;
}
final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw));
final apiUrl =
'http${apiService.apiSecure}://${apiService.apiHost}/api/upload';
// try {
Log.info('Starting upload from ${media.mediaFile.mediaId}');
final headers = await getAuthenticationHeader();
if (headers == null) {
Log.error('Auth headers are empty. Returning');
return;
}
final task = UploadTask.fromFile(
taskId: 'upload_${media.mediaFile.mediaId}',
displayName: media.mediaFile.type.name,
@ -643,9 +635,7 @@ Future<void> _uploadUploadRequest(MediaFileService media) async {
url: apiUrl,
priority: 0,
retries: 10,
headers: {
'x-twonly-auth-token': apiAuthToken,
},
headers: headers,
);
final connectivityResult = await Connectivity().checkConnectivity();

View file

@ -1,6 +1,10 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:fixnum/fixnum.dart';
import 'package:twonly/core/bridge/wrapper/key_manager.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
@ -14,6 +18,9 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dar
import 'package:twonly/src/services/api/messages.api.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/secure_storage.dart';
class Result<T, E> {
Result.error(this.error) : value = null;
@ -106,3 +113,36 @@ Future<bool> importSignalContactAndCreateRequest(
return true;
}
Future<Map<String, String>?> getAuthenticationHeader() async {
var headers = <String, String>{};
if (userService.currentUser.canUseLoginTokenForAuth) {
final loginToken = await FlutterKeyManager.getLoginToken();
headers = {
'x-twonly-user-id': userService.currentUser.userId
.toRadixString(16)
.padLeft(16, '0')
.toUpperCase(),
'x-twonly-login-token': uint8ListToHex(loginToken),
};
} else {
final apiAuthTokenRaw = await SecureStorage.instance.read(
key: SecureStorageKeys.apiAuthToken,
);
if (apiAuthTokenRaw == null) {
Log.error('api auth token not defined.');
return null;
}
final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw));
headers = {
'x-twonly-auth-token': apiAuthToken,
};
}
return headers;
}

View file

@ -34,6 +34,7 @@ Future<void> initializeBackgroundTaskManager() async {
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
SentryWidgetsFlutterBinding.ensureInitialized();
await AppEnvironment.init();
switch (task) {
case 'eu.twonly.periodic_task':
// if (await initBackgroundExecution()) {
@ -58,7 +59,6 @@ Future<bool> initBackgroundExecution() async {
return false;
}
await AppEnvironment.init();
AppState.isInBackgroundTask = true;
if (await StartupGuard.isAppStarting()) {

View file

@ -5,7 +5,7 @@ import 'dart:math';
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/localization/generated/app_localizations.dart';
@ -266,8 +266,7 @@ Future<void> showLocalPushNotificationWithoutUserId(
}
Future<String?> getAvatarIcon(int contactId) async {
final directory = await getApplicationCacheDirectory();
final avatarsDirectory = Directory('${directory.path}/avatars');
final avatarsDirectory = Directory('${AppEnvironment.cacheDir}/avatars');
final filePath = '${avatarsDirectory.path}/$contactId.png';
final file = File(filePath);
if (file.existsSync()) {

View file

@ -119,6 +119,7 @@ Future<void> initFCMService() async {
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
SentryWidgetsFlutterBinding.ensureInitialized();
await AppEnvironment.init();
final isInitialized = await initBackgroundExecution();
Log.info('Handling a background message: ${message.messageId}');
await handleRemoteMessage(message);

View file

@ -5,7 +5,7 @@ import 'dart:io';
import 'package:clock/clock.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/utils/log.dart';
class Sticker {
@ -21,8 +21,7 @@ class Sticker {
}
Future<List<Sticker>> getStickerIndex() async {
final directory = await getApplicationCacheDirectory();
final indexFile = File('${directory.path}/stickers.json');
final indexFile = File('${AppEnvironment.cacheDir}/stickers.json');
var res = <Sticker>[];
if (indexFile.existsSync() && kReleaseMode) {

View file

@ -7,8 +7,8 @@ import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
@ -131,8 +131,7 @@ class _MessageInputState extends State<MessageInput> {
_currentDuration = 0;
});
await HapticFeedback.heavyImpact();
final audioTmpPath =
'${(await getApplicationCacheDirectory()).path}/recording.m4a';
final audioTmpPath = '${AppEnvironment.cacheDir}/recording.m4a';
unawaited(
recorderController.record(
path: audioTmpPath,

View file

@ -1,5 +1,3 @@
import 'dart:convert';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart';
@ -11,6 +9,7 @@ import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart';
import 'package:twonly/src/services/api/utils.api.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/secure_storage.dart';
@ -57,16 +56,18 @@ class _ContactUsState extends State<ContactUsView> {
Log.error('api auth token not defined.');
return null;
}
final apiAuthToken = uint8ListToHex(base64Decode(apiAuthTokenRaw));
final apiUrl =
'http${apiService.apiSecure}://${apiService.apiHost}/api/upload';
final requestMultipart = http.MultipartRequest(
'POST',
Uri.parse(apiUrl),
);
requestMultipart.headers['x-twonly-auth-token'] = apiAuthToken;
final requestMultipart = http.MultipartRequest('POST', Uri.parse(apiUrl));
final headers = await getAuthenticationHeader();
if (headers == null) {
Log.error('Auth headers are empty. Returning');
return null;
}
requestMultipart.headers.addAll(headers);
requestMultipart.files.add(
http.MultipartFile.fromBytes(

View file

@ -1,8 +1,10 @@
use crate::error::{Result, TwonlyError};
use crate::keys::KeyManager;
use crate::secure_storage::{self, SecureStorage};
use aes_gcm::aead::rand_core::RngCore;
use aes_gcm::aead::{Aead, KeyInit, OsRng};
use aes_gcm::{Aes256Gcm, Nonce};
use mdk_core::key_packages;
use scrypt::{scrypt, Params};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
@ -21,11 +23,16 @@ impl BackupPasswordKeys {
}
}
pub(crate) fn from_password(password: &str, salt: &str) -> Result<Self> {
pub(crate) fn from_password(password: &str, username: &str) -> Result<Self> {
let params = Params::new(17, 8, 1)?;
let mut output = [0u8; 64];
scrypt(password.as_bytes(), salt.as_bytes(), &params, &mut output)?;
scrypt(
password.as_bytes(),
username.as_bytes(),
&params,
&mut output,
)?;
let mut backup_id = [0u8; 32];
let mut encryption_key = [0u8; 32];
@ -34,21 +41,13 @@ impl BackupPasswordKeys {
Ok(Self::new(backup_id, encryption_key))
}
}
#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub(crate) struct BackupPlainTextContent {
pub(crate) user_id: i64,
pub(crate) key_manager: KeyManager,
}
impl BackupPlainTextContent {
fn get_encrypted_backup(&self) -> Result<Vec<u8>> {
let Some(keys) = &self.key_manager.backup_password else {
fn encrypt_key_manager(key_manager: KeyManager) -> Result<Vec<u8>> {
let Some(keys) = &key_manager.backup_password else {
return Err(TwonlyError::Generic("No backup password".into()));
};
let serialized_bytes = postcard::to_allocvec(&self)?;
let serialized_bytes = postcard::to_allocvec(&key_manager)?;
let key = aes_gcm::Key::<Aes256Gcm>::from_slice(&keys.encryption_key);
let cipher = Aes256Gcm::new(key);
@ -66,10 +65,11 @@ impl BackupPlainTextContent {
Ok(encrypted_bytes)
}
pub(crate) fn from_encrypted_backup(
pub(crate) fn restore_key_manager(
secure_storage: SecureStorage,
encrypted_bytes: &[u8],
keys: &BackupPasswordKeys,
) -> Result<Self> {
) -> Result<()> {
if encrypted_bytes.len() < 12 {
return Err(TwonlyError::Generic(
"Invalid encrypted backup length".into(),
@ -84,10 +84,22 @@ impl BackupPlainTextContent {
let decrypted_bytes = cipher.decrypt(nonce, ciphertext)?;
Ok(postcard::from_bytes(&decrypted_bytes)?)
let key_manager: KeyManager = postcard::from_bytes(&decrypted_bytes)?;
key_manager.store_to_keychain(&secure_storage)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub(crate) struct BackupPlainTextContent {
pub(crate) user_id: i64,
pub(crate) key_manager: KeyManager,
}
impl BackupPlainTextContent {}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -0,0 +1,12 @@
use crate::error::Result;
use crate::{bridge::get_twonly_flutter, keys::KeyManager};
pub struct FlutterKeyManager {}
impl FlutterKeyManager {
pub async fn get_login_token() -> Result<Vec<u8>> {
let ctx = get_twonly_flutter()?;
let key_manager = KeyManager::try_from_keychain(&ctx.secure_storage)?;
Ok(key_manager.main_key.get_login_token().to_vec())
}
}

View file

@ -1 +1,2 @@
pub mod key_manager;
pub mod user_discovery;

View file

@ -99,7 +99,7 @@ impl Context {
Err(err) => {
tracing::error!("{err}");
if rust_db_path.exists() {
tracing::error!("Rust Database exsist, while the key manager not. This must be a secure storage error.");
tracing::error!("Rust Database exists, while the key manager not. This must be a secure storage error.");
return Err(TwonlyError::SecureStorageError);
}
tracing::info!("Generating a new key manager.");

View file

@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueMoi,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1680338106;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1007286393;
// Section: executor
@ -46,6 +46,21 @@ flutter_rust_bridge::frb_generated_default_handler!();
// Section: wire_funcs
fn wire__crate__bridge__wrapper__key_manager__flutter_key_manager_get_login_token_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec,_,_,_>(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "flutter_key_manager_get_login_token", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || {
let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) };
let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message);
deserializer.end(); move |context| async move {
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
let output_ok = crate::bridge::wrapper::key_manager::FlutterKeyManager::get_login_token().await?; Ok(output_ok)
})().await)
} })
}
fn wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@ -729,6 +744,13 @@ impl SseDecode for bool {
}
}
impl SseDecode for crate::bridge::wrapper::key_manager::FlutterKeyManager {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
return crate::bridge::wrapper::key_manager::FlutterKeyManager {};
}
}
impl SseDecode for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@ -917,14 +939,15 @@ fn pde_ffi_dispatcher_primary_impl(
) {
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
1 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len),
2 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_new_messages_impl(port, ptr, rust_vec_len, data_len),
3 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_handle_new_messages_impl(port, ptr, rust_vec_len, data_len),
4 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_initialize_or_update_impl(port, ptr, rust_vec_len, data_len),
5 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_should_request_new_messages_impl(port, ptr, rust_vec_len, data_len),
6 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len),
7 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len),
8 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len),
1 => wire__crate__bridge__wrapper__key_manager__flutter_key_manager_get_login_token_impl(port, ptr, rust_vec_len, data_len),
2 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len),
3 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_new_messages_impl(port, ptr, rust_vec_len, data_len),
4 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_handle_new_messages_impl(port, ptr, rust_vec_len, data_len),
5 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_initialize_or_update_impl(port, ptr, rust_vec_len, data_len),
6 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_should_request_new_messages_impl(port, ptr, rust_vec_len, data_len),
7 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len),
8 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len),
9 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@ -966,6 +989,23 @@ impl flutter_rust_bridge::IntoIntoDart<FrbWrapper<crate::bridge::AnnouncedUser>>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::bridge::wrapper::key_manager::FlutterKeyManager {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
Vec::<u8>::new().into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
for crate::bridge::wrapper::key_manager::FlutterKeyManager
{
}
impl flutter_rust_bridge::IntoIntoDart<crate::bridge::wrapper::key_manager::FlutterKeyManager>
for crate::bridge::wrapper::key_manager::FlutterKeyManager
{
fn into_into_dart(self) -> crate::bridge::wrapper::key_manager::FlutterKeyManager {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart
for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery
{
@ -1073,6 +1113,11 @@ impl SseEncode for bool {
}
}
impl SseEncode for crate::bridge::wrapper::key_manager::FlutterKeyManager {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
}
impl SseEncode for crate::bridge::wrapper::user_discovery::FlutterUserDiscovery {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}

View file

@ -40,12 +40,12 @@ impl MainKey {
/// Download token required to download a backup.
/// This ensures that the user who tries to download the backup must have knowledge over the
/// main key
pub fn backup_download_token(&self) -> [u8; 32] {
pub fn get_backup_download_token(&self) -> [u8; 32] {
self.derive_key(b"backup_download_token")
}
/// Uses as a password to authenitcate agains the server
pub fn server_auth_token(&self) -> [u8; 32] {
pub fn get_login_token(&self) -> [u8; 32] {
self.derive_key(b"server_auth_token")
}

View file

@ -7,6 +7,7 @@ use crate::error::TwonlyError;
pub(crate) use crate::keys::identity_key::IdentityKey;
pub(crate) use crate::keys::main_key::{DatabaseKey, MainKey};
use crate::secure_storage::SecureStorage;
use aes_gcm::Aes256Gcm;
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
@ -14,6 +15,7 @@ const KEY_MANAGER_ID: &str = "twonly_key_manager";
#[derive(Debug, PartialEq, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub(crate) struct KeyManager {
pub(crate) user_id: Option<i64>,
pub(crate) main_key: MainKey,
pub(crate) identity_keys: Vec<IdentityKey>,
pub(crate) backup_password: Option<BackupPasswordKeys>,
@ -25,6 +27,7 @@ impl KeyManager {
main_key: MainKey::generate(),
identity_keys: vec![],
backup_password: None,
user_id: None,
})
}