From 9012104d99a84240ecc6c292678c11fff7f8e49c Mon Sep 17 00:00:00 2001 From: Murali Date: Fri, 2 Feb 2024 12:33:27 +0530 Subject: [PATCH 1/4] feat:replace md5 checksum --- packages/at_commons/lib/src/at_constants.dart | 3 +++ .../at_commons/lib/src/keystore/at_key.dart | 22 ++++++++++++++++++- .../lib/src/keystore/public_key_hash.dart | 19 ++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 packages/at_commons/lib/src/keystore/public_key_hash.dart diff --git a/packages/at_commons/lib/src/at_constants.dart b/packages/at_commons/lib/src/at_constants.dart index 31aafc08..be2c91f4 100644 --- a/packages/at_commons/lib/src/at_constants.dart +++ b/packages/at_commons/lib/src/at_constants.dart @@ -62,6 +62,9 @@ class AtConstants { static const String sharedKeyStatus = 'sharedKeyStatus'; static const String sharedKeyEncrypted = 'sharedKeyEnc'; static const String sharedWithPublicKeyCheckSum = 'pubKeyCS'; + static const String sharedWithPublicKeyHash = 'pubKeyHash'; + static const String sharedWithPublicKeyHashValue = 'hash'; + static const String sharedWithPublicKeyHashAlgo = 'algo'; static const String sharedKeyEncryptedEncryptingKeyName = 'skeEncKeyName'; static const String sharedKeyEncryptedEncryptingAlgo = 'skeEncAlgo'; static const String firstByte = '#'; diff --git a/packages/at_commons/lib/src/keystore/at_key.dart b/packages/at_commons/lib/src/keystore/at_key.dart index b9edfec3..c054e9ed 100644 --- a/packages/at_commons/lib/src/keystore/at_key.dart +++ b/packages/at_commons/lib/src/keystore/at_key.dart @@ -1,6 +1,7 @@ import 'package:at_commons/src/keystore/at_key_builder_impl.dart'; import 'package:at_commons/src/utils/at_key_regex_utils.dart'; import 'package:at_commons/src/utils/string_utils.dart'; +import 'package:at_commons/src/keystore/public_key_hash.dart'; import 'package:meta/meta.dart'; import '../at_constants.dart'; @@ -489,8 +490,13 @@ class Metadata { /// Stores the checksum of the encryption public key used to encrypt the [sharedKeyEnc]. We use this /// to verify that the encryption key-pair used to encrypt and decrypt the value are same + @Deprecated('Use pubKeyHash') String? pubKeyCS; + /// Stores the hash of the encryption public key used to encrypt the [sharedKeyEnc] + /// The hash is used to verify whether the current atsign's public key used for encrypting data by another atsign, has changed while decrypting the data + PublicKeyHash? pubKeyHash; + /// If the [AtValue] is public data (i.e. it is not encrypted) and contains one or more new line (\n) characters, /// then the data will be encoded, and the encoding will be set to type of encoding (e.g. "base64") String? encoding; @@ -553,7 +559,7 @@ class Metadata { ', refreshAt : ${refreshAt?.toUtc().toString()}, createdAt : ${createdAt?.toUtc().toString()}' ', updatedAt : ${updatedAt?.toUtc().toString()}, isBinary : $isBinary, isEncrypted : $isEncrypted' ', isCached : $isCached, dataSignature: $dataSignature, sharedKeyStatus: $sharedKeyStatus' - ', encryptedSharedKey: $sharedKeyEnc, pubKeyCheckSum: $pubKeyCS, encoding: $encoding' + ', encryptedSharedKey: $sharedKeyEnc, pubKeyHash: $pubKeyHash, encoding: $encoding' ', encKeyName: $encKeyName, encAlgo: $encAlgo, ivNonce: $ivNonce' ', skeEncKeyName: $skeEncKeyName, skeEncAlgo: $skeEncAlgo}'; } @@ -595,6 +601,14 @@ class Metadata { if (pubKeyCS.isNotNullOrEmpty) { sb.write(':${AtConstants.sharedWithPublicKeyCheckSum}:$pubKeyCS'); } + if (pubKeyHash != null && pubKeyHash!.hash.isNotNullOrEmpty) { + sb.write( + ':${AtConstants.sharedWithPublicKeyHashValue}:${pubKeyHash!.hash}'); + } + if (pubKeyHash != null && pubKeyHash!.publicKeyHashingAlgo != null) { + sb.write( + ':${AtConstants.sharedWithPublicKeyHashAlgo}:${pubKeyHash!.publicKeyHashingAlgo}'); + } if (encoding.isNotNullOrEmpty) { sb.write(':${AtConstants.encoding}:$encoding'); } @@ -667,6 +681,9 @@ class Metadata { if (fullJson || pubKeyCS != null) { map[AtConstants.sharedWithPublicKeyCheckSum] = pubKeyCS; } + if (fullJson || pubKeyHash != null) { + map[AtConstants.sharedWithPublicKeyHash] = pubKeyHash!.toJson(); + } if (fullJson || encoding != null) { map[AtConstants.encoding] = encoding; } @@ -734,6 +751,7 @@ class Metadata { metaData.sharedKeyStatus = json[AtConstants.sharedKeyStatus]; metaData.sharedKeyEnc = json[AtConstants.sharedKeyEncrypted]; metaData.pubKeyCS = json[AtConstants.sharedWithPublicKeyCheckSum]; + metaData.pubKeyHash = json[AtConstants.sharedWithPublicKeyHash]; metaData.encoding = json[AtConstants.encoding]; metaData.encKeyName = json[AtConstants.encryptingKeyName]; metaData.encAlgo = json[AtConstants.encryptingAlgo]; @@ -769,6 +787,7 @@ class Metadata { isCached == other.isCached && sharedKeyEnc == other.sharedKeyEnc && pubKeyCS == other.pubKeyCS && + pubKeyHash == other.pubKeyHash && encoding == other.encoding && encKeyName == other.encKeyName && encAlgo == other.encAlgo && @@ -797,6 +816,7 @@ class Metadata { isCached.hashCode ^ sharedKeyEnc.hashCode ^ pubKeyCS.hashCode ^ + pubKeyHash.hashCode ^ encoding.hashCode ^ encKeyName.hashCode ^ encAlgo.hashCode ^ diff --git a/packages/at_commons/lib/src/keystore/public_key_hash.dart b/packages/at_commons/lib/src/keystore/public_key_hash.dart new file mode 100644 index 00000000..1293bf50 --- /dev/null +++ b/packages/at_commons/lib/src/keystore/public_key_hash.dart @@ -0,0 +1,19 @@ +/// Represents hash of an atsign's public encryption key and the hashing algorithm used +class PublicKeyHash { + String? hash; + PublicKeyHashingAlgo? publicKeyHashingAlgo; + + @override + String toString() { + return 'PublicKeyHash{hash: $hash, publicKeyHashingAlgo: $publicKeyHashingAlgo}'; + } + + Map toJson() { + var map = {}; + map['hash'] = hash; + map['algo'] = publicKeyHashingAlgo; + return map; + } +} + +enum PublicKeyHashingAlgo { sha256, sha512 } From b6ab138f2f5fbbbfc2c3ce0ade7f991f05c3fb6b Mon Sep 17 00:00:00 2001 From: Murali Date: Fri, 2 Feb 2024 16:29:15 +0530 Subject: [PATCH 2/4] fix: export public key hash --- packages/at_commons/lib/at_commons.dart | 1 + .../at_commons/lib/src/keystore/public_key_hash.dart | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/at_commons/lib/at_commons.dart b/packages/at_commons/lib/at_commons.dart index 34f40c10..cecbd664 100644 --- a/packages/at_commons/lib/at_commons.dart +++ b/packages/at_commons/lib/at_commons.dart @@ -32,3 +32,4 @@ export 'package:at_commons/src/enroll/enrollment.dart'; @experimental export 'package:at_commons/src/telemetry/at_telemetry.dart'; export 'package:at_commons/src/utils/string_utils.dart'; +export 'package:at_commons/src/keystore/public_key_hash.dart'; diff --git a/packages/at_commons/lib/src/keystore/public_key_hash.dart b/packages/at_commons/lib/src/keystore/public_key_hash.dart index 1293bf50..a13362b4 100644 --- a/packages/at_commons/lib/src/keystore/public_key_hash.dart +++ b/packages/at_commons/lib/src/keystore/public_key_hash.dart @@ -14,6 +14,17 @@ class PublicKeyHash { map['algo'] = publicKeyHashingAlgo; return map; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is PublicKeyHash && + runtimeType == other.runtimeType && + hash == other.hash && + publicKeyHashingAlgo == other.publicKeyHashingAlgo; + + @override + int get hashCode => hash.hashCode ^ publicKeyHashingAlgo.hashCode; } enum PublicKeyHashingAlgo { sha256, sha512 } From efc560651b8f9585049e10bb64d1a353201f1bf7 Mon Sep 17 00:00:00 2001 From: Murali Date: Mon, 5 Feb 2024 21:39:12 +0530 Subject: [PATCH 3/4] feat: review comments and unit tests --- .../at_commons/lib/src/keystore/at_key.dart | 9 ++- .../lib/src/keystore/public_key_hash.dart | 13 +++- packages/at_commons/test/at_key_test.dart | 39 +++++++++++ .../at_commons/test/public_key_hash_test.dart | 67 +++++++++++++++++++ 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 packages/at_commons/test/public_key_hash_test.dart diff --git a/packages/at_commons/lib/src/keystore/at_key.dart b/packages/at_commons/lib/src/keystore/at_key.dart index c054e9ed..f9430b69 100644 --- a/packages/at_commons/lib/src/keystore/at_key.dart +++ b/packages/at_commons/lib/src/keystore/at_key.dart @@ -601,13 +601,11 @@ class Metadata { if (pubKeyCS.isNotNullOrEmpty) { sb.write(':${AtConstants.sharedWithPublicKeyCheckSum}:$pubKeyCS'); } - if (pubKeyHash != null && pubKeyHash!.hash.isNotNullOrEmpty) { + if (pubKeyHash != null) { sb.write( ':${AtConstants.sharedWithPublicKeyHashValue}:${pubKeyHash!.hash}'); - } - if (pubKeyHash != null && pubKeyHash!.publicKeyHashingAlgo != null) { sb.write( - ':${AtConstants.sharedWithPublicKeyHashAlgo}:${pubKeyHash!.publicKeyHashingAlgo}'); + ':${AtConstants.sharedWithPublicKeyHashAlgo}:${pubKeyHash!.publicKeyHashingAlgo.name}'); } if (encoding.isNotNullOrEmpty) { sb.write(':${AtConstants.encoding}:$encoding'); @@ -751,7 +749,8 @@ class Metadata { metaData.sharedKeyStatus = json[AtConstants.sharedKeyStatus]; metaData.sharedKeyEnc = json[AtConstants.sharedKeyEncrypted]; metaData.pubKeyCS = json[AtConstants.sharedWithPublicKeyCheckSum]; - metaData.pubKeyHash = json[AtConstants.sharedWithPublicKeyHash]; + metaData.pubKeyHash = + PublicKeyHash.fromJson(json[AtConstants.sharedWithPublicKeyHash]); metaData.encoding = json[AtConstants.encoding]; metaData.encKeyName = json[AtConstants.encryptingKeyName]; metaData.encAlgo = json[AtConstants.encryptingAlgo]; diff --git a/packages/at_commons/lib/src/keystore/public_key_hash.dart b/packages/at_commons/lib/src/keystore/public_key_hash.dart index a13362b4..1f0c6346 100644 --- a/packages/at_commons/lib/src/keystore/public_key_hash.dart +++ b/packages/at_commons/lib/src/keystore/public_key_hash.dart @@ -1,7 +1,9 @@ /// Represents hash of an atsign's public encryption key and the hashing algorithm used class PublicKeyHash { - String? hash; - PublicKeyHashingAlgo? publicKeyHashingAlgo; + String hash; + PublicKeyHashingAlgo publicKeyHashingAlgo; + + PublicKeyHash(this.hash, this.publicKeyHashingAlgo); @override String toString() { @@ -11,10 +13,15 @@ class PublicKeyHash { Map toJson() { var map = {}; map['hash'] = hash; - map['algo'] = publicKeyHashingAlgo; + map['algo'] = publicKeyHashingAlgo.name; return map; } + static PublicKeyHash fromJson(Map json) { + return PublicKeyHash( + json['hash'], PublicKeyHashingAlgo.values.byName(json['algo'])); + } + @override bool operator ==(Object other) => identical(this, other) || diff --git a/packages/at_commons/test/at_key_test.dart b/packages/at_commons/test/at_key_test.dart index b990ca36..5bc0add1 100644 --- a/packages/at_commons/test/at_key_test.dart +++ b/packages/at_commons/test/at_key_test.dart @@ -937,4 +937,43 @@ void main() { expect(localKey.toString(), 'local:testkey.test@alice'); }); }); + group('A group of tests to verify public key hash in metadata', () { + test('Test to verify metadata toJson method when public key hash is set', + () { + var metadata = Metadata() + ..pubKeyHash = PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512) + ..isPublic = false + ..ttr = -1; + var metadataJson = metadata.toJson(); + expect(metadataJson[AtConstants.sharedWithPublicKeyHash]['hash'], + 'randomhash'); + expect(metadataJson[AtConstants.sharedWithPublicKeyHash]['algo'], + PublicKeyHashingAlgo.sha512.name); + }); + test( + 'Test to verify metadata toProtocol fragment method when public key hash is set', + () { + var metadata = Metadata() + ..pubKeyHash = PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512) + ..isPublic = false + ..ttr = -1; + var metadataFragment = metadata.toAtProtocolFragment(); + expect(metadataFragment, contains('hash:randomhash')); + expect(metadataFragment, contains('algo:sha512')); + }); + test('Test to verify metadata fromJson method when public key hash is set', + () { + var jsonMap = {}; + jsonMap['ttr'] = -1; + jsonMap['isBinary'] = false; + jsonMap['isEncrypted'] = true; + jsonMap['isPublic'] = false; + jsonMap['pubKeyHash'] = {'hash': 'randomhash', 'algo': 'sha512'}; + var metadataObject = Metadata.fromJson(jsonMap); + expect(metadataObject.pubKeyHash, isNotNull); + expect(metadataObject.pubKeyHash!.hash, 'randomhash'); + expect(metadataObject.pubKeyHash!.publicKeyHashingAlgo, + PublicKeyHashingAlgo.sha512); + }); + }); } diff --git a/packages/at_commons/test/public_key_hash_test.dart b/packages/at_commons/test/public_key_hash_test.dart new file mode 100644 index 00000000..6e897786 --- /dev/null +++ b/packages/at_commons/test/public_key_hash_test.dart @@ -0,0 +1,67 @@ +import 'package:at_commons/at_commons.dart'; +import 'package:test/test.dart'; + +void main() { + group('A group of tests to verify public key hash methods', () { + test('Test to verify toJson method', () { + final publicKeyHash = + PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512); + var toJson = publicKeyHash.toJson(); + expect(toJson['hash'], 'randomhash'); + expect(toJson['algo'], 'sha512'); + }); + test('Test to verify fromJson method', () { + var jsonMap = {}; + jsonMap['hash'] = 'randomhash'; + jsonMap['algo'] = 'sha256'; + final publicKeyHash = PublicKeyHash.fromJson(jsonMap); + expect(publicKeyHash.hash, 'randomhash'); + expect(publicKeyHash.publicKeyHashingAlgo, PublicKeyHashingAlgo.sha256); + }); + test('Test to verify equals operator two objects equals', () { + final publicKeyHash_1 = + PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512); + expect(publicKeyHash_1 == publicKeyHash_2, true); + }); + test('Test to verify equals operator two objects different hash', () { + final publicKeyHash_1 = + PublicKeyHash('randomhash_1', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash_2', PublicKeyHashingAlgo.sha512); + expect(publicKeyHash_1 == publicKeyHash_2, false); + }); + test('Test to verify equals operator two objects different hash algo', () { + final publicKeyHash_1 = + PublicKeyHash('randomhash_1', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash_2', PublicKeyHashingAlgo.sha256); + expect(publicKeyHash_1 == publicKeyHash_2, false); + }); + test('Test to verify hashcodes are same - two objects equals', () { + final publicKeyHash_1 = + PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash', PublicKeyHashingAlgo.sha512); + equals(publicKeyHash_1.hashCode, publicKeyHash_2.hashCode); + }); + test('Test to verify hashcodes are different - two objects different hash', + () { + final publicKeyHash_1 = + PublicKeyHash('randomhash_1', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash_2', PublicKeyHashingAlgo.sha512); + expect(publicKeyHash_1.hashCode == publicKeyHash_2.hashCode, false); + }); + test( + 'Test to verify hashcodes are different - two objects different hash algo', + () { + final publicKeyHash_1 = + PublicKeyHash('randomhash_1', PublicKeyHashingAlgo.sha512); + final publicKeyHash_2 = + PublicKeyHash('randomhash_2', PublicKeyHashingAlgo.sha256); + expect(publicKeyHash_1.hashCode == publicKeyHash_2.hashCode, false); + }); + }); +} From 72e235cb43c8a388c75d1bb4e5f6ab89c1bbc37b Mon Sep 17 00:00:00 2001 From: Murali Date: Mon, 5 Feb 2024 21:49:42 +0530 Subject: [PATCH 4/4] fix: null check if public key hash jsonmap is empty --- .../at_commons/lib/src/keystore/public_key_hash.dart | 5 ++++- packages/at_commons/test/at_key_test.dart | 11 +++++++++++ packages/at_commons/test/public_key_hash_test.dart | 5 +++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/at_commons/lib/src/keystore/public_key_hash.dart b/packages/at_commons/lib/src/keystore/public_key_hash.dart index 1f0c6346..8f0278c5 100644 --- a/packages/at_commons/lib/src/keystore/public_key_hash.dart +++ b/packages/at_commons/lib/src/keystore/public_key_hash.dart @@ -17,7 +17,10 @@ class PublicKeyHash { return map; } - static PublicKeyHash fromJson(Map json) { + static PublicKeyHash? fromJson(Map? json) { + if (json == null) { + return null; + } return PublicKeyHash( json['hash'], PublicKeyHashingAlgo.values.byName(json['algo'])); } diff --git a/packages/at_commons/test/at_key_test.dart b/packages/at_commons/test/at_key_test.dart index 5bc0add1..1365f93b 100644 --- a/packages/at_commons/test/at_key_test.dart +++ b/packages/at_commons/test/at_key_test.dart @@ -975,5 +975,16 @@ void main() { expect(metadataObject.pubKeyHash!.publicKeyHashingAlgo, PublicKeyHashingAlgo.sha512); }); + test( + 'Test to verify metadata fromJson method when public key hash is not set', + () { + var jsonMap = {}; + jsonMap['ttr'] = -1; + jsonMap['isBinary'] = false; + jsonMap['isEncrypted'] = true; + jsonMap['isPublic'] = false; + var metadataObject = Metadata.fromJson(jsonMap); + expect(metadataObject.pubKeyHash, null); + }); }); } diff --git a/packages/at_commons/test/public_key_hash_test.dart b/packages/at_commons/test/public_key_hash_test.dart index 6e897786..2c96f53a 100644 --- a/packages/at_commons/test/public_key_hash_test.dart +++ b/packages/at_commons/test/public_key_hash_test.dart @@ -15,8 +15,9 @@ void main() { jsonMap['hash'] = 'randomhash'; jsonMap['algo'] = 'sha256'; final publicKeyHash = PublicKeyHash.fromJson(jsonMap); - expect(publicKeyHash.hash, 'randomhash'); - expect(publicKeyHash.publicKeyHashingAlgo, PublicKeyHashingAlgo.sha256); + expect(publicKeyHash, isNotNull); + expect(publicKeyHash?.hash, 'randomhash'); + expect(publicKeyHash?.publicKeyHashingAlgo, PublicKeyHashingAlgo.sha256); }); test('Test to verify equals operator two objects equals', () { final publicKeyHash_1 =