From 6bd6dbea741099afbb189346d2e7375a2e1e1641 Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Thu, 20 Feb 2020 22:15:48 +0000 Subject: [PATCH 1/4] Provide salt to c10l deploys --- packages/client/test/browser/e2e/index.html | 2 +- packages/client/test/e2e/app/service.spec.ts | 6 ++- packages/service/src/deploy/header.ts | 8 ++-- packages/service/src/deploy/index.ts | 9 +++-- packages/service/src/rpc.ts | 2 +- packages/service/test/deploy-header.spec.ts | 41 +++++++++----------- packages/service/test/deploy.spec.ts | 19 +++++++-- packages/service/test/service.spec.ts | 8 ++-- 8 files changed, 56 insertions(+), 39 deletions(-) diff --git a/packages/client/test/browser/e2e/index.html b/packages/client/test/browser/e2e/index.html index fea1a903..9ca88ef3 100644 --- a/packages/client/test/browser/e2e/index.html +++ b/packages/client/test/browser/e2e/index.html @@ -54,7 +54,7 @@ bytecode, arguments: [], // TODO: make confidential once we have the new runtime. - header: { confidential: false }, + header: { salt_if_confidential: null }, coder: test.coder, gateway: test.gateway, }); diff --git a/packages/client/test/e2e/app/service.spec.ts b/packages/client/test/e2e/app/service.spec.ts index 4040d0d4..bb7001be 100644 --- a/packages/client/test/e2e/app/service.spec.ts +++ b/packages/client/test/e2e/app/service.spec.ts @@ -1,3 +1,5 @@ +import { randomBytes } from 'tweetnacl'; + import oasis from '../../../src/'; const process = require('process'); @@ -13,12 +15,12 @@ describe('Counter', () => { const cases = [ { label: 'confidential', - header: { confidential: true }, + header: { saltIfConfidential: randomBytes(32) }, options: { gasLimit: '0xe79732' }, }, { label: 'non-confidential', - header: { confidential: false }, + header: {}, options: {}, }, ]; diff --git a/packages/service/src/deploy/header.ts b/packages/service/src/deploy/header.ts index 2186793f..38a1fccc 100644 --- a/packages/service/src/deploy/header.ts +++ b/packages/service/src/deploy/header.ts @@ -1,8 +1,10 @@ import { bytes } from '@oasislabs/common'; +export const SALT_NUM_BYTES = 32; + export type DeployHeaderOptions = { expiry?: number; - confidential?: boolean; + saltIfConfidential?: Uint8Array | null; }; export class DeployHeaderError extends Error {} @@ -13,7 +15,7 @@ export class DeployHeader { /** * @param {Number} version is the header version number. * @param {Object} is the header body with two fields, expiry (Number) - * and confidential (boolean). + * and saltIfConfidential (boolean). */ constructor(public version: number, public body: DeployHeaderOptions) {} @@ -98,7 +100,7 @@ export class DeployHeader { * @returns true iff the keys in the headerBody are part of the valid set. */ public static isValidBody(headerBody: DeployHeaderOptions): boolean { - const validKeys = ['expiry', 'confidential']; + const validKeys = ['expiry', 'saltIfConfidential']; const keys = Object.keys(headerBody); for (let k = 0; k < keys.length; k += 1) { diff --git a/packages/service/src/deploy/index.ts b/packages/service/src/deploy/index.ts index 681f99e3..46520bca 100644 --- a/packages/service/src/deploy/index.ts +++ b/packages/service/src/deploy/index.ts @@ -1,3 +1,4 @@ +import nacl from 'tweetnacl'; import { Address, Db, bytes } from '@oasislabs/common'; import { Service } from '../service'; @@ -7,7 +8,7 @@ import { RpcOptions, defaultOasisGateway, } from '../oasis-gateway'; -import { DeployHeader, DeployHeaderOptions } from './header'; +import { DeployHeader, DeployHeaderOptions, SALT_NUM_BYTES } from './header'; import { OasisCoder } from '../coder/oasis'; import { RpcCoder } from '../coder'; import { DeployError } from '../error'; @@ -130,7 +131,9 @@ async function toDeployOptions( * filling in any left out options with the default header. */ function deployHeader(options: DeployOptions): DeployHeaderOptions { - const defaultHeader = { confidential: true }; + const defaultHeader = { + saltIfConfidential: nacl.randomBytes(SALT_NUM_BYTES), + }; return Object.assign(defaultHeader, options.header); } @@ -168,7 +171,7 @@ function oasisGateway(options: DeployOptions): OasisGateway { */ function validateDeployOptions(deployOptions: DeployOptions, args: any[]) { if ( - deployOptions.header!.confidential && + deployOptions.header!.saltIfConfidential && (!deployOptions.options || !deployOptions.options.gasLimit) ) { throw new DeployError( diff --git a/packages/service/src/rpc.ts b/packages/service/src/rpc.ts index 06b72e63..59dc8fd9 100644 --- a/packages/service/src/rpc.ts +++ b/packages/service/src/rpc.ts @@ -187,7 +187,7 @@ export class RpcFactory { const deployHeader = DeployHeader.parseFromCode(response.code); - if (!deployHeader || !deployHeader.body.confidential) { + if (!deployHeader || !deployHeader.body.saltIfConfidential) { return OasisCoder.plaintext(); } diff --git a/packages/service/test/deploy-header.spec.ts b/packages/service/test/deploy-header.spec.ts index 98a22764..f8d04aaa 100644 --- a/packages/service/test/deploy-header.spec.ts +++ b/packages/service/test/deploy-header.spec.ts @@ -1,3 +1,4 @@ +import { randomBytes } from 'tweetnacl'; import { bytes } from '@oasislabs/common'; import { DeployHeader } from '../src/deploy/header'; import { makeExpectedBytecode } from './utils'; @@ -8,25 +9,25 @@ describe('DeployHeader', () => { { description: 'errors when writing a deploy header to empty bytecode', bytecode: new Uint8Array(), - header: { expiry: 100000, confidential: false }, + header: { expiry: 100000, saltIfConfidential: null }, error: 'Malformed deploycode', }, { description: 'errors when writing an invalid deploy header', bytecode: bytes.parseHex('0x1234'), - header: { invalid: 1234, expiry: 100000, confidential: false }, + header: { invalid: 1234, expiry: 100000 }, error: 'Malformed deploycode', }, { description: 'errors when writing to bytecode that already has an invalid header', bytecode: makeExpectedBytecode( - { expiry: 100000, confidential: false, badkey: true }, + { expiry: 100000, saltIfConfidential: null, badkey: true }, '1234' ), - header: { expiry: 100000, confidential: false }, + header: { expiry: 100000, saltIfConfidential: null }, error: - 'Invalid body {"expiry":100000,"confidential":false,"badkey":true}', + 'Invalid body {"expiry":100000,"saltIfConfidential":null,"badkey":true}', }, ]; @@ -38,6 +39,8 @@ describe('DeployHeader', () => { }); }); + const saltIfConfidential = randomBytes(32); + const successTests = [ { description: 'does not change the bytecode if the header is empty', @@ -48,18 +51,15 @@ describe('DeployHeader', () => { { description: 'writes a deploy header to non-empty bytecode', bytecode: bytes.parseHex('0x1234'), - header: { expiry: 100000, confidential: false }, - expected: makeExpectedBytecode( - { expiry: 100000, confidential: false }, - '1234' - ), + header: { expiry: 100000 }, + expected: makeExpectedBytecode({ expiry: 100000 }, '1234'), }, { description: 'overwrites a deploy header to non-empty bytecode with an existing confidential header', - bytecode: makeExpectedBytecode({ confidential: false }, '1234'), - header: { confidential: true }, - expected: makeExpectedBytecode({ confidential: true }, '1234'), + bytecode: makeExpectedBytecode({}, '1234'), + header: { saltIfConfidential }, + expected: makeExpectedBytecode({ saltIfConfidential }, '1234'), }, { description: @@ -71,24 +71,21 @@ describe('DeployHeader', () => { { description: 'overwrites a deploy header to non-empty bytecode with an existing expiry and confidential header', - bytecode: makeExpectedBytecode( - { expiry: 100000, confidential: false }, - '1234' - ), - header: { expiry: 100001, confidential: true }, + bytecode: makeExpectedBytecode({ expiry: 100000 }, '1234'), + header: { expiry: 100001, saltIfConfidential }, expected: makeExpectedBytecode( - { expiry: 100001, confidential: true }, + { expiry: 100001, saltIfConfidential }, '1234' ), }, ]; successTests.forEach(test => { - it(test.description, function() { - const data = DeployHeader.deployCode( + it(test.description, async function() { + const data = (await DeployHeader.deployCode( test.header, test.bytecode - ) as Uint8Array; + )) as Uint8Array; expect(data).toEqual(test.expected); }); }); diff --git a/packages/service/test/deploy.spec.ts b/packages/service/test/deploy.spec.ts index 468839cc..0510245b 100644 --- a/packages/service/test/deploy.spec.ts +++ b/packages/service/test/deploy.spec.ts @@ -1,3 +1,4 @@ +import { randomBytes } from 'tweetnacl'; import { cbor, bytes } from '@oasislabs/common'; import { idl } from '@oasislabs/test'; import { @@ -7,7 +8,11 @@ import { } from './utils'; import { deploy } from '../src/index'; import { RpcRequest, setGateway } from '../src/oasis-gateway'; -import { DeployHeaderReader } from '../src/deploy/header'; +import { + DeployHeaderReader, + DeployHeaderOptions, + SALT_NUM_BYTES, +} from '../src/deploy/header'; setGateway(new EmptyOasisGateway()); @@ -27,12 +32,15 @@ describe('Service deploys', () => { }, { bytecode: bytes.parseHex('0x0102039999'), - header: { confidential: false }, + header: { saltIfConfidential: null }, label: 'deploys a service without confidentiality', }, { bytecode: bytes.parseHex('0x0102039999'), - header: { confidential: true, expiry: 12345 }, + header: { + saltIfConfidential: randomBytes(SALT_NUM_BYTES), + expiry: 12345, + }, label: 'deploys a service with expiry', gasLimit: '0xffff', }, @@ -69,7 +77,10 @@ describe('Service deploys', () => { // Check header. const header = DeployHeaderReader.header(deployCode); // Should have used the default header since we didn't specify one. - const expectedHeader = { version: 1, body: { confidential: true } }; + const expectedHeader: { version: number; body: DeployHeaderOptions } = { + version: 1, + body: { saltIfConfidential: header?.body.saltIfConfidential }, + }; if (test.header !== undefined) { expectedHeader.body = test.header!; } diff --git a/packages/service/test/service.spec.ts b/packages/service/test/service.spec.ts index f7c63667..1b1fcdb9 100644 --- a/packages/service/test/service.spec.ts +++ b/packages/service/test/service.spec.ts @@ -121,12 +121,14 @@ describe('Service', () => { const address = '0x288e7e1cc60962f40d4d782950470e3705c5acf4'; const bin = bytes.toHex( new Uint8Array( - require('fs').readFileSync('test/wasm/mantle-counter.wasm') + await require('fs').promises.readFile('test/wasm/mantle-counter.wasm') ) ); const gateway = { - getCode: () => { - return { code: makeExpectedBytecode({ confidential: false }, bin) }; + async getCode(): Promise { + return { + code: makeExpectedBytecode({ saltIfConfidential: null }, bin), + }; }, }; // @ts-ignore From da8044f4e2f78d74a90f626647a28b286419ebd4 Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Thu, 20 Feb 2020 15:11:34 -0800 Subject: [PATCH 2/4] Fix browser test --- .../client/test/browser/service/index.html | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/client/test/browser/service/index.html b/packages/client/test/browser/service/index.html index 56de173e..03085d31 100644 --- a/packages/client/test/browser/service/index.html +++ b/packages/client/test/browser/service/index.html @@ -102,9 +102,20 @@ return { publicKey: keys.peerPublicKey.bytes }; } async getCode(request) { - // Just return any bytecode with the deploy header with confidential === true. + // Just return any bytecode with the deploy header with `saltIfConfidential` set + const prefix = '00736973'; // \0sis + const saltIfConfidential = []; + saltIfConfidential.length = 32; + saltIfConfidential.fill(1); + const headerStr = JSON.stringify({ saltIfConfidential }); + const headerVersion = (1).toString(16).padStart(4, '0'); + const header = oasis.utils.bytes.toHex(new TextEncoder().encode(headerStr)); + const headerLength = headerStr.length.toString(16).padStart(4, '0'); + const body = '0102039999816f636f6e7374727563746f722d617267'; return { - code: new Uint8Array(oasis.utils.bytes.parseHex('0x00736973000100247b22636f6e666964656e7469616c223a747275652c22657870697279223a31323334357d0102039999816f636f6e7374727563746f722d617267')) + code: oasis.utils.bytes.parseHex( + `${prefix}${headerVersion}${headerLength}${header.slice(2)}${body}` + ) }; } hasSigner() { @@ -153,7 +164,7 @@ let nonConfidentialTest = { gateway: (resolve) => new RequestMockOasisGateway(resolve), db: () => new DummyStorage(), - coder: new oasis.utils.OasisCoder.plaintext(), + coder: oasis.utils.OasisCoder.plaintext(), }; // Confidential test case. let confidentialTest = { @@ -168,7 +179,7 @@ })); return db; }, - coder: new oasis.utils.OasisCoder.confidential({ + coder: oasis.utils.OasisCoder.confidential({ publicKey: keys.peerPublicKey, privateKey: keys.peerPrivateKey, peerPublicKey: keys.publicKey, From 3075d0b64d1c73088bd109a1405d2552e2f163dd Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Thu, 20 Feb 2020 15:28:40 -0800 Subject: [PATCH 3/4] Fix build --- packages/confidential/src/index.ts | 6 +++++- packages/service/package.json | 3 ++- packages/service/src/deploy/index.ts | 4 ++-- yarn.lock | 5 +++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/confidential/src/index.ts b/packages/confidential/src/index.ts index 52add004..4af76ce7 100644 --- a/packages/confidential/src/index.ts +++ b/packages/confidential/src/index.ts @@ -107,7 +107,11 @@ export async function decrypt( } export function nonce(): Nonce { - return new Nonce(nacl.randomBytes(aead.nonceSize())); + return new Nonce(randomBytes(aead.nonceSize())); +} + +export function randomBytes(length: number): Uint8Array { + return nacl.randomBytes(length); } /** diff --git a/packages/service/package.json b/packages/service/package.json index 08cf886b..30d26de7 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -51,7 +51,8 @@ "camelcase-keys": "^6.0.1", "eventemitter3": "^4.0.0", "js-sha3": "^0.8.0", - "pako": "^1.0.10" + "pako": "^1.0.10", + "tweetnacl": "^1.0.3" }, "browser": { "./dist/index.umd.js": "./dist/index.browser.umd.js", diff --git a/packages/service/src/deploy/index.ts b/packages/service/src/deploy/index.ts index 46520bca..6e3203b5 100644 --- a/packages/service/src/deploy/index.ts +++ b/packages/service/src/deploy/index.ts @@ -1,5 +1,5 @@ -import nacl from 'tweetnacl'; import { Address, Db, bytes } from '@oasislabs/common'; +import { randomBytes } from '@oasislabs/confidential'; import { Service } from '../service'; import { Idl, fromWasm } from '../idl'; @@ -132,7 +132,7 @@ async function toDeployOptions( */ function deployHeader(options: DeployOptions): DeployHeaderOptions { const defaultHeader = { - saltIfConfidential: nacl.randomBytes(SALT_NUM_BYTES), + saltIfConfidential: randomBytes(SALT_NUM_BYTES), }; return Object.assign(defaultHeader, options.header); } diff --git a/yarn.lock b/yarn.lock index 1eed2362..f580c8e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7685,6 +7685,11 @@ tweetnacl@^1.0.1: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.1.tgz#2594d42da73cd036bd0d2a54683dd35a6b55ca17" integrity sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A== +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" From 92666732a114db95b3bb9803415e67c46838fc27 Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Thu, 20 Feb 2020 15:40:52 -0800 Subject: [PATCH 4/4] Address review comment --- packages/client/test/browser/service/index.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/client/test/browser/service/index.html b/packages/client/test/browser/service/index.html index 03085d31..6f0481c5 100644 --- a/packages/client/test/browser/service/index.html +++ b/packages/client/test/browser/service/index.html @@ -104,9 +104,7 @@ async getCode(request) { // Just return any bytecode with the deploy header with `saltIfConfidential` set const prefix = '00736973'; // \0sis - const saltIfConfidential = []; - saltIfConfidential.length = 32; - saltIfConfidential.fill(1); + const saltIfConfidential = Array(32).fill(1); const headerStr = JSON.stringify({ saltIfConfidential }); const headerVersion = (1).toString(16).padStart(4, '0'); const header = oasis.utils.bytes.toHex(new TextEncoder().encode(headerStr));