2022-08-02 13:03:58 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:cryptography/cryptography.dart';
|
2022-08-05 15:32:59 +00:00
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
import 'package:omemo_dart/src/helpers.dart';
|
2022-08-02 13:03:58 +00:00
|
|
|
import 'package:pinenacl/api.dart';
|
|
|
|
import 'package:pinenacl/tweetnacl.dart';
|
|
|
|
|
|
|
|
const privateKeyLength = 32;
|
|
|
|
const publicKeyLength = 32;
|
|
|
|
|
|
|
|
class OmemoPublicKey {
|
|
|
|
|
|
|
|
const OmemoPublicKey(this._pubkey);
|
|
|
|
|
|
|
|
factory OmemoPublicKey.fromBytes(List<int> bytes, KeyPairType type) {
|
|
|
|
return OmemoPublicKey(
|
|
|
|
SimplePublicKey(
|
|
|
|
bytes,
|
|
|
|
type: type,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2022-08-02 13:10:31 +00:00
|
|
|
|
|
|
|
final SimplePublicKey _pubkey;
|
2022-08-02 13:03:58 +00:00
|
|
|
|
|
|
|
KeyPairType get type => _pubkey.type;
|
|
|
|
|
|
|
|
/// Return the bytes that comprise the public key.
|
|
|
|
Future<List<int>> getBytes() async => _pubkey.bytes;
|
|
|
|
|
|
|
|
/// Returns the public key encoded as base64.
|
|
|
|
Future<String> asBase64() async => base64Encode(_pubkey.bytes);
|
|
|
|
|
|
|
|
Future<OmemoPublicKey> toCurve25519() async {
|
|
|
|
assert(type == KeyPairType.ed25519, 'Cannot convert non-Ed25519 public key to X25519');
|
|
|
|
|
|
|
|
final pkc = Uint8List(publicKeyLength);
|
|
|
|
TweetNaClExt.crypto_sign_ed25519_pk_to_x25519_pk(
|
|
|
|
pkc,
|
|
|
|
Uint8List.fromList(await getBytes()),
|
|
|
|
);
|
|
|
|
|
|
|
|
return OmemoPublicKey(SimplePublicKey(List<int>.from(pkc), type: KeyPairType.x25519));
|
|
|
|
}
|
|
|
|
|
|
|
|
SimplePublicKey asPublicKey() => _pubkey;
|
2022-08-05 15:32:59 +00:00
|
|
|
|
|
|
|
@visibleForTesting
|
|
|
|
Future<bool> equals(OmemoPublicKey key) async {
|
|
|
|
return type == key.type && listsEqual(
|
|
|
|
await getBytes(),
|
|
|
|
await key.getBytes(),
|
|
|
|
);
|
|
|
|
}
|
2022-08-02 13:03:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class OmemoPrivateKey {
|
|
|
|
|
|
|
|
const OmemoPrivateKey(this._privkey, this.type);
|
|
|
|
final List<int> _privkey;
|
|
|
|
final KeyPairType type;
|
|
|
|
|
|
|
|
Future<List<int>> getBytes() async => _privkey;
|
|
|
|
|
|
|
|
Future<OmemoPrivateKey> toCurve25519() async {
|
|
|
|
assert(type == KeyPairType.ed25519, 'Cannot convert non-Ed25519 private key to X25519');
|
|
|
|
|
|
|
|
final skc = Uint8List(privateKeyLength);
|
|
|
|
TweetNaClExt.crypto_sign_ed25519_sk_to_x25519_sk(
|
|
|
|
skc,
|
|
|
|
Uint8List.fromList(await getBytes()),
|
|
|
|
);
|
|
|
|
|
|
|
|
return OmemoPrivateKey(List<int>.from(skc), KeyPairType.x25519);
|
|
|
|
}
|
2022-08-05 15:32:59 +00:00
|
|
|
|
|
|
|
@visibleForTesting
|
|
|
|
Future<bool> equals(OmemoPrivateKey key) async {
|
|
|
|
return type == key.type && listsEqual(
|
|
|
|
await getBytes(),
|
|
|
|
await key.getBytes(),
|
|
|
|
);
|
|
|
|
}
|
2022-08-02 13:03:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A generic wrapper class for both Ed25519 and X25519 keypairs
|
|
|
|
class OmemoKeyPair {
|
|
|
|
|
|
|
|
const OmemoKeyPair(this.pk, this.sk, this.type);
|
|
|
|
|
2022-08-05 15:32:59 +00:00
|
|
|
/// 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.
|
2022-08-02 13:03:58 +00:00
|
|
|
static Future<OmemoKeyPair> generateNewPair(KeyPairType type) async {
|
2022-08-02 13:10:31 +00:00
|
|
|
assert(type == KeyPairType.ed25519 || type == KeyPairType.x25519, 'Keypair must be either Ed25519 or X25519');
|
2022-08-02 13:03:58 +00:00
|
|
|
|
|
|
|
SimpleKeyPair kp;
|
|
|
|
if (type == KeyPairType.ed25519) {
|
|
|
|
final ed = Ed25519();
|
|
|
|
kp = await ed.newKeyPair();
|
|
|
|
} else if (type == KeyPairType.x25519) {
|
|
|
|
final x = Cryptography.instance.x25519();
|
|
|
|
kp = await x.newKeyPair();
|
|
|
|
} else {
|
|
|
|
// Should never happen
|
|
|
|
throw Exception();
|
|
|
|
}
|
|
|
|
|
|
|
|
final kpd = await kp.extract();
|
|
|
|
|
|
|
|
return OmemoKeyPair(
|
|
|
|
OmemoPublicKey(await kp.extractPublicKey()),
|
|
|
|
OmemoPrivateKey(await kpd.extractPrivateKeyBytes(), type),
|
|
|
|
type,
|
|
|
|
);
|
|
|
|
}
|
2022-08-05 15:32:59 +00:00
|
|
|
|
|
|
|
final KeyPairType type;
|
|
|
|
final OmemoPublicKey pk;
|
|
|
|
final OmemoPrivateKey sk;
|
2022-08-02 13:03:58 +00:00
|
|
|
|
|
|
|
/// Return the bytes that comprise the public key.
|
|
|
|
Future<OmemoKeyPair> toCurve25519() async {
|
|
|
|
assert(type == KeyPairType.ed25519, 'Cannot convert non-Ed25519 keypair to X25519');
|
|
|
|
|
|
|
|
return OmemoKeyPair(
|
|
|
|
await pk.toCurve25519(),
|
|
|
|
await sk.toCurve25519(),
|
|
|
|
KeyPairType.x25519,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<SimpleKeyPairData> asKeyPair() async {
|
|
|
|
return SimpleKeyPairData(
|
|
|
|
await sk.getBytes(),
|
|
|
|
publicKey: pk.asPublicKey(),
|
|
|
|
type: type,
|
|
|
|
);
|
|
|
|
}
|
2022-08-05 15:32:59 +00:00
|
|
|
|
|
|
|
@visibleForTesting
|
|
|
|
Future<bool> equals(OmemoKeyPair pair) async {
|
|
|
|
return type == pair.type &&
|
|
|
|
await pk.equals(pair.pk) &&
|
|
|
|
await sk.equals(pair.sk);
|
|
|
|
}
|
2022-08-02 13:03:58 +00:00
|
|
|
}
|