From 1fde8c2ecc0edb98a37e055d7d66071bea543266 Mon Sep 17 00:00:00 2001 From: Warren He Date: Tue, 15 Oct 2024 17:28:43 -0700 Subject: [PATCH 01/13] ts-web/rt: restore async callformat encode/decode --- client-sdk/ts-web/rt/src/callformat.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 793721f231..29e3554b98 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -32,14 +32,14 @@ export interface MetaEncryptedX25519DeoxysII { * 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 function encodeCallWithNonceAndKeys( +export async function encodeCallWithNonceAndKeys( nonce: Uint8Array, sk: Uint8Array, pk: Uint8Array, call: types.Call, format: types.CallFormat, config?: EncodeConfig, -): [types.Call, unknown] { +): Promise<[types.Call, unknown]> { switch (format) { case transaction.CALLFORMAT_PLAIN: return [call, undefined]; @@ -80,15 +80,15 @@ export function encodeCallWithNonceAndKeys( * 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( +export async function encodeCall( call: types.Call, format: types.CallFormat, config?: EncodeConfig, -): [types.Call, unknown] { +): Promise<[types.Call, unknown]> { const nonce = new Uint8Array(deoxysii.NonceSize); crypto.getRandomValues(nonce); const keyPair = nacl.box.keyPair(); - return encodeCallWithNonceAndKeys( + return await encodeCallWithNonceAndKeys( nonce, keyPair.secretKey, keyPair.publicKey, @@ -101,11 +101,11 @@ export function encodeCall( /** * decodeResult performs result decoding based on the specified call format metadata. */ -export function decodeResult( +export async function decodeResult( result: types.CallResult, format: types.CallFormat, meta?: MetaEncryptedX25519DeoxysII, -): types.CallResult { +): Promise { switch (format) { case transaction.CALLFORMAT_PLAIN: // In case of plain-text data format, we simply pass on the result unchanged. From c4207cee71546a8f91766b8c77e6c1941fbeeab8 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 13:27:45 -0700 Subject: [PATCH 02/13] ts-web/rt: mrae web crypto implementation, async --- client-sdk/ts-web/rt/src/mrae/deoxysii.ts | 73 ++++++++++++++++++----- client-sdk/ts-web/rt/test/mrae.test.ts | 21 +++++-- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/client-sdk/ts-web/rt/src/mrae/deoxysii.ts b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts index 69f11e6731..c374edd457 100644 --- a/client-sdk/ts-web/rt/src/mrae/deoxysii.ts +++ b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts @@ -1,17 +1,62 @@ import {hmac} from '@noble/hashes/hmac'; import {sha512_256} from '@noble/hashes/sha512'; +import * as oasis from '@oasisprotocol/client'; import * as deoxysii from '@oasisprotocol/deoxysii'; -import * as nacl from 'tweetnacl'; const BOX_KDF_TWEAK = 'MRAE_Box_Deoxys-II-256-128'; +export async function generateKeyPair(extractable: boolean): Promise { + return (await crypto.subtle.generateKey({name: 'X25519'}, extractable, [ + 'deriveBits', + ])) as CryptoKeyPair; +} + +export async function keyPairFromPrivateKey(privateKey: Uint8Array): Promise { + const privateDER = oasis.misc.concat( + new Uint8Array([ + // PrivateKeyInfo + 0x30, 0x2e, + // version 0 + 0x02, 0x01, 0x00, + // privateKeyAlgorithm 1.3.101.110 + 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, + // privateKey + 0x04, 0x22, 0x04, 0x20, + ]), + privateKey, + ); + const privateCK = await crypto.subtle.importKey('pkcs8', privateDER, {name: 'X25519'}, true, [ + 'deriveBits', + ]); + const privateJWK = await crypto.subtle.exportKey('jwk', privateCK); + const publicJWK = { + kty: privateJWK.kty, + crv: privateJWK.crv, + x: privateJWK.x, + } as JsonWebKey; + const publicCK = await crypto.subtle.importKey('jwk', publicJWK, {name: 'X25519'}, true, []); + return { + publicKey: publicCK, + privateKey: privateCK, + } as CryptoKeyPair; +} + +export async function publicKeyFromKeyPair(keyPair: CryptoKeyPair): Promise { + return new Uint8Array(await crypto.subtle.exportKey('raw', keyPair.publicKey)); +} + /** * deriveSymmetricKey derives a MRAE AEAD symmetric key suitable for use with the asymmetric * box primitives from the provided X25519 public and private keys. */ - -export function deriveSymmetricKey(publicKey: Uint8Array, privateKey: Uint8Array): Uint8Array { - const pmk = nacl.scalarMult(privateKey, publicKey); +export async function deriveSymmetricKey( + publicKey: Uint8Array, + privateCK: CryptoKey, +): Promise { + const publicCK = await crypto.subtle.importKey('raw', publicKey, {name: 'X25519'}, true, []); + const pmk = new Uint8Array( + await crypto.subtle.deriveBits({name: 'X25519', public: publicCK}, privateCK, 256), + ); return hmac(sha512_256, BOX_KDF_TWEAK, pmk); } @@ -20,15 +65,15 @@ export function deriveSymmetricKey(publicKey: Uint8Array, privateKey: Uint8Array * Deoxys-II-256-128 using a symmetric key derived from the provided * X25519 public and private keys. */ -export function boxSeal( +export async function boxSeal( nonce: Uint8Array, plainText: Uint8Array, associateData: Uint8Array, publicKey: Uint8Array, - privateKey: Uint8Array, -): Uint8Array { - const sharedKey = deriveSymmetricKey(publicKey, privateKey); - var aead = new deoxysii.AEAD(sharedKey); + privateCK: CryptoKey, +): Promise { + const sharedKey = await deriveSymmetricKey(publicKey, privateCK); + const aead = new deoxysii.AEAD(sharedKey); return aead.encrypt(nonce, plainText, associateData); } @@ -37,14 +82,14 @@ export function boxSeal( * Deoxys-II-256-128 using a symmetric key derived from the provided * X25519 public and private keys. */ -export function boxOpen( +export async function boxOpen( nonce: Uint8Array, ciperText: Uint8Array, associateData: Uint8Array, publicKey: Uint8Array, - privateKey: Uint8Array, -): Uint8Array { - const sharedKey = deriveSymmetricKey(publicKey, privateKey); - var aead = new deoxysii.AEAD(sharedKey); + privateCK: CryptoKey, +): Promise { + const sharedKey = await deriveSymmetricKey(publicKey, privateCK); + const aead = new deoxysii.AEAD(sharedKey); return aead.decrypt(nonce, ciperText, associateData); } diff --git a/client-sdk/ts-web/rt/test/mrae.test.ts b/client-sdk/ts-web/rt/test/mrae.test.ts index ce7af7701b..c901df2973 100644 --- a/client-sdk/ts-web/rt/test/mrae.test.ts +++ b/client-sdk/ts-web/rt/test/mrae.test.ts @@ -1,18 +1,29 @@ -import * as oasisRT from './../src'; +import {webcrypto} from 'crypto'; + import * as oasis from '@oasisprotocol/client'; -import * as nacl from 'tweetnacl'; + +import * as oasisRT from './../src'; + +if (typeof crypto === 'undefined') { + // @ts-expect-error there are some inconsequential type differences + globalThis.crypto = webcrypto; +} describe('mrae', () => { describe('symmetricKey', () => { - it('Should drive symmetric key correctly', () => { + it('Should drive symmetric key correctly', async () => { const privateKeyHex = 'c07b151fbc1e7a11dff926111188f8d872f62eba0396da97c0a24adb75161750'; const privateKey = oasis.misc.fromHex(privateKeyHex); - const publicKey = nacl.scalarMult.base(privateKey); + const keyPair = await oasisRT.mraeDeoxysii.keyPairFromPrivateKey(privateKey); + const publicKey = await oasisRT.mraeDeoxysii.publicKeyFromKeyPair(keyPair); expect(oasis.misc.toHex(publicKey)).toEqual( '3046db3fa70ce605457dc47c48837ebd8bd0a26abfde5994d033e1ced68e2576', ); - const sharedKey = oasisRT.mraeDeoxysii.deriveSymmetricKey(publicKey, privateKey); + const sharedKey = await oasisRT.mraeDeoxysii.deriveSymmetricKey( + publicKey, + keyPair.privateKey, + ); expect(oasis.misc.toHex(sharedKey)).toEqual( 'e69ac21066a8c2284e8fdc690e579af4513547b9b31dd144792c1904b45cf586', ); From 11ced12f4d31aab8c89f910cee0065248e06268c Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 13:53:15 -0700 Subject: [PATCH 03/13] ts-web/rt: callformat web crypto changes --- client-sdk/ts-web/rt/src/callformat.ts | 23 +++++++------------- client-sdk/ts-web/rt/test/callformat.test.ts | 13 ++++++----- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 29e3554b98..72be085b10 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -1,6 +1,5 @@ import * as oasis from '@oasisprotocol/client'; import * as deoxysii from '@oasisprotocol/deoxysii'; -import * as nacl from 'tweetnacl'; import * as mraeDeoxysii from './mrae/deoxysii'; import * as transaction from './transaction'; @@ -24,7 +23,7 @@ export interface EncodeConfig { } export interface MetaEncryptedX25519DeoxysII { - sk: Uint8Array; + sk: CryptoKey; pk: Uint8Array; } @@ -34,8 +33,7 @@ export interface MetaEncryptedX25519DeoxysII { */ export async function encodeCallWithNonceAndKeys( nonce: Uint8Array, - sk: Uint8Array, - pk: Uint8Array, + clientKP: CryptoKeyPair, call: types.Call, format: types.CallFormat, config?: EncodeConfig, @@ -47,9 +45,11 @@ export async function encodeCallWithNonceAndKeys( if (config?.publicKey === undefined) { throw new Error('callformat: runtime call data public key not set'); } + const pk = await mraeDeoxysii.publicKeyFromKeyPair(clientKP); + const sk = clientKP.privateKey; const rawCall = oasis.misc.toCBOR(call); const zeroBuffer = new Uint8Array(0); - const sealedCall = mraeDeoxysii.boxSeal( + const sealedCall = await mraeDeoxysii.boxSeal( nonce, rawCall, zeroBuffer, @@ -87,15 +87,8 @@ export async function encodeCall( ): Promise<[types.Call, unknown]> { const nonce = new Uint8Array(deoxysii.NonceSize); crypto.getRandomValues(nonce); - const keyPair = nacl.box.keyPair(); - return await encodeCallWithNonceAndKeys( - nonce, - keyPair.secretKey, - keyPair.publicKey, - call, - format, - config, - ); + const clientKP = await mraeDeoxysii.generateKeyPair(true); + return await encodeCallWithNonceAndKeys(nonce, clientKP, call, format, config); } /** @@ -117,7 +110,7 @@ export async function decodeResult( result.unknown, ) as types.ResultEnvelopeX25519DeoxysII; const zeroBuffer = new Uint8Array(0); - const pt = mraeDeoxysii.boxOpen( + const pt = await mraeDeoxysii.boxOpen( envelop?.nonce, envelop?.data, zeroBuffer, diff --git a/client-sdk/ts-web/rt/test/callformat.test.ts b/client-sdk/ts-web/rt/test/callformat.test.ts index 04c06bb3fe..b614710f59 100644 --- a/client-sdk/ts-web/rt/test/callformat.test.ts +++ b/client-sdk/ts-web/rt/test/callformat.test.ts @@ -1,18 +1,21 @@ -import * as oasisRT from './../src'; -import * as nacl from 'tweetnacl'; import {webcrypto} from 'crypto'; +import * as oasis from '@oasisprotocol/client'; +import * as deoxysii from '@oasisprotocol/deoxysii'; + +import * as oasisRT from './../src'; + if (typeof crypto === 'undefined') { // @ts-expect-error there are some inconsequential type differences globalThis.crypto = webcrypto; } describe('callformat', () => { - describe('encodeCall/DecodeResult', () => { + describe('encodeCall/decodeResult', () => { it('Should encode and decode the message correctly', async () => { const message = 'I will find some random message here'; - const pairs = nacl.box.keyPair(); - const publicKey = pairs.publicKey; + const runtimeKP = await oasisRT.mraeDeoxysii.generateKeyPair(true); + const publicKey = await oasisRT.mraeDeoxysii.publicKeyFromKeyPair(runtimeKP); const rawCall: oasisRT.types.Call = { format: oasisRT.transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, method: '', From 88349f2e26ff5083ab89954fa547251d0ada70f1 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 13:54:06 -0700 Subject: [PATCH 04/13] ts-web/rt: callformat decodeResult match meta type from encodeCall --- client-sdk/ts-web/rt/src/callformat.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 72be085b10..2c3924bc9f 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -97,7 +97,7 @@ export async function encodeCall( export async function decodeResult( result: types.CallResult, format: types.CallFormat, - meta?: MetaEncryptedX25519DeoxysII, + meta?: unknown, ): Promise { switch (format) { case transaction.CALLFORMAT_PLAIN: @@ -106,6 +106,7 @@ export async function decodeResult( case transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII: if (result.unknown) { if (meta) { + const metaEncryptedX25519DeoxysII = meta as MetaEncryptedX25519DeoxysII; const envelop = oasis.misc.fromCBOR( result.unknown, ) as types.ResultEnvelopeX25519DeoxysII; @@ -114,8 +115,8 @@ export async function decodeResult( envelop?.nonce, envelop?.data, zeroBuffer, - meta.pk, - meta.sk, + metaEncryptedX25519DeoxysII.pk, + metaEncryptedX25519DeoxysII.sk, ); return oasis.misc.fromCBOR(pt) as types.CallResult; } else { From d01af2193efdf59a072eeeaee5757c59c389e520 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 13:59:12 -0700 Subject: [PATCH 05/13] ts-web/rt: add callformat interop test --- client-sdk/go/callformat/callformat_test.go | 3 +- client-sdk/ts-web/rt/test/callformat.test.ts | 47 ++++++++++++++++++++ runtime-sdk/src/callformat.rs | 3 +- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/client-sdk/go/callformat/callformat_test.go b/client-sdk/go/callformat/callformat_test.go index 7700a064c0..19457f6306 100644 --- a/client-sdk/go/callformat/callformat_test.go +++ b/client-sdk/go/callformat/callformat_test.go @@ -30,7 +30,8 @@ func TestInterop(t *testing.T) { } callEnc, metadata := encodeCallEncryptedX25519DeoxysII(&call, clientPK, &clientSK, nonce, &cfg) - // If these change, update runtime-sdk/src/callformat.rs too. + // If these change, update runtime-sdk/src/callformat.rs and + // client-sdk/ts-web/rt/test/callformat.test.ts too. require.Equal(t, "a264626f6479f6666d6574686f64646d6f636b", hex.EncodeToString(cbor.Marshal(call))) require.Equal(t, "a264626f6479a462706b5820eedc75d3c500fc1b2d321757c383e276ab705c5a02013b3f1966e9caf73cdb0264646174615823c4635f2f9496a033a578e3f1e007be5d6cfa9631fb2fe2c8c76d26b322b6afb2fa5cdf6565706f636801656e6f6e63654f00000000000000000000000000000066666f726d617401", hex.EncodeToString(cbor.Marshal(callEnc))) diff --git a/client-sdk/ts-web/rt/test/callformat.test.ts b/client-sdk/ts-web/rt/test/callformat.test.ts index b614710f59..39fb2703a0 100644 --- a/client-sdk/ts-web/rt/test/callformat.test.ts +++ b/client-sdk/ts-web/rt/test/callformat.test.ts @@ -1,5 +1,6 @@ import {webcrypto} from 'crypto'; +import {sha512_256} from '@noble/hashes/sha512'; import * as oasis from '@oasisprotocol/client'; import * as deoxysii from '@oasisprotocol/deoxysii'; @@ -48,4 +49,50 @@ describe('callformat', () => { expect(decodedResult.body).toEqual(message); }); }); + it('Should interop', async () => { + const clientSK = sha512_256('callformat test client'); + const clientKP = await oasisRT.mraeDeoxysii.keyPairFromPrivateKey(clientSK); + const runtimeSK = sha512_256('callformat test runtime'); + const runtimeKP = await oasisRT.mraeDeoxysii.keyPairFromPrivateKey(runtimeSK); + const runtimePK = await oasisRT.mraeDeoxysii.publicKeyFromKeyPair(runtimeKP); + + const call = { + method: 'mock', + body: null, + } as oasisRT.types.Call; + const nonce = new Uint8Array(deoxysii.NonceSize); + const config = { + publicKey: {key: runtimePK}, + } as oasisRT.callformat.EncodeConfig; + const [callEnc, meta] = await oasisRT.callformat.encodeCallWithNonceAndKeys( + nonce, + clientKP, + call, + oasisRT.transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, + config, + ); + + expect(oasis.misc.toHex(oasis.misc.toCBOR(call))).toEqual( + 'a264626f6479f6666d6574686f64646d6f636b', + ); + expect(oasis.misc.toHex(oasis.misc.toCBOR(callEnc))).toEqual( + 'a264626f6479a462706b5820eedc75d3c500fc1b2d321757c383e276ab705c5a02013b3f1966e9caf73cdb0264646174615823c4635f2f9496a033a578e3f1e007be5d6cfa9631fb2fe2c8c76d26b322b6afb2fa5cdf6565706f636801656e6f6e63654f00000000000000000000000000000066666f726d617401', + ); + + const result = oasis.misc.fromCBOR( + oasis.misc.fromHex('a1626f6bf6'), + ) as oasisRT.types.CallResult; + const resultEnc = oasis.misc.fromCBOR( + oasis.misc.fromHex( + 'a167756e6b6e6f776ea264646174615528d1c5eedc5e54e1ef140ba905e84e0bea8daf60af656e6f6e63654f000000000000000000000000000000', + ), + ) as oasisRT.types.CallResult; + + const resultOurs = await oasisRT.callformat.decodeResult( + resultEnc, + oasisRT.transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, + meta, + ); + expect(resultOurs).toEqual(result); + }); }); diff --git a/runtime-sdk/src/callformat.rs b/runtime-sdk/src/callformat.rs index 351f9f278c..6e75abb75a 100644 --- a/runtime-sdk/src/callformat.rs +++ b/runtime-sdk/src/callformat.rs @@ -391,7 +391,8 @@ mod test { let result = types::transaction::CallResult::from(result_m_g()); let result_enc = callformat::encode_result(&ctx, result_m_g(), metadata); - // If these change, update client-sdk/go/callformat/callformat_test.go too. + // If these change, update client-sdk/go/callformat/callformat_test.go + // and client-sdk/ts-web/rt/test/callformat.test.ts too. assert_eq!(hex::encode(cbor::to_vec(result)), "a1626f6bf6"); assert_eq!(hex::encode(cbor::to_vec(result_enc)), "a167756e6b6e6f776ea264646174615528d1c5eedc5e54e1ef140ba905e84e0bea8daf60af656e6f6e63654f000000000000000000000000000000"); } From 3bfd7a699ee145743d47fc007f9ffc86af3e973d Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:42:06 -0700 Subject: [PATCH 06/13] ts-web/rt: add CallEnvelopeX25519DeoxysII.epoch --- client-sdk/ts-web/rt/src/callformat.ts | 7 +++++++ client-sdk/ts-web/rt/src/types.ts | 1 + client-sdk/ts-web/rt/test/callformat.test.ts | 1 + 3 files changed, 9 insertions(+) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 2c3924bc9f..a06f95c4ef 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -20,6 +20,10 @@ export interface EncodeConfig { * publicKey is an optional runtime's call data public key to use for encrypted call formats. */ publicKey?: types.KeyManagerSignedPublicKey; + /** + * epoch is the epoch of the ephemeral runtime key (when publicKey is set). + */ + epoch?: oasis.types.longnum; } export interface MetaEncryptedX25519DeoxysII { @@ -61,6 +65,9 @@ export async function encodeCallWithNonceAndKeys( nonce: nonce, data: sealedCall, }; + if (config.epoch) { + envelope.epoch = config.epoch; + } const encoded: types.Call = { format: transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, method: '', diff --git a/client-sdk/ts-web/rt/src/types.ts b/client-sdk/ts-web/rt/src/types.ts index 4da799d0ca..e1cfb69441 100644 --- a/client-sdk/ts-web/rt/src/types.ts +++ b/client-sdk/ts-web/rt/src/types.ts @@ -466,6 +466,7 @@ export interface EVMLogEvent { export interface CallEnvelopeX25519DeoxysII { pk: Uint8Array; nonce: Uint8Array; + epoch?: oasis.types.longnum; data: Uint8Array; } diff --git a/client-sdk/ts-web/rt/test/callformat.test.ts b/client-sdk/ts-web/rt/test/callformat.test.ts index 39fb2703a0..9e3975b09e 100644 --- a/client-sdk/ts-web/rt/test/callformat.test.ts +++ b/client-sdk/ts-web/rt/test/callformat.test.ts @@ -63,6 +63,7 @@ describe('callformat', () => { const nonce = new Uint8Array(deoxysii.NonceSize); const config = { publicKey: {key: runtimePK}, + epoch: 1, } as oasisRT.callformat.EncodeConfig; const [callEnc, meta] = await oasisRT.callformat.encodeCallWithNonceAndKeys( nonce, From 9f6352b5c3e122e1e97f646fd8e4f9575875a8dc Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:43:25 -0700 Subject: [PATCH 07/13] ts-web/rt: mark Call.method optional --- client-sdk/ts-web/rt/src/transaction.ts | 4 ++-- client-sdk/ts-web/rt/src/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client-sdk/ts-web/rt/src/transaction.ts b/client-sdk/ts-web/rt/src/transaction.ts index ffb05afd53..5e1c56091d 100644 --- a/client-sdk/ts-web/rt/src/transaction.ts +++ b/client-sdk/ts-web/rt/src/transaction.ts @@ -164,8 +164,8 @@ export type CallHandler = (body: BODY) => void; export type CallHandlers = {[method: string]: CallHandler}; export function visitCall(handlers: CallHandlers, call: types.Call) { - if (handlers[call.method]) { - handlers[call.method](call.body as never); + if (handlers[call.method!]) { + handlers[call.method!](call.body as never); return true; } return false; diff --git a/client-sdk/ts-web/rt/src/types.ts b/client-sdk/ts-web/rt/src/types.ts index e1cfb69441..2d4690630e 100644 --- a/client-sdk/ts-web/rt/src/types.ts +++ b/client-sdk/ts-web/rt/src/types.ts @@ -241,7 +241,7 @@ export type CallFormat = number; */ export interface Call { format?: CallFormat; - method: string; + method?: string; body: unknown; ro?: boolean; } From 4d121a88d47c9b925a25dce5972430fa827c93cd Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:47:07 -0700 Subject: [PATCH 08/13] ts-web/rt: callformat use absent method to match go client --- client-sdk/ts-web/rt/src/callformat.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index a06f95c4ef..960a42171b 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -70,7 +70,6 @@ export async function encodeCallWithNonceAndKeys( } const encoded: types.Call = { format: transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, - method: '', body: oasis.misc.toCBOR(envelope), }; const meta: MetaEncryptedX25519DeoxysII = { From 94686d366cf782fa92a84e38b10bcdbe0943211e Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:45:00 -0700 Subject: [PATCH 09/13] ts-web/rt: callformat fix extra encoding of body --- client-sdk/ts-web/rt/src/callformat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 960a42171b..227a68f710 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -70,7 +70,7 @@ export async function encodeCallWithNonceAndKeys( } const encoded: types.Call = { format: transaction.CALLFORMAT_ENCRYPTED_X25519DEOXYSII, - body: oasis.misc.toCBOR(envelope), + body: envelope, }; const meta: MetaEncryptedX25519DeoxysII = { sk: sk, From 74bef2ee89168e17e5dbe19d0c7ab93e016231a6 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:48:34 -0700 Subject: [PATCH 10/13] ts-web/rt: correct CallResult.unknown type --- client-sdk/ts-web/rt/src/callformat.ts | 8 +++----- client-sdk/ts-web/rt/src/types.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client-sdk/ts-web/rt/src/callformat.ts b/client-sdk/ts-web/rt/src/callformat.ts index 227a68f710..97dbb35847 100644 --- a/client-sdk/ts-web/rt/src/callformat.ts +++ b/client-sdk/ts-web/rt/src/callformat.ts @@ -113,13 +113,11 @@ export async function decodeResult( if (result.unknown) { if (meta) { const metaEncryptedX25519DeoxysII = meta as MetaEncryptedX25519DeoxysII; - const envelop = oasis.misc.fromCBOR( - result.unknown, - ) as types.ResultEnvelopeX25519DeoxysII; + const envelope = result.unknown as types.ResultEnvelopeX25519DeoxysII; const zeroBuffer = new Uint8Array(0); const pt = await mraeDeoxysii.boxOpen( - envelop?.nonce, - envelop?.data, + envelope?.nonce, + envelope?.data, zeroBuffer, metaEncryptedX25519DeoxysII.pk, metaEncryptedX25519DeoxysII.sk, diff --git a/client-sdk/ts-web/rt/src/types.ts b/client-sdk/ts-web/rt/src/types.ts index 2d4690630e..4bcda5d95e 100644 --- a/client-sdk/ts-web/rt/src/types.ts +++ b/client-sdk/ts-web/rt/src/types.ts @@ -252,7 +252,7 @@ export interface Call { export interface CallResult { ok?: unknown; fail?: FailedCallResult; - unknown?: Uint8Array; + unknown?: unknown; } export interface FailedCallResult { From 8865d635337847d5a1a64d48e245133b70e542e2 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 18:49:08 -0700 Subject: [PATCH 11/13] ts-web/rt: mrae correct a typo --- client-sdk/ts-web/rt/src/mrae/deoxysii.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client-sdk/ts-web/rt/src/mrae/deoxysii.ts b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts index c374edd457..6938935059 100644 --- a/client-sdk/ts-web/rt/src/mrae/deoxysii.ts +++ b/client-sdk/ts-web/rt/src/mrae/deoxysii.ts @@ -68,13 +68,13 @@ export async function deriveSymmetricKey( export async function boxSeal( nonce: Uint8Array, plainText: Uint8Array, - associateData: Uint8Array, + associatedData: Uint8Array, publicKey: Uint8Array, privateCK: CryptoKey, ): Promise { const sharedKey = await deriveSymmetricKey(publicKey, privateCK); const aead = new deoxysii.AEAD(sharedKey); - return aead.encrypt(nonce, plainText, associateData); + return aead.encrypt(nonce, plainText, associatedData); } /** @@ -85,11 +85,11 @@ export async function boxSeal( export async function boxOpen( nonce: Uint8Array, ciperText: Uint8Array, - associateData: Uint8Array, + associatedData: Uint8Array, publicKey: Uint8Array, privateCK: CryptoKey, ): Promise { const sharedKey = await deriveSymmetricKey(publicKey, privateCK); const aead = new deoxysii.AEAD(sharedKey); - return aead.decrypt(nonce, ciperText, associateData); + return aead.decrypt(nonce, ciperText, associatedData); } From 807ffb947018926f4c0fc9afd2874f80d4e17528 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 19:03:31 -0700 Subject: [PATCH 12/13] ts-web/rt: remove tweetnacl dependency --- client-sdk/ts-web/package-lock.json | 4 +--- client-sdk/ts-web/rt/package.json | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client-sdk/ts-web/package-lock.json b/client-sdk/ts-web/package-lock.json index 07916a8b09..2be6521b12 100644 --- a/client-sdk/ts-web/package-lock.json +++ b/client-sdk/ts-web/package-lock.json @@ -9450,8 +9450,7 @@ "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@oasisprotocol/client": "^1.1.0", - "@oasisprotocol/deoxysii": "^0.0.6", - "tweetnacl": "^1.0.3" + "@oasisprotocol/deoxysii": "^0.0.6" }, "devDependencies": { "@types/jest": "^29.5.13", @@ -10539,7 +10538,6 @@ "process": "^0.11.10", "stream-browserify": "^3.0.0", "ts-jest": "^29.2.5", - "tweetnacl": "^1.0.3", "typedoc": "^0.26.7", "typescript": "^5.6.2", "webpack": "^5.95.0", diff --git a/client-sdk/ts-web/rt/package.json b/client-sdk/ts-web/rt/package.json index 1bae08d644..a36b9cef76 100644 --- a/client-sdk/ts-web/rt/package.json +++ b/client-sdk/ts-web/rt/package.json @@ -25,8 +25,7 @@ "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@oasisprotocol/client": "^1.1.0", - "@oasisprotocol/deoxysii": "^0.0.6", - "tweetnacl": "^1.0.3" + "@oasisprotocol/deoxysii": "^0.0.6" }, "devDependencies": { "@types/jest": "^29.5.13", From 2f3a18babde03b7bab1c27d40fe78e0a8c061f27 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Oct 2024 19:03:39 -0700 Subject: [PATCH 13/13] ts-web/rt: add changelog --- client-sdk/ts-web/rt/docs/changelog.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client-sdk/ts-web/rt/docs/changelog.md b/client-sdk/ts-web/rt/docs/changelog.md index 671026de0f..a98aa4d292 100644 --- a/client-sdk/ts-web/rt/docs/changelog.md +++ b/client-sdk/ts-web/rt/docs/changelog.md @@ -2,16 +2,28 @@ ## Unreleased changes +Breaking changes: + +- `callformat.encodeCallWithNonceAndKeys` now takes a client `CryptoKeyPair` + instead of `sk` and `pk` `Uint8Array`s. + You can generate one with `mraeDeoxysii.generateKeyPair` and export its + public key with `mraeDeoxysii.publicKeyFromKeyPair`. +- `mraeDeoxysii` functions `deriveSymmetricKey`, `boxSeal`, and `boxOpen` are + now async. + New features: - Functions that internally need to compute a hash, such as `address.fromSigspec`, are declared as synchronous now. - secp256k1 verification is declared as synchronous now. +- `callformat.decodeResult`'s `meta` parameter now takes `unknown`, matching + what you get from `callformat.encodeCall`. Little things: - We're switching lots of cryptography dependencies to noble cryptography libraries. +- X25519 key exchange now uses the Web Crypto API. ## v1.1.0