79 lines
2.3 KiB
JavaScript
79 lines
2.3 KiB
JavaScript
function getCryptoApi() {
|
|
const api = globalThis.crypto;
|
|
if (!api?.subtle || typeof api.getRandomValues !== 'function') {
|
|
throw new Error('WebCrypto недоступен в текущем браузере.');
|
|
}
|
|
return api;
|
|
}
|
|
|
|
function getSubtleApi() {
|
|
return getCryptoApi().subtle;
|
|
}
|
|
|
|
function base64UrlToBase64(value) {
|
|
const normalized = String(value || '').trim().replace(/-/g, '+').replace(/_/g, '/');
|
|
return normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
|
|
}
|
|
|
|
export function utf8Bytes(value) {
|
|
return new TextEncoder().encode(String(value ?? ''));
|
|
}
|
|
|
|
export function bytesToBase64(bytes) {
|
|
let binary = '';
|
|
const chunk = 0x8000;
|
|
for (let i = 0; i < bytes.length; i += chunk) {
|
|
const slice = bytes.subarray(i, i + chunk);
|
|
binary += String.fromCharCode(...slice);
|
|
}
|
|
return btoa(binary);
|
|
}
|
|
|
|
export function base64ToBytes(value) {
|
|
const binary = atob(base64UrlToBase64(value));
|
|
const out = new Uint8Array(binary.length);
|
|
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
|
|
return out;
|
|
}
|
|
|
|
export async function generateEd25519Pair() {
|
|
return getSubtleApi().generateKey({ name: 'Ed25519' }, true, ['sign', 'verify']);
|
|
}
|
|
|
|
export async function exportEd25519PublicKeyB64(publicKey) {
|
|
const raw = await getSubtleApi().exportKey('raw', publicKey);
|
|
return bytesToBase64(new Uint8Array(raw));
|
|
}
|
|
|
|
export async function exportPkcs8B64(privateKey) {
|
|
const raw = await getSubtleApi().exportKey('pkcs8', privateKey);
|
|
return bytesToBase64(new Uint8Array(raw));
|
|
}
|
|
|
|
export async function importPkcs8Ed25519(pkcs8B64) {
|
|
return getSubtleApi().importKey('pkcs8', base64ToBytes(pkcs8B64), { name: 'Ed25519' }, false, ['sign']);
|
|
}
|
|
|
|
export async function signBase64(privateKey, text) {
|
|
const signature = await getSubtleApi().sign({ name: 'Ed25519' }, privateKey, utf8Bytes(text));
|
|
return bytesToBase64(new Uint8Array(signature));
|
|
}
|
|
|
|
export async function sha256Bytes(bytes) {
|
|
const digest = await getSubtleApi().digest('SHA-256', bytes);
|
|
return new Uint8Array(digest);
|
|
}
|
|
|
|
export async function sha256Text(text) {
|
|
return sha256Bytes(utf8Bytes(text));
|
|
}
|
|
|
|
export function randomBase64(size) {
|
|
const bytes = getCryptoApi().getRandomValues(new Uint8Array(size));
|
|
return bytesToBase64(bytes);
|
|
}
|
|
|
|
export function bytesToHex(bytes) {
|
|
return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
}
|