Криптон. Оживляем «криптографический танк» из девяностых

в 8:05, , рубрики: ancud, DES, ISA, timeweb_статьи, x86, блюминг, гост, кб1, криптон, шифратор, шифропроцессор
Приветствую всех!
Не так давно я рассказывал про построенный на базе «гражданского» девайса портативный шифратор ‭«Электроника МК-85С‭». Самое время поведать миру о ещё одном, не менее значимом, но чуть менее известном девайсе — отечественном криптопроцессоре «Блюминг-1», а также об алгоритме, который который он реализовывал.

Криптон. Оживляем «криптографический танк» из девяностых - 1

Итак, в сегодняшней статье рассмотрим ещё одного представителя российской шифровальной техники — построенную на данном чипе плату шифрования ‭«Криптон‭». Узнаем, как работает эта штука и где она применялась. Заодно посмотрим на её софт и попробуем что-то зашифровать. Как водится, будет много интересного.

Суть такова

История данного устройства началась в середине семидесятых годов, когда начал разрабатываться алгоритм ГОСТ 28147-89 (он же ‭«Магма-2‭»), по принципу работы напоминающий DES. Создавался он в ходе проекта по разработки криптографических систем защиты информации на ЭВМ Единой системы. Требования, предъявлявшиеся к нему, были крайне высокими: предполагалось, что шифровать им будут информацию высокой степени секретности, причём сам алгоритм секретным был быть не должен.

В 1987 году было создано и устройство шифрования — криптопроцессор ‭«Блюминг-1‭», реализующий данный алгоритм. Примечателен он тем, что стал первым отечественным однокристальным шифратором, давшим начало развития целой области средств защиты информации у нас.

Криптон. Оживляем «криптографический танк» из девяностых - 2

А вот и построенная на базе данного чипа плата «Дебют-3». Существовало три версии: «1» для ЭВМ ДВК, «2» — для Системы малых ЭВМ, «3» — для PC с шиной ISA.

Криптон. Оживляем «криптографический танк» из девяностых - 3

Криптон. Оживляем «криптографический танк» из девяностых - 4

Небольшой прикол

Есть у меня любовь к поиску и документированию ошибок и опечаток в объявлениях, на сайтах и где-то ещё. Чем глупее ошибка и чем серьёзнее место обнаружения — тем веселее. Сайт ANCUD здесь тоже отличился аж тройным неправильным описанием одного и того же слова…

Криптон. Оживляем «криптографический танк» из девяностых - 5

Фирма эта работает и сейчас и продолжает заниматься выпуском средств защиты информации (преимущественно для гостайны).

Криптон. Оживляем «криптографический танк» из девяностых - 6

Снова «Криптон-3», но в несколько другом исполнении. Увы, я общался с товарищем, кому удалось её достать, но запустить её не удалось…

Обзор оборудования

Про эти платы не удалось найти практически никакой информации. Даже при банальном поиске «Криптон-4» обнаруживается мало чего. Так что сейчас немного развеем завесу тайны.

Криптон. Оживляем «криптографический танк» из девяностых - 7

Криптон. Оживляем «криптографический танк» из девяностых - 8

Криптон. Оживляем «криптографический танк» из девяностых - 9

Дискеты с софтом.

Криптон. Оживляем «криптографический танк» из девяностых - 10

Криптон. Оживляем «криптографический танк» из девяностых - 11

А вот более старые версии софта. Что от чего, мне неведомо, досталось всё одной кучей.

Криптон. Оживляем «криптографический танк» из девяностых - 12

Ещё один дистрибутив софта — LiteSign. В отличие от остальных дискет, эта в опечатанной коробочке (которую до меня уже, разумеется, открывали).

Криптон. Оживляем «криптографический танк» из девяностых - 13

А вот более новая версия платы, уже на PCI. Чипы шифраторов изменили свои корпуса, вместо БМК теперь ПЛИС. Шумовые диоды также, очевидно, никуда не подевались. Разъём 4P4C, предназначенный для подключения ключа iButton.

Криптон. Оживляем «криптографический танк» из девяностых - 14

На обратной стороне этикетка — изделие М-503Г. По сравнению с предыдущей платой, железка не такая уж и древняя.

Криптон. Оживляем «криптографический танк» из девяностых - 15

На этом история данных устройств не заканчивается. Следующим шагом было избавление от керамических чипов шифраторов. Их обязанности были возложены на ПЛИС. Плата же получила название Криптон-8/PCI.

Криптон. Оживляем «криптографический танк» из девяностых - 16

Последняя (если верить сайту ANCUD) версия — Криптон-10/PCI-E.

Криптон. Оживляем «криптографический танк» из девяностых - 17

Параллельно с абонентскими шифраторами выпускаются и средства доверенной загрузки — Криптон-Замок.

Инструкция и софт

Криптон. Оживляем «криптографический танк» из девяностых - 18

Помимо платы и дискет мне достался мануал, который я не поленился отсканировать. Страниц в нём немного, так что я разместил его прямо тут.

Под спойлером много картинок

Криптон. Оживляем «криптографический танк» из девяностых - 19

Криптон. Оживляем «криптографический танк» из девяностых - 20

Криптон. Оживляем «криптографический танк» из девяностых - 21

Криптон. Оживляем «криптографический танк» из девяностых - 22

Криптон. Оживляем «криптографический танк» из девяностых - 23

Криптон. Оживляем «криптографический танк» из девяностых - 24

Криптон. Оживляем «криптографический танк» из девяностых - 25

Криптон. Оживляем «криптографический танк» из девяностых - 26

Криптон. Оживляем «криптографический танк» из девяностых - 27

Криптон. Оживляем «криптографический танк» из девяностых - 28

Криптон. Оживляем «криптографический танк» из девяностых - 29

Криптон. Оживляем «криптографический танк» из девяностых - 30

Криптон. Оживляем «криптографический танк» из девяностых - 31

Криптон. Оживляем «криптографический танк» из девяностых - 32

Криптон. Оживляем «криптографический танк» из девяностых - 33

Криптон. Оживляем «криптографический танк» из девяностых - 34

Криптон. Оживляем «криптографический танк» из девяностых - 35

Криптон. Оживляем «криптографический танк» из девяностых - 36

Криптон. Оживляем «криптографический танк» из девяностых - 37

Также я оцифровал все имеющиеся у меня дискеты и выложил файлы на old-dos.ru. Помимо этого прочесал архивы сайта ANCUD и все найденные материалы также залил туда.

Первый запуск

Ну что же, время запускать!

Криптон. Оживляем «криптографический танк» из девяностых - 38

В качестве тестового компа будем использовать вот такую плату на процессоре 386SX. Втыкаем в неё всё нужное железо и плату шифрования, и пробуем включать.

Криптон. Оживляем «криптографический танк» из девяностых - 39

После прохождения POST запускается BIOS платы.

Криптон. Оживляем «криптографический танк» из девяностых - 40

Хранилища ключей у неё нет, поэтому они подтягиваются при загрузке с дискет или считывателей смарт-карт, где остаются до отключения питания. Тестовые ключи (узел замены и главный ключ) находятся на комплектной дискете. Нужны они для запуска платы, если ни одного «боевого» ключа ещё не сгенерировано.

Криптон. Оживляем «криптографический танк» из девяностых - 41

Втыкаем дискету с софтом, и ключи успешно подтягиваются. После этого начинается загрузка DOS.

Шифруем и расшифровываем

Криптон. Оживляем «криптографический танк» из девяностых - 42

Теперь запускаем главный софт — crtools. По сути это и есть утилита для шифрования, далёкий предок всяческих «КриптоАРМ» и тому подобного ПО. Здесь достаточно выбрать нужный файл и запустить операцию из меню, после чего он будет зашифрован или расшифрован. Никаких кодов, секретных комбинаций кнопочек и тому подобных манипуляций, всё делается за пару секунд в несколько нажатий клавиш.

Криптон. Оживляем «криптографический танк» из девяностых - 43

Параметры системы.

Генератор ключей

Криптон. Оживляем «криптографический танк» из девяностых - 44

В отличие от «Электроники МК-85С», где для генерации марканта надо было жмякать кнопку, или многих используемых сейчас систем, где требуется жмякать все клавиши и водить туда-сюда мышью для создания случайной последовательности, в «Криптоне» есть аппаратный датчик случайных чисел, что позволяет генерировать эти ключи практически моментально. Также для дополнительной защиты свежесозданный ключ можно защитить паролем.

ЭЦП и неожиданный облом

Следующей на очереди идёт программа CryptonSign, предназначавшаяся для цифровой подписи.

Криптон. Оживляем «криптографический танк» из девяностых - 45

Однако при попытке её запустить меня ждала неудача: программа выдала ошибку о каком-то неверном номере?
Помните ту ПЗУшку с номером платы? Да, именно для подобных целей она и используется. Просто поражает, насколько мелкосерийным было это производство: ПЗУ с уникальным номером, написанным на нём от руки, дистрибутив с софтом, уникальный для каждой платы. Судя по всему, использовался какой-то самописный генератор дистрибутивов, выдававший экзешник, залоченный на определённую плату, но не удивлюсь, если этот номер там и вовсе захардкожен, а для каждой платы софт пересобирали. Точные объёмы производства мне неведомы, но выпущено было буквально две-три тысячи плат, совсем немного по меркам компьютерного железа.

LiteSign

Криптон. Оживляем «криптографический танк» из девяностых - 46

Софт из коробочки тоже оказался непрост.

Криптон. Оживляем «криптографический танк» из девяностых - 47

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

Эмулятор и софт под Windows

Напоследок взглянем на то, какой софт существовал для работы с шифратором в среде Windows. Выпущен он уже куда позднее, нежели тот, что под DOS, также имеет защиту от копирования (на этот раз с помощью аппаратного ключа для LPT-порта) и сейчас доступен только в демо-версии по причине прекращения продаж и отсутствия «кряка». Впрочем, для того, чтобы увидеть его в работе, этого более чем достаточно.
На случай, если платы нет, можно воспользоваться эмулятором (для Windows 9x/NT4.0/2000).

Криптон. Оживляем «криптографический танк» из девяностых - 48

Первым делом ставим необходимый для работы всего софта Crypton API.

Криптон. Оживляем «криптографический танк» из девяностых - 49

Теперь накатываем эмулятор, и всё, можно пробовать запускать ПО.

Криптон. Оживляем «криптографический танк» из девяностых - 50

Помимо софта для шифрования отдельных файлов ANCUD также предоставлял и более специализированный, например, спецархиватор для почты ArcMail.

Криптон. Оживляем «криптографический танк» из девяностых - 51

Но с ним, увы, не задалось: версия под Windows не запускалась, а под DOS — не видела ни эмулятор, ни настоящую плату. Пришлось переместиться на Windows 2000, где софт ожил.

Криптон. Оживляем «криптографический танк» из девяностых - 52

Открываем параметры и устанавливаем путь для хранения ключей.

Криптон. Оживляем «криптографический танк» из девяностых - 53

А вот и наш софт.

Криптон. Оживляем «криптографический танк» из девяностых - 54

Криптон. Оживляем «криптографический танк» из девяностых - 55

Другим ПО для Windows является «Криптон-Подпись», интегрирующийся прямо в контекстное меню.

Криптон для программиста

Помимо использования штатного софта была возможность разрабатывать свой. Для этого существовал Crypton DK — набор библиотек и документации для разработки. Для того, чтобы оснастить свою программу возможностью аппаратного шифрования, достаточно было установить Crypton API и подключить прилагаемые к Crypton DK DLL-ки.

Пример использования каждой из функций Crypton DK

/////////////////////////////////////////////////////////////////
// Sample: testing Crypton API functions
// (c) Ancud 1998
// --------------------------------------------------------------
// The sample shows using all the Crypton API functions
/////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "....INCCryptLib.h"

CRHANDLE hCR;                // Handle to a crypton service
#define TestBufSize 2048     // The size of the buffers for encryption/decryption and random tests
char TestBuf1[TestBufSize];  // Buffers for encryption/decryption and random tests
char TestBuf2[TestBufSize];
char TestBuf3[TestBufSize];
crSYNCHRO crSync;
crKEY     crKey1,crKey2;     // Buffers for keys
crIP      crIP1,crIP2;       // Buffers for imitoprefixes

void                         // Almost obvious function ;)
WriteRes(BOOL res)
{
 if (res)
   printf(" tOKn");
 else
   printf(" tFALSEn");
}

void                         // Making equal buffers
SetEqualBufs(char* Buf1,char* Buf2,int Size)
{
 char j=35;
 int  i;
 for (i=0; i<Size; i++)
 {
  *(Buf1+i)=j;
  *(Buf2+i)=j;
  j++;
 }
}

void                         // Making different buffers
SetBufs(char* Buf1,char* Buf2,int Size)
{
 char j=32;
 int  i;
 for (i=0; i<Size; i++)
 {
  *(Buf1+i)=j;
  *(Buf2+i)=255-j;
  j++;
 }
}

void
main()
{
 printf("nTest of Crypton API functions (c) Ancud 1998n");
 if (crInitCryptonLib())             // Don't forget to call this function
                                     // before using Crypton API functions
 {
  hCR=crInitService();     // Get a handle to the hardware service
  if (hCR)
  {
   BOOL res;

   if(crTestHardware(hCR)){
     printf("Using DK with Crypton Hardware!n");
   }else
     printf("Using DK with Crypton Emulator!n");

   //  crTestHardware
   printf("crTestHardwaret"); WriteRes(crTestHardware(hCR));

   //  crGetCryptonNumber
   printf("crGetCryptonNumbert%in",crGetCryptonNumber(hCR));

   //  crRandom
   printf("crRandomt");
   SetEqualBufs(TestBuf1,TestBuf2,TestBufSize);
   res=crRandom(hCR,TestBufSize,TestBuf1);
   res&=(memcmp(TestBuf1,TestBuf2,TestBufSize)!=0);
   WriteRes(res);

   //  crControlledRandom
   {
    crCTRLRANDOM TestBuf1,TestBuf2;
    printf("crControlledRandom");
    SetEqualBufs(TestBuf1,TestBuf2,CTRLRANDOM_LEN);
    res=crControlledRandom(hCR,TestBuf1);
    res&=(memcmp(TestBuf1,TestBuf2,CTRLRANDOM_LEN)!=0);
    WriteRes(res);
   }

   //  crSetK1
   printf("crSetK1tt");
   crRandom(hCR,CRYPTON_KEY_LEN,crKey1);
   WriteRes(crSetK1(hCR,crKey1));

   //  crSetK2
   printf("crSetK2tt");
   crRandom(hCR,CRYPTON_KEY_LEN,crKey1);
   WriteRes(crSetK2(hCR,crKey1));

   //  crSetK1onK2
   printf("crSetK1onK2t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   res=crSetK1onK2(hCR,crKey1);
   res&=crGetK1onK2(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crGetK1onK2
   printf("crGetK1onK2t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   crSetK1onK2(hCR,crKey1);
   res=crGetK1onK2(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crGetK2onK1
   printf("crGetK2onK1t");
   crRandom(hCR,CRYPTON_KEY_LEN,crKey1);
   crSetK1(hCR,crKey1);
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   res=crGetK2onK1(hCR,crKey1);
   res&=crGetK2onK1(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crSetK2onK3
   printf("crSetK2onK3t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   res=crSetK2onK3(hCR,crKey1);
   res&=crGetK2onK3(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crGetK2onK3
   printf("crGetK2onK3t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   crSetK2onK3(hCR,crKey1);
   res=crGetK2onK3(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crSetK1onK3
   printf("crSetK1onK3t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   res=crSetK1onK3(hCR,crKey1);
   res&=crGetK1onK3(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crGetK1onK3
   printf("crGetK1onK3t");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   crSetK1onK3(hCR,crKey1);
   res=crGetK1onK3(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)==0);
   WriteRes(res);

   //  crSetRandomK1onK3
   printf("crSetRandomK1onK3");
   SetEqualBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   res=crGetK1onK3(hCR,crKey1);
   res&=crSetRandomK1onK3(hCR);
   res&=crGetK1onK3(hCR,crKey2);
   res&=(memcmp(crKey1,crKey2,CRYPTON_KEY_LEN)!=0);
   WriteRes(res);

   //  crGetIPforDataonK1
   printf("crGetIPforDataonK1");
   SetBufs(TestBuf1,TestBuf2,TestBufSize);
   SetBufs(crIP1,crIP2,CRYPTON_IP_LEN);
   res=crGetIPforDataonK1(hCR,TestBufSize,TestBuf1,crIP1);
   res&=crGetIPforDataonK1(hCR,TestBufSize,TestBuf1,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)==0);
   res&=crGetIPforDataonK1(hCR,TestBufSize,TestBuf2,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)!=0);
   WriteRes(res);

   //  crGetIPforK1onK2
   printf("crGetIPforK1onK2");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   SetBufs(crIP1,crIP2,CRYPTON_IP_LEN);
   crSetK1(hCR,crKey1);
   res=crGetIPforK1onK2(hCR,crIP1);
   res&=crGetIPforK1onK2(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)==0);
   crSetK1(hCR,crKey2);
   res&=crGetIPforK1onK2(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)!=0);
   WriteRes(res);

   //  crGetIPforK2onK3
   printf("crGetIPforK2onK3");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   SetBufs(crIP1,crIP2,CRYPTON_IP_LEN);
   crSetK2(hCR,crKey1);
   res=crGetIPforK2onK3(hCR,crIP1);
   res&=crGetIPforK2onK3(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)==0);
   crSetK2(hCR,crKey2);
   res&=crGetIPforK2onK3(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)!=0);
   WriteRes(res);

   //  crGetIPforK2onK1
   printf("crGetIPforK2onK1");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   SetBufs(crIP1,crIP2,CRYPTON_IP_LEN);
   crSetK2(hCR,crKey1);
   res=crGetIPforK2onK1(hCR,crIP1);
   res&=crGetIPforK2onK1(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)==0);
   crSetK2(hCR,crKey2);
   res&=crGetIPforK2onK1(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)!=0);
   WriteRes(res);

   //  crGetIPforK1onK3
   printf("crGetIPforK1onK3");
   SetBufs(crKey1,crKey2,CRYPTON_KEY_LEN);
   SetBufs(crIP1,crIP2,CRYPTON_IP_LEN);
   crSetK1(hCR,crKey1);
   res=crGetIPforK1onK3(hCR,crIP1);
   res&=crGetIPforK1onK3(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)==0);
   crSetK1(hCR,crKey2);
   res&=crGetIPforK1onK3(hCR,crIP2);
   res&=(memcmp(crIP1,crIP2,CRYPTON_IP_LEN)!=0);
   WriteRes(res);

   //  crGammaCodeonK1Ex
   printf("crGammaCodeonK1Ex");
   SetEqualBufs(TestBuf1,TestBuf2,TestBufSize);
   crRandom(hCR,CRYPTON_SYNCHRO_LEN,crSync);
   res=crGammaCodeonK1Ex(hCR,TestBufSize,crSync,TestBuf1,TestBuf2);
   res&=(memcmp(TestBuf1,TestBuf2,TestBufSize)!=0);
   SetBufs(TestBuf1,TestBuf3,TestBufSize);
   res&=crGammaCodeonK1Ex(hCR,TestBufSize,crSync,TestBuf1,TestBuf2);
   res&=crGammaCodeonK1Ex(hCR,TestBufSize,crSync,TestBuf2,TestBuf3);
   res&=(memcmp(TestBuf1,TestBuf3,TestBufSize)==0);
   WriteRes(res);

   //  crGammaRecoveryCodeonK1Ex
   printf("crGammaRecoveryCodeonK1Ex");
   SetEqualBufs(TestBuf1,TestBuf2,TestBufSize);
   crRandom(hCR,CRYPTON_SYNCHRO_LEN,crSync);
   res=crGammaRecoveryCodeonK1Ex(hCR,TestBufSize,crSync,TestBuf1,TestBuf2);
   res&=(memcmp(TestBuf1,TestBuf2,TestBufSize)!=0);
   SetBufs(TestBuf1,TestBuf3,TestBufSize);
   res&=crGammaRecoveryCodeonK1Ex(hCR,TestBufSize,crSync,TestBuf1,TestBuf2);
   res&=crGammaRecoveryDecodeonK1Ex(hCR,TestBufSize,crSync,TestBuf2,TestBuf3);
   res&=(memcmp(TestBuf1,TestBuf3,TestBufSize)==0);
   WriteRes(res);

   crDoneService(hCR);               // We don't need this handle any more
  }
  else
    printf("Error starting the service.n");
  crDoneCryptonLib();                // We aren't going to use Crypton API any more
 }
 else
   printf("Error initialization of the Crypton library.n");
 printf("nPress any key...n"); _getch();
}

Использовать это проще простого: генерация ключей, шифрование в разных режимах и расшифровывание осуществляются вызовами буквально одной-единственной функции, всё остальное выполняет плата в паре с Crypton API.

Криптон. Оживляем «криптографический танк» из девяностых - 56

Вот, например, синтаксис функции шифрования. Достаточно лишь указать ей исходные данные и их размер, после чего останется только забрать результат.

Что же в итоге?

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

Забавно, что в своё время даже существовали вирусы для ПК с такими платами. На сайте ANCUD упоминалась некая программа, которая якобы ускоряла шифрование файлов, тогда как на деле «Криптон» при её запуске и вовсе не использовался, а файлы защищались куда более слабым алгоритмом.

Такие дела.

Ссылки


Читайте также:

Автор: MaFrance351

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js