Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
25196c3367 | ||
|
|
eeb68c7e0f |
+5
-1
@@ -19,4 +19,8 @@
|
||||
7.0.3
|
||||
7.1.2
|
||||
7.8.2
|
||||
7.9.2
|
||||
7.9.2
|
||||
8.0.2
|
||||
8.0.3
|
||||
8.0.4
|
||||
8.0.5
|
||||
@@ -3,3 +3,5 @@ exclude_patterns:
|
||||
- "**/*.spec.ts"
|
||||
- "packages/networks/src/substrate.ts"
|
||||
- "packages/util/src/is/utf8.ts"
|
||||
- "packages/x-noble-hashes/src/**.ts"
|
||||
- "packages/x-noble-secp256k1/src/**.ts"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
packages/x-noble-hashes/**/*.ts
|
||||
packages/x-noble-secp256k1/**/*.ts
|
||||
@@ -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:
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 8.1.1 Dec 5, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add Picasso genesisHash (Thanks to https://github.com/hussein-aitlahcen)
|
||||
|
||||
Changes:
|
||||
|
||||
- Allow for use of `secp256k1` from `@polkadot/wasm-crypto`
|
||||
- Adjust `isBigInt` to not do `instanceof` check
|
||||
- Ensure consequtive capitals in `stringCamelCase` are lowercased
|
||||
- Use `@polkadot/x-bigint` for `BigInt` constructor
|
||||
- Split `@noble/hashes` fork into `x-noble-hashes`
|
||||
- Add `@noble/secp256k1` fork into `x-noble-secp256k1`, replaces `elliptic`
|
||||
|
||||
|
||||
## 8.0.5 Dec 1, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Adjust sha3 expectation of `BigInt` availability
|
||||
|
||||
|
||||
## 8.0.4 Nov 30, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Adjust `stringCamelCase` with leading `_`
|
||||
|
||||
|
||||
## 8.0.3 Nov 30, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Remove `Buffer` usage in `x-randomvalues`
|
||||
- Adjust `stringCamelCase` for all-caps parts
|
||||
|
||||
|
||||
## 8.0.2 Nov 26, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Explicitly include BigInt patched versions
|
||||
|
||||
|
||||
## 8.0.1 Nov 26, 2021
|
||||
|
||||
**Important** In the 8.0 version the hashing utilities have been consolidated around an optimized and audited version from `@noble/hashes`. As in the past the WASM hashing will be used by default if the environment is initialized.
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ module.exports = Object.assign({}, config, {
|
||||
'@polkadot/util-(crypto)(.*)$': '<rootDir>/packages/util-$1/src/$2',
|
||||
// eslint-disable-next-line sort-keys
|
||||
'@polkadot/util(.*)$': '<rootDir>/packages/util/src/$1',
|
||||
'@polkadot/x-(bigint|global)(.*)$': '<rootDir>/packages/x-$1/src/$2',
|
||||
'@polkadot/x-(fetch|randomvalues|textdecoder|textencoder|ws)(.*)$': '<rootDir>/packages/x-$1/src/node',
|
||||
'@polkadot/x-global(.*)$': '<rootDir>/packages/x-global/src/$1',
|
||||
'@polkadot/x-rxjs(.*)$': '<rootDir>/packages/x-rxjs/src/$1'
|
||||
'@polkadot/x-noble-(hashes|secp256k1)(.*)$': '<rootDir>/packages/x-noble-$1/src/$2'
|
||||
}
|
||||
});
|
||||
|
||||
+5
-6
@@ -10,7 +10,7 @@
|
||||
"url": "https://github.com/polkadot-js/common.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -22,17 +22,16 @@
|
||||
"lint": "polkadot-dev-run-lint",
|
||||
"networks:crosscheck": "polkadot-dev-run-test packages/networks/src/crosscheck",
|
||||
"networks:sync": "yarn build && node packages/networks/scripts/fromSubstrate.mjs",
|
||||
"postinstall": "polkadot-dev-yarn-only && patch-package --error-on-fail --patch-dir packages/util-crypto/src/patches",
|
||||
"postinstall": "polkadot-dev-yarn-only",
|
||||
"test": "polkadot-dev-run-test --coverage --runInBand --testPathIgnorePatterns crosscheck --testPathIgnorePatterns toMiniSecretCmp",
|
||||
"test:mnemonicCmp": "polkadot-dev-run-test packages/util-crypto/src/mnemonic/toMiniSecretCmp.spec.ts",
|
||||
"test:one": "polkadot-dev-run-test --runInBand"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@polkadot/dev": "^0.63.33",
|
||||
"@polkadot/ts": "^0.4.15",
|
||||
"@types/jest": "^27.0.3",
|
||||
"patch-package": "^6.4.7"
|
||||
"@polkadot/dev": "^0.63.35",
|
||||
"@polkadot/ts": "^0.4.16",
|
||||
"@types/jest": "^27.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"typescript": "^4.5.2"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
"react-native": "react-native.js",
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.1.1' };
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/hw-ledger-transports": "8.0.1",
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/hw-ledger-transports": "8.1.1",
|
||||
"@polkadot/util": "8.1.1",
|
||||
"@zondax/ledger-substrate": "^0.18.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.1.1' };
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/util-crypto": "8.0.1"
|
||||
"@polkadot/util": "8.1.1",
|
||||
"@polkadot/util-crypto": "8.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/util-crypto": "8.0.1"
|
||||
"@polkadot/util": "8.1.1",
|
||||
"@polkadot/util-crypto": "8.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/keyring', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/keyring', version: '8.1.1' };
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/x-fetch": "8.0.1"
|
||||
"@polkadot/util": "8.1.1",
|
||||
"@polkadot/x-fetch": "8.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
'nodle-chain': [
|
||||
'0xa3d114c2b8d0627c1aa9b134eafcf7d05ca561fdc19fb388bb9457f81809fb23'
|
||||
],
|
||||
picasso: [
|
||||
'0xe8e7f0f4c4f5a00720b4821dbfddefea7490bcf0b19009961cc46957984e2c1c'
|
||||
],
|
||||
plasm: [
|
||||
'0x3e86364d4b4894021cb2a0390bcf2feb5517d5292f2de2bb9404227e908b0b8b'
|
||||
],
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/networks', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/networks', version: '8.1.1' };
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"bundledDependencies": [
|
||||
"@noble/hashes"
|
||||
],
|
||||
"contributors": [],
|
||||
"description": "A collection of useful crypto utilities for @polkadot",
|
||||
"engines": {
|
||||
@@ -25,27 +22,31 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"browser": {
|
||||
"crypto": false,
|
||||
"stream": false
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@noble/hashes": "0.4.1",
|
||||
"@polkadot/networks": "8.0.1",
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/wasm-crypto": "^4.4.1",
|
||||
"@polkadot/x-randomvalues": "8.0.1",
|
||||
"bn.js": "^4.12.0",
|
||||
"@noble/secp256k1": "^1.3.0",
|
||||
"@polkadot/networks": "8.1.1",
|
||||
"@polkadot/util": "8.1.1",
|
||||
"@polkadot/wasm-crypto": "^4.5.1",
|
||||
"@polkadot/x-bigint": "8.1.1",
|
||||
"@polkadot/x-noble-hashes": "8.1.1",
|
||||
"@polkadot/x-noble-secp256k1": "8.1.1",
|
||||
"@polkadot/x-randomvalues": "8.1.1",
|
||||
"ed2curve": "^0.3.0",
|
||||
"elliptic": "^6.5.4",
|
||||
"micro-base": "^0.9.0",
|
||||
"tweetnacl": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/ed2curve": "^0.2.2",
|
||||
"@types/elliptic": "^6.4.14"
|
||||
"@types/ed2curve": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "8.0.1"
|
||||
"@polkadot/util": "8.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import type { HexString } from '@polkadot/util/types';
|
||||
import type { HashType } from '../secp256k1/types';
|
||||
import type { Prefix } from './types';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { assert, u8aConcat } from '@polkadot/util';
|
||||
|
||||
import { secp256k1Hasher } from '../secp256k1/hasher';
|
||||
import { hasher } from '../secp256k1/hasher';
|
||||
import { encodeAddress } from './encode';
|
||||
|
||||
/**
|
||||
@@ -15,15 +15,9 @@ import { encodeAddress } from './encode';
|
||||
* @summary Converts an EVM address to its corresponding SS58 address.
|
||||
*/
|
||||
export function evmToAddress (evmAddress: HexString | string | Uint8Array, ss58Format?: Prefix, hashType: HashType = 'blake2'): string {
|
||||
const wrapError = (message: string) => `Converting ${evmAddress as string}: ${message}`;
|
||||
|
||||
const message = u8aConcat('evm:', evmAddress);
|
||||
|
||||
if (message.length !== 24) {
|
||||
throw new Error(wrapError('Invalid evm address length'));
|
||||
}
|
||||
assert(message.length === 24, () => `Converting ${evmAddress as string}: Invalid evm address length`);
|
||||
|
||||
const address = secp256k1Hasher(hashType, message);
|
||||
|
||||
return encodeAddress(address, ss58Format);
|
||||
return encodeAddress(hasher(hashType, message), ss58Format);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,9 +21,9 @@ interface Coder {
|
||||
}
|
||||
|
||||
interface Config {
|
||||
alphabet: string;
|
||||
chars: string;
|
||||
coder: Coder;
|
||||
ipfsChar?: string;
|
||||
ipfs?: string;
|
||||
regex?: RegExp;
|
||||
type: string;
|
||||
}
|
||||
@@ -34,24 +34,24 @@ type EncodeFn = (value: U8aLike, ipfsCompat?: boolean) => string;
|
||||
|
||||
type ValidateFn = (value?: unknown, ipfsCompat?: boolean) => value is string;
|
||||
|
||||
export function createDecode ({ coder, ipfsChar }: Config, validate: ValidateFn): DecodeFn {
|
||||
export function createDecode ({ coder, ipfs }: Config, validate: ValidateFn): DecodeFn {
|
||||
return (value: string, ipfsCompat?: boolean): Uint8Array => {
|
||||
validate(value, ipfsCompat);
|
||||
|
||||
return coder.decode(
|
||||
ipfsChar && ipfsCompat
|
||||
ipfs && ipfsCompat
|
||||
? value.substr(1)
|
||||
: value
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function createEncode ({ coder, ipfsChar }: Config): EncodeFn {
|
||||
export function createEncode ({ coder, ipfs }: Config): EncodeFn {
|
||||
return (value: U8aLike, ipfsCompat?: boolean): string => {
|
||||
const out = coder.encode(u8aToU8a(value));
|
||||
|
||||
return ipfsChar && ipfsCompat
|
||||
? `${ipfsChar}${out}`
|
||||
return ipfs && ipfsCompat
|
||||
? `${ipfs}${out}`
|
||||
: out;
|
||||
};
|
||||
}
|
||||
@@ -66,20 +66,24 @@ export function createIs (validate: ValidateFn): ValidateFn {
|
||||
};
|
||||
}
|
||||
|
||||
export function createValidate ({ alphabet, ipfsChar, regex, type }: Config): ValidateFn {
|
||||
export function createValidate ({ chars, ipfs, type }: Config): ValidateFn {
|
||||
return (value?: unknown, ipfsCompat?: boolean): value is string => {
|
||||
assert(value && typeof value === 'string', () => `Expected non-null, non-empty ${type} string input`);
|
||||
|
||||
if (ipfsChar && ipfsCompat) {
|
||||
assert(value[0] === ipfsChar, () => `Expected ipfs-compatible ${type} to start with '${ipfsChar}'`);
|
||||
if (ipfs && ipfsCompat) {
|
||||
assert(value[0] === ipfs, () => `Expected ipfs-compatible ${type} to start with '${ipfs}'`);
|
||||
}
|
||||
|
||||
if (regex) {
|
||||
assert(regex.test(value), `Invalid ${type} encoding`);
|
||||
} else {
|
||||
for (let i = (ipfsCompat ? 1 : 0); i < value.length; i++) {
|
||||
assert(alphabet.includes(value[i]), () => `Invalid ${type} character "${value[i]}" (0x${value.charCodeAt(i).toString(16)}) at index ${i}`);
|
||||
}
|
||||
for (let i = (ipfsCompat ? 1 : 0); i < value.length; i++) {
|
||||
assert(
|
||||
chars.includes(value[i]) || (
|
||||
value[i] === '=' && (
|
||||
(i === value.length - 1) ||
|
||||
!chars.includes(value[i + 1])
|
||||
)
|
||||
),
|
||||
() => `Invalid ${type} character "${value[i]}" (0x${value.charCodeAt(i).toString(16)}) at index ${i}`
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { blake2b as blake2bJs } from '@noble/hashes/lib/blake2b';
|
||||
|
||||
import { hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2b, isReady } from '@polkadot/wasm-crypto';
|
||||
import { blake2b as blake2bJs } from '@polkadot/x-noble-hashes/blake2b';
|
||||
|
||||
import { createAsHex } from '../helpers';
|
||||
|
||||
|
||||
@@ -1,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,18 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '@polkadot/x-bigint/shim';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { utils as utilsNobleSecp256k1 } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { cryptoWaitReady } from './crypto';
|
||||
import { hmacSha256AsU8a } from './hmac';
|
||||
|
||||
// Set overrides on the secp256k1 utils
|
||||
// - hmacShaSync - This needs to be set, unset by default
|
||||
utilsNobleSecp256k1.hmacSha256Sync = (key: Uint8Array, ...messages: Uint8Array[]) =>
|
||||
hmacSha256AsU8a(key, u8aConcat(...messages));
|
||||
|
||||
// start init process immediately
|
||||
cryptoWaitReady().catch((): void => {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import type { Keypair } from '../../types';
|
||||
|
||||
import nacl from 'tweetnacl';
|
||||
import { randomAsU8a } from '../../random';
|
||||
import { ed25519PairFromSeed } from './fromSeed';
|
||||
|
||||
/**
|
||||
* @name ed25519PairFromRandom
|
||||
@@ -20,5 +21,5 @@ import nacl from 'tweetnacl';
|
||||
* ```
|
||||
*/
|
||||
export function ed25519PairFromRandom (): Keypair {
|
||||
return nacl.sign.keyPair();
|
||||
return ed25519PairFromSeed(randomAsU8a());
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import { ed25519PairFromSeed } from './fromSeed';
|
||||
export function ed25519PairFromString (value: string): Keypair {
|
||||
return ed25519PairFromSeed(
|
||||
blake2AsU8a(
|
||||
stringToU8a(value),
|
||||
256
|
||||
stringToU8a(value)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hmac } from '@noble/hashes/lib/hmac';
|
||||
import { sha256 } from '@noble/hashes/lib/sha256';
|
||||
import { sha512 } from '@noble/hashes/lib/sha512';
|
||||
|
||||
import { hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { hmacSha256, hmacSha512, isReady } from '@polkadot/wasm-crypto';
|
||||
import { hmac } from '@polkadot/x-noble-hashes/hmac';
|
||||
import { sha256 } from '@polkadot/x-noble-hashes/sha256';
|
||||
import { sha512 } from '@polkadot/x-noble-hashes/sha512';
|
||||
|
||||
type BitLength = 256 | 512;
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '@noble/hashes/lib/sha3';
|
||||
|
||||
import { hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, keccak256, keccak512 } from '@polkadot/wasm-crypto';
|
||||
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '@polkadot/x-noble-hashes/sha3';
|
||||
|
||||
import { createAsHex, createBitHasher } from '../helpers';
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.1.1' };
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
diff --git a/node_modules/@noble/hashes/lib/_sha2.js b/node_modules/@noble/hashes/lib/_sha2.js
|
||||
index 3a7e7d7..0517727 100644
|
||||
--- a/node_modules/@noble/hashes/lib/_sha2.js
|
||||
+++ b/node_modules/@noble/hashes/lib/_sha2.js
|
||||
@@ -2,12 +2,14 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SHA2 = void 0;
|
||||
const utils_1 = require("./utils");
|
||||
+const _32n = (0, utils_1._n)(32);
|
||||
+const _u32_max = (0, utils_1._n)(0xffffffff);
|
||||
// Polyfill for Safari 14
|
||||
function setBigUint64(view, byteOffset, value, isLE) {
|
||||
if (typeof view.setBigUint64 === 'function')
|
||||
return view.setBigUint64(byteOffset, value, isLE);
|
||||
- const wh = Number((value >> 32n) & 0xffffffffn);
|
||||
- const wl = Number(value & 0xffffffffn);
|
||||
+ const wh = Number((value >> _32n) & _u32_max);
|
||||
+ const wl = Number(value & _u32_max);
|
||||
const [h, l] = isLE ? [4, 0] : [0, 4];
|
||||
view.setUint32(byteOffset + h, wh, isLE);
|
||||
view.setUint32(byteOffset + l, wl, isLE);
|
||||
diff --git a/node_modules/@noble/hashes/lib/_u64.js b/node_modules/@noble/hashes/lib/_u64.js
|
||||
index 396889a..f958bd5 100644
|
||||
--- a/node_modules/@noble/hashes/lib/_u64.js
|
||||
+++ b/node_modules/@noble/hashes/lib/_u64.js
|
||||
@@ -1,11 +1,13 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.add5H = exports.add5L = exports.add4H = exports.add4L = exports.add3H = exports.add3L = exports.add = exports.rotlBL = exports.rotlBH = exports.rotlSL = exports.rotlSH = exports.rotr32L = exports.rotr32H = exports.rotrBL = exports.rotrBH = exports.rotrSL = exports.rotrSH = exports.shrSL = exports.shrSH = exports.toBig = exports.split = exports.fromBig = void 0;
|
||||
-const U32_MASK64 = 2n ** 32n - 1n;
|
||||
+const utils_1 = require("./utils");
|
||||
+const U32_MASK64 = (0, utils_1._n)(2 ** 32 - 1);
|
||||
+const _32n = (0, utils_1._n)(32);
|
||||
function fromBig(n, le = false) {
|
||||
if (le)
|
||||
- return { h: Number(n & U32_MASK64), l: Number((n >> 32n) & U32_MASK64) };
|
||||
- return { h: Number((n >> 32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
||||
+ return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
|
||||
+ return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
||||
}
|
||||
exports.fromBig = fromBig;
|
||||
function split(lst, le = false) {
|
||||
@@ -17,7 +19,7 @@ function split(lst, le = false) {
|
||||
return [Ah, Al];
|
||||
}
|
||||
exports.split = split;
|
||||
-const toBig = (h, l) => (BigInt(h >>> 0) << 32n) | BigInt(l >>> 0);
|
||||
+const toBig = (h, l) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);
|
||||
exports.toBig = toBig;
|
||||
// for Shift in [0, 32)
|
||||
const shrSH = (h, l, s) => h >>> s;
|
||||
diff --git a/node_modules/@noble/hashes/lib/blake3.d.ts b/node_modules/@noble/hashes/lib/blake3.d.ts
|
||||
index 80f8856..3ab02fc 100644
|
||||
--- a/node_modules/@noble/hashes/lib/blake3.d.ts
|
||||
+++ b/node_modules/@noble/hashes/lib/blake3.d.ts
|
||||
@@ -27,8 +27,8 @@ declare class BLAKE3 extends blake2.BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {
|
||||
private b2CompressOut;
|
||||
protected finish(): void;
|
||||
private writeInto;
|
||||
- XOFInto(out: Uint8Array): Uint8Array;
|
||||
- XOF(bytes: number): Uint8Array;
|
||||
+ xofInto(out: Uint8Array): Uint8Array;
|
||||
+ xof(bytes: number): Uint8Array;
|
||||
digestInto(out: Uint8Array): Uint8Array;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
diff --git a/node_modules/@noble/hashes/lib/blake3.js b/node_modules/@noble/hashes/lib/blake3.js
|
||||
index bfc7a19..52b28f0 100644
|
||||
--- a/node_modules/@noble/hashes/lib/blake3.js
|
||||
+++ b/node_modules/@noble/hashes/lib/blake3.js
|
||||
@@ -229,14 +229,14 @@ class BLAKE3 extends blake2.BLAKE2 {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
- XOFInto(out) {
|
||||
+ xofInto(out) {
|
||||
if (!this.enableXOF)
|
||||
throw new Error('XOF impossible after digest call');
|
||||
return this.writeInto(out);
|
||||
}
|
||||
- XOF(bytes) {
|
||||
+ xof(bytes) {
|
||||
(0, utils_1.assertNumber)(bytes);
|
||||
- return this.XOFInto(new Uint8Array(bytes));
|
||||
+ return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
digestInto(out) {
|
||||
if (out.length < this.outputLen)
|
||||
diff --git a/node_modules/@noble/hashes/lib/sha3-addons.js b/node_modules/@noble/hashes/lib/sha3-addons.js
|
||||
index 51793e2..6a9eef7 100644
|
||||
--- a/node_modules/@noble/hashes/lib/sha3-addons.js
|
||||
+++ b/node_modules/@noble/hashes/lib/sha3-addons.js
|
||||
@@ -321,7 +321,7 @@ class KeccakPRG extends sha3_1.Keccak {
|
||||
throw new Error('KeccakPRG: digest is not allowed, please use .fetch instead.');
|
||||
}
|
||||
fetch(bytes) {
|
||||
- return this.XOF(bytes);
|
||||
+ return this.xof(bytes);
|
||||
}
|
||||
// Ensure irreversibility (even if state leaked previous outputs cannot be computed)
|
||||
forget() {
|
||||
diff --git a/node_modules/@noble/hashes/lib/sha3.d.ts b/node_modules/@noble/hashes/lib/sha3.d.ts
|
||||
index 6f0a062..1105727 100644
|
||||
--- a/node_modules/@noble/hashes/lib/sha3.d.ts
|
||||
+++ b/node_modules/@noble/hashes/lib/sha3.d.ts
|
||||
@@ -17,8 +17,8 @@ export declare class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
|
||||
update(data: Input): this;
|
||||
protected finish(): void;
|
||||
protected writeInto(out: Uint8Array): Uint8Array;
|
||||
- XOFInto(out: Uint8Array): Uint8Array;
|
||||
- XOF(bytes: number): Uint8Array;
|
||||
+ xofInto(out: Uint8Array): Uint8Array;
|
||||
+ xof(bytes: number): Uint8Array;
|
||||
digestInto(out: Uint8Array): Uint8Array;
|
||||
digest(): Uint8Array;
|
||||
destroy(): void;
|
||||
diff --git a/node_modules/@noble/hashes/lib/sha3.js b/node_modules/@noble/hashes/lib/sha3.js
|
||||
index 91adaf4..41a84b4 100644
|
||||
--- a/node_modules/@noble/hashes/lib/sha3.js
|
||||
+++ b/node_modules/@noble/hashes/lib/sha3.js
|
||||
@@ -22,20 +22,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.shake256 = exports.shake128 = exports.keccak_512 = exports.keccak_384 = exports.keccak_256 = exports.keccak_224 = exports.sha3_512 = exports.sha3_384 = exports.sha3_256 = exports.sha3_224 = exports.Keccak = exports.keccakP = void 0;
|
||||
const u64 = __importStar(require("./_u64"));
|
||||
const utils_1 = require("./utils");
|
||||
+const _0n = (0, utils_1._n)(0);
|
||||
+const _1n = (0, utils_1._n)(1);
|
||||
+const _2n = (0, utils_1._n)(2);
|
||||
+const _7n = (0, utils_1._n)(7);
|
||||
+const _256n = (0, utils_1._n)(256);
|
||||
+const _0x71n = (0, utils_1._n)(0x71);
|
||||
// Various per round constants calculations
|
||||
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA] = [[], [], []];
|
||||
-for (let round = 0, R = 1n, x = 1, y = 0; round < 24; round++) {
|
||||
+for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
||||
// Pi
|
||||
[x, y] = [y, (2 * x + 3 * y) % 5];
|
||||
SHA3_PI.push(2 * (5 * y + x));
|
||||
// Rotational
|
||||
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
|
||||
// Iota
|
||||
- let t = 0n;
|
||||
+ let t = _0n;
|
||||
for (let j = 0; j < 7; j++) {
|
||||
- R = ((R << 1n) ^ ((R >> 7n) * 0x71n)) % 256n;
|
||||
- if (R & 2n)
|
||||
- t ^= 1n << ((1n << BigInt(j)) - 1n);
|
||||
+ R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
|
||||
+ if (R & _2n)
|
||||
+ t ^= _1n << ((_1n << BigInt(j)) - _1n);
|
||||
}
|
||||
_SHA3_IOTA.push(t);
|
||||
}
|
||||
@@ -161,15 +167,15 @@ class Keccak extends utils_1.Hash {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
- XOFInto(out) {
|
||||
+ xofInto(out) {
|
||||
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
|
||||
if (!this.enableXOF)
|
||||
throw new Error('XOF is not possible for this instance');
|
||||
return this.writeInto(out);
|
||||
}
|
||||
- XOF(bytes) {
|
||||
+ xof(bytes) {
|
||||
(0, utils_1.assertNumber)(bytes);
|
||||
- return this.XOFInto(new Uint8Array(bytes));
|
||||
+ return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
digestInto(out) {
|
||||
if (out.length < this.outputLen)
|
||||
diff --git a/node_modules/@noble/hashes/lib/sha512.js b/node_modules/@noble/hashes/lib/sha512.js
|
||||
index 567bd80..0a733ab 100644
|
||||
--- a/node_modules/@noble/hashes/lib/sha512.js
|
||||
+++ b/node_modules/@noble/hashes/lib/sha512.js
|
||||
@@ -26,26 +26,26 @@ const utils_1 = require("./utils");
|
||||
// Round contants (first 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409):
|
||||
// prettier-ignore
|
||||
const [SHA512_Kh, SHA512_Kl] = u64.split([
|
||||
- 0x428a2f98d728ae22n, 0x7137449123ef65cdn, 0xb5c0fbcfec4d3b2fn, 0xe9b5dba58189dbbcn,
|
||||
- 0x3956c25bf348b538n, 0x59f111f1b605d019n, 0x923f82a4af194f9bn, 0xab1c5ed5da6d8118n,
|
||||
- 0xd807aa98a3030242n, 0x12835b0145706fben, 0x243185be4ee4b28cn, 0x550c7dc3d5ffb4e2n,
|
||||
- 0x72be5d74f27b896fn, 0x80deb1fe3b1696b1n, 0x9bdc06a725c71235n, 0xc19bf174cf692694n,
|
||||
- 0xe49b69c19ef14ad2n, 0xefbe4786384f25e3n, 0x0fc19dc68b8cd5b5n, 0x240ca1cc77ac9c65n,
|
||||
- 0x2de92c6f592b0275n, 0x4a7484aa6ea6e483n, 0x5cb0a9dcbd41fbd4n, 0x76f988da831153b5n,
|
||||
- 0x983e5152ee66dfabn, 0xa831c66d2db43210n, 0xb00327c898fb213fn, 0xbf597fc7beef0ee4n,
|
||||
- 0xc6e00bf33da88fc2n, 0xd5a79147930aa725n, 0x06ca6351e003826fn, 0x142929670a0e6e70n,
|
||||
- 0x27b70a8546d22ffcn, 0x2e1b21385c26c926n, 0x4d2c6dfc5ac42aedn, 0x53380d139d95b3dfn,
|
||||
- 0x650a73548baf63den, 0x766a0abb3c77b2a8n, 0x81c2c92e47edaee6n, 0x92722c851482353bn,
|
||||
- 0xa2bfe8a14cf10364n, 0xa81a664bbc423001n, 0xc24b8b70d0f89791n, 0xc76c51a30654be30n,
|
||||
- 0xd192e819d6ef5218n, 0xd69906245565a910n, 0xf40e35855771202an, 0x106aa07032bbd1b8n,
|
||||
- 0x19a4c116b8d2d0c8n, 0x1e376c085141ab53n, 0x2748774cdf8eeb99n, 0x34b0bcb5e19b48a8n,
|
||||
- 0x391c0cb3c5c95a63n, 0x4ed8aa4ae3418acbn, 0x5b9cca4f7763e373n, 0x682e6ff3d6b2b8a3n,
|
||||
- 0x748f82ee5defb2fcn, 0x78a5636f43172f60n, 0x84c87814a1f0ab72n, 0x8cc702081a6439ecn,
|
||||
- 0x90befffa23631e28n, 0xa4506cebde82bde9n, 0xbef9a3f7b2c67915n, 0xc67178f2e372532bn,
|
||||
- 0xca273eceea26619cn, 0xd186b8c721c0c207n, 0xeada7dd6cde0eb1en, 0xf57d4f7fee6ed178n,
|
||||
- 0x06f067aa72176fban, 0x0a637dc5a2c898a6n, 0x113f9804bef90daen, 0x1b710b35131c471bn,
|
||||
- 0x28db77f523047d84n, 0x32caab7b40c72493n, 0x3c9ebe0a15c9bebcn, 0x431d67c49c100d4cn,
|
||||
- 0x4cc5d4becb3e42b6n, 0x597f299cfc657e2an, 0x5fcb6fab3ad6faecn, 0x6c44198c4a475817n
|
||||
+ (0, utils_1._n)('0x428a2f98d728ae22'), (0, utils_1._n)('0x7137449123ef65cd'), (0, utils_1._n)('0xb5c0fbcfec4d3b2f'), (0, utils_1._n)('0xe9b5dba58189dbbc'),
|
||||
+ (0, utils_1._n)('0x3956c25bf348b538'), (0, utils_1._n)('0x59f111f1b605d019'), (0, utils_1._n)('0x923f82a4af194f9b'), (0, utils_1._n)('0xab1c5ed5da6d8118'),
|
||||
+ (0, utils_1._n)('0xd807aa98a3030242'), (0, utils_1._n)('0x12835b0145706fbe'), (0, utils_1._n)('0x243185be4ee4b28c'), (0, utils_1._n)('0x550c7dc3d5ffb4e2'),
|
||||
+ (0, utils_1._n)('0x72be5d74f27b896f'), (0, utils_1._n)('0x80deb1fe3b1696b1'), (0, utils_1._n)('0x9bdc06a725c71235'), (0, utils_1._n)('0xc19bf174cf692694'),
|
||||
+ (0, utils_1._n)('0xe49b69c19ef14ad2'), (0, utils_1._n)('0xefbe4786384f25e3'), (0, utils_1._n)('0x0fc19dc68b8cd5b5'), (0, utils_1._n)('0x240ca1cc77ac9c65'),
|
||||
+ (0, utils_1._n)('0x2de92c6f592b0275'), (0, utils_1._n)('0x4a7484aa6ea6e483'), (0, utils_1._n)('0x5cb0a9dcbd41fbd4'), (0, utils_1._n)('0x76f988da831153b5'),
|
||||
+ (0, utils_1._n)('0x983e5152ee66dfab'), (0, utils_1._n)('0xa831c66d2db43210'), (0, utils_1._n)('0xb00327c898fb213f'), (0, utils_1._n)('0xbf597fc7beef0ee4'),
|
||||
+ (0, utils_1._n)('0xc6e00bf33da88fc2'), (0, utils_1._n)('0xd5a79147930aa725'), (0, utils_1._n)('0x06ca6351e003826f'), (0, utils_1._n)('0x142929670a0e6e70'),
|
||||
+ (0, utils_1._n)('0x27b70a8546d22ffc'), (0, utils_1._n)('0x2e1b21385c26c926'), (0, utils_1._n)('0x4d2c6dfc5ac42aed'), (0, utils_1._n)('0x53380d139d95b3df'),
|
||||
+ (0, utils_1._n)('0x650a73548baf63de'), (0, utils_1._n)('0x766a0abb3c77b2a8'), (0, utils_1._n)('0x81c2c92e47edaee6'), (0, utils_1._n)('0x92722c851482353b'),
|
||||
+ (0, utils_1._n)('0xa2bfe8a14cf10364'), (0, utils_1._n)('0xa81a664bbc423001'), (0, utils_1._n)('0xc24b8b70d0f89791'), (0, utils_1._n)('0xc76c51a30654be30'),
|
||||
+ (0, utils_1._n)('0xd192e819d6ef5218'), (0, utils_1._n)('0xd69906245565a910'), (0, utils_1._n)('0xf40e35855771202a'), (0, utils_1._n)('0x106aa07032bbd1b8'),
|
||||
+ (0, utils_1._n)('0x19a4c116b8d2d0c8'), (0, utils_1._n)('0x1e376c085141ab53'), (0, utils_1._n)('0x2748774cdf8eeb99'), (0, utils_1._n)('0x34b0bcb5e19b48a8'),
|
||||
+ (0, utils_1._n)('0x391c0cb3c5c95a63'), (0, utils_1._n)('0x4ed8aa4ae3418acb'), (0, utils_1._n)('0x5b9cca4f7763e373'), (0, utils_1._n)('0x682e6ff3d6b2b8a3'),
|
||||
+ (0, utils_1._n)('0x748f82ee5defb2fc'), (0, utils_1._n)('0x78a5636f43172f60'), (0, utils_1._n)('0x84c87814a1f0ab72'), (0, utils_1._n)('0x8cc702081a6439ec'),
|
||||
+ (0, utils_1._n)('0x90befffa23631e28'), (0, utils_1._n)('0xa4506cebde82bde9'), (0, utils_1._n)('0xbef9a3f7b2c67915'), (0, utils_1._n)('0xc67178f2e372532b'),
|
||||
+ (0, utils_1._n)('0xca273eceea26619c'), (0, utils_1._n)('0xd186b8c721c0c207'), (0, utils_1._n)('0xeada7dd6cde0eb1e'), (0, utils_1._n)('0xf57d4f7fee6ed178'),
|
||||
+ (0, utils_1._n)('0x06f067aa72176fba'), (0, utils_1._n)('0x0a637dc5a2c898a6'), (0, utils_1._n)('0x113f9804bef90dae'), (0, utils_1._n)('0x1b710b35131c471b'),
|
||||
+ (0, utils_1._n)('0x28db77f523047d84'), (0, utils_1._n)('0x32caab7b40c72493'), (0, utils_1._n)('0x3c9ebe0a15c9bebc'), (0, utils_1._n)('0x431d67c49c100d4c'),
|
||||
+ (0, utils_1._n)('0x4cc5d4becb3e42b6'), (0, utils_1._n)('0x597f299cfc657e2a'), (0, utils_1._n)('0x5fcb6fab3ad6faec'), (0, utils_1._n)('0x6c44198c4a475817')
|
||||
]);
|
||||
// Temporary buffer, not used to store anything between runs
|
||||
const SHA512_W_H = new Uint32Array(80);
|
||||
diff --git a/node_modules/@noble/hashes/lib/utils.d.ts b/node_modules/@noble/hashes/lib/utils.d.ts
|
||||
index 8233e80..67c4246 100644
|
||||
--- a/node_modules/@noble/hashes/lib/utils.d.ts
|
||||
+++ b/node_modules/@noble/hashes/lib/utils.d.ts
|
||||
@@ -1,5 +1,6 @@
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
export declare type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array | Uint16Array | Int16Array | Uint32Array | Int32Array;
|
||||
+export declare const _n: (value: string | number | bigint | boolean) => bigint;
|
||||
export declare const u8: (arr: TypedArray) => Uint8Array;
|
||||
export declare const u32: (arr: TypedArray) => Uint32Array;
|
||||
export declare const createView: (arr: TypedArray) => DataView;
|
||||
@@ -24,8 +25,8 @@ export declare abstract class Hash<T extends Hash<T>> {
|
||||
clone(): T;
|
||||
}
|
||||
export declare type HashXOF<T extends Hash<T>> = Hash<T> & {
|
||||
- XOF(bytes: number): Uint8Array;
|
||||
- XOFInto(buf: Uint8Array): Uint8Array;
|
||||
+ xof(bytes: number): Uint8Array;
|
||||
+ xofInto(buf: Uint8Array): Uint8Array;
|
||||
};
|
||||
declare type EmptyObj = {};
|
||||
export declare function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(def: T1, _opts?: T2): T1 & T2;
|
||||
@@ -46,7 +47,7 @@ export declare function wrapConstructorWithOpts<H extends Hash<H>, T extends Obj
|
||||
};
|
||||
export declare const crypto: {
|
||||
node?: any;
|
||||
- web?: Crypto;
|
||||
+ web?: any;
|
||||
};
|
||||
export declare function randomBytes(bytesLength?: number): Uint8Array;
|
||||
export {};
|
||||
diff --git a/node_modules/@noble/hashes/lib/utils.js b/node_modules/@noble/hashes/lib/utils.js
|
||||
index 1d45928..3811cf9 100644
|
||||
--- a/node_modules/@noble/hashes/lib/utils.js
|
||||
+++ b/node_modules/@noble/hashes/lib/utils.js
|
||||
@@ -1,7 +1,9 @@
|
||||
"use strict";
|
||||
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
-exports.randomBytes = exports.crypto = exports.wrapConstructorWithOpts = exports.wrapConstructor = exports.checkOpts = exports.Hash = exports.assertHash = exports.assertBool = exports.assertNumber = exports.toBytes = exports.asyncLoop = exports.nextTick = exports.bytesToHex = exports.isLE = exports.rotr = exports.createView = exports.u32 = exports.u8 = void 0;
|
||||
+exports.randomBytes = exports.crypto = exports.wrapConstructorWithOpts = exports.wrapConstructor = exports.checkOpts = exports.Hash = exports.assertHash = exports.assertBool = exports.assertNumber = exports.toBytes = exports.asyncLoop = exports.nextTick = exports.bytesToHex = exports.isLE = exports.rotr = exports.createView = exports.u32 = exports.u8 = exports._n = void 0;
|
||||
+// helper to protect against older bundlers
|
||||
+exports._n = typeof BigInt !== 'undefined' ? BigInt : () => Number.NaN;
|
||||
// Cast array to different type
|
||||
const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
exports.u8 = u8;
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { _n } from '@noble/hashes/lib/utils';
|
||||
|
||||
describe('@noble/hashes', (): void => {
|
||||
it('is patched', (): void => {
|
||||
expect(
|
||||
_n(123) === 123n
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { pbkdf2 as pbkdf2Js } from '@noble/hashes/lib/pbkdf2';
|
||||
import { sha512 } from '@noble/hashes/lib/sha512';
|
||||
|
||||
import { hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, pbkdf2 } from '@polkadot/wasm-crypto';
|
||||
import { pbkdf2 as pbkdf2Js } from '@polkadot/x-noble-hashes/pbkdf2';
|
||||
import { sha512 } from '@polkadot/x-noble-hashes/sha512';
|
||||
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
import type { Params } from './types';
|
||||
|
||||
import { scrypt as scryptJs } from '@noble/hashes/lib/scrypt';
|
||||
|
||||
import { hasBigInt, objectSpread, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, scrypt } from '@polkadot/wasm-crypto';
|
||||
import { scrypt as scryptJs } from '@polkadot/x-noble-hashes/scrypt';
|
||||
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import { DEFAULT_PARAMS } from './defaults';
|
||||
|
||||
@@ -2,17 +2,43 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { secp256k1Compress } from './';
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1Compress } from '.';
|
||||
|
||||
describe('secp256k1Compress', (): void => {
|
||||
it('compresses a known key', (): void => {
|
||||
expect(
|
||||
secp256k1Compress(
|
||||
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
);
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
|
||||
it('returns a compressed key as-is', (): void => {
|
||||
expect(
|
||||
secp256k1Compress(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'),
|
||||
onlyJs
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
);
|
||||
});
|
||||
|
||||
it('compresses a known key', (): void => {
|
||||
expect(
|
||||
secp256k1Compress(
|
||||
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af'),
|
||||
onlyJs
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
performanceWasm('secp256k1Compress', 100000, (input, onlyJs) => secp256k1Compress(input, onlyJs),
|
||||
[[
|
||||
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
]]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1Compress as wasm } from '@polkadot/wasm-crypto';
|
||||
import { Point } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { secp256k1 } from './secp256k1';
|
||||
export function secp256k1Compress (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
|
||||
if (publicKey.length === 33) {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
export function secp256k1Compress (publicKey: Uint8Array): Uint8Array {
|
||||
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
||||
assert(publicKey.length === 65, 'Invalid publicKey provided');
|
||||
|
||||
return new Uint8Array(
|
||||
secp256k1
|
||||
.keyFromPublic(publicKey)
|
||||
.getPublic()
|
||||
.encodeCompressed()
|
||||
);
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? wasm(publicKey)
|
||||
: Point.fromHex(publicKey).toRawBytes(true);
|
||||
}
|
||||
|
||||
@@ -2,27 +2,43 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { secp256k1Expand } from './';
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1Expand } from '.';
|
||||
|
||||
describe('secp256k1Expand', (): void => {
|
||||
it('expands a known key', (): void => {
|
||||
expect(
|
||||
secp256k1Expand(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
);
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
it('expands a known full key', (): void => {
|
||||
expect(
|
||||
secp256k1Expand(
|
||||
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
);
|
||||
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
|
||||
it('expands a known key', (): void => {
|
||||
expect(
|
||||
secp256k1Expand(
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'),
|
||||
onlyJs
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
);
|
||||
});
|
||||
|
||||
it('expands a known full key', (): void => {
|
||||
expect(
|
||||
secp256k1Expand(
|
||||
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af'),
|
||||
onlyJs
|
||||
)
|
||||
).toEqual(
|
||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
performanceWasm('secp256k1Expand', 2000, (input, onlyJs) => secp256k1Expand(input, onlyJs),
|
||||
[[
|
||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
]]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Expand as wasm } from '@polkadot/wasm-crypto';
|
||||
import { Point } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../bn';
|
||||
import { secp256k1 } from './secp256k1';
|
||||
|
||||
export function secp256k1Expand (publicKey: Uint8Array): Uint8Array {
|
||||
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
||||
export function secp256k1Expand (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
|
||||
if (publicKey.length === 65) {
|
||||
return publicKey.subarray(1);
|
||||
}
|
||||
|
||||
const expanded = secp256k1
|
||||
.keyFromPublic(publicKey)
|
||||
.getPublic();
|
||||
assert(publicKey.length === 33, 'Invalid publicKey provided');
|
||||
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
return wasm(publicKey).subarray(1);
|
||||
}
|
||||
|
||||
const { x, y } = Point.fromHex(publicKey);
|
||||
|
||||
return u8aConcat(
|
||||
bnToU8a(expanded.getX(), BN_BE_256_OPTS),
|
||||
bnToU8a(expanded.getY(), BN_BE_256_OPTS)
|
||||
bnToU8a(x, BN_BE_256_OPTS),
|
||||
bnToU8a(y, BN_BE_256_OPTS)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
|
||||
describe('secp256k1Hasher', (): void => {
|
||||
it('fails with unknown hasher', (): void => {
|
||||
expect(
|
||||
() => secp256k1Hasher('unknown' as 'blake2', 'testing')
|
||||
).toThrow(/Unsupported secp256k1 hasher 'unknown', expected one of blake2, keccak/);
|
||||
});
|
||||
import { hasher } from './hasher';
|
||||
|
||||
describe('hasher', (): void => {
|
||||
it('creates a blake2 hash', (): void => {
|
||||
expect(
|
||||
secp256k1Hasher('blake2', 'abc')
|
||||
hasher('blake2', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25])
|
||||
);
|
||||
@@ -20,7 +14,7 @@ describe('secp256k1Hasher', (): void => {
|
||||
|
||||
it('creates a keccak hash', (): void => {
|
||||
expect(
|
||||
secp256k1Hasher('keccak', 'abc')
|
||||
hasher('keccak', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([78, 3, 101, 122, 234, 69, 169, 79, 199, 212, 123, 168, 38, 200, 214, 103, 192, 209, 230, 227, 58, 100, 160, 54, 236, 68, 245, 143, 161, 45, 108, 69])
|
||||
);
|
||||
|
||||
@@ -6,14 +6,8 @@ import type { HashType } from './types';
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
import { keccakAsU8a } from '../keccak';
|
||||
|
||||
const HASH_TYPES = ['blake2', 'keccak'];
|
||||
|
||||
export function secp256k1Hasher (hashType: HashType, data: Uint8Array | string): Uint8Array {
|
||||
if (hashType === 'blake2') {
|
||||
return blake2AsU8a(data);
|
||||
} else if (hashType === 'keccak') {
|
||||
return keccakAsU8a(data);
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported secp256k1 hasher '${hashType as string}', expected one of ${HASH_TYPES.join(', ')}`);
|
||||
export function hasher (hashType: HashType, data: Uint8Array | string, onlyJs?: boolean): Uint8Array {
|
||||
return hashType === 'keccak'
|
||||
? keccakAsU8a(data, undefined, onlyJs)
|
||||
: blake2AsU8a(data, undefined, undefined, onlyJs);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a, u8aToHex } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../../test/performance';
|
||||
import { mnemonicToMiniSecret } from '../../mnemonic';
|
||||
import { secp256k1PairFromSeed } from '..';
|
||||
|
||||
@@ -37,23 +39,33 @@ const tests: Test[] = [
|
||||
];
|
||||
|
||||
describe('secp256k1PairFromSeed', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
const TEST = hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae');
|
||||
const RESULT = {
|
||||
publicKey: hexToU8a('0x03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b'),
|
||||
secretKey: hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae')
|
||||
};
|
||||
|
||||
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
|
||||
expect(secp256k1PairFromSeed(TEST)).toEqual(RESULT);
|
||||
});
|
||||
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
|
||||
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
|
||||
expect(secp256k1PairFromSeed(TEST, onlyJs)).toEqual(RESULT);
|
||||
});
|
||||
|
||||
tests.forEach(([mnemonic, secretKey, publicKey], index): void => {
|
||||
it(`creates valid against known (${index})`, (): void => {
|
||||
const seed = mnemonicToMiniSecret(mnemonic);
|
||||
const pair = secp256k1PairFromSeed(seed);
|
||||
tests.forEach(([mnemonic, secretKey, publicKey], index): void => {
|
||||
it(`creates valid against known (${index})`, (): void => {
|
||||
const seed = mnemonicToMiniSecret(mnemonic);
|
||||
const pair = secp256k1PairFromSeed(seed, onlyJs);
|
||||
|
||||
expect(u8aToHex(pair.secretKey)).toEqual(secretKey);
|
||||
expect(u8aToHex(pair.publicKey)).toEqual(publicKey);
|
||||
expect(u8aToHex(pair.secretKey)).toEqual(secretKey);
|
||||
expect(u8aToHex(pair.publicKey)).toEqual(publicKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
performanceWasm('secp256k1PairFromSeed', 500, (input, onlyJs) =>
|
||||
secp256k1PairFromSeed(input, onlyJs)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,22 +3,28 @@
|
||||
|
||||
import type { Keypair } from '../../types';
|
||||
|
||||
import { assert, bnToU8a } from '@polkadot/util';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../../bn';
|
||||
import { secp256k1 } from '../secp256k1';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1FromSeed } from '@polkadot/wasm-crypto';
|
||||
import { getPublicKey } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
/**
|
||||
* @name secp256k1PairFromSeed
|
||||
* @description Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed.
|
||||
*/
|
||||
export function secp256k1PairFromSeed (seed: Uint8Array): Keypair {
|
||||
export function secp256k1PairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keypair {
|
||||
assert(seed.length === 32, 'Expected valid 32-byte private key as a seed');
|
||||
|
||||
const key = secp256k1.keyFromPrivate(seed);
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
const full = secp256k1FromSeed(seed);
|
||||
|
||||
return {
|
||||
publicKey: full.slice(32),
|
||||
secretKey: full.slice(0, 32)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
publicKey: new Uint8Array(key.getPublic().encodeCompressed()),
|
||||
secretKey: bnToU8a(key.getPrivate(), BN_BE_256_OPTS)
|
||||
publicKey: getPublicKey(seed, true),
|
||||
secretKey: seed
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,17 +2,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { keccakAsU8a } from '../keccak';
|
||||
import { secp256k1Recover } from '.';
|
||||
|
||||
describe('secp256k1Recover', (): void => {
|
||||
it('recovers a publicKey', (): void => {
|
||||
const pubKey = '0x93a9fc7154c6da3c826415df01eb0e37fb4da4b0';
|
||||
const sig = '0x7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff';
|
||||
const msg = '0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9';
|
||||
const res = keccakAsU8a(secp256k1Recover(u8aToU8a(msg), u8aToU8a(sig), 0));
|
||||
const sig = u8aToU8a('0x7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff');
|
||||
const msg = u8aToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
|
||||
|
||||
expect(u8aToHex(res.subarray(12))).toEqual(pubKey);
|
||||
describe('secp256k1Recover', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
|
||||
it('recovers a publicKey', (): void => {
|
||||
const pubKey = '0x93a9fc7154c6da3c826415df01eb0e37fb4da4b0';
|
||||
const res = keccakAsU8a(secp256k1Recover(msg, sig, 0, undefined, onlyJs));
|
||||
|
||||
expect(u8aToHex(res.subarray(-20))).toEqual(pubKey);
|
||||
});
|
||||
});
|
||||
|
||||
performanceWasm('secp256k1Recover', 200, (_, onlyJs) =>
|
||||
secp256k1Recover(msg, sig, 0, undefined, onlyJs)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { secp256k1 } from './secp256k1';
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
import type { HashType } from './types';
|
||||
|
||||
import { assert, hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, secp256k1Recover as wasm } from '@polkadot/wasm-crypto';
|
||||
import { recoverPublicKey, Signature } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { secp256k1Compress } from './compress';
|
||||
import { secp256k1Expand } from './expand';
|
||||
|
||||
/**
|
||||
* @name secp256k1Recover
|
||||
* @description Recovers a publicKey from the supplied signature
|
||||
*/
|
||||
export function secp256k1Recover (message: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array {
|
||||
return new Uint8Array(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
|
||||
secp256k1
|
||||
.recoverPubKey(message, { r: signature.slice(0, 32), s: signature.slice(32, 64) }, recovery)
|
||||
.encode(null, true)
|
||||
);
|
||||
export function secp256k1Recover (msgHash: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, recovery: number, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
|
||||
const sig = u8aToU8a(signature).subarray(0, 64);
|
||||
const msg = u8aToU8a(msgHash);
|
||||
const publicKey = !hasBigInt || (!onlyJs && isReady())
|
||||
? wasm(msg, sig, recovery)
|
||||
: recoverPublicKey(msg, Signature.fromCompact(sig).toRawBytes(), recovery);
|
||||
|
||||
assert(publicKey, 'Unable to recover publicKey from signature');
|
||||
|
||||
return hashType === 'keccak'
|
||||
? secp256k1Expand(publicKey, onlyJs)
|
||||
: secp256k1Compress(publicKey, onlyJs);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import elliptic from 'elliptic';
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
export const secp256k1 = new elliptic.ec('secp256k1');
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1PairFromSeed, secp256k1Sign } from '.';
|
||||
|
||||
const pair = secp256k1PairFromSeed(hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae'));
|
||||
const msg = hexToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
|
||||
|
||||
describe('sign', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
|
||||
it('generates a known signature', (): void => {
|
||||
expect(
|
||||
secp256k1Sign(msg, pair, undefined, onlyJs)
|
||||
).toEqual(hexToU8a(
|
||||
// from elliptic, this is - 0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300
|
||||
// libsecp256k1 & @noble/hashes do agree here...
|
||||
'0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01'
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
// since the libsecp256k1 signatures don't match (but can be verified), we
|
||||
// do both signing and verification here (checking the extracted key)
|
||||
performanceWasm('secp256k1Sign', 1000, (_, onlyJs) =>
|
||||
secp256k1Sign(msg, pair, undefined, onlyJs)
|
||||
);
|
||||
});
|
||||
@@ -4,25 +4,32 @@
|
||||
import type { Keypair } from '../types';
|
||||
import type { HashType } from './types';
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Sign as wasm } from '@polkadot/wasm-crypto';
|
||||
import { Signature, signSync } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../bn';
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { secp256k1 } from './secp256k1';
|
||||
import { hasher } from './hasher';
|
||||
|
||||
/**
|
||||
* @name secp256k1Sign
|
||||
* @description Returns message signature of `message`, using the supplied pair
|
||||
*/
|
||||
export function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2'): Uint8Array {
|
||||
export function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
|
||||
assert(secretKey?.length === 32, 'Expected valid secp256k1 secretKey, 32-bytes');
|
||||
|
||||
const key = secp256k1.keyFromPrivate(secretKey);
|
||||
const ecsig = key.sign(secp256k1Hasher(hashType, message));
|
||||
const data = hasher(hashType, message, onlyJs);
|
||||
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
return wasm(data, secretKey);
|
||||
}
|
||||
|
||||
const [sigBytes, recoveryParam] = signSync(data, secretKey, { canonical: true, recovered: true });
|
||||
const { r, s } = Signature.fromHex(sigBytes);
|
||||
|
||||
return u8aConcat(
|
||||
bnToU8a(ecsig.r, BN_BE_256_OPTS),
|
||||
bnToU8a(ecsig.s, BN_BE_256_OPTS),
|
||||
new Uint8Array([ecsig.recoveryParam || 0])
|
||||
bnToU8a(r, BN_BE_256_OPTS),
|
||||
bnToU8a(s, BN_BE_256_OPTS),
|
||||
new Uint8Array([recoveryParam || 0])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '../cryptoInit';
|
||||
|
||||
import { stringToU8a } from '@polkadot/util';
|
||||
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { hasher } from './hasher';
|
||||
import { secp256k1Expand, secp256k1PairFromSeed, secp256k1Sign, secp256k1Verify } from '.';
|
||||
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
@@ -15,7 +17,7 @@ describe('sign and verify', (): void => {
|
||||
const sig = '0x92fcacf0946bbd10b31dfe16d567ed1d3014e81007dd9e5256e19c0f07eacc1643b151ca29e449a765e16a7ce59b88d800467d6b3412d30ea8ad22307a59664b00';
|
||||
const msg = stringToU8a('secp256k1');
|
||||
|
||||
expect(secp256k1Verify(msg, sig, address)).toBe(true);
|
||||
expect(secp256k1Verify(msg, sig, address)).toEqual(true);
|
||||
});
|
||||
|
||||
it('has 65-byte signatures', (): void => {
|
||||
@@ -27,25 +29,25 @@ describe('sign and verify', (): void => {
|
||||
it('signs/verifies a message by random key (blake2)', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair);
|
||||
const address = secp256k1Hasher('blake2', pair.publicKey);
|
||||
const address = hasher('blake2', pair.publicKey);
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address)).toBe(true);
|
||||
expect(secp256k1Verify(MESSAGE, signature, address)).toEqual(true);
|
||||
});
|
||||
|
||||
it('signs/verifies a message by random key (keccak)', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
|
||||
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'keccak')).toBe(true);
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'keccak')).toEqual(true);
|
||||
});
|
||||
|
||||
it('fails verification on hasher mismatches', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
|
||||
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'blake2')).toBe(false);
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'blake2')).toEqual(false);
|
||||
});
|
||||
|
||||
it('works over a range of random keys (blake2)', (): void => {
|
||||
@@ -57,10 +59,10 @@ describe('sign and verify', (): void => {
|
||||
secp256k1Verify(
|
||||
MESSAGE,
|
||||
secp256k1Sign(MESSAGE, pair, 'blake2'),
|
||||
secp256k1Hasher('blake2', pair.publicKey),
|
||||
hasher('blake2', pair.publicKey),
|
||||
'blake2'
|
||||
)
|
||||
).toBe(true);
|
||||
).toEqual(true);
|
||||
} catch (error) {
|
||||
console.error(`blake2 failed on #${i}`);
|
||||
throw error;
|
||||
@@ -77,10 +79,10 @@ describe('sign and verify', (): void => {
|
||||
secp256k1Verify(
|
||||
MESSAGE,
|
||||
secp256k1Sign(MESSAGE, pair, 'keccak'),
|
||||
secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey)),
|
||||
hasher('keccak', secp256k1Expand(pair.publicKey)),
|
||||
'keccak'
|
||||
)
|
||||
).toBe(true);
|
||||
).toEqual(true);
|
||||
} catch (error) {
|
||||
console.error(`keccak failed on #${i}`);
|
||||
throw error;
|
||||
|
||||
@@ -4,13 +4,22 @@
|
||||
import { secp256k1PrivateKeyTweakAdd } from './tweakAdd';
|
||||
|
||||
describe('TweakAdd', (): void => {
|
||||
describe('PrivateKey TweakAdd', (): void => {
|
||||
it('fails for wrong array length', (): void => {
|
||||
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
|
||||
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
|
||||
|
||||
expect(
|
||||
() => secp256k1PrivateKeyTweakAdd(A, B)
|
||||
).toThrow(/Expected tweak to be an Uint8Array/);
|
||||
});
|
||||
|
||||
describe.each([false, true])('onlyBn=%p', (onlyBn): void => {
|
||||
it('succeeds for a simple case', (): void => {
|
||||
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
|
||||
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
|
||||
|
||||
expect(
|
||||
secp256k1PrivateKeyTweakAdd(A, B)
|
||||
secp256k1PrivateKeyTweakAdd(A, B, onlyBn)
|
||||
).toEqual(new Uint8Array([
|
||||
3, 4, 3, 4, 3, 4, 3, 4, 3,
|
||||
4, 3, 4, 3, 4, 3, 4, 3, 4,
|
||||
@@ -18,14 +27,5 @@ describe('TweakAdd', (): void => {
|
||||
4, 3, 4, 3, 4
|
||||
]));
|
||||
});
|
||||
|
||||
it('fails for wrong array length', (): void => {
|
||||
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
|
||||
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
|
||||
|
||||
expect(
|
||||
() => secp256k1PrivateKeyTweakAdd(A, B)
|
||||
).toThrow(/Expected tweak to be an Uint8Array/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { sha256 as sha256Js } from '@noble/hashes/lib/sha256';
|
||||
import { sha512 as sha512Js } from '@noble/hashes/lib/sha512';
|
||||
|
||||
import { hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, sha256, sha512 } from '@polkadot/wasm-crypto';
|
||||
import { sha256 as sha256Js } from '@polkadot/x-noble-hashes/sha256';
|
||||
import { sha512 as sha512Js } from '@polkadot/x-noble-hashes/sha512';
|
||||
|
||||
import { createBitHasher } from '../helpers';
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { _0n, _1n, _n } from '@polkadot/util';
|
||||
import { _0n, _1n } from '@polkadot/util';
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
// Adapted from https://github.com/pierrec/js-xxhash/blob/0504e76f3d31a21ae8528a7f590c7289c9e431d2/lib/xxhash64.js
|
||||
//
|
||||
@@ -26,29 +27,29 @@ interface State {
|
||||
v4: bigint;
|
||||
}
|
||||
|
||||
const P64_1 = _n('11400714785074694791');
|
||||
const P64_2 = _n('14029467366897019727');
|
||||
const P64_3 = _n('1609587929392839161');
|
||||
const P64_4 = _n('9650029242287828579');
|
||||
const P64_5 = _n('2870177450012600261');
|
||||
const P64_1 = BigInt('11400714785074694791');
|
||||
const P64_2 = BigInt('14029467366897019727');
|
||||
const P64_3 = BigInt('1609587929392839161');
|
||||
const P64_4 = BigInt('9650029242287828579');
|
||||
const P64_5 = BigInt('2870177450012600261');
|
||||
|
||||
// mask for a u64, all bits set
|
||||
const U64 = _n('0xffffffffffffffff');
|
||||
const U64 = BigInt('0xffffffffffffffff');
|
||||
|
||||
// various constants
|
||||
const _7n = _n(7);
|
||||
const _11n = _n(11);
|
||||
const _12n = _n(12);
|
||||
const _16n = _n(16);
|
||||
const _18n = _n(18);
|
||||
const _23n = _n(23);
|
||||
const _27n = _n(27);
|
||||
const _29n = _n(29);
|
||||
const _31n = _n(31);
|
||||
const _32n = _n(32);
|
||||
const _33n = _n(33);
|
||||
const _64n = _n(64);
|
||||
const _256n = _n(256);
|
||||
const _7n = BigInt(7);
|
||||
const _11n = BigInt(11);
|
||||
const _12n = BigInt(12);
|
||||
const _16n = BigInt(16);
|
||||
const _18n = BigInt(18);
|
||||
const _23n = BigInt(23);
|
||||
const _27n = BigInt(27);
|
||||
const _29n = BigInt(29);
|
||||
const _31n = BigInt(31);
|
||||
const _32n = BigInt(32);
|
||||
const _33n = BigInt(33);
|
||||
const _64n = BigInt(64);
|
||||
const _256n = BigInt(256);
|
||||
|
||||
function rotl (a: bigint, b: bigint): bigint {
|
||||
const c = a & U64;
|
||||
|
||||
@@ -10,8 +10,8 @@ type ExecFn = (input: Uint8Array, onlyJs: boolean) => unknown;
|
||||
|
||||
const GENERATED = arrayRange(256).map(() => [randomAsU8a()]);
|
||||
|
||||
export function performanceWasm (name: string, count: number, exec: ExecFn): void {
|
||||
performanceCmp(name, ['WebAssembly', 'JavaScript'], count, GENERATED, exec);
|
||||
export function performanceWasm (name: string, count: number, exec: ExecFn, inputs = GENERATED): void {
|
||||
performanceCmp(name, ['WebAssembly', 'JavaScript'], count, inputs, exec);
|
||||
}
|
||||
|
||||
export { performance, performanceCmp };
|
||||
|
||||
@@ -34,12 +34,14 @@
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/x-textdecoder": "8.0.1",
|
||||
"@polkadot/x-textencoder": "8.0.1",
|
||||
"@polkadot/x-bigint": "8.1.1",
|
||||
"@polkadot/x-global": "8.1.1",
|
||||
"@polkadot/x-textdecoder": "8.1.1",
|
||||
"@polkadot/x-textencoder": "8.1.1",
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"bn.js": "^4.12.0",
|
||||
"ip-regex": "^4.3.0"
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
type NewBigInt = (value: string | number | bigint | boolean) => bigint;
|
||||
|
||||
/**
|
||||
* @name _n
|
||||
* @description Creates a new instance of BigInt in environments that do support it
|
||||
*/
|
||||
export const _n: NewBigInt = typeof BigInt !== 'undefined'
|
||||
? BigInt
|
||||
: () => Number.NaN as unknown as bigint;
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
/**
|
||||
* @name _0n
|
||||
* @summary BigInt constant for 0.
|
||||
*/
|
||||
export const _0n = _n(0);
|
||||
export const _0n = BigInt(0);
|
||||
|
||||
/**
|
||||
* @name _1n
|
||||
* @summary BigInt constant for 1.
|
||||
*/
|
||||
export const _1n = _n(1);
|
||||
export const _1n = BigInt(1);
|
||||
|
||||
/**
|
||||
* @name _1Mn
|
||||
* @summary BigInt constant for 1,000,000.
|
||||
*/
|
||||
export const _1Mn = _n(1_000_000);
|
||||
export const _1Mn = BigInt(1_000_000);
|
||||
|
||||
/**
|
||||
* @name _1Bn
|
||||
* @summary BigInt constant for 1,000,000,000.
|
||||
*/
|
||||
export const _1Bn = _n(1_000_000_000);
|
||||
export const _1Bn = BigInt(1_000_000_000);
|
||||
|
||||
/**
|
||||
* @name _1Qn
|
||||
@@ -45,4 +37,4 @@ export const _1Qn = _1Bn * _1Bn;
|
||||
* @name _2pow53n
|
||||
* @summary BigInt constant for MAX_SAFE_INTEGER
|
||||
*/
|
||||
export const _2pow53n = _n(Number.MAX_SAFE_INTEGER);
|
||||
export const _2pow53n = BigInt(Number.MAX_SAFE_INTEGER);
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
import type { BN } from '../bn';
|
||||
import type { ToBigInt, ToBn } from '../types';
|
||||
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
import { assert } from '../assert';
|
||||
import { _0n, _1n, _2pow53n, _n } from './consts';
|
||||
import { _0n, _1n, _2pow53n } from './consts';
|
||||
import { nToBigInt } from './toBigInt';
|
||||
|
||||
const _sqrt2pow53n = _n(94906265);
|
||||
const _sqrt2pow53n = BigInt(94906265);
|
||||
|
||||
/**
|
||||
* @name nSqrt
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import type { BN } from '../bn/bn';
|
||||
import type { HexString, ToBigInt, ToBn } from '../types';
|
||||
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
import { hexToBigInt } from '../hex/toBigInt';
|
||||
import { isBn } from '../is/bn';
|
||||
import { isHex } from '../is/hex';
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
import type { BN } from '../bn/bn';
|
||||
import type { ToBigInt, ToBn, ToBnOptions } from '../types';
|
||||
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
import { objectSpread } from '../object/spread';
|
||||
import { _0n, _1n, _n } from './consts';
|
||||
import { _0n, _1n } from './consts';
|
||||
import { nToBigInt } from './toBigInt';
|
||||
|
||||
interface Options extends ToBnOptions {
|
||||
bitLength?: number;
|
||||
}
|
||||
|
||||
const DIV = _n(256);
|
||||
const NEG_MASK = _n(0xff);
|
||||
const DIV = BigInt(256);
|
||||
const NEG_MASK = BigInt(0xff);
|
||||
|
||||
function createEmpty ({ bitLength = 0 }: Options): Uint8Array {
|
||||
return bitLength === -1
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const hasBigInt = typeof BigInt !== 'undefined';
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
export const hasBigInt = typeof BigInt === 'function' && typeof BigInt.asIntN === 'function';
|
||||
export const hasBuffer = typeof Buffer !== 'undefined';
|
||||
export const hasDirname = typeof __dirname !== 'undefined';
|
||||
export const hasProcess = typeof process === 'object';
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import type { ToBnOptions } from '../types';
|
||||
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
import { objectSpread } from '../object/spread';
|
||||
import { u8aToBigInt } from '../u8a/toBigInt';
|
||||
import { hexToU8a } from './toU8a';
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
* ```
|
||||
*/
|
||||
export function isBigInt (value: unknown): value is bigint {
|
||||
return typeof value === 'bigint' || value instanceof BigInt;
|
||||
return typeof value === 'bigint';
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/util', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/util', version: '8.1.1' };
|
||||
|
||||
@@ -33,8 +33,8 @@ const randomWords = arrayRange(256).map((): [string] => {
|
||||
describe('stringCamelCase', (): void => {
|
||||
it('works correctly', (): void => {
|
||||
expect(
|
||||
stringCamelCase('Snake_case-...Something spaced')
|
||||
).toBe('snakeCaseSomethingSpaced');
|
||||
stringCamelCase('Snake_case-...SomethingSomething spaced')
|
||||
).toBe('snakeCaseSomethingSomethingSpaced');
|
||||
});
|
||||
|
||||
it('works correctly for String (class', (): void => {
|
||||
@@ -43,5 +43,41 @@ describe('stringCamelCase', (): void => {
|
||||
).toBe('fooBarBazTestSpacedExtra');
|
||||
});
|
||||
|
||||
it('adjusts all-uppercase', (): void => {
|
||||
expect(
|
||||
stringCamelCase('DEDUP_KEY_PREFIX')
|
||||
).toEqual('dedupKeyPrefix');
|
||||
expect(
|
||||
stringCamelCase('SOMETHING')
|
||||
).toEqual('something');
|
||||
});
|
||||
|
||||
it('adjusts all-uppercase + digits', (): void => {
|
||||
expect(
|
||||
stringCamelCase('SS58 PreFIX')
|
||||
).toEqual('ss58Prefix');
|
||||
expect(
|
||||
stringCamelCase('SS58Prefix')
|
||||
).toEqual('ss58Prefix');
|
||||
expect(
|
||||
stringCamelCase('UUID64')
|
||||
).toEqual('uuid64');
|
||||
expect(
|
||||
stringCamelCase('BLAKE2B')
|
||||
).toEqual('blake2B');
|
||||
});
|
||||
|
||||
it('adjusts with leading _', (): void => {
|
||||
expect(
|
||||
stringCamelCase('_reserved')
|
||||
).toEqual('reserved');
|
||||
});
|
||||
|
||||
it('adjusts with minimal chars per part', (): void => {
|
||||
expect(
|
||||
stringCamelCase('_a_b_c_def')
|
||||
).toEqual('aBCDef');
|
||||
});
|
||||
|
||||
performance('stringCamelCase', 64000, randomWords, stringCamelCase);
|
||||
});
|
||||
|
||||
@@ -3,27 +3,32 @@
|
||||
|
||||
import type { AnyString } from '../types';
|
||||
|
||||
const RE_WHITE = /[-_., ]+/g;
|
||||
const RE_WORDS = /(?:^\w|[A-Z]|\b\w)/g;
|
||||
const RE_JOIN = /\s/g;
|
||||
|
||||
// Adapted from https://stackoverflow.com/a/2970667
|
||||
//
|
||||
// this is not as optimal as the original (we split into multiple), however
|
||||
// it does pass the tests (which the original doesn't) and it is still 10+x
|
||||
// improvement over the original camelcase npm package (at running)
|
||||
// this is not as optimal as the original asnwer (we split into multiple),
|
||||
// however it does pass the tests (which the original doesn't) and it is still
|
||||
// a 10+x improvement over the original camelcase npm package (at running)
|
||||
//
|
||||
// original: 20.88 μs/op
|
||||
// this: 1.67 μs/op
|
||||
// this: 1.91 μs/op
|
||||
//
|
||||
// Caveat of this: only Ascii, but acceptable for the intended usecase
|
||||
function converter (fn: (w: string, i: number) => string): (value: AnyString) => string {
|
||||
return (value: AnyString): string =>
|
||||
value
|
||||
.toString()
|
||||
.replace(RE_WHITE, ' ')
|
||||
.replace(RE_WORDS, fn)
|
||||
.replace(RE_JOIN, '');
|
||||
// replace all seperators (including consequtive) with spaces
|
||||
.replace(/[-_., ]+/g, ' ')
|
||||
// we don't want leading or trailing spaces
|
||||
.trim()
|
||||
// all consequtive capitals are changed to lowercase
|
||||
.replace(/[A-Z]{2,}/g, (w) => w.toLowerCase())
|
||||
// split into words
|
||||
.split(' ')
|
||||
// apply the function to the first letter, the rest as-is
|
||||
.map((w, i) => fn(w[0], i) + w.slice(1))
|
||||
// combine into a single word
|
||||
.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
import type { ToBnOptions } from '../types';
|
||||
|
||||
import { _1n, _n } from '../bi/consts';
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
|
||||
import { _1n } from '../bi/consts';
|
||||
import { objectSpread } from '../object/spread';
|
||||
|
||||
const U8_MAX = _n(256);
|
||||
const U16_MAX = _n(256 * 256);
|
||||
const U8_MAX = BigInt(256);
|
||||
const U16_MAX = BigInt(256 * 256);
|
||||
|
||||
function xor (input: Uint8Array): Uint8Array {
|
||||
const result = new Uint8Array(input.length);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# @polkadot/x-bigint
|
||||
|
||||
A cross-environment BigInt.
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"contributors": [],
|
||||
"description": "A cross-environment BigInt replacement",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-bigint#readme",
|
||||
"license": "Apache-2.0",
|
||||
"maintainers": [],
|
||||
"name": "@polkadot/x-bigint",
|
||||
"repository": {
|
||||
"directory": "packages/x-bigint",
|
||||
"type": "git",
|
||||
"url": "https://github.com/polkadot-js/common.git"
|
||||
},
|
||||
"sideEffects": [
|
||||
"./shim.js",
|
||||
"./shim.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "8.1.1",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/x-global": "8.1.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { xglobal } from '@polkadot/x-global';
|
||||
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export const BigInt: BigIntConstructor = typeof xglobal.BigInt === 'function' && typeof xglobal.BigInt.asIntN === 'function'
|
||||
? xglobal.BigInt
|
||||
: (() => Number.NaN) as unknown as BigIntConstructor;
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/x-bigint', version: '8.1.1' };
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/x-bigint authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BigInt } from '@polkadot/x-bigint';
|
||||
import { xglobal } from '@polkadot/x-global';
|
||||
|
||||
if (typeof xglobal.BigInt !== 'function') {
|
||||
(xglobal as Record<string, unknown>).BigInt = BigInt;
|
||||
}
|
||||
@@ -17,11 +17,11 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/util": "8.0.1",
|
||||
"@polkadot/util": "8.1.1",
|
||||
"buffer-es6": "^4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/x-bundle', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/x-bundle', version: '8.1.1' };
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
"react-native": "react-native.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@polkadot/x-global": "8.0.1",
|
||||
"@polkadot/x-global": "8.1.1",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"node-fetch": "^2.6.6"
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/x-fetch', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/x-fetch', version: '8.1.1' };
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/x-fetch authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { fetch } from '@polkadot/x-fetch';
|
||||
import { xglobal } from '@polkadot/x-global';
|
||||
|
||||
if (typeof xglobal.fetch !== 'function') {
|
||||
(xglobal as Record<string, unknown>).fetch = fetch;
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.0.1",
|
||||
"version": "8.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3"
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/x-global', version: '8.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/x-global', version: '8.1.1' };
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Paul Miller (https://paulmillr.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,438 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"contributors": [],
|
||||
"description": "An fork of @noble/hashes with extra protection on BigInt usage",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-noble-hashes#readme",
|
||||
"license": "MIT",
|
||||
"maintainers": [],
|
||||
"name": "@polkadot/x-noble-hashes",
|
||||
"repository": {
|
||||
"directory": "packages/x-noble-hashes",
|
||||
"type": "git",
|
||||
"url": "https://github.com/polkadot-js/common.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "8.1.1",
|
||||
"browser": {
|
||||
"crypto": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { assertNumber, Hash, Input, toBytes, u32 } from './utils';
|
||||
// prettier-ignore
|
||||
export const SIGMA = new Uint8Array([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
||||
// For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1].
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
]);
|
||||
|
||||
export type BlakeOpts = {
|
||||
dkLen?: number;
|
||||
key?: Input;
|
||||
salt?: Input;
|
||||
personalization?: Input;
|
||||
};
|
||||
|
||||
export abstract class BLAKE2<T extends BLAKE2<T>> extends Hash<T> {
|
||||
protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void;
|
||||
protected abstract get(): number[];
|
||||
protected abstract set(...args: number[]): void;
|
||||
abstract override destroy(): void;
|
||||
protected buffer: Uint8Array;
|
||||
protected buffer32: Uint32Array;
|
||||
protected length: number = 0;
|
||||
protected pos: number = 0;
|
||||
protected finished = false;
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(
|
||||
readonly blockLen: number,
|
||||
public outputLen: number,
|
||||
opts: BlakeOpts = {},
|
||||
keyLen: number,
|
||||
saltLen: number,
|
||||
persLen: number
|
||||
) {
|
||||
super();
|
||||
assertNumber(blockLen);
|
||||
assertNumber(outputLen);
|
||||
assertNumber(keyLen);
|
||||
if (outputLen < 0 || outputLen > keyLen)
|
||||
throw new Error('Blake2: outputLen bigger than keyLen');
|
||||
if (opts.key !== undefined && (opts.key.length < 1 || opts.key.length > keyLen))
|
||||
throw new Error(`Key should be up 1..${keyLen} byte long or undefined`);
|
||||
if (opts.salt !== undefined && opts.salt.length !== saltLen)
|
||||
throw new Error(`Salt should be ${saltLen} byte long or undefined`);
|
||||
if (opts.personalization !== undefined && opts.personalization.length !== persLen)
|
||||
throw new Error(`Personalization should be ${persLen} byte long or undefined`);
|
||||
this.buffer32 = u32((this.buffer = new Uint8Array(blockLen)));
|
||||
}
|
||||
update(data: Input) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
// Main difference with other hashes: there is flag for last block,
|
||||
// so we cannot process current block before we know that there
|
||||
// is the next one. This significantly complicates logic and reduces ability
|
||||
// to do zero-copy processing
|
||||
const { finished, blockLen, buffer, buffer32 } = this;
|
||||
if (finished) throw new Error('digest() was already called');
|
||||
data = toBytes(data);
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len; ) {
|
||||
// If buffer is full and we still have input (don't process last block, same as blake2s)
|
||||
if (this.pos === blockLen) {
|
||||
this.compress(buffer32, 0, false);
|
||||
this.pos = 0;
|
||||
}
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
const dataOffset = data.byteOffset + pos;
|
||||
// full block && aligned to 4 bytes && not last in input
|
||||
if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
|
||||
const data32 = new Uint32Array(data.buffer, dataOffset, Math.floor((len - pos) / 4));
|
||||
for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
|
||||
this.length += blockLen;
|
||||
this.compress(data32, pos32, false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
this.length += take;
|
||||
pos += take;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
digestInto(out: Uint8Array) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
|
||||
throw new Error('_Blake2: Invalid output buffer');
|
||||
const { finished, pos, buffer32 } = this;
|
||||
if (finished) throw new Error('digest() was already called');
|
||||
this.finished = true;
|
||||
// Padding
|
||||
this.buffer.subarray(pos).fill(0);
|
||||
this.compress(buffer32, 0, true);
|
||||
const out32 = u32(out);
|
||||
this.get().forEach((v, i) => (out32[i] = v));
|
||||
}
|
||||
digest() {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
_cloneInto(to?: T): T {
|
||||
const { buffer, length, finished, destroyed, outputLen, pos } = this;
|
||||
to ||= new (this.constructor as any)({ dkLen: outputLen }) as T;
|
||||
to.set(...this.get());
|
||||
to.length = length;
|
||||
to.finished = finished;
|
||||
to.destroyed = destroyed;
|
||||
to.outputLen = outputLen;
|
||||
to.buffer.set(buffer);
|
||||
to.pos = pos;
|
||||
return to;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Hash, createView, Input, toBytes } from './utils';
|
||||
|
||||
// Polyfill for Safari 14
|
||||
function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void {
|
||||
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
|
||||
const _32n = BigInt(32);
|
||||
const _u32_max = BigInt(0xffffffff);
|
||||
const wh = Number((value >> _32n) & _u32_max);
|
||||
const wl = Number(value & _u32_max);
|
||||
const h = isLE ? 4 : 0;
|
||||
const l = isLE ? 0 : 4;
|
||||
view.setUint32(byteOffset + h, wh, isLE);
|
||||
view.setUint32(byteOffset + l, wl, isLE);
|
||||
}
|
||||
|
||||
// Base SHA2 class (RFC 6234)
|
||||
export abstract class SHA2<T extends SHA2<T>> extends Hash<T> {
|
||||
protected abstract process(buf: DataView, offset: number): void;
|
||||
protected abstract get(): number[];
|
||||
protected abstract set(...args: number[]): void;
|
||||
abstract override destroy(): void;
|
||||
protected abstract roundClean(): void;
|
||||
// For partial updates less than block size
|
||||
protected buffer: Uint8Array;
|
||||
protected view: DataView;
|
||||
protected finished = false;
|
||||
protected length = 0;
|
||||
protected pos = 0;
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(
|
||||
readonly blockLen: number,
|
||||
public outputLen: number,
|
||||
readonly padOffset: number,
|
||||
readonly isLE: boolean
|
||||
) {
|
||||
super();
|
||||
this.buffer = new Uint8Array(blockLen);
|
||||
this.view = createView(this.buffer);
|
||||
}
|
||||
update(data: Input): this {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
const { view, buffer, blockLen, finished } = this;
|
||||
if (finished) throw new Error('digest() was already called');
|
||||
data = toBytes(data);
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len; ) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
// Fast path: we have at least one block in input, cast it to view and process
|
||||
if (take === blockLen) {
|
||||
const dataView = createView(data);
|
||||
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
|
||||
continue;
|
||||
}
|
||||
buffer.set(data.subarray(pos, pos + take), this.pos);
|
||||
this.pos += take;
|
||||
pos += take;
|
||||
if (this.pos === blockLen) {
|
||||
this.process(view, 0);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
this.length += data.length;
|
||||
this.roundClean();
|
||||
return this;
|
||||
}
|
||||
digestInto(out: Uint8Array) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
|
||||
throw new Error('_Sha2: Invalid output buffer');
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
this.finished = true;
|
||||
// Padding
|
||||
// We can avoid allocation of buffer for padding completely if it
|
||||
// was previously not allocated here. But it won't change performance.
|
||||
const { buffer, view, blockLen, isLE } = this;
|
||||
let { pos } = this;
|
||||
// append the bit '1' to the message
|
||||
buffer[pos++] = 0b10000000;
|
||||
this.buffer.subarray(pos).fill(0);
|
||||
// we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
|
||||
if (this.padOffset > blockLen - pos) {
|
||||
this.process(view, 0);
|
||||
pos = 0;
|
||||
}
|
||||
// Pad until full block byte with zeros
|
||||
for (let i = pos; i < blockLen; i++) buffer[i] = 0;
|
||||
// NOTE: sha512 requires length to be 128bit integer, but length in JS will overflow before that
|
||||
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
|
||||
// So we just write lowest 64bit of that value.
|
||||
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
||||
this.process(view, 0);
|
||||
const oview = createView(out);
|
||||
this.get().forEach((v, i) => oview.setUint32(4 * i, v, isLE));
|
||||
}
|
||||
digest() {
|
||||
const { buffer, outputLen } = this;
|
||||
this.digestInto(buffer);
|
||||
const res = buffer.slice(0, outputLen);
|
||||
this.destroy();
|
||||
return res;
|
||||
}
|
||||
_cloneInto(to?: T): T {
|
||||
to ||= new (this.constructor as any)() as T;
|
||||
to.set(...this.get());
|
||||
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
||||
to.length = length;
|
||||
to.pos = pos;
|
||||
to.finished = finished;
|
||||
to.destroyed = destroyed;
|
||||
if (length % blockLen) to.buffer.set(buffer);
|
||||
return to;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
const U32_MASK64 = BigInt(2 ** 32 - 1);
|
||||
const _32n = BigInt(32);
|
||||
|
||||
export function fromBig(n: bigint, le = false) {
|
||||
if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
|
||||
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
||||
}
|
||||
|
||||
export function split(lst: bigint[], le = false) {
|
||||
let Ah = new Uint32Array(lst.length);
|
||||
let Al = new Uint32Array(lst.length);
|
||||
for (let i = 0; i < lst.length; i++) {
|
||||
const { h, l } = fromBig(lst[i], le);
|
||||
[Ah[i], Al[i]] = [h, l];
|
||||
}
|
||||
return [Ah, Al];
|
||||
}
|
||||
|
||||
export const toBig = (h: number, l: number) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);
|
||||
// for Shift in [0, 32)
|
||||
export const shrSH = (h: number, l: number, s: number) => h >>> s;
|
||||
export const shrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
|
||||
// Right rotate for Shift in [1, 32)
|
||||
export const rotrSH = (h: number, l: number, s: number) => (h >>> s) | (l << (32 - s));
|
||||
export const rotrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
|
||||
// Right rotate for Shift in (32, 64), NOTE: 32 is special case.
|
||||
export const rotrBH = (h: number, l: number, s: number) => (h << (64 - s)) | (l >>> (s - 32));
|
||||
export const rotrBL = (h: number, l: number, s: number) => (h >>> (s - 32)) | (l << (64 - s));
|
||||
// Right rotate for shift===32 (just swaps l&h)
|
||||
export const rotr32H = (h: number, l: number) => l;
|
||||
export const rotr32L = (h: number, l: number) => h;
|
||||
// Left rotate for Shift in [1, 32)
|
||||
export const rotlSH = (h: number, l: number, s: number) => (h << s) | (l >>> (32 - s));
|
||||
export const rotlSL = (h: number, l: number, s: number) => (l << s) | (h >>> (32 - s));
|
||||
// Left rotate for Shift in (32, 64), NOTE: 32 is special case.
|
||||
export const rotlBH = (h: number, l: number, s: number) => (l << (s - 32)) | (h >>> (64 - s));
|
||||
export const rotlBL = (h: number, l: number, s: number) => (h << (s - 32)) | (l >>> (64 - s));
|
||||
|
||||
// JS uses 32-bit signed integers for bitwise operations which means we cannot
|
||||
// simple take carry out of low bit sum by shift, we need to use division.
|
||||
export function add(Ah: number, Al: number, Bh: number, Bl: number) {
|
||||
const l = (Al >>> 0) + (Bl >>> 0);
|
||||
return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };
|
||||
}
|
||||
// Addition with more than 2 elements
|
||||
export const add3L = (Al: number, Bl: number, Cl: number) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
|
||||
export const add3H = (low: number, Ah: number, Bh: number, Ch: number) =>
|
||||
(Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;
|
||||
export const add4L = (Al: number, Bl: number, Cl: number, Dl: number) =>
|
||||
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
|
||||
export const add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number) =>
|
||||
(Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;
|
||||
export const add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number) =>
|
||||
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
|
||||
export const add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) =>
|
||||
(Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;
|
||||
@@ -0,0 +1,200 @@
|
||||
import * as blake2 from './_blake2';
|
||||
import * as u64 from './_u64';
|
||||
import { toBytes, u32, wrapConstructorWithOpts } from './utils';
|
||||
|
||||
// Same as SHA-512 but LE
|
||||
// prettier-ignore
|
||||
const IV = new Uint32Array([
|
||||
0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
|
||||
0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19
|
||||
]);
|
||||
// Temporary buffer
|
||||
const BUF = new Uint32Array(32);
|
||||
|
||||
// Mixing function G splitted in two halfs
|
||||
function G1(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
|
||||
// NOTE: V is LE here
|
||||
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
|
||||
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
|
||||
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
|
||||
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
|
||||
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
|
||||
// v[a] = (v[a] + v[b] + x) | 0;
|
||||
let ll = u64.add3L(Al, Bl, Xl);
|
||||
Ah = u64.add3H(ll, Ah, Bh, Xh);
|
||||
Al = ll | 0;
|
||||
// v[d] = rotr(v[d] ^ v[a], 32)
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });
|
||||
// v[c] = (v[c] + v[d]) | 0;
|
||||
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
|
||||
// v[b] = rotr(v[b] ^ v[c], 24)
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) });
|
||||
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
|
||||
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
|
||||
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
|
||||
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
|
||||
}
|
||||
|
||||
function G2(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
|
||||
// NOTE: V is LE here
|
||||
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
|
||||
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
|
||||
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
|
||||
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
|
||||
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
|
||||
// v[a] = (v[a] + v[b] + x) | 0;
|
||||
let ll = u64.add3L(Al, Bl, Xl);
|
||||
Ah = u64.add3H(ll, Ah, Bh, Xh);
|
||||
Al = ll | 0;
|
||||
// v[d] = rotr(v[d] ^ v[a], 16)
|
||||
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
|
||||
({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });
|
||||
// v[c] = (v[c] + v[d]) | 0;
|
||||
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
|
||||
// v[b] = rotr(v[b] ^ v[c], 63)
|
||||
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
|
||||
({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) });
|
||||
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
|
||||
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
|
||||
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
|
||||
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
|
||||
}
|
||||
|
||||
class BLAKE2b extends blake2.BLAKE2<BLAKE2b> {
|
||||
// Same as SHA-512, but LE
|
||||
private v0l = IV[0] | 0;
|
||||
private v0h = IV[1] | 0;
|
||||
private v1l = IV[2] | 0;
|
||||
private v1h = IV[3] | 0;
|
||||
private v2l = IV[4] | 0;
|
||||
private v2h = IV[5] | 0;
|
||||
private v3l = IV[6] | 0;
|
||||
private v3h = IV[7] | 0;
|
||||
private v4l = IV[8] | 0;
|
||||
private v4h = IV[9] | 0;
|
||||
private v5l = IV[10] | 0;
|
||||
private v5h = IV[11] | 0;
|
||||
private v6l = IV[12] | 0;
|
||||
private v6h = IV[13] | 0;
|
||||
private v7l = IV[14] | 0;
|
||||
private v7h = IV[15] | 0;
|
||||
|
||||
constructor(opts: blake2.BlakeOpts = {}) {
|
||||
super(128, opts.dkLen === undefined ? 64 : opts.dkLen, opts, 64, 16, 16);
|
||||
const keyLength = opts.key ? opts.key.length : 0;
|
||||
this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
|
||||
if (opts.salt) {
|
||||
const salt = u32(toBytes(opts.salt));
|
||||
this.v4l ^= salt[0];
|
||||
this.v4h ^= salt[1];
|
||||
this.v5l ^= salt[2];
|
||||
this.v5h ^= salt[3];
|
||||
}
|
||||
if (opts.personalization) {
|
||||
const pers = u32(toBytes(opts.personalization));
|
||||
this.v6l ^= pers[0];
|
||||
this.v6h ^= pers[1];
|
||||
this.v7l ^= pers[2];
|
||||
this.v7h ^= pers[3];
|
||||
}
|
||||
if (opts.key) {
|
||||
// Pad to blockLen and update
|
||||
const tmp = new Uint8Array(this.blockLen);
|
||||
tmp.set(toBytes(opts.key));
|
||||
this.update(tmp);
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
protected get(): [
|
||||
number, number, number, number, number, number, number, number,
|
||||
number, number, number, number, number, number, number, number
|
||||
] {
|
||||
let {v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h} = this;
|
||||
return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
v0l: number, v0h: number, v1l: number, v1h: number,
|
||||
v2l: number, v2h: number, v3l: number, v3h: number,
|
||||
v4l: number, v4h: number, v5l: number, v5h: number,
|
||||
v6l: number, v6h: number, v7l: number, v7h: number
|
||||
) {
|
||||
this.v0l = v0l | 0;
|
||||
this.v0h = v0h | 0;
|
||||
this.v1l = v1l | 0;
|
||||
this.v1h = v1h | 0;
|
||||
this.v2l = v2l | 0;
|
||||
this.v2h = v2h | 0;
|
||||
this.v3l = v3l | 0;
|
||||
this.v3h = v3h | 0;
|
||||
this.v4l = v4l | 0;
|
||||
this.v4h = v4h | 0;
|
||||
this.v5l = v5l | 0;
|
||||
this.v5h = v5h | 0;
|
||||
this.v6l = v6l | 0;
|
||||
this.v6h = v6h | 0;
|
||||
this.v7l = v7l | 0;
|
||||
this.v7h = v7h | 0;
|
||||
}
|
||||
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
|
||||
this.get().forEach((v, i) => (BUF[i] = v)); // First half from state.
|
||||
BUF.set(IV, 16); // Second half from IV.
|
||||
let { h, l } = u64.fromBig(BigInt(this.length));
|
||||
BUF[24] = IV[8] ^ l; // Low word of the offset.
|
||||
BUF[25] = IV[9] ^ h; // High word.
|
||||
// Invert all bits for last block
|
||||
if (isLast) {
|
||||
BUF[28] = ~BUF[28];
|
||||
BUF[29] = ~BUF[29];
|
||||
}
|
||||
let j = 0;
|
||||
const s = blake2.SIGMA;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
G1(0, 4, 8, 12, msg, offset + 2 * s[j++]);
|
||||
G2(0, 4, 8, 12, msg, offset + 2 * s[j++]);
|
||||
G1(1, 5, 9, 13, msg, offset + 2 * s[j++]);
|
||||
G2(1, 5, 9, 13, msg, offset + 2 * s[j++]);
|
||||
G1(2, 6, 10, 14, msg, offset + 2 * s[j++]);
|
||||
G2(2, 6, 10, 14, msg, offset + 2 * s[j++]);
|
||||
G1(3, 7, 11, 15, msg, offset + 2 * s[j++]);
|
||||
G2(3, 7, 11, 15, msg, offset + 2 * s[j++]);
|
||||
|
||||
G1(0, 5, 10, 15, msg, offset + 2 * s[j++]);
|
||||
G2(0, 5, 10, 15, msg, offset + 2 * s[j++]);
|
||||
G1(1, 6, 11, 12, msg, offset + 2 * s[j++]);
|
||||
G2(1, 6, 11, 12, msg, offset + 2 * s[j++]);
|
||||
G1(2, 7, 8, 13, msg, offset + 2 * s[j++]);
|
||||
G2(2, 7, 8, 13, msg, offset + 2 * s[j++]);
|
||||
G1(3, 4, 9, 14, msg, offset + 2 * s[j++]);
|
||||
G2(3, 4, 9, 14, msg, offset + 2 * s[j++]);
|
||||
}
|
||||
this.v0l ^= BUF[0] ^ BUF[16];
|
||||
this.v0h ^= BUF[1] ^ BUF[17];
|
||||
this.v1l ^= BUF[2] ^ BUF[18];
|
||||
this.v1h ^= BUF[3] ^ BUF[19];
|
||||
this.v2l ^= BUF[4] ^ BUF[20];
|
||||
this.v2h ^= BUF[5] ^ BUF[21];
|
||||
this.v3l ^= BUF[6] ^ BUF[22];
|
||||
this.v3h ^= BUF[7] ^ BUF[23];
|
||||
this.v4l ^= BUF[8] ^ BUF[24];
|
||||
this.v4h ^= BUF[9] ^ BUF[25];
|
||||
this.v5l ^= BUF[10] ^ BUF[26];
|
||||
this.v5h ^= BUF[11] ^ BUF[27];
|
||||
this.v6l ^= BUF[12] ^ BUF[28];
|
||||
this.v6h ^= BUF[13] ^ BUF[29];
|
||||
this.v7l ^= BUF[14] ^ BUF[30];
|
||||
this.v7h ^= BUF[15] ^ BUF[31];
|
||||
BUF.fill(0);
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.buffer32.fill(0);
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export const blake2b = wrapConstructorWithOpts<BLAKE2b, blake2.BlakeOpts>(
|
||||
(opts) => new BLAKE2b(opts)
|
||||
);
|
||||
@@ -0,0 +1,133 @@
|
||||
import * as u64 from './_u64';
|
||||
import * as blake2 from './_blake2';
|
||||
import { rotr, toBytes, wrapConstructorWithOpts, u32 } from './utils';
|
||||
|
||||
// Initial state:
|
||||
// first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19)
|
||||
// same as SHA-256
|
||||
// prettier-ignore
|
||||
export const IV = new Uint32Array([
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
]);
|
||||
|
||||
// Mixing function G splitted in two halfs
|
||||
function G1(a: number, b: number, c: number, d: number, x: number) {
|
||||
a = (a + b + x) | 0;
|
||||
d = rotr(d ^ a, 16);
|
||||
c = (c + d) | 0;
|
||||
b = rotr(b ^ c, 12);
|
||||
return { a, b, c, d };
|
||||
}
|
||||
|
||||
function G2(a: number, b: number, c: number, d: number, x: number) {
|
||||
a = (a + b + x) | 0;
|
||||
d = rotr(d ^ a, 8);
|
||||
c = (c + d) | 0;
|
||||
b = rotr(b ^ c, 7);
|
||||
return { a, b, c, d };
|
||||
}
|
||||
// prettier-ignore
|
||||
export function compress(s: Uint8Array, offset: number, msg: Uint32Array, rounds: number,
|
||||
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number,
|
||||
v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,
|
||||
) {
|
||||
let j = 0;
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
({ a: v0, b: v4, c: v8, d: v12 } = G1(v0, v4, v8, v12, msg[offset + s[j++]]));
|
||||
({ a: v0, b: v4, c: v8, d: v12 } = G2(v0, v4, v8, v12, msg[offset + s[j++]]));
|
||||
({ a: v1, b: v5, c: v9, d: v13 } = G1(v1, v5, v9, v13, msg[offset + s[j++]]));
|
||||
({ a: v1, b: v5, c: v9, d: v13 } = G2(v1, v5, v9, v13, msg[offset + s[j++]]));
|
||||
({ a: v2, b: v6, c: v10, d: v14 } = G1(v2, v6, v10, v14, msg[offset + s[j++]]));
|
||||
({ a: v2, b: v6, c: v10, d: v14 } = G2(v2, v6, v10, v14, msg[offset + s[j++]]));
|
||||
({ a: v3, b: v7, c: v11, d: v15 } = G1(v3, v7, v11, v15, msg[offset + s[j++]]));
|
||||
({ a: v3, b: v7, c: v11, d: v15 } = G2(v3, v7, v11, v15, msg[offset + s[j++]]));
|
||||
|
||||
({ a: v0, b: v5, c: v10, d: v15 } = G1(v0, v5, v10, v15, msg[offset + s[j++]]));
|
||||
({ a: v0, b: v5, c: v10, d: v15 } = G2(v0, v5, v10, v15, msg[offset + s[j++]]));
|
||||
({ a: v1, b: v6, c: v11, d: v12 } = G1(v1, v6, v11, v12, msg[offset + s[j++]]));
|
||||
({ a: v1, b: v6, c: v11, d: v12 } = G2(v1, v6, v11, v12, msg[offset + s[j++]]));
|
||||
({ a: v2, b: v7, c: v8, d: v13 } = G1(v2, v7, v8, v13, msg[offset + s[j++]]));
|
||||
({ a: v2, b: v7, c: v8, d: v13 } = G2(v2, v7, v8, v13, msg[offset + s[j++]]));
|
||||
({ a: v3, b: v4, c: v9, d: v14 } = G1(v3, v4, v9, v14, msg[offset + s[j++]]));
|
||||
({ a: v3, b: v4, c: v9, d: v14 } = G2(v3, v4, v9, v14, msg[offset + s[j++]]));
|
||||
}
|
||||
return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };
|
||||
}
|
||||
|
||||
class BLAKE2s extends blake2.BLAKE2<BLAKE2s> {
|
||||
// Internal state, same as SHA-256
|
||||
private v0 = IV[0] | 0;
|
||||
private v1 = IV[1] | 0;
|
||||
private v2 = IV[2] | 0;
|
||||
private v3 = IV[3] | 0;
|
||||
private v4 = IV[4] | 0;
|
||||
private v5 = IV[5] | 0;
|
||||
private v6 = IV[6] | 0;
|
||||
private v7 = IV[7] | 0;
|
||||
|
||||
constructor(opts: blake2.BlakeOpts = {}) {
|
||||
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, opts, 32, 8, 8);
|
||||
const keyLength = opts.key ? opts.key.length : 0;
|
||||
this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
|
||||
if (opts.salt) {
|
||||
const salt = u32(toBytes(opts.salt));
|
||||
this.v4 ^= salt[0];
|
||||
this.v5 ^= salt[1];
|
||||
}
|
||||
if (opts.personalization) {
|
||||
const pers = u32(toBytes(opts.personalization));
|
||||
this.v6 ^= pers[0];
|
||||
this.v7 ^= pers[1];
|
||||
}
|
||||
if (opts.key) {
|
||||
// Pad to blockLen and update
|
||||
const tmp = new Uint8Array(this.blockLen);
|
||||
tmp.set(toBytes(opts.key));
|
||||
this.update(tmp);
|
||||
}
|
||||
}
|
||||
protected get(): [number, number, number, number, number, number, number, number] {
|
||||
const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;
|
||||
return [v0, v1, v2, v3, v4, v5, v6, v7];
|
||||
}
|
||||
// prettier-ignore
|
||||
protected set(
|
||||
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number
|
||||
) {
|
||||
this.v0 = v0 | 0;
|
||||
this.v1 = v1 | 0;
|
||||
this.v2 = v2 | 0;
|
||||
this.v3 = v3 | 0;
|
||||
this.v4 = v4 | 0;
|
||||
this.v5 = v5 | 0;
|
||||
this.v6 = v6 | 0;
|
||||
this.v7 = v7 | 0;
|
||||
}
|
||||
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
|
||||
const { h, l } = u64.fromBig(BigInt(this.length));
|
||||
// prettier-ignore
|
||||
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
|
||||
compress(
|
||||
blake2.SIGMA, offset, msg, 10,
|
||||
this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7,
|
||||
IV[0], IV[1], IV[2], IV[3], l ^ IV[4], h ^ IV[5], isLast ? ~IV[6] : IV[6], IV[7]
|
||||
);
|
||||
this.v0 ^= v0 ^ v8;
|
||||
this.v1 ^= v1 ^ v9;
|
||||
this.v2 ^= v2 ^ v10;
|
||||
this.v3 ^= v3 ^ v11;
|
||||
this.v4 ^= v4 ^ v12;
|
||||
this.v5 ^= v5 ^ v13;
|
||||
this.v6 ^= v6 ^ v14;
|
||||
this.v7 ^= v7 ^ v15;
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.buffer32.fill(0);
|
||||
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export const blake2s = wrapConstructorWithOpts<BLAKE2s, blake2.BlakeOpts>(
|
||||
(opts) => new BLAKE2s(opts)
|
||||
);
|
||||
@@ -0,0 +1,240 @@
|
||||
import * as u64 from './_u64';
|
||||
import * as blake2 from './_blake2';
|
||||
import * as blake2s from './blake2s';
|
||||
import { Input, u8, u32, toBytes, wrapConstructorWithOpts, assertNumber, HashXOF } from './utils';
|
||||
|
||||
// Flag bitset
|
||||
enum Flags {
|
||||
CHUNK_START = 1 << 0,
|
||||
CHUNK_END = 1 << 1,
|
||||
PARENT = 1 << 2,
|
||||
ROOT = 1 << 3,
|
||||
KEYED_HASH = 1 << 4,
|
||||
DERIVE_KEY_CONTEXT = 1 << 5,
|
||||
DERIVE_KEY_MATERIAL = 1 << 6,
|
||||
}
|
||||
|
||||
const SIGMA: Uint8Array = (() => {
|
||||
const Id = Array.from({ length: 16 }, (_, i) => i);
|
||||
const permute = (arr: number[]) =>
|
||||
[2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);
|
||||
const res: number[] = [];
|
||||
for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v);
|
||||
return Uint8Array.from(res);
|
||||
})();
|
||||
|
||||
// - key: is 256-bit key
|
||||
// - context: string should be hardcoded, globally unique, and application - specific.
|
||||
// A good default format for the context string is "[application] [commit timestamp] [purpose]"
|
||||
// - Only one of 'key' (keyed mode) or 'context' (derive key mode) can be used at same time
|
||||
export type Blake3Opts = { dkLen?: number; key?: Input; context?: Input };
|
||||
|
||||
// Why is this so slow? It should be 6x faster than blake2b.
|
||||
// - There is only 30% reduction in number of rounds from blake2s
|
||||
// - This function uses tree mode to achive parallelisation via SIMD and threading,
|
||||
// however in JS we don't have threads and SIMD, so we get only overhead from tree structure
|
||||
// - It is possible to speed it up via Web Workers, hovewer it will make code singnificantly more
|
||||
// complicated, which we are trying to avoid, since this library is intended to be used
|
||||
// for cryptographic purposes. Also, parallelization happens only on chunk level (1024 bytes),
|
||||
// which won't really benefit small inputs.
|
||||
class BLAKE3 extends blake2.BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {
|
||||
private IV: Uint32Array;
|
||||
private flags = 0 | 0;
|
||||
private state: Uint32Array;
|
||||
private chunkPos = 0; // Position of current block in chunk
|
||||
private chunksDone = 0; // How many chunks we already have
|
||||
private stack: Uint32Array[] = [];
|
||||
// Output
|
||||
private posOut = 0;
|
||||
private bufferOut32 = new Uint32Array(16);
|
||||
private bufferOut: Uint8Array;
|
||||
private chunkOut = 0; // index of output chunk
|
||||
private enableXOF = true;
|
||||
|
||||
constructor(opts: Blake3Opts = {}, flags = 0) {
|
||||
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, {}, Number.MAX_SAFE_INTEGER, 0, 0);
|
||||
this.outputLen = opts.dkLen === undefined ? 32 : opts.dkLen;
|
||||
assertNumber(this.outputLen);
|
||||
if (opts.key !== undefined && opts.context !== undefined)
|
||||
throw new Error('Blake3: only key or context can be specified at same time');
|
||||
else if (opts.key !== undefined) {
|
||||
const key = toBytes(opts.key);
|
||||
if (key.length !== 32) throw new Error('Blake3: key should be 32 byte');
|
||||
this.IV = u32(key);
|
||||
this.flags = flags | Flags.KEYED_HASH;
|
||||
} else if (opts.context !== undefined) {
|
||||
const context_key = new BLAKE3({ dkLen: 32 }, Flags.DERIVE_KEY_CONTEXT)
|
||||
.update(opts.context)
|
||||
.digest();
|
||||
this.IV = u32(context_key);
|
||||
this.flags = flags | Flags.DERIVE_KEY_MATERIAL;
|
||||
} else {
|
||||
this.IV = blake2s.IV.slice();
|
||||
this.flags = flags;
|
||||
}
|
||||
this.state = this.IV.slice();
|
||||
this.bufferOut = u8(this.bufferOut32);
|
||||
}
|
||||
// Unused
|
||||
protected get() {
|
||||
return [];
|
||||
}
|
||||
protected set() {}
|
||||
private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) {
|
||||
const { state, pos } = this;
|
||||
const { h, l } = u64.fromBig(BigInt(counter), true);
|
||||
// prettier-ignore
|
||||
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
|
||||
blake2s.compress(
|
||||
SIGMA, bufPos, buf, 7,
|
||||
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
|
||||
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], h, l, pos, flags
|
||||
);
|
||||
state[0] = v0 ^ v8;
|
||||
state[1] = v1 ^ v9;
|
||||
state[2] = v2 ^ v10;
|
||||
state[3] = v3 ^ v11;
|
||||
state[4] = v4 ^ v12;
|
||||
state[5] = v5 ^ v13;
|
||||
state[6] = v6 ^ v14;
|
||||
state[7] = v7 ^ v15;
|
||||
}
|
||||
protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false) {
|
||||
// Compress last block
|
||||
let flags = this.flags;
|
||||
if (!this.chunkPos) flags |= Flags.CHUNK_START;
|
||||
if (this.chunkPos === 15 || isLast) flags |= Flags.CHUNK_END;
|
||||
if (!isLast) this.pos = this.blockLen;
|
||||
this.b2Compress(this.chunksDone, flags, buf, bufPos);
|
||||
this.chunkPos += 1;
|
||||
// If current block is last in chunk (16 blocks), then compress chunks
|
||||
if (this.chunkPos === 16 || isLast) {
|
||||
let chunk = this.state;
|
||||
this.state = this.IV.slice();
|
||||
// If not the last one, compress only when there are trailing zeros in chunk counter
|
||||
// chunks used as binary tree where current stack is path. Zero means current leaf is finished and can be compressed.
|
||||
// 1 (001) - leaf not finished (just push current chunk to stack)
|
||||
// 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back)
|
||||
// 3 (011) - last leaf not finished
|
||||
// 4 (100) - leafs finished at depth=1 and depth=2
|
||||
for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {
|
||||
if (!(last = this.stack.pop())) break;
|
||||
this.buffer32.set(last, 0);
|
||||
this.buffer32.set(chunk, 8);
|
||||
this.pos = this.blockLen;
|
||||
this.b2Compress(0, this.flags | Flags.PARENT, this.buffer32, 0);
|
||||
chunk = this.state;
|
||||
this.state = this.IV.slice();
|
||||
}
|
||||
this.chunksDone++;
|
||||
this.chunkPos = 0;
|
||||
this.stack.push(chunk);
|
||||
}
|
||||
this.pos = 0;
|
||||
}
|
||||
override _cloneInto(to?: BLAKE3): BLAKE3 {
|
||||
to = super._cloneInto(to) as BLAKE3;
|
||||
const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;
|
||||
to.state.set(state.slice());
|
||||
to.stack = stack.map((i) => Uint32Array.from(i));
|
||||
to.IV.set(IV);
|
||||
to.flags = flags;
|
||||
to.chunkPos = chunkPos;
|
||||
to.chunksDone = chunksDone;
|
||||
to.posOut = posOut;
|
||||
to.chunkOut = chunkOut;
|
||||
to.enableXOF = this.enableXOF;
|
||||
to.bufferOut32.set(this.bufferOut32);
|
||||
return to;
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.state.fill(0);
|
||||
this.buffer32.fill(0);
|
||||
this.IV.fill(0);
|
||||
this.bufferOut32.fill(0);
|
||||
for (let i of this.stack) i.fill(0);
|
||||
}
|
||||
// Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)
|
||||
private b2CompressOut() {
|
||||
const { state, pos, flags, buffer32, bufferOut32 } = this;
|
||||
const { h, l } = u64.fromBig(BigInt(this.chunkOut++));
|
||||
// prettier-ignore
|
||||
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
|
||||
blake2s.compress(
|
||||
SIGMA, 0, buffer32, 7,
|
||||
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
|
||||
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], l, h, pos, flags
|
||||
);
|
||||
bufferOut32[0] = v0 ^ v8;
|
||||
bufferOut32[1] = v1 ^ v9;
|
||||
bufferOut32[2] = v2 ^ v10;
|
||||
bufferOut32[3] = v3 ^ v11;
|
||||
bufferOut32[4] = v4 ^ v12;
|
||||
bufferOut32[5] = v5 ^ v13;
|
||||
bufferOut32[6] = v6 ^ v14;
|
||||
bufferOut32[7] = v7 ^ v15;
|
||||
bufferOut32[8] = state[0] ^ v8;
|
||||
bufferOut32[9] = state[1] ^ v9;
|
||||
bufferOut32[10] = state[2] ^ v10;
|
||||
bufferOut32[11] = state[3] ^ v11;
|
||||
bufferOut32[12] = state[4] ^ v12;
|
||||
bufferOut32[13] = state[5] ^ v13;
|
||||
bufferOut32[14] = state[6] ^ v14;
|
||||
bufferOut32[15] = state[7] ^ v15;
|
||||
this.posOut = 0;
|
||||
}
|
||||
protected finish() {
|
||||
if (this.finished) return;
|
||||
this.finished = true;
|
||||
// Padding
|
||||
this.buffer.fill(0, this.pos);
|
||||
// Process last chunk
|
||||
let flags = this.flags | Flags.ROOT;
|
||||
if (this.stack.length) {
|
||||
flags |= Flags.PARENT;
|
||||
this.compress(this.buffer32, 0, true);
|
||||
this.chunksDone = 0;
|
||||
this.pos = this.blockLen;
|
||||
} else {
|
||||
flags |= (!this.chunkPos ? Flags.CHUNK_START : 0) | Flags.CHUNK_END;
|
||||
}
|
||||
this.flags = flags;
|
||||
this.b2CompressOut();
|
||||
}
|
||||
private writeInto(out: Uint8Array) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (!(out instanceof Uint8Array)) throw new Error('Blake3: Invalid output buffer');
|
||||
this.finish();
|
||||
const { blockLen, bufferOut } = this;
|
||||
for (let pos = 0, len = out.length; pos < len; ) {
|
||||
if (this.posOut >= blockLen) this.b2CompressOut();
|
||||
const take = Math.min(this.blockLen - this.posOut, len - pos);
|
||||
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
||||
this.posOut += take;
|
||||
pos += take;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
xofInto(out: Uint8Array): Uint8Array {
|
||||
if (!this.enableXOF) throw new Error('XOF impossible after digest call');
|
||||
return this.writeInto(out);
|
||||
}
|
||||
xof(bytes: number): Uint8Array {
|
||||
assertNumber(bytes);
|
||||
return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
override digestInto(out: Uint8Array) {
|
||||
if (out.length < this.outputLen) throw new Error('Blake3: Invalid output buffer');
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
this.enableXOF = false;
|
||||
this.writeInto(out);
|
||||
this.destroy();
|
||||
return out;
|
||||
}
|
||||
override digest() {
|
||||
return this.digestInto(new Uint8Array(this.outputLen));
|
||||
}
|
||||
}
|
||||
|
||||
export const blake3 = wrapConstructorWithOpts<BLAKE3, Blake3Opts>((opts) => new BLAKE3(opts));
|
||||
@@ -0,0 +1,6 @@
|
||||
import nodeCrypto from 'crypto';
|
||||
|
||||
export const crypto: { node?: any; web?: any } = {
|
||||
node: nodeCrypto,
|
||||
web: undefined,
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
// Global symbol available in browsers only
|
||||
declare const self: Record<string, any> | undefined;
|
||||
export const crypto: { node?: any; web?: any } = {
|
||||
node: undefined,
|
||||
web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined,
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
// prettier-ignore
|
||||
import {
|
||||
assertHash, assertNumber, CHash, Input, toBytes
|
||||
} from './utils';
|
||||
import { hmac } from './hmac';
|
||||
|
||||
// HKDF (RFC 5869)
|
||||
// HKDF-Extract(IKM, salt) -> PRK NOTE: arguments position differs from spec (IKM is first one, since it is not optional)
|
||||
export function hkdf_extract(hash: CHash, ikm: Input, salt?: Input) {
|
||||
assertHash(hash);
|
||||
// NOTE: some libraries treats zero-length array as 'not provided', we don't, since we have undefined as 'not provided'
|
||||
// More info: https://github.com/RustCrypto/KDFs/issues/15
|
||||
if (salt === undefined) salt = new Uint8Array(hash.outputLen); // if not provided, it is set to a string of HashLen zeros
|
||||
return hmac(hash, toBytes(salt), toBytes(ikm));
|
||||
}
|
||||
|
||||
// HKDF-Expand(PRK, info, L) -> OKM
|
||||
const HKDF_COUNTER = new Uint8Array([0]);
|
||||
const EMPTY_BUFFER = new Uint8Array();
|
||||
export function hkdf_expand(
|
||||
hash: CHash,
|
||||
prk: Input, // a pseudorandom key of at least HashLen octets (usually, the output from the extract step)
|
||||
info?: Input, // optional context and application specific information (can be a zero-length string)
|
||||
length: number = 32 // length of output keying material in octets
|
||||
) {
|
||||
assertHash(hash);
|
||||
assertNumber(length);
|
||||
if (length > 255 * hash.outputLen) throw new Error('Length should be <= 255*HashLen');
|
||||
const blocks = Math.ceil(length / hash.outputLen);
|
||||
if (info === undefined) info = EMPTY_BUFFER;
|
||||
// first L(ength) octets of T
|
||||
const okm = new Uint8Array(blocks * hash.outputLen);
|
||||
// Re-use HMAC instance between blocks
|
||||
const HMAC = hmac.init(hash, prk);
|
||||
const HMACTmp = HMAC._cloneInto();
|
||||
const T = new Uint8Array(HMAC.outputLen);
|
||||
for (let counter = 0; counter < blocks; counter++) {
|
||||
HKDF_COUNTER[0] = counter + 1;
|
||||
// T(0) = empty string (zero length)
|
||||
// T(N) = HMAC-Hash(PRK, T(N-1) | info | N)
|
||||
HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)
|
||||
.update(info)
|
||||
.update(HKDF_COUNTER)
|
||||
.digestInto(T);
|
||||
okm.set(T, hash.outputLen * counter);
|
||||
HMAC._cloneInto(HMACTmp);
|
||||
}
|
||||
HMAC.destroy();
|
||||
HMACTmp.destroy();
|
||||
T.fill(0);
|
||||
return okm.slice(0, length);
|
||||
}
|
||||
// Extract+Expand
|
||||
export const hkdf = (
|
||||
hash: CHash,
|
||||
ikm: Input,
|
||||
salt: Input | undefined,
|
||||
info: Input | undefined,
|
||||
length: number
|
||||
) => hkdf_expand(hash, hkdf_extract(hash, ikm, salt), info, length);
|
||||
@@ -0,0 +1,76 @@
|
||||
import { assertHash, Hash, CHash, Input, toBytes } from './utils';
|
||||
// HMAC (RFC 2104)
|
||||
class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {
|
||||
oHash: T;
|
||||
iHash: T;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
private finished = false;
|
||||
private destroyed = false;
|
||||
|
||||
constructor(hash: CHash, _key: Input) {
|
||||
super();
|
||||
assertHash(hash);
|
||||
const key = toBytes(_key);
|
||||
this.iHash = hash.create() as T;
|
||||
if (!(this.iHash instanceof Hash))
|
||||
throw new TypeError('Expected instance of class which extends utils.Hash');
|
||||
const blockLen = (this.blockLen = this.iHash.blockLen);
|
||||
this.outputLen = this.iHash.outputLen;
|
||||
const pad = new Uint8Array(blockLen);
|
||||
// blockLen can be bigger than outputLen
|
||||
pad.set(key.length > this.iHash.blockLen ? hash.create().update(key).digest() : key);
|
||||
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;
|
||||
this.iHash.update(pad);
|
||||
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
||||
this.oHash = hash.create() as T;
|
||||
// Undo internal XOR && apply outer XOR
|
||||
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;
|
||||
this.oHash.update(pad);
|
||||
pad.fill(0);
|
||||
}
|
||||
update(buf: Input) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
this.iHash.update(buf);
|
||||
return this;
|
||||
}
|
||||
digestInto(out: Uint8Array) {
|
||||
if (this.destroyed) throw new Error('instance is destroyed');
|
||||
if (!(out instanceof Uint8Array) || out.length !== this.outputLen)
|
||||
throw new Error('HMAC: Invalid output buffer');
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
this.finished = true;
|
||||
this.iHash.digestInto(out);
|
||||
this.oHash.update(out);
|
||||
this.oHash.digestInto(out);
|
||||
this.destroy();
|
||||
}
|
||||
digest() {
|
||||
const out = new Uint8Array(this.oHash.outputLen);
|
||||
this.digestInto(out);
|
||||
return out;
|
||||
}
|
||||
_cloneInto(to?: HMAC<T>): HMAC<T> {
|
||||
// Create new instance without calling constructor since key already in state and we don't know it.
|
||||
to ||= Object.create(Object.getPrototypeOf(this), {});
|
||||
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
||||
to = to as this;
|
||||
to.finished = finished;
|
||||
to.destroyed = destroyed;
|
||||
to.blockLen = blockLen;
|
||||
to.outputLen = outputLen;
|
||||
to.oHash = oHash._cloneInto(to.oHash);
|
||||
to.iHash = iHash._cloneInto(to.iHash);
|
||||
return to;
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.oHash.destroy();
|
||||
this.iHash.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export const hmac = (hash: CHash, key: Input, message: Input): Uint8Array =>
|
||||
new HMAC<any>(hash, key).update(message).digest();
|
||||
hmac.create = (hash: CHash, key: Input) => new HMAC<any>(hash, key);
|
||||
hmac.init = hmac.create;
|
||||
@@ -0,0 +1,3 @@
|
||||
throw new Error(
|
||||
'noble-hashes have no entry-point. Please consult the README.md to learn how to use them'
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/x-noble-hashes authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/x-noble-hashes', version: '8.1.1' };
|
||||
@@ -0,0 +1,92 @@
|
||||
import { hmac } from './hmac';
|
||||
// prettier-ignore
|
||||
import {
|
||||
Hash, CHash, Input, createView, toBytes, assertNumber, assertHash, checkOpts, asyncLoop
|
||||
} from './utils';
|
||||
|
||||
// PBKDF (RFC 2898)
|
||||
export type Pbkdf2Opt = {
|
||||
c: number; // Iterations
|
||||
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
|
||||
asyncTick?: number; // Maximum time in ms for which async function can block execution
|
||||
};
|
||||
// Common prologue and epilogue for sync/async functions
|
||||
function pbkdf2Init(hash: CHash, _password: Input, _salt: Input, _opts: Pbkdf2Opt) {
|
||||
assertHash(hash);
|
||||
const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
|
||||
const { c, dkLen, asyncTick } = opts;
|
||||
assertNumber(c);
|
||||
assertNumber(dkLen);
|
||||
assertNumber(asyncTick);
|
||||
if (c < 1) throw new Error('PBKDF2: iterations (c) should be >= 1');
|
||||
const password = toBytes(_password);
|
||||
const salt = toBytes(_salt);
|
||||
// DK = PBKDF2(PRF, Password, Salt, c, dkLen);
|
||||
const DK = new Uint8Array(dkLen);
|
||||
// U1 = PRF(Password, Salt + INT_32_BE(i))
|
||||
const PRF = hmac.init(hash, password);
|
||||
const PRFSalt = PRF._cloneInto().update(salt);
|
||||
return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
|
||||
}
|
||||
|
||||
function pbkdf2Output<T extends Hash<T>>(
|
||||
PRF: Hash<T>,
|
||||
PRFSalt: Hash<T>,
|
||||
DK: Uint8Array,
|
||||
prfW: Hash<T>,
|
||||
u: Uint8Array
|
||||
) {
|
||||
PRF.destroy();
|
||||
PRFSalt.destroy();
|
||||
if (prfW) prfW.destroy();
|
||||
u.fill(0);
|
||||
return DK;
|
||||
}
|
||||
|
||||
export function pbkdf2(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
|
||||
const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
|
||||
let prfW: any; // Working copy
|
||||
const arr = new Uint8Array(4);
|
||||
const view = createView(arr);
|
||||
const u = new Uint8Array(PRF.outputLen);
|
||||
// DK = T1 + T2 + ⋯ + Tdklen/hlen
|
||||
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
|
||||
// Ti = F(Password, Salt, c, i)
|
||||
const Ti = DK.subarray(pos, pos + PRF.outputLen);
|
||||
view.setInt32(0, ti, false);
|
||||
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
|
||||
// U1 = PRF(Password, Salt + INT_32_BE(i))
|
||||
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
|
||||
Ti.set(u.subarray(0, Ti.length));
|
||||
for (let ui = 1; ui < c; ui++) {
|
||||
// Uc = PRF(Password, Uc−1)
|
||||
PRF._cloneInto(prfW).update(u).digestInto(u);
|
||||
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
|
||||
}
|
||||
}
|
||||
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
|
||||
}
|
||||
|
||||
export async function pbkdf2Async(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
|
||||
const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
|
||||
let prfW: any; // Working copy
|
||||
const arr = new Uint8Array(4);
|
||||
const view = createView(arr);
|
||||
const u = new Uint8Array(PRF.outputLen);
|
||||
// DK = T1 + T2 + ⋯ + Tdklen/hlen
|
||||
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
|
||||
// Ti = F(Password, Salt, c, i)
|
||||
const Ti = DK.subarray(pos, pos + PRF.outputLen);
|
||||
view.setInt32(0, ti, false);
|
||||
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
|
||||
// U1 = PRF(Password, Salt + INT_32_BE(i))
|
||||
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
|
||||
Ti.set(u.subarray(0, Ti.length));
|
||||
await asyncLoop(c - 1, asyncTick, (i) => {
|
||||
// Uc = PRF(Password, Uc−1)
|
||||
PRF._cloneInto(prfW).update(u).digestInto(u);
|
||||
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
|
||||
});
|
||||
}
|
||||
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { SHA2 } from './_sha2';
|
||||
|
||||
import { wrapConstructor } from './utils';
|
||||
|
||||
// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
|
||||
// https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf
|
||||
const Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]);
|
||||
const Id = Uint8Array.from({ length: 16 }, (_, i) => i);
|
||||
const Pi = Id.map((i) => (9 * i + 5) % 16);
|
||||
let idxL = [Id];
|
||||
let idxR = [Pi];
|
||||
for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k]));
|
||||
|
||||
const shifts = [
|
||||
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
|
||||
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
|
||||
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
|
||||
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
|
||||
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5],
|
||||
].map((i) => new Uint8Array(i));
|
||||
|
||||
const shiftsL = idxL.map((idx, i) => idx.map((j) => shifts[i][j]));
|
||||
const shiftsR = idxR.map((idx, i) => idx.map((j) => shifts[i][j]));
|
||||
|
||||
const Kl = new Uint32Array([0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]);
|
||||
const Kr = new Uint32Array([0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]);
|
||||
// The rotate left (circular left shift) operation for uint32
|
||||
const rotl = (word: number, shift: number) => (word << shift) | (word >>> (32 - shift));
|
||||
// It's called f() in spec.
|
||||
function f(group: number, x: number, y: number, z: number): number {
|
||||
if (group === 0) return x ^ y ^ z;
|
||||
else if (group === 1) return (x & y) | (~x & z);
|
||||
else if (group === 2) return (x | ~y) ^ z;
|
||||
else if (group === 3) return (x & z) | (y & ~z);
|
||||
else return x ^ (y | ~z);
|
||||
}
|
||||
// Temporary buffer, not used to store anything between runs
|
||||
const BUF = new Uint32Array(16);
|
||||
export class RIPEMD160 extends SHA2<RIPEMD160> {
|
||||
private h0 = 0x67452301 | 0;
|
||||
private h1 = 0xefcdab89 | 0;
|
||||
private h2 = 0x98badcfe | 0;
|
||||
private h3 = 0x10325476 | 0;
|
||||
private h4 = 0xc3d2e1f0 | 0;
|
||||
|
||||
constructor() {
|
||||
super(64, 20, 8, true);
|
||||
}
|
||||
protected get(): [number, number, number, number, number] {
|
||||
const { h0, h1, h2, h3, h4 } = this;
|
||||
return [h0, h1, h2, h3, h4];
|
||||
}
|
||||
protected set(h0: number, h1: number, h2: number, h3: number, h4: number) {
|
||||
this.h0 = h0 | 0;
|
||||
this.h1 = h1 | 0;
|
||||
this.h2 = h2 | 0;
|
||||
this.h3 = h3 | 0;
|
||||
this.h4 = h4 | 0;
|
||||
}
|
||||
protected process(view: DataView, offset: number): void {
|
||||
for (let i = 0; i < 16; i++, offset += 4) BUF[i] = view.getUint32(offset, true);
|
||||
// prettier-ignore
|
||||
let al = this.h0 | 0, ar = al,
|
||||
bl = this.h1 | 0, br = bl,
|
||||
cl = this.h2 | 0, cr = cl,
|
||||
dl = this.h3 | 0, dr = dl,
|
||||
el = this.h4 | 0, er = el;
|
||||
|
||||
// Instead of iterating 0 to 80, we split it into 5 groups
|
||||
// And use the groups in constants, functions, etc. Much simpler
|
||||
for (let group = 0; group < 5; group++) {
|
||||
const rGroup = 4 - group;
|
||||
const hbl = Kl[group], hbr = Kr[group]; // prettier-ignore
|
||||
const rl = idxL[group], rr = idxR[group]; // prettier-ignore
|
||||
const sl = shiftsL[group], sr = shiftsR[group]; // prettier-ignore
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const tl = (rotl(al + f(group, bl, cl, dl) + BUF[rl[i]] + hbl, sl[i]) + el) | 0;
|
||||
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore
|
||||
}
|
||||
// 2 loops are 10% faster
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const tr = (rotl(ar + f(rGroup, br, cr, dr) + BUF[rr[i]] + hbr, sr[i]) + er) | 0;
|
||||
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore
|
||||
}
|
||||
}
|
||||
// Add the compressed chunk to the current hash value
|
||||
this.set(
|
||||
(this.h1 + cl + dr) | 0,
|
||||
(this.h2 + dl + er) | 0,
|
||||
(this.h3 + el + ar) | 0,
|
||||
(this.h4 + al + br) | 0,
|
||||
(this.h0 + bl + cr) | 0
|
||||
);
|
||||
}
|
||||
protected roundClean() {
|
||||
BUF.fill(0);
|
||||
}
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
this.buffer.fill(0);
|
||||
this.set(0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export const ripemd160 = wrapConstructor(() => new RIPEMD160());
|
||||
@@ -0,0 +1,219 @@
|
||||
import { sha256 } from './sha256';
|
||||
import { pbkdf2 } from './pbkdf2';
|
||||
import { assertNumber, asyncLoop, checkOpts, Input, u32 } from './utils';
|
||||
|
||||
// Left rotate for uint32
|
||||
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
|
||||
|
||||
// prettier-ignore
|
||||
function XorAndSalsa(
|
||||
prev: Uint32Array,
|
||||
pi: number,
|
||||
input: Uint32Array,
|
||||
ii: number,
|
||||
out: Uint32Array,
|
||||
oi: number
|
||||
) {
|
||||
// Based on https://cr.yp.to/salsa20.html
|
||||
// Xor blocks
|
||||
let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
|
||||
let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
|
||||
let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
|
||||
let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
|
||||
let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
|
||||
let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
|
||||
let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
|
||||
let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
|
||||
// Save state to temporary variables (salsa)
|
||||
let x00 = y00, x01 = y01, x02 = y02, x03 = y03,
|
||||
x04 = y04, x05 = y05, x06 = y06, x07 = y07,
|
||||
x08 = y08, x09 = y09, x10 = y10, x11 = y11,
|
||||
x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
||||
// Main loop (salsa)
|
||||
for (let i = 0; i < 8; i += 2) {
|
||||
x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9);
|
||||
x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18);
|
||||
x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9);
|
||||
x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18);
|
||||
x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9);
|
||||
x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18);
|
||||
x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9);
|
||||
x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18);
|
||||
x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9);
|
||||
x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18);
|
||||
x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9);
|
||||
x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18);
|
||||
x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9);
|
||||
x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18);
|
||||
x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9);
|
||||
x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18);
|
||||
}
|
||||
// Write output (salsa)
|
||||
out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;
|
||||
out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;
|
||||
out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;
|
||||
out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;
|
||||
out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;
|
||||
out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;
|
||||
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
|
||||
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
|
||||
}
|
||||
|
||||
function BlockMix(input: Uint32Array, ii: number, out: Uint32Array, oi: number, r: number) {
|
||||
// The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks)
|
||||
let head = oi + 0;
|
||||
let tail = oi + 16 * r;
|
||||
for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1]
|
||||
for (let i = 0; i < r; i++, head += 16, ii += 16) {
|
||||
// We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1
|
||||
XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1])
|
||||
if (i > 0) tail += 16; // First iteration overwrites tmp value in tail
|
||||
XorAndSalsa(out, head, input, (ii += 16), out, tail); // tail[i] = Salsa(blockIn[2*i+1] ^ head[i])
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 7914
|
||||
export type ScryptOpts = {
|
||||
N: number; // costFactor CPU/memory cost parameter - Must be a power of 2 (e.g. 1024)
|
||||
r: number; // blocksize parameter, which fine-tunes sequential memory read size and performance. (8 is commonly used)
|
||||
p: number; // Parallelization parameter. (1 .. 232-1 * hLen/MFlen)
|
||||
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
|
||||
asyncTick?: number; // Maximum time in ms for which async function can block execution
|
||||
maxmem?: number;
|
||||
onProgress?: (progress: number) => void;
|
||||
};
|
||||
|
||||
// Common prologue and epilogue for sync/async functions
|
||||
function scryptInit(password: Input, salt: Input, _opts?: ScryptOpts) {
|
||||
// Maxmem - 1GB+1KB by default
|
||||
const opts = checkOpts(
|
||||
{
|
||||
dkLen: 32,
|
||||
asyncTick: 10,
|
||||
maxmem: 1024 ** 3 + 1024,
|
||||
},
|
||||
_opts
|
||||
);
|
||||
const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
|
||||
assertNumber(N);
|
||||
assertNumber(r);
|
||||
assertNumber(p);
|
||||
assertNumber(dkLen);
|
||||
assertNumber(asyncTick);
|
||||
assertNumber(maxmem);
|
||||
if (onProgress !== undefined && typeof onProgress !== 'function')
|
||||
throw new Error('progressCb should be function');
|
||||
const blockSize = 128 * r;
|
||||
const blockSize32 = blockSize / 4;
|
||||
if (N <= 1 || (N & (N - 1)) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) {
|
||||
// NOTE: we limit N to be less than 2**32 because of 32 bit variant of Integrify function
|
||||
// There is no JS engines that allows alocate more than 4GB per single Uint8Array for now, but can change in future.
|
||||
throw new Error(
|
||||
'Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32'
|
||||
);
|
||||
}
|
||||
if (p < 0 || p > ((2 ** 32 - 1) * 32) / blockSize) {
|
||||
throw new Error(
|
||||
'Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)'
|
||||
);
|
||||
}
|
||||
if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) {
|
||||
throw new Error(
|
||||
'Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32'
|
||||
);
|
||||
}
|
||||
const memUsed = blockSize * (N + p);
|
||||
if (memUsed > maxmem) {
|
||||
throw new Error(
|
||||
`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`
|
||||
);
|
||||
}
|
||||
// [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor)
|
||||
// Since it has only one iteration there is no reason to use async variant
|
||||
const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p });
|
||||
const B32 = u32(B);
|
||||
// Re-used between parallel iterations. Array(iterations) of B
|
||||
const V = u32(new Uint8Array(blockSize * N));
|
||||
const tmp = u32(new Uint8Array(blockSize));
|
||||
let blockMixCb = () => {};
|
||||
if (onProgress) {
|
||||
const totalBlockMix = 2 * N * p;
|
||||
// Invoke callback if progress changes from 10.01 to 10.02
|
||||
// Allows to draw smooth progress bar on up to 8K screen
|
||||
const callbackPer = Math.max(Math.floor(totalBlockMix / 10000), 1);
|
||||
let blockMixCnt = 0;
|
||||
blockMixCb = () => {
|
||||
blockMixCnt++;
|
||||
if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
|
||||
onProgress(blockMixCnt / totalBlockMix);
|
||||
};
|
||||
}
|
||||
return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
|
||||
}
|
||||
|
||||
function scryptOutput(
|
||||
password: Input,
|
||||
dkLen: number,
|
||||
B: Uint8Array,
|
||||
V: Uint32Array,
|
||||
tmp: Uint32Array
|
||||
) {
|
||||
const res = pbkdf2(sha256, password, B, { c: 1, dkLen });
|
||||
B.fill(0);
|
||||
V.fill(0);
|
||||
tmp.fill(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
export function scrypt(password: Input, salt: Input, _opts: ScryptOpts) {
|
||||
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(
|
||||
password,
|
||||
salt,
|
||||
_opts
|
||||
);
|
||||
for (let pi = 0; pi < p; pi++) {
|
||||
const Pi = blockSize32 * pi;
|
||||
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
|
||||
for (let i = 0, pos = 0; i < N - 1; i++) {
|
||||
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
|
||||
blockMixCb();
|
||||
}
|
||||
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
|
||||
blockMixCb();
|
||||
for (let i = 0; i < N; i++) {
|
||||
// First u32 of the last 64-byte block (u32 is LE)
|
||||
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
|
||||
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
|
||||
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
|
||||
blockMixCb();
|
||||
}
|
||||
}
|
||||
return scryptOutput(password, dkLen, B, V, tmp);
|
||||
}
|
||||
|
||||
export async function scryptAsync(password: Uint8Array, salt: Uint8Array, _opts: ScryptOpts) {
|
||||
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit(
|
||||
password,
|
||||
salt,
|
||||
_opts
|
||||
);
|
||||
for (let pi = 0; pi < p; pi++) {
|
||||
const Pi = blockSize32 * pi;
|
||||
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
|
||||
let pos = 0;
|
||||
await asyncLoop(N - 1, asyncTick, (i) => {
|
||||
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
|
||||
blockMixCb();
|
||||
});
|
||||
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
|
||||
blockMixCb();
|
||||
await asyncLoop(N, asyncTick, (i) => {
|
||||
// First u32 of the last 64-byte block (u32 is LE)
|
||||
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
|
||||
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
|
||||
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
|
||||
blockMixCb();
|
||||
});
|
||||
}
|
||||
return scryptOutput(password, dkLen, B, V, tmp);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user