Skip to content
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

CU-86dthj1c5 - Increasing SignMessage / VerifyMessage compatibility #40

Merged
merged 4 commits into from
Jun 14, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/neon-dappkit",
"comment": "Increased compatibility of signMessage and verifyMessage",
"type": "minor"
}
],
"packageName": "@cityofzion/neon-dappkit"
}
28 changes: 26 additions & 2 deletions packages/neon-dappkit-types/src/Neo3Signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
Expand Down
19 changes: 15 additions & 4 deletions packages/neon-dappkit/NEON-SIGNER.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
```

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it better to use the SignMessageVersion enum instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

### Encrypt and Decrypt data

Expand Down
19 changes: 18 additions & 1 deletion packages/neon-dappkit/src/NeonSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class NeonSigner implements Neo3Signer {
publicKey: this.account.publicKey,
data: wallet.sign(messageHex, this.account.privateKey),
salt,
message,
messageHex,
}
}
Expand All @@ -53,6 +54,7 @@ export class NeonSigner implements Neo3Signer {
publicKey: this.account.publicKey,
data: wallet.sign(messageHex, this.account.privateKey, salt),
salt,
message,
messageHex,
}
}
Expand All @@ -63,6 +65,7 @@ export class NeonSigner implements Neo3Signer {
return {
publicKey: this.account.publicKey,
data: wallet.sign(messageHex, this.account.privateKey),
message,
messageHex,
}
}
Expand All @@ -74,7 +77,21 @@ export class NeonSigner implements Neo3Signer {
}

async verifyMessage(verifyArgs: SignedMessage): Promise<boolean> {
return wallet.verify(verifyArgs.messageHex, verifyArgs.data, verifyArgs.publicKey)
return (await this.verifyMessageSimple(verifyArgs)) || (await this.verifyMessageLegacy(verifyArgs))
}

private async verifyMessageSimple(verifyArgs: SignedMessage): Promise<boolean> {
const messageHex = verifyArgs.messageHex ?? u.str2hexstring(verifyArgs.message)
return wallet.verify(messageHex, verifyArgs.data, verifyArgs.publicKey)
}

private async verifyMessageLegacy(verifyArgs: SignedMessage): Promise<boolean> {
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)
}

/**
Expand Down
13 changes: 13 additions & 0 deletions packages/neon-dappkit/test/NeonSigner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
2 changes: 1 addition & 1 deletion rush.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading