feat: Allow serialising Device to Json
This commit is contained in:
parent
3a7489a9c3
commit
cd77996db4
@ -6,5 +6,6 @@ export 'src/events.dart';
|
|||||||
export 'src/helpers.dart';
|
export 'src/helpers.dart';
|
||||||
export 'src/keys.dart';
|
export 'src/keys.dart';
|
||||||
export 'src/omemo/bundle.dart';
|
export 'src/omemo/bundle.dart';
|
||||||
|
export 'src/omemo/device.dart';
|
||||||
export 'src/omemo/sessionmanager.dart';
|
export 'src/omemo/sessionmanager.dart';
|
||||||
export 'src/x3dh/x3dh.dart';
|
export 'src/x3dh/x3dh.dart';
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:cryptography/cryptography.dart';
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:omemo_dart/src/helpers.dart';
|
||||||
import 'package:pinenacl/api.dart';
|
import 'package:pinenacl/api.dart';
|
||||||
import 'package:pinenacl/tweetnacl.dart';
|
import 'package:pinenacl/tweetnacl.dart';
|
||||||
|
|
||||||
@ -42,12 +44,14 @@ class OmemoPublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SimplePublicKey asPublicKey() => _pubkey;
|
SimplePublicKey asPublicKey() => _pubkey;
|
||||||
|
|
||||||
/// Convert the public key into a [SimpleKeyPairData] with a stub private key. Useful
|
@visibleForTesting
|
||||||
/// for when cryptography calls for a KeyPair, but only uses the public key.
|
Future<bool> equals(OmemoPublicKey key) async {
|
||||||
//SimpleKeyPairData asPseudoKeypair() {
|
return type == key.type && listsEqual(
|
||||||
//
|
await getBytes(),
|
||||||
//}
|
await key.getBytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OmemoPrivateKey {
|
class OmemoPrivateKey {
|
||||||
@ -69,16 +73,39 @@ class OmemoPrivateKey {
|
|||||||
|
|
||||||
return OmemoPrivateKey(List<int>.from(skc), KeyPairType.x25519);
|
return OmemoPrivateKey(List<int>.from(skc), KeyPairType.x25519);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
Future<bool> equals(OmemoPrivateKey key) async {
|
||||||
|
return type == key.type && listsEqual(
|
||||||
|
await getBytes(),
|
||||||
|
await key.getBytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic wrapper class for both Ed25519 and X25519 keypairs
|
/// A generic wrapper class for both Ed25519 and X25519 keypairs
|
||||||
class OmemoKeyPair {
|
class OmemoKeyPair {
|
||||||
|
|
||||||
const OmemoKeyPair(this.pk, this.sk, this.type);
|
const OmemoKeyPair(this.pk, this.sk, this.type);
|
||||||
final KeyPairType type;
|
|
||||||
final OmemoPublicKey pk;
|
|
||||||
final OmemoPrivateKey sk;
|
|
||||||
|
|
||||||
|
/// Create an OmemoKeyPair just from a [type] and the bytes of the private and public
|
||||||
|
/// key.
|
||||||
|
factory OmemoKeyPair.fromBytes(List<int> publicKey, List<int> privateKey, KeyPairType type) {
|
||||||
|
return OmemoKeyPair(
|
||||||
|
OmemoPublicKey.fromBytes(
|
||||||
|
publicKey,
|
||||||
|
type,
|
||||||
|
),
|
||||||
|
OmemoPrivateKey(
|
||||||
|
privateKey,
|
||||||
|
type,
|
||||||
|
),
|
||||||
|
type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a completely new random OmemoKeyPair of type [type]. [type] must be either
|
||||||
|
/// KeyPairType.ed25519 or KeyPairType.x25519.
|
||||||
static Future<OmemoKeyPair> generateNewPair(KeyPairType type) async {
|
static Future<OmemoKeyPair> generateNewPair(KeyPairType type) async {
|
||||||
assert(type == KeyPairType.ed25519 || type == KeyPairType.x25519, 'Keypair must be either Ed25519 or X25519');
|
assert(type == KeyPairType.ed25519 || type == KeyPairType.x25519, 'Keypair must be either Ed25519 or X25519');
|
||||||
|
|
||||||
@ -102,6 +129,10 @@ class OmemoKeyPair {
|
|||||||
type,
|
type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final KeyPairType type;
|
||||||
|
final OmemoPublicKey pk;
|
||||||
|
final OmemoPrivateKey sk;
|
||||||
|
|
||||||
/// Return the bytes that comprise the public key.
|
/// Return the bytes that comprise the public key.
|
||||||
Future<OmemoKeyPair> toCurve25519() async {
|
Future<OmemoKeyPair> toCurve25519() async {
|
||||||
@ -121,4 +152,11 @@ class OmemoKeyPair {
|
|||||||
type: type,
|
type: type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
Future<bool> equals(OmemoKeyPair pair) async {
|
||||||
|
return type == pair.type &&
|
||||||
|
await pk.equals(pair.pk) &&
|
||||||
|
await sk.equals(pair.sk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,56 @@ class Device {
|
|||||||
|
|
||||||
const Device(this.id, this.ik, this.spk, this.spkId, this.spkSignature, this.opks);
|
const Device(this.id, this.ik, this.spk, this.spkId, this.spkSignature, this.opks);
|
||||||
|
|
||||||
|
/// Deserialize the Device
|
||||||
|
factory Device.fromJson(Map<String, dynamic> data) {
|
||||||
|
// NOTE: We use the way OpenSSH names their keys, meaning that ik is the Identity
|
||||||
|
// Keypair's private key, while ik_pub refers to the Identity Keypair's public
|
||||||
|
// key.
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
'id': 123,
|
||||||
|
'ik': 'base/64/encoded',
|
||||||
|
'ik_pub': 'base/64/encoded',
|
||||||
|
'spk': 'base/64/encoded',
|
||||||
|
'spk_pub': 'base/64/encoded',
|
||||||
|
'spk_id': 123,
|
||||||
|
'spk_sig': 'base/64/encoded',
|
||||||
|
'opks': [
|
||||||
|
{
|
||||||
|
'id': 0,
|
||||||
|
'public': 'base/64/encoded',
|
||||||
|
'private': 'base/64/encoded'
|
||||||
|
}, ...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
final opks = <int, OmemoKeyPair>{};
|
||||||
|
for (final opk in data['opks']! as List<Map<String, dynamic>>) {
|
||||||
|
opks[opk['id']! as int] = OmemoKeyPair.fromBytes(
|
||||||
|
base64.decode(opk['public']! as String),
|
||||||
|
base64.decode(opk['private']! as String),
|
||||||
|
KeyPairType.x25519,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Device(
|
||||||
|
data['id']! as int,
|
||||||
|
OmemoKeyPair.fromBytes(
|
||||||
|
base64.decode(data['ik_pub']! as String),
|
||||||
|
base64.decode(data['ik']! as String),
|
||||||
|
KeyPairType.ed25519,
|
||||||
|
),
|
||||||
|
OmemoKeyPair.fromBytes(
|
||||||
|
base64.decode(data['spk_pub']! as String),
|
||||||
|
base64.decode(data['spk']! as String),
|
||||||
|
KeyPairType.x25519,
|
||||||
|
),
|
||||||
|
data['spk_id']! as int,
|
||||||
|
base64.decode(data['spk_sig']! as String),
|
||||||
|
opks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a completely new device, i.e. cryptographic identity.
|
/// Generate a completely new device, i.e. cryptographic identity.
|
||||||
static Future<Device> generateNewDevice({ int opkAmount = 100 }) async {
|
static Future<Device> generateNewDevice({ int opkAmount = 100 }) async {
|
||||||
final id = generateRandom32BitNumber();
|
final id = generateRandom32BitNumber();
|
||||||
@ -92,4 +142,28 @@ class Device {
|
|||||||
encodedOpks,
|
encodedOpks,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serialise the device information.
|
||||||
|
Future<Map<String, dynamic>> toJson() async {
|
||||||
|
/// Serialise the OPKs
|
||||||
|
final serialisedOpks = List<Map<String, dynamic>>.empty(growable: true);
|
||||||
|
for (final entry in opks.entries) {
|
||||||
|
serialisedOpks.add({
|
||||||
|
'id': entry.key,
|
||||||
|
'public': base64.encode(await entry.value.pk.getBytes()),
|
||||||
|
'private': base64.encode(await entry.value.sk.getBytes()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'ik': base64.encode(await ik.sk.getBytes()),
|
||||||
|
'ik_pub': base64.encode(await ik.pk.getBytes()),
|
||||||
|
'spk': base64.encode(await spk.sk.getBytes()),
|
||||||
|
'spk_pub': base64.encode(await spk.pk.getBytes()),
|
||||||
|
'spk_id': spkId,
|
||||||
|
'spk_sig': base64.encode(spkSignature),
|
||||||
|
'opks': serialisedOpks,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
24
test/serialisation_test.dart
Normal file
24
test/serialisation_test.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:omemo_dart/omemo_dart.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('Test serialising and deserialising Device', () async {
|
||||||
|
// Generate a random session
|
||||||
|
final oldSession = await OmemoSessionManager.generateNewIdentity(opkAmount: 1);
|
||||||
|
final oldDevice = await oldSession.getDevice();
|
||||||
|
final serialised = await oldDevice.toJson();
|
||||||
|
|
||||||
|
final newDevice = Device.fromJson(serialised);
|
||||||
|
expect(oldDevice.id, newDevice.id);
|
||||||
|
expect(await oldDevice.ik.equals(newDevice.ik), true);
|
||||||
|
expect(await oldDevice.spk.equals(newDevice.spk), true);
|
||||||
|
expect(listsEqual(oldDevice.spkSignature, newDevice.spkSignature), true);
|
||||||
|
expect(oldDevice.spkId, newDevice.spkId);
|
||||||
|
|
||||||
|
// Check the Ontime-Prekeys
|
||||||
|
expect(oldDevice.opks.length, newDevice.opks.length);
|
||||||
|
for (final entry in oldDevice.opks.entries) {
|
||||||
|
expect(await newDevice.opks[entry.key]!.equals(entry.value), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user