blob: fe63af14ba43a66c57d6130330fd118fabce04f8 [file] [log] [blame] [view]
# What is this?
Implementations of XTS and LDT for Nearby Presence "v0" advertisements.
See the appendix below for more details on XTS and LDT.
## Project structure
*Note all new crates follow the convention of using underscore `_` instead of
hyphen `-` in crate names
### `ldt`
An implementation
of [`LDT`](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf)
which can use `xts-aes` as its tweakable block cipher.
### `ldt_tbc`
The Tweakable Block Cipher traits for use in LDT. These traits have
implementations in the `xts_aes`
### `ldt_np_adv`
Higher-level wrapper around the core LDT algorithm that does key derivation and
payload validation the way Nearby Presence advertisements need.
### `ldt_np_adv_ffi`
C API for rust library, currently exposes C/C++ clients the needed API's to use
the NP specific LDT rust implementation.
For an example of how to integrate with these API's see program
in `ldt_np_c_sample`
### `ldt_np_c_sample`
Sample c program which provides its own OpenSSL based AES implementation to
encrypt data through the LDT rust implementation
An example of how to interface with the `ldt_np_adv_ffi` API's
### `np_hkdf`
The Key Derivation functions used for creating keys used by nearby presence from
a key_seed
### `xts_aes`
An implementation
of [`XTS-AES`](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf)
## Setup for MacOS local development
Dependencies:
```
brew install protobuf rapidjson google-benchmark
```
We depend on OpenSSL of version at least 3.0.5 being installed on your machine
to build the fuzzers, for macOS run:
```
brew install openssl@3
```
Your build system may still be picking up an older version so you will have to
symlink to the brew installed version:
```
brew link --force openssl
```
The in-box version of Clang which comes from XCode developer tools does not have
a fuzzer runtime so we will have to use our own
```
brew install llvm
```
then to override the default version it needs to come before it in $PATH. first
find your path:
```
$(brew --prefix llvm)/bin
```
then add this to the beginning of your path
```
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.bash_profile
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
```
verify success with:
```
clang --version
```
it should display the path to the homebrew version and not the xcode version.
Some other dependencies you may need include:
```
brew install ninja bindgen
```
## Examples
Examples use [clap](https://docs.rs/clap/latest/clap/) for nice CLIs, so try
running with `--help` to see all args.
*Note:* the examples are in the `ldt` crate, so `cd` into that first.
### `ldt_prp`
Confirm that LDT is, in fact, behaving as a PRP. That is, flipping one bit in
the ciphertext is on average going to flip half of the bits in the decrypted
plaintext, and that a change to the first `n` bytes of plaintext is increasingly
likely to be detected as `n` increases.
```
cargo run --release --example ldt_prp -- \
--trials 1000000 \
--check-leading-bytes 16
```
### `ldt_benchmark`
For interactive exploration of LDT performance looking for a needle in a
haystack of ciphertexts.
```
cargo run --release --example ldt_benchmark -- \
--trials 500 \
--keys 1000
```
### `ldt_np_c_sample`
From the root directory run the following commands to build and run the C
sample.
```
mkdir -p cmake-build && cd cmake-build
cmake ..
make
./ldt_np_c_sample/sample
```
### `ldt_np_c_sample/tests`
Test cases for the ldt_np_adv_ffi C API which are built alongside the sample,
use the following commands to run the tests, from root of repo:
```
mkdir -p cmake-build && cd cmake-build
cmake .. -DENABLE_TESTS=TRUE
make
cd ldt_np_c_sample/tests && ctest
```
you can then view the output of the tests
in `ldt_np_c_sample/tests/Testing/Temporary/LastTest.log`
To run the benchmarks:
`ldt_np_c_sample/tests/benchmarks`
## Fuzzing
To build all the fuzzers, run `scripts/build-fuzzers.sh`.
### Rust
Crates with fuzzers: `ldt`, `ldt_np_adv`, `xts_aes`
- `cd` to a crate's directory
- `cargo fuzz list` to list available fuzzers
- `cargo fuzz run [fuzzer name]` to run a fuzzer
### C
Build cmake project with `-DENABLE_FUZZ=true`<br>
Fuzz targets will be output to the build dir for:<br>
- `ldt_np_adv_ffi_fuzz`<br>
- `np_cpp_ffi/fuzz`
- To run `fuzzer_np_cpp_deserialize`
use: `./fuzzer_np_cpp_deserialize -max_len=255 corpus`
- The `corpus` directory provides seed data to help the fuzzer generate
more relevant data to input
## Cross-compilation for Android
- Add the 64bit ARM target to the stable and nightly toolchains:
- `rustup target add aarch64-linux-android`
- `rustup target add aarch64-linux-android --toolchain nightly`
- Install the v22 NDK that still links against `libgcc` the way rust's stdlib
expects.
- Newer NDKs use `libunwind` instead, which can be used just fine if you
build your own rust stdlib, but for our purposes there's no problem with
just using NDK 22
- `./sdkmanager --install platform-tools 'ndk;22.0.7026061'`
- Configure the linker used for the ARMv8 Android target to be the NDK's linker.
- `export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$ANDROID_HOME/ndk/22.0.7026061/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang`
- `export PKG_CONFIG_SYSROOT_DIR=$ANDROID_HOME/ndk/22.0.7026061/toolchains/llvm/prebuilt/linux-x86_64/sysroot`
- See if everything builds, using the nightly toolchain for the moment to
convince the `aes` crate to use intrinsics on aarch64
- `cargo +nightly build --workspace --all-targets --target aarch64-linux-android`
- `cargo +nightly bench --workspace --no-run --target aarch64-linux-android`
- Prepare a place for the benchmark to be on the phone
- `adb shell`
- then
- `mkdir -p /data/local/tmp/np && cd /data/local/tmp/np`
- Leave the shell on the phone open so you can use it to run the benchmark.
- Find the benchmark binary in the build products
- Use whatever directory you configured as the `target-dir` in
`.cargo/config.toml` initially, and look for the file without the
trailing `.d`.
- `find TARGET_DIR -name 'ldt_scan*' | grep android`
- Copy the file to the phone
- `adb push FILE_FOUND_ABOVE /data/local/tmp/np/`
- In your `adb shell`, run the benchmark
- `./ldt_scan-... --bench`
### Building min-sized release cross-compiled for Android
- Copy and paste the following into your `~/.cargo/config.toml`, replacing with
a path to your NDK and Host OS
```
[target.aarch64-linux-android]
# Replace this with a path to your ndk version and the prebuilt toolchain for your Host OS
linker = "Library/Android/sdk/ndk/23.2.8568313/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang"
```
- then run:
`cargo +nightly build -Z build-std=core,alloc -Z build-std-features=panic_immediate_abort --target aarch64-linux-android --profile release-min-size`
## Appendix
### XTS
XTS-AES has a NIST
spec: [The XTS-AES Tweakable Block Cipher - An Extract from IEEE Std 1619-2007](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf)
XTS is a scheme for turning a block cipher (AES in this case) into a _tweakable_
block cipher. Tweakable block ciphers incorporate a _tweak_ which is cheaper to
change than the key, with the assumption being that the tweak will change with
each block or sequence of blocks. XTS-AES in particular is used in disk
encryption, where the sector number or the like might be incorporated into the
tweak to prevent the same data in different places on the disk being encrypted
into the same ciphertext.
### LDT
LDT is the current state of the art in length
doublers: [Efficient Length Doubling From Tweakable Block Ciphers](https://eprint.iacr.org/2017/841.pdf)
. It builds on top of a tweakable block cipher, which is why we also have an XTS
implementation.
A length doubler is a way of adapting a block cipher to act as a secure PRP (
pseudo random permutation) on data of lengths in `[block size, 2 * block size)`.
For comparison, block ciphers act as PRPs on one block at a time rather than the
whole message. Wide block modes would also work, but have higher overhead.
We use a length doubler in Nearby Presence so that changing any ciphertext bit
should flip each bit in the decrypted plaintext with 50% probability for each
bit, making it possible to detect changes anywhere because it is very unlikely
for none of the bit flips to affect the metadata key (which has a known digest).