Skip to content

cipher: stop building the hash and RSA halves nothing calls - #220

Merged
widgetii merged 2 commits into
mainfrom
cipher-trim-hash-rsa
Sep 6, 2026
Merged

widgetii merged 2 commits into
mainfrom
cipher-trim-hash-rsa

Conversation

@widgetii

@widgetii widgetii commented Sep 6, 2026

Copy link
Copy Markdown
Member

open_cipher.ko ships at 172,328 bytes installed, and about half of it is two subsystems with no caller. On gk7202v300-lite that module is the whole +171.1 KB the firmware-explorer trend attributes to hisilicon-opensdk — byte for byte, nothing else moved. Today it stopped being cosmetic: gk7205v300_lite failed CI four kilobytes over its 5120 KB partition.

Why a driver for a few algorithms is 172 KB

Three reasons, and none of them is the AES:

  1. It is not a register poker. The vendor tree is a complete crypto stack — symmetric engine, hash engine, RSA/IFEP engine, key ladder, TRNG.
  2. The RSA path carries a software mbedTLS implementation. bignum.c (2670 lines), rsa.c, rsa_internal.c, asn1parse.c, oid.c — a userspace big-number library compiled into a kernel module as the fallback for the hardware block.
  3. A .ko gets no dead-code elimination. Modules are linked with ld -r; there is no --gc-sections. Every object the Kbuild names ships whether or not an ioctl reaches it. INSTALL_MOD_STRIP=1 is only --strip-debug and recovers 1.2 KB, because there was no debug info to begin with.

Where the 110,772 bytes of code and data went:

bytes share
RSA (IFEP driver + mbedTLS bignum/RSA/ASN.1) 39,392 35.6%
symmetric — the AES the camera actually uses 33,919 30.6%
plumbing (dispatch, osal, drv_lib) 18,214 16.4%
hash (SHA-1…SHA-512, HMAC, SM3) 14,064 12.7%
klad + OTP 3,636 3.3%
TRNG 925 0.8%

The remaining 62 KB of the file is ELF metadata the module loader consumes — 32 KB of relocations, 30 KB of symtab/strtab. Not strippable, but it scales down with the code.

Nothing calls hash or RSA

majestic is the only consumer of /dev/cipher on these boards — it goes straight to the ioctls rather than through libhi_cipher.so — and its include/majestic/hisi/cipher_abi.h is explicit about using neither:

What the SRTP transform needs is here … and what the recorder's chip-bound key mode needs: the key ladder's wrap call and the hard-key selector, and the TRNG. No hash (measured five times slower than mbedTLS) and no RSA (once per clip, mbedTLS does it in software).

The engine's HMAC-SHA1 closes its handle on finish and so reprograms the key for every packet — 192.4 µs against mbedTLS's 37.4. The recorder's public-key mode is RSAES-OAEP once per clip, in userspace.

Verified by symbols rather than by reading: the two subsystems are reached through exactly twelve entry points, all of them from kapi_dispatch.c. Nothing in symc, klad, the TRNG or the OTP path touches them. Re-checked independently against the cv500 tree, which is a different SDK vintage — same answer, same twelve.

What changes

kapi_hash_rsa_absent.c supplies those twelve; the objects come off the Kbuild lists. Also gone are the _v100 cores and the ext_* extensions, which compile to empty objects under these parts' CHIP_TYPE.

The dispatch table is deliberately left whole. crypto_ioctl() indexes dispatch_func[] by the command's nr and then checks the row's cmd matches, and majestic derives its command numbers from the same struct sizes the driver does — a removed row lands every later command on the wrong handler, and the only thing that catches it reports "copy data from user failed", which is a lie about a table that is simply off by one. All 17 rows and their handlers stay; HashStart and RsaEncrypt now return HI_ERR_CIPHER_UNSUPPORTED instead of running code no camera reaches.

Vendor sources are untouched and still in the tree. Restoring either subsystem is putting its objects back on the list and dropping the stub file — not a merge.

Two copies of the stub rather than one shared file, because the trees are not interchangeable: cv500's kapi_rsa_* take a cryp_rsa_crypt_data where ev200's take six arguments, its kapi_hash_finish takes the output buffer length, and its log macros are lower case.

Measured

Both built against their own kernel and toolchain, baseline and trimmed in the same setup:

installed compressed (xz -9e)
before after before after
gk7205v200 / hi3516ev200 172,328 96,188 (−44.2%) 51,844 28,760 (−23.1 KB)
hi3516cv500 171,380 97,208 (−43.3%) 52,468 29,476 (−22.5 KB)

Text drops 90,992 → 44,644 on the ev200 build. All 74 mbedTLS symbols gone, all 17 dispatch rows present, every remaining undefined symbol an ordinary kernel or osal one.

Against a 4 KB overflow on gk7205v300_lite, that is roughly 6× the margin needed, and it lands on hi3518ev300_lite too — the board the hisilicon-opensdk.mk comments describe as sitting at 5120/5120 KB with sensors already trimmed to make room.

Coverage

This is every board that builds this driver. The other chips naming cipher objects either build no cipher module at all — hi3516av100, hi3516cv200 and hi3516cv100 have the objects defined with obj-m commented out — or build a different and much smaller one: hi3520dv200's V2-era hi_cipher is three files and hi3519dv500's V5 security_subsys is two, neither with an RSA bignum or a software crypto fallback in it.

Testing

Built clean for CHIPARCH=gk7205v200 (kernel 4.9.37) and CHIPARCH=hi3516cv500. Not yet run on hardware — the behaviour worth checking on a camera is that SRTP still takes the engine, i.e. /proc/interrupts shows cipher completions tracking packet rate under a WebRTC session, and that majestic --otp-status still passes its known-answer tests for the ladder and the TRNG.

open_cipher.ko shipped at 172,328 bytes installed, and about half of that
was two subsystems with no caller. A kernel module gets no dead-code
elimination -- ld -r takes every object the Kbuild names -- so the vendor
SDK's full crypto stack shipped whole: 39 KB of RSA, being the IFEP
hardware driver plus a software mbedTLS bignum/RSA/ASN.1 fallback compiled
into the kernel, and 14 KB of hash, SHA-1 through SHA-512 with HMAC.

majestic is the only thing that opens /dev/cipher on these boards, and its
include/majestic/hisi/cipher_abi.h is explicit about using neither: the
engine's HMAC-SHA1 reprograms its key per packet and measured 192.4 us
against mbedTLS's 37.4, and its RSA runs once per clip where userspace
mbedTLS already does the job. `nm` agrees -- the two subsystems are reached
through exactly twelve symbols, all of them from kapi_dispatch.c, and
nothing in symc, klad, the TRNG or the OTP path touches them.

So kapi_hash_rsa_absent.c supplies those twelve and the objects come off
the list. The dispatch table is deliberately left whole: crypto_ioctl()
indexes dispatch_func[] by the command's nr, and majestic derives its
command numbers from the same struct sizes the driver does, so a removed
row would land every later command on the wrong handler. The rows and
their handlers stay; HashStart and RsaEncrypt now return
HI_ERR_CIPHER_UNSUPPORTED instead of running code no camera reaches.

Vendor sources are untouched and still in the tree. Restoring either
subsystem is putting its objects back and dropping this file, not a merge.

Measured on gk7205v200, same toolchain and kernel: 172,328 -> 96,188 bytes
installed, -44.2%, and 51,844 -> 28,760 compressed, which is about 23 KB
off an xz squashfs. gk7205v300_lite failed CI today four kilobytes over
its 5120 KB partition.

hi3516cv500 builds this from its own kbuild and keeps the full stack for
now: its tree is the later SDK vintage, the kapi signatures differ, and
the same trim there wants its own file and its own check.
The previous commit did the hi3516ev200 tree, which covers ev200/ev300 and
the Goke parts. hi3516cv500 builds the same 36-object vendor list from its
own kbuild and carried the same weight -- 171,380 bytes installed, with the
same mbedTLS bignum/RSA/ASN.1 implementation compiled into the kernel and
the same SHA-1-through-SHA-512 hash engine that nothing opens.

The boundary was re-checked against this tree's own objects rather than
carried over. It is the same twelve symbols, all from kapi_dispatch.c, and
nothing in symc, klad, the TRNG or the OTP path reaches them -- which is
worth having verified, because these two trees are different SDK vintages
and not interchangeable: cv500's kapi_rsa_* take a cryp_rsa_crypt_data
where ev200's take six arguments, its kapi_hash_finish takes the output
buffer length, and its log macros are lower case. Hence a second copy of
kapi_hash_rsa_absent.c rather than a shared one.

Measured on hi3516cv500, same toolchain and kernel: 171,380 -> 97,208
bytes installed, -43.3%, and 52,468 -> 29,476 compressed. All 17 dispatch
rows intact, no mbedTLS symbols left, every remaining undefined symbol an
ordinary kernel or osal one.

That is every board that builds this driver. The other chips naming cipher
objects either build no cipher module at all -- hi3516av100, hi3516cv200
and hi3516cv100 have the objects defined with obj-m commented out -- or
build a different and much smaller one: hi3520dv200's V2-era hi_cipher is
three files and hi3519dv500's V5 security_subsys is two, neither with an
RSA bignum or a software crypto fallback in it.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Trim unused hash and RSA subsystems from cipher modules

✨ Enhancement ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Excludes unused hash, RSA, fallback, and inactive extension objects from both cipher builds.
• Preserves ioctl numbering with SDK-specific stubs returning unsupported for removed operations.
• Reduces installed module size by roughly 43–44% to restore firmware capacity.
Diagram

graph TD
  B["Kbuild lists"] --> M["Cipher module"] --> D["Ioctl dispatch"]
  U["Majestic client"] --> D
  D --> S["Symc Klad TRNG"] --> H["Hardware engines"]
  D --> A["Hash RSA stubs"] --> E["Unsupported error"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Feature-gate subsystems with Kconfig
  • ➕ Allows hash or RSA to be enabled independently for future consumers
  • ➕ Makes capability selection explicit per board configuration
  • ➖ Requires broader integration across two incompatible vendor SDK vintages
  • ➖ Adds configuration complexity for capabilities unused by every current consumer
  • ➖ Still requires ABI-preserving dispatch behavior when features are disabled

Recommendation: Keep the PR's object-list trimming and SDK-specific stubs for the current fixed board set. It provides the smallest, lowest-risk change, preserves ioctl numbering, leaves vendor sources restorable, and avoids introducing unused configuration machinery; Kconfig feature gates become worthwhile only if a supported board needs hash or RSA.

Files changed (4) +355 / -45

Refactor (2) +305 / -0
kapi_hash_rsa_absent.cStub CV500 hash and RSA entry points +139/-0

Stub CV500 hash and RSA entry points

• Provides the CV500-specific hash and RSA symbols required by initialization and ioctl dispatch. Lifecycle calls succeed so module loading continues, while operational calls return HI_ERR_CIPHER_UNSUPPORTED using CV500 API signatures.

kernel/cipher/hi3516cv500/src/drv/cipher_v1.0/drivers/kapi_hash_rsa_absent.c

kapi_hash_rsa_absent.cStub EV200 hash and RSA entry points +166/-0

Stub EV200 hash and RSA entry points

• Supplies all EV200 hash and RSA entry points referenced by the unchanged dispatch layer. Initialization, deinitialization, and release remain successful, while actual hash and RSA requests report unsupported without shipping their implementations.

kernel/cipher/hi3516ev200/src/drv/cipher_v1.0/drivers/kapi_hash_rsa_absent.c

Other (2) +50 / -45
KbuildTrim EV200 cipher objects and document module-size rationale +34/-24

Trim EV200 cipher objects and document module-size rationale

• Removes unused hash, RSA, mbedTLS fallback, inactive v100, and empty extension objects from EV200-family cipher builds. Adds the EV200-specific absence stub while retaining symmetric crypto, key ladder, OTP, and TRNG support.

kernel/cipher/Kbuild

hi3516cv500.kbuildTrim CV500 cipher objects and select compatibility stubs +16/-21

Trim CV500 cipher objects and select compatibility stubs

• Removes CV500 hash, RSA, bundled mbedTLS, inactive v100, and empty extension objects from open_cipher.ko. Selects the CV500-specific absence stub and documents measured installed and compressed size reductions.

kernel/hi3516cv500.kbuild

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@widgetii

widgetii commented Sep 6, 2026

Copy link
Copy Markdown
Member Author

Validated on hardware, one camera per trimmed tree

hi3516ev300-imx335 (hi3516ev200 tree) and hi3516av300-imx415 (hi3516cv500 tree), both kernel 4.9.37. Baseline built from HEAD~2, trimmed from HEAD, each against its own kernel so vermagic matches the camera:

baseline trimmed loaded (lsmod)
ev200 / hi3516ev300 172,460 96,384 (−44.1%) 149,163 → 84,923
cv500 / hi3516av300 171,380 97,208 (−43.3%) 136,180 → 77,299

majestic --otp-status on the ev300 — identical line for line, before and after

aes-ctr known answer:       PASS
counter carry past 32 bits: PASS
batched encrypt:            PASS
trng:                       64 of 64 words in 191 us
key ladder slot 1/2/3:      blank, hard-key channel PASS
config ioctl:               soft key 2.8 us, hard key 23.6 us
bulk 256 KiB, software key  PASS 11338 us wall, 2466 us cpu (batched, 30720 per ioctl)
bulk 256 KiB, hard key      PASS 11037 us wall, 2167 us cpu (batched, 30720 per ioctl)

Cipher IRQ 37 went 441 → 484 across one run, so the engine did the work rather than a software fallback.

A self-test on both, diffed between the two modules

The av300's majestic is master+96bdd8d — one commit before --otp-status landed — so I wrote a self-test against the ABI in majestic's cipher_abi.h: AES-128-CTR against NIST SP 800-38A F.5.1, a case whose IV sits at the 32-bit carry, the batched two-package call with a different IV per package, eight TRNG words, and a key-ladder wrap plus hard-key channel.

Output byte-identical between baseline and trimmed on both cameras, OVERALL: PASS. It also agrees with majestic's own verdict where both were available.

One incidental confirmation that the ladder round-trips: the wrap of 000102…0f differs per board (b65d3003… vs ffe617e3…, the two blank slots wrapping at different AES widths) but the hard-key ciphertext is the same 0d66790a… on both — because the ladder unwraps back to the clear block, so both channels end up keyed with it.

The hash refusal is the stub, not luck

HashStart (CRYPTO_IOWR(0x06, 24) = 0xC0184D06) returns 0 on the av300 baseline and 0x804D000C (BUSY) on the ev300 baseline — the real engine answering — and 0x804D0013 (HI_ERR_CIPHER_UNSUPPORTED) on both trimmed modules. No oops either way.

One unrelated bug found, and ruled out as a cause

On the av300, S95majestic stop; start leaves the sensor i2c bus wedged — 200+ hibvt-i2c 120b5000.i2c: wait idle timeout, sensor autodetection failed, Cannot start SDK, image.jpg 200 with 0 bytes. It first appeared after a cipher swap, so I ran the control: on a fresh boot with the stock, untouched open_cipher, a plain majestic stop/start reproduces it exactly. Pre-existing, and the cipher driver touches no i2c. A reboot clears it. The ev300 does not show it. Worth its own issue.

Cameras left clean

Modules were insmod'd from /tmp, never written to flash, so a reboot restores stock by itself. Both rebooted and verified afterwards: stock open_cipher back, majestic up, image.jpg 200 with 72,916 and 555,535 bytes, zero i2c errors.

@widgetii
widgetii merged commit 916e767 into main Sep 6, 2026
66 of 70 checks passed
@widgetii
widgetii deleted the cipher-trim-hash-rsa branch September 6, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant