mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
refactor: optimize random byte generation for recovery and JWT secret functions
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
const RECOVERY_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
const RECOVERY_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
|
const RECOVERY_ALPHABET_LENGTH = RECOVERY_ALPHABET.length;
|
||||||
|
const RECOVERY_MAX_UNBIASED_BYTE = Math.floor(256 / RECOVERY_ALPHABET_LENGTH) * RECOVERY_ALPHABET_LENGTH;
|
||||||
|
|
||||||
function normalizeRecoveryCode(raw: string): string {
|
function normalizeRecoveryCode(raw: string): string {
|
||||||
return String(raw || '').toUpperCase().replace(/[^A-Z2-7]/g, '');
|
return String(raw || '').toUpperCase().replace(/[^A-Z2-7]/g, '');
|
||||||
@@ -9,15 +11,14 @@ function formatRecoveryCode(compact: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createRecoveryCode(): string {
|
export function createRecoveryCode(): string {
|
||||||
const bytes = crypto.getRandomValues(new Uint8Array(20));
|
|
||||||
let compact = '';
|
let compact = '';
|
||||||
for (const b of bytes) {
|
|
||||||
compact += RECOVERY_ALPHABET[b % RECOVERY_ALPHABET.length];
|
|
||||||
}
|
|
||||||
// 20 bytes -> 20 chars in this simple mapping. Expand to 32 chars for friendlier grouping.
|
|
||||||
while (compact.length < 32) {
|
while (compact.length < 32) {
|
||||||
const extra = crypto.getRandomValues(new Uint8Array(1))[0];
|
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
||||||
compact += RECOVERY_ALPHABET[extra % RECOVERY_ALPHABET.length];
|
for (const b of bytes) {
|
||||||
|
if (b >= RECOVERY_MAX_UNBIASED_BYTE) continue;
|
||||||
|
compact += RECOVERY_ALPHABET[b % RECOVERY_ALPHABET_LENGTH];
|
||||||
|
if (compact.length >= 32) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return formatRecoveryCode(compact.slice(0, 32));
|
return formatRecoveryCode(compact.slice(0, 32));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,10 +74,15 @@ export default function JwtWarningPage(props: JwtWarningPageProps) {
|
|||||||
|
|
||||||
function generateJwtSecret(length: number): string {
|
function generateJwtSecret(length: number): string {
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
||||||
const bytes = crypto.getRandomValues(new Uint8Array(length));
|
|
||||||
let out = '';
|
let out = '';
|
||||||
for (let i = 0; i < length; i += 1) {
|
const maxUnbiasedByte = Math.floor(256 / chars.length) * chars.length;
|
||||||
out += chars[bytes[i] % chars.length];
|
while (out.length < length) {
|
||||||
|
const bytes = crypto.getRandomValues(new Uint8Array(length));
|
||||||
|
for (const value of bytes) {
|
||||||
|
if (value >= maxUnbiasedByte) continue;
|
||||||
|
out += chars[value % chars.length];
|
||||||
|
if (out.length >= length) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,16 @@ interface SettingsPageProps {
|
|||||||
|
|
||||||
function randomBase32Secret(length: number): string {
|
function randomBase32Secret(length: number): string {
|
||||||
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
const random = crypto.getRandomValues(new Uint8Array(length));
|
|
||||||
let out = '';
|
let out = '';
|
||||||
for (const x of random) out += alphabet[x % alphabet.length];
|
const maxUnbiasedByte = Math.floor(256 / alphabet.length) * alphabet.length;
|
||||||
|
while (out.length < length) {
|
||||||
|
const random = crypto.getRandomValues(new Uint8Array(length));
|
||||||
|
for (const x of random) {
|
||||||
|
if (x >= maxUnbiasedByte) continue;
|
||||||
|
out += alphabet[x % alphabet.length];
|
||||||
|
if (out.length >= length) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,12 +138,6 @@ function parseFieldType(value: number | string | null | undefined): CustomFieldT
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fieldTypeLabel(type: CustomFieldType): string {
|
|
||||||
if (type === 3) return t('txt_linked');
|
|
||||||
const found = FIELD_TYPE_OPTIONS.find((x) => x.value === type);
|
|
||||||
return found ? found.label : t('txt_text');
|
|
||||||
}
|
|
||||||
|
|
||||||
function toBooleanFieldValue(raw: string): boolean {
|
function toBooleanFieldValue(raw: string): boolean {
|
||||||
const v = String(raw || '').trim().toLowerCase();
|
const v = String(raw || '').trim().toLowerCase();
|
||||||
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
||||||
|
|||||||
@@ -112,10 +112,6 @@ function randomGuid(): string {
|
|||||||
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAesBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
||||||
return new Uint8Array(bytes).buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCipherKeyParts(cipher: Cipher, userEnc: Uint8Array, userMac: Uint8Array): Promise<{ enc: Uint8Array; mac: Uint8Array }> {
|
async function getCipherKeyParts(cipher: Cipher, userEnc: Uint8Array, userMac: Uint8Array): Promise<{ enc: Uint8Array; mac: Uint8Array }> {
|
||||||
if (cipher.key && typeof cipher.key === 'string') {
|
if (cipher.key && typeof cipher.key === 'string') {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user