From 5e9c9618546cd44596562cacd1633e5815c5c2dc Mon Sep 17 00:00:00 2001 From: Shjorty <201505261@post.au.dk> Date: Wed, 22 Mar 2023 11:49:30 +0100 Subject: [PATCH 1/3] signMessage and verifyMessageSignature now supports a binary message --- packages/common/CHANGELOG.md | 1 + packages/common/README.md | 2 ++ packages/common/src/signHelpers.ts | 20 +++++++----- packages/common/test/signHelpers.test.ts | 41 +++++++++++++----------- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index d37c5d939..38df07ac4 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -9,6 +9,7 @@ ### Changed - Bumped @concordium/rust-bindings to 0.11.0. (Includes a fix to serialization of negative numbers for smart contract values) +- `signMessage` and `verifyMessageSignature` can now handle the message being a buffer/Uint8Array instead of only a utf8 string. ## 6.3.0 2023-02-27 diff --git a/packages/common/README.md b/packages/common/README.md index 6c050f146..98e49b8ec 100644 --- a/packages/common/README.md +++ b/packages/common/README.md @@ -670,6 +670,8 @@ if (!verifyMessageSignature(message, signature, accountInfo)) { } ``` +The message can either be a utf8 encoded string or a Uint8Array directly containing the message bytes. + ## Deserialize smart contract types with only the specific type's schema The SDK exposes a general function to deserialize smart contract values from binary format to their JSON representation. In the previous sections the schema used was assumed to be the schema for an entire module, this function can be used with the schema containing only the specific type of the parameter, return value, event or error. diff --git a/packages/common/src/signHelpers.ts b/packages/common/src/signHelpers.ts index 3ef1c1a27..6d49e80d4 100644 --- a/packages/common/src/signHelpers.ts +++ b/packages/common/src/signHelpers.ts @@ -55,24 +55,28 @@ export function signTransaction( /** * @param account the address of the account that will sign this message. - * @param message the message to sign, assumed to be utf8 encoded. + * @param message the message to sign, assumed to be utf8 encoded string or a Uint8Array/buffer. */ -function getMessageDigest(account: AccountAddress, message: string): Buffer { +function getMessageDigest( + account: AccountAddress, + message: string | Uint8Array +): Buffer { const prepend = Buffer.alloc(8, 0); - const rawMessage = Buffer.from(message, 'utf8'); + const rawMessage = + typeof message === 'string' ? Buffer.from(message, 'utf8') : message; return sha256([account.decodedAddress, prepend, rawMessage]); } /** * Helper function to sign a message. - * Note that this function prepends the string "MyGoodPrepend" to ensure that the message is not a transaction. + * Note that this function prepends 8 zero-bytes to ensure that the message is not a transaction. * Note that the current prepend is temporary and will later be replaced. - * @param message the message to sign, assumed to be utf8 encoded. + * @param message the message to sign, assumed to be utf8 encoded string or a Uint8Array/buffer. * @param signer An object that handles the keys of the account, and performs the actual signing. */ export function signMessage( account: AccountAddress, - message: string, + message: string | Uint8Array, signer: AccountSigner ): Promise { return signer.sign(getMessageDigest(account, message)); @@ -80,12 +84,12 @@ export function signMessage( /** * Helper function to verify a signed message. - * @param message the message to sign, assumed to be utf8 encoded. + * @param message the message to sign, assumed to be utf8 encoded string or a Uint8Array/buffer. * @param signature the signature of a message, from a specific account. * @param accountInfo the address and credentials of the account */ export async function verifyMessageSignature( - message: string, + message: string | Uint8Array, signature: AccountTransactionSignature, accountInfo: Pick< AccountInfo, diff --git a/packages/common/test/signHelpers.test.ts b/packages/common/test/signHelpers.test.ts index 32fc731f4..465ad90dc 100644 --- a/packages/common/test/signHelpers.test.ts +++ b/packages/common/test/signHelpers.test.ts @@ -24,11 +24,12 @@ const TEST_CREDENTIALS = { }, }; -test('test signMessage', async () => { +const testEachMessageType = test.each(['test', Buffer.from('test', 'utf8')]); + +testEachMessageType('[%o] test signMessage', async (message) => { const account = new AccountAddress( '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5' ); - const message = 'test'; const signature = await signMessage( account, message, @@ -41,24 +42,26 @@ test('test signMessage', async () => { ); }); -test('verifyMessageSignature returns true on the correct address/signature', async () => { - const message = 'test'; - const signature = await verifyMessageSignature( - message, - { - 0: { - 0: '445197d79ca90d8cc8440328dac9f307932ade0c03cc7aa575b59b746e26e5f1bca13ade5ff7a56e918ba5a32450fdf52b034cd2580929b21213263e81f7f809', +testEachMessageType( + '[%o] verifyMessageSignature returns true on the correct address/signature', + async (message) => { + const signature = await verifyMessageSignature( + message, + { + 0: { + 0: '445197d79ca90d8cc8440328dac9f307932ade0c03cc7aa575b59b746e26e5f1bca13ade5ff7a56e918ba5a32450fdf52b034cd2580929b21213263e81f7f809', + }, }, - }, - { - accountAddress: - '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5', - accountThreshold: 1, - accountCredentials: TEST_CREDENTIALS, - } as unknown as AccountInfo - ); - expect(signature).toBeTruthy(); -}); + { + accountAddress: + '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5', + accountThreshold: 1, + accountCredentials: TEST_CREDENTIALS, + } as unknown as AccountInfo + ); + expect(signature).toBeTruthy(); + } +); test('verifyMessageSignature returns false on the incorrect address', async () => { const message = 'test'; From cf9bc5b7c6b75a5ac036c0df04f60173946cf3e7 Mon Sep 17 00:00:00 2001 From: Shjorty <201505261@post.au.dk> Date: Wed, 22 Mar 2023 13:53:37 +0100 Subject: [PATCH 2/3] Fix serializeTypeValue returning empty string instead of throwing an error --- packages/common/CHANGELOG.md | 4 ++++ packages/common/src/serialization.ts | 8 +------- packages/common/test/serialization.test.ts | 4 ++++ packages/rust-bindings/src/external_functions.rs | 5 +++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 38df07ac4..fdd3e7353 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -11,6 +11,10 @@ - Bumped @concordium/rust-bindings to 0.11.0. (Includes a fix to serialization of negative numbers for smart contract values) - `signMessage` and `verifyMessageSignature` can now handle the message being a buffer/Uint8Array instead of only a utf8 string. +### Fixed + +- `serializeTypeValue` now reports an error when called with invalid data, such as a receive function with missing schema, or a schema that cannot be parsed. + ## 6.3.0 2023-02-27 ### Added diff --git a/packages/common/src/serialization.ts b/packages/common/src/serialization.ts index dcec536b7..8be91c1ac 100644 --- a/packages/common/src/serialization.ts +++ b/packages/common/src/serialization.ts @@ -518,13 +518,7 @@ export function serializeTypeValue( JSON.stringify(value), rawSchema.toString('hex') ); - try { - return Buffer.from(serializedValue, 'hex'); - } catch (e) { - throw new Error( - 'unable to deserialize value, due to: ' + serializedValue - ); // In this case serializedValue is the error message from the rust module - } + return Buffer.from(serializedValue, 'hex'); } /** diff --git a/packages/common/test/serialization.test.ts b/packages/common/test/serialization.test.ts index 7d7714d78..970fada8e 100644 --- a/packages/common/test/serialization.test.ts +++ b/packages/common/test/serialization.test.ts @@ -169,3 +169,7 @@ test('serialize type value and serializeUpdateContractParameters give same resul serializedType.toString('hex') ); }); + +test('serializeTypeValue throws an error if unable to serialize', () => { + expect(() => serializeTypeValue('test', Buffer.alloc(0))).toThrow(); +}); diff --git a/packages/rust-bindings/src/external_functions.rs b/packages/rust-bindings/src/external_functions.rs index ec723cc83..e5adaf715 100644 --- a/packages/rust-bindings/src/external_functions.rs +++ b/packages/rust-bindings/src/external_functions.rs @@ -162,8 +162,9 @@ pub fn get_init_contract_parameter_schema_ext( } #[wasm_bindgen(js_name = serializeTypeValue)] -pub fn serialize_type_value_ext(value: JsonString, value_type: HexString) -> HexString { - error_to_string(serialize_type_value_aux(value, value_type)) +pub fn serialize_type_value_ext(value: JsonString, schema: HexString) -> Result { + serialize_type_value_aux(value, schema) + .map_err(|e| format!("Unable to serialize value due to: {}", e)) } #[wasm_bindgen(js_name = createIdRequestV1)] From 619eb74aa16d52609a64176879cb7e5d1d55ef6c Mon Sep 17 00:00:00 2001 From: Shjorty <201505261@post.au.dk> Date: Thu, 23 Mar 2023 10:36:51 +0100 Subject: [PATCH 3/3] Fix incorrect comment on signMessage --- packages/common/src/signHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/signHelpers.ts b/packages/common/src/signHelpers.ts index 6d49e80d4..5c5324358 100644 --- a/packages/common/src/signHelpers.ts +++ b/packages/common/src/signHelpers.ts @@ -69,7 +69,7 @@ function getMessageDigest( /** * Helper function to sign a message. - * Note that this function prepends 8 zero-bytes to ensure that the message is not a transaction. + * Note that this function prepends the account address (32 bytes) and 8 zero-bytes to ensure that the message is not a transaction. * Note that the current prepend is temporary and will later be replaced. * @param message the message to sign, assumed to be utf8 encoded string or a Uint8Array/buffer. * @param signer An object that handles the keys of the account, and performs the actual signing.