Compare commits

...
46 Commits
Author SHA1 Message Date
github-actions[bot] 25196c3367 [CI Skip] release/stable 8.0.2
skip-checks: true
2021-11-26 09:58:36 +00:00
Jaco eeb68c7e0f Explicit inclusion (patch-package + bundled is problematic) (#1250)
* Explicit inclusion (patch-package + bundled is problematic)

* 8.0.2
2021-11-26 11:53:45 +02:00
github-actions[bot] 1604b92771 [CI Skip] release/stable 8.0.1
skip-checks: true
2021-11-26 09:32:49 +00:00
Jaco 1881b564c5 8.0.1 (#1249)
* 8.0.1

* correct version

* Remove unused dep
2021-11-26 11:29:09 +02:00
github-actions[bot] 3bd4b56eb0 [CI Skip] release/stable 8.0.0
skip-checks: true
2021-11-26 08:51:49 +00:00
Jaco ce46e6da0c 8.0.0-0 (indicating bump) (#1248)
* 8.0.0-0 (indicating bump)

* root
2021-11-26 10:47:29 +02:00
github-actions[bot] bd1efcbba0 [CI Skip] release/beta 7.9.3-19
skip-checks: true
2021-11-26 08:26:57 +00:00
Jaco f2e4fa0d34 Hash bigint syntax (tmp incl. w/ _n adjustment) (#1246)
* Hash bigint syntax (tmp incl. w/ _n adjustment)

* headers with licence & PR link

* Re-adjust package.json

* Use patch-package

* test for patch application

* Upgrade dev

* Remove eslintignore
2021-11-26 10:22:17 +02:00
Github Actions adbf452c8d [CI Skip] release/beta 7.9.3-18
skip-checks: true
2021-11-26 07:07:40 +00:00
Jaco 56a5a079dd Rename bi constants (consistency) (#1247)
* Rename bi constants (consistency)

* sqrt constant

* Rename bi* functions

* secp256k1 also part of renames
2021-11-26 09:03:14 +02:00
Github Actions 0791b2dc9e [CI Skip] release/beta 7.9.3-17
skip-checks: true
2021-11-25 15:19:42 +00:00
Jaco 9821eba11f Rename internal BigInt helper (#1244)
* Rename internal BigInt helper

* Consistency
2021-11-25 17:16:09 +02:00
Github Actions b0b0151071 [CI Skip] release/beta 7.9.3-16
skip-checks: true
2021-11-25 14:08:16 +00:00
Jaco 8422e0da66 CHANGELOG (#1243) 2021-11-25 16:03:24 +02:00
Github Actions 5faa0fd544 [CI Skip] release/beta 7.9.3-15
skip-checks: true
2021-11-25 10:21:50 +00:00
Jaco b16396b9b3 Rename internal keypair locations (#1242)
* Rename internal keypair locations

* save all :)

* Incdicate testing

* add dummy test
2021-11-25 12:17:23 +02:00
Github Actions f342348ad2 [CI Skip] release/beta 7.9.3-14
skip-checks: true
2021-11-25 09:38:14 +00:00
Jaco a8f13bd387 Additional adjustments on newBigInt (#1241)
* Additional adjustments on newBigInt

* typing
2021-11-25 11:34:34 +02:00
Github Actions dbb77f35b6 [CI Skip] release/beta 7.9.3-13
skip-checks: true
2021-11-25 09:03:15 +00:00
Jaco c60e35ee9e Ensure BigInt(...) is called conditionally (#1240)
* Ensure BigInt(...) is called consitionally

* adjust

* comments

* adjust
2021-11-25 10:59:26 +02:00
Github Actions b102d9c2b3 [CI Skip] release/beta 7.9.3-12
skip-checks: true
2021-11-25 07:19:56 +00:00
Jaco 0ee4bc81f5 Performance test adjustments (#1239)
* Performance test adjustments

* testing

* adjust

* alphabet

* Adjust
2021-11-25 09:16:01 +02:00
Github Actions b05abf869a [CI Skip] release/beta 7.9.3-11
skip-checks: true
2021-11-24 14:59:34 +00:00
Jaco bc014f11dd Stale schedule (#1238) 2021-11-24 16:55:43 +02:00
Github Actions f1429f10af [CI Skip] release/beta 7.9.3-10
skip-checks: true
2021-11-24 13:25:40 +00:00
Jaco be8cabbd1e Rename {ed, sr}25519 functions (#1237)
* Rename {ed, sr}25519 functions

* Finalize renames

* Fix wasm imports

* CHANGELOG
2021-11-24 15:21:28 +02:00
Github Actions 8d0a0ed2b7 [CI Skip] release/beta 7.9.3-9
skip-checks: true
2021-11-24 12:04:11 +00:00
Jaco 39baaa2b6d CHANGELOG (#1236) 2021-11-24 14:00:23 +02:00
Github Actions 821980f64e [CI Skip] release/beta 7.9.3-8
skip-checks: true
2021-11-24 11:46:56 +00:00
Jaco 6688ff0a43 Use @noble/hashes (#1188)
* Use hashing functions from @noble/hashes

* Remove built-in pbkdf2

* Remove @types

* -19

* Remove @types

* Update packages/util-crypto/src/mnemonic/bip39.ts

* Align

* BigInt alignment

* hmac BigInt assert

* Legacy hmacSha

* Legacy fallbacks

* Align has

* Re-add micro-base

* remove old random inputs

* re-add @types/ed2curve

* Adjust pbkdf2

* Remove legacy (we have wasm)

* Update packages/util-crypto/src/detectPackage.ts

* Update packages/util-crypto/src/detectPackage.ts

* Ordering

* Comments

* Adjust

* Fix build

* cleanup legacySeed
2021-11-24 13:42:49 +02:00
Github Actions ee18b661ad [CI Skip] release/beta 7.9.3-7
skip-checks: true
2021-11-24 07:06:06 +00:00
Jaco 9a5998b921 bigint consts without BigInt(*) wrapper (#1235) 2021-11-24 09:02:17 +02:00
Github Actions f27f705f0c [CI Skip] release/beta 7.9.3-6
skip-checks: true
2021-11-23 16:00:47 +00:00
Jaco 5e16b1e84c Remove concatBuffer viariant (#1234) 2021-11-23 17:56:33 +02:00
Github Actions 25a53e43fd [CI Skip] release/beta 7.9.3-5
skip-checks: true
2021-11-23 14:58:30 +00:00
Jaco 75b60b9999 Adjust and enable old perfromance tests (#1233) 2021-11-23 16:54:39 +02:00
Github Actions e8813aa110 [CI Skip] release/beta 7.9.3-4
skip-checks: true
2021-11-23 08:55:23 +00:00
Jaco b55cdb25f6 xxhash64 replacement (#1220)
* xxhash64 replacement

* yarn.lock

* hasBigInt check

* extract common

* lint fixes

* Dedupe

* flatten

* Update changes

* Update packages/util-crypto/src/xxhash/asU8a.spec.ts

* Remove useless assignment

* flatten

* combine intermediate vars

* Align var naming

* lint

* Remove unneeded inner conversions

* Update packages/util-crypto/src/xxhash/xxhash64.ts

* Adjust

* bitLength type

* BitLength type

* additional BitLength

* Small cleanups

* Adjust

* Adjust

* blak2 bitLength adjust

* with 384

* renames

* Adjust

* Adjust

* localize

* Comments

* format performance outputs

* format performance outputs

* isWasmOnly revert
2021-11-23 10:51:08 +02:00
Github Actions 05e852337a [CI Skip] release/beta 7.9.3-3
skip-checks: true
2021-11-22 14:45:37 +00:00
Jaco cdd2d9ace5 Allow for . & , in camelCase (#1231) 2021-11-22 16:41:50 +02:00
Github Actions a2d9538b31 [CI Skip] release/beta 7.9.3-2
skip-checks: true
2021-11-22 14:17:13 +00:00
Jaco 391ab49b8f Adjust camelCase with simplified version (#1230)
* Adjust camelCase with simplified version

* Adjust count

* Adjust

* readability

* Ascii-only

* single-line callbacks

* CHANGELOG

* Dedupe string{Lower, Upper}First

* replacer -> fn
2021-11-22 16:12:29 +02:00
Github Actions b390714285 [CI Skip] release/beta 7.9.3-1
skip-checks: true
2021-11-22 06:54:50 +00:00
Jaco 277c80eb96 Revery isWasmOnly helper (#1229)
* Revery isWasmOnly helper

* Adjust
2021-11-22 08:51:03 +02:00
Github Actions 28f0040080 [CI Skip] release/beta 7.9.3-0
skip-checks: true
2021-11-22 06:26:28 +00:00
Jaco 5f280aabb8 Bump dev (w/ /src/ check) (#1228) 2021-11-22 08:22:37 +02:00
181 changed files with 4106 additions and 1050 deletions
+2 -1
View File
@@ -19,4 +19,5 @@
7.0.3
7.1.2
7.8.2
7.9.2
7.9.2
8.0.2
+1
View File
@@ -0,0 +1 @@
packages/util-crypto/src/noble-hashes/lib/*.ts
+1 -1
View File
@@ -2,7 +2,7 @@ name: 'Lock Threads'
on:
schedule:
- cron: '30 1/2 * * *'
- cron: '15 1 * * *'
jobs:
lock:
+1 -1
View File
@@ -1,7 +1,7 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 2/2 * * *'
- cron: '50 1 * * *'
jobs:
stale:
+25
View File
@@ -1,5 +1,30 @@
# CHANGELOG
## 8.0.2
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.
**Breaking change** The `schnorrkel*` functions have been renamed to `sr25519*`. Likewise the `ed25519` operations have been renamed from `nacl*` to `ed25519*`. Additionally the `{ed, sr, secp256k1}25519KeyPairFrom*` functions have been renamed to `{ed, sr, secp256k1}25519PairFrom*`.
Changes:
- Introduce `ed25519*` & `sr25519*` functions
- Replace camelcase dep with simplified/optimized version
- Replace (most) hashing operations with `@noble/hashes`
- Replace JS fallback for `xxhash`
- Adjust `BigInt` utility exports (with capability detection)
- Previously added `bi*` functions renamed to `n*`
- Extend performance tests with implementation compares
- Remove non-significant Node Buffer concat fill
## 7.9.2 Nov 22, 2021
Changes:
+3 -3
View File
@@ -10,7 +10,7 @@
"url": "https://github.com/polkadot-js/common.git"
},
"sideEffects": false,
"version": "7.9.2",
"version": "8.0.2",
"workspaces": [
"packages/*"
],
@@ -29,8 +29,8 @@
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@polkadot/dev": "^0.63.29",
"@polkadot/ts": "^0.4.14",
"@polkadot/dev": "^0.63.33",
"@polkadot/ts": "^0.4.15",
"@types/jest": "^27.0.3"
},
"resolutions": {
+1 -1
View File
@@ -16,7 +16,7 @@
},
"sideEffects": false,
"type": "module",
"version": "7.9.2",
"version": "8.0.2",
"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: '7.9.2' };
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '8.0.2' };
+3 -3
View File
@@ -19,12 +19,12 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "7.9.2",
"version": "8.0.2",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/hw-ledger-transports": "7.9.2",
"@polkadot/util": "7.9.2",
"@polkadot/hw-ledger-transports": "8.0.2",
"@polkadot/util": "8.0.2",
"@zondax/ledger-substrate": "^0.18.0"
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/hw-ledger', version: '7.9.2' };
export const packageInfo = { name: '@polkadot/hw-ledger', version: '8.0.2' };
+5 -5
View File
@@ -20,15 +20,15 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "7.9.2",
"version": "8.0.2",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/util": "7.9.2",
"@polkadot/util-crypto": "7.9.2"
"@polkadot/util": "8.0.2",
"@polkadot/util-crypto": "8.0.2"
},
"peerDependencies": {
"@polkadot/util": "7.9.2",
"@polkadot/util-crypto": "7.9.2"
"@polkadot/util": "8.0.2",
"@polkadot/util-crypto": "8.0.2"
}
}
+7 -7
View File
@@ -5,17 +5,17 @@ import type { EncryptedJsonEncoding, Keypair, KeypairType } from '@polkadot/util
import type { KeyringInstance, KeyringOptions, KeyringPair, KeyringPair$Json, KeyringPair$Meta } from './types';
import { assert, hexToU8a, isHex, isUndefined, stringToU8a } from '@polkadot/util';
import { base64Decode, decodeAddress, encodeAddress, ethereumEncode, hdEthereum, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, naclKeypairFromSeed as naclFromSeed, schnorrkelKeypairFromSeed as schnorrkelFromSeed, secp256k1KeypairFromSeed as secp256k1FromSeed } from '@polkadot/util-crypto';
import { base64Decode, decodeAddress, ed25519PairFromSeed as ed25519FromSeed, encodeAddress, ethereumEncode, hdEthereum, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, secp256k1PairFromSeed as secp256k1FromSeed, sr25519PairFromSeed as sr25519FromSeed } from '@polkadot/util-crypto';
import { DEV_PHRASE } from './defaults';
import { createPair } from './pair';
import { Pairs } from './pairs';
const keypairFromSeed = {
const PairFromSeed = {
ecdsa: (seed: Uint8Array): Keypair => secp256k1FromSeed(seed),
ed25519: (seed: Uint8Array): Keypair => naclFromSeed(seed),
ed25519: (seed: Uint8Array): Keypair => ed25519FromSeed(seed),
ethereum: (seed: Uint8Array): Keypair => secp256k1FromSeed(seed),
sr25519: (seed: Uint8Array): Keypair => schnorrkelFromSeed(seed)
sr25519: (seed: Uint8Array): Keypair => sr25519FromSeed(seed)
};
function pairToPublic ({ publicKey }: KeyringPair): Uint8Array {
@@ -142,7 +142,7 @@ export class Keyring implements KeyringInstance {
*/
public addFromSeed (seed: Uint8Array, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
return this.addPair(
createPair({ toSS58: this.encodeAddress, type }, keypairFromSeed[type](seed), meta, null)
createPair({ toSS58: this.encodeAddress, type }, PairFromSeed[type](seed), meta, null)
);
}
@@ -224,9 +224,9 @@ export class Keyring implements KeyringInstance {
const derived = type === 'ethereum'
? isPhraseHex
? keypairFromSeed[type](seed) // for eth, if the private key is provided as suri, it must be derived only once
? PairFromSeed[type](seed) // for eth, if the private key is provided as suri, it must be derived only once
: hdEthereum(seed, derivePath.substring(1))
: keyFromPath(keypairFromSeed[type](seed), path, type);
: keyFromPath(PairFromSeed[type](seed), path, type);
return createPair({ toSS58: this.encodeAddress, type }, derived, meta, null);
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/keyring', version: '7.9.2' };
export const packageInfo = { name: '@polkadot/keyring', version: '8.0.2' };
+7 -7
View File
@@ -7,7 +7,7 @@ import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta, SignOptions } fro
import type { PairInfo } from './types';
import { assert, objectSpread, u8aConcat, u8aEmpty, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util';
import { blake2AsU8a, convertPublicKeyToCurve25519, convertSecretKeyToCurve25519, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, naclKeypairFromSeed as naclFromSeed, naclOpen, naclSeal, naclSign, schnorrkelKeypairFromSeed as schnorrkelFromSeed, schnorrkelSign, schnorrkelVrfSign, schnorrkelVrfVerify, secp256k1Compress, secp256k1Expand, secp256k1KeypairFromSeed as secp256k1FromSeed, secp256k1Sign, signatureVerify } from '@polkadot/util-crypto';
import { blake2AsU8a, convertPublicKeyToCurve25519, convertSecretKeyToCurve25519, ed25519PairFromSeed as ed25519FromSeed, ed25519Sign, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, naclOpen, naclSeal, secp256k1Compress, secp256k1Expand, secp256k1PairFromSeed as secp256k1FromSeed, secp256k1Sign, signatureVerify, sr25519PairFromSeed as sr25519FromSeed, sr25519Sign, sr25519VrfSign, sr25519VrfVerify } from '@polkadot/util-crypto';
import { decodePair } from './decode';
import { encodePair } from './encode';
@@ -22,9 +22,9 @@ const SIG_TYPE_NONE = new Uint8Array();
const TYPE_FROM_SEED = {
ecdsa: secp256k1FromSeed,
ed25519: naclFromSeed,
ed25519: ed25519FromSeed,
ethereum: secp256k1FromSeed,
sr25519: schnorrkelFromSeed
sr25519: sr25519FromSeed
};
const TYPE_PREFIX = {
@@ -36,9 +36,9 @@ const TYPE_PREFIX = {
const TYPE_SIGNATURE = {
ecdsa: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'blake2'),
ed25519: naclSign,
ed25519: ed25519Sign,
ethereum: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'keccak'),
sr25519: schnorrkelSign
sr25519: sr25519Sign
};
const TYPE_ADDRESS = {
@@ -215,7 +215,7 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
if (type === 'sr25519') {
return schnorrkelVrfSign(message, { secretKey }, context, extra);
return sr25519VrfSign(message, { secretKey }, context, extra);
}
const proof = TYPE_SIGNATURE[type](u8aToU8a(message), { publicKey, secretKey });
@@ -224,7 +224,7 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
},
vrfVerify: (message: HexString | string | Uint8Array, vrfResult: Uint8Array, signerPublic: HexString | Uint8Array | string, context?: HexString | string | Uint8Array, extra?: HexString | string | Uint8Array): boolean => {
if (type === 'sr25519') {
return schnorrkelVrfVerify(message, vrfResult, publicKey, context, extra);
return sr25519VrfVerify(message, vrfResult, publicKey, context, extra);
}
const result = signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), TYPE_ADDRESS[type](u8aToU8a(signerPublic)));
+4 -4
View File
@@ -4,7 +4,7 @@
import type { KeyringPair } from '../types';
import { u8aToHex } from '@polkadot/util';
import { cryptoWaitReady, encodeAddress as toSS58, naclKeypairFromSeed, randomAsU8a, schnorrkelKeypairFromSeed, secp256k1KeypairFromSeed } from '@polkadot/util-crypto';
import { cryptoWaitReady, ed25519PairFromSeed, encodeAddress as toSS58, randomAsU8a, secp256k1PairFromSeed, sr25519PairFromSeed } from '@polkadot/util-crypto';
import { createPair } from '.';
@@ -19,9 +19,9 @@ describe('vrf', (): void => {
beforeAll(async (): Promise<void> => {
await cryptoWaitReady();
ecdsa = createPair({ toSS58, type: 'ecdsa' }, secp256k1KeypairFromSeed(randomAsU8a()));
ed25519 = createPair({ toSS58, type: 'ed25519' }, naclKeypairFromSeed(randomAsU8a()));
sr25519 = createPair({ toSS58, type: 'sr25519' }, schnorrkelKeypairFromSeed(randomAsU8a()));
ecdsa = createPair({ toSS58, type: 'ecdsa' }, secp256k1PairFromSeed(randomAsU8a()));
ed25519 = createPair({ toSS58, type: 'ed25519' }, ed25519PairFromSeed(randomAsU8a()));
sr25519 = createPair({ toSS58, type: 'sr25519' }, sr25519PairFromSeed(randomAsU8a()));
});
it('has deterministic signature values for ecdsa', (): void => {
+3 -3
View File
@@ -17,13 +17,13 @@
},
"sideEffects": false,
"type": "module",
"version": "7.9.2",
"version": "8.0.2",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3"
},
"devDependencies": {
"@polkadot/util": "7.9.2",
"@polkadot/x-fetch": "7.9.2"
"@polkadot/util": "8.0.2",
"@polkadot/x-fetch": "8.0.2"
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/networks', version: '7.9.2' };
export const packageInfo = { name: '@polkadot/networks', version: '8.0.2' };
+11 -16
View File
@@ -1,6 +1,9 @@
{
"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": {
@@ -22,35 +25,27 @@
"./detectPackage.cjs"
],
"type": "module",
"version": "7.9.2",
"version": "8.0.2",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.16.3",
"@polkadot/networks": "7.9.2",
"@polkadot/util": "7.9.2",
"@noble/hashes": "0.4.1",
"@polkadot/networks": "8.0.2",
"@polkadot/util": "8.0.2",
"@polkadot/wasm-crypto": "^4.4.1",
"@polkadot/x-randomvalues": "7.9.2",
"blakejs": "^1.1.1",
"@polkadot/x-randomvalues": "8.0.2",
"bn.js": "^4.12.0",
"create-hash": "^1.2.0",
"ed2curve": "^0.3.0",
"elliptic": "^6.5.4",
"hash.js": "^1.1.7",
"js-sha3": "^0.8.0",
"micro-base": "^0.9.0",
"scryptsy": "^2.1.0",
"tweetnacl": "^1.0.3",
"xxhashjs": "^0.2.2"
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@types/bn.js": "^4.11.6",
"@types/create-hash": "^1.2.2",
"@types/ed2curve": "^0.2.2",
"@types/elliptic": "^6.4.14",
"@types/scryptsy": "^2.0.0",
"@types/xxhashjs": "^0.2.2"
"@types/elliptic": "^6.4.14"
},
"peerDependencies": {
"@polkadot/util": "7.9.2"
"@polkadot/util": "8.0.2"
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import type { Prefix } from './types';
import { assert } from '@polkadot/util';
import { keyExtractPath } from '../key';
import { schnorrkelDerivePublic } from '../schnorrkel';
import { sr25519DerivePublic } from '../sr25519';
import { decodeAddress } from './decode';
import { encodeAddress } from './encode';
@@ -30,7 +30,7 @@ export function deriveAddress (who: HexString | Uint8Array | string, suri: strin
let publicKey = decodeAddress(who);
for (const { chainCode } of path) {
publicKey = schnorrkelDerivePublic(publicKey, chainCode);
publicKey = sr25519DerivePublic(publicKey, chainCode);
}
return encodeAddress(publicKey, ss58Format);
+1 -1
View File
@@ -10,7 +10,7 @@ import { assert, u8aToU8a } from '@polkadot/util';
//
// ends up in the build-generated code as
//
// import("../../../util/src/types").U8aLike
// impor("../../../util/src/types").U8aLike
//
// eslint-disable-next-line @typescript-eslint/ban-types
type U8aLike = number[] | Buffer | Uint8Array | string | String;
@@ -4,7 +4,7 @@
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { blake2AsU8a } from '.';
describe('blake2AsU8a', (): void => {
@@ -61,7 +61,7 @@ describe('blake2AsU8a', (): void => {
});
describe.each([256, 512] as (256 | 512)[])('bitLength=$p', (bitLength): void => {
performanceTest(`blake2AsU8a, bitLength=${bitLength}`, 64000, (input, onlyJs) =>
performanceWasm(`blake2AsU8a, bitLength=${bitLength}`, 64000, (input, onlyJs) =>
blake2AsU8a(input, bitLength, null, onlyJs)
);
});
+8 -7
View File
@@ -3,12 +3,13 @@
import type { HexString } from '@polkadot/util/types';
import js from 'blakejs';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { blake2b, isReady } from '@polkadot/wasm-crypto';
import { u8aToU8a } from '@polkadot/util';
import { blake2b } from '@polkadot/wasm-crypto';
import { createAsHex } from '../helpers';
import { blake2b as blake2bJs } from '../noble-hashes/lib/blake2b';
import { createAsHex, isWasmOnly } from '../helpers';
type BitLength = 64 | 128 | 256 | 384 | 512;
/**
* @name blake2AsU8a
@@ -24,13 +25,13 @@ import { createAsHex, isWasmOnly } from '../helpers';
* blake2AsU8a('abc'); // => [0xba, 0x80, 0xa5, 0x3f, 0x98, 0x1c, 0x4d, 0x0d]
* ```
*/
export function blake2AsU8a (data: HexString | Uint8Array | string, bitLength = 256, key?: Uint8Array | null, onlyJs?: boolean): Uint8Array {
export function blake2AsU8a (data: HexString | Uint8Array | string, bitLength: BitLength = 256, key?: Uint8Array | null, onlyJs?: boolean): Uint8Array {
const byteLength = Math.ceil(bitLength / 8);
const u8a = u8aToU8a(data);
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? blake2b(u8a, u8aToU8a(key), byteLength)
: js.blake2b(u8a, key || undefined, byteLength);
: blake2bJs(u8a, { dkLen: byteLength, key: key || undefined });
}
/**
+2 -1
View File
@@ -12,6 +12,7 @@ export * from './base58';
export * from './base64';
export * from './blake2';
export * from './crypto';
export * from './ed25519';
export * from './ethereum';
export * from './hd';
export * from './hmac';
@@ -23,9 +24,9 @@ export * from './networks';
export * from './nacl';
export * from './pbkdf2';
export * from './random';
export * from './schnorrkel';
export * from './scrypt';
export * from './secp256k1';
export * from './sha';
export * from './signature';
export * from './sr25519';
export * from './xxhash';
@@ -7,7 +7,7 @@ import { blake2AsU8a } from '../blake2/asU8a';
const HDKD = compactAddLength(stringToU8a('Ed25519HDKD'));
export function naclDeriveHard (seed: Uint8Array, chainCode: Uint8Array): Uint8Array {
export function ed25519DeriveHard (seed: Uint8Array, chainCode: Uint8Array): Uint8Array {
assert(isU8a(chainCode) && chainCode.length === 32, 'Invalid chainCode passed to derive');
return blake2AsU8a(
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
/**
* @summary Implements ed25519 operations
*/
export { convertPublicKeyToCurve25519, convertSecretKeyToCurve25519 } from './convertKey';
export { ed25519DeriveHard } from './deriveHard';
export { ed25519PairFromRandom } from './pair/fromRandom';
export { ed25519PairFromSecret } from './pair/fromSecret';
export { ed25519PairFromSeed } from './pair/fromSeed';
export { ed25519PairFromString } from './pair/fromString';
export { ed25519Sign } from './sign';
export { ed25519Verify } from './verify';
@@ -3,13 +3,13 @@
import type { Keypair } from '../../types';
import { naclKeypairFromRandom } from '..';
import { ed25519PairFromRandom } from '..';
describe('naclKeypairFromRandom', (): void => {
describe('ed25519PairFromRandom', (): void => {
let keypair: Keypair;
beforeEach((): void => {
keypair = naclKeypairFromRandom();
keypair = ed25519PairFromRandom();
});
it('generates a valid publicKey', (): void => {
@@ -6,7 +6,7 @@ import type { Keypair } from '../../types';
import nacl from 'tweetnacl';
/**
* @name naclKeypairFromRandom
* @name ed25519PairFromRandom
* @summary Creates a new public/secret keypair.
* @description
* Returns a new generate object containing a `publicKey` & `secretKey`.
@@ -14,11 +14,11 @@ import nacl from 'tweetnacl';
* <BR>
*
* ```javascript
* import { naclKeypairFromRandom } from '@polkadot/util-crypto';
* import { ed25519PairFromRandom } from '@polkadot/util-crypto';
*
* naclKeypairFromRandom(); // => { secretKey: [...], publicKey: [...] }
* ed25519PairFromRandom(); // => { secretKey: [...], publicKey: [...] }
* ```
*/
export function naclKeypairFromRandom (): Keypair {
export function ed25519PairFromRandom (): Keypair {
return nacl.sign.keyPair();
}
@@ -1,9 +1,9 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { naclKeypairFromSecret } from '..';
import { ed25519PairFromSecret } from '..';
describe('naclKeypairFromSecret', (): void => {
describe('ed25519PairFromSecret', (): void => {
const secretKey = new Uint8Array([
18, 52, 86, 120, 144, 18, 52, 86,
120, 144, 18, 52, 86, 120, 144, 18,
@@ -17,7 +17,7 @@ describe('naclKeypairFromSecret', (): void => {
it('generates a valid publicKey/secretKey pair', (): void => {
expect(
naclKeypairFromSecret(secretKey)
ed25519PairFromSecret(secretKey)
).toEqual({
publicKey: new Uint8Array([
180, 114, 93, 155, 165, 255, 217, 82,
@@ -6,7 +6,7 @@ import type { Keypair } from '../../types';
import nacl from 'tweetnacl';
/**
* @name naclKeypairFromSecret
* @name ed25519PairFromSecret
* @summary Creates a new public/secret keypair from a secret.
* @description
* Returns a object containing a `publicKey` & `secretKey` generated from the supplied secret.
@@ -14,11 +14,11 @@ import nacl from 'tweetnacl';
* <BR>
*
* ```javascript
* import { naclKeypairFromSecret } from '@polkadot/util-crypto';
* import { ed25519PairFromSecret } from '@polkadot/util-crypto';
*
* naclKeypairFromSecret(...); // => { secretKey: [...], publicKey: [...] }
* ed25519PairFromSecret(...); // => { secretKey: [...], publicKey: [...] }
* ```
*/
export function naclKeypairFromSecret (secret: Uint8Array): Keypair {
export function ed25519PairFromSecret (secret: Uint8Array): Keypair {
return nacl.sign.keyPair.fromSecretKey(secret);
}
@@ -4,9 +4,9 @@
import { stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { naclKeypairFromSeed } from '..';
import { ed25519PairFromSeed } from '..';
describe('naclKeypairFromSeed', (): void => {
describe('ed25519PairFromSeed', (): void => {
// NOTE: Aligned with Rust test, b"12345678901234567890123456789012"
const TEST = stringToU8a('12345678901234567890123456789012');
const RESULT = {
@@ -33,7 +33,7 @@ describe('naclKeypairFromSeed', (): void => {
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
[true, false].forEach((onlyJs): void => {
expect(
naclKeypairFromSeed(TEST, onlyJs)
ed25519PairFromSeed(TEST, onlyJs)
).toEqual(RESULT);
});
});
@@ -8,7 +8,7 @@ import nacl from 'tweetnacl';
import { ed25519KeypairFromSeed, isReady } from '@polkadot/wasm-crypto';
/**
* @name naclKeypairFromSeed
* @name ed25519PairFromSeed
* @summary Creates a new public/secret keypair from a seed.
* @description
* Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed.
@@ -16,12 +16,12 @@ import { ed25519KeypairFromSeed, isReady } from '@polkadot/wasm-crypto';
* <BR>
*
* ```javascript
* import { naclKeypairFromSeed } from '@polkadot/util-crypto';
* import { ed25519PairFromSeed } from '@polkadot/util-crypto';
*
* naclKeypairFromSeed(...); // => { secretKey: [...], publicKey: [...] }
* ed25519PairFromSeed(...); // => { secretKey: [...], publicKey: [...] }
* ```
*/
export function naclKeypairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keypair {
export function ed25519PairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keypair {
if (!onlyJs && isReady()) {
const full = ed25519KeypairFromSeed(seed);
@@ -1,12 +1,12 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { naclKeypairFromString } from '..';
import { ed25519PairFromString } from '..';
describe('naclKeypairFromSeed', (): void => {
describe('ed25519PairFromSeed', (): void => {
it('generates a valid publicKey/secretKey pair', (): void => {
expect(
naclKeypairFromString('test')
ed25519PairFromString('test')
).toEqual({
publicKey: new Uint8Array([188, 108, 179, 142, 36, 142, 76, 87, 77, 193, 147, 139, 254, 110, 196, 217, 117, 233, 167, 165, 250, 150, 247, 237, 198, 68, 129, 4, 211, 209, 136, 48]),
secretKey: new Uint8Array([146, 139, 32, 54, 105, 67, 226, 175, 209, 30, 188, 14, 174, 46, 83, 169, 59, 241, 119, 164, 252, 243, 91, 204, 100, 213, 3, 112, 78, 101, 226, 2, 188, 108, 179, 142, 36, 142, 76, 87, 77, 193, 147, 139, 254, 110, 196, 217, 117, 233, 167, 165, 250, 150, 247, 237, 198, 68, 129, 4, 211, 209, 136, 48])
@@ -6,10 +6,10 @@ import type { Keypair } from '../../types';
import { stringToU8a } from '@polkadot/util';
import { blake2AsU8a } from '../../blake2/asU8a';
import { naclKeypairFromSeed } from './fromSeed';
import { ed25519PairFromSeed } from './fromSeed';
/**
* @name naclKeypairFromString
* @name ed25519PairFromString
* @summary Creates a new public/secret keypair from a string.
* @description
* Returns a object containing a `publicKey` & `secretKey` generated from the supplied string. The string is hashed and the value used as the input seed.
@@ -17,13 +17,13 @@ import { naclKeypairFromSeed } from './fromSeed';
* <BR>
*
* ```javascript
* import { naclKeypairFromString } from '@polkadot/util-crypto';
* import { ed25519PairFromString } from '@polkadot/util-crypto';
*
* naclKeypairFromString('test'); // => { secretKey: [...], publicKey: [...] }
* ed25519PairFromString('test'); // => { secretKey: [...], publicKey: [...] }
* ```
*/
export function naclKeypairFromString (value: string): Keypair {
return naclKeypairFromSeed(
export function ed25519PairFromString (value: string): Keypair {
return ed25519PairFromSeed(
blake2AsU8a(
stringToU8a(value),
256
@@ -4,14 +4,14 @@
import { stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { naclKeypairFromSeed, naclSign } from '.';
import { performanceWasm } from '../../test/performance';
import { ed25519PairFromSeed, ed25519Sign } from '.';
const PAIR = naclKeypairFromSeed(
const PAIR = ed25519PairFromSeed(
stringToU8a('12345678901234567890123456789012')
);
describe('naclSign', (): void => {
describe('ed25519Sign', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
@@ -19,7 +19,7 @@ describe('naclSign', (): void => {
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('returns a valid signature for the message', (): void => {
expect(
naclSign(
ed25519Sign(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
PAIR,
onlyJs
@@ -30,7 +30,7 @@ describe('naclSign', (): void => {
});
});
performanceTest('naclSign', 250, (input, onlyJs) =>
naclSign(input, PAIR, onlyJs)
performanceWasm('ed25519Sign', 250, (input, onlyJs) =>
ed25519Sign(input, PAIR, onlyJs)
);
});
@@ -7,12 +7,10 @@ import type { Keypair } from '../types';
import nacl from 'tweetnacl';
import { assert, u8aToU8a } from '@polkadot/util';
import { ed25519Sign } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { ed25519Sign as wasmSign, isReady } from '@polkadot/wasm-crypto';
/**
* @name naclSign
* @name ed25519Sign
* @summary Signs a message using the supplied secretKey
* @description
* Returns message signature of `message`, using the `secretKey`.
@@ -20,17 +18,17 @@ import { isWasmOnly } from '../helpers';
* <BR>
*
* ```javascript
* import { naclSign } from '@polkadot/util-crypto';
* import { ed25519Sign } from '@polkadot/util-crypto';
*
* naclSign([...], [...]); // => [...]
* ed25519Sign([...], [...]); // => [...]
* ```
*/
export function naclSign (message: HexString | Uint8Array | string, { publicKey, secretKey }: Partial<Keypair>, onlyJs?: boolean): Uint8Array {
export function ed25519Sign (message: HexString | Uint8Array | string, { publicKey, secretKey }: Partial<Keypair>, onlyJs?: boolean): Uint8Array {
assert(secretKey, 'Expected a valid secretKey');
const messageU8a = u8aToU8a(message);
return isWasmOnly(onlyJs)
? ed25519Sign(publicKey as Uint8Array, secretKey.subarray(0, 32), messageU8a)
return !onlyJs && isReady()
? wasmSign(publicKey as Uint8Array, secretKey.subarray(0, 32), messageU8a)
: nacl.sign.detached(messageU8a, secretKey);
}
@@ -4,16 +4,16 @@
import { stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { naclKeypairFromSeed, naclVerify } from '.';
import { ed25519PairFromSeed, ed25519Verify } from '.';
describe('naclVerify', (): void => {
describe('ed25519Verify', (): void => {
let publicKey: Uint8Array;
let signature: Uint8Array;
beforeEach(async (): Promise<void> => {
await waitReady();
publicKey = naclKeypairFromSeed(
publicKey = ed25519PairFromSeed(
stringToU8a('12345678901234567890123456789012')
).publicKey;
signature = new Uint8Array([28, 58, 206, 239, 249, 70, 59, 191, 166, 40, 219, 218, 235, 170, 25, 79, 10, 94, 9, 197, 34, 126, 1, 150, 246, 68, 28, 238, 36, 26, 172, 163, 168, 90, 202, 211, 126, 246, 57, 212, 43, 24, 88, 197, 240, 113, 118, 76, 37, 81, 91, 110, 236, 50, 144, 134, 100, 223, 220, 238, 34, 185, 211, 7]);
@@ -22,7 +22,7 @@ describe('naclVerify', (): void => {
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('validates a correctly signed message', (): void => {
expect(
naclVerify(
ed25519Verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
signature,
publicKey,
@@ -33,7 +33,7 @@ describe('naclVerify', (): void => {
it('fails a correctly signed message (message changed)', (): void => {
expect(
naclVerify(
ed25519Verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
signature,
publicKey,
@@ -46,7 +46,7 @@ describe('naclVerify', (): void => {
signature[0] = 0xff;
expect(
naclVerify(
ed25519Verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
signature,
publicKey,
@@ -57,7 +57,7 @@ describe('naclVerify', (): void => {
it('throws error when publicKey lengths do not match', (): void => {
expect(
() => naclVerify(
() => ed25519Verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
signature,
new Uint8Array([1, 2]),
@@ -68,7 +68,7 @@ describe('naclVerify', (): void => {
it('throws error when signature lengths do not match', (): void => {
expect(
() => naclVerify(
() => ed25519Verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
new Uint8Array([1, 2]),
publicKey,
@@ -6,12 +6,10 @@ import type { HexString } from '@polkadot/util/types';
import nacl from 'tweetnacl';
import { assert, u8aToU8a } from '@polkadot/util';
import { ed25519Verify } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { ed25519Verify as wasmVerify, isReady } from '@polkadot/wasm-crypto';
/**
* @name naclSign
* @name ed25519Sign
* @summary Verifies the signature on the supplied message.
* @description
* Verifies the `signature` on `message` with the supplied `publicKey`. Returns `true` on sucess, `false` otherwise.
@@ -19,12 +17,12 @@ import { isWasmOnly } from '../helpers';
* <BR>
*
* ```javascript
* import { naclVerify } from '@polkadot/util-crypto';
* import { ed25519Verify } from '@polkadot/util-crypto';
*
* naclVerify([...], [...], [...]); // => true/false
* ed25519Verify([...], [...], [...]); // => true/false
* ```
*/
export function naclVerify (message: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, publicKey: HexString | Uint8Array | string, onlyJs?: boolean): boolean {
export function ed25519Verify (message: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, publicKey: HexString | Uint8Array | string, onlyJs?: boolean): boolean {
const messageU8a = u8aToU8a(message);
const publicKeyU8a = u8aToU8a(publicKey);
const signatureU8a = u8aToU8a(signature);
@@ -32,7 +30,7 @@ export function naclVerify (message: HexString | Uint8Array | string, signature:
assert(publicKeyU8a.length === 32, () => `Invalid publicKey, received ${publicKeyU8a.length}, expected 32`);
assert(signatureU8a.length === 64, () => `Invalid signature, received ${signatureU8a.length} bytes, expected 64`);
return isWasmOnly(onlyJs)
? ed25519Verify(signatureU8a, messageU8a, publicKeyU8a)
return !onlyJs && isReady()
? wasmVerify(signatureU8a, messageU8a, publicKeyU8a)
: nacl.sign.detached.verify(messageU8a, signatureU8a, publicKeyU8a);
}
@@ -7,7 +7,7 @@ import { assert, bnToU8a, stringToU8a, u8aConcat } from '@polkadot/util';
import { BN_BE_32_OPTS } from '../../bn';
import { hmacShaAsU8a } from '../../hmac';
import { secp256k1KeypairFromSeed, secp256k1PrivateKeyTweakAdd } from '../../secp256k1';
import { secp256k1PairFromSeed, secp256k1PrivateKeyTweakAdd } from '../../secp256k1';
import { HARDENED, hdValidatePath } from '../validatePath';
interface CodedKeypair extends Keypair {
@@ -19,7 +19,7 @@ const MASTER_SECRET = stringToU8a('Bitcoin seed');
function createCoded (secretKey: Uint8Array, chainCode: Uint8Array): CodedKeypair {
return {
chainCode,
publicKey: secp256k1KeypairFromSeed(secretKey).publicKey,
publicKey: secp256k1PairFromSeed(secretKey).publicKey,
secretKey
};
}
+2 -2
View File
@@ -5,8 +5,8 @@ import type { Keypair } from '../../types';
import { assert } from '@polkadot/util';
import { ed25519PairFromSeed } from '../../ed25519';
import { mnemonicValidate } from '../../mnemonic';
import { naclKeypairFromSeed } from '../../nacl';
import { HARDENED, hdValidatePath } from '../validatePath';
import { ledgerDerivePrivate } from './derivePrivate';
import { ledgerMaster } from './master';
@@ -35,5 +35,5 @@ export function hdLedger (_mnemonic: string, path: string): Keypair {
seed = ledgerDerivePrivate(seed, (n < HARDENED) ? (n + HARDENED) : n);
}
return naclKeypairFromSeed(seed.slice(0, 32));
return ed25519PairFromSeed(seed.slice(0, 32));
}
-5
View File
@@ -4,7 +4,6 @@
import type { HexString } from '@polkadot/util/types';
import { u8aToHex } from '@polkadot/util';
import { isReady } from '@polkadot/wasm-crypto';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createAsHex <T extends (...args: any[]) => Uint8Array> (fn: T): (...args: Parameters<T>) => HexString {
@@ -16,7 +15,3 @@ export function createBitHasher <T> (bitLength: T, fn: (data: HexString | Buffer
return (data: HexString | Buffer | Uint8Array | string, onlyJs?: boolean): Uint8Array =>
fn(data, bitLength, onlyJs);
}
export function isWasmOnly (onlyJs?: boolean): boolean {
return isReady() && !onlyJs;
}
@@ -4,7 +4,7 @@
import { hexToU8a, stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { hmacShaAsU8a } from '.';
describe('hmacShaAsU8a', (): void => {
@@ -23,7 +23,7 @@ describe('hmacShaAsU8a', (): void => {
)
};
describe.each([256, 512] as (256 | 512)[])('bitLength=$p', (bitLength): void => {
describe.each([256, 512] as (256 | 512)[])('bitLength=%p', (bitLength): void => {
describe.each([false, true])('onlyJs=%p', (onlyJs): void => {
it('returns an hex representation (string)', (): void => {
expect(
@@ -32,7 +32,7 @@ describe('hmacShaAsU8a', (): void => {
});
});
performanceTest(`hmacShaAsU8a, bitLength=${bitLength}`, 16000, (input, onlyJs) =>
performanceWasm(`hmacShaAsU8a, bitLength=${bitLength}`, 16000, (input, onlyJs) =>
hmacShaAsU8a(input, input, bitLength, onlyJs)
);
});
+25 -18
View File
@@ -1,38 +1,45 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import hash from 'hash.js';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { hmacSha256, hmacSha512, isReady } from '@polkadot/wasm-crypto';
import { u8aToU8a } from '@polkadot/util';
import { hmacSha256, hmacSha512 } from '@polkadot/wasm-crypto';
import { hmac } from '../noble-hashes/lib/hmac';
import { sha256 } from '../noble-hashes/lib/sha256';
import { sha512 } from '../noble-hashes/lib/sha512';
import { isWasmOnly } from '../helpers';
type BitLength = 256 | 512;
type HashFn = (key: Uint8Array | string, data: Uint8Array, onlyJs?: boolean) => Uint8Array;
function createSha (bitLength: 256 | 512 = 256): HashFn {
function createSha (bitLength: BitLength): (key: Uint8Array | string, data: Uint8Array, onlyJs?: boolean) => Uint8Array {
return (key: Uint8Array | string, data: Uint8Array, onlyJs?: boolean): Uint8Array =>
hmacShaAsU8a(key, data, bitLength, onlyJs);
}
export function hmacShaAsU8a (key: Uint8Array | string, data: Uint8Array, bitLength: 256 | 512 = 256, onlyJs?: boolean): Uint8Array {
/**
* @name hmacShaAsU8a
* @description creates a Hmac Sha (256/512) Uint8Array from the key & data
*/
export function hmacShaAsU8a (key: Uint8Array | string, data: Uint8Array, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
const is256 = bitLength === 256;
const u8aKey = u8aToU8a(key);
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? is256
? hmacSha256(u8aKey, data)
: hmacSha512(u8aKey, data)
: new Uint8Array(
is256
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
? hash.hmac(hash.sha256, u8aKey).update(data).digest()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
: hash.hmac(hash.sha512, u8aKey).update(data).digest()
);
: is256
? hmac(sha256, u8aKey, data)
: hmac(sha512, u8aKey, data);
}
/**
* @name hmacSha256AsU8a
* @description creates a Hmac Sha256 Uint8Array from the key & data
*/
export const hmacSha256AsU8a = createSha(256);
/**
* @name hmacSha512AsU8a
* @description creates a Hmac Sha512 Uint8Array from the key & data
*/
export const hmacSha512AsU8a = createSha(512);
@@ -4,7 +4,7 @@
import { hexToU8a, stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { keccakAsU8a } from '.';
describe('keccakAsU8a', (): void => {
@@ -43,7 +43,7 @@ describe('keccakAsU8a', (): void => {
});
});
performanceTest(`keccakAsU8a, bitLength=${bitLength}`, 128000, (input, onlyJs) =>
performanceWasm(`keccakAsU8a, bitLength=${bitLength}`, 128000, (input, onlyJs) =>
keccakAsU8a(input, bitLength, onlyJs)
);
});
+10 -11
View File
@@ -3,12 +3,13 @@
import type { HexString } from '@polkadot/util/types';
import js from 'js-sha3';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, keccak256, keccak512 } from '@polkadot/wasm-crypto';
import { u8aToU8a } from '@polkadot/util';
import { keccak256, keccak512 } from '@polkadot/wasm-crypto';
import { createAsHex, createBitHasher } from '../helpers';
import { keccak_256 as keccak256Js, keccak_512 as keccak512Js } from '../noble-hashes/lib/sha3';
import { createAsHex, createBitHasher, isWasmOnly } from '../helpers';
type BitLength = 256 | 512;
/**
* @name keccakAsU8a
@@ -24,19 +25,17 @@ import { createAsHex, createBitHasher, isWasmOnly } from '../helpers';
* keccakAsU8a('123'); // => Uint8Array
* ```
*/
export function keccakAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: 256 | 512 = 256, onlyJs?: boolean): Uint8Array {
export function keccakAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
const is256 = bitLength === 256;
const u8a = u8aToU8a(value);
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? is256
? keccak256(u8a)
: keccak512(u8a)
: new Uint8Array(
is256
? js.keccak256.update(u8a).arrayBuffer()
: js.keccak512.update(u8a).arrayBuffer()
);
: is256
? keccak256Js(u8a)
: keccak512Js(u8a);
}
/**
+2 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { secp256k1DeriveHard } from '../secp256k1/deriveHard';
import { secp256k1KeypairFromSeed } from '../secp256k1/keypair/fromSeed';
import { secp256k1PairFromSeed } from '../secp256k1/pair/fromSeed';
import { createSeedDeriveFn } from './hdkdDerive';
export const keyHdkdEcdsa = createSeedDeriveFn(secp256k1KeypairFromSeed, secp256k1DeriveHard);
export const keyHdkdEcdsa = createSeedDeriveFn(secp256k1PairFromSeed, secp256k1DeriveHard);
+2 -3
View File
@@ -1,8 +1,7 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { naclDeriveHard } from '../nacl/deriveHard';
import { naclKeypairFromSeed } from '../nacl/keypair/fromSeed';
import { ed25519DeriveHard, ed25519PairFromSeed } from '../ed25519';
import { createSeedDeriveFn } from './hdkdDerive';
export const keyHdkdEd25519 = createSeedDeriveFn(naclKeypairFromSeed, naclDeriveHard);
export const keyHdkdEd25519 = createSeedDeriveFn(ed25519PairFromSeed, ed25519DeriveHard);
+4 -4
View File
@@ -3,12 +3,12 @@
import type { Keypair } from '../types';
import { schnorrkelDeriveHard } from '../schnorrkel/deriveHard';
import { schnorrkelDeriveSoft } from '../schnorrkel/deriveSoft';
import { sr25519DeriveHard } from '../sr25519/deriveHard';
import { sr25519DeriveSoft } from '../sr25519/deriveSoft';
import { DeriveJunction } from './DeriveJunction';
export function keyHdkdSr25519 (keypair: Keypair, { chainCode, isSoft }: DeriveJunction): Keypair {
return isSoft
? schnorrkelDeriveSoft(keypair, chainCode)
: schnorrkelDeriveHard(keypair, chainCode);
? sr25519DeriveSoft(keypair, chainCode)
: sr25519DeriveHard(keypair, chainCode);
}
@@ -1,9 +1,9 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { bip39Generate } from '@polkadot/wasm-crypto';
import { hasBigInt } from '@polkadot/util';
import { bip39Generate, isReady } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { generateMnemonic } from './bip39';
export type WordCount = 12 | 15 | 18 | 21 | 24;
@@ -30,7 +30,7 @@ const STRENGTH_MAP = {
* ```
*/
export function mnemonicGenerate (numWords: WordCount = 12, onlyJs?: boolean): string {
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? bip39Generate(numWords)
: generateMnemonic(STRENGTH_MAP[numWords]);
}
@@ -3,7 +3,7 @@
import { u8aToHex } from '@polkadot/util';
import tests from '../schnorrkel/keypair/testing';
import tests from '../sr25519/pair/testing.spec';
import { cryptoWaitReady } from '..';
import { mnemonicToEntropy } from './toEntropy';
@@ -1,13 +1,13 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { bip39ToEntropy } from '@polkadot/wasm-crypto';
import { hasBigInt } from '@polkadot/util';
import { bip39ToEntropy, isReady } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { mnemonicToEntropy as jsToEntropy } from './bip39';
export function mnemonicToEntropy (mnemonic: string, onlyJs?: boolean): Uint8Array {
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? bip39ToEntropy(mnemonic)
: jsToEntropy(mnemonic);
}
@@ -1,10 +1,9 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert } from '@polkadot/util';
import { bip39ToSeed } from '@polkadot/wasm-crypto';
import { assert, hasBigInt } from '@polkadot/util';
import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { mnemonicToSeedSync } from './bip39';
import { mnemonicValidate } from './validate';
@@ -29,13 +28,9 @@ export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs?:
assert(mnemonicValidate(mnemonic), 'Invalid bip39 mnemonic specified');
assert([32, 64].includes(byteLength), () => `Invalid seed length ${byteLength}, expected 32 or 64`);
if (byteLength === 32) {
return isWasmOnly(onlyJs)
return byteLength === 32
? !hasBigInt || (!onlyJs && isReady())
? bip39ToSeed(mnemonic, password)
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32);
} else if (byteLength === 64) {
return mnemonicToSeedSync(mnemonic, password);
}
return new Uint8Array();
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32)
: mnemonicToSeedSync(mnemonic, password);
}
@@ -3,7 +3,7 @@
import { u8aEq, u8aToHex } from '@polkadot/util';
import tests from '../schnorrkel/keypair/testing';
import tests from '../sr25519/pair/testing.spec';
import { cryptoWaitReady } from '..';
import { mnemonicToMiniSecret } from './toMiniSecret';
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import { assert, stringToU8a } from '@polkadot/util';
import { bip39ToMiniSecret } from '@polkadot/wasm-crypto';
import { bip39ToMiniSecret, isReady } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { pbkdf2Encode } from '../pbkdf2';
import { mnemonicToEntropy } from './toEntropy';
import { mnemonicValidate } from './validate';
@@ -12,7 +11,7 @@ import { mnemonicValidate } from './validate';
export function mnemonicToMiniSecret (mnemonic: string, password = '', onlyJs?: boolean): Uint8Array {
assert(mnemonicValidate(mnemonic), 'Invalid bip39 mnemonic specified');
if (isWasmOnly(onlyJs)) {
if (!onlyJs && isReady()) {
return bip39ToMiniSecret(mnemonic, password);
}
@@ -3,7 +3,7 @@
import { arrayRange, u8aEq } from '@polkadot/util';
import { cryptoWaitReady, mnemonicGenerate, mnemonicToMiniSecret, naclKeypairFromSeed, schnorrkelKeypairFromSeed } from '..';
import { cryptoWaitReady, ed25519PairFromSeed, mnemonicGenerate, mnemonicToMiniSecret, sr25519PairFromSeed } from '..';
// NOTE: This basically controls how long stuff runs for, YMMV
//
@@ -29,8 +29,8 @@ describe.each([true, false])('mnemonicToMiniSecret (compare), onlyJsMnemonic=%p'
// do iterations to check and re-check that all matches
it.concurrent.each(arrayRange(NUM_CHECKS))('check=%p', (count): void => {
const minisecret = mnemonicToMiniSecret(mnemonic, count ? `${count}` : '', onlyJsMnemonic);
const edpub = naclKeypairFromSeed(minisecret).publicKey;
const srpub = schnorrkelKeypairFromSeed(minisecret).publicKey;
const edpub = ed25519PairFromSeed(minisecret).publicKey;
const srpub = sr25519PairFromSeed(minisecret).publicKey;
const testmini = mnemonicToMiniSecret(mnemonic, count ? `${count}` : '', onlyJsMini);
@@ -41,13 +41,13 @@ describe.each([true, false])('mnemonicToMiniSecret (compare), onlyJsMnemonic=%p'
// compare the sr25519 keypair generated
expect(
u8aEq(srpub, schnorrkelKeypairFromSeed(testmini).publicKey)
u8aEq(srpub, sr25519PairFromSeed(testmini).publicKey)
).toEqual(true);
// compare ed both in WASM and JS
[true, false].forEach((onlyJsEd): void => {
expect(
u8aEq(edpub, naclKeypairFromSeed(testmini, onlyJsEd).publicKey)
u8aEq(edpub, ed25519PairFromSeed(testmini, onlyJsEd).publicKey)
).toEqual(true);
});
});
@@ -1,9 +1,9 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { bip39Validate } from '@polkadot/wasm-crypto';
import { hasBigInt } from '@polkadot/util';
import { bip39Validate, isReady } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { validateMnemonic } from './bip39';
/**
@@ -20,7 +20,7 @@ import { validateMnemonic } from './bip39';
* ```
*/
export function mnemonicValidate (mnemonic: string, onlyJs?: boolean): boolean {
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? bip39Validate(mnemonic)
: validateMnemonic(mnemonic);
}
@@ -1,9 +1,9 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { naclBoxKeypairFromSecret } from '..';
import { naclBoxPairFromSecret } from '..';
describe('naclBoxKeypairFromSecret', (): void => {
describe('naclBoxPairFromSecret', (): void => {
const secretKey = new Uint8Array([
18, 52, 86, 120, 144, 18, 52, 86,
120, 144, 18, 52, 86, 120, 144, 18,
@@ -13,7 +13,7 @@ describe('naclBoxKeypairFromSecret', (): void => {
it('generates a valid publicKey/secretKey pair', (): void => {
expect(
naclBoxKeypairFromSecret(secretKey)
naclBoxPairFromSecret(secretKey)
).toEqual({
publicKey: new Uint8Array([
206, 110, 228, 222, 155, 43, 229, 254,
@@ -6,7 +6,7 @@ import type { Keypair } from '../../types';
import nacl from 'tweetnacl';
/**
* @name naclBoxKeypairFromSecret
* @name naclBoxPairFromSecret
* @summary Creates a new public/secret box keypair from a secret.
* @description
* Returns a object containing a box `publicKey` & `secretKey` generated from the supplied secret.
@@ -14,11 +14,11 @@ import nacl from 'tweetnacl';
* <BR>
*
* ```javascript
* import { naclBoxKeypairFromSecret } from '@polkadot/util-crypto';
* import { naclBoxPairFromSecret } from '@polkadot/util-crypto';
*
* naclBoxKeypairFromSecret(...); // => { secretKey: [...], publicKey: [...] }
* naclBoxPairFromSecret(...); // => { secretKey: [...], publicKey: [...] }
* ```
*/
export function naclBoxKeypairFromSecret (secret: Uint8Array): Keypair {
export function naclBoxPairFromSecret (secret: Uint8Array): Keypair {
return nacl.box.keyPair.fromSecretKey(secret.slice(0, 32));
}
+2 -9
View File
@@ -2,17 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
/**
* @summary Implements [NaCl](http://nacl.cr.yp.to/) secret-key authenticated encryption, public-key authenticated encryption, hashing, and public-key signatures
* @summary Implements [NaCl](http://nacl.cr.yp.to/) secret-key authenticated encryption, public-key authenticated encryption
*/
export { naclDecrypt } from './decrypt';
export { naclEncrypt } from './encrypt';
export { naclKeypairFromRandom } from './keypair/fromRandom';
export { naclKeypairFromSecret } from './keypair/fromSecret';
export { naclKeypairFromSeed } from './keypair/fromSeed';
export { naclKeypairFromString } from './keypair/fromString';
export { naclSign } from './sign';
export { naclVerify } from './verify';
export { naclBoxKeypairFromSecret } from './box/fromSecret';
export { naclBoxPairFromSecret } from './box/fromSecret';
export { naclOpen } from './open';
export { naclSeal } from './seal';
export { convertSecretKeyToCurve25519, convertPublicKeyToCurve25519 } from './convertKey';
+14 -13
View File
@@ -1,17 +1,18 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { ed25519PairFromString } from '../ed25519';
import { keyExtractPath } from '../key';
import { keyFromPath } from '../key/fromPath';
import { naclBoxKeypairFromSecret, naclKeypairFromString, naclOpen, naclSeal } from '.';
import { naclBoxPairFromSecret, naclOpen, naclSeal } from '.';
describe('naclOpen', (): void => {
it('opens a sealed message by the sender', (): void => {
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
const sender = naclKeypairFromString('sender');
const receiver = naclKeypairFromString('receiver');
const senderBox = naclBoxKeypairFromSecret(sender.secretKey);
const receiverBox = naclBoxKeypairFromSecret(receiver.secretKey);
const sender = ed25519PairFromString('sender');
const receiver = ed25519PairFromString('receiver');
const senderBox = naclBoxPairFromSecret(sender.secretKey);
const receiverBox = naclBoxPairFromSecret(receiver.secretKey);
const { nonce, sealed } = naclSeal(message, senderBox.secretKey, receiverBox.publicKey);
expect(
@@ -22,20 +23,20 @@ describe('naclOpen', (): void => {
});
it('polkadot does double ratchet', () => {
const sender = naclKeypairFromString('sender');
const receiver = naclKeypairFromString('receiver');
const sender = ed25519PairFromString('sender');
const receiver = ed25519PairFromString('receiver');
// Make id key for identifying each other
const senderIdKey = keyFromPath(sender, keyExtractPath('//1//1//1//1').path, 'ed25519');
const receiverIdKey = keyFromPath(receiver, keyExtractPath('//2//2//2//2').path, 'ed25519');
// Receiver sends encrypting public key to receive message to decrypt with his private key
const receiverIdBoxKey = naclBoxKeypairFromSecret(receiverIdKey.secretKey);
const receiverIdBoxKey = naclBoxPairFromSecret(receiverIdKey.secretKey);
// console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey.publicKey.toString()}`);
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
const senderIdBoxKey = naclBoxKeypairFromSecret(senderIdKey.secretKey);
const senderIdBoxKey = naclBoxPairFromSecret(senderIdKey.secretKey);
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
const { nonce, sealed } = naclSeal(message, senderIdBoxKey.secretKey, receiverIdBoxKey.publicKey);
@@ -51,12 +52,12 @@ describe('naclOpen', (): void => {
const receiverIdKey2 = keyFromPath(receiver, keyExtractPath('//2//2//2//3').path, 'ed25519');
// Receiver sends encrypting public key to receive message to decrypt with his private key
const receiverIdBoxKey2 = naclBoxKeypairFromSecret(receiverIdKey2.secretKey);
const receiverIdBoxKey2 = naclBoxPairFromSecret(receiverIdKey2.secretKey);
// console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey2.publicKey.toString()}`);
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
const senderIdBoxKey2 = naclBoxKeypairFromSecret(senderIdKey2.secretKey);
const senderIdBoxKey2 = naclBoxPairFromSecret(senderIdKey2.secretKey);
const message2 = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
let ctx = naclSeal(message2, senderIdBoxKey2.secretKey, receiverIdBoxKey2.publicKey);
const sealed2 = ctx.sealed;
@@ -70,12 +71,12 @@ describe('naclOpen', (): void => {
// This time derive key for each message to send and receive
// Receiver sends encrypting public key to receive message to decrypt with his private key
const receiverIdBoxKey2One = naclBoxKeypairFromSecret(receiverIdBoxKey2.secretKey);
const receiverIdBoxKey2One = naclBoxPairFromSecret(receiverIdBoxKey2.secretKey);
// console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey2One.publicKey.toString()}`);
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
const senderIdBoxKey2One = naclBoxKeypairFromSecret(senderIdBoxKey2.secretKey);
const senderIdBoxKey2One = naclBoxPairFromSecret(senderIdBoxKey2.secretKey);
const message3 = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
ctx = naclSeal(message3, senderIdBoxKey2One.secretKey, receiverIdBoxKey2One.publicKey);
+6 -5
View File
@@ -1,15 +1,16 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { naclBoxKeypairFromSecret, naclKeypairFromString, naclSeal } from '.';
import { ed25519PairFromString } from '../ed25519';
import { naclBoxPairFromSecret, naclSeal } from '.';
describe('naclSeal', (): void => {
it('seals a message', (): void => {
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
const sender = naclKeypairFromString('sender');
const receiver = naclKeypairFromString('receiver');
const senderBox = naclBoxKeypairFromSecret(sender.secretKey);
const receiverBox = naclBoxKeypairFromSecret(receiver.secretKey);
const sender = ed25519PairFromString('sender');
const receiver = ed25519PairFromString('receiver');
const senderBox = naclBoxPairFromSecret(sender.secretKey);
const receiverBox = naclBoxPairFromSecret(receiver.secretKey);
const senderBoxSecret = senderBox.secretKey;
const receiverBoxPublic = receiverBox.publicKey;
@@ -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 ![Node CI](https://github.com/paulmillr/noble-hashes/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
Fast, secure & minimal JS implementation of SHA2, SHA3, RIPEMD, BLAKE2/3, HMAC, HKDF, PBKDF2 & Scrypt.
- **noble** family, zero dependencies
- 🔻 Helps JS bundlers with lack of entry point; ensures small size of your app
- 🔁 No unrolled loops: makes it much easier to verify and reduces source code size 2-5x
- 🏎 Ultra-fast, hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness: chained tests, sliding window tests, DoS tests
- 🧪 Differential fuzzing ensures even more correctness with [cryptofuzz](https://github.com/guidovranken/cryptofuzz)
- 🔑 Scrypt supports `n: 2**22` with 4GB arrays while other implementations crash on `2**21` or even `2**20`, `maxmem` security param, `onProgress` callback
- 🦘 SHA3 supports Keccak, TupleHash, KangarooTwelve and MarsupilamiFourteen
- All primitives are just ~2KLOC / 41KB minified / 14KB gzipped. SHA256-only is 240LOC / 7KB minified / 3KB gzipped
The library's initial development was funded by [Ethereum Foundation](https://ethereum.org/).
### This library belongs to _noble_ crypto
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, small files
- Easily auditable TypeScript/JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out all libraries:
[secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519),
[bls12-381](https://github.com/paulmillr/noble-bls12-381),
[hashes](https://github.com/paulmillr/noble-hashes)
## Usage
Use NPM in node.js / browser, or include single file from
[GitHub's releases page](https://github.com/paulmillr/noble-hashes/releases):
> npm install ../noble-hashes
The library does not have an entry point. It allows you to select specific primitives and drop everything else. If you only want to use sha256, just use the library with rollup or other bundlers. This is done to make your bundles tiny.
```js
const { sha256 } = require('../noble-hashes/lib/sha256');
console.log(sha256(new Uint8Array([1, 2, 3])));
// Uint8Array(32) [3, 144, 88, 198, 242, 192, 203, 73, ...]
// you could also pass strings that will be UTF8-encoded to Uint8Array
console.log(sha256('abc'))); // == sha256(new TextEncoder().encode('abc'))
// sha384 is here, because it uses same internals as sha512
const { sha512, sha512_256, sha384 } = require('../noble-hashes/lib/sha512');
// prettier-ignore
const {
sha3_224, sha3_256, sha3_384, sha3_512,
keccak_224, keccak_256, keccak_384, keccak_512,
shake128, shake256
} = require('../noble-hashes/lib/sha3');
// prettier-ignore
const {
cshake128, cshake256, kmac128, kmac256,
k12, m14,
tuplehash256, parallelhash256, keccakprg
} = require('../noble-hashes/lib/sha3-addons');
const { ripemd160 } = require('../noble-hashes/lib/ripemd160');
const { blake3 } = require('../noble-hashes/lib/blake3');
const { blake2b } = require('../noble-hashes/lib/blake2b');
const { blake2s } = require('../noble-hashes/lib/blake2s');
const { hmac } = require('../noble-hashes/lib/hmac');
const { hkdf } = require('../noble-hashes/lib/hkdf');
const { pbkdf2, pbkdf2Async } = require('../noble-hashes/lib/pbkdf2');
const { scrypt, scryptAsync } = require('../noble-hashes/lib/scrypt');
// small utility method that converts bytes to hex
const { bytesToHex as toHex } = require('../noble-hashes/lib/utils');
console.log(toHex(sha256('abc')));
// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
```
## API
All hash functions:
- can be called directly, with `Uint8Array`.
- return `Uint8Array`
- can receive `string`, which is automatically converted to `Uint8Array`
via utf8 encoding **(not hex)**
- support hashing 4GB of data per update on 64-bit systems (unlimited with streaming)
```ts
function hash(message: Uint8Array | string): Uint8Array;
hash(new Uint8Array([1, 3]));
hash('string') == hash(new TextEncoder().encode('string'));
```
All hash functions can be constructed via `hash.create()` method:
- the result is `Hash` subclass instance, which has `update()` and `digest()` methods
- `digest()` finalizes the hash and makes it no longer usable
```ts
hash
.create()
.update(new Uint8Array([1, 3]))
.digest();
```
_Some_ hash functions can also receive `options` object, which can be either passed as a:
- second argument to hash function: `blake3('abc', { key: 'd', dkLen: 32 })`
- first argument to class initializer: `blake3.create({ context: 'e', dkLen: 32 })`
## Modules
- [SHA2 (sha256, sha384, sha512, sha512_256)](#sha2-sha256-sha384-sha512-sha512_256)
- [SHA3 (FIPS, SHAKE, Keccak)](#sha3-fips-shake-keccak)
- [SHA3 Addons (cSHAKE, KMAC, KangarooTwelve, MarsupilamiFourteen)](#sha3-addons-cshake-kmac-tuplehash-parallelhash-kangarootwelve-marsupilamifourteen)
- [RIPEMD-160](#ripemd-160)
- [BLAKE2b, BLAKE2s](#blake2b-blake2s)
- [BLAKE3](#blake3)
- [HMAC](#hmac)
- [HKDF](#hkdf)
- [PBKDF2](#pbkdf2)
- [Scrypt](#scrypt)
- [utils](#utils)
##### SHA2 (sha256, sha384, sha512, sha512_256)
```typescript
import { sha256 } from '../noble-hashes/lib/sha256.js';
const h1a = sha256('abc');
const h1b = sha256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
```typescript
import { sha512 } from '../noble-hashes/lib/sha512.js';
const h2a = sha512('abc');
const h2b = sha512
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
// SHA512/256 variant
import { sha512_256 } from '../noble-hashes/lib/sha512.js';
const h3a = sha512_256('abc');
const h3b = sha512_256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
// SHA384
import { sha384 } from '../noble-hashes/lib/sha512.js';
const h4a = sha384('abc');
const h4b = sha384
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and [the paper on SHA512/256](https://eprint.iacr.org/2010/548.pdf).
##### SHA3 (FIPS, SHAKE, Keccak)
```typescript
import {
sha3_224,
sha3_256,
sha3_384,
sha3_512,
keccak_224,
keccak_256,
keccak_384,
keccak_512,
shake128,
shake256,
} from '../noble-hashes/lib/sha3.js';
const h5a = sha3_256('abc');
const h5b = sha3_256
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
const h6a = keccak_256('abc');
const h7a = shake128('abc', { dkLen: 512 });
const h7b = shake256('abc', { dkLen: 512 });
```
See ([FIPS PUB 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf), [Website](https://keccak.team/keccak.html)).
Check out [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub)
##### SHA3 Addons (cSHAKE, KMAC, TupleHash, ParallelHash, KangarooTwelve, MarsupilamiFourteen)
```typescript
import {
cshake128,
cshake256,
kmac128,
kmac256,
k12,
m14,
tuplehash128,
tuplehash256,
parallelhash128,
parallelhash256,
keccakprg,
} from '../noble-hashes/lib/sha3-addons.js';
const h7c = cshake128('abc', { personalization: 'def' });
const h7d = cshake256('abc', { personalization: 'def' });
const h7e = kmac128('key', 'message');
const h7f = kmac256('key', 'message');
const h7h = k12('abc');
const h7g = m14('abc');
const h7i = tuplehash128(['ab', 'c']); // tuplehash(['ab', 'c']) !== tuplehash(['a', 'bc']) !== tuplehash(['abc'])
// Same as k12/blake3, but without reduced number of rounds. Doesn't speedup anything due lack of SIMD and threading,
// added for compatibility.
const h7j = parallelhash128('abc', { blockLen: 8 });
// pseudo-random generator, first argument is capacity. XKCP recommends 254 bits capacity for 128-bit security strength.
// * with a capacity of 254 bits.
const p = keccakprg(254);
p.feed('test');
const rand1b = p.fetch(1);
```
- Full [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf): cSHAKE, KMAC, TupleHash, ParallelHash + XOF variants
- 🦘 K12 ([KangarooTwelve Paper](https://keccak.team/files/KangarooTwelve.pdf), [RFC Draft](https://www.ietf.org/archive/id/draft-irtf-cfrg-kangarootwelve-06.txt)) and M14 aka MarsupilamiFourteen are basically parallel versions of Keccak with reduced number of rounds (same as Blake3 and ParallelHash).
- [KeccakPRG](https://keccak.team/files/CSF-0.1.pdf): Pseudo-random generator based on Keccak
##### RIPEMD-160
```typescript
import { ripemd160 } from '../noble-hashes/lib/ripemd160.js';
// function ripemd160(data: Uint8Array): Uint8Array;
const hash8 = ripemd160('abc');
const hash9 = ripemd160()
.create()
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 2286](https://datatracker.ietf.org/doc/html/rfc2286), [Website](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html)
##### BLAKE2b, BLAKE2s
```typescript
import { blake2b } from '../noble-hashes/lib/blake2b.js';
import { blake2s } from '../noble-hashes/lib/blake2s.js';
const h10a = blake2s('abc');
const b2params = { key: new Uint8Array([1]), personalization: t, salt: t, dkLen: 32 };
const h10b = blake2s('abc', b2params);
const h10c = blake2s
.create(b2params)
.update(Uint8Array.from([1, 2, 3]))
.digest();
```
See [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693), [Website](https://www.blake2.net).
##### BLAKE3
```typescript
import { blake3 } from '../noble-hashes/lib/blake3.js';
// All params are optional
const h11 = blake3('abc', { dkLen: 256, key: 'def', context: 'fji' });
```
See [Website](https://blake3.io).
##### HMAC
```typescript
import { hmac } from '../noble-hashes/lib/hmac.js';
import { sha256 } from '../noble-hashes/lib/sha256.js';
const mac1 = hmac(sha256, 'key', 'message');
const mac2 = hmac.create(sha256, Uint8Array.from([1, 2, 3])).update(Uint8Array.from([4, 5, 6]).digest();
```
Matches [RFC 2104](https://datatracker.ietf.org/doc/html/rfc2104).
##### HKDF
```typescript
import { hkdf } from '../noble-hashes/lib/kdf.js';
import { sha256 } from '../noble-hashes/lib/sha256.js';
import { randomBytes } from '../noble-hashes/utils.js';
const inputKey = randomBytes(32);
const salt = randomBytes(32);
const info = 'abc';
const dkLen = 32;
const hk1 = hkdf(sha256, inputKey, salt, info, dkLen);
// == same as
import { hkdf_extract, hkdf_expand } from '../noble-hashes/lib/kdf.js';
import { sha256 } from '../noble-hashes/lib/sha256.js';
const prk = hkdf_extract(sha256, inputKey, salt);
const hk2 = hkdf_expand(sha256, prk, info, dkLen);
```
Matches [RFC 5869](https://datatracker.ietf.org/doc/html/rfc5869).
##### PBKDF2
```typescript
import { pbkdf2, pbkdf2Async } from '../noble-hashes/lib/pbkdf2.js';
import { sha256 } from '../noble-hashes/lib/sha256.js';
const pbkey1 = pbkdf2(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey2 = await pbkdf2Async(sha256, 'password', 'salt', { c: 32, dkLen: 32 });
const pbkey3 = await pbkdf2Async(sha256, Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
c: 32,
dkLen: 32,
});
```
Matches [RFC 2898](https://datatracker.ietf.org/doc/html/rfc2898).
##### Scrypt
```typescript
import { scrypt, scryptAsync } from '../noble-hashes/lib/scrypt.js';
const scr1 = scrypt('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr2 = await scryptAsync('password', 'salt', { N: 2 ** 16, r: 8, p: 1, dkLen: 32 });
const scr3 = await scryptAsync(Uint8Array.from([1, 2, 3]), Uint8Array.from([4, 5, 6]), {
N: 2 ** 22,
r: 8,
p: 1,
dkLen: 32,
onProgress(percentage) {
console.log('progress', percentage);
},
maxmem: 2 ** 32 + 128 * 8 * 1, // N * r * p * 128 + (128*r*p)
});
```
Matches [RFC 7914](https://datatracker.ietf.org/doc/html/rfc7914), [Website](https://www.tarsnap.com/scrypt.html)
- `N, r, p` are work factors. To understand them, see [the blog post](https://blog.filippo.io/the-scrypt-parameters/).
- `dkLen` is the length of output bytes
- It is common to use N from `2**10` to `2**22` and `{r: 8, p: 1, dkLen: 32}`
- `onProgress` can be used with async version of the function to report progress to a user.
Memory usage of scrypt is calculated with the formula `N * r * p * 128 + (128 * r * p)`, which means
`{N: 2 ** 22, r: 8, p: 1}` will use 4GB + 1KB of memory. To prevent DoS, we limit scrypt to `1GB + 1KB` of RAM used,
which corresponds to `{N: 2 ** 20, r: 8, p: 1}`. If you want to use higher values, increase `maxmem` using the formula above.
_Note:_ noble supports `2**22` (4GB RAM) which is the highest amount amongst JS libs. Many other implementations don't support it.
We cannot support `2**23`, because there is a limitation in JS engines that makes allocating
arrays bigger than 4GB impossible, but we're looking into other possible solutions.
##### utils
```typescript
import { bytesToHex as toHex, randomBytes } from '../noble-hashes/lib/scrypt.js';
console.log(toHex(randomBytes(32)));
```
- `bytesToHex` will convert `Uint8Array` to a hex string
- `randomBytes(bytes)` will produce cryptographically secure random `Uint8Array` of length `bytes`
## Security
Noble is production-ready.
The library will be audited by an independent security firm in the next few months.
The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
A note on [timing attacks](https://en.wikipedia.org/wiki/Timing_attack): _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading rootkits with every `npm install`. Our goal is to minimize this attack vector.
## Speed
Benchmarks measured on Apple M1 with macOS 12 using 32-byte inputs.
Note that PBKDF2 and Scrypt are tested with extremely high work factor.
To run benchmarks, execute `npm run bench-install` and then `npm run bench`
```
SHA256 x 1,131,221 ops/sec @ 884ns/op
SHA384 x 452,284 ops/sec @ 2μs/op
SHA512 x 451,059 ops/sec @ 2μs/op
SHA3-256, keccak256, shake256 x 185,494 ops/sec @ 5μs/op
Kangaroo12 x 300,480 ops/sec @ 3μs/op
Marsupilami14 x 269,614 ops/sec @ 3μs/op
BLAKE2b x 291,375 ops/sec @ 3μs/op
BLAKE2s x 505,561 ops/sec @ 1μs/op
BLAKE3 x 576,036 ops/sec @ 1μs/op
HMAC-SHA256 x 342,583 ops/sec @ 2μs/op
RIPEMD160 x 1,191,895 ops/sec @ 839ns/op
HKDF-SHA256 x 115,500 ops/sec @ 8μs/op
PBKDF2-HMAC-SHA256 262144 x 2 ops/sec @ 338ms/op
PBKDF2-HMAC-SHA512 262144 x 0 ops/sec @ 1024ms/op
Scrypt r: 8, p: 1, n: 262144 x 1 ops/sec @ 637ms/op
```
Compare to native node.js implementation that uses C bindings instead of pure-js code:
```
SHA256 32B native x 1,164,144 ops/sec @ 859ns/op
SHA384 32B native x 938,086 ops/sec @ 1μs/op
SHA512 32B native x 946,969 ops/sec @ 1μs/op
SHA3 32B native x 879,507 ops/sec @ 1μs/op
keccak, k12, m14 are not implemented
BLAKE2b 32B native x 879,507 ops/sec @ 1μs/op
BLAKE2s 32B native x 977,517 ops/sec @ 1μs/op
BLAKE3 is not implemented
RIPEMD160 32B native x 913,242 ops/sec @ 1μs/op
HMAC-SHA256 32B native x 755,287 ops/sec @ 1μs/op
HKDF-SHA256 32B native x 207,856 ops/sec @ 4μs/op
PBKDF2-HMAC-SHA256 262144 native x 23 ops/sec @ 42ms/op
Scrypt 262144 native x 1 ops/sec @ 564ms/op
Scrypt 262144 scrypt.js x 0 ops/sec @ 1678ms/op
```
It is possible to [make this library 4x+ faster](./test/benchmark/README.md) by
_doing code generation of full loop unrolls_. We've decided against it. Reasons:
- the library must be auditable, with minimum amount of code, and zero dependencies
- most method invocations with the lib are going to be something like hashing 32b to 64kb of data
- hashing big inputs is 10x faster with low-level languages, which means you should probably pick 'em instead
The current performance is good enough when compared to other projects; SHA256 takes only 900 nanoseconds to run.
## Contributing & testing
1. Clone the repository.
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests. See [our approach to testing](./test/README.md)
5. `npm run test-dos` will test against DoS; by measuring function complexity. **Takes ~20 minutes**
6. `npm run test-big` will execute hashing on 4GB inputs,
scrypt with 1024 different `N, r, p` combinations, etc. **Takes several hours**. Using 8-32+ core CPU helps.
## License
The MIT License (MIT)
Copyright (c) 2021 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
See LICENSE file.
@@ -0,0 +1,128 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { assertNumber, Hash, Input, toBytes, u32 } from './utils';
// prettier-ignore
export const SIGMA = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
// For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1].
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
]);
export type BlakeOpts = {
dkLen?: number;
key?: Input;
salt?: Input;
personalization?: Input;
};
export abstract class BLAKE2<T extends BLAKE2<T>> extends Hash<T> {
protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void;
protected abstract get(): number[];
protected abstract set(...args: number[]): void;
abstract override destroy(): void;
protected buffer: Uint8Array;
protected buffer32: Uint32Array;
protected length: number = 0;
protected pos: number = 0;
protected finished = false;
protected destroyed = false;
constructor(
readonly blockLen: number,
public outputLen: number,
opts: BlakeOpts = {},
keyLen: number,
saltLen: number,
persLen: number
) {
super();
assertNumber(blockLen);
assertNumber(outputLen);
assertNumber(keyLen);
if (outputLen < 0 || outputLen > keyLen)
throw new Error('Blake2: outputLen bigger than keyLen');
if (opts.key !== undefined && (opts.key.length < 1 || opts.key.length > keyLen))
throw new Error(`Key should be up 1..${keyLen} byte long or undefined`);
if (opts.salt !== undefined && opts.salt.length !== saltLen)
throw new Error(`Salt should be ${saltLen} byte long or undefined`);
if (opts.personalization !== undefined && opts.personalization.length !== persLen)
throw new Error(`Personalization should be ${persLen} byte long or undefined`);
this.buffer32 = u32((this.buffer = new Uint8Array(blockLen)));
}
update(data: Input) {
if (this.destroyed) throw new Error('instance is destroyed');
// Main difference with other hashes: there is flag for last block,
// so we cannot process current block before we know that there
// is the next one. This significantly complicates logic and reduces ability
// to do zero-copy processing
const { finished, blockLen, buffer, buffer32 } = this;
if (finished) throw new Error('digest() was already called');
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len; ) {
// If buffer is full and we still have input (don't process last block, same as blake2s)
if (this.pos === blockLen) {
this.compress(buffer32, 0, false);
this.pos = 0;
}
const take = Math.min(blockLen - this.pos, len - pos);
const dataOffset = data.byteOffset + pos;
// full block && aligned to 4 bytes && not last in input
if (take === blockLen && !(dataOffset % 4) && pos + take < len) {
const data32 = new Uint32Array(data.buffer, dataOffset, Math.floor((len - pos) / 4));
for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {
this.length += blockLen;
this.compress(data32, pos32, false);
}
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
this.length += take;
pos += take;
}
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
throw new Error('_Blake2: Invalid output buffer');
const { finished, pos, buffer32 } = this;
if (finished) throw new Error('digest() was already called');
this.finished = true;
// Padding
this.buffer.subarray(pos).fill(0);
this.compress(buffer32, 0, true);
const out32 = u32(out);
this.get().forEach((v, i) => (out32[i] = v));
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to?: T): T {
const { buffer, length, finished, destroyed, outputLen, pos } = this;
to ||= new (this.constructor as any)({ dkLen: outputLen }) as T;
to.set(...this.get());
to.length = length;
to.finished = finished;
to.destroyed = destroyed;
to.outputLen = outputLen;
to.buffer.set(buffer);
to.pos = pos;
return to;
}
}
@@ -0,0 +1,116 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { _n, Hash, createView, Input, toBytes } from './utils';
const _32n = _n(32);
const _u32_max = _n(0xffffffff);
// Polyfill for Safari 14
function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void {
if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const [h, l] = isLE ? [4, 0] : [0, 4];
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
// Base SHA2 class (RFC 6234)
export abstract class SHA2<T extends SHA2<T>> extends Hash<T> {
protected abstract process(buf: DataView, offset: number): void;
protected abstract get(): number[];
protected abstract set(...args: number[]): void;
abstract override destroy(): void;
protected abstract roundClean(): void;
// For partial updates less than block size
protected buffer: Uint8Array;
protected view: DataView;
protected finished = false;
protected length = 0;
protected pos = 0;
protected destroyed = false;
constructor(
readonly blockLen: number,
public outputLen: number,
readonly padOffset: number,
readonly isLE: boolean
) {
super();
this.buffer = new Uint8Array(blockLen);
this.view = createView(this.buffer);
}
update(data: Input): this {
if (this.destroyed) throw new Error('instance is destroyed');
const { view, buffer, blockLen, finished } = this;
if (finished) throw new Error('digest() was already called');
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len; ) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input, cast it to view and process
if (take === blockLen) {
const dataView = createView(data);
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(view, 0);
this.pos = 0;
}
}
this.length += data.length;
this.roundClean();
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length < this.outputLen)
throw new Error('_Sha2: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.finished = true;
// Padding
// We can avoid allocation of buffer for padding completely if it
// was previously not allocated here. But it won't change performance.
const { buffer, view, blockLen, isLE } = this;
let { pos } = this;
// append the bit '1' to the message
buffer[pos++] = 0b10000000;
this.buffer.subarray(pos).fill(0);
// we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
if (this.padOffset > blockLen - pos) {
this.process(view, 0);
pos = 0;
}
// Pad until full block byte with zeros
for (let i = pos; i < blockLen; i++) buffer[i] = 0;
// NOTE: sha512 requires length to be 128bit integer, but length in JS will overflow before that
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
// So we just write lowest 64bit of that value.
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
this.process(view, 0);
const oview = createView(out);
this.get().forEach((v, i) => oview.setUint32(4 * i, v, isLE));
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
_cloneInto(to?: T): T {
to ||= new (this.constructor as any)() as T;
to.set(...this.get());
const { blockLen, buffer, length, finished, destroyed, pos } = this;
to.length = length;
to.pos = pos;
to.finished = finished;
to.destroyed = destroyed;
if (length % blockLen) to.buffer.set(buffer);
return to;
}
}
@@ -0,0 +1,60 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { _n } from './utils';
const U32_MASK64 = _n(2 ** 32 - 1);
const _32n = _n(32);
export function fromBig(n: bigint, le = false) {
if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };
return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
}
export function split(lst: bigint[], le = false) {
let [Ah, Al] = [new Uint32Array(lst.length), new Uint32Array(lst.length)];
for (let i = 0; i < lst.length; i++) {
const { h, l } = fromBig(lst[i], le);
[Ah[i], Al[i]] = [h, l];
}
return [Ah, Al];
}
export const toBig = (h: number, l: number) => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);
// for Shift in [0, 32)
export const shrSH = (h: number, l: number, s: number) => h >>> s;
export const shrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
// Right rotate for Shift in [1, 32)
export const rotrSH = (h: number, l: number, s: number) => (h >>> s) | (l << (32 - s));
export const rotrSL = (h: number, l: number, s: number) => (h << (32 - s)) | (l >>> s);
// Right rotate for Shift in (32, 64), NOTE: 32 is special case.
export const rotrBH = (h: number, l: number, s: number) => (h << (64 - s)) | (l >>> (s - 32));
export const rotrBL = (h: number, l: number, s: number) => (h >>> (s - 32)) | (l << (64 - s));
// Right rotate for shift===32 (just swaps l&h)
export const rotr32H = (h: number, l: number) => l;
export const rotr32L = (h: number, l: number) => h;
// Left rotate for Shift in [1, 32)
export const rotlSH = (h: number, l: number, s: number) => (h << s) | (l >>> (32 - s));
export const rotlSL = (h: number, l: number, s: number) => (l << s) | (h >>> (32 - s));
// Left rotate for Shift in (32, 64), NOTE: 32 is special case.
export const rotlBH = (h: number, l: number, s: number) => (l << (s - 32)) | (h >>> (64 - s));
export const rotlBL = (h: number, l: number, s: number) => (h << (s - 32)) | (l >>> (64 - s));
// JS uses 32-bit signed integers for bitwise operations which means we cannot
// simple take carry out of low bit sum by shift, we need to use division.
export function add(Ah: number, Al: number, Bh: number, Bl: number) {
const l = (Al >>> 0) + (Bl >>> 0);
return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };
}
// Addition with more than 2 elements
export const add3L = (Al: number, Bl: number, Cl: number) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
export const add3H = (low: number, Ah: number, Bh: number, Ch: number) =>
(Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;
export const add4L = (Al: number, Bl: number, Cl: number, Dl: number) =>
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
export const add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number) =>
(Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;
export const add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number) =>
(Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
export const add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) =>
(Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;
@@ -0,0 +1,202 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import * as blake2 from './_blake2';
import * as u64 from './_u64';
import { toBytes, u32, wrapConstructorWithOpts } from './utils';
// Same as SHA-512 but LE
// prettier-ignore
const IV = new Uint32Array([
0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,
0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19
]);
// Temporary buffer
const BUF = new Uint32Array(32);
// Mixing function G splitted in two halfs
function G1(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
// NOTE: V is LE here
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
// v[a] = (v[a] + v[b] + x) | 0;
let ll = u64.add3L(Al, Bl, Xl);
Ah = u64.add3H(ll, Ah, Bh, Xh);
Al = ll | 0;
// v[d] = rotr(v[d] ^ v[a], 32)
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });
// v[c] = (v[c] + v[d]) | 0;
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
// v[b] = rotr(v[b] ^ v[c], 24)
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) });
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
}
function G2(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {
// NOTE: V is LE here
const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore
let Al = BUF[2 * a], Ah = BUF[2 * a + 1]; // prettier-ignore
let Bl = BUF[2 * b], Bh = BUF[2 * b + 1]; // prettier-ignore
let Cl = BUF[2 * c], Ch = BUF[2 * c + 1]; // prettier-ignore
let Dl = BUF[2 * d], Dh = BUF[2 * d + 1]; // prettier-ignore
// v[a] = (v[a] + v[b] + x) | 0;
let ll = u64.add3L(Al, Bl, Xl);
Ah = u64.add3H(ll, Ah, Bh, Xh);
Al = ll | 0;
// v[d] = rotr(v[d] ^ v[a], 16)
({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });
// v[c] = (v[c] + v[d]) | 0;
({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));
// v[b] = rotr(v[b] ^ v[c], 63)
({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) });
(BUF[2 * a] = Al), (BUF[2 * a + 1] = Ah);
(BUF[2 * b] = Bl), (BUF[2 * b + 1] = Bh);
(BUF[2 * c] = Cl), (BUF[2 * c + 1] = Ch);
(BUF[2 * d] = Dl), (BUF[2 * d + 1] = Dh);
}
class BLAKE2b extends blake2.BLAKE2<BLAKE2b> {
// Same as SHA-512, but LE
private v0l = IV[0] | 0;
private v0h = IV[1] | 0;
private v1l = IV[2] | 0;
private v1h = IV[3] | 0;
private v2l = IV[4] | 0;
private v2h = IV[5] | 0;
private v3l = IV[6] | 0;
private v3h = IV[7] | 0;
private v4l = IV[8] | 0;
private v4h = IV[9] | 0;
private v5l = IV[10] | 0;
private v5h = IV[11] | 0;
private v6l = IV[12] | 0;
private v6h = IV[13] | 0;
private v7l = IV[14] | 0;
private v7h = IV[15] | 0;
constructor(opts: blake2.BlakeOpts = {}) {
super(128, opts.dkLen === undefined ? 64 : opts.dkLen, opts, 64, 16, 16);
const keyLength = opts.key ? opts.key.length : 0;
this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
if (opts.salt) {
const salt = u32(toBytes(opts.salt));
this.v4l ^= salt[0];
this.v4h ^= salt[1];
this.v5l ^= salt[2];
this.v5h ^= salt[3];
}
if (opts.personalization) {
const pers = u32(toBytes(opts.personalization));
this.v6l ^= pers[0];
this.v6h ^= pers[1];
this.v7l ^= pers[2];
this.v7h ^= pers[3];
}
if (opts.key) {
// Pad to blockLen and update
const tmp = new Uint8Array(this.blockLen);
tmp.set(toBytes(opts.key));
this.update(tmp);
}
}
// prettier-ignore
protected get(): [
number, number, number, number, number, number, number, number,
number, number, number, number, number, number, number, number
] {
let {v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h} = this;
return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];
}
// prettier-ignore
protected set(
v0l: number, v0h: number, v1l: number, v1h: number,
v2l: number, v2h: number, v3l: number, v3h: number,
v4l: number, v4h: number, v5l: number, v5h: number,
v6l: number, v6h: number, v7l: number, v7h: number
) {
this.v0l = v0l | 0;
this.v0h = v0h | 0;
this.v1l = v1l | 0;
this.v1h = v1h | 0;
this.v2l = v2l | 0;
this.v2h = v2h | 0;
this.v3l = v3l | 0;
this.v3h = v3h | 0;
this.v4l = v4l | 0;
this.v4h = v4h | 0;
this.v5l = v5l | 0;
this.v5h = v5h | 0;
this.v6l = v6l | 0;
this.v6h = v6h | 0;
this.v7l = v7l | 0;
this.v7h = v7h | 0;
}
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
this.get().forEach((v, i) => (BUF[i] = v)); // First half from state.
BUF.set(IV, 16); // Second half from IV.
let { h, l } = u64.fromBig(BigInt(this.length));
BUF[24] = IV[8] ^ l; // Low word of the offset.
BUF[25] = IV[9] ^ h; // High word.
// Invert all bits for last block
if (isLast) {
BUF[28] = ~BUF[28];
BUF[29] = ~BUF[29];
}
let j = 0;
const s = blake2.SIGMA;
for (let i = 0; i < 12; i++) {
G1(0, 4, 8, 12, msg, offset + 2 * s[j++]);
G2(0, 4, 8, 12, msg, offset + 2 * s[j++]);
G1(1, 5, 9, 13, msg, offset + 2 * s[j++]);
G2(1, 5, 9, 13, msg, offset + 2 * s[j++]);
G1(2, 6, 10, 14, msg, offset + 2 * s[j++]);
G2(2, 6, 10, 14, msg, offset + 2 * s[j++]);
G1(3, 7, 11, 15, msg, offset + 2 * s[j++]);
G2(3, 7, 11, 15, msg, offset + 2 * s[j++]);
G1(0, 5, 10, 15, msg, offset + 2 * s[j++]);
G2(0, 5, 10, 15, msg, offset + 2 * s[j++]);
G1(1, 6, 11, 12, msg, offset + 2 * s[j++]);
G2(1, 6, 11, 12, msg, offset + 2 * s[j++]);
G1(2, 7, 8, 13, msg, offset + 2 * s[j++]);
G2(2, 7, 8, 13, msg, offset + 2 * s[j++]);
G1(3, 4, 9, 14, msg, offset + 2 * s[j++]);
G2(3, 4, 9, 14, msg, offset + 2 * s[j++]);
}
this.v0l ^= BUF[0] ^ BUF[16];
this.v0h ^= BUF[1] ^ BUF[17];
this.v1l ^= BUF[2] ^ BUF[18];
this.v1h ^= BUF[3] ^ BUF[19];
this.v2l ^= BUF[4] ^ BUF[20];
this.v2h ^= BUF[5] ^ BUF[21];
this.v3l ^= BUF[6] ^ BUF[22];
this.v3h ^= BUF[7] ^ BUF[23];
this.v4l ^= BUF[8] ^ BUF[24];
this.v4h ^= BUF[9] ^ BUF[25];
this.v5l ^= BUF[10] ^ BUF[26];
this.v5h ^= BUF[11] ^ BUF[27];
this.v6l ^= BUF[12] ^ BUF[28];
this.v6h ^= BUF[13] ^ BUF[29];
this.v7l ^= BUF[14] ^ BUF[30];
this.v7h ^= BUF[15] ^ BUF[31];
BUF.fill(0);
}
destroy() {
this.destroyed = true;
this.buffer32.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
export const blake2b = wrapConstructorWithOpts<BLAKE2b, blake2.BlakeOpts>(
(opts) => new BLAKE2b(opts)
);
@@ -0,0 +1,135 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import { rotr, toBytes, wrapConstructorWithOpts, u32 } from './utils';
// Initial state:
// first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19)
// same as SHA-256
// prettier-ignore
export const IV = new Uint32Array([
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]);
// Mixing function G splitted in two halfs
function G1(a: number, b: number, c: number, d: number, x: number) {
a = (a + b + x) | 0;
d = rotr(d ^ a, 16);
c = (c + d) | 0;
b = rotr(b ^ c, 12);
return { a, b, c, d };
}
function G2(a: number, b: number, c: number, d: number, x: number) {
a = (a + b + x) | 0;
d = rotr(d ^ a, 8);
c = (c + d) | 0;
b = rotr(b ^ c, 7);
return { a, b, c, d };
}
// prettier-ignore
export function compress(s: Uint8Array, offset: number, msg: Uint32Array, rounds: number,
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number,
v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,
) {
let j = 0;
for (let i = 0; i < rounds; i++) {
({ a: v0, b: v4, c: v8, d: v12 } = G1(v0, v4, v8, v12, msg[offset + s[j++]]));
({ a: v0, b: v4, c: v8, d: v12 } = G2(v0, v4, v8, v12, msg[offset + s[j++]]));
({ a: v1, b: v5, c: v9, d: v13 } = G1(v1, v5, v9, v13, msg[offset + s[j++]]));
({ a: v1, b: v5, c: v9, d: v13 } = G2(v1, v5, v9, v13, msg[offset + s[j++]]));
({ a: v2, b: v6, c: v10, d: v14 } = G1(v2, v6, v10, v14, msg[offset + s[j++]]));
({ a: v2, b: v6, c: v10, d: v14 } = G2(v2, v6, v10, v14, msg[offset + s[j++]]));
({ a: v3, b: v7, c: v11, d: v15 } = G1(v3, v7, v11, v15, msg[offset + s[j++]]));
({ a: v3, b: v7, c: v11, d: v15 } = G2(v3, v7, v11, v15, msg[offset + s[j++]]));
({ a: v0, b: v5, c: v10, d: v15 } = G1(v0, v5, v10, v15, msg[offset + s[j++]]));
({ a: v0, b: v5, c: v10, d: v15 } = G2(v0, v5, v10, v15, msg[offset + s[j++]]));
({ a: v1, b: v6, c: v11, d: v12 } = G1(v1, v6, v11, v12, msg[offset + s[j++]]));
({ a: v1, b: v6, c: v11, d: v12 } = G2(v1, v6, v11, v12, msg[offset + s[j++]]));
({ a: v2, b: v7, c: v8, d: v13 } = G1(v2, v7, v8, v13, msg[offset + s[j++]]));
({ a: v2, b: v7, c: v8, d: v13 } = G2(v2, v7, v8, v13, msg[offset + s[j++]]));
({ a: v3, b: v4, c: v9, d: v14 } = G1(v3, v4, v9, v14, msg[offset + s[j++]]));
({ a: v3, b: v4, c: v9, d: v14 } = G2(v3, v4, v9, v14, msg[offset + s[j++]]));
}
return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };
}
class BLAKE2s extends blake2.BLAKE2<BLAKE2s> {
// Internal state, same as SHA-256
private v0 = IV[0] | 0;
private v1 = IV[1] | 0;
private v2 = IV[2] | 0;
private v3 = IV[3] | 0;
private v4 = IV[4] | 0;
private v5 = IV[5] | 0;
private v6 = IV[6] | 0;
private v7 = IV[7] | 0;
constructor(opts: blake2.BlakeOpts = {}) {
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, opts, 32, 8, 8);
const keyLength = opts.key ? opts.key.length : 0;
this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);
if (opts.salt) {
const salt = u32(toBytes(opts.salt));
this.v4 ^= salt[0];
this.v5 ^= salt[1];
}
if (opts.personalization) {
const pers = u32(toBytes(opts.personalization));
this.v6 ^= pers[0];
this.v7 ^= pers[1];
}
if (opts.key) {
// Pad to blockLen and update
const tmp = new Uint8Array(this.blockLen);
tmp.set(toBytes(opts.key));
this.update(tmp);
}
}
protected get(): [number, number, number, number, number, number, number, number] {
const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;
return [v0, v1, v2, v3, v4, v5, v6, v7];
}
// prettier-ignore
protected set(
v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number
) {
this.v0 = v0 | 0;
this.v1 = v1 | 0;
this.v2 = v2 | 0;
this.v3 = v3 | 0;
this.v4 = v4 | 0;
this.v5 = v5 | 0;
this.v6 = v6 | 0;
this.v7 = v7 | 0;
}
protected compress(msg: Uint32Array, offset: number, isLast: boolean) {
const { h, l } = u64.fromBig(BigInt(this.length));
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
compress(
blake2.SIGMA, offset, msg, 10,
this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7,
IV[0], IV[1], IV[2], IV[3], l ^ IV[4], h ^ IV[5], isLast ? ~IV[6] : IV[6], IV[7]
);
this.v0 ^= v0 ^ v8;
this.v1 ^= v1 ^ v9;
this.v2 ^= v2 ^ v10;
this.v3 ^= v3 ^ v11;
this.v4 ^= v4 ^ v12;
this.v5 ^= v5 ^ v13;
this.v6 ^= v6 ^ v14;
this.v7 ^= v7 ^ v15;
}
destroy() {
this.destroyed = true;
this.buffer32.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0);
}
}
export const blake2s = wrapConstructorWithOpts<BLAKE2s, blake2.BlakeOpts>(
(opts) => new BLAKE2s(opts)
);
@@ -0,0 +1,242 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import * as u64 from './_u64';
import * as blake2 from './_blake2';
import * as blake2s from './blake2s';
import { Input, u8, u32, toBytes, wrapConstructorWithOpts, assertNumber, HashXOF } from './utils';
// Flag bitset
enum Flags {
CHUNK_START = 1 << 0,
CHUNK_END = 1 << 1,
PARENT = 1 << 2,
ROOT = 1 << 3,
KEYED_HASH = 1 << 4,
DERIVE_KEY_CONTEXT = 1 << 5,
DERIVE_KEY_MATERIAL = 1 << 6,
}
const SIGMA: Uint8Array = (() => {
const Id = Array.from({ length: 16 }, (_, i) => i);
const permute = (arr: number[]) =>
[2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);
const res: number[] = [];
for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v);
return Uint8Array.from(res);
})();
// - key: is 256-bit key
// - context: string should be hardcoded, globally unique, and application - specific.
// A good default format for the context string is "[application] [commit timestamp] [purpose]"
// - Only one of 'key' (keyed mode) or 'context' (derive key mode) can be used at same time
export type Blake3Opts = { dkLen?: number; key?: Input; context?: Input };
// Why is this so slow? It should be 6x faster than blake2b.
// - There is only 30% reduction in number of rounds from blake2s
// - This function uses tree mode to achive parallelisation via SIMD and threading,
// however in JS we don't have threads and SIMD, so we get only overhead from tree structure
// - It is possible to speed it up via Web Workers, hovewer it will make code singnificantly more
// complicated, which we are trying to avoid, since this library is intended to be used
// for cryptographic purposes. Also, parallelization happens only on chunk level (1024 bytes),
// which won't really benefit small inputs.
class BLAKE3 extends blake2.BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {
private IV: Uint32Array;
private flags = 0 | 0;
private state: Uint32Array;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
private stack: Uint32Array[] = [];
// Output
private posOut = 0;
private bufferOut32 = new Uint32Array(16);
private bufferOut: Uint8Array;
private chunkOut = 0; // index of output chunk
private enableXOF = true;
constructor(opts: Blake3Opts = {}, flags = 0) {
super(64, opts.dkLen === undefined ? 32 : opts.dkLen, {}, Number.MAX_SAFE_INTEGER, 0, 0);
this.outputLen = opts.dkLen === undefined ? 32 : opts.dkLen;
assertNumber(this.outputLen);
if (opts.key !== undefined && opts.context !== undefined)
throw new Error('Blake3: only key or context can be specified at same time');
else if (opts.key !== undefined) {
const key = toBytes(opts.key);
if (key.length !== 32) throw new Error('Blake3: key should be 32 byte');
this.IV = u32(key);
this.flags = flags | Flags.KEYED_HASH;
} else if (opts.context !== undefined) {
const context_key = new BLAKE3({ dkLen: 32 }, Flags.DERIVE_KEY_CONTEXT)
.update(opts.context)
.digest();
this.IV = u32(context_key);
this.flags = flags | Flags.DERIVE_KEY_MATERIAL;
} else {
this.IV = blake2s.IV.slice();
this.flags = flags;
}
this.state = this.IV.slice();
this.bufferOut = u8(this.bufferOut32);
}
// Unused
protected get() {
return [];
}
protected set() {}
private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) {
const { state, pos } = this;
const { h, l } = u64.fromBig(BigInt(counter), true);
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
blake2s.compress(
SIGMA, bufPos, buf, 7,
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], h, l, pos, flags
);
state[0] = v0 ^ v8;
state[1] = v1 ^ v9;
state[2] = v2 ^ v10;
state[3] = v3 ^ v11;
state[4] = v4 ^ v12;
state[5] = v5 ^ v13;
state[6] = v6 ^ v14;
state[7] = v7 ^ v15;
}
protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false) {
// Compress last block
let flags = this.flags;
if (!this.chunkPos) flags |= Flags.CHUNK_START;
if (this.chunkPos === 15 || isLast) flags |= Flags.CHUNK_END;
if (!isLast) this.pos = this.blockLen;
this.b2Compress(this.chunksDone, flags, buf, bufPos);
this.chunkPos += 1;
// If current block is last in chunk (16 blocks), then compress chunks
if (this.chunkPos === 16 || isLast) {
let chunk = this.state;
this.state = this.IV.slice();
// If not the last one, compress only when there are trailing zeros in chunk counter
// chunks used as binary tree where current stack is path. Zero means current leaf is finished and can be compressed.
// 1 (001) - leaf not finished (just push current chunk to stack)
// 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back)
// 3 (011) - last leaf not finished
// 4 (100) - leafs finished at depth=1 and depth=2
for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {
if (!(last = this.stack.pop())) break;
this.buffer32.set(last, 0);
this.buffer32.set(chunk, 8);
this.pos = this.blockLen;
this.b2Compress(0, this.flags | Flags.PARENT, this.buffer32, 0);
chunk = this.state;
this.state = this.IV.slice();
}
this.chunksDone++;
this.chunkPos = 0;
this.stack.push(chunk);
}
this.pos = 0;
}
override _cloneInto(to?: BLAKE3): BLAKE3 {
to = super._cloneInto(to) as BLAKE3;
const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;
to.state.set(state.slice());
to.stack = stack.map((i) => Uint32Array.from(i));
to.IV.set(IV);
to.flags = flags;
to.chunkPos = chunkPos;
to.chunksDone = chunksDone;
to.posOut = posOut;
to.chunkOut = chunkOut;
to.enableXOF = this.enableXOF;
to.bufferOut32.set(this.bufferOut32);
return to;
}
destroy() {
this.destroyed = true;
this.state.fill(0);
this.buffer32.fill(0);
this.IV.fill(0);
this.bufferOut32.fill(0);
for (let i of this.stack) i.fill(0);
}
// Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)
private b2CompressOut() {
const { state, pos, flags, buffer32, bufferOut32 } = this;
const { h, l } = u64.fromBig(BigInt(this.chunkOut++));
// prettier-ignore
const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =
blake2s.compress(
SIGMA, 0, buffer32, 7,
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
blake2s.IV[0], blake2s.IV[1], blake2s.IV[2], blake2s.IV[3], l, h, pos, flags
);
bufferOut32[0] = v0 ^ v8;
bufferOut32[1] = v1 ^ v9;
bufferOut32[2] = v2 ^ v10;
bufferOut32[3] = v3 ^ v11;
bufferOut32[4] = v4 ^ v12;
bufferOut32[5] = v5 ^ v13;
bufferOut32[6] = v6 ^ v14;
bufferOut32[7] = v7 ^ v15;
bufferOut32[8] = state[0] ^ v8;
bufferOut32[9] = state[1] ^ v9;
bufferOut32[10] = state[2] ^ v10;
bufferOut32[11] = state[3] ^ v11;
bufferOut32[12] = state[4] ^ v12;
bufferOut32[13] = state[5] ^ v13;
bufferOut32[14] = state[6] ^ v14;
bufferOut32[15] = state[7] ^ v15;
this.posOut = 0;
}
protected finish() {
if (this.finished) return;
this.finished = true;
// Padding
this.buffer.fill(0, this.pos);
// Process last chunk
let flags = this.flags | Flags.ROOT;
if (this.stack.length) {
flags |= Flags.PARENT;
this.compress(this.buffer32, 0, true);
this.chunksDone = 0;
this.pos = this.blockLen;
} else {
flags |= (!this.chunkPos ? Flags.CHUNK_START : 0) | Flags.CHUNK_END;
}
this.flags = flags;
this.b2CompressOut();
}
private writeInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array)) throw new Error('Blake3: Invalid output buffer');
this.finish();
const { blockLen, bufferOut } = this;
for (let pos = 0, len = out.length; pos < len; ) {
if (this.posOut >= blockLen) this.b2CompressOut();
const take = Math.min(this.blockLen - this.posOut, len - pos);
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
this.posOut += take;
pos += take;
}
return out;
}
xofInto(out: Uint8Array): Uint8Array {
if (!this.enableXOF) throw new Error('XOF impossible after digest call');
return this.writeInto(out);
}
xof(bytes: number): Uint8Array {
assertNumber(bytes);
return this.xofInto(new Uint8Array(bytes));
}
override digestInto(out: Uint8Array) {
if (out.length < this.outputLen) throw new Error('Blake3: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.enableXOF = false;
this.writeInto(out);
this.destroy();
return out;
}
override digest() {
return this.digestInto(new Uint8Array(this.outputLen));
}
}
export const blake3 = wrapConstructorWithOpts<BLAKE3, Blake3Opts>((opts) => new BLAKE3(opts));
@@ -0,0 +1,62 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
// prettier-ignore
import {
assertHash, assertNumber, CHash, Input, toBytes
} from './utils';
import { hmac } from './hmac';
// HKDF (RFC 5869)
// HKDF-Extract(IKM, salt) -> PRK NOTE: arguments position differs from spec (IKM is first one, since it is not optional)
export function hkdf_extract(hash: CHash, ikm: Input, salt?: Input) {
assertHash(hash);
// NOTE: some libraries treats zero-length array as 'not provided', we don't, since we have undefined as 'not provided'
// More info: https://github.com/RustCrypto/KDFs/issues/15
if (salt === undefined) salt = new Uint8Array(hash.outputLen); // if not provided, it is set to a string of HashLen zeros
return hmac(hash, toBytes(salt), toBytes(ikm));
}
// HKDF-Expand(PRK, info, L) -> OKM
const HKDF_COUNTER = new Uint8Array([0]);
const EMPTY_BUFFER = new Uint8Array();
export function hkdf_expand(
hash: CHash,
prk: Input, // a pseudorandom key of at least HashLen octets (usually, the output from the extract step)
info?: Input, // optional context and application specific information (can be a zero-length string)
length: number = 32 // length of output keying material in octets
) {
assertHash(hash);
assertNumber(length);
if (length > 255 * hash.outputLen) throw new Error('Length should be <= 255*HashLen');
const blocks = Math.ceil(length / hash.outputLen);
if (info === undefined) info = EMPTY_BUFFER;
// first L(ength) octets of T
const okm = new Uint8Array(blocks * hash.outputLen);
// Re-use HMAC instance between blocks
const HMAC = hmac.init(hash, prk);
const HMACTmp = HMAC._cloneInto();
const T = new Uint8Array(HMAC.outputLen);
for (let counter = 0; counter < blocks; counter++) {
HKDF_COUNTER[0] = counter + 1;
// T(0) = empty string (zero length)
// T(N) = HMAC-Hash(PRK, T(N-1) | info | N)
HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)
.update(info)
.update(HKDF_COUNTER)
.digestInto(T);
okm.set(T, hash.outputLen * counter);
HMAC._cloneInto(HMACTmp);
}
HMAC.destroy();
HMACTmp.destroy();
T.fill(0);
return okm.slice(0, length);
}
// Extract+Expand
export const hkdf = (
hash: CHash,
ikm: Input,
salt: Input | undefined,
info: Input | undefined,
length: number
) => hkdf_expand(hash, hkdf_extract(hash, ikm, salt), info, length);
@@ -0,0 +1,78 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { assertHash, Hash, CHash, Input, toBytes } from './utils';
// HMAC (RFC 2104)
class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {
oHash: T;
iHash: T;
blockLen: number;
outputLen: number;
private finished = false;
private destroyed = false;
constructor(hash: CHash, _key: Input) {
super();
assertHash(hash);
const key = toBytes(_key);
this.iHash = hash.create() as T;
if (!(this.iHash instanceof Hash))
throw new TypeError('Expected instance of class which extends utils.Hash');
const blockLen = (this.blockLen = this.iHash.blockLen);
this.outputLen = this.iHash.outputLen;
const pad = new Uint8Array(blockLen);
// blockLen can be bigger than outputLen
pad.set(key.length > this.iHash.blockLen ? hash.create().update(key).digest() : key);
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;
this.iHash.update(pad);
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
this.oHash = hash.create() as T;
// Undo internal XOR && apply outer XOR
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;
this.oHash.update(pad);
pad.fill(0);
}
update(buf: Input) {
if (this.destroyed) throw new Error('instance is destroyed');
this.iHash.update(buf);
return this;
}
digestInto(out: Uint8Array) {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array) || out.length !== this.outputLen)
throw new Error('HMAC: Invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.finished = true;
this.iHash.digestInto(out);
this.oHash.update(out);
this.oHash.digestInto(out);
this.destroy();
}
digest() {
const out = new Uint8Array(this.oHash.outputLen);
this.digestInto(out);
return out;
}
_cloneInto(to?: HMAC<T>): HMAC<T> {
// Create new instance without calling constructor since key already in state and we don't know it.
to ||= Object.create(Object.getPrototypeOf(this), {});
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
to = to as this;
to.finished = finished;
to.destroyed = destroyed;
to.blockLen = blockLen;
to.outputLen = outputLen;
to.oHash = oHash._cloneInto(to.oHash);
to.iHash = iHash._cloneInto(to.iHash);
return to;
}
destroy() {
this.destroyed = true;
this.oHash.destroy();
this.iHash.destroy();
}
}
export const hmac = (hash: CHash, key: Input, message: Input): Uint8Array =>
new HMAC<any>(hash, key).update(message).digest();
hmac.create = (hash: CHash, key: Input) => new HMAC<any>(hash, key);
hmac.init = hmac.create;
@@ -0,0 +1,5 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
throw new Error(
'noble-hashes have no entry-point. Please consult the README.md to learn how to use them'
);
@@ -0,0 +1,94 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { hmac } from './hmac';
// prettier-ignore
import {
Hash, CHash, Input, createView, toBytes, assertNumber, assertHash, checkOpts, asyncLoop
} from './utils';
// PBKDF (RFC 2898)
export type Pbkdf2Opt = {
c: number; // Iterations
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
asyncTick?: number; // Maximum time in ms for which async function can block execution
};
// Common prologue and epilogue for sync/async functions
function pbkdf2Init(hash: CHash, _password: Input, _salt: Input, _opts: Pbkdf2Opt) {
assertHash(hash);
const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
const { c, dkLen, asyncTick } = opts;
assertNumber(c);
assertNumber(dkLen);
assertNumber(asyncTick);
if (c < 1) throw new Error('PBKDF2: iterations (c) should be >= 1');
const password = toBytes(_password);
const salt = toBytes(_salt);
// DK = PBKDF2(PRF, Password, Salt, c, dkLen);
const DK = new Uint8Array(dkLen);
// U1 = PRF(Password, Salt + INT_32_BE(i))
const PRF = hmac.init(hash, password);
const PRFSalt = PRF._cloneInto().update(salt);
return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
}
function pbkdf2Output<T extends Hash<T>>(
PRF: Hash<T>,
PRFSalt: Hash<T>,
DK: Uint8Array,
prfW: Hash<T>,
u: Uint8Array
) {
PRF.destroy();
PRFSalt.destroy();
if (prfW) prfW.destroy();
u.fill(0);
return DK;
}
export function pbkdf2(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
let prfW: any; // Working copy
const arr = new Uint8Array(4);
const view = createView(arr);
const u = new Uint8Array(PRF.outputLen);
// DK = T1 + T2 + ⋯ + Tdklen/hlen
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
// Ti = F(Password, Salt, c, i)
const Ti = DK.subarray(pos, pos + PRF.outputLen);
view.setInt32(0, ti, false);
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
// U1 = PRF(Password, Salt + INT_32_BE(i))
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
Ti.set(u.subarray(0, Ti.length));
for (let ui = 1; ui < c; ui++) {
// Uc = PRF(Password, Uc−1)
PRF._cloneInto(prfW).update(u).digestInto(u);
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
}
}
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
}
export async function pbkdf2Async(hash: CHash, password: Input, salt: Input, _opts: Pbkdf2Opt) {
const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, _opts);
let prfW: any; // Working copy
const arr = new Uint8Array(4);
const view = createView(arr);
const u = new Uint8Array(PRF.outputLen);
// DK = T1 + T2 + ⋯ + Tdklen/hlen
for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
// Ti = F(Password, Salt, c, i)
const Ti = DK.subarray(pos, pos + PRF.outputLen);
view.setInt32(0, ti, false);
// F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
// U1 = PRF(Password, Salt + INT_32_BE(i))
(prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
Ti.set(u.subarray(0, Ti.length));
await asyncLoop(c - 1, asyncTick, (i) => {
// Uc = PRF(Password, Uc−1)
PRF._cloneInto(prfW).update(u).digestInto(u);
for (let i = 0; i < Ti.length; i++) Ti[i] ^= u[i];
});
}
return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
}
@@ -0,0 +1,106 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { SHA2 } from './_sha2';
import { wrapConstructor } from './utils';
// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
// https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf
const Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]);
const Id = Uint8Array.from({ length: 16 }, (_, i) => i);
const Pi = Id.map((i) => (9 * i + 5) % 16);
let [idxL, idxR] = [[Id], [Pi]];
for (let i = 0; i < 4; i++) for (let j of [idxL, idxR]) j.push(j[i].map((k) => Rho[k]));
const shifts = [
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5],
].map((i) => new Uint8Array(i));
const shiftsL = idxL.map((idx, i) => idx.map((j) => shifts[i][j]));
const shiftsR = idxR.map((idx, i) => idx.map((j) => shifts[i][j]));
const Kl = new Uint32Array([0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]);
const Kr = new Uint32Array([0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]);
// The rotate left (circular left shift) operation for uint32
const rotl = (word: number, shift: number) => (word << shift) | (word >>> (32 - shift));
// It's called f() in spec.
function f(group: number, x: number, y: number, z: number): number {
if (group === 0) return x ^ y ^ z;
else if (group === 1) return (x & y) | (~x & z);
else if (group === 2) return (x | ~y) ^ z;
else if (group === 3) return (x & z) | (y & ~z);
else return x ^ (y | ~z);
}
// Temporary buffer, not used to store anything between runs
const BUF = new Uint32Array(16);
export class RIPEMD160 extends SHA2<RIPEMD160> {
private h0 = 0x67452301 | 0;
private h1 = 0xefcdab89 | 0;
private h2 = 0x98badcfe | 0;
private h3 = 0x10325476 | 0;
private h4 = 0xc3d2e1f0 | 0;
constructor() {
super(64, 20, 8, true);
}
protected get(): [number, number, number, number, number] {
const { h0, h1, h2, h3, h4 } = this;
return [h0, h1, h2, h3, h4];
}
protected set(h0: number, h1: number, h2: number, h3: number, h4: number) {
this.h0 = h0 | 0;
this.h1 = h1 | 0;
this.h2 = h2 | 0;
this.h3 = h3 | 0;
this.h4 = h4 | 0;
}
protected process(view: DataView, offset: number): void {
for (let i = 0; i < 16; i++, offset += 4) BUF[i] = view.getUint32(offset, true);
// prettier-ignore
let al = this.h0 | 0, ar = al,
bl = this.h1 | 0, br = bl,
cl = this.h2 | 0, cr = cl,
dl = this.h3 | 0, dr = dl,
el = this.h4 | 0, er = el;
// Instead of iterating 0 to 80, we split it into 5 groups
// And use the groups in constants, functions, etc. Much simpler
for (let group = 0; group < 5; group++) {
const rGroup = 4 - group;
const hbl = Kl[group], hbr = Kr[group]; // prettier-ignore
const rl = idxL[group], rr = idxR[group]; // prettier-ignore
const sl = shiftsL[group], sr = shiftsR[group]; // prettier-ignore
for (let i = 0; i < 16; i++) {
const tl = (rotl(al + f(group, bl, cl, dl) + BUF[rl[i]] + hbl, sl[i]) + el) | 0;
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore
}
// 2 loops are 10% faster
for (let i = 0; i < 16; i++) {
const tr = (rotl(ar + f(rGroup, br, cr, dr) + BUF[rr[i]] + hbr, sr[i]) + er) | 0;
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore
}
}
// Add the compressed chunk to the current hash value
this.set(
(this.h1 + cl + dr) | 0,
(this.h2 + dl + er) | 0,
(this.h3 + el + ar) | 0,
(this.h4 + al + br) | 0,
(this.h0 + bl + cr) | 0
);
}
protected roundClean() {
BUF.fill(0);
}
destroy() {
this.destroyed = true;
this.buffer.fill(0);
this.set(0, 0, 0, 0, 0);
}
}
export const ripemd160 = wrapConstructor(() => new RIPEMD160());
@@ -0,0 +1,220 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { sha256 } from './sha256';
import { pbkdf2 } from './pbkdf2';
import { assertNumber, asyncLoop, checkOpts, Input, u32 } from './utils';
// Left rotate for uint32
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));
// prettier-ignore
function XorAndSalsa(
prev: Uint32Array,
pi: number,
input: Uint32Array,
ii: number,
out: Uint32Array,
oi: number
) {
// Based on https://cr.yp.to/salsa20.html
// Xor blocks
let y00 = prev[pi++] ^ input[ii++], y01 = prev[pi++] ^ input[ii++];
let y02 = prev[pi++] ^ input[ii++], y03 = prev[pi++] ^ input[ii++];
let y04 = prev[pi++] ^ input[ii++], y05 = prev[pi++] ^ input[ii++];
let y06 = prev[pi++] ^ input[ii++], y07 = prev[pi++] ^ input[ii++];
let y08 = prev[pi++] ^ input[ii++], y09 = prev[pi++] ^ input[ii++];
let y10 = prev[pi++] ^ input[ii++], y11 = prev[pi++] ^ input[ii++];
let y12 = prev[pi++] ^ input[ii++], y13 = prev[pi++] ^ input[ii++];
let y14 = prev[pi++] ^ input[ii++], y15 = prev[pi++] ^ input[ii++];
// Save state to temporary variables (salsa)
let x00 = y00, x01 = y01, x02 = y02, x03 = y03,
x04 = y04, x05 = y05, x06 = y06, x07 = y07,
x08 = y08, x09 = y09, x10 = y10, x11 = y11,
x12 = y12, x13 = y13, x14 = y14, x15 = y15;
// Main loop (salsa)
for (let i = 0; i < 8; i += 2) {
x04 ^= rotl(x00 + x12 | 0, 7); x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13); x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7); x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13); x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7); x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13); x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7); x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13); x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7); x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13); x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7); x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13); x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7); x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13); x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7); x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13); x15 ^= rotl(x14 + x13 | 0, 18);
}
// Write output (salsa)
out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
}
function BlockMix(input: Uint32Array, ii: number, out: Uint32Array, oi: number, r: number) {
// The block B is r 128-byte chunks (which is equivalent of 2r 64-byte chunks)
let [head, tail] = [oi + 0, oi + 16 * r];
for (let i = 0; i < 16; i++) out[tail + i] = input[ii + (2 * r - 1) * 16 + i]; // X ← B[2r−1]
for (let i = 0; i < r; i++, head += 16, ii += 16) {
// We write odd & even Yi at same time. Even: 0bXXXXX0 Odd: 0bXXXXX1
XorAndSalsa(out, tail, input, ii, out, head); // head[i] = Salsa(blockIn[2*i] ^ tail[i-1])
if (i > 0) tail += 16; // First iteration overwrites tmp value in tail
XorAndSalsa(out, head, input, (ii += 16), out, tail); // tail[i] = Salsa(blockIn[2*i+1] ^ head[i])
}
}
// RFC 7914
export type ScryptOpts = {
N: number; // costFactor CPU/memory cost parameter - Must be a power of 2 (e.g. 1024)
r: number; // blocksize parameter, which fine-tunes sequential memory read size and performance. (8 is commonly used)
p: number; // Parallelization parameter. (1 .. 232-1 * hLen/MFlen)
dkLen?: number; // Desired key length in bytes (Intended output length in octets of the derived key
asyncTick?: number; // Maximum time in ms for which async function can block execution
maxmem?: number;
onProgress?: (progress: number) => void;
};
// Common prologue and epilogue for sync/async functions
function scryptInit(password: Input, salt: Input, _opts?: ScryptOpts) {
// Maxmem - 1GB+1KB by default
const opts = checkOpts(
{
dkLen: 32,
asyncTick: 10,
maxmem: 1024 ** 3 + 1024,
},
_opts
);
const { N, r, p, dkLen, asyncTick, maxmem, onProgress } = opts;
assertNumber(N);
assertNumber(r);
assertNumber(p);
assertNumber(dkLen);
assertNumber(asyncTick);
assertNumber(maxmem);
if (onProgress !== undefined && typeof onProgress !== 'function')
throw new Error('progressCb should be function');
const blockSize = 128 * r;
const blockSize32 = blockSize / 4;
if (N <= 1 || (N & (N - 1)) !== 0 || N >= 2 ** (blockSize / 8) || N > 2 ** 32) {
// NOTE: we limit N to be less than 2**32 because of 32 bit variant of Integrify function
// There is no JS engines that allows alocate more than 4GB per single Uint8Array for now, but can change in future.
throw new Error(
'Scrypt: N must be larger than 1, a power of 2, less than 2^(128 * r / 8) and less than 2^32'
);
}
if (p < 0 || p > ((2 ** 32 - 1) * 32) / blockSize) {
throw new Error(
'Scrypt: p must be a positive integer less than or equal to ((2^32 - 1) * 32) / (128 * r)'
);
}
if (dkLen < 0 || dkLen > (2 ** 32 - 1) * 32) {
throw new Error(
'Scrypt: dkLen should be positive integer less than or equal to (2^32 - 1) * 32'
);
}
const memUsed = blockSize * (N + p);
if (memUsed > maxmem) {
throw new Error(
`Scrypt: parameters too large, ${memUsed} (128 * r * (N + p)) > ${maxmem} (maxmem)`
);
}
// [B0...Bp−1] ← PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor)
// Since it has only one iteration there is no reason to use async variant
const B = pbkdf2(sha256, password, salt, { c: 1, dkLen: blockSize * p });
const B32 = u32(B);
// Re-used between parallel iterations. Array(iterations) of B
const V = u32(new Uint8Array(blockSize * N));
const tmp = u32(new Uint8Array(blockSize));
let blockMixCb = () => {};
if (onProgress) {
const totalBlockMix = 2 * N * p;
// Invoke callback if progress changes from 10.01 to 10.02
// Allows to draw smooth progress bar on up to 8K screen
const callbackPer = Math.max(Math.floor(totalBlockMix / 10000), 1);
let blockMixCnt = 0;
blockMixCb = () => {
blockMixCnt++;
if (onProgress && (!(blockMixCnt % callbackPer) || blockMixCnt === totalBlockMix))
onProgress(blockMixCnt / totalBlockMix);
};
}
return { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick };
}
function scryptOutput(
password: Input,
dkLen: number,
B: Uint8Array,
V: Uint32Array,
tmp: Uint32Array
) {
const res = pbkdf2(sha256, password, B, { c: 1, dkLen });
B.fill(0);
V.fill(0);
tmp.fill(0);
return res;
}
export function scrypt(password: Input, salt: Input, _opts: ScryptOpts) {
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb } = scryptInit(
password,
salt,
_opts
);
for (let pi = 0; pi < p; pi++) {
const Pi = blockSize32 * pi;
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
for (let i = 0, pos = 0; i < N - 1; i++) {
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
blockMixCb();
}
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
blockMixCb();
for (let i = 0; i < N; i++) {
// First u32 of the last 64-byte block (u32 is LE)
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
blockMixCb();
}
}
return scryptOutput(password, dkLen, B, V, tmp);
}
export async function scryptAsync(password: Uint8Array, salt: Uint8Array, _opts: ScryptOpts) {
const { N, r, p, dkLen, blockSize32, V, B32, B, tmp, blockMixCb, asyncTick } = scryptInit(
password,
salt,
_opts
);
for (let pi = 0; pi < p; pi++) {
const Pi = blockSize32 * pi;
for (let i = 0; i < blockSize32; i++) V[i] = B32[Pi + i]; // V[0] = B[i]
let pos = 0;
await asyncLoop(N - 1, asyncTick, (i) => {
BlockMix(V, pos, V, (pos += blockSize32), r); // V[i] = BlockMix(V[i-1]);
blockMixCb();
});
BlockMix(V, (N - 1) * blockSize32, B32, Pi, r); // Process last element
blockMixCb();
await asyncLoop(N, asyncTick, (i) => {
// First u32 of the last 64-byte block (u32 is LE)
const j = B32[Pi + blockSize32 - 16] % N; // j = Integrify(X) % iterations
for (let k = 0; k < blockSize32; k++) tmp[k] = B32[Pi + k] ^ V[j * blockSize32 + k]; // tmp = B ^ V[j]
BlockMix(tmp, 0, B32, Pi, r); // B = BlockMix(B ^ V[j])
blockMixCb();
});
}
return scryptOutput(password, dkLen, B, V, tmp);
}
@@ -0,0 +1,112 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { SHA2 } from './_sha2';
import { rotr, wrapConstructor } from './utils';
// Choice: a ? b : c
const Chi = (a: number, b: number, c: number) => (a & b) ^ (~a & c);
// Majority function, true if any two inpust is true
const Maj = (a: number, b: number, c: number) => (a & b) ^ (a & c) ^ (b & c);
// Round constants:
// first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
// prettier-ignore
const SHA256_K = new Uint32Array([
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]);
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
// prettier-ignore
const IV = new Uint32Array([
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]);
// Temporary buffer, not used to store anything between runs
// Named this way because it matches specification.
const SHA256_W = new Uint32Array(64);
class SHA256 extends SHA2<SHA256> {
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
private A = IV[0] | 0;
private B = IV[1] | 0;
private C = IV[2] | 0;
private D = IV[3] | 0;
private E = IV[4] | 0;
private F = IV[5] | 0;
private G = IV[6] | 0;
private H = IV[7] | 0;
constructor() {
super(64, 32, 8, false);
}
protected get(): [number, number, number, number, number, number, number, number] {
const { A, B, C, D, E, F, G, H } = this;
return [A, B, C, D, E, F, G, H];
}
// prettier-ignore
protected set(
A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number
) {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
this.E = E | 0;
this.F = F | 0;
this.G = G | 0;
this.H = H | 0;
}
protected process(view: DataView, offset: number): void {
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
for (let i = 16; i < 64; i++) {
const W15 = SHA256_W[i - 15];
const W2 = SHA256_W[i - 2];
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
}
// Compression function main loop, 64 rounds
let { A, B, C, D, E, F, G, H } = this;
for (let i = 0; i < 64; i++) {
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
const T2 = (sigma0 + Maj(A, B, C)) | 0;
H = G;
G = F;
F = E;
E = (D + T1) | 0;
D = C;
C = B;
B = A;
A = (T1 + T2) | 0;
}
// Add the compressed chunk to the current hash value
A = (A + this.A) | 0;
B = (B + this.B) | 0;
C = (C + this.C) | 0;
D = (D + this.D) | 0;
E = (E + this.E) | 0;
F = (F + this.F) | 0;
G = (G + this.G) | 0;
H = (H + this.H) | 0;
this.set(A, B, C, D, E, F, G, H);
}
protected roundClean() {
SHA256_W.fill(0);
}
destroy() {
this.set(0, 0, 0, 0, 0, 0, 0, 0);
this.buffer.fill(0);
}
}
export const sha256 = wrapConstructor(() => new SHA256());
@@ -0,0 +1,401 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { Input, toBytes, wrapConstructorWithOpts, assertNumber, u32, Hash, HashXOF } from './utils';
import { Keccak, ShakeOpts } from './sha3';
// cSHAKE && KMAC (NIST SP800-185)
function leftEncode(n: number): Uint8Array {
const res = [n & 0xff];
n >>= 8;
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
res.unshift(res.length);
return new Uint8Array(res);
}
function rightEncode(n: number): Uint8Array {
const res = [n & 0xff];
n >>= 8;
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
res.push(res.length);
return new Uint8Array(res);
}
const toBytesOptional = (buf?: Input) => (buf !== undefined ? toBytes(buf) : new Uint8Array([]));
// NOTE: second modulo is necessary since we don't need to add padding if current element takes whole block
const getPadding = (len: number, block: number) => new Uint8Array((block - (len % block)) % block);
export type cShakeOpts = ShakeOpts & { personalization?: Input; NISTfn?: Input };
// Personalization
function cshakePers(hash: Keccak, opts: cShakeOpts = {}): Keccak {
if (!opts || (!opts.personalization && !opts.NISTfn)) return hash;
// Encode and pad inplace to avoid unneccesary memory copies/slices (so we don't need to zero them later)
// bytepad(encode_string(N) || encode_string(S), 168)
const blockLenBytes = leftEncode(hash.blockLen);
const fn = toBytesOptional(opts.NISTfn);
const fnLen = leftEncode(8 * fn.length); // length in bits
const pers = toBytesOptional(opts.personalization);
const persLen = leftEncode(8 * pers.length); // length in bits
if (!fn.length && !pers.length) return hash;
hash.suffix = 0x04;
hash.update(blockLenBytes).update(fnLen).update(fn).update(persLen).update(pers);
let totalLen = blockLenBytes.length + fnLen.length + fn.length + persLen.length + pers.length;
hash.update(getPadding(totalLen, hash.blockLen));
return hash;
}
const gencShake = (suffix: number, blockLen: number, outputLen: number) =>
wrapConstructorWithOpts<Keccak, cShakeOpts>((opts: cShakeOpts = {}) =>
cshakePers(
new Keccak(blockLen, suffix, opts.dkLen !== undefined ? opts.dkLen : outputLen, true),
opts
)
);
export const cshake128 = gencShake(0x1f, 168, 128 / 8);
export const cshake256 = gencShake(0x1f, 136, 256 / 8);
class KMAC extends Keccak implements HashXOF<KMAC> {
constructor(
blockLen: number,
outputLen: number,
enableXOF: boolean,
key: Input,
opts: cShakeOpts = {}
) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'KMAC', personalization: opts.personalization });
key = toBytes(key);
// 1. newX = bytepad(encode_string(K), 168) || X || right_encode(L).
const blockLenBytes = leftEncode(this.blockLen);
const keyLen = leftEncode(8 * key.length);
this.update(blockLenBytes).update(keyLen).update(key);
const totalLen = blockLenBytes.length + keyLen.length + key.length;
this.update(getPadding(totalLen, this.blockLen));
}
protected override finish() {
if (!this.finished) this.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
super.finish();
}
override _cloneInto(to?: KMAC): KMAC {
// Create new instance without calling constructor since key already in state and we don't know it.
// Force "to" to be instance of KMAC instead of Sha3.
if (!to) {
to = Object.create(Object.getPrototypeOf(this), {}) as KMAC;
to.state = this.state.slice();
to.blockLen = this.blockLen;
to.state32 = u32(to.state);
}
return super._cloneInto(to) as KMAC;
}
override clone(): KMAC {
return this._cloneInto();
}
}
function genKmac(blockLen: number, outputLen: number, xof = false) {
const kmac = (key: Input, message: Input, opts?: cShakeOpts): Uint8Array =>
kmac.create(key, opts).update(message).digest();
kmac.create = (key: Input, opts: cShakeOpts = {}) =>
new KMAC(blockLen, opts.dkLen !== undefined ? opts.dkLen : outputLen, xof, key, opts);
kmac.init = kmac.create;
return kmac;
}
export const kmac128 = genKmac(168, 128 / 8);
export const kmac256 = genKmac(136, 256 / 8);
export const kmac128xof = genKmac(168, 128 / 8, true);
export const kmac256xof = genKmac(136, 256 / 8, true);
// TupleHash
// Usage: tuple(['ab', 'cd']) != tuple(['a', 'bcd'])
class TupleHash extends Keccak implements HashXOF<TupleHash> {
constructor(blockLen: number, outputLen: number, enableXOF: boolean, opts: cShakeOpts = {}) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'TupleHash', personalization: opts.personalization });
// Change update after cshake processed
this.update = (data: Input) => {
data = toBytes(data);
super.update(leftEncode(data.length * 8));
super.update(data);
return this;
};
}
protected override finish() {
if (!this.finished) super.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
super.finish();
}
override _cloneInto(to?: TupleHash): TupleHash {
to ||= new TupleHash(this.blockLen, this.outputLen, this.enableXOF);
return super._cloneInto(to) as TupleHash;
}
override clone(): TupleHash {
return this._cloneInto();
}
}
function genTuple(blockLen: number, outputLen: number, xof = false) {
const tuple = (messages: Input[], opts?: cShakeOpts): Uint8Array => {
const h = tuple.create(opts);
for (const msg of messages) h.update(msg);
return h.digest();
};
tuple.create = (opts: cShakeOpts = {}) =>
new TupleHash(blockLen, opts.dkLen !== undefined ? opts.dkLen : outputLen, xof, opts);
tuple.init = tuple.create;
return tuple;
}
export const tuplehash128 = genTuple(168, 128 / 8);
export const tuplehash256 = genTuple(136, 256 / 8);
export const tuplehash128xof = genTuple(168, 128 / 8, true);
export const tuplehash256xof = genTuple(136, 256 / 8, true);
// ParallelHash (same as K12/M14, but without speedup for inputs less 8kb, reduced number of rounds and more simple)
type ParallelOpts = cShakeOpts & { blockLen?: number };
class ParallelHash extends Keccak implements HashXOF<ParallelHash> {
private leafHash?: Hash<Keccak>;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
private chunkLen: number;
constructor(
blockLen: number,
outputLen: number,
protected leafCons: () => Hash<Keccak>,
enableXOF: boolean,
opts: ParallelOpts = {}
) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'ParallelHash', personalization: opts.personalization });
let { blockLen: B } = opts;
B ||= 8;
assertNumber(B);
this.chunkLen = B;
super.update(leftEncode(B));
// Change update after cshake processed
this.update = (data: Input) => {
data = toBytes(data);
const { chunkLen, leafCons } = this;
for (let pos = 0, len = data.length; pos < len; ) {
if (this.chunkPos == chunkLen || !this.leafHash) {
if (this.leafHash) {
super.update(this.leafHash.digest());
this.chunksDone++;
}
this.leafHash = leafCons();
this.chunkPos = 0;
}
const take = Math.min(chunkLen - this.chunkPos, len - pos);
this.leafHash.update(data.subarray(pos, pos + take));
this.chunkPos += take;
pos += take;
}
return this;
};
}
protected override finish() {
if (this.finished) return;
if (this.leafHash) {
super.update(this.leafHash.digest());
this.chunksDone++;
}
super.update(rightEncode(this.chunksDone));
super.update(rightEncode(this.enableXOF ? 0 : this.outputLen * 8)); // outputLen in bits
super.finish();
}
override _cloneInto(to?: ParallelHash): ParallelHash {
to ||= new ParallelHash(this.blockLen, this.outputLen, this.leafCons, this.enableXOF);
if (this.leafHash) to.leafHash = this.leafHash._cloneInto(to.leafHash as Keccak);
to.chunkPos = this.chunkPos;
to.chunkLen = this.chunkLen;
to.chunksDone = this.chunksDone;
return super._cloneInto(to) as ParallelHash;
}
override destroy() {
super.destroy.call(this);
if (this.leafHash) this.leafHash.destroy();
}
override clone(): ParallelHash {
return this._cloneInto();
}
}
function genParallel(
blockLen: number,
outputLen: number,
leaf: ReturnType<typeof gencShake>,
xof = false
) {
const parallel = (message: Input, opts?: ParallelOpts): Uint8Array =>
parallel.create(opts).update(message).digest();
parallel.create = (opts: ParallelOpts = {}) =>
new ParallelHash(
blockLen,
opts.dkLen !== undefined ? opts.dkLen : outputLen,
() => leaf.init({ dkLen: 2 * outputLen }),
xof,
opts
);
parallel.init = parallel.create;
return parallel;
}
export const parallelhash128 = genParallel(168, 128 / 8, cshake128);
export const parallelhash256 = genParallel(136, 256 / 8, cshake256);
export const parallelhash128xof = genParallel(168, 128 / 8, cshake128, true);
export const parallelhash256xof = genParallel(136, 256 / 8, cshake256, true);
// Kangaroo
// Same as NIST rightEncode, but returns [0] for zero string
function rightEncodeK12(n: number): Uint8Array {
const res = [];
for (; n > 0; n >>= 8) res.unshift(n & 0xff);
res.push(res.length);
return new Uint8Array(res);
}
export type KangarooOpts = { dkLen?: number; personalization?: Input };
const EMPTY = new Uint8Array([]);
class KangarooTwelve extends Keccak implements HashXOF<KangarooTwelve> {
readonly chunkLen = 8192;
private leafHash?: Keccak;
private personalization: Uint8Array;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
constructor(
blockLen: number,
protected leafLen: number,
outputLen: number,
rounds: number,
opts: KangarooOpts
) {
super(blockLen, 0x07, outputLen, true, rounds);
const { personalization } = opts;
this.personalization = toBytesOptional(personalization);
}
override update(data: Input) {
data = toBytes(data);
const { chunkLen, blockLen, leafLen, rounds } = this;
for (let pos = 0, len = data.length; pos < len; ) {
if (this.chunkPos == chunkLen) {
if (this.leafHash) super.update(this.leafHash.digest());
else {
this.suffix = 0x06; // Its safe to change suffix here since its used only in digest()
super.update(new Uint8Array([3, 0, 0, 0, 0, 0, 0, 0]));
}
this.leafHash = new Keccak(blockLen, 0x0b, leafLen, false, rounds);
this.chunksDone++;
this.chunkPos = 0;
}
const take = Math.min(chunkLen - this.chunkPos, len - pos);
const chunk = data.subarray(pos, pos + take);
if (this.leafHash) this.leafHash.update(chunk);
else super.update(chunk);
this.chunkPos += take;
pos += take;
}
return this;
}
protected override finish() {
if (this.finished) return;
const { personalization } = this;
this.update(personalization).update(rightEncodeK12(personalization.length));
// Leaf hash
if (this.leafHash) {
super.update(this.leafHash.digest());
super.update(rightEncodeK12(this.chunksDone));
super.update(new Uint8Array([0xff, 0xff]));
}
super.finish.call(this);
}
override destroy() {
super.destroy.call(this);
if (this.leafHash) this.leafHash.destroy();
// We cannot zero personalization buffer since it is user provided and we don't want to mutate user input
this.personalization = EMPTY;
}
override _cloneInto(to?: KangarooTwelve): KangarooTwelve {
const { blockLen, leafLen, leafHash, outputLen, rounds } = this;
to ||= new KangarooTwelve(blockLen, leafLen, outputLen, rounds, {});
super._cloneInto(to);
if (leafHash) to.leafHash = leafHash._cloneInto(to.leafHash);
to.personalization.set(this.personalization);
to.leafLen = this.leafLen;
to.chunkPos = this.chunkPos;
to.chunksDone = this.chunksDone;
return to;
}
override clone(): KangarooTwelve {
return this._cloneInto();
}
}
// Default to 32 bytes, so it can be used without opts
export const k12 = wrapConstructorWithOpts<KangarooTwelve, KangarooOpts>(
(opts: KangarooOpts = {}) =>
new KangarooTwelve(168, 32, opts.dkLen !== undefined ? opts.dkLen : 32, 12, opts)
);
// MarsupilamiFourteen
export const m14 = wrapConstructorWithOpts<KangarooTwelve, KangarooOpts>(
(opts: KangarooOpts = {}) =>
new KangarooTwelve(136, 64, opts.dkLen !== undefined ? opts.dkLen : 64, 14, opts)
);
// https://keccak.team/files/CSF-0.1.pdf
// + https://github.com/XKCP/XKCP/tree/master/lib/high/Keccak/PRG
class KeccakPRG extends Keccak {
protected rate: number;
constructor(capacity: number) {
assertNumber(capacity);
// Rho should be full bytes
if (capacity < 0 || capacity > 1600 - 10 || (1600 - capacity - 2) % 8)
throw new Error('KeccakPRG: Invalid capacity');
// blockLen = rho in bytes
super((1600 - capacity - 2) / 8, 0, 0, true);
this.rate = 1600 - capacity;
this.posOut = Math.floor((this.rate + 7) / 8);
}
override keccak() {
// Duplex padding
this.state[this.pos] ^= 0x01;
this.state[this.blockLen] ^= 0x02; // Rho is full bytes
super.keccak();
this.pos = 0;
this.posOut = 0;
}
override update(data: Input) {
super.update(data);
this.posOut = this.blockLen;
return this;
}
feed(data: Input) {
return this.update(data);
}
protected override finish() {}
override digestInto(out: Uint8Array): Uint8Array {
throw new Error('KeccakPRG: digest is not allowed, please use .fetch instead.');
}
fetch(bytes: number): Uint8Array {
return this.xof(bytes);
}
// Ensure irreversibility (even if state leaked previous outputs cannot be computed)
forget() {
if (this.rate < 1600 / 2 + 1) throw new Error('KeccakPRG: rate too low to use forget');
this.keccak();
for (let i = 0; i < this.blockLen; i++) this.state[i] = 0;
this.pos = this.blockLen;
this.keccak();
this.posOut = this.blockLen;
}
override _cloneInto(to?: KeccakPRG): KeccakPRG {
const { rate } = this;
to ||= new KeccakPRG(1600 - rate);
super._cloneInto(to);
to.rate = rate;
return to;
}
override clone(): KeccakPRG {
return this._cloneInto();
}
}
export const keccakprg = (capacity = 254) => new KeccakPRG(capacity);
@@ -0,0 +1,218 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import * as u64 from './_u64';
import {
_n,
Hash,
u32,
Input,
toBytes,
wrapConstructor,
wrapConstructorWithOpts,
assertNumber,
HashXOF,
} from './utils';
const _0n = _n(0);
const _1n = _n(1);
const _2n = _n(2);
const _7n = _n(7);
const _256n = _n(256);
const _0x71n = _n(0x71);
// Various per round constants calculations
const [SHA3_PI, SHA3_ROTL, _SHA3_IOTA]: [number[], number[], bigint[]] = [[], [], []];
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
// Pi
[x, y] = [y, (2 * x + 3 * y) % 5];
SHA3_PI.push(2 * (5 * y + x));
// Rotational
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
// Iota
let t = _0n;
for (let j = 0; j < 7; j++) {
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
if (R & _2n) t ^= _1n << ((_1n << BigInt(j)) - _1n);
}
_SHA3_IOTA.push(t);
}
const [SHA3_IOTA_H, SHA3_IOTA_L] = u64.split(_SHA3_IOTA, true);
// Left rotation (without 0, 32, 64)
const rotlH = (h: number, l: number, s: number) =>
s > 32 ? u64.rotlBH(h, l, s) : u64.rotlSH(h, l, s);
const rotlL = (h: number, l: number, s: number) =>
s > 32 ? u64.rotlBL(h, l, s) : u64.rotlSL(h, l, s);
// Same as keccakf1600, but allows to skip some rounds
export function keccakP(s: Uint32Array, rounds: number = 24) {
const B = new Uint32Array(5 * 2);
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
for (let round = 24 - rounds; round < 24; round++) {
// Theta θ
for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
for (let x = 0; x < 10; x += 2) {
const idx1 = (x + 8) % 10;
const idx0 = (x + 2) % 10;
const B0 = B[idx0];
const B1 = B[idx0 + 1];
const Th = rotlH(B0, B1, 1) ^ B[idx1];
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
for (let y = 0; y < 50; y += 10) {
s[x + y] ^= Th;
s[x + y + 1] ^= Tl;
}
}
// Rho (ρ) and Pi (π)
let curH = s[2];
let curL = s[3];
for (let t = 0; t < 24; t++) {
const shift = SHA3_ROTL[t];
const Th = rotlH(curH, curL, shift);
const Tl = rotlL(curH, curL, shift);
const PI = SHA3_PI[t];
curH = s[PI];
curL = s[PI + 1];
s[PI] = Th;
s[PI + 1] = Tl;
}
// Chi (χ)
for (let y = 0; y < 50; y += 10) {
for (let x = 0; x < 10; x++) B[x] = s[y + x];
for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
}
// Iota (ι)
s[0] ^= SHA3_IOTA_H[round];
s[1] ^= SHA3_IOTA_L[round];
}
B.fill(0);
}
export class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
protected state: Uint8Array;
protected pos = 0;
protected posOut = 0;
protected finished = false;
protected state32: Uint32Array;
protected destroyed = false;
// NOTE: we accept arguments in bytes instead of bits here.
constructor(
public blockLen: number,
public suffix: number,
public outputLen: number,
protected enableXOF = false,
protected rounds: number = 24
) {
super();
// Can be passed from user as dkLen
assertNumber(outputLen);
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
if (0 >= this.blockLen || this.blockLen >= 200)
throw new Error('Sha3 supports only keccak-f1600 function');
this.state = new Uint8Array(200);
this.state32 = u32(this.state);
}
protected keccak() {
keccakP(this.state32, this.rounds);
this.posOut = 0;
this.pos = 0;
}
update(data: Input) {
if (this.destroyed) throw new Error('instance is destroyed');
if (this.finished) throw new Error('digest() was already called');
const { blockLen, state } = this;
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len; ) {
const take = Math.min(blockLen - this.pos, len - pos);
for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++];
if (this.pos === blockLen) this.keccak();
}
return this;
}
protected finish() {
if (this.finished) return;
this.finished = true;
const { state, suffix, pos, blockLen } = this;
// Do the padding
state[pos] ^= suffix;
if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak();
state[blockLen - 1] ^= 0x80;
this.keccak();
}
protected writeInto(out: Uint8Array): Uint8Array {
if (this.destroyed) throw new Error('instance is destroyed');
if (!(out instanceof Uint8Array)) throw new Error('Keccak: invalid output buffer');
this.finish();
for (let pos = 0, len = out.length; pos < len; ) {
if (this.posOut >= this.blockLen) this.keccak();
const take = Math.min(this.blockLen - this.posOut, len - pos);
out.set(this.state.subarray(this.posOut, this.posOut + take), pos);
this.posOut += take;
pos += take;
}
return out;
}
xofInto(out: Uint8Array): Uint8Array {
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
if (!this.enableXOF) throw new Error('XOF is not possible for this instance');
return this.writeInto(out);
}
xof(bytes: number): Uint8Array {
assertNumber(bytes);
return this.xofInto(new Uint8Array(bytes));
}
digestInto(out: Uint8Array) {
if (out.length < this.outputLen) throw new Error('Keccak: invalid output buffer');
if (this.finished) throw new Error('digest() was already called');
this.finish();
this.writeInto(out);
this.destroy();
return out;
}
digest() {
return this.digestInto(new Uint8Array(this.outputLen));
}
destroy() {
this.destroyed = true;
this.state.fill(0);
}
_cloneInto(to?: Keccak): Keccak {
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
to.state32.set(this.state32);
to.pos = this.pos;
to.posOut = this.posOut;
to.finished = this.finished;
to.rounds = rounds;
// Suffix can change in cSHAKE
to.suffix = suffix;
to.outputLen = outputLen;
to.enableXOF = enableXOF;
to.destroyed = this.destroyed;
return to;
}
}
const gen = (suffix: number, blockLen: number, outputLen: number) =>
wrapConstructor(() => new Keccak(blockLen, suffix, outputLen));
export const sha3_224 = gen(0x06, 144, 224 / 8);
export const sha3_256 = gen(0x06, 136, 256 / 8);
export const sha3_384 = gen(0x06, 104, 384 / 8);
export const sha3_512 = gen(0x06, 72, 512 / 8);
export const keccak_224 = gen(0x01, 144, 224 / 8);
export const keccak_256 = gen(0x01, 136, 256 / 8);
export const keccak_384 = gen(0x01, 104, 384 / 8);
export const keccak_512 = gen(0x01, 72, 512 / 8);
export type ShakeOpts = { dkLen?: number };
const genShake = (suffix: number, blockLen: number, outputLen: number) =>
wrapConstructorWithOpts<Keccak, ShakeOpts>(
(opts: ShakeOpts = {}) =>
new Keccak(blockLen, suffix, opts.dkLen !== undefined ? opts.dkLen : outputLen, true)
);
export const shake128 = genShake(0x1f, 168, 128 / 8);
export const shake256 = genShake(0x1f, 136, 256 / 8);
@@ -0,0 +1,223 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
import { SHA2 } from './_sha2';
import * as u64 from './_u64';
import { _n, wrapConstructor } from './utils';
// Round contants (first 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409):
// prettier-ignore
const [SHA512_Kh, SHA512_Kl] = u64.split([
_n('0x428a2f98d728ae22'), _n('0x7137449123ef65cd'), _n('0xb5c0fbcfec4d3b2f'), _n('0xe9b5dba58189dbbc'),
_n('0x3956c25bf348b538'), _n('0x59f111f1b605d019'), _n('0x923f82a4af194f9b'), _n('0xab1c5ed5da6d8118'),
_n('0xd807aa98a3030242'), _n('0x12835b0145706fbe'), _n('0x243185be4ee4b28c'), _n('0x550c7dc3d5ffb4e2'),
_n('0x72be5d74f27b896f'), _n('0x80deb1fe3b1696b1'), _n('0x9bdc06a725c71235'), _n('0xc19bf174cf692694'),
_n('0xe49b69c19ef14ad2'), _n('0xefbe4786384f25e3'), _n('0x0fc19dc68b8cd5b5'), _n('0x240ca1cc77ac9c65'),
_n('0x2de92c6f592b0275'), _n('0x4a7484aa6ea6e483'), _n('0x5cb0a9dcbd41fbd4'), _n('0x76f988da831153b5'),
_n('0x983e5152ee66dfab'), _n('0xa831c66d2db43210'), _n('0xb00327c898fb213f'), _n('0xbf597fc7beef0ee4'),
_n('0xc6e00bf33da88fc2'), _n('0xd5a79147930aa725'), _n('0x06ca6351e003826f'), _n('0x142929670a0e6e70'),
_n('0x27b70a8546d22ffc'), _n('0x2e1b21385c26c926'), _n('0x4d2c6dfc5ac42aed'), _n('0x53380d139d95b3df'),
_n('0x650a73548baf63de'), _n('0x766a0abb3c77b2a8'), _n('0x81c2c92e47edaee6'), _n('0x92722c851482353b'),
_n('0xa2bfe8a14cf10364'), _n('0xa81a664bbc423001'), _n('0xc24b8b70d0f89791'), _n('0xc76c51a30654be30'),
_n('0xd192e819d6ef5218'), _n('0xd69906245565a910'), _n('0xf40e35855771202a'), _n('0x106aa07032bbd1b8'),
_n('0x19a4c116b8d2d0c8'), _n('0x1e376c085141ab53'), _n('0x2748774cdf8eeb99'), _n('0x34b0bcb5e19b48a8'),
_n('0x391c0cb3c5c95a63'), _n('0x4ed8aa4ae3418acb'), _n('0x5b9cca4f7763e373'), _n('0x682e6ff3d6b2b8a3'),
_n('0x748f82ee5defb2fc'), _n('0x78a5636f43172f60'), _n('0x84c87814a1f0ab72'), _n('0x8cc702081a6439ec'),
_n('0x90befffa23631e28'), _n('0xa4506cebde82bde9'), _n('0xbef9a3f7b2c67915'), _n('0xc67178f2e372532b'),
_n('0xca273eceea26619c'), _n('0xd186b8c721c0c207'), _n('0xeada7dd6cde0eb1e'), _n('0xf57d4f7fee6ed178'),
_n('0x06f067aa72176fba'), _n('0x0a637dc5a2c898a6'), _n('0x113f9804bef90dae'), _n('0x1b710b35131c471b'),
_n('0x28db77f523047d84'), _n('0x32caab7b40c72493'), _n('0x3c9ebe0a15c9bebc'), _n('0x431d67c49c100d4c'),
_n('0x4cc5d4becb3e42b6'), _n('0x597f299cfc657e2a'), _n('0x5fcb6fab3ad6faec'), _n('0x6c44198c4a475817')
]);
// Temporary buffer, not used to store anything between runs
const SHA512_W_H = new Uint32Array(80);
const SHA512_W_L = new Uint32Array(80);
export class SHA512 extends SHA2<SHA512> {
// We cannot use array here since array allows indexing by variable which means optimizer/compiler cannot use registers.
// Also looks cleaner and easier to verify with spec.
// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
// h -- high 32 bits, l -- low 32 bits
Ah = 0x6a09e667 | 0;
Al = 0xf3bcc908 | 0;
Bh = 0xbb67ae85 | 0;
Bl = 0x84caa73b | 0;
Ch = 0x3c6ef372 | 0;
Cl = 0xfe94f82b | 0;
Dh = 0xa54ff53a | 0;
Dl = 0x5f1d36f1 | 0;
Eh = 0x510e527f | 0;
El = 0xade682d1 | 0;
Fh = 0x9b05688c | 0;
Fl = 0x2b3e6c1f | 0;
Gh = 0x1f83d9ab | 0;
Gl = 0xfb41bd6b | 0;
Hh = 0x5be0cd19 | 0;
Hl = 0x137e2179 | 0;
constructor() {
super(128, 64, 16, false);
}
// prettier-ignore
protected get(): [
number, number, number, number, number, number, number, number,
number, number, number, number, number, number, number, number
] {
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
}
// prettier-ignore
protected set(
Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,
Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number
) {
this.Ah = Ah | 0;
this.Al = Al | 0;
this.Bh = Bh | 0;
this.Bl = Bl | 0;
this.Ch = Ch | 0;
this.Cl = Cl | 0;
this.Dh = Dh | 0;
this.Dl = Dl | 0;
this.Eh = Eh | 0;
this.El = El | 0;
this.Fh = Fh | 0;
this.Fl = Fl | 0;
this.Gh = Gh | 0;
this.Gl = Gl | 0;
this.Hh = Hh | 0;
this.Hl = Hl | 0;
}
protected process(view: DataView, offset: number) {
// Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array
for (let i = 0; i < 16; i++, offset += 4) {
SHA512_W_H[i] = view.getUint32(offset);
SHA512_W_L[i] = view.getUint32((offset += 4));
}
for (let i = 16; i < 80; i++) {
// s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)
const W15h = SHA512_W_H[i - 15] | 0;
const W15l = SHA512_W_L[i - 15] | 0;
const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);
const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);
// s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)
const W2h = SHA512_W_H[i - 2] | 0;
const W2l = SHA512_W_L[i - 2] | 0;
const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);
const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);
// SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];
const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
SHA512_W_H[i] = SUMh | 0;
SHA512_W_L[i] = SUMl | 0;
}
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
// Compression function main loop, 80 rounds
for (let i = 0; i < 80; i++) {
// S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)
const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);
const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);
//const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const CHIh = (Eh & Fh) ^ (~Eh & Gh);
const CHIl = (El & Fl) ^ (~El & Gl);
// T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]
// prettier-ignore
const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
const T1l = T1ll | 0;
// S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)
const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);
const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);
const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);
const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);
Hh = Gh | 0;
Hl = Gl | 0;
Gh = Fh | 0;
Gl = Fl | 0;
Fh = Eh | 0;
Fl = El | 0;
({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
Dh = Ch | 0;
Dl = Cl | 0;
Ch = Bh | 0;
Cl = Bl | 0;
Bh = Ah | 0;
Bl = Al | 0;
const All = u64.add3L(T1l, sigma0l, MAJl);
Ah = u64.add3H(All, T1h, sigma0h, MAJh);
Al = All | 0;
}
// Add the compressed chunk to the current hash value
({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
}
protected roundClean() {
SHA512_W_H.fill(0);
SHA512_W_L.fill(0);
}
destroy() {
this.buffer.fill(0);
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
class SHA512_256 extends SHA512 {
// h -- high 32 bits, l -- low 32 bits
override Ah = 0x22312194 | 0;
override Al = 0xfc2bf72c | 0;
override Bh = 0x9f555fa3 | 0;
override Bl = 0xc84c64c2 | 0;
override Ch = 0x2393b86b | 0;
override Cl = 0x6f53b151 | 0;
override Dh = 0x96387719 | 0;
override Dl = 0x5940eabd | 0;
override Eh = 0x96283ee2 | 0;
override El = 0xa88effe3 | 0;
override Fh = 0xbe5e1e25 | 0;
override Fl = 0x53863992 | 0;
override Gh = 0x2b0199fc | 0;
override Gl = 0x2c85b8aa | 0;
override Hh = 0x0eb72ddc | 0;
override Hl = 0x81c52ca2 | 0;
constructor() {
super();
this.outputLen = 32;
}
}
class SHA384 extends SHA512 {
// h -- high 32 bits, l -- low 32 bits
override Ah = 0xcbbb9d5d | 0;
override Al = 0xc1059ed8 | 0;
override Bh = 0x629a292a | 0;
override Bl = 0x367cd507 | 0;
override Ch = 0x9159015a | 0;
override Cl = 0x3070dd17 | 0;
override Dh = 0x152fecd8 | 0;
override Dl = 0xf70e5939 | 0;
override Eh = 0x67332667 | 0;
override El = 0xffc00b31 | 0;
override Fh = 0x8eb44a87 | 0;
override Fl = 0x68581511 | 0;
override Gh = 0xdb0c2e0d | 0;
override Gl = 0x64f98fa7 | 0;
override Hh = 0x47b5481d | 0;
override Hl = 0xbefa4fa4 | 0;
constructor() {
super();
this.outputLen = 48;
}
}
export const sha512 = wrapConstructor(() => new SHA512());
export const sha512_256 = wrapConstructor(() => new SHA512_256());
export const sha384 = wrapConstructor(() => new SHA384());
@@ -0,0 +1,177 @@
/*! noble-hashes - MIT License (c) 2021 Paul Miller (paulmillr.com) */
// https://github.com/paulmillr/noble-hashes/pull/13
// prettier-ignore
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
Uint16Array | Int16Array | Uint32Array | Int32Array;
// helper to protect against older bundlers
export const _n: (value: string | number | bigint | boolean) => bigint =
typeof BigInt !== 'undefined' ? BigInt : () => Number.NaN as unknown as bigint;
// Cast array to different type
export const u8 = (arr: TypedArray) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
export const u32 = (arr: TypedArray) =>
new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
// Cast array to view
export const createView = (arr: TypedArray) =>
new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
// The rotate right (circular right shift) operation for uint32
export const rotr = (word: number, shift: number) => (word << (32 - shift)) | (word >>> shift);
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
// There is almost no big endian hardware, but js typed arrays uses platform specific endianess.
// So, just to be sure not to corrupt anything.
if (!isLE) throw new Error('Non little-endian hardware is not supported');
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
export function bytesToHex(uint8a: Uint8Array): string {
// pre-caching chars could speed this up 6x.
let hex = '';
for (let i = 0; i < uint8a.length; i++) {
hex += hexes[uint8a[i]];
}
return hex;
}
// Currently avoid insertion of polyfills with packers (browserify/webpack/etc)
// But setTimeout is pretty slow, maybe worth to investigate howto do minimal polyfill here
export const nextTick: () => Promise<unknown> = (() => {
const nodeRequire =
typeof module !== 'undefined' &&
typeof module.require === 'function' &&
module.require.bind(module);
try {
if (nodeRequire) {
const { setImmediate } = nodeRequire('timers');
return () => new Promise((resolve) => setImmediate(resolve));
}
} catch (e) {}
return () => new Promise((resolve) => setTimeout(resolve, 0));
})();
// Returns control to thread each 'tick' ms to avoid blocking
export async function asyncLoop(iters: number, tick: number, cb: (i: number) => void) {
let ts = Date.now();
for (let i = 0; i < iters; i++) {
cb(i);
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
const diff = Date.now() - ts;
if (diff >= 0 && diff < tick) continue;
await nextTick();
ts += diff;
}
}
export type Input = Uint8Array | string;
export function toBytes(data: Input) {
if (typeof data === 'string') data = new TextEncoder().encode(data);
if (!(data instanceof Uint8Array))
throw new TypeError(`Expected input type is Uint8Array (got ${typeof data})`);
return data;
}
export function assertNumber(n: number) {
if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`);
}
export function assertBool(b: boolean) {
if (typeof b !== 'boolean') {
throw new Error(`Expected boolean, not ${b}`);
}
}
export function assertHash(hash: CHash) {
if (typeof hash !== 'function' || typeof hash.init !== 'function')
throw new Error('Hash should be wrapped by utils.wrapConstructor');
assertNumber(hash.outputLen);
assertNumber(hash.blockLen);
}
// For runtime check if class implements interface
export abstract class Hash<T extends Hash<T>> {
abstract blockLen: number; // Bytes per block
abstract outputLen: number; // Bytes in output
abstract update(buf: Input): this;
// Writes digest into buf
abstract digestInto(buf: Uint8Array): void;
abstract digest(): Uint8Array;
// Cleanup internal state. Not '.clean' because instance is not usable after that.
// Clean usually resets instance to initial state, but it is not possible for keyed hashes if key is consumed into state.
// NOTE: if digest is not consumed by user, user need manually call '.destroy' if zeroing is required
abstract destroy(): void;
// Unsafe because doesn't check if "to" is correct. Can be used as clone() if no opts passed.
// Why cloneInto instead of clone? Mostly performance (same as _digestInto), but also has nice property: it reuses instance
// which means all internal buffers is overwritten, which also causes overwrite buffer which used for digest (in some cases).
// We don't provide any guarantees about cleanup (it is impossible to!), so should be enough for now.
abstract _cloneInto(to?: T): T;
// Safe version that clones internal state
clone(): T {
return this._cloneInto();
}
}
export type HashXOF<T extends Hash<T>> = Hash<T> & {
// XOF: streaming API to read digest in chunks. Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.
// NOTE: when hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot destroy state,
// next call can require more bytes.
xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream
xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf
};
// Check if object doens't have custom constructor (like Uint8Array/Array)
const isPlainObject = (obj: any) =>
Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
type EmptyObj = {};
export function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(def: T1, _opts?: T2): T1 & T2 {
if (_opts !== undefined && (typeof _opts !== 'object' || !isPlainObject(_opts)))
throw new TypeError('Options should be object or undefined');
const opts = Object.assign(def, _opts);
return opts as T1 & T2;
}
export type CHash = ReturnType<typeof wrapConstructor>;
export function wrapConstructor<T extends Hash<T>>(hashConstructor: () => Hash<T>) {
const hashC = (message: Input): Uint8Array => hashConstructor().update(toBytes(message)).digest();
const tmp = hashConstructor();
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = () => hashConstructor();
hashC.init = hashC.create;
return hashC;
}
export function wrapConstructorWithOpts<H extends Hash<H>, T extends Object>(
hashCons: (opts?: T) => Hash<H>
) {
const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();
const tmp = hashCons({} as T);
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (opts: T) => hashCons(opts);
hashC.init = hashC.create;
return hashC;
}
export const crypto: { node?: any; web?: any } = (() => {
const webCrypto = typeof self === 'object' && 'crypto' in self ? self.crypto : undefined;
const nodeRequire = typeof module !== 'undefined' && typeof require === 'function';
return {
node: nodeRequire && !webCrypto ? require('crypto') : undefined,
web: webCrypto,
};
})();
export function randomBytes(bytesLength = 32): Uint8Array {
if (crypto.web) {
return crypto.web.getRandomValues(new Uint8Array(bytesLength));
} else if (crypto.node) {
return new Uint8Array(crypto.node.randomBytes(bytesLength).buffer);
} else {
throw new Error("The environment doesn't have randomBytes function");
}
}
+1 -1
View File
@@ -3,4 +3,4 @@
// Auto-generated by @polkadot/dev, do not edit
export const packageInfo = { name: '@polkadot/util-crypto', version: '7.9.2' };
export const packageInfo = { name: '@polkadot/util-crypto', version: '8.0.2' };
@@ -4,6 +4,7 @@
import { u8aEq, u8aToHex } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceWasm } from '../../test/performance';
import { randomAsU8a } from '../random/asU8a';
import { pbkdf2Encode } from '.';
@@ -40,4 +41,8 @@ describe('pbkdf2Encode', (): void => {
);
});
});
performanceWasm('pbkdf2Encode', 8, (input, onlyJs) =>
pbkdf2Encode(input, input, undefined, onlyJs)
);
});
+6 -6
View File
@@ -3,12 +3,12 @@
import type { HexString } from '@polkadot/util/types';
import { u8aToU8a } from '@polkadot/util';
import { pbkdf2 } from '@polkadot/wasm-crypto';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, pbkdf2 } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { pbkdf2 as pbkdf2Js } from '../noble-hashes/lib/pbkdf2';
import { sha512 } from '../noble-hashes/lib/sha512';
import { randomAsU8a } from '../random/asU8a';
import { pbkdf2Sync } from './pbkdf2';
interface Result {
password: Uint8Array;
@@ -21,9 +21,9 @@ export function pbkdf2Encode (passphrase?: HexString | Buffer | Uint8Array | str
const u8aSalt = u8aToU8a(salt);
return {
password: isWasmOnly(onlyJs)
password: !hasBigInt || (!onlyJs && isReady())
? pbkdf2(u8aPass, u8aSalt, rounds)
: pbkdf2Sync(u8aPass, u8aSalt, rounds),
: pbkdf2Js(sha512, u8aPass, u8aSalt, { c: rounds, dkLen: 64 }),
rounds,
salt
};
-38
View File
@@ -1,38 +0,0 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
// Adapted from https://gist.github.com/calvinmetcalf/91e8e84dc63c75f2aa53
import { bnToU8a, u8aConcat } from '@polkadot/util';
import { BN_BE_32_OPTS } from '../bn';
import { hmacShaAsU8a } from '../hmac';
export function pbkdf2Sync (password: Uint8Array, salt: Uint8Array, rounds: number, len = 64): Uint8Array {
let out = new Uint8Array();
let num = 0;
const block = u8aConcat(salt, new Uint8Array(4));
while (out.length < len) {
num++;
block.set(bnToU8a(num, BN_BE_32_OPTS), salt.length);
let prev = hmacShaAsU8a(password, block, 512);
const md = prev;
let i = 0;
while (++i < rounds) {
prev = hmacShaAsU8a(password, prev, 512);
let j = -1;
while (++j < prev.length) {
md[j] ^= prev[j];
}
}
out = u8aConcat(out, md);
}
return out.slice(0, len);
}
@@ -1,12 +0,0 @@
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
export { schnorrkelAgreement } from './agreement';
export { schnorrkelDeriveHard } from './deriveHard';
export { schnorrkelDerivePublic } from './derivePublic';
export { schnorrkelDeriveSoft } from './deriveSoft';
export { schnorrkelKeypairFromSeed } from './keypair/fromSeed';
export { schnorrkelSign } from './sign';
export { schnorrkelVerify } from './verify';
export { schnorrkelVrfSign } from './vrfSign';
export { schnorrkelVrfVerify } from './vrfVerify';
@@ -3,7 +3,7 @@
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { DEFAULT_PARAMS } from './defaults';
import { scryptEncode } from '.';
@@ -30,7 +30,7 @@ describe('scryptEncode', (): void => {
});
});
performanceTest('scryptEncode', 2, (input, onlyJs) =>
performanceWasm('scryptEncode', 4, (input, onlyJs) =>
scryptEncode(input, input, undefined, onlyJs)
);
});
+10 -12
View File
@@ -4,12 +4,10 @@
import type { HexString } from '@polkadot/util/types';
import type { Params } from './types';
import scryptsy from 'scryptsy';
import { hasBigInt, objectSpread, u8aToU8a } from '@polkadot/util';
import { isReady, scrypt } from '@polkadot/wasm-crypto';
import { bufferToU8a, u8aToBuffer, u8aToU8a } from '@polkadot/util';
import { scrypt } from '@polkadot/wasm-crypto';
import { isWasmOnly } from '../helpers';
import { scrypt as scryptJs } from '../noble-hashes/lib/scrypt';
import { randomAsU8a } from '../random/asU8a';
import { DEFAULT_PARAMS } from './defaults';
@@ -22,11 +20,11 @@ interface Result {
export function scryptEncode (passphrase?: HexString | Uint8Array | string, salt = randomAsU8a(), params = DEFAULT_PARAMS, onlyJs?: boolean): Result {
const u8a = u8aToU8a(passphrase);
const password = isWasmOnly(onlyJs)
? scrypt(u8a, salt, Math.log2(params.N), params.r, params.p)
: bufferToU8a(
scryptsy(u8aToBuffer(u8a), u8aToBuffer(salt), params.N, params.r, params.p, 64)
);
return { params, password, salt };
return {
params,
password: !hasBigInt || (!onlyJs && isReady())
? scrypt(u8a, salt, Math.log2(params.N), params.r, params.p)
: scryptJs(u8a, salt, objectSpread({ dkLen: 64 }, params)),
salt
};
}
+1 -1
View File
@@ -3,7 +3,7 @@
export { secp256k1Compress } from './compress';
export { secp256k1Expand } from './expand';
export { secp256k1KeypairFromSeed } from './keypair/fromSeed';
export { secp256k1PairFromSeed } from './pair/fromSeed';
export { secp256k1Recover } from './recover';
export { secp256k1Sign } from './sign';
export { secp256k1PrivateKeyTweakAdd } from './tweakAdd';
@@ -4,7 +4,7 @@
import { hexToU8a, u8aToHex } from '@polkadot/util';
import { mnemonicToMiniSecret } from '../../mnemonic';
import { secp256k1KeypairFromSeed } from '..';
import { secp256k1PairFromSeed } from '..';
// mnemonic, secret, public, account_id
type Test = [string, string, string, string];
@@ -36,7 +36,7 @@ const tests: Test[] = [
]
];
describe('secp256k1KeypairFromSeed', (): void => {
describe('secp256k1PairFromSeed', (): void => {
const TEST = hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae');
const RESULT = {
publicKey: hexToU8a('0x03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b'),
@@ -44,13 +44,13 @@ describe('secp256k1KeypairFromSeed', (): void => {
};
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
expect(secp256k1KeypairFromSeed(TEST)).toEqual(RESULT);
expect(secp256k1PairFromSeed(TEST)).toEqual(RESULT);
});
tests.forEach(([mnemonic, secretKey, publicKey], index): void => {
it(`creates valid against known (${index})`, (): void => {
const seed = mnemonicToMiniSecret(mnemonic);
const pair = secp256k1KeypairFromSeed(seed);
const pair = secp256k1PairFromSeed(seed);
expect(u8aToHex(pair.secretKey)).toEqual(secretKey);
expect(u8aToHex(pair.publicKey)).toEqual(publicKey);
@@ -9,10 +9,10 @@ import { BN_BE_256_OPTS } from '../../bn';
import { secp256k1 } from '../secp256k1';
/**
* @name secp256k1KeypairFromSeed
* @name secp256k1PairFromSeed
* @description Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed.
*/
export function secp256k1KeypairFromSeed (seed: Uint8Array): Keypair {
export function secp256k1PairFromSeed (seed: Uint8Array): Keypair {
assert(seed.length === 32, 'Expected valid 32-byte private key as a seed');
const key = secp256k1.keyFromPrivate(seed);
@@ -5,7 +5,7 @@ import { stringToU8a } from '@polkadot/util';
import { randomAsU8a } from '../random/asU8a';
import { secp256k1Hasher } from './hasher';
import { secp256k1Expand, secp256k1KeypairFromSeed, secp256k1Sign, secp256k1Verify } from '.';
import { secp256k1Expand, secp256k1PairFromSeed, secp256k1Sign, secp256k1Verify } from '.';
const MESSAGE = stringToU8a('this is a message');
@@ -19,13 +19,13 @@ describe('sign and verify', (): void => {
});
it('has 65-byte signatures', (): void => {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
expect(secp256k1Sign(MESSAGE, pair)).toHaveLength(65);
});
it('signs/verifies a message by random key (blake2)', (): void => {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair);
const address = secp256k1Hasher('blake2', pair.publicKey);
@@ -33,7 +33,7 @@ describe('sign and verify', (): void => {
});
it('signs/verifies a message by random key (keccak)', (): void => {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
@@ -41,7 +41,7 @@ describe('sign and verify', (): void => {
});
it('fails verification on hasher mismatches', (): void => {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
@@ -50,7 +50,7 @@ describe('sign and verify', (): void => {
it('works over a range of random keys (blake2)', (): void => {
for (let i = 0; i < 256; i++) {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
try {
expect(
@@ -70,7 +70,7 @@ describe('sign and verify', (): void => {
it('works over a range of random keys (keccak)', (): void => {
for (let i = 0; i < 256; i++) {
const pair = secp256k1KeypairFromSeed(randomAsU8a());
const pair = secp256k1PairFromSeed(randomAsU8a());
try {
expect(
+20 -11
View File
@@ -3,31 +3,40 @@
import type { HexString } from '@polkadot/util/types';
import hash from 'hash.js';
import { hasBigInt, u8aToU8a } from '@polkadot/util';
import { isReady, sha256, sha512 } from '@polkadot/wasm-crypto';
import { u8aToU8a } from '@polkadot/util';
import { sha256, sha512 } from '@polkadot/wasm-crypto';
import { createBitHasher } from '../helpers';
import { sha256 as sha256Js } from '../noble-hashes/lib/sha256';
import { sha512 as sha512Js } from '../noble-hashes/lib/sha512';
import { createBitHasher, isWasmOnly } from '../helpers';
type BitLength = 256 | 512;
/**
* @name shaAsU8a
* @summary Creates a sha Uint8Array from the input.
*/
export function shaAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: 256 | 512 = 256, onlyJs?: boolean): Uint8Array {
export function shaAsU8a (value: HexString | Buffer | Uint8Array | string, bitLength: BitLength = 256, onlyJs?: boolean): Uint8Array {
const is256 = bitLength === 256;
const u8a = u8aToU8a(value);
return isWasmOnly(onlyJs)
return !hasBigInt || (!onlyJs && isReady())
? is256
? sha256(u8a)
: sha512(u8a)
: new Uint8Array(
is256
? hash.sha256().update(u8a).digest()
: hash.sha512().update(u8a).digest()
);
: is256
? sha256Js(u8a)
: sha512Js(u8a);
}
/**
* @name sha256AsU8a
* @summary Creates a sha256 Uint8Array from the input.
*/
export const sha256AsU8a = createBitHasher(256, shaAsU8a);
/**
* @name sha512AsU8a
* @summary Creates a sha512 Uint8Array from the input.
*/
export const sha512AsU8a = createBitHasher(512, shaAsU8a);
@@ -4,7 +4,7 @@
import { hexToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { sha256AsU8a } from '.';
const TESTS = [
@@ -43,7 +43,7 @@ describe('sha256AsU8a', (): void => {
});
});
performanceTest('sha256AsU8a', 128000, (input, onlyJs) =>
performanceWasm('sha256AsU8a', 128000, (input, onlyJs) =>
sha256AsU8a(input, onlyJs)
);
});
@@ -3,7 +3,7 @@
import { waitReady } from '@polkadot/wasm-crypto';
import { performanceTest } from '../../test/performance';
import { performanceWasm } from '../../test/performance';
import { sha512AsU8a } from '.';
describe('sha512AsU8a', (): void => {
@@ -23,7 +23,7 @@ describe('sha512AsU8a', (): void => {
});
});
performanceTest('sha512AsU8a', 64000, (input, onlyJs) =>
performanceWasm('sha512AsU8a', 64000, (input, onlyJs) =>
sha512AsU8a(input, onlyJs)
);
});
+6 -6
View File
@@ -7,9 +7,9 @@ import type { KeypairType, VerifyResult } from '../types';
import { assert, u8aIsWrapped, u8aToU8a, u8aUnwrapBytes, u8aWrapBytes } from '@polkadot/util';
import { decodeAddress } from '../address/decode';
import { naclVerify } from '../nacl/verify';
import { schnorrkelVerify } from '../schnorrkel/verify';
import { ed25519Verify } from '../ed25519/verify';
import { secp256k1Verify } from '../secp256k1/verify';
import { sr25519Verify } from '../sr25519/verify';
interface VerifyInput {
message: Uint8Array;
@@ -31,8 +31,8 @@ const VERIFIERS_ECDSA: Verifier[] = [
];
const VERIFIERS: Verifier[] = [
['ed25519', naclVerify],
['sr25519', schnorrkelVerify],
['ed25519', ed25519Verify],
['sr25519', sr25519Verify],
...VERIFIERS_ECDSA
];
@@ -66,9 +66,9 @@ function verifyMultisig (result: VerifyResult, { message, publicKey, signature }
try {
result.isValid = {
ecdsa: () => verifyDetect(result, { message, publicKey, signature: signature.subarray(1) }, VERIFIERS_ECDSA).isValid,
ed25519: () => naclVerify(message, signature.subarray(1), publicKey),
ed25519: () => ed25519Verify(message, signature.subarray(1), publicKey),
none: () => { throw Error('no verify for `none` crypto type'); },
sr25519: () => schnorrkelVerify(message, signature.subarray(1), publicKey)
sr25519: () => sr25519Verify(message, signature.subarray(1), publicKey)
}[type]();
} catch (error) {
// ignore, result.isValid still set to false
@@ -6,7 +6,7 @@ import type { Keypair } from '../types';
import { u8aToHex } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { schnorrkelAgreement, schnorrkelKeypairFromSeed } from '.';
import { sr25519Agreement, sr25519PairFromSeed } from '.';
describe('agreement', (): void => {
let pairA: Keypair;
@@ -15,18 +15,18 @@ describe('agreement', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
pairA = schnorrkelKeypairFromSeed('0x98b3d305d5a5eace562387e47e59badd4d77e3f72cabfb10a60f8a197059f0a8');
pairB = schnorrkelKeypairFromSeed('0x9732eea001851ff862d949a1699c9971f3a26edbede2ad7922cbbe9a0701f366');
pairA = sr25519PairFromSeed('0x98b3d305d5a5eace562387e47e59badd4d77e3f72cabfb10a60f8a197059f0a8');
pairB = sr25519PairFromSeed('0x9732eea001851ff862d949a1699c9971f3a26edbede2ad7922cbbe9a0701f366');
});
it('matches a known agreement (both ways)', (): void => {
const TEST = '0xb03a0b198c34c16f35cae933d88b16341b4cef3e84e851f20e664c6a30527f4e';
expect(
u8aToHex(schnorrkelAgreement(pairA.secretKey, pairB.publicKey))
u8aToHex(sr25519Agreement(pairA.secretKey, pairB.publicKey))
).toEqual(TEST);
expect(
u8aToHex(schnorrkelAgreement(pairB.secretKey, pairA.publicKey))
u8aToHex(sr25519Agreement(pairB.secretKey, pairA.publicKey))
).toEqual(TEST);
});
});
@@ -7,10 +7,10 @@ import { assert, u8aToU8a } from '@polkadot/util';
import { sr25519Agree } from '@polkadot/wasm-crypto';
/**
* @name schnorrkelAgreement
* @name sr25519Agreement
* @description Key agreement between other's public key and self secret key
*/
export function schnorrkelAgreement (secretKey: HexString | Uint8Array | string, publicKey: HexString | Uint8Array | string): Uint8Array {
export function sr25519Agreement (secretKey: HexString | Uint8Array | string, publicKey: HexString | Uint8Array | string): Uint8Array {
const secretKeyU8a = u8aToU8a(secretKey);
const publicKeyU8a = u8aToU8a(publicKey);
@@ -5,15 +5,15 @@ import type { Keypair } from '../types';
import { assert, isU8a } from '@polkadot/util';
import { schnorrkelKeypairFromU8a } from './keypair/fromU8a';
import { schnorrkelKeypairToU8a } from './keypair/toU8a';
import { sr25519PairFromU8a } from './pair/fromU8a';
import { sr25519KeypairToU8a } from './pair/toU8a';
export function createDeriveFn (derive: (pair: Uint8Array, cc: Uint8Array) => Uint8Array): (keypair: Keypair, chainCode: Uint8Array) => Keypair {
return (keypair: Keypair, chainCode: Uint8Array): Keypair => {
assert(isU8a(chainCode) && chainCode.length === 32, 'Invalid chainCode passed to derive');
return schnorrkelKeypairFromU8a(
derive(schnorrkelKeypairToU8a(keypair), chainCode)
return sr25519PairFromU8a(
derive(sr25519KeypairToU8a(keypair), chainCode)
);
};
}
@@ -5,4 +5,4 @@ import { sr25519DeriveKeypairHard } from '@polkadot/wasm-crypto';
import { createDeriveFn } from './derive';
export const schnorrkelDeriveHard = createDeriveFn(sr25519DeriveKeypairHard);
export const sr25519DeriveHard = createDeriveFn(sr25519DeriveKeypairHard);

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