Skip to content

Commit

Permalink
Merge pull request #214 from atsign-foundation/atkey_types
Browse files Browse the repository at this point in the history
feat: Atkey add getKeyType
  • Loading branch information
murali-shris authored Aug 11, 2022
2 parents df8b846 + 2e416cd commit 5212067
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 2 deletions.
1 change: 1 addition & 0 deletions at_commons/lib/at_commons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export 'package:at_commons/src/exception/at_exceptions.dart'
export 'package:at_commons/src/exception/at_server_exceptions.dart';
export 'package:at_commons/src/exception/error_message.dart';
export 'package:at_commons/src/keystore/at_key.dart';
export 'package:at_commons/src/keystore/key_type.dart';
export 'package:at_commons/src/shared_key_status.dart';
export 'package:at_commons/src/security/secure_socket_config.dart';
export 'package:at_commons/src/validators/at_key_validation.dart';
Expand Down
5 changes: 5 additions & 0 deletions at_commons/lib/src/exception/at_exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ class InvalidResponseException extends AtException {
InvalidResponseException(message) : super(message);
}

/// Exception thrown when a key is invalid
class InvalidAtKeyException extends AtException {
InvalidAtKeyException(message) : super(message);
}

enum ExceptionScenario {
noNetworkConnectivity,
rootServerNotReachable,
Expand Down
10 changes: 8 additions & 2 deletions at_commons/lib/src/keystore/at_key.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:at_commons/at_commons.dart';
import 'package:at_commons/src/keystore/at_key_builder_impl.dart';
import 'package:at_commons/src/keystore/key_type.dart';
import 'package:at_commons/src/utils/at_key_regex_utils.dart';

class AtKey {
String? key;
Expand All @@ -25,7 +25,7 @@ class AtKey {
}

String _dotNamespaceIfPresent() {
if (namespace != null) {
if (namespace != null && namespace!.isNotEmpty) {
return '.$namespace';
} else {
return '';
Expand Down Expand Up @@ -217,6 +217,12 @@ class AtKey {
atKey.metadata = metaData;
return atKey;
}

/// Returns one of the valid keys from [KeyType] if there is a regex match. Otherwise returns [KeyType.invalidKey]
/// Set enforceNamespace=true for strict namespace validation in the key.
static KeyType getKeyType(String key, {bool enforceNameSpace = false}) {
return RegexUtil.keyType(key, enforceNameSpace);
}
}

/// Represents a public key.
Expand Down
1 change: 1 addition & 0 deletions at_commons/lib/src/keystore/key_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ enum KeyType {
privateKey,
cachedPublicKey,
cachedSharedKey,
reservedKey,
invalidKey
}

Expand Down
14 changes: 14 additions & 0 deletions at_commons/lib/src/utils/at_key_regex_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ abstract class Regexes {
static const charsInEntity = r'''[\w\.\-_'*"]''';
static const allowedEmoji =
r'''((\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]))''';
static const _charsInReservedKey =
r'(shared_key|publickey|privatekey|self_encryption_key|commitLogCompactionStats|accessLogCompactionStats|notificationCompactionStats|signing_privatekey|signing_publickey|signing_keypair_generated|at_pkam_privatekey|at_pkam_publickey|at_secret_deleted|at_secret|_[\w-]+|)';

static const String namespaceFragment = '''\\.(?<namespace>$charsInNamespace)''';
static const String ownershipFragment = '''@(?<owner>($charsInAtSign|$allowedEmoji){1,55})''';
Expand All @@ -20,13 +22,15 @@ abstract class Regexes {
static const String sharedKeyStartFragment = '''((@$sharedWithFragment)(_*$entityFragment)''';
static const String cachedSharedKeyStartFragment = '''((cached:)(@$sharedWithFragment)(_*$entityFragment)''';
static const String cachedPublicKeyStartFragment = '''(?<visibility>(cached:public:){1})((@$sharedWithFragment)?$entityFragment''';
static const String reservedKeyFragment = '''(((@(?<sharedWith>($charsInAtSign|$allowedEmoji){1,55}))|public|privatekey):)?(?<atKey>$_charsInReservedKey)(@(?<owner>($charsInAtSign|$allowedEmoji){1,55}))?''';

String get publicKey;
String get privateKey;
String get selfKey;
String get sharedKey;
String get cachedSharedKey;
String get cachedPublicKey;
String get reservedKey;

static final Regexes _regexesWithMandatoryNamespace = RegexesWithMandatoryNamespace();
static final Regexes _regexesNonMandatoryNamespace = RegexesNonMandatoryNamespace();
Expand Down Expand Up @@ -66,6 +70,9 @@ class RegexesWithMandatoryNamespace implements Regexes {

@override
String get cachedPublicKey => _cachedPublicKey;

@override
String get reservedKey => Regexes.reservedKeyFragment;
}

class RegexesNonMandatoryNamespace implements Regexes {
Expand Down Expand Up @@ -94,13 +101,20 @@ class RegexesNonMandatoryNamespace implements Regexes {

@override
String get cachedPublicKey => _cachedPublicKey;

@override
String get reservedKey => Regexes.reservedKeyFragment;
}

class RegexUtil {
/// Returns a first matching key type after matching the key against regexes for each of the key type
static KeyType keyType(String key, bool enforceNamespace) {
Regexes regexes = Regexes(enforceNamespace);

if (matchAll(regexes.reservedKey, key)) {
return KeyType.reservedKey;
}

// matches the key with public key regex.
if (matchAll(regexes.publicKey, key)) {
return KeyType.publicKey;
Expand Down
3 changes: 3 additions & 0 deletions at_commons/lib/src/validators/at_key_validation_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class _AtKeyValidatorImpl extends AtKeyValidator {
case KeyType.cachedSharedKey:
_regex = regexes.cachedSharedKey;
break;
case KeyType.reservedKey:
_regex = regexes.reservedKey;
break;
case KeyType.invalidKey:
_regex = '';
break;
Expand Down
95 changes: 95 additions & 0 deletions at_commons/test/at_key_type_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:at_commons/at_commons.dart';
import 'package:at_commons/src/keystore/key_type.dart';
import 'package:test/test.dart';

void main() {
group('A group of tests to verify key types', () {
test('Test to verify a public key type with namespace', () {
var keyType = AtKey.getKeyType('public:phone.wavi@bob');
expect(keyType, equals(KeyType.publicKey));
});

test('Test to verify a cached public key type with namespace', () {
var keyType = AtKey.getKeyType('cached:public:phone.buzz@bob');
expect(keyType, equals(KeyType.cachedPublicKey));
});

test('Test to verify a shared key type with namespace', () {
var keyType = AtKey.getKeyType('@alice:phone.wavi@bob');
expect(keyType, equals(KeyType.sharedKey));
});

test('Test to verify a cached shared key type with namespace', () {
var keyType = AtKey.getKeyType('cached:@alice:phone.buzz@bob');
expect(keyType, equals(KeyType.cachedSharedKey));
});

test('Test to verify self key type with namespace', () {
var keyType = AtKey.getKeyType('@bob:phone.buzz@bob');
expect(keyType, equals(KeyType.selfKey));
});
});
group('A group of tests to check invalid key types', () {
test('Test public key type without namespace', () {
var keyType =
AtKey.getKeyType('public:phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});

test('Test cached public key type without namespace', () {
var keyType =
AtKey.getKeyType('cached:public:phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});

test('Test shared key type without namespace', () {
var keyType =
AtKey.getKeyType('@alice:phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});

test('Test cached shared key type without namespace', () {
var keyType =
AtKey.getKeyType('cached:@alice:phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});

test('Test self key type without sharedWith atsign and without namespace',
() {
var keyType = AtKey.getKeyType('phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});
test('Test self key type with atsign and without namespace', () {
var keyType = AtKey.getKeyType('@bob:phone@bob', enforceNameSpace: true);
expect(keyType, equals(KeyType.invalidKey));
});
});

group('A group of tests to check reserved key types', () {
test('Test reserved key type for shared_key', () {
var keyType = AtKey.getKeyType('@bob:shared_key@alice');
expect(keyType, equals(KeyType.reservedKey));
});

test('Test reserved key type for encryption publickey', () {
var keyType = AtKey.getKeyType('public:publickey@alice');
expect(keyType, equals(KeyType.reservedKey));
});

test('Test reserved key type for public session key', () {
var keyType = AtKey.getKeyType(
'public:_a29464d0-1f2d-4216-b903-031963bc4ab3@alice');
expect(keyType, equals(KeyType.reservedKey));
});

test('Test reserved key type for latest notification id', () {
var keyType = AtKey.getKeyType('_latestNotificationIdv2');
expect(keyType, equals(KeyType.reservedKey));
});

test('Test reserved key type for signing public key', () {
var keyType = AtKey.getKeyType('public:signing_publickey@colin');
expect(keyType, equals(KeyType.reservedKey));
});
});
}

0 comments on commit 5212067

Please sign in to comment.