Skip to content
Open
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
27 changes: 7 additions & 20 deletions src/multichain/core/aptos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
SimpleTransaction,
UserTransactionResponse,
Ed25519PrivateKey,
Ed25519PublicKey,
Ed25519Signature,
} from "@aptos-labs/ts-sdk"
import { generateSignedTransaction } from "@aptos-labs/ts-sdk";

Expand All @@ -15,7 +17,6 @@ import { IPayParams, XmTransactionResponse } from "./types/interfaces"
import { required } from "./utils"
import { XMScript } from "@/types"
import { hexToUint8Array, uint8ArrayToHex } from "@/encryption";
import * as forge from "node-forge";

/* LICENSE

Expand Down Expand Up @@ -430,27 +431,13 @@ export class APTOS extends DefaultChain implements AptosDefaultChain {
publicKey: string
): Promise<boolean> {
try {
// Convert hex strings to Uint8Arrays
const signatureBytes = hexToUint8Array(signature)
const publicKeyBytes = hexToUint8Array(publicKey)
const pubKey = new Ed25519PublicKey(publicKey)
const sig = new Ed25519Signature(hexToUint8Array(signature))

// Validate input sizes
if (signatureBytes.length !== 64) {
throw new Error("Invalid signature length. Ed25519 signatures must be 64 bytes.")
}
if (publicKeyBytes.length !== 32) {
throw new Error("Invalid public key length. Ed25519 public keys must be 32 bytes.")
}

// Use node-forge for Ed25519 signature verification
const isValid = forge.pki.ed25519.verify({
message: message,
encoding: "utf8",
signature: signatureBytes,
publicKey: publicKeyBytes
return pubKey.verifySignature({
message,
signature: sig,
Comment on lines +434 to +439
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For the @aptos-labs/ts-sdkversion used in this repository, how does Ed25519PublicKey.verifySignature interpretmessage when it is passed a JavaScript string? Does it verify UTF-8 bytes, or does it expect hex/HexInput semantics?

💡 Result:

In @aptos-labs/ts-sdk (e.g., v1.16.0), Ed25519PublicKey.verifySignature({ message }) treats a JavaScript string as follows:

  • If the string is a valid hex string per the SDK’s Hex.isValid rules (optional 0x prefix, non-empty, even-length, only hex chars), it is interpreted as hex bytes (i.e., HexInput semantics), not UTF-8. [1][2]
  • Otherwise, it converts the string to UTF-8 bytes via Buffer.from(message, "utf8") and verifies those bytes. [2][1]

So: it’s “hex-if-it-looks-like-hex, otherwise UTF-8.” [2][1]

Sources: [1] https://app.unpkg.com/%40aptos-labs/ts-sdk%401.16.0/files/dist/esm/chunk-CX4BEIQT.mjs, [2] https://app.unpkg.com/%40aptos-labs/ts-sdk%401.16.0/files/dist/esm/chunk-F7EMGK4M.mjs, [3] https://app.unpkg.com/%40aptos-labs/ts-sdk%401.16.0/files/dist/esm/chunk-BCUSI3N6.mjs


Verify the same UTF-8 bytes that signMessage signs.

Line 412 signs new TextEncoder().encode(message), but Line 438 passes the raw JS string into verifySignature. The @aptos-labs/ts-sdk applies a heuristic when interpreting string input: if it looks like valid hex, it treats it as hex bytes; otherwise, it converts to UTF-8.

For the test case "Hello Aptos!", this works because it is not valid hex, so the SDK falls back to UTF-8. However, any message that happens to be a valid hex string (e.g., "aabbcc") will be signed as UTF-8 bytes but verified as hex bytes, causing verification to fail.

Encode the message before verification to make both paths use identical bytes and avoid this fragility.

Proposed fix
     async verifyMessage(
         message: string,
         signature: string,
         publicKey: string
     ): Promise<boolean> {
         try {
+            const messageBytes = new TextEncoder().encode(message)
             const pubKey = new Ed25519PublicKey(publicKey)
             const sig = new Ed25519Signature(hexToUint8Array(signature))
 
             return pubKey.verifySignature({
-                message,
+                message: messageBytes,
                 signature: sig,
             })
         } catch (error) {
             console.error("Failed to verify message:", error)
             return false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/multichain/core/aptos.ts` around lines 434 - 439, The verification uses a
raw JS string while signMessage signs new TextEncoder().encode(message), causing
a mismatch for hex-looking strings; update the call to pubKey.verifySignature in
Aptos-related code to pass the same UTF-8 bytes (e.g., new
TextEncoder().encode(message)) instead of the raw string so
Ed25519PublicKey.verifySignature and the earlier signMessage use identical byte
input.

})

return isValid
} catch (error) {
console.error("Failed to verify message:", error)
return false
Expand Down
Loading