Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TON]: Add TONMessageSigner module for TON dapp support #3985

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

package com.trustwallet.core.app.blockchains.theopennetwork

import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.PrivateKey
import wallet.core.jni.TONMessageSigner
import wallet.core.jni.TONWallet

class TestTheOpenNetworkMessageSigner {
init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun TheOpenNetworkMessageSignerSignMessage() {
// The private key has been derived by using [ton-mnemonic](https://www.npmjs.com/package/tonweb-mnemonic/v/0.0.2)
// from the following mnemonic:
// document shield addict crime broom point story depend suit satisfy test chicken valid tail speak fortune sound drill seek cube cheap body music recipe
val privateKey = PrivateKey("112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18".toHexByteArray())
val message = "Hello world"
val signature = TONMessageSigner.signMessage(privateKey, message)
// The following signature has been computed by calling `window.ton.send("ton_personalSign", { data: "Hello world" });`.
assertEquals(signature, "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504")
}
}
26 changes: 26 additions & 0 deletions include/TrustWalletCore/TWTONMessageSigner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#pragma once

#include "TWBase.h"
#include "TWPrivateKey.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN

/// TON message signing.
TW_EXPORT_CLASS
struct TWTONMessageSigner;

/// Signs an arbitrary message to prove ownership of an address for off-chain services.
/// https://github.com/ton-foundation/specs/blob/main/specs/wtf-0002.md
///
/// \param privateKey: the private key used for signing
/// \param message: A custom message which is input to the signing.
/// \returns the signature, Hex-encoded. On invalid input null is returned. Returned object needs to be deleted after use.
TW_EXPORT_STATIC_METHOD
TWString *_Nullable TWTONMessageSignerSignMessage(struct TWPrivateKey *_Nonnull privateKey, TWString* _Nonnull message);

TW_EXTERN_C_END
1 change: 1 addition & 0 deletions rust/chains/tw_ton/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
// Copyright © 2017 Trust Wallet.

pub mod address_converter;
pub mod personal_message_signer;
pub mod wallet_provider;
50 changes: 50 additions & 0 deletions rust/chains/tw_ton/src/modules/personal_message_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_hash::sha2::sha512;
use tw_keypair::ed25519::sha512::PrivateKey;
use tw_keypair::ed25519::Signature;
use tw_keypair::traits::SigningKeyTrait;
use tw_keypair::KeyPairResult;

pub const TON_PERSONAL_MESSAGE_PREFIX: &str = "ton-safe-sign-magic";

pub struct PersonalMessageSigner;

impl PersonalMessageSigner {
/// Signs an arbitrary message.
/// https://www.openmask.app/docs/api-reference/rpc-api#ton_personalsign
/// https://github.com/OpenProduct/openmask-extension/blob/7566ceb2772fed7a3a27d2a67bd34bf89e862557/src/view/screen/notifications/sign/api.ts#L21-L48
pub fn sign(private_key: &PrivateKey, msg: &str) -> KeyPairResult<Signature> {
let msg_hash = sha512(msg.as_bytes());

let mut msg_to_sign = vec![0xff_u8, 0xff];
msg_to_sign.extend_from_slice(TON_PERSONAL_MESSAGE_PREFIX.as_bytes());
msg_to_sign.extend_from_slice(msg_hash.as_slice());

private_key.sign(msg_to_sign)
}
}

#[cfg(test)]
mod tests {
use super::*;
use tw_hash::H512;

#[test]
fn test_sign_personal_message() {
// The private key has been derived by using [ton-mnemonic](https://www.npmjs.com/package/tonweb-mnemonic/v/0.0.2)
// from the following mnemonic:
// document shield addict crime broom point story depend suit satisfy test chicken valid tail speak fortune sound drill seek cube cheap body music recipe
let private_key = PrivateKey::try_from(
"112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18",
)
.unwrap();

let signature = PersonalMessageSigner::sign(&private_key, "Hello world").unwrap();
// The following signature has been computed by calling `window.ton.send("ton_personalSign", { data: "Hello world" });`.
let expected_sig = "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504";
assert_eq!(signature.to_bytes(), H512::from(expected_sig));
}
}
6 changes: 6 additions & 0 deletions rust/tw_keypair/src/ffi/privkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ pub struct TWPrivateKey(pub(crate) PrivateKey);

impl RawPtrTrait for TWPrivateKey {}

impl AsRef<PrivateKey> for TWPrivateKey {
fn as_ref(&self) -> &PrivateKey {
&self.0
}
}

/// Create a private key with the given block of data.
///
/// \param input *non-null* byte array.
Expand Down
44 changes: 44 additions & 0 deletions rust/wallet_core_rs/src/ffi/ton/message_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#![allow(clippy::missing_safety_doc)]

use tw_encoding::hex::ToHex;
use tw_keypair::ed25519;
use tw_keypair::ffi::privkey::TWPrivateKey;
use tw_memory::ffi::tw_string::TWString;
use tw_memory::ffi::RawPtrTrait;
use tw_misc::try_or_else;
use tw_ton::modules::personal_message_signer::PersonalMessageSigner;

/// Signs an arbitrary message to prove ownership of an address for off-chain services.
/// https://github.com/ton-foundation/specs/blob/main/specs/wtf-0002.md
///
/// \param private_key: the private key used for signing
/// \param message: A custom message which is input to the signing.
/// \returns the signature, Hex-encoded. On invalid input null is returned. Returned object needs to be deleted after use.
#[no_mangle]
pub unsafe extern "C" fn tw_ton_message_signer_sign_message(
private_key: *const TWPrivateKey,
message: *const TWString,
) -> *mut TWString {
let private_key = try_or_else!(
TWPrivateKey::from_ptr_as_ref(private_key),
std::ptr::null_mut
);
let private_key_bytes = private_key.as_ref().key();
let ed25519_private_key = try_or_else!(
ed25519::sha512::PrivateKey::try_from(private_key_bytes.as_slice()),
std::ptr::null_mut
);

let message = try_or_else!(TWString::from_ptr_as_ref(message), std::ptr::null_mut);
let message_str = try_or_else!(message.as_str(), std::ptr::null_mut);

let signature = try_or_else!(
PersonalMessageSigner::sign(&ed25519_private_key, message_str),
std::ptr::null_mut
);
TWString::from(signature.to_bytes().to_hex()).into_ptr()
}
1 change: 1 addition & 0 deletions rust/wallet_core_rs/src/ffi/ton/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
// Copyright © 2017 Trust Wallet.

pub mod address_converter;
pub mod message_signer;
pub mod wallet;
8 changes: 8 additions & 0 deletions rust/wallet_core_rs/tests/ethereum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#[path = "ethereum/ethereum_abi.rs"]
mod ethereum_abi;
#[path = "ethereum/ethereum_rlp.rs"]
mod ethereum_rlp;
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ fn number_n<const BITS: u32>(value: u64) -> Proto::NumberNParam<'static> {

#[test]
fn test_ethereum_abi_decode_contract_call() {
const CUSTOM_ABI_JSON: &str = include_str!("data/custom.json");
const CUSTOM_DECODED_JSON: &str = include_str!("data/custom_decoded.json");
const CUSTOM_ABI_JSON: &str = include_str!("../data/custom.json");
const CUSTOM_DECODED_JSON: &str = include_str!("../data/custom_decoded.json");

let encoded = "ec37a4a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000067472757374790000000000000000000000000000000000000000000000000000".decode_hex().unwrap();

Expand Down
8 changes: 8 additions & 0 deletions rust/wallet_core_rs/tests/solana.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#[path = "solana/solana_address.rs"]
mod solana_address;
#[path = "solana/solana_transaction.rs"]
mod solana_transaction;
10 changes: 10 additions & 0 deletions rust/wallet_core_rs/tests/ton.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#[path = "ton/ton_address_converter.rs"]
mod ton_address_converter;
#[path = "ton/ton_message_signer.rs"]
mod ton_message_signer;
#[path = "ton/ton_wallet.rs"]
mod ton_wallet;
22 changes: 22 additions & 0 deletions rust/wallet_core_rs/tests/ton/ton_message_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper;
use tw_memory::test_utils::tw_string_helper::TWStringHelper;
use wallet_core_rs::ffi::ton::message_signer::tw_ton_message_signer_sign_message;

#[test]
fn test_ton_wallet_create_state_init() {
let private_key = TWPrivateKeyHelper::with_hex(
"112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18",
);
assert!(!private_key.is_null());
let message = TWStringHelper::create("Hello world");

let signature = TWStringHelper::wrap(unsafe {
tw_ton_message_signer_sign_message(private_key.ptr(), message.ptr())
});
assert!(!signature.ptr().is_null());
assert_eq!(signature.to_string().unwrap(), "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504");
}
22 changes: 22 additions & 0 deletions src/interface/TWTONMessageSigner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#include "TrustWalletCore/TWTONMessageSigner.h"
#include "rust/Wrapper.h"
#include "PrivateKey.h"

using namespace TW;

TWString *_Nullable TWTONMessageSignerSignMessage(struct TWPrivateKey *_Nonnull privateKey, TWString* _Nonnull message) {
auto* privateKeyRustRaw = Rust::tw_private_key_create_with_data(privateKey->impl.bytes.data(), privateKey->impl.bytes.size());
const auto privateKeyRust = std::shared_ptr<Rust::TWPrivateKey>(privateKeyRustRaw, Rust::tw_private_key_delete);

Rust::TWStringWrapper messageRust = TWStringUTF8Bytes(message);
Rust::TWStringWrapper signature = Rust::tw_ton_message_signer_sign_message(privateKeyRust.get(), messageRust.get());

if (!signature) {
return nullptr;
}
return TWStringCreateWithUTF8Bytes(signature.c_str());
}
12 changes: 12 additions & 0 deletions swift/Tests/Blockchains/TheOpenNetworkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ class TheOpenNetworkTests: XCTestCase {
let stateInit = TONWallet.buildV4R2StateInit(publicKey: publicKey, workchain: 0, walletId: 0x29a9a317)!
XCTAssertEqual(stateInit, "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg=")
}

func testMessageSigner() {
// The private key has been derived by using [ton-mnemonic](https://www.npmjs.com/package/tonweb-mnemonic/v/0.0.2)
// from the following mnemonic:
// document shield addict crime broom point story depend suit satisfy test chicken valid tail speak fortune sound drill seek cube cheap body music recipe
let privateKeyData = Data(hexString: "112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18")!
let privateKey = PrivateKey(data: privateKeyData)!
let message = "Hello world"
let signature = TONMessageSigner.signMessage(privateKey: privateKey, message: message)!
// The following signature has been computed by calling `window.ton.send("ton_personalSign", { data: "Hello world" });`.
XCTAssertEqual(signature, "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504")
}

func testSign() {
let privateKeyData = Data(hexString: "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0")!
Expand Down
21 changes: 21 additions & 0 deletions tests/chains/TheOpenNetwork/TWTONMessageSignerTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#include "TestUtilities.h"

#include "HexCoding.h"
#include "TrustWalletCore/TWTONMessageSigner.h"

namespace TW::TheOpenNetwork::tests {

TEST(TWTONMessageSigner, SignMessage) {
const auto privateKeyBytes = DATA("112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18");
const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(privateKeyBytes.get()));
const auto message = STRING("Hello world");

const auto signature = WRAPS(TWTONMessageSignerSignMessage(privateKey.get(), message.get()));
assertStringsEqual(signature, "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504");
}

} // namespace TW::TheOpenNetwork::tests
Loading