From fb6dbb060aaf37e43e4a8639ca4b13fa2b2afda4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Thu, 28 Feb 2019 11:25:50 -0500
Subject: [PATCH] daemon: added signature verification of name

Verification is done in two ways, first it checks if the public-key
actually matches the registered name,
and second it uses the public-key to verify the signature.

These two checks are sufficient to guarantee that owner of the
registered name is the one who posted name data to the
blockchain nameserver.

Change-Id: If5cfd0eeb01b305f290b7a5e7d424688ccf8ccb1
---
 src/ringdht/namedirectory.cpp | 48 +++++++++++++++++++++++++----------
 src/ringdht/namedirectory.h   |  5 ++--
 2 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/src/ringdht/namedirectory.cpp b/src/ringdht/namedirectory.cpp
index 6152a71be9..253030bf1a 100644
--- a/src/ringdht/namedirectory.cpp
+++ b/src/ringdht/namedirectory.cpp
@@ -40,6 +40,7 @@ constexpr const char* const QUERY_NAME {"/name/"};
 constexpr const char* const QUERY_ADDR {"/addr/"};
 constexpr const char* const HTTPS_PROTO {"https://"};
 constexpr const char* const CACHE_DIRECTORY {"namecache"};
+const std::string  HEX_PREFIX = "0x";
 
 /** Parser for URIs.         ( protocol        )    ( username         ) ( hostname                            ) */
 const std::regex URI_VALIDATOR {"^([a-zA-Z]+:(?://)?)?(?:([a-z0-9-_]{1,64})@)?([a-zA-Z0-9\\-._~%!$&'()*+,;=:\\[\\]]+)"};
@@ -66,7 +67,7 @@ NameDirectory::lookupUri(const std::string& uri, const std::string& default_serv
         }
     }
     RING_ERR("Can't parse URI: %s", uri.c_str());
-    cb("", Response::invalidName);
+    cb("", Response::invalidResponse);
 }
 
 NameDirectory::NameDirectory(const std::string& s)
@@ -168,14 +169,18 @@ void NameDirectory::lookupAddress(const std::string& addr, LookupCallback cb)
     }
 }
 
-static const std::string HEX_PREFIX {"0x"};
+bool
+NameDirectory::verify(const std::string& name, const dht::crypto::PublicKey& pk, const std::string& signature)
+{
+    return pk.checkSignature(std::vector<uint8_t>(name.begin(), name.end()), base64::decode(signature));
+}
 
 void NameDirectory::lookupName(const std::string& n, LookupCallback cb)
 {
     try {
         std::string name {n};
         if (not validateName(name)) {
-            cb(name, Response::invalidName);
+            cb(name, Response::invalidResponse);
             return;
         }
         toLower(name);
@@ -217,20 +222,37 @@ void NameDirectory::lookupName(const std::string& n, LookupCallback cb)
                     return;
                 }
                 auto addr = json["addr"].asString();
+                auto publickey = json["publickey"].asString();
+                auto signature = json["signature"].asString();
+
                 if (!addr.compare(0, HEX_PREFIX.size(), HEX_PREFIX))
                     addr = addr.substr(HEX_PREFIX.size());
-                if (not addr.empty()) {
-                    RING_DBG("Found address for %s: %s", name.c_str(), addr.c_str());
-                    {
-                        std::lock_guard<std::mutex> l(lock_);
-                        addrCache_.emplace(name, addr);
-                        nameCache_.emplace(addr, name);
-                    }
-                    cb(addr, Response::found);
-                    saveCache();
-                } else {
+                if (addr.empty()) {
                     cb("", Response::notFound);
+                    return;
+                }
+
+                if (not publickey.empty() and not signature.empty()) {
+                    try {
+                        auto pk = dht::crypto::PublicKey(base64::decode(publickey));
+                        if(pk.getId().toString() != addr or not verify(name, pk, signature)) {
+                            cb("", Response::invalidResponse);
+                            return;
+                        }
+                    } catch (const std::exception& e) {
+                        cb("", Response::invalidResponse);
+                        return;
+                    }
+                }
+
+                RING_DBG("Found address for %s: %s", name.c_str(), addr.c_str());
+                {
+                    std::lock_guard<std::mutex> l(lock_);
+                    addrCache_.emplace(name, addr);
+                    nameCache_.emplace(addr, name);
                 }
+                cb(addr, Response::found);
+                saveCache();
             } else if (code >= 400 && code < 500) {
                 cb("", Response::notFound);
             } else {
diff --git a/src/ringdht/namedirectory.h b/src/ringdht/namedirectory.h
index 5929c18bd1..f9ffca1c3b 100644
--- a/src/ringdht/namedirectory.h
+++ b/src/ringdht/namedirectory.h
@@ -18,7 +18,7 @@
 #pragma once
 
 #include "noncopyable.h"
-
+#include "opendht/crypto.h"
 #include <functional>
 #include <map>
 #include <string>
@@ -29,7 +29,7 @@ namespace ring {
 class NameDirectory
 {
 public:
-    enum class Response : int { found = 0, invalidName, notFound, error };
+    enum class Response : int { found = 0, invalidResponse, notFound, error };
     enum class RegistrationResponse : int { success = 0,
                                             invalidName,
                                             alreadyTaken,
@@ -84,6 +84,7 @@ private:
     }
 
     bool validateName(const std::string& name) const;
+    static bool verify(const std::string& name, const dht::crypto::PublicKey& publickey, const std::string& signature);
 
     void saveCache();
     void loadCache();
-- 
GitLab