From 35613b32ac327ebf3afb11df2b75cc1781f74a50 Mon Sep 17 00:00:00 2001 From: Red Date: Mon, 7 Aug 2023 17:48:34 +0800 Subject: [PATCH] feat: generateKey with seed for ecdsa and eddsa --- .idea/.gitignore | 3 ++ .idea/dart-pg.iml | 13 +++++++++ .idea/misc.xml | 6 ++++ .idea/modules.xml | 8 +++++ .idea/vcs.xml | 6 ++++ lib/src/helpers.dart | 26 +++++++++++++++++ lib/src/openpgp.dart | 2 ++ lib/src/packet/key/key_pair_params.dart | 39 +++++++++++++++++++++---- lib/src/packet/secret_key.dart | 2 ++ lib/src/packet/secret_subkey.dart | 2 ++ lib/src/type/private_key.dart | 6 ++++ 11 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/dart-pg.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/dart-pg.iml b/.idea/dart-pg.iml new file mode 100644 index 00000000..dff73777 --- /dev/null +++ b/.idea/dart-pg.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..639900d1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..3059f289 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/lib/src/helpers.dart b/lib/src/helpers.dart index e1fd1713..1017fb39 100644 --- a/lib/src/helpers.dart +++ b/lib/src/helpers.dart @@ -64,12 +64,38 @@ class Helper { ), ); + static pc.SecureRandom _secureWithSeed(final Uint8List seed) { + if (seed.isNotEmpty) { + return pc.SecureRandom('Fortuna') + ..seed( + pc.KeyParameter( + seed + ), + ); + } else { + return pc.SecureRandom('Fortuna') + ..seed( + pc.KeyParameter( + Uint8List.fromList( + List.generate( + 32, + ((_) => _random.nextInt(0xffffffff)), + ), + ), + ), + ); + } + } + static BigInt readMPI(Uint8List bytes) { final bitLength = bytes.sublist(0, 2).toUint16(); return bytes.sublist(2, ((bitLength + 7) >> 3) + 2).toBigIntWithSign(1); } static pc.SecureRandom secureRandom() => _secureRandom; + static pc.SecureRandom secureWithSeed(final Uint8List seed) { + return _secureWithSeed(seed); + } static Uint8List generatePrefix([ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes256, diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 2b4dddd1..a8d36deb 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -48,6 +48,7 @@ class OpenPGP { final int keyExpirationTime = 0, final String? subkeyPassphrase, final DateTime? date, + required Uint8List seed }) async => PrivateKey.generate( userIDs, @@ -59,6 +60,7 @@ class OpenPGP { keyExpirationTime: keyExpirationTime, subkeyPassphrase: subkeyPassphrase, date: date, + seed: seed ); /// Read an armored & unlock OpenPGP private key with the given passphrase. diff --git a/lib/src/packet/key/key_pair_params.dart b/lib/src/packet/key/key_pair_params.dart index 04e4ad49..b925da0b 100644 --- a/lib/src/packet/key/key_pair_params.dart +++ b/lib/src/packet/key/key_pair_params.dart @@ -36,6 +36,7 @@ class KeyPairParams { final RSAKeySize rsaKeySize = RSAKeySize.s4096, final DHKeySize dhKeySize = DHKeySize.l2048n224, final CurveInfo curve = CurveInfo.secp521r1, + required Uint8List seed, }) async { switch (algorithm) { case KeyAlgorithm.rsaEncryptSign: @@ -43,7 +44,7 @@ class KeyPairParams { case KeyAlgorithm.rsaSign: return _generateRSAKeyPair(rsaKeySize); case KeyAlgorithm.ecdsa: - final keyPair = _generateECKeyPair(curve); + final keyPair = _generateECKeyPairWithSeed(seed, curve); final q = keyPair.publicKey.Q!; return KeyPairParams( ECDSAPublicParams( @@ -56,7 +57,7 @@ class KeyPairParams { if (curve == CurveInfo.curve25519) { return _generateCurve25519KeyPair(); } else { - final keyPair = _generateECKeyPair(curve); + final keyPair = _generateECKeyPairWithSeed(seed, curve); final q = keyPair.publicKey.Q!; return KeyPairParams( ECDHPublicParams( @@ -69,7 +70,7 @@ class KeyPairParams { ); } case KeyAlgorithm.eddsa: - return _generateEd25519KeyPair(); + return _generateEd25519KeyPair(seed); case KeyAlgorithm.dsa: return _generateDSAKeyPair(dhKeySize); case KeyAlgorithm.elgamal: @@ -210,8 +211,36 @@ class KeyPairParams { } } - static KeyPairParams _generateEd25519KeyPair() { - final seed = Helper.secureRandom().nextBytes(TweetNaCl.seedSize); + static AsymmetricKeyPair _generateECKeyPairWithSeed(final Uint8List seed, + [ + final CurveInfo curve = CurveInfo.secp521r1, + ]) { + switch (curve) { + case CurveInfo.curve25519: + case CurveInfo.ed25519: + throw UnsupportedError( + 'Curve ${curve.name} is unsupported for key generation.', + ); + default: + final keyGen = KeyGenerator('EC') + ..init( + ParametersWithRandom( + ECKeyGeneratorParameters( + ECDomainParameters(curve.name.toLowerCase()), + ), + Helper.secureWithSeed(seed), + ), + ); + final keyPair = keyGen.generateKeyPair(); + return AsymmetricKeyPair( + keyPair.publicKey as ECPublicKey, + keyPair.privateKey as ECPrivateKey, + ); + } + } + + static KeyPairParams _generateEd25519KeyPair(final Uint8List feedSeed) { + final seed = Helper.secureWithSeed(feedSeed).nextBytes(TweetNaCl.seedSize); return KeyPairParams( EdDSAPublicParams( CurveInfo.ed25519.asn1Oid, diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index f88a47ce..e12ad797 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -111,12 +111,14 @@ class SecretKeyPacket extends ContainedPacket implements KeyPacket { final DHKeySize dhKeySize = DHKeySize.l2048n224, final CurveInfo curve = CurveInfo.secp521r1, final DateTime? date, + required Uint8List seed, }) async { final keyPair = await KeyPairParams.generate( algorithm, rsaKeySize: rsaKeySize, dhKeySize: dhKeySize, curve: curve, + seed: seed ); return SecretKeyPacket( diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 84e0a39d..7fbae954 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -43,12 +43,14 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacket { final DHKeySize dhKeySize = DHKeySize.l2048n224, final CurveInfo curve = CurveInfo.secp521r1, final DateTime? date, + required Uint8List seed }) async { final keyPair = await KeyPairParams.generate( algorithm, rsaKeySize: rsaKeySize, dhKeySize: dhKeySize, curve: curve, + seed: seed ); return SecretSubkeyPacket( diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index b2b8261d..6a779c68 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -24,6 +24,7 @@ import '../packet/signature_packet.dart'; import '../packet/user_id.dart'; import 'key.dart'; import 'subkey.dart'; +import 'dart:typed_data'; /// Class that represents an OpenPGP Private Key class PrivateKey extends Key { @@ -72,6 +73,7 @@ class PrivateKey extends Key { final int keyExpirationTime = 0, final String? subkeyPassphrase, final DateTime? date, + required Uint8List seed, }) async { if (userIDs.isEmpty || passphrase.isEmpty) { throw ArgumentError( @@ -106,6 +108,7 @@ class PrivateKey extends Key { dhKeySize: dhKeySize, curve: (type == KeyGenerationType.eddsa) ? CurveInfo.ed25519 : curve, date: date, + seed: seed ).then((secretKey) => secretKey.encrypt(passphrase)); final secretSubkey = await SecretSubkeyPacket.generate( subkeyAlgorithm, @@ -113,6 +116,7 @@ class PrivateKey extends Key { dhKeySize: dhKeySize, curve: (type == KeyGenerationType.eddsa) ? CurveInfo.curve25519 : curve, date: date, + seed: seed ).then( (secretSubkey) => secretSubkey.encrypt(subkeyPassphrase ?? passphrase), ); @@ -358,6 +362,7 @@ class PrivateKey extends Key { final int keyExpirationTime = 0, final bool subkeySign = false, final DateTime? date, + required Uint8List seed, }) async { if (passphrase.isEmpty) { throw ArgumentError('passphrase are required for key generation'); @@ -368,6 +373,7 @@ class PrivateKey extends Key { dhKeySize: dhKeySize, curve: curve, date: date, + seed: seed ).then((secretSubkey) => secretSubkey.encrypt(passphrase)); return PrivateKey.fromPacketList(PacketList([