Jump to content

KDE PIM/KItinerary/MAV Barcode: Difference between revisions

From KDE Community Wiki
Vkrause (talk | contribs)
Created page with "= General Observation = * Uses PDF417 barcode format. * Variable length. * For domestic tickets only. * No similarities with a known ERA format. = Outer Structure = * Two b..."
 
Vkrause (talk | contribs)
 
(39 intermediate revisions by 2 users not shown)
Line 1: Line 1:
= General Observation =
= Current Version (>= 2020?) =


* Uses PDF417 barcode format.
== General Observation ==
 
* Uses PDF417 barcode format, same content on PDF and in the official app.
* Variable length.
* Variable length.
* For domestic tickets only.
* For domestic tickets only.
* No similarities with a known ERA format.
* No similarities with a known ERA format.


= Outer Structure =
== Outer Structure ==


* Two byte header, fixed 0x0403.
* Two byte header, fixed 0x0403.
Line 12: Line 14:
* 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 ==
 
Note: this is based on very few samples so far and thus isn't very reliable yet!


* Seems byte- rather than bit-aligned.
* Seems byte- rather than bit-aligned.
* String encoding is UTF-8.
* String encoding is UTF-8.
* Number encoding seems big endian.
* Number encoding seems big endian.
* Variable length, observed between 217 and 274 bytes.
* Content has a high amount of null bytes.
* 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.
{| class="wikitable"
! 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 || 1 || ? || ticket type?? || 'C' for bike addon tickets, 'B' or 'D' for regular tickets (with or without seat reservation, 'E' for tickets with surcharge (and with or without seat reservation), null for "connecting ticket to season ticket"
|-
| 25 || 2 || ? || ? ||
|-
| 27 || 1 || null || ? || null in all samples
|-
| 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 || 1 || null || ? || null in all samples
|-
| 103 || 1 || ? || ? || 0x00 or 0x05 in all samples
|-
| 104 || 1 || ? || ? || varying between samples, 0x01 or 0xef observed
|-
| 105 || 1 || ? || ? || often but not always the same as byte 103
|-
| 106 || 4 || ? || ? || varying between samples
|}
=== Seat Reservation Block ===


== Layout ==
* 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.


{| class="wikitable"
{| class="wikitable"
! Offset !! Size !! Content Type !! Meaning !! Notes
! 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 ===
 
* <s>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.</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>
 
= Alternative Format (>= 2022?) =
 
== General Observations ==
 
* PDF looks generally similar to the above, with slightly smaller PDF417 codes
* Unclear whether this supersedes the above format, or whether this only applies to a certain type of ticket.
 
== Outer Structure ==
 
* Two byte header, fixed 0x0501. Since mid-2024 also 0x0601 (with everything else still matching). Maybe this is some kind of a version indicator?
* 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 in the trip block don't seem to be UIC station codes, but unidentified 4 digit values.
 
= 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 ==
 
{| class="wikitable"
! 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
|-
|-
| 0 || 17 || string || ticket number || printed as "CIV" on the PDF, numeric
| 22 || yyyy.DD.mm || day of travel? ||
|-
|-
| 17 || 22 || ? || ? ||
| 23 || hh:mm || time of departure ||
|-
|-
| 39 || 45 || string || passenger name || null terminated
| 24 || || || empty?
|-
|-
| 84 || 4 || uint32 || passenger birth date || year * 10000 + month * 100 + day
| 25 || || || empty?
|-
|-
| 88 || 128 || ? || ? ||
| 26 || || train number || incl. product/category prefix
|-
|-
| 216 || 17 || ? || ? || bytes 216 and following are only present on tickets with seat reservations
| 27 || || coach number ||
|-
|-
| 233 || 5 || string || train number || null-terminated
| 28 || || seat number ||
|-
|-
| 238 || 2 || ? || coach number? || numeric or string encoding, too few samples to be sure
| 29 || number || price of reservation || in HUF
|-
|-
| 240 || 6 || numeric || seat number? || too few samples to be sure
| 30 || || extension-ticket || "Helyjegy", "Pót- és helyjegy"
|-
|-
| 246 || 28 || null || ? || null bytes in all samples
| 31 || MÁV <number> || ? || same as entry 7, not always present
|}
|}

Latest revision as of 15:18, 8 August 2024

Current Version (>= 2020?)

General Observation

  • Uses PDF417 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

  • Two byte header, fixed 0x0403.
  • 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

  • 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 1 ? ticket type?? 'C' for bike addon tickets, 'B' or 'D' for regular tickets (with or without seat reservation, 'E' for tickets with surcharge (and with or without seat reservation), null for "connecting ticket to season ticket"
25 2 ? ?
27 1 null ? null in all samples
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 1 null ? null in all samples
103 1 ? ? 0x00 or 0x05 in all samples
104 1 ? ? varying between samples, 0x01 or 0xef observed
105 1 ? ? often but not always the same as byte 103
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.

Alternative Format (>= 2022?)

General Observations

  • PDF looks generally similar to the above, with slightly smaller PDF417 codes
  • Unclear whether this supersedes the above format, or whether this only applies to a certain type of ticket.

Outer Structure

  • Two byte header, fixed 0x0501. Since mid-2024 also 0x0601 (with everything else still matching). Maybe this is some kind of a version indicator?
  • 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 in the trip block don't seem to be UIC station codes, but unidentified 4 digit values.

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