adelosa / cardutil

Payment cards tools including ISO8583 parser and Mastercard IPM files processing
MIT License
24 stars 4 forks source link

Question - IpmWriter - DE55 TAG9F10 #18

Closed elisandromurlikydock closed 3 months ago

elisandromurlikydock commented 3 months ago

Hello, I have a question about writing chip data tags using the IpmWriter function.

I have, for example, all of them open: "TAG82": "1980", "TAG95": "0000000001", "TAG9A": "240327", "TAG9C": "00", "TAG5F2A": "0986", "TAG9F10": "0110a04003220000000000000000000000", ... etc

I'm using len(bytes.fromhex(TAG9F10)), for all tags len is ok, only for TAG 9F10 the value is returned in size 17, but I don't understand how size 11 is used in ipm.

adelosa commented 3 months ago

From what I can tell from your code, this is not an issue with cardutil, but with the way you are creating the DE55 element.

icc_data_final += key.replace("TAG", "").lower() + str("{:02d}".format(len(bytes.fromhex(value)))) + str(value)

Review this line of code to make sure you set the length correctly. You will have issues for any fields over 9 bytes long. Clue: Length is binary, not decimal.

By the way, why a new account @elisandrom?

elisandromurlikydock commented 3 months ago

Hello @adelosa , now I created the file correctly. Thank you for your help.

Oh about the user, my original is the other one, I was logged in for a long time and forgot to log out :D

adelosa commented 3 months ago

For anyone else wanting to write ICC tag data, I quite like the approach of your code. Define a dictionary with the tag elements:

dictDE55tags = {
    "TAG82": "1980",
    "TAG95": "0000048001",
    "TAG9A": "240327",
    "TAG9C": "00"
}

Then a function to transform that to bytes (the subject of the question, fixed below)

icc_data_final = str()
for key, value in dictDE55tags.items():
    icc_data_final += key.replace("TAG", "").lower() + str("{:02x}".format(len(bytes.fromhex(value)))) + str(value)
de55_bytes = bytes.fromhex(icc_data_final)

You can then add that to the DE55 key in a dict you are writing to IPM

ipm_message = {"DE55": de55_bytes}
with open("ipm_filename", 'wb') as ipm_out_file:
    writer = IpmWriter(ipm_out_file, encoding='cp500', blocked=True)
    writer.write(ipm_message)

I may add support for writing DE55 from the TAG elements. Right now, TAG elements are ignored so you have to populate DE55 if you want icc data included.

elisandromurlikydock commented 3 months ago

Yes, I created a json with the tags:

[
    {
        "tx": {
            "MTI": "1240",
            "DE24": "200",
            "DE94": "032201",
            "DE55": {
                     "TAG82": "1980",
                     "TAG95": "0000048001",
                     "TAG9A": "240327",
                     "TAG9C": "00",
                     "TAG5F2A": "0986",
                                         "TAG9F10": "0110a04003220000000000000000000000",
                    ....
            },
            others...
        }
    }
]

And when I'm analyzing the tags, I check if it's a DE55, so I do the conversion: Example:

_IPM_FINAL = []
for elm in json:
    elmTx = elm['tx']
    dictTx = {}
    for key, value in elmTx.items():
        if key == 'DE55':
            icc = str()
            for keyEsp, valueEsp in value.items():
                sizeBytes = len(bytes.fromhex(valueEsp))
                icc += str(keyEsp.replace("TAG", "").lower()) + (f'{sizeBytes:x}').zfill(2) + str(valueEsp)
            dictTx[key] = bytes.fromhex(icc)
        else:
            dictTx[key] = value

    _IPM_FINAL.append(dictTx)

with open(path_ipm_out, 'wb') as ipm_out:
    writer = IpmWriter(ipm_out, encoding='cp500', blocked=True)
    writer.write_many(_IPM_FINAL)
    writer.close()

I also analyze whether it is an element with PDS and process it in the code together, everything worked well :D