Skip to content

Commit

Permalink
net/tools:shadowsocks-server
Browse files Browse the repository at this point in the history
  • Loading branch information
iceboy233 committed Feb 22, 2021
1 parent 624d109 commit 2641610
Show file tree
Hide file tree
Showing 11 changed files with 715 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build:linux --action_env=BAZEL_LINKLIBS=-l%:libstdc++.a:-lm
build:linux --action_env=BAZEL_LINKOPTS=-static-libgcc
build:linux --cxxopt=-std=c++17
build:linux --host_cxxopt=-std=c++17
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.vscode/
/bazel-*
28 changes: 28 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository")

git_repository(
name = "boringssl",
commit = "127d006498acc7c932b85d41d5f4ba3f0cbfbfd6",
remote = "https://github.com/google/boringssl.git",
)

git_repository(
name = "com_google_absl",
commit = "62f05b1f57ad660e9c09e02ce7d591dcc4d0ca08",
remote = "https://github.com/abseil/abseil-cpp.git",
)

git_repository(
name = "org_boost_boost",
commit = "13413ef10592ca33bec6bcadf67f62ced07a1b7f",
remote = "https://github.com/iceboy233/boost.git",
)

load("@org_boost_boost//:boost_deps.bzl", "boost_deps")
boost_deps()

git_repository(
name = "org_iceboy_trunk",
commit = "cea91e3757e4ab227336177d524b842049d8f123",
remote = "https://github.com/iceboy233/trunk.git",
)
37 changes: 37 additions & 0 deletions net/shadowsocks/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])

cc_library(
name = "aes-crypto",
srcs = ["aes-crypto.cc"],
hdrs = ["aes-crypto.h"],
deps = [
"@boringssl//:crypto",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"@org_iceboy_trunk//base:logging",
"@org_iceboy_trunk//net:asio",
],
)

cc_library(
name = "tcp-server",
srcs = ["tcp-server.cc"],
hdrs = ["tcp-server.h"],
deps = [
":aes-crypto",
":wire-structs",
"@com_google_absl//absl/types:span",
"@org_boost_boost//:smart_ptr",
"@org_iceboy_trunk//base:logging",
"@org_iceboy_trunk//net:asio",
],
)

cc_library(
name = "wire-structs",
hdrs = ["wire-structs.h"],
deps = [
"@org_iceboy_trunk//base:packed",
"@org_iceboy_trunk//net:asio",
],
)
206 changes: 206 additions & 0 deletions net/shadowsocks/aes-crypto.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#include "net/shadowsocks/aes-crypto.h"

#include <stdlib.h>
#include <openssl/digest.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/rand.h>

#include "base/logging.h"

namespace net {
namespace shadowsocks {

AesMasterKey AesMasterKey::from_password(std::string_view password) {
AesMasterKey key;
if (!EVP_BytesToKey(
EVP_aes_128_gcm(), EVP_md5(), nullptr,
reinterpret_cast<const uint8_t *>(password.data()), password.size(), 1,
key.data(), nullptr)) {
LOG(fatal) << "EVP_BytesToKey failed";
abort();
}
return key;
}

AesSessionKey::AesSessionKey(
const AesMasterKey &master_key, const uint8_t salt[16]) {
std::array<uint8_t, 16> key;
if (!HKDF(
key.data(), key.size(), EVP_sha1(),
master_key.data(), master_key.size(), salt, 16,
reinterpret_cast<const uint8_t *>("ss-subkey"), 9)) {
LOG(fatal) << "HKDF failed";
abort();
}
if (!EVP_AEAD_CTX_init(
&aead_ctx_, EVP_aead_aes_128_gcm(), key.data(), key.size(), 16,
nullptr)) {
LOG(fatal) << "EVP_AEAD_CTX_init failed";
abort();
}
}

AesSessionKey::~AesSessionKey() {
EVP_AEAD_CTX_cleanup(&aead_ctx_);
}

void AesSessionKey::encrypt(
absl::Span<const uint8_t> in, uint8_t *out, uint8_t out_tag[16]) {
size_t out_tag_len;
if (!EVP_AEAD_CTX_seal_scatter(
&aead_ctx_, out, out_tag, &out_tag_len, 16,
reinterpret_cast<uint8_t *>(&nonce_low_), 12,
in.data(), in.size(), nullptr, 0, nullptr, 0) ||
out_tag_len != 16) {
LOG(fatal) << "EVP_AEAD_CTX_seal_scatter failed";
abort();
}
if (!++nonce_low_) {
++nonce_high_;
}
}

bool AesSessionKey::decrypt(
absl::Span<const uint8_t> in, const uint8_t in_tag[16], uint8_t *out) {
if (!EVP_AEAD_CTX_open_gather(
&aead_ctx_, out, reinterpret_cast<uint8_t *>(&nonce_low_), 12,
in.data(), in.size(), in_tag, 16, nullptr, 0)) {
return false;
}
if (!++nonce_low_) {
++nonce_high_;
}
return true;
}

AesStream::AesStream(tcp::socket &socket, const AesMasterKey &master_key)
: socket_(socket),
master_key_(master_key),
read_buffer_(std::make_unique<uint8_t[]>(read_buffer_size_)),
write_buffer_(std::make_unique<uint8_t[]>(write_buffer_size_)) {}

void AesStream::read(
std::function<void(std::error_code, absl::Span<const uint8_t>)> callback) {
if (!read_key_) {
read_header(std::move(callback));
} else {
read_length(std::move(callback));
}
}

void AesStream::write(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback) {
if (!write_key_) {
write_header(chunk, std::move(callback));
} else {
write_length(chunk, std::move(callback));
}
}

void AesStream::read_header(
std::function<void(std::error_code, absl::Span<const uint8_t>)> callback) {
async_read(
socket_,
buffer(read_buffer_.get(), 16),
[this, callback = std::move(callback)](
std::error_code ec, size_t) mutable {
if (ec) {
callback(ec, {});
return;
}
read_key_.emplace(master_key_, read_buffer_.get());
read_length(std::move(callback));
});
}

void AesStream::read_length(
std::function<void(std::error_code, absl::Span<const uint8_t>)> callback) {
async_read(
socket_,
buffer(read_buffer_.get(), 18),
[this, callback = std::move(callback)](
std::error_code ec, size_t) mutable {
if (ec) {
callback(ec, {});
return;
}
if (!read_key_->decrypt(
{&read_buffer_[0], 2}, &read_buffer_[2], &read_buffer_[0])) {
callback(
std::make_error_code(std::errc::result_out_of_range), {});
return;
}
size_t length = (read_buffer_[0] << 8) | read_buffer_[1];
if (length >= 16384) {
callback(
std::make_error_code(std::errc::result_out_of_range), {});
return;
}
read_payload(length, std::move(callback));
});
}

void AesStream::read_payload(
size_t length,
std::function<void(std::error_code, absl::Span<const uint8_t>)> callback) {
async_read(
socket_,
buffer(read_buffer_.get(), length + 16),
[this, length, callback = std::move(callback)](
std::error_code ec, size_t) {
if (ec) {
callback(ec, {});
return;
}
if (!read_key_->decrypt(
{&read_buffer_[0], length}, &read_buffer_[length],
&read_buffer_[0])) {
callback(
std::make_error_code(std::errc::result_out_of_range), {});
return;
}
callback({}, {&read_buffer_[0], length});
});
}

void AesStream::write_header(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback) {
RAND_bytes(write_buffer_.get(), 16);
write_key_.emplace(master_key_, write_buffer_.get());
write_buffer_[16] = static_cast<uint8_t>(chunk.size() >> 8);
write_buffer_[17] = static_cast<uint8_t>(chunk.size());
write_key_->encrypt(
{&write_buffer_[16], 2}, &write_buffer_[16], &write_buffer_[18]);
write_key_->encrypt(
chunk, &write_buffer_[34], &write_buffer_[34 + chunk.size()]);
write_payload(chunk.size() + 50, std::move(callback));
}

void AesStream::write_length(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback) {
write_buffer_[0] = static_cast<uint8_t>(chunk.size() >> 8);
write_buffer_[1] = static_cast<uint8_t>(chunk.size());
write_key_->encrypt(
{&write_buffer_[0], 2}, &write_buffer_[0], &write_buffer_[2]);
write_key_->encrypt(
chunk, &write_buffer_[18], &write_buffer_[18 + chunk.size()]);
write_payload(chunk.size() + 34, std::move(callback));
}

void AesStream::write_payload(
size_t length,
std::function<void(std::error_code)> callback) {
async_write(
socket_,
buffer(write_buffer_.get(), length),
[callback = std::move(callback)](std::error_code ec, size_t) {
callback(ec);
});
}

} // namespace shadowsocks
} // namespace net
85 changes: 85 additions & 0 deletions net/shadowsocks/aes-crypto.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef _NET_SHADOWSOCKS_AES_CRYPTO_H
#define _NET_SHADOWSOCKS_AES_CRYPTO_H

#include <stddef.h>
#include <stdint.h>
#include <openssl/aead.h>
#include <array>
#include <functional>
#include <optional>
#include <string_view>
#include <system_error>

#include "absl/types/span.h"
#include "net/asio.h"

namespace net {
namespace shadowsocks {

class AesMasterKey : public std::array<uint8_t, 16> {
public:
static AesMasterKey from_password(std::string_view password);
};

class AesSessionKey {
public:
AesSessionKey(const AesMasterKey &master_key, const uint8_t salt[16]);
~AesSessionKey();

void encrypt(
absl::Span<const uint8_t> in, uint8_t *out, uint8_t out_tag[16]);
bool decrypt(
absl::Span<const uint8_t> in, const uint8_t in_tag[16], uint8_t *out);

private:
EVP_AEAD_CTX aead_ctx_;
uint64_t nonce_low_ = 0;
uint32_t nonce_high_ = 0;
};

class AesStream {
public:
AesStream(tcp::socket &socket, const AesMasterKey &master_key);

void read(
std::function<void(
std::error_code, absl::Span<const uint8_t>)> callback);
void write(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback);

private:
void read_header(
std::function<void(
std::error_code, absl::Span<const uint8_t>)> callback);
void read_length(
std::function<void(
std::error_code, absl::Span<const uint8_t>)> callback);
void read_payload(
size_t length,
std::function<void(
std::error_code, absl::Span<const uint8_t>)> callback);
void write_header(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback);
void write_length(
absl::Span<const uint8_t> chunk,
std::function<void(std::error_code)> callback);
void write_payload(
size_t length,
std::function<void(std::error_code)> callback);

tcp::socket &socket_;
AesMasterKey master_key_;
std::optional<AesSessionKey> read_key_;
std::optional<AesSessionKey> write_key_;
std::unique_ptr<uint8_t[]> read_buffer_;
static constexpr size_t read_buffer_size_ = 16384 + 16;
std::unique_ptr<uint8_t[]> write_buffer_;
static constexpr size_t write_buffer_size_ = 16384 + 50;
};

} // namespace shadowsocks
} // namespace net

#endif // _NET_SHADOWSOCKS_AES_CRYPTO_H
Loading

0 comments on commit 2641610

Please sign in to comment.