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: added rsa4096 and ed25519 implementation #749

Open
wants to merge 3 commits into
base: trunk
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
10 changes: 5 additions & 5 deletions packages/at_chops/lib/src/algorithm/at_algorithm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:typed_data';

import 'package:at_chops/src/algorithm/at_iv.dart';
import 'package:at_chops/src/key/at_key_pair.dart';
import 'package:at_chops/src/key/at_private_key.dart';
import 'package:at_chops/src/key/at_public_key.dart';
import 'package:at_chops/src/model/hash_params.dart';
Expand Down Expand Up @@ -45,11 +44,12 @@ abstract class ASymmetricEncryptionAlgorithm
/// Interface for data signing. Data is signed using private key from a key pair
/// Signed data signature is verified with public key of the key pair.
abstract class AtSigningAlgorithm {
/// Signs the data using [AtPrivateKey] of [AsymmetricKeyPair]
Uint8List sign(Uint8List data);
/// Signs the data using private key of asymmetric key pair
FutureOr<Uint8List> sign(Uint8List data);

/// Verifies the data signature using [AtPublicKey] of [AsymmetricKeyPair] or the passed [publicKey]
bool verify(Uint8List signedData, Uint8List signature, {String? publicKey});
/// Verifies the data signature using public key of asymmetric key pair or the passed [publicKey]
FutureOr<bool> verify(Uint8List signedData, Uint8List signature,
{String? publicKey});
}

/// Interface for hashing data. Refer [DefaultHash] for sample implementation.
Expand Down
44 changes: 44 additions & 0 deletions packages/at_chops/lib/src/algorithm/ed25519_signing_algo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'dart:async';
import 'dart:typed_data';

import 'package:at_chops/src/algorithm/at_algorithm.dart';
import 'package:at_commons/at_commons.dart';
import 'package:better_cryptography/better_cryptography.dart';

/// Data signing and verification for Ed25519 - elliptic curve algorithm
/// Keypair for the algorithm has to generated using [AtChopsUtil.generateEd25519KeyPair()]
class Ed25519SigningAlgo implements AtSigningAlgorithm {
final _algorithm = Ed25519();
SimpleKeyPair? _ed25519KeyPair;

set edd25519KeyPair(SimpleKeyPair value) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo, should be ed not edd

_ed25519KeyPair = value;
}

Ed25519SigningAlgo();

@override
Future<Uint8List> sign(Uint8List data) async {
if (_ed25519KeyPair == null) {
throw AtSigningException(
'edd25519 key pair has to be set for signing operation');
}
final signature = await _algorithm.sign(data, keyPair: _ed25519KeyPair!);
return Uint8List.fromList(signature.bytes);
}

@override
Future<bool> verify(Uint8List signedData, Uint8List signature,
{String? publicKey}) async {
if (publicKey == null) {
throw AtSigningException(
'public key has to be passed for signature verification');
}
return await _algorithm.verify(
signedData,
signature: Signature(signature,
publicKey:
SimplePublicKey(publicKey.codeUnits, type: KeyPairType.ed25519)),
);
}
}
9 changes: 5 additions & 4 deletions packages/at_chops/lib/src/at_chops_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ class AtChopsImpl extends AtChops {
..atSigningMetaData = atSigningMetadata
..atSigningResultType = AtSigningResultType.bytes;
try {
atSigningResult.result = base64Encode(signingAlgorithm.sign(data));
atSigningResult.result =
base64Encode(signingAlgorithm.sign(data) as List<int>);
} on AtSigningException {
rethrow;
}
Expand Down Expand Up @@ -210,9 +211,8 @@ class AtChopsImpl extends AtChops {
EncryptionKeyType encryptionKeyType, String? keyName) {
switch (encryptionKeyType) {
case EncryptionKeyType.rsa2048:
return RsaEncryptionAlgo.fromKeyPair(_getEncryptionKeyPair(keyName)!);
case EncryptionKeyType.rsa4096:
throw AtEncryptionException('EncryptionKeyType.rsa4096 not supported');
return RsaEncryptionAlgo.fromKeyPair(_getEncryptionKeyPair(keyName)!);
case EncryptionKeyType.ecc:
throw AtEncryptionException('EncryptionKeyType.ecc not supported');
case EncryptionKeyType.aes128:
Expand All @@ -229,7 +229,8 @@ class AtChopsImpl extends AtChops {
if (keyName == null) {
return atChopsKeys.atEncryptionKeyPair!;
}
// #TODO plugin implementation for different keyNames
// #TODO For now return atEncryptionKeyPair which can be rsa2048 or rsa4096.
// #TODO When we remove atChopsKeys from AtChopsImpl constructor, plugin implementation for different keyNames
return null;
}

Expand Down
2 changes: 2 additions & 0 deletions packages/at_chops/lib/src/key/key_names.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class KeyNames {
static const String selfEncryptionKey = 'selfEncryptionKey';
static const String apkamSymmetricKey = 'apkamSymmetricKey';
static const String rsa2048EncKey = 'rsa2048EncKey';
static const String rsa4096EncKey = 'rsa4096EncKey';
}
7 changes: 7 additions & 0 deletions packages/at_chops/lib/src/util/at_chops_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:at_chops/src/key/impl/aes_key.dart';
import 'package:at_chops/src/key/impl/at_encryption_key_pair.dart';
import 'package:at_chops/src/key/impl/at_pkam_key_pair.dart';
import 'package:at_chops/src/key/key_type.dart';
import 'package:better_cryptography/better_cryptography.dart';
import 'package:crypton/crypton.dart';
import 'package:encrypt/encrypt.dart';

Expand Down Expand Up @@ -50,6 +51,12 @@ class AtChopsUtil {
return ECKeypair.fromRandom();
}

/// Generates an symmetric keypair for ED25519 elliptic curve signing and verification
static Future<SimpleKeyPair> generateEd25519KeyPair() async {
return await Ed25519().newKeyPair();
}

/// Generates symmetric AES key based on [keyType]
static SymmetricKey generateSymmetricKey(EncryptionKeyType keyType) {
switch (keyType) {
case EncryptionKeyType.aes128:
Expand Down
3 changes: 2 additions & 1 deletion packages/at_chops/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: at_chops
description: Package for at_protocol cryptographic and hashing operations
version: 2.2.0
version: 3.0.0
repository: https://github.com/atsign-foundation/at_libraries

environment:
Expand All @@ -18,6 +18,7 @@ dependencies:
at_commons: ^5.0.2
at_utils: ^3.0.19
cryptography: ^2.7.0
better_cryptography: ^1.0.0+1

dev_dependencies:
lints: ^5.0.0
Expand Down
50 changes: 50 additions & 0 deletions packages/at_chops/test/ed25519_signing_algo_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'dart:math';
import 'dart:typed_data';

import 'package:at_chops/at_chops.dart';
import 'package:at_chops/src/algorithm/ed25519_signing_algo.dart';
import 'package:test/test.dart';

void main() {
group('A group of tests for ed25519 signing and verification', () {
test('Test data signing and verification using generated keypair',
() async {
final ed25519KeyPair = await AtChopsUtil.generateEd25519KeyPair();
final dataToSign = 'Hello World@123!';
final signingAlgo = Ed25519SigningAlgo();
signingAlgo.edd25519KeyPair = ed25519KeyPair;
final signature =
await signingAlgo.sign(Uint8List.fromList(dataToSign.codeUnits));
final publicKeyBytes = (await ed25519KeyPair.extractPublicKey()).bytes;
print(publicKeyBytes.length);
final verifyResult = await signingAlgo.verify(
Uint8List.fromList(dataToSign.codeUnits), signature,
publicKey: String.fromCharCodes(publicKeyBytes));
expect(verifyResult, true);
});
test('Test data signing and verification - pass incorrect public key',
() async {
final ed25519KeyPair = await AtChopsUtil.generateEd25519KeyPair();
final dataToSign = 'Hello World@123!';
final signingAlgo = Ed25519SigningAlgo();
signingAlgo.edd25519KeyPair = ed25519KeyPair;
final signature =
await signingAlgo.sign(Uint8List.fromList(dataToSign.codeUnits));
final publicKeyBytes = (await ed25519KeyPair.extractPublicKey()).bytes;
print(publicKeyBytes.length);
const characters =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
final random = Random();
final wrongPublicKey = String.fromCharCodes(
Iterable.generate(
32,
(_) => characters.codeUnitAt(random.nextInt(characters.length)),
),
);
final verifyResult = await signingAlgo.verify(
Uint8List.fromList(dataToSign.codeUnits), signature,
publicKey: wrongPublicKey);
expect(verifyResult, false);
});
});
}
24 changes: 24 additions & 0 deletions packages/at_chops/test/rsa_encryption_algo_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ void main() {
var decryptedData = defaultEncryptionAlgo.decrypt(encryptedData);
expect(utf8.decode(decryptedData), dataToEncrypt);
});
test('Test asymmetric encryption/decryption using rsa 4096', () {
var defaultEncryptionAlgo = RsaEncryptionAlgo();
var rsa2048KeyPair =
AtChopsUtil.generateAtEncryptionKeyPair(keySize: 4096);
var rsaPublicKey = rsa2048KeyPair.atPublicKey;
var dataToEncrypt = 'Hello World12!@';
defaultEncryptionAlgo.atPublicKey = rsaPublicKey;
var encryptedData =
defaultEncryptionAlgo.encrypt(utf8.encode(dataToEncrypt));
var rsaPrivateKey = rsa2048KeyPair.atPrivateKey;
defaultEncryptionAlgo.atPrivateKey = rsaPrivateKey;
var decryptedData = defaultEncryptionAlgo.decrypt(encryptedData);
expect(utf8.decode(decryptedData), dataToEncrypt);
});
test('Test encrypt throws exception when passed public key is null', () {
var defaultEncryptionAlgo = RsaEncryptionAlgo();
var dataToEncrypt = 'Hello World12!@';
Expand Down Expand Up @@ -57,6 +71,16 @@ void main() {
var decryptedData = defaultEncryptionAlgo.decrypt(encryptedData);
expect(utf8.decode(decryptedData), dataToEncrypt);
});
test('Test asymmetric encryption/decryption using rsa 4096 key pair', () {
var rsa2048KeyPair =
AtChopsUtil.generateAtEncryptionKeyPair(keySize: 4096);
var defaultEncryptionAlgo = RsaEncryptionAlgo.fromKeyPair(rsa2048KeyPair);
var dataToEncrypt = 'Hello World12!@';
var encryptedData =
defaultEncryptionAlgo.encrypt(utf8.encode(dataToEncrypt));
var decryptedData = defaultEncryptionAlgo.decrypt(encryptedData);
expect(utf8.decode(decryptedData), dataToEncrypt);
});
test('Test encrypt throws exception when encryption keypair is null', () {
var defaultEncryptionAlgo = RsaEncryptionAlgo.fromKeyPair(null);
var dataToEncrypt = 'Hello World12!@';
Expand Down
Loading