add alipan
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,4 +2,5 @@
|
||||
*.exe
|
||||
/build
|
||||
/cDrive.*
|
||||
/.vscode/settings.json
|
||||
/.vscode/*
|
||||
.alipan.json
|
||||
|
||||
6
Makefile
6
Makefile
@@ -9,9 +9,9 @@ CPPFILES := $(foreach dir,$(SRCS),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o)
|
||||
|
||||
INCLUDES := -I$(CURDIR)/include -I$(CURDIR)
|
||||
CFLAGS := -g -Wall $(INCLUDES) `curl-config --cflags`
|
||||
CFLAGS := -s -Wall $(INCLUDES) `curl-config --cflags`
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -std=c++17
|
||||
LDFLAGS := `curl-config --libs` -lmbedcrypto
|
||||
LDFLAGS := `curl-config --libs` -lmbedcrypto -lfmt
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
@@ -23,4 +23,4 @@ run: all
|
||||
@./$(TARGET)
|
||||
|
||||
clean:
|
||||
$(RM) -fr $(BUILD) $(TARGET)
|
||||
$(RM) $(OFILES) $(TARGET)
|
||||
@@ -1,16 +1,17 @@
|
||||
# clist
|
||||
# cDrive
|
||||
|
||||
Cloud drive explorer for C/C++
|
||||
|
||||
## Build On Debian
|
||||
|
||||
* Debian `apt-get install libcurl4-openssl-dev libspdlog-dev libqrcodegen-dev libmbedtls-dev`
|
||||
* Debian `apt-get install libcurl4-openssl-dev libfmt-dev libqrcodegen-dev libmbedtls-dev`
|
||||
|
||||
## Build On Windows
|
||||
|
||||
```bash
|
||||
curl -sL https://github.com/gabime/spdlog/archive/v1.13.0.tar.gz | tar zxf -
|
||||
curl -sL https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz | tar zxf -
|
||||
cmake -B build -G 'MinGW Makefiles' -DCMAKE_INSTALL_PREFIX=/c/MinGW64 -DCMAKE_BUILD_TYPE=Release \
|
||||
-DSPDLOG_BUILD_EXAMPLE=OFF -DSPDLOG_BUILD_SHARED=ON
|
||||
-DFMT_DOC=OFF -DFMT_TEST=OFF
|
||||
|
||||
curl -sL https://github.com/Mbed-TLS/mbedtls/archive/v2.28.3.tar.gz | tar zxf -
|
||||
cmake -B build -G 'MinGW Makefiles' -DCMAKE_INSTALL_PREFIX=/c/MinGW64 -DCMAKE_BUILD_TYPE=Release \
|
||||
|
||||
@@ -22,8 +22,10 @@ public:
|
||||
typedef std::shared_ptr<drive> ref;
|
||||
typedef void (*fnQrcode)(const std::string&);
|
||||
|
||||
virtual int qrLogin(fnQrcode printQr) = 0;
|
||||
virtual bool qrLogin(fnQrcode printQr) = 0;
|
||||
virtual std::vector<dItem> list(const std::string& file_id) = 0;
|
||||
virtual std::string link(const std::string& file_id) = 0;
|
||||
virtual void remove(const std::string& file_id) = 0;
|
||||
virtual std::string mkdir(const std::string& parent_id, const std::string& name) = 0;
|
||||
virtual std::string upload(const std::string& parent_id, const std::string& file) = 0;
|
||||
};
|
||||
@@ -32,4 +34,4 @@ public:
|
||||
drive::ref new_drive(drive_type type);
|
||||
|
||||
std::string hex_encode(const unsigned char* data, size_t len);
|
||||
|
||||
std::string device_name();
|
||||
|
||||
24765
include/nlohmann/json.hpp
Normal file
24765
include/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
346
src/drive/alipan.cpp
Normal file
346
src/drive/alipan.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
#include "alipan.h"
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/base64.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const std::string ALIPAN_GET_QRCODE =
|
||||
"https://passport.alipan.com/newlogin/qrcode/generate.do?appName=aliyun_drive&fromSite=52";
|
||||
const std::string ALIPAN_CHECK_QRCODE =
|
||||
"https://passport.alipan.com/newlogin/qrcode/query.do?appName=aliyun_drive&fromSite=52";
|
||||
const std::string ALIPAN_PRE_AUTH =
|
||||
"https://auth.alipan.com/v2/oauth/authorize?login_type=custom&response_type=code"
|
||||
"&redirect_uri=https%3A%2F%2Fwww.aliyundrive.com%2Fsign%2Fcallback&client_id=25dzX3vbYqktVxyX"
|
||||
"&sid=ls2z86hbnehde&state=%7B%22origin%22%3A%22file%3A%2F%2F%22%7D";
|
||||
const std::string ALIPAN_MINI_LOGIN =
|
||||
"https://passport.alipan.com/mini_login.htm?lang=zh_cn&appName=aliyun_drive&appEntrance=web"
|
||||
"&styleType=auto&bizParams=¬LoadSsoView=false¬KeepLogin=false&isMobile=false";
|
||||
const std::string ALIPAN_CANARY = "X-Canary: client=windows,app=adrive,version=v4.12.0";
|
||||
|
||||
bool alipan::qrLogin(fnQrcode printQr) {
|
||||
try {
|
||||
std::ifstream f(".alipan.json");
|
||||
if (f.is_open()) {
|
||||
json j = json::parse(f);
|
||||
this->access_token = j.at("access_token");
|
||||
this->refresh_token = j.at("refresh_token");
|
||||
this->device_id = j.at("device_id");
|
||||
}
|
||||
|
||||
if (!this->access_token.empty()) {
|
||||
return this->getSelfuser();
|
||||
} else if (!this->refresh_token.empty()) {
|
||||
return this->refreshToken();
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
fmt::print("load token {}\n", ex.what());
|
||||
}
|
||||
|
||||
HTTP s;
|
||||
std::stringstream ss;
|
||||
|
||||
s.set_headers({
|
||||
"Origin: https://passport.alipan.com",
|
||||
"Referer: " + ALIPAN_MINI_LOGIN,
|
||||
ALIPAN_CANARY,
|
||||
});
|
||||
s.get(ALIPAN_GET_QRCODE, &ss);
|
||||
json r = json::parse(ss.str());
|
||||
json qr = r.at("content").at("data");
|
||||
if (qr == nullptr || !qr.contains("codeContent")) return -1;
|
||||
printQr(qr.at("codeContent"));
|
||||
|
||||
std::string query = HTTP::encode_form({
|
||||
{"t", std::to_string(qr.at("t").get<time_t>())},
|
||||
{"ck", qr.at("ck")},
|
||||
{"appName", "aliyun_drive"},
|
||||
{"appEntrance", "web"},
|
||||
{"isMobile", "false"},
|
||||
{"lang", "zh_CN"},
|
||||
{"returnUrl", ""},
|
||||
{"fromSite", "52"},
|
||||
{"bizParams", ""},
|
||||
});
|
||||
|
||||
s.set_headers({
|
||||
"Content-Type: application/x-www-form-urlencoded",
|
||||
"Origin: https://passport.alipan.com",
|
||||
"Referer: " + ALIPAN_MINI_LOGIN,
|
||||
ALIPAN_CANARY,
|
||||
});
|
||||
|
||||
bool scaned = false;
|
||||
while (true) {
|
||||
json j = json::parse(s.post(ALIPAN_CHECK_QRCODE, query));
|
||||
json data = j.at("content").at("data");
|
||||
std::string qrCodeStatus = data.at("qrCodeStatus");
|
||||
|
||||
if (qrCodeStatus == "NEW") {
|
||||
} else if (qrCodeStatus == "SCANED") {
|
||||
if (!scaned) {
|
||||
fmt::print("扫描成功 请在APP上确认\n");
|
||||
scaned = true;
|
||||
}
|
||||
} else if (qrCodeStatus == "EXPIRED") {
|
||||
fmt::print("二维码过期\n");
|
||||
return false;
|
||||
} else if (qrCodeStatus == "CANCELED") {
|
||||
fmt::print("已取消\n");
|
||||
return false;
|
||||
} else if (qrCodeStatus == "CONFIRMED") {
|
||||
// decode token
|
||||
std::string bizExt = data.at("bizExt");
|
||||
std::vector<char> gbkBiz(bizExt.size());
|
||||
size_t out_len = 0;
|
||||
|
||||
mbedtls_base64_decode(
|
||||
(uint8_t*)gbkBiz.data(), gbkBiz.size(), &out_len, (const uint8_t*)bizExt.c_str(), bizExt.size());
|
||||
|
||||
static std::regex remove_re("[^\\x20-\\x7f]");
|
||||
bizExt = std::regex_replace(std::string(gbkBiz.data(), out_len), remove_re, "");
|
||||
if (bizExt.empty()) return -1;
|
||||
|
||||
json login = json::parse(bizExt).at("pds_login_result");
|
||||
this->user_id = login.at("userId");
|
||||
this->drive_id = login.at("defaultDriveId");
|
||||
this->access_token = login.at("accessToken");
|
||||
this->refresh_token = login.at("refreshToken");
|
||||
// 计算设备ID
|
||||
unsigned char digest[32];
|
||||
mbedtls_sha256_ret((uint8_t*)this->user_id.c_str(), this->user_id.size(), digest, 0);
|
||||
this->device_id = hex_encode(digest, sizeof(digest));
|
||||
fmt::print("login user_id({}) drive_id({})\n", this->user_id, this->drive_id);
|
||||
|
||||
return this->createSession();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<dItem> alipan::list(const std::string& file_id) {
|
||||
json data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{"parent_file_id", file_id},
|
||||
{"limit", 20},
|
||||
{"all", false},
|
||||
{"url_expire_sec", 14400},
|
||||
{"image_thumbnail_process", "image/resize,w_256/format,jpeg"},
|
||||
{"image_url_process", "image/resize,w_1920/format,jpeg/interlace,1"},
|
||||
{"video_thumbnail_process", "video/snapshot,t_1000,f_jpg,ar_auto,w_256"},
|
||||
{"fields", "file_id,name,type,"},
|
||||
{"order_by", "updated_at"},
|
||||
{"order_direction", "DESC"},
|
||||
};
|
||||
json j = this->request("/adrive/v3/file/list", data);
|
||||
|
||||
std::vector<dItem> list;
|
||||
for (auto& item : j.at("items")) {
|
||||
dItem it;
|
||||
it.folder = item.at("type") == "folder";
|
||||
it.id = item.at("file_id");
|
||||
it.name = item.at("name");
|
||||
list.push_back(it);
|
||||
}
|
||||
|
||||
fmt::print("{}\n", j.dump(2));
|
||||
return list;
|
||||
}
|
||||
|
||||
std::string alipan::link(const std::string& file_id) {
|
||||
json data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{"file_id", file_id},
|
||||
{"expire_sec", 14400},
|
||||
};
|
||||
json j = this->request("/v2/file/get_download_url", data);
|
||||
return j.at("url");
|
||||
}
|
||||
|
||||
std::string alipan::mkdir(const std::string& parent_id, const std::string& name) {
|
||||
json data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{"parent_file_id", parent_id},
|
||||
{"name", name},
|
||||
{"type", "folder"},
|
||||
{"check_name_mode", "refuse"},
|
||||
};
|
||||
json j = this->request("/adrive/v2/file/createWithFolders", data);
|
||||
return j.at("file_id");
|
||||
}
|
||||
|
||||
void alipan::remove(const std::string& file_id) {
|
||||
json data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{"file_id", file_id},
|
||||
};
|
||||
this->request("/v2/recyclebin/trash", data);
|
||||
}
|
||||
|
||||
std::string alipan::upload(const std::string& parent_id, const std::string& file) {
|
||||
fs::path name(file);
|
||||
json data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{
|
||||
"part_info_list",
|
||||
{
|
||||
{{"part_number", 1}},
|
||||
},
|
||||
},
|
||||
{"parent_file_id", parent_id},
|
||||
{"name", name.filename().string()},
|
||||
{"size", fs::file_size(file)},
|
||||
{"type", "file"},
|
||||
{"check_name_mode", "overwrite"},
|
||||
{"create_scene", "file_upload"},
|
||||
{"device_name", device_name()},
|
||||
{"content_hash_name", "none"},
|
||||
{"proof_version", "v1"},
|
||||
};
|
||||
json j = this->request("/adrive/v2/file/createWithFolders", data);
|
||||
|
||||
std::ifstream f(file, std::ios::binary);
|
||||
HTTP c;
|
||||
for (auto& info : j.at("part_info_list")) {
|
||||
std::string resp = c.put(info.at("upload_url"), &f);
|
||||
if (!resp.empty()) fmt::print("upload part: {}\n", resp);
|
||||
}
|
||||
|
||||
data = {
|
||||
{"drive_id", this->drive_id},
|
||||
{"file_id", j.at("file_id")},
|
||||
{"upload_id", j.at("upload_id")},
|
||||
};
|
||||
j = this->request("/v2/file/complete", data);
|
||||
return j.at("file_id");
|
||||
}
|
||||
|
||||
bool alipan::getSelfuser() {
|
||||
json j = this->request("/v2/user/get", {});
|
||||
this->user_id = j.at("user_id");
|
||||
this->drive_id = j.at("default_drive_id");
|
||||
return this->createSession();
|
||||
}
|
||||
|
||||
json alipan::request(const std::string& api, const json& data) {
|
||||
HTTP s;
|
||||
while (true) {
|
||||
std::vector<std::string> headers = {
|
||||
"Content-Type: application/json",
|
||||
ALIPAN_CANARY,
|
||||
};
|
||||
if (this->access_token.size() > 0) {
|
||||
headers.push_back("Authorization: Bearer " + this->access_token);
|
||||
}
|
||||
if (this->device_id.size() > 0) {
|
||||
headers.push_back("X-Device-Id: " + this->device_id);
|
||||
}
|
||||
if (this->signature.size() > 0) {
|
||||
headers.push_back("X-Signature: " + this->signature);
|
||||
}
|
||||
s.set_headers(headers);
|
||||
json j = json::parse(s.post("https://api.alipan.com" + api, data.dump()));
|
||||
|
||||
if (!j.contains("code")) return j;
|
||||
if (j.at("code").is_null()) return j;
|
||||
|
||||
std::string code = j.at("code");
|
||||
if (code == "AccessTokenInvalid") {
|
||||
if (this->refresh_token.empty()) throw std::runtime_error(j.at("message"));
|
||||
this->refreshToken();
|
||||
} else if (code == "DeviceSessionSignatureInvalid") {
|
||||
this->createSession();
|
||||
} else {
|
||||
throw std::runtime_error(j.at("message"));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool alipan::refreshToken() {
|
||||
HTTP s;
|
||||
s.set_headers({"Content-Type: application/json", "x-requested-with: XMLHttpRequest"});
|
||||
json data = {{"refresh_token", this->refresh_token}, {"grant_type", "refresh_token"}};
|
||||
std::string r = s.post("https://auth.alipan.com/v2/account/token", data.dump());
|
||||
json j = json::parse(r);
|
||||
if (j.contains("code")) {
|
||||
fmt::print("refresh failed: {}\n", j.dump());
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->refresh_token = j.at("refresh_token");
|
||||
this->access_token = j.at("access_token");
|
||||
|
||||
fmt::print("refress success ({})\n", this->refresh_token);
|
||||
return this->createSession();
|
||||
}
|
||||
|
||||
static inline int mbd_rand(void* rng_state, unsigned char* output, size_t len) {
|
||||
srand(time(nullptr));
|
||||
for (size_t i = 0; i < len; ++i) output[i] = rand();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool alipan::createSession() {
|
||||
std::string msg = "5dde4e1bdf9e4966b387ba58f4b3fdc3:" + this->device_id + ":" + this->user_id + ":0";
|
||||
std::string pub_key;
|
||||
|
||||
mbedtls_ecdsa_context ctx_sign;
|
||||
mbedtls_ecdsa_init(&ctx_sign);
|
||||
// gen secp256k1 keypair
|
||||
mbedtls_ecdsa_genkey(&ctx_sign, MBEDTLS_ECP_DP_SECP256K1, mbd_rand, nullptr);
|
||||
// mbedtls_ecp_group_load(&ctx_sign.grp, MBEDTLS_ECP_DP_SECP256K1);
|
||||
// mbedtls_mpi_read_string(&ctx_sign.d, 16, this->device_id.c_str());
|
||||
// mbedtls_ecp_mul(&ctx_sign.grp, &ctx_sign.Q, &ctx_sign.d, &ctx_sign.grp.G, mbd_rand, nullptr);
|
||||
|
||||
// dump public key
|
||||
size_t pub_len = 0;
|
||||
std::vector<uint8_t> pub(MBEDTLS_ECP_MAX_BYTES, 0);
|
||||
mbedtls_ecp_point_write_binary(
|
||||
&ctx_sign.grp, &ctx_sign.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &pub_len, pub.data(), pub.size());
|
||||
pub_key = hex_encode(pub.data(), pub_len);
|
||||
|
||||
// sign message
|
||||
unsigned char msg_hash[32];
|
||||
mbedtls_sha256_ret((uint8_t*)msg.c_str(), msg.size(), msg_hash, 0);
|
||||
|
||||
mbedtls_mpi r, s;
|
||||
std::vector<uint8_t> sigdata(MBEDTLS_ECDSA_MAX_LEN, 0);
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_ecdsa_sign(&ctx_sign.grp, &r, &s, &ctx_sign.d, msg_hash, sizeof(msg_hash), mbd_rand, nullptr);
|
||||
|
||||
size_t plen = mbedtls_mpi_size(&r);
|
||||
mbedtls_mpi_write_binary(&r, sigdata.data(), plen);
|
||||
mbedtls_mpi_write_binary(&s, sigdata.data() + plen, plen);
|
||||
sigdata[plen * 2] = 1;
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_ecdsa_free(&ctx_sign);
|
||||
this->signature = hex_encode(sigdata.data(), plen * 2 + 1);
|
||||
// log_debug("sign ({}), pubkey ({}), msg ({})", this->signature, pub_key, msg);
|
||||
|
||||
json data = {
|
||||
{"deviceName", device_name()},
|
||||
{"modelName", "Windows客户端"},
|
||||
{"pubKey", pub_key},
|
||||
};
|
||||
json j = this->request("/users/v1/users/device/create_session", data);
|
||||
|
||||
std::ofstream(".alipan.json") << json({
|
||||
{"access_token", this->access_token},
|
||||
{"refresh_token", this->refresh_token},
|
||||
{"device_id", this->device_id},
|
||||
});
|
||||
return j.at("success").get<bool>();
|
||||
}
|
||||
31
src/drive/alipan.h
Normal file
31
src/drive/alipan.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "http.h"
|
||||
#include "drive.h"
|
||||
|
||||
class alipan : public drive {
|
||||
public:
|
||||
bool qrLogin(fnQrcode printQr) override;
|
||||
std::vector<dItem> list(const std::string& file_id) override;
|
||||
std::string link(const std::string& file_id) override;
|
||||
std::string mkdir(const std::string& parent_id, const std::string& name) override;
|
||||
void remove(const std::string& file_id) override;
|
||||
std::string upload(const std::string& parent_id, const std::string& file) override;
|
||||
|
||||
private:
|
||||
bool getSelfuser();
|
||||
bool refreshToken();
|
||||
bool createSession();
|
||||
|
||||
nlohmann::json request(const std::string& api, const nlohmann::json& j);
|
||||
|
||||
private:
|
||||
std::string device_id;
|
||||
std::string signature;
|
||||
std::string access_token;
|
||||
std::string refresh_token;
|
||||
|
||||
std::string drive_id;
|
||||
std::string user_id;
|
||||
};
|
||||
18
src/drive/drive.cpp
Normal file
18
src/drive/drive.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "alipan.h"
|
||||
|
||||
static std::map<drive_type, drive::ref> m;
|
||||
|
||||
drive::ref new_drive(drive_type type) {
|
||||
auto it = m.find(type);
|
||||
if (it == m.end()) {
|
||||
switch (type) {
|
||||
case dt_alipan:
|
||||
it->second = std::make_shared<alipan>();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("unsupport drive type");
|
||||
}
|
||||
m.insert(std::make_pair(type, it->second));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
69
src/drive/misc.cpp
Normal file
69
src/drive/misc.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "drive.h"
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <SystemConfiguration/SystemConfiguration.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
std::string hex_encode(const unsigned char* data, size_t len) {
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < len; i++) ss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string ansi_to_utf8(const char* locale, std::string const& str_gbk) {
|
||||
std::vector<wchar_t> buff(str_gbk.size());
|
||||
std::locale loc(locale);
|
||||
wchar_t* to_next = nullptr;
|
||||
const char* from_next = nullptr;
|
||||
mbstate_t state = {};
|
||||
int res = std::use_facet<std::codecvt<wchar_t, char, mbstate_t>>(loc).in(state, str_gbk.data(),
|
||||
str_gbk.data() + str_gbk.size(), from_next, buff.data(), buff.data() + buff.size(), to_next);
|
||||
|
||||
if (std::codecvt_base::ok != res) return "";
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> cutf8;
|
||||
return cutf8.to_bytes(std::wstring(buff.data(), to_next));
|
||||
}
|
||||
|
||||
std::string device_name() {
|
||||
#ifdef __SWITCH__
|
||||
SetSysDeviceNickName nick;
|
||||
if (R_SUCCEEDED(setsysGetDeviceNickname(&nick))) {
|
||||
return nick.nickname;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
DWORD nSize = 128;
|
||||
std::vector<WCHAR> buf(nSize);
|
||||
if (GetComputerNameW(buf.data(), &nSize)) {
|
||||
std::string name;
|
||||
name.resize(nSize);
|
||||
WideCharToMultiByte(CP_UTF8, 0, buf.data(), nSize, name.data(), name.size(), nullptr, nullptr);
|
||||
return name;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
CFStringRef nameRef = SCDynamicStoreCopyComputerName(nullptr, nullptr);
|
||||
if (nameRef) {
|
||||
std::vector<char> name(CFStringGetLength(nameRef) * 3);
|
||||
CFStringGetCString(nameRef, name.data(), name.size(), kCFStringEncodingUTF8);
|
||||
CFRelease(nameRef);
|
||||
return name.data();
|
||||
}
|
||||
#else
|
||||
std::vector<char> buf(128);
|
||||
if (gethostname(buf.data(), buf.size()) == 0) {
|
||||
return buf.data();
|
||||
}
|
||||
#endif
|
||||
return "clist";
|
||||
}
|
||||
@@ -86,6 +86,7 @@ std::string HTTP::encode_form(const Form& form) {
|
||||
}
|
||||
|
||||
int HTTP::get(const std::string& url, std::ostream* out) {
|
||||
out->clear();
|
||||
curl_easy_setopt(this->easy, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(this->easy, CURLOPT_HTTPGET, 1L);
|
||||
return this->perform(out);
|
||||
|
||||
50
src/main.cpp
50
src/main.cpp
@@ -1,5 +1,7 @@
|
||||
#include "http.h"
|
||||
#include "drive.h"
|
||||
#include <qrcodegen/qrcodegen.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
void printQr(const std::string& text) {
|
||||
int border = 1;
|
||||
@@ -10,16 +12,58 @@ void printQr(const std::string& text) {
|
||||
|
||||
for (int y = -border; y < width + border; y++) {
|
||||
for (int x = -border; x < width + border; x++) {
|
||||
qrcodegen_getModule(qrcode.data(), x, y) ? printf("\033[40m \033[0m") : printf("\033[47m \033[0m");
|
||||
qrcodegen_getModule(qrcode.data(), x, y) ? fmt::print("\033[40m \033[0m")
|
||||
: fmt::print("\033[47m \033[0m");
|
||||
}
|
||||
printf("\n");
|
||||
fmt::print("\n");
|
||||
}
|
||||
printf("\n");
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
drive::ref c = new_drive(dt_alipan);
|
||||
if (!c->qrLogin(printQr)) return 1;
|
||||
|
||||
uint32_t choice = 0;
|
||||
std::vector<std::string> stack = {"root"};
|
||||
while (true) {
|
||||
auto list = c->list(stack.back());
|
||||
if (stack.size() > 1) {
|
||||
fmt::print("0: 返回上一级\n");
|
||||
}
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
fmt::print("{}: {}\n", i + 1, list[i].name);
|
||||
}
|
||||
fmt::print("{}: 创建目录\n", list.size() + 1);
|
||||
fmt::print("{}: 上传文件\n", list.size() + 2);
|
||||
fmt::print("选择文件或目录: \n");
|
||||
scanf("%u", &choice);
|
||||
|
||||
if (choice > list.size() + 2 || choice < 0) {
|
||||
break;
|
||||
} else if (choice == 0 && stack.size() > 1) {
|
||||
stack.pop_back();
|
||||
} else if (choice == list.size() + 1) {
|
||||
char name[1024];
|
||||
fmt::print("请输入目录名: \n");
|
||||
scanf("%s", name);
|
||||
c->mkdir(stack.back(), name);
|
||||
} else if (choice == list.size() + 2) {
|
||||
char name[1024];
|
||||
fmt::print("请输入文件名: \n");
|
||||
scanf("%s", name);
|
||||
c->upload(stack.back(), name);
|
||||
} else if (list[choice - 1].folder) {
|
||||
stack.push_back(list[choice - 1].id);
|
||||
} else {
|
||||
std::string link = c->link(list[choice - 1].id);
|
||||
fmt::print("url: {}\n", link);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
109
test/sign.cpp
Normal file
109
test/sign.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// g++ -o sign sign.cpp -l mbedcrypto -lsecp256k1
|
||||
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <secp256k1.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
std::vector<uint8_t> hex_decode(const std::string &s) {
|
||||
std::vector<uint8_t> bytes;
|
||||
for (size_t i = 0; i < s.length(); i += 2) {
|
||||
const std::string hex = s.substr(i, 2);
|
||||
const uint8_t decimal = std::strtol(hex.c_str(), 0, 16);
|
||||
bytes.push_back(decimal);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string hex_encode(const unsigned char *data, size_t len) {
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < len; i++) ss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static inline int mbd_rand(void *rng_state, unsigned char *output, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) output[i] = rand();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::string device_id = "cbf97fb4ff5c592ab387fd303ac9069b75930cf6e10d58da67f8426dd0f82ff2";
|
||||
std::string user_id = "71b9ac79010a448b95836a2be0dde671";
|
||||
std::string msg = "5dde4e1bdf9e4966b387ba58f4b3fdc3:" + device_id + ":" + user_id + ":0";
|
||||
|
||||
std::vector<uint8_t> pub(MBEDTLS_ECP_MAX_BYTES, 0);
|
||||
std::vector<uint8_t> sigdata(65, 0);
|
||||
|
||||
unsigned char msg_hash[32];
|
||||
int ret = 0;
|
||||
mbedtls_sha256_ret((uint8_t *)msg.c_str(), msg.size(), msg_hash, 0);
|
||||
|
||||
{
|
||||
mbedtls_ecdsa_context ctx_sign;
|
||||
mbedtls_ecdsa_init(&ctx_sign);
|
||||
// load private key
|
||||
mbedtls_ecp_group_load(&ctx_sign.grp, MBEDTLS_ECP_DP_SECP256K1);
|
||||
mbedtls_mpi_read_string(&ctx_sign.d, 16, device_id.c_str());
|
||||
mbedtls_ecp_mul(&ctx_sign.grp, &ctx_sign.Q, &ctx_sign.d, &ctx_sign.grp.G, nullptr, nullptr);
|
||||
// dump public key
|
||||
size_t pub_len = 0;
|
||||
mbedtls_ecp_point_write_binary(&ctx_sign.grp, &ctx_sign.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &pub_len, pub.data(), pub.size());
|
||||
pub.resize(pub_len);
|
||||
printf("pubkey %s\n", hex_encode(pub.data(), pub_len).c_str());
|
||||
// sign message
|
||||
mbedtls_mpi r, s;
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_ecdsa_sign(&ctx_sign.grp, &r, &s, &ctx_sign.d, msg_hash, sizeof(msg_hash), mbd_rand, nullptr);
|
||||
mbedtls_mpi_write_binary(&r, &sigdata[0], 32);
|
||||
mbedtls_mpi_write_binary(&s, &sigdata[32], 32);
|
||||
sigdata[64] = 1;
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_mpi_free(&s);
|
||||
printf("sigdata %s\n", hex_encode(sigdata.data(), sigdata.size()).c_str());
|
||||
mbedtls_ecdsa_free(&ctx_sign);
|
||||
}
|
||||
|
||||
{
|
||||
mbedtls_ecdsa_context ctx_verify;
|
||||
mbedtls_ecdsa_init(&ctx_verify);
|
||||
// load public key
|
||||
mbedtls_ecp_group_load(&ctx_verify.grp, MBEDTLS_ECP_DP_SECP256K1);
|
||||
ret = mbedtls_ecp_point_read_binary(&ctx_verify.grp, &ctx_verify.Q, pub.data(), pub.size());
|
||||
if (ret < 0) printf("mbedtls_ecp_point_read_string -0x%x\n", -ret);
|
||||
// verify signature
|
||||
mbedtls_mpi r, s;
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_mpi_read_binary(&r, &sigdata[0], 32);
|
||||
mbedtls_mpi_read_binary(&s, &sigdata[32], 32);
|
||||
ret = mbedtls_ecdsa_verify(&ctx_verify.grp, msg_hash, sizeof(msg_hash), &ctx_verify.Q, &r, &s);
|
||||
if (ret < 0)
|
||||
printf("mbedtls_ecdsa_verify -0x%x\n", -ret);
|
||||
else
|
||||
printf("mbedtls_ecdsa_verify ok\n");
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_ecdsa_free(&ctx_verify);
|
||||
}
|
||||
|
||||
{
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
ret = secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata.data());
|
||||
if (!ret) printf("secp256k1_ecdsa_signature_parse_compact failed\n");
|
||||
ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pub.data(), pub.size());
|
||||
if (!ret) printf("secp256k1_ec_pubkey_parse failed\n");
|
||||
ret = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
|
||||
if (!ret)
|
||||
printf("secp256k1_ecdsa_verify failed\n");
|
||||
else
|
||||
printf("secp256k1_ecdsa_verify ok\n");
|
||||
secp256k1_context_destroy(ctx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user