Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generateKey with seed for ecdsa and eddsa #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .idea/dart-pg.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions lib/src/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/openpgp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class OpenPGP {
final int keyExpirationTime = 0,
final String? subkeyPassphrase,
final DateTime? date,
required Uint8List seed
}) async =>
PrivateKey.generate(
userIDs,
Expand All @@ -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.
Expand Down
39 changes: 34 additions & 5 deletions lib/src/packet/key/key_pair_params.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ 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:
case KeyAlgorithm.rsaEncrypt:
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(
Expand All @@ -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(
Expand All @@ -69,7 +70,7 @@ class KeyPairParams {
);
}
case KeyAlgorithm.eddsa:
return _generateEd25519KeyPair();
return _generateEd25519KeyPair(seed);
case KeyAlgorithm.dsa:
return _generateDSAKeyPair(dhKeySize);
case KeyAlgorithm.elgamal:
Expand Down Expand Up @@ -210,8 +211,36 @@ class KeyPairParams {
}
}

static KeyPairParams _generateEd25519KeyPair() {
final seed = Helper.secureRandom().nextBytes(TweetNaCl.seedSize);
static AsymmetricKeyPair<ECPublicKey, ECPrivateKey> _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<ECPublicKey, ECPrivateKey>(
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,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/packet/secret_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions lib/src/packet/secret_subkey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
6 changes: 6 additions & 0 deletions lib/src/type/private_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -106,13 +108,15 @@ 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,
rsaKeySize: rsaKeySize,
dhKeySize: dhKeySize,
curve: (type == KeyGenerationType.eddsa) ? CurveInfo.curve25519 : curve,
date: date,
seed: seed
).then(
(secretSubkey) => secretSubkey.encrypt(subkeyPassphrase ?? passphrase),
);
Expand Down Expand Up @@ -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');
Expand All @@ -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([
Expand Down