extract method

Future<Uint8List> extract({
  1. required Uint8List imageBytes,
  2. required Uint8List key,
  3. required SteganographyMethod method,
})

Implementation

Future<Uint8List> extract({
  required Uint8List imageBytes,
  required Uint8List key,
  required SteganographyMethod method,
}) async {
  if (method != SteganographyMethod.telegramRobust2 &&
      !isSupportedCarrierImage(imageBytes)) {
    throw ArgumentError.value(
      imageBytes.length,
      'imageBytes',
      'Not a PNG or JPEG (invalid signature)',
    );
  }
  Uint8List? packed;
  try {
    packed = await _native.extractPng(
      pngBytes: imageBytes,
      method: steganographyMethodToInt(method),
    );
  } on PlatformException catch (e) {
    throw _mapPlatform(e);
  }
  if (packed == null) {
    throw StateError('No steganographic payload found');
  }
  final encrypted = switch (method) {
    SteganographyMethod.telegramRobust2 => StegoPayload.tryUnpackRobust(
        packed,
        keyBytes: key,
      ),
    SteganographyMethod.dct => StegoPayload.tryUnpackRobust(
        packed,
        keyBytes: key,
      ),
    SteganographyMethod.dctResidualModulation => StegoPayload.tryUnpackDrm(
        packed,
      ),
    _ => StegoPayload.tryUnpack(packed),
  };
  if (encrypted == null) {
    throw StateError('Invalid payload header');
  }
  try {
    return await SteganographyCrypto.decrypt(encrypted, keyBytes: key);
  } catch (e) {
    final macLike = e.toString().contains('MAC') ||
        e.toString().contains('authentication') ||
        e.toString().contains('SecretBoxAuthentication');
    if (macLike) {
      throw StateError(
        'AES-GCM MAC mismatch: wrong key or payload corrupted after recompression. '
        'Use SteganographyMethod.telegramRobust2 for lossy channels.',
      );
    }
    throw StateError('Decryption failed: $e');
  }
}