Как правильно подойти к вопросу: неправильно работает crypto.subtle.deriveKey в Firefox, баг или мой косяк?

Ссылка скопирована
1 ответ

По вводным: генерирую пару ключей X25519, из них хочу получить общий секрет длиной в 128 бит для AES-128. Но столкнулся со странным поведением браузеров.
Нужно понять: в Google Chrome всё нормально, полученный ключ имеет длину 128 бит, как и полагается. В Mozilla Firefox почему-то параметр length игнорируется, даже если туда передать что-то отличное от числа, всегда генерируется ключ в 256 бит не зависимо от этого параметра. Это баг Firefox, или я неправильно использую функции?
По вводным: притом если использовать deriveBits вместо deriveKey, или алгоритм ECDH вместо X25519, то всё нормально, ключ 128 бит, как и полагается.
Вот код на Codepen:
Snippet

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Дмитрий К. Ответ

С crypto.subtle.deriveKey для X25519 и AES важно различать длину сырого общего секрета и длину ключа, который вы импортируете/получаете для AES. У X25519 общий секрет имеет фиксированную длину 256 бит. Если вы просите сразу deriveKey в AES-128, браузер должен вернуть AES-ключ нужной длины, но поведение может отличаться из-за реализации WebCrypto и поддержки алгоритмов.

Более переносимый путь: сначала получить raw bits через deriveBits, затем пропустить через HKDF/SHA-256 и импортировать AES-128 ключ. Так вы явно контролируете длину.

const bits = await crypto.subtle.deriveBits(
  { name: 'X25519', public: remotePublicKey },
  privateKey,
  256
);
 
const hkdfKey = await crypto.subtle.importKey(
  'raw',
  bits,
  'HKDF',
  false,
  ['deriveKey']
);
 
const aesKey = await crypto.subtle.deriveKey(
  {
    name: 'HKDF',
    hash: 'SHA-256',
    salt,
    info: new TextEncoder().encode('session key')
  },
  hkdfKey,
  { name: 'AES-GCM', length: 128 },
  false,
  ['encrypt', 'decrypt']
);

const bits = await crypto.subtle.deriveBits( { name: 'X25519', public: remotePublicKey }, privateKey, 256 ); const hkdfKey = await crypto.subtle.importKey( 'raw', bits, 'HKDF', false, ['deriveKey'] ); const aesKey = await crypto.subtle.deriveKey( { name: 'HKDF', hash: 'SHA-256', salt, info: new TextEncoder().encode('session key') }, hkdfKey, { name: 'AES-GCM', length: 128 }, false, ['encrypt', 'decrypt'] );

Если Firefox игнорирует length именно в вашем варианте, это может быть баг реализации или ограничение конкретной связки алгоритмов. Но для ECDH/X25519 всё равно криптографически правильнее использовать KDF, а не напрямую брать shared secret как AES-ключ.

Проверьте также, что вы не смотрите на длину экспортируемого raw материала X25519 вместо длины AES key. AES-GCM key length и shared secret length — разные вещи.

Итог: используйте deriveBits + HKDF + AES-GCM 128. Это переносимее между Chrome и Firefox и лучше с точки зрения криптографии, чем прямой deriveKey из X25519 в AES.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно