From 76d7ff03e89b69969fcce7a75b8b44c939895c35 Mon Sep 17 00:00:00 2001 From: Inbestigator Date: Wed, 6 Mar 2024 02:00:45 -0800 Subject: [PATCH] Added bypassdnd --- src/plugins/bypassdnd/index.tsx | 155 +++++++++ src/plugins/encryptcord/index.tsx | 457 -------------------------- src/plugins/encryptcord/rsa-utils.tsx | 116 ------- 3 files changed, 155 insertions(+), 573 deletions(-) create mode 100644 src/plugins/bypassdnd/index.tsx delete mode 100644 src/plugins/encryptcord/index.tsx delete mode 100644 src/plugins/encryptcord/rsa-utils.tsx diff --git a/src/plugins/bypassdnd/index.tsx b/src/plugins/bypassdnd/index.tsx new file mode 100644 index 000000000..1a826a6e1 --- /dev/null +++ b/src/plugins/bypassdnd/index.tsx @@ -0,0 +1,155 @@ +import { NavContextMenuPatchCallback, addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; +import { showNotification } from "@api/Notifications"; +import { definePluginSettings } from "@api/Settings"; +import { DataStore } from "@api/index"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, Menu, PrivateChannelsStore, UserStore } from "@webpack/common"; +import { Channel, Guild, Message, User } from "discord-types/general"; +interface ContextProps { + channel: Channel; + user: User; + guild: Guild; +} + +interface IMessageCreate { + type: "MESSAGE_CREATE"; + optimistic: boolean; + isPushNotification: boolean; + channelId: string; + guildId: string; + message: Message; +} + +const GuildContext: NavContextMenuPatchCallback = (children, { guild }: ContextProps) => () => { + if (!guild) return; + children.splice(-1, 0, ( + + { + if (bypasses["guilds"].includes(guild.id)) bypasses["guilds"] = await bypasses["guilds"].filter(id => id !== guild.id); + else bypasses["guilds"].push(guild.id); + await DataStore.set("bypassdnd", bypasses); + settings.store.guilds = (bypasses["guilds"].join(', ')); + }} + /> + + )); +}; + +const ChannelContext: NavContextMenuPatchCallback = (children, { channel }: ContextProps) => () => { + if (!channel) return; + children.splice(-1, 0, ( + + { + if (bypasses["channels"].includes(channel.id)) bypasses["channels"] = await bypasses["channels"].filter(id => id !== channel.id); + else bypasses["channels"].push(channel.id); + await DataStore.set("bypassdnd", bypasses); + settings.store.channels = (bypasses["channels"].join(', ')); + }} + /> + + )); +}; + +const UserContext: NavContextMenuPatchCallback = (children, { user }: ContextProps) => () => { + if (!user) return; + children.splice(-1, 0, ( + + { + if (bypasses["users"].includes(user.id)) bypasses["users"] = await bypasses["users"].filter(id => id !== user.id); + else bypasses["users"].push(user.id); + await DataStore.set("bypassdnd", bypasses); + settings.store.users = (bypasses["users"].join(', ')); + }} + /> + + )); +}; + +let bypasses; + +const settings = definePluginSettings({ + guilds: { + type: OptionType.STRING, + description: "Guilds to let bypass (notified when pinged anywhere in guild)", + default: "", + placeholder: "Separate with commas", + onChange: async function (value) { + bypasses["guild"] = value.replace(/\s/g, '').split(',').filter(id => id.trim() !== ''); + await DataStore.set("bypassdnd", bypasses); + }, + }, + channels: { + type: OptionType.STRING, + description: "Channels to let bypass (notified when pinged in that channel)", + default: "", + placeholder: "Separate with commas", + onChange: async function (value) { + bypasses["channels"] = value.replace(/\s/g, '').split(',').filter(id => id.trim() !== ''); + await DataStore.set("bypassdnd", bypasses); + }, + }, + users: { + type: OptionType.STRING, + description: "Users to let bypass (notified for all messages)", + default: "", + placeholder: "Separate with commas", + onChange: async function (value) { + bypasses["users"] = value.replace(/\s/g, '').split(',').filter(id => id.trim() !== ''); + await DataStore.set("bypassdnd", bypasses); + }, + } +}); + +export default definePlugin({ + name: "BypassDND", + description: "Still get notifications from specific sources.", + authors: [Devs.Inbestigator], + flux: { + async MESSAGE_CREATE({ optimistic, type, message, guildId, channelId }: IMessageCreate) { + if (optimistic || type !== "MESSAGE_CREATE") return; + if (message.state === "SENDING") return; + if (message.author.id === UserStore.getCurrentUser().id) return; + if (!message.content) return; + + const { guilds, channels, users } = bypasses; + if ((guilds.includes(guildId) || channels.includes(channelId)) && (message.content.includes(`<@${UserStore.getCurrentUser().id}>`) || message.mentions.some(mention => mention.id === UserStore.getCurrentUser().id))) { + await showNotification({ + title: `${message.author.username} sent a message in ${ChannelStore.getChannel(channelId).name}`, + body: message.content, + onClick: () => window.location.href = `https://discord.com/channels/${guildId}/${channelId}/${message.id}` + }); + return; + } + if (users.includes(message.author.id) && channelId === await PrivateChannelsStore.getOrEnsurePrivateChannel(message.author.id)) { + await showNotification({ + title: `${message.author.username} sent a message in a DM`, + body: message.content, + onClick: () => window.location.href = `https://discord.com/channels/@me/${channelId}/${message.id}` + }); + } + } + }, + settings, + async start() { + addContextMenuPatch("guild-context", GuildContext); + addContextMenuPatch("channel-context", ChannelContext); + addContextMenuPatch("user-context", UserContext); + bypasses = await DataStore.get("bypassdnd") ?? { guilds: [], channels: [], users: [] }; + await DataStore.set("bypassdnd", bypasses); + }, + stop() { + removeContextMenuPatch("guild-context", GuildContext); + removeContextMenuPatch("channel-context", ChannelContext); + removeContextMenuPatch("user-context", UserContext); + } +}); diff --git a/src/plugins/encryptcord/index.tsx b/src/plugins/encryptcord/index.tsx deleted file mode 100644 index d3e247026..000000000 --- a/src/plugins/encryptcord/index.tsx +++ /dev/null @@ -1,457 +0,0 @@ -import { addChatBarButton, ChatBarButton } from "@api/ChatButtons"; -import { removeButton } from "@api/MessagePopover"; -import definePlugin from "@utils/types"; -import * as DataStore from "@api/DataStore"; -import { sleep } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; -import { addPreSendListener, removePreSendListener, SendListener } from "@api/MessageEvents"; -import { useEffect, useState, FluxDispatcher, PrivateChannelsStore } from "@webpack/common"; -import { generateKeys, encryptData, decryptData, formatPemKey } from "./rsa-utils"; -import { Devs } from "@utils/constants"; -import { - RestAPI, - SnowflakeUtils, - UserUtils, - UserStore, - MessageActions, -} from "@webpack/common"; -import { - ApplicationCommandInputType, - sendBotMessage, - ApplicationCommandOptionType, - findOption, -} from "@api/Commands"; -import { Message } from "discord-types/general"; -const MessageCreator = findByPropsLazy("createBotMessage"); -const CloudUtils = findByPropsLazy("CloudUpload"); -import { getCurrentChannel, openPrivateChannel } from "@utils/discord"; - -let enabled; -let setEnabled; - -// Interface for Message Create -interface IMessageCreate { - type: "MESSAGE_CREATE"; - optimistic: boolean; - isPushNotification: boolean; - channelId: string; - message: Message; -} - -// Chat Bar Icon Component -const ChatBarIcon: ChatBarButton = ({ isMainChat }) => { - [enabled, setEnabled] = useState(false); - let [buttonDisabled, setButtonDisabled] = useState(false); - - useEffect(() => { - const listener: SendListener = async (_, message) => { - if (enabled) { - const groupChannel = await DataStore.get('encryptcordChannelId'); - if (getCurrentChannel().id !== groupChannel) { - sendBotMessage(getCurrentChannel().id, { content: `You must be in <#${groupChannel}> to send an encrypted message!\n> If you wish to send an unencrypted message, please click the button in the chatbar.` }); - message.content = ""; - return; - } - const trimmedMessage = message.content.trim(); - await MessageActions.receiveMessage(groupChannel, await createMessage(trimmedMessage, UserStore.getCurrentUser().id, groupChannel, 0)); - const encryptcordGroupMembers = await DataStore.get('encryptcordGroupMembers'); - const dmPromises = Object.keys(encryptcordGroupMembers).map(async (memberId) => { - const groupMember = await UserUtils.getUser(memberId).catch(() => null); - if (!groupMember) return; - const encryptedMessage = await encryptData(encryptcordGroupMembers[memberId].key, trimmedMessage); - const encryptedMessageString = JSON.stringify(encryptedMessage); - await sendTempMessage(groupMember.id, encryptedMessageString, `message`); - }); - - await Promise.all(dmPromises); - message.content = ""; - } - }; - - addPreSendListener(listener); - return () => void removePreSendListener(listener); - }, [enabled]); - - if (!isMainChat) return null; - - return ( - { - if (await DataStore.get('encryptcordGroup') == false || (await DataStore.get('encryptcordChannelId') != getCurrentChannel().id)) { - setButtonDisabled(true); - await sendTempMessage(getCurrentChannel().id, "", `join\`\`\`\n${await DataStore.get("encryptcordPublicKey")}\`\`\``, false); - sendBotMessage(getCurrentChannel().id, { content: `*Checking for any groups in this channel...*\n> If none is found, a new one will be created \n> [Tip] You can do \`/encryptcord leave\` to leave a group` }); - await sleep(5000); - if (await DataStore.get('encryptcordGroup') == true && (await DataStore.get('encryptcordChannelId') != getCurrentChannel().id)) { - sendBotMessage(getCurrentChannel().id, { content: "*Leaving current group...*" }); - await leave("", { channel: { id: await DataStore.get('encryptcordChannelId') } }); - } else if (await DataStore.get('encryptcordGroup') == true) { - setButtonDisabled(false); - return; - }; - await startGroup("", { channel: { id: getCurrentChannel().id } }); - } - setEnabled(!enabled); - setButtonDisabled(false); - }} - buttonProps={{ - style: { - transition: 'transform 0.3s ease-in-out', - transform: `rotate(${enabled ? 0 : 15}deg)`, - }, - disabled: buttonDisabled - }} - > - - {!enabled && <> - - - - - } - - - - ); -}; - -// Export Plugin -export default definePlugin({ - name: "Encryptcord", - description: "End-to-end encryption in Discord!", - authors: [Devs.Inbestigator], - dependencies: ["CommandsAPI"], - patches: [ - { - find: "executeMessageComponentInteraction:", - replacement: { - match: /await\s+l\.default\.post\({\s*url:\s*A\.Endpoints\.INTERACTIONS,\s*body:\s*C,\s*timeout:\s*3e3\s*},\s*t\s*=>\s*{\s*h\(T,\s*p,\s*f,\s*t\)\s*}\s*\)/, - replace: 'await $self.joinGroup(C);$&' - } - } - ], - async joinGroup(interaction) { - const sender = await UserUtils.getUser(interaction.application_id).catch(() => null); - if (!sender || (sender.bot == true && sender.id != "1")) return; - if (interaction.data.component_type != 2) return; - switch (interaction.data.custom_id) { - case "removeFromSelf": - await handleLeaving(sender.id, await DataStore.get("encryptcordGroupMembers") ?? {}, interaction.channel_id); - await sendTempMessage(sender.id, "", "leaving"); - FluxDispatcher.dispatch({ - type: "MESSAGE_DELETE", - channelId: interaction.channel_id, - id: interaction.message_id, - mlDeleted: true - }); - break; - case "createGroup": - await leave("", { channel: { id: interaction.channel_id } }); - await startGroup("", { channel: { id: interaction.channel_id } }); - break; - default: - return; - } - }, - flux: { - async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) { - if (optimistic || type !== "MESSAGE_CREATE") return; - if (message.state === "SENDING") return; - if (message.author.id == UserStore.getCurrentUser().id) return; - if (!message.content) return; - const encryptcordGroupMembers = await DataStore.get('encryptcordGroupMembers'); - if (!Object.keys(encryptcordGroupMembers).some(key => key == message.author.id)) { - switch (message.content.toLowerCase().split("```")[0]) { - case "groupdata": - const response = await fetch(message.attachments[0].url); - const groupdata = await response.json(); - await handleGroupData(groupdata); - break; - case "join": - if (encryptcordGroupMembers[UserStore.getCurrentUser().id].child) return; - if (!await DataStore.get("encryptcordGroup")) return; - const sender = await UserUtils.getUser(message.author.id).catch(() => null); - if (!sender) return; - const userKey = message.content.split("```")[1]; - await handleJoin(sender.id, userKey, encryptcordGroupMembers); - break; - default: - break; - } - return; - } - const dmChannelId = await PrivateChannelsStore.getOrEnsurePrivateChannel(message.author.id); - if (channelId !== dmChannelId) return; - const sender = await UserUtils.getUser(message.author.id).catch(() => null); - if (!sender) return; - const groupChannel = await DataStore.get('encryptcordChannelId'); - switch (message.content.toLowerCase()) { - case "leaving": - handleLeaving(sender.id, encryptcordGroupMembers, groupChannel); - break; - case "message": - const msgResponse = await fetch(message.attachments[0].url); - const messagedata = await msgResponse.json(); - await handleMessage(messagedata, sender.id, groupChannel); - break; - case "groupdata": - const response = await fetch(message.attachments[0].url); - const groupdata = await response.json(); - await handleGroupData(groupdata); - break; - default: - break; - } - }, - }, - commands: [ - { - name: "encryptcord", - description: "End-to-end encryption in Discord!", - options: [ - { - name: "leave", - description: "Leave current group", - options: [], - type: ApplicationCommandOptionType.SUB_COMMAND, - }, - { - name: "data", - description: "View your keys and current group members", - options: [], - type: ApplicationCommandOptionType.SUB_COMMAND, - }, - ], - inputType: ApplicationCommandInputType.BOT, - execute: (opts, ctx) => { - switch (opts[0].name) { - case "start": - startGroup(opts[0].options, ctx); - break; - case "leave": - leave(opts[0].options, ctx); - break; - case "data": - data(opts[0].options, ctx); - break; - } - }, - }, - ], - async start() { - addChatBarButton("Encryptcord", ChatBarIcon); - const pair = await generateKeys(); - await DataStore.set('encryptcordPublicKey', pair.publicKey); - await DataStore.set('encryptcordPrivateKey', pair.privateKey); - if (await DataStore.get("encryptcordGroup") == true) { - await leave("", { channel: { id: await DataStore.get("encryptcordChannelId") } }); - } - await DataStore.set('encryptcordGroup', false); - await DataStore.set('encryptcordChannelId', ""); - await DataStore.set('encryptcordGroupMembers', {}); - }, - async stop() { - removeButton("Encryptcord"); - if (await DataStore.get("encryptcordGroup") == true) { - await leave("", { channel: { id: await DataStore.get("encryptcordChannelId") } }); - } - }, -}); - -// Send Temporary Message -async function sendTempMessage(recipientId: string, attachment: string, content: string, dm: boolean = true) { - if (recipientId == UserStore.getCurrentUser().id) return; - const dmChannelId = dm ? await PrivateChannelsStore.getOrEnsurePrivateChannel(recipientId) : recipientId; - if (attachment && attachment != "") { - const upload = await new CloudUtils.CloudUpload({ - file: new File([new Blob([attachment])], "file.text", { type: "text/plain; charset=utf-8" }), - isClip: false, - isThumbnail: false, - platform: 1, - }, dmChannelId, false, 0); - upload.on("complete", async () => { - const messageId = await RestAPI.post({ - url: `/channels/${dmChannelId}/messages`, - body: { - content, - attachments: [{ - id: "0", - filename: upload.filename, - uploaded_filename: upload.uploadedFilename, - }], - nonce: SnowflakeUtils.fromTimestamp(Date.now()), - }, - }).then((response) => response.body.id); - - await sleep(500); - RestAPI.delete({ - url: `/channels/${dmChannelId}/messages/${messageId}` - }); - }); - await upload.upload(); - return; - } - - const messageId = await RestAPI.post({ - url: `/channels/${dmChannelId}/messages`, - body: { - content, - nonce: SnowflakeUtils.fromTimestamp(Date.now()), - }, - }).then((response) => response.body.id); - - await sleep(500); - RestAPI.delete({ - url: `/channels/${dmChannelId}/messages/${messageId}` - }); -} - -// Handle leaving group -async function handleLeaving(senderId: string, encryptcordGroupMembers: object, groupChannel: string) { - const updatedMembers = Object.keys(encryptcordGroupMembers).reduce((result, memberId) => { - if (memberId !== senderId) { - result[memberId] = encryptcordGroupMembers[memberId]; - if (result[memberId].child == senderId) { - result[memberId].child = encryptcordGroupMembers[senderId].child; - } - if (result[memberId].parent == senderId) { - result[memberId].parent = encryptcordGroupMembers[senderId].parent; - } - } - return result; - }, {}); - - await DataStore.set('encryptcordGroupMembers', updatedMembers); - - await MessageActions.receiveMessage(groupChannel, await createMessage("", senderId, groupChannel, 2)); -} - -// Handle receiving message -async function handleMessage(message, senderId: string, groupChannel: string) { - const decryptedMessage = await decryptData(await DataStore.get("encryptcordPrivateKey"), message); - await MessageActions.receiveMessage(groupChannel, await createMessage(decryptedMessage, senderId, groupChannel, 0)); -} - -// Handle receiving group data -async function handleGroupData(groupData) { - await DataStore.set('encryptcordChannelId', groupData.channel); - await DataStore.set('encryptcordGroupMembers', groupData.members); - await DataStore.set('encryptcordGroup', true); - await MessageActions.receiveMessage(groupData.channel, await createMessage("", UserStore.getCurrentUser().id, groupData.channel, 7)); - setEnabled(true); -} - -// Handle joining group -async function handleJoin(senderId: string, senderKey: string, encryptcordGroupMembers: object) { - encryptcordGroupMembers[senderId] = { key: senderKey, parent: UserStore.getCurrentUser().id, child: null }; - encryptcordGroupMembers[UserStore.getCurrentUser().id].child = senderId; - await DataStore.set('encryptcordGroupMembers', encryptcordGroupMembers); - const groupChannel = await DataStore.get('encryptcordChannelId'); - const newMember = await UserUtils.getUser(senderId).catch(() => null); - if (!newMember) return; - - const membersData = {}; - Object.entries(encryptcordGroupMembers) - .forEach(([memberId, value]) => { - membersData[memberId] = value; - }); - - const membersDataString = JSON.stringify({ members: membersData, channel: groupChannel }); - - const dmPromises = Object.keys(encryptcordGroupMembers).map(async (memberId) => { - const groupMember = await UserUtils.getUser(memberId).catch(() => null); - if (!groupMember) return; - await sendTempMessage(groupMember.id, membersDataString, `groupdata`); - }); - - await Promise.all(dmPromises); - await MessageActions.receiveMessage(groupChannel, { - ...await createMessage("", senderId, groupChannel, 7), components: [{ - type: 1, - components: [{ - type: 2, - style: 4, - label: 'I don\'t want to talk to you!', - custom_id: 'removeFromSelf' - }, - { - type: 2, - style: 2, - label: '(Other users can still send/receive messages to/from them)', - disabled: true, - custom_id: 'encryptcord' - }] - }] - }); -} - -// Create message for group -async function createMessage(message: string, senderId: string, channelId: string, type: number) { - const messageStart = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); - const sender = await UserUtils.getUser(senderId).catch(() => null); - if (!sender) return; - return { ...messageStart, content: message, author: sender, type, flags: 0 }; -} - -// Start E2EE Group -async function startGroup(opts, ctx) { - const channelId = ctx.channel.id; - await DataStore.set('encryptcordChannelId', channelId); - await DataStore.set('encryptcordGroupMembers', { - [UserStore.getCurrentUser().id]: { key: await DataStore.get("encryptcordPublicKey"), parent: null, child: null } - }); - await DataStore.set('encryptcordGroup', true); - sendBotMessage(channelId, { content: "Group created!\n> Other users can click the lock icon to join." }); - await MessageActions.receiveMessage(channelId, await createMessage("", UserStore.getCurrentUser().id, channelId, 7)); - setEnabled(true); -} - -// Leave the Group; -async function leave(opts, ctx) { - const channelId = ctx.channel.id; - if (!(await DataStore.get('encryptcordGroup'))) { - sendBotMessage(channelId, { content: `You're not in a group!` }); - return; - } - const user = UserStore.getCurrentUser(); - const encryptcordGroupMembers = await DataStore.get('encryptcordGroupMembers'); - - const dmPromises = Object.keys(encryptcordGroupMembers).map(async (memberId) => { - const groupMember = await UserUtils.getUser(memberId).catch(() => null); - if (!groupMember) return; - await sendTempMessage(groupMember.id, "", `leaving`); - }); - - await Promise.all(dmPromises); - await DataStore.set('encryptcordGroup', false); - await DataStore.set('encryptcordChannelId', ""); - await DataStore.set('encryptcordGroupMembers', {}); - await MessageActions.receiveMessage(channelId, await createMessage("", user.id, channelId, 2)); - setEnabled(false); -} - -// View user data -async function data(opts, ctx) { - const channelId = ctx.channel.id; - const encryptcordGroupMembers = await DataStore.get('encryptcordGroupMembers'); - const encryptcordPublicKey = await DataStore.get('encryptcordPublicKey'); - const encryptcordPrivateKey = await DataStore.get('encryptcordPrivateKey'); - const exportedPrivateKey = await crypto.subtle.exportKey("pkcs8", encryptcordPrivateKey); - const groupMembers = Object.keys(encryptcordGroupMembers); - sendBotMessage(channelId, { - content: `## Public key:\n\`\`\`${encryptcordPublicKey}\`\`\`\n## Private key:\n||\`\`\`${formatPemKey(exportedPrivateKey, "private")}\`\`\`||*(DO **NOT** SHARE THIS)*\n## Group members:\n\`\`\`json\n${JSON.stringify(groupMembers)}\`\`\`` - }); -} - diff --git a/src/plugins/encryptcord/rsa-utils.tsx b/src/plugins/encryptcord/rsa-utils.tsx deleted file mode 100644 index 80e3ca35f..000000000 --- a/src/plugins/encryptcord/rsa-utils.tsx +++ /dev/null @@ -1,116 +0,0 @@ -export const generateKeys = async () => { - const keyPair = await crypto.subtle.generateKey( - { - name: "RSA-OAEP", - modulusLength: 4096, - publicExponent: new Uint8Array([1, 0, 1]), - hash: "SHA-256", - }, - true, - ["encrypt", "decrypt"] - ); - - const exportedPublicKey = await crypto.subtle.exportKey("spki", keyPair.publicKey); - const publicKey = formatPemKey(exportedPublicKey, "public"); - - return { privateKey: keyPair.privateKey, publicKey }; -}; - -export const encryptData = async (pemPublicKey, data) => { - const publicKey = await importPemPublicKey(pemPublicKey); - - const chunkSize = 446; - - const encryptedChunks: any[] = []; - const encoder = new TextEncoder(); - - for (let i = 0; i < data.length; i += chunkSize) { - const chunk = await data.substring(i, i + chunkSize); - const encryptedChunk = await crypto.subtle.encrypt( - { - name: "RSA-OAEP", - }, - publicKey, - encoder.encode(chunk) - ); - encryptedChunks.push(arrayBufferToBase64(encryptedChunk)); - } - - return encryptedChunks; -}; - -export const decryptData = async (privateKey, encArray) => { - const decryptionPromises = encArray.map(async (encStr) => { - const encBuffer = base64ToArrayBuffer(encStr); - - const dec = await crypto.subtle.decrypt( - { - name: "RSA-OAEP", - }, - privateKey, - encBuffer - ); - - return new TextDecoder().decode(dec); - }); - - const decryptedMessages = await Promise.all(decryptionPromises); - - return decryptedMessages.join(''); -}; - -// Helper functions -const arrayBufferToBase64 = (buffer) => { - const binary = String.fromCharCode(...new Uint8Array(buffer)); - return btoa(binary); -}; - -const base64ToArrayBuffer = (base64String) => { - const binaryString = atob(base64String); - const length = binaryString.length; - const buffer = new ArrayBuffer(length); - const view = new Uint8Array(buffer); - - for (let i = 0; i < length; i++) { - view[i] = binaryString.charCodeAt(i); - } - - return buffer; -}; - -export const formatPemKey = (keyData, type) => { - const base64Key = arrayBufferToBase64(keyData); - return `-----BEGIN ${type.toUpperCase()} KEY-----\n` + base64Key + `\n-----END ${type.toUpperCase()} KEY----- `; -}; - -const importPemPublicKey = async (pemKey) => { - try { - const trimmedPemKey = pemKey.trim(); - - const keyBody = trimmedPemKey - .replace("-----BEGIN PUBLIC KEY-----", "") - .replace("-----END PUBLIC KEY-----", ""); - - const binaryDer = atob(keyBody); - - const arrayBuffer = new Uint8Array(binaryDer.length); - for (let i = 0; i < binaryDer.length; i++) { - arrayBuffer[i] = binaryDer.charCodeAt(i); - } - - return await crypto.subtle.importKey( - "spki", - arrayBuffer, - { - name: "RSA-OAEP", - hash: { name: "SHA-256" }, - }, - true, - ["encrypt"] - ); - } catch (error) { - console.error("Error importing PEM public key:", error); - throw error; - } -}; -