Skip to content

Commit

Permalink
Improve device transfer speed on macOS (#1275)
Browse files Browse the repository at this point in the history
* add common crypto

* display device transfer network speed

* display transfer speed for device trasnfer

* bump common crypto version

* improve hmac calculate
  • Loading branch information
boyan01 authored Jul 6, 2023
1 parent b847ec5 commit d660a8c
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 153 deletions.
159 changes: 159 additions & 0 deletions lib/utils/crypto/aes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import 'dart:typed_data';

import 'package:common_crypto/common_crypto.dart' as cc;
import 'package:flutter/cupertino.dart';
import 'package:pointycastle/block/aes.dart';
import 'package:pointycastle/block/modes/cbc.dart';
import 'package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';

import '../platform.dart';

typedef OnCipherCallback = void Function(Uint8List data);

abstract class AesCipher {
factory AesCipher({
required Uint8List key,
required Uint8List iv,
required bool encrypt,
}) {
if (kPlatformIsDarwin) {
return AesCipherCommonCryptoImpl(key: key, iv: iv, encrypt: encrypt);
} else {
return AesCipherPointyCastleImpl(key: key, iv: iv, encrypt: encrypt);
}
}

static Uint8List _crypt({
required Uint8List key,
required Uint8List iv,
required Uint8List data,
required bool encrypt,
}) {
final cipher = AesCipher(key: key, iv: iv, encrypt: encrypt);
final result = <int>[];
cipher
..update(data, result.addAll)
..finish(result.addAll);
return Uint8List.fromList(result);
}

static Uint8List encrypt({
required Uint8List key,
required Uint8List iv,
required Uint8List data,
}) =>
_crypt(key: key, iv: iv, data: data, encrypt: true);

static Uint8List decrypt({
required Uint8List key,
required Uint8List iv,
required Uint8List data,
}) =>
_crypt(key: key, iv: iv, data: data, encrypt: false);

void update(Uint8List data, OnCipherCallback onCipher);

void finish(OnCipherCallback onCipher);
}

@visibleForTesting
class AesCipherCommonCryptoImpl implements AesCipher {
AesCipherCommonCryptoImpl({
required Uint8List key,
required Uint8List iv,
required bool encrypt,
}) : _aesCrypto = cc.AesCryptor(key: key, iv: iv, encrypt: encrypt);

final cc.AesCryptor _aesCrypto;

var _disposed = false;

@override
void update(Uint8List data, OnCipherCallback onCipher) {
if (_disposed) {
throw StateError('AesCipherCommonCryptoImpl has been disposed.');
}
_aesCrypto.update(data, onCipher);
}

@override
void finish(OnCipherCallback onCipher) {
if (_disposed) {
throw StateError('AesCipherCommonCryptoImpl has been disposed.');
}
_aesCrypto
..finalize(onCipher)
..dispose();
_disposed = true;
}
}

class AesCipherPointyCastleImpl implements AesCipher {
AesCipherPointyCastleImpl({
required Uint8List key,
required Uint8List iv,
required bool encrypt,
}) : _cipher = _createAESCipher(aesKey: key, iv: iv, encrypt: encrypt);

static PaddedBlockCipherImpl _createAESCipher({
required Uint8List aesKey,
required Uint8List iv,
required bool encrypt,
}) {
final cbcCipher = CBCBlockCipher(AESEngine());
return PaddedBlockCipherImpl(PKCS7Padding(), cbcCipher)
..init(
encrypt,
PaddedBlockCipherParameters(
ParametersWithIV(KeyParameter(aesKey), iv),
null,
),
);
}

final PaddedBlockCipherImpl _cipher;
Uint8List? _carry;
List<int>? _preBytes;

@override
void update(Uint8List bytes, OnCipherCallback onCipher) {
final toProcess = _preBytes;
_preBytes = bytes;
if (toProcess == null) {
return;
}
final Uint8List data;
if (_carry == null) {
data = Uint8List.fromList(toProcess);
} else {
data = Uint8List.fromList(_carry! + toProcess);
_carry = null;
}
final length = data.length - (data.length % 1024);
if (length < data.length) {
_carry = data.sublist(length);
} else {
_carry = null;
}
final encryptedData = Uint8List(length);
var offset = 0;
while (offset < length) {
offset += _cipher.processBlock(data, offset, encryptedData, offset);
}
onCipher(encryptedData);
}

@override
void finish(OnCipherCallback onCipher) {
final Uint8List lastBlock;
if (_carry == null) {
lastBlock = Uint8List.fromList(_preBytes ?? []);
} else {
lastBlock = Uint8List.fromList(_carry! + _preBytes!);
}
final encryptedData = _cipher.process(lastBlock);
onCipher(encryptedData);
}
}
74 changes: 74 additions & 0 deletions lib/utils/crypto/hmac.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'dart:typed_data';

import 'package:common_crypto/common_crypto.dart';
import 'package:pointycastle/digests/sha256.dart';
import 'package:pointycastle/macs/hmac.dart';
import 'package:pointycastle/pointycastle.dart';

import '../platform.dart';

Uint8List calculateHMac(Uint8List key, Uint8List data) {
final calculator = HMacCalculator(key)..addBytes(data);
return calculator.result;
}

abstract class HMacCalculator {
factory HMacCalculator(Uint8List key) {
if (kPlatformIsDarwin) {
return _HMacCalculatorCommonCrypto(key);
}
return _HMacCalculatorPointyCastleImpl(key);
}

void addBytes(Uint8List data);

Uint8List get result;
}

class _HMacCalculatorPointyCastleImpl implements HMacCalculator {
_HMacCalculatorPointyCastleImpl(this._hMacKey)
: _hmac = HMac(SHA256Digest(), 64) {
_hmac.init(KeyParameter(_hMacKey));
}

final Uint8List _hMacKey;
final HMac _hmac;

@override
void addBytes(Uint8List data) {
_hmac.update(data, 0, data.length);
}

@override
Uint8List get result {
final bytes = Uint8List(_hmac.macSize);
final len = _hmac.doFinal(bytes, 0);
return bytes.sublist(0, len);
}
}

class _HMacCalculatorCommonCrypto implements HMacCalculator {
_HMacCalculatorCommonCrypto(Uint8List key) : _hmac = HMacSha256(key);

final HMacSha256 _hmac;
var _isDisposed = false;

@override
void addBytes(Uint8List data) {
if (_isDisposed) {
throw StateError('HMacCalculator is disposed');
}
_hmac.update(data);
}

@override
Uint8List get result {
if (_isDisposed) {
throw StateError('HMacCalculator is disposed');
}
final result = _hmac.finalize();
_hmac.dispose();
_isDisposed = true;
return result;
}
}
27 changes: 0 additions & 27 deletions lib/utils/device_transfer/cipher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import 'dart:convert';
import 'dart:typed_data';

import 'package:libsignal_protocol_dart/src/kdf/hkdfv3.dart';
import 'package:pointycastle/digests/sha256.dart';
import 'package:pointycastle/macs/hmac.dart';
import 'package:pointycastle/pointycastle.dart';

import '../crypto_util.dart';

Expand All @@ -28,31 +25,7 @@ TransferSecretKey generateTransferKey() {
return TransferSecretKey(bytes);
}

Uint8List calculateHMac(Uint8List key, Uint8List data) {
final calculator = HMacCalculator(key)..addBytes(data);
return calculator.result;
}

Uint8List generateTransferIv() {
const _kIVBytesCount = 16;
return Uint8List.fromList(generateRandomKey(_kIVBytesCount));
}

class HMacCalculator {
HMacCalculator(this._hMacKey) : _hmac = HMac.withDigest(SHA256Digest()) {
_hmac.init(KeyParameter(_hMacKey));
}

final Uint8List _hMacKey;
final HMac _hmac;

void addBytes(Uint8List data) {
_hmac.update(data, 0, data.length);
}

Uint8List get result {
final bytes = Uint8List(_hmac.macSize);
final len = _hmac.doFinal(bytes, 0);
return bytes.sublist(0, len);
}
}
Loading

0 comments on commit d660a8c

Please sign in to comment.