SHiNE-server/SHiNE-browser-plugin-wallet/js/lib/crypto-utils.js

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('');
}