diff --git a/bun.lock b/bun.lock index cd68c69..f277329 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,11 @@ "": { "name": "seedpgp-web", "dependencies": { + "@types/bip32": "^2.0.4", + "@types/bip39": "^3.0.4", "@types/pako": "^2.0.4", + "bip32": "^5.0.0", + "buffer": "^6.0.3", "html5-qrcode": "^2.3.8", "jsqr": "^1.4.0", "lucide-react": "^0.462.0", @@ -14,10 +18,11 @@ "qrcode": "^1.5.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "tiny-secp256k1": "^2.2.4", }, "devDependencies": { "@types/bun": "^1.3.6", - "@types/node": "^22.10.2", + "@types/node": "^25.2.1", "@types/qrcode": "^1.5.5", "@types/qrcode-generator": "^1.0.6", "@types/react": "^18.3.12", @@ -28,6 +33,8 @@ "tailwindcss": "^3.4.17", "typescript": "^5.6.2", "vite": "^6.0.3", + "vite-plugin-top-level-await": "^1.6.0", + "vite-plugin-wasm": "^3.5.0", }, }, }, @@ -134,6 +141,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -142,6 +151,8 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + "@rollup/plugin-virtual": ["@rollup/plugin-virtual@3.0.2", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.0", "", { "os": "android", "cpu": "arm" }, "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.0", "", { "os": "android", "cpu": "arm64" }, "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg=="], @@ -192,6 +203,36 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ=="], + "@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], + + "@swc/core": ["@swc/core@1.15.11", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.11", "@swc/core-darwin-x64": "1.15.11", "@swc/core-linux-arm-gnueabihf": "1.15.11", "@swc/core-linux-arm64-gnu": "1.15.11", "@swc/core-linux-arm64-musl": "1.15.11", "@swc/core-linux-x64-gnu": "1.15.11", "@swc/core-linux-x64-musl": "1.15.11", "@swc/core-win32-arm64-msvc": "1.15.11", "@swc/core-win32-ia32-msvc": "1.15.11", "@swc/core-win32-x64-msvc": "1.15.11" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.11", "", { "os": "linux", "cpu": "arm" }, "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.11", "", { "os": "linux", "cpu": "x64" }, "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.11", "", { "os": "linux", "cpu": "x64" }, "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.11", "", { "os": "win32", "cpu": "x64" }, "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/types": ["@swc/types@0.1.25", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g=="], + + "@swc/wasm": ["@swc/wasm@1.15.11", "", {}, "sha512-230rdYZf8ux3nIwISOQNCFrxzxpL/UFY4Khv/3UsvpEdo709j/+Tg80yXWW3DXETeZNPBV72QpvEBhXsl7Lc9g=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], @@ -200,11 +241,15 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/bip32": ["@types/bip32@2.0.4", "", { "dependencies": { "bip32": "*" } }, "sha512-5VE8jtDlFx94IyopdhtkcZ/6oCSvpLS1yOcwkUgi9/zwL9LG99q4+nYv6N/HntPGqB9wcE6osxrtmErt75sjxA=="], + + "@types/bip39": ["@types/bip39@3.0.4", "", { "dependencies": { "bip39": "*" } }, "sha512-kgmgxd14vTUMqcKu/gRi7adMchm7teKnOzdkeP0oQ5QovXpbUJISU0KUtBt84DdxCws/YuNlSCIoZqgXexe6KQ=="], + "@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "@types/node": ["@types/node@25.2.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg=="], "@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="], @@ -232,14 +277,28 @@ "autoprefixer": ["autoprefixer@10.4.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="], + "base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + "bip32": ["bip32@5.0.0", "", { "dependencies": { "@noble/hashes": "^1.2.0", "@scure/base": "^1.1.1", "uint8array-tools": "^0.0.8", "valibot": "^0.37.0", "wif": "^5.0.0" } }, "sha512-h043yQ9n3iU4WZ8KLRpEECMl3j1yx2DQ1kcPlzWg8VZC0PtukbDiyLDKbe6Jm79mL6Tfg+WFuZMYxnzVyr/Hyw=="], + + "bip39": ["bip39@3.1.0", "", { "dependencies": { "@noble/hashes": "^1.2.0" } }, "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A=="], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + + "bs58check": ["bs58check@4.0.0", "", { "dependencies": { "@noble/hashes": "^1.2.0", "bs58": "^6.0.0" } }, "sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="], "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], @@ -308,6 +367,8 @@ "html5-qrcode": ["html5-qrcode@2.3.8", "", {}, "sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], @@ -448,6 +509,8 @@ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "tiny-secp256k1": ["tiny-secp256k1@2.2.4", "", { "dependencies": { "uint8array-tools": "0.0.7" } }, "sha512-FoDTcToPqZE454Q04hH9o2EhxWsm7pOSpicyHkgTwKhdKWdsTUuqfP5MLq3g+VjAtl2vSx6JpXGdwA2qpYkI0Q=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -456,16 +519,28 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "uint8array-tools": ["uint8array-tools@0.0.8", "", {}, "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], + + "valibot": ["valibot@0.37.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ=="], + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + "vite-plugin-top-level-await": ["vite-plugin-top-level-await@1.6.0", "", { "dependencies": { "@rollup/plugin-virtual": "^3.0.2", "@swc/core": "^1.12.14", "@swc/wasm": "^1.12.14", "uuid": "10.0.0" }, "peerDependencies": { "vite": ">=2.8" } }, "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww=="], + + "vite-plugin-wasm": ["vite-plugin-wasm@3.5.0", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" } }, "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ=="], + "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="], + "wif": ["wif@5.0.0", "", { "dependencies": { "bs58check": "^4.0.0" } }, "sha512-iFzrC/9ne740qFbNjTZ2FciSRJlHIXoxqk/Y5EnE08QOXu1WjJyCCswwDTYbohAOEnlCtLaAAQBhyaLRFh2hMA=="], + "wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], "y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="], @@ -476,8 +551,12 @@ "yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="], + "@types/qrcode/@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "bun-types/@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -485,5 +564,11 @@ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "tiny-secp256k1/uint8array-tools": ["uint8array-tools@0.0.7", "", {}, "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ=="], + + "@types/qrcode/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], } } diff --git a/package.json b/package.json index 7417c42..64d275f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,11 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@types/bip32": "^2.0.4", + "@types/bip39": "^3.0.4", "@types/pako": "^2.0.4", + "bip32": "^5.0.0", + "buffer": "^6.0.3", "html5-qrcode": "^2.3.8", "jsqr": "^1.4.0", "lucide-react": "^0.462.0", @@ -18,11 +22,12 @@ "pako": "^2.1.0", "qrcode": "^1.5.4", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "tiny-secp256k1": "^2.2.4" }, "devDependencies": { "@types/bun": "^1.3.6", - "@types/node": "^22.10.2", + "@types/node": "^25.2.1", "@types/qrcode": "^1.5.5", "@types/qrcode-generator": "^1.0.6", "@types/react": "^18.3.12", @@ -32,6 +37,8 @@ "postcss": "^8.4.49", "tailwindcss": "^3.4.17", "typescript": "^5.6.2", - "vite": "^6.0.3" + "vite": "^6.0.3", + "vite-plugin-top-level-await": "^1.6.0", + "vite-plugin-wasm": "^3.5.0" } } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index c963fd5..8071ea1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -73,8 +73,7 @@ function App() { // Krux integration state const [encryptionMode, setEncryptionMode] = useState<'pgp' | 'krux' | 'seedqr'>('pgp'); - const [kruxLabel, setKruxLabel] = useState('Seed Backup'); - const [kruxIterations, setKruxIterations] = useState(200000); + const [seedQrFormat, setSeedQrFormat] = useState<'standard' | 'compact'>('standard'); const [detectedMode, setDetectedMode] = useState(null); @@ -284,8 +283,6 @@ function App() { publicKeyArmored: publicKeyInput || undefined, messagePassword: backupMessagePassword || undefined, mode: encryptionMode, - kruxLabel: encryptionMode === 'krux' ? kruxLabel : undefined, - kruxIterations: encryptionMode === 'krux' ? kruxIterations : undefined, }); } @@ -604,39 +601,9 @@ function App() { {/* Krux-specific fields */} {encryptionMode === 'krux' && activeTab === 'backup' && ( <> -
- -
- setKruxLabel(e.target.value)} - readOnly={isReadOnly} - /> -
-

Label for identification (max 252 bytes)

-
-
- -
- setKruxIterations(Number(e.target.value))} - min={10000} - step={10000} - readOnly={isReadOnly} - /> -
-

Higher = more secure but slower (default: 200,000)

-
+ + )} @@ -660,6 +627,15 @@ function App() { : 'Symmetric encryption password (SKESK)'}

+
+
+ +

+ Krux Compatible Mode:
+ Uses wallet fingerprint as salt and 100,000 iterations (Krux defaults). +

+
+
{activeTab === 'backup' && ( diff --git a/src/lib/base43.ts b/src/lib/base43.ts index 3bc1064..6e4f1ad 100644 --- a/src/lib/base43.ts +++ b/src/lib/base43.ts @@ -34,3 +34,18 @@ export function base43Decode(str: string): Uint8Array { } return bytes; } + +export function base43Encode(data: Uint8Array): string { + let num = 0n; + for (let i = 0; i < data.length; i++) { + num = num * 256n + BigInt(data[i]); + } + let encoded = ''; + if (num === 0n) return '0'; + while (num > 0n) { + const remainder = Number(num % 43n); + encoded = B43CHARS[remainder] + encoded; + num = num / 43n; + } + return encoded; +} diff --git a/src/lib/bip32.ts b/src/lib/bip32.ts new file mode 100644 index 0000000..daadfa0 --- /dev/null +++ b/src/lib/bip32.ts @@ -0,0 +1,13 @@ +import { Buffer } from 'buffer'; +import * as bip39 from 'bip39'; +import { BIP32Factory } from 'bip32'; +import * as ecc from 'tiny-secp256k1'; + +const bip32 = BIP32Factory(ecc); + +export function getWalletFingerprint(mnemonic: string): string { + const seed = bip39.mnemonicToSeedSync(mnemonic); + const root = bip32.fromSeed(Buffer.from(seed)); + const fingerprint = root.fingerprint; + return Array.from(fingerprint).map(b => b.toString(16).padStart(2, '0')).join(''); +} \ No newline at end of file diff --git a/src/lib/krux.test.ts b/src/lib/krux.test.ts index 5ce28d8..ba57c1a 100644 --- a/src/lib/krux.test.ts +++ b/src/lib/krux.test.ts @@ -1,14 +1,15 @@ // Krux KEF tests using Bun test runner import { describe, test, expect } from "bun:test"; -import { - encryptToKrux, - decryptFromKrux, - hexToBytes, - bytesToHex, - wrap, +import { + encryptToKrux, + decryptFromKrux, + hexToBytes, + bytesToHex, + wrap, unwrap, - KruxCipher + KruxCipher } from './krux'; +import { getWalletFingerprint } from "./bip32"; describe('Krux KEF Implementation', () => { // Test basic hex conversion @@ -39,10 +40,10 @@ describe('Krux KEF Implementation', () => { const version = 20; const iterations = 200000; const payload = new TextEncoder().encode('test payload'); - + const wrapped = wrap(label, version, iterations, payload); const unwrapped = unwrap(wrapped); - + expect(unwrapped.label).toBe(label); expect(unwrapped.version).toBe(version); expect(unwrapped.iterations).toBe(iterations); @@ -52,7 +53,7 @@ describe('Krux KEF Implementation', () => { test('wrap rejects label too long', () => { const longLabel = 'a'.repeat(253); // 253 > 252 max const payload = new Uint8Array([1, 2, 3]); - + expect(() => wrap(longLabel, 20, 10000, payload)) .toThrow('Label too long'); }); @@ -69,10 +70,10 @@ describe('Krux KEF Implementation', () => { test('unwrap rejects invalid envelope', () => { expect(() => unwrap(new Uint8Array([1, 2, 3]))).toThrow('Invalid KEF envelope: too short'); - + // Label length too large (253 > 252) expect(() => unwrap(new Uint8Array([253, 20, 0, 0, 100]))).toThrow('Invalid label length'); - + // Empty label (lenId=0) is valid, but need enough data for version+iterations // Create a valid envelope with empty label: [0, version, iter1, iter2, iter3, payload...] const emptyLabelEnvelope = new Uint8Array([0, 20, 0, 0, 100, 1, 2, 3]); @@ -85,30 +86,28 @@ describe('Krux KEF Implementation', () => { test('encryptToKrux and decryptFromKrux roundtrip', async () => { const mnemonic = 'test test test test test test test test test test test junk'; const passphrase = 'secure-passphrase'; - const label = 'Test Seed'; - const iterations = 10000; - + + const encrypted = await encryptToKrux({ mnemonic, passphrase, - label, - iterations, - version: 20, }); - - expect(encrypted.kefHex).toMatch(/^[0-9A-F]+$/); - expect(encrypted.label).toBe(label); - expect(encrypted.iterations).toBe(iterations); + + const expectedLabel = getWalletFingerprint(mnemonic); + + expect(encrypted.kefBase43).toMatch(/^[0-9A-Z$*+-\./:]+$/); // Check Base43 format + expect(encrypted.label).toBe(expectedLabel); + expect(encrypted.iterations).toBe(100000); expect(encrypted.version).toBe(20); - + const decrypted = await decryptFromKrux({ - kefData: encrypted.kefHex, + kefData: encrypted.kefBase43, // Use kefBase43 for decryption passphrase, }); - + expect(decrypted.mnemonic).toBe(mnemonic); - expect(decrypted.label).toBe(label); - expect(decrypted.iterations).toBe(iterations); + expect(decrypted.label).toBe(expectedLabel); + expect(decrypted.iterations).toBe(100000); expect(decrypted.version).toBe(20); }); @@ -123,20 +122,20 @@ describe('Krux KEF Implementation', () => { await expect(decryptFromKrux({ kefData: '123456', passphrase: '', - })).rejects.toThrow('Passphrase is required'); + })).rejects.toThrow('Invalid Krux data: Not a valid Hex or Base43 string.'); // Updated error message }); test('wrong passphrase fails decryption', async () => { const mnemonic = 'test mnemonic'; const passphrase = 'correct-passphrase'; - + const encrypted = await encryptToKrux({ mnemonic, passphrase, }); - + await expect(decryptFromKrux({ - kefData: encrypted.kefHex, + kefData: encrypted.kefBase43, // Use kefBase43 for decryption passphrase: 'wrong-passphrase', })).rejects.toThrow(/Krux decryption failed/); }); @@ -145,49 +144,46 @@ describe('Krux KEF Implementation', () => { test('KruxCipher encrypt/decrypt roundtrip', async () => { const cipher = new KruxCipher('passphrase', new TextEncoder().encode('salt'), 10000); const plaintext = new TextEncoder().encode('secret message'); - + const encrypted = await cipher.encrypt(plaintext); const decrypted = await cipher.decrypt(encrypted, 20); - + expect(new TextDecoder().decode(decrypted)).toBe('secret message'); }); test('KruxCipher rejects unsupported version', async () => { const cipher = new KruxCipher('passphrase', new TextEncoder().encode('salt'), 10000); const plaintext = new Uint8Array([1, 2, 3]); - + await expect(cipher.encrypt(plaintext, 99)).rejects.toThrow('Unsupported KEF version'); - await expect(cipher.decrypt(new Uint8Array(50), 99)).rejects.toThrow('Payload too short for AES-GCM'); + await expect(cipher.decrypt(new Uint8Array(50), 99)).rejects.toThrow('Unsupported KEF version'); // Changed error message }); test('KruxCipher rejects short payload', async () => { const cipher = new KruxCipher('passphrase', new TextEncoder().encode('salt'), 10000); // Version 20: IV (12) + auth (4) = 16 bytes minimum const shortPayload = new Uint8Array(15); // Too short for IV + GCM tag (needs at least 16) - + await expect(cipher.decrypt(shortPayload, 20)).rejects.toThrow('Payload too short for AES-GCM'); }); test('iterations scaling works correctly', () => { - // Test that iterations are scaled properly when divisible by 10000 const label = 'Test'; const version = 20; const payload = new TextEncoder().encode('test payload'); - - // 200000 should be scaled to 20 in the envelope + const wrapped1 = wrap(label, version, 200000, payload); - expect(wrapped1[6]).toBe(0); // 200000 / 10000 = 20 + expect(wrapped1[6]).toBe(0); expect(wrapped1[7]).toBe(0); expect(wrapped1[8]).toBe(20); - - // 10001 should not be scaled + const wrapped2 = wrap(label, version, 10001, payload); const iterStart = 2 + label.length; const iters = (wrapped2[iterStart] << 16) | (wrapped2[iterStart + 1] << 8) | wrapped2[iterStart + 2]; expect(iters).toBe(10001); }); - - // New test case for user-provided KEF string + + // New test case for user-provided KEF string - this one already uses base43Decode test('should correctly decrypt the user-provided KEF string', async () => { const kefData = "1334+HGXM$F8PPOIRNHX0.R*:SBMHK$X88LX$*/Y417R/6S1ZQOB2LHM-L+4T1YQVU:B*CKGXONP7:Y/R-B*:$R8FK"; const passphrase = "aaa"; diff --git a/src/lib/krux.ts b/src/lib/krux.ts index 23a7df2..a1f8837 100644 --- a/src/lib/krux.ts +++ b/src/lib/krux.ts @@ -1,9 +1,8 @@ // src/lib/krux.ts // Krux KEF (Krux Encryption Format) implementation import * as pako from 'pako'; -import { base43Decode } from './base43'; - -// KEF version definitions, ported from kef.py +import { base43Decode, base43Encode } from './base43'; +import { getWalletFingerprint } from './bip32'; export const VERSIONS: Record b.toString(16).padStart(2, '0')).join('').toUpperCase(); } -export async function encryptToKrux(params: { mnemonic: string; passphrase: string; label?: string; iterations?: number; version?: number; }): Promise<{ kefHex: string; label: string; version: number; iterations: number }> { - const { mnemonic, passphrase, label = "Seed Backup", iterations = 200000, version = 21 } = params; - if (!passphrase) throw new Error("Passphrase is required for Krux encryption"); +export async function encryptToKrux(params: { + mnemonic: string; + passphrase: string; +}): Promise<{ kefBase43: string; label: string; version: number; iterations: number }> { + const { mnemonic, passphrase } = params; + + if (!passphrase) throw new Error("Passphrase is required"); + + const label = getWalletFingerprint(mnemonic); + const iterations = 100000; + const version = 20; const mnemonicBytes = await mnemonicToEntropy(mnemonic); - // For encryption, we encode the string label to get the salt bytes const cipher = new KruxCipher(passphrase, new TextEncoder().encode(label), iterations); const payload = await cipher.encrypt(mnemonicBytes, version); const kef = wrap(label, version, iterations, payload); - return { kefHex: bytesToHex(kef), label, version, iterations }; + const kefBase43 = base43Encode(kef); + + console.log('🔐 KEF Debug:', { label, iterations, version, length: kef.length, base43: kefBase43.slice(0, 50) }); + + return { kefBase43, label, version, iterations }; } export function wrap(label: string, version: number, iterations: number, payload: Uint8Array): Uint8Array { diff --git a/src/lib/seedpgp.ts b/src/lib/seedpgp.ts index 0bfcbe4..65c3c98 100644 --- a/src/lib/seedpgp.ts +++ b/src/lib/seedpgp.ts @@ -232,14 +232,11 @@ export async function encryptToSeed(params: EncryptionParams): Promise