From 78bb546e6632df4c34c9807be8cca627dbbda934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=81lio=20Santos?= Date: Thu, 9 Nov 2023 15:14:28 +0100 Subject: [PATCH] draft enclave snap --- packages/snap/snap.manifest.json | 2 +- packages/snap/src/index.ts | 107 +++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index b87236c..5211279 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/juliosantos/idos-snap.git" }, "source": { - "shasum": "vpIkYjF3kE4hSGartB4qTDdN2xdBfbbrDtd0FMQJEqc=", + "shasum": "tOMCi8CQ2iAhWTtQxgjkOyj4DB08XIzuNy6GQg+RwPY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/index.ts b/packages/snap/src/index.ts index 13f92cf..0064a27 100644 --- a/packages/snap/src/index.ts +++ b/packages/snap/src/index.ts @@ -29,29 +29,49 @@ const setState = async (newState = {}, { clear = false } = {}) => { }); }; -const snapPrompt = async ({ origin = "unknown" }) => { - return snap.request({ - method: "snap_dialog", - params: { - type: "prompt", - content: panel([ - ...panelHeader(origin), - text(`Please enter your idOS password`), - text(`It will take a few seconds to digest.`), - ]), - placeholder: "idOS password", - }, - }) +const clearState = async () => await snap.request({ + method: 'snap_manageState', + params: { operation: 'clear' }, +}); + +const storage = async (payload = {}) => { + const toStore = Object.entries(payload).reduce((res, [k, v]) => ( + !!v ? Object.assign(res, {[k]: v}) : res + ), {}) + await setState(toStore); + + let { humanId, signerAddress, signerPublicKey, keyPair } = await getState(); + + const encryptionPublicKey = keyPair?.publicKey; + + return { storage: { + humanId, + signerAddress, + signerPublicKey, + encryptionPublicKey, + }}; }; -const init = async ({ humanId }, origin) => { - let { keyPair } = await getState(); +const init = async (requestParams, origin) => { + let { keyPair, humanId } = await getState(); + let publicKey; + + if (keyPair) { + publicKey = Uint8Array.from(Object.values(keyPair.publicKey)); + } else { + const password = await snap.request({ + method: "snap_dialog", + params: { + type: "prompt", + content: panel([ + ...panelHeader(origin), + text(`Please enter your idOS password`), + text(`It will take a few seconds to digest.`), + ]), + placeholder: "idOS password", + }, + }); - let { publicKey } = keyPair; - publicKey &&= Uint8Array.from(Object.values(keyPair.publicKey)); - - if (!keyPair) { - const password = await snapPrompt({ origin }); const secretKey = await scrypt.scrypt( Utf8Codec.encode(password.normalize("NFKC")), Utf8Codec.encode(humanId), @@ -60,7 +80,7 @@ const init = async ({ humanId }, origin) => { keyPair = nacl.box.keyPair.fromSecretKey(secretKey); - void await setState({ humanId, keyPair }); + void await setState({ keyPair }); publicKey = keyPair.publicKey; } @@ -95,32 +115,53 @@ const encrypt = async ({ message, receiverPublicKey }) => { const decrypt = async ({ message: fullMessage, senderPublicKey }) => { if (!fullMessage) throw new Error("decrypt: no message"); - fullMessage = Base64Codec.decode(fullMessage); + fullMessage = Uint8Array.from(Object.values(fullMessage)) const { keyPair } = await getState(); - if (!keyPair) throw new error("decrypt: no keypair"); + if (!keyPair) throw new Error("decrypt: no keypair"); let { publicKey, secretKey } = keyPair; publicKey = Uint8Array.from(Object.values(publicKey)) secretKey = Uint8Array.from(Object.values(secretKey)) - senderPublicKey &&= Base64Codec.decode(senderPublicKey); + senderPublicKey &&= Uint8Array.from(Object.values(senderPublicKey)); senderPublicKey ||= publicKey; const nonce = fullMessage.slice(0, nacl.box.nonceLength); const message = fullMessage.slice(nacl.box.nonceLength, fullMessage.length); - const decrypted = nacl.box.open(message, nonce, publicKey, secretKey); + const decrypted = nacl.box.open(message, nonce, senderPublicKey, secretKey); if (!decrypted) throw new Error("decrypt: decryption failed"); - return { decrypted: Utf8Codec.decode(decrypted) }; + return { decrypted }; +}; + +const reset = async () => await clearState(); + +const confirm = async({ message }, origin) => { + const confirmed = snap.request({ + method: "snap_dialog", + params: { + type: "confirmation", + content: panel([ + ...panelHeader(origin), + text("**This dapp is asking you:**"), + text(`_${message}_`), + ]), + }, + }); + + return { confirmed }; }; export const onRpcRequest: OnRpcRequestHandler = async ({ origin, request }) => { switch (request.method) { - case "init": - if (!request.params.humanId) return { error: "no human ID" }; + case "storage": + return storage(request.params) + .then(({ storage }) => JSON.stringify(storage)) + .catch(console.warn); + case "init": return init(request.params, origin) .then(({ publicKey }) => publicKey) .catch(console.warn); @@ -139,6 +180,16 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ origin, request }) => .then(({ decrypted }) => decrypted) .catch(console.warn); + case "reset": + return reset() + .then(() => console.log("reset!")) + .catch(console.warn); + + case "confirm": + return confirm(request.params, origin) + .then(({ confirmed }) => confirmed) + .catch(console.warn); + default: throw new Error("Unexpected request."); }