Merge branch 'licy183-ecelgamal' into master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5da7a58..92a2cbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
Changelog
=========
+#### Version 3.3.0 (2021-08-12)
+
+* ECElGamal Encryptor and Decryptor
+
#### Version 3.2.0 (2021-07-29)
* Better ASN1 Dump
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 6bbac2b..1d4cc8e 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -17,4 +17,5 @@
Yurii Baryshev <https://github.com/YuriiBaryshev>
Graciliano M. Passos <https://github.com/gmpassos>
Netthaw <https://github.com/Netthaw>
+Uchiha Kakashi <https://github.com/licy183>
diff --git a/lib/asymmetric/ec_elgamal.dart b/lib/asymmetric/ec_elgamal.dart
new file mode 100644
index 0000000..2412f3f
--- /dev/null
+++ b/lib/asymmetric/ec_elgamal.dart
@@ -0,0 +1,81 @@
+// See file LICENSE for more information.
+
+library impl.asymmetric.ecc.elgamal;
+
+import 'package:pointycastle/api.dart';
+import 'package:pointycastle/export.dart';
+import 'package:pointycastle/src/platform_check/platform_check.dart';
+
+BigInt _generateK(BigInt n, SecureRandom random) {
+ var nBitLength = n.bitLength;
+ BigInt k;
+ do {
+ k = random.nextBigInteger(nBitLength);
+ } while ((k == BigInt.zero) || (k.compareTo(n) >= 0));
+ return k;
+}
+
+SecureRandom _newSecureRandom() => FortunaRandom()
+ ..seed(KeyParameter(Platform.instance.platformEntropySource().getBytes(32)));
+
+/// The basic ElGamal encryptor using Elliptic Curve
+class ECElGamalEncryptor implements ECEncryptor {
+ ECPublicKey? _key;
+ late SecureRandom _random;
+
+ /// Process a single EC [point] using the basic ElGamal algorithm.
+ @override
+ ECPair encrypt(ECPoint point) {
+ if (_key == null) {
+ throw StateError('ECElGamalEncryptor is not initialised');
+ }
+ var key = _key!;
+ var ec = key.parameters!;
+ var k = _generateK(ec.n, _random);
+ return ECPair(
+ (ec.G * k)!,
+ ((key.Q! * k)! + point)!,
+ );
+ }
+
+ @override
+ void init(CipherParameters params) {
+ AsymmetricKeyParameter akparams;
+ if (params is ParametersWithRandom) {
+ akparams = params.parameters as AsymmetricKeyParameter<AsymmetricKey>;
+ _random = params.random;
+ } else {
+ akparams = params as AsymmetricKeyParameter<AsymmetricKey>;
+ _random = _newSecureRandom();
+ }
+ var k = akparams.key as ECAsymmetricKey;
+ if (!(k is ECPublicKey)) {
+ throw ArgumentError('ECPublicKey is required for encryption.');
+ }
+ _key = k;
+ }
+}
+
+/// The basic ElGamal decryptor using Elliptic Curve
+class ECElGamalDecryptor implements ECDecryptor {
+ ECPrivateKey? _key;
+
+ /// Decrypt an EC [pair] producing the original [ECPoint].
+ @override
+ ECPoint decrypt(ECPair pair) {
+ if (_key == null) {
+ throw StateError('ECElGamalEncryptor is not initialised');
+ }
+ return (pair.y - (pair.x * _key!.d)!)!;
+ }
+
+ @override
+ void init(CipherParameters params) {
+ var akparams = params as AsymmetricKeyParameter<AsymmetricKey>;
+ var k = akparams.key as ECAsymmetricKey;
+ if (!(k is ECPrivateKey)) {
+ throw ArgumentError('ECPrivateKey is required for decryption.');
+ }
+ _key = k;
+ }
+}
diff --git a/lib/ecc/api.dart b/lib/ecc/api.dart
index 8ff895d..d16db16 100644
--- a/lib/ecc/api.dart
+++ b/lib/ecc/api.dart
@@ -193,3 +193,36 @@
return r.hashCode + s.hashCode;
}
}
+
+/// A pair of [ECPoint]s.
+class ECPair {
+ final ECPoint x;
+ final ECPoint y;
+
+ const ECPair(this.x, this.y);
+
+ @override
+ bool operator ==(other) {
+ if (other is! ECPair) return false;
+ return (other.x == x) && (other.y == y);
+ }
+
+ @override
+ int get hashCode => x.hashCode + y.hashCode * 37;
+}
+
+/// The encryptor using Elliptic Curve
+abstract class ECEncryptor {
+ ECPair encrypt(ECPoint point);
+
+ /// Initialize the encryptor.
+ void init(CipherParameters params);
+}
+
+/// The decryptor using Elliptic Curve
+abstract class ECDecryptor {
+ ECPoint decrypt(ECPair pair);
+
+ /// Initialize the decryptor.
+ void init(CipherParameters params);
+}
diff --git a/lib/ecc/ecc_fp.dart b/lib/ecc/ecc_fp.dart
index 7a8ab8e..ced7404 100644
--- a/lib/ecc/ecc_fp.dart
+++ b/lib/ecc/ecc_fp.dart
@@ -197,7 +197,7 @@
/// Elliptic curve points over Fp
class ECPoint extends ecc.ECPointBase {
- /// Create a point that encodes with or without point compresion.
+ /// Create a point that encodes with or without point compression.
///
/// @param curve the curve to use
/// @param x affine x co-ordinate
diff --git a/test/asymmetric/ec_elgamal_test.dart b/test/asymmetric/ec_elgamal_test.dart
new file mode 100644
index 0000000..7218bb3
--- /dev/null
+++ b/test/asymmetric/ec_elgamal_test.dart
@@ -0,0 +1,83 @@
+// See file LICENSE for more information.
+
+library test.asymmetric.ec_elgamal_test;
+
+import 'package:pointycastle/asymmetric/ec_elgamal.dart';
+import 'package:pointycastle/ecc/ecc_fp.dart' as fp;
+import 'package:pointycastle/export.dart';
+import 'package:pointycastle/src/platform_check/platform_check.dart';
+import 'package:test/test.dart';
+import 'package:pointycastle/pointycastle.dart';
+
+import '../test/src/helpers.dart';
+
+SecureRandom _newSecureRandom() => FortunaRandom()
+ ..seed(KeyParameter(Platform.instance.platformEntropySource().getBytes(32)));
+
+void main() {
+ var n = BigInt.parse(
+ '6277101735386680763835789423176059013767194773182842284081');
+ var q = BigInt.parse(
+ '6277101735386680763835789423207666416083908700390324961279');
+ var a = BigInt.parse(
+ 'fffffffffffffffffffffffffffffffefffffffffffffffc',
+ radix: 16,
+ );
+ var b = BigInt.parse(
+ '64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1',
+ radix: 16,
+ );
+ var curve = fp.ECCurve(q, a, b);
+ var params = ECDomainParametersImpl(
+ 'test_elgamal',
+ curve,
+ curve.decodePoint(
+ createUint8ListFromHexString(
+ '03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012',
+ ),
+ )!, // G
+ n,
+ );
+ var pubKey = ECPublicKey(
+ curve.decodePoint(
+ createUint8ListFromHexString(
+ '0262b12d60690cdcf330babab6e69763b471f994dd702d16a5'),
+ ), // Q
+ params,
+ );
+ var priKey = ECPrivateKey(
+ BigInt.parse(
+ '651056770906015076056810763456358567190100156695615665659'), // d
+ params,
+ );
+ var secureRandom = _newSecureRandom();
+ var pRandom = ParametersWithRandom<PublicKeyParameter>(
+ PublicKeyParameter(pubKey),
+ secureRandom,
+ );
+
+ test('ECElgamal encrypt and decrypt test: first', () {
+ var value = BigInt.from(123);
+ var data = (priKey.parameters!.G * value)!;
+ var encryptor = ECElGamalEncryptor();
+ encryptor.init(pRandom);
+ var pair = encryptor.encrypt(data);
+ var decryptor = ECElGamalDecryptor();
+ decryptor.init(PrivateKeyParameter(priKey));
+ var result = decryptor.decrypt(pair);
+ expect(data, equals(result));
+ });
+
+ test('ECElgamal encrypt and decrypt test: second', () {
+ var value =
+ _newSecureRandom().nextBigInteger(pubKey.parameters!.n.bitLength - 1);
+ var data = (priKey.parameters!.G * value)!;
+ var encryptor = ECElGamalEncryptor();
+ encryptor.init(pRandom);
+ var pair = encryptor.encrypt(data);
+ var decryptor = ECElGamalDecryptor();
+ decryptor.init(PrivateKeyParameter(priKey));
+ var result = decryptor.decrypt(pair);
+ expect(data, equals(result));
+ });
+}