8000 TON proof updates by Vladimir-Khm · Pull Request #342 · xssnick/tonutils-go · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

TON proof updates #342

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

Open
wants to merge 3 commits into
base: dev-14
Choose a base branch
from

Conversation

Vladimir-Khm
Copy link

1. Its very hard to know what exactly payload was sent

func (v *TonConnectVerifier) VerifyProof(ctx context.Context, addr *address.Address, proof TonConnectProof, expectedPayload string, stateInit []byte) error {
	// ...

	if proof.Payload != expectedPayload {
		return errors.New("invalid payload in proof")
	}

	// ..
}

ton/wallet/proof.go

In the current implementation, when verifying TON proof, you need to remember the exact payload your server sent to the user. In practice, this can be very difficult or impossible

A better approach to verify that the payload was sent by your server is to sign the payload. This way, you can easily verify its origin without having to remember it exactly (similar to JWT)

Proposed Solution

The solution involves introducing two functions: GeneratePayload and CheckPayload, and simplify VerifyProof

  • GeneratePayload: Generates a payload containing a random value and an expiration timestamp, then signs it using a secret.
  • CheckPayload: Verifies the signature of the payload and validates the expiration timestamp.

Additionally, we can provide a new function, VerifyProofWithPayload, which accepts a custom payloadVerifier function to handle the payload verification logic:

func (v *TonConnectVerifier) VerifyProofWithPayload(ctx context.Context, addr *address.Address, proof TonConnectProof, stateInit []byte, payloadVerifier func(payload, secret string) error, secret string) error {
	if err := payloadVerifier(proof.Payload, secret); err != nil {
		return fmt.Errorf("payload check failed: %w", err)
	}

	return v.VerifyProof(ctx, addr, proof, stateInit)
}

Remove the payload check from VerifyProof, as it is unrelated to the core verification logic:

func (v *TonConnectVerifier) VerifyProof(ctx context.Context, addr *address.Address, proof TonConnectProof, stateInit []byte) error {
	// ...
}

2. Optimize getting a public key

When verifying a signature, we need to retrieve the public key. There are two possible ways:

  1. Parse from stateInit (already provided in the request)
  2. Or fetch via API or other external request if the wallet is already initialized

Currently, the implementation prioritizes fetching the public key via an API request and falls back to parsing it from stateInit only if the API request fails. However, parsing the key from stateInit is much faster and more reliable since it avoids making an external request

It will be more optimized to make the vice versa approach: firstly, try to parse the key from stateInit, as it's faster, and generally, we have it. And only if it fails, try to get the public key from the request, as it is longer and less reliable:

func (v *TonConnectVerifier) getPubKey(ctx context.Context, addr *address.Address, stateInit []byte) (ed25519.PublicKey, error) {
	// First, try to get public key "offline" from stateInit
	key, err := getPublicKeyFromStateInit(addr, stateInit)

	// Only if it's impossible to parse public key from stateInit, make a request to get it
	if err != nil {
		key, err = GetPublicKey(ctx, v.client, addr)
		if err != nil {
			return nil, fmt.Errorf("failed to get public key: %w", err)
		}
	}

	return key, nil
}

- VerifyProof verifies only proof, not payload. VerifyProofWithPayload to check with payload
- Payload generation and check functions
@xssnick xssnick changed the base branch from master to dev-14 July 8, 2025 12:08
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
payload = h.Sum(payload)
return hex.EncodeToString(payload[:32]), nil
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets increase random size to 24 bytes and keep whole hash, not only 16 bytes, it costs us almost nothing, but looks more secure

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we can add half of domain hash (16 bytes) to payload before hashing, for example, to additionally bound to domain

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your comments! I agree that keeping all hash is more secure, so let's do it)

The random bytes in the payload ensure uniqueness, but they don’t improve cryptographic security, which comes from hmac and its secret. Increasing the random bytes won’t make payload more secure, but ok, let's enlarge it 16 bytes to make random nonce more distributed

As for domain, it could be useful in complex systems with multiple domains, but in a single-domain system it doesn’t add much value. Plus, we then check the domain in VerifyProof

So checking the full hash is very reasonable, but increasing random bytes or adding domain may not actually increase security. Should I add a commit to check the full hash and enlarge random bytes to 16?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While hashing only 16 bytes we can get only 2^128 unique hashes (so it is almost same as keeping 16 bytes of hash), but when we increasing payload to 32 bytes we could get up to 2^256 unique hashes, and full hash security is utilized.

So i think lets increase random size and check full hash

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation, it's really so. I'll fix it)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0