Самоблокирующийся апплет Javacard
Мой вопрос заключается в том, можно ли заблокировать апплет из кода самого апплета в качестве меры противодействия обнаруженным манипуляциям из кода.
Очевидный выбор - использовать GPSystem.lockCard();
и это работает, однако мне интересно, возможно ли заблокировать только апплет. Также я могу заблокировать сам апплет из аутентифицированного сеанса соответствующего домена безопасности. Но возможно ли это из самого кода апплета. Кажется, учитывая GPSystem.setCardContentState();
метод, используемый с GPSystem.APPLICATION_LOCKED
, так что я тоже это проверял, но это не работает.
Перечитываю описание спецификации GP Card 2.2 PDF:
OPEN отклоняет любой запрос перехода из состояния жизненного цикла LOCKED;
В моем Eclipse JavaDoc говорит:
OPEN должен отклонить любой запрос на переход в состояние жизненного цикла LOCKED
Что тут происходит?
5 ответов
Интересно посмотреть, как этот механизм эволюционировал от спецификации карт GlobalPlatform 2.1.1 до 2.2.1 (все еще в 2.3):
В GP 2.1.1 только эмитент карты (объект вне карты) или OPEN (в результате "исключений") могут инициировать блокировку приложения:
У эмитента карты есть механизм, позволяющий отключить статус непрерывного выполнения приложения на карте. Этот механизм может быть вызван изнутри OPEN на основе исключений, обрабатываемых OPEN, или из-за использования внешних вызванных команд. Эмитент карты является единственным лицом, которое может инициировать блокировку Приложения.
Метод
GPSystem.setCardContentState()
четко определены, чтобы разрешить только изменения состояния для конкретных состояний жизненного цикла приложения (значения между0x07
а также0x7F
с самыми низкими установленными 3 битами). Поскольку константа дляAPPLICATION_LOCKED
в более поздних спецификациях0x80
установка этого состояния не допускается. Это также поясняется в примечаниях к этому методу:- ОТКРЫТЫЙ должен отклонить любой запрос перехода в Состояния жизненного цикла, УСТАНОВЛЕННЫЕ или ЗАБЛОКИРОВАННЫЕ
Следовательно, попытка установить состояние приложения заблокированным изнутри самого приложения должно завершиться неудачно на плате, реализующей GP 2.1.1.
ОБНОВЛЕНИЕ (2016-05-20): Я проверил это на нескольких картах NXP JCOP (которые, как утверждается, соответствуют GP 2.1.1), и установки значений, для которых установлен верхний бит или любой из очищенных младших 3 битов, действительно дают сбой.
Это изменилось в GP 2.2. Теперь приложению разрешено блокировать себя:
Карта имеет механизм для отключения и последующего повторного включения статуса непрерывного выполнения приложения на карте. Этот механизм может быть вызван изнутри OPEN на основе исключений, обрабатываемых OPEN, или из-за использования внешних вызванных команд. Приложение с привилегией Global Lock, само Приложение или прямо или косвенно связанный Домен Безопасности являются единственными объектами, которые могут инициировать блокировку Приложения.
Спецификация GP Card не требует, чтобы приложение имело какое-либо специальное разрешение на самоблокировку.
К сожалению, спецификация API для метода
GPSystem.setCardContentState()
до сих пор не совсем понятно. Во-первых, в описании метода все еще говорится, что только значения между0x07
а также0x7F
с самыми низкими установленными 3 битами должно быть разрешено:Этот метод устанавливает конкретное состояние жизненного цикла приложения текущего контекста апплета. Состояния жизненного цикла для конкретного приложения варьируются от 0x07 до 0x7F, если установлены 3 младших бита.
Во-вторых, в документации по API, которая является частью приложения A к документу спецификации GP Card 2.2 и JavaDoc в файлах экспорта API, имеются отклоняющиеся примечания. В то время как примечания в спецификации были изменены на:
- ОТКРЫТО должно отклонить любой запрос на переход в состояние УСТАНОВЛЕННОГО жизненного цикла;
- OPEN отклоняет любой запрос перехода из состояния жизненного цикла LOCKED;
Примечания в файлах экспорта API (GPSystem.java и JavaDoc) остались такими же, как в GP 2.1.1.
Следовательно, если этот метод был реализован в соответствии со спецификацией, он все равно должен отклонить установку состояния жизненного цикла приложения на
APPLICATION_LOCKED
,ОБНОВЛЕНИЕ (2016-05-20): Я проверил это на карте NXP J3D081 (JCOP v2.4.2 R2) (которая, как утверждается, соответствует GP 2.2). К сожалению, установка значений, для которых установлен верхний бит или любой из 3 младших битов, очищена.
Тем не менее, есть также метод
GPRegistryEntry.setState()
, В документации этого метода говорится, что:- Запрос на переход в состояние жизненного цикла, отличное от APPLICATION_LOCKED и APPLICATION_UNLOCKED, должен приниматься только в том случае, если вызывающее приложение соответствует этому GPRegistryEntry;
- Приложение должно быть в состоянии заблокировать и не должно быть в состоянии разблокировать себя;
Таким образом, было бы интересно посмотреть, работает ли следующее на той же карте, где используется
setCardContentState()
не удалось:GPSystem.getRegistryEntry(null).setState(GPSystem.APPLICATION_LOCKED);
ОБНОВЛЕНИЕ (2016-05-20): Я проверил это на карте NXP J3D081 (JCOP v2.4.2 R2) (которая, как утверждается, соответствует GP 2.2). К сожалению, это тоже не удается. Btw. это не имеет значения, если
null
или жеJCSystem.getAID()
используется в качестве параметра дляgetRegistryEntry()
,ОБНОВЛЕНИЕ (2016-06-14): По словам Paul Bastian, представитель NXP подтвердил, что приложения не могут устанавливать себя в заблокированное состояние на картах JCOP v2.4.x.
ОБНОВЛЕНИЕ (2016-06-06): Я проверил это на карте Infineon SLE97CNFX (которая, как утверждается, соответствует GP 2.2.1), и это сработало. Я мог бы успешно установить состояние заблокировано с помощью
APPLICATION_LOCKED
(0x80). Затем состояние устанавливается наprevious_state | 0x80
, Попытка установить другие значения состояния, для которых установлен верхний бит (например, 0x8F), не работает (как я и ожидал).В ГП 2.2.1 приведена документация по методу
GPSystem.setCardContentState()
был изменен (снова). В примечании об изменении четко указано, что метод был обновлен и теперь позволяет приложению блокироваться (файл экспорта версии 1.5. Соответствует GP 2.2.1):- экспорт файла версии 1.5: этот метод теперь позволяет приложению, связанному с текущим контекстом апплета, блокировать себя.
Определение метода было изменено на:
Этот метод позволяет приложению, связанному с текущим контекстом апплета, изменять свое состояние на конкретное состояние жизненного цикла приложения или блокировать себя. Приложение не может разблокировать себя, используя этот метод.
Диапазон значений для параметра состояния, передаваемого методу, теперь явно включает значение
APPLICATION_LOCKED
:bState
- конкретное состояние жизненного цикла приложения (от 0x07 до 0x7F с установленными 3 младшими битами), илиAPPLICATION_LOCKED
(0x80).Следовательно, карты, реализующие GP 2.2.1 или выше, должны в конечном итоге позволять приложениям изменять свое собственное состояние жизненного цикла на заблокированное с использованием метода.
GPSystem.setCardContentState()
,ОБНОВЛЕНИЕ (2016-06-06): Я проверил это на карте Infineon SLE97CNFX (которая, как утверждают, соответствует GP 2.2.1 (или это 2.3?)), И это работало. Я мог бы успешно установить состояние заблокировано с помощью
APPLICATION_LOCKED
(0x80). Затем состояние устанавливается наprevious_state | 0x80
, Попытка установить другие значения состояния, для которых установлен верхний бит (например, 0x8F), не работает (как я и ожидал).
Альтернативное решение
Что вы можете сделать, чтобы преодолеть проблему, не имея возможности установить жизненный цикл приложения в состояние APPLICATION_LOCKED
, это использовать специфические для приложения состояния жизненного цикла:
public class LockableApplet extends Applet {
[... applet installation / instantiation code ...]
private static final byte APPLICATION_STATE_UNLOCKED = (byte)0x07;
private static final byte APPLICATION_STATE_LOCKED = (byte)0x7F;
public boolean select() {
if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
return false;
}
return true;
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
[... applet logic code ...]
}
}
Обнаружив проблему, которая должна привести к блокировке вашего приложения, вы можете заблокировать апплет с помощью следующего вызова:
GPSystem.setCardContentState(APPLICATION_STATE_LOCKED);
Позже вы можете снова разблокировать приложение, используя команду SET STATUS через домен безопасности.
(Покупатель остерегается: кажется, этот способ просто не работает - см. Комментарии)
(В контексте спецификации карты GlobalPlatform 2.2.1)
Вы должны соблюдать правила состояния жизненного цикла приложения, показанные на рис. 5-2 (здесь применяется стрелка с отметкой "5").
Правильный путь должен быть:
GPSystem.setCardContentState((byte)(GPSystem.getCardContentState() | GPSystem.APPLICATION_LOCKED));
или же
GPSystem.getRegistryEntry(JCSystem.getAID()).setState((byte)(GPSystem.getCardContentState() | GPSystem.APPLICATION_LOCKED))
0x80
Состояние жизненного цикла недопустимо для приложения. Смотрите таблицу 11-4 (по крайней мере, b1
а также b2
биты должны быть установлены, b3
немного наверное так же).
EDIT>
(Я признаюсь, что написал этот ответ, основываясь исключительно на том факте, что OPEN сохраняет исходное состояние, из которого была заблокирована сущность)
Мне это очень интересно, поэтому я провел несколько тестов, используя следующий апплет (отрывок):
public void process(APDU apdu) {
byte[] buffer = apdu.getBuffer();
if(selectingApplet()) {
return;
}
short claIns = Util.getShort(buffer, ISO7816.OFFSET_CLA);
switch(claIns) {
case (short) 0x8007:
buffer[0]=GPSystem.getCardContentState();
if(buffer[0]==buffer[ISO7816.OFFSET_P1]) {
if(GPSystem.setCardContentState(buffer[ISO7816.OFFSET_P2])) {
buffer[1]=0x01;
} else {
buffer[1]=0x00;
}
} else {
buffer[1]=(byte)0xFF;
}
buffer[2]=GPSystem.getCardContentState();
apdu.setOutgoingAndSend((short)0, (short)3);
return;
default: {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
return;
}
}
}
И следующие APDU:
8007070F03 // To test transition into Application Specific State
80070F8F03 // To test my theory
80070F8003 // To test the GPSystem.APPLICATION_LOCKED constant directly
Результаты для моего набора карт (Gemalto, Morpho, JCOP - к сожалению, все они GP 2.1.1) соответствуют отличному ответу Майкла Роланда и спецификациям GP - попытка приложения заблокировать себя отклонена.
Полученные ответные APDU для всех карт GP 2.1.1:
8007070F03 -> 07010F9000 // Succeeded in transition from `07` to `0F`
80070F8F03 -> 0F000F9000 // Failed transition from `0F` to `8F`
80070F8003 -> 0F000F9000 // Failed transition from `0F` to `80`
Просто примечание: этот инструмент весьма полезен для определения реализованной версии GP, так как он анализирует данные распознавания карты.
Как таковой спецификации ядра GP нет. Продукт совместим с конфигурацией GP. Конфигурация GP не бесплатна. Продукты JCOP 2.4.x соответствуют конфигурации GP 2.2.x "Руководство по отображению существующих GP 2.1.1 Реализация на v2.2.1". Как следует из названия, эта конфигурация предназначена для сопоставления с обратной совместимостью. По сути, продукты JCOP 2.4.x являются только продуктами, совместимыми с GP 2.1.1 (с парой функций из GP 2.2.x). Глобальная блокировка не обязательна для апплетов.
Да. Это обычная и предполагаемая операция приложения GlobalPlatform. Вы должны установить ваше приложение с нужными привилегиями (gp -install -privs CardLock), и код для этого:
GPSystem.setCardContentState(GPSystem.APPLICATION_LOCKED);
Позже вы можете разблокировать приложение с
gp -unlock-applet <aid>
Да, это довольно просто: используйте private static boolean
пометьте и отметьте его в начале process(APDU apdu)
метод:
public class MiniApplet extends Applet {
public static void install(byte[] bArray, short bOffset, byte bLength) {
new MiniApplet();
}
protected MiniApplet() {
register();
}
private static final short SW_APPLET_IS_LOCKED = (short) 0x9199; //any error SW
private static boolean appletLocked = false; //static -> faster access, this flag is checked each call! "private" modifier is VERY important!
public void process(APDU apdu) {
if (selectingApplet()) {
return; //it is a good practice not to throw any exceptions on SELECT command
}
if (appletLocked) {
ISOException.throwIt(SW_APPLET_IS_LOCKED);
}
if (attackDetected()) { //implement your attack detection
appletLocked = true;
}
}
}