Skip to content

Commit fc3e893

Browse files
committed
fix: support new and old, tests
1 parent 8d29915 commit fc3e893

File tree

4 files changed

+64
-18
lines changed

4 files changed

+64
-18
lines changed

packages/core/src/connect/message-signing/encoding.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import {
22
encodeMessage as stacksEncodeMessage,
33
hashMessage as stacksHashMessage,
44
} from '@stacks/encryption';
5-
import { decodeMessage, encodeMessage, hashMessage } from './encoding';
5+
import { decodeMessage, encodeMessage, hashMessage, LEGACY_CHAIN_PREFIX_BYTES } from './encoding';
66
import { bytesToHex, utf8ToBytes } from 'micro-stacks/common';
77
import { concatBytes } from '@noble/hashes/utils';
88

99
describe('encoding', function () {
1010
test('compat', () => {
1111
const message = stacksEncodeMessage('hi there');
12-
const myMessage = encodeMessage('hi there');
12+
const myMessage = encodeMessage('hi there', LEGACY_CHAIN_PREFIX_BYTES);
1313
expect(bytesToHex(message)).toEqual(bytesToHex(myMessage));
1414
});
1515
test('hashing', () => {
1616
const message = bytesToHex(stacksHashMessage('hi there'));
17-
const myMessage = bytesToHex(hashMessage('hi there'));
17+
const myMessage = bytesToHex(hashMessage('hi there', LEGACY_CHAIN_PREFIX_BYTES));
1818
expect(message).toEqual(myMessage);
1919
});
2020
// src: https://github.com/hirosystems/stacks.js/blob/a86f01e7a25a79ae9d3da0df5a654caf2bb7e9e9/packages/encryption/tests/messageSignature.test.ts

packages/core/src/connect/message-signing/encoding.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,29 @@ import { decode, encode, encodingLength } from './varuint';
33
import { utf8ToBytes } from '@noble/hashes/utils';
44
import { concatByteArrays } from 'micro-stacks/common';
55

6-
const chainPrefix = '\x17Stacks Signed Message:\n';
7-
const chainPrefixBytes = utf8ToBytes(chainPrefix);
6+
const CHAIN_PREFIX = '\x17Stacks Signed Message:\n';
7+
export const CHAIN_PREFIX_BYTES = utf8ToBytes(CHAIN_PREFIX);
8+
const LEGACY_CHAIN_PREFIX = '\x18Stacks Message Signing:\n';
9+
export const LEGACY_CHAIN_PREFIX_BYTES = utf8ToBytes(LEGACY_CHAIN_PREFIX);
810

9-
export function hashMessage(message: string | Uint8Array) {
10-
return hashSha256(encodeMessage(message));
11+
export function hashMessage(message: string | Uint8Array, prefix: Uint8Array = CHAIN_PREFIX_BYTES) {
12+
return hashSha256(encodeMessage(message, prefix));
1113
}
1214

13-
export function encodeMessage(message: string | Uint8Array): Uint8Array {
15+
export function encodeMessage(
16+
message: string | Uint8Array,
17+
prefix: Uint8Array = CHAIN_PREFIX_BYTES
18+
): Uint8Array {
1419
const bytes = typeof message === 'string' ? utf8ToBytes(message) : message;
1520
const encoded = encode(bytes.length);
16-
return concatByteArrays([chainPrefixBytes, encoded, bytes]);
21+
return concatByteArrays([prefix, encoded, bytes]);
1722
}
1823

19-
export function decodeMessage(encodedMessage: Uint8Array): Uint8Array {
20-
const messageWithoutChainPrefix = encodedMessage.subarray(chainPrefixBytes.byteLength);
24+
export function decodeMessage(
25+
encodedMessage: Uint8Array,
26+
prefix: Uint8Array = CHAIN_PREFIX_BYTES
27+
): Uint8Array {
28+
const messageWithoutChainPrefix = encodedMessage.subarray(prefix.byteLength);
2129
const decoded = decode(messageWithoutChainPrefix);
2230
const varIntLength = encodingLength(decoded);
2331
// Remove the varuint prefix

packages/core/src/connect/message-signing/verify.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
verifyMessageSignature,
2424
verifyStructuredMessageSignature,
2525
} from './verify';
26-
import { hashMessage } from './encoding';
26+
import { hashMessage, LEGACY_CHAIN_PREFIX_BYTES } from './encoding';
2727
import { getStructuredDataHashes, makeStructuredDataHash } from './structured-message';
2828
import { publicKeyToStxAddress, StacksNetworkVersion } from 'micro-stacks/crypto';
2929

@@ -91,7 +91,7 @@ describe('Verify messages (RSV mode)', () => {
9191
};
9292
const stxAddress = publicKeyToStxAddress(payload.publicKey);
9393

94-
const hash = hashMessage(message);
94+
const hash = hashMessage(message, LEGACY_CHAIN_PREFIX_BYTES);
9595

9696
// make sure we can get the public key
9797
test(getPublicKeyFromSignature.name, () => {

packages/core/src/connect/message-signing/verify.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { recoverPublicKey, Signature, verify } from '@noble/secp256k1';
22
import { bytesToHex, hexToBigInt } from 'micro-stacks/common';
3-
import { hashMessage } from './encoding';
3+
import { hashMessage, LEGACY_CHAIN_PREFIX_BYTES } from './encoding';
44

55
import { getStructuredDataHashes, makeStructuredDataHash } from './structured-message';
66
import { StructuredSignatureRequestOptions } from './types';
@@ -55,13 +55,14 @@ export const recoverSignature = (options: { signature: string; mode?: 'vrs' | 'r
5555
};
5656
};
5757

58-
export const verifyMessageSignature = (options: {
58+
export const _verifyMessageSignature = (options: {
5959
// string = message, bytes = hash
6060
message: string | Uint8Array;
6161
signature: string;
6262
publicKey?: string;
6363
stxAddress?: string;
6464
mode?: 'vrs' | 'rsv';
65+
prefix?: Uint8Array;
6566
}) => {
6667
if (!options.publicKey && !options.stxAddress)
6768
throw Error(
@@ -72,7 +73,7 @@ export const verifyMessageSignature = (options: {
7273
let publicKey = options.publicKey;
7374

7475
try {
75-
const hash = typeof message === 'string' ? hashMessage(message) : message;
76+
const hash = typeof message === 'string' ? hashMessage(message, options.prefix) : message;
7677
const { signature, recoveryBytes } = recoverSignature({
7778
signature: options.signature,
7879
mode,
@@ -109,13 +110,14 @@ export const verifyMessageSignature = (options: {
109110
}
110111
};
111112

112-
export const verifyStructuredMessageSignature = (options: {
113+
const _verifyStructuredMessageSignature = (options: {
113114
message: StructuredSignatureRequestOptions['message'];
114115
domain: StructuredSignatureRequestOptions['domain'];
115116
signature: string;
116117
publicKey?: string;
117118
stxAddress?: string;
118119
mode?: 'vrs' | 'rsv';
120+
prefix?: Uint8Array;
119121
}) => {
120122
if (!options.publicKey && !options.stxAddress)
121123
throw Error(
@@ -129,11 +131,47 @@ export const verifyStructuredMessageSignature = (options: {
129131

130132
const hashBytes = makeStructuredDataHash(domain, message);
131133

132-
return verifyMessageSignature({
134+
return _verifyMessageSignature({
133135
message: hashBytes,
134136
signature: options.signature,
135137
publicKey: options.publicKey,
136138
stxAddress: options.stxAddress,
137139
mode: options.mode,
140+
prefix: options.prefix,
138141
});
139142
};
143+
144+
export const verifyMessageSignature = (options: {
145+
// string = message, bytes = hash
146+
message: string | Uint8Array;
147+
signature: string;
148+
publicKey?: string;
149+
stxAddress?: string;
150+
mode?: 'vrs' | 'rsv';
151+
}) => {
152+
const isValid = _verifyMessageSignature(options);
153+
return isValid
154+
? true
155+
: _verifyMessageSignature({
156+
...options,
157+
prefix: LEGACY_CHAIN_PREFIX_BYTES,
158+
});
159+
};
160+
161+
export const verifyStructuredMessageSignature = (options: {
162+
message: StructuredSignatureRequestOptions['message'];
163+
domain: StructuredSignatureRequestOptions['domain'];
164+
signature: string;
165+
publicKey?: string;
166+
stxAddress?: string;
167+
mode?: 'vrs' | 'rsv';
168+
}) => {
169+
const isValid = _verifyStructuredMessageSignature(options);
170+
return (
171+
isValid ??
172+
_verifyStructuredMessageSignature({
173+
...options,
174+
prefix: LEGACY_CHAIN_PREFIX_BYTES,
175+
})
176+
);
177+
};

0 commit comments

Comments
 (0)