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/keys.dart';
|
||||
export 'src/omemo/bundle.dart';
|
||||
export 'src/omemo/device.dart';
|
||||
export 'src/omemo/sessionmanager.dart';
|
||||
export 'src/x3dh/x3dh.dart';
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
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/tweetnacl.dart';
|
||||
|
||||
@ -43,11 +45,13 @@ class OmemoPublicKey {
|
||||
|
||||
SimplePublicKey asPublicKey() => _pubkey;
|
||||
|
||||
/// Convert the public key into a [SimpleKeyPairData] with a stub private key. Useful
|
||||
/// for when cryptography calls for a KeyPair, but only uses the public key.
|
||||
//SimpleKeyPairData asPseudoKeypair() {
|
||||
//
|
||||
//}
|
||||
@visibleForTesting
|
||||
Future<bool> equals(OmemoPublicKey key) async {
|
||||
return type == key.type && listsEqual(
|
||||
await getBytes(),
|
||||
await key.getBytes(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OmemoPrivateKey {
|
||||
@ -69,16 +73,39 @@ class OmemoPrivateKey {
|
||||
|
||||
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
|
||||
class OmemoKeyPair {
|
||||
|
||||
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 {
|
||||
assert(type == KeyPairType.ed25519 || type == KeyPairType.x25519, 'Keypair must be either Ed25519 or X25519');
|
||||
|
||||
@ -103,6 +130,10 @@ class OmemoKeyPair {
|
||||
);
|
||||
}
|
||||
|
||||
final KeyPairType type;
|
||||
final OmemoPublicKey pk;
|
||||
final OmemoPrivateKey sk;
|
||||
|
||||
/// Return the bytes that comprise the public key.
|
||||
Future<OmemoKeyPair> toCurve25519() async {
|
||||
assert(type == KeyPairType.ed25519, 'Cannot convert non-Ed25519 keypair to X25519');
|
||||
@ -121,4 +152,11 @@ class OmemoKeyPair {
|
||||
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);
|
||||
|
||||
/// 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.
|
||||
static Future<Device> generateNewDevice({ int opkAmount = 100 }) async {
|
||||
final id = generateRandom32BitNumber();
|
||||
@ -92,4 +142,28 @@ class Device {
|
||||
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