Merge pull request #196 from CosmWasm/add-iov-deps
Fork IOV dependencies
This commit is contained in:
commit
16eb718b45
9
NOTICE
9
NOTICE
@ -8,6 +8,15 @@ The code in packages/faucet was forked from https://github.com/iov-one/iov-fauce
|
||||
The code in packages/cli was forked from https://github.com/iov-one/iov-core/tree/v2.0.0-alpha.7/packages/iov-cli
|
||||
on 2020-02-06.
|
||||
|
||||
The code in packages/crypto was forked from https://github.com/iov-one/iov-core/tree/v2.3.2/packages/iov-crypto
|
||||
on 2020-06-05.
|
||||
|
||||
The code in packages/utils was forked from https://github.com/iov-one/iov-core/tree/v2.3.2/packages/iov-utils
|
||||
on 2020-06-05.
|
||||
|
||||
The code in packages/encoding and packages/math was forked from https://github.com/iov-one/iov-core/tree/v2.3.2/packages/iov-encoding
|
||||
on 2020-06-05.
|
||||
|
||||
Copyright 2018-2020 IOV SAS
|
||||
Copyright 2020 Confio UO
|
||||
Copyright 2020 Simon Warta
|
||||
|
||||
@ -20,9 +20,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/bcp"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docs": "shx rm -rf docs && typedoc --options typedoc.js",
|
||||
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
|
||||
@ -39,12 +37,13 @@
|
||||
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/crypto": "^0.8.0",
|
||||
"@cosmjs/encoding": "^0.8.0",
|
||||
"@cosmjs/math": "^0.8.0",
|
||||
"@cosmjs/sdk38": "^0.8.0",
|
||||
"@cosmjs/utils": "^0.8.0",
|
||||
"@iov/bcp": "^2.3.2",
|
||||
"@iov/crypto": "^2.3.2",
|
||||
"@iov/encoding": "^2.3.2",
|
||||
"@iov/stream": "^2.3.2",
|
||||
"@iov/utils": "^2.3.2",
|
||||
"bn.js": "^5.1.1",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"readonly-date": "^1.0.0",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { fromBase64, fromHex } from "@cosmjs/encoding";
|
||||
import { Algorithm, PubkeyBytes } from "@iov/bcp";
|
||||
import { fromBase64, fromHex } from "@iov/encoding";
|
||||
|
||||
import { pubkeyToAddress } from "./address";
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Secp256k1 } from "@cosmjs/crypto";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
import { PubKey, pubkeyToAddress as sdkPubkeyToAddress, pubkeyType } from "@cosmjs/sdk38";
|
||||
import { Address, Algorithm, PubkeyBundle } from "@iov/bcp";
|
||||
import { Secp256k1 } from "@iov/crypto";
|
||||
import { toBase64 } from "@iov/encoding";
|
||||
|
||||
// See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography
|
||||
export function pubkeyToAddress(pubkey: PubkeyBundle, prefix: string): Address {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { toHex, toUtf8 } from "@cosmjs/encoding";
|
||||
import { ChainId } from "@iov/bcp";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { toHex, toUtf8 } from "@iov/encoding";
|
||||
|
||||
const hashedPrefix = "hashed-";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import { PostableBytes, PrehashType } from "@iov/bcp";
|
||||
import { toUtf8 } from "@iov/encoding";
|
||||
|
||||
import { CosmosCodec } from "./cosmoscodec";
|
||||
import { chainId, nonce, sendTxJson, signedTxBin, signedTxEncodedJson, signedTxJson } from "./testdata.spec";
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Bech32, fromUtf8, toUtf8 } from "@cosmjs/encoding";
|
||||
import { isStdTx, makeSignBytes, StdTx } from "@cosmjs/sdk38";
|
||||
import {
|
||||
Address,
|
||||
@ -14,7 +15,6 @@ import {
|
||||
TxCodec,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { Bech32, fromUtf8, toUtf8 } from "@iov/encoding";
|
||||
|
||||
import { pubkeyToAddress } from "./address";
|
||||
import { Caip5 } from "./caip5";
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import { Random, Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto";
|
||||
import { Bech32, fromBase64 } from "@cosmjs/encoding";
|
||||
import { decodeSignature } from "@cosmjs/sdk38";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import {
|
||||
Account,
|
||||
Address,
|
||||
@ -17,10 +20,7 @@ import {
|
||||
TransactionState,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { Random, Secp256k1, Secp256k1Signature, Sha256 } from "@iov/crypto";
|
||||
import { Bech32, fromBase64 } from "@iov/encoding";
|
||||
import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol";
|
||||
import { assert } from "@iov/utils";
|
||||
import BN from "bn.js";
|
||||
|
||||
import { CosmosConnection, TokenConfiguration } from "./cosmosconnection";
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { fromUtf8 } from "@cosmjs/encoding";
|
||||
import { Uint53 } from "@cosmjs/math";
|
||||
import {
|
||||
CosmosClient,
|
||||
findSequenceForSignedTx,
|
||||
@ -35,7 +37,6 @@ import {
|
||||
TxCodec,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { fromUtf8, Uint53 } from "@iov/encoding";
|
||||
import { concat, DefaultValueProducer, ValueAndUpdates } from "@iov/stream";
|
||||
import equal from "fast-deep-equal";
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { fromBase64, fromHex } from "@cosmjs/encoding";
|
||||
import { Coin, IndexedTx, Msg, PubKey, StdSignature } from "@cosmjs/sdk38";
|
||||
import { Address, Algorithm, SendTransaction, TokenTicker } from "@iov/bcp";
|
||||
import { fromBase64, fromHex } from "@iov/encoding";
|
||||
|
||||
import {
|
||||
decodeAmount,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import {
|
||||
Coin,
|
||||
IndexedTx,
|
||||
@ -29,7 +31,6 @@ import {
|
||||
TransactionId,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { Decimal, fromBase64 } from "@iov/encoding";
|
||||
|
||||
import { BankToken } from "./types";
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
Address,
|
||||
Algorithm,
|
||||
@ -11,7 +12,6 @@ import {
|
||||
SignedTransaction,
|
||||
TokenTicker,
|
||||
} from "@iov/bcp";
|
||||
import { fromBase64 } from "@iov/encoding";
|
||||
|
||||
import {
|
||||
buildSignedTx,
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Secp256k1 } from "@cosmjs/crypto";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
Coin,
|
||||
CosmosSdkTx,
|
||||
@ -19,8 +21,6 @@ import {
|
||||
SignedTransaction,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { Secp256k1 } from "@iov/crypto";
|
||||
import { toBase64 } from "@iov/encoding";
|
||||
|
||||
import { BankToken } from "./types";
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { fromBase64, toUtf8 } from "@cosmjs/encoding";
|
||||
import {
|
||||
Address,
|
||||
Algorithm,
|
||||
@ -12,7 +13,6 @@ import {
|
||||
TokenTicker,
|
||||
TransactionId,
|
||||
} from "@iov/bcp";
|
||||
import { fromBase64, toUtf8 } from "@iov/encoding";
|
||||
|
||||
import data from "./testdata/cosmoshub.json";
|
||||
|
||||
|
||||
2
packages/bcp/types/decode.d.ts
vendored
2
packages/bcp/types/decode.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { Coin, IndexedTx, Msg, PubKey, StdFee, StdSignature, StdTx } from "@cosmjs/sdk38";
|
||||
import {
|
||||
Amount,
|
||||
@ -12,7 +13,6 @@ import {
|
||||
SignedTransaction,
|
||||
UnsignedTransaction,
|
||||
} from "@iov/bcp";
|
||||
import { Decimal } from "@iov/encoding";
|
||||
import { BankToken } from "./types";
|
||||
export declare function decodePubkey(pubkey: PubKey): PubkeyBundle;
|
||||
export declare function decodeSignature(signature: string): SignatureBytes;
|
||||
|
||||
@ -39,10 +39,11 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@cosmjs/cosmwasm": "^0.8.0",
|
||||
"@cosmjs/crypto": "^0.8.0",
|
||||
"@cosmjs/encoding": "^0.8.0",
|
||||
"@cosmjs/math": "^0.8.0",
|
||||
"@cosmjs/sdk38": "^0.8.0",
|
||||
"@iov/crypto": "^2.3.2",
|
||||
"@iov/encoding": "^2.3.2",
|
||||
"@iov/utils": "^2.3.2",
|
||||
"@cosmjs/utils": "^0.8.0",
|
||||
"axios": "^0.19.2",
|
||||
"babylon": "^6.18.0",
|
||||
"colors": "^1.3.3",
|
||||
|
||||
@ -59,6 +59,27 @@ export function main(originalArgs: readonly string[]): void {
|
||||
"UploadResult",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@cosmjs/crypto",
|
||||
[
|
||||
"Bip39",
|
||||
"Ed25519",
|
||||
"Ed25519Keypair",
|
||||
"EnglishMnemonic",
|
||||
"Random",
|
||||
"Secp256k1",
|
||||
"Sha256",
|
||||
"Sha512",
|
||||
"Slip10",
|
||||
"Slip10Curve",
|
||||
"Slip10RawIndex",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@cosmjs/encoding",
|
||||
["fromAscii", "fromBase64", "fromHex", "fromUtf8", "toAscii", "toBase64", "toHex", "toUtf8", "Bech32"],
|
||||
],
|
||||
["@cosmjs/math", ["Decimal", "Int53", "Uint32", "Uint53", "Uint64"]],
|
||||
[
|
||||
"@cosmjs/sdk38",
|
||||
[
|
||||
@ -82,43 +103,7 @@ export function main(originalArgs: readonly string[]): void {
|
||||
"StdTx",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@iov/crypto",
|
||||
[
|
||||
"Bip39",
|
||||
"Ed25519",
|
||||
"Ed25519Keypair",
|
||||
"EnglishMnemonic",
|
||||
"Random",
|
||||
"Secp256k1",
|
||||
"Sha256",
|
||||
"Sha512",
|
||||
"Slip10",
|
||||
"Slip10Curve",
|
||||
"Slip10RawIndex",
|
||||
],
|
||||
],
|
||||
[
|
||||
"@iov/encoding",
|
||||
[
|
||||
"fromAscii",
|
||||
"fromBase64",
|
||||
"fromHex",
|
||||
"fromUtf8",
|
||||
"toAscii",
|
||||
"toBase64",
|
||||
"toHex",
|
||||
"toUtf8",
|
||||
"Bech32",
|
||||
"Decimal",
|
||||
// integers
|
||||
"Int53",
|
||||
"Uint32",
|
||||
"Uint53",
|
||||
"Uint64",
|
||||
],
|
||||
],
|
||||
["@iov/utils", ["assert", "sleep"]],
|
||||
["@cosmjs/utils", ["assert", "sleep"]],
|
||||
]);
|
||||
|
||||
console.info(colors.green("Initializing session for you. Have fun!"));
|
||||
|
||||
@ -36,10 +36,11 @@
|
||||
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/crypto": "^0.8.0",
|
||||
"@cosmjs/encoding": "^0.8.0",
|
||||
"@cosmjs/math": "^0.8.0",
|
||||
"@cosmjs/sdk38": "^0.8.0",
|
||||
"@iov/crypto": "^2.3.2",
|
||||
"@iov/encoding": "^2.3.2",
|
||||
"@iov/utils": "^2.3.2",
|
||||
"@cosmjs/utils": "^0.8.0",
|
||||
"axios": "^0.19.0",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"pako": "^1.0.11"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Uint53 } from "@cosmjs/math";
|
||||
import { Coin, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38";
|
||||
import { Uint53 } from "@iov/encoding";
|
||||
import { assert, sleep } from "@iov/utils";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
|
||||
import { CosmWasmClient } from "./cosmwasmclient";
|
||||
import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs";
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@cosmjs/encoding";
|
||||
import { makeSignBytes, MsgSend, Secp256k1Pen, StdFee } from "@cosmjs/sdk38";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@iov/encoding";
|
||||
import { assert, sleep } from "@iov/utils";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
import { Code, CosmWasmClient, PrivateCosmWasmClient } from "./cosmwasmclient";
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
|
||||
import {
|
||||
BroadcastMode,
|
||||
Coin,
|
||||
@ -7,8 +9,6 @@ import {
|
||||
PubKey,
|
||||
StdTx,
|
||||
} from "@cosmjs/sdk38";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { fromBase64, fromHex, toHex } from "@iov/encoding";
|
||||
|
||||
import { Log, parseLogs } from "./logs";
|
||||
import { RestClient } from "./restclient";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { isNonNullObject } from "@iov/encoding";
|
||||
import { isNonNullObject } from "@cosmjs/utils";
|
||||
|
||||
export interface Attribute {
|
||||
readonly key: string;
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding";
|
||||
import {
|
||||
Coin,
|
||||
encodeBech32Pubkey,
|
||||
@ -14,9 +16,7 @@ import {
|
||||
StdSignature,
|
||||
StdTx,
|
||||
} from "@cosmjs/sdk38";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@iov/encoding";
|
||||
import { assert, sleep } from "@iov/utils";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
import { findAttribute, parseLogs } from "./logs";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { fromBase64, fromUtf8, toHex, toUtf8 } from "@cosmjs/encoding";
|
||||
import { BroadcastMode, CosmosSdkTx, RestClient as BaseRestClient } from "@cosmjs/sdk38";
|
||||
import { fromBase64, fromUtf8, toHex, toUtf8 } from "@iov/encoding";
|
||||
|
||||
import { JsonObject, Model, parseWasmData, WasmData } from "./types";
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { toHex } from "@cosmjs/encoding";
|
||||
import { Coin, Secp256k1Pen } from "@cosmjs/sdk38";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { toHex } from "@iov/encoding";
|
||||
import { assert } from "@iov/utils";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { PrivateCosmWasmClient } from "./cosmwasmclient";
|
||||
import { RestClient } from "./restclient";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Sha256 } from "@cosmjs/crypto";
|
||||
import { toBase64, toHex } from "@cosmjs/encoding";
|
||||
import { BroadcastMode, Coin, coins, makeSignBytes, MsgSend, StdFee, StdSignature } from "@cosmjs/sdk38";
|
||||
import { Sha256 } from "@iov/crypto";
|
||||
import { toBase64, toHex } from "@iov/encoding";
|
||||
import pako from "pako";
|
||||
|
||||
import { isValidBuilder } from "./builder";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Random } from "@iov/crypto";
|
||||
import { Bech32, fromBase64 } from "@iov/encoding";
|
||||
import { Random } from "@cosmjs/crypto";
|
||||
import { Bech32, fromBase64 } from "@cosmjs/encoding";
|
||||
|
||||
import hackatom from "./testdata/contract.json";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { fromBase64, fromHex } from "@iov/encoding";
|
||||
import { fromBase64, fromHex } from "@cosmjs/encoding";
|
||||
|
||||
export interface WasmData {
|
||||
// key is hex-encoded
|
||||
|
||||
8
packages/crypto/.eslintignore
Normal file
8
packages/crypto/.eslintignore
Normal file
@ -0,0 +1,8 @@
|
||||
node_modules/
|
||||
|
||||
build/
|
||||
custom_types/
|
||||
dist/
|
||||
docs/
|
||||
generated/
|
||||
types/
|
||||
3
packages/crypto/.gitignore
vendored
Normal file
3
packages/crypto/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
build/
|
||||
dist/
|
||||
docs/
|
||||
16
packages/crypto/README.md
Normal file
16
packages/crypto/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# @cosmjs/crypto
|
||||
|
||||
[](https://www.npmjs.com/package/@cosmjs/crypto)
|
||||
|
||||
This package contains low-level cryptographic functionality used in other
|
||||
@cosmjs libraries. Little of it is implemented here, but mainly it is a curation
|
||||
of external libraries along with correctness tests. We add type-safety, some
|
||||
more checks, and a simple API to these libraries. This can also be freely
|
||||
imported outside of CosmJS based applications.
|
||||
|
||||
## License
|
||||
|
||||
This package is part of the cosmjs repository, licensed under the Apache
|
||||
License 2.0 (see
|
||||
[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and
|
||||
[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)).
|
||||
26
packages/crypto/jasmine-testrunner.js
Executable file
26
packages/crypto/jasmine-testrunner.js
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require("source-map-support").install();
|
||||
const defaultSpecReporterConfig = require("../../jasmine-spec-reporter.config.json");
|
||||
|
||||
// setup Jasmine
|
||||
const Jasmine = require("jasmine");
|
||||
const jasmine = new Jasmine();
|
||||
jasmine.loadConfig({
|
||||
spec_dir: "build",
|
||||
spec_files: ["**/*.spec.js"],
|
||||
helpers: [],
|
||||
random: false,
|
||||
seed: null,
|
||||
stopSpecOnExpectationFailure: false,
|
||||
});
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000;
|
||||
|
||||
// setup reporter
|
||||
const { SpecReporter } = require("jasmine-spec-reporter");
|
||||
const reporter = new SpecReporter({ ...defaultSpecReporterConfig });
|
||||
|
||||
// initialize and execute
|
||||
jasmine.env.clearReporters();
|
||||
jasmine.addReporter(reporter);
|
||||
jasmine.execute();
|
||||
50
packages/crypto/karma.conf.js
Normal file
50
packages/crypto/karma.conf.js
Normal file
@ -0,0 +1,50 @@
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: ".",
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ["jasmine"],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: ["dist/web/tests.js"],
|
||||
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false,
|
||||
timeoutInterval: 15000,
|
||||
},
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ["progress", "kjhtml"],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
|
||||
// Use debug until Firefox starting issues on Travis are understood
|
||||
// https://travis-ci.com/iov-one/iov-core/jobs/174888518
|
||||
logLevel: config.LOG_DEBUG,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ["Firefox"],
|
||||
|
||||
browserNoActivityTimeout: 90000,
|
||||
|
||||
// Keep brower open for debugging. This is overridden by yarn scripts
|
||||
singleRun: false,
|
||||
});
|
||||
};
|
||||
1
packages/crypto/nonces/README.txt
Normal file
1
packages/crypto/nonces/README.txt
Normal file
@ -0,0 +1 @@
|
||||
Directory used to trigger lerna package updates for all packages
|
||||
67
packages/crypto/package.json
Normal file
67
packages/crypto/package.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "@cosmjs/crypto",
|
||||
"version": "0.8.0",
|
||||
"description": "Cryptography resources for blockchain projects",
|
||||
"contributors": [
|
||||
"IOV SAS <admin@iov.one>",
|
||||
"Simon Warta"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"main": "build/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"build/",
|
||||
"types/",
|
||||
"*.md",
|
||||
"!*.spec.*",
|
||||
"!**/testdata/"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/crypto"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"docs": "shx rm -rf docs && typedoc --options typedoc.js",
|
||||
"lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"",
|
||||
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
|
||||
"format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"",
|
||||
"test-node": "node jasmine-testrunner.js",
|
||||
"test-edge": "yarn pack-web && karma start --single-run --browsers Edge",
|
||||
"test-firefox": "yarn pack-web && karma start --single-run --browsers Firefox",
|
||||
"test-chrome": "yarn pack-web && karma start --single-run --browsers ChromeHeadless",
|
||||
"test-safari": "yarn pack-web && karma start --single-run --browsers Safari",
|
||||
"test": "yarn build-or-skip && yarn test-node",
|
||||
"move-types": "shx rm -r ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts",
|
||||
"format-types": "prettier --write --loglevel warn \"./types/**/*.d.ts\"",
|
||||
"build": "shx rm -rf ./build && tsc && yarn move-types && yarn format-types",
|
||||
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
|
||||
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/encoding": "^0.8.0",
|
||||
"@cosmjs/math": "^0.8.0",
|
||||
"@cosmjs/utils": "^0.8.0",
|
||||
"bip39": "^3.0.2",
|
||||
"bn.js": "^4.11.8",
|
||||
"elliptic": "^6.4.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"libsodium-wrappers": "^0.7.6",
|
||||
"pbkdf2": "^3.0.16",
|
||||
"ripemd160": "^2.0.2",
|
||||
"sha.js": "^2.4.11",
|
||||
"type-tagger": "^1.0.0",
|
||||
"unorm": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/elliptic": "^6.4.12",
|
||||
"@types/libsodium-wrappers": "^0.7.7",
|
||||
"@types/pbkdf2": "^3.0.0",
|
||||
"@types/ripemd160": "^2.0.0",
|
||||
"@types/sha.js": "^2.4.0",
|
||||
"@types/unorm": "^1.3.27"
|
||||
}
|
||||
}
|
||||
416
packages/crypto/src/bip39.spec.ts
Normal file
416
packages/crypto/src/bip39.spec.ts
Normal file
@ -0,0 +1,416 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Bip39 } from "./bip39";
|
||||
import { EnglishMnemonic } from "./englishmnemonic";
|
||||
import bip39Vectors from "./testdata/bip39.json";
|
||||
|
||||
describe("Bip39", () => {
|
||||
describe("encode", () => {
|
||||
it("can encode to mnemonic", () => {
|
||||
// Test vectors from https://github.com/trezor/python-mnemonic/blob/b502451a33a440783926e04428115e0bed87d01f/vectors.json
|
||||
// plus similar vectors generated for the missing lengths 15 and 21 words
|
||||
const { "12": vec12, "15": vec15, "18": vec18, "21": vec21, "24": vec24 } = bip39Vectors.encoding;
|
||||
for (const vectors of [vec12, vec15, vec18, vec21, vec24]) {
|
||||
for (const { entropy, mnemonic } of vectors) {
|
||||
expect(Bip39.encode(fromHex(entropy)).toString()).toEqual(mnemonic);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("throws for invalid input", () => {
|
||||
// invalid input length
|
||||
expect(() => Bip39.encode(fromHex(""))).toThrowError(/invalid input length/);
|
||||
expect(() => Bip39.encode(fromHex("00"))).toThrowError(/invalid input length/);
|
||||
expect(() => Bip39.encode(fromHex("000000000000000000000000000000"))).toThrowError(
|
||||
/invalid input length/,
|
||||
);
|
||||
expect(() => Bip39.encode(fromHex("0000000000000000000000000000000000"))).toThrowError(
|
||||
/invalid input length/,
|
||||
);
|
||||
expect(() => Bip39.encode(fromHex("0000000000000000000000000000000000000000000000"))).toThrowError(
|
||||
/invalid input length/,
|
||||
);
|
||||
expect(() => Bip39.encode(fromHex("00000000000000000000000000000000000000000000000000"))).toThrowError(
|
||||
/invalid input length/,
|
||||
);
|
||||
expect(() =>
|
||||
Bip39.encode(fromHex("00000000000000000000000000000000000000000000000000000000000000")),
|
||||
).toThrowError(/invalid input length/);
|
||||
expect(() =>
|
||||
Bip39.encode(fromHex("000000000000000000000000000000000000000000000000000000000000000000")),
|
||||
).toThrowError(/invalid input length/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("decode", () => {
|
||||
it("can decode from mnemonic", () => {
|
||||
const { "12": vec12, "15": vec15, "18": vec18, "21": vec21, "24": vec24 } = bip39Vectors.encoding;
|
||||
for (const vectors of [vec12, vec15, vec18, vec21, vec24]) {
|
||||
for (const { entropy, mnemonic } of vectors) {
|
||||
expect(Bip39.decode(new EnglishMnemonic(mnemonic))).toEqual(fromHex(entropy));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("mnemonicToSeed", () => {
|
||||
it("can calculate seed from mnemonic (trezor test vectors)", async () => {
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("legal winner thank year wave sausage worth useful legal winner thank yellow"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("scheme spot photo card baby mountain device kick cradle pact join borrow"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("cat swing flag economy stadium alone churn speed unique patch report train"),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"vessel ladder alter error federal sibling chat ability sun glass valve picture",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold",
|
||||
),
|
||||
"TREZOR",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("can calculate seed from mnemonic (no password)", async () => {
|
||||
// custom test vectors using
|
||||
// $ git clone https://github.com/trezor/python-mnemonic.git && cd python-mnemonic
|
||||
// $ python3 -m venv venv
|
||||
// $ source venv/bin/activate
|
||||
// $ pip install wheel bip32utils
|
||||
// $ pip install -r requirements.txt
|
||||
// patch generate_vectors.py to your needs
|
||||
// $ python generate_vectors.py
|
||||
|
||||
// empty password
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("robust pipe raise illness symptom crowd trip will slow assault recipe oven"),
|
||||
"",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"5539eed11e1096e9d52f69f15ad3d7c6547a40a3865b9517dbcbb03c31f231900622f58616d64d2d1cc0440f31d67fb0b2699a5fc885f796c746e0f844477093",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"pair ethics august street tornado spare present under capital raise cross current main craft stone clutch tray all",
|
||||
),
|
||||
"",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"1272467e954cec4e0ad720002d037a3aaf795a57ffbeea6aaa0c242d410eb52050292447aa2c68470a07ecc80171edfa9e027793265047be3128d94e867a4f99",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"allow finger front connect strategy purchase journey distance trouble guitar honey alpha giraffe canal junk vintage chronic blade gate custom soap flip first mix",
|
||||
),
|
||||
"",
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"476a41ac016b5bdf9f114456929975a036ae326e2efdca441ac5a0949ef89ab9246dc9e49a5d2d64d1926eb9dbe17576cb010471c2a821b216202acdf3d7a27b",
|
||||
),
|
||||
);
|
||||
|
||||
// no password
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic("robust pipe raise illness symptom crowd trip will slow assault recipe oven"),
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"5539eed11e1096e9d52f69f15ad3d7c6547a40a3865b9517dbcbb03c31f231900622f58616d64d2d1cc0440f31d67fb0b2699a5fc885f796c746e0f844477093",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"pair ethics august street tornado spare present under capital raise cross current main craft stone clutch tray all",
|
||||
),
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"1272467e954cec4e0ad720002d037a3aaf795a57ffbeea6aaa0c242d410eb52050292447aa2c68470a07ecc80171edfa9e027793265047be3128d94e867a4f99",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Bip39.mnemonicToSeed(
|
||||
new EnglishMnemonic(
|
||||
"allow finger front connect strategy purchase journey distance trouble guitar honey alpha giraffe canal junk vintage chronic blade gate custom soap flip first mix",
|
||||
),
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"476a41ac016b5bdf9f114456929975a036ae326e2efdca441ac5a0949ef89ab9246dc9e49a5d2d64d1926eb9dbe17576cb010471c2a821b216202acdf3d7a27b",
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
51
packages/crypto/src/bip39.ts
Normal file
51
packages/crypto/src/bip39.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { fromHex, toHex } from "@cosmjs/encoding";
|
||||
import * as bip39 from "bip39";
|
||||
import { pbkdf2 } from "pbkdf2";
|
||||
import * as unorm from "unorm";
|
||||
|
||||
import { EnglishMnemonic } from "./englishmnemonic";
|
||||
|
||||
export class Bip39 {
|
||||
public static encode(entropy: Uint8Array): EnglishMnemonic {
|
||||
const allowedEntropyLengths: readonly number[] = [16, 20, 24, 28, 32];
|
||||
|
||||
if (allowedEntropyLengths.indexOf(entropy.length) === -1) {
|
||||
throw new Error("invalid input length");
|
||||
}
|
||||
|
||||
return new EnglishMnemonic(bip39.entropyToMnemonic(toHex(entropy)));
|
||||
}
|
||||
|
||||
public static decode(mnemonic: EnglishMnemonic): Uint8Array {
|
||||
return fromHex(bip39.mnemonicToEntropy(mnemonic.toString()));
|
||||
}
|
||||
|
||||
public static async mnemonicToSeed(mnemonic: EnglishMnemonic, password?: string): Promise<Uint8Array> {
|
||||
// reimplementation of bip39.mnemonicToSeed using the asynchronous
|
||||
// interface of https://www.npmjs.com/package/pbkdf2
|
||||
const mnemonicBytes = Buffer.from(unorm.nfkd(mnemonic.toString()), "utf8");
|
||||
const salt = "mnemonic" + (password ? unorm.nfkd(password) : "");
|
||||
const saltBytes = Buffer.from(salt, "utf8");
|
||||
return this.pbkdf2(mnemonicBytes, saltBytes, 2048, 64, "sha512");
|
||||
}
|
||||
|
||||
// convert pbkdf2's calllback interface to Promise interface
|
||||
private static async pbkdf2(
|
||||
secret: Uint8Array,
|
||||
salt: Uint8Array,
|
||||
iterations: number,
|
||||
keylen: number,
|
||||
digest: string,
|
||||
): Promise<Uint8Array> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
// TODO: Patch @types/pbkdf2 to allow Uint8Array as secret and salt argument
|
||||
pbkdf2(Buffer.from(secret), Buffer.from(salt), iterations, keylen, digest, (err, derivedKey) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(new Uint8Array(derivedKey));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
242
packages/crypto/src/englishmnemonic.spec.ts
Normal file
242
packages/crypto/src/englishmnemonic.spec.ts
Normal file
@ -0,0 +1,242 @@
|
||||
import { fromAscii, fromBase64, fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { EnglishMnemonic } from "./englishmnemonic";
|
||||
import { Sha256 } from "./sha";
|
||||
import wordlists from "./testdata/bip39_wordlists.json";
|
||||
|
||||
describe("EnglishMnemonic", () => {
|
||||
describe("wordlist", () => {
|
||||
it("matches the words from the bitcoin/bips/bip-0039 spec", () => {
|
||||
const lineFeed = 0x0a;
|
||||
const bip39EnglishTxt = fromBase64(wordlists.english);
|
||||
|
||||
// Ensure we loaded the correct english.txt from https://github.com/bitcoin/bips/tree/master/bip-0039
|
||||
const checksum = new Sha256(bip39EnglishTxt).digest();
|
||||
expect(checksum).toEqual(fromHex("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"));
|
||||
|
||||
const wordsFromSpec: string[] = [];
|
||||
|
||||
let start = 0; // the start cursor marks the first byte of the word
|
||||
let end = 0; // the end cursor marks the line feed byte
|
||||
while (end < bip39EnglishTxt.length - 1) {
|
||||
end = start;
|
||||
while (bip39EnglishTxt[end] !== lineFeed) end++;
|
||||
const slice = bip39EnglishTxt.slice(start, end);
|
||||
wordsFromSpec.push(fromAscii(slice));
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
expect(EnglishMnemonic.wordlist).toEqual(wordsFromSpec);
|
||||
});
|
||||
});
|
||||
|
||||
it("works for valid inputs", () => {
|
||||
expect(() => {
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
);
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address",
|
||||
);
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
|
||||
);
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit",
|
||||
);
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it("rejects invalid whitespacing", () => {
|
||||
// extra space (leading, middle, trailing)
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
|
||||
// newline, tab
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon\nabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon\tabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
});
|
||||
|
||||
it("rejects disallowed letters", () => {
|
||||
// Disallowed letters in words (capital, number, special char)
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"Abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon Abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"route66 abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon route66 abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"lötkolben abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon lötkolben abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic format/i);
|
||||
});
|
||||
|
||||
it("word counts other than 12, 15, 18, 21, 24", () => {
|
||||
// too few and too many words (11, 13, 17, 19, 23, 25)
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 11/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 13/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 17/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 19/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 23/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
),
|
||||
).toThrowError(/invalid word count(.*)got: 25/i);
|
||||
});
|
||||
|
||||
it("rejects invalid checksums", () => {
|
||||
// 12x, 15x, 18x, 21x, 24x "zoo"
|
||||
expect(() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo")).toThrowError(
|
||||
/invalid mnemonic checksum/i,
|
||||
);
|
||||
expect(
|
||||
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
|
||||
).toThrowError(/invalid mnemonic checksum/i);
|
||||
expect(
|
||||
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
|
||||
).toThrowError(/invalid mnemonic checksum/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic checksum/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
|
||||
),
|
||||
).toThrowError(/invalid mnemonic checksum/i);
|
||||
});
|
||||
|
||||
it("rejects valid mnemonics of other languages", () => {
|
||||
// valid Spanish and Italian bip39 mnemonics
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"humo odio oriente colina taco fingir salto geranio glaciar academia suave vigor",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"yema folleto tos llave obtener natural fruta deseo laico sopa novato lazo imponer afinar vena hoja zarza cama",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"burla plaza arroz ronda pregunta vacuna veloz boina retiro exento prensa tortuga cabeza pilar anual molino molde fiesta masivo jefe leve fatiga clase plomo",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"braccio trincea armonia emiro svedese lepre stridulo metallo baldo rasente potassio rilassato",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"riparato arrosto globulo singolo bozzolo roba pirolisi ultimato padrone munto leggero avanzato monetario guanto lorenzo latino inoltrare modulo",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
expect(
|
||||
() =>
|
||||
new EnglishMnemonic(
|
||||
"promessa mercurio spessore snodo trave risata mecenate vichingo ceto orecchino vissuto risultato canino scarso futile fune epilogo uovo inedito apatico folata egoismo rifugio coma",
|
||||
),
|
||||
).toThrowError(/contains invalid word/i);
|
||||
});
|
||||
|
||||
describe("toString", () => {
|
||||
it("works", () => {
|
||||
const original =
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
const mnemonic = new EnglishMnemonic(original);
|
||||
expect(mnemonic.toString()).toEqual(original);
|
||||
});
|
||||
});
|
||||
});
|
||||
39
packages/crypto/src/englishmnemonic.ts
Normal file
39
packages/crypto/src/englishmnemonic.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import * as bip39 from "bip39";
|
||||
|
||||
export class EnglishMnemonic {
|
||||
public static readonly wordlist: readonly string[] = bip39.wordlists.english;
|
||||
|
||||
// list of space separated lower case words (1 or more)
|
||||
private static readonly mnemonicMatcher = /^[a-z]+( [a-z]+)*$/;
|
||||
|
||||
private readonly data: string;
|
||||
|
||||
public constructor(mnemonic: string) {
|
||||
if (!EnglishMnemonic.mnemonicMatcher.test(mnemonic)) {
|
||||
throw new Error("Invalid mnemonic format");
|
||||
}
|
||||
|
||||
const words = mnemonic.split(" ");
|
||||
const allowedWordsLengths: readonly number[] = [12, 15, 18, 21, 24];
|
||||
if (allowedWordsLengths.indexOf(words.length) === -1) {
|
||||
throw new Error(
|
||||
`Invalid word count in mnemonic (allowed: ${allowedWordsLengths} got: ${words.length})`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const word of words) {
|
||||
if (EnglishMnemonic.wordlist.indexOf(word) === -1) {
|
||||
throw new Error("Mnemonic contains invalid word");
|
||||
}
|
||||
}
|
||||
|
||||
// Throws with informative error message if mnemonic is not valid
|
||||
bip39.mnemonicToEntropy(mnemonic);
|
||||
|
||||
this.data = mnemonic;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
5
packages/crypto/src/hash.ts
Normal file
5
packages/crypto/src/hash.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface HashFunction {
|
||||
readonly blockSize: number;
|
||||
readonly update: (_: Uint8Array) => HashFunction;
|
||||
readonly digest: () => Uint8Array;
|
||||
}
|
||||
241
packages/crypto/src/hmac.spec.ts
Normal file
241
packages/crypto/src/hmac.spec.ts
Normal file
@ -0,0 +1,241 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Hmac } from "./hmac";
|
||||
import { Sha1, Sha256, Sha512 } from "./sha";
|
||||
|
||||
describe("HMAC", () => {
|
||||
it("can perform HMAC(SHA1) according to Botan test vectors", () => {
|
||||
// https://github.com/randombit/botan/blob/a5a260c/src/tests/data/mac/hmac.vec
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"));
|
||||
hmac.update(fromHex("4869205468657265"));
|
||||
expect(hmac.digest()).toEqual(fromHex("B617318655057264E28BC0B6FB378C8EF146BE00"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C"));
|
||||
hmac.update(fromHex("546573742057697468205472756E636174696F6E"));
|
||||
expect(hmac.digest()).toEqual(fromHex("4C1A03424B55E07FE7F27BE1D58BB9324A9A5A04"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("4CA0EF38F1794B28A8F8EE110EE79D48CE13BE25"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"54657374205573696E67204C6172676572205468616E20426C6F636B2D53697A65204B6579202D2048617368204B6579204669727374",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(fromHex("AA4AE5E15272D00E95705637CE8A3B55ED402112"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("4CA0EF38F1794B28A8F8EE110EE79D48CE13BE25"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"54657374205573696E67204C6172676572205468616E20426C6F636B2D53697A65204B657920616E64204C6172676572205468616E204F6E6520426C6F636B2D53697A652044617461",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(fromHex("E8E99D0F45237D786D6BBAA7965C7808BBFF1A91"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("0102030405060708090A0B0C0D0E0F10111213141516171819"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"CDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCD",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(fromHex("4C9007F4026250C6BC8414F9BF50C86C2D7235DA"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha1, fromHex("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(fromHex("125D7342B9AC11CD91A39AF48AA17B4F63F175D3"));
|
||||
}
|
||||
});
|
||||
|
||||
it("can perform HMAC(SHA256) according to Botan test vectors", () => {
|
||||
// https://github.com/randombit/botan/blob/a5a260c/src/tests/data/mac/hmac.vec#L60
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha256,
|
||||
fromHex("0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20"),
|
||||
);
|
||||
hmac.update(fromHex("616263"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha256, fromHex("4A656665"));
|
||||
hmac.update(fromHex("7768617420646F2079612077616E7420666F72206E6F7468696E673F"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("5BDCC146BF60754E6A042426089575C75A003F089D2739839DEC58B964EC3843"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha256,
|
||||
fromHex("0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20"),
|
||||
);
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F7071",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("470305FC7E40FE34D3EEB3E773D95AAB73ACF0FD060447A5EB4595BF33A9D1A3"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha256,
|
||||
fromHex("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"),
|
||||
);
|
||||
hmac.update(fromHex("4869205468657265"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("198A607EB44BFBC69903A0F1CF2BBDC5BA0AA3F3D9AE3C1C7A3B1696A0B68CF7"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha256,
|
||||
fromHex("0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C"),
|
||||
);
|
||||
hmac.update(fromHex("546573742057697468205472756E636174696F6E"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("7546AF01841FC09B1AB9C3749A5F1C17D4F589668A587B2700A9C97C1193CF42"),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("can perform HMAC(SHA512) according to RFC 4231", () => {
|
||||
// Test Case 1–7 from https://tools.ietf.org/html/rfc4231#section-4
|
||||
{
|
||||
const hmac = new Hmac(Sha512, fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"));
|
||||
hmac.update(fromHex("4869205468657265"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha512, fromHex("4a656665"));
|
||||
hmac.update(fromHex("7768617420646f2079612077616e7420666f72206e6f7468696e673f"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha512, fromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha512, fromHex("0102030405060708090a0b0c0d0e0f10111213141516171819"));
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha512, fromHex("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
|
||||
hmac.update(fromHex("546573742057697468205472756e636174696f6e"));
|
||||
expect(hmac.digest().slice(0, 16)).toEqual(fromHex("415fad6271580a531d4179bc891d87a6"));
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha512,
|
||||
fromHex(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
),
|
||||
);
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(
|
||||
Sha512,
|
||||
fromHex(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
),
|
||||
);
|
||||
hmac.update(
|
||||
fromHex(
|
||||
"5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e",
|
||||
),
|
||||
);
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex(
|
||||
"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58",
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("can perform incremental hashing", () => {
|
||||
const hmac = new Hmac(Sha1, fromHex("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"));
|
||||
// full message: 4869205468657265
|
||||
hmac.update(fromHex(""));
|
||||
hmac.update(fromHex("48"));
|
||||
hmac.update(fromHex(""));
|
||||
hmac.update(fromHex(""));
|
||||
hmac.update(fromHex("69"));
|
||||
hmac.update(fromHex("20"));
|
||||
hmac.update(fromHex("5468"));
|
||||
hmac.update(fromHex("657265"));
|
||||
hmac.update(fromHex(""));
|
||||
expect(hmac.digest()).toEqual(fromHex("B617318655057264E28BC0B6FB378C8EF146BE00"));
|
||||
});
|
||||
|
||||
it("works with empty keys", () => {
|
||||
// Generated using Python 3
|
||||
// hmac.new(b'', bytearray.fromhex("7061756c"), hashlib.sha256).hexdigest()
|
||||
{
|
||||
const hmac = new Hmac(Sha256, fromHex(""));
|
||||
hmac.update(fromHex("7061756c"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("50972b73add1dbbbe6884104d0f91efcef184e0aef6e485a075b3cab1f70e572"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha256, fromHex(""));
|
||||
hmac.update(fromHex("70"));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("a1d75fa8dc0d1a84cf6df6f0e55cb52d89d44acb26e786c9329cf8dc8804a94e"),
|
||||
);
|
||||
}
|
||||
{
|
||||
const hmac = new Hmac(Sha256, fromHex(""));
|
||||
hmac.update(fromHex(""));
|
||||
expect(hmac.digest()).toEqual(
|
||||
fromHex("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
49
packages/crypto/src/hmac.ts
Normal file
49
packages/crypto/src/hmac.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { HashFunction } from "./hash";
|
||||
|
||||
export class Hmac<H extends HashFunction> implements HashFunction {
|
||||
public readonly blockSize: number;
|
||||
|
||||
private readonly messageHasher: H;
|
||||
private readonly oKeyPad: Uint8Array;
|
||||
private readonly iKeyPad: Uint8Array;
|
||||
private readonly hash: (data: Uint8Array) => Uint8Array;
|
||||
|
||||
public constructor(hashFunctionConstructor: new () => H, originalKey: Uint8Array) {
|
||||
// This implementation is based on https://en.wikipedia.org/wiki/HMAC#Implementation
|
||||
// with the addition of incremental hashing support. Thus part of the algorithm
|
||||
// is in the constructor and the rest in digest().
|
||||
|
||||
const blockSize = new hashFunctionConstructor().blockSize;
|
||||
|
||||
this.hash = (data) => new hashFunctionConstructor().update(data).digest();
|
||||
|
||||
let key = originalKey;
|
||||
if (key.length > blockSize) {
|
||||
key = this.hash(key);
|
||||
}
|
||||
|
||||
if (key.length < blockSize) {
|
||||
const zeroPadding = new Uint8Array(blockSize - key.length);
|
||||
key = new Uint8Array([...key, ...zeroPadding]);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
this.oKeyPad = key.map((keyByte) => keyByte ^ 0x5c);
|
||||
// eslint-disable-next-line no-bitwise
|
||||
this.iKeyPad = key.map((keyByte) => keyByte ^ 0x36);
|
||||
this.messageHasher = new hashFunctionConstructor();
|
||||
this.blockSize = blockSize;
|
||||
|
||||
this.update(this.iKeyPad);
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Hmac<H> {
|
||||
this.messageHasher.update(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
const innerHash = this.messageHasher.digest();
|
||||
return this.hash(new Uint8Array([...this.oKeyPad, ...innerHash]));
|
||||
}
|
||||
}
|
||||
30
packages/crypto/src/index.ts
Normal file
30
packages/crypto/src/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export { Bip39 } from "./bip39";
|
||||
export { EnglishMnemonic } from "./englishmnemonic";
|
||||
export { HashFunction } from "./hash";
|
||||
export { Hmac } from "./hmac";
|
||||
export { Keccak256 } from "./keccak";
|
||||
export {
|
||||
Xchacha20poly1305Ietf,
|
||||
Xchacha20poly1305IetfCiphertext,
|
||||
Xchacha20poly1305IetfKey,
|
||||
Xchacha20poly1305IetfMessage,
|
||||
Xchacha20poly1305IetfNonce,
|
||||
Argon2id,
|
||||
Argon2idOptions,
|
||||
Ed25519,
|
||||
Ed25519Keypair,
|
||||
} from "./libsodium";
|
||||
export { Random } from "./random";
|
||||
export { Ripemd160 } from "./ripemd";
|
||||
export { Secp256k1, Secp256k1Keypair } from "./secp256k1";
|
||||
export { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
export { Sha1, Sha256, Sha512 } from "./sha";
|
||||
export {
|
||||
pathToString,
|
||||
stringToPath,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
Slip10RawIndex,
|
||||
Slip10Result,
|
||||
slip10CurveFromString,
|
||||
} from "./slip10";
|
||||
28
packages/crypto/src/keccak.spec.ts
Normal file
28
packages/crypto/src/keccak.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { fromHex, toHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Keccak256 } from "./keccak";
|
||||
import keccakVectors from "./testdata/keccak.json";
|
||||
|
||||
describe("Keccak256", () => {
|
||||
it("exists", () => {
|
||||
expect(Keccak256).toBeTruthy();
|
||||
});
|
||||
|
||||
it("works for empty input", () => {
|
||||
{
|
||||
const hash = new Keccak256(new Uint8Array([])).digest();
|
||||
expect(toHex(hash)).toEqual("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
|
||||
}
|
||||
{
|
||||
const hash = new Keccak256().digest();
|
||||
expect(toHex(hash)).toEqual("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
|
||||
}
|
||||
});
|
||||
|
||||
it("works for all the Botan test vectors", () => {
|
||||
// https://github.com/randombit/botan/blob/2.8.0/src/tests/data/hash/keccak.vec#L806
|
||||
for (const { in: input, out: output } of keccakVectors.keccak256) {
|
||||
expect(new Keccak256(fromHex(input)).digest()).toEqual(fromHex(output));
|
||||
}
|
||||
});
|
||||
});
|
||||
26
packages/crypto/src/keccak.ts
Normal file
26
packages/crypto/src/keccak.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import jssha3 from "js-sha3";
|
||||
|
||||
import { HashFunction } from "./hash";
|
||||
|
||||
export class Keccak256 implements HashFunction {
|
||||
public readonly blockSize = 512 / 8;
|
||||
|
||||
private readonly impl: jssha3.Hasher;
|
||||
|
||||
public constructor(firstData?: Uint8Array) {
|
||||
this.impl = jssha3.keccak256.create();
|
||||
|
||||
if (firstData) {
|
||||
this.update(firstData);
|
||||
}
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Keccak256 {
|
||||
this.impl.update(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
return new Uint8Array(this.impl.digest());
|
||||
}
|
||||
}
|
||||
948
packages/crypto/src/libsodium.spec.ts
Normal file
948
packages/crypto/src/libsodium.spec.ts
Normal file
@ -0,0 +1,948 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import { fromHex, toAscii } from "@cosmjs/encoding";
|
||||
|
||||
import {
|
||||
Argon2id,
|
||||
Argon2idOptions,
|
||||
Ed25519,
|
||||
Ed25519Keypair,
|
||||
Xchacha20poly1305Ietf,
|
||||
Xchacha20poly1305IetfCiphertext,
|
||||
Xchacha20poly1305IetfKey,
|
||||
Xchacha20poly1305IetfMessage,
|
||||
Xchacha20poly1305IetfNonce,
|
||||
} from "./libsodium";
|
||||
|
||||
describe("Libsodium", () => {
|
||||
describe("Argon2id", () => {
|
||||
// we use relatively week values here to avoid slowing down test execution
|
||||
|
||||
it("works for 1 MiB memory and opsLimit = 5", async () => {
|
||||
const options: Argon2idOptions = {
|
||||
outputLength: 32,
|
||||
opsLimit: 5,
|
||||
memLimitKib: 1024,
|
||||
};
|
||||
const salt = toAscii("ABCDEFGHIJKLMNOP");
|
||||
|
||||
// echo -n "123" | ./argon2 ABCDEFGHIJKLMNOP -id -v 13 -k 1024 -t 5
|
||||
await Argon2id.execute("123", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("3c5d010180ba0cf5b6b858cba23b318e42d33088983c404598599c3b029ecac6")),
|
||||
);
|
||||
await Argon2id.execute("!'§$%&/()", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("b0268bd63015c3d8f866f9be385507b466a9bfc75f271c2c1e97c00bf53224ba")),
|
||||
);
|
||||
await Argon2id.execute("ö", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("b113fc7863dbc87b7d1366c3b468d3864a2473ce46e90ed3641fff87ada561f7")),
|
||||
);
|
||||
await Argon2id.execute("😎", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("dc92db2a69a5607a75472e1581ac0851292ed9a2606f1000f62fa2efc97964e0")),
|
||||
);
|
||||
});
|
||||
|
||||
it("works for 8 MiB memory and opsLimit = 2", async () => {
|
||||
const options: Argon2idOptions = {
|
||||
outputLength: 32,
|
||||
opsLimit: 2,
|
||||
memLimitKib: 8 * 1024,
|
||||
};
|
||||
const salt = toAscii("ABCDEFGHIJKLMNOP");
|
||||
|
||||
// echo -n "123" | ./argon2 ABCDEFGHIJKLMNOP -id -v 13 -k 8192 -t 2
|
||||
await Argon2id.execute("123", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("3ee950488d26ce691657b1d753f562139857b61a58f234d6cb0ce84c4cc27328")),
|
||||
);
|
||||
await Argon2id.execute("!'§$%&/()", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("ab410498b44942a28f9d0dde72f0398edf104021ee41bb80412464975817a8a1")),
|
||||
);
|
||||
await Argon2id.execute("ö", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("f80c502bc3fe7b191f6e7e06359955d5dbd23f532548b7058ecbcf77a58e683d")),
|
||||
);
|
||||
await Argon2id.execute("😎", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("474d9445596d2600ba3dc9bbe87d21ed4879e2445cafb10fcb69c5c3ab8ecbc7")),
|
||||
);
|
||||
});
|
||||
|
||||
it("works for 10 MiB memory and opsLimit = 1", async () => {
|
||||
const options: Argon2idOptions = {
|
||||
outputLength: 32,
|
||||
opsLimit: 1,
|
||||
memLimitKib: 10 * 1024,
|
||||
};
|
||||
const salt = toAscii("ABCDEFGHIJKLMNOP");
|
||||
|
||||
// echo -n "123" | ./argon2 ABCDEFGHIJKLMNOP -id -v 13 -k 10240 -t 1
|
||||
await Argon2id.execute("123", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("f1832edbd41c209546eafd01f3aae28390de39bc13ff38981c4fc0c1ceaa05e3")),
|
||||
);
|
||||
await Argon2id.execute("!'§$%&/()", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("30c74f405d148fd5c882a0f4238aad9ed85ef255adc102411d22736d68f76f76")),
|
||||
);
|
||||
await Argon2id.execute("ö", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("b80a62f11e7a058194a8ddd80d341c47e0f3b6c41c72ee15b7926788e9963e8f")),
|
||||
);
|
||||
await Argon2id.execute("😎", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex("b868aa1875de2edc57bc22de1fc75f9d19f451067c529565f73c61958088b5e9")),
|
||||
);
|
||||
});
|
||||
|
||||
it("works for different output lengths", async () => {
|
||||
// echo -n "123" | ./argon2 ABCDEFGHIJKLMNOP -id -v 13 -k 1024 -t 5 -l 16
|
||||
const salt = toAscii("ABCDEFGHIJKLMNOP");
|
||||
|
||||
// libsodium does not support output length < 16
|
||||
const data = new Map<number, string>();
|
||||
data.set(16, "01a5ea70c68132b474bdb7f996f55a5a");
|
||||
data.set(24, "14cf66110e167ebdbea968328bba3f40113077bc359acbe8");
|
||||
data.set(32, "3c5d010180ba0cf5b6b858cba23b318e42d33088983c404598599c3b029ecac6");
|
||||
data.set(
|
||||
48,
|
||||
"1141dc209086803b06fe0835be055ed592289c9baf9a9db6cd584cd63c2712ca0efc989017a73d6dafb7211a9d09413f",
|
||||
);
|
||||
data.set(
|
||||
96,
|
||||
"cdb42cddd7190d0ab2453571f644ebf1214177886a51639f23518e1c92d73a196cadddf927bbc8fac59ab3615325642920d7dd73171c7b63f17e1ae173a7b6372bac7525a3230ab1edf6e3ed5c971321186c00a544c79d96bc65263eb5a85d50",
|
||||
);
|
||||
data.set(
|
||||
128,
|
||||
"c0108a962f59c30b6af298025ad8b8027791cc91f74b96a01a92993e41871e391516e831210bdd3ae20fe501b9c2279d59d42ebc777286088d56a87f30eea04829b9903cb05a468f320e4aced531c7b10631463141a9cbd903dbad4c9b43b2ca0c56ff5a0093179924685e061979e49a593719bb3373152856df922b0007bd9f",
|
||||
);
|
||||
data.set(
|
||||
192,
|
||||
"092aeca103de921794a97abfd4f0dd1e51de29c62f372e2a984f72d280c12067316db192e47d37ccfd07243bb1ea9a14f7a361a1ab3f5c4be70fb33fea868d9047bdf9ccc52cde1f1cefbb77923b236a690f30f03ebd2ebf72cd47e2acf28627d64b6bd0fe1f8fb2e598017c4892413b83df2ab4c210b51bd730644fa042fee64653a33fbc81dc715c1e05ed4592b71dee1b3fa080b3d332bd96b50c9a1b1c71b7b4e131517dcb63ab628679d20a386f98948d8b9ecf99f32611c9f747abb2d5",
|
||||
);
|
||||
data.set(
|
||||
256,
|
||||
"94aaf5677f2c0ad13d504bbbfe9b05bbcb8194c8415c119c9d3c170fbcff0e0a42ffa48c11085c6c61f0942d88d32e0da3408099991148db876e29fc5ca80b8425ac0a09987393d7c67fc62ff21fb9713442f3a67690350a871d99bedaecb7c86c357410631c89eedf04c97e386ecb5c0028d53f2d1d6aacba67d2e7bd23792689367dfd777eb28ff4de1753955dcb5f85f34a03684089590927ebd09c251cec4abb7f717ebed22690116938c5ca8404ae7814e9391c4f3c023bafad92b26899f94b6b3dc13ebc7fa693a9233a73f3b2f06b337af3a848b006e8c53bf24b79ca50df8f638304a8671f6949fde9239e0bfa78b5a7ddf424b808a0bfcd2b4fbb20",
|
||||
);
|
||||
|
||||
for (const length of data.keys()) {
|
||||
const options: Argon2idOptions = {
|
||||
outputLength: length,
|
||||
opsLimit: 5,
|
||||
memLimitKib: 1024,
|
||||
};
|
||||
await Argon2id.execute("123", salt, options).then((result) =>
|
||||
expect(result).toEqual(fromHex(data.get(length)!)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("throw for invalid salt lengths", async () => {
|
||||
const password = "123";
|
||||
const options: Argon2idOptions = {
|
||||
outputLength: 32,
|
||||
opsLimit: 1,
|
||||
memLimitKib: 10 * 1024,
|
||||
};
|
||||
|
||||
// 8 bytes
|
||||
await Argon2id.execute(password, fromHex("aabbccddeeff0011"), options)
|
||||
.then(() => fail("Argon2id with invalid salt length must not resolve"))
|
||||
.catch((e) => expect(e).toMatch(/invalid salt length/));
|
||||
// 15 bytes
|
||||
await Argon2id.execute(password, fromHex("aabbccddeeff001122334455667788"), options)
|
||||
.then(() => fail("Argon2id with invalid salt length must not resolve"))
|
||||
.catch((e) => expect(e).toMatch(/invalid salt length/));
|
||||
// 17 bytes
|
||||
await Argon2id.execute(password, fromHex("aabbccddeeff00112233445566778899aa"), options)
|
||||
.then(() => fail("Argon2id with invalid salt length must not resolve"))
|
||||
.catch((e) => expect(e).toMatch(/invalid salt length/));
|
||||
// 32 bytes
|
||||
await Argon2id.execute(
|
||||
password,
|
||||
fromHex("aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"),
|
||||
options,
|
||||
)
|
||||
.then(() => fail("Argon2id with invalid salt length must not resolve"))
|
||||
.catch((e) => expect(e).toMatch(/invalid salt length/));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ed25519Keypair", () => {
|
||||
it("loads from Libsodium private key", () => {
|
||||
const keypair = Ed25519Keypair.fromLibsodiumPrivkey(
|
||||
fromHex(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
),
|
||||
);
|
||||
expect(keypair.privkey).toEqual(
|
||||
fromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
|
||||
);
|
||||
expect(keypair.pubkey).toEqual(
|
||||
fromHex("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
|
||||
);
|
||||
});
|
||||
|
||||
it("exports Libsodium private key", () => {
|
||||
const keypair = new Ed25519Keypair(
|
||||
fromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
|
||||
fromHex("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
|
||||
);
|
||||
expect(keypair.toLibsodiumPrivkey()).toEqual(
|
||||
fromHex(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ed25519", () => {
|
||||
it("exists", () => {
|
||||
expect(Ed25519).toBeTruthy();
|
||||
});
|
||||
|
||||
it("generates keypairs", async () => {
|
||||
{
|
||||
// ok
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Ed25519.makeKeypair(seed);
|
||||
expect(keypair).toBeTruthy();
|
||||
expect(keypair.pubkey).toBeTruthy();
|
||||
expect(keypair.privkey).toBeTruthy();
|
||||
expect(keypair.pubkey.byteLength).toEqual(32);
|
||||
expect(keypair.privkey.byteLength).toEqual(32);
|
||||
expect(keypair.privkey).toEqual(seed);
|
||||
}
|
||||
|
||||
{
|
||||
// Test secret to public conversion (TEST 1–4 from https://tools.ietf.org/html/rfc8032#section-7.1)
|
||||
const privkey1 = fromHex("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
|
||||
const pubkey1 = (await Ed25519.makeKeypair(privkey1)).pubkey;
|
||||
expect(pubkey1).toEqual(fromHex("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"));
|
||||
|
||||
const privkey2 = fromHex("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb");
|
||||
const pubkey2 = (await Ed25519.makeKeypair(privkey2)).pubkey;
|
||||
expect(pubkey2).toEqual(fromHex("3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c"));
|
||||
|
||||
const privkey3 = fromHex("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7");
|
||||
const pubkey3 = (await Ed25519.makeKeypair(privkey3)).pubkey;
|
||||
expect(pubkey3).toEqual(fromHex("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"));
|
||||
|
||||
const privkey4 = fromHex("f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5");
|
||||
const pubkey4 = (await Ed25519.makeKeypair(privkey4)).pubkey;
|
||||
expect(pubkey4).toEqual(fromHex("278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e"));
|
||||
}
|
||||
|
||||
{
|
||||
// seed too short
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0");
|
||||
await Ed25519.makeKeypair(seed)
|
||||
.then(() => {
|
||||
fail("promise must not resolve");
|
||||
})
|
||||
.catch((error) => {
|
||||
expect(error.message).toContain("invalid seed length");
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// seed too long
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9aa");
|
||||
await Ed25519.makeKeypair(seed)
|
||||
.then(() => {
|
||||
fail("promise must not resolve");
|
||||
})
|
||||
.catch((error) => {
|
||||
expect(error.message).toContain("invalid seed length");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("generates keypairs deterministically", async () => {
|
||||
const seedA1 = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const seedA2 = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const seedB1 = fromHex("c0c42a0276d456ee007faae2cc7d1bc8925dd74983726d548e10da14c3aed12a");
|
||||
const seedB2 = fromHex("c0c42a0276d456ee007faae2cc7d1bc8925dd74983726d548e10da14c3aed12a");
|
||||
|
||||
const keypairA1 = await Ed25519.makeKeypair(seedA1);
|
||||
const keypairA2 = await Ed25519.makeKeypair(seedA2);
|
||||
const keypairB1 = await Ed25519.makeKeypair(seedB1);
|
||||
const keypairB2 = await Ed25519.makeKeypair(seedB2);
|
||||
|
||||
expect(keypairA1).toEqual(keypairA2);
|
||||
expect(keypairB1).toEqual(keypairB2);
|
||||
expect(keypairA1).not.toEqual(keypairB1);
|
||||
expect(keypairA2).not.toEqual(keypairB2);
|
||||
});
|
||||
|
||||
it("creates signatures", async () => {
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Ed25519.makeKeypair(seed);
|
||||
const message = new Uint8Array([0x11, 0x22]);
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature).toBeTruthy();
|
||||
expect(signature.byteLength).toEqual(64);
|
||||
});
|
||||
|
||||
it("creates signatures deterministically", async () => {
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Ed25519.makeKeypair(seed);
|
||||
const message = new Uint8Array([0x11, 0x22]);
|
||||
|
||||
const signature1 = await Ed25519.createSignature(message, keypair);
|
||||
const signature2 = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature1).toEqual(signature2);
|
||||
});
|
||||
|
||||
it("verifies signatures", async () => {
|
||||
const seed = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Ed25519.makeKeypair(seed);
|
||||
const message = new Uint8Array([0x11, 0x22]);
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
|
||||
{
|
||||
// valid
|
||||
const ok = await Ed25519.verifySignature(signature, message, keypair.pubkey);
|
||||
expect(ok).toEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
// message corrupted
|
||||
const corruptedMessage = message.map((x, i) => (i === 0 ? x ^ 0x01 : x));
|
||||
const ok = await Ed25519.verifySignature(signature, corruptedMessage, keypair.pubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
|
||||
{
|
||||
// signature corrupted
|
||||
const corruptedSignature = signature.map((x, i) => (i === 0 ? x ^ 0x01 : x));
|
||||
const ok = await Ed25519.verifySignature(corruptedSignature, message, keypair.pubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
|
||||
{
|
||||
// wrong pubkey
|
||||
const otherSeed = fromHex("91099374790843e29552c3cfa5e9286d6c77e00a2c109aaf3d0a307081314a09");
|
||||
const wrongPubkey = (await Ed25519.makeKeypair(otherSeed)).pubkey;
|
||||
const ok = await Ed25519.verifySignature(signature, message, wrongPubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("works with RFC8032 test vectors", async () => {
|
||||
{
|
||||
// TEST 1 from https://tools.ietf.org/html/rfc8032#section-7.1
|
||||
const keypair = new Ed25519Keypair(
|
||||
fromHex("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"),
|
||||
fromHex("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"),
|
||||
);
|
||||
const message = fromHex("");
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature).toEqual(
|
||||
fromHex(
|
||||
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
|
||||
),
|
||||
);
|
||||
const valid = await Ed25519.verifySignature(signature, message, keypair.pubkey);
|
||||
expect(valid).toEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
// TEST 2 from https://tools.ietf.org/html/rfc8032#section-7.1
|
||||
const keypair = new Ed25519Keypair(
|
||||
fromHex("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"),
|
||||
fromHex("3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c"),
|
||||
);
|
||||
const message = fromHex("72");
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature).toEqual(
|
||||
fromHex(
|
||||
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
|
||||
),
|
||||
);
|
||||
const valid = await Ed25519.verifySignature(signature, message, keypair.pubkey);
|
||||
expect(valid).toEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
// TEST 3 from https://tools.ietf.org/html/rfc8032#section-7.1
|
||||
const keypair = new Ed25519Keypair(
|
||||
fromHex("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7"),
|
||||
fromHex("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"),
|
||||
);
|
||||
const message = fromHex("af82");
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature).toEqual(
|
||||
fromHex(
|
||||
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
|
||||
),
|
||||
);
|
||||
const valid = await Ed25519.verifySignature(signature, message, keypair.pubkey);
|
||||
expect(valid).toEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
// TEST 1024 from https://tools.ietf.org/html/rfc8032#section-7.1
|
||||
const keypair = new Ed25519Keypair(
|
||||
fromHex("f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5"),
|
||||
fromHex("278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e"),
|
||||
);
|
||||
const message = fromHex(
|
||||
"08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0",
|
||||
);
|
||||
const signature = await Ed25519.createSignature(message, keypair);
|
||||
expect(signature).toEqual(
|
||||
fromHex(
|
||||
"0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03",
|
||||
),
|
||||
);
|
||||
const valid = await Ed25519.verifySignature(signature, message, keypair.pubkey);
|
||||
expect(valid).toEqual(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Xchacha20poly1305Ietf", () => {
|
||||
it("can encrypt and decypt simple data", async () => {
|
||||
const key = fromHex(
|
||||
"1324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916d8",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex("000000000000000000000000000000000000000000000000") as Xchacha20poly1305IetfNonce;
|
||||
|
||||
const originalMessage = new Uint8Array([0x11, 0x22, 0x33, 0x44]) as Xchacha20poly1305IetfMessage;
|
||||
const ciphertext = await Xchacha20poly1305Ietf.encrypt(originalMessage, key, nonce);
|
||||
expect(ciphertext).toBeTruthy();
|
||||
expect(ciphertext.length).toEqual(4 /* message length */ + 16 /* tag length*/);
|
||||
|
||||
const decrypted = await Xchacha20poly1305Ietf.decrypt(ciphertext, key, nonce);
|
||||
expect(decrypted).toBeTruthy();
|
||||
expect(decrypted).toEqual(originalMessage);
|
||||
});
|
||||
|
||||
it("throws when encrypting with wrong key length", async () => {
|
||||
const nonce = fromHex("000000000000000000000000000000000000000000000000") as Xchacha20poly1305IetfNonce;
|
||||
const message = new Uint8Array([]) as Xchacha20poly1305IetfMessage;
|
||||
|
||||
{
|
||||
// empty
|
||||
const key = fromHex("") as Xchacha20poly1305IetfKey;
|
||||
await Xchacha20poly1305Ietf.encrypt(message, key, nonce)
|
||||
.then(() => fail("encryption must not succeed"))
|
||||
.catch((error) => expect(error).toMatch(/invalid key length/));
|
||||
}
|
||||
{
|
||||
// 31 bytes
|
||||
const key = fromHex(
|
||||
"1324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
await Xchacha20poly1305Ietf.encrypt(message, key, nonce)
|
||||
.then(() => fail("encryption must not succeed"))
|
||||
.catch((error) => expect(error).toMatch(/invalid key length/));
|
||||
}
|
||||
{
|
||||
// 33 bytes
|
||||
const key = fromHex(
|
||||
"1324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916d8aa",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
await Xchacha20poly1305Ietf.encrypt(message, key, nonce)
|
||||
.then(() => fail("encryption must not succeed"))
|
||||
.catch((error) => expect(error).toMatch(/invalid key length/));
|
||||
}
|
||||
{
|
||||
// 64 bytes
|
||||
const key = fromHex(
|
||||
"1324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916d81324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916d8",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
await Xchacha20poly1305Ietf.encrypt(message, key, nonce)
|
||||
.then(() => fail("encryption must not succeed"))
|
||||
.catch((error) => expect(error).toMatch(/invalid key length/));
|
||||
}
|
||||
});
|
||||
|
||||
it("decryption fails with wrong ciphertext/key/nonce", async () => {
|
||||
const key = fromHex(
|
||||
"1324cdddc4b94e625bbabcac862c9429ba011e2184a1ccad60e7c3f6ff4916d8",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex("000000000000000000000000000000000000000000000000") as Xchacha20poly1305IetfNonce;
|
||||
|
||||
const originalMessage = new Uint8Array([0x11, 0x22, 0x33, 0x44]) as Xchacha20poly1305IetfMessage;
|
||||
const ciphertext = await Xchacha20poly1305Ietf.encrypt(originalMessage, key, nonce);
|
||||
expect(ciphertext).toBeTruthy();
|
||||
expect(ciphertext.length).toEqual(4 /* message length */ + 16 /* tag length*/);
|
||||
|
||||
{
|
||||
// baseline
|
||||
expect(await Xchacha20poly1305Ietf.decrypt(ciphertext, key, nonce)).toEqual(originalMessage);
|
||||
}
|
||||
{
|
||||
// corrupted ciphertext
|
||||
const corruptedCiphertext = ciphertext.map((x, i) =>
|
||||
i === 0 ? x ^ 0x01 : x,
|
||||
) as Xchacha20poly1305IetfCiphertext;
|
||||
await Xchacha20poly1305Ietf.decrypt(corruptedCiphertext, key, nonce).then(
|
||||
() => fail("promise must not resolve"),
|
||||
(error) => expect(error.message).toMatch(/ciphertext cannot be decrypted using that key/i),
|
||||
);
|
||||
}
|
||||
{
|
||||
// corrupted key
|
||||
const corruptedKey = key.map((x, i) => (i === 0 ? x ^ 0x01 : x)) as Xchacha20poly1305IetfKey;
|
||||
await Xchacha20poly1305Ietf.decrypt(ciphertext, corruptedKey, nonce).then(
|
||||
() => fail("promise must not resolve"),
|
||||
(error) => expect(error.message).toMatch(/ciphertext cannot be decrypted using that key/i),
|
||||
);
|
||||
}
|
||||
{
|
||||
// corrupted nonce
|
||||
const corruptedNonce = nonce.map((x, i) => (i === 0 ? x ^ 0x01 : x)) as Xchacha20poly1305IetfNonce;
|
||||
await Xchacha20poly1305Ietf.decrypt(ciphertext, key, corruptedNonce).then(
|
||||
() => fail("promise must not resolve"),
|
||||
(error) => expect(error.message).toMatch(/ciphertext cannot be decrypted using that key/i),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("encrypt conforms to Botan implementation ", async () => {
|
||||
// Test data generated by
|
||||
// echo -n "<message>" | ./botan encryption --mode=chacha20poly1305 --iv=000000000000000000000000000000000000000000000000 --ad= --key=0000000000000000000000000000000000000000000000000000000000000000 | xxd -p -c 1000000
|
||||
|
||||
const makeMessage = (hex: string): Xchacha20poly1305IetfMessage =>
|
||||
fromHex(hex) as Xchacha20poly1305IetfMessage;
|
||||
|
||||
// Tested messages:
|
||||
// empty, "a", "ab", "abc", 577 (prime) random bytes, 1024 random bytes
|
||||
|
||||
{
|
||||
// zero key, zero nonce
|
||||
const key = fromHex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage(""), key, nonce)).toEqual(
|
||||
fromHex("8f3b945a51906dc8600de9f8962d00e6"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("61"), key, nonce)).toEqual(
|
||||
fromHex("19841f4c8866efab6c6b5329aef9e36752"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("6162"), key, nonce)).toEqual(
|
||||
fromHex("19fcc0cbdcffdf83f822811687b79930dcf7"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("616263"), key, nonce)).toEqual(
|
||||
fromHex("19fcf5f6e7aba23c37d7a59e4c8f061e15f1f6"),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"d5a9fa454739afd6e6b2dc5d09d9f83d98dd5abb3b3b188c81a33ad048bed75c2aa1a53cc81c02b1d3204506114cc4b58278d4a8fc8ec3916298ac165a94ff84d32320086cd98c0127a3d372112a75dc7629e55ac704dc8fd55be9033409f03254fae947912c0f737e8626f44b63c8dc7d7d3cddf57348b06fe80af61627f9a5a282b8d49cd2adf5191dd7855ffa5b931295773744691ba0b99305de8581184046c074148a8488d021122ddec21482bde85029aecd09907371ae67abc6cfc44655c3b49e7df3e2fac6b73b2803746bd1b4d17b30f5c2dfc31a7c33be5c737330e855d7630155523e3fb85c55a5ff95fd0754f4e9c5f0d88827bb28caa9af2d8f3e52b5d0957dd37d77af5df644bc483a6583d3b1e32d3b5dcbe7a6684ff8c0e44c7250744f705fce4e588f3a2ae65cac0c2b218d455911a2415fd839d3ef288d7447d6e561f4d70e93a739f6890ee3b6edcd3089266bd17d73a0c7fb96a1cdf96b2b7b18f38fe73bb6eb437a37588628ab51894b421137d1032a480643f5b36bb894ae82b8927fa7e87c906b90a3379b3f0aabee718a1e87f31dbfa3f8b1ebb5df9fdf96cd93ecdfcd421ded6299929676fa58ed3034f13474fa278b0a2c62b136e211e7a33e437e24386aae16524adc17557112fdaf484c5e6a038e8513cb2cb157816d6c85406aaae5514ca5b900dd18a90147311a37c82538e1f9adbad1bdc993264475cc54f61e54e048d63190cf138f3cdb5faf52e954d1caa8081a614beb44b2d9bea7e30d51513b4c3fd77e1cfd84e58588f733116c9be6b15cd4f72e3d602c2fe77f7880b91f62197c547df9d2",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"71292e48a0498805a5ad2fa4aeb78492a70a9d24fb0f3b4c76b13e22ae9259f7d21f7913a090c4addb00d672f42c2fbb77a85637a02bea20fb2dac9f11e8b0efb5ec0bf43c5af49243f446b7503f32987a6fbaf9d4b58c6a6aee7c0636433d9fef5b83615f05ade143a7ba2606d6c0f7ccb414b17b4b9ea836bf0f96f7d83b3f26ec4f84d05fca2e958f451ceb28c4390a203b724cc9d129bbcba64fe4dd72949ba5eaf160737050897648f0694f8f5757a72d297fbf7a25affbfcf3377853e8c58d5f066911ae970ad0fc1945cd8791f405a23daff28de3d3ded54441a099eafeefd2e18fcef8966593bd4b29b1dccaeba58f2189c496e22044fa31352e33219d5feb62b829ceeefc621b020e741e30efc938189097779590c0b02e32b99ae57f85a18c876421edd4d476870a4ecd27ce6e4b722489dd8a790580f213ee04a54f5dcfc77dd65551be59d73b858c912e5603271dbe1082b05b0162a0564df81ccb101ba37d43eacc8a067d51dcdb3f11ee1735f87394ab6302269eb62de5cf910123ffe2b584f8393569a82d49981e02b9d200b7bd8c52dba24226fcbe1bc83c4b03918e0050ffee8a7ba6bd5430d9bbc6915042644d44402adc59578d6eba3e02a39aa8e9ef3f4660720bfa95a474d9d1ad6d325ac3c1efdbf7ebf2c2d7a2bd7060ad98c835ad899146953e0b9611f381fd5212c4fc8efccb74b494f5ad2929c1bdf50f14629715b3ca3a4ebf478441883d201c24da12ca0dccf83adb37057db487a78a86e2f2aac85c77b66896e0b8bee01a57ea7353e710c8ba70c172590ac2843520e26a2bea555abb2e08be3ee158c73325f2d28053c8adf0ac529051b729a15ef40033d9308208768db4f3af4470304de164d7cf7db5fbd80bd82007bb66703d5b5d8d3716579399736fd81e1e4b0f8df407c5aff52dcc7d4b9104203a45f32888921398704f74f022346778e4feec6acdfea405d24a3cfba770fe230dc17eb0f50f6290b1f3ddfcf9d3a0171fc914da5281f9e5d955a8c75ba3b472b4e724bfe11aa2b720c4c99b6f9cd40eaf957dc39941025a23cff213892a03c840555d984ab370a74ce8fee6fefaf9bc80e390d5cc258a277974304bcbb83a2eebcfe080ef886640685a7174862b89133e35cbb42819d9949d5077aafc752be3fbda1405193d2be0733e91b6a0b45388a039e5fe50d5bcf98472c8be6217fe5595c3198d5db6c1856d9db97c484182cf1071b3473beb4f9df03efbbee998dfa49da014fff5b996d93bd8a79b2a3931666a49eefc13cbbf56185e2ea9924ff1ea632ad72859984397529de6b6c500f5fab65813810606f4449afcf57a59f6a7fd7140bb8a59b87fe0d8de65398938c0b7a8697eda50c175296c3a82a93de00a40f623d62c02adff2ca8ccdab1357a009d7e7f6f461152c28fe917fc56d94fd66546c9691ed640692515e4bdd1d7ae0a2d1c",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// zero key, random nonce
|
||||
const key = fromHex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"623ac6e73c2c00951bf4c7490e4692f8e30f5f8c1c4196da",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage(""), key, nonce)).toEqual(
|
||||
fromHex("ee5f3601ce18d227df5d5a8b2ddb05e3"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("61"), key, nonce)).toEqual(
|
||||
fromHex("0c5a3d0c311a2a9598ce968549a8d6a6f9"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("6162"), key, nonce)).toEqual(
|
||||
fromHex("0ce46df742a0423e9954a903c9f5bf54412e"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("616263"), key, nonce)).toEqual(
|
||||
fromHex("0ce4be1fd7c793ac56c6d734955514bd8391a3"),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"c0b1b1d9acd58a7e8bc324343f0f9bf4eca174c4f9056b7e2dda6f185ea7587d032b3b202df5db9ae73ace5f994b366cb82d05175b6af3f7b93f53a8bd3bf99f69dae2c5bdcdf9bb74c21706687956db8d9879a833ebaf7d70d74b52c750026f5dd14dc03c6376848443f0dc18fa39d2acfaaf41276789621aa6b844af8a3e00e0dc807ec113343a9a14c4e205064f113af0aaa01f00a0cee37e8f0f54d9818248f60f9f1bd2ff5c615f7ca72c6d71085b0ef79f51d8978cabf6c6dc31d526aa0a692748882bda4e2684014b7bdd2e598b1fed56b9ec4038a10a32be2c9e1b88d99d76d17646326ae4a8513bf030dc19af0e83a69219231dec942d72abd963a3e2fc854494f70539e5a92076f32431334bdba71aa81bb96fad0daa4e64a99f0d3bbc781554ba52bdac4690216faff2d25c229d2d69ae16da7d3a8adbc0424a3a923e562d3064dcc78155b4223a73c2587f4bc77c0ebe9592264c74437d95e976f11aef5e26db2ae7b54a4034301a21bce58aee4f51b8c4d09b2b188121c012f5a476580573b497cbf26f96c03dd22f2128f1e8e0669857a87d6a60741cef746b0af6c56adaff445f7736c65265d3a38305421b0b9e6161dcc2eac86f1d97a253c9df8594cf654e1c85ac6a323935b9a30911422f2514bc62aca52dacc4fea99fb2f28c71ca79737d8968b5d420143f4d313d3affda4a584877f08d59f7b83c7a16045c6d9d39ae696905139a322d02be29ce99a01493f9e3f38fbcb48b7c66d30c8b78c703eb1079eb5077f07ef5ecb281d15bedd35db5decd343c85ebe5471fd40004c24d3ad31878c5e92c98143728d2",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"643165d44ba5adadc8dcd7cd9861e75bd376b35b393148bedac86beab88bd6d6fb95e70f45791d86ef1a5d2b7c2bdd624dfd878807cfda46208a5321f647b6f40f15c939ed4e8128109582c3296c119f81de260b205aff98cf62de57c51acfc2e67027e6f24ad416b9626c0e554f31f91d33872da95f5f7a43f1bd244e75fc9a64b2772e8d9e53e11686567bb1d4d0bb2245e6e517a06a47e1262c9e3585eb569593917af12507dcc93b198987367ce2e4f9f318e36e7dda75a35d84c062b1049a27ccd09cc99623eae3c67a3d64c219cbcb345be3dc121868a8d444314df152cf277353f8dd98c2be83b0257c7e952e43fff86ede2d6d77eb6bff8937587d0d41f1dbf6b9a318aa6e646682b9ec6739c1914cb3dba1f5a7f62abc0819e8c50c084b89ed9cae2c9e36ca699c4f0763599e67f7d2087edaf24560d21000436612a9244f0f2c465e98acab5aef36f1b0c0c485d0e896c5c65f0eedd118bd79dc9351218fe5a817271089a77e1fdb999885a0cc52fc603d58629a27ce314fd06e0f1dc109657ea210552f7aae86e4e906b8ae2943b9aa9e1bf42c35f92b5a4557e29e6a8b72173c576e300f7d02537ae8aeb52913a4ca18d4a89cccb6b39ad57adcfd9e0edb85b43224c1e60b66bac387a6cfe95e0f827835c12938c5d0833ac00e73c5a0846ec99e9eb2cb71a68e3b2e63a86969aa2face17c99bcd834afafc4ee1e2a8f26fc976d8ac49bc99c5b5b1630b27c85676fe6b9c0aa928e26585102e553486d943bae01de725d3b0a29b47216c2b5a43fb1d9d528b16760447643e93b2bfce4247dce0e9167c26d776ce5db7044411f3d1d4f51c400f0b14f65f68f395af75568955bbd1662b9e85c2b9651d0296be568d410af43d78a7202d357f7093644bbab734db1f8e2c4eb8f950b4e20122c0f079d3980539e8a7a22b0444d8e548f17a21c5cd01a1fdf237cd55e6031921488598919c6ded91d33169240d1dd5684de3368c0148cf82bbf9587a118ce770e5371b34b97a50cfeb2f685142dfa609225f6ed6c754c567eba859d794e89bd3b88f768bed757ecd2fbee8216816d4b15b6f39fb59b583353478372b6e395d845b57aef27dcdf40ee5e906e62fd6b5ae4ec071d95a3d9dc688b7aa1fe9bed64070e8881cbb7e10d37da67306d8c9c6f71dc175f055bba88271dfdaf0a88a4c5460fcf83c0b9ef58e75ee18a75bee1ddd3de18d130bc90327e2338cf2f640256d02ecffe9fa113c02c168c97845df2549c62504d1e6054d80caa18821222dbf73707816c7513c92574a8257bd84507b8706f9eea99d02910c3a7e5d4ed9d37d5f22888288e158a0c665bc5be491c514d763851e22a4852c8b8178cac89745949f264db4d4b3ac711ddc3b2e556aa10d957e60734e45b3f5b84a800752da82f36d820474126703c734515123b1054723a741d2840a8e935690158c47c8aa185",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// random key, zero nonce
|
||||
const key = fromHex(
|
||||
"1e1d2cd50e63f38a81275236f5ccc04c06dbe7a9b050eb1e38cb196c4125bbe2",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage(""), key, nonce)).toEqual(
|
||||
fromHex("6142f36622e4becf1fad773123d067e8"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("61"), key, nonce)).toEqual(
|
||||
fromHex("94f1c39995a77dd01265112ea5f5c3470c"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("6162"), key, nonce)).toEqual(
|
||||
fromHex("94f1ba350b17e0b580bb7541812ffcaf4eda"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("616263"), key, nonce)).toEqual(
|
||||
fromHex("94f1a17b016c87a9a1135b57acc427edea2b6e"),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"58a4ae64d149c7fe648d93f0a293055b08c72302a457aa99d6892b258b9d5bcc59332df250f735d98ff32351907738efbc21ef0412e9a00ae7f2fecedcd6ae52abe266f1e17990ce2f958bfdd6bc32ca70f0734788d44318d7fc86c687059da9ac6b859c5286b3941b2847891ea87a56e81a3d7d543dea2c984492ff8345ab8f52ec69d0ce1392dc42f1df875b9170b198b04ee4d9f7cfd430c145066a9b4592b828d5785e600b655982797557827cfc8c9e2bd55df3fc956f5199c59974dc2e1550361487181516f322b44d7d1649dd072932e0225bf929e1c6d905055051ccc779edbdd9c8643f318a6f564197e3187b27f84b4e153317e860542a3bbe541738c9e6115da21bd23cc40a8ef8b205f824fd4bcf4abfd88cfb4436a4e651c39a14412fe68da9a1a1abf62674ff001e31a6d2b435828321686e1b135a51d65e8117dcf31aab6b8120ebcc6a8c048d47685c9a0fd58ee629b9a7ee73734082da893484ef9de37a291881ff808192d0726c41f1b04fbd687331e1e8a71cc1f84186556c6bc81545413c52a1cc21e42675929369340f93d55396ce25e1fdd819015e8295d529b85032f21aeefadfb1aaac0e1d3e04b5fba0150a9b9796a628e5627713443aed99f480cd7495b5eb5a4c65d2b9bad43fce9b7d24a18cf3e8e983942ed476f2deb38aa3ff38f0b84a3432e5fb25647a3ca0d2a563e8655f93cd1be4f2672375740a469b75dcdd66fc5f3645e751caadfcf11ec3efdf1015160b3035134b44acc6fef3b5fd00d9c7e713590d1021ab6c2ced3316a813a10969e3da876fe152b8253ddd4f853fdc9954e3f3349de7",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"fc247a693639e02d2792600905fd79f43710e49d64638959219b2fd76db1d567a18df1dd387bf3c587d3b0257517d3e149f16d9b4e4c89bb7e47fe4797aae139cd2d4d0db1fae85d4bc21e3897a9758e7cb62ce49b6513fd684913c3854f500417caefba9caf11062609db5b531d727d59d31511da053c34c113979f62ba6915d6829e80829ef507ce634d1eef43ef1b800502a1d157055d3299e6970bc72f46654d4b9db497f3e5f1e61c5bfcd9711633692f52ef4516c3b104029d68c34b80851edd8c93fa597b3f45737c3bafa59d47fdebed786bab0928643fff1883bb16d1c3e83f5753ce976ba18e48cdd9aa2f97d6838302217d7def9f86d1a73f4ab99bc4b8a370f60641b7094c7ab27a53f2aeb7a06639059444a06320e29b10999b27b6de1e45bddf82317adfc9dfa88fba6497decae353ed4056414b9191d772a92cc6ea38b749037fc6328441080f35f0e7541841169d7a748f4fd628806eef6c94bf8f266db624efbd12beaa7953cb5504b70cfc8cedef83e0e471acafe83d7cecdb3aa81853c6a28fb4f4673d1d5c0b15b19f565fd31fca9f7a78a29eb322d716099b31759321c35dd7418f8703e723ad550c1aafd9a07ec5b1e87aafa7baf82705b1a2d325fcf530dfd4bfd9ba5bd77f42c81f69f7f48724111b94ae47fdbf1541de2b173a4e1c03537c389a1df4d5bc30296955341c5706290afe950c1c666f0da63f6be858967143bcfa36405169ca78b13b8a6b83cc860d2784d81d51251487b995c6b6a45a99d48b1d441893b462cf93fe8fb7765e6ff255a87e7c294b1e69e2483a56586d5e422bf9095e7b2aac6305ae4112024e02966b6d6494c4252c44f2ff807b27597891f95b2c2781444ff402155f2e34477dca2d5926934bba819561cdf7fa3e6e1009db51a4d99f2c364854b20b5e4f7713a409ebd31e16e0f6505158fdd2406a085c66ec2af17aa2e197f22094cf6167168243b5e73e45286b1fcc8b66f28058a0a6b2554f641780c4e31d8b6f47f34e0e978ecd3127ae148561a8c18c1f36b5349ae74fddc3caa4ba88c31a12a42f918c10f0bf56defcc8bcfb0975c2d550160d381c9b323d87d927f490abd1c992e490aa0c069437c34998f8b0e8e6f97ea010195387d512d2bef913fcfee4c088a36b0e03c5e14296cc991c2efaf9f27f2c44aa5734defac0138de2049b03ace45b3f4db3865be3157924ede7ee0e381fefb4c418363326f5c0d72ca1e9b9bd2cac73e3e793b1afc8101f3cd57aa7edb9d49777f447637d423821cde426e9f56083d0fd0c39816d08434f04ddc1eeeeb9fe6bc4c985d33c66f64a221f63f2d2f9fc2fa68ccd8b797ca84fbb92fda61093490492bdceb5796de563592f97531a15edb2b45b15008207ebb27fc4d9dfef4014e3f347af35c1e5686df24491b88477c90180a89d845299097a54f97d71ac3b07e6722602dd823434",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// random key, random nonce
|
||||
const key = fromHex(
|
||||
"1e1d2cd50e63f38a81275236f5ccc04c06dbe7a9b050eb1e38cb196c4125bbe2",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"623ac6e73c2c00951bf4c7490e4692f8e30f5f8c1c4196da",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage(""), key, nonce)).toEqual(
|
||||
fromHex("7e51936afe05c64a87fcd64cf811f22d"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("61"), key, nonce)).toEqual(
|
||||
fromHex("fb5530e69e505cb1d3d5344f354e8e96d5"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("6162"), key, nonce)).toEqual(
|
||||
fromHex("fb4927d0dbb75b8d866c0f891195be725cfe"),
|
||||
);
|
||||
expect(await Xchacha20poly1305Ietf.encrypt(makeMessage("616263"), key, nonce)).toEqual(
|
||||
fromHex("fb4984e6af4f702a738693d59104c6690da890"),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"371c8bc350aca6a7aeb8eed66276f2a11f4f48147aa0871bc693603beeec2c68fc89dff91dcc2b7799960f40c3a8cb2c9d98d0c8fb22e3823f066e0f73e95c1420a386aec2060c21e8cd2cd79f3c14cc6af7d249acd2cd7945318b0184fdacb6eba33eb7e4498431c9ed484f24c1c4ddd8a8f3ed0f82d01e0afe595ef389c3dcad0688443cdebb3bbc0f8b34e8b6455012c77fed003d433e267ac0aea55618ef95ba3726484f6982900bc12304c2dcd89101301a9dc8eb350dc24532c7f46d9f34565d6abb6546c2fd8c74a1fd2a715b81d54cc93bd12b315ec070c81cfa072472a5b9e89ec545a0d1895e112abca47b3d2a610f1d06ccf80c04529a01794cc5498fe6ecf48cdb97f8b591b1688d6706b72cbc480f463016f177b7d2b941d1e48ac7a735e7f84fcf42004aa2c37941930a4f5b98ab3c68f32667f4f814c12c0e17d2c7599b2d248d51e3e7dae9f1df5441f1290bc26e2e7043ead46958c2abbc76ed2bc8e01befbd9a4d55af491d3187815b90cb1dc6016b5e15698a5b6cf41166d291118f19a25e6fdce0a04a1ecfa4a7ee66f8d21774921b9a31fe72a20de610338c7c041e2c7980e28d6da6b769f321e142ed2cc4334ec87e974052f39442d1a87c28846c1bfee8fd41254cc2359dcc885b95f1b8d64d8ca5bc82035a52f7ce50ae9456dbec99b6a2c4f29c4d3f140edfb0adce7b990db7bfe8f440c1674c33b5bde7814c12bccc1968fef2282c927b08f95b06e48716191ec95bd0c65f18f88e8fe2e626eee92bcbb960df674320a2591e82e973ccac49d67dc3f34897e51b9089520ae8275be009c0b5695325e7dd",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.encrypt(
|
||||
makeMessage(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"939c5fceb7dc8174eda71d2fc5188e0e20988f8bba94a4db318164c908c0a2c3043703d67540ed6b91b69c3426c8202268485257a787ca33a6b36e863895137f466cad52928574b28c9ab912de29538866b18deabf639d9cfa841e0486b7611b500254912a6026a3f4ccd49d6974ccf66961db8181ba060653a95c3e1276014629687f147053dce0309d19ad5c64dafa0a7233a8089d89b72422633fc40a723b48dfa9c3a2b89102386fa40daf99d1322ef6349d2f7e0163d397de6a3643fa31a418b6f2af870aaf31ebb390bb939d1bc10195c461e17911976296320129edfe641fbc6a105eef088ba2bf0fa6f2ed4cd1db1ac7513282920bfb80619df8526bea82b85ed9d8c6047378d7452245310c3d6657e17cfc7cdeaa50a194c4008be5b93056cd2fec31ecd88cb31fe3d1d018c80a3167caeca4db1e3dac33d4c000262cc8de7b870fa6d27c1d0917e573adccfa3f3e9f5a157dbd6b4b7132982e9e59d6d64b736ed7e24aa6a06b84a29e88bec41d2c782c439dd95f19bf3a357c88ebdf65c071820f25c0b2c9d8e69325e63d2136cda11e1138ce4ac5a8a134082e6f84afc264c9dd3f48c7db363d901e22de918a4a4278bd863a9658e99cd5b14ccde5e9f767cebd67c6acb72071cf340b980a7047b556d45fee093854fe449e3b660f678261f26b017a8d01008032622e3a978be3f83b9d203959f3bd9918d69fd83b9b6eace0e2d15f6187b2f89b5e381ce0bae59c7d91c7354003fbc903eb3b2ea74d9ab1de63ff4eb2c6f59a8826dd84e13de1508bf7ac5a358521026eee39c1e45a5bd889404da32f6cee415928e01b723cea9165a487073989adbd4ed139dc32fd9912f750bfc0f4d7113a24e2855218ec99d5de1c87276727134cd778989937c00c30c26e7b0c91f39aa1eed52089f55092bac70bd91b190ae573490713c00cc47915540a8e15e7fdbd02874d9f8de11e3d91b0add39b27cddbb91e9cc11958896d5f43cfaff9b672bd18af5260e28c48c12fde57d34f0e9df7199f514bac7e06d164fc96ca69170c5194f040ed14d45a07766e87afa919fe4f082e3d95c6e73c3fe26cf742f71152d9ac4a1e554830075b1812ab5acde59b98bcc1e1894935e7a1bd66bc7187a3d2555f10589d60dd93e5d997fe6ffe18fe787eeab5ccfb80c012c34e4fb4fb30387e0ba9d9e2d07876f3598b95b92ece64a0f3b4aa70fbf8415ffa195a076689947c5e539098856bab5950f0bccf839c283a5972e8e70000883ddcee167eb2c424b72a82753a7f4ef14bc66ba621331e37392fce0eae3d820059e0316b5b7f1cb46a2ef026a6a942745267918cccbd535af4bc3b1e14d9b543946840c824f21730a233d01cc438cb2ac5435135a627c77508433c8c657f87bfffea9bcefe9b8e21f4e7c64a39fe3766896f4e32829bee81c2d7f12464067b734245b5246814f986f35517fee6ec",
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("decrypt conforms to Botan implementation ", async () => {
|
||||
// same data as in the encryption tests, but reversed
|
||||
|
||||
const makeCiphertext = (hex: string): Xchacha20poly1305IetfCiphertext =>
|
||||
fromHex(hex) as Xchacha20poly1305IetfCiphertext;
|
||||
|
||||
{
|
||||
// zero key, zero nonce
|
||||
const key = fromHex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(makeCiphertext("8f3b945a51906dc8600de9f8962d00e6"), key, nonce),
|
||||
).toEqual(fromHex(""));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("19841f4c8866efab6c6b5329aef9e36752"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("61"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("19fcc0cbdcffdf83f822811687b79930dcf7"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("6162"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("19fcf5f6e7aba23c37d7a59e4c8f061e15f1f6"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("616263"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"d5a9fa454739afd6e6b2dc5d09d9f83d98dd5abb3b3b188c81a33ad048bed75c2aa1a53cc81c02b1d3204506114cc4b58278d4a8fc8ec3916298ac165a94ff84d32320086cd98c0127a3d372112a75dc7629e55ac704dc8fd55be9033409f03254fae947912c0f737e8626f44b63c8dc7d7d3cddf57348b06fe80af61627f9a5a282b8d49cd2adf5191dd7855ffa5b931295773744691ba0b99305de8581184046c074148a8488d021122ddec21482bde85029aecd09907371ae67abc6cfc44655c3b49e7df3e2fac6b73b2803746bd1b4d17b30f5c2dfc31a7c33be5c737330e855d7630155523e3fb85c55a5ff95fd0754f4e9c5f0d88827bb28caa9af2d8f3e52b5d0957dd37d77af5df644bc483a6583d3b1e32d3b5dcbe7a6684ff8c0e44c7250744f705fce4e588f3a2ae65cac0c2b218d455911a2415fd839d3ef288d7447d6e561f4d70e93a739f6890ee3b6edcd3089266bd17d73a0c7fb96a1cdf96b2b7b18f38fe73bb6eb437a37588628ab51894b421137d1032a480643f5b36bb894ae82b8927fa7e87c906b90a3379b3f0aabee718a1e87f31dbfa3f8b1ebb5df9fdf96cd93ecdfcd421ded6299929676fa58ed3034f13474fa278b0a2c62b136e211e7a33e437e24386aae16524adc17557112fdaf484c5e6a038e8513cb2cb157816d6c85406aaae5514ca5b900dd18a90147311a37c82538e1f9adbad1bdc993264475cc54f61e54e048d63190cf138f3cdb5faf52e954d1caa8081a614beb44b2d9bea7e30d51513b4c3fd77e1cfd84e58588f733116c9be6b15cd4f72e3d602c2fe77f7880b91f62197c547df9d2",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"71292e48a0498805a5ad2fa4aeb78492a70a9d24fb0f3b4c76b13e22ae9259f7d21f7913a090c4addb00d672f42c2fbb77a85637a02bea20fb2dac9f11e8b0efb5ec0bf43c5af49243f446b7503f32987a6fbaf9d4b58c6a6aee7c0636433d9fef5b83615f05ade143a7ba2606d6c0f7ccb414b17b4b9ea836bf0f96f7d83b3f26ec4f84d05fca2e958f451ceb28c4390a203b724cc9d129bbcba64fe4dd72949ba5eaf160737050897648f0694f8f5757a72d297fbf7a25affbfcf3377853e8c58d5f066911ae970ad0fc1945cd8791f405a23daff28de3d3ded54441a099eafeefd2e18fcef8966593bd4b29b1dccaeba58f2189c496e22044fa31352e33219d5feb62b829ceeefc621b020e741e30efc938189097779590c0b02e32b99ae57f85a18c876421edd4d476870a4ecd27ce6e4b722489dd8a790580f213ee04a54f5dcfc77dd65551be59d73b858c912e5603271dbe1082b05b0162a0564df81ccb101ba37d43eacc8a067d51dcdb3f11ee1735f87394ab6302269eb62de5cf910123ffe2b584f8393569a82d49981e02b9d200b7bd8c52dba24226fcbe1bc83c4b03918e0050ffee8a7ba6bd5430d9bbc6915042644d44402adc59578d6eba3e02a39aa8e9ef3f4660720bfa95a474d9d1ad6d325ac3c1efdbf7ebf2c2d7a2bd7060ad98c835ad899146953e0b9611f381fd5212c4fc8efccb74b494f5ad2929c1bdf50f14629715b3ca3a4ebf478441883d201c24da12ca0dccf83adb37057db487a78a86e2f2aac85c77b66896e0b8bee01a57ea7353e710c8ba70c172590ac2843520e26a2bea555abb2e08be3ee158c73325f2d28053c8adf0ac529051b729a15ef40033d9308208768db4f3af4470304de164d7cf7db5fbd80bd82007bb66703d5b5d8d3716579399736fd81e1e4b0f8df407c5aff52dcc7d4b9104203a45f32888921398704f74f022346778e4feec6acdfea405d24a3cfba770fe230dc17eb0f50f6290b1f3ddfcf9d3a0171fc914da5281f9e5d955a8c75ba3b472b4e724bfe11aa2b720c4c99b6f9cd40eaf957dc39941025a23cff213892a03c840555d984ab370a74ce8fee6fefaf9bc80e390d5cc258a277974304bcbb83a2eebcfe080ef886640685a7174862b89133e35cbb42819d9949d5077aafc752be3fbda1405193d2be0733e91b6a0b45388a039e5fe50d5bcf98472c8be6217fe5595c3198d5db6c1856d9db97c484182cf1071b3473beb4f9df03efbbee998dfa49da014fff5b996d93bd8a79b2a3931666a49eefc13cbbf56185e2ea9924ff1ea632ad72859984397529de6b6c500f5fab65813810606f4449afcf57a59f6a7fd7140bb8a59b87fe0d8de65398938c0b7a8697eda50c175296c3a82a93de00a40f623d62c02adff2ca8ccdab1357a009d7e7f6f461152c28fe917fc56d94fd66546c9691ed640692515e4bdd1d7ae0a2d1c",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// zero key, random nonce
|
||||
const key = fromHex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"623ac6e73c2c00951bf4c7490e4692f8e30f5f8c1c4196da",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(makeCiphertext("ee5f3601ce18d227df5d5a8b2ddb05e3"), key, nonce),
|
||||
).toEqual(fromHex(""));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("0c5a3d0c311a2a9598ce968549a8d6a6f9"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("61"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("0ce46df742a0423e9954a903c9f5bf54412e"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("6162"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("0ce4be1fd7c793ac56c6d734955514bd8391a3"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("616263"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"c0b1b1d9acd58a7e8bc324343f0f9bf4eca174c4f9056b7e2dda6f185ea7587d032b3b202df5db9ae73ace5f994b366cb82d05175b6af3f7b93f53a8bd3bf99f69dae2c5bdcdf9bb74c21706687956db8d9879a833ebaf7d70d74b52c750026f5dd14dc03c6376848443f0dc18fa39d2acfaaf41276789621aa6b844af8a3e00e0dc807ec113343a9a14c4e205064f113af0aaa01f00a0cee37e8f0f54d9818248f60f9f1bd2ff5c615f7ca72c6d71085b0ef79f51d8978cabf6c6dc31d526aa0a692748882bda4e2684014b7bdd2e598b1fed56b9ec4038a10a32be2c9e1b88d99d76d17646326ae4a8513bf030dc19af0e83a69219231dec942d72abd963a3e2fc854494f70539e5a92076f32431334bdba71aa81bb96fad0daa4e64a99f0d3bbc781554ba52bdac4690216faff2d25c229d2d69ae16da7d3a8adbc0424a3a923e562d3064dcc78155b4223a73c2587f4bc77c0ebe9592264c74437d95e976f11aef5e26db2ae7b54a4034301a21bce58aee4f51b8c4d09b2b188121c012f5a476580573b497cbf26f96c03dd22f2128f1e8e0669857a87d6a60741cef746b0af6c56adaff445f7736c65265d3a38305421b0b9e6161dcc2eac86f1d97a253c9df8594cf654e1c85ac6a323935b9a30911422f2514bc62aca52dacc4fea99fb2f28c71ca79737d8968b5d420143f4d313d3affda4a584877f08d59f7b83c7a16045c6d9d39ae696905139a322d02be29ce99a01493f9e3f38fbcb48b7c66d30c8b78c703eb1079eb5077f07ef5ecb281d15bedd35db5decd343c85ebe5471fd40004c24d3ad31878c5e92c98143728d2",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"643165d44ba5adadc8dcd7cd9861e75bd376b35b393148bedac86beab88bd6d6fb95e70f45791d86ef1a5d2b7c2bdd624dfd878807cfda46208a5321f647b6f40f15c939ed4e8128109582c3296c119f81de260b205aff98cf62de57c51acfc2e67027e6f24ad416b9626c0e554f31f91d33872da95f5f7a43f1bd244e75fc9a64b2772e8d9e53e11686567bb1d4d0bb2245e6e517a06a47e1262c9e3585eb569593917af12507dcc93b198987367ce2e4f9f318e36e7dda75a35d84c062b1049a27ccd09cc99623eae3c67a3d64c219cbcb345be3dc121868a8d444314df152cf277353f8dd98c2be83b0257c7e952e43fff86ede2d6d77eb6bff8937587d0d41f1dbf6b9a318aa6e646682b9ec6739c1914cb3dba1f5a7f62abc0819e8c50c084b89ed9cae2c9e36ca699c4f0763599e67f7d2087edaf24560d21000436612a9244f0f2c465e98acab5aef36f1b0c0c485d0e896c5c65f0eedd118bd79dc9351218fe5a817271089a77e1fdb999885a0cc52fc603d58629a27ce314fd06e0f1dc109657ea210552f7aae86e4e906b8ae2943b9aa9e1bf42c35f92b5a4557e29e6a8b72173c576e300f7d02537ae8aeb52913a4ca18d4a89cccb6b39ad57adcfd9e0edb85b43224c1e60b66bac387a6cfe95e0f827835c12938c5d0833ac00e73c5a0846ec99e9eb2cb71a68e3b2e63a86969aa2face17c99bcd834afafc4ee1e2a8f26fc976d8ac49bc99c5b5b1630b27c85676fe6b9c0aa928e26585102e553486d943bae01de725d3b0a29b47216c2b5a43fb1d9d528b16760447643e93b2bfce4247dce0e9167c26d776ce5db7044411f3d1d4f51c400f0b14f65f68f395af75568955bbd1662b9e85c2b9651d0296be568d410af43d78a7202d357f7093644bbab734db1f8e2c4eb8f950b4e20122c0f079d3980539e8a7a22b0444d8e548f17a21c5cd01a1fdf237cd55e6031921488598919c6ded91d33169240d1dd5684de3368c0148cf82bbf9587a118ce770e5371b34b97a50cfeb2f685142dfa609225f6ed6c754c567eba859d794e89bd3b88f768bed757ecd2fbee8216816d4b15b6f39fb59b583353478372b6e395d845b57aef27dcdf40ee5e906e62fd6b5ae4ec071d95a3d9dc688b7aa1fe9bed64070e8881cbb7e10d37da67306d8c9c6f71dc175f055bba88271dfdaf0a88a4c5460fcf83c0b9ef58e75ee18a75bee1ddd3de18d130bc90327e2338cf2f640256d02ecffe9fa113c02c168c97845df2549c62504d1e6054d80caa18821222dbf73707816c7513c92574a8257bd84507b8706f9eea99d02910c3a7e5d4ed9d37d5f22888288e158a0c665bc5be491c514d763851e22a4852c8b8178cac89745949f264db4d4b3ac711ddc3b2e556aa10d957e60734e45b3f5b84a800752da82f36d820474126703c734515123b1054723a741d2840a8e935690158c47c8aa185",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// random key, zero nonce
|
||||
const key = fromHex(
|
||||
"1e1d2cd50e63f38a81275236f5ccc04c06dbe7a9b050eb1e38cb196c4125bbe2",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"000000000000000000000000000000000000000000000000",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(makeCiphertext("6142f36622e4becf1fad773123d067e8"), key, nonce),
|
||||
).toEqual(fromHex(""));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("94f1c39995a77dd01265112ea5f5c3470c"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("61"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("94f1ba350b17e0b580bb7541812ffcaf4eda"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("6162"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("94f1a17b016c87a9a1135b57acc427edea2b6e"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("616263"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"58a4ae64d149c7fe648d93f0a293055b08c72302a457aa99d6892b258b9d5bcc59332df250f735d98ff32351907738efbc21ef0412e9a00ae7f2fecedcd6ae52abe266f1e17990ce2f958bfdd6bc32ca70f0734788d44318d7fc86c687059da9ac6b859c5286b3941b2847891ea87a56e81a3d7d543dea2c984492ff8345ab8f52ec69d0ce1392dc42f1df875b9170b198b04ee4d9f7cfd430c145066a9b4592b828d5785e600b655982797557827cfc8c9e2bd55df3fc956f5199c59974dc2e1550361487181516f322b44d7d1649dd072932e0225bf929e1c6d905055051ccc779edbdd9c8643f318a6f564197e3187b27f84b4e153317e860542a3bbe541738c9e6115da21bd23cc40a8ef8b205f824fd4bcf4abfd88cfb4436a4e651c39a14412fe68da9a1a1abf62674ff001e31a6d2b435828321686e1b135a51d65e8117dcf31aab6b8120ebcc6a8c048d47685c9a0fd58ee629b9a7ee73734082da893484ef9de37a291881ff808192d0726c41f1b04fbd687331e1e8a71cc1f84186556c6bc81545413c52a1cc21e42675929369340f93d55396ce25e1fdd819015e8295d529b85032f21aeefadfb1aaac0e1d3e04b5fba0150a9b9796a628e5627713443aed99f480cd7495b5eb5a4c65d2b9bad43fce9b7d24a18cf3e8e983942ed476f2deb38aa3ff38f0b84a3432e5fb25647a3ca0d2a563e8655f93cd1be4f2672375740a469b75dcdd66fc5f3645e751caadfcf11ec3efdf1015160b3035134b44acc6fef3b5fd00d9c7e713590d1021ab6c2ced3316a813a10969e3da876fe152b8253ddd4f853fdc9954e3f3349de7",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"fc247a693639e02d2792600905fd79f43710e49d64638959219b2fd76db1d567a18df1dd387bf3c587d3b0257517d3e149f16d9b4e4c89bb7e47fe4797aae139cd2d4d0db1fae85d4bc21e3897a9758e7cb62ce49b6513fd684913c3854f500417caefba9caf11062609db5b531d727d59d31511da053c34c113979f62ba6915d6829e80829ef507ce634d1eef43ef1b800502a1d157055d3299e6970bc72f46654d4b9db497f3e5f1e61c5bfcd9711633692f52ef4516c3b104029d68c34b80851edd8c93fa597b3f45737c3bafa59d47fdebed786bab0928643fff1883bb16d1c3e83f5753ce976ba18e48cdd9aa2f97d6838302217d7def9f86d1a73f4ab99bc4b8a370f60641b7094c7ab27a53f2aeb7a06639059444a06320e29b10999b27b6de1e45bddf82317adfc9dfa88fba6497decae353ed4056414b9191d772a92cc6ea38b749037fc6328441080f35f0e7541841169d7a748f4fd628806eef6c94bf8f266db624efbd12beaa7953cb5504b70cfc8cedef83e0e471acafe83d7cecdb3aa81853c6a28fb4f4673d1d5c0b15b19f565fd31fca9f7a78a29eb322d716099b31759321c35dd7418f8703e723ad550c1aafd9a07ec5b1e87aafa7baf82705b1a2d325fcf530dfd4bfd9ba5bd77f42c81f69f7f48724111b94ae47fdbf1541de2b173a4e1c03537c389a1df4d5bc30296955341c5706290afe950c1c666f0da63f6be858967143bcfa36405169ca78b13b8a6b83cc860d2784d81d51251487b995c6b6a45a99d48b1d441893b462cf93fe8fb7765e6ff255a87e7c294b1e69e2483a56586d5e422bf9095e7b2aac6305ae4112024e02966b6d6494c4252c44f2ff807b27597891f95b2c2781444ff402155f2e34477dca2d5926934bba819561cdf7fa3e6e1009db51a4d99f2c364854b20b5e4f7713a409ebd31e16e0f6505158fdd2406a085c66ec2af17aa2e197f22094cf6167168243b5e73e45286b1fcc8b66f28058a0a6b2554f641780c4e31d8b6f47f34e0e978ecd3127ae148561a8c18c1f36b5349ae74fddc3caa4ba88c31a12a42f918c10f0bf56defcc8bcfb0975c2d550160d381c9b323d87d927f490abd1c992e490aa0c069437c34998f8b0e8e6f97ea010195387d512d2bef913fcfee4c088a36b0e03c5e14296cc991c2efaf9f27f2c44aa5734defac0138de2049b03ace45b3f4db3865be3157924ede7ee0e381fefb4c418363326f5c0d72ca1e9b9bd2cac73e3e793b1afc8101f3cd57aa7edb9d49777f447637d423821cde426e9f56083d0fd0c39816d08434f04ddc1eeeeb9fe6bc4c985d33c66f64a221f63f2d2f9fc2fa68ccd8b797ca84fbb92fda61093490492bdceb5796de563592f97531a15edb2b45b15008207ebb27fc4d9dfef4014e3f347af35c1e5686df24491b88477c90180a89d845299097a54f97d71ac3b07e6722602dd823434",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
);
|
||||
}
|
||||
{
|
||||
// random key, random nonce
|
||||
const key = fromHex(
|
||||
"1e1d2cd50e63f38a81275236f5ccc04c06dbe7a9b050eb1e38cb196c4125bbe2",
|
||||
) as Xchacha20poly1305IetfKey;
|
||||
const nonce = fromHex(
|
||||
"623ac6e73c2c00951bf4c7490e4692f8e30f5f8c1c4196da",
|
||||
) as Xchacha20poly1305IetfNonce;
|
||||
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(makeCiphertext("7e51936afe05c64a87fcd64cf811f22d"), key, nonce),
|
||||
).toEqual(fromHex(""));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("fb5530e69e505cb1d3d5344f354e8e96d5"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("61"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("fb4927d0dbb75b8d866c0f891195be725cfe"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("6162"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext("fb4984e6af4f702a738693d59104c6690da890"),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(fromHex("616263"));
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"371c8bc350aca6a7aeb8eed66276f2a11f4f48147aa0871bc693603beeec2c68fc89dff91dcc2b7799960f40c3a8cb2c9d98d0c8fb22e3823f066e0f73e95c1420a386aec2060c21e8cd2cd79f3c14cc6af7d249acd2cd7945318b0184fdacb6eba33eb7e4498431c9ed484f24c1c4ddd8a8f3ed0f82d01e0afe595ef389c3dcad0688443cdebb3bbc0f8b34e8b6455012c77fed003d433e267ac0aea55618ef95ba3726484f6982900bc12304c2dcd89101301a9dc8eb350dc24532c7f46d9f34565d6abb6546c2fd8c74a1fd2a715b81d54cc93bd12b315ec070c81cfa072472a5b9e89ec545a0d1895e112abca47b3d2a610f1d06ccf80c04529a01794cc5498fe6ecf48cdb97f8b591b1688d6706b72cbc480f463016f177b7d2b941d1e48ac7a735e7f84fcf42004aa2c37941930a4f5b98ab3c68f32667f4f814c12c0e17d2c7599b2d248d51e3e7dae9f1df5441f1290bc26e2e7043ead46958c2abbc76ed2bc8e01befbd9a4d55af491d3187815b90cb1dc6016b5e15698a5b6cf41166d291118f19a25e6fdce0a04a1ecfa4a7ee66f8d21774921b9a31fe72a20de610338c7c041e2c7980e28d6da6b769f321e142ed2cc4334ec87e974052f39442d1a87c28846c1bfee8fd41254cc2359dcc885b95f1b8d64d8ca5bc82035a52f7ce50ae9456dbec99b6a2c4f29c4d3f140edfb0adce7b990db7bfe8f440c1674c33b5bde7814c12bccc1968fef2282c927b08f95b06e48716191ec95bd0c65f18f88e8fe2e626eee92bcbb960df674320a2591e82e973ccac49d67dc3f34897e51b9089520ae8275be009c0b5695325e7dd",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"ad376ccca21922a93f532f98bcede77577c5fb857ab280215b7ead7321844f23a42349e9095f394f028f0c731d43db471e39a008a79f5932cb709f2e48743f7a77bd3ee87f93fca8f33ff792daf289e7d4577299f52e0808222311df5e1bdf97c844daa0e9c62123c5df2f3ddc7f873052ee6ee282fa65a7a54d8b91c8e32f628b96c55bdc7db6a43ab156de528dd00e92a27e384addd02fb91cfff0bcf5a64f81738f82b49677661ffedc91af07f3e1ad62aef54daa9980ba4a221a5986c7c29d6867fa9e5db4ff9066da136afdd733b15a404d2dc195885ab0d82e1d1cc5cbae4011ce83146f079702813693483624c53c31775995e96b71def34a7d82c53b9bf4d7f612e89fdeb237f4df28e69f8cb8a3bc04c883915d3416a26c8e23c78efeb1ed658d0d6e7f95bc75c1652014084169569afac77c5b039d7db15c01a5b270b38a1e3d37fb1723a2de1b8ff0333d26ca5514473d3e98f815c894623779653018b7c7abcfe0cdc16f76b773332da2ef861cb7322aebbc64c880b7d805edd416cb231b18d910f0a93924f186fa490feb02ee5d21c17e76c526527951824f0c25dd658731c568d2beeb4619602362d3f07a5458399f68089dd196ef3db907730d48fa1fb4c5ea3dc6e9fc4493dc9662de0d87308be5cdbb8355e2b3a4c483d09ad71d97439950719cf88a6a0d1b616e0960ef2a320f4258a66f83d29f6a8d906427f4d94c0043bfd173a04c06c2e0e48e5c77d32578ee75bf553855f5daa8e376aca79b131bad85a0e78e224babd103ca84e7a3562610409b1c9b52b42548cd6c",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await Xchacha20poly1305Ietf.decrypt(
|
||||
makeCiphertext(
|
||||
"939c5fceb7dc8174eda71d2fc5188e0e20988f8bba94a4db318164c908c0a2c3043703d67540ed6b91b69c3426c8202268485257a787ca33a6b36e863895137f466cad52928574b28c9ab912de29538866b18deabf639d9cfa841e0486b7611b500254912a6026a3f4ccd49d6974ccf66961db8181ba060653a95c3e1276014629687f147053dce0309d19ad5c64dafa0a7233a8089d89b72422633fc40a723b48dfa9c3a2b89102386fa40daf99d1322ef6349d2f7e0163d397de6a3643fa31a418b6f2af870aaf31ebb390bb939d1bc10195c461e17911976296320129edfe641fbc6a105eef088ba2bf0fa6f2ed4cd1db1ac7513282920bfb80619df8526bea82b85ed9d8c6047378d7452245310c3d6657e17cfc7cdeaa50a194c4008be5b93056cd2fec31ecd88cb31fe3d1d018c80a3167caeca4db1e3dac33d4c000262cc8de7b870fa6d27c1d0917e573adccfa3f3e9f5a157dbd6b4b7132982e9e59d6d64b736ed7e24aa6a06b84a29e88bec41d2c782c439dd95f19bf3a357c88ebdf65c071820f25c0b2c9d8e69325e63d2136cda11e1138ce4ac5a8a134082e6f84afc264c9dd3f48c7db363d901e22de918a4a4278bd863a9658e99cd5b14ccde5e9f767cebd67c6acb72071cf340b980a7047b556d45fee093854fe449e3b660f678261f26b017a8d01008032622e3a978be3f83b9d203959f3bd9918d69fd83b9b6eace0e2d15f6187b2f89b5e381ce0bae59c7d91c7354003fbc903eb3b2ea74d9ab1de63ff4eb2c6f59a8826dd84e13de1508bf7ac5a358521026eee39c1e45a5bd889404da32f6cee415928e01b723cea9165a487073989adbd4ed139dc32fd9912f750bfc0f4d7113a24e2855218ec99d5de1c87276727134cd778989937c00c30c26e7b0c91f39aa1eed52089f55092bac70bd91b190ae573490713c00cc47915540a8e15e7fdbd02874d9f8de11e3d91b0add39b27cddbb91e9cc11958896d5f43cfaff9b672bd18af5260e28c48c12fde57d34f0e9df7199f514bac7e06d164fc96ca69170c5194f040ed14d45a07766e87afa919fe4f082e3d95c6e73c3fe26cf742f71152d9ac4a1e554830075b1812ab5acde59b98bcc1e1894935e7a1bd66bc7187a3d2555f10589d60dd93e5d997fe6ffe18fe787eeab5ccfb80c012c34e4fb4fb30387e0ba9d9e2d07876f3598b95b92ece64a0f3b4aa70fbf8415ffa195a076689947c5e539098856bab5950f0bccf839c283a5972e8e70000883ddcee167eb2c424b72a82753a7f4ef14bc66ba621331e37392fce0eae3d820059e0316b5b7f1cb46a2ef026a6a942745267918cccbd535af4bc3b1e14d9b543946840c824f21730a233d01cc438cb2ac5435135a627c77508433c8c657f87bfffea9bcefe9b8e21f4e7c64a39fe3766896f4e32829bee81c2d7f12464067b734245b5246814f986f35517fee6ec",
|
||||
),
|
||||
key,
|
||||
nonce,
|
||||
),
|
||||
).toEqual(
|
||||
fromHex(
|
||||
"09b7b8c14569057a7c4cdc611b839bda48123c1aba86a3e1ac6ca981c7a8c1885c9d95c661d3ff530aaf9f07f8233049ebe92297fb3a708352c59fa703087011117215142f10843b976862579be7cea3d8112d3ae69f58ed9d9684da5c51123a73e5b08627ef83b1f8feb3ef91ca8f1be327468e0cc2b3bffc1a8ef1291cedf80ff8320b90f0d17fb623c447e65f4fa48a17327d427d1aa6bb445c61dda9cc9b5c1611675e618fe6b79ab9bf045cfe0b1295aa72ff1c73d6641fb942a831506c0d268c628abff8925c011d222c443b73f18e994077f1c7a893123ed400cf2f11b8fa144c0d8fc5afcd2960281f067f1329cd4abf15a1a701762121b1e103db9538f989443fbc824d39fab22b622ec98632e957adbb39dd956f31b42af3629d8fcd461c9d4519105c0f308c7c45888583832c3c659b17b0733bc7257a9c00899a4ba9933c211579480e5c30d6837241a59d044280df466d55d0b46dcfa2db4c809023d77c2503ed3afd82489c98b0949baac0a00403af770e65c45607b615912eaf7c727b15cf976e742c1cb75fc160966dda4504edc7322a9479cb2617286c85b1412b9ffc067be3f9d2fd49568a29fe40115cf76de6dd7cc3f7e833bafbdffc39097150fe14960582a39d10102aa86718f59b102c89441806c80acfe300ea415be03162e729bd92a75b4e18a33470409034bc7fc7e9fb6c4823d6bfc77d75046c0927922dae805c7ced7a4a6fb4f46a15ee6b145e0dae56e6480ac726f7ccd5296fb2c82b5ebc2239eac2d81cea4fa789e0187134a270b6e74fc7932983e6e993e391ecc0dc9cb5963a644c6ece2eba6564533a330861eaac6120f84959e5b41ccbbe2255db17106a9955a94964ba07c5b1d1a0159f3f1e8f76f65181c17e3d616398df19fe6d8625922f5a0fd384b3238761a2003e5b5101c0c795c48ceb3ec72d9f7f5cf42df449075153f642f0ffb01414a5f6c7985855d752fd7c5a71d8eead5cf51681bb67adf267d5fb290933dff2aa25eff736616cb6437738b0121e4d14bdaf5e7e8009631ecb1f30bf574c7e3c6a79cd571dd08a38054cec4dce6fb7805a0fc8c729d75f1233bc85149e0bd1d13895855f71f2f57eda6101ed274c9e41468c28b97f5f969cc849ef5027c5afd57c24ae85e16e9659cd3954c9535d3218b20f191982df2dfad4fd13d0e33b24d83274ed6066814d70d41f84874c6072b9515aa5d2f0716d851a84533fd43f21c902fb54320e04fd0b7d05d169137482f82d46b275672a7c8aa51e4546ab494f0aa2dee67d5ea2476c0a7463af609727d52ac54054ee7f454f9420d5e9f5e28357c2b0745a466087d505919ae4923586649b5b3e845929c78ff4c22c6d5f8a31eb8eedb3351e6d5f9ce68dbb318a5d62bc116bdea32a77c46492a3ca52f06a00a14420daee72b97673ef6d4a3bc35a",
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
130
packages/crypto/src/libsodium.ts
Normal file
130
packages/crypto/src/libsodium.ts
Normal file
@ -0,0 +1,130 @@
|
||||
// Keep all classes requiring libsodium-js in one file as having multiple
|
||||
// requiring of the libsodium-wrappers module currently crashes browsers
|
||||
//
|
||||
// libsodium.js API: https://gist.github.com/webmaster128/b2dbe6d54d36dd168c9fabf441b9b09c
|
||||
|
||||
import sodium from "libsodium-wrappers";
|
||||
import { As } from "type-tagger";
|
||||
|
||||
export type Xchacha20poly1305IetfKey = Uint8Array & As<"xchacha20poly1305ietf-key">;
|
||||
export type Xchacha20poly1305IetfMessage = Uint8Array & As<"xchacha20poly1305ietf-message">;
|
||||
export type Xchacha20poly1305IetfNonce = Uint8Array & As<"xchacha20poly1305ietf-nonce">;
|
||||
export type Xchacha20poly1305IetfCiphertext = Uint8Array & As<"xchacha20poly1305ietf-ciphertext">;
|
||||
|
||||
export interface Argon2idOptions {
|
||||
// in bytes
|
||||
readonly outputLength: number;
|
||||
// integer between 1 and 4294967295
|
||||
readonly opsLimit: number;
|
||||
// memory limit measured in KiB (like argon2 command line tool)
|
||||
// Note: only ~ 16 MiB of memory are available using the non-sumo version of libsodium
|
||||
readonly memLimitKib: number;
|
||||
}
|
||||
|
||||
export class Argon2id {
|
||||
public static async execute(
|
||||
password: string,
|
||||
salt: Uint8Array,
|
||||
options: Argon2idOptions,
|
||||
): Promise<Uint8Array> {
|
||||
await sodium.ready;
|
||||
return sodium.crypto_pwhash(
|
||||
options.outputLength,
|
||||
password,
|
||||
salt, // libsodium only supports 16 byte salts and will throw when you don't respect that
|
||||
options.opsLimit,
|
||||
options.memLimitKib * 1024,
|
||||
sodium.crypto_pwhash_ALG_ARGON2ID13,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Ed25519Keypair {
|
||||
// a libsodium privkey has the format `<ed25519 privkey> + <ed25519 pubkey>`
|
||||
public static fromLibsodiumPrivkey(libsodiumPrivkey: Uint8Array): Ed25519Keypair {
|
||||
if (libsodiumPrivkey.length !== 64) {
|
||||
throw new Error(`Unexpected key length ${libsodiumPrivkey.length}. Must be 64.`);
|
||||
}
|
||||
return new Ed25519Keypair(libsodiumPrivkey.slice(0, 32), libsodiumPrivkey.slice(32, 64));
|
||||
}
|
||||
|
||||
public readonly privkey: Uint8Array;
|
||||
public readonly pubkey: Uint8Array;
|
||||
|
||||
public constructor(privkey: Uint8Array, pubkey: Uint8Array) {
|
||||
this.privkey = privkey;
|
||||
this.pubkey = pubkey;
|
||||
}
|
||||
|
||||
public toLibsodiumPrivkey(): Uint8Array {
|
||||
return new Uint8Array([...this.privkey, ...this.pubkey]);
|
||||
}
|
||||
}
|
||||
|
||||
export class Ed25519 {
|
||||
/**
|
||||
* Generates a keypair deterministically from a given 32 bytes seed.
|
||||
*
|
||||
* This seed equals the Ed25519 private key.
|
||||
* For implementation details see crypto_sign_seed_keypair in
|
||||
* https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures.html
|
||||
* and diagram on https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
|
||||
*/
|
||||
public static async makeKeypair(seed: Uint8Array): Promise<Ed25519Keypair> {
|
||||
await sodium.ready;
|
||||
const keypair = sodium.crypto_sign_seed_keypair(seed);
|
||||
return Ed25519Keypair.fromLibsodiumPrivkey(keypair.privateKey);
|
||||
}
|
||||
|
||||
public static async createSignature(message: Uint8Array, keyPair: Ed25519Keypair): Promise<Uint8Array> {
|
||||
await sodium.ready;
|
||||
return sodium.crypto_sign_detached(message, keyPair.toLibsodiumPrivkey());
|
||||
}
|
||||
|
||||
public static async verifySignature(
|
||||
signature: Uint8Array,
|
||||
message: Uint8Array,
|
||||
pubkey: Uint8Array,
|
||||
): Promise<boolean> {
|
||||
await sodium.ready;
|
||||
return sodium.crypto_sign_verify_detached(signature, message, pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
export class Xchacha20poly1305Ietf {
|
||||
public static async encrypt(
|
||||
message: Xchacha20poly1305IetfMessage,
|
||||
key: Xchacha20poly1305IetfKey,
|
||||
nonce: Xchacha20poly1305IetfNonce,
|
||||
): Promise<Xchacha20poly1305IetfCiphertext> {
|
||||
await sodium.ready;
|
||||
|
||||
const additionalData = null;
|
||||
|
||||
return sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
message,
|
||||
additionalData,
|
||||
null, // secret nonce: unused and should be null (https://download.libsodium.org/doc/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction)
|
||||
nonce,
|
||||
key,
|
||||
) as Xchacha20poly1305IetfCiphertext;
|
||||
}
|
||||
|
||||
public static async decrypt(
|
||||
ciphertext: Xchacha20poly1305IetfCiphertext,
|
||||
key: Xchacha20poly1305IetfKey,
|
||||
nonce: Xchacha20poly1305IetfNonce,
|
||||
): Promise<Xchacha20poly1305IetfMessage> {
|
||||
await sodium.ready;
|
||||
|
||||
const additionalData = null;
|
||||
|
||||
return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
null, // secret nonce: unused and should be null (https://download.libsodium.org/doc/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction)
|
||||
ciphertext,
|
||||
additionalData,
|
||||
nonce,
|
||||
key,
|
||||
) as Xchacha20poly1305IetfMessage;
|
||||
}
|
||||
}
|
||||
38
packages/crypto/src/random.spec.ts
Normal file
38
packages/crypto/src/random.spec.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { isUint8Array } from "@cosmjs/utils";
|
||||
|
||||
import { Random } from "./random";
|
||||
|
||||
describe("Random", () => {
|
||||
it("returns an Uint8Array", () => {
|
||||
const data = Random.getBytes(5);
|
||||
expect(isUint8Array(data)).toEqual(true);
|
||||
});
|
||||
|
||||
it("creates random bytes", () => {
|
||||
{
|
||||
const bytes = Random.getBytes(0);
|
||||
expect(bytes.length).toEqual(0);
|
||||
}
|
||||
|
||||
{
|
||||
const bytes = Random.getBytes(1);
|
||||
expect(bytes.length).toEqual(1);
|
||||
}
|
||||
|
||||
{
|
||||
const bytes = Random.getBytes(32);
|
||||
expect(bytes.length).toEqual(32);
|
||||
}
|
||||
|
||||
{
|
||||
const bytes = Random.getBytes(4096);
|
||||
expect(bytes.length).toEqual(4096);
|
||||
}
|
||||
|
||||
{
|
||||
const bytes1 = Random.getBytes(32);
|
||||
const bytes2 = Random.getBytes(32);
|
||||
expect(bytes1).not.toEqual(bytes2);
|
||||
}
|
||||
});
|
||||
});
|
||||
27
packages/crypto/src/random.ts
Normal file
27
packages/crypto/src/random.ts
Normal file
@ -0,0 +1,27 @@
|
||||
declare const window: any;
|
||||
declare const self: any;
|
||||
|
||||
export class Random {
|
||||
/**
|
||||
* Returns `count` cryptographically secure random bytes
|
||||
*/
|
||||
public static getBytes(count: number): Uint8Array {
|
||||
try {
|
||||
const globalObject = typeof window === "object" ? window : self;
|
||||
const cryptoApi =
|
||||
typeof globalObject.crypto !== "undefined" ? globalObject.crypto : globalObject.msCrypto;
|
||||
|
||||
const out = new Uint8Array(count);
|
||||
cryptoApi.getRandomValues(out);
|
||||
return out;
|
||||
} catch {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const crypto = require("crypto");
|
||||
return new Uint8Array([...crypto.randomBytes(count)]);
|
||||
} catch {
|
||||
throw new Error("No secure random number generator found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
packages/crypto/src/ripemd.spec.ts
Normal file
28
packages/crypto/src/ripemd.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Ripemd160 } from "./ripemd";
|
||||
import ripemdVectors from "./testdata/ripemd.json";
|
||||
|
||||
describe("Ripemd160", () => {
|
||||
it("exists", () => {
|
||||
expect(Ripemd160).toBeTruthy();
|
||||
});
|
||||
|
||||
it("works for empty input", () => {
|
||||
{
|
||||
const hash = new Ripemd160(new Uint8Array([])).digest();
|
||||
expect(hash).toEqual(fromHex("9c1185a5c5e9fc54612808977ee8f548b2258d31"));
|
||||
}
|
||||
{
|
||||
const hash = new Ripemd160().digest();
|
||||
expect(hash).toEqual(fromHex("9c1185a5c5e9fc54612808977ee8f548b2258d31"));
|
||||
}
|
||||
});
|
||||
|
||||
it("works for all the Botan test vectors", () => {
|
||||
// https://github.com/randombit/botan/blob/2.12.1/src/tests/data/hash/ripemd160.vec
|
||||
for (const { in: input, out: output } of ripemdVectors.ripemd160) {
|
||||
expect(new Ripemd160(fromHex(input)).digest()).toEqual(fromHex(output));
|
||||
}
|
||||
});
|
||||
});
|
||||
24
packages/crypto/src/ripemd.ts
Normal file
24
packages/crypto/src/ripemd.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import RIPEMD160 from "ripemd160";
|
||||
|
||||
import { HashFunction } from "./hash";
|
||||
|
||||
export class Ripemd160 implements HashFunction {
|
||||
public readonly blockSize = 512 / 8;
|
||||
|
||||
private readonly impl = new RIPEMD160();
|
||||
|
||||
public constructor(firstData?: Uint8Array) {
|
||||
if (firstData) {
|
||||
this.update(firstData);
|
||||
}
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Ripemd160 {
|
||||
this.impl.update(Buffer.from(data));
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
return Uint8Array.from(this.impl.digest());
|
||||
}
|
||||
}
|
||||
589
packages/crypto/src/secp256k1.spec.ts
Normal file
589
packages/crypto/src/secp256k1.spec.ts
Normal file
@ -0,0 +1,589 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Secp256k1 } from "./secp256k1";
|
||||
import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
import { Sha256 } from "./sha";
|
||||
|
||||
describe("Secp256k1", () => {
|
||||
// How to generate Secp256k1 test vectors:
|
||||
// $ git clone https://github.com/pyca/cryptography.git && cd cryptography
|
||||
// $ python2 -m virtualenv venv
|
||||
// $ source venv/bin/activate
|
||||
// $ pip install cryptography cryptography_vectors pytest ecdsa
|
||||
// $ curl https://patch-diff.githubusercontent.com/raw/webmaster128/cryptography/pull/1.diff | git apply
|
||||
//
|
||||
// optionally normalize signatures to lowS representation:
|
||||
// $ curl https://patch-diff.githubusercontent.com/raw/webmaster128/cryptography/pull/2.diff | git apply
|
||||
//
|
||||
// $ python ./docs/development/custom-vectors/secp256k1/generate_secp256k1.py > secp256k1_test_vectors.txt
|
||||
|
||||
it("encodes public key uncompressed when making a keypair", async () => {
|
||||
// example data generated by OpenSSL (caution: LibreSSL 2.2.7 sometimes adds a leading 00 for the privkey):
|
||||
// openssl ecparam -name secp256k1 -genkey | openssl ec -text -noout -conv_form uncompressed
|
||||
const privkey = fromHex("5b1d5975dfdfb0027802265241d891e4af744cd39e78595658afaa7ac801d1d3");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
expect(keypair.pubkey).toEqual(
|
||||
fromHex(
|
||||
"043e1113575cf01f9281381f626deccf76cdc85a320052297f7cae3548ea024d0b9fbec8a56d345a2984050be859c96471ef6aa4669ff31a659ce1d32db372f9b9",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves private key when making a keypair", async () => {
|
||||
const privkey = fromHex("8c8bc2bc7954db5ef751e3e84b4e99bbe387a90d8019d8066c0e1e8bf33e713f");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
expect(keypair.privkey).toEqual(
|
||||
fromHex("8c8bc2bc7954db5ef751e3e84b4e99bbe387a90d8019d8066c0e1e8bf33e713f"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can load private keys", async () => {
|
||||
expect(
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("5eaf4344dab73d0caee1fd03607bb969074fb217f076896c2125f8607feab7b1"),
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("f7ac570ea2844e29e7f3b3c6a724ee1f47d3de8c2175a69abae94ae871573d0e"),
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("e4ade2a5232a7c6f37e7b854a774e25e6047ee7c6d63e8304ae04fa190bc1732"),
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
// smallest and largest allowed values: 1 and N-1 (from https://crypto.stackexchange.com/a/30273)
|
||||
expect(
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140"),
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
// too short and too long
|
||||
await Secp256k1.makeKeypair(fromHex("e4ade2a5232a7c6f37e7b854a774e25e6047ee7c6d63e8304ae04fa190bc17"))
|
||||
.then(() => fail("promise must be rejected"))
|
||||
.catch((error) => expect(error.message).toContain("not a valid secp256k1 private key"));
|
||||
await Secp256k1.makeKeypair(fromHex("e4ade2a5232a7c6f37e7b854a774e25e6047ee7c6d63e8304ae04fa190bc1732aa"))
|
||||
.then(() => fail("promise must be rejected"))
|
||||
.catch((error) => expect(error.message).toContain("not a valid secp256k1 private key"));
|
||||
// value out of range (too small)
|
||||
await Secp256k1.makeKeypair(fromHex("0000000000000000000000000000000000000000000000000000000000000000"))
|
||||
.then(() => fail("promise must be rejected"))
|
||||
.catch((error) => expect(error.message).toContain("not a valid secp256k1 private key"));
|
||||
// value out of range (>= n)
|
||||
await Secp256k1.makeKeypair(fromHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
.then(() => fail("promise must be rejected"))
|
||||
.catch((error) => expect(error.message).toContain("not a valid secp256k1 private key"));
|
||||
await Secp256k1.makeKeypair(fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"))
|
||||
.then(() => fail("promise must be rejected"))
|
||||
.catch((error) => expect(error.message).toContain("not a valid secp256k1 private key"));
|
||||
});
|
||||
|
||||
it("creates signatures", async () => {
|
||||
const privkey = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
const messageHash = new Uint8Array([0x11, 0x22]);
|
||||
const signature = (await Secp256k1.createSignature(messageHash, keypair.privkey)).toDer();
|
||||
expect(signature).toBeTruthy();
|
||||
expect(signature.byteLength).toBeGreaterThanOrEqual(70);
|
||||
expect(signature.byteLength).toBeLessThanOrEqual(72);
|
||||
});
|
||||
|
||||
it("creates signatures deterministically", async () => {
|
||||
const privkey = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
const messageHash = new Uint8Array([0x11, 0x22]);
|
||||
|
||||
const signature1 = await Secp256k1.createSignature(messageHash, keypair.privkey);
|
||||
const signature2 = await Secp256k1.createSignature(messageHash, keypair.privkey);
|
||||
expect(signature1).toEqual(signature2);
|
||||
});
|
||||
|
||||
it("throws for empty message hash in signing", async () => {
|
||||
const privkey = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
const messageHash = new Uint8Array([]);
|
||||
await Secp256k1.createSignature(messageHash, keypair.privkey)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch((error) => expect(error).toMatch(/message hash must not be empty/i));
|
||||
});
|
||||
|
||||
it("throws for message hash longer than 32 bytes in signing", async () => {
|
||||
const privkey = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
const messageHash = fromHex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff11");
|
||||
await Secp256k1.createSignature(messageHash, keypair.privkey)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch((error) => expect(error).toMatch(/message hash length must not exceed 32 bytes/i));
|
||||
});
|
||||
|
||||
it("verifies signatures", async () => {
|
||||
const privkey = fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9");
|
||||
const keypair = await Secp256k1.makeKeypair(privkey);
|
||||
const messageHash = new Uint8Array([0x11, 0x22]);
|
||||
const signature = await Secp256k1.createSignature(messageHash, keypair.privkey);
|
||||
|
||||
{
|
||||
// valid
|
||||
const ok = await Secp256k1.verifySignature(signature, messageHash, keypair.pubkey);
|
||||
expect(ok).toEqual(true);
|
||||
}
|
||||
|
||||
{
|
||||
// messageHash corrupted
|
||||
const corruptedMessageHash = messageHash.map((x, i) => (i === 0 ? x ^ 0x01 : x));
|
||||
const ok = await Secp256k1.verifySignature(signature, corruptedMessageHash, keypair.pubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
|
||||
{
|
||||
// signature corrupted
|
||||
const corruptedSignature = Secp256k1Signature.fromDer(
|
||||
signature.toDer().map((x, i) => (i === 5 ? x ^ 0x01 : x)),
|
||||
);
|
||||
const ok = await Secp256k1.verifySignature(corruptedSignature, messageHash, keypair.pubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
|
||||
{
|
||||
// wrong pubkey
|
||||
const otherPrivkey = fromHex("91099374790843e29552c3cfa5e9286d6c77e00a2c109aaf3d0a307081314a09");
|
||||
const wrongPubkey = (await Secp256k1.makeKeypair(otherPrivkey)).pubkey;
|
||||
const ok = await Secp256k1.verifySignature(signature, messageHash, wrongPubkey);
|
||||
expect(ok).toEqual(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws for empty message hash in verification", async () => {
|
||||
const dummySignature = Secp256k1Signature.fromDer(
|
||||
fromHex(
|
||||
"304602210083de9be443bcf480892b8c8ca1d5ee65c79a315642c3f7b5305aff3065fda2780221009747932122b93cec42cad8ee4630a8f6cbe127578b8c495b4ab927275f657658",
|
||||
),
|
||||
);
|
||||
const keypair = await Secp256k1.makeKeypair(
|
||||
fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9"),
|
||||
);
|
||||
const messageHash = new Uint8Array([]);
|
||||
await Secp256k1.verifySignature(dummySignature, messageHash, keypair.pubkey)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch((error) => expect(error).toMatch(/message hash must not be empty/i));
|
||||
});
|
||||
|
||||
it("throws for message hash longer than 32 bytes in verification", async () => {
|
||||
const dummySignature = Secp256k1Signature.fromDer(
|
||||
fromHex(
|
||||
"304602210083de9be443bcf480892b8c8ca1d5ee65c79a315642c3f7b5305aff3065fda2780221009747932122b93cec42cad8ee4630a8f6cbe127578b8c495b4ab927275f657658",
|
||||
),
|
||||
);
|
||||
const keypair = await Secp256k1.makeKeypair(
|
||||
fromHex("43a9c17ccbb0e767ea29ce1f10813afde5f1e0a7a504e89b4d2cc2b952b8e0b9"),
|
||||
);
|
||||
const messageHash = fromHex("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff11");
|
||||
await Secp256k1.verifySignature(dummySignature, messageHash, keypair.privkey)
|
||||
.then(() => fail("must not resolve"))
|
||||
.catch((error) => expect(error).toMatch(/message hash length must not exceed 32 bytes/i));
|
||||
});
|
||||
|
||||
it("verifies unnormalized pyca/cryptography signatures", async () => {
|
||||
// signatures are mixed lowS and non-lowS, prehash type is sha256
|
||||
const data: readonly {
|
||||
readonly message: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
readonly signature: Uint8Array;
|
||||
}[] = [
|
||||
{
|
||||
message: fromHex(
|
||||
"5c868fedb8026979ebd26f1ba07c27eedf4ff6d10443505a96ecaf21ba8c4f0937b3cd23ffdc3dd429d4cd1905fb8dbcceeff1350020e18b58d2ba70887baa3a9b783ad30d3fbf210331cdd7df8d77defa398cdacdfc2e359c7ba4cae46bb74401deb417f8b912a1aa966aeeba9c39c7dd22479ae2b30719dca2f2206c5eb4b7",
|
||||
),
|
||||
privkey: fromHex("21142a7e90031ea750c9fa1ba1beae16782386be438133bd43195826ae2e25f0"),
|
||||
signature: fromHex(
|
||||
"30440220207082eb2c3dfa0b454e0906051270ba4074ac93760ba9e7110cd94714751111022051eb0dbbc9920e72146fb564f99d039802bf6ef2561446eb126ef364d21ee9c4",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"17cd4a74d724d55355b6fb2b0759ca095298e3fd1856b87ca1cb2df5409058022736d21be071d820b16dfc441be97fbcea5df787edc886e759475469e2128b22f26b82ca993be6695ab190e673285d561d3b6d42fcc1edd6d12db12dcda0823e9d6079e7bc5ff54cd452dad308d52a15ce9c7edd6ef3dad6a27becd8e001e80f",
|
||||
),
|
||||
privkey: fromHex("824282b6069fe3df857ce37204df4312c35750ee7a0f5e5fd8181666d5e46fb2"),
|
||||
signature: fromHex(
|
||||
"30440220626d61b7be1488b563e8a85bfb623b2331903964b5c0476c9f9ad29144f076fe02202002a2c0ab5e48626bf761cf677dfeede9c7309d2436d4b8c2b89f21ee2ebc6a",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"db0d31717b04802adbbae1997487da8773440923c09b869e12a57c36dda34af11b8897f266cd81c02a762c6b74ea6aaf45aaa3c52867eb8f270f5092a36b498f88b65b2ebda24afe675da6f25379d1e194d093e7a2f66e450568dbdffebff97c4597a00c96a5be9ba26deefcca8761c1354429622c8db269d6a0ec0cc7a8585c",
|
||||
),
|
||||
privkey: fromHex("5c0da42cec87a6b7a173514965343c30013386c0fe9b39203ed7af43ea425944"),
|
||||
signature: fromHex(
|
||||
"304602210083de9be443bcf480892b8c8ca1d5ee65c79a315642c3f7b5305aff3065fda2780221009747932122b93cec42cad8ee4630a8f6cbe127578b8c495b4ab927275f657658",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"47c9deddfd8c841a63a99be96e972e40fa035ae10d929babfc86c437b9d5d495577a45b7f8a35ce3f880e7d8ae8cd8eb685cf41f0506e68046ccf5559232c674abb9c3683829dcc8002683c4f4ca3a29a7bfde20d96dd0f1a0ead847dea18f297f220f94932536ca4deacedc2c6701c3ee50e28e358dcc54cdbf69daf0eb87f6",
|
||||
),
|
||||
privkey: fromHex("ab30a326599165b48c65ab8d3c77d312d7b2ea4853f721e18cc278628a866980"),
|
||||
signature: fromHex(
|
||||
"30440220723da69da81c8f6b081a9a728b9bba785d2067e0ed769675f8a7563d22ed8a1602203a993793cf39b96b3cd625df0e06f206e17579cd8ebcb7e704174c3d94dba684",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"f15433188c2bbc93b2150bb2f34d36ba8ae49f8f7e4e81aed651c8fb2022b2a7e851c4dbbbc21c14e27380316bfdebb0a049246349537dba687581c1344e40f75afd2735bb21ea074861de6801d28b22e8beb76fdd25598812b2061ca3fba229daf59a4ab416704543b02e16b8136c22acc7e197748ae19b5cbbc160fdc3a8cd",
|
||||
),
|
||||
privkey: fromHex("1560b9fa5229f623a9c556132da4fc0e58633f39ce6421d25b5a6cde4ad7e019"),
|
||||
signature: fromHex(
|
||||
"304502200e0c5228e6783bee4d0406f4f7b7d79f705f0dbb55126966f79e631bd8b23079022100faae33aec5b0fafd3413c14bfdef9c7c9ac6abd06c923c48ab136a2c56826118",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"1bc796124b87793b7f7fdd53b896f8f0d0f2d2be36d1944e3c2a0ac5c6b2839f59a4b4fad200f8035ec98630c51ef0d40863a5ddd69b703d73f06b4afae8ad1a88e19b1b26e8c10b7bff953c05eccc82fd771b220910165f3a906b7c931683e431998d1fd06c32dd11b4f872bf980d547942f22124c7e50c9523321aee23a36d",
|
||||
),
|
||||
privkey: fromHex("42f7d48e1c90f3d20252713f7c7c6ce8492c2b99bcef198927de334cda6bad00"),
|
||||
signature: fromHex(
|
||||
"3046022100b9d3962edadc893f8eeff379f136c7b8fc6ea824a5afc6cbda7e3cb4c7a1e860022100bb1c1f901cf450edfdce20686352bb0cf0a643301123140ec87c92480d7f9d6a",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"18e55ac264031da435b613fc9dc6c4aafc49aae8ddf6f220d523415896ff915fae5c5b2e6aed61d88e5721823f089c46173afc5d9b47fd917834c85284f62dda6ed2d7a6ff10eb553b9312b05dad7decf7f73b69479c02f14ea0a2aa9e05ec07396cd37c28795c90e590631137102315635d702278e352aa41d0826adadff5e1",
|
||||
),
|
||||
privkey: fromHex("6fe5b986c18da5d4fbcea6f602f469ac039085247ccb97b6292992363ea1d21c"),
|
||||
signature: fromHex(
|
||||
"30460221009369ab86afae5e22ed5f4012964804d2a19c36b8b58cf2855205b1cfcc937422022100a27dfc38d899b78edcf38a1b2b53578e72270b083d7d69424c4b4a7d25d39f4d",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"a5290666c97294d090f8da898e555cbd33990579e5e95498444bfb318b4aa1643e0d4348425e21c7c6f99f9955f3048f56c22b68c4a516af5c90ed5268acc9c5a20fec0200c2a282a90e20d3c46d4ecdda18ba18b803b19263de2b79238da921707a0864799cdee9f02913b40681c02c6923070688844b58fe415b7d71ea6845",
|
||||
),
|
||||
privkey: fromHex("fbf02ff086b215d057130a509346b64eb63bec0e38db692e07ad24c6ca8fe210"),
|
||||
signature: fromHex(
|
||||
"3045022100c5e439cef76b28dc0fe9d260763bec05b5e795ac8d90b25d9fccbc1918bc32f302201b06144e6b191224d5eda822a5b3b2026af6aa7f25a9061c9e81c312728aa94a",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"13ad0600229c2a66b2f11617f69c7210ad044c49265dc98ec3c64f56e56a083234d277d404e2c40523c414ad23af5cc2f91a47fe59e7ca572f7fe1d3d3cfceaedadac4396749a292a38e92727273272335f12b2acea21cf069682e67d7e7d7a31ab5bb8e472298a9451aeae6f160f36e6623c9b632b9c93371a002818addc243",
|
||||
),
|
||||
privkey: fromHex("474a7dc7f5033b6bf5e3027254cd0dbd956f16f61874b2992839a867f607d0dd"),
|
||||
signature: fromHex(
|
||||
"3045022100ee8615a5fab6fc674e6d3d9cde8da2b18dece076ae94d96662e16109db12d72002203171705cdab2b3d34c58e556c80358c105807e98243f5754b70b771071308b94",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"51ad843da5eafc177d49a50a82609555e52773c5dfa14d7c02db5879c11a6b6e2e0860df38452dc579d763f91a83ade23b73f4fcbd703f35dd6ecfbb4c9578d5b604ed809c8633e6ac5679a5f742ce94fea3b97b5ba8a29ea28101a7b35f9eaa894dda54e3431f2464d18faf8342b7c59dfe0598c0ab29a14622a08eea70126b",
|
||||
),
|
||||
privkey: fromHex("e8a2939a46e6bb7e706e419c0101d39f0494935b17fe3ca907b2ea3558d6ab3a"),
|
||||
signature: fromHex(
|
||||
"3046022100f753c447161aa3a58e5deeca31797f21484fb0ec3a7fe6e464ab1914896f253b02210099640fbcce1f25fd66744b046e0dfd57fa23070555f438af6c5e5828d47e9fa7",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"678b505467d55ce01aec23fd4851957137c3a1de3ff2c673ec95a577aa9fb011b4b4a8eb7a0e6f391d4236a35b7e769692ace5851d7c53700e180fa522d3d37dbaa496163f3de6d96391e38ff83271e621f2458729ff74de462cdce6b3029f308d4eb8aef036357b9de06d68558e0388a6e88af91340c875050b8c91c4e26fc8",
|
||||
),
|
||||
privkey: fromHex("08ce8f7118eda55b008f6eb3281a445a3ddbc5209d5ac16c09dbf40fe4bbc22c"),
|
||||
signature: fromHex(
|
||||
"30440220439fd0423bde36a1616a6fa4343bb7e07a6b3f6dc629aa8c93c91831055e476c022020998a26ae4b96ef36d48d83e8af0288f0bbc2db5ca5c8271a42f3fdc478fcb2",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"9bf457159f0d44b78d0e151ee53c41cecd98fb4e4129fcda8cc84a758636f84dcad9032f3ec422219d8a7ec61ea89f45d19cab3c3d451de1a634e3d2532231bc03031973d7150cf8e83d8b6a34f25fc136446878e3851b780abdca069c8e981b3ea3f1bf1ff6e47a03f97aed64c1cc90dd00389fa21bb973f142af5e8ceccef4",
|
||||
),
|
||||
privkey: fromHex("820be5c5e14e802300ca024fce318910f00470f6c3eabb12e7f3fac9383cf247"),
|
||||
signature: fromHex(
|
||||
"304502204ce72a83cf1d148db4d1e46e2f773c677f72933c40d7100b9192750a1c8222a80221009d5fbd67ce89ba8c79df9dc3b42922026a8498921c2bdb4ea8f36496d88c2cfb",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"2469172b7a046e6112dfe365590dfddb7c045cccd4ab353edc3076091aad1c780a9a73ff93f3dbf9e2189c5d1fdd6f6167d0ae8cc0f53dc8950e60dd0410e23589999d4ce4fa49e268774defd4edce01c05b205014b63591a041745bfffc6ae4d72d3add353e49478106653cc735b07b0fe665c42d0e6766e525bb9718264c87",
|
||||
),
|
||||
privkey: fromHex("d92d6170e63bc33647e6dcdf1981771ecd57e11d47d73138696fbf49a430c3ab"),
|
||||
signature: fromHex(
|
||||
"304502201f1e1fb673e9a7dee09961c6824b473189904deb4f0d8e28da51f77f4de2efe6022100ae8df1fcdb226fac8b46e494720e45f6d9a5350174faaf22e47b6329ee6c5e1b",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"6f8983e74f304c3657cffde0682b42699cb2c3475b925058ff37292c40a0aa296690ad129730339ac60cf784225b2fd3db58297c8ce5889df7a48d3e74a363ae4135e8a234cab53ca4c11c031d561a6cf7dd47b925ed5bc4c2794ba7b74a868b0c3da31ff1e4540d0768612192a236d86f74fb8c73f375b71c62f1648c0e6126",
|
||||
),
|
||||
privkey: fromHex("a70eb435feaeb6ccda7d3ebd3c4ae40b60643bc933f37ad1aca41dd086e8ae50"),
|
||||
signature: fromHex(
|
||||
"30460221009cf7d941dcbbbe61c2a6f5112cb518094e79e5d203891de2247e75fd532c3f21022100fc5a04579b2526f2543efd2a57e82b647da08b6924bff39cf021398a56ad70de",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"6fbe6f0f178fdc8a3ad1a8eecb02d37108c5831281fe85e3ff8eeb66ca1082a217b6d602439948f828e140140412544f994da75b6efc203b295235deca060ecfc7b71f05e5af2acc564596772ddbfb4078b4665f6b85f4e70641af26e31f6a14e5c88604459df4eeeed9b77b33c4b82a3c1458bd2fd1dc7214c04f9c79c8f09b",
|
||||
),
|
||||
privkey: fromHex("34a677d6f0c132eeffc3451b61e5d55969399699019ac929e6fdb5215d37be5e"),
|
||||
signature: fromHex(
|
||||
"3045022059cd6c2a30227afbd693d87b201d0989435d6e116c144276a5223466a822c0f2022100b01495efda969b3fd3a2c05aa098a4e04b0d0e748726fc6174627da15b143799",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"2b49de971bb0f705a3fb5914eb7638d72884a6c3550667dbfdf301adf26bde02f387fd426a31be6c9ff8bfe8690c8113c88576427f1466508458349fc86036afcfb66448b947707e791e71f558b2bf4e7e7507773aaf4e9af51eda95cbce0a0f752b216f8a54a045d47801ff410ee411a1b66a516f278327df2462fb5619470e",
|
||||
),
|
||||
privkey: fromHex("2258cdecaf3510bc398d08c000245cadceadcf149022730d93b176b4844713e1"),
|
||||
signature: fromHex(
|
||||
"30460221009eaf69170aeba44966afe957295526ee9852b5034c18dc5aeef3255c8567838a022100ebd4c8de2c22b5cb8803d6e070186786f6d5dae2202b9f899276fa31a66cb3bb",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"1fa7201d96ad4d190415f2656d1387fa886afc38e5cd18b8c60da367acf32c627d2c9ea19ef3f030e559fc2a21695cdbb65ddf6ba36a70af0d3fa292a32de31da6acc6108ab2be8bd37843338f0c37c2d62648d3d49013edeb9e179dadf78bf885f95e712fcdfcc8a172e47c09ab159f3a00ed7b930f628c3c48257e92fc7407",
|
||||
),
|
||||
privkey: fromHex("a67cf8cead99827c7956327aa04ab30cfd2d67f21b78f28a35694ece51052a61"),
|
||||
signature: fromHex(
|
||||
"304402210091058d1b912514940e1002855cc930c01a21234bad88f607f213af495c32b69f021f5d387ce3de25f1b9bad1fb180de110686d91b461ae2972fa4e4a7018519870",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"74715fe10748a5b98b138f390f7ca9629c584c5d6ad268fc455c8de2e800b73fa1ea9aaee85de58baa2ce9ce68d822fc31842c6b153baef3a12bf6b4541f74af65430ae931a64c8b4950ad1c76b31aea8c229b3623390e233c112586aa5907bbe419841f54f0a7d6d19c003b91dc84bbb59b14ec477a1e9d194c137e21c75bbb",
|
||||
),
|
||||
privkey: fromHex("4f1050422c4fce146bab0d735a70a91d6447210964b064309f90315c986be400"),
|
||||
signature: fromHex(
|
||||
"3046022100fe43eb9c38b506d118e20f8605ac8954fc0406efd306ba7ea5b07577a2735d15022100d589e91bf5014c7c360342ad135259dd7ae684e2c21234d7a912b43d148fcf19",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"d10131982dd1a1d839aba383cd72855bf41061c0cb04dfa1acad3181f240341d744ca6002b52f25fb3c63f16d050c4a4ef2c0ebf5f16ce987558f4b9d4a5ad3c6b81b617de00e04ba32282d8bf223bfedbb325b741dfdc8f56fa85c65d42f05f6a1330d8cc6664ad32050dd7b9e3993f4d6c91e5e12cbd9e82196e009ad22560",
|
||||
),
|
||||
privkey: fromHex("79506f5f68941c60a0d7c62595652a5f42f2b9f5aa2b6456af1c56a79a346c2f"),
|
||||
signature: fromHex(
|
||||
"3046022100ccdbbd2500043bf7f705536d5984ab5f05fdc0fa3cf464d8c88f861e3fc8e54c022100d5c6342c08dcd8242e1daf3595cae968e320a025aa45ec4bc725795da3d1becb",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"ef9dbd90ded96ad627a0a987ab90537a3e7acc1fdfa991088e9d999fd726e3ce1e1bd89a7df08d8c2bf51085254c89dc67bc21e8a1a93f33a38c18c0ce3880e958ac3e3dbe8aec49f981821c4ac6812dd29fab3a9ebe7fbd799fb50f12021b48d1d9abca8842547b3b99befa612cc8b4ca5f9412e0352e72ab1344a0ac2913db",
|
||||
),
|
||||
privkey: fromHex("4c53b8e372f70593afb08fb0f3ba228e1bd2430f562414e9bd1b89e53becbac8"),
|
||||
signature: fromHex(
|
||||
"304402205c707b6df7667324f950216b933d28e307a0223b24d161bc5887208d7f880b3a02204b7bc56586dc51d806ac3ad72807bc62d1d06d0812f121bd91e9770d84885c39",
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
for (const [index, row] of data.entries()) {
|
||||
const pubkey = (await Secp256k1.makeKeypair(row.privkey)).pubkey;
|
||||
const messageHash = new Sha256(row.message).digest();
|
||||
const isValid = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromDer(row.signature),
|
||||
messageHash,
|
||||
pubkey,
|
||||
);
|
||||
expect(isValid).withContext(`(index ${index})`).toEqual(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("matches normalized pyca/cryptography signatures", async () => {
|
||||
// signatures are normalized to lowS, prehash type is sha256
|
||||
const data: readonly {
|
||||
readonly message: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
readonly signature: Uint8Array;
|
||||
}[] = [
|
||||
{
|
||||
message: fromHex(
|
||||
"5c868fedb8026979ebd26f1ba07c27eedf4ff6d10443505a96ecaf21ba8c4f0937b3cd23ffdc3dd429d4cd1905fb8dbcceeff1350020e18b58d2ba70887baa3a9b783ad30d3fbf210331cdd7df8d77defa398cdacdfc2e359c7ba4cae46bb74401deb417f8b912a1aa966aeeba9c39c7dd22479ae2b30719dca2f2206c5eb4b7",
|
||||
),
|
||||
privkey: fromHex("1812bcfaa7566ba0724846d47dd4cc39306a506382cba33710ce6abd4d86553c"),
|
||||
signature: fromHex(
|
||||
"3044022045c0b7f8c09a9e1f1cea0c25785594427b6bf8f9f878a8af0b1abbb48e16d09202200d8becd0c220f67c51217eecfd7184ef0732481c843857e6bc7fc095c4f6b788",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"17cd4a74d724d55355b6fb2b0759ca095298e3fd1856b87ca1cb2df5409058022736d21be071d820b16dfc441be97fbcea5df787edc886e759475469e2128b22f26b82ca993be6695ab190e673285d561d3b6d42fcc1edd6d12db12dcda0823e9d6079e7bc5ff54cd452dad308d52a15ce9c7edd6ef3dad6a27becd8e001e80f",
|
||||
),
|
||||
privkey: fromHex("1aea57ea357cecc13b876b76a1825f433ff603d76e6794fdb55aeda481b9482b"),
|
||||
signature: fromHex(
|
||||
"304402204e0ea79d4a476276e4b067facdec7460d2c98c8a65326a6e5c998fd7c650611402200e45aea5034af973410e65cf97651b3f2b976e3fc79c6a93065ed7cb69a2ab5a",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"db0d31717b04802adbbae1997487da8773440923c09b869e12a57c36dda34af11b8897f266cd81c02a762c6b74ea6aaf45aaa3c52867eb8f270f5092a36b498f88b65b2ebda24afe675da6f25379d1e194d093e7a2f66e450568dbdffebff97c4597a00c96a5be9ba26deefcca8761c1354429622c8db269d6a0ec0cc7a8585c",
|
||||
),
|
||||
privkey: fromHex("03708999fddd22091e93a8fd6b2205b662089a97507623cb5ce04240bcae55b8"),
|
||||
signature: fromHex(
|
||||
"3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"47c9deddfd8c841a63a99be96e972e40fa035ae10d929babfc86c437b9d5d495577a45b7f8a35ce3f880e7d8ae8cd8eb685cf41f0506e68046ccf5559232c674abb9c3683829dcc8002683c4f4ca3a29a7bfde20d96dd0f1a0ead847dea18f297f220f94932536ca4deacedc2c6701c3ee50e28e358dcc54cdbf69daf0eb87f6",
|
||||
),
|
||||
privkey: fromHex("44da7ab9eab17b93175bf4d5388c6b334f35a3283215b9e602a264d2e831fea3"),
|
||||
signature: fromHex(
|
||||
"3045022100f2cab57d108aaf7c9c9dd061404447d59f968d1468b25dd827d624b64601c32a022077558dbf7bf90885b9128c371959085e9dd1b7d8a5c45b7265e8e7d9f125c008",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"f15433188c2bbc93b2150bb2f34d36ba8ae49f8f7e4e81aed651c8fb2022b2a7e851c4dbbbc21c14e27380316bfdebb0a049246349537dba687581c1344e40f75afd2735bb21ea074861de6801d28b22e8beb76fdd25598812b2061ca3fba229daf59a4ab416704543b02e16b8136c22acc7e197748ae19b5cbbc160fdc3a8cd",
|
||||
),
|
||||
privkey: fromHex("5452b1bf1b1d96929f7ee3637a1bca637490f97634e6d164aeddda3dbb243a26"),
|
||||
signature: fromHex(
|
||||
"3045022100d702bec0f058f5e18f5fcdb204f79250562f11121f5513ae1006c9b93ddafb11022063de551c508405a280a21fb007b660542b58fcd3256b7cea45e3f2ebe9a29ecd",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"1bc796124b87793b7f7fdd53b896f8f0d0f2d2be36d1944e3c2a0ac5c6b2839f59a4b4fad200f8035ec98630c51ef0d40863a5ddd69b703d73f06b4afae8ad1a88e19b1b26e8c10b7bff953c05eccc82fd771b220910165f3a906b7c931683e431998d1fd06c32dd11b4f872bf980d547942f22124c7e50c9523321aee23a36d",
|
||||
),
|
||||
privkey: fromHex("a4095a3d464d20ea154f4312c087bd22c8a92207717cca40b1f3267e13cbf05c"),
|
||||
signature: fromHex(
|
||||
"3045022100ae17ab6a3bd2ccd0901cc3904103e825895540bf416a5f717b74b529512e4c1802204bc049a8a2287cfccea77fb3769755ba92c35154c635448cf633244edf4f6fe1",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"18e55ac264031da435b613fc9dc6c4aafc49aae8ddf6f220d523415896ff915fae5c5b2e6aed61d88e5721823f089c46173afc5d9b47fd917834c85284f62dda6ed2d7a6ff10eb553b9312b05dad7decf7f73b69479c02f14ea0a2aa9e05ec07396cd37c28795c90e590631137102315635d702278e352aa41d0826adadff5e1",
|
||||
),
|
||||
privkey: fromHex("4babce8321dcd2b5e3ac936e278519fb4b9be96688bbefb2e87d53b863a349b8"),
|
||||
signature: fromHex(
|
||||
"3044022003b51d02eac41f2969fc36c816c9772da21a139376b09d1c8809bb8f543be62f02200629c1396ae304d2c2e7b63890d91e56dfc3459f4d664cb914c7ff2a12a21925",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"a5290666c97294d090f8da898e555cbd33990579e5e95498444bfb318b4aa1643e0d4348425e21c7c6f99f9955f3048f56c22b68c4a516af5c90ed5268acc9c5a20fec0200c2a282a90e20d3c46d4ecdda18ba18b803b19263de2b79238da921707a0864799cdee9f02913b40681c02c6923070688844b58fe415b7d71ea6845",
|
||||
),
|
||||
privkey: fromHex("02f98a6eb5320fc0c65f3eb2911b8500bedf47230bdfa2ba5e1c0c1c0bf9fc0a"),
|
||||
signature: fromHex(
|
||||
"30440220400f52f4c4925b4b8886706331535230fafb6455c3a3eef6fbf19a82593812300220727cc4b3341d7d95d0dc404d910dc009b3b5f21baadc0c4ee199a46e558d7f56",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"13ad0600229c2a66b2f11617f69c7210ad044c49265dc98ec3c64f56e56a083234d277d404e2c40523c414ad23af5cc2f91a47fe59e7ca572f7fe1d3d3cfceaedadac4396749a292a38e92727273272335f12b2acea21cf069682e67d7e7d7a31ab5bb8e472298a9451aeae6f160f36e6623c9b632b9c93371a002818addc243",
|
||||
),
|
||||
privkey: fromHex("f2a08a52b0edbaa64dacb3244666d4fdb684e6cc995bed81ec9d86c58f9999de"),
|
||||
signature: fromHex(
|
||||
"3045022100b2927afc8856b7e14d02e01e7aa3c76951a4621bfde5d794adda165b51dbe198022006eee6e0b087143ed06933cba699fbe4097ba7d7b038b173cbbd183718a86d43",
|
||||
),
|
||||
},
|
||||
{
|
||||
message: fromHex(
|
||||
"51ad843da5eafc177d49a50a82609555e52773c5dfa14d7c02db5879c11a6b6e2e0860df38452dc579d763f91a83ade23b73f4fcbd703f35dd6ecfbb4c9578d5b604ed809c8633e6ac5679a5f742ce94fea3b97b5ba8a29ea28101a7b35f9eaa894dda54e3431f2464d18faf8342b7c59dfe0598c0ab29a14622a08eea70126b",
|
||||
),
|
||||
privkey: fromHex("e27f10d444fa50b53283863941619b11d585b27018b0e29301371371b45e3c65"),
|
||||
signature: fromHex(
|
||||
"3044022100fe9717965673fbe585780e18d892a3cfa77b59ac2f44f5337a3e58ce6ecd4409021f155459b19d2e9a2e676d7d8d48a9303391ffb9befdd3a57324306d69e0e0ab",
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
for (const [index, row] of data.entries()) {
|
||||
const keypair = await Secp256k1.makeKeypair(row.privkey);
|
||||
const messageHash = new Sha256(row.message).digest();
|
||||
|
||||
// create signature
|
||||
const calculatedSignature = await Secp256k1.createSignature(messageHash, row.privkey);
|
||||
|
||||
// verify calculated signature
|
||||
const ok1 = await Secp256k1.verifySignature(calculatedSignature, messageHash, keypair.pubkey);
|
||||
expect(ok1).withContext(`(index ${index})`).toEqual(true);
|
||||
|
||||
// verify original signature
|
||||
const ok2 = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromDer(row.signature),
|
||||
messageHash,
|
||||
keypair.pubkey,
|
||||
);
|
||||
expect(ok2).withContext(`(index ${index})`).toEqual(true);
|
||||
|
||||
// compare signatures
|
||||
expect(calculatedSignature.toDer()).withContext(`(index ${index})`).toEqual(row.signature);
|
||||
}
|
||||
});
|
||||
|
||||
describe("recoverPubkey", () => {
|
||||
it("can recover pubkey", async () => {
|
||||
{
|
||||
// Test data from https://github.com/ethereumjs/ethereumjs-util/blob/v6.1.0/test/index.js#L496
|
||||
const expectedPubkey = (
|
||||
await Secp256k1.makeKeypair(
|
||||
fromHex("3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1"),
|
||||
)
|
||||
).pubkey;
|
||||
const signature = new ExtendedSecp256k1Signature(
|
||||
fromHex("99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9"),
|
||||
fromHex("129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66"),
|
||||
0,
|
||||
);
|
||||
const messageHash = fromHex("82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28");
|
||||
const pubkey = Secp256k1.recoverPubkey(signature, messageHash);
|
||||
expect(pubkey).toEqual(expectedPubkey);
|
||||
}
|
||||
{
|
||||
// Test data from https://github.com/randombit/botan/blob/2.9.0/src/tests/data/pubkey/ecdsa_key_recovery.vec
|
||||
const expectedPubkeyX = "F3F8BB913AA68589A2C8C607A877AB05252ADBD963E1BE846DDEB8456942AEDC";
|
||||
const expectedPubkeyY = "A2ED51F08CA3EF3DAC0A7504613D54CD539FC1B3CBC92453CD704B6A2D012B2C";
|
||||
const expectedPubkey = fromHex(`04${expectedPubkeyX}${expectedPubkeyY}`);
|
||||
const r = fromHex("E30F2E6A0F705F4FB5F8501BA79C7C0D3FAC847F1AD70B873E9797B17B89B390");
|
||||
const s = fromHex("81F1A4457589F30D76AB9F89E748A68C8A94C30FE0BAC8FB5C0B54EA70BF6D2F");
|
||||
const signature = new ExtendedSecp256k1Signature(r, s, 0);
|
||||
const messageHash = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
const pubkey = Secp256k1.recoverPubkey(signature, messageHash);
|
||||
expect(pubkey).toEqual(expectedPubkey);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("compressPubkey", () => {
|
||||
it("throws for a pubkey with invalid length", () => {
|
||||
const pubkey = fromHex("aa".repeat(32));
|
||||
expect(() => Secp256k1.compressPubkey(pubkey)).toThrowError(/invalid pubkey length/i);
|
||||
});
|
||||
|
||||
it("returns a compressed pubkey unchanged", () => {
|
||||
const pubkey = fromHex("02d41a0aa167b21699429eab224bc03f2cd386f0af5d20cefbd0336f1544aea24f");
|
||||
expect(Secp256k1.compressPubkey(pubkey)).toEqual(pubkey);
|
||||
});
|
||||
|
||||
it("compresses an uncompressed pubkey", () => {
|
||||
// Test data generated at https://iancoleman.io/bitcoin-key-compression/
|
||||
const pubkey = fromHex(
|
||||
"044f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029013b587a681e836cc187a8164b98a5848a2b89b3173315fdd0740d5032e259cd5",
|
||||
);
|
||||
const compressed = fromHex("034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c70290");
|
||||
expect(Secp256k1.compressPubkey(pubkey)).toEqual(compressed);
|
||||
});
|
||||
});
|
||||
|
||||
describe("trimRecoveryByte", () => {
|
||||
it("throws for a signature with invalid length", () => {
|
||||
const signature = fromHex("aa".repeat(66));
|
||||
expect(() => Secp256k1.trimRecoveryByte(signature)).toThrowError(/invalid signature length/i);
|
||||
});
|
||||
|
||||
it("returns a trimmed signature", () => {
|
||||
const signature = fromHex("aa".repeat(64));
|
||||
expect(Secp256k1.trimRecoveryByte(signature)).toEqual(signature);
|
||||
});
|
||||
|
||||
it("trims a signature with recovery byte", () => {
|
||||
const signature = fromHex("aa".repeat(64) + "bb");
|
||||
expect(Secp256k1.trimRecoveryByte(signature)).toEqual(fromHex("aa".repeat(64)));
|
||||
});
|
||||
});
|
||||
});
|
||||
137
packages/crypto/src/secp256k1.ts
Normal file
137
packages/crypto/src/secp256k1.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { fromHex, toHex } from "@cosmjs/encoding";
|
||||
import BN from "bn.js";
|
||||
import elliptic from "elliptic";
|
||||
import { As } from "type-tagger";
|
||||
|
||||
import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
|
||||
const secp256k1 = new elliptic.ec("secp256k1");
|
||||
const secp256k1N = new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "hex");
|
||||
|
||||
interface Keypair {
|
||||
readonly pubkey: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
}
|
||||
|
||||
export type Secp256k1Keypair = Keypair & As<"secp256k1-keypair">;
|
||||
|
||||
export class Secp256k1 {
|
||||
public static async makeKeypair(privkey: Uint8Array): Promise<Secp256k1Keypair> {
|
||||
if (privkey.length !== 32) {
|
||||
// is this check missing in secp256k1.validatePrivateKey?
|
||||
// https://github.com/bitjson/bitcoin-ts/issues/4
|
||||
throw new Error("input data is not a valid secp256k1 private key");
|
||||
}
|
||||
|
||||
const keypair = secp256k1.keyFromPrivate(privkey);
|
||||
if (keypair.validate().result !== true) {
|
||||
throw new Error("input data is not a valid secp256k1 private key");
|
||||
}
|
||||
|
||||
// range test that is not part of the elliptic implementation
|
||||
const privkeyAsBigInteger = new BN(privkey);
|
||||
if (privkeyAsBigInteger.gte(secp256k1N)) {
|
||||
// not strictly smaller than N
|
||||
throw new Error("input data is not a valid secp256k1 private key");
|
||||
}
|
||||
|
||||
const out: Keypair = {
|
||||
privkey: fromHex(keypair.getPrivate("hex")),
|
||||
// encodes uncompressed as
|
||||
// - 1-byte prefix "04"
|
||||
// - 32-byte x coordinate
|
||||
// - 32-byte y coordinate
|
||||
pubkey: Uint8Array.from(keypair.getPublic("array")),
|
||||
};
|
||||
return out as Secp256k1Keypair;
|
||||
}
|
||||
|
||||
// Creates a signature that is
|
||||
// - deterministic (RFC 6979)
|
||||
// - lowS signature
|
||||
// - DER encoded
|
||||
public static async createSignature(
|
||||
messageHash: Uint8Array,
|
||||
privkey: Uint8Array,
|
||||
): Promise<ExtendedSecp256k1Signature> {
|
||||
if (messageHash.length === 0) {
|
||||
throw new Error("Message hash must not be empty");
|
||||
}
|
||||
if (messageHash.length > 32) {
|
||||
throw new Error("Message hash length must not exceed 32 bytes");
|
||||
}
|
||||
|
||||
const keypair = secp256k1.keyFromPrivate(privkey);
|
||||
// the `canonical` option ensures creation of lowS signature representations
|
||||
const { r, s, recoveryParam } = keypair.sign(messageHash, { canonical: true });
|
||||
if (typeof recoveryParam !== "number") throw new Error("Recovery param missing");
|
||||
return new ExtendedSecp256k1Signature(
|
||||
Uint8Array.from(r.toArray()),
|
||||
Uint8Array.from(s.toArray()),
|
||||
recoveryParam,
|
||||
);
|
||||
}
|
||||
|
||||
public static async verifySignature(
|
||||
signature: Secp256k1Signature,
|
||||
messageHash: Uint8Array,
|
||||
pubkey: Uint8Array,
|
||||
): Promise<boolean> {
|
||||
if (messageHash.length === 0) {
|
||||
throw new Error("Message hash must not be empty");
|
||||
}
|
||||
if (messageHash.length > 32) {
|
||||
throw new Error("Message hash length must not exceed 32 bytes");
|
||||
}
|
||||
|
||||
const keypair = secp256k1.keyFromPublic(pubkey);
|
||||
|
||||
// From https://github.com/indutny/elliptic:
|
||||
//
|
||||
// Sign the message's hash (input must be an array, or a hex-string)
|
||||
//
|
||||
// Signature MUST be either:
|
||||
// 1) DER-encoded signature as hex-string; or
|
||||
// 2) DER-encoded signature as buffer; or
|
||||
// 3) object with two hex-string properties (r and s); or
|
||||
// 4) object with two buffer properties (r and s)
|
||||
//
|
||||
// Uint8Array is not a Buffer, but elliptic seems to be happy with the interface
|
||||
// common to both types. Uint8Array is not an array of ints but the interface is
|
||||
// similar
|
||||
try {
|
||||
return keypair.verify(messageHash, signature.toDer());
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static recoverPubkey(signature: ExtendedSecp256k1Signature, messageHash: Uint8Array): Uint8Array {
|
||||
const signatureForElliptic = { r: toHex(signature.r()), s: toHex(signature.s()) };
|
||||
const point = secp256k1.recoverPubKey(messageHash, signatureForElliptic, signature.recovery);
|
||||
const keypair = secp256k1.keyFromPublic(point);
|
||||
return fromHex(keypair.getPublic(false, "hex"));
|
||||
}
|
||||
|
||||
public static compressPubkey(pubkey: Uint8Array): Uint8Array {
|
||||
switch (pubkey.length) {
|
||||
case 33:
|
||||
return pubkey;
|
||||
case 65:
|
||||
return Uint8Array.from(secp256k1.keyFromPublic(pubkey).getPublic(true, "array"));
|
||||
default:
|
||||
throw new Error("Invalid pubkey length");
|
||||
}
|
||||
}
|
||||
|
||||
public static trimRecoveryByte(signature: Uint8Array): Uint8Array {
|
||||
switch (signature.length) {
|
||||
case 64:
|
||||
return signature;
|
||||
case 65:
|
||||
return signature.slice(0, 64);
|
||||
default:
|
||||
throw new Error("Invalid signature length");
|
||||
}
|
||||
}
|
||||
}
|
||||
162
packages/crypto/src/secp256k1signature.spec.ts
Normal file
162
packages/crypto/src/secp256k1signature.spec.ts
Normal file
@ -0,0 +1,162 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
|
||||
describe("Secp256k1Signature", () => {
|
||||
describe("fromFixedLength", () => {
|
||||
it("works", () => {
|
||||
const data = fromHex(
|
||||
"000000000000000000000000000000000000000000000000000000000000223300000000000000000000000000000000000000000000000000000000000000aa",
|
||||
);
|
||||
const signature = Secp256k1Signature.fromFixedLength(data);
|
||||
expect(signature.r()).toEqual(new Uint8Array([0x22, 0x33]));
|
||||
expect(signature.s()).toEqual(new Uint8Array([0xaa]));
|
||||
});
|
||||
|
||||
it("throws for invalid length", () => {
|
||||
const data = fromHex(
|
||||
"000000000000000000000000000000000000000000000000000000000000223300000000000000000000000000000000000000000000000000000000000000aa01",
|
||||
);
|
||||
expect(() => Secp256k1Signature.fromFixedLength(data)).toThrowError(/got invalid data length/i);
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed", () => {
|
||||
const signature = new Secp256k1Signature(new Uint8Array([0x22, 0x33]), new Uint8Array([0xaa]));
|
||||
expect(signature).toBeTruthy();
|
||||
});
|
||||
|
||||
it("can get r and s", () => {
|
||||
const signature = new Secp256k1Signature(new Uint8Array([0x22, 0x33]), new Uint8Array([0xaa]));
|
||||
expect(signature.r()).toEqual(new Uint8Array([0x22, 0x33]));
|
||||
expect(signature.s()).toEqual(new Uint8Array([0xaa]));
|
||||
});
|
||||
|
||||
it("can padd r and s", () => {
|
||||
const signature = new Secp256k1Signature(new Uint8Array([0x22, 0x33]), new Uint8Array([0xaa]));
|
||||
expect(signature.r(2)).toEqual(fromHex("2233"));
|
||||
expect(signature.r(5)).toEqual(fromHex("0000002233"));
|
||||
expect(signature.r(32)).toEqual(
|
||||
fromHex("0000000000000000000000000000000000000000000000000000000000002233"),
|
||||
);
|
||||
expect(signature.s(1)).toEqual(fromHex("AA"));
|
||||
expect(signature.s(2)).toEqual(fromHex("00AA"));
|
||||
expect(signature.s(7)).toEqual(fromHex("000000000000AA"));
|
||||
});
|
||||
|
||||
it("throws when output size of r or s is too small", () => {
|
||||
const signature = new Secp256k1Signature(
|
||||
new Uint8Array([0x22, 0x33, 0x44]),
|
||||
new Uint8Array([0xaa, 0xbb]),
|
||||
);
|
||||
expect(() => signature.r(0)).toThrowError(/length too small to hold parameter r/i);
|
||||
expect(() => signature.r(1)).toThrowError(/length too small to hold parameter r/i);
|
||||
expect(() => signature.r(2)).toThrowError(/length too small to hold parameter r/i);
|
||||
expect(() => signature.s(0)).toThrowError(/length too small to hold parameter s/i);
|
||||
expect(() => signature.s(1)).toThrowError(/length too small to hold parameter s/i);
|
||||
});
|
||||
|
||||
it("throws for r with leading 0", () => {
|
||||
expect(
|
||||
() =>
|
||||
new Secp256k1Signature(
|
||||
fromHex("00F25B86E1D8A11D72475B3ED273B0781C7D7F6F9E1DAE0DD5D3EE9B84F3FAB891"),
|
||||
new Uint8Array([0xaa]),
|
||||
),
|
||||
).toThrowError(/unsigned integer r must be encoded as unpadded big endian./i);
|
||||
});
|
||||
|
||||
it("throws for s with leading 0", () => {
|
||||
expect(
|
||||
() =>
|
||||
new Secp256k1Signature(
|
||||
new Uint8Array([0xaa]),
|
||||
fromHex("00F25B86E1D8A11D72475B3ED273B0781C7D7F6F9E1DAE0DD5D3EE9B84F3FAB891"),
|
||||
),
|
||||
).toThrowError(/unsigned integer s must be encoded as unpadded big endian./i);
|
||||
});
|
||||
|
||||
it("can encode to DER", () => {
|
||||
// Signature 3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935
|
||||
// decoded by http://asn1-playground.oss.com/
|
||||
const signature = new Secp256k1Signature(
|
||||
fromHex("F25B86E1D8A11D72475B3ED273B0781C7D7F6F9E1DAE0DD5D3EE9B84F3FAB891"),
|
||||
fromHex("63D9C4E1391DE077244583E9A6E3D8E8E1F236A3BF5963735353B93B1A3BA935"),
|
||||
);
|
||||
expect(signature.toDer()).toEqual(
|
||||
fromHex(
|
||||
"3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("can decode from DER", () => {
|
||||
// Signature 3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935
|
||||
// decoded by http://asn1-playground.oss.com/
|
||||
const signature = Secp256k1Signature.fromDer(
|
||||
fromHex(
|
||||
"3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935",
|
||||
),
|
||||
);
|
||||
expect(signature.toDer()).toEqual(
|
||||
fromHex(
|
||||
"3045022100f25b86e1d8a11d72475b3ed273b0781c7d7f6f9e1dae0dd5d3ee9b84f3fab891022063d9c4e1391de077244583e9a6e3d8e8e1f236a3bf5963735353b93b1a3ba935",
|
||||
),
|
||||
);
|
||||
expect(signature.r()).toEqual(
|
||||
fromHex("F25B86E1D8A11D72475B3ED273B0781C7D7F6F9E1DAE0DD5D3EE9B84F3FAB891"),
|
||||
);
|
||||
expect(signature.s()).toEqual(
|
||||
fromHex("63D9C4E1391DE077244583E9A6E3D8E8E1F236A3BF5963735353B93B1A3BA935"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ExtendedSecp256k1Signature", () => {
|
||||
it("can be constructed", () => {
|
||||
const signature = new ExtendedSecp256k1Signature(new Uint8Array([0x22, 0x33]), new Uint8Array([0xaa]), 1);
|
||||
expect(signature.recovery).toEqual(1);
|
||||
});
|
||||
|
||||
it("throws for recovery param out of range", () => {
|
||||
expect(() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), Number.NaN)).toThrowError(
|
||||
/the recovery parameter must be an integer/i,
|
||||
);
|
||||
expect(
|
||||
() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), Number.NEGATIVE_INFINITY),
|
||||
).toThrowError(/the recovery parameter must be an integer/i);
|
||||
expect(
|
||||
() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), Number.POSITIVE_INFINITY),
|
||||
).toThrowError(/the recovery parameter must be an integer/i);
|
||||
expect(() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), 1.1)).toThrowError(
|
||||
/the recovery parameter must be an integer/i,
|
||||
);
|
||||
|
||||
expect(() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), -1)).toThrowError(
|
||||
/the recovery parameter must be one of 0, 1, 2, 3/i,
|
||||
);
|
||||
expect(() => new ExtendedSecp256k1Signature(fromHex("aa"), fromHex("bb"), 5)).toThrowError(
|
||||
/the recovery parameter must be one of 0, 1, 2, 3/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("can be encoded as fixed length", () => {
|
||||
const signature = new ExtendedSecp256k1Signature(new Uint8Array([0x22, 0x33]), new Uint8Array([0xaa]), 1);
|
||||
expect(signature.toFixedLength()).toEqual(
|
||||
fromHex(
|
||||
"000000000000000000000000000000000000000000000000000000000000223300000000000000000000000000000000000000000000000000000000000000aa01",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("can be decoded from fixed length", () => {
|
||||
const signature = ExtendedSecp256k1Signature.fromFixedLength(
|
||||
fromHex(
|
||||
"000000000000000000000000000000000000000000000000000000000000223300000000000000000000000000000000000000000000000000000000000000aa01",
|
||||
),
|
||||
);
|
||||
expect(signature.r()).toEqual(new Uint8Array([0x22, 0x33]));
|
||||
expect(signature.s()).toEqual(new Uint8Array([0xaa]));
|
||||
expect(signature.recovery).toEqual(1);
|
||||
});
|
||||
});
|
||||
179
packages/crypto/src/secp256k1signature.ts
Normal file
179
packages/crypto/src/secp256k1signature.ts
Normal file
@ -0,0 +1,179 @@
|
||||
function trimLeadingNullBytes(inData: Uint8Array): Uint8Array {
|
||||
let numberOfLeadingNullBytes = 0;
|
||||
for (const byte of inData) {
|
||||
if (byte === 0x00) {
|
||||
numberOfLeadingNullBytes++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inData.slice(numberOfLeadingNullBytes);
|
||||
}
|
||||
|
||||
const derTagInteger = 0x02;
|
||||
|
||||
export class Secp256k1Signature {
|
||||
/**
|
||||
* Takes the pair of integers (r, s) as 2x32 byte of binary data.
|
||||
*
|
||||
* Note: This is the format Cosmos SDK uses natively.
|
||||
*
|
||||
* @param data a 64 byte value containing integers r and s.
|
||||
*/
|
||||
public static fromFixedLength(data: Uint8Array): Secp256k1Signature {
|
||||
if (data.length !== 64) {
|
||||
throw new Error(`Got invalid data length: ${data.length}. Expected 2x 32 bytes for the pair (r, s)`);
|
||||
}
|
||||
return new Secp256k1Signature(
|
||||
trimLeadingNullBytes(data.slice(0, 32)),
|
||||
trimLeadingNullBytes(data.slice(32, 64)),
|
||||
);
|
||||
}
|
||||
|
||||
public static fromDer(data: Uint8Array): Secp256k1Signature {
|
||||
let pos = 0;
|
||||
|
||||
if (data[pos++] !== 0x30) {
|
||||
throw new Error("Prefix 0x30 expected");
|
||||
}
|
||||
|
||||
const bodyLength = data[pos++];
|
||||
if (data.length - pos !== bodyLength) {
|
||||
throw new Error("Data length mismatch detected");
|
||||
}
|
||||
|
||||
// r
|
||||
const rTag = data[pos++];
|
||||
if (rTag !== derTagInteger) {
|
||||
throw new Error("INTEGER tag expected");
|
||||
}
|
||||
const rLength = data[pos++];
|
||||
if (rLength >= 0x80) {
|
||||
throw new Error("Decoding length values above 127 not supported");
|
||||
}
|
||||
const rData = data.slice(pos, pos + rLength);
|
||||
pos += rLength;
|
||||
|
||||
// s
|
||||
const sTag = data[pos++];
|
||||
if (sTag !== derTagInteger) {
|
||||
throw new Error("INTEGER tag expected");
|
||||
}
|
||||
const sLength = data[pos++];
|
||||
if (sLength >= 0x80) {
|
||||
throw new Error("Decoding length values above 127 not supported");
|
||||
}
|
||||
const sData = data.slice(pos, pos + sLength);
|
||||
pos += sLength;
|
||||
|
||||
return new Secp256k1Signature(
|
||||
// r/s data can contain leading 0 bytes to express integers being non-negative in DER
|
||||
trimLeadingNullBytes(rData),
|
||||
trimLeadingNullBytes(sData),
|
||||
);
|
||||
}
|
||||
|
||||
private readonly data: {
|
||||
readonly r: Uint8Array;
|
||||
readonly s: Uint8Array;
|
||||
};
|
||||
|
||||
public constructor(r: Uint8Array, s: Uint8Array) {
|
||||
if (r.length > 32 || r.length === 0 || r[0] === 0x00) {
|
||||
throw new Error("Unsigned integer r must be encoded as unpadded big endian.");
|
||||
}
|
||||
|
||||
if (s.length > 32 || s.length === 0 || s[0] === 0x00) {
|
||||
throw new Error("Unsigned integer s must be encoded as unpadded big endian.");
|
||||
}
|
||||
|
||||
this.data = {
|
||||
r: r,
|
||||
s: s,
|
||||
};
|
||||
}
|
||||
|
||||
public r(length?: number): Uint8Array {
|
||||
if (length === undefined) {
|
||||
return this.data.r;
|
||||
} else {
|
||||
const paddingLength = length - this.data.r.length;
|
||||
if (paddingLength < 0) {
|
||||
throw new Error("Length too small to hold parameter r");
|
||||
}
|
||||
const padding = new Uint8Array(paddingLength);
|
||||
return new Uint8Array([...padding, ...this.data.r]);
|
||||
}
|
||||
}
|
||||
|
||||
public s(length?: number): Uint8Array {
|
||||
if (length === undefined) {
|
||||
return this.data.s;
|
||||
} else {
|
||||
const paddingLength = length - this.data.s.length;
|
||||
if (paddingLength < 0) {
|
||||
throw new Error("Length too small to hold parameter s");
|
||||
}
|
||||
const padding = new Uint8Array(paddingLength);
|
||||
return new Uint8Array([...padding, ...this.data.s]);
|
||||
}
|
||||
}
|
||||
|
||||
public toDer(): Uint8Array {
|
||||
// DER supports negative integers but our data is unsigned. Thus we need to prepend
|
||||
// a leading 0 byte when the higest bit is set to differentiate nagative values
|
||||
const rEncoded = this.data.r[0] >= 0x80 ? new Uint8Array([0, ...this.data.r]) : this.data.r;
|
||||
const sEncoded = this.data.s[0] >= 0x80 ? new Uint8Array([0, ...this.data.s]) : this.data.s;
|
||||
|
||||
const rLength = rEncoded.length;
|
||||
const sLength = sEncoded.length;
|
||||
const data = new Uint8Array([derTagInteger, rLength, ...rEncoded, derTagInteger, sLength, ...sEncoded]);
|
||||
|
||||
return new Uint8Array([0x30, data.length, ...data]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Secp256k1Signature plus the recovery parameter
|
||||
*/
|
||||
export class ExtendedSecp256k1Signature extends Secp256k1Signature {
|
||||
/**
|
||||
* Decode extended signature from the simple fixed length encoding
|
||||
* described in toFixedLength().
|
||||
*/
|
||||
public static fromFixedLength(data: Uint8Array): ExtendedSecp256k1Signature {
|
||||
if (data.length !== 65) {
|
||||
throw new Error(`Got invalid data length ${data.length}. Expected 32 + 32 + 1`);
|
||||
}
|
||||
return new ExtendedSecp256k1Signature(
|
||||
trimLeadingNullBytes(data.slice(0, 32)),
|
||||
trimLeadingNullBytes(data.slice(32, 64)),
|
||||
data[64],
|
||||
);
|
||||
}
|
||||
|
||||
public readonly recovery: number;
|
||||
|
||||
public constructor(r: Uint8Array, s: Uint8Array, recovery: number) {
|
||||
super(r, s);
|
||||
|
||||
if (!Number.isInteger(recovery)) {
|
||||
throw new Error("The recovery parameter must be an integer.");
|
||||
}
|
||||
|
||||
if (recovery < 0 || recovery > 4) {
|
||||
throw new Error("The recovery parameter must be one of 0, 1, 2, 3.");
|
||||
}
|
||||
|
||||
this.recovery = recovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple custom encoding that encodes the extended signature as
|
||||
* r (32 bytes) | s (32 bytes) | recovery param (1 byte)
|
||||
* where | denotes concatenation of bonary data.
|
||||
*/
|
||||
public toFixedLength(): Uint8Array {
|
||||
return new Uint8Array([...this.r(32), ...this.s(32), this.recovery]);
|
||||
}
|
||||
}
|
||||
28
packages/crypto/src/sha.spec.ts
Normal file
28
packages/crypto/src/sha.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { fromHex, toHex } from "@cosmjs/encoding";
|
||||
|
||||
import { Sha256 } from "./sha";
|
||||
import shaVectors from "./testdata/sha.json";
|
||||
|
||||
describe("Sha256", () => {
|
||||
it("exists", () => {
|
||||
expect(Sha256).toBeTruthy();
|
||||
});
|
||||
|
||||
it("works for empty input", () => {
|
||||
{
|
||||
const hash = new Sha256(new Uint8Array([])).digest();
|
||||
expect(toHex(hash)).toEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||
}
|
||||
{
|
||||
const hash = new Sha256().digest();
|
||||
expect(toHex(hash)).toEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||
}
|
||||
});
|
||||
|
||||
it("works for all the Botan test vectors", () => {
|
||||
// https://github.com/randombit/botan/blob/2.6.0/src/tests/data/hash/sha2_32.vec#L13
|
||||
for (const { in: input, out: output } of shaVectors.sha256) {
|
||||
expect(new Sha256(fromHex(input)).digest()).toEqual(fromHex(output));
|
||||
}
|
||||
});
|
||||
});
|
||||
73
packages/crypto/src/sha.ts
Normal file
73
packages/crypto/src/sha.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { Hash } from "crypto";
|
||||
import shajs from "sha.js";
|
||||
|
||||
import { HashFunction } from "./hash";
|
||||
|
||||
export class Sha1 implements HashFunction {
|
||||
public readonly blockSize = 512 / 8;
|
||||
|
||||
private readonly impl: Hash;
|
||||
|
||||
public constructor(firstData?: Uint8Array) {
|
||||
this.impl = shajs("sha1");
|
||||
|
||||
if (firstData) {
|
||||
this.update(firstData);
|
||||
}
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Sha1 {
|
||||
this.impl.update(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
return new Uint8Array(this.impl.digest());
|
||||
}
|
||||
}
|
||||
|
||||
export class Sha256 implements HashFunction {
|
||||
public readonly blockSize = 512 / 8;
|
||||
|
||||
private readonly impl: Hash;
|
||||
|
||||
public constructor(firstData?: Uint8Array) {
|
||||
this.impl = shajs("sha256");
|
||||
|
||||
if (firstData) {
|
||||
this.update(firstData);
|
||||
}
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Sha256 {
|
||||
this.impl.update(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
return new Uint8Array(this.impl.digest());
|
||||
}
|
||||
}
|
||||
|
||||
export class Sha512 implements HashFunction {
|
||||
public readonly blockSize = 1024 / 8;
|
||||
|
||||
private readonly impl: Hash;
|
||||
|
||||
public constructor(firstData?: Uint8Array) {
|
||||
this.impl = shajs("sha512");
|
||||
|
||||
if (firstData) {
|
||||
this.update(firstData);
|
||||
}
|
||||
}
|
||||
|
||||
public update(data: Uint8Array): Sha512 {
|
||||
this.impl.update(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
public digest(): Uint8Array {
|
||||
return new Uint8Array(this.impl.digest());
|
||||
}
|
||||
}
|
||||
472
packages/crypto/src/slip10.spec.ts
Normal file
472
packages/crypto/src/slip10.spec.ts
Normal file
@ -0,0 +1,472 @@
|
||||
import { fromHex } from "@cosmjs/encoding";
|
||||
|
||||
import {
|
||||
pathToString,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
slip10CurveFromString,
|
||||
Slip10RawIndex,
|
||||
stringToPath,
|
||||
} from "./slip10";
|
||||
|
||||
describe("Slip10", () => {
|
||||
it("has working slip10CurveFromString()", () => {
|
||||
expect(slip10CurveFromString("Bitcoin seed")).toEqual(Slip10Curve.Secp256k1);
|
||||
expect(slip10CurveFromString("ed25519 seed")).toEqual(Slip10Curve.Ed25519);
|
||||
expect(() => slip10CurveFromString("something else")).toThrowError(/unknown curve/i);
|
||||
expect(() => slip10CurveFromString("")).toThrowError(/unknown curve/i);
|
||||
});
|
||||
|
||||
describe("Test vector 1 for secp256k1", () => {
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-secp256k1
|
||||
const seed = fromHex("000102030405060708090a0b0c0d0e0f");
|
||||
|
||||
it("can derive path m", () => {
|
||||
const path: readonly Slip10RawIndex[] = [];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.hardened(0)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.hardened(0), Slip10RawIndex.normal(1)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1/2'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.normal(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1/2'/2", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.normal(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
Slip10RawIndex.normal(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1/2'/2/1000000000", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.normal(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
Slip10RawIndex.normal(2),
|
||||
Slip10RawIndex.normal(1000000000),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test vector 2 for secp256k1", () => {
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-secp256k1
|
||||
const seed = fromHex(
|
||||
"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
|
||||
);
|
||||
|
||||
it("can derive path m", () => {
|
||||
const path: readonly Slip10RawIndex[] = [];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.normal(0)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0/2147483647'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.normal(0), Slip10RawIndex.hardened(2147483647)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0/2147483647'/1", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.normal(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.normal(1),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0/2147483647'/1/2147483646'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.normal(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.normal(1),
|
||||
Slip10RawIndex.hardened(2147483646),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0/2147483647'/1/2147483646'/2", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.normal(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.normal(1),
|
||||
Slip10RawIndex.hardened(2147483646),
|
||||
Slip10RawIndex.normal(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Secp256k1, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test vector 1 for ed25519", () => {
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519
|
||||
const seed = fromHex("000102030405060708090a0b0c0d0e0f");
|
||||
|
||||
it("can derive path m", () => {
|
||||
const path: readonly Slip10RawIndex[] = [];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("90046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.hardened(0)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("8b59aa11380b624e81507a27fedda59fea6d0b779a778918a2fd3590e16e9c69"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.hardened(0), Slip10RawIndex.hardened(1)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("a320425f77d1b5c2505a6b1b27382b37368ee640e3557c315416801243552f14"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1'/2'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("2e69929e00b5ab250f49c3fb1c12f252de4fed2c1db88387094a0f8c4c9ccd6c"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1'/2'/2'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
Slip10RawIndex.hardened(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("8f6d87f93d750e0efccda017d662a1b31a266e4a6f5993b15f5c1f07f74dd5cc"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/1'/2'/2'/1000000000'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(1),
|
||||
Slip10RawIndex.hardened(2),
|
||||
Slip10RawIndex.hardened(2),
|
||||
Slip10RawIndex.hardened(1000000000),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("68789923a0cac2cd5a29172a475fe9e0fb14cd6adb5ad98a3fa70333e7afa230"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test vector 2 for ed25519", () => {
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519
|
||||
const seed = fromHex(
|
||||
"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
|
||||
);
|
||||
|
||||
it("can derive path m", () => {
|
||||
const path: readonly Slip10RawIndex[] = [];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("ef70a74db9c3a5af931b5fe73ed8e1a53464133654fd55e7a66f8570b8e33c3b"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [Slip10RawIndex.hardened(0)];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("0b78a3226f915c082bf118f83618a618ab6dec793752624cbeb622acb562862d"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/2147483647'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("138f0b2551bcafeca6ff2aa88ba8ed0ed8de070841f0c4ef0165df8181eaad7f"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/2147483647'/1'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.hardened(1),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("73bd9fff1cfbde33a1b846c27085f711c0fe2d66fd32e139d3ebc28e5a4a6b90"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/2147483647'/1'/2147483646'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.hardened(1),
|
||||
Slip10RawIndex.hardened(2147483646),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("0902fe8a29f9140480a00ef244bd183e8a13288e4412d8389d140aac1794825a"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72"),
|
||||
);
|
||||
});
|
||||
|
||||
it("can derive path m/0'/2147483647'/1'/2147483646'/2'", () => {
|
||||
const path: readonly Slip10RawIndex[] = [
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.hardened(2147483647),
|
||||
Slip10RawIndex.hardened(1),
|
||||
Slip10RawIndex.hardened(2147483646),
|
||||
Slip10RawIndex.hardened(2),
|
||||
];
|
||||
const derived = Slip10.derivePath(Slip10Curve.Ed25519, seed, path);
|
||||
expect(derived.chainCode).toEqual(
|
||||
fromHex("5d70af781f3a37b829f0d060924d5e960bdc02e85423494afc0b1a41bbe196d4"),
|
||||
);
|
||||
expect(derived.privkey).toEqual(
|
||||
fromHex("551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pathToString", () => {
|
||||
it("works for no component", () => {
|
||||
// See https://github.com/bitcoin/bips/blob/master/bip-0032/derivation.png from BIP32
|
||||
expect(pathToString([])).toEqual("m");
|
||||
});
|
||||
|
||||
it("works for normal components", () => {
|
||||
const one = Slip10RawIndex.normal(1);
|
||||
expect(pathToString([one])).toEqual("m/1");
|
||||
expect(pathToString([one, one])).toEqual("m/1/1");
|
||||
expect(pathToString([one, one, one])).toEqual("m/1/1/1");
|
||||
|
||||
const min = Slip10RawIndex.normal(0);
|
||||
expect(pathToString([min])).toEqual("m/0");
|
||||
|
||||
const max = Slip10RawIndex.normal(2 ** 31 - 1);
|
||||
expect(pathToString([max])).toEqual("m/2147483647");
|
||||
});
|
||||
|
||||
it("works for hardened components", () => {
|
||||
const one = Slip10RawIndex.hardened(1);
|
||||
expect(pathToString([one])).toEqual("m/1'");
|
||||
expect(pathToString([one, one])).toEqual("m/1'/1'");
|
||||
expect(pathToString([one, one, one])).toEqual("m/1'/1'/1'");
|
||||
|
||||
const min = Slip10RawIndex.hardened(0);
|
||||
expect(pathToString([min])).toEqual("m/0'");
|
||||
|
||||
const max = Slip10RawIndex.hardened(2 ** 31 - 1);
|
||||
expect(pathToString([max])).toEqual("m/2147483647'");
|
||||
});
|
||||
|
||||
it("works for mixed components", () => {
|
||||
const one = Slip10RawIndex.normal(1);
|
||||
const two = Slip10RawIndex.hardened(2);
|
||||
expect(pathToString([one, two, two, one])).toEqual("m/1/2'/2'/1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("stringToPath", () => {
|
||||
it("works for no component", () => {
|
||||
// See https://github.com/bitcoin/bips/blob/master/bip-0032/derivation.png from BIP32
|
||||
expect(stringToPath("m")).toEqual([]);
|
||||
});
|
||||
|
||||
it("throws for broken start", () => {
|
||||
expect(() => stringToPath("")).toThrowError(/must start with 'm'/);
|
||||
expect(() => stringToPath("M")).toThrowError(/must start with 'm'/);
|
||||
expect(() => stringToPath("/1/1")).toThrowError(/must start with 'm'/);
|
||||
});
|
||||
|
||||
it("works for normal components", () => {
|
||||
const one = Slip10RawIndex.normal(1);
|
||||
expect(stringToPath("m/1")).toEqual([one]);
|
||||
expect(stringToPath("m/1/1")).toEqual([one, one]);
|
||||
expect(stringToPath("m/1/1/1")).toEqual([one, one, one]);
|
||||
|
||||
const min = Slip10RawIndex.normal(0);
|
||||
expect(stringToPath("m/0")).toEqual([min]);
|
||||
|
||||
const max = Slip10RawIndex.normal(2 ** 31 - 1);
|
||||
expect(stringToPath("m/2147483647")).toEqual([max]);
|
||||
});
|
||||
|
||||
it("errors for syntax error in component", () => {
|
||||
expect(() => stringToPath("m/ 1/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/-1/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m//1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/1*/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/1/1/1 ")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/1''/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/1 '/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath("m/'/1/1")).toThrowError(/syntax error/i);
|
||||
expect(() => stringToPath('m/1"/1/1')).toThrowError(/syntax error/i);
|
||||
});
|
||||
|
||||
it("errors for value too high", () => {
|
||||
expect(() => stringToPath("m/2147483648/1/1")).toThrowError(/value too high/i);
|
||||
expect(() => stringToPath("m/1/9007199254740991/1")).toThrowError(/value too high/i);
|
||||
expect(() => stringToPath("m/1/1/9007199254740992")).toThrowError(/not in int53 range/i);
|
||||
});
|
||||
|
||||
it("works for hardened components", () => {
|
||||
const one = Slip10RawIndex.hardened(1);
|
||||
expect(stringToPath("m/1'")).toEqual([one]);
|
||||
expect(stringToPath("m/1'/1'")).toEqual([one, one]);
|
||||
expect(stringToPath("m/1'/1'/1'")).toEqual([one, one, one]);
|
||||
|
||||
const min = Slip10RawIndex.hardened(0);
|
||||
expect(stringToPath("m/0'")).toEqual([min]);
|
||||
|
||||
const max = Slip10RawIndex.hardened(2 ** 31 - 1);
|
||||
expect(stringToPath("m/2147483647'")).toEqual([max]);
|
||||
});
|
||||
|
||||
it("works for mixed components", () => {
|
||||
const one = Slip10RawIndex.normal(1);
|
||||
const two = Slip10RawIndex.hardened(2);
|
||||
expect(stringToPath("m/1/2'/2'/1")).toEqual([one, two, two, one]);
|
||||
});
|
||||
});
|
||||
});
|
||||
213
packages/crypto/src/slip10.ts
Normal file
213
packages/crypto/src/slip10.ts
Normal file
@ -0,0 +1,213 @@
|
||||
import { fromHex, toAscii } from "@cosmjs/encoding";
|
||||
import { Uint32, Uint53 } from "@cosmjs/math";
|
||||
import BN from "bn.js";
|
||||
import elliptic from "elliptic";
|
||||
|
||||
import { Hmac } from "./hmac";
|
||||
import { Sha512 } from "./sha";
|
||||
|
||||
export interface Slip10Result {
|
||||
readonly chainCode: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw values must match the curve string in SLIP-0010 master key generation
|
||||
*
|
||||
* @see https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation
|
||||
*/
|
||||
export enum Slip10Curve {
|
||||
Secp256k1 = "Bitcoin seed",
|
||||
Ed25519 = "ed25519 seed",
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse mapping of Slip10Curve
|
||||
*/
|
||||
export function slip10CurveFromString(curveString: string): Slip10Curve {
|
||||
switch (curveString) {
|
||||
case Slip10Curve.Ed25519:
|
||||
return Slip10Curve.Ed25519;
|
||||
case Slip10Curve.Secp256k1:
|
||||
return Slip10Curve.Secp256k1;
|
||||
default:
|
||||
throw new Error(`Unknown curve string: '${curveString}'`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Slip10RawIndex extends Uint32 {
|
||||
public static hardened(hardenedIndex: number): Slip10RawIndex {
|
||||
return new Slip10RawIndex(hardenedIndex + 2 ** 31);
|
||||
}
|
||||
|
||||
public static normal(normalIndex: number): Slip10RawIndex {
|
||||
return new Slip10RawIndex(normalIndex);
|
||||
}
|
||||
|
||||
public isHardened(): boolean {
|
||||
return this.data >= 2 ** 31;
|
||||
}
|
||||
}
|
||||
|
||||
const secp256k1 = new elliptic.ec("secp256k1");
|
||||
|
||||
// Universal private key derivation accoring to
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0010.md
|
||||
export class Slip10 {
|
||||
public static derivePath(
|
||||
curve: Slip10Curve,
|
||||
seed: Uint8Array,
|
||||
path: readonly Slip10RawIndex[],
|
||||
): Slip10Result {
|
||||
let result = this.master(curve, seed);
|
||||
for (const rawIndex of path) {
|
||||
result = this.child(curve, result.privkey, result.chainCode, rawIndex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static master(curve: Slip10Curve, seed: Uint8Array): Slip10Result {
|
||||
const i = new Hmac(Sha512, toAscii(curve)).update(seed).digest();
|
||||
const il = i.slice(0, 32);
|
||||
const ir = i.slice(32, 64);
|
||||
|
||||
if (curve !== Slip10Curve.Ed25519 && (this.isZero(il) || this.isGteN(curve, il))) {
|
||||
return this.master(curve, i);
|
||||
}
|
||||
|
||||
return {
|
||||
chainCode: ir,
|
||||
privkey: il,
|
||||
};
|
||||
}
|
||||
|
||||
private static child(
|
||||
curve: Slip10Curve,
|
||||
parentPrivkey: Uint8Array,
|
||||
parentChainCode: Uint8Array,
|
||||
rawIndex: Slip10RawIndex,
|
||||
): Slip10Result {
|
||||
let i: Uint8Array;
|
||||
if (rawIndex.isHardened()) {
|
||||
const payload = new Uint8Array([0x00, ...parentPrivkey, ...rawIndex.toBytesBigEndian()]);
|
||||
i = new Hmac(Sha512, parentChainCode).update(payload).digest();
|
||||
} else {
|
||||
if (curve === Slip10Curve.Ed25519) {
|
||||
throw new Error("Normal keys are not allowed with ed25519");
|
||||
} else {
|
||||
// Step 1 of https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key
|
||||
// Calculate I = HMAC-SHA512(Key = c_par, Data = ser_P(point(k_par)) || ser_32(i)).
|
||||
// where the functions point() and ser_p() are defined in BIP-0032
|
||||
const data = new Uint8Array([
|
||||
...Slip10.serializedPoint(curve, new BN(parentPrivkey)),
|
||||
...rawIndex.toBytesBigEndian(),
|
||||
]);
|
||||
i = new Hmac(Sha512, parentChainCode).update(data).digest();
|
||||
}
|
||||
}
|
||||
|
||||
return this.childImpl(curve, parentPrivkey, parentChainCode, rawIndex, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ser_P(point(k_par)) from BIP-0032
|
||||
*
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
*/
|
||||
private static serializedPoint(curve: Slip10Curve, p: BN): Uint8Array {
|
||||
switch (curve) {
|
||||
case Slip10Curve.Secp256k1:
|
||||
return fromHex(secp256k1.g.mul(p).encodeCompressed("hex"));
|
||||
default:
|
||||
throw new Error("curve not supported");
|
||||
}
|
||||
}
|
||||
|
||||
private static childImpl(
|
||||
curve: Slip10Curve,
|
||||
parentPrivkey: Uint8Array,
|
||||
parentChainCode: Uint8Array,
|
||||
rawIndex: Slip10RawIndex,
|
||||
i: Uint8Array,
|
||||
): Slip10Result {
|
||||
// step 2 (of the Private parent key → private child key algorithm)
|
||||
|
||||
const il = i.slice(0, 32);
|
||||
const ir = i.slice(32, 64);
|
||||
|
||||
// step 3
|
||||
const returnChainCode = ir;
|
||||
|
||||
// step 4
|
||||
if (curve === Slip10Curve.Ed25519) {
|
||||
return {
|
||||
chainCode: returnChainCode,
|
||||
privkey: il,
|
||||
};
|
||||
}
|
||||
|
||||
// step 5
|
||||
const n = this.n(curve);
|
||||
const returnChildKeyAsNumber = new BN(il).add(new BN(parentPrivkey)).mod(n);
|
||||
const returnChildKey = Uint8Array.from(returnChildKeyAsNumber.toArray("be", 32));
|
||||
|
||||
// step 6
|
||||
if (this.isGteN(curve, il) || this.isZero(returnChildKey)) {
|
||||
const newI = new Hmac(Sha512, parentChainCode)
|
||||
.update(new Uint8Array([0x01, ...ir, ...rawIndex.toBytesBigEndian()]))
|
||||
.digest();
|
||||
return this.childImpl(curve, parentPrivkey, parentChainCode, rawIndex, newI);
|
||||
}
|
||||
|
||||
// step 7
|
||||
return {
|
||||
chainCode: returnChainCode,
|
||||
privkey: returnChildKey,
|
||||
};
|
||||
}
|
||||
|
||||
private static isZero(privkey: Uint8Array): boolean {
|
||||
return privkey.every((byte) => byte === 0);
|
||||
}
|
||||
|
||||
private static isGteN(curve: Slip10Curve, privkey: Uint8Array): boolean {
|
||||
const keyAsNumber = new BN(privkey);
|
||||
return keyAsNumber.gte(this.n(curve));
|
||||
}
|
||||
|
||||
private static n(curve: Slip10Curve): BN {
|
||||
switch (curve) {
|
||||
case Slip10Curve.Secp256k1:
|
||||
return new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
|
||||
default:
|
||||
throw new Error("curve not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function pathToString(path: readonly Slip10RawIndex[]): string {
|
||||
return path.reduce((current, component): string => {
|
||||
const componentString = component.isHardened()
|
||||
? `${component.toNumber() - 2 ** 31}'`
|
||||
: component.toString();
|
||||
return current + "/" + componentString;
|
||||
}, "m");
|
||||
}
|
||||
|
||||
export function stringToPath(input: string): readonly Slip10RawIndex[] {
|
||||
if (!input.startsWith("m")) throw new Error("Path string must start with 'm'");
|
||||
let rest = input.slice(1);
|
||||
|
||||
const out = new Array<Slip10RawIndex>();
|
||||
while (rest) {
|
||||
const match = rest.match(/^\/([0-9]+)('?)/);
|
||||
if (!match) throw new Error("Syntax error while reading path component");
|
||||
const [fullMatch, numberString, apostrophe] = match;
|
||||
const value = Uint53.fromString(numberString).toNumber();
|
||||
if (value >= 2 ** 31) throw new Error("Component value too high. Must not exceed 2**31-1.");
|
||||
if (apostrophe) out.push(Slip10RawIndex.hardened(value));
|
||||
else out.push(Slip10RawIndex.normal(value));
|
||||
rest = rest.slice(fullMatch.length);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
174
packages/crypto/src/testdata/bip39.json
vendored
Normal file
174
packages/crypto/src/testdata/bip39.json
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"encoding": {
|
||||
"12": [
|
||||
{
|
||||
"entropy": "00000000000000000000000000000000",
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
},
|
||||
{
|
||||
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"mnemonic": "legal winner thank year wave sausage worth useful legal winner thank yellow"
|
||||
},
|
||||
{
|
||||
"entropy": "80808080808080808080808080808080",
|
||||
"mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
|
||||
},
|
||||
{
|
||||
"entropy": "ffffffffffffffffffffffffffffffff",
|
||||
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"
|
||||
},
|
||||
{
|
||||
"entropy": "9e885d952ad362caeb4efe34a8e91bd2",
|
||||
"mnemonic": "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"
|
||||
},
|
||||
{
|
||||
"entropy": "c0ba5a8e914111210f2bd131f3d5e08d",
|
||||
"mnemonic": "scheme spot photo card baby mountain device kick cradle pact join borrow"
|
||||
},
|
||||
{
|
||||
"entropy": "23db8160a31d3e0dca3688ed941adbf3",
|
||||
"mnemonic": "cat swing flag economy stadium alone churn speed unique patch report train"
|
||||
},
|
||||
{
|
||||
"entropy": "f30f8c1da665478f49b001d94c5fc452",
|
||||
"mnemonic": "vessel ladder alter error federal sibling chat ability sun glass valve picture"
|
||||
}
|
||||
],
|
||||
"15": [
|
||||
{
|
||||
"entropy": "0000000000000000000000000000000000000000",
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address"
|
||||
},
|
||||
{
|
||||
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage wise"
|
||||
},
|
||||
{
|
||||
"entropy": "8080808080808080808080808080808080808080",
|
||||
"mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor accident"
|
||||
},
|
||||
{
|
||||
"entropy": "ffffffffffffffffffffffffffffffffffffffff",
|
||||
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrist"
|
||||
},
|
||||
{
|
||||
"entropy": "610b25967cdcca9d59875f5cb50b0ea754333118",
|
||||
"mnemonic": "genre float grain whale smile exchange gravity typical frequent portion senior exchange drip obtain glow"
|
||||
},
|
||||
{
|
||||
"entropy": "232c38b9a99e10d2253fa1aa2739a8e9c3b112d0",
|
||||
"mnemonic": "case gift common farm three harbor neutral vintage pretty degree health squeeze deposit maximum donor"
|
||||
},
|
||||
{
|
||||
"entropy": "c9a0ed1374180cd8f8c5cc0bc9c719dfb67add59",
|
||||
"mnemonic": "situate alter dynamic trial liar hockey toast ridge armed evolve shoe satoshi guilt huge grass"
|
||||
},
|
||||
{
|
||||
"entropy": "ef38bbc81a5fa1756dfd654b0cc1a1759ce1ed94",
|
||||
"mnemonic": "upset shift velvet cruise wheel rival retire protect enrich gravity hair twenty sock walnut fat"
|
||||
}
|
||||
],
|
||||
"18": [
|
||||
{
|
||||
"entropy": "000000000000000000000000000000000000000000000000",
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent"
|
||||
},
|
||||
{
|
||||
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will"
|
||||
},
|
||||
{
|
||||
"entropy": "808080808080808080808080808080808080808080808080",
|
||||
"mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always"
|
||||
},
|
||||
{
|
||||
"entropy": "ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when"
|
||||
},
|
||||
{
|
||||
"entropy": "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
|
||||
"mnemonic": "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog"
|
||||
},
|
||||
{
|
||||
"entropy": "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
|
||||
"mnemonic": "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave"
|
||||
},
|
||||
{
|
||||
"entropy": "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
|
||||
"mnemonic": "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access"
|
||||
},
|
||||
{
|
||||
"entropy": "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
|
||||
"mnemonic": "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump"
|
||||
}
|
||||
],
|
||||
"21": [
|
||||
{
|
||||
"entropy": "00000000000000000000000000000000000000000000000000000000",
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit"
|
||||
},
|
||||
{
|
||||
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year viable"
|
||||
},
|
||||
{
|
||||
"entropy": "80808080808080808080808080808080808080808080808080808080",
|
||||
"mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd apart"
|
||||
},
|
||||
{
|
||||
"entropy": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo veteran"
|
||||
},
|
||||
{
|
||||
"entropy": "8a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc7",
|
||||
"mnemonic": "media soon raw edge embody palm reason pave right danger clay occur modify conduct ethics vessel journey toast cradle stuff laundry"
|
||||
},
|
||||
{
|
||||
"entropy": "49208c3112119c4770d44d1a9726b44135748bda464954338f3c1d91",
|
||||
"mnemonic": "empty afford arrange category border casual select meadow box rice public list firm echo harbor since feature organ someone depth bitter"
|
||||
},
|
||||
{
|
||||
"entropy": "dc8a247c5379f57c24dbfbd6a0699a8282875dd0a3f0f39b8fc020a4",
|
||||
"mnemonic": "symptom eye business plunge palm safe nature legal stove addict grit agree chronic puzzle dream lawn vicious symbol useless donor eagle"
|
||||
},
|
||||
{
|
||||
"entropy": "a66afdf103bf42b5858cd1c23a20f7e5114edb111880d634b53454bf",
|
||||
"mnemonic": "please fitness labor alter vintage food bike olive season speed digital sketch belt horn dutch avoid stomach pizza escape practice walnut"
|
||||
}
|
||||
],
|
||||
"24": [
|
||||
{
|
||||
"entropy": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
|
||||
},
|
||||
{
|
||||
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title"
|
||||
},
|
||||
{
|
||||
"entropy": "8080808080808080808080808080808080808080808080808080808080808080",
|
||||
"mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless"
|
||||
},
|
||||
{
|
||||
"entropy": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote"
|
||||
},
|
||||
{
|
||||
"entropy": "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
|
||||
"mnemonic": "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length"
|
||||
},
|
||||
{
|
||||
"entropy": "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
|
||||
"mnemonic": "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside"
|
||||
},
|
||||
{
|
||||
"entropy": "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
|
||||
"mnemonic": "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform"
|
||||
},
|
||||
{
|
||||
"entropy": "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
|
||||
"mnemonic": "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
packages/crypto/src/testdata/bip39_wordlists.json
vendored
Normal file
3
packages/crypto/src/testdata/bip39_wordlists.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1076
packages/crypto/src/testdata/keccak.json
vendored
Normal file
1076
packages/crypto/src/testdata/keccak.json
vendored
Normal file
File diff suppressed because one or more lines are too long
383
packages/crypto/src/testdata/ripemd.json
vendored
Normal file
383
packages/crypto/src/testdata/ripemd.json
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
{
|
||||
"ripemd160": [
|
||||
{
|
||||
"in": "ac",
|
||||
"out": "faf1685349fe8b832bb2ff1cba482583b3560f3f"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "e9ec",
|
||||
"out": "c4eb5003f5fc64e122194ef6fba841b30b1e8761"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "74524b",
|
||||
"out": "d5904a1d8ba51b972bcf6d00681a5f0306c57ac5"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "e233e3b4",
|
||||
"out": "9cea3480ef59215c514c4fff9b813677120aa8aa"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "445a2f97ad",
|
||||
"out": "6ae5414cbe327cdd20fd8887f2f94d7b69222ab7"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "a242802b76ec",
|
||||
"out": "5dca1535835d75adfa1b204c5cde68c2ca8f1d67"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "cbec35d2fc21e9",
|
||||
"out": "c9809ca68c9013f34b7525936db84b5737c591da"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "e8f3385f8b210a7e",
|
||||
"out": "497049f4d70e7b42a3f30582c27cc22da5a51cd7"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "61a82b5b622c48f6be",
|
||||
"out": "df01106caf345f6e084dc5a46137ead345d445d9"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "4ac97ba5461eaf9228fe",
|
||||
"out": "c44507e89c39b4e9965fc9610b6b82985b1a2b57"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "0e0b7d71b3cdf458b883b5",
|
||||
"out": "39f567e3b8a7ae93be5ad8e35067d8b2ac81b9ce"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "43a4a8265fb24f3279d6cde3",
|
||||
"out": "f6342d8150996d93ca43369a69a502f14dde041f"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "5cc368c293058fa49906602624",
|
||||
"out": "893a6a07ee0a2669baf707ed3ba2376125ef9654"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "0a52feeb91c5d0355fbb35e93f5d",
|
||||
"out": "5162732ef94afe500679f550cbecf8043b70ada0"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "a3fc5e1467219db6add73f59808955",
|
||||
"out": "3dd7b8c0d2278884b5a5c22a3caa061131a5ad7a"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "ed45b0bc151b3a739d710ab3af72a32b",
|
||||
"out": "5ba207c3a45289463c98a21e2138c3758d2ddc4a"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "af7f01692a1aa195e2a3856dd936285f2b",
|
||||
"out": "d744fc79d37aa33e16c60dd7973b7e0d1e790828"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "5028d7983e49966980ac8a564caeddcce890",
|
||||
"out": "5906becdaf00a5c700d548efa92fb23fcd1c8d6f"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "c17757bf03b8661b4407d06e4e47e8b6c678fb",
|
||||
"out": "2b7202910cdbd3cda240affaa5929d6f82febc48"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "5c4172923580ed789714245e42fda873f405ee97",
|
||||
"out": "053dafe5970069c38f5575a54c4bbad3fdc34dbc"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "74f77522ad9fbc0e45da88354ee4f5552eac5f02f7",
|
||||
"out": "0b72df59fcc468cc4bef4b0a77da59dd4ea4486b"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "33a75fb9cdbfbc9a257b122be7ed337bd65cb1ffe9a8",
|
||||
"out": "1b8a94bcefe1e6cd9000a70ab342fe7930e62220"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "8d7042cbdcd336c86e7732a1e633bdcc137380afc37d25",
|
||||
"out": "f18c667a28aa595890e689d6cd19fae3f9fb7d7c"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "d9419ba075c613f701e19de6449e21a22fe4bb58067a5b0c",
|
||||
"out": "d656a0f985e186388c2a211ae0ec8ea30b059505"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "8e5a281fa95e7e9b42ec0cb0b9a35b54851257ac78494078b4",
|
||||
"out": "aebecedafad9edfdc204ac1303d14f549ee12ccc"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "3e01a429466e67653b7b07751f8e497929a0de5515fba7d69fd6",
|
||||
"out": "1640abee310d1860d7944f0b9929d3e4264ad358"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "1cf65ab71ad544d423cb904f064a5d227a01761d6a6188296201e1",
|
||||
"out": "0181503434c565b41eff7c432e220bcc290f0a71"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "5781599b64981d4657b3c4751da4d7594bf446e386621ec838c30d84",
|
||||
"out": "07d728840ab1449d31cbadcd5466d887edfd4650"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "44c392b435a8ce90062f7ba756d23fd7a3582f23c61dbe8a06bcaa2b62",
|
||||
"out": "4adaccc7626aabf56320040e42174bf284d4333c"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "053aee544bd93733c6faa8af9b89d1d7c1067832273f23eb6bb5d1d7abf4",
|
||||
"out": "a993d46bfd0705cb8692f6d6b88ae5dcb20cbcce"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "7a641e5243317176e497ddd286e47a2a36def6451a4a44aeba12c03cfefa57",
|
||||
"out": "c0ceed6ac5a60323ef0b6d07535ad5264eef0aff"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "3bd0af34c27efca58ce047f265f1cfc8be60280cbfa57225afe6b353ec1b1b86",
|
||||
"out": "aed53c23187adcf3c2d85fe77aa3ffa7cacc85ad"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "5c861109b9a1fdfb6e9b61737461f722e6582c94c9b406ded106dff2fbacdf5b07",
|
||||
"out": "ca900eed8f00a8144c60756317ca7b23fe5797de"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "d0416b0ba3b19ee355186ac2a09acfe91964f229de3d4119ad5ab4a09cf0fe10dd2a",
|
||||
"out": "b2421a5314d8311c18773decc2798300a36ce63d"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "f7077210023297cf642c3755338ebd2ca234c22269ffad31a7bc6b12ebbffccede293d",
|
||||
"out": "797538f704ef4fb5d7e93dde8b9c742eab19010f"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "169ed88895e743a171581730d9ce32b90e557c2ac178f00ad15c13b2639bea59ab5cf95b",
|
||||
"out": "2ba71c7b2be43af1825a3bbc7dccd104a8f8ab74"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "747ef1d2ffe2942444429b7b6d46631c7bc8c00a63d44176014e8594d9f2e09451e956b0f7",
|
||||
"out": "5eedcddccab146ba30c9613d5b4825ddf6d0df57"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "546e13efd694f71819385ee6f9a8022eb57b0e6173ad2042984aacbb6cf506102317ddda6773",
|
||||
"out": "5bcde7dee110935114f5422194178110a0c8558e"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "8d0ad66f63ee8211654eb12362f5ce0f7a0672a3e77a0f3fbb6e6986c13abd2debd6e7f916fc7f",
|
||||
"out": "54fd721965d4c6dbebe9e3d2b5a334957ea3f0bc"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "6120f98d23ddabf5ca10022cf22a58cc89cd516f6b9ad1901a6c450b89569bd75b6ed43d1ab1ed1f",
|
||||
"out": "560fd1cf282e07a4fbbcb495ea33c550cec1cc1a"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "7497fea1d85825b29dbece7c90f52b1b4760393693b31f56eb366571d49cb6b0ee854bde58baa64fbe",
|
||||
"out": "b61aa965cc9a9a1ae9a7cc0087e7417cf942a0a2"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "2470b0926239d1a1b94f85204edde011000e3d08db9f59e86a8cf7217c48c5dbe5cd00480ffe00a22b89",
|
||||
"out": "81586567893ffa39a8079018f46b9ff14811c590"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "56199f4131af9aef9bb9f1cefd8552e75e1b607770a461e6deba3664972908df469b7e065d0db531608171",
|
||||
"out": "6b74a4ab1c06e029004f14db759c344dbb770b1b"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "554809f00123973a8308b8d3d5f10ee4e5514da51f615e1f606957e1febc87ba9c369f6ecf29f99cdd57733f",
|
||||
"out": "fbf816d9a81a8605487f6529255ddc43d031065b"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "4315b59de93ec8ab4bcd8efd4fee3b87ca4194c24211ea0e79055ddb1fba7511203fd56c0154ae12db25abb434",
|
||||
"out": "f465d07419a5f3aade344ba5d64a5133eba1b873"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "7c2d0a5131f7c83789b1cba64e5e6b3485e3340ada2135008fbf1862bf711c7a65592fd4a49c511e29cf910003f5",
|
||||
"out": "c9d8494fddf45d298164d5bda3df18ab29434d58"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "7597db5290120386e158890c7bcd70dfffc100184a8339f98e7ebb17b23b6a88772f4a05f2e0a7b2059dea991bf154",
|
||||
"out": "ce9ab0cf93f471ab913043df76270514ad281ef7"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "b17ba36d07623609ada5cf2d8f878025f02c3a4f578021bdec12345a53780c71ff9ad5f280bf8e1039e5643389f478ee",
|
||||
"out": "2b42fe8018b985141e4ec9604f2c75d454155ee5"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "ab1fd81a1d2128a9767132079c36929bd76336157054fa889ad09b117dd753166cdc9ae51eea15ddf8d7b3113f8534df77",
|
||||
"out": "22954d720f8259b5f04d288a453dfb54712be71c"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "53ff3194fd863938021f2033f2e31f508af10e8d0b309fc2e3477495141732bdbda9cba44f83b5459fca222a894783741c7b",
|
||||
"out": "544e6cace0c57a4af18f0913d951191869ef933d"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "160ea25128e0509398038a0f490ba73c62a03e58997174594fda1b45924f406dd3bce56253021b446d27ed16cd7ac0eefeaa48",
|
||||
"out": "6a56c5cb8cb5f4e64ef06fb32d94188884682a2d"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "f18ce863efd88f83ecdaa3acc57786ad1a961b31446436d4e8f86f5999d475ea783ba3473d09c60d0ca6fe3d8bc16d5b5460c296",
|
||||
"out": "d85dc07c16a44c4defef238ebfdb7fd14c73a788"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "890d6a3eb3eaa9c75fb27d643257a8196387a5a49556ca3e81351b2668e56fe1d0e15ba1139c59b48f879664f6cb3b80a9baba6ded",
|
||||
"out": "50f41fff64a49d8753883f71792305a0eb0ecbc3"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "370a044bc71750bbcd57491db54dd6335f426299a3fdd0fbf6c419856cf2fec70fbbaaf6ad0adc2b31681be7437f1de7d02393034e00",
|
||||
"out": "a31c9760489983399ea803bc7945914b7ca59142"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "64bf8d78e7e1ba0f32f6d4d4f1aa68b12965a7f942b2b152a47869750ddab6f1f33c82ba1e75ef76f1b2d9769016cbac063717fb06184e",
|
||||
"out": "e2401f5aa89927a7f51d4d6345b482b135edbd32"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "f434cdf6be6104121a533fb7f7d4f100ff1f0f6ddaa76297a32bdae535cf040cb38be51cddb96107e1045cf24444bf482d9ea131cd163a53",
|
||||
"out": "d95ab29a9f3c2a2db6374ff7a0deecee521ba792"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "1b99beb5458c96d9d402d85cb1ecb797d352ac8c04f3ffaca76a33b365ceedce9adba53a5bf381eca73d863da273fbc47fb3f9a9f6f25829c7",
|
||||
"out": "220f85e79731c094c8d457cf97296f89efe404e6"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "034a75357af6255f0b55856fa240fdcdebafadbfbcb500d17e1830a3d074b252ab2777333a9381d63dbd4db685a72a67e41825b331f0e183daa9",
|
||||
"out": "a0671c3badc8d07c59f6551017b00211b3c35b9c"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "9c17ddd3617c5522491deb7ff1bf56076acd10ee7c482d606247ebd6d67f8062a3e9ecf29d938e04879c062ffea08c2f87f3be840c50294ffa1de9",
|
||||
"out": "47fad7e0d17a05345e130fdf458071900e90371d"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "147c4fdd4bb30f38661c0a4c57cfccf5de5369b089edcb74af24e814ad4bc17a466fbd2501e745ae16d2d0c976d3dcf2f93e2e7088733bc6f4013f28",
|
||||
"out": "fca67427fac3bfff73fbc789a4d8fc0058b1729a"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "719f6946f69e185fbdd545ef118f9527630025acf9b62df5c38e69c9855c7b63e905eefd099e0635d2c40815a01b5ea79585da3e4f1a2ec97c46dd1f00",
|
||||
"out": "7eef5539213a672c7599b489b0f80b954b0e9d1d"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "997c62cf0780fdd3164780d38ba784905e4a87bf1da252e13f6c320040193e5cad1eab40a6db7291df54952c1dbe3b76541856d2b71591d0794435243372",
|
||||
"out": "7e882963afd9163ac13f7e4c1a7dca9fc4c6ccf7"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "d93d8e8987e0d01bdaa37b1afc53a07c5a7f46273d1f77a472d83d0f9d9b241599afc51846c16e33d1c1e2e532bed264c49b2a9919f324834b097509933798",
|
||||
"out": "fd21687491f7f8d2a8edd82db8f08137d6c1499f"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "cc54b091b967d610be6d6d889f909f843419091cdae2c4447b8c830045b12760964ad8d05eb0285a428baca3e7b7398a37f7982238288732710ec97b5e32122a",
|
||||
"out": "bb391a32d4f84199a444f6e9476b908209044624"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "61b9077fd4a0e2e73060b4b26e86fc7a8131e0e305a5d874bbcd28bedfa46fa84dd89431fef6e16e4b5b3781956dfdff1c1843db12329f6c1c365f9a91b3a1f15b",
|
||||
"out": "e3d6e5f7196b310a56089e76220a59d523dda37a"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "9caffbb1325c6579b094c4ee8f5b1eb9ccdc5ee861ffa090e055ff2ce6323295afca2de193f15e5fe64ab7ac655b9e99145ce1786f266bfac834a0ff367fd174540c",
|
||||
"out": "954d88771139f64710e8f31a0b990e8a1e569a74"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "bc15671078b9ad440eb3c16fe28a33216db848f19a289a6c805655053311a2299f9c7d091d3aa130e0df2e318e75402d54528a9c67fb4b14d754d4dc41e1c06787669e",
|
||||
"out": "69f3a729309b288ee077582807dbeec2ed5d4360"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "",
|
||||
"out": "9c1185a5c5e9fc54612808977ee8f548b2258d31"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "61",
|
||||
"out": "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "616263",
|
||||
"out": "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "6d65737361676520646967657374",
|
||||
"out": "5d0689ef49d2fae572b881b123a85ffa21595f36"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "6162636465666768696a6b6c6d6e6f707172737475767778797a",
|
||||
"out": "f71c27109c692c1b56bbdceb5b9d2865b3708dbc"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "6162636462636465636465666465666765666768666768696768696a68696a6b696a6b6c6a6b6c6d6b6c6d6e6c6d6e6f6d6e6f706e6f7071",
|
||||
"out": "12a053384a9c0c88e405a06c27dcf49ada62eb2b"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a30313233343536373839",
|
||||
"out": "b0e20b6e3116640286ed3a87a5713079b21f5189"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930",
|
||||
"out": "9b752e45573d4b39f4dbd3323cab82bf63326bfb"
|
||||
},
|
||||
|
||||
{
|
||||
"in": "35b8a2f1b5f332f159afb86212dcc4de02b3fba3794b0e5fe74a4ed9f1a55f55ae7711c6fee3178a410e3979d8b07594d769ad733ea00d64dcc1d601b871f918f1fdd914c2b6f9ec49b8ac13a62a07e1091e0dd024e645bf3b602b4706583060cf9502e4ea6911ac9eb90e13816530b7683babaae225781440c35e8031515c45d5e4f5493faed6b0457dd661b713895fb9d22646ecad7c07187b65aa508fe4f5b15a5c428264910143581512dcfbda974902037d82451e4b1e12ad3364c8f848e2b2a7d53d25f30c06aa6c62784de8b8b6d097559c62d340c8103d51e0c303b1781f5a95aa6ad1b7efe3db9e8d8be9d1271afc4da9b0928642818965c922ddbb0b0a53e6d2ece3672e9568d0e285177bc5e5a9c2349788f8987267ad5ed26092a552ad3674344de7fd79b9e33b855da47f8ca0a70ee806945a6417c92c97cdc14c6bd9b7b25a2d89feb62c594fd11a7cf3bea1ef3018b26b0e401ea8c7b8f07dac07b77acb78c3f45a73ba9fc32301eb3a19dff000f3413ae0e925481d8f93d387cd458d749ba848bd4a92b9b7aeb0e244e479abc4a85f50e94c7299e06bf0daa1587f49907cd94c426c33acfc31e08d183a4353a36d8c6d39aaa1af20519629c01a45bdcd60b115cd490e9873d74fbea994b75bb7fee9f2167850afaf42873d44543e3d6359a0a0a140705d4021de256ab12c38184bf822b9b69d7fb059c32ee332d86ab9df26d1cf832381555cad1688a362bdb1f2982a55aaa37e5137b95b5961e8f354ea30ecb13b98dc697605556df4c4eede6593016b7bc30862c55c6651da8a3589dd3e9ee42495374ba7e6c6bdeff8cc078559dcd99581061348b13fae03259832ead8bb84fab48ebf725c6c5037539fc373791f21d34b56de04a6cbc591b798b7cae2ad8bd5bf9e963559c981286f26802fd1a49a5a906ab814b34fb514a8ed0fec7b67a550a59fbeda7b0685e69add13b68a69e070cf2c9e8d6cf4caee4aba732ba20a8ba6c3db8bffd392cc2d14d46367d451f0a3b77a411b128224aa473247c4a9a446606cea8414e9c09de13301c8e60b2ee51e9801e1cffc6e9ef283238e2ad209e5d48105d48044bfd28bd9f32c53d5d37ccb158fe597983cbb45a05f1b9148f25772ecf3667554247cdb2a95a99e06275c36c6e4fd779dc9f8fa457516ea4cd020f6726e3a32df65e34bd85ddfe16b285a0afb0f3e2dd8954a9e636bca5cf7fed181b041d14d223f10ad0de9e23372dd7a61718cab6b9cc7c1d88d98697356811ac0b834228a299f6eef970e11d59e9fd307dacd40256ad7beb203175ccabf70d9137bc29e2c7b0494c3fa6d338b027bb993e870b02ec68a08ec8a24bd163d5ab43411ab7bcf1a09f64a7eafe15a0c350f5d6f0e68c1a83fece5227112f8e6f8ef11149b3188970a279c9559bf11e7a4ef9f72c9c1e9b58ee48ff880aa",
|
||||
"out": "b48c57caa63ffcc261e4b3532e75cba497b4fb6c"
|
||||
}
|
||||
]
|
||||
}
|
||||
1564
packages/crypto/src/testdata/sha.json
vendored
Normal file
1564
packages/crypto/src/testdata/sha.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
packages/crypto/tsconfig.json
Normal file
12
packages/crypto/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "build",
|
||||
"declarationDir": "build/types",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
14
packages/crypto/typedoc.js
Normal file
14
packages/crypto/typedoc.js
Normal file
@ -0,0 +1,14 @@
|
||||
const packageJson = require("./package.json");
|
||||
|
||||
module.exports = {
|
||||
src: ["./src"],
|
||||
out: "docs",
|
||||
exclude: "**/*.spec.ts",
|
||||
target: "es6",
|
||||
name: `${packageJson.name} Documentation`,
|
||||
readme: "README.md",
|
||||
mode: "file",
|
||||
excludeExternals: true,
|
||||
excludeNotExported: true,
|
||||
excludePrivate: true,
|
||||
};
|
||||
7
packages/crypto/types/bip39.d.ts
vendored
Normal file
7
packages/crypto/types/bip39.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import { EnglishMnemonic } from "./englishmnemonic";
|
||||
export declare class Bip39 {
|
||||
static encode(entropy: Uint8Array): EnglishMnemonic;
|
||||
static decode(mnemonic: EnglishMnemonic): Uint8Array;
|
||||
static mnemonicToSeed(mnemonic: EnglishMnemonic, password?: string): Promise<Uint8Array>;
|
||||
private static pbkdf2;
|
||||
}
|
||||
7
packages/crypto/types/englishmnemonic.d.ts
vendored
Normal file
7
packages/crypto/types/englishmnemonic.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
export declare class EnglishMnemonic {
|
||||
static readonly wordlist: readonly string[];
|
||||
private static readonly mnemonicMatcher;
|
||||
private readonly data;
|
||||
constructor(mnemonic: string);
|
||||
toString(): string;
|
||||
}
|
||||
5
packages/crypto/types/hash.d.ts
vendored
Normal file
5
packages/crypto/types/hash.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export interface HashFunction {
|
||||
readonly blockSize: number;
|
||||
readonly update: (_: Uint8Array) => HashFunction;
|
||||
readonly digest: () => Uint8Array;
|
||||
}
|
||||
11
packages/crypto/types/hmac.d.ts
vendored
Normal file
11
packages/crypto/types/hmac.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import { HashFunction } from "./hash";
|
||||
export declare class Hmac<H extends HashFunction> implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly messageHasher;
|
||||
private readonly oKeyPad;
|
||||
private readonly iKeyPad;
|
||||
private readonly hash;
|
||||
constructor(hashFunctionConstructor: new () => H, originalKey: Uint8Array);
|
||||
update(data: Uint8Array): Hmac<H>;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
30
packages/crypto/types/index.d.ts
vendored
Normal file
30
packages/crypto/types/index.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
export { Bip39 } from "./bip39";
|
||||
export { EnglishMnemonic } from "./englishmnemonic";
|
||||
export { HashFunction } from "./hash";
|
||||
export { Hmac } from "./hmac";
|
||||
export { Keccak256 } from "./keccak";
|
||||
export {
|
||||
Xchacha20poly1305Ietf,
|
||||
Xchacha20poly1305IetfCiphertext,
|
||||
Xchacha20poly1305IetfKey,
|
||||
Xchacha20poly1305IetfMessage,
|
||||
Xchacha20poly1305IetfNonce,
|
||||
Argon2id,
|
||||
Argon2idOptions,
|
||||
Ed25519,
|
||||
Ed25519Keypair,
|
||||
} from "./libsodium";
|
||||
export { Random } from "./random";
|
||||
export { Ripemd160 } from "./ripemd";
|
||||
export { Secp256k1, Secp256k1Keypair } from "./secp256k1";
|
||||
export { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
export { Sha1, Sha256, Sha512 } from "./sha";
|
||||
export {
|
||||
pathToString,
|
||||
stringToPath,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
Slip10RawIndex,
|
||||
Slip10Result,
|
||||
slip10CurveFromString,
|
||||
} from "./slip10";
|
||||
8
packages/crypto/types/keccak.d.ts
vendored
Normal file
8
packages/crypto/types/keccak.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import { HashFunction } from "./hash";
|
||||
export declare class Keccak256 implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly impl;
|
||||
constructor(firstData?: Uint8Array);
|
||||
update(data: Uint8Array): Keccak256;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
45
packages/crypto/types/libsodium.d.ts
vendored
Normal file
45
packages/crypto/types/libsodium.d.ts
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
import { As } from "type-tagger";
|
||||
export declare type Xchacha20poly1305IetfKey = Uint8Array & As<"xchacha20poly1305ietf-key">;
|
||||
export declare type Xchacha20poly1305IetfMessage = Uint8Array & As<"xchacha20poly1305ietf-message">;
|
||||
export declare type Xchacha20poly1305IetfNonce = Uint8Array & As<"xchacha20poly1305ietf-nonce">;
|
||||
export declare type Xchacha20poly1305IetfCiphertext = Uint8Array & As<"xchacha20poly1305ietf-ciphertext">;
|
||||
export interface Argon2idOptions {
|
||||
readonly outputLength: number;
|
||||
readonly opsLimit: number;
|
||||
readonly memLimitKib: number;
|
||||
}
|
||||
export declare class Argon2id {
|
||||
static execute(password: string, salt: Uint8Array, options: Argon2idOptions): Promise<Uint8Array>;
|
||||
}
|
||||
export declare class Ed25519Keypair {
|
||||
static fromLibsodiumPrivkey(libsodiumPrivkey: Uint8Array): Ed25519Keypair;
|
||||
readonly privkey: Uint8Array;
|
||||
readonly pubkey: Uint8Array;
|
||||
constructor(privkey: Uint8Array, pubkey: Uint8Array);
|
||||
toLibsodiumPrivkey(): Uint8Array;
|
||||
}
|
||||
export declare class Ed25519 {
|
||||
/**
|
||||
* Generates a keypair deterministically from a given 32 bytes seed.
|
||||
*
|
||||
* This seed equals the Ed25519 private key.
|
||||
* For implementation details see crypto_sign_seed_keypair in
|
||||
* https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures.html
|
||||
* and diagram on https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
|
||||
*/
|
||||
static makeKeypair(seed: Uint8Array): Promise<Ed25519Keypair>;
|
||||
static createSignature(message: Uint8Array, keyPair: Ed25519Keypair): Promise<Uint8Array>;
|
||||
static verifySignature(signature: Uint8Array, message: Uint8Array, pubkey: Uint8Array): Promise<boolean>;
|
||||
}
|
||||
export declare class Xchacha20poly1305Ietf {
|
||||
static encrypt(
|
||||
message: Xchacha20poly1305IetfMessage,
|
||||
key: Xchacha20poly1305IetfKey,
|
||||
nonce: Xchacha20poly1305IetfNonce,
|
||||
): Promise<Xchacha20poly1305IetfCiphertext>;
|
||||
static decrypt(
|
||||
ciphertext: Xchacha20poly1305IetfCiphertext,
|
||||
key: Xchacha20poly1305IetfKey,
|
||||
nonce: Xchacha20poly1305IetfNonce,
|
||||
): Promise<Xchacha20poly1305IetfMessage>;
|
||||
}
|
||||
6
packages/crypto/types/random.d.ts
vendored
Normal file
6
packages/crypto/types/random.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export declare class Random {
|
||||
/**
|
||||
* Returns `count` cryptographically secure random bytes
|
||||
*/
|
||||
static getBytes(count: number): Uint8Array;
|
||||
}
|
||||
8
packages/crypto/types/ripemd.d.ts
vendored
Normal file
8
packages/crypto/types/ripemd.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import { HashFunction } from "./hash";
|
||||
export declare class Ripemd160 implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly impl;
|
||||
constructor(firstData?: Uint8Array);
|
||||
update(data: Uint8Array): Ripemd160;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
20
packages/crypto/types/secp256k1.d.ts
vendored
Normal file
20
packages/crypto/types/secp256k1.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
import { As } from "type-tagger";
|
||||
import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";
|
||||
interface Keypair {
|
||||
readonly pubkey: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
}
|
||||
export declare type Secp256k1Keypair = Keypair & As<"secp256k1-keypair">;
|
||||
export declare class Secp256k1 {
|
||||
static makeKeypair(privkey: Uint8Array): Promise<Secp256k1Keypair>;
|
||||
static createSignature(messageHash: Uint8Array, privkey: Uint8Array): Promise<ExtendedSecp256k1Signature>;
|
||||
static verifySignature(
|
||||
signature: Secp256k1Signature,
|
||||
messageHash: Uint8Array,
|
||||
pubkey: Uint8Array,
|
||||
): Promise<boolean>;
|
||||
static recoverPubkey(signature: ExtendedSecp256k1Signature, messageHash: Uint8Array): Uint8Array;
|
||||
static compressPubkey(pubkey: Uint8Array): Uint8Array;
|
||||
static trimRecoveryByte(signature: Uint8Array): Uint8Array;
|
||||
}
|
||||
export {};
|
||||
34
packages/crypto/types/secp256k1signature.d.ts
vendored
Normal file
34
packages/crypto/types/secp256k1signature.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
export declare class Secp256k1Signature {
|
||||
/**
|
||||
* Takes the pair of integers (r, s) as 2x32 byte of binary data.
|
||||
*
|
||||
* Note: This is the format Cosmos SDK uses natively.
|
||||
*
|
||||
* @param data a 64 byte value containing integers r and s.
|
||||
*/
|
||||
static fromFixedLength(data: Uint8Array): Secp256k1Signature;
|
||||
static fromDer(data: Uint8Array): Secp256k1Signature;
|
||||
private readonly data;
|
||||
constructor(r: Uint8Array, s: Uint8Array);
|
||||
r(length?: number): Uint8Array;
|
||||
s(length?: number): Uint8Array;
|
||||
toDer(): Uint8Array;
|
||||
}
|
||||
/**
|
||||
* A Secp256k1Signature plus the recovery parameter
|
||||
*/
|
||||
export declare class ExtendedSecp256k1Signature extends Secp256k1Signature {
|
||||
/**
|
||||
* Decode extended signature from the simple fixed length encoding
|
||||
* described in toFixedLength().
|
||||
*/
|
||||
static fromFixedLength(data: Uint8Array): ExtendedSecp256k1Signature;
|
||||
readonly recovery: number;
|
||||
constructor(r: Uint8Array, s: Uint8Array, recovery: number);
|
||||
/**
|
||||
* A simple custom encoding that encodes the extended signature as
|
||||
* r (32 bytes) | s (32 bytes) | recovery param (1 byte)
|
||||
* where | denotes concatenation of bonary data.
|
||||
*/
|
||||
toFixedLength(): Uint8Array;
|
||||
}
|
||||
22
packages/crypto/types/sha.d.ts
vendored
Normal file
22
packages/crypto/types/sha.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
import { HashFunction } from "./hash";
|
||||
export declare class Sha1 implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly impl;
|
||||
constructor(firstData?: Uint8Array);
|
||||
update(data: Uint8Array): Sha1;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
export declare class Sha256 implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly impl;
|
||||
constructor(firstData?: Uint8Array);
|
||||
update(data: Uint8Array): Sha256;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
export declare class Sha512 implements HashFunction {
|
||||
readonly blockSize: number;
|
||||
private readonly impl;
|
||||
constructor(firstData?: Uint8Array);
|
||||
update(data: Uint8Array): Sha512;
|
||||
digest(): Uint8Array;
|
||||
}
|
||||
40
packages/crypto/types/slip10.d.ts
vendored
Normal file
40
packages/crypto/types/slip10.d.ts
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
import { Uint32 } from "@cosmjs/math";
|
||||
export interface Slip10Result {
|
||||
readonly chainCode: Uint8Array;
|
||||
readonly privkey: Uint8Array;
|
||||
}
|
||||
/**
|
||||
* Raw values must match the curve string in SLIP-0010 master key generation
|
||||
*
|
||||
* @see https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation
|
||||
*/
|
||||
export declare enum Slip10Curve {
|
||||
Secp256k1 = "Bitcoin seed",
|
||||
Ed25519 = "ed25519 seed",
|
||||
}
|
||||
/**
|
||||
* Reverse mapping of Slip10Curve
|
||||
*/
|
||||
export declare function slip10CurveFromString(curveString: string): Slip10Curve;
|
||||
export declare class Slip10RawIndex extends Uint32 {
|
||||
static hardened(hardenedIndex: number): Slip10RawIndex;
|
||||
static normal(normalIndex: number): Slip10RawIndex;
|
||||
isHardened(): boolean;
|
||||
}
|
||||
export declare class Slip10 {
|
||||
static derivePath(curve: Slip10Curve, seed: Uint8Array, path: readonly Slip10RawIndex[]): Slip10Result;
|
||||
private static master;
|
||||
private static child;
|
||||
/**
|
||||
* Implementation of ser_P(point(k_par)) from BIP-0032
|
||||
*
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
*/
|
||||
private static serializedPoint;
|
||||
private static childImpl;
|
||||
private static isZero;
|
||||
private static isGteN;
|
||||
private static n;
|
||||
}
|
||||
export declare function pathToString(path: readonly Slip10RawIndex[]): string;
|
||||
export declare function stringToPath(input: string): readonly Slip10RawIndex[];
|
||||
17
packages/crypto/webpack.web.config.js
Normal file
17
packages/crypto/webpack.web.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
const glob = require("glob");
|
||||
const path = require("path");
|
||||
|
||||
const target = "web";
|
||||
const distdir = path.join(__dirname, "dist", "web");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
// bundle used for Karma tests
|
||||
target: target,
|
||||
entry: glob.sync("./build/**/*.spec.js"),
|
||||
output: {
|
||||
path: distdir,
|
||||
filename: "tests.js",
|
||||
},
|
||||
},
|
||||
];
|
||||
8
packages/encoding/.eslintignore
Normal file
8
packages/encoding/.eslintignore
Normal file
@ -0,0 +1,8 @@
|
||||
node_modules/
|
||||
|
||||
build/
|
||||
custom_types/
|
||||
dist/
|
||||
docs/
|
||||
generated/
|
||||
types/
|
||||
3
packages/encoding/.gitignore
vendored
Normal file
3
packages/encoding/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
build/
|
||||
dist/
|
||||
docs/
|
||||
24
packages/encoding/README.md
Normal file
24
packages/encoding/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# @cosmjs/encoding
|
||||
|
||||
[](https://www.npmjs.com/package/@cosmjs/encoding)
|
||||
|
||||
This package is an extension to the JavaScript standard library that is not
|
||||
bound to blockchain products. It provides basic hex/base64/ascii encoding to
|
||||
Uint8Array that doesn't rely on Buffer and also provides better error messages
|
||||
on invalid input.
|
||||
|
||||
## Convert between bech32 and hex addresses
|
||||
|
||||
```
|
||||
>> Bech32.encode("tiov", fromHex("1234ABCD0000AA0000FFFF0000AA00001234ABCD"))
|
||||
'tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea'
|
||||
>> toHex(Bech32.decode("tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea").data)
|
||||
'1234abcd0000aa0000ffff0000aa00001234abcd'
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This package is part of the cosmjs repository, licensed under the Apache
|
||||
License 2.0 (see
|
||||
[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and
|
||||
[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)).
|
||||
26
packages/encoding/jasmine-testrunner.js
Executable file
26
packages/encoding/jasmine-testrunner.js
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require("source-map-support").install();
|
||||
const defaultSpecReporterConfig = require("../../jasmine-spec-reporter.config.json");
|
||||
|
||||
// setup Jasmine
|
||||
const Jasmine = require("jasmine");
|
||||
const jasmine = new Jasmine();
|
||||
jasmine.loadConfig({
|
||||
spec_dir: "build",
|
||||
spec_files: ["**/*.spec.js"],
|
||||
helpers: [],
|
||||
random: false,
|
||||
seed: null,
|
||||
stopSpecOnExpectationFailure: false,
|
||||
});
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000;
|
||||
|
||||
// setup reporter
|
||||
const { SpecReporter } = require("jasmine-spec-reporter");
|
||||
const reporter = new SpecReporter({ ...defaultSpecReporterConfig });
|
||||
|
||||
// initialize and execute
|
||||
jasmine.env.clearReporters();
|
||||
jasmine.addReporter(reporter);
|
||||
jasmine.execute();
|
||||
47
packages/encoding/karma.conf.js
Normal file
47
packages/encoding/karma.conf.js
Normal file
@ -0,0 +1,47 @@
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: ".",
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ["jasmine"],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: ["dist/web/tests.js"],
|
||||
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false,
|
||||
timeoutInterval: 15000,
|
||||
},
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ["progress", "kjhtml"],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ["Firefox"],
|
||||
|
||||
browserNoActivityTimeout: 90000,
|
||||
|
||||
// Keep brower open for debugging. This is overridden by yarn scripts
|
||||
singleRun: false,
|
||||
});
|
||||
};
|
||||
1
packages/encoding/nonces/README.txt
Normal file
1
packages/encoding/nonces/README.txt
Normal file
@ -0,0 +1 @@
|
||||
Directory used to trigger lerna package updates for all packages
|
||||
48
packages/encoding/package.json
Normal file
48
packages/encoding/package.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@cosmjs/encoding",
|
||||
"version": "0.8.0",
|
||||
"description": "Encoding helpers for blockchain projects",
|
||||
"contributors": ["IOV SAS <admin@iov.one>"],
|
||||
"license": "Apache-2.0",
|
||||
"main": "build/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"build/",
|
||||
"types/",
|
||||
"*.md",
|
||||
"!*.spec.*",
|
||||
"!**/testdata/"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/encoding"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"docs": "shx rm -rf docs && typedoc --options typedoc.js",
|
||||
"lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"",
|
||||
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
|
||||
"format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"",
|
||||
"test-node": "node jasmine-testrunner.js",
|
||||
"test-edge": "yarn pack-web && karma start --single-run --browsers Edge",
|
||||
"test-firefox": "yarn pack-web && karma start --single-run --browsers Firefox",
|
||||
"test-chrome": "yarn pack-web && karma start --single-run --browsers ChromeHeadless",
|
||||
"test-safari": "yarn pack-web && karma start --single-run --browsers Safari",
|
||||
"test": "yarn build-or-skip && yarn test-node",
|
||||
"move-types": "shx rm -r ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts",
|
||||
"format-types": "prettier --write --loglevel warn \"./types/**/*.d.ts\"",
|
||||
"build": "shx rm -rf ./build && tsc && yarn move-types && yarn format-types",
|
||||
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
|
||||
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.0",
|
||||
"bech32": "^1.1.4",
|
||||
"readonly-date": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/base64-js": "^1.2.5"
|
||||
}
|
||||
}
|
||||
26
packages/encoding/src/ascii.spec.ts
Normal file
26
packages/encoding/src/ascii.spec.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { fromAscii, toAscii } from "./ascii";
|
||||
|
||||
describe("ascii", () => {
|
||||
it("encodes to ascii", () => {
|
||||
expect(toAscii("")).toEqual(new Uint8Array([]));
|
||||
expect(toAscii("abc")).toEqual(new Uint8Array([0x61, 0x62, 0x63]));
|
||||
expect(toAscii(" ?=-n|~+-*/\\")).toEqual(
|
||||
new Uint8Array([0x20, 0x3f, 0x3d, 0x2d, 0x6e, 0x7c, 0x7e, 0x2b, 0x2d, 0x2a, 0x2f, 0x5c]),
|
||||
);
|
||||
|
||||
expect(() => toAscii("ö")).toThrow();
|
||||
expect(() => toAscii("ß")).toThrow();
|
||||
});
|
||||
|
||||
it("decodes from ascii", () => {
|
||||
expect(fromAscii(new Uint8Array([]))).toEqual("");
|
||||
expect(fromAscii(new Uint8Array([0x61, 0x62, 0x63]))).toEqual("abc");
|
||||
expect(
|
||||
fromAscii(new Uint8Array([0x20, 0x3f, 0x3d, 0x2d, 0x6e, 0x7c, 0x7e, 0x2b, 0x2d, 0x2a, 0x2f, 0x5c])),
|
||||
).toEqual(" ?=-n|~+-*/\\");
|
||||
|
||||
expect(() => fromAscii(new Uint8Array([0x00]))).toThrow();
|
||||
expect(() => fromAscii(new Uint8Array([0x7f]))).toThrow();
|
||||
expect(() => fromAscii(new Uint8Array([0xff]))).toThrow();
|
||||
});
|
||||
});
|
||||
31
packages/encoding/src/ascii.ts
Normal file
31
packages/encoding/src/ascii.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export function toAscii(input: string): Uint8Array {
|
||||
const toNums = (str: string): readonly number[] =>
|
||||
str.split("").map((x: string) => {
|
||||
const charCode = x.charCodeAt(0);
|
||||
// 0x00–0x1F control characters
|
||||
// 0x20–0x7E printable characters
|
||||
// 0x7F delete character
|
||||
// 0x80–0xFF out of 7 bit ascii range
|
||||
if (charCode < 0x20 || charCode > 0x7e) {
|
||||
throw new Error("Cannot encode character that is out of printable ASCII range: " + charCode);
|
||||
}
|
||||
return charCode;
|
||||
});
|
||||
return Uint8Array.from(toNums(input));
|
||||
}
|
||||
|
||||
export function fromAscii(data: Uint8Array): string {
|
||||
const fromNums = (listOfNumbers: readonly number[]): readonly string[] =>
|
||||
listOfNumbers.map((x: number): string => {
|
||||
// 0x00–0x1F control characters
|
||||
// 0x20–0x7E printable characters
|
||||
// 0x7F delete character
|
||||
// 0x80–0xFF out of 7 bit ascii range
|
||||
if (x < 0x20 || x > 0x7e) {
|
||||
throw new Error("Cannot decode character that is out of printable ASCII range: " + x);
|
||||
}
|
||||
return String.fromCharCode(x);
|
||||
});
|
||||
|
||||
return fromNums(Array.from(data)).join("");
|
||||
}
|
||||
65
packages/encoding/src/base64.spec.ts
Normal file
65
packages/encoding/src/base64.spec.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { fromBase64, toBase64 } from "./base64";
|
||||
|
||||
describe("base64", () => {
|
||||
it("encodes to base64", () => {
|
||||
expect(toBase64(new Uint8Array([]))).toEqual("");
|
||||
expect(toBase64(new Uint8Array([0x00]))).toEqual("AA==");
|
||||
expect(toBase64(new Uint8Array([0x00, 0x00]))).toEqual("AAA=");
|
||||
expect(toBase64(new Uint8Array([0x00, 0x00, 0x00]))).toEqual("AAAA");
|
||||
expect(toBase64(new Uint8Array([0x00, 0x00, 0x00, 0x00]))).toEqual("AAAAAA==");
|
||||
expect(toBase64(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00]))).toEqual("AAAAAAA=");
|
||||
expect(toBase64(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))).toEqual("AAAAAAAA");
|
||||
expect(toBase64(new Uint8Array([0x61]))).toEqual("YQ==");
|
||||
expect(toBase64(new Uint8Array([0x62]))).toEqual("Yg==");
|
||||
expect(toBase64(new Uint8Array([0x63]))).toEqual("Yw==");
|
||||
expect(toBase64(new Uint8Array([0x61, 0x62, 0x63]))).toEqual("YWJj");
|
||||
});
|
||||
|
||||
it("decodes from base64", () => {
|
||||
expect(fromBase64("")).toEqual(new Uint8Array([]));
|
||||
expect(fromBase64("AA==")).toEqual(new Uint8Array([0x00]));
|
||||
expect(fromBase64("AAA=")).toEqual(new Uint8Array([0x00, 0x00]));
|
||||
expect(fromBase64("AAAA")).toEqual(new Uint8Array([0x00, 0x00, 0x00]));
|
||||
expect(fromBase64("AAAAAA==")).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00]));
|
||||
expect(fromBase64("AAAAAAA=")).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00]));
|
||||
expect(fromBase64("AAAAAAAA")).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
||||
expect(fromBase64("YQ==")).toEqual(new Uint8Array([0x61]));
|
||||
expect(fromBase64("Yg==")).toEqual(new Uint8Array([0x62]));
|
||||
expect(fromBase64("Yw==")).toEqual(new Uint8Array([0x63]));
|
||||
expect(fromBase64("YWJj")).toEqual(new Uint8Array([0x61, 0x62, 0x63]));
|
||||
|
||||
// invalid length
|
||||
expect(() => fromBase64("a")).toThrow();
|
||||
expect(() => fromBase64("aa")).toThrow();
|
||||
expect(() => fromBase64("aaa")).toThrow();
|
||||
|
||||
// proper length including invalid character
|
||||
expect(() => fromBase64("aaa!")).toThrow();
|
||||
expect(() => fromBase64("aaa*")).toThrow();
|
||||
expect(() => fromBase64("aaaä")).toThrow();
|
||||
|
||||
// proper length plus invalid character
|
||||
expect(() => fromBase64("aaaa!")).toThrow();
|
||||
expect(() => fromBase64("aaaa*")).toThrow();
|
||||
expect(() => fromBase64("aaaaä")).toThrow();
|
||||
|
||||
// extra spaces
|
||||
expect(() => fromBase64("aaaa ")).toThrow();
|
||||
expect(() => fromBase64(" aaaa")).toThrow();
|
||||
expect(() => fromBase64("aa aa")).toThrow();
|
||||
expect(() => fromBase64("aaaa\n")).toThrow();
|
||||
expect(() => fromBase64("\naaaa")).toThrow();
|
||||
expect(() => fromBase64("aa\naa")).toThrow();
|
||||
|
||||
// position of =
|
||||
expect(() => fromBase64("=aaa")).toThrow();
|
||||
expect(() => fromBase64("==aa")).toThrow();
|
||||
|
||||
// concatenated base64 strings should not be supported
|
||||
// see https://github.com/beatgammit/base64-js/issues/42
|
||||
expect(() => fromBase64("AAA=AAA=")).toThrow();
|
||||
|
||||
// wrong number of =
|
||||
expect(() => fromBase64("a===")).toThrow();
|
||||
});
|
||||
});
|
||||
12
packages/encoding/src/base64.ts
Normal file
12
packages/encoding/src/base64.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import * as base64js from "base64-js";
|
||||
|
||||
export function toBase64(data: Uint8Array): string {
|
||||
return base64js.fromByteArray(data);
|
||||
}
|
||||
|
||||
export function fromBase64(base64String: string): Uint8Array {
|
||||
if (!base64String.match(/^[a-zA-Z0-9+/]*={0,2}$/)) {
|
||||
throw new Error("Invalid base64 string format");
|
||||
}
|
||||
return base64js.toByteArray(base64String);
|
||||
}
|
||||
19
packages/encoding/src/bech32.spec.ts
Normal file
19
packages/encoding/src/bech32.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Bech32 } from "./bech32";
|
||||
import { fromHex } from "./hex";
|
||||
|
||||
describe("Bech32", () => {
|
||||
// test data generate using https://github.com/nym-zone/bech32
|
||||
// bech32 -e -h eth 9d4e856e572e442f0a4b2763e72d08a0e99d8ded
|
||||
const ethAddressRaw = fromHex("9d4e856e572e442f0a4b2763e72d08a0e99d8ded");
|
||||
|
||||
it("encodes", () => {
|
||||
expect(Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw");
|
||||
});
|
||||
|
||||
it("decodes", () => {
|
||||
expect(Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({
|
||||
prefix: "eth",
|
||||
data: ethAddressRaw,
|
||||
});
|
||||
});
|
||||
});
|
||||
16
packages/encoding/src/bech32.ts
Normal file
16
packages/encoding/src/bech32.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import * as bech32 from "bech32";
|
||||
|
||||
export class Bech32 {
|
||||
public static encode(prefix: string, data: Uint8Array): string {
|
||||
const address = bech32.encode(prefix, bech32.toWords(data));
|
||||
return address;
|
||||
}
|
||||
|
||||
public static decode(address: string): { readonly prefix: string; readonly data: Uint8Array } {
|
||||
const decodedAddress = bech32.decode(address);
|
||||
return {
|
||||
prefix: decodedAddress.prefix,
|
||||
data: new Uint8Array(bech32.fromWords(decodedAddress.words)),
|
||||
};
|
||||
}
|
||||
}
|
||||
44
packages/encoding/src/hex.spec.ts
Normal file
44
packages/encoding/src/hex.spec.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { fromHex, toHex } from "./hex";
|
||||
|
||||
describe("fromHex", () => {
|
||||
it("works", () => {
|
||||
// simple
|
||||
expect(fromHex("")).toEqual(new Uint8Array([]));
|
||||
expect(fromHex("00")).toEqual(new Uint8Array([0x00]));
|
||||
expect(fromHex("01")).toEqual(new Uint8Array([0x01]));
|
||||
expect(fromHex("10")).toEqual(new Uint8Array([0x10]));
|
||||
expect(fromHex("11")).toEqual(new Uint8Array([0x11]));
|
||||
expect(fromHex("112233")).toEqual(new Uint8Array([0x11, 0x22, 0x33]));
|
||||
expect(fromHex("0123456789abcdef")).toEqual(
|
||||
new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
|
||||
);
|
||||
|
||||
// capital letters
|
||||
expect(fromHex("AA")).toEqual(new Uint8Array([0xaa]));
|
||||
expect(fromHex("aAbBcCdDeEfF")).toEqual(new Uint8Array([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]));
|
||||
|
||||
// error
|
||||
expect(() => fromHex("a")).toThrow();
|
||||
expect(() => fromHex("aaa")).toThrow();
|
||||
expect(() => fromHex("a!")).toThrow();
|
||||
expect(() => fromHex("a ")).toThrow();
|
||||
expect(() => fromHex("aa ")).toThrow();
|
||||
expect(() => fromHex(" aa")).toThrow();
|
||||
expect(() => fromHex("a a")).toThrow();
|
||||
expect(() => fromHex("gg")).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("toHex", () => {
|
||||
it("works", () => {
|
||||
expect(toHex(new Uint8Array([]))).toEqual("");
|
||||
expect(toHex(new Uint8Array([0x00]))).toEqual("00");
|
||||
expect(toHex(new Uint8Array([0x01]))).toEqual("01");
|
||||
expect(toHex(new Uint8Array([0x10]))).toEqual("10");
|
||||
expect(toHex(new Uint8Array([0x11]))).toEqual("11");
|
||||
expect(toHex(new Uint8Array([0x11, 0x22, 0x33]))).toEqual("112233");
|
||||
expect(toHex(new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]))).toEqual(
|
||||
"0123456789abcdef",
|
||||
);
|
||||
});
|
||||
});
|
||||
23
packages/encoding/src/hex.ts
Normal file
23
packages/encoding/src/hex.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export function toHex(data: Uint8Array): string {
|
||||
let out = "";
|
||||
for (const byte of data) {
|
||||
out += ("0" + byte.toString(16)).slice(-2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromHex(hexstring: string): Uint8Array {
|
||||
if (hexstring.length % 2 !== 0) {
|
||||
throw new Error("hex string length must be a multiple of 2");
|
||||
}
|
||||
|
||||
const listOfInts: number[] = [];
|
||||
for (let i = 0; i < hexstring.length; i += 2) {
|
||||
const hexByteAsString = hexstring.substr(i, 2);
|
||||
if (!hexByteAsString.match(/[0-9a-f]{2}/i)) {
|
||||
throw new Error("hex string contains invalid characters");
|
||||
}
|
||||
listOfInts.push(parseInt(hexByteAsString, 16));
|
||||
}
|
||||
return new Uint8Array(listOfInts);
|
||||
}
|
||||
6
packages/encoding/src/index.ts
Normal file
6
packages/encoding/src/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { fromAscii, toAscii } from "./ascii";
|
||||
export { fromBase64, toBase64 } from "./base64";
|
||||
export { Bech32 } from "./bech32";
|
||||
export { fromHex, toHex } from "./hex";
|
||||
export { fromRfc3339, toRfc3339 } from "./rfc3339";
|
||||
export { fromUtf8, toUtf8 } from "./utf8";
|
||||
178
packages/encoding/src/rfc3339.spec.ts
Normal file
178
packages/encoding/src/rfc3339.spec.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import { fromRfc3339, toRfc3339 } from "./rfc3339";
|
||||
|
||||
describe("RFC3339", () => {
|
||||
it("parses dates with different time zones", () => {
|
||||
// time zone +/- 0
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+00:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-00:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13)));
|
||||
|
||||
// time zone positive (full hours)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+01:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 1, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+02:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 2, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+03:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 3, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+11:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 11, 12, 13)));
|
||||
|
||||
// time zone negative (full hours)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-01:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 1, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-02:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 2, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-03:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 3, 12, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-11:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 11, 12, 13)));
|
||||
|
||||
// time zone positive (minutes only)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+00:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 1, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+00:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 30, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+00:45")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 45, 13)));
|
||||
|
||||
// time zone negative (minutes only)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-00:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 1, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-00:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 30, 13)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-00:45")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 45, 13)));
|
||||
|
||||
// time zone positive (hours and minutes)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+01:01")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 - 1, 12 - 1, 13)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+04:30")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 - 4, 12 - 30, 13)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13+10:20")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 - 10, 12 - 20, 13)),
|
||||
);
|
||||
|
||||
// time zone negative (hours and minutes)
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-01:01")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 + 1, 12 + 1, 13)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-04:30")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 + 4, 12 + 30, 13)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13-10:20")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11 + 10, 12 + 20, 13)),
|
||||
);
|
||||
});
|
||||
|
||||
it("parses dates with milliseconds", () => {
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.123Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)));
|
||||
});
|
||||
|
||||
it("parses dates with low precision fractional seconds", () => {
|
||||
// 1 digit
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.0Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.1Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 100)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.9Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 900)));
|
||||
|
||||
// 2 digit
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.00Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.12Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 120)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.99Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 990)));
|
||||
});
|
||||
|
||||
it("parses dates with high precision fractional seconds", () => {
|
||||
// everything after the 3rd digit is truncated
|
||||
|
||||
// 4 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.0000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.1234Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.9999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)));
|
||||
|
||||
// 5 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.00000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.12345Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.99999Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)),
|
||||
);
|
||||
|
||||
// 6 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.000000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)));
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.123456Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.999999Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)),
|
||||
);
|
||||
|
||||
// 7 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.0000000Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.1234567Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.9999999Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)),
|
||||
);
|
||||
|
||||
// 8 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.00000000Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.12345678Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.99999999Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)),
|
||||
);
|
||||
|
||||
// 9 digits
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.000000000Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.123456789Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123)),
|
||||
);
|
||||
expect(fromRfc3339("2002-10-02T11:12:13.999999999Z")).toEqual(
|
||||
new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999)),
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts space separators", () => {
|
||||
// https://tools.ietf.org/html/rfc3339#section-5.6
|
||||
// Applications using this syntax may choose, for the sake of readability,
|
||||
// to specify a full-date and full-time separated by (say) a space character.
|
||||
expect(fromRfc3339("2002-10-02 11:12:13Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13)));
|
||||
});
|
||||
|
||||
it("throws for invalid format", () => {
|
||||
// extra whitespace
|
||||
expect(() => fromRfc3339(" 2002-10-02T11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13Z ")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13 Z")).toThrow();
|
||||
|
||||
// wrong date separators
|
||||
expect(() => fromRfc3339("2002:10-02T11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10:02T11:12:13Z")).toThrow();
|
||||
|
||||
// wrong time separators
|
||||
expect(() => fromRfc3339("2002-10-02T11-12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12-13Z")).toThrow();
|
||||
|
||||
// wrong separator
|
||||
expect(() => fromRfc3339("2002-10-02TT11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02 T11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T 11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02t11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02x11:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02311:12:13Z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02.11:12:13Z")).toThrow();
|
||||
|
||||
// wrong time zone
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13z")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13 00:00")).toThrow();
|
||||
expect(() => fromRfc3339("2002-10-02T11:12:13+0000")).toThrow();
|
||||
|
||||
// wrong fractional seconds
|
||||
expect(() => fromRfc3339("2018-07-30T19:21:12345Z")).toThrow();
|
||||
expect(() => fromRfc3339("2018-07-30T19:21:12.Z")).toThrow();
|
||||
});
|
||||
|
||||
it("encodes dates", () => {
|
||||
expect(toRfc3339(new Date(Date.UTC(0, 0, 1, 0, 0, 0)))).toEqual("1900-01-01T00:00:00.000Z");
|
||||
expect(toRfc3339(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 456)))).toEqual("2002-10-02T11:12:13.456Z");
|
||||
});
|
||||
});
|
||||
58
packages/encoding/src/rfc3339.ts
Normal file
58
packages/encoding/src/rfc3339.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
const rfc3339Matcher = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(\.\d{1,9})?((?:[+-]\d{2}:\d{2})|Z)$/;
|
||||
|
||||
function padded(integer: number, length = 2): string {
|
||||
const filled = "00000" + integer.toString();
|
||||
return filled.substring(filled.length - length);
|
||||
}
|
||||
|
||||
export function fromRfc3339(str: string): ReadonlyDate {
|
||||
const matches = rfc3339Matcher.exec(str);
|
||||
if (!matches) {
|
||||
throw new Error("Date string is not in RFC3339 format");
|
||||
}
|
||||
|
||||
const year = +matches[1];
|
||||
const month = +matches[2];
|
||||
const day = +matches[3];
|
||||
const hour = +matches[4];
|
||||
const minute = +matches[5];
|
||||
const second = +matches[6];
|
||||
|
||||
// fractional seconds match either undefined or a string like ".1", ".123456789"
|
||||
const milliSeconds = matches[7] ? Math.floor(+matches[7] * 1000) : 0;
|
||||
|
||||
let tzOffsetSign: number;
|
||||
let tzOffsetHours: number;
|
||||
let tzOffsetMinutes: number;
|
||||
|
||||
// if timezone is undefined, it must be Z or nothing (otherwise the group would have captured).
|
||||
if (matches[8] === "Z") {
|
||||
tzOffsetSign = 1;
|
||||
tzOffsetHours = 0;
|
||||
tzOffsetMinutes = 0;
|
||||
} else {
|
||||
tzOffsetSign = matches[8].substring(0, 1) === "-" ? -1 : 1;
|
||||
tzOffsetHours = +matches[8].substring(1, 3);
|
||||
tzOffsetMinutes = +matches[8].substring(4, 6);
|
||||
}
|
||||
|
||||
const tzOffset = tzOffsetSign * (tzOffsetHours * 60 + tzOffsetMinutes) * 60; // seconds
|
||||
|
||||
return new ReadonlyDate(
|
||||
ReadonlyDate.UTC(year, month - 1, day, hour, minute, second, milliSeconds) - tzOffset * 1000,
|
||||
);
|
||||
}
|
||||
|
||||
export function toRfc3339(date: Date | ReadonlyDate): string {
|
||||
const year = date.getUTCFullYear();
|
||||
const month = padded(date.getUTCMonth() + 1);
|
||||
const day = padded(date.getUTCDate());
|
||||
const hour = padded(date.getUTCHours());
|
||||
const minute = padded(date.getUTCMinutes());
|
||||
const second = padded(date.getUTCSeconds());
|
||||
const ms = padded(date.getUTCMilliseconds(), 3);
|
||||
|
||||
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}Z`;
|
||||
}
|
||||
62
packages/encoding/src/utf8.spec.ts
Normal file
62
packages/encoding/src/utf8.spec.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { fromUtf8, toUtf8 } from "./utf8";
|
||||
|
||||
describe("utf8", () => {
|
||||
it("encodes ascii strings", () => {
|
||||
expect(toUtf8("")).toEqual(new Uint8Array([]));
|
||||
expect(toUtf8("abc")).toEqual(new Uint8Array([0x61, 0x62, 0x63]));
|
||||
expect(toUtf8(" ?=-n|~+-*/\\")).toEqual(
|
||||
new Uint8Array([0x20, 0x3f, 0x3d, 0x2d, 0x6e, 0x7c, 0x7e, 0x2b, 0x2d, 0x2a, 0x2f, 0x5c]),
|
||||
);
|
||||
});
|
||||
|
||||
it("decodes ascii string", () => {
|
||||
expect(fromUtf8(new Uint8Array([]))).toEqual("");
|
||||
expect(fromUtf8(new Uint8Array([0x61, 0x62, 0x63]))).toEqual("abc");
|
||||
expect(
|
||||
fromUtf8(new Uint8Array([0x20, 0x3f, 0x3d, 0x2d, 0x6e, 0x7c, 0x7e, 0x2b, 0x2d, 0x2a, 0x2f, 0x5c])),
|
||||
).toEqual(" ?=-n|~+-*/\\");
|
||||
});
|
||||
|
||||
it("encodes null character", () => {
|
||||
expect(toUtf8("\u0000")).toEqual(new Uint8Array([0x00]));
|
||||
});
|
||||
|
||||
it("decodes null byte", () => {
|
||||
expect(fromUtf8(new Uint8Array([0x00]))).toEqual("\u0000");
|
||||
});
|
||||
|
||||
it("encodes Basic Multilingual Plane strings", () => {
|
||||
expect(toUtf8("ö")).toEqual(new Uint8Array([0xc3, 0xb6]));
|
||||
expect(toUtf8("¥")).toEqual(new Uint8Array([0xc2, 0xa5]));
|
||||
expect(toUtf8("Ф")).toEqual(new Uint8Array([0xd0, 0xa4]));
|
||||
expect(toUtf8("ⱴ")).toEqual(new Uint8Array([0xe2, 0xb1, 0xb4]));
|
||||
expect(toUtf8("ⵘ")).toEqual(new Uint8Array([0xe2, 0xb5, 0x98]));
|
||||
});
|
||||
|
||||
it("decodes Basic Multilingual Plane strings", () => {
|
||||
expect(fromUtf8(new Uint8Array([0xc3, 0xb6]))).toEqual("ö");
|
||||
expect(fromUtf8(new Uint8Array([0xc2, 0xa5]))).toEqual("¥");
|
||||
expect(fromUtf8(new Uint8Array([0xd0, 0xa4]))).toEqual("Ф");
|
||||
expect(fromUtf8(new Uint8Array([0xe2, 0xb1, 0xb4]))).toEqual("ⱴ");
|
||||
expect(fromUtf8(new Uint8Array([0xe2, 0xb5, 0x98]))).toEqual("ⵘ");
|
||||
});
|
||||
|
||||
it("encodes Supplementary Multilingual Plane strings", () => {
|
||||
// U+1F0A1
|
||||
expect(toUtf8("🂡")).toEqual(new Uint8Array([0xf0, 0x9f, 0x82, 0xa1]));
|
||||
// U+1034A
|
||||
expect(toUtf8("𐍊")).toEqual(new Uint8Array([0xf0, 0x90, 0x8d, 0x8a]));
|
||||
});
|
||||
|
||||
it("decodes Supplementary Multilingual Plane strings", () => {
|
||||
// U+1F0A1
|
||||
expect(fromUtf8(new Uint8Array([0xf0, 0x9f, 0x82, 0xa1]))).toEqual("🂡");
|
||||
// U+1034A
|
||||
expect(fromUtf8(new Uint8Array([0xf0, 0x90, 0x8d, 0x8a]))).toEqual("𐍊");
|
||||
});
|
||||
|
||||
it("throws on invalid utf8 bytes", () => {
|
||||
// Broken UTF8 example from https://github.com/nodejs/node/issues/16894
|
||||
expect(() => fromUtf8(new Uint8Array([0xf0, 0x80, 0x80]))).toThrow();
|
||||
});
|
||||
});
|
||||
36
packages/encoding/src/utf8.ts
Normal file
36
packages/encoding/src/utf8.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// Global symbols in some environments
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
|
||||
declare const TextEncoder: any | undefined;
|
||||
declare const TextDecoder: any | undefined;
|
||||
|
||||
function isValidUtf8(data: Uint8Array): boolean {
|
||||
const toStringAndBack = Buffer.from(Buffer.from(data).toString("utf8"), "utf8");
|
||||
return Buffer.compare(Buffer.from(data), toStringAndBack) === 0;
|
||||
}
|
||||
|
||||
export function toUtf8(str: string): Uint8Array {
|
||||
// Browser and future nodejs (https://github.com/nodejs/node/issues/20365)
|
||||
if (typeof TextEncoder !== "undefined") {
|
||||
return new TextEncoder().encode(str);
|
||||
}
|
||||
|
||||
// Use Buffer hack instead of nodejs util.TextEncoder to ensure
|
||||
// webpack does not bundle the util module for browsers.
|
||||
return new Uint8Array(Buffer.from(str, "utf8"));
|
||||
}
|
||||
|
||||
export function fromUtf8(data: Uint8Array): string {
|
||||
// Browser and future nodejs (https://github.com/nodejs/node/issues/20365)
|
||||
if (typeof TextDecoder !== "undefined") {
|
||||
return new TextDecoder("utf-8", { fatal: true }).decode(data);
|
||||
}
|
||||
|
||||
// Use Buffer hack instead of nodejs util.TextDecoder to ensure
|
||||
// webpack does not bundle the util module for browsers.
|
||||
// Buffer.toString has no fatal option
|
||||
if (!isValidUtf8(data)) {
|
||||
throw new Error("Invalid UTF8 data");
|
||||
}
|
||||
return Buffer.from(data).toString("utf8");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user