Meridian Office

Формат лицензионного ключа MRDN (версия 1)

Нормативная спецификация. Реализации: kernel/crates/meridian-license/src/key.rs (разбор без секрета), services/license-service и tools/keygen (полная, с MAC). Контекст — docs/10-licensing-accounts.md, раздел 4.

1. Внешний вид

MRDN-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
│    └──────────── 25 символов Crockford Base32 = 125 бит ────────────┘
└─ префикс-литерал (не кодирует данных)

2. Алфавит Crockford Base32

Значение 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Символ 0 1 2 3 4 5 6 7 8 9 A B C D E F
Значение 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Символ G H J K M N P Q R S T V W X Y Z

Исключены I, L, O, U. При вводе: I, i, L, l → 1; O, o → 0; U/u — ошибка формата. Строка алфавита: 0123456789ABCDEFGHJKMNPQRSTVWXYZ.

3. Битовая раскладка (big-endian, бит 0 — старший)

бит:   0    4    8    12               28                                    76               92                           120   125
       ├────┼────┼────┼────────────────┼─────────────────────────────────────┼────────────────┼────────────────────────────┼─────┤
поле:  ver  ed   kind  seats (16)       serial (48) = batch (16) ‖ rnd (32)   issued (16)      mac (28)                     check(5)
символ: 0    │    1   2   3   4   5    │ 6 … 15                              │ 15 16 17 18    │ 18 … 23                     │ 24
Поле Биты Размер Тип Допустимые значения
version 0–3 4 uint 1; остальные — KEY_UNSUPPORTED_VERSION
edition 4–7 4 enum 1 free (резерв, ключи не выпускаются), 2 home, 3 pro, 4 business, 5 enterprise, 6 education, 7 trial; 0, 8–15 — резерв
kind 8–11 4 enum 1 perpetual, 2 subscription, 3 volume, 4 trial, 5 partner, 6 float_server; 0, 7–15 — резерв
seats 12–27 16 uint 1…65535; 0 недопустим
batch 28–43 16 uint идентификатор партии (key_batches.batch_id); 0 — служебная (тесты)
rnd 44–75 32 uint CSPRNG, уникален внутри партии
issued 76–91 16 uint дней от 2027-01-01 (UTC); 0 — выпуск до эпохи или тестовый
mac 92–119 28 uint старшие 28 бит HMAC-SHA256(K_issuer, "MRDN-KEY-v1" ‖ P12)
check 120–124 5 symbol контрольный символ по первым 24 символам

payload92 — биты 0–91 (поля version…issued). P12 — 12 байт: payload92 << 4 (92 бита + 4 нулевых бита дополнения, big-endian). bits120 — биты 0–119 = payload92 ‖ mac28; первые 24 символа кодируют ровно bits120 (24 × 5 = 120).

Семантика seats по kind: perpetual/subscription личные — устройства; subscription Business/Education — пользователи; volume — активации; float_server — размер пула; trial/partner — устройства.

4. Алгоритмы

4.1 MAC

input  = "MRDN-KEY-v1" (11 байт ASCII) ‖ P12 (12 байт)
tag    = HMAC-SHA256(K_issuer, input)                 ; K_issuer — 32 байта, только издатель и license-service
mac28  = (tag[0] << 20 | tag[1] << 12 | tag[2] << 4 | tag[3] >> 4)   ; старшие 28 бит

Клиент MAC не проверяет (секрета нет) и не должен делать вид, что проверяет. Сервер проверяет MAC до обращения к БД (отсев мусора и подделок), затем ищет запись по (batch, rnd).

4.2 Контрольный символ

v[i]  = значение символа i (0…31), i = 0…23
w[i]  = 2·i + 1                     ; нечётные веса 1, 3, 5, …, 47
check = ALPHABET[ (Σ v[i]·w[i]) mod 32 ]

Свойства: любая одиночная ошибка символа обнаруживается (нечётный вес × ненулевая разница ≢ 0 mod 32); перестановка соседних символов обнаруживается, кроме случая |v[i] − v[i+1]| = 16 (вероятность 1/31); две случайные ошибки — 31/32.

4.3 Кодирование

1. Проверить диапазоны полей.
2. payload92 = version<<88 | edition<<84 | kind<<80 | seats<<64 | batch<<48 | rnd<<16 | issued
3. mac28     = MAC(payload92)           (4.1)
4. bits120   = payload92<<28 | mac28
5. sym[i]    = ALPHABET[(bits120 >> (5·(23−i))) & 31], i = 0…23
6. sym[24]   = check(sym[0..24])        (4.2)
7. вывод: "MRDN-" + группы по 5 символов через "-"

4.4 Декодирование (клиент и сервер)

1. normalize: удалить пробелы/дефисы, в верхний регистр, снять префикс "MRDN"; I/L→1, O→0
2. длина == 25 и все символы из алфавита              иначе KEY_INVALID_FORMAT
3. check(sym[0..24]) == sym[24]                          иначе KEY_CHECKSUM
4. bits120 = Σ value(sym[i]) << (5·(23−i)); payload92 = bits120 >> 28; mac28 = bits120 & 0x0FFFFFFF
5. version == 1                                          иначе KEY_UNSUPPORTED_VERSION
6. edition ∈ {2..7}, kind ∈ {1..6}, seats ≥ 1             иначе KEY_INVALID_FORMAT
7. (сервер) MAC(payload92) == mac28                      иначе KEY_NOT_FOUND  (не раскрываем, что именно не так)
8. (сервер) SELECT … WHERE batch = ? AND rnd = ?         нет записи → KEY_NOT_FOUND

4.5 Отпечаток ключа

key_fingerprint = hex( SHA-256( "MRDN-KEY-FP-v1" ‖ 25 символов канонического вида без дефисов и префикса ) )[0..32] (первые 16 байт в hex). Используется в .mlic, CRL, журналах и поиске; сам ключ в журналах маскируется: MRDN-28800-…-T2E7W.

5. Тестовые векторы

Секрет для векторов: K_issuer = "issuer-test-secret-do-not-use" (ASCII, 29 байт). Все реализации обязаны воспроизводить эти ключи.

# Ключ version edition kind seats batch rnd issued (дата) mac28
1 MRDN-28800-C0G3N-EQW3V-00JBK-T2E7W 1 2 home 1 perpetual 3 0x0101 0xD5D7E0F6 73 (2027-03-15) 0x73D09C7
2 MRDN-2CG00-M005N-MCBZE-007X7-KT6MP 1 3 pro 2 subscription 5 0x0002 0xD68C5FDC 31 (2027-02-01) 0xA79E8D4
3 MRDN-2GG03-4011J-5C77N-01D1H-T6YY1 1 4 business 2 subscription 25 0x0010 0xC8AC39EA 180 (2027-06-30) 0x31D1BDE
4 MRDN-2MR1Y-G101X-KW9KV-G2XH2-NCQQN 1 5 enterprise 3 volume 500 0x0200 0xF67C4CF7 374 (2028-01-10) 0x22AB2F7
5 MRDN-2RG03-R1G1K-A117G-G1WRX-JGZ6A 1 6 education 2 subscription 30 0x0300 0xCD4109E1 243 (2027-09-01) 0x1D943E6
6 MRDN-2X000-47G12-BC1TB-00PJB-0JWWQ 1 7 trial 4 trial 1 0x0F00 0x896C0E96 90 (2027-04-01) 0x4B04B9C
7 MRDN-2NG0S-0102F-SB3B4-G333C-EFXEX 1 5 enterprise 6 float_server 200 0x0201 0x3F2B1AC9 396 (2028-02-01) 0x6C73FAE

Промежуточные значения (hex):

# bits120 (30 hex) P12 (вход HMAC после домена) key_fingerprint
1 12100030101D5D7E0F6004973D09C7 12100030101D5D7E0F600490 2b55530fe3a20f6fc38a3b13dcc983cc
2 13200050002D68C5FDC001FA79E8D4 13200050002D68C5FDC001F0 136acb0924097e5febcc424907458f8c
3 14200190010C8AC39EA00B431D1BDE 14200190010C8AC39EA00B40 e67c1836719d19b24fe0eb86f06619db
4 15301F40200F67C4CF7017622AB2F7 15301F40200F67C4CF701760 38a2abbdf456ef374f630bb4c67ab8c4
5 162001E0300CD4109E100F31D943E6 162001E0300CD4109E100F30 9389295a77992197c8ba04b20d1d39c6
6 17400010F00896C0E96005A4B04B9C 17400010F00896C0E96005A0 0fcddd28ac2b4f55764c89e539d0102b
7 15600C802013F2B1AC9018C6C73FAE 15600C802013F2B1AC9018C0 84ab1e09460f710774e48af15ce1f908

5.1 Полный разбор вектора 1

Ключ:        MRDN-28800-C0G3N-EQW3V-00JBK-T2E7W
Символы:     2 8 8 0 0 C 0 G 3 N E Q W 3 V 0 0 J B K T 2 E 7 | W
Значения:    2 8 8 0 0 12 0 16 3 21 14 23 28 3 27 0 0 18 11 19 26 2 14 7 | check
bits120:     0001 0010 0001 0000000000000011 0000000100000001 11010101110101111110000011110110 0000000001001001 0111001111010000100111000111
             ver  ed   kind seats=3          batch=0x0101     rnd=0xD5D7E0F6                   issued=73         mac28=0x73D09C7
HMAC вход:   4D52444E2D4B45592D7631 ‖ 12100030101D5D7E0F600490
             ("MRDN-KEY-v1")          (P12)
HMAC tag:    73D09C7DE1310ED9F486BCB5ED98E6FC5BF987463E7128F77BAD440BD59E42F4
mac28:       tag[0..4] >> 4 = 0x73D09C7                       ✓ совпадает с битами 92–119
check:       Σ v[i]·(2i+1) = 7164; 7164 mod 32 = 28 → 'W'      ✓

5.2 Отрицательные векторы

Ввод Результат
MRDN-28800-C0G3N-EQW3V-00JBK-T2E7X KEY_CHECKSUM (последний символ)
MRDN-28A00-C0G3N-EQW3V-00JBK-T2E7W KEY_CHECKSUM (одиночная ошибка в символе 2)
mrdn 28800 c0g3n eqw3v 00jbk t2e7w OK — равен вектору 1 (регистр, пробелы)
MRDN-288OO-C0G3N-EQW3V-00JBK-T2E7W OK — O→0
MRDN-28800-C0G3N-EQW3V-00JBK-T2E7 KEY_INVALID_FORMAT (24 символа)
MRDN-28800-C0G3N-EQW3U-00JBK-T2E7W KEY_INVALID_FORMAT (символ U)
вектор 1, но K_issuer = "other" клиент: OK; сервер: KEY_NOT_FOUND

6. Хранение и отображение

7. Расширение формата