diff --git a/client-sdk/ts-web/rt/package.json b/client-sdk/ts-web/rt/package.json index 1dd4e34eb1..7caab7e390 100644 --- a/client-sdk/ts-web/rt/package.json +++ b/client-sdk/ts-web/rt/package.json @@ -17,6 +17,7 @@ "@oasisprotocol/client": "^0.1.0-alpha9", "elliptic": "^6.5.3", "js-sha512": "^0.8.0", + "randombytes": "^2.0.1", "sha3": "^2.1.4", "tweetnacl": "^1.0.3" }, diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index c934f256c3..384cda156a 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -1,7 +1,12 @@ import * as oasis from '@oasisprotocol/client'; +// @ts-expect-error missing declaration +import * as deoxysii from 'deoxysii'; +import * as nacl from 'tweetnacl'; +// @ts-expect-error missing declaration +import * as randomBytes from 'randombytes'; +import * as mraeDeoxysii from './mrae/deoxysii'; import * as transaction from './transaction'; -import * as mrae from './mrae'; import * as types from './types'; /** @@ -27,17 +32,17 @@ export interface MetaEncryptedX25519DeoxysII { } /** - * encodeCall encodes a call based on its configured call format. + * encodeCallWithNonceAndKeys encodes a call based on its configured call format. * It returns the encoded call and any metadata needed to successfully decode the result. */ -export async function encodeCall( +export function encodeCallWithNonceAndKeys( nonce: Uint8Array, sk: Uint8Array, pk: Uint8Array, call: types.Call, format: types.CallFormat, config?: EncodeConfig, -): Promise<[types.Call, unknown]> { +): [types.Call, unknown] { switch (format) { case transaction.CALLFORMAT_PLAIN: return [call, undefined]; @@ -46,7 +51,7 @@ export async function encodeCall( throw new Error('callformat: runtime call data public key not set'); } const rawCall = oasis.misc.toCBOR(call); - const sealedCall = mrae.boxSeal(nonce, rawCall, null, config.publicKey.key, sk); + const sealedCall = mraeDeoxysii.boxSeal(nonce, rawCall, null, config.publicKey.key, sk); const envelope: types.CallEnvelopeX25519DeoxysII = { pk: pk, nonce: nonce, @@ -67,27 +72,61 @@ export async function encodeCall( } } +/** + * encodeCall randomly generates nonce and keyPair and then call encodeCallWithNonceAndKeys + * It returns the encoded call and any metadata needed to successfully decode the result. + */ +export function encodeCall( + call: types.Call, + format: types.CallFormat, + config?: EncodeConfig, +): [types.Call, unknown] { + const nonce = randomBytes(deoxysii.NonceSize); + const keyPair = nacl.box.keyPair(); + return encodeCallWithNonceAndKeys( + nonce, + keyPair.secretKey, + keyPair.publicKey, + call, + format, + config, + ); +} + /** * decodeResult performs result decoding based on the specified call format metadata. */ export function decodeResult( result: types.CallResult, + format: types.CallFormat, meta?: MetaEncryptedX25519DeoxysII, ): types.CallResult { - if (meta === undefined) { - // In case of plain-text data format, we simply pass on the result unchanged. - return result; - } - - if (result.unknown) { - const envelop = oasis.misc.fromCBOR(result.unknown) as types.ResultEnvelopeX25519DeoxysII; - const pt = mrae.boxOpen(envelop.nonce, envelop.data, null, meta.pk, meta.sk); - return oasis.misc.fromCBOR(pt) as types.CallResult; - } else if (result.fail) { - throw new Error( - `callformat: failed call: module :${result.fail.module} code: ${result.fail.code} message: ${result.fail.message}`, - ); + switch (format) { + case transaction.CALLFORMAT_PLAIN: + // In case of plain-text data format, we simply pass on the result unchanged. + return result; + case transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII: + if (result.unknown) { + const envelop = oasis.misc.fromCBOR( + result.unknown, + ) as types.ResultEnvelopeX25519DeoxysII; + const pt = mraeDeoxysii.boxOpen( + envelop.nonce, + envelop.data, + null, + meta.pk, + meta.sk, + ); + return oasis.misc.fromCBOR(pt) as types.CallResult; + } else if (result.fail) { + throw new Error( + `callformat: failed call: module :${result.fail.module} code: ${result.fail.code} message: ${result.fail.message}`, + ); + } + throw Object.assign(new Error(`callformat: unexpected result: ${result.ok}`), { + source: result, + }); + default: + throw new Error(`callformat: unsupported call format: ${format}`); } - - throw new Error(`callformat: unexpected result: ${result.ok}`); } diff --git a/client-sdk/ts-web/rt/src/index.ts b/client-sdk/ts-web/rt/src/index.ts index 1ae2781bcf..0b5454fdd2 100644 --- a/client-sdk/ts-web/rt/src/index.ts +++ b/client-sdk/ts-web/rt/src/index.ts @@ -7,7 +7,7 @@ export * as contracts from './contracts'; export * as core from './core'; export * as event from './event'; export * as evm from './evm'; -export * as mrae from './mrae'; +export * as mraeDeoxysii from './mrae/deoxysii'; export * as rewards from './rewards'; export * as signatureSecp256k1 from './signature_secp256k1'; export * as token from './token'; diff --git a/client-sdk/ts-web/rt/src/mrae.ts b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts similarity index 100% rename from client-sdk/ts-web/rt/src/mrae.ts rename to client-sdk/ts-web/rt/src/mrae/deoxysii.ts index 4407c4cfbd..d408fbe38a 100644 --- a/client-sdk/ts-web/rt/src/mrae.ts +++ b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts @@ -1,8 +1,8 @@ import * as oasis from '@oasisprotocol/client'; -import * as nacl from 'tweetnacl'; -import {sha512_256} from 'js-sha512'; // @ts-expect-error missing declaration import * as deoxysii from 'deoxysii'; +import {sha512_256} from 'js-sha512'; +import * as nacl from 'tweetnacl'; const BOX_KDF_TWEAK = 'MRAE_Box_Deoxys-II-256-128'; diff --git a/client-sdk/ts-web/rt/test/mrae.test.ts b/client-sdk/ts-web/rt/test/mrae.test.ts index b553fb9625..9f78df03a4 100644 --- a/client-sdk/ts-web/rt/test/mrae.test.ts +++ b/client-sdk/ts-web/rt/test/mrae.test.ts @@ -1,17 +1,16 @@ import * as oasisRT from './../src'; -import * as ed from '@noble/ed25519'; import * as oasis from '@oasisprotocol/client'; import * as nacl from 'tweetnacl'; describe('mrae', () => { describe('symmetricKey', () => { - it('Should drive symmetric key correctly', async () => { - const privateKeyHex = "c07b151fbc1e7a11dff926111188f8d872f62eba0396da97c0a24adb75161750"; + it('Should drive symmetric key correctly', () => { + const privateKeyHex = "c07b151fbc1e7a11dff926111188f8d872f62eba0396da97c0a24adb75161750"; const privateKey = oasis.misc.fromHex(privateKeyHex); - const publicKey = ed.curve25519.scalarMultBase(privateKey); + const publicKey = nacl.scalarMult.base(privateKey); expect(oasis.misc.toHex(publicKey)).toEqual("3046db3fa70ce605457dc47c48837ebd8bd0a26abfde5994d033e1ced68e2576"); - const sharedKey = oasisRT.mrae.deriveSymmetricKey(publicKey, privateKey); - expect(oasis.misc.toHex(sharedKey)).toEqual("e69ac21066a8c2284e8fdc690e579af4513547b9b31dd144792c1904b45cf586"); + const sharedKey = oasisRT.mraeDeoxysii.deriveSymmetricKey(publicKey, privateKey); + expect(oasis.misc.toHex(sharedKey)).toEqual("e69ac21066a8c2284e8fdc690e579af4513547b9b31dd144792c1904b45cf586"); }); }); });