Commit graph

886 commits

Author SHA1 Message Date
bunnie
d6c662b378 memoize some common clock settings
this reduces boot time because the space to search for
an arbitrary clock setting is pretty big.
2025-12-18 02:22:38 +08:00
bunnie
9093bb4ed7 simplify keystore setup
The TL;DR is this now treats dabao exactly the same as
baosec in terms of keystore setup from the security flow perspective.

baosec & dabao were different originally because baosec would
rely upon the Avalanche generator TRNG as supplemental randomness
and dabao would not. This special case had to be handled in the
bootloader and propagated through.

However, in a previous commit, the keystore setup was moved to
the OS *anyways* - thus making this difference moot.

This patch backs out the one-way counters that would track the
dabao initialization state.

It also doesn't fail the boot on the erase key proof check
failing if the keystores aren't initialized yet. This creates
yet another condition that could be glitched over to bypass
the erase check but it is checked twice indepedently.
2025-12-18 02:16:50 +08:00
bunnie
d874c3e5c2 commit for version bump 2025-12-16 02:02:37 +08:00
bunnie
b31c7d411d major rework to clock settings
this is a big, ugly, multi-file commit but it turns out when
you start shaking one part of the clock tree everything rattles
along with it.

Basically this updates the clock tree to use a search for an
optimal setting instead of a hueristic/table lookup, and then
a bunch of small bugs in the clock settings were found that had
to be proopagated to dependent crates.

i2c clock set now seems ... more correct, the frequency is
not overclocking the bus (was at 600khz before, now just below 400khz)

the uart equations finally sees sane

the spi clocks check out

the only inconsistency left is that i can't seem to get a fractional
division on peri clk. it would be really nice to be able to do a
fractional divide and get a little more peformance out of the spi
busses but i think it just doesn't work that way - the "fractional"
division is actually more of a "time-average frequency is correct"
but when you go into fractional mode it's spending some time pulsing
at 2x the rate and some time at 1x the rate to get to the time average
frequency, and when you're at the 2x rate things break.
2025-12-16 01:02:00 +08:00
bunnie
679ece9b19 major rework of the clock-set routines
the routines provided in the reference crossbar libraries are
not great and the recco of their team is to just use hard-coded
table lookups. Replaced this with a computational method that can
find arbitrary clocks; however, the computation *is* somewhat expensive
because you have to iterate through a fairly large solution space
to find an "optimal" solution.

That being said, these solutions can be memoized if they are
frequently re-used. I rather prefer this method than just an undocumented
table lookup.

More testing is needed but the initial results look pretty good.

Also peripheral clock setting is reworked to be more clear as to what
is going on. I think I finally have some clarity on why a mysterious
"divide by 2" was ending up in everything; other dependencies have
been reworked to clean that up. I think actually I may have been
overclocking the peripheral bus by a factor of 2 prior to now :-/
2025-12-16 00:58:37 +08:00
bunnie
021cf609e4 remove wfi apis from hal
this is now in the susres crate
2025-12-16 00:58:10 +08:00
bunnie
8dc1e9c837 rework backup RAM hashing to ignore the erase proof slice
a slice of key material is written to backup RAM from boot0
all the way to the loader, to prove to the loader that secret
keys were erased in a higher privilege state (loader then erases
the key, and this key is considered leakable without harm to
security).

The problem is that writing the key to backup RAM in boot0
was fixing the hash on the backup block, which is before
the check happens to see if it was a cold power-on. This would
defeat the RTC setting & backup setup logic.

THe solution to this is to exclude that one key from the hashing
area and special case it. A set of routines and API names were
added to help enforce this case.
2025-12-15 15:19:01 +08:00
bunnie
3fd9087b7f commit a partially working wfi cycle 2025-12-11 02:28:37 +08:00
bunnie
df3529d595 add power down/up opcodes to the gfx layer 2025-12-11 02:21:40 +08:00
bunnie
261ea7a880 update apis in hal-service 2025-12-11 02:18:44 +08:00
bunnie
8950a24327 move i2c in bao1x-hal-services into bao1x-hal
this makes i2c accessible without having to rely explicitly
on the server itself, making the library callable in more contexts.
necessitated avoiding a name space collision and moving the
I2C hardware drivers to a stucture named I2cDriver; the I2C
object now unambiguously refers to the userspace interface
to I2C.
2025-12-11 02:13:21 +08:00
bunnie
8269f9f759 update apis to accommodate power savings mode 2025-12-11 02:10:25 +08:00
bunnie
0a47620da2 add powerdown api to sh1107 2025-12-11 02:09:15 +08:00
bunnie
a3a566b240 add API calls for debugging kernel under baochip targets
also updated some really bit-rotted docs

we will deprecate the docs/ directory soon, they are wrong
and misleading at this point.
2025-12-09 16:19:04 +08:00
bunnie
41d09786ae tighten up clock gates in boot0
confirmed that these settings do not affect the glitch sensors.
2025-12-08 16:54:23 +08:00
bunnie
a3f9a9942d update APIs to use the central clocking implementation 2025-12-08 15:58:27 +08:00
bunnie
11b506bdc4 build a consolidated clock setting routine
This routine pares down the clock setting routine to hopefully
a core minimum set of side-effects while allowing the system
to boot.

It also takes the register bank offsets as arguments, as well
as various functions to accomplish different tasks as argument,
so that it can be used in different contexts (such as in `std`).

Finally, the BIO clock rate can be set to slow or fast. The power
cost of the fast setting is about 12mW. If you're not needing
the high speed features of the BIO, slow setting is probably what
you want.

Currently, the system is configured such that after boot1, the
BIO is running in slow mode (to save power for battery powered
configurations); and then the clock set calls in baremetal and
dabao bump this up to the fast speed since these two configurations
are assumed to be running off of a USB wire with ample power.
2025-12-08 15:55:20 +08:00
bunnie
4e0b67878a remove old code 2025-12-07 19:00:29 +08:00
bunnie
ebb7e8dfae refactor QR code notification implementation
Previous implementation sent the QR code as draw commands.
This is inefficient as it would require hundreds of draw
commands to be sent per QR code rendered.

This implementation codes the QR code as a bool and sends
it in a single memory message, and implements a QR code renderer
on the receiver side.

The QR code rendering is also cleaned up so that the QR code is
always centered and takes the full screen, to give the best
conditions for someone scanning the code from another device.
2025-12-07 18:58:33 +08:00
bunnie
53c7a729f7
add primitives for interrupt-based glitch handlers
glitches can trigger interrupts from several sources:

- ECC errors on multiple blocks
- consistency check errors on hardened logic
- light sensor
- voltage sensor
- glue cells

These primitives respond to these glitches in an interrupt
context. At the moment, by default a fairly lax approach is
taken to responding to the glitches: partially, we want to make
sure that the sensors aren't too sensitive and trigger by accident.
The final thresholds can be adjusted fairly readily.

In addition, the mesh can trigger an interrupt, and typically,
that interrupt is a false reading (because the mesh takes a
long time to settle, and we boot the system while the mesh
settles). Thus, we take an approach of polling the mesh
effectively, and when interrupts happen due to the mesh we
just clear it and ignore it and allow the polling routine
to manage the mesh. The mesh is theorized to be a static
attack, anyways.
2025-12-07 01:33:53 +08:00
bunnie
15ac4d8529
add primitives to buram for storing slices
this is used by the key erase proof routine, but will be
useful in general for storing data in the buram

also, the ranges of slices are currently defined at
the top of the file but might be moved somewhere more
maintainable later.

RRAM writing got bollards added to it, as RRAM write could
be a glitch target: if a glitch could manipulate the
address somehow to stick stuff at an arbitrary location,
during write, that would be bad. However, it's probably
also reasonably dangerous to try and glitch RRAM writes
because there's a chance you spray over off-target items
and thus trigger a signature check failure on your next
boot.
2025-12-06 18:36:51 +08:00
bunnie
09e68291cf
during key erasure, add the proof that secret keys were erased 2025-12-06 18:35:43 +08:00
bunnie
eee9cd62c9
enable secure erase confirmation in loader (leaks one chaff key)
A problem was discovered while implementing the secure boot
hardening. Namely, an attacker could trick the loader into
loading a devkey-signed kernel by incrementing the
developer mode counter without actually triggering the
secure erase of the keys, and finding a way to glitch
over the check of the developer mode counter check.

Two countermeasuers are implemented:

1. if the developer mode flag is set, trigger a secure erase
even if the image signatures aren't developer mode

2. leak one of the chaff keys into the loader, so there are
now at least two indpendent checks of being in developer
mode in the loader. One is based on the counter, which
is only weakly correlated with the erased state. The
other is checking for the erase value in the leaked key,
which is a strong indicator of being in developer mode.

The leaked key is, of course, erased before entering the
kernel (and the key is leaked into a region of memory
that is, in theory, securely claimed by the keystore, so
it's not like anyone can just read it; however, it is
in a less secure location than the protected key area).

The impact of an adversary obtaining the key is only
a slight weakening of security because the chaff keys
are numerous, they still have thousands of bits to
guess even if they got a couple hundred of them.

The next series of patches implements the scheme outlined
above, starting with defining the key to be leaked. The
leaked key is intentionally the last key in the slice
of keys used in the erasure algorithm, so that presumably
if the leaked key is showing an erased value, all other
keys have been erased.
2025-12-06 18:24:40 +08:00
bunnie
9ddf6beae6
move the kernel start address
this correctly gives the loader 256k of space, which is what
its link file specifies. The loader was previously under 128k
by chance, but with glitch hardening it's grown past that
boundary.
2025-12-06 18:23:10 +08:00
bunnie
b99b4cb665
cleanup signature checking API
Signature checking parameters are now consolidated into
a single "SecurityConfiguration" record. This allows the
sum total of security state transitions to be summarized in
the bao1x-api/src/pubkeys/mod.rs file, easing maintenance
and audit.

Reaction to the signature check is split into a `hardened_erase_policy`
API call which can take the results of the signature check and
react to it if necessary.

This allows the signature check routine to be used for both
audit (where reaction is not necessary) and for live testing
of the code (where reaction is required). It also simplifies
the implementation of double-checking signatures in the case
of paranoid mode.

Another important improvement is that the signature checking
API outputs the target address for the jump, but masked with
an XOR of the function code of the signed block. The purpose
of the XOR mask isn't to obfuscate the value; rather, it's
to prevent there being a single, simple, fixed-constant
jump instruction laying around that a glitcher can use as
a target: instead, a pair of registers must be loaded
with the target and its mask. This is an example of what
the final jump looks like:

```
60003a8e:	ffffffff          	.word	0xffffffff
60003a92:	0440006f          	j	60003ad6 <bao1x_hal::hardening::die>
60003a96:	ffffffff          	.word	0xffffffff
60003a9a:	167d                	addi	a2,a2,-1
60003a9c:	fe71                	bnez	a2,60003a78 <rust_entry+0x2f74>
60003a9e:	a00d                	j	60003ac0 <rust_entry+0x2fbc>
60003aa0:	0360006f          	j	60003ad6 <bao1x_hal::hardening::die>
60003aa4:	ffffffff          	.word	0xffffffff
60003aa8:	02e0006f          	j	60003ad6 <bao1x_hal::hardening::die>
60003aac:	ffffffff          	.word	0xffffffff
60003ab0:	0260006f          	j	60003ad6 <bao1x_hal::hardening::die>
60003ab4:	ffffffff          	.word	0xffffffff
60003ab8:	01e0006f          	j	60003ad6 <bao1x_hal::hardening::die>
60003abc:	ffffffff          	.word	0xffffffff
60003ac0:	0149c533          	xor	a0,s3,s4
60003ac4:	600805b7          	lui	a1,0x60080
60003ac8:	d0058593          	addi	a1,a1,-768 # 6007fd00 <_etext+0x6e9dc>
60003acc:	82aa                	mv	t0,a0
60003ace:	852e                	mv	a0,a1
60003ad0:	4581                	li	a1,0
60003ad2:	8282                	jr	t0
```

So here you can see the alternating bollards in the region that
will cause any glitches that don't land exactly right to
either halt the machine or cause a reaction.

Line 0x6003ac0 is where the xor happens to compute the target
address. Should a glitcher land on that address, `s3` and `s4`
will need to be loaded with the target address and a value when
XOR'd with it that yields a meaningful result. While there is
a non-zero chance that this can be arranged, it's definitely
not as easy as targeting a jump-to-fixed address exit sequence.
2025-12-05 22:57:34 +08:00
bunnie
d9d146352f
ensure that hardened_get2 is used correctly with an assert 2025-12-05 22:43:49 +08:00
bunnie
32ffba0931
introduce security configurations
The management of different security configurations was getting
a bit out of hand, so all the security configurations used by
the system and their transitions are now consolidated into a single
file, This will make auditing and checking the transitions a lot
easier.
2025-12-05 21:37:21 +08:00
bunnie
1dbb65cc97
zerorize backup registers & fix aoram range in zeroize
backup registers are a high priority target on zeroizing
because it would contain the ephemeral unlock secret, e.g.
PIN code.
2025-12-05 00:59:59 +08:00
bunnie
9e5c57b7de
inline the pll check
this prevents it from being a single area that has to be patched
to bypass the pll check
2025-12-04 15:30:43 +08:00
bunnie
362124127e
add redundant counters, paranoid mode, attack counters
This allocates some additional one way counters dedicated
to hardening:

- Paranoid mode is duplicated in slots 65 & 67
- An attack counter is in slot 66
- Redundant revocation counters are seated in slot 68

The primary revocation counters are in slot 116. This means
that cutting out the revocation counters would probably require
at least two edits to the memory array - simply cutting the
word addressing line for those columns might be enough to
defeat a counter.

Paranoid mode duplicate is stored somewhat close to the
primary counter, which means it could be subject to a single
line cut to fully defeat the counter, but the space is getting
pretty crowded so I'll leave it like that; I really want to
leave the bottom 64 slots available for application use.

A "flag" key is allocated, plus a dummy flag is committed
to the repo. This is a value that, if retrieved, would
ostensibly bring the retriever glory and bounty, to the
extent that any glory or bounty is to be had (ps: I'm poor
and this project is obscure). The actual flag, of course,
is not in the repo...I hope!

The flag value is different from all other values in that
this is the only shared, common secret across all devices.

All other device secrets are generated on a per-device
basis from the TRNGs. So the flag needs some special treatment
such that it's not overwritten during device keying.

The flag exists because I'm hoping that anyone who manages
to extract it will also provide a helpful explanation of
the procedure to get it out, so that the system could be
patched (if the error is patchable) or rev'd in future
silicon. That being said, a flag retrieval is not necessarily
equal to a full break of security across all devices because
as mentioned above, most device secrets are on a per-device
basis. The flag represents then, the amount of effort necessary
to apply to every device to retrieve the secrets within.
2025-12-04 14:56:07 +08:00
bunnie
31385a5599
start moving clock routines into an API library
clock setting calls, for legacy reasons, are duplicated in all the
stages of the bootloaders. This is a start toward moving these routines
into a common set of calls that improves maintainability.
2025-12-04 14:54:26 +08:00
bunnie
bd08328530
add bollard macro and HardenedBool
These macros are added to the platform-agnostic API crate.

bollard is a macro that can respond to the following argument
formats:

bollard!(): inserts 4 illegal instructions and a jump over them
bollard!(#): inserts # of illegal instructions and a jump over them
bollard!(f, #): inserts # of an interleave of illegal instructions
and a jump to f

the last form is intended to "up the ante" by including a call
to a routine that can shut down the chip and/or wipe secrets
should the defender choose to do that. The other ones just
trampoline into an illegal instruction vector which is a fairly
weak defense because the CPU could once again get glitched out
of that.

HardenedBool is a boolean represented by a high-hamming distance
numeric value so that single-bit flips just lead to an invalid
bool, instead of a change in the meaning of the bool.
2025-12-04 14:50:33 +08:00
bunnie
75fb1a1889
Harden the signature check routine
- key revocation check is fully duplicated and tested twice
- bollards & random delays added throughout the code. Random
delays require passing in a csprng object from the calling
environment.
- the primary go/no-go decision on whether a signature passes
or not is unfortunately not hardenable because the API is in
a third party crate. However, the regions around that call are
random-delayed and surrounded with bollards
- the state bits that determine whether the system is secure
and/or in developer mode are implemented using HardenedBool
- erase_secrets has some delays added to it as well as bollards.
However, it does remain a weakness that an attacker
could pull RRAM power during the erase cycle by just isolating
the RRAM power supply and putting a fast current limit on it,
thus preventing effective erasure by starving the circuit
of power.

Nothing we can really do about an active attack on the power
supply to prevent erasure, I think. It is unfortunately required
by design to have an isolated power supply for the RRAM to perform
elevated-voltage forming at CP time, and so, it should always
be possible to limit the current going into it thus preventing,
at least somewhat, any programming operations on the array.
2025-12-04 14:36:28 +08:00
bunnie
05af4f3f62
add hardened_get() and hardened_get2() to one way counters
One way counters are used ot record some security-critical properties,
such as key revocations and paranoid mode.

Two methods of retrieving the counters so that they are hardened
are provide here. Both of them rely on the fact that any value
other than 0 for the counter means it is set.

`hardened_get()` retrieves the counter value and sums it several
times. It does this *twice*, and returns *both* values as a tuple.
By returning both values as a tuple, it reduces the likelihood
that the compiler optimizes out the redundant read. The receiver
should implement serial checks that operate on the hardened_get()
result (i.e., test one value of the tuple, then test the other
value of the tuple redundantly, instead of OR'ing them together
into a single test)

`hardened_get2()` is for counters that are fully duplicated in
the array. For extremely critical counters, they are stored in
two physically separate elements. This is done to make bypass
of the counter harder than just FIB'ing out a single bit in
the memory array. In the case of `hardened_get2()`, a tuple
is returned, but instead of reading the same value twice, the
tuple corresponds to the summed value over each of the redundant
counters.
2025-12-04 14:29:30 +08:00
bunnie
9294c06d45
Add glitch hardening library
This library contains the following primitives:

check_pll() - checks that the PLL is the primary clock source.
This is important because allowing direct clocking of the system
from the external clock pin makes glitching much, much easier.

die() - a routine that implements a reaction to attacks. At the
moment, we just note attacks and then halt the machine. Eventually,
this could be upgraded to be more reactive, e.g. erasing secrets
or bricking the chip.

Csprng - this is a chacha8 cipher seeded with ring oscillator
TRNG data, plus whatever tiny bit of entropy we can scavenge
from uninitialized RAM (to make it so that it's not as trivial
as cutting the RO wire and tying it to 0 to bypass the library).
There's also a tiny health check on the RO before using it, again,
to make it harder than just cutting the RO wire. The sole purpose
of the Csprng is to generate random delays that frustrate glitching
attacks. The delay is currently tuned to a range of 0-255 iterations
of a loop.

paranoid_mode() - turns on fully automatic reactive sensor
countermeasures. Whenever a voltage glitch is detected, the system
will just reboot.

mesh countermeasures - these set up the secure mesh and the
reaction policy to tamper detection on the secure meshes

apply_attack_policy() - an API call that applies the currently
determined countermeasure in case that the detected attacks
counter has gone above a threshold. The reason we don't simply
react right away is that some countermeasures are fairly
sensitive, and *also*, very importantly, the chip *is* packaged
in a CSP and thus especially for dabao users you will expect
to see random trips of the attack sensors due to environmental
noise (photoflashes, nearby lightning strikes, etc.).
2025-12-04 13:34:01 +08:00
bunnie
852a12645b round out stateful list creation in the ux-api libraries 2025-11-30 21:33:04 +08:00
bunnie
106f7153a1 filter key presses through the video layer
the video layer is the "traffic cop" managing when modals
get focus. This patch creates a feed from the video layer
that listers to subscribe to that filters out key presses
destined for modals.
2025-11-28 18:40:56 +08:00
bunnie
738797754b make widgets respond to the "fire" button 2025-11-28 18:40:33 +08:00
bunnie
720680a931 build a hosted mode dummy for aqcuire qr 2025-11-27 22:10:57 +08:00
bunnie
74134bfac2 add ux-api acquisition features 2025-11-27 16:52:44 +08:00
bunnie
bc4e459e28 convert spim txrx timeout from a hard-error into an error message
occassionally when the bus is loaded, some SPIM transactions won't
complete. this needs to be looked into a little more, i think...
2025-11-27 12:34:40 +08:00
bunnie
7e368d231f add mod refs for kpc_aoint 2025-11-26 20:47:28 +08:00
bunnie
59e8707b1a add hardware abstractions/translation maps for baosec keyboard 2025-11-26 20:47:10 +08:00
bunnie
b021a1bcd3 fix typo 2025-11-26 20:46:54 +08:00
bunnie
40c94b5abc add kpc_aoint abstraction layers 2025-11-26 20:45:12 +08:00
bunnie
1ca144f47f remove stale constants; add an irq handler storage class
constants made obsolete in the latest utra update are removed,
and a storage class for multi-purpose interrupt handlers is added.
2025-11-25 19:08:53 +08:00
bunnie
1ebf9b8d94 use the newly extracted utra; fix ranges
in xous, you can't make a range smaller than 4096 bytes,
so range lengths have to be enforced at an abstraction layer
above the page allocator
2025-11-25 19:08:07 +08:00
bunnie
765a36bd3a add option to defer draw updates
this is helpful for composited UIs
2025-11-25 19:07:32 +08:00
bunnie
d30d889774 add rtc hardware drivers and service 2025-11-24 18:28:00 +08:00
bunnie
960ba350cb add bu_mem locations
these failed to extract automatically from the RTL
2025-11-23 08:48:24 +08:00