Created
September 13, 2022 00:40
-
-
Save hootyjeremy/58ff2cd7d11423f3cf5e6cd11afd6b3b to your computer and use it in GitHub Desktop.
PointyCastle and Cryptography throughput tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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