From a762c896f3228a0915b273a7941f509708d12871 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 13:01:25 +0200 Subject: [PATCH 01/12] Add @cosmjs/crypto --- NOTICE | 3 + packages/crypto/.eslintignore | 8 + packages/crypto/.gitignore | 3 + packages/crypto/README.md | 16 + packages/crypto/jasmine-testrunner.js | 26 + packages/crypto/karma.conf.js | 50 + packages/crypto/nonces/README.txt | 1 + packages/crypto/package.json | 65 + packages/crypto/src/bip39.spec.ts | 416 +++++ packages/crypto/src/bip39.ts | 51 + packages/crypto/src/englishmnemonic.spec.ts | 247 +++ packages/crypto/src/englishmnemonic.ts | 40 + packages/crypto/src/hash.ts | 5 + packages/crypto/src/hmac.spec.ts | 241 +++ packages/crypto/src/hmac.ts | 49 + packages/crypto/src/index.ts | 30 + packages/crypto/src/keccak.spec.ts | 28 + packages/crypto/src/keccak.ts | 26 + packages/crypto/src/libsodium.spec.ts | 948 ++++++++++ packages/crypto/src/libsodium.ts | 130 ++ packages/crypto/src/random.spec.ts | 38 + packages/crypto/src/random.ts | 27 + packages/crypto/src/ripemd.spec.ts | 28 + packages/crypto/src/ripemd.ts | 24 + packages/crypto/src/secp256k1.spec.ts | 589 +++++++ packages/crypto/src/secp256k1.ts | 137 ++ .../crypto/src/secp256k1signature.spec.ts | 162 ++ packages/crypto/src/secp256k1signature.ts | 179 ++ packages/crypto/src/sha.spec.ts | 28 + packages/crypto/src/sha.ts | 73 + packages/crypto/src/slip10.spec.ts | 472 +++++ packages/crypto/src/slip10.ts | 212 +++ packages/crypto/src/testdata/bip39.json | 174 ++ .../crypto/src/testdata/bip39_wordlists.json | 3 + packages/crypto/src/testdata/keccak.json | 1076 ++++++++++++ packages/crypto/src/testdata/ripemd.json | 383 ++++ packages/crypto/src/testdata/sha.json | 1564 +++++++++++++++++ packages/crypto/tsconfig.json | 12 + packages/crypto/tslint.json | 3 + packages/crypto/typedoc.js | 14 + packages/crypto/types/bip39.d.ts | 7 + packages/crypto/types/englishmnemonic.d.ts | 7 + packages/crypto/types/hash.d.ts | 5 + packages/crypto/types/hmac.d.ts | 11 + packages/crypto/types/index.d.ts | 30 + packages/crypto/types/keccak.d.ts | 8 + packages/crypto/types/libsodium.d.ts | 45 + packages/crypto/types/random.d.ts | 6 + packages/crypto/types/ripemd.d.ts | 8 + packages/crypto/types/secp256k1.d.ts | 20 + packages/crypto/types/secp256k1signature.d.ts | 34 + packages/crypto/types/sha.d.ts | 22 + packages/crypto/types/slip10.d.ts | 40 + packages/crypto/webpack.web.config.js | 17 + yarn.lock | 40 +- 55 files changed, 7880 insertions(+), 1 deletion(-) create mode 100644 packages/crypto/.eslintignore create mode 100644 packages/crypto/.gitignore create mode 100644 packages/crypto/README.md create mode 100755 packages/crypto/jasmine-testrunner.js create mode 100644 packages/crypto/karma.conf.js create mode 100644 packages/crypto/nonces/README.txt create mode 100644 packages/crypto/package.json create mode 100644 packages/crypto/src/bip39.spec.ts create mode 100644 packages/crypto/src/bip39.ts create mode 100644 packages/crypto/src/englishmnemonic.spec.ts create mode 100644 packages/crypto/src/englishmnemonic.ts create mode 100644 packages/crypto/src/hash.ts create mode 100644 packages/crypto/src/hmac.spec.ts create mode 100644 packages/crypto/src/hmac.ts create mode 100644 packages/crypto/src/index.ts create mode 100644 packages/crypto/src/keccak.spec.ts create mode 100644 packages/crypto/src/keccak.ts create mode 100644 packages/crypto/src/libsodium.spec.ts create mode 100644 packages/crypto/src/libsodium.ts create mode 100644 packages/crypto/src/random.spec.ts create mode 100644 packages/crypto/src/random.ts create mode 100644 packages/crypto/src/ripemd.spec.ts create mode 100644 packages/crypto/src/ripemd.ts create mode 100644 packages/crypto/src/secp256k1.spec.ts create mode 100644 packages/crypto/src/secp256k1.ts create mode 100644 packages/crypto/src/secp256k1signature.spec.ts create mode 100644 packages/crypto/src/secp256k1signature.ts create mode 100644 packages/crypto/src/sha.spec.ts create mode 100644 packages/crypto/src/sha.ts create mode 100644 packages/crypto/src/slip10.spec.ts create mode 100644 packages/crypto/src/slip10.ts create mode 100644 packages/crypto/src/testdata/bip39.json create mode 100644 packages/crypto/src/testdata/bip39_wordlists.json create mode 100644 packages/crypto/src/testdata/keccak.json create mode 100644 packages/crypto/src/testdata/ripemd.json create mode 100644 packages/crypto/src/testdata/sha.json create mode 100644 packages/crypto/tsconfig.json create mode 100644 packages/crypto/tslint.json create mode 100644 packages/crypto/typedoc.js create mode 100644 packages/crypto/types/bip39.d.ts create mode 100644 packages/crypto/types/englishmnemonic.d.ts create mode 100644 packages/crypto/types/hash.d.ts create mode 100644 packages/crypto/types/hmac.d.ts create mode 100644 packages/crypto/types/index.d.ts create mode 100644 packages/crypto/types/keccak.d.ts create mode 100644 packages/crypto/types/libsodium.d.ts create mode 100644 packages/crypto/types/random.d.ts create mode 100644 packages/crypto/types/ripemd.d.ts create mode 100644 packages/crypto/types/secp256k1.d.ts create mode 100644 packages/crypto/types/secp256k1signature.d.ts create mode 100644 packages/crypto/types/sha.d.ts create mode 100644 packages/crypto/types/slip10.d.ts create mode 100644 packages/crypto/webpack.web.config.js diff --git a/NOTICE b/NOTICE index bfe81681..20af5cca 100644 --- a/NOTICE +++ b/NOTICE @@ -8,6 +8,9 @@ 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. + Copyright 2018-2020 IOV SAS Copyright 2020 Confio UO Copyright 2020 Simon Warta diff --git a/packages/crypto/.eslintignore b/packages/crypto/.eslintignore new file mode 100644 index 00000000..f373a53f --- /dev/null +++ b/packages/crypto/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ + +build/ +custom_types/ +dist/ +docs/ +generated/ +types/ diff --git a/packages/crypto/.gitignore b/packages/crypto/.gitignore new file mode 100644 index 00000000..68bf3735 --- /dev/null +++ b/packages/crypto/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +docs/ diff --git a/packages/crypto/README.md b/packages/crypto/README.md new file mode 100644 index 00000000..36d72d6f --- /dev/null +++ b/packages/crypto/README.md @@ -0,0 +1,16 @@ +# @cosmjs/crypto + +[![npm version](https://img.shields.io/npm/v/@cosmjs/crypto.svg)](https://www.npmjs.com/package/@cosmjs/crypto) + +@iov/cosmjs 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 cosmwasm-js repository, licensed under the Apache +License 2.0 (see +[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and +[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). diff --git a/packages/crypto/jasmine-testrunner.js b/packages/crypto/jasmine-testrunner.js new file mode 100755 index 00000000..9fada59b --- /dev/null +++ b/packages/crypto/jasmine-testrunner.js @@ -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(); diff --git a/packages/crypto/karma.conf.js b/packages/crypto/karma.conf.js new file mode 100644 index 00000000..3accce1a --- /dev/null +++ b/packages/crypto/karma.conf.js @@ -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, + }); +}; diff --git a/packages/crypto/nonces/README.txt b/packages/crypto/nonces/README.txt new file mode 100644 index 00000000..092fe732 --- /dev/null +++ b/packages/crypto/nonces/README.txt @@ -0,0 +1 @@ +Directory used to trigger lerna package updates for all packages diff --git a/packages/crypto/package.json b/packages/crypto/package.json new file mode 100644 index 00000000..f3ebc372 --- /dev/null +++ b/packages/crypto/package.json @@ -0,0 +1,65 @@ +{ + "name": "@cosmjs/crypto", + "version": "0.8.0", + "description": "Cryptography resources for blockchain projects", + "contributors": [ + "IOV SAS ", + "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/cosmwasm-js/tree/master/packages/crypto" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "docs": "shx rm -rf docs && typedoc --options typedoc.js", + "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\" && tslint -t verbose --project .", + "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": { + "@iov/encoding": "^2.3.2", + "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" + } +} diff --git a/packages/crypto/src/bip39.spec.ts b/packages/crypto/src/bip39.spec.ts new file mode 100644 index 00000000..3e2b7a2b --- /dev/null +++ b/packages/crypto/src/bip39.spec.ts @@ -0,0 +1,416 @@ +import { fromHex } from "@iov/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", + ), + ); + }); + }); +}); diff --git a/packages/crypto/src/bip39.ts b/packages/crypto/src/bip39.ts new file mode 100644 index 00000000..89980e46 --- /dev/null +++ b/packages/crypto/src/bip39.ts @@ -0,0 +1,51 @@ +import { fromHex, toHex } from "@iov/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 { + // 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 { + return new Promise((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)); + } + }); + }); + } +} diff --git a/packages/crypto/src/englishmnemonic.spec.ts b/packages/crypto/src/englishmnemonic.spec.ts new file mode 100644 index 00000000..5b2dd7ac --- /dev/null +++ b/packages/crypto/src/englishmnemonic.spec.ts @@ -0,0 +1,247 @@ +import { fromAscii, fromBase64, fromHex } from "@iov/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")); + + // tslint:disable-next-line: readonly-array + 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); + }); + }); + + // tslint:disable:no-unused-expression + + 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); + }); + + // tslint:enable:no-unused-expression + + 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); + }); + }); +}); diff --git a/packages/crypto/src/englishmnemonic.ts b/packages/crypto/src/englishmnemonic.ts new file mode 100644 index 00000000..c2ce1da1 --- /dev/null +++ b/packages/crypto/src/englishmnemonic.ts @@ -0,0 +1,40 @@ +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 + // tslint:disable-next-line:no-unused-expression + bip39.mnemonicToEntropy(mnemonic); + + this.data = mnemonic; + } + + public toString(): string { + return this.data; + } +} diff --git a/packages/crypto/src/hash.ts b/packages/crypto/src/hash.ts new file mode 100644 index 00000000..08fe193b --- /dev/null +++ b/packages/crypto/src/hash.ts @@ -0,0 +1,5 @@ +export interface HashFunction { + readonly blockSize: number; + readonly update: (_: Uint8Array) => HashFunction; + readonly digest: () => Uint8Array; +} diff --git a/packages/crypto/src/hmac.spec.ts b/packages/crypto/src/hmac.spec.ts new file mode 100644 index 00000000..a89fc29b --- /dev/null +++ b/packages/crypto/src/hmac.spec.ts @@ -0,0 +1,241 @@ +import { fromHex } from "@iov/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"), + ); + } + }); +}); diff --git a/packages/crypto/src/hmac.ts b/packages/crypto/src/hmac.ts new file mode 100644 index 00000000..b5c72d31 --- /dev/null +++ b/packages/crypto/src/hmac.ts @@ -0,0 +1,49 @@ +import { HashFunction } from "./hash"; + +export class Hmac 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]); + } + + // tslint:disable-next-line:no-bitwise + this.oKeyPad = key.map((keyByte) => keyByte ^ 0x5c); + // tslint: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 { + this.messageHasher.update(data); + return this; + } + + public digest(): Uint8Array { + const innerHash = this.messageHasher.digest(); + return this.hash(new Uint8Array([...this.oKeyPad, ...innerHash])); + } +} diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts new file mode 100644 index 00000000..3eccee6d --- /dev/null +++ b/packages/crypto/src/index.ts @@ -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"; diff --git a/packages/crypto/src/keccak.spec.ts b/packages/crypto/src/keccak.spec.ts new file mode 100644 index 00000000..e82c8a73 --- /dev/null +++ b/packages/crypto/src/keccak.spec.ts @@ -0,0 +1,28 @@ +import { fromHex, toHex } from "@iov/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)); + } + }); +}); diff --git a/packages/crypto/src/keccak.ts b/packages/crypto/src/keccak.ts new file mode 100644 index 00000000..3ee60552 --- /dev/null +++ b/packages/crypto/src/keccak.ts @@ -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()); + } +} diff --git a/packages/crypto/src/libsodium.spec.ts b/packages/crypto/src/libsodium.spec.ts new file mode 100644 index 00000000..cdbaee46 --- /dev/null +++ b/packages/crypto/src/libsodium.spec.ts @@ -0,0 +1,948 @@ +/* tslint:disable:no-bitwise */ +import { fromHex, toAscii } from "@iov/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(); + 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 "" | ./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", + ), + ); + } + }); + }); +}); diff --git a/packages/crypto/src/libsodium.ts b/packages/crypto/src/libsodium.ts new file mode 100644 index 00000000..a094a8e5 --- /dev/null +++ b/packages/crypto/src/libsodium.ts @@ -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 { + 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 ` + ` + 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 { + 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 { + await sodium.ready; + return sodium.crypto_sign_detached(message, keyPair.toLibsodiumPrivkey()); + } + + public static async verifySignature( + signature: Uint8Array, + message: Uint8Array, + pubkey: Uint8Array, + ): Promise { + 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 { + 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 { + 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; + } +} diff --git a/packages/crypto/src/random.spec.ts b/packages/crypto/src/random.spec.ts new file mode 100644 index 00000000..6f9511b5 --- /dev/null +++ b/packages/crypto/src/random.spec.ts @@ -0,0 +1,38 @@ +import { isUint8Array } from "@iov/encoding"; + +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); + } + }); +}); diff --git a/packages/crypto/src/random.ts b/packages/crypto/src/random.ts new file mode 100644 index 00000000..efd690df --- /dev/null +++ b/packages/crypto/src/random.ts @@ -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"); + } + } + } +} diff --git a/packages/crypto/src/ripemd.spec.ts b/packages/crypto/src/ripemd.spec.ts new file mode 100644 index 00000000..d3a8dd37 --- /dev/null +++ b/packages/crypto/src/ripemd.spec.ts @@ -0,0 +1,28 @@ +import { fromHex } from "@iov/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)); + } + }); +}); diff --git a/packages/crypto/src/ripemd.ts b/packages/crypto/src/ripemd.ts new file mode 100644 index 00000000..c726e37f --- /dev/null +++ b/packages/crypto/src/ripemd.ts @@ -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()); + } +} diff --git a/packages/crypto/src/secp256k1.spec.ts b/packages/crypto/src/secp256k1.spec.ts new file mode 100644 index 00000000..15a92707 --- /dev/null +++ b/packages/crypto/src/secp256k1.spec.ts @@ -0,0 +1,589 @@ +/* tslint:disable:no-bitwise */ +import { fromHex } from "@iov/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))); + }); + }); +}); diff --git a/packages/crypto/src/secp256k1.ts b/packages/crypto/src/secp256k1.ts new file mode 100644 index 00000000..e0f1eb15 --- /dev/null +++ b/packages/crypto/src/secp256k1.ts @@ -0,0 +1,137 @@ +import { fromHex, toHex } from "@iov/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 { + 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 { + 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 { + 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"); + } + } +} diff --git a/packages/crypto/src/secp256k1signature.spec.ts b/packages/crypto/src/secp256k1signature.spec.ts new file mode 100644 index 00000000..041f29e5 --- /dev/null +++ b/packages/crypto/src/secp256k1signature.spec.ts @@ -0,0 +1,162 @@ +import { fromHex } from "@iov/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); + }); +}); diff --git a/packages/crypto/src/secp256k1signature.ts b/packages/crypto/src/secp256k1signature.ts new file mode 100644 index 00000000..0defcb19 --- /dev/null +++ b/packages/crypto/src/secp256k1signature.ts @@ -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]); + } +} diff --git a/packages/crypto/src/sha.spec.ts b/packages/crypto/src/sha.spec.ts new file mode 100644 index 00000000..e0384da4 --- /dev/null +++ b/packages/crypto/src/sha.spec.ts @@ -0,0 +1,28 @@ +import { fromHex, toHex } from "@iov/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)); + } + }); +}); diff --git a/packages/crypto/src/sha.ts b/packages/crypto/src/sha.ts new file mode 100644 index 00000000..cf4c4f53 --- /dev/null +++ b/packages/crypto/src/sha.ts @@ -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()); + } +} diff --git a/packages/crypto/src/slip10.spec.ts b/packages/crypto/src/slip10.spec.ts new file mode 100644 index 00000000..36838f6d --- /dev/null +++ b/packages/crypto/src/slip10.spec.ts @@ -0,0 +1,472 @@ +import { fromHex } from "@iov/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]); + }); + }); +}); diff --git a/packages/crypto/src/slip10.ts b/packages/crypto/src/slip10.ts new file mode 100644 index 00000000..a2e37b63 --- /dev/null +++ b/packages/crypto/src/slip10.ts @@ -0,0 +1,212 @@ +import { fromHex, toAscii, Uint32, Uint53 } from "@iov/encoding"; +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(); + 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; +} diff --git a/packages/crypto/src/testdata/bip39.json b/packages/crypto/src/testdata/bip39.json new file mode 100644 index 00000000..7e190387 --- /dev/null +++ b/packages/crypto/src/testdata/bip39.json @@ -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" + } + ] + } +} diff --git a/packages/crypto/src/testdata/bip39_wordlists.json b/packages/crypto/src/testdata/bip39_wordlists.json new file mode 100644 index 00000000..ac5d558e --- /dev/null +++ b/packages/crypto/src/testdata/bip39_wordlists.json @@ -0,0 +1,3 @@ +{ + "english": "YWJhbmRvbgphYmlsaXR5CmFibGUKYWJvdXQKYWJvdmUKYWJzZW50CmFic29yYgphYnN0cmFjdAphYnN1cmQKYWJ1c2UKYWNjZXNzCmFjY2lkZW50CmFjY291bnQKYWNjdXNlCmFjaGlldmUKYWNpZAphY291c3RpYwphY3F1aXJlCmFjcm9zcwphY3QKYWN0aW9uCmFjdG9yCmFjdHJlc3MKYWN0dWFsCmFkYXB0CmFkZAphZGRpY3QKYWRkcmVzcwphZGp1c3QKYWRtaXQKYWR1bHQKYWR2YW5jZQphZHZpY2UKYWVyb2JpYwphZmZhaXIKYWZmb3JkCmFmcmFpZAphZ2FpbgphZ2UKYWdlbnQKYWdyZWUKYWhlYWQKYWltCmFpcgphaXJwb3J0CmFpc2xlCmFsYXJtCmFsYnVtCmFsY29ob2wKYWxlcnQKYWxpZW4KYWxsCmFsbGV5CmFsbG93CmFsbW9zdAphbG9uZQphbHBoYQphbHJlYWR5CmFsc28KYWx0ZXIKYWx3YXlzCmFtYXRldXIKYW1hemluZwphbW9uZwphbW91bnQKYW11c2VkCmFuYWx5c3QKYW5jaG9yCmFuY2llbnQKYW5nZXIKYW5nbGUKYW5ncnkKYW5pbWFsCmFua2xlCmFubm91bmNlCmFubnVhbAphbm90aGVyCmFuc3dlcgphbnRlbm5hCmFudGlxdWUKYW54aWV0eQphbnkKYXBhcnQKYXBvbG9neQphcHBlYXIKYXBwbGUKYXBwcm92ZQphcHJpbAphcmNoCmFyY3RpYwphcmVhCmFyZW5hCmFyZ3VlCmFybQphcm1lZAphcm1vcgphcm15CmFyb3VuZAphcnJhbmdlCmFycmVzdAphcnJpdmUKYXJyb3cKYXJ0CmFydGVmYWN0CmFydGlzdAphcnR3b3JrCmFzawphc3BlY3QKYXNzYXVsdAphc3NldAphc3Npc3QKYXNzdW1lCmFzdGhtYQphdGhsZXRlCmF0b20KYXR0YWNrCmF0dGVuZAphdHRpdHVkZQphdHRyYWN0CmF1Y3Rpb24KYXVkaXQKYXVndXN0CmF1bnQKYXV0aG9yCmF1dG8KYXV0dW1uCmF2ZXJhZ2UKYXZvY2Fkbwphdm9pZAphd2FrZQphd2FyZQphd2F5CmF3ZXNvbWUKYXdmdWwKYXdrd2FyZApheGlzCmJhYnkKYmFjaGVsb3IKYmFjb24KYmFkZ2UKYmFnCmJhbGFuY2UKYmFsY29ueQpiYWxsCmJhbWJvbwpiYW5hbmEKYmFubmVyCmJhcgpiYXJlbHkKYmFyZ2FpbgpiYXJyZWwKYmFzZQpiYXNpYwpiYXNrZXQKYmF0dGxlCmJlYWNoCmJlYW4KYmVhdXR5CmJlY2F1c2UKYmVjb21lCmJlZWYKYmVmb3JlCmJlZ2luCmJlaGF2ZQpiZWhpbmQKYmVsaWV2ZQpiZWxvdwpiZWx0CmJlbmNoCmJlbmVmaXQKYmVzdApiZXRyYXkKYmV0dGVyCmJldHdlZW4KYmV5b25kCmJpY3ljbGUKYmlkCmJpa2UKYmluZApiaW9sb2d5CmJpcmQKYmlydGgKYml0dGVyCmJsYWNrCmJsYWRlCmJsYW1lCmJsYW5rZXQKYmxhc3QKYmxlYWsKYmxlc3MKYmxpbmQKYmxvb2QKYmxvc3NvbQpibG91c2UKYmx1ZQpibHVyCmJsdXNoCmJvYXJkCmJvYXQKYm9keQpib2lsCmJvbWIKYm9uZQpib251cwpib29rCmJvb3N0CmJvcmRlcgpib3JpbmcKYm9ycm93CmJvc3MKYm90dG9tCmJvdW5jZQpib3gKYm95CmJyYWNrZXQKYnJhaW4KYnJhbmQKYnJhc3MKYnJhdmUKYnJlYWQKYnJlZXplCmJyaWNrCmJyaWRnZQpicmllZgpicmlnaHQKYnJpbmcKYnJpc2sKYnJvY2NvbGkKYnJva2VuCmJyb256ZQpicm9vbQpicm90aGVyCmJyb3duCmJydXNoCmJ1YmJsZQpidWRkeQpidWRnZXQKYnVmZmFsbwpidWlsZApidWxiCmJ1bGsKYnVsbGV0CmJ1bmRsZQpidW5rZXIKYnVyZGVuCmJ1cmdlcgpidXJzdApidXMKYnVzaW5lc3MKYnVzeQpidXR0ZXIKYnV5ZXIKYnV6egpjYWJiYWdlCmNhYmluCmNhYmxlCmNhY3R1cwpjYWdlCmNha2UKY2FsbApjYWxtCmNhbWVyYQpjYW1wCmNhbgpjYW5hbApjYW5jZWwKY2FuZHkKY2Fubm9uCmNhbm9lCmNhbnZhcwpjYW55b24KY2FwYWJsZQpjYXBpdGFsCmNhcHRhaW4KY2FyCmNhcmJvbgpjYXJkCmNhcmdvCmNhcnBldApjYXJyeQpjYXJ0CmNhc2UKY2FzaApjYXNpbm8KY2FzdGxlCmNhc3VhbApjYXQKY2F0YWxvZwpjYXRjaApjYXRlZ29yeQpjYXR0bGUKY2F1Z2h0CmNhdXNlCmNhdXRpb24KY2F2ZQpjZWlsaW5nCmNlbGVyeQpjZW1lbnQKY2Vuc3VzCmNlbnR1cnkKY2VyZWFsCmNlcnRhaW4KY2hhaXIKY2hhbGsKY2hhbXBpb24KY2hhbmdlCmNoYW9zCmNoYXB0ZXIKY2hhcmdlCmNoYXNlCmNoYXQKY2hlYXAKY2hlY2sKY2hlZXNlCmNoZWYKY2hlcnJ5CmNoZXN0CmNoaWNrZW4KY2hpZWYKY2hpbGQKY2hpbW5leQpjaG9pY2UKY2hvb3NlCmNocm9uaWMKY2h1Y2tsZQpjaHVuawpjaHVybgpjaWdhcgpjaW5uYW1vbgpjaXJjbGUKY2l0aXplbgpjaXR5CmNpdmlsCmNsYWltCmNsYXAKY2xhcmlmeQpjbGF3CmNsYXkKY2xlYW4KY2xlcmsKY2xldmVyCmNsaWNrCmNsaWVudApjbGlmZgpjbGltYgpjbGluaWMKY2xpcApjbG9jawpjbG9nCmNsb3NlCmNsb3RoCmNsb3VkCmNsb3duCmNsdWIKY2x1bXAKY2x1c3RlcgpjbHV0Y2gKY29hY2gKY29hc3QKY29jb251dApjb2RlCmNvZmZlZQpjb2lsCmNvaW4KY29sbGVjdApjb2xvcgpjb2x1bW4KY29tYmluZQpjb21lCmNvbWZvcnQKY29taWMKY29tbW9uCmNvbXBhbnkKY29uY2VydApjb25kdWN0CmNvbmZpcm0KY29uZ3Jlc3MKY29ubmVjdApjb25zaWRlcgpjb250cm9sCmNvbnZpbmNlCmNvb2sKY29vbApjb3BwZXIKY29weQpjb3JhbApjb3JlCmNvcm4KY29ycmVjdApjb3N0CmNvdHRvbgpjb3VjaApjb3VudHJ5CmNvdXBsZQpjb3Vyc2UKY291c2luCmNvdmVyCmNveW90ZQpjcmFjawpjcmFkbGUKY3JhZnQKY3JhbQpjcmFuZQpjcmFzaApjcmF0ZXIKY3Jhd2wKY3JhenkKY3JlYW0KY3JlZGl0CmNyZWVrCmNyZXcKY3JpY2tldApjcmltZQpjcmlzcApjcml0aWMKY3JvcApjcm9zcwpjcm91Y2gKY3Jvd2QKY3J1Y2lhbApjcnVlbApjcnVpc2UKY3J1bWJsZQpjcnVuY2gKY3J1c2gKY3J5CmNyeXN0YWwKY3ViZQpjdWx0dXJlCmN1cApjdXBib2FyZApjdXJpb3VzCmN1cnJlbnQKY3VydGFpbgpjdXJ2ZQpjdXNoaW9uCmN1c3RvbQpjdXRlCmN5Y2xlCmRhZApkYW1hZ2UKZGFtcApkYW5jZQpkYW5nZXIKZGFyaW5nCmRhc2gKZGF1Z2h0ZXIKZGF3bgpkYXkKZGVhbApkZWJhdGUKZGVicmlzCmRlY2FkZQpkZWNlbWJlcgpkZWNpZGUKZGVjbGluZQpkZWNvcmF0ZQpkZWNyZWFzZQpkZWVyCmRlZmVuc2UKZGVmaW5lCmRlZnkKZGVncmVlCmRlbGF5CmRlbGl2ZXIKZGVtYW5kCmRlbWlzZQpkZW5pYWwKZGVudGlzdApkZW55CmRlcGFydApkZXBlbmQKZGVwb3NpdApkZXB0aApkZXB1dHkKZGVyaXZlCmRlc2NyaWJlCmRlc2VydApkZXNpZ24KZGVzawpkZXNwYWlyCmRlc3Ryb3kKZGV0YWlsCmRldGVjdApkZXZlbG9wCmRldmljZQpkZXZvdGUKZGlhZ3JhbQpkaWFsCmRpYW1vbmQKZGlhcnkKZGljZQpkaWVzZWwKZGlldApkaWZmZXIKZGlnaXRhbApkaWduaXR5CmRpbGVtbWEKZGlubmVyCmRpbm9zYXVyCmRpcmVjdApkaXJ0CmRpc2FncmVlCmRpc2NvdmVyCmRpc2Vhc2UKZGlzaApkaXNtaXNzCmRpc29yZGVyCmRpc3BsYXkKZGlzdGFuY2UKZGl2ZXJ0CmRpdmlkZQpkaXZvcmNlCmRpenp5CmRvY3Rvcgpkb2N1bWVudApkb2cKZG9sbApkb2xwaGluCmRvbWFpbgpkb25hdGUKZG9ua2V5CmRvbm9yCmRvb3IKZG9zZQpkb3VibGUKZG92ZQpkcmFmdApkcmFnb24KZHJhbWEKZHJhc3RpYwpkcmF3CmRyZWFtCmRyZXNzCmRyaWZ0CmRyaWxsCmRyaW5rCmRyaXAKZHJpdmUKZHJvcApkcnVtCmRyeQpkdWNrCmR1bWIKZHVuZQpkdXJpbmcKZHVzdApkdXRjaApkdXR5CmR3YXJmCmR5bmFtaWMKZWFnZXIKZWFnbGUKZWFybHkKZWFybgplYXJ0aAplYXNpbHkKZWFzdAplYXN5CmVjaG8KZWNvbG9neQplY29ub215CmVkZ2UKZWRpdAplZHVjYXRlCmVmZm9ydAplZ2cKZWlnaHQKZWl0aGVyCmVsYm93CmVsZGVyCmVsZWN0cmljCmVsZWdhbnQKZWxlbWVudAplbGVwaGFudAplbGV2YXRvcgplbGl0ZQplbHNlCmVtYmFyawplbWJvZHkKZW1icmFjZQplbWVyZ2UKZW1vdGlvbgplbXBsb3kKZW1wb3dlcgplbXB0eQplbmFibGUKZW5hY3QKZW5kCmVuZGxlc3MKZW5kb3JzZQplbmVteQplbmVyZ3kKZW5mb3JjZQplbmdhZ2UKZW5naW5lCmVuaGFuY2UKZW5qb3kKZW5saXN0CmVub3VnaAplbnJpY2gKZW5yb2xsCmVuc3VyZQplbnRlcgplbnRpcmUKZW50cnkKZW52ZWxvcGUKZXBpc29kZQplcXVhbAplcXVpcAplcmEKZXJhc2UKZXJvZGUKZXJvc2lvbgplcnJvcgplcnVwdAplc2NhcGUKZXNzYXkKZXNzZW5jZQplc3RhdGUKZXRlcm5hbApldGhpY3MKZXZpZGVuY2UKZXZpbApldm9rZQpldm9sdmUKZXhhY3QKZXhhbXBsZQpleGNlc3MKZXhjaGFuZ2UKZXhjaXRlCmV4Y2x1ZGUKZXhjdXNlCmV4ZWN1dGUKZXhlcmNpc2UKZXhoYXVzdApleGhpYml0CmV4aWxlCmV4aXN0CmV4aXQKZXhvdGljCmV4cGFuZApleHBlY3QKZXhwaXJlCmV4cGxhaW4KZXhwb3NlCmV4cHJlc3MKZXh0ZW5kCmV4dHJhCmV5ZQpleWVicm93CmZhYnJpYwpmYWNlCmZhY3VsdHkKZmFkZQpmYWludApmYWl0aApmYWxsCmZhbHNlCmZhbWUKZmFtaWx5CmZhbW91cwpmYW4KZmFuY3kKZmFudGFzeQpmYXJtCmZhc2hpb24KZmF0CmZhdGFsCmZhdGhlcgpmYXRpZ3VlCmZhdWx0CmZhdm9yaXRlCmZlYXR1cmUKZmVicnVhcnkKZmVkZXJhbApmZWUKZmVlZApmZWVsCmZlbWFsZQpmZW5jZQpmZXN0aXZhbApmZXRjaApmZXZlcgpmZXcKZmliZXIKZmljdGlvbgpmaWVsZApmaWd1cmUKZmlsZQpmaWxtCmZpbHRlcgpmaW5hbApmaW5kCmZpbmUKZmluZ2VyCmZpbmlzaApmaXJlCmZpcm0KZmlyc3QKZmlzY2FsCmZpc2gKZml0CmZpdG5lc3MKZml4CmZsYWcKZmxhbWUKZmxhc2gKZmxhdApmbGF2b3IKZmxlZQpmbGlnaHQKZmxpcApmbG9hdApmbG9jawpmbG9vcgpmbG93ZXIKZmx1aWQKZmx1c2gKZmx5CmZvYW0KZm9jdXMKZm9nCmZvaWwKZm9sZApmb2xsb3cKZm9vZApmb290CmZvcmNlCmZvcmVzdApmb3JnZXQKZm9yawpmb3J0dW5lCmZvcnVtCmZvcndhcmQKZm9zc2lsCmZvc3Rlcgpmb3VuZApmb3gKZnJhZ2lsZQpmcmFtZQpmcmVxdWVudApmcmVzaApmcmllbmQKZnJpbmdlCmZyb2cKZnJvbnQKZnJvc3QKZnJvd24KZnJvemVuCmZydWl0CmZ1ZWwKZnVuCmZ1bm55CmZ1cm5hY2UKZnVyeQpmdXR1cmUKZ2FkZ2V0CmdhaW4KZ2FsYXh5CmdhbGxlcnkKZ2FtZQpnYXAKZ2FyYWdlCmdhcmJhZ2UKZ2FyZGVuCmdhcmxpYwpnYXJtZW50CmdhcwpnYXNwCmdhdGUKZ2F0aGVyCmdhdWdlCmdhemUKZ2VuZXJhbApnZW5pdXMKZ2VucmUKZ2VudGxlCmdlbnVpbmUKZ2VzdHVyZQpnaG9zdApnaWFudApnaWZ0CmdpZ2dsZQpnaW5nZXIKZ2lyYWZmZQpnaXJsCmdpdmUKZ2xhZApnbGFuY2UKZ2xhcmUKZ2xhc3MKZ2xpZGUKZ2xpbXBzZQpnbG9iZQpnbG9vbQpnbG9yeQpnbG92ZQpnbG93CmdsdWUKZ29hdApnb2RkZXNzCmdvbGQKZ29vZApnb29zZQpnb3JpbGxhCmdvc3BlbApnb3NzaXAKZ292ZXJuCmdvd24KZ3JhYgpncmFjZQpncmFpbgpncmFudApncmFwZQpncmFzcwpncmF2aXR5CmdyZWF0CmdyZWVuCmdyaWQKZ3JpZWYKZ3JpdApncm9jZXJ5Cmdyb3VwCmdyb3cKZ3J1bnQKZ3VhcmQKZ3Vlc3MKZ3VpZGUKZ3VpbHQKZ3VpdGFyCmd1bgpneW0KaGFiaXQKaGFpcgpoYWxmCmhhbW1lcgpoYW1zdGVyCmhhbmQKaGFwcHkKaGFyYm9yCmhhcmQKaGFyc2gKaGFydmVzdApoYXQKaGF2ZQpoYXdrCmhhemFyZApoZWFkCmhlYWx0aApoZWFydApoZWF2eQpoZWRnZWhvZwpoZWlnaHQKaGVsbG8KaGVsbWV0CmhlbHAKaGVuCmhlcm8KaGlkZGVuCmhpZ2gKaGlsbApoaW50CmhpcApoaXJlCmhpc3RvcnkKaG9iYnkKaG9ja2V5CmhvbGQKaG9sZQpob2xpZGF5CmhvbGxvdwpob21lCmhvbmV5Cmhvb2QKaG9wZQpob3JuCmhvcnJvcgpob3JzZQpob3NwaXRhbApob3N0CmhvdGVsCmhvdXIKaG92ZXIKaHViCmh1Z2UKaHVtYW4KaHVtYmxlCmh1bW9yCmh1bmRyZWQKaHVuZ3J5Cmh1bnQKaHVyZGxlCmh1cnJ5Cmh1cnQKaHVzYmFuZApoeWJyaWQKaWNlCmljb24KaWRlYQppZGVudGlmeQppZGxlCmlnbm9yZQppbGwKaWxsZWdhbAppbGxuZXNzCmltYWdlCmltaXRhdGUKaW1tZW5zZQppbW11bmUKaW1wYWN0CmltcG9zZQppbXByb3ZlCmltcHVsc2UKaW5jaAppbmNsdWRlCmluY29tZQppbmNyZWFzZQppbmRleAppbmRpY2F0ZQppbmRvb3IKaW5kdXN0cnkKaW5mYW50CmluZmxpY3QKaW5mb3JtCmluaGFsZQppbmhlcml0CmluaXRpYWwKaW5qZWN0CmluanVyeQppbm1hdGUKaW5uZXIKaW5ub2NlbnQKaW5wdXQKaW5xdWlyeQppbnNhbmUKaW5zZWN0Cmluc2lkZQppbnNwaXJlCmluc3RhbGwKaW50YWN0CmludGVyZXN0CmludG8KaW52ZXN0Cmludml0ZQppbnZvbHZlCmlyb24KaXNsYW5kCmlzb2xhdGUKaXNzdWUKaXRlbQppdm9yeQpqYWNrZXQKamFndWFyCmphcgpqYXp6CmplYWxvdXMKamVhbnMKamVsbHkKamV3ZWwKam9iCmpvaW4Kam9rZQpqb3VybmV5CmpveQpqdWRnZQpqdWljZQpqdW1wCmp1bmdsZQpqdW5pb3IKanVuawpqdXN0Cmthbmdhcm9vCmtlZW4Ka2VlcAprZXRjaHVwCmtleQpraWNrCmtpZApraWRuZXkKa2luZApraW5nZG9tCmtpc3MKa2l0CmtpdGNoZW4Ka2l0ZQpraXR0ZW4Ka2l3aQprbmVlCmtuaWZlCmtub2NrCmtub3cKbGFiCmxhYmVsCmxhYm9yCmxhZGRlcgpsYWR5Cmxha2UKbGFtcApsYW5ndWFnZQpsYXB0b3AKbGFyZ2UKbGF0ZXIKbGF0aW4KbGF1Z2gKbGF1bmRyeQpsYXZhCmxhdwpsYXduCmxhd3N1aXQKbGF5ZXIKbGF6eQpsZWFkZXIKbGVhZgpsZWFybgpsZWF2ZQpsZWN0dXJlCmxlZnQKbGVnCmxlZ2FsCmxlZ2VuZApsZWlzdXJlCmxlbW9uCmxlbmQKbGVuZ3RoCmxlbnMKbGVvcGFyZApsZXNzb24KbGV0dGVyCmxldmVsCmxpYXIKbGliZXJ0eQpsaWJyYXJ5CmxpY2Vuc2UKbGlmZQpsaWZ0CmxpZ2h0Cmxpa2UKbGltYgpsaW1pdApsaW5rCmxpb24KbGlxdWlkCmxpc3QKbGl0dGxlCmxpdmUKbGl6YXJkCmxvYWQKbG9hbgpsb2JzdGVyCmxvY2FsCmxvY2sKbG9naWMKbG9uZWx5CmxvbmcKbG9vcApsb3R0ZXJ5CmxvdWQKbG91bmdlCmxvdmUKbG95YWwKbHVja3kKbHVnZ2FnZQpsdW1iZXIKbHVuYXIKbHVuY2gKbHV4dXJ5Cmx5cmljcwptYWNoaW5lCm1hZAptYWdpYwptYWduZXQKbWFpZAptYWlsCm1haW4KbWFqb3IKbWFrZQptYW1tYWwKbWFuCm1hbmFnZQptYW5kYXRlCm1hbmdvCm1hbnNpb24KbWFudWFsCm1hcGxlCm1hcmJsZQptYXJjaAptYXJnaW4KbWFyaW5lCm1hcmtldAptYXJyaWFnZQptYXNrCm1hc3MKbWFzdGVyCm1hdGNoCm1hdGVyaWFsCm1hdGgKbWF0cml4Cm1hdHRlcgptYXhpbXVtCm1hemUKbWVhZG93Cm1lYW4KbWVhc3VyZQptZWF0Cm1lY2hhbmljCm1lZGFsCm1lZGlhCm1lbG9keQptZWx0Cm1lbWJlcgptZW1vcnkKbWVudGlvbgptZW51Cm1lcmN5Cm1lcmdlCm1lcml0Cm1lcnJ5Cm1lc2gKbWVzc2FnZQptZXRhbAptZXRob2QKbWlkZGxlCm1pZG5pZ2h0Cm1pbGsKbWlsbGlvbgptaW1pYwptaW5kCm1pbmltdW0KbWlub3IKbWludXRlCm1pcmFjbGUKbWlycm9yCm1pc2VyeQptaXNzCm1pc3Rha2UKbWl4Cm1peGVkCm1peHR1cmUKbW9iaWxlCm1vZGVsCm1vZGlmeQptb20KbW9tZW50Cm1vbml0b3IKbW9ua2V5Cm1vbnN0ZXIKbW9udGgKbW9vbgptb3JhbAptb3JlCm1vcm5pbmcKbW9zcXVpdG8KbW90aGVyCm1vdGlvbgptb3Rvcgptb3VudGFpbgptb3VzZQptb3ZlCm1vdmllCm11Y2gKbXVmZmluCm11bGUKbXVsdGlwbHkKbXVzY2xlCm11c2V1bQptdXNocm9vbQptdXNpYwptdXN0Cm11dHVhbApteXNlbGYKbXlzdGVyeQpteXRoCm5haXZlCm5hbWUKbmFwa2luCm5hcnJvdwpuYXN0eQpuYXRpb24KbmF0dXJlCm5lYXIKbmVjawpuZWVkCm5lZ2F0aXZlCm5lZ2xlY3QKbmVpdGhlcgpuZXBoZXcKbmVydmUKbmVzdApuZXQKbmV0d29yawpuZXV0cmFsCm5ldmVyCm5ld3MKbmV4dApuaWNlCm5pZ2h0Cm5vYmxlCm5vaXNlCm5vbWluZWUKbm9vZGxlCm5vcm1hbApub3J0aApub3NlCm5vdGFibGUKbm90ZQpub3RoaW5nCm5vdGljZQpub3ZlbApub3cKbnVjbGVhcgpudW1iZXIKbnVyc2UKbnV0Cm9hawpvYmV5Cm9iamVjdApvYmxpZ2UKb2JzY3VyZQpvYnNlcnZlCm9idGFpbgpvYnZpb3VzCm9jY3VyCm9jZWFuCm9jdG9iZXIKb2RvcgpvZmYKb2ZmZXIKb2ZmaWNlCm9mdGVuCm9pbApva2F5Cm9sZApvbGl2ZQpvbHltcGljCm9taXQKb25jZQpvbmUKb25pb24Kb25saW5lCm9ubHkKb3BlbgpvcGVyYQpvcGluaW9uCm9wcG9zZQpvcHRpb24Kb3JhbmdlCm9yYml0Cm9yY2hhcmQKb3JkZXIKb3JkaW5hcnkKb3JnYW4Kb3JpZW50Cm9yaWdpbmFsCm9ycGhhbgpvc3RyaWNoCm90aGVyCm91dGRvb3IKb3V0ZXIKb3V0cHV0Cm91dHNpZGUKb3ZhbApvdmVuCm92ZXIKb3duCm93bmVyCm94eWdlbgpveXN0ZXIKb3pvbmUKcGFjdApwYWRkbGUKcGFnZQpwYWlyCnBhbGFjZQpwYWxtCnBhbmRhCnBhbmVsCnBhbmljCnBhbnRoZXIKcGFwZXIKcGFyYWRlCnBhcmVudApwYXJrCnBhcnJvdApwYXJ0eQpwYXNzCnBhdGNoCnBhdGgKcGF0aWVudApwYXRyb2wKcGF0dGVybgpwYXVzZQpwYXZlCnBheW1lbnQKcGVhY2UKcGVhbnV0CnBlYXIKcGVhc2FudApwZWxpY2FuCnBlbgpwZW5hbHR5CnBlbmNpbApwZW9wbGUKcGVwcGVyCnBlcmZlY3QKcGVybWl0CnBlcnNvbgpwZXQKcGhvbmUKcGhvdG8KcGhyYXNlCnBoeXNpY2FsCnBpYW5vCnBpY25pYwpwaWN0dXJlCnBpZWNlCnBpZwpwaWdlb24KcGlsbApwaWxvdApwaW5rCnBpb25lZXIKcGlwZQpwaXN0b2wKcGl0Y2gKcGl6emEKcGxhY2UKcGxhbmV0CnBsYXN0aWMKcGxhdGUKcGxheQpwbGVhc2UKcGxlZGdlCnBsdWNrCnBsdWcKcGx1bmdlCnBvZW0KcG9ldApwb2ludApwb2xhcgpwb2xlCnBvbGljZQpwb25kCnBvbnkKcG9vbApwb3B1bGFyCnBvcnRpb24KcG9zaXRpb24KcG9zc2libGUKcG9zdApwb3RhdG8KcG90dGVyeQpwb3ZlcnR5CnBvd2Rlcgpwb3dlcgpwcmFjdGljZQpwcmFpc2UKcHJlZGljdApwcmVmZXIKcHJlcGFyZQpwcmVzZW50CnByZXR0eQpwcmV2ZW50CnByaWNlCnByaWRlCnByaW1hcnkKcHJpbnQKcHJpb3JpdHkKcHJpc29uCnByaXZhdGUKcHJpemUKcHJvYmxlbQpwcm9jZXNzCnByb2R1Y2UKcHJvZml0CnByb2dyYW0KcHJvamVjdApwcm9tb3RlCnByb29mCnByb3BlcnR5CnByb3NwZXIKcHJvdGVjdApwcm91ZApwcm92aWRlCnB1YmxpYwpwdWRkaW5nCnB1bGwKcHVscApwdWxzZQpwdW1wa2luCnB1bmNoCnB1cGlsCnB1cHB5CnB1cmNoYXNlCnB1cml0eQpwdXJwb3NlCnB1cnNlCnB1c2gKcHV0CnB1enpsZQpweXJhbWlkCnF1YWxpdHkKcXVhbnR1bQpxdWFydGVyCnF1ZXN0aW9uCnF1aWNrCnF1aXQKcXVpegpxdW90ZQpyYWJiaXQKcmFjY29vbgpyYWNlCnJhY2sKcmFkYXIKcmFkaW8KcmFpbApyYWluCnJhaXNlCnJhbGx5CnJhbXAKcmFuY2gKcmFuZG9tCnJhbmdlCnJhcGlkCnJhcmUKcmF0ZQpyYXRoZXIKcmF2ZW4KcmF3CnJhem9yCnJlYWR5CnJlYWwKcmVhc29uCnJlYmVsCnJlYnVpbGQKcmVjYWxsCnJlY2VpdmUKcmVjaXBlCnJlY29yZApyZWN5Y2xlCnJlZHVjZQpyZWZsZWN0CnJlZm9ybQpyZWZ1c2UKcmVnaW9uCnJlZ3JldApyZWd1bGFyCnJlamVjdApyZWxheApyZWxlYXNlCnJlbGllZgpyZWx5CnJlbWFpbgpyZW1lbWJlcgpyZW1pbmQKcmVtb3ZlCnJlbmRlcgpyZW5ldwpyZW50CnJlb3BlbgpyZXBhaXIKcmVwZWF0CnJlcGxhY2UKcmVwb3J0CnJlcXVpcmUKcmVzY3VlCnJlc2VtYmxlCnJlc2lzdApyZXNvdXJjZQpyZXNwb25zZQpyZXN1bHQKcmV0aXJlCnJldHJlYXQKcmV0dXJuCnJldW5pb24KcmV2ZWFsCnJldmlldwpyZXdhcmQKcmh5dGhtCnJpYgpyaWJib24KcmljZQpyaWNoCnJpZGUKcmlkZ2UKcmlmbGUKcmlnaHQKcmlnaWQKcmluZwpyaW90CnJpcHBsZQpyaXNrCnJpdHVhbApyaXZhbApyaXZlcgpyb2FkCnJvYXN0CnJvYm90CnJvYnVzdApyb2NrZXQKcm9tYW5jZQpyb29mCnJvb2tpZQpyb29tCnJvc2UKcm90YXRlCnJvdWdoCnJvdW5kCnJvdXRlCnJveWFsCnJ1YmJlcgpydWRlCnJ1ZwpydWxlCnJ1bgpydW53YXkKcnVyYWwKc2FkCnNhZGRsZQpzYWRuZXNzCnNhZmUKc2FpbApzYWxhZApzYWxtb24Kc2Fsb24Kc2FsdApzYWx1dGUKc2FtZQpzYW1wbGUKc2FuZApzYXRpc2Z5CnNhdG9zaGkKc2F1Y2UKc2F1c2FnZQpzYXZlCnNheQpzY2FsZQpzY2FuCnNjYXJlCnNjYXR0ZXIKc2NlbmUKc2NoZW1lCnNjaG9vbApzY2llbmNlCnNjaXNzb3JzCnNjb3JwaW9uCnNjb3V0CnNjcmFwCnNjcmVlbgpzY3JpcHQKc2NydWIKc2VhCnNlYXJjaApzZWFzb24Kc2VhdApzZWNvbmQKc2VjcmV0CnNlY3Rpb24Kc2VjdXJpdHkKc2VlZApzZWVrCnNlZ21lbnQKc2VsZWN0CnNlbGwKc2VtaW5hcgpzZW5pb3IKc2Vuc2UKc2VudGVuY2UKc2VyaWVzCnNlcnZpY2UKc2Vzc2lvbgpzZXR0bGUKc2V0dXAKc2V2ZW4Kc2hhZG93CnNoYWZ0CnNoYWxsb3cKc2hhcmUKc2hlZApzaGVsbApzaGVyaWZmCnNoaWVsZApzaGlmdApzaGluZQpzaGlwCnNoaXZlcgpzaG9jawpzaG9lCnNob290CnNob3AKc2hvcnQKc2hvdWxkZXIKc2hvdmUKc2hyaW1wCnNocnVnCnNodWZmbGUKc2h5CnNpYmxpbmcKc2ljawpzaWRlCnNpZWdlCnNpZ2h0CnNpZ24Kc2lsZW50CnNpbGsKc2lsbHkKc2lsdmVyCnNpbWlsYXIKc2ltcGxlCnNpbmNlCnNpbmcKc2lyZW4Kc2lzdGVyCnNpdHVhdGUKc2l4CnNpemUKc2thdGUKc2tldGNoCnNraQpza2lsbApza2luCnNraXJ0CnNrdWxsCnNsYWIKc2xhbQpzbGVlcApzbGVuZGVyCnNsaWNlCnNsaWRlCnNsaWdodApzbGltCnNsb2dhbgpzbG90CnNsb3cKc2x1c2gKc21hbGwKc21hcnQKc21pbGUKc21va2UKc21vb3RoCnNuYWNrCnNuYWtlCnNuYXAKc25pZmYKc25vdwpzb2FwCnNvY2Nlcgpzb2NpYWwKc29jawpzb2RhCnNvZnQKc29sYXIKc29sZGllcgpzb2xpZApzb2x1dGlvbgpzb2x2ZQpzb21lb25lCnNvbmcKc29vbgpzb3JyeQpzb3J0CnNvdWwKc291bmQKc291cApzb3VyY2UKc291dGgKc3BhY2UKc3BhcmUKc3BhdGlhbApzcGF3bgpzcGVhawpzcGVjaWFsCnNwZWVkCnNwZWxsCnNwZW5kCnNwaGVyZQpzcGljZQpzcGlkZXIKc3Bpa2UKc3BpbgpzcGlyaXQKc3BsaXQKc3BvaWwKc3BvbnNvcgpzcG9vbgpzcG9ydApzcG90CnNwcmF5CnNwcmVhZApzcHJpbmcKc3B5CnNxdWFyZQpzcXVlZXplCnNxdWlycmVsCnN0YWJsZQpzdGFkaXVtCnN0YWZmCnN0YWdlCnN0YWlycwpzdGFtcApzdGFuZApzdGFydApzdGF0ZQpzdGF5CnN0ZWFrCnN0ZWVsCnN0ZW0Kc3RlcApzdGVyZW8Kc3RpY2sKc3RpbGwKc3RpbmcKc3RvY2sKc3RvbWFjaApzdG9uZQpzdG9vbApzdG9yeQpzdG92ZQpzdHJhdGVneQpzdHJlZXQKc3RyaWtlCnN0cm9uZwpzdHJ1Z2dsZQpzdHVkZW50CnN0dWZmCnN0dW1ibGUKc3R5bGUKc3ViamVjdApzdWJtaXQKc3Vid2F5CnN1Y2Nlc3MKc3VjaApzdWRkZW4Kc3VmZmVyCnN1Z2FyCnN1Z2dlc3QKc3VpdApzdW1tZXIKc3VuCnN1bm55CnN1bnNldApzdXBlcgpzdXBwbHkKc3VwcmVtZQpzdXJlCnN1cmZhY2UKc3VyZ2UKc3VycHJpc2UKc3Vycm91bmQKc3VydmV5CnN1c3BlY3QKc3VzdGFpbgpzd2FsbG93CnN3YW1wCnN3YXAKc3dhcm0Kc3dlYXIKc3dlZXQKc3dpZnQKc3dpbQpzd2luZwpzd2l0Y2gKc3dvcmQKc3ltYm9sCnN5bXB0b20Kc3lydXAKc3lzdGVtCnRhYmxlCnRhY2tsZQp0YWcKdGFpbAp0YWxlbnQKdGFsawp0YW5rCnRhcGUKdGFyZ2V0CnRhc2sKdGFzdGUKdGF0dG9vCnRheGkKdGVhY2gKdGVhbQp0ZWxsCnRlbgp0ZW5hbnQKdGVubmlzCnRlbnQKdGVybQp0ZXN0CnRleHQKdGhhbmsKdGhhdAp0aGVtZQp0aGVuCnRoZW9yeQp0aGVyZQp0aGV5CnRoaW5nCnRoaXMKdGhvdWdodAp0aHJlZQp0aHJpdmUKdGhyb3cKdGh1bWIKdGh1bmRlcgp0aWNrZXQKdGlkZQp0aWdlcgp0aWx0CnRpbWJlcgp0aW1lCnRpbnkKdGlwCnRpcmVkCnRpc3N1ZQp0aXRsZQp0b2FzdAp0b2JhY2NvCnRvZGF5CnRvZGRsZXIKdG9lCnRvZ2V0aGVyCnRvaWxldAp0b2tlbgp0b21hdG8KdG9tb3Jyb3cKdG9uZQp0b25ndWUKdG9uaWdodAp0b29sCnRvb3RoCnRvcAp0b3BpYwp0b3BwbGUKdG9yY2gKdG9ybmFkbwp0b3J0b2lzZQp0b3NzCnRvdGFsCnRvdXJpc3QKdG93YXJkCnRvd2VyCnRvd24KdG95CnRyYWNrCnRyYWRlCnRyYWZmaWMKdHJhZ2ljCnRyYWluCnRyYW5zZmVyCnRyYXAKdHJhc2gKdHJhdmVsCnRyYXkKdHJlYXQKdHJlZQp0cmVuZAp0cmlhbAp0cmliZQp0cmljawp0cmlnZ2VyCnRyaW0KdHJpcAp0cm9waHkKdHJvdWJsZQp0cnVjawp0cnVlCnRydWx5CnRydW1wZXQKdHJ1c3QKdHJ1dGgKdHJ5CnR1YmUKdHVpdGlvbgp0dW1ibGUKdHVuYQp0dW5uZWwKdHVya2V5CnR1cm4KdHVydGxlCnR3ZWx2ZQp0d2VudHkKdHdpY2UKdHdpbgp0d2lzdAp0d28KdHlwZQp0eXBpY2FsCnVnbHkKdW1icmVsbGEKdW5hYmxlCnVuYXdhcmUKdW5jbGUKdW5jb3Zlcgp1bmRlcgp1bmRvCnVuZmFpcgp1bmZvbGQKdW5oYXBweQp1bmlmb3JtCnVuaXF1ZQp1bml0CnVuaXZlcnNlCnVua25vd24KdW5sb2NrCnVudGlsCnVudXN1YWwKdW52ZWlsCnVwZGF0ZQp1cGdyYWRlCnVwaG9sZAp1cG9uCnVwcGVyCnVwc2V0CnVyYmFuCnVyZ2UKdXNhZ2UKdXNlCnVzZWQKdXNlZnVsCnVzZWxlc3MKdXN1YWwKdXRpbGl0eQp2YWNhbnQKdmFjdXVtCnZhZ3VlCnZhbGlkCnZhbGxleQp2YWx2ZQp2YW4KdmFuaXNoCnZhcG9yCnZhcmlvdXMKdmFzdAp2YXVsdAp2ZWhpY2xlCnZlbHZldAp2ZW5kb3IKdmVudHVyZQp2ZW51ZQp2ZXJiCnZlcmlmeQp2ZXJzaW9uCnZlcnkKdmVzc2VsCnZldGVyYW4KdmlhYmxlCnZpYnJhbnQKdmljaW91cwp2aWN0b3J5CnZpZGVvCnZpZXcKdmlsbGFnZQp2aW50YWdlCnZpb2xpbgp2aXJ0dWFsCnZpcnVzCnZpc2EKdmlzaXQKdmlzdWFsCnZpdGFsCnZpdmlkCnZvY2FsCnZvaWNlCnZvaWQKdm9sY2Fubwp2b2x1bWUKdm90ZQp2b3lhZ2UKd2FnZQp3YWdvbgp3YWl0CndhbGsKd2FsbAp3YWxudXQKd2FudAp3YXJmYXJlCndhcm0Kd2Fycmlvcgp3YXNoCndhc3AKd2FzdGUKd2F0ZXIKd2F2ZQp3YXkKd2VhbHRoCndlYXBvbgp3ZWFyCndlYXNlbAp3ZWF0aGVyCndlYgp3ZWRkaW5nCndlZWtlbmQKd2VpcmQKd2VsY29tZQp3ZXN0CndldAp3aGFsZQp3aGF0CndoZWF0CndoZWVsCndoZW4Kd2hlcmUKd2hpcAp3aGlzcGVyCndpZGUKd2lkdGgKd2lmZQp3aWxkCndpbGwKd2luCndpbmRvdwp3aW5lCndpbmcKd2luawp3aW5uZXIKd2ludGVyCndpcmUKd2lzZG9tCndpc2UKd2lzaAp3aXRuZXNzCndvbGYKd29tYW4Kd29uZGVyCndvb2QKd29vbAp3b3JkCndvcmsKd29ybGQKd29ycnkKd29ydGgKd3JhcAp3cmVjawp3cmVzdGxlCndyaXN0CndyaXRlCndyb25nCnlhcmQKeWVhcgp5ZWxsb3cKeW91CnlvdW5nCnlvdXRoCnplYnJhCnplcm8Kem9uZQp6b28K" +} diff --git a/packages/crypto/src/testdata/keccak.json b/packages/crypto/src/testdata/keccak.json new file mode 100644 index 00000000..22e6eecd --- /dev/null +++ b/packages/crypto/src/testdata/keccak.json @@ -0,0 +1,1076 @@ +{ + "keccak256": [ + { + "in": "", + "out": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + }, + { + "in": "cc", + "out": "eead6dbfc7340a56caedc044696a168870549a6a7f6f56961e84a54bd9970b8a" + }, + { + "in": "41fb", + "out": "a8eaceda4d47b3281a795ad9e1ea2122b407baf9aabcb9e18b5717b7873537d2" + }, + { + "in": "1f877c", + "out": "627d7bc1491b2ab127282827b8de2d276b13d7d70fb4c5957fdf20655bc7ac30" + }, + { + "in": "c1ecfdfc", + "out": "b149e766d7612eaf7d55f74e1a4fdd63709a8115b14f61fcd22aa4abc8b8e122" + }, + { + "in": "21f134ac57", + "out": "67f05544dbe97d5d6417c1b1ea9bc0e3a99a541381d1cd9b08a9765687eb5bb4" + }, + { + "in": "c6f50bb74e29", + "out": "923062c4e6f057597220d182dbb10e81cd25f60b54005b2a75dd33d6dac518d0" + }, + { + "in": "119713cc83eeef", + "out": "feb8405dcd315d48c6cbf7a3504996de8e25cc22566efec67433712eda99894f" + }, + { + "in": "4a4f202484512526", + "out": "e620d8f2982b24fedaaa3baa9b46c3f9ce204ee356666553ecb35e15c3ff9bf9" + }, + { + "in": "1f66ab4185ed9b6375", + "out": "9e03f7c9a3d055eca1d786ed6fb624d93f1cf0ac27f9c2b6c05e509fac9e7fca" + }, + { + "in": "eed7422227613b6f53c9", + "out": "caad8e1ed546630748a12f5351b518a9a431cda6ba56cbfc3ccbdd8aae5092f7" + }, + { + "in": "eaeed5cdffd89dece455f1", + "out": "d61708bdb3211a9aab28d4df01dfa4b29ed40285844d841042257e97488617b0" + }, + { + "in": "5be43c90f22902e4fe8ed2d3", + "out": "0f53be55990780b3fad9870f04f7d8153c3ae605c057c85abb5d71765043aaa8" + }, + { + "in": "a746273228122f381c3b46e4f1", + "out": "32215ae88204a782b62d1810d945de49948de458600f5e1e3896ceca2ed3292b" + }, + { + "in": "3c5871cd619c69a63b540eb5a625", + "out": "9510da68e58ebb8d2ab9de8485bb408e358299a9c011ae8544b0d0faf9d4a4ea" + }, + { + "in": "fa22874bcc068879e8ef11a69f0722", + "out": "f20b3bcf743aa6fa084038520791c364cb6d3d1dd75841f8d7021cd98322bd8f" + }, + { + "in": "52a608ab21ccdd8a4457a57ede782176", + "out": "0e32defa2071f0b5ac0e6a108b842ed0f1d3249712f58ee0ddf956fe332a5f95" + }, + { + "in": "82e192e4043ddcd12ecf52969d0f807eed", + "out": "9204550677b9aa770e6e93e319b9958540d54ff4dccb063c8561302cd8aff676" + }, + { + "in": "75683dcb556140c522543bb6e9098b21a21e", + "out": "a6d5444cb7aa61f5106cdedb39d5e1dd7d608f102798d7e818ac87289123a1db" + }, + { + "in": "06e4efe45035e61faaf4287b4d8d1f12ca97e5", + "out": "5796b993d0bd1257cf26782b4e58fafb22b0986d88684ab5a2e6cec6706275f9" + }, + { + "in": "e26193989d06568fe688e75540aea06747d9f851", + "out": "cfbe73c6585be6204dd473abe356b539477174c4b770bfc91e9fdbcbc57086e6" + }, + { + "in": "d8dc8fdefbdce9d44e4cbafe78447bae3b5436102a", + "out": "31c8006b0ec35e690674297cb27476db6066b5fa9825c60728e9e0bb338fb7c3" + }, + { + "in": "57085fd7e14216ab102d8317b0cb338a786d5fc32d8f", + "out": "3b8fa3904fe1b837565a50d0fbf03e487d6d72fc3cea41adcce33df1b835d247" + }, + { + "in": "a05404df5dbb57697e2c16fa29defac8ab3560d6126fa0", + "out": "37febc4df9d50daeabd0caa6578812a687e55f1ac0b109d2512810d00548c85b" + }, + { + "in": "aecbb02759f7433d6fcb06963c74061cd83b5b3ffa6f13c6", + "out": "2329810b5a4735bcd49c10e6456c0b1ded5eac258af47cbb797ca162ab6d1ba8" + }, + { + "in": "aafdc9243d3d4a096558a360cc27c8d862f0be73db5e88aa55", + "out": "6fffa070b865be3ee766dc2db49b6aa55c369f7de3703ada2612d754145c01e6" + }, + { + "in": "7bc84867f6f9e9fdc3e1046cae3a52c77ed485860ee260e30b15", + "out": "b30761c053e926f150b9dce7e005b4d87811ccfb9e3b6edb0221022f01711cf0" + }, + { + "in": "fac523575a99ec48279a7a459e98ff901918a475034327efb55843", + "out": "04f1b3c1e25ba5d012e22ad144e5a8719d94322d05ad9ef61e7db49b59959b3a" + }, + { + "in": "0f8b2d8fcfd9d68cffc17ccfb117709b53d26462a3f346fb7c79b85e", + "out": "aeef4b4da420834ffced26db291248fb2d01e765e2b0564057f8e6c2030ac37f" + }, + { + "in": "a963c3e895ff5a0be4824400518d81412f875fa50521e26e85eac90c04", + "out": "03d26aeeb4a7bdddbff7cff667198c425941a2776922df2bec545f5304e2c61c" + }, + { + "in": "03a18688b10cc0edf83adf0a84808a9718383c4070c6c4f295098699ac2c", + "out": "435cfc0d1afd8d5509a9ccbf49706575038685bf08db549d9714548240463ee9" + }, + { + "in": "84fb51b517df6c5accb5d022f8f28da09b10232d42320ffc32dbecc3835b29", + "out": "d477fb02caaa95b3280ec8ee882c29d9e8a654b21ef178e0f97571bf9d4d3c1c" + }, + { + "in": "9f2fcc7c90de090d6b87cd7e9718c1ea6cb21118fc2d5de9f97e5db6ac1e9c10", + "out": "24dd2ee02482144f539f810d2caa8a7b75d0fa33657e47932122d273c3f6f6d1" + }, + { + "in": "de8f1b3faa4b7040ed4563c3b8e598253178e87e4d0df75e4ff2f2dedd5a0be046", + "out": "e78c421e6213aff8de1f025759a4f2c943db62bbde359c8737e19b3776ed2dd2" + }, + { + "in": "62f154ec394d0bc757d045c798c8b87a00e0655d0481a7d2d9fb58d93aedc676b5a0", + "out": "cce3e3d498328a4d9c5b4dbf9a1209628ab82621ad1a0d0a18680362889e6164" + }, + { + "in": "b2dcfe9ff19e2b23ce7da2a4207d3e5ec7c6112a8a22aec9675a886378e14e5bfbad4e", + "out": "f871db93c5c92ecd65d4edb96fcb12e4729bc2a1899f7fb029f50bff431cbb72" + }, + { + "in": "47f5697ac8c31409c0868827347a613a3562041c633cf1f1f86865a576e02835ed2c2492", + "out": "4eb143477431df019311aed936cab91a912ec1e6868b71e9eddb777408d4af34" + }, + { + "in": "512a6d292e67ecb2fe486bfe92660953a75484ff4c4f2eca2b0af0edcdd4339c6b2ee4e542", + "out": "9a0c1d50a59dbf657f6713c795ed14e1f23b4eaa137c5540aacdb0a7e32c29fc" + }, + { + "in": "973cf2b4dcf0bfa872b41194cb05bb4e16760a1840d8343301802576197ec19e2a1493d8f4fb", + "out": "ba062e5d370216d11985c4ca7a2658ddc7328b4be4b40a52dd8fa3ca662f09d1" + }, + { + "in": "80beebcd2e3f8a9451d4499961c9731ae667cdc24ea020ce3b9aa4bbc0a7f79e30a934467da4b0", + "out": "3a083ae163df42bd51b9c664bee9dc4362f16e63383df16473df71be6dd40c1c" + }, + { + "in": "7abaa12ec2a7347674e444140ae0fb659d08e1c66decd8d6eae925fa451d65f3c0308e29446b8ed3", + "out": "4876e273ac00942576d9608d5b63ecc9a3e75d5e0c42c6abdbcde037785af9a7" + }, + { + "in": "c88dee9927679b8af422abcbacf283b904ff31e1cac58c7819809f65d5807d46723b20f67ba610c2b7", + "out": "4797ba1c7ab7197050d6b2e506f2df4550e4b673df78f18c465424e48df5e997" + }, + { + "in": "01e43fe350fcec450ec9b102053e6b5d56e09896e0ddd9074fe138e6038210270c834ce6eadc2bb86bf6", + "out": "41c91be98c5813a4c5d8ae7c29b9919c1cc95b4a05f82433948cb99d9a6d039c" + }, + { + "in": "337023370a48b62ee43546f17c4ef2bf8d7ecd1d49f90bab604b839c2e6e5bd21540d29ba27ab8e309a4b7", + "out": "ee354290e3f9ce9123c49ba616e1a2684a90f3ddd84e73a1d2c232f740412b18" + }, + { + "in": "6892540f964c8c74bd2db02c0ad884510cb38afd4438af31fc912756f3efec6b32b58ebc38fc2a6b913596a8", + "out": "fbec0b6d71696eede900b77aa6d7d25f4ab45df8961ca9c8b3f4f9b51af983ab" + }, + { + "in": "f5961dfd2b1ffffda4ffbf30560c165bfedab8ce0be525845deb8dc61004b7db38467205f5dcfb34a2acfe96c0", + "out": "9d24aeea08f9a4b5fb8b6de85a2296f5f4108ddd1eea4f8ee58819cf84edb765" + }, + { + "in": "ca061a2eb6ceed8881ce2057172d869d73a1951e63d57261384b80ceb5451e77b06cf0f5a0ea15ca907ee1c27eba", + "out": "732034cae3ff1116f07fc18b5a26ef8faf3fe75d3dbca05e48795365e0a17c40" + }, + { + "in": "1743a77251d69242750c4f1140532cd3c33f9b5ccdf7514e8584d4a5f9fbd730bcf84d0d4726364b9bf95ab251d9bb", + "out": "deac521805bc6a97c0870e9e225d1c4b2fd8f3a9a7f6b39e357c26414821e2dd" + }, + { + "in": "d8faba1f5194c4db5f176fabfff856924ef627a37cd08cf55608bba8f1e324d7c7f157298eabc4dce7d89ce5162499f9", + "out": "ad55537347b20d9fca02683e6de1032ec10eb84da4cbd501e49744a666292edf" + }, + { + "in": "be9684be70340860373c9c482ba517e899fc81baaa12e5c6d7727975d1d41ba8bef788cdb5cf4606c9c1c7f61aed59f97d", + "out": "b1f990204bf630569a3edc634864274786f40ce1c57165ee32d0e29f5d0c6851" + }, + { + "in": "7e15d2b9ea74ca60f66c8dfab377d9198b7b16deb6a1ba0ea3c7ee2042f89d3786e779cf053c77785aa9e692f821f14a7f51", + "out": "fa460cd51bc611786d364fcabe39052bcd5f009edfa81f4701c5b22b729b0016" + }, + { + "in": "9a219be43713bd578015e9fda66c0f2d83cac563b776ab9f38f3e4f7ef229cb443304fba401efb2bdbd7ece939102298651c86", + "out": "f7b0fe5a69ff44060d4f6ad2486e6cde9ed679af9aa1ada613e4cc392442beb5" + }, + { + "in": "c8f2b693bd0d75ef99caebdc22adf4088a95a3542f637203e283bbc3268780e787d68d28cc3897452f6a22aa8573ccebf245972a", + "out": "24204d491f202534859fc0a208237184471a2d801fb3b934d0968d0d843d0345" + }, + { + "in": "ec0f99711016c6a2a07ad80d16427506ce6f441059fd269442baaa28c6ca037b22eeac49d5d894c0bf66219f2c08e9d0e8ab21de52", + "out": "81147cba0647eee78c4784874c0557621a138ca781fb6f5dcd0d9c609af56f35" + }, + { + "in": "0dc45181337ca32a8222fe7a3bf42fc9f89744259cff653504d6051fe84b1a7ffd20cb47d4696ce212a686bb9be9a8ab1c697b6d6a33", + "out": "5b6d7eda559574fae882e6266f4c2be362133e44b5a947ecb6e75db9fc8567e0" + }, + { + "in": "de286ba4206e8b005714f80fb1cdfaebde91d29f84603e4a3ebc04686f99a46c9e880b96c574825582e8812a26e5a857ffc6579f63742f", + "out": "86f87e75c87f9be39e4aa6d0c5a37a5964d6ffdc462525c0642c9db010de38ee" + }, + { + "in": "eebcc18057252cbf3f9c070f1a73213356d5d4bc19ac2a411ec8cdeee7a571e2e20eaf61fd0c33a0ffeb297ddb77a97f0a415347db66bcaf", + "out": "959fe007b57c2947c36d1d66cc0808d80db7df45d68a34852b70d2dda192c25c" + }, + { + "in": "416b5cdc9fe951bd361bd7abfc120a5054758eba88fdd68fd84e39d3b09ac25497d36b43cbe7b85a6a3cebda8db4e5549c3ee51bb6fcb6ac1e", + "out": "1a93567eebc41cc44d9346cde646005d3e82de8eeeb131e9c1f6d1e4afd260f7" + }, + { + "in": "5c5faf66f32e0f8311c32e8da8284a4ed60891a5a7e50fb2956b3cbaa79fc66ca376460e100415401fc2b8518c64502f187ea14bfc9503759705", + "out": "549db056b65edf7d05bd66661b6d0a39b29b825bc80910f8bf7060a53bff68e1" + }, + { + "in": "7167e1e02be1a7ca69d788666f823ae4eef39271f3c26a5cf7cee05bca83161066dc2e217b330df821103799df6d74810eed363adc4ab99f36046a", + "out": "794abfd7eb622d5608c1c7b3f0a7821a71900b7172847fb0907aa2899972663e" + }, + { + "in": "2fda311dbba27321c5329510fae6948f03210b76d43e7448d1689a063877b6d14c4f6d0eaa96c150051371f7dd8a4119f7da5c483cc3e6723c01fb7d", + "out": "9ce89958cbddd8dcb22f66e8cba5f6091a51953189464803bdc773abc7faa906" + }, + { + "in": "95d1474a5aab5d2422aca6e481187833a6212bd2d0f91451a67dd786dfc91dfed51b35f47e1deb8a8ab4b9cb67b70179cc26f553ae7b569969ce151b8d", + "out": "6da733817dc826e8da773beca7338131ab7396417104eda25970980c4eb2a15f" + }, + { + "in": "c71bd7941f41df044a2927a8ff55b4b467c33d089f0988aa253d294addbdb32530c0d4208b10d9959823f0c0f0734684006df79f7099870f6bf53211a88d", + "out": "66c9cdc8e8c6c9417d7ffbef3b54b702eee5f01a9bda8dd4e28fe3335debbb51" + }, + { + "in": "f57c64006d9ea761892e145c99df1b24640883da79d9ed5262859dcda8c3c32e05b03d984f1ab4a230242ab6b78d368dc5aaa1e6d3498d53371e84b0c1d4ba", + "out": "24ab37a93674ccb1ceec9e5681efc8bdf9fcc7721cf1cac175e0b20e461575b8" + }, + { + "in": "e926ae8b0af6e53176dbffcc2a6b88c6bd765f939d3d178a9bde9ef3aa131c61e31c1e42cdfaf4b4dcde579a37e150efbef5555b4c1cb40439d835a724e2fae7", + "out": "574271cd13959e8ddeae5bfbdb02a3fdf54f2babfd0cbeb893082a974957d0c1" + }, + { + "in": "16e8b3d8f988e9bb04de9c96f2627811c973ce4a5296b4772ca3eefeb80a652bdf21f50df79f32db23f9f73d393b2d57d9a0297f7a2f2e79cfda39fa393df1ac00", + "out": "1947e901fa59ea789845775f2a4db9b4848f8a776073d53d84cbd5d927a96bff" + }, + { + "in": "fc424eeb27c18a11c01f39c555d8b78a805b88dba1dc2a42ed5e2c0ec737ff68b2456d80eb85e11714fa3f8eabfb906d3c17964cb4f5e76b29c1765db03d91be37fc", + "out": "0c1b8c1af237e9c5501b50316a80865aac08a34acf4f8bedd4a2d6e7b7bcbb85" + }, + { + "in": "abe3472b54e72734bdba7d9158736464251c4f21b33fbbc92d7fac9a35c4e3322ff01d2380cbaa4ef8fb07d21a2128b7b9f5b6d9f34e13f39c7ffc2e72e47888599ba5", + "out": "c4315666c71fea834d8ff27f025f5cc34f37c1aae78604a4b08dac45decd42be" + }, + { + "in": "36f9f0a65f2ca498d739b944d6eff3da5ebba57e7d9c41598a2b0e4380f3cf4b479ec2348d015ffe6256273511154afcf3b4b4bf09d6c4744fdd0f62d75079d440706b05", + "out": "5ff8734db3f9977eee9cf5e2cf725c57af09926490c55abd9d00a42e91a8c344" + }, + { + "in": "abc87763cae1ca98bd8c5b82caba54ac83286f87e9610128ae4de68ac95df5e329c360717bd349f26b872528492ca7c94c2c1e1ef56b74dbb65c2ac351981fdb31d06c77a4", + "out": "1e141a171cab085252ea4c2f8f1f1087dd85a75ab3acd0b3c28eaa5735d349af" + }, + { + "in": "94f7ca8e1a54234c6d53cc734bb3d3150c8ba8c5f880eab8d25fed13793a9701ebe320509286fd8e422e931d99c98da4df7e70ae447bab8cffd92382d8a77760a259fc4fbd72", + "out": "ef763f22f359dd7f5b3fe6a745c423d6b641ec07ba5235232a0701510f74426e" + }, + { + "in": "13bd2811f6ed2b6f04ff3895aceed7bef8dcd45eb121791bc194a0f806206bffc3b9281c2b308b1a729ce008119dd3066e9378acdcc50a98a82e20738800b6cddbe5fe9694ad6d", + "out": "6a769f93f255b078fe73aff68f0422a279939920e4690b4aff0e433cfa3d3df3" + }, + { + "in": "1eed9cba179a009ec2ec5508773dd305477ca117e6d569e66b5f64c6bc64801ce25a8424ce4a26d575b8a6fb10ead3fd1992edddeec2ebe7150dc98f63adc3237ef57b91397aa8a7", + "out": "c06dd4261638c44afcb186f0af5de20ea53aa63316fbb71728f874ff3daceb0d" + }, + { + "in": "ba5b67b5ec3a3ffae2c19dd8176a2ef75c0cd903725d45c9cb7009a900c0b0ca7a2967a95ae68269a6dbf8466c7b6844a1d608ac661f7eff00538e323db5f2c644b78b2d48de1a08aa", + "out": "b5d84b1809e83b5e75aa53bdee79e3a97f3fe3a7d3162ebd4908240ff69131d8" + }, + { + "in": "0efa26ac5673167dcacab860932ed612f65ff49b80fa9ae65465e5542cb62075df1c5ae54fba4db807be25b070033efa223bdd5b1d3c94c6e1909c02b620d4b1b3a6c9fed24d70749604", + "out": "cad7abb5bba5905b5181dd2dbc4e68cfd01ba8659f21c8290d3f835c1a68bbe5" + }, + { + "in": "bbfd933d1fd7bf594ac7f435277dc17d8d5a5b8e4d13d96d2f64e771abbd51a5a8aea741beccbddb177bcea05243ebd003cfdeae877cca4da94605b67691919d8b033f77d384ca01593c1b", + "out": "83ca09c1f418b5dad0a7f64a904a2e07c3314f7d02d92622f8f4674bc1f6aa3d" + }, + { + "in": "90078999fd3c35b8afbf4066cbde335891365f0fc75c1286cdd88fa51fab94f9b8def7c9ac582a5dbcd95817afb7d1b48f63704e19c2baa4df347f48d4a6d603013c23f1e9611d595ebac37c", + "out": "330de3ee16aef6711461a994863eed47af71b362d4c2f243534ef432f63a091a" + }, + { + "in": "64105eca863515c20e7cfbaa0a0b8809046164f374d691cdbd6508aaabc1819f9ac84b52bafc1b0fe7cddbc554b608c01c8904c669d8db316a0953a4c68ece324ec5a49ffdb59a1bd6a292aa0e", + "out": "b5675197e49b357218f7118cd15ee773b39bd59b224d9a45ca71c6e371d938f1" + }, + { + "in": "d4654be288b9f3b711c2d02015978a8cc57471d5680a092aa534f7372c71ceaab725a383c4fcf4d8deaa57fca3ce056f312961eccf9b86f14981ba5bed6ab5b4498e1f6c82c6cae6fc14845b3c8a", + "out": "cd9038c1066a59990df5752107b066eebbe672cbca0f60d687d03a9d821934be" + }, + { + "in": "12d9394888305ac96e65f2bf0e1b18c29c90fe9d714dd59f651f52b88b3008c588435548066ea2fc4c101118c91f32556224a540de6efddbca296ef1fb00341f5b01fecfc146bdb251b3bdad556cd2", + "out": "d3172ca263aff2b9db6fb13337f2543c5af51151801a76194012f710306c14f6" + }, + { + "in": "871a0d7a5f36c3da1dfce57acd8ab8487c274fad336bc137ebd6ff4658b547c1dcfab65f037aa58f35ef16aff4abe77ba61f65826f7be681b5b6d5a1ea8085e2ae9cd5cf0991878a311b549a6d6af230", + "out": "9e3d4bcf580eece39bcf13e5716e5bb8f5e8c3fc3723f66246f836d8db1238f1" + }, + { + "in": "e90b4ffef4d457bc7711ff4aa72231ca25af6b2e206f8bf859d8758b89a7cd36105db2538d06da83bad5f663ba11a5f6f61f236fd5f8d53c5e89f183a3cec615b50c7c681e773d109ff7491b5cc22296c5", + "out": "edc2d3b49c85b8dd75f7b5128da04cd76bf4878779a0077af3f1d7fb44f18931" + }, + { + "in": "e728de62d75856500c4c77a428612cd804f30c3f10d36fb219c5ca0aa30726ab190e5f3f279e0733d77e7267c17be27d21650a9a4d1e32f649627638dbada9702c7ca303269ed14014b2f3cf8b894eac8554", + "out": "80dce7f04dd6ac17ce709b56cf6ea6c0a57190649bb187b5e6d95fa18100c7ac" + }, + { + "in": "6348f229e7b1df3b770c77544e5166e081850fa1c6c88169db74c76e42eb983facb276ad6a0d1fa7b50d3e3b6fcd799ec97470920a7abed47d288ff883e24ca21c7f8016b93bb9b9e078bdb9703d2b781b616e", + "out": "49bbd5435d2706f85fe77b84a5fa15ddd8259e5d2c20fb947f139373e5c86121" + }, + { + "in": "4b127fde5de733a1680c2790363627e63ac8a3f1b4707d982caea258655d9bf18f89afe54127482ba01e08845594b671306a025c9a5c5b6f93b0a39522dc877437be5c2436cbf300ce7ab6747934fcfc30aeaaf6", + "out": "6b6c11f9731d60789d713daf53d2eb10ab9ccf15430ea5d1249be06edfe2bff6" + }, + { + "in": "08461f006cff4cc64b752c957287e5a0faabc05c9bff89d23fd902d324c79903b48fcb8f8f4b01f3e4ddb483593d25f000386698f5ade7faade9615fdc50d32785ea51d49894e45baa3dc707e224688c6408b68b11", + "out": "7e738e8eb3d47d18e97d87c7b3fc681f86417883ced92ba93c3077812bbd17e7" + }, + { + "in": "68c8f8849b120e6e0c9969a5866af591a829b92f33cd9a4a3196957a148c49138e1e2f5c7619a6d5edebe995acd81ec8bb9c7b9cfca678d081ea9e25a75d39db04e18d475920ce828b94e72241f24db72546b352a0e4", + "out": "a278ba93ba0d7cd2677be08c9dfc5f516a37f722bb06565fa22500f66fe031a9" + }, + { + "in": "b8d56472954e31fb54e28fca743f84d8dc34891cb564c64b08f7b71636debd64ca1edbdba7fc5c3e40049ce982bba8c7e0703034e331384695e9de76b5104f2fbc4535ecbeebc33bc27f29f18f6f27e8023b0fbb6f563c", + "out": "9c0a9f0da113d39f491b7da6c4da5d84fe1cc46367e5acc433ca3e0500951738" + }, + { + "in": "0d58ac665fa84342e60cefee31b1a4eacdb092f122dfc68309077aed1f3e528f578859ee9e4cefb4a728e946324927b675cd4f4ac84f64db3dacfe850c1dd18744c74ceccd9fe4dc214085108f404eab6d8f452b5442a47d", + "out": "6bed496d02fe4cc27d96dceed14a67da7bdf75e19b624896dff6b0b68e4fcc12" + }, + { + "in": "1755e2d2e5d1c1b0156456b539753ff416651d44698e87002dcf61dcfa2b4e72f264d9ad591df1fdee7b41b2eb00283c5aebb3411323b672eaa145c5125185104f20f335804b02325b6dea65603f349f4d5d8b782dd3469ccd", + "out": "ecd2e3faf4ba4dd67e5a8656cebebdb24611611678e92eb60f7cbd3111d0a345" + }, + { + "in": "b180de1a611111ee7584ba2c4b020598cd574ac77e404e853d15a101c6f5a2e5c801d7d85dc95286a1804c870bb9f00fd4dcb03aa8328275158819dcad7253f3e3d237aeaa7979268a5db1c6ce08a9ec7c2579783c8afc1f91a7", + "out": "634a95a7e8ba58f7818a13903ec8f3411b6ecb7e389ec9aa97c0ecf87fadd588" + }, + { + "in": "cf3583cbdfd4cbc17063b1e7d90b02f0e6e2ee05f99d77e24e560392535e47e05077157f96813544a17046914f9efb64762a23cf7a49fe52a0a4c01c630cfe8727b81fb99a89ff7cc11dca5173057e0417b8fe7a9efba6d95c555f", + "out": "a0fe352ba2389b0430edbe1201032eb09c255514c5c5b529c4baafceb1ac9817" + }, + { + "in": "072fc02340ef99115bad72f92c01e4c093b9599f6cfc45cb380ee686cb5eb019e806ab9bd55e634ab10aa62a9510cc0672cd3eddb589c7df2b67fcd3329f61b1a4441eca87a33c8f55da4fbbad5cf2b2527b8e983bb31a2fadec7523", + "out": "9a0bfe14f9f3127aca86773a620945731df781a6d7dc82930ccde2f69dac8f94" + }, + { + "in": "76eecf956a52649f877528146de33df249cd800e21830f65e90f0f25ca9d6540fde40603230eca6760f1139c7f268deba2060631eea92b1fff05f93fd5572fbe29579ecd48bc3a8d6c2eb4a6b26e38d6c5fbf2c08044aeea470a8f2f26", + "out": "19e5101bde60b200a8b171e4c3ea3dfd913e10111d96f9682acc7467282b4e31" + }, + { + "in": "7adc0b6693e61c269f278e6944a5a2d8300981e40022f839ac644387bfac9086650085c2cdc585fea47b9d2e52d65a2b29a7dc370401ef5d60dd0d21f9e2b90fae919319b14b8c5565b0423cefb827d5f1203302a9d01523498a4db10374", + "out": "4cc2aff141987f4c2e683fa2de30042bacdcd06087d7a7b014996e9cfeaa58ce" + }, + { + "in": "e1fffa9826cce8b86bccefb8794e48c46cdf372013f782eced1e378269b7be2b7bf51374092261ae120e822be685f2e7a83664bcfbe38fe8633f24e633ffe1988e1bc5acf59a587079a57a910bda60060e85b5f5b6f776f0529639d9cce4bd", + "out": "9a8ce819894efccc2153b239c3adc3f07d0968eac5ec8080ac0174f2d5e6959c" + }, + { + "in": "69f9abba65592ee01db4dce52dbab90b08fc04193602792ee4daa263033d59081587b09bbe49d0b49c9825d22840b2ff5d9c5155f975f8f2c2e7a90c75d2e4a8040fe39f63bbafb403d9e28cc3b86e04e394a9c9e8065bd3c85fa9f0c7891600", + "out": "8b35768525f59ac77d35522ac885831a9947299e114a8956fe5bca103db7bb2c" + }, + { + "in": "38a10a352ca5aedfa8e19c64787d8e9c3a75dbf3b8674bfab29b5dbfc15a63d10fae66cd1a6e6d2452d557967eaad89a4c98449787b0b3164ca5b717a93f24eb0b506ceb70cbbcb8d72b2a72993f909aad92f044e0b5a2c9ac9cb16a0ca2f81f49", + "out": "955f1f7e4e54660b26f30086f2dddaedd32813547c1b95d305d882682b4ff7a0" + }, + { + "in": "6d8c6e449bc13634f115749c248c17cd148b72157a2c37bf8969ea83b4d6ba8c0ee2711c28ee11495f43049596520ce436004b026b6c1f7292b9c436b055cbb72d530d860d1276a1502a5140e3c3f54a93663e4d20edec32d284e25564f624955b52", + "out": "8fac5a34ebafa38b55333624a9514fe97d9956e74309c5252cd2090d3bbe2f9e" + }, + { + "in": "6efcbcaf451c129dbe00b9cef0c3749d3ee9d41c7bd500ade40cdc65dedbbbadb885a5b14b32a0c0d087825201e303288a733842fa7e599c0c514e078f05c821c7a4498b01c40032e9f1872a1c925fa17ce253e8935e4c3c71282242cb716b2089ccc1", + "out": "62039e0f53869480f88c87bb3d19a31aad32878f27f2c4e78ff02bbea2b8b0b9" + }, + { + "in": "433c5303131624c0021d868a30825475e8d0bd3052a022180398f4ca4423b98214b6beaac21c8807a2c33f8c93bd42b092cc1b06cedf3224d5ed1ec29784444f22e08a55aa58542b524b02cd3d5d5f6907afe71c5d7462224a3f9d9e53e7e0846dcbb4ce", + "out": "ce87a5173bffd92399221658f801d45c294d9006ee9f3f9d419c8d427748dc41" + }, + { + "in": "a873e0c67ca639026b6683008f7aa6324d4979550e9bce064ca1e1fb97a30b147a24f3f666c0a72d71348ede701cf2d17e2253c34d1ec3b647dbcef2f879f4eb881c4830b791378c901eb725ea5c172316c6d606e0af7df4df7f76e490cd30b2badf45685f", + "out": "2ef8907b60108638e50eac535cc46ca02e04581ddb4235fbac5cb5c53583e24b" + }, + { + "in": "006917b64f9dcdf1d2d87c8a6173b64f6587168e80faa80f82d84f60301e561e312d9fbce62f39a6fb476e01e925f26bcc91de621449be6504c504830aae394096c8fc7694651051365d4ee9070101ec9b68086f2ea8f8ab7b811ea8ad934d5c9b62c60a4771", + "out": "be8b5bd36518e9c5f4c768fc02461bb3d39a5d00edef82cec7df351df80238e0" + }, + { + "in": "f13c972c52cb3cc4a4df28c97f2df11ce089b815466be88863243eb318c2adb1a417cb1041308598541720197b9b1cb5ba2318bd5574d1df2174af14884149ba9b2f446d609df240ce335599957b8ec80876d9a085ae084907bc5961b20bf5f6ca58d5dab38adb", + "out": "52cbc5dbe49b009663c43f079dd180e38a77533778062a72a29e864a58522922" + }, + { + "in": "e35780eb9799ad4c77535d4ddb683cf33ef367715327cf4c4a58ed9cbdcdd486f669f80189d549a9364fa82a51a52654ec721bb3aab95dceb4a86a6afa93826db923517e928f33e3fba850d45660ef83b9876accafa2a9987a254b137c6e140a21691e1069413848", + "out": "3a8dfcfd1b362003ddfa17910727539e64b18021abba018b5f58d71f7a449733" + }, + { + "in": "64ec021c9585e01ffe6d31bb50d44c79b6993d72678163db474947a053674619d158016adb243f5c8d50aa92f50ab36e579ff2dabb780a2b529370daa299207cfbcdd3a9a25006d19c4f1fe33e4b1eaec315d8c6ee1e730623fd1941875b924eb57d6d0c2edc4e78d6", + "out": "fa221deee80e25e53c6c448aa22028b72501f07d1ff2c3fc7f93af9838b2d0a9" + }, + { + "in": "5954bab512cf327d66b5d9f296180080402624ad7628506b555eea8382562324cf452fba4a2130de3e165d11831a270d9cb97ce8c2d32a96f50d71600bb4ca268cf98e90d6496b0a6619a5a8c63db6d8a0634dfc6c7ec8ea9c006b6c456f1b20cd19e781af20454ac880", + "out": "ed9c8b87fce27be4e95610db1ddd0c035847f4699dfc8c039a798a30343a6059" + }, + { + "in": "03d9f92b2c565709a568724a0aff90f8f347f43b02338f94a03ed32e6f33666ff5802da4c81bdce0d0e86c04afd4edc2fc8b4141c2975b6f07639b1994c973d9a9afce3d9d365862003498513bfa166d2629e314d97441667b007414e739d7febf0fe3c32c17aa188a8683", + "out": "a485cc9cf4ca4f659f89a0b791a4423953424ac57146b879d385a9e4062afe52" + }, + { + "in": "f31e8b4f9e0621d531d22a380be5d9abd56faec53cbd39b1fab230ea67184440e5b1d15457bd25f56204fa917fa48e669016cb48c1ffc1e1e45274b3b47379e00a43843cf8601a5551411ec12503e5aac43d8676a1b2297ec7a0800dbfee04292e937f21c005f17411473041", + "out": "93cd4369a7796239a5cdf78bce22ebb2137a631c3a613d5e35816d2a64a34947" + }, + { + "in": "758ea3fea738973db0b8be7e599bbef4519373d6e6dcd7195ea885fc991d896762992759c2a09002912fb08e0cb5b76f49162aeb8cf87b172cf3ad190253df612f77b1f0c532e3b5fc99c2d31f8f65011695a087a35ee4eee5e334c369d8ee5d29f695815d866da99df3f79403", + "out": "3751ce08750d927eb5c3ae4ca62a703a481d86a4fa1c011e812b4bc0a2fef08d" + }, + { + "in": "47c6e0c2b74948465921868804f0f7bd50dd323583dc784f998a93cd1ca4c6ef84d41dc81c2c40f34b5bee6a93867b3bdba0052c5f59e6f3657918c382e771d33109122cc8bb0e1e53c4e3d13b43ce44970f5e0c079d2ad7d7a3549cd75760c21bb15b447589e86e8d76b1e9ced2", + "out": "a88c7ef7b89b7b6f75d83922b8fd00f034d719f97c67884121434447ae9dd3b9" + }, + { + "in": "f690a132ab46b28edfa6479283d6444e371c6459108afd9c35dbd235e0b6b6ff4c4ea58e7554bd002460433b2164ca51e868f7947d7d7a0d792e4abf0be5f450853cc40d85485b2b8857ea31b5ea6e4ccfa2f3a7ef3380066d7d8979fdac618aad3d7e886dea4f005ae4ad05e5065f", + "out": "2b4f8f9ef7d6ed60bb4881e635e0f887a51b0c1a42bab077976b43d2c715e11a" + }, + { + "in": "58d6a99bc6458824b256916770a8417040721cccfd4b79eacd8b65a3767ce5ba7e74104c985ac56b8cc9aebd16febd4cda5adb130b0ff2329cc8d611eb14dac268a2f9e633c99de33997fea41c52a7c5e1317d5b5daed35eba7d5a60e45d1fa7eaabc35f5c2b0a0f2379231953322c4e", + "out": "586cffdc434313cc4e133e85ac88b3e5dea71818abcac236f0aae418f72b6cde" + }, + { + "in": "befab574396d7f8b6705e2d5b58b2c1c820bb24e3f4bae3e8fbcd36dbf734ee14e5d6ab972aedd3540235466e825850ee4c512ea9795abfd33f330d9fd7f79e62bbb63a6ea85de15beaeea6f8d204a28956059e2632d11861dfb0e65bc07ac8a159388d5c3277e227286f65ff5e5b5aec1", + "out": "52d14ab96b24aa4a7a55721aa8550b1fccac3653c78234783f7295ae5f39a17a" + }, + { + "in": "8e58144fa9179d686478622ce450c748260c95d1ba43b8f9b59abeca8d93488da73463ef40198b4d16fb0b0707201347e0506ff19d01bea0f42b8af9e71a1f1bd168781069d4d338fdef00bf419fbb003031df671f4a37979564f69282de9c65407847dd0da505ab1641c02dea4f0d834986", + "out": "b6345edd966030cf70dfb5b7552bc141c42efe7a7e84f957b1baf4671bae4354" + }, + { + "in": "b55c10eae0ec684c16d13463f29291bf26c82e2fa0422a99c71db4af14dd9c7f33eda52fd73d017cc0f2dbe734d831f0d820d06d5f89dacc485739144f8cfd4799223b1aff9031a105cb6a029ba71e6e5867d85a554991c38df3c9ef8c1e1e9a7630be61caabca69280c399c1fb7a12d12aefc", + "out": "0347901965d3635005e75a1095695cca050bc9ed2d440c0372a31b348514a889" + }, + { + "in": "2eeea693f585f4ed6f6f8865bbae47a6908aecd7c429e4bec4f0de1d0ca0183fa201a0cb14a529b7d7ac0e6ff6607a3243ee9fb11bcf3e2304fe75ffcddd6c5c2e2a4cd45f63c962d010645058d36571404a6d2b4f44755434d76998e83409c3205aa1615db44057db991231d2cb42624574f545", + "out": "f0bf7105870f2382b76863bb97aee79f95ae0e8142675bbccdb3475b0c99352f" + }, + { + "in": "dab11dc0b047db0420a585f56c42d93175562852428499f66a0db811fcdddab2f7cdffed1543e5fb72110b64686bc7b6887a538ad44c050f1e42631bc4ec8a9f2a047163d822a38989ee4aab01b4c1f161b062d873b1cfa388fd301514f62224157b9bef423c7783b7aac8d30d65cd1bba8d689c2d", + "out": "631c6f5abe50b27c9dea557fc3fbd3fb25781fcb1bbf9f2e010cca20ec52dbc4" + }, + { + "in": "42e99a2f80aee0e001279a2434f731e01d34a44b1a8101726921c0590c30f3120eb83059f325e894a5ac959dca71ce2214799916424e859d27d789437b9d27240bf8c35adbafcecc322b48aa205b293962d858652abacbd588bcf6cbc388d0993bd622f96ed54614c25b6a9aa527589eaaffcf17ddf7", + "out": "3757a53d195b43b403a796a74aafb2064072a69e372ee5b36cc2b7a791f75c9f" + }, + { + "in": "3c9b46450c0f2cae8e3823f8bdb4277f31b744ce2eb17054bddc6dff36af7f49fb8a2320cc3bdf8e0a2ea29ad3a55de1165d219adeddb5175253e2d1489e9b6fdd02e2c3d3a4b54d60e3a47334c37913c5695378a669e9b72dec32af5434f93f46176ebf044c4784467c700470d0c0b40c8a088c815816", + "out": "0cc903acbced724b221d34877d1d1427182f9493a33df7758720e8bfc7af98ee" + }, + { + "in": "d1e654b77cb155f5c77971a64df9e5d34c26a3cad6c7f6b300d39deb1910094691adaa095be4ba5d86690a976428635d5526f3e946f7dc3bd4dbc78999e653441187a81f9adcd5a3c5f254bc8256b0158f54673dcc1232f6e918ebfc6c51ce67eaeb042d9f57eec4bfe910e169af78b3de48d137df4f2840", + "out": "f23750c32973f24c2422f4e2b43589d9e76d6a575938e01a96ae8e73d026569c" + }, + { + "in": "626f68c18a69a6590159a9c46be03d5965698f2dac3de779b878b3d9c421e0f21b955a16c715c1ec1e22ce3eb645b8b4f263f60660ea3028981eebd6c8c3a367285b691c8ee56944a7cd1217997e1d9c21620b536bdbd5de8925ff71dec6fbc06624ab6b21e329813de90d1e572dfb89a18120c3f606355d25", + "out": "1ece87e44a99f59d26411418fb8793689ff8a9c6ef75599056087d8c995bce1e" + }, + { + "in": "651a6fb3c4b80c7c68c6011675e6094eb56abf5fc3057324ebc6477825061f9f27e7a94633abd1fa598a746e4a577caf524c52ec1788471f92b8c37f23795ca19d559d446cab16cbcdce90b79fa1026cee77bf4ab1b503c5b94c2256ad75b3eac6fd5dcb96aca4b03a834bfb4e9af988cecbf2ae597cb9097940", + "out": "71b4f90ac9215d7474b1197d1b8b24449fd57e9b05483d32edbebcb21a82f866" + }, + { + "in": "8aaf072fce8a2d96bc10b3c91c809ee93072fb205ca7f10abd82ecd82cf040b1bc49ea13d1857815c0e99781de3adbb5443ce1c897e55188ceaf221aa9681638de05ae1b322938f46bce51543b57ecdb4c266272259d1798de13be90e10efec2d07484d9b21a3870e2aa9e06c21aa2d0c9cf420080a80a91dee16f", + "out": "3b3678bb116fadab484291f0cf972606523501f5b45d51063797972928e333c0" + }, + { + "in": "53f918fd00b1701bd504f8cdea803acca21ac18c564ab90c2a17da592c7d69688f6580575395551e8cd33e0fef08ca6ed4588d4d140b3e44c032355df1c531564d7f4835753344345a6781e11cd5e095b73df5f82c8ae3ad00877936896671e947cc52e2b29dcd463d90a0c9929128da222b5a211450bbc0e02448e2", + "out": "4068246495f508897813332962d3ae0b84685045e832a9a39ad5e94c154d2679" + }, + { + "in": "a64599b8a61b5ccec9e67aed69447459c8da3d1ec6c7c7c82a7428b9b584fa67e90f68e2c00fbbed4613666e5168da4a16f395f7a3c3832b3b134bfc9cbaa95d2a0fe252f44ac6681eb6d40ab91c1d0282fed6701c57463d3c5f2bb8c6a7301fb4576aa3b5f15510db8956ff77478c26a7c09bea7b398cfc83503f538e", + "out": "82696259536520e5e4d47e106bd1dcb397529aafb75878f332d2af2684493f1b" + }, + { + "in": "0e3ab0e054739b00cdb6a87bd12cae024b54cb5e550e6c425360c2e87e59401f5ec24ef0314855f0f56c47695d56a7fb1417693af2a1ed5291f2fee95f75eed54a1b1c2e81226fbff6f63ade584911c71967a8eb70933bc3f5d15bc91b5c2644d9516d3c3a8c154ee48e118bd1442c043c7a0dba5ac5b1d5360aae5b9065", + "out": "b494852603393b2b71845bacbdce89fa1427dfe4af9cdf925d4f93fa83b9966b" + }, + { + "in": "a62fc595b4096e6336e53fcdfc8d1cc175d71dac9d750a6133d23199eaac288207944cea6b16d27631915b4619f743da2e30a0c00bbdb1bbb35ab852ef3b9aec6b0a8dcc6e9e1abaa3ad62ac0a6c5de765de2c3711b769e3fde44a74016fff82ac46fa8f1797d3b2a726b696e3dea5530439acee3a45c2a51bc32dd055650b", + "out": "d8a619c0dfbed2a9498a147b53d7b33dd653d390e5c0cd691f02c8608822d06a" + }, + { + "in": "2b6db7ced8665ebe9deb080295218426bdaa7c6da9add2088932cdffbaa1c14129bccdd70f369efb149285858d2b1d155d14de2fdb680a8b027284055182a0cae275234cc9c92863c1b4ab66f304cf0621cd54565f5bff461d3b461bd40df28198e3732501b4860eadd503d26d6e69338f4e0456e9e9baf3d827ae685fb1d817", + "out": "d82e257d000dc9fa279a00e2961e3286d2fe1c02ef59833ab8a6a7101bc25054" + }, + { + "in": "10db509b2cdcaba6c062ae33be48116a29eb18e390e1bbada5ca0a2718afbcd23431440106594893043cc7f2625281bf7de2655880966a23705f0c5155c2f5cca9f2c2142e96d0a2e763b70686cd421b5db812daced0c6d65035fde558e94f26b3e6dde5bd13980cc80292b723013bd033284584bff27657871b0cf07a849f4ae2", + "out": "8d5b7dbf3947219acdb04fb2e11a84a313c54c22f2ae858dfc8887bf6265f5f3" + }, + { + "in": "9334de60c997bda6086101a6314f64e4458f5ff9450c509df006e8c547983c651ca97879175aaba0c539e82d05c1e02c480975cbb30118121061b1ebac4f8d9a3781e2db6b18042e01ecf9017a64a0e57447ec7fcbe6a7f82585f7403ee2223d52d37b4bf426428613d6b4257980972a0acab508a7620c1cb28eb4e9d30fc41361ec", + "out": "607c3f31342c3ee5c93e552a8dd79fa86dccae2c1b58aabac25b5918acfa4da5" + }, + { + "in": "e88ab086891693aa535ceb20e64c7ab97c7dd3548f3786339897a5f0c39031549ca870166e477743ccfbe016b4428d89738e426f5ffe81626137f17aecff61b72dbee2dc20961880cfe281dfab5ee38b1921881450e16032de5e4d55ad8d4fca609721b0692bac79be5a06e177fe8c80c0c83519fb3347de9f43d5561cb8107b9b5edc", + "out": "0656de9dcd7b7112a86c7ba199637d2c1c9e9cfbb713e4ede79f8862ee69993f" + }, + { + "in": "fd19e01a83eb6ec810b94582cb8fbfa2fcb992b53684fb748d2264f020d3b960cb1d6b8c348c2b54a9fcea72330c2aaa9a24ecdb00c436abc702361a82bb8828b85369b8c72ece0082fe06557163899c2a0efa466c33c04343a839417057399a63a3929be1ee4805d6ce3e5d0d0967fe9004696a5663f4cac9179006a2ceb75542d75d68", + "out": "4ddd6224858299f3378e3f5a0ecc52fa4c419c8ebb20f635c4c43f36324ecb4e" + }, + { + "in": "59ae20b6f7e0b3c7a989afb28324a40fca25d8651cf1f46ae383ef6d8441587aa1c04c3e3bf88e8131ce6145cfb8973d961e8432b202fa5af3e09d625faad825bc19da9b5c6c20d02abda2fcc58b5bd3fe507bf201263f30543819510c12bc23e2ddb4f711d087a86edb1b355313363a2de996b891025e147036087401ccf3ca7815bf3c49", + "out": "ec096314e2f73b6a7027fffa02104c2f6dd187f20c743445befd4b5c034b3295" + }, + { + "in": "77ee804b9f3295ab2362798b72b0a1b2d3291dceb8139896355830f34b3b328561531f8079b79a6e9980705150866402fdc176c05897e359a6cb1a7ab067383eb497182a7e5aef7038e4c96d133b2782917417e391535b5e1b51f47d8ed7e4d4025fe98dc87b9c1622614bff3d1029e68e372de719803857ca52067cddaad958951cb2068cc6", + "out": "fe71d01c2ee50e054d6b07147ef62954fde7e6959d6eeba68e3c94107eb0084d" + }, + { + "in": "b771d5cef5d1a41a93d15643d7181d2a2ef0a8e84d91812f20ed21f147bef732bf3a60ef4067c3734b85bc8cd471780f10dc9e8291b58339a677b960218f71e793f2797aea349406512829065d37bb55ea796fa4f56fd8896b49b2cd19b43215ad967c712b24e5032d065232e02c127409d2ed4146b9d75d763d52db98d949d3b0fed6a8052fbb", + "out": "bd6f5492582a7c1b116304de28314df9fffe95b0da11af52fe9440a717a34859" + }, + { + "in": "b32d95b0b9aad2a8816de6d06d1f86008505bd8c14124f6e9a163b5a2ade55f835d0ec3880ef50700d3b25e42cc0af050ccd1be5e555b23087e04d7bf9813622780c7313a1954f8740b6ee2d3f71f768dd417f520482bd3a08d4f222b4ee9dbd015447b33507dd50f3ab4247c5de9a8abd62a8decea01e3b87c8b927f5b08beb37674c6f8e380c04", + "out": "e717a7769448abbe5fef8187954a88ac56ded1d22e63940ab80d029585a21921" + }, + { + "in": "04410e31082a47584b406f051398a6abe74e4da59bb6f85e6b49e8a1f7f2ca00dfba5462c2cd2bfde8b64fb21d70c083f11318b56a52d03b81cac5eec29eb31bd0078b6156786da3d6d8c33098c5c47bb67ac64db14165af65b44544d806dde5f487d5373c7f9792c299e9686b7e5821e7c8e2458315b996b5677d926dac57b3f22da873c601016a0d", + "out": "a95d50b50b4545f0947441df74a1e9d74622eb3baa49c1bbfc3a0cce6619c1aa" + }, + { + "in": "8b81e9badde026f14d95c019977024c9e13db7a5cd21f9e9fc491d716164bbacdc7060d882615d411438aea056c340cdf977788f6e17d118de55026855f93270472d1fd18b9e7e812bae107e0dfde7063301b71f6cfe4e225cab3b232905a56e994f08ee2891ba922d49c3dafeb75f7c69750cb67d822c96176c46bd8a29f1701373fb09a1a6e3c7158f", + "out": "ed53d72595ace3a6d5166a4ede41cce362d644bded772be616b87bcf678a6364" + }, + { + "in": "fa6eed24da6666a22208146b19a532c2ec9ba94f09f1def1e7fc13c399a48e41acc2a589d099276296348f396253b57cb0e40291bd282773656b6e0d8bea1cda084a3738816a840485fcf3fb307f777fa5feac48695c2af4769720258c77943fb4556c362d9cba8bf103aeb9034baa8ea8bfb9c4f8e6742ce0d52c49ea8e974f339612e830e9e7a9c29065", + "out": "810401b247c23529e24655cab86c42df44085da76ca01c9a14618e563b7c41be" + }, + { + "in": "9bb4af1b4f09c071ce3cafa92e4eb73ce8a6f5d82a85733440368dee4eb1cbc7b55ac150773b6fe47dbe036c45582ed67e23f4c74585dab509df1b83610564545642b2b1ec463e18048fc23477c6b2aa035594ecd33791af6af4cbc2a1166aba8d628c57e707f0b0e8707caf91cd44bdb915e0296e0190d56d33d8dde10b5b60377838973c1d943c22ed335e", + "out": "9f01e63f2355393ecb1908d0caf39718833004a4bf37ebf4cf8d7319b65172df" + }, + { + "in": "2167f02118cc62043e9091a647cadbed95611a521fe0d64e8518f16c808ab297725598ae296880a773607a798f7c3cfce80d251ebec6885015f9abf7eaabae46798f82cb5926de5c23f44a3f9f9534b3c6f405b5364c2f8a8bdc5ca49c749bed8ce4ba48897062ae8424ca6dde5f55c0e42a95d1e292ca54fb46a84fbc9cd87f2d0c9e7448de3043ae22fdd229", + "out": "7ec11de7db790a850281f043592779b409195db4ecedeefbb93ba683d3bca851" + }, + { + "in": "94b7fa0bc1c44e949b1d7617d31b4720cbe7ca57c6fa4f4094d4761567e389ecc64f6968e4064df70df836a47d0c713336b5028b35930d29eb7a7f9a5af9ad5cf441745baec9bb014ceeff5a41ba5c1ce085feb980bab9cf79f2158e03ef7e63e29c38d7816a84d4f71e0f548b7fc316085ae38a060ff9b8dec36f91ad9ebc0a5b6c338cbb8f6659d342a24368cf", + "out": "a74af9c523b4a08d9db9692ea89255977a5919b9292b7cd0d92c90c97c98e224" + }, + { + "in": "ea40e83cb18b3a242c1ecc6ccd0b7853a439dab2c569cfc6dc38a19f5c90acbf76aef9ea3742ff3b54ef7d36eb7ce4ff1c9ab3bc119cff6be93c03e208783335c0ab8137be5b10cdc66ff3f89a1bddc6a1eed74f504cbe7290690bb295a872b9e3fe2cee9e6c67c41db8efd7d863cf10f840fe618e7936da3dca5ca6df933f24f6954ba0801a1294cd8d7e66dfafec", + "out": "344d129c228359463c40555d94213d015627e5871c04f106a0feef9361cdecb6" + }, + { + "in": "157d5b7e4507f66d9a267476d33831e7bb768d4d04cc3438da12f9010263ea5fcafbde2579db2f6b58f911d593d5f79fb05fe3596e3fa80ff2f761d1b0e57080055c118c53e53cdb63055261d7c9b2b39bd90acc32520cbbdbda2c4fd8856dbcee173132a2679198daf83007a9b5c51511ae49766c792a29520388444ebefe28256fb33d4260439cba73a9479ee00c63", + "out": "4ce7c2b935f21fc34c5e56d940a555c593872aec2f896de4e68f2a017060f535" + }, + { + "in": "836b34b515476f613fe447a4e0c3f3b8f20910ac89a3977055c960d2d5d2b72bd8acc715a9035321b86703a411dde0466d58a59769672aa60ad587b8481de4bba552a1645779789501ec53d540b904821f32b0bd1855b04e4848f9f8cfe9ebd8911be95781a759d7ad9724a7102dbe576776b7c632bc39b9b5e19057e226552a5994c1dbb3b5c7871a11f5537011044c53", + "out": "24b69d8ab35baccbd92f94e1b70b07c4c0ecf14eaeac4b6b8560966d5be086f3" + }, + { + "in": "cc7784a4912a7ab5ad3620aab29ba87077cd3cb83636adc9f3dc94f51edf521b2161ef108f21a0a298557981c0e53ce6ced45bdf782c1ef200d29bab81dd6460586964edab7cebdbbec75fd7925060f7da2b853b2b089588fa0f8c16ec6498b14c55dcee335cb3a91d698e4d393ab8e8eac0825f8adebeee196df41205c011674e53426caa453f8de1cbb57932b0b741d4c6", + "out": "19f34215373e8e80f686953e03ca472b50216719cb515e0667d4e686e45fcf7c" + }, + { + "in": "7639b461fff270b2455ac1d1afce782944aea5e9087eb4a39eb96bb5c3baaf0e868c8526d3404f9405e79e77bfac5ffb89bf1957b523e17d341d7323c302ea7083872dd5e8705694acdda36d5a1b895aaa16eca6104c82688532c8bfe1790b5dc9f4ec5fe95baed37e1d287be710431f1e5e8ee105bc42ed37d74b1e55984bf1c09fe6a1fa13ef3b96faeaed6a2a1950a12153", + "out": "290bd4808e5676eb0c978084e4cd68e745031659a26807ad615b10cda589b969" + }, + { + "in": "eb6513fc61b30cfba58d4d7e80f94d14589090cf1d80b1df2e68088dc6104959ba0d583d585e9578ab0aec0cf36c48435eb52ed9ab4bbce7a5abe679c97ae2dbe35e8cc1d45b06dda3cf418665c57cbee4bbb47fa4caf78f4ee656fec237fe4eebbafa206e1ef2bd0ee4ae71bd0e9b2f54f91daadf1febfd7032381d636b733dcb3bf76fb14e23aff1f68ed3dbcf75c9b99c6f26", + "out": "70999ab9818309afa8f1adc4fea47a071a8abd94012f7ce28cc794a0d997c5cb" + }, + { + "in": "1594d74bf5dde444265d4c04dad9721ff3e34cbf622daf341fe16b96431f6c4df1f760d34f296eb97d98d560ad5286fec4dce1724f20b54fd7df51d4bf137add656c80546fb1bf516d62ee82baa992910ef4cc18b70f3f8698276fcfb44e0ec546c2c39cfd8ee91034ff9303058b4252462f86c823eb15bf481e6b79cc3a02218595b3658e8b37382bd5048eaed5fd02c37944e73b", + "out": "83120033b0140fe3e3e1cbfebff323abc08535c0aa017803f5d2f4ecb35f5dfb" + }, + { + "in": "4cfa1278903026f66fedd41374558be1b585d03c5c55dac94361df286d4bd39c7cb8037ed3b267b07c346626449d0cc5b0dd2cf221f7e4c3449a4be99985d2d5e67bff2923357ddeab5abcb4619f3a3a57b2cf928a022eb27676c6cf805689004fca4d41ea6c2d0a4789c7605f7bb838dd883b3ad3e6027e775bcf262881428099c7fff95b14c095ea130e0b9938a5e22fc52650f591", + "out": "5584bf3e93bc25945c508b9188d0502c6e755bbebabfc8cb907fa7a252ef464a" + }, + { + "in": "d3e65cb92cfa79662f6af493d696a07ccf32aaadcceff06e73e8d9f6f909209e66715d6e978788c49efb9087b170ecf3aa86d2d4d1a065ae0efc8924f365d676b3cb9e2bec918fd96d0b43dee83727c9a93bf56ca2b2e59adba85696546a815067fc7a78039629d4948d157e7b0d826d1bf8e81237bab7321312fdaa4d521744f988db6fdf04549d0fdca393d639c729af716e9c8bba48", + "out": "c234b252c21edb842634cc124da5bee8a4749cffba134723f7963b3a9729c0b4" + }, + { + "in": "842cc583504539622d7f71e7e31863a2b885c56a0ba62db4c2a3f2fd12e79660dc7205ca29a0dc0a87db4dc62ee47a41db36b9ddb3293b9ac4baae7df5c6e7201e17f717ab56e12cad476be49608ad2d50309e7d48d2d8de4fa58ac3cfeafeee48c0a9eec88498e3efc51f54d300d828dddccb9d0b06dd021a29cf5cb5b2506915beb8a11998b8b886e0f9b7a80e97d91a7d01270f9a7717", + "out": "645f25456752091fffcaade806c34c79dffe72140c7c75d6a6ecfeedf6db401c" + }, + { + "in": "6c4b0a0719573e57248661e98febe326571f9a1ca813d3638531ae28b4860f23c3a3a8ac1c250034a660e2d71e16d3acc4bf9ce215c6f15b1c0fc7e77d3d27157e66da9ceec9258f8f2bf9e02b4ac93793dd6e29e307ede3695a0df63cbdc0fc66fb770813eb149ca2a916911bee4902c47c7802e69e405fe3c04ceb5522792a5503fa829f707272226621f7c488a7698c0d69aa561be9f378", + "out": "2d7cac697e7410c1f7735dd691624a7d04fa51815858e8ba98b19b0ded0638b5" + }, + { + "in": "51b7dbb7ce2ffeb427a91ccfe5218fd40f9e0b7e24756d4c47cd55606008bdc27d16400933906fd9f30effdd4880022d081155342af3fb6cd53672ab7fb5b3a3bcbe47be1fd3a2278cae8a5fd61c1433f7d350675dd21803746cadca574130f01200024c6340ab0cc2cf74f2234669f34e9009ef2eb94823d62b31407f4ba46f1a1eec41641e84d77727b59e746b8a671bef936f05be820759fa", + "out": "f664f626bc6b7a8cf03be429155ee1f5cd6ecf14816de49a5e229903f89a4dc6" + }, + { + "in": "83599d93f5561e821bd01a472386bc2ff4efbd4aed60d5821e84aae74d8071029810f5e286f8f17651cd27da07b1eb4382f754cd1c95268783ad09220f5502840370d494beb17124220f6afce91ec8a0f55231f9652433e5ce3489b727716cf4aeba7dcda20cd29aa9a859201253f948dd94395aba9e3852bd1d60dda7ae5dc045b283da006e1cbad83cc13292a315db5553305c628dd091146597", + "out": "06425e83e4af817d735e9962c0cddce2cd40a087a6b0af3599719e415ab9a72a" + }, + { + "in": "2be9bf526c9d5a75d565dd11ef63b979d068659c7f026c08bea4af161d85a462d80e45040e91f4165c074c43ac661380311a8cbed59cc8e4c4518e80cd2c78ab1cabf66bff83eab3a80148550307310950d034a6286c93a1ece8929e6385c5e3bb6ea8a7c0fb6d6332e320e71cc4eb462a2a62e2bfe08f0ccad93e61bedb5dd0b786a728ab666f07e0576d189c92bf9fb20dca49ac2d3956d47385e2", + "out": "e8c329149b075c459e11c8ac1e7e6acfa51ca981c89ec0768ed79d19f4e484fb" + }, + { + "in": "ca76d3a12595a817682617006848675547d3e8f50c2210f9af906c0e7ce50b4460186fe70457a9e879e79fd4d1a688c70a347361c847ba0dd6aa52936eaf8e58a1be2f5c1c704e20146d366aeb3853bed9de9befe9569ac8aaea37a9fb7139a1a1a7d5c748605a8defb297869ebedd71d615a5da23496d11e11abbb126b206fa0a7797ee7de117986012d0362dcef775c2fe145ada6bda1ccb326bf644", + "out": "c86768f6c349eb323bd82db19676e10bd8ae9f7057763556bbb6d0b671e60f2a" + }, + { + "in": "f76b85dc67421025d64e93096d1d712b7baf7fb001716f02d33b2160c2c882c310ef13a576b1c2d30ef8f78ef8d2f465007109aad93f74cb9e7d7bef7c9590e8af3b267c89c15db238138c45833c98cc4a471a7802723ef4c744a853cf80a0c2568dd4ed58a2c9644806f42104cee53628e5bdf7b63b0b338e931e31b87c24b146c6d040605567ceef5960df9e022cb469d4c787f4cba3c544a1ac91f95f", + "out": "d97f46f3b7edbfb16e52bfec7dba0815b94d46e4251e48a853eabdf876127714" + }, + { + "in": "25b8c9c032ea6bcd733ffc8718fbb2a503a4ea8f71dea1176189f694304f0ff68e862a8197b839957549ef243a5279fc2646bd4c009b6d1edebf24738197abb4c992f6b1dc9ba891f570879accd5a6b18691a93c7d0a8d38f95b639c1daeb48c4c2f15ccf5b9d508f8333c32de78781b41850f261b855c4bebcc125a380c54d501c5d3bd07e6b52102116088e53d76583b0161e2a58d0778f091206aabd5a1", + "out": "51d08e00aaa252812d873357107616055b1b8c5fb2ac7917d0f901dfb01fac47" + }, + { + "in": "21cfdc2a7ccb7f331b3d2eefff37e48ad9fa9c788c3f3c200e0173d99963e1cbca93623b264e920394ae48bb4c3a5bb96ffbc8f0e53f30e22956adabc2765f57fb761e147ecbf8567533db6e50c8a1f894310a94edf806dd8ca6a0e141c0fa7c9fae6c6ae65f18c93a8529e6e5b553bf55f25be2e80a9882bd37f145fecbeb3d447a3c4e46c21524cc55cdd62f521ab92a8ba72b897996c49bb273198b7b1c9e", + "out": "c6a188a6bdaca4dd7b1bc3e41019afe93473063f932c166e3242b7f52a3c6f8e" + }, + { + "in": "4e452ba42127dcc956ef4f8f35dd68cb225fb73b5bc7e1ec5a898bba2931563e74faff3b67314f241ec49f4a7061e3bd0213ae826bab380f1f14faab8b0efddd5fd1bb49373853a08f30553d5a55ccbbb8153de4704f29ca2bdeef0419468e05dd51557ccc80c0a96190bbcc4d77ecff21c66bdf486459d427f986410f883a80a5bcc32c20f0478bb9a97a126fc5f95451e40f292a4614930d054c851acd019ccf", + "out": "2b31fbc565110110011ab2c8f6cc3da8fb55d41b1ae5e04310283f207d39682d" + }, + { + "in": "fa85671df7dadf99a6ffee97a3ab9991671f5629195049880497487867a6c446b60087fac9a0f2fcc8e3b24e97e42345b93b5f7d3691829d3f8ccd4bb36411b85fc2328eb0c51cb3151f70860ad3246ce0623a8dc8b3c49f958f8690f8e3860e71eb2b1479a5cea0b3f8befd87acaf5362435eaeccb52f38617bc6c5c2c6e269ead1fbd69e941d4ad2012da2c5b21bcfbf98e4a77ab2af1f3fda3233f046d38f1dc8", + "out": "1351f5dba46098b9a773381d85d52fad491b3a82af9107f173db81fb35ed91d2" + }, + { + "in": "e90847ae6797fbc0b6b36d6e588c0a743d725788ca50b6d792352ea8294f5ba654a15366b8e1b288d84f5178240827975a763bc45c7b0430e8a559df4488505e009c63da994f1403f407958203cebb6e37d89c94a5eacf6039a327f6c4dbbc7a2a307d976aa39e41af6537243fc218dfa6ab4dd817b6a397df5ca69107a9198799ed248641b63b42cb4c29bfdd7975ac96edfc274ac562d0474c60347a078ce4c25e88", + "out": "dffc700f3e4d84d9131cbb1f98fb843dbafcb2ef94a52e89d204d431451a3331" + }, + { + "in": "f6d5c2b6c93954fc627602c00c4ca9a7d3ed12b27173f0b2c9b0e4a5939398a665e67e69d0b12fb7e4ceb253e8083d1ceb724ac07f009f094e42f2d6f2129489e846eaff0700a8d4453ef453a3eddc18f408c77a83275617fabc4ea3a2833aa73406c0e966276079d38e8e38539a70e194cc5513aaa457c699383fd1900b1e72bdfb835d1fd321b37ba80549b078a49ea08152869a918ca57f5b54ed71e4fd3ac5c06729", + "out": "26726b52242ef8ecf4c66aed9c4b46bf6f5d87044a0b99d4e4af47dc360b9b0e" + }, + { + "in": "cf8562b1bed89892d67ddaaf3deeb28246456e972326dbcdb5cf3fb289aca01e68da5d59896e3a6165358b071b304d6ab3d018944be5049d5e0e2bb819acf67a6006111089e6767132d72dd85beddcbb2d64496db0cc92955ab4c6234f1eea24f2d51483f2e209e4589bf9519fac51b4d061e801125e605f8093bb6997bc163d551596fe4ab7cfae8fb9a90f6980480ce0c229fd1675409bd788354daf316240cfe0af93eb", + "out": "25e536315f08a40976adecb54756ebc0b224c38faf11509371b5a692a5269ab5" + }, + { + "in": "2ace31abb0a2e3267944d2f75e1559985db7354c6e605f18dc8470423fca30b7331d9b33c4a4326783d1caae1b4f07060eff978e4746bf0c7e30cd61040bd5ec2746b29863eb7f103ebda614c4291a805b6a4c8214230564a0557bc7102e0bd3ed23719252f7435d64d210ee2aafc585be903fa41e1968c50fd5d5367926df7a05e3a42cf07e656ff92de73b036cf8b19898c0cb34557c0c12c2d8b84e91181af467bc75a9d1", + "out": "ab504592ad7184be83cc659efb5d3de88ba04b060b45d16a76f034080dde56c6" + }, + { + "in": "0d8d09aed19f1013969ce5e7eb92f83a209ae76be31c754844ea9116ceb39a22ebb6003017bbcf26555fa6624185187db8f0cb3564b8b1c06bf685d47f3286eda20b83358f599d2044bbf0583fab8d78f854fe0a596183230c5ef8e54426750eaf2cc4e29d3bdd037e734d863c2bd9789b4c243096138f7672c232314effdfc6513427e2da76916b5248933be312eb5dde4cf70804fb258ac5fb82d58d08177ac6f4756017fff5", + "out": "5d8ee133ec441a3df50a5268a8f393f13f30f23f226ae3a18ec331844402ff54" + }, + { + "in": "c3236b73deb7662bf3f3daa58f137b358ba610560ef7455785a9befdb035a066e90704f929bd9689cef0ce3bda5acf4480bceb8d09d10b098ad8500d9b6071dfc3a14af6c77511d81e3aa8844986c3bea6f469f9e02194c92868cd5f51646256798ff0424954c1434bdfed9facb390b07d342e992936e0f88bfd0e884a0ddb679d0547ccdec6384285a45429d115ac7d235a717242021d1dc35641f5f0a48e8445dba58e6cb2c8ea", + "out": "712b1cc04c009b52035cc44c9505bb5cb577ba0ad1734ec23620f57eef3d37fb" + }, + { + "in": "b39feb8283eadc63e8184b51df5ae3fd41aac8a963bb0be1cd08aa5867d8d910c669221e73243360646f6553d1ca05a84e8dc0de05b6419ec349ca994480193d01c92525f3fb3dcefb08afc6d26947bdbbfd85193f53b50609c6140905c53a6686b58e53a319a57b962331ede98149af3de3118a819da4d76706a0424b4e1d2910b0ed26af61d150ebcb46595d4266a0bd7f651ba47d0c7f179ca28545007d92e8419d48fdfbd744ce", + "out": "942e39e230a2251ffdb2f85202871c98597008401b322ff9840cc90cc85b337d" + }, + { + "in": "a983d54f503803e8c7999f4edbbe82e9084f422143a932ddddc47a17b0b7564a7f37a99d0786e99476428d29e29d3c197a72bfab1342c12a0fc4787fd7017d7a6174049ea43b5779169ef7472bdbbd941dcb82fc73aac45a8a94c9f2bd3477f61fd3b796f02a1b8264a214c6fea74b7051b226c722099ec7883a462b83b6afdd4009248b8a237f605fe5a08fe7d8b45321421ebba67bd70a0b00ddbf94baab7f359d5d1eea105f28dcfb", + "out": "b542b6cd8ef2dab4ed83b77ac6dc52daf554ecda4ef7ab0a50e546bebe2d8e5a" + }, + { + "in": "e4d1c1897a0a866ce564635b74222f9696bf2c7f640dd78d7e2aca66e1b61c642bb03ea7536aae597811e9bf4a7b453ede31f97b46a5f0ef51a071a2b3918df16b152519ae3776f9f1edab4c2a377c3292e96408359d3613844d5eb393000283d5ad3401a318b12fd1474b8612f2bb50fb6a8b9e023a54d7dde28c43d6d8854c8d9d1155935c199811dbfc87e9e0072e90eb88681cc7529714f8fb8a2c9d88567adfb974ee205a9bf7b848", + "out": "f7e9e825722e6554a8619cca3e57f5b5e6b7347431d55ce178372c917bfb3dc2" + }, + { + "in": "b10c59723e3dcadd6d75df87d0a1580e73133a9b7d00cb95ec19f5547027323be75158b11f80b6e142c6a78531886d9047b08e551e75e6261e79785366d7024bd7cd9cf322d9be7d57fb661069f2481c7bb759cd71b4b36ca2bc2df6d3a328faebdb995a9794a8d72155ed551a1f87c80bf6059b43fc764900b18a1c2441f7487743cf84e565f61f8dd2ece6b6ccc9444049197aaaf53e926fbee3bfca8be588ec77f29d211be89de18b15f6", + "out": "14bb22b98eaf41a4c224fd3c37188a755f9b04f46f3e23a652da3db9e25d2f2c" + }, + { + "in": "db11f609baba7b0ca634926b1dd539c8cbada24967d7add4d9876f77c2d80c0f4dcefbd7121548373582705cca2495bd2a43716fe64ed26d059cfb566b3364bd49ee0717bdd9810dd14d8fad80dbbdc4cafb37cc60fb0fe2a80fb4541b8ca9d59dce457738a9d3d8f641af8c3fd6da162dc16fc01aac527a4a0255b4d231c0be50f44f0db0b713af03d968fe7f0f61ed0824c55c4b5265548febd6aad5c5eedf63efe793489c39b8fd29d104ce", + "out": "eb5668f9941c06e5e38ea01b7fa980638b9536ca1939950c1629f84a6eff3866" + }, + { + "in": "bebd4f1a84fc8b15e4452a54bd02d69e304b7f32616aadd90537937106ae4e28de9d8aab02d19bc3e2fde1d651559e296453e4dba94370a14dbbb2d1d4e2022302ee90e208321efcd8528ad89e46dc839ea9df618ea8394a6bff308e7726bae0c19bcd4be52da6258e2ef4e96aa21244429f49ef5cb486d7ff35cac1bacb7e95711944bccb2ab34700d42d1eb38b5d536b947348a458ede3dc6bd6ec547b1b0cae5b257be36a7124e1060c170ffa", + "out": "913014bb6e243fac3a22a185f8227a68c2311dc0b718e276bbbdb73af98be35f" + }, + { + "in": "5aca56a03a13784bdc3289d9364f79e2a85c12276b49b92db0adaa4f206d5028f213f678c3510e111f9dc4c1c1f8b6acb17a6413aa227607c515c62a733817ba5e762cc6748e7e0d6872c984d723c9bb3b117eb8963185300a80bfa65cde495d70a46c44858605fccbed086c2b45cef963d33294dbe9706b13af22f1b7c4cd5a001cfec251fba18e722c6e1c4b1166918b4f6f48a98b64b3c07fc86a6b17a6d0480ab79d4e6415b520f1c484d675b1", + "out": "0284418c10190f413042e3eceb3954979b94afbf2e545fc7f8a3c7db2c235916" + }, + { + "in": "a5aad0e4646a32c85cfcac73f02fc5300f1982fabb2f2179e28303e447854094cdfc854310e5c0f60993ceff54d84d6b46323d930adb07c17599b35b505f09e784bca5985e0172257797fb53649e2e9723efd16865c31b5c3d5113b58bb0bfc8920fabdda086d7537e66d709d050bd14d0c960873f156fad5b3d3840cdfcdc9be6af519db262a27f40896ab25cc39f96984d650611c0d5a3080d5b3a1bf186abd42956588b3b58cd948970d298776060", + "out": "8febff801787f5803e151dca3434a5cd44adb49f1c2ffd5d0cd077a9075a492d" + }, + { + "in": "06cbbe67e94a978203ead6c057a1a5b098478b4b4cbef5a97e93c8e42f5572713575fc2a884531d7622f8f879387a859a80f10ef02708cd8f7413ab385afc357678b9578c0ebf641ef076a1a30f1f75379e9dcb2a885bdd295905ee80c0168a62a9597d10cf12dd2d8cee46645c7e5a141f6e0e23aa482abe5661c16e69ef1e28371e2e236c359ba4e92c25626a7b7ff13f6ea4ae906e1cfe163e91719b1f750a96cbde5fbc953d9e576cd216afc90323a", + "out": "ea7511b993b786df59a3b3e0b3cd876c0f056d6ca43cc89c51c1b21ccdc79b42" + }, + { + "in": "f1c528cf7739874707d4d8ad5b98f7c77169de0b57188df233b2dc8a5b31eda5db4291dd9f68e6bad37b8d7f6c9c0044b3bf74bbc3d7d1798e138709b0d75e7c593d3cccdc1b20c7174b4e692add820ace262d45ccfae2077e878796347168060a162ecca8c38c1a88350bd63bb539134f700fd4addd5959e255337daa06bc86358fabcbefdfb5bc889783d843c08aadc6c4f6c36f65f156e851c9a0f917e4a367b5ad93d874812a1de6a7b93cd53ad97232", + "out": "baaecb6e9db57971d5c70f5819ff89c5093254de19ef6059c43cc0afda7c5d34" + }, + { + "in": "9d9f3a7ecd51b41f6572fd0d0881e30390dfb780991dae7db3b47619134718e6f987810e542619dfaa7b505c76b7350c6432d8bf1cfebdf1069b90a35f0d04cbdf130b0dfc7875f4a4e62cdb8e525aadd7ce842520a482ac18f09442d78305fe85a74e39e760a4837482ed2f437dd13b2ec1042afcf9decdc3e877e50ff4106ad10a525230d11920324a81094da31deab6476aa42f20c84843cfc1c58545ee80352bdd3740dd6a16792ae2d86f11641bb717c2", + "out": "56db69430b8ca852221d55d7bbff477dc83f7cb44ab44ddd64c31a52c483db4f" + }, + { + "in": "5179888724819fbad3afa927d3577796660e6a81c52d98e9303261d5a4a83232f6f758934d50aa83ff9e20a5926dfebaac49529d006eb923c5ae5048ed544ec471ed7191edf46363383824f915769b3e688094c682b02151e5ee01e510b431c8865aff8b6b6f2f59cb6d129da79e97c6d2b8fa6c6da3f603199d2d1bcab547682a81cd6cf65f6551121391d78bcc23b5bd0e922ec6d8bf97c952e84dd28aef909aba31edb903b28fbfc33b7703cd996215a11238", + "out": "f8538f597f4463cad7a91905744b87156db33c65ba87b912427fec3669f425d4" + }, + { + "in": "576ef3520d30b7a4899b8c0d5e359e45c5189add100e43be429a02fb3de5ff4f8fd0e79d9663acca72cd29c94582b19292a557c5b1315297d168fbb54e9e2ecd13809c2b5fce998edc6570545e1499dbe7fb74d47cd7f35823b212b05bf3f5a79caa34224fdd670d335fcb106f5d92c3946f44d3afcbae2e41ac554d8e6759f332b76be89a0324aa12c5482d1ea3ee89ded4936f3e3c080436f539fa137e74c6d3389bdf5a45074c47bc7b20b0948407a66d855e2f", + "out": "447eda923cfe1112a6f1a3e4c735bf8ee9e4f2aee7de666a472ff8cf0fc65315" + }, + { + "in": "0df2152fa4f4357c8741529dd77e783925d3d76e95bafa2b542a2c33f3d1d117d159cf473f82310356fee4c90a9e505e70f8f24859656368ba09381fa245eb6c3d763f3093f0c89b972e66b53d59406d9f01aea07f8b3b615cac4ee4d05f542e7d0dab45d67ccccd3a606ccbeb31ea1fa7005ba07176e60dab7d78f6810ef086f42f08e595f0ec217372b98970cc6321576d92ce38f7c397a403bada1548d205c343ac09deca86325373c3b76d9f32028fea8eb32515", + "out": "74d94c13afea4ddd07a637b68b6fe095017c092b3cdccdc498e26035d86d921e" + }, + { + "in": "3e15350d87d6ebb5c8ad99d42515cfe17980933c7a8f6b8bbbf0a63728cefaad2052623c0bd5931839112a48633fb3c2004e0749c87a41b26a8b48945539d1ff41a4b269462fd199bfecd45374756f55a9116e92093ac99451aefb2af9fd32d6d7f5fbc7f7a540d5097c096ebc3b3a721541de073a1cc02f7fb0fb1b9327fb0b1218ca49c9487ab5396622a13ae546c97abdef6b56380dda7012a8384091b6656d0ab272d363cea78163ff765cdd13ab1738b940d16cae", + "out": "cc11196c095bffa090a05ba0bc255d38bda7218d9311143f4f200b1852d1bb0d" + }, + { + "in": "c38d6b0b757cb552be40940ece0009ef3b0b59307c1451686f1a22702922800d58bce7a636c1727ee547c01b214779e898fc0e560f8ae7f61bef4d75eaa696b921fd6b735d171535e9edd267c192b99880c87997711002009095d8a7a437e258104a41a505e5ef71e5613ddd2008195f0c574e6ba3fe40099cfa116e5f1a2fa8a6da04badcb4e2d5d0de31fdc4800891c45781a0aac7c907b56d631fca5ce8b2cde620d11d1777ed9fa603541de794ddc5758fcd5fad78c0", + "out": "8c085b54c213704374ddd920a45168608be65dfd036a562659f47143604144c2" + }, + { + "in": "8d2de3f0b37a6385c90739805b170057f091cd0c7a0bc951540f26a5a75b3e694631bb64c7635eed316f51318e9d8de13c70a2aba04a14836855f35e480528b776d0a1e8a23b547c8b8d6a0d09b241d3be9377160cca4e6793d00a515dc2992cb7fc741daca171431da99cce6f7789f129e2ac5cf65b40d703035cd2185bb936c82002daf8cbc27a7a9e554b06196630446a6f0a14ba155ed26d95bd627b7205c072d02b60db0fd7e49ea058c2e0ba202daff0de91e845cf79", + "out": "d2e233264a3773495ffd12159ef7b631660c1b3e53a3da0f24ae14466f167757" + }, + { + "in": "c464bbdad275c50dcd983b65ad1019b9ff85a1e71c807f3204bb2c921dc31fbcd8c5fc45868ae9ef85b6c9b83bba2a5a822201ed68586ec5ec27fb2857a5d1a2d09d09115f22dcc39fe61f5e1ba0ff6e8b4acb4c6da748be7f3f0839739394ff7fa8e39f7f7e84a33c3866875c01bcb1263c9405d91908e9e0b50e7459fabb63d8c6bbb73d8e3483c099b55bc30ff092ff68b6adedfd477d63570c9f5515847f36e24ba0b705557130cec57ebad1d0b31a378e91894ee26e3a04", + "out": "ffac7ca5fa067419d1bdb00c0e49c6e1a748880923a23ed5dd67dde63d777edb" + }, + { + "in": "8b8d68bb8a75732fe272815a68a1c9c5aa31b41dedc8493e76525d1d013d33cebd9e21a5bb95db2616976a8c07fcf411f5f6bc6f7e0b57aca78cc2790a6f9b898858ac9c79b165ff24e66677531e39f572be5d81eb3264524181115f32780257bfb9aeec6af12af28e587cac068a1a2953b59ad680f4c245b2e3ec36f59940d37e1d3db38e13edb29b5c0f404f6ff87f80fc8be7a225ff22fbb9c8b6b1d7330c57840d24bc75b06b80d30dad6806544d510af6c4785e823ac3e0b8", + "out": "5b2eca0920d32b1964bbf5810a6e6e53675ed1b83897fd04600d72e097845859" + }, + { + "in": "6b018710446f368e7421f1bc0ccf562d9c1843846bc8d98d1c9bf7d9d6fcb48bfc3bf83b36d44c4fa93430af75cd190bde36a7f92f867f58a803900df8018150384d85d82132f123006ac2aeba58e02a037fe6afbd65eca7c44977dd3dc74f48b6e7a1bfd5cc4dcf24e4d52e92bd4455848e4928b0eac8b7476fe3cc03e862aa4dff4470dbfed6de48e410f25096487ecfc32a27277f3f5023b2725ade461b1355889554a8836c9cf53bd767f5737d55184eea1ab3f53edd0976c485", + "out": "68f41fdfc7217e89687ed118bc31ac6ed2d9d1e1a2f1b20a2d429729fa03517b" + }, + { + "in": "c9534a24714bd4be37c88a3da1082eda7cabd154c309d7bd670dccd95aa535594463058a29f79031d6ecaa9f675d1211e9359be82669a79c855ea8d89dd38c2c761ddd0ec0ce9e97597432e9a1beae062cdd71edfdfd464119be9e69d18a7a7fd7ce0e2106f0c8b0abf4715e2ca48ef9f454dc203c96656653b727083513f8efb86e49c513bb758b3b052fe21f1c05bb33c37129d6cc81f1aef6adc45b0e8827a830fe545cf57d0955802c117d23ccb55ea28f95c0d8c2f9c5a242b33f", + "out": "fa2f3de31e9cf25ab9a978c82d605a43ee39b68ac8e30f49f9d209cb4e172ab4" + }, + { + "in": "07906c87297b867abf4576e9f3cc7f82f22b154afcbf293b9319f1b0584da6a40c27b32e0b1b7f412c4f1b82480e70a9235b12ec27090a5a33175a2bb28d8adc475cefe33f7803f8ce27967217381f02e67a3b4f84a71f1c5228e0c2ad971373f6f672624fcea8d1a9f85170fad30fa0bbd25035c3b41a6175d467998bd1215f6f3866f53847f9cf68ef3e2fbb54bc994de2302b829c5eea68ec441fcbafd7d16ae4fe9fff98bf00e5bc2ad54dd91ff9fda4dd77b6c754a91955d1fbaad0", + "out": "ba2af506c10da8d7751e67ed766cfcd47d048d6ef9277dbd2abfe2fd5d787b79" + }, + { + "in": "588e94b9054abc2189df69b8ba34341b77cdd528e7860e5defcaa79b0c9a452ad4b82aa306be84536eb7cedcbe058d7b84a6aef826b028b8a0271b69ac3605a9635ea9f5ea0aa700f3eb7835bc54611b922964300c953efe7491e3677c2cebe0822e956cd16433b02c68c4a23252c3f9e151a416b4963257b783e038f6b4d5c9f110f871652c7a649a7bcedcbccc6f2d0725bb903cc196ba76c76aa9f10a190b1d1168993baa9ffc96a1655216773458bec72b0e39c9f2c121378feab4e76a", + "out": "3cd33f8811af12183c53e978528f53ae7d559432724029e55fcfa9b990b91713" + }, + { + "in": "08959a7e4baae874928813364071194e2939772f20db7c3157078987c557c2a6d5abe68d520eef3dc491692e1e21bcd880adebf63bb4213b50897fa005256ed41b5690f78f52855c8d9168a4b666fce2da2b456d7a7e7c17ab5f2fb1ee90b79e698712e963715983fd07641ae4b4e9dc73203fac1ae11fa1f8c7941fcc82eab247addb56e2638447e9d609e610b60ce086656aaebf1da3c8a231d7d94e2fd0afe46b391ff14a72eaeb3f44ad4df85866def43d4781a0b3578bc996c87970b132", + "out": "3ecc9d27994022045cbeab4fc041f12419cec8060c8f6f9f0372884df6074b5c" + }, + { + "in": "cb2a234f45e2ecd5863895a451d389a369aab99cfef0d5c9ffca1e6e63f763b5c14fb9b478313c8e8c0efeb3ac9500cf5fd93791b789e67eac12fd038e2547cc8e0fc9db591f33a1e4907c64a922dda23ec9827310b306098554a4a78f050262db5b545b159e1ff1dca6eb734b872343b842c57eafcfda8405eedbb48ef32e99696d135979235c3a05364e371c2d76f1902f1d83146df9495c0a6c57d7bf9ee77e80f9787aee27be1fe126cdc9ef893a4a7dcbbc367e40fe4e1ee90b42ea25af01", + "out": "1501988a55372ac1b0b78849f3b7e107e0bf1f2cbaf670de7f15acbb1a00ad3d" + }, + { + "in": "d16beadf02ab1d4dc6f88b8c4554c51e866df830b89c06e786a5f8757e8909310af51c840efe8d20b35331f4355d80f73295974653ddd620cdde4730fb6c8d0d2dcb2b45d92d4fbdb567c0a3e86bd1a8a795af26fbf29fc6c65941cddb090ff7cd230ac5268ab4606fccba9eded0a2b5d014ee0c34f0b2881ac036e24e151be89eeb6cd9a7a790afccff234d7cb11b99ebf58cd0c589f20bdac4f9f0e28f75e3e04e5b3debce607a496d848d67fa7b49132c71b878fd5557e082a18eca1fbda94d4b", + "out": "5c4e860a0175c92c1e6af2cbb3084162403ced073faac901d0d358b6bf5eefa9" + }, + { + "in": "8f65f6bc59a85705016e2bae7fe57980de3127e5ab275f573d334f73f8603106ec3553016608ef2dd6e69b24be0b7113bf6a760ba6e9ce1c48f9e186012cf96a1d4849d75df5bb8315387fd78e9e153e76f8ba7ec6c8849810f59fb4bb9b004318210b37f1299526866f44059e017e22e96cbe418699d014c6ea01c9f0038b10299884dbec3199bb05adc94e955a1533219c1115fed0e5f21228b071f40dd57c4240d98d37b73e412fe0fa4703120d7c0c67972ed233e5deb300a22605472fa3a3ba86", + "out": "272b4f689263057fbf7605aaa67af012d742267164c4fab68035d99c5829b4f0" + }, + { + "in": "84891e52e0d451813210c3fd635b39a03a6b7a7317b221a7abc270dfa946c42669aacbbbdf801e1584f330e28c729847ea14152bd637b3d0f2b38b4bd5bf9c791c58806281103a3eabbaede5e711e539e6a8b2cf297cf351c078b4fa8f7f35cf61bebf8814bf248a01d41e86c5715ea40c63f7375379a7eb1d78f27622fb468ab784aaaba4e534a6dfd1df6fa15511341e725ed2e87f98737ccb7b6a6dfae416477472b046bf1811187d151bfa9f7b2bf9acdb23a3be507cdf14cfdf517d2cb5fb9e4ab6", + "out": "9b28e42b67ef32ec80da10a07b004e1d71c6dce71d8013ffa0305d0d0ce0469d" + }, + { + "in": "fdd7a9433a3b4afabd7a3a5e3457e56debf78e84b7a0b0ca0e8c6d53bd0c2dae31b2700c6128334f43981be3b213b1d7a118d59c7e6b6493a86f866a1635c12859cfb9ad17460a77b4522a5c1883c3d6acc86e6162667ec414e9a104aa892053a2b1d72165a855bacd8faf8034a5dd9b716f47a0818c09bb6baf22aa503c06b4ca261f557761989d2afbd88b6a678ad128af68672107d0f1fc73c5ca740459297b3292b281e93bceb761bde7221c3a55708e5ec84472cddcaa84ecf23723cc0991355c6280", + "out": "ee53f83d2e2ccc315c6377eadda5f42f42f3aadd664e3e895c37cbe9d0e9b9de" + }, + { + "in": "70a40bfbef92277a1aad72f6b79d0177197c4ebd432668cfec05d099accb651062b5dff156c0b27336687a94b26679cfdd9daf7ad204338dd9c4d14114033a5c225bd11f217b5f4732da167ee3f939262d4043fc9cba92303b7b5e96aea12adda64859df4b86e9ee0b58e39091e6b188b408ac94e1294a8911245ee361e60e601eff58d1d37639f3753bec80ebb4efde25817436076623fc65415fe51d1b0280366d12c554d86743f3c3b6572e400361a60726131441ba493a83fbe9afda90f7af1ae717238d", + "out": "21ccfda65c4b915303012b852ab29481030f87347c29917e21f210f2bd5efc9c" + }, + { + "in": "74356e449f4bf8644f77b14f4d67cb6bd9c1f5ae357621d5b8147e562b65c66585caf2e491b48529a01a34d226d436959153815380d5689e30b35357cdac6e08d3f2b0e88e200600d62bd9f5eaf488df86a4470ea227006182e44809009868c4c280c43d7d64a5268fa719074960087b3a6abc837882f882c837834535929389a12b2c78187e2ea07ef8b8eef27dc85002c3ae35f1a50bee6a1c48ba7e175f3316670b27983472aa6a61eed0a683a39ee323080620ea44a9f74411ae5ce99030528f9ab49c79f2", + "out": "f5bf70710da440edb43afd3eb7698180317ffefa81406bb4df9c2bb8b0b1c034" + }, + { + "in": "8c3798e51bc68482d7337d3abb75dc9ffe860714a9ad73551e120059860dde24ab87327222b64cf774415a70f724cdf270de3fe47dda07b61c9ef2a3551f45a5584860248fabde676e1cd75f6355aa3eaeabe3b51dc813d9fb2eaa4f0f1d9f834d7cad9c7c695ae84b329385bc0bef895b9f1edf44a03d4b410cc23a79a6b62e4f346a5e8dd851c2857995ddbf5b2d717aeb847310e1f6a46ac3d26a7f9b44985af656d2b7c9406e8a9e8f47dcb4ef6b83caacf9aefb6118bfcff7e44bef6937ebddc89186839b77", + "out": "e83ea21f5bc0976953af86069a10eb6024a1ac59d609688e4a9759bb8b6c9441" + }, + { + "in": "fa56bf730c4f8395875189c10c4fb251605757a8fecc31f9737e3c2503b02608e6731e85d7a38393c67de516b85304824bfb135e33bf22b3a23b913bf6acd2b7ab85198b8187b2bcd454d5e3318cacb32fd6261c31ae7f6c54ef6a7a2a4c9f3ecb81ce3555d4f0ad466dd4c108a90399d70041997c3b25345a9653f3c9a6711ab1b91d6a9d2216442da2c973cbd685ee7643bfd77327a2f7ae9cb283620a08716dfb462e5c1d65432ca9d56a90e811443cd1ecb8f0de179c9cb48ba4f6fec360c66f252f6e64edc96b", + "out": "a2d93c6367e1862809d367ec37f9da44cb3a8b4319c6a094c5e7d7266fe3a593" + }, + { + "in": "b6134f9c3e91dd8000740d009dd806240811d51ab1546a974bcb18d344642baa5cd5903af84d58ec5ba17301d5ec0f10ccd0509cbb3fd3fff9172d193af0f782252fd1338c7244d40e0e42362275b22d01c4c3389f19dd69bdf958ebe28e31a4ffe2b5f18a87831cfb7095f58a87c9fa21db72ba269379b2dc2384b3da953c7925761fed324620acea435e52b424a7723f6a2357374157a34cd8252351c25a1b232826cefe1bd3e70ffc15a31e7c0598219d7f00436294d11891b82497bc78aa5363892a2495df8c1eef", + "out": "3c647b195f22dc16d6decc8873017df369ee1c4696340934db158dc4059c76df" + }, + { + "in": "c941cdb9c28ab0a791f2e5c8e8bb52850626aa89205bec3a7e22682313d198b1fa33fc7295381354858758ae6c8ec6fac3245c6e454d16fa2f51c4166fab51df272858f2d603770c40987f64442d487af49cd5c3991ce858ea2a60dab6a65a34414965933973ac2457089e359160b7cdedc42f29e10a91921785f6b7224ee0b349393cdcff6151b50b377d609559923d0984cda6000829b916ab6896693ef6a2199b3c22f7dc5500a15b8258420e314c222bc000bc4e5413e6dd82c993f8330f5c6d1be4bc79f08a1a0a46", + "out": "3bb394d056d94fde68920cd383378ee3abcc44b7259d3db9cd0a897e021f7e2e" + }, + { + "in": "4499efffac4bcea52747efd1e4f20b73e48758be915c88a1ffe5299b0b005837a46b2f20a9cb3c6e64a9e3c564a27c0f1c6ad1960373036ec5bfe1a8fc6a435c2185ed0f114c50e8b3e4c7ed96b06a036819c9463e864a58d6286f785e32a804443a56af0b4df6abc57ed5c2b185ddee8489ea080deeee66aa33c2e6dab36251c402682b6824821f998c32163164298e1fafd31babbcffb594c91888c6219079d907fdb438ed89529d6d96212fd55abe20399dbefd342248507436931cdead496eb6e4a80358acc78647d043", + "out": "43640f408613cbf7393d900b921f22b826357f3b4fdff7168ec45cbfb3ef5eff" + }, + { + "in": "eecbb8fdfa4da62170fd06727f697d81f83f601ff61e478105d3cb7502f2c89bf3e8f56edd469d049807a38882a7eefbc85fc9a950952e9fa84b8afebd3ce782d4da598002827b1eb98882ea1f0a8f7aa9ce013a6e9bc462fb66c8d4a18da21401e1b93356eb12f3725b6db1684f2300a98b9a119e5d27ff704affb618e12708e77e6e5f34139a5a41131fd1d6336c272a8fc37080f041c71341bee6ab550cb4a20a6ddb6a8e0299f2b14bc730c54b8b1c1c487b494bdccfd3a53535ab2f231590bf2c4062fd2ad58f906a2d0d", + "out": "cb3713a5d5abbc6af72f8b38a701c71269b3b51c62ec5116f96ad0d42a10fd90" + }, + { + "in": "e64f3e4ace5c8418d65fec2bc5d2a303dd458034736e3b0df719098be7a206deaf52d6ba82316caf330ef852375188cde2b39cc94aa449578a7e2a8e3f5a9d68e816b8d16889fbc0ebf0939d04f63033ae9ae2bdab73b88c26d6bd25ee460ee1ef58fb0afa92cc539f8c76d3d097e7a6a63ebb9b5887edf3cf076028c5bbd5b9db3211371ad3fe121d4e9bf44229f4e1ecf5a0f9f0eba4d5ceb72878ab22c3f0eb5a625323ac66f7061f4a81fac834471e0c59553f108475fe290d43e6a055ae3ee46fb67422f814a68c4be3e8c9", + "out": "b304fc4ca22131857d242eb12fe899ed9e6b55717c3360f113512a84174e6a77" + }, + { + "in": "d2cb2d733033f9e91395312808383cc4f0ca974e87ec68400d52e96b3fa6984ac58d9ad0938dde5a973008d818c49607d9de2284e7618f1b8aed8372fbd52ed54557af4220fac09dfa8443011699b97d743f8f2b1aef3537ebb45dcc9e13dfb438428ee190a4efdb3caeb7f3933117bf63abdc7e57beb4171c7e1ad260ab0587806c4d137b6316b50abc9cce0dff3acada47bbb86be777e617bbe578ff4519844db360e0a96c6701290e76bb95d26f0f804c8a4f2717eac4e7de9f2cff3bbc55a17e776c0d02856032a6cd10ad2838", + "out": "a3ca830d4771c1baa7fada76c5fceadd0f3cb9736e19cfec52e9e74f56bfdd55" + }, + { + "in": "f2998955613dd414cc111df5ce30a995bb792e260b0e37a5b1d942fe90171a4ac2f66d4928d7ad377f4d0554cbf4c523d21f6e5f379d6f4b028cdcb9b1758d3b39663242ff3cb6ede6a36a6f05db3bc41e0d861b384b6dec58bb096d0a422fd542df175e1be1571fb52ae66f2d86a2f6824a8cfaacbac4a7492ad0433eeb15454af8f312b3b2a577750e3efbd370e8a8cac1582581971fba3ba4bd0d76e718dacf8433d33a59d287f8cc92234e7a271041b526e389efb0e40b6a18b3aaf658e82ed1c78631fd23b4c3eb27c3faec8685", + "out": "ca158c46370e64a9f032f5ba8e091460fd555ef700edf7087e56bebffa261de7" + }, + { + "in": "447797e2899b72a356ba55bf4df3acca6cdb1041eb477bd1834a9f9acbc340a294d729f2f97df3a610be0ff15edb9c6d5db41644b9874360140fc64f52aa03f0286c8a640670067a84e017926a70438db1bb361defee7317021425f8821def26d1efd77fc853b818545d055adc9284796e583c76e6fe74c9ac2587aa46aa8f8804f2feb5836cc4b3ababab8429a5783e17d5999f32242eb59ef30cd7adabc16d72dbdb097623047c98989f88d14eaf02a7212be16ec2d07981aaa99949ddf89ecd90333a77bc4e1988a82abf7c7caf3291", + "out": "5901cda0cd1510db5455d072d2737a6721ad9ee3272953a19c7ab378bf3646c5" + }, + { + "in": "9f2c18ade9b380c784e170fb763e9aa205f64303067eb1bcea93df5dac4bf5a2e00b78195f808df24fc76e26cb7be31dc35f0844cded1567bba29858cffc97fb29010331b01d6a3fb3159cc1b973d255da9843e34a0a4061cabdb9ed37f241bfabb3c20d32743f4026b59a4ccc385a2301f83c0b0a190b0f2d01acb8f0d41111e10f2f4e149379275599a52dc089b35fdd5234b0cfb7b6d8aebd563ca1fa653c5c021dfd6f5920e6f18bfafdbecbf0ab00281333ed50b9a999549c1c8f8c63d7626c48322e9791d5ff72294049bde91e73f8", + "out": "f64562d6273efb5ebd027e0a6f38c3fb204a6dbe894ee01200ea249b747cfe66" + }, + { + "in": "ae159f3fa33619002ae6bcce8cbbdd7d28e5ed9d61534595c4c9f43c402a9bb31f3b301cbfd4a43ce4c24cd5c9849cc6259eca90e2a79e01ffbac07ba0e147fa42676a1d668570e0396387b5bcd599e8e66aaed1b8a191c5a47547f61373021fa6deadcb55363d233c24440f2c73dbb519f7c9fa5a8962efd5f6252c0407f190dfefad707f3c7007d69ff36b8489a5b6b7c557e79dd4f50c06511f599f56c896b35c917b63ba35c6ff8092baf7d1658e77fc95d8a6a43eeb4c01f33f03877f92774be89c1114dd531c011e53a34dc248a2f0e6", + "out": "e7d7a113b3a33175d0abd2cf4f9add8e41dc86c93c9552c5b3588277fbcaa24a" + }, + { + "in": "3b8e97c5ffc2d6a40fa7de7fcefc90f3b12c940e7ab415321e29ee692dfac799b009c99dcddb708fce5a178c5c35ee2b8617143edc4c40b4d313661f49abdd93cea79d117518805496fe6acf292c4c2a1f76b403a97d7c399daf85b46ad84e16246c67d6836757bde336c290d5d401e6c1386ab32797af6bb251e9b2d8fe754c47482b72e0b394eab76916126fd68ea7d65eb93d59f5b4c5ac40f7c3b37e7f3694f29424c24af8c8f0ef59cd9dbf1d28e0e10f799a6f78cad1d45b9db3d7dee4a7059abe99182714983b9c9d44d7f5643596d4f3", + "out": "3b40c1493af411ae7849904d478df2407254bf62b88e9bffd7b42bd2a60ce0fa" + }, + { + "in": "3434ec31b10fafdbfeec0dd6bd94e80f7ba9dca19ef075f7eb017512af66d6a4bcf7d16ba0819a1892a6372f9b35bcc7ca8155ee19e8428bc22d214856ed5fa9374c3c09bde169602cc219679f65a1566fc7316f4cc3b631a18fb4449fa6afa16a3db2bc4212eff539c67cf184680826535589c7111d73bffce431b4c40492e763d9279560aaa38eb2dc14a212d723f994a1fe656ff4dd14551ce4e7c621b2aa5604a10001b2878a897a28a08095c325e10a26d2fb1a75bfd64c250309bb55a44f23bbac0d5516a1c687d3b41ef2fbbf9cc56d4739", + "out": "feeb172aeab2f0deb748fb77801ca22d3ce99b7a9f9789e479b93d1f4b1d227f" + }, + { + "in": "7c7953d81c8d208fd1c97681d48f49dd003456de60475b84070ef4847c333b74575b1fc8d2a186964485a3b8634feaa3595aaa1a2f4595a7d6b6153563dee31bbac443c8a33eed6d5d956a980a68366c2527b550ee950250dfb691eacbd5d56ae14b970668be174c89df2fea43ae52f13142639c884fd62a3683c0c3792f0f24ab1318bcb27e21f4737fab62c77ea38bc8fd1cf41f7dab64c13febe7152bf5bb7ab5a78f5346d43cc741cb6f72b7b8980f268b68bf62abdfb1577a52438fe14b591498cc95f071228460c7c5d5ceb4a7bde588e7f21c", + "out": "b240bc52b8af1b502e26bf1d5e75fe2663bfba503faf10f46754dc3d23cb61c1" + }, + { + "in": "7a6a4f4fdc59a1d223381ae5af498d74b7252ecf59e389e49130c7eaee626e7bd9897effd92017f4ccde66b0440462cdedfd352d8153e6a4c8d7a0812f701cc737b5178c2556f07111200eb627dbc299caa792dfa58f35935299fa3a3519e9b03166dffa159103ffa35e8577f7c0a86c6b46fe13db8e2cdd9dcfba85bdddcce0a7a8e155f81f712d8e9fe646153d3d22c811bd39f830433b2213dd46301941b59293fd0a33e2b63adbd95239bc01315c46fdb678875b3c81e053a40f581cfbec24a1404b1671a1b88a6d06120229518fb13a74ca0ac5ae", + "out": "3ebace41f578fde6603e032fc1c7cfeef1cb79fe938a94d4c7b58b0ba4cb9720" + }, + { + "in": "d9faa14cebe9b7de551b6c0765409a33938562013b5e8e0e1e0a6418df7399d0a6a771fb81c3ca9bd3bb8e2951b0bc792525a294ebd1083688806fe5e7f1e17fd4e3a41d00c89e8fcf4a363caedb1acb558e3d562f1302b3d83bb886ed27b76033798131dab05b4217381eaaa7ba15ec820bb5c13b516dd640eaec5a27d05fdfca0f35b3a5312146806b4c0275bcd0aaa3b2017f346975db566f9b4d137f4ee10644c2a2da66deeca5342e236495c3c6280528bfd32e90af4cd9bb908f34012b52b4bc56d48cc8a6b59bab014988eabd12e1a0a1c2e170e7", + "out": "65eb4bd5ecca7164ce9b66727f112c1ac6120ddd200dcb5ce75b7487843fcdb8" + }, + { + "in": "2d8427433d0c61f2d96cfe80cf1e932265a191365c3b61aaa3d6dcc039f6ba2ad52a6a8cc30fc10f705e6b7705105977fa496c1c708a277a124304f1fc40911e7441d1b5e77b951aad7b01fd5db1b377d165b05bbf898042e39660caf8b279fe5229d1a8db86c0999ed65e53d01ccbc4b43173ccf992b3a14586f6ba42f5fe30afa8ae40c5df29966f9346da5f8b35f16a1de3ab6de0f477d8d8660918060e88b9b9e9ca6a4207033b87a812dbf5544d39e4882010f82b6ce005f8e8ff6fe3c3806bc2b73c2b83afb704345629304f9f86358712e9fae3ca3e", + "out": "d7155f6d3a90801f5e547689389ff62a604c81b7c1583d9204ac6b0194f0e8dd" + }, + { + "in": "5e19d97887fcaac0387e22c6f803c34a3dacd2604172433f7a8a7a526ca4a2a1271ecfc5d5d7be5ac0d85d921095350dfc65997d443c21c8094e0a3fefd2961bcb94aed03291ae310ccda75d8ace4bc7d89e7d3e5d1650bda5d668b8b50bfc8e608e184f4d3a9a2badc4ff5f07e0c0bc8a9f2e0b2a26fd6d8c550008faaab75fd71af2a424bec9a7cd9d83fad4c8e9319115656a8717d3b523a68ff8004258b9990ed362308461804ba3e3a7e92d8f2ffae5c2fba55ba5a3c27c0a2f71bd711d2fe1799c2adb31b200035481e9ee5c4adf2ab9c0fa50b23975cf", + "out": "aa7adaf16f39e398b4ab0ada037710556b720b0248d84817b2cfdf7600933595" + }, + { + "in": "c8e976ab4638909387ce3b8d4e510c3230e5690e02c45093b1d297910abc481e56eea0f296f98379dfc9080af69e73b2399d1c143bee80ae1328162ce1ba7f6a8374679b20aacd380eb4e61382c99998704d62701afa914f9a2705cdb065885f50d086c3eb5753700c387118bb142f3e6da1e988dfb31ac75d7368931e45d1391a274b22f83ceb072f9bcabc0b216685bfd789f5023971024b1878a205442522f9ea7d8797a4102a3df41703768251fd5e017c85d1200a464118aa35654e7ca39f3c375b8ef8cbe7534dbc64bc20befb417cf60ec92f63d9ee7397", + "out": "b195463fe22a160802be0a0464ee3ab4d2b117de517b331c7bf04c8ba90c6120" + }, + { + "in": "7145fa124b7429a1fc2231237a949ba7201bcc1822d3272de005b682398196c25f7e5cc2f289fbf44415f699cb7fe6757791b1443410234ae061edf623359e2b4e32c19bf88450432dd01caa5eb16a1dc378f391ca5e3c4e5f356728bddd4975db7c890da8bbc84cc73ff244394d0d48954978765e4a00b593f70f2ca082673a261ed88dbcef1127728d8cd89bc2c597e9102ced6010f65fa75a14ebe467fa57ce3bd4948b6867d74a9df5c0ec6f530cbf2ee61ce6f06bc8f2864dff5583776b31df8c7ffcb61428a56bf7bd37188b4a5123bbf338393af46eda85e6", + "out": "9f9296c53e753a4de4e5c5a547f51763a96903b083fbc7a7828effe4763a7ce6" + }, + { + "in": "7fdfadcc9d29bad23ae038c6c65cda1aef757221b8872ed3d75ff8df7da0627d266e224e812c39f7983e4558bfd0a1f2bef3feb56ba09120ef762917b9c093867948547aee98600d10d87b20106878a8d22c64378bf634f7f75900c03986b077b0bf8b740a82447b61b99fee5376c5eb6680ec9e3088f0bdd0c56883413d60c1357d3c811950e5890e7600103c916341b80c743c6a852b7b4fb60c3ba21f3bc15b8382437a68454779cf3cd7f9f90ccc8ef28d0b706535b1e4108eb5627bb45d719cb046839aee311ca1abdc8319e050d67972cb35a6b1601b25dbf487", + "out": "51de4090aec36f6c446476c709253272cab595d9887ca5d52a9b38086854d58f" + }, + { + "in": "988638219fd3095421f826f56e4f09e356296b628c3ce6930c9f2e758fd1a80c8273f2f61e4daae65c4f110d3e7ca0965ac7d24e34c0dc4ba2d6ff0bf5bbe93b3585f354d7543cb542a1aa54674d375077f2d360a8f4d42f3db131c3b7ab7306267ba107659864a90c8c909460a73621d1f5d9d3fd95beb19b23db1cb6c0d0fba91d36891529b8bd8263caa1bab56a4affaed44962df096d8d5b1eb845ef31188b3e10f1af811a13f156beb7a288aae593ebd1471b624aa1a7c6adf01e2200b3d72d88a3aed3100c88231e41efc376906f0b580dc895f080fda5741db1cb", + "out": "87a17400f919f2f53232b2205e1e8b14bd5698a76e74b9bdd5638a5c7ba5de1e" + }, + { + "in": "5aab62756d307a669d146aba988d9074c5a159b3de85151a819b117ca1ff6597f6156e80fdd28c9c3176835164d37da7da11d94e09add770b68a6e081cd22ca0c004bfe7cd283bf43a588da91f509b27a6584c474a4a2f3ee0f1f56447379240a5ab1fb77fdca49b305f07ba86b62756fb9efb4fc225c86845f026ea542076b91a0bc2cdd136e122c659be259d98e5841df4c2f60330d4d8cdee7bf1a0a244524eecc68ff2aef5bf0069c9e87a11c6e519de1a4062a10c83837388f7ef58598a3846f49d499682b683c4a062b421594fafbc1383c943ba83bdef515efcf10d", + "out": "9742536c461d0c3503a6c943fa8105dbcd1e542f728d71ccc0517cffc232ea68" + }, + { + "in": "47b8216aa0fbb5d67966f2e82c17c07aa2d6327e96fcd83e3de7333689f3ee79994a1bf45082c4d725ed8d41205cb5bcdf5c341f77facb1da46a5b9b2cbc49eadf786bcd881f371a95fa17df73f606519aea0ff79d5a11427b98ee7f13a5c00637e2854134691059839121fea9abe2cd1bcbbbf27c74caf3678e05bfb1c949897ea01f56ffa4dafbe8644611685c617a3206c7a7036e4ac816799f693dafe7f19f303ce4eba09d21e03610201bfc665b72400a547a1e00fa9b7ad8d84f84b34aef118515e74def11b9188bd1e1f97d9a12c30132ec2806339bdadacda2fd8b78", + "out": "ae3bf0936497a2955df874b7f2685314c7606030b9c6e7bfb8a8dff9825957b5" + }, + { + "in": "8cff1f67fe53c098896d9136389bd8881816ccab34862bb67a656e3d98896f3ce6ffd4da73975809fcdf9666760d6e561c55238b205d8049c1cedeef374d1735daa533147bfa960b2cce4a4f254176bb4d1bd1e89654432b8dbe1a135c42115b394b024856a2a83dc85d6782be4b444239567ccec4b184d4548eae3ff6a192f343292ba2e32a0f267f31cc26719eb85245d415fb897ac2da433ee91a99424c9d7f1766a44171d1651001c38fc79294accc68ceb5665d36218454d3ba169ae058a831338c17743603f81ee173bfc0927464f9bd728dee94c6aeab7aae6ee3a627e8", + "out": "5fe0216dcc1bdb48f3375b9173b7b232939aa2177c6d056e908c8f2b9293b030" + }, + { + "in": "eacd07971cff9b9939903f8c1d8cbb5d4db1b548a85d04e037514a583604e787f32992bf2111b97ac5e8a938233552731321522ab5e8583561260b7d13ebeef785b23a41fd8576a6da764a8ed6d822d4957a545d5244756c18aa80e1aad4d1f9c20d259dee1711e2cc8fd013169fb7cc4ce38b362f8e0936ae9198b7e838dcea4f7a5b9429bb3f6bbcf2dc92565e3676c1c5e6eb3dd2a0f86aa23edd3d0891f197447692794b3dfa269611ad97f72b795602b4fdb198f3fd3eb41b415064256e345e8d8c51c555dc8a21904a9b0f1ad0effab7786aac2da3b196507e9f33ca356427", + "out": "c339904ec865f24fb3f88f142a8786d770934e006eaeddbf45acbb6b38431021" + }, + { + "in": "23ac4e9a42c6ef45c3336ce6dfc2ff7de8884cd23dc912fef0f7756c09d335c189f3ad3a23697abda851a81881a0c8ccafc980ab2c702564c2be15fe4c4b9f10dfb2248d0d0cb2e2887fd4598a1d4acda897944a2ffc580ff92719c95cf2aa42dc584674cb5a9bc5765b9d6ddf5789791d15f8dd925aa12bffafbce60827b490bb7df3dda6f2a143c8bf96abc903d83d59a791e2d62814a89b8080a28060568cf24a80ae61179fe84e0ffad00388178cb6a617d37efd54cc01970a4a41d1a8d3ddce46edbba4ab7c90ad565398d376f431189ce8c1c33e132feae6a8cd17a61c630012", + "out": "4ca8b7febdf0a8062e9b76185cf4165071bb30928c18f14338c305626789c6d3" + }, + { + "in": "0172df732282c9d488669c358e3492260cbe91c95cfbc1e3fea6c4b0ec129b45f242ace09f152fc6234e1bee8aab8cd56e8b486e1dcba9c05407c2f95da8d8f1c0af78ee2ed82a3a79ec0cb0709396ee62aadb84f8a4ee8a7ccca3c1ee84e302a09ea802204afecf04097e67d0f8e8a9d2651126c0a598a37081e42d168b0ae8a71951c524259e4e2054e535b779679bdade566fe55700858618e626b4a0faf895bcce9011504a49e05fd56127eae3d1f8917afb548ecadabda1020111fec9314c413498a360b08640549a22cb23c731ace743252a8227a0d2689d4c6001606678dfb921", + "out": "23d2614420859b2f13ac084453dd35c33fe47c894dd50c087fd1653fcaeea00b" + }, + { + "in": "3875b9240cf3e0a8b59c658540f26a701cf188496e2c2174788b126fd29402d6a75453ba0635284d08835f40051a2a9683dc92afb9383719191231170379ba6f4adc816fecbb0f9c446b785bf520796841e58878b73c58d3ebb097ce4761fdeabe15de2f319dfbaf1742cdeb389559c788131a6793e193856661376c81ce9568da19aa6925b47ffd77a43c7a0e758c37d69254909ff0fbd415ef8eb937bcd49f91468b49974c07dc819abd67395db0e05874ff83dddab895344abd0e7111b2df9e58d76d85ad98106b36295826be04d435615595605e4b4bb824b33c4afeb5e7bb0d19f909", + "out": "5590bb75247d7cd0b35620f0062b90ffb2a24de41220ed629d9e9a7abcadfb51" + }, + { + "in": "747cc1a59fefba94a9c75ba866c30dc5c1cb0c0f8e9361d98484956dd5d1a40f6184afbe3dac9f76028d1caeccfbf69199c6ce2b4c092a3f4d2a56fe5a33a00757f4d7dee5dfb0524311a97ae0668a47971b95766e2f6dd48c3f57841f91f04a00ad5ea70f2d479a2620dc5cd78eaab3a3b011719b7e78d19ddf70d9423798af77517ebc55392fcd01fc600d8d466b9e7a7a85bf33f9cc5419e9bd874ddfd60981150ddaf8d7febaa4374f0872a5628d318000311e2f5655365ad4d407c20e5c04df17a222e7deec79c5ab1116d8572f91cd06e1ccc7ced53736fc867fd49ecebe6bf8082e8a", + "out": "e5932441b012e503b0b0c6104703ba02613e472ad65655c085b0adb07656b28f" + }, + { + "in": "57af971fccaec97435dc2ec9ef0429bcedc6b647729ea168858a6e49ac1071e706f4a5a645ca14e8c7746d65511620682c906c8b86ec901f3dded4167b3f00b06cbfac6aee3728051b3e5ff10b4f9ed8bd0b8da94303c833755b3ca3aeddf0b54bc8d6632138b5d25bab03d17b3458a9d782108006f5bb7de75b5c0ba854b423d8bb801e701e99dc4feaad59bc1c7112453b04d33ea3635639fb802c73c2b71d58a56bbd671b18fe34ed2e3dca38827d63fdb1d4fb3285405004b2b3e26081a8ff08cd6d2b08f8e7b7e90a2ab1ed7a41b1d0128522c2f8bff56a7fe67969422ce839a9d4608f03", + "out": "21c0d84eb7b61774f97db5d9acf1dffafb662c01ed291a442bec6f14d1334699" + }, + { + "in": "04e16dedc1227902baaf332d3d08923601bdd64f573faa1bb7201918cfe16b1e10151dae875da0c0d63c59c3dd050c4c6a874011b018421afc4623ab0381831b2da2a8ba42c96e4f70864ac44e106f94311051e74c77c1291bf5db9539e69567bf6a11cf6932bbbad33f8946bf5814c066d851633d1a513510039b349939bfd42b858c21827c8ff05f1d09b1b0765dc78a135b5ca4dfba0801bcaddfa175623c8b647eacfb4444b85a44f73890607d06d507a4f8393658788669f6ef4deb58d08c50ca0756d5e2f49d1a7ad73e0f0b3d3b5f090acf622b1878c59133e4a848e05153592ea81c6fbf", + "out": "0d1e6bb88188b49af0a9a05eb1af94255e6799515a2f8eb46aa6af9a9dd5b9e0" + }, + { + "in": "7c815c384eee0f288ece27cced52a01603127b079c007378bc5d1e6c5e9e6d1c735723acbbd5801ac49854b2b569d4472d33f40bbb8882956245c366dc3582d71696a97a4e19557e41e54dee482a14229005f93afd2c4a7d8614d10a97a9dfa07f7cd946fa45263063ddd29db8f9e34db60daa32684f0072ea2a9426ecebfa5239fb67f29c18cbaa2af6ed4bf4283936823ac1790164fec5457a9cba7c767ca59392d94cab7448f50eb34e9a93a80027471ce59736f099c886dea1ab4cba4d89f5fc7ae2f21ccd27f611eca4626b2d08dc22382e92c1efb2f6afdc8fdc3d2172604f5035c46b8197d3", + "out": "935ded24f5cecc69e1f012b60b7831abce7ef50eeb0bea7f816c3dbf2b4abdc1" + }, + { + "in": "e29d505158dbdd937d9e3d2145658ee6f5992a2fc790f4f608d9cdb44a091d5b94b88e81fac4fdf5c49442f13b911c55886469629551189eaff62488f1a479b7db11a1560e198ddccccf50159093425ff7f1cb8d1d1246d0978764087d6bac257026b090efae8cec5f22b6f21c59ace1ac7386f5b8837ca6a12b6fbf5534dd0560ef05ca78104d3b943ddb220feaec89aa5e692a00f822a2ab9a2fe60350d75e7be16ff2526dc643872502d01f42f188abed0a6e9a6f5fd0d1ce7d5755c9ffa66b0af0b20bd806f08e06156690d81ac811778ca3dac2c249b96002017fce93e507e3b953acf99964b847", + "out": "6755bf7e60e4e07965bac24e51b1de93e3dd42ae780f256647d4cc2ef8eff771" + }, + { + "in": "d85588696f576e65eca0155f395f0cfacd83f36a99111ed5768df2d116d2121e32357ba4f54ede927f189f297d3a97fad4e9a0f5b41d8d89dd7fe20156799c2b7b6bf9c957ba0d6763f5c3bc5129747bbb53652b49290cff1c87e2cdf2c4b95d8aaee09bc8fbfa6883e62d237885810491bfc101f1d8c636e3d0ede838ad05c207a3df4fad76452979eb99f29afaecedd1c63b8d36cf378454a1bb67a741c77ac6b6b3f95f4f02b64dabc15438613ea49750df42ee90101f115aa9abb9ff64324dde9dabbb01054e1bd6b4bcdc7930a44c2300d87ca78c06924d0323ad7887e46c90e8c4d100acd9eed21e", + "out": "62c9f5e5b56e2994327a7f9a03888da7bad67e387593803b1807482b137b4509" + }, + { + "in": "3a12f8508b40c32c74492b66323375dcfe49184c78f73179f3314b79e63376b8ac683f5a51f1534bd729b02b04d002f55cbd8e8fc9b5ec1ea6bbe6a0d0e7431518e6ba45d124035f9d3dce0a8bb7bf1430a9f657e0b4ea9f20eb20c786a58181a1e20a96f1628f8728a13bdf7a4b4b32fc8aa7054cc4881ae7fa19afa65c6c3ee1b3ade3192af42054a8a911b8ec1826865d46d93f1e7c5e2b7813c92a506e53886f3d4701bb93d2a681ad109c845904bb861af8af0646b6e399b38b614051d34f6842563a0f37ec00cb3d865fc5d746c4987de2a65071100883a2a9c7a2bfe1e2dd603d9ea24dc7c5fd06be", + "out": "9927fa5efd86304e73d54aa4928818c05b01504672c529471394a82e049e5f95" + }, + { + "in": "1861edce46fa5ad17e1ff1deae084dec580f97d0a67885dfe834b9dfac1ae076742ce9e267512ca51f6df5a455af0c5fd6abf94acea103a3370c354485a7846fb84f3ac7c2904b5b2fbf227002ce512133bb7e1c4e50057bfd1e44db33c7cdb969a99e284b184f50a14b068a1fc5009d9b298dbe92239572a7627aac02abe8f3e3b473417f36d4d2505d16b7577f4526c9d94a270a2dfe450d06da8f6fa956879a0a55cfe99e742ea555ea477ba3e9b44ccd508c375423611af92e55345dc215779b2d5119eba49c71d49b9fe3f1569fa24e5ca3e332d042422a8b8158d3ec66a80012976f31ffdf305f0c9c5e", + "out": "84e056bf7bdfc73a3aaa95b00a74a136d776069beeb304423bead90120db6350" + }, + { + "in": "08d0ffde3a6e4ef65608ea672e4830c12943d7187ccff08f4941cfc13e545f3b9c7ad5eebbe2b01642b486caf855c2c73f58c1e4e3391da8e2d63d96e15fd84953ae5c231911b00ad6050cd7aafdaac9b0f663ae6aab45519d0f5391a541707d479034e73a6ad805ae3598096af078f1393301493d663dd71f83869ca27ba508b7e91e81e128c1716dc3acfe3084b2201e04cf8006617eecf1b640474a5d45cfde9f4d3ef92d6d055b909892194d8a8218db6d8203a84261d200d71473d7488f3427416b6896c137d455f231071cacbc86e0415ab88aec841d96b7b8af41e05bb461a40645bf176601f1e760de5f", + "out": "401c3be59cc373453aef9603f7335c1d5fe669909a1425d7671dcb84a49887ca" + }, + { + "in": "d782abb72a5be3392757be02d3e45be6e2099d6f000d042c8a543f50ed6ebc055a7f133b0dd8e9bc348536edcaae2e12ec18e8837df7a1b3c87ec46d50c241dee820fd586197552dc20beea50f445a07a38f1768a39e2b2ff05dddedf751f1def612d2e4d810daa3a0cc904516f9a43af660315385178a529e51f8aae141808c8bc5d7b60cac26bb984ac1890d0436ef780426c547e94a7b08f01acbfc4a3825eae04f520a9016f2fb8bf5165ed12736fc71e36a49a73614739eaa3ec834069b1b40f1350c2b3ab885c02c640b9f7686ed5f99527e41cfcd796fe4c256c9173186c226169ff257954ebda81c0e5f99", + "out": "020485dcd264296afdb7f643ca828c93356f1714cbcc2fbbdd30f9896c3f2789" + }, + { + "in": "5fce8109a358570e40983e1184e541833bb9091e280f258cfb144387b05d190e431cb19baa67273ba0c58abe91308e1844dcd0b3678baa42f335f2fa05267a0240b3c718a5942b3b3e3bfa98a55c25a1466e8d7a603722cb2bbf03afa54cd769a99f310735ee5a05dae2c22d397bd95635f58c48a67f90e1b73aafcd3f82117f0166657838691005b18da6f341d6e90fc1cdb352b30fae45d348294e501b63252de14740f2b85ae5299ddec3172de8b6d0ba219a20a23bb5e10ff434d39db3f583305e9f5c039d98569e377b75a70ab837d1df269b8a4b566f40bb91b577455fd3c356c914fa06b9a7ce24c7317a172d", + "out": "f8c43e28816bb41993bdb866888f3cc59efba208390144d3878dbf9fbfa1d57e" + }, + { + "in": "6172f1971a6e1e4e6170afbad95d5fec99bf69b24b674bc17dd78011615e502de6f56b86b1a71d3f4348087218ac7b7d09302993be272e4a591968aef18a1262d665610d1070ee91cc8da36e1f841a69a7a682c580e836941d21d909a3afc1f0b963e1ca5ab193e124a1a53df1c587470e5881fb54dae1b0d840f0c8f9d1b04c645ba1041c7d8dbf22030a623aa15638b3d99a2c400ff76f3252079af88d2b37f35ee66c1ad7801a28d3d388ac450b97d5f0f79e4541755356b3b1a5696b023f39ab7ab5f28df4202936bc97393b93bc915cb159ea1bd7a0a414cb4b7a1ac3af68f50d79f0c9c7314e750f7d02faa58bfa", + "out": "4ea524e705020284b18284e34683725590e1ee565a6ff598ed4d42b1c987471e" + }, + { + "in": "5668ecd99dfbe215c4118398ac9c9eaf1a1433fab4ccdd3968064752b625ea944731f75d48a27d047d67547f14dd0ffaa55fa5e29f7af0d161d85eafc4f2029b717c918eab9d304543290bdba7158b68020c0ba4e079bc95b5bc0fc044a992b94b4ccd3bd66d0eabb5dbbab904d62e00752c4e3b0091d773bcf4c14b4377da3efff824b1cb2fa01b32d1e46c909e626ed2dae920f4c7dbeb635bc754facbd8d49beba3f23c1c41ccbfcd0ee0c114e69737f5597c0bf1d859f0c767e18002ae8e39c26261ffde2920d3d0baf0e906138696cfe5b7e32b600f45df3aaa39932f3a7df95b60fa8712a2271fcaf3911ce7b511b1", + "out": "e4963e74ae01ff7774b96b4f614d1cb2a4cf8d206ed93c66fa42f71432be2c3f" + }, + { + "in": "03d625488354df30e3f875a68edfcf340e8366a8e1ab67f9d5c5486a96829dfac0578289082b2a62117e1cf418b43b90e0adc881fc6ae8105c888e9ecd21aea1c9ae1a4038dfd17378fed71d02ae492087d7cdcd98f746855227967cb1ab4714261ee3bead3f4db118329d3ebef4bc48a875c19ba763966da0ebea800e01b2f50b00e9dd4caca6dcb314d00184ef71ea2391d760c950710db4a70f9212ffc54861f9dc752ce18867b8ad0c48df8466ef7231e7ac567f0eb55099e622ebb86cb237520190a61c66ad34f1f4e289cb3282ae3eaac6152ed24d2c92bae5a7658252a53c49b7b02dfe54fdb2e90074b6cf310ac661", + "out": "0f0d72bf8c0198459e45ece9cc18e930cb86263accf1fc7a00bc857ac9f201ad" + }, + { + "in": "2edc282ffb90b97118dd03aaa03b145f363905e3cbd2d50ecd692b37bf000185c651d3e9726c690d3773ec1e48510e42b17742b0b0377e7de6b8f55e00a8a4db4740cee6db0830529dd19617501dc1e9359aa3bcf147e0a76b3ab70c4984c13e339e6806bb35e683af8527093670859f3d8a0fc7d493bcba6bb12b5f65e71e705ca5d6c948d66ed3d730b26db395b3447737c26fad089aa0ad0e306cb28bf0acf106f89af3745f0ec72d534968cca543cd2ca50c94b1456743254e358c1317c07a07bf2b0eca438a709367fafc89a57239028fc5fecfd53b8ef958ef10ee0608b7f5cb9923ad97058ec067700cc746c127a61ee3", + "out": "dd1d2a92b3f3f3902f064365838e1f5f3468730c343e2974e7a9ecfcd84aa6db" + }, + { + "in": "90b28a6aa1fe533915bcb8e81ed6cacdc10962b7ff82474f845eeb86977600cf70b07ba8e3796141ee340e3fce842a38a50afbe90301a3bdcc591f2e7d9de53e495525560b908c892439990a2ca2679c5539ffdf636777ad9c1cdef809cda9e8dcdb451abb9e9c17efa4379abd24b182bd981cafc792640a183b61694301d04c5b3eaad694a6bd4cc06ef5da8fa23b4fa2a64559c5a68397930079d250c51bcf00e2b16a6c49171433b0aadfd80231276560b80458dd77089b7a1bbcc9e7e4b9f881eacd6c92c4318348a13f4914eb27115a1cfc5d16d7fd94954c3532efaca2cab025103b2d02c6fd71da3a77f417d7932685888a", + "out": "21bf20664cec2cd2ceb1dffc1d78893d5ca1a7da88eb6bfd0c6efca6190c9e15" + }, + { + "in": "2969447d175490f2aa9bb055014dbef2e6854c95f8d60950bfe8c0be8de254c26b2d31b9e4de9c68c9adf49e4ee9b1c2850967f29f5d08738483b417bb96b2a56f0c8aca632b552059c59aac3f61f7b45c966b75f1d9931ff4e596406378cee91aaa726a3a84c33f37e9cdbe626b5745a0b06064a8a8d56e53aaf102d23dd9df0a3fdf7a638509a6761a33fa42fa8ddbd8e16159c93008b53765019c3f0e9f10b144ce2ac57f5d7297f9c9949e4ff68b70d339f87501ce8550b772f32c6da8ad2ce2100a895d8b08fa1eead7c376b407709703c510b50f87e73e43f8e7348f87c3832a547ef2bbe5799abedcf5e1f372ea809233f006", + "out": "6472d7c530b548e4b47d2278d7172b421a0fb6398a2823dd2f2b26208af8942e" + }, + { + "in": "721645633a44a2c78b19024eaecf58575ab23c27190833c26875dc0f0d50b46aea9c343d82ea7d5b3e50ec700545c615daeaea64726a0f05607576dcd396d812b03fb6551c641087856d050b10e6a4d5577b82a98afb89cee8594c9dc19e79feff0382fcfd127f1b803a4b9946f4ac9a4378e1e6e041b1389a53e3450cd32d9d2941b0cbabdb50da8ea2513145164c3ab6bcbd251c448d2d4b087ac57a59c2285d564f16da4ed5e607ed979592146ffb0ef3f3db308fb342df5eb5924a48256fc763141a278814c82d6d6348577545870ae3a83c7230ac02a1540fe1798f7ef09e335a865a2ae0949b21e4f748fb8a51f44750e213a8fb", + "out": "2ac7ff80ee36d500995c973b8746d8466715e6d8b0f554aacb5d2876d7f5b874" + }, + { + "in": "6b860d39725a14b498bb714574b4d37ca787404768f64c648b1751b353ac92bac2c3a28ea909fdf0423336401a02e63ec24325300d823b6864bb701f9d7c7a1f8ec9d0ae3584aa6dd62ea1997cd831b4babd9a4da50932d4efda745c61e4130890e156aee6113716daf95764222a91187db2effea49d5d0596102d619bd26a616bbfda8335505fbb0d90b4c180d1a2335b91538e1668f9f9642790b4e55f9cab0fe2bdd2935d001ee6419abab5457880d0dbff20ed8758f4c20fe759efb33141cf0e892587fe8187e5fbc57786b7e8b089612c936dfc03d27efbbe7c8673f1606bd51d5ff386f4a7ab68edf59f385eb1291f117bfe717399", + "out": "9ff81d575f7bf0c4ef340b4279d56e16ce68821afcdf2a69105d4f9cadadd3cf" + }, + { + "in": "6a01830af3889a25183244decb508bd01253d5b508ab490d3124afbf42626b2e70894e9b562b288d0a2450cfacf14a0ddae5c04716e5a0082c33981f6037d23d5e045ee1ef2283fb8b6378a914c5d9441627a722c282ff452e25a7ea608d69cee4393a0725d17963d0342684f255496d8a18c2961145315130549311fc07f0312fb78e6077334f87eaa873bee8aa95698996eb21375eb2b4ef53c14401207deb4568398e5dd9a7cf97e8c9663e23334b46912f8344c19efcf8c2ba6f04325f1a27e062b62a58d0766fc6db4d2c6a1928604b0175d872d16b7908ebc041761187cc785526c2a3873feac3a642bb39f5351550af9770c328af7b", + "out": "09edc465d4fd91c5e86b292f041bcc17571e1f2e17d584dff21dd7dd8d8bff35" + }, + { + "in": "b3c5e74b69933c2533106c563b4ca20238f2b6e675e8681e34a389894785bdade59652d4a73d80a5c85bd454fd1e9ffdad1c3815f5038e9ef432aac5c3c4fe840cc370cf86580a6011778bbedaf511a51b56d1a2eb68394aa299e26da9ada6a2f39b9faff7fba457689b9c1a577b2a1e505fdf75c7a0a64b1df81b3a356001bf0df4e02a1fc59f651c9d585ec6224bb279c6beba2966e8882d68376081b987468e7aed1ef90ebd090ae825795cdca1b4f09a979c8dfc21a48d8a53cdbb26c4db547fc06efe2f9850edd2685a4661cb4911f165d4b63ef25b87d0a96d3dff6ab0758999aad214d07bd4f133a6734fde445fe474711b69a98f7e2b", + "out": "c6d86cc4ccef3bb70bf7bfddec6a9a04a0dd0a68fe1bf51c14648cf506a03e98" + }, + { + "in": "83af34279ccb5430febec07a81950d30f4b66f484826afee7456f0071a51e1bbc55570b5cc7ec6f9309c17bf5befdd7c6ba6e968cf218a2b34bd5cf927ab846e38a40bbd81759e9e33381016a755f699df35d660007b5eadf292feefb735207ebf70b5bd17834f7bfa0e16cb219ad4af524ab1ea37334aa66435e5d397fc0a065c411ebbce32c240b90476d307ce802ec82c1c49bc1bec48c0675ec2a6c6f3ed3e5b741d13437095707c565e10d8a20b8c20468ff9514fcf31b4249cd82dcee58c0a2af538b291a87e3390d737191a07484a5d3f3fb8c8f15ce056e5e5f8febe5e1fb59d6740980aa06ca8a0c20f5712b4cde5d032e92ab89f0ae1", + "out": "1afc9ba63eea27603b3a7a5562e12b31e8fe9a96812b531e9d048385fb76d44f" + }, + { + "in": "a7ed84749ccc56bb1dfba57119d279d412b8a986886d810f067af349e8749e9ea746a60b03742636c464fc1ee233acc52c1983914692b64309edfdf29f1ab912ec3e8da074d3f1d231511f5756f0b6eead3e89a6a88fe330a10face267bffbfc3e3090c7fd9a850561f363ad75ea881e7244f80ff55802d5ef7a1a4e7b89fcfa80f16df54d1b056ee637e6964b9e0ffd15b6196bdd7db270c56b47251485348e49813b4eb9ed122a01b3ea45ad5e1a929df61d5c0f3e77e1fdc356b63883a60e9cbb9fc3e00c2f32dbd469659883f690c6772e335f617bc33f161d6f6984252ee12e62b6000ac5231e0c9bc65be223d8dfd94c5004a101af9fd6c0fb", + "out": "9b5e15531385f0d495fdbe686e3e02eca42b9f1b1ce8837ad3b3e42e6198050a" + }, + { + "in": "a6fe30dcfcda1a329e82ab50e32b5f50eb25c873c5d2305860a835aecee6264aa36a47429922c4b8b3afd00da16035830edb897831c4e7b00f2c23fc0b15fdc30d85fb70c30c431c638e1a25b51caf1d7e8b050b7f89bfb30f59f0f20fecff3d639abc4255b3868fc45dd81e47eb12ab40f2aac735df5d1dc1ad997cefc4d836b854cee9ac02900036f3867fe0d84afff37bde3308c2206c62c4743375094108877c73b87b2546fe05ea137bedfc06a2796274099a0d554da8f7d7223a48cbf31b7decaa1ebc8b145763e3673168c1b1b715c1cd99ecd3ddb238b06049885ecad9347c2436dff32c771f34a38587a44a82c5d3d137a03caa27e66c8ff6", + "out": "216fc325f942eed08401527a8f41c088527c6479342622c907ea08ff3290f8c6" + }, + { + "in": "83167ff53704c3aa19e9fb3303539759c46dd4091a52ddae9ad86408b69335989e61414bc20ab4d01220e35241eff5c9522b079fba597674c8d716fe441e566110b6211531ceccf8fd06bc8e511d00785e57788ed9a1c5c73524f01830d2e1148c92d0edc97113e3b7b5cd3049627abdb8b39dd4d6890e0ee91993f92b03354a88f52251c546e64434d9c3d74544f23fb93e5a2d2f1fb15545b4e1367c97335b0291944c8b730ad3d4789273fa44fb98d78a36c3c3764abeeac7c569c1e43a352e5b770c3504f87090dee075a1c4c85c0c39cf421bdcc615f9eff6cb4fe6468004aece5f30e1ecc6db22ad9939bb2b0ccc96521dfbf4ae008b5b46bc006e", + "out": "43184b9f2db5b6da5160bc255dbe19a0c94533b884809815b7b326d868589edc" + }, + { + "in": "3a3a819c48efde2ad914fbf00e18ab6bc4f14513ab27d0c178a188b61431e7f5623cb66b23346775d386b50e982c493adbbfc54b9a3cd383382336a1a0b2150a15358f336d03ae18f666c7573d55c4fd181c29e6ccfde63ea35f0adf5885cfc0a3d84a2b2e4dd24496db789e663170cef74798aa1bbcd4574ea0bba40489d764b2f83aadc66b148b4a0cd95246c127d5871c4f11418690a5ddf01246a0c80a43c70088b6183639dcfda4125bd113a8f49ee23ed306faac576c3fb0c1e256671d817fc2534a52f5b439f72e424de376f4c565cca82307dd9ef76da5b7c4eb7e085172e328807c02d011ffbf33785378d79dc266f6a5be6bb0e4a92eceebaeb1", + "out": "348fb774adc970a16b1105669442625e6adaa8257a89effdb5a802f161b862ea" + }, + { + "in": "724627916c50338643e6996f07877eafd96bdf01da7e991d4155b9be1295ea7d21c9391f4c4a41c75f77e5d27389253393725f1427f57914b273ab862b9e31dabce506e558720520d33352d119f699e784f9e548ff91bc35ca147042128709820d69a8287ea3257857615eb0321270e94b84f446942765ce882b191faee7e1c87e0f0bd4e0cd8a927703524b559b769ca4ece1f6dbf313fdcf67c572ec4185c1a88e86ec11b6454b371980020f19633b6b95bd280e4fbcb0161e1a82470320cec6ecfa25ac73d09f1536f286d3f9dacafb2cd1d0ce72d64d197f5c7520b3ccb2fd74eb72664ba93853ef41eabf52f015dd591500d018dd162815cc993595b195", + "out": "ea0e416c0f7b4f11e3f00479fddf954f2539e5e557753bd546f69ee375a5de29" + }, + { + "in": "3139840b8ad4bcd39092916fd9d01798ff5aa1e48f34702c72dfe74b12e98a114e318cdd2d47a9c320fff908a8dbc2a5b1d87267c8e983829861a567558b37b292d4575e200de9f1de45755faff9efae34964e4336c259f1e66599a7c904ec02539f1a8eab8706e0b4f48f72fec2794909ee4a7b092d6061c74481c9e21b9332dc7c6e482d7f9cc3210b38a6f88f7918c2d8c55e64a428ce2b68fd07ab572a8b0a2388664f99489f04eb54df1376271810e0e7bce396f52807710e0dea94eb49f4b367271260c3456b9818fc7a72234e6bf2205ff6a36546205015ebd7d8c2527aa430f58e0e8ac97a7b6b793cd403d517d66295f37a34d0b7d2fa7bc345ac04ca1e266480deec39f5c88641c9dc0bd1358158fdecdd96685bbbb5c1fe5ea89d2cb4a9d5d12bb8c893281ff38e87d6b4841f0650092d447e013f20ea934e18", + "out": "59e904b2aa0ccbf2a9d127446f113b7cc3d07b970e07a322325ecee66ae0c9ca" + }, + { + "in": "023d91ac532601c7ca3942d62827566d9268bb4276fcaa1ae927693a6961652676dba09219a01b3d5adfa12547a946e78f3c5c62dd880b02d2eeeb4b96636529c6b01120b23efc49ccfb36b8497cd19767b53710a636683bc5e0e5c9534cfc004691e87d1bee39b86b953572927bd668620eab87836d9f3f8f28ace41150776c0bc6657178ebf297fe1f7214edd9f215ffb491b681b06ac2032d35e6fdf832a8b06056da70d77f1e9b4d26ae712d8523c86f79250718405f91b0a87c725f2d3f52088965f887d8cf87206dfde422386e58edda34dde2783b3049b86917b4628027a05d4d1f429d2b49c4b1c898dddcb82f343e145596de11a54182f39f4718ecae8f506bd9739f5cd5d5686d7fefc834514cd1b2c91c33b381b45e2e5335d7a8720a8f17afc8c2cb2bd88b14aa2dca099b00aa575d0a0ccf099cdec4870fb710d2680e60c48bfc291ff0cef2eebf9b36902e9fba8c889bf6b4b9f5ce53a19b0d9399cd19d61bd08c0c2ec25e099959848e6a550ca7137b63f43138d7b651", + "out": "6c2a841318066b90a9604d0c8eccb2986b84a0c8675cd243e96957d26e9c1cfd" + }, + { + "in": "20ff454369a5d05b81a78f3db05819fea9b08c2384f75cb0ab6aa115dd690da3131874a1ca8f708ad1519ea952c1e249cb540d196392c79e87755424fee7c890808c562722359eea52e8a12fbbb969dd7961d2ba52037493755a5fa04f0d50a1aa26c9b44148c0d3b94d1c4a59a31aca15ae8bd44acb7833d8e91c4b86fa3135a423387b8151b4133ed23f6d7187b50ec2204ad901ad74d396e44274e0ecafaae17b3b9085e22260b35ca53b15cc52abba758af6798fbd04eceeced648f3af4fdb3ded7557a9a5cfb7382612a8a8f3f45947d1a29ce29072928ec193ca25d51071bd5e1984ecf402f306ea762f0f25282f5296d997658be3f983696ffa6d095c6369b4daf79e9a5d3136229128f8eb63c12b9e9fa78aff7a3e9e19a62022493cd136defbb5bb7ba1b938f367fd2f63eb5ca76c0b0ff21b9e36c3f07230cf3c3074e5da587040a76975d7e39f4494ace5486fcbf380ab7558c4fe89656335b82e4db8659509eab46a19613126e594042732dd4c411f41aa8cdeac71c0fb40a94e6da558c05e77b6182806f26d9afdf3da00c69419222c8186a6efad600b410e6ce2f2a797e49dc1f135319801fa6f396b06f975e2a190a023e474b618e7", + "out": "0ec8d9d20ddf0a7b0251e941a7261b557507ff6287b504362a8f1734c5a91012" + }, + { + "in": "4fbdc596508d24a2a0010e140980b809fb9c6d55ec75125891dd985d37665bd80f9beb6a50207588abf3ceee8c77cd8a5ad48a9e0aa074ed388738362496d2fb2c87543bb3349ea64997ce3e7b424ea92d122f57dbb0855a803058437fe08afb0c8b5e7179b9044bbf4d81a7163b3139e30888b536b0f957eff99a7162f4ca5aa756a4a982dfadbf31ef255083c4b5c6c1b99a107d7d3afffdb89147c2cc4c9a2643f478e5e2d393aea37b4c7cb4b5e97dadcf16b6b50aae0f3b549ece47746db6ce6f67dd4406cd4e75595d5103d13f9dfa79372924d328f8dd1fcbeb5a8e2e8bf4c76de08e3fc46aa021f989c49329c7acac5a688556d7bcbcb2a5d4be69d3284e9c40ec4838ee8592120ce20a0b635ecadaa84fd5690509f54f77e35a417c584648bc9839b974e07bfab0038e90295d0b13902530a830d1c2bdd53f1f9c9faed43ca4eed0a8dd761bc7edbdda28a287c60cd42af5f9c758e5c7250231c09a582563689afc65e2b79a7a2b68200667752e9101746f03184e2399e4ed8835cb8e9ae90e296af220ae234259fe0bd0bcc60f7a4a5ff3f70c5ed4de9c8c519a10e962f673c82c5e9351786a8a3bfd570031857bd4c87f4fca31ed4d50e14f2107da02cb5058700b74ea241a8b41d78461658f1b2b90bfd84a4c2c9d6543861ab3c56451757dcfb9ba60333488dbdd02d601b41aae317ca7474eb6e6dd", + "out": "0ea33e2e34f572440640244c7f1f5f04697ce97139bda72a6558d8663c02b388" + }, + { + "in": "d1890b4704e169c28e44ddf62a1091450404910539fc2daeb26e8acf4533b024e5215c2d02820dd8fb2cfc1743955cbacff0f8f35dfbb5e3f942f36247f68211d518f3f601aae12a1cdc000bab43d4c973f287e80741dd1fcf6c34f2e6b4b6c313d01c4ff3cbf9166f26946f18ef2d58271ba9233f09a6b77bfd4f48b36eb3d73d1133c4f842a7dc3907f680b0b773242c11e3dd973a44327ea7cea9c0f8e07d682b6651e506b587559fe01ed721000baf570a16fbdd9ea29fa3def4be912058321a8b720c5c102e48a6e7ed6f8838d400dd57d06eedbcd15323f86d855c94b21e41b14ec9e1bbc8019211fd88138c91f9abbd9bb3914d26c1ddc21673d2d51263b39d66e741d924cf2b192c5d2c1a140126a3d64a2c77be6c2c6ebe8599978ae90bd36cbb9af64d078910c4094ab3bf399c34f2ab8ef843e9fe1bf88bf443ba21e4377e5f49c07fd9653b526e14562237f02d11b904bca6ac31ae721a43e3c4910a24af6f4d80c031c109fc0fe49f15274bca92bda04c3b4196c192f6ce489c63a806acfc895ab52cad657c1783b528e12d0ed856e1f8fc91f2aafdfa0a92498d68530772ee73b359fcf1418d1096c46b34dcf90e5b468bbb2970becbd70089cfb039d64cc50fff5eef26384d34f24515a6558b06a1fdd88f1050c5bd78cc6ed83d4c2b0e882aebcf84afb0430d0bf09f2fb42b8b4589158093a7709aae75a790910e211ee1333ffb6fd80778da3bf73858978e9dd647978841b18001dbaaea43ca0c0a03dbb9bcf30ce76a6f4b2cf2a9b6531b3e4051e7e05090cd421bc66c4731e7122ad129fc42dedc83bb460e3f889992fbd3ca072686e56b72c720fbc98d723ef7f247286f77ccddc728738e941b1a74d4f16671c21fdd5643a115ddbcb88ee7ec67ea66fd2bce718df6e085d4b5fc71a72696636a8f7b3a68afa51a896771faaa7f1f827430ac5e8089dbc0d4175e1b22a057bc5f1724eadc1a41e78fa3acaa8b97e5f2e19ef9d59ae12b04e7f0e8a621e098a66910e2a5ed2102b824cd3ea044a854f1cd0b33e61e7f737414b2953549f25dd34d19aa1981de7cd5649ff6c6364a4f25312ef62395a747ab88aad722c05aec40deea8eee5e779ef458a68840bc6bd5d29ad40f98b3ae010b6213372abb7bb8b8", + "out": "c7490b05b172a1dbaaa66faa823108d44c82f1e82a41fd57fd95000f30de747e" + }, + { + "in": "4fa3df1dea75ad4b9c379206a95fed930000482e5b683fd2b17dc8e7d5c4bc1b73186ccc13c9ff2dd09fc1d4f68034d120e84ca73a00b71a3b46d1efc6ff88cf2eda65810b098cc5e651d9cf064e87076d5a871849f3b405d3d58ef5b1f10520a9fb4fc84a81a87b13dbfbf9d8674943e28c257e46d8ad7be1785f1dc7c9b1bd574ad1dda48f0255c853d2490bd3d63da22a8369cfd02594999a2ef443308fb8298266a11efa177102c75dc674e89fc9dcc1a0d3c863bc26141102175d2678eb6e13d90bbd9a5eb89ae8c0cb47d7f340d3d32042a2762bc9bf2b40eb40e87fb42610fe7e357051f01494704fbff73321b47301a0799b7ee3fe5e62200f397a61ed4509a62f7106ed0efb0abd6ae9e4a1fe9b02c092dcdc75015cf602f3b9a8988b609e6c0d1c5c3e219ff57875c2ef01615f89447ea602dfc94eec17a398c014bd346691fe209a002771dc8164422cd166afb457a8b3071282178a3ebd201d9b07b27e711e7ee7d33aa5210ed4e4e92486775d14a6ced092e34a7ac82670939948fec149f9c018fcaad3fc597d315713f44fc5e1725f448ecaed40e8d841bd02f1e81c019b08f99412e360c0bd378391c67d964b47f50c26f0a483ed664023616b0fc9afe43620dbe9ccfe070ef295c049eac754c2123130c6b2c0232f6403aa7f0dc35a5999bf95d34ad612234c6289277adb60e4f72ec2df570f05395b3be8a0a3c78b732821aa08927c524e15d65f66a3db8c1c96fb70bc0686aac310051f469fc5ef880c0f66947c1c328f97684ea24cbe63baed8d114f40507c2901034e6ab3893f366d53f1cfca309309218cabceca4722fa9ccbc7249b87c12ff8397f40487eb00082e7f551d27e301c3bc7b5389f7042534bf7e692dfea4da24f7c34b8d2ff145f54b517fc97134ec5ac2cb925c508d7a6bd01fe7b764648274972bf08560d30802e0eb7edcc57af4797bbf92e8688268606b0f1bc901fcc22136281665ec16393fa9601c4fbdb18cd1d1ee382bc07973903e91ffa87399d1141d49f4f0c064acf3ac9897891df10bca0116f2c3fef180fe6a8e937c478f2ef293ae9186dcb1f76b6e48101df64e57ea7c64c5c0025e221c8f5cba5cc92d9cec628140996b26d17f439b780f59a999301122f82d0495f8ab5ae1ea5790f45e992dfe00d5f82a7ff1354aefdcefc0d2d1731d22fa2b75afd4fda25ab194055fa9628381055247c8c7587d22e73c60136c4282452d47ae03aa035febc26fccd42a1cb79cf866db6418a49fd8261e877ddbb839cc39514ddb87a8a40d795532626fea4a4c35d13e028f9ed1bc09b06be999b8ddd2258aa0596bcbbf72af67e10bedd58d599b8d577a583d676bf5561f80ce5e9528729a92df578fe75dbc70474b75747a8d55de70e57bdd62d4344dc2115ed4dd62f1fc98bfa1e7421fc0700025c46d0ed1bef35c3b778563211b9fa9e8ba4bbcbf01c2fb626ab7ef325ce9f468df2cacdb178d36557cd85d542c067c289e926c1ea2f20abd329e984168bb6def1ddccf214dcb6a53afd462f0e7e7a19e8c88f049244125a6d7dd41e58bc9b2ff7fa2478df76af73090cb1ab59e388ba20e2c297c967737a1af61793b68ecd7439444c48e28e2d09c48fada5e0d1d15e5b340a52f8b3b854cca479f0a598445e14f53b3ba36891050c79673df3e2b5825c955a29e5c9a22f3991d0aa785718cfea1d2385f8e47e4a75acbc7988d0558d541d71c4e6c5f1cb15b60cea0c34a67bbce105d7a896025e0254de7d7af724c9027d44b8642192a08ab8e1ef3046dda6014df7f4c9e63c635e48ab2e70b640d480998ec9357e665f99d76fe5529ef23c1bdfe017c3a66cd4eb2ddb42ef85ea0cd65534", + "out": "eb351838fe8225fbebea9168dbb708872ca43ac93480c3affbe6cc3a15a2263c" + }, + { + "in": "523de8b1f4cbb65e81ff0b6ccd6eb8ef0a0f0a691acaf4a77f25acd2d66ad4b3efd25be70308853c094412a518a32020e3020a9f6ab32f0cd60ec0d7a194917d6c457b168a54a4b46f7b0d0c71bd61cd202f4c718776a701e0770b0efa05418770f98e4e79cd066366fb3300e8be359a98b82b764bc2fbbf59c7e8f94a157b01c6c7577b1428138cd422bc47330f8ed99f4c0aab0d984287445539839389ee08d6345108af26aded0ec1d7be774cfb8c5205dfe07cf6caf8c1afe37c7a2e4fe6013b93eb2463de4e0971c7178d6a76b16a0e8960c984ce8bbe71b3b466edf0445b835f09414d01f14c7b6167ff78ff118127bbd5f812c27facd57b3b120e2bcfe87315c7a92b82ef5d50ca14a7174d1bea7e056523e055a6ae42ea3765094e5544e5ed003c989c2f98f38a17e3dda74dbaf9c669a319638a2698b0e4a611480d8ad3cf016792ecd1034925f42b9811a7214d623d047abca31997ddeb03275f80dd21f40ddc80616e7ad3d481e8ebc0a1a6a398e16a78369215541ed10b75671adeb1aae6e11142a1cf665fc1b7332dfbb0e10c21a2b48f78e57319ac9c58dfa8b1c2548e2979ef1accfeb215afcd6c2c1b46fe97dd491758378330effc7283661d2cb84fa05281e9e517408508d24d042e7b9bcd34db87ce972e4cbcdb98615fb93093369dfedc782f44bcd03e81cf93051318b2401ff29f753a264bda65af199e3fcbb8b5d39c838a67d6c7a3db046dc56c323ddbb5340cbc229e47cff8c9d29b7a49ac0ec8c1440ae498c7d150ef91c29bea7df3efcc2871a13a1d72d139cb4603d9fffe85f6ddd544850ef63c3944fb35dbc00d4308ceaa6394b6e23f650d323f8f7ef50ddb68f1486eabf989bf44451f620ec9485c0b52d1415d3c909a2cfbe9d77db19d069d33baee4d77292e63fcbf65c1eba24bffddefe95211ef0aaf8abfda9f94445e582976f986f5382cb669506af2b4a5a0c43000a3c72c5ca4aacdc9d3d39fc5c492a393b6c341b86dacbbf6ba8b465100cc683edb2d9b9f83edf9c6a32645f51cc79adc22a52a007baaca618be35e356d1fd1cfbda73f1ed09253039def609450fd2d5943b9cd49cbd52a318ee3510d7cf3fd8fb388ac6cb9c6eefef3d3cad8501b91cc04a888d33e16d6a4c9666f5f5f3b257193f2b46dedde11842909d8c48ade57775b0b272e2dc9cef1a083eb2ce58f4d1f211922fd6aded1b82fe6f5b11251cd396e5a3666ed9626036e4e356231c146bba0a91afd3648eb7bfe0b9c14f15af2f92309826f468945cad0ac422de3d6a773b76178422107ce0270e7f580b5cceba82ca0184aafa8341141e65e39859885768fbc5ce63b965a0604b659e71d9da2c7a43646088d8071d76926163aafc69e25355bb0a222b7b2da9f0a20c021adc462e905a9c3bf31c16d87fbec3f014f3957a720f1432e1741553092052fb58a198640479abcaa51b104cc93e2636e1460643ea812bd44e819c2166eb6b349ba5bdebad59078910b5c22a56f004b8d9e4b1224d8d204b48abe7355548a402736c5cb110f3a1476ed631ff168f4f3efd89b38de4751536548647523d334fad7cc2d142973f2db3c1fe08fc5cf83f9f2bd2daa524b37864816af29ee05951fa09d1c51d9d14ee4f72fd7bbf18b1a724ff5a0958a063947c430142ad2356e4400aaeca442e163372a8f1cd36e2db988e7781165e5d4e7074ace40858e8370e883694af09977704347fb735c8717c42bc4eeeb2aaa50dfe637c640909ce379bfb9e2608f88751377038d1669f248178ad580a908d7a1b8dcc7e53e01801f1e485b5893f103f03e0f53b2b1440be95644d85aa7f6eb7edfbb46652196695ea23c08573397b111ff909025e20c5201293b4d223bf7aa01de7cb28b94714370434b9588097e2401b62c7a0def1fbf89809e810749fd3ce9ec3c07ce4bf4c43dc966429b2beb4d711fc6c448a12097b36f1e6817eaf4937a983f85d9cf3e62cc1b2ac6ae1ec9eaa8cd8ee2c3322239cfe5db3d4e8786282e630a7d259c2fefeca03031c960a66a71e436a3ed6f2f3cfab4bd77c660d14205abf606fe561a346f7d849b69475ac9f6822d80b9a2e56d5d495e4b309b0ea963c9fc5c7ef94b217ee5337989afbc7107d233a8b362ac27c4f69df9e191cd65ae97d6eb9e5484eb6f10349575e4cae51452380151f902415ac9cf42c824eb23c9541d2da1c26db85f53cdafb06a12b8393cd580a8e494edb6710c720dcae30832967e33e6303a92b1df0841d7724284ffd2e00b95c6d623b168d21ac1bd3c675eda33182a2c22370998de1e5eb905372cc6ef32d5b765f5c94870df4842d011603be4cdb1c227e41eb2f2e8542cd325884fedc9c5c7bb07a92d20d64b836215c59f162a3da8bb67d6fc13fef97cab6ecb8a29e431a6519a6261c4521ccb90e6e609869e6fe398404ae047f64ec4263566defee66329dd40ac985eb8a08d26529a544891b6f57cc235c63c09057ab6b6ed720ef41a3c9ae65768b43f6dcf4962a103dd93c213171dc2c9194e43265c689b49331450281a3febc618d1aa4d65a135137051fd46b568ce294c89", + "out": "96baee8eca9dcdbdc467549e307d95c20a07feb72eccc780dbc29d40ae7cae45" + }, + { + "in": "f5080d4c59e804bf8f34b334cabbcc7d32011bde3677f4b9069416ac204114cd9da7a0ed0f4b4d8344416336eec15553ef526b6dec267b1242657dd0b508af81fecf9cff9c82a6a7a9539814dd7e097615ef15373836b5d2f765cc8d5f82e90449f13aa741d5ee2fe63898e55acd85116846807606fe1e2e29f98f9940b067d0d1df01f080211b2ee4b0a30803782a7bc2eafdc5ebdba91eb05f7d7dc8e34bf6d44fec05824f53418f235fb64e899ee147bcb403c8855e94af378d182d79c3eaf977cb4e9d4a16d990a6c388ceb567b97785e6f2bc6745102b99ae765e960b6b32baf01e2379cd6ecb74d3e1a56552f5976dfe5c742bc92be596ca742ffc3d0fa032ac29f9f7c1a5c43bcca62df7d9de35d0c7c179db2e1aa255cedcca55064c2049fee1af2ce5ef696ed4bc46b7c55bdd51f2d44c8713fb2475c0b85246ac0103cc3863b7eb026ae076a600313f6fb40a4df62a2af81b7e917951ea870ecb31b3401928b5046d9a1e62d14b30fdebaf262868517318fe17ec3c0d52524f44120ed8ed3ba70c643300cd0bc70da72c964a88f52c3a91ec20bfeb5caefcd4d9c7685d8407476b5f34676c5ebd1e88a6cff1c625322f8cd59b9ed60cefb21f9491b95e72791f7ac7eaa3c16159fe9df7a989add6c2282c47585e11397eda9f47df2b40166e03bcdd6186b46c6835118268ddbef19a28bbade1bde0228ffd7e8b3c3c598d89e24b8cdee79c940254de26cc6814ba2722e42f7571600b7325e1ff300251d52a895b8ccbd049b2953b8d231445f68f7c26ec25a4b8695c8ac116f736be939edd762c9b4743e463c9b9b2f88e0bc0ce78781cddc3bca825acd463c7cac2aa6c430bbe820ea94af9a40b1b5c006e9641a2ffa6e427379e1ad49c81b98320b3431ff0030dc683d61026438bc6a6d34b2c73704d9f62eaeb13abb3e4b0562b4e0482cd6b2d7aebc0367ea29a88f4a76f3d76fa1197e1dca92c8216c84c1af9b8c78c9e3a7799a4a79a783033b0f5547e8e75e69cf3615ab04ef989fe1a463b1672c571d50ab56972896e8a50c242f22c7f6e27ca4ca793f627e79608680f5421b28bdd2589f05e65430df774ee873fcd1234064f7a33cf5a1fa4e368137ff9c1597f1fa0fa36493f20538077669eadfd3b06f788c912c715fb5d334db6bed133a8fdc40f5496e66ad63881f0ba3727416715865253dc5290327b515bf68da188dd5b4b0eac7ca712cafa8fcae0c5503fe58a219182f1c30da6d0c19cfee897b7d837c97996a35f4ca8cf0537a01d17e7de0cc9c129e4da0adaf1fda85030df9127be628263b0624f372c47c3ac87eb945a57f5c732beee81a7403001798992f3dc944114ff3d54c4666ac5ac8c98d0d5596cbdeb420665f5edaae747d54cf7edd37b162e372249d135938cf17d174d12d88279cb4c32bd6f018c766da6983d4ea51d6bd8ff0a9b34e9a93bbda70cf1b4b867d60a74811fd98d52faa559b52c755cb70a76c94bd19654cae7017ccd70222bf08c5d7ad1f5e4e6344fdb3abe703452c29a696f39f9826ed8bc510a4a148e5bf8a5dbe6b82d7220164f08011c05ac5159d52ce9d45d758b645bbb248c2d341dbefa1f8602c5d458a64f38f3b04db39089807b6a10e1bb52770b92ce72e2d3bb0c2241cded35054b84558d1cc099ef7b2296951951d5b6a22f93bf962ac5ef8fb55ec6cc2b316428edf12078ed1b66d525d022819cbd489e1bedb02ffbd507d55f9b5d4e22f6396ea233453754688d20151a09c70044b8a5a9ac033c3c3b847ad833d5c05b33407666ee82f9581df9034ee15a9ca67d52f1d9b634b84c1b8ba9e515f1f060a5ac5cbae2de75f94e112f7198e239df08d3103f065627438995026df511c6e5bfdeee5667d511d4181850c7c5d179107c1b86d24d5532a88a4149a2810dcae73731b0e1247281a6fd31613df6891b4c17b7a6a9ad9b77468254b93f85958aa0f01cefc10b25169dc46e035d3f24557b4bf0e7d60174219108d916ffdc55e25bffd9809efd058e12c14f39c69d8fb73d3ec6458f47f2f8db901ba76c86550b11b54d0641d4db3eb000057dd00f2e511fb7a47e959a4402a3ac5462234b40b184020fcf7a0396c4d00a987c8741a4537bc17102a5c42afeab9f71ea66ed4cbc7b5ee682ff04f56f4ba1ea0bb326c4089930f9e3f3ffa3e06637cce32113881a06cc3a13837448145c2bd01307a580fdbc385d8f46fb92ffedbc8918d269dd1871164d4b3e2023441ec8b99c82a5f09821cddf6b38c9acc3bf3a38d5628016159588c33eaa29d9463a537c000a16ad8c177dc4cf716e625f46fc4ca8c19fbd8ef320f1d680639195c8b195b0a02738e0665f4190d6287e589cd6dd45b9e8cc23b08e1681bfc6f66b88de6b091e825ea4bbfbd697e10bc407570ae4f2a3ebe569554639c2b8e051656cc30c837f5a92260ead1d552b45801b6d28134166796c87f900225cfdc3cc49d72dfbc18d8d95b1e160ed3cafd5c3467d48aff87402cbcb1e1420e3fcb588aa19c8f42753b59db6fb6a9fdba127ca806dba7dd97f2488fc2e438eef57a4cc85b88dcfde76ae1ff61225a1ca8bf4a14f729950322ea681b16d6492902506702dc8f348e4d3ae7fb55fac1231fde82091b34f1791b6ae37587b10325f6ff5e23b855845b86eae90785b9d10d90a16644d01bb626f343b908a9591f4069b21822ca4ecf985c1e710475f33df9af4764cfb0ffe649063775338f15bea7cff29f164678160960a80ed148c9b7faa58e9139911d3dd9536f69646f718f083dc9029d6294fc4c607688aa75af350ac2c0b001a157d023d73d86ed8133809fcb9592d12089cbd7a1bb6bba882fe227c09a53ff088907cb4bc2fb4b7f62d41d3d397c4fe0ad12bb3964370e21712951c679814d506e738c0201e42181d231136a435ae0397b61ccbc5e8bbebf8ea77c8bc48bd6211f29248f9d498d818e2b544d28a5e60ba727f32ef4ba2707962230c900076fb764d0ed5ce078c9db14de894bbb836c6de9e83202ae89f9a8d8cb0341e1c81b5fa8b16731b8e231e969c0f1ef95336d4e73ead6da23de3ad1eb608acce4d4d93996dd76ec1f5f2c576f6b3b76e07bd8a810ff5d88b00ffe48c42700b61cc499336e7fb57ad72ff44fc631c7222c9a3d1abf6e77b5ed7fe2f7228fed6c849bf7142c4103989a80f7c15642ae61650cdca7e854eb25e9e72f4c3e3768e6ccc8bfd556b56d3507edde9e5c331ddea75568b07813d20e8f4c9547838ed28448f2e67158acf0c00b131473847816c5e2dc215", + "out": "6c8723da27cd98e20f2583d01868051ef2dafa645901f82c74d5320eafc18aac" + }, + { + "in": "08944cb473b828b118a31986db67fc757f238182e790553404b792aa4f0095a6a83291e287cdd16521a3ae8c48f56fbc909dfccfaa7bcc570c2159f26592dcd6b15bc4dd55cc05595ac634b2c3de15360b0f07a03b5957bc9333cc5097919399dd9973ace15e55940178c4c96bb5e0a0a10bae175769548ebce11e0d7d9db29647f197d4b87f7039f5d4e59e016531dbebf55a797ac9a6835032cdf34240a7ee7423e89c09124829cafc5f89431c8afc54fd979e50d48a82b47a53523c84b6004daa323efb708203e5388a6a5110c6ce2e341048a65fdeadeb3837a03420f9faddc3f02a544f1e46d96b07c90c7971a7040a179e8198e90aa019268e00367120d5f3d98a5cce82c885e77144b1aad66ee682847776b04f01f501dcbefe3903080a8058b3b8f1d823d917ecf31fc2d5b0795bf95a55c7093eca7c801dd0bd0dbdbede7d56513128b29fc0b4d25a6240b24c99e017bdff7acafc8f8de9faf5a2944384aece82bea04dccc6d51fc6e6f27aa38f131b7959b13681a09b311d242e6222a1ce5687de5c080508b1db16b6f8290d33a3cc0d0138ac61fd9093825e9d3752889e9f20db9f80f92750eac88b38ac81c0016d40371eab4a87e845e91446b0a07081b84f559cdb95340cb020af22aea1bff2fda12f7a42973ff163a1c6f33db8b8214ae27abdf1c54f5b03e29310fa210125e1296e8af93a2996dbaefbadd4c51c2c3b8a3e2bc9fe060c42ba32768f6992a99599206cd2291ccc5bbd50856f7f8d2d0ae1efb5892c15a799b77482de4553736b162abb06631f1688f6746e7d7a37ee7ef24e6cc901175f04960c01990178f81e957e941deaac8846b3704e24204f43ddb0765c433f3f7d4d201459cd65682b7ddf3d47e95cdb31b96a4cb22907f08ba6e92a4a07703b2dcf150f922c4b7cf181380303fb72547847305999c3c8f9ac877d05d9dc4159deb8a13d36ad1d533a56950e20f906d29d51ddc45bd15c1773991707480e37b827044bdc6473181b760a9036e0d3fa491c2f08c55130d8cdd5ac8e97d0813164af3d28a585f0c2ec7004d498f95c6b62231a632a56c2d0c48fc3a6992d4051957b9ed6d9a86dbccd962a8883cf82caf01da2f51a203d56b6089bc8fd0b1bd414c8063031ed469555e22ef872689c130b1c101034d572fd8cd0eddabec9ef1503d7f728b0941efe2b9512438c7ddb176be2ec2d9ffcd56495a4511428df02819cdda18d1ed5d3b16c6f42aa0ac681a9fab51e8a1a856c15c51a3ec1031427142ea12543014dd4acac640b8a7729e63ab7df1051112cdefd4b988a2258334fa9a7f5b3a87a02074b9f69dd81b83fc74089a91d76aa4041259e80fa255f2084902aeb9e996ac2288ab464bdec47aab26a28a2a8194989755d48fc9a5c9279285f2f1dbb8b8018f3e4e13115d78a879792e45a8f4f24ed4a317440ba63e6929056efc1d2529b75a709d6c0097dc2d97f646f334ebe6195ec5630132fde58e25dbc17dad822d9fa0938a2a2c926b105d108403dc29cf371c3504ff73bce9c7acf9a74c4954ce6a32da96b21cf3211b3e49953dab78c49c3e532a349003c59c62f7d40261cba63a9ea21c89a38aa63ce431c43ae261c4d9999b1caf491fab8e7be6e8c3454f1be8793b2d27141fc107da599a4694c41353d7785c05b5e31440458d17c6db66feb8a9c5c073fb946a67ac0312bb669d9b12fabaa5272ca6631379ef4ed420a4424a5cd08526384c047c33a84d5d7dc0c2153663b54c73dd799a3568c01b818992cdf8143f1dadd6b50cae6eae13ac66f31ffa2b362cc4d2880592b7fee4b9e4cd6aa5e5de27aab9b5dad9f7d39407ae927530cab2b61cd7394a21ef47bfb813b5ea6091458d239664923280ed0d5cca8285bb2281a2f9fb3ffecc8e9147e1e8fac957d90c9e5f513738745a47c2ad0c31fd8986ef3b6388c6e821f166513811d547ab4336b5e04643497fc9f8d6e380ef6478b82b6e2f5f65dd98a63c68c32b94610e1d3b9538f13a7688fbb1ec3448be9bd77bb93a34546172ae8d614f85228988e7feb18c9a0c9827699e8b3cbc69750bdfecda8268f694f4c509befc1a1166f85c829725299d173f867a300987a2d36d1bbbe37be3208fb8efe9152a41a5f0e931b6382ff7f9b18937958fb180e61f2a8c28f36c3c80c3722935aacb81c24aa17fb3e7a1026f7031a7449818ed62ba7705ca27c2d3268f90b6322921683dff800a306cfc186cf2a61b37f35837b217e3b2cecb0843d84eac67431e3d689f01522d4a4c73618b7c2965c9dabb15c0be637d10ceef72271cf39a7b803b41767bc34433c3e6ff449a439ae13da1eafa038cb9f2e1c84f1ce39c05df56fe3d7b82386c4e628b6e27cbc5d575c66ada3510c246bd04db48f4afc2d7352966da2266c2bc9831532f53655d8be42b421ac0d70d8ad1d3587257886dbf93668e907e861ba64f45999badb0f766eadce5238b5ed397f265935194812c03c5769137bac97140525303cf48d65f39004a3f59b1fab09895cee05335d15b9b12265892f4abb92ab1dd2002ed00cf3562cb67dfe1055968e4ab3306bb34bb87d0f64b26848812a2f7b50424a21ff94081a7f70f7b684ab0f092b2b085dcf84ca38414cf7290f607bf79c37ea84253abca8d4184d2dbe2e900200b81479e1ce8b71dcf2bd6e3c557a8e431d627ba669c2ea03068e0f7ea62c29777b22142d7a1d451bd541ef8ebddbba4e3bd8ffcd340e935be7c66efc14a13ea48134f655b0de3180101f09d204c379743a357e6df1268b55a9f7524398ecf3a59849a27b142239059998083e8fa91785e91c4d220b2fb17e3389ebaa384a49d89b5d78136dd2454f06cde9837f096b744d53221127869904ac227cdf30bfea78cc5545583f999b9c42a1184e2fb9ff3ec095b9da0d138205c4eac4c8c480c43153608849f63e161135c79d8b6c9cfe9b8dfd8afab559d8b595ddd43835033b4bbd391e028bb2a60832d9b697ee61408f149744dce71aa11bb2b0436c1e2626ac3a27cda293366b90b9cde2d927855130758d3946b867192dcf3fce9a3b9a5276e8c37b8cb136fc90a6dc22650f95e796a9886efd3f424be63a66dbb1041cb3d4a06f4e7eee89f0b6d15c36f9ea010c66b332011c8888e8e4ab2b3ab5223191e1388613a0fd0f07c1b26d7cc7cdf1ac62a226454d6291b431cc3ef2db2b2442b37defb942117fa247096beae598611b8104f37bebedd8bb8b949a89b5bf8e228eca1d8f16bfec75a02ffbb4eee3a6d4a6087c43634d675311e72a9f3253bb5dd364e07eb4b9c84f586ba267baffaefec79e03b83b18595fe06d7e063ee604ff287004d141c1a43af0ca7c5651d98f633fa875b4743353fb07bde59b6567ae25f7095f1d9edf30570e2f7d7ec194216898d910f9e295a41dfee072cb56f914bb78cc9854129250f9874b63bb3ebe9a1cdc6ebcb0916e1c440354ded6aa818f2811da913912a21d3961ac94a39f0827d3a419616905dc45842c8e69a43004b8ae922c8de1e8cd0668674a7760153213835bc63fae4f8d65614afd74a34d42abad5025b884b34639340b45d49cced423771916e18aa077291923017ca50795f3b7a3f349a3d29923833ce57801c631576e23b838a7767ca1bda92b82ac502db3688ffc83c09a4e40cac31d20d9d32fa6724a80be7091cde9c7a6560cfb326b467caddb9e9b7a491eda283efb0b61b4a1116dd859d5c0897eaa2a3fb2cd82ffb33770bf9e08091363b6b81d23e61c2a647d2be440c5c79ea89690656d9f10b1f07942834e1cb6e2d2df106eb6d6a21fa23819e65028515e88bd279f9317beaffd394ea51f8639371c3a89f11305a4ca35fb0711f5e2c7c3dd1659c790245812113204b4ed8aae9ff09d43c6ddb13f5070d98831b2c7639fb6b9b01c288812ddfa8861db32dc8268c07d30cf969953042b3dad530d9d744c06aabe7a886c0fe57b09b7f42d193fb3e9c06329818251a2f7e6474462c95ded", + "out": "3d20746dda871a104202ec1ad7d60d8e165fbf97afc1929511e41781a35cba45" + }, + { + "in": "1a3deafce70af6f3f55d66ad9ce78d5f4d5c5f2638a810afcd07d67e9f9a1380d6b34be482ef030c22f1e978f544609cce35a74c5109ee7038495b6210cdbca8dc82c6e9e7b0d593fad9665382b3c401ab8941df71307dd77ebaf140aa66a1f76316478850e58886a9610631e9c722f459fa00c0b53124fb4f12778bbba3760826d3dba67cd030a96b654af93f8e395f5f439549489f8161683f124bc980e6939c83a6085e4b6caaf8bcd89a0e01ed70db487166cc29735d9235a9cdc57b80c9c2e591df6322f5bedd32937073f781a30389552ae83fbe147d1b3d3461a3df96c15cd96900c56718eaae838417057579115936862679f5f2a45dadf65d14108af1641df987b57986384fa1433789f5dfbe87e90bd4e9d8d4d0741fcda7348322b967b566b18612dbb8fe64f151947c3f7e361ee868676bccd0cb3a1afe046be70057a05add3e65af31e3ff414a627c0183e8ff583b41b75b203650420216e6dfcab289665f054cfe3ea0943647528518573bbb1d0f27e1449e98739eaf0d009432df0c1edc1625264b94a71db762659ff5a3a7a867f182d1f1fd34b341a4a181221870dc4a494013091a7e3b2b07e0160c438f1ee1e8a2b989c4ffec36b5083ea427606767c29672f44779a8505b422b25a56907f565b27690d011426a62df0036d57d967cd1d14e915bbc2691e7af818c769d9e1f9edd40894be88fa1d7a5952afd898e837716acd73953bf2d1d448123fd1a0276d2c2ebdc760a4b787416cfae5e963fcbdc8b551cb70f91da0ed4a8090fef178e879f2c34f3a269dffff09772d44a13d7a17468b5834c460957d5243c325f63f05f89d4ed98d361e7f0ab8a83948a64d0cff8514841aa21c7f337920a9983e59be4a0f1339e1e462f92dc1fc070126206012458a499a8111fae078e00b0ca3bc1d6c7087cd318d5603c1c7e0425e6f729ceeca5f35b82f8a42e0e9b950efb0904c5fb5c06d91d239913665ed1f1ee4b82185a01ba86ca2d3ea94e5a8842231a94c05280183b7aca289984103f122203ec2fba4a382e6f5236d6f68da05e3bb0c558421f0efab91dceef6d1ecdc60f9b88f8befe31cdc3c2f024a1af2c7336aa5d151e8cda814a5fe898badeb9dd680e337e682ebc22bfae445417e37d2d89a338659a280ab1206db74dd42c6f25639c1803bfdf2156df613b0f5924d209f7f9003ce8794f989f4f27b82121210f4f65ec5a1f7723305cee438c41f793ee04496bbe337bbd2fd3023830b1c8889c6f4d0c1192e364edbe1cd987ba5d66224ee9c9405e1dfcec0eeffc5c73d3123f6731c6295d1e6b854b884fd22b6a3bbbe5395312585cd138bca67532c6ab71bebc6657c50da87d2ac6068fa3970202c5e15eb7b4b3d2676c0134bcf1eac2b26ba46930b5e660b16060894884c88bfacd6779276b86f685ab6f17c6d53f621275fad66d021d26d1d480afab4b5ec75e0e763ffc45f599ea02504da5d91eb5efc3e4ae196f219e45e7cb05594958c876ff474a020ef73c1f09b1f7f7457e816d3af51d86663d4d461754cd5e907456691e02446d6cacfd33516206a31870543d574592087773653d4086c2bdcbab3c9b65ca11ad0d4e58ddda8b440309989857103929549b7300ced42651d4086661694092c42875cb62858e6d1be5f7274b4bcd83aa4da05caca186a30902830790f9ffa24418e1f9db00fa40477e83b05c2d11ad7d81dddb1e31f94a9dd5e9e13391c22479b570976e3afc1be41086d3be6689d87ca4326a7cde8e5b396a678d3cdb2c80fecfba2bc799ae8b1528e96d880cd098dde910d097eaae660ad4d7ea51c18f18aa1b39614299a172512521dfd231b9840909839eb69c892ee23f1bceec1fadba75786c7ded93bc9983f74ceab397eb8ba84f7e4130b34258d628594a6f9e2348fd91ba2594e07b8057e8a2ae3adfea0ef919555385977041c5b6dc4f3880569171f7217aaa9a85f2f5bbdfe3ffdf79248f2a35fd4dec34980c67290339b1c0a5a6ab8838157ae2f5140b4a24924a6688ae5ce72a48103ee9029ce8a0f15b1fbb19a12faab80a7cd9c0e389fc2775833e3190f1cf735ecdfe7f6b6c326506aa82613cbeda8dd3691b81f4c1e3b0fc32d7e6719cbfc12f4a26e0fc29d6417953abc9568db4ed9a294b9fd5f2a666dda546aba301b1c60985033953efd6f4538333b5c7dd3148814a3fd7927c366f40b3d7abbdeb2332ddb586af80959097663cfab2feecad6d368ae10eff9663d5f8bab95935d25f45776f7f04b46817d05165a9dd4770509abb92f8b9e7373ca780703569981754a51d6d376d65c57f55cd70e2df5fdf5a6b829ae30ce3bf942815c8b4be858db58151d02a68aab9fd373e047efa51bd1a0cd1b61744d9e97ceba3334b3baafea3bc9e43ae097cf2c3d713eecc247ff43ec74d54907d8bf45e45b2e0e11d82b126a8179d3f66c055e11f69ea67aacc5fee8af01faa379e51998f5070f9ee0fd30a2eb22a925586fb1b39024eb5eb1e127c76a149e7f02af1b73c16e9e5a5dbe378e08a9fadf1194c625132ab3fdefe8fe9a89bb8e0035a1a3ac5278f5d3d0ade0e41c81c6853a41c4ac45be3f68180fe23f27f18be2e339de1d559d75de63adf7a32bae42b037aeaa3e123a5314891bcd35ca48d57df4c17540e97202a8ea1328da25b1fd6be2b56aec1e5deb209f3b7a13adb1cbe53eb645956e577a7621d74e42376d70bc5c4aacd239a852fbb7b3f62cf59fe10438c1dc8e1e46566325da0ca43aaa63fb7e0b450a2db3e3a2204704d894db24b72b3078106e096cd543dcf027650cb4965e38ac36a8ad588c5962b4e26548ab88f0bc20e10acc1c3fc00ef415b3c32499264552b14e2c0e789a3b8a8bff9620fd939d0b34e806177ec696a4b3b1ca4b32ba979b2690cfb3a6b17bcee6877ffce757e4116da01099ffe82add5a0c593e73449a96db9cc2b9e846d166b095174f2caf8b35dd878c836d9bb6eeeaf8e1bc5d0e149c739828cc480d731dc16b35b80d4ad82ed7d29bd05018239efecf8deae180c6a459dbcbfe4aab9a5e2c1e1bc31418cf2eeeb31fdf8ba02c9a91525e9163f672bae2edec38c1bdb84ea237b4ef86bf5c0f0ffe178e3761e82d94f66e5ea40ba8170bf768409e1b4177aafd9937bce3fbff590320d7c445372463fbbfb34f57447f42c16e026f179cbf82f617c86d1e8d42f6c908f9c6b77e38d25d51303dbd781ffab569b4cf31fd0b947c45e1768a2e9dfe8369f520dc38d77937b69b821db4ffea8f50ebc404f0587b5598189f54b5a5b98966fd16801c87de2c3c7813dd70dc600824d426d88c55e89d47214d59206a7a65a65da7ca2e42fa62ed17e7aa5b3ed446bcc71f17fec8593be96d2037bd07f9476d4d732b32bc5df8c921316b45699004716fc89f8d45bae402c26dbcdf1a340847b932ff882dbeafbedd252e126c89a1e1fdd8908a1f67d15d8e432dad8e08e950a3bc46b96cb89cc5bdac703b3fa3e986ef1c6e7e6606e6845ba1eb2fbdcfee744b5e45206f4a419e1cb103c8490eb293ee9aec1f0a0d294f9d3847737413d30873f3c94740e8fd072817815ebbce3f09edec9d1211a9e99547d620b2ec56c89e9cb8144ae9e46636324bd13c6cca3ab9cd9fd8f7f937ababc598232384427a2d4ce0cbf9765f7225e208c3ce128602b0ad08a1baab77edb3111f0c6ca7ba0eac9d89d5b4378eb82c17f6ea08308a79a53d150d3f85efab77294f02ee0e2885ee2ab2793392b87db11fa77992f5b4fd75ef2f1a822e87407a4878894215ab89b6cc4a120f5a78b3c31ab80ffcc9acef53fc6f7f85685eb9d56d30d87c21abbf1652eef8f32c7c567bd1f08623b09c29f33561d42727a5649a3850071aa6c11735ae63c4fd31559ce560b27a362786a83353fe460b37074664a9421d3b2f6a864d5aca087187b27e2b82f31cb3df5e985cea271c609b94b4e58356d40c7d5c7ff2e5990fb39588154843ea5fca92f120075d4c4d006661a0fa1b0585454bea725473eef7d58117d5840c8348999003736c5eeb7858ffd273a1c3eb2812f5697c59110275b08f6befbe84c92497d5f73b7b6f794a849713b23ac5f29d5c7112fb2e7a6e89eb54ddfa3122e6c79624c1bf25ebfb9fe5ce6daa779f3ecb2984da42f8c6adc77b21dd291e684fca50e46070962a2d4f00813d8de1b8ed33fed9715180c7ea8e2bb74fa65d9c7f6e142f3c81cdc59172e1020f62f65ca5a12cf2bed9dea04a4d8cabc2948f7be823a3e792625275b3925a6c8d8e2b428c75a5db0f7120278cd7d6cab768755c7fe2fbf89fded1fb38ac7f76a2f8798ca36ed42cb7c07f006271205f546a4812c20077f050d4cdc79459fa686e97f0704b7a9ff7de16318e862c53d361bc635a55a264be15016545dbfce3c6d6849576adefb6884edd768214e0b438b0231b4f2692c2c0b5c177674f8a0de236eacd9e0cec7c8647e4e9a5861b957ec834a2f8572f01304c3fd6a06019e5f1499b62baa8670b652467fa9a4f10f053263bfe9743cc7d933f86136aae3a6fb56754d7d238397a0030cebea87cb255af36138c373dbbac41dd4a697032e4796c552ad9c9b3fa713c3a4e09e0ec5581e94be7f31065157662f9e9c678b1ef1b8b8a847c51789c22b1841bcfc855820af3258af9e08231090b45d10046a00178e89bd515616b8a44e77bf57795dabaf40687b2cda7a5014168f", + "out": "4ba150c2f4db2449515cc9b545ec0012747324700d0ac1e554eab84a262baf00" + }, + { + "in": "1633256ab03b20ce079196b708a1c02d1b6072219070712c8589ee21341d50752acb6cfda17e982d828bbd6cdf54bc7232fd418a323d64939928597b9b52f07cf488250c5e42bfd3ab48012d709f8d747225839296386fce5fc5aecc4ba7a1076d089dea8ecefaa0cf66fca8602395719c12a04f929321784d7ab8239fce2ff3bdae046a266132b5c2ad9f7261f3014e87b389a6695978693d9371d0b1ff9c405f338c2fde4687359603950a54cf4b9cdd9b24480b239acc5405c14c886bbb0378391cef0662a38882bdd09e3866ab9a66cfbd28eb5ee4f8009bdefc4aeb16700eba7dc557b489190a71fda75e85f7ef841697f70ffd4fea185e7a67c81c5b8f273bfb97b2cef695c1c74446c4b425be6b2e66dc0aaacb247e4467b7c7d84ec33b6b5ab8fa1979f503008bdcff948cdbf1226b1b066cbcf34797298f3ba8c60fa01e0ac8b803223c656112fb91435d75453bae4707b63330467dd13e0a4b992e6f7e46995899a2d95d23f4ac3d0802b2a6e7d024dea19ca408c4bbe053f14c9ce264f129724a18bcb18f385b1ca091a11434ea96d98c8d0602e98edc8dfa14141af93ed0ba66e885e9fa108591ae59e109ae34d6b9f5586e4b4d75e7df7c32958a65e88a9baf41082a0a3f11539dc4ea2cbd9e1c6c3c439b622f1de574fa75470c8c939b51d2d1c2a7204b859881d43086bfd8fb90346218d099c5ab36846f3b98a7c847318bdfa01e09717943fcd864c5a8a17b6ceb89d98e872d388f20adc2be5e2006846904f41682fb1283214f3d20dbc9fc9e0ff571844a1282e88590d7c085b2c568ec5acc4462b389feaa5757f7033187e2de31955fce55fedc909255048b327ccab2e582bbc9d8054bf5cb45145c7d3a3af9cd5cf6ecba490c634ecf00e646bf95e8642c43a4978ef08a574ef1f78f6ce57c3b34b5a123d123617fc8ec9b2ac0f9b70a7f6062d38dd7b8e9fb4eccef13ded5c0477483addae4f1cc0cfca274b1307ed0de72fbcb819154cda897d7575213042615f1741a8cb646a39f8d134fdf9e60e000eb8220f65cc30f5fa52c431b9e3b6101b96e25b8d0440b96e572a18a01747c02afcd7513542f7aace194632099d16274f31ebabb60ddd94fe43dacce900ec0902eb5e686d48ed8d09ae63da0e15c736809903a0297a92de84e0260f11f446e1fc448e0ebf59faea3c726f97925c57cbdf85b1f77078d36257c85d56cbbedce180fe12b687ada2dc9912fac60334166bd2cef06b089ed5c9563844d71d8fead2f3a93f3c07c52537336a8a70bf5b596b9007b9fdf2d082000f20e6b70d2a7e6c7ed27c4146895a6d85a246f623c1b9258a2f891f823ade4ceffd59d4ffad077351e2f506e9a5bdd3900f0204b9e8969afe72f5dccb9cdf986d197ae4c4db53014041ae6221b750e5290e307ad292c8de6b899235212ef8ce954785537dc9435af11e0f3427a9c7b22efa752ea0b7eade5f6eb4093bacb78676e506698139e4f774423b8942166f9a7d22480d814fc0ae19cf4960fbf6e01ffa65c8da5bed4f1ae2b9ecec5be7b3c38dd4045b0c93ee6cc77a7e61e85d331b23c0d164b104518b3405497054445a353e9b48f2ac5e8e96298d6655614336cffe6d8c9c915e387391519ad2632366aa3bc935030fd12927efca17505ed74c94650c778539004854df6c24269aab9c273a493d3e5b0b1d687c33c2face46b4bb3742d6df743d09164d2e0ee7f6ba128bd5fba2e3b33c199ae80fa9dee3ad811d02baa3d42a6362b2ad47bba8a2c5cd00b46cf22cfe367281488a4852eb8b7face79f0ca6f8e78d32578dfee01711c4dcf3c26d0ba13f3075478e708c5c5315afdc2e4c0062d16458213bec506a9e991a61825ff78da9ba1baabbefa56b4a8c9e2e7b60ec4b7b541c8e0f79c86bb5f03f736761a37169b2aab8884ec6ea217b02c59035f5bb327243d126b78d4aab430212439b5a75b80618dafeb66aa3aff866c4daee47d374b512e74ada933ef24a841ba271c6f02c870e8ab950fe06e93c91df0e99165dc01bcb190e411eccd85358fd4a88127a22e4cf4266a90845124bf97b25d7b1c46d3a0d68a684f84e2a638c692a52cb6e8c651a3ac492b0460004073d5349e35552359ca37660f77b2770d6b2b3f7b1922424ac4a8598b4c61a6db507608a72a6a7d573cc055206276e14005a28a0ec41f28d7e260611d40f089ffe5e529375691412f4e9e12e62c3be2c563c26d2444ea9c69e6c935feb4dc4e802e5fe3906f8acef4798d940c3cd574bb5e74506c3e0b70cb62454a25f589eadb6b0709fe3b50417cd1d98f08e08b7cf68a04cccf8d6588f9fc2f31e533cda6159baa4297fa446450d71c16ea2324ec09773e7c8817ecf680ed12f64a04863efe3d9d8760f34de5b0860b3991ff0ee5edba22c4d69120de19d5429e4aae91c9e7cf05cc807159a58f13b480872ac1609d87e7009dedb71c09ceaab640a2b6135855ceae4ac2954933a0255b425d9fdcd9c246f82aeb7c3bb78c6e73e03db7aec4245a28693fbd36ef4938d59cce19eafc00671a0851612406a075713c5d1154d8e13b59b7c5b0902239d4bacfa386ac817ac5ee02a181a9a47c622b3ecf287e14843d452af347110498a620b34ab4e116308d976062c9ee9cd35db6cb79805b93ac9a15afbcb52f1ed4309879d1924a4ba190b0b86e60a516e77d34b4e0a49d4ef2cef3cc2f410fd8ec901363fc9ebd75eb460d4d8910bdf27ce26a8b4aeb94f9f76242401dc35d0644842b99fb6c439b82d82ecfe1af0d01f9becb15bec83f13b260f7f714aa381032923fde8f8018f3518547451435c9a5207294d08a907c73696f6cb000745e072e25b73b3ee11595433d27a1f11468686f08094f1d31f5ada81f11f0677a29d72ebb2e1c4792ccc607cb938647e1f153f9eef03d982595c631e49b6b7c1fa003a6eb8d59cb8892cd0888b05240f12701753f89007c859515a2fef944bc60b36003a26702ac6fe04d2e942978fc31a97eb29871d6752399d3521720729007b6a7215a4282b2a4efc2c56bd129e74c9b00847692b96fcc71cf7a7f19f3fd6b45c519fd73b4860880a2dd74e5727b31a93f0a87f0078155344ae9f7bdbf00d83393b634b5dca88a398e42c320eb95c4a826acea90b65e4767b2eba748f97c247568393e2fd3a66075cc12935b6d7eb5c2ff5282185cb62c73972a37b3ca508004b4f796bdf82b83b5bdf90d6bfd32b5089b0ca2683dc7fb2337de42e650ed911dbee1ef98257f9ba5af54b1a54b04c0087a5a64ba779d86461ba15337c2e7d4955fdd777a025de226306a17c384f1c52cdb5946fb0b46dd5c13bd7a55fe2e27e4c6d40d61d6ffc024468f8edfc7c7992df5dc5d05063fe723199224f53678e48f25250ea28bdf1089718eb8b730d1c06735c2f871164e2eb5e885a8dfd2a083be97edc94159ce9bf75d2433f1d782762f771903cbf9a1c9d13f710ba0e151b079dc0a8262bceb1dbcbbc0f35df6eecf7baa7105b9808745853c96b4372e95e482035916b726dac7be95a72b19dad48db1b19e6eb2edab5ac1b3013839e7806625abc129f41813e6d71ee4ab2040d81e42e6ed73abba64ff2eb433b910ea7d4f5ed3d8d27d39bb454ec019df6114f544d7b155549d0c56d14551faf353994a80f30f3c97e863a4f2af316468a568038eb4d799350a6facaff90ecd44e0f44efb6dc42ee4b0dc2c59ea9c1827326df08c0a6e55cf4f9c3ea0e78cff3635f5d08e44f1400d20f638d56ba84b4832090454de57ef04b6c8805a36f63e5ccc6e830c87ffc164647ced20e4c486d09de7a5f9e4b68d5456cdb22b0dded2b95b3bcae529215c2d25d6823c7d66a4fae0a1e9f022ba5663204f2314dfa51a1f10e11d6d62a8ba6c28b6ae7da1deb5b57f2b65d7456059ad9f03dc5a524054da39dd100d74eb657de219795e3c45a0e4c762ba22f9da9d8159e425a1ee783b4b22c250d8894cbec706ce16d5ca393404ff478f141be7cc69e45b077ba1955f1f49efbe4847c795347f703300f672334f490abf8b644a34b56da00ec45a350314b9adf27caf7c51cb7dba0c5477e7d37662f4f23247bcb8f7dd5f3e9cb8bda40fa97568832af0adc68f71422e412254a6bfc8943bb465b01fcc8de0b957677c78bc1f7566953e9d2446239f602c682a521c14f741fea98c7e27aabec339b6f5b94c78287a894afdae971f8da7c7e4a4c92c8da47be82dc2532ec2da9bacedd2be6db2b2fb34dccdcb34116507376578cbca105e5e443bec0f2ef23be34cdf862edab34f0ff21335e3acd92f59688b419f824ea61eea82bc80e3463452192377131ba51fb0795e089fc077d0eca8012e58b0637ad7022206887fe9ec00ee5df7ad2e26fe819ee35c7a179c579098aa3df645d9064cd557da90bdd21f871ceb048ca56df9653a10ed60f5e9f0ed7f8d89bcf5c22d1143cf44718ff2dfd8e10cef8aabb67d2305f18177c1426bd4cd03f2625e459ce905067826a214e08e56d8f9455593e6b324e72dedcc429d3befe2ae0599e360df95e80d453a3a849e48389fa745635bede30e7932de6a3816e31a2217f98d5e40238963d0a36c159fd4ec32d8a5cf59d433def3378634af6887fdb3f3edb96fc8840fe1b538c329674ae810e8c8b2b46db208716d38e9d1aeab097068ad83add7dd2647839b3a7388b0615bde26f8692e9c07d8adecc2a875203c3d3a9c6cb1d7d06307e9e1d9c3bc536dd8eb271e9a2159c904e61e8c9357fe759f36366aef5a3d14cee82913cd2708aa6069369ced763c8e830d70924e82e9015c2998e86efc1dce6ac2ebcb49455542a6d7dab265ad6d7381ffeee1aa40f8fac0659b6fb56bb03cd8cafaacd48d13672f7d524eb9684cfed4dbb7476e99149c28ec08f33ba6aff839aa178f86b8eeaf1739c829177ba78547ad394136aa3fad451a11e9642506568b39668b2436610e06ea45fa11d04d3759b033b5382645f15b3c39270b81b80487643913a24f2f1c1a1ed57c85ccddc8cd6d59b62fa67cc80572968c8fd01894f0153634c88792a7c4a407a4a4ce46cec5fe5d2569f95a27de242444ea0c715b357518caea23e767e8545983f0d3a4df66111b4aa1d399ccafd796d7a80e592d5a51d2b3f60b5b04f8d9c009ca56cbd4dd84127a29b72adb7645fb7279c9818b2b43963bd605f45b6575a5e2e369e0b401f5ec10ec703f1179b0ab9d4a89d6f096573952e513827364a84d38922734137e969d8167d6959b70f42f2bda37e4c989abaa8024c1a84ed6beb74780927f78b32ea736b9b2b4a795c355c0319811729d9cc399d23519730338d62e16e5035fc52a817090703fe776d65ef9fef5ba5f4ffec3cc8e9eb2e312c50a479bdd4e6ab0a56c18c2df69ed408417bee28bb41dd13f8366ff6eda4b34090fc9bc045271", + "out": "192eaea84038d588ad55dc5143f2bb10040ef78bfc7fb91f6b5e4c053466af00" + } + ] +} diff --git a/packages/crypto/src/testdata/ripemd.json b/packages/crypto/src/testdata/ripemd.json new file mode 100644 index 00000000..8f224b1f --- /dev/null +++ b/packages/crypto/src/testdata/ripemd.json @@ -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" + } + ] +} diff --git a/packages/crypto/src/testdata/sha.json b/packages/crypto/src/testdata/sha.json new file mode 100644 index 00000000..ba4107b7 --- /dev/null +++ b/packages/crypto/src/testdata/sha.json @@ -0,0 +1,1564 @@ +{ + "sha256": [ + { + "in": "", + "out": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "in": "61", + "out": "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" + }, + { + "in": "616263", + "out": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + }, + { + "in": "6d65737361676520646967657374", + "out": "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650" + }, + { + "in": "6162636462636465636465666465666765666768666768696768696a68696a6b696a6b6c6a6b6c6d6b6c6d6e6c6d6e6f6d6e6f706e6f7071", + "out": "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" + }, + { + "in": "3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930", + "out": "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e" + }, + { + "in": "8000000000000000000000000000000000000000000000000000000000000000", + "out": "84b3f13b0a4fbcce4c3f811e0313e00dcbd27431c229eff576598be8d1afb848" + }, + { + "in": "4000000000000000000000000000000000000000000000000000000000000000", + "out": "94ce3f7973aaed52c6c446bcc59bd8d43b6695fa3fdb9e3d1cc47503a432d3d8" + }, + { + "in": "2000000000000000000000000000000000000000000000000000000000000000", + "out": "6d0855a335802f0bc20946f3c48c05b6b98c14b578020d5f42a166b97fe6f59f" + }, + { + "in": "1000000000000000000000000000000000000000000000000000000000000000", + "out": "56fd25956f759e64e853071ff01587c364eaf6286c97da600e0be78c701637db" + }, + { + "in": "0800000000000000000000000000000000000000000000000000000000000000", + "out": "067dc6a810183e9069f63c2020b692c122c8d58263ed7f5c0e531504dc3b6e06" + }, + { + "in": "0400000000000000000000000000000000000000000000000000000000000000", + "out": "4b78063b9c224da311bd1d3fb969bba19e7e91ee07b506f9c4c438828915563f" + }, + { + "in": "0200000000000000000000000000000000000000000000000000000000000000", + "out": "5778f985db754c6628691f56fadae50c65fddbe8eb2e93039633fefa05d45e31" + }, + { + "in": "0100000000000000000000000000000000000000000000000000000000000000", + "out": "01d0fabd251fcbbe2b93b4b927b26ad2a1a99077152e45ded1e678afa45dbec5" + }, + { + "in": "0080000000000000000000000000000000000000000000000000000000000000", + "out": "0cad7906b177460ef96d15a612e83653862592a190f78fbb7c09f4aa89e616a7" + }, + { + "in": "0040000000000000000000000000000000000000000000000000000000000000", + "out": "e30c1ba805347bc13f4e4e4e82658ab2c1c97bef72c4f3d555590784c64b3587" + }, + { + "in": "0020000000000000000000000000000000000000000000000000000000000000", + "out": "3b5c755fc2a3a868da0a668b2704635b13e3dda0acfd386b4c025acb644400c3" + }, + { + "in": "0010000000000000000000000000000000000000000000000000000000000000", + "out": "9f911fc19889661af03c2e9849208883bb606beb75bb6c162ff63f65e4ca8157" + }, + { + "in": "0008000000000000000000000000000000000000000000000000000000000000", + "out": "3ec07f4585ef5c91814f7ddcae396dc42156df82307c46c7ae977cfcddbd04a3" + }, + { + "in": "0004000000000000000000000000000000000000000000000000000000000000", + "out": "7e8bbd09e5c09de409cdf71c4b39f59b350b4b5dec4a45d0b6127fdf873b4602" + }, + { + "in": "0002000000000000000000000000000000000000000000000000000000000000", + "out": "1616c4b5c8dbb446f269f2c9705b857b4a4315355a52966933e6b0e51da74a76" + }, + { + "in": "0001000000000000000000000000000000000000000000000000000000000000", + "out": "36258e205d83bfad1642d88f39a6cc128ca554016de9cf414bef5c5c4df31019" + }, + { + "in": "0000800000000000000000000000000000000000000000000000000000000000", + "out": "9b885490f45e3558384e94fe2c64773323ae14bcb8f9e8e02b3fed18eb930a30" + }, + { + "in": "0000400000000000000000000000000000000000000000000000000000000000", + "out": "3bfb6efd3afc93bcd7ecde51304456c2dc0c697a337de3fd611cbec3b3bcc53c" + }, + { + "in": "0000200000000000000000000000000000000000000000000000000000000000", + "out": "1d8052462938001afe80a3a2dd04ea8b28aea9f613849c5285401b7df2e8d604" + }, + { + "in": "0000100000000000000000000000000000000000000000000000000000000000", + "out": "c15f125ada3b323c7d79fb6d1c96d87ca7890c468a209c66f80b5ea223e5e533" + }, + { + "in": "0000080000000000000000000000000000000000000000000000000000000000", + "out": "4c49b88894d742530818a686961a640ca28692c2e2ad020b7cfd86de3e594068" + }, + { + "in": "0000040000000000000000000000000000000000000000000000000000000000", + "out": "85bfb6e1a0aa50099696212dce8a0067f4fc8a2c4da0946b6106be1dd01d7d78" + }, + { + "in": "0000020000000000000000000000000000000000000000000000000000000000", + "out": "5d5e9793d0f89cc3a709d2ffc1b488f1fdc7caade69ba027ea7b44f18bcd082f" + }, + { + "in": "0000010000000000000000000000000000000000000000000000000000000000", + "out": "1eb50e78e6dfb0a4725fb71ba0443c3129a6822213f3d40da2439fcd2bced580" + }, + { + "in": "0000008000000000000000000000000000000000000000000000000000000000", + "out": "f7b7700280839e9e0ff8aeccb3be5c586df0bd6b7bdb4ac8c8ed45ae3eef3686" + }, + { + "in": "0000004000000000000000000000000000000000000000000000000000000000", + "out": "2e0d44f2be93b23accb8e7680fa0b58e25f48e33334481a9297c8f5e9428f326" + }, + { + "in": "0000002000000000000000000000000000000000000000000000000000000000", + "out": "672504ffc411a43e4e9cac5a44bb62df4a7da166d18e4d47607cefaadfb667f1" + }, + { + "in": "0000001000000000000000000000000000000000000000000000000000000000", + "out": "4a9f5ff8813d3465074fd633b060b49318d9ee98b3cf3b3f4a3c903e4ac66396" + }, + { + "in": "0000000800000000000000000000000000000000000000000000000000000000", + "out": "c68386bcdcef8edf31d9ecce2a34e006f49ae1652ff0bbda847ff6601f762815" + }, + { + "in": "0000000400000000000000000000000000000000000000000000000000000000", + "out": "a5fe20624689a0f3378834e922989ddaaedad5bf51d3b1f5e84d63778a8f43e7" + }, + { + "in": "0000000200000000000000000000000000000000000000000000000000000000", + "out": "03de22d278e3be1e8fdb7da3ee6679e6d514dee8fc118fb27a91664cdebed8af" + }, + { + "in": "0000000100000000000000000000000000000000000000000000000000000000", + "out": "d1ad35a94f018ccb8e40a06fed17db11f0638da3f3e638108ade5d151eccce23" + }, + { + "in": "0000000080000000000000000000000000000000000000000000000000000000", + "out": "4d205614446cbdf1a8160a7182bcb24efb32d725e016bb91d84c1e7df55201aa" + }, + { + "in": "0000000040000000000000000000000000000000000000000000000000000000", + "out": "72171d3bfe9863d702b81ae9c69135ad007200a5a7b8dc419f884c944a309dd0" + }, + { + "in": "0000000020000000000000000000000000000000000000000000000000000000", + "out": "3f0a6edf24b8d9f9038b828d2f45f7625123f8a1b07e39892c86fabb2fe687d7" + }, + { + "in": "0000000010000000000000000000000000000000000000000000000000000000", + "out": "c9bf3eea4d22268bd1ef0027a5e1e398f0d6c4a8190bd99ad869a8796eb0cd4f" + }, + { + "in": "0000000008000000000000000000000000000000000000000000000000000000", + "out": "5706ed5b0db45898c5c01f4c4b5360043e1029ca00b33c33e684c27c30222a1d" + }, + { + "in": "0000000004000000000000000000000000000000000000000000000000000000", + "out": "ecab997b21788d277cfccc07aa388c4b199ae63d6e606cde28328ec209b794bc" + }, + { + "in": "0000000002000000000000000000000000000000000000000000000000000000", + "out": "58d2c35ffac68bcf336a44b98aff5740823cabaee65865c608d487ffcffc95bf" + }, + { + "in": "0000000001000000000000000000000000000000000000000000000000000000", + "out": "909f22c2b34103bf854580c5f2c4f64c2520aa57b492e422d1801a160b6c6e67" + }, + { + "in": "0000000000800000000000000000000000000000000000000000000000000000", + "out": "a0fc0a816ab024c9ded26d9a474b53c66635376400fb3ab117bab262321a1308" + }, + { + "in": "0000000000400000000000000000000000000000000000000000000000000000", + "out": "9debededb5b1b6a2a2fabe9104c8d3f425144f290490ed788d6b6a19994c703b" + }, + { + "in": "0000000000200000000000000000000000000000000000000000000000000000", + "out": "cbbf98775780c3b92bbc871c1d5137107be63933d0f3fe1be7aee434aa5509bd" + }, + { + "in": "0000000000100000000000000000000000000000000000000000000000000000", + "out": "c1b245d91a44973947297e576511b7fc55cbcd06159cb0f111101e601b36843e" + }, + { + "in": "0000000000080000000000000000000000000000000000000000000000000000", + "out": "6986e93dbc3b044d949945c0af3bc35ed63915e0268e9395d552d4acbc5a79b9" + }, + { + "in": "0000000000040000000000000000000000000000000000000000000000000000", + "out": "5ff28a82880765e64116aec484f2b3a0ec1dbeacec2bbc78737e5504a94c2df2" + }, + { + "in": "0000000000020000000000000000000000000000000000000000000000000000", + "out": "76dbb5ba9ec438d93638bfa8f62664201e29ba84bc6b1ab704d9688e89431503" + }, + { + "in": "0000000000010000000000000000000000000000000000000000000000000000", + "out": "80d3eb7836cb04382bbaf764eec343f07c9618bdfe98e01fd2ba2958902253c0" + }, + { + "in": "0000000000008000000000000000000000000000000000000000000000000000", + "out": "89ff132255600368de253f025bf92bb5af9bf4d1ffebb25575ce30eb77e4e4a6" + }, + { + "in": "0000000000004000000000000000000000000000000000000000000000000000", + "out": "2012053e7ae584deee9b71a412fcc9351c29961e9d3972615c10ea59329a1d3a" + }, + { + "in": "0000000000002000000000000000000000000000000000000000000000000000", + "out": "414daaed4defcabb5a3e4a82131b914e597522efce506af7b294a14486b40ed8" + }, + { + "in": "0000000000001000000000000000000000000000000000000000000000000000", + "out": "bc52effbbb3c6f7c66ad400b707cd98930a828f6b5ba3e0c71d706f42000e80c" + }, + { + "in": "0000000000000800000000000000000000000000000000000000000000000000", + "out": "6225473f5d9bab2a34928d9f3c9891c990982718319f2408d07670a7460d783d" + }, + { + "in": "0000000000000400000000000000000000000000000000000000000000000000", + "out": "b2b2dea4697fceb6cc466a7859a30c2bf5c6e477f6f442a1918802bfe707990b" + }, + { + "in": "0000000000000200000000000000000000000000000000000000000000000000", + "out": "873d4cb3f71aa7704660011b30e9b573ae0e839cf0b102633f197290f19998a7" + }, + { + "in": "0000000000000100000000000000000000000000000000000000000000000000", + "out": "da24cf903bb826bfc026106f54eacfe50c0e8319be205b47e181642723da2305" + }, + { + "in": "0000000000000080000000000000000000000000000000000000000000000000", + "out": "afe22988ec899e95704b9e87082ee375f78db2687478ccfbc2dfdc1e121c49f4" + }, + { + "in": "0000000000000040000000000000000000000000000000000000000000000000", + "out": "497d0d90f9a77f1d67f0567e67f7ed60f9d324cde0db266e51afd4b25cf24fa4" + }, + { + "in": "0000000000000020000000000000000000000000000000000000000000000000", + "out": "0845ca0aaaf8f899437303a1c4a24101437da7c90d1147e653295ba68ced2d1c" + }, + { + "in": "0000000000000010000000000000000000000000000000000000000000000000", + "out": "a1bd23817c23ba5910b9ee8404740a0ce3e81df31a5afcd172b4613ecb1a9b65" + }, + { + "in": "0000000000000008000000000000000000000000000000000000000000000000", + "out": "13ea147d01f645b321e81f39d15ce4aeb9cf2e0373d6fcbbc1ca7cdcfcc40c29" + }, + { + "in": "0000000000000004000000000000000000000000000000000000000000000000", + "out": "912e64c464286112afc2ccd15e638707f293d8a8133e03d6795e96562d471183" + }, + { + "in": "0000000000000002000000000000000000000000000000000000000000000000", + "out": "1af94aa75bdb9b976831e1a6a1a7bbd14697f710e514ac4019b33815f167b555" + }, + { + "in": "0000000000000001000000000000000000000000000000000000000000000000", + "out": "e13fcb8f649438f18530ec00daa36a110fc641a226c3dde990f82c4b561da4db" + }, + { + "in": "0000000000000000800000000000000000000000000000000000000000000000", + "out": "c8387bf8dbadf1c9ba583a8d27b620f4bd13cee4ea2ef98bcb0e1bdfd6f3d8c8" + }, + { + "in": "0000000000000000400000000000000000000000000000000000000000000000", + "out": "79a257d3b1260eae2c407b55a33c28e19777c185b5254ab051442d2353b35464" + }, + { + "in": "0000000000000000200000000000000000000000000000000000000000000000", + "out": "6552ebe29b1b037562f1888498bbb208054638e97c0c625f127c1a203efffc65" + }, + { + "in": "0000000000000000100000000000000000000000000000000000000000000000", + "out": "2d77a6f16003c7f09e6daea5caf4e61b9cd822cdf21a33f300eeaf33264ac67f" + }, + { + "in": "0000000000000000080000000000000000000000000000000000000000000000", + "out": "f5b8339cb1efc7f3fcb94dcdf8bb3ec191ee016609082e47242ee6253a05da9b" + }, + { + "in": "0000000000000000040000000000000000000000000000000000000000000000", + "out": "bd7cdcc82d46856db3e580548999dfba0d8bd38e0edbb797188de335d933c8b3" + }, + { + "in": "0000000000000000020000000000000000000000000000000000000000000000", + "out": "d340dbc1256765fea41925fda295bdfeaa1055cbfeae0fdba8608e3116d746dc" + }, + { + "in": "0000000000000000010000000000000000000000000000000000000000000000", + "out": "49293309d25cafc914c11063c2d1cd286a4500d519b11b4fc98500efb85f6d9c" + }, + { + "in": "0000000000000000008000000000000000000000000000000000000000000000", + "out": "6091339d451e8f830a46c5ce040717c9d06f36aa4c5a9a8a9f1622384a6eb694" + }, + { + "in": "0000000000000000004000000000000000000000000000000000000000000000", + "out": "6cbd66038b54d94a931006ea23db48c300e1384ce7fa0f7ccfb8efe2fc0ac4f8" + }, + { + "in": "0000000000000000002000000000000000000000000000000000000000000000", + "out": "8a1a30a8f415d71bbb03c5ae3df4836ce54cbcfd78816bce86e0b983b059e972" + }, + { + "in": "0000000000000000001000000000000000000000000000000000000000000000", + "out": "8039aa16a81b23f2410448563996605c13766d1e7417f4bdfbd8ad5d0d33554e" + }, + { + "in": "0000000000000000000800000000000000000000000000000000000000000000", + "out": "7549d699d3d1989d940600f25501c243e9af21fe51ef2adb8358c159c9e9663f" + }, + { + "in": "0000000000000000000400000000000000000000000000000000000000000000", + "out": "831f3936cacea264ef4fae2e36b2110f6729baf434e61a6ee379d0c014f2daae" + }, + { + "in": "0000000000000000000200000000000000000000000000000000000000000000", + "out": "013edbd6b8ea866d7ae7f553c079663ca22acf4e21e64f0085ece1b449bd10ef" + }, + { + "in": "0000000000000000000100000000000000000000000000000000000000000000", + "out": "d9b846387ce55ac512a1e2807aaf6f8dcfbeb462ed6d4176cc56a0b0bdfe1047" + }, + { + "in": "0000000000000000000080000000000000000000000000000000000000000000", + "out": "0528b59ed6ebba187a69c3c41e88120b1315cee497bb6731191dc05000cd1e78" + }, + { + "in": "0000000000000000000040000000000000000000000000000000000000000000", + "out": "1eba730823ba27ecade25b38144d339053446006ec3f66131aee473ea3fb9e04" + }, + { + "in": "0000000000000000000020000000000000000000000000000000000000000000", + "out": "b8ae9bf4e323c6ef3bfe75b6969ebfa68fe6c14b06481cf06869d12c555dbfa9" + }, + { + "in": "0000000000000000000010000000000000000000000000000000000000000000", + "out": "c89944ef4886967a517064a212e38bda5fca80ca54e18103a75d54a6e230c694" + }, + { + "in": "0000000000000000000008000000000000000000000000000000000000000000", + "out": "452e5d0af5dabfdc5c74868d916f1e390a7354937785f1f6d4d0b1d72f06bd8f" + }, + { + "in": "0000000000000000000004000000000000000000000000000000000000000000", + "out": "87ad2f7f2e5db1be3bcc982ea058955ae34a3cf0cbf87dc4813ae5b0e6b3c517" + }, + { + "in": "0000000000000000000002000000000000000000000000000000000000000000", + "out": "fb4635ac471cbebe2eb3367f259232d7b62b8a6342e1bf73294fe447c3b8076e" + }, + { + "in": "0000000000000000000001000000000000000000000000000000000000000000", + "out": "9ada02e914eb181d22e7f6d9b3f39804a0f758bda23995e567e3a1edff0b60e8" + }, + { + "in": "0000000000000000000000800000000000000000000000000000000000000000", + "out": "d41bc420cd25ba9038a4e1d1c4ceb1b05d993d0b68095f46b4bb524b72f15287" + }, + { + "in": "0000000000000000000000400000000000000000000000000000000000000000", + "out": "329954d9cb855dd7798e587403353dd69cebf91a5020ae5fc4d742656e4cad0b" + }, + { + "in": "0000000000000000000000200000000000000000000000000000000000000000", + "out": "7ff37d832db4da7cc3473fc3f0b263949630f21dfd8522a544994c5a3b12644b" + }, + { + "in": "0000000000000000000000100000000000000000000000000000000000000000", + "out": "7f27bee720048484188f774d660d86276b6383ab2e40990f181e5349dc72fc52" + }, + { + "in": "0000000000000000000000080000000000000000000000000000000000000000", + "out": "aafca0ac1d6c80ee40fb43fad51f006d39de0a101449b450a3e0fd9d44fe0230" + }, + { + "in": "0000000000000000000000040000000000000000000000000000000000000000", + "out": "941a10f2333950e3501c6229c085a54185e55a1017c9b8dfd9187b614371884c" + }, + { + "in": "0000000000000000000000020000000000000000000000000000000000000000", + "out": "fa7862ed967e19ec63f91344184684099c7bd734edf810509e2fb308fe5daf16" + }, + { + "in": "0000000000000000000000010000000000000000000000000000000000000000", + "out": "e5c92e4c6590ad2f982267be2b13e110c44d9c69e93516f594b7e433a0e93af4" + }, + { + "in": "0000000000000000000000008000000000000000000000000000000000000000", + "out": "0656dd98ded7d764bcdee4e96a9699e70974ae77cf72f166b7a979b707f1878b" + }, + { + "in": "0000000000000000000000004000000000000000000000000000000000000000", + "out": "1fa047efed4a196a9b538c099e50b67a0f537897fca6f0aeb386f43e65d48a31" + }, + { + "in": "0000000000000000000000002000000000000000000000000000000000000000", + "out": "74bf1c0e1da85358fa86ecbbab419c9223f41d47702583593cd01539e861db73" + }, + { + "in": "0000000000000000000000001000000000000000000000000000000000000000", + "out": "846c472932fda94b56483e7a903c6545b5cde4fe5d0a2ffa03b52ad53570d54a" + }, + { + "in": "0000000000000000000000000800000000000000000000000000000000000000", + "out": "e78743b610392b52cd122071f9071a8e9c35aabec0bd63c73c5fd12171838b32" + }, + { + "in": "0000000000000000000000000400000000000000000000000000000000000000", + "out": "4a435a44a2be1f40d8e6a1f7c332c5330ede4e0f55505304e571b4443255a5a9" + }, + { + "in": "0000000000000000000000000200000000000000000000000000000000000000", + "out": "18122cf13ab2412fa65cb693713794de6b30403b65688234a6fa940fc6d506ff" + }, + { + "in": "0000000000000000000000000100000000000000000000000000000000000000", + "out": "bae3e67a9d5505a5685ebb52b8510b44315c0faa422f0ebfd4ef3413490248fb" + }, + { + "in": "0000000000000000000000000080000000000000000000000000000000000000", + "out": "b7f300939fcc6ea45a8920c7b8d3f18e753c0076062e4a499b69af96328ceaf9" + }, + { + "in": "0000000000000000000000000040000000000000000000000000000000000000", + "out": "7e5aafc21b6238fffc561283ac2b592c1f5bb35237061629ac9a4af7153cdb3a" + }, + { + "in": "0000000000000000000000000020000000000000000000000000000000000000", + "out": "b98f3c9fa25cc07aa02f7456e15c7707da6702628ab589351b8737a814dbcb9b" + }, + { + "in": "0000000000000000000000000010000000000000000000000000000000000000", + "out": "b01668cf19c69b22737f7409ede201cc37bf1b23fc0630fda9364652171389ac" + }, + { + "in": "0000000000000000000000000008000000000000000000000000000000000000", + "out": "a8bbe8b393ab0e3d0e822c8ab9f23b8f1c985e93e6ec17050cc6a0d82b27a078" + }, + { + "in": "0000000000000000000000000004000000000000000000000000000000000000", + "out": "fb246a9eec0aa00a971416718cf0bb789f44496183642024c5a3c8043c5e72f0" + }, + { + "in": "0000000000000000000000000002000000000000000000000000000000000000", + "out": "5937034d4c6184bc32cef38aca4bcea720f3d3061191d1e0eb5c84e242c7deef" + }, + { + "in": "0000000000000000000000000001000000000000000000000000000000000000", + "out": "c0deb1f9a2060a3ce111bc6c36ddfdeaed2713229eac55c75608b9272e10b78e" + }, + { + "in": "0000000000000000000000000000800000000000000000000000000000000000", + "out": "08cc99bd1c9e6c2ac03dd17c7be3f0d744aac4144d542d2f4d2f3366837e030e" + }, + { + "in": "0000000000000000000000000000400000000000000000000000000000000000", + "out": "b5026f002368efab1fc2aa97be628698c41db381b44f8bc2c8be3635c8f0bcfa" + }, + { + "in": "0000000000000000000000000000200000000000000000000000000000000000", + "out": "98a020137d37236b11d0acfec699107679eaf0339e8c0aab3aff1538296f754e" + }, + { + "in": "0000000000000000000000000000100000000000000000000000000000000000", + "out": "a30b09a7870d5fcf10704ca8a00083ff4ed2d0b78f530161c698a0dc272c5f12" + }, + { + "in": "0000000000000000000000000000080000000000000000000000000000000000", + "out": "c4cef7219fba14b0515ff84ca552273e471efb23a26274778c11d0fb61805a8d" + }, + { + "in": "0000000000000000000000000000040000000000000000000000000000000000", + "out": "0f70f20e2513a79ad1d153c98981d5cd21de4c134977658d1c9c4b4367a73f99" + }, + { + "in": "0000000000000000000000000000020000000000000000000000000000000000", + "out": "690a81f1fe0464fab24a2e9861c24e52087d902b2dfb344713b42285051e5c81" + }, + { + "in": "0000000000000000000000000000010000000000000000000000000000000000", + "out": "403e430c39eeca88d967926b543a06fbb68654c348801fde7466f34a5579e2ed" + }, + { + "in": "0000000000000000000000000000008000000000000000000000000000000000", + "out": "715623d0a82109017f74e8de00bf5b700bc6c161ca403cdeb9a09b659268b779" + }, + { + "in": "0000000000000000000000000000004000000000000000000000000000000000", + "out": "6e367ff2768a6d4c980d7d1b75f312663cb816e5c0191a8839f6f9e50a44853f" + }, + { + "in": "0000000000000000000000000000002000000000000000000000000000000000", + "out": "81f33a1bc1018f2c4886865f55ad2fddd891160d06717805f2687155e26dc2e4" + }, + { + "in": "0000000000000000000000000000001000000000000000000000000000000000", + "out": "1c074ca4abaf8b662d0c75635f84fe4ed9011ce476c45f8214f798438e1cb9fd" + }, + { + "in": "0000000000000000000000000000000800000000000000000000000000000000", + "out": "003d837bbb718e13778188399eacd53df9781ee8ad3f77bbd35f5b617d38ab24" + }, + { + "in": "0000000000000000000000000000000400000000000000000000000000000000", + "out": "37212e518e30d555ea442ec7467b1e95dc06371c9784705d0d885d1c61981029" + }, + { + "in": "0000000000000000000000000000000200000000000000000000000000000000", + "out": "74119615b3146e59d08dddf07cf0614264d73f2118cf4a5cecfbaa691b005f2b" + }, + { + "in": "0000000000000000000000000000000100000000000000000000000000000000", + "out": "3269aeb6831504cb8679ea40f749072951eb1728cc5e21e45fd0d6b423f6fa42" + }, + { + "in": "0000000000000000000000000000000080000000000000000000000000000000", + "out": "d504fd065f8d2bcd3d1d3a4b298328e09f1cb44e3106d156477e992aabe9812c" + }, + { + "in": "0000000000000000000000000000000040000000000000000000000000000000", + "out": "8aa4012b4573828d21c20ac64d18a6ea73da0347b5d1a71442091ca48655db70" + }, + { + "in": "0000000000000000000000000000000020000000000000000000000000000000", + "out": "650189d78de4cf754ed237a9ddadd9686b58d85d06d82e937df6075f4cc87642" + }, + { + "in": "0000000000000000000000000000000010000000000000000000000000000000", + "out": "d140e61d738c3298875886b8d8de576e48ff5c7e9f4d0e66d0149d0bdee19f49" + }, + { + "in": "0000000000000000000000000000000008000000000000000000000000000000", + "out": "59e7addb5e068640de3f8fb015017e7aa7495430d2533f87d4ece9f7ee548fa3" + }, + { + "in": "0000000000000000000000000000000004000000000000000000000000000000", + "out": "5481b8528ef488d0a4ed259244306aff83145b7d675e159efece21def7561297" + }, + { + "in": "0000000000000000000000000000000002000000000000000000000000000000", + "out": "671eaa12d76be21eb2ced2f61ac1e98df94c1952c2cdfc047895c74f15a7f3fa" + }, + { + "in": "0000000000000000000000000000000001000000000000000000000000000000", + "out": "6cc4f0e930b34481d03a4134331852eaed66667e3b3d8605f7cd3777551d2b6f" + }, + { + "in": "0000000000000000000000000000000000800000000000000000000000000000", + "out": "58a971df91f981284dfca88c6a21ab89d4e6a12e0d8a1e12bb585eb697d597e3" + }, + { + "in": "0000000000000000000000000000000000400000000000000000000000000000", + "out": "afe25f910412d62db9fefbac5caf3d240153725fff1b8d85ff835bd418028738" + }, + { + "in": "0000000000000000000000000000000000200000000000000000000000000000", + "out": "ac99c31bff7b16e8916e8ed5c969ce7dd1b7a4a009f2f03ea8fb240b1ab16ce5" + }, + { + "in": "0000000000000000000000000000000000100000000000000000000000000000", + "out": "85a6cecfa95e645104a45e2e34e98f92039ed921adf65e78631e270548521637" + }, + { + "in": "0000000000000000000000000000000000080000000000000000000000000000", + "out": "1c686e808a38e9d3d800ab94f8ef98888fa959593fe9a78850ee01ccabf170b9" + }, + { + "in": "0000000000000000000000000000000000040000000000000000000000000000", + "out": "058ae81be2477d2bb0ef309f69713d68196a0a9d758be3814b565088fd752ab3" + }, + { + "in": "0000000000000000000000000000000000020000000000000000000000000000", + "out": "5b1919a40f9a5aef568e1e24b414a85c0d60d1dcabaa2cceca09a91d78a91d15" + }, + { + "in": "0000000000000000000000000000000000010000000000000000000000000000", + "out": "ffd4927e8c35afd614a39aec5654a8cef3cfb47b737bcd55342bfda0b9d81e53" + }, + { + "in": "0000000000000000000000000000000000008000000000000000000000000000", + "out": "d47a6b8bdba65ddf5820dbcb3f1733018cfa0a3a278540c7fbd575e36e20f063" + }, + { + "in": "0000000000000000000000000000000000004000000000000000000000000000", + "out": "9d8a03520fc2f2653ac52b7c0da06f5436858a811e5f4b2db0b2182c2c8f6d12" + }, + { + "in": "0000000000000000000000000000000000002000000000000000000000000000", + "out": "13a17e3e7c07f58935075a41b2b5b332cc64f7099e320430dbef41685dd95b27" + }, + { + "in": "0000000000000000000000000000000000001000000000000000000000000000", + "out": "9250c7028c00aad64f1a1140f4de8812608fcf15f3e91ab886c911e71bd41324" + }, + { + "in": "0000000000000000000000000000000000000800000000000000000000000000", + "out": "c019bc0605ed4157c8d3e4761bc74d403558e426d04403b17c9923aa5a732c48" + }, + { + "in": "0000000000000000000000000000000000000400000000000000000000000000", + "out": "524fcbe1456ce1d535e6e75098f7a817eeb99f6d3b77a9705bf40674e84aeaf6" + }, + { + "in": "0000000000000000000000000000000000000200000000000000000000000000", + "out": "f4a5e902e2633f78a4ad90c9305dca8ccff31a8ce6fe8dc3755a591d201bb51c" + }, + { + "in": "0000000000000000000000000000000000000100000000000000000000000000", + "out": "bb242c3d49140119969ce07f0021e400ecdcbdeb3fc8c92459eff346878a6ff6" + }, + { + "in": "0000000000000000000000000000000000000080000000000000000000000000", + "out": "362b9130763c98696ace92e946f2646edb7a9d419cbe12abf9cb40b9bbdb32df" + }, + { + "in": "0000000000000000000000000000000000000040000000000000000000000000", + "out": "6bf88e66479d09985823f2b87bd7dfdda415442b5132b2482f9b092cd3fbaa4a" + }, + { + "in": "0000000000000000000000000000000000000020000000000000000000000000", + "out": "df6777df2585349325132c5a6ab16481498bf9a1957ebc2f113095669c7afe96" + }, + { + "in": "0000000000000000000000000000000000000010000000000000000000000000", + "out": "37108d63c635ff214af8a98dca6a3e288fa14ea8f5e8c52a583b6f12bdba3cf5" + }, + { + "in": "0000000000000000000000000000000000000008000000000000000000000000", + "out": "c22faef7e57a7bf3e344132436fae85bd59a9bf4ae01da2545a6ee3779773af2" + }, + { + "in": "0000000000000000000000000000000000000004000000000000000000000000", + "out": "a1947f12bd61d72067884d47ac2c8df8541fbb4a9eb2d0f1a8f6997207819ae0" + }, + { + "in": "0000000000000000000000000000000000000002000000000000000000000000", + "out": "dda4abdad1290fd66a171d9724a409b02612f8606c58d85d530f30e7cedea3d2" + }, + { + "in": "0000000000000000000000000000000000000001000000000000000000000000", + "out": "658fce7342d3c8e1edebd0ef0b72612346500b72ceeb1aeed11845f1ad1401f6" + }, + { + "in": "0000000000000000000000000000000000000000800000000000000000000000", + "out": "518ee98053e9d1f1f42f9e59dd74a6d9aa0d1f1fdddd4f799134b4c111226d98" + }, + { + "in": "0000000000000000000000000000000000000000400000000000000000000000", + "out": "1f2513f0cd6195005b81727f7784b1f109c2f680a5e3343a9ea0fac21f35bab6" + }, + { + "in": "0000000000000000000000000000000000000000200000000000000000000000", + "out": "2d05a4ed553cbd1f532eea35d158f01f2867d49a7120abe733a5f6c2e5c92c21" + }, + { + "in": "0000000000000000000000000000000000000000100000000000000000000000", + "out": "0bfc42ae0f93d32617bb3c7cf8e4a6515aa8668922e05af39a27cae39f9ca221" + }, + { + "in": "0000000000000000000000000000000000000000080000000000000000000000", + "out": "c3f2631b8f76c94534abde0eb456efa47dc43b08f7cdbe379622621b28458915" + }, + { + "in": "0000000000000000000000000000000000000000040000000000000000000000", + "out": "9c15e4a671eb6e390bdb67ac4441da8ccc9e56afe28b8bcc928fc6f3e552569a" + }, + { + "in": "0000000000000000000000000000000000000000020000000000000000000000", + "out": "efd7a7e53f7f21fe9b9d4a5bf3be5ae7adb0947f8412be25f7e88a45743e6fb8" + }, + { + "in": "0000000000000000000000000000000000000000010000000000000000000000", + "out": "a524ac0d0dacf565bba3b84015d995202e1f67409d63fe16d442caf5c72c3d3c" + }, + { + "in": "0000000000000000000000000000000000000000008000000000000000000000", + "out": "80073614fb76a09c3dd701e83be717117a3694d05331b032f8104ca241f11482" + }, + { + "in": "0000000000000000000000000000000000000000004000000000000000000000", + "out": "c475a4c797002b5c66a21def4295cdc4c9de4f045fb0fce7a1bdb977224bac97" + }, + { + "in": "0000000000000000000000000000000000000000002000000000000000000000", + "out": "6d8547cabf8f0aeaf7a9b3598cba769f545c61e8873ffd6d5e7ee9c1c5527f95" + }, + { + "in": "0000000000000000000000000000000000000000001000000000000000000000", + "out": "7187108d9f4506c15b5e6ad523167bd2badabe69242eb6aebcfe52f70874e757" + }, + { + "in": "0000000000000000000000000000000000000000000800000000000000000000", + "out": "a195eaa4e6377b4c0360c343d52f82704bd85bc5905d669c5e4eca7c1f028c88" + }, + { + "in": "0000000000000000000000000000000000000000000400000000000000000000", + "out": "72c72361133dffebfd3ebbb8fdc6b76dc530aa7ff5e22ee709b65be866dacd86" + }, + { + "in": "0000000000000000000000000000000000000000000200000000000000000000", + "out": "20f3bf2e8f3876fa733f41cecfda7c641ce579b8ace9dfe06a64a4ba72bfa901" + }, + { + "in": "0000000000000000000000000000000000000000000100000000000000000000", + "out": "d3b0f2a184d2aedacc760448dc351b63e975d5e48444320733f19381eb973659" + }, + { + "in": "0000000000000000000000000000000000000000000080000000000000000000", + "out": "25d380557ddacd3761fed7b1ef2ad9b2cb25ef263dddc08e34646efbb696f718" + }, + { + "in": "0000000000000000000000000000000000000000000040000000000000000000", + "out": "7df39f3b439912e4859500ed516f4096ef60a96911f626052b3315416c307f90" + }, + { + "in": "0000000000000000000000000000000000000000000020000000000000000000", + "out": "d05070bc61fc95f05359b8b36c70db0e3d5fc077f73c02ccf4310a24c91a0f33" + }, + { + "in": "0000000000000000000000000000000000000000000010000000000000000000", + "out": "cd3683d700693575fbb3355f844458ed60b1a45294cd27cd380a9a10e660e407" + }, + { + "in": "0000000000000000000000000000000000000000000008000000000000000000", + "out": "ab19432215b64f93a344f7e6a46b386c4ac159924ab4a6f366fb8552a96dbce6" + }, + { + "in": "0000000000000000000000000000000000000000000004000000000000000000", + "out": "1f6b7158c0b0a4311fbef86e39bd812bc94207d154b9cb2380433f16f6821b7b" + }, + { + "in": "0000000000000000000000000000000000000000000002000000000000000000", + "out": "e65a4e3567d420d98ef51c29405ab004a019d794c51a67a680f6d54f21f08e5c" + }, + { + "in": "0000000000000000000000000000000000000000000001000000000000000000", + "out": "53382a486c2390f7ff94a33bafce7285f382bdb5bed4ff076d7a161b169d6b4a" + }, + { + "in": "0000000000000000000000000000000000000000000000800000000000000000", + "out": "9e36673a278e023992b653aa0683ce64ba7cce51239e091c80d98bc627b45a00" + }, + { + "in": "0000000000000000000000000000000000000000000000400000000000000000", + "out": "ad51cb9156c8cb66c202133e098061688a0360ea0e5b49a9f47eb00c346eb1f4" + }, + { + "in": "0000000000000000000000000000000000000000000000200000000000000000", + "out": "9dd8990fd868111f6854ac54d85ba38b9b0be3e02037b4a3dad2d0d9e89dbce3" + }, + { + "in": "0000000000000000000000000000000000000000000000100000000000000000", + "out": "8a4a51aeb3bfe9feecd9f4af22d2d34c534f3a2b127e959236407206a4e1db2e" + }, + { + "in": "0000000000000000000000000000000000000000000000080000000000000000", + "out": "32f59d1c8e62ee49bedccf59007194eebbc698b888050723c5325a746463e3c2" + }, + { + "in": "0000000000000000000000000000000000000000000000040000000000000000", + "out": "fccd7332fbd25ce39ee7522f432185f2322ef05cf1f5e36a2458272ff1397a0a" + }, + { + "in": "0000000000000000000000000000000000000000000000020000000000000000", + "out": "62775bb6a290e509389c0ea6bae5c8567a0e034a813f9ad62666b4871d8faca3" + }, + { + "in": "0000000000000000000000000000000000000000000000010000000000000000", + "out": "264af724ebd387076f1427eaf7d7e94734a209ddffab1bf455528d8bfb548681" + }, + { + "in": "0000000000000000000000000000000000000000000000008000000000000000", + "out": "96fc8ff80897ed6f514a67fb4fa5cf9d53814e305fed248bae6b5761a331e0c3" + }, + { + "in": "0000000000000000000000000000000000000000000000004000000000000000", + "out": "f98b7c970726889639c19c75fa4e63bff6d48063806a19ea4584b84f3c40079b" + }, + { + "in": "0000000000000000000000000000000000000000000000002000000000000000", + "out": "76ca7485a665d0b76abbb2909b193d8f8572c8db77f969256480f63728aca867" + }, + { + "in": "0000000000000000000000000000000000000000000000001000000000000000", + "out": "8004b9b6a39295d73d00759682c72de9a49e7278189ca9a1d704fffde8f8aecb" + }, + { + "in": "0000000000000000000000000000000000000000000000000800000000000000", + "out": "98177f21e631c18fd21733c5bccf33243970ac5ebdbb19a257dc96bf43d5151a" + }, + { + "in": "0000000000000000000000000000000000000000000000000400000000000000", + "out": "dce21d3195a4b65b3b2c3ef0a954a157785c8ca4fd195ae15bd7f420d9d5ce14" + }, + { + "in": "0000000000000000000000000000000000000000000000000200000000000000", + "out": "edc0e40c3027d41914bf3144a08078742b337b96da8e503d4ff84bb6a8e97d55" + }, + { + "in": "0000000000000000000000000000000000000000000000000100000000000000", + "out": "013f21dd7052786e2c338b57f23ec2c7feb0c12f7b3b28fbb5affaca27103f51" + }, + { + "in": "0000000000000000000000000000000000000000000000000080000000000000", + "out": "bf8362ce587255d74a374de2775680c02a02b2106d05eb9b1eb83bc88a9c97ef" + }, + { + "in": "0000000000000000000000000000000000000000000000000040000000000000", + "out": "ca72bc9bb4f0f6e2ddd3d641d94a62163a066af9d77ac3937cb00d134dfd46f2" + }, + { + "in": "0000000000000000000000000000000000000000000000000020000000000000", + "out": "6b42a2d46eebc60c0238517a9f2b78ba27ce4b87ed35261fb0a0deddd39dc753" + }, + { + "in": "0000000000000000000000000000000000000000000000000010000000000000", + "out": "d441092f524ae16d1339e78fb13892732a5975705f90f82e54ee09d80448e49d" + }, + { + "in": "0000000000000000000000000000000000000000000000000008000000000000", + "out": "c7f6dfa7f3e4f92d6c20aeeae921fa209685d0c20714d69a95d1f94fa41d097c" + }, + { + "in": "0000000000000000000000000000000000000000000000000004000000000000", + "out": "300223df08c84b22c569fc15ecac264f9d1cfdad758962b406c757fca69d0a0a" + }, + { + "in": "0000000000000000000000000000000000000000000000000002000000000000", + "out": "fb81b52e00690b2f03f8d410a357e582f6f4367d4359fe7dd7cc6c6a3ad24ceb" + }, + { + "in": "0000000000000000000000000000000000000000000000000001000000000000", + "out": "bd5ecece8b8cbc564a91294968c3be209b15730cf7594d2b79bee0d44391be7e" + }, + { + "in": "0000000000000000000000000000000000000000000000000000800000000000", + "out": "80179fb24a6eec0173daf26891251c3054ea8d7280f88d4c57a3f52b5f1aa388" + }, + { + "in": "0000000000000000000000000000000000000000000000000000400000000000", + "out": "59c6f16a4878fc819f3a3cdfcf7c5a8ceafe9231ce259a7f9e377e3d8b41c16b" + }, + { + "in": "0000000000000000000000000000000000000000000000000000200000000000", + "out": "b18656e3d9d293f342a9a4b88884bd9a650d72368c1703b74abd2d4add1b6a5a" + }, + { + "in": "0000000000000000000000000000000000000000000000000000100000000000", + "out": "a7213daeeba47277122dfc9ffadbb36881c6fa9c89293d2291407522639e017b" + }, + { + "in": "0000000000000000000000000000000000000000000000000000080000000000", + "out": "33d62d3a60af659d70978e12f6ae01d6d62686965288b584796b6aecc37167b7" + }, + { + "in": "0000000000000000000000000000000000000000000000000000040000000000", + "out": "3f2f1919206cfbebc68db1bd552d78aff61f5ad296af45f15145e176cd4e93bf" + }, + { + "in": "0000000000000000000000000000000000000000000000000000020000000000", + "out": "4aca600624783f035871a05365edfd0d01a67c9dd59d38a68117474d81f4a93e" + }, + { + "in": "0000000000000000000000000000000000000000000000000000010000000000", + "out": "9b352cde8f0bdefcc8b1f37d34b3641ff6f55c05ff5193928402ed95c986d1cd" + }, + { + "in": "0000000000000000000000000000000000000000000000000000008000000000", + "out": "b14b9519fe51b738f81ba61ae48723835412b544f41e8ca4d3c2be8b8e7b7acb" + }, + { + "in": "0000000000000000000000000000000000000000000000000000004000000000", + "out": "aff464673edd86fed0c0cc97be3de9375e61503680b17ac4fd44aedc02ef086c" + }, + { + "in": "0000000000000000000000000000000000000000000000000000002000000000", + "out": "b600ccfec9ddae068109c82b0b7ba3632501857eb23ca7860fd26f3bc1335697" + }, + { + "in": "0000000000000000000000000000000000000000000000000000001000000000", + "out": "884aa440d0320d6fb74a8b02bbe5f7df50cc4b83571523cdc4a2683776ad6218" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000800000000", + "out": "c20d94291275f858e53fdc834b0e02fd496145b8713f53555e863425a61d1e88" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000400000000", + "out": "b1087c12c70d6224460202da3fa5b985d3d2b130f2347d6bc7dc7668856ef5b8" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000200000000", + "out": "be7f81f61289afd0e08467938f054f69d2795e570d0f5c7577125bd37d72069c" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000100000000", + "out": "d7e79aeeaef9cdc5889c68e98dcc7c1d85172d0f183a62815df0104ca67f3068" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000080000000", + "out": "abcab82bd056c3a975c8cc78b160b1a726ea2d58dc8775ec9cc1e97b1887263a" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000040000000", + "out": "716a3871c88d6c0d6ab03c6c925ca5b0fec9816cb393be5226e387048dc379aa" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000020000000", + "out": "046284f44965ffe307372b5eaa47d0dcfc282b13b1d13fee3786a0a2173ca034" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000010000000", + "out": "0316cc9c233290d72c53938cf8d216e24447d95114c3f9bbf2fa508eea9e72cb" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000008000000", + "out": "388a34ba91711c31e14675bba6cc29a157237e2b1b6095b02a49373a8abf43b3" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000004000000", + "out": "ccfc11fc56d48daf0f233275e9e591b76758ce6099465fa3e8b925facc8c1d87" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000002000000", + "out": "f1c19314208ec565b4e50664b650fc0d256b4eb5a177acdccefc78bef7543a6c" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000001000000", + "out": "954a6575b642bdddd05409cf5973ba837f25b2e391950be91fa23334093d88f5" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000800000", + "out": "d8c9ae155f36f1edd6a9a0fbce9d8a2d97efc4896eeab31fa4b2a267f10f83d7" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000400000", + "out": "e1291a0e2d900f2d61c7b915ec60cb2f26c58de63b0da7f1aa1f40fb609c7261" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000200000", + "out": "18da34910affcd9b1f0b80d57b2b545dbd3fcbb9327b0744553b0ae309d01f2e" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000100000", + "out": "0b07727d83b28795bd6cbbccbebea5cdedf3430c407723fd2f5270bed6f574b7" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000080000", + "out": "6580335a2908a8845a95e26e793d522451579d91c90a4c92d8667361957c4fa1" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000040000", + "out": "34b59ecbd01296ea9fe6c2f1e22ad83ae34ad9917f762e5ae194700a95f5b08b" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000020000", + "out": "8c7809b15f5525f59885e518a954871a34a4850a0c5ac531cb5564c91d10fa81" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000010000", + "out": "a1f386b9ae9b170e1f02e3fa611b991e4a383e1d998fc03f1026028b70c5fbb9" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000008000", + "out": "25e947aaa44b7574bce0d0ac4d91d63489a7837f6af73764eab3cc83eff2b01f" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000004000", + "out": "1b85d67d1481c20bacb50aea0c506affa04b258c049a8cba641dd4d3a0ece1a1" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000002000", + "out": "cbc332ec5110feae214ff569feca4bef1b3ceb809f0e2362e3924a762153785c" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000001000", + "out": "0193d8ff39177fc604d8c0e60d5495222da10cd84d4ae6d12bf84ca923158b31" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000800", + "out": "b6a210aeb8d431276712b83dfb27a338166436c37b13e533e6a664bc0696e21e" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000400", + "out": "f1337369c5303a28da9132c4562c7d1d7381e3f30575f05c72dd3e969cfca5ef" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000200", + "out": "5bf54ec3ce05919efeddee2ae288118db06a3aed340d89cfe279d0c6927cd336" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000100", + "out": "084e640fa57cbf5f097fd08636fe5e98e23839d95c532099efe1a7a838dbafca" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000080", + "out": "63794a3bf8875ac2c32ba6238d27e7e15e56a3b794b8d2d6aec82faef2360e3a" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000040", + "out": "d9ece2cd2214f52c55dcd9bca56a900ec79c1343f12df8a60f0298d255896b61" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000020", + "out": "9633190bf775667487569d0f5e7adfebc899e55ab9d62aaabe9f8754a3fa9c20" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000010", + "out": "a3ecde0c1d9daa6b7a949c87a1af7963c69cb2c412fb3086c495f14630c17b7b" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000008", + "out": "38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000004", + "out": "e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000002", + "out": "9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2" + }, + { + "in": "0000000000000000000000000000000000000000000000000000000000000001", + "out": "ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5" + }, + { + "in": "ff", + "out": "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" + }, + { + "in": "973a", + "out": "149b712a766c0a02b72c6c3a7affadd4085d7b05931b4516edc65a9eb6e22680" + }, + { + "in": "3009b8", + "out": "96a75387879e8d57480df10dce407c6ed6797eb43256f5410ede8755ea05d4a8" + }, + { + "in": "f88519bb", + "out": "9658e45d0ae5f088cd673e167919285968a2878cf9d1cc63a10f09fde37c997a" + }, + { + "in": "8701e337bb", + "out": "3a309afcfd5ed8e1ce622a8d559961e428664a6f727df4c22136fc4eba08ed23" + }, + { + "in": "0d90326391f9", + "out": "00037ad7367512d5aad2f2a804722d3155d2d0c98de3935f2174f922975ee15d" + }, + { + "in": "5d10fa95bd08f9", + "out": "57511743dc186aaff0f93b4040ab48cb49139e7c3cdee653f61b5e5f61c71d36" + }, + { + "in": "4984cb3608f9645d", + "out": "8b68120dc066c80f689de09bf75a661848657589d362fdc528a2ed63409b5dbc" + }, + { + "in": "f64202a9b1da2e688a", + "out": "4e49c74f6deca3ff64cc1280e2be7c8568a02999afb9e5c0ed9ee78322b7a9ce" + }, + { + "in": "86f8219d69cfde40a2d1", + "out": "5cf4657d7b134962d64f46dce6e09f94262123a0f693360f842345fd3f475665" + }, + { + "in": "3d075a7686d6f2b0d933d5", + "out": "476eed19a66a112b7991a04062ff281b9595a4bc45c101629e92e3dd402f8ea5" + }, + { + "in": "eb6bf5ba40801b27104a5fb3", + "out": "a1a726de33b6028f6d75beb140c0fec93abbcd0b985e6dda3bf818af8e2b7096" + }, + { + "in": "23501486108f3de2e9524c2efe", + "out": "b3883f5c7978d40cfa25f60e26af8e26480586df3bfb5f238e53f3a5f241fb92" + }, + { + "in": "6e33b6d645acd2c80cedb84500cd", + "out": "e208985459aa801305493a313178d45d74e73b26ab268ecc57762d1f219b1dbd" + }, + { + "in": "eafff26976bd4f943a705e36e62a69", + "out": "48a1434c314bc67604fcae1eff83f17cf05ae693b6427cfae3b1f6692488d8fb" + }, + { + "in": "522051983c571eff97886f19b08fbe69", + "out": "1f73010d11dbc5c283bd2ecf00b1e8d3745095260e1fcf00a3abe0f0cca4a2e5" + }, + { + "in": "fb487ffdc5115f00ee6d5b7437e990b78a", + "out": "ffe14ff1bec3d445a8e6c6f8cf66d054d9b0fd00032dd1fe7fea7defe2cb88e3" + }, + { + "in": "e5158768a77c5fb06ed06ed333aa54a6c185", + "out": "8bf02c8642733293011185538969d1799f5ce86df439930f995c4e3e530089fb" + }, + { + "in": "87583947befaad4b43b4dd48fba48d659f679c", + "out": "12b778aa6a84e7ffa6449f394e4a2f90af4bea3dc85e55ab2476216a87689b82" + }, + { + "in": "620418d700021fbe28e0173ce6102dfd3bc0a303", + "out": "186acebbf96463131aa08f68e1ecf96bd17a041bf4ff054585de252b85efcf6a" + }, + { + "in": "68f055aeb433075ce3e29c4763a49a5919d90cdccd", + "out": "dbf521256dafd25f7e35e58e3be51664f950292073a18ebd9a13e06b5b82965a" + }, + { + "in": "c1faebffad2bf41ef1b65c68632ca044ed9a572a4cb0", + "out": "b62c1a8fd2258e256b06315d1c2d98746ac18d7b170ead81999b6110ebf69601" + }, + { + "in": "5c861f96020cc754b59066431ec96978fe0ab7fd9d1d0d", + "out": "5d3f355244c3b1e8904c7bed52bfc412b5cb8922634a20088d7ee7061bf2fb33" + }, + { + "in": "9bbac4e256126423dfdea90be07315fb057f1523137c9c02", + "out": "f9508865f80493f20a01a0277bed6e111149dbd99b9808578abcc26cfe090a0a" + }, + { + "in": "ec77f4ecc1f6edfc63dffbfe2edf3fc9d348d3544a70888c1e", + "out": "af0ca3504e8364ffceaeb3601e260f0fc43f9c565e76c3434cf16166ef62cbc1" + }, + { + "in": "8758d30e8dd41c7f4449c4f719f4aafa900d836d3919f6bed77b", + "out": "ae4bb846e12b020a438565fc6001340b8c05c2f9da4ca416c60061d7f271a582" + }, + { + "in": "d4f2a3729002173ab3f226c88fbc8c6b3cf7b73f9bad2bb8888569", + "out": "a0d93864b001794ca7e99e354ddb595929863f3338c5840598988d5782bd28db" + }, + { + "in": "e4d8ebfce3d71014659ac8afff185e6c96a6d404accfa70d794ff56e", + "out": "c8a86ee18a1f5be4a513b647afde990acc08699a66862c21879de3d10e73a0fb" + }, + { + "in": "0f212467411d16e92afbfe5b622de745b41d631b2eb5477153be84ff95", + "out": "d5987e1e058d24e929c4acdb342a8ad290de72bf95d534d84b49629c4a4e4200" + }, + { + "in": "4eda3360c3e4bd5e40ed0b9efa421a43dc364752333fa9f317db86b6e5d8", + "out": "68c0c4f2390a0a5415d9acf86c659b148372ec1882687586251997c7ce519cb7" + }, + { + "in": "95a4f9b80ad217fdf65ac46bf3be11d49cf03f013e72fdf6b3d9c6be560508", + "out": "a08a038debfe196f35d8f0087f9f2fab1c0a10e3b01e6b2b80376a3da25c0b1f" + }, + { + "in": "a0fba76b5e023c7e0cda8b843b4105a7f0091092fdc254df303b3c26f4a1f8d3", + "out": "9f3d31e526c953d6d14acc8768d890f800d09ddfa361797571f30b854b53d9c0" + }, + { + "in": "e58fd27c82c2f2605600d10cdce144c54dc24f90c2d6e7ce7e8fa02fc32f247565", + "out": "95d42ca4521f927bd726fabd165f37e0da75b93407bf207eb2ccc8bd8ea1a8a9" + }, + { + "in": "7b92504499e1290321c0fc7203f6c7d9912cc4be9521aea911ab14fdbfba79bc112d", + "out": "eff00679e3b10e40e7f3f2b15070ebb8a5f0897efec7ac7ae55b39c569c2cb00" + }, + { + "in": "d515195525f3d271ca8072e23b86a00c603b0949c5a23a3e13078319046519671169dc", + "out": "d2e644512d4b18f01dce2f8481ab99af5e7bfcd0e82c67fb36eb09dfb0399858" + }, + { + "in": "c399a4765aa46378b1cb64b7bd9d84ed6e288052d1d1cf5dff6c0d0b12dad359578fff51", + "out": "f08f6b46673f1a596ee4e493cb5136dfd5020e2af13869b1c297e0341ae99de5" + }, + { + "in": "d1e565edf6f4ee2b12a16b440712d9e52d890c96f3ee91495e53156a196fbbfa46a7474f64", + "out": "b8edc023612884c4d42b55161455c250e3f560962a835a2afbdfbb25a4b51c39" + }, + { + "in": "31a55caa88925a63484e53d946b5fe86b34a221519fd341ae1e77c7c4196ddcd174f16c98263", + "out": "db84214c82f246ef781652f39e39d8594b5dee43a2d9ffa3f67b4bb11ccac40d" + }, + { + "in": "3d888a1040affd114a719d42ef733314612618aca03ab040f35ec71543ee1f02edc37ce6a84c49", + "out": "2447efdba2dd415fa6454a944c43753f9191ae5c7b0dbb065ca5c468da0e4cc7" + }, + { + "in": "0f801c7fc8ff2ee603c63cf1ae42d1dc9d73497245341986c2c39d08075e624998c47694e038806c", + "out": "4a5051fb9ad23815d88b9e003883169b25fd02bb1a06facb36aab35e252d62f0" + }, + { + "in": "bede92ec1ab0bf966c7bc098db7807b86a735a2b89ca7712f1f93b3faba69f2a867a5f361ca235a088", + "out": "39a6b566a9d5a3ab3be43d022d06a77338821a891bb952431dc500220e3d62c8" + }, + { + "in": "47b80bc42fad7773884849297b4ba941ddd42fc14fca3c39e2dfcc48c6afca7d8c9d55d5156473c3440a", + "out": "28ca6ab9e8b5aba81389ebb03273f09f54e398497632a5826b39b41920cec687" + }, + { + "in": "9522c8a3990d3d4ccb4c3d41c9b4655ddd35c33307d4efeb74c583c29bfb84a98954cc5868c23c78127589", + "out": "e0a7c694a3700fec0a16335375641371a9c2b2c68b4da08ae67ca1feada33ae8" + }, + { + "in": "169b7817ed75369341658cc854ebbc0ac11a531f94c1f4b9260732610803b0f2096a2e9a65b6c642c2546442", + "out": "12bd7465b0e681478552b94dcf6074ca70529d18492d857fc8e658c3398ae5b1" + }, + { + "in": "4f52b6723e7165f12d76a70bb311c04fda7e68faf091bcaf77065ed3e77015d82314a252efbd7cc4a92473cf60", + "out": "c0ca351e2a50e8d1e2e0e994a7f91fc6cf338822fb0fcdc388342a5f99f0a62f" + }, + { + "in": "f38b8c4fdc73f40e3253c1b3b14f196558380a3413d34924e0487f5aa77b3384e9f6e046309aa062f7c813b601e1", + "out": "6da821d8b24a88eaa765d0bfc4abc03e147e15bd62dddde622fede4532944d52" + }, + { + "in": "41705ae5a1d12fbccb4426cc811262d21c0c46dfd2dcd8ad7b92cc939b5549e063ac399a1e5613b545364f18f7bf4e", + "out": "6eeac8187bc0564f68e63cd9231ff6f352c18461907a2fb1c2de93acd44940bb" + }, + { + "in": "cc70e84af58d027f5ff15b6fde96ad30450d7f871c423a3531ae6ffadc8dc87acded57d0421ba15e237575c04abe8e12", + "out": "f0b28375878e437940d7ee4e7bc2db4c4c8b5d3b6be0c0755c10aeab00a29ca3" + }, + { + "in": "2dadcc9fd75291b810439ad51b29502464f810f66e6998f348f4e2b5d5f128570ecb1fd671c9423e71da3eb5f6df1e2f33", + "out": "510f97486b03ff7af1feca9d0308239f63b4f8349f1fe2a577cf9da93e23ac3e" + }, + { + "in": "0c086b9c06e92746e5325b86c9d8423abbee782e0cff47bfc618fcf49a1e09fb5c031089137553268d63aaee75a662d93c87", + "out": "ba5369c00659ac9c5337a38e51feae0f005b0123c9909fad5bd4605bdfe9c445" + }, + { + "in": "ddbb8c24637ff24e073e9ee299ad5e73493c6a3ad0eebe0d5130a65f3336906378a02e84b2df4d925dddbd44e84234f80b025c", + "out": "a7f11830a67d3e2c7f4a37a4b5d274d4d0f3840b86148f1af85170e230333188" + }, + { + "in": "6651a6054e16d4988752fe56b3e4088cf86ff6ca90eebc0e3e5c4a5f14bf9cf643bf8652827f439f6549351356d2761632bb14a0", + "out": "3f400ec10fb5ebd455ee9175661657ed36d44aa6ead84f3772fb7b164d40b29b" + }, + { + "in": "18532af22d7f1e2a18abc479cf6e899561c4a9a92ab27967d2e079ae69b651f01a9c95aa6908623d2ded86aed3a14e361d7acc465b", + "out": "d523310b1faf11c3fa9ffdec513ef15b41defdb877b4e9525b2519c8b1e5c909" + }, + { + "in": "c022f99c785a0f2f1210f27d94cfd60d9c99368c469882c18899f7fdf2bf12e6be231ba8f6858368678596d64328fba2870f526d4c0e", + "out": "7f9ecd5c9f908d5794a065d457dcd31dfe6469aa680f1b58c8cb666abd17c1a9" + }, + { + "in": "966d0d84d3f2bdc957935613d946509145b3345c90ba2244810ea6095eabb9c4f785868e61115b23aadb8cdcde99fc83509d52cc67d582", + "out": "bcf40210b6a8caee5751984946eebf7fb411f8e5e0fd5162e5c640e6b2a85075" + }, + { + "in": "7b5e2ff358964bce486dea8c9b85176ebe361ca9c8a81487187d72773232bf2ee9576e02568ae3cb740c05b8a2bcfd7fb9b03eafe41b9a93", + "out": "1cf74ec4b3315d56faac576d2dbaf1d62d53e0d59a256e6f5c83c496a71887a6" + }, + { + "in": "dfc064e095b6e2cc8dcee66470fe361ef1dcea43b9513e7154d2c12c357c5a1c86f59ba0837c1ad55314a5d2d36bbde483ee7700cfb0abbfc7", + "out": "5b8e6ca7ebfbf16b9ab45d9beb2789990713d61ce01823569d7b5a34cdb4b9c0" + }, + { + "in": "ea76a6e9f25f8b634f9997bdbdf34780d6d1e206c404d1adf7df45ea579d64e5eec7d847faa1423636cce2823333e0bf7b2789f9968f997b45e3", + "out": "ec7994335933794f14bfe62ed48841383b163af4d916b10c60614d1796f06d37" + }, + { + "in": "d73f2d890a180ae81922e250e43f13484ef5679bd5cf8baac362dea4fe58f60cb9cb87802d72fab4132c505695fa4e5e499b6d8f8ca5a13e57c508", + "out": "139508399c2b7ac0a49ff2b316443046860e7d932f21e7517cbbf6172542fddf" + }, + { + "in": "970dd3b6376c81f6fc633f10c32a14f5ce0dec000c4d47578c5e6ef7aee8f31fdb3767d46e1376097193e1c030d8e1ccdde953f8b0c1c78d5fa42fb8", + "out": "a5674de8d6bffaa12d400c8846e8748cb13273c649779baf62ed258a4792dc01" + }, + { + "in": "5506b56c3090990e3d33cf5e7ab66ad5a55cfed9e960d43caca74437cbbef28cf748f80a53f730e4b124b21d6c4823bd4bef135cd33e8abe6279a343ea", + "out": "e76fab992242da701c8238ff19ff2681ed304bd846df5cdc843be66dca51a5da" + }, + { + "in": "7c72ded849bbebaeb8e56854039df5b4f8713840a992d4b4643eff24f2a60573e7f89eaf10efe903116a033f63a62017196dc27d189c55841c227795958f", + "out": "98e5de57125a1dab829fc0de9cb014b60267f3a4af412beaa2417a7f9c5ccfef" + }, + { + "in": "ae4798b1ab6e006be45acc330baa0fe66cb17164b18f169026fddefb16cad07283c382008a0d7deaf08266d07627b7fa34e1299280f07ad1142a988b2061f0", + "out": "75a55f359dbfda6de4750467c18ebc50bba0f86c15ae146f00d2a319c16f1928" + }, + { + "in": "3b47876f88f7c4e7ce09f8bb35240915391cd5d335f12b40b3da5e30eb504a196b484f864929c89793f93691ad4c062ee4861e811ef2c0c047266752ba43524d", + "out": "14609c054e038a8cb4f0886de99b31307f2e707a072c674abfa646161b6bff63" + }, + { + "in": "293ba657b574043afb815d8baeb83b03499d61ee9229a32db39a8deb5edea3509d62cf54a15efefa68396940381b780410a53a1d742149729083beb205bad17205", + "out": "1a4e8df414957fac9338ad0031f77176ccce4575f40d5eeb44c18bce5a7ad812" + }, + { + "in": "087ecfd0dde2f63a7246aafe357d3168b810e47a07f8eeb24082b157cdee2b8d86723f4dcf08047e1bc238c3e649808263312fc766dd0ed9346f07b91c5e8aa2856e", + "out": "c446a79314dab7d4e93c1a06dc561c1edce3dbd41a9216e745db5cfb58b4bd10" + }, + { + "in": "c06e7a4d50f1038d0cc42b915d6d500297da6baa9311a946b381df248d5f1c9c4ffe52ba6c15f174d5a0365b193a8854c74616c4e2178e77a5286d86ea5e5e5f1d00fd", + "out": "0ee3a6c829c9a0a47d567b4add3224521ad9e2f3b7d5c1110a974c725f625b05" + }, + { + "in": "676c2841c4945535a4be0407946507018ffd9a56a05d3ccee17549038e5df00366c7aaffa9063547685f0a0d2d6bee151d923ded73d06a7a468bf79d48b93694a513d973", + "out": "65b35ba31cbb29ad6bf5c885b3fa81c8e432bf4fc35b38b1031006113cefd14e" + }, + { + "in": "eca5c2a10879dea022d18594548871f3d55310a113fa9fbc303b7fcee224aab34628637e19dd4a0128d28418630e6bd5717efacc1a944aef9529d8b014fc8049a4fa5f6b28", + "out": "70d209943c26f74c32132f6c72b387ceb8c7375e67b214930a306b4ed71117c7" + }, + { + "in": "5fca053e81d9d1165e2dcb05c61af6daee510620d12cd359f55b91cf4d26a1fb5275e787e649f79040dd2e61fe42ab9e17403120921c62ca0bba185de631e9f0629f67499ade", + "out": "abeb92ac392ac96787d04481026bec5210c96a9cc9db2735d7cca73a53eb5d5c" + }, + { + "in": "897704d8aa9a96f684a999e6e04a4e00e13c363f0dbdc4866b4cebbd0a9a181af61fbc13b546135248bf78c4cc8de88d297191489bd6d6d6e54be31550eea0b2a6ffdc78be579d", + "out": "aecc884db001ca82acb3e8568fe1934abdcadf0ed4b76ed5f68600a0671a30bb" + }, + { + "in": "069e2c17b8a2b2c69979863a53d7c5a83cae7f51cee7b496d8d6e5628239b4fbdc9692b2b2cb7663c68290117bf15efda791654f9517fe08cd96814b3202324f9bbcd7af76f408d4", + "out": "7b9f7e72e2dc21d382ef002eda3404bb4707fa085b0f8c68142a31748b78c338" + }, + { + "in": "554b3377a960eda40efea9fb9f97c68da8d9b269036558d5c40cc1d386f57c2bef45d07907b0d086bca6cb13131e9c5439cac1fc839d5a6e3c4132c5950def0dd075d38c62926bff40", + "out": "f3cd66a5aec9e1896a988e2bf1fd24a7c394f987e70edf2bf58d4bc988cb2c6f" + }, + { + "in": "5b63e62b236aaf438d7774f53b76ad569b670e6689537f51ca0ab7fe23e89283305718759ad4c588194e3b76366873aced496fb763f0d77c2e3a544aa07f470d7a4023ec39c200fa52d8", + "out": "389d006116a6b368ea275b0152e7535b6c0d0acc19fc9aa1f217bb76164a6747" + }, + { + "in": "a827215df69240b9fa27a5f0d01c81824725bb3b89ac58eaef5658657ffa5157b3ee6aefee529f7a0ee4160f5baefeefcb542dd8e96a43170b6e3bf3fbc5f41d6a00cb2c229790f801c86f", + "out": "a622312f496564e08edd2526c31d1232523bbe051baf351cb08b829b4cfec272" + }, + { + "in": "793f35fecf83366e0f252ff50b639f70988bd87441fe3f6a01f60de59e2e331fa68e9e12c20d3918f4895392051d3958fe3248cd43bf298b7f417d6448e29a7ea96d7731b7e885e014374410", + "out": "cdfdee23fad49434b697448122522cceee85abd664b30d216e923fda6bce24bb" + }, + { + "in": "c41419d5eed53e00812bb5fe549963c1e1df6a6a9450767f37774fe604033bbc2dee6348e2b2ca7e43697ccc45957ec27aa030a7c136feb81df63914cb0d38fde7f1be96009a993d62cbf27791", + "out": "e65f755c8890d288e8b234d490b5063996da0d9c47301d965f6ea87c7b1936d4" + }, + { + "in": "38a6136f07c7c11c99b2255a8e5ddf3e5c0825cfb4f9c5245e8cbe8ebffabd28cae44117f52cb704e6928b07d0eadeaa10684f8a3a9727d94d1232a4d448b65b793ed71ed666e6bb239e8a6ff8a4", + "out": "9b87fd77dacb93cf2fb316c78bdbc5e5cc7fc480718823cd16cc0cc527baac46" + }, + { + "in": "3326d34ca091609af2c5174d0d08eb2ddf0b341de4411d72be44228cbedea4047cdc388800ee6a557f260da19c23c016951970699d95d67ccff825f7d5f9c140cf484bd6ddca7e66aa8e8f3d7e0835", + "out": "b0bb87b246aad67cccb9a306e4768bad5f95e06f0e08a784b5a86dab9ba52b02" + }, + { + "in": "3bad194f1615792001aa4805f0328eca6227ad785d6217285b9dc1cb79ca71e01e95ba91597781df728f6dd4d115c1848fb8f3dcb367cda35f17cb8ee897492f8fe067130b3b40b542d7929afb4f08e3", + "out": "b1f0065c655cdd4bc3ca68bdae1194df719a6ed45f299bb597c9fed006c36b87" + }, + { + "in": "23ebd4b1d86bbf9ed8e1648b7bdbc2ec8c2f82c64018335c5c76e2cf908b11eb9c6eb2758cd0cc73f215c5979334b810f51ae39bbdcae98f8de9753e65e1c80b7606f3fcde32772a1603a4fdcd6fe3d109", + "out": "34745040df3050620965df4baa159ad79f1be50d93fd02c804c7cab741b2ae8d" + }, + { + "in": "e6e89ab035b35fd24d2efd0465854f886539b8218d1f4614c9929d72ebfb33d574ec4b97ae51238c542570729db8053bec8bf770b63f5b3332b2bb5d3f6e3fd4a942804ae06b2e9850454a28bd9c8fd9d1a8", + "out": "a93e25f37d70efd4fe4e37754083efc23ad924bb41e944cfc7befbb1d828b2d6" + }, + { + "in": "760cf0a6724676cf48cf494a4dd4b90040bf0622a3e3a6a315f587af8856b7c5899cb5c91fe499a334134ad6578ddb71cc255e087756929a972d282661b93618dfdcc9335d8c91aeeb94c7ae1d778468cd25f0", + "out": "4e77a9ee0224b965103c9125d0684b5d98c6935acd5644cae9917e834f693f9a" + }, + { + "in": "1a687b6941e5ccf1292fe0ee45aa19429a5852b4c0cd8ea6074797dbbbe796dd694c4371f6bf1d8c73b6871a8b2e7c57525a3125bc7361e77581a7e27e6d38d925ebeb3df8c46ad3f650f48c6cc7f80f2b368c17", + "out": "5b831cc26f5bbcc8b6d0a62df829e8017dfd1773a80b138b5ca4d6565ac28d4c" + }, + { + "in": "18340bcb8baef6d990d90f5cfb9fead4e380897219ae35073bb1db33f2653e58a0394dac354ddcb8444c5deb25844393ead5ee25211e944b222e9aaf73d3a6275b1f7f31459f1e6fb55c64aab1e482dd8c91fcd1c1", + "out": "dd331c4e1982f2a115b8c516156363b1c58d453fc68593038f568a61375e40e2" + }, + { + "in": "e8507672c6d566d62970ee499c67f5ac9c44fb3c5d577c78e8fbb12ab48458259765e8f2f6d0e7a343e2d20abea2b3dc094cd26379f73a79af40ebaeb0304c01da08f4ba2f682d48291c05c056617ad340b60581af70", + "out": "f211d53a0e2e172fc456f23396e3a631e1a153f179e28d06529aca5c39ceca74" + }, + { + "in": "a5e5d8f4cf5371c4702543f89ff7c1b46918288b5f06282d3e26489248b7b3e8135d9c71e9b2fa4436f004c45322da8476454b9c350e1bc477ed575504997a768b30d8ffb16023241e9c6d200819d3d9558d0af6d74211", + "out": "4da2ab81c2245fcd196d7cbc7ac5bd262e11ac7b34abb3b2e09a6534fd2a52d7" + }, + { + "in": "790168da4356e71a50575f5629fb1bce51ef4ce99e9ce9988aeceb1f1624662526a300a4ea3b68225c86bdd7ae48e2b09764e0e939a6120ec30b8563b20abbacf42849e190a3483ea681a1910fab84210dd9443331ae39d8", + "out": "727954b75ab7a1e4513b113e05055cf8a78940241e319d359b7a92a7c78eda3a" + }, + { + "in": "cd713c0d54e47c64ad36cf54769d87fc653e45b75622bd71ab444b8ada3d247cf6de7cbd68136b65fd7dcc46b829120c7d6ef12b416aa027c064f830342bd5426b91c4f4c6531779003f0f4a4850091a299c7b2d284d3cb84a", + "out": "52d52244b334667f16f8c4367c32e07b4b61d3ce1841cd0ec1128253ba225924" + }, + { + "in": "a3786164b8b87a3d4a32d1ce5151341ab96068ee556e339e519c7c83ca19aaaa0577bb6502973f8f40ce87d6279f3384201fb7817075b92423beb339043512c4fa94707ca823e3ff4eefec142eff91838651679f198f2563e67f", + "out": "f61822440c88ce3b83ce2372a2a4b201139a324e6e19ec8797667b13abe18010" + }, + { + "in": "fa6d2d40e0653597f75e394f45dc0b362b528cf2cc21a289fd280506a710e7ca844298f430caf1e4e0f021742eefcd301180a1d0a7a7fbc0dfd0db8c660efe9c833c6f1c9ff9b26eb4ce1be198487fd5127d953f6003b431560aa2", + "out": "153419b335d6ab697ee165364803812d80617dda1e4f053baaeea1ffbc148ea4" + }, + { + "in": "3050ee1dc8f4c9ae18e85bebcf8de0412b8905c1582c0864dba61bb4be0ec35792c7de9c9de93ddd2b36aa6919a5fd955871c3a47dc307bbd804dcee613d55db0953c0354fe2613072f0602e9633ff0920159a1e6364dc6f81dd07c5", + "out": "37f1d7184e1308664d81f076231e0eced20d8d1e1af28dcc26e38d24084821e1" + }, + { + "in": "27c57b7af9e3d12bd0080589e341e17d3a6005aaf22374c94c851ccd54fc8d1c5214055491032d16123c3e13bd1eb14df38dc775d04436af6ec77ff523b893df1fba68f785e77ddeab75aeba9b246901975c213f8f8946f716c657c967", + "out": "7e5cde6276be0fdb98c230b2ea39a511fac1b0cfd811a68d0f312af05d3adfe2" + }, + { + "in": "92ca69e867f9761d84cb348535ad0a256f51e3a883cb81f2cf0b98803c76c7f35f7c24f960653b133e151097cb7dcc7d2fe1c4b4b295c4b1ad09d22cfd6f46719c55c5d479ff883811ee80ab458f5a73d97b6b16a9052e02248bccd04f46", + "out": "ef177ebe105881bb17d3130a8f88883f1ee8c75b9843aa24955ccffcecf35b4b" + }, + { + "in": "af577fe64cb9e83b8db1213eb42e695361b09027fe028dae2caad2053896884d10369eda5c4e221115c306e2295845b84d37d416c24f48901ce10ba9ad9cb935182dd8c7c63e60a0c7b82a65a0c759a806a4b6f99b912f4334153a6c14850c", + "out": "c43d14a7250b39e1e39e628ed7d5dbfb1c77818ad49f959b7a99266e55ca06ea" + }, + { + "in": "d60684ad738ecea11c36540bbc74b3c098bf0ac9d930708d09777f8d0bdaead29280dd0a547dfb173a499f27ab63b47bac72a378ed7a08c7b8ce3241e559a49ce8fe8298baaac3d1df3b12cb1976c850856798c9b5424f0e93f0e2eb808e018f", + "out": "37607b2655b0c134658e9c80063f875156deda87bff4675200d3665cbec59f44" + }, + { + "in": "57c98fcdc1c6c073f316cf07b4cefb38f088e6cc29fd7504557db22a711cd71776743e9f1174d205b7a28737515bdd8ad90a68f86663ad0cc32e7ea77b8bbab2cd4555b2ac07c5550a4b3a9f6bd1a6cd4c88d309fed9434723b90e5f51c098fc59", + "out": "77e8c3f2fb95d755c575b43a86dc0b5e8d49f6dd72a79a47b0317207e92581f4" + }, + { + "in": "89d5d2f1c2906224e80d259aaf9ceb4e3eaf10b4ff871dff0fd18699773b9c200db8fa2af9a1361363d6b588b56651f3ff05990bcc57e6975ee83ff5964af4647f423a2d32bf65718a4291665b158613af16f1f048d8f5872b350bc54640da3bdf4c", + "out": "755cab9ed51681c7e71795bfdb66015123f70a8f1190d4b41f3130aace642ccc" + }, + { + "in": "23de91fbd7311a58b821b5625c7d055194dc1e5000be5d94178ad3fd67967941b5eab62a6b0f17e9635fd5c818ee6a51206a9a98b4177bce0adbd3b9844f69c941d3941a9345cafd5efbbbe70c287a6cc3063f1f941abe0229f4ce72e2bccc4e59700c", + "out": "596fbff31ff4e7163c27d3cd23d4ce7fc2a5192a22f3706f8727943fd7944e14" + }, + { + "in": "876dbcd4e797ba073549e370b4d5d8ebd74913e72006cd9763cad301765d79236d876b168144c0fe621f1e80471caeed2024758967fc653c859229824f73977e55f8cf0078e098191cd077e3ec744534965a9c8ac1a92e566cec1125e397125b193a3aa2", + "out": "72f557042ba095fb1d73eccbf2d2e70f1cebada211c513bc3ac52d2c0b356279" + }, + { + "in": "d81c941c365cd3532572caa53faf31d7c1666fe0ae5aacad7f69523386c6bf215e2aea59297b82faccbe4cadc22545d6bd882c8310d9df274d1fdec6060ce65a55303840fc7545900213b2b03032e6b1d0d8a35fab6a47772f653d1af8776705a5b4106cd8", + "out": "1d4b5dc2c54cf61381519d0b879e063d582a129dc5cff9226c4f406e28a25047" + }, + { + "in": "a841ffa8dc92fee6791f624cc8c24d4b5f43e488eb555ac4932f8fae7c9e66382968084c3c548c72164247ea3eeb3fbc6391de092a100203ffa94854e494efc102b1a8e2493f5dd501f25ce29096ac55ad85f44829e6636563ee6be0a3831295af5d09e0c37a", + "out": "4f8296d2e2e3efcf851db491b68595676fb52a022817f72a5092c7d7210fad6b" + }, + { + "in": "c4f0218c4e732a65de55d513068aca367b59cc77ac172159d10335b3b6b3cdafc497b870e5c8124826f2e8dcd819fa9daf5353ce8bfa06d0a3a51be5926bc5b1c48a7f8df8be079df93a5e31a83cee77689a9bc6a182ded5b416ab132c80953f8686297d8f5e1a", + "out": "8961c85d32aeba936958ac84d937155007a8a6c57fec71e74d5150c28c08b511" + }, + { + "in": "7268f226b77b788b1d9117158bc9afc8badd165cdfa25b0bca050be6a571c043965d3c56d876a79ebcd2c4acb35ff08c8ebed3ea56717aab0d3d14542c478878ca5eaebd216b35ee6d0acf6de10696ac58d54f073923fa94e431fadb572b840c6c713a90ef45a1e0", + "out": "d50cbd42f2c915c3f295a920e22a0304e9752b8fb2595d92472599bd67d31dff" + }, + { + "in": "c73e0b4a4053aabdf1e29a65b09d022f1394277bb21e0cf1bd1f9459bc7fb3835fba1c0b812e2867ccef884231f2ea42d9f11a689a4aea58ffe2c9a991f3bef2e779cf10d7326a3177cf14be3a607b86b4d57264dd6ae4a1e0984e520f286d865cc589ab8d56a85875", + "out": "16f36a7376ae6f436fc9b406e396e2aca7b198c75278963f2356ba8250b8fa45" + }, + { + "in": "1a3800e5f2e087bd943b82f4a8e058078e08688112b90844dbc6862cdbcb5f94ae25f751222583eddf91fac7e07873ce903bf60b1f87ede724fb000b532b556524b4f39bcc2f18050074f3e75a9eca9e18746adfc6606524969e78764bf6b00d9a6b151e51ee1c289d57", + "out": "9bf5c1147b6b51f1282f17dfcadf9a145183d59e7787d2b1b66880072354a545" + }, + { + "in": "7be4ea6fc4b5657a402cf56600a531050c2f0399e24a7355a70d0dceb2f7e9ea4d7b23ec4cf717ecdca6128e823cfa294bf77ef1130496bd55c4218ffbb5c3a13f5697ac9f274e5c023d1681c5eaa49d4242ef1c09e24ba809657ac70f14b7be3cdaaa1f9c086629c8ed33", + "out": "8e5facb215d8cda215f92e89837436a599781e4a4cf8969e01f60855e7fd0636" + }, + { + "in": "53d2469d8ab1baccbc356941d1e46b57b619256dfed64cfc57fc8c0741e402c83f7d8423f216c92d16653403373fc1badadd248e2fb09da4ed919eae75a87e4a2eebe143c12ccc1122aaab91cd1ba00cd2e767f1372b2201a163678dab11c688da47eb4e0cba81efedfd7978", + "out": "c22c82136aca63faa9e94057df45ac1f1e0a42b76aca0c09a1391a1109aae1f1" + }, + { + "in": "9ac84800c6414ad40047f215b244ae0080dbe0ad6d7df75c0080ead0b02f2fa06649f7dbadda4acba223a097bfcbf2040e13886c1733d5827d6feae5de7a60498ab0de5bacf4db7bfee612440903b91b66d836d643780af9cb6a703eadd3d9b1c66e0db2832eecbd892d86901a", + "out": "bb99e1adacb814e5a6515001edcf2444851b2dab7ee9baad7c7a6d47ec8c3ab7" + }, + { + "in": "56504f109a5e577010eb5e98c6558e231943e0cfa32c0b2b0f3c2cd6a0054a2b9e9d75fa68877767daced55851447271f3af2ccb686f129cbead4c293e20c00d52fd75d1f99b19c124c24d455526fe1df9f9d219fcfa92972cd38c83a8138874b7366662f94c0bbe5d9b8365b9fb", + "out": "ec6c767c53b9b5184d548507b866be140107590733d8e7056607d9199479731e" + }, + { + "in": "56bd9ca50966c8dc38740bacac77464870c10129f70ae0e6555ba1d10118bd050260c51ae20204be89e89ae584b7b0bd1c21f31b009298b516284bcd34c792735ae5b16fbe392d85af6528b6f262087b122bf7da5be633415934afc4bd492a6b5603ab3fa58944c6b65d327799e879", + "out": "d80bf63cf3f88309d76bee0dcff42e09c6af9a750df7328df23cd93ed6cf40ed" + }, + { + "in": "9e6683645a4134573220706f51bc6fdbb0f68ac43ca634992ece5af9c8c25686456945fdabd1796fd541576d4a7399b1048548e43354381f170038838edd5e65225f0f0d3d286b1be618b4f55f4a7b7e549ffb1025fd6d5c1591678f8ec70f45f1a7c4ee6611169ddb2d04b9532f207e", + "out": "d8cc725d6a64608d317d677560488645603c8393f46c4da329d7dae6b496adca" + }, + { + "in": "5d96be06103ae7db1a3ffeb6fd3a36767a75abbc12a2054fce11f0fdafff02a860d2e70633e37cbc7a5a2245a44850928d23278a2c77623e5e600c3317b04c7534ac373b557fd23ba7482cd984283dd8122d21aad43b0297950677ce46aed23151c5434908aa4452354810376b4b7f7b08", + "out": "d31d217a9d2529f9ed1447d0ef3299c1779d31569d9d05fdf5b3513fea6ce77e" + }, + { + "in": "d985f6c0066654a39b02e59f699fbbc50ff748c08f6850ec5bf14133e7a4620fe28fe422bd9889139c1c9ac2650680631b22b68bd034d93980b65ac4e5ebb07cf5100c9728ffcc0ff6eea69a676e4bdd0017a24771ea83661952abf4e1d0e3f7f24b7862dd9d1aaad3d24dc05914df864c89", + "out": "f1dba5717f9c7b691170f1e231fc99ffa4e104ac58b9bf0faec98dee31cbc939" + }, + { + "in": "b548c6f1220f0fa73d3ed439c1432f60a4e46ab6f83d926dc946d4c252470391ec2b44d9f1436b2c9b55f20238576020d95352180a94d6040204a74378219346a849685b7bc28bd164595e2c97329dd59631119cbb9e1d323bb1a83f7acf06f802659d1393485a7341ad04799e06b7814d0a81", + "out": "18b70142489c3c3558b779e0d4cda50350ebb1b143568a2d4f9501b0f9dc8f12" + }, + { + "in": "b60732dcfea1fdc4149f277d7025bd5884c47283b7fca241ac21e217d7deb491f94f53d629fdb6dc61302f3b7cd14aa7fd91b9e703447b42395d82322e2e4f176c6e83f0924886a2ef0ccd14cfadda53f2aba7918e8a4c8dbd3f8075648b508f2c241c15f5177dd5acbc4d12ee4002d236267482", + "out": "9ff5de03f07ca1f14f9dae20aeb83f1bc68c062f20bb71f3cbf710080ef1d1b8" + }, + { + "in": "89e1b7a0b3bdedbdbec67e260681dbcf2afcf814565a955604efff8d00409537fbc4077399f7626c11b342269a0b4b70beff1c496d7268f51af61586f179fa59b6a31ff530f3c72a7e35ca78c626c196b4f49553b7c87dfd0d6aecc7a10dcf535dcec1a63440765429294ef344ee49b68440b1ba3b", + "out": "77724ab3ba98a411d4bdea831e74fa65455367fcf893511b46deb8b69528e010" + }, + { + "in": "9919ed7c116a6b2295385cc53914134cec53a09ee205c52e5bc7aae85cdfb1e5b901b1fe3e5a4b260c826cddc7603f050cc717c4831af65e3d6df0b72d5d4dc19137e8c1ea1952b62ca0fd0924d5bd4c875be68eba59672d28f38667b612c035c60c963f989104356b237e2478667dc09eb85dc50cef", + "out": "9579cbb4cc270b4833d0d2d5a8ceed5b2a9ba197ed3925cbacec39b8ffe88eb2" + }, + { + "in": "fde621e8f6c28a3116d051ddde0ce49d951fdb32c699c794d272e2544034530d16660797bd9a2dd041aa1a52c88667c56e1972973689505216149814f8097cce392831b4f00d104837b22751fcc1b492f0f946d30c329d77e985f1d8ad2523489e980cfa25abd4cee8404262ac0bfdae59495af51de394", + "out": "84bc7e5cc1cd2451c1f4006423de65348ff516ca234d3e1db5974f2caceea7f3" + }, + { + "in": "0bd9b05363d85a8c29335b7a0fc8775a14e4b3d318a07020c1760405fed790da50429bcd4107c316f52707603af7e7283cfcca0f5d7e265b53cc1899af5c2386a8f3d30a476289255eb3a8504423e8f9afd46b71dad21bbffd6cdf0f7c37ff811805f9e9f727966ab279bec9124384ee12e91556135ac64b", + "out": "0f25eb2a310b897d9fe5296e86fb54ca90a24a33c2350f7214871a76d83acdb7" + }, + { + "in": "e01b90e74c37529ae3630977bb67d135cd40bb856ef72b5acf2cb1779cb4466b7ee622faa605d184a599f8eaffa1a4b66fa82f0f1cf3b6e8d61be885b346e6fbd0a7d3ab9001d5d092b4769149987de7aa07783e33c0bca6e04f2d51b06cb8d10fa1c6f8189e9f168f8bd1d179e736e089330d999dd1de1804", + "out": "2b202a19499aeb7ec32887183974ba38d222f1dddc1957e891c7637866c6d02b" + }, + { + "in": "69de25ed8b06c25730de7d4f0f0b721418f7da3ffd0fed184d89ec7cc68c2fff7bba7328d5646ba5e1de9a10c7d8c48738500684f1c37f3f5627085f9c532f3369c79bc20d8fc4eea6098eefe809ce2fabe6815a73ad5bc596bca47229f15915138af98ebe1fd7a9dbf47e9cfa4236b8158187c8c7d6b0aa413a", + "out": "8e1578c57746970bbcf5208fed44a07297407a3b88050c9c1b2aa4dfbc371bb3" + }, + { + "in": "ef8e3ce9e94140f5f23bdf1571a99524abbc97559c04c758ac7d38b2461ceeb8b9526069866d4debc20451ca7babe6938b619a7231c1e27af42aa0deb7533e08e768029bcd14f8e26354d672ae6633d75bca0b96aefbe1e553e3d27a77bf97b0b5dfe7801fcc98552a97e2a7ca6bff07a7dcad0b8758e00be9c9b6", + "out": "191951344b69cbab81a8c5161683d523959547443c7d991370e602e89c657d65" + }, + { + "in": "c6ba237ff92b7017164b862f6423f427a2f7fc6b9e08db177ae8a0f525b96a5f8e6d3cec815fd0bc67082bd87ee4be865d207344f75e14bab8370cbff15535fe08bf69addd46d483ca0bfe3407d067885722a644346d401811827f6ff66bf32b7bfe7ffcea9abc33d0edc38183fde134eefa3e7f35edcd8cd96ed598", + "out": "d49655944cf84e2c736caa721990d87ec4a206d97697a6e69317c60cfe2ff880" + }, + { + "in": "94a5b0b0884ec2a5b6ef06867ff449a256a905ec5353c2dde56722c1020e4b7a6c2ae6fb81907a8ea4a48fb1b5cd5821446f561488afecde2de4980cf60de4c4e8bda2e3b09fec46592134131be45b4105ddf7857bb793b94f7d58ad27d1856048f0896846791e17dbb050aa0b623884666ee13d2a0bed0963e054738e", + "out": "7715f423cb25126e8f60a84860d8e38d5a6dd405cea7b87928a855fa0ccd413a" + }, + { + "in": "04fac7ec9ee4765731c4373ba10ab30e61902fc70b6ebf653bd1396157c05ccafc821928706bbc0346e750fba1c37c4b5b930ef70967c621b786da91055ad6d8a90e374d27cc65a830bfbb7346d3f6bade676d690394af5d02588b48c9482f651f0a4c9154a93b166f4b75f867af9dd34a943bd268a69456f4753f24368c", + "out": "32f8c89ff99fc942bebc45eef397b2e6957cb87a20887c8576fb1574c509956f" + }, + { + "in": "16488821759e6757a19cbbdad74e956c576e55b76d6c652c8ab1683591e53845bd8943e6c63ee98f3735624d51975240b86043b36e904267a79679f685e1924cdb8c49700722e8b360f83b2747d00b982c568488eab5ad9d2860f20750ee11baebbe116ebeab2ba4dc12b21875019d29e78673404e6c5b2cf0174f93c41255", + "out": "9131972a3cdeaadd1bf73f449f89dc1bc8b0dfffa75ff6d32824c52695a39efa" + }, + { + "in": "075d68d52224ee85a0f029e116c1894b0c673ded797f803ea298163d316c6b59c9584a0203d08f5f79f36891fabf8430cf9212c02fb2a287dec3ddc772003167909d68e912de0192817c085a6fb729accadb2acab6d5e91abeb92f4ce68123dcd4fad9f6ed80515142ef1081981a6c2d62b1630eef02690dcc71f120e661dcd1", + "out": "c00f6356a020b716ef4bb1c89c82bf7ce6e5dd167bfbf2c81ac5df42217dc3fd" + } + ] +} diff --git a/packages/crypto/tsconfig.json b/packages/crypto/tsconfig.json new file mode 100644 index 00000000..167e8c02 --- /dev/null +++ b/packages/crypto/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declarationDir": "build/types", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/crypto/tslint.json b/packages/crypto/tslint.json new file mode 100644 index 00000000..0946f209 --- /dev/null +++ b/packages/crypto/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/crypto/typedoc.js b/packages/crypto/typedoc.js new file mode 100644 index 00000000..e2387c7d --- /dev/null +++ b/packages/crypto/typedoc.js @@ -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, +}; diff --git a/packages/crypto/types/bip39.d.ts b/packages/crypto/types/bip39.d.ts new file mode 100644 index 00000000..b7772e95 --- /dev/null +++ b/packages/crypto/types/bip39.d.ts @@ -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; + private static pbkdf2; +} diff --git a/packages/crypto/types/englishmnemonic.d.ts b/packages/crypto/types/englishmnemonic.d.ts new file mode 100644 index 00000000..a38f3c39 --- /dev/null +++ b/packages/crypto/types/englishmnemonic.d.ts @@ -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; +} diff --git a/packages/crypto/types/hash.d.ts b/packages/crypto/types/hash.d.ts new file mode 100644 index 00000000..08fe193b --- /dev/null +++ b/packages/crypto/types/hash.d.ts @@ -0,0 +1,5 @@ +export interface HashFunction { + readonly blockSize: number; + readonly update: (_: Uint8Array) => HashFunction; + readonly digest: () => Uint8Array; +} diff --git a/packages/crypto/types/hmac.d.ts b/packages/crypto/types/hmac.d.ts new file mode 100644 index 00000000..3a8c48ca --- /dev/null +++ b/packages/crypto/types/hmac.d.ts @@ -0,0 +1,11 @@ +import { HashFunction } from "./hash"; +export declare class Hmac 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; + digest(): Uint8Array; +} diff --git a/packages/crypto/types/index.d.ts b/packages/crypto/types/index.d.ts new file mode 100644 index 00000000..3eccee6d --- /dev/null +++ b/packages/crypto/types/index.d.ts @@ -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"; diff --git a/packages/crypto/types/keccak.d.ts b/packages/crypto/types/keccak.d.ts new file mode 100644 index 00000000..419fb31b --- /dev/null +++ b/packages/crypto/types/keccak.d.ts @@ -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; +} diff --git a/packages/crypto/types/libsodium.d.ts b/packages/crypto/types/libsodium.d.ts new file mode 100644 index 00000000..d045971f --- /dev/null +++ b/packages/crypto/types/libsodium.d.ts @@ -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; +} +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; + static createSignature(message: Uint8Array, keyPair: Ed25519Keypair): Promise; + static verifySignature(signature: Uint8Array, message: Uint8Array, pubkey: Uint8Array): Promise; +} +export declare class Xchacha20poly1305Ietf { + static encrypt( + message: Xchacha20poly1305IetfMessage, + key: Xchacha20poly1305IetfKey, + nonce: Xchacha20poly1305IetfNonce, + ): Promise; + static decrypt( + ciphertext: Xchacha20poly1305IetfCiphertext, + key: Xchacha20poly1305IetfKey, + nonce: Xchacha20poly1305IetfNonce, + ): Promise; +} diff --git a/packages/crypto/types/random.d.ts b/packages/crypto/types/random.d.ts new file mode 100644 index 00000000..81a523c7 --- /dev/null +++ b/packages/crypto/types/random.d.ts @@ -0,0 +1,6 @@ +export declare class Random { + /** + * Returns `count` cryptographically secure random bytes + */ + static getBytes(count: number): Uint8Array; +} diff --git a/packages/crypto/types/ripemd.d.ts b/packages/crypto/types/ripemd.d.ts new file mode 100644 index 00000000..db0d6572 --- /dev/null +++ b/packages/crypto/types/ripemd.d.ts @@ -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; +} diff --git a/packages/crypto/types/secp256k1.d.ts b/packages/crypto/types/secp256k1.d.ts new file mode 100644 index 00000000..7f0e6653 --- /dev/null +++ b/packages/crypto/types/secp256k1.d.ts @@ -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; + static createSignature(messageHash: Uint8Array, privkey: Uint8Array): Promise; + static verifySignature( + signature: Secp256k1Signature, + messageHash: Uint8Array, + pubkey: Uint8Array, + ): Promise; + static recoverPubkey(signature: ExtendedSecp256k1Signature, messageHash: Uint8Array): Uint8Array; + static compressPubkey(pubkey: Uint8Array): Uint8Array; + static trimRecoveryByte(signature: Uint8Array): Uint8Array; +} +export {}; diff --git a/packages/crypto/types/secp256k1signature.d.ts b/packages/crypto/types/secp256k1signature.d.ts new file mode 100644 index 00000000..1b7a5025 --- /dev/null +++ b/packages/crypto/types/secp256k1signature.d.ts @@ -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; +} diff --git a/packages/crypto/types/sha.d.ts b/packages/crypto/types/sha.d.ts new file mode 100644 index 00000000..a5b8b212 --- /dev/null +++ b/packages/crypto/types/sha.d.ts @@ -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; +} diff --git a/packages/crypto/types/slip10.d.ts b/packages/crypto/types/slip10.d.ts new file mode 100644 index 00000000..328116be --- /dev/null +++ b/packages/crypto/types/slip10.d.ts @@ -0,0 +1,40 @@ +import { Uint32 } from "@iov/encoding"; +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[]; diff --git a/packages/crypto/webpack.web.config.js b/packages/crypto/webpack.web.config.js new file mode 100644 index 00000000..9d5836a8 --- /dev/null +++ b/packages/crypto/webpack.web.config.js @@ -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", + }, + }, +]; diff --git a/yarn.lock b/yarn.lock index e7f945b0..8dd5abc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -984,7 +984,7 @@ dependencies: "@types/babel-types" "*" -"@types/bn.js@^4.11.6": +"@types/bn.js@*", "@types/bn.js@^4.11.6": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== @@ -1026,6 +1026,13 @@ resolved "https://registry.yarnpkg.com/@types/diff/-/diff-4.0.2.tgz#2e9bb89f9acc3ab0108f0f3dc4dbdcf2fff8a99c" integrity sha512-mIenTfsIe586/yzsyfql69KRnA75S8SVXQbTLpDejRrjH0QSJcpu3AUOi/Vjnt9IOsXKxPhJfGpQUNMueIU1fQ== +"@types/elliptic@^6.4.12": + version "6.4.12" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.12.tgz#e8add831f9cc9a88d9d84b3733ff669b68eaa124" + integrity sha512-gP1KsqoouLJGH6IJa28x7PXb3cRqh83X8HCLezd2dF+XcAIMKYv53KV+9Zn6QA561E120uOqZBQ+Jy/cl+fviw== + dependencies: + "@types/bn.js" "*" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1123,6 +1130,11 @@ "@types/abstract-leveldown" "*" "@types/node" "*" +"@types/libsodium-wrappers@^0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.7.tgz#cdb25e85458612ec80f0157c3815fac187d0b6d2" + integrity sha512-Li91pVKcLvQJK3ZolwCPo85oxf2gKBCApgnesRxYg4OVYchLXcJB2eivX8S87vfQVv6ZRnyCO1lLDosZGJfpRg== + "@types/mime@*": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" @@ -1153,6 +1165,13 @@ resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.1.tgz#33b237f3c9aff44d0f82fe63acffa4a365ef4a61" integrity sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg== +"@types/pbkdf2@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.0.0.tgz#5d9ca5f12a78a08cc89ad72883ad4a30af359229" + integrity sha512-6J6MHaAlBJC/eVMy9jOwj9oHaprfutukfW/Dyt0NEnpQ/6HN6YQrpvLwzWdWDeWZIdenjGHlbYDzyEODO5Z+2Q== + dependencies: + "@types/node" "*" + "@types/random-js@^1.0.31": version "1.0.31" resolved "https://registry.yarnpkg.com/@types/random-js/-/random-js-1.0.31.tgz#18a8bcc075afa504421e638fcbe021f27e802941" @@ -1163,6 +1182,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== +"@types/ripemd160@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/ripemd160/-/ripemd160-2.0.0.tgz#d33e49cf66edf4668828030d4aa80116bbf8ae81" + integrity sha512-LD6AO/+8cAa1ghXax9NG9iPDLPUEGB2WWPjd//04KYfXxTwHvlDEfL0NRjrM5z9XWBi6WbKw75Are0rDyn3PSA== + dependencies: + "@types/node" "*" + "@types/serve-static@*": version "1.13.3" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" @@ -1171,6 +1197,18 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/sha.js@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/sha.js/-/sha.js-2.4.0.tgz#bce682ef860b40f419d024fa08600c3b8d24bb01" + integrity sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ== + dependencies: + "@types/node" "*" + +"@types/unorm@^1.3.27": + version "1.3.28" + resolved "https://registry.yarnpkg.com/@types/unorm/-/unorm-1.3.28.tgz#580141162f2fd221faae2b2d68da6c839402c375" + integrity sha512-l3uh18vcvkQ964HSK7Tx0YbhxN/Hj+k1w3nLT08n770lngqVKljmF7Ht4e7elFbx6L2WYse97whtpJOo8MHtxQ== + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" From 3bf3f456771e92f4f9b967ada2af73ee01e568d7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 13:06:50 +0200 Subject: [PATCH 02/12] Fix linter settings in @cosmjs/crypto --- packages/crypto/package.json | 2 +- packages/crypto/src/englishmnemonic.spec.ts | 5 ----- packages/crypto/src/englishmnemonic.ts | 1 - packages/crypto/src/hmac.ts | 4 ++-- packages/crypto/src/libsodium.spec.ts | 2 +- packages/crypto/src/secp256k1.spec.ts | 2 +- packages/crypto/tslint.json | 3 --- 7 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 packages/crypto/tslint.json diff --git a/packages/crypto/package.json b/packages/crypto/package.json index f3ebc372..6c626d87 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -25,7 +25,7 @@ }, "scripts": { "docs": "shx rm -rf docs && typedoc --options typedoc.js", - "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\" && tslint -t verbose --project .", + "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", diff --git a/packages/crypto/src/englishmnemonic.spec.ts b/packages/crypto/src/englishmnemonic.spec.ts index 5b2dd7ac..f1260dc5 100644 --- a/packages/crypto/src/englishmnemonic.spec.ts +++ b/packages/crypto/src/englishmnemonic.spec.ts @@ -14,7 +14,6 @@ describe("EnglishMnemonic", () => { const checksum = new Sha256(bip39EnglishTxt).digest(); expect(checksum).toEqual(fromHex("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda")); - // tslint:disable-next-line: readonly-array const wordsFromSpec: string[] = []; let start = 0; // the start cursor marks the first byte of the word @@ -31,8 +30,6 @@ describe("EnglishMnemonic", () => { }); }); - // tslint:disable:no-unused-expression - it("works for valid inputs", () => { expect(() => { new EnglishMnemonic( @@ -234,8 +231,6 @@ describe("EnglishMnemonic", () => { ).toThrowError(/contains invalid word/i); }); - // tslint:enable:no-unused-expression - describe("toString", () => { it("works", () => { const original = diff --git a/packages/crypto/src/englishmnemonic.ts b/packages/crypto/src/englishmnemonic.ts index c2ce1da1..cceb99f0 100644 --- a/packages/crypto/src/englishmnemonic.ts +++ b/packages/crypto/src/englishmnemonic.ts @@ -28,7 +28,6 @@ export class EnglishMnemonic { } // Throws with informative error message if mnemonic is not valid - // tslint:disable-next-line:no-unused-expression bip39.mnemonicToEntropy(mnemonic); this.data = mnemonic; diff --git a/packages/crypto/src/hmac.ts b/packages/crypto/src/hmac.ts index b5c72d31..5b3e7482 100644 --- a/packages/crypto/src/hmac.ts +++ b/packages/crypto/src/hmac.ts @@ -27,9 +27,9 @@ export class Hmac implements HashFunction { key = new Uint8Array([...key, ...zeroPadding]); } - // tslint:disable-next-line:no-bitwise + // eslint-disable-next-line no-bitwise this.oKeyPad = key.map((keyByte) => keyByte ^ 0x5c); - // tslint:disable-next-line:no-bitwise + // eslint-disable-next-line no-bitwise this.iKeyPad = key.map((keyByte) => keyByte ^ 0x36); this.messageHasher = new hashFunctionConstructor(); this.blockSize = blockSize; diff --git a/packages/crypto/src/libsodium.spec.ts b/packages/crypto/src/libsodium.spec.ts index cdbaee46..d29f28f8 100644 --- a/packages/crypto/src/libsodium.spec.ts +++ b/packages/crypto/src/libsodium.spec.ts @@ -1,4 +1,4 @@ -/* tslint:disable:no-bitwise */ +/* eslint-disable no-bitwise */ import { fromHex, toAscii } from "@iov/encoding"; import { diff --git a/packages/crypto/src/secp256k1.spec.ts b/packages/crypto/src/secp256k1.spec.ts index 15a92707..6af91378 100644 --- a/packages/crypto/src/secp256k1.spec.ts +++ b/packages/crypto/src/secp256k1.spec.ts @@ -1,4 +1,4 @@ -/* tslint:disable:no-bitwise */ +/* eslint-disable no-bitwise */ import { fromHex } from "@iov/encoding"; import { Secp256k1 } from "./secp256k1"; diff --git a/packages/crypto/tslint.json b/packages/crypto/tslint.json deleted file mode 100644 index 0946f209..00000000 --- a/packages/crypto/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tslint.json" -} From 92417373cdcc4c35d2b7a17062066760a5ce7cb9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 9 Jun 2020 17:39:36 +0200 Subject: [PATCH 03/12] Use @cosmjs/crypto in this project --- packages/bcp/package.json | 2 +- packages/bcp/src/address.ts | 2 +- packages/bcp/src/caip5.ts | 2 +- packages/bcp/src/cosmosconnection.spec.ts | 2 +- packages/bcp/src/encode.ts | 2 +- packages/cli/package.json | 2 +- packages/cli/src/cli.ts | 32 +++++++++---------- packages/cosmwasm/package.json | 2 +- packages/cosmwasm/src/cosmwasmclient.spec.ts | 2 +- packages/cosmwasm/src/cosmwasmclient.ts | 2 +- packages/cosmwasm/src/restclient.spec.ts | 2 +- .../src/signingcosmwasmclient.spec.ts | 2 +- .../cosmwasm/src/signingcosmwasmclient.ts | 2 +- packages/cosmwasm/src/testutils.spec.ts | 2 +- packages/faucet/package.json | 2 +- packages/faucet/src/actions/generate.ts | 2 +- packages/faucet/src/faucet.spec.ts | 2 +- packages/faucet/src/profile.ts | 2 +- packages/sdk38/package.json | 2 +- packages/sdk38/src/address.ts | 2 +- packages/sdk38/src/cosmosclient.ts | 2 +- packages/sdk38/src/pen.spec.ts | 2 +- packages/sdk38/src/pen.ts | 2 +- packages/sdk38/src/sequence.ts | 2 +- packages/sdk38/src/testutils.spec.ts | 2 +- packages/sdk38/types/pen.d.ts | 2 +- 26 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/bcp/package.json b/packages/bcp/package.json index 22f56d73..216d6ef7 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -39,9 +39,9 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { + "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^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", diff --git a/packages/bcp/src/address.ts b/packages/bcp/src/address.ts index f77490aa..83b4c2f1 100644 --- a/packages/bcp/src/address.ts +++ b/packages/bcp/src/address.ts @@ -1,6 +1,6 @@ +import { Secp256k1 } from "@cosmjs/crypto"; 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 diff --git a/packages/bcp/src/caip5.ts b/packages/bcp/src/caip5.ts index 11438339..95de524f 100644 --- a/packages/bcp/src/caip5.ts +++ b/packages/bcp/src/caip5.ts @@ -1,5 +1,5 @@ +import { Sha256 } from "@cosmjs/crypto"; import { ChainId } from "@iov/bcp"; -import { Sha256 } from "@iov/crypto"; import { toHex, toUtf8 } from "@iov/encoding"; const hashedPrefix = "hashed-"; diff --git a/packages/bcp/src/cosmosconnection.spec.ts b/packages/bcp/src/cosmosconnection.spec.ts index e08f55a2..0f2a9036 100644 --- a/packages/bcp/src/cosmosconnection.spec.ts +++ b/packages/bcp/src/cosmosconnection.spec.ts @@ -1,3 +1,4 @@ +import { Random, Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; import { decodeSignature } from "@cosmjs/sdk38"; import { Account, @@ -17,7 +18,6 @@ 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"; diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 2eacaaef..c6861285 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { Secp256k1 } from "@cosmjs/crypto"; import { Coin, CosmosSdkTx, @@ -19,7 +20,6 @@ import { SignedTransaction, UnsignedTransaction, } from "@iov/bcp"; -import { Secp256k1 } from "@iov/crypto"; import { toBase64 } from "@iov/encoding"; import { BankToken } from "./types"; diff --git a/packages/cli/package.json b/packages/cli/package.json index 935873ef..9c0f7526 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -39,8 +39,8 @@ ], "dependencies": { "@cosmjs/cosmwasm": "^0.8.0", + "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", - "@iov/crypto": "^2.3.2", "@iov/encoding": "^2.3.2", "@iov/utils": "^2.3.2", "axios": "^0.19.2", diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index a7237820..34f58eee 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -59,6 +59,22 @@ export function main(originalArgs: readonly string[]): void { "UploadResult", ], ], + [ + "@cosmjs/crypto", + [ + "Bip39", + "Ed25519", + "Ed25519Keypair", + "EnglishMnemonic", + "Random", + "Secp256k1", + "Sha256", + "Sha512", + "Slip10", + "Slip10Curve", + "Slip10RawIndex", + ], + ], [ "@cosmjs/sdk38", [ @@ -82,22 +98,6 @@ export function main(originalArgs: readonly string[]): void { "StdTx", ], ], - [ - "@iov/crypto", - [ - "Bip39", - "Ed25519", - "Ed25519Keypair", - "EnglishMnemonic", - "Random", - "Secp256k1", - "Sha256", - "Sha512", - "Slip10", - "Slip10Curve", - "Slip10RawIndex", - ], - ], [ "@iov/encoding", [ diff --git a/packages/cosmwasm/package.json b/packages/cosmwasm/package.json index 9ce6c070..0099399f 100644 --- a/packages/cosmwasm/package.json +++ b/packages/cosmwasm/package.json @@ -36,8 +36,8 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { + "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", - "@iov/crypto": "^2.3.2", "@iov/encoding": "^2.3.2", "@iov/utils": "^2.3.2", "axios": "^0.19.0", diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index e2143f96..90541a88 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { Sha256 } from "@cosmjs/crypto"; 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 { ReadonlyDate } from "readonly-date"; diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index d22be7aa..ef24e064 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -1,3 +1,4 @@ +import { Sha256 } from "@cosmjs/crypto"; import { BroadcastMode, Coin, @@ -7,7 +8,6 @@ import { PubKey, StdTx, } from "@cosmjs/sdk38"; -import { Sha256 } from "@iov/crypto"; import { fromBase64, fromHex, toHex } from "@iov/encoding"; import { Log, parseLogs } from "./logs"; diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index ab94b1da..c73b97e9 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { Sha256 } from "@cosmjs/crypto"; import { Coin, encodeBech32Pubkey, @@ -14,7 +15,6 @@ 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 { ReadonlyDate } from "readonly-date"; diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index 75aba69a..3d701571 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -1,5 +1,5 @@ +import { Sha256 } from "@cosmjs/crypto"; import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; -import { Sha256 } from "@iov/crypto"; import { toHex } from "@iov/encoding"; import { assert } from "@iov/utils"; diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index f46e8034..9f5ad298 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -1,5 +1,5 @@ +import { Sha256 } from "@cosmjs/crypto"; 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"; diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index c4854b0f..bd106923 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -1,4 +1,4 @@ -import { Random } from "@iov/crypto"; +import { Random } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@iov/encoding"; import hackatom from "./testdata/contract.json"; diff --git a/packages/faucet/package.json b/packages/faucet/package.json index e686f072..ed9622c1 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -35,8 +35,8 @@ "test": "yarn build-or-skip && yarn test-node" }, "dependencies": { + "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", - "@iov/crypto": "^2.3.2", "@iov/encoding": "^2.3.2", "@iov/utils": "^2.3.2", "@koa/cors": "^3.0.0", diff --git a/packages/faucet/src/actions/generate.ts b/packages/faucet/src/actions/generate.ts index 46590838..096b18f7 100644 --- a/packages/faucet/src/actions/generate.ts +++ b/packages/faucet/src/actions/generate.ts @@ -1,4 +1,4 @@ -import { Bip39, Random } from "@iov/crypto"; +import { Bip39, Random } from "@cosmjs/crypto"; import * as constants from "../constants"; import { createPens } from "../profile"; diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index 9c6e8f6c..de2721b6 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -1,5 +1,5 @@ +import { Random } from "@cosmjs/crypto"; import { CosmosClient } from "@cosmjs/sdk38"; -import { Random } from "@iov/crypto"; import { Bech32 } from "@iov/encoding"; import { assert } from "@iov/utils"; diff --git a/packages/faucet/src/profile.ts b/packages/faucet/src/profile.ts index 186993f0..7aaf70e8 100644 --- a/packages/faucet/src/profile.ts +++ b/packages/faucet/src/profile.ts @@ -1,5 +1,5 @@ +import { pathToString } from "@cosmjs/crypto"; import { makeCosmoshubPath, Pen, Secp256k1Pen } from "@cosmjs/sdk38"; -import { pathToString } from "@iov/crypto"; export async function createPens( mnemonic: string, diff --git a/packages/sdk38/package.json b/packages/sdk38/package.json index 2ef9efa8..7287eacf 100644 --- a/packages/sdk38/package.json +++ b/packages/sdk38/package.json @@ -36,7 +36,7 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { - "@iov/crypto": "^2.3.2", + "@cosmjs/crypto": "^0.8.0", "@iov/encoding": "^2.3.2", "@iov/utils": "^2.3.2", "axios": "^0.19.0", diff --git a/packages/sdk38/src/address.ts b/packages/sdk38/src/address.ts index 44ab1e18..cfdd153a 100644 --- a/packages/sdk38/src/address.ts +++ b/packages/sdk38/src/address.ts @@ -1,4 +1,4 @@ -import { Ripemd160, Sha256 } from "@iov/crypto"; +import { Ripemd160, Sha256 } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@iov/encoding"; import { PubKey, pubkeyType } from "./types"; diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 3a390e6a..44232426 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -1,4 +1,4 @@ -import { Sha256 } from "@iov/crypto"; +import { Sha256 } from "@cosmjs/crypto"; import { fromBase64, toHex } from "@iov/encoding"; import { Coin } from "./coins"; diff --git a/packages/sdk38/src/pen.spec.ts b/packages/sdk38/src/pen.spec.ts index 2cb7caaf..13bf1b5c 100644 --- a/packages/sdk38/src/pen.spec.ts +++ b/packages/sdk38/src/pen.spec.ts @@ -1,4 +1,4 @@ -import { Secp256k1, Secp256k1Signature, Sha256 } from "@iov/crypto"; +import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; import { fromHex, toAscii } from "@iov/encoding"; import { Secp256k1Pen } from "./pen"; diff --git a/packages/sdk38/src/pen.ts b/packages/sdk38/src/pen.ts index 936abc0e..1c65a0da 100644 --- a/packages/sdk38/src/pen.ts +++ b/packages/sdk38/src/pen.ts @@ -7,7 +7,7 @@ import { Slip10, Slip10Curve, Slip10RawIndex, -} from "@iov/crypto"; +} from "@cosmjs/crypto"; import { rawSecp256k1PubkeyToAddress } from "./address"; import { encodeSecp256k1Signature } from "./signature"; diff --git a/packages/sdk38/src/sequence.ts b/packages/sdk38/src/sequence.ts index 5cf2a738..cc9a8a38 100644 --- a/packages/sdk38/src/sequence.ts +++ b/packages/sdk38/src/sequence.ts @@ -1,4 +1,4 @@ -import { Secp256k1, Secp256k1Signature, Sha256 } from "@iov/crypto"; +import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; import { makeSignBytes } from "./encoding"; import { decodeSignature } from "./signature"; diff --git a/packages/sdk38/src/testutils.spec.ts b/packages/sdk38/src/testutils.spec.ts index e4d7ca7b..6864b7ef 100644 --- a/packages/sdk38/src/testutils.spec.ts +++ b/packages/sdk38/src/testutils.spec.ts @@ -1,4 +1,4 @@ -import { Random } from "@iov/crypto"; +import { Random } from "@cosmjs/crypto"; import { Bech32 } from "@iov/encoding"; export function makeRandomAddress(): string { diff --git a/packages/sdk38/types/pen.d.ts b/packages/sdk38/types/pen.d.ts index 068db3cb..39e899f4 100644 --- a/packages/sdk38/types/pen.d.ts +++ b/packages/sdk38/types/pen.d.ts @@ -1,4 +1,4 @@ -import { Slip10RawIndex } from "@iov/crypto"; +import { Slip10RawIndex } from "@cosmjs/crypto"; import { StdSignature } from "./types"; export declare type PrehashType = "sha256" | "sha512" | null; /** From e582313b1717cfb3e6d597bb1cbf24218237c47d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 13:23:16 +0200 Subject: [PATCH 04/12] Add package @cosmjs/utils --- NOTICE | 3 ++ packages/utils/.eslintignore | 8 +++++ packages/utils/.gitignore | 3 ++ packages/utils/README.md | 13 ++++++++ packages/utils/jasmine-testrunner.js | 26 ++++++++++++++++ packages/utils/karma.conf.js | 45 ++++++++++++++++++++++++++++ packages/utils/nonces/README.txt | 1 + packages/utils/package.json | 40 +++++++++++++++++++++++++ packages/utils/src/assert.ts | 5 ++++ packages/utils/src/index.ts | 2 ++ packages/utils/src/sleep.spec.ts | 27 +++++++++++++++++ packages/utils/src/sleep.ts | 3 ++ packages/utils/tsconfig.json | 12 ++++++++ packages/utils/typedoc.js | 14 +++++++++ packages/utils/types/assert.d.ts | 1 + packages/utils/types/index.d.ts | 2 ++ packages/utils/types/sleep.d.ts | 1 + packages/utils/webpack.web.config.js | 18 +++++++++++ 18 files changed, 224 insertions(+) create mode 100644 packages/utils/.eslintignore create mode 100644 packages/utils/.gitignore create mode 100644 packages/utils/README.md create mode 100755 packages/utils/jasmine-testrunner.js create mode 100644 packages/utils/karma.conf.js create mode 100644 packages/utils/nonces/README.txt create mode 100644 packages/utils/package.json create mode 100644 packages/utils/src/assert.ts create mode 100644 packages/utils/src/index.ts create mode 100644 packages/utils/src/sleep.spec.ts create mode 100644 packages/utils/src/sleep.ts create mode 100644 packages/utils/tsconfig.json create mode 100644 packages/utils/typedoc.js create mode 100644 packages/utils/types/assert.d.ts create mode 100644 packages/utils/types/index.d.ts create mode 100644 packages/utils/types/sleep.d.ts create mode 100644 packages/utils/webpack.web.config.js diff --git a/NOTICE b/NOTICE index 20af5cca..7742ebc0 100644 --- a/NOTICE +++ b/NOTICE @@ -11,6 +11,9 @@ 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. + Copyright 2018-2020 IOV SAS Copyright 2020 Confio UO Copyright 2020 Simon Warta diff --git a/packages/utils/.eslintignore b/packages/utils/.eslintignore new file mode 100644 index 00000000..f373a53f --- /dev/null +++ b/packages/utils/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ + +build/ +custom_types/ +dist/ +docs/ +generated/ +types/ diff --git a/packages/utils/.gitignore b/packages/utils/.gitignore new file mode 100644 index 00000000..68bf3735 --- /dev/null +++ b/packages/utils/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +docs/ diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 00000000..af296daa --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,13 @@ +# @cosmjs/utils + +[![npm version](https://img.shields.io/npm/v/@cosmjs/utils.svg)](https://www.npmjs.com/package/@cosmjs/utils) + +Utility functions independent of blockchain applications. Primarily used for testing +but stuff like `sleep` can also be useful at runtime. + +## License + +This package is part of the cosmwasm-js repository, licensed under the Apache +License 2.0 (see +[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and +[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). diff --git a/packages/utils/jasmine-testrunner.js b/packages/utils/jasmine-testrunner.js new file mode 100755 index 00000000..9fada59b --- /dev/null +++ b/packages/utils/jasmine-testrunner.js @@ -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(); diff --git a/packages/utils/karma.conf.js b/packages/utils/karma.conf.js new file mode 100644 index 00000000..38218d78 --- /dev/null +++ b/packages/utils/karma.conf.js @@ -0,0 +1,45 @@ +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"], + + // Keep brower open for debugging. This is overridden by yarn scripts + singleRun: false, + }); +}; diff --git a/packages/utils/nonces/README.txt b/packages/utils/nonces/README.txt new file mode 100644 index 00000000..092fe732 --- /dev/null +++ b/packages/utils/nonces/README.txt @@ -0,0 +1 @@ +Directory used to trigger lerna package updates for all packages diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 00000000..afc0fe2c --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,40 @@ +{ + "name": "@cosmjs/utils", + "version": "0.8.0", + "description": "Utility tools, primarily for testing code", + "contributors": ["IOV SAS "], + "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/cosmwasm-js/tree/master/packages/utils" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "docs": "shx rm -rf docs && typedoc --options typedoc.js", + "format": "prettier --write --loglevel warn \"./src/**/*.ts\"", + "format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"", + "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"", + "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", + "test-node": "node jasmine-testrunner.js", + "test": "yarn build-or-skip && yarn test-node", + "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.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" + } +} diff --git a/packages/utils/src/assert.ts b/packages/utils/src/assert.ts new file mode 100644 index 00000000..0ab49030 --- /dev/null +++ b/packages/utils/src/assert.ts @@ -0,0 +1,5 @@ +export function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new Error(msg || "condition is not truthy"); + } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 00000000..3e156674 --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,2 @@ +export { assert } from "./assert"; +export { sleep } from "./sleep"; diff --git a/packages/utils/src/sleep.spec.ts b/packages/utils/src/sleep.spec.ts new file mode 100644 index 00000000..579890ca --- /dev/null +++ b/packages/utils/src/sleep.spec.ts @@ -0,0 +1,27 @@ +import { sleep } from "./sleep"; + +describe("sleep", () => { + it("resolves after at least x milliseconds", async () => { + for (const x of [10, 30, 120, 280]) { + const start = Date.now(); + await sleep(x); + const sleepingTime = Date.now() - start; + + // Add 1 ms safety margin due to rounding issues. The elapsed time between + // timestamps 1000 and 1010 can be somethting between 10 and 11 ms. + expect(sleepingTime + 1).toBeGreaterThanOrEqual(x); + } + }); + + it("resolves within a reasonable amount of time >= x milliseconds", async () => { + // Don't be too strict as jest will run many tests at the same time and test systems can be slow sometimes. + const tolerance = 30; // ms + + for (const x of [10, 30, 120, 280]) { + const start = Date.now(); + await sleep(x); + const sleepingTime = Date.now() - start; + expect(sleepingTime).toBeLessThanOrEqual(x + tolerance); + } + }); +}); diff --git a/packages/utils/src/sleep.ts b/packages/utils/src/sleep.ts new file mode 100644 index 00000000..f9a5c4a7 --- /dev/null +++ b/packages/utils/src/sleep.ts @@ -0,0 +1,3 @@ +export async function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 00000000..167e8c02 --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declarationDir": "build/types", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/utils/typedoc.js b/packages/utils/typedoc.js new file mode 100644 index 00000000..e2387c7d --- /dev/null +++ b/packages/utils/typedoc.js @@ -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, +}; diff --git a/packages/utils/types/assert.d.ts b/packages/utils/types/assert.d.ts new file mode 100644 index 00000000..2e9da4bf --- /dev/null +++ b/packages/utils/types/assert.d.ts @@ -0,0 +1 @@ +export declare function assert(condition: any, msg?: string): asserts condition; diff --git a/packages/utils/types/index.d.ts b/packages/utils/types/index.d.ts new file mode 100644 index 00000000..3e156674 --- /dev/null +++ b/packages/utils/types/index.d.ts @@ -0,0 +1,2 @@ +export { assert } from "./assert"; +export { sleep } from "./sleep"; diff --git a/packages/utils/types/sleep.d.ts b/packages/utils/types/sleep.d.ts new file mode 100644 index 00000000..deb121ba --- /dev/null +++ b/packages/utils/types/sleep.d.ts @@ -0,0 +1 @@ +export declare function sleep(ms: number): Promise; diff --git a/packages/utils/webpack.web.config.js b/packages/utils/webpack.web.config.js new file mode 100644 index 00000000..3e0a32a6 --- /dev/null +++ b/packages/utils/webpack.web.config.js @@ -0,0 +1,18 @@ +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", + }, + plugins: [], + }, +]; From 4cca97651b037c88c6d501e7f273fda219d6a8a8 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 9 Jun 2020 17:45:48 +0200 Subject: [PATCH 05/12] Use @cosmjs/utils in this project --- packages/bcp/package.json | 2 +- packages/bcp/src/cosmosconnection.spec.ts | 2 +- packages/cli/package.json | 2 +- packages/cli/src/cli.ts | 2 +- packages/cosmwasm/package.json | 2 +- packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts | 2 +- packages/cosmwasm/src/cosmwasmclient.spec.ts | 2 +- packages/cosmwasm/src/restclient.spec.ts | 2 +- packages/cosmwasm/src/signingcosmwasmclient.spec.ts | 2 +- packages/faucet/package.json | 2 +- packages/faucet/src/faucet.spec.ts | 2 +- packages/faucet/src/faucet.ts | 2 +- packages/sdk38/package.json | 2 +- packages/sdk38/src/cosmosclient.searchtx.spec.ts | 2 +- packages/sdk38/src/cosmosclient.spec.ts | 2 +- packages/sdk38/src/restclient.spec.ts | 2 +- packages/sdk38/src/signingcosmosclient.spec.ts | 2 +- yarn.lock | 5 ----- 18 files changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/bcp/package.json b/packages/bcp/package.json index 216d6ef7..1b26354d 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -41,10 +41,10 @@ "dependencies": { "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", + "@cosmjs/utils": "^0.8.0", "@iov/bcp": "^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", diff --git a/packages/bcp/src/cosmosconnection.spec.ts b/packages/bcp/src/cosmosconnection.spec.ts index 0f2a9036..e47758dc 100644 --- a/packages/bcp/src/cosmosconnection.spec.ts +++ b/packages/bcp/src/cosmosconnection.spec.ts @@ -1,5 +1,6 @@ import { Random, Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; import { decodeSignature } from "@cosmjs/sdk38"; +import { assert } from "@cosmjs/utils"; import { Account, Address, @@ -20,7 +21,6 @@ import { } from "@iov/bcp"; 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"; diff --git a/packages/cli/package.json b/packages/cli/package.json index 9c0f7526..3e417545 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -41,8 +41,8 @@ "@cosmjs/cosmwasm": "^0.8.0", "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", + "@cosmjs/utils": "^0.8.0", "@iov/encoding": "^2.3.2", - "@iov/utils": "^2.3.2", "axios": "^0.19.2", "babylon": "^6.18.0", "colors": "^1.3.3", diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 34f58eee..6b960ef5 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -118,7 +118,7 @@ export function main(originalArgs: readonly string[]): void { "Uint64", ], ], - ["@iov/utils", ["assert", "sleep"]], + ["@cosmjs/utils", ["assert", "sleep"]], ]); console.info(colors.green("Initializing session for you. Have fun!")); diff --git a/packages/cosmwasm/package.json b/packages/cosmwasm/package.json index 0099399f..ccf45069 100644 --- a/packages/cosmwasm/package.json +++ b/packages/cosmwasm/package.json @@ -38,8 +38,8 @@ "dependencies": { "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", + "@cosmjs/utils": "^0.8.0", "@iov/encoding": "^2.3.2", - "@iov/utils": "^2.3.2", "axios": "^0.19.0", "fast-deep-equal": "^3.1.1", "pako": "^1.0.11" diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index f278f0ba..ca14c2d1 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Coin, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38"; +import { assert, sleep } from "@cosmjs/utils"; import { Uint53 } from "@iov/encoding"; -import { assert, sleep } from "@iov/utils"; import { CosmWasmClient } from "./cosmwasmclient"; import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs"; diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 90541a88..744f5397 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Sha256 } from "@cosmjs/crypto"; import { makeSignBytes, MsgSend, Secp256k1Pen, StdFee } from "@cosmjs/sdk38"; +import { assert, sleep } from "@cosmjs/utils"; import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@iov/encoding"; -import { assert, sleep } from "@iov/utils"; import { ReadonlyDate } from "readonly-date"; import { Code, CosmWasmClient, PrivateCosmWasmClient } from "./cosmwasmclient"; diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index c73b97e9..3789f60b 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -15,8 +15,8 @@ import { StdSignature, StdTx, } from "@cosmjs/sdk38"; +import { assert, sleep } from "@cosmjs/utils"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@iov/encoding"; -import { assert, sleep } from "@iov/utils"; import { ReadonlyDate } from "readonly-date"; import { findAttribute, parseLogs } from "./logs"; diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index 3d701571..69d4995f 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -1,7 +1,7 @@ import { Sha256 } from "@cosmjs/crypto"; import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; +import { assert } from "@cosmjs/utils"; import { toHex } from "@iov/encoding"; -import { assert } from "@iov/utils"; import { PrivateCosmWasmClient } from "./cosmwasmclient"; import { RestClient } from "./restclient"; diff --git a/packages/faucet/package.json b/packages/faucet/package.json index ed9622c1..dcaae4ff 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -37,8 +37,8 @@ "dependencies": { "@cosmjs/crypto": "^0.8.0", "@cosmjs/sdk38": "^0.8.0", + "@cosmjs/utils": "^0.8.0", "@iov/encoding": "^2.3.2", - "@iov/utils": "^2.3.2", "@koa/cors": "^3.0.0", "axios": "^0.19.0", "koa": "^2.11.0", diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index de2721b6..2809cd1c 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -1,7 +1,7 @@ import { Random } from "@cosmjs/crypto"; import { CosmosClient } from "@cosmjs/sdk38"; +import { assert } from "@cosmjs/utils"; import { Bech32 } from "@iov/encoding"; -import { assert } from "@iov/utils"; import { Faucet } from "./faucet"; import { TokenConfiguration } from "./types"; diff --git a/packages/faucet/src/faucet.ts b/packages/faucet/src/faucet.ts index 9fe0655e..6e9f78fd 100644 --- a/packages/faucet/src/faucet.ts +++ b/packages/faucet/src/faucet.ts @@ -1,5 +1,5 @@ import { CosmosClient, Pen, SigningCosmosClient } from "@cosmjs/sdk38"; -import { sleep } from "@iov/utils"; +import { sleep } from "@cosmjs/utils"; import { debugAccount, logAccountsState, logSendJob } from "./debugging"; import { createPens } from "./profile"; diff --git a/packages/sdk38/package.json b/packages/sdk38/package.json index 7287eacf..3b655453 100644 --- a/packages/sdk38/package.json +++ b/packages/sdk38/package.json @@ -37,8 +37,8 @@ }, "dependencies": { "@cosmjs/crypto": "^0.8.0", + "@cosmjs/utils": "^0.8.0", "@iov/encoding": "^2.3.2", - "@iov/utils": "^2.3.2", "axios": "^0.19.0", "fast-deep-equal": "^3.1.1" }, diff --git a/packages/sdk38/src/cosmosclient.searchtx.spec.ts b/packages/sdk38/src/cosmosclient.searchtx.spec.ts index 3cf19b05..25d06700 100644 --- a/packages/sdk38/src/cosmosclient.searchtx.spec.ts +++ b/packages/sdk38/src/cosmosclient.searchtx.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { assert, sleep } from "@cosmjs/utils"; import { Uint53 } from "@iov/encoding"; -import { assert, sleep } from "@iov/utils"; import { Coin } from "./coins"; import { CosmosClient } from "./cosmosclient"; diff --git a/packages/sdk38/src/cosmosclient.spec.ts b/packages/sdk38/src/cosmosclient.spec.ts index 12bce12b..3f777899 100644 --- a/packages/sdk38/src/cosmosclient.spec.ts +++ b/packages/sdk38/src/cosmosclient.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { sleep } from "@iov/utils"; +import { sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; import { CosmosClient, PrivateCosmWasmClient } from "./cosmosclient"; diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index 3e919e14..b014803a 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { assert, sleep } from "@cosmjs/utils"; import { fromBase64 } from "@iov/encoding"; -import { assert, sleep } from "@iov/utils"; import { ReadonlyDate } from "readonly-date"; import { rawSecp256k1PubkeyToAddress } from "./address"; diff --git a/packages/sdk38/src/signingcosmosclient.spec.ts b/packages/sdk38/src/signingcosmosclient.spec.ts index 5054d925..f2c186ba 100644 --- a/packages/sdk38/src/signingcosmosclient.spec.ts +++ b/packages/sdk38/src/signingcosmosclient.spec.ts @@ -1,4 +1,4 @@ -import { assert } from "@iov/utils"; +import { assert } from "@cosmjs/utils"; import { Coin } from "./coins"; import { PrivateCosmWasmClient } from "./cosmosclient"; diff --git a/yarn.lock b/yarn.lock index 8dd5abc7..3c9a67f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -157,11 +157,6 @@ dependencies: xstream "^11.10.0" -"@iov/utils@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@iov/utils/-/utils-2.3.2.tgz#a499ec304b4febaeb3af309dedbb30e14a09c91e" - integrity sha512-mtdZ8zh/LGjwA72HofOc8JF3KN1Rc1jwaQATePLDwIIJRw0AJXx2GLRBBRjja41huuw9ND0E2mQWlYLtYsNnUA== - "@koa/cors@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.0.0.tgz#df021b4df2dadf1e2b04d7c8ddf93ba2d42519cb" From 3cfad7a5415f9094fb862a7ab4fa8710d6ea1ad1 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 13:58:22 +0200 Subject: [PATCH 06/12] Add @cosmjs/encoding and @cosmjs/math --- NOTICE | 3 + packages/encoding/.eslintignore | 8 + packages/encoding/.gitignore | 3 + packages/encoding/README.md | 24 ++ packages/encoding/jasmine-testrunner.js | 26 ++ packages/encoding/karma.conf.js | 47 +++ packages/encoding/nonces/README.txt | 1 + packages/encoding/package.json | 47 +++ packages/encoding/src/ascii.spec.ts | 26 ++ packages/encoding/src/ascii.ts | 31 ++ packages/encoding/src/base64.spec.ts | 65 ++++ packages/encoding/src/base64.ts | 12 + packages/encoding/src/bech32.spec.ts | 19 + packages/encoding/src/bech32.ts | 16 + packages/encoding/src/hex.spec.ts | 44 +++ packages/encoding/src/hex.ts | 23 ++ packages/encoding/src/index.ts | 6 + packages/encoding/src/rfc3339.spec.ts | 178 +++++++++ packages/encoding/src/rfc3339.ts | 58 +++ packages/encoding/src/utf8.spec.ts | 62 ++++ packages/encoding/src/utf8.ts | 36 ++ packages/encoding/tsconfig.json | 12 + packages/encoding/typedoc.js | 14 + packages/encoding/types/ascii.d.ts | 2 + packages/encoding/types/base64.d.ts | 2 + packages/encoding/types/bech32.d.ts | 9 + packages/encoding/types/hex.d.ts | 2 + packages/encoding/types/index.d.ts | 6 + packages/encoding/types/rfc3339.d.ts | 3 + packages/encoding/types/utf8.d.ts | 2 + packages/encoding/webpack.web.config.js | 17 + packages/math/.eslintignore | 8 + packages/math/.gitignore | 3 + packages/math/README.md | 10 + packages/math/jasmine-testrunner.js | 26 ++ packages/math/karma.conf.js | 47 +++ packages/math/nonces/README.txt | 1 + packages/math/package.json | 49 +++ packages/math/src/decimal.spec.ts | 213 +++++++++++ packages/math/src/decimal.ts | 121 ++++++ packages/math/src/index.ts | 2 + packages/math/src/integers.spec.ts | 475 ++++++++++++++++++++++++ packages/math/src/integers.ts | 212 +++++++++++ packages/math/tsconfig.json | 12 + packages/math/typedoc.js | 14 + packages/math/types/decimal.d.ts | 26 ++ packages/math/types/index.d.ts | 2 + packages/math/types/integers.d.ts | 44 +++ packages/math/webpack.web.config.js | 17 + packages/utils/src/index.ts | 1 + packages/utils/src/typechecks.spec.ts | 58 +++ packages/utils/src/typechecks.ts | 26 ++ yarn.lock | 5 + 53 files changed, 2176 insertions(+) create mode 100644 packages/encoding/.eslintignore create mode 100644 packages/encoding/.gitignore create mode 100644 packages/encoding/README.md create mode 100755 packages/encoding/jasmine-testrunner.js create mode 100644 packages/encoding/karma.conf.js create mode 100644 packages/encoding/nonces/README.txt create mode 100644 packages/encoding/package.json create mode 100644 packages/encoding/src/ascii.spec.ts create mode 100644 packages/encoding/src/ascii.ts create mode 100644 packages/encoding/src/base64.spec.ts create mode 100644 packages/encoding/src/base64.ts create mode 100644 packages/encoding/src/bech32.spec.ts create mode 100644 packages/encoding/src/bech32.ts create mode 100644 packages/encoding/src/hex.spec.ts create mode 100644 packages/encoding/src/hex.ts create mode 100644 packages/encoding/src/index.ts create mode 100644 packages/encoding/src/rfc3339.spec.ts create mode 100644 packages/encoding/src/rfc3339.ts create mode 100644 packages/encoding/src/utf8.spec.ts create mode 100644 packages/encoding/src/utf8.ts create mode 100644 packages/encoding/tsconfig.json create mode 100644 packages/encoding/typedoc.js create mode 100644 packages/encoding/types/ascii.d.ts create mode 100644 packages/encoding/types/base64.d.ts create mode 100644 packages/encoding/types/bech32.d.ts create mode 100644 packages/encoding/types/hex.d.ts create mode 100644 packages/encoding/types/index.d.ts create mode 100644 packages/encoding/types/rfc3339.d.ts create mode 100644 packages/encoding/types/utf8.d.ts create mode 100644 packages/encoding/webpack.web.config.js create mode 100644 packages/math/.eslintignore create mode 100644 packages/math/.gitignore create mode 100644 packages/math/README.md create mode 100755 packages/math/jasmine-testrunner.js create mode 100644 packages/math/karma.conf.js create mode 100644 packages/math/nonces/README.txt create mode 100644 packages/math/package.json create mode 100644 packages/math/src/decimal.spec.ts create mode 100644 packages/math/src/decimal.ts create mode 100644 packages/math/src/index.ts create mode 100644 packages/math/src/integers.spec.ts create mode 100644 packages/math/src/integers.ts create mode 100644 packages/math/tsconfig.json create mode 100644 packages/math/typedoc.js create mode 100644 packages/math/types/decimal.d.ts create mode 100644 packages/math/types/index.d.ts create mode 100644 packages/math/types/integers.d.ts create mode 100644 packages/math/webpack.web.config.js create mode 100644 packages/utils/src/typechecks.spec.ts create mode 100644 packages/utils/src/typechecks.ts diff --git a/NOTICE b/NOTICE index 7742ebc0..865070d5 100644 --- a/NOTICE +++ b/NOTICE @@ -14,6 +14,9 @@ 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 diff --git a/packages/encoding/.eslintignore b/packages/encoding/.eslintignore new file mode 100644 index 00000000..f373a53f --- /dev/null +++ b/packages/encoding/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ + +build/ +custom_types/ +dist/ +docs/ +generated/ +types/ diff --git a/packages/encoding/.gitignore b/packages/encoding/.gitignore new file mode 100644 index 00000000..68bf3735 --- /dev/null +++ b/packages/encoding/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +docs/ diff --git a/packages/encoding/README.md b/packages/encoding/README.md new file mode 100644 index 00000000..c8845fa4 --- /dev/null +++ b/packages/encoding/README.md @@ -0,0 +1,24 @@ +# @cosmjs/encoding + +[![npm version](https://img.shields.io/npm/v/@cosmjs/encoding.svg)](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 cosmwasm-js repository, licensed under the Apache +License 2.0 (see +[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and +[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). diff --git a/packages/encoding/jasmine-testrunner.js b/packages/encoding/jasmine-testrunner.js new file mode 100755 index 00000000..9fada59b --- /dev/null +++ b/packages/encoding/jasmine-testrunner.js @@ -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(); diff --git a/packages/encoding/karma.conf.js b/packages/encoding/karma.conf.js new file mode 100644 index 00000000..006da5fe --- /dev/null +++ b/packages/encoding/karma.conf.js @@ -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, + }); +}; diff --git a/packages/encoding/nonces/README.txt b/packages/encoding/nonces/README.txt new file mode 100644 index 00000000..092fe732 --- /dev/null +++ b/packages/encoding/nonces/README.txt @@ -0,0 +1 @@ +Directory used to trigger lerna package updates for all packages diff --git a/packages/encoding/package.json b/packages/encoding/package.json new file mode 100644 index 00000000..5eedd106 --- /dev/null +++ b/packages/encoding/package.json @@ -0,0 +1,47 @@ +{ + "name": "@cosmjs/encoding", + "version": "0.8.0", + "description": "Encoding helpers for blockchain projects", + "contributors": ["IOV SAS "], + "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/cosmwasm-js/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\"", + "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" + } +} diff --git a/packages/encoding/src/ascii.spec.ts b/packages/encoding/src/ascii.spec.ts new file mode 100644 index 00000000..a6942ec1 --- /dev/null +++ b/packages/encoding/src/ascii.spec.ts @@ -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(); + }); +}); diff --git a/packages/encoding/src/ascii.ts b/packages/encoding/src/ascii.ts new file mode 100644 index 00000000..d488317d --- /dev/null +++ b/packages/encoding/src/ascii.ts @@ -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(""); +} diff --git a/packages/encoding/src/base64.spec.ts b/packages/encoding/src/base64.spec.ts new file mode 100644 index 00000000..f0f59039 --- /dev/null +++ b/packages/encoding/src/base64.spec.ts @@ -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(); + }); +}); diff --git a/packages/encoding/src/base64.ts b/packages/encoding/src/base64.ts new file mode 100644 index 00000000..df25db1b --- /dev/null +++ b/packages/encoding/src/base64.ts @@ -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); +} diff --git a/packages/encoding/src/bech32.spec.ts b/packages/encoding/src/bech32.spec.ts new file mode 100644 index 00000000..59e6d2c5 --- /dev/null +++ b/packages/encoding/src/bech32.spec.ts @@ -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, + }); + }); +}); diff --git a/packages/encoding/src/bech32.ts b/packages/encoding/src/bech32.ts new file mode 100644 index 00000000..38fedde4 --- /dev/null +++ b/packages/encoding/src/bech32.ts @@ -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)), + }; + } +} diff --git a/packages/encoding/src/hex.spec.ts b/packages/encoding/src/hex.spec.ts new file mode 100644 index 00000000..7a7eed0c --- /dev/null +++ b/packages/encoding/src/hex.spec.ts @@ -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", + ); + }); +}); diff --git a/packages/encoding/src/hex.ts b/packages/encoding/src/hex.ts new file mode 100644 index 00000000..388346c0 --- /dev/null +++ b/packages/encoding/src/hex.ts @@ -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); +} diff --git a/packages/encoding/src/index.ts b/packages/encoding/src/index.ts new file mode 100644 index 00000000..6fb102e2 --- /dev/null +++ b/packages/encoding/src/index.ts @@ -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"; diff --git a/packages/encoding/src/rfc3339.spec.ts b/packages/encoding/src/rfc3339.spec.ts new file mode 100644 index 00000000..227f1e32 --- /dev/null +++ b/packages/encoding/src/rfc3339.spec.ts @@ -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"); + }); +}); diff --git a/packages/encoding/src/rfc3339.ts b/packages/encoding/src/rfc3339.ts new file mode 100644 index 00000000..1b58fe14 --- /dev/null +++ b/packages/encoding/src/rfc3339.ts @@ -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`; +} diff --git a/packages/encoding/src/utf8.spec.ts b/packages/encoding/src/utf8.spec.ts new file mode 100644 index 00000000..be52ef9d --- /dev/null +++ b/packages/encoding/src/utf8.spec.ts @@ -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(); + }); +}); diff --git a/packages/encoding/src/utf8.ts b/packages/encoding/src/utf8.ts new file mode 100644 index 00000000..75b16d55 --- /dev/null +++ b/packages/encoding/src/utf8.ts @@ -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"); +} diff --git a/packages/encoding/tsconfig.json b/packages/encoding/tsconfig.json new file mode 100644 index 00000000..167e8c02 --- /dev/null +++ b/packages/encoding/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declarationDir": "build/types", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/encoding/typedoc.js b/packages/encoding/typedoc.js new file mode 100644 index 00000000..e2387c7d --- /dev/null +++ b/packages/encoding/typedoc.js @@ -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, +}; diff --git a/packages/encoding/types/ascii.d.ts b/packages/encoding/types/ascii.d.ts new file mode 100644 index 00000000..42d32698 --- /dev/null +++ b/packages/encoding/types/ascii.d.ts @@ -0,0 +1,2 @@ +export declare function toAscii(input: string): Uint8Array; +export declare function fromAscii(data: Uint8Array): string; diff --git a/packages/encoding/types/base64.d.ts b/packages/encoding/types/base64.d.ts new file mode 100644 index 00000000..3eb3915c --- /dev/null +++ b/packages/encoding/types/base64.d.ts @@ -0,0 +1,2 @@ +export declare function toBase64(data: Uint8Array): string; +export declare function fromBase64(base64String: string): Uint8Array; diff --git a/packages/encoding/types/bech32.d.ts b/packages/encoding/types/bech32.d.ts new file mode 100644 index 00000000..8ec29b94 --- /dev/null +++ b/packages/encoding/types/bech32.d.ts @@ -0,0 +1,9 @@ +export declare class Bech32 { + static encode(prefix: string, data: Uint8Array): string; + static decode( + address: string, + ): { + readonly prefix: string; + readonly data: Uint8Array; + }; +} diff --git a/packages/encoding/types/hex.d.ts b/packages/encoding/types/hex.d.ts new file mode 100644 index 00000000..a337851f --- /dev/null +++ b/packages/encoding/types/hex.d.ts @@ -0,0 +1,2 @@ +export declare function toHex(data: Uint8Array): string; +export declare function fromHex(hexstring: string): Uint8Array; diff --git a/packages/encoding/types/index.d.ts b/packages/encoding/types/index.d.ts new file mode 100644 index 00000000..6fb102e2 --- /dev/null +++ b/packages/encoding/types/index.d.ts @@ -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"; diff --git a/packages/encoding/types/rfc3339.d.ts b/packages/encoding/types/rfc3339.d.ts new file mode 100644 index 00000000..e19a9e8d --- /dev/null +++ b/packages/encoding/types/rfc3339.d.ts @@ -0,0 +1,3 @@ +import { ReadonlyDate } from "readonly-date"; +export declare function fromRfc3339(str: string): ReadonlyDate; +export declare function toRfc3339(date: Date | ReadonlyDate): string; diff --git a/packages/encoding/types/utf8.d.ts b/packages/encoding/types/utf8.d.ts new file mode 100644 index 00000000..53df44c1 --- /dev/null +++ b/packages/encoding/types/utf8.d.ts @@ -0,0 +1,2 @@ +export declare function toUtf8(str: string): Uint8Array; +export declare function fromUtf8(data: Uint8Array): string; diff --git a/packages/encoding/webpack.web.config.js b/packages/encoding/webpack.web.config.js new file mode 100644 index 00000000..9d5836a8 --- /dev/null +++ b/packages/encoding/webpack.web.config.js @@ -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", + }, + }, +]; diff --git a/packages/math/.eslintignore b/packages/math/.eslintignore new file mode 100644 index 00000000..f373a53f --- /dev/null +++ b/packages/math/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ + +build/ +custom_types/ +dist/ +docs/ +generated/ +types/ diff --git a/packages/math/.gitignore b/packages/math/.gitignore new file mode 100644 index 00000000..68bf3735 --- /dev/null +++ b/packages/math/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +docs/ diff --git a/packages/math/README.md b/packages/math/README.md new file mode 100644 index 00000000..36092095 --- /dev/null +++ b/packages/math/README.md @@ -0,0 +1,10 @@ +# @cosmjs/math + +[![npm version](https://img.shields.io/npm/v/@cosmjs/math.svg)](https://www.npmjs.com/package/@cosmjs/math) + +## License + +This package is part of the cosmwasm-js repository, licensed under the Apache +License 2.0 (see +[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and +[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). diff --git a/packages/math/jasmine-testrunner.js b/packages/math/jasmine-testrunner.js new file mode 100755 index 00000000..9fada59b --- /dev/null +++ b/packages/math/jasmine-testrunner.js @@ -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(); diff --git a/packages/math/karma.conf.js b/packages/math/karma.conf.js new file mode 100644 index 00000000..006da5fe --- /dev/null +++ b/packages/math/karma.conf.js @@ -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, + }); +}; diff --git a/packages/math/nonces/README.txt b/packages/math/nonces/README.txt new file mode 100644 index 00000000..092fe732 --- /dev/null +++ b/packages/math/nonces/README.txt @@ -0,0 +1 @@ +Directory used to trigger lerna package updates for all packages diff --git a/packages/math/package.json b/packages/math/package.json new file mode 100644 index 00000000..b1c39533 --- /dev/null +++ b/packages/math/package.json @@ -0,0 +1,49 @@ +{ + "name": "@cosmjs/math", + "version": "0.8.0", + "description": "Math helpers for blockchain projects", + "contributors": ["IOV SAS "], + "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/cosmwasm-js/tree/master/packages/math" + }, + "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\"", + "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", + "bn.js": "^4.11.8", + "readonly-date": "^1.0.0" + }, + "devDependencies": { + "@types/base64-js": "^1.2.5", + "@types/bn.js": "^4.11.6" + } +} diff --git a/packages/math/src/decimal.spec.ts b/packages/math/src/decimal.spec.ts new file mode 100644 index 00000000..f0d6f1d3 --- /dev/null +++ b/packages/math/src/decimal.spec.ts @@ -0,0 +1,213 @@ +import { Decimal } from "./decimal"; + +describe("Decimal", () => { + describe("fromAtomics", () => { + it("leads to correct atomics value", () => { + expect(Decimal.fromAtomics("1", 0).atomics).toEqual("1"); + expect(Decimal.fromAtomics("1", 1).atomics).toEqual("1"); + expect(Decimal.fromAtomics("1", 2).atomics).toEqual("1"); + + expect(Decimal.fromAtomics("1", 5).atomics).toEqual("1"); + expect(Decimal.fromAtomics("2", 5).atomics).toEqual("2"); + expect(Decimal.fromAtomics("3", 5).atomics).toEqual("3"); + expect(Decimal.fromAtomics("10", 5).atomics).toEqual("10"); + expect(Decimal.fromAtomics("20", 5).atomics).toEqual("20"); + expect(Decimal.fromAtomics("30", 5).atomics).toEqual("30"); + expect(Decimal.fromAtomics("100000000000000000000000", 5).atomics).toEqual("100000000000000000000000"); + expect(Decimal.fromAtomics("200000000000000000000000", 5).atomics).toEqual("200000000000000000000000"); + expect(Decimal.fromAtomics("300000000000000000000000", 5).atomics).toEqual("300000000000000000000000"); + + expect(Decimal.fromAtomics("44", 5).atomics).toEqual("44"); + expect(Decimal.fromAtomics("044", 5).atomics).toEqual("44"); + expect(Decimal.fromAtomics("0044", 5).atomics).toEqual("44"); + expect(Decimal.fromAtomics("00044", 5).atomics).toEqual("44"); + }); + + it("reads fractional digits correctly", () => { + expect(Decimal.fromAtomics("44", 0).toString()).toEqual("44"); + expect(Decimal.fromAtomics("44", 1).toString()).toEqual("4.4"); + expect(Decimal.fromAtomics("44", 2).toString()).toEqual("0.44"); + expect(Decimal.fromAtomics("44", 3).toString()).toEqual("0.044"); + expect(Decimal.fromAtomics("44", 4).toString()).toEqual("0.0044"); + }); + }); + + describe("fromUserInput", () => { + it("throws helpful error message for invalid characters", () => { + expect(() => Decimal.fromUserInput(" 13", 5)).toThrowError(/invalid character at position 1/i); + expect(() => Decimal.fromUserInput("1,3", 5)).toThrowError(/invalid character at position 2/i); + expect(() => Decimal.fromUserInput("13-", 5)).toThrowError(/invalid character at position 3/i); + expect(() => Decimal.fromUserInput("13/", 5)).toThrowError(/invalid character at position 3/i); + expect(() => Decimal.fromUserInput("13\\", 5)).toThrowError(/invalid character at position 3/i); + }); + + it("throws for more than one separator", () => { + expect(() => Decimal.fromUserInput("1.3.5", 5)).toThrowError(/more than one separator found/i); + expect(() => Decimal.fromUserInput("1..3", 5)).toThrowError(/more than one separator found/i); + expect(() => Decimal.fromUserInput("..", 5)).toThrowError(/more than one separator found/i); + }); + + it("throws for separator only", () => { + expect(() => Decimal.fromUserInput(".", 5)).toThrowError(/fractional part missing/i); + }); + + it("throws for more fractional digits than supported", () => { + expect(() => Decimal.fromUserInput("44.123456", 5)).toThrowError( + /got more fractional digits than supported/i, + ); + expect(() => Decimal.fromUserInput("44.1", 0)).toThrowError( + /got more fractional digits than supported/i, + ); + }); + + it("throws for fractional digits that are not non-negative integers", () => { + // no integer + expect(() => Decimal.fromUserInput("1", Number.NaN)).toThrowError( + /fractional digits is not an integer/i, + ); + expect(() => Decimal.fromUserInput("1", Number.POSITIVE_INFINITY)).toThrowError( + /fractional digits is not an integer/i, + ); + expect(() => Decimal.fromUserInput("1", Number.NEGATIVE_INFINITY)).toThrowError( + /fractional digits is not an integer/i, + ); + expect(() => Decimal.fromUserInput("1", 1.78945544484)).toThrowError( + /fractional digits is not an integer/i, + ); + + // negative + expect(() => Decimal.fromUserInput("1", -1)).toThrowError(/fractional digits must not be negative/i); + expect(() => Decimal.fromUserInput("1", Number.MIN_SAFE_INTEGER)).toThrowError( + /fractional digits must not be negative/i, + ); + + // exceeds supported range + expect(() => Decimal.fromUserInput("1", 101)).toThrowError(/fractional digits must not exceed 100/i); + }); + + it("returns correct value", () => { + expect(Decimal.fromUserInput("44", 0).atomics).toEqual("44"); + expect(Decimal.fromUserInput("44", 1).atomics).toEqual("440"); + expect(Decimal.fromUserInput("44", 2).atomics).toEqual("4400"); + expect(Decimal.fromUserInput("44", 3).atomics).toEqual("44000"); + + expect(Decimal.fromUserInput("44.2", 1).atomics).toEqual("442"); + expect(Decimal.fromUserInput("44.2", 2).atomics).toEqual("4420"); + expect(Decimal.fromUserInput("44.2", 3).atomics).toEqual("44200"); + + expect(Decimal.fromUserInput("44.1", 6).atomics).toEqual("44100000"); + expect(Decimal.fromUserInput("44.12", 6).atomics).toEqual("44120000"); + expect(Decimal.fromUserInput("44.123", 6).atomics).toEqual("44123000"); + expect(Decimal.fromUserInput("44.1234", 6).atomics).toEqual("44123400"); + expect(Decimal.fromUserInput("44.12345", 6).atomics).toEqual("44123450"); + expect(Decimal.fromUserInput("44.123456", 6).atomics).toEqual("44123456"); + }); + + it("cuts leading zeros", () => { + expect(Decimal.fromUserInput("4", 2).atomics).toEqual("400"); + expect(Decimal.fromUserInput("04", 2).atomics).toEqual("400"); + expect(Decimal.fromUserInput("004", 2).atomics).toEqual("400"); + }); + + it("cuts tailing zeros", () => { + expect(Decimal.fromUserInput("4.12", 5).atomics).toEqual("412000"); + expect(Decimal.fromUserInput("4.120", 5).atomics).toEqual("412000"); + expect(Decimal.fromUserInput("4.1200", 5).atomics).toEqual("412000"); + expect(Decimal.fromUserInput("4.12000", 5).atomics).toEqual("412000"); + expect(Decimal.fromUserInput("4.120000", 5).atomics).toEqual("412000"); + expect(Decimal.fromUserInput("4.1200000", 5).atomics).toEqual("412000"); + }); + + it("interprets the empty string as zero", () => { + expect(Decimal.fromUserInput("", 0).atomics).toEqual("0"); + expect(Decimal.fromUserInput("", 1).atomics).toEqual("0"); + expect(Decimal.fromUserInput("", 2).atomics).toEqual("0"); + expect(Decimal.fromUserInput("", 3).atomics).toEqual("0"); + }); + + it("accepts american notation with skipped leading zero", () => { + expect(Decimal.fromUserInput(".1", 3).atomics).toEqual("100"); + expect(Decimal.fromUserInput(".12", 3).atomics).toEqual("120"); + expect(Decimal.fromUserInput(".123", 3).atomics).toEqual("123"); + }); + }); + + describe("toString", () => { + it("displays no decimal point for full numbers", () => { + expect(Decimal.fromUserInput("44", 0).toString()).toEqual("44"); + expect(Decimal.fromUserInput("44", 1).toString()).toEqual("44"); + expect(Decimal.fromUserInput("44", 2).toString()).toEqual("44"); + + expect(Decimal.fromUserInput("44", 2).toString()).toEqual("44"); + expect(Decimal.fromUserInput("44.0", 2).toString()).toEqual("44"); + expect(Decimal.fromUserInput("44.00", 2).toString()).toEqual("44"); + expect(Decimal.fromUserInput("44.000", 2).toString()).toEqual("44"); + }); + + it("only shows significant digits", () => { + expect(Decimal.fromUserInput("44.1", 2).toString()).toEqual("44.1"); + expect(Decimal.fromUserInput("44.10", 2).toString()).toEqual("44.1"); + expect(Decimal.fromUserInput("44.100", 2).toString()).toEqual("44.1"); + }); + + it("fills up leading zeros", () => { + expect(Decimal.fromAtomics("3", 0).toString()).toEqual("3"); + expect(Decimal.fromAtomics("3", 1).toString()).toEqual("0.3"); + expect(Decimal.fromAtomics("3", 2).toString()).toEqual("0.03"); + expect(Decimal.fromAtomics("3", 3).toString()).toEqual("0.003"); + }); + }); + + describe("toFloatApproximation", () => { + it("works", () => { + expect(Decimal.fromUserInput("0", 5).toFloatApproximation()).toEqual(0); + expect(Decimal.fromUserInput("1", 5).toFloatApproximation()).toEqual(1); + expect(Decimal.fromUserInput("1.5", 5).toFloatApproximation()).toEqual(1.5); + expect(Decimal.fromUserInput("0.1", 5).toFloatApproximation()).toEqual(0.1); + + expect(Decimal.fromUserInput("1234500000000000", 5).toFloatApproximation()).toEqual(1.2345e15); + expect(Decimal.fromUserInput("1234500000000000.002", 5).toFloatApproximation()).toEqual(1.2345e15); + }); + }); + + describe("plus", () => { + it("returns correct values", () => { + const zero = Decimal.fromUserInput("0", 5); + expect(zero.plus(Decimal.fromUserInput("0", 5)).toString()).toEqual("0"); + expect(zero.plus(Decimal.fromUserInput("1", 5)).toString()).toEqual("1"); + expect(zero.plus(Decimal.fromUserInput("2", 5)).toString()).toEqual("2"); + expect(zero.plus(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("2.8"); + expect(zero.plus(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("0.12345"); + + const one = Decimal.fromUserInput("1", 5); + expect(one.plus(Decimal.fromUserInput("0", 5)).toString()).toEqual("1"); + expect(one.plus(Decimal.fromUserInput("1", 5)).toString()).toEqual("2"); + expect(one.plus(Decimal.fromUserInput("2", 5)).toString()).toEqual("3"); + expect(one.plus(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("3.8"); + expect(one.plus(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("1.12345"); + + const oneDotFive = Decimal.fromUserInput("1.5", 5); + expect(oneDotFive.plus(Decimal.fromUserInput("0", 5)).toString()).toEqual("1.5"); + expect(oneDotFive.plus(Decimal.fromUserInput("1", 5)).toString()).toEqual("2.5"); + expect(oneDotFive.plus(Decimal.fromUserInput("2", 5)).toString()).toEqual("3.5"); + expect(oneDotFive.plus(Decimal.fromUserInput("2.8", 5)).toString()).toEqual("4.3"); + expect(oneDotFive.plus(Decimal.fromUserInput("0.12345", 5)).toString()).toEqual("1.62345"); + + // original value remain unchanged + expect(zero.toString()).toEqual("0"); + expect(one.toString()).toEqual("1"); + expect(oneDotFive.toString()).toEqual("1.5"); + }); + + it("throws for different fractional digits", () => { + const zero = Decimal.fromUserInput("0", 5); + expect(() => zero.plus(Decimal.fromUserInput("1", 1))).toThrowError(/do not match/i); + expect(() => zero.plus(Decimal.fromUserInput("1", 2))).toThrowError(/do not match/i); + expect(() => zero.plus(Decimal.fromUserInput("1", 3))).toThrowError(/do not match/i); + expect(() => zero.plus(Decimal.fromUserInput("1", 4))).toThrowError(/do not match/i); + + expect(() => zero.plus(Decimal.fromUserInput("1", 6))).toThrowError(/do not match/i); + expect(() => zero.plus(Decimal.fromUserInput("1", 7))).toThrowError(/do not match/i); + }); + }); +}); diff --git a/packages/math/src/decimal.ts b/packages/math/src/decimal.ts new file mode 100644 index 00000000..e126e4ed --- /dev/null +++ b/packages/math/src/decimal.ts @@ -0,0 +1,121 @@ +import BN from "bn.js"; + +// Too large values lead to massive memory usage. Limit to something sensible. +// The largest value we need is 18 (Ether). +const maxFractionalDigits = 100; + +/** + * A type for arbitrary precision, non-negative decimals. + * + * Instances of this class are immutable. + */ +export class Decimal { + public static fromUserInput(input: string, fractionalDigits: number): Decimal { + Decimal.verifyFractionalDigits(fractionalDigits); + + const badCharacter = input.match(/[^0-9.]/); + if (badCharacter) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + throw new Error(`Invalid character at position ${badCharacter.index! + 1}`); + } + + let whole: string; + let fractional: string; + + if (input.search(/\./) === -1) { + // integer format, no separator + whole = input; + fractional = ""; + } else { + const parts = input.split("."); + switch (parts.length) { + case 0: + case 1: + throw new Error("Fewer than two elements in split result. This must not happen here."); + case 2: + if (!parts[1]) throw new Error("Fractional part missing"); + whole = parts[0]; + fractional = parts[1].replace(/0+$/, ""); + break; + default: + throw new Error("More than one separator found"); + } + } + + if (fractional.length > fractionalDigits) { + throw new Error("Got more fractional digits than supported"); + } + + const quantity = `${whole}${fractional.padEnd(fractionalDigits, "0")}`; + + return new Decimal(quantity, fractionalDigits); + } + + public static fromAtomics(atomics: string, fractionalDigits: number): Decimal { + Decimal.verifyFractionalDigits(fractionalDigits); + return new Decimal(atomics, fractionalDigits); + } + + private static verifyFractionalDigits(fractionalDigits: number): void { + if (!Number.isInteger(fractionalDigits)) throw new Error("Fractional digits is not an integer"); + if (fractionalDigits < 0) throw new Error("Fractional digits must not be negative"); + if (fractionalDigits > maxFractionalDigits) { + throw new Error(`Fractional digits must not exceed ${maxFractionalDigits}`); + } + } + + public get atomics(): string { + return this.data.atomics.toString(); + } + + public get fractionalDigits(): number { + return this.data.fractionalDigits; + } + + private readonly data: { + readonly atomics: BN; + readonly fractionalDigits: number; + }; + + private constructor(atomics: string, fractionalDigits: number) { + this.data = { + atomics: new BN(atomics), + fractionalDigits: fractionalDigits, + }; + } + + public toString(): string { + const factor = new BN(10).pow(new BN(this.data.fractionalDigits)); + const whole = this.data.atomics.div(factor); + const fractional = this.data.atomics.mod(factor); + + if (fractional.isZero()) { + return whole.toString(); + } else { + const fullFractionalPart = fractional.toString().padStart(this.data.fractionalDigits, "0"); + const trimmedFractionalPart = fullFractionalPart.replace(/0+$/, ""); + return `${whole.toString()}.${trimmedFractionalPart}`; + } + } + + /** + * Returns an approximation as a float type. Only use this if no + * exact calculation is required. + */ + public toFloatApproximation(): number { + const out = Number(this.toString()); + if (Number.isNaN(out)) throw new Error("Conversion to number failed"); + return out; + } + + /** + * a.plus(b) returns a+b. + * + * Both values need to have the same fractional digits. + */ + public plus(b: Decimal): Decimal { + if (this.fractionalDigits !== b.fractionalDigits) throw new Error("Fractional digits do not match"); + const sum = this.data.atomics.add(new BN(b.atomics)); + return new Decimal(sum.toString(), this.fractionalDigits); + } +} diff --git a/packages/math/src/index.ts b/packages/math/src/index.ts new file mode 100644 index 00000000..b888136f --- /dev/null +++ b/packages/math/src/index.ts @@ -0,0 +1,2 @@ +export { Decimal } from "./decimal"; +export { Int53, Uint32, Uint53, Uint64 } from "./integers"; diff --git a/packages/math/src/integers.spec.ts b/packages/math/src/integers.spec.ts new file mode 100644 index 00000000..2b3a5b5a --- /dev/null +++ b/packages/math/src/integers.spec.ts @@ -0,0 +1,475 @@ +import { Int53, Uint32, Uint53, Uint64 } from "./integers"; + +describe("Integers", () => { + describe("Uint32", () => { + it("can be constructed", () => { + expect(new Uint32(0)).toBeTruthy(); + expect(new Uint32(1)).toBeTruthy(); + expect(new Uint32(1.0)).toBeTruthy(); + expect(new Uint32(42)).toBeTruthy(); + expect(new Uint32(1000000000)).toBeTruthy(); + expect(new Uint32(2147483647)).toBeTruthy(); + expect(new Uint32(2147483648)).toBeTruthy(); + expect(new Uint32(4294967295)).toBeTruthy(); + }); + + it("throws for invald numbers", () => { + expect(() => new Uint32(NaN)).toThrowError(/not a number/); + + expect(() => new Uint32(1.1)).toThrowError(/not an integer/i); + expect(() => new Uint32(Number.NEGATIVE_INFINITY)).toThrowError(/not an integer/i); + expect(() => new Uint32(Number.POSITIVE_INFINITY)).toThrowError(/not an integer/i); + }); + + it("throws for values out of range", () => { + expect(() => new Uint32(-1)).toThrowError(/not in uint32 range/); + expect(() => new Uint32(4294967296)).toThrowError(/not in uint32 range/); + expect(() => new Uint32(Number.MIN_SAFE_INTEGER)).toThrowError(/not in uint32 range/); + expect(() => new Uint32(Number.MAX_SAFE_INTEGER)).toThrowError(/not in uint32 range/); + }); + + it("can convert to number", () => { + expect(new Uint32(0).toNumber()).toEqual(0); + expect(new Uint32(1).toNumber()).toEqual(1); + expect(new Uint32(42).toNumber()).toEqual(42); + expect(new Uint32(1000000000).toNumber()).toEqual(1000000000); + expect(new Uint32(2147483647).toNumber()).toEqual(2147483647); + expect(new Uint32(2147483648).toNumber()).toEqual(2147483648); + expect(new Uint32(4294967295).toNumber()).toEqual(4294967295); + }); + + it("can convert to string", () => { + expect(new Uint32(0).toString()).toEqual("0"); + expect(new Uint32(1).toString()).toEqual("1"); + expect(new Uint32(42).toString()).toEqual("42"); + expect(new Uint32(1000000000).toString()).toEqual("1000000000"); + expect(new Uint32(2147483647).toString()).toEqual("2147483647"); + expect(new Uint32(2147483648).toString()).toEqual("2147483648"); + expect(new Uint32(4294967295).toString()).toEqual("4294967295"); + }); + + describe("toBytesBigEndian", () => { + it("works", () => { + expect(new Uint32(0).toBytesBigEndian()).toEqual(new Uint8Array([0, 0, 0, 0])); + expect(new Uint32(1).toBytesBigEndian()).toEqual(new Uint8Array([0, 0, 0, 1])); + expect(new Uint32(42).toBytesBigEndian()).toEqual(new Uint8Array([0, 0, 0, 42])); + expect(new Uint32(1000000000).toBytesBigEndian()).toEqual(new Uint8Array([0x3b, 0x9a, 0xca, 0x00])); + expect(new Uint32(2147483647).toBytesBigEndian()).toEqual(new Uint8Array([0x7f, 0xff, 0xff, 0xff])); + expect(new Uint32(2147483648).toBytesBigEndian()).toEqual(new Uint8Array([0x80, 0x00, 0x00, 0x00])); + expect(new Uint32(4294967295).toBytesBigEndian()).toEqual(new Uint8Array([0xff, 0xff, 0xff, 0xff])); + }); + }); + + describe("toBytesLittleEndian", () => { + it("works", () => { + expect(new Uint32(0).toBytesLittleEndian()).toEqual(new Uint8Array([0, 0, 0, 0])); + expect(new Uint32(1).toBytesLittleEndian()).toEqual(new Uint8Array([1, 0, 0, 0])); + expect(new Uint32(42).toBytesLittleEndian()).toEqual(new Uint8Array([42, 0, 0, 0])); + expect(new Uint32(1000000000).toBytesLittleEndian()).toEqual( + new Uint8Array([0x00, 0xca, 0x9a, 0x3b]), + ); + expect(new Uint32(2147483647).toBytesLittleEndian()).toEqual( + new Uint8Array([0xff, 0xff, 0xff, 0x7f]), + ); + expect(new Uint32(2147483648).toBytesLittleEndian()).toEqual( + new Uint8Array([0x00, 0x00, 0x00, 0x80]), + ); + expect(new Uint32(4294967295).toBytesLittleEndian()).toEqual( + new Uint8Array([0xff, 0xff, 0xff, 0xff]), + ); + }); + }); + + describe("fromBigEndianBytes", () => { + it("can be constructed from to byte array", () => { + expect(Uint32.fromBigEndianBytes([0, 0, 0, 0]).toNumber()).toEqual(0); + expect(Uint32.fromBigEndianBytes([0, 0, 0, 1]).toNumber()).toEqual(1); + expect(Uint32.fromBigEndianBytes([0, 0, 0, 42]).toNumber()).toEqual(42); + expect(Uint32.fromBigEndianBytes([0x3b, 0x9a, 0xca, 0x00]).toNumber()).toEqual(1000000000); + expect(Uint32.fromBigEndianBytes([0x7f, 0xff, 0xff, 0xff]).toNumber()).toEqual(2147483647); + expect(Uint32.fromBigEndianBytes([0x80, 0x00, 0x00, 0x00]).toNumber()).toEqual(2147483648); + expect(Uint32.fromBigEndianBytes([0xff, 0xff, 0xff, 0xff]).toNumber()).toEqual(4294967295); + }); + + it("can be constructed from Buffer", () => { + expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 0])).toNumber()).toEqual(0); + expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 1])).toNumber()).toEqual(1); + expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 42])).toNumber()).toEqual(42); + expect(Uint32.fromBigEndianBytes(Buffer.from([0x3b, 0x9a, 0xca, 0x00])).toNumber()).toEqual( + 1000000000, + ); + expect(Uint32.fromBigEndianBytes(Buffer.from([0x7f, 0xff, 0xff, 0xff])).toNumber()).toEqual( + 2147483647, + ); + expect(Uint32.fromBigEndianBytes(Buffer.from([0x80, 0x00, 0x00, 0x00])).toNumber()).toEqual( + 2147483648, + ); + expect(Uint32.fromBigEndianBytes(Buffer.from([0xff, 0xff, 0xff, 0xff])).toNumber()).toEqual( + 4294967295, + ); + }); + + it("throws for invalid input length", () => { + expect(() => Uint32.fromBigEndianBytes([])).toThrowError(/Invalid input length/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0])).toThrowError(/Invalid input length/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 0, 0])).toThrowError(/Invalid input length/); + }); + + it("throws for invalid values", () => { + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, -1])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 1.5])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 256])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, NaN])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( + /Invalid value in byte/, + ); + expect(() => Uint32.fromBigEndianBytes([0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( + /Invalid value in byte/, + ); + }); + }); + }); + + describe("Int53", () => { + it("can be constructed", () => { + expect(new Int53(0)).toBeTruthy(); + expect(new Int53(1)).toBeTruthy(); + expect(new Int53(1.0)).toBeTruthy(); + expect(new Int53(42)).toBeTruthy(); + expect(new Int53(1000000000)).toBeTruthy(); + expect(new Int53(2147483647)).toBeTruthy(); + expect(new Int53(2147483648)).toBeTruthy(); + expect(new Int53(4294967295)).toBeTruthy(); + expect(new Int53(9007199254740991)).toBeTruthy(); + + expect(new Int53(-1)).toBeTruthy(); + expect(new Int53(-42)).toBeTruthy(); + expect(new Int53(-2147483648)).toBeTruthy(); + expect(new Int53(-2147483649)).toBeTruthy(); + expect(new Int53(-9007199254740991)).toBeTruthy(); + }); + + it("throws for invald numbers", () => { + expect(() => new Int53(NaN)).toThrowError(/not a number/); + + expect(() => new Int53(1.1)).toThrowError(/not an integer/i); + expect(() => new Int53(Number.NEGATIVE_INFINITY)).toThrowError(/not an integer/i); + expect(() => new Int53(Number.POSITIVE_INFINITY)).toThrowError(/not an integer/i); + }); + + it("throws for values out of range", () => { + expect(() => new Int53(Number.MIN_SAFE_INTEGER - 1)).toThrowError(/not in int53 range/); + expect(() => new Int53(Number.MAX_SAFE_INTEGER + 1)).toThrowError(/not in int53 range/); + }); + + it("can convert to number", () => { + expect(new Int53(0).toNumber()).toEqual(0); + expect(new Int53(1).toNumber()).toEqual(1); + expect(new Int53(42).toNumber()).toEqual(42); + expect(new Int53(1000000000).toNumber()).toEqual(1000000000); + expect(new Int53(2147483647).toNumber()).toEqual(2147483647); + expect(new Int53(2147483648).toNumber()).toEqual(2147483648); + expect(new Int53(4294967295).toNumber()).toEqual(4294967295); + expect(new Int53(9007199254740991).toNumber()).toEqual(9007199254740991); + + expect(new Int53(-1).toNumber()).toEqual(-1); + expect(new Int53(-9007199254740991).toNumber()).toEqual(-9007199254740991); + }); + + it("can convert to string", () => { + expect(new Int53(0).toString()).toEqual("0"); + expect(new Int53(1).toString()).toEqual("1"); + expect(new Int53(42).toString()).toEqual("42"); + expect(new Int53(1000000000).toString()).toEqual("1000000000"); + expect(new Int53(2147483647).toString()).toEqual("2147483647"); + expect(new Int53(2147483648).toString()).toEqual("2147483648"); + expect(new Int53(4294967295).toString()).toEqual("4294967295"); + expect(new Int53(9007199254740991).toString()).toEqual("9007199254740991"); + + expect(new Int53(-1).toString()).toEqual("-1"); + expect(new Int53(-9007199254740991).toString()).toEqual("-9007199254740991"); + }); + + it("can be constructed from string", () => { + expect(Int53.fromString("0").toString()).toEqual("0"); + expect(Int53.fromString("1").toString()).toEqual("1"); + expect(Int53.fromString("9007199254740991").toString()).toEqual("9007199254740991"); + + expect(Int53.fromString("-1").toString()).toEqual("-1"); + expect(Int53.fromString("-9007199254740991").toString()).toEqual("-9007199254740991"); + }); + + it("throws for invalid string format", () => { + expect(() => Int53.fromString(" 0")).toThrowError(/invalid string format/i); + expect(() => Int53.fromString("+0")).toThrowError(/invalid string format/i); + expect(() => Int53.fromString("1e6")).toThrowError(/invalid string format/i); + + expect(() => Int53.fromString("9007199254740992")).toThrowError(/input not in int53 range/i); + expect(() => Int53.fromString("-9007199254740992")).toThrowError(/input not in int53 range/i); + }); + }); + + describe("Uint53", () => { + it("can be constructed", () => { + expect(new Uint53(0)).toBeTruthy(); + expect(new Uint53(1)).toBeTruthy(); + expect(new Uint53(1.0)).toBeTruthy(); + expect(new Uint53(42)).toBeTruthy(); + expect(new Uint53(1000000000)).toBeTruthy(); + expect(new Uint53(2147483647)).toBeTruthy(); + expect(new Uint53(2147483648)).toBeTruthy(); + expect(new Uint53(4294967295)).toBeTruthy(); + expect(new Uint53(9007199254740991)).toBeTruthy(); + }); + + it("throws for invald numbers", () => { + expect(() => new Uint53(NaN)).toThrowError(/not a number/); + + expect(() => new Uint53(1.1)).toThrowError(/not an integer/i); + expect(() => new Uint53(Number.NEGATIVE_INFINITY)).toThrowError(/not an integer/i); + expect(() => new Uint53(Number.POSITIVE_INFINITY)).toThrowError(/not an integer/i); + }); + + it("throws for values out of range", () => { + expect(() => new Uint53(Number.MIN_SAFE_INTEGER - 1)).toThrowError(/not in int53 range/); + expect(() => new Uint53(Number.MAX_SAFE_INTEGER + 1)).toThrowError(/not in int53 range/); + }); + + it("throws for negative inputs", () => { + expect(() => new Uint53(-1)).toThrowError(/is negative/); + expect(() => new Uint53(-42)).toThrowError(/is negative/); + expect(() => new Uint53(-2147483648)).toThrowError(/is negative/); + expect(() => new Uint53(-2147483649)).toThrowError(/is negative/); + expect(() => new Uint53(-9007199254740991)).toThrowError(/is negative/); + }); + + it("can convert to number", () => { + expect(new Uint53(0).toNumber()).toEqual(0); + expect(new Uint53(1).toNumber()).toEqual(1); + expect(new Uint53(42).toNumber()).toEqual(42); + expect(new Uint53(1000000000).toNumber()).toEqual(1000000000); + expect(new Uint53(2147483647).toNumber()).toEqual(2147483647); + expect(new Uint53(2147483648).toNumber()).toEqual(2147483648); + expect(new Uint53(4294967295).toNumber()).toEqual(4294967295); + expect(new Uint53(9007199254740991).toNumber()).toEqual(9007199254740991); + }); + + it("can convert to string", () => { + expect(new Uint53(0).toString()).toEqual("0"); + expect(new Uint53(1).toString()).toEqual("1"); + expect(new Uint53(42).toString()).toEqual("42"); + expect(new Uint53(1000000000).toString()).toEqual("1000000000"); + expect(new Uint53(2147483647).toString()).toEqual("2147483647"); + expect(new Uint53(2147483648).toString()).toEqual("2147483648"); + expect(new Uint53(4294967295).toString()).toEqual("4294967295"); + expect(new Uint53(9007199254740991).toString()).toEqual("9007199254740991"); + }); + + it("can be constructed from string", () => { + expect(Uint53.fromString("0").toString()).toEqual("0"); + expect(Uint53.fromString("1").toString()).toEqual("1"); + expect(Uint53.fromString("9007199254740991").toString()).toEqual("9007199254740991"); + }); + + it("throws for invalid string format", () => { + expect(() => Uint53.fromString(" 0")).toThrowError(/invalid string format/i); + expect(() => Uint53.fromString("+0")).toThrowError(/invalid string format/i); + expect(() => Uint53.fromString("1e6")).toThrowError(/invalid string format/i); + + expect(() => Uint53.fromString("-9007199254740992")).toThrowError(/input not in int53 range/i); + expect(() => Uint53.fromString("9007199254740992")).toThrowError(/input not in int53 range/i); + + expect(() => Uint53.fromString("-1")).toThrowError(/input is negative/i); + }); + }); + + describe("Uint64", () => { + describe("fromBigEndianBytes", () => { + it("can be constructed from bytes", () => { + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + }); + + it("can be constructed from Uint8Array", () => { + Uint64.fromBytesBigEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + }); + + it("throws for wrong number of bytes", () => { + expect(() => Uint64.fromBytesBigEndian([])).toThrowError(/invalid input length/i); + expect(() => Uint64.fromBytesBigEndian([0x00])).toThrowError(/invalid input length/i); + expect(() => Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError( + /invalid input length/i, + ); + expect(() => + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + ).toThrowError(/invalid input length/i); + }); + + it("throws for wrong byte value", () => { + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 256])).toThrowError( + /invalid value in byte/i, + ); + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, -1])).toThrowError( + /invalid value in byte/i, + ); + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 1.5])).toThrowError( + /invalid value in byte/i, + ); + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( + /invalid value in byte/i, + ); + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( + /invalid value in byte/i, + ); + expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NaN])).toThrowError( + /invalid value in byte/i, + ); + }); + }); + + describe("fromString", () => { + it("can be constructed from string", () => { + { + const a = Uint64.fromString("0"); + expect(a).toBeTruthy(); + } + { + const a = Uint64.fromString("1"); + expect(a).toBeTruthy(); + } + { + const a = Uint64.fromString("01"); + expect(a).toBeTruthy(); + } + { + const a = Uint64.fromString("9999999999999999999"); + expect(a).toBeTruthy(); + } + { + const a = Uint64.fromString("18446744073709551615"); + expect(a).toBeTruthy(); + } + }); + + it("throws for invalid string values", () => { + expect(() => Uint64.fromString(" 1")).toThrowError(/invalid string format/i); + expect(() => Uint64.fromString("-1")).toThrowError(/invalid string format/i); + expect(() => Uint64.fromString("+1")).toThrowError(/invalid string format/i); + expect(() => Uint64.fromString("1e6")).toThrowError(/invalid string format/i); + }); + + it("throws for string values exceeding uint64", () => { + expect(() => Uint64.fromString("18446744073709551616")).toThrowError(/input exceeds uint64 range/i); + expect(() => Uint64.fromString("99999999999999999999")).toThrowError(/input exceeds uint64 range/i); + }); + }); + + describe("fromNumber", () => { + it("can be constructed from number", () => { + const a = Uint64.fromNumber(0); + expect(a.toNumber()).toEqual(0); + const b = Uint64.fromNumber(1); + expect(b.toNumber()).toEqual(1); + const c = Uint64.fromNumber(Number.MAX_SAFE_INTEGER); + expect(c.toNumber()).toEqual(Number.MAX_SAFE_INTEGER); + }); + + it("throws when constructed from wrong numbers", () => { + // not a number + expect(() => Uint64.fromNumber(Number.NaN)).toThrowError(/input is not a number/i); + + // not an integer + expect(() => Uint64.fromNumber(Number.NEGATIVE_INFINITY)).toThrowError( + /input is not a safe integer/i, + ); + expect(() => Uint64.fromNumber(Number.POSITIVE_INFINITY)).toThrowError( + /input is not a safe integer/i, + ); + expect(() => Uint64.fromNumber(Number.MAX_SAFE_INTEGER + 1)).toThrowError( + /input is not a safe integer/i, + ); + + // negative integer + expect(() => Uint64.fromNumber(-1)).toThrowError(/input is negative/i); + expect(() => Uint64.fromNumber(Number.MIN_SAFE_INTEGER)).toThrowError(/input is negative/i); + }); + }); + + it("can export bytes (big endian)", () => { + expect( + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian(), + ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + expect( + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesBigEndian(), + ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])); + expect( + Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian(), + ).toEqual(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + expect( + Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesBigEndian(), + ).toEqual(new Uint8Array([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d])); + expect( + Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesBigEndian(), + ).toEqual(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])); + }); + + it("can export bytes (little endian)", () => { + expect( + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), + ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + expect( + Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesLittleEndian(), + ).toEqual(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + expect( + Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), + ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])); + expect( + Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesLittleEndian(), + ).toEqual(new Uint8Array([0x0d, 0x4e, 0x20, 0xa9, 0x5f, 0xbc, 0x22, 0xab])); + expect( + Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesLittleEndian(), + ).toEqual(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])); + }); + + it("can export strings", () => { + { + const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + expect(a.toString()).toEqual("0"); + } + { + const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + expect(a.toString()).toEqual("1"); + } + { + const a = Uint64.fromBytesBigEndian([0x8a, 0xc7, 0x23, 0x04, 0x89, 0xe7, 0xff, 0xff]); + expect(a.toString()).toEqual("9999999999999999999"); + } + { + const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + expect(a.toString()).toEqual("18446744073709551615"); + } + }); + + it("can export numbers", () => { + { + const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + expect(a.toNumber()).toEqual(0); + } + { + const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + expect(a.toNumber()).toEqual(1); + } + { + // value too large for 53 bit integer + const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); + } + { + // Number.MAX_SAFE_INTEGER + 1 + const a = Uint64.fromBytesBigEndian([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); + } + }); + }); +}); diff --git a/packages/math/src/integers.ts b/packages/math/src/integers.ts new file mode 100644 index 00000000..b63813d0 --- /dev/null +++ b/packages/math/src/integers.ts @@ -0,0 +1,212 @@ +/* eslint-disable no-bitwise */ +import BN from "bn.js"; + +const uint64MaxValue = new BN("18446744073709551615", 10, "be"); + +/** Internal interface to ensure all integer types can be used equally */ +interface Integer { + readonly toNumber: () => number; + readonly toString: () => string; +} + +interface WithByteConverters { + readonly toBytesBigEndian: () => Uint8Array; + readonly toBytesLittleEndian: () => Uint8Array; +} + +export class Uint32 implements Integer, WithByteConverters { + public static fromBigEndianBytes(bytes: ArrayLike): Uint32 { + if (bytes.length !== 4) { + throw new Error("Invalid input length. Expected 4 bytes."); + } + + for (let i = 0; i < bytes.length; ++i) { + if (!Number.isInteger(bytes[i]) || bytes[i] > 255 || bytes[i] < 0) { + throw new Error("Invalid value in byte. Found: " + bytes[i]); + } + } + + // Use mulitiplication instead of shifting since bitwise operators are defined + // on SIGNED int32 in JavaScript and we don't want to risk surprises + return new Uint32(bytes[0] * 2 ** 24 + bytes[1] * 2 ** 16 + bytes[2] * 2 ** 8 + bytes[3]); + } + + protected readonly data: number; + + public constructor(input: number) { + if (Number.isNaN(input)) { + throw new Error("Input is not a number"); + } + + if (!Number.isInteger(input)) { + throw new Error("Input is not an integer"); + } + + if (input < 0 || input > 4294967295) { + throw new Error("Input not in uint32 range: " + input.toString()); + } + + this.data = input; + } + + public toBytesBigEndian(): Uint8Array { + // Use division instead of shifting since bitwise operators are defined + // on SIGNED int32 in JavaScript and we don't want to risk surprises + return new Uint8Array([ + Math.floor(this.data / 2 ** 24) & 0xff, + Math.floor(this.data / 2 ** 16) & 0xff, + Math.floor(this.data / 2 ** 8) & 0xff, + Math.floor(this.data / 2 ** 0) & 0xff, + ]); + } + + public toBytesLittleEndian(): Uint8Array { + // Use division instead of shifting since bitwise operators are defined + // on SIGNED int32 in JavaScript and we don't want to risk surprises + return new Uint8Array([ + Math.floor(this.data / 2 ** 0) & 0xff, + Math.floor(this.data / 2 ** 8) & 0xff, + Math.floor(this.data / 2 ** 16) & 0xff, + Math.floor(this.data / 2 ** 24) & 0xff, + ]); + } + + public toNumber(): number { + return this.data; + } + + public toString(): string { + return this.data.toString(); + } +} + +export class Int53 implements Integer { + public static fromString(str: string): Int53 { + if (!str.match(/^-?[0-9]+$/)) { + throw new Error("Invalid string format"); + } + + return new Int53(Number.parseInt(str, 10)); + } + + protected readonly data: number; + + public constructor(input: number) { + if (Number.isNaN(input)) { + throw new Error("Input is not a number"); + } + + if (!Number.isInteger(input)) { + throw new Error("Input is not an integer"); + } + + if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) { + throw new Error("Input not in int53 range: " + input.toString()); + } + + this.data = input; + } + + public toNumber(): number { + return this.data; + } + + public toString(): string { + return this.data.toString(); + } +} + +export class Uint53 implements Integer { + public static fromString(str: string): Uint53 { + const signed = Int53.fromString(str); + return new Uint53(signed.toNumber()); + } + + protected readonly data: Int53; + + public constructor(input: number) { + const signed = new Int53(input); + if (signed.toNumber() < 0) { + throw new Error("Input is negative"); + } + this.data = signed; + } + + public toNumber(): number { + return this.data.toNumber(); + } + + public toString(): string { + return this.data.toString(); + } +} + +export class Uint64 implements Integer, WithByteConverters { + public static fromBytesBigEndian(bytes: ArrayLike): Uint64 { + if (bytes.length !== 8) { + throw new Error("Invalid input length. Expected 8 bytes."); + } + + for (let i = 0; i < bytes.length; ++i) { + if (!Number.isInteger(bytes[i]) || bytes[i] > 255 || bytes[i] < 0) { + throw new Error("Invalid value in byte. Found: " + bytes[i]); + } + } + + const asArray: number[] = []; + for (let i = 0; i < bytes.length; ++i) { + asArray.push(bytes[i]); + } + + return new Uint64(new BN([...asArray])); + } + + public static fromString(str: string): Uint64 { + if (!str.match(/^[0-9]+$/)) { + throw new Error("Invalid string format"); + } + return new Uint64(new BN(str, 10, "be")); + } + + public static fromNumber(input: number): Uint64 { + if (Number.isNaN(input)) { + throw new Error("Input is not a number"); + } + + let bigint: BN; + try { + bigint = new BN(input); + } catch { + throw new Error("Input is not a safe integer"); + } + return new Uint64(bigint); + } + + private readonly data: BN; + + private constructor(data: BN) { + if (data.isNeg()) { + throw new Error("Input is negative"); + } + if (data.gt(uint64MaxValue)) { + throw new Error("Input exceeds uint64 range"); + } + this.data = data; + } + + public toBytesBigEndian(): Uint8Array { + return Uint8Array.from(this.data.toArray("be", 8)); + } + + public toBytesLittleEndian(): Uint8Array { + return Uint8Array.from(this.data.toArray("le", 8)); + } + + public toString(): string { + return this.data.toString(10); + } + + public toNumber(): number { + return this.data.toNumber(); + } +} diff --git a/packages/math/tsconfig.json b/packages/math/tsconfig.json new file mode 100644 index 00000000..167e8c02 --- /dev/null +++ b/packages/math/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declarationDir": "build/types", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/math/typedoc.js b/packages/math/typedoc.js new file mode 100644 index 00000000..e2387c7d --- /dev/null +++ b/packages/math/typedoc.js @@ -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, +}; diff --git a/packages/math/types/decimal.d.ts b/packages/math/types/decimal.d.ts new file mode 100644 index 00000000..9d65be9d --- /dev/null +++ b/packages/math/types/decimal.d.ts @@ -0,0 +1,26 @@ +/** + * A type for arbitrary precision, non-negative decimals. + * + * Instances of this class are immutable. + */ +export declare class Decimal { + static fromUserInput(input: string, fractionalDigits: number): Decimal; + static fromAtomics(atomics: string, fractionalDigits: number): Decimal; + private static verifyFractionalDigits; + get atomics(): string; + get fractionalDigits(): number; + private readonly data; + private constructor(); + toString(): string; + /** + * Returns an approximation as a float type. Only use this if no + * exact calculation is required. + */ + toFloatApproximation(): number; + /** + * a.plus(b) returns a+b. + * + * Both values need to have the same fractional digits. + */ + plus(b: Decimal): Decimal; +} diff --git a/packages/math/types/index.d.ts b/packages/math/types/index.d.ts new file mode 100644 index 00000000..b888136f --- /dev/null +++ b/packages/math/types/index.d.ts @@ -0,0 +1,2 @@ +export { Decimal } from "./decimal"; +export { Int53, Uint32, Uint53, Uint64 } from "./integers"; diff --git a/packages/math/types/integers.d.ts b/packages/math/types/integers.d.ts new file mode 100644 index 00000000..39324e96 --- /dev/null +++ b/packages/math/types/integers.d.ts @@ -0,0 +1,44 @@ +/** Internal interface to ensure all integer types can be used equally */ +interface Integer { + readonly toNumber: () => number; + readonly toString: () => string; +} +interface WithByteConverters { + readonly toBytesBigEndian: () => Uint8Array; + readonly toBytesLittleEndian: () => Uint8Array; +} +export declare class Uint32 implements Integer, WithByteConverters { + static fromBigEndianBytes(bytes: ArrayLike): Uint32; + protected readonly data: number; + constructor(input: number); + toBytesBigEndian(): Uint8Array; + toBytesLittleEndian(): Uint8Array; + toNumber(): number; + toString(): string; +} +export declare class Int53 implements Integer { + static fromString(str: string): Int53; + protected readonly data: number; + constructor(input: number); + toNumber(): number; + toString(): string; +} +export declare class Uint53 implements Integer { + static fromString(str: string): Uint53; + protected readonly data: Int53; + constructor(input: number); + toNumber(): number; + toString(): string; +} +export declare class Uint64 implements Integer, WithByteConverters { + static fromBytesBigEndian(bytes: ArrayLike): Uint64; + static fromString(str: string): Uint64; + static fromNumber(input: number): Uint64; + private readonly data; + private constructor(); + toBytesBigEndian(): Uint8Array; + toBytesLittleEndian(): Uint8Array; + toString(): string; + toNumber(): number; +} +export {}; diff --git a/packages/math/webpack.web.config.js b/packages/math/webpack.web.config.js new file mode 100644 index 00000000..9d5836a8 --- /dev/null +++ b/packages/math/webpack.web.config.js @@ -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", + }, + }, +]; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 3e156674..ea096301 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,2 +1,3 @@ export { assert } from "./assert"; export { sleep } from "./sleep"; +export { isNonNullObject, isUint8Array } from "./typechecks"; diff --git a/packages/utils/src/typechecks.spec.ts b/packages/utils/src/typechecks.spec.ts new file mode 100644 index 00000000..5abbac11 --- /dev/null +++ b/packages/utils/src/typechecks.spec.ts @@ -0,0 +1,58 @@ +import { isNonNullObject, isUint8Array } from "./typechecks"; + +describe("typechecks", () => { + describe("isNonNullObject", () => { + it("returns true for objects", () => { + expect(isNonNullObject({})).toEqual(true); + expect(isNonNullObject({ foo: 123 })).toEqual(true); + expect(isNonNullObject(new Uint8Array([]))).toEqual(true); + }); + + it("returns true for arrays", () => { + // > object is a type that represents the non-primitive type, i.e. + // > anything that is not number, string, boolean, symbol, null, or undefined. + // https://www.typescriptlang.org/docs/handbook/basic-types.html#object + expect(isNonNullObject([])).toEqual(true); + }); + + it("returns false for null", () => { + expect(isNonNullObject(null)).toEqual(false); + }); + + it("returns false for other kind of data", () => { + expect(isNonNullObject(undefined)).toEqual(false); + expect(isNonNullObject("abc")).toEqual(false); + expect(isNonNullObject(123)).toEqual(false); + expect(isNonNullObject(true)).toEqual(false); + }); + }); + + describe("isUint8Array", () => { + it("returns true for Uint8Arrays", () => { + expect(isUint8Array(new Uint8Array())).toEqual(true); + expect(isUint8Array(new Uint8Array([1, 2, 3]))).toEqual(true); + }); + + it("returns false for Buffer", () => { + // One could start a big debate about whether or not a Buffer is a Uint8Array, which + // required a definition of "is a" in a languages that has no proper object oriented + // programming support. + // + // In all our software we use Uint8Array for storing binary data and copy Buffers into + // new Uint8Array to make deep equality checks work and to ensure our code works the same + // way in browsers and Node.js. So our expectation is: _a Buffer is not an Uint8Array_. + expect(isUint8Array(Buffer.from(""))).toEqual(false); + }); + + it("returns false for other kind of data", () => { + expect(isUint8Array(undefined)).toEqual(false); + expect(isUint8Array("abc")).toEqual(false); + expect(isUint8Array(123)).toEqual(false); + expect(isUint8Array(true)).toEqual(false); + + expect(isUint8Array([])).toEqual(false); + expect(isUint8Array(new Int8Array())).toEqual(false); + expect(isUint8Array(new Uint16Array())).toEqual(false); + }); + }); +}); diff --git a/packages/utils/src/typechecks.ts b/packages/utils/src/typechecks.ts new file mode 100644 index 00000000..664fcc53 --- /dev/null +++ b/packages/utils/src/typechecks.ts @@ -0,0 +1,26 @@ +/** + * Checks if data is a non-null object (i.e. matches the TypeScript object type) + */ +export function isNonNullObject(data: unknown): data is object { + return typeof data === "object" && data !== null; +} + +/** + * Checks if data is an Uint8Array. Note: Buffer is treated as not a Uint8Array + */ +export function isUint8Array(data: unknown): data is Uint8Array { + if (!isNonNullObject(data)) return false; + + // Avoid instanceof check which is unreliable in some JS environments + // https://medium.com/@simonwarta/limitations-of-the-instanceof-operator-f4bcdbe7a400 + + // Use check that was discussed in https://github.com/crypto-browserify/pbkdf2/pull/81 + if (Object.prototype.toString.call(data) !== "[object Uint8Array]") return false; + + if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer !== "undefined") { + // Buffer.isBuffer is available at runtime + if (Buffer.isBuffer(data)) return false; + } + + return true; +} diff --git a/yarn.lock b/yarn.lock index 3c9a67f4..ded15fab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -979,6 +979,11 @@ dependencies: "@types/babel-types" "*" +"@types/base64-js@^1.2.5": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.3.0.tgz#c939fdba49846861caf5a246b165dbf5698a317c" + integrity sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw== + "@types/bn.js@*", "@types/bn.js@^4.11.6": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" From 88faba8bedad5e9632a22b5a5219e73d14bb6541 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 9 Jun 2020 17:54:17 +0200 Subject: [PATCH 07/12] Replace internal usages of @iov/encoding with @cosmjs/{encoding,math} --- packages/bcp/package.json | 3 ++- packages/bcp/src/address.spec.ts | 2 +- packages/bcp/src/address.ts | 2 +- packages/bcp/src/caip5.ts | 2 +- packages/bcp/src/cosmoscodec.spec.ts | 2 +- packages/bcp/src/cosmoscodec.ts | 2 +- packages/bcp/src/cosmosconnection.spec.ts | 2 +- packages/bcp/src/cosmosconnection.ts | 3 ++- packages/bcp/src/decode.spec.ts | 2 +- packages/bcp/src/decode.ts | 3 ++- packages/bcp/src/encode.spec.ts | 2 +- packages/bcp/src/encode.ts | 2 +- packages/bcp/src/testdata.spec.ts | 2 +- packages/bcp/types/decode.d.ts | 2 +- packages/cli/package.json | 3 ++- packages/cli/src/cli.ts | 25 ++++--------------- packages/cosmwasm/package.json | 3 ++- .../src/cosmwasmclient.searchtx.spec.ts | 2 +- packages/cosmwasm/src/cosmwasmclient.spec.ts | 2 +- packages/cosmwasm/src/cosmwasmclient.ts | 2 +- packages/cosmwasm/src/logs.ts | 2 +- packages/cosmwasm/src/restclient.spec.ts | 2 +- packages/cosmwasm/src/restclient.ts | 2 +- .../src/signingcosmwasmclient.spec.ts | 2 +- .../cosmwasm/src/signingcosmwasmclient.ts | 2 +- packages/cosmwasm/src/testutils.spec.ts | 2 +- packages/cosmwasm/src/types.ts | 2 +- packages/crypto/package.json | 4 ++- packages/crypto/src/bip39.spec.ts | 2 +- packages/crypto/src/bip39.ts | 2 +- packages/crypto/src/englishmnemonic.spec.ts | 2 +- packages/crypto/src/hmac.spec.ts | 2 +- packages/crypto/src/keccak.spec.ts | 2 +- packages/crypto/src/libsodium.spec.ts | 2 +- packages/crypto/src/random.spec.ts | 2 +- packages/crypto/src/ripemd.spec.ts | 2 +- packages/crypto/src/secp256k1.spec.ts | 2 +- packages/crypto/src/secp256k1.ts | 2 +- .../crypto/src/secp256k1signature.spec.ts | 2 +- packages/crypto/src/sha.spec.ts | 2 +- packages/crypto/src/slip10.spec.ts | 2 +- packages/crypto/src/slip10.ts | 3 ++- packages/crypto/types/slip10.d.ts | 2 +- packages/faucet/package.json | 3 ++- packages/faucet/src/addresses.ts | 2 +- packages/faucet/src/debugging.ts | 2 +- packages/faucet/src/faucet.spec.ts | 2 +- packages/faucet/src/tokenmanager.ts | 2 +- packages/sdk38/package.json | 3 ++- packages/sdk38/src/address.spec.ts | 2 +- packages/sdk38/src/address.ts | 2 +- .../sdk38/src/cosmosclient.searchtx.spec.ts | 2 +- packages/sdk38/src/cosmosclient.ts | 2 +- packages/sdk38/src/encoding.ts | 2 +- packages/sdk38/src/logs.ts | 2 +- packages/sdk38/src/pen.spec.ts | 2 +- packages/sdk38/src/pubkey.spec.ts | 2 +- packages/sdk38/src/pubkey.ts | 2 +- packages/sdk38/src/restclient.spec.ts | 2 +- packages/sdk38/src/restclient.ts | 3 ++- packages/sdk38/src/signature.spec.ts | 2 +- packages/sdk38/src/signature.ts | 2 +- packages/sdk38/src/testutils.spec.ts | 2 +- packages/utils/types/index.d.ts | 1 + packages/utils/types/typechecks.d.ts | 8 ++++++ 65 files changed, 87 insertions(+), 82 deletions(-) create mode 100644 packages/utils/types/typechecks.d.ts diff --git a/packages/bcp/package.json b/packages/bcp/package.json index 1b26354d..a89269db 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -40,10 +40,11 @@ }, "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/encoding": "^2.3.2", "@iov/stream": "^2.3.2", "bn.js": "^5.1.1", "fast-deep-equal": "^3.1.1", diff --git a/packages/bcp/src/address.spec.ts b/packages/bcp/src/address.spec.ts index aa077b0a..d6dbe3ff 100644 --- a/packages/bcp/src/address.spec.ts +++ b/packages/bcp/src/address.spec.ts @@ -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"; diff --git a/packages/bcp/src/address.ts b/packages/bcp/src/address.ts index 83b4c2f1..413f8b14 100644 --- a/packages/bcp/src/address.ts +++ b/packages/bcp/src/address.ts @@ -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 { 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 { diff --git a/packages/bcp/src/caip5.ts b/packages/bcp/src/caip5.ts index 95de524f..8c789ffe 100644 --- a/packages/bcp/src/caip5.ts +++ b/packages/bcp/src/caip5.ts @@ -1,6 +1,6 @@ import { Sha256 } from "@cosmjs/crypto"; +import { toHex, toUtf8 } from "@cosmjs/encoding"; import { ChainId } from "@iov/bcp"; -import { toHex, toUtf8 } from "@iov/encoding"; const hashedPrefix = "hashed-"; diff --git a/packages/bcp/src/cosmoscodec.spec.ts b/packages/bcp/src/cosmoscodec.spec.ts index 49437f9a..0540ca28 100644 --- a/packages/bcp/src/cosmoscodec.spec.ts +++ b/packages/bcp/src/cosmoscodec.spec.ts @@ -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"; diff --git a/packages/bcp/src/cosmoscodec.ts b/packages/bcp/src/cosmoscodec.ts index fa5b3c24..2a4eaff0 100644 --- a/packages/bcp/src/cosmoscodec.ts +++ b/packages/bcp/src/cosmoscodec.ts @@ -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"; diff --git a/packages/bcp/src/cosmosconnection.spec.ts b/packages/bcp/src/cosmosconnection.spec.ts index e47758dc..efcd9eb2 100644 --- a/packages/bcp/src/cosmosconnection.spec.ts +++ b/packages/bcp/src/cosmosconnection.spec.ts @@ -1,4 +1,5 @@ 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 { @@ -19,7 +20,6 @@ import { TransactionState, UnsignedTransaction, } from "@iov/bcp"; -import { Bech32, fromBase64 } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; import BN from "bn.js"; diff --git a/packages/bcp/src/cosmosconnection.ts b/packages/bcp/src/cosmosconnection.ts index 83e55905..32036c6c 100644 --- a/packages/bcp/src/cosmosconnection.ts +++ b/packages/bcp/src/cosmosconnection.ts @@ -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"; diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index e43aa110..ef4f96b1 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -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, diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index d7316ec4..d1812bd8 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -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"; diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index b6bd153d..c31fc507 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -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, diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index c6861285..371ecc95 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Secp256k1 } from "@cosmjs/crypto"; +import { toBase64 } from "@cosmjs/encoding"; import { Coin, CosmosSdkTx, @@ -20,7 +21,6 @@ import { SignedTransaction, UnsignedTransaction, } from "@iov/bcp"; -import { toBase64 } from "@iov/encoding"; import { BankToken } from "./types"; diff --git a/packages/bcp/src/testdata.spec.ts b/packages/bcp/src/testdata.spec.ts index 6dc2c519..c95749a0 100644 --- a/packages/bcp/src/testdata.spec.ts +++ b/packages/bcp/src/testdata.spec.ts @@ -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"; diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 5e89d64c..b2825da9 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -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; diff --git a/packages/cli/package.json b/packages/cli/package.json index 3e417545..93f05d03 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -40,9 +40,10 @@ "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", "@cosmjs/utils": "^0.8.0", - "@iov/encoding": "^2.3.2", "axios": "^0.19.2", "babylon": "^6.18.0", "colors": "^1.3.3", diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 6b960ef5..1e612b3d 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -75,6 +75,11 @@ export function main(originalArgs: readonly string[]): void { "Slip10RawIndex", ], ], + [ + "@cosmjs/encoding", + ["fromAscii", "fromBase64", "fromHex", "fromUtf8", "toAscii", "toBase64", "toHex", "toUtf8", "Bech32"], + ], + ["@cosmjs/math", ["Decimal", "Int53", "Uint32", "Uint53", "Uint64"]], [ "@cosmjs/sdk38", [ @@ -98,26 +103,6 @@ export function main(originalArgs: readonly string[]): void { "StdTx", ], ], - [ - "@iov/encoding", - [ - "fromAscii", - "fromBase64", - "fromHex", - "fromUtf8", - "toAscii", - "toBase64", - "toHex", - "toUtf8", - "Bech32", - "Decimal", - // integers - "Int53", - "Uint32", - "Uint53", - "Uint64", - ], - ], ["@cosmjs/utils", ["assert", "sleep"]], ]); diff --git a/packages/cosmwasm/package.json b/packages/cosmwasm/package.json index ccf45069..3d57d0e8 100644 --- a/packages/cosmwasm/package.json +++ b/packages/cosmwasm/package.json @@ -37,9 +37,10 @@ }, "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/encoding": "^2.3.2", "axios": "^0.19.0", "fast-deep-equal": "^3.1.1", "pako": "^1.0.11" diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index ca14c2d1..f6bef50d 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -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 { assert, sleep } from "@cosmjs/utils"; -import { Uint53 } from "@iov/encoding"; import { CosmWasmClient } from "./cosmwasmclient"; import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs"; diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 744f5397..b28fe430 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -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 { assert, sleep } from "@cosmjs/utils"; -import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@iov/encoding"; import { ReadonlyDate } from "readonly-date"; import { Code, CosmWasmClient, PrivateCosmWasmClient } from "./cosmwasmclient"; diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index ef24e064..886b860a 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -1,4 +1,5 @@ import { Sha256 } from "@cosmjs/crypto"; +import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { BroadcastMode, Coin, @@ -8,7 +9,6 @@ import { PubKey, StdTx, } from "@cosmjs/sdk38"; -import { fromBase64, fromHex, toHex } from "@iov/encoding"; import { Log, parseLogs } from "./logs"; import { RestClient } from "./restclient"; diff --git a/packages/cosmwasm/src/logs.ts b/packages/cosmwasm/src/logs.ts index e1eaa1cb..f37ff56c 100644 --- a/packages/cosmwasm/src/logs.ts +++ b/packages/cosmwasm/src/logs.ts @@ -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; diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index 3789f60b..007291b1 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -1,5 +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, @@ -16,7 +17,6 @@ import { StdTx, } from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; -import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@iov/encoding"; import { ReadonlyDate } from "readonly-date"; import { findAttribute, parseLogs } from "./logs"; diff --git a/packages/cosmwasm/src/restclient.ts b/packages/cosmwasm/src/restclient.ts index 7beb90ff..8da81232 100644 --- a/packages/cosmwasm/src/restclient.ts +++ b/packages/cosmwasm/src/restclient.ts @@ -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"; diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index 69d4995f..aab2c4b2 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -1,7 +1,7 @@ import { Sha256 } from "@cosmjs/crypto"; +import { toHex } from "@cosmjs/encoding"; import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; -import { toHex } from "@iov/encoding"; import { PrivateCosmWasmClient } from "./cosmwasmclient"; import { RestClient } from "./restclient"; diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index 9f5ad298..7dd234fe 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -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 { toBase64, toHex } from "@iov/encoding"; import pako from "pako"; import { isValidBuilder } from "./builder"; diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index bd106923..82c745a0 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -1,5 +1,5 @@ import { Random } from "@cosmjs/crypto"; -import { Bech32, fromBase64 } from "@iov/encoding"; +import { Bech32, fromBase64 } from "@cosmjs/encoding"; import hackatom from "./testdata/contract.json"; diff --git a/packages/cosmwasm/src/types.ts b/packages/cosmwasm/src/types.ts index f04c6982..bba7fc95 100644 --- a/packages/cosmwasm/src/types.ts +++ b/packages/cosmwasm/src/types.ts @@ -1,4 +1,4 @@ -import { fromBase64, fromHex } from "@iov/encoding"; +import { fromBase64, fromHex } from "@cosmjs/encoding"; export interface WasmData { // key is hex-encoded diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 6c626d87..a94e89c9 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -41,7 +41,9 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { - "@iov/encoding": "^2.3.2", + "@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", diff --git a/packages/crypto/src/bip39.spec.ts b/packages/crypto/src/bip39.spec.ts index 3e2b7a2b..d77298ee 100644 --- a/packages/crypto/src/bip39.spec.ts +++ b/packages/crypto/src/bip39.spec.ts @@ -1,4 +1,4 @@ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { Bip39 } from "./bip39"; import { EnglishMnemonic } from "./englishmnemonic"; diff --git a/packages/crypto/src/bip39.ts b/packages/crypto/src/bip39.ts index 89980e46..f16d6dea 100644 --- a/packages/crypto/src/bip39.ts +++ b/packages/crypto/src/bip39.ts @@ -1,4 +1,4 @@ -import { fromHex, toHex } from "@iov/encoding"; +import { fromHex, toHex } from "@cosmjs/encoding"; import * as bip39 from "bip39"; import { pbkdf2 } from "pbkdf2"; import * as unorm from "unorm"; diff --git a/packages/crypto/src/englishmnemonic.spec.ts b/packages/crypto/src/englishmnemonic.spec.ts index f1260dc5..e365c0b9 100644 --- a/packages/crypto/src/englishmnemonic.spec.ts +++ b/packages/crypto/src/englishmnemonic.spec.ts @@ -1,4 +1,4 @@ -import { fromAscii, fromBase64, fromHex } from "@iov/encoding"; +import { fromAscii, fromBase64, fromHex } from "@cosmjs/encoding"; import { EnglishMnemonic } from "./englishmnemonic"; import { Sha256 } from "./sha"; diff --git a/packages/crypto/src/hmac.spec.ts b/packages/crypto/src/hmac.spec.ts index a89fc29b..2a992c2b 100644 --- a/packages/crypto/src/hmac.spec.ts +++ b/packages/crypto/src/hmac.spec.ts @@ -1,4 +1,4 @@ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { Hmac } from "./hmac"; import { Sha1, Sha256, Sha512 } from "./sha"; diff --git a/packages/crypto/src/keccak.spec.ts b/packages/crypto/src/keccak.spec.ts index e82c8a73..ee1f20c7 100644 --- a/packages/crypto/src/keccak.spec.ts +++ b/packages/crypto/src/keccak.spec.ts @@ -1,4 +1,4 @@ -import { fromHex, toHex } from "@iov/encoding"; +import { fromHex, toHex } from "@cosmjs/encoding"; import { Keccak256 } from "./keccak"; import keccakVectors from "./testdata/keccak.json"; diff --git a/packages/crypto/src/libsodium.spec.ts b/packages/crypto/src/libsodium.spec.ts index d29f28f8..cc8bc8bc 100644 --- a/packages/crypto/src/libsodium.spec.ts +++ b/packages/crypto/src/libsodium.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable no-bitwise */ -import { fromHex, toAscii } from "@iov/encoding"; +import { fromHex, toAscii } from "@cosmjs/encoding"; import { Argon2id, diff --git a/packages/crypto/src/random.spec.ts b/packages/crypto/src/random.spec.ts index 6f9511b5..c5af21d7 100644 --- a/packages/crypto/src/random.spec.ts +++ b/packages/crypto/src/random.spec.ts @@ -1,4 +1,4 @@ -import { isUint8Array } from "@iov/encoding"; +import { isUint8Array } from "@cosmjs/utils"; import { Random } from "./random"; diff --git a/packages/crypto/src/ripemd.spec.ts b/packages/crypto/src/ripemd.spec.ts index d3a8dd37..234691c2 100644 --- a/packages/crypto/src/ripemd.spec.ts +++ b/packages/crypto/src/ripemd.spec.ts @@ -1,4 +1,4 @@ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { Ripemd160 } from "./ripemd"; import ripemdVectors from "./testdata/ripemd.json"; diff --git a/packages/crypto/src/secp256k1.spec.ts b/packages/crypto/src/secp256k1.spec.ts index 6af91378..36833fdf 100644 --- a/packages/crypto/src/secp256k1.spec.ts +++ b/packages/crypto/src/secp256k1.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable no-bitwise */ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { Secp256k1 } from "./secp256k1"; import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature"; diff --git a/packages/crypto/src/secp256k1.ts b/packages/crypto/src/secp256k1.ts index e0f1eb15..ec05ed8e 100644 --- a/packages/crypto/src/secp256k1.ts +++ b/packages/crypto/src/secp256k1.ts @@ -1,4 +1,4 @@ -import { fromHex, toHex } from "@iov/encoding"; +import { fromHex, toHex } from "@cosmjs/encoding"; import BN from "bn.js"; import elliptic from "elliptic"; import { As } from "type-tagger"; diff --git a/packages/crypto/src/secp256k1signature.spec.ts b/packages/crypto/src/secp256k1signature.spec.ts index 041f29e5..27b58767 100644 --- a/packages/crypto/src/secp256k1signature.spec.ts +++ b/packages/crypto/src/secp256k1signature.spec.ts @@ -1,4 +1,4 @@ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature"; diff --git a/packages/crypto/src/sha.spec.ts b/packages/crypto/src/sha.spec.ts index e0384da4..3549164b 100644 --- a/packages/crypto/src/sha.spec.ts +++ b/packages/crypto/src/sha.spec.ts @@ -1,4 +1,4 @@ -import { fromHex, toHex } from "@iov/encoding"; +import { fromHex, toHex } from "@cosmjs/encoding"; import { Sha256 } from "./sha"; import shaVectors from "./testdata/sha.json"; diff --git a/packages/crypto/src/slip10.spec.ts b/packages/crypto/src/slip10.spec.ts index 36838f6d..52718df8 100644 --- a/packages/crypto/src/slip10.spec.ts +++ b/packages/crypto/src/slip10.spec.ts @@ -1,4 +1,4 @@ -import { fromHex } from "@iov/encoding"; +import { fromHex } from "@cosmjs/encoding"; import { pathToString, diff --git a/packages/crypto/src/slip10.ts b/packages/crypto/src/slip10.ts index a2e37b63..9f507954 100644 --- a/packages/crypto/src/slip10.ts +++ b/packages/crypto/src/slip10.ts @@ -1,4 +1,5 @@ -import { fromHex, toAscii, Uint32, Uint53 } from "@iov/encoding"; +import { fromHex, toAscii } from "@cosmjs/encoding"; +import { Uint32, Uint53 } from "@cosmjs/math"; import BN from "bn.js"; import elliptic from "elliptic"; diff --git a/packages/crypto/types/slip10.d.ts b/packages/crypto/types/slip10.d.ts index 328116be..ffd92295 100644 --- a/packages/crypto/types/slip10.d.ts +++ b/packages/crypto/types/slip10.d.ts @@ -1,4 +1,4 @@ -import { Uint32 } from "@iov/encoding"; +import { Uint32 } from "@cosmjs/math"; export interface Slip10Result { readonly chainCode: Uint8Array; readonly privkey: Uint8Array; diff --git a/packages/faucet/package.json b/packages/faucet/package.json index dcaae4ff..8d660574 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -36,9 +36,10 @@ }, "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/encoding": "^2.3.2", "@koa/cors": "^3.0.0", "axios": "^0.19.0", "koa": "^2.11.0", diff --git a/packages/faucet/src/addresses.ts b/packages/faucet/src/addresses.ts index 6b1f8ddf..bafe414b 100644 --- a/packages/faucet/src/addresses.ts +++ b/packages/faucet/src/addresses.ts @@ -1,4 +1,4 @@ -import { Bech32 } from "@iov/encoding"; +import { Bech32 } from "@cosmjs/encoding"; export function isValidAddress(input: string, requiredPrefix: string): boolean { try { diff --git a/packages/faucet/src/debugging.ts b/packages/faucet/src/debugging.ts index 24b79804..1db74139 100644 --- a/packages/faucet/src/debugging.ts +++ b/packages/faucet/src/debugging.ts @@ -1,5 +1,5 @@ +import { Decimal } from "@cosmjs/math"; import { Coin } from "@cosmjs/sdk38"; -import { Decimal } from "@iov/encoding"; import { MinimalAccount, SendJob, TokenConfiguration } from "./types"; diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index 2809cd1c..3d0320e3 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -1,7 +1,7 @@ import { Random } from "@cosmjs/crypto"; +import { Bech32 } from "@cosmjs/encoding"; import { CosmosClient } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; -import { Bech32 } from "@iov/encoding"; import { Faucet } from "./faucet"; import { TokenConfiguration } from "./types"; diff --git a/packages/faucet/src/tokenmanager.ts b/packages/faucet/src/tokenmanager.ts index 1036c9a0..88996eee 100644 --- a/packages/faucet/src/tokenmanager.ts +++ b/packages/faucet/src/tokenmanager.ts @@ -1,5 +1,5 @@ +import { Decimal, Uint53 } from "@cosmjs/math"; import { Coin } from "@cosmjs/sdk38"; -import { Decimal, Uint53 } from "@iov/encoding"; import { BankTokenMeta, MinimalAccount, TokenConfiguration } from "./types"; diff --git a/packages/sdk38/package.json b/packages/sdk38/package.json index 3b655453..1764aa82 100644 --- a/packages/sdk38/package.json +++ b/packages/sdk38/package.json @@ -37,8 +37,9 @@ }, "dependencies": { "@cosmjs/crypto": "^0.8.0", + "@cosmjs/encoding": "^0.8.0", + "@cosmjs/math": "^0.8.0", "@cosmjs/utils": "^0.8.0", - "@iov/encoding": "^2.3.2", "axios": "^0.19.0", "fast-deep-equal": "^3.1.1" }, diff --git a/packages/sdk38/src/address.spec.ts b/packages/sdk38/src/address.spec.ts index 2a9de080..f5e3b627 100644 --- a/packages/sdk38/src/address.spec.ts +++ b/packages/sdk38/src/address.spec.ts @@ -1,4 +1,4 @@ -import { fromHex, toBase64 } from "@iov/encoding"; +import { fromHex, toBase64 } from "@cosmjs/encoding"; import { pubkeyToAddress } from "./address"; diff --git a/packages/sdk38/src/address.ts b/packages/sdk38/src/address.ts index cfdd153a..5f48efa0 100644 --- a/packages/sdk38/src/address.ts +++ b/packages/sdk38/src/address.ts @@ -1,5 +1,5 @@ import { Ripemd160, Sha256 } from "@cosmjs/crypto"; -import { Bech32, fromBase64 } from "@iov/encoding"; +import { Bech32, fromBase64 } from "@cosmjs/encoding"; import { PubKey, pubkeyType } from "./types"; diff --git a/packages/sdk38/src/cosmosclient.searchtx.spec.ts b/packages/sdk38/src/cosmosclient.searchtx.spec.ts index 25d06700..c75c5e88 100644 --- a/packages/sdk38/src/cosmosclient.searchtx.spec.ts +++ b/packages/sdk38/src/cosmosclient.searchtx.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { Uint53 } from "@cosmjs/math"; import { assert, sleep } from "@cosmjs/utils"; -import { Uint53 } from "@iov/encoding"; import { Coin } from "./coins"; import { CosmosClient } from "./cosmosclient"; diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 44232426..4c42f385 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -1,5 +1,5 @@ import { Sha256 } from "@cosmjs/crypto"; -import { fromBase64, toHex } from "@iov/encoding"; +import { fromBase64, toHex } from "@cosmjs/encoding"; import { Coin } from "./coins"; import { Log, parseLogs } from "./logs"; diff --git a/packages/sdk38/src/encoding.ts b/packages/sdk38/src/encoding.ts index 2d03e0f4..30a3193e 100644 --- a/packages/sdk38/src/encoding.ts +++ b/packages/sdk38/src/encoding.ts @@ -1,4 +1,4 @@ -import { toUtf8 } from "@iov/encoding"; +import { toUtf8 } from "@cosmjs/encoding"; import { Msg, StdFee } from "./types"; diff --git a/packages/sdk38/src/logs.ts b/packages/sdk38/src/logs.ts index e1eaa1cb..f37ff56c 100644 --- a/packages/sdk38/src/logs.ts +++ b/packages/sdk38/src/logs.ts @@ -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; diff --git a/packages/sdk38/src/pen.spec.ts b/packages/sdk38/src/pen.spec.ts index 13bf1b5c..89b37d1b 100644 --- a/packages/sdk38/src/pen.spec.ts +++ b/packages/sdk38/src/pen.spec.ts @@ -1,5 +1,5 @@ import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; -import { fromHex, toAscii } from "@iov/encoding"; +import { fromHex, toAscii } from "@cosmjs/encoding"; import { Secp256k1Pen } from "./pen"; import { decodeSignature } from "./signature"; diff --git a/packages/sdk38/src/pubkey.spec.ts b/packages/sdk38/src/pubkey.spec.ts index f9054ace..03253695 100644 --- a/packages/sdk38/src/pubkey.spec.ts +++ b/packages/sdk38/src/pubkey.spec.ts @@ -1,4 +1,4 @@ -import { fromBase64 } from "@iov/encoding"; +import { fromBase64 } from "@cosmjs/encoding"; import { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; import { PubKey } from "./types"; diff --git a/packages/sdk38/src/pubkey.ts b/packages/sdk38/src/pubkey.ts index ada06a75..2095c05a 100644 --- a/packages/sdk38/src/pubkey.ts +++ b/packages/sdk38/src/pubkey.ts @@ -1,4 +1,4 @@ -import { Bech32, fromBase64, fromHex, toBase64, toHex } from "@iov/encoding"; +import { Bech32, fromBase64, fromHex, toBase64, toHex } from "@cosmjs/encoding"; import equal from "fast-deep-equal"; import { PubKey, pubkeyType } from "./types"; diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index b014803a..92d3f0ed 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { fromBase64 } from "@cosmjs/encoding"; import { assert, sleep } from "@cosmjs/utils"; -import { fromBase64 } from "@iov/encoding"; import { ReadonlyDate } from "readonly-date"; import { rawSecp256k1PubkeyToAddress } from "./address"; diff --git a/packages/sdk38/src/restclient.ts b/packages/sdk38/src/restclient.ts index 6a1363ba..7fbdf8f8 100644 --- a/packages/sdk38/src/restclient.ts +++ b/packages/sdk38/src/restclient.ts @@ -1,4 +1,5 @@ -import { fromBase64, isNonNullObject } from "@iov/encoding"; +import { fromBase64 } from "@cosmjs/encoding"; +import { isNonNullObject } from "@cosmjs/utils"; import axios, { AxiosError, AxiosInstance } from "axios"; import { Coin } from "./coins"; diff --git a/packages/sdk38/src/signature.spec.ts b/packages/sdk38/src/signature.spec.ts index 7466caca..6d29d18a 100644 --- a/packages/sdk38/src/signature.spec.ts +++ b/packages/sdk38/src/signature.spec.ts @@ -1,4 +1,4 @@ -import { fromBase64 } from "@iov/encoding"; +import { fromBase64 } from "@cosmjs/encoding"; import { decodeSignature, encodeSecp256k1Signature } from "./signature"; import { StdSignature } from "./types"; diff --git a/packages/sdk38/src/signature.ts b/packages/sdk38/src/signature.ts index fd89da65..bd38fde6 100644 --- a/packages/sdk38/src/signature.ts +++ b/packages/sdk38/src/signature.ts @@ -1,4 +1,4 @@ -import { fromBase64, toBase64 } from "@iov/encoding"; +import { fromBase64, toBase64 } from "@cosmjs/encoding"; import { encodeSecp256k1Pubkey } from "./pubkey"; import { pubkeyType, StdSignature } from "./types"; diff --git a/packages/sdk38/src/testutils.spec.ts b/packages/sdk38/src/testutils.spec.ts index 6864b7ef..b04b1de5 100644 --- a/packages/sdk38/src/testutils.spec.ts +++ b/packages/sdk38/src/testutils.spec.ts @@ -1,5 +1,5 @@ import { Random } from "@cosmjs/crypto"; -import { Bech32 } from "@iov/encoding"; +import { Bech32 } from "@cosmjs/encoding"; export function makeRandomAddress(): string { return Bech32.encode("cosmos", Random.getBytes(20)); diff --git a/packages/utils/types/index.d.ts b/packages/utils/types/index.d.ts index 3e156674..ea096301 100644 --- a/packages/utils/types/index.d.ts +++ b/packages/utils/types/index.d.ts @@ -1,2 +1,3 @@ export { assert } from "./assert"; export { sleep } from "./sleep"; +export { isNonNullObject, isUint8Array } from "./typechecks"; diff --git a/packages/utils/types/typechecks.d.ts b/packages/utils/types/typechecks.d.ts new file mode 100644 index 00000000..3e911797 --- /dev/null +++ b/packages/utils/types/typechecks.d.ts @@ -0,0 +1,8 @@ +/** + * Checks if data is a non-null object (i.e. matches the TypeScript object type) + */ +export declare function isNonNullObject(data: unknown): data is object; +/** + * Checks if data is an Uint8Array. Note: Buffer is treated as not a Uint8Array + */ +export declare function isUint8Array(data: unknown): data is Uint8Array; From ac6fd0e11551ed1ecaa3ea7b0504dc2f67eae286 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 14:25:09 +0200 Subject: [PATCH 08/12] Fix broken package name @iov/cosmjs in README --- packages/crypto/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/crypto/README.md b/packages/crypto/README.md index 36d72d6f..abd8c68b 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -2,11 +2,11 @@ [![npm version](https://img.shields.io/npm/v/@cosmjs/crypto.svg)](https://www.npmjs.com/package/@cosmjs/crypto) -@iov/cosmjs 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. +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 From d23b303aa85297254e3f12dfe35f4feb8fc43c76 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 14:26:55 +0200 Subject: [PATCH 09/12] Add text formatters to encoding and math --- packages/encoding/package.json | 1 + packages/math/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/encoding/package.json b/packages/encoding/package.json index 5eedd106..e4fa4f3d 100644 --- a/packages/encoding/package.json +++ b/packages/encoding/package.json @@ -24,6 +24,7 @@ "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", diff --git a/packages/math/package.json b/packages/math/package.json index b1c39533..98cfa72a 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -24,6 +24,7 @@ "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", From ae7370b16ee9cd9cc44802f34e41671dafd00b9f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 14:37:28 +0200 Subject: [PATCH 10/12] Remove unnecessary dependencies from @cosmjs/math --- packages/math/package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/math/package.json b/packages/math/package.json index 98cfa72a..7236bb41 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -38,13 +38,9 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { - "base64-js": "^1.3.0", - "bech32": "^1.1.4", - "bn.js": "^4.11.8", - "readonly-date": "^1.0.0" + "bn.js": "^4.11.8" }, "devDependencies": { - "@types/base64-js": "^1.2.5", "@types/bn.js": "^4.11.6" } } From 064500e55a314f1bc004f15103594bff3e6643d2 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 5 Jun 2020 14:51:18 +0200 Subject: [PATCH 11/12] Set @iov/cosmos-sdk to private --- packages/bcp/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/bcp/package.json b/packages/bcp/package.json index a89269db..d9d0b7f8 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -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\"", From 17aa817b2355523cfd63511ec07213a565d41232 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 9 Jun 2020 18:02:29 +0200 Subject: [PATCH 12/12] Update repo URLs --- packages/crypto/README.md | 6 +++--- packages/crypto/package.json | 2 +- packages/encoding/README.md | 6 +++--- packages/encoding/package.json | 2 +- packages/math/README.md | 6 +++--- packages/math/package.json | 2 +- packages/utils/README.md | 6 +++--- packages/utils/package.json | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/crypto/README.md b/packages/crypto/README.md index abd8c68b..a8da6d18 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -10,7 +10,7 @@ imported outside of CosmJS based applications. ## License -This package is part of the cosmwasm-js repository, licensed under the Apache +This package is part of the cosmjs repository, licensed under the Apache License 2.0 (see -[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and -[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). +[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and +[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)). diff --git a/packages/crypto/package.json b/packages/crypto/package.json index a94e89c9..e0078eaa 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -18,7 +18,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/CosmWasm/cosmwasm-js/tree/master/packages/crypto" + "url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/crypto" }, "publishConfig": { "access": "public" diff --git a/packages/encoding/README.md b/packages/encoding/README.md index c8845fa4..785c41a1 100644 --- a/packages/encoding/README.md +++ b/packages/encoding/README.md @@ -18,7 +18,7 @@ on invalid input. ## License -This package is part of the cosmwasm-js repository, licensed under the Apache +This package is part of the cosmjs repository, licensed under the Apache License 2.0 (see -[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and -[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). +[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and +[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)). diff --git a/packages/encoding/package.json b/packages/encoding/package.json index e4fa4f3d..68bf0e46 100644 --- a/packages/encoding/package.json +++ b/packages/encoding/package.json @@ -15,7 +15,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/CosmWasm/cosmwasm-js/tree/master/packages/encoding" + "url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/encoding" }, "publishConfig": { "access": "public" diff --git a/packages/math/README.md b/packages/math/README.md index 36092095..665f0a39 100644 --- a/packages/math/README.md +++ b/packages/math/README.md @@ -4,7 +4,7 @@ ## License -This package is part of the cosmwasm-js repository, licensed under the Apache +This package is part of the cosmjs repository, licensed under the Apache License 2.0 (see -[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and -[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). +[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and +[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)). diff --git a/packages/math/package.json b/packages/math/package.json index 7236bb41..67445825 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -15,7 +15,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/CosmWasm/cosmwasm-js/tree/master/packages/math" + "url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/math" }, "publishConfig": { "access": "public" diff --git a/packages/utils/README.md b/packages/utils/README.md index af296daa..0ca47c2a 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -7,7 +7,7 @@ but stuff like `sleep` can also be useful at runtime. ## License -This package is part of the cosmwasm-js repository, licensed under the Apache +This package is part of the cosmjs repository, licensed under the Apache License 2.0 (see -[NOTICE](https://github.com/confio/cosmwasm-js/blob/master/NOTICE) and -[LICENSE](https://github.com/confio/cosmwasm-js/blob/master/LICENSE)). +[NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and +[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)). diff --git a/packages/utils/package.json b/packages/utils/package.json index afc0fe2c..90c65ab0 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -15,7 +15,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/CosmWasm/cosmwasm-js/tree/master/packages/utils" + "url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/utils" }, "publishConfig": { "access": "public"