PPFDaaS is a privacy-preserving payment-fraud inference system built on CKKS homomorphic encryption. The bank encrypts transaction features locally, the vendor evaluates a linear model on ciphertext only, and only the bank ever decrypts a score. Plaintext transaction data never leaves the bank; the deployed vendor server never holds a secret key.
This README is a map into three primary sources, in priority order for anything contract-sensitive:
docs/spec.md(v1.1) — normative CKKS parameters, proto contract, threat model (§6), rotation-strategy taxonomy (§7), transciphering threat model (§8).PROJECT_STATE.md— session-by-session execution history.AUDIT.md— measurement-integrity findings (CPU governor, cross-architecture confounds, what is/isn't apples-to-apples). Read this before citing any latency number.
The deployed vendor server (vendor_server_160, built from
vendor_server/src/inference_service_160.cpp on EvalContext160,
vendor_server/include/eval_context_160.h) has no seal::SecretKey, no seal::Decryptor,
no seal::KeyGenerator, and no seal::PublicKey/seal::Encryptor anywhere in the linked
process — not even transiently. It can only encode plaintext model weights and evaluate
(multiply_plain, rescale, rotate_vector). Galois keys are never read from a local file;
they arrive only through an explicit provisioning protocol (below) driven entirely by the
bank, over a channel that can be mutually authenticated TLS
(scripts/generate_dev_certs.sh, §6.7). Full semi-honest threat model, adversary model, and
the honest limits of what this system protects (input privacy, not model privacy — see
§6.5): docs/spec.md §6.
A second, legacy 200-bit server (vendor_server_main, inference_service.cpp,
CKKSContext) also exists in this repo, used only for the §5.7 self-ablation measurements.
It does hold a secret key and is a structurally different codebase from
vendor_server_160 — not part of any deployment story. CKKSContext160
(ckks_context_160.{h,cpp}) and the benchmark/benchmark_160 local-circuit binaries are
similarly secret-key-holding, self-timing tools, explicitly out of TCB and never linked into
Dockerfile.server's target (vendor_server_160).
Replacing an earlier design where Galois keys were bind-mounted from a shared host
directory, vendor_server_160 now boots holding no key material, in state
PROV_AWAITING_KEYS, and refuses RunInference (ERR_NOT_PROVISIONED) until the bank
drives it through (proto/inference.proto, vendor_server/include/provisioning_state.h):
PROV_INIT -> PROV_AWAITING_KEYS -> PROV_VALIDATING -> PROV_READY
\ | |
\------------> PROV_FAULT <---------/
ProvisionGaloisKeys— the bank pushes an evaluation-only bundle: serialized Galois keys plus the encryption parameters they were generated under. No bytes that grant decryption are ever sent. Relin keys are deliberately never provisioned — the depth-1 circuit has no ciphertext × ciphertext multiply, so there is nothing for them to do. The server checks the byte stream deserializes correctly, that itsparms_idmatches the server's own hardcoded parameters, and that every element ofROTATION_STEPS = {1,2,4,8,16,32,64,128}is present. Failure →PROV_FAULT.CanaryCheck/CanaryConfirm— this rung alone can catch "structurally valid Galois keys generated under the wrong secret key," and it does so without the secret key ever leaving the bank: the bank encrypts a known constant, the server applies the real production rotation schedule and returns the (still-opaque-to-it) result, and the bank decrypts locally and reports back a pass/fail verdict viaCanaryConfirm.passed→PROV_READY; anything else → terminalPROV_FAULT.- Continuous validation — every subsequent
RunInferencecall re-checksciphertext.parms_id(); 3 consecutive mismatches whilePROV_READYalso tripsPROV_FAULT.
PROV_FAULT is terminal: no in-process recovery, no degraded mode, no substitute-key
fallback — a process restart and full re-provisioning is the only way out. Full state-machine
and adversary-model detail: docs/spec.md §6.2–§6.3.
- A production-oriented Depth-1 CKKS inference path (n=8192), eval-only on the server by construction, with a fail-closed provisioning protocol (above).
- Two CKKS parameter variants at n=8192/tc128 (128-bit security): a 200-bit baseline
(
{60,40,40,60}) and a 160-bit reduced/deployed variant ({60,40,60}) — see "CKKS Parameters" below. - A Degree-2 fallback path (n=16384), fully implemented, automatically selected by an AUC gate when the Depth-1 model doesn't clear an accuracy bar.
- Three named, measured rotation/reduction strategies (SEAL sequential fold, SEAL BSGS
two-layer, and cross-library hoisting comparisons via OpenFHE and Lattigo) —
docs/spec.md§7. - A research arm for HHE/transciphering (HERA-16 symmetric cipher as a CKKS
upload-bandwidth reducer) — client side measured, server side pending on hardware. See
"Transciphering / Hybrid HE (HHE) Arm" below and
docs/spec.md§8. - End-to-end plumbing across C++, Python, Go, gRPC/protobuf, with an honest-measurement
discipline: every timing run is parity-gated against a plaintext oracle
(
scripts/parity_gate.py) before being trusted, and every artifact is reproducible viascripts/reproduce_all.py/make reproduce.
The HE-evaluated model is an independently trained LogisticRegression fit directly on
the 256-feature dataset (compiler/train_logistic_regression.py). XGBoost
(compiler/train_xgboost.py) is trained on the same features and used only to validate
the dataset/feature pipeline and to establish an accuracy ceiling (target AUC ≥ 0.98) — it is
not linearized, distilled, or otherwise compressed into the LR model; there is no
SHAP-based or least-squares surrogate step. The gap between the two is reported directly as
linearization_cost_auc = xgb_test_auc - lr_test_auc
in artifacts/linearization_cost.json, framed honestly as "the accuracy cost of using a
linear model the HE circuit can evaluate at depth 1, relative to a non-linear ceiling," not
as an approximation-error bound on a distillation step.
- Source: the ULB
creditcard.csvdataset (data/creditcard.csv, fetched viascripts/fetch_creditcard_dataset.sh, not tracked in git). compiler/train_xgboost.pyscales, winsorizes, clips, expands with degree-2 polynomial interactions, and truncates to a fixed 256-feature contract.- Batches of up to 16 transactions are packed into a single ciphertext as a 16×256 slot layout (4096 of the 4096 available slots at n=8192, one transaction per 256-slot lane).
Both variants use poly_modulus_degree = 8192, scale = 2^40, SEAL 4.1.2, and
sec_level_type::tc128 (128-bit security), and provision the same restricted Galois key set
{1,2,4,8,16,32,64,128}:
| Variant | coeff_modulus |
Total bits | Spare mult. levels after depth-1 circuit | Status |
|---|---|---|---|---|
| 200-bit baseline | {60,40,40,60} |
200 | 1 | Self-ablation reference only (vendor_server_main) |
| 160-bit reduced | {60,40,60} |
160 | 0 | Deployed default (vendor_server_160) |
n=8192 permits up to 218 total coeff_modulus bits at 128-bit security per the HE standard
parameter tables, so both variants sit at the identical security level — the trade-off is
purely operational (the 160-bit variant has no headroom for future circuit-depth increases
without a full key regeneration). Recommended production default is 160-bit; see
docs/spec.md §5.5 for the exact condition under which 200-bit should be preferred instead.
One multiply_plain (ciphertext × plaintext model weights) → one rescale_to_next → an
8-step sequential-fold tree-sum over the Galois key set above
(vendor_server/src/rotation_hoisting.cpp::hoisted_tree_sum, doubling steps 1→128,
log2(256) = 8). Rotations consume no multiplicative level; only the one multiply_plain
does. After the fold, acc.slot[k*256] holds transaction k's dot product for k = 0..15;
the bias is added server-side as a add_plain_inplace afterward (§6.5) and the client applies
sigmoid.
Selected automatically by compiler/auc_dispatch.py when the Depth-1 LR's AUC falls below
0.92 (borderline retry zone: [0.92, 0.94); primary path: ≥ 0.94). It is a separate CKKS
context and binary, not a config flag:
n = 16384,coeff_modulus = {60,40,40,40,60}— all keys must be regenerated (n=8192 keys are incompatible).- Bank-side plaintext polynomial expansion (
compiler/degree2_linearizer.py,bank_client/backend/feature_pipeline_degree2.py): top-256 linear terms + top-32-feature pairwise interactions (C(32,2) = 496terms, zero-padded) = 512 features per transaction, packed 16×512 into the 8192-slot ciphertext. - The HE circuit itself stays depth-1 (
multiply_plain+ rescale + a 9-step tree-sum for 512 features) — the added expressivity comes entirely from the plaintext feature engineering, not from a deeper HE circuit, so no relinearization keys are needed here either. - AUC gate: ≥ 0.96 required to accept the fallback weights
(
compiler/serialize_degree2_weights.py,artifacts/degree2_weights.bin, 4108 bytes). - On the current dataset/pipeline, Depth-1 AUC is 0.979 (
artifacts/dispatch_result.json) — the primary path is active and the fallback has not been exercised end-to-end in production, only built and unit-tested.
TimingBreakdown carries 5 fields, with deserialization_us explicitly as field 1 (not
an afterthought bolted onto the end) so that ct.load() deserialization time is captured
rather than silently folded into a residual:
message TimingBreakdown {
int64 deserialization_us = 1; // ct.load() time
int64 multiply_plain_us = 2; // multiply_plain + rescale
int64 rotation_hoisting_us = 3; // hoisted_tree_sum
int64 serialization_us = 4; // ct.save() into response
int64 total_inference_us = 5; // full RunInference wall time
}Invariant: deserialization_us + multiply_plain_us + rotation_hoisting_us + serialization_us
≈ total_inference_us (residual ≤ ~0.3 ms). The service also exposes the provisioning RPCs
(ProvisionGaloisKeys, CanaryCheck, CanaryConfirm, GetProvisioningStatus) and a
Phase-7 transciphering canary RPC (CanaryCheckTranscipher, currently stub-only — see below).
Terminology note first: SEAL's public Evaluator::rotate_vector does not expose true
Halevi-Shoup hoisting (shared digit-decomposition across rotations of the same source
ciphertext) — so despite the name, neither strategy below implemented against SEAL is
"hoisted" in that technical sense. Three strategies are measured:
- Sequential fold (
hoisted_tree_sum, SEAL) — 8 rotations of the accumulator, critical path 8, not parallelizable. The only strategy the deployed server actually provisions and runs. - BSGS two-layer (
bsgs_reduction, SEAL, Phase 4.1) — 30 rotations (15 baby + 15 giant) across 2 independent, OpenMP-parallelizable layers. More total work, shorter critical path. Requires a superset Galois key set (30 elements) not provisioned in production. - Hoisted flat (OpenFHE / Lattigo, cross-library) — same 30-rotation BSGS set,
reimplemented against a library that does expose genuine hoisting
(
EvalFastRotation/RotateHoistedNew).
Key finding (§7.5.2, matched-ring N=8192, governor-validated, tools/lattigo_benchmark/):
Lattigo's genuinely-hoisted BSGS was measured ~5x slower (mean), ~4x slower (p99) than
SEAL's 6-core-OMP BSGS at the same ring/rotation-set — decomposed into a 3.72x OMP-parallelism
factor and a 1.34x net Go-vs-C++/language-and-hoisting factor that are not separately
isolated. The "genuine hoisting removes SEAL's public-API ceiling" hypothesis is not
demonstrated by any measurement available in this repo — the one library with a hoisting
API is net-slower at matched thread count, for reasons this repo's data cannot fully
decompose. An earlier attempt at this comparison using OpenFHE (§7.5.1) was invalidated by a
ring-dimension confound (OpenFHE's parameter generator rejects N=8192 at this depth/security
level and silently uses N=16384); that result (SEAL ~25x faster than OpenFHE) is kept for
transparency but is not a clean library-only delta. Full derivation, consistency checks, and
the two remaining un-run comparisons: docs/spec.md §7.5–§7.5.2.
Every latency number in this repo is exactly one of three comparison types, and conflating them is the single most common way to overclaim from this codebase:
- Type 1 — Self-ablation. Same circuit, same hardware, only the CKKS modulus chain differs (200-bit vs 160-bit). This is what the 160-bit-vs-200-bit numbers below are, and only what they are — never cite them as beating an external library or a different reduction strategy.
- Type 2 — Reduction-strategy comparison. Same codebase, same modulus chain (160-bit),
strategy differs (fold vs BSGS vs naive).
artifacts/rotation_strategy_comparison.json,artifacts/execution_matrix.json. - Type 3 — Cross-library comparison. Same circuit, different HE library entirely (OpenFHE, Lattigo), for external validation that the numbers aren't a SEAL-build artifact. OpenFHE's cell is PENDING in this environment (not installed); Lattigo's ran (§7.5.2 above).
cpu_governor=performance, turbo disabled, taskset-pinned to distinct physical cores, 13th
Gen Intel Core i7-13650HX, n=1000/arm, in-band parity gate passed both arms
(artifacts/comparison_results.json):
| mean (µs) | median (µs) | |
|---|---|---|
| 200-bit baseline | 14,877.6 | 14,972.0 |
| 160-bit reduced | 7,538.1 | 7,563.5 |
Reduction: ~49% (mean), ~49% (median), Mann-Whitney U=1,000,000, p≈0. This is a Type 1
self-ablation only — it does not, and has never claimed to, represent a comparison against
any external baseline (the previously-circulated 3.92x figure was retired; see
docs/spec.md §5.7 for the correction history and why an earlier "same binary family"
framing was itself inaccurate — the current clean architecture-matched comparison is
vendor_server/build/benchmark vs benchmark_160, both local-circuit-only,
scripts/privacy_cost_matched_pair.py → artifacts/privacy_cost_matched_pair.json).
Fold beats BSGS (Type 2), and cross-library hoisting does not currently beat SEAL's unhoisted BSGS at matched thread count (Type 3) — see "Rotation/Reduction Strategy Taxonomy" above. Type 3 vs OpenFHE remains PENDING (not installed in this environment).
Hardware/CPU-governor caveat: the current dev host has no root access to switch the CPU
governor by default; numbers above are explicitly governor-validated (performance, turbo
disabled) where stated, and every artifact JSON that isn't carries a "status" field noting
its governor state — do not average across governor states, and see AUDIT.md for why some
percentages (not absolute deltas) moved materially between powersave and performance
re-runs.
The HHE arm replaces the bank's CKKS ciphertext upload with a symmetric-cipher (HERA-16) online phase, keeping the server-side HE computation identical; the vendor's TCB is unchanged (§8.1).
Client side: complete and measured (tools/transciphering/, standalone Go module,
explicitly not part of the deployed TCB — same status as tools/openfhe_benchmark/):
cipher/hera.go/cipher/backend.go— HERA-16 stream cipher (m=16, r=4, t=2^26, 128-bit security under current algebraic analysis; Rubato and Elisabeth-4 were considered and rejected as broken, seedocs/spec.md§8.8).- Measured (
tools/transciphering/results/hera_bench_lane*.json, n=100): client encrypt ~0.53 ms for a single transaction, ~7 ms for a 16-lane batch. - Upload size: as low as 1,052 bytes for a single transaction vs 262,257 bytes for
standard CKKS (~249x smaller); 16,412 bytes vs 262,257 bytes at full 16-lane occupancy
(~16x smaller). Full ladder:
artifacts/bandwidth_ladder.json.
Server side: pending, on hardware, not on missing code. The remaining step —
homomorphic HERA evaluation inside BFV followed by an FV→CKKS repacking (StC + modular
reduction) so the existing CKKS inference circuit runs unmodified — requires
KAIST-CryptLab's ckks_fv (RtF-Transciphering) scheme bridge; standard Lattigo v6.2.0 does
not include this module. The lightest available benchmark
(BenchmarkRtFHera80s) OOM-killed this repo's 15 GB-RAM dev host (exit 137) before reaching
the online transcipher phase — an independent literature source anchors the requirement at
~60 GB RAM for HERA at 80-bit cipher security. A cloud runbook is scaffolded at
scripts/cloud_transcipher_bench/ (target: AWS r7i.4xlarge, 128 GiB,
BenchmarkRtFHera80as) but its README states explicitly it has not been run and should
not be executed without an explicit go-ahead — this is a cost/scheduling decision, not a
blocked task.
Consequently, artifacts/hhe_breakeven.json's online_transcipher_ms and
repacking_ms fields remain "PENDING" and every cell's overall status is "PARTIAL" —
there is no end-to-end HHE-vs-CKKS latency verdict yet. Two literature fallbacks were
checked and both dead-ended (RtF Table 5 is unreachable — HTTP 403 on every accessible
mirror; Presto measures client-side stream-key generation on bank hardware, not server-side
HE evaluation) — see docs/spec.md §8.3 for the full trail.
The honest framing of this arm's current contribution is bandwidth reduction, not latency. §8.10 additionally shows the CPU-time motivation for HHE is regime-dependent, not a blanket win: at single-transaction granularity the server dominates total latency and client-encrypt choice barely matters; at full 16-lane batch occupancy, client-encrypt does become the bottleneck (5.02x the server's amortized per-transaction cost) — but by that same occupancy, HERA-16's own encrypt cost has grown past plain CKKS's (+15.3%), so switching ciphers would not reduce the now-dominant client-side cost at that exact operating point. The surviving, unconditional HHE advantage at every occupancy is upload bandwidth (16–249x smaller), not CPU time. The open empirical question this repo has not yet answered is the bandwidth-savings-vs-server-transcipher-compute break-even — do not read any existing artifact as implying HHE currently beats CKKS end-to-end; it hasn't been measured.
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor # optional, recommended before benchmarking
source .venv/bin/activate
python scripts/demo_e2e.pyOptional benchmark evidence:
python3 tests/benchmark_comparison.py- C++: Microsoft SEAL 4.1.2, gRPC, protobuf, OpenMP, CMake
- Python: scikit-learn, XGBoost, numpy, pybind11 bindings
- Go:
tools/transciphering/,tools/lattigo_benchmark/(research/comparison tools, not in the deployed TCB) - Tooling: Catch2 tests, Python verification and benchmark scripts
- Bank side:
bank_client/bank_client.pybank_client/backend/feature_pipeline_degree2.pybank_client/he_wrapper/seal_wrapper.cpp,seal_wrapper_160.cpp
- Vendor side:
vendor_server/src/,vendor_server/include/vendor_server/include/eval_context_160.h— deployed, eval-only capability surfacevendor_server/include/provisioning_state.h— fail-closed state machinevendor_server/tests/
- Compiler / data pipeline:
compiler/train_xgboost.py,train_logistic_regression.pycompiler/degree2_linearizer.py,serialize_weights.py,serialize_degree2_weights.pycompiler/auc_dispatch.py,gen_keys_160.py
- Interface definitions:
proto/inference.proto,vendor_server/generated/
- Validation and benchmarking:
tests/verify_all.py,tests/benchmark_comparison.py,tests/benchmark_throughput.pyscripts/parity_gate.py— in-band correctness gate, required before any timing is trustedscripts/rotation_strategy_comparison.py,build_execution_matrix.pyscripts/privacy_cost_analysis.py,privacy_cost_matched_pair.pyscripts/e2e_latency_breakdown.py,c3_client_server_comparison.py,bandwidth_ladder.py,hhe_breakeven.pyscripts/reproduce_all.py(make reproduce/make dry-run),demo_e2e.py
- Cross-library / cross-arm research tools (not part of the deployed TCB):
tools/openfhe_benchmark/— OpenFHE hoisted-flat comparison, §7.4/§7.5tools/lattigo_benchmark/— Lattigo hoisted-BSGS comparison, §7.5.2tools/transciphering/— HHE/HERA-16 client-side arm, §8scripts/cloud_transcipher_bench/— scaffolded, not-yet-run cloud runbook for the server-side transcipher benchmarktools/local_benchmark/— secret-key-holding 160-bit benchmark context
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallelNotes:
- If protobuf is installed from Debian packages, CMake module mode for protobuf is expected.
- gRPC plugin path is typically
/usr/bin/grpc_cpp_pluginon Debian-based systems.
The ULB credit-card dataset is required locally as data/creditcard.csv; it is intentionally
not tracked in git (repository size limits).
bash scripts/fetch_creditcard_dataset.shSupports Kaggle CLI mode (automatic, requires kaggle credentials) or manual mode (prints
the exact expected path so you can place the CSV yourself).
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallelbash scripts/fetch_creditcard_dataset.shpython3 compiler/train_xgboost.py
python3 compiler/train_logistic_regression.py
python3 compiler/gen_keys_160.pypython3 tests/verify_all.py
ctest --test-dir vendor_server/build --output-on-failureecho performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
python3 tests/benchmark_comparison.pyNotes:
- The benchmark prints a CPU governor warning when not in
performancemode. - Gate thresholds are calibrated to reference hardware; cross-machine variance is expected.
Professor trace mode (full per-request pipeline trace to stderr):
TRACE=1 python3 tests/benchmark_comparison.pysource .venv/bin/activate
python scripts/demo_e2e.pyIf the script appears stuck with no output, pull latest changes and rerun — startup readiness uses TCP port checks, not buffered server stdout.
python3 scripts/generate_research_artifacts.py
python3 scripts/generate_ablation.py
python3 scripts/generate_roc.pypython3 scripts/show_accuracy_check.py- Result plots and CSV/JSON summaries:
results/ - Benchmark JSON:
artifacts/comparison_results.json - Additional logs:
logs/
Terminal A:
./build/vendor_server/vendor_server_160 artifacts/model_weights.bin 50052Terminal B:
python3 scripts/generate_ablation.py
python3 scripts/demo_e2e.pypython3 tests/verify_all.py
ctest --test-dir vendor_server/build --output-on-failureFor focused performance evidence generation:
python3 tests/benchmark_comparison.py
python3 scripts/generate_research_artifacts.py
python3 scripts/generate_ablation.py
python3 scripts/generate_roc.pyPrefer the performance CPU governor before running tests/benchmark_comparison.py for
stable cross-run comparisons.
This repository's governing rule: a number appears in an artifact or the paper only if it
was produced by executing the thing it describes. No estimated, synthesized, or interpolated
point numbers. Where a measurement could not be made (missing hardware access, missing
library, insufficient RAM), the artifact says "status": "PENDING" with a concrete reason —
never a fabricated value. AUDIT.md is the canonical record of what was checked; key points:
- Every latency number in
artifacts/states the CPU governor it was measured under (hardware_manifest.cpu_governorwhere present) — governor-validated (performance, turbo disabled) figures and supersededpowersavefigures are both kept, clearly labeled; do not mix them. - The §5.8 privacy-cost number was de-confounded: the original measurement compared two
structurally different server architectures (200-bit decrypt-capable legacy service vs
160-bit eval-only service), not just two modulus chains. The corrected,
architecture-matched number is in
artifacts/privacy_cost_matched_pair.json; the old, confounded number is kept underdeployed_cross_architecture_e2e_delta_DEPRECATEDfor transparency, not citation. - OpenFHE vs SEAL BSGS (§7.5.1) is confounded by ring dimension (OpenFHE's own parameter generator rejects N=8192 at this depth/security level and silently uses N=16384); the matched-ring comparison (§7.5.2) uses Lattigo instead and is the one that should be cited for the hoisting question.
- Server-side HHE transciphering (FV→CKKS) is PENDING for a RAM reason, not a missing-code
reason — see "Transciphering / Hybrid HE (HHE) Arm" above and
tools/transciphering/README.md.
- Normative engineering spec and contracts:
docs/spec.md(CKKS parameters, proto fields, threat model §6, rotation taxonomy §7, transciphering §8) - Implementation handoff and sprint notes:
PROJECT_STATE.md - Measurement-integrity audit (governor, confounds, transciphering blocker):
AUDIT.md - Remediation history and current one-line status:
PPFDaaS_REMEDIATION_PLAN.md
For any interface- or contract-sensitive change (proto fields, timing-breakdown semantics,
CKKS parameterization, threat-model claims), follow docs/spec.md first and treat
PROJECT_STATE.md as execution history. Before citing any latency/throughput number in a
paper or presentation, check AUDIT.md and the artifact's own status /
*_DEPRECATED fields first.
- The Degree-2 fallback path is built and unit-gated (AUC ≥ 0.96) but has not been exercised through a live end-to-end gRPC demo in this repository state (the current dataset's Depth-1 AUC of 0.979 keeps the primary path active).
tools/openfhe_benchmark/'sfold/naivecells and the fullreduction_strategy x modulus_chain x libraryexecution matrix beyond what's listed above remain PENDING — OpenFHE is not installed in this environment.- Cloud pricing for the
r7i.4xlarge/r7i.8xlargetransciphering benchmark instances has not been pulled/confirmed as of this writing.