Как получить ключи (HKDF) из двух пар ключей EC в Dart/Flutter?

В настоящее время я внедряю ISO 18013-5 (мобильное водительское удостоверение) с использованием Dart и Flutter (извините, не могу поделиться полным документом спецификации). Вкратце, для предотвращения подслушивания во время обмена данными между считывателем (например, полицией) и гражданином, два эфемерных ключа (EDeviceKeyиEReaderKey) генерируются (с использованием ECDH), по одному в каждом устройстве. После этого происходит обмен открытыми ключами для создания одного сеансового ключа на устройство (skDeviceиskReader). Эти ключи получены с использованием ECKA-DH (как определено в BSI TR-03111).

Чтобы объяснить мою борьбу, давайте сосредоточимся на одном приложении, гражданине: чтобы получить сеансовый ключ, мне нужно использовать HKDF с закрытым ключом гражданина и открытым ключом читателя. Для этого у меня есть следующее:

      Future<SimpleKeyPair> generateEDeviceKeyPair() async {
  final algorithm = Ecdh.p256(length: 256);

  final keyPair = await algorithm.newKeyPair();

  return keyPair;
}

// The public key will be generated on the reader application, using this method
Future<EcKeyPair> generateEReaderKeyPair() async {
  final algorithm = Ecdh.p256(length: 256);

  final keyPair = await algorithm.newKeyPair();

  return keyPair;
}

Future<SecretKey> generateSKDevice(EcKeyPairData eDeviceKeyPriv, EcKeyPublic eReaderKeyPub) async {
  final zAB = null;
  final algorithm = Hkdf(
    hmac: Hmac(Sha256()),
    outputLength: 32,
  );

  // HOW TO "MERGE" BOTH KEYS?
  final secretKey = eDeviceKeyPriv + eReaderKeyPub // ??

  final output = await algorithm.deriveKey(secretKey: secretKey, info: utf8.encode("SKDevice"));

  return output;
}

Затем, чтобы использовать сеансовый ключ для шифрования данных:

      Future<List<int>> encryptMDocResponse(Object mdocResponse, SecretKey skDevice) async {
  final algorithm = AesGcm.with256bits();

  // Encrypt
  final secretBox = await algorithm.encrypt(
    utf8.encode(json.encode(mdocResponse)),
    secretKey: skDevice
  );

  return secretBox.cipherText;
}

Проблема заключается вgenerateSKDeviceметод. Он должен получить закрытый ключ от гражданина (eDeviceKeyPriv) и открытый ключ от считывателя (eReaderKeyPub). Мой вопрос: как объединить оба ключа, чтобы получить один, используяderiveKeyметод (криптобиблиотека: https://pub.dev/packages/cryptography)? Делая параллель с Javascript, я мог бы использоватьcrypto.subtle.deriveKeyметод.

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

Заранее спасибо!

РЕДАКТИРОВАТЬ:

      import 'package:cryptography/cryptography.dart';
import 'dart:convert';

Object getMDoc() {
  return {
    "test": "test",
    "test2": {"a": "b"},
    "test3": 3
  };
}

Future<SimpleKeyPair> generateEDeviceKeyPair() async {
  final algorithm = X25519();

  final keyPair = await algorithm.newKeyPair();

  return keyPair;
}

// Changed algorithm to X25519
Future<SimpleKeyPair> generateEReaderKeyPair() async {
  final algorithm = X25519();

  final keyPair = await algorithm.newKeyPair();

  return keyPair;
}

// Generated a sharedSecretKey to derive the session key
Future<SecretKey> generateSKDevice(
    SimpleKeyPairData eDeviceKeyPriv, SimplePublicKey eReaderKeyPub) async {
  final algorithm = X25519();

  final sharedSecretKey = await algorithm.sharedSecretKey(
      keyPair: eDeviceKeyPriv, remotePublicKey: eReaderKeyPub);

  final deriveAlgorithm = Hkdf(
    hmac: Hmac(Sha256()),
    outputLength: 32,
  );

  final skDevice = await deriveAlgorithm.deriveKey(
      secretKey: sharedSecretKey,
      info: utf8.encode("SKDevice"),
      nonce: <int>[1, 2]);

  return skDevice;
}

Future<SecretKey> generateSKReader(
    SimpleKeyPairData eReaderKeyPriv, SimplePublicKey eDeviceKeyPub) async {
  // final algorithm = Ecdh.p256(length: 256);
  final algorithm = X25519();

  final sharedSecret = await algorithm.sharedSecretKey(
      keyPair: eReaderKeyPriv, remotePublicKey: eDeviceKeyPub);

  final deriveAlgorithm = Hkdf(
    hmac: Hmac(Sha256()),
    outputLength: 32,
  );

  final output = await deriveAlgorithm.deriveKey(
      secretKey: sharedSecret,
      info: utf8.encode("SKReader"),
      nonce: <int>[1, 2]);

  return output;
}

Future<SecretBox> encryptMDocResponse(
    Object mdocResponse, SecretKey skDevice) async {
  final algorithm = AesGcm.with256bits();

  final secretBox = await algorithm.encrypt(
    utf8.encode(json.encode(mdocResponse)),
    secretKey: skDevice,
  );

  return secretBox;
}

Future<List<int>> decryptMDocResponse(
    SecretBox mdocResponse, SecretKey skReader) async {
  final algorithm = AesGcm.with256bits();

  final chipherText = await algorithm.decrypt(
    mdocResponse,
    secretKey: skReader,
  );

  return chipherText;
}

// Example of use
Future<void> main(List<String> arguments) async {
  // Holder: get mdoc
  final mdoc = getMDoc();

  // Holder & Reader: generate Ephemeral Keys
  final eDeviceKey = await generateEDeviceKeyPair();
  final eReaderKey = await generateEReaderKeyPair();

  // Holder: generate Session Key (skDevice)
  final skDevice = await generateSKDevice(
      await eDeviceKey.extract(), await eReaderKey.extractPublicKey());

  // Holder: encrypt device response
  final response = await encryptMDocResponse(mdoc, skDevice);

  // Reader: generate Session Key (skDevice)
  final skReader = await generateSKReader(
      await eReaderKey.extract(), await eDeviceKey.extractPublicKey());

  // Reader: decrypt device response
  final decrypted = await decryptMDocResponse(response, skReader); // Error here
}

1 ответ

Как обсуждалось с user9014097 в комментариях:

  • Во-первых: пришлось изменить алгоритм наX25519для поддержки Flutter на мобильных устройствах;
  • Второе: чтобы получить сеансовый ключ, мне пришлось создатьsharedSecret:
      final sharedSecret = await algorithm.sharedSecretKey(keyPair: eReaderKeyPriv, remotePublicKey: eDeviceKeyPub);
  • Третье: используйте то же самоеinfoпараметр в приложениях

(Остальной код доступен в разделе EDITed исходного сообщения)

Другие вопросы по тегам