From f1540c897e24e420bcb34b491a7b25fd93377184 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sat, 7 Feb 2015 19:01:48 +0100 Subject: [PATCH] Client sending encoded handshake, server decoding the insecure bits --- auth-client.cc | 2 +- crypto.cc | 72 +++++++++++++++++++++++++++++++++++++++++++------- crypto.h | 13 +++++++-- tlv.cc | 53 +++++++++++++++++++++++++++++++++---- tlv.h | 6 +++-- 5 files changed, 127 insertions(+), 19 deletions(-) diff --git a/auth-client.cc b/auth-client.cc index 8d4ab5e..cdf2872 100644 --- a/auth-client.cc +++ b/auth-client.cc @@ -67,6 +67,6 @@ int main(int argc, char *argv[]) { server_public_key_file >> server_public_key; } - auto client = CryptoPubClient::FromHostname(server_address, server_port); + auto client = CryptoPubClient::FromHostname(server_address, server_port, secret_key, server_public_key); client->Loop(); } diff --git a/crypto.cc b/crypto.cc index 352cd7e..477478a 100644 --- a/crypto.cc +++ b/crypto.cc @@ -8,12 +8,19 @@ #include #include +#include #include #include "crypto.h" -#include "tlv.h" -#define TLV_TYPE_CLIENT_HANDSHAKE 0x8001 + +#define TLV_TYPE_ENCRYPTED_BLOB 0x0000 +#define TLV_TYPE_NONCE 0x0001 +#define TLV_TYPE_PUBLIC_KEY 0x0002 + +#define TLV_TYPE_ENCRYPTED 0x8000 +#define TLV_TYPE_CLIENT_HANDSHAKE 0x8001 +#define TLV_TYPE_CLIENT_HANDSHAKE_SECURE 0x8002 void CryptoBase::GenKey(std::string* key) { @@ -30,10 +37,38 @@ void CryptoBase::GenKeyPair(std::string* secret_key, std::string* public_key) { secret_key->assign((char*)secret_key_buf, crypto_box_SECRETKEYBYTES); } +void CryptoBase::DerivePublicKey(const std::string& secret_key, std::string* public_key) { + assert(secret_key.length() == crypto_box_SECRETKEYBYTES); + unsigned char buf[crypto_box_PUBLICKEYBYTES]; + assert(!crypto_scalarmult_base(buf, (const unsigned char*)secret_key.data())); + public_key->assign((char*)buf, crypto_box_PUBLICKEYBYTES); +} + +void CryptoBase::EncodeEncryptAppend(const std::string& secret_key, const std::string& public_key, const TLVNode& input, TLVNode* container) { + std::string encoded; + input.Encode(&encoded); + + size_t encrypted_bytes = encoded.length() + crypto_box_MACBYTES; + + unsigned char nonce[crypto_box_NONCEBYTES]; + randombytes_buf(nonce, crypto_box_NONCEBYTES); + + unsigned char output[encrypted_bytes]; + assert(!crypto_box_easy(output, (const unsigned char*)encoded.data(), encoded.length(), nonce, (const unsigned char*)public_key.data(), (const unsigned char*)secret_key.data())); + + TLVNode encrypted(TLV_TYPE_ENCRYPTED); + encrypted.AppendChild(TLVNode(TLV_TYPE_NONCE, std::string((char*)nonce, crypto_box_NONCEBYTES))); + encrypted.AppendChild(TLVNode(TLV_TYPE_ENCRYPTED_BLOB, std::string((char*)output, encrypted_bytes))); + + container->AppendChild(encrypted); +} + CryptoPubServer::CryptoPubServer(const std::string& secret_key) : secret_key_(secret_key), event_base_(event_base_new()) { + assert(secret_key_.length() == crypto_box_SECRETKEYBYTES); + struct sockaddr_in6 server_addr = {0}; server_addr.sin6_family = AF_INET6; server_addr.sin6_addr = in6addr_any; @@ -89,9 +124,14 @@ void CryptoPubServerConnection::OnReadable_(struct bufferevent* bev, void* this_ } void CryptoPubServerConnection::OnReadable() { - char buf[128]; - int bytes = bufferevent_read(bev_, buf, 128); - std::cerr << "OnReadable: " << bytes << " bytes" << std::endl; + char buf[UINT16_MAX]; + int bytes = bufferevent_read(bev_, buf, UINT16_MAX); + std::unique_ptr decoded(TLVNode::Decode(std::string(buf, bytes))); + if (!decoded.get()) { + // TODO: re-buffer? + return; + } + std::cerr << "successful decode" << std::endl; } void CryptoPubServerConnection::OnError_(struct bufferevent* bev, const short what, void* this__) { @@ -104,9 +144,15 @@ void CryptoPubServerConnection::OnError(const short what) { } -CryptoPubClient::CryptoPubClient(struct sockaddr* addr, socklen_t addrlen) +CryptoPubClient::CryptoPubClient(struct sockaddr* addr, socklen_t addrlen, const std::string& secret_key, const std::string& server_public_key) : event_base_(event_base_new()), - bev_(bufferevent_socket_new(event_base_, -1, BEV_OPT_CLOSE_ON_FREE)) { + bev_(bufferevent_socket_new(event_base_, -1, BEV_OPT_CLOSE_ON_FREE)), + secret_key_(secret_key), + server_public_key_(server_public_key) { + assert(secret_key_.length() == crypto_box_SECRETKEYBYTES); + assert(server_public_key_.length() == crypto_box_PUBLICKEYBYTES); + DerivePublicKey(secret_key_, &public_key_); + bufferevent_setcb(bev_, &CryptoPubClient::OnReadable_, NULL, &CryptoPubClient::OnConnectOrError_, this); bufferevent_enable(bev_, EV_READ); bufferevent_enable(bev_, EV_WRITE); @@ -118,14 +164,14 @@ CryptoPubClient::~CryptoPubClient() { event_base_free(event_base_); } -CryptoPubClient* CryptoPubClient::FromHostname(const std::string& server_address, const std::string& server_port) { +CryptoPubClient* CryptoPubClient::FromHostname(const std::string& server_address, const std::string& server_port, const std::string& secret_key, const std::string& server_public_key) { struct addrinfo* res; int gai_ret = getaddrinfo(server_address.c_str(), server_port.c_str(), NULL, &res); if (gai_ret) { std::cerr << "Failed to resolve server_address: " << gai_strerror(gai_ret) << std::endl; return nullptr; } - auto ret = new CryptoPubClient((struct sockaddr*)res->ai_addr, res->ai_addrlen); + auto ret = new CryptoPubClient((struct sockaddr*)res->ai_addr, res->ai_addrlen, secret_key, server_public_key); freeaddrinfo(res); return ret; } @@ -147,7 +193,15 @@ void CryptoPubClient::OnConnectOrError_(struct bufferevent* bev, const short wha } void CryptoPubClient::OnConnect() { + std::string ephemeral_public_key; + GenKeyPair(&ephemeral_secret_key_, &ephemeral_public_key); + TLVNode handshake(TLV_TYPE_CLIENT_HANDSHAKE); + handshake.AppendChild(TLVNode(TLV_TYPE_PUBLIC_KEY, public_key_)); + TLVNode secure_handshake(TLV_TYPE_CLIENT_HANDSHAKE_SECURE); + secure_handshake.AppendChild(TLVNode(TLV_TYPE_PUBLIC_KEY, ephemeral_public_key)); + EncodeEncryptAppend(secret_key_, server_public_key_, secure_handshake, &handshake); + std::string out; handshake.Encode(&out); bufferevent_write(bev_, out.data(), out.length()); diff --git a/crypto.h b/crypto.h index 7f9c5d2..03e2078 100644 --- a/crypto.h +++ b/crypto.h @@ -4,12 +4,16 @@ #include +#include "tlv.h" + class CryptoBase { public: virtual ~CryptoBase() {}; static void GenKey(std::string* key); static void GenKeyPair(std::string* secret_key, std::string* public_key); + static void DerivePublicKey(const std::string& secret_key, std::string* public_key); + static void EncodeEncryptAppend(const std::string& secret_key, const std::string& public_key, const TLVNode& input, TLVNode* container); }; class CryptoPubServerConnection; @@ -56,10 +60,10 @@ class CryptoPubServerConnection : public CryptoBase { class CryptoPubClient : public CryptoBase { public: - CryptoPubClient(struct sockaddr* addr, socklen_t addrlen); + CryptoPubClient(struct sockaddr* addr, socklen_t addrlen, const std::string& secret_key, const std::string& server_public_key); ~CryptoPubClient(); - static CryptoPubClient* FromHostname(const std::string& server_address, const std::string& server_port); + static CryptoPubClient* FromHostname(const std::string& server_address, const std::string& server_port, const std::string& secret_key, const std::string& server_public_key); void Loop(); @@ -71,4 +75,9 @@ class CryptoPubClient : public CryptoBase { struct event_base* event_base_; struct bufferevent* bev_; + + const std::string secret_key_; + const std::string server_public_key_; + std::string public_key_; + std::string ephemeral_secret_key_; }; diff --git a/tlv.cc b/tlv.cc index 9163db7..2402679 100644 --- a/tlv.cc +++ b/tlv.cc @@ -1,4 +1,5 @@ #include +#include #include "tlv.h" @@ -14,11 +15,11 @@ TLVNode::TLVNode(const uint16_t type, const std::string value) : type_(type), value_(value) {} -void TLVNode::Encode(std::string *output) { +void TLVNode::Encode(std::string *output) const { assert(value_.length() <= UINT16_MAX); struct header header = { - .type = type_, - .value_length = (uint16_t)value_.length(), + .type = htons(type_), + .value_length = htons((uint16_t)value_.length()), }; size_t header_start = output->length(); output->append((char*)&header, sizeof(header)); @@ -29,11 +30,53 @@ void TLVNode::Encode(std::string *output) { } size_t total_child_length = output->length() - header_start - sizeof(header); assert(total_child_length <= UINT16_MAX); - header.value_length = (uint16_t)total_child_length; + header.value_length = htons((uint16_t)total_child_length); output->replace(header_start, sizeof(header), (char*)&header, sizeof(header)); + } else { + output->append(value_); } } -bool TLVNode::IsContainer() { +TLVNode* TLVNode::Decode(const std::string& input) { + if (input.length() < sizeof(struct header)) { + return NULL; + } + auto header = (struct header*)input.data(); + std::cerr << "[type=" << htons(header->type) << ", value_length=" << htons(header->value_length) << "]" << std::endl; + if (input.length() < sizeof(*header) + htons(header->value_length)) { + return NULL; + } + + if (htons(header->type) & 0x8000) { + // Container + std::unique_ptr container(new TLVNode(htons(header->type))); + + size_t cursor = sizeof(*header); + while (cursor < input.length()) { + auto next_header = (struct header*)(input.data() + cursor); + if (cursor + sizeof(*next_header) + htons(next_header->value_length) > input.length()) { + return NULL; + } + std::unique_ptr child(Decode(input.substr(cursor))); + if (!child.get()) { + return NULL; + } + container->AppendChild(*child); + cursor += sizeof(*next_header) + htons(next_header->value_length); + } + + return container.release(); + } else { + // Scalar + return new TLVNode(htons(header->type), input.substr(sizeof(*header))); + } +} + +void TLVNode::AppendChild(const TLVNode& child) { + assert(this->IsContainer()); + children_.push_back(child); +} + +bool TLVNode::IsContainer() const { return type_ & 0x8000; } diff --git a/tlv.h b/tlv.h index d9a1678..f366914 100644 --- a/tlv.h +++ b/tlv.h @@ -9,9 +9,11 @@ class TLVNode { TLVNode(const uint16_t type, const std::string value); static TLVNode* Decode(const std::string& input); - void Encode(std::string *output); - bool IsContainer(); + void AppendChild(const TLVNode& child); + + void Encode(std::string *output) const; + bool IsContainer() const; private: const uint16_t type_;