fix: crypto in non-secure contexts

This commit is contained in:
overtrue
2025-03-30 14:00:05 +08:00
parent d37e5ca169
commit d8e7d959a3
4 changed files with 166 additions and 12 deletions
-12
View File
@@ -9,8 +9,6 @@ declare module 'vue' {
export interface GlobalComponents {
NA: typeof import('naive-ui')['NA']
NAlert: typeof import('naive-ui')['NAlert']
NAnchor: typeof import('naive-ui')['NAnchor']
NAnchorLink: typeof import('naive-ui')['NAnchorLink']
NAvatar: typeof import('naive-ui')['NAvatar']
NBadge: typeof import('naive-ui')['NBadge']
NButton: typeof import('naive-ui')['NButton']
@@ -26,11 +24,9 @@ declare module 'vue' {
NDescriptions: typeof import('naive-ui')['NDescriptions']
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDivider: typeof import('naive-ui')['NDivider']
NDrawer: typeof import('naive-ui')['NDrawer']
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
NDropdown: typeof import('naive-ui')['NDropdown']
NDynamicTags: typeof import('naive-ui')['NDynamicTags']
NEmpty: typeof import('naive-ui')['NEmpty']
NFlex: typeof import('naive-ui')['NFlex']
NForm: typeof import('naive-ui')['NForm']
@@ -38,17 +34,12 @@ declare module 'vue' {
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
NFormItemGridItem: typeof import('naive-ui')['NFormItemGridItem']
NGrid: typeof import('naive-ui')['NGrid']
NH1: typeof import('naive-ui')['NH1']
NH2: typeof import('naive-ui')['NH2']
NH3: typeof import('naive-ui')['NH3']
NH4: typeof import('naive-ui')['NH4']
NHr: typeof import('naive-ui')['NHr']
NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
NLayout: typeof import('naive-ui')['NLayout']
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NLi: typeof import('naive-ui')['NLi']
NList: typeof import('naive-ui')['NList']
@@ -57,12 +48,10 @@ declare module 'vue' {
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NOl: typeof import('naive-ui')['NOl']
NP: typeof import('naive-ui')['NP']
NPageHeader: typeof import('naive-ui')['NPageHeader']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NProgress: typeof import('naive-ui')['NProgress']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NSpin: typeof import('naive-ui')['NSpin']
@@ -73,7 +62,6 @@ declare module 'vue' {
NTag: typeof import('naive-ui')['NTag']
NText: typeof import('naive-ui')['NText']
NThing: typeof import('naive-ui')['NThing']
NUl: typeof import('naive-ui')['NUl']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
+1
View File
@@ -25,6 +25,7 @@
"json-editor-vue": "^0.17.3",
"lodash": "^4.17.21",
"rustfs": "^8.0.2",
"node-forge": "^1.3.1",
"nuxt": "^3.15.4",
"nuxtjs-naive-ui": "^1.0.2",
"pinia": "^2.3.0",
+1
View File
@@ -28,6 +28,7 @@
"json-editor-vue": "^0.17.3",
"lodash": "^4.17.21",
"rustfs": "^8.0.2",
"node-forge": "^1.3.1",
"nuxt": "^3.15.4",
"nuxtjs-naive-ui": "^1.0.2",
"pinia": "^2.3.0",
+164
View File
@@ -0,0 +1,164 @@
// @ts-nocheck
// This plugin imports the SubtleCrypto polyfill for browser compatibility
import forge from "node-forge";
// Polyfill for SubtleCrypto
// https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features/
// This is only needed for non-secure contexts (http://localhost:3000)
// and is used to provide a consistent API for cryptographic operations
// across different environments. It uses the forge library to implement
// the SubtleCrypto API in a way that is compatible with the Web Crypto API.
// The polyfill is loaded dynamically to avoid unnecessary overhead in secure contexts.
export default defineNuxtPlugin(() => {
if (!globalThis.crypto) {
globalThis.crypto = {} as Crypto;
}
const cryptoObj = globalThis.crypto;
if (!cryptoObj.subtle) {
cryptoObj.subtle = {
async digest(algorithm: string | Algorithm, data: BufferSource): Promise<ArrayBuffer> {
if (algorithm !== "SHA-256" && (algorithm as Algorithm).name !== "SHA-256") {
throw new Error("Only SHA-256 is supported.");
}
const md = forge.md.sha256.create();
md.update(forge.util.createBuffer(new Uint8Array(data as ArrayBuffer)).getBytes());
return new Uint8Array(md.digest().bytes().split("").map(c => c.charCodeAt(0))).buffer;
},
};
}
if (!cryptoObj.randomUUID) {
cryptoObj.randomUUID = function () {
const randomBytes = cryptoObj.getRandomValues(new Uint8Array(16));
randomBytes[6] = (randomBytes[6] & 0x0f) | 0x40; // UUID version 4
randomBytes[8] = (randomBytes[8] & 0x3f) | 0x80; // UUID variant 1
return [...randomBytes]
.map((b, i) =>
[4, 6, 8, 10].includes(i) ? `-${b.toString(16).padStart(2, "0")}` : b.toString(16).padStart(2, "0")
)
.join("");
};
}
if (!cryptoObj.getRandomValues) {
cryptoObj.getRandomValues = function (arr: Uint8Array): Uint8Array {
const randomBytes = forge.random.getBytesSync(arr.length);
for (let i = 0; i < arr.length; i++) {
arr[i] = randomBytes.charCodeAt(i);
}
return arr;
};
}
if (!cryptoObj.subtle.generateKey) {
cryptoObj.subtle.generateKey = async function (
algorithm: GenerateKeyParams,
extractable: boolean,
keyUsages: string[]
): Promise<CryptoKeyPair> {
if (algorithm.name !== "ECDSA" || algorithm.namedCurve !== "P-256") {
throw new Error("Only ECDSA with P-256 is supported.");
}
const keys = forge.pki.ec.generateKeyPair({ namedCurve: "P-256" });
return {
privateKey: keys.privateKey as CryptoKey,
publicKey: keys.publicKey as CryptoKey,
};
};
}
if (!cryptoObj.subtle.sign) {
cryptoObj.subtle.sign = async function (
algorithm: Algorithm,
key: CryptoKey,
data: BufferSource
): Promise<ArrayBuffer> {
if (algorithm.name !== "ECDSA" || algorithm.hash.name !== "SHA-256") {
throw new Error("Only ECDSA with SHA-256 is supported.");
}
const md = forge.md.sha256.create();
md.update(forge.util.createBuffer(new Uint8Array(data as ArrayBuffer)).getBytes());
const signature = forge.pki.ec.sign(md, key);
return new Uint8Array(signature.split("").map(c => c.charCodeAt(0))).buffer;
};
}
if (!cryptoObj.subtle.verify) {
cryptoObj.subtle.verify = async function (
algorithm: Algorithm,
key: CryptoKey,
signature: BufferSource,
data: BufferSource
): Promise<boolean> {
if (algorithm.name !== "ECDSA" || algorithm.hash.name !== "SHA-256") {
throw new Error("Only ECDSA with SHA-256 is supported.");
}
const md = forge.md.sha256.create();
md.update(forge.util.createBuffer(new Uint8Array(data as ArrayBuffer)).getBytes());
return forge.pki.ec.verify(md, key, signature);
};
}
if (!cryptoObj.subtle.exportKey) {
cryptoObj.subtle.exportKey = async function (
format: string,
key: CryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
if (format === "jwk") {
if ((key as any).type === "private") {
return forge.pki.privateKeyToJwk(key) as JsonWebKey;
} else if ((key as any).type === "public") {
return forge.pki.publicKeyToJwk(key) as JsonWebKey;
}
}
throw new Error("Unsupported export key format or type.");
};
}
if (!cryptoObj.subtle.importKey) {
cryptoObj.subtle.importKey = async function (
format: string,
keyData: JsonWebKey | ArrayBuffer,
algorithm: Algorithm,
extractable: boolean,
keyUsages: string[]
): Promise<CryptoKey> {
if (format === "jwk") {
if (keyData.type === "private") {
return forge.pki.jwkToPrivateKey(keyData) as CryptoKey;
} else if (keyData.type === "public") {
return forge.pki.jwkToPublicKey(keyData) as CryptoKey;
}
}
throw new Error("Unsupported import key format or type.");
};
}
if (!cryptoObj.subtle.encrypt) {
cryptoObj.subtle.encrypt = async function (
algorithm: Algorithm,
key: CryptoKey,
data: BufferSource
): Promise<ArrayBuffer> {
throw new Error("Encrypt not implemented");
};
}
if (!cryptoObj.subtle.decrypt) {
cryptoObj.subtle.decrypt = async function (
algorithm: Algorithm,
key: CryptoKey,
data: BufferSource
): Promise<ArrayBuffer> {
throw new Error("Decrypt not implemented");
};
}
return {
provide: {
crypto: cryptoObj,
},
};
})