mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-07-17 22:14:08 +00:00
enable and handle heartbeath
This commit is contained in:
parent
6db45a6da8
commit
4d943eca1c
38 changed files with 19135 additions and 261 deletions
|
|
@ -35,7 +35,6 @@ If you decide to give twonly a try, please keep in mind that it is still in its
|
|||
- End-to-End encryption using the [Signal Protocol](https://de.wikipedia.org/wiki/Signal-Protokoll)
|
||||
- Open Source and can be downloaded directly from GitHub
|
||||
- No email or phone number required to register
|
||||
- Privacy friendly - Everything is stored on the device
|
||||
- The backend is hosted exclusively in Europe
|
||||
|
||||
## Roadmap
|
||||
|
|
@ -43,7 +42,6 @@ If you decide to give twonly a try, please keep in mind that it is still in its
|
|||
### Currently
|
||||
|
||||
- Focus on user-friendliness so that people enjoy using the app
|
||||
- User discovery without a phone number
|
||||
- Passwordless recovery without a phone number
|
||||
- Implementation of features so that Snapchat can actually be replaced
|
||||
- E2EE cloud backup of memories
|
||||
|
|
|
|||
173
lib/core/bridge/callbacks/user_discovery.dart
Normal file
173
lib/core/bridge/callbacks/user_discovery.dart
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// 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 '../../bridge.dart';
|
||||
import '../../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`
|
||||
|
||||
class UserDiscoveryStoreFlutter {
|
||||
const UserDiscoveryStoreFlutter();
|
||||
|
||||
Future<AnnouncedUser?> getAnnouncedUserByPublicId({
|
||||
required PlatformInt64 publicId,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetAnnouncedUserByPublicId(
|
||||
that: this,
|
||||
publicId: publicId,
|
||||
);
|
||||
|
||||
Future<String> getConfig() => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetConfig(
|
||||
that: this,
|
||||
);
|
||||
|
||||
Future<Uint8List?> getContactPromotion({
|
||||
required PlatformInt64 contactId,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactPromotion(
|
||||
that: this,
|
||||
contactId: contactId,
|
||||
);
|
||||
|
||||
Future<Uint8List?> getContactVersion({
|
||||
required PlatformInt64 contactId,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetContactVersion(
|
||||
that: this,
|
||||
contactId: contactId,
|
||||
);
|
||||
|
||||
Future<List<OtherPromotion>> getOtherPromotionsByPublicId({
|
||||
required PlatformInt64 publicId,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOtherPromotionsByPublicId(
|
||||
that: this,
|
||||
publicId: publicId,
|
||||
);
|
||||
|
||||
Future<List<Uint8List>> getOwnPromotionsAfterVersion({
|
||||
required int version,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetOwnPromotionsAfterVersion(
|
||||
that: this,
|
||||
version: version,
|
||||
);
|
||||
|
||||
Future<Uint8List> getShareForContact({
|
||||
required PlatformInt64 contactId,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterGetShareForContact(
|
||||
that: this,
|
||||
contactId: contactId,
|
||||
);
|
||||
|
||||
Future<void> pushNewUserRelation({
|
||||
required PlatformInt64 fromContactId,
|
||||
required AnnouncedUser announcedUser,
|
||||
PlatformInt64? publicKeyVerifiedTimestamp,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushNewUserRelation(
|
||||
that: this,
|
||||
fromContactId: fromContactId,
|
||||
announcedUser: announcedUser,
|
||||
publicKeyVerifiedTimestamp: publicKeyVerifiedTimestamp,
|
||||
);
|
||||
|
||||
Future<void> pushOwnPromotionAndClearOldVersion({
|
||||
required PlatformInt64 contactId,
|
||||
required int version,
|
||||
required List<int> promotion,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterPushOwnPromotionAndClearOldVersion(
|
||||
that: this,
|
||||
contactId: contactId,
|
||||
version: version,
|
||||
promotion: promotion,
|
||||
);
|
||||
|
||||
Future<void> setContactVersion({
|
||||
required PlatformInt64 contactId,
|
||||
required List<int> update,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetContactVersion(
|
||||
that: this,
|
||||
contactId: contactId,
|
||||
update: update,
|
||||
);
|
||||
|
||||
Future<void> setShares({required List<Uint8List> shares}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterSetShares(
|
||||
that: this,
|
||||
shares: shares,
|
||||
);
|
||||
|
||||
Future<void> storeOtherPromotion({
|
||||
required OtherPromotion promotion,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterStoreOtherPromotion(
|
||||
that: this,
|
||||
promotion: promotion,
|
||||
);
|
||||
|
||||
Future<void> updateConfig({required String update}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryStoreFlutterUpdateConfig(
|
||||
that: this,
|
||||
update: update,
|
||||
);
|
||||
|
||||
@override
|
||||
int get hashCode => 0;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is UserDiscoveryStoreFlutter && runtimeType == other.runtimeType;
|
||||
}
|
||||
|
||||
class UserDiscoveryUtilsFlutter {
|
||||
const UserDiscoveryUtilsFlutter();
|
||||
|
||||
Future<Uint8List> signData({required List<int> inputData}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterSignData(
|
||||
that: this,
|
||||
inputData: inputData,
|
||||
);
|
||||
|
||||
Future<bool> verifySignature({
|
||||
required List<int> inputData,
|
||||
required List<int> pubkey,
|
||||
required List<int> signature,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifySignature(
|
||||
that: this,
|
||||
inputData: inputData,
|
||||
pubkey: pubkey,
|
||||
signature: signature,
|
||||
);
|
||||
|
||||
Future<bool> verifyStoredPubkey({
|
||||
required PlatformInt64 fromContactId,
|
||||
required List<int> pubkey,
|
||||
}) => RustLib.instance.api
|
||||
.crateBridgeCallbacksUserDiscoveryUserDiscoveryUtilsFlutterVerifyStoredPubkey(
|
||||
that: this,
|
||||
fromContactId: fromContactId,
|
||||
pubkey: pubkey,
|
||||
);
|
||||
|
||||
@override
|
||||
int get hashCode => 0;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is UserDiscoveryUtilsFlutter && runtimeType == other.runtimeType;
|
||||
}
|
||||
37
lib/core/bridge/wrapper.dart
Normal file
37
lib/core/bridge/wrapper.dart
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// 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 RustUtils {
|
||||
const RustUtils();
|
||||
|
||||
static Future<List<Uint8List>> generateShares({
|
||||
required List<int> secret,
|
||||
required int total,
|
||||
required int threshold,
|
||||
}) => RustLib.instance.api.crateBridgeWrapperRustUtilsGenerateShares(
|
||||
secret: secret,
|
||||
total: total,
|
||||
threshold: threshold,
|
||||
);
|
||||
|
||||
static Future<Uint8List> recoverSecret({
|
||||
required List<Uint8List> shares,
|
||||
required int threshold,
|
||||
}) => RustLib.instance.api.crateBridgeWrapperRustUtilsRecoverSecret(
|
||||
shares: shares,
|
||||
threshold: threshold,
|
||||
);
|
||||
|
||||
@override
|
||||
int get hashCode => 0;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is RustUtils && runtimeType == other.runtimeType;
|
||||
}
|
||||
|
|
@ -53,6 +53,10 @@ class RustKeyManager {
|
|||
signedPreKeyId: signedPreKeyId,
|
||||
);
|
||||
|
||||
/// Serialize the key_manager. Needed for the passwordless_recovery feature.
|
||||
static Future<Uint8List> serialize() => RustLib.instance.api
|
||||
.crateBridgeWrapperKeyManagerRustKeyManagerSerialize();
|
||||
|
||||
static Future<void> setUserId({required PlatformInt64 userId}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
import 'bridge.dart';
|
||||
import 'bridge/callbacks.dart';
|
||||
import 'bridge/callbacks/user_discovery.dart';
|
||||
import 'bridge/wrapper.dart';
|
||||
import 'bridge/wrapper/backup.dart';
|
||||
import 'bridge/wrapper/key_manager.dart';
|
||||
import 'bridge/wrapper/user_discovery.dart';
|
||||
|
|
@ -134,6 +136,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
InitConfig dco_decode_box_autoadd_init_config(dynamic raw);
|
||||
|
||||
@protected
|
||||
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter dco_decode_box_autoadd_user_discovery_utils_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
||||
|
||||
|
|
@ -205,6 +220,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustUtils dco_decode_rust_utils(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_32(dynamic raw);
|
||||
|
||||
|
|
@ -217,6 +235,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void dco_decode_unit(dynamic raw);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter dco_decode_user_discovery_store_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter dco_decode_user_discovery_utils_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw);
|
||||
|
||||
|
|
@ -266,6 +294,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
OtherPromotion sse_decode_box_autoadd_other_promotion(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter sse_decode_box_autoadd_user_discovery_utils_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
FlutterUserDiscovery sse_decode_flutter_user_discovery(
|
||||
SseDeserializer deserializer,
|
||||
|
|
@ -357,6 +400,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_32(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -369,6 +415,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter sse_decode_user_discovery_store_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter sse_decode_user_discovery_utils_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -517,6 +573,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_other_promotion(
|
||||
OtherPromotion self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
||||
UserDiscoveryStoreFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_user_discovery_utils_flutter(
|
||||
UserDiscoveryUtilsFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_flutter_user_discovery(
|
||||
FlutterUserDiscovery self,
|
||||
|
|
@ -634,6 +708,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||
|
||||
|
|
@ -646,6 +723,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_user_discovery_store_flutter(
|
||||
UserDiscoveryStoreFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_user_discovery_utils_flutter(
|
||||
UserDiscoveryUtilsFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
import 'bridge.dart';
|
||||
import 'bridge/callbacks.dart';
|
||||
import 'bridge/callbacks/user_discovery.dart';
|
||||
import 'bridge/wrapper.dart';
|
||||
import 'bridge/wrapper/backup.dart';
|
||||
import 'bridge/wrapper/key_manager.dart';
|
||||
import 'bridge/wrapper/user_discovery.dart';
|
||||
|
|
@ -136,6 +138,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
InitConfig dco_decode_box_autoadd_init_config(dynamic raw);
|
||||
|
||||
@protected
|
||||
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter dco_decode_box_autoadd_user_discovery_utils_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
||||
|
||||
|
|
@ -207,6 +222,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
||||
|
||||
@protected
|
||||
RustUtils dco_decode_rust_utils(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_32(dynamic raw);
|
||||
|
||||
|
|
@ -219,6 +237,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void dco_decode_unit(dynamic raw);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter dco_decode_user_discovery_store_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter dco_decode_user_discovery_utils_flutter(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw);
|
||||
|
||||
|
|
@ -268,6 +296,21 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
InitConfig sse_decode_box_autoadd_init_config(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
OtherPromotion sse_decode_box_autoadd_other_promotion(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter sse_decode_box_autoadd_user_discovery_utils_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
FlutterUserDiscovery sse_decode_flutter_user_discovery(
|
||||
SseDeserializer deserializer,
|
||||
|
|
@ -359,6 +402,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_32(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -371,6 +417,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
UserDiscoveryStoreFlutter sse_decode_user_discovery_store_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
UserDiscoveryUtilsFlutter sse_decode_user_discovery_utils_flutter(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -519,6 +575,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_other_promotion(
|
||||
OtherPromotion self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
||||
UserDiscoveryStoreFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_box_autoadd_user_discovery_utils_flutter(
|
||||
UserDiscoveryUtilsFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_flutter_user_discovery(
|
||||
FlutterUserDiscovery self,
|
||||
|
|
@ -636,6 +710,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||
|
||||
|
|
@ -648,6 +725,18 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_user_discovery_store_flutter(
|
||||
UserDiscoveryStoreFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_user_discovery_utils_flutter(
|
||||
UserDiscoveryUtilsFlutter self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer);
|
||||
|
||||
|
|
|
|||
3127
lib/src/database/schemas/twonly_db/drift_schema_v21.json
Normal file
3127
lib/src/database/schemas/twonly_db/drift_schema_v21.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,10 @@ class Contacts extends Table {
|
|||
DateTimeColumn get recoveryLastHeartbeat => dateTime().nullable()();
|
||||
BlobColumn get recoverySecretShare => blob().nullable()();
|
||||
|
||||
// This is the share from the contact in case the contact has selected this user as his trusted friend.
|
||||
BlobColumn get recoveryContactsSecretShare => blob().nullable()();
|
||||
DateTimeColumn get recoveryContactsLastHeartbeat => dateTime().nullable()();
|
||||
|
||||
BoolColumn get askForFriendPromotions => boolean().nullable()();
|
||||
|
||||
IntColumn get mediaSendCounter => integer().withDefault(const Constant(0))();
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 20;
|
||||
int get schemaVersion => 21;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
final connection = driftDatabase(
|
||||
|
|
@ -259,6 +259,16 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
schema.contacts.recoverySecretShare,
|
||||
);
|
||||
},
|
||||
from20To21: (m, schema) async {
|
||||
await m.addColumn(
|
||||
schema.contacts,
|
||||
schema.contacts.recoveryContactsSecretShare,
|
||||
);
|
||||
await m.addColumn(
|
||||
schema.contacts,
|
||||
schema.contacts.recoveryContactsLastHeartbeat,
|
||||
);
|
||||
},
|
||||
)(m, from, to);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -252,6 +252,28 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
|||
type: DriftSqlType.blob,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _recoveryContactsSecretShareMeta =
|
||||
const VerificationMeta('recoveryContactsSecretShare');
|
||||
@override
|
||||
late final GeneratedColumn<Uint8List> recoveryContactsSecretShare =
|
||||
GeneratedColumn<Uint8List>(
|
||||
'recovery_contacts_secret_share',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.blob,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _recoveryContactsLastHeartbeatMeta =
|
||||
const VerificationMeta('recoveryContactsLastHeartbeat');
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> recoveryContactsLastHeartbeat =
|
||||
GeneratedColumn<DateTime>(
|
||||
'recovery_contacts_last_heartbeat',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _askForFriendPromotionsMeta =
|
||||
const VerificationMeta('askForFriendPromotions');
|
||||
@override
|
||||
|
|
@ -310,6 +332,8 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
|||
recoveryIsTrustedFriend,
|
||||
recoveryLastHeartbeat,
|
||||
recoverySecretShare,
|
||||
recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions,
|
||||
mediaSendCounter,
|
||||
mediaReceivedCounter,
|
||||
|
|
@ -475,6 +499,24 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
|||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('recovery_contacts_secret_share')) {
|
||||
context.handle(
|
||||
_recoveryContactsSecretShareMeta,
|
||||
recoveryContactsSecretShare.isAcceptableOrUnknown(
|
||||
data['recovery_contacts_secret_share']!,
|
||||
_recoveryContactsSecretShareMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('recovery_contacts_last_heartbeat')) {
|
||||
context.handle(
|
||||
_recoveryContactsLastHeartbeatMeta,
|
||||
recoveryContactsLastHeartbeat.isAcceptableOrUnknown(
|
||||
data['recovery_contacts_last_heartbeat']!,
|
||||
_recoveryContactsLastHeartbeatMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('ask_for_friend_promotions')) {
|
||||
context.handle(
|
||||
_askForFriendPromotionsMeta,
|
||||
|
|
@ -587,6 +629,14 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
|||
DriftSqlType.blob,
|
||||
data['${effectivePrefix}recovery_secret_share'],
|
||||
),
|
||||
recoveryContactsSecretShare: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.blob,
|
||||
data['${effectivePrefix}recovery_contacts_secret_share'],
|
||||
),
|
||||
recoveryContactsLastHeartbeat: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}recovery_contacts_last_heartbeat'],
|
||||
),
|
||||
askForFriendPromotions: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}ask_for_friend_promotions'],
|
||||
|
|
@ -628,6 +678,8 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
final bool recoveryIsTrustedFriend;
|
||||
final DateTime? recoveryLastHeartbeat;
|
||||
final Uint8List? recoverySecretShare;
|
||||
final Uint8List? recoveryContactsSecretShare;
|
||||
final DateTime? recoveryContactsLastHeartbeat;
|
||||
final bool? askForFriendPromotions;
|
||||
final int mediaSendCounter;
|
||||
final int mediaReceivedCounter;
|
||||
|
|
@ -651,6 +703,8 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
required this.recoveryIsTrustedFriend,
|
||||
this.recoveryLastHeartbeat,
|
||||
this.recoverySecretShare,
|
||||
this.recoveryContactsSecretShare,
|
||||
this.recoveryContactsLastHeartbeat,
|
||||
this.askForFriendPromotions,
|
||||
required this.mediaSendCounter,
|
||||
required this.mediaReceivedCounter,
|
||||
|
|
@ -695,6 +749,16 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
if (!nullToAbsent || recoverySecretShare != null) {
|
||||
map['recovery_secret_share'] = Variable<Uint8List>(recoverySecretShare);
|
||||
}
|
||||
if (!nullToAbsent || recoveryContactsSecretShare != null) {
|
||||
map['recovery_contacts_secret_share'] = Variable<Uint8List>(
|
||||
recoveryContactsSecretShare,
|
||||
);
|
||||
}
|
||||
if (!nullToAbsent || recoveryContactsLastHeartbeat != null) {
|
||||
map['recovery_contacts_last_heartbeat'] = Variable<DateTime>(
|
||||
recoveryContactsLastHeartbeat,
|
||||
);
|
||||
}
|
||||
if (!nullToAbsent || askForFriendPromotions != null) {
|
||||
map['ask_for_friend_promotions'] = Variable<bool>(askForFriendPromotions);
|
||||
}
|
||||
|
|
@ -739,6 +803,14 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoverySecretShare: recoverySecretShare == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(recoverySecretShare),
|
||||
recoveryContactsSecretShare:
|
||||
recoveryContactsSecretShare == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(recoveryContactsSecretShare),
|
||||
recoveryContactsLastHeartbeat:
|
||||
recoveryContactsLastHeartbeat == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(recoveryContactsLastHeartbeat),
|
||||
askForFriendPromotions: askForFriendPromotions == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(askForFriendPromotions),
|
||||
|
|
@ -788,6 +860,12 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoverySecretShare: serializer.fromJson<Uint8List?>(
|
||||
json['recoverySecretShare'],
|
||||
),
|
||||
recoveryContactsSecretShare: serializer.fromJson<Uint8List?>(
|
||||
json['recoveryContactsSecretShare'],
|
||||
),
|
||||
recoveryContactsLastHeartbeat: serializer.fromJson<DateTime?>(
|
||||
json['recoveryContactsLastHeartbeat'],
|
||||
),
|
||||
askForFriendPromotions: serializer.fromJson<bool?>(
|
||||
json['askForFriendPromotions'],
|
||||
),
|
||||
|
|
@ -828,6 +906,12 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoveryLastHeartbeat,
|
||||
),
|
||||
'recoverySecretShare': serializer.toJson<Uint8List?>(recoverySecretShare),
|
||||
'recoveryContactsSecretShare': serializer.toJson<Uint8List?>(
|
||||
recoveryContactsSecretShare,
|
||||
),
|
||||
'recoveryContactsLastHeartbeat': serializer.toJson<DateTime?>(
|
||||
recoveryContactsLastHeartbeat,
|
||||
),
|
||||
'askForFriendPromotions': serializer.toJson<bool?>(
|
||||
askForFriendPromotions,
|
||||
),
|
||||
|
|
@ -856,6 +940,8 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
bool? recoveryIsTrustedFriend,
|
||||
Value<DateTime?> recoveryLastHeartbeat = const Value.absent(),
|
||||
Value<Uint8List?> recoverySecretShare = const Value.absent(),
|
||||
Value<Uint8List?> recoveryContactsSecretShare = const Value.absent(),
|
||||
Value<DateTime?> recoveryContactsLastHeartbeat = const Value.absent(),
|
||||
Value<bool?> askForFriendPromotions = const Value.absent(),
|
||||
int? mediaSendCounter,
|
||||
int? mediaReceivedCounter,
|
||||
|
|
@ -890,6 +976,12 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoverySecretShare: recoverySecretShare.present
|
||||
? recoverySecretShare.value
|
||||
: this.recoverySecretShare,
|
||||
recoveryContactsSecretShare: recoveryContactsSecretShare.present
|
||||
? recoveryContactsSecretShare.value
|
||||
: this.recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat.present
|
||||
? recoveryContactsLastHeartbeat.value
|
||||
: this.recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions: askForFriendPromotions.present
|
||||
? askForFriendPromotions.value
|
||||
: this.askForFriendPromotions,
|
||||
|
|
@ -939,6 +1031,12 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoverySecretShare: data.recoverySecretShare.present
|
||||
? data.recoverySecretShare.value
|
||||
: this.recoverySecretShare,
|
||||
recoveryContactsSecretShare: data.recoveryContactsSecretShare.present
|
||||
? data.recoveryContactsSecretShare.value
|
||||
: this.recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat: data.recoveryContactsLastHeartbeat.present
|
||||
? data.recoveryContactsLastHeartbeat.value
|
||||
: this.recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions: data.askForFriendPromotions.present
|
||||
? data.askForFriendPromotions.value
|
||||
: this.askForFriendPromotions,
|
||||
|
|
@ -973,6 +1071,10 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ')
|
||||
..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ')
|
||||
..write('recoverySecretShare: $recoverySecretShare, ')
|
||||
..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ')
|
||||
..write(
|
||||
'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ',
|
||||
)
|
||||
..write('askForFriendPromotions: $askForFriendPromotions, ')
|
||||
..write('mediaSendCounter: $mediaSendCounter, ')
|
||||
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
||||
|
|
@ -1001,6 +1103,8 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
recoveryIsTrustedFriend,
|
||||
recoveryLastHeartbeat,
|
||||
$driftBlobEquality.hash(recoverySecretShare),
|
||||
$driftBlobEquality.hash(recoveryContactsSecretShare),
|
||||
recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions,
|
||||
mediaSendCounter,
|
||||
mediaReceivedCounter,
|
||||
|
|
@ -1038,6 +1142,12 @@ class Contact extends DataClass implements Insertable<Contact> {
|
|||
other.recoverySecretShare,
|
||||
this.recoverySecretShare,
|
||||
) &&
|
||||
$driftBlobEquality.equals(
|
||||
other.recoveryContactsSecretShare,
|
||||
this.recoveryContactsSecretShare,
|
||||
) &&
|
||||
other.recoveryContactsLastHeartbeat ==
|
||||
this.recoveryContactsLastHeartbeat &&
|
||||
other.askForFriendPromotions == this.askForFriendPromotions &&
|
||||
other.mediaSendCounter == this.mediaSendCounter &&
|
||||
other.mediaReceivedCounter == this.mediaReceivedCounter);
|
||||
|
|
@ -1063,6 +1173,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
final Value<bool> recoveryIsTrustedFriend;
|
||||
final Value<DateTime?> recoveryLastHeartbeat;
|
||||
final Value<Uint8List?> recoverySecretShare;
|
||||
final Value<Uint8List?> recoveryContactsSecretShare;
|
||||
final Value<DateTime?> recoveryContactsLastHeartbeat;
|
||||
final Value<bool?> askForFriendPromotions;
|
||||
final Value<int> mediaSendCounter;
|
||||
final Value<int> mediaReceivedCounter;
|
||||
|
|
@ -1086,6 +1198,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
this.recoveryIsTrustedFriend = const Value.absent(),
|
||||
this.recoveryLastHeartbeat = const Value.absent(),
|
||||
this.recoverySecretShare = const Value.absent(),
|
||||
this.recoveryContactsSecretShare = const Value.absent(),
|
||||
this.recoveryContactsLastHeartbeat = const Value.absent(),
|
||||
this.askForFriendPromotions = const Value.absent(),
|
||||
this.mediaSendCounter = const Value.absent(),
|
||||
this.mediaReceivedCounter = const Value.absent(),
|
||||
|
|
@ -1110,6 +1224,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
this.recoveryIsTrustedFriend = const Value.absent(),
|
||||
this.recoveryLastHeartbeat = const Value.absent(),
|
||||
this.recoverySecretShare = const Value.absent(),
|
||||
this.recoveryContactsSecretShare = const Value.absent(),
|
||||
this.recoveryContactsLastHeartbeat = const Value.absent(),
|
||||
this.askForFriendPromotions = const Value.absent(),
|
||||
this.mediaSendCounter = const Value.absent(),
|
||||
this.mediaReceivedCounter = const Value.absent(),
|
||||
|
|
@ -1134,6 +1250,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
Expression<bool>? recoveryIsTrustedFriend,
|
||||
Expression<DateTime>? recoveryLastHeartbeat,
|
||||
Expression<Uint8List>? recoverySecretShare,
|
||||
Expression<Uint8List>? recoveryContactsSecretShare,
|
||||
Expression<DateTime>? recoveryContactsLastHeartbeat,
|
||||
Expression<bool>? askForFriendPromotions,
|
||||
Expression<int>? mediaSendCounter,
|
||||
Expression<int>? mediaReceivedCounter,
|
||||
|
|
@ -1166,6 +1284,10 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
'recovery_last_heartbeat': recoveryLastHeartbeat,
|
||||
if (recoverySecretShare != null)
|
||||
'recovery_secret_share': recoverySecretShare,
|
||||
if (recoveryContactsSecretShare != null)
|
||||
'recovery_contacts_secret_share': recoveryContactsSecretShare,
|
||||
if (recoveryContactsLastHeartbeat != null)
|
||||
'recovery_contacts_last_heartbeat': recoveryContactsLastHeartbeat,
|
||||
if (askForFriendPromotions != null)
|
||||
'ask_for_friend_promotions': askForFriendPromotions,
|
||||
if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter,
|
||||
|
|
@ -1194,6 +1316,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
Value<bool>? recoveryIsTrustedFriend,
|
||||
Value<DateTime?>? recoveryLastHeartbeat,
|
||||
Value<Uint8List?>? recoverySecretShare,
|
||||
Value<Uint8List?>? recoveryContactsSecretShare,
|
||||
Value<DateTime?>? recoveryContactsLastHeartbeat,
|
||||
Value<bool?>? askForFriendPromotions,
|
||||
Value<int>? mediaSendCounter,
|
||||
Value<int>? mediaReceivedCounter,
|
||||
|
|
@ -1222,6 +1346,10 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
recoveryLastHeartbeat:
|
||||
recoveryLastHeartbeat ?? this.recoveryLastHeartbeat,
|
||||
recoverySecretShare: recoverySecretShare ?? this.recoverySecretShare,
|
||||
recoveryContactsSecretShare:
|
||||
recoveryContactsSecretShare ?? this.recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat:
|
||||
recoveryContactsLastHeartbeat ?? this.recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions:
|
||||
askForFriendPromotions ?? this.askForFriendPromotions,
|
||||
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
|
||||
|
|
@ -1303,6 +1431,16 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
recoverySecretShare.value,
|
||||
);
|
||||
}
|
||||
if (recoveryContactsSecretShare.present) {
|
||||
map['recovery_contacts_secret_share'] = Variable<Uint8List>(
|
||||
recoveryContactsSecretShare.value,
|
||||
);
|
||||
}
|
||||
if (recoveryContactsLastHeartbeat.present) {
|
||||
map['recovery_contacts_last_heartbeat'] = Variable<DateTime>(
|
||||
recoveryContactsLastHeartbeat.value,
|
||||
);
|
||||
}
|
||||
if (askForFriendPromotions.present) {
|
||||
map['ask_for_friend_promotions'] = Variable<bool>(
|
||||
askForFriendPromotions.value,
|
||||
|
|
@ -1339,6 +1477,10 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
|||
..write('recoveryIsTrustedFriend: $recoveryIsTrustedFriend, ')
|
||||
..write('recoveryLastHeartbeat: $recoveryLastHeartbeat, ')
|
||||
..write('recoverySecretShare: $recoverySecretShare, ')
|
||||
..write('recoveryContactsSecretShare: $recoveryContactsSecretShare, ')
|
||||
..write(
|
||||
'recoveryContactsLastHeartbeat: $recoveryContactsLastHeartbeat, ',
|
||||
)
|
||||
..write('askForFriendPromotions: $askForFriendPromotions, ')
|
||||
..write('mediaSendCounter: $mediaSendCounter, ')
|
||||
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
||||
|
|
@ -13121,6 +13263,8 @@ typedef $$ContactsTableCreateCompanionBuilder =
|
|||
Value<bool> recoveryIsTrustedFriend,
|
||||
Value<DateTime?> recoveryLastHeartbeat,
|
||||
Value<Uint8List?> recoverySecretShare,
|
||||
Value<Uint8List?> recoveryContactsSecretShare,
|
||||
Value<DateTime?> recoveryContactsLastHeartbeat,
|
||||
Value<bool?> askForFriendPromotions,
|
||||
Value<int> mediaSendCounter,
|
||||
Value<int> mediaReceivedCounter,
|
||||
|
|
@ -13146,6 +13290,8 @@ typedef $$ContactsTableUpdateCompanionBuilder =
|
|||
Value<bool> recoveryIsTrustedFriend,
|
||||
Value<DateTime?> recoveryLastHeartbeat,
|
||||
Value<Uint8List?> recoverySecretShare,
|
||||
Value<Uint8List?> recoveryContactsSecretShare,
|
||||
Value<DateTime?> recoveryContactsLastHeartbeat,
|
||||
Value<bool?> askForFriendPromotions,
|
||||
Value<int> mediaSendCounter,
|
||||
Value<int> mediaReceivedCounter,
|
||||
|
|
@ -13525,6 +13671,18 @@ class $$ContactsTableFilterComposer
|
|||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<Uint8List> get recoveryContactsSecretShare =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsSecretShare,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get recoveryContactsLastHeartbeat =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsLastHeartbeat,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get askForFriendPromotions => $composableBuilder(
|
||||
column: $table.askForFriendPromotions,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
|
|
@ -13928,6 +14086,18 @@ class $$ContactsTableOrderingComposer
|
|||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<Uint8List> get recoveryContactsSecretShare =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsSecretShare,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get recoveryContactsLastHeartbeat =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsLastHeartbeat,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get askForFriendPromotions => $composableBuilder(
|
||||
column: $table.askForFriendPromotions,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
|
|
@ -14032,6 +14202,18 @@ class $$ContactsTableAnnotationComposer
|
|||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<Uint8List> get recoveryContactsSecretShare =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsSecretShare,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get recoveryContactsLastHeartbeat =>
|
||||
$composableBuilder(
|
||||
column: $table.recoveryContactsLastHeartbeat,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<bool> get askForFriendPromotions => $composableBuilder(
|
||||
column: $table.askForFriendPromotions,
|
||||
builder: (column) => column,
|
||||
|
|
@ -14395,6 +14577,10 @@ class $$ContactsTableTableManager
|
|||
Value<bool> recoveryIsTrustedFriend = const Value.absent(),
|
||||
Value<DateTime?> recoveryLastHeartbeat = const Value.absent(),
|
||||
Value<Uint8List?> recoverySecretShare = const Value.absent(),
|
||||
Value<Uint8List?> recoveryContactsSecretShare =
|
||||
const Value.absent(),
|
||||
Value<DateTime?> recoveryContactsLastHeartbeat =
|
||||
const Value.absent(),
|
||||
Value<bool?> askForFriendPromotions = const Value.absent(),
|
||||
Value<int> mediaSendCounter = const Value.absent(),
|
||||
Value<int> mediaReceivedCounter = const Value.absent(),
|
||||
|
|
@ -14418,6 +14604,8 @@ class $$ContactsTableTableManager
|
|||
recoveryIsTrustedFriend: recoveryIsTrustedFriend,
|
||||
recoveryLastHeartbeat: recoveryLastHeartbeat,
|
||||
recoverySecretShare: recoverySecretShare,
|
||||
recoveryContactsSecretShare: recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions: askForFriendPromotions,
|
||||
mediaSendCounter: mediaSendCounter,
|
||||
mediaReceivedCounter: mediaReceivedCounter,
|
||||
|
|
@ -14443,6 +14631,10 @@ class $$ContactsTableTableManager
|
|||
Value<bool> recoveryIsTrustedFriend = const Value.absent(),
|
||||
Value<DateTime?> recoveryLastHeartbeat = const Value.absent(),
|
||||
Value<Uint8List?> recoverySecretShare = const Value.absent(),
|
||||
Value<Uint8List?> recoveryContactsSecretShare =
|
||||
const Value.absent(),
|
||||
Value<DateTime?> recoveryContactsLastHeartbeat =
|
||||
const Value.absent(),
|
||||
Value<bool?> askForFriendPromotions = const Value.absent(),
|
||||
Value<int> mediaSendCounter = const Value.absent(),
|
||||
Value<int> mediaReceivedCounter = const Value.absent(),
|
||||
|
|
@ -14466,6 +14658,8 @@ class $$ContactsTableTableManager
|
|||
recoveryIsTrustedFriend: recoveryIsTrustedFriend,
|
||||
recoveryLastHeartbeat: recoveryLastHeartbeat,
|
||||
recoverySecretShare: recoverySecretShare,
|
||||
recoveryContactsSecretShare: recoveryContactsSecretShare,
|
||||
recoveryContactsLastHeartbeat: recoveryContactsLastHeartbeat,
|
||||
askForFriendPromotions: askForFriendPromotions,
|
||||
mediaSendCounter: mediaSendCounter,
|
||||
mediaReceivedCounter: mediaReceivedCounter,
|
||||
|
|
|
|||
|
|
@ -10537,6 +10537,540 @@ i1.GeneratedColumn<i2.Uint8List> _column_251(String aliasedName) =>
|
|||
type: i1.DriftSqlType.blob,
|
||||
$customConstraints: 'NULL',
|
||||
);
|
||||
|
||||
final class Schema21 extends i0.VersionedSchema {
|
||||
Schema21({required super.database}) : super(version: 21);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
contacts,
|
||||
groups,
|
||||
mediaFiles,
|
||||
messages,
|
||||
messageHistories,
|
||||
reactions,
|
||||
groupMembers,
|
||||
receipts,
|
||||
receivedReceipts,
|
||||
signalIdentityKeyStores,
|
||||
signalPreKeyStores,
|
||||
signalSenderKeyStores,
|
||||
signalSessionStores,
|
||||
signalSignedPreKeyStores,
|
||||
messageActions,
|
||||
groupHistories,
|
||||
keyVerifications,
|
||||
verificationTokens,
|
||||
userDiscoveryAnnouncedUsers,
|
||||
userDiscoveryUserRelations,
|
||||
userDiscoveryOtherPromotions,
|
||||
userDiscoveryOwnPromotions,
|
||||
userDiscoveryShares,
|
||||
shortcuts,
|
||||
shortcutMembers,
|
||||
];
|
||||
late final Shape56 contacts = Shape56(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'contacts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(user_id)'],
|
||||
columns: [
|
||||
_column_106,
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_109,
|
||||
_column_110,
|
||||
_column_111,
|
||||
_column_112,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
_column_211,
|
||||
_column_212,
|
||||
_column_213,
|
||||
_column_249,
|
||||
_column_250,
|
||||
_column_251,
|
||||
_column_252,
|
||||
_column_253,
|
||||
_column_247,
|
||||
_column_214,
|
||||
_column_215,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape23 groups = Shape23(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id)'],
|
||||
columns: [
|
||||
_column_119,
|
||||
_column_120,
|
||||
_column_121,
|
||||
_column_122,
|
||||
_column_123,
|
||||
_column_124,
|
||||
_column_125,
|
||||
_column_126,
|
||||
_column_127,
|
||||
_column_128,
|
||||
_column_129,
|
||||
_column_130,
|
||||
_column_131,
|
||||
_column_132,
|
||||
_column_133,
|
||||
_column_134,
|
||||
_column_118,
|
||||
_column_135,
|
||||
_column_136,
|
||||
_column_137,
|
||||
_column_138,
|
||||
_column_139,
|
||||
_column_140,
|
||||
_column_141,
|
||||
_column_142,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape51 mediaFiles = Shape51(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'media_files',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(media_id)'],
|
||||
columns: [
|
||||
_column_143,
|
||||
_column_144,
|
||||
_column_145,
|
||||
_column_146,
|
||||
_column_147,
|
||||
_column_148,
|
||||
_column_149,
|
||||
_column_239,
|
||||
_column_240,
|
||||
_column_207,
|
||||
_column_150,
|
||||
_column_151,
|
||||
_column_152,
|
||||
_column_153,
|
||||
_column_154,
|
||||
_column_155,
|
||||
_column_156,
|
||||
_column_157,
|
||||
_column_244,
|
||||
_column_245,
|
||||
_column_118,
|
||||
_column_241,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape25 messages = Shape25(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'messages',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_159,
|
||||
_column_160,
|
||||
_column_144,
|
||||
_column_161,
|
||||
_column_162,
|
||||
_column_163,
|
||||
_column_164,
|
||||
_column_165,
|
||||
_column_153,
|
||||
_column_166,
|
||||
_column_167,
|
||||
_column_168,
|
||||
_column_169,
|
||||
_column_118,
|
||||
_column_170,
|
||||
_column_171,
|
||||
_column_172,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape26 messageHistories = Shape26(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_173,
|
||||
_column_174,
|
||||
_column_175,
|
||||
_column_161,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape27 reactions = Shape27(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'reactions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'],
|
||||
columns: [_column_174, _column_176, _column_177, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape38 groupMembers = Shape38(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_members',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id, contact_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_178,
|
||||
_column_179,
|
||||
_column_180,
|
||||
_column_209,
|
||||
_column_210,
|
||||
_column_181,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape37 receipts = Shape37(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [
|
||||
_column_182,
|
||||
_column_183,
|
||||
_column_184,
|
||||
_column_185,
|
||||
_column_186,
|
||||
_column_208,
|
||||
_column_187,
|
||||
_column_188,
|
||||
_column_189,
|
||||
_column_190,
|
||||
_column_191,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape30 receivedReceipts = Shape30(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'received_receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [_column_182, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape31 signalIdentityKeyStores = Shape31(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_identity_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_194, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape32 signalPreKeyStores = Shape32(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_pre_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(pre_key_id)'],
|
||||
columns: [_column_195, _column_196, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape11 signalSenderKeyStores = Shape11(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_sender_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(sender_key_name)'],
|
||||
columns: [_column_197, _column_198],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape33 signalSessionStores = Shape33(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_session_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_199, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape50 signalSignedPreKeyStores = Shape50(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_signed_pre_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(signed_pre_key_id)'],
|
||||
columns: [_column_242, _column_243, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape34 messageActions = Shape34(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_actions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'],
|
||||
columns: [_column_174, _column_183, _column_144, _column_200],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape35 groupHistories = Shape35(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_history_id)'],
|
||||
columns: [
|
||||
_column_201,
|
||||
_column_158,
|
||||
_column_202,
|
||||
_column_203,
|
||||
_column_204,
|
||||
_column_205,
|
||||
_column_206,
|
||||
_column_144,
|
||||
_column_200,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape54 keyVerifications = Shape54(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'key_verifications',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_216,
|
||||
_column_183,
|
||||
_column_144,
|
||||
_column_248,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape41 verificationTokens = Shape41(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'verification_tokens',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_217, _column_218, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape52 userDiscoveryAnnouncedUsers = Shape52(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_announced_users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(announced_user_id)'],
|
||||
columns: [
|
||||
_column_219,
|
||||
_column_220,
|
||||
_column_221,
|
||||
_column_222,
|
||||
_column_223,
|
||||
_column_224,
|
||||
_column_246,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape43 userDiscoveryUserRelations = Shape43(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_user_relations',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'],
|
||||
columns: [_column_225, _column_226, _column_227],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape44 userDiscoveryOtherPromotions = Shape44(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_other_promotions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(from_contact_id, public_id)'],
|
||||
columns: [
|
||||
_column_226,
|
||||
_column_228,
|
||||
_column_229,
|
||||
_column_230,
|
||||
_column_231,
|
||||
_column_227,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape45 userDiscoveryOwnPromotions = Shape45(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_own_promotions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_232, _column_183, _column_233],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape46 userDiscoveryShares = Shape46(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_shares',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_234, _column_235, _column_175],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape47 shortcuts = Shape47(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'shortcuts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_173, _column_236, _column_237],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape48 shortcutMembers = Shape48(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'shortcut_members',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(shortcut_id, group_id)'],
|
||||
columns: [_column_238, _column_158],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
}
|
||||
|
||||
class Shape56 extends i0.VersionedTable {
|
||||
Shape56({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get userId =>
|
||||
columnsByName['user_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get username =>
|
||||
columnsByName['username']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get displayName =>
|
||||
columnsByName['display_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get nickName =>
|
||||
columnsByName['nick_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get avatarSvgCompressed =>
|
||||
columnsByName['avatar_svg_compressed']!
|
||||
as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<int> get senderProfileCounter =>
|
||||
columnsByName['sender_profile_counter']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get accepted =>
|
||||
columnsByName['accepted']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get deletedByUser =>
|
||||
columnsByName['deleted_by_user']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get requested =>
|
||||
columnsByName['requested']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get blocked =>
|
||||
columnsByName['blocked']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get verified =>
|
||||
columnsByName['verified']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get accountDeleted =>
|
||||
columnsByName['account_deleted']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get userDiscoveryVersion =>
|
||||
columnsByName['user_discovery_version']!
|
||||
as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<int> get userDiscoveryExcluded =>
|
||||
columnsByName['user_discovery_excluded']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get userDiscoveryManualApproved =>
|
||||
columnsByName['user_discovery_manual_approved']!
|
||||
as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get recoveryIsTrustedFriend =>
|
||||
columnsByName['recovery_is_trusted_friend']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get recoveryLastHeartbeat =>
|
||||
columnsByName['recovery_last_heartbeat']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get recoverySecretShare =>
|
||||
columnsByName['recovery_secret_share']!
|
||||
as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get recoveryContactsSecretShare =>
|
||||
columnsByName['recovery_contacts_secret_share']!
|
||||
as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<int> get recoveryContactsLastHeartbeat =>
|
||||
columnsByName['recovery_contacts_last_heartbeat']!
|
||||
as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get askForFriendPromotions =>
|
||||
columnsByName['ask_for_friend_promotions']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get mediaSendCounter =>
|
||||
columnsByName['media_send_counter']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get mediaReceivedCounter =>
|
||||
columnsByName['media_received_counter']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<i2.Uint8List> _column_252(String aliasedName) =>
|
||||
i1.GeneratedColumn<i2.Uint8List>(
|
||||
'recovery_contacts_secret_share',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.blob,
|
||||
$customConstraints: 'NULL',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_253(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'recovery_contacts_last_heartbeat',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NULL',
|
||||
);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
|
|
@ -10557,6 +11091,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
required Future<void> Function(i1.Migrator m, Schema18 schema) from17To18,
|
||||
required Future<void> Function(i1.Migrator m, Schema19 schema) from18To19,
|
||||
required Future<void> Function(i1.Migrator m, Schema20 schema) from19To20,
|
||||
required Future<void> Function(i1.Migrator m, Schema21 schema) from20To21,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
|
|
@ -10655,6 +11190,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
final migrator = i1.Migrator(database, schema);
|
||||
await from19To20(migrator, schema);
|
||||
return 20;
|
||||
case 20:
|
||||
final schema = Schema21(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from20To21(migrator, schema);
|
||||
return 21;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
|
|
@ -10681,6 +11221,7 @@ i1.OnUpgrade stepByStep({
|
|||
required Future<void> Function(i1.Migrator m, Schema18 schema) from17To18,
|
||||
required Future<void> Function(i1.Migrator m, Schema19 schema) from18To19,
|
||||
required Future<void> Function(i1.Migrator m, Schema20 schema) from19To20,
|
||||
required Future<void> Function(i1.Migrator m, Schema21 schema) from20To21,
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
|
|
@ -10702,5 +11243,6 @@ i1.OnUpgrade stepByStep({
|
|||
from17To18: from17To18,
|
||||
from18To19: from18To19,
|
||||
from19To20: from19To20,
|
||||
from20To21: from20To21,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -208,22 +208,29 @@ class TwonlySafeBackup {
|
|||
|
||||
@JsonSerializable()
|
||||
class PasswordLessRecovery {
|
||||
PasswordLessRecovery({
|
||||
this.email,
|
||||
this.pinSeed,
|
||||
this.pinUnlockToken,
|
||||
this.threshold,
|
||||
this.lastHeartbeat,
|
||||
});
|
||||
PasswordLessRecovery();
|
||||
|
||||
factory PasswordLessRecovery.fromJson(Map<String, dynamic> json) =>
|
||||
_$PasswordLessRecoveryFromJson(json);
|
||||
|
||||
// Only stored, so the user can see his deposit email address...
|
||||
String? email;
|
||||
String? pinSeed;
|
||||
String? pinUnlockToken;
|
||||
int? threshold;
|
||||
DateTime? lastHeartbeat;
|
||||
|
||||
// <--
|
||||
// Data shared with trusted friends
|
||||
// Trusted friends are able to brute-force the pin -> Server delets after X tries
|
||||
List<int>? pinSeed;
|
||||
List<int>? pinUnlockToken;
|
||||
|
||||
// Stored not on the server, so the server is unable to link a email to a user until the actuall recovery or can
|
||||
// brute-force the pin
|
||||
List<int>? encryptedServerKeyNonce;
|
||||
List<int>? encryptedServerKeyMac;
|
||||
// --->
|
||||
|
||||
// Checking with the server that the server data is valid and not delted throug the pin protection for example.
|
||||
DateTime? lastServerHeartbeat;
|
||||
List<int>? encryptedServerKey;
|
||||
|
||||
Map<String, dynamic> toJson() => _$PasswordLessRecoveryToJson(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,15 +243,27 @@ const _$LastBackupUploadStateEnumMap = {
|
|||
|
||||
PasswordLessRecovery _$PasswordLessRecoveryFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => PasswordLessRecovery(
|
||||
email: json['email'] as String?,
|
||||
pinSeed: json['pinSeed'] as String?,
|
||||
pinUnlockToken: json['pinUnlockToken'] as String?,
|
||||
threshold: (json['threshold'] as num?)?.toInt(),
|
||||
lastHeartbeat: json['lastHeartbeat'] == null
|
||||
) => PasswordLessRecovery()
|
||||
..email = json['email'] as String?
|
||||
..pinSeed = (json['pinSeed'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList()
|
||||
..pinUnlockToken = (json['pinUnlockToken'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList()
|
||||
..encryptedServerKeyNonce =
|
||||
(json['encryptedServerKeyNonce'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList()
|
||||
..encryptedServerKeyMac = (json['encryptedServerKeyMac'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList()
|
||||
..lastServerHeartbeat = json['lastServerHeartbeat'] == null
|
||||
? null
|
||||
: DateTime.parse(json['lastHeartbeat'] as String),
|
||||
);
|
||||
: DateTime.parse(json['lastServerHeartbeat'] as String)
|
||||
..encryptedServerKey = (json['encryptedServerKey'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList();
|
||||
|
||||
Map<String, dynamic> _$PasswordLessRecoveryToJson(
|
||||
PasswordLessRecovery instance,
|
||||
|
|
@ -259,6 +271,8 @@ Map<String, dynamic> _$PasswordLessRecoveryToJson(
|
|||
'email': instance.email,
|
||||
'pinSeed': instance.pinSeed,
|
||||
'pinUnlockToken': instance.pinUnlockToken,
|
||||
'threshold': instance.threshold,
|
||||
'lastHeartbeat': instance.lastHeartbeat?.toIso8601String(),
|
||||
'encryptedServerKeyNonce': instance.encryptedServerKeyNonce,
|
||||
'encryptedServerKeyMac': instance.encryptedServerKeyMac,
|
||||
'lastServerHeartbeat': instance.lastServerHeartbeat?.toIso8601String(),
|
||||
'encryptedServerKey': instance.encryptedServerKey,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2182,6 +2182,87 @@ class ApplicationData_Deprecated extends $pb.GeneratedMessage {
|
|||
static ApplicationData_Deprecated? _defaultInstance;
|
||||
}
|
||||
|
||||
class ApplicationData_RegisterPasswordLessRecovery
|
||||
extends $pb.GeneratedMessage {
|
||||
factory ApplicationData_RegisterPasswordLessRecovery({
|
||||
$core.List<$core.int>? pinUnlockToken,
|
||||
$core.List<$core.int>? encryptedServerKey,
|
||||
}) {
|
||||
final result = create();
|
||||
if (pinUnlockToken != null) result.pinUnlockToken = pinUnlockToken;
|
||||
if (encryptedServerKey != null)
|
||||
result.encryptedServerKey = encryptedServerKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
ApplicationData_RegisterPasswordLessRecovery._();
|
||||
|
||||
factory ApplicationData_RegisterPasswordLessRecovery.fromBuffer(
|
||||
$core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory ApplicationData_RegisterPasswordLessRecovery.fromJson(
|
||||
$core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'ApplicationData.RegisterPasswordLessRecovery',
|
||||
package:
|
||||
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
||||
createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'pinUnlockToken', $pb.PbFieldType.OY,
|
||||
protoName: 'pinUnlockToken')
|
||||
..a<$core.List<$core.int>>(
|
||||
2, _omitFieldNames ? '' : 'encryptedServerKey', $pb.PbFieldType.OY,
|
||||
protoName: 'encryptedServerKey')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
ApplicationData_RegisterPasswordLessRecovery clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
ApplicationData_RegisterPasswordLessRecovery copyWith(
|
||||
void Function(ApplicationData_RegisterPasswordLessRecovery)
|
||||
updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as ApplicationData_RegisterPasswordLessRecovery))
|
||||
as ApplicationData_RegisterPasswordLessRecovery;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ApplicationData_RegisterPasswordLessRecovery create() =>
|
||||
ApplicationData_RegisterPasswordLessRecovery._();
|
||||
@$core.override
|
||||
ApplicationData_RegisterPasswordLessRecovery createEmptyInstance() =>
|
||||
create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static ApplicationData_RegisterPasswordLessRecovery getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
|
||||
ApplicationData_RegisterPasswordLessRecovery>(create);
|
||||
static ApplicationData_RegisterPasswordLessRecovery? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get pinUnlockToken => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set pinUnlockToken($core.List<$core.int> value) => $_setBytes(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasPinUnlockToken() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearPinUnlockToken() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get encryptedServerKey => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set encryptedServerKey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasEncryptedServerKey() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearEncryptedServerKey() => $_clearField(2);
|
||||
}
|
||||
|
||||
enum ApplicationData_ApplicationData {
|
||||
textMessage,
|
||||
getUserByUsername,
|
||||
|
|
@ -2209,6 +2290,7 @@ enum ApplicationData_ApplicationData {
|
|||
ipaForceCheck,
|
||||
addAdditionalUser,
|
||||
setLoginToken,
|
||||
registerPasswordlessRecovery,
|
||||
notSet
|
||||
}
|
||||
|
||||
|
|
@ -2240,6 +2322,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
ApplicationData_IPAForceCheck? ipaForceCheck,
|
||||
ApplicationData_AddAdditionalUser? addAdditionalUser,
|
||||
ApplicationData_SetLoginToken? setLoginToken,
|
||||
ApplicationData_RegisterPasswordLessRecovery? registerPasswordlessRecovery,
|
||||
}) {
|
||||
final result = create();
|
||||
if (textMessage != null) result.textMessage = textMessage;
|
||||
|
|
@ -2274,6 +2357,8 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
if (ipaForceCheck != null) result.ipaForceCheck = ipaForceCheck;
|
||||
if (addAdditionalUser != null) result.addAdditionalUser = addAdditionalUser;
|
||||
if (setLoginToken != null) result.setLoginToken = setLoginToken;
|
||||
if (registerPasswordlessRecovery != null)
|
||||
result.registerPasswordlessRecovery = registerPasswordlessRecovery;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -2314,6 +2399,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
28: ApplicationData_ApplicationData.ipaForceCheck,
|
||||
29: ApplicationData_ApplicationData.addAdditionalUser,
|
||||
30: ApplicationData_ApplicationData.setLoginToken,
|
||||
31: ApplicationData_ApplicationData.registerPasswordlessRecovery,
|
||||
0: ApplicationData_ApplicationData.notSet
|
||||
};
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
|
|
@ -2347,7 +2433,8 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
27,
|
||||
28,
|
||||
29,
|
||||
30
|
||||
30,
|
||||
31
|
||||
])
|
||||
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
|
||||
protoName: 'textMessage',
|
||||
|
|
@ -2439,6 +2526,9 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
..aOM<ApplicationData_SetLoginToken>(
|
||||
30, _omitFieldNames ? '' : 'setLoginToken',
|
||||
subBuilder: ApplicationData_SetLoginToken.create)
|
||||
..aOM<ApplicationData_RegisterPasswordLessRecovery>(
|
||||
31, _omitFieldNames ? '' : 'registerPasswordlessRecovery',
|
||||
subBuilder: ApplicationData_RegisterPasswordLessRecovery.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -2486,6 +2576,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
@$pb.TagNumber(28)
|
||||
@$pb.TagNumber(29)
|
||||
@$pb.TagNumber(30)
|
||||
@$pb.TagNumber(31)
|
||||
ApplicationData_ApplicationData whichApplicationData() =>
|
||||
_ApplicationData_ApplicationDataByTag[$_whichOneof(0)]!;
|
||||
@$pb.TagNumber(1)
|
||||
|
|
@ -2514,6 +2605,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
@$pb.TagNumber(28)
|
||||
@$pb.TagNumber(29)
|
||||
@$pb.TagNumber(30)
|
||||
@$pb.TagNumber(31)
|
||||
void clearApplicationData() => $_clearField($_whichOneof(0));
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
|
|
@ -2819,6 +2911,21 @@ class ApplicationData extends $pb.GeneratedMessage {
|
|||
void clearSetLoginToken() => $_clearField(30);
|
||||
@$pb.TagNumber(30)
|
||||
ApplicationData_SetLoginToken ensureSetLoginToken() => $_ensure(25);
|
||||
|
||||
@$pb.TagNumber(31)
|
||||
ApplicationData_RegisterPasswordLessRecovery
|
||||
get registerPasswordlessRecovery => $_getN(26);
|
||||
@$pb.TagNumber(31)
|
||||
set registerPasswordlessRecovery(
|
||||
ApplicationData_RegisterPasswordLessRecovery value) =>
|
||||
$_setField(31, value);
|
||||
@$pb.TagNumber(31)
|
||||
$core.bool hasRegisterPasswordlessRecovery() => $_has(26);
|
||||
@$pb.TagNumber(31)
|
||||
void clearRegisterPasswordlessRecovery() => $_clearField(31);
|
||||
@$pb.TagNumber(31)
|
||||
ApplicationData_RegisterPasswordLessRecovery
|
||||
ensureRegisterPasswordlessRecovery() => $_ensure(26);
|
||||
}
|
||||
|
||||
class Response_PreKey extends $pb.GeneratedMessage {
|
||||
|
|
|
|||
|
|
@ -575,6 +575,15 @@ const ApplicationData$json = {
|
|||
'9': 0,
|
||||
'10': 'setLoginToken'
|
||||
},
|
||||
{
|
||||
'1': 'register_passwordless_recovery',
|
||||
'3': 31,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.client_to_server.ApplicationData.RegisterPasswordLessRecovery',
|
||||
'9': 0,
|
||||
'10': 'registerPasswordlessRecovery'
|
||||
},
|
||||
],
|
||||
'3': [
|
||||
ApplicationData_TextMessage$json,
|
||||
|
|
@ -596,7 +605,8 @@ const ApplicationData$json = {
|
|||
ApplicationData_DeleteAccount$json,
|
||||
ApplicationData_AddAdditionalUser$json,
|
||||
ApplicationData_SetLoginToken$json,
|
||||
ApplicationData_Deprecated$json
|
||||
ApplicationData_Deprecated$json,
|
||||
ApplicationData_RegisterPasswordLessRecovery$json
|
||||
],
|
||||
'8': [
|
||||
{'1': 'ApplicationData'},
|
||||
|
|
@ -775,6 +785,35 @@ const ApplicationData_Deprecated$json = {
|
|||
'1': 'Deprecated',
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use applicationDataDescriptor instead')
|
||||
const ApplicationData_RegisterPasswordLessRecovery$json = {
|
||||
'1': 'RegisterPasswordLessRecovery',
|
||||
'2': [
|
||||
{
|
||||
'1': 'pinUnlockToken',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 0,
|
||||
'10': 'pinUnlockToken',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'encryptedServerKey',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 1,
|
||||
'10': 'encryptedServerKey',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_pinUnlockToken'},
|
||||
{'1': '_encryptedServerKey'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
||||
'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dE1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm'
|
||||
|
|
@ -820,27 +859,32 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
|||
'dmVBZGRpdGlvbmFsVXNlchJjChFhZGRBZGRpdGlvbmFsVXNlchgdIAEoCzIzLmNsaWVudF90b1'
|
||||
'9zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkFkZEFkZGl0aW9uYWxVc2VySABSEWFkZEFkZGl0aW9u'
|
||||
'YWxVc2VyElkKD3NldF9sb2dpbl90b2tlbhgeIAEoCzIvLmNsaWVudF90b19zZXJ2ZXIuQXBwbG'
|
||||
'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhpqCgtUZXh0TWVzc2Fn'
|
||||
'ZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEoDFIEYm9keRIgCglwdXNoX2'
|
||||
'RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2Vy'
|
||||
'bmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hhbmdlVXNlcm5hbWUSGgoIdX'
|
||||
'Nlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2ds'
|
||||
'ZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUg'
|
||||
'Z1c2VySWQaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFwoVR2V0QWRkQWNjb3VudHNJbnZpdGVzGhUK'
|
||||
'E0dldEN1cnJlbnRQbGFuSW5mb3MaLwoUUmVtb3ZlQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZB'
|
||||
'gBIAEoA1IGdXNlcklkGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1'
|
||||
'c2VySWQaMgoXR2V0U2lnbmVkUHJlS2V5QnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlck'
|
||||
'lkGpsBChJVcGRhdGVTaWduZWRQcmVLZXkSKAoQc2lnbmVkX3ByZWtleV9pZBgBIAEoA1IOc2ln'
|
||||
'bmVkUHJla2V5SWQSIwoNc2lnbmVkX3ByZWtleRgCIAEoDFIMc2lnbmVkUHJla2V5EjYKF3NpZ2'
|
||||
'5lZF9wcmVrZXlfc2lnbmF0dXJlGAMgASgMUhVzaWduZWRQcmVrZXlTaWduYXR1cmUaNQoMRG93'
|
||||
'bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuGk4KClJlcG'
|
||||
'9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3J0ZWRVc2VySWQSFgoGcmVh'
|
||||
'c29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJvZHVjdF9pZBgBIAEoCVIJcH'
|
||||
'JvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcmlmaWNhdGlvbl9kYXRhGAMg'
|
||||
'ASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2saDwoNRGVsZXRlQWNjb3VudB'
|
||||
'osChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaMAoNU2V0TG9n'
|
||||
'aW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tlbhoMCgpEZXByZWNhdGVkQh'
|
||||
'EKD0FwcGxpY2F0aW9uRGF0YQ==');
|
||||
'ljYXRpb25EYXRhLlNldExvZ2luVG9rZW5IAFINc2V0TG9naW5Ub2tlbhKGAQoecmVnaXN0ZXJf'
|
||||
'cGFzc3dvcmRsZXNzX3JlY292ZXJ5GB8gASgLMj4uY2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdG'
|
||||
'lvbkRhdGEuUmVnaXN0ZXJQYXNzd29yZExlc3NSZWNvdmVyeUgAUhxyZWdpc3RlclBhc3N3b3Jk'
|
||||
'bGVzc1JlY292ZXJ5GmoKC1RleHRNZXNzYWdlEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBISCg'
|
||||
'Rib2R5GAMgASgMUgRib2R5EiAKCXB1c2hfZGF0YRgEIAEoDEgAUghwdXNoRGF0YYgBAUIMCgpf'
|
||||
'cHVzaF9kYXRhGi8KEUdldFVzZXJCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEgASgJUgh1c2Vybm'
|
||||
'FtZRosCg5DaGFuZ2VVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaNQoUVXBk'
|
||||
'YXRlR29vZ2xlRmNtVG9rZW4SHQoKZ29vZ2xlX2ZjbRgBIAEoCVIJZ29vZ2xlRmNtGiYKC0dldF'
|
||||
'VzZXJCeUlkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoTChFHZXRBdmFpbGFibGVQbGFucxoX'
|
||||
'ChVHZXRBZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxovChRSZW1vdm'
|
||||
'VBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaLQoSR2V0UHJla2V5c0J5'
|
||||
'VXNlcklkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoyChdHZXRTaWduZWRQcmVLZXlCeVVzZX'
|
||||
'JJZBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVwZGF0ZVNpZ25lZFByZUtleRIoChBz'
|
||||
'aWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1zaWduZWRfcHJla2V5GA'
|
||||
'IgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYXR1cmUYAyABKAxSFXNp'
|
||||
'Z25lZFByZWtleVNpZ25hdHVyZRo1CgxEb3dubG9hZERvbmUSJQoOZG93bmxvYWRfdG9rZW4YAS'
|
||||
'ABKAxSDWRvd25sb2FkVG9rZW4aTgoKUmVwb3J0VXNlchIoChByZXBvcnRlZF91c2VyX2lkGAEg'
|
||||
'ASgDUg5yZXBvcnRlZFVzZXJJZBIWCgZyZWFzb24YAiABKAlSBnJlYXNvbhpxCgtJUEFQdXJjaG'
|
||||
'FzZRIdCgpwcm9kdWN0X2lkGAEgASgJUglwcm9kdWN0SWQSFgoGc291cmNlGAIgASgJUgZzb3Vy'
|
||||
'Y2USKwoRdmVyaWZpY2F0aW9uX2RhdGEYAyABKAlSEHZlcmlmaWNhdGlvbkRhdGEaDwoNSVBBRm'
|
||||
'9yY2VDaGVjaxoPCg1EZWxldGVBY2NvdW50GiwKEUFkZEFkZGl0aW9uYWxVc2VyEhcKB3VzZXJf'
|
||||
'aWQYASABKANSBnVzZXJJZBowCg1TZXRMb2dpblRva2VuEh8KC2xvZ2luX3Rva2VuGAEgASgMUg'
|
||||
'psb2dpblRva2VuGgwKCkRlcHJlY2F0ZWQaqgEKHFJlZ2lzdGVyUGFzc3dvcmRMZXNzUmVjb3Zl'
|
||||
'cnkSKwoOcGluVW5sb2NrVG9rZW4YASABKAxIAFIOcGluVW5sb2NrVG9rZW6IAQESMwoSZW5jcn'
|
||||
'lwdGVkU2VydmVyS2V5GAIgASgMSAFSEmVuY3J5cHRlZFNlcnZlcktleYgBAUIRCg9fcGluVW5s'
|
||||
'b2NrVG9rZW5CFQoTX2VuY3J5cHRlZFNlcnZlcktleUIRCg9BcHBsaWNhdGlvbkRhdGE=');
|
||||
|
||||
@$core.Deprecated('Use responseDescriptor instead')
|
||||
const Response$json = {
|
||||
|
|
|
|||
|
|
@ -1839,6 +1839,142 @@ class EncryptedContent_KeyVerificationProof extends $pb.GeneratedMessage {
|
|||
void clearCalculatedMac() => $_clearField(1);
|
||||
}
|
||||
|
||||
class EncryptedContent_PasswordLessRecovery extends $pb.GeneratedMessage {
|
||||
factory EncryptedContent_PasswordLessRecovery({
|
||||
$core.List<$core.int>? recoverySecretShare,
|
||||
$core.bool? delete,
|
||||
}) {
|
||||
final result = create();
|
||||
if (recoverySecretShare != null)
|
||||
result.recoverySecretShare = recoverySecretShare;
|
||||
if (delete != null) result.delete = delete;
|
||||
return result;
|
||||
}
|
||||
|
||||
EncryptedContent_PasswordLessRecovery._();
|
||||
|
||||
factory EncryptedContent_PasswordLessRecovery.fromBuffer(
|
||||
$core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory EncryptedContent_PasswordLessRecovery.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EncryptedContent.PasswordLessRecovery',
|
||||
createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'recoverySecretShare', $pb.PbFieldType.OY,
|
||||
protoName: 'recoverySecretShare')
|
||||
..aOB(2, _omitFieldNames ? '' : 'delete')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_PasswordLessRecovery clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_PasswordLessRecovery copyWith(
|
||||
void Function(EncryptedContent_PasswordLessRecovery) updates) =>
|
||||
super.copyWith((message) =>
|
||||
updates(message as EncryptedContent_PasswordLessRecovery))
|
||||
as EncryptedContent_PasswordLessRecovery;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_PasswordLessRecovery create() =>
|
||||
EncryptedContent_PasswordLessRecovery._();
|
||||
@$core.override
|
||||
EncryptedContent_PasswordLessRecovery createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_PasswordLessRecovery getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
|
||||
EncryptedContent_PasswordLessRecovery>(create);
|
||||
static EncryptedContent_PasswordLessRecovery? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get recoverySecretShare => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set recoverySecretShare($core.List<$core.int> value) => $_setBytes(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasRecoverySecretShare() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearRecoverySecretShare() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool get delete => $_getBF(1);
|
||||
@$pb.TagNumber(2)
|
||||
set delete($core.bool value) => $_setBool(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasDelete() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearDelete() => $_clearField(2);
|
||||
}
|
||||
|
||||
class EncryptedContent_PasswordLessRecoveryHeartbeat
|
||||
extends $pb.GeneratedMessage {
|
||||
factory EncryptedContent_PasswordLessRecoveryHeartbeat({
|
||||
$core.List<$core.int>? hash,
|
||||
}) {
|
||||
final result = create();
|
||||
if (hash != null) result.hash = hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat._();
|
||||
|
||||
factory EncryptedContent_PasswordLessRecoveryHeartbeat.fromBuffer(
|
||||
$core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory EncryptedContent_PasswordLessRecoveryHeartbeat.fromJson(
|
||||
$core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EncryptedContent.PasswordLessRecoveryHeartbeat',
|
||||
createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'hash', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat copyWith(
|
||||
void Function(EncryptedContent_PasswordLessRecoveryHeartbeat)
|
||||
updates) =>
|
||||
super.copyWith((message) => updates(
|
||||
message as EncryptedContent_PasswordLessRecoveryHeartbeat))
|
||||
as EncryptedContent_PasswordLessRecoveryHeartbeat;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_PasswordLessRecoveryHeartbeat create() =>
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat._();
|
||||
@$core.override
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat createEmptyInstance() =>
|
||||
create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_PasswordLessRecoveryHeartbeat getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat>(create);
|
||||
static EncryptedContent_PasswordLessRecoveryHeartbeat? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get hash => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set hash($core.List<$core.int> value) => $_setBytes(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasHash() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearHash() => $_clearField(1);
|
||||
}
|
||||
|
||||
class EncryptedContent extends $pb.GeneratedMessage {
|
||||
factory EncryptedContent({
|
||||
$core.String? groupId,
|
||||
|
|
@ -1865,6 +2001,9 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
EncryptedContent_UserDiscoveryUpdate? userDiscoveryUpdate,
|
||||
EncryptedContent_KeyVerificationProof? keyVerificationProof,
|
||||
$core.bool? askForFriendPromotions,
|
||||
EncryptedContent_PasswordLessRecovery? passwordlessRecovery,
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat?
|
||||
passwordlessRecoveryHeartbeat,
|
||||
}) {
|
||||
final result = create();
|
||||
if (groupId != null) result.groupId = groupId;
|
||||
|
|
@ -1899,6 +2038,10 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
result.keyVerificationProof = keyVerificationProof;
|
||||
if (askForFriendPromotions != null)
|
||||
result.askForFriendPromotions = askForFriendPromotions;
|
||||
if (passwordlessRecovery != null)
|
||||
result.passwordlessRecovery = passwordlessRecovery;
|
||||
if (passwordlessRecoveryHeartbeat != null)
|
||||
result.passwordlessRecoveryHeartbeat = passwordlessRecoveryHeartbeat;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1971,6 +2114,12 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
24, _omitFieldNames ? '' : 'keyVerificationProof',
|
||||
subBuilder: EncryptedContent_KeyVerificationProof.create)
|
||||
..aOB(25, _omitFieldNames ? '' : 'askForFriendPromotions')
|
||||
..aOM<EncryptedContent_PasswordLessRecovery>(
|
||||
26, _omitFieldNames ? '' : 'passwordlessRecovery',
|
||||
subBuilder: EncryptedContent_PasswordLessRecovery.create)
|
||||
..aOM<EncryptedContent_PasswordLessRecoveryHeartbeat>(
|
||||
27, _omitFieldNames ? '' : 'passwordlessRecoveryHeartbeat',
|
||||
subBuilder: EncryptedContent_PasswordLessRecoveryHeartbeat.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -2263,6 +2412,34 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
$core.bool hasAskForFriendPromotions() => $_has(23);
|
||||
@$pb.TagNumber(25)
|
||||
void clearAskForFriendPromotions() => $_clearField(25);
|
||||
|
||||
@$pb.TagNumber(26)
|
||||
EncryptedContent_PasswordLessRecovery get passwordlessRecovery => $_getN(24);
|
||||
@$pb.TagNumber(26)
|
||||
set passwordlessRecovery(EncryptedContent_PasswordLessRecovery value) =>
|
||||
$_setField(26, value);
|
||||
@$pb.TagNumber(26)
|
||||
$core.bool hasPasswordlessRecovery() => $_has(24);
|
||||
@$pb.TagNumber(26)
|
||||
void clearPasswordlessRecovery() => $_clearField(26);
|
||||
@$pb.TagNumber(26)
|
||||
EncryptedContent_PasswordLessRecovery ensurePasswordlessRecovery() =>
|
||||
$_ensure(24);
|
||||
|
||||
@$pb.TagNumber(27)
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat
|
||||
get passwordlessRecoveryHeartbeat => $_getN(25);
|
||||
@$pb.TagNumber(27)
|
||||
set passwordlessRecoveryHeartbeat(
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat value) =>
|
||||
$_setField(27, value);
|
||||
@$pb.TagNumber(27)
|
||||
$core.bool hasPasswordlessRecoveryHeartbeat() => $_has(25);
|
||||
@$pb.TagNumber(27)
|
||||
void clearPasswordlessRecoveryHeartbeat() => $_clearField(27);
|
||||
@$pb.TagNumber(27)
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat
|
||||
ensurePasswordlessRecoveryHeartbeat() => $_ensure(25);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
|
|
|
|||
|
|
@ -385,6 +385,26 @@ const EncryptedContent$json = {
|
|||
'10': 'keyVerificationProof',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'passwordless_recovery',
|
||||
'3': 26,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.EncryptedContent.PasswordLessRecovery',
|
||||
'9': 24,
|
||||
'10': 'passwordlessRecovery',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'passwordless_recovery_heartbeat',
|
||||
'3': 27,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.EncryptedContent.PasswordLessRecoveryHeartbeat',
|
||||
'9': 25,
|
||||
'10': 'passwordlessRecoveryHeartbeat',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'3': [
|
||||
EncryptedContent_ErrorMessages$json,
|
||||
|
|
@ -405,7 +425,9 @@ const EncryptedContent$json = {
|
|||
EncryptedContent_TypingIndicator$json,
|
||||
EncryptedContent_UserDiscoveryRequest$json,
|
||||
EncryptedContent_UserDiscoveryUpdate$json,
|
||||
EncryptedContent_KeyVerificationProof$json
|
||||
EncryptedContent_KeyVerificationProof$json,
|
||||
EncryptedContent_PasswordLessRecovery$json,
|
||||
EncryptedContent_PasswordLessRecoveryHeartbeat$json
|
||||
],
|
||||
'8': [
|
||||
{'1': '_group_id'},
|
||||
|
|
@ -432,6 +454,8 @@ const EncryptedContent$json = {
|
|||
{'1': '_user_discovery_request'},
|
||||
{'1': '_user_discovery_update'},
|
||||
{'1': '_key_verification_proof'},
|
||||
{'1': '_passwordless_recovery'},
|
||||
{'1': '_passwordless_recovery_heartbeat'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -955,6 +979,34 @@ const EncryptedContent_KeyVerificationProof$json = {
|
|||
],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||
const EncryptedContent_PasswordLessRecovery$json = {
|
||||
'1': 'PasswordLessRecovery',
|
||||
'2': [
|
||||
{
|
||||
'1': 'recoverySecretShare',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 0,
|
||||
'10': 'recoverySecretShare',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'delete', '3': 2, '4': 1, '5': 8, '10': 'delete'},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_recoverySecretShare'},
|
||||
],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||
const EncryptedContent_PasswordLessRecoveryHeartbeat$json = {
|
||||
'1': 'PasswordLessRecoveryHeartbeat',
|
||||
'2': [
|
||||
{'1': 'hash', '3': 1, '4': 1, '5': 12, '10': 'hash'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `EncryptedContent`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||
'ChBFbmNyeXB0ZWRDb250ZW50Eh4KCGdyb3VwX2lkGAIgASgJSABSB2dyb3VwSWSIAQESKQoOaX'
|
||||
|
|
@ -988,79 +1040,88 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
|||
'aXNjb3ZlcnlSZXF1ZXN0iAEBEl4KFXVzZXJfZGlzY292ZXJ5X3VwZGF0ZRgXIAEoCzIlLkVuY3'
|
||||
'J5cHRlZENvbnRlbnQuVXNlckRpc2NvdmVyeVVwZGF0ZUgWUhN1c2VyRGlzY292ZXJ5VXBkYXRl'
|
||||
'iAEBEmEKFmtleV92ZXJpZmljYXRpb25fcHJvb2YYGCABKAsyJi5FbmNyeXB0ZWRDb250ZW50Lk'
|
||||
'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBGpYCCg1FcnJv'
|
||||
'ck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZX'
|
||||
'MuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0'
|
||||
'SWQinAEKBFR5cGUSPAo4RVJST1JfUFJPQ0VTU0lOR19NRVNTQUdFX0NSRUFURURfQUNDT1VOVF'
|
||||
'9SRVFVRVNUX0lOU1RFQUQQABIYChRVTktOT1dOX01FU1NBR0VfVFlQRRACEhcKE1NFU1NJT05f'
|
||||
'T1VUX09GX1NZTkMQAxIjCh9HUk9VUF9OT1RfRk9VTkRfT1JfTk9UX0FfTUVNQkVSEAQahwEKC0'
|
||||
'dyb3VwQ3JlYXRlEhsKCXN0YXRlX2tleRgDIAEoDFIIc3RhdGVLZXkSKAoQZ3JvdXBfcHVibGlj'
|
||||
'X2tleRgEIAEoDFIOZ3JvdXBQdWJsaWNLZXkSIgoKZ3JvdXBfbmFtZRgFIAEoCUgAUglncm91cE'
|
||||
'5hbWWIAQFCDQoLX2dyb3VwX25hbWUaNQoJR3JvdXBKb2luEigKEGdyb3VwX3B1YmxpY19rZXkY'
|
||||
'ASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibGljS2V5GsgCCgtHcm91cF'
|
||||
'VwZGF0ZRIqChFncm91cF9hY3Rpb25fdHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEjMKE2Fm'
|
||||
'ZmVjdGVkX2NvbnRhY3RfaWQYAiABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESKQoObmV3X2'
|
||||
'dyb3VwX25hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElcKJm5ld19kZWxldGVfbWVzc2Fn'
|
||||
'ZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGAQgASgDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaW'
|
||||
'xsaXNlY29uZHOIAQFCFgoUX2FmZmVjdGVkX2NvbnRhY3RfaWRCEQoPX25ld19ncm91cF9uYW1l'
|
||||
'QikKJ19uZXdfZGVsZXRlX21lc3NhZ2VzX2FmdGVyX21pbGxpc2Vjb25kcxqvAQoLVGV4dE1lc3'
|
||||
'NhZ2USKgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0'
|
||||
'GAIgASgJUgR0ZXh0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEi0KEHF1b3RlX21lc3'
|
||||
'NhZ2VfaWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEwoRX3F1b3RlX21lc3NhZ2VfaWQa'
|
||||
'zgEKFUFkZGl0aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2'
|
||||
'VuZGVyTWVzc2FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyAB'
|
||||
'KAlSBHR5cGUSOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE'
|
||||
'1lc3NhZ2VEYXRhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpkCghSZWFjdGlvbhIq'
|
||||
'ChF0YXJnZXRfbWVzc2FnZV9pZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhQKBWVtb2ppGAIgAS'
|
||||
'gJUgVlbW9qaRIWCgZyZW1vdmUYAyABKAhSBnJlbW92ZRq+AgoNTWVzc2FnZVVwZGF0ZRI4CgR0'
|
||||
'eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5NZXNzYWdlVXBkYXRlLlR5cGVSBHR5cGUSLw'
|
||||
'oRc2VuZGVyX21lc3NhZ2VfaWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEBEj0KG211bHRp'
|
||||
'cGxlX3RhcmdldF9tZXNzYWdlX2lkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYWdlSWRzEh'
|
||||
'cKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVzdGFtcCIt'
|
||||
'CgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhQKEl9zZW5kZX'
|
||||
'JfbWVzc2FnZV9pZEIHCgVfdGV4dBqFBgoFTWVkaWESKgoRc2VuZGVyX21lc3NhZ2VfaWQYASAB'
|
||||
'KAlSD3NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGAIgASgOMhwuRW5jcnlwdGVkQ29udGVudC5NZW'
|
||||
'RpYS5UeXBlUgR0eXBlEkYKHWRpc3BsYXlfbGltaXRfaW5fbWlsbGlzZWNvbmRzGAMgASgDSABS'
|
||||
'GmRpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEjcKF3JlcXVpcmVzX2F1dGhlbnRpY2F0aW'
|
||||
'9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGlt'
|
||||
'ZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBiABKAlIAVIOcXVvdGVNZXNzYWdlSWSIAQESKg'
|
||||
'oOZG93bmxvYWRfdG9rZW4YByABKAxIAlINZG93bmxvYWRUb2tlbogBARIqCg5lbmNyeXB0aW9u'
|
||||
'X2tleRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEioKDmVuY3J5cHRpb25fbWFjGAkgASgMSA'
|
||||
'RSDWVuY3J5cHRpb25NYWOIAQESLgoQZW5jcnlwdGlvbl9ub25jZRgKIAEoDEgFUg9lbmNyeXB0'
|
||||
'aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb2'
|
||||
'5hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJ'
|
||||
'REVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIgCh5fZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY2'
|
||||
'9uZHNCEwoRX3F1b3RlX21lc3NhZ2VfaWRCEQoPX2Rvd25sb2FkX3Rva2VuQhEKD19lbmNyeXB0'
|
||||
'aW9uX2tleUIRCg9fZW5jcnlwdGlvbl9tYWNCEwoRX2VuY3J5cHRpb25fbm9uY2VCGgoYX2FkZG'
|
||||
'l0aW9uYWxfbWVzc2FnZV9kYXRhGqkBCgtNZWRpYVVwZGF0ZRI2CgR0eXBlGAEgASgOMiIuRW5j'
|
||||
'cnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEioKEXRhcmdldF9tZXNzYWdlX2'
|
||||
'lkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JF'
|
||||
'RBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgAS'
|
||||
'gOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUS'
|
||||
'CwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGqQCCg1Db250YWN0VXBkYXRlEj'
|
||||
'gKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlw'
|
||||
'ZRI3ChVhdmF0YXJfc3ZnX2NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZI'
|
||||
'gBARIfCgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgBARImCgxkaXNwbGF5X25hbWUYBCAB'
|
||||
'KAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCGA'
|
||||
'oWX2F2YXRhcl9zdmdfY29tcHJlc3NlZEILCglfdXNlcm5hbWVCDwoNX2Rpc3BsYXlfbmFtZRrZ'
|
||||
'AQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQuUHVzaEtleXMuVH'
|
||||
'lwZVIEdHlwZRIaCgZrZXlfaWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgASgMSAFSA2tl'
|
||||
'eYgBARIiCgpjcmVhdGVkX2F0GAQgASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUV'
|
||||
'VFU1QQABIKCgZVUERBVEUQAUIJCgdfa2V5X2lkQgYKBF9rZXlCDQoLX2NyZWF0ZWRfYXQarwEK'
|
||||
'CUZsYW1lU3luYxIjCg1mbGFtZV9jb3VudGVyGAEgASgDUgxmbGFtZUNvdW50ZXISOQoZbGFzdF'
|
||||
'9mbGFtZV9jb3VudGVyX2NoYW5nZRgCIAEoA1IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIfCgti'
|
||||
'ZXN0X2ZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZBIhCgxmb3JjZV91cGRhdGUYBCABKAhSC2Zvcm'
|
||||
'NlVXBkYXRlGk0KD1R5cGluZ0luZGljYXRvchIbCglpc190eXBpbmcYASABKAhSCGlzVHlwaW5n'
|
||||
'Eh0KCmNyZWF0ZWRfYXQYAiABKANSCWNyZWF0ZWRBdBo/ChRVc2VyRGlzY292ZXJ5UmVxdWVzdB'
|
||||
'InCg9jdXJyZW50X3ZlcnNpb24YASABKAxSDmN1cnJlbnRWZXJzaW9uGjEKE1VzZXJEaXNjb3Zl'
|
||||
'cnlVcGRhdGUSGgoIbWVzc2FnZXMYASADKAxSCG1lc3NhZ2VzGj0KFEtleVZlcmlmaWNhdGlvbl'
|
||||
'Byb29mEiUKDmNhbGN1bGF0ZWRfbWFjGAEgASgMUg1jYWxjdWxhdGVkTWFjQgsKCV9ncm91cF9p'
|
||||
'ZEIRCg9faXNfZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3Nlbm'
|
||||
'Rlcl91c2VyX2Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25z'
|
||||
'QhEKD19tZXNzYWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udG'
|
||||
'FjdF91cGRhdGVCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0IMCgpfcHVzaF9r'
|
||||
'ZXlzQgsKCV9yZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg8KDV9ncm91cF9jcmVhdGVCDQoLX2'
|
||||
'dyb3VwX2pvaW5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVzZW5kX2dyb3VwX3B1YmxpY19rZXlC'
|
||||
'EQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2FnZUITChFfdHlwaW'
|
||||
'5nX2luZGljYXRvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVxdWVzdEIYChZfdXNlcl9kaXNjb3Zl'
|
||||
'cnlfdXBkYXRlQhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb29m');
|
||||
'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBEmAKFXBhc3N3'
|
||||
'b3JkbGVzc19yZWNvdmVyeRgaIAEoCzImLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUm'
|
||||
'Vjb3ZlcnlIGFIUcGFzc3dvcmRsZXNzUmVjb3ZlcnmIAQESfAofcGFzc3dvcmRsZXNzX3JlY292'
|
||||
'ZXJ5X2hlYXJ0YmVhdBgbIAEoCzIvLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUmVjb3'
|
||||
'ZlcnlIZWFydGJlYXRIGVIdcGFzc3dvcmRsZXNzUmVjb3ZlcnlIZWFydGJlYXSIAQEalgIKDUVy'
|
||||
'cm9yTWVzc2FnZXMSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNzYW'
|
||||
'dlcy5UeXBlUgR0eXBlEiwKEnJlbGF0ZWRfcmVjZWlwdF9pZBgCIAEoCVIQcmVsYXRlZFJlY2Vp'
|
||||
'cHRJZCKcAQoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU'
|
||||
'5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVTU0FHRV9UWVBFEAISFwoTU0VTU0lP'
|
||||
'Tl9PVVRfT0ZfU1lOQxADEiMKH0dST1VQX05PVF9GT1VORF9PUl9OT1RfQV9NRU1CRVIQBBqHAQ'
|
||||
'oLR3JvdXBDcmVhdGUSGwoJc3RhdGVfa2V5GAMgASgMUghzdGF0ZUtleRIoChBncm91cF9wdWJs'
|
||||
'aWNfa2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRIiCgpncm91cF9uYW1lGAUgASgJSABSCWdyb3'
|
||||
'VwTmFtZYgBAUINCgtfZ3JvdXBfbmFtZRo1CglHcm91cEpvaW4SKAoQZ3JvdXBfcHVibGljX2tl'
|
||||
'eRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkayAIKC0dyb3'
|
||||
'VwVXBkYXRlEioKEWdyb3VwX2FjdGlvbl90eXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMwoT'
|
||||
'YWZmZWN0ZWRfY29udGFjdF9pZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZIgBARIpCg5uZX'
|
||||
'dfZ3JvdXBfbmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQESVwombmV3X2RlbGV0ZV9tZXNz'
|
||||
'YWdlc19hZnRlcl9taWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZnRlck'
|
||||
'1pbGxpc2Vjb25kc4gBAUIWChRfYWZmZWN0ZWRfY29udGFjdF9pZEIRCg9fbmV3X2dyb3VwX25h'
|
||||
'bWVCKQonX25ld19kZWxldGVfbWVzc2FnZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGq8BCgtUZXh0TW'
|
||||
'Vzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhIKBHRl'
|
||||
'eHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGAMgASgDUgl0aW1lc3RhbXASLQoQcXVvdGVfbW'
|
||||
'Vzc2FnZV9pZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUITChFfcXVvdGVfbWVzc2FnZV9p'
|
||||
'ZBrOAQoVQWRkaXRpb25hbERhdGFNZXNzYWdlEioKEXNlbmRlcl9tZXNzYWdlX2lkGAEgASgJUg'
|
||||
'9zZW5kZXJNZXNzYWdlSWQSHAoJdGltZXN0YW1wGAIgASgDUgl0aW1lc3RhbXASEgoEdHlwZRgD'
|
||||
'IAEoCVIEdHlwZRI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgEIAEoDEgAUhVhZGRpdGlvbm'
|
||||
'FsTWVzc2FnZURhdGGIAQFCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGmQKCFJlYWN0aW9u'
|
||||
'EioKEXRhcmdldF9tZXNzYWdlX2lkGAEgASgJUg90YXJnZXRNZXNzYWdlSWQSFAoFZW1vamkYAi'
|
||||
'ABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGr4CCg1NZXNzYWdlVXBkYXRlEjgK'
|
||||
'BHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdGUuVHlwZVIEdHlwZR'
|
||||
'IvChFzZW5kZXJfbWVzc2FnZV9pZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESPQobbXVs'
|
||||
'dGlwbGVfdGFyZ2V0X21lc3NhZ2VfaWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZH'
|
||||
'MSFwoEdGV4dBgEIAEoCUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1w'
|
||||
'Ii0KBFR5cGUSCgoGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCFAoSX3Nlbm'
|
||||
'Rlcl9tZXNzYWdlX2lkQgcKBV90ZXh0GoUGCgVNZWRpYRIqChFzZW5kZXJfbWVzc2FnZV9pZBgB'
|
||||
'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk'
|
||||
'1lZGlhLlR5cGVSBHR5cGUSRgodZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY29uZHMYAyABKANI'
|
||||
'AFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNwoXcmVxdWlyZXNfYXV0aGVudGljYX'
|
||||
'Rpb24YBCABKAhSFnJlcXVpcmVzQXV0aGVudGljYXRpb24SHAoJdGltZXN0YW1wGAUgASgDUgl0'
|
||||
'aW1lc3RhbXASLQoQcXVvdGVfbWVzc2FnZV9pZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBAR'
|
||||
'IqCg5kb3dubG9hZF90b2tlbhgHIAEoDEgCUg1kb3dubG9hZFRva2VuiAEBEioKDmVuY3J5cHRp'
|
||||
'b25fa2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmIAQESKgoOZW5jcnlwdGlvbl9tYWMYCSABKA'
|
||||
'xIBFINZW5jcnlwdGlvbk1hY4gBARIuChBlbmNyeXB0aW9uX25vbmNlGAogASgMSAVSD2VuY3J5'
|
||||
'cHRpb25Ob25jZYgBARI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgLIAEoDEgGUhVhZGRpdG'
|
||||
'lvbmFsTWVzc2FnZURhdGGIAQEiPgoEVHlwZRIMCghSRVVQTE9BRBAAEgkKBUlNQUdFEAESCQoF'
|
||||
'VklERU8QAhIHCgNHSUYQAxIJCgVBVURJTxAEQiAKHl9kaXNwbGF5X2xpbWl0X2luX21pbGxpc2'
|
||||
'Vjb25kc0ITChFfcXVvdGVfbWVzc2FnZV9pZEIRCg9fZG93bmxvYWRfdG9rZW5CEQoPX2VuY3J5'
|
||||
'cHRpb25fa2V5QhEKD19lbmNyeXB0aW9uX21hY0ITChFfZW5jcnlwdGlvbl9ub25jZUIaChhfYW'
|
||||
'RkaXRpb25hbF9tZXNzYWdlX2RhdGEaqQEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5F'
|
||||
'bmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKgoRdGFyZ2V0X21lc3NhZ2'
|
||||
'VfaWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RP'
|
||||
'UkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYAS'
|
||||
'ABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlw'
|
||||
'ZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIapAIKDUNvbnRhY3RVcGRhdG'
|
||||
'USOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0'
|
||||
'eXBlEjcKFWF2YXRhcl9zdmdfY29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJTdmdDb21wcmVzc2'
|
||||
'VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiYKDGRpc3BsYXlfbmFtZRgE'
|
||||
'IAEoCUgCUgtkaXNwbGF5TmFtZYgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAU'
|
||||
'IYChZfYXZhdGFyX3N2Z19jb21wcmVzc2VkQgsKCV91c2VybmFtZUIPCg1fZGlzcGxheV9uYW1l'
|
||||
'GtkBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy'
|
||||
'5UeXBlUgR0eXBlEhoKBmtleV9pZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAyABKAxIAVID'
|
||||
'a2V5iAEBEiIKCmNyZWF0ZWRfYXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUSCwoHUk'
|
||||
'VRVUVTVBAAEgoKBlVQREFURRABQgkKB19rZXlfaWRCBgoEX2tleUINCgtfY3JlYXRlZF9hdBqv'
|
||||
'AQoJRmxhbWVTeW5jEiMKDWZsYW1lX2NvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI5ChlsYX'
|
||||
'N0X2ZsYW1lX2NvdW50ZXJfY2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh8K'
|
||||
'C2Jlc3RfZnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiEKDGZvcmNlX3VwZGF0ZRgEIAEoCFILZm'
|
||||
'9yY2VVcGRhdGUaTQoPVHlwaW5nSW5kaWNhdG9yEhsKCWlzX3R5cGluZxgBIAEoCFIIaXNUeXBp'
|
||||
'bmcSHQoKY3JlYXRlZF9hdBgCIAEoA1IJY3JlYXRlZEF0Gj8KFFVzZXJEaXNjb3ZlcnlSZXF1ZX'
|
||||
'N0EicKD2N1cnJlbnRfdmVyc2lvbhgBIAEoDFIOY3VycmVudFZlcnNpb24aMQoTVXNlckRpc2Nv'
|
||||
'dmVyeVVwZGF0ZRIaCghtZXNzYWdlcxgBIAMoDFIIbWVzc2FnZXMaPQoUS2V5VmVyaWZpY2F0aW'
|
||||
'9uUHJvb2YSJQoOY2FsY3VsYXRlZF9tYWMYASABKAxSDWNhbGN1bGF0ZWRNYWMafQoUUGFzc3dv'
|
||||
'cmRMZXNzUmVjb3ZlcnkSNQoTcmVjb3ZlcnlTZWNyZXRTaGFyZRgBIAEoDEgAUhNyZWNvdmVyeV'
|
||||
'NlY3JldFNoYXJliAEBEhYKBmRlbGV0ZRgCIAEoCFIGZGVsZXRlQhYKFF9yZWNvdmVyeVNlY3Jl'
|
||||
'dFNoYXJlGjMKHVBhc3N3b3JkTGVzc1JlY292ZXJ5SGVhcnRiZWF0EhIKBGhhc2gYASABKAxSBG'
|
||||
'hhc2hCCwoJX2dyb3VwX2lkQhEKD19pc19kaXJlY3RfY2hhdEIZChdfc2VuZGVyX3Byb2ZpbGVf'
|
||||
'Y291bnRlckIgCh5fc2VuZGVyX3VzZXJfZGlzY292ZXJ5X3ZlcnNpb25CHAoaX2Fza19mb3JfZn'
|
||||
'JpZW5kX3Byb21vdGlvbnNCEQoPX21lc3NhZ2VfdXBkYXRlQggKBl9tZWRpYUIPCg1fbWVkaWFf'
|
||||
'dXBkYXRlQhEKD19jb250YWN0X3VwZGF0ZUISChBfY29udGFjdF9yZXF1ZXN0Qg0KC19mbGFtZV'
|
||||
'9zeW5jQgwKCl9wdXNoX2tleXNCCwoJX3JlYWN0aW9uQg8KDV90ZXh0X21lc3NhZ2VCDwoNX2dy'
|
||||
'b3VwX2NyZWF0ZUINCgtfZ3JvdXBfam9pbkIPCg1fZ3JvdXBfdXBkYXRlQhoKGF9yZXNlbmRfZ3'
|
||||
'JvdXBfcHVibGljX2tleUIRCg9fZXJyb3JfbWVzc2FnZXNCGgoYX2FkZGl0aW9uYWxfZGF0YV9t'
|
||||
'ZXNzYWdlQhMKEV90eXBpbmdfaW5kaWNhdG9yQhkKF191c2VyX2Rpc2NvdmVyeV9yZXF1ZXN0Qh'
|
||||
'gKFl91c2VyX2Rpc2NvdmVyeV91cGRhdGVCGQoXX2tleV92ZXJpZmljYXRpb25fcHJvb2ZCGAoW'
|
||||
'X3Bhc3N3b3JkbGVzc19yZWNvdmVyeUIiCiBfcGFzc3dvcmRsZXNzX3JlY292ZXJ5X2hlYXJ0Ym'
|
||||
'VhdA==');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,559 @@
|
|||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from passwordless_recovery.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
|
||||
|
||||
/// Send from the person who tries to recover their account.
|
||||
/// This can be done via a link, which will then be opend in the app of the contact.
|
||||
/// The contact than has to manualy select from which user he got the request.
|
||||
/// -> Using this phishing is harder, as the user has to manualy select the user to recovery
|
||||
/// -> The user who wants to recover his account does not need to remember her old username
|
||||
class RecoveryRequest extends $pb.GeneratedMessage {
|
||||
factory RecoveryRequest({
|
||||
$fixnum.Int64? tempId,
|
||||
$core.List<$core.int>? publicKey,
|
||||
}) {
|
||||
final result = create();
|
||||
if (tempId != null) result.tempId = tempId;
|
||||
if (publicKey != null) result.publicKey = publicKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
RecoveryRequest._();
|
||||
|
||||
factory RecoveryRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory RecoveryRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'RecoveryRequest',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..aInt64(1, _omitFieldNames ? '' : 'tempId')
|
||||
..a<$core.List<$core.int>>(
|
||||
2, _omitFieldNames ? '' : 'publicKey', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
RecoveryRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
RecoveryRequest copyWith(void Function(RecoveryRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as RecoveryRequest))
|
||||
as RecoveryRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RecoveryRequest create() => RecoveryRequest._();
|
||||
@$core.override
|
||||
RecoveryRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RecoveryRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<RecoveryRequest>(create);
|
||||
static RecoveryRequest? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get tempId => $_getI64(0);
|
||||
@$pb.TagNumber(1)
|
||||
set tempId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTempId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTempId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get publicKey => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set publicKey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasPublicKey() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearPublicKey() => $_clearField(2);
|
||||
}
|
||||
|
||||
/// Used as envelope for TrustedFriendShare and RecoveryData
|
||||
class EncryptedEnvelope extends $pb.GeneratedMessage {
|
||||
factory EncryptedEnvelope({
|
||||
$core.List<$core.int>? encryptedData,
|
||||
$core.List<$core.int>? iv,
|
||||
$core.List<$core.int>? mac,
|
||||
}) {
|
||||
final result = create();
|
||||
if (encryptedData != null) result.encryptedData = encryptedData;
|
||||
if (iv != null) result.iv = iv;
|
||||
if (mac != null) result.mac = mac;
|
||||
return result;
|
||||
}
|
||||
|
||||
EncryptedEnvelope._();
|
||||
|
||||
factory EncryptedEnvelope.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory EncryptedEnvelope.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EncryptedEnvelope',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'encryptedData', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
2, _omitFieldNames ? '' : 'iv', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
3, _omitFieldNames ? '' : 'mac', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedEnvelope clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedEnvelope copyWith(void Function(EncryptedEnvelope) updates) =>
|
||||
super.copyWith((message) => updates(message as EncryptedEnvelope))
|
||||
as EncryptedEnvelope;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedEnvelope create() => EncryptedEnvelope._();
|
||||
@$core.override
|
||||
EncryptedEnvelope createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedEnvelope getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<EncryptedEnvelope>(create);
|
||||
static EncryptedEnvelope? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get encryptedData => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set encryptedData($core.List<$core.int> value) => $_setBytes(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasEncryptedData() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearEncryptedData() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get iv => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set iv($core.List<$core.int> value) => $_setBytes(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasIv() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearIv() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get mac => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set mac($core.List<$core.int> value) => $_setBytes(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasMac() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearMac() => $_clearField(3);
|
||||
}
|
||||
|
||||
class TrustedFriendShare_User extends $pb.GeneratedMessage {
|
||||
factory TrustedFriendShare_User({
|
||||
$fixnum.Int64? userId,
|
||||
$core.String? displayName,
|
||||
$core.List<$core.int>? avatar,
|
||||
}) {
|
||||
final result = create();
|
||||
if (userId != null) result.userId = userId;
|
||||
if (displayName != null) result.displayName = displayName;
|
||||
if (avatar != null) result.avatar = avatar;
|
||||
return result;
|
||||
}
|
||||
|
||||
TrustedFriendShare_User._();
|
||||
|
||||
factory TrustedFriendShare_User.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory TrustedFriendShare_User.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'TrustedFriendShare.User',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..aInt64(1, _omitFieldNames ? '' : 'userId')
|
||||
..aOS(2, _omitFieldNames ? '' : 'displayName')
|
||||
..a<$core.List<$core.int>>(
|
||||
3, _omitFieldNames ? '' : 'avatar', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
TrustedFriendShare_User clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
TrustedFriendShare_User copyWith(
|
||||
void Function(TrustedFriendShare_User) updates) =>
|
||||
super.copyWith((message) => updates(message as TrustedFriendShare_User))
|
||||
as TrustedFriendShare_User;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TrustedFriendShare_User create() => TrustedFriendShare_User._();
|
||||
@$core.override
|
||||
TrustedFriendShare_User createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TrustedFriendShare_User getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<TrustedFriendShare_User>(create);
|
||||
static TrustedFriendShare_User? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get userId => $_getI64(0);
|
||||
@$pb.TagNumber(1)
|
||||
set userId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasUserId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearUserId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get displayName => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set displayName($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasDisplayName() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearDisplayName() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get avatar => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set avatar($core.List<$core.int> value) => $_setBytes(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasAvatar() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearAvatar() => $_clearField(3);
|
||||
}
|
||||
|
||||
/// Send from the trusted friend to
|
||||
/// This is encrypted with the received public key.
|
||||
class TrustedFriendShare extends $pb.GeneratedMessage {
|
||||
factory TrustedFriendShare({
|
||||
TrustedFriendShare_User? trustedFriend,
|
||||
TrustedFriendShare_User? shareUser,
|
||||
$core.int? threshold,
|
||||
$core.List<$core.int>? sharedSecretData,
|
||||
}) {
|
||||
final result = create();
|
||||
if (trustedFriend != null) result.trustedFriend = trustedFriend;
|
||||
if (shareUser != null) result.shareUser = shareUser;
|
||||
if (threshold != null) result.threshold = threshold;
|
||||
if (sharedSecretData != null) result.sharedSecretData = sharedSecretData;
|
||||
return result;
|
||||
}
|
||||
|
||||
TrustedFriendShare._();
|
||||
|
||||
factory TrustedFriendShare.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory TrustedFriendShare.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'TrustedFriendShare',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..aOM<TrustedFriendShare_User>(1, _omitFieldNames ? '' : 'trustedFriend',
|
||||
subBuilder: TrustedFriendShare_User.create)
|
||||
..aOM<TrustedFriendShare_User>(2, _omitFieldNames ? '' : 'shareUser',
|
||||
subBuilder: TrustedFriendShare_User.create)
|
||||
..aI(3, _omitFieldNames ? '' : 'threshold')
|
||||
..a<$core.List<$core.int>>(
|
||||
4, _omitFieldNames ? '' : 'sharedSecretData', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
TrustedFriendShare clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
TrustedFriendShare copyWith(void Function(TrustedFriendShare) updates) =>
|
||||
super.copyWith((message) => updates(message as TrustedFriendShare))
|
||||
as TrustedFriendShare;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TrustedFriendShare create() => TrustedFriendShare._();
|
||||
@$core.override
|
||||
TrustedFriendShare createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static TrustedFriendShare getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<TrustedFriendShare>(create);
|
||||
static TrustedFriendShare? _defaultInstance;
|
||||
|
||||
/// This allows to display the user which user has send him his recovery data.
|
||||
@$pb.TagNumber(1)
|
||||
TrustedFriendShare_User get trustedFriend => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set trustedFriend(TrustedFriendShare_User value) => $_setField(1, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTrustedFriend() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTrustedFriend() => $_clearField(1);
|
||||
@$pb.TagNumber(1)
|
||||
TrustedFriendShare_User ensureTrustedFriend() => $_ensure(0);
|
||||
|
||||
/// This allows to display the userdata, showing that he is recovering the correct person.
|
||||
@$pb.TagNumber(2)
|
||||
TrustedFriendShare_User get shareUser => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set shareUser(TrustedFriendShare_User value) => $_setField(2, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasShareUser() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearShareUser() => $_clearField(2);
|
||||
@$pb.TagNumber(2)
|
||||
TrustedFriendShare_User ensureShareUser() => $_ensure(1);
|
||||
|
||||
/// The minimum threshold required to decrypte the shares.
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get threshold => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set threshold($core.int value) => $_setSignedInt32(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasThreshold() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearThreshold() => $_clearField(3);
|
||||
|
||||
/// The actual share which will become: SharedSecretData
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<$core.int> get sharedSecretData => $_getN(3);
|
||||
@$pb.TagNumber(4)
|
||||
set sharedSecretData($core.List<$core.int> value) => $_setBytes(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasSharedSecretData() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearSharedSecretData() => $_clearField(4);
|
||||
}
|
||||
|
||||
/// The user identity keys. Can be used to restore the data backup.
|
||||
class RecoveryData extends $pb.GeneratedMessage {
|
||||
factory RecoveryData({
|
||||
$fixnum.Int64? userId,
|
||||
$core.List<$core.int>? keyManager,
|
||||
}) {
|
||||
final result = create();
|
||||
if (userId != null) result.userId = userId;
|
||||
if (keyManager != null) result.keyManager = keyManager;
|
||||
return result;
|
||||
}
|
||||
|
||||
RecoveryData._();
|
||||
|
||||
factory RecoveryData.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory RecoveryData.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'RecoveryData',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..aInt64(1, _omitFieldNames ? '' : 'userId')
|
||||
..a<$core.List<$core.int>>(
|
||||
3, _omitFieldNames ? '' : 'keyManager', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
RecoveryData clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
RecoveryData copyWith(void Function(RecoveryData) updates) =>
|
||||
super.copyWith((message) => updates(message as RecoveryData))
|
||||
as RecoveryData;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RecoveryData create() => RecoveryData._();
|
||||
@$core.override
|
||||
RecoveryData createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static RecoveryData getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<RecoveryData>(create);
|
||||
static RecoveryData? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get userId => $_getI64(0);
|
||||
@$pb.TagNumber(1)
|
||||
set userId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasUserId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearUserId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get keyManager => $_getN(1);
|
||||
@$pb.TagNumber(3)
|
||||
set keyManager($core.List<$core.int> value) => $_setBytes(1, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasKeyManager() => $_has(1);
|
||||
@$pb.TagNumber(3)
|
||||
void clearKeyManager() => $_clearField(3);
|
||||
}
|
||||
|
||||
/// After received all shares this is decrypted by the user restoring its own
|
||||
class SharedSecretData extends $pb.GeneratedMessage {
|
||||
factory SharedSecretData({
|
||||
$core.List<$core.int>? recoveryData,
|
||||
$core.List<$core.int>? pinSeed,
|
||||
$core.List<$core.int>? pinUnlockToken,
|
||||
$core.String? emailHint,
|
||||
$core.List<$core.int>? encryptedServerKeyNonce,
|
||||
$core.List<$core.int>? encryptedServerKeyMac,
|
||||
}) {
|
||||
final result = create();
|
||||
if (recoveryData != null) result.recoveryData = recoveryData;
|
||||
if (pinSeed != null) result.pinSeed = pinSeed;
|
||||
if (pinUnlockToken != null) result.pinUnlockToken = pinUnlockToken;
|
||||
if (emailHint != null) result.emailHint = emailHint;
|
||||
if (encryptedServerKeyNonce != null)
|
||||
result.encryptedServerKeyNonce = encryptedServerKeyNonce;
|
||||
if (encryptedServerKeyMac != null)
|
||||
result.encryptedServerKeyMac = encryptedServerKeyMac;
|
||||
return result;
|
||||
}
|
||||
|
||||
SharedSecretData._();
|
||||
|
||||
factory SharedSecretData.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory SharedSecretData.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'SharedSecretData',
|
||||
package: const $pb.PackageName(
|
||||
_omitMessageNames ? '' : 'passwordless_recovery'),
|
||||
createEmptyInstance: create)
|
||||
..a<$core.List<$core.int>>(
|
||||
1, _omitFieldNames ? '' : 'recoveryData', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
2, _omitFieldNames ? '' : 'pinSeed', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
3, _omitFieldNames ? '' : 'pinUnlockToken', $pb.PbFieldType.OY)
|
||||
..aOS(4, _omitFieldNames ? '' : 'emailHint')
|
||||
..a<$core.List<$core.int>>(
|
||||
5, _omitFieldNames ? '' : 'encryptedServerKeyNonce', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
6, _omitFieldNames ? '' : 'encryptedServerKeyMac', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
SharedSecretData clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
SharedSecretData copyWith(void Function(SharedSecretData) updates) =>
|
||||
super.copyWith((message) => updates(message as SharedSecretData))
|
||||
as SharedSecretData;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SharedSecretData create() => SharedSecretData._();
|
||||
@$core.override
|
||||
SharedSecretData createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static SharedSecretData getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<SharedSecretData>(create);
|
||||
static SharedSecretData? _defaultInstance;
|
||||
|
||||
/// The recovery data is encrypted in case a second factor was chosen.
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.int> get recoveryData => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set recoveryData($core.List<$core.int> value) => $_setBytes(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasRecoveryData() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearRecoveryData() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get pinSeed => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set pinSeed($core.List<$core.int> value) => $_setBytes(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasPinSeed() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearPinSeed() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get pinUnlockToken => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set pinUnlockToken($core.List<$core.int> value) => $_setBytes(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasPinUnlockToken() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearPinUnlockToken() => $_clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.String get emailHint => $_getSZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set emailHint($core.String value) => $_setString(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasEmailHint() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearEmailHint() => $_clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.List<$core.int> get encryptedServerKeyNonce => $_getN(4);
|
||||
@$pb.TagNumber(5)
|
||||
set encryptedServerKeyNonce($core.List<$core.int> value) =>
|
||||
$_setBytes(4, value);
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasEncryptedServerKeyNonce() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearEncryptedServerKeyNonce() => $_clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
$core.List<$core.int> get encryptedServerKeyMac => $_getN(5);
|
||||
@$pb.TagNumber(6)
|
||||
set encryptedServerKeyMac($core.List<$core.int> value) =>
|
||||
$_setBytes(5, value);
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasEncryptedServerKeyMac() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearEncryptedServerKeyMac() => $_clearField(6);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const $core.bool _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from passwordless_recovery.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// This is a generated file - do not edit.
|
||||
//
|
||||
// Generated from passwordless_recovery.proto.
|
||||
|
||||
// @dart = 3.3
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_relative_imports
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:convert' as $convert;
|
||||
import 'dart:core' as $core;
|
||||
import 'dart:typed_data' as $typed_data;
|
||||
|
||||
@$core.Deprecated('Use recoveryRequestDescriptor instead')
|
||||
const RecoveryRequest$json = {
|
||||
'1': 'RecoveryRequest',
|
||||
'2': [
|
||||
{'1': 'temp_id', '3': 1, '4': 1, '5': 3, '10': 'tempId'},
|
||||
{'1': 'public_key', '3': 2, '4': 1, '5': 12, '10': 'publicKey'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RecoveryRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List recoveryRequestDescriptor = $convert.base64Decode(
|
||||
'Cg9SZWNvdmVyeVJlcXVlc3QSFwoHdGVtcF9pZBgBIAEoA1IGdGVtcElkEh0KCnB1YmxpY19rZX'
|
||||
'kYAiABKAxSCXB1YmxpY0tleQ==');
|
||||
|
||||
@$core.Deprecated('Use encryptedEnvelopeDescriptor instead')
|
||||
const EncryptedEnvelope$json = {
|
||||
'1': 'EncryptedEnvelope',
|
||||
'2': [
|
||||
{'1': 'encrypted_data', '3': 1, '4': 1, '5': 12, '10': 'encryptedData'},
|
||||
{'1': 'iv', '3': 2, '4': 1, '5': 12, '10': 'iv'},
|
||||
{'1': 'mac', '3': 3, '4': 1, '5': 12, '10': 'mac'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `EncryptedEnvelope`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List encryptedEnvelopeDescriptor = $convert.base64Decode(
|
||||
'ChFFbmNyeXB0ZWRFbnZlbG9wZRIlCg5lbmNyeXB0ZWRfZGF0YRgBIAEoDFINZW5jcnlwdGVkRG'
|
||||
'F0YRIOCgJpdhgCIAEoDFICaXYSEAoDbWFjGAMgASgMUgNtYWM=');
|
||||
|
||||
@$core.Deprecated('Use trustedFriendShareDescriptor instead')
|
||||
const TrustedFriendShare$json = {
|
||||
'1': 'TrustedFriendShare',
|
||||
'2': [
|
||||
{
|
||||
'1': 'trusted_friend',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.passwordless_recovery.TrustedFriendShare.User',
|
||||
'10': 'trustedFriend'
|
||||
},
|
||||
{
|
||||
'1': 'share_user',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.passwordless_recovery.TrustedFriendShare.User',
|
||||
'10': 'shareUser'
|
||||
},
|
||||
{'1': 'threshold', '3': 3, '4': 1, '5': 5, '10': 'threshold'},
|
||||
{
|
||||
'1': 'shared_secret_data',
|
||||
'3': 4,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'10': 'sharedSecretData'
|
||||
},
|
||||
],
|
||||
'3': [TrustedFriendShare_User$json],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use trustedFriendShareDescriptor instead')
|
||||
const TrustedFriendShare_User$json = {
|
||||
'1': 'User',
|
||||
'2': [
|
||||
{'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'},
|
||||
{'1': 'display_name', '3': 2, '4': 1, '5': 9, '10': 'displayName'},
|
||||
{'1': 'avatar', '3': 3, '4': 1, '5': 12, '10': 'avatar'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `TrustedFriendShare`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List trustedFriendShareDescriptor = $convert.base64Decode(
|
||||
'ChJUcnVzdGVkRnJpZW5kU2hhcmUSVQoOdHJ1c3RlZF9mcmllbmQYASABKAsyLi5wYXNzd29yZG'
|
||||
'xlc3NfcmVjb3ZlcnkuVHJ1c3RlZEZyaWVuZFNoYXJlLlVzZXJSDXRydXN0ZWRGcmllbmQSTQoK'
|
||||
'c2hhcmVfdXNlchgCIAEoCzIuLnBhc3N3b3JkbGVzc19yZWNvdmVyeS5UcnVzdGVkRnJpZW5kU2'
|
||||
'hhcmUuVXNlclIJc2hhcmVVc2VyEhwKCXRocmVzaG9sZBgDIAEoBVIJdGhyZXNob2xkEiwKEnNo'
|
||||
'YXJlZF9zZWNyZXRfZGF0YRgEIAEoDFIQc2hhcmVkU2VjcmV0RGF0YRpaCgRVc2VyEhcKB3VzZX'
|
||||
'JfaWQYASABKANSBnVzZXJJZBIhCgxkaXNwbGF5X25hbWUYAiABKAlSC2Rpc3BsYXlOYW1lEhYK'
|
||||
'BmF2YXRhchgDIAEoDFIGYXZhdGFy');
|
||||
|
||||
@$core.Deprecated('Use recoveryDataDescriptor instead')
|
||||
const RecoveryData$json = {
|
||||
'1': 'RecoveryData',
|
||||
'2': [
|
||||
{'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'},
|
||||
{'1': 'key_manager', '3': 3, '4': 1, '5': 12, '10': 'keyManager'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `RecoveryData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List recoveryDataDescriptor = $convert.base64Decode(
|
||||
'CgxSZWNvdmVyeURhdGESFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEh8KC2tleV9tYW5hZ2VyGA'
|
||||
'MgASgMUgprZXlNYW5hZ2Vy');
|
||||
|
||||
@$core.Deprecated('Use sharedSecretDataDescriptor instead')
|
||||
const SharedSecretData$json = {
|
||||
'1': 'SharedSecretData',
|
||||
'2': [
|
||||
{'1': 'recovery_data', '3': 1, '4': 1, '5': 12, '10': 'recoveryData'},
|
||||
{
|
||||
'1': 'pin_seed',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 0,
|
||||
'10': 'pinSeed',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'pin_unlock_token',
|
||||
'3': 3,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 1,
|
||||
'10': 'pinUnlockToken',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'email_hint',
|
||||
'3': 4,
|
||||
'4': 1,
|
||||
'5': 9,
|
||||
'9': 2,
|
||||
'10': 'emailHint',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'encrypted_server_key_nonce',
|
||||
'3': 5,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 3,
|
||||
'10': 'encryptedServerKeyNonce',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'encrypted_server_key_mac',
|
||||
'3': 6,
|
||||
'4': 1,
|
||||
'5': 12,
|
||||
'9': 4,
|
||||
'10': 'encryptedServerKeyMac',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_pin_seed'},
|
||||
{'1': '_pin_unlock_token'},
|
||||
{'1': '_email_hint'},
|
||||
{'1': '_encrypted_server_key_nonce'},
|
||||
{'1': '_encrypted_server_key_mac'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `SharedSecretData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List sharedSecretDataDescriptor = $convert.base64Decode(
|
||||
'ChBTaGFyZWRTZWNyZXREYXRhEiMKDXJlY292ZXJ5X2RhdGEYASABKAxSDHJlY292ZXJ5RGF0YR'
|
||||
'IeCghwaW5fc2VlZBgCIAEoDEgAUgdwaW5TZWVkiAEBEi0KEHBpbl91bmxvY2tfdG9rZW4YAyAB'
|
||||
'KAxIAVIOcGluVW5sb2NrVG9rZW6IAQESIgoKZW1haWxfaGludBgEIAEoCUgCUgllbWFpbEhpbn'
|
||||
'SIAQESQAoaZW5jcnlwdGVkX3NlcnZlcl9rZXlfbm9uY2UYBSABKAxIA1IXZW5jcnlwdGVkU2Vy'
|
||||
'dmVyS2V5Tm9uY2WIAQESPAoYZW5jcnlwdGVkX3NlcnZlcl9rZXlfbWFjGAYgASgMSARSFWVuY3'
|
||||
'J5cHRlZFNlcnZlcktleU1hY4gBAUILCglfcGluX3NlZWRCEwoRX3Bpbl91bmxvY2tfdG9rZW5C'
|
||||
'DQoLX2VtYWlsX2hpbnRCHQobX2VuY3J5cHRlZF9zZXJ2ZXJfa2V5X25vbmNlQhsKGV9lbmNyeX'
|
||||
'B0ZWRfc2VydmVyX2tleV9tYWM=');
|
||||
|
|
@ -58,6 +58,8 @@ message EncryptedContent {
|
|||
optional UserDiscoveryRequest user_discovery_request = 22;
|
||||
optional UserDiscoveryUpdate user_discovery_update = 23;
|
||||
optional KeyVerificationProof key_verification_proof = 24;
|
||||
optional PasswordLessRecovery passwordless_recovery = 26;
|
||||
optional PasswordLessRecoveryHeartbeat passwordless_recovery_heartbeat = 27;
|
||||
|
||||
message ErrorMessages {
|
||||
enum Type {
|
||||
|
|
@ -82,9 +84,7 @@ message EncryptedContent {
|
|||
bytes group_public_key = 1;
|
||||
}
|
||||
|
||||
message ResendGroupPublicKey {
|
||||
|
||||
}
|
||||
message ResendGroupPublicKey {}
|
||||
|
||||
message GroupUpdate {
|
||||
string group_action_type = 1; // GroupActionType.name
|
||||
|
|
@ -217,4 +217,13 @@ message EncryptedContent {
|
|||
bytes calculated_mac = 1;
|
||||
}
|
||||
|
||||
message PasswordLessRecovery {
|
||||
optional bytes recoverySecretShare = 1;
|
||||
bool delete = 2;
|
||||
}
|
||||
|
||||
message PasswordLessRecoveryHeartbeat {
|
||||
bytes hash = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -46,40 +46,24 @@ message TrustedFriendShare {
|
|||
}
|
||||
}
|
||||
|
||||
// After received all shares this is decrypted by the user restoring its own
|
||||
message SharedSecretData {
|
||||
|
||||
// No second factor was selected
|
||||
optional RecoveryData recovery_data = 1;
|
||||
|
||||
|
||||
// Server has
|
||||
optional SecondFactorMail second_factor_mail = 2;
|
||||
optional SecondFactorPin second_factor_pin = 3;
|
||||
|
||||
// The recovery data in case a second factor was used
|
||||
// The decryption key is loaded from the server either using the PIN or the MAIL
|
||||
optional bytes recovery_data_encrypted = 4;
|
||||
|
||||
|
||||
message SecondFactorPin {
|
||||
// Required to try the PIN to get the share from the server.
|
||||
// This prevents that someone else can lock the pin, as the server only
|
||||
// allows 3 tries then after 1 day again 3 tries until the key is deleted.
|
||||
bytes unlock_token = 1;
|
||||
// This never is send to the server but used to hash the pin before sending it to the server.
|
||||
// This prevents that the server every knows the shot 4-diget PIN.
|
||||
bytes pin_seed = 2;
|
||||
}
|
||||
|
||||
message SecondFactorMail {}
|
||||
|
||||
}
|
||||
|
||||
// The data which is recovered at the end.
|
||||
// The backup_master_key allows to recover the actual backup uploaded in the background to the server.
|
||||
// In case the backup is not available any more the user can use its user_id and his private_key to requister as a new user.
|
||||
// The user identity keys. Can be used to restore the data backup.
|
||||
message RecoveryData {
|
||||
int64 user_id = 1;
|
||||
bytes key_manager = 3;
|
||||
}
|
||||
|
||||
// After received all shares this is decrypted by the user restoring its own
|
||||
message SharedSecretData {
|
||||
|
||||
// The recovery data is encrypted in case a second factor was chosen.
|
||||
bytes recovery_data = 1;
|
||||
|
||||
optional bytes pin_seed = 2;
|
||||
optional bytes pin_unlock_token = 3;
|
||||
|
||||
optional string email_hint = 4;
|
||||
optional bytes encrypted_server_key_nonce = 5;
|
||||
optional bytes encrypted_server_key_mac = 6;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -758,6 +758,22 @@ class ApiService {
|
|||
return sendRequestSync(req, contactId: userId.toInt());
|
||||
}
|
||||
|
||||
Future<Result> registerPasswordLessRecovery(
|
||||
List<int> encryptedServerKey,
|
||||
List<int>? pinUnlockToken,
|
||||
) async {
|
||||
final req = createClientToServerFromApplicationData(
|
||||
ApplicationData(
|
||||
registerPasswordlessRecovery:
|
||||
ApplicationData_RegisterPasswordLessRecovery(
|
||||
encryptedServerKey: encryptedServerKey,
|
||||
pinUnlockToken: pinUnlockToken,
|
||||
),
|
||||
),
|
||||
);
|
||||
return sendRequestSync(req);
|
||||
}
|
||||
|
||||
Future<Result> addAdditionalUser(Int64 userId) async {
|
||||
final get = ApplicationData_AddAdditionalUser()..userId = userId;
|
||||
final appData = ApplicationData()..addAdditionalUser = get;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import 'package:twonly/src/services/group.service.dart';
|
|||
import 'package:twonly/src/services/key_verification.service.dart';
|
||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||
import 'package:twonly/src/services/notifications/fcm.notifications.dart';
|
||||
import 'package:twonly/src/services/passwordless_recovery.service.dart';
|
||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -370,6 +371,24 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
return (null, null);
|
||||
}
|
||||
|
||||
if (content.hasPasswordlessRecovery()) {
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecovery(
|
||||
fromUserId,
|
||||
content.passwordlessRecovery,
|
||||
receiptId,
|
||||
);
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
if (content.hasPasswordlessRecoveryHeartbeat()) {
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat(
|
||||
fromUserId,
|
||||
content.passwordlessRecoveryHeartbeat,
|
||||
receiptId,
|
||||
);
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
if (content.hasContactUpdate()) {
|
||||
await handleContactUpdate(
|
||||
fromUserId,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert' show base64Encode, utf8;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert' show utf8;
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:crypto/crypto.dart' hide Hmac;
|
||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart'
|
||||
show FlutterChacha20;
|
||||
import 'package:cryptography_plus/cryptography_plus.dart' show SecretKey;
|
||||
import 'package:hashlib/hashlib.dart' show Scrypt;
|
||||
import 'package:cryptography_plus/cryptography_plus.dart' show Hmac, SecretKey;
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:twonly/core/bridge/wrapper.dart';
|
||||
import 'package:twonly/core/bridge/wrapper/key_manager.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/json/userdata.model.dart'
|
||||
show PasswordLessRecovery;
|
||||
import 'package:twonly/src/model/protobuf/client/generated/passwordless_recovery/types.pb.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||
as pb;
|
||||
import 'package:twonly/src/model/protobuf/client/generated/passwordless_recovery.pb.dart';
|
||||
import 'package:twonly/src/services/api/messages.api.dart';
|
||||
import 'package:twonly/src/services/user.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
|
|
@ -22,94 +32,352 @@ class PasswordlessRecoveryService {
|
|||
required String secondFactorValue,
|
||||
required int threshold,
|
||||
}) async {
|
||||
await twonlyDB.contactsDao.resetRecoveryDataForAllContacts();
|
||||
// 1. Get all currently trusted friends contacts and send them delete messages
|
||||
final oldTrustedFriends = await (twonlyDB.select(
|
||||
twonlyDB.contacts,
|
||||
)..where((t) => t.recoveryIsTrustedFriend.equals(true))).get();
|
||||
|
||||
// 2. Update the user model configuration
|
||||
for (final contact in oldTrustedFriends) {
|
||||
try {
|
||||
await sendCipherText(
|
||||
contact.userId,
|
||||
pb.EncryptedContent(
|
||||
passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery(
|
||||
delete: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error(
|
||||
'Failed to send delete PasswordLessRecovery message to contact ${contact.userId}: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Reset current recovery data to ensure a clean state
|
||||
|
||||
await twonlyDB.contactsDao.resetRecoveryDataForAllContacts();
|
||||
await UserService.update((u) => u.passwordLessRecovery = null);
|
||||
|
||||
final config = PasswordLessRecovery();
|
||||
final chacha20 = FlutterChacha20.poly1305Aead();
|
||||
|
||||
final serverKey = getRandomUint8List(32);
|
||||
Uint8List? protectedEmailServerKey;
|
||||
Uint8List? pinUnlockToken;
|
||||
Uint8List? protectedPin;
|
||||
// 2. If enabled, handle the second factor and create serverKey
|
||||
|
||||
Uint8List? serverKey;
|
||||
Uint8List? encryptedServerKey;
|
||||
|
||||
SecretKey? secondFactorEncryptedServerKeyKey;
|
||||
String? emailHint;
|
||||
|
||||
switch (secondFactorType) {
|
||||
case SecondFactorType.email:
|
||||
final emailSeed = getRandomUint8List(32);
|
||||
|
||||
// Store it, so the user can see it again.
|
||||
config.email = secondFactorValue;
|
||||
|
||||
final chacha20 = FlutterChacha20.poly1305Aead();
|
||||
|
||||
final scrypt = Scrypt(
|
||||
cost: 65536,
|
||||
salt: emailSeed,
|
||||
);
|
||||
|
||||
final protectedEmailKey = scrypt
|
||||
.convert(utf8.encode(secondFactorValue))
|
||||
.bytes;
|
||||
|
||||
// there must be a emailSeed like for the pin...
|
||||
|
||||
// The serverKey is encrypted with the email; this protects the server from seeing the user's email address, while
|
||||
// also ensuring that the server can only send the real secret to the user's configured email, as a different
|
||||
// email will result in a different secret key.
|
||||
final secretBox = await chacha20.encrypt(
|
||||
serverKey,
|
||||
secretKey: SecretKey(
|
||||
Uint8List.fromList(protectedEmailKey),
|
||||
),
|
||||
nonce: chacha20.newNonce(),
|
||||
);
|
||||
// This is only stored so the user can see there email, and verify that he has set the valid mail...
|
||||
config.email = secondFactorValue;
|
||||
emailHint = createEmailHint(secondFactorValue);
|
||||
|
||||
protectedEmailServerKey = EncryptedEnvelope(
|
||||
encryptedData: secretBox.cipherText,
|
||||
// iv: secretBox.nonce,
|
||||
mac: secretBox.mac.bytes,
|
||||
).writeToBuffer();
|
||||
// E-Mail Protection:
|
||||
// - Server can only learn the email during recovery. Ensured as the server gets the NONCE and MAC to decrpyt during recovery.
|
||||
// - Trusted-friends: Server key is send to the mail, they whould need access to the user's mail account.
|
||||
secondFactorEncryptedServerKeyKey = SecretKey(
|
||||
Uint8List.fromList(utf8.encode(config.email!)),
|
||||
);
|
||||
|
||||
case SecondFactorType.pin:
|
||||
final pinSeed = getRandomUint8List(32);
|
||||
pinUnlockToken = getRandomUint8List(32);
|
||||
|
||||
// The pin seed is to ensure that the server does never learns the real 4-digit pin. The seed is never send to
|
||||
// the server. As the pin are heavily protected against brute-forcing and will be discared by the server after X
|
||||
// tries, this prevents that a malicous user trigger this deletion...
|
||||
config.pinSeed = base64Encode(pinSeed);
|
||||
config.pinUnlockToken = base64Encode(pinUnlockToken);
|
||||
// The pin seed - never shared with the server - ensures that the server is unable to brute-force real user's pin.
|
||||
config.pinSeed = getRandomUint8List(32);
|
||||
|
||||
final scrypt = Scrypt(
|
||||
cost: 65536,
|
||||
salt: pinSeed,
|
||||
// As the pin is heavily protected against brute-forcing e.g. will be deleted by the server after X tries, the
|
||||
// unlock token is required to prevent a malicous user (except the trusted friends) to triger this deletion.
|
||||
config.pinUnlockToken = getRandomUint8List(32);
|
||||
|
||||
// Brute-force protection for the user's pin:
|
||||
// - Server: Does not know the seed.
|
||||
// - Trusted friends: Can only check the result X times before the server deletes the key.
|
||||
final pinProtectionKey = await Hmac.sha256().calculateMac(
|
||||
Uint8List.fromList(utf8.encode(secondFactorValue)),
|
||||
secretKey: SecretKey(config.pinSeed!),
|
||||
);
|
||||
|
||||
final key = scrypt.convert(utf8.encode(secondFactorValue)).bytes;
|
||||
protectedPin = key.sublist(0, 32);
|
||||
// To restore the user has to provide the server with this encryption key. The server then can verify the
|
||||
// correct pin was entered, when decrypting the server key as he also receives the mac and nonce from the user
|
||||
// during recovery. Only when the mac is correct the server provides the user with the serverKey.
|
||||
secondFactorEncryptedServerKeyKey = SecretKey(pinProtectionKey.bytes);
|
||||
|
||||
case SecondFactorType.none:
|
||||
}
|
||||
|
||||
// 3. Use the second factor and generate the shares...
|
||||
if (secondFactorEncryptedServerKeyKey != null) {
|
||||
// The server key is used to encrypt the RecoveryData of the users. This ensures that when the trusted friends
|
||||
// colaberate, they additional need the serverKey to decrypt the user's key.
|
||||
serverKey = getRandomUint8List(32);
|
||||
|
||||
// Generate the shares, and store them in the contacrs.table.
|
||||
final secretBox = await chacha20.encrypt(
|
||||
serverKey,
|
||||
secretKey: secondFactorEncryptedServerKeyKey,
|
||||
nonce: chacha20.newNonce(),
|
||||
);
|
||||
|
||||
Log.info('Enabling passwordless recovery with:');
|
||||
Log.info(' - Trusted Friends: $trustedFriendIds');
|
||||
Log.info(' - Second Factor Type: $secondFactorType');
|
||||
Log.info(' - Second Factor Value: $secondFactorValue');
|
||||
Log.info(' - Threshold: $threshold');
|
||||
// This is send to the server and then deleted localy
|
||||
encryptedServerKey = Uint8List.fromList(secretBox.cipherText);
|
||||
|
||||
// Use the serverKey to protect the recovery_data_encrypted
|
||||
// Because the server does not know the nonce and the mac, he is unable to link a email to the user and also
|
||||
// unable to brute-force the pin.
|
||||
config
|
||||
..encryptedServerKeyNonce = secretBox.nonce
|
||||
..encryptedServerKey = encryptedServerKey
|
||||
..encryptedServerKeyMac = secretBox.mac.bytes;
|
||||
}
|
||||
|
||||
// 4. Send the data to the server:
|
||||
// 3. Using shamir's secret to generate the shares for the users.
|
||||
|
||||
// to th server
|
||||
// protectedPin, pinUnlockToken, protectedEmailServerKey;
|
||||
// 3.1. Create the SharedSecretData
|
||||
|
||||
// 5. send to the contacts / implement the heartbeat check, and notify the user in case the hearbeath shows that to few users are active...
|
||||
var recoveryData = RecoveryData(
|
||||
userId: Int64(userService.currentUser.userId),
|
||||
keyManager: await RustKeyManager.serialize(),
|
||||
).writeToBuffer();
|
||||
|
||||
if (serverKey != null) {
|
||||
// Second factor was enabled, so encrypt the recoveryData using the serverKey.
|
||||
|
||||
final secretBox = await chacha20.encrypt(
|
||||
recoveryData,
|
||||
secretKey: SecretKey(serverKey),
|
||||
nonce: chacha20.newNonce(),
|
||||
);
|
||||
|
||||
recoveryData = EncryptedEnvelope(
|
||||
encryptedData: secretBox.cipherText,
|
||||
iv: secretBox.nonce,
|
||||
mac: secretBox.mac.bytes,
|
||||
).writeToBuffer();
|
||||
}
|
||||
|
||||
final sharedSecretData = SharedSecretData(
|
||||
recoveryData: recoveryData,
|
||||
pinSeed: config.pinSeed,
|
||||
pinUnlockToken: config.pinUnlockToken,
|
||||
emailHint: emailHint,
|
||||
encryptedServerKeyMac: config.encryptedServerKeyMac,
|
||||
encryptedServerKeyNonce: config.encryptedServerKeyNonce,
|
||||
).writeToBuffer();
|
||||
|
||||
// 3.2. Use the amount of trusted friends to generate the shares
|
||||
|
||||
final List<Uint8List> shares;
|
||||
try {
|
||||
shares = await RustUtils.generateShares(
|
||||
secret: sharedSecretData,
|
||||
total: trustedFriendIds.length,
|
||||
threshold: threshold,
|
||||
);
|
||||
|
||||
if (shares.length != trustedFriendIds.length) {
|
||||
Log.error('shares.length != trustedFriendIds.length');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
Log.error('Failed to generate secret shares: $e');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3.4. Store the shares in the contact's rows
|
||||
for (final contactId in trustedFriendIds) {
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
contactId,
|
||||
ContactsCompanion(
|
||||
recoveryIsTrustedFriend: const Value(true),
|
||||
recoveryLastHeartbeat: const Value(null),
|
||||
recoverySecretShare: Value(shares.removeLast()),
|
||||
),
|
||||
);
|
||||
}
|
||||
// The passwordless is configured sucessfully.
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<void> performHeartbeat() async {
|
||||
final config = userService.currentUser.passwordLessRecovery;
|
||||
|
||||
if (config != null) {
|
||||
final lastHeartbeat = config.lastServerHeartbeat;
|
||||
final isOlderThanAMonth =
|
||||
lastHeartbeat != null &&
|
||||
clock.now().difference(lastHeartbeat).inDays > 30;
|
||||
|
||||
if ((lastHeartbeat == null || isOlderThanAMonth) &&
|
||||
config.encryptedServerKey != null) {
|
||||
final res = await apiService.registerPasswordLessRecovery(
|
||||
config.encryptedServerKey!,
|
||||
config.pinUnlockToken,
|
||||
);
|
||||
|
||||
if (res.isSuccess) {
|
||||
await UserService.update((u) {
|
||||
u.passwordLessRecovery?.lastServerHeartbeat = clock.now();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get all contacts where recoveryLastHeartbeat is NULL. Then for each contacts send
|
||||
final pendingShares =
|
||||
await (twonlyDB.select(twonlyDB.contacts)..where(
|
||||
(t) =>
|
||||
t.recoveryIsTrustedFriend.equals(true) &
|
||||
t.recoveryLastHeartbeat.isNull() &
|
||||
t.recoverySecretShare.isNotNull(),
|
||||
))
|
||||
.get();
|
||||
|
||||
for (final contact in pendingShares) {
|
||||
try {
|
||||
await sendCipherText(
|
||||
contact.userId,
|
||||
pb.EncryptedContent(
|
||||
passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery(
|
||||
recoverySecretShare: contact.recoverySecretShare,
|
||||
delete: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
contact.userId,
|
||||
ContactsCompanion(
|
||||
recoveryLastHeartbeat: Value(clock.now()),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error(
|
||||
'Failed to send PasswordLessRecovery share to contact ${contact.userId}: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send heartbeat to the friends I am a trusted friend.
|
||||
final oneWeekAgo = clock.now().subtract(const Duration(days: 7));
|
||||
final trustedFriendsToNotify =
|
||||
await (twonlyDB.select(twonlyDB.contacts)..where(
|
||||
(t) =>
|
||||
t.recoveryContactsSecretShare.isNotNull() &
|
||||
(t.recoveryContactsLastHeartbeat.isNull() |
|
||||
t.recoveryContactsLastHeartbeat.isSmallerThanValue(
|
||||
oneWeekAgo,
|
||||
)),
|
||||
))
|
||||
.get();
|
||||
|
||||
for (final contact in trustedFriendsToNotify) {
|
||||
try {
|
||||
final share = contact.recoveryContactsSecretShare!;
|
||||
final hash = sha256.convert(share).bytes;
|
||||
|
||||
await sendCipherText(
|
||||
contact.userId,
|
||||
pb.EncryptedContent(
|
||||
passwordlessRecoveryHeartbeat:
|
||||
pb.EncryptedContent_PasswordLessRecoveryHeartbeat(
|
||||
hash: hash,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
contact.userId,
|
||||
ContactsCompanion(
|
||||
recoveryContactsLastHeartbeat: Value(clock.now()),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error(
|
||||
'Failed to send PasswordLessRecoveryHeartbeat to contact ${contact.userId}: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> handlePasswordlessRecovery(
|
||||
int fromUserId,
|
||||
pb.EncryptedContent_PasswordLessRecovery msg,
|
||||
String receiptId,
|
||||
) async {
|
||||
if (msg.delete) {
|
||||
Log.info(
|
||||
'[$receiptId] Received request to delete passwordless recovery share from contact $fromUserId',
|
||||
);
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
fromUserId,
|
||||
const ContactsCompanion(
|
||||
recoveryContactsSecretShare: Value(null),
|
||||
recoveryContactsLastHeartbeat: Value(null),
|
||||
),
|
||||
);
|
||||
} else if (msg.hasRecoverySecretShare()) {
|
||||
Log.info(
|
||||
'[$receiptId] Received new passwordless recovery share from contact $fromUserId',
|
||||
);
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
fromUserId,
|
||||
ContactsCompanion(
|
||||
recoveryContactsSecretShare: Value(
|
||||
Uint8List.fromList(msg.recoverySecretShare),
|
||||
),
|
||||
recoveryContactsLastHeartbeat: const Value(
|
||||
null, // this will trigger that a heartbeat will be send...
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> handlePasswordlessRecoveryHeartbeat(
|
||||
int fromUserId,
|
||||
pb.EncryptedContent_PasswordLessRecoveryHeartbeat msg,
|
||||
String receiptId,
|
||||
) async {
|
||||
Log.info(
|
||||
'[$receiptId] Received passwordless recovery heartbeat from contact $fromUserId',
|
||||
);
|
||||
final contact = await twonlyDB.contactsDao.getContactById(fromUserId);
|
||||
final storedShare = contact?.recoverySecretShare;
|
||||
|
||||
if (storedShare == null) {
|
||||
unawaited(
|
||||
sendCipherText(
|
||||
fromUserId,
|
||||
pb.EncryptedContent(
|
||||
passwordlessRecovery: pb.EncryptedContent_PasswordLessRecovery(
|
||||
delete: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
Log.warn(
|
||||
'[$receiptId] Received passwordless recovery heartbeat from $fromUserId but we did not send him a secret share.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final computedHash = sha256.convert(storedShare).bytes;
|
||||
final recoveryLastHeartbeat =
|
||||
const ListEquality().equals(computedHash, msg.hash)
|
||||
? clock.now()
|
||||
: null; // The stored share not valid (maybe a old backup was restored). This will cause the performHeartbeat to resend him his share
|
||||
Log.info(
|
||||
'[$receiptId] Got heartbeat: ($recoveryLastHeartbeat)',
|
||||
);
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
fromUserId,
|
||||
ContactsCompanion(
|
||||
recoveryLastHeartbeat: Value(recoveryLastHeartbeat),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -405,3 +405,26 @@ String joinWithAnd(List<String> items, String andWord) {
|
|||
if (items.length == 1) return items.first;
|
||||
return '${items.sublist(0, items.length - 1).join(', ')} $andWord ${items.last}';
|
||||
}
|
||||
|
||||
String createEmailHint(String email) {
|
||||
final parts = email.split('@');
|
||||
if (parts.length != 2) return email;
|
||||
|
||||
final local = parts[0];
|
||||
final domain = parts[1];
|
||||
|
||||
final localMasked = local.length > 2
|
||||
? '${local[0]}${'*' * (local.length - 2)}${local[local.length - 1]}'
|
||||
: local;
|
||||
|
||||
final domainParts = domain.split('.');
|
||||
if (domainParts.isEmpty) return '$localMasked@$domain';
|
||||
|
||||
final domainName = domainParts[0];
|
||||
final domainNameMasked = domainName.length > 1
|
||||
? '${domainName[0]}${'*' * (domainName.length - 1)}'
|
||||
: domainName;
|
||||
|
||||
final restDomain = domainParts.skip(1).join('.');
|
||||
return '$localMasked@$domainNameMasked${restDomain.isNotEmpty ? '.$restDomain' : ''}';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::io::Result;
|
||||
fn main() -> Result<()> {
|
||||
prost_build::compile_protos(&["src/user_discovery/types.proto"], &["src/"])?;
|
||||
prost_build::compile_protos(&["src/passwordless_recovery/types.proto"], &["src/"])?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,4 +108,12 @@ impl RustKeyManager {
|
|||
crate::keys::KeyManager::remove_from_keychain(&ctx.secure_storage)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serialize the key_manager. Needed for the passwordless_recovery feature.
|
||||
pub async fn serialize() -> Result<Vec<u8>> {
|
||||
let ctx = get_twonly_flutter()?;
|
||||
let key_manager = ctx.key_manager.lock().await;
|
||||
let serialized_bytes = postcard::to_allocvec(&*key_manager)?;
|
||||
Ok(serialized_bytes)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,29 @@
|
|||
pub mod backup;
|
||||
pub mod key_manager;
|
||||
pub mod user_discovery;
|
||||
|
||||
use crate::error::Result;
|
||||
use blahaj::{Share, Sharks};
|
||||
|
||||
pub struct RustUtils {}
|
||||
|
||||
impl RustUtils {
|
||||
pub fn generate_shares(secret: Vec<u8>, total: u8, threshold: u8) -> Result<Vec<Vec<u8>>> {
|
||||
let sharks = Sharks(threshold);
|
||||
let dealer = sharks.dealer(&secret);
|
||||
let shares = dealer.take(total as usize).map(|x| Vec::from(&x)).collect();
|
||||
Ok(shares)
|
||||
}
|
||||
|
||||
pub fn recover_secret(shares: Vec<Vec<u8>>, threshold: u8) -> Result<Vec<u8>> {
|
||||
let sharks = Sharks(threshold);
|
||||
let shares: Vec<Share> = shares
|
||||
.iter()
|
||||
.filter_map(|x| Share::try_from(x.as_slice()).ok())
|
||||
.collect();
|
||||
let secret = sharks
|
||||
.recover(&shares)
|
||||
.map_err(|e| crate::error::TwonlyError::Generic(e.to_string()))?;
|
||||
Ok(secret)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
// Section: imports
|
||||
|
||||
use crate::user_discovery::traits::UserDiscoveryStore;
|
||||
use crate::user_discovery::traits::UserDiscoveryUtils;
|
||||
use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
|
||||
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
|
||||
use flutter_rust_bridge::{Handler, IntoIntoDart};
|
||||
|
|
@ -38,7 +40,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 = -1867463121;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -508562557;
|
||||
|
||||
// Section: executor
|
||||
|
||||
|
|
@ -544,6 +546,43 @@ fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_signed_pre
|
|||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_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: "rust_key_manager_serialize",
|
||||
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::RustKeyManager::serialize()
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
|
|
@ -600,6 +639,349 @@ let api_record = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); mo
|
|||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__wrapper__rust_utils_generate_shares_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_normal::<flutter_rust_bridge::for_generated::SseCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "rust_utils_generate_shares",
|
||||
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);
|
||||
let api_secret = <Vec<u8>>::sse_decode(&mut deserializer);
|
||||
let api_total = <u8>::sse_decode(&mut deserializer);
|
||||
let api_threshold = <u8>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok = crate::bridge::wrapper::RustUtils::generate_shares(
|
||||
api_secret,
|
||||
api_total,
|
||||
api_threshold,
|
||||
)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__wrapper__rust_utils_recover_secret_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_normal::<flutter_rust_bridge::for_generated::SseCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "rust_utils_recover_secret",
|
||||
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);
|
||||
let api_shares = <Vec<Vec<u8>>>::sse_decode(&mut deserializer);
|
||||
let api_threshold = <u8>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok = crate::bridge::wrapper::RustUtils::recover_secret(
|
||||
api_shares,
|
||||
api_threshold,
|
||||
)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_announced_user_by_public_id_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: "user_discovery_store_flutter_get_announced_user_by_public_id", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_public_id = <i64>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_announced_user_by_public_id(&api_that, api_public_id).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_config_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: "user_discovery_store_flutter_get_config", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_config(&api_that).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_promotion_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: "user_discovery_store_flutter_get_contact_promotion", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_contact_id = <i64>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_contact_promotion(&api_that, api_contact_id).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_version_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: "user_discovery_store_flutter_get_contact_version", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_contact_id = <i64>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_contact_version(&api_that, api_contact_id).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_other_promotions_by_public_id_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: "user_discovery_store_flutter_get_other_promotions_by_public_id", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_public_id = <i64>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_other_promotions_by_public_id(&api_that, api_public_id).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_own_promotions_after_version_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: "user_discovery_store_flutter_get_own_promotions_after_version", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_version = <u32>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_own_promotions_after_version(&api_that, api_version).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_share_for_contact_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: "user_discovery_store_flutter_get_share_for_contact", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_contact_id = <i64>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::get_share_for_contact(&api_that, api_contact_id).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_new_user_relation_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: "user_discovery_store_flutter_push_new_user_relation", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_from_contact_id = <i64>::sse_decode(&mut deserializer);
|
||||
let api_announced_user = <crate::bridge::AnnouncedUser>::sse_decode(&mut deserializer);
|
||||
let api_public_key_verified_timestamp = <Option<i64>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::push_new_user_relation(&api_that, api_from_contact_id, api_announced_user, api_public_key_verified_timestamp).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_own_promotion_and_clear_old_version_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: "user_discovery_store_flutter_push_own_promotion_and_clear_old_version", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_contact_id = <i64>::sse_decode(&mut deserializer);
|
||||
let api_version = <u32>::sse_decode(&mut deserializer);
|
||||
let api_promotion = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::push_own_promotion_and_clear_old_version(&api_that, api_contact_id, api_version, api_promotion).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_contact_version_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: "user_discovery_store_flutter_set_contact_version", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_contact_id = <i64>::sse_decode(&mut deserializer);
|
||||
let api_update = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::set_contact_version(&api_that, api_contact_id, api_update).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_shares_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: "user_discovery_store_flutter_set_shares", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_shares = <Vec<Vec<u8>>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::set_shares(&api_that, api_shares).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_store_other_promotion_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: "user_discovery_store_flutter_store_other_promotion", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_promotion = <crate::bridge::OtherPromotion>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::store_other_promotion(&api_that, api_promotion).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_update_config_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: "user_discovery_store_flutter_update_config", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter>::sse_decode(&mut deserializer);
|
||||
let api_update = <String>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter::update_config(&api_that, api_update).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_sign_data_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: "user_discovery_utils_flutter_sign_data", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter>::sse_decode(&mut deserializer);
|
||||
let api_input_data = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::sign_data(&api_that, &api_input_data).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_signature_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: "user_discovery_utils_flutter_verify_signature", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter>::sse_decode(&mut deserializer);
|
||||
let api_input_data = <Vec<u8>>::sse_decode(&mut deserializer);
|
||||
let api_pubkey = <Vec<u8>>::sse_decode(&mut deserializer);
|
||||
let api_signature = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::verify_signature(&api_that, &api_input_data, &api_pubkey, &api_signature).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
fn wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_stored_pubkey_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: "user_discovery_utils_flutter_verify_stored_pubkey", 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);
|
||||
let api_that = <crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter>::sse_decode(&mut deserializer);
|
||||
let api_from_contact_id = <i64>::sse_decode(&mut deserializer);
|
||||
let api_pubkey = <Vec<u8>>::sse_decode(&mut deserializer);deserializer.end(); move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move {
|
||||
let output_ok = crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter::verify_stored_pubkey(&api_that, api_from_contact_id, &api_pubkey).await?; Ok(output_ok)
|
||||
})().await)
|
||||
} })
|
||||
}
|
||||
|
||||
// Section: static_checks
|
||||
|
||||
|
|
@ -1356,6 +1738,13 @@ impl SseDecode for crate::bridge::wrapper::key_manager::RustKeyManager {
|
|||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::bridge::wrapper::RustUtils {
|
||||
// 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::RustUtils {};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for u32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
|
|
@ -1383,6 +1772,20 @@ impl SseDecode for () {
|
|||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter {
|
||||
// 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::callbacks::user_discovery::UserDiscoveryStoreFlutter {};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter {
|
||||
// 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::callbacks::user_discovery::UserDiscoveryUtilsFlutter {};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for usize {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
|
|
@ -1431,8 +1834,27 @@ fn pde_ffi_dispatcher_primary_impl(
|
|||
23 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_load_signed_prekeys_impl(port, ptr, rust_vec_len, data_len),
|
||||
24 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_key_manager_impl(port, ptr, rust_vec_len, data_len),
|
||||
25 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_signed_prekey_impl(port, ptr, rust_vec_len, data_len),
|
||||
26 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
27 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_store_signed_prekey_impl(port, ptr, rust_vec_len, data_len),
|
||||
26 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_impl(port, ptr, rust_vec_len, data_len),
|
||||
27 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
28 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_store_signed_prekey_impl(port, ptr, rust_vec_len, data_len),
|
||||
29 => wire__crate__bridge__wrapper__rust_utils_generate_shares_impl(port, ptr, rust_vec_len, data_len),
|
||||
30 => wire__crate__bridge__wrapper__rust_utils_recover_secret_impl(port, ptr, rust_vec_len, data_len),
|
||||
31 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_announced_user_by_public_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
32 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_config_impl(port, ptr, rust_vec_len, data_len),
|
||||
33 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_promotion_impl(port, ptr, rust_vec_len, data_len),
|
||||
34 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_contact_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
35 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_other_promotions_by_public_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
36 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_own_promotions_after_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
37 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_get_share_for_contact_impl(port, ptr, rust_vec_len, data_len),
|
||||
38 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_new_user_relation_impl(port, ptr, rust_vec_len, data_len),
|
||||
39 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_push_own_promotion_and_clear_old_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
40 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_contact_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
41 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_set_shares_impl(port, ptr, rust_vec_len, data_len),
|
||||
42 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_store_other_promotion_impl(port, ptr, rust_vec_len, data_len),
|
||||
43 => wire__crate__bridge__callbacks__user_discovery__user_discovery_store_flutter_update_config_impl(port, ptr, rust_vec_len, data_len),
|
||||
44 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_sign_data_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_signature_impl(port, ptr, rust_vec_len, data_len),
|
||||
46 => wire__crate__bridge__callbacks__user_discovery__user_discovery_utils_flutter_verify_stored_pubkey_impl(port, ptr, rust_vec_len, data_len),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1608,6 +2030,65 @@ impl flutter_rust_bridge::IntoIntoDart<crate::bridge::wrapper::key_manager::Rust
|
|||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::bridge::wrapper::RustUtils {
|
||||
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::RustUtils
|
||||
{
|
||||
}
|
||||
impl flutter_rust_bridge::IntoIntoDart<crate::bridge::wrapper::RustUtils>
|
||||
for crate::bridge::wrapper::RustUtils
|
||||
{
|
||||
fn into_into_dart(self) -> crate::bridge::wrapper::RustUtils {
|
||||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart
|
||||
for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter
|
||||
{
|
||||
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::callbacks::user_discovery::UserDiscoveryStoreFlutter
|
||||
{
|
||||
}
|
||||
impl
|
||||
flutter_rust_bridge::IntoIntoDart<
|
||||
crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter,
|
||||
> for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter
|
||||
{
|
||||
fn into_into_dart(self) -> crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter {
|
||||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart
|
||||
for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter
|
||||
{
|
||||
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::callbacks::user_discovery::UserDiscoveryUtilsFlutter
|
||||
{
|
||||
}
|
||||
impl
|
||||
flutter_rust_bridge::IntoIntoDart<
|
||||
crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter,
|
||||
> for crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter
|
||||
{
|
||||
fn into_into_dart(self) -> crate::bridge::callbacks::user_discovery::UserDiscoveryUtilsFlutter {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
|
|
@ -1849,6 +2330,11 @@ impl SseEncode for crate::bridge::wrapper::key_manager::RustKeyManager {
|
|||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::bridge::wrapper::RustUtils {
|
||||
// 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 u32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
|
@ -1881,6 +2367,16 @@ impl SseEncode for () {
|
|||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::bridge::callbacks::user_discovery::UserDiscoveryStoreFlutter {
|
||||
// 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::callbacks::user_discovery::UserDiscoveryUtilsFlutter {
|
||||
// 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 usize {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
|
@ -1906,6 +2402,8 @@ mod io {
|
|||
// Section: imports
|
||||
|
||||
use super::*;
|
||||
use crate::user_discovery::traits::UserDiscoveryStore;
|
||||
use crate::user_discovery::traits::UserDiscoveryUtils;
|
||||
use flutter_rust_bridge::for_generated::byteorder::{
|
||||
NativeEndian, ReadBytesExt, WriteBytesExt,
|
||||
};
|
||||
|
|
@ -1928,6 +2426,8 @@ mod web {
|
|||
// Section: imports
|
||||
|
||||
use super::*;
|
||||
use crate::user_discovery::traits::UserDiscoveryStore;
|
||||
use crate::user_discovery::traits::UserDiscoveryUtils;
|
||||
use flutter_rust_bridge::for_generated::byteorder::{
|
||||
NativeEndian, ReadBytesExt, WriteBytesExt,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ mod error;
|
|||
mod frb_generated;
|
||||
mod keys;
|
||||
mod log;
|
||||
mod passwordless_recovery;
|
||||
mod secure_storage;
|
||||
mod standalone;
|
||||
mod user_discovery;
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/passwordless_recovery.rs"));
|
||||
|
||||
struct PasswordLessRecovery {}
|
||||
|
||||
impl PasswordLessRecovery {
|
||||
pub(crate) fn create_shared_secret_data(
|
||||
total_shares: i64,
|
||||
threshold: i64,
|
||||
pin_unlock_token: Option<Vec<u8>>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -16,13 +16,11 @@ protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "messages.proto"
|
|||
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "groups.proto"
|
||||
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "qr.proto"
|
||||
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "data.proto"
|
||||
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "passwordless_recovery.proto"
|
||||
|
||||
mkdir "$GENERATED_DIR/user_discovery/" &>/dev/null
|
||||
protoc --proto_path="./rust/src/user_discovery/" --dart_out="$GENERATED_DIR/user_discovery/" "types.proto"
|
||||
|
||||
mkdir "$GENERATED_DIR/passwordless_recovery/" &>/dev/null
|
||||
protoc --proto_path="./rust/src/passwordless_recovery/" --dart_out="$GENERATED_DIR/passwordless_recovery/" "types.proto"
|
||||
|
||||
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "push_notification.proto"
|
||||
protoc --proto_path="$CLIENT_DIR" --swift_out="./ios/NotificationService/" "push_notification.proto"
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import 'schema_v17.dart' as v17;
|
|||
import 'schema_v18.dart' as v18;
|
||||
import 'schema_v19.dart' as v19;
|
||||
import 'schema_v20.dart' as v20;
|
||||
import 'schema_v21.dart' as v21;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
|
|
@ -69,6 +70,8 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||
return v19.DatabaseAtV19(db);
|
||||
case 20:
|
||||
return v20.DatabaseAtV20(db);
|
||||
case 21:
|
||||
return v21.DatabaseAtV21(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
|
|
@ -95,5 +98,6 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
11000
test/drift/twonly_db/generated/schema_v21.dart
Normal file
11000
test/drift/twonly_db/generated/schema_v21.dart
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -57,20 +57,6 @@ void main() {
|
|||
shareAction: 70,
|
||||
likeAction: 90,
|
||||
),
|
||||
LinkParserTest(
|
||||
title: 'Kuketz-Blog 🛡 (@kuketzblog@social.tchncs.de)',
|
||||
url: 'https://social.tchncs.de/@kuketzblog/115898752560771936',
|
||||
siteName: 'Mastodon',
|
||||
desc:
|
||||
'AWS verspricht jetzt »Souveränität« mit einem »europäischen« Cloud-Angebot – Standort Deutschland, großes Vertrauens-Theater.\n'
|
||||
'\n'
|
||||
'Nur: Souveränität ist keine Postleitzahl. Wenn der Anbieter Amazon heißt, bleibt es dasselbe Märchen mit neuem Umschlag: Der Cloud Act, FISA etc. gilt trotzdem. US-Recht schlägt Geografie. Das Gerede von »Souveränität« ist kein Konzept, sondern Marketing.\n'
|
||||
'\n'
|
||||
'https://www.heise.de/news/AWS-verspricht-Souveraenitaet-mit-europaeischem-Cloudangebot-11141800.html',
|
||||
vendor: Vendor.mastodonSocialMediaPosting,
|
||||
shareAction: 15,
|
||||
likeAction: 190,
|
||||
),
|
||||
LinkParserTest(
|
||||
title:
|
||||
'David Kriesel: Traue keinem Scan, den du nicht selbst gefälscht hast',
|
||||
|
|
|
|||
610
test/services/passwordless_recovery_service_test.dart
Normal file
610
test/services/passwordless_recovery_service_test.dart
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
// ignore_for_file: strict_raw_type
|
||||
|
||||
import 'dart:convert' show utf8;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||
import 'package:twonly/core/bridge.dart' as bridge;
|
||||
import 'package:twonly/core/frb_generated.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/callbacks/callbacks.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/json/userdata.model.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||
as api_pb;
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||
as pb;
|
||||
import 'package:twonly/src/services/api.service.dart';
|
||||
import 'package:twonly/src/services/api/utils.api.dart';
|
||||
import 'package:twonly/src/services/passwordless_recovery.service.dart';
|
||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||
import 'package:twonly/src/services/user.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
import '../mocks/platform_channels.dart';
|
||||
|
||||
class MockApiService extends ApiService {
|
||||
final List<(List<int>, List<int>?)> registeredPasswordLessRecoveries = [];
|
||||
final List<(int, Uint8List, List<int>?)> sentTextMessages = [];
|
||||
|
||||
@override
|
||||
Future<Result> registerPasswordLessRecovery(
|
||||
List<int> encryptedServerKey,
|
||||
List<int>? pinUnlockToken,
|
||||
) async {
|
||||
registeredPasswordLessRecoveries.add((encryptedServerKey, pinUnlockToken));
|
||||
return Result.success(api_pb.Response_Ok());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result> sendTextMessage(
|
||||
int target,
|
||||
Uint8List msg,
|
||||
List<int>? pushData,
|
||||
) async {
|
||||
sentTextMessages.add((target, msg, pushData));
|
||||
return Result.success(api_pb.Response_Ok());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result> updateSignedPreKey(
|
||||
int id,
|
||||
List<int> key,
|
||||
List<int> signature,
|
||||
) async {
|
||||
return Result.success(api_pb.Response_Ok());
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (!Platform.isMacOS) {
|
||||
return;
|
||||
}
|
||||
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
late Directory tempDir;
|
||||
|
||||
setUpAll(() async {
|
||||
Log.init();
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
final dylibPath =
|
||||
'${Directory.current.path}/rust/target/debug/librust_lib_twonly.dylib';
|
||||
if (File(dylibPath).existsSync()) {
|
||||
await RustLib.init(
|
||||
externalLibrary: ExternalLibrary.open(dylibPath),
|
||||
);
|
||||
} else {
|
||||
await RustLib.init();
|
||||
}
|
||||
await initFlutterCallbacksForRust();
|
||||
|
||||
tempDir = Directory.systemTemp.createTempSync('twonly_passwordless_test_');
|
||||
AppEnvironment.initTesting(
|
||||
customCacheDir: tempDir.path,
|
||||
customSupportDir: tempDir.path,
|
||||
);
|
||||
|
||||
await bridge.initializeTwonlyFlutter(
|
||||
config: bridge.InitConfig(
|
||||
databaseDir: tempDir.path,
|
||||
dataDir: tempDir.path,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
setupPlatformChannelMocks();
|
||||
await locator.reset();
|
||||
final dbFile = File('${tempDir.path}/twonly.sqlite');
|
||||
if (dbFile.existsSync()) {
|
||||
dbFile.deleteSync();
|
||||
}
|
||||
|
||||
locator
|
||||
..registerSingleton<TwonlyDB>(
|
||||
TwonlyDB(NativeDatabase(dbFile)),
|
||||
)
|
||||
..registerSingleton<UserService>(UserService())
|
||||
..registerSingleton<ApiService>(MockApiService());
|
||||
|
||||
userService.currentUser = UserData(
|
||||
userId: 1,
|
||||
username: 'me',
|
||||
displayName: 'Me',
|
||||
subscriptionPlan: 'Free',
|
||||
currentSetupPage: null,
|
||||
appVersion: 100,
|
||||
);
|
||||
userService.isUserCreated = true;
|
||||
await UserService.save(userService.currentUser);
|
||||
|
||||
await createIfNotExistsSignalIdentity();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
try {
|
||||
await twonlyDB.close();
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
if (tempDir.existsSync()) {
|
||||
try {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
} catch (_) {}
|
||||
}
|
||||
});
|
||||
|
||||
Future<void> setupSignalSession(int contactId) async {
|
||||
final identityKeyPair = generateIdentityKeyPair();
|
||||
final registrationId = generateRegistrationId(true);
|
||||
final signedPreKey = generateSignedPreKey(identityKeyPair, 1);
|
||||
final preKey = generatePreKeys(1, 1).first;
|
||||
|
||||
final responseUserData = api_pb.Response_UserData()
|
||||
..userId = Int64(contactId)
|
||||
..username = utf8.encode('user_$contactId')
|
||||
..registrationId = Int64(registrationId)
|
||||
..publicIdentityKey = identityKeyPair.getPublicKey().serialize()
|
||||
..signedPrekey = signedPreKey.getKeyPair().publicKey.serialize()
|
||||
..signedPrekeyId = Int64(signedPreKey.id)
|
||||
..signedPrekeySignature = signedPreKey.signature;
|
||||
|
||||
responseUserData.prekeys.add(
|
||||
api_pb.Response_PreKey()
|
||||
..id = Int64(preKey.id)
|
||||
..prekey = preKey.getKeyPair().publicKey.serialize(),
|
||||
);
|
||||
|
||||
final success = await processSignalUserData(responseUserData);
|
||||
expect(success, isTrue);
|
||||
}
|
||||
|
||||
group('PasswordlessRecoveryService - enablePasswordlessRecovery', () {
|
||||
test('works with SecondFactorType.none', () async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'),
|
||||
);
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(3), username: 'friend_3'),
|
||||
);
|
||||
|
||||
final success =
|
||||
await PasswordlessRecoveryService.enablePasswordlessRecovery(
|
||||
trustedFriendIds: [2, 3],
|
||||
secondFactorType: SecondFactorType.none,
|
||||
secondFactorValue: '',
|
||||
threshold: 2,
|
||||
);
|
||||
|
||||
expect(success, isTrue);
|
||||
|
||||
final c2 = await twonlyDB.contactsDao.getContactById(2);
|
||||
final c3 = await twonlyDB.contactsDao.getContactById(3);
|
||||
|
||||
expect(c2?.recoveryIsTrustedFriend, isTrue);
|
||||
expect(c3?.recoveryIsTrustedFriend, isTrue);
|
||||
expect(c2?.recoverySecretShare, isNotNull);
|
||||
expect(c3?.recoverySecretShare, isNotNull);
|
||||
expect(c2?.recoveryLastHeartbeat, isNull);
|
||||
expect(c3?.recoveryLastHeartbeat, isNull);
|
||||
});
|
||||
|
||||
test('works with SecondFactorType.email', () async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'),
|
||||
);
|
||||
|
||||
final success =
|
||||
await PasswordlessRecoveryService.enablePasswordlessRecovery(
|
||||
trustedFriendIds: [2],
|
||||
secondFactorType: SecondFactorType.email,
|
||||
secondFactorValue: 'a_very_long_email_addr@gmail.com',
|
||||
threshold: 1,
|
||||
);
|
||||
|
||||
expect(success, isTrue);
|
||||
|
||||
final c2 = await twonlyDB.contactsDao.getContactById(2);
|
||||
expect(c2?.recoveryIsTrustedFriend, isTrue);
|
||||
expect(c2?.recoverySecretShare, isNotNull);
|
||||
});
|
||||
|
||||
test('works with SecondFactorType.pin', () async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(2), username: 'friend_2'),
|
||||
);
|
||||
|
||||
final success =
|
||||
await PasswordlessRecoveryService.enablePasswordlessRecovery(
|
||||
trustedFriendIds: [2],
|
||||
secondFactorType: SecondFactorType.pin,
|
||||
secondFactorValue: '123456',
|
||||
threshold: 1,
|
||||
);
|
||||
|
||||
expect(success, isTrue);
|
||||
|
||||
final c2 = await twonlyDB.contactsDao.getContactById(2);
|
||||
expect(c2?.recoveryIsTrustedFriend, isTrue);
|
||||
expect(c2?.recoverySecretShare, isNotNull);
|
||||
});
|
||||
|
||||
test('sends delete messages to old trusted friends', () async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(2),
|
||||
username: 'friend_2',
|
||||
recoveryIsTrustedFriend: const Value(true),
|
||||
),
|
||||
);
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(3), username: 'friend_3'),
|
||||
);
|
||||
|
||||
await setupSignalSession(2);
|
||||
await setupSignalSession(3);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.sentTextMessages.clear();
|
||||
|
||||
final success =
|
||||
await PasswordlessRecoveryService.enablePasswordlessRecovery(
|
||||
trustedFriendIds: [3],
|
||||
secondFactorType: SecondFactorType.none,
|
||||
secondFactorValue: '',
|
||||
threshold: 1,
|
||||
);
|
||||
|
||||
expect(success, isTrue);
|
||||
|
||||
// Verify old friend got a delete message
|
||||
expect(mockApi.sentTextMessages.isNotEmpty, isTrue);
|
||||
final deleteMsg = mockApi.sentTextMessages.firstWhere((t) => t.$1 == 2);
|
||||
expect(deleteMsg, isNotNull);
|
||||
|
||||
final oldFriend = await twonlyDB.contactsDao.getContactById(2);
|
||||
expect(oldFriend?.recoveryIsTrustedFriend, isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('PasswordlessRecoveryService - performHeartbeat', () {
|
||||
test(
|
||||
'triggers server registration based on lastServerHeartbeat time',
|
||||
() async {
|
||||
final config = PasswordLessRecovery()
|
||||
..encryptedServerKey = Uint8List.fromList([1, 2, 3])
|
||||
..pinUnlockToken = Uint8List.fromList([4, 5, 6]);
|
||||
|
||||
await UserService.update((u) => u.passwordLessRecovery = config);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.registeredPasswordLessRecoveries.clear();
|
||||
|
||||
// Case 1: lastServerHeartbeat is null -> should trigger registration
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
expect(mockApi.registeredPasswordLessRecoveries.length, 1);
|
||||
expect(
|
||||
userService.currentUser.passwordLessRecovery?.lastServerHeartbeat,
|
||||
isNotNull,
|
||||
);
|
||||
|
||||
// Case 2: lastServerHeartbeat is < 30 days old -> should not trigger
|
||||
mockApi.registeredPasswordLessRecoveries.clear();
|
||||
final now = clock.now();
|
||||
await UserService.update(
|
||||
(u) => u.passwordLessRecovery?.lastServerHeartbeat = now.subtract(
|
||||
const Duration(days: 15),
|
||||
),
|
||||
);
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
expect(mockApi.registeredPasswordLessRecoveries.length, 0);
|
||||
|
||||
// Case 3: lastServerHeartbeat is > 30 days old -> should trigger
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
await UserService.update(
|
||||
(u) => u.passwordLessRecovery?.lastServerHeartbeat = now.subtract(
|
||||
const Duration(days: 31),
|
||||
),
|
||||
);
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
expect(mockApi.registeredPasswordLessRecoveries.length, 1);
|
||||
expect(
|
||||
userService.currentUser.passwordLessRecovery?.lastServerHeartbeat,
|
||||
now,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'sends secret shares to trusted friends with null recoveryLastHeartbeat',
|
||||
() async {
|
||||
final config = PasswordLessRecovery()
|
||||
..encryptedServerKey = Uint8List.fromList([1, 2, 3])
|
||||
..pinUnlockToken = Uint8List.fromList([4, 5, 6]);
|
||||
await UserService.update((u) => u.passwordLessRecovery = config);
|
||||
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(2),
|
||||
username: 'friend_2',
|
||||
recoveryIsTrustedFriend: const Value(true),
|
||||
recoverySecretShare: Value(Uint8List.fromList([1, 2, 3])),
|
||||
recoveryLastHeartbeat: const Value(null),
|
||||
),
|
||||
);
|
||||
|
||||
await setupSignalSession(2);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.sentTextMessages.clear();
|
||||
|
||||
final baseTime = DateTime(2026, 6, 20, 12, 0);
|
||||
|
||||
await withClock(Clock.fixed(baseTime), () async {
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
});
|
||||
|
||||
// Verify message sent
|
||||
expect(mockApi.sentTextMessages.length, 1);
|
||||
expect(mockApi.sentTextMessages.first.$1, 2);
|
||||
|
||||
// Verify db update
|
||||
final c2 = await twonlyDB.contactsDao.getContactById(2);
|
||||
expect(c2?.recoveryLastHeartbeat, baseTime);
|
||||
|
||||
// Verify calling again does not resend
|
||||
mockApi.sentTextMessages.clear();
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
expect(mockApi.sentTextMessages.length, 0);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'sends heartbeat to friends who chose us as a trusted friend based on time',
|
||||
() async {
|
||||
final config = PasswordLessRecovery()
|
||||
..encryptedServerKey = Uint8List.fromList([1, 2, 3])
|
||||
..pinUnlockToken = Uint8List.fromList([4, 5, 6]);
|
||||
await UserService.update((u) => u.passwordLessRecovery = config);
|
||||
|
||||
final shareBytes = Uint8List.fromList([10, 20, 30]);
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(3),
|
||||
username: 'friend_3',
|
||||
recoveryContactsSecretShare: Value(shareBytes),
|
||||
recoveryContactsLastHeartbeat: const Value(null),
|
||||
),
|
||||
);
|
||||
|
||||
await setupSignalSession(3);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.sentTextMessages.clear();
|
||||
|
||||
final baseTime = DateTime(2026, 6, 20, 12, 0);
|
||||
|
||||
// Case 1: recoveryContactsLastHeartbeat is null -> should send
|
||||
await withClock(Clock.fixed(baseTime), () async {
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
});
|
||||
expect(mockApi.sentTextMessages.length, 1);
|
||||
expect(mockApi.sentTextMessages.first.$1, 3);
|
||||
|
||||
final c3 = await twonlyDB.contactsDao.getContactById(3);
|
||||
expect(c3?.recoveryContactsLastHeartbeat, baseTime);
|
||||
|
||||
// Case 2: last heartbeat is < 7 days -> should not send
|
||||
mockApi.sentTextMessages.clear();
|
||||
await withClock(
|
||||
Clock.fixed(baseTime.add(const Duration(days: 5))),
|
||||
() async {
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
},
|
||||
);
|
||||
expect(mockApi.sentTextMessages.length, 0);
|
||||
|
||||
// Case 3: last heartbeat is > 7 days -> should send
|
||||
mockApi.sentTextMessages.clear();
|
||||
final targetTime = baseTime.add(const Duration(days: 8));
|
||||
await withClock(Clock.fixed(targetTime), () async {
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
});
|
||||
expect(mockApi.sentTextMessages.length, 1);
|
||||
|
||||
final c3Updated = await twonlyDB.contactsDao.getContactById(3);
|
||||
expect(c3Updated?.recoveryContactsLastHeartbeat, targetTime);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'sends heartbeat to friends who chose us as a trusted friend even if our config is null',
|
||||
() async {
|
||||
await UserService.update((u) => u.passwordLessRecovery = null);
|
||||
|
||||
final shareBytes = Uint8List.fromList([10, 20, 30]);
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(4),
|
||||
username: 'friend_4',
|
||||
recoveryContactsSecretShare: Value(shareBytes),
|
||||
recoveryContactsLastHeartbeat: const Value(null),
|
||||
),
|
||||
);
|
||||
|
||||
await setupSignalSession(4);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.sentTextMessages.clear();
|
||||
|
||||
final baseTime = DateTime(2026, 6, 20, 12, 0);
|
||||
|
||||
await withClock(Clock.fixed(baseTime), () async {
|
||||
await PasswordlessRecoveryService.performHeartbeat();
|
||||
});
|
||||
|
||||
expect(mockApi.sentTextMessages.length, 1);
|
||||
expect(mockApi.sentTextMessages.first.$1, 4);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('PasswordlessRecoveryService - handlePasswordlessRecovery', () {
|
||||
test(
|
||||
'handlePasswordlessRecovery deletes share when delete is true',
|
||||
() async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(4),
|
||||
username: 'friend_4',
|
||||
recoveryContactsSecretShare: Value(Uint8List.fromList([7, 8, 9])),
|
||||
recoveryContactsLastHeartbeat: Value(DateTime.now()),
|
||||
),
|
||||
);
|
||||
|
||||
final msg = pb.EncryptedContent_PasswordLessRecovery()..delete = true;
|
||||
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecovery(
|
||||
4,
|
||||
msg,
|
||||
'receipt_id_1',
|
||||
);
|
||||
|
||||
final c4 = await twonlyDB.contactsDao.getContactById(4);
|
||||
expect(c4?.recoveryContactsSecretShare, isNull);
|
||||
expect(c4?.recoveryContactsLastHeartbeat, isNull);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'handlePasswordlessRecovery saves share when delete is false',
|
||||
() async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(4),
|
||||
username: 'friend_4',
|
||||
),
|
||||
);
|
||||
|
||||
final msg = pb.EncryptedContent_PasswordLessRecovery()
|
||||
..delete = false
|
||||
..recoverySecretShare = [11, 22, 33];
|
||||
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecovery(
|
||||
4,
|
||||
msg,
|
||||
'receipt_id_2',
|
||||
);
|
||||
|
||||
final c4 = await twonlyDB.contactsDao.getContactById(4);
|
||||
expect(
|
||||
c4?.recoveryContactsSecretShare,
|
||||
Uint8List.fromList([11, 22, 33]),
|
||||
);
|
||||
expect(c4?.recoveryContactsLastHeartbeat, isNull);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('PasswordlessRecoveryService - handlePasswordlessRecoveryHeartbeat', () {
|
||||
test('sends delete message back if stored share is null', () async {
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(userId: const Value(5), username: 'friend_5'),
|
||||
);
|
||||
|
||||
await setupSignalSession(5);
|
||||
|
||||
final mockApi = apiService as MockApiService;
|
||||
mockApi.sentTextMessages.clear();
|
||||
|
||||
final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat()
|
||||
..hash = [1, 2, 3];
|
||||
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat(
|
||||
5,
|
||||
msg,
|
||||
'receipt_id_3',
|
||||
);
|
||||
|
||||
// Await unawaited sendCipherText call
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
|
||||
// We expect a delete message sent back
|
||||
expect(mockApi.sentTextMessages.isNotEmpty, isTrue);
|
||||
expect(mockApi.sentTextMessages.first.$1, 5);
|
||||
});
|
||||
|
||||
test(
|
||||
'updates recoveryLastHeartbeat to clock.now() when hash matches',
|
||||
() async {
|
||||
final shareBytes = Uint8List.fromList([1, 2, 3]);
|
||||
final hashBytes = sha256.convert(shareBytes).bytes;
|
||||
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(5),
|
||||
username: 'friend_5',
|
||||
recoverySecretShare: Value(shareBytes),
|
||||
),
|
||||
);
|
||||
|
||||
final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat()
|
||||
..hash = hashBytes;
|
||||
|
||||
final now = DateTime(2026, 6, 20, 12, 0);
|
||||
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat(
|
||||
5,
|
||||
msg,
|
||||
'receipt_id_4',
|
||||
);
|
||||
});
|
||||
|
||||
final c5 = await twonlyDB.contactsDao.getContactById(5);
|
||||
expect(c5?.recoveryLastHeartbeat, now);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'sets recoveryLastHeartbeat to null when hash does not match',
|
||||
() async {
|
||||
final shareBytes = Uint8List.fromList([1, 2, 3]);
|
||||
final wrongHashBytes = sha256.convert([4, 5, 6]).bytes;
|
||||
|
||||
await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion.insert(
|
||||
userId: const Value(5),
|
||||
username: 'friend_5',
|
||||
recoverySecretShare: Value(shareBytes),
|
||||
recoveryLastHeartbeat: Value(DateTime.now()),
|
||||
),
|
||||
);
|
||||
|
||||
final msg = pb.EncryptedContent_PasswordLessRecoveryHeartbeat()
|
||||
..hash = wrongHashBytes;
|
||||
|
||||
await PasswordlessRecoveryService.handlePasswordlessRecoveryHeartbeat(
|
||||
5,
|
||||
msg,
|
||||
'receipt_id_5',
|
||||
);
|
||||
|
||||
final c5 = await twonlyDB.contactsDao.getContactById(5);
|
||||
expect(c5?.recoveryLastHeartbeat, isNull);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue