From 2ebae42793a8945fbb165757440b418a0bd2c381 Mon Sep 17 00:00:00 2001 From: Yaroslav Moria <5eeman@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:42:36 +0200 Subject: [PATCH] Added key management system. --- lib/common/kms/index.dart | 4 + .../kms/key-providers/bjj-provider.dart | 104 +++++++++++++++ .../kms/key-providers/ed25519-provider.dart | 97 ++++++++++++++ lib/common/kms/key-providers/index.dart | 3 + .../kms/key-providers/secp256k1-provider.dart | 126 ++++++++++++++++++ lib/common/kms/kms.dart | 126 ++++++++++++++++++ lib/common/kms/provider-helpers.dart | 11 ++ lib/common/kms/store/abstract-key-store.dart | 20 +++ lib/common/kms/store/index.dart | 3 + lib/common/kms/store/memory-key-store.dart | 26 ++++ lib/common/kms/store/types.dart | 34 +++++ lib/common/utils/big_int_extension.dart | 12 ++ lib/identity/libs/bjj/eddsa_babyjub.dart | 14 +- pubspec.yaml | 2 + test/common/kms.dart | 74 ++++++++++ .../use_cases/authenticate_use_case_test.dart | 12 +- ...ile_and_did_current_env_use_case_test.dart | 4 +- .../fetch_and_save_claims_use_case_test.dart | 6 +- .../get_auth_inputs_use_case_test.dart | 6 +- .../get_auth_token_use_case_test.dart | 4 +- .../get_iden3comm_proofs_use_case_test.dart | 2 +- .../identity/add_identity_use_case_test.dart | 8 +- .../add_new_identity_use_case_test.dart | 4 +- ...check_identity_validity_use_case_test.dart | 2 +- .../create_identity_use_case_test.dart | 8 +- .../update_identity_use_case_test.dart | 8 +- .../profile/add_profile_use_case_test.dart | 12 +- .../create_profiles_use_case_test.dart | 6 +- .../profile/remove_profile_use_case_test.dart | 6 +- 29 files changed, 697 insertions(+), 47 deletions(-) create mode 100644 lib/common/kms/index.dart create mode 100644 lib/common/kms/key-providers/bjj-provider.dart create mode 100644 lib/common/kms/key-providers/ed25519-provider.dart create mode 100644 lib/common/kms/key-providers/index.dart create mode 100644 lib/common/kms/key-providers/secp256k1-provider.dart create mode 100644 lib/common/kms/kms.dart create mode 100644 lib/common/kms/provider-helpers.dart create mode 100644 lib/common/kms/store/abstract-key-store.dart create mode 100644 lib/common/kms/store/index.dart create mode 100644 lib/common/kms/store/memory-key-store.dart create mode 100644 lib/common/kms/store/types.dart create mode 100644 test/common/kms.dart diff --git a/lib/common/kms/index.dart b/lib/common/kms/index.dart new file mode 100644 index 00000000..d79f7af3 --- /dev/null +++ b/lib/common/kms/index.dart @@ -0,0 +1,4 @@ +export 'kms.dart'; +export 'key-providers/index.dart'; +export 'store/index.dart'; +export 'provider-helpers.dart'; diff --git a/lib/common/kms/key-providers/bjj-provider.dart b/lib/common/kms/key-providers/bjj-provider.dart new file mode 100644 index 00000000..07082774 --- /dev/null +++ b/lib/common/kms/key-providers/bjj-provider.dart @@ -0,0 +1,104 @@ +import "dart:typed_data"; + +import "package:encrypt/encrypt.dart"; +import "package:polygonid_flutter_sdk/common/kms/index.dart"; +import "package:polygonid_flutter_sdk/common/utils/big_int_extension.dart"; +import "package:polygonid_flutter_sdk/common/utils/uint8_list_utils.dart"; +import "package:web3dart/crypto.dart"; + +import "../../../identity/libs/bjj/eddsa_babyjub.dart"; + +/// Provider for Baby Jub Jub keys +/// @public +/// @class BjjProvider +/// @implements implements IKeyProvider interface +class BjjProvider implements IKeyProvider { + /// key type that is handled by BJJ Provider + /// @type {KmsKeyType} + KmsKeyType get keyType => KmsKeyType.BabyJubJub; + + AbstractPrivateKeyStore keyStore; + + /// Creates an instance of BjjProvider. + /// @param {KmsKeyType} keyType - kms key type + /// @param {AbstractPrivateKeyStore} keyStore - key store for kms + BjjProvider(KmsKeyType keyType, this.keyStore) { + if (keyType != KmsKeyType.BabyJubJub) { + throw Exception('Key type must be BabyJubJub'); + } + } + + /// generates a baby jub jub key from a seed phrase + /// @param {Uint8List} seed - byte array seed + /// @returns kms key identifier + @override + Future newPrivateKeyFromSeed(Uint8List seed) async { + final privateKey = PrivateKey(seed); + + final publicKey = privateKey.public(); + + final kmsId = KmsKeyId( + type: keyType, + id: keyPath(keyType, publicKey.hex()), + ); + await keyStore.importKey(alias: kmsId.id, key: privateKey.hex()); + + return kmsId; + } + + /// Gets public key by kmsKeyId + /// + /// @param {KmsKeyId} keyId - key identifier + @override + Future publicKey(KmsKeyId keyId) async { + final privateKey = await _privateKey(keyId); + return privateKey.public().hex(); + } + + /// signs prepared payload of size, + /// with a key id + /// + /// @param {KmsKeyId} keyId - key identifier + /// @param {Uint8List} data - data to sign (32 bytes) + /// @returns Uint8List signature + @override + Future sign( + KmsKeyId keyId, + Uint8List data, [ + Map? opts, + ]) async { + if (data.length != 32) { + throw Exception('data to sign is too large'); + } + + final i = bytesToUnsignedInt(data); + if (!i.checkBigIntInField()) { + throw Exception('data to sign is too large'); + } + final privateKey = await _privateKey(keyId); + + final signature = privateKey.sign(i); + + return Uint8ArrayUtils.uint8ListfromString(signature); + } + + @override + Future verify( + Uint8List message, String signatureHex, KmsKeyId keyId) async { + final publicKey = await this.publicKey(keyId); + + final bytes = hexToBytes(publicKey); + final pbkey = PublicKey.newFromCompressed(bytesToUnsignedInt(bytes)); + + return pbkey.verify( + Uint8ArrayUtils.uint8ListToString(message), + Signature.newFromCompressed(hexToBytes(signatureHex)), + ); + } + + Future _privateKey(KmsKeyId keyId) async { + final privateKeyHex = await keyStore.get(alias: keyId.id); + + return PrivateKey(decodeHexString(privateKeyHex)); + } +} diff --git a/lib/common/kms/key-providers/ed25519-provider.dart b/lib/common/kms/key-providers/ed25519-provider.dart new file mode 100644 index 00000000..7156e916 --- /dev/null +++ b/lib/common/kms/key-providers/ed25519-provider.dart @@ -0,0 +1,97 @@ +import 'dart:typed_data'; + +import 'package:polygonid_flutter_sdk/common/kms/kms.dart'; +import 'package:polygonid_flutter_sdk/common/kms/provider-helpers.dart'; +import 'package:polygonid_flutter_sdk/common/kms/store/abstract-key-store.dart'; +import 'package:polygonid_flutter_sdk/common/kms/store/types.dart'; +import 'package:polygonid_flutter_sdk/common/utils/uint8_list_utils.dart'; +import 'package:web3dart/crypto.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart' as ed25519; + +/// Provider for Ed25519 keys +/// @public +/// @class Ed25519Provider +/// @implements IKeyProvider interface +class Ed25519Provider implements IKeyProvider { + final KmsKeyType keyType; + final AbstractPrivateKeyStore _keyStore; + + /// Creates an instance of Ed25519Provider. + /// @param {KmsKeyType} keyType - kms key type + /// @param {AbstractPrivateKeyStore} keyStore - key store for kms + Ed25519Provider(this.keyType, this._keyStore); + + /// generates a ed25519 key from a seed phrase + /// @param {Uint8List} seed - byte array seed + /// @returns {Future} kms key identifier + @override + Future newPrivateKeyFromSeed(Uint8List seed) async { + if (seed.length != 32) { + throw Exception('Seed should be 32 bytes'); + } + final publicKey = ed25519.PublicKey(seed); + final kmsId = KmsKeyId( + type: keyType, + id: keyPath(keyType, bytesToHex(publicKey.bytes)), + ); + + await _keyStore.importKey( + alias: kmsId.id, + key: bytesToHex(seed), + ); + + return kmsId; + } + + /// Gets public key by kmsKeyId + /// @param {KmsKeyId} keyId - key identifier + /// @returns {Future} Public key as a hex String + @override + Future publicKey(KmsKeyId keyId) async { + final privateKeyHex = await _privateKey(keyId); + final privateKey = ed25519.newKeyFromSeed(hexToBytes(privateKeyHex)); + final publicKey = ed25519.public(privateKey); + return bytesToHex(publicKey.bytes); + } + + /// signs prepared payload of size, + /// with a key id + /// @param {KmsKeyId} keyId - key identifier + /// @param {Uint8List} digest - data to sign (32 bytes) + /// @returns {Future} signature + @override + Future sign( + KmsKeyId keyId, + Uint8List digest, [ + Map? opts, + ]) async { + final privateKeyHex = await _privateKey(keyId); + final privateKey = ed25519.newKeyFromSeed(hexToBytes(privateKeyHex)); + + return ed25519.sign(privateKey, digest); + } + + /// Verifies a signature for the given message and key identifier. + /// @param digest - The message to verify the signature against. + /// @param signatureHex - The signature to verify, as a hexadecimal String. + /// @param keyId - The key identifier to use for verification. + /// @returns A Future that resolves to a boolean indicating whether the signature is valid. + Future verify( + Uint8List digest, String signatureHex, KmsKeyId keyId) async { + final publicKeyHex = await this.publicKey(keyId); + final publicKey = ed25519.PublicKey(hexToBytes(publicKeyHex)); + + return ed25519.verify( + publicKey, + digest, + hexToBytes(signatureHex), + ); + } + + /// Retrieves the private key for a given keyId from the key store. + /// @param {KmsKeyId} keyId - The identifier of the key to retrieve. + /// @returns {Future} The private key associated with the keyId. + Future _privateKey(KmsKeyId keyId) async { + return _keyStore.get(alias: keyId.id); + } +} diff --git a/lib/common/kms/key-providers/index.dart b/lib/common/kms/key-providers/index.dart new file mode 100644 index 00000000..f2f7752d --- /dev/null +++ b/lib/common/kms/key-providers/index.dart @@ -0,0 +1,3 @@ +export 'bjj-provider.dart'; +export 'ed25519-provider.dart'; +export 'secp256k1-provider.dart'; diff --git a/lib/common/kms/key-providers/secp256k1-provider.dart b/lib/common/kms/key-providers/secp256k1-provider.dart new file mode 100644 index 00000000..ec74a6bc --- /dev/null +++ b/lib/common/kms/key-providers/secp256k1-provider.dart @@ -0,0 +1,126 @@ +import 'dart:typed_data'; + +import 'package:crypto/crypto.dart'; +import 'package:polygonid_flutter_sdk/common/kms/kms.dart'; +import 'package:polygonid_flutter_sdk/common/kms/provider-helpers.dart'; +import 'package:polygonid_flutter_sdk/common/kms/store/abstract-key-store.dart'; +import 'package:polygonid_flutter_sdk/common/kms/store/types.dart'; +import 'package:web3dart/crypto.dart'; +import 'package:secp256k1/secp256k1.dart' as secp256k1; + +/// Provider for Secp256k1 +/// @public +/// @class Secp256k1Provider +/// @implements implements IKeyProvider interface +class Sec256k1Provider implements IKeyProvider { + /// key type that is handled by BJJ Provider + /// @type {KmsKeyType} + final KmsKeyType keyType; + final AbstractPrivateKeyStore _keyStore; + + /// Creates an instance of Sec256k1Provider. + /// @param {KmsKeyType} keyType - kms key type + /// @param {AbstractPrivateKeyStore} keyStore - key store for kms + Sec256k1Provider(this.keyType, this._keyStore) { + if (keyType != KmsKeyType.Secp256k1) { + throw Exception('Key type must be Secp256k1'); + } + } + + /// generates a baby jub jub key from a seed phrase + /// @param {Uint8List} seed - byte array seed + /// @returns kms key identifier + @override + Future newPrivateKeyFromSeed(Uint8List seed) async { + if (seed.length != 32) { + throw Exception('Seed should be 32 bytes'); + } + final privateKey = secp256k1.PrivateKey.fromHex(bytesToHex(seed)); + final publicKey = privateKey.publicKey; + + final kmsId = KmsKeyId( + type: keyType, + id: keyPath(keyType, publicKey.toHex()), + ); + + await _keyStore.importKey( + alias: kmsId.id, + key: bytesToHex(seed).padLeft(64, '0'), + ); + + return kmsId; + } + + /// Gets public key by kmsKeyId + /// + /// @param {KmsKeyId} keyId - key identifier + @override + Future publicKey(KmsKeyId keyId) async { + final privateKeyHex = await _privateKey(keyId); + final privateKey = secp256k1.PrivateKey.fromHex(privateKeyHex); + final publicKey = privateKey.publicKey; + return publicKey.toHex(); // 04 + x + y (uncompressed key) + } + + /// Signs the given data using the private key associated with the specified key identifier. + /// @param keyId - The key identifier to use for signing. + /// @param data - The data to sign. + /// @param opts - Signing options, such as the algorithm to use. + /// @returns A Future that resolves to the signature as a Uint8List. + @override + Future sign( + KmsKeyId keyId, + Uint8List data, [ + Map? opts, + ]) async { + final privateKeyHex = await _privateKey(keyId); + final privateKey = secp256k1.PrivateKey.fromHex(privateKeyHex); + + final signature = privateKey.signature(bytesToHex(data)); + + final signatureBytes = Uint8List(64); + final rBytes = hexToBytes(signature.R.toRadixString(16).padLeft(32, '0')); + signatureBytes.setRange(0, 32, rBytes); + final sBytes = hexToBytes(signature.S.toRadixString(16).padLeft(32, '0')); + signatureBytes.setRange(32, 64, sBytes); + + // final signatureBase64 = await ES256KSigner( + // hexToBytes(privateKeyHex), opts?["alg"] == 'ES256K-R')(data); + // + // if (signatureBase64.runtimeType != String) { + // throw Exception('signatureBase64 must be a String'); + // } + + return signatureBytes; + } + + /// Verifies a signature for the given message and key identifier. + /// @param message - The message to verify the signature against. + /// @param signatureHex - The signature to verify, as a hexadecimal String. + /// @param keyId - The key identifier to use for verification. + /// @returns A Future that resolves to a boolean indicating whether the signature is valid. + @override + Future verify( + Uint8List message, + String signatureHex, + KmsKeyId keyId, + ) async { + final publicKeyHex = await this.publicKey(keyId); + + final publicKey = secp256k1.PublicKey.fromHex(publicKeyHex); + + final signature = secp256k1.Signature.fromHexes( + signatureHex.substring(0, signatureHex.length ~/ 2), + signatureHex.substring(signatureHex.length ~/ 2, signatureHex.length), + ); + + return signature.verify( + publicKey, + bytesToHex(message), + ); + } + + Future _privateKey(KmsKeyId keyId) async { + return _keyStore.get(alias: keyId.id); + } +} diff --git a/lib/common/kms/kms.dart b/lib/common/kms/kms.dart new file mode 100644 index 00000000..0952889d --- /dev/null +++ b/lib/common/kms/kms.dart @@ -0,0 +1,126 @@ +import 'dart:typed_data'; + +import 'package:polygonid_flutter_sdk/common/kms/store/index.dart'; + +/// KeyProvider is responsible for signing and creation of the keys +/// +/// @public +/// @interface IKeyProvider +abstract class IKeyProvider { + /// property to store key type + /// + /// @type {KmsKeyType} + KmsKeyType get keyType; + + /// gets public key by key id + /// + /// @param {KmsKeyId} keyID - kms key identifier + /// @returns 'Promise' + Future publicKey(KmsKeyId keyID); + + /// sign data with kms key + /// + /// @param {KmsKeyId} keyId - key identifier + /// @param {Uint8Array} data - bytes payload + /// @param {{ [key: string]: unknown }} opts - additional options for signing + /// @returns 'Promise' + Future sign( + KmsKeyId keyId, + Uint8List data, [ + Map? opts, + ]); + + /// creates new key pair from given seed + /// + /// @param {Uint8Array} seed - seed + /// @returns 'Promise' + Future newPrivateKeyFromSeed(Uint8List seed); + + /// Verifies a message signature using the provided key ID. + /// + /// @param message - The message bytes to verify. + /// @param signatureHex - The signature in hexadecimal format. + /// @param keyId - The KMS key ID used to verify the signature. + /// @returns A promise that resolves to a boolean indicating whether the signature is valid. + Future verify(Uint8List message, String signatureHex, KmsKeyId keyId); +} + +/// Key management system class contains different key providers. +/// allows to register custom provider, create key, get public key and sign +/// +/// @public +/// @class KMS - class +class KMS { + final _registry = {}; + + /// register key provider in the KMS + /// + /// @param {KmsKeyType} keyType - kms key type + /// @param {IKeyProvider} keyProvider - key provider implementation + void registerKeyProvider(KmsKeyType keyType, IKeyProvider keyProvider) { + if (_registry.containsKey(keyType)) { + throw Exception('present keyType'); + } + _registry[keyType] = keyProvider; + } + + /// generates a new key and returns it kms key id + /// + /// @param {KmsKeyType} keyType + /// @param {Uint8Array} bytes + /// @returns kms key id + Future createKeyFromSeed( + KmsKeyType keyType, Uint8List bytes) async { + final keyProvider = _registry[keyType]; + if (keyProvider == null) { + throw Exception('keyProvider not found for: ${keyType}'); + } + return keyProvider.newPrivateKeyFromSeed(bytes); + } + + /// gets public key for key id + /// + /// @param {KmsKeyId} keyId -- key id + /// @returns public key + Future publicKey(KmsKeyId keyId) async { + final keyProvider = _registry[keyId.type]; + if (keyProvider == null) { + throw Exception('keyProvider not found for: ${keyId.type}'); + } + + return keyProvider.publicKey(keyId); + } + + /// sign Uint8Array with giv KmsKeyIden + /// + /// @param {KmsKeyId} keyId - key id + /// @param {Uint8Array} data - prepared data bytes + /// @returns 'Promise' - return signature + Future sign( + KmsKeyId keyId, + Uint8List data, [ + Map opts = const {}, + ]) async { + final keyProvider = _registry[keyId.type]; + if (keyProvider == null) { + throw Exception('keyProvider not found for: ${keyId.type}'); + } + + return keyProvider.sign(keyId, data, opts); + } + + /// Verifies a signature against the provided data and key ID. + /// + /// @param data - The data to verify the signature against. + /// @param signatureHex - The signature to verify, in hexadecimal format. + /// @param keyId - The key ID to use for verification. + /// @returns A promise that resolves to a boolean indicating whether the signature is valid. + Future verify( + Uint8List data, String signatureHex, KmsKeyId keyId) async { + final keyProvider = _registry[keyId.type]; + if (keyProvider == null) { + throw Exception('keyProvider not found for: ${keyId.type}'); + } + return keyProvider.verify(data, signatureHex, keyId); + } +} diff --git a/lib/common/kms/provider-helpers.dart b/lib/common/kms/provider-helpers.dart new file mode 100644 index 00000000..7029a8a7 --- /dev/null +++ b/lib/common/kms/provider-helpers.dart @@ -0,0 +1,11 @@ +import 'package:polygonid_flutter_sdk/common/kms/store/types.dart'; + +/// builds key path +/// +/// @param {KmsKeyType} keyType - key type +/// @param {string} keyID - key id +/// @returns string path +String keyPath(KmsKeyType keyType, String keyID) { + const basePath = ''; + return basePath + keyType.name + ':' + keyID; +} diff --git a/lib/common/kms/store/abstract-key-store.dart b/lib/common/kms/store/abstract-key-store.dart new file mode 100644 index 00000000..12410a07 --- /dev/null +++ b/lib/common/kms/store/abstract-key-store.dart @@ -0,0 +1,20 @@ +/// KeyStore that allows to import and get keys by alias. +/// +/// @abstract +/// @public +/// @class AbstractPrivateKeyStore +abstract class AbstractPrivateKeyStore { + /// imports key by alias + /// + /// @abstract + /// @param {{ alias: string; key: string }} args - key alias and hex representation + /// @returns `Promise` + Future importKey({required String alias, required String key}); + + /// get key by alias + /// + /// @abstract + /// @param {{ alias: string }} args -key alias + /// @returns `Promise` + Future get({required String alias}); +} diff --git a/lib/common/kms/store/index.dart b/lib/common/kms/store/index.dart new file mode 100644 index 00000000..2990aa83 --- /dev/null +++ b/lib/common/kms/store/index.dart @@ -0,0 +1,3 @@ +export './abstract-key-store.dart'; +export './memory-key-store.dart'; +export './types.dart'; diff --git a/lib/common/kms/store/memory-key-store.dart b/lib/common/kms/store/memory-key-store.dart new file mode 100644 index 00000000..04c49349 --- /dev/null +++ b/lib/common/kms/store/memory-key-store.dart @@ -0,0 +1,26 @@ +import "package:polygonid_flutter_sdk/common/kms/store/abstract-key-store.dart"; + +/// Key Store to use in memory +/// +/// @public +/// @class InMemoryPrivateKeyStore +/// @implements implements AbstractPrivateKeyStore interface +class InMemoryPrivateKeyStore implements AbstractPrivateKeyStore { + Map _data; + + InMemoryPrivateKeyStore() : _data = {}; + + @override + Future get({required String alias}) async { + final privateKey = _data[alias]; + if (privateKey == null) { + throw Exception('no key under given alias'); + } + return privateKey; + } + + @override + Future importKey({required String alias, required String key}) async { + _data[alias] = key; + } +} diff --git a/lib/common/kms/store/types.dart b/lib/common/kms/store/types.dart new file mode 100644 index 00000000..6ba565a3 --- /dev/null +++ b/lib/common/kms/store/types.dart @@ -0,0 +1,34 @@ +/// Key type that can be used in the key management system +/// +/// @enum {number} +enum KmsKeyType { + BabyJubJub('BJJ'), + Secp256k1('Secp256k1'), + Ed25519('Ed25519'); + + final String name; + + const KmsKeyType(this.name); +} + +/// ID of the key that describe contain key type +/// +/// @public +/// @interface KmsKeyId +class KmsKeyId { + final KmsKeyType type; + final String id; + + KmsKeyId({required this.type, required this.id}); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is KmsKeyId && + runtimeType == other.runtimeType && + type == other.type && + id == other.id; + + @override + int get hashCode => type.hashCode ^ id.hashCode; +} diff --git a/lib/common/utils/big_int_extension.dart b/lib/common/utils/big_int_extension.dart index 48e819cf..c16c1e39 100644 --- a/lib/common/utils/big_int_extension.dart +++ b/lib/common/utils/big_int_extension.dart @@ -1,7 +1,15 @@ +import 'dart:typed_data'; + +import 'package:web3dart/crypto.dart'; + extension BigIntQ on BigInt { static BigInt Q = BigInt.parse( "21888242871839275222246405745257275088548364400416034343698204186575808495617"); + bool checkBigIntInField() { + return this < Q; + } + BigInt qNormalize() { if (this < Q) { return this; @@ -9,4 +17,8 @@ extension BigIntQ on BigInt { return this % Q; } + + Uint8List toBytes() { + return hexToBytes(toRadixString(16)); + } } diff --git a/lib/identity/libs/bjj/eddsa_babyjub.dart b/lib/identity/libs/bjj/eddsa_babyjub.dart index e0675cfc..8b09dcc1 100644 --- a/lib/identity/libs/bjj/eddsa_babyjub.dart +++ b/lib/identity/libs/bjj/eddsa_babyjub.dart @@ -94,9 +94,15 @@ class PublicKey { sigList.add(signature.r8[1].toInt()); sigList.add(signature.s.toInt()); return bjjLib.verifyPoseidon( - Uint8ArrayUtils.uint8ListToString(Uint8List.fromList(pointList)), - Uint8ArrayUtils.uint8ListToString(Uint8List.fromList(sigList)), - messageHash); + Uint8ArrayUtils.uint8ListToString(Uint8List.fromList(pointList)), + Uint8ArrayUtils.uint8ListToString(Uint8List.fromList(sigList)), + messageHash, + ); + } + + String hex() { + BabyjubjubLib bjjLib = BabyjubjubLib(); + return bjjLib.packPoint(p[0].toString(), p[1].toString()); } } @@ -137,6 +143,8 @@ class PrivateKey { bjjLib.signPoseidon(HexUtils.bytesToHex(sk), messageHash.toString()); return signature; } + + String hex() => HexUtils.bytesToHex(sk); } String packSignature(Uint8List signature) { diff --git a/pubspec.yaml b/pubspec.yaml index 07c7106c..7750c0a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 ninja_prime: ^2.0.0 + ed25519_edwards: ^0.3.1 + secp256k1: ^0.3.0 dev_dependencies: flutter_test: diff --git a/test/common/kms.dart b/test/common/kms.dart new file mode 100644 index 00000000..7c35e8b9 --- /dev/null +++ b/test/common/kms.dart @@ -0,0 +1,74 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:polygonid_flutter_sdk/common/kms/index.dart'; +import 'package:polygonid_flutter_sdk/common/kms/kms.dart'; +import 'package:polygonid_flutter_sdk/common/kms/store/memory-key-store.dart'; +import 'package:polygonid_flutter_sdk/common/utils/big_int_extension.dart'; +import 'package:web3dart/crypto.dart'; + +Future testFlow(IKeyProvider provider) async { + final seed1 = getRandomBytes(32); + final seed2 = getRandomBytes(32); + expect(seed1, isNot(seed2)); + // expect(seed1).to.not.deep.equal(seed2); + + var dataToSign1 = getRandomBytes(32); + var dataToSign2 = getRandomBytes(32); + if (provider is BjjProvider) { + // because challenge should be in the finite field of Constant.Q + dataToSign1 = (BigIntQ.Q - BigInt.one).toBytes(); + dataToSign2 = (BigIntQ.Q - BigInt.parse('100')).toBytes(); + } + + final keyId1 = await provider.newPrivateKeyFromSeed(seed1); + final keyId2 = await provider.newPrivateKeyFromSeed(seed2); + final keyId3 = await provider.newPrivateKeyFromSeed(seed1); + + final signature1 = await provider.sign(keyId1, dataToSign1); + final signature2 = await provider.sign(keyId2, dataToSign2); + final signature3 = await provider.sign(keyId3, dataToSign1); + + final isPublicKey1Valid = + await provider.verify(dataToSign1, bytesToHex(signature1), keyId1); + final isPublicKey2Valid = + await provider.verify(dataToSign2, bytesToHex(signature2), keyId2); + final isPublicKey3Valid = + await provider.verify(dataToSign1, bytesToHex(signature3), keyId3); + + // expect(signature1).to.not.deep.equal(signature2); + // expect(signature1).to.deep.equal(signature3); + + expect(isPublicKey1Valid, true); + expect(isPublicKey2Valid, true); + expect(isPublicKey3Valid, true); +} + +void main() { + test( + "should signatures be valid and equal for the same data and private key", + () async { + final keyStore = InMemoryPrivateKeyStore(); + final ed25519Provider = Ed25519Provider(KmsKeyType.Ed25519, keyStore); + final secp256k1Provider = + Sec256k1Provider(KmsKeyType.Secp256k1, keyStore); + final bjjProvider = BjjProvider(KmsKeyType.BabyJubJub, keyStore); + await Future.wait([ + // testFlow(bjjProvider), + testFlow(ed25519Provider), + testFlow(secp256k1Provider) + ]); + }, + ); +} + +Uint8List getRandomBytes(int length) { + final random = Random(); + + final result = Uint8List(length); + for (var i = 0; i < length; i++) { + result[i] = random.nextInt(256); + } + return result; +} diff --git a/test/iden3comm/domain/use_cases/authenticate_use_case_test.dart b/test/iden3comm/domain/use_cases/authenticate_use_case_test.dart index 1afb794a..b8d6511f 100644 --- a/test/iden3comm/domain/use_cases/authenticate_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/authenticate_use_case_test.dart @@ -126,13 +126,13 @@ void main() { .first; expect(capturedProofs.message, Iden3commMocks.authRequest); expect(capturedProofs.genesisDid, CommonMocks.did); - expect(capturedProofs.privateKey, CommonMocks.privateKey); + expect(capturedProofs._privateKey, CommonMocks.privateKey); var captureDidIdentifier = verify(getDidIdentifierUseCase.execute( param: captureAnyNamed('param'))) .captured .first; - expect(captureDidIdentifier.privateKey, CommonMocks.privateKey); + expect(captureDidIdentifier._privateKey, CommonMocks.privateKey); expect(captureDidIdentifier.blockchain, CommonMocks.name); expect(captureDidIdentifier.network, CommonMocks.network); @@ -147,7 +147,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.genesisNonce); verify(getPackageNameUseCase.execute()); @@ -172,7 +172,7 @@ void main() { .captured .first; expect(capturedAuthToken.genesisDid, CommonMocks.did); - expect(capturedAuthToken.privateKey, CommonMocks.privateKey); + expect(capturedAuthToken._privateKey, CommonMocks.privateKey); expect(capturedAuthToken.message, CommonMocks.message); var capturedAuthenticate = verify(iden3commRepository.authenticate( @@ -209,7 +209,7 @@ void main() { .first; expect(capturedProofs.message, Iden3commMocks.authRequest); expect(capturedProofs.genesisDid, CommonMocks.did); - expect(capturedProofs.privateKey, CommonMocks.privateKey); + expect(capturedProofs._privateKey, CommonMocks.privateKey); var verifyConfig = verify(getEnvUseCase.execute(param: captureAnyNamed('param'))); @@ -222,7 +222,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.genesisNonce); verify(getPackageNameUseCase.execute()); diff --git a/test/iden3comm/domain/use_cases/check_profile_and_did_current_env_use_case_test.dart b/test/iden3comm/domain/use_cases/check_profile_and_did_current_env_use_case_test.dart index c227cfb4..5319a04d 100644 --- a/test/iden3comm/domain/use_cases/check_profile_and_did_current_env_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/check_profile_and_did_current_env_use_case_test.dart @@ -75,7 +75,7 @@ void main() { verify(getDidIdentifierUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(captureDidIdentifier.privateKey, CommonMocks.privateKey); + expect(captureDidIdentifier._privateKey, CommonMocks.privateKey); expect(captureDidIdentifier.blockchain, CommonMocks.name); expect(captureDidIdentifier.network, CommonMocks.network); }); @@ -115,7 +115,7 @@ void main() { verify(getDidIdentifierUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(captureDidIdentifier.privateKey, CommonMocks.privateKey); + expect(captureDidIdentifier._privateKey, CommonMocks.privateKey); expect(captureDidIdentifier.blockchain, CommonMocks.name); expect(captureDidIdentifier.network, CommonMocks.network); }); diff --git a/test/iden3comm/domain/use_cases/fetch_and_save_claims_use_case_test.dart b/test/iden3comm/domain/use_cases/fetch_and_save_claims_use_case_test.dart index ef42a680..d398e4c3 100644 --- a/test/iden3comm/domain/use_cases/fetch_and_save_claims_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/fetch_and_save_claims_use_case_test.dart @@ -184,7 +184,7 @@ void main() { expect(authVerify.callCount, requests.length); for (int i = 0; i < requests.length; i++) { expect(authVerify.captured[i].genesisDid, param.genesisDid); - expect(authVerify.captured[i].privateKey, param.privateKey); + expect(authVerify.captured[i]._privateKey, param.privateKey); expect(authVerify.captured[i].message, requests[i]); } @@ -199,7 +199,7 @@ void main() { .captured .first; expect(captureCheck.did, param.genesisDid); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.genesisNonce); var fetchVerify = verify(iden3commCredentialRepository.fetchClaim( @@ -248,7 +248,7 @@ void main() { expect(authVerify.callCount, 1); expect(authVerify.captured[0].genesisDid, param.genesisDid); - expect(authVerify.captured[0].privateKey, param.privateKey); + expect(authVerify.captured[0]._privateKey, param.privateKey); expect(authVerify.captured[0].message, requests[0]); var fetchVerify = verify(iden3commCredentialRepository.fetchClaim( diff --git a/test/iden3comm/domain/use_cases/get_auth_inputs_use_case_test.dart b/test/iden3comm/domain/use_cases/get_auth_inputs_use_case_test.dart index 80a3bfa0..01f58820 100644 --- a/test/iden3comm/domain/use_cases/get_auth_inputs_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/get_auth_inputs_use_case_test.dart @@ -108,13 +108,13 @@ void main() { .captured .first; expect(capturedIdentity.genesisDid, param.genesisDid); - expect(capturedIdentity.privateKey, param.privateKey); + expect(capturedIdentity._privateKey, param.privateKey); var capturedSign = verify(signMessageUseCase.execute(param: captureAnyNamed("param"))) .captured .first; - expect(capturedSign.privateKey, param.privateKey); + expect(capturedSign._privateKey, param.privateKey); expect(capturedSign.message, CommonMocks.challenge); var capturedAuthInputs = verify(iden3commRepository.getAuthInputs( @@ -159,7 +159,7 @@ void main() { .captured .first; expect(capturedIdentity.genesisDid, param.genesisDid); - expect(capturedIdentity.privateKey, param.privateKey); + expect(capturedIdentity._privateKey, param.privateKey); verifyNever(signMessageUseCase.execute(param: captureAnyNamed("param"))); diff --git a/test/iden3comm/domain/use_cases/get_auth_token_use_case_test.dart b/test/iden3comm/domain/use_cases/get_auth_token_use_case_test.dart index d87425a7..cf4c798b 100644 --- a/test/iden3comm/domain/use_cases/get_auth_token_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/get_auth_token_use_case_test.dart @@ -93,7 +93,7 @@ void main() { .captured .first; expect(captureAuthInputs.genesisDid, CommonMocks.did); - expect(captureAuthInputs.privateKey, CommonMocks.privateKey); + expect(captureAuthInputs._privateKey, CommonMocks.privateKey); expect( verify(loadCircuitUseCase.execute(param: captureAnyNamed('param'))) @@ -138,7 +138,7 @@ void main() { .captured .first; expect(captureAuthInputs.genesisDid, CommonMocks.did); - expect(captureAuthInputs.privateKey, CommonMocks.privateKey); + expect(captureAuthInputs._privateKey, CommonMocks.privateKey); expect( verify(loadCircuitUseCase.execute(param: captureAnyNamed('param'))) diff --git a/test/iden3comm/domain/use_cases/get_iden3comm_proofs_use_case_test.dart b/test/iden3comm/domain/use_cases/get_iden3comm_proofs_use_case_test.dart index 46a25b89..74bd9fb9 100644 --- a/test/iden3comm/domain/use_cases/get_iden3comm_proofs_use_case_test.dart +++ b/test/iden3comm/domain/use_cases/get_iden3comm_proofs_use_case_test.dart @@ -156,7 +156,7 @@ main() { getIden3commClaimsUseCase.execute(param: captureAnyNamed('param'))); expect(verifyGetClaims.callCount, 1); expect(verifyGetClaims.captured.first.genesisDid, param.genesisDid); - expect(verifyGetClaims.captured.first.privateKey, param.privateKey); + expect(verifyGetClaims.captured.first._privateKey, param.privateKey); var verifyLoadCircuit = verify(proofRepository.loadCircuitFiles(captureAny)); diff --git a/test/identity/domain/use_cases/identity/add_identity_use_case_test.dart b/test/identity/domain/use_cases/identity/add_identity_use_case_test.dart index 6735b037..1e60214c 100644 --- a/test/identity/domain/use_cases/identity/add_identity_use_case_test.dart +++ b/test/identity/domain/use_cases/identity/add_identity_use_case_test.dart @@ -69,7 +69,7 @@ void main() { verify(createIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(captureCreate.privateKey, CommonMocks.privateKey); + expect(captureCreate._privateKey, CommonMocks.privateKey); expect(captureCreate.profiles, CommonMocks.bigIntValues); expect( @@ -91,7 +91,7 @@ void main() { for (int i = 0; i < CommonMocks.profiles.values.length; i++) { expect( verifyState.captured[i].did, CommonMocks.profiles.values.toList()[i]); - expect(verifyState.captured[i].privateKey, CommonMocks.privateKey); + expect(verifyState.captured[i]._privateKey, CommonMocks.privateKey); } }); @@ -114,7 +114,7 @@ void main() { verify(createIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(captureCreate.privateKey, CommonMocks.privateKey); + expect(captureCreate._privateKey, CommonMocks.privateKey); expect(captureCreate.profiles, CommonMocks.bigIntValues); expect( @@ -146,7 +146,7 @@ void main() { verify(createIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(captureCreate.privateKey, CommonMocks.privateKey); + expect(captureCreate._privateKey, CommonMocks.privateKey); expect(captureCreate.profiles, CommonMocks.bigIntValues); verifyNever(identityRepository.getIdentity( diff --git a/test/identity/domain/use_cases/identity/add_new_identity_use_case_test.dart b/test/identity/domain/use_cases/identity/add_new_identity_use_case_test.dart index 51b4f306..ab4721bc 100644 --- a/test/identity/domain/use_cases/identity/add_new_identity_use_case_test.dart +++ b/test/identity/domain/use_cases/identity/add_new_identity_use_case_test.dart @@ -53,7 +53,7 @@ void main() { verify(addIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedCreate.privateKey, CommonMocks.privateKey); + expect(capturedCreate._privateKey, CommonMocks.privateKey); }); test( @@ -72,7 +72,7 @@ void main() { verify(addIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedCreate.privateKey, CommonMocks.privateKey); + expect(capturedCreate._privateKey, CommonMocks.privateKey); }); test( diff --git a/test/identity/domain/use_cases/identity/check_identity_validity_use_case_test.dart b/test/identity/domain/use_cases/identity/check_identity_validity_use_case_test.dart index 44bcc022..0b64c502 100644 --- a/test/identity/domain/use_cases/identity/check_identity_validity_use_case_test.dart +++ b/test/identity/domain/use_cases/identity/check_identity_validity_use_case_test.dart @@ -53,7 +53,7 @@ void main() { param: captureAnyNamed('param'))) .captured .first - .privateKey, + ._privateKey, CommonMocks.privateKey); }); diff --git a/test/identity/domain/use_cases/identity/create_identity_use_case_test.dart b/test/identity/domain/use_cases/identity/create_identity_use_case_test.dart index a46d57bb..f66e7696 100644 --- a/test/identity/domain/use_cases/identity/create_identity_use_case_test.dart +++ b/test/identity/domain/use_cases/identity/create_identity_use_case_test.dart @@ -86,11 +86,11 @@ void main() { param: captureAnyNamed('param'))) .captured; expect(capturedGetDid.length, CommonMocks.intValues.length + 1); - expect(capturedGetDid.first.privateKey, CommonMocks.privateKey); + expect(capturedGetDid.first._privateKey, CommonMocks.privateKey); expect(capturedGetDid.first.profileNonce, CommonMocks.genesisNonce); for (int i = 1; i < CommonMocks.bigIntValues.length + 1; i++) { - expect(capturedGetDid[i].privateKey, CommonMocks.privateKey); + expect(capturedGetDid[i]._privateKey, CommonMocks.privateKey); expect(capturedGetDid[i].profileNonce, CommonMocks.bigIntValues[i - 1]); } }); @@ -112,7 +112,7 @@ void main() { param: captureAnyNamed('param'))) .captured; expect(capturedGetDid.length, 1); - expect(capturedGetDid.first.privateKey, CommonMocks.privateKey); + expect(capturedGetDid.first._privateKey, CommonMocks.privateKey); expect(capturedGetDid.first.profileNonce, CommonMocks.genesisNonce); }); @@ -138,7 +138,7 @@ void main() { param: captureAnyNamed('param'))) .captured; expect(capturedGetDid.length, 1); - expect(capturedGetDid.first.privateKey, CommonMocks.privateKey); + expect(capturedGetDid.first._privateKey, CommonMocks.privateKey); expect(capturedGetDid.first.profileNonce, CommonMocks.genesisNonce); }); } diff --git a/test/identity/domain/use_cases/identity/update_identity_use_case_test.dart b/test/identity/domain/use_cases/identity/update_identity_use_case_test.dart index 14ddcc4b..2a20ecdf 100644 --- a/test/identity/domain/use_cases/identity/update_identity_use_case_test.dart +++ b/test/identity/domain/use_cases/identity/update_identity_use_case_test.dart @@ -64,7 +64,7 @@ void main() { verify(getIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedGet.privateKey, param.privateKey); + expect(capturedGet._privateKey, param.privateKey); expect(capturedGet.genesisDid, param.genesisDid); var capturedStore = verify(identityRepository.storeIdentity( @@ -83,7 +83,7 @@ void main() { // When await useCase.execute(param: param).then((_) => null).catchError((error) { expect(error, isA()); - expect(error.privateKey, param.privateKey); + expect(error._privateKey, param.privateKey); }); // Then @@ -91,7 +91,7 @@ void main() { verify(getIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedGet.privateKey, param.privateKey); + expect(capturedGet._privateKey, param.privateKey); expect(capturedGet.genesisDid, param.genesisDid); verifyNever(identityRepository.storeIdentity( @@ -113,7 +113,7 @@ void main() { verify(getIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedGet.privateKey, param.privateKey); + expect(capturedGet._privateKey, param.privateKey); expect(capturedGet.genesisDid, param.genesisDid); verifyNever(identityRepository.storeIdentity( diff --git a/test/identity/domain/use_cases/profile/add_profile_use_case_test.dart b/test/identity/domain/use_cases/profile/add_profile_use_case_test.dart index b132087d..fd262ab1 100644 --- a/test/identity/domain/use_cases/profile/add_profile_use_case_test.dart +++ b/test/identity/domain/use_cases/profile/add_profile_use_case_test.dart @@ -102,7 +102,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, BigInt.two); var getIdentityCapture = @@ -115,7 +115,7 @@ void main() { verify(updateIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedUpdate.privateKey, CommonMocks.privateKey); + expect(capturedUpdate._privateKey, CommonMocks.privateKey); expect(capturedUpdate.profiles[1], CommonMocks.profiles[1]); }, ); @@ -141,7 +141,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.nonce); var captureGetIdentity = @@ -149,7 +149,7 @@ void main() { .captured .first; expect(captureGetIdentity.genesisDid, CommonMocks.did); - expect(captureGetIdentity.privateKey, CommonMocks.privateKey); + expect(captureGetIdentity._privateKey, CommonMocks.privateKey); verifyNever(updateIdentityUseCase.execute(param: captureAnyNamed('param'))); }); @@ -170,7 +170,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, BigInt.two); var captureGetIdentity = @@ -178,7 +178,7 @@ void main() { .captured .first; expect(captureGetIdentity.genesisDid, CommonMocks.did); - expect(captureGetIdentity.privateKey, CommonMocks.privateKey); + expect(captureGetIdentity._privateKey, CommonMocks.privateKey); verifyNever(updateIdentityUseCase.execute(param: captureAnyNamed('param'))); }); diff --git a/test/identity/domain/use_cases/profile/create_profiles_use_case_test.dart b/test/identity/domain/use_cases/profile/create_profiles_use_case_test.dart index b1bea6e2..8cb94541 100644 --- a/test/identity/domain/use_cases/profile/create_profiles_use_case_test.dart +++ b/test/identity/domain/use_cases/profile/create_profiles_use_case_test.dart @@ -75,11 +75,11 @@ void main() { param: captureAnyNamed('param'))) .captured; expect(capturedGetDid.length, CommonMocks.intValues.length + 1); - expect(capturedGetDid.first.privateKey, CommonMocks.privateKey); + expect(capturedGetDid.first._privateKey, CommonMocks.privateKey); expect(capturedGetDid.first.profileNonce, CommonMocks.genesisNonce); for (int i = 1; i < CommonMocks.bigIntValues.length + 1; i++) { - expect(capturedGetDid[i].privateKey, CommonMocks.privateKey); + expect(capturedGetDid[i]._privateKey, CommonMocks.privateKey); expect(capturedGetDid[i].profileNonce, CommonMocks.bigIntValues[i - 1]); } }, @@ -107,7 +107,7 @@ void main() { param: captureAnyNamed('param'))) .captured; expect(capturedGetDid.length, 1); - expect(capturedGetDid.first.privateKey, CommonMocks.privateKey); + expect(capturedGetDid.first._privateKey, CommonMocks.privateKey); expect(capturedGetDid.first.profileNonce, CommonMocks.genesisNonce); }); } diff --git a/test/identity/domain/use_cases/profile/remove_profile_use_case_test.dart b/test/identity/domain/use_cases/profile/remove_profile_use_case_test.dart index 5f2a7323..ba6b82ed 100644 --- a/test/identity/domain/use_cases/profile/remove_profile_use_case_test.dart +++ b/test/identity/domain/use_cases/profile/remove_profile_use_case_test.dart @@ -102,7 +102,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.genesisNonce); var getIdentityCapture = @@ -115,7 +115,7 @@ void main() { verify(updateIdentityUseCase.execute(param: captureAnyNamed('param'))) .captured .first; - expect(capturedUpdate.privateKey, CommonMocks.privateKey); + expect(capturedUpdate._privateKey, CommonMocks.privateKey); expect(capturedUpdate.profiles[1], CommonMocks.profiles[1]); }, ); @@ -137,7 +137,7 @@ void main() { .captured .first; expect(captureCheck.did, CommonMocks.did); - expect(captureCheck.privateKey, CommonMocks.privateKey); + expect(captureCheck._privateKey, CommonMocks.privateKey); expect(captureCheck.profileNonce, CommonMocks.genesisNonce); var getIdentityCapture =