Closed owtmeta closed 2 months ago
Thanks for leaving it here. This will be supported, but difficult to say when exactly.
public static void main(String[] args) throws Exception {
String proof = "{\n" +
" \"timestamp\": 1722999580, \n" +
" \"domain\": {\n" +
" \"lengthBytes\": 16, \n" +
" \"value\": \"xxx.xxx.com\"\n" +
" }, \n" +
" \"signature\": \"i6dpzjKo/S+1xxx==\", \n" +
" \"payload\": \"doc-example-<BACKEND_AUTH_ID>\"\n" +
" }";
String accountString = "{\n" +
" \"address\": \"0:307d63475bxxx", \n" +
" \"chain\": \"-239\", \n" +
" \"walletStateInit\": \"xxx=\", \n" +
" \"publicKey\": \"712d3b3d5xxxxxx\"\n" +
" }";
TonProof tonProof = JSONObject.parseObject(proof,TonProof.class);
WalletAccount account= JSONObject.parseObject(accountString,WalletAccount.class);
log.info("tonProof:{}",JSONObject.toJSONString(tonProof));
log.info("account:{}",JSONObject.toJSONString(account));
boolean ret = checkProof(tonProof,account);
log.info("ret:{}",ret);
}
public static boolean checkProof(TonProof tonProof, WalletAccount account) throws Exception {
String address = account.getAddress().replace("0:", "");
byte[] addressBytes = Hex.decodeHex(address);
byte[] publicKeyBytes = Hex.decodeHex(account.getPublicKey());
long timestamp = tonProof.getTimestamp();
int domainLength = tonProof.getDomain().getLengthBytes();//.lengthBytes;
String domainValue = tonProof.getDomain().getValue();//.value;
byte[] signature = Base64.decode(tonProof.getSignature());
String payload = tonProof.getPayload();
// Create message
byte[] h = "ton-proof-item-v2/".getBytes(StandardCharsets.UTF_8);
ByteBuffer messageBuffer = ByteBuffer.allocate(h.length+ 4 + addressBytes.length + 4 + domainValue.length() + 8 + payload.length());
messageBuffer.put(h);
messageBuffer.putInt(Integer.reverseBytes(0)); // workchain, little-endian
messageBuffer.put(addressBytes);
messageBuffer.putInt(Integer.reverseBytes(domainLength)); // domain length, little-endian
messageBuffer.put(domainValue.getBytes(StandardCharsets.UTF_8));
messageBuffer.putLong(Long.reverseBytes(timestamp)); // timestamp, little-endian
messageBuffer.put(payload.getBytes(StandardCharsets.UTF_8));
byte[] message = messageBuffer.array();
// Create message for signing
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] hashedMessage = sha256.digest(message);
byte[] signatureMessage = new byte[2 + "ton-connect".length() + hashedMessage.length];
signatureMessage[0] = (byte) 0xFF;
signatureMessage[1] = (byte) 0xFF;
System.arraycopy("ton-connect".getBytes(StandardCharsets.UTF_8), 0, signatureMessage, 2, "ton-connect".length());
System.arraycopy(hashedMessage, 0, signatureMessage, 2 + "ton-connect".length(), hashedMessage.length);
byte[] hashedSignatureMessage = sha256.digest(signatureMessage);
// Verify signature using Bouncy Castle
Ed25519PublicKeyParameters publicKeyParameters = new Ed25519PublicKeyParameters(publicKeyBytes, 0);
Ed25519Signer verifier = new Ed25519Signer();
verifier.init(false, publicKeyParameters);
verifier.update(hashedSignatureMessage, 0, hashedSignatureMessage.length);
return verifier.verifySignature(signature);
}
static {
Security.addProvider(new BouncyCastleProvider());
}
@Data
class TonProof {
private long timestamp;
private Domain domain;
private String signature;
private String payload;
}
@Data
class Domain {
private int lengthBytes;
private String value;
}
@Data
class WalletAccount {
private String address;
private String publicKey;
private String chain;
private String walletStateInit;
}
public static void main(String[] args) throws Exception { String proof = "{\n" + " \"timestamp\": 1722999580, \n" + " \"domain\": {\n" + " \"lengthBytes\": 16, \n" + " \"value\": \"xxx.xxx.com\"\n" + " }, \n" + " \"signature\": \"i6dpzjKo/S+1xxx==\", \n" + " \"payload\": \"doc-example-<BACKEND_AUTH_ID>\"\n" + " }"; String accountString = "{\n" + " \"address\": \"0:307d63475bxxx", \n" + " \"chain\": \"-239\", \n" + " \"walletStateInit\": \"xxx=\", \n" + " \"publicKey\": \"712d3b3d5xxxxxx\"\n" + " }"; TonProof tonProof = JSONObject.parseObject(proof,TonProof.class); WalletAccount account= JSONObject.parseObject(accountString,WalletAccount.class); log.info("tonProof:{}",JSONObject.toJSONString(tonProof)); log.info("account:{}",JSONObject.toJSONString(account)); boolean ret = checkProof(tonProof,account); log.info("ret:{}",ret); } public static boolean checkProof(TonProof tonProof, WalletAccount account) throws Exception { String address = account.getAddress().replace("0:", ""); byte[] addressBytes = Hex.decodeHex(address); byte[] publicKeyBytes = Hex.decodeHex(account.getPublicKey()); long timestamp = tonProof.getTimestamp(); int domainLength = tonProof.getDomain().getLengthBytes();//.lengthBytes; String domainValue = tonProof.getDomain().getValue();//.value; byte[] signature = Base64.decode(tonProof.getSignature()); String payload = tonProof.getPayload(); // Create message byte[] h = "ton-proof-item-v2/".getBytes(StandardCharsets.UTF_8); ByteBuffer messageBuffer = ByteBuffer.allocate(h.length+ 4 + addressBytes.length + 4 + domainValue.length() + 8 + payload.length()); messageBuffer.put(h); messageBuffer.putInt(Integer.reverseBytes(0)); // workchain, little-endian messageBuffer.put(addressBytes); messageBuffer.putInt(Integer.reverseBytes(domainLength)); // domain length, little-endian messageBuffer.put(domainValue.getBytes(StandardCharsets.UTF_8)); messageBuffer.putLong(Long.reverseBytes(timestamp)); // timestamp, little-endian messageBuffer.put(payload.getBytes(StandardCharsets.UTF_8)); byte[] message = messageBuffer.array(); // Create message for signing MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] hashedMessage = sha256.digest(message); byte[] signatureMessage = new byte[2 + "ton-connect".length() + hashedMessage.length]; signatureMessage[0] = (byte) 0xFF; signatureMessage[1] = (byte) 0xFF; System.arraycopy("ton-connect".getBytes(StandardCharsets.UTF_8), 0, signatureMessage, 2, "ton-connect".length()); System.arraycopy(hashedMessage, 0, signatureMessage, 2 + "ton-connect".length(), hashedMessage.length); byte[] hashedSignatureMessage = sha256.digest(signatureMessage); // Verify signature using Bouncy Castle Ed25519PublicKeyParameters publicKeyParameters = new Ed25519PublicKeyParameters(publicKeyBytes, 0); Ed25519Signer verifier = new Ed25519Signer(); verifier.init(false, publicKeyParameters); verifier.update(hashedSignatureMessage, 0, hashedSignatureMessage.length); return verifier.verifySignature(signature); } static { Security.addProvider(new BouncyCastleProvider()); }
@Data class TonProof { private long timestamp; private Domain domain; private String signature; private String payload; } @Data class Domain { private int lengthBytes; private String value; } @Data class WalletAccount { private String address; private String publicKey; private String chain; private String walletStateInit; }
Is there a specific implementation demo? Thank you
Thanks for the example. Let me review it and hopefully, I will add it very soon!
TonConnect has been added to the ton4j. See the latest release. Thanks for the code snippets!
verify a user to provide them with their personal information from the back end.
https://docs.ton.org/develop/dapps/ton-connect/sign
Is there a plan to implement this?
Tks!