diff --git a/common/changes/@cityofzion/neon-dappkit-types/CU-86dthj1c5_2024-06-07-15-03.json b/common/changes/@cityofzion/neon-dappkit-types/CU-86dthj1c5_2024-06-07-15-03.json new file mode 100644 index 0000000..15f76c0 --- /dev/null +++ b/common/changes/@cityofzion/neon-dappkit-types/CU-86dthj1c5_2024-06-07-15-03.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/neon-dappkit-types", + "comment": "SignedMessage object might have an optional message property instead of the messageHex", + "type": "minor" + } + ], + "packageName": "@cityofzion/neon-dappkit-types" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/neon-dappkit/CU-86dthj1c5_2024-06-07-15-03.json b/common/changes/@cityofzion/neon-dappkit/CU-86dthj1c5_2024-06-07-15-03.json new file mode 100644 index 0000000..f46730b --- /dev/null +++ b/common/changes/@cityofzion/neon-dappkit/CU-86dthj1c5_2024-06-07-15-03.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/neon-dappkit", + "comment": "Increased compatibility of signMessage and verifyMessage", + "type": "minor" + } + ], + "packageName": "@cityofzion/neon-dappkit" +} \ No newline at end of file diff --git a/packages/neon-dappkit-types/src/Neo3Signer.ts b/packages/neon-dappkit-types/src/Neo3Signer.ts index 1cfa34e..95969c5 100644 --- a/packages/neon-dappkit-types/src/Neo3Signer.ts +++ b/packages/neon-dappkit-types/src/Neo3Signer.ts @@ -16,9 +16,9 @@ export type SignMessagePayload = { } /** - * A simple type that defines the Signed Message format + * The base for the SignedMessage, it doesn't have message or messageHex */ -export type SignedMessage = { +export interface SignedMessageBase { /** * signer's public key */ @@ -33,13 +33,37 @@ export type SignedMessage = { * salt used to encrypt */ salt?: string +} +/** + * The SignedMessage including the messageHex, message is optional + */ +export interface SignedMessageWithHex extends SignedMessageBase { /** * message hex */ messageHex: string + + message?: string } +/** + * The SignedMessage including the original message, the messageHex is optional + */ +export interface SignedMessageWithTheOriginal extends SignedMessageBase { + /** + * original message + */ + message: string + + messageHex?: string +} + +/** + * A simple type that defines the Signed Message format + */ +export type SignedMessage = SignedMessageWithHex | SignedMessageWithTheOriginal + export interface EncryptedPayload { randomVector: string cipherText: string diff --git a/packages/neon-dappkit/NEON-SIGNER.md b/packages/neon-dappkit/NEON-SIGNER.md index 42fc82b..11d02b6 100644 --- a/packages/neon-dappkit/NEON-SIGNER.md +++ b/packages/neon-dappkit/NEON-SIGNER.md @@ -29,16 +29,27 @@ The process of signing and then verifying a message is useful to prove that the truly signed your specific message. ```ts // 1) sign a message -const mySignedMessage = await signer.signMessage({ message: 'My message', version: 2 }) -// the signed message contains messageHex, data, publicKey and salt +const mySignedMessage = await signer.signMessage({ message: 'My message' }) +// the signed message contains the message, messageHex, data, publicKey and salt // 2) store or share these information to be verified later or by someone else // 3) check if the signature is valid, if the method returns true, it is certain that that specific publicKey signed that messageHex const valid = await signer.verifyMessage(mySignedMessage) ``` -You can use different **versions**, the default is `2`, but you can use `3` to sign a message without salt, and `1` to -use the legacy version. +You can use different signing **versions**: +1. CLASSIC: The same format as Neoline, with salt. +2. DEFAULT: The more human-readable version, easier to verify with neon-js (with salt). +3. WITHOUT_SALT: The same format as Neoline, but without salt. + +```ts +import { SignMessageVersion } from '@cityofzion/neon-dappkit-types' +// ... +const mySignedMessage = await signer.signMessage({ + message: 'My message', + version: SignMessageVersion.CLASSIC +}) +``` ### Encrypt and Decrypt data diff --git a/packages/neon-dappkit/src/NeonSigner.ts b/packages/neon-dappkit/src/NeonSigner.ts index 391b6d9..b88c83d 100644 --- a/packages/neon-dappkit/src/NeonSigner.ts +++ b/packages/neon-dappkit/src/NeonSigner.ts @@ -41,6 +41,7 @@ export class NeonSigner implements Neo3Signer { publicKey: this.account.publicKey, data: wallet.sign(messageHex, this.account.privateKey), salt, + message, messageHex, } } @@ -53,6 +54,7 @@ export class NeonSigner implements Neo3Signer { publicKey: this.account.publicKey, data: wallet.sign(messageHex, this.account.privateKey, salt), salt, + message, messageHex, } } @@ -63,6 +65,7 @@ export class NeonSigner implements Neo3Signer { return { publicKey: this.account.publicKey, data: wallet.sign(messageHex, this.account.privateKey), + message, messageHex, } } @@ -74,7 +77,21 @@ export class NeonSigner implements Neo3Signer { } async verifyMessage(verifyArgs: SignedMessage): Promise { - return wallet.verify(verifyArgs.messageHex, verifyArgs.data, verifyArgs.publicKey) + return (await this.verifyMessageSimple(verifyArgs)) || (await this.verifyMessageLegacy(verifyArgs)) + } + + private async verifyMessageSimple(verifyArgs: SignedMessage): Promise { + const messageHex = verifyArgs.messageHex ?? u.str2hexstring(verifyArgs.message) + return wallet.verify(messageHex, verifyArgs.data, verifyArgs.publicKey) + } + + private async verifyMessageLegacy(verifyArgs: SignedMessage): Promise { + const message = verifyArgs.salt + (verifyArgs.message ?? u.hexstring2str(verifyArgs.messageHex)) + const parameterHexString = Buffer.from(message).toString('hex') + const lengthHex = u.num2VarInt(parameterHexString.length / 2) + const concatenatedString = lengthHex + parameterHexString + const messageHex = '010001f0' + concatenatedString + '0000' + return wallet.verify(messageHex, verifyArgs.data, verifyArgs.publicKey) } /** diff --git a/packages/neon-dappkit/test/NeonSigner.spec.ts b/packages/neon-dappkit/test/NeonSigner.spec.ts index 6481100..e5c7c58 100644 --- a/packages/neon-dappkit/test/NeonSigner.spec.ts +++ b/packages/neon-dappkit/test/NeonSigner.spec.ts @@ -73,6 +73,19 @@ describe('NeonSigner', function () { assert(verified) }) + it('can verify legacy messages', async () => { + const signer = new NeonSigner() + const verified = await signer.verifyMessage({ + message: 'Hello World', + messageHex: '48656c6c6f20576f726c64', + salt: '1b7fbf00b4f43ed9afd51d794e3470b8', + data: 'ba32f6a69e80e7e0ddc8c18324988769cd785f0e36f6ca5b7c0ab49149b89a27f9173fcfa8404386dd69bc14605ed8440e4970da0bda802b58a673fc5d00f3d4', + publicKey: '0297f256ad8c5361dca4e931f7c69568fbb14b0b203062678d160dbaeff88e9e53', + }) + + assert(verified) + }) + it('can verify when failing', async () => { const signer = new NeonSigner() const verified = await signer.verifyMessage({ diff --git a/rush.json b/rush.json index fa9c956..d5952b0 100644 --- a/rush.json +++ b/rush.json @@ -2,7 +2,7 @@ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", "rushVersion": "5.102.0", "pnpmVersion": "7.33.5", - "nodeSupportedVersionRange": ">=14.15.0 <15.0.0 || >=16.13.0 <17.0.0 || >=18.15.0 <19.0.0", + "nodeSupportedVersionRange": ">=14.15.0 <15.0.0 || >=16.13.0 <17.0.0 || >=18.15.0 <=20.14.0", "repository": { "url": "https://github.com/CityOfZion/neon-dappkit", "defaultBranch": "main"