KeepassX format implementation in pure dart.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

211 lines
6.0 KiB

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:kdbx/kdbx.dart';
/// A bitmask that limits an integer to 32 bits.
const mask32 = 0xFFFFFFFF;
/// The number of bytes in a 32-bit word.
const bytesPerWord = 4;
class ByteUtils {
static final _random = Random.secure();
static Uint8List randomBytes(int length) =>
Uint8List.fromList(List.generate(length, (i) => _random.nextInt(1 << 8)));
static bool eq(List<int> a, List<int> b) {
if (a.length != b.length) {
return false;
}
for (var i = a.length - 1; i >= 0; i--) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
static String toHex(int val) => '0x${val.toRadixString(16).padLeft(2, '0')}';
static String toHexList(List<int>? list) =>
list?.map((val) => toHex(val)).join(' ') ?? '(null)';
}
extension Uint8ListExt on Uint8List {
String encodeBase64() => base64.encode(this);
}
class ReaderHelper {
factory ReaderHelper(Uint8List? byteData) => KdbxFormat.dartWebWorkaround
? ReaderHelperDartWeb(byteData!)
: ReaderHelper._(byteData!);
ReaderHelper._(this.byteData) : lengthInBytes = byteData.lengthInBytes;
final Uint8List byteData;
int pos = 0;
final int lengthInBytes;
// ByteData _nextByteBuffer(int byteCount) {
// final ret = ByteData.view(data, pos, pos += byteCount);
// pos += byteCount;
// return ret;
// }
// ByteData _nextByteBuffer(int byteCount) =>
// ByteData.view(data, pos, (pos += byteCount) - pos);
// ByteData _nextByteBuffer(int byteCount) {
// try {
// return ByteData.view(data, pos, byteCount);
// } finally {
// pos += byteCount;
// }
// }
ByteData _nextByteBuffer(int byteCount) => _advanceByteCount(
byteCount,
() => ByteData.view(
byteData.buffer, pos + byteData.offsetInBytes, byteCount));
Uint8List _nextBytes(int byteCount) => _advanceByteCount(
byteCount,
() => Uint8List.view(
byteData.buffer, pos + byteData.offsetInBytes, byteCount));
T _advanceByteCount<T>(int byteCount, T Function() func) {
try {
return func();
} finally {
pos += byteCount;
}
}
int readUint8() => _nextByteBuffer(1).getUint8(0);
int readUint16() => _nextByteBuffer(2).getUint16(0, Endian.little);
int readUint32() => _nextByteBuffer(4).getUint32(0, Endian.little);
int readUint64() => _nextByteBuffer(8).getUint64(0, Endian.little);
int readInt32() => _nextByteBuffer(4).getInt32(0, Endian.little);
int readInt64() => _nextByteBuffer(8).getInt64(0, Endian.little);
Uint8List readBytes(int size) => _nextBytes(size);
String readString(int size) => const Utf8Decoder().convert(readBytes(size));
Uint8List readBytesUpTo(int maxSize) =>
_nextBytes(min(maxSize, lengthInBytes - pos));
Uint8List readRemaining() => _nextBytes(lengthInBytes - pos);
static int singleUint32(Uint8List? bytes) => ReaderHelper(bytes).readUint32();
static int singleUint64(Uint8List? bytes) => ReaderHelper(bytes).readUint64();
}
class ReaderHelperDartWeb extends ReaderHelper {
ReaderHelperDartWeb(Uint8List byteData) : super._(byteData);
@override
int readUint64() {
final lo = readUint32();
final hi = readUint32();
print('lo: $lo / hi: $hi ---- ');
return (hi << 32) + lo;
}
@override
int readInt64() {
return readUint64();
}
}
typedef LengthWriter = void Function(int length);
class WriterHelper {
factory WriterHelper([BytesBuilder? output]) => KdbxFormat.dartWebWorkaround
? WriterHelperDartWeb(output)
: WriterHelper._(output);
WriterHelper._([BytesBuilder? output]) : output = output ?? BytesBuilder();
final BytesBuilder output;
void _write(ByteData byteData) => output.add(byteData.buffer.asUint8List());
void writeBytes(Uint8List bytes, [LengthWriter? lengthWriter]) {
lengthWriter?.call(bytes.length);
output.add(bytes);
// output.asUint8List().addAll(bytes);
}
void writeUint32(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(4);
_write(ByteData(4)..setUint32(0, value, Endian.little));
// output.asUint32List().add(value);
}
void writeUint64(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(8);
_write(ByteData(8)..setUint64(0, value, Endian.little));
}
void writeUint16(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(2);
_write(ByteData(2)..setUint16(0, value, Endian.little));
}
void writeInt32(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(4);
_write(ByteData(4)..setInt32(0, value, Endian.little));
}
void writeInt64(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(8);
_write(ByteData(8)..setInt64(0, value, Endian.little));
}
void writeUint8(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(1);
output.addByte(value);
}
static Uint8List singleUint32Bytes(int val) =>
(WriterHelper()..writeUint32(val)).output.toBytes();
static Uint8List singleUint64Bytes(int val) =>
(WriterHelper()..writeUint64(val)).output.toBytes();
int writeString(String value, [LengthWriter? lengthWriter]) {
final bytes = const Utf8Encoder().convert(value);
lengthWriter?.call(bytes.length);
writeBytes(bytes);
return bytes.length;
}
}
class WriterHelperDartWeb extends WriterHelper {
WriterHelperDartWeb([BytesBuilder? output]) : super._(output);
@override
void writeUint64(int value, [LengthWriter? lengthWriter]) {
lengthWriter?.call(8);
final _endian = Endian.little;
final highBits = value >> 32;
final lowBits = value & mask32;
final byteData = ByteData(8);
if (_endian == Endian.big) {
byteData.setUint32(0, highBits, _endian);
byteData.setUint32(0 + bytesPerWord, lowBits, _endian);
} else {
byteData.setUint32(0, lowBits, _endian);
byteData.setUint32(0 + bytesPerWord, highBits, _endian);
}
_write(byteData);
}
@override
void writeInt64(int value, [LengthWriter? lengthWriter]) {
writeUint64(value, lengthWriter);
}
}