Всем привет!
Традиционное вступление в стиле "плач Ярославны": GlobalPlatform, ISO 7816, JavaCard и прочие смежные стандарты - боль. Тонна материала написанная сухим языком так и навивает мысль, что авторы всего этого не инженеры, а юристы. Для тех немногих счастливчиков, кому не приходится иметь с этим дело, в качестве примера скажу, что каждый стандарт ETSI начинается со смысловых определений и толкований глаголов "shall", "shall not", "should", "should not" и т.д. Нет, в канцелярском стиле ничего плохого нет. Плохо становится когда он плавно переходит в поросячью латынь - и это одно из самых терпимых определений. Это же додуматься надо "размазать" требования к несчастной SMS-ске между пятью стандартами (ETSI 102-223/-225/-226 и 131-111/-115).
Ну вот ты преодолел пучины стандартов, затем засел за написание JavaCard-апплета, с чем тоже успешно справился, ну а дальше начинается квест "найди тулзы". Инструментарий от Oracle для сборки .cap-файлов недоступен из России, благо есть один удобный в открытом доступе. Там же рядышком лежит тулза для установки/удаления апплетов (да и вообще управления жизненным циклом карты).
Итак, ты скомпилировал и загрузил апплет на карту. Классно! А дальше? А дальше поговорим в статье.
Что есть на рынке?
Вообще, инструментов для взаимодействия с картой крайне мало. Из известных мне это JCShell от NXP Semiconductors и PCOM от Oberthur. Судя по синтаксису можно заключить, что авторы, как бы помягче выразиться, как никто другой пострадали от чтения отраслевых стандартов.
Вот скрипт на JCShell:
DEFUN External_authenticate_session @includeJCipherPlugin /send "0084 0000 08" *9000 /set-var RAND_IC ${last.response.data} /echo ${RAND_IC} /set-var data $(enc -m DES3/CBC -k ${KENC} -p NOPAD ${RAND_IC}) ## send EXT AUTH /send "0082 00${KIDX} #(${data})" *9000 END
А вот на PCOM:
;; Prepare data for EXT AUTH .SET_DATA %RAND_IFD %RAND_IC %K_IFD .SET_KEY %KENC .SET_VECT_INI 0000000000000000 .DES3CBC L FF .DISPLAY L ;;Compute MAC .SET_DATA L .SET_KEY %KMAC .SET_VECT_INI 0000000000000000 .MAC3 J 80 /P .DISPLAY J
Всякий раз как сложность сценария выходит за рамки "Status Word" читаемость скриптов скатывается на уровень регулярных выражений. К тому же оба инструмента проприетарные и в открытом доступе их не найти.
Попытка упростить себе жизнь.
За последние годы я четко определил видение своего будущего - стать миллионером! На худой конец освою python. Штош, как человек далекий от питона с удивлением обнаружил, насколько он удобен: операции со списками, функциями, объектами и прочим-прочим - все максимально абстрагировано в угоду быстрой разработки и читаемости кода (тут главное не увлекаться). Самым любознательным советую сравнить исходники FunGP и GlobalPlatformPro в части, отвечающей за установление защищенного соединения.
В общем и целом, результат получился следующим:
isd.mutual_auth()
Это всё, что нужно написать тестеру, чтобы осуществить процедуру MUTUAL AUTHENTICATE. Ладно, шучу - не всё, просто привел в качестве примера на контрасте с предыдущими монстрами. Но вот скрипт, который считывает данные с карты:
from fun_gp import SmartCard from fun_gp.utils import lv known_readers = ['ACS ACR39U ICC Reader 0', 'ACS ACR39U ICC Reader 00 00'] isd_default_keys = ["404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F"] isd = SmartCard(known_readers, isd_default_keys) isd.apdu_plain('00A4 0400' + lv('a000000151000000')) # isd.apdu_plain('80CA 2F00 02 5C00', name='GET DATA: list of applications') isd.apdu_plain('80CA 00E0 00', expected_sw=0x9000) isd.apdu_plain('80CA 0042 00', expected_sw=0x6A88, name='GET DATA: Issuer Identification Number')
Тут обращаем внимание на следующие элементы:
known_readers[] - узнать имя ридера можно разными путями, но самый простой - это запустить этот скрипт. Он рухнет, но перед этим выдаст список доступных ридеров, откуда и возьмете название своего:
Context established. Available PCSC readers: ACS ACR39U ICC Reader 0 >> 00A40400 08 A000000151000000 Traceback (most recent call last): --//--
isd_default_keys[] - святая-святых. Убедитесь, что они корректны.
SmartCard(known_readers, isd_default_keys)- обратите внимание, что переменная, хранящая указатель на экземпляр этого класса называется isd. На всякий случай уточню, что ISD (Issuer Security Domain) - это самый главный апплет на карте, который отвечает за установку/удаление других апплетов, а также обладает правом окирпичить вашу карту.
Банального общения с картой мне показалось недостаточным, потому-то проект перерос из тулзы для коммуникации по протоколу APDU в легковесный (пока) фреймворк, способный накатить апплет на карту:
from fun_gp import SmartCard from fun_gp.utils import lv known_readers = ['ACS ACR39U ICC Reader 0', 'ACS ACR39U ICC Reader 00 00'] isd_default_keys = ["404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F"] applets = [ "../../resources/SimpleApplet.cap", # 'A000000082' ] isd = SmartCard(known_readers, isd_default_keys) isd.apdu_plain('00A4 0400' + lv('a000000151000000'), expected_sw=0x9000, name='SELECT: isd') isd.mutual_auth() for cap in applets: isd.install_app_scp02(cap)
Тулза GlobalPlatformPro (да и беспокойные JCShell с PCOM'ом) требует явного указания AID пакета и AID апплета. Я решил поручить эту грязную работенку методу install_app_scp02(), который извлекает эту инфу из .cap-файла. Важное уточнение - после вызова метода isd.mutual_auth() вся последующая коммуникация с картой через методы, оканчивающиеся на scp02(): apdu_scp02(), install_app_scp02() и т.д.
Удалить апплет можно так:
from fun_gp import SmartCard from fun_gp.utils import lv known_readers = ['ACS ACR39U ICC Reader 0', 'ACS ACR39U ICC Reader 00 00'] isd_default_keys = ["404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F", "404142434445464748494A4B4C4D4E4F"] packages = [ 'A000000082', # 'SimpleApplet.cap' ] isd = SmartCard(known_readers, isd_default_keys) isd.apdu_plain('00A4 0400' + lv('a000000151000000'), expected_sw=0x9000, name='SELECT: isd') isd.mutual_auth() for aid in packages: isd.uninstall_app_scp02(package_aid=aid)
Надеюсь, те немногие, кто занят в области разработки под смарт-карты дадут обратную связь. Отдельное обращение к python-лордам и миледи: "не стреляйте в пианиста, он играет как умеет".
Всем пока!
vilgeforce
Эх, еще бы все магические байтики APDU спрятать за нормальными функциями... Но все равно круто, запишу к себе ссылочку на пост
void-deref Автор
Ой, не знаю... такое неблагодарное дело под пять параметров (заголовок APDU) писать функции или константы. Сколько раз ни видел такое решение, сколько раз сам ни пытался такое сделать, все заканчивалось избыточной раздутостью и "кристаллизацией" кода, который так и норовил где-нибудь упасть.
p.s. спасибо за оценку!
VelocidadAbsurda
Навскидку вспоминается модуль pygp (опубликован на pypy), в котором основные операции как раз уже обёрнуты в отдельные функции.