feat: Add managing our own keys

This commit is contained in:
PapaTutuWawa 2022-08-04 13:19:27 +02:00
parent 27b1931629
commit e34e0cc7fb
3 changed files with 118 additions and 1 deletions

View File

@ -34,3 +34,8 @@ List<int> generateRandomBytes(int length) {
return bytes; return bytes;
} }
/// Generate a random number between 0 inclusive and 2**32 exclusive (2**32 - 1 inclusive).
int generateRandom32BitNumber() {
return Random.secure().nextInt(4294967295 /*pow(2, 32) - 1*/);
}

101
lib/src/omemo/device.dart Normal file
View File

@ -0,0 +1,101 @@
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
import 'package:meta/meta.dart';
import 'package:omemo_dart/src/helpers.dart';
import 'package:omemo_dart/src/keys.dart';
import 'package:omemo_dart/src/omemo/bundle.dart';
import 'package:omemo_dart/src/x3dh/x3dh.dart';
/// This class represents an OmemoBundle but with all keypairs belonging to the keys
@immutable
class Device {
const Device(this.id, this.ik, this.spk, this.spkId, this.spkSignature, this.opks);
/// Generate a completely new device, i.e. cryptographic identity.
static Future<Device> generateNewDevice({ int opkAmount = 100 }) async {
final id = generateRandom32BitNumber();
final ik = await OmemoKeyPair.generateNewPair(KeyPairType.ed25519);
final spk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
final spkId = generateRandom32BitNumber();
final signature = await sig(ik, await spk.pk.getBytes());
final opks = <String, OmemoKeyPair>{};
for (var i = 0; i < opkAmount; i++) {
opks[i.toString()] = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
}
return Device(id.toString(), ik, spk, spkId.toString(), signature, opks);
}
/// The device Id
final String id;
/// The identity key
final OmemoKeyPair ik;
/// The signed prekey...
final OmemoKeyPair spk;
/// ...its Id, ...
final String spkId;
/// ...and its signature
final List<int> spkSignature;
/// Map of an id to the associated Onetime-Prekey
final Map<String, OmemoKeyPair> opks;
/// This replaces the Onetime-Prekey with id [id] with a completely new one. Returns
/// a new Device object that copies over everything but replaces said key.
Future<Device> replaceOnetimePrekey(String id) async {
final newOpk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
return Device(
id,
ik,
spk,
spkId,
spkSignature,
opks.map((keyId, opk) {
if (keyId == id) {
return MapEntry(id, newOpk);
}
return MapEntry(id, opk);
}),
);
}
/// This replaces the Signed-Prekey with a completely new one. Returns a new Device object
/// that copies over everything but replaces the Signed-Prekey and its signature.
Future<Device> replaceSignedPrekey() async {
final newSpk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
final newSpkId = generateRandom32BitNumber();
final newSignature = await sig(ik, await newSpk.pk.getBytes());
return Device(
id,
ik,
newSpk,
newSpkId.toString(),
newSignature,
opks,
);
}
Future<OmemoBundle> toBundle() async {
final encodedOpks = <String, String>{};
for (final opkKey in opks.keys) {
encodedOpks[opkKey] = base64.encode(await opks[opkKey]!.pk.getBytes());
}
return OmemoBundle(
id,
base64.encode(await spk.pk.getBytes()),
spkId,
base64.encode(spkSignature),
base64.encode(await ik.pk.getBytes()),
encodedOpks,
);
}
}

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:omemo_dart/src/crypto.dart'; import 'package:omemo_dart/src/crypto.dart';
import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart'; import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart';
import 'package:omemo_dart/src/helpers.dart'; import 'package:omemo_dart/src/helpers.dart';
import 'package:omemo_dart/src/omemo/device.dart';
import 'package:synchronized/synchronized.dart'; import 'package:synchronized/synchronized.dart';
/// The info used for when encrypting the AES key for the actual payload. /// The info used for when encrypting the AES key for the actual payload.
@ -21,7 +22,14 @@ class EncryptionResult {
class OmemoSessionManager { class OmemoSessionManager {
OmemoSessionManager() : _ratchetMap = {}, _deviceMap = {}, _lock = Lock(); OmemoSessionManager(this.device) : _ratchetMap = {}, _deviceMap = {}, _lock = Lock();
/// Generate a new cryptographic identity.
static Future<OmemoSessionManager> generateNewIdentity({ int opkAmount = 100 }) async {
final device = await Device.generateNewDevice(opkAmount: opkAmount);
return OmemoSessionManager(device);
}
/// Lock for _ratchetMap and _bundleMap /// Lock for _ratchetMap and _bundleMap
final Lock _lock; final Lock _lock;
@ -32,6 +40,9 @@ class OmemoSessionManager {
/// Mapping of a bare Jid to its Device Ids /// Mapping of a bare Jid to its Device Ids
final Map<String, List<String>> _deviceMap; final Map<String, List<String>> _deviceMap;
/// Our own keys
Device device;
/// Add a session [ratchet] with the [deviceId] to the internal tracking state. /// Add a session [ratchet] with the [deviceId] to the internal tracking state.
Future<void> addSession(String jid, String deviceId, OmemoDoubleRatchet ratchet) async { Future<void> addSession(String jid, String deviceId, OmemoDoubleRatchet ratchet) async {
await _lock.synchronized(() async { await _lock.synchronized(() async {