Расшифровываем PDF417 без подсказок

2357face92e8d340d141c6eec74410a1.svg

Вы ведь обращаете внимание на зашифрованную информацию? Все эти бесконечные штрих, QR и другие коды, встречающиеся буквально на каждом шагу. Товары в магазинах, кассовые чеки, таблички с информацией для туристов, квитанции для оплаты коммунальных платежей и так далее.

Сегодня я хочу рассказать о коде, который регулярно можно увидеть в аэропортах, а если точнее — на посадочных талонах. Герой этой истории — код PDF417. Давайте посмотрим, можно ли расшифровать такой код вручную и без подсказок? Какой информацией нужно обладать для этого? Сколько на это понадобится времени? Всё это и множество другого ждёт вас в данной публикации. Начнём!

Внимание, под катом — 30 изображений общим размером в 16.3 МБ.


Оглавление


Оглавнение


01. Вступление и о повествовании

(Назад | Оглавление | Вперёд)

Первый вопрос, на который я сразу же хочу ответить, — «Зачем это нужно?». Ответ достаточно прост — в какой-то момент мне было скучно и я задумался. А после — не мог остановиться.

Данная история покажет, как и в какой последовательности я определял части разных PDF417 кодов. Естественно, не все предпринятые мной шаги были рациональными или принесли какой-либо результат. Однако, ещё в самом начале я решил, подсматривать в ответ — не так интересно, как промучиться во много раз больше и убить гору времени даже не на стопроцентный результат.

Весь рассказ поделен на разделы, согласно этапам. Если какой-то из них покажется вам слишком многословным, просто переходите к следующему.


02. Базовая информация

(Назад | Оглавление | Вперёд)

Итак, PDF417 коды печатают на всех посадочных талонах при регистрации в аэропорту. Кроме того, именно их присылают по электронной почте при самостоятельной регистрации онлайн. Иногда такие коды получают и при регистрации через мобильные приложения авиакомпаний, но в таких случаях чаще используют более компактные коды.

Начнём с кода #01 одного из моих посадочных талонов авиакомпании UIA (Рис. 1). Это небольшое TIFF изображение. Его наименьший элемент — прямоугольник 2×4 пикселя. Назовём его символом. Код состоит из 6 блоков шириной в 17 и высотой в 12 символов. В каждом блоке первый символ — заполненный, а последний — пустой.


17cbdeaac2b3b0d514bec7622b57d556.svg

Рис. 1. Пример стандартного PDF417 кода прямиком с одного из моих посадочных талонов (#01)

Присмотревшись к любому блоку, можно заметить, что строка в нём — набор из 4 заполненных и 4 пустых последовательностей символов, общей длиной в 17 символов. Разгадка названия кода оказалась весьма прозаичной — это параметры одного блока информации. Такую последовательность из 17 символов будем называть словом.

По краям кода расположены ещё два блока (похожие на штрихкод) шириной в 17 (левый) и 18 (правый) символов. Вероятно, они лишь определяют тип кода, поскольку не меняются между строками. В таком случае, полезной информации они не несут.

Если сравнить несколько разных PDF417 кодов одного перевозчика (Рис. 2), можно выяснить, что первый и последний блоки в них всех одинаковы. Очевидно, они содержат сервисную информацию или же параметры записи данных.


76bca72bbd8678b3ccbf7f82637fe828.svg

Рис. 2. Пять разных PDF417 кодов с посадочных талонов одной авиакомпании (#01: #05)


03. Применение PDF417 кодов

(Назад | Оглавление | Вперёд)

Данные коды посадочных талонов проверяют перед тем, как пустить вас на рейс. В таком случае, код должен содержать как минимум номер рейса. Последний представляет из себя комбинацию 2 букв (код авиакомпании) и 4 цифр (порядковый номер рейса). Если порядковый номер меньше тысячи, его иногда указывают без 0 вначале. В итоге, в каждом коде должно содержаться 5–6 символов, ответственных за номер рейса.

Кроме того, всегда перед посадкой в самолёт проверяют ваши документы (а иногда ещё и переспрашивают ваше имя, особенно когда прочитать его не так просто). Есть два варианта, где взять эту информацию:


  • Имя содержится прямо в коде;
  • В коде есть номер билета, а все данные о пассажире получают по этому номеру онлайн.

Второй вариант лично мне видится менее реалистичным. Несмотря на то, что номер билета и не так велик (около 13 цифр, хотя и может быть длиннее в зависимости от перевозчика), потребовалось бы при считывании получать данные каждого билета от аэропорта или авиакомпании. Это было бы немного (или не немного) дольше и сильно повлияло бы на скорость пропуска сотни пассажиров (что бывает критично для недорогих перевозчиков).

Для упрощения, предположим, что имя на самом деле зашифровано прямо в коде. В моем случае — это 18 символов. Поскольку необходим ещё и разделитель между именем и фамилией, то все 19.


04. Позиционируем данные

(Назад | Оглавление | Вперёд)

Поскольку я не знаю, как именно информация записана в коде, попробуем найти области одинаковых слов в разных кодах (Рис. 3).

Далее я буду называть слова по их положению в коде, указывая вначале индекс ряда, а после слеша — индекс слова в ряду. Последовательность рядов или слов объединяются двоеточием.

В каждом из 5 кодов содержится 14 одинаковых слов вначале (0/0: 3/1) и ещё 9 слов разбросаных по коду (5/2:3, 6/1, 7/0:1, 8/0, 9/1:3). То есть 25 слов, общих для всех посадочных талонов. При этом, слова 9/1:3 одинаковые. Возможно, они просто показывают конец полезной информации в коде, поскольку далее все слова во всех пяти кодах разные.

Итак, у нас есть достаточно длинный набор из 14 тех же слов в каждом случае. Предположим, здесь спрятано имя. Кроме того, там должен быть минимум один разделитель — между именем и фамилией. Возможно, есть также и второй — в конце, чтобы отделить остальную информацию. Поскольку каждое слово точно не может содержать лишь один символ, оно, вероятно, содержит два. Тогда я могу поместиться в 9 слов + 1 слово разделитель.

Дальше, где-то должна быть представлена информация о рейсе. #01, #02 и #04 — это коды, соответствующие одному и тому же рейсу PS 0336, а #03 и #05 — рейсу PS 0335. Одинаковыми для этих двух групп являются слова:


  • 4/3: 5/1 (3 слова);
  • 6/1.

Поскольку нам нужно ещё и наличие 2 букв авиакомпании где-то рядом, нам подходит второй вариант, расположенный сразу после повторяющихся для всех кодов слов. Хочу обратить внимание на тот факт, что перед 6/1 присутствует сразу 3 одинаковых во всех случаях слова.

В кодах можно заметить слова, одинаковые для #02 и #03, а также другие одинаковые для #04 и #05. Это 5 слов в диапазоне 3/2: 4/2. Для этих пар общее — номер брони, состоящий из 6 символов (латиница + цифры в произвольном порядке).


287fd161381aa95e45a756ebf295a334.svg

Рис. 3. Построчное сравнение кодов #01: #05


05. Добавляем новых PDF417

(Назад | Оглавление | Вперёд)

Следующим шагом было добыть ещё несколько кодов с посадочных талонов для проверки части предположений. С этим мне помогла моя жена, разрешив взять данные из её посадочных талонов на рейс PS 0336 (#06) и ещё две пары PS 0335 / PS 0336 (#07: #10). При этом, #09: #10 относятся к тому же бронированию билетов, что и #04: #05. Интересен тот факт, что код #06 на одну строку короче и состоит лишь из 11 строк. Все 5 кодов приведены на Рис. 4.

Сразу можно заметить, что #06 выбивается из этой группы из-за нестыковок в правом и левом информационных блоках. При этом, им свойственна цикличность каждые 3 строки. Так каждое $3k+1$ левое и $3k+2$ правое информационное слово отличаются от других кодов ($k=0, 1, 2, ...$). Одновременно, остальные 4 из 6 слов совпадают (оба $3k$, правое $3k+1$ и левое $3k+2$). Цикличность строк можно заметить так же в том, что для строк $3k$ левое и правое слова совпадают.

Коды #07: #10 отлично подходят под все предыдущие предположения. Это касается как вероятного размещения кода бронирования, так и информации одинаковой для одного номера рейса. Кроме того, первые 14 слов кода, в которых вероятно размещается имя, тоже одинаковы для четырёх кодов.

И это всё перестаёт работать для более короткого кода #06. Конечно, не совсем всё, 13 слов в диапазоне 0/1: 3/1 остаются такими же, как и для других кодов. При этом, все остальные слова не соответствуют другим кодам. Но на данный момент нам это не важно.

Стоит отметить, что самое первое слово в коде отличается от других кодов, хотя вся остальная строка такая же. Значит это слово содержит информацию о всём коде. Возможно, размер кода (количество слов или строк).


98d3b137368f41a7be42b205a7337870.svg

Рис. 4. Построчное сравнение кодов #06: #10


06. Уточняем позиционирование

(Назад | Оглавление | Вперёд)

Итак, у нас есть 9 и ещё 1 код. Давайте сравним интересующие нас блоки и попробуем определить положение информации в коде. Возьмём чуть больше чем половину кода. Ту, где по моему мнению находится имя, номер брони и номер рейса (Рис. 5).

Как видно, слова 0/1:2 одинаковы для нас обоих. Потому пришлось воспользоваться тем, что я именую логикой. Где-то давно я предположил, что каждое слово может содержать 2 буквы. Если бы была всего 1 буква на слово, то в 11 букв и два разделителя поместились бы даже не все фамилии жителей Земли. Если 3 или больше — в моём коде наблюдались бы одинаковые слова для большого количества «пробелов» (пустых символов) перед началом следующей значимой информации кода.

Для нас двоих совпадают только 2 первые буквы в именах, а фамилии разные. При этом в моём коде слова 1/0 и ½ одинаковы. Предположив, что метод передачи данных в одной строке одинаков, это означает повторение двух пар символов, разделённых ещё одной парой. Это было не сложно.

Итак, фамилия пасажира начинается со слова 0/3. После неё, вероятно, идёт разделитель, а потом — имя и ещё раз разделитель. В таком случае, максимум информации о пасажире в подобном коде — 20 символов, а минимум 2 символа — разделители. Но пока оставим имя в покое.

Следующая часть кода, предположительно, номер бронирования. Этому свидетельствуют 5 одинаковых слов для 4 билетов из одной брони (#04, #05, #09, #10). Кроме того, слово 3/2 для #07 и #08 тоже совпадает со словами в перечисленной четвёрке.

Номера бронирования для всех билетов:


  • SNK79J (#01);
  • RUKG4T (#02, #03);
  • LW2BUW (#04, #05, #09, #10);
  • SLRSFS (#06);
  • LWJ2JA (#07, #08).

Для приведённых шести общее — LW. Слово 3/2 совпадает также для #01 и #06, у которых общая лишь S. Предположим, что это слово скрывает лишь 1 символ (либо разделитель и 1 символ).

Слова 4/3: 5/1 всё также соответствуют каждому рейсу. Что это за 3–6 символов остаётся неочевидным.

Поскольку я уже почти уверен, что за одним словом скрывается до двух символов, слова 6/0:1 соответствуют номеру рейса (03 и 36 либо 35). Слова 5/2:3 содержат код авиаперевозчика PS и один либо 2 разделителя.


133b8442439aebb7f9451e2a8aa399fa.svg

Рис. 5. Уточнение позиций данных #01: #10 для первых 7 рядов


07. Метод подбора

(Назад | Оглавление | Вперёд)

Давайте посмотрим, что же осталось во второй половине кода. Как я уже упоминал ранее, последние 8 слов вряд ли содержат полезную информацию, а перед ними находится 3 слова, показывающие конец данных.

Аналогично со словом 6/3, слова 7/2 и 8/1 повторяются в парах билетов из одного бронирования и одного рейса (#02 и #07, #03 и #08, #04 и #09, #05 и #10). Слова 7/0:1 и 8/0 одинаковы для всех билетов (за исключением нашего уникального #06), а 8/3 и 9/0 одинаковы для всех, кроме #01 и #06.

Поскольку здесь я уже не мог угадать, что же может скрываться за этими словами, я решил посмотреть на тексты посадочных талонов. Там находится стандартная информация, которую вам и / или работникам аэропорта стоит знать о билете. Сюда входит:


  • номер билета (13 цифр);
  • некий номер безопасности (3-буквенный код аэропорта вылета, минус и 3 цифры);
  • номер участника программы лояльности (зависит от программы, иногда буквы вначале, всегда много цифр);
  • информация о багаже;
  • аэропорты вылета и прилёта (и их коды);
  • время и дата посадки и вылета;
  • терминал (не часто отображает актуальную информацию);
  • ваше место в самолёте;
  • класс резервирования (одна латинская буква).

Ранее я уже решил, что 13 цифр номера билета шифровать нет смысла — он занимает слишком много места. Аналогично с программой лояльности (кроме того, что тогда этот номер был бы во всех 5 билетах одного человека).

Информацию о багаже, время с датой вылета и посадки, а также терминал писать смысла не много, поскольку эти данные могут меняться практически в любой момент до непосредственно посадки в самолёт.

Осталось немного — код безопасности, аэропорты, место в самолёте и класс резервирования. Сравним их для разных билетов.

Заметили? У нас есть 3 разных места с буквой B, а также 2 — A и 2 — C. Их найти было не сложно — слово 7/3. В таком случае, слово 7/2 содержит цифры из номера места. Потому их четыре пары и ещё два разных.

Не многим сложнее было найти и код безопасности. А точнее, его цифры — в словах 8/1:2. При этом, 8/1 содержит первые две, а 8/2 — последнюю цифру и, возможно, разделитель. Слово 8/0 разделяет место пассажира и код безопасности и одинаково для всех кодов.

Что скрывается за словами 6/2 и 6/3 непонятно. Это же касается и последних слов-данных 8/3 и 9/0. Слова 8/0, 7/0 и 7/1 одинаковы для всех кодов.


66584d080a0ffff84a74918e5da5ad46.svg

Рис. 6. Уточнение позиций данных #01: #10 для последних 6 строк


08. Построчный анализ

(Назад | Оглавление | Вперёд)

Настало время рассмотреть особенности разных строк кодов. Здесь не будет текста. Давайте просто посмотрим на свойства слов каждые три строки. Представим каждое слово как последовательность из 8 символов, шириной от 1 до 6. Тогда для строк 0, 3, 6 и 9 получим следующий результат на основе 19 разных слов (Рис. 7).


d87544363ed4194d5365f62362df1abd.svg

Рис. 7. Слова в строках 0, 3, 6 и 9

А вот для строк 1, 4, 7 и 10, состоящих из 32 разных слов, правила немного другие (Рис. 8).


3a73e01069a258d2479c52353f87a29e.svg

Рис. 8. Слова в строках 1, 4, 7 и 10

Соответственно, для 28 слов из строк 2, 5, 8 и 11 результат будет следующий (Рис. 9).


7ec1d226c835427ed869f8c52d8c283f.svg

Рис. 9. Слова в строках 2, 5, 8 и 11

В таком случае, с помощью трёх несложных шагов, можно вывести формулу для определения группы, к которой относится строка (Рис. 10).


8b98c19cbeadd508433465aa3cd9ae6a.svg

Рис. 10. Общие правила


09. Получение всех возможных слов

(Назад | Оглавление | Вперёд)

Итак, в каждом слове есть 8 символов. Максимальная длина одного — 6 элементов. Отталкиваясь лишь от этого, можно получить 1 679 616 комбинаций. Если же уточнить, что длина слова должна быть равна 17 элементов, а также для слов в каждой строке должны выполняться найденные ранее правила, у нас остаётся лишь 3 488 возможных вариантов.

Из этих 3 488 вариантов:


  • 1 484 для группы 0;
  • 1 002 для группы 3;
  • 1 002 для группы 6.

Поскольку группы 3 и 6 содержат меньше возможных комбинаций, а все три группы должны бы быть абсолютно одинаковы, будем считать их верхней границей. Таким образом, 482 комбинации из группы 0 не используются.

Осталось понять, как же эти слова кодируются. Я уже практически уверен, что каждое слово кода может содержать 2 буквы. В латинице — 26 букв от A до Z, для которых потребуется $26^2 = 676$ слов. Кроме того, нужен ещё как минимум один разделитель (к примеру, пробел), что даёт уже 729 слов.

Не сложно заметить, что цифры сюда уже не влезают, поскольку $(26 + 10)^2 = 1 296$, а с разделителем — все 1 369. В таком случае, нам нужно использовать набор возможных слов ещё раз, добавив «режимы» и переключатели между ними.

Итак, вернёмся к количеству возможных комбинаций. Мы можем зашифровать не больше чем квадрат числа возможных символов. Получается, что наш лимит — 31 символ (961 комбинация).


Предполагаемая таблица


10. Словарь v0–01 и «неправильный» код

(Назад | Оглавление | Вперёд)

Давайте подведём промежуточные итоги наших знаний. Фамилии, коды бронирования, коды рейсов, номера мест и коды безопасности позволили составить словарь из 38 символьных комбинаций. Большинство из них известно только для одной из трёх групп. Лишь 03 и 04 известны сразу для двух групп.

Давайте ещё раз внимательно посмотрим на код #06. Для удобства, приведу построчное его сравнение с кодом #07 (Рис. 11).


c0a4f4490fbb7920e9cfb0fcd2a04fdd.svg

Рис. 11. Построчное сравнение кодов #06 и #07. Рационализация записи данных.

Как видно, слова в коде совпадают до того момента, пока не оказывается, что для записи номера бронирования нужно меньше символов (поскольку нет перехода между буквами и цифрами). Таким образом, далее код съезжает на одно слово вперёд.

Как я уже предполагал ранее, в конце всех кодов, кроме #06 присутствуют три одинаковых слова. Вначале я назвал их «стоп-словами», но поскольку их нет в единственном коротком коде, они представляют собой скорее пустые символы для выравнивания всего PDF417. А вот количество проверочных слов (8 последних) для всех кодов одинаково.

Если быть внимательным, можно заметить, что для кода #06 слова 7/2 и 7/3 одинаковы. Поскольку первое из них содержит переход к режиму 0, описанному ранее, и букву A то 7/3 уже не может содержать такого же перехода. А вот обратный переход вполне возможен и он должен сопровождаться символом с таким же порядковым номером в таблице, что и A. В результате, слово 7/3 в коде #06 записывает переход к режиму 1 и цифру 0. Таким образом, код безопасности состоит из 4 цифр с ведущим 0, что звучит вполне логично.

Получается, переключатели между этими двумя режимами отображения (буквы и цифры) на самом деле имеют одно значение, а потому нет смысла называть их по-разному.

Кстати, слово 7/0 тоже идентично упомянутым ранее. Поскольку после него идёт число, оно соответствует (s)0. В таком случае, предыдущее слово 6/3 должно оканчиваться буквой. Таким образом, на номер ряда в самолёте отводится 3 цифры, хотя таких длинных самолётов не бывает?

Чтобы исключить вероятность ошибки в моём анализе, нужно было найти слова, которые точно соответствовали и (s) + буква, и (s) + цифра. Это было не сложно, согласно моему предположению, 4/0 в коде #07 означает (s)2, что должно равняться (s)C. А такое слово находится на позиции 7/3 кода #09. Поскольку ряды 4 и 7 соответствуют одной группе, а слова одинаковы, гипотеза подтверждается.

Одновременно, это подтверждает принцип размещения кода бронирования в PDF417. В результате, словарь дополнился до 50 позиций, а ещё одна получила значение для иной группы рядов.


11. Другие авиалинии & v0–02

(Назад | Оглавление | Вперёд)

Давайте изучим коды других авиалиний. Ведь больше данных — это всегда больше возможностей. В качестве других у нас будут Turkish Airlines (#11: #14). Первое, что бросается в глаза — они используют коды, повёрнутые на 180 градусов. Поскольку я уже привык к предыдущему набору, я просто разверну новые 4 кода в стандартное состояние. Кроме того, код растянут так, чтобы наименьший элемент был привычного размера.

Каждый код состоит из 26 строк и 5 столбцов с информационными словами. Все они, вместе с левыми и правыми сервисными словами, а также паттернами старта и конца, делают код довольно большим. На Рис. 12 представлено построчное сравнение для первых 17 строк кодов.


913b6d46c37437b005fe2e3a0e6d6300.svg

Рис. 12. Построчное сравнение кодов #11: #14. Часть 1

Сразу отметим то, что мы уже знаем благодаря предыдущей группе кодов. Сюда относится фамилия в начале кода (сразу после первого слова 0/0, содержащего общее их количество, и двух слов 0/1:2, стандартных для всех посадочных талонов). Для меня это слова 0/3: ½. После фамилии-имени идут 6 одинаковых для всех 4 билетов слов (2/4: ¾). Если следовать той же схеме, что и для прошлых авиалиний, здесь должен быть номер бронирования. В данном случае — EUPT2M7, что на один символ и один переход между группами символов длиннее, чем предыдущие коды. Далее — 3 слова, одинаковые для одного рейса.

После всего этого расположены 4 слова (2 одинаковые и 2 в соответствии рейсу), в которых расположены коды (TK) и номера рейсов (1870 и 0762). Слово 6/1 снова повторяется для одного рейса, а вот 6/2 — разные. В этих двух словах расположены номера мест 23 A/B и 17 K/J. И сразу за ними идёт код безопасности в словах 6/3: 7/0.

В результате, у нас осталось ещё 2/3 кода. Что делать с ним разберёмся чуть позже, а пока вернёмся к трём словам, которые повторяются для рейса. Для данной группы — слова 4/0:2, а для 10 билетов другой авиакомпании — 4/3: 5/1. Если быть достаточно наблюдательным, можно заметить, что первое из трёх слов (находится в одной группе для всех кодов) одинаково (32233211) для всех рейсов из аэропорта Марко Поло. В 3 слова, то есть 6 обычных символов, может поместиться как-раз коды двух аэропортов — вылета и прилёта.

Давайте попробуем понять, что спрятано в следующей части кодов. Слово 8/0 содержит число 18, 14/2 — число 62, а вот 13/0 содержит то же, что и 4/3. В билетах моей жены слово 14/3 содержит число 18. При этом, начиная с 14/2 коды для нас разные. Недолго вглядываясь в напечатанный посадочный талон, можно определить, что здесь скрывается номер карты программы лояльности.

В результате, наш словарь состоит уже из 78 двухсимвольных комбинаций, а одна из них — (s)A / (s)0 известна и для всех трёх групп рядов.


12. Ещё пара кодов TA & v0–03

(Назад | Оглавление | Вперёд)

Ещё два кода #15 и #16 тех же авиалиний вносят разнообразие в возможные размеры PDF417. На этот раз — 27 строк состоят из 4 информационных слов каждая. Использовав наш текущий словарь, можно сразу восстановить 15 слов (Рис. 13, отмечены серым).


940c5a89065d634b676470d13dad334f.svg

Рис. 13. Построчное сравнение кодов #15 и #16. Часть 1

Несложно восстановить и слово 7/2, ответственное за число 16 (номер места). Всплывает также интересный факт, что после кодов аэропортов ISTKBP сразу же (без разделителя) идёт TK — код авиалиний, который входит в номер рейса (слово 5/2). В таком случае, 5/3 содержит разделитель (d) и переключатель (s), но не известно, в какой последовательности.

Слова 6/0:1 ответственны за номер рейсов (1062 и 0459), а вот 4/3: 5/1 дополняют словарь комбинациями LJ UI ST из названий аэропортов. К сожалению, для этих посадочных талонов я не смог найти номер бронирования, а слова 3/2: 4/2, где он, вероятно, размещён, почему-то разные. Кроме того, печатный посадочный талон не содержит кода безопасности, а потому хоть он и должен размещаться в словах 8/1:2, мы его не знаем.

Вернувшись к кодам #01: #10 можно добавить в словарь ещё пару комбинаций, пропущенных вначале, в том числе PS — код авиакомпании. В результате, у нас есть 87 комбинаций в словаре.


13. Сервисные слова & v0–04 & v0–05

(Назад | Оглавление | Вперёд)

Пришло время рассмотреть, что же из себя представляет информация в сервисных столбцах (левом и правом). Результат представлен на Рис. 14, однако давайте посмотрим подробнее, как к нему дойти. Благодаря известной части словаря, удалось расшифровать:


  • 7 / 24 для кодов #01: #05, #07: #10 (Рис. 15);
  • 7 / 22 для кода #06 (Рис. 15);
  • 4 / 52 для кодов #11: #14(Рис. 16);
  • 5 / 54 для кодов #15: #16 (Рис. 17);
  • 10 / 48 для кодов #17: #18, о которых речь пойдёт в следующем разделе (Рис. 18).


434482912395f011ee13830696885937.svg

Рис. 14. Сервисные слова

Самой простой оказалась группа слов $k/R$ и $(k+2)/L$, где $k = 3i$, $i = 0, 1, 2…$ (отмечены синим цветом на Рис.). Для неё правое число равняется 2, 3 или 4, в зависимости от количества столбиков в коде (3, 4 и 5 соответственно).

В группе $k/L$, $(k+1)/R$ (отмечены зелёным) правое число принимает значения 3, 3, 8, 8 и 7 для кодов длиной в 12, 11, 26, 27 и 24 строк. Поскольку цикличность в 3 строки сопровождает весь код, здесь тоже используется тройка. Данное число является результатом целочисленного деления номера последней строки (оно же количество строк отнять единицу) на 3.

Последняя группа $(k+1)/L$, $(k+2)/R$ (оранжевый) оказалась самой сложной. Очевидно, что количество строк играет здесь важную роль, поскольку число разное для кодов из 12 и 11 строк (Рис. 15). В то же время, оно может принимать значение больше 9 (L или же 11 для кодов в 27 и 24 строки, Рис. 17 и Рис. 18 соответственно). В таком случае, это некое число плюс остаток от деления номера последней строки на 3.

Узнать значение константы помог единственный элемент кода, который я ещё не рассматривал — конец кода. Как вы помните, там представлено некоторое количество слов, разное для каждого кода. Где-то в начале анализа я решил, что они не несут полезной информации. Пришло время восстановить справедливость — это коды для коррекции ошибок. Но нам важно их количество. Оно равняется 8, 16 и 32 для всех 18 рассмотренных кодов. Поскольку эти числа недвусмысленно намекают на степени двойки, а число 3 содержится во всех формулах, также как и отнимание единицы, восстановить формулу оказалось несложно.

В результате, мы знаем все правые цифры в сервисных словах. Осталось определить левые. Они одинаковы для каждого блока из 3 строк. Таким образом, снова необходимо провести операцию целочисленного деления. В таком случае, это значение показывает, которым является по счёту текущий блок из 3 строк.

Поскольку, теперь восстановить все 200 L и R слов теперь было проще простого, словарь дополнился до 132 уникальных комбинаций.

А применение нового словаря позволило восстановить правильное положение имен, а также дополнить версию v0–05 до 135.


Рис. 015
c860635158df87b03a080ed4c98453ca.svg

Рис. 15. Сервисные слова #01: #10


Рис. 016
b9cdb5e4554bf881ada0e9650821e332.svg

Рис. 16. Сервисные слова #11: #14


Рис. 017
a94852a7467f530fb200cbb0a6f1708a.svg

Рис. 17. Сервисные слова #15: #16


Рис. 018
81d85ba9c5159a0f6ce2b82f3a47bce8.svg

Рис. 18. Сервисные слова #17: #18


14. Снова другие авиалинии & v0–06

(Назад | Оглавление | Вперёд)

Перейдём к новой авиакомпании — Adria Airways, таких у нас есть четыре.

Чтобы внести разнообразие, начнём не с #17 и #18, а с #19 и #20. Это коды из 20 рядов и 4 колонок. С помощью них можно восстановить такие комбинации, как UK (из кодов аэропортов LJU и KBP), JP — код авиалиний, а также 09 (часть номера рейса 934), 10 и 01 (Рис. 19). Используя L и R сервисные столбцы (Рис. 20), можно получить ещё 20 слов из разных групп словаря.


91a69569cacf54ecc8becbe37b03801d.svg

Рис. 19. Коды #19: #20


Рис. 020
6e6c19671324ffc3bfd3ebdfdd7c6788.svg

Рис. 20. Сервисные слова #19: #20

Почему в этих двух кодах не расшифрован код брони? Для обоих он — LBZOKV. Следуя тому же принципу, который был использован ранее, он должен располагаться блоками (d)L, BZ, OK и V (d) в словах 3/2: 4/1. Тем не менее, слово 3/2 не соответствует комбинации (d)L из словаря. Видимо, вместо (d) в коде присутствует другой разделитель, либо его нет. В любом случае, стоит учесть это далее.

Перейдём к кодам #17 и #18 (Рис. 21). С их помощью можно восстановить ещё несколько комбинаций, в том числе VA, SK, LE, YI, VS, EV, EL, NI, YZ, KB, PL, JU, JP, 35 и 00. Часть из них уже была в нашем словаре для некоторых групп, а другой не было вовсе. В результате, словарь дополнился до 149 уникальных комбинаций.


55744a46c0fa962f2d82ff3750e720f1.svg

Рис. 21. Построчное сравнение кодов #17 и #18

Кроме того, пришло время подсчитать количество неизвестных нам символов. Для всех 20 кодов, этот список состоит из 376 уникальных значений, а точнее — 114, 130 и 132 кодовых слова для разных групп.


15. Исправляем свои ошибки

(Назад | Оглавление | Вперёд)

Ещё при первом рассмотрении кодов #01 — #10, я решил, что слово на позиции 0/0 содержит в себе количество всех слов в коде. Это подтверждал тот факт, что для #06, который был короче на одну строку, данное значение отличалось. На данный момент, мне известны значения этого слова для кодов #11: #14 (38), #15: #16 (32) и #19: #20 (24).

Поскольку я уже знал, что количество проверочных слов, присутствующих в конце каждого кода, зашифровано в сервисных столбцах L и R, я решил, что нужно подсчитать лишь полезные слова. В результате, были получены значения 98, 92 и 64 соответственно. Очевидно, что это не совсем то, чего я ожидал.

Найти правильный ответ позволил возврат к самому началу. Не началу всей истории, но лишь составления списка всех возможных слов. Очевидно, что чтобы определить порядковый номер слова из двух символов, нужно всего лишь взять сумму одного числа, умноженного на размерность базиса, и второго числа.

Вначале я знал лишь верхнее ограничение количества слов и решил, что базис вполне может занимать всё возможное пространство из 31 элемента. Теперь же становится очевидно, что настоящая его размерность — 30. В таком случае комбинация 38 становится словом под номером 98 ($30 * 3 + 8$), комбинация 32 — номером 92 ($30 * 3 + 2$), а 24 — номером 64 ($30 * 2 + 4$).

В результате, первое слово кодов #01: #05, #07: #10 имеет номер 40 (комбинация BK), #06 — номер 36 (BG), а кодов #17: #18 — номер 56 (комбинация B и символа под номером 26).


16. Все оставшиеся коды & v0–07

(Назад | Оглавление | Вперёд)

Напоследок осталось ещё два кода Adria Airways (#21 и #22, Рис. 22), с помощью которых определяются комбинации BZ, VI, LJ, 85, 16, 10, 55, 31, 92, 81, 40, 32, 4(d), 98 и 45. Сервисные L и R слова этих кодов (Рис. 23) добавляют 37, 47, 57, 67, 77, 87, 06, 26, 36, 46 и 76.


Рис. 022
990579f3436f8095886247d0851b8b20.svg

Рис. 22. Коды #21: #22


Рис. 023
fdac438d4d269fc2d3c8441209fe2e91.svg

Рис. 23. Сервисные слова #21: #22

Интересно, что коды #21 и #22 содержат как номера карт лояльности, так и номера самих билетов. В кодах же #19 и #20 из того же бронирования, этих данных нет (либо они представлены в другом виде).

Самым-самым последним (#23, Рис. 24) оказался ещё один код той же авиакомпании. Он помог восстановить ещё 7 комбинаций, а вот анализ L и R слов (Рис. 25) не открыл ничего нового.


fa07248916b16ec22cdf3dd2ad4ab273.svg

Рис. 24. Код #23


Рис. 025
3a4b85f274166aec820f226202b5f182.svg

Рис. 25. Сервисные слова #23

В результате, словарь версии v0–07 состоит из 174 уникальных комбинаций, а точнее — 315 слов из разных групп. Список неизвестных слов состоит из 411 элементов.


17. Дальше стоит быть умнее & v0–08

(Назад | Оглавление | Вперёд)

Поскольку подбирать отдельные слова в кодах стало достаточно трудно, а просто угадывать их нет никакого смысла, стоит на минуту (день, неделю) задуматься. По какому принципу генерируются 17-символьные слова в PDF417.

Помните, при расчётах всех возможных комбинаций (здесь), получилось, что для группы 0 их возможно 1 484, группы 3 — 1 002 и групп

© Habrahabr.ru