Developer API
Installation
$ pip install cardutil
cardutil.iso8583
Parsers for ISO8583 messages.
The iso8583 module provides ISO8583 message parsing functions. See ISO8583 Wikipedia page. for more details. Also supports Mastercard ® PDS field structures.
The parsing functions are modelled on the python standard json library. The functions convert raw ISO8583 messages to python dictionaries.
Dictionary keys that represent the elements of an ISO8583 message.
MTI - Message type indicator
DE(1-127) - Standard fields
PDSxxxx - Mastercard PDS fields
TAGxxxx - ICC tag fields
Import the library:
from cardutil.iso8583 import dumps, loads
Read an ISO8583 message returning dict:
>>> import binascii
>>> binary_bitmap = binascii.unhexlify('c0000000000000000000000000000000')
>>> message_bytes = b'1144' + binary_bitmap + b'164444555566667777'
>>> message_dict = loads(message_bytes)
>>> message_dict
{'MTI': '1144', 'DE2': '4444555566667777'}
Create an ISO8583 message returning bytes:
>>> message_dict = {'MTI': '1144', 'DE2': '4444555566667777'}
>>> message_bytes = dumps(message_dict)
>>> message_bytes
b'1144\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00164444555566667777'
Add encoding parameter if you need different message encoding. All standard python encoding codecs are available. Default is latin_1.
>>> message_dict = {'MTI': '1144', 'DE2': '4444555566667777'}
>>> message_bytes = dumps(message_dict, encoding='cp500')
>>> message_bytes
b'\xf1\xf1\xf4\xf4\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xf6\xf4\xf4\xf4\xf4\xf5\xf5\xf5\xf5\xf6\xf6\xf6\xf6\xf7\xf7\xf7\xf7'
>>> message_dict = loads(message_bytes, encoding='cp500')
>>> message_dict
{'MTI': '1144', 'DE2': '4444555566667777'}
Set hex_bitmap to True if you require a hex format bitmap:
>>> message_bytes = dumps(message_dict, hex_bitmap=True)
>>> message_bytes
b'1144c0000000000000000000000000000000164444555566667777'
>>> message_dict = loads(message_bytes, hex_bitmap=True)
>>> message_dict
{'MTI': '1144', 'DE2': '4444555566667777'}
- cardutil.iso8583.dumps(obj: dict, encoding=None, iso_config=None, hex_bitmap=False)[source]
Serialize obj to a ISO8583 message byte string
- Parameters:
obj – dict containing message data
encoding – python text encoding scheme
iso_config – iso8583 message configuration dict
hex_bitmap – bitmap in hex format
- Returns:
byte string containing ISO8583 message
The default usage will generate a latin_1 encoded message with a binary bitmap:
import cardutil.iso8583 message_dict = {'MTI': '1144', 'DE2': '4444555566667777'} cardutil.iso8583.dumps(message_dict)
- cardutil.iso8583.loads(b: bytes, encoding=None, iso_config=None, hex_bitmap=False)[source]
Deserialise b (byte string) to a python object
- Parameters:
b – bytes containing message
encoding – python text encoding scheme
iso_config – iso8583 message configuration dictionary
hex_bitmap – bitmap in hex format
- Returns:
dict containing message data
import cardutil.iso8583 message_bytes = b'1144... iso message ...' cardutil.iso8583.loads(message_bytes)
cardutil.mciipm
Mastercard ® IPM clearing file readers and writers
VBS file readers and writers
IPM file readers and writers
IPM parameter extract reader
Support for 1014 blocked format
Read an IPM file:
from cardutil import mciipm
with open('ipm_in.bin', 'rb') as ipm_in:
reader = mciipm.IpmReader(ipm_in)
for record in reader:
print(record)
Create an IPM file:
from cardutil import mciipm
with open('ipm_out.bin', 'wb') as ipm_out:
writer = mciipm.IpmWriter(ipm_out)
writer.write({'MTI': '1111', 'DE2': '9999111122221111'})
writer.close()
MasterCard file formats
VBS file format
This format is a basic variable record format.
There are no carriage returns or line feeds in the file. A file consists of records. Each record is prefixed with a 4 byte binary length.
Say you had a file with the following 2 records:
"This is first record 1234567" <- length 28
"This is second record AAAABBBBB123" <- length 34
Add 4 byte binary length to the start of each record. (x’1C’ = 28, x’22’ = 34) with the file finishing with a zero length record length
00000000: 00 00 00 1C 54 68 69 73 20 69 73 20 66 69 72 73 ....This is firs
00000010: 74 20 72 65 63 6F 72 64 20 31 32 33 34 35 36 37 t record 1234567
00000020: 00 00 00 22 54 68 69 73 20 69 73 20 73 65 63 6F ..."This is seco
00000030: 6E 64 20 72 65 63 6F 72 64 20 41 41 41 41 42 42 nd record AAAABB
00000040: 42 42 42 31 32 33 00 00 00 00 BBB123....
1014 blocked file format
This is the same as VBS format with 1014 blocking applied.
The VBS data is blocked into lengths of 1012, and an additional 2 x’40’ characters are appended at each block.
Finally, the total file length is made a multiple of 1014 with the final incomplete record being filled with the x’40’ character
Taking the above VBS example
00000000: 00 00 00 1C 54 68 69 73 20 69 73 20 66 69 72 73 ....This is firs
00000010: 74 20 72 65 63 6F 72 64 20 31 32 33 34 35 36 37 t record 1234567
00000020: 00 00 00 22 54 68 69 73 20 69 73 20 73 65 63 6F ..."This is seco
00000030: 6E 64 20 72 65 63 6F 72 64 20 41 41 41 41 42 42 nd record AAAABB
00000040: 42 42 42 31 32 33 00 00 00 00 BBB123....
Block to 1014 by adding 2 * x’40’ characters every 1012 characters in the data. Finally fill with x’40’ characters to next 1014 increment. In this case, there is only one increment
00000000: 00 00 00 1C 54 68 69 73 20 69 73 20 66 69 72 73 ....This is firs
00000010: 74 20 72 65 63 6F 72 64 20 31 32 33 34 35 36 37 t record 1234567
00000020: 00 00 00 22 54 68 69 73 20 69 73 20 73 65 63 6F ..."This is seco
00000030: 6E 64 20 72 65 63 6F 72 64 20 41 41 41 41 42 42 nd record AAAABB
00000040: 42 42 42 31 32 33 00 00 00 00 40 40 40 40 40 40 BBB123....@@@@@@
00000050: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 @@@@@@@@@@@@@@@@
000003E0: 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 @@@@@@@@@@@@@@@@
000003F0: 40 40 40 40 40 40 @@@@@@
- class cardutil.mciipm.Block1014(file_obj)[source]
Bases:
object
1014 Blocker for file objects. Wrap around a file object. Return 1014 blocked data
- PAD_CHAR = b'@'
- class cardutil.mciipm.Unblock1014(file_obj: BinaryIO)[source]
Bases:
object
Unblocks 1014 blocked file objects. Wrap around a 1014 blocked file object. Return file like object providing only unblocked data
- class cardutil.mciipm.VbsReader(vbs_file: BinaryIO, blocked: bool = False)[source]
Bases:
object
The VbsReader class can be used to iterate through a VBS formatted file object record by record.
from cardutil.mciipm import VbsReader with open('vbs_file.bin', 'rb') as vbs_file: vbs_reader = VbsReader(vbs_file) for vbs_record in vbs_reader: print(vbs_record)
- record_number = 1
- last_record = None
- class cardutil.mciipm.IpmReader(ipm_file: BinaryIO, encoding: str | None = None, iso_config: dict | None = None, **kwargs)[source]
Bases:
VbsReader
IPM reader can be used to iterate through an IPM file
The file object must be in VBS format.
from cardutil.mciipm import IpmReader with open('vbs_in.bin', 'rb') as vbs_in: reader = IpmReader(vbs_in) for record in reader: print(record)
If the file required 1014 block format, then set the
blocked
parameter to True.from cardutil.mciipm import IpmReader with open('blocked_in.bin', 'rb') as blocked_in: reader = IpmReader(blocked_in, blocked=True) for record in reader: print(record)
- class cardutil.mciipm.IpmParamReader(param_file: BinaryIO, table_id: str, encoding: str | None = None, param_config: dict | None = None, expanded: bool = False, **kwargs)[source]
Bases:
VbsReader
IPM Param reader can be used to iterate through an IPM parameter extract file. The record is returned as a dictionary containing the parameter keys.
from cardutil.mciipm import IpmParamReader with open('param.bin', 'rb') as param_in: reader = IpmParamReader(param_in, table_id='IP0040T1') for record in reader: print(record)
If the parameter file is 1014 block format, then set the
blocked
parameter to True.from cardutil.mciipm import IpmParamReader with open('blocked_param.bin', 'rb') as param_in: reader = IpmParamReader(param_in, table_id='IP0040T1', blocked=True) for record in reader: print(record)
- class cardutil.mciipm.VbsWriter(out_file: BinaryIO, blocked: bool = False)[source]
Bases:
object
Writes VBS formatted files.
The writer can be used as follows:
>>> with io.BytesIO() as vbs_out: ... writer = VbsWriter(vbs_out) ... writer.write(b'This is the record') ... writer.close()
The close method must be issued to finalise the file by adding the zero length record which indicated the end of the file. The message is a byte string containing the data.
Alternatively, you can use as a context manager which will take care of the writer closure.
>>> with io.BytesIO() as vbs_out: ... with VbsWriter(vbs_out, blocked=True) as writer: ... writer.write(b'This is the record')
- write(record: bytes) None [source]
Add a new record to the VBS output file
- Parameters:
record – byte string containing data
- Returns:
None
- class cardutil.mciipm.IpmWriter(file_obj: BinaryIO, encoding: str | None = None, iso_config: dict | None = None, **kwargs)[source]
Bases:
VbsWriter
IPM writer can be used to write records to a Mastercard IPM file
>>> with io.BytesIO() as ipm_out: ... writer = IpmWriter(ipm_out) ... writer.write({'MTI': '1111', 'DE2': '9999111122221111'}) ... writer.close()
If the required file is 1014 block format, then set the
blocked
parameter to True.>>> with io.BytesIO() as ipm_out: ... writer = IpmWriter(ipm_out, blocked=True) ... writer.write({'MTI': '1111', 'DE2': '9999111122221111'}) ... writer.close()
You can provide the specific file encoding if required. All standard python encoding schemes are supported. Mainframe systems likely use
cp500
>>> with io.BytesIO() as ipm_out: ... writer = IpmWriter(ipm_out, encoding='cp500') ... writer.write({'MTI': '1111', 'DE2': '9999111122221111'}) ... writer.close()
Alternatively use as a context manager to ensure closure at end of processing
>>> with io.BytesIO() as ipm_out: ... with IpmWriter(ipm_out, encoding='cp500') as writer: ... writer.write({'MTI': '1111', 'DE2': '9999111122221111'})
- write(obj: dict) None [source]
Writes new record to IPM file
- Parameters:
obj – dictionary object containing ISO8583 elements.
See
cardutil.iso8583
for expected dict object keys.- Returns:
None
- cardutil.mciipm.unblock_1014(input_data: BinaryIO, output_data: BinaryIO)[source]
Unblocks a 1014 byte blocked file object
- Parameters:
input_data – 1014 blocked IPM file object
output_data – unblocked file object
- cardutil.mciipm.block_1014(input_data: BinaryIO, output_data: BinaryIO)[source]
Creates a 1014 byte blocked file object
- Parameters:
input_data – file object to be 1014 byte blocked
output_data – 1014 byte blocked file object
- cardutil.mciipm.vbs_list_to_bytes(byte_list: iter, **kwargs) bytes [source]
Convenience function for creating VBS byte strings (optionally blocked) from list of byte strings
- Parameters:
byte_list – a list containing byte string records
kwargs – any options to be passed to VbsWriter constructor. See
cardutil.mciipm.VbsWriter
- Returns:
single byte string containing VBS data.
- cardutil.mciipm.vbs_bytes_to_list(vbs_bytes: bytes, **kwargs) list [source]
Convenience function for unpacking VBS byte strings to byte string list
- Parameters:
vbs_bytes – single byte string containing VBS data
kwargs – any options to be passed to VbsReader constructor. See
cardutil.mciipm.VbsReader
- Returns:
a list containing byte string records
- cardutil.mciipm.ipm_info(input_data: BinaryIO) dict [source]
Use this function to inspect an IPM file and provide details
- Parameters:
input_data – The file like object of IPM data
- Returns:
a dictionary containing file information:
{ "isValidIPM": True, "reason": "If not valid, describes the reason" "isBlocked": True, "encoding": "latin1", }
cardutil.pinblock
Pinblock
There are many different pin block formats in use in the payment card industry. The pinblock module provides functions for working with the various pin blocks formats in a consistent way.
Available pin block formats:
How to use:
# create the pinblock instance. Inputs will vary depending on the format
>>> pb = Iso0PinBlock(pin='1234', card_number='1111222233334444')
>>> pb.pin
'1234'
# output pinblock bytes
>>> binascii.hexlify(pb.to_bytes())
b'041226dddccccbbb'
# create pinblock instance from bytes
>>> pb2 = Iso0PinBlock.from_bytes(pb.to_bytes(), card_number='1111222233334444')
>>> pb2.pin
'1234'
Pinblock mix-ins
Common operations associated with pin blocks include encryption and calculation of a pin verification value.
This module allows you to add pinblock encryption and pin verification calculators to a pinblock class through the use of mixins.
Creating pinblock objects
How to create pinblock class with encryption and pin verification support:
# use a predefined pinblock object
>>> Pinblock = Iso0TDESPinBlockWithVisaPVV
# or create your own class including required mix-ins
>>> class PinBlock(Iso0PinBlock, TdesEncryptedPinBlockMixin, VisaPVVPinBlockMixin):
... pass
# or use the type builtin to create the class
>>> PinBlock = type('MyPinBlock', (Iso0PinBlock, TdesEncryptedPinBlockMixin, VisaPVVPinBlockMixin), {})
# create the pinblock instance. Inputs will vary depending on the format
>>> pb = PinBlock(pin='1234', card_number='1111222233334444')
>>> pb.pin
'1234'
Pinblock encryption
The encryption mixin’s adds pin block encrption and decryption. Adds from_enc_bytes constructor and to_enc_bytes method.
Note
The use of an encryption mix-in requires the install of additional modules.
Use pip install cardutil[crypto]
.
Available pinblock encryption mix-ins:
cardutil.pinblock.AesEncryptedPinBlockMixin
How to encrypt and decrypt a pinblock:
# output encrypted pinblock bytes
>>> epb = pb.to_enc_bytes(key='00' * 16)
>>> binascii.hexlify(epb)
b'4c0906d10308871a'
# create new pinblock from encrypted pinblock bytes
>>> pb2 = PinBlock.from_enc_bytes(
... enc_pin_block=epb,
... card_number='1111222233334444',
... key='00' * 16)
>>> pb2.pin
'1234'
Pin verification values
The pin verification mixin’s add pin verification value calculators to the pinblock object. Adds to_pvv method.
Note
The use of a pin verification mix-in requires the install of additional modules.
Use pip install cardutil[crypto]
.
Available pin verification mix-ins:
How to generate pin verification value:
>>> pb.to_pvv(pvv_key='00' * 16)
'6264'
- class cardutil.pinblock.AbstractPinBlock(pin: str, *args: any, **kwargs: any)[source]
Bases:
ABC
Base PinBlock object class from which implementation will subclass
- property pin: str
The card pin as a string
- class cardutil.pinblock.Iso0PinBlock(pin: str, card_number: str | None = None, **kwargs: any)[source]
Bases:
AbstractPinBlock
ISO 9564-1 format 0, ANSI X9.8, Visa-1 and ECI-0
Pin block structure:
P1 = LLPPPPFFFFFFFFFF P2 = 0000CCCCCCCCCCCC PIN Block = P1 XOR P2
where:
* L = Length of pin * P = Pin * F = Fill, x'F' * C = Last 12 digits of card number (excluding check digit)
- class cardutil.pinblock.Iso4PinBlock(pin: str, random_value: int | None = None, **kwargs: any)[source]
Bases:
AbstractPinBlock
ISO 9564-1 Format 4 pin block
https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.csfb400/iso4_sum.htm
This block is 16 bytes long.
Pinblock structure:
CLPPPPaaaaaaaaAARRRRRRRRRRRRRRRR 441234aaaaaaaaaa837c658036105d19
where:
* C = type of pinblock, x'4' * L = Length of pin, x'4' to x'C' * P = Pin * a = additional Pin or x'A' * A = Fill, x'A' * R = Random values, x'0' to x'F'
- classmethod from_bytes(pin_block: bytes, *args: any, **kwargs: any) AbstractPinBlock [source]
Create pinblock object from pinblock bytes
- Parameters:
pin_block – bytes containing pin block
- Returns:
PinBlock object
- class cardutil.pinblock.TdesEncryptedPinBlockMixin[source]
Bases:
ABC
Adds 3DES encryption to pin blocks
- classmethod from_enc_bytes(enc_pin_block: bytes, key: str, *args: any, **kwargs: any)[source]
Create pinblock object using encrypted pinblock and card number
- Parameters:
enc_pin_block – bytes containing encrypted pin block
card_number – string containing card number
key – hex string containing pin protection key (PPK)
- Returns:
PinBlock object
- class cardutil.pinblock.AESEncryptedPinBlockMixin[source]
Bases:
ABC
Adds AES encryption to pin blocks
- classmethod from_enc_bytes(enc_pin_block: bytes, key: str, *args: any, **kwargs: any)[source]
Create pinblock object using encrypted pinblock and card number
- Parameters:
enc_pin_block – bytes containing encrypted pin block
key – hex string containing pin protection key (PPK)
- Returns:
PinBlock object
- class cardutil.pinblock.VisaPVVPinBlockMixin[source]
Bases:
ABC
Adds Visa PVV calculator to pin blocks
- abstract property pin: str
- cardutil.pinblock.calculate_pvv(pin: str, pvv_key: str, key_index: int, card_number: str)[source]
The algorithm generates a 4-digit PIN verification value (PVV).
- Parameters:
pin – the pin to calculate PVV for
pvv_key – the pvv key as a hex formatted string
key_index – the visa key index
card_number – the card number
- Returns:
pvv value
- class cardutil.pinblock.Iso0TDESPinBlockWithVisaPVV(pin: str, card_number: str | None = None, **kwargs: any)[source]
Bases:
Iso0PinBlock
,TdesEncryptedPinBlockMixin
,VisaPVVPinBlockMixin
- class cardutil.pinblock.Iso4AESPinBlockWithVisaPVV(pin: str, random_value: int | None = None, **kwargs: any)[source]
Bases:
Iso4PinBlock
,AESEncryptedPinBlockMixin
,VisaPVVPinBlockMixin
cardutil.card
Provides services related to card numbers.
check digits
masking
- cardutil.card.calculate_check_digit(card_number: str) str [source]
calculate luhn 10 check digit.
- Parameters:
card_number – number to calculate check digit for excluding check digit.
- Returns:
check digit value
- cardutil.card.validate_check_digit(card_number: str) str [source]
validate luhn 10 check digit
- Parameters:
card_number – number with check digit
- Returns:
None
- Raises:
AssertionError – Check digit is not valid
cardutil.key
- cardutil.key.get_zone_master_key(*key_parts: str) -> (<class 'str'>, <class 'str'>)[source]
combine keys components to get clear key
- Parameters:
key_parts – list of keys components to be combined
- Returns:
clear key, key check value
- cardutil.key.get_enc_zone_master_key(master_key: str, *key_parts: str) -> (<class 'str'>, <class 'str'>)[source]
cardutil.config
The config module contains the default configuration for the cardutil library.
The config consists of a single dictionary called config with a number of keys for different configuration data.
bit_config
The iso8583 module functions require configuration that details how to process the bitmap in a message.
Field definitions in an ISO8583 message are generally the same but there can be variations you may need to cater for with the different card schemes. The library supports provision of custom ISO8583 configuration.
If a configuration is not provided, the variable config['bit_config']
in config.py
provides a Mastercard
specific ISO8583 configuration.
The config is in the form of a python dictionary structured as follows
{
"1": {
"field_name": "Bitmap secondary",
"field_type": "FIXED",
"field_length": 8},
"2": {
"field_name": "PAN",
"field_type": "LLVAR",
"field_length": 0,
"field_processor": "PAN",
"field_processor_config": "",
"field_python_type": "string",
"field_date_format": "%y%m%d"}
}
The config dictionary contains a string key for each valid bit in the bitmap. For example, the config for bit 1 with have the key “1”.
The value for the bit is a dictionary containing the following values:
- field_name
description of the bit field
- field_type
- defines the ISO8583 field type
FIXED : field with fixed length
LLVAR : field with variable length - 2 character length value
LLLVAR : field with variable length - 2 character length value
- field_length
length of the field. Use zero for variable length fields.
- field_processor
- (optional) a process to apply to a field.
PAN
: For use with PAN fields. Mask PAN using first 6, last 4 patternPAN-PREFIX
: For use with PAN fields. Only get first 9 PAN numbers – prefixICC
: Mastercard ICC field. Adds TAGxxxx keys to outputPDS
: Mastercard PDS field. Processes PDSxxxx fieldsDE43
: Mastercard Merchant details field. Adds DE43 sub fields DE43_*
- field_processor_config
(optional) Where a field processor requires extra config, it can be placed in this field.
- For DE43 processor:
field should contain a regex to split the DE43 field up into components. The regex groups defined will be added to the returned dictionary. Use Python regex variation.
- field_python_type
- (optional) When processing between iso and python, determines the python object type.
string
: default if no type providedint
decimal
: use if your field has decimal placedatetime
: use if your field is a date
- field_date_format
(optional) If your field python type is datetime, then you use this to specify the format of the date in the iso record.
Use standard python datetime.strptime see strftime() and strptime() Behaviour.
Default format is “%y%m%d”
output_data_elements
This config defines the ISO elements that will be present in CSV file output. Is a list of the field keys
["MTI", "DE2", "DE3", "DE4", "DE12", "DE14", "DE22", "DE23", "DE24", "DE25", "DE26"]
mci_parameter_tables
Provides configuration required to extract IPM parameter extracts.
Expanded file format should be used - not compressed. The effective timestamp, table_id and active inactive code should not be included here - they are automatically included in the extract.
{
"IP0006T1": {
"card_program_id": {"start": 19, "end": 22},
"data_element_id": {"start": 22, "end": 25},
"data_element_name": {"start": 25, "end": 82},
"data_element_format": {"start": 82, "end": 85}
},
"IP0040T1": {}
}