Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db738e978a | |||
| 023c2f640b | |||
| 46e751cea3 | |||
| dd8c11bea4 | |||
| 928ca5ca64 | |||
| 67c38498f4 | |||
| d0f14f83f9 |
@ -1,5 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
- "10"
|
||||
- "12"
|
||||
script: npm run ci
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
require('fake-indexeddb/auto')
|
||||
// eslint-disable-next-line
|
||||
window.crypto = require('isomorphic-webcrypto')
|
||||
264
package-lock.json
generated
264
package-lock.json
generated
@ -2279,6 +2279,39 @@
|
||||
"@types/yargs": "^12.0.9"
|
||||
}
|
||||
},
|
||||
"@peculiar/asn1-schema": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.3.tgz",
|
||||
"integrity": "sha512-Tfgj9eNJ6cTKEtEuidKenLHMx/Q5M8KEE9hnohHqvdpqHJXWYr5RlT3GjAHPjGXy5+mr7sSfuXfzE6aAkEGN7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asn1js": "^2.0.22",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@peculiar/json-schema": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.6.tgz",
|
||||
"integrity": "sha512-A8DM0ueA+LUqD/HuNPHDd8yMkhbRmnV0iosxyB/uOV1cfiKlCKXDeqkzHTOZpveRI05iCjZxqkPZ2+Nnw1wB4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@peculiar/webcrypto": {
|
||||
"version": "1.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.21.tgz",
|
||||
"integrity": "sha512-dMQe+vTKSKDpiizQj5q7lFqU56zBgavrjcST4d8RMxEbmgoUOuAUOXlkI5DoqVy3ktcfAhk6CRV4YkaSUEXdAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@peculiar/asn1-schema": "^1.0.3",
|
||||
"@peculiar/json-schema": "^1.1.5",
|
||||
"asn1js": "^2.0.26",
|
||||
"pvtsutils": "^1.0.6",
|
||||
"tslib": "^1.10.0",
|
||||
"webcrypto-core": "^1.0.14"
|
||||
}
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
|
||||
@ -2745,6 +2778,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@unimodules/core": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@unimodules/core/-/core-4.0.0.tgz",
|
||||
"integrity": "sha512-lHxRmCG9DK3/aA2lnBKPS32K95NpYE10mZQRp5dycSptgN0DIeWWHuE01SndcSUACGyEP+tDO+DnGo8mhLlt4Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"compare-versions": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"@unimodules/react-native-adapter": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-4.0.0.tgz",
|
||||
"integrity": "sha512-zGAyDhqAEWvshdSxc523srP6OAZaSr95Cv5EuxLJbFGcJENHhK8o/qxhwS7/LYTF3LqtOlnSlwQta3v3y6kF4A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"invariant": "^2.2.4",
|
||||
"lodash": "^4.5.0",
|
||||
"prop-types": "^15.6.1"
|
||||
}
|
||||
},
|
||||
"JSONStream": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
|
||||
@ -3323,6 +3378,12 @@
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||
},
|
||||
"asmcrypto.js": {
|
||||
"version": "0.22.0",
|
||||
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz",
|
||||
"integrity": "sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==",
|
||||
"dev": true
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
@ -3343,6 +3404,15 @@
|
||||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"asn1js": {
|
||||
"version": "2.0.26",
|
||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz",
|
||||
"integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pvutils": "^1.0.17"
|
||||
}
|
||||
},
|
||||
"assert": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
|
||||
@ -3429,6 +3499,24 @@
|
||||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"b64-lite": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/b64-lite/-/b64-lite-1.4.0.tgz",
|
||||
"integrity": "sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base-64": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"b64u-lite": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/b64u-lite/-/b64u-lite-1.1.0.tgz",
|
||||
"integrity": "sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"b64-lite": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"babel-core": {
|
||||
"version": "7.0.0-bridge.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
|
||||
@ -3838,11 +3926,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"base-64": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
|
||||
"integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=",
|
||||
"dev": true
|
||||
},
|
||||
"base64-arraybuffer": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
||||
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
||||
},
|
||||
"base64-arraybuffer-es6": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.5.0.tgz",
|
||||
"integrity": "sha512-UCIPaDJrNNj5jG2ZL+nzJ7czvZV/ZYX6LaIRgfVU1k1edJOQg7dkbiSKzwHkNp6aHEHER/PhlFBrMYnlvJJQEw==",
|
||||
"dev": true
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
|
||||
@ -4643,6 +4743,13 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"compare-versions": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz",
|
||||
"integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"component-bind": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||
@ -5899,6 +6006,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"expo-random": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-random/-/expo-random-7.0.0.tgz",
|
||||
"integrity": "sha512-+Ajxz4ZwTl2xGIGOg24xaIAw/RvGfKbkDlpsnwKc1FQr7Eka3ZvdQ3q6XKCXtqMG99jXmk9w6YVcbSUMyakpag==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
@ -6109,6 +6226,16 @@
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
|
||||
"dev": true
|
||||
},
|
||||
"fake-indexeddb": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-3.0.0.tgz",
|
||||
"integrity": "sha512-VrnV9dJWlVWvd8hp9MMR+JS4RLC4ZmToSkuCg91ZwpYE5mSODb3n5VEaV62Hf3AusnbrPfwQhukU+rGZm5W8PQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"realistic-structured-clone": "^2.0.1",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
@ -7880,6 +8007,25 @@
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"isomorphic-webcrypto": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.2.tgz",
|
||||
"integrity": "sha512-XbUC6ZwVvJfts2FYfAuNRU99tt89EzTknZcKZjxx6/6hxNeLgF6sIDbA/RdA3spbcNrXYyPOHa90khbUgZWarw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@peculiar/webcrypto": "^1.0.19",
|
||||
"@unimodules/core": "*",
|
||||
"@unimodules/react-native-adapter": "*",
|
||||
"asmcrypto.js": "^0.22.0",
|
||||
"b64-lite": "^1.3.1",
|
||||
"b64u-lite": "^1.0.1",
|
||||
"expo-random": "*",
|
||||
"msrcrypto": "^1.5.6",
|
||||
"react-native-securerandom": "^0.1.1",
|
||||
"str2buf": "^1.3.0",
|
||||
"webcrypto-shim": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@ -9782,6 +9928,12 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"msrcrypto": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/msrcrypto/-/msrcrypto-1.5.7.tgz",
|
||||
"integrity": "sha512-vH/uVdMPgdtLrDCdR2gWps2fB10EYWjXYi67W9RzNSd5Jch3noWGUvNUXSIJA87VTDaE+wvjS7yRSN4gALTslg==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||
@ -10763,6 +10915,30 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"pvtsutils": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.6.tgz",
|
||||
"integrity": "sha512-0yNrOdJyLE7FZzmeEHTKanwBr5XbmDAd020cKa4ZiTYuGMBYBZmq7vHOhcOqhVllh6gghDBbaz1lnVdOqiB7cw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "^10.14.17",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.6.tgz",
|
||||
"integrity": "sha512-0a2X6cgN3RdPBL2MIlR6Lt0KlM7fOFsutuXcdglcOq6WvLnYXgPQSh0Mx6tO1KCAE8MxbHSOSTWDoUxRq+l3DA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"pvutils": {
|
||||
"version": "1.0.17",
|
||||
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz",
|
||||
"integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
@ -10863,6 +11039,16 @@
|
||||
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
|
||||
"dev": true
|
||||
},
|
||||
"react-native-securerandom": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz",
|
||||
"integrity": "sha1-8TBiOkEsM4sK+t7bwgTFy7i/IHA=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"base64-js": "*"
|
||||
}
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.3.tgz",
|
||||
@ -11279,6 +11465,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"realistic-structured-clone": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/realistic-structured-clone/-/realistic-structured-clone-2.0.2.tgz",
|
||||
"integrity": "sha512-5IEvyfuMJ4tjQOuKKTFNvd+H9GSbE87IcendSBannE28PTrbolgaVg5DdEApRKhtze794iXqVUFKV60GLCNKEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-js": "^2.5.3",
|
||||
"domexception": "^1.0.1",
|
||||
"typeson": "^5.8.2",
|
||||
"typeson-registry": "^1.0.0-alpha.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz",
|
||||
"integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"realpath-native": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz",
|
||||
@ -11843,6 +12049,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
|
||||
"dev": true
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
@ -12345,6 +12557,12 @@
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
|
||||
"dev": true
|
||||
},
|
||||
"str2buf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/str2buf/-/str2buf-1.3.0.tgz",
|
||||
"integrity": "sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==",
|
||||
"dev": true
|
||||
},
|
||||
"stream-browserify": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
||||
@ -13134,6 +13352,36 @@
|
||||
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
|
||||
"dev": true
|
||||
},
|
||||
"typeson": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/typeson/-/typeson-5.13.0.tgz",
|
||||
"integrity": "sha512-xcSaSt+hY/VcRYcqZuVkJwMjDXXJb4CZd51qDocpYw8waA314ygyOPlKhsGsw4qKuJ0tfLLUrxccrm+xvyS0AQ==",
|
||||
"dev": true
|
||||
},
|
||||
"typeson-registry": {
|
||||
"version": "1.0.0-alpha.29",
|
||||
"resolved": "https://registry.npmjs.org/typeson-registry/-/typeson-registry-1.0.0-alpha.29.tgz",
|
||||
"integrity": "sha512-DqRoIx0CtmBGXuumFk7ex5QE6JCWHNKry6D1psGUUB9uIPRrj/SCtuRAidZjLgieWpwn1EfrTFtG0IN2t//F8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-arraybuffer-es6": "0.5.0",
|
||||
"typeson": "5.13.0",
|
||||
"whatwg-url": "7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"whatwg-url": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
|
||||
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"tr46": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.6.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz",
|
||||
@ -13638,6 +13886,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"webcrypto-core": {
|
||||
"version": "1.0.15",
|
||||
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.15.tgz",
|
||||
"integrity": "sha512-PA4VeKekgPxlmp18Opd4hrdOvtjsJCHyKpNfCyjLWEFIh/7M37QCFgCssx/MVBuNHBkzs9Q7W8Rm4BFgCKheUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pvtsutils": "^1.0.6",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"webcrypto-shim": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/webcrypto-shim/-/webcrypto-shim-0.1.5.tgz",
|
||||
"integrity": "sha512-mE+E00gulvbLjHaAwl0kph60oOLQRsKyivEFgV9DMM/3Y05F1vZvGq12hAcNzHRnYxyEOABBT/XMtwGSg5xA7A==",
|
||||
"dev": true
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
|
||||
@ -91,6 +91,8 @@
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-react": "^7.16.0",
|
||||
"fake-indexeddb": "^3.0.0",
|
||||
"isomorphic-webcrypto": "^2.3.2",
|
||||
"jest": "^24.9.0",
|
||||
"loose-envify": "^1.4.0",
|
||||
"node-sass": "^4.13.0",
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import _debug from 'debug'
|
||||
import socket from '../socket'
|
||||
import { Dispatch, ThunkResult } from '../store'
|
||||
import { callId } from '../window'
|
||||
import { ClientSocket } from '../socket'
|
||||
import * as NotifyActions from './NotifyActions'
|
||||
import * as SocketActions from './SocketActions'
|
||||
import { generateECDHKeyPair, exportKey } from '../crypto'
|
||||
import { getIdentity, Identity, saveIdentity } from '../db'
|
||||
import { ME } from '../constants'
|
||||
|
||||
const debug = _debug('peercalls')
|
||||
|
||||
export interface InitAction {
|
||||
type: 'INIT'
|
||||
@ -22,6 +28,11 @@ export const init = (): ThunkResult<Promise<void>> =>
|
||||
async (dispatch, getState) => {
|
||||
const socket = await dispatch(connect())
|
||||
|
||||
const keyPair = await dispatch(findOrCreateEncryptionKeys())
|
||||
const publicKey = keyPair && await exportKey(keyPair.publicKey)
|
||||
// TODO use publicKey
|
||||
publicKey
|
||||
|
||||
dispatch(SocketActions.handshake({
|
||||
socket,
|
||||
roomName: callId,
|
||||
@ -43,3 +54,45 @@ export const connect = () => (dispatch: Dispatch) => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const findOrCreateEncryptionKeys = () => async (
|
||||
dispatch: Dispatch,
|
||||
): Promise<CryptoKeyPair | undefined> => {
|
||||
|
||||
let identity: Identity | undefined
|
||||
try {
|
||||
identity = await getIdentity(ME)
|
||||
} catch (err) {
|
||||
dispatch(NotifyActions.error('This browser does not support IndexedDB'))
|
||||
debug('IndexedDB error: %s', err)
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (identity && identity.privateKey) {
|
||||
dispatch(NotifyActions.info('Using encryption keys from IndexedDB'))
|
||||
return {
|
||||
privateKey: identity.privateKey,
|
||||
publicKey: identity.publicKey,
|
||||
}
|
||||
}
|
||||
|
||||
let keyPair: CryptoKeyPair
|
||||
try {
|
||||
keyPair = await generateECDHKeyPair()
|
||||
} catch (err) {
|
||||
dispatch(NotifyActions.error('Unable to generate encryption keys'))
|
||||
debug('Unable to generate encryption keys: %s', err)
|
||||
return undefined
|
||||
}
|
||||
const { privateKey, publicKey } = keyPair
|
||||
|
||||
identity = {
|
||||
id: ME,
|
||||
privateKey,
|
||||
publicKey,
|
||||
}
|
||||
|
||||
await saveIdentity(identity)
|
||||
|
||||
return keyPair
|
||||
}
|
||||
|
||||
49
src/client/crypto/index.test.ts
Normal file
49
src/client/crypto/index.test.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { exportKey, encrypt, decrypt, generateECDHKeyPair, deriveECDHKey, hasWebCryptoAPI, importKey } from './index'
|
||||
|
||||
describe('crypto', () => {
|
||||
|
||||
let keypair1: CryptoKeyPair
|
||||
let keypair2: CryptoKeyPair
|
||||
|
||||
let derived1: CryptoKey
|
||||
let derived2: CryptoKey
|
||||
beforeAll(async () => {
|
||||
keypair1 = await generateECDHKeyPair()
|
||||
keypair2 = await generateECDHKeyPair()
|
||||
|
||||
derived1 = await deriveECDHKey({
|
||||
privateKey: keypair1.privateKey,
|
||||
publicKey: keypair2.publicKey,
|
||||
})
|
||||
derived2 = await deriveECDHKey({
|
||||
privateKey: keypair2.privateKey,
|
||||
publicKey: keypair1.publicKey,
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasWebCryptoAPI', () => {
|
||||
it('returns true when crypto.subtle api is available', () => {
|
||||
expect(hasWebCryptoAPI()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exportKey and importKey', () => {
|
||||
it('exports public key', async () => {
|
||||
const value = await exportKey(keypair1.publicKey)
|
||||
console.log(value)
|
||||
const key = await importKey(value)
|
||||
expect(key).toBeTruthy()
|
||||
expect(key).toEqual(keypair1.publicKey)
|
||||
})
|
||||
})
|
||||
|
||||
describe('encrypt and decrypt', () => {
|
||||
it('can be encrypted with one pair and decrypted with other', async () => {
|
||||
const message = 'test message'
|
||||
const encrypted = await encrypt(derived1, message)
|
||||
const decrypted = await decrypt(derived2, encrypted)
|
||||
expect(decrypted).toEqual(message)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
107
src/client/crypto/index.ts
Normal file
107
src/client/crypto/index.ts
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
export function hasWebCryptoAPI(): boolean {
|
||||
return !!(window && window.crypto && window.crypto.subtle)
|
||||
}
|
||||
|
||||
export function hasWebCryptoAPI2(): boolean {
|
||||
return !!(window?.crypto?.subtle)
|
||||
}
|
||||
|
||||
export async function generateECDHKeyPair() {
|
||||
const key = await window.crypto.subtle.generateKey(
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: 'P-256',
|
||||
},
|
||||
/* extractable */ true,
|
||||
['deriveKey'],
|
||||
)
|
||||
return key
|
||||
}
|
||||
|
||||
export async function deriveECDHKey(params: {
|
||||
privateKey: CryptoKey
|
||||
publicKey: CryptoKey
|
||||
}) {
|
||||
const { privateKey, publicKey } = params
|
||||
const derivedKey = await window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'ECDH',
|
||||
public: publicKey,
|
||||
},
|
||||
privateKey,
|
||||
{
|
||||
name: 'AES-CTR',
|
||||
length: 256,
|
||||
},
|
||||
/* extractable */ true,
|
||||
['encrypt', 'decrypt'],
|
||||
)
|
||||
|
||||
return derivedKey
|
||||
}
|
||||
|
||||
export async function encrypt(key: CryptoKey, data: string): Promise<string> {
|
||||
const encrypted = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: 'AES-CTR',
|
||||
counter: new Uint8Array(16),
|
||||
length: 128,
|
||||
},
|
||||
key,
|
||||
str2ab(data),
|
||||
)
|
||||
return ab2str(encrypted)
|
||||
}
|
||||
|
||||
export async function decrypt(key: CryptoKey, data: string): Promise<string> {
|
||||
const arrayBuffer = str2ab(data)
|
||||
const decrypted = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: 'AES-CTR',
|
||||
counter: new ArrayBuffer(16),
|
||||
length: 128,
|
||||
},
|
||||
key,
|
||||
arrayBuffer,
|
||||
)
|
||||
return ab2str(decrypted)
|
||||
}
|
||||
|
||||
export async function exportKey(key: CryptoKey) {
|
||||
return await window.crypto.subtle.exportKey('jwk', key)
|
||||
}
|
||||
|
||||
function ab2str(
|
||||
buf: ArrayBuffer,
|
||||
ArrayType: typeof Uint16Array | typeof Uint8Array = Uint16Array,
|
||||
): string {
|
||||
return String.fromCharCode.apply(
|
||||
null, new ArrayType(buf) as unknown as number[])
|
||||
}
|
||||
|
||||
export async function importKey(keyData: JsonWebKey) {
|
||||
if (keyData.kty !== 'EC' || keyData.crv !== 'P-256') {
|
||||
throw new Error(`Unsupported key type: ${keyData.kty}, crv: ${keyData.crv}`)
|
||||
}
|
||||
const key = await window.crypto.subtle.importKey(
|
||||
'jwk',
|
||||
keyData,
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: keyData.crv,
|
||||
},
|
||||
/* extractable */ true,
|
||||
keyData.key_ops || [],
|
||||
)
|
||||
return key
|
||||
}
|
||||
|
||||
function str2ab(str: string): ArrayBuffer {
|
||||
const buf = new ArrayBuffer(str.length*2) // 2 bytes for each char
|
||||
const bufView = new Uint16Array(buf)
|
||||
for (let i=0, strLen=str.length; i < strLen; i++) {
|
||||
bufView[i] = str.charCodeAt(i)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
34
src/client/db/identities.test.ts
Normal file
34
src/client/db/identities.test.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { getIdentity, saveIdentity } from './identities'
|
||||
import { promisify } from './promisify'
|
||||
import { generateECDHKeyPair } from '../crypto'
|
||||
|
||||
describe('identities', () => {
|
||||
|
||||
const TEST_DB = 'TEST_DB'
|
||||
|
||||
let keyPair: CryptoKeyPair
|
||||
beforeEach(async () => {
|
||||
keyPair = await generateECDHKeyPair()
|
||||
})
|
||||
afterEach(async () => {
|
||||
await promisify(window.indexedDB.deleteDatabase(TEST_DB))
|
||||
})
|
||||
|
||||
describe('getIdentity', () => {
|
||||
it('reads saved identity', async () => {
|
||||
const identity = {
|
||||
id: 'one',
|
||||
...keyPair,
|
||||
}
|
||||
await saveIdentity(identity)
|
||||
const identity2 = await getIdentity(identity.id)
|
||||
expect(identity2).not.toBe(identity)
|
||||
expect(identity2).toEqual(identity)
|
||||
})
|
||||
it('does not fail when not found', async () => {
|
||||
const identity = await getIdentity('notfound')
|
||||
expect(identity).toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
53
src/client/db/identities.ts
Normal file
53
src/client/db/identities.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { open } from './open'
|
||||
import { promisify } from './promisify'
|
||||
import { exportKey, importKey } from '../crypto'
|
||||
|
||||
export interface Identity {
|
||||
id: string
|
||||
publicKey: CryptoKey
|
||||
privateKey?: CryptoKey
|
||||
}
|
||||
|
||||
export interface StoredIdentity {
|
||||
id: string
|
||||
publicKey: JsonWebKey
|
||||
privateKey?: JsonWebKey
|
||||
}
|
||||
|
||||
const DB = 'PEERCALLS'
|
||||
|
||||
export async function getIdentity(id: string): Promise<Identity | undefined> {
|
||||
const db = await open(DB, 1)
|
||||
let stored: StoredIdentity | undefined
|
||||
try {
|
||||
const tx = db.transaction('identities', 'readonly')
|
||||
const store = tx.objectStore('identities')
|
||||
stored = await promisify(store.get(id))
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
|
||||
return stored && {
|
||||
id: stored.id,
|
||||
privateKey: stored.privateKey && await importKey(stored.privateKey),
|
||||
publicKey: await importKey(stored.publicKey),
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveIdentity(identity: Identity): Promise<void> {
|
||||
const stored = {
|
||||
id: identity.id,
|
||||
privateKey: identity.privateKey && await exportKey(identity.privateKey),
|
||||
publicKey: await exportKey(identity.publicKey),
|
||||
}
|
||||
|
||||
const db = await open(DB, 1)
|
||||
try {
|
||||
const tx = db.transaction('identities', 'readwrite')
|
||||
const store = tx.objectStore('identities')
|
||||
await promisify(store.put(stored))
|
||||
await promisify(tx)
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
46
src/client/db/index.test.ts
Normal file
46
src/client/db/index.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { open, promisify } from './index'
|
||||
|
||||
describe('db', () => {
|
||||
|
||||
const TEST_DB = 'TEST_DB'
|
||||
|
||||
// async function getError(promise: Promise<unknown>): Promise<Error> {
|
||||
// let error: Error
|
||||
// try {
|
||||
// await promise
|
||||
// } catch (err) {
|
||||
// error = err
|
||||
// }
|
||||
// expect(error!).toBeTruthy()
|
||||
// return error!
|
||||
// }
|
||||
|
||||
afterEach(async () => {
|
||||
db && db.close()
|
||||
await promisify(window.indexedDB.deleteDatabase(TEST_DB))
|
||||
})
|
||||
let db: IDBDatabase
|
||||
|
||||
describe('open', () => {
|
||||
|
||||
it('can use a custom upgrade function', async () => {
|
||||
let called = false
|
||||
db = await open(TEST_DB, 1, ev => {
|
||||
called = true
|
||||
})
|
||||
expect(called).toBe(true)
|
||||
})
|
||||
|
||||
it('opens a new database and upgrades it', async () => {
|
||||
db = await open(TEST_DB, 1)
|
||||
const tx = db.transaction('identities', 'readwrite')
|
||||
const store = tx.objectStore('identities')
|
||||
await promisify(store.put({id: 'test'}))
|
||||
const value = await promisify(store.get('test'))
|
||||
expect(value).toEqual({id: 'test'})
|
||||
await promisify(tx)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
3
src/client/db/index.ts
Normal file
3
src/client/db/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { promisify } from './promisify'
|
||||
export { open } from './open'
|
||||
export * from './identities'
|
||||
12
src/client/db/open.ts
Normal file
12
src/client/db/open.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { promisify } from './promisify'
|
||||
import { Upgrade, upgrade } from './upgrade'
|
||||
|
||||
export async function open(
|
||||
name: string,
|
||||
version: number,
|
||||
doUpgrade: Upgrade = upgrade,
|
||||
) {
|
||||
const request = window.indexedDB.open(name, version)
|
||||
request.onupgradeneeded = doUpgrade
|
||||
return promisify(request)
|
||||
}
|
||||
16
src/client/db/promisify.ts
Normal file
16
src/client/db/promisify.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export async function promisify(request: IDBTransaction): Promise<void>
|
||||
export async function promisify<T>(request: IDBRequest<T>): Promise<T>
|
||||
export async function promisify<T>(request: IDBRequest<T> | IDBTransaction) {
|
||||
if ('oncomplete' in request) {
|
||||
// this is a transaction
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
request.oncomplete = () => resolve()
|
||||
request.onerror = err => reject(err)
|
||||
})
|
||||
}
|
||||
// this is an IDBRequest
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
request.onsuccess = () => resolve(request.result)
|
||||
request.onerror = err => reject(err)
|
||||
})
|
||||
}
|
||||
12
src/client/db/upgrade.ts
Normal file
12
src/client/db/upgrade.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export type Upgrade =
|
||||
(this: IDBOpenDBRequest, event: IDBVersionChangeEvent) => void
|
||||
|
||||
export function upgrade(this: IDBOpenDBRequest, event: IDBVersionChangeEvent) {
|
||||
const db = this.result
|
||||
switch (event.oldVersion) {
|
||||
case 0:
|
||||
db.createObjectStore('identities', {
|
||||
keyPath: 'id',
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user