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

Add support for OKP-Keys and Ed25519 Signature #17

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
2 changes: 2 additions & 0 deletions lib/crypto_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ library crypto_keys;
import 'dart:convert';
import 'dart:typed_data';

import 'package:ed25519_edwards/ed25519_edwards.dart' as ed;
import 'package:pointycastle/export.dart' as pc;

import 'src/algorithms.dart';
Expand All @@ -15,6 +16,7 @@ export 'src/algorithms.dart'
part 'src/asymmetric_operator.dart';
part 'src/ec_keys.dart';
part 'src/keys.dart';
part 'src/okp_keys.dart';
part 'src/operator.dart';
part 'src/rsa_keys.dart';
part 'src/symmetric_keys.dart';
Expand Down
18 changes: 18 additions & 0 deletions lib/src/algorithms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ class _SigAlgorithms extends Identifier {
/// Contains the identifiers for supported ECDSA signing algorithms
final ecdsa = _EcdsaSigAlgorithms();

/// Contains Identifier for supported edDSA signing algorithms
final eddsa = AlgorithmIdentifier._('sig/edDSA', () => pce.EdDSASigner());

_SigAlgorithms() : super._('sig');
}

Expand Down Expand Up @@ -253,6 +256,18 @@ class _Curves {

/// P-256K
final p256k = const Identifier._('curve/P-256K');

/// Ed25519
final ed25519 = const Identifier._('curve/Ed25519');

/// X25519
final x25519 = const Identifier._('curve/X25519');

/// Ed448
final ed448 = const Identifier._('curve/Ed448');

/// X448
final x448 = const Identifier._('curve/X448');
}

/// An identifier for uniquely identify algorithms and other objects
Expand Down Expand Up @@ -306,6 +321,9 @@ class AlgorithmIdentifier<T extends pc.Algorithm> extends Identifier {
/// ECDSA using P-521 and SHA-512
'ES512': algorithms.signing.ecdsa.sha512,

/// EdDSA using Ed25519 or Ed448 with SHA-512
'EdDSA': algorithms.signing.eddsa,

/// RSASSA-PSS using SHA-256 and MGF1 with SHA-256
'PS256': null,

Expand Down
39 changes: 37 additions & 2 deletions lib/src/asymmetric_operator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class _AsymmetricOperator<T extends Key> implements Operator<T> {
case 'P-521':
return pc.ECCurve_secp521r1();
}
throw ArgumentError('Unknwon curve type $name');
throw ArgumentError('Unknown curve type $name');
}

pc.ECDomainParameters get ecDomainParameters =>
Expand All @@ -35,6 +35,26 @@ abstract class _AsymmetricOperator<T extends Key> implements Operator<T> {
k.exponent,
));
}
if (key is OkpPublicKey) {
var k = key as OkpPublicKey;

if (k.curve == curves.ed25519) {
return pc.PublicKeyParameter<pc.Ed25519PublicKey>(
pc.Ed25519PublicKey(k.okpPublicKey));
} else {
throw UnsupportedError('${k.curve} is not supported for signing now');
}
}
if (key is OkpPrivateKey) {
var k = key as OkpPrivateKey;

if (k.curve == curves.ed25519) {
return pc.PrivateKeyParameter<pc.Ed25519PrivateKey>(
pc.Ed25519PrivateKey(k.okpPrivateKey));
} else {
throw UnsupportedError('${k.curve} is not supported for signing now');
}
}
var d = ecDomainParameters;

if (key is EcPrivateKey) {
Expand Down Expand Up @@ -65,9 +85,19 @@ class _AsymmetricSigner extends Signer<PrivateKey>
@override
Signature sign(List<int> data) {
data = data is Uint8List ? data : Uint8List.fromList(data);

if (key is OkpKey) {
if (_algorithm.algorithmName != 'EdDSA') {
throw ArgumentError(
'${_algorithm.algorithmName} cannot be used with this key.');
}
_algorithm.init(true, pc.Ed25519CipherParameters(keyParameter));
return Signature(
(_algorithm.generateSignature(data) as pc.EdSignature).bytes);
}

_algorithm.init(
true, pc.ParametersWithRandom(keyParameter, DefaultSecureRandom()));

if (key is RsaKey) {
return Signature(
(_algorithm.generateSignature(data) as pc.RSASignature).bytes);
Expand All @@ -89,6 +119,7 @@ class _AsymmetricSigner extends Signer<PrivateKey>

return Signature(bytes);
}

throw UnsupportedError('Unknown key type $key');
}
}
Expand Down Expand Up @@ -125,6 +156,10 @@ class _AsymmetricVerifier extends Verifier<PublicKey>
_bigIntFromBytes(signature.data.skip(l)),
));
}
if (key is OkpKey) {
_algorithm.init(false, pc.Ed25519CipherParameters(keyParameter));
return _algorithm.verifySignature(data, pc.EdSignature(signature.data));
}
throw UnsupportedError('Unknown key type $key');
}
}
Expand Down
49 changes: 47 additions & 2 deletions lib/src/impl.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import '../crypto_keys.dart';

import 'dart:typed_data';

import 'package:collection/collection.dart';
import 'package:quiver/core.dart';

import '../crypto_keys.dart';

class RsaPublicKeyImpl extends PublicKey
with Key
implements RsaPublicKey, RsaKey {
Expand Down Expand Up @@ -87,6 +88,28 @@ class EcPublicKeyImpl extends PublicKey with Key implements EcPublicKey, EcKey {
other.curve == curve);
}

class OkpPublicKeyImpl extends PublicKey
with Key
implements OkpPublicKey, OkpKey {
@override
final Identifier curve;

@override
final Uint8List okpPublicKey;

OkpPublicKeyImpl({required this.okpPublicKey, required this.curve});

@override
int get hashCode => hash2(okpPublicKey, curve);

@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is OkpPublicKey &&
other.okpPublicKey == okpPublicKey &&
other.curve == curve);
}

class EcPrivateKeyImpl extends PrivateKey
with Key
implements EcPrivateKey, EcKey {
Expand All @@ -109,6 +132,28 @@ class EcPrivateKeyImpl extends PrivateKey
other.curve == curve);
}

class OkpPrivateKeyImpl extends PrivateKey
with Key
implements OkpPrivateKey, OkpKey {
@override
final Identifier curve;

@override
final Uint8List okpPrivateKey;

OkpPrivateKeyImpl({required this.okpPrivateKey, required this.curve});

@override
int get hashCode => hash2(okpPrivateKey, curve);

@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is EcPrivateKey &&
other.eccPrivateKey == okpPrivateKey &&
other.curve == curve);
}

class SymmetricKeyImpl extends Object
with Key, PublicKey, PrivateKey
implements SymmetricKey {
Expand Down
31 changes: 31 additions & 0 deletions lib/src/keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ class KeyPair {
curve: curve));
}

factory KeyPair.generateOkp(Identifier curve) {
if (curve == curves.ed25519) {
var key = ed.generateKey();

return KeyPair(
publicKey: OkpPublicKey(
curve: curve,
okpPublicKey: Uint8List.fromList(key.publicKey.bytes)),
privateKey: OkpPrivateKey(
curve: curve, okpPrivateKey: ed.seed(key.privateKey)));
} else {
throw UnsupportedError('Curve ${curve.name} is not supported');
}
}

/// Create a key pair from a JsonWebKey
factory KeyPair.fromJwk(Map<String, dynamic> jwk) {
switch (jwk['kty']) {
Expand Down Expand Up @@ -143,6 +158,18 @@ class KeyPair {
yCoordinate: _base64ToInt(jwk['y']),
curve: _parseCurve(jwk['crv']))
: null);
case 'OKP':
return KeyPair(
privateKey: jwk.containsKey('d') && jwk.containsKey('crv')
? OkpPrivateKey(
okpPrivateKey: Uint8List.fromList(_base64ToBytes(jwk['d'])),
curve: _parseCurve(jwk['crv']))
: null,
publicKey: jwk.containsKey('x') && jwk.containsKey('crv')
? OkpPublicKey(
okpPublicKey: Uint8List.fromList(_base64ToBytes(jwk['x'])),
curve: _parseCurve(jwk['crv']))
: null);
}
throw ArgumentError('Unknown key type ${jwk['kty']}');
}
Expand Down Expand Up @@ -182,6 +209,10 @@ Identifier _parseCurve(String name) {
'P-256K': curves.p256k,
'P-384': curves.p384,
'P-521': curves.p521,
'Ed25519': curves.ed25519,
'X25519': curves.x25519,
'Ed448': curves.ed448,
'X448': curves.x448
}[name];
if (v == null) {
throw UnsupportedError('Unknown curve $name');
Expand Down
27 changes: 27 additions & 0 deletions lib/src/okp_keys.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
part of '../crypto_keys.dart';

/// Base class for Octet Key Pairs (OKP-Keys)
abstract class OkpKey extends Key {
/// The cryptographic curve used with the key
Identifier get curve;
}

/// An OKP public key
abstract class OkpPublicKey extends OkpKey implements PublicKey {
/// The public key value
Uint8List get okpPublicKey;

factory OkpPublicKey(
{required Uint8List okpPublicKey,
required Identifier curve}) = OkpPublicKeyImpl;
}

/// An OKP private key
abstract class OkpPrivateKey extends OkpKey implements PrivateKey {
/// The OKP private key value
Uint8List get okpPrivateKey;

factory OkpPrivateKey(
{required Uint8List okpPrivateKey,
required Identifier curve}) = OkpPrivateKeyImpl;
}
63 changes: 63 additions & 0 deletions lib/src/pointycastle_ext.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:typed_data';

import 'package:ed25519_edwards/ed25519_edwards.dart' as ed;
import 'package:pointycastle/export.dart';

class ParametersWithIVAndAad<UnderlyingParameters extends CipherParameters>
Expand Down Expand Up @@ -283,3 +284,65 @@ class AESKeyWrap implements BlockCipher {
throw UnsupportedError('Should not be called.');
}
}

class EdDSASigner implements Signer {
Uint8List? _key;
@override
String get algorithmName => 'EdDSA';

EdDSASigner();

@override
EdSignature generateSignature(Uint8List message) {
var private = ed.newKeyFromSeed(_key!);
return EdSignature(ed.sign(private, message));
}

@override
void init(bool forSigning, CipherParameters params) {
if (params is! Ed25519CipherParameters) {
throw ArgumentError('Invalid CipherParameters');
}
if (forSigning && params.key is! PrivateKey) {
throw ArgumentError('Private Key needed for signing');
} else {
if (params.key is PrivateKey) {
_key = (params.key as Ed25519PrivateKey).privateKey;
} else {
_key = (params.key as Ed25519PublicKey).publicKey;
}
}
}

@override
void reset() {}

@override
bool verifySignature(Uint8List message, Signature signature) {
if (signature is! EdSignature) {
throw ArgumentError('Invalid signature');
}
return ed.verify(ed.PublicKey(_key!), message, signature.bytes);
}
}

class Ed25519CipherParameters extends AsymmetricKeyParameter {
Ed25519CipherParameters(AsymmetricKeyParameter params) : super(params.key);
}

class Ed25519PublicKey extends AsymmetricKey implements PublicKey {
Uint8List publicKey;
Ed25519PublicKey(this.publicKey);
}

class Ed25519PrivateKey extends AsymmetricKey implements PrivateKey {
Uint8List privateKey;

Ed25519PrivateKey(this.privateKey);
}

class EdSignature implements Signature {
Uint8List bytes;

EdSignature(this.bytes);
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies:
meta: ^1.3.0
collection: ^1.14.13
quiver: ^3.0.0
ed25519_edwards: ^0.3.1

dev_dependencies:
test: ^1.16.2
Expand Down
Loading