Ключ: датчик случайных чисел Валидаты инициализируется из мастера — человек водит мышью в браузере
На сервере без экрана окно Валидаты «Пожалуйста, двигайте мышью» никто не видел, ключ в тихом режиме падал с 0x80090022 (ИШ: «Не найден ключ ЭП (E0700009)»), пересборка справочника — тоже. bj-validata-rng: тихая проверка датчика (testcsp -silent -random), сеанс на виртуальном экране, движения из поля мастера повторяются в окне Валидаты (XTEST), прогресс — по полосе её окна. Установщик ключа проверяет датчик до записи на флешку. zpki.ini: Count = числу профилей, блоки подряд. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -14,8 +14,73 @@
|
||||
.kw-tile small{display:block;color:var(--muted);font-size:13px;line-height:1.4;margin-top:3px}
|
||||
.kw-panel{margin-top:16px}
|
||||
.kw-drive{display:flex;gap:10px;align-items:flex-start;padding:10px;border:1px solid var(--border,#ddd);border-radius:8px;cursor:pointer;margin:0}
|
||||
.kw-pad{height:200px;margin-top:12px;border:2px dashed var(--border);border-radius:12px;display:flex;align-items:center;justify-content:center;text-align:center;padding:0 16px;color:var(--muted);user-select:none;-webkit-user-select:none;touch-action:none;cursor:default}
|
||||
.kw-pad.on{border-color:var(--accent);background:var(--accent-weak);color:var(--accent);cursor:crosshair}
|
||||
.kw-bar{height:8px;margin-top:12px;border-radius:4px;background:var(--surface-2,#eee);overflow:hidden}
|
||||
.kw-bar i{display:block;height:100%;width:0;background:var(--accent);transition:width .3s}
|
||||
.kw-rng.ok .kw-bar i{background:var(--ok)}
|
||||
.kw-rng-row{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap;margin-top:10px}
|
||||
.kw-locked{opacity:.45;pointer-events:none;filter:grayscale(.3)}
|
||||
@media (max-width:600px){.kw-tiles{grid-template-columns:minmax(0,1fr)}}
|
||||
</style>
|
||||
{{/* ===== Датчик случайных чисел Валидаты: один раз для сервера, до первой работы с ключом (validatarng.go) ===== */}}
|
||||
{{if not $s.Done}}
|
||||
<div class="card kw-rng" id="kw-rng" hidden>
|
||||
<h2>Датчик случайных чисел Валидаты</h2>
|
||||
<p class="muted" style="margin:0">Перед первой работой с ключом СКЗИ Валидата набирает случайность от движений мыши — на рабочем месте она показывает окно «Пожалуйста, двигайте мышью». У сервера экрана нет: окно Валидаты открыто на сервере, а мышью водите вы — здесь, bj передаёт ваши движения в это окно. Делается один раз для сервера, около минуты.</p>
|
||||
<div class="kw-pad" id="kw-pad">Нажмите «Начать» и водите мышью по этому полю, пока полоса не заполнится</div>
|
||||
<div class="kw-bar"><i id="kw-rng-bar"></i></div>
|
||||
<div class="kw-rng-row"><span id="kw-rng-msg" class="muted">Датчик ещё не инициализирован — без этого ключ на сервере не работает.</span>
|
||||
<span style="display:flex;gap:8px"><button type="button" class="btn btn-secondary" id="kw-rng-stop" hidden>Остановить</button><button type="button" class="btn btn-ok" id="kw-rng-go">Начать</button></span></div>
|
||||
</div>
|
||||
<script>(function(){
|
||||
var card=document.getElementById('kw-rng'), pad=document.getElementById('kw-pad'),
|
||||
bar=document.getElementById('kw-rng-bar'), msg=document.getElementById('kw-rng-msg'), go=document.getElementById('kw-rng-go'), stop=document.getElementById('kw-rng-stop');
|
||||
var active=false, buf=[], lastT=0, sending=false, lastSent=0, shown=false, finished=false;
|
||||
var J={'Accept':'application/json'};
|
||||
function lock(on){ var m=document.getElementById('kw-main'); if(m) m.classList.toggle('kw-locked', on); }
|
||||
function pct(v){ return Math.round(Math.min(1,v||0)*100); }
|
||||
function show(v){
|
||||
if(finished) return;
|
||||
if(v.ready){ lock(false); active=false;
|
||||
if(shown||v.state==='done'){ card.hidden=false; card.classList.add('ok'); finished=true; bar.style.width='100%'; pad.hidden=true; go.hidden=true; stop.hidden=true;
|
||||
msg.textContent='✓ Датчик готов — теперь подключите ключ ниже. Повторять это не нужно.'; }
|
||||
return; }
|
||||
shown=true; card.hidden=false; lock(true);
|
||||
if(v.state==='running'||v.state==='starting'){
|
||||
active=true; pad.classList.add('on'); go.hidden=true; stop.hidden=false; bar.style.width=pct(v.progress)+'%';
|
||||
pad.textContent=v.window?'Водите мышью по полю — '+pct(v.progress)+' %':'Открываю окно Валидаты на сервере…';
|
||||
msg.textContent=v.window?'Ведите мышью без остановки, пока полоса не заполнится.':'Ещё пара секунд…';
|
||||
} else {
|
||||
active=false; pad.classList.remove('on'); go.hidden=false; stop.hidden=true; bar.style.width='0';
|
||||
pad.textContent='Нажмите «Начать» и водите мышью по этому полю, пока полоса не заполнится';
|
||||
go.textContent=(v.state==='failed'||v.state==='cancelled')?'Начать заново':'Начать';
|
||||
msg.textContent=v.error?('Не получилось: '+v.error):(v.ok===false?'Состояние датчика не проверено':'Датчик ещё не инициализирован — без этого ключ на сервере не работает.');
|
||||
}
|
||||
}
|
||||
function status(){ return fetch('/admin/setup/rng/status',{headers:J,cache:'no-store'}).then(function(r){return r.json()}).then(show).catch(function(){}); }
|
||||
go.addEventListener('click',function(){ go.disabled=true; msg.textContent='Открываю окно Валидаты на сервере…';
|
||||
fetch('/admin/setup/rng/start',{method:'POST',headers:J,cache:'no-store'}).then(function(r){return r.json()}).then(show)
|
||||
.catch(function(){ msg.textContent='Сервер не ответил — попробуйте ещё раз'; }).then(function(){ go.disabled=false; }); });
|
||||
stop.addEventListener('click',function(){ active=false; buf=[];
|
||||
fetch('/admin/setup/rng/cancel',{method:'POST',headers:J,cache:'no-store'}).then(function(r){return r.json()}).then(show).catch(function(){}); });
|
||||
pad.addEventListener('pointermove',function(e){ if(!active) return;
|
||||
var r=pad.getBoundingClientRect(), list=(e.getCoalescedEvents&&e.getCoalescedEvents().length)?e.getCoalescedEvents():[e];
|
||||
list.forEach(function(ev){ var x=Math.round((ev.clientX-r.left)/r.width*1000), y=Math.round((ev.clientY-r.top)/r.height*1000), t=ev.timeStamp;
|
||||
var dt=lastT?Math.round(t-lastT):0; lastT=t;
|
||||
buf.push([Math.max(0,Math.min(1000,x)),Math.max(0,Math.min(1000,y)),Math.max(0,Math.min(1000,dt))]); });
|
||||
});
|
||||
setInterval(function(){
|
||||
if(!active||sending) return;
|
||||
if(buf.length){ var m=buf.splice(0,1500); sending=true; lastSent=Date.now();
|
||||
fetch('/admin/setup/rng/moves',{method:'POST',headers:{'Accept':'application/json','Content-Type':'application/json'},body:JSON.stringify({m:m}),cache:'no-store'})
|
||||
.then(function(r){return r.json()}).then(show).catch(function(){}).then(function(){ sending=false; });
|
||||
} else if(Date.now()-lastSent>1000){ sending=true; lastSent=Date.now(); status().then(function(){ sending=false; }); }
|
||||
},250);
|
||||
document.addEventListener('DOMContentLoaded',status); if(document.readyState!=='loading') status();
|
||||
})();</script>
|
||||
{{end}}
|
||||
<div id="kw-main">
|
||||
{{/* ===== Лента шагов — когда путь выбран (флешка с ключом или новая флешка из архива) ===== */}}
|
||||
{{if or $s.StagingID $s.Done}}
|
||||
<div class="card">
|
||||
@@ -174,4 +239,5 @@
|
||||
r.forEach(function(x){ x.addEventListener('change', sync); }); sync();
|
||||
})();</script>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user