1
0

add alipan

This commit is contained in:
2024-02-02 17:18:44 +08:00
parent 7b9fc827ca
commit 33ac4f1250
12 changed files with 25400 additions and 13 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
*.exe
/build
/cDrive.*
/.vscode/settings.json
/.vscode/*
.alipan.json

View File

@@ -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)

View File

@@ -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 \

View File

@@ -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

File diff suppressed because it is too large Load Diff

346
src/drive/alipan.cpp Normal file
View 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=&notLoadSsoView=false&notKeepLogin=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
View 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
View 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
View 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";
}

View File

@@ -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);

View File

@@ -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
View 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;
}