nkbmusic/lib/offline/song_cipher.dart
metatroncubeswdev 178d2d00ab Nanga Kathai: Flutter app, self-hosted backend and song tools
- Flutter app (Android/iOS): home, explore, albums, search, player with
  equalizer, mini player, sign up/sign in with email codes, profile,
  encrypted offline downloads, About page
- server/: one-command deploy of self-hosted Supabase behind HestiaCP/nginx
  (or Caddy), migrations, backups, email templates
- supabase/migrations: schema, row-level security, profiles
- tools/: song scanner and importer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:39:26 -04:00

54 lines
1.6 KiB
Dart

import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
/// AES-256 in counter mode, so any byte range of a song can be decrypted on
/// its own (seeking without reading the whole file). Encrypting and decrypting
/// are the same operation.
///
/// File layout: 8-byte random nonce, then the encrypted song.
class SongCipher {
SongCipher(Uint8List key) : _aes = AESEngine()..init(true, KeyParameter(key));
static const headerLength = 8;
static const _block = 16;
final AESEngine _aes;
static Uint8List newNonce() {
final r = Random.secure();
return Uint8List.fromList(List.generate(headerLength, (_) => r.nextInt(256)));
}
static Uint8List newKey() {
final r = Random.secure();
return Uint8List.fromList(List.generate(32, (_) => r.nextInt(256)));
}
/// XORs [data] with the key stream for song position [offset] (bytes after the header), in place.
void apply(Uint8List data, int offset, Uint8List nonce) {
final counter = Uint8List(_block)..setRange(0, headerLength, nonce);
final stream = Uint8List(_block);
var blockIndex = -1;
for (var i = 0; i < data.length; i++) {
final pos = offset + i;
final b = pos ~/ _block;
if (b != blockIndex) {
blockIndex = b;
_writeCounter(counter, b);
_aes.processBlock(counter, 0, stream, 0);
}
data[i] ^= stream[pos % _block];
}
}
static void _writeCounter(Uint8List counter, int blockIndex) {
var v = blockIndex;
for (var i = _block - 1; i >= headerLength; i--) {
counter[i] = v & 0xff;
v >>= 8;
}
}
}