pycrate/pycrate_mobile/TS102225.py

313 lines
8.6 KiB
Python

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2019. Benoit Michau. P1Sec.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_mobile/TS102225.py
# * Created : 2019-07-19
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
#
# Implement parts of ETSI TS 102.225
# Secured Packet Structure for UICC
from pycrate_core.base import *
from pycrate_core.elt import *
from pycrate_core.repr import *
from pycrate_mobile.TS31111_SAT import *
# 5.1.1 Coding of the SPI
SPI_cntt_dict = {
0 : 'No counter available',
1 : 'Counter available, no replay checking',
2 : 'Process only if counter over receiver value',
3 : 'Process only if counter by 1 over receiver value',
}
SPI_ciph_dict = {
0 : 'No ciphering',
1 : 'Ciphering'
}
SPI_intt_dict = {
0 : 'No integrity check',
1 : 'Redundancy check',
2 : 'Cryptographic checksum',
3 : 'Digital signature',
}
SPI_sms_dict = {
0 : 'PoR response using SMS-DELIVER-REPORT',
1 : 'PoR response using SMS-SUBMIT',
}
# PoR : Proof of Receipt
SPI_porc_dict = {
0 : 'PoR response shall not be ciphered',
1 : 'PoR response shall be ciphered'
}
SPI_pori_dict = {
0 : 'No integrity applied to PoR response',
1 : 'Redundancy check applied to PoR response',
2 : 'Cryptographic checksum applied to PoR response',
3 : 'Digital signature applied to PoR response',
}
SPI_porr_dict = {
0 : 'No PoR response',
1 : 'PoR to be sent',
2 : 'PoR to be sent on error',
3 : 'reserved',
}
class SPI(Envelope):
_GEN = (
Uint('reserved', bl=3, rep=REPR_HEX),
Uint('CounterType', bl=2, dic=SPI_cntt_dict),
Uint('CipherType', bl=1, dic=SPI_ciph_dict),
Uint('IntegrityType', bl=2, dic=SPI_intt_dict),
Uint('reserved', bl=2, rep=REPR_HEX),
Uint('SMSPoR', bl=1, dic=SPI_sms_dict), # TS 31.115
Uint('PoRCipherType', bl=1, dic=SPI_porc_dict),
Uint('PoRIntegrityType', bl=2, dic=SPI_pori_dict),
Uint('PoRExpect', bl=2, dic=SPI_porr_dict)
)
# 5.1.2 Coding of the KIc
KIc_alg_dict = {
0 : 'Algorithm known implicitely',
1 : 'DES',
2 : 'AES',
3 : 'proprietary',
}
KIc_des_dict = {
0 : 'DES in CBC mode', # before release 8
1 : 'Triple-DES in outer-CBC mode with 2 keys',
2 : 'Triple-DES in outer-CBC mode with 3 keys',
3 : 'DES in ECB mode', # before release 8
}
KIc_aes_dict = {
0 : 'AES in CBC mode',
1 : 'reserved',
2 : 'reserved',
3 : 'reserved',
}
KIc_alg_sel_dict = {
0 : {},
1 : KIc_des_dict,
2 : KIc_aes_dict,
3 : {}
}
class KIc(Envelope):
_GEN = (
Uint('Keys', bl=4, rep=REPR_HEX),
Uint('AlgSubtype', bl=2),
Uint('AlgType', bl=2, dic=KIc_alg_dict)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_dicauto(lambda: KIc_alg_sel_dict[self[2]()])
# 5.1.3.1 Coding of the KID for Cryptographic Checksum
KID_CC_alg_dict = {
0 : 'Algorithm known implicitely',
1 : 'DES',
2 : 'AES',
3 : 'proprietary',
}
KID_CC_des_dict = {
0 : 'DES in CBC mode', # before release 8
1 : 'Triple-DES in outer-CBC mode with 2 keys',
2 : 'Triple-DES in outer-CBC mode with 3 keys',
3 : 'Reserved',
}
KID_CC_aes_dict = {
0 : 'AES in CMAC mode',
1 : 'Reserved',
2 : 'Reserved',
3 : 'Reserved',
}
KID_CC_alg_sel_dict = {
0 : {},
1 : KID_CC_des_dict,
2 : KID_CC_aes_dict,
3 : {}
}
class KID_CC(Envelope):
_GEN = (
Uint('Keys', bl=4, rep=REPR_HEX),
Uint('AlgSubtype', bl=2),
Uint('AlgType', bl=2, dic=KID_CC_alg_dict)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_dicauto(lambda: KID_CC_alg_sel_dict[self[2]()])
# 5.1.3.2 Coding of the KID for Redundancy Check
KID_RC_alg_dict = {
0 : 'Algorithm known implicitely',
1 : 'CRC',
2 : 'Reserved',
3 : 'proprietary',
}
KID_RC_crc_dict = {
0 : 'CRC-16',
1 : 'CRC-32',
2 : 'Reserved',
3 : 'Reserved',
}
KID_RC_alg_sel_dict = {
0 : {},
1 : KID_RC_crc_dict,
2 : {},
3 : {}
}
class KID_RC(Envelope):
_GEN = (
Uint('GPKeyVersNum', bl=4, rep=REPR_HEX),
Uint('AlgSubtype', bl=2),
Uint('AlgType', bl=2, dic=KID_RC_alg_dict)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_dicauto(lambda: KID_RC_alg_sel_dict[self[2]()])
# KID Digital Signature
class KID_DS(Envelope):
_GEN = (
Uint('GPKeyVersNum', bl=4, rep=REPR_HEX),
Uint('AlgSubtype', bl=2),
Uint('AlgType', bl=2)
)
# 5.1 Command packet structure
class PacketCmd(Envelope):
_GEN = (
Uint8('CmdPacketId'),
BERLen('CmdPacketLen'), # 1 or more bytes
Uint8('CmdHeaderId'),
BERLen('CmdHeaderLen'), # 1 or more bytes
SPI(), # 2 bytes
KIc(), # 1 byte
Alt('KID', GEN={ # 1 byte
0 : Uint8('NoIntegrity', val=0),
1 : KID_RC(),
2 : KID_CC(),
3 : KID_DS()
},
DEFAULT=Uint8('NoIntegrity', val=0, bl=0),
sel=lambda self: self.get_env()['SPI']['IntegrityType']()),
Uint24('TAR', rep=REPR_HEX, dic=TAR_dict),
Uint('CNTR', bl=40),
Uint8('PCNTR'),
Buf('IntegrityCheck', rep=REPR_HEX),
Buf('Data', rep=REPR_HEX),
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: 1 + self[3].get_len() + self[3]() + self[-1].get_len())
self[3].set_valauto(lambda: 13 + self[-2].get_len())
self[-2].set_blauto(lambda: max(0, self[3]() - 13))
self[-1].set_blauto(lambda: max(0, self[1]() - self[3].get_len() - self[3]() - 1))
# 5.2 Response Packet structure
RespStatus_dict = {
0x00 : 'PoR OK',
0x01 : 'RC/CC/DS failed',
0x02 : 'CNTR low',
0x03 : 'CNTR high',
0x04 : 'CNTR Blocked',
0x05 : 'Ciphering error',
0x06 : 'Unidentified security error. This code is for the case where the Receiving Entity cannot correctly ' \
'interpret the Command Header and the Response Packet is sent unciphered with no RC/CC/DS ',
0x07 : 'Insufficient memory to process incoming message',
0x08 : 'This status code "more time" should be used if the Receiving Entity/Application needs more time ' \
'to process the Command Packet due to timing constraints. In this case a later Response Packet ' \
'should be returned to the Sending Entity once processing has been completed',
0x09 : 'TAR Unknown',
0x0A : 'Insufficient security level',
0x0B : 'Reserved for 3GPP (see TS 131 115 [5])',
0x0C : 'Reserved for 3GPP (see TS 131 115 [5])',
0x0D : 'to 0xBF Reserved for future use',
0xC0 : 'to 0xFE Reserved for proprietary use',
0xFF : 'Reserved for future use',
}
class PacketResp(Envelope):
_GEN = (
Uint8('RespPacketId'),
BERLen('RespPacketLen'),
Uint8('RespHeaderId'),
BERLen('RespHeaderLen'),
Uint24('TAR', rep=REPR_HEX, dic=TAR_dict),
Uint('CNTR', bl=40),
Uint8('PCNTR'),
Uint8('Status', dic=RespStatus_dict),
Buf('IntegrityCheck', rep=REPR_HEX),
Buf('Data', rep=REPR_HEX),
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: 1 + self[3].get_len() + self[3]() + self[-1].get_len())
self[3].set_valauto(lambda: 10 + self[-2].get_len())
self[-2].set_blauto(lambda: max(0, self[3]() - 10))
self[-1].set_blauto(lambda: max(0, self[1]() - self[3].get_len() - self[3]() - 1))