English | 简体中文
An RSA-SM4 digital envelope implemented on a Zynq-7000 FPGA. The project combines an SM4 block cipher, Montgomery-based RSA modular exponentiation, and key encapsulation control in programmable logic. A BRAM/GPIO board interface, Vitis bare-metal application, and PyQt5 serial host complete the end-to-end path from host input to FPGA encryption and decryption.
From cryptographic RTL to Zynq board communication, this project delivers a well-structured and extensible digital envelope implementation for FPGA cryptographic acceleration, hardware-software co-design, and live demonstration.
- Highlights
- How It Works
- System Architecture
- Repository Structure
- RTL Integration
- Board Data Protocol
- Implementation Results
- Requirements
- Quick Start
- Host Application
- License and Acknowledgments
- RSA-SM4 hybrid encryption — SM4 processes 128-bit data blocks while RSA encapsulates the 128-bit SM4 session key.
- All-RTL cryptographic core — RSA modular exponentiation, Montgomery reduction, SM4 key expansion, all 32 rounds, and the control state machines are implemented in Verilog.
- Pipelined SM4 datapath — the fully unrolled 32-round pipeline produces the corresponding
out_syncapproximately 32 clock cycles afterin_sync. - Zynq PS/PL co-design — the PS exchanges data through BRAM and controls PL operations through EMIO GPIO.
- End-to-end demonstration stack — Vitis bare-metal firmware, a Windows serial GUI, a prebuilt host executable, and a generated bitstream are included.
A digital envelope combines the throughput of symmetric encryption with the key distribution capabilities of public-key cryptography.
- The PL generates a 128-bit SM4 session key.
- The session key is split into two 64-bit segments and sent to the RSA modular exponentiation unit.
- Two 65-bit RSA results are concatenated into the 130-bit encrypted key
CMKey. - The SM4 key expansion module generates 32 round keys.
- Plaintext enters the SM4 encryption pipeline in 128-bit blocks.
- The board wrapper writes
CMKeyand the SM4 ciphertext back to BRAM.
- The 130-bit
CMKeyis read from BRAM or supplied through thede_cmkeyinterface. - The two RSA ciphertext segments are processed sequentially to recover the 128-bit SM4 session key.
- The key expansion module supplies the SM4 round keys in reverse order.
- Ciphertext enters the SM4 decryption pipeline in 128-bit blocks and the recovered data is produced.
┌──────────────────────── Windows Host ────────────────────────┐
│ PyQt5 serial GUI: select port, send data, display results │
└───────────────────────────┬───────────────────────────────────┘
│ UART, 115200 baud by default
┌───────────────────────────▼───────────────────────────────────┐
│ Zynq Processing System │
│ Vitis bare-metal app: commands, BRAM access, EMIO GPIO │
└───────────────┬───────────────────────────────┬───────────────┘
│ BRAM │ start/mode/done/reset
┌───────────────▼───────────────────────────────▼───────────────┐
│ Programmable Logic │
│ envelope_top │
│ ├── rdm_gen 128-bit session-key generator │
│ ├── mod_exp RSA modular exponentiation control │
│ │ └── redc_x Montgomery reduction │
│ └── sm4_top_parallel │
│ ├── key_expansion_parallel 32-round key expansion │
│ └── sm4_encdec_parallel 32-round data pipeline │
└───────────────────────────────────────────────────────────────┘
The project provides two top-level integration options:
RSA-SM4/RSA-SM4_top.vis a streaming top level for integrating the cryptographic core into another RTL design. It directly exposes the encrypted session key and 128-bit data interfaces.board_test/source_1/imports/RSA-SM4/evlp_top.vis the Zynq demonstration top level. It adds BRAM transfers, GPIO handshaking, and multi-block control.
.
├── RSA-SM4/ # Standalone cryptographic core
│ ├── RSA-SM4_top.v # Streaming RSA-SM4 top level
│ ├── RSA/ # RSA exponentiation and Montgomery reduction
│ └── SM4/ # Constants, rounds, key expansion, and datapath
├── board_test/source_1/
│ ├── imports/RSA-SM4/ # Board wrapper and RTL source snapshot
│ ├── ip/blk_mem_gen_0/ # 32-bit × 1024-word BRAM configuration
│ └── new/cipher.coe # BRAM initialization example
├── vitis/src/main.c # Zynq PS bare-metal control application
├── Uart-host/ # PyQt5 serial host and Windows executable
├── bit_stream/ # Generated FPGA bitstream
├── Documents/ # Timing, utilization, power, and interface figures
└── LICENSE # GNU AGPL-3.0
RSA-SM4/ contains the standalone core for module-level reuse, while board_test/.../RSA-SM4/ organizes the RTL around the complete Zynq board datapath.
RSA_SM4_top embeds the cryptographic core into a larger RTL design:
| Signal | Direction | Width | Description |
|---|---|---|---|
clk |
Input | 1 | Operating clock |
rst_n |
Input | 1 | Active-low asynchronous reset |
crypt_pre |
Input | 1 | Start pulse, asserted for one clock cycle |
den_sel |
Input | 1 | Mode select: 0 encrypt, 1 decrypt; hold stable during an operation |
crypt_ready |
Output | 1 | RSA processing and SM4 key expansion complete |
de_cmkey |
Input | 130 | RSA-encapsulated session key used for decryption |
en_cmkey |
Output | 130 | RSA-encapsulated session key produced during encryption |
input_stream |
Input | 128 | SM4 input block |
output_stream |
Output | 128 | SM4 output block |
The SM4 submodule uses in_sync and out_sync as data-valid markers, making it easy to connect to an upstream data source, FIFO, or another streaming module.
The default RSA parameters are defined in RSA-SM4/RSA/mod_exp.v:
| Parameter | Default | Description |
|---|---|---|
n |
21536215303153667899 |
65-bit modulus |
e |
17 |
Public exponent |
d |
11401525742632630793 |
Private exponent |
q |
1411149436910194189 |
Montgomery precomputed parameter |
r2 |
15661607970342841481 |
Montgomery precomputed parameter |
To customize the RSA key, configure n, e, d, and the Montgomery precomputed parameters together for the target datapath width.
envelope_top and the Vitis application use the following EMIO signals:
| Function | PL Signal | PS Pin | Active Level |
|---|---|---|---|
| Start | GPIO_in[0] |
54 | High pulse |
| Mode | GPIO_in[1] |
55 | 0 encrypt, 1 decrypt |
| Done | GPIO_out[1] |
56 | Active high |
| Reset | GPIO_in[3] |
57 | A high PS output resets the PL logic |
The Vitis example accesses BRAM through XPAR_BRAM_0_BASEADDR; the original project address shown in the source comments is 0x40000000. Use the xparameters.h generated for the target platform when porting the design.
The board wrapper stores one digital envelope using the following byte layout:
| Byte Offset | Logical Bit Range | Content |
|---|---|---|
0..19 |
0..159 |
160-bit envelope header: two RSA key-cipher segments occupy the first 130 bits and the remaining 30 bits provide alignment |
20.. |
160.. |
SM4 ciphertext, beginning at BRAM address 20 |
The BRAM IP is configured for a 32-bit data width and a depth of 1024 words. The board controller assembles every four words into one 128-bit SM4 block and writes the result with all byte lanes enabled.
The serial protocol uses a simple two-stage text exchange:
Encrypt: host sends "0\n" -> waits about 1 second -> sends plaintext and newline
Decrypt: host sends "1\n" -> waits about 1 second -> sends envelope hex and newline
After an operation completes, the Vitis application returns the result through standard output. Encryption produces a continuous hexadecimal envelope; decryption produces the recovered text.
All user-specified timing constraints are met, with a Setup WNS of 5.839 ns and a Hold WHS of 0.117 ns.
The post-synthesis on-chip power estimate is 0.426 W, consisting of 0.317 W dynamic power and 0.109 W static power, demonstrating an efficient FPGA cryptographic datapath.
RSA_SM4_top uses 8,878 Slice LUTs, 7,106 Slice Registers, and 74 DSPs. On the xc7z020, these figures correspond to approximately 16.7% of the available LUTs, 6.7% of the registers, and 33.6% of the DSPs, leaving ample capacity for additional control and communication logic.
- ALINX Zynq-7000 development platform with an
xc7z020device - AMD/Xilinx Vivado with support for the included XCI
- AMD/Xilinx Vitis for the Zynq bare-metal application
- JTAG programmer and USB-to-UART connection
Use the included bitstream and host application for a quick end-to-end demonstration, or integrate the RTL, BRAM XCI, and Vitis source into a Zynq design with platform-specific PS, BRAM, GPIO, UART, and address-map settings.
- Windows
- USB-to-UART driver
- Prebuilt
Uart-host/main.exe
git clone https://github.com/7urtle/FPGA-RSA-SM4-Digital-Envelope.git
cd FPGA-RSA-SM4-Digital-Envelope- Connect to the
xc7z020device in Vivado Hardware Manager. - Program the PL with
bit_stream/digital_envelope_host_sucess.bitor a locally generated bitstream. - Build and run
vitis/src/main.con the matching Vitis hardware platform. - Connect the board UART and confirm that
Ready:appears in the terminal.
- Run
Uart-host/main.exe. - Select Refresh and choose the board's serial port.
- Connect with the default
115200 / 8-N-1settings. - Enter the data and select Encrypt or Decrypt.
- Read the FPGA result in the receive area.
The GUI provides serial-port discovery, baud-rate/data-bit/stop-bit/parity settings, character encoding, hexadecimal send/receive modes, and dedicated encryption and decryption controls. Its default configuration is:
Baud rate: 115200
Data bits: 8
Stop bits: 1
Parity: None
Encoding: UTF-8
- Encrypt — enter plaintext without spaces and select Encrypt. The receive area displays the hexadecimal envelope containing the encapsulated key and SM4 ciphertext.
- Decrypt — paste the complete hexadecimal envelope and select Decrypt. The receive area displays the recovered plaintext.
- Hexadecimal options — these options control raw byte conversion at the serial layer. Leave them disabled for the standard button workflow so that
0,1, and newline characters are transmitted as text.
This project is released under the GNU Affero General Public License v3.0. Review the complete license before copying, modifying, or distributing the project.
The serial host is based in part on PyQt-multifunctional-uart-helper. Thanks to its original author for the foundation.




