Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minus tweetnacl plus webcrypto #2024

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions client-sdk/ts-web/core/docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## Unreleased changes

Breaking changes:

- `signature.NaclSigner` is moved out to a new
`@oasisprotocol/signer-tweetnacl` package.
- `hdkey.HDKey.getAccountSigner` now returns a `signature.Signer` instead of
a tweetnacl `SignKeyPair`.
To get the private key, use the new `hdkey.HDKey.seedFromMnemonic` and
`hdkey.HDKey.privateKeyFromSeed` functions.

New features:

- Hashing and many related functions that internally need to compute a hash,
such as getting the address of a public key, are now declared as
synchronous.
We had implementations that used synchronous hashing libraries all along,
but this is us giving up on eventually using the Web Crypto API for
SHA-512/256.
- For Ed25519 signing, there's a new `signature.WebCryptoSigner` taking the
place of `signature.NaclSigner`.
`await signature.WebCryptoSigner.generate(extractable)` is equivalent to
`signature.NaclSigner.fromRandom(note)`, and
`await signature.WebCryptoSigner.fromPrivateKey(priv)` is equivalent to
`signature.NaclSigner.fromSeed(priv, note)`.

Little things:

- Removed dependency on tweetnacl.
- We're switching lots of cryptography dependencies to noble cryptography
libraries.
- Ed25519 verification now uses the Web Crypto API.

## v1.1.0

Spotlight change:
Expand Down
3 changes: 1 addition & 2 deletions client-sdk/ts-web/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
"bip39": "^3.1.0",
"cborg": "^2.0.3",
"grpc-web": "^1.5.0",
"protobufjs": "~7.4.0",
"tweetnacl": "^1.0.3"
"protobufjs": "~7.4.0"
},
"devDependencies": {
"@types/jest": "^29.5.13",
Expand Down
18 changes: 9 additions & 9 deletions client-sdk/ts-web/core/playground/src/startPlayground.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,27 @@ export async function startPlayground() {

// Try sending a transaction.
{
const src = oasis.signature.NaclSigner.fromRandom('this key is not important');
const dst = oasis.signature.NaclSigner.fromRandom('this key is not important');
const src = await oasis.signature.WebCryptoSigner.generate(false);
const dst = await oasis.signature.WebCryptoSigner.generate(false);
console.log('src', src, 'dst', dst);

const chainContext = await nic.consensusGetChainContext();
console.log('chain context', chainContext);

const genesis = await nic.consensusGetGenesisDocument();
const ourChainContext = await oasis.genesis.chainContext(genesis);
const ourChainContext = oasis.genesis.chainContext(genesis);
console.log('computed from genesis', ourChainContext);
if (ourChainContext !== chainContext) throw new Error('computed chain context mismatch');

const nonce = await nic.consensusGetSignerNonce({
account_address: await oasis.staking.addressFromPublicKey(src.public()),
account_address: oasis.staking.addressFromPublicKey(src.public()),
height: oasis.consensus.HEIGHT_LATEST,
});
console.log('nonce', nonce);

const account = await nic.stakingAccount({
height: oasis.consensus.HEIGHT_LATEST,
owner: await oasis.staking.addressFromPublicKey(src.public()),
owner: oasis.staking.addressFromPublicKey(src.public()),
});
console.log('account', account);
if ((account.general?.nonce ?? 0) !== nonce) throw new Error('nonce mismatch');
Expand All @@ -64,7 +64,7 @@ export async function startPlayground() {
tw.setNonce(account.general?.nonce ?? 0);
tw.setFeeAmount(oasis.quantity.fromBigInt(0n));
tw.setBody({
to: await oasis.staking.addressFromPublicKey(dst.public()),
to: oasis.staking.addressFromPublicKey(dst.public()),
amount: oasis.quantity.fromBigInt(0n),
});

Expand All @@ -75,7 +75,7 @@ export async function startPlayground() {

await tw.sign(new oasis.signature.BlindContextSigner(src), chainContext);
console.log('singed transaction', tw.signedTransaction);
console.log('hash', await tw.hash());
console.log('hash', tw.hash());

await tw.submit(nic);
console.log('sent');
Expand All @@ -100,9 +100,9 @@ export async function startPlayground() {
signedTransaction,
);
console.log({
hash: await oasis.consensus.hashSignedTransaction(signedTransaction),
hash: oasis.consensus.hashSignedTransaction(signedTransaction),
from: oasis.staking.addressToBech32(
await oasis.staking.addressFromPublicKey(
oasis.staking.addressFromPublicKey(
signedTransaction.signature.public_key,
),
),
Expand Down
11 changes: 7 additions & 4 deletions client-sdk/ts-web/core/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export const IDENTITY_MODULE_NAME = 'identity';
*/
export const IDENTITY_ERR_CERTIFICATE_ROTATION_FORBIDDEN_CODE = 1;

export function openSignedEntity(context: string, signed: types.SignatureSigned) {
return misc.fromCBOR(signature.openSigned(context, signed)) as types.Entity;
export async function openSignedEntity(context: string, signed: types.SignatureSigned) {
return misc.fromCBOR(await signature.openSigned(context, signed)) as types.Entity;
}

export async function signSignedEntity(
Expand All @@ -84,8 +84,11 @@ export async function signSignedEntity(
return await signature.signSigned(signer, context, misc.toCBOR(entity));
}

export function openMultiSignedNode(context: string, multiSigned: types.SignatureMultiSigned) {
return misc.fromCBOR(signature.openMultiSigned(context, multiSigned)) as types.Node;
export async function openMultiSignedNode(
context: string,
multiSigned: types.SignatureMultiSigned,
) {
return misc.fromCBOR(await signature.openMultiSigned(context, multiSigned)) as types.Node;
}

export async function signMultiSignedNode(
Expand Down
4 changes: 2 additions & 2 deletions client-sdk/ts-web/core/src/consensus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ export const TRANSACTION_ERR_GAS_PRICE_TOO_LOW_CODE = 3;
*/
export const TRANSACTION_ERR_UPGRADE_PENDING = 4;

export function openSignedTransaction(chainContext: string, signed: types.SignatureSigned) {
export async function openSignedTransaction(chainContext: string, signed: types.SignatureSigned) {
const context = signature.combineChainContext(TRANSACTION_SIGNATURE_CONTEXT, chainContext);
return misc.fromCBOR(signature.openSigned(context, signed)) as types.ConsensusTransaction;
return misc.fromCBOR(await signature.openSigned(context, signed)) as types.ConsensusTransaction;
}

export async function signSignedTransaction(
Expand Down
57 changes: 41 additions & 16 deletions client-sdk/ts-web/core/src/hdkey.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {hmac} from '@noble/hashes/hmac';
import {sha512} from '@noble/hashes/sha512';
import {SignKeyPair, sign} from 'tweetnacl';
import {generateMnemonic, mnemonicToSeed, validateMnemonic} from 'bip39';
import {generateMnemonic, mnemonicToSeed} from 'bip39';
import {concat} from './misc';
import {Signer, WebCryptoSigner} from './signature';

const ED25519_CURVE = 'ed25519 seed';
const HARDENED_OFFSET = 0x80000000;
Expand All @@ -13,27 +13,54 @@ const pathRegex = new RegExp("^m(\\/[0-9]+')+$");
* https://github.com/oasisprotocol/adrs/blob/main/0008-standard-account-key-generation.md
*/
export class HDKey {
public readonly keypair: SignKeyPair;
private static ensureValidIndex(index: number) {
if (index < 0 || index > 0x7fffffff) {
throw new Error('Account number must be >= 0 and <= 2147483647');
}
}

/**
* Generates the keypair matching the supplied parameters
* @param mnemonic BIP-0039 Mnemonic
* Generates the seed matching the supplied parameters
* @param mnemonic BIP-0039 mnemonic
* @param passphrase Optional BIP-0039 passphrase
* @returns BIP-0039 seed
*/
public static async seedFromMnemonic(mnemonic: string, passphrase?: string) {
return new Uint8Array(await mnemonicToSeed(mnemonic, passphrase));
}

/**
* Generates the signer matching the supplied parameters
* @param seed BIP-0039 seed
* @param index Account index
* @returns ed25519 private key for these parameters
*/
public static privateKeyFromSeed(seed: Uint8Array, index: number = 0) {
HDKey.ensureValidIndex(index);

const key = HDKey.makeHDKey(ED25519_CURVE, seed);
return key.derivePath(`m/44'/474'/${index}'`).privateKey;
}

/**
* Generates the Signer matching the supplied parameters
* @param mnemonic BIP-0039 mnemonic
* @param index Account index
* @param passphrase Optional BIP-0039 passphrase
* @returns SignKeyPair for these parameters
* @returns Signer for these parameters
*/
public static async getAccountSigner(
mnemonic: string,
index: number = 0,
passphrase?: string,
): Promise<SignKeyPair> {
if (index < 0 || index > 0x7fffffff) {
throw new Error('Account number must be >= 0 and <= 2147483647');
}
): Promise<Signer> {
// privateKeyFromSeed checks too, but validate before the expensive
// seedFromMnemonic call.
HDKey.ensureValidIndex(index);

const seed = await mnemonicToSeed(mnemonic, passphrase);
const key = HDKey.makeHDKey(ED25519_CURVE, seed);
return key.derivePath(`m/44'/474'/${index}'`).keypair;
const seed = await HDKey.seedFromMnemonic(mnemonic, passphrase);
const privateKey = HDKey.privateKeyFromSeed(seed, index);
return await WebCryptoSigner.fromPrivateKey(privateKey);
}

/**
Expand All @@ -48,9 +75,7 @@ export class HDKey {
private constructor(
private readonly privateKey: Uint8Array,
private readonly chainCode: Uint8Array,
) {
this.keypair = sign.keyPair.fromSeed(privateKey);
}
) {}

/**
* Returns the HDKey for the given derivation path
Expand Down
6 changes: 6 additions & 0 deletions client-sdk/ts-web/core/src/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export function fromBase64(base64: string) {
return u8;
}

export function fromBase64url(base64url: string) {
const padding = ['', '', '==', '='][base64url.length % 4];
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/') + padding;
return fromBase64(base64);
}

export function toStringUTF8(u8: Uint8Array) {
return new TextDecoder().decode(u8);
}
Expand Down
Loading
Loading