SHiNE-server/shine-UI/js/services/qr-key-transfer-service.js

88 lines
2.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.deviceKey) 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: {
deviceKey: String(keys?.deviceKey || ''),
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.deviceKey && !keys.blockchainKey && !keys.rootKey) {
throw new Error('В QR-коде нет ключей');
}
return {
login,
keys: {
deviceKey: String(keys.deviceKey || ''),
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);
}