187 lines
5.4 KiB
Dart
187 lines
5.4 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:x25519/x25519.dart' as x25519;
|
|
|
|
import '../invalid_key_exception.dart';
|
|
import '../util/key_helper.dart';
|
|
import 'djb_ec_private_key.dart';
|
|
import 'djb_ec_public_key.dart';
|
|
import 'ec_key_pair.dart';
|
|
import 'ec_private_key.dart';
|
|
import 'ec_public_key.dart';
|
|
import 'ed25519.dart';
|
|
|
|
typedef KeyPairGenerator = GeneratedKeyPair Function();
|
|
typedef AgreementCalculator = Uint8List Function(Uint8List, Uint8List);
|
|
|
|
class GeneratedKeyPair {
|
|
GeneratedKeyPair(this.private, this.public);
|
|
|
|
final Uint8List private;
|
|
final Uint8List public;
|
|
}
|
|
|
|
class Curve {
|
|
static const int djbType = 0x05;
|
|
|
|
static KeyPairGenerator? keyPairGenerator;
|
|
static AgreementCalculator? agreementCalculator;
|
|
|
|
static ECKeyPair generateKeyPair() {
|
|
final x25519.KeyPair keyPair;
|
|
final generator = keyPairGenerator;
|
|
if (generator != null) {
|
|
final kp = generator();
|
|
keyPair = x25519.KeyPair(privateKey: kp.private, publicKey: kp.public);
|
|
} else {
|
|
keyPair = x25519.generateKeyPair();
|
|
}
|
|
|
|
return ECKeyPair(DjbECPublicKey(Uint8List.fromList(keyPair.publicKey)),
|
|
DjbECPrivateKey(Uint8List.fromList(keyPair.privateKey)));
|
|
}
|
|
|
|
static ECKeyPair generateKeyPairFromPrivate(List<int> private) {
|
|
if (private.length != 32) {
|
|
throw InvalidKeyException(
|
|
'Invalid private key length: ${private.length}');
|
|
}
|
|
final public = List<int>.filled(32, 0);
|
|
|
|
private[0] &= 248;
|
|
private[31] &= 127;
|
|
private[31] |= 64;
|
|
|
|
x25519.ScalarBaseMult(public, private);
|
|
|
|
return ECKeyPair(DjbECPublicKey(Uint8List.fromList(public)),
|
|
DjbECPrivateKey(Uint8List.fromList(private)));
|
|
}
|
|
|
|
static ECPublicKey decodePointList(List<int> bytes, int offset) =>
|
|
decodePoint(Uint8List.fromList(bytes), offset);
|
|
|
|
static ECPublicKey decodePoint(Uint8List bytes, int offset) {
|
|
if (bytes.length - offset < 1) {
|
|
throw InvalidKeyException('No key type identifier');
|
|
}
|
|
|
|
final type = bytes[offset] & 0xFF;
|
|
|
|
switch (type) {
|
|
case Curve.djbType:
|
|
if (bytes.length - offset < 33) {
|
|
throw InvalidKeyException('Bad key length: ${bytes.length}');
|
|
}
|
|
|
|
final keyBytes = Uint8List(32);
|
|
arraycopy(bytes, offset + 1, keyBytes, 0, keyBytes.length);
|
|
return DjbECPublicKey(keyBytes);
|
|
default:
|
|
throw InvalidKeyException('Bad key type: $type');
|
|
}
|
|
}
|
|
|
|
static void arraycopy(
|
|
List<int> src, int srcPos, List<int> dest, int destPos, int length) {
|
|
dest.setRange(destPos, length + destPos, src, srcPos);
|
|
}
|
|
|
|
static ECPrivateKey decodePrivatePoint(Uint8List bytes) =>
|
|
DjbECPrivateKey(bytes);
|
|
|
|
static Uint8List calculateAgreement(
|
|
ECPublicKey? publicKey, ECPrivateKey? privateKey) {
|
|
if (publicKey == null) {
|
|
throw Exception('publicKey value is null');
|
|
}
|
|
|
|
if (privateKey == null) {
|
|
throw Exception('privateKey value is null');
|
|
}
|
|
if (publicKey.getType() != privateKey.getType()) {
|
|
throw Exception('Public and private keys must be of the same type!');
|
|
}
|
|
|
|
if (publicKey.getType() == djbType) {
|
|
final calculator = agreementCalculator;
|
|
if (calculator != null) {
|
|
return calculator(
|
|
(privateKey as DjbECPrivateKey).privateKey,
|
|
(publicKey as DjbECPublicKey).publicKey,
|
|
);
|
|
}
|
|
|
|
final secretKey = x25519.X25519(
|
|
List<int>.from((privateKey as DjbECPrivateKey).privateKey),
|
|
List<int>.from((publicKey as DjbECPublicKey).publicKey),
|
|
);
|
|
return secretKey;
|
|
} else {
|
|
throw Exception('Unknown type: ${publicKey.getType()}');
|
|
}
|
|
}
|
|
|
|
static bool verifySignature(
|
|
ECPublicKey? signingKey, Uint8List? message, Uint8List? signature) {
|
|
if (signingKey == null || message == null || signature == null) {
|
|
throw InvalidKeyException('Values must not be null');
|
|
}
|
|
|
|
if (signingKey.getType() == djbType) {
|
|
if (signature.length != 64) {
|
|
return false;
|
|
}
|
|
|
|
final publicKey = (signingKey as DjbECPublicKey).publicKey;
|
|
return verifySig(publicKey, message, signature);
|
|
} else {
|
|
throw InvalidKeyException(
|
|
'Unknown Signing Key type${signingKey.getType()}');
|
|
}
|
|
}
|
|
|
|
static Uint8List calculateSignature(
|
|
ECPrivateKey? signingKey, Uint8List? message) {
|
|
if (signingKey == null || message == null) {
|
|
throw Exception('Values must not be null');
|
|
}
|
|
|
|
if (signingKey.getType() == djbType) {
|
|
final privateKey = signingKey.serialize();
|
|
final random = generateRandomBytes();
|
|
|
|
return sign(privateKey, message, random);
|
|
} else {
|
|
throw Exception('Unknown Signing Key type${signingKey.getType()}');
|
|
}
|
|
}
|
|
|
|
static Uint8List calculateVrfSignature(
|
|
ECPrivateKey? signingKey, Uint8List? message) {
|
|
if (signingKey == null || message == null) {
|
|
throw Exception('Values must not be null');
|
|
}
|
|
|
|
if (signingKey.getType() == djbType) {
|
|
// TODO
|
|
} else {
|
|
throw Exception('Unknown Signing Key type${signingKey.getType()}');
|
|
}
|
|
return Uint8List(0);
|
|
}
|
|
|
|
static Uint8List verifyVrfSignature(
|
|
ECPublicKey? signingKey, Uint8List? message, Uint8List? signature) {
|
|
if (signingKey == null || message == null || signature == null) {
|
|
throw Exception('Values must not be null');
|
|
}
|
|
|
|
if (signingKey.getType() == djbType) {
|
|
// TODO
|
|
} else {
|
|
throw Exception('Unknown Signing Key type${signingKey.getType()}');
|
|
}
|
|
return Uint8List(0);
|
|
}
|
|
}
|