The encryption components of the Ultra Martian Encryption App, version 1.0.5. import 'dart:typed_data'; import 'package:cross_file/cross_file.dart'; import 'package:crypto/crypto.dart'; import 'dart:math'; import 'dart:io'; import 'package:path/path.dart' as p; import 'package:ultra_martian/encryption_components/gearbox.dart'; class FileEngine { final gearbox = Gearbox(); final String systemExtension = '.martian'; final int hashAt5Mil = 5000000; final requestChunkSize = 10000000; final int modLength = 36; final int verificationBytesLenVariance = 24; final int minVerificationBytesLen = 12; final int checksumLength = 32; final int refreshSliceLen = 16; Future> manager(List passedIn) async { List passwordBytes = []; try { passwordBytes = passedIn[0]; Directory writeDirectory = Directory(passedIn[1]); List xFilePaths = passedIn[2]; String mode = passedIn[3]; bool isUltra = passedIn[4]; List log = []; List filePathsAsStrings = []; for (var each in xFilePaths) { filePathsAsStrings.add(each.path); } if (!writeDirectory.existsSync()) { return ["*Write directory not found, nothing done."]; } if (mode == "encrypt") { for (String filePath in filePathsAsStrings) { File inputFile = File(filePath); if (!inputFile.existsSync()) { log.add("*Input file not found: $filePath"); continue; } int fileLength = inputFile.lengthSync(); if (fileLength == 0) { log.add("*File is empty, cannot be encrypted: $filePath"); continue; } if (filePath.endsWith(systemExtension)) { log.add( "*Encrypt target file already has the $systemExtension extension, $filePath is skipped. Not encrypted again."); continue; } String fileName = '${p.basename(filePath)}$systemExtension'; String outputFilePath = p.join(writeDirectory.path, fileName); if (File(outputFilePath).existsSync()) { log.add( "*Encryption target file exists in write directory, will not overwrite $outputFilePath"); continue; } String result = await chunkEncryptor( passwordBytes, filePath, outputFilePath, fileLength, isUltra); await Future.delayed(Duration(milliseconds: 500)); log.add(result); } } if (mode == "decrypt") { for (String filePath in filePathsAsStrings) { File inputFile = File(filePath); if (!inputFile.existsSync()) { log.add("*File not found: $filePath"); continue; } if (!filePath.endsWith(systemExtension)) { log.add( "*File does not have the $systemExtension extension, $filePath is skipped."); continue; } int fileLength = inputFile.lengthSync(); if (fileLength == 0) { log.add("*File is empty and cannot be decrypted: $filePath."); continue; } String fileName = p.basenameWithoutExtension(filePath); String outputFilePath = p.join(writeDirectory.path, fileName); if (File(outputFilePath).existsSync()) { log.add( "*Target file exists in write directory, will not overwrite $outputFilePath."); continue; } String result = await chunkDecryptor( passwordBytes, filePath, outputFilePath, fileLength, isUltra); log.add(result); await Future.delayed(Duration(milliseconds: 500)); } } if (Platform.isWindows) { for (int i = 0; i < log.length; i++) { log[i] = log[i].replaceAll('/', '\\'); } } return log; } catch (e) { return ["*Catastrophic failure."]; } finally { passedIn.clear(); for (int i = 0; i < passwordBytes.length; i++) { passwordBytes[i] = 0; } } } Future chunkEncryptor( List passwordBytes, String inputFilePathString, String outputFilePathString, int fileLength, bool isUltra) async { IOSink? sink; Uint8List xorCipherData = Uint8List(0); Uint8List verificationBytes = Uint8List(0); Uint8List rollingChecksumBytes = Uint8List(0); BytesBuilder hashBag = BytesBuilder(); Uint8List hashBytes = Uint8List(0); Uint8List payloadBytes = Uint8List(0); BytesBuilder preBuilder = BytesBuilder(); BytesBuilder postBuilder = BytesBuilder(); Uint8List writeFirst = Uint8List(0); Uint8List chunkBytes = Uint8List(0); Uint8List localPasswordBytes = Uint8List.fromList(passwordBytes); try { final outputFile = File(outputFilePathString); int fileChunkReadPosition = 0; final Uint8List modBytes = Uint8List.fromList(gearbox.generateRandomBytes(modLength)); int verificationBytesLen = Random.secure().nextInt(verificationBytesLenVariance) + minVerificationBytesLen; verificationBytes = Uint8List.fromList( gearbox.generateRandomBytes(verificationBytesLen), ); rollingChecksumBytes = Uint8List.fromList( sha256.convert(verificationBytes).bytes, ); if (isUltra) { localPasswordBytes = Uint8List.fromList( await gearbox.stretchPw([...localPasswordBytes, ...modBytes])); } xorCipherData = Uint8List.fromList( gearbox.martian256([...localPasswordBytes, ...modBytes])); List slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); payloadBytes = Uint8List.fromList( await readChunk( filePath: inputFilePathString, startByte: fileChunkReadPosition, chunkSize: requestChunkSize, ), ); fileChunkReadPosition = payloadBytes.length; preBuilder.add([verificationBytesLen]); preBuilder.add(verificationBytes); writeFirst = preBuilder.toBytes(); preBuilder.clear(); bool keepGoing = true; bool firstFileWrite = true; int fileProgress = 0; int chunkPos = -1; int xorCipherPos = -1; sink = outputFile.openWrite(mode: FileMode.write); for (int i = 0; i < writeFirst.length; i++) { xorCipherPos += 1; int byte = writeFirst[i]; hashBag.addByte(byte); byte ^= xorCipherData[xorCipherPos]; postBuilder.addByte(byte); } hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); while (keepGoing) { if (xorCipherPos == xorCipherData.length - 1) { xorCipherData = Uint8List.fromList( gearbox.martian256([...localPasswordBytes, ...modBytes, ...slice]), ); slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); xorCipherPos = -1; } chunkPos += 1; xorCipherPos += 1; fileProgress += 1; int byte = payloadBytes[chunkPos]; hashBag.addByte(byte); byte ^= xorCipherData[xorCipherPos]; postBuilder.addByte(byte); if (hashBag.length == hashAt5Mil && (fileLength - fileProgress) > 100) { hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); } if (chunkPos == payloadBytes.length - 1) { if (firstFileWrite) { firstFileWrite = false; sink.add(modBytes); chunkBytes = postBuilder.toBytes(); sink.add(chunkBytes); postBuilder.clear(); } else { chunkBytes = postBuilder.toBytes(); sink.add(chunkBytes); postBuilder.clear(); } payloadBytes = Uint8List.fromList( await readChunk( filePath: inputFilePathString, startByte: fileChunkReadPosition, chunkSize: requestChunkSize, ), ); fileChunkReadPosition += payloadBytes.length; if (payloadBytes.isEmpty) { /// exit from while loop, after running on keepGoing = false; } chunkPos = -1; } if (!keepGoing) { postBuilder.clear(); hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); sink.add(rollingChecksumBytes); } } return "Success: $inputFilePathString encrypted and written to $outputFilePathString."; } catch (e) { return "*Encrypt process failed, $inputFilePathString."; } finally { if (sink != null) { await sink.flush(); await sink.close(); } for (int i = 0; i < localPasswordBytes.length; i++) { localPasswordBytes[i] = 0; } for (int i = 0; i < xorCipherData.length; i++) { xorCipherData[i] = 0; } for (int i = 0; i < verificationBytes.length; i++) { verificationBytes[i] = 0; } for (int i = 0; i < rollingChecksumBytes.length; i++) { rollingChecksumBytes[i] = 0; } for (int i = 0; i < payloadBytes.length; i++) { payloadBytes[i] = 0; } for (int i = 0; i < writeFirst.length; i++) { writeFirst[i] = 0; } for (int i = 0; i < chunkBytes.length; i++) { chunkBytes[i] = 0; } for (int i = 0; i < hashBytes.length; i++) { hashBytes[i] = 0; } } } Future chunkDecryptor( List passwordBytes, String inputFilePathString, String outputFilePathString, int fileLength, bool isUltra) async { int expectedOriginalFileLength = 0; IOSink? sink; Uint8List last32 = Uint8List(0); Uint8List payloadBytes = Uint8List(0); Uint8List xorCipherData = Uint8List(0); Uint8List verificationBytes = Uint8List(0); Uint8List rollingChecksumBytes = Uint8List(0); BytesBuilder hashBag = BytesBuilder(); Uint8List hashBytes = Uint8List(0); BytesBuilder postBuilder = BytesBuilder(); Uint8List chunkBytes = Uint8List(0); int paddingLength = 0; Uint8List localPasswordBytes = Uint8List.fromList(passwordBytes); try { final inputFile = File(inputFilePathString); final outputFile = File(outputFilePathString); final int fileLength = inputFile.lengthSync(); if (fileLength < modLength + 1 + minVerificationBytesLen + checksumLength + 1) { return "*Decrypt process failed, file too small or corrupt."; } int chunkReadPosition = 0; payloadBytes = Uint8List.fromList( await readChunk( filePath: inputFilePathString, startByte: chunkReadPosition, chunkSize: requestChunkSize, ), ); if (payloadBytes.length >= 32) { last32 = Uint8List.fromList( payloadBytes.sublist(payloadBytes.length - 32), ); } else { last32 = Uint8List.fromList(payloadBytes); } chunkReadPosition = payloadBytes.length; final Uint8List modBytes = Uint8List.fromList( payloadBytes.sublist(0, modLength), ); if (isUltra) { localPasswordBytes = Uint8List.fromList( await gearbox.stretchPw([...localPasswordBytes, ...modBytes])); } payloadBytes = Uint8List.fromList( payloadBytes.sublist(modLength), ); xorCipherData = Uint8List.fromList( gearbox.martian256([...localPasswordBytes, ...modBytes]), ); List slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); sink = outputFile.openWrite(mode: FileMode.write); int chunkPos = 0; int xorCipherPos = 0; int fileProgress = 0; int verificationLengthByte = payloadBytes[chunkPos]; verificationLengthByte ^= xorCipherData[xorCipherPos]; paddingLength = verificationLengthByte; hashBag.addByte(paddingLength); fileProgress += 1; final List verificationList = []; for (int i = 0; i < paddingLength; i++) { xorCipherPos += 1; chunkPos += 1; int byte = payloadBytes[chunkPos]; byte ^= xorCipherData[xorCipherPos]; verificationList.add(byte); hashBag.addByte(byte); fileProgress += 1; } verificationBytes = Uint8List.fromList(verificationList); rollingChecksumBytes = Uint8List.fromList( sha256.convert(verificationBytes).bytes, ); hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); int notPartOfOriginalFileLen = modLength + 1 + verificationLengthByte + checksumLength; expectedOriginalFileLength = fileLength - notPartOfOriginalFileLen; bool keepGoing = true; fileProgress = 0; while (keepGoing) { if (xorCipherPos == xorCipherData.length - 1) { xorCipherData = Uint8List.fromList( gearbox.martian256([...localPasswordBytes, ...modBytes, ...slice]), ); slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); xorCipherPos = -1; } xorCipherPos += 1; chunkPos += 1; int byte = payloadBytes[chunkPos]; byte ^= xorCipherData[xorCipherPos]; postBuilder.addByte(byte); hashBag.addByte(byte); fileProgress += 1; if (hashBag.length == hashAt5Mil && (expectedOriginalFileLength - fileProgress) > 100) { hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); } if (chunkPos == payloadBytes.length - 1) { if (postBuilder.isNotEmpty) { chunkBytes = postBuilder.toBytes(); postBuilder.clear(); sink.add(chunkBytes); } payloadBytes = Uint8List.fromList( await readChunk( filePath: inputFilePathString, startByte: chunkReadPosition, chunkSize: requestChunkSize, ), ); chunkReadPosition += payloadBytes.length; if (payloadBytes.length >= 32) { last32 = Uint8List.fromList( payloadBytes.sublist(payloadBytes.length - 32), ); } else if (payloadBytes.isNotEmpty) { final List combi = [...last32, ...payloadBytes]; last32 = Uint8List.fromList( combi.sublist(combi.length - 32), ); } if (payloadBytes.isEmpty) { keepGoing = false; } chunkPos = -1; } if (fileProgress == expectedOriginalFileLength) { keepGoing = false; } } if (postBuilder.isNotEmpty) { chunkBytes = postBuilder.toBytes(); sink.add(chunkBytes); postBuilder.clear(); } hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); String log; if (gearbox.bytesEqual(rollingChecksumBytes, last32)) { log = "Success: $outputFilePathString written."; } else { log = "*FAILURE: $outputFilePathString written, but the file has failed a check. The password was wrong, the wrong app mode used, the file has been tampered with, or some of the expected data was corrupted. The output file is likely complete garbage, unless a small amount of the input file was corrupted and everything else correct."; } return log; } catch (e) { return "*Decrypt process failed, $inputFilePathString."; } finally { if (sink != null) { await sink.flush(); await sink.close(); } for (int i = 0; i < localPasswordBytes.length; i++) { localPasswordBytes[i] = 0; } for (int i = 0; i < xorCipherData.length; i++) { xorCipherData[i] = 0; } for (int i = 0; i < verificationBytes.length; i++) { verificationBytes[i] = 0; } for (int i = 0; i < rollingChecksumBytes.length; i++) { rollingChecksumBytes[i] = 0; } for (int i = 0; i < payloadBytes.length; i++) { payloadBytes[i] = 0; } for (int i = 0; i < chunkBytes.length; i++) { chunkBytes[i] = 0; } for (int i = 0; i < hashBytes.length; i++) { hashBytes[i] = 0; } for (int i = 0; i < last32.length; i++) { last32[i] = 0; } hashBag.clear(); postBuilder.clear(); paddingLength = 999; } } Future readChunk( {required String filePath, required int startByte, required int chunkSize}) async { RandomAccessFile? raf; try { final file = File(filePath); final fileSize = await file.length(); if (startByte >= fileSize) return Uint8List(0); raf = await file.open(mode: FileMode.read); await raf.setPosition(startByte); final bytesToRead = min(chunkSize, fileSize - startByte); return await raf.read(bytesToRead); } finally { await raf?.close(); } } } import 'dart:convert'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'gearbox.dart'; class TextEngine { final gearbox = Gearbox(); final int modLength = 50; final int textHashInterval = 2500; final int padWithVerificationTo = 24; final int minVerificationLen = 12; final int verificationVariance = 12; final int checksumLength = 32; final int refreshSliceLen = 16; final int base32ChecksumLengthPaddingStripped = 52; static const b32Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; Future> textEncryptor(List passedIn) async { List passwordBytes = []; String inputString = ""; List inputStringBytes = []; String base32Output = ""; Uint8List xorCipherData = Uint8List(0); Uint8List verificationBytes = Uint8List(0); Uint8List rollingChecksumBytes = Uint8List(0); Uint8List hashBytes = Uint8List(0); BytesBuilder hashBag = BytesBuilder(); BytesBuilder postBuilder = BytesBuilder(); try { passwordBytes = passedIn[0]; inputString = passedIn[1]; bool isUltra = passedIn[2]; inputString = inputString.trim(); inputStringBytes = utf8.encode(inputString); inputString = ""; if (inputStringBytes.isEmpty) { return ["Nothing to encrypt."]; } final int inputByteLen = inputStringBytes.length; final String b32ModString = gearbox.generateRandomB32String(modLength); final Uint8List modBytes = Uint8List.fromList(utf8.encode(b32ModString)); if (isUltra){ passwordBytes = await gearbox.stretchPw([... passwordBytes, ... modBytes]); } xorCipherData = Uint8List.fromList( gearbox.martian256([...passwordBytes, ...modBytes])); List slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); List verificationBytes = gearbox.generateRandomBytesForText( inputByteLen, padWithVerificationTo, minVerificationLen, verificationVariance); final int verificationBytesLen = verificationBytes.length; BytesBuilder preBuilder = BytesBuilder(); preBuilder.addByte(verificationBytesLen); preBuilder.add(verificationBytes); final Uint8List headerBytes = Uint8List.fromList(preBuilder.toBytes()); preBuilder.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert(verificationBytes).bytes, ); int inputPos = -1; int xorCipherPos = -1; for (int i = 0; i < headerBytes.length; i++) { xorCipherPos += 1; int byte = headerBytes[i]; hashBag.addByte(byte); byte ^= xorCipherData[xorCipherPos]; postBuilder.addByte(byte); } hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); bool keepGoing = true; while (keepGoing) { xorCipherPos += 1; inputPos += 1; if (xorCipherPos == xorCipherData.length) { xorCipherData = Uint8List.fromList( gearbox.martian256([...passwordBytes, ...modBytes, ...slice]), ); slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); xorCipherPos = 0; } int byte = inputStringBytes[inputPos]; hashBag.addByte(byte); byte ^= xorCipherData[xorCipherPos]; postBuilder.addByte(byte); if (hashBag.length == textHashInterval) { hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); } if (inputPos == inputByteLen - 1) { keepGoing = false; } if (!keepGoing) { final Uint8List outputAsBytes = Uint8List.fromList(postBuilder.toBytes()); postBuilder.clear(); hashBytes = Uint8List.fromList(hashBag.toBytes()); hashBag.clear(); rollingChecksumBytes = Uint8List.fromList( sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes, ); final String cipherB32 = base32EncodeBranchless(outputAsBytes); final String checksumB32 = base32EncodeBranchless(rollingChecksumBytes); base32Output = b32ModString + cipherB32 + checksumB32; } } return ["success", base32Output]; } catch (e) { return ["*Failed."]; } finally { for (int i = 0; i < passwordBytes.length; i++) { passwordBytes[i] = 0; } for (int i = 0; i < xorCipherData.length; i++) { xorCipherData[i] = 0; } for (int i = 0; i < inputStringBytes.length; i++) { inputStringBytes[i] = 0; } for (int i = 0; i < verificationBytes.length; i++) { verificationBytes[i] = 0; } for (int i = 0; i < hashBytes.length; i++) { hashBytes[i] = 0; } hashBag.clear(); postBuilder.clear(); base32Output = ""; } } Future> textDecryptor(List passedIn) async { List passwordBytes = []; String inputString = ""; List encryptedBytes = []; List onlyDecryptedTextBytes = []; String output = ""; String checkStringAsB32 = ""; List expectedChecksum = []; Uint8List hashBytes = Uint8List(0); Uint8List xorCipherData = Uint8List(0); List verificationBytes = []; List rollingChecksumBytes = []; int verificationLength = 0; BytesBuilder hashBag = BytesBuilder(); try { passwordBytes = passedIn[0]; inputString = passedIn[1]; bool isUltra = passedIn[2]; final cleanBuffer = StringBuffer(); for (int i = 0; i < inputString.length; i++) { final int code = inputString.codeUnitAt(i); if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 50 && code <= 55)) { cleanBuffer.writeCharCode(code); } } inputString = cleanBuffer.toString(); if (inputString.length < modLength + base32ChecksumLengthPaddingStripped + minVerificationLen + 3) { return ["Some kind of error"]; } try { checkStringAsB32 = inputString.substring( inputString.length - base32ChecksumLengthPaddingStripped); expectedChecksum = base32Decode(checkStringAsB32); inputString = inputString.substring(0, inputString.length - base32ChecksumLengthPaddingStripped); } catch (e) { return ["Something is not right."]; } final String modString = inputString.substring(0, modLength); inputString = inputString.substring(modLength); final List modBytes = utf8.encode(modString); if (isUltra){ passwordBytes = await gearbox.stretchPw([... passwordBytes, ... modBytes]); } try { encryptedBytes = base32Decode(inputString); } catch (e) { return ["Input cannot be right"]; } if (encryptedBytes.isEmpty) { return ["Input cannot be right"]; } xorCipherData = Uint8List.fromList( gearbox.martian256([...passwordBytes, ...modBytes]), ); List slice = xorCipherData.sublist(xorCipherData.length - refreshSliceLen); hashBag = BytesBuilder(); int decryptionPos = 0; int xorCipherPos = 0; int byte = encryptedBytes[decryptionPos]; byte ^= xorCipherData[xorCipherPos]; verificationLength = byte; hashBag.addByte(verificationLength); decryptionPos += 1; int headerEnd = 1 + verificationLength; List headerBytesLessLenIndicator = encryptedBytes.sublist(1, headerEnd); List encryptedActualContent = encryptedBytes.sublist(headerEnd); for (int i = 0; i < verificationLength; i++) { xorCipherPos += 1; byte = headerBytesLessLenIndicator[i]; byte ^= xorCipherData[xorCipherPos]; verificationBytes.add(byte); hashBag.addByte(byte); } rollingChecksumBytes = sha256.convert(verificationBytes).bytes; hashBytes = hashBag.toBytes(); hashBag.clear(); rollingChecksumBytes = sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes; for (int i =0; i < encryptedActualContent.length; i++) { xorCipherPos += 1; if (xorCipherPos == xorCipherData.length) { xorCipherData = Uint8List.fromList( gearbox.martian256([...passwordBytes, ...modBytes, ...slice]), ); slice = xorCipherData.sublist(xorCipherData.length - 16); xorCipherPos = 0; } byte = encryptedActualContent[i]; byte ^= xorCipherData[xorCipherPos]; onlyDecryptedTextBytes.add(byte); hashBag.addByte(byte); if (hashBag.length == textHashInterval) { hashBytes = hashBag.toBytes(); hashBag.clear(); rollingChecksumBytes = sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes; } } output = utf8.decode(onlyDecryptedTextBytes); hashBytes = hashBag.toBytes(); hashBag.clear(); rollingChecksumBytes = sha256.convert([...rollingChecksumBytes, ...hashBytes]).bytes; if (gearbox.bytesEqual(expectedChecksum, rollingChecksumBytes)) { return ["success", output]; } else { return ["fail", output]; } } catch (e) { return ["Some kind of error. Are you using the app in the same mode as when the ciphertext was produced?"]; } finally { for (int i = 0; i < passwordBytes.length; i++) { passwordBytes[i] = 0; } for (int i = 0; i < encryptedBytes.length; i++) { encryptedBytes[i] = 0; } for (int i = 0; i < xorCipherData.length; i++) { xorCipherData[i] = 0; } for (int i = 0; i < verificationBytes.length; i++) { verificationBytes[i] = 0; } for (int i = 0; i < hashBytes.length; i++) { hashBytes[i] = 0; } for (int i = 0; i < rollingChecksumBytes.length; i++) { rollingChecksumBytes[i] = 0; } for (int i = 0; i < expectedChecksum.length; i++) { expectedChecksum[i] = 0; } for (int i = 0; i < onlyDecryptedTextBytes.length; i++) { onlyDecryptedTextBytes[i] = 0; } hashBag.clear(); checkStringAsB32 = ""; inputString = ""; output = ""; } } final List reversedTable = buildReverseTable(); static List buildReverseTable() { final r = List.filled(256, -1); for (int i = 0; i < b32Alphabet.length; i++) { r[b32Alphabet.codeUnitAt(i)] = i; r[b32Alphabet.toLowerCase().codeUnitAt(i)] = i; } return r; } String base32EncodeBranchless(Uint8List bytes) { final out = StringBuffer(); int buffer = 0; int bits = 0; for (final b in bytes) { buffer = (buffer << 8) | b; bits += 8; while (bits >= 5) { out.write(b32Alphabet[(buffer >> (bits - 5)) & 31]); bits -= 5; } } if (bits > 0) { final idx = (buffer << (5 - bits)) & 31; out.write(b32Alphabet[idx]); } return out.toString(); } Uint8List base32Decode(String input) { input = input.replaceAll('=', ''); try{ int buffer = 0; int bits = 0; final out = BytesBuilder(); for (int i = 0; i < input.length; i++) { final v = reversedTable[input.codeUnitAt(i)]; if (v < 0) { throw FormatException('Invalid Base32 character: ${input[i]}'); } buffer = (buffer << 5) | v; bits += 5; if (bits >= 8) { bits -= 8; out.addByte((buffer >> bits) & 0xFF); } } return out.toBytes(); }catch (e){ return Uint8List(0); } } } import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; class Gearbox { List martian256(List inputBytes) { List outputBytes = []; for (int i = 0; i < 256; i++) { List hashMe = [...inputBytes, i]; Digest digest = sha512.convert(hashMe); outputBytes.addAll(digest.bytes); } outputBytes = outputBytes.reversed.toList(); return outputBytes; } Future> stretchPw(List inputBytes, {int rounds = 640000}) async { const int hashSize = 64; final int totalBytes = rounds * hashSize; final Uint8List buffer = Uint8List(totalBytes); List currentHash = sha512.convert(inputBytes).bytes; final Uint8List hashInput = Uint8List(hashSize + 1); for (int i = 0; i < rounds; i++) { hashInput.setRange(0, hashSize, currentHash); hashInput[hashSize] = i % 256; currentHash = sha512.convert(hashInput).bytes; buffer.setAll(i * hashSize, currentHash); } final List primes = [61027, 75041, 210011, 330017, 450007]; for (int i = buffer.length -1; i >= 0; i--) { int current = buffer[i]; int prime = primes[i % primes.length]; int targetIdx = (prime * (current + 1 + i)) % buffer.length; int targetValue = buffer[targetIdx]; buffer[targetIdx] = current; buffer[i] = targetValue; } return sha512.convert(buffer).bytes; } bool bytesEqual(List a, List b) { if (a.length != b.length) { return false; } int diff = 0; for (int i = 0; i < a.length; i++) { diff |= a[i] ^ b[i]; } return diff == 0; } Uint8List generateRandomBytes(int length) { final random = Random.secure(); final bytes = Uint8List(length); for (int i = 0; i < length; i++) { bytes[i] = random.nextInt(256); } return bytes; } Uint8List generateRandomBytesForText( int contentLength, int padUpTo, int minLen, int variance) { final random = Random.secure(); final length = max(0, padUpTo - contentLength) + minLen + random.nextInt(variance); final bytes = Uint8List(length); for (int i = 0; i < length; i++) { bytes[i] = random.nextInt(256); } return bytes; } String generateRandomB32String(int length) { const b32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; final rand = Random.secure(); return List.generate(length, (_) => b32Chars[rand.nextInt(b32Chars.length)]) .join(); } }