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

release: fix for ESM ENG-591 #16

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [1.0.2-canary.1](https://github.com/algorandfoundation/xHD-Wallet-API-ts/compare/v1.0.1...v1.0.2-canary.1) (2024-10-16)


### Bug Fixes

* ensure module is loaded before consuming as imports ([39131e1](https://github.com/algorandfoundation/xHD-Wallet-API-ts/commit/39131e1645bdaaf2ce0d57152b9bdc90e429dcd5))

## [1.0.1](https://github.com/algorandfoundation/xHD-Wallet-API-ts/compare/v1.0.0...v1.0.1) (2024-10-15)


Expand Down
8 changes: 8 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { JestConfigWithTsJest } from 'ts-jest';

const jestConfig: JestConfigWithTsJest = {
preset: 'ts-jest/presets/default-esm',
testPathIgnorePatterns: ["src"],
};

export default jestConfig;
36 changes: 5 additions & 31 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@algorandfoundation/xhd-wallet-api",
"version": "1.0.1",
"version": "1.0.2-canary.1",
"description": "A Typescript implementation of Algorand ARC-52 extended hierarchical determinstic wallets.",
"exports": {
".": {
Expand All @@ -11,8 +11,8 @@
"type": "module",
"scripts": {
"build": "tsc && tsc-alias -p tsconfig.json -f",
"test": "jest --verbose",
"test:cov": "jest --coverage"
"test": "tsc && tsc-alias -p tsconfig.json -f && node --experimental-vm-modules node_modules/.bin/jest",
"test:cov": "tsc && tsc-alias -p tsconfig.json -f && node --experimental-vm-modules node_modules/.bin/jest --coverage"
},
"keywords": [],
"author": "Algorand Foundation",
Expand All @@ -21,12 +21,14 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^12.0.1",
"@types/bn.js": "^5.1.6",
"@types/jest": "^29.5.5",
"@types/libsodium-wrappers-sumo": "^0.7.7",
"@types/node": "^20.7.1",
"semantic-release": "^24.1.1",
"bip32-ed25519": "^0.0.4",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.10",
"tweetnacl": "^1.0.3",
"typescript": "^5.2.2"
Expand All @@ -43,34 +45,6 @@
"ts-custom-error": "^3.2.0",
"ts-log": "^2.2.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": ".",
"testRegex": ".spec.ts$",
"coverageDirectory": "../test/coverage",
"collectCoverageFrom": [
"**/!(*.module|*.interface|main|repl|exception.filter|logging.interceptor).{ts,js}"
],
"coveragePathIgnorePatterns": [
"/src/migration/"
],
"testEnvironment": "node",
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
},
"transform": {
"^.+\\.tsx?$": [
"ts-jest",
{
"useESM": true
}
]
}
},
"repository": "[email protected]:algorandfoundation/xHD-Wallet-API-ts.git",
"files": [
"dist",
Expand Down
55 changes: 26 additions & 29 deletions src/bip32-ed25519.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { createHash, createHmac } from "crypto";
import { read } from "fs";

import {
crypto_core_ed25519_add,
crypto_core_ed25519_add,
crypto_scalarmult_ed25519_base_noclamp,
ready
} from "libsodium-wrappers-sumo";
var BN = require("bn.js");
} from "./sumo.js";
import BN from 'bn.js'
import * as util from 'util'

/**
*
* Reference of BIP32-Ed25519 Hierarchical Deterministic Keys over a Non-linear Keyspace (https://acrobat.adobe.com/id/urn:aaid:sc:EU:04fe29b0-ea1a-478b-a886-9bb558a5242a)
*
* @see section V. BIP32-Ed25519: Specification;
*
*
* A) Root keys
*
*
* @param seed - 256 bite seed generated from BIP39 Mnemonic
* @returns - Extended root key (kL, kR, c) where kL is the left 32 bytes of the root key, kR is the right 32 bytes of the root key, and c is the chain code. Total 96 bytes
*/
Expand Down Expand Up @@ -47,7 +46,7 @@ export function fromSeed(seed: Buffer): Uint8Array {

/**
* This function takes an array of up to 256 bits and sets the last g trailing bits to zero
*
*
* @param array - An array of up to 256 bits
* @param g - The number of bits to zero
* @returns - The array with the last g bits set to zero
Expand Down Expand Up @@ -80,25 +79,23 @@ export function trunc_256_minus_g_bits(array: Uint8Array, g: number): Uint8Array

/**
* @see section V. BIP32-Ed25519: Specification;
*
*
* subsections:
*
*
* B) Child Keys
* and
* C) Private Child Key Derivation
*
*
* @param extendedKey - extended key (kL, kR, c) where kL is the left 32 bytes of the root key the scalar (pvtKey). kR is the right 32 bytes of the root key, and c is the chain code. Total 96 bytes
* @param index - index of the child key
* @param g - Defines how many bits to zero in the left 32 bytes of the child key. Standard BIP32-ed25519 derivations use 32 bits.
* @param g - Defines how many bits to zero in the left 32 bytes of the child key. Standard BIP32-ed25519 derivations use 32 bits.
* @returns - (kL, kR, c) where kL is the left 32 bytes of the child key (the new scalar), kR is the right 32 bytes of the child key, and c is the chain code. Total 96 bytes
*/
export async function deriveChildNodePrivate(
extendedKey: Uint8Array,
index: number,
index: number,
g: number = 9
): Promise<Uint8Array> {
await ready // wait for libsodium to be ready

const kL: Buffer = Buffer.from(extendedKey.subarray(0, 32));
const kR: Buffer = Buffer.from(extendedKey.subarray(32, 64));
const cc: Uint8Array = extendedKey.subarray(64, 96);
Expand All @@ -113,23 +110,23 @@ export async function deriveChildNodePrivate(
// ######################################
// Standard BIP32-ed25519 derivation
// #######################################
// zL = kl + 8 * trunc_keep_28_bytes (z_left_hand_side)
// zL = kl + 8 * trunc_keep_28_bytes (z_left_hand_side)
// zR = zr + kr

// ######################################
// Chris Peikert's ammendment to BIP32-ed25519 derivation
// #######################################
// zL = kl + 8 * trunc_256_minus_g_bits (z_left_hand_side, g)
// zL = kl + 8 * trunc_256_minus_g_bits (z_left_hand_side, g)
// Needs to satisfy g >= d + 6
//
// D = 2 ^ d , D is the maximum levels of BIP32 derivations to ensure a more secure key derivation


// Picking g == 9 && d == 3
// 256 - 9 == 247 bits (30 bytes + leftover)
// D = 2 ^ 3 == 8 Max Levels of derivations (Although we only need 5 due to BIP44)

// making sure
// making sure
// g == 9 >= 3 + 6

const zL: Uint8Array = trunc_256_minus_g_bits(zLeft, g);
Expand All @@ -143,15 +140,15 @@ export async function deriveChildNodePrivate(
const zlBigNumMul8 = klBigNum.add(zlBigNum.mul(big8))

// check if zlBigNumMul8 is equal or larger than 2^255
if (zlBigNumMul8.cmp(new BN(2).pow(new BN(255))) >= 0) {
if (zlBigNumMul8.cmp(new BN(2).pow(new BN(255))) >= 0) {
console.log(util.inspect(zlBigNumMul8), { colors: true, depth: null })
throw new Error('zL * 8 is larger than 2^255, which is not safe')
}

const left = klBigNum.add(zlBigNum.mul(big8)).toArrayLike(Buffer, 'le', 32);

let right = new BN(kR, 16, 'le').add(new BN(zRight, 16, 'le')).toArrayLike(Buffer, 'le').slice(0, 32);

const rightBuffer = Buffer.alloc(32);
Buffer.from(right).copy(rightBuffer, 0, 0, right.length) // padding with zeros if needed

Expand All @@ -161,14 +158,14 @@ export async function deriveChildNodePrivate(

/**
* * @see section V. BIP32-Ed25519: Specification;
*
*
* subsections:
*
*
* D) Public Child key
*
*
* @param extendedKey - extend public key (p, c) where p is the public key and c is the chain code. Total 64 bytes
* @param index - unharden index (i < 2^31) of the child key
* @param g - Defines how many bits to zero in the left 32 bytes of the child key. Standard BIP32-ed25519 derivations use 32 bits.
* @param g - Defines how many bits to zero in the left 32 bytes of the child key. Standard BIP32-ed25519 derivations use 32 bits.
* @returns - 64 bytes, being the 32 bytes of the child key (the new public key) followed by the 32 bytes of the chain code
*/
export async function deriveChildNodePublic(extendedKey: Uint8Array, index: number, g: number = 9): Promise<Uint8Array> {
Expand All @@ -185,15 +182,15 @@ export async function deriveChildNodePublic(extendedKey: Uint8Array, index: numb
// Step 1: Compute Z
data[0] = 0x02;
const z: Buffer = createHmac("sha512", cc).update(data).digest();

// Step 2: Compute child public key
const zL: Uint8Array = trunc_256_minus_g_bits(z.subarray(0, 32), g)

// ######################################
// Standard BIP32-ed25519 derivation
// #######################################
// zL = 8 * 28bytesOf(z_left_hand_side)

// ######################################
// Chris Peikert's ammendment to BIP32-ed25519 derivation
// #######################################
Expand All @@ -213,7 +210,7 @@ export async function deriveChildNodePublic(extendedKey: Uint8Array, index: numb
/**
*
* @see section V. BIP32-Ed25519: Specification
*
*
* @param kl - The scalar
* @param cc - chain code
* @param index - non-hardened ( < 2^31 ) index
Expand Down Expand Up @@ -243,7 +240,7 @@ function derivedNonHardened(
/**
*
* @see section V. BIP32-Ed25519: Specification
*
*
* @param kl - The scalar (a.k.a private key)
* @param kr - the right 32 bytes of the root key
* @param cc - chain code
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './x.hd.wallet.api.crypto'
export * from './bip32-ed25519'
export * from './x.hd.wallet.api.crypto.js'
export * from './bip32-ed25519.js'
21 changes: 21 additions & 0 deletions src/sumo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pkg from 'libsodium-wrappers-sumo'

await pkg.ready;

export const crypto_core_ed25519_add = pkg.crypto_core_ed25519_add;
export const crypto_core_ed25519_scalar_add = pkg.crypto_core_ed25519_scalar_add
export const crypto_core_ed25519_scalar_mul = pkg.crypto_core_ed25519_scalar_mul
export const crypto_core_ed25519_scalar_reduce = pkg.crypto_core_ed25519_scalar_reduce
export const crypto_hash_sha512 = pkg.crypto_hash_sha512
export const crypto_scalarmult_ed25519_base_noclamp = pkg.crypto_scalarmult_ed25519_base_noclamp
export const crypto_sign_verify_detached = pkg.crypto_sign_verify_detached
export const crypto_sign_ed25519_pk_to_curve25519 = pkg.crypto_sign_ed25519_pk_to_curve25519
export const crypto_scalarmult = pkg.crypto_scalarmult
export const crypto_generichash = pkg.crypto_generichash
export const crypto_sign_keypair = pkg.crypto_sign_keypair
export const crypto_sign_ed25519_sk_to_curve25519 = pkg.crypto_sign_ed25519_sk_to_curve25519
export const crypto_secretbox_open_easy = pkg.crypto_secretbox_open_easy
export const crypto_secretbox_easy = pkg.crypto_secretbox_easy
export const crypto_kx_client_session_keys = pkg.crypto_kx_client_session_keys
export const crypto_kx_server_session_keys = pkg.crypto_kx_server_session_keys
export const to_base64 = pkg.to_base64
Loading
Loading