P-256 ECDH shared key binary triggers Alert 48 in Verilator chip sim while ISS passes
- Dominant language
- SystemVerilog
- Stars
- 3.6k
- Forks
- 1.1k
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 141
Description
## Summary
A self-contained OTBN binary for P-256 ECDH shared key generation (compiled from official upstream `p256_base.s`, `p256_isoncurve.s`, `p256_isoncurve_proj.s`, `p256_shared_key.s`) triggers Alert 48 in Verilator chip simulation, while the same binary passes the OTBN ISS (instruction set simulator). The official `run_p256` binary (which uses `p256_base_mult` instead of `p256_shared_key`) passes chip sim without issues.
## Reproduction
### Environment
- OpenTitan Earl Grey Verilator chip simulation
- All P-256 assembly files copied verbatim from upstream `sw/otbn/crypto/`
### Outer Ibex test BUILD
```python
opentitan_test(
name = "test_p256_only",
srcs = ["ibex/test_p256_only.c"],
exec_env = EARLGREY_TEST_ENVS,
deps = [
"//hw/top/dt",
"//sw/device/lib/dif:otbn",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:entropy_testutils",
"//sw/device/lib/testing:otbn_testutils",
"//sw/device/lib/testing/test_framework:check",
"//sw/device/lib/testing/test_framework:ottf_main",
"//test_ecdh_p256/otbn/p256:p256_ecdh_shared_key",
],
)
```
### OTBN binary BUILD
```python
# test_ecdh_p256/otbn/p256/BUILD
otbn_binary(
name = "p256_ecdh_shared_key",
srcs = [
"p256_ecdh_shared_key_test.s",
"p256_base.s",
"p256_isoncurve.s",
"p256_isoncurve_proj.s",
"p256_shared_key.s",
],
)
```
### Ibex Test Code (test_p256_only.c)
```c
#include "sw/device/lib/dif/dif_otbn.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/entropy_testutils.h"
#include "sw/device/lib/testing/otbn_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
OTTF_DEFINE_TEST_CONFIG();
OTBN_DECLARE_APP_SYMBOLS(p256_ecdh_shared_key);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdh_shared_key, d0);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdh_shared_key, d1);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdh_shared_key, x);
OTBN_DECLARE_SYMBOL_ADDR(p256_ecdh_shared_key, y);
static const otbn_app_t kApp = OTBN_APP_T_INIT(p256_ecdh_shared_key);
static const uint8_t kD0[64] = {
0x71,0x10,0x6d,0xfe,0x16,0xa0,0xd0,0x21,0x81,0xc7,0xb2,0xb0,0x5d,0xef,0x90,0x95,
0x79,0xa3,0xdf,0x3f,0xe8,0xeb,0x76,0x1b,0x63,0x02,0x21,0x74,0x41,0xfc,0x20,0x14,
};
static const uint8_t kD1[64] = {0};
static const uint8_t kGx[32] = {
0x34,0xc3,0xa8,0xbf,0xb3,0xb7,0x73,0x97,0x89,0x06,0x6b,0xf3,0xb2,0xc0,0xc0,0x6e,
0xf3,0x8b,0x6c,0xdb,0x58,0xce,0x28,0x16,0x46,0xc5,0xcd,0xfa,0x6a,0x1a,0x55,0xb5,
};
static const uint8_t kGy[32] = {
0x2e,0x8c,0x00,0x9e,0x58,0x70,0x70,0xa8,0x24,0x69,0x9c,0xab,0xd0,0x11,0x7a,0x7f,
0xfa,0x17,0x3a,0xb5,0xea,0x09,0xdd,0x43,0x43,0xc1,0x31,0x1f,0x97,0xc6,0xa1,0x42,
};
bool test_main(void) {
dif_otbn_t otbn;
CHECK_DIF_OK(dif_otbn_init_from_dt(kDtOtbn, &otbn));
CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
LOG_INFO("Load p256_ecdh_shared_key...");
CHECK_STATUS_OK(otbn_testutils_load_app(&otbn, kApp));
LOG_INFO("Write inputs...");
CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, 64, kD0,
OTBN_ADDR_T_INIT(p256_ecdh_shared_key, d0)));
CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, 64, kD1,
OTBN_ADDR_T_INIT(p256_ecdh_shared_key, d1)));
CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, 32, kGx,
OTBN_ADDR_T_INIT(p256_ecdh_shared_key, x)));
CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, 32, kGy,
OTBN_ADDR_T_INIT(p256_ecdh_shared_key, y)));
LOG_INFO("Execute...");
CHECK_STATUS_OK(otbn_testutils_execute(&otbn));
CHECK_STATUS_OK(otbn_testutils_wait_for_done(&otbn, kDifOtbnErrBitsNoError));
LOG_INFO("P-256 Exec OK");
return true;
}
```
### Run command
```bash
bazel test //test_ecdh_p256:test_p256_only_sim_verilator \
--test_timeout=2000 --cache_test_results=no \
--sandbox_writable_path=/run/user/1000/ccache-tmp --test_output=all
```
### ISS verification (passes)
```bash
bazel test //test_ecdh_p256/otbn/test:p256_ecdh_test \
--sandbox_writable_path=/run/user/1000/ccache-tmp
# PASSED
```
## Error
```
[3761166] ===== OTBN SW ERROR =====
[3761166] PC=0x0050 err_bits=0x008
E00006 ottf_alerts.c:172] ERROR: Alert 48 is asserted but not expected
I00007 status.c:43] FAIL!
[3761166] illegal_insn=1 call_stack=0 bad_data=0 loop=0
[3761166] rf_indirect_err=1 (a=0 b=0 d=1) key_invalid=0
```
- `err_bits=0x008` → `kDifOtbnErrBitsIllegalInsn`
- `rf_indirect_err=1 (d=1)` → destination register index out of WDR range
- `PC=0x0050` → `trigger_fault_if_fg0_not_z` in `p256_base.s`
- FG0.Z is computed incorrectly in RTL simulation while ISS produces the correct value; `trigger_fault_if_fg0_not_z` detects this discrepancy
## Working comparison
The official `//sw/otbn/crypto:run_p256` binary passes chip sim with the same test data, but uses a different code path (`p256_base_mult` via `MODE_BASE_POINT_MULT` dispatch instead of `p256_shared_key`). Both binaries share the same underlying `p256_base.s` code including `trigger_fault_if_fg0_not_z` and `trigger_fault_if_fg0_z`.
## Observations
1. ISS passes → OTBN assembly logic is correct
2. Chip sim fails → RTL flag computation differs from ISS
3. `run_p256` via `p256_base_mult` passes → specific code path matters
4. `p256_shared_key` path fails → `bn.subm` flag issue before `trigger_fault` check
We would appreciate any guidance on whether this is a known RTL simulation artifact or if there is a recommended workaround for running standalone P-256 ECDH shared key binaries in Verilator chip simulation. Thank you!
Contributor guide
Research direction
Start with p256_base.s at trigger_fault_if_fg0_not_z and compare the p256_shared_key.s path with the working p256_base_mult path. Reproduce using //test_ecdh_p256:test_p256_only_sim_verilator, then compare against the passing ISS target //test_ecdh_p256/otbn/test:p256_ecdh_test. Done means the standalone shared-key binary no longer reports the illegal instruction or Alert 48 in Verilator.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- cryptography, embedded-iot, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100