From f345404416e08394c09ee9422d204db3e0624d0d Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Sep 2023 13:54:09 +0100 Subject: [PATCH] Add missing files --- .gitignore | 5 ++ js/booteth/eip1193.js | 20 +++++ js/booteth/package.json | 9 +++ js/booteth/session.js | 173 ++++++++++++++++++++++++++++++++++++++++ js/booteth/wallet.js | 117 +++++++++++++++++++++++++++ lets-encrypt-r3.pem | 30 +++++++ 6 files changed, 354 insertions(+) create mode 100644 .gitignore create mode 100644 js/booteth/eip1193.js create mode 100644 js/booteth/package.json create mode 100644 js/booteth/session.js create mode 100644 js/booteth/wallet.js create mode 100644 lets-encrypt-r3.pem diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15dde8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.pyc +__pycache__ +build/* +dist/* +*.egg-info diff --git a/js/booteth/eip1193.js b/js/booteth/eip1193.js new file mode 100644 index 0000000..45c94f6 --- /dev/null +++ b/js/booteth/eip1193.js @@ -0,0 +1,20 @@ +// only pass it jsonrpcapiprovider or subclass +function EIP1193DevWallet(baseProvider) { + self.baseProvider = baseProvider; + self.request = self._request_jsonrpc; +} + +EIP1193DevWallet.prototype.request_jsonrpc = async function(args) { + let r = null; + switch(args.method) { + default: + r = await self.baseProvider.send(args.method, args.params); + } + console.debug('provider result', r); + return r; +}; + +EIP1193DevWallet.prototype.request = async function(args) { + console.debug('provider args', args); + return this.request_jsonrpc(args); +}; diff --git a/js/booteth/package.json b/js/booteth/package.json new file mode 100644 index 0000000..d617196 --- /dev/null +++ b/js/booteth/package.json @@ -0,0 +1,9 @@ +{ + "devDependencies": { + "ethers": "6.5.1", + "jsqr": "^1.4.0", + "openpgp": "^5.9.0", + "qrcode": "1.5.3", + "MimeJS": "git://git.defalsify.org/mime-js.git#lash/plain-part" + } +} diff --git a/js/booteth/session.js b/js/booteth/session.js new file mode 100644 index 0000000..4b65148 --- /dev/null +++ b/js/booteth/session.js @@ -0,0 +1,173 @@ +openpgp.config.rejectCurves = new Set(); + +// pinched from https://stackoverflow.com/questions/16826200/javascript-silly-name-generator +const name_parts = [ + ["Runny", "Buttercup", "Dinky", "Stinky", "Crusty", + "Greasy","Gidget", "Cheesypoof", "Lumpy", "Wacky", "Tiny", "Flunky", + "Fluffy", "Zippy", "Doofus", "Gobsmacked", "Slimy", "Grimy", "Salamander", + "Oily", "Burrito", "Bumpy", "Loopy", "Snotty", "Irving", "Egbert"], + + ["Waffer", "Lilly","Rugrat","Sand", "Fuzzy","Kitty", + "Puppy", "Snuggles","Rubber", "Stinky", "Lulu", "Lala", "Sparkle", "Glitter", + "Silver", "Golden", "Rainbow", "Cloud", "Rain", "Stormy", "Wink", "Sugar", + "Twinkle", "Star", "Halo", "Angel"], + ["Snicker", "Buffalo", "Gross", "Bubble", "Sheep", + "Corset", "Toilet", "Lizard", "Waffle", "Kumquat", "Burger", "Chimp", "Liver", + "Gorilla", "Rhino", "Emu", "Pizza", "Toad", "Gerbil", "Pickle", "Tofu", + "Chicken", "Potato", "Hamster", "Lemur", "Vermin"], + ["face", "dip", "nose", "brain", "head", "breath", + "pants", "shorts", "lips", "mouth", "muffin", "butt", "bottom", "elbow", + "honker", "toes", "buns", "spew", "kisser", "fanny", "squirt", "chunks", + "brains", "wit", "juice", "shower"], +]; + +function generateName() { + name = ''; + for (let i = 0; i < name_parts.length; i++) { + if (i > 0 && i < 3) { + name += ' '; + } + const ii = Math.random() * name_parts[i].length; + name += name_parts[i][Math.floor(ii)]; + } + return name; +} +async function createSessionKey(passphrase) { + const name = generateName(); + const k = await openpgp.generateKey({ + curve: 'secp256k1', + userIDs: [{name: name, email: 'none@example.com'}], + passphrase: passphrase, + format: 'armored', + }); + return k.privateKey; +} + +async function toKeyPair(pk, passphrase) { + let o = await openpgp.readPrivateKey({ + armoredKey: pk, + }); + if (!passphrase) { + passphrase = undefined; + } + if (passphrase !== undefined) { + o = await openpgp.decryptKey({ + privateKey: o, + passphrase: passphrase, + }); + } + const r = o.toPacketList().findPacket(openpgp.enums.packet.secretKey); + return { + 'publicKey': r.publicParams.Q, + 'privateKey': r.privateParams.d, + }; +} + +function SessionWallet() { + this.provider = null; + this.wallet_key = null; + this.wallet = null; + this.wallet_address = null; + //self.wallet_address = "0xeb3907ecad74a0013c259d5874ae7f22dcbcc95c"; +} + +async function ethKeyFromPGPKey(pk, passphrase) { + const k = await toKeyPair(pk, passphrase); + const signKey = new ethers.SigningKey(k.privateKey); + return new ethers.Wallet(signKey); + +} + +SessionWallet.prototype.applyKey = async function(pk, passphrase) { + this.wallet = await ethKeyFromPGPKey(pk, passphrase); + this.wallet_key = pk; + this.wallet_address = this.wallet.address; + console.log('wallet key applied', this.wallet_address); +} + +SessionWallet.prototype.connect = async function(provider) { + this.provider = provider; + return this; +} + +SessionWallet.prototype.getAddress = function() { + return this.wallet_address; +} + +SessionWallet.prototype.getNonce = async function() { + return this.provider.send('eth_getTransactionCount', [this.wallet_address, 'latest']); +} + +async function sessionwallet_create(id, passphrase) { + const w = new SessionWallet(); + if (window.localStorage.getItem('sessionKey_' + id) == null) { + console.debug('creating new key'); + const newPk = await createSessionKey(); + const ethKey = await ethKeyFromPGPKey(newPk); + const o = await openpgp.readKey({ + armoredKey: newPk, + }); + let pl = o.toPacketList(); + let u = pl.findPacket(openpgp.enums.packet.userID); + let a = ethKey.address; + if (a.substring(0, 2) == '0x') { + a = a.substring(2); + } + const uNew = openpgp.UserIDPacket.fromObject({ + name: u.name, + email: a.toLowerCase() + '@defalsify.org', + comment: "evm", + }); + const r = await openpgp.reformatKey({ + privateKey: o, + userIDs: [uNew], + }); + let pku = await openpgp.readKey({ + armoredKey: r.privateKey, + }); + if (passphrase !== undefined) { + console.debug('encrypting'); + pku = await openpgp.encryptKey({ + privateKey: pku, + passphrase: passphrase, + + }); + } + window.localStorage.setItem('sessionKey_' + id, pku.armor()); + pl = pku.toPacketList(); + u = pl.findPacket(openpgp.enums.packet.userID); + console.debug('added key', u.name, u.email); + } + const pksrc = window.localStorage.getItem('sessionKey_' + id); + console.debug('src', pksrc); + let pk = await openpgp.readKey({ + armoredKey: pksrc, + }); + if (passphrase !== undefined) { + pk = await openpgp.decryptKey({ + privateKey: pk, + passphrase: passphrase, + }); + } + await w.applyKey(pk.armor()); // the key is decrypted at this point + return w; +} + +async function sessionwallet_importEth(id, passphrase) { + const pk = window.localStorage.getItem('sessionKey_' + id); + await ethers.Wallet.fromEncryptedJson(pk, passphrase); + return pk; +} + +async function sessionwallet_exportEth(id, passphrase, progress) { + const pk = window.localStorage.getItem('sessionKey_' + id); + const w = await ethKeyFromPGPKey(pk, passphrase); + const we = await w.encrypt(passphrase, progress); + return we; +} + +async function sessionwallet_exportPgp(id, passphrase, progress) { + const pk = window.localStorage.getItem('sessionKey_' + id); + console.log(pk); + return pk; +} diff --git a/js/booteth/wallet.js b/js/booteth/wallet.js new file mode 100644 index 0000000..ad8a023 --- /dev/null +++ b/js/booteth/wallet.js @@ -0,0 +1,117 @@ +WALLETS = [ + 'boot', + 'brave', + 'metamask', +]; + +provider_rpc_url = 'http://localhost:8545'; +wallet_fallback = undefined; +wallet_init = false; +wallet_registered = {}; +provider_registered = {}; +var current_provider = null; +var current_signer = null; +ethers = undefined; + +function register_ethers(m) { + ethers = m; +} + +function wallet_register(wallet_key, wallet_constructor) { + if (!wallet_key in WALLETS) { + throw 'unknown wallet: ' + wallet; + } + if (wallet_registered[wallet_key] != undefined) { + console.warn('wallet already registered', wallet_key); + return; + } + console.log('registered wallet', wallet_key); + wallet_registered[wallet_key] = wallet_constructor; +} + +function provider_register(provider_key, provider_constructor) { + if (provider_registered[provider_key] != undefined) { + console.warn('provider already registered', provider_key); + return; + } + console.log('registered provider', provider_key); + provider_registered[provider_key] = provider_constructor; +} + +async function wallet_connect(wallet_key, instanceId, passphrase) { + if (!passphrase) { + passphrase = undefined; + } + const fn = wallet_registered[wallet_key]; + let signer = await fn(instanceId, passphrase); + let signed_connected; + try { + signer_connected = await signer.connect(current_provider); + } catch(e) { + console.debug('signer connect error', e); + signer_connected = await current_provider.getSigner(); + } + current_signer = signer_connected; + console.log('connected to wallet', wallet_key, current_signer); + return current_signer; +} + +async function provider_connect(provider_key) { + const fn = provider_registered[provider_key]; + current_provider = await fn(); + console.log('registered provider', provider_key); + return current_provider; +} + +function wallet_detect(ethers_instance) { + if (wallet_init) { + return; + } + ethers = ethers_instance; + if (wallet_fallback !== undefined) { + wallet_register('default', wallet_fallback); + } + if (window.ethereum != null) { + if (window.ethereum.isBraveWallet) { + wallet_register('brave', metamaskish_signer_connect); + provider_register('brave', metamaskish_provider_connect); + } else if (window.ethereum.isMetaMask) { + wallet_register('metamask', metamaskish_signer_connect); + provider_register('metamask', metamaskish_provider_connect); + } + } + wallet_init = true; +} + +async function metamaskish_provider_connect() { + let provider; + return new ethers.BrowserProvider(window.ethereum) +} + +async function metamaskish_signer_connect() { + let signer_provider; + if (current_provider != null) { + signer_provider = current_provider; + } else { + current_provider = new ethers.BrowserProvider(window.ethereum); + signer_provider = current_provider; + } + return await signer_provider.getSigner(); +} + +async function jsonrpc_provider_connect(url) { + if (url === undefined) { + url = provider_rpc_url; + } + return new ethers.JsonRpcProvider(url); +} + +function getWallet(id) { + return wallet_registered[id]; +} + +provider_register('default', jsonrpc_provider_connect); + +function getProvider(id) { + return provider_registered[id]; +} diff --git a/lets-encrypt-r3.pem b/lets-encrypt-r3.pem new file mode 100644 index 0000000..43b222a --- /dev/null +++ b/lets-encrypt-r3.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw +WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP +R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx +sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm +NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg +Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG +/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA +FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw +AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw +Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB +gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W +PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl +ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz +CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm +lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 +avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 +yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O +yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids +hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ +HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv +MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX +nLRbwHOoq7hHwg== +-----END CERTIFICATE-----