mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-06-15 09:51:21 +00:00
started to add symmetric key encryption
This commit is contained in:
parent
770080b942
commit
418c2c79a9
158
package-lock.json
generated
158
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "qapp-core",
|
"name": "qapp-core",
|
||||||
"version": "1.0.7",
|
"version": "1.0.9",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "qapp-core",
|
"name": "qapp-core",
|
||||||
"version": "1.0.7",
|
"version": "1.0.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
@ -15,8 +15,11 @@
|
|||||||
"@mui/material": "^6.4.7",
|
"@mui/material": "^6.4.7",
|
||||||
"@tanstack/react-virtual": "^3.13.2",
|
"@tanstack/react-virtual": "^3.13.2",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.0.10",
|
||||||
|
"bloom-filters": "^3.0.4",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
|
"compressorjs": "^1.2.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-dropzone": "^14.3.8",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.16.0",
|
||||||
"short-unique-id": "^5.2.0",
|
"short-unique-id": "^5.2.0",
|
||||||
"zustand": "^4.3.2"
|
"zustand": "^4.3.2"
|
||||||
@ -1295,6 +1298,11 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/seedrandom": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ=="
|
||||||
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||||
@ -1325,6 +1333,14 @@
|
|||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/attr-accept": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
|
||||||
|
"integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-plugin-macros": {
|
"node_modules/babel-plugin-macros": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||||
@ -1345,6 +1361,14 @@
|
|||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@ -1364,6 +1388,29 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/bloom-filters": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-BdnPWo2OpYhlvuP2fRzJBdioMCkm7Zp0HCf8NJgF5Mbyqy7VQ/CnTiVWMMyq4EZCBHwj0Kq6098gW2/3RsZsrA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/seedrandom": "^3.0.8",
|
||||||
|
"base64-arraybuffer": "^1.0.2",
|
||||||
|
"is-buffer": "^2.0.5",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"long": "^5.2.0",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"seedrandom": "^3.0.5",
|
||||||
|
"xxhashjs": "^0.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blueimp-canvas-to-blob": {
|
||||||
|
"version": "3.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
|
||||||
|
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg=="
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
@ -1478,6 +1525,15 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/compressorjs": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"blueimp-canvas-to-blob": "^3.29.0",
|
||||||
|
"is-blob": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/consola": {
|
"node_modules/consola": {
|
||||||
"version": "3.4.1",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/consola/-/consola-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/consola/-/consola-3.4.1.tgz",
|
||||||
@ -1534,6 +1590,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/cuint": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
@ -1644,6 +1705,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/file-selector": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/find-root": {
|
"node_modules/find-root": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||||
@ -1786,6 +1858,39 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/is-blob": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-buffer": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.16.1",
|
"version": "2.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||||
@ -1886,12 +1991,22 @@
|
|||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
"node_modules/lodash.sortby": {
|
"node_modules/lodash.sortby": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||||
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
|
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/long": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng=="
|
||||||
|
},
|
||||||
"node_modules/loose-envify": {
|
"node_modules/loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
@ -2141,6 +2256,22 @@
|
|||||||
"react": "^19.0.0"
|
"react": "^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-dropzone": {
|
||||||
|
"version": "14.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz",
|
||||||
|
"integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==",
|
||||||
|
"dependencies": {
|
||||||
|
"attr-accept": "^2.2.4",
|
||||||
|
"file-selector": "^2.1.0",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.8 || 18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-intersection-observer": {
|
"node_modules/react-intersection-observer": {
|
||||||
"version": "9.16.0",
|
"version": "9.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz",
|
||||||
@ -2188,6 +2319,11 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reflect-metadata": {
|
||||||
|
"version": "0.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
|
||||||
|
"integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="
|
||||||
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
@ -2265,6 +2401,11 @@
|
|||||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/seedrandom": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -2520,6 +2661,11 @@
|
|||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
|
},
|
||||||
"node_modules/tsup": {
|
"node_modules/tsup": {
|
||||||
"version": "8.4.0",
|
"version": "8.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsup/-/tsup-8.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsup/-/tsup-8.4.0.tgz",
|
||||||
@ -2715,6 +2861,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xxhashjs": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
|
||||||
|
"dependencies": {
|
||||||
|
"cuint": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zustand": {
|
"node_modules/zustand": {
|
||||||
"version": "4.5.6",
|
"version": "4.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||||
|
@ -26,8 +26,11 @@
|
|||||||
"@mui/material": "^6.4.7",
|
"@mui/material": "^6.4.7",
|
||||||
"@tanstack/react-virtual": "^3.13.2",
|
"@tanstack/react-virtual": "^3.13.2",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.0.10",
|
||||||
|
"bloom-filters": "^3.0.4",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
|
"compressorjs": "^1.2.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
"react-dropzone": "^14.3.8",
|
||||||
"react-intersection-observer": "^9.16.0",
|
"react-intersection-observer": "^9.16.0",
|
||||||
"short-unique-id": "^5.2.0",
|
"short-unique-id": "^5.2.0",
|
||||||
"zustand": "^4.3.2"
|
"zustand": "^4.3.2"
|
||||||
|
109
src/common/ImagePicker.tsx
Normal file
109
src/common/ImagePicker.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
import {
|
||||||
|
useDropzone,
|
||||||
|
DropzoneRootProps,
|
||||||
|
DropzoneInputProps,
|
||||||
|
} from "react-dropzone";
|
||||||
|
import Compressor from "compressorjs";
|
||||||
|
import { fileToBase64 } from "../utils/base64";
|
||||||
|
|
||||||
|
type Mode = "single" | "multi";
|
||||||
|
|
||||||
|
interface CommonProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
mode?: Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SingleModeProps extends CommonProps {
|
||||||
|
mode?: "single";
|
||||||
|
onPick: (base64: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultiModeProps extends CommonProps {
|
||||||
|
mode: "multi";
|
||||||
|
onPick: (base64s: string[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageUploaderProps = SingleModeProps | MultiModeProps;
|
||||||
|
|
||||||
|
export const ImagePicker: React.FC<ImageUploaderProps> = ({
|
||||||
|
children,
|
||||||
|
onPick,
|
||||||
|
mode = "single",
|
||||||
|
}) => {
|
||||||
|
const onDrop = useCallback(
|
||||||
|
async (acceptedFiles: File[]) => {
|
||||||
|
const images =
|
||||||
|
mode === "single" ? acceptedFiles.slice(0, 1) : acceptedFiles;
|
||||||
|
|
||||||
|
const base64s: string[] = [];
|
||||||
|
|
||||||
|
for (const image of images) {
|
||||||
|
try {
|
||||||
|
let fileToConvert: File;
|
||||||
|
|
||||||
|
if (image.type === "image/gif") {
|
||||||
|
if (image.size > 500 * 1024) {
|
||||||
|
console.error("GIF file size exceeds 500KB limit.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fileToConvert = image;
|
||||||
|
} else {
|
||||||
|
fileToConvert = await new Promise<File>((resolve, reject) => {
|
||||||
|
new Compressor(image, {
|
||||||
|
quality: 0.6,
|
||||||
|
maxWidth: 1200,
|
||||||
|
mimeType: "image/webp",
|
||||||
|
success(result) {
|
||||||
|
resolve(
|
||||||
|
new File([result], image.name, { type: "image/webp" })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
console.error("Compression error:", err);
|
||||||
|
reject(err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}).catch(() => image); // fallback to original if compression fails
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64 = await fileToBase64(fileToConvert);
|
||||||
|
base64s.push(base64);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("File processing error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "single") {
|
||||||
|
if (base64s[0]) {
|
||||||
|
(onPick as (base64: string) => void)(base64s[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(onPick as (base64s: string[]) => void)(base64s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onPick, mode]
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getRootProps,
|
||||||
|
getInputProps,
|
||||||
|
}: {
|
||||||
|
getRootProps: () => DropzoneRootProps;
|
||||||
|
getInputProps: () => DropzoneInputProps;
|
||||||
|
isDragActive: boolean;
|
||||||
|
} = useDropzone({
|
||||||
|
onDrop,
|
||||||
|
accept: {
|
||||||
|
"image/*": [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box {...getRootProps()} sx={{ display: "flex" }}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -2,7 +2,7 @@ import React, { createContext, useContext, useMemo } from "react";
|
|||||||
import { useAuth, UseAuthProps } from "../hooks/useAuth";
|
import { useAuth, UseAuthProps } from "../hooks/useAuth";
|
||||||
import { useResources } from "../hooks/useResources";
|
import { useResources } from "../hooks/useResources";
|
||||||
import { useAppInfo } from "../hooks/useAppInfo";
|
import { useAppInfo } from "../hooks/useAppInfo";
|
||||||
import { IdentifierBuilder } from "../utils/encryption";
|
import { addAndEncryptSymmetricKeys, IdentifierBuilder } from "../utils/encryption";
|
||||||
import { useIdentifiers } from "../hooks/useIdentifiers";
|
import { useIdentifiers } from "../hooks/useIdentifiers";
|
||||||
import { objectToBase64 } from "../utils/base64";
|
import { objectToBase64 } from "../utils/base64";
|
||||||
import { base64ToObject } from "../utils/publish";
|
import { base64ToObject } from "../utils/publish";
|
||||||
@ -10,7 +10,8 @@ import { base64ToObject } from "../utils/publish";
|
|||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
objectToBase64,
|
objectToBase64,
|
||||||
base64ToObject
|
base64ToObject,
|
||||||
|
addAndEncryptSymmetricKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
2424
src/deps/nacl-fast.ts
Normal file
2424
src/deps/nacl-fast.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ export const usePublish = (
|
|||||||
const hasFetched = useRef(false);
|
const hasFetched = useRef(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<null | string>(null);
|
const [error, setError] = useState<null | string>(null);
|
||||||
const publish = usePublishStore().getPublish(metadata || null);
|
const publish = usePublishStore().getPublish(metadata || null, true);
|
||||||
const setPublish = usePublishStore().setPublish;
|
const setPublish = usePublishStore().setPublish;
|
||||||
const getPublish = usePublishStore().getPublish;
|
const getPublish = usePublishStore().getPublish;
|
||||||
|
|
||||||
|
@ -3,3 +3,4 @@ export { GlobalProvider, useGlobal } from "./context/GlobalProvider";
|
|||||||
export {usePublish} from "./hooks/usePublish"
|
export {usePublish} from "./hooks/usePublish"
|
||||||
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
export {ResourceListDisplay} from "./components/ResourceList/ResourceListDisplay"
|
||||||
export {QortalSearchParams} from './types/interfaces/resources'
|
export {QortalSearchParams} from './types/interfaces/resources'
|
||||||
|
export {ImagePicker} from './common/ImagePicker'
|
||||||
|
@ -31,7 +31,7 @@ class Semaphore {
|
|||||||
|
|
||||||
const semaphore = new Semaphore(1)
|
const semaphore = new Semaphore(1)
|
||||||
|
|
||||||
export const fileToBase64 = (file : File): Promise<string> => new Promise((resolve, reject) => {
|
export const fileToBase64 = (file : File | Blob): Promise<string> => new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader(); // Create a new instance
|
const reader = new FileReader(); // Create a new instance
|
||||||
semaphore.acquire();
|
semaphore.acquire();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
48
src/utils/bloomFilter.ts
Normal file
48
src/utils/bloomFilter.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { BloomFilter } from 'bloom-filters';
|
||||||
|
import { base64ToObject } from './base64';
|
||||||
|
|
||||||
|
|
||||||
|
export async function hashPublicKey(publicKeyString: string) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(publicKeyString);
|
||||||
|
|
||||||
|
const digest = await crypto.subtle.digest("SHA-256", data);
|
||||||
|
const hashBytes = new Uint8Array(digest);
|
||||||
|
return Array.from(hashBytes)
|
||||||
|
.map((b) => b.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function generateBloomFilterBase64(publicKeys: string[]) {
|
||||||
|
if (publicKeys.length > 100) throw new Error("Max 100 users allowed");
|
||||||
|
|
||||||
|
const bloom = BloomFilter.create(100, 0.0004); // ~0.04% FPR
|
||||||
|
|
||||||
|
for (const pk of publicKeys) {
|
||||||
|
const hash = await hashPublicKey(pk);
|
||||||
|
bloom.add(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize to compact form
|
||||||
|
const byteArray = new Uint8Array(bloom.saveAsJSON()._data);
|
||||||
|
const base64 = btoa(String.fromCharCode(...byteArray));
|
||||||
|
|
||||||
|
if (byteArray.length > 230) {
|
||||||
|
throw new Error(`Bloom filter exceeds 230 bytes: ${byteArray.length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function userCanProbablyDecrypt(base64Bloom: string, userPublicKey: string) {
|
||||||
|
const base64ToJson = base64ToObject(base64Bloom)
|
||||||
|
|
||||||
|
const bloom = BloomFilter.fromJSON(base64ToJson)
|
||||||
|
|
||||||
|
const hash = await hashPublicKey(userPublicKey);
|
||||||
|
return bloom.has(hash);
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,25 @@
|
|||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
|
import { objectToBase64, uint8ArrayToBase64 } from "./base64";
|
||||||
|
import { RequestQueueWithPromise } from "./queue";
|
||||||
|
import { base64ToUint8Array } from "./publish";
|
||||||
|
import nacl from "../deps/nacl-fast";
|
||||||
|
|
||||||
|
export const requestQueueGetPublicKeys = new RequestQueueWithPromise(10);
|
||||||
|
|
||||||
export enum EnumCollisionStrength {
|
export enum EnumCollisionStrength {
|
||||||
LOW = 8,
|
LOW = 8,
|
||||||
MEDIUM = 11,
|
MEDIUM = 11,
|
||||||
HIGH = 14,
|
HIGH = 14,
|
||||||
PARENT_REF = 14,
|
PARENT_REF = 14,
|
||||||
ENTITY_LABEL= 6
|
ENTITY_LABEL = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function hashWord(
|
||||||
export async function hashWord(word: string, collisionStrength: number, publicSalt: string) {
|
word: string,
|
||||||
|
collisionStrength: number,
|
||||||
|
publicSalt: string
|
||||||
|
) {
|
||||||
const saltedWord = publicSalt + word; // Use public salt directly
|
const saltedWord = publicSalt + word; // Use public salt directly
|
||||||
const encoded = new TextEncoder().encode(saltedWord);
|
const encoded = new TextEncoder().encode(saltedWord);
|
||||||
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
||||||
@ -24,8 +33,6 @@ export async function hashWord(word: string, collisionStrength: number, publicSa
|
|||||||
.slice(0, collisionStrength);
|
.slice(0, collisionStrength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 10, dictionary: "alphanum" });
|
const uid = new ShortUniqueId({ length: 10, dictionary: "alphanum" });
|
||||||
|
|
||||||
interface EntityConfig {
|
interface EntityConfig {
|
||||||
@ -39,10 +46,12 @@ export type IdentifierBuilder = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Recursive function to traverse identifierBuilder
|
// Recursive function to traverse identifierBuilder
|
||||||
function findEntityConfig(identifierBuilder: IdentifierBuilder, path: string[]): EntityConfig {
|
function findEntityConfig(
|
||||||
|
identifierBuilder: IdentifierBuilder,
|
||||||
|
path: string[]
|
||||||
|
): EntityConfig {
|
||||||
let current: EntityConfig | undefined = { children: identifierBuilder }; // ✅ Wrap it inside `{ children }` so it behaves like other levels
|
let current: EntityConfig | undefined = { children: identifierBuilder }; // ✅ Wrap it inside `{ children }` so it behaves like other levels
|
||||||
|
|
||||||
|
|
||||||
for (const key of path) {
|
for (const key of path) {
|
||||||
if (!current.children || !current.children[key]) {
|
if (!current.children || !current.children[key]) {
|
||||||
throw new Error(`Entity '${key}' is not defined in identifierBuilder`);
|
throw new Error(`Entity '${key}' is not defined in identifierBuilder`);
|
||||||
@ -53,7 +62,6 @@ function findEntityConfig(identifierBuilder: IdentifierBuilder, path: string[]):
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Function to generate a prefix for searching
|
// Function to generate a prefix for searching
|
||||||
export async function buildSearchPrefix(
|
export async function buildSearchPrefix(
|
||||||
appName: string,
|
appName: string,
|
||||||
@ -63,10 +71,18 @@ export async function buildSearchPrefix(
|
|||||||
identifierBuilder: IdentifierBuilder
|
identifierBuilder: IdentifierBuilder
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// Hash app name (11 chars)
|
// Hash app name (11 chars)
|
||||||
const appHash: string = await hashWord(appName, EnumCollisionStrength.HIGH, publicSalt);
|
const appHash: string = await hashWord(
|
||||||
|
appName,
|
||||||
|
EnumCollisionStrength.HIGH,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
|
|
||||||
// Hash entity type (4 chars)
|
// Hash entity type (4 chars)
|
||||||
const entityPrefix: string = await hashWord(entityType, EnumCollisionStrength.ENTITY_LABEL, publicSalt);
|
const entityPrefix: string = await hashWord(
|
||||||
|
entityType,
|
||||||
|
EnumCollisionStrength.ENTITY_LABEL,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
|
|
||||||
// ✅ Detect if this entity is actually a root entity
|
// ✅ Detect if this entity is actually a root entity
|
||||||
const isRootEntity = !!identifierBuilder[entityType];
|
const isRootEntity = !!identifierBuilder[entityType];
|
||||||
@ -76,7 +92,11 @@ export async function buildSearchPrefix(
|
|||||||
if (isRootEntity && parentId === null) {
|
if (isRootEntity && parentId === null) {
|
||||||
parentRef = "00000000000000"; // ✅ Only for true root entities
|
parentRef = "00000000000000"; // ✅ Only for true root entities
|
||||||
} else if (parentId) {
|
} else if (parentId) {
|
||||||
parentRef = await hashWord(parentId, EnumCollisionStrength.PARENT_REF, publicSalt);
|
parentRef = await hashWord(
|
||||||
|
parentId,
|
||||||
|
EnumCollisionStrength.PARENT_REF,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ If there's no parentRef, return without it
|
// ✅ If there's no parentRef, return without it
|
||||||
@ -85,8 +105,6 @@ export async function buildSearchPrefix(
|
|||||||
: `${appHash}-${entityPrefix}-`; // ✅ Global search for entity type
|
: `${appHash}-${entityPrefix}-`; // ✅ Global search for entity type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to generate IDs dynamically with `publicSalt`
|
// Function to generate IDs dynamically with `publicSalt`
|
||||||
export async function buildIdentifier(
|
export async function buildIdentifier(
|
||||||
appName: string,
|
appName: string,
|
||||||
@ -95,13 +113,19 @@ export async function buildIdentifier(
|
|||||||
parentId: string | null,
|
parentId: string | null,
|
||||||
identifierBuilder: IdentifierBuilder
|
identifierBuilder: IdentifierBuilder
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
|
||||||
|
|
||||||
// Hash app name (11 chars)
|
// Hash app name (11 chars)
|
||||||
const appHash: string = await hashWord(appName, EnumCollisionStrength.HIGH, publicSalt);
|
const appHash: string = await hashWord(
|
||||||
|
appName,
|
||||||
|
EnumCollisionStrength.HIGH,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
|
|
||||||
// Hash entity type (4 chars)
|
// Hash entity type (4 chars)
|
||||||
const entityPrefix: string = await hashWord(entityType, EnumCollisionStrength.ENTITY_LABEL, publicSalt);
|
const entityPrefix: string = await hashWord(
|
||||||
|
entityType,
|
||||||
|
EnumCollisionStrength.ENTITY_LABEL,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
|
|
||||||
// Generate a unique identifier for this entity
|
// Generate a unique identifier for this entity
|
||||||
const entityUid = uid.rnd();
|
const entityUid = uid.rnd();
|
||||||
@ -109,8 +133,251 @@ export async function buildIdentifier(
|
|||||||
// Determine parent reference
|
// Determine parent reference
|
||||||
let parentRef = "00000000000000"; // Default for feeds
|
let parentRef = "00000000000000"; // Default for feeds
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
parentRef = await hashWord(parentId, EnumCollisionStrength.PARENT_REF, publicSalt);
|
parentRef = await hashWord(
|
||||||
|
parentId,
|
||||||
|
EnumCollisionStrength.PARENT_REF,
|
||||||
|
publicSalt
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${appHash}-${entityPrefix}-${parentRef}-${entityUid}`;
|
return `${appHash}-${entityPrefix}-${parentRef}-${entityUid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createSymmetricKeyAndNonce = () => {
|
||||||
|
const messageKey = new Uint8Array(32); // 32 bytes for the symmetric key
|
||||||
|
crypto.getRandomValues(messageKey);
|
||||||
|
|
||||||
|
return { messageKey: uint8ArrayToBase64(messageKey) };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPublicKeysByNames = async (names: string[]) => {
|
||||||
|
// Use the request queue for fetching public keys
|
||||||
|
const memberPromises = names.map((name) =>
|
||||||
|
requestQueueGetPublicKeys.enqueue(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/names/${name}`);
|
||||||
|
const nameInfo = await response.json();
|
||||||
|
const resAddress = await fetch(`/addresses/${nameInfo}`);
|
||||||
|
const resData = await resAddress.json();
|
||||||
|
return resData.publicKey;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const members = await Promise.all(memberPromises);
|
||||||
|
return members?.filter((item: string | null) => !!item);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addAndEncryptSymmetricKeys = async ({
|
||||||
|
previousData,
|
||||||
|
names,
|
||||||
|
}: {
|
||||||
|
previousData: Object;
|
||||||
|
names: string[];
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
let highestKey = 0;
|
||||||
|
if (previousData) {
|
||||||
|
highestKey = Math.max(
|
||||||
|
...Object.keys(previousData || {})
|
||||||
|
.filter((item) => !isNaN(+item))
|
||||||
|
.map(Number)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupmemberPublicKeys = await getPublicKeysByNames(names);
|
||||||
|
const symmetricKey = createSymmetricKeyAndNonce();
|
||||||
|
const nextNumber = highestKey + 1;
|
||||||
|
const objectToSave = {
|
||||||
|
...previousData,
|
||||||
|
[nextNumber]: symmetricKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave);
|
||||||
|
const encryptedData = await qortalRequest({
|
||||||
|
action: "ENCRYPT_DATA",
|
||||||
|
base64: symmetricKeyAndNonceBase64,
|
||||||
|
publicKeys: groupmemberPublicKeys,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (encryptedData) {
|
||||||
|
return encryptedData;
|
||||||
|
} else {
|
||||||
|
throw new Error("Cannot encrypt content");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const encryptWithSymmetricKeys = async ({
|
||||||
|
data64,
|
||||||
|
secretKeyObject,
|
||||||
|
typeNumber = 2,
|
||||||
|
}: any) => {
|
||||||
|
// Find the highest key in the secretKeyObject
|
||||||
|
const highestKey = Math.max(
|
||||||
|
...Object.keys(secretKeyObject)
|
||||||
|
.filter((item) => !isNaN(+item))
|
||||||
|
.map(Number)
|
||||||
|
);
|
||||||
|
const highestKeyObject = secretKeyObject[highestKey];
|
||||||
|
|
||||||
|
// Convert data and keys from base64
|
||||||
|
const Uint8ArrayData = base64ToUint8Array(data64);
|
||||||
|
const messageKey = base64ToUint8Array(highestKeyObject.messageKey);
|
||||||
|
|
||||||
|
if (!(Uint8ArrayData instanceof Uint8Array)) {
|
||||||
|
throw new Error("The Uint8ArrayData you've submitted is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonce, encryptedData, encryptedDataBase64, finalEncryptedData;
|
||||||
|
|
||||||
|
// Convert type number to a fixed length of 3 digits
|
||||||
|
const typeNumberStr = typeNumber.toString().padStart(3, "0");
|
||||||
|
|
||||||
|
if (highestKeyObject.nonce) {
|
||||||
|
// Old format: Use the nonce from secretKeyObject
|
||||||
|
nonce = base64ToUint8Array(highestKeyObject.nonce);
|
||||||
|
|
||||||
|
// Encrypt the data with the existing nonce and message key
|
||||||
|
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
||||||
|
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
||||||
|
|
||||||
|
// Concatenate the highest key, type number, and encrypted data (old format)
|
||||||
|
const highestKeyStr = highestKey.toString().padStart(10, "0"); // Fixed length of 10 digits
|
||||||
|
finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64);
|
||||||
|
} else {
|
||||||
|
// New format: Generate a random nonce and embed it in the message
|
||||||
|
nonce = new Uint8Array(24); // 24 bytes for the nonce
|
||||||
|
crypto.getRandomValues(nonce);
|
||||||
|
|
||||||
|
// Encrypt the data with the new nonce and message key
|
||||||
|
encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey);
|
||||||
|
encryptedDataBase64 = uint8ArrayToBase64(encryptedData);
|
||||||
|
|
||||||
|
// Convert the nonce to base64
|
||||||
|
const nonceBase64 = uint8ArrayToBase64(nonce);
|
||||||
|
|
||||||
|
// Concatenate the highest key, type number, nonce, and encrypted data (new format)
|
||||||
|
const highestKeyStr = highestKey.toString().padStart(10, "0"); // Fixed length of 10 digits
|
||||||
|
|
||||||
|
const highestKeyBytes = new TextEncoder().encode(
|
||||||
|
highestKeyStr.padStart(10, "0")
|
||||||
|
);
|
||||||
|
const typeNumberBytes = new TextEncoder().encode(
|
||||||
|
typeNumberStr.padStart(3, "0")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 3: Concatenate all binary
|
||||||
|
const combinedBinary = new Uint8Array(
|
||||||
|
highestKeyBytes.length +
|
||||||
|
typeNumberBytes.length +
|
||||||
|
nonce.length +
|
||||||
|
encryptedData.length
|
||||||
|
);
|
||||||
|
// finalEncryptedData = btoa(highestKeyStr) + btoa(typeNumberStr) + nonceBase64 + encryptedDataBase64;
|
||||||
|
combinedBinary.set(highestKeyBytes, 0);
|
||||||
|
combinedBinary.set(typeNumberBytes, highestKeyBytes.length);
|
||||||
|
combinedBinary.set(nonce, highestKeyBytes.length + typeNumberBytes.length);
|
||||||
|
combinedBinary.set(
|
||||||
|
encryptedData,
|
||||||
|
highestKeyBytes.length + typeNumberBytes.length + nonce.length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 4: Base64 encode once
|
||||||
|
finalEncryptedData = uint8ArrayToBase64(combinedBinary);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalEncryptedData;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface SecretKeyValue {
|
||||||
|
messageKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decryptSingle = async ({
|
||||||
|
base64,
|
||||||
|
secretKeyObject,
|
||||||
|
}: {
|
||||||
|
base64: string;
|
||||||
|
secretKeyObject: Record<number, SecretKeyValue>;
|
||||||
|
}) => {
|
||||||
|
// First, decode the base64-encoded input (if skipDecodeBase64 is not set)
|
||||||
|
const decodedData = base64;
|
||||||
|
|
||||||
|
// Then, decode it again for the specific format (if double encoding is used)
|
||||||
|
const decodeForNumber = atob(decodedData);
|
||||||
|
|
||||||
|
// Extract the key (assuming it's always the first 10 characters)
|
||||||
|
const keyStr = decodeForNumber.slice(0, 10);
|
||||||
|
|
||||||
|
// Convert the key string back to a number
|
||||||
|
const highestKey = parseInt(keyStr, 10);
|
||||||
|
|
||||||
|
// Check if we have a valid secret key for the extracted highestKey
|
||||||
|
if (!secretKeyObject[highestKey]) {
|
||||||
|
throw new Error("Cannot find correct secretKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretKeyEntry = secretKeyObject[highestKey];
|
||||||
|
|
||||||
|
let typeNumberStr, nonceBase64, encryptedDataBase64;
|
||||||
|
|
||||||
|
// Determine if typeNumber exists by checking if the next 3 characters after keyStr are digits
|
||||||
|
const possibleTypeNumberStr = decodeForNumber.slice(10, 13);
|
||||||
|
|
||||||
|
// const typeNumberStr = new TextDecoder().decode(typeNumberBytes);
|
||||||
|
if (decodeForNumber.slice(10, 13) !== "001") {
|
||||||
|
const decodedBinary = base64ToUint8Array(decodedData);
|
||||||
|
const highestKeyBytes = decodedBinary.slice(0, 10); // if ASCII digits only
|
||||||
|
const highestKeyStr = new TextDecoder().decode(highestKeyBytes);
|
||||||
|
|
||||||
|
const nonce = decodedBinary.slice(13, 13 + 24);
|
||||||
|
const encryptedData = decodedBinary.slice(13 + 24);
|
||||||
|
const highestKey = parseInt(highestKeyStr, 10);
|
||||||
|
|
||||||
|
const messageKey = base64ToUint8Array(
|
||||||
|
secretKeyObject[+highestKey].messageKey
|
||||||
|
);
|
||||||
|
const decryptedBytes = nacl.secretbox.open(
|
||||||
|
encryptedData,
|
||||||
|
nonce,
|
||||||
|
messageKey
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if decryption was successful
|
||||||
|
if (!decryptedBytes) {
|
||||||
|
throw new Error("Decryption failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the decrypted Uint8Array back to a Base64 string
|
||||||
|
return uint8ArrayToBase64(decryptedBytes);
|
||||||
|
}
|
||||||
|
// New format: Extract type number and nonce
|
||||||
|
typeNumberStr = possibleTypeNumberStr; // Extract type number
|
||||||
|
nonceBase64 = decodeForNumber.slice(13, 45); // Extract nonce (next 32 characters after type number)
|
||||||
|
encryptedDataBase64 = decodeForNumber.slice(45); // The remaining part is the encrypted data
|
||||||
|
|
||||||
|
// Convert Base64 strings to Uint8Array
|
||||||
|
const Uint8ArrayData = base64ToUint8Array(encryptedDataBase64);
|
||||||
|
const nonce = base64ToUint8Array(nonceBase64);
|
||||||
|
const messageKey = base64ToUint8Array(secretKeyEntry.messageKey);
|
||||||
|
|
||||||
|
if (!(Uint8ArrayData instanceof Uint8Array)) {
|
||||||
|
throw new Error("The Uint8ArrayData you've submitted is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the data using the nonce and messageKey
|
||||||
|
const decryptedData = nacl.secretbox.open(Uint8ArrayData, nonce, messageKey);
|
||||||
|
|
||||||
|
// Check if decryption was successful
|
||||||
|
if (!decryptedData) {
|
||||||
|
throw new Error("Decryption failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the decrypted Uint8Array back to a Base64 string
|
||||||
|
return uint8ArrayToBase64(decryptedData);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user