Skip to content

Commit

Permalink
Merge pull request #1372 from oasisprotocol/pro-wh/feature/cryptolibs
Browse files Browse the repository at this point in the history
ts-web: trying out other crypto libraries
  • Loading branch information
pro-wh authored May 3, 2024
2 parents f2dbe08 + e8c3392 commit d33a30b
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 252 deletions.
2 changes: 1 addition & 1 deletion client-sdk/ts-web/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
"test-e2e-node": "node --experimental-global-webcrypto playground/e2e-test-nodejs.js"
},
"dependencies": {
"@noble/hashes": "^1.4.0",
"bech32": "^2.0.0",
"bip39": "^3.1.0",
"cborg": "^2.0.3",
"grpc-web": "^1.5.0",
"js-sha512": "^0.9.0",
"protobufjs": "~7.2.6",
"tweetnacl": "^1.0.3"
},
Expand Down
4 changes: 2 additions & 2 deletions client-sdk/ts-web/core/src/hash.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {sha512_256} from 'js-sha512';
import {sha512_256} from '@noble/hashes/sha512';

export async function hash(data: Uint8Array) {
return new Uint8Array(sha512_256.arrayBuffer(data));
return sha512_256(data);
}
6 changes: 3 additions & 3 deletions client-sdk/ts-web/core/src/hdkey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {sha512} from 'js-sha512';
import {hmac} from '@noble/hashes/hmac';
import {sha512} from '@noble/hashes/sha512';
import {SignKeyPair, sign} from 'tweetnacl';
import {generateMnemonic, mnemonicToSeed, validateMnemonic} from 'bip39';
import {concat} from './misc';
Expand Down Expand Up @@ -91,8 +92,7 @@ export class HDKey {
}

private static makeHDKey(hmacKey: string | Uint8Array, data: Uint8Array): HDKey {
//@ts-ignore The types of js-sha512 are outdated
const hash = sha512.hmac.arrayBuffer(hmacKey, data);
const hash = hmac(sha512, hmacKey, data);

const I = new Uint8Array(hash);
const IL = I.slice(0, 32);
Expand Down
267 changes: 58 additions & 209 deletions client-sdk/ts-web/package-lock.json

Large diffs are not rendered by default.

8 changes: 2 additions & 6 deletions client-sdk/ts-web/rt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,14 @@
"test-e2e-cy": "cypress run"
},
"dependencies": {
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.4.0",
"@oasisprotocol/client": "^1.1.0",
"@oasisprotocol/deoxysii": "^0.0.5",
"elliptic": "^6.5.5",
"js-sha512": "^0.9.0",
"randombytes": "^2.0.1",
"sha3": "^2.1.4",
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@types/elliptic": "^6.4.18",
"@types/jest": "^29.5.12",
"@types/randombytes": "^2.0.3",
"buffer": "^6.0.3",
"cypress": "^13.8.0",
"jest": "^29.7.0",
Expand Down
2 changes: 1 addition & 1 deletion client-sdk/ts-web/rt/playground/src/consensus.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export const playground = (async function () {
}

// Import the key into a signer.
const dave = oasisRT.signatureSecp256k1.EllipticSigner.fromPrivate(
const dave = oasisRT.signatureSecp256k1.NobleSigner.fromPrivate(
davePriv,
'this key is not important',
);
Expand Down
2 changes: 1 addition & 1 deletion client-sdk/ts-web/rt/playground/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const keyvalueWrapper = new Wrapper(KEYVALUE_RUNTIME_ID);
export const playground = (async function () {
// Try secp256k1 signing.
{
const signer = oasisRT.signatureSecp256k1.EllipticSigner.fromRandom(
const signer = oasisRT.signatureSecp256k1.NobleSigner.fromRandom(
'this key is not important',
);
console.log('signer', signer);
Expand Down
14 changes: 5 additions & 9 deletions client-sdk/ts-web/rt/src/address.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {secp256k1} from '@noble/curves/secp256k1';
import {keccak_256} from '@noble/hashes/sha3';
import * as oasis from '@oasisprotocol/client';
import * as elliptic from 'elliptic';
import {Keccak} from 'sha3';

import * as types from './types';

Expand All @@ -9,20 +9,16 @@ export const V0_SECP256K1ETH_CONTEXT_VERSION = 0;
export const V0_MULTISIG_CONTEXT_IDENTIFIER = 'oasis-runtime-sdk/address: multisig';
export const V0_MULTISIG_CONTEXT_VERSION = 0;

const SECP256K1 = new elliptic.ec('secp256k1');

export async function fromSigspec(spec: types.SignatureAddressSpec) {
if (spec.ed25519) {
return await oasis.staking.addressFromPublicKey(spec.ed25519);
} else if (spec.secp256k1eth) {
// Use a scheme such that we can compute Secp256k1 addresses from Ethereum
// addresses as this makes things more interoperable.
const untaggedPk = SECP256K1.keyFromPublic(spec.secp256k1eth)
.getPublic(false, 'array')
const untaggedPk = secp256k1.ProjectivePoint.fromHex(spec.secp256k1eth)
.toRawBytes(false)
.slice(1);
const hash = new Keccak(256);
hash.update(Buffer.from(untaggedPk));
const pkData = hash.digest().slice(32 - 20);
const pkData = keccak_256(new Uint8Array(untaggedPk)).slice(32 - 20);
return await oasis.address.fromData(
V0_SECP256K1ETH_CONTEXT_IDENTIFIER,
V0_SECP256K1ETH_CONTEXT_VERSION,
Expand Down
4 changes: 2 additions & 2 deletions client-sdk/ts-web/rt/src/callformat.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as oasis from '@oasisprotocol/client';
import * as deoxysii from '@oasisprotocol/deoxysii';
import * as nacl from 'tweetnacl';
import * as randomBytes from 'randombytes';

import * as mraeDeoxysii from './mrae/deoxysii';
import * as transaction from './transaction';
Expand Down Expand Up @@ -86,7 +85,8 @@ export async function encodeCall(
format: types.CallFormat,
config?: EncodeConfig,
): Promise<[types.Call, unknown]> {
const nonce = randomBytes(deoxysii.NonceSize);
const nonce = new Uint8Array(deoxysii.NonceSize);
crypto.getRandomValues(nonce);
const keyPair = nacl.box.keyPair();
return await encodeCallWithNonceAndKeys(
nonce,
Expand Down
7 changes: 3 additions & 4 deletions client-sdk/ts-web/rt/src/mrae/deoxysii.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {hmac} from '@noble/hashes/hmac';
import {sha512_256} from '@noble/hashes/sha512';
import * as deoxysii from '@oasisprotocol/deoxysii';
import {sha512_256} from 'js-sha512';
import * as nacl from 'tweetnacl';

const BOX_KDF_TWEAK = 'MRAE_Box_Deoxys-II-256-128';
Expand All @@ -11,9 +12,7 @@ const BOX_KDF_TWEAK = 'MRAE_Box_Deoxys-II-256-128';

export function deriveSymmetricKey(publicKey: Uint8Array, privateKey: Uint8Array): Uint8Array {
const pmk = nacl.scalarMult(privateKey, publicKey);
var kdf = sha512_256.hmac.create(BOX_KDF_TWEAK);
kdf.update(pmk);
return new Uint8Array(kdf.arrayBuffer());
return hmac(sha512_256, BOX_KDF_TWEAK, pmk);
}

/**
Expand Down
24 changes: 10 additions & 14 deletions client-sdk/ts-web/rt/src/signature_secp256k1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {secp256k1} from '@noble/curves/secp256k1';
import * as oasis from '@oasisprotocol/client';
import * as elliptic from 'elliptic';

export interface Signer {
public(): Uint8Array;
Expand All @@ -11,18 +11,14 @@ export interface ContextSigner {
sign(context: string, message: Uint8Array): Promise<Uint8Array>;
}

const SECP256K1 = new elliptic.ec('secp256k1');

export async function verify(
context: string,
message: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array,
) {
const signerMessage = await oasis.signature.prepareSignerMessage(context, message);
const publicKeyA = Array.from(publicKey);
// @ts-expect-error acceptance of array-like encoded public key is not modeled
return SECP256K1.verify(signerMessage, signature, publicKeyA);
return secp256k1.verify(signature, signerMessage, publicKey);
}

export class BlindContextSigner implements ContextSigner {
Expand All @@ -42,28 +38,28 @@ export class BlindContextSigner implements ContextSigner {
}
}

export class EllipticSigner implements Signer {
key: elliptic.ec.KeyPair;
export class NobleSigner implements Signer {
key: Uint8Array;

constructor(key: elliptic.ec.KeyPair, note: string) {
constructor(key: Uint8Array, note: string) {
if (note !== 'this key is not important') throw new Error('insecure signer implementation');
this.key = key;
}

static fromRandom(note: string) {
return new EllipticSigner(SECP256K1.genKeyPair(), note);
return new NobleSigner(secp256k1.utils.randomPrivateKey(), note);
}

static fromPrivate(priv: Uint8Array, note: string) {
return new EllipticSigner(SECP256K1.keyFromPrivate(priv), note);
return new NobleSigner(priv, note);
}

public(): Uint8Array {
return new Uint8Array(this.key.getPublic(true, 'array'));
return secp256k1.getPublicKey(this.key);
}

async sign(message: Uint8Array): Promise<Uint8Array> {
const sig = this.key.sign(message, {canonical: true});
return new Uint8Array(sig.toDER());
const sig = secp256k1.sign(message, this.key, {lowS: true});
return sig.toDERRawBytes();
}
}
6 changes: 6 additions & 0 deletions client-sdk/ts-web/rt/test/callformat.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import * as oasisRT from './../src';
import * as nacl from 'tweetnacl';
import {webcrypto} from 'crypto';

if (typeof crypto === 'undefined') {
// @ts-expect-error there are some inconsequential type differences
globalThis.crypto = webcrypto;
}

describe('callformat', () => {
describe('encodeCall/DecodeResult', () => {
Expand Down

0 comments on commit d33a30b

Please sign in to comment.