diff --git a/services/tunnelbroker/docker-server/contents/server/src/Service/TunnelbrokerServiceImpl.cpp b/services/tunnelbroker/docker-server/contents/server/src/Service/TunnelbrokerServiceImpl.cpp index 167e7b352..13095ad72 100644 --- a/services/tunnelbroker/docker-server/contents/server/src/Service/TunnelbrokerServiceImpl.cpp +++ b/services/tunnelbroker/docker-server/contents/server/src/Service/TunnelbrokerServiceImpl.cpp @@ -1,182 +1,192 @@ #include "TunnelbrokerServiceImpl.h" #include "AmqpManager.h" #include "AwsTools.h" #include "Constants.h" #include "CryptoTools.h" #include "DatabaseManager.h" #include "DeliveryBroker.h" #include "Tools.h" #include #include #include #include namespace comm { namespace network { TunnelBrokerServiceImpl::TunnelBrokerServiceImpl() { Aws::InitAPI({}); // List of AWS DynamoDB tables to check if they are created and can be // accessed before any AWS API methods const std::list tablesList = { DEVICE_SESSIONS_TABLE_NAME, DEVICE_SESSIONS_VERIFICATION_MESSAGES_TABLE_NAME}; for (const std::string &table : tablesList) { if (!database::DatabaseManager::getInstance().isTableAvailable(table)) { throw std::runtime_error( "Error: AWS DynamoDB table '" + table + "' is not available"); } }; }; TunnelBrokerServiceImpl::~TunnelBrokerServiceImpl() { Aws::ShutdownAPI({}); }; grpc::Status TunnelBrokerServiceImpl::SessionSignature( grpc::ServerContext *context, const tunnelbroker::SessionSignatureRequest *request, tunnelbroker::SessionSignatureResponse *reply) { const std::string toSign = generateRandomString(SIGNATURE_REQUEST_LENGTH); std::shared_ptr SessionSignItem = std::make_shared(toSign, request->deviceid()); database::DatabaseManager::getInstance().putSessionSignItem(*SessionSignItem); reply->set_tosign(toSign); return grpc::Status::OK; }; grpc::Status TunnelBrokerServiceImpl::NewSession( grpc::ServerContext *context, const tunnelbroker::NewSessionRequest *request, tunnelbroker::NewSessionResponse *reply) { std::shared_ptr deviceSessionItem; std::shared_ptr sessionSignItem; const std::string deviceId = request->deviceid(); const std::string signature = request->signature(); const std::string publicKey = request->publickey(); const boost::uuids::uuid uuid = boost::uuids::random_generator()(); const std::string newSessionId = boost::lexical_cast(uuid); try { deviceSessionItem = database::DatabaseManager::getInstance().findSessionItem(newSessionId); if (deviceSessionItem != nullptr) { std::cout << "gRPC: " << "Session unique ID " << newSessionId << " already used" << std::endl; return grpc::Status( grpc::StatusCode::INTERNAL, "Session unique ID already used"); } sessionSignItem = database::DatabaseManager::getInstance().findSessionSignItem(deviceId); if (sessionSignItem == nullptr) { std::cout << "gRPC: " << "Session sign request not found for deviceId: " << deviceId << std::endl; return grpc::Status( grpc::StatusCode::NOT_FOUND, "Session sign request not found"); } + const std::string verificationMessage = sessionSignItem->getSign(); + if (!comm::network::crypto::rsaVerifyString( + publicKey, verificationMessage, signature)) { + std::cout << "gRPC: " + << "Signature for the verification message is not valid" + << std::endl; + return grpc::Status( + grpc::StatusCode::PERMISSION_DENIED, + "Signature for the verification message is not valid"); + } database::DatabaseManager::getInstance().removeSessionSignItem(deviceId); deviceSessionItem = std::make_shared( newSessionId, deviceId, request->publickey(), request->notifytoken(), tunnelbroker::NewSessionRequest_DeviceTypes_Name(request->devicetype()), request->deviceappversion(), request->deviceos()); database::DatabaseManager::getInstance().putSessionItem(*deviceSessionItem); } catch (std::runtime_error &e) { std::cout << "gRPC: " << "Error while processing 'NewSession' request: " << e.what() << std::endl; return grpc::Status(grpc::StatusCode::INTERNAL, e.what()); } reply->set_sessionid(newSessionId); return grpc::Status::OK; }; grpc::Status TunnelBrokerServiceImpl::Send( grpc::ServerContext *context, const tunnelbroker::SendRequest *request, google::protobuf::Empty *reply) { try { const std::string sessionId = request->sessionid(); std::shared_ptr sessionItem = database::DatabaseManager::getInstance().findSessionItem(sessionId); if (sessionItem == nullptr) { std::cout << "gRPC: " << "Session " << sessionId << " not found" << std::endl; return grpc::Status( grpc::StatusCode::PERMISSION_DENIED, "No such session found. SessionId: " + sessionId); } const std::string clientDeviceId = sessionItem->getDeviceId(); if (!AMQPSend( request->todeviceid(), clientDeviceId, std::string(request->payload()))) { std::cout << "gRPC: " << "Error while publish the message to AMQP" << std::endl; return grpc::Status( grpc::StatusCode::INTERNAL, "Error while publish the message to AMQP"); } } catch (std::runtime_error &e) { std::cout << "gRPC: " << "Error while processing 'Send' request: " << e.what() << std::endl; return grpc::Status(grpc::StatusCode::INTERNAL, e.what()); } return grpc::Status::OK; }; grpc::Status TunnelBrokerServiceImpl::Get( grpc::ServerContext *context, const tunnelbroker::GetRequest *request, grpc::ServerWriter *writer) { try { const std::string sessionId = request->sessionid(); std::shared_ptr sessionItem = database::DatabaseManager::getInstance().findSessionItem(sessionId); if (sessionItem == nullptr) { std::cout << "gRPC: " << "Session " << sessionId << " not found" << std::endl; return grpc::Status( grpc::StatusCode::PERMISSION_DENIED, "No such session found. SessionId: " + sessionId); } const std::string clientDeviceId = sessionItem->getDeviceId(); std::vector messagesToDeliver; while (1) { messagesToDeliver = DeliveryBroker::getInstance().get(clientDeviceId); for (auto const &message : messagesToDeliver) { tunnelbroker::GetResponse response; response.set_fromdeviceid(message.fromDeviceID); response.set_payload(message.payload); if (!writer->Write(response)) { throw std::runtime_error( "gRPC: 'Get' writer error on sending data to the client"); } AMQPAck(message.deliveryTag); } if (!DeliveryBroker::getInstance().isEmpty(clientDeviceId)) { DeliveryBroker::getInstance().remove(clientDeviceId); } DeliveryBroker::getInstance().wait(clientDeviceId); } } catch (std::runtime_error &e) { std::cout << "gRPC: " << "Error while processing 'Get' request: " << e.what() << std::endl; return grpc::Status(grpc::StatusCode::INTERNAL, e.what()); } return grpc::Status::OK; }; } // namespace network } // namespace comm diff --git a/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.cpp b/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.cpp new file mode 100644 index 000000000..7e7f8a1ae --- /dev/null +++ b/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.cpp @@ -0,0 +1,52 @@ +#include "CryptoTools.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace comm { +namespace network { +namespace crypto { + +bool rsaVerifyString( + const std::string &publicKeyBase64, + const std::string &message, + const std::string &signatureBase64) { + + CryptoPP::RSA::PublicKey publicKey; + std::string decodedSignature; + std::unique_ptr base64Decoder; + + try { + base64Decoder = std::make_unique(); + publicKey.Load( + CryptoPP::StringSource(publicKeyBase64, true, base64Decoder.release()) + .Ref()); + + base64Decoder = std::make_unique( + std::make_unique(decodedSignature).release()); + CryptoPP::StringSource stringSource( + signatureBase64, true, base64Decoder.release()); + + CryptoPP::RSASSA_PKCS1v15_SHA_Verifier verifierSha256(publicKey); + return verifierSha256.VerifyMessage( + reinterpret_cast(message.c_str()), + message.length(), + reinterpret_cast(decodedSignature.c_str()), + decodedSignature.length()); + + } catch (const std::exception &e) { + std::cout << "CryptoTools: " + << "Got an exception " << e.what() << std::endl; + return false; + } +} + +} // namespace crypto +} // namespace network +} // namespace comm diff --git a/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.h b/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.h new file mode 100644 index 000000000..9fc40fda7 --- /dev/null +++ b/services/tunnelbroker/docker-server/contents/server/src/Tools/CryptoTools.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace comm { +namespace network { +namespace crypto { + +bool rsaVerifyString( + const std::string &publicKeyBase64, + const std::string &message, + const std::string &signatureBase64); + +} // namespace crypto +} // namespace network +} // namespace comm