Skip to content

Instantly share code, notes, and snippets.

@hootyjeremy
Created September 13, 2022 00:40
Show Gist options
  • Save hootyjeremy/58ff2cd7d11423f3cf5e6cd11afd6b3b to your computer and use it in GitHub Desktop.
Save hootyjeremy/58ff2cd7d11423f3cf5e6cd11afd6b3b to your computer and use it in GitHub Desktop.
PointyCastle and Cryptography throughput tests
import 'dart:convert';
import 'dart:typed_data';
import 'dart:io';
import 'package:cryptography/cryptography.dart';
import 'package:pointycastle/pointycastle.dart' as pointycastle;
import 'package:pointycastle/src/platform_check/platform_check.dart';
// flutter pub add cryptography
// flutter pub add pointycastle
Future<void> main() async {
await encryptWithCryptography();
await encryptWithPointyCastle();
print('done');
}
Future<void> encryptWithCryptography() async {
try {
final stopWatchForAES = Stopwatch();
final algorithm = AesCbc.with256bits(macAlgorithm: Hmac.sha256());
// Generate a random 256-bit secret key
final secretKey = await algorithm.newSecretKey();
// Generate a random 96-bit nonce.
final nonce = algorithm.newNonce();
final inputBytes = List.filled(10000000, 0); // 10 mb
print('---------------------------------------');
print('Beginning encryption with cryptography package...');
stopWatchForAES.start();
final secretBox = await algorithm.encrypt(
inputBytes,
secretKey: secretKey,
nonce: nonce,
);
stopWatchForAES.stop();
print('secretBox.cipherText.length: ${secretBox.cipherText.length}');
inputBytes.clear;
print('duration for cryptography: ${stopWatchForAES.elapsed}');
} catch (e) {
print(e);
}
}
Future<void> encryptWithPointyCastle() async {
try {
final cbc = pointycastle.BlockCipher('AES/CBC');
const passPhrase = 'p@ssw0rd';
Uint8List salt = generateRandomBytes(32)!;
Uint8List key = passphraseToKey(
passPhrase,
salt: latin1.decode(salt),
bitLength: 256,
);
// IV for both encrypt and decrypt (must ALWAYS be 128 bits for AES)
Uint8List iv = generateRandomBytes(128 ~/ 8)!;
cbc.init(
true,
pointycastle.ParametersWithIV(pointycastle.KeyParameter(key), iv),
);
final stopWatchForCBC = Stopwatch();
//Uint8List inputFile = pad(File(inputFileName).readAsBytesSync(), 16);
Uint8List inputBytes = pad(
Uint8List.fromList(List.filled(10000000, 0)),
16,
); // 10 mb
Uint8List outputBytes = Uint8List(inputBytes.length);
int offset = 0;
print('---------------------------------------');
print('Beginning encryption with PointyCastle...');
stopWatchForCBC.start();
while (offset < inputBytes.length) {
offset += cbc.processBlock(
inputBytes,
offset,
outputBytes,
offset,
);
}
stopWatchForCBC.stop();
inputBytes.clear;
print('outputBytes.length: ${outputBytes.length}');
print('duration for PointyCastle: ${stopWatchForCBC.elapsed}');
} catch (e) {
print('error: $e');
}
}
//----------------------------------------------------------------
/// Generate random bytes to use as the Initialization Vector (IV).
Uint8List? generateRandomBytes(int numBytes) {
if (_secureRandom == null) {
// First invocation: create _secureRandom and seed it
_secureRandom = pointycastle.SecureRandom('Fortuna');
_secureRandom!.seed(pointycastle.KeyParameter(
Platform.instance.platformEntropySource().getBytes(32)));
}
// Use it to generate the random bytes
final iv = _secureRandom!.nextBytes(numBytes);
return iv;
}
pointycastle.SecureRandom? _secureRandom;
/// Derive a key from a passphrase.
///
/// The [passPhrase] is an arbitrary length secret string.
///
/// The [bitLength] is the length of key produced. It determines whether
/// AES-128, AES-192, or AES-256 will be used. It must be one of those values.
Uint8List passphraseToKey(
String passPhrase, {
String salt = '',
int iterations = 30000,
required int bitLength,
}) {
if (![128, 192, 256].contains(bitLength)) {
throw ArgumentError.value(bitLength, 'bitLength', 'invalid for AES');
}
final numBytes = bitLength ~/ 8;
final kd = pointycastle.KeyDerivator('SHA-256/HMAC/PBKDF2')
..init(pointycastle.Pbkdf2Parameters(
utf8.encode(salt) as Uint8List, iterations, numBytes));
return kd.process(utf8.encode(passPhrase) as Uint8List);
}
Uint8List pad(Uint8List bytes, int blockSizeBytes) {
// The PKCS #7 padding just fills the extra bytes with the same value.
// That value is the number of bytes of padding there is.
//
// For example, something that requires 3 bytes of padding with append
// [0x03, 0x03, 0x03] to the bytes. If the bytes is already a multiple of the
// block size, a full block of padding is added.
final padLength = blockSizeBytes - (bytes.length % blockSizeBytes);
final padded = Uint8List(bytes.length + padLength)..setAll(0, bytes);
pointycastle.Padding('PKCS7').addPadding(padded, bytes.length);
return padded;
}
Uint8List unpad(Uint8List padded) {
//print(sec.Padding('PKCS7').padCount(padded));
//print(padded.length - sec.Padding('PKCS7').padCount(padded));
return padded.sublist(
0, padded.length - pointycastle.Padding('PKCS7').padCount(padded));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment