From d09a7747bc50949e9e490e3e1290b2b061a52a61 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sat, 5 Feb 2022 19:15:15 +0300
Subject: [PATCH 01/10] added new class AsyncKeysAuth

---
 Heart/AbstractSpace/asynckeysauth.cpp |  77 +++++++++++++
 Heart/AbstractSpace/asynckeysauth.h   | 152 ++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)
 create mode 100644 Heart/AbstractSpace/asynckeysauth.cpp
 create mode 100644 Heart/AbstractSpace/asynckeysauth.h

diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
new file mode 100644
index 0000000..fb45a88
--- /dev/null
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021-2021 QuasarApp.
+ * Distributed under the lgplv3 software license, see the accompanying
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+*/
+
+#include "asynckeysauth.h"
+#include "QCryptographicHash"
+
+
+namespace QH {
+
+AsyncKeysAuth::AsyncKeysAuth() {
+
+}
+
+bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
+
+    if (std::abs(time(0) - _unixTime) > allowedTimeRangeSec) {
+        return false;
+    }
+
+    QByteArray data = _publicKey;
+    data.insert(0, reinterpret_cast<const char*>(&_unixTime),
+                sizeof(_unixTime));
+
+    auto signData = QCryptographicHash::hash(data,
+                             QCryptographicHash::Sha256);
+
+    return decrypt(signData, _publicKey) == signData;
+}
+
+bool AsyncKeysAuth::prepare() {
+    _unixTime = time(0);
+
+    QByteArray data = _publicKey;
+    data.insert(0, reinterpret_cast<const char*>(&_unixTime),
+                sizeof(_unixTime));
+
+    auto signData = QCryptographicHash::hash(data,
+                             QCryptographicHash::Sha256);
+
+    setSignature(encrypt(signData, getPrivateKey()));
+
+    return isValid();
+}
+
+unsigned int AsyncKeysAuth::unixTime() const {
+    return _unixTime;
+}
+
+void AsyncKeysAuth::setUnixTime(unsigned int newUnixTime) {
+    _unixTime = newUnixTime;
+}
+
+const QByteArray &AsyncKeysAuth::signature() const {
+    return _signature;
+}
+
+const QByteArray &AsyncKeysAuth::publicKey() const {
+    return _publicKey;
+}
+
+void AsyncKeysAuth::setPublicKey(const QByteArray &newPublicKey) {
+    _publicKey = newPublicKey;
+}
+
+bool AsyncKeysAuth::isValid() const {
+    return _publicKey.size() && _signature.size() && _unixTime;
+}
+
+void AsyncKeysAuth::setSignature(const QByteArray &newSignature) {
+    _signature = newSignature;
+}
+
+}
diff --git a/Heart/AbstractSpace/asynckeysauth.h b/Heart/AbstractSpace/asynckeysauth.h
new file mode 100644
index 0000000..c194aab
--- /dev/null
+++ b/Heart/AbstractSpace/asynckeysauth.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021-2021 QuasarApp.
+ * Distributed under the lgplv3 software license, see the accompanying
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+*/
+
+#ifndef ASYNCKEYSAUTH_H
+#define ASYNCKEYSAUTH_H
+
+#include <QByteArray>
+
+
+
+namespace QH {
+
+/**
+ * @brief The AsyncKeysAuth class is base class for works with authorization of a pair of asynchronous keys
+ *
+ * ### How to it works:
+ *
+ * * -----
+ *
+ * ** Client Part **
+ *
+ * * Client make a pair keys (public and private) = PUB and PRIV
+ * * Client get current unix time = U
+ * * Client make a data for signing = S
+ *      S = SHA256{U + PUB}
+ * * Client make a signature SIG
+ *      SIG = PRIV.encript(S)
+ * * Client prepare a auth request for server = R:
+ *      R = {U,SIG,PUB}
+ * * Cleint send R to server
+ *
+ * * -----
+ *
+ * ** Server Part **
+ *
+ * * Server receive R from client.
+ * * Server compare U time with current unix time.
+ * * If the diferrence more then allowed value then server reject an auth
+ * * Server make S value as a client
+ * * Server decript SIG value and comapre it with S value
+ * * If decripted value is deferrend of the S then server reject an auth.
+ * * If decripted value equals with S value then server accept an auth.
+ * * After accept server create new user with ID = sha256(PUB) or
+ * if user alredy exits make them as a logined user.
+ *
+ *
+ */
+class AsyncKeysAuth
+{
+public:
+    AsyncKeysAuth();
+
+    /**
+     * @brief auth This method make authentication and return true if the authentication finished successful else false.
+     * @return true if the authentication finished successful else false.
+     */
+    bool auth(int allowedTimeRangeSec) const;
+
+    /**
+     * @brief prepare This method will generate signature for autentication of client. Please inboke this method before send request to server.
+     * @return true if signature generated sucessuful.
+     */
+    bool prepare();
+
+    /**
+     * @brief unixTime This method return unix time that client added for authentication.
+     * @return unix time that client added for authentication.
+     * @see AsyncKeysAuth::setUnixTime
+     */
+    unsigned int unixTime() const;
+
+    /**
+     * @brief setUnixTime This method sets new value of the unixTime propertye.
+     * @param newUnixTime This is new unix time value. Unix time sets in secunds from 1970 year
+     */
+    void setUnixTime(unsigned int newUnixTime);
+
+    /**
+     * @brief signature This method return signature array.
+     * @return signature array.
+     * @see AsyncKeysAuth::setSignature
+     */
+    const QByteArray &signature() const;
+
+    /**
+     * @brief publicKey This method return public key that client added for authentication.
+     * @note The @a publicKey will be used forcreate user id.
+     * @return public key that client added for authentication.
+     * @see AsyncKeysAuth::setPublicKey
+     */
+    const QByteArray &publicKey() const;
+
+    /**
+     * @brief setPublicKey This method sets new public key for authentication.
+     * @param newPublicKey Thiy is new key.
+     * @see AsyncKeysAuth::publicKey
+     */
+    void setPublicKey(const QByteArray &newPublicKey);
+
+    /**
+     * @brief isValid this method check this ibject to valid.
+     * @return return true if object contains valid signature else false.
+     * @note Invoke the AsyncKeysAuth::prepare method before check valid of object. All object that not be preparred is invalid.
+     */
+    bool isValid() const;
+
+protected:
+
+    /**
+     * @brief encrypt This method should be encript the @a inputData using the @a key.
+     * @param inputData This is input data that should be encripted.
+     * @param key This is a privete key for encription the @a inpputData.
+     * @return encripted data array.
+     * @see AsyncKeysAuth::descrupt
+     */
+    virtual QByteArray encrypt(const QByteArray& inputData, const QByteArray& key) const = 0;
+
+    /**
+     * @brief decrypt This method should be decrypt the @a inputData using the @a key.
+     * @param inputData This is input data that should be decripted.
+     * @param key This is a public key for encription the @a inpputData.
+     * @return decripted data array.
+     * @see AsyncKeysAuth::encrypt
+     */
+    virtual QByteArray decrypt(const QByteArray& inputData, const QByteArray& key) const = 0;
+
+    /**
+     * @brief getPrivateKey This method should be return private key for the public key that saved in this object.
+     * @return private key for the public key that saved in this object.
+     */
+    virtual QByteArray getPrivateKey() const = 0;
+
+    /**
+     * @brief setSignature Tihis is internal method for sets new signature value.
+     * @param newSignature new signature value.
+     * @note used in the
+     */
+    void setSignature(const QByteArray &newSignature);
+
+private:
+    unsigned int _unixTime = 0;
+    QByteArray _signature;
+    QByteArray _publicKey;
+};
+
+}
+
+#endif // ASYNCKEYSAUTH_H

From 1e290c6810b046cde9f942449040ad6c7956e216 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sat, 5 Feb 2022 19:15:56 +0300
Subject: [PATCH 02/10] fix windows

---
 Heart/AbstractSpace/asynckeysauth.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.h b/Heart/AbstractSpace/asynckeysauth.h
index c194aab..a5a4d28 100644
--- a/Heart/AbstractSpace/asynckeysauth.h
+++ b/Heart/AbstractSpace/asynckeysauth.h
@@ -10,7 +10,7 @@
 
 #include <QByteArray>
 
-
+#include "heart_global.h"
 
 namespace QH {
 
@@ -49,7 +49,7 @@ namespace QH {
  *
  *
  */
-class AsyncKeysAuth
+class HEARTSHARED_EXPORT AsyncKeysAuth
 {
 public:
     AsyncKeysAuth();

From 9845f972feb526da2a7231f924272fdca34941c4 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Mon, 7 Feb 2022 10:13:36 +0300
Subject: [PATCH 03/10] remove qtsecret

---
 .gitmodules                           |  3 ---
 Heart/AbstractSpace/asynckeysauth.cpp |  4 ++--
 Heart/AbstractSpace/asynckeysauth.h   | 25 ++++++++++++-------------
 Heart/CMakeLists.txt                  |  3 ---
 Heart/Qt-Secret                       |  1 -
 5 files changed, 14 insertions(+), 22 deletions(-)
 delete mode 160000 Heart/Qt-Secret

diff --git a/.gitmodules b/.gitmodules
index 44502c6..39d96f8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,6 +2,3 @@
 	path = QuasarAppLib
 	url = https://github.com/QuasarApp/QuasarAppLib.git
 
-[submodule "Heart/Qt-Secret"]
-	path = Heart/Qt-Secret
-	url = https://github.com/QuasarApp/Qt-Secret.git
diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
index fb45a88..912e3e5 100644
--- a/Heart/AbstractSpace/asynckeysauth.cpp
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -28,7 +28,7 @@ bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
     auto signData = QCryptographicHash::hash(data,
                              QCryptographicHash::Sha256);
 
-    return decrypt(signData, _publicKey) == signData;
+    return checkSign(signData, signData, _publicKey);
 }
 
 bool AsyncKeysAuth::prepare() {
@@ -41,7 +41,7 @@ bool AsyncKeysAuth::prepare() {
     auto signData = QCryptographicHash::hash(data,
                              QCryptographicHash::Sha256);
 
-    setSignature(encrypt(signData, getPrivateKey()));
+    setSignature(signMessage(signData, getPrivateKey()));
 
     return isValid();
 }
diff --git a/Heart/AbstractSpace/asynckeysauth.h b/Heart/AbstractSpace/asynckeysauth.h
index a5a4d28..fa2874c 100644
--- a/Heart/AbstractSpace/asynckeysauth.h
+++ b/Heart/AbstractSpace/asynckeysauth.h
@@ -28,7 +28,7 @@ namespace QH {
  * * Client make a data for signing = S
  *      S = SHA256{U + PUB}
  * * Client make a signature SIG
- *      SIG = PRIV.encript(S)
+ *      SIG = PRIV.signMessage(S)
  * * Client prepare a auth request for server = R:
  *      R = {U,SIG,PUB}
  * * Cleint send R to server
@@ -41,9 +41,8 @@ namespace QH {
  * * Server compare U time with current unix time.
  * * If the diferrence more then allowed value then server reject an auth
  * * Server make S value as a client
- * * Server decript SIG value and comapre it with S value
- * * If decripted value is deferrend of the S then server reject an auth.
- * * If decripted value equals with S value then server accept an auth.
+ * * Server check SIG value and comapre it with S value
+ * * If message sign is valid then server accept an auth else reject.
  * * After accept server create new user with ID = sha256(PUB) or
  * if user alredy exits make them as a logined user.
  *
@@ -111,22 +110,23 @@ public:
 protected:
 
     /**
-     * @brief encrypt This method should be encript the @a inputData using the @a key.
-     * @param inputData This is input data that should be encripted.
-     * @param key This is a privete key for encription the @a inpputData.
-     * @return encripted data array.
+     * @brief signMessage This method should be sign the @a message using the @a key.
+     * @param message This is input data that should be signed.
+     * @param key This is a privete key for encription the @a message.
+     * @return signature data array.
      * @see AsyncKeysAuth::descrupt
      */
-    virtual QByteArray encrypt(const QByteArray& inputData, const QByteArray& key) const = 0;
+    virtual QByteArray &signMessage(const QByteArray& message, const QByteArray& key) const = 0;
 
     /**
-     * @brief decrypt This method should be decrypt the @a inputData using the @a key.
-     * @param inputData This is input data that should be decripted.
+     * @brief checkSign This method should be check signature of the @a message using the @a key.
+     * @param message This is input data that should be decripted.
+     * @param signature This is signature that will be checked for the @a message.
      * @param key This is a public key for encription the @a inpputData.
      * @return decripted data array.
      * @see AsyncKeysAuth::encrypt
      */
-    virtual QByteArray decrypt(const QByteArray& inputData, const QByteArray& key) const = 0;
+    virtual bool checkSign(const QByteArray& message, const QByteArray& signature, const QByteArray& key) const = 0;
 
     /**
      * @brief getPrivateKey This method should be return private key for the public key that saved in this object.
@@ -141,7 +141,6 @@ protected:
      */
     void setSignature(const QByteArray &newSignature);
 
-private:
     unsigned int _unixTime = 0;
     QByteArray _signature;
     QByteArray _publicKey;
diff --git a/Heart/CMakeLists.txt b/Heart/CMakeLists.txt
index 15176c3..5732390 100644
--- a/Heart/CMakeLists.txt
+++ b/Heart/CMakeLists.txt
@@ -7,9 +7,6 @@
 
 cmake_minimum_required(VERSION 3.10)
 
-if (${HEART_BUILD_LVL} GREATER_EQUAL 2)
-    add_subdirectory(Qt-Secret)
-endif()
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 set(CMAKE_CXX_STANDARD 17)
diff --git a/Heart/Qt-Secret b/Heart/Qt-Secret
deleted file mode 160000
index 810376b..0000000
--- a/Heart/Qt-Secret
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 810376b593479606e4c8841e70676bb1f5790e2c

From 622302a3ff87f8ca4b75a93e9acab93a613fc47c Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Wed, 9 Feb 2022 18:09:24 +0300
Subject: [PATCH 04/10] fix auth signature

---
 Heart/AbstractSpace/asynckeysauth.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.h b/Heart/AbstractSpace/asynckeysauth.h
index fa2874c..f4a8b29 100644
--- a/Heart/AbstractSpace/asynckeysauth.h
+++ b/Heart/AbstractSpace/asynckeysauth.h
@@ -116,7 +116,7 @@ protected:
      * @return signature data array.
      * @see AsyncKeysAuth::descrupt
      */
-    virtual QByteArray &signMessage(const QByteArray& message, const QByteArray& key) const = 0;
+    virtual QByteArray signMessage(const QByteArray& message, const QByteArray& key) const = 0;
 
     /**
      * @brief checkSign This method should be check signature of the @a message using the @a key.

From 5ff6c3ef96674d49769f8d98f86cfdc7d8327e83 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Thu, 10 Feb 2022 20:06:39 +0300
Subject: [PATCH 05/10] added tests for ecdsa auth

---
 Heart/AbstractSpace/authecdsa.cpp          | 180 +++++++++++++++++++++
 Heart/AbstractSpace/authecdsa.h            |  45 ++++++
 HeartTests/AbstractSpace/ecdsaauthtest.cpp |  62 +++++++
 HeartTests/AbstractSpace/ecdsaauthtest.h   |  24 +++
 HeartTests/tst_testprotockol.cpp           |   2 +
 QuasarAppLib                               |   2 +-
 6 files changed, 314 insertions(+), 1 deletion(-)
 create mode 100644 Heart/AbstractSpace/authecdsa.cpp
 create mode 100644 Heart/AbstractSpace/authecdsa.h
 create mode 100644 HeartTests/AbstractSpace/ecdsaauthtest.cpp
 create mode 100644 HeartTests/AbstractSpace/ecdsaauthtest.h

diff --git a/Heart/AbstractSpace/authecdsa.cpp b/Heart/AbstractSpace/authecdsa.cpp
new file mode 100644
index 0000000..b396b74
--- /dev/null
+++ b/Heart/AbstractSpace/authecdsa.cpp
@@ -0,0 +1,180 @@
+//#
+//# Copyright (C) 2021-2022 QuasarApp.
+//# Distributed under the GPLv3 software license, see the accompanying
+//# Everyone is permitted to copy and distribute verbatim copies
+//# of this license document, but changing it is not allowed.
+//#
+
+
+#include "authecdsa.h"
+
+#include <openssl/ec.h>      // for EC_GROUP_new_by_curve_name, EC_GROUP_free, EC_KEY_new, EC_KEY_set_group, EC_KEY_generate_key, EC_KEY_free
+#include <openssl/ecdsa.h>   // for ECDSA_do_sign, ECDSA_do_verify
+#include <openssl/obj_mac.h> // for NID_secp192k1
+#include <openssl/evp.h>
+
+#include <QCryptographicHash>
+#include <QIODevice>
+#include <QVector>
+
+namespace QH {
+
+AuthECDSA::AuthECDSA() {
+
+}
+
+QByteArray bignumToArray(const BIGNUM* num) {
+    int length = BN_bn2mpi(num, nullptr);
+    QVector<unsigned char> data(length);
+    BN_bn2mpi(num, data.data());
+    QByteArray result;
+    result.insert(0, reinterpret_cast<char*>(data.data()), data.length());
+    return result;
+}
+
+BIGNUM* bignumFromArray(const QByteArray& array) {
+    return BN_mpi2bn(reinterpret_cast<const unsigned char*>(array.data()), array.length(), nullptr);
+}
+
+QByteArray extractPrivateKey(EC_KEY* ec_key) {
+    const BIGNUM* ec_priv = EC_KEY_get0_private_key(ec_key);
+    int length = BN_bn2mpi(ec_priv, nullptr);
+    QVector<unsigned char> data(length);
+    data.insert(0,  length);
+    BN_bn2mpi(ec_priv, data.data());
+    QByteArray result;
+    result.insert(0, reinterpret_cast<char*>(data.data()), data.length());
+    return result;
+}
+
+QByteArray extractPublicKey(EC_KEY* key, EC_GROUP* group) {
+
+    QByteArray data;
+    point_conversion_form_t form = EC_GROUP_get_point_conversion_form(group);
+
+    unsigned char* pub_key_buffer;
+    size_t length = EC_KEY_key2buf(key, form, &pub_key_buffer, nullptr);
+
+    if (length <= 0) {
+        return {};
+    }
+
+    data.insert(0, reinterpret_cast<const char*>(pub_key_buffer), length);
+
+    OPENSSL_free(pub_key_buffer);
+
+    return data;
+}
+
+bool AuthECDSA::makeKeys(QByteArray &pubKey, QByteArray &privKey) {
+
+    EC_KEY *eckey= nullptr;
+    EC_GROUP *ecgroup = nullptr;
+
+
+    auto free = [&eckey, &ecgroup] () {
+        if (ecgroup)
+            EC_GROUP_free(ecgroup);
+
+        if (eckey)
+            EC_KEY_free(eckey);
+    };
+
+    eckey = EC_KEY_new();
+    if (!eckey)
+        return false;
+
+    ecgroup = EC_GROUP_new_by_curve_name(NID_secp256k1);
+
+    if (!ecgroup) {
+        free();
+        return false;
+    }
+
+    const int success = 1;
+    if ( success != EC_KEY_set_group(eckey, ecgroup)) {
+        free();
+        return false;
+    }
+
+    if ( success != EC_KEY_generate_key(eckey)) {
+        free();
+        return false;
+    }
+
+    pubKey = extractPublicKey(eckey, ecgroup);
+    privKey = extractPrivateKey(eckey);
+
+    return pubKey.length() && privKey.length();
+}
+
+QByteArray AuthECDSA::signMessage(const QByteArray &inputData,
+                                  const QByteArray &key) const {
+
+    auto hash = QCryptographicHash::hash(inputData, QCryptographicHash::Sha256);
+
+    EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+
+    BIGNUM* priv = BN_mpi2bn(reinterpret_cast<const unsigned char*>(key.data()),
+                             key.length(), nullptr);
+    EC_KEY_set_private_key(eckey, priv);
+    BN_free(priv);
+
+    ECDSA_SIG *signature = ECDSA_do_sign(reinterpret_cast<const unsigned char*>(hash.data()),
+                                         hash.length(), eckey);
+    EC_KEY_free(eckey);
+
+    const BIGNUM * R, *S;
+    ECDSA_SIG_get0(signature, &R, &S);
+
+    QByteArray result;
+    QDataStream stream(&result, QIODevice::WriteOnly);
+
+    stream << bignumToArray(R);
+    stream << bignumToArray(S);
+
+    ECDSA_SIG_free(signature);
+
+    return result;
+}
+
+bool AuthECDSA::checkSign(const QByteArray &inputData,
+                          const QByteArray &signature,
+                          const QByteArray &key) const {
+
+    // extract key from raw array;
+    EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    const EC_GROUP* ec_group = EC_KEY_get0_group(eckey);
+    EC_POINT* ec_point = EC_POINT_new(ec_group);
+    EC_POINT_oct2point(ec_group, ec_point,
+                       reinterpret_cast<const unsigned char*>(key.data()),
+                       key.length(), nullptr);
+    EC_KEY_set_public_key(eckey, ec_point);
+    EC_POINT_free(ec_point);
+
+    // extract signature from raw array
+
+    BIGNUM * R, *S;
+    QDataStream stream(signature);
+
+    QByteArray rR,rS;
+    stream >> rR;
+    stream >> rS;
+    R = bignumFromArray(rR);
+    S = bignumFromArray(rS);
+
+    ECDSA_SIG *sig = ECDSA_SIG_new();
+    ECDSA_SIG_set0(sig, R, S);
+
+    auto hash = QCryptographicHash::hash(inputData, QCryptographicHash::Sha256);
+
+    int verify_status = ECDSA_do_verify(reinterpret_cast<const unsigned char*>(hash.data()),
+                                        hash.length(), sig, eckey);
+
+    ECDSA_SIG_free(sig);
+
+    return verify_status == 1;
+
+}
+
+}
diff --git a/Heart/AbstractSpace/authecdsa.h b/Heart/AbstractSpace/authecdsa.h
new file mode 100644
index 0000000..efec031
--- /dev/null
+++ b/Heart/AbstractSpace/authecdsa.h
@@ -0,0 +1,45 @@
+//#
+//# Copyright (C) 2021-2022 QuasarApp.
+//# Distributed under the GPLv3 software license, see the accompanying
+//# Everyone is permitted to copy and distribute verbatim copies
+//# of this license document, but changing it is not allowed.
+//#
+
+
+#ifndef AUTHECDSA_H
+#define AUTHECDSA_H
+
+#include "abstractdata.h"
+#include <asynckeysauth.h>
+
+namespace QH {
+
+/**
+ * @brief The AuthECDSA class is ecdsa implementation of the Async authentication. This implementation based on Openssl library.
+ */
+class AuthECDSA: public QH::AsyncKeysAuth
+{
+
+public:
+    AuthECDSA();
+
+    /**
+     * @brief makeKeys This static method generate the public and private keys of the ECDSA.
+     * @param pubKey This is result public key.
+     * @param privKey This is result private key.
+     * @return true if keys generated successful.
+     */
+    static bool makeKeys(QByteArray &pubKey, QByteArray &privKey);
+    
+    // AsyncKeysAuth interface
+protected:
+    QByteArray signMessage(const QByteArray &inputData, const QByteArray &key) const override;
+    bool checkSign(const QByteArray &inputData, const QByteArray &signature, const QByteArray &key) const override;
+
+};
+
+
+}
+
+
+#endif // AUTHECDSA_H
diff --git a/HeartTests/AbstractSpace/ecdsaauthtest.cpp b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
new file mode 100644
index 0000000..286cdb8
--- /dev/null
+++ b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022-2022 QuasarApp.
+ * Distributed under the lgplv3 software license, see the accompanying
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+*/
+
+#include "ecdsaauthtest.h"
+#include "authecdsa.h"
+#include <QtTest>
+
+/*
+ * test class
+ */
+class ECDSA: public QH::AuthECDSA {
+
+public:
+    ECDSA(const QByteArray &publicKey, const QByteArray &privKey) {
+        setPublicKey(publicKey);
+        _priv = privKey;
+    }
+
+    // AsyncKeysAuth interface
+protected:
+    QByteArray getPrivateKey() const override {
+        return _priv;
+    };
+
+private:
+    QByteArray _priv;
+
+};
+
+ECDSAAuthTest::ECDSAAuthTest() {
+
+}
+
+ECDSAAuthTest::~ECDSAAuthTest() {
+
+}
+
+void ECDSAAuthTest::test() {
+    QByteArray pub, priv;
+
+    QVERIFY(QH::AuthECDSA::makeKeys(pub, priv));
+
+    QVERIFY(pub.length() && priv.length());
+
+    ECDSA edsa(pub, priv);
+
+    QVERIFY(!edsa.isValid());
+
+    QVERIFY(!edsa.auth(60));
+
+    QVERIFY(edsa.prepare());
+    QVERIFY(edsa.isValid());
+
+    QVERIFY(edsa.auth(60));
+    QVERIFY(!edsa.auth(0));
+
+
+}
diff --git a/HeartTests/AbstractSpace/ecdsaauthtest.h b/HeartTests/AbstractSpace/ecdsaauthtest.h
new file mode 100644
index 0000000..886ed9d
--- /dev/null
+++ b/HeartTests/AbstractSpace/ecdsaauthtest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022-2022 QuasarApp.
+ * Distributed under the lgplv3 software license, see the accompanying
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+*/
+
+#ifndef ECDSAAUTHTEST_H
+#define ECDSAAUTHTEST_H
+
+#include "test.h"
+#include "testutils.h"
+
+class ECDSAAuthTest:  public Test, protected TestUtils
+{
+public:
+    ECDSAAuthTest();
+    ~ECDSAAuthTest();
+
+    void test();
+
+};
+
+#endif // ECDSAAUTHTEST_H
diff --git a/HeartTests/tst_testprotockol.cpp b/HeartTests/tst_testprotockol.cpp
index d2d0b43..f22fa71 100644
--- a/HeartTests/tst_testprotockol.cpp
+++ b/HeartTests/tst_testprotockol.cpp
@@ -12,6 +12,7 @@
 #include "abstractnodetest.h"
 #include <shedullertest.h>
 #include <bigdatatest.h>
+#include <ecdsaauthtest.h>
 #endif
 #if HEART_BUILD_LVL >= 1
 #include <basenodetest.h>
@@ -42,6 +43,7 @@ private slots:
     TestCase(abstractNodeTest, AbstractNodeTest)
     TestCase(bigDataTest, BigDataTest);
     TestCase(shedullerTest, ShedullerTest);
+    TestCase(ecdsaAuthTest, ECDSAAuthTest);
 #endif
 #if HEART_BUILD_LVL >= 1
     TestCase(baseNodeTest, BaseNodeTest)
diff --git a/QuasarAppLib b/QuasarAppLib
index f7ee5cd..9cc9e77 160000
--- a/QuasarAppLib
+++ b/QuasarAppLib
@@ -1 +1 @@
-Subproject commit f7ee5cd272d6e49d27b557e3fa481e54fe27e519
+Subproject commit 9cc9e77babc5cbf2f46276caff3783c273764556

From 918357b9e7b938cde33c4139441bbbe9912f5913 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sun, 13 Feb 2022 18:45:11 +0300
Subject: [PATCH 06/10] added support ecdsa login

---
 Heart/AbstractSpace/asynckeysauth.cpp      |  12 +-
 Heart/AbstractSpace/authecdsa.cpp          | 161 ++++++++++++++-------
 Heart/AbstractSpace/authecdsa.h            |   5 +
 HeartTests/AbstractSpace/ecdsaauthtest.cpp |   4 +-
 QuasarAppLib                               |   2 +-
 5 files changed, 122 insertions(+), 62 deletions(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
index 912e3e5..da920ec 100644
--- a/Heart/AbstractSpace/asynckeysauth.cpp
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -17,7 +17,7 @@ AsyncKeysAuth::AsyncKeysAuth() {
 
 bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
 
-    if (std::abs(time(0) - _unixTime) > allowedTimeRangeSec) {
+    if (std::abs(time(0) - _unixTime) >= allowedTimeRangeSec) {
         return false;
     }
 
@@ -25,10 +25,7 @@ bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
     data.insert(0, reinterpret_cast<const char*>(&_unixTime),
                 sizeof(_unixTime));
 
-    auto signData = QCryptographicHash::hash(data,
-                             QCryptographicHash::Sha256);
-
-    return checkSign(signData, signData, _publicKey);
+    return checkSign(data, _signature, _publicKey);
 }
 
 bool AsyncKeysAuth::prepare() {
@@ -38,10 +35,7 @@ bool AsyncKeysAuth::prepare() {
     data.insert(0, reinterpret_cast<const char*>(&_unixTime),
                 sizeof(_unixTime));
 
-    auto signData = QCryptographicHash::hash(data,
-                             QCryptographicHash::Sha256);
-
-    setSignature(signMessage(signData, getPrivateKey()));
+    setSignature(signMessage(data, getPrivateKey()));
 
     return isValid();
 }
diff --git a/Heart/AbstractSpace/authecdsa.cpp b/Heart/AbstractSpace/authecdsa.cpp
index b396b74..a0a9482 100644
--- a/Heart/AbstractSpace/authecdsa.cpp
+++ b/Heart/AbstractSpace/authecdsa.cpp
@@ -8,14 +8,15 @@
 
 #include "authecdsa.h"
 
-#include <openssl/ec.h>      // for EC_GROUP_new_by_curve_name, EC_GROUP_free, EC_KEY_new, EC_KEY_set_group, EC_KEY_generate_key, EC_KEY_free
 #include <openssl/ecdsa.h>   // for ECDSA_do_sign, ECDSA_do_verify
 #include <openssl/obj_mac.h> // for NID_secp192k1
 #include <openssl/evp.h>
+#include <openssl/err.h>
 
 #include <QCryptographicHash>
 #include <QIODevice>
 #include <QVector>
+#include <quasarapp.h>
 
 namespace QH {
 
@@ -23,6 +24,14 @@ AuthECDSA::AuthECDSA() {
 
 }
 
+void printlastOpenSSlError() {
+    int error = ERR_get_error();
+    char buffer[256];
+    ERR_error_string(error, buffer);
+    QuasarAppUtils::Params::log(QString("openssl: %0").arg(buffer),
+                                QuasarAppUtils::Error);
+}
+
 QByteArray bignumToArray(const BIGNUM* num) {
     int length = BN_bn2mpi(num, nullptr);
     QVector<unsigned char> data(length);
@@ -33,18 +42,19 @@ QByteArray bignumToArray(const BIGNUM* num) {
 }
 
 BIGNUM* bignumFromArray(const QByteArray& array) {
-    return BN_mpi2bn(reinterpret_cast<const unsigned char*>(array.data()), array.length(), nullptr);
+    auto d = reinterpret_cast<const unsigned char*>(array.data());
+    BIGNUM* result = BN_mpi2bn(d,
+                               array.length(), nullptr);
+    if (!result) {
+        printlastOpenSSlError();
+    }
+
+    return result;
 }
 
 QByteArray extractPrivateKey(EC_KEY* ec_key) {
     const BIGNUM* ec_priv = EC_KEY_get0_private_key(ec_key);
-    int length = BN_bn2mpi(ec_priv, nullptr);
-    QVector<unsigned char> data(length);
-    data.insert(0,  length);
-    BN_bn2mpi(ec_priv, data.data());
-    QByteArray result;
-    result.insert(0, reinterpret_cast<char*>(data.data()), data.length());
-    return result;
+    return bignumToArray(ec_priv);
 }
 
 QByteArray extractPublicKey(EC_KEY* key, EC_GROUP* group) {
@@ -56,6 +66,7 @@ QByteArray extractPublicKey(EC_KEY* key, EC_GROUP* group) {
     size_t length = EC_KEY_key2buf(key, form, &pub_key_buffer, nullptr);
 
     if (length <= 0) {
+        printlastOpenSSlError();
         return {};
     }
 
@@ -71,34 +82,14 @@ bool AuthECDSA::makeKeys(QByteArray &pubKey, QByteArray &privKey) {
     EC_KEY *eckey= nullptr;
     EC_GROUP *ecgroup = nullptr;
 
-
-    auto free = [&eckey, &ecgroup] () {
-        if (ecgroup)
-            EC_GROUP_free(ecgroup);
-
-        if (eckey)
-            EC_KEY_free(eckey);
-    };
-
-    eckey = EC_KEY_new();
-    if (!eckey)
-        return false;
-
-    ecgroup = EC_GROUP_new_by_curve_name(NID_secp256k1);
-
-    if (!ecgroup) {
-        free();
+    if (!prepareKeyAdnGroupObjects(&eckey, &ecgroup)) {
         return false;
     }
 
-    const int success = 1;
-    if ( success != EC_KEY_set_group(eckey, ecgroup)) {
-        free();
-        return false;
-    }
-
-    if ( success != EC_KEY_generate_key(eckey)) {
-        free();
+    if (!EC_KEY_generate_key(eckey)) {
+        printlastOpenSSlError();
+        EC_GROUP_free(ecgroup);
+        EC_KEY_free(eckey);
         return false;
     }
 
@@ -111,18 +102,34 @@ bool AuthECDSA::makeKeys(QByteArray &pubKey, QByteArray &privKey) {
 QByteArray AuthECDSA::signMessage(const QByteArray &inputData,
                                   const QByteArray &key) const {
 
-    auto hash = QCryptographicHash::hash(inputData, QCryptographicHash::Sha256);
+    EC_KEY *eckey= nullptr;
+    EC_GROUP *ecgroup = nullptr;
 
-    EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
+    if (!prepareKeyAdnGroupObjects(&eckey, &ecgroup)) {
+        return {};
+    }
 
-    BIGNUM* priv = BN_mpi2bn(reinterpret_cast<const unsigned char*>(key.data()),
-                             key.length(), nullptr);
-    EC_KEY_set_private_key(eckey, priv);
-    BN_free(priv);
+    auto hash = QCryptographicHash::hash(inputData,
+                                         QCryptographicHash::Sha256);
+
+    BIGNUM* priv = bignumFromArray(key);
+    if (!EC_KEY_set_private_key(eckey, priv)) {
+        printlastOpenSSlError();
+        EC_GROUP_free(ecgroup);
+        EC_KEY_free(eckey);
+        return {};
+    };
 
     ECDSA_SIG *signature = ECDSA_do_sign(reinterpret_cast<const unsigned char*>(hash.data()),
                                          hash.length(), eckey);
+    BN_free(priv);
     EC_KEY_free(eckey);
+    EC_GROUP_free(ecgroup);
+
+    if (!signature) {
+        printlastOpenSSlError();
+        return {};
+    }
 
     const BIGNUM * R, *S;
     ECDSA_SIG_get0(signature, &R, &S);
@@ -142,15 +149,6 @@ bool AuthECDSA::checkSign(const QByteArray &inputData,
                           const QByteArray &signature,
                           const QByteArray &key) const {
 
-    // extract key from raw array;
-    EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
-    const EC_GROUP* ec_group = EC_KEY_get0_group(eckey);
-    EC_POINT* ec_point = EC_POINT_new(ec_group);
-    EC_POINT_oct2point(ec_group, ec_point,
-                       reinterpret_cast<const unsigned char*>(key.data()),
-                       key.length(), nullptr);
-    EC_KEY_set_public_key(eckey, ec_point);
-    EC_POINT_free(ec_point);
 
     // extract signature from raw array
 
@@ -166,15 +164,78 @@ bool AuthECDSA::checkSign(const QByteArray &inputData,
     ECDSA_SIG *sig = ECDSA_SIG_new();
     ECDSA_SIG_set0(sig, R, S);
 
-    auto hash = QCryptographicHash::hash(inputData, QCryptographicHash::Sha256);
+    auto hash = QCryptographicHash::hash(inputData,
+                                         QCryptographicHash::Sha256);
+
+
+    EC_KEY *eckey= nullptr;
+    EC_GROUP *ecgroup = nullptr;
+
+    if (!prepareKeyAdnGroupObjects(&eckey, &ecgroup)) {
+        ECDSA_SIG_free(sig);
+        return {};
+    }
+
+
+    // extract key from raw array;
+    EC_POINT* ec_point = EC_POINT_new(ecgroup);
+    EC_POINT_oct2point(ecgroup, ec_point,
+                       reinterpret_cast<const unsigned char*>(key.data()),
+                       key.length(), nullptr);
+
+    EC_KEY_set_public_key(eckey, ec_point);
+
 
     int verify_status = ECDSA_do_verify(reinterpret_cast<const unsigned char*>(hash.data()),
                                         hash.length(), sig, eckey);
 
     ECDSA_SIG_free(sig);
+    EC_POINT_free(ec_point);
 
     return verify_status == 1;
 
 }
 
+bool AuthECDSA::prepareKeyAdnGroupObjects(EC_KEY **eckey, EC_GROUP **ecgroup) {
+
+    // input data should be valid pointers to pointers of key and group objects.
+    if (!(eckey && ecgroup))
+        return false;
+
+    // input pointers should be nullptr;
+    if ((*eckey) || (*ecgroup))
+        return false;
+
+    auto free = [eckey, ecgroup] () {
+        if (*ecgroup)
+            EC_GROUP_free(*ecgroup);
+
+        if (*eckey)
+            EC_KEY_free(*eckey);
+    };
+
+    *eckey = EC_KEY_new();
+    if (!*eckey) {
+        printlastOpenSSlError();
+        free();
+        return false;
+    }
+
+    *ecgroup = EC_GROUP_new_by_curve_name(NID_secp256k1);
+
+    if (!*ecgroup) {
+        printlastOpenSSlError();
+        free();
+        return false;
+    }
+
+    if (!EC_KEY_set_group(*eckey, *ecgroup)) {
+        printlastOpenSSlError();
+        free();
+        return false;
+    }
+
+    return true;
+}
+
 }
diff --git a/Heart/AbstractSpace/authecdsa.h b/Heart/AbstractSpace/authecdsa.h
index efec031..faa8363 100644
--- a/Heart/AbstractSpace/authecdsa.h
+++ b/Heart/AbstractSpace/authecdsa.h
@@ -11,6 +11,8 @@
 
 #include "abstractdata.h"
 #include <asynckeysauth.h>
+#include <openssl/ec.h>      // for EC_GROUP_new_by_curve_name, EC_GROUP_free, EC_KEY_new, EC_KEY_set_group, EC_KEY_generate_key, EC_KEY_free
+
 
 namespace QH {
 
@@ -36,6 +38,9 @@ protected:
     QByteArray signMessage(const QByteArray &inputData, const QByteArray &key) const override;
     bool checkSign(const QByteArray &inputData, const QByteArray &signature, const QByteArray &key) const override;
 
+private:
+    static bool prepareKeyAdnGroupObjects(EC_KEY **eckey, EC_GROUP **ecgroup);
+
 };
 
 
diff --git a/HeartTests/AbstractSpace/ecdsaauthtest.cpp b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
index 286cdb8..2e21ed2 100644
--- a/HeartTests/AbstractSpace/ecdsaauthtest.cpp
+++ b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
@@ -50,12 +50,12 @@ void ECDSAAuthTest::test() {
 
     QVERIFY(!edsa.isValid());
 
-    QVERIFY(!edsa.auth(60));
+    QVERIFY(!edsa.auth(600));
 
     QVERIFY(edsa.prepare());
     QVERIFY(edsa.isValid());
 
-    QVERIFY(edsa.auth(60));
+    QVERIFY(edsa.auth(600));
     QVERIFY(!edsa.auth(0));
 
 
diff --git a/QuasarAppLib b/QuasarAppLib
index 9cc9e77..b6d3b0f 160000
--- a/QuasarAppLib
+++ b/QuasarAppLib
@@ -1 +1 @@
-Subproject commit 9cc9e77babc5cbf2f46276caff3783c273764556
+Subproject commit b6d3b0f72e129a78871a42d5e17dac18d1400cdb

From e3b03b1022ff408e9d96e045814c9bd1c2959180 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sun, 13 Feb 2022 19:04:06 +0300
Subject: [PATCH 07/10] added test description

---
 Heart/AbstractSpace/asynckeysauth.cpp      |  8 +++++++-
 HeartTests/AbstractSpace/ecdsaauthtest.cpp | 12 ++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
index da920ec..3d6f1e1 100644
--- a/Heart/AbstractSpace/asynckeysauth.cpp
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -17,7 +17,13 @@ AsyncKeysAuth::AsyncKeysAuth() {
 
 bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
 
-    if (std::abs(time(0) - _unixTime) >= allowedTimeRangeSec) {
+    int diff = time(0) - _unixTime;
+
+    if (diff < 0) {
+        return false;
+    }
+
+    if (diff >= allowedTimeRangeSec) {
         return false;
     }
 
diff --git a/HeartTests/AbstractSpace/ecdsaauthtest.cpp b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
index 2e21ed2..cc37905 100644
--- a/HeartTests/AbstractSpace/ecdsaauthtest.cpp
+++ b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
@@ -40,23 +40,35 @@ ECDSAAuthTest::~ECDSAAuthTest() {
 }
 
 void ECDSAAuthTest::test() {
+    // create a publick and private keys array.
     QByteArray pub, priv;
 
+    // make public and private keys.
     QVERIFY(QH::AuthECDSA::makeKeys(pub, priv));
 
+    // check createed keys. should be larget then 0.
     QVERIFY(pub.length() && priv.length());
 
+    // create test auth object using ecdsa algorithm
     ECDSA edsa(pub, priv);
 
+    // The terst object should be invalid because it is not prepared.
     QVERIFY(!edsa.isValid());
 
+    // the authetication should be failed bacause ecdsa class is invalid.
     QVERIFY(!edsa.auth(600));
 
+    // prepare an authentication object.
     QVERIFY(edsa.prepare());
+    // the prepared object should be valid.
     QVERIFY(edsa.isValid());
 
+    // authentication should be finished successful because auth object contains prepared valid signature.
     QVERIFY(edsa.auth(600));
+    // authentication should be failed because the time range is depricated.
     QVERIFY(!edsa.auth(0));
 
 
+
+
 }

From 48e07658e908b1a3ef790dae3eacea942b8c6fde Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sun, 13 Feb 2022 20:01:06 +0300
Subject: [PATCH 08/10] fiw windows build

---
 Heart/AbstractSpace/asynckeysauth.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
index 3d6f1e1..eede591 100644
--- a/Heart/AbstractSpace/asynckeysauth.cpp
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -7,7 +7,7 @@
 
 #include "asynckeysauth.h"
 #include "QCryptographicHash"
-
+#include <time.h>
 
 namespace QH {
 

From 0e8a9d324ef7b66266504b2aed3566ea8a7d2c72 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Sun, 13 Feb 2022 20:08:19 +0300
Subject: [PATCH 09/10] fix windows

---
 Heart/AbstractSpace/authecdsa.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Heart/AbstractSpace/authecdsa.h b/Heart/AbstractSpace/authecdsa.h
index faa8363..b5cdb3b 100644
--- a/Heart/AbstractSpace/authecdsa.h
+++ b/Heart/AbstractSpace/authecdsa.h
@@ -19,7 +19,7 @@ namespace QH {
 /**
  * @brief The AuthECDSA class is ecdsa implementation of the Async authentication. This implementation based on Openssl library.
  */
-class AuthECDSA: public QH::AsyncKeysAuth
+class HEARTSHARED_EXPORT AuthECDSA: public QH::AsyncKeysAuth
 {
 
 public:

From ba3f765639ad4c9c3e959a843eaa03bc30460b54 Mon Sep 17 00:00:00 2001
From: EndrII <EndrIIMail@gmail.com>
Date: Mon, 14 Feb 2022 10:42:58 +0300
Subject: [PATCH 10/10] id generator option

---
 Heart/AbstractSpace/asynckeysauth.cpp      | 12 +++++++--
 Heart/AbstractSpace/asynckeysauth.h        |  3 ++-
 HeartTests/AbstractSpace/ecdsaauthtest.cpp | 30 +++++++++++++++++++---
 3 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/Heart/AbstractSpace/asynckeysauth.cpp b/Heart/AbstractSpace/asynckeysauth.cpp
index eede591..67d12ee 100644
--- a/Heart/AbstractSpace/asynckeysauth.cpp
+++ b/Heart/AbstractSpace/asynckeysauth.cpp
@@ -7,6 +7,7 @@
 
 #include "asynckeysauth.h"
 #include "QCryptographicHash"
+#include <QString>
 #include <time.h>
 
 namespace QH {
@@ -15,7 +16,7 @@ AsyncKeysAuth::AsyncKeysAuth() {
 
 }
 
-bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
+bool AsyncKeysAuth::auth(int allowedTimeRangeSec, QString* userId) const {
 
     int diff = time(0) - _unixTime;
 
@@ -31,7 +32,14 @@ bool AsyncKeysAuth::auth(int allowedTimeRangeSec) const {
     data.insert(0, reinterpret_cast<const char*>(&_unixTime),
                 sizeof(_unixTime));
 
-    return checkSign(data, _signature, _publicKey);
+    bool result = checkSign(data, _signature, _publicKey);
+
+    if (result && userId) {
+        *userId = QCryptographicHash::hash(_publicKey,
+                                          QCryptographicHash::Sha256).toHex();
+    }
+
+    return result;
 }
 
 bool AsyncKeysAuth::prepare() {
diff --git a/Heart/AbstractSpace/asynckeysauth.h b/Heart/AbstractSpace/asynckeysauth.h
index f4a8b29..c369547 100644
--- a/Heart/AbstractSpace/asynckeysauth.h
+++ b/Heart/AbstractSpace/asynckeysauth.h
@@ -55,9 +55,10 @@ public:
 
     /**
      * @brief auth This method make authentication and return true if the authentication finished successful else false.
+     * @brief retLoginedUserId This is logined user id in hex
      * @return true if the authentication finished successful else false.
      */
-    bool auth(int allowedTimeRangeSec) const;
+    bool auth(int allowedTimeRangeSec, QString* retLoginedUserId) const;
 
     /**
      * @brief prepare This method will generate signature for autentication of client. Please inboke this method before send request to server.
diff --git a/HeartTests/AbstractSpace/ecdsaauthtest.cpp b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
index cc37905..e81ff82 100644
--- a/HeartTests/AbstractSpace/ecdsaauthtest.cpp
+++ b/HeartTests/AbstractSpace/ecdsaauthtest.cpp
@@ -8,7 +8,7 @@
 #include "ecdsaauthtest.h"
 #include "authecdsa.h"
 #include <QtTest>
-
+#include "thread"
 /*
  * test class
  */
@@ -42,10 +42,16 @@ ECDSAAuthTest::~ECDSAAuthTest() {
 void ECDSAAuthTest::test() {
     // create a publick and private keys array.
     QByteArray pub, priv;
+    QString userID;
 
     // make public and private keys.
     QVERIFY(QH::AuthECDSA::makeKeys(pub, priv));
 
+    // make user id
+    QString userIDOfPubKey = QCryptographicHash::hash(pub,
+                                                       QCryptographicHash::Sha256).
+            toHex();
+
     // check createed keys. should be larget then 0.
     QVERIFY(pub.length() && priv.length());
 
@@ -56,7 +62,8 @@ void ECDSAAuthTest::test() {
     QVERIFY(!edsa.isValid());
 
     // the authetication should be failed bacause ecdsa class is invalid.
-    QVERIFY(!edsa.auth(600));
+    QVERIFY(!edsa.auth(600, &userID));
+    QVERIFY(userID.isEmpty());
 
     // prepare an authentication object.
     QVERIFY(edsa.prepare());
@@ -64,9 +71,24 @@ void ECDSAAuthTest::test() {
     QVERIFY(edsa.isValid());
 
     // authentication should be finished successful because auth object contains prepared valid signature.
-    QVERIFY(edsa.auth(600));
+    QVERIFY(edsa.auth(600, &userID));
+    QVERIFY(userID == userIDOfPubKey);
+
+    // forget user id before new auth
+    userID.clear();
+
     // authentication should be failed because the time range is depricated.
-    QVERIFY(!edsa.auth(0));
+    QVERIFY(!edsa.auth(0, &userID));
+    QVERIFY(userID.isEmpty());
+
+    // change subsribe time and try login.
+    edsa.setUnixTime(time(0) + 1);
+
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    // should be failed because signature is different of the time.
+    QVERIFY(!edsa.auth(600, &userID));
+    QVERIFY(userID.isEmpty());