Skip to content

Commit

Permalink
Merge pull request #353 from ko-matsu/feature/add-sign-message-capi
Browse files Browse the repository at this point in the history
feat: add message sign c-api.
  • Loading branch information
k-matsuzawa authored Feb 18, 2022
2 parents e5eb759 + 0f586f0 commit b962557
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 4 deletions.
6 changes: 3 additions & 3 deletions include/cfdc/cfdcapi_elements_transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ CFDC_API int CfdGetConfidentialTxOutByHandle(

/**
* @brief set utxo data on transaction input.
* @details for calculate schnorr sighature.
* @details for calculate schnorr signature.
* @param[in] handle cfd handle.
* @param[in] create_handle create transaction handle.
* @param[in] txid utxo txid.
Expand All @@ -495,7 +495,7 @@ CFDC_API int CfdSetConfidentialTxUtxoDataByHandle(

/**
* @brief set genesis block hash for confidential transaction.
* @details for calculate schnorr sighature.
* @details for calculate schnorr signature.
* @param[in] handle cfd handle.
* @param[in] create_handle create transaction handle.
* @param[in] genesis_block_hash genesis block hash.
Expand Down Expand Up @@ -657,7 +657,7 @@ CFDC_API int CfdFinalizeBlindTx(
* @param[out] issuance_txid issuance utxo txid.
* If 'CfdFreeStringBuffer' is implemented,
* Call 'CfdFreeStringBuffer' after you are finished using it.
* @param[out] issuance_vout issuance utxo vount.
* @param[out] issuance_vout issuance utxo vout.
* @param[out] is_issuance_asset issuance asset.
* @param[out] is_issuance_token issuance token.
* @return CfdErrorCode
Expand Down
34 changes: 34 additions & 0 deletions include/cfdc/cfdcapi_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,40 @@ CFDC_API int CfdPrivkeyTweakMul(
CFDC_API int CfdNegatePrivkey(
void* handle, const char* privkey, char** output);

/**
* @brief Negate privkey.
* @param[in] handle cfd handle.
* @param[in] privkey privkey(hex or wif).
* @param[in] message message.
* @param[in] magic message magic word.
* default is bitcoin magic word.
* @param[in] is_output_base64 output signature is base64.
* @param[out] signature message signature (hex or base64).
* If 'CfdFreeStringBuffer' is implemented,
* Call 'CfdFreeStringBuffer' after you are finished using it.
* @return CfdErrorCode
*/
CFDC_API int CfdSignMessage(
void* handle, const char* privkey, const char* message, const char* magic,
bool is_output_base64, char** signature);

/**
* @brief Verify message.
* @param[in] handle cfd handle.
* @param[in] signature message signature (hex or base64).
* @param[in] pubkey pubkey.
* @param[in] message message.
* @param[in] magic message magic word.
* default is bitcoin magic word.
* @param[out] recovered_pubkey recoverable pubkey from signature.
* If 'CfdFreeStringBuffer' is implemented,
* Call 'CfdFreeStringBuffer' after you are finished using it.
* @return CfdErrorCode
*/
CFDC_API int CfdVerifyMessage(
void* handle, const char* signature, const char* pubkey,
const char* message, const char* magic, char** recovered_pubkey);

/**
* @brief create extkey from seed.
* @param[in] handle cfd handle.
Expand Down
2 changes: 1 addition & 1 deletion local_resource/external_project_local_setting.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CFD_VERSION=0.4.0
CFD_VERSION=0.4.1
CFDCORE_TARGET_VERSION=refs/tags/v0.4.2
LIBWALLY_TARGET_VERSION=refs/tags/cfd-0.4.4
CFDCORE_TARGET_URL=cryptogarageinc/cfd-core.git
111 changes: 111 additions & 0 deletions src/capi/cfdcapi_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,117 @@ int CfdNegatePrivkey(void* handle, const char* privkey, char** output) {
}
}

int CfdSignMessage(
void* handle, const char* privkey, const char* message, const char* magic,
bool is_output_base64, char** signature) {
try {
cfd::Initialize();
if (signature == nullptr) {
warn(CFD_LOG_SOURCE, "signature is null.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. signature is null.");
}
if (IsEmptyString(privkey)) {
warn(CFD_LOG_SOURCE, "privkey is null or empty.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. privkey is null or empty.");
}
if (IsEmptyString(message)) {
warn(CFD_LOG_SOURCE, "message is null or empty.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. message is null or empty.");
}
Privkey sk;
if (Privkey::HasWif(privkey)) {
sk = Privkey::FromWif(privkey);
} else {
sk = Privkey(privkey);
}

ByteData sig;
if (IsEmptyString(magic)) {
sig = sk.SignBitcoinMessage(message);
} else {
sig = sk.SignMessage(message, magic);
}
if (is_output_base64) {
*signature = CreateString(CryptoUtil::EncodeBase64(sig));
} else {
*signature = CreateString(sig.GetHex());
}

return CfdErrorCode::kCfdSuccess;
} catch (const CfdException& except) {
return SetLastError(handle, except);
} catch (const std::exception& std_except) {
SetLastFatalError(handle, std_except.what());
return CfdErrorCode::kCfdUnknownError;
} catch (...) {
SetLastFatalError(handle, "unknown error.");
return CfdErrorCode::kCfdUnknownError;
}
}

int CfdVerifyMessage(
void* handle, const char* signature, const char* pubkey,
const char* message, const char* magic, char** recovered_pubkey) {
try {
cfd::Initialize();
if (IsEmptyString(signature)) {
warn(CFD_LOG_SOURCE, "signature is null or empty.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. signature is null or empty.");
}
if (IsEmptyString(pubkey)) {
warn(CFD_LOG_SOURCE, "pubkey is null or empty.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. pubkey is null or empty.");
}
if (IsEmptyString(message)) {
warn(CFD_LOG_SOURCE, "message is null or empty.");
throw CfdException(
CfdError::kCfdIllegalArgumentError,
"Failed to parameter. message is null or empty.");
}
Pubkey pubkey_obj(pubkey);
ByteData signature_obj;
if (strnlen(signature, 132) == 130) {
signature_obj = ByteData(signature);
} else {
signature_obj = CryptoUtil::DecodeBase64(signature);
}

Pubkey recoverable_pubkey;
bool is_success = false;
if (IsEmptyString(magic)) {
is_success = pubkey_obj.VerifyBitcoinMessage(
signature_obj, message, &recoverable_pubkey);
} else {
is_success = pubkey_obj.VerifyMessage(
signature_obj, message, magic, &recoverable_pubkey);
}
if ((recovered_pubkey != nullptr) && recoverable_pubkey.IsValid()) {
*recovered_pubkey = CreateString(recoverable_pubkey.GetHex());
}

if (!is_success) return CfdErrorCode::kCfdSignVerificationError;
return CfdErrorCode::kCfdSuccess;
} catch (const CfdException& except) {
return SetLastError(handle, except);
} catch (const std::exception& std_except) {
SetLastFatalError(handle, std_except.what());
return CfdErrorCode::kCfdUnknownError;
} catch (...) {
SetLastFatalError(handle, "unknown error.");
return CfdErrorCode::kCfdUnknownError;
}
}

int CfdCreateExtkeyFromSeed(
void* handle, const char* seed_hex, int network_type, int key_type,
char** extkey) {
Expand Down
74 changes: 74 additions & 0 deletions test/capi/test_cfdcapi_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,80 @@ TEST(cfdcapi_key, MnemonicTest) {
EXPECT_EQ(kCfdSuccess, ret);
}

TEST(cfdcapi_key, MessageSignTest) {
void* handle = NULL;
int ret = CfdCreateHandle(&handle);
EXPECT_EQ(kCfdSuccess, ret);
EXPECT_FALSE((NULL == handle));

const char* exp_sig_b64 = "H9Dk+Y13ybD+hH7okYJemWs0N9cIJ23Zn5T+lDFo+ZO3G9QSb6Qla2TATXFji29uip6vKsi8TJRZQHbCTu85/74=";
const char* exp_sig_hex = "1fd0e4f98d77c9b0fe847ee891825e996b3437d708276dd99f94fe943168f993b71bd4126fa4256b64c04d71638b6f6e8a9eaf2ac8bc4c94594076c24eef39ffbe";
const char* privkey_wif = "cUUuHBXPzhaFnny2gCBzZZzYtFyps9B1sDJbtJMC8ssjUhNMq9xk";
const char* privkey_hex = "cde0d924f9ffaa23951d86160329630261e68eaa62b8b389735b42238618c50e";
const char* pubkey = "0278bfaa4e045cc450ce9397136ac9bac1914147d85945dd9c8c3b2edc0108249d";
const char* pubkey2 = "0378bfaa4e045cc450ce9397136ac9bac1914147d85945dd9c8c3b2edc0108249d";
const char* msg = "This is just a test message";
char* signature = NULL;
char* recovered_pubkey = NULL;

ret = CfdSignMessage(handle, privkey_wif, msg, NULL, true,
&signature);
EXPECT_EQ(kCfdSuccess, ret);
if (ret == kCfdSuccess) {
EXPECT_STREQ(exp_sig_b64, signature);
CfdFreeStringBuffer(signature);
signature = NULL;
}

ret = CfdVerifyMessage(handle, exp_sig_b64, pubkey, msg, NULL,
&recovered_pubkey);
EXPECT_EQ(kCfdSuccess, ret);
if (ret == kCfdSuccess) {
EXPECT_STREQ(pubkey, recovered_pubkey);
CfdFreeStringBuffer(recovered_pubkey);
recovered_pubkey = NULL;
}

ret = CfdSignMessage(handle, privkey_hex, msg, NULL, false,
&signature);
EXPECT_EQ(kCfdSuccess, ret);
if (ret == kCfdSuccess) {
EXPECT_STREQ(exp_sig_hex, signature);
CfdFreeStringBuffer(signature);
signature = NULL;
}

ret = CfdVerifyMessage(handle, exp_sig_hex, pubkey, msg, NULL,
&recovered_pubkey);
EXPECT_EQ(kCfdSuccess, ret);
if (ret == kCfdSuccess) {
EXPECT_STREQ(pubkey, recovered_pubkey);
CfdFreeStringBuffer(recovered_pubkey);
recovered_pubkey = NULL;
}

// verify error
ret = CfdVerifyMessage(handle, exp_sig_hex, pubkey, "This is just a test message2", NULL,
&recovered_pubkey);
EXPECT_EQ(kCfdSignVerificationError, ret);
if (ret == kCfdSuccess) {
EXPECT_STRNE(pubkey, recovered_pubkey);
CfdFreeStringBuffer(recovered_pubkey);
recovered_pubkey = NULL;
}

ret = CfdVerifyMessage(handle, exp_sig_hex, pubkey2, msg, NULL,
&recovered_pubkey);
EXPECT_EQ(kCfdSignVerificationError, ret);
if (ret == kCfdSuccess) {
EXPECT_STREQ(pubkey, recovered_pubkey);
CfdFreeStringBuffer(recovered_pubkey);
recovered_pubkey = NULL;
}

ret = CfdFreeHandle(handle);
EXPECT_EQ(kCfdSuccess, ret);
}

TEST(cfdcapi_key, EcdsaAdaptorSignatureTest) {
void* handle = NULL;
Expand Down

0 comments on commit b962557

Please sign in to comment.