Урок 6. Личные данные и счётчик попыток: карта проверяет ПИН

В картинках

После этого урока карта знает свой номер счёта и ПИН. Неверный ПИН — карта считает ошибки; три подряд — блокируется навсегда, и не помогают ни верный ПИН, ни «вынуть и вставить».


6.1. Идея: личность — в транзисторах

Программа у всех карт одна, а личность своя: номер счёта, ПИН и секретный ключ. Они лежат в матрице из урока 3: транзистор = бит 1. Строка pr — байт, столбец pb — бит. Вся «личность» карты 1 — 22 транзистора.

ПИН записан двоично-десятичным кодом: каждая цифра — 4 бита, 12 → 0001 0010.

VCCбитpb0pb1pb2pb3pb4pb5pb6pb7pr0счёт01pr1ПИН 1–212pr2ПИН 3–434pr3ключ 15Apr4ключ 2C3pr5ключ 317pr6ключ 48Eпунктир — GND0pq01pq10pq20pq31pq40pq50pq60pq7= 12
Личность карты 1 — 22 транзистора. Сверху резисторы держат столбцы в 1, в каждой строке-байте транзистор стоит там, где бит 1, снизу NOT. Читается строка pr1 (адрес 17, первые цифры ПИН): её транзисторы прижали pb4 и pb1 — на выходе 0001 0010 = «12».

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
NOTI0
O0
FAC
nFAC
ANDI0
I1
O0
oST
selTRY
wTRY
RBITD
LD
nR
CLK
Q
acc0
wTRY
nFAC
CLK
try0
RBITD
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)номер счёта010000 0001pb0
17 ($11)ПИН, цифры 1–2120001 0010pb4, pb1
18 ($12)ПИН, цифры 3–4340011 0100pb5, pb4, pb2
19 ($13)ключ, байт 15A0101 1010pb6, pb4, pb3, pb1
20 ($14)ключ, байт 2C31100 0011pb7, pb6, pb1, pb0
21 ($15)ключ, байт 3170001 0111pb4, pb2, pb1, pb0
22 ($16)ключ, байт 48E1000 1110pb7, 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        RET

6.4. Проверка

Иди по строкам сверху вниз: одна строка — одно действие. После SEND жди, пока погаснет лампа «обмен» (lamp), и сравни с колонкой «Лампы». Команда P — это три посылки подряд: сама буква P и две половины ПИН.

№Что сделатьЛампы (● горит)Что это
1Clock — MAX (клик по корпусу)
2включить FAC и RST, подождать секунду, выключить оба
3ничего не трогать, смотреть на лампы○○●●●○●● 59карта поздоровалась: $3B
4тумблеры T7…T0 = 0100 1110, нажать SEND○○○○○○○● 1N — номер счёта: 1
5тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
6тумблеры T7…T0 = 0001 0010, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
7тумблеры T7…T0 = 0011 0100, нажать SEND●○○●○○○○ 144P 12 34 — ПИН верный ($90)
8тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
9тумблеры T7…T0 = 0001 0001, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
10тумблеры T7…T0 = 0001 0001, нажать SEND○●●○○○●○ 98P 11 11 — неверный, осталось 2 попытки ($62)
11тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
12тумблеры T7…T0 = 0010 0010, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
13тумблеры T7…T0 = 0010 0010, нажать SEND○●●○○○○● 97P 22 22 — неверный, осталась 1 ($61)
14RST включить и выключить («вынули и вставили карту»)○○●●●○●● 59снова приветствие: карта перезапустилась
15тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
16тумблеры T7…T0 = 0011 0011, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
17тумблеры T7…T0 = 0011 0011, нажать SEND○●●○○○○○ 96P 33 33 — третья ошибка: заблокирована ($60)
18тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
19тумблеры T7…T0 = 0001 0010, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
20тумблеры T7…T0 = 0011 0100, нажать SEND○●●○○○○○ 96P 12 34 — даже верный ПИН: заблокирована
21FAC включить и выключить («завод подготовил карту заново»)
22RST включить и выключить○○●●●○●● 59приветствие
23тумблеры T7…T0 = 0101 0000, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
24тумблеры T7…T0 = 0001 0010, нажать SEND○○○○○○○○ гаснутбайт ушёл, карта ждёт следующий
25тумблеры T7…T0 = 0011 0100, нажать SEND●○○●○○○○ 144P 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)

Не получается? Что-то не работает →