Skip to content

Instantly share code, notes, and snippets.

@foloinfo
Created January 2, 2023 23:30
Show Gist options
  • Save foloinfo/ed3733393ec859c0c8fb510b7baf6355 to your computer and use it in GitHub Desktop.
Save foloinfo/ed3733393ec859c0c8fb510b7baf6355 to your computer and use it in GitHub Desktop.
Deno compatible encryption with x25519-xsalsa20-poly1305 for Metamask (using eth_getEncryptionPublicKey and eth_decrypt)
import * as nacl from 'https://raw.githubusercontent.com/intob/tweetnacl-deno/master/src/nacl.ts'
export function hex(arrayBuffer)
{
return Array.prototype.map.call(
new Uint8Array(arrayBuffer),
n => n.toString(16).padStart(2, "0")
).join("");
}
// slightly modified from
// https://github.com/MetaMask/eth-sig-util/blob/main/src/encryption.ts
export function isNullish(value: any) {
return value === null || value === undefined;
}
export interface EthEncryptedData {
version: string;
nonce: string;
ephemPublicKey: string;
ciphertext: string;
}
/**
* Encrypt a message.
*
* @param options - The encryption options.
* @param options.publicKey - The public key of the message recipient.
* @param options.data - The message data.
* @param options.version - The type of encryption to use.
* @returns The encrypted data.
*/
export function encrypt({
publicKey,
data,
version,
}: {
publicKey: string;
data: unknown;
version: string;
}): EthEncryptedData {
if (isNullish(publicKey)) {
throw new Error('Missing publicKey parameter');
} else if (isNullish(data)) {
throw new Error('Missing data parameter');
} else if (isNullish(version)) {
throw new Error('Missing version parameter');
}
switch (version) {
case 'x25519-xsalsa20-poly1305': {
if (typeof data !== 'string') {
throw new Error('Message data must be given as a string');
}
// generate ephemeral keypair
const ephemeralKeyPair = nacl.box_keyPair();
// assemble encryption parameters - from string to UInt8
let pubKeyUInt8Array;
try {
pubKeyUInt8Array = nacl.decodeBase64(publicKey);
} catch (err) {
throw new Error('Bad public key');
}
const msgParamsUInt8Array = nacl.decodeUTF8(data);
const nonce = nacl.randomBytes(24);
// encrypt
const encryptedMessage = nacl.box(
msgParamsUInt8Array,
nonce,
pubKeyUInt8Array,
ephemeralKeyPair.secretKey,
);
// handle encrypted data
const output = {
version: 'x25519-xsalsa20-poly1305',
nonce: nacl.encodeBase64(nonce),
ephemPublicKey: nacl.encodeBase64(ephemeralKeyPair.publicKey),
ciphertext: nacl.encodeBase64(encryptedMessage),
};
// return encrypted msg data
return output;
}
default:
throw new Error('Encryption type/version not supported');
}
}
import { encrypt, hex } from './eth_encrypt.ts'
const publicKey = 'public key generated by eth_getEncryptionPublicKey'
const data = 'whatever you want to encrypt'
const encrypted = encrypt({
publicKey,
data,
version: 'x25519-xsalsa20-poly1305',
})
const encoder = new TextEncoder()
const encryptedMessage = hex(
encoder.encode(
JSON.stringify(encrypted)
)
)
// you can pass this encryptedString to eth_decrypt to read on client
// https://docs.metamask.io/guide/rpc-api.html#unrestricted-methods
// ethereum
// .request({
// method: 'eth_decrypt',
// params: [encryptedMessage, accounts[0]],
// })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment