Simulate encrypted SystemVerilog IP with broker-based key management.
IEEE 1735 is the industry standard for distributing encrypted HDL intellectual property. It allows IP vendors to ship encrypted SystemVerilog source files that only authorized EDA tools can decrypt and simulate.
RyuSim supports IEEE 1735 with a security-first architecture: the RSA private key never exists on the user's machine. Instead, session key unwrapping is performed by a Seiraiyu-hosted remote broker over HTTPS, AES decryption happens in memory, generated C++ is written to a memory-backed file descriptor (memfd/tmpfs), and decrypted source is scrubbed from memory immediately after compilation.
This design directly addresses the vulnerabilities documented by Speith et al. (IEEE S&P 2022), where every major EDA vendor's local key storage was broken within hours to weeks.
Simulating encrypted IP requires nothing beyond the .svp file itself. RyuSim handles decryption transparently — no API keys, no extra flags.
Create a Makefile alongside your testbench, just like any other cocotb test:
SIM ?= ryusim
TOPLEVEL_LANG := verilog
VERILOG_SOURCES = $(PWD)/design_protected.svp
TOPLEVEL = module_name
COCOTB_TEST_MODULES = test_module
include $(shell cocotb-config --makefiles)/Makefile.sim
Then run:
make
# Compile — RyuSim detects pragma protect and decrypts transparently
ryusim compile design_protected.svp --top module_name
RyuSim detects pragma protect regions in .svp files, contacts the broker to unwrap the session key, decrypts the source in memory, and proceeds with normal compilation. No additional flags or configuration are needed.
Copy this encrypted design into pipelined_adder.svp to test the full flow.
module pipelined_adder #(
parameter WIDTH = 8
) (
input logic clk,
input logic rst,
input logic [WIDTH-1:0] a,
input logic [WIDTH-1:0] b,
output logic [WIDTH:0] sum,
output logic valid
);
`pragma protect version = 2
`pragma protect encrypt_agent = "ryusim-protect"
`pragma protect key_keyowner = "Seiraiyu"
`pragma protect key_keyname = "RyuSim-RSA-2026-01"
`pragma protect key_method = rsa
`pragma protect key_block
KUNB27Vcm4Xhq16d59nvsQGT9HzJjaRgve+imwNX9AlD2TVngWh3FIVi3XyIjf5DV7OU1vgT09gcxODE7Kf4fBH02x8TULG7rSJGpW9tjmx+6SPceQVVaa4hT7w9bwN2v5BGHPgeTerZuVs6H4g/I/kl6CHkQAJgEIFU51N6ucMqiYbcPhyc15VybXq1iBNI6QKFNw+QtA4EOQMfnww33g8qBO826U3sq1FYQ7vNCsUHgncMyZERKkqYuYUplwYfq50DlzTNnltbhZSM9du2xNOlicmDcKgMvjeyVPbrUrRQ8k5Gl0+9j6cj8MOtL6wMtBTVX0nTt7wb61eTuB2YVFKfODpQXRyu2ZPuNP8lrAPfyzE33ElLsxq6sZh2psweTmhbXMKrkYD3quuaImbR2L3GbWiH/4Mo/t0VLC3kb/zh+gB/K42jJRfA5ljqK9XqI9fyOWIYyBk0iU4UftiObE27sWbWy17Xm8BtqQXqpEdOrhbkZYJS1+dVgi3XHgmlRY18+nykBZWAlfzif85AQnNcRzzDvf3y5bItxUx9EF3iXGsQ1qrAEUN+iRY0dWC7+Hf8gY/qqTSjtzbyDlpS8CweitBSG3Q4IqBrIqyHjmNHRo4p7ehb8WG9pYkEP9zyQCf9/GLXZrZ4BRJ+8iKP9bc74H0C5utAZbg3caNUjnA=
`pragma protect data_method = "aes-256-gcm"
`pragma protect iv = "6cdf01d6adb98217219aa68f"
`pragma protect begin_protected
`pragma protect data_block
iFJYVYfclfGcGOgXdun96UA+IerbCZf0YgSyfGCvPie6shRDy/TtJ7ggMgB60XIiD9Tr0XUMip+D4pVtbhGkq34u3PjKaXX4IuJ7wiyyN8zrBiDLQd65pqYTeiw3ySFRXpjlPIEcY1szNCdwqyKOSGxFwG9KPwFKD8P6wjlK0cQwlYpcE1I/eNarUUd4AO0CoucZsyQLdK6RO6xaRZGNSiCXIdtTniato5B7KHV59RXAW7Rr0EyJLMxIKnd98GpiAXya8HXsREhSQKuX2YlLPb2Pxha+JvWEVZWvOT6kQJJkYA6rbApkNxJOP8QYHSVv+388SKc6lZLsoYbTJtDDndw1OA5cF4wZZzSIS4E4JF4OxKscVsvQKjkS5MIqPBSe0abTLKX8nVTNRmD/DGbt3h4ox/dGYlJ6Ye7SqSxXqq8AE6IU8Df8rBhqPA06mDMA3ErSh7n/75nspnP9pKm/RzyAS0r/aoNM57sgxYFvVgUsTKWfVjZc2iiPyI5sKkICyyazdaKN+9B99SPnh03GhN2Bs9y1hQXBKU8MIcHKd5JRTfOMSC4YTPpg37rhg3/4YDCWvwb7ohRmulTWROB/hHA691thNoQHtqgNlQaPc9qaUAlqZl803OPRCsGD2pZ2KgFEX7ScYDqd3qXGheRROyDfVmaROpea5kjJunGqLKNhmDpsh0nrHOvZD0tnjQt6ozct1B3zrioqR3AkrkPKtp53sGIpBZVEOR0/H8nWfo9jT2WTH2RXCXrpFQ2dmv4RdcI7AgxbrJMTT775g2UqP+18WUMNPtmTI36yXBJ5IWwCh0RlXo7c8zIFlI3xyiNVaKnAXjnY5FkQv03XbtYStn/JRej3W20xBSh5pZhNOzD5Q+YI0Qmia6lsqUavriu+FR+jogCc/9KutYDJloMGF1/H1K/SKg8S
`pragma protect auth_tag = "bf5f6dc6a5edcd53cd5273d581df6ebc"
`pragma protect end_protected
endmodule
Create test_pipelined_adder.py:
import cocotb
from cocotb.triggers import RisingEdge, Timer
from cocotb.clock import Clock
@cocotb.test()
async def test_pipelined_add(dut):
"""Verify the encrypted pipelined adder compiles and simulates."""
cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
# Reset
dut.rst.value = 1
dut.a.value = 0
dut.b.value = 0
for _ in range(3):
await RisingEdge(dut.clk)
dut.rst.value = 0
# Apply inputs and wait for pipeline to produce results
dut.a.value = 10
dut.b.value = 20
for _ in range(5):
await RisingEdge(dut.clk)
await Timer(1, unit="ns")
assert dut.valid.value == 1, "valid should be asserted after pipeline fill"
result = int(dut.sum.value)
assert result == 30, f"Expected 10+20=30, got {result}"
dut._log.info(f"Encrypted IP works! sum={result}")
Create a Makefile:
SIM ?= ryusim
TOPLEVEL_LANG := verilog
VERILOG_SOURCES = $(PWD)/pipelined_adder.svp
TOPLEVEL = pipelined_adder
COCOTB_TEST_MODULES = test_pipelined_adder
include $(shell cocotb-config --makefiles)/Makefile.sim
Run make and the encrypted design compiles and simulates just like plaintext.
RyuSim's IEEE 1735 pipeline operates in six stages:
pragma protect begin_protected / end_protected regions. It extracts the RSA-wrapped session key (key_block), the AES-encrypted payload (data_block), and metadata such as key_keyowner, key_keyname, and data_method.POST https://broker.seiraiyu.com/api/v1/unwrap) over HTTPS with TLS certificate verification. The broker performs RSA private-key decryption server-side and returns the AES session key. The RSA private key never leaves the broker.data_block locally. For AES-256-GCM (the recommended method), the GCM authentication tag is verified to detect any tampering.pragma protect region in the file buffer. The reassembled source is fed to Slang for parsing.explicit_bzero. The plaintext SystemVerilog no longer exists in process memory.is_protected in the IR. Their generated C++ is written to a memory-backed file descriptor (memfd_create on Linux, or /dev/shm/ as fallback), compiled to an object file, and the C++ source is deleted immediately. Only the compiled .o is retained for linking.The ryusim-protect command-line tool encrypts SystemVerilog files for distribution using the IEEE 1735 standard.
Use the standard `pragma protect begin / `pragma protect end directives (IEEE 1800-2023 §34.5) to define exactly what gets encrypted. Everything outside these markers stays plaintext — including the module declaration, ports, parameters, typedefs, and defines — so consumers can see the interface and override parameters without access to the source.
module my_ip #(
parameter int WIDTH = 8
) (
input logic clk,
input logic [WIDTH-1:0] data_in,
output logic [WIDTH-1:0] data_out
);
// Typedefs and constants visible to consumers
typedef enum logic [1:0] { IDLE, RUN, DONE } state_t;
`pragma protect begin
// Implementation — encrypted in the .svp output
state_t state;
always_ff @(posedge clk) begin
// ...
end
`pragma protect end
endmodule
ryusim-protect encrypt \
--input my_ip.sv \
--output my_ip.svp \
--recipient ryusim \
--data-method aes-256-gcm
The --recipient flag specifies which tools can decrypt the file. Use ryusim for RyuSim. You can specify multiple recipients to create a single .svp file that works with multiple tools:
ryusim-protect encrypt \
--input my_ip.sv \
--output my_ip.svp \
--recipient ryusim \
--recipient vivado \
--recipient questa \
--data-method aes-256-gcm
Each recipient gets its own key_block in the pragma protect envelope, containing the AES session key wrapped with that tool's RSA public key. The data_block (AES-encrypted source) is shared across all recipients.
ryusim-protect validate my_ip.svp
Checks the structural integrity of the pragma protect envelope without decrypting. Reports whether the file has valid syntax, lists the key blocks present, and verifies base64 encoding.
ryusim-protect info my_ip.svp
Displays the envelope version, data method, number of key blocks, and the key_keyowner/key_keyname for each block.
ryusim-protect list-recipients
Shows the built-in recipient registry with known identifiers for major EDA tools:
| Recipient ID | key_keyowner | key_keyname |
|---|---|---|
ryusim |
Seiraiyu | RyuSim-RSA-2026-01 |
vivado |
Xilinx | xilinxt_2019_11 |
questa |
Mentor Graphics Corporation | MGC-VERIF-SIM-RSA-2 |
quartus |
Altera Corporation | Altera-Quartus-RSA-1 |
vcs |
Synopsys | SNPS-VCS-RSA-2 |
xcelium |
Cadence Design Systems. | CDS-RSA-KEY-VER-1 |
The tool ships with RyuSim's public key built in. Other vendor public keys must be provided by the user (typically obtained from the vendor's tool installation directory). The registry provides only the correct identifier strings.
When encrypting for multiple recipients, each gets an independent RSA-wrapped copy of the same AES session key. The encrypted payload is identical for all recipients. This means:
.svp file works across multiple toolskey_block per recipient)ryusim-protect encryptNo local private key. The RSA private key used to unwrap session keys exists only on the Seiraiyu broker server. It is never downloaded, embedded in binaries, or cached on the user's machine. This directly addresses the root cause identified by Speith et al. (IEEE S&P 2022): every vendor that stored keys locally had them extracted.
Broker-based RSA unwrap. Session key unwrapping is a server-side operation. The client sends the RSA-encrypted session key to the broker; the broker performs the RSA decryption and returns the AES session key over TLS. No user-facing credentials are required — the broker validates requests and applies rate limiting to prevent abuse.
GCM integrity verification. AES-256-GCM provides authenticated encryption. If any byte of the encrypted payload or authentication tag is modified, decryption fails with an integrity error. This prevents the padding-oracle attacks that were demonstrated against CBC-based IEEE 1735 implementations.
Memory-backed code generation. Generated C++ for protected modules is written to memfd_create file descriptors (or /dev/shm/ as fallback) rather than the normal obj_dir/ on disk. The C++ source is deleted immediately after compilation to .o. Only the compiled object file is retained.
Explicit memory scrub. After Slang parses the decrypted source into its AST, the decrypted buffer is zeroed using explicit_bzero (which the compiler cannot optimize away). This minimizes the window during which plaintext source exists in process memory.
| Threat | Protection |
|---|---|
Casual viewing (opening .svp in an editor) |
AES encryption renders content unreadable |
| Private key extraction from simulator binary | Key never exists on user's machine |
| Padding-oracle / data modification attacks | AES-256-GCM authenticated encryption rejects tampered data |
| Generated C++ source readable on disk | memfd/tmpfs output with immediate deletion |
| Decrypted source lingering in memory | explicit_bzero after Slang parse |
| Unauthorized use of broker | Rate limiting and request validation |
| Key compromise blast radius | Per-version keys with rotation support |
.o and linked .so files contain machine code that encodes the design logic, similar to any compiled binaryCause: The broker refused the unwrap request. This is uncommon and typically indicates a server-side issue.
Fix:
curl -s https://broker.seiraiyu.com/healthCause: The broker is unreachable. This can be a network outage, DNS failure, firewall blocking HTTPS (port 443), or a proxy configuration issue.
Fix:
curl -s https://broker.seiraiyu.com/healthecho $RYUSIM_BROKER_URLCause: The encrypted file does not contain a key block for RyuSim. It was encrypted for other tools but not for RyuSim.
Fix:
--recipient ryusimryusim-protect info <file> to see which recipients are includedryusim-protect encrypt --input source.sv --output source.svp --recipient ryusim
Cause: The AES-256-GCM authentication tag does not match. The encrypted file has been modified since encryption — either corrupted during transfer or intentionally tampered with.
Fix:
.svp file from the IP vendorCause: The encrypted file uses an encryption algorithm that RyuSim does not support.
Fix:
aes-256-gcm (recommended)ryusim-protect info <file> to see the current data_methodCause: The TLS certificate presented by the broker could not be verified. This may indicate a man-in-the-middle attack, an expired certificate, or a missing CA bundle.
Fix:
Cause: The pragma protect structure in the file has syntax errors.
Fix:
ryusim-protect validate <file> to identify the specific structural issueIP vendors who want their encrypted SystemVerilog to work with RyuSim need to include a key block for key_keyowner="Seiraiyu", key_keyname="RyuSim-RSA-2026-01" in their pragma protect envelope.
To have your tool's public key included in the ryusim-protect built-in recipient registry:
key_keyowner and key_keyname strings your tool expects, and your RSA public key in PEM format.ryusim-protect list-recipients registry in the next release.RyuSim's public key and metadata are distributed in the ieee1735/ directory of the RyuSim installation:
ieee1735/Seiraiyu_RyuSim-RSA-2026-01_public.pem — RSA-2048 public key in PEM formatieee1735/ryusim_ieee1735.json — Machine-readable metadata:{
"tool": "RyuSim",
"vendor": "Seiraiyu",
"key_owner": "Seiraiyu",
"key_name": "RyuSim-RSA-2026-01",
"key_method": "rsa",
"key_size": 2048,
"supported_data_methods": ["aes-256-gcm"],
"supported_languages": ["verilog", "systemverilog"],
"public_key_file": "Seiraiyu_RyuSim-RSA-2026-01_public.pem",
"broker_url": "https://broker.seiraiyu.com/api/v1/unwrap"
}
Use aes-256-gcm as the data_method when encrypting for RyuSim.