mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
move files
This commit is contained in:
parent
85c0f32f21
commit
b06c2e1cc8
13 changed files with 356 additions and 204 deletions
106
lib/src/model/json/message.dart
Normal file
106
lib/src/model/json/message.dart
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
import 'package:twonly/src/utils/json.dart';
|
||||||
|
part 'message.g.dart';
|
||||||
|
|
||||||
|
enum MessageKind { textMessage, image, video, contactRequest }
|
||||||
|
|
||||||
|
// so _$MessageKindEnumMap gets generated
|
||||||
|
@JsonSerializable()
|
||||||
|
class _MessageKind {
|
||||||
|
MessageKind? kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Message {
|
||||||
|
@Int64Converter()
|
||||||
|
final Int64 fromUserId;
|
||||||
|
final MessageKind kind;
|
||||||
|
final MessageContent? content;
|
||||||
|
DateTime timestamp;
|
||||||
|
|
||||||
|
Message(
|
||||||
|
{required this.fromUserId,
|
||||||
|
required this.kind,
|
||||||
|
this.content,
|
||||||
|
required this.timestamp});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Message(kind: $kind, content: $content, timestamp: $timestamp)';
|
||||||
|
}
|
||||||
|
|
||||||
|
Message fromJson(Map<String, dynamic> json) {
|
||||||
|
dynamic content;
|
||||||
|
MessageKind kind = $enumDecode(_$MessageKindEnumMap, json['kind']);
|
||||||
|
switch (kind) {
|
||||||
|
case MessageKind.textMessage:
|
||||||
|
content = TextContent.fromJson(json["content"]);
|
||||||
|
break;
|
||||||
|
case MessageKind.image:
|
||||||
|
content = ImageContent.fromJson(json["content"]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return Message(
|
||||||
|
fromUserId: const Int64Converter().fromJson(json['fromUserId'] as String),
|
||||||
|
kind: kind,
|
||||||
|
timestamp: DateTime.parse(json['timestamp'] as String),
|
||||||
|
content: content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson(Message instance) {
|
||||||
|
var json = <String, dynamic>{
|
||||||
|
'fromUserId': const Int64Converter().toJson(instance.fromUserId),
|
||||||
|
'kind': _$MessageKindEnumMap[instance.kind]!,
|
||||||
|
'timestamp': instance.timestamp.toIso8601String(),
|
||||||
|
'content': instance.content
|
||||||
|
};
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MessageContent {
|
||||||
|
MessageContent();
|
||||||
|
factory MessageContent.fromJson(Map<String, dynamic> json) {
|
||||||
|
return TextContent("");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
}
|
||||||
|
// factory MessageContent.fromJson(Map<String, dynamic> json) =>
|
||||||
|
// _$MessageContentFromJson(json);
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class TextContent extends MessageContent {
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
TextContent(this.text);
|
||||||
|
|
||||||
|
factory TextContent.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$TextContentFromJson(json);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$TextContentToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ImageContent extends MessageContent {
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
ImageContent(this.imageUrl);
|
||||||
|
|
||||||
|
factory ImageContent.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ImageContentFromJson(json);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$ImageContentToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @JsonSerializable()
|
||||||
|
// class VideoContent extends MessageContent {
|
||||||
|
// final String videoUrl;
|
||||||
|
|
||||||
|
// VideoContent(this.videoUrl);
|
||||||
|
// }
|
||||||
56
lib/src/model/json/message.g.dart
Normal file
56
lib/src/model/json/message.g.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'message.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_MessageKind _$MessageKindFromJson(Map<String, dynamic> json) => _MessageKind()
|
||||||
|
..kind = $enumDecodeNullable(_$MessageKindEnumMap, json['kind']);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$MessageKindToJson(_MessageKind instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': _$MessageKindEnumMap[instance.kind],
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$MessageKindEnumMap = {
|
||||||
|
MessageKind.textMessage: 'textMessage',
|
||||||
|
MessageKind.image: 'image',
|
||||||
|
MessageKind.video: 'video',
|
||||||
|
MessageKind.contactRequest: 'contactRequest',
|
||||||
|
};
|
||||||
|
|
||||||
|
Message _$MessageFromJson(Map<String, dynamic> json) => Message(
|
||||||
|
fromUserId: const Int64Converter().fromJson(json['fromUserId'] as String),
|
||||||
|
kind: $enumDecode(_$MessageKindEnumMap, json['kind']),
|
||||||
|
content: json['content'] == null
|
||||||
|
? null
|
||||||
|
: MessageContent.fromJson(json['content'] as Map<String, dynamic>),
|
||||||
|
timestamp: DateTime.parse(json['timestamp'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$MessageToJson(Message instance) => <String, dynamic>{
|
||||||
|
'fromUserId': const Int64Converter().toJson(instance.fromUserId),
|
||||||
|
'kind': _$MessageKindEnumMap[instance.kind]!,
|
||||||
|
'content': instance.content,
|
||||||
|
'timestamp': instance.timestamp.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
TextContent _$TextContentFromJson(Map<String, dynamic> json) => TextContent(
|
||||||
|
json['text'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$TextContentToJson(TextContent instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'text': instance.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageContent _$ImageContentFromJson(Map<String, dynamic> json) => ImageContent(
|
||||||
|
json['imageUrl'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ImageContentToJson(ImageContent instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'imageUrl': instance.imageUrl,
|
||||||
|
};
|
||||||
|
|
@ -1,22 +1,8 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:twonly/src/model/user_data_json.dart';
|
import 'package:twonly/src/utils/json.dart';
|
||||||
part 'signal_identity_json.g.dart';
|
part 'signal_identity.g.dart';
|
||||||
|
|
||||||
class Uint8ListConverter implements JsonConverter<Uint8List, String> {
|
|
||||||
const Uint8ListConverter();
|
|
||||||
@override
|
|
||||||
Uint8List fromJson(String json) {
|
|
||||||
return base64Decode(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toJson(Uint8List object) {
|
|
||||||
return base64Encode(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class SignalIdentity {
|
class SignalIdentity {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'signal_identity_json.dart';
|
part of 'signal_identity.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
part 'user_data_json.g.dart';
|
import 'package:twonly/src/utils/json.dart';
|
||||||
|
part 'user_data.g.dart';
|
||||||
class Int64Converter implements JsonConverter<Int64, String> {
|
|
||||||
const Int64Converter();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Int64 fromJson(String json) {
|
|
||||||
return Int64.parseInt(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toJson(Int64 object) {
|
|
||||||
return object.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class UserData {
|
class UserData {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'user_data_json.dart';
|
part of 'user_data.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
|
|
@ -3,17 +3,18 @@ import 'dart:math';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client;
|
import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client;
|
||||||
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
|
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
|
||||||
import 'package:twonly/src/proto/api/error.pb.dart';
|
import 'package:twonly/src/proto/api/error.pb.dart';
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server;
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server;
|
||||||
import 'package:twonly/src/signal/signal_helper.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
// ignore: library_prefixes
|
||||||
|
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||||
import 'package:web_socket_channel/io.dart';
|
import 'package:web_socket_channel/io.dart';
|
||||||
|
// ignore: implementation_imports
|
||||||
import 'package:libsignal_protocol_dart/src/ecc/ed25519.dart';
|
import 'package:libsignal_protocol_dart/src/ecc/ed25519.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:fixnum/fixnum.dart';
|
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:twonly/src/utils/signal.dart';
|
||||||
import 'package:twonly/src/model/signal_identity_json.dart';
|
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
|
|
||||||
import 'connect_sender_key_store.dart';
|
import 'connect_sender_key_store.dart';
|
||||||
import 'connect_signal_protocol_store.dart';
|
import 'connect_signal_protocol_store.dart';
|
||||||
|
|
||||||
|
|
@ -23,8 +18,8 @@ class SignalDataModel {
|
||||||
// Session validation
|
// Session validation
|
||||||
Future<Fingerprint?> generateSessionFingerPrint(String target) async {
|
Future<Fingerprint?> generateSessionFingerPrint(String target) async {
|
||||||
try {
|
try {
|
||||||
IdentityKey? targetIdentity = await signalStore.getIdentity(
|
IdentityKey? targetIdentity = await signalStore
|
||||||
SignalProtocolAddress(target, SignalHelper.defaultDeviceId));
|
.getIdentity(SignalProtocolAddress(target, defaultDeviceId));
|
||||||
if (targetIdentity != null) {
|
if (targetIdentity != null) {
|
||||||
final generator = NumericFingerprintGenerator(5200);
|
final generator = NumericFingerprintGenerator(5200);
|
||||||
final localFingerprint = generator.createFor(
|
final localFingerprint = generator.createFor(
|
||||||
|
|
@ -48,8 +43,8 @@ class SignalDataModel {
|
||||||
|
|
||||||
Future<String?> getEncryptedText(String text, String target) async {
|
Future<String?> getEncryptedText(String text, String target) async {
|
||||||
try {
|
try {
|
||||||
SessionCipher session = SessionCipher.fromStore(signalStore,
|
SessionCipher session = SessionCipher.fromStore(
|
||||||
SignalProtocolAddress(target, SignalHelper.defaultDeviceId));
|
signalStore, SignalProtocolAddress(target, defaultDeviceId));
|
||||||
final ciphertext =
|
final ciphertext =
|
||||||
await session.encrypt(Uint8List.fromList(utf8.encode(text)));
|
await session.encrypt(Uint8List.fromList(utf8.encode(text)));
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
|
|
@ -65,8 +60,8 @@ class SignalDataModel {
|
||||||
|
|
||||||
Future<String?> getDecryptedText(String source, String msg) async {
|
Future<String?> getDecryptedText(String source, String msg) async {
|
||||||
try {
|
try {
|
||||||
SessionCipher session = SessionCipher.fromStore(signalStore,
|
SessionCipher session = SessionCipher.fromStore(
|
||||||
SignalProtocolAddress(source, SignalHelper.defaultDeviceId));
|
signalStore, SignalProtocolAddress(source, defaultDeviceId));
|
||||||
Map data = jsonDecode(msg);
|
Map data = jsonDecode(msg);
|
||||||
if (data["type"] == CiphertextMessage.prekeyType) {
|
if (data["type"] == CiphertextMessage.prekeyType) {
|
||||||
PreKeySignalMessage pre =
|
PreKeySignalMessage pre =
|
||||||
|
|
@ -89,156 +84,3 @@ class SignalDataModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int userIdToRegistrationId(List<int> userId) {
|
|
||||||
int result = 0;
|
|
||||||
for (int i = 8; i < 16; i++) {
|
|
||||||
result = (result << 8) | userId[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
String uint8ListToHex(List<int> list) {
|
|
||||||
final StringBuffer hexBuffer = StringBuffer();
|
|
||||||
for (int byte in list) {
|
|
||||||
hexBuffer.write(byte.toRadixString(16).padLeft(2, '0'));
|
|
||||||
}
|
|
||||||
return hexBuffer.toString().toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SignalHelper {
|
|
||||||
static const int defaultDeviceId = 1;
|
|
||||||
|
|
||||||
static Future<ECPrivateKey?> getPrivateKey() async {
|
|
||||||
final signalIdentity = await getSignalIdentity();
|
|
||||||
if (signalIdentity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IdentityKeyPair identityKeyPair =
|
|
||||||
IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List);
|
|
||||||
|
|
||||||
return identityKeyPair.getPrivateKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<bool> addNewContact(Response_UserData userData) async {
|
|
||||||
final Int64 userId = userData.userId;
|
|
||||||
|
|
||||||
SignalProtocolAddress targetAddress =
|
|
||||||
SignalProtocolAddress(userId.toString(), SignalHelper.defaultDeviceId);
|
|
||||||
|
|
||||||
SignalProtocolStore? signalStore = await SignalHelper.getSignalStore();
|
|
||||||
if (signalStore == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionBuilder sessionBuilder =
|
|
||||||
SessionBuilder.fromSignalStore(signalStore, targetAddress);
|
|
||||||
|
|
||||||
ECPublicKey? tempPrePublicKey;
|
|
||||||
int? tempPreKeyId;
|
|
||||||
if (userData.prekeys.isNotEmpty) {
|
|
||||||
tempPrePublicKey = Curve.decodePoint(
|
|
||||||
DjbECPublicKey(Uint8List.fromList(userData.prekeys.first.prekey))
|
|
||||||
.serialize(),
|
|
||||||
1);
|
|
||||||
tempPreKeyId = userData.prekeys.first.id.toInt();
|
|
||||||
}
|
|
||||||
// Signed pre key calculation
|
|
||||||
int tempSignedPreKeyId = userData.signedPrekeyId.toInt();
|
|
||||||
// Map? tempSignedPreKey = remoteBundle["signedPreKey"];
|
|
||||||
ECPublicKey? tempSignedPreKeyPublic;
|
|
||||||
Uint8List? tempSignedPreKeySignature;
|
|
||||||
// if (tempSignedPreKey != null) {
|
|
||||||
tempSignedPreKeyPublic = Curve.decodePoint(
|
|
||||||
DjbECPublicKey(Uint8List.fromList(userData.signedPrekey)).serialize(),
|
|
||||||
1);
|
|
||||||
tempSignedPreKeySignature =
|
|
||||||
Uint8List.fromList(userData.signedPrekeySignature);
|
|
||||||
// }
|
|
||||||
// Identity key calculation
|
|
||||||
IdentityKey tempIdentityKey = IdentityKey(Curve.decodePoint(
|
|
||||||
DjbECPublicKey(Uint8List.fromList(userData.publicIdentityKey))
|
|
||||||
.serialize(),
|
|
||||||
1));
|
|
||||||
PreKeyBundle preKeyBundle = PreKeyBundle(
|
|
||||||
userData.userId.toInt(),
|
|
||||||
1,
|
|
||||||
tempPreKeyId,
|
|
||||||
tempPrePublicKey,
|
|
||||||
tempSignedPreKeyId,
|
|
||||||
tempSignedPreKeyPublic,
|
|
||||||
tempSignedPreKeySignature,
|
|
||||||
tempIdentityKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await sessionBuilder.processPreKeyBundle(preKeyBundle);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
Logger("signal_helper").shout("Error: $e");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
|
||||||
return await getSignalStoreFromIdentity((await getSignalIdentity())!);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<SignalIdentity?> getSignalIdentity() async {
|
|
||||||
final storage = getSecureStorage();
|
|
||||||
final signalIdentityJson = await storage.read(key: "signal_identity");
|
|
||||||
if (signalIdentityJson == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalIdentity.fromJson(jsonDecode(signalIdentityJson));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<ConnectSignalProtocolStore> getSignalStoreFromIdentity(
|
|
||||||
SignalIdentity signalIdentity) async {
|
|
||||||
final IdentityKeyPair identityKeyPair =
|
|
||||||
IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List);
|
|
||||||
|
|
||||||
return ConnectSignalProtocolStore(
|
|
||||||
identityKeyPair, signalIdentity.registrationId.toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<PreKeyRecord>> getPreKeys() async {
|
|
||||||
final preKeys = generatePreKeys(0, 200);
|
|
||||||
final signalStore = await getSignalStore();
|
|
||||||
if (signalStore == null) return [];
|
|
||||||
for (final p in preKeys) {
|
|
||||||
await signalStore.preKeyStore.storePreKey(p.id, p);
|
|
||||||
}
|
|
||||||
return preKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future createIfNotExistsSignalIdentity() async {
|
|
||||||
final storage = getSecureStorage();
|
|
||||||
|
|
||||||
final signalIdentity = await storage.read(key: "signal_identity");
|
|
||||||
if (signalIdentity != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final identityKeyPair = generateIdentityKeyPair();
|
|
||||||
final registrationId = generateRegistrationId(true);
|
|
||||||
|
|
||||||
ConnectSignalProtocolStore signalStore =
|
|
||||||
ConnectSignalProtocolStore(identityKeyPair, registrationId);
|
|
||||||
|
|
||||||
final signedPreKey =
|
|
||||||
generateSignedPreKey(identityKeyPair, SignalHelper.defaultDeviceId);
|
|
||||||
|
|
||||||
await signalStore.signedPreKeyStore
|
|
||||||
.storeSignedPreKey(signedPreKey.id, signedPreKey);
|
|
||||||
|
|
||||||
final storedSignalIdentity = SignalIdentity(
|
|
||||||
identityKeyPairU8List: identityKeyPair.serialize(),
|
|
||||||
registrationId: Int64(registrationId));
|
|
||||||
|
|
||||||
await storage.write(
|
|
||||||
key: "signal_identity", value: jsonEncode(storedSignalIdentity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ import 'dart:convert';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/main.dart';
|
import 'package:twonly/main.dart';
|
||||||
import 'package:twonly/src/model/contacts_model.dart';
|
import 'package:twonly/src/model/contacts_model.dart';
|
||||||
import 'package:twonly/src/signal/signal_helper.dart';
|
|
||||||
import 'package:twonly/src/providers/api_provider.dart';
|
import 'package:twonly/src/providers/api_provider.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/model/user_data_json.dart';
|
// ignore: library_prefixes
|
||||||
|
import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
||||||
|
import 'package:twonly/src/model/json/user_data.dart';
|
||||||
|
|
||||||
Future<bool> addNewContact(String username) async {
|
Future<bool> addNewContact(String username) async {
|
||||||
final res = await apiProvider.getUserData(username);
|
final res = await apiProvider.getUserData(username);
|
||||||
|
|
|
||||||
31
lib/src/utils/json.dart
Normal file
31
lib/src/utils/json.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class Int64Converter implements JsonConverter<Int64, String> {
|
||||||
|
const Int64Converter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Int64 fromJson(String json) {
|
||||||
|
return Int64.parseInt(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson(Int64 object) {
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Uint8ListConverter implements JsonConverter<Uint8List, String> {
|
||||||
|
const Uint8ListConverter();
|
||||||
|
@override
|
||||||
|
Uint8List fromJson(String json) {
|
||||||
|
return base64Decode(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson(Uint8List object) {
|
||||||
|
return base64Encode(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
142
lib/src/utils/signal.dart
Normal file
142
lib/src/utils/signal.dart
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:twonly/src/model/json/signal_identity.dart';
|
||||||
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
||||||
|
import 'package:twonly/src/signal/connect_signal_protocol_store.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
const int defaultDeviceId = 1;
|
||||||
|
|
||||||
|
Future<ECPrivateKey?> getPrivateKey() async {
|
||||||
|
final signalIdentity = await getSignalIdentity();
|
||||||
|
if (signalIdentity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final IdentityKeyPair identityKeyPair =
|
||||||
|
IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List);
|
||||||
|
|
||||||
|
return identityKeyPair.getPrivateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> addNewContact(Response_UserData userData) async {
|
||||||
|
final Int64 userId = userData.userId;
|
||||||
|
|
||||||
|
SignalProtocolAddress targetAddress =
|
||||||
|
SignalProtocolAddress(userId.toString(), defaultDeviceId);
|
||||||
|
|
||||||
|
SignalProtocolStore? signalStore = await getSignalStore();
|
||||||
|
if (signalStore == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionBuilder sessionBuilder =
|
||||||
|
SessionBuilder.fromSignalStore(signalStore, targetAddress);
|
||||||
|
|
||||||
|
ECPublicKey? tempPrePublicKey;
|
||||||
|
int? tempPreKeyId;
|
||||||
|
if (userData.prekeys.isNotEmpty) {
|
||||||
|
tempPrePublicKey = Curve.decodePoint(
|
||||||
|
DjbECPublicKey(Uint8List.fromList(userData.prekeys.first.prekey))
|
||||||
|
.serialize(),
|
||||||
|
1);
|
||||||
|
tempPreKeyId = userData.prekeys.first.id.toInt();
|
||||||
|
}
|
||||||
|
// Signed pre key calculation
|
||||||
|
int tempSignedPreKeyId = userData.signedPrekeyId.toInt();
|
||||||
|
// Map? tempSignedPreKey = remoteBundle["signedPreKey"];
|
||||||
|
ECPublicKey? tempSignedPreKeyPublic;
|
||||||
|
Uint8List? tempSignedPreKeySignature;
|
||||||
|
// if (tempSignedPreKey != null) {
|
||||||
|
tempSignedPreKeyPublic = Curve.decodePoint(
|
||||||
|
DjbECPublicKey(Uint8List.fromList(userData.signedPrekey)).serialize(), 1);
|
||||||
|
tempSignedPreKeySignature =
|
||||||
|
Uint8List.fromList(userData.signedPrekeySignature);
|
||||||
|
// }
|
||||||
|
// Identity key calculation
|
||||||
|
IdentityKey tempIdentityKey = IdentityKey(Curve.decodePoint(
|
||||||
|
DjbECPublicKey(Uint8List.fromList(userData.publicIdentityKey))
|
||||||
|
.serialize(),
|
||||||
|
1));
|
||||||
|
PreKeyBundle preKeyBundle = PreKeyBundle(
|
||||||
|
userData.userId.toInt(),
|
||||||
|
1,
|
||||||
|
tempPreKeyId,
|
||||||
|
tempPrePublicKey,
|
||||||
|
tempSignedPreKeyId,
|
||||||
|
tempSignedPreKeyPublic,
|
||||||
|
tempSignedPreKeySignature,
|
||||||
|
tempIdentityKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sessionBuilder.processPreKeyBundle(preKeyBundle);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
Logger("signal_helper").shout("Error: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
||||||
|
return await getSignalStoreFromIdentity((await getSignalIdentity())!);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SignalIdentity?> getSignalIdentity() async {
|
||||||
|
final storage = getSecureStorage();
|
||||||
|
final signalIdentityJson = await storage.read(key: "signal_identity");
|
||||||
|
if (signalIdentityJson == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SignalIdentity.fromJson(jsonDecode(signalIdentityJson));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ConnectSignalProtocolStore> getSignalStoreFromIdentity(
|
||||||
|
SignalIdentity signalIdentity) async {
|
||||||
|
final IdentityKeyPair identityKeyPair =
|
||||||
|
IdentityKeyPair.fromSerialized(signalIdentity.identityKeyPairU8List);
|
||||||
|
|
||||||
|
return ConnectSignalProtocolStore(
|
||||||
|
identityKeyPair, signalIdentity.registrationId.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<PreKeyRecord>> getPreKeys() async {
|
||||||
|
final preKeys = generatePreKeys(0, 200);
|
||||||
|
final signalStore = await getSignalStore();
|
||||||
|
if (signalStore == null) return [];
|
||||||
|
for (final p in preKeys) {
|
||||||
|
await signalStore.preKeyStore.storePreKey(p.id, p);
|
||||||
|
}
|
||||||
|
return preKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future createIfNotExistsSignalIdentity() async {
|
||||||
|
final storage = getSecureStorage();
|
||||||
|
|
||||||
|
final signalIdentity = await storage.read(key: "signal_identity");
|
||||||
|
if (signalIdentity != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final identityKeyPair = generateIdentityKeyPair();
|
||||||
|
final registrationId = generateRegistrationId(true);
|
||||||
|
|
||||||
|
ConnectSignalProtocolStore signalStore =
|
||||||
|
ConnectSignalProtocolStore(identityKeyPair, registrationId);
|
||||||
|
|
||||||
|
final signedPreKey = generateSignedPreKey(identityKeyPair, defaultDeviceId);
|
||||||
|
|
||||||
|
await signalStore.signedPreKeyStore
|
||||||
|
.storeSignedPreKey(signedPreKey.id, signedPreKey);
|
||||||
|
|
||||||
|
final storedSignalIdentity = SignalIdentity(
|
||||||
|
identityKeyPairU8List: identityKeyPair.serialize(),
|
||||||
|
registrationId: Int64(registrationId));
|
||||||
|
|
||||||
|
await storage.write(
|
||||||
|
key: "signal_identity", value: jsonEncode(storedSignalIdentity));
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/main.dart';
|
import 'package:twonly/main.dart';
|
||||||
import 'package:twonly/src/model/user_data_json.dart';
|
import 'package:twonly/src/model/json/user_data.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
Future<bool> isUserCreated() async {
|
Future<bool> isUserCreated() async {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:twonly/src/model/user_data_json.dart';
|
import 'package:twonly/src/model/json/user_data.dart';
|
||||||
import 'package:restart_app/restart_app.dart';
|
import 'package:restart_app/restart_app.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/settings/settings_controller.dart';
|
import 'package:twonly/src/settings/settings_controller.dart';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue