Compare commits

...
58 Commits
Author SHA1 Message Date
github-actions[bot] 05c82721e8 [CI Skip] release/stable 8.1.1
skip-checks: true
2021-12-05 08:58:29 +00:00
Jaco 1b13cf0eea 8.1.1 (#1283) 2021-12-05 10:54:07 +02:00
github-actions[bot] e6777ece62 [CI Skip] release/beta 8.0.6-18
skip-checks: true
2021-12-05 08:15:04 +00:00
Jaco 7436976598 Add shims for x-* (not used, convenience) (#1282)
* Add shims for x-* (not used, convenience)

* Update packages/x-ws/src/shim.ts

* Remove (now) unused dependencies
2021-12-05 10:09:47 +02:00
github-actions[bot] 699ef952c3 [CI Skip] release/beta 8.0.6-17
skip-checks: true
2021-12-04 11:02:06 +00:00
Jaco cd9dc35475 Align x-noble with upstream (#1281)
* Align x-noble with upstream

* Remove own version of hashes

* @noble/hashes from master
2021-12-04 12:57:36 +02:00
github-actions[bot] ee7923c457 [CI Skip] release/beta 8.0.6-16
skip-checks: true
2021-12-04 07:16:16 +00:00
Jaco bd3f51844c Add BigInt shim (#1280) 2021-12-04 09:11:16 +02:00
github-actions[bot] 986dfe5006 [CI Skip] release/beta 8.0.6-15
skip-checks: true
2021-12-03 15:29:06 +00:00
Jaco 1eba41b2e6 Use @noble/secp256k1 (#1189)
* Use @noble/secp256k1

* Remove elliptic

* Remove @types

* -19

* verify uses recover internally

* fromSeed

* Init Noble utils inside cryptoInit

* remove bn.js dep (used from @polkadot/util)

* With x-noble-secp256k1

* Adjust

* Remove unused functions

* CHANGELOG

* Fix useless assignment

* Cleanup cryptoInit

* adjust hasher

* bn.js only via util

* align versions

* Remove x-secp256k1 (to be pulled in from master merge)

* Remove (now) invalid test

* Remove assertBigInt

* hasBigInt

* cleanups

* Apply suggestions from code review

* evmAddress fixes

* Extra comment
2021-12-03 17:23:46 +02:00
github-actions[bot] 88c2d16951 [CI Skip] release/beta 8.0.6-14
skip-checks: true
2021-12-03 14:28:20 +00:00
Jaco 303a57c4b5 Use secp256k1 from wasm (#1276)
* Use secp256k1 from wasm

* Expand & recover

* sign (tests failing)

* Pass onlyJs flags everywhere

* Adjust, all ready to actually pass... they don't

* naming

* Fix verify tests

* Adjust verify with non-hashing

* bump wasm

* Allow verify against address or publicKey

* Passing tests

* comment for wasm

* bi/bn versions for tweakadd

* Bumps

* Adjust
2021-12-03 16:23:41 +02:00
github-actions[bot] 7bfca0d40f [CI Skip] release/beta 8.0.6-13
skip-checks: true
2021-12-03 13:47:26 +00:00
Jaco 916187dbf2 Add x-noble-secp256k1 (#1279)
* Add x-noble-secp256k1

* Ignores

* Remove unused functions

* Fix jest
2021-12-03 15:42:57 +02:00
github-actions[bot] cfc598b30f [CI Skip] release/beta 8.0.6-12
skip-checks: true
2021-12-03 12:06:48 +00:00
Jaco 67bfe3a52e Bump deps (#1278) 2021-12-03 14:01:30 +02:00
github-actions[bot] 0299cba18e [CI Skip] release/beta 8.0.6-11
skip-checks: true
2021-12-02 13:51:53 +00:00
Jaco 182c6770d1 Bump dev (#1277) 2021-12-02 15:46:22 +02:00
github-actions[bot] c89ec8a964 [CI Skip] release/beta 8.0.6-10
skip-checks: true
2021-12-02 06:53:44 +00:00
Jaco dc63304735 Remove unused x-noble-hashes functions (#1275) 2021-12-02 08:48:44 +02:00
github-actions[bot] 9ce825de94 [CI Skip] release/beta 8.0.6-9
skip-checks: true
2021-12-02 05:37:08 +00:00
Jaco 3cb41c3b03 CHANGELOG 2021-12-02 07:32:47 +02:00
Jaco b1f9c37f6a x-bigint package (#1274)
* x-bigint package

* x-bigint in has*

* 8.0.6-8

* Adjust

* Adjust

* jest config

* Adjust

* Remove commented code

* Update README

* CHANGELOG
2021-12-02 07:29:44 +02:00
github-actions[bot] 4a69486993 [CI Skip] release/beta 8.0.6-8
skip-checks: true
2021-12-01 19:41:34 +00:00
Jaco 3a7ab0454f instanceof BigInt (#1273) 2021-12-01 21:37:21 +02:00
github-actions[bot] ecfc262bcd [CI Skip] release/beta 8.0.6-7
skip-checks: true
2021-12-01 18:41:25 +00:00
Jaco 776a653f42 Use BigInt only via xglobal (#1272)
* Use BigInt only via xglobal

* Adjust

* yarn
2021-12-01 20:37:11 +02:00
github-actions[bot] ee99ffdbb4 [CI Skip] release/beta 8.0.6-6
skip-checks: true
2021-12-01 16:15:34 +00:00
Jaco bda848f484 Expand has checks (#1271) 2021-12-01 18:11:03 +02:00
github-actions[bot] 82a63887a6 [CI Skip] release/beta 8.0.6-5
skip-checks: true
2021-12-01 14:25:07 +00:00
Jaco 50317d7dc3 Adjust for anon (#1269) 2021-12-01 16:19:50 +02:00
github-actions[bot] 39e6a3c048 [CI Skip] release/beta 8.0.6-4
skip-checks: true
2021-12-01 13:33:02 +00:00
Jaco db93c3076b Revert "Remove ed2curve dependency (#1265)" (#1267)
* Revert "Remove ed2curve dependency (#1265)"

This reverts commit 7e9b2770b6.

* Adjust
2021-12-01 15:28:42 +02:00
Jaco c8f0ad55c4 Split into @polkadot/x-noble-hashes (indicating diff from upstream) (#1268)
* Aplit @noble/hashes fork

* Re-add original (non-eslint)

* Original package imports in README

* package.json

* Update packages/x-noble-hashes/package.json

* CC ignore

* jest
2021-12-01 15:27:29 +02:00
github-actions[bot] 2104c50bd0 [CI Skip] release/beta 8.0.6-3
skip-checks: true
2021-12-01 09:56:10 +00:00
Jaco 7e9b2770b6 Remove ed2curve dependency (#1265)
* Remove edcurve dependency

* browser overrides

* Remove bundledDependencies
2021-12-01 11:51:55 +02:00
github-actions[bot] 8bf24c2f85 [CI Skip] release/beta 8.0.6-2
skip-checks: true
2021-12-01 08:04:23 +00:00
Jaco 60337a3b55 Adjust stale cron (#1264) 2021-12-01 09:59:19 +02:00
github-actions[bot] f527c80b5c [CI Skip] release/beta 8.0.6-1
skip-checks: true
2021-12-01 07:26:29 +00:00
Jaco bdd2bcf236 camelCase with mixed uppercase/numbers (#1260)
* camelCase with mixed uppercase/numbers

* consequtive capitals

* CHANGELOG

* CHANGELOG
2021-12-01 09:21:31 +02:00
github-actions[bot] cf370febf0 [CI Skip] release/beta 8.0.6-0
skip-checks: true
2021-12-01 07:19:00 +00:00
Hussein Ait-Lahcen 715ef6e7db Add Picasso Genesis Hash (#1261) 2021-12-01 09:14:36 +02:00
github-actions[bot] cc644e85bf [CI Skip] release/stable 8.0.5
skip-checks: true
2021-12-01 06:00:21 +00:00
Jaco c7b8d95097 Adjust sha3 expectation of BigInt availability (#1263) 2021-12-01 07:54:50 +02:00
github-actions[bot] 42de7cebe1 [CI Skip] release/stable 8.0.4
skip-checks: true
2021-11-30 05:16:59 +00:00
Jaco 2e520c933a 8.0.4 (#1259) 2021-11-30 07:12:55 +02:00
github-actions[bot] 5d1dd0dd27 [CI Skip] release/stable 8.0.3
skip-checks: true
2021-11-30 04:37:24 +00:00
Jaco 154f9f22f1 8.0.3 (#1258) 2021-11-30 06:33:07 +02:00
github-actions[bot] 98e398954e [CI Skip] release/beta 8.0.3-3
skip-checks: true
2021-11-29 16:38:10 +00:00
Jaco 3643c2e306 Adjust string{Camel, Pascal}Case (#1256) 2021-11-29 18:34:04 +02:00
github-actions[bot] 80195f6d11 [CI Skip] release/beta 8.0.3-2
skip-checks: true
2021-11-27 10:55:11 +00:00
Jaco 6bc648bfe0 Some tweaks on base{32, 58, 64} (#1254)
* Some tweaks on base{32, 58, 64}

* align

* rationale
2021-11-27 12:50:40 +02:00
github-actions[bot] c29b12d3b5 [CI Skip] release/beta 8.0.3-1
skip-checks: true
2021-11-27 07:28:39 +00:00
Jaco 8a5b514944 Additional tiny base64 decoder tests (#1252)
* Additional tiny base64 decoder tests

* Update packages/util-crypto/src/base64/encode.spec.ts

* Update packages/x-randomvalues/src/base64.spec.ts
2021-11-27 09:23:18 +02:00
github-actions[bot] 64fd66bd2a [CI Skip] release/beta 8.0.3-0
skip-checks: true
2021-11-27 06:09:53 +00:00
Jaco 75aa7ba2c7 Tiny base64 decoder (no Buffer dep) (#1251)
* Tiny base64 decoder (no Buffer dep)

* CHANGELOG
2021-11-27 08:05:34 +02:00
github-actions[bot] 25196c3367 [CI Skip] release/stable 8.0.2
skip-checks: true
2021-11-26 09:58:36 +00:00
Jaco eeb68c7e0f Explicit inclusion (patch-package + bundled is problematic) (#1250)
* Explicit inclusion (patch-package + bundled is problematic)

* 8.0.2
2021-11-26 11:53:45 +02:00
127 changed files with 6097 additions and 1359 deletions
+5 -1
View File
@@ -19,4 +19,8 @@
7.0.3
7.1.2
7.8.2
7.9.2
7.9.2
8.0.2
8.0.3
8.0.4
8.0.5
+2
View File
@@ -3,3 +3,5 @@ exclude_patterns:
- "**/*.spec.ts"
- "packages/networks/src/substrate.ts"
- "packages/util/src/is/utf8.ts"
- "packages/x-noble-hashes/src/**.ts"
- "packages/x-noble-secp256k1/src/**.ts"
+2
View File
@@ -0,0 +1,2 @@
packages/x-noble-hashes/**/*.ts
packages/x-noble-secp256k1/**/*.ts
+1 -1
View File
@@ -2,7 +2,7 @@ name: 'Lock Threads'
on:
schedule:
- cron: '15 1 * * *'
- cron: '15 1/3 * * *'
jobs:
lock:
+1 -1
View File
@@ -1,7 +1,7 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '50 1 * * *'
- cron: '50 1/3 * * *'
jobs:
stale:
+45
View File
@@ -1,5 +1,50 @@
# CHANGELOG
## 8.1.1 Dec 5, 2021
Contributed:
- Add Picasso genesisHash (Thanks to https://github.com/hussein-aitlahcen)
Changes:
- Allow for use of `secp256k1` from `@polkadot/wasm-crypto`
- Adjust `isBigInt` to not do `instanceof` check
- Ensure consequtive capitals in `stringCamelCase` are lowercased
- Use `@polkadot/x-bigint` for `BigInt` constructor
- Split `@noble/hashes` fork into `x-noble-hashes`
- Add `@noble/secp256k1` fork into `x-noble-secp256k1`, replaces `elliptic`
## 8.0.5 Dec 1, 2021
Changes:
- Adjust sha3 expectation of `BigInt` availability
## 8.0.4 Nov 30, 2021
Changes:
- Adjust `stringCamelCase` with leading `_`
## 8.0.3 Nov 30, 2021
Changes:
- Remove `Buffer` usage in `x-randomvalues`
- Adjust `stringCamelCase` for all-caps parts
## 8.0.2 Nov 26, 2021
Changes:
- Explicitly include BigInt patched versions
## 8.0.1 Nov 26, 2021
**Important** In the 8.0 version the hashing utilities have been consolidated around an optimized and audited version from `@noble/hashes`. As in the past the WASM hashing will be used by default if the environment is initialized.
+2 -2
View File
@@ -13,8 +13,8 @@ module.exports = Object.assign({}, config, {
'@polkadot/util-(crypto)(.*)$': '<rootDir>/packages/util-$1/src/$2',
// eslint-disable-next-line sort-keys
'@polkadot/util(.*)$': '<rootDir>/packages/util/src/$1',
'@polkadot/x-(bigint|global)(.*)$': '<rootDir>/packages/x-$1/src/$2',
'@polkadot/x-(fetch|randomvalues|textdecoder|textencoder|ws)(.*)$': '<rootDir>/packages/x-$1/src/node',
'@polkadot/x-global(.*)$': '<rootDir>/packages/x-global/src/$1',
'@polkadot/x-rxjs(.*)$': '<rootDir>/packages/x-rxjs/src/$1'
'@polkadot/x-noble-(hashes|secp256k1)(.*)$': '<rootDir>/packages/x-noble-$1/src/$2'
}
});
+5 -6
View File
@@ -10,7 +10,7 @@
"url": "https://github.com/polkadot-js/common.git"
},
"sideEffects": false,
"version": "8.0.1",
"version": "8.1.1",
"workspaces": [
"packages/*"
],
@@ -22,17 +22,16 @@
"lint": "polkadot-dev-run-lint",
"networks:crosscheck": "polkadot-dev-run-test packages/networks/src/crosscheck",
"networks:sync": "yarn build && node packages/networks/scripts/fromSubstrate.mjs",
"postinstall": "polkadot-dev-yarn-only && patch-package --error-on-fail --patch-dir packages/util-crypto/src/patches",
"postinstall": "polkadot-dev-yarn-only",
"test": "polkadot-dev-run-test --coverage --runInBand --testPathIgnorePatterns crosscheck --testPathIgnorePatterns toMiniSecretCmp",
"test:mnemonicCmp": "polkadot-dev-run-test packages/util-crypto/src/mnemonic/toMiniSecretCmp.spec.ts",
"test:one": "polkadot-dev-run-test --runInBand"
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@polkadot/dev": "^0.63.33",
"@polkadot/ts": "^0.4.15",
"@types/jest": "^27.0.3",
"patch-package": "^6.4.7"
"@polkadot/dev": "^0.63.35",
"@polkadot/ts": "^0.4.16",
"@types/jest": "^27.0.3"
},
"resolutions": {
"typescript": "^4.5.2"
+1 -1
View File
@@ -16,7 +16,7 @@
},
"sideEffects": false,
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"browser": "browser.js",
"main": "node.js",
"react-native": "react-native.js",
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.1.1' };
+3 -3
View File
@@ -19,12 +19,12 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/hw-ledger-transports": "8.0.1",
"@polkadot/util": "8.0.1",
"@polkadot/hw-ledger-transports": "8.1.1",
"@polkadot/util": "8.1.1",
"@zondax/ledger-substrate": "^0.18.0"
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.1.1' };
+5 -5
View File
@@ -20,15 +20,15 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/util": "8.0.1",
"@polkadot/util-crypto": "8.0.1"
"@polkadot/util": "8.1.1",
"@polkadot/util-crypto": "8.1.1"
},
"peerDependencies": {
"@polkadot/util": "8.0.1",
"@polkadot/util-crypto": "8.0.1"
"@polkadot/util": "8.1.1",
"@polkadot/util-crypto": "8.1.1"
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/keyring', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/keyring', version: '8.1.1' };
+3 -3
View File
@@ -17,13 +17,13 @@
},
"sideEffects": false,
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3"
},
"devDependencies": {
"@polkadot/util": "8.0.1",
"@polkadot/x-fetch": "8.0.1"
"@polkadot/util": "8.1.1",
"@polkadot/x-fetch": "8.1.1"
}
}
+3
View File
@@ -51,6 +51,9 @@ export const knownGenesis: KnownGenesis = {
'nodle-chain': [
'0xa3d114c2b8d0627c1aa9b134eafcf7d05ca561fdc19fb388bb9457f81809fb23'
],
picasso: [
'0xe8e7f0f4c4f5a00720b4821dbfddefea7490bcf0b19009961cc46957984e2c1c'
],
plasm: [
'0x3e86364d4b4894021cb2a0390bcf2feb5517d5292f2de2bb9404227e908b0b8b'
],
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/networks', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/networks', version: '8.1.1' };
+15 -14
View File
@@ -1,9 +1,6 @@
{
"author": "Jaco Greeff <jacogr@gmail.com>",
"bugs": "https://github.com/polkadot-js/common/issues",
"bundledDependencies": [
"@noble/hashes"
],
"contributors": [],
"description": "A collection of useful crypto utilities for @polkadot",
"engines": {
@@ -25,27 +22,31 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"browser": {
"crypto": false,
"stream": false
},
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@noble/hashes": "0.4.1",
"@polkadot/networks": "8.0.1",
"@polkadot/util": "8.0.1",
"@polkadot/wasm-crypto": "^4.4.1",
"@polkadot/x-randomvalues": "8.0.1",
"bn.js": "^4.12.0",
"@noble/secp256k1": "^1.3.0",
"@polkadot/networks": "8.1.1",
"@polkadot/util": "8.1.1",
"@polkadot/wasm-crypto": "^4.5.1",
"@polkadot/x-bigint": "8.1.1",
"@polkadot/x-noble-hashes": "8.1.1",
"@polkadot/x-noble-secp256k1": "8.1.1",
"@polkadot/x-randomvalues": "8.1.1",
"ed2curve": "^0.3.0",
"elliptic": "^6.5.4",
"micro-base": "^0.9.0",
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@types/bn.js": "^4.11.6",
"@types/ed2curve": "^0.2.2",
"@types/elliptic": "^6.4.14"
"@types/ed2curve": "^0.2.2"
},
"peerDependencies": {
"@polkadot/util": "8.0.1"
"@polkadot/util": "8.1.1"
}
}
@@ -5,9 +5,9 @@ import type { HexString } from '@polkadot/util/types';
import type { HashType } from '../secp256k1/types';
import type { Prefix } from './types';
import { u8aConcat } from '@polkadot/util';
import { assert, u8aConcat } from '@polkadot/util';
import { secp256k1Hasher } from '../secp256k1/hasher';
import { hasher } from '../secp256k1/hasher';
import { encodeAddress } from './encode';
/**
@@ -15,15 +15,9 @@ import { encodeAddress } from './encode';
* @summary Converts an EVM address to its corresponding SS58 address.
*/
export function evmToAddress (evmAddress: HexString | string | Uint8Array, ss58Format?: Prefix, hashType: HashType = 'blake2'): string {
const wrapError = (message: string) => `Converting ${evmAddress as string}: ${message}`;
const message = u8aConcat('evm:', evmAddress);
if (message.length !== 24) {
throw new Error(wrapError('Invalid evm address length'));
}
assert(message.length === 24, () => `Converting ${evmAddress as string}: Invalid evm address length`);
const address = secp256k1Hasher(hashType, message);
return encodeAddress(address, ss58Format);
return encodeAddress(hasher(hashType, message), ss58Format);
}
+8 -8
View File
@@ -5,20 +5,20 @@ import { utils } from 'micro-base';
import { createDecode, createEncode, createIs, createValidate } from './helpers';
const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
const chars = 'abcdefghijklmnopqrstuvwxyz234567';
const BASE32_CONFIG = {
alphabet: BASE32_ALPHABET,
const config = {
chars,
coder: utils.chain(
// We define our own chain, the default base32 has padding
utils.radix2(5),
utils.alphabet(BASE32_ALPHABET),
utils.alphabet(chars),
{
decode: (input: string) => input.split(''),
encode: (input: string[]) => input.join('')
}
),
ipfsChar: 'b',
ipfs: 'b',
type: 'base32'
};
@@ -28,7 +28,7 @@ const BASE32_CONFIG = {
* @description
* Validates that the supplied value is valid base32, throwing exceptions if not
*/
export const base32Validate = createValidate(BASE32_CONFIG);
export const base32Validate = createValidate(config);
/**
* @name isBase32
@@ -42,7 +42,7 @@ export const isBase32 = createIs(base32Validate);
* @description
* From the provided input, decode the base32 and return the result as an `Uint8Array`.
*/
export const base32Decode = createDecode(BASE32_CONFIG, base32Validate);
export const base32Decode = createDecode(config, base32Validate);
/**
* @name base32Encode
@@ -50,4 +50,4 @@ export const base32Decode = createDecode(BASE32_CONFIG, base32Validate);
* @description
* From the provided input, create the base32 and return the result as a string.
*/
export const base32Encode = createEncode(BASE32_CONFIG);
export const base32Encode = createEncode(config);
+20 -16
View File
@@ -21,9 +21,9 @@ interface Coder {
}
interface Config {
alphabet: string;
chars: string;
coder: Coder;
ipfsChar?: string;
ipfs?: string;
regex?: RegExp;
type: string;
}
@@ -34,24 +34,24 @@ type EncodeFn = (value: U8aLike, ipfsCompat?: boolean) => string;
type ValidateFn = (value?: unknown, ipfsCompat?: boolean) => value is string;
export function createDecode ({ coder, ipfsChar }: Config, validate: ValidateFn): DecodeFn {
export function createDecode ({ coder, ipfs }: Config, validate: ValidateFn): DecodeFn {
return (value: string, ipfsCompat?: boolean): Uint8Array => {
validate(value, ipfsCompat);
return coder.decode(
ipfsChar && ipfsCompat
ipfs && ipfsCompat
? value.substr(1)
: value
);
};
}
export function createEncode ({ coder, ipfsChar }: Config): EncodeFn {
export function createEncode ({ coder, ipfs }: Config): EncodeFn {
return (value: U8aLike, ipfsCompat?: boolean): string => {
const out = coder.encode(u8aToU8a(value));
return ipfsChar && ipfsCompat
? `${ipfsChar}${out}`
return ipfs && ipfsCompat
? `${ipfs}${out}`
: out;
};
}
@@ -66,20 +66,24 @@ export function createIs (validate: ValidateFn): ValidateFn {
};
}
export function createValidate ({ alphabet, ipfsChar, regex, type }: Config): ValidateFn {
export function createValidate ({ chars, ipfs, type }: Config): ValidateFn {
return (value?: unknown, ipfsCompat?: boolean): value is string => {
assert(value && typeof value === 'string', () => `Expected non-null, non-empty ${type} string input`);
if (ipfsChar && ipfsCompat) {
assert(value[0] === ipfsChar, () => `Expected ipfs-compatible ${type} to start with '${ipfsChar}'`);
if (ipfs && ipfsCompat) {
assert(value[0] === ipfs, () => `Expected ipfs-compatible ${type} to start with '${ipfs}'`);
}
if (regex) {
assert(regex.test(value), `Invalid ${type} encoding`);
} else {
for (let i = (ipfsCompat ? 1 : 0); i < value.length; i++) {
assert(alphabet.includes(value[i]), () => `Invalid ${type} character "${value[i]}" (0x${value.charCodeAt(i).toString(16)}) at index ${i}`);
}
for (let i = (ipfsCompat ? 1 : 0); i < value.length; i++) {
assert(
chars.includes(value[i]) || (
value[i] === '=' && (
(i === value.length - 1) ||
!chars.includes(value[i + 1])
)
),
() => `Invalid ${type} character "${value[i]}" (0x${value.charCodeAt(i).toString(16)}) at index ${i}`
);
}
return true;
+6 -6
View File
@@ -5,10 +5,10 @@ import { base58 } from 'micro-base';
import { createDecode, createEncode, createIs, createValidate } from '../base32/helpers';
const BASE58_CONFIG = {
alphabet: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
const config = {
chars: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
coder: base58,
ipfsChar: 'z',
ipfs: 'z',
type: 'base58'
};
@@ -18,7 +18,7 @@ const BASE58_CONFIG = {
* @description
* Validates that the supplied value is valid base58, throwing exceptions if not
*/
export const base58Validate = createValidate(BASE58_CONFIG);
export const base58Validate = createValidate(config);
/**
* @name base58Decode
@@ -26,7 +26,7 @@ export const base58Validate = createValidate(BASE58_CONFIG);
* @description
* From the provided input, decode the base58 and return the result as an `Uint8Array`.
*/
export const base58Decode = createDecode(BASE58_CONFIG, base58Validate);
export const base58Decode = createDecode(config, base58Validate);
/**
* @name base58Encode
@@ -34,7 +34,7 @@ export const base58Decode = createDecode(BASE58_CONFIG, base58Validate);
* @description
* From the provided input, create the base58 and return the result as a string.
*/
export const base58Encode = createEncode(BASE58_CONFIG);
export const base58Encode = createEncode(config);
/**
* @name isBase58
+5 -6
View File
@@ -5,10 +5,9 @@ import { base64 } from 'micro-base';
import { createDecode, createEncode, createIs, createValidate } from '../base32/helpers';
const BASE64_CONFIG = {
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
const config = {
chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
coder: base64,
regex: /^(?:[A-Za-z0-9+/]{2}[A-Za-z0-9+/]{2})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,
type: 'base64'
};
@@ -18,7 +17,7 @@ const BASE64_CONFIG = {
* @description
* Validates that the supplied value is valid base64
*/
export const base64Validate = createValidate(BASE64_CONFIG);
export const base64Validate = createValidate(config);
/**
* @name isBase64
@@ -32,7 +31,7 @@ export const isBase64 = createIs(base64Validate);
* @description
* From the provided input, decode the base64 and return the result as an `Uint8Array`.
*/
export const base64Decode = createDecode(BASE64_CONFIG, base64Validate);
export const base64Decode = createDecode(config, base64Validate);
/**
* @name base64Encode
@@ -40,4 +39,4 @@ export const base64Decode = createDecode(BASE64_CONFIG, base64Validate);
* @description
* From the provided input, create the base64 and return the result as a string.
*/
export const base64Encode = createEncode(BASE64_CONFIG);
export const base64Encode = createEncode(config);
+17 -1
View File
@@ -6,11 +6,27 @@ import { stringToU8a } from '@polkadot/util';
import { base64Decode } from '.';
describe('base64Decode', (): void => {
it('decodes a mixed base64 utf8 string', (): void => {
it('decodes a mixed base64 utf8 string (1)', (): void => {
expect(
base64Decode('aGVsbG8gd29ybGQg0J/RgNC40LLQtdGC0YHRgtCy0YPRjiDQvNC4IOS9oOWlvQ==')
).toEqual(
stringToU8a('hello world Приветствую ми 你好')
);
});
it('decodes a mixed base64 utf8 string (2)', (): void => {
expect(
base64Decode('4pyTIMOgIGxhIG1vZGU=')
).toEqual(
stringToU8a('✓ à la mode')
);
});
it('decodes a mixed base64 utf8 string (3)', (): void => {
expect(
base64Decode('SGVsbG8gV29ybGQh')
).toEqual(
stringToU8a('Hello World!')
);
});
});
@@ -7,6 +7,6 @@ describe('base64Validate', (): void => {
it('validates a mixed base64 utf8 string', (): void => {
expect(
() => base64Validate('aGVsbG8gd29ybGQg0J/RgNC40LLQtd^GC0YHRgtCy0YPRjiDQvNC4IOS9oOWlvQ==')
).toThrow(/Invalid base64 encoding/);
).toThrow(/Invalid base64 character "\^" \(0x5e\) at index 30/);
});
});
+1 -2
View File
@@ -3,10 +3,9 @@
import type { HexString } from '@polkadot/util/types';
import { blake2b as blake2bJs } from '@noble/hashes/lib/blake2b';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { blake2b, isReady } from '@polkadot/wasm-crypto';
import { blake2b as blake2bJs } from '@polkadot/x-noble-hashes/blake2b';
import { createAsHex } from '../helpers';
+1
View File
@@ -1,6 +1,7 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
export const BN_BE_OPTS = { isLe: false };
export const BN_LE_OPTS = { isLe: true };
export const BN_LE_16_OPTS = { bitLength: 16, isLe: true };
+11
View File
@@ -1,7 +1,18 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import '@polkadot/x-bigint/shim';
import { u8aConcat } from '@polkadot/util';
import { utils as utilsNobleSecp256k1 } from '@polkadot/x-noble-secp256k1';
import { cryptoWaitReady } from './crypto';
import { hmacSha256AsU8a } from './hmac';
// Set overrides on the secp256k1 utils
// - hmacShaSync - This needs to be set, unset by default
utilsNobleSecp256k1.hmacSha256Sync = (key: Uint8Array, ...messages: Uint8Array[]) =>
hmacSha256AsU8a(key, u8aConcat(...messages));
// start init process immediately
cryptoWaitReady().catch((): void => {
@@ -3,7 +3,8 @@
import type { Keypair } from '../../types';
import nacl from 'tweetnacl';
import { randomAsU8a } from '../../random';
import { ed25519PairFromSeed } from './fromSeed';
/**
* @name ed25519PairFromRandom
@@ -20,5 +21,5 @@ import nacl from 'tweetnacl';
* ```
*/
export function ed25519PairFromRandom (): Keypair {
return nacl.sign.keyPair();
return ed25519PairFromSeed(randomAsU8a());
}
@@ -25,8 +25,7 @@ import { ed25519PairFromSeed } from './fromSeed';
export function ed25519PairFromString (value: string): Keypair {
return ed25519PairFromSeed(
blake2AsU8a(
stringToU8a(value),
256
stringToU8a(value)
)
);
}
+3 -4
View File
@@ -1,12 +1,11 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { hmac } from '@noble/hashes/lib/hmac';
import { sha256 } from '@noble/hashes/lib/sha256';
import { sha512 } from '@noble/hashes/lib/sha512';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { hmacSha256, hmacSha512, isReady } from '@polkadot/wasm-crypto';
import { hmac } from '@polkadot/x-noble-hashes/hmac';
import { sha256 } from '@polkadot/x-noble-hashes/sha256';
import { sha512 } from '@polkadot/x-noble-hashes/sha512';
type BitLength = 256 | 512;
+1 -2
View File
@@ -3,10 +3,9 @@
import type { HexString } from '@polkadot/util/types';
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '@noble/hashes/lib/sha3';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, keccak256, keccak512 } from '@polkadot/wasm-crypto';
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '@polkadot/x-noble-hashes/sha3';
import { createAsHex, createBitHasher } from '../helpers';
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.1.1' };
@@ -1,268 +0,0 @@
diff --git a/node_modules/@noble/hashes/lib/_sha2.js b/node_modules/@noble/hashes/lib/_sha2.js
index 3a7e7d7..0517727 100644
--- a/node_modules/@noble/hashes/lib/_sha2.js
+++ b/node_modules/@noble/hashes/lib/_sha2.js
@@ -2,12 +2,14 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.SHA2 = void 0;
const utils_1 = require("./utils");
+const _32n = (0, utils_1._n)(32);
+const _u32_max = (0, utils_1._n)(0xffffffff);
// Polyfill for Safari 14
function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === 'function')
return view.setBigUint64(byteOffset, value, isLE);
- const wh = Number((value >> 32n) & 0xffffffffn);
- const wl = Number(value & 0xffffffffn);
+ const wh = Number((value >> _32n) & _u32_max);
+ const wl = Number(value & _u32_max);
const [h, l] = isLE ? [4, 0] : [0, 4];
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
diff --git a/node_modules/@noble/hashes/lib/_u64.js b/node_modules/@noble/hashes/lib/_u64.js
index 396889a..f958bd5 100644
--- a/node_modules/@noble/hashes/lib/_u64.js
+++ b/node_modules/@noble/hashes/lib/_u64.js
@@ -1,11 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.add5H = exports.add5L = exports.add4H = exports.add4L = exports.add3H = exports.add3L = exports.add = exports.rotlBL = exports.rotlBH = exports.rotlSL = exports.rotlSH = exports.rotr32L = exports.rotr32H = exports.rotrBL = exports.rotrBH = exports.rotrSL = exports.rotrSH = exports.shrSL = exports.shrSH = exports.toBig = exports.split = exports.fromBig = void 0;
-const U32_MASK64 = 2n ** 32n - 1n;
+const utils_1 = require("./utils");
+const U32_MASK64 = (0, utils_1._n)(2 ** 32 - 1);
+const _32n = (0, utils_1._n)(32);
function fromBig(n, le = false) {
if (le)
- return { h: Number(n & U32_MASK64), l: Number((n >> 32n) & U32_MASK64) };
- return { h: Number((n >> 32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
+ return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
+ return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
}
exports.fromBig = fromBig;
function split(lst, le = false) {
@@ -17,7 +19,7 @@ function split(lst, le = false) {
return [Ah, Al];
}
exports.split = split;
-const toBig = (h, l) => (BigInt(h >>> 0) << 32n) | BigInt(l >>> 0);
+const toBig = (h, l) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);
exports.toBig = toBig;
// for Shift in [0, 32)
const shrSH = (h, l, s) => h >>> s;
diff --git a/node_modules/@noble/hashes/lib/blake3.d.ts b/node_modules/@noble/hashes/lib/blake3.d.ts
index 80f8856..3ab02fc 100644
--- a/node_modules/@noble/hashes/lib/blake3.d.ts
+++ b/node_modules/@noble/hashes/lib/blake3.d.ts
@@ -27,8 +27,8 @@ declare class BLAKE3 extends blake2.BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {
private b2CompressOut;
protected finish(): void;
private writeInto;
- XOFInto(out: Uint8Array): Uint8Array;
- XOF(bytes: number): Uint8Array;
+ xofInto(out: Uint8Array): Uint8Array;
+ xof(bytes: number): Uint8Array;
digestInto(out: Uint8Array): Uint8Array;
digest(): Uint8Array;
}
diff --git a/node_modules/@noble/hashes/lib/blake3.js b/node_modules/@noble/hashes/lib/blake3.js
index bfc7a19..52b28f0 100644
--- a/node_modules/@noble/hashes/lib/blake3.js
+++ b/node_modules/@noble/hashes/lib/blake3.js
@@ -229,14 +229,14 @@ class BLAKE3 extends blake2.BLAKE2 {
}
return out;
}
- XOFInto(out) {
+ xofInto(out) {
if (!this.enableXOF)
throw new Error('XOF impossible after digest call');
return this.writeInto(out);
}
- XOF(bytes) {
+ xof(bytes) {
(0, utils_1.assertNumber)(bytes);
- return this.XOFInto(new Uint8Array(bytes));
+ return this.xofInto(new Uint8Array(bytes));
}
digestInto(out) {
if (out.length < this.outputLen)
diff --git a/node_modules/@noble/hashes/lib/sha3-addons.js b/node_modules/@noble/hashes/lib/sha3-addons.js
index 51793e2..6a9eef7 100644
--- a/node_modules/@noble/hashes/lib/sha3-addons.js
+++ b/node_modules/@noble/hashes/lib/sha3-addons.js
@@ -321,7 +321,7 @@ class KeccakPRG extends sha3_1.Keccak {
throw new Error('KeccakPRG: digest is not allowed, please use .fetch instead.');
}
fetch(bytes) {
- return this.XOF(bytes);
+ return this.xof(bytes);
}
// Ensure irreversibility (even if state leaked previous outputs cannot be computed)
forget() {
diff --git a/node_modules/@noble/hashes/lib/sha3.d.ts b/node_modules/@noble/hashes/lib/sha3.d.ts
index 6f0a062..1105727 100644
--- a/node_modules/@noble/hashes/lib/sha3.d.ts
+++ b/node_modules/@noble/hashes/lib/sha3.d.ts
@@ -17,8 +17,8 @@ export declare class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
update(data: Input): this;
protected finish(): void;
protected writeInto(out: Uint8Array): Uint8Array;
- XOFInto(out: Uint8Array): Uint8Array;
- XOF(bytes: number): Uint8Array;
+ xofInto(out: Uint8Array): Uint8Array;
+ xof(bytes: number): Uint8Array;
digestInto(out: Uint8Array): Uint8Array;
digest(): Uint8Array;
destroy(): void;
diff --git a/node_modules/@noble/hashes/lib/sha3.js b/node_modules/@noble/hashes/lib/sha3.js
index 91adaf4..41a84b4 100644
--- a/node_modules/@noble/hashes/lib/sha3.js
+++ b/node_modules/@noble/hashes/lib/sha3.js
@@ -22,20 +22,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.shake256 = exports.shake128 = exports.keccak_512 = exports.keccak_384 = exports.keccak_256 = exports.keccak_224 = exports.sha3_512 = exports.sha3_384 = exports.sha3_256 = exports.sha3_224 = exports.Keccak = exports.keccakP = void 0;
const u64 = __importStar(require("./_u64"));
const utils_1 = require("./utils");
+const _0n = (0, utils_1._n)(0);
+const _1n = (0, utils_1._n)(1);
+const _2n = (0, utils_1._n)(2);
+const _7n = (0, utils_1._n)(7);
+const _256n = (0, utils_1._n)(256);
+const _0x71n = (0, utils_1._n)(0x71);
// Various per round constants calculations
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA] = [[], [], []];
-for (let round = 0, R = 1n, x = 1, y = 0; round < 24; round++) {
+for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
// Pi
[x, y] = [y, (2 * x + 3 * y) % 5];
SHA3_PI.push(2 * (5 * y + x));
// Rotational
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
// Iota
- let t = 0n;
+ let t = _0n;
for (let j = 0; j < 7; j++) {
- R = ((R << 1n) ^ ((R >> 7n) * 0x71n)) % 256n;
- if (R & 2n)
- t ^= 1n << ((1n << BigInt(j)) - 1n);
+ R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
+ if (R & _2n)
+ t ^= _1n << ((_1n << BigInt(j)) - _1n);
}
_SHA3_IOTA.push(t);
}
@@ -161,15 +167,15 @@ class Keccak extends utils_1.Hash {
}
return out;
}
- XOFInto(out) {
+ xofInto(out) {
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
if (!this.enableXOF)
throw new Error('XOF is not possible for this instance');
return this.writeInto(out);
}
- XOF(bytes) {
+ xof(bytes) {
(0, utils_1.assertNumber)(bytes);
- return this.XOFInto(new Uint8Array(bytes));
+ return this.xofInto(new Uint8Array(bytes));
}
digestInto(out) {
if (out.length < this.outputLen)
diff --git a/node_modules/@noble/hashes/lib/sha512.js b/node_modules/@noble/hashes/lib/sha512.js
index 567bd80..0a733ab 100644
--- a/node_modules/@noble/hashes/lib/sha512.js
+++ b/node_modules/@noble/hashes/lib/sha512.js
@@ -26,26 +26,26 @@ const utils_1 = require("./utils");
// Round contants (first 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409):
// prettier-ignore
const [SHA512_Kh, SHA512_Kl] = u64.split([
- 0x428a2f98d728ae22n, 0x7137449123ef65cdn, 0xb5c0fbcfec4d3b2fn, 0xe9b5dba58189dbbcn,
- 0x3956c25bf348b538n, 0x59f111f1b605d019n, 0x923f82a4af194f9bn, 0xab1c5ed5da6d8118n,
- 0xd807aa98a3030242n, 0x12835b0145706fben, 0x243185be4ee4b28cn, 0x550c7dc3d5ffb4e2n,
- 0x72be5d74f27b896fn, 0x80deb1fe3b1696b1n, 0x9bdc06a725c71235n, 0xc19bf174cf692694n,
- 0xe49b69c19ef14ad2n, 0xefbe4786384f25e3n, 0x0fc19dc68b8cd5b5n, 0x240ca1cc77ac9c65n,
- 0x2de92c6f592b0275n, 0x4a7484aa6ea6e483n, 0x5cb0a9dcbd41fbd4n, 0x76f988da831153b5n,
- 0x983e5152ee66dfabn, 0xa831c66d2db43210n, 0xb00327c898fb213fn, 0xbf597fc7beef0ee4n,
- 0xc6e00bf33da88fc2n, 0xd5a79147930aa725n, 0x06ca6351e003826fn, 0x142929670a0e6e70n,
- 0x27b70a8546d22ffcn, 0x2e1b21385c26c926n, 0x4d2c6dfc5ac42aedn, 0x53380d139d95b3dfn,
- 0x650a73548baf63den, 0x766a0abb3c77b2a8n, 0x81c2c92e47edaee6n, 0x92722c851482353bn,
- 0xa2bfe8a14cf10364n, 0xa81a664bbc423001n, 0xc24b8b70d0f89791n, 0xc76c51a30654be30n,
- 0xd192e819d6ef5218n, 0xd69906245565a910n, 0xf40e35855771202an, 0x106aa07032bbd1b8n,
- 0x19a4c116b8d2d0c8n, 0x1e376c085141ab53n, 0x2748774cdf8eeb99n, 0x34b0bcb5e19b48a8n,
- 0x391c0cb3c5c95a63n, 0x4ed8aa4ae3418acbn, 0x5b9cca4f7763e373n, 0x682e6ff3d6b2b8a3n,
- 0x748f82ee5defb2fcn, 0x78a5636f43172f60n, 0x84c87814a1f0ab72n, 0x8cc702081a6439ecn,
- 0x90befffa23631e28n, 0xa4506cebde82bde9n, 0xbef9a3f7b2c67915n, 0xc67178f2e372532bn,
- 0xca273eceea26619cn, 0xd186b8c721c0c207n, 0xeada7dd6cde0eb1en, 0xf57d4f7fee6ed178n,
- 0x06f067aa72176fban, 0x0a637dc5a2c898a6n, 0x113f9804bef90daen, 0x1b710b35131c471bn,
- 0x28db77f523047d84n, 0x32caab7b40c72493n, 0x3c9ebe0a15c9bebcn, 0x431d67c49c100d4cn,
- 0x4cc5d4becb3e42b6n, 0x597f299cfc657e2an, 0x5fcb6fab3ad6faecn, 0x6c44198c4a475817n
+ (0, utils_1._n)('0x428a2f98d728ae22'), (0, utils_1._n)('0x7137449123ef65cd'), (0, utils_1._n)('0xb5c0fbcfec4d3b2f'), (0, utils_1._n)('0xe9b5dba58189dbbc'),
+ (0, utils_1._n)('0x3956c25bf348b538'), (0, utils_1._n)('0x59f111f1b605d019'), (0, utils_1._n)('0x923f82a4af194f9b'), (0, utils_1._n)('0xab1c5ed5da6d8118'),
+ (0, utils_1._n)('0xd807aa98a3030242'), (0, utils_1._n)('0x12835b0145706fbe'), (0, utils_1._n)('0x243185be4ee4b28c'), (0, utils_1._n)('0x550c7dc3d5ffb4e2'),
+ (0, utils_1._n)('0x72be5d74f27b896f'), (0, utils_1._n)('0x80deb1fe3b1696b1'), (0, utils_1._n)('0x9bdc06a725c71235'), (0, utils_1._n)('0xc19bf174cf692694'),
+ (0, utils_1._n)('0xe49b69c19ef14ad2'), (0, utils_1._n)('0xefbe4786384f25e3'), (0, utils_1._n)('0x0fc19dc68b8cd5b5'), (0, utils_1._n)('0x240ca1cc77ac9c65'),
+ (0, utils_1._n)('0x2de92c6f592b0275'), (0, utils_1._n)('0x4a7484aa6ea6e483'), (0, utils_1._n)('0x5cb0a9dcbd41fbd4'), (0, utils_1._n)('0x76f988da831153b5'),
+ (0, utils_1._n)('0x983e5152ee66dfab'), (0, utils_1._n)('0xa831c66d2db43210'), (0, utils_1._n)('0xb00327c898fb213f'), (0, utils_1._n)('0xbf597fc7beef0ee4'),
+ (0, utils_1._n)('0xc6e00bf33da88fc2'), (0, utils_1._n)('0xd5a79147930aa725'), (0, utils_1._n)('0x06ca6351e003826f'), (0, utils_1._n)('0x142929670a0e6e70'),
+ (0, utils_1._n)('0x27b70a8546d22ffc'), (0, utils_1._n)('0x2e1b21385c26c926'), (0, utils_1._n)('0x4d2c6dfc5ac42aed'), (0, utils_1._n)('0x53380d139d95b3df'),
+ (0, utils_1._n)('0x650a73548baf63de'), (0, utils_1._n)('0x766a0abb3c77b2a8'), (0, utils_1._n)('0x81c2c92e47edaee6'), (0, utils_1._n)('0x92722c851482353b'),
+ (0, utils_1._n)('0xa2bfe8a14cf10364'), (0, utils_1._n)('0xa81a664bbc423001'), (0, utils_1._n)('0xc24b8b70d0f89791'), (0, utils_1._n)('0xc76c51a30654be30'),
+ (0, utils_1._n)('0xd192e819d6ef5218'), (0, utils_1._n)('0xd69906245565a910'), (0, utils_1._n)('0xf40e35855771202a'), (0, utils_1._n)('0x106aa07032bbd1b8'),
+ (0, utils_1._n)('0x19a4c116b8d2d0c8'), (0, utils_1._n)('0x1e376c085141ab53'), (0, utils_1._n)('0x2748774cdf8eeb99'), (0, utils_1._n)('0x34b0bcb5e19b48a8'),
+ (0, utils_1._n)('0x391c0cb3c5c95a63'), (0, utils_1._n)('0x4ed8aa4ae3418acb'), (0, utils_1._n)('0x5b9cca4f7763e373'), (0, utils_1._n)('0x682e6ff3d6b2b8a3'),
+ (0, utils_1._n)('0x748f82ee5defb2fc'), (0, utils_1._n)('0x78a5636f43172f60'), (0, utils_1._n)('0x84c87814a1f0ab72'), (0, utils_1._n)('0x8cc702081a6439ec'),
+ (0, utils_1._n)('0x90befffa23631e28'), (0, utils_1._n)('0xa4506cebde82bde9'), (0, utils_1._n)('0xbef9a3f7b2c67915'), (0, utils_1._n)('0xc67178f2e372532b'),
+ (0, utils_1._n)('0xca273eceea26619c'), (0, utils_1._n)('0xd186b8c721c0c207'), (0, utils_1._n)('0xeada7dd6cde0eb1e'), (0, utils_1._n)('0xf57d4f7fee6ed178'),
+ (0, utils_1._n)('0x06f067aa72176fba'), (0, utils_1._n)('0x0a637dc5a2c898a6'), (0, utils_1._n)('0x113f9804bef90dae'), (0, utils_1._n)('0x1b710b35131c471b'),
+ (0, utils_1._n)('0x28db77f523047d84'), (0, utils_1._n)('0x32caab7b40c72493'), (0, utils_1._n)('0x3c9ebe0a15c9bebc'), (0, utils_1._n)('0x431d67c49c100d4c'),
+ (0, utils_1._n)('0x4cc5d4becb3e42b6'), (0, utils_1._n)('0x597f299cfc657e2a'), (0, utils_1._n)('0x5fcb6fab3ad6faec'), (0, utils_1._n)('0x6c44198c4a475817')
]);
// Temporary buffer, not used to store anything between runs
const SHA512_W_H = new Uint32Array(80);
diff --git a/node_modules/@noble/hashes/lib/utils.d.ts b/node_modules/@noble/hashes/lib/utils.d.ts
index 8233e80..67c4246 100644
--- a/node_modules/@noble/hashes/lib/utils.d.ts
+++ b/node_modules/@noble/hashes/lib/utils.d.ts
@@ -1,5 +1,6 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
export declare type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array | Uint16Array | Int16Array | Uint32Array | Int32Array;
+export declare const _n: (value: string | number | bigint | boolean) => bigint;
export declare const u8: (arr: TypedArray) => Uint8Array;
export declare const u32: (arr: TypedArray) => Uint32Array;
export declare const createView: (arr: TypedArray) => DataView;
@@ -24,8 +25,8 @@ export declare abstract class Hash<T extends Hash<T>> {
clone(): T;
}
export declare type HashXOF<T extends Hash<T>> = Hash<T> & {
- XOF(bytes: number): Uint8Array;
- XOFInto(buf: Uint8Array): Uint8Array;
+ xof(bytes: number): Uint8Array;
+ xofInto(buf: Uint8Array): Uint8Array;
};
declare type EmptyObj = {};
export declare function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(def: T1, _opts?: T2): T1 & T2;
@@ -46,7 +47,7 @@ export declare function wrapConstructorWithOpts<H extends Hash<H>, T extends Obj
};
export declare const crypto: {
node?: any;
- web?: Crypto;
+ web?: any;
};
export declare function randomBytes(bytesLength?: number): Uint8Array;
export {};
diff --git a/node_modules/@noble/hashes/lib/utils.js b/node_modules/@noble/hashes/lib/utils.js
index 1d45928..3811cf9 100644
--- a/node_modules/@noble/hashes/lib/utils.js
+++ b/node_modules/@noble/hashes/lib/utils.js
@@ -1,7 +1,9 @@
"use strict";
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
Object.defineProperty(exports, "__esModule", { value: true });
-exports.randomBytes = exports.crypto = exports.wrapConstructorWithOpts = exports.wrapConstructor = exports.checkOpts = exports.Hash = exports.assertHash = exports.assertBool = exports.assertNumber = exports.toBytes = exports.asyncLoop = exports.nextTick = exports.bytesToHex = exports.isLE = exports.rotr = exports.createView = exports.u32 = exports.u8 = void 0;
+exports.randomBytes = exports.crypto = exports.wrapConstructorWithOpts = exports.wrapConstructor = exports.checkOpts = exports.Hash = exports.assertHash = exports.assertBool = exports.assertNumber = exports.toBytes = exports.asyncLoop = exports.nextTick = exports.bytesToHex = exports.isLE = exports.rotr = exports.createView = exports.u32 = exports.u8 = exports._n = void 0;
+// helper to protect against older bundlers
+exports._n = typeof BigInt !== 'undefined' ? BigInt : () => Number.NaN;
// Cast array to different type
const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
exports.u8 = u8;
@@ -1,12 +0,0 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { _n } from '@noble/hashes/lib/utils';
describe('@noble/hashes', (): void => {
it('is patched', (): void => {
expect(
_n(123) === 123n
).toBe(true);
});
});
+2 -3
View File
@@ -3,11 +3,10 @@
import type { HexString } from '@polkadot/util/types';
import { pbkdf2 as pbkdf2Js } from '@noble/hashes/lib/pbkdf2';
import { sha512 } from '@noble/hashes/lib/sha512';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, pbkdf2 } from '@polkadot/wasm-crypto';
import { pbkdf2 as pbkdf2Js } from '@polkadot/x-noble-hashes/pbkdf2';
import { sha512 } from '@polkadot/x-noble-hashes/sha512';
import { randomAsU8a } from '../random/asU8a';
+1 -2
View File
@@ -4,10 +4,9 @@
import type { HexString } from '@polkadot/util/types';
import type { Params } from './types';
import { scrypt as scryptJs } from '@noble/hashes/lib/scrypt';
import { hasBigInt, objectSpread, u8aToU8a } from '@polkadot/util';
import { isReady, scrypt } from '@polkadot/wasm-crypto';
import { scrypt as scryptJs } from '@polkadot/x-noble-hashes/scrypt';
import { randomAsU8a } from '../random/asU8a';
import { DEFAULT_PARAMS } from './defaults';
@@ -2,17 +2,43 @@
// SPDX-License-Identifier: Apache-2.0
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { secp256k1Compress } from './';
import { performanceWasm } from '../../test/performance';
import { secp256k1Compress } from '.';
describe('secp256k1Compress', (): void => {
it('compresses a known key', (): void => {
expect(
secp256k1Compress(
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
)
).toEqual(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
);
beforeEach(async (): Promise<void> => {
await waitReady();
});
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('returns a compressed key as-is', (): void => {
expect(
secp256k1Compress(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'),
onlyJs
)
).toEqual(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
);
});
it('compresses a known key', (): void => {
expect(
secp256k1Compress(
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af'),
onlyJs
)
).toEqual(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
);
});
});
performanceWasm('secp256k1Compress', 100000, (input, onlyJs) => secp256k1Compress(input, onlyJs),
[[
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
]]
);
});
+11 -10
View File
@@ -1,17 +1,18 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert } from '@polkadot/util';
import { assert, hasBigInt } from '@polkadot/util';
import { isReady, secp256k1Compress as wasm } from '@polkadot/wasm-crypto';
import { Point } from '@polkadot/x-noble-secp256k1';
import { secp256k1 } from './secp256k1';
export function secp256k1Compress (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
if (publicKey.length === 33) {
return publicKey;
}
export function secp256k1Compress (publicKey: Uint8Array): Uint8Array {
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
assert(publicKey.length === 65, 'Invalid publicKey provided');
return new Uint8Array(
secp256k1
.keyFromPublic(publicKey)
.getPublic()
.encodeCompressed()
);
return !hasBigInt || (!onlyJs && isReady())
? wasm(publicKey)
: Point.fromHex(publicKey).toRawBytes(true);
}
@@ -2,27 +2,43 @@
// SPDX-License-Identifier: Apache-2.0
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { secp256k1Expand } from './';
import { performanceWasm } from '../../test/performance';
import { secp256k1Expand } from '.';
describe('secp256k1Expand', (): void => {
it('expands a known key', (): void => {
expect(
secp256k1Expand(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
)
).toEqual(
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
);
beforeEach(async (): Promise<void> => {
await waitReady();
});
it('expands a known full key', (): void => {
expect(
secp256k1Expand(
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
)
).toEqual(
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
);
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('expands a known key', (): void => {
expect(
secp256k1Expand(
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'),
onlyJs
)
).toEqual(
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
);
});
it('expands a known full key', (): void => {
expect(
secp256k1Expand(
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af'),
onlyJs
)
).toEqual(
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
);
});
});
performanceWasm('secp256k1Expand', 2000, (input, onlyJs) => secp256k1Expand(input, onlyJs),
[[
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
]]
);
});
+16 -9
View File
@@ -1,20 +1,27 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
import { isReady, secp256k1Expand as wasm } from '@polkadot/wasm-crypto';
import { Point } from '@polkadot/x-noble-secp256k1';
import { BN_BE_256_OPTS } from '../bn';
import { secp256k1 } from './secp256k1';
export function secp256k1Expand (publicKey: Uint8Array): Uint8Array {
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
export function secp256k1Expand (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
if (publicKey.length === 65) {
return publicKey.subarray(1);
}
const expanded = secp256k1
.keyFromPublic(publicKey)
.getPublic();
assert(publicKey.length === 33, 'Invalid publicKey provided');
if (!hasBigInt || (!onlyJs && isReady())) {
return wasm(publicKey).subarray(1);
}
const { x, y } = Point.fromHex(publicKey);
return u8aConcat(
bnToU8a(expanded.getX(), BN_BE_256_OPTS),
bnToU8a(expanded.getY(), BN_BE_256_OPTS)
bnToU8a(x, BN_BE_256_OPTS),
bnToU8a(y, BN_BE_256_OPTS)
);
}
@@ -1,18 +1,12 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { secp256k1Hasher } from './hasher';
describe('secp256k1Hasher', (): void => {
it('fails with unknown hasher', (): void => {
expect(
() => secp256k1Hasher('unknown' as 'blake2', 'testing')
).toThrow(/Unsupported secp256k1 hasher 'unknown', expected one of blake2, keccak/);
});
import { hasher } from './hasher';
describe('hasher', (): void => {
it('creates a blake2 hash', (): void => {
expect(
secp256k1Hasher('blake2', 'abc')
hasher('blake2', 'abc')
).toEqual(
new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25])
);
@@ -20,7 +14,7 @@ describe('secp256k1Hasher', (): void => {
it('creates a keccak hash', (): void => {
expect(
secp256k1Hasher('keccak', 'abc')
hasher('keccak', 'abc')
).toEqual(
new Uint8Array([78, 3, 101, 122, 234, 69, 169, 79, 199, 212, 123, 168, 38, 200, 214, 103, 192, 209, 230, 227, 58, 100, 160, 54, 236, 68, 245, 143, 161, 45, 108, 69])
);
+4 -10
View File
@@ -6,14 +6,8 @@ import type { HashType } from './types';
import { blake2AsU8a } from '../blake2';
import { keccakAsU8a } from '../keccak';
const HASH_TYPES = ['blake2', 'keccak'];
export function secp256k1Hasher (hashType: HashType, data: Uint8Array | string): Uint8Array {
if (hashType === 'blake2') {
return blake2AsU8a(data);
} else if (hashType === 'keccak') {
return keccakAsU8a(data);
}
throw new Error(`Unsupported secp256k1 hasher '${hashType as string}', expected one of ${HASH_TYPES.join(', ')}`);
export function hasher (hashType: HashType, data: Uint8Array | string, onlyJs?: boolean): Uint8Array {
return hashType === 'keccak'
? keccakAsU8a(data, undefined, onlyJs)
: blake2AsU8a(data, undefined, undefined, onlyJs);
}
@@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
import { hexToU8a, u8aToHex } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceWasm } from '../../../test/performance';
import { mnemonicToMiniSecret } from '../../mnemonic';
import { secp256k1PairFromSeed } from '..';
@@ -37,23 +39,33 @@ const tests: Test[] = [
];
describe('secp256k1PairFromSeed', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
const TEST = hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae');
const RESULT = {
publicKey: hexToU8a('0x03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b'),
secretKey: hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae')
};
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
expect(secp256k1PairFromSeed(TEST)).toEqual(RESULT);
});
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
expect(secp256k1PairFromSeed(TEST, onlyJs)).toEqual(RESULT);
});
tests.forEach(([mnemonic, secretKey, publicKey], index): void => {
it(`creates valid against known (${index})`, (): void => {
const seed = mnemonicToMiniSecret(mnemonic);
const pair = secp256k1PairFromSeed(seed);
tests.forEach(([mnemonic, secretKey, publicKey], index): void => {
it(`creates valid against known (${index})`, (): void => {
const seed = mnemonicToMiniSecret(mnemonic);
const pair = secp256k1PairFromSeed(seed, onlyJs);
expect(u8aToHex(pair.secretKey)).toEqual(secretKey);
expect(u8aToHex(pair.publicKey)).toEqual(publicKey);
expect(u8aToHex(pair.secretKey)).toEqual(secretKey);
expect(u8aToHex(pair.publicKey)).toEqual(publicKey);
});
});
});
performanceWasm('secp256k1PairFromSeed', 500, (input, onlyJs) =>
secp256k1PairFromSeed(input, onlyJs)
);
});
@@ -3,22 +3,28 @@
import type { Keypair } from '../../types';
import { assert, bnToU8a } from '@polkadot/util';
import { BN_BE_256_OPTS } from '../../bn';
import { secp256k1 } from '../secp256k1';
import { assert, hasBigInt } from '@polkadot/util';
import { isReady, secp256k1FromSeed } from '@polkadot/wasm-crypto';
import { getPublicKey } from '@polkadot/x-noble-secp256k1';
/**
* @name secp256k1PairFromSeed
* @description Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed.
*/
export function secp256k1PairFromSeed (seed: Uint8Array): Keypair {
export function secp256k1PairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keypair {
assert(seed.length === 32, 'Expected valid 32-byte private key as a seed');
const key = secp256k1.keyFromPrivate(seed);
if (!hasBigInt || (!onlyJs && isReady())) {
const full = secp256k1FromSeed(seed);
return {
publicKey: full.slice(32),
secretKey: full.slice(0, 32)
};
}
return {
publicKey: new Uint8Array(key.getPublic().encodeCompressed()),
secretKey: bnToU8a(key.getPrivate(), BN_BE_256_OPTS)
publicKey: getPublicKey(seed, true),
secretKey: seed
};
}
@@ -2,17 +2,30 @@
// SPDX-License-Identifier: Apache-2.0
import { u8aToHex, u8aToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceWasm } from '../../test/performance';
import { keccakAsU8a } from '../keccak';
import { secp256k1Recover } from '.';
describe('secp256k1Recover', (): void => {
it('recovers a publicKey', (): void => {
const pubKey = '0x93a9fc7154c6da3c826415df01eb0e37fb4da4b0';
const sig = '0x7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff';
const msg = '0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9';
const res = keccakAsU8a(secp256k1Recover(u8aToU8a(msg), u8aToU8a(sig), 0));
const sig = u8aToU8a('0x7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff');
const msg = u8aToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
expect(u8aToHex(res.subarray(12))).toEqual(pubKey);
describe('secp256k1Recover', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('recovers a publicKey', (): void => {
const pubKey = '0x93a9fc7154c6da3c826415df01eb0e37fb4da4b0';
const res = keccakAsU8a(secp256k1Recover(msg, sig, 0, undefined, onlyJs));
expect(u8aToHex(res.subarray(-20))).toEqual(pubKey);
});
});
performanceWasm('secp256k1Recover', 200, (_, onlyJs) =>
secp256k1Recover(msg, sig, 0, undefined, onlyJs)
);
});
+21 -8
View File
@@ -1,17 +1,30 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { secp256k1 } from './secp256k1';
import type { HexString } from '@polkadot/util/types';
import type { HashType } from './types';
import { assert, hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, secp256k1Recover as wasm } from '@polkadot/wasm-crypto';
import { recoverPublicKey, Signature } from '@polkadot/x-noble-secp256k1';
import { secp256k1Compress } from './compress';
import { secp256k1Expand } from './expand';
/**
* @name secp256k1Recover
* @description Recovers a publicKey from the supplied signature
*/
export function secp256k1Recover (message: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array {
return new Uint8Array(
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
secp256k1
.recoverPubKey(message, { r: signature.slice(0, 32), s: signature.slice(32, 64) }, recovery)
.encode(null, true)
);
export function secp256k1Recover (msgHash: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, recovery: number, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
const sig = u8aToU8a(signature).subarray(0, 64);
const msg = u8aToU8a(msgHash);
const publicKey = !hasBigInt || (!onlyJs && isReady())
? wasm(msg, sig, recovery)
: recoverPublicKey(msg, Signature.fromCompact(sig).toRawBytes(), recovery);
assert(publicKey, 'Unable to recover publicKey from signature');
return hashType === 'keccak'
? secp256k1Expand(publicKey, onlyJs)
: secp256k1Compress(publicKey, onlyJs);
}
@@ -1,7 +0,0 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import elliptic from 'elliptic';
// eslint-disable-next-line new-cap
export const secp256k1 = new elliptic.ec('secp256k1');
@@ -0,0 +1,35 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceWasm } from '../../test/performance';
import { secp256k1PairFromSeed, secp256k1Sign } from '.';
const pair = secp256k1PairFromSeed(hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae'));
const msg = hexToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
describe('sign', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('generates a known signature', (): void => {
expect(
secp256k1Sign(msg, pair, undefined, onlyJs)
).toEqual(hexToU8a(
// from elliptic, this is - 0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300
// libsecp256k1 & @noble/hashes do agree here...
'0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01'
));
});
});
// since the libsecp256k1 signatures don't match (but can be verified), we
// do both signing and verification here (checking the extracted key)
performanceWasm('secp256k1Sign', 1000, (_, onlyJs) =>
secp256k1Sign(msg, pair, undefined, onlyJs)
);
});
+16 -9
View File
@@ -4,25 +4,32 @@
import type { Keypair } from '../types';
import type { HashType } from './types';
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
import { isReady, secp256k1Sign as wasm } from '@polkadot/wasm-crypto';
import { Signature, signSync } from '@polkadot/x-noble-secp256k1';
import { BN_BE_256_OPTS } from '../bn';
import { secp256k1Hasher } from './hasher';
import { secp256k1 } from './secp256k1';
import { hasher } from './hasher';
/**
* @name secp256k1Sign
* @description Returns message signature of `message`, using the supplied pair
*/
export function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2'): Uint8Array {
export function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
assert(secretKey?.length === 32, 'Expected valid secp256k1 secretKey, 32-bytes');
const key = secp256k1.keyFromPrivate(secretKey);
const ecsig = key.sign(secp256k1Hasher(hashType, message));
const data = hasher(hashType, message, onlyJs);
if (!hasBigInt || (!onlyJs && isReady())) {
return wasm(data, secretKey);
}
const [sigBytes, recoveryParam] = signSync(data, secretKey, { canonical: true, recovered: true });
const { r, s } = Signature.fromHex(sigBytes);
return u8aConcat(
bnToU8a(ecsig.r, BN_BE_256_OPTS),
bnToU8a(ecsig.s, BN_BE_256_OPTS),
new Uint8Array([ecsig.recoveryParam || 0])
bnToU8a(r, BN_BE_256_OPTS),
bnToU8a(s, BN_BE_256_OPTS),
new Uint8Array([recoveryParam || 0])
);
}
@@ -1,10 +1,12 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import '../cryptoInit';
import { stringToU8a } from '@polkadot/util';
import { randomAsU8a } from '../random/asU8a';
import { secp256k1Hasher } from './hasher';
import { hasher } from './hasher';
import { secp256k1Expand, secp256k1PairFromSeed, secp256k1Sign, secp256k1Verify } from '.';
const MESSAGE = stringToU8a('this is a message');
@@ -15,7 +17,7 @@ describe('sign and verify', (): void => {
const sig = '0x92fcacf0946bbd10b31dfe16d567ed1d3014e81007dd9e5256e19c0f07eacc1643b151ca29e449a765e16a7ce59b88d800467d6b3412d30ea8ad22307a59664b00';
const msg = stringToU8a('secp256k1');
expect(secp256k1Verify(msg, sig, address)).toBe(true);
expect(secp256k1Verify(msg, sig, address)).toEqual(true);
});
it('has 65-byte signatures', (): void => {
@@ -27,25 +29,25 @@ describe('sign and verify', (): void => {
it('signs/verifies a message by random key (blake2)', (): void => {
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair);
const address = secp256k1Hasher('blake2', pair.publicKey);
const address = hasher('blake2', pair.publicKey);
expect(secp256k1Verify(MESSAGE, signature, address)).toBe(true);
expect(secp256k1Verify(MESSAGE, signature, address)).toEqual(true);
});
it('signs/verifies a message by random key (keccak)', (): void => {
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
expect(secp256k1Verify(MESSAGE, signature, address, 'keccak')).toBe(true);
expect(secp256k1Verify(MESSAGE, signature, address, 'keccak')).toEqual(true);
});
it('fails verification on hasher mismatches', (): void => {
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
expect(secp256k1Verify(MESSAGE, signature, address, 'blake2')).toBe(false);
expect(secp256k1Verify(MESSAGE, signature, address, 'blake2')).toEqual(false);
});
it('works over a range of random keys (blake2)', (): void => {
@@ -57,10 +59,10 @@ describe('sign and verify', (): void => {
secp256k1Verify(
MESSAGE,
secp256k1Sign(MESSAGE, pair, 'blake2'),
secp256k1Hasher('blake2', pair.publicKey),
hasher('blake2', pair.publicKey),
'blake2'
)
).toBe(true);
).toEqual(true);
} catch (error) {
console.error(`blake2 failed on #${i}`);
throw error;
@@ -77,10 +79,10 @@ describe('sign and verify', (): void => {
secp256k1Verify(
MESSAGE,
secp256k1Sign(MESSAGE, pair, 'keccak'),
secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey)),
hasher('keccak', secp256k1Expand(pair.publicKey)),
'keccak'
)
).toBe(true);
).toEqual(true);
} catch (error) {
console.error(`keccak failed on #${i}`);
throw error;
@@ -4,13 +4,22 @@
import { secp256k1PrivateKeyTweakAdd } from './tweakAdd';
describe('TweakAdd', (): void => {
describe('PrivateKey TweakAdd', (): void => {
it('fails for wrong array length', (): void => {
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
expect(
() => secp256k1PrivateKeyTweakAdd(A, B)
).toThrow(/Expected tweak to be an Uint8Array/);
});
describe.each([false, true])('onlyBn=%p', (onlyBn): void => {
it('succeeds for a simple case', (): void => {
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
expect(
secp256k1PrivateKeyTweakAdd(A, B)
secp256k1PrivateKeyTweakAdd(A, B, onlyBn)
).toEqual(new Uint8Array([
3, 4, 3, 4, 3, 4, 3, 4, 3,
4, 3, 4, 3, 4, 3, 4, 3, 4,
@@ -18,14 +27,5 @@ describe('TweakAdd', (): void => {
4, 3, 4, 3, 4
]));
});
it('fails for wrong array length', (): void => {
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
expect(
() => secp256k1PrivateKeyTweakAdd(A, B)
).toThrow(/Expected tweak to be an Uint8Array/);
});
});
});
+42 -20
View File
@@ -1,32 +1,54 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert, BN, bnToU8a, isU8a } from '@polkadot/util';
import { _0n, assert, BN, bnToU8a, hasBigInt, isU8a, nToU8a, u8aToBigInt } from '@polkadot/util';
import { BigInt } from '@polkadot/x-bigint';
import { BN_BE_256_OPTS } from '../bn';
import { BN_BE_256_OPTS, BN_BE_OPTS } from '../bn';
// pre-defined curve param as lifted form elliptic
// https://github.com/indutny/elliptic/blob/e71b2d9359c5fe9437fbf46f1f05096de447de57/lib/elliptic/curves.js#L182
const N = new BN(
'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141'.replace(/ /g, ''),
'hex'
);
const N = 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141'.replace(/ /g, '');
const N_BI = BigInt(`0x${N}`);
const N_BN = new BN(N, 'hex');
export function secp256k1PrivateKeyTweakAdd (seckey: Uint8Array, tweak: Uint8Array): Uint8Array {
function addBi (seckey: Uint8Array, tweak: Uint8Array): Uint8Array {
let res = u8aToBigInt(tweak, BN_BE_OPTS);
assert(res < N_BI, 'Tweak parameter is out of range');
res += u8aToBigInt(seckey, BN_BE_OPTS);
if (res >= N_BI) {
res -= N_BI;
}
assert(res !== _0n, 'Invalid resulting private key');
return nToU8a(res, BN_BE_256_OPTS);
}
function addBn (seckey: Uint8Array, tweak: Uint8Array): Uint8Array {
const res = new BN(tweak);
assert(res.cmp(N_BN) < 0, 'Tweak parameter is out of range');
res.iadd(new BN(seckey));
if (res.cmp(N_BN) >= 0) {
res.isub(N_BN);
}
assert(!res.isZero(), 'Invalid resulting private key');
return bnToU8a(res, BN_BE_256_OPTS);
}
export function secp256k1PrivateKeyTweakAdd (seckey: Uint8Array, tweak: Uint8Array, onlyBn?: boolean): Uint8Array {
assert(isU8a(seckey) && seckey.length === 32, 'Expected seckey to be an Uint8Array with length 32');
assert(isU8a(tweak) && tweak.length === 32, 'Expected tweak to be an Uint8Array with length 32');
const bn = new BN(tweak);
assert(bn.cmp(N) < 0, 'Tweak parameter is out of range');
bn.iadd(new BN(seckey));
if (bn.cmp(N) >= 0) {
bn.isub(N);
}
assert(!bn.isZero(), 'Invalid resulting private key');
return bnToU8a(bn, BN_BE_256_OPTS);
return !hasBigInt || onlyBn
? addBn(seckey, tweak)
: addBi(seckey, tweak);
}
@@ -1,19 +1,75 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { secp256k1Verify } from '.';
import '../cryptoInit';
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceWasm } from '../../test/performance';
import { hasher } from './hasher';
import { secp256k1PairFromSeed, secp256k1Verify } from '.';
const message = 'Pay KSMs to the Kusama account:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee';
describe('secp256k1Verify', (): void => {
it('validates known ETH against address', (): void => {
const message = 'Pay KSMs to the Kusama account:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee';
expect(
secp256k1Verify(
`\x19Ethereum Signed Message:\n${message.length.toString()}${message}`,
'0x55bd020bdbbdc02de34e915effc9b18a99002f4c29f64e22e8dcbb69e722ea6c28e1bb53b9484063fbbfd205e49dcc1f620929f520c9c4c3695150f05a28f52a01',
'0x002309df96687e44280bb72c3818358faeeb699c',
'keccak'
)
).toBe(true);
beforeEach(async (): Promise<void> => {
await waitReady();
});
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('validates known ETH against address', (): void => {
expect(
secp256k1Verify(
`\x19Ethereum Signed Message:\n${message.length.toString()}${message}`,
'0x55bd020bdbbdc02de34e915effc9b18a99002f4c29f64e22e8dcbb69e722ea6c28e1bb53b9484063fbbfd205e49dcc1f620929f520c9c4c3695150f05a28f52a01',
'0x002309df96687e44280bb72c3818358faeeb699c',
'keccak',
onlyJs
)
).toEqual(true);
});
describe.each([false, true])('validation against known, publicKey=%p', (isPublic): void => {
const pair = secp256k1PairFromSeed(hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae'));
const msg = hexToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
const addr = isPublic
? pair.publicKey
: hasher('blake2', pair.publicKey, onlyJs);
it('signature from JS', (): void => {
expect(
secp256k1Verify(
msg,
'0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300',
addr,
'blake2',
onlyJs
)
).toEqual(true);
});
it('signature from wasm', (): void => {
expect(
secp256k1Verify(
msg,
'0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01',
addr,
'blake2',
onlyJs
)
).toEqual(true);
});
});
});
performanceWasm('secp256k1Verify', 100, (_, onlyJs) =>
secp256k1Verify(
`\x19Ethereum Signed Message:\n${message.length.toString()}${message}`,
'0x55bd020bdbbdc02de34e915effc9b18a99002f4c29f64e22e8dcbb69e722ea6c28e1bb53b9484063fbbfd205e49dcc1f620929f520c9c4c3695150f05a28f52a01',
'0x002309df96687e44280bb72c3818358faeeb699c',
'keccak',
onlyJs
)
);
});
+13 -23
View File
@@ -6,36 +6,26 @@ import type { HashType } from './types';
import { assert, u8aEq, u8aToU8a } from '@polkadot/util';
import { secp256k1Expand } from './expand';
import { secp256k1Hasher } from './hasher';
import { secp256k1 } from './secp256k1';
import { hasher } from './hasher';
import { secp256k1Recover } from './recover';
/**
* @name secp256k1Verify
* @description Verifies the signature of `message`, using the supplied pair
*/
export function secp256k1Verify (message: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, address: HexString | Uint8Array | string, hashType: HashType = 'blake2'): boolean {
const isEthereum = hashType === 'keccak';
const u8a = u8aToU8a(signature);
export function secp256k1Verify (msgHash: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, address: HexString | Uint8Array | string, hashType: HashType = 'blake2', onlyJs?: boolean): boolean {
const sig = u8aToU8a(signature);
assert(u8a.length === 65, `Expected signature with 65 bytes, ${u8a.length} found instead`);
assert(sig.length === 65, `Expected signature with 65 bytes, ${sig.length} found instead`);
const publicKey = new Uint8Array(
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
secp256k1
.recoverPubKey(
secp256k1Hasher(hashType, message),
{ r: u8a.slice(0, 32), s: u8a.slice(32, 64) },
u8a[64]
)
.encodeCompressed()
);
const signingAddress = secp256k1Hasher(hashType, isEthereum ? secp256k1Expand(publicKey) : publicKey);
const inputAddress = u8aToU8a(address);
const publicKey = secp256k1Recover(hasher(hashType, msgHash), sig, sig[64], hashType, onlyJs);
const signerAddr = hasher(hashType, publicKey, onlyJs);
const inputAddr = u8aToU8a(address);
// for Ethereum (keccak) the last 20 bytes is the address
return isEthereum
? u8aEq(signingAddress.slice(-20), inputAddress.slice(-20))
: u8aEq(signingAddress, inputAddress);
return u8aEq(publicKey, inputAddr) || (
hashType === 'keccak'
? u8aEq(signerAddr.slice(-20), inputAddr.slice(-20))
: u8aEq(signerAddr, inputAddr)
);
}
+2 -3
View File
@@ -3,11 +3,10 @@
import type { HexString } from '@polkadot/util/types';
import { sha256 as sha256Js } from '@noble/hashes/lib/sha256';
import { sha512 as sha512Js } from '@noble/hashes/lib/sha512';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, sha256, sha512 } from '@polkadot/wasm-crypto';
import { sha256 as sha256Js } from '@polkadot/x-noble-hashes/sha256';
import { sha512 as sha512Js } from '@polkadot/x-noble-hashes/sha512';
import { createBitHasher } from '../helpers';
+21 -20
View File
@@ -1,7 +1,8 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { _0n, _1n, _n } from '@polkadot/util';
import { _0n, _1n } from '@polkadot/util';
import { BigInt } from '@polkadot/x-bigint';
// Adapted from https://github.com/pierrec/js-xxhash/blob/0504e76f3d31a21ae8528a7f590c7289c9e431d2/lib/xxhash64.js
//
@@ -26,29 +27,29 @@ interface State {
v4: bigint;
}
const P64_1 = _n('11400714785074694791');
const P64_2 = _n('14029467366897019727');
const P64_3 = _n('1609587929392839161');
const P64_4 = _n('9650029242287828579');
const P64_5 = _n('2870177450012600261');
const P64_1 = BigInt('11400714785074694791');
const P64_2 = BigInt('14029467366897019727');
const P64_3 = BigInt('1609587929392839161');
const P64_4 = BigInt('9650029242287828579');
const P64_5 = BigInt('2870177450012600261');
// mask for a u64, all bits set
const U64 = _n('0xffffffffffffffff');
const U64 = BigInt('0xffffffffffffffff');
// various constants
const _7n = _n(7);
const _11n = _n(11);
const _12n = _n(12);
const _16n = _n(16);
const _18n = _n(18);
const _23n = _n(23);
const _27n = _n(27);
const _29n = _n(29);
const _31n = _n(31);
const _32n = _n(32);
const _33n = _n(33);
const _64n = _n(64);
const _256n = _n(256);
const _7n = BigInt(7);
const _11n = BigInt(11);
const _12n = BigInt(12);
const _16n = BigInt(16);
const _18n = BigInt(18);
const _23n = BigInt(23);
const _27n = BigInt(27);
const _29n = BigInt(29);
const _31n = BigInt(31);
const _32n = BigInt(32);
const _33n = BigInt(33);
const _64n = BigInt(64);
const _256n = BigInt(256);
function rotl (a: bigint, b: bigint): bigint {
const c = a & U64;
+2 -2
View File
@@ -10,8 +10,8 @@ type ExecFn = (input: Uint8Array, onlyJs: boolean) => unknown;
const GENERATED = arrayRange(256).map(() => [randomAsU8a()]);
export function performanceWasm (name: string, count: number, exec: ExecFn): void {
performanceCmp(name, ['WebAssembly', 'JavaScript'], count, GENERATED, exec);
export function performanceWasm (name: string, count: number, exec: ExecFn, inputs = GENERATED): void {
performanceCmp(name, ['WebAssembly', 'JavaScript'], count, inputs, exec);
}
export { performance, performanceCmp };
+5 -3
View File
@@ -34,12 +34,14 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/x-textdecoder": "8.0.1",
"@polkadot/x-textencoder": "8.0.1",
"@polkadot/x-bigint": "8.1.1",
"@polkadot/x-global": "8.1.1",
"@polkadot/x-textdecoder": "8.1.1",
"@polkadot/x-textencoder": "8.1.1",
"@types/bn.js": "^4.11.6",
"bn.js": "^4.12.0",
"ip-regex": "^4.3.0"
+6 -14
View File
@@ -1,39 +1,31 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
type NewBigInt = (value: string | number | bigint | boolean) => bigint;
/**
* @name _n
* @description Creates a new instance of BigInt in environments that do support it
*/
export const _n: NewBigInt = typeof BigInt !== 'undefined'
? BigInt
: () => Number.NaN as unknown as bigint;
import { BigInt } from '@polkadot/x-bigint';
/**
* @name _0n
* @summary BigInt constant for 0.
*/
export const _0n = _n(0);
export const _0n = BigInt(0);
/**
* @name _1n
* @summary BigInt constant for 1.
*/
export const _1n = _n(1);
export const _1n = BigInt(1);
/**
* @name _1Mn
* @summary BigInt constant for 1,000,000.
*/
export const _1Mn = _n(1_000_000);
export const _1Mn = BigInt(1_000_000);
/**
* @name _1Bn
* @summary BigInt constant for 1,000,000,000.
*/
export const _1Bn = _n(1_000_000_000);
export const _1Bn = BigInt(1_000_000_000);
/**
* @name _1Qn
@@ -45,4 +37,4 @@ export const _1Qn = _1Bn * _1Bn;
* @name _2pow53n
* @summary BigInt constant for MAX_SAFE_INTEGER
*/
export const _2pow53n = _n(Number.MAX_SAFE_INTEGER);
export const _2pow53n = BigInt(Number.MAX_SAFE_INTEGER);
+4 -2
View File
@@ -4,11 +4,13 @@
import type { BN } from '../bn';
import type { ToBigInt, ToBn } from '../types';
import { BigInt } from '@polkadot/x-bigint';
import { assert } from '../assert';
import { _0n, _1n, _2pow53n, _n } from './consts';
import { _0n, _1n, _2pow53n } from './consts';
import { nToBigInt } from './toBigInt';
const _sqrt2pow53n = _n(94906265);
const _sqrt2pow53n = BigInt(94906265);
/**
* @name nSqrt
+2
View File
@@ -4,6 +4,8 @@
import type { BN } from '../bn/bn';
import type { HexString, ToBigInt, ToBn } from '../types';
import { BigInt } from '@polkadot/x-bigint';
import { hexToBigInt } from '../hex/toBigInt';
import { isBn } from '../is/bn';
import { isHex } from '../is/hex';
+5 -3
View File
@@ -4,16 +4,18 @@
import type { BN } from '../bn/bn';
import type { ToBigInt, ToBn, ToBnOptions } from '../types';
import { BigInt } from '@polkadot/x-bigint';
import { objectSpread } from '../object/spread';
import { _0n, _1n, _n } from './consts';
import { _0n, _1n } from './consts';
import { nToBigInt } from './toBigInt';
interface Options extends ToBnOptions {
bitLength?: number;
}
const DIV = _n(256);
const NEG_MASK = _n(0xff);
const DIV = BigInt(256);
const NEG_MASK = BigInt(0xff);
function createEmpty ({ bitLength = 0 }: Options): Uint8Array {
return bitLength === -1
+3 -1
View File
@@ -1,7 +1,9 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
export const hasBigInt = typeof BigInt !== 'undefined';
import { BigInt } from '@polkadot/x-bigint';
export const hasBigInt = typeof BigInt === 'function' && typeof BigInt.asIntN === 'function';
export const hasBuffer = typeof Buffer !== 'undefined';
export const hasDirname = typeof __dirname !== 'undefined';
export const hasProcess = typeof process === 'object';
+2
View File
@@ -3,6 +3,8 @@
import type { ToBnOptions } from '../types';
import { BigInt } from '@polkadot/x-bigint';
import { objectSpread } from '../object/spread';
import { u8aToBigInt } from '../u8a/toBigInt';
import { hexToU8a } from './toU8a';
+1 -1
View File
@@ -16,5 +16,5 @@
* ```
*/
export function isBigInt (value: unknown): value is bigint {
return typeof value === 'bigint' || value instanceof BigInt;
return typeof value === 'bigint';
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/util', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/util', version: '8.1.1' };
+38 -2
View File
@@ -33,8 +33,8 @@ const randomWords = arrayRange(256).map((): [string] => {
describe('stringCamelCase', (): void => {
it('works correctly', (): void => {
expect(
stringCamelCase('Snake_case-...Something spaced')
).toBe('snakeCaseSomethingSpaced');
stringCamelCase('Snake_case-...SomethingSomething spaced')
).toBe('snakeCaseSomethingSomethingSpaced');
});
it('works correctly for String (class', (): void => {
@@ -43,5 +43,41 @@ describe('stringCamelCase', (): void => {
).toBe('fooBarBazTestSpacedExtra');
});
it('adjusts all-uppercase', (): void => {
expect(
stringCamelCase('DEDUP_KEY_PREFIX')
).toEqual('dedupKeyPrefix');
expect(
stringCamelCase('SOMETHING')
).toEqual('something');
});
it('adjusts all-uppercase + digits', (): void => {
expect(
stringCamelCase('SS58 PreFIX')
).toEqual('ss58Prefix');
expect(
stringCamelCase('SS58Prefix')
).toEqual('ss58Prefix');
expect(
stringCamelCase('UUID64')
).toEqual('uuid64');
expect(
stringCamelCase('BLAKE2B')
).toEqual('blake2B');
});
it('adjusts with leading _', (): void => {
expect(
stringCamelCase('_reserved')
).toEqual('reserved');
});
it('adjusts with minimal chars per part', (): void => {
expect(
stringCamelCase('_a_b_c_def')
).toEqual('aBCDef');
});
performance('stringCamelCase', 64000, randomWords, stringCamelCase);
});
+16 -11
View File
@@ -3,27 +3,32 @@
import type { AnyString } from '../types';
const RE_WHITE = /[-_., ]+/g;
const RE_WORDS = /(?:^\w|[A-Z]|\b\w)/g;
const RE_JOIN = /\s/g;
// Adapted from https://stackoverflow.com/a/2970667
//
// this is not as optimal as the original (we split into multiple), however
// it does pass the tests (which the original doesn't) and it is still 10+x
// improvement over the original camelcase npm package (at running)
// this is not as optimal as the original asnwer (we split into multiple),
// however it does pass the tests (which the original doesn't) and it is still
// a 10+x improvement over the original camelcase npm package (at running)
//
// original: 20.88 μs/op
// this: 1.67 μs/op
// this: 1.91 μs/op
//
// Caveat of this: only Ascii, but acceptable for the intended usecase
function converter (fn: (w: string, i: number) => string): (value: AnyString) => string {
return (value: AnyString): string =>
value
.toString()
.replace(RE_WHITE, ' ')
.replace(RE_WORDS, fn)
.replace(RE_JOIN, '');
// replace all seperators (including consequtive) with spaces
.replace(/[-_., ]+/g, ' ')
// we don't want leading or trailing spaces
.trim()
// all consequtive capitals are changed to lowercase
.replace(/[A-Z]{2,}/g, (w) => w.toLowerCase())
// split into words
.split(' ')
// apply the function to the first letter, the rest as-is
.map((w, i) => fn(w[0], i) + w.slice(1))
// combine into a single word
.join('');
}
/**
+5 -3
View File
@@ -3,11 +3,13 @@
import type { ToBnOptions } from '../types';
import { _1n, _n } from '../bi/consts';
import { BigInt } from '@polkadot/x-bigint';
import { _1n } from '../bi/consts';
import { objectSpread } from '../object/spread';
const U8_MAX = _n(256);
const U16_MAX = _n(256 * 256);
const U8_MAX = BigInt(256);
const U16_MAX = BigInt(256 * 256);
function xor (input: Uint8Array): Uint8Array {
const result = new Uint8Array(input.length);
+3
View File
@@ -0,0 +1,3 @@
# @polkadot/x-bigint
A cross-environment BigInt.
+28
View File
@@ -0,0 +1,28 @@
{
"author": "Jaco Greeff <jacogr@gmail.com>",
"bugs": "https://github.com/polkadot-js/common/issues",
"contributors": [],
"description": "A cross-environment BigInt replacement",
"engines": {
"node": ">=14.0.0"
},
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-bigint#readme",
"license": "Apache-2.0",
"maintainers": [],
"name": "@polkadot/x-bigint",
"repository": {
"directory": "packages/x-bigint",
"type": "git",
"url": "https://github.com/polkadot-js/common.git"
},
"sideEffects": [
"./shim.js",
"./shim.cjs"
],
"type": "module",
"version": "8.1.1",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/x-global": "8.1.1"
}
}
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { xglobal } from '@polkadot/x-global';
export { packageInfo } from './packageInfo';
export const BigInt: BigIntConstructor = typeof xglobal.BigInt === 'function' && typeof xglobal.BigInt.asIntN === 'function'
? xglobal.BigInt
: (() => Number.NaN) as unknown as BigIntConstructor;
+6
View File
@@ -0,0 +1,6 @@
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-bigint', version: '8.1.1' };
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { BigInt } from '@polkadot/x-bigint';
import { xglobal } from '@polkadot/x-global';
if (typeof xglobal.BigInt !== 'function') {
(xglobal as Record<string, unknown>).BigInt = BigInt;
}
+2 -2
View File
@@ -17,11 +17,11 @@
},
"sideEffects": false,
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/util": "8.0.1",
"@polkadot/util": "8.1.1",
"buffer-es6": "^4.9.3"
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-bundle', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/x-bundle', version: '8.1.1' };
+2 -2
View File
@@ -17,13 +17,13 @@
},
"sideEffects": false,
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"browser": "browser.js",
"main": "node.js",
"react-native": "react-native.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/x-global": "8.0.1",
"@polkadot/x-global": "8.1.1",
"@types/node-fetch": "^2.5.12",
"node-fetch": "^2.6.6"
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-fetch', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/x-fetch', version: '8.1.1' };
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2017-2021 @polkadot/x-fetch authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { fetch } from '@polkadot/x-fetch';
import { xglobal } from '@polkadot/x-global';
if (typeof xglobal.fetch !== 'function') {
(xglobal as Record<string, unknown>).fetch = fetch;
}
+1 -1
View File
@@ -17,7 +17,7 @@
},
"sideEffects": false,
"type": "module",
"version": "8.0.1",
"version": "8.1.1",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3"
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-global', version: '8.0.1' };
export const packageInfo = { name: '@polkadot/x-global', version: '8.1.1' };
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 Paul Miller (https://paulmillr.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+438
View File
@@ -0,0 +1,438 @@
# noble-hashes ![Node CI](https://github.com/paulmillr/noble-hashes/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
Fast, secure & minimal JS implementation of SHA2, SHA3, RIPEMD, BLAKE2/3, HMAC, HKDF, PBKDF2 & Scrypt.
- **noble** family, zero dependencies
- 🔻 Helps JS bundlers with lack of entry point; ensures small size of your app
- 🔁 No unrolled loops: makes it much easier to verify and reduces source code size 2-5x
- 🏎 Ultra-fast, hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness: chained tests, sliding window tests, DoS tests
- 🧪 Differential fuzzing ensures even more correctness with [cryptofuzz](https://github.com/guidovranken/cryptofuzz)
- 🔑 Scrypt supports `n: 2**22` with 4GB arrays while other implementations crash on `2**21` or even `2**20`, `maxmem` security param, `onProgress` callback
- 🦘 SHA3 supports Keccak, TupleHash, KangarooTwelve and MarsupilamiFourteen
- All primitives are just ~2KLOC / 41KB minified / 14KB gzipped. SHA256-only is 240LOC / 7KB minified / 3KB gzipped
The library's initial development was funded by [Ethereum Foundation](https://ethereum.org/).
### This library belongs to _noble_ crypto
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, small files
- Easily auditable TypeScript/JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out all libraries:
[secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519),
[bls12-381](https://github.com/paulmillr/noble-bls12-381),
[hashes](https://github.com/paulmillr/noble-hashes)
## Usage
Use NPM in node.js / browser, or include single file from
[GitHub's releases page](https://github.com/paulmillr/noble-hashes/releases):
> npm install ../noble-hashes
The library does not have an entry point. It allows you to select specific primitives and drop everything else. If you only want to use sha256, just use the library with rollup or other bundlers. This is done to make your bundles tiny.
```js
const { sha256 } = require('@noble/hashes/lib/sha256');
console.log(sha256(new Uint8Array([1, 2, 3])));
// Uint8Array(32) [3, 144, 88, 198, 242, 192, 203, 73, ...]
// you could also pass strings that will be UTF8-encoded to Uint8Array
console.log(sha256('abc'))); // == sha256(new TextEncoder().encode('abc'))
// sha384 is here, because it uses same internals as sha512
const { sha512, sha512_256, sha384 } = require('@noble/hashes/lib/sha512');
// prettier-ignore
const {
sha3_224, sha3_256, sha3_384, sha3_512,
keccak_224, keccak_256, keccak_384, keccak_512,
shake128, shake256
} = require('@noble/hashes/lib/sha3');
// prettier-ignore
const {
cshake128, cshake256, kmac128, kmac256,
k12, m14,
tuplehash256, parallelhash256, keccakprg
} = require('@noble/hashes/lib/sha3-addons');
const { ripemd160 } = require('@noble/hashes/lib/ripemd160');
const { blake3 } = require('@noble/hashes/lib/blake3');
const { blake2b } = require('@noble/hashes/lib/blake2b');
const { blake2s } = require('@noble/hashes/lib/blake2s');
const { hmac } = require('@noble/hashes/lib/hmac');
const { hkdf } = require('@noble/hashes/lib/hkdf');
const { pbkdf2, pbkdf2Async } = require('@noble/hashes/lib/pbkdf2');
const { scrypt, scryptAsync } = require('@noble/hashes/lib/scrypt');
// small utility method that converts bytes to hex
const { bytesToHex as toHex } = require('@noble/hashes/lib/utils');
console.log(toHex(sha256('abc')));
// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
```
## API
All hash functions:
- can be called directly, with `Uint8Array`.
- return `Uint8Array`
- can receive `string`, which is automatically converted to `Uint8Array`
via utf8 encoding **(not hex)**
- support hashing 4GB of data per update on 64-bit systems (unlimited with streaming)
```ts
function hash(message: Uint8Array | string): Uint8Array;
hash(new Uint8Array([1, 3]));
hash('string') == hash(new TextEncoder().encode('string'));
```
All hash functions can be constructed via `hash.create()` method:
- the result is `Hash` subclass instance, which has `update()` and `digest()` methods
- `digest()` finalizes the hash and makes it no longer usable
```ts
hash
.create()
.update(new Uint8Array([1, 3]))
.digest();
```
_Some_ hash functions can also receive `options` object, which can be either passed as a:
- second argument to hash function: `blake3('abc', { key: 'd', dkLen: 32 })`
- first argument to class initializer: `blake3.create({ context: 'e', dkLen: 32 })`
## Modules
- [SHA2 (sha256, sha384, sha512, sha512_256)](#sha2-sha256-sha384-sha512-sha512_256)
- [SHA3 (FIPS, SHAKE, Keccak)](#sha3-fips-shake-keccak)
- [SHA3 Addons (cSHAKE, KMAC, KangarooTwelve, MarsupilamiFourteen)](#sha3-addons-cshake-kmac-tuplehash-parallelhash-kangarootwelve-marsupilamifourteen)
- [RIPEMD-160](#ripemd-160)
- [BLAKE2b, BLAKE2s](#blake2b-blake2s)
- [BLAKE3](#blake3)
- [HMAC](#hmac)
- [HKDF](#hkdf)
- [PBKDF2](#pbkdf2)
- [Scrypt](#scrypt)
- [utils](#utils)
##### SHA2 (sha256, sha384, sha512, sha512_256)
```typescript
import { sha256 } from '@noble/hashes/lib/sha256.js';
const h1a = sha256('abc');
const h1b = sha256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
```typescript
import { sha512 } from '@noble/hashes/lib/sha512.js';
const h2a = sha512('abc');
const h2b = sha512
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
// SHA512/256 variant
import { sha512_256 } from '@noble/hashes/lib/sha512.js';
const h3a = sha512_256('abc');
const h3b = sha512_256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
// SHA384
import { sha384 } from '@noble/hashes/lib/sha512.js';
const h4a = sha384('abc');
const h4b = sha384
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and [the paper on SHA512/256](https://eprint.iacr.org/2010/548.pdf).
##### SHA3 (FIPS, SHAKE, Keccak)
```typescript
import {
sha3_224,
sha3_256,
sha3_384,
sha3_512,
keccak_224,
keccak_256,
keccak_384,
keccak_512,
shake128,
shake256,
} from '@noble/hashes/lib/sha3.js';
const h5a = sha3_256('abc');
const h5b = sha3_256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
const h6a = keccak_256('abc');
const h7a = shake128('abc', { dkLen: 512 });
const h7b = shake256('abc', { dkLen: 512 });
```
See ([FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf), [Website](https://keccak.team/keccak.html)).
Check out [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub)
##### SHA3 Addons (cSHAKE, KMAC, TupleHash, ParallelHash, KangarooTwelve, MarsupilamiFourteen)
```typescript
import {
cshake128,
cshake256,
kmac128,
kmac256,
k12,
m14,
tuplehash128,
tuplehash256,
parallelhash128,
parallelhash256,
keccakprg,
} from '@noble/hashes/lib/sha3-addons.js';
const h7c = cshake128('abc', { personalization: 'def' });
const h7d = cshake256('abc', { personalization: 'def' });
const h7e = kmac128('key', 'message');
const h7f = kmac256('key', 'message');
const h7h = k12('abc');
const h7g = m14('abc');
const h7i = tuplehash128(['ab', 'c']); // tuplehash(['ab', 'c']) !== tuplehash(['a', 'bc']) !== tuplehash(['abc'])
// Same as k12/blake3, but without reduced number of rounds. Doesn't speedup anything due lack of SIMD and threading,
// added for compatibility.
const h7j = parallelhash128('abc', { blockLen: 8 });
// pseudo-random generator, first argument is capacity. XKCP recommends 254 bits capacity for 128-bit security strength.
// * with a capacity of 254 bits.
const p = keccakprg(254);
p.feed('test');
const rand1b = p.fetch(1);
```
- Full [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf): cSHAKE, KMAC, TupleHash, ParallelHash + XOF variants
- 🦘 K12 ([KangarooTwelve Paper](https://keccak.team/files/KangarooTwelve.pdf), [RFC Draft](https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-06.txt)) and M14 aka MarsupilamiFourteen are basically parallel versions of Keccak with reduced number of rounds (same as Blake3 and ParallelHash).
- [KeccakPRG](https://keccak.team/files/CSF-0.1.pdf): Pseudo-random generator based on Keccak
##### RIPEMD-160
```typescript
import { ripemd160 } from '@noble/hashes/lib/ripemd160.js';
// function ripemd160(data: Uint8Array): Uint8Array;
const hash8 = ripemd160('abc');
const hash9 = ripemd160()
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 2286](https://datatracker.ietf.org/doc/html/rfc2286), [Website](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html)
##### BLAKE2b, BLAKE2s
```typescript
import { blake2b } from '@noble/hashes/lib/blake2b.js';
import { blake2s } from '@noble/hashes/lib/blake2s.js';
const h10a = blake2s('abc');
const b2params = { key: new Uint8Array([1]), personalization: t, salt: t, dkLen: 32 };
const h10b = blake2s('abc', b2params);
const h10c = blake2s
.create(b2params)
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693), [Website](https://www.blake2.net).
##### BLAKE3
```typescript
import { blake3 } from '@noble/hashes/lib/blake3.js';
// All params are optional
const h11 = blake3('abc', { dkLen: 256, key: 'def', context: 'fji' });
```
See [Website](https://blake3.io).
##### HMAC
```typescript
import { hmac } from '@noble/hashes/lib/hmac.js';
import { sha256 } from '@noble/hashes/lib/sha256.js';
const mac1 = hmac(sha256, 'key', 'message');
const mac2 = hmac.create(sha256, Uint8Array.from([1, 2, 3])).update(Uint8Array.from([4, 5, 6]).digest();
```
Matches [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104).
##### HKDF
```typescript
import { hkdf } from '@noble/hashes/lib/kdf.js';
import { sha256 } from '@noble/hashes/lib/sha256.js';
import { randomBytes } from '../noble-hashes/utils.js';
const inputKey = randomBytes(32);
const salt = randomBytes(32);
const info = 'abc';
const dkLen = 32;
const hk1 = hkdf(sha256, inputKey, salt, info, dkLen);
// == same as
import { hkdf_extract, hkdf_expand } from '@noble/hashes/lib/kdf.js';
import { sha256 } from '@noble/hashes/lib/sha256.js';
const prk = hkdf_extract(sha256, inputKey, salt);
const hk2 = hkdf_expand(sha256, prk, info, dkLen);
```
Matches [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869).
##### PBKDF2
```typescript
import { pbkdf2, pbkdf2Async } from '@noble/hashes/lib/pbkdf2.js';
import { sha256 } from '@noble/hashes/lib/sha256.js';
const pbkey1 = pbkdf2(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey2 = await pbkdf2Async(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey3 = await pbkdf2Async(sha256, Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
c: 32,
dkLen: 32,
});
```
Matches [RFC 2898](https://datatracker.ietf.org/doc/html/rfc2898).
##### Scrypt
```typescript
import { scrypt, scryptAsync } from '@noble/hashes/lib/scrypt.js';
const scr1 = scrypt('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr2 = await scryptAsync('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr3 = await scryptAsync(Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
N: 2 ** 22,
r: 8,
p: 1,
dkLen: 32,
onProgress(percentage) {
console.log('progress', percentage);
},
maxmem: 2 ** 32 + 128 * 8 * 1, // N * r * p * 128 + (128*r*p)
});
```
Matches [RFC 7914](https://datatracker.ietf.org/doc/html/rfc7914), [Website](https://www.tarsnap.com/scrypt.html)
- `N, r, p` are work factors. To understand them, see [the blog post](https://blog.filippo.io/the-scrypt-parameters/).
- `dkLen` is the length of output bytes
- It is common to use N from `2**10` to `2**22` and `{r: 8, p: 1, dkLen: 32}`
- `onProgress` can be used with async version of the function to report progress to a user.
Memory usage of scrypt is calculated with the formula `N * r * p * 128 + (128 * r * p)`, which means
`{N: 2 ** 22, r: 8, p: 1}` will use 4GB + 1KB of memory. To prevent DoS, we limit scrypt to `1GB + 1KB` of RAM used,
which corresponds to `{N: 2 ** 20, r: 8, p: 1}`. If you want to use higher values, increase `maxmem` using the formula above.
_Note:_ noble supports `2**22` (4GB RAM) which is the highest amount amongst JS libs. Many other implementations don't support it.
We cannot support `2**23`, because there is a limitation in JS engines that makes allocating
arrays bigger than 4GB impossible, but we're looking into other possible solutions.
##### utils
```typescript
import { bytesToHex as toHex, randomBytes } from '@noble/hashes/lib/scrypt.js';
console.log(toHex(randomBytes(32)));
```
- `bytesToHex` will convert `Uint8Array` to a hex string
- `randomBytes(bytes)` will produce cryptographically secure random `Uint8Array` of length `bytes`
## Security
Noble is production-ready.
The library will be audited by an independent security firm in the next few months.
The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
A note on [timing attacks](https://en.wikipedia.org/wiki/Timing_attack): _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading rootkits with every `npm install`. Our goal is to minimize this attack vector.
## Speed
Benchmarks measured on Apple M1 with macOS 12 using 32-byte inputs.
Note that PBKDF2 and Scrypt are tested with extremely high work factor.
To run benchmarks, execute `npm run bench-install` and then `npm run bench`
```
SHA256 x 1,131,221 ops/sec @ 884ns/op
SHA384 x 452,284 ops/sec @ 2μs/op
SHA512 x 451,059 ops/sec @ 2μs/op
SHA3-256, keccak256, shake256 x 185,494 ops/sec @ 5μs/op
Kangaroo12 x 300,480 ops/sec @ 3μs/op
Marsupilami14 x 269,614 ops/sec @ 3μs/op
BLAKE2b x 291,375 ops/sec @ 3μs/op
BLAKE2s x 505,561 ops/sec @ 1μs/op
BLAKE3 x 576,036 ops/sec @ 1μs/op
HMAC-SHA256 x 342,583 ops/sec @ 2μs/op
RIPEMD160 x 1,191,895 ops/sec @ 839ns/op
HKDF-SHA256 x 115,500 ops/sec @ 8μs/op
PBKDF2-HMAC-SHA256 262144 x 2 ops/sec @ 338ms/op
PBKDF2-HMAC-SHA512 262144 x 0 ops/sec @ 1024ms/op
Scrypt r: 8, p: 1, n: 262144 x 1 ops/sec @ 637ms/op
```
Compare to native node.js implementation that uses C bindings instead of pure-js code:
```
SHA256 32B native x 1,164,144 ops/sec @ 859ns/op
SHA384 32B native x 938,086 ops/sec @ 1μs/op
SHA512 32B native x 946,969 ops/sec @ 1μs/op
SHA3 32B native x 879,507 ops/sec @ 1μs/op
keccak, k12, m14 are not implemented
BLAKE2b 32B native x 879,507 ops/sec @ 1μs/op
BLAKE2s 32B native x 977,517 ops/sec @ 1μs/op
BLAKE3 is not implemented
RIPEMD160 32B native x 913,242 ops/sec @ 1μs/op
HMAC-SHA256 32B native x 755,287 ops/sec @ 1μs/op
HKDF-SHA256 32B native x 207,856 ops/sec @ 4μs/op
PBKDF2-HMAC-SHA256 262144 native x 23 ops/sec @ 42ms/op
Scrypt 262144 native x 1 ops/sec @ 564ms/op
Scrypt 262144 scrypt.js x 0 ops/sec @ 1678ms/op
```
It is possible to [make this library 4x+ faster](./test/benchmark/README.md) by
_doing code generation of full loop unrolls_. We've decided against it. Reasons:
- the library must be auditable, with minimum amount of code, and zero dependencies
- most method invocations with the lib are going to be something like hashing 32b to 64kb of data
- hashing big inputs is 10x faster with low-level languages, which means you should probably pick 'em instead
The current performance is good enough when compared to other projects; SHA256 takes only 900 nanoseconds to run.
## Contributing & testing
1. Clone the repository.
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests. See [our approach to testing](./test/README.md)
5. `npm run test-dos` will test against DoS; by measuring function complexity. **Takes ~20 minutes**
6. `npm run test-big` will execute hashing on 4GB inputs,
scrypt with 1024 different `N, r, p` combinations, etc. **Takes several hours**. Using 8-32+ core CPU helps.
## License
The MIT License (MIT)
Copyright (c) 2021 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
See LICENSE file.
+27
View File
@@ -0,0 +1,27 @@
{
"author": "Jaco Greeff <jacogr@gmail.com>",
"bugs": "https://github.com/polkadot-js/common/issues",
"contributors": [],
"description": "An fork of @noble/hashes with extra protection on BigInt usage",
"engines": {
"node": ">=14.0.0"
},
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-noble-hashes#readme",
"license": "MIT",
"maintainers": [],
"name": "@polkadot/x-noble-hashes",
"repository": {
"directory": "packages/x-noble-hashes",
"type": "git",
"url": "https://github.com/polkadot-js/common.git"
},
"sideEffects": false,
"type": "module",
"version": "8.1.1",
"browser": {
"crypto": false
},
"dependencies": {
"@babel/runtime": "^7.16.3"
}
}
+126
View File
@@ -0,0 +1,126 @@
import { assertNumber, Hash, Input, toBytes, u32 } from './utils';
// prettier-ignore
export const SIGMA = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
// For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1].
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
]);
export type BlakeOpts = {
dkLen?: number;
key?: Input;
salt?: Input;
personalization?: Input;
};
export abstract class BLAKE2<T extends BLAKE2<T>> extends Hash<T> {
protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void;
protected abstract get(): number[];
protected abstract set(...args: number[]): void;
abstract override destroy(): void;
protected buffer: Uint8Array;
protected buffer32: Uint32Array;
protected length: number = 0;
protected pos: number = 0;
protected finished = false;
protected destroyed = false;
constructor(
readonly blockLen: number,
public outputLen: number,
opts: BlakeOpts = {},
keyLen: number,
saltLen: number,
persLen: number
) {
super();
assertNumber(blockLen);
assertNumber(outputLen);
assertNumber(keyLen);
if (outputLen < 0 || outputLen > keyLen)
throw new Error('Blake2: outputLen bigger than keyLen');
if (opts.key !== undefined && (opts.key.length < 1 || opts.key.length > keyLen))
throw new Error(`Key should be up 1..${keyLen} byte long or undefined`);
if (opts.salt !== undefined && opts.salt.length !== saltLen)
throw new Error(`Salt should be ${saltLen} byte long or undefined`);
if (opts.personalization !== undefined && opts.personalization.length !== persLen)
throw new Error(`Personalization should be ${persLen} byte long or undefined`);
this.buffer32 = u32((this.buffer = new Uint8Array(blockLen)));
}
update(data: Input) {
if (this.destroyed) throw new Error('instance is destroyed');
// Main difference with other hashes: there is flag for last block,
// so we cannot process current block before we know that there
// is the next one. This significantly complicates logic and reduces ability
// to do zero-copy processing
const { finished, blockLen, buffer, buffer32 } = this;
if (finished) throw new Error('digest() was already called');
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len; ) {
// If buffer is full and we still have input (don't process last block, same as blake2s)
if (this.pos === blockLen) {
this.compress(buffer32, 0, false);
this.pos = 0;
}
const take = Math.min(blockLen - this.pos, len - pos);
const dataOffset = data.byteOffset + pos;
// full block && aligned to 4 bytes && not last in input
if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
const data32 = new Uint32Array(data.buffer, dataOffset, Math.floor((len - pos) / 4));
for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
this.length += blockLen;
this.compress(data32, pos32, false);
}
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
this.length += take;
pos += take;
}
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
throw new Error('_Blake2: Invalid output buffer');
const { finished, pos, buffer32 } = this;
if (finished) throw new Error('digest() was already called');
this.finished = true;
// Padding
this.buffer.subarray(pos).fill(0);
this.compress(buffer32, 0, true);
const out32 = u32(out);
this.get().forEach((v, i) => (out32[i] = v));
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to?: T): T {
const { buffer, length, finished, destroyed, outputLen, pos } = this;
to ||= new (this.constructor as any)({ dkLen: outputLen }) as T;
to.set(...this.get());
to.length = length;
to.finished = finished;
to.destroyed = destroyed;
to.outputLen = outputLen;
to.buffer.set(buffer);
to.pos = pos;
return to;
}
}
+114
View File
@@ -0,0 +1,114 @@
import { Hash, createView, Input, toBytes } from './utils';
// Polyfill for Safari 14
function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void {
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
// Base SHA2 class (RFC 6234)
export abstract class SHA2<T extends SHA2<T>> extends Hash<T> {
protected abstract process(buf: DataView, offset: number): void;
protected abstract get(): number[];
protected abstract set(...args: number[]): void;
abstract override destroy(): void;
protected abstract roundClean(): void;
// For partial updates less than block size
protected buffer: Uint8Array;
protected view: DataView;
protected finished = false;
protected length = 0;
protected pos = 0;
protected destroyed = false;
constructor(
readonly blockLen: number,
public outputLen: number,
readonly padOffset: number,
readonly isLE: boolean
) {
super();
this.buffer = new Uint8Array(blockLen);
this.view = createView(this.buffer);
}
update(data: Input): this {
if (this.destroyed) throw new Error('instance is destroyed');
const { view, buffer, blockLen, finished } = this;
if (finished) throw new Error('digest() was already called');
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len; ) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input, cast it to view and process
if (take === blockLen) {
const dataView = createView(data);
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(view, 0);
this.pos = 0;
}
}
this.length += data.length;
this.roundClean();
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
throw new Error('_Sha2: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.finished = true;
// Padding
// We can avoid allocation of buffer for padding completely if it
// was previously not allocated here. But it won't change performance.
const { buffer, view, blockLen, isLE } = this;
let { pos } = this;
// append the bit '1' to the message
buffer[pos++] = 0b10000000;
this.buffer.subarray(pos).fill(0);
// we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
if (this.padOffset > blockLen - pos) {
this.process(view, 0);
pos = 0;
}
// Pad until full block byte with zeros
for (let i = pos; i < blockLen; i++) buffer[i] = 0;
// NOTE: sha512 requires length to be 128bit integer, but length in JS will overflow before that
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
// So we just write lowest 64bit of that value.
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
this.process(view, 0);
const oview = createView(out);
this.get().forEach((v, i) => oview.setUint32(4 * i, v, isLE));
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to?: T): T {
to ||= new (this.constructor as any)() as T;
to.set(...this.get());
const { blockLen, buffer, length, finished, destroyed, pos } = this;
to.length = length;
to.pos = pos;
to.finished = finished;
to.destroyed = destroyed;
if (length % blockLen) to.buffer.set(buffer);
return to;
}
}
+56
View File
@@ -0,0 +1,56 @@
const U32_MASK64 = BigInt(2 ** 32 - 1);
const _32n = BigInt(32);
export function fromBig(n: bigint, le = false) {
if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
}
export function split(lst: bigint[], le = false) {
let Ah = new Uint32Array(lst.length);
let Al = new Uint32Array(lst.length);
for (let i = 0; i < lst.length; i++) {
const { h, l } = fromBig(lst[i], le);
[Ah[i], Al[i]] = [h, l];
}
return [Ah, Al];
}
export const toBig = (h: number, l: number) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);
// for Shift in [0, 32)
export const shrSH = (h: number, l: number, s: number) => h >>> s;
export const shrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
// Right rotate for Shift in [1, 32)
export const rotrSH = (h: number, l: number, s: number) => (h >>> s) | (l << (32 - s));
export const rotrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
// Right rotate for Shift in (32, 64), NOTE: 32 is special case.
export const rotrBH = (h: number, l: number, s: number) => (h << (64 - s)) | (l >>> (s - 32));
export const rotrBL = (h: number, l: number, s: number) => (h >>> (s - 32)) | (l << (64 - s));
// Right rotate for shift===32 (just swaps l&h)
export const rotr32H = (h: number, l: number) => l;
export const rotr32L = (h: number, l: number) => h;
// Left rotate for Shift in [1, 32)
export const rotlSH = (h: number, l: number, s: number) => (h << s) | (l >>> (32 - s));
export const rotlSL = (h: number, l: number, s: number) => (l << s) | (h >>> (32 - s));
// Left rotate for Shift in (32, 64), NOTE: 32 is special case.
export const rotlBH = (h: number, l: number, s: number) => (l << (s - 32)) | (h >>> (64 - s));
export const rotlBL = (h: number, l: number, s: number) => (h << (s - 32)) | (l >>> (64 - s));
// JS uses 32-bit signed integers for bitwise operations which means we cannot
// simple take carry out of low bit sum by shift, we need to use division.
export function add(Ah: number, Al: number, Bh: number, Bl: number) {
const l = (Al >>> 0) + (Bl >>> 0);
return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };
}
// Addition with more than 2 elements
export const add3L = (Al: number, Bl: number, Cl: number) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
export const add3H = (low: number, Ah: number, Bh: number, Ch: number) =>
(Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;
export const add4L = (Al: number, Bl: number, Cl: number, Dl: number) =>
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
export const add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number) =>
(Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;
export const add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number) =>
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
export const add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) =>
(Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;
+200
View File
@@ -0,0 +1,200 @@
import * as blake2 from './_blake2';
import * as u64 from './_u64';
import { toBytes, u32, wrapConstructorWithOpts } from './utils';
// Same as SHA-512 but LE
// prettier-ignore
const IV = new Uint32Array([
0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19
]);
// Temporary buffer
const BUF = new Uint32Array(32);
// Mixing function G splitted in two halfs
function G1(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
// NOTE: V is LE here
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
// v[a] = (v[a] + v[b] + x) | 0;
let ll = u64.add3L(Al, Bl, Xl);
Ah = u64.add3H(ll, Ah, Bh, Xh);
Al = ll | 0;
// v[d] = rotr(v[d] ^ v[a], 32)
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });
// v[c] = (v[c] + v[d]) | 0;
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
// v[b] = rotr(v[b] ^ v[c], 24)
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) });
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
}
function G2(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
// NOTE: V is LE here
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
// v[a] = (v[a] + v[b] + x) | 0;
let ll = u64.add3L(Al, Bl, Xl);
Ah = u64.add3H(ll, Ah, Bh, Xh);
Al = ll | 0;
// v[d] = rotr(v[d] ^ v[a], 16)
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });
// v[c] = (v[c] + v[d]) | 0;
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
// v[b] = rotr(v[b] ^ v[c], 63)
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) });
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
}
class BLAKE2b extends blake2.BLAKE2<BLAKE2b> {
// Same as SHA-512, but LE
private v0l = IV[0] | 0;
private v0h = IV[1] | 0;
private v1l = IV[2] | 0;
private v1h = IV[3] | 0;
private v2l = IV[4] | 0;
private v2h = IV[5] | 0;
private v3l = IV[6] | 0;
private v3h = IV[7] | 0;
private v4l = IV[8] | 0;
private v4h = IV[9] | 0;
private v5l = IV[10] | 0;
private v5h = IV[11] | 0;
private v6l = IV[12] | 0;
private v6h = IV[13] | 0;
private v7l = IV[14] | 0;
private v7h = IV[15] | 0;
constructor(opts: blake2.BlakeOpts = {}) {
super(128, opts.dkLen === undefined ? 64 : opts.dkLen, opts, 64, 16, 16);
const keyLength = opts.key ? opts.key.length : 0;
this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
if (opts.salt) {
const salt = u32(toBytes(opts.salt));
this.v4l ^= salt[0];
this.v4h ^= salt[1];
this.v5l ^= salt[2];
this.v5h ^= salt[3];
}
if (opts.personalization) {
const pers = u32(toBytes(opts.personalization));
this.v6l ^= pers[0];
this.v6h ^= pers[1];
this.v7l ^= pers[2];
this.v7h ^= pers[3];
}
if (opts.key) {
// Pad to blockLen and update
const tmp = new Uint8Array(this.blockLen);
tmp.set(toBytes(opts.key));
this.update(tmp);
}
}
// prettier-ignore
protected get(): [
number, number, number, number, number, number, number, number,
number, number, number, number, number, number, number, number
] {
let {v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h} = this;
return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];
}
// prettier-ignore
protected set(
v0l: number, v0h: number, v1l: number, v1h: number,
v2l: number, v2h: number, v3l: number, v3h: number,
v4l: number, v4h: number, v5l: number, v5h: number,
v6l: number, v6h: number, v7l: number, v7h: number
) {
this.v0l = v0l | 0;
this.v0h = v0h | 0;
this.v1l = v1l | 0;
this.v1h = v1h | 0;
this.v2l = v2l | 0;
this.v2h = v2h | 0;
this.v3l = v3l | 0;
this.v3h = v3h | 0;
this.v4l = v4l | 0;
this.v4h = v4h | 0;
this.v5l = v5l | 0;
this.v5h = v5h | 0;
this.v6l = v6l | 0;
this.v6h = v6h | 0;
this.v7l = v7l | 0;
this.v7h = v7h | 0;
}
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
this.get().forEach((v, i) => (BUF[i] = v)); // First half from state.
BUF.set(IV, 16); // Second half from IV.
let { h, l } = u64.fromBig(BigInt(this.length));
BUF[24] = IV[8] ^ l; // Low word of the offset.
BUF[25] = IV[9] ^ h; // High word.
// Invert all bits for last block
if (isLast) {
BUF[28] = ~BUF[28];
BUF[29] = ~BUF[29];
}
let j = 0;
const s = blake2.SIGMA;
for (let i = 0; i < 12; i++) {
G1(0, 4, 8, 12, msg, offset + 2 * s[j++]);
G2(0, 4, 8, 12, msg, offset + 2 * s[j++]);
G1(1, 5, 9, 13, msg, offset + 2 * s[j++]);
G2(1, 5, 9, 13, msg, offset + 2 * s[j++]);
G1(2, 6, 10, 14, msg, offset + 2 * s[j++]);
G2(2, 6, 10, 14, msg, offset + 2 * s[j++]);
G1(3, 7, 11, 15, msg, offset + 2 * s[j++]);
G2(3, 7, 11, 15, msg, offset + 2 * s[j++]);
G1(0, 5, 10, 15, msg, offset + 2 * s[j++]);
G2(0, 5, 10, 15, msg, offset + 2 * s[j++]);
G1(1, 6, 11, 12, msg, offset + 2 * s[j++]);
G2(1, 6, 11, 12, msg, offset + 2 * s[j++]);
G1(2, 7, 8, 13, msg, offset + 2 * s[j++]);
G2(2, 7, 8, 13, msg, offset + 2 * s[j++]);
G1(3, 4, 9, 14, msg, offset + 2 * s[j++]);
G2(3, 4, 9, 14, msg, offset + 2 * s[j++]);
}
this.v0l ^= BUF[0] ^ BUF[16];
this.v0h ^= BUF[1] ^ BUF[17];
this.v1l ^= BUF[2] ^ BUF[18];
this.v1h ^= BUF[3] ^ BUF[19];
this.v2l ^= BUF[4] ^ BUF[20];
this.v2h ^= BUF[5] ^ BUF[21];
this.v3l ^= BUF[6] ^ BUF[22];
this.v3h ^= BUF[7] ^ BUF[23];
this.v4l ^= BUF[8] ^ BUF[24];
this.v4h ^= BUF[9] ^ BUF[25];
this.v5l ^= BUF[10] ^ BUF[26];
this.v5h ^= BUF[11] ^ BUF[27];
this.v6l ^= BUF[12] ^ BUF[28];
this.v6h ^= BUF[13] ^ BUF[29];
this.v7l ^= BUF[14] ^ BUF[30];
this.v7h ^= BUF[15] ^ BUF[31];
BUF.fill(0);
}
destroy() {
this.destroyed = true;
this.buffer32.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
export const blake2b = wrapConstructorWithOpts<BLAKE2b, blake2.BlakeOpts>(
(opts) => new BLAKE2b(opts)
);
+133
View File
@@ -0,0 +1,133 @@
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import { rotr, toBytes, wrapConstructorWithOpts, u32 } from './utils';
// Initial state:
// first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19)
// same as SHA-256
// prettier-ignore
export const IV = new Uint32Array([
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]);
// Mixing function G splitted in two halfs
function G1(a: number, b: number, c: number, d: number, x: number) {
a = (a + b + x) | 0;
d = rotr(d ^ a, 16);
c = (c + d) | 0;
b = rotr(b ^ c, 12);
return { a, b, c, d };
}
function G2(a: number, b: number, c: number, d: number, x: number) {
a = (a + b + x) | 0;
d = rotr(d ^ a, 8);
c = (c + d) | 0;
b = rotr(b ^ c, 7);
return { a, b, c, d };
}
// prettier-ignore
export function compress(s: Uint8Array, offset: number, msg: Uint32Array, rounds: number,
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number,
v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,
) {
let j = 0;
for (let i = 0; i < rounds; i++) {
({ a: v0, b: v4, c: v8, d: v12 } = G1(v0, v4, v8, v12, msg[offset + s[j++]]));
({ a: v0, b: v4, c: v8, d: v12 } = G2(v0, v4, v8, v12, msg[offset + s[j++]]));
({ a: v1, b: v5, c: v9, d: v13 } = G1(v1, v5, v9, v13, msg[offset + s[j++]]));
({ a: v1, b: v5, c: v9, d: v13 } = G2(v1, v5, v9, v13, msg[offset + s[j++]]));
({ a: v2, b: v6, c: v10, d: v14 } = G1(v2, v6, v10, v14, msg[offset + s[j++]]));
({ a: v2, b: v6, c: v10, d: v14 } = G2(v2, v6, v10, v14, msg[offset + s[j++]]));
({ a: v3, b: v7, c: v11, d: v15 } = G1(v3, v7, v11, v15, msg[offset + s[j++]]));
({ a: v3, b: v7, c: v11, d: v15 } = G2(v3, v7, v11, v15, msg[offset + s[j++]]));
({ a: v0, b: v5, c: v10, d: v15 } = G1(v0, v5, v10, v15, msg[offset + s[j++]]));
({ a: v0, b: v5, c: v10, d: v15 } = G2(v0, v5, v10, v15, msg[offset + s[j++]]));
({ a: v1, b: v6, c: v11, d: v12 } = G1(v1, v6, v11, v12, msg[offset + s[j++]]));
({ a: v1, b: v6, c: v11, d: v12 } = G2(v1, v6, v11, v12, msg[offset + s[j++]]));
({ a: v2, b: v7, c: v8, d: v13 } = G1(v2, v7, v8, v13, msg[offset + s[j++]]));
({ a: v2, b: v7, c: v8, d: v13 } = G2(v2, v7, v8, v13, msg[offset + s[j++]]));
({ a: v3, b: v4, c: v9, d: v14 } = G1(v3, v4, v9, v14, msg[offset + s[j++]]));
({ a: v3, b: v4, c: v9, d: v14 } = G2(v3, v4, v9, v14, msg[offset + s[j++]]));
}
return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };
}
class BLAKE2s extends blake2.BLAKE2<BLAKE2s> {
// Internal state, same as SHA-256
private v0 = IV[0] | 0;
private v1 = IV[1] | 0;
private v2 = IV[2] | 0;
private v3 = IV[3] | 0;
private v4 = IV[4] | 0;
private v5 = IV[5] | 0;
private v6 = IV[6] | 0;
private v7 = IV[7] | 0;
constructor(opts: blake2.BlakeOpts = {}) {
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, opts, 32, 8, 8);
const keyLength = opts.key ? opts.key.length : 0;
this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
if (opts.salt) {
const salt = u32(toBytes(opts.salt));
this.v4 ^= salt[0];
this.v5 ^= salt[1];
}
if (opts.personalization) {
const pers = u32(toBytes(opts.personalization));
this.v6 ^= pers[0];
this.v7 ^= pers[1];
}
if (opts.key) {
// Pad to blockLen and update
const tmp = new Uint8Array(this.blockLen);
tmp.set(toBytes(opts.key));
this.update(tmp);
}
}
protected get(): [number, number, number, number, number, number, number, number] {
const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;
return [v0, v1, v2, v3, v4, v5, v6, v7];
}
// prettier-ignore
protected set(
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number
) {
this.v0 = v0 | 0;
this.v1 = v1 | 0;
this.v2 = v2 | 0;
this.v3 = v3 | 0;
this.v4 = v4 | 0;
this.v5 = v5 | 0;
this.v6 = v6 | 0;
this.v7 = v7 | 0;
}
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
const { h, l } = u64.fromBig(BigInt(this.length));
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
compress(
blake2.SIGMA, offset, msg, 10,
this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7,
IV[0], IV[1], IV[2], IV[3], l ^ IV[4], h ^ IV[5], isLast ? ~IV[6] : IV[6], IV[7]
);
this.v0 ^= v0 ^ v8;
this.v1 ^= v1 ^ v9;
this.v2 ^= v2 ^ v10;
this.v3 ^= v3 ^ v11;
this.v4 ^= v4 ^ v12;
this.v5 ^= v5 ^ v13;
this.v6 ^= v6 ^ v14;
this.v7 ^= v7 ^ v15;
}
destroy() {
this.destroyed = true;
this.buffer32.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0);
}
}
export const blake2s = wrapConstructorWithOpts<BLAKE2s, blake2.BlakeOpts>(
(opts) => new BLAKE2s(opts)
);
+240
View File
@@ -0,0 +1,240 @@
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import * as blake2s from './blake2s';
import { Input, u8, u32, toBytes, wrapConstructorWithOpts, assertNumber, HashXOF } from './utils';
// Flag bitset
enum Flags {
CHUNK_START = 1 << 0,
CHUNK_END = 1 << 1,
PARENT = 1 << 2,
ROOT = 1 << 3,
KEYED_HASH = 1 << 4,
DERIVE_KEY_CONTEXT = 1 << 5,
DERIVE_KEY_MATERIAL = 1 << 6,
}
const SIGMA: Uint8Array = (() => {
const Id = Array.from({ length: 16 }, (_, i) => i);
const permute = (arr: number[]) =>
[2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);
const res: number[] = [];
for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v);
return Uint8Array.from(res);
})();
// - key: is 256-bit key
// - context: string should be hardcoded, globally unique, and application - specific.
// A good default format for the context string is "[application] [commit timestamp] [purpose]"
// - Only one of 'key' (keyed mode) or 'context' (derive key mode) can be used at same time
export type Blake3Opts = { dkLen?: number; key?: Input; context?: Input };
// Why is this so slow? It should be 6x faster than blake2b.
// - There is only 30% reduction in number of rounds from blake2s
// - This function uses tree mode to achive parallelisation via SIMD and threading,
// however in JS we don't have threads and SIMD, so we get only overhead from tree structure
// - It is possible to speed it up via Web Workers, hovewer it will make code singnificantly more
// complicated, which we are trying to avoid, since this library is intended to be used
// for cryptographic purposes. Also, parallelization happens only on chunk level (1024 bytes),
// which won't really benefit small inputs.
class BLAKE3 extends blake2.BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {
private IV: Uint32Array;
private flags = 0 | 0;
private state: Uint32Array;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
private stack: Uint32Array[] = [];
// Output
private posOut = 0;
private bufferOut32 = new Uint32Array(16);
private bufferOut: Uint8Array;
private chunkOut = 0; // index of output chunk
private enableXOF = true;
constructor(opts: Blake3Opts = {}, flags = 0) {
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, {}, Number.MAX_SAFE_INTEGER, 0, 0);
this.outputLen = opts.dkLen === undefined ? 32 : opts.dkLen;
assertNumber(this.outputLen);
if (opts.key !== undefined && opts.context !== undefined)
throw new Error('Blake3: only key or context can be specified at same time');
else if (opts.key !== undefined) {
const key = toBytes(opts.key);
if (key.length !== 32) throw new Error('Blake3: key should be 32 byte');
this.IV = u32(key);
this.flags = flags | Flags.KEYED_HASH;
} else if (opts.context !== undefined) {
const context_key = new BLAKE3({ dkLen: 32 }, Flags.DERIVE_KEY_CONTEXT)
.update(opts.context)
.digest();
this.IV = u32(context_key);
this.flags = flags | Flags.DERIVE_KEY_MATERIAL;
} else {
this.IV = blake2s.IV.slice();
this.flags = flags;
}
this.state = this.IV.slice();
this.bufferOut = u8(this.bufferOut32);
}
// Unused
protected get() {
return [];
}
protected set() {}
private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) {
const { state, pos } = this;
const { h, l } = u64.fromBig(BigInt(counter), true);
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
blake2s.compress(
SIGMA, bufPos, buf, 7,
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], h, l, pos, flags
);
state[0] = v0 ^ v8;
state[1] = v1 ^ v9;
state[2] = v2 ^ v10;
state[3] = v3 ^ v11;
state[4] = v4 ^ v12;
state[5] = v5 ^ v13;
state[6] = v6 ^ v14;
state[7] = v7 ^ v15;
}
protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false) {
// Compress last block
let flags = this.flags;
if (!this.chunkPos) flags |= Flags.CHUNK_START;
if (this.chunkPos === 15 || isLast) flags |= Flags.CHUNK_END;
if (!isLast) this.pos = this.blockLen;
this.b2Compress(this.chunksDone, flags, buf, bufPos);
this.chunkPos += 1;
// If current block is last in chunk (16 blocks), then compress chunks
if (this.chunkPos === 16 || isLast) {
let chunk = this.state;
this.state = this.IV.slice();
// If not the last one, compress only when there are trailing zeros in chunk counter
// chunks used as binary tree where current stack is path. Zero means current leaf is finished and can be compressed.
// 1 (001) - leaf not finished (just push current chunk to stack)
// 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back)
// 3 (011) - last leaf not finished
// 4 (100) - leafs finished at depth=1 and depth=2
for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {
if (!(last = this.stack.pop())) break;
this.buffer32.set(last, 0);
this.buffer32.set(chunk, 8);
this.pos = this.blockLen;
this.b2Compress(0, this.flags | Flags.PARENT, this.buffer32, 0);
chunk = this.state;
this.state = this.IV.slice();
}
this.chunksDone++;
this.chunkPos = 0;
this.stack.push(chunk);
}
this.pos = 0;
}
override _cloneInto(to?: BLAKE3): BLAKE3 {
to = super._cloneInto(to) as BLAKE3;
const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;
to.state.set(state.slice());
to.stack = stack.map((i) => Uint32Array.from(i));
to.IV.set(IV);
to.flags = flags;
to.chunkPos = chunkPos;
to.chunksDone = chunksDone;
to.posOut = posOut;
to.chunkOut = chunkOut;
to.enableXOF = this.enableXOF;
to.bufferOut32.set(this.bufferOut32);
return to;
}
destroy() {
this.destroyed = true;
this.state.fill(0);
this.buffer32.fill(0);
this.IV.fill(0);
this.bufferOut32.fill(0);
for (let i of this.stack) i.fill(0);
}
// Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)
private b2CompressOut() {
const { state, pos, flags, buffer32, bufferOut32 } = this;
const { h, l } = u64.fromBig(BigInt(this.chunkOut++));
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
blake2s.compress(
SIGMA, 0, buffer32, 7,
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], l, h, pos, flags
);
bufferOut32[0] = v0 ^ v8;
bufferOut32[1] = v1 ^ v9;
bufferOut32[2] = v2 ^ v10;
bufferOut32[3] = v3 ^ v11;
bufferOut32[4] = v4 ^ v12;
bufferOut32[5] = v5 ^ v13;
bufferOut32[6] = v6 ^ v14;
bufferOut32[7] = v7 ^ v15;
bufferOut32[8] = state[0] ^ v8;
bufferOut32[9] = state[1] ^ v9;
bufferOut32[10] = state[2] ^ v10;
bufferOut32[11] = state[3] ^ v11;
bufferOut32[12] = state[4] ^ v12;
bufferOut32[13] = state[5] ^ v13;
bufferOut32[14] = state[6] ^ v14;
bufferOut32[15] = state[7] ^ v15;
this.posOut = 0;
}
protected finish() {
if (this.finished) return;
this.finished = true;
// Padding
this.buffer.fill(0, this.pos);
// Process last chunk
let flags = this.flags | Flags.ROOT;
if (this.stack.length) {
flags |= Flags.PARENT;
this.compress(this.buffer32, 0, true);
this.chunksDone = 0;
this.pos = this.blockLen;
} else {
flags |= (!this.chunkPos ? Flags.CHUNK_START : 0) | Flags.CHUNK_END;
}
this.flags = flags;
this.b2CompressOut();
}
private writeInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array)) throw new Error('Blake3: Invalid output buffer');
this.finish();
const { blockLen, bufferOut } = this;
for (let pos = 0, len = out.length; pos < len; ) {
if (this.posOut >= blockLen) this.b2CompressOut();
const take = Math.min(this.blockLen - this.posOut, len - pos);
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
this.posOut += take;
pos += take;
}
return out;
}
xofInto(out: Uint8Array): Uint8Array {
if (!this.enableXOF) throw new Error('XOF impossible after digest call');
return this.writeInto(out);
}
xof(bytes: number): Uint8Array {
assertNumber(bytes);
return this.xofInto(new Uint8Array(bytes));
}
override digestInto(out: Uint8Array) {
if (out.length < this.outputLen) throw new Error('Blake3: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.enableXOF = false;
this.writeInto(out);
this.destroy();
return out;
}
override digest() {
return this.digestInto(new Uint8Array(this.outputLen));
}
}
export const blake3 = wrapConstructorWithOpts<BLAKE3, Blake3Opts>((opts) => new BLAKE3(opts));
+6
View File
@@ -0,0 +1,6 @@
import nodeCrypto from 'crypto';
export const crypto: { node?: any; web?: any } = {
node: nodeCrypto,
web: undefined,
};
@@ -0,0 +1,6 @@
// Global symbol available in browsers only
declare const self: Record<string, any> | undefined;
export const crypto: { node?: any; web?: any } = {
node: undefined,
web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
};
+60
View File
@@ -0,0 +1,60 @@
// prettier-ignore
import {
assertHash, assertNumber, CHash, Input, toBytes
} from './utils';
import { hmac } from './hmac';
// HKDF (RFC 5869)
// HKDF-Extract(IKM, salt) -> PRK NOTE: arguments position differs from spec (IKM is first one, since it is not optional)
export function hkdf_extract(hash: CHash, ikm: Input, salt?: Input) {
assertHash(hash);
// NOTE: some libraries treats zero-length array as 'not provided', we don't, since we have undefined as 'not provided'
// More info: https://github.com/RustCrypto/KDFs/issues/15
if (salt === undefined) salt = new Uint8Array(hash.outputLen); // if not provided, it is set to a string of HashLen zeros
return hmac(hash, toBytes(salt), toBytes(ikm));
}
// HKDF-Expand(PRK, info, L) -> OKM
const HKDF_COUNTER = new Uint8Array([0]);
const EMPTY_BUFFER = new Uint8Array();
export function hkdf_expand(
hash: CHash,
prk: Input, // a pseudorandom key of at least HashLen octets (usually, the output from the extract step)
info?: Input, // optional context and application specific information (can be a zero-length string)
length: number = 32 // length of output keying material in octets
) {
assertHash(hash);
assertNumber(length);
if (length > 255 * hash.outputLen) throw new Error('Length should be <= 255*HashLen');
const blocks = Math.ceil(length / hash.outputLen);
if (info === undefined) info = EMPTY_BUFFER;
// first L(ength) octets of T
const okm = new Uint8Array(blocks * hash.outputLen);
// Re-use HMAC instance between blocks
const HMAC = hmac.init(hash, prk);
const HMACTmp = HMAC._cloneInto();
const T = new Uint8Array(HMAC.outputLen);
for (let counter = 0; counter < blocks; counter++) {
HKDF_COUNTER[0] = counter + 1;
// T(0) = empty string (zero length)
// T(N) = HMAC-Hash(PRK, T(N-1) | info | N)
HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)
.update(info)
.update(HKDF_COUNTER)
.digestInto(T);
okm.set(T, hash.outputLen * counter);
HMAC._cloneInto(HMACTmp);
}
HMAC.destroy();
HMACTmp.destroy();
T.fill(0);
return okm.slice(0, length);
}
// Extract+Expand
export const hkdf = (
hash: CHash,
ikm: Input,
salt: Input | undefined,
info: Input | undefined,
length: number
) => hkdf_expand(hash, hkdf_extract(hash, ikm, salt), info, length);
+76
View File
@@ -0,0 +1,76 @@
import { assertHash, Hash, CHash, Input, toBytes } from './utils';
// HMAC (RFC 2104)
class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {
oHash: T;
iHash: T;
blockLen: number;
outputLen: number;
private finished = false;
private destroyed = false;
constructor(hash: CHash, _key: Input) {
super();
assertHash(hash);
const key = toBytes(_key);
this.iHash = hash.create() as T;
if (!(this.iHash instanceof Hash))
throw new TypeError('Expected instance of class which extends utils.Hash');
const blockLen = (this.blockLen = this.iHash.blockLen);
this.outputLen = this.iHash.outputLen;
const pad = new Uint8Array(blockLen);
// blockLen can be bigger than outputLen
pad.set(key.length > this.iHash.blockLen ? hash.create().update(key).digest() : key);
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;
this.iHash.update(pad);
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
this.oHash = hash.create() as T;
// Undo internal XOR && apply outer XOR
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;
this.oHash.update(pad);
pad.fill(0);
}
update(buf: Input) {
if (this.destroyed) throw new Error('instance is destroyed');
this.iHash.update(buf);
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length !== this.outputLen)
throw new Error('HMAC: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.finished = true;
this.iHash.digestInto(out);
this.oHash.update(out);
this.oHash.digestInto(out);
this.destroy();
}
digest() {
const out = new Uint8Array(this.oHash.outputLen);
this.digestInto(out);
return out;
}
_cloneInto(to?: HMAC<T>): HMAC<T> {
// Create new instance without calling constructor since key already in state and we don't know it.
to ||= Object.create(Object.getPrototypeOf(this), {});
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
to = to as this;
to.finished = finished;
to.destroyed = destroyed;
to.blockLen = blockLen;
to.outputLen = outputLen;
to.oHash = oHash._cloneInto(to.oHash);
to.iHash = iHash._cloneInto(to.iHash);
return to;
}
destroy() {
this.destroyed = true;
this.oHash.destroy();
this.iHash.destroy();
}
}
export const hmac = (hash: CHash, key: Input, message: Input): Uint8Array =>
new HMAC<any>(hash, key).update(message).digest();
hmac.create = (hash: CHash, key: Input) => new HMAC<any>(hash, key);
hmac.init = hmac.create;
+3
View File
@@ -0,0 +1,3 @@
throw new Error(
'noble-hashes have no entry-point. Please consult the README.md to learn how to use them'
);
@@ -0,0 +1,6 @@
// Copyright 2017-2021 @polkadot/x-noble-hashes authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/x-noble-hashes', version: '8.1.1' };
+92
View File
@@ -0,0 +1,92 @@
import { hmac } from './hmac';
// prettier-ignore
import {
Hash, CHash, Input, createView, toBytes, assertNumber, assertHash, checkOpts, asyncLoop
} from './utils';
// PBKDF (RFC 2898)
export type Pbkdf2Opt = {
c: number; // Iterations
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
asyncTick?: number; // Maximum time in ms for which async function can block execution
};
// Common prologue and epilogue for sync/async functions
function pbkdf2Init(hash: CHash, _password: Input, _salt: Input, _opts: Pbkdf2Opt) {
assertHash(hash);
const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
const { c, dkLen, asyncTick } = opts;
assertNumber(c);
assertNumber(dkLen);
assertNumber(asyncTick);
if (c < 1) throw new Error('PBKDF2: iterations (c) should be >= 1');
const password = toBytes(_password);
const salt = toBytes(_salt);
// DK = PBKDF2(PRF, Password, Salt, c, dkLen);
const DK = new Uint8Array(dkLen);
// U1 = PRF(Password, Salt + INT_32_BE(i))
const PRF = hmac.init(hash, password);
const PRFSalt = PRF._cloneInto().update(salt);
return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
}
function pbkdf2Output<T extends Hash<T>>(
PRF: Hash<T>,
PRFSalt: Hash<T>,
DK: Uint8Array,
prfW: Hash<T>,
u: Uint8Array
) {
PRF.destroy();
PRFSalt.destroy();
if (prfW) prfW.destroy();
u.fill(0);
return DK;
}
export function pbkdf2(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
let prfW: any; // Working copy
const arr = new Uint8Array(4);
const view = createView(arr);
const u = new Uint8Array(PRF.outputLen);
// DK = T1 + T2 + ⋯ + Tdklen/hlen
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
// Ti = F(Password, Salt, c, i)
const Ti = DK.subarray(pos, pos + PRF.outputLen);
view.setInt32(0, ti, false);
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
// U1 = PRF(Password, Salt + INT_32_BE(i))
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
Ti.set(u.subarray(0, Ti.length));
for (let ui = 1; ui < c; ui++) {
// Uc = PRF(Password, Uc−1)
PRF._cloneInto(prfW).update(u).digestInto(u);
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
}
}
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
}
export async function pbkdf2Async(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
let prfW: any; // Working copy
const arr = new Uint8Array(4);
const view = createView(arr);
const u = new Uint8Array(PRF.outputLen);
// DK = T1 + T2 + ⋯ + Tdklen/hlen
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
// Ti = F(Password, Salt, c, i)
const Ti = DK.subarray(pos, pos + PRF.outputLen);
view.setInt32(0, ti, false);
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
// U1 = PRF(Password, Salt + INT_32_BE(i))
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
Ti.set(u.subarray(0, Ti.length));
await asyncLoop(c - 1, asyncTick, (i) => {
// Uc = PRF(Password, Uc−1)
PRF._cloneInto(prfW).update(u).digestInto(u);
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
});
}
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
}
+105
View File
@@ -0,0 +1,105 @@
import { SHA2 } from './_sha2';
import { wrapConstructor } from './utils';
// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
// https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf
const Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]);
const Id = Uint8Array.from({ length: 16 }, (_, i) => i);
const Pi = Id.map((i) => (9 * i + 5) % 16);
let idxL = [Id];
let idxR = [Pi];
for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k]));
const shifts = [
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5],
].map((i) => new Uint8Array(i));
const shiftsL = idxL.map((idx, i) => idx.map((j) => shifts[i][j]));
const shiftsR = idxR.map((idx, i) => idx.map((j) => shifts[i][j]));
const Kl = new Uint32Array([0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]);
const Kr = new Uint32Array([0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]);
// The rotate left (circular left shift) operation for uint32
const rotl = (word: number, shift: number) => (word << shift) | (word >>> (32 - shift));
// It's called f() in spec.
function f(group: number, x: number, y: number, z: number): number {
if (group === 0) return x ^ y ^ z;
else if (group === 1) return (x & y) | (~x & z);
else if (group === 2) return (x | ~y) ^ z;
else if (group === 3) return (x & z) | (y & ~z);
else return x ^ (y | ~z);
}
// Temporary buffer, not used to store anything between runs
const BUF = new Uint32Array(16);
export class RIPEMD160 extends SHA2<RIPEMD160> {
private h0 = 0x67452301 | 0;
private h1 = 0xefcdab89 | 0;
private h2 = 0x98badcfe | 0;
private h3 = 0x10325476 | 0;
private h4 = 0xc3d2e1f0 | 0;
constructor() {
super(64, 20, 8, true);
}
protected get(): [number, number, number, number, number] {
const { h0, h1, h2, h3, h4 } = this;
return [h0, h1, h2, h3, h4];
}
protected set(h0: number, h1: number, h2: number, h3: number, h4: number) {
this.h0 = h0 | 0;
this.h1 = h1 | 0;
this.h2 = h2 | 0;
this.h3 = h3 | 0;
this.h4 = h4 | 0;
}
protected process(view: DataView, offset: number): void {
for (let i = 0; i < 16; i++, offset += 4) BUF[i] = view.getUint32(offset, true);
// prettier-ignore
let al = this.h0 | 0, ar = al,
bl = this.h1 | 0, br = bl,
cl = this.h2 | 0, cr = cl,
dl = this.h3 | 0, dr = dl,
el = this.h4 | 0, er = el;
// Instead of iterating 0 to 80, we split it into 5 groups
// And use the groups in constants, functions, etc. Much simpler
for (let group = 0; group < 5; group++) {
const rGroup = 4 - group;
const hbl = Kl[group], hbr = Kr[group]; // prettier-ignore
const rl = idxL[group], rr = idxR[group]; // prettier-ignore
const sl = shiftsL[group], sr = shiftsR[group]; // prettier-ignore
for (let i = 0; i < 16; i++) {
const tl = (rotl(al + f(group, bl, cl, dl) + BUF[rl[i]] + hbl, sl[i]) + el) | 0;
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore
}
// 2 loops are 10% faster
for (let i = 0; i < 16; i++) {
const tr = (rotl(ar + f(rGroup, br, cr, dr) + BUF[rr[i]] + hbr, sr[i]) + er) | 0;
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore
}
}
// Add the compressed chunk to the current hash value
this.set(
(this.h1 + cl + dr) | 0,
(this.h2 + dl + er) | 0,
(this.h3 + el + ar) | 0,
(this.h4 + al + br) | 0,
(this.h0 + bl + cr) | 0
);
}
protected roundClean() {
BUF.fill(0);
}
destroy() {
this.destroyed = true;
this.buffer.fill(0);
this.set(0, 0, 0, 0, 0);
}
}
export const ripemd160 = wrapConstructor(() => new RIPEMD160());
+219
View File
@@ -0,0 +1,219 @@
import { sha256 } from './sha256';
import { pbkdf2 } from './pbkdf2';
import { assertNumber, asyncLoop, checkOpts, Input, u32 } from './utils';
// Left rotate for uint32
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
// prettier-ignore
function XorAndSalsa(
prev: Uint32Array,
pi: number,
input: Uint32Array,
ii: number,
out: Uint32Array,
oi: number
) {
// Based on https://cr.yp.to/salsa20.html
// Xor blocks
let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
// Save state to temporary variables (salsa)
let x00 = y00, x01 = y01, x02 = y02, x03 = y03,
x04 = y04, x05 = y05, x06 = y06, x07 = y07,
x08 = y08, x09 = y09, x10 = y10, x11 = y11,
x12 = y12, x13 = y13, x14 = y14, x15 = y15;
// Main loop (salsa)
for (let i = 0; i < 8; i += 2) {
x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18);
}
// Write output (salsa)
out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
}
function BlockMix(input: Uint32Array, ii: number, out: Uint32Array, oi: number, r: number) {
// The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks)
let head = oi + 0;
let tail = oi + 16 * r;
for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1]
for (let i = 0; i < r; i++, head += 16, ii += 16) {
// We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1
XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1])
if (i > 0) tail += 16; // First iteration overwrites tmp value in tail
XorAndSalsa(out, head, input, (ii += 16), out, tail); // tail[i] = Salsa(blockIn[2*i+1] ^ head[i])
}
}
// RFC 7914
export type ScryptOpts = {
N: number; // costFactor CPU/memory cost parameter - Must be a power of 2 (e.g. 1024)
r: number; // blocksize parameter, which fine-tunes sequential memory read size and performance. (8 is commonly used)
p: number; // Parallelization parameter. (1 .. 232-1 * hLen/MFlen)
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
asyncTick?: number; // Maximum time in ms for which async function can block execution
maxmem?: number;
onProgress?: (progress: number) => void;
};
// Common prologue and epilogue for sync/async functions
function scryptInit(password: Input, salt: Input, _opts?: ScryptOpts) {
// Maxmem - 1GB+1KB by default
const opts = checkOpts(
{
dkLen: 32,
asyncTick: 10,
maxmem: 1024 ** 3 + 1024,
},
_opts
);
const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
assertNumber(N);
assertNumber(r);
assertNumber(p);
assertNumber(dkLen);
assertNumber(asyncTick);
assertNumber(maxmem);
if (onProgress !== undefined && typeof onProgress !== 'function')
throw new Error('progressCb should be function');
const blockSize = 128 * r;
const blockSize32 = blockSize / 4;
if (N <= 1 || (N & (N - 1)) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) {
// NOTE: we limit N to be less than 2**32 because of 32 bit variant of Integrify function
// There is no JS engines that allows alocate more than 4GB per single Uint8Array for now, but can change in future.
throw new Error(
'Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32'
);
}
if (p < 0 || p > ((2 ** 32 - 1) * 32) / blockSize) {
throw new Error(
'Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)'
);
}
if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) {
throw new Error(
'Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32'
);
}
const memUsed = blockSize * (N + p);
if (memUsed > maxmem) {
throw new Error(
`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`
);
}
// [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor)
// Since it has only one iteration there is no reason to use async variant
const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p });
const B32 = u32(B);
// Re-used between parallel iterations. Array(iterations) of B
const V = u32(new Uint8Array(blockSize * N));
const tmp = u32(new Uint8Array(blockSize));
let blockMixCb = () => {};
if (onProgress) {
const totalBlockMix = 2 * N * p;
// Invoke callback if progress changes from 10.01 to 10.02
// Allows to draw smooth progress bar on up to 8K screen
const callbackPer = Math.max(Math.floor(totalBlockMix / 10000), 1);
let blockMixCnt = 0;
blockMixCb = () => {
blockMixCnt++;
if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
onProgress(blockMixCnt / totalBlockMix);
};
}
return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
}
function scryptOutput(
password: Input,
dkLen: number,
B: Uint8Array,
V: Uint32Array,
tmp: Uint32Array
) {
const res = pbkdf2(sha256, password, B, { c: 1, dkLen });
B.fill(0);
V.fill(0);
tmp.fill(0);
return res;
}
export function scrypt(password: Input, salt: Input, _opts: ScryptOpts) {
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(
password,
salt,
_opts
);
for (let pi = 0; pi < p; pi++) {
const Pi = blockSize32 * pi;
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
for (let i = 0, pos = 0; i < N - 1; i++) {
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
blockMixCb();
}
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
blockMixCb();
for (let i = 0; i < N; i++) {
// First u32 of the last 64-byte block (u32 is LE)
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
blockMixCb();
}
}
return scryptOutput(password, dkLen, B, V, tmp);
}
export async function scryptAsync(password: Uint8Array, salt: Uint8Array, _opts: ScryptOpts) {
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit(
password,
salt,
_opts
);
for (let pi = 0; pi < p; pi++) {
const Pi = blockSize32 * pi;
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
let pos = 0;
await asyncLoop(N - 1, asyncTick, (i) => {
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
blockMixCb();
});
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
blockMixCb();
await asyncLoop(N, asyncTick, (i) => {
// First u32 of the last 64-byte block (u32 is LE)
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
blockMixCb();
});
}
return scryptOutput(password, dkLen, B, V, tmp);
}

Some files were not shown because too many files have changed in this diff Show More