fix: copy the master at each layer so zeroization stays local
This commit is contained in:
parent
d7179694a6
commit
f7452832ea
3 changed files with 19 additions and 5 deletions
|
|
@ -67,7 +67,12 @@ class FumiNative {
|
||||||
/// operation, so it never needs an isolate — and widget tests can settle
|
/// operation, so it never needs an isolate — and widget tests can settle
|
||||||
/// it inside their fake-async zones.
|
/// it inside their fake-async zones.
|
||||||
void setMaster(Uint8List master, {int? rotations}) {
|
void setMaster(Uint8List master, {int? rotations}) {
|
||||||
_master = master;
|
// Own copy: the caller keeps its buffer (the onboarding screen zeroes
|
||||||
|
// its reference on dispose, and wipe zeroes Hive's), and the account
|
||||||
|
// rebuilds after register/restore/rotate must derive from untouched
|
||||||
|
// bytes — a shared buffer zeroized elsewhere would rebuild an account
|
||||||
|
// that matches none of our keys.
|
||||||
|
_master = Uint8List.fromList(master);
|
||||||
_rebuildAccount(rotations: rotations);
|
_rebuildAccount(rotations: rotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,17 @@ class SmolStore {
|
||||||
void setMaster(Uint8List fresh) {
|
void setMaster(Uint8List fresh) {
|
||||||
// Hive's in-memory state updates synchronously and persists in the
|
// Hive's in-memory state updates synchronously and persists in the
|
||||||
// background, so the store is consistent without awaiting the write.
|
// background, so the store is consistent without awaiting the write.
|
||||||
unawaited(_meta.put("master", fresh));
|
// The put takes its own copy: the caller's buffer is the caller's to
|
||||||
|
// zeroize (the onboarding screen does, on dispose), and a shared object
|
||||||
|
// would scrub the store's view with it.
|
||||||
|
unawaited(_meta.put("master", Uint8List.fromList(fresh)));
|
||||||
_native.setMaster(fresh, rotations: 0);
|
_native.setMaster(fresh, rotations: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The master restored from a backup, already at the rotation index the
|
/// The master restored from a backup, already at the rotation index the
|
||||||
/// server bound.
|
/// server bound.
|
||||||
void restoreMaster(Uint8List master, int index) {
|
void restoreMaster(Uint8List master, int index) {
|
||||||
unawaited(_meta.put("master", master));
|
unawaited(_meta.put("master", Uint8List.fromList(master)));
|
||||||
_native.setMaster(master, rotations: index);
|
_native.setMaster(master, rotations: index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,20 @@ void main() {
|
||||||
final store = await freshStore("wipe");
|
final store = await freshStore("wipe");
|
||||||
final master = randomBytes(32);
|
final master = randomBytes(32);
|
||||||
store.setMaster(master);
|
store.setMaster(master);
|
||||||
|
// The store's own copy, read back before the wipe.
|
||||||
|
final stored = store.master()!;
|
||||||
store.pinServer("example.org",
|
store.pinServer("example.org",
|
||||||
"lm2gqd7e5q67xq3isc5hx6jfj2q7a7xvq4l7trctxipudujovmgq");
|
"lm2gqd7e5q67xq3isc5hx6jfj2q7a7xvq4l7trctxipudujovmgq");
|
||||||
store.markRead("cd" * 32);
|
store.markRead("cd" * 32);
|
||||||
|
|
||||||
await store.wipe();
|
await store.wipe();
|
||||||
// The bytes the store owned are overwritten, not just dereferenced —
|
// The bytes the store owned are overwritten, not just dereferenced —
|
||||||
// the caller's reference sees the zeros too.
|
// the reference read back through the store sees the zeros. The
|
||||||
expect(master.every((b) => b == 0), isTrue);
|
// caller's own buffer is the caller's to zeroize: the store holds a
|
||||||
|
// copy precisely so nobody else's zeroization can reach it, and vice
|
||||||
|
// versa a wipe never reaches a buffer the store handed out.
|
||||||
|
expect(stored.every((b) => b == 0), isTrue);
|
||||||
|
expect(master.every((b) => b == 0), isFalse);
|
||||||
expect(store.master(), isNull);
|
expect(store.master(), isNull);
|
||||||
expect(store.identity(), isNull);
|
expect(store.identity(), isNull);
|
||||||
expect(store.serverPin("example.org"), isNull);
|
expect(store.serverPin("example.org"), isNull);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue