🐛 fix: Refactor key generation logic and update dependencies for ML-KEM-768 integration
This commit is contained in:
parent
5ce5d62b22
commit
b8f630f8ab
@ -1,6 +1,10 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { generatePassword, generateRealityKeyPair, generateRealityShortId } from './generate';
|
import {
|
||||||
import { generateVlessX25519Pair } from './generate/mlkem768x25519plus';
|
generateMLKEM768KeyPair,
|
||||||
|
generatePassword,
|
||||||
|
generateRealityKeyPair,
|
||||||
|
generateRealityShortId,
|
||||||
|
} from './generate';
|
||||||
|
|
||||||
export const protocols = [
|
export const protocols = [
|
||||||
'shadowsocks',
|
'shadowsocks',
|
||||||
@ -700,10 +704,10 @@ export const PROTOCOL_FIELDS: Record<string, FieldConfig[]> = {
|
|||||||
placeholder: (t) => t('encryption_private_key_placeholder'),
|
placeholder: (t) => t('encryption_private_key_placeholder'),
|
||||||
group: 'encryption',
|
group: 'encryption',
|
||||||
generate: {
|
generate: {
|
||||||
function: () => generateVlessX25519Pair(),
|
function: generateMLKEM768KeyPair,
|
||||||
updateFields: {
|
updateFields: {
|
||||||
encryption_private_key: 'privateKeyB64',
|
encryption_private_key: 'privateKey',
|
||||||
encryption_password: 'passwordB64',
|
encryption_password: 'publicKey',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
condition: (p) => p.encryption === 'mlkem768x25519plus',
|
condition: (p) => p.encryption === 'mlkem768x25519plus',
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
export { generatePassword } from './random';
|
export { generateMLKEM768KeyPair } from './mlkem768';
|
||||||
export {
|
export { generateRealityShortId } from './short-id';
|
||||||
generateRealityKeyPair,
|
export { generatePassword } from './uid';
|
||||||
generateRealityShortId,
|
export { generateRealityKeyPair } from './x25519';
|
||||||
publicKeyFromPrivate,
|
|
||||||
} from './reality-key';
|
|
||||||
|
|||||||
34
apps/admin/app/dashboard/servers/generate/mlkem768.ts
Normal file
34
apps/admin/app/dashboard/servers/generate/mlkem768.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import mlkem from 'mlkem-wasm';
|
||||||
|
import { toB64Url } from './util';
|
||||||
|
|
||||||
|
export async function generateMLKEM768KeyPair() {
|
||||||
|
const mlkemKeyPair = await mlkem.generateKey({ name: 'ML-KEM-768' }, true, [
|
||||||
|
'encapsulateBits',
|
||||||
|
'decapsulateBits',
|
||||||
|
]);
|
||||||
|
const mlkemPublicKeyRaw = await mlkem.exportKey('raw-public', mlkemKeyPair.publicKey);
|
||||||
|
const mlkemPrivateKeyRaw = await mlkem.exportKey('raw-seed', mlkemKeyPair.privateKey);
|
||||||
|
|
||||||
|
return {
|
||||||
|
publicKey: toB64Url(new Uint8Array(mlkemPublicKeyRaw)),
|
||||||
|
privateKey: toB64Url(new Uint8Array(mlkemPrivateKeyRaw)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const test = await generateMLKEM768KeyPair();
|
||||||
|
|
||||||
|
console.log('生成的密钥信息:');
|
||||||
|
console.log('私钥长度:', test.privateKey.length);
|
||||||
|
console.log('公钥长度:', test.publicKey.length);
|
||||||
|
console.log(test);
|
||||||
|
|
||||||
|
// 从 VLESS 配置字符串中提取的密钥
|
||||||
|
const extractedKeys = {
|
||||||
|
decryptionKey:
|
||||||
|
'B2qLcDHhiztvBaB4BhMCnU-fM-axE4DZowMK9TIvL5qma2M5fVAmFswPdfej2N1SmlJa5ppidC1ksHLVfoEvjw',
|
||||||
|
encryptionKey:
|
||||||
|
'FgEI5sTIzBgZS4psemcdGDCRb5JvdDFqzOeVvLJYEmmZFSgxG1SkYxx8DUO-txGqvYdhFruja-ZzmIQoGOCQEMd0RbK4BhYRm4jE96GXTytu7Hi7pYo9-2SEuKC10righ2ceqis0LoggubajmAFvkop6igZfcmEA3MJ9aHpjGiUszDWy0pA99WY7c1ebTom8xQetW2J2OnkrA_UE8Cuy8AhMPvSBQrsFNmBqnttfVlmKzPS3RWBkIpAZIie1XMGk76nDEXgEsBulEhmYO2Mc-oRfirQfGmYfD1ybYBs9gAKgzQixTrkJtOuRU4GjkwQpxuyeVuww8Pin19IzAlVpUdeEJPVlRHwQDNJ__YsZDJB6rSIaY4tM4ysSs3N-1mmYvkVs1WSa0uy14KRheqMIPfsw_KxLBvp2_ZdZeQIrW3dDxgOuxXWip-g78desyONwwkx0bQK-JDcELHEH0TVmTOe4mSqW1fPI6jJI_ZChmQwBxZKpp2RN2xKmw6W3z4ETdUQDTZgePEkXDveneltNHrGT73WJQ7uEs6hwfXwD2vGcuFx6DKybfYAzgWh9t4IM3mBI7OiGqDItigqIDgaBF6LPTXwby3VxgfEVXXqXzMVdc0BL4da1BPGF-lAtvVJtbpBp7_O_JlB1wWajv8eLCpKjRiKGQey7oLZyRROQ3loNFyRDBjoBVSa2etCmFRCV1wegIBqmJRImJQxPsIV_kkIxeWmPfUIik1GpSTtrWkAFCXZAl4oSFiNhLwlmW_g_s_glGeqcZBNAkiw-OlssPESmqLti-bSJPbmoJMNE0poJXCYX27YyPKVyCtJKM5pzOkUBqMIVsxliU4N2IaWHb8uMYKW8U0tQ8aaeR3Bf5ll0fza78aY3lSQw7At21XkN9LhugzWv-_CPGZQpStGCWtJl5dChLPlmZbRddmekp7UEfXAPW6ONyNrFZ9WUvBCwsdmXcChTXwkg-FIMFHpU25c6IwWqyveuZrQmpvZYEQSgyWhsUBMPywI1vqiLfuhnaqBNU9wPbGA0IrG-w9UGpQErl9ssqPdZRjaIbiM-PKKooehp34QzU3ENj5h944gC4yHMkMzOPUaFl8YUWwmkGCsTNnJyq7NLucTOdQSUsLM2QWEkxWk9c6YyQkwx2mUsR1eGmrsbo8BGK6ppbgotpzMjGZfPOQRdHYh0lzGQ28HGetJ97Vei8Vxxl2u4j6CfHTPET4EHZ_uTuPtRIaaMegKOtUgyKqKj4BqsB5tatIECz8N1H_LP5qlRvUxYqrU-JikuRCoAdfl7VFYLLhe_80cny2UoWFpJ1iovprnALDbIIKZ03LG0OXI8dfEolQijO1xhwoUb7Ci3Xma-wKyEWHhSw0e600SRT0RhDuOh5hVg4KVLILUDDuMIJQyiPRBW3qwAMCk1SzCeXvFqbWFU-TQnVoLGNboaygQ6aumgP_B4DBcmEdyVqAMHIOdAPENvLauALKSiBHVjEHgeB7KpVCGsIGOjdCgb4UmKdsxnXBxC7peXspcmGgmHL-VU6KdMhwHwq1mYeNVEzeshb5mWYj5fgysp_e5U--geYKefs5Y',
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('提取的密钥信息:');
|
||||||
|
console.log('私钥长度:', extractedKeys.decryptionKey.length);
|
||||||
|
console.log('公钥长度:', extractedKeys.encryptionKey.length);
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import { x25519 } from '@noble/curves/ed25519';
|
|
||||||
|
|
||||||
const toB64Url = (u8: Uint8Array) =>
|
|
||||||
(typeof Buffer !== 'undefined'
|
|
||||||
? Buffer.from(u8).toString('base64')
|
|
||||||
: btoa(String.fromCharCode(...u8))
|
|
||||||
)
|
|
||||||
.replace(/\+/g, '-')
|
|
||||||
.replace(/\//g, '_')
|
|
||||||
.replace(/=+$/g, '');
|
|
||||||
|
|
||||||
export type VlessX25519Pair = {
|
|
||||||
passwordB64: string;
|
|
||||||
privateKeyB64: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function generateVlessX25519Pair(): VlessX25519Pair {
|
|
||||||
const { secretKey, publicKey } = x25519.keygen();
|
|
||||||
return {
|
|
||||||
passwordB64: toB64Url(publicKey),
|
|
||||||
privateKeyB64: toB64Url(secretKey),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import { x25519 } from '@noble/curves/ed25519.js';
|
|
||||||
|
|
||||||
function toB64Url(bytes: Uint8Array) {
|
|
||||||
return btoa(String.fromCharCode(...bytes))
|
|
||||||
.replace(/\+/g, '-')
|
|
||||||
.replace(/\//g, '_')
|
|
||||||
.replace(/=+$/g, '');
|
|
||||||
}
|
|
||||||
function fromB64Url(s: string) {
|
|
||||||
const b64 = s
|
|
||||||
.replace(/-/g, '+')
|
|
||||||
.replace(/_/g, '/')
|
|
||||||
.padEnd(Math.ceil(s.length / 4) * 4, '=');
|
|
||||||
const bin = atob(b64);
|
|
||||||
return new Uint8Array([...bin].map((c) => c.charCodeAt(0)));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Generate a Reality key pair
|
|
||||||
* @returns An object containing the private and public keys in base64url format
|
|
||||||
*/
|
|
||||||
export function generateRealityKeyPair() {
|
|
||||||
const { secretKey, publicKey } = x25519.keygen();
|
|
||||||
return { privateKey: toB64Url(secretKey), publicKey: toB64Url(publicKey) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derive public key from private key
|
|
||||||
* @param privateKeyB64Url Private key in base64url format
|
|
||||||
* @returns Public key in base64url format
|
|
||||||
*/
|
|
||||||
export function publicKeyFromPrivate(privateKeyB64Url: string) {
|
|
||||||
return toB64Url(x25519.getPublicKey(fromB64Url(privateKeyB64Url)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a short ID for Reality
|
|
||||||
* @returns A random hexadecimal string of length 2, 4, 6, 8, 10, 12, 14, or 16
|
|
||||||
*/
|
|
||||||
export function generateRealityShortId() {
|
|
||||||
const hex = '0123456789abcdef';
|
|
||||||
const lengths = [2, 4, 6, 8, 10, 12, 14, 16];
|
|
||||||
const idx = Math.floor(Math.random() * lengths.length);
|
|
||||||
const len = lengths[idx] ?? 16;
|
|
||||||
let out = '';
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
out += hex.charAt(Math.floor(Math.random() * hex.length));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
15
apps/admin/app/dashboard/servers/generate/short-id.ts
Normal file
15
apps/admin/app/dashboard/servers/generate/short-id.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Generate a short ID for Reality
|
||||||
|
* @returns A random hexadecimal string of length 2, 4, 6, 8, 10, 12, 14, or 16
|
||||||
|
*/
|
||||||
|
export function generateRealityShortId() {
|
||||||
|
const hex = '0123456789abcdef';
|
||||||
|
const lengths = [2, 4, 6, 8, 10, 12, 14, 16];
|
||||||
|
const idx = Math.floor(Math.random() * lengths.length);
|
||||||
|
const len = lengths[idx] ?? 16;
|
||||||
|
let out = '';
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
out += hex.charAt(Math.floor(Math.random() * hex.length));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
6
apps/admin/app/dashboard/servers/generate/util.ts
Normal file
6
apps/admin/app/dashboard/servers/generate/util.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export function toB64Url(bytes: Uint8Array) {
|
||||||
|
return btoa(String.fromCharCode(...bytes))
|
||||||
|
.replace(/\+/g, '-')
|
||||||
|
.replace(/\//g, '_')
|
||||||
|
.replace(/=+$/g, '');
|
||||||
|
}
|
||||||
11
apps/admin/app/dashboard/servers/generate/x25519.ts
Normal file
11
apps/admin/app/dashboard/servers/generate/x25519.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { x25519 } from '@noble/curves/ed25519';
|
||||||
|
import { toB64Url } from './util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a Reality key pair
|
||||||
|
* @returns An object containing the private and public keys in base64url format
|
||||||
|
*/
|
||||||
|
export function generateRealityKeyPair() {
|
||||||
|
const { secretKey, publicKey } = x25519.keygen();
|
||||||
|
return { privateKey: toB64Url(secretKey), publicKey: toB64Url(publicKey) };
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@
|
|||||||
"ahooks": "^3.9.4",
|
"ahooks": "^3.9.4",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"mlkem-wasm": "^0.0.6",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"next": "^15.5.2",
|
"next": "^15.5.2",
|
||||||
"next-intl": "^3.26.3",
|
"next-intl": "^3.26.3",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user