Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bff05aad32 | ||
|
|
0f98c1ac22 | ||
|
|
f72d9a2a26 | ||
|
|
1d0558a537 | ||
|
|
94e7c05573 | ||
|
|
12601805b1 | ||
|
|
9194f417fe | ||
|
|
6a3118e66c | ||
|
|
4ccad202c5 | ||
|
|
96a019f6eb | ||
|
|
bad6a1b991 | ||
|
|
d83e50ee19 | ||
|
|
feae08fc0f | ||
|
|
fdd32df12f | ||
|
|
17a9b653ac | ||
|
|
6154454c38 | ||
|
|
ac0ec5be55 | ||
|
|
3802426046 | ||
|
|
d91b27b180 | ||
|
|
1f9a1a5f8a | ||
|
|
6b3edffe55 | ||
|
|
7d46eb8423 | ||
|
|
a7eb0ed319 | ||
|
|
227a34d55d | ||
|
|
8a29dc2b57 | ||
|
|
88d1a71df8 | ||
|
|
6f53068297 | ||
|
|
5bf71dab41 | ||
|
|
b6f3dfb2ca | ||
|
|
4d3a2d9610 | ||
|
|
7eaf013572 | ||
|
|
c673b96963 | ||
|
|
c57eee5027 | ||
|
|
9efd2db377 | ||
|
|
c90246ec0b | ||
|
|
fbe1532f62 | ||
|
|
f514402b15 | ||
|
|
5ea6992704 | ||
|
|
7955022ce5 | ||
|
|
000d097de2 | ||
|
|
8a6c8b64df | ||
|
|
306e503d23 | ||
|
|
648019f9d4 | ||
|
|
5e48cfbbaa | ||
|
|
57825fc73b | ||
|
|
19ae016723 | ||
|
|
8d7bed5865 | ||
|
|
e019a3043a | ||
|
|
9dfd013755 | ||
|
|
a5df4ed764 | ||
|
|
a938a9f998 | ||
|
|
1a6f81e2a7 | ||
|
|
6ab85a42bb | ||
|
|
21c9e1aefd | ||
|
|
58f740f3de | ||
|
|
ddc6b83e96 | ||
|
|
285e595930 | ||
|
|
dce16d59e6 | ||
|
|
b8edcb0e65 | ||
|
|
b63607c4a0 | ||
|
|
c56c86432a | ||
|
|
e52fe9a838 | ||
|
|
06dca3d955 | ||
|
|
0908dbb861 | ||
|
|
e97410df52 | ||
|
|
faa6734329 | ||
|
|
f2f25815c1 | ||
|
|
80785babb4 | ||
|
|
59761086d0 | ||
|
|
05c82721e8 | ||
|
|
1b13cf0eea | ||
|
|
e6777ece62 | ||
|
|
7436976598 | ||
|
|
699ef952c3 | ||
|
|
cd9dc35475 | ||
|
|
ee7923c457 | ||
|
|
bd3f51844c | ||
|
|
986dfe5006 | ||
|
|
1eba41b2e6 | ||
|
|
88c2d16951 | ||
|
|
303a57c4b5 | ||
|
|
7bfca0d40f | ||
|
|
916187dbf2 | ||
|
|
cfc598b30f | ||
|
|
67bfe3a52e | ||
|
|
0299cba18e | ||
|
|
182c6770d1 | ||
|
|
c89ec8a964 | ||
|
|
dc63304735 | ||
|
|
9ce825de94 | ||
|
|
3cb41c3b03 | ||
|
|
b1f9c37f6a | ||
|
|
4a69486993 | ||
|
|
3a7ab0454f | ||
|
|
ecfc262bcd | ||
|
|
776a653f42 | ||
|
|
ee99ffdbb4 | ||
|
|
bda848f484 | ||
|
|
82a63887a6 | ||
|
|
50317d7dc3 | ||
|
|
39e6a3c048 | ||
|
|
db93c3076b | ||
|
|
c8f0ad55c4 | ||
|
|
2104c50bd0 | ||
|
|
7e9b2770b6 | ||
|
|
8bf24c2f85 | ||
|
|
60337a3b55 | ||
|
|
f527c80b5c | ||
|
|
bdd2bcf236 | ||
|
|
cf370febf0 | ||
|
|
715ef6e7db | ||
|
|
cc644e85bf | ||
|
|
c7b8d95097 | ||
|
|
42de7cebe1 | ||
|
|
2e520c933a | ||
|
|
5d1dd0dd27 | ||
|
|
154f9f22f1 | ||
|
|
98e398954e | ||
|
|
3643c2e306 | ||
|
|
80195f6d11 | ||
|
|
6bc648bfe0 | ||
|
|
c29b12d3b5 | ||
|
|
8a5b514944 | ||
|
|
64fd66bd2a | ||
|
|
75aa7ba2c7 |
+6
-1
@@ -20,4 +20,9 @@
|
||||
7.1.2
|
||||
7.8.2
|
||||
7.9.2
|
||||
8.0.2
|
||||
8.0.2
|
||||
8.0.3
|
||||
8.0.4
|
||||
8.0.5
|
||||
8.1.2
|
||||
8.2.2
|
||||
@@ -1,5 +1,21 @@
|
||||
checks:
|
||||
argument-count:
|
||||
config:
|
||||
threshold: 5
|
||||
method-complexity:
|
||||
config:
|
||||
threshold: 7
|
||||
method-count:
|
||||
config:
|
||||
threshold: 25
|
||||
method-lines:
|
||||
config:
|
||||
threshold: 30
|
||||
|
||||
exclude_patterns:
|
||||
- "**/*.spec.js"
|
||||
- "**/*.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
-1
@@ -1 +1,2 @@
|
||||
packages/util-crypto/src/noble-hashes/lib/*.ts
|
||||
packages/x-noble-hashes/**/*.ts
|
||||
packages/x-noble-secp256k1/**/*.ts
|
||||
|
||||
+2
-8
@@ -6,18 +6,12 @@ const base = require('@polkadot/dev/config/eslint.cjs');
|
||||
module.exports = {
|
||||
...base,
|
||||
ignorePatterns: [
|
||||
'.eslintrc.js',
|
||||
'.github/**',
|
||||
'.vscode/**',
|
||||
'.yarn/**',
|
||||
'**/build/*',
|
||||
'**/coverage/*',
|
||||
'**/node_modules/*'
|
||||
...base.ignorePatterns
|
||||
],
|
||||
parserOptions: {
|
||||
...base.parserOptions,
|
||||
project: [
|
||||
'./tsconfig.json'
|
||||
'./tsconfig.eslint.json'
|
||||
]
|
||||
},
|
||||
rules: {
|
||||
|
||||
@@ -2,7 +2,7 @@ name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '15 1 * * *'
|
||||
- cron: '15 1/3 * * *'
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '50 1 * * *'
|
||||
- cron: '50 1/3 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
+3
-2
@@ -11,7 +11,9 @@ tmp/
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.idea
|
||||
.npmrc
|
||||
.vscode
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
@@ -24,5 +26,4 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
store.db
|
||||
store.db.compacted
|
||||
.idea
|
||||
.vscode
|
||||
tsconfig.*buildinfo
|
||||
|
||||
+68
-1
@@ -1,6 +1,73 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 8.0.2
|
||||
## 8.2.2 Dec 19, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add Bifrost Ledger capabilities (Thanks to https://github.com/awesomepan)
|
||||
- Add Sora genesisHash & Ledger capabilities (Thanks to https://github.com/NoodleSploder)
|
||||
|
||||
Changes:
|
||||
|
||||
- Add `isPromise` check utility function
|
||||
- Add `assertUnreachable` for codepaths that should not be followed
|
||||
- Preserve all-caps word splits in `stringCamelCase`
|
||||
- Adjust `x-*` fallbacks via `x-global/extractGlobal`
|
||||
- Sync with upstream Substrate ss58 registry
|
||||
- Additional tests for `bip39` wordlists
|
||||
- Maintainability cleanups
|
||||
- Update to latest Ledger dependencies
|
||||
- Swap to upstream `@noble` packages
|
||||
- Adjust tsconfig with references
|
||||
- 8.2.2 (8.2.1 failed CI due to incorrect CHANGELOG)
|
||||
|
||||
|
||||
## 8.1.2 Dec 5, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Adjustment to non-full-word consecutive uppercase via `string{Camel, Pascal}Case`
|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||
+2
-3
@@ -13,8 +13,7 @@ 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-(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-(bigint|global)(.*)$': '<rootDir>/packages/x-$1/src/$2',
|
||||
'@polkadot/x-(fetch|randomvalues|textdecoder|textencoder|ws)(.*)$': '<rootDir>/packages/x-$1/src/node'
|
||||
}
|
||||
});
|
||||
|
||||
+5
-5
@@ -10,7 +10,7 @@
|
||||
"url": "https://github.com/polkadot-js/common.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -28,12 +28,12 @@
|
||||
"test:one": "polkadot-dev-run-test --runInBand"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@polkadot/dev": "^0.63.33",
|
||||
"@polkadot/ts": "^0.4.15",
|
||||
"@babel/core": "^7.16.5",
|
||||
"@polkadot/dev": "^0.64.5",
|
||||
"@polkadot/ts": "^0.4.20",
|
||||
"@types/jest": "^27.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"typescript": "^4.5.2"
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
"react-native": "react-native.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@ledgerhq/hw-transport": "^6.11.2",
|
||||
"@ledgerhq/hw-transport-webhid": "^6.11.2",
|
||||
"@ledgerhq/hw-transport-webusb": "^6.11.2"
|
||||
"@babel/runtime": "^7.16.5",
|
||||
"@ledgerhq/hw-transport": "^6.20.0",
|
||||
"@ledgerhq/hw-transport-webhid": "^6.20.0",
|
||||
"@ledgerhq/hw-transport-webusb": "^6.20.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^6.11.2"
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^6.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.0.2' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.2.2' };
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
@@ -19,12 +19,12 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/hw-ledger-transports": "8.0.2",
|
||||
"@polkadot/util": "8.0.2",
|
||||
"@zondax/ledger-substrate": "^0.18.0"
|
||||
"@babel/runtime": "^7.16.5",
|
||||
"@polkadot/hw-ledger-transports": "8.2.2",
|
||||
"@polkadot/util": "8.2.2",
|
||||
"@zondax/ledger-substrate": "^0.23.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { knownLedger } from '@polkadot/networks/defaults';
|
||||
|
||||
import { ledgerApps } from './defaults';
|
||||
|
||||
describe('defaults', (): void => {
|
||||
it('has entries for each of the apps (hwledger -> networks)', (): void => {
|
||||
expect(
|
||||
Object.keys(knownLedger).filter((network) =>
|
||||
!ledgerApps[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('has entries for each of the apps (networks -> hwledger)', (): void => {
|
||||
expect(
|
||||
Object.keys(ledgerApps).filter((network) =>
|
||||
!knownLedger[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -4,10 +4,11 @@
|
||||
import type Transport from '@ledgerhq/hw-transport';
|
||||
import type { SubstrateApp } from '@zondax/ledger-substrate';
|
||||
|
||||
import { newCentrifugeApp, newDockApp, newEdgewareApp, newEquilibriumApp, newGenshiroApp, newKusamaApp, newNodleApp, newPolkadotApp, newPolymeshApp, newStatemineApp } from '@zondax/ledger-substrate';
|
||||
import { newBifrostApp, newCentrifugeApp, newDockApp, newEdgewareApp, newEquilibriumApp, newGenshiroApp, newKusamaApp, newNodleApp, newPolkadotApp, newPolymeshApp, newSoraApp, newStatemineApp } from '@zondax/ledger-substrate';
|
||||
|
||||
// These match up with the keys of the knownLedger object in the @polkadot/networks/defaults.ts
|
||||
export const ledgerApps: Record<string, (transport: Transport) => SubstrateApp> = {
|
||||
bifrost: newBifrostApp,
|
||||
centrifuge: newCentrifugeApp,
|
||||
'dock-mainnet': newDockApp,
|
||||
edgeware: newEdgewareApp,
|
||||
@@ -17,5 +18,6 @@ export const ledgerApps: Record<string, (transport: Transport) => SubstrateApp>
|
||||
'nodle-chain': newNodleApp,
|
||||
polkadot: newPolkadotApp,
|
||||
polymesh: newPolymeshApp,
|
||||
sora: newSoraApp,
|
||||
statemine: newStatemineApp
|
||||
};
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.0.2' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.2.2' };
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../hw-ledger-transports" },
|
||||
{ "path": "../util" }
|
||||
]
|
||||
}
|
||||
@@ -20,15 +20,15 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/util": "8.0.2",
|
||||
"@polkadot/util-crypto": "8.0.2"
|
||||
"@babel/runtime": "^7.16.5",
|
||||
"@polkadot/util": "8.2.2",
|
||||
"@polkadot/util-crypto": "8.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "8.0.2",
|
||||
"@polkadot/util-crypto": "8.0.2"
|
||||
"@polkadot/util": "8.2.2",
|
||||
"@polkadot/util-crypto": "8.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +369,14 @@ describe('keypair', (): void => {
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('creates with dev phrase with derivation path specified - addFromUri with type', (): void => {
|
||||
const keyringUntyped = new Keyring();
|
||||
|
||||
expect(
|
||||
keyringUntyped.addFromUri(PHRASE, {}, 'ethereum').address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('encodes a pair toJSON (and decodes)', (): void => {
|
||||
const pair = keyring.createFromUri(PHRASE);
|
||||
const json = pair.toJson('password');
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/keyring', version: '8.0.2' };
|
||||
export const packageInfo = { name: '@polkadot/keyring', version: '8.2.2' };
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import type { KeyringInstance, KeyringOptions } from './types';
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { assert, hexToU8a } from '@polkadot/util';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { Keyring } from './keyring';
|
||||
@@ -111,6 +111,15 @@ export const PAIRSETHEREUM: PairDef[] = [
|
||||
}
|
||||
];
|
||||
|
||||
function createMeta (name?: string, seed?: string) {
|
||||
assert(name || seed, 'Testing pair should have either a name or a seed');
|
||||
|
||||
return {
|
||||
isTesting: true,
|
||||
name: name || (seed && seed.replace('//', '_').toLowerCase())
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @name testKeyring
|
||||
* @summary Create an instance of Keyring pre-populated with locked test accounts
|
||||
@@ -119,17 +128,12 @@ export const PAIRSETHEREUM: PairDef[] = [
|
||||
*/
|
||||
export function createTestKeyring (options: KeyringOptions = {}, isDerived = true): KeyringInstance {
|
||||
const keyring = new Keyring(options);
|
||||
const pairs: PairDef[] = (options.type && options.type === 'ethereum') ? PAIRSETHEREUM : PAIRSSR25519;
|
||||
const pairs = (options.type && options.type === 'ethereum')
|
||||
? PAIRSETHEREUM
|
||||
: PAIRSSR25519;
|
||||
|
||||
for (const { name, publicKey, secretKey, seed, type } of pairs) {
|
||||
if (!name && !seed) {
|
||||
throw new Error('Testing pair should have either a name or a seed');
|
||||
}
|
||||
|
||||
const meta = {
|
||||
isTesting: true,
|
||||
name: name || (seed && seed.replace('//', '_').toLowerCase())
|
||||
};
|
||||
const meta = createMeta(name, seed);
|
||||
const pair = !isDerived && !name && seed
|
||||
? keyring.addFromUri(seed, meta, options.type)
|
||||
: keyring.addPair(
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../util" },
|
||||
{ "path": "../util-crypto" }
|
||||
]
|
||||
}
|
||||
@@ -17,13 +17,14 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3"
|
||||
"@babel/runtime": "^7.16.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util": "8.0.2",
|
||||
"@polkadot/x-fetch": "8.0.2"
|
||||
"@polkadot/hw-ledger": "8.2.2",
|
||||
"@polkadot/util": "8.2.2",
|
||||
"@polkadot/x-fetch": "8.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,46 @@
|
||||
// Copyright 2017-2021 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ledgerApps } from '@polkadot/hw-ledger/defaults';
|
||||
import { isHex } from '@polkadot/util';
|
||||
|
||||
import { knownGenesis, knownLedger } from './defaults';
|
||||
|
||||
describe('defaults', (): void => {
|
||||
it('has hex values for all genesis chains', (): void => {
|
||||
expect(
|
||||
Object.keys(knownGenesis).filter((network) =>
|
||||
!knownGenesis[network].length ||
|
||||
knownGenesis[network].some((g) => !isHex(g, 256))
|
||||
)
|
||||
).toEqual([]);
|
||||
describe('genesis', (): void => {
|
||||
it('has hex values for all genesis chains', (): void => {
|
||||
expect(
|
||||
Object.keys(knownGenesis).filter((network) =>
|
||||
!knownGenesis[network].length ||
|
||||
knownGenesis[network].some((g) => !isHex(g, 256))
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('has genesis for all Ledger chains', (): void => {
|
||||
expect(
|
||||
Object.keys(knownLedger).filter((network) =>
|
||||
!knownGenesis[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('has genesis for all Ledger chains', (): void => {
|
||||
expect(
|
||||
Object.keys(knownLedger).filter((network) =>
|
||||
!knownGenesis[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
describe('Ledger', (): void => {
|
||||
it('has entries for each of the apps (hwledger -> networks)', (): void => {
|
||||
expect(
|
||||
Object.keys(knownLedger).filter((network) =>
|
||||
!ledgerApps[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('has entries for each of the apps (networks -> hwledger)', (): void => {
|
||||
expect(
|
||||
Object.keys(ledgerApps).filter((network) =>
|
||||
!knownLedger[network]
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,6 +51,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
'nodle-chain': [
|
||||
'0xa3d114c2b8d0627c1aa9b134eafcf7d05ca561fdc19fb388bb9457f81809fb23'
|
||||
],
|
||||
picasso: [
|
||||
'0xe8e7f0f4c4f5a00720b4821dbfddefea7490bcf0b19009961cc46957984e2c1c'
|
||||
],
|
||||
plasm: [
|
||||
'0x3e86364d4b4894021cb2a0390bcf2feb5517d5292f2de2bb9404227e908b0b8b'
|
||||
],
|
||||
@@ -60,6 +63,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
polymesh: [
|
||||
'0x6fbd74e5e1d0a61d52ccfe9d4adaed16dd3a7caa37c6bc4d0c2fa12e8b2f4063'
|
||||
],
|
||||
sora: [
|
||||
'0x7e4e32d0feafd4f9c9414b0be86373f9a1efa904809b683453a9af6856d38ad5'
|
||||
],
|
||||
stafi: [
|
||||
'0x290a4149f09ea0e402c74c1c7e96ae4239588577fe78932f94f5404c68243d80'
|
||||
],
|
||||
@@ -76,6 +82,7 @@ export const knownIcon: KnownIcon = {
|
||||
centrifuge: 'polkadot',
|
||||
kusama: 'polkadot',
|
||||
polkadot: 'polkadot',
|
||||
sora: 'polkadot',
|
||||
statemine: 'polkadot',
|
||||
statemint: 'polkadot',
|
||||
westmint: 'polkadot'
|
||||
@@ -85,6 +92,7 @@ export const knownIcon: KnownIcon = {
|
||||
// and maps to the known slip44 (minus the `0x8` hard derivation flag)
|
||||
// NOTE: Any network here needs to have a genesisHash attached in the config above
|
||||
export const knownLedger: KnownLedger = {
|
||||
bifrost: 0x00000314,
|
||||
centrifuge: 0x000002eb,
|
||||
'dock-mainnet': 0x00000252,
|
||||
edgeware: 0x0000020b,
|
||||
@@ -94,6 +102,7 @@ export const knownLedger: KnownLedger = {
|
||||
'nodle-chain': 0x000003eb,
|
||||
polkadot: 0x00000162,
|
||||
polymesh: 0x00000253,
|
||||
sora: 0x00000269,
|
||||
statemine: 0x000001b2 // common-good on Kusama, shares derivation
|
||||
};
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/networks', version: '8.0.2' };
|
||||
export const packageInfo = { name: '@polkadot/networks', version: '8.2.2' };
|
||||
|
||||
@@ -504,12 +504,21 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
website: 'https://crust.network'
|
||||
},
|
||||
{
|
||||
decimals: [0, 9, 9, 9, 9, 9, 9, 9],
|
||||
displayName: 'Equilibrium Network',
|
||||
network: 'equilibrium',
|
||||
decimals: [9, 9, 9],
|
||||
displayName: 'Genshiro Network',
|
||||
network: 'genshiro',
|
||||
prefix: 67,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['Unknown', 'USD', 'EQ', 'ETH', 'BTC', 'EOS', 'DOT', 'CRV'],
|
||||
symbols: ['GENS', 'EQD', 'LPT0'],
|
||||
website: 'https://genshiro.equilibrium.io'
|
||||
},
|
||||
{
|
||||
decimals: [9],
|
||||
displayName: 'Equilibrium Network',
|
||||
network: 'equilibrium',
|
||||
prefix: 68,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['EQ'],
|
||||
website: 'https://equilibrium.io'
|
||||
},
|
||||
{
|
||||
@@ -531,12 +540,12 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
website: 'https://zeitgeist.pm'
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
decimals: [18],
|
||||
displayName: 'Manta network',
|
||||
network: 'manta',
|
||||
prefix: 77,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['MA'],
|
||||
symbols: ['MANTA'],
|
||||
website: 'https://manta.network'
|
||||
},
|
||||
{
|
||||
@@ -665,6 +674,15 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
symbols: ['NEER'],
|
||||
website: 'https://bit.country'
|
||||
},
|
||||
{
|
||||
decimals: [18],
|
||||
displayName: 'Efinity',
|
||||
network: 'efinity',
|
||||
prefix: 1110,
|
||||
standardAccount: 'Sr25519',
|
||||
symbols: ['EFI'],
|
||||
website: 'https://efinity.io/'
|
||||
},
|
||||
{
|
||||
decimals: [18],
|
||||
displayName: 'Moonbeam',
|
||||
@@ -692,6 +710,24 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
symbols: ['KAPEX'],
|
||||
website: 'https://totemaccounting.com'
|
||||
},
|
||||
{
|
||||
decimals: [10],
|
||||
displayName: 'Interlay',
|
||||
network: 'interlay',
|
||||
prefix: 2032,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['INTR'],
|
||||
website: 'https://interlay.io/'
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Kintsugi',
|
||||
network: 'kintsugi',
|
||||
prefix: 2092,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['KINT'],
|
||||
website: 'https://interlay.io/'
|
||||
},
|
||||
{
|
||||
decimals: [18],
|
||||
displayName: 'Subspace testnet',
|
||||
@@ -723,7 +759,7 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
decimals: [12],
|
||||
displayName: 'CESS Testnet',
|
||||
network: 'cess-testnet',
|
||||
prefix: 10042,
|
||||
prefix: 11330,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['TCESS'],
|
||||
website: 'https://cess.cloud'
|
||||
@@ -732,7 +768,7 @@ export const knownSubstrate: KnownSubstrate[] = [
|
||||
decimals: [12],
|
||||
displayName: 'CESS',
|
||||
network: 'cess',
|
||||
prefix: 10043,
|
||||
prefix: 11331,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['CESS'],
|
||||
website: 'https://cess.cloud'
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../hw-ledger" },
|
||||
{ "path": "../util" },
|
||||
{ "path": "../x-fetch" }
|
||||
]
|
||||
}
|
||||
@@ -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,29 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.2",
|
||||
"version": "8.2.2",
|
||||
"browser": {
|
||||
"crypto": false,
|
||||
"stream": false
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@noble/hashes": "0.4.1",
|
||||
"@polkadot/networks": "8.0.2",
|
||||
"@polkadot/util": "8.0.2",
|
||||
"@polkadot/wasm-crypto": "^4.4.1",
|
||||
"@polkadot/x-randomvalues": "8.0.2",
|
||||
"bn.js": "^4.12.0",
|
||||
"@babel/runtime": "^7.16.5",
|
||||
"@noble/hashes": "^0.4.5",
|
||||
"@noble/secp256k1": "^1.3.4",
|
||||
"@polkadot/networks": "8.2.2",
|
||||
"@polkadot/util": "8.2.2",
|
||||
"@polkadot/wasm-crypto": "^4.5.1",
|
||||
"@polkadot/x-bigint": "8.2.2",
|
||||
"@polkadot/x-randomvalues": "8.2.2",
|
||||
"ed2curve": "^0.3.0",
|
||||
"elliptic": "^6.5.4",
|
||||
"micro-base": "^0.9.0",
|
||||
"micro-base": "^0.10.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.2"
|
||||
"@polkadot/util": "8.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,15 @@
|
||||
|
||||
import { hexToU8a, stringToU8a, u8aToHex } from '@polkadot/util';
|
||||
|
||||
import { createTestPairs, TestKeyringMap } from '../../../keyring/src/testingPairs';
|
||||
import { ALICE_PUBLIC_SR } from './encode.spec';
|
||||
import { decodeAddress } from '.';
|
||||
|
||||
describe('decodeAddress', (): void => {
|
||||
let keyring: TestKeyringMap;
|
||||
|
||||
beforeAll((): void => {
|
||||
keyring = createTestPairs({ type: 'sr25519' });
|
||||
});
|
||||
|
||||
it('decodes an address', (): void => {
|
||||
expect(
|
||||
decodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')
|
||||
).toEqual(
|
||||
keyring.alice.publicKey
|
||||
ALICE_PUBLIC_SR
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
|
||||
import { encodeAddress } from '.';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
export const ALICE_PUBLIC_SR = new Uint8Array([212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125]);
|
||||
|
||||
export const ALICE_PUBLIC_ED = new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]);
|
||||
|
||||
const SUBKEY = [
|
||||
{
|
||||
@@ -69,7 +69,7 @@ const SUBKEY = [
|
||||
describe('encode', (): void => {
|
||||
it('encodes an address to a valid value', (): void => {
|
||||
expect(
|
||||
keyring.alice.address
|
||||
encodeAddress(ALICE_PUBLIC_ED)
|
||||
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ describe('encode', (): void => {
|
||||
it('fails when non-valid publicKey provided', (): void => {
|
||||
expect(
|
||||
(): string => encodeAddress(
|
||||
keyring.alice.publicKey.slice(0, 30)
|
||||
ALICE_PUBLIC_ED.slice(0, 30)
|
||||
)
|
||||
).toThrow(/Expected a valid key/);
|
||||
});
|
||||
@@ -141,7 +141,7 @@ describe('encode', (): void => {
|
||||
|
||||
it('encodes with 2 byte prefix', (): void => {
|
||||
expect(
|
||||
encodeAddress(keyring.alice.publicKey, 255)
|
||||
encodeAddress(ALICE_PUBLIC_ED, 255)
|
||||
).toEqual('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k');
|
||||
});
|
||||
|
||||
@@ -155,19 +155,19 @@ describe('encode', (): void => {
|
||||
|
||||
it('does not encode for > 16,383, < 0', (): void => {
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, -1)
|
||||
() => encodeAddress(ALICE_PUBLIC_ED, -1)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 16384)
|
||||
() => encodeAddress(ALICE_PUBLIC_ED, 16384)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
});
|
||||
|
||||
it('does not encode reserved', (): void => {
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 46)
|
||||
() => encodeAddress(ALICE_PUBLIC_ED, 46)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 47)
|
||||
() => encodeAddress(ALICE_PUBLIC_ED, 47)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
|
||||
import { ALICE_PUBLIC_SR } from './encode.spec';
|
||||
import { addressEq } from '.';
|
||||
|
||||
describe('addressEq', (): void => {
|
||||
@@ -34,12 +33,10 @@ describe('addressEq', (): void => {
|
||||
});
|
||||
|
||||
it('returns true for equal, address vs publicKey', (): void => {
|
||||
const keyring = createTestPairs({ type: 'sr25519' });
|
||||
|
||||
expect(
|
||||
addressEq(
|
||||
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
|
||||
keyring.alice.publicKey
|
||||
ALICE_PUBLIC_SR
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { U8aLike } from '@polkadot/util/types';
|
||||
|
||||
import { assert, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
// NOTE: Work around a TS issue. These are re-defined like in @polkadot/util/types
|
||||
// since without it the import
|
||||
//
|
||||
// import type { U8aLike } from '@polkadot/util/types';
|
||||
//
|
||||
// ends up in the build-generated code as
|
||||
//
|
||||
// impor("../../../util/src/types").U8aLike
|
||||
//
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
type U8aLike = number[] | Buffer | Uint8Array | string | String;
|
||||
// re-export the type so *.d.ts files don't have ../src imports
|
||||
export type { U8aLike } from '@polkadot/util/types';
|
||||
|
||||
interface Coder {
|
||||
decode: (value: string) => Uint8Array;
|
||||
@@ -21,9 +14,9 @@ interface Coder {
|
||||
}
|
||||
|
||||
interface Config {
|
||||
alphabet: string;
|
||||
chars: string;
|
||||
coder: Coder;
|
||||
ipfsChar?: string;
|
||||
ipfs?: string;
|
||||
regex?: RegExp;
|
||||
type: string;
|
||||
}
|
||||
@@ -34,24 +27,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 +59,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;
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { blake2AsU8a } from '.';
|
||||
|
||||
describe('blake2AsU8a', (): void => {
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
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 { createAsHex } from '../helpers';
|
||||
import { blake2b as blake2bJs } from '../noble-hashes/lib/blake2b';
|
||||
|
||||
type BitLength = 64 | 128 | 256 | 384 | 512;
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '@polkadot/x-bigint/shim';
|
||||
|
||||
import { utils as utilsNobleSecp256k1 } from '@noble/secp256k1';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
|
||||
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)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { stringToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { ed25519PairFromSeed, ed25519Sign } from '.';
|
||||
|
||||
const PAIR = ed25519PairFromSeed(
|
||||
|
||||
@@ -3,7 +3,16 @@
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { hasBigInt, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
// re-export so TS *.d.ts generation is correct
|
||||
export type { HexString } from '@polkadot/util/types';
|
||||
|
||||
interface DualHash {
|
||||
256: (u8a: Uint8Array) => Uint8Array;
|
||||
512: (u8a: Uint8Array) => Uint8Array;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function createAsHex <T extends (...args: any[]) => Uint8Array> (fn: T): (...args: Parameters<T>) => HexString {
|
||||
@@ -15,3 +24,13 @@ export function createBitHasher <T> (bitLength: T, fn: (data: HexString | Buffer
|
||||
return (data: HexString | Buffer | Uint8Array | string, onlyJs?: boolean): Uint8Array =>
|
||||
fn(data, bitLength, onlyJs);
|
||||
}
|
||||
|
||||
export function createDualHasher (wa: DualHash, js: DualHash): (value: HexString | Buffer | Uint8Array | string, bitLength?: 256 | 512, onlyJs?: boolean) => Uint8Array {
|
||||
return (value: HexString | Buffer | Uint8Array | string, bitLength: 256 | 512 = 256, onlyJs?: boolean): Uint8Array => {
|
||||
const u8a = u8aToU8a(value);
|
||||
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? wa[bitLength](u8a)
|
||||
: js[bitLength](u8a);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { hexToU8a, stringToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { hmacShaAsU8a } from '.';
|
||||
|
||||
describe('hmacShaAsU8a', (): void => {
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
// 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 '../noble-hashes/lib/hmac';
|
||||
import { sha256 } from '../noble-hashes/lib/sha256';
|
||||
import { sha512 } from '../noble-hashes/lib/sha512';
|
||||
|
||||
type BitLength = 256 | 512;
|
||||
|
||||
const JS_HASH = {
|
||||
256: sha256,
|
||||
512: sha512
|
||||
};
|
||||
|
||||
const WA_MHAC = {
|
||||
256: hmacSha256,
|
||||
512: hmacSha512
|
||||
};
|
||||
|
||||
function createSha (bitLength: BitLength): (key: Uint8Array | string, data: Uint8Array, onlyJs?: boolean) => Uint8Array {
|
||||
return (key: Uint8Array | string, data: Uint8Array, onlyJs?: boolean): Uint8Array =>
|
||||
hmacShaAsU8a(key, data, bitLength, onlyJs);
|
||||
@@ -20,16 +30,11 @@ function createSha (bitLength: BitLength): (key: Uint8Array | string, data: Uint
|
||||
* @description creates a Hmac Sha (256/512) Uint8Array from the key & data
|
||||
*/
|
||||
export function hmacShaAsU8a (key: Uint8Array | string, data: Uint8Array, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
|
||||
const is256 = bitLength === 256;
|
||||
const u8aKey = u8aToU8a(key);
|
||||
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? is256
|
||||
? hmacSha256(u8aKey, data)
|
||||
: hmacSha512(u8aKey, data)
|
||||
: is256
|
||||
? hmac(sha256, u8aKey, data)
|
||||
: hmac(sha512, u8aKey, data);
|
||||
? WA_MHAC[bitLength](u8aKey, data)
|
||||
: hmac(JS_HASH[bitLength], u8aKey, data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { hexToU8a, stringToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { keccakAsU8a } from '.';
|
||||
|
||||
describe('keccakAsU8a', (): void => {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 { keccak256, keccak512 } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { createAsHex, createBitHasher } from '../helpers';
|
||||
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '../noble-hashes/lib/sha3';
|
||||
|
||||
type BitLength = 256 | 512;
|
||||
import { createAsHex, createBitHasher, createDualHasher } from '../helpers';
|
||||
|
||||
/**
|
||||
* @name keccakAsU8a
|
||||
@@ -25,18 +21,11 @@ type BitLength = 256 | 512;
|
||||
* keccakAsU8a('123'); // => Uint8Array
|
||||
* ```
|
||||
*/
|
||||
export function keccakAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
|
||||
const is256 = bitLength === 256;
|
||||
const u8a = u8aToU8a(value);
|
||||
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? is256
|
||||
? keccak256(u8a)
|
||||
: keccak512(u8a)
|
||||
: is256
|
||||
? keccak256Js(u8a)
|
||||
: keccak512Js(u8a);
|
||||
}
|
||||
export const keccakAsU8a = createDualHasher(
|
||||
{ 256: keccak256, 512: keccak512 },
|
||||
{ 256: keccak256Js, 512: keccak512Js }
|
||||
);
|
||||
|
||||
/**
|
||||
* @name keccak256AsU8a
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import DEFAULT_WORDLIST from './bip39-en';
|
||||
|
||||
describe('wordlist', (): void => {
|
||||
it('has the correct number of words', (): void => {
|
||||
expect(DEFAULT_WORDLIST).toHaveLength(2048);
|
||||
});
|
||||
|
||||
it('has no empty words', (): void => {
|
||||
expect(DEFAULT_WORDLIST.some((n) => !n.length || n.trim() !== n)).toEqual(false);
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,438 +0,0 @@
|
||||
# noble-hashes  [](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.
|
||||
@@ -1,128 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import { _n, Hash, createView, Input, toBytes } from './utils';
|
||||
|
||||
const _32n = _n(32);
|
||||
const _u32_max = _n(0xffffffff);
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import { _n } from './utils';
|
||||
|
||||
const U32_MASK64 = _n(2 ** 32 - 1);
|
||||
|
||||
const _32n = _n(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, Al] = [new Uint32Array(lst.length), 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;
|
||||
@@ -1,202 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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)
|
||||
);
|
||||
@@ -1,135 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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)
|
||||
);
|
||||
@@ -1,242 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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));
|
||||
@@ -1,62 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
// 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);
|
||||
@@ -1,78 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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;
|
||||
@@ -1,5 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
throw new Error(
|
||||
'noble-hashes have no entry-point. Please consult the README.md to learn how to use them'
|
||||
);
|
||||
@@ -1,94 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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);
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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, idxR] = [[Id], [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());
|
||||
@@ -1,220 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
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, tail] = [oi + 0, 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);
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import { SHA2 } from './_sha2';
|
||||
import { rotr, wrapConstructor } from './utils';
|
||||
|
||||
// Choice: a ? b : c
|
||||
const Chi = (a: number, b: number, c: number) => (a & b) ^ (~a & c);
|
||||
// Majority function, true if any two inpust is true
|
||||
const Maj = (a: number, b: number, c: number) => (a & b) ^ (a & c) ^ (b & c);
|
||||
|
||||
// Round constants:
|
||||
// first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
|
||||
// prettier-ignore
|
||||
const SHA256_K = new Uint32Array([
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
]);
|
||||
|
||||
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
|
||||
// prettier-ignore
|
||||
const IV = new Uint32Array([
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
]);
|
||||
|
||||
// Temporary buffer, not used to store anything between runs
|
||||
// Named this way because it matches specification.
|
||||
const SHA256_W = new Uint32Array(64);
|
||||
class SHA256 extends SHA2<SHA256> {
|
||||
// We cannot use array here since array allows indexing by variable
|
||||
// which means optimizer/compiler cannot use registers.
|
||||
private A = IV[0] | 0;
|
||||
private B = IV[1] | 0;
|
||||
private C = IV[2] | 0;
|
||||
private D = IV[3] | 0;
|
||||
private E = IV[4] | 0;
|
||||
private F = IV[5] | 0;
|
||||
private G = IV[6] | 0;
|
||||
private H = IV[7] | 0;
|
||||
|
||||
constructor() {
|
||||
super(64, 32, 8, false);
|
||||
}
|
||||
protected get(): [number, number, number, number, number, number, number, number] {
|
||||
const { A, B, C, D, E, F, G, H } = this;
|
||||
return [A, B, C, D, E, F, G, H];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number
|
||||
) {
|
||||
this.A = A | 0;
|
||||
this.B = B | 0;
|
||||
this.C = C | 0;
|
||||
this.D = D | 0;
|
||||
this.E = E | 0;
|
||||
this.F = F | 0;
|
||||
this.G = G | 0;
|
||||
this.H = H | 0;
|
||||
}
|
||||
protected process(view: DataView, offset: number): void {
|
||||
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
|
||||
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
|
||||
for (let i = 16; i < 64; i++) {
|
||||
const W15 = SHA256_W[i - 15];
|
||||
const W2 = SHA256_W[i - 2];
|
||||
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
|
||||
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
|
||||
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
|
||||
}
|
||||
// Compression function main loop, 64 rounds
|
||||
let { A, B, C, D, E, F, G, H } = this;
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
||||
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
|
||||
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
||||
const T2 = (sigma0 + Maj(A, B, C)) | 0;
|
||||
H = G;
|
||||
G = F;
|
||||
F = E;
|
||||
E = (D + T1) | 0;
|
||||
D = C;
|
||||
C = B;
|
||||
B = A;
|
||||
A = (T1 + T2) | 0;
|
||||
}
|
||||
// Add the compressed chunk to the current hash value
|
||||
A = (A + this.A) | 0;
|
||||
B = (B + this.B) | 0;
|
||||
C = (C + this.C) | 0;
|
||||
D = (D + this.D) | 0;
|
||||
E = (E + this.E) | 0;
|
||||
F = (F + this.F) | 0;
|
||||
G = (G + this.G) | 0;
|
||||
H = (H + this.H) | 0;
|
||||
this.set(A, B, C, D, E, F, G, H);
|
||||
}
|
||||
protected roundClean() {
|
||||
SHA256_W.fill(0);
|
||||
}
|
||||
destroy() {
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
this.buffer.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
export const sha256 = wrapConstructor(() => new SHA256());
|
||||
@@ -1,401 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import { Input, toBytes, wrapConstructorWithOpts, assertNumber, u32, Hash, HashXOF } from './utils';
|
||||
import { Keccak, ShakeOpts } from './sha3';
|
||||
// cSHAKE && KMAC (NIST SP800-185)
|
||||
function leftEncode(n: number): Uint8Array {
|
||||
const res = [n & 0xff];
|
||||
n >>= 8;
|
||||
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
|
||||
res.unshift(res.length);
|
||||
return new Uint8Array(res);
|
||||
}
|
||||
|
||||
function rightEncode(n: number): Uint8Array {
|
||||
const res = [n & 0xff];
|
||||
n >>= 8;
|
||||
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
|
||||
res.push(res.length);
|
||||
return new Uint8Array(res);
|
||||
}
|
||||
|
||||
const toBytesOptional = (buf?: Input) => (buf !== undefined ? toBytes(buf) : new Uint8Array([]));
|
||||
// NOTE: second modulo is necessary since we don't need to add padding if current element takes whole block
|
||||
const getPadding = (len: number, block: number) => new Uint8Array((block - (len % block)) % block);
|
||||
export type cShakeOpts = ShakeOpts & { personalization?: Input; NISTfn?: Input };
|
||||
|
||||
// Personalization
|
||||
function cshakePers(hash: Keccak, opts: cShakeOpts = {}): Keccak {
|
||||
if (!opts || (!opts.personalization && !opts.NISTfn)) return hash;
|
||||
// Encode and pad inplace to avoid unneccesary memory copies/slices (so we don't need to zero them later)
|
||||
// bytepad(encode_string(N) || encode_string(S), 168)
|
||||
const blockLenBytes = leftEncode(hash.blockLen);
|
||||
const fn = toBytesOptional(opts.NISTfn);
|
||||
const fnLen = leftEncode(8 * fn.length); // length in bits
|
||||
const pers = toBytesOptional(opts.personalization);
|
||||
const persLen = leftEncode(8 * pers.length); // length in bits
|
||||
if (!fn.length && !pers.length) return hash;
|
||||
hash.suffix = 0x04;
|
||||
hash.update(blockLenBytes).update(fnLen).update(fn).update(persLen).update(pers);
|
||||
let totalLen = blockLenBytes.length + fnLen.length + fn.length + persLen.length + pers.length;
|
||||
hash.update(getPadding(totalLen, hash.blockLen));
|
||||
return hash;
|
||||
}
|
||||
|
||||
const gencShake = (suffix: number, blockLen: number, outputLen: number) =>
|
||||
wrapConstructorWithOpts<Keccak, cShakeOpts>((opts: cShakeOpts = {}) =>
|
||||
cshakePers(
|
||||
new Keccak(blockLen, suffix, opts.dkLen !== undefined ? opts.dkLen : outputLen, true),
|
||||
opts
|
||||
)
|
||||
);
|
||||
|
||||
export const cshake128 = gencShake(0x1f, 168, 128 / 8);
|
||||
export const cshake256 = gencShake(0x1f, 136, 256 / 8);
|
||||
|
||||
class KMAC extends Keccak implements HashXOF<KMAC> {
|
||||
constructor(
|
||||
blockLen: number,
|
||||
outputLen: number,
|
||||
enableXOF: boolean,
|
||||
key: Input,
|
||||
opts: cShakeOpts = {}
|
||||
) {
|
||||
super(blockLen, 0x1f, outputLen, enableXOF);
|
||||
cshakePers(this, { NISTfn: 'KMAC', personalization: opts.personalization });
|
||||
key = toBytes(key);
|
||||
// 1. newX = bytepad(encode_string(K), 168) || X || right_encode(L).
|
||||
const blockLenBytes = leftEncode(this.blockLen);
|
||||
const keyLen = leftEncode(8 * key.length);
|
||||
this.update(blockLenBytes).update(keyLen).update(key);
|
||||
const totalLen = blockLenBytes.length + keyLen.length + key.length;
|
||||
this.update(getPadding(totalLen, this.blockLen));
|
||||
}
|
||||
protected override finish() {
|
||||
if (!this.finished) this.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
|
||||
super.finish();
|
||||
}
|
||||
override _cloneInto(to?: KMAC): KMAC {
|
||||
// Create new instance without calling constructor since key already in state and we don't know it.
|
||||
// Force "to" to be instance of KMAC instead of Sha3.
|
||||
if (!to) {
|
||||
to = Object.create(Object.getPrototypeOf(this), {}) as KMAC;
|
||||
to.state = this.state.slice();
|
||||
to.blockLen = this.blockLen;
|
||||
to.state32 = u32(to.state);
|
||||
}
|
||||
return super._cloneInto(to) as KMAC;
|
||||
}
|
||||
override clone(): KMAC {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
|
||||
function genKmac(blockLen: number, outputLen: number, xof = false) {
|
||||
const kmac = (key: Input, message: Input, opts?: cShakeOpts): Uint8Array =>
|
||||
kmac.create(key, opts).update(message).digest();
|
||||
kmac.create = (key: Input, opts: cShakeOpts = {}) =>
|
||||
new KMAC(blockLen, opts.dkLen !== undefined ? opts.dkLen : outputLen, xof, key, opts);
|
||||
kmac.init = kmac.create;
|
||||
return kmac;
|
||||
}
|
||||
|
||||
export const kmac128 = genKmac(168, 128 / 8);
|
||||
export const kmac256 = genKmac(136, 256 / 8);
|
||||
export const kmac128xof = genKmac(168, 128 / 8, true);
|
||||
export const kmac256xof = genKmac(136, 256 / 8, true);
|
||||
|
||||
// TupleHash
|
||||
// Usage: tuple(['ab', 'cd']) != tuple(['a', 'bcd'])
|
||||
class TupleHash extends Keccak implements HashXOF<TupleHash> {
|
||||
constructor(blockLen: number, outputLen: number, enableXOF: boolean, opts: cShakeOpts = {}) {
|
||||
super(blockLen, 0x1f, outputLen, enableXOF);
|
||||
cshakePers(this, { NISTfn: 'TupleHash', personalization: opts.personalization });
|
||||
// Change update after cshake processed
|
||||
this.update = (data: Input) => {
|
||||
data = toBytes(data);
|
||||
super.update(leftEncode(data.length * 8));
|
||||
super.update(data);
|
||||
return this;
|
||||
};
|
||||
}
|
||||
protected override finish() {
|
||||
if (!this.finished) super.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
|
||||
super.finish();
|
||||
}
|
||||
override _cloneInto(to?: TupleHash): TupleHash {
|
||||
to ||= new TupleHash(this.blockLen, this.outputLen, this.enableXOF);
|
||||
return super._cloneInto(to) as TupleHash;
|
||||
}
|
||||
override clone(): TupleHash {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
|
||||
function genTuple(blockLen: number, outputLen: number, xof = false) {
|
||||
const tuple = (messages: Input[], opts?: cShakeOpts): Uint8Array => {
|
||||
const h = tuple.create(opts);
|
||||
for (const msg of messages) h.update(msg);
|
||||
return h.digest();
|
||||
};
|
||||
tuple.create = (opts: cShakeOpts = {}) =>
|
||||
new TupleHash(blockLen, opts.dkLen !== undefined ? opts.dkLen : outputLen, xof, opts);
|
||||
tuple.init = tuple.create;
|
||||
return tuple;
|
||||
}
|
||||
|
||||
export const tuplehash128 = genTuple(168, 128 / 8);
|
||||
export const tuplehash256 = genTuple(136, 256 / 8);
|
||||
export const tuplehash128xof = genTuple(168, 128 / 8, true);
|
||||
export const tuplehash256xof = genTuple(136, 256 / 8, true);
|
||||
|
||||
// ParallelHash (same as K12/M14, but without speedup for inputs less 8kb, reduced number of rounds and more simple)
|
||||
type ParallelOpts = cShakeOpts & { blockLen?: number };
|
||||
|
||||
class ParallelHash extends Keccak implements HashXOF<ParallelHash> {
|
||||
private leafHash?: Hash<Keccak>;
|
||||
private chunkPos = 0; // Position of current block in chunk
|
||||
private chunksDone = 0; // How many chunks we already have
|
||||
private chunkLen: number;
|
||||
constructor(
|
||||
blockLen: number,
|
||||
outputLen: number,
|
||||
protected leafCons: () => Hash<Keccak>,
|
||||
enableXOF: boolean,
|
||||
opts: ParallelOpts = {}
|
||||
) {
|
||||
super(blockLen, 0x1f, outputLen, enableXOF);
|
||||
cshakePers(this, { NISTfn: 'ParallelHash', personalization: opts.personalization });
|
||||
let { blockLen: B } = opts;
|
||||
B ||= 8;
|
||||
assertNumber(B);
|
||||
this.chunkLen = B;
|
||||
super.update(leftEncode(B));
|
||||
// Change update after cshake processed
|
||||
this.update = (data: Input) => {
|
||||
data = toBytes(data);
|
||||
const { chunkLen, leafCons } = this;
|
||||
for (let pos = 0, len = data.length; pos < len; ) {
|
||||
if (this.chunkPos == chunkLen || !this.leafHash) {
|
||||
if (this.leafHash) {
|
||||
super.update(this.leafHash.digest());
|
||||
this.chunksDone++;
|
||||
}
|
||||
this.leafHash = leafCons();
|
||||
this.chunkPos = 0;
|
||||
}
|
||||
const take = Math.min(chunkLen - this.chunkPos, len - pos);
|
||||
this.leafHash.update(data.subarray(pos, pos + take));
|
||||
this.chunkPos += take;
|
||||
pos += take;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
}
|
||||
protected override finish() {
|
||||
if (this.finished) return;
|
||||
if (this.leafHash) {
|
||||
super.update(this.leafHash.digest());
|
||||
this.chunksDone++;
|
||||
}
|
||||
super.update(rightEncode(this.chunksDone));
|
||||
super.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
|
||||
super.finish();
|
||||
}
|
||||
override _cloneInto(to?: ParallelHash): ParallelHash {
|
||||
to ||= new ParallelHash(this.blockLen, this.outputLen, this.leafCons, this.enableXOF);
|
||||
if (this.leafHash) to.leafHash = this.leafHash._cloneInto(to.leafHash as Keccak);
|
||||
to.chunkPos = this.chunkPos;
|
||||
to.chunkLen = this.chunkLen;
|
||||
to.chunksDone = this.chunksDone;
|
||||
return super._cloneInto(to) as ParallelHash;
|
||||
}
|
||||
override destroy() {
|
||||
super.destroy.call(this);
|
||||
if (this.leafHash) this.leafHash.destroy();
|
||||
}
|
||||
override clone(): ParallelHash {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
|
||||
function genParallel(
|
||||
blockLen: number,
|
||||
outputLen: number,
|
||||
leaf: ReturnType<typeof gencShake>,
|
||||
xof = false
|
||||
) {
|
||||
const parallel = (message: Input, opts?: ParallelOpts): Uint8Array =>
|
||||
parallel.create(opts).update(message).digest();
|
||||
parallel.create = (opts: ParallelOpts = {}) =>
|
||||
new ParallelHash(
|
||||
blockLen,
|
||||
opts.dkLen !== undefined ? opts.dkLen : outputLen,
|
||||
() => leaf.init({ dkLen: 2 * outputLen }),
|
||||
xof,
|
||||
opts
|
||||
);
|
||||
parallel.init = parallel.create;
|
||||
return parallel;
|
||||
}
|
||||
|
||||
export const parallelhash128 = genParallel(168, 128 / 8, cshake128);
|
||||
export const parallelhash256 = genParallel(136, 256 / 8, cshake256);
|
||||
export const parallelhash128xof = genParallel(168, 128 / 8, cshake128, true);
|
||||
export const parallelhash256xof = genParallel(136, 256 / 8, cshake256, true);
|
||||
|
||||
// Kangaroo
|
||||
// Same as NIST rightEncode, but returns [0] for zero string
|
||||
function rightEncodeK12(n: number): Uint8Array {
|
||||
const res = [];
|
||||
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
|
||||
res.push(res.length);
|
||||
return new Uint8Array(res);
|
||||
}
|
||||
|
||||
export type KangarooOpts = { dkLen?: number; personalization?: Input };
|
||||
const EMPTY = new Uint8Array([]);
|
||||
|
||||
class KangarooTwelve extends Keccak implements HashXOF<KangarooTwelve> {
|
||||
readonly chunkLen = 8192;
|
||||
private leafHash?: Keccak;
|
||||
private personalization: Uint8Array;
|
||||
private chunkPos = 0; // Position of current block in chunk
|
||||
private chunksDone = 0; // How many chunks we already have
|
||||
constructor(
|
||||
blockLen: number,
|
||||
protected leafLen: number,
|
||||
outputLen: number,
|
||||
rounds: number,
|
||||
opts: KangarooOpts
|
||||
) {
|
||||
super(blockLen, 0x07, outputLen, true, rounds);
|
||||
const { personalization } = opts;
|
||||
this.personalization = toBytesOptional(personalization);
|
||||
}
|
||||
override update(data: Input) {
|
||||
data = toBytes(data);
|
||||
const { chunkLen, blockLen, leafLen, rounds } = this;
|
||||
for (let pos = 0, len = data.length; pos < len; ) {
|
||||
if (this.chunkPos == chunkLen) {
|
||||
if (this.leafHash) super.update(this.leafHash.digest());
|
||||
else {
|
||||
this.suffix = 0x06; // Its safe to change suffix here since its used only in digest()
|
||||
super.update(new Uint8Array([3, 0, 0, 0, 0, 0, 0, 0]));
|
||||
}
|
||||
this.leafHash = new Keccak(blockLen, 0x0b, leafLen, false, rounds);
|
||||
this.chunksDone++;
|
||||
this.chunkPos = 0;
|
||||
}
|
||||
const take = Math.min(chunkLen - this.chunkPos, len - pos);
|
||||
const chunk = data.subarray(pos, pos + take);
|
||||
if (this.leafHash) this.leafHash.update(chunk);
|
||||
else super.update(chunk);
|
||||
this.chunkPos += take;
|
||||
pos += take;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
protected override finish() {
|
||||
if (this.finished) return;
|
||||
const { personalization } = this;
|
||||
this.update(personalization).update(rightEncodeK12(personalization.length));
|
||||
// Leaf hash
|
||||
if (this.leafHash) {
|
||||
super.update(this.leafHash.digest());
|
||||
super.update(rightEncodeK12(this.chunksDone));
|
||||
super.update(new Uint8Array([0xff, 0xff]));
|
||||
}
|
||||
super.finish.call(this);
|
||||
}
|
||||
override destroy() {
|
||||
super.destroy.call(this);
|
||||
if (this.leafHash) this.leafHash.destroy();
|
||||
// We cannot zero personalization buffer since it is user provided and we don't want to mutate user input
|
||||
this.personalization = EMPTY;
|
||||
}
|
||||
override _cloneInto(to?: KangarooTwelve): KangarooTwelve {
|
||||
const { blockLen, leafLen, leafHash, outputLen, rounds } = this;
|
||||
to ||= new KangarooTwelve(blockLen, leafLen, outputLen, rounds, {});
|
||||
super._cloneInto(to);
|
||||
if (leafHash) to.leafHash = leafHash._cloneInto(to.leafHash);
|
||||
to.personalization.set(this.personalization);
|
||||
to.leafLen = this.leafLen;
|
||||
to.chunkPos = this.chunkPos;
|
||||
to.chunksDone = this.chunksDone;
|
||||
return to;
|
||||
}
|
||||
override clone(): KangarooTwelve {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
// Default to 32 bytes, so it can be used without opts
|
||||
export const k12 = wrapConstructorWithOpts<KangarooTwelve, KangarooOpts>(
|
||||
(opts: KangarooOpts = {}) =>
|
||||
new KangarooTwelve(168, 32, opts.dkLen !== undefined ? opts.dkLen : 32, 12, opts)
|
||||
);
|
||||
// MarsupilamiFourteen
|
||||
export const m14 = wrapConstructorWithOpts<KangarooTwelve, KangarooOpts>(
|
||||
(opts: KangarooOpts = {}) =>
|
||||
new KangarooTwelve(136, 64, opts.dkLen !== undefined ? opts.dkLen : 64, 14, opts)
|
||||
);
|
||||
|
||||
// https://keccak.team/files/CSF-0.1.pdf
|
||||
// + https://github.com/XKCP/XKCP/tree/master/lib/high/Keccak/PRG
|
||||
class KeccakPRG extends Keccak {
|
||||
protected rate: number;
|
||||
constructor(capacity: number) {
|
||||
assertNumber(capacity);
|
||||
// Rho should be full bytes
|
||||
if (capacity < 0 || capacity > 1600 - 10 || (1600 - capacity - 2) % 8)
|
||||
throw new Error('KeccakPRG: Invalid capacity');
|
||||
// blockLen = rho in bytes
|
||||
super((1600 - capacity - 2) / 8, 0, 0, true);
|
||||
this.rate = 1600 - capacity;
|
||||
this.posOut = Math.floor((this.rate + 7) / 8);
|
||||
}
|
||||
override keccak() {
|
||||
// Duplex padding
|
||||
this.state[this.pos] ^= 0x01;
|
||||
this.state[this.blockLen] ^= 0x02; // Rho is full bytes
|
||||
super.keccak();
|
||||
this.pos = 0;
|
||||
this.posOut = 0;
|
||||
}
|
||||
override update(data: Input) {
|
||||
super.update(data);
|
||||
this.posOut = this.blockLen;
|
||||
return this;
|
||||
}
|
||||
feed(data: Input) {
|
||||
return this.update(data);
|
||||
}
|
||||
protected override finish() {}
|
||||
override digestInto(out: Uint8Array): Uint8Array {
|
||||
throw new Error('KeccakPRG: digest is not allowed, please use .fetch instead.');
|
||||
}
|
||||
fetch(bytes: number): Uint8Array {
|
||||
return this.xof(bytes);
|
||||
}
|
||||
// Ensure irreversibility (even if state leaked previous outputs cannot be computed)
|
||||
forget() {
|
||||
if (this.rate < 1600 / 2 + 1) throw new Error('KeccakPRG: rate too low to use forget');
|
||||
this.keccak();
|
||||
for (let i = 0; i < this.blockLen; i++) this.state[i] = 0;
|
||||
this.pos = this.blockLen;
|
||||
this.keccak();
|
||||
this.posOut = this.blockLen;
|
||||
}
|
||||
override _cloneInto(to?: KeccakPRG): KeccakPRG {
|
||||
const { rate } = this;
|
||||
to ||= new KeccakPRG(1600 - rate);
|
||||
super._cloneInto(to);
|
||||
to.rate = rate;
|
||||
return to;
|
||||
}
|
||||
override clone(): KeccakPRG {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
|
||||
export const keccakprg = (capacity = 254) => new KeccakPRG(capacity);
|
||||
@@ -1,218 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import * as u64 from './_u64';
|
||||
import {
|
||||
_n,
|
||||
Hash,
|
||||
u32,
|
||||
Input,
|
||||
toBytes,
|
||||
wrapConstructor,
|
||||
wrapConstructorWithOpts,
|
||||
assertNumber,
|
||||
HashXOF,
|
||||
} from './utils';
|
||||
|
||||
const _0n = _n(0);
|
||||
const _1n = _n(1);
|
||||
const _2n = _n(2);
|
||||
const _7n = _n(7);
|
||||
const _256n = _n(256);
|
||||
const _0x71n = _n(0x71);
|
||||
|
||||
// Various per round constants calculations
|
||||
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA]: [number[], number[], bigint[]] = [[], [], []];
|
||||
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;
|
||||
for (let j = 0; j < 7; j++) {
|
||||
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
|
||||
if (R & _2n) t ^= _1n << ((_1n << BigInt(j)) - _1n);
|
||||
}
|
||||
_SHA3_IOTA.push(t);
|
||||
}
|
||||
const [SHA3_IOTA_H, SHA3_IOTA_L] = u64.split(_SHA3_IOTA, true);
|
||||
|
||||
// Left rotation (without 0, 32, 64)
|
||||
const rotlH = (h: number, l: number, s: number) =>
|
||||
s > 32 ? u64.rotlBH(h, l, s) : u64.rotlSH(h, l, s);
|
||||
const rotlL = (h: number, l: number, s: number) =>
|
||||
s > 32 ? u64.rotlBL(h, l, s) : u64.rotlSL(h, l, s);
|
||||
|
||||
// Same as keccakf1600, but allows to skip some rounds
|
||||
export function keccakP(s: Uint32Array, rounds: number = 24) {
|
||||
const B = new Uint32Array(5 * 2);
|
||||
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
||||
for (let round = 24 - rounds; round < 24; round++) {
|
||||
// Theta θ
|
||||
for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
||||
for (let x = 0; x < 10; x += 2) {
|
||||
const idx1 = (x + 8) % 10;
|
||||
const idx0 = (x + 2) % 10;
|
||||
const B0 = B[idx0];
|
||||
const B1 = B[idx0 + 1];
|
||||
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
||||
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
s[x + y] ^= Th;
|
||||
s[x + y + 1] ^= Tl;
|
||||
}
|
||||
}
|
||||
// Rho (ρ) and Pi (π)
|
||||
let curH = s[2];
|
||||
let curL = s[3];
|
||||
for (let t = 0; t < 24; t++) {
|
||||
const shift = SHA3_ROTL[t];
|
||||
const Th = rotlH(curH, curL, shift);
|
||||
const Tl = rotlL(curH, curL, shift);
|
||||
const PI = SHA3_PI[t];
|
||||
curH = s[PI];
|
||||
curL = s[PI + 1];
|
||||
s[PI] = Th;
|
||||
s[PI + 1] = Tl;
|
||||
}
|
||||
// Chi (χ)
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
for (let x = 0; x < 10; x++) B[x] = s[y + x];
|
||||
for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
||||
}
|
||||
// Iota (ι)
|
||||
s[0] ^= SHA3_IOTA_H[round];
|
||||
s[1] ^= SHA3_IOTA_L[round];
|
||||
}
|
||||
B.fill(0);
|
||||
}
|
||||
|
||||
export class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
|
||||
protected state: Uint8Array;
|
||||
protected pos = 0;
|
||||
protected posOut = 0;
|
||||
protected finished = false;
|
||||
protected state32: Uint32Array;
|
||||
protected destroyed = false;
|
||||
// NOTE: we accept arguments in bytes instead of bits here.
|
||||
constructor(
|
||||
public blockLen: number,
|
||||
public suffix: number,
|
||||
public outputLen: number,
|
||||
protected enableXOF = false,
|
||||
protected rounds: number = 24
|
||||
) {
|
||||
super();
|
||||
// Can be passed from user as dkLen
|
||||
assertNumber(outputLen);
|
||||
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
|
||||
if (0 >= this.blockLen || this.blockLen >= 200)
|
||||
throw new Error('Sha3 supports only keccak-f1600 function');
|
||||
this.state = new Uint8Array(200);
|
||||
this.state32 = u32(this.state);
|
||||
}
|
||||
protected keccak() {
|
||||
keccakP(this.state32, this.rounds);
|
||||
this.posOut = 0;
|
||||
this.pos = 0;
|
||||
}
|
||||
update(data: Input) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
const { blockLen, state } = this;
|
||||
data = toBytes(data);
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len; ) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++];
|
||||
if (this.pos === blockLen) this.keccak();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
protected finish() {
|
||||
if (this.finished) return;
|
||||
this.finished = true;
|
||||
const { state, suffix, pos, blockLen } = this;
|
||||
// Do the padding
|
||||
state[pos] ^= suffix;
|
||||
if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak();
|
||||
state[blockLen - 1] ^= 0x80;
|
||||
this.keccak();
|
||||
}
|
||||
protected writeInto(out: Uint8Array): Uint8Array {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (!(out instanceof Uint8Array)) throw new Error('Keccak: invalid output buffer');
|
||||
this.finish();
|
||||
for (let pos = 0, len = out.length; pos < len; ) {
|
||||
if (this.posOut >= this.blockLen) this.keccak();
|
||||
const take = Math.min(this.blockLen - this.posOut, len - pos);
|
||||
out.set(this.state.subarray(this.posOut, this.posOut + take), pos);
|
||||
this.posOut += take;
|
||||
pos += take;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
xofInto(out: Uint8Array): Uint8Array {
|
||||
// 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: number): Uint8Array {
|
||||
assertNumber(bytes);
|
||||
return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
digestInto(out: Uint8Array) {
|
||||
if (out.length < this.outputLen) throw new Error('Keccak: invalid output buffer');
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
this.finish();
|
||||
this.writeInto(out);
|
||||
this.destroy();
|
||||
return out;
|
||||
}
|
||||
digest() {
|
||||
return this.digestInto(new Uint8Array(this.outputLen));
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.state.fill(0);
|
||||
}
|
||||
_cloneInto(to?: Keccak): Keccak {
|
||||
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
||||
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
|
||||
to.state32.set(this.state32);
|
||||
to.pos = this.pos;
|
||||
to.posOut = this.posOut;
|
||||
to.finished = this.finished;
|
||||
to.rounds = rounds;
|
||||
// Suffix can change in cSHAKE
|
||||
to.suffix = suffix;
|
||||
to.outputLen = outputLen;
|
||||
to.enableXOF = enableXOF;
|
||||
to.destroyed = this.destroyed;
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
const gen = (suffix: number, blockLen: number, outputLen: number) =>
|
||||
wrapConstructor(() => new Keccak(blockLen, suffix, outputLen));
|
||||
|
||||
export const sha3_224 = gen(0x06, 144, 224 / 8);
|
||||
export const sha3_256 = gen(0x06, 136, 256 / 8);
|
||||
export const sha3_384 = gen(0x06, 104, 384 / 8);
|
||||
export const sha3_512 = gen(0x06, 72, 512 / 8);
|
||||
export const keccak_224 = gen(0x01, 144, 224 / 8);
|
||||
export const keccak_256 = gen(0x01, 136, 256 / 8);
|
||||
export const keccak_384 = gen(0x01, 104, 384 / 8);
|
||||
export const keccak_512 = gen(0x01, 72, 512 / 8);
|
||||
|
||||
export type ShakeOpts = { dkLen?: number };
|
||||
|
||||
const genShake = (suffix: number, blockLen: number, outputLen: number) =>
|
||||
wrapConstructorWithOpts<Keccak, ShakeOpts>(
|
||||
(opts: ShakeOpts = {}) =>
|
||||
new Keccak(blockLen, suffix, opts.dkLen !== undefined ? opts.dkLen : outputLen, true)
|
||||
);
|
||||
|
||||
export const shake128 = genShake(0x1f, 168, 128 / 8);
|
||||
export const shake256 = genShake(0x1f, 136, 256 / 8);
|
||||
@@ -1,223 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
import { SHA2 } from './_sha2';
|
||||
import * as u64 from './_u64';
|
||||
import { _n, wrapConstructor } from './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([
|
||||
_n('0x428a2f98d728ae22'), _n('0x7137449123ef65cd'), _n('0xb5c0fbcfec4d3b2f'), _n('0xe9b5dba58189dbbc'),
|
||||
_n('0x3956c25bf348b538'), _n('0x59f111f1b605d019'), _n('0x923f82a4af194f9b'), _n('0xab1c5ed5da6d8118'),
|
||||
_n('0xd807aa98a3030242'), _n('0x12835b0145706fbe'), _n('0x243185be4ee4b28c'), _n('0x550c7dc3d5ffb4e2'),
|
||||
_n('0x72be5d74f27b896f'), _n('0x80deb1fe3b1696b1'), _n('0x9bdc06a725c71235'), _n('0xc19bf174cf692694'),
|
||||
_n('0xe49b69c19ef14ad2'), _n('0xefbe4786384f25e3'), _n('0x0fc19dc68b8cd5b5'), _n('0x240ca1cc77ac9c65'),
|
||||
_n('0x2de92c6f592b0275'), _n('0x4a7484aa6ea6e483'), _n('0x5cb0a9dcbd41fbd4'), _n('0x76f988da831153b5'),
|
||||
_n('0x983e5152ee66dfab'), _n('0xa831c66d2db43210'), _n('0xb00327c898fb213f'), _n('0xbf597fc7beef0ee4'),
|
||||
_n('0xc6e00bf33da88fc2'), _n('0xd5a79147930aa725'), _n('0x06ca6351e003826f'), _n('0x142929670a0e6e70'),
|
||||
_n('0x27b70a8546d22ffc'), _n('0x2e1b21385c26c926'), _n('0x4d2c6dfc5ac42aed'), _n('0x53380d139d95b3df'),
|
||||
_n('0x650a73548baf63de'), _n('0x766a0abb3c77b2a8'), _n('0x81c2c92e47edaee6'), _n('0x92722c851482353b'),
|
||||
_n('0xa2bfe8a14cf10364'), _n('0xa81a664bbc423001'), _n('0xc24b8b70d0f89791'), _n('0xc76c51a30654be30'),
|
||||
_n('0xd192e819d6ef5218'), _n('0xd69906245565a910'), _n('0xf40e35855771202a'), _n('0x106aa07032bbd1b8'),
|
||||
_n('0x19a4c116b8d2d0c8'), _n('0x1e376c085141ab53'), _n('0x2748774cdf8eeb99'), _n('0x34b0bcb5e19b48a8'),
|
||||
_n('0x391c0cb3c5c95a63'), _n('0x4ed8aa4ae3418acb'), _n('0x5b9cca4f7763e373'), _n('0x682e6ff3d6b2b8a3'),
|
||||
_n('0x748f82ee5defb2fc'), _n('0x78a5636f43172f60'), _n('0x84c87814a1f0ab72'), _n('0x8cc702081a6439ec'),
|
||||
_n('0x90befffa23631e28'), _n('0xa4506cebde82bde9'), _n('0xbef9a3f7b2c67915'), _n('0xc67178f2e372532b'),
|
||||
_n('0xca273eceea26619c'), _n('0xd186b8c721c0c207'), _n('0xeada7dd6cde0eb1e'), _n('0xf57d4f7fee6ed178'),
|
||||
_n('0x06f067aa72176fba'), _n('0x0a637dc5a2c898a6'), _n('0x113f9804bef90dae'), _n('0x1b710b35131c471b'),
|
||||
_n('0x28db77f523047d84'), _n('0x32caab7b40c72493'), _n('0x3c9ebe0a15c9bebc'), _n('0x431d67c49c100d4c'),
|
||||
_n('0x4cc5d4becb3e42b6'), _n('0x597f299cfc657e2a'), _n('0x5fcb6fab3ad6faec'), _n('0x6c44198c4a475817')
|
||||
]);
|
||||
|
||||
// Temporary buffer, not used to store anything between runs
|
||||
const SHA512_W_H = new Uint32Array(80);
|
||||
const SHA512_W_L = new Uint32Array(80);
|
||||
|
||||
export class SHA512 extends SHA2<SHA512> {
|
||||
// We cannot use array here since array allows indexing by variable which means optimizer/compiler cannot use registers.
|
||||
// Also looks cleaner and easier to verify with spec.
|
||||
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
|
||||
// h -- high 32 bits, l -- low 32 bits
|
||||
Ah = 0x6a09e667 | 0;
|
||||
Al = 0xf3bcc908 | 0;
|
||||
Bh = 0xbb67ae85 | 0;
|
||||
Bl = 0x84caa73b | 0;
|
||||
Ch = 0x3c6ef372 | 0;
|
||||
Cl = 0xfe94f82b | 0;
|
||||
Dh = 0xa54ff53a | 0;
|
||||
Dl = 0x5f1d36f1 | 0;
|
||||
Eh = 0x510e527f | 0;
|
||||
El = 0xade682d1 | 0;
|
||||
Fh = 0x9b05688c | 0;
|
||||
Fl = 0x2b3e6c1f | 0;
|
||||
Gh = 0x1f83d9ab | 0;
|
||||
Gl = 0xfb41bd6b | 0;
|
||||
Hh = 0x5be0cd19 | 0;
|
||||
Hl = 0x137e2179 | 0;
|
||||
|
||||
constructor() {
|
||||
super(128, 64, 16, false);
|
||||
}
|
||||
// prettier-ignore
|
||||
protected get(): [
|
||||
number, number, number, number, number, number, number, number,
|
||||
number, number, number, number, number, number, number, number
|
||||
] {
|
||||
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
||||
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,
|
||||
Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number
|
||||
) {
|
||||
this.Ah = Ah | 0;
|
||||
this.Al = Al | 0;
|
||||
this.Bh = Bh | 0;
|
||||
this.Bl = Bl | 0;
|
||||
this.Ch = Ch | 0;
|
||||
this.Cl = Cl | 0;
|
||||
this.Dh = Dh | 0;
|
||||
this.Dl = Dl | 0;
|
||||
this.Eh = Eh | 0;
|
||||
this.El = El | 0;
|
||||
this.Fh = Fh | 0;
|
||||
this.Fl = Fl | 0;
|
||||
this.Gh = Gh | 0;
|
||||
this.Gl = Gl | 0;
|
||||
this.Hh = Hh | 0;
|
||||
this.Hl = Hl | 0;
|
||||
}
|
||||
protected process(view: DataView, offset: number) {
|
||||
// Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array
|
||||
for (let i = 0; i < 16; i++, offset += 4) {
|
||||
SHA512_W_H[i] = view.getUint32(offset);
|
||||
SHA512_W_L[i] = view.getUint32((offset += 4));
|
||||
}
|
||||
for (let i = 16; i < 80; i++) {
|
||||
// s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)
|
||||
const W15h = SHA512_W_H[i - 15] | 0;
|
||||
const W15l = SHA512_W_L[i - 15] | 0;
|
||||
const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);
|
||||
const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);
|
||||
// s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)
|
||||
const W2h = SHA512_W_H[i - 2] | 0;
|
||||
const W2l = SHA512_W_L[i - 2] | 0;
|
||||
const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);
|
||||
const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);
|
||||
// SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];
|
||||
const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
|
||||
const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
|
||||
SHA512_W_H[i] = SUMh | 0;
|
||||
SHA512_W_L[i] = SUMl | 0;
|
||||
}
|
||||
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
||||
// Compression function main loop, 80 rounds
|
||||
for (let i = 0; i < 80; i++) {
|
||||
// S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)
|
||||
const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);
|
||||
const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);
|
||||
//const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
|
||||
const CHIh = (Eh & Fh) ^ (~Eh & Gh);
|
||||
const CHIl = (El & Fl) ^ (~El & Gl);
|
||||
// T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]
|
||||
// prettier-ignore
|
||||
const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
|
||||
const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
|
||||
const T1l = T1ll | 0;
|
||||
// S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)
|
||||
const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);
|
||||
const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);
|
||||
const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);
|
||||
const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);
|
||||
Hh = Gh | 0;
|
||||
Hl = Gl | 0;
|
||||
Gh = Fh | 0;
|
||||
Gl = Fl | 0;
|
||||
Fh = Eh | 0;
|
||||
Fl = El | 0;
|
||||
({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
|
||||
Dh = Ch | 0;
|
||||
Dl = Cl | 0;
|
||||
Ch = Bh | 0;
|
||||
Cl = Bl | 0;
|
||||
Bh = Ah | 0;
|
||||
Bl = Al | 0;
|
||||
const All = u64.add3L(T1l, sigma0l, MAJl);
|
||||
Ah = u64.add3H(All, T1h, sigma0h, MAJh);
|
||||
Al = All | 0;
|
||||
}
|
||||
// Add the compressed chunk to the current hash value
|
||||
({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
|
||||
({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
|
||||
({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
|
||||
({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
|
||||
({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
|
||||
({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
|
||||
({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
|
||||
({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
|
||||
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
|
||||
}
|
||||
protected roundClean() {
|
||||
SHA512_W_H.fill(0);
|
||||
SHA512_W_L.fill(0);
|
||||
}
|
||||
destroy() {
|
||||
this.buffer.fill(0);
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class SHA512_256 extends SHA512 {
|
||||
// h -- high 32 bits, l -- low 32 bits
|
||||
override Ah = 0x22312194 | 0;
|
||||
override Al = 0xfc2bf72c | 0;
|
||||
override Bh = 0x9f555fa3 | 0;
|
||||
override Bl = 0xc84c64c2 | 0;
|
||||
override Ch = 0x2393b86b | 0;
|
||||
override Cl = 0x6f53b151 | 0;
|
||||
override Dh = 0x96387719 | 0;
|
||||
override Dl = 0x5940eabd | 0;
|
||||
override Eh = 0x96283ee2 | 0;
|
||||
override El = 0xa88effe3 | 0;
|
||||
override Fh = 0xbe5e1e25 | 0;
|
||||
override Fl = 0x53863992 | 0;
|
||||
override Gh = 0x2b0199fc | 0;
|
||||
override Gl = 0x2c85b8aa | 0;
|
||||
override Hh = 0x0eb72ddc | 0;
|
||||
override Hl = 0x81c52ca2 | 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.outputLen = 32;
|
||||
}
|
||||
}
|
||||
|
||||
class SHA384 extends SHA512 {
|
||||
// h -- high 32 bits, l -- low 32 bits
|
||||
override Ah = 0xcbbb9d5d | 0;
|
||||
override Al = 0xc1059ed8 | 0;
|
||||
override Bh = 0x629a292a | 0;
|
||||
override Bl = 0x367cd507 | 0;
|
||||
override Ch = 0x9159015a | 0;
|
||||
override Cl = 0x3070dd17 | 0;
|
||||
override Dh = 0x152fecd8 | 0;
|
||||
override Dl = 0xf70e5939 | 0;
|
||||
override Eh = 0x67332667 | 0;
|
||||
override El = 0xffc00b31 | 0;
|
||||
override Fh = 0x8eb44a87 | 0;
|
||||
override Fl = 0x68581511 | 0;
|
||||
override Gh = 0xdb0c2e0d | 0;
|
||||
override Gl = 0x64f98fa7 | 0;
|
||||
override Hh = 0x47b5481d | 0;
|
||||
override Hl = 0xbefa4fa4 | 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.outputLen = 48;
|
||||
}
|
||||
}
|
||||
|
||||
export const sha512 = wrapConstructor(() => new SHA512());
|
||||
export const sha512_256 = wrapConstructor(() => new SHA512_256());
|
||||
export const sha384 = wrapConstructor(() => new SHA384());
|
||||
@@ -1,177 +0,0 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
// https://github.com/paulmillr/noble-hashes/pull/13
|
||||
|
||||
// prettier-ignore
|
||||
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
|
||||
Uint16Array | Int16Array | Uint32Array | Int32Array;
|
||||
|
||||
// helper to protect against older bundlers
|
||||
export const _n: (value: string | number | bigint | boolean) => bigint =
|
||||
typeof BigInt !== 'undefined' ? BigInt : () => Number.NaN as unknown as bigint;
|
||||
|
||||
// Cast array to different type
|
||||
export const u8 = (arr: TypedArray) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
export const u32 = (arr: TypedArray) =>
|
||||
new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
||||
|
||||
// Cast array to view
|
||||
export const createView = (arr: TypedArray) =>
|
||||
new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
|
||||
// The rotate right (circular right shift) operation for uint32
|
||||
export const rotr = (word: number, shift: number) => (word << (32 - shift)) | (word >>> shift);
|
||||
|
||||
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
|
||||
// There is almost no big endian hardware, but js typed arrays uses platform specific endianess.
|
||||
// So, just to be sure not to corrupt anything.
|
||||
if (!isLE) throw new Error('Non little-endian hardware is not supported');
|
||||
|
||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||
export function bytesToHex(uint8a: Uint8Array): string {
|
||||
// pre-caching chars could speed this up 6x.
|
||||
let hex = '';
|
||||
for (let i = 0; i < uint8a.length; i++) {
|
||||
hex += hexes[uint8a[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
// Currently avoid insertion of polyfills with packers (browserify/webpack/etc)
|
||||
// But setTimeout is pretty slow, maybe worth to investigate howto do minimal polyfill here
|
||||
export const nextTick: () => Promise<unknown> = (() => {
|
||||
const nodeRequire =
|
||||
typeof module !== 'undefined' &&
|
||||
typeof module.require === 'function' &&
|
||||
module.require.bind(module);
|
||||
try {
|
||||
if (nodeRequire) {
|
||||
const { setImmediate } = nodeRequire('timers');
|
||||
return () => new Promise((resolve) => setImmediate(resolve));
|
||||
}
|
||||
} catch (e) {}
|
||||
return () => new Promise((resolve) => setTimeout(resolve, 0));
|
||||
})();
|
||||
|
||||
// Returns control to thread each 'tick' ms to avoid blocking
|
||||
export async function asyncLoop(iters: number, tick: number, cb: (i: number) => void) {
|
||||
let ts = Date.now();
|
||||
for (let i = 0; i < iters; i++) {
|
||||
cb(i);
|
||||
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
|
||||
const diff = Date.now() - ts;
|
||||
if (diff >= 0 && diff < tick) continue;
|
||||
await nextTick();
|
||||
ts += diff;
|
||||
}
|
||||
}
|
||||
|
||||
export type Input = Uint8Array | string;
|
||||
export function toBytes(data: Input) {
|
||||
if (typeof data === 'string') data = new TextEncoder().encode(data);
|
||||
if (!(data instanceof Uint8Array))
|
||||
throw new TypeError(`Expected input type is Uint8Array (got ${typeof data})`);
|
||||
return data;
|
||||
}
|
||||
|
||||
export function assertNumber(n: number) {
|
||||
if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`);
|
||||
}
|
||||
|
||||
export function assertBool(b: boolean) {
|
||||
if (typeof b !== 'boolean') {
|
||||
throw new Error(`Expected boolean, not ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertHash(hash: CHash) {
|
||||
if (typeof hash !== 'function' || typeof hash.init !== 'function')
|
||||
throw new Error('Hash should be wrapped by utils.wrapConstructor');
|
||||
assertNumber(hash.outputLen);
|
||||
assertNumber(hash.blockLen);
|
||||
}
|
||||
|
||||
// For runtime check if class implements interface
|
||||
export abstract class Hash<T extends Hash<T>> {
|
||||
abstract blockLen: number; // Bytes per block
|
||||
abstract outputLen: number; // Bytes in output
|
||||
abstract update(buf: Input): this;
|
||||
// Writes digest into buf
|
||||
abstract digestInto(buf: Uint8Array): void;
|
||||
abstract digest(): Uint8Array;
|
||||
// Cleanup internal state. Not '.clean' because instance is not usable after that.
|
||||
// Clean usually resets instance to initial state, but it is not possible for keyed hashes if key is consumed into state.
|
||||
// NOTE: if digest is not consumed by user, user need manually call '.destroy' if zeroing is required
|
||||
abstract destroy(): void;
|
||||
// Unsafe because doesn't check if "to" is correct. Can be used as clone() if no opts passed.
|
||||
// Why cloneInto instead of clone? Mostly performance (same as _digestInto), but also has nice property: it reuses instance
|
||||
// which means all internal buffers is overwritten, which also causes overwrite buffer which used for digest (in some cases).
|
||||
// We don't provide any guarantees about cleanup (it is impossible to!), so should be enough for now.
|
||||
abstract _cloneInto(to?: T): T;
|
||||
// Safe version that clones internal state
|
||||
clone(): T {
|
||||
return this._cloneInto();
|
||||
}
|
||||
}
|
||||
|
||||
export type HashXOF<T extends Hash<T>> = Hash<T> & {
|
||||
// XOF: streaming API to read digest in chunks. Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.
|
||||
// NOTE: when hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot destroy state,
|
||||
// next call can require more bytes.
|
||||
xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream
|
||||
xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf
|
||||
};
|
||||
|
||||
// Check if object doens't have custom constructor (like Uint8Array/Array)
|
||||
const isPlainObject = (obj: any) =>
|
||||
Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
|
||||
|
||||
type EmptyObj = {};
|
||||
export function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(def: T1, _opts?: T2): T1 & T2 {
|
||||
if (_opts !== undefined && (typeof _opts !== 'object' || !isPlainObject(_opts)))
|
||||
throw new TypeError('Options should be object or undefined');
|
||||
const opts = Object.assign(def, _opts);
|
||||
return opts as T1 & T2;
|
||||
}
|
||||
|
||||
export type CHash = ReturnType<typeof wrapConstructor>;
|
||||
|
||||
export function wrapConstructor<T extends Hash<T>>(hashConstructor: () => Hash<T>) {
|
||||
const hashC = (message: Input): Uint8Array => hashConstructor().update(toBytes(message)).digest();
|
||||
const tmp = hashConstructor();
|
||||
hashC.outputLen = tmp.outputLen;
|
||||
hashC.blockLen = tmp.blockLen;
|
||||
hashC.create = () => hashConstructor();
|
||||
hashC.init = hashC.create;
|
||||
return hashC;
|
||||
}
|
||||
|
||||
export function wrapConstructorWithOpts<H extends Hash<H>, T extends Object>(
|
||||
hashCons: (opts?: T) => Hash<H>
|
||||
) {
|
||||
const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();
|
||||
const tmp = hashCons({} as T);
|
||||
hashC.outputLen = tmp.outputLen;
|
||||
hashC.blockLen = tmp.blockLen;
|
||||
hashC.create = (opts: T) => hashCons(opts);
|
||||
hashC.init = hashC.create;
|
||||
return hashC;
|
||||
}
|
||||
|
||||
export const crypto: { node?: any; web?: any } = (() => {
|
||||
const webCrypto = typeof self === 'object' && 'crypto' in self ? self.crypto : undefined;
|
||||
const nodeRequire = typeof module !== 'undefined' && typeof require === 'function';
|
||||
return {
|
||||
node: nodeRequire && !webCrypto ? require('crypto') : undefined,
|
||||
web: webCrypto,
|
||||
};
|
||||
})();
|
||||
|
||||
export function randomBytes(bytesLength = 32): Uint8Array {
|
||||
if (crypto.web) {
|
||||
return crypto.web.getRandomValues(new Uint8Array(bytesLength));
|
||||
} else if (crypto.node) {
|
||||
return new Uint8Array(crypto.node.randomBytes(bytesLength).buffer);
|
||||
} else {
|
||||
throw new Error("The environment doesn't have randomBytes function");
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.0.2' };
|
||||
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.2.2' };
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
import { u8aEq, u8aToHex } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { pbkdf2Encode } from '.';
|
||||
|
||||
const KNOWN_SALT = new Uint8Array([
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
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 '../noble-hashes/lib/pbkdf2';
|
||||
import { sha512 } from '../noble-hashes/lib/sha512';
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
|
||||
interface Result {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { DEFAULT_PARAMS } from './defaults';
|
||||
import { scryptEncode } from '.';
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
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 '../noble-hashes/lib/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')
|
||||
]]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import { Point } from '@noble/secp256k1';
|
||||
|
||||
import { secp256k1 } from './secp256k1';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1Compress as wasm } from '@polkadot/wasm-crypto';
|
||||
|
||||
export function secp256k1Compress (publicKey: Uint8Array): Uint8Array {
|
||||
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
||||
export function secp256k1Compress (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
|
||||
if (publicKey.length === 33) {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
return new Uint8Array(
|
||||
secp256k1
|
||||
.keyFromPublic(publicKey)
|
||||
.getPublic()
|
||||
.encodeCompressed()
|
||||
);
|
||||
assert(publicKey.length === 65, 'Invalid publicKey provided');
|
||||
|
||||
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')
|
||||
]]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { Point } from '@noble/secp256k1';
|
||||
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Expand as wasm } from '@polkadot/wasm-crypto';
|
||||
|
||||
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])
|
||||
);
|
||||
|
||||
@@ -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,8 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a, u8aToHex } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToMiniSecret } from '../../mnemonic';
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1PairFromSeed } from '..';
|
||||
|
||||
// mnemonic, secret, public, account_id
|
||||
@@ -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,29 @@
|
||||
|
||||
import type { Keypair } from '../../types';
|
||||
|
||||
import { assert, bnToU8a } from '@polkadot/util';
|
||||
import { getPublicKey } from '@noble/secp256k1';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../../bn';
|
||||
import { secp256k1 } from '../secp256k1';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1FromSeed } from '@polkadot/wasm-crypto';
|
||||
|
||||
/**
|
||||
* @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 { keccakAsU8a } from '../keccak';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
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)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
// 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 { recoverPublicKey, Signature } from '@noble/secp256k1';
|
||||
|
||||
import { assert, hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, secp256k1Recover as wasm } from '@polkadot/wasm-crypto';
|
||||
|
||||
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)
|
||||
);
|
||||
});
|
||||
@@ -4,25 +4,33 @@
|
||||
import type { Keypair } from '../types';
|
||||
import type { HashType } from './types';
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { Signature, signSync } from '@noble/secp256k1';
|
||||
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Sign as wasm } from '@polkadot/wasm-crypto';
|
||||
|
||||
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/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,21 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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, sha512 } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { createBitHasher } from '../helpers';
|
||||
import { sha256 as sha256Js } from '../noble-hashes/lib/sha256';
|
||||
import { sha512 as sha512Js } from '../noble-hashes/lib/sha512';
|
||||
|
||||
type BitLength = 256 | 512;
|
||||
import { createBitHasher, createDualHasher } from '../helpers';
|
||||
|
||||
/**
|
||||
* @name shaAsU8a
|
||||
* @summary Creates a sha Uint8Array from the input.
|
||||
*/
|
||||
export function shaAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
|
||||
const is256 = bitLength === 256;
|
||||
const u8a = u8aToU8a(value);
|
||||
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? is256
|
||||
? sha256(u8a)
|
||||
: sha512(u8a)
|
||||
: is256
|
||||
? sha256Js(u8a)
|
||||
: sha512Js(u8a);
|
||||
}
|
||||
export const shaAsU8a = createDualHasher(
|
||||
{ 256: sha256, 512: sha512 },
|
||||
{ 256: sha256Js, 512: sha512Js }
|
||||
);
|
||||
|
||||
/**
|
||||
* @name sha256AsU8a
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { sha256AsU8a } from '.';
|
||||
|
||||
const TESTS = [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { performanceWasm } from '../test/performance';
|
||||
import { sha512AsU8a } from '.';
|
||||
|
||||
describe('sha512AsU8a', (): void => {
|
||||
|
||||
+4
-4
@@ -2,16 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { arrayRange } from '@polkadot/util';
|
||||
import { randomAsU8a } from '@polkadot/util-crypto';
|
||||
import { performance, performanceCmp } from '@polkadot/util/test/performance';
|
||||
|
||||
import { performance, performanceCmp } from '../../util/test/performance';
|
||||
import { randomAsU8a } from '..';
|
||||
|
||||
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 };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user