KDE PIM/KItinerary/MAV Barcode: Difference between revisions
Appearance
< KDE PIM | KItinerary
Martonmiklos (talk | contribs) |
No edit summary |
||
| (23 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
{{Warning|The below is outdated and doesn't reflect the latest understanding of MÁV barcodes anymore! See [https://trainticket.wiki/ticket-standards/domestic-standards/hungary/ the train ticket wiki] instead.}} | |||
= Current Version (>= 2020?) = | = Current Version (>= 2020?) = | ||
== General Observation == | == General Observation == | ||
* Uses PDF417 barcode format, same content on PDF and in the official app. | * Uses PDF417 or Aztec barcode format, same content on PDF and in the official app. | ||
* Variable length. | * Variable length. | ||
* For domestic tickets only. | * For domestic tickets only. | ||
| Line 10: | Line 12: | ||
== Outer Structure == | == Outer Structure == | ||
* | * First byte is a u8 version number. | ||
* Second byte is a u8 signing key id. | |||
* Gzip-compressed payload using deflate compression, starting with the standard Gzip header 0x1f8b0800000000000000. | * Gzip-compressed payload using deflate compression, starting with the standard Gzip header 0x1f8b0800000000000000. | ||
* 256 remaining bytes, high entropy and length suggest a cryptographic signature. | * 256 remaining bytes, high entropy and length suggest a cryptographic signature. | ||
== Payload Structure == | == Payload Structure (v4) == | ||
* Seems byte- rather than bit-aligned. | * Seems byte- rather than bit-aligned. | ||
| Line 22: | Line 23: | ||
* Number encoding seems big endian. | * Number encoding seems big endian. | ||
* Content has a high amount of null bytes. | * Content has a high amount of null bytes. | ||
* Date/time values are encoded as seconds since | * Date/time values are encoded as seconds since 2017-01-01 00:00:00 CET (or 2016-12-31 23:00:00 UTC). Exception: traveler birth date. | ||
* | * Consists of a variable set of blocks. | ||
* | * Block layout is defined by information in the header block, there does not seem to be a TLV-like structure. | ||
** | * Observed block layouts: | ||
** | ** Header block. | ||
** | ** Either passenger or bike addon block. | ||
** | ** Trip block. | ||
** 0-2 reservation/surcharge blocks. | |||
=== Header | === Header Block === | ||
* Always present. | |||
* Always at offset 0. | |||
* 39 bytes long. | |||
{| class="wikitable" | {| class="wikitable" | ||
| Line 39: | Line 43: | ||
| 0 || 17 || string || ticket number || printed as "CIV" in the PDF | | 0 || 17 || string || ticket number || printed as "CIV" in the PDF | ||
|- | |- | ||
| 17 || 1 || null || ? || | | 17 || 1 || null || ? || null in all samples | ||
|- | |- | ||
| 18 || 2 || uint16 || UIC company code || issuer?, 0x0483 (1155) for | | 18 || 2 || uint16 || UIC company code || issuer?, 0x0483 (1155) for MÁV-Start | ||
|- | |- | ||
| 20 || 4 || time || issuing time || | | 20 || 4 || time || issuing time || | ||
|- | |- | ||
| 24 || | | 24 || 4 || float32 || price || in HUF | ||
|- | |||
| 28 || 1 || ? || ticket type?? || Bits 0x01 and 0x80 indicate presence of passenger and trip blocks. Can be null for e.g. bike place reservations. | |||
|- | |||
| 29 || 1 || null || ? || null in all samples | |||
|- | |||
| 30 || 1 || uint8 || number of reservation/surcharge blocks || | |||
|- | |||
| 31 || 4 || null || ? || null in all samples | |||
|- | |||
| 35 || 4 || ? || ? || only two distinct values observed in all samples | |||
|} | |||
=== Passenger Block === | |||
* Present when header block byte 28 has bit 0x80 set. | |||
* When present, located right after the header block (offset 39). | |||
* 68 bytes long. | |||
{| class="wikitable" | |||
! Offset !! Size !! Data Type !! Meaning !! Notes | |||
|- | |||
| 0 || 45 || string || passenger name || null terminated | |||
|- | |- | ||
| | | 45 || 4 || uint32 || passenger birth date || year * 10000 + month * 100 + day | ||
|- | |||
| 49 || 15 || null || ? || null in all samples | |||
|- | |||
| 64 || 4 || ? || ? || | |||
|} | |||
=== Bike Addon Block === | |||
* Present when header block byte 28 is 0x01. | |||
* When present, located right after the header block. | |||
* 4 bytes long. | |||
{| class="wikitable" | |||
! Offset !! Size !! Data Type !! Meaning !! Notes | |||
|- | |- | ||
| | | 0 || 4 || ? || ? || values seem fixed in all samples? | ||
|} | |||
=== Trip Block === | |||
* Present when header block byte 28 has bit 0x01 set. | |||
* Follows the passenger or bike addon block (offset 107 or 43). | |||
* 110 bytes long. | |||
{| class="wikitable" | |||
! Offset !! Size !! Data Type !! Meaning !! Notes | |||
|- | |- | ||
| | | 0 || 3 || uint24 || UIC departure station code || including the national prefix ('55' for HU) | ||
|- | |- | ||
| | | 3 || 3 || uint24 || UIC arrival station code || | ||
|- | |- | ||
| | | 6 || 90 || 10 * uint24 || UIC station codes || list of vias, null if not set | ||
|- | |- | ||
| | | 96 || 1 || text || class || "1" or "2" | ||
|- | |- | ||
| | | 97|| 1 || ? || ? || 0x01 in all samples | ||
|- | |- | ||
| | | 98 || 4 || time || time of validity/travel || | ||
|- | |- | ||
| | | 102 || 3 || uint24 || validity length || in minutes | ||
|- | |- | ||
| | | 105 || 1 || ? || ? || | ||
|- | |- | ||
| | | 106 || 4 || ? || ? || varying between samples | ||
|} | |} | ||
=== Seat Reservation Block === | === Seat Reservation Block === | ||
* 57 bytes long. | |||
* Also used for surcharge ("Pótjegy") blocks, in which case the fields for coach and seat numbers are all null. | |||
Also used for "Pótjegy" | * Order of surcharge and reservation blocks seems undefined if both are present. | ||
* Follow all other blocks, number of blocks determined by header block byte 30. | |||
{| class="wikitable" | {| class="wikitable" | ||
| Line 87: | Line 138: | ||
| 10 || 4 || time || time of validity/travel || | | 10 || 4 || time || time of validity/travel || | ||
|- | |- | ||
| 14 || 2 || uint16 || UIC company code || operator?, 0x0483 (1155) for | | 14 || 2 || uint16 || UIC company code || operator?, 0x0483 (1155) for MÁV-Start | ||
|- | |- | ||
| 16 || 5 || string || train number || null-terminated | | 16 || 5 || string || train number || null-terminated | ||
| Line 107: | Line 158: | ||
* <s>If the train number is included, one would expect at least the day of travel to be included as well.</s> | * <s>If the train number is included, one would expect at least the day of travel to be included as well.</s> | ||
* <s>Class: several candidate locations exist, but given it's small footprint we need a lot more samples to confirm one of those.</s> | * <s>Class: several candidate locations exist, but given it's small footprint we need a lot more samples to confirm one of those.</s> | ||
= Version 5/6 Format = | |||
== Outer Structure == | |||
* First byte is a u8 version number | |||
* Second byte is a u8 signing key id | |||
* 17 digit ticket numbers, ASCII | |||
* 1 null byte | |||
* 4 digit issuer UIC code (1155 for MAV), ASCII | |||
* GZip header and gzip-comprressed data | |||
== Inner Structure == | |||
The compressed data matches the format of the above format, with the following exceptions: | |||
* The header block misses its first 20 bytes, ie. the information that occur before the compressed data already. | |||
* Station codes are no longer UIC stations codes but Hungarian local ones (https://www.wikidata.org/wiki/Property:P11451). | |||
= Old Format (<2020?) = | = Old Format (<2020?) = | ||
Latest revision as of 16:22, 6 January 2026
Current Version (>= 2020?)
General Observation
- Uses PDF417 or Aztec barcode format, same content on PDF and in the official app.
- Variable length.
- For domestic tickets only.
- No similarities with a known ERA format.
Outer Structure
- First byte is a u8 version number.
- Second byte is a u8 signing key id.
- Gzip-compressed payload using deflate compression, starting with the standard Gzip header 0x1f8b0800000000000000.
- 256 remaining bytes, high entropy and length suggest a cryptographic signature.
Payload Structure (v4)
- Seems byte- rather than bit-aligned.
- String encoding is UTF-8.
- Number encoding seems big endian.
- Content has a high amount of null bytes.
- Date/time values are encoded as seconds since 2017-01-01 00:00:00 CET (or 2016-12-31 23:00:00 UTC). Exception: traveler birth date.
- Consists of a variable set of blocks.
- Block layout is defined by information in the header block, there does not seem to be a TLV-like structure.
- Observed block layouts:
- Header block.
- Either passenger or bike addon block.
- Trip block.
- 0-2 reservation/surcharge blocks.
Header Block
- Always present.
- Always at offset 0.
- 39 bytes long.
| Offset | Size | Data Type | Meaning | Notes |
|---|---|---|---|---|
| 0 | 17 | string | ticket number | printed as "CIV" in the PDF |
| 17 | 1 | null | ? | null in all samples |
| 18 | 2 | uint16 | UIC company code | issuer?, 0x0483 (1155) for MÁV-Start |
| 20 | 4 | time | issuing time | |
| 24 | 4 | float32 | price | in HUF |
| 28 | 1 | ? | ticket type?? | Bits 0x01 and 0x80 indicate presence of passenger and trip blocks. Can be null for e.g. bike place reservations. |
| 29 | 1 | null | ? | null in all samples |
| 30 | 1 | uint8 | number of reservation/surcharge blocks | |
| 31 | 4 | null | ? | null in all samples |
| 35 | 4 | ? | ? | only two distinct values observed in all samples |
Passenger Block
- Present when header block byte 28 has bit 0x80 set.
- When present, located right after the header block (offset 39).
- 68 bytes long.
| Offset | Size | Data Type | Meaning | Notes |
|---|---|---|---|---|
| 0 | 45 | string | passenger name | null terminated |
| 45 | 4 | uint32 | passenger birth date | year * 10000 + month * 100 + day |
| 49 | 15 | null | ? | null in all samples |
| 64 | 4 | ? | ? |
Bike Addon Block
- Present when header block byte 28 is 0x01.
- When present, located right after the header block.
- 4 bytes long.
| Offset | Size | Data Type | Meaning | Notes |
|---|---|---|---|---|
| 0 | 4 | ? | ? | values seem fixed in all samples? |
Trip Block
- Present when header block byte 28 has bit 0x01 set.
- Follows the passenger or bike addon block (offset 107 or 43).
- 110 bytes long.
| Offset | Size | Data Type | Meaning | Notes |
|---|---|---|---|---|
| 0 | 3 | uint24 | UIC departure station code | including the national prefix ('55' for HU) |
| 3 | 3 | uint24 | UIC arrival station code | |
| 6 | 90 | 10 * uint24 | UIC station codes | list of vias, null if not set |
| 96 | 1 | text | class | "1" or "2" |
| 97 | 1 | ? | ? | 0x01 in all samples |
| 98 | 4 | time | time of validity/travel | |
| 102 | 3 | uint24 | validity length | in minutes |
| 105 | 1 | ? | ? | |
| 106 | 4 | ? | ? | varying between samples |
Seat Reservation Block
- 57 bytes long.
- Also used for surcharge ("Pótjegy") blocks, in which case the fields for coach and seat numbers are all null.
- Order of surcharge and reservation blocks seems undefined if both are present.
- Follow all other blocks, number of blocks determined by header block byte 30.
| Offset | Size | Data Type | Meaning | Notes |
|---|---|---|---|---|
| 0 | 3 | uint24 | UIC departure station code | |
| 3 | 3 | uint24 | UIC arrival station code | |
| 6 | 4 | ? | ? | |
| 10 | 4 | time | time of validity/travel | |
| 14 | 2 | uint16 | UIC company code | operator?, 0x0483 (1155) for MÁV-Start |
| 16 | 5 | string | train number | null-terminated |
| 21 | 1 | ? | ? | 0x01 in all samples |
| 22 | 3 | string | coach number | null terminated |
| 25 | 2 | uint16 | seat number | |
| 27 | 2 | uint16 | seat number | repeated from byte 25/26? |
| 29 | 28 | null | ? | null bytes in all samples |
Missing/Suspected Information
Station names are not included, but station codes might be. UIC station numbers (possibly without the country prefix) would be the obvious suspect, given the MÁV website uses those as well.If the train number is included, one would expect at least the day of travel to be included as well.Class: several candidate locations exist, but given it's small footprint we need a lot more samples to confirm one of those.
Version 5/6 Format
Outer Structure
- First byte is a u8 version number
- Second byte is a u8 signing key id
- 17 digit ticket numbers, ASCII
- 1 null byte
- 4 digit issuer UIC code (1155 for MAV), ASCII
- GZip header and gzip-comprressed data
Inner Structure
The compressed data matches the format of the above format, with the following exceptions:
- The header block misses its first 20 bytes, ie. the information that occur before the compressed data already.
- Station codes are no longer UIC stations codes but Hungarian local ones (https://www.wikidata.org/wiki/Property:P11451).
Old Format (<2020?)
General Structure
- QR code containing a hex string
- content of the hex string is zlib-compressed, no header bytes
- result of decompression is a UTF-8 encoded string
- the first 512 bytes appear to be a hex string again, length and entropy suggest a cryptographic signature
- the second part looks like a '!' delimited list
Content
| Index | Format | Meaning | Notes |
|---|---|---|---|
| 0 | ~<number> | ticket number | printed as "CIV" in the PDF |
| 1 | passenger name | ||
| 2 | yyyy.MM.dd | passenger birth date | |
| 3 | total price | in HUF | |
| 4 | ? | ? | "P05" |
| 5 | yyyy.MM.dd hh:mm | begin of validity | |
| 6 | yyyy.MM.dd hh:mm~v | end of validity | no idea what the literal '~v' means there |
| 7 | MÁV <number> | travel distance | |
| 8 | '-' separated string | vias | |
| 9 | departure station name | ||
| 10 | arrival station name | ||
| 11 | empty? | ||
| 12 | empty? | ||
| 13 | empty? | ||
| 14 | empty? | ||
| 15 | train number | number only, not product/category prefix | |
| 16 | class | ||
| 17 | yyyy.MM.dd~m | day of travel? | |
| 18 | string | discount_type | e.g. "Teljesárú" (full price) |
| 19 | <number>~h | ticket price | in HUF |
| 20 | departure station name | possibly for the seat reservation | |
| 21 | arrival station name | possibly for the seat reservation | |
| 22 | yyyy.DD.mm | day of travel? | |
| 23 | hh:mm | time of departure | |
| 24 | empty? | ||
| 25 | empty? | ||
| 26 | train number | incl. product/category prefix | |
| 27 | coach number | ||
| 28 | seat number | ||
| 29 | number | price of reservation | in HUF |
| 30 | extension-ticket | "Helyjegy", "Pót- és helyjegy" | |
| 31 | MÁV <number> | ? | same as entry 7, not always present |
