100 lines
2.9 KiB
Dart
100 lines
2.9 KiB
Dart
import 'dart:typed_data';
|
|
import 'dart:math';
|
|
|
|
import 'package:pointycastle/export.dart';
|
|
|
|
class EncryptedVideoPayload {
|
|
final Uint8List encryptedFileBytes;
|
|
final String contentId;
|
|
final String contentKeyHex;
|
|
final String encryptedFileName;
|
|
|
|
EncryptedVideoPayload({
|
|
required this.encryptedFileBytes,
|
|
required this.contentId,
|
|
required this.contentKeyHex,
|
|
required this.encryptedFileName,
|
|
});
|
|
}
|
|
|
|
Future<EncryptedVideoPayload> encryptVideoForUpload(
|
|
Uint8List sourceBytes,
|
|
) async {
|
|
final random = Random.secure();
|
|
final keyBytes = Uint8List.fromList(
|
|
List<int>.generate(16, (_) => random.nextInt(256)),
|
|
);
|
|
final nonceBytes = Uint8List.fromList(
|
|
List<int>.generate(8, (_) => random.nextInt(256)),
|
|
);
|
|
|
|
final contentId = _generateUuidV4(random);
|
|
final contentIdBytes = _uuidToBytes(contentId);
|
|
final contentKeyHex = _bytesToHex(keyBytes);
|
|
|
|
final counterBlock = Uint8List(16);
|
|
counterBlock.setRange(0, nonceBytes.length, nonceBytes);
|
|
|
|
final cipher = CTRStreamCipher(AESEngine())
|
|
..init(
|
|
true,
|
|
ParametersWithIV<KeyParameter>(KeyParameter(keyBytes), counterBlock),
|
|
);
|
|
final encryptedData = cipher.process(sourceBytes);
|
|
|
|
final magic = Uint8List.fromList('MYPLR1'.codeUnits);
|
|
const version = 1;
|
|
final headerSize = magic.length + 1 + contentIdBytes.length + nonceBytes.length;
|
|
final finalBytes = Uint8List(headerSize + encryptedData.length);
|
|
|
|
var offset = 0;
|
|
finalBytes.setRange(offset, offset + magic.length, magic);
|
|
offset += magic.length;
|
|
finalBytes[offset] = version;
|
|
offset += 1;
|
|
finalBytes.setRange(offset, offset + contentIdBytes.length, contentIdBytes);
|
|
offset += contentIdBytes.length;
|
|
finalBytes.setRange(offset, offset + nonceBytes.length, nonceBytes);
|
|
offset += nonceBytes.length;
|
|
finalBytes.setRange(offset, offset + encryptedData.length, encryptedData);
|
|
|
|
return EncryptedVideoPayload(
|
|
encryptedFileBytes: finalBytes,
|
|
contentId: contentId,
|
|
contentKeyHex: contentKeyHex,
|
|
encryptedFileName: '$contentId.spot',
|
|
);
|
|
}
|
|
|
|
String _bytesToHex(Uint8List bytes) {
|
|
final buffer = StringBuffer();
|
|
for (final byte in bytes) {
|
|
buffer.write(byte.toRadixString(16).padLeft(2, '0'));
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
|
|
Uint8List _uuidToBytes(String uuid) {
|
|
final hex = uuid.replaceAll('-', '');
|
|
final output = Uint8List(16);
|
|
for (var i = 0; i < 16; i++) {
|
|
output[i] = int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
String _generateUuidV4(Random random) {
|
|
final bytes = List<int>.generate(16, (_) => random.nextInt(256));
|
|
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
|
|
final hex = bytes
|
|
.map((b) => b.toRadixString(16).padLeft(2, '0'))
|
|
.join();
|
|
return '${hex.substring(0, 8)}-'
|
|
'${hex.substring(8, 12)}-'
|
|
'${hex.substring(12, 16)}-'
|
|
'${hex.substring(16, 20)}-'
|
|
'${hex.substring(20, 32)}';
|
|
}
|