diff --git a/package.json b/package.json index 44e154043..4070b1e97 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@blockone/tslint-config-blockone": "3.0.0", + "@types/elliptic": "^6.4.9", "@types/jest": "24.0.6", "@types/node": "11.9.4", "@types/text-encoding": "0.0.35", @@ -42,6 +43,7 @@ "babel-preset-env": "1.7.0", "babel-preset-stage-1": "6.24.1", "cypress": "3.1.5", + "elliptic": "^6.5.0", "jest": "23.5.0", "jest-fetch-mock": "2.1.1", "json-loader": "0.5.7", @@ -55,6 +57,11 @@ "webpack": "4.29.5", "webpack-cli": "3.2.3" }, + "resolutions": { + "braces": "2.3.1", + "handlebars": "4.1.2", + "js-yaml": "3.13.1" + }, "jest": { "automock": false, "setupFiles": [ diff --git a/src/eosjs-numeric.ts b/src/eosjs-numeric.ts index 53ab250b8..e9e20fa2c 100644 --- a/src/eosjs-numeric.ts +++ b/src/eosjs-numeric.ts @@ -124,11 +124,41 @@ export function signedBinaryToDecimal(bignum: Uint8Array, minDigits = 1) { return binaryToDecimal(bignum, minDigits); } +function base58ToBinaryVarSize(s: string) { + const result = [] as number[]; + for (let i = 0; i < s.length; ++i) { + let carry = base58Map[s.charCodeAt(i)]; + if (carry < 0) { + throw new Error('invalid base-58 value'); + } + for (let j = 0; j < result.length; ++j) { + const x = result[j] * 58 + carry; + result[j] = x & 0xff; + carry = x >> 8; + } + if (carry) { + result.push(carry); + } + } + for (const ch of s) { + if (ch === '1') { + result.push(0); + } else { + break; + } + } + result.reverse(); + return new Uint8Array(result); +} + /** * Convert an unsigned base-58 number in `s` to a bignum * @param size bignum size (bytes) */ export function base58ToBinary(size: number, s: string) { + if (!size) { + return base58ToBinaryVarSize(s); + } const result = new Uint8Array(size); for (let i = 0; i < s.length; ++i) { let carry = base58Map[s.charCodeAt(i)]; @@ -217,6 +247,7 @@ export function base64ToBinary(s: string) { export enum KeyType { k1 = 0, r1 = 1, + wa = 2, } /** Public key data size, excluding type field */ @@ -246,11 +277,11 @@ function digestSuffixRipemd160(data: Uint8Array, suffix: string) { } function stringToKey(s: string, type: KeyType, size: number, suffix: string): Key { - const whole = base58ToBinary(size + 4, s); - const result = { type, data: new Uint8Array(whole.buffer, 0, size) }; + const whole = base58ToBinary(size ? size + 4 : 0, s); + const result = { type, data: new Uint8Array(whole.buffer, 0, whole.length - 4) }; const digest = new Uint8Array(digestSuffixRipemd160(result.data, suffix)); - if (digest[0] !== whole[size + 0] || digest[1] !== whole[size + 1] - || digest[2] !== whole[size + 2] || digest[3] !== whole[size + 3]) { + if (digest[0] !== whole[whole.length - 4] || digest[1] !== whole[whole.length - 3] + || digest[2] !== whole[whole.length - 2] || digest[3] !== whole[whole.length - 1]) { throw new Error('checksum doesn\'t match'); } return result; @@ -289,6 +320,8 @@ export function stringToPublicKey(s: string): Key { return stringToKey(s.substr(7), KeyType.k1, publicKeyDataSize, 'K1'); } else if (s.substr(0, 7) === 'PUB_R1_') { return stringToKey(s.substr(7), KeyType.r1, publicKeyDataSize, 'R1'); + } else if (s.substr(0, 7) === 'PUB_WA_') { + return stringToKey(s.substr(7), KeyType.wa, 0, 'WA'); } else { throw new Error('unrecognized public key format'); } @@ -300,6 +333,8 @@ export function publicKeyToString(key: Key) { return keyToString(key, 'K1', 'PUB_K1_'); } else if (key.type === KeyType.r1 && key.data.length === publicKeyDataSize) { return keyToString(key, 'R1', 'PUB_R1_'); + } else if (key.type === KeyType.wa) { + return keyToString(key, 'WA', 'PUB_WA_'); } else { throw new Error('unrecognized public key format'); } @@ -352,6 +387,8 @@ export function stringToSignature(s: string): Key { return stringToKey(s.substr(7), KeyType.k1, signatureDataSize, 'K1'); } else if (s.substr(0, 7) === 'SIG_R1_') { return stringToKey(s.substr(7), KeyType.r1, signatureDataSize, 'R1'); + } else if (s.substr(0, 7) === 'SIG_WA_') { + return stringToKey(s.substr(7), KeyType.wa, 0, 'WA'); } else { throw new Error('unrecognized signature format'); } @@ -363,6 +400,8 @@ export function signatureToString(signature: Key) { return keyToString(signature, 'K1', 'SIG_K1_'); } else if (signature.type === KeyType.r1) { return keyToString(signature, 'R1', 'SIG_R1_'); + } else if (signature.type === KeyType.wa) { + return keyToString(signature, 'WA', 'SIG_WA_'); } else { throw new Error('unrecognized signature format'); } diff --git a/src/eosjs-serialize.ts b/src/eosjs-serialize.ts index da4a1033b..82a5000a2 100644 --- a/src/eosjs-serialize.ts +++ b/src/eosjs-serialize.ts @@ -198,6 +198,14 @@ export class SerialBuffer { // tslint:disable-line max-classes-per-file return result; } + /** Skip `len` bytes */ + public skip(len: number) { + if (this.readPos + len > this.length) { + throw new Error('Read past end of buffer'); + } + this.readPos += len; + } + /** Append a `uint16` */ public pushUint16(v: number) { this.push((v >> 0) & 0xff, (v >> 8) & 0xff); @@ -489,7 +497,15 @@ export class SerialBuffer { // tslint:disable-line max-classes-per-file /** Get a public key */ public getPublicKey() { const type = this.get(); - const data = this.getUint8Array(numeric.publicKeyDataSize); + let data: Uint8Array; + if (type === numeric.KeyType.wa) { + const begin = this.readPos; + this.skip(34); + this.skip(this.getVaruint32()); + data = new Uint8Array(this.array.buffer, this.array.byteOffset + begin, this.readPos - begin); + } else { + data = this.getUint8Array(numeric.publicKeyDataSize); + } return numeric.publicKeyToString({ type, data }); } @@ -517,7 +533,16 @@ export class SerialBuffer { // tslint:disable-line max-classes-per-file /** Get a signature */ public getSignature() { const type = this.get(); - const data = this.getUint8Array(numeric.signatureDataSize); + let data: Uint8Array; + if (type === numeric.KeyType.wa) { + const begin = this.readPos; + this.skip(65); + this.skip(this.getVaruint32()); + this.skip(this.getVaruint32()); + data = new Uint8Array(this.array.buffer, this.array.byteOffset + begin, this.readPos - begin); + } else { + data = this.getUint8Array(numeric.signatureDataSize); + } return numeric.signatureToString({ type, data }); } } // SerialBuffer diff --git a/src/eosjs-webauthn-sig.ts b/src/eosjs-webauthn-sig.ts new file mode 100644 index 000000000..a27e12e6b --- /dev/null +++ b/src/eosjs-webauthn-sig.ts @@ -0,0 +1,104 @@ +/** + * @module WebAuthn-Sig + */ +// copyright defined in eosjs/LICENSE.txt + +import { SignatureProvider, SignatureProviderArgs } from './eosjs-api-interfaces'; +import * as ser from './eosjs-serialize'; +import * as numeric from './eosjs-numeric'; +import { ec } from 'elliptic'; + +/** Signs transactions using WebAuthn */ +export class WebAuthnSignatureProvider implements SignatureProvider { + /** Map public key to credential ID (hex). User must populate this. */ + public keys = new Map(); + + /** Public keys that the `SignatureProvider` holds */ + public async getAvailableKeys() { + return Array.from(this.keys.keys()); + } + + /** Sign a transaction */ + public async sign( + { chainId, requiredKeys, serializedTransaction, serializedContextFreeData }: + SignatureProviderArgs, + ) { + const signBuf = new ser.SerialBuffer(); + signBuf.pushArray(ser.hexToUint8Array(chainId)); + signBuf.pushArray(serializedTransaction); + if (serializedContextFreeData) { + signBuf.pushArray(new Uint8Array(await crypto.subtle.digest('SHA-256', serializedContextFreeData.buffer))); + } else { + signBuf.pushArray(new Uint8Array(32)); + } + const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', signBuf.asUint8Array().slice().buffer)); + + const signatures = [] as string[]; + for (const key of requiredKeys) { + const id = ser.hexToUint8Array(this.keys.get(key)); + const assertion = await (navigator as any).credentials.get({ + publicKey: { + timeout: 60000, + allowCredentials: [{ + id, + type: 'public-key', + }], + challenge: digest.buffer, + }, + }); + const e = new ec('p256') as any; + const pubKey = e.keyFromPublic(numeric.stringToPublicKey(key).data.subarray(0, 33)).getPublic(); + + const fixup = (x: Uint8Array) => { + const a = Array.from(x); + while (a.length < 32) { + a.unshift(0); + } + while (a.length > 32) { + if (a.shift() !== 0) { + throw new Error('Signature has an r or s that is too big'); + } + } + return new Uint8Array(a); + }; + + const der = new ser.SerialBuffer({ array: new Uint8Array(assertion.response.signature) }); + if (der.get() !== 0x30) { + throw new Error('Signature missing DER prefix'); + } + if (der.get() !== der.array.length - 2) { + throw new Error('Signature has bad length'); + } + if (der.get() !== 0x02) { + throw new Error('Signature has bad r marker'); + } + const r = fixup(der.getUint8Array(der.get())); + if (der.get() !== 0x02) { + throw new Error('Signature has bad s marker'); + } + const s = fixup(der.getUint8Array(der.get())); + + const whatItReallySigned = new ser.SerialBuffer(); + whatItReallySigned.pushArray(new Uint8Array(assertion.response.authenticatorData)); + whatItReallySigned.pushArray(new Uint8Array( + await crypto.subtle.digest('SHA-256', assertion.response.clientDataJSON))); + const hash = new Uint8Array( + await crypto.subtle.digest('SHA-256', whatItReallySigned.asUint8Array().slice())); + const recid = e.getKeyRecoveryParam(hash, new Uint8Array(assertion.response.signature), pubKey); + + const sigData = new ser.SerialBuffer(); + sigData.push(recid + 27 + 4); + sigData.pushArray(r); + sigData.pushArray(s); + sigData.pushBytes(new Uint8Array(assertion.response.authenticatorData)); + sigData.pushBytes(new Uint8Array(assertion.response.clientDataJSON)); + + const sig = numeric.signatureToString({ + type: numeric.KeyType.wa, + data: sigData.asUint8Array().slice(), + }); + signatures.push(sig); + } + return { signatures, serializedTransaction, serializedContextFreeData }; + } +} diff --git a/yarn.lock b/yarn.lock index 0d034848f..896ccb254 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,6 +54,13 @@ resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6" integrity sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w== +"@types/bn.js@*": + version "4.11.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.5.tgz#40e36197433f78f807524ec623afcf0169ac81dc" + integrity sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng== + dependencies: + "@types/node" "*" + "@types/chai-jquery@1.1.35": version "1.1.35" resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04" @@ -72,6 +79,13 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" integrity sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg== +"@types/elliptic@^6.4.9": + version "6.4.9" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.9.tgz#e24874622535dd022778cf56e7421799375108d8" + integrity sha512-Mn+OyENd6YHwJKgUSyCTUDunEDFMaFpCXt52JCA00sxtzEa1ji6H0doZHL3iXhqMTo1Ob53X+Dv0s4PAJ+IVlA== + dependencies: + "@types/bn.js" "*" + "@types/jest-diff@*": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89" @@ -111,6 +125,11 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" integrity sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw== +"@types/node@*": + version "12.7.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.1.tgz#3b5c3a26393c19b400844ac422bd0f631a94d69d" + integrity sha512-aK9jxMypeSrhiYofWWBf/T7O+KwaiAHzM4sveCdWPn71lzUSMimRnKzhXDKfKwV1kWoBo2P1aGgaIYGLf9/ljw== + "@types/node@11.9.4": version "11.9.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.4.tgz#ceb0048a546db453f6248f2d1d95e937a6f00a14" @@ -1361,25 +1380,18 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== +braces@2.3.1, braces@^1.8.2, braces@^2.3.1, braces@^2.3.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + integrity sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" + define-property "^1.0.0" extend-shallow "^2.0.1" fill-range "^4.0.0" isobject "^3.0.1" + kind-of "^6.0.2" repeat-element "^1.1.2" snapdragon "^0.8.1" snapdragon-node "^2.0.1" @@ -2258,6 +2270,19 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +elliptic@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" + integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -2481,13 +2506,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -2628,17 +2646,6 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2946,10 +2953,10 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.0.3: - version "4.1.1" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.1.tgz#6e4e41c18ebe7719ae4d38e5aca3d32fa3dd23d3" - integrity sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA== +handlebars@4.1.2, handlebars@^4.0.3: + version "4.1.2" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" + integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -3400,13 +3407,6 @@ is-installed-globally@0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3414,11 +3414,6 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - is-path-inside@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" @@ -3924,10 +3919,10 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.7.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e" - integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ== +js-yaml@3.13.1, js-yaml@^3.7.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -4323,11 +4318,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -math-random@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" - integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -5180,11 +5170,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= - pretty-format@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" @@ -5322,15 +5307,6 @@ ramda@0.24.1: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc= -randomatic@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" - integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5497,7 +5473,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=