fix(train): no row's ID tells the instance a date: the days' IDs are keyed by the account (ADR 0030, D57)
A day's, night's and heart chunk's ID was a UUID of version 5 of the person and the date, which the instance can hash for every date itself: each ID told it which days the band was worn. halkyon_keys' keyedRowId hashes the name under the account's X25519 secret key (BLAKE2b, a UUID of version 8), the same on every device of the account and unreadable without its keys; ContentCipher.rowId and TrainKeys.rowId hand it on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
9b9b8d08ca
commit
839d49c4cc
2 changed files with 249 additions and 36 deletions
|
|
@ -69,6 +69,12 @@ abstract interface class ContentCipher() {
|
|||
/// [unlock].
|
||||
bool verify(List<int> message, List<int> signature);
|
||||
|
||||
/// The ID of the row named [name], keyed by the account (halkyon_keys'
|
||||
/// `keyedRowId`, Halkyon Train's ADR 0030, D57): the same on each of its
|
||||
/// devices, and nothing of [name] read from it by the instance. Throws a
|
||||
/// [StateError] before [unlock].
|
||||
String rowId(String name);
|
||||
|
||||
/// Wipes every key kept open here.
|
||||
void dispose();
|
||||
}
|
||||
|
|
@ -213,6 +219,12 @@ class SodiumContentCipher() implements ContentCipher {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String rowId(String name) {
|
||||
final (sodium, account) = _unlocked();
|
||||
return keyedRowId(sodium, account, name);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final key in _keys.values) {
|
||||
|
|
|
|||
|
|
@ -96,6 +96,25 @@ class FakeContentCipher() implements ContentCipher {
|
|||
signature.length == 64 &&
|
||||
_same(Uint8List.fromList(signature), sign(message));
|
||||
|
||||
/// A UUID of version 8 made from the account and [name], as the real
|
||||
/// one's form; but nothing here keys it.
|
||||
@override
|
||||
String rowId(String name) {
|
||||
final account = _unlocked();
|
||||
final bytes = Uint8List(16);
|
||||
final tag = utf8.encode('row|$account|$name');
|
||||
for (var i = 0; i < tag.length; i++) {
|
||||
bytes[i % 16] = (bytes[i % 16] * 31 + tag[i]) & 0xff;
|
||||
}
|
||||
bytes[6] = bytes[6] & 0x0f | 0x80;
|
||||
bytes[8] = bytes[8] & 0x3f | 0x80;
|
||||
final hex = [
|
||||
for (final byte in bytes) byte.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)}';
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() => opened.clear();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue