import qrcode from '../vendor-qrcode-generator.js'; const TRANSFER_PREFIX = 'shine-key-transfer-v1:'; const encoder = new TextEncoder(); const decoder = new TextDecoder(); function bytesToBase64Url(bytes) { let binary = ''; bytes.forEach((b) => { binary += String.fromCharCode(b); }); return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); } function base64UrlToBytes(value) { const normalized = String(value || '').trim().replace(/-/g, '+').replace(/_/g, '/'); const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4); const binary = atob(padded); const out = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i); return out; } export function keyLabel(id) { if (id === 'root') return 'root'; if (id === 'blockchain') return 'blockchain'; if (id === 'device') return 'device'; return id; } export function describeTransferKeys(keys = {}) { const out = []; if (keys.clientKey) out.push('device'); if (keys.blockchainKey) out.push('blockchain'); if (keys.rootKey) out.push('root'); return out; } export function makeKeyTransferText({ login, keys }) { const payload = { v: 1, type: 'shine-key-transfer', login: String(login || '').trim(), keys: { clientKey: String(keys?.clientKey || ''), blockchainKey: String(keys?.blockchainKey || ''), rootKey: String(keys?.rootKey || ''), }, createdAtMs: Date.now(), }; const json = JSON.stringify(payload); return `${TRANSFER_PREFIX}${bytesToBase64Url(encoder.encode(json))}`; } export function parseKeyTransferText(text) { const raw = String(text || '').trim(); if (!raw.startsWith(TRANSFER_PREFIX)) { throw new Error('Это не QR-код переноса ключей SHiNE'); } const json = decoder.decode(base64UrlToBytes(raw.slice(TRANSFER_PREFIX.length))); const payload = JSON.parse(json); if (payload?.v !== 1 || payload?.type !== 'shine-key-transfer') { throw new Error('Неподдерживаемый формат QR-кода'); } const login = String(payload.login || '').trim(); if (!login) throw new Error('В QR-коде нет логина'); const keys = payload.keys && typeof payload.keys === 'object' ? payload.keys : {}; if (!keys.clientKey && !keys.blockchainKey && !keys.rootKey) { throw new Error('В QR-коде нет ключей'); } return { login, keys: { clientKey: String(keys.clientKey || ''), blockchainKey: String(keys.blockchainKey || ''), rootKey: String(keys.rootKey || ''), }, keyTypes: describeTransferKeys(keys), }; } export function renderQrSvg(text, { cellSize = 4, margin = 4 } = {}) { const qr = qrcode(0, 'L'); qr.addData(String(text || ''), 'Byte'); qr.make(); return qr.createSvgTag(cellSize, margin); }