Что такое nfc?
Как многие наверняка знают, NFC — технология высокочастотной беспроводной связи с малым (несколько сантиметров, но не больше 10 см) радиусом действия. В основе Near Field Communication (обмена на ближнем расстоянии) лежит радиочастотная идентификация (RFID) — данные, которые хранятся в транспондерах, считываются и записываются при помощи радиосигналов. Активные и пассивные объекты, в свою очередь, идентифицируются автоматически.
Но стандартного определения здесь явно недостаточно. История NFC довольно интересная: технология берет свое начало более 12 лет назад — именно тогда три технологических гиганта (Nokia, Sony и NXP Semiconductor) решили создать форум NFC для развития интерфейса касательного взаимодействия между устройствами.
Несмотря на то, что сам интерфейс был далеко не быстрым (и остается таким по сей день), идея передачи небольших данных с низким энергопотреблением все же взяла свое. RFID заработала по-новому и получила массовое признание. В результате многие производители смартфонов, аксессуаров и других устройств сейчас не обделяют NFC ни один свой новый продукт.
Рабочей частотой для NFC является 13,56 МГц, максимальная скорость обмена данными едва превышает 400 Кбит/с. Однако в данном случае огромную роль играет время установления соединения: чтобы подключить два устройства при помощи этой технологии, понадобится менее десятой доли секунды.
Принцип работы NFC основан на электромагнитной индукции. Суть в том, что при помощи антенны передатчик считывателя постоянно излучает сигнал в форме синусоиды на вышеупомянутой чистоте.
Датчик (или Listening Device) также оснащен рамочной антенной. Когда датчик и считыватель (Polling Device) оказываются на расстоянии, достаточном для работы NFC, и та, и другая катушка образуют воздушный трансформатор. Магнитное поле порождается переменным током в катушке считывателя, после этого ток создается во второй катушке — датчика. Этой энергии запросто хватает для работы последнего, поэтому NFC способна работать с пассивными устройствами.
В это время происходит шунтирование антенны одним из транзисторов устройства-передатчика, откуда возникает модуляция высокочастотного сигнала. Этот сигнал и «ловит» считыватель. В основном для NFC используется Манчестерское кодирование (с коэффициентом амплитудной модуляции 10 %). Также используется модифицированный код Миллера, правда в этом случае скорость едва будет превышать 100 Кбит/с.
В пассивном режиме считыватель создает электромагнитное поле, NFC-метка модулирует его и формирует ответ. Иными словами, метке вовсе не обязательно быть подключенной к источнику питания или иметь встроенный аккумулятор, поэтому ее размеры можно сократить до минимума. Если же мы имеем два устройства с двумя активными сигналами, здесь все проще — они работают, грубо говоря, «по очереди».
Пассивная NFC-метка выглядит примерно так:
В смартфонах антенну NFC, как правило, закрепляют под задней крышкой для более устойчивого сигнала.
После этого ваш смартфон готов выступать сразу в нескольких ипостасях. Он может стать не только платежным средством и ключом, но и также средством идентификации владельца, проездным билетом или просто бонусной картой. Вот основные режимы работы смартфона с NFC на борту.
- Пиринг — два активных устройства связываются между собой и обмениваются данными. Это могут быть как два смартфона, так и смартфон и сторонний аксессуар. Например, можно быстро установить соединение между телефоном и внешней колонкой или наушниками. Главное, чтобы у девайса была заявлена поддержка NFC: среди таких устройств KR — 8100, Dacom Athlete, Bluedio R Legend и другие.
- Чтение и запись — смартфон как считывает коды с NFC-метки, так и записывает информацию в ее память.
- Эмуляция карты — смартфон становится полноценной банковской картой. Достаточно поднести его к терминалу, чтобы совершить оплату, причем в качестве карты могут также выступать, например, умные часы.
Что такое emv карта?
EMV — это международный стандарт для банковских карт с чипом. В разработке этого стандарта принимали участия
E
uropay
M
asterCard
V
ISA, отсюда и название. Попробуем разобраться, как же все таки карта общается с POS-терминалом по бесконтактному интерфейсу.
Начнем с самых основ.
Бесконтактная EMV карта на физическом уровне работает почти так же, как и RFID метка. Если базисно то, чип попадает в электромагнитное поле, а в замкнутом проводящем контуре (в нашем случае это будет антенна, расположенная по периметру), помещенном в переменное магнитное поле, образуется переменный электрический ток.
Этот ток заряжает специальный конденсатор, подключенный параллельно к резонансному контуру карты. Энергия, запасенная в конденсаторе, используется для выполнения микросхемой карты различных операций. Когда ридер изменяет электромагнитное поле, изменения сразу будут заметны на чипе.
Используя модуляцию сигнала, мы можем передавать информацию в бинарном виде. Если на карте подключить нагрузочное сопротивление и или изменить емкость конденсатора, то можно изменить силу тока в контуре карты, что приведет к изменению создаваемого им электромагнитного поля в области контура ридера, таким образом карточка передает данные.
Сам чип карты представляет собой смарт карту, на которой работает JavaCard, отдельная версия Java для платформ с малыми вычислительными ресурсами и поддержкой криптографических алгоритмов. На JavaCard загружаются апплеты, которые, и являются приложениями.
Также существует GlobalPlatform это некий стандарт для JavaCard, который предоставляет возможность безопасного управления данными на карте и позволяет загружать, изменять и удалять приложения на карте. В этой статье механизмы безопасности самой смарт карты мы рассматривать не будем.
Также еще напомню немного терминологии, для тех, кто не знаком.
POS-терминал (Point of Sale) — устройство продавца, которое считывает карту и инициирует платеж. Далее будем называть это устройство просто терминалом. Банк эмитент — это банк, который выпустил вашу карту.Банк эквайер — банк, который выдает продавцам POS-терминалы и обрабатывает платежи с них.
Платежная система — центральное звено между банком эквайером и банком эмитентом, через нее проходят абсолютно все платежи, и она знает какой банк какому сколько должен перевести денег. Платежных систем в мире не мало, кроме всем известных Visa и MasterCard есть ещё и American Express, China UnionPay и российская платежная система МИР.
Хорошо, карта и ридер могут общаться. Они посылают друг другу APDU-команды в виде Tag-Length-Value т.е. передается название тэга в шестнадцатеричном виде, его длина и само значение. Все команды описаны конечно же в документации и выглядят примерно так:
Стандартная EMV транзакция проходит в несколько этапов, я опишу полный алгоритм взаимодействия в случае контактного интерфейса, для бесконтактного интерфейса алгоритм несколько укорочен:
Коротко рассмотрим каждую операцию.
Выбор приложения. Часто бывает, что на одной карте может быть несколько приложений. Например, банковская карта и проездной билет. И терминалу как-то необходимо разобраться, где и какой алгоритм ему использовать. Для выбора приложения используются так называемые Идентификационные Коды приложения (Application Identifier – AID).
Что бы в этом разобраться терминал посылает команду SELECT. Например, AID карты Visa Classic будет выглядеть следующим образом: A0000000031010. Если в ответ придет несколько таких кодов и терминал умеет работать с несколькими приложениями, то терминал выведет на экран список и предложит выбрать нужное нам приложение. Если терминал не поддерживает ни один из кодов приложений, то операция будет отклонена терминалом.
Инициализация обработки приложения. Здесь сначала проверяется географическое место пребывания. Например, карты Maestro Momentum могут работать для оплаты только в России. Этот этап сделан для того, чтобы предоставить эмитентам возможность применять существующие онлайн методы риск-менеджмента при проведении офлайн операций.
На этом этапе EMV-транзакция может быть отменена по инициативе самой карты, если данный тип операции запрещен в данной стране мира эмитентом. Далее карта передает терминалу набор специально структурированной информации, содержащей описание функциональности карты и приложения.
Считывание данных приложения. Терминалу передаются различные данные карты необходимые для транзакции, например номер карты, expiration date, счетчик транзакций и много других данных. О некоторых из них будет сказано далее.
Пример данных:
Также передается сертификат публичного ключа банка эмитента и самой карты. Для того чтобы терминал был способен проверить цифровую подпись некоторых данных карты используется PKI-инфраструктура (Public Key Infrastructure). Вкратце, у платежной системы есть пара ключей — публичный и приватный и платежная система является для всех участников CA (Center Authority).
По сути платежная система для каждого банка эмитента выпускает новую пару ключей, и при этом формирует сертификат публичного ключа банка эмитента, подписывая его приватным ключом CA. Далее, когда банк выпускает новую карту, он соответственно генерирует для карточки пару ключей, и также формирует сертификат публичного ключа карты, подписывая его с помощью приватного ключа банка.
В терминалах обычно зашит сертификат публичного ключа для различных платежных систем. Таким образом, когда карточка передает сертификат публичного ключа банка эмитента и сертификат самой карты, терминал может с легкостью проверить всю цепочку, используя публичный ключ платежной системы.
Терминал с помощью публичного ключа платежной системы сначала проверяет подлинность сертификата банка эмитента, если он подлинный, то значит ему можно доверять и теперь с помощью сертификата банка эмитента можно проверить сертификат самой карты. Более подробней в статье про безопасность EMV .
Офлайн аутентификация. Терминал определяет тип поддерживаемого метода оффлайн аутентификации. Существует статичная (Static Data Authentication – SDA), динамическая (Dynamic Data Authentication – DDA) и комбинированная (Combined Data Authentication – CDA).
Эти методы также построены на основе PKI. SDA это просто подписанные данные на приватном ключе банка эмитента, DDA — терминал посылает какое-то случайное число и карточка должна подписать его, используя свой приватный ключ, а терминал проверит эту подпись используя полученный ранее сертификат карты, таким образом терминал удостовериться в том, что карточка и правда обладает приватным ключом — следовательно является подлинной. CDA это просто комбинация обоих способов.
Обработка ограничений. Здесь терминал проверяет полученные ранее данные с карты на условие пригодности для данной операции. Например, проверяет срок начала/окончания действия приложения Application Expiration Date (Tag ‘5F24’) и Application Effective Date (Tag ‘5F25’).
Также производится проверка версии приложения. Результаты операций, проводимых на данном этапе, также записываются в отчет TVR (Terminal verification results). По результатам этого этапа транзакция не может быть отменена, даже в случае, если, например, срок действия приложения истек.
Проверка держателя карты. Верификация держателя карты производится для того, чтобы аутентифицировать человека, предоставившего карту и проверить, является ли он подлинным владельцем карты. Стандарт EMV предоставляет различные методы верификации держателя карты (Cardholder Verification Method).
Список поддерживаемых методов верификации:
Вот
также есть интересная информация на эту тему.
Риск-менеджмент на стороне терминала. На этом этапе терминал проводит внутреннюю проверку параметров транзакции, исходя из установок риск-менеджмента банка-эквайера. Процедуры риск-менеджмента могут быть выполнены терминалом в любое время между моментами завершения процесса чтения данных карты и формирования терминалом первой команды GENERATE AC. Риск-менеджмент на стороне терминала включает в себя три механизма:
Анализ действий терминала. На этом этапе терминал анализирует результаты предыдущих шагов транзакции. По результатам анализа терминал принимает решение о том, следует ли провести операцию в online-режиме, разрешить ее проведение в офлайн режиме или отклонить операцию.
Риск-менеджмент на стороне карты. Карта, получив из команды GENERATE AC данные, касающиеся транзакции, терминала и результатов проверок терминала, в свою очередь выполняет собственные процедуры управления рисками и выносит собственное решение о способе завершения операции.
Анализ действий карты. На этом этапе карта завершает проведение процедур риск-менеджмента и формирует ответную криптограмму терминалу. Если карта решает одобрить транзакцию, то формируется Transaction Certificate. Если карта принимает решение о выполнение операции в режиме реального времени, то она формирует ARQC (Authorization Request Cryptogram).
Еще одна криптограмма ARPC (Authorization Response Cryptogram) нужна для аутентификации эмитента. Эмитент формирует криптограмму ARPC и отсылает криптограмму карте, если карта подтвердит пришедшую криптограмму, то следовательно, эмитент аутентифицирован картой.
Немного о безопасности ключей и взаимной аутентификации карты и эмитента из книги И. М. Голдовского:
Смысл взаимной аутентификации заключается в том, что карта и терминал аутентифицируют друг друга с помощью проверки подлинности криптограмм ARQC и ARPC. Криптограммы представляют собой данные, формируемые с использованием секретного ключа (который известен карте и банку эмитенту), номера транзакции, случайного числа, сгенерированного терминалом, а также некоторых реквизитов транзакции, терминала и карты. В случае ARPC к перечисленным данным еще добавляется авторизационный код ответа эмитента. Без знания секретного ключа карты для генерации криптограммы вычислить значения ARQC/ARPC невозможно за обозримое время с текущим уровнем технологий, и потому факт их успешной верификации указывает на подлинность карты и эмитента. Онлайн аутентификация является наиболее надежным способом аутентификации карты. Это связано с тем, что она выполняется непосредственно эмитентом, без посредника в виде терминала. Кроме того, для онлайновой аутентификации используется алгоритм 3DES с временным ключом размером 112 битов, криптостойкость которого соответствует криптостойкости алгоритма RSA с длиной модуля асимметричного ключа, используемого для офлайн аутентификации приложения карты, более 1700 бит. Использование на карте асимметричных ключей такой длины все еще достаточная редкость. Обычно используются ключи с модулем длиной 1024, 1152 или 1408 бит.
В конечном итоге онлайн транзакция проходит по цепочке: Карта <—> POS-Терминал <—> Банк Эквайер <—> Платежная Система <—> Банк Эмитент.
Mapping string to ndef
To
map text to NDEF
given |record:NDEFRecordInit| and |ndef|,
run these steps:
This is useful when clients specifically want to write text in a
[=well-known type record=].
Other options would be to use the value «`mime`»
with an explicit MIME type text type, which allows for
better differentiation, e.g. when using «`text/xml`», or
«`text/vcard`».
- If |record|’s mediaType is not `undefined`,
[= exception/throw =] a {{TypeError}} and abort these steps. - If the type of |record|’s data is not {{DOMString}} or
{{BufferSource}}, [= exception/throw =] a {{TypeError}} and abort
these steps. - Let |documentLanguage:string| be the [=document element=]’s
lang attribute. - If |documentLanguage| is the empty string, set it to «`en`».
- Let |language:string| be |record|’s lang if it [= map/exists =],
or else to |documentLanguage|. - Switch on the type of |record|’s data:
- {{DOMString}}
- If |record|’s encoding is neither `undefined` nor
«`utf-8`», [= exception/throw =] a {{TypeError}} and abort
these steps. - Let |encoding label:string| be «`utf-8`».
- {{BufferSource}}
- Let |encoding label:string| be |record|’s encoding if
it [= map/exists =], or else «`utf-8`». - If |encoding label| is not equal to «`utf-8`», «`utf-16`»,
«`utf-16le`» or «`utf-16be`» [= exception/throw =] a
{{TypeError}} and abort these steps.
- Let |encoding name| be the [=encoding/name|name=]
obtained
from |encoding label|. - Let |header:byte| be a byte constructed the following way:
- If |encoding name| is equal to UTF-8, set bit `7` to the value
`0`, or else set the value to `1`. - Set bit `6` to the value `0` (reserved).
- Let |languageLength:octet| be the length of the
|language| string. - If |languageLength| cannot be stored in 6 bit
(|languageLength| > 63), [= exception/throw =] a {{SyntaxError}}. - Set bit `5` to bit `0` to |languageLength|.
- If |encoding name| is equal to UTF-8, set bit `7` to the value
- Let |data:byte sequence| be an empty [= byte sequence =].
- Set the first byte (position 0) of |data| to |header|.
- Set position 1 (second byte) to position |languageLength|
of |data| to |language|. - Switch on the type of |record|’s data:
- {{DOMString}}
- Let |stream:byte stream| be the resulting
byte stream of
running UTF-8 encode on |record|’s data. - Read bytes
from |stream| into |data| (from position |languageLength| 1)
until read
returns end-of-stream. - {{BufferSource}}
- Set bytes from |record|’s data into |data|
(from position |languageLength| 1) .
- Set |length:unsigned long| to the [=byte sequence/length=]
of |data|. - Set the |ndef|’s TNF field to `1` (
[=well-known type record=]). - Set the |ndef|’s TYPE field to «`T`» (`0x54`).
- Set the |ndef|’s PAYLOAD LENGTH field to |length|.
- If |length| > `0`, set the |ndef|’s PAYLOAD field
to |data|.
- Set the |ndef|’s TNF field to `1` (
- Return |ndef|.
Parsing ndef records
To
parse an NDEF record
given |ndef| and |context:string| into a
|record:NDEFRecord|, run these steps:
- Set |record|’s id to |ndef|’s |id:string|.
- Set |record|’s lang to `null`.
- Set |record|’s encoding to `null`.
- If |ndef|’s |typeNameField:number| (TNF field) is `0`
(empty record):- Set |record|’s id to `null`.
- Set |record|’s recordType to «`empty`».
- Set |record|’s mediaType to `null`.
- Set |record|’s data to `null`.
- If |ndef|’s |typeNameField| is `1` ([=well-known type record=]), then
- If |ndef|’s |type:string| is «`T`» (`0x54`),
set |record| to the result of running
parse an NDEF text record on |ndef|. - If |ndef|’s |type:string| is «`U`» (`0x55`),
set |record| to the result of running
parse an NDEF URL record on |ndef|. - If |ndef|’s |type:string| is «`Sp`» (`0x53` `0x70`),
set |record| to the result of running
parse an NDEF smart-poster record on |ndef|. - If |ndef|’s |type:string| is «`s`» (`0x73`)
and if |context| is equal to `»smart-poster»`,
set |record| to the result of running
parse a smart-poster size record on |ndef|. - If |ndef|’s |type:string| is «`t`» (`0x74`)
and if |context| is equal to `»smart-poster»`,
set |record| to the result of running
parse a smart-poster type record on |ndef|. - If |ndef|’s |type:string| is «`act`» (`0x61` `0x63` `0x74`)
and if |context| is equal to `»smart-poster»`,
set |record| to the result of running
parse a smart-poster action record on |ndef|. - If running the validate local type steps on
|ndef|’s |type:string| returns `true`,- If |context| is not `»external»` or `»smart-poster»`,
[= exception/throw =] a {{TypeError}} and abort these steps. - Set |record| to the result of running
parse a local type record on |ndef|.
- If |context| is not `»external»` or `»smart-poster»`,
- Otherwise [= exception/throw =] a {{TypeError}} and abort these
steps.
- If |ndef|’s |type:string| is «`T`» (`0x54`),
- If |ndef|’s |typeNameField| is `2` (MIME type record), then
set |record| to the result of running
parse an NDEF MIME type record on |ndef|, or make sure that
the underlying platform provides equivalent values to the |record|
object’s properties. - If |ndef|’s |typeNameField| is `3` (absolute-URL record),
then set |record| to the result of running
parse an NDEF absolute-URL record on |ndef|. - If |ndef|’s |typeNameField| is `4` (external type record),
then set |record| to the result of running
parse an NDEF external type record on |ndef|, or make sure that
the underlying platform provides equivalent values to the |record|
object’s properties. - If |ndef|’s |typeNameField| is `5` (unknown record)
then set |record| to the result of running
parse an NDEF unknown record on |ndef|, or make sure that the
underlying platform provides equivalent values to the |record| object’s
properties. - Otherwise [= exception/throw =] a {{TypeError}} and abort these steps.
Smart poster record
is defined in [[NDEF-SMARTPOSTER]] to describe a
given web content as an NDEF record that contains an
NDEF message
as payload, including the following records:
- A single mandatory URI record that refers to the
smart poster content.The [[NDEF-SMARTPOSTER]] states that applications SHALL use only the
smart poster record if it is present in an NDEF message
that also contains other URI records. - Zero or more Text records that act as a title record
related to the content. When there are more than one title record
present, they MUST be with different language tags.
Applications SHOULD select one title record for presentation
to the end user. - Zero or more MIME type records that act as icon record
related to the content. The MIME type is usually «`image/jpg`»,
«`image/png`», «`image/gif`», or even «`video/mpeg`».
Applications SHOULD select one icon record for presentation
to the end user. - One optional type record that has a [=local type name=]
«`t`» specific to smart poster and the PAYLOAD field
contains a UTF-8 encoded MIME type for the content referred to by the
URI record. - One optional size record that has [=local type name=] «`s`»
specific to smart poster and the PAYLOAD field contains
a 4-byte 32 bit unsigned integer that denotes the size of the object
referred to by the URL in the URI record of the
smart poster. - One optional action record that has a [=local type name=]
«`act`» specific to smart poster and the PAYLOAD field
contains a single byte, whose value has the following meaning:Value Description 0 Do the action 1 Save for later 2 Open for editing 3..0xFF Reserved for future use There is no default action on the smart poster content if the
action record is missing.At the time of NDEF standardization the value `0` («do the action») was
intended for use cases like send an SMS, make a call or launch browser.
Similarly, the value `1`, («save the content for later processing») was
intended for use cases like store the SMS in inbox, save the URL in
bookmarks, or save the phone number to contacts. Also, the value `2`
(«open for editing») was meant to open the smart poster content with a
default application for editing.Implementations don’t need to implement any standardized behavior for the
actions defined here. In this API it’s up to the applications what actions
they define (that may include the use cases above). However, Web NFC
just provides the values. - A smart poster MAY also contain other records, which can be
handled in an application specific manner.
The example below shows a smart poster record that embeds a text and a
URL record.
The nfc standard
NFC is standardized in the NFC Forum and described in [[NFC-STANDARDS]].
Ndef compatible tag types
The NFC Forum has mandated the support of five different tag types to be
operable with NFC devices. The same is required on operating systems, such
as Android.
In addition to that, the MIFARE Standard specifies a way
for NDEF to work on top of the older MIFARE Standard, which may
be optionally supported by implementers.
A note about the NDEF mapping can be found here:
MIFARE Classic as NFC Type MIFARE Classic Tag.
- NFC Forum Type 1: This tag is based on the ISO/IEC 14443-3A
(NFC-A). The tags are rewritable and can be
configured to become read-only. Memory size can be between `96` bytes and
`2` Kbytes. Communication speed is `106` kbit/s. In contrast to all other
types, these tags have no anti-collision protection for dealing with multiple
tags within the NFC field. - NFC Forum Type 2: This tag is based on the
ISO/IEC 14443-3A (NFC-A). The tags are rewritable and can be configured
to become read-only. Memory size can be between `48` bytes and `2` Kbytes.
Communication speed is `106` kbit/s. - NFC Forum Type 3: This tag is based on the Japanese Industrial
Standard (JIS) X 6319-4 (ISO/IEC 18092), commonly known as FeliCa. The tags are
preconfigured to be either rewritable or read-only. Memory is `2` kbytes.
Communication speed is `212` kbit/s or `424` kbit/s. - NFC Forum Type 4: This tag is based on the ISO/IEC 14443-4 A/B
(NFC A, NFC B) and thus supports either NFC-A or NFC-B
for communication. On top of that the tag may optionally support ISO-DEP
(Data Exchange Protocol defined in ISO/IEC 14443 (ISO/IEC 14443-4:2008
Part 4: Transmission protocol). The tags are preconfigured
to be either rewritable or read-only. Variable memory, up to `32` kbytes.
Supports three different communication speeds `106` or `212` or
`424` kbit/s. - NFC Forum Type 5: This tag is based on ISO/IEC 15693 (NFC-V) and
allows reading and writing an NDEF message on an ISO/IEC 15693 RF tag
that is accessible by long range RFID readers as well. The NFC communication
is limited to short distance and may use the Active Communication Mode of
ISO/IEC 18092 where the sending peer generates the field which balances
power consumption and improves link stability. Variable memory, up to `64` kbytes.
Communication speed is `26.48` kbit/s. - MIFARE Standard: This tag, often sold under the brand names MIFARE
Classic or MIFARE Mini, is based on the ISO/IEC 14443-3A (also known as NFC-A,
as defined in ISO/IEC 14443-3:2021, Part 3: Initialization and anticollision).
The tags are rewritable and can be configured to become read-only. Memory size
can be between `320` and `4` kbytes. Communication speed is `106` kbit/s.MIFARE Standard is not an NFC Forum type and can only be read by devices
using NXP hardware. Support for reading and writing to tags based on the
MIFARE Standard is thus non-nominative, but the type is included
due to the popularity and use in legacy systems.
In addition to data types standardized for NDEF records by the NFC
Forum, many commercial products such as bus cards, door openers may be based
on the MIFARE Standard which requires specific NFC chips (same vendor of
card and reader) in order to function.
The write() method
The
NDEFReader.write
method, when invoked, MUST run the
write a message
algorithm:
- Let |p:Promise| be a new {{Promise}} object.
- If not currently executing in the currently active top-level
browsing context, then reject |p| with and
{{«InvalidStateError»}} {{DOMException}} and return |p|. - Let |message:NDEFMessageSource| be the first argument.
- Let |options:NDEFWriteOptions| be the second argument.
- Let |signal:AbortSignal| be the |options|’ dictionary member
of the same name if present, or `null` otherwise. - If |signal| is [= AbortSignal/aborted =], then reject |p|
with |signal|’s [=AbortSignal/abort reason=] and return |p|. - If |signal| is not `null`, then
add the following abort steps to |signal|:- Run the abort a pending write operation on the
environment settings object.
- Run the abort a pending write operation on the
- [=promise/React=] to |p|:
- If |p| was settled (fulfilled or rejected), then clear the
pending write tuple if it exists.
- If |p| was settled (fulfilled or rejected), then clear the
- Return |p| and run the following steps in parallel:
- If the obtain permission steps return `false`, then
reject |p| with a {{«NotAllowedError»}} {{DOMException}} and
abort these steps. - If there is no underlying NFC Adapter, or if a connection
cannot be established, then reject |p| with a
{{«NotSupportedError»}} {{DOMException}}
and abort these steps. - If the UA is not allowed to access the underlying NFC Adapter
(e.g. a user preference), then reject |p| with a
{{«NotReadableError»}} {{DOMException}}
and abort these steps. - If pushing data is not supported by the underlying
NFC Adapter, then reject |p| with a {{«NotSupportedError»}}
{{DOMException}} and abort these steps. - An implementation MAY reject |p| with
a {{«NotSupportedError»}} {{DOMException}}
and abort these steps.The UA might abort message write at this point. The reasons
for termination are implementation details. For example, the
implementation might be unable to support the requested
operation. - Let |output| be the notation for the NDEF message
to be created by UA, as the result of invoking
create NDEF message with |message|, `»»`, and `0`.
If this throws an exception, reject |p| with that
exception and abort these steps. - Attempt to abort a pending write operation.
A write replaces all previously configured write operations.
- Set `this`.[[WriteOptions]] to |options|.
- Set `this`.[[WriteMessage]] to |output|.
- Set pending write tuple to (`this`, |p|).
- Run the start the NFC write steps whenever an
NFC tag |device| comes within communication range.If NFC is suspended, continue waiting until promise is
aborted by the user or an NFC tag comes within
communication range.
- If the obtain permission steps return `false`, then
To
start the NFC write
, run these steps:
- Let |p:Promise| be the pending write tuple‘s promise.
- Let |writer| be the pending write tuple‘s writer.
- Let |options:NDEFWriteOptions| be |writer|.[[WriteOptions]].
- If the NFC tag in proximity range does not expose
NDEF technology for formatting or writing, then
reject |p| with a {{«NotSupportedError»}} {{DOMException}} and
return |p|. - Verify that NFC is not suspended.
- In case of success, run the following steps:
- If |device| is an NFC tag and if |options|’s overwrite is
`false`, read the tag to check whether there are NDEF
records on the tag. If yes, then reject |p| with a
{{«NotAllowedError»}} {{DOMException}} and return |p|. - Let |output:NDEFMessage| be |writer|.[[WriteMessage]].
- Initiate data transfer to |device| using
|output| as buffer, using the NFC adapter
in communication range with |device|.If the NFC tag in proximity range is unformatted and
NDEF-formatable, format it and write |output| as
buffer.Multiple adapters should be used sequentially by users.
There is very little likelihood that a simultaneous tap
will happen on two or multiple different and connected
NFC adapters.
If it happens, the user will likely need to repeat the
taps until success, preferably one device at a time.
The error here gives an indication that the operation
needs to be repeated. Otherwise the user may think the
operation succeeded on all connected NFC adapters. - If the transfer fails, reject |p| with
{{«NetworkError»}} {{DOMException}}
and abort these steps. - When the transfer has completed, resolve |p|.
- If |device| is an NFC tag and if |options|’s overwrite is
Клонируем карту mastercard в режиме magstripe
Перейдем непосредственно к принципу клонирования. Данный метод атаки на бесконтактные карты был опубликован двумя исследователями
из Австрийского университета. В его основе лежит общий принцип, который называется
Skimming
. Это такой сценарий, при котором злоумышленник крадет деньги с банковской карточки путем считывания (копирования) информации с этой карты. В общем случае здесь важно сохранять PIN-код в тайне и не допускать его утечки. Но в методе австрийских ребят это нам знать не нужно.
(MasterCard PayPass M/Chip)MagStripe (MasterCard PayPass MagStripe)
режим.
MagStripe — это режим поддержки карт с магнитной полосой. Этот режим реализуется на картах MasterCard с бесконтактным интерфейсом. Режим MagStripe скорее нужен для банков которым сложно переводить всю инфраструктуру для поддержки чиповых бесконтактных EMV транзакций. Кстати, у карт Visa также есть аналогичный режим работы — PayWave MSD (Magnetic Stripe Data).
Процесс обработки транзакции для бесконтактных карт урезан в сравнении с чиповыми и обычно работает в следующем режиме:
- Терминал отправляет команду SELECT PPSE (Proximity Payment System Environment). Карта шлет список поддерживаемых приложений.
- Терминал отправляет команду SELECT. В ответ получает необходимые детали приложения.
- Терминал отправляет команду GET_PROCESSING_OPTIONS. Карта отвечает какой тип аутентификации она поддерживает и существует ли там верификация держателя карты.
- Терминал отправляет команду READ_RECORDS. Карта в ответе посылает Track1 и Track2 практически аналогичный тому, что записан на магнитной полосе карты.
- Терминал отправляет команду COMPUTE_CRYPTOGRAPHIC_CHECKSUM. Которая означает, что карта должна на основе переданного Unpredictable Number сгенерировать значение CVC3.
Карта поддерживает специальную команду COMPUTE CRYPTOGRAPHIC CHECKSUM, аргументом которой являются данные, определенные в объекте Unpredictable Number Data Object (UDOL).
В результате карта с помощью алгоритма 3DES и секретного ключа вычисляет динамическую величину CVC3 (Card Verification Code).
В качестве аргумента функции 3DES используется конкатенация данных UDOL и счетчика транзакции (Application Transaction Counter,ATC).
Таким образом, значение величины CVC3 всегда зависит от объектов UN и ATC.
Другими словами, эта команда нужна, чтобы карта сгенерировала некую “подпись” для того, чтобы эмитент мог верифицировать карту. Однако, в этой подписи отсутствует подпись самой транзакции. В подписи содержатся значения ATC — 2 байта, CVC3 (Track1)
— 2 байта, CVC3 (Track2) — 2 байта, которые генерируются картой на основе секретного ключа, который также знает банк-эмитент и счетчика транзакций (ATC). При этом также для генерации подписи POS-терминал сообщает карте UN (Unpredictable Number)
— 4 байта, который также используется в генерации подписи. Unpredictable Number препятствует формированию кодов аутентификации на реальной карте для последующего использования в мошеннических транзакциях. Для атаки нам сильно мешает UN, поскольку 4 байта не представляется возможным перебрать, не выйдя за пределы счетчика транзакций. Однако, в спецификации этого есть некоторые слабости.
Во-первых, спецификация ограничивает UN кодировкой чисел, а именно Двоично-Десятичным Кодом (BCD), что по сути означает что, если мы посмотрим на такое закодированное число в HEX, то мы увидим только цифры от 0 до 9, все остальные значения считаются как бы запрещенными. Таким образом, количество UN уменьшается с 4,294,967,295 до 99,999,999.
Во-вторых, количество значащих цифр UN определяется картой. Таким образом в зависимости от специальных параметров в треках количество цифр в UN может быть от 10 до 10000 в зависимости от типа карты, на практике чаще всего встречается 1000 значений.
Таким образом план атаки выглядит следующий:
- Считываем карту и узнаем количество значащих цифр у UN, которое будет предоставлять терминал
- Перебираем все UN, получаем все возможные значения функции COMPUTE_CRYPTOGRAHIC_CHECKSUM, сохраняем их в соответствующей таблице с мапингом UN -> Result
- Подносим к POS-терминалу, узнаем число, которое просит POS-терминал.
- Выбираем из таблицы нужный результат и подставляем его в ответ терминалу.
- Транзакция уходит.
- PROFIT. Но успех одобрения транзакции не гарантирован, поскольку банк эмитент может отклонить такую транзакцию.
Стоит отметить также, что счетчик транзакций (ATC) препятствует повторному использованию ранее использованных кодов аутентификации, а значит что если мы использовали такую атаку, то необходимо копировать карту заново, поскольку счетчик транзакции уже использовался для получения информации и был использован в подписи, что значит, что если мы имели счетчик транзакций 1000, а после отправили транзакцию в банк, то банк уже не примет транзакции со счетчиком ниже <1001.
В большинстве случаев передаваемые данные с карты статические для всех транзакций. Конечно, кроме COMPUTE_CRYPTOGRAPHIC_CHECKSUM. Для генерации динамического CVC3 кода, приложение карты должно быть прочитано командой SELECT, затем GET_PROCESSING_OPTIONS, а только потом COMPUTE_CRYPTOGRACHIC_CHECKSUM и это довольно важный момент.
Для работы с терминалом и картой использовалась программа Terminal Simulator от MasterCard. Он прекрасно работает с различными NFC-считывателями и считывателями смарт карт. К тому же он абсолютно бесплатен. Он позволяет тестировать карты при различных настройках POS-терминала и ведет подробный лог всех запросов от терминала и ответов карты. Также его можно использовать для тестирования приложения на телефоне, работающего в режиме карты.
Для чтения карты использовался NFC считыватель ACR122.
Теперь давайте попробуем все это преобразовать в код. Приложение будем писать на языке Kotlin под Android. Сначала попытаемся описать общую структуру команды.
data class Command(
var CLA: String = 0x00.toString(),
var INS: String = 0x00.toString(),
var P1: String = "",
var P2: String = "",
var Lc: String = "",
var Nc: String = "",
var Le: String = "",
var Nr: String = "",
var SW1WS2: String = ""
) {
fun split(): ByteArray {
return getHexString().hexToByteArray()
}
fun getHexString() = CLA.plus(INS).plus(P1).plus(P2).plus(Lc).plus(Nc).plus(Le).plus(Nr).plus(SW1WS2)
}
Для начала нам нужно настроить работу с NFC. На телефоне мы можем работать в двух режимах. В режиме карты, это когда мы отвечаем на команды от терминала, и в режиме терминала когда отсылаем команды и производим считывание, например карты. Т.е. сначала мы можем клонировать карту, а потом сделать так чтобы на запросы от терминала мы отвечали уже заготовленными командами.
Далее упрощенная реализация взаимодействия с NFC:
private var nfcAdapter: NfcAdapter? = null /*!< represents the local NFC adapter */
private var tag: Tag? = null /*!< represents an NFC tag that has been discovered */
private lateinit var tagcomm: IsoDep /*!< provides access to ISO-DEP (ISO 14443-4) */
private val nfctechfilter = arrayOf(arrayOf(NfcA::class.java.name)) /*!< NFC tech lists */
private var nfcintent: PendingIntent? = null
....
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
nfcintent = PendingIntent.getActivity(this, 0, Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
cardEmulation = CardEmulation.getInstance(nfcAdapter)
nfcAdapter?.enableForegroundDispatch(this, nfcintent, null, nfctechfilter)
}
....
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
cardReading(tag)
}
.....
override fun onResume() {
super.onResume()
if (canSetPreferredCardEmulationService()) {
this.cardEmulation?.setPreferredService(this, ComponentName(this, "com.nooan.cardpaypasspass.NfcService"));
}
}
override fun onPause() {
if (canSetPreferredCardEmulationService()) {
this.cardEmulation?.unsetPreferredService(this)
}
super.onPause()
}
private fun cardReading(tag: Tag?) {
tagcomm = IsoDep.get(tag)
try {
tagcomm.connect()
} catch (e: IOException) {
error = "Reading card data ... Error tagcomm: " e.message
Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show()
return
}
try {
when {
commands != null -> readCardWithOurCommands()
mChip -> readCardMChip()
else -> readCardMagStripe()
}
} catch (e: IOException) {
error = "Reading card data ... Error tranceive: " e.message
Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show()
return
} finally {
tagcomm.close()
}
}
protected fun execute(command: Command, log:Boolean): ByteArray {
val bytes = command.split()
listLogs.add(bytes.toHex())
val recv = tagcomm.transceive(bytes)
listLogs.add(recv.toHex())
return recv
}
Здесь описывается последовательность команд и перебор значений Unpredictable Number в цикле от 0 до 999, в нужную нам команду изменяем Nc на «00000${String.format(»d», i)}».replace(«..(?!$)».toRegex(), «$0 «). И не забываем выполнять GET_PROCESSING_OPTIONS каждый раз перед COMPUTE_CRYPTOGRAPHIC_CHECKSUM иначе чек сумма подсчитываться не будет.
В результате это все можно записать в файл и использовать уже при работе с настоящим терминалом. Здесь же мы получаем Имя и Номер карточки, можем отобразить это на экране.