Урок 6. Личные данные и счётчик попыток: карта проверяет ПИН
В картинкахПосле этого урока карта знает свой номер счёта и ПИН. Неверный ПИН — карта считает ошибки; три подряд — блокируется навсегда, и не помогают ни верный ПИН, ни «вынуть и вставить».
6.1. Идея: личность — в транзисторах
Программа у всех карт одна, а личность своя: номер счёта, ПИН и секретный ключ. Они лежат в матрице из урока 3: транзистор = бит 1. Строка pr — байт, столбец pb — бит. Вся «личность» карты 1 — 22 транзистора.
ПИН записан двоично-десятичным кодом: каждая цифра — 4 бита, 12 → 0001 0010.
6.2. Идея: попытки, которые не стираются
Два триггера try0, try1 считают неверные ПИН подряд. Сброс RST их не трогает — иначе «вынул и вставил» снова давало бы три попытки. Обнуляет только «заводская подготовка» — нога C6 (FAC): когда-то на неё подавали напряжение записи.
У настоящих карт ПИН можно сменить. Он лежит не в «прошитой» матрице, а в EEPROM — памяти, которую можно перезаписать и которая держит данные без питания. В банкомате вводишь старый ПИН и новый, банк присылает карте команду «замени ПИН», подписанную своим ключом, — и карта сама переписывает его. Так же банк снимает блокировку после трёх ошибок. Бывает и так, что ПИН проверяет не карта, а банк: тогда карта его вовсе не хранит. У нас ПИН прошит транзисторами, как у самых первых чиповых карт, — поменять его можно, только перепаяв матрицу (урок 8).
6.3. Сборка
Тот же проект, что в уроке 5.
И второе: карта сама считает неверные ПИН. Три ошибки подряд — заблокирована навсегда; ни сброс, ни верный ПИН не помогают. Это делает программа, а помнят — два триггера, которые сброс не трогает.
Шаг 1. Счётчик попыток и заводская подготовка
Два триггера try0, try1 считают неверные ПИН подряд: 0–3, на 3 карта заблокирована. Пишет их программа (ST 24), читает — LD 24. Сброс RST их не трогает — иначе «вынул и вставил» снова давало бы три попытки. Обнуляет только тумблер FAC — нога C6, «заводская подготовка»: когда-то на неё подавали напряжение записи. Тумблер FAC ставь справа, рядом с RST и переходником (снаружи будущего чипа).
| Деталь | Нога | Метка |
|---|---|---|
| Input (тумблер) | выход | FAC |
| NOT | I0 O0 | FAC nFAC |
| AND | I0 I1 O0 | oST selTRY wTRY |
| RBIT | D LD nR CLK Q | acc0 wTRY nFAC CLK try0 |
| RBIT | D LD nR CLK Q | acc1 wTRY nFAC CLK try1 |
Шаг 2. Личные данные: транзисторы матрицы
Карта 1: счёт 01, ПИН 1234, ключ 5A C3 17 8E. Транзистор = бит 1: затвор — строка pr<n> (байт по адресу 16 + n), сток — столбец pb<k> (бит k), исток — GND. Вся «личность» карты — эти 22 транзистора:
| Адрес | Что | Hex | Биты 7…0 | Транзисторы строки (столбцы) |
|---|---|---|---|---|
| 16 ($10) | номер счёта | 01 | 0000 0001 | pb0 |
| 17 ($11) | ПИН, цифры 1–2 | 12 | 0001 0010 | pb4, pb1 |
| 18 ($12) | ПИН, цифры 3–4 | 34 | 0011 0100 | pb5, pb4, pb2 |
| 19 ($13) | ключ, байт 1 | 5A | 0101 1010 | pb6, pb4, pb3, pb1 |
| 20 ($14) | ключ, байт 2 | C3 | 1100 0011 | pb7, pb6, pb1, pb0 |
| 21 ($15) | ключ, байт 3 | 17 | 0001 0111 | pb4, pb2, pb1, pb0 |
| 22 ($16) | ключ, байт 4 | 8E | 1000 1110 | pb7, pb3, pb2, pb1 |
ПИН записан двоично-десятичным кодом: каждая цифра — 4 бита (12 → 0001 0010).
| Деталь | Нога | Метка |
|---|---|---|
| nmos | затвор сток исток | pr0 pb0 GND |
| nmos | затвор сток исток | pr1 pb1 GND |
| nmos | затвор сток исток | pr1 pb4 GND |
| nmos | затвор сток исток | pr2 pb2 GND |
| nmos | затвор сток исток | pr2 pb4 GND |
| nmos | затвор сток исток | pr2 pb5 GND |
| nmos | затвор сток исток | pr3 pb1 GND |
| nmos | затвор сток исток | pr3 pb3 GND |
| nmos | затвор сток исток | pr3 pb4 GND |
| nmos | затвор сток исток | pr3 pb6 GND |
| nmos | затвор сток исток | pr4 pb0 GND |
| nmos | затвор сток исток | pr4 pb1 GND |
| nmos | затвор сток исток | pr4 pb6 GND |
| nmos | затвор сток исток | pr4 pb7 GND |
| nmos | затвор сток исток | pr5 pb0 GND |
| nmos | затвор сток исток | pr5 pb1 GND |
| nmos | затвор сток исток | pr5 pb2 GND |
| nmos | затвор сток исток | pr5 pb4 GND |
| nmos | затвор сток исток | pr6 pb1 GND |
| nmos | затвор сток исток | pr6 pb2 GND |
| nmos | затвор сток исток | pr6 pb3 GND |
| nmos | затвор сток исток | pr6 pb7 GND |
Шаг 3. Программа в ПЗУ
⚙ у ROM → кнопка ⬆ → файл 06-pin.rom.json — программа карты без шифра. После сброса карта сама шлёт приветствие $3B (59), дальше ждёт команду:
| Послать (тумблеры T) | Ответ (лампы) | Что |
|---|---|---|
| N (78) | номер счёта | команда «номер» |
| P (80), потом 2 байта ПИН | 144 ($90) 98 / 97 ($62 / $61) 96 ($60) | ПИН верный неверный, осталось 2 / 1 попытки карта заблокирована |
| другое | 109 ($6D) | нет такой команды |
; Урок 6 — карта проверяет ПИН: программа карты без шифра (вызов-ответ — урок 7).; Одна на все карты; личное — в постоянной памяти $10–$17.; Каждая команда — ровно один такт, поэтому длительность бита на линии IO считаем командами: P = 16 тактов.;; Разговор с терминалом:; сброс → $3B (приветствие, как первый байт ATR настоящих карт); 'N' → номер счёта; 'P' ПИН1 ПИН2 → $90 верно · $62/$61 неверно (осталось попыток) · $60 карта заблокирована; (урок 7) 'A' В1 В2 → О1 = первый байт шифра(вызов, ключ) — только после верного ПИН; иначе $69; (урок 7) 'R' → О2 = второй байт ответа (как GET RESPONSE настоящих карт: ответ длиннее байта —; терминал забирает остаток отдельной командой; на лампах переходника виден каждый байт); другое → $6D (нет такой команды) — в этом уроке и 'A', 'R' ; --- ОЗУ (8 байт) ---.equ B $00 ; байт: принимаемый / отправляемый.equ N $01 ; счётчик бит.equ D $02 ; счётчик задержки.equ H $03 ; первый байт аргумента / старший байт шифра.equ L $04 ; второй байт аргумента / младший байт шифра.equ OK $05 ; 1 = ПИН верен в этом сеансе; --- постоянная память (на транзисторах) ---.equ ACC $10 ; номер счёта.equ PINH $11 ; ПИН, цифры 1–2.equ PINL $12 ; ПИН, цифры 3–4.equ K0 $13 ; ключ, 4 байта.equ K1 $14.equ K2 $15.equ K3 $16; --- регистр попыток и порт ---.equ TRY $18 ; неверные попытки подряд (3 = заблокирована).equ IN $1C ; бит 0 = линия IO.equ OUT $1D ; бит 0 = 1 → прижать линию к 0 start: LD #0 ST OK ; после сброса ПИН снова не введён ST OUT ; линию отпустить LD #$3B ST B CALL tx ; приветствие loop: CALL rx ; команда LD B CMP #'N' JZ cmdN CMP #'P' JZ cmdP LD #$6D JMP reply cmdN: LD ACC JMP reply ; --- проверка ПИН ---cmdP: CALL rx LD B ST H CALL rx LD B ST L LD #0 ST OK ; новая попытка — прежний верный ПИН не в счёт LD TRY CMP #3 JZ blocked LD H CMP PINH JNZ wrong LD L CMP PINL JNZ wrong LD #0 ST TRY ; верно: счётчик попыток — в ноль LD #1 ST OK LD #$90 JMP replywrong: LD TRY ADD #1 ST TRY LD #3 SUB TRY ; сколько попыток осталось OR #$60 JMP replyblocked: LD #$60 JMP reply reply: ST B CALL tx JMP loop ; --- приём байта: ждать старт-бит, потом 8 бит старшим вперёд, по одному каждые 16 тактов ---rx: LD IN AND #1 JNZ rx ; линия 1 — ждём LD #8 ST N .rept 10 ; дойти до середины первого бита NOP .endrrxbit: NOP NOP NOP NOP NOP LD B ADD B ; B сдвинуть влево ST B LD IN ; ← здесь читаем бит AND #1 ADD B ST B LD N SUB #1 ST N JNZ rxbit ; петля — ровно 16 тактов LD #6 ST Drxg: LD D ; защита: переждать стоп-бит терминала (2 + 6 × 4 = 26 тактов) SUB #1 ST D JNZ rxg RET ; --- передача байта B: старт 0, 8 бит старшим вперёд, стоп 1 ---tx: LD #1 ST OUT ; старт-бит .rept 7 NOP .endr LD #8 ST Ntxbit: LD B ADD B ; старший бит → C ST B LD #0 ADC #0 ; A = C XOR #1 ; бит 1 → не прижимать, 0 → прижать ST OUT ; ← здесь бит выходит на линию NOP NOP NOP NOP NOP LD N SUB #1 ST N JNZ txbit ; петля — ровно 16 тактов NOP NOP NOP NOP NOP LD #0 ST OUT ; стоп-бит LD #8 ST Ddly: LD D ; защитный интервал: 2 + 8 × 4 = 34 такта SUB #1 ST D JNZ dly RET6.4. Проверка
Иди по строкам сверху вниз: одна строка — одно действие. После SEND жди, пока погаснет лампа «обмен» (lamp), и сравни с колонкой «Лампы». Команда P — это три посылки подряд: сама буква P и две половины ПИН.
| № | Что сделать | Лампы (● горит) | Что это |
|---|---|---|---|
| 1 | Clock — MAX (клик по корпусу) | ||
| 2 | включить FAC и RST, подождать секунду, выключить оба | ||
| 3 | ничего не трогать, смотреть на лампы | ○○●●●○●● 59 | карта поздоровалась: $3B |
| 4 | тумблеры T7…T0 = 0100 1110, нажать SEND | ○○○○○○○● 1 | N — номер счёта: 1 |
| 5 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 6 | тумблеры T7…T0 = 0001 0010, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 7 | тумблеры T7…T0 = 0011 0100, нажать SEND | ●○○●○○○○ 144 | P 12 34 — ПИН верный ($90) |
| 8 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 9 | тумблеры T7…T0 = 0001 0001, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 10 | тумблеры T7…T0 = 0001 0001, нажать SEND | ○●●○○○●○ 98 | P 11 11 — неверный, осталось 2 попытки ($62) |
| 11 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 12 | тумблеры T7…T0 = 0010 0010, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 13 | тумблеры T7…T0 = 0010 0010, нажать SEND | ○●●○○○○● 97 | P 22 22 — неверный, осталась 1 ($61) |
| 14 | RST включить и выключить («вынули и вставили карту») | ○○●●●○●● 59 | снова приветствие: карта перезапустилась |
| 15 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 16 | тумблеры T7…T0 = 0011 0011, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 17 | тумблеры T7…T0 = 0011 0011, нажать SEND | ○●●○○○○○ 96 | P 33 33 — третья ошибка: заблокирована ($60) |
| 18 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 19 | тумблеры T7…T0 = 0001 0010, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 20 | тумблеры T7…T0 = 0011 0100, нажать SEND | ○●●○○○○○ 96 | P 12 34 — даже верный ПИН: заблокирована |
| 21 | FAC включить и выключить («завод подготовил карту заново») | ||
| 22 | RST включить и выключить | ○○●●●○●● 59 | приветствие |
| 23 | тумблеры T7…T0 = 0101 0000, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 24 | тумблеры T7…T0 = 0001 0010, нажать SEND | ○○○○○○○○ гаснут | байт ушёл, карта ждёт следующий |
| 25 | тумблеры T7…T0 = 0011 0100, нажать SEND | ●○○●○○○○ 144 | P 12 34 — снова верно |
- Пока горит лампа «обмен», лампы
srмигают — смотреть надо, когда она погасла. - После буквы
Pи первой половины ПИН лампы гаснут: карта приняла байт и молча ждёт следующий. Ответ — только после третьей посылки. - Строки 17–20 — главное в уроке: после трёх ошибок карта не верит даже правильному ПИН, и сброс ей не помогает.
В чип не сворачивать — урок 8.
⚠ Частая ошибка. Пропущен один транзистор матрицы — в ключе. ПИН проверяется верно (ключ тут не нужен), а в уроке 7 карта отвечает на вызов не то. Сверь матрицу с таблицей по строкам.
Вставка урока 6 (Cmd+I)
Поверх урока 5.
# Урок 6 — личные данные карты (транзисторы матрицы: карта 1 — счёт 01, ПИН 1234, ключ 5A C3 17 8E)# и счётчик неверных ПИН (try0, try1; обнуляет только FAC — нога C6).# Вставка: Cmd+I поверх урока 5. Потом: ПЗУ — загрузить 06-pin.rom.json (⬆ в ROM). # ---------- Счётчик попыток и заводская подготовка ----------input(Out=FAC)NOT(FAC, nFAC)AND(oST, selTRY, wTRY)RBIT(D=acc0, LD=wTRY, nR=nFAC, CLK=CLK, Q=try0)RBIT(D=acc1, LD=wTRY, nR=nFAC, CLK=CLK, Q=try1) # ---------- Личные данные: транзисторы матрицы ----------nmos(pr0, pb0, GND)nmos(pr1, pb1, GND)nmos(pr1, pb4, GND)nmos(pr2, pb2, GND)nmos(pr2, pb4, GND)nmos(pr2, pb5, GND)nmos(pr3, pb1, GND)nmos(pr3, pb3, GND)nmos(pr3, pb4, GND)nmos(pr3, pb6, GND)nmos(pr4, pb0, GND)nmos(pr4, pb1, GND)nmos(pr4, pb6, GND)nmos(pr4, pb7, GND)nmos(pr5, pb0, GND)nmos(pr5, pb1, GND)nmos(pr5, pb2, GND)nmos(pr5, pb4, GND)nmos(pr6, pb1, GND)nmos(pr6, pb2, GND)nmos(pr6, pb3, GND)nmos(pr6, pb7, GND)Не получается? Что-то не работает →