Skip to content
Draft
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
19 changes: 19 additions & 0 deletions packages/cookbook/messages/sign-verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
signNep413Message,
} from '@near-js/signer'; //
import {
verifyNep413Message,
} from '@near-js/utils'; // this should move

export default async function signAndVerifyNep413Message(accountId: string = ACCOUNT_ID, transactionHash: string = TX_HASH) {

// create a payload (Nep413Message)

const signedMessage = signNep413Message(Nep413Message);

try {
const isValid = verifyNep413Message(Nep413Message, signedMessage);
} catch (e) {
// invalid
}
}
68 changes: 68 additions & 0 deletions packages/utils/src/verify-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
SignedMessage,
Copy link
Author

Choose a reason for hiding this comment

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

I need this to be a shared type, could put it in types

SignedMessage + Nep413Message or just Message

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for putting shared types on the types package

} from '@near-js/signer'; // shared type maybe?

type SignedMessage {
accountId: string; // The account name to which the publicKey corresponds as plain text (e.g. "alice.near")
publicKey: PublicKey; // The public counterpart of the key used to sign, expressed as a string with format "<key-type>:<base58-key-bytes>" (e.g. "ed25519:6TupyNrcHGTt5XRLmHTc2KGaiSbjhQi1KHtCXTgbcr4Y")
signature: Uint8Array; // The base64 representation of the signature.
state?: string;
}

export function verifyNep413Message({ accountId, publicKey, signature, state }: SignedMessage) {
// we need to be passed the payload (what the message SHOULD be)
const dataThatWasHashed = createNEP413Payload(nep413PayloadToVerify);

const payloadHash = hashPayload(dataThatWasHashed);
const signatureBytes = base64.decode(signedMessage);

// determine the keypair type from publicKey
KeyPair.fromString(publicKey)

// if it is ed22519, then verify this way:
const isValid = ed25519.verify(
signatureBytes,
payloadHash,
base58.decode(publicKey.split(":")[1]),
);
if (!isValid) {
throw new Error("Ed25519 signature verification failed.");
}

return true;


try { // I do think we should throw, because there may be an unsupported keypair type
// return the verified payload on success, { success: true, payload: verifiedPayload }


verifySignature(payloadHash, signatureBytes, publicKey);
} catch (error) {
throw new Error(
`Cryptographic signature verification failed: ${error instanceof Error ? error.message : String(error)
}`,
);
Copy link
Author

Choose a reason for hiding this comment

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

near-sign-verify throws on failed verify, I think we should do this here too rather than returning a boolean because it forces the developer to handle it

Copy link
Contributor

Choose a reason for hiding this comment

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

completely agree

}
}

export function verifySignature(
payloadHash: Uint8Array,
signatureBytes: Uint8Array,
publicKeyString: string,
): boolean {
if (publicKeyString.startsWith(ED25519_PREFIX)) {
const isValid = ed25519.verify(
signatureBytes,
payloadHash,
base58.decode(publicKeyString.split(":")[1]),
);
if (!isValid) {
throw new Error("Ed25519 signature verification failed.");
}
return true;
}

throw new Error(
`Unsupported public key type: "${publicKeyString}". Must start with "${ED25519_PREFIX}".`,
);
}