ipmkit
Lossless Mastercard IPM tooling. A drop-in replacement for cardutil with its bugs fixed.
IPM → CSV → IPM gives a file identical to the original, byte for byte. Nothing is converted, trimmed or masked.
Install
pip install ipmkit
The PIN block and key modules (ipmkit.pinblock, ipmkit.key) also need pip install "ipmkit[crypto]".
Convert files
ipmkit ipm2csv T112.ipm -o T112.csv
ipmkit csv2ipm T112.csv -o T112_rebuilt.ipm
ipmkit compare T112.ipm T112_rebuilt.ipm
The encoding is always EBCDIC cp037 unless you pass --encoding (for example --encoding latin_1 for an ASCII file). ipm2csv warns if the first MTI looks like ASCII. 1014 blocking is detected automatically; use --blocked/--unblocked to override it, or --config-file for a custom field config.
ipm2csv then verifies that the CSV rebuilds the identical file. If it doesn't, it fails instead of handing you a lossy CSV.
Speed on large files
Files with 20,000 or more records are split into chunks and converted on several CPU cores, one process per core up to 8. The chunks are joined back in their original order, so records keep their exact order and the CSV is byte-identical to a single-process run. Use --workers N to choose the number of processes (--workers 1 for a single process).
100,002 records (72 MB, cp037, 1014-blocked), on a 10-core Mac:
--workers 1 |
default | |
|---|---|---|
ipm2csv (includes verification) |
5.8 s | 1.8 s |
ipm2csv --no-verify |
3.6 s | 1.0 s |
csv2ipm |
2.4 s | 0.85 s |
What's in the CSV
| Column | Content |
|---|---|
ROW |
META for the file details row, otherwise the record number |
MTI |
message type, exactly as in the file |
BITMAP |
record bitmap in hex. A bit that is set marks the field present, even if the cell is empty |
DEn |
field value exactly as in the file: spaces and leading zeros kept, dates not converted. Only the fields used in the file get a column |
DE55 |
ICC data as hex, exactly as in the file |
DE48, DE62, DE123–DE125 |
the PDS data exactly as in the file |
RAW |
hex of a whole record that couldn't be parsed. It goes back unchanged. In the META row it holds the file details (encoding, blocking, end-of-file marker, trailing bytes) |
A text value that can't sit safely in a CSV (control characters, or text that itself starts with hex:) is written as hex: followed by its bytes in hex.
Opening the CSV in Excel and saving it drops leading zeros and reformats values. Edit it with a text editor, or import every column as Text.
Editing the CSV
You can change values. Lengths for LLVAR/LLLVAR fields are recalculated and the 1014 blocking is redone. A fixed-length field with the wrong length is rejected, never silently padded or cut. To remove a field, clear its cell and its bit in BITMAP (or clear the BITMAP cell to have it recalculated).
Header and trailer generation
When the CSV has no file header (MTI 1644, DE24 697) or no trailer (MTI 1644, DE24 695), csv2ipm generates them:
- header: DE48 = PDS 0105 File ID + PDS 0122 processing mode (
--processing-mode:Pproduction, the default, orTtest); DE71 =00000001 - trailer: DE48 = PDS 0105 + PDS 0301 (sum of all DE4 amounts, 16 digits) + PDS 0306 (message count including header and trailer, 8 digits); DE71 = message count
The File ID is copied from the existing header or trailer if there is one. Otherwise it is built from --file-type, --file-date (YYMMDD, default today), --processor-id and --file-seq. A CSV made from an IPM file that had no header or trailer remembers that, so csv2ipm doesn't add one. --no-header-trailer turns generation off.
A hand-made CSV only needs MTI and DEn columns. It is written as cp037, not blocked, unless you pass --encoding / --blocked. A CSV made by ipm2csv remembers its encoding in the META row, and --encoding overrides that too.
Python API
from ipmkit import ipmcsv
ipmcsv.convert_ipm_to_csv('in.ipm', 'out.csv') # verified
ipmcsv.convert_csv_to_ipm('out.csv', 'new.ipm')
ipm = ipmcsv.read_ipm(open('in.ipm', 'rb').read()) # IpmFile: records with raw field bytes
ipm.records[1].fields[42] # b'MERCHANT0000001'
data = ipmcsv.write_ipm(ipm)
cardutil compatibility
ipmkit.iso8583, ipmkit.mciipm, ipmkit.card, ipmkit.pinblock, ipmkit.key and ipmkit.config have the same API as cardutil 0.7.3. Change cardutil to ipmkit in your imports. The library functions (loads, dumps, IpmReader, IpmWriter) keep cardutil's latin_1 default so existing code behaves the same. The commands that read or write IPM files (mci_ipm_to_csv, mci_csv_to_ipm, mci_ipm_param_to_csv) default to cp037. The cardutil test suite passes against ipmkit, with one test changed because a value too long for its field now raises an error instead of being cut off.
ipmkit installs the same command names as cardutil (mci_ipm_to_csv, mideu and others). If both packages are installed in one environment, whichever was installed last provides those commands, so use separate environments if you need both.
Fixes (details in ANALYSIS.md):
- messages with only a primary bitmap can be read; DE128 is processed
- values too long for a field raise an error instead of being cut off or corrupting the length prefix
dumps()no longer changes the dict you pass in; too much PDS data raises a clear error- DE55 is parsed as real BER-TLV (multi-byte tags such as
DF01, long-form lengths) - 10–12 digit PINs work in ISO-0/ISO-4 PIN blocks
- Luhn validation works under
python -O; short PANs are masked safely loadsis about 2.7× faster (it no longer builds a hex dump of every message when debug logging is off)
License
MIT. Based on cardutil, Copyright (c) 2019-2025 Anthony Delosa.
Metadata
Release files for ipmkit 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ipmkit-0.1.1.tar.gz | 68.6 kB | With provenance | Details for ipmkit-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ipmkit-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for ipmkit-0.1.1-py3-none-any.whl |
Total release size: 127.8 kB
Release files / ipmkit-0.1.1.tar.gz
| Download URL | ipmkit-0.1.1.tar.gz |
|---|---|
| Size | 68.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
81ff6757c9a428bb96c4a74b9325b5cb5f29d99fb80917ed3fcdd37aa2a0c419
|
|
BLAKE2b-256 checksum How to use checksums |
1c50b6079aba2d34eab35c0b8a286db8562f28243d3c9304e94862783570040d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / ipmkit-0.1.1-py3-none-any.whl
| Download URL | ipmkit-0.1.1-py3-none-any.whl |
|---|---|
| Size | 59.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60699fe75fdc3f678543cb449c68cff70e5ccbb23de6581bb8b2468ca83da283
|
|
BLAKE2b-256 checksum How to use checksums |
46522069342c9c0baed2cd7275239af6a44cfedff3115e71e4422b6dbed21831
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for ipmkit 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ipmkit-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.