change the notification modal to accept the primary message and an
Option of independent text to display as a qrcode
pub fn show_notification(&self, notification: &str, qrtext:Option<&str>) -> Result<(), xous::Error> {}
Probably nobody is really using secret Bases right now, but it's
probably a good idea to lay out all the infrastructure and start
testing it for when it comes time to do it with data that really
matters.
Previous version was v0.0.1.1
New version is v0.0.2.1 (don't ask me why I skipped v0.0.2.0)
The most significant change is that the key derivation for the basis
is now split into two keys. One key is used for the page table ECB
encoding, and a different key is used for the data AES-GCM-SIV encoding.
This is because someone much smarter than me looked at my scheme
and said it is very dangerous to use the same key for two different
types of ciphers. This person is correct, so it had to be rectified.
For the System Basis, the keys are encrypted using the root keys
wrapper API. In this case, we simply create two random keys, one for
the page table, and one for the data region, and wrap them using
the root keys API.
For user secret Basis, the derived keys are sent through HKDF
with two different "info" strings to derive a pair of keys from
a common password.
In case the cryptographer who looked at this is reading along,
note that the basis name is encoded as a fixed-length, 0-padded
record, so, we don't encode the length of the basis in the hash
used to derive the keys. When I explained the scheme I thought
I was using a variable-length basis name to derive the key, but
when I checked the code, it's always 64 bytes long, and bytes
that are not used are 0-padded, so, I think we don't need the
length encoding field.
This doesn't work because the shutdown call destroys all the servers then terminates
the processes. But, the problem is that the processes are still
running, but servers are disappearing. So what you end up with is a
panic when the first message gets delivered to a server that no longer
exists, and then you end up with the program hanging on that panic
because some servers haven't been destroyed/exited yet and are now
just spinning waiting to be scheduled. so the call works in the tidy
kernel test cases but to get it to work on a full OS you'd need to
have a way to put every server into an eternal blocking state before
making the shutdown call (to prevent new messages from being issued
while servers are going away), which is more work than i'm willing to
put into the problem right now.
An edge case was missed where a page which was allocated out of
the FSCB and then later written to disk was having its journal
number set to 0.
The root cause of the logic issue is that the page table itself
does not store a journal number. It's assumed that the page table
is ground truth, and is unjournaled.
As long as the pages don't exist in the FSCB, that's not a problem,
the journal number doesn't matter.
But, let's say this sequence happens:
1. FSCB allocates some free space
2. process takes that free space allocation and commits it to its own page table
3. FSCB update log gets a note that free space is now allocated
4. system reboots
5. process reads its page table from disk. There are no journals associated
with the page table on disk, so, it assigns a journal number of 0 by default.
6. we now have a conflict between the Process' view of the journal age
of that page table entry, and the FSCB's view.
The fix to this is during the page table scan, to check and see if
any candidate pages happen to already exist inside the FSCB. If it does,
then you adopt the latest journal number for that page in the FSCB.
This keeps the two views consistent.
A second bug was also identified while fixing this where the "Random"
order we were hoping for in visiting the free space cache as an
iterator is less random than we had hoped. So, there is a patch to
extract the free pages and pull one random one from the pool, using
entropy coming from the TRNG. This is less computationally efficient,
but at least it is correct and has more transparent properties
than relying on the indeterminism of the HashSet implementation.
- add a "dangerous debug" command that only exists when the feature
"pddbtest" is enabled. The feature can't work on real hardware, but
still, it enables things like dumping of the database and storing
the keys for inspection, so it's isolated in a feature that prevents
the code from being compiled in the first place when it's not selected.
- fix a bug where keys that have zero length aren't being created
correctly. this is a common use case for reserving some space for
data that isn't yet available.
I did observe some PDDB corruption that happened today that looks
ostensibly linked to a record that didn't sync up with the disk,
so, let's be safer and sync everything. We can work on optimizing
performance after correctness is guaranteed.
The problem with the current is_mounted() call is that it blocks
while the PDDB is requesting a password or actively formatting
(otherwise it doesn't block). It's actually just...partially blocking.
That doesn't cut it for libstd-linked system services that need to run but
before the PDDB comes up. In particular, we need to be able to respond
to time queries *somehow*, even before our timezone and offsets are
loaded from the PDDB.
I considered doing a callback mechanism for mounting, but this
is kind of complex and probably not worth it. Instead, a separate
thread that can return the value of a single boolean revealing
the mount state of the PDDB is created, and you can create an
object that's capable of inspecting that boolean.
This will create a 100MiB file in tools/ppdb-images/hosted.bin
(sorry!!) which is persistent storage for hosted mode.
Delete the file if you want to start over from scratch.
It's...pretty brittle. I expect problems, but maybe it will at
least get things moving.
thanks again to Michael Rosenberg for catching this bug and
teaching about about how to use the Subtle/.ct_eq() traits to
implement constant time equality checks.
So you'd like to think that an authenticating cipher would like,
authenticate stuff on decryption. And AES-GCM-SIV was billed as sort
of AES with the pointy edges taken off.
Well, there's always one more bug, actually this time a salamander:
https://eprint.iacr.org/2020/1456.pdf Apparently one can forge
authentication if the attacker can control keys and ciphertext. In the
case of the PDDB I think the attack is largely theoretical, because
you'd have to forge through both the AES-GCM-SIV and the rather
insufficient nonce/murmur hash check on the PTE that locates the page
to be decrypted.
But it's never good to have cryptographic properties depend upon
something that's known to be insufficient, so let's assume the attacker
can simply forge keys and stick some data on the device.
This wouldn't be a problem for confidentiality but it could lead to
the ability to plant evidence, but more importantly could lead to code
paths that were thought to be protected against fuzzing being fuzzed
as you could conceivably create a Basis root record that "checked out"
and then the larger body of the PDDB code would try to chomp on other
pages that are forged, leading to a bigger attack surface. Plus who
knows what else might exist beyond salamanders...
This patch does a "key commitment" on the Basis root page, so that it
is now cryptographically hard to create a salamander. Basically
instead of using the system root key directly in AES-GCM-SIV, the key
goes through a KDF that expands it with yet another nonce plus a bit
of header data. The KDF is SHA512/256 and it expands it twice,
changing one bit in the header data (as prescribed by the paper cited
above), to create a version of the key that is written to disk (the
commitment), and a version of the key that is used to
decrypt/authenticate the AES-GCM-SIV data. Only if both the commitment
and the authentication check out, do we accept a Basis root page as
valid.
I had some concerns about the slightly different structure of the
Basis Root page being detectable, but I have been assured that the key
commitment form and the MAC in AES-GCM-SIV satisfy a property known as
IND$-CPA ("indistinguishable from uniform randomness by a
chosen-plaintext attacker") by Rogaway[1], and, more strongly, "Secure
nonce misuse-resistant authenticated encryption (mrAE)" by Gueron and
Lindell (see appendix for def)[2]. Still, just out of total paranoia
and nothing else, I organize the basis root page such that the MAC is
in the identical location as the other non-committed pages...just in
case there is some mathematical property of the MAC that could be
leveraged to leak info about the existence of the basis root record.
The commitment nonce and sha512/256 result I understand better and
feel pretty comfortable that they are going to be strongly random and
indistinguishable from AES ciphertext.
Thanks to Michael Rosenberg for taking the time to explain all this to
me and to guide me through the process of making the fixes. It is a
major PDDB API-breaking change and I'm glad it's able to be rolled out
before most users have received a device.
[1] https://web.cs.ucdavis.edu/~rogaway/papers/ad.pdf
[2] https://eprint.iacr.org/2015/102