gmsp_test.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2. // File name : gmsp_test.c
  3. // Description : Linux user-space SPI test tool for GMSP protocol.
  4. // Sends command frames to the CUni360S-Z chip via spidev,
  5. // receives response frames, and verifies CRC + status.
  6. //
  7. // Usage:
  8. // ./gmsp_test --spi /dev/spidev0.0 --cmd 0xF0
  9. // ./gmsp_test --spi /dev/spidev0.0 --cmd 0x01 --data "deadbeef"
  10. // ./gmsp_test --spi /dev/spidev0.0 --speed 5000000 --test-all
  11. //
  12. // Build:
  13. // gcc -std=c99 -o gmsp_test tools/gmsp_test.c
  14. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <stdint.h>
  19. #include <fcntl.h>
  20. #include <unistd.h>
  21. #include <sys/ioctl.h>
  22. #include <linux/spi/spidev.h>
  23. #include <getopt.h>
  24. #include <errno.h>
  25. /* ======================================================================== */
  26. /* GMSP Protocol Constants (from gmsp_frame.h) */
  27. /* ======================================================================== */
  28. #define GMSP_SYNC_HI 0xAA
  29. #define GMSP_SYNC_LO 0x55
  30. #define GMSP_MAX_PAYLOAD 240
  31. #define GMSP_FRAME_OVERHEAD 7 /* SYNC(2)+LEN(2)+CMD/STATUS(1)+CRC(2) */
  32. /* ---- Command codes ---------------------------------------------------- */
  33. #define CMD_SM2_KEYGEN 0x01
  34. #define CMD_SM2_SIGN 0x02
  35. #define CMD_SM2_VERIFY 0x03
  36. #define CMD_SM2_ENCRYPT 0x04
  37. #define CMD_SM2_DECRYPT 0x05
  38. #define CMD_SM2_KEYEX 0x06
  39. #define CMD_SM3_HASH 0x10
  40. #define CMD_SM4_ECB_ENC 0x20
  41. #define CMD_SM4_ECB_DEC 0x21
  42. #define CMD_SM4_CBC_ENC 0x22
  43. #define CMD_SM4_CBC_DEC 0x23
  44. #define CMD_SM4_STREAM_INIT 0x24
  45. #define CMD_SM4_STREAM_UPD 0x25
  46. #define CMD_SM4_STREAM_FIN 0x26
  47. #define CMD_EXPORT_PUBKEY 0x30
  48. #define CMD_GEN_CERT 0x31
  49. #define CMD_GET_INFO 0xF0
  50. /* ---- Status codes ----------------------------------------------------- */
  51. #define STAT_SUCCESS 0x00
  52. #define STAT_BAD_CRC 0x01
  53. #define STAT_BAD_LEN 0x02
  54. #define STAT_BAD_PARAM 0x03
  55. #define STAT_ERR_KEY 0x04
  56. #define STAT_ERR_CRYPTO 0x05
  57. #define STAT_ERR_KEY_NOT_INIT 0x06
  58. #define STAT_ERR_SESSION 0x07
  59. #define STAT_UNKNOWN_CMD 0xFF
  60. /* ---- SPI defaults ----------------------------------------------------- */
  61. #define DEFAULT_SPI_DEVICE "/dev/spidev0.0"
  62. #define DEFAULT_SPI_SPEED 1000000 /* 1 MHz */
  63. #define DEFAULT_SPI_MODE SPI_MODE_0 /* CPOL=0, CPHA=0 */
  64. #define DEFAULT_SPI_BITS 8
  65. #define CS_DELAY_US 10 /* 10 us between frames */
  66. #define RESPONSE_BUF_SIZE (GMSP_FRAME_OVERHEAD + GMSP_MAX_PAYLOAD + 16)
  67. /* ======================================================================== */
  68. /* CRC16-CCITT Lookup Table (from gmsp_crc.c) */
  69. /* Poly 0x1021, init 0xFFFF, non-reflected */
  70. /* ======================================================================== */
  71. static const uint16_t crc16_ccitt_table[256] = {
  72. 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
  73. 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
  74. 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
  75. 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
  76. 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
  77. 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
  78. 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
  79. 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
  80. 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
  81. 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
  82. 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
  83. 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
  84. 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
  85. 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
  86. 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
  87. 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
  88. 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
  89. 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
  90. 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
  91. 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
  92. 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
  93. 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
  94. 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
  95. 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
  96. 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
  97. 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
  98. 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
  99. 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
  100. 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
  101. 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
  102. 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
  103. 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
  104. };
  105. /* ======================================================================== */
  106. /* CRC16-CCITT implementation (from gmsp_crc.c, adapted for Linux stdint) */
  107. /* ======================================================================== */
  108. static uint16_t gmsp_crc16_calc(const uint8_t *data, uint16_t len)
  109. {
  110. uint16_t crc = 0xFFFF; /* GMSP_CRC16_INIT */
  111. uint16_t i;
  112. uint8_t idx;
  113. for (i = 0; i < len; i++) {
  114. idx = (uint8_t)((crc >> 8) ^ data[i]);
  115. crc = (uint16_t)((crc << 8) ^ crc16_ccitt_table[idx]);
  116. }
  117. return crc;
  118. }
  119. /* ======================================================================== */
  120. /* Frame construction */
  121. /* ======================================================================== */
  122. /**
  123. * build_cmd_frame - Build a GMSP command frame.
  124. *
  125. * Layout: SYNC(0xAA,0x55) + LEN(2B big-endian) + CMD(1B) + PAYLOAD(N) + CRC16(2B)
  126. * CRC covers LEN_HI..PAYLOAD (everything between SYNC and CRC fields).
  127. *
  128. * @cmd: Command code (CMD_*)
  129. * @payload: Payload data (may be NULL if payload_len==0)
  130. * @payload_len: Number of payload bytes
  131. * @out_buf: Output buffer (must be >= payload_len + 7)
  132. *
  133. * Returns: total frame length written to out_buf
  134. */
  135. static uint16_t build_cmd_frame(uint8_t cmd, const uint8_t *payload,
  136. uint16_t payload_len, uint8_t *out_buf)
  137. {
  138. uint16_t crc;
  139. uint16_t offset = 0;
  140. uint16_t j;
  141. /* --- SYNC bytes --- */
  142. out_buf[offset++] = GMSP_SYNC_HI; /* 0xAA */
  143. out_buf[offset++] = GMSP_SYNC_LO; /* 0x55 */
  144. /* --- LEN field (big-endian) = payload length --- */
  145. out_buf[offset++] = (uint8_t)(payload_len >> 8);
  146. out_buf[offset++] = (uint8_t)(payload_len & 0xFF);
  147. /* --- CMD byte --- */
  148. out_buf[offset++] = cmd;
  149. /* --- PAYLOAD data --- */
  150. if (payload != NULL && payload_len > 0) {
  151. for (j = 0; j < payload_len; j++) {
  152. out_buf[offset++] = payload[j];
  153. }
  154. }
  155. /* --- CRC16 (big-endian) over LEN+CMD+PAYLOAD --- */
  156. /* CRC data starts at offset 2 (skip SYNC), length = offset - 2 */
  157. crc = gmsp_crc16_calc(&out_buf[2], (uint16_t)(offset - 2));
  158. out_buf[offset++] = (uint8_t)(crc >> 8);
  159. out_buf[offset++] = (uint8_t)(crc & 0xFF);
  160. return offset; /* = payload_len + 7 */
  161. }
  162. /* ======================================================================== */
  163. /* Response parsing */
  164. /* ======================================================================== */
  165. /**
  166. * parse_response - Parse a GMSP response frame from a receive buffer.
  167. *
  168. * Response layout: SYNC(0xAA,0x55) + LEN(2B) + STATUS(1B) + DATA(N) + CRC16(2B)
  169. * CRC covers LEN_HI..DATA (everything between SYNC and CRC).
  170. *
  171. * @rx_buf: Received data buffer
  172. * @rx_len: Number of bytes received
  173. * @out_status: Output status byte
  174. * @out_data: Output pointer to response data (points into rx_buf)
  175. * @out_dlen: Output length of response data
  176. *
  177. * Returns: 0 on success, negative on error
  178. */
  179. static int parse_response(const uint8_t *rx_buf, uint16_t rx_len,
  180. uint8_t *out_status,
  181. const uint8_t **out_data, uint16_t *out_dlen)
  182. {
  183. uint16_t i;
  184. uint8_t byte;
  185. uint16_t state = 0; /* 0=IDLE, 1=GOT_AA, 2=GOT_55, 3=GOT_LEN_HI,
  186. 4=GOT_LEN_LO, 5..=PAYLOAD, then CRC */
  187. uint16_t resp_len = 0;
  188. uint16_t payload_cnt = 0;
  189. uint16_t crc_recv = 0;
  190. uint16_t crc_start = 0; /* Index of LEN_HI byte in rx_buf */
  191. uint16_t payload_start = 0;
  192. uint8_t status = 0xFF;
  193. for (i = 0; i < rx_len; i++) {
  194. byte = rx_buf[i];
  195. switch (state) {
  196. case 0: /* IDLE - looking for 0xAA */
  197. if (byte == GMSP_SYNC_HI) {
  198. state = 1;
  199. }
  200. break;
  201. case 1: /* GOT_AA - looking for 0x55 */
  202. if (byte == GMSP_SYNC_LO) {
  203. state = 2;
  204. } else if (byte == GMSP_SYNC_HI) {
  205. /* Stay in state 1 */
  206. } else {
  207. state = 0;
  208. }
  209. break;
  210. case 2: /* GOT_55 - this byte is LEN MSB */
  211. resp_len = (uint16_t)byte << 8;
  212. crc_start = i; /* LEN_HI byte position for CRC calculation */
  213. state = 3;
  214. break;
  215. case 3: /* GOT_LEN_HI - this byte is LEN LSB */
  216. resp_len |= (uint16_t)byte;
  217. if (resp_len > GMSP_MAX_PAYLOAD) {
  218. fprintf(stderr, "Response LEN %u exceeds max %u\n",
  219. resp_len, GMSP_MAX_PAYLOAD);
  220. state = 0;
  221. return -1;
  222. }
  223. state = 4;
  224. break;
  225. case 4: /* GOT_LEN_LO - this byte is STATUS */
  226. status = byte;
  227. if (resp_len > 0) {
  228. state = 5; /* Expect payload data */
  229. } else {
  230. state = 6; /* No payload, expect CRC */
  231. }
  232. break;
  233. default: /* 5+ : collecting payload, then CRC */
  234. if (state == 5) {
  235. /* First payload byte */
  236. payload_start = i;
  237. payload_cnt = 1;
  238. if (payload_cnt >= resp_len) {
  239. state = 6; /* All payload received, expect CRC */
  240. } else {
  241. state = 7; /* More payload bytes */
  242. }
  243. } else if (state == 7) {
  244. /* Continuing payload */
  245. payload_cnt++;
  246. if (payload_cnt >= resp_len) {
  247. state = 6; /* All payload received */
  248. }
  249. } else if (state == 6) {
  250. /* CRC MSB */
  251. crc_recv = (uint16_t)byte << 8;
  252. state = 8;
  253. } else if (state == 8) {
  254. /* CRC LSB - validate */
  255. crc_recv |= (uint16_t)byte;
  256. uint16_t crc_data_len = (uint16_t)(i - crc_start - 1);
  257. uint16_t crc_calc = gmsp_crc16_calc(&rx_buf[crc_start], crc_data_len);
  258. if (crc_calc != crc_recv) {
  259. fprintf(stderr, "CRC mismatch: calc=0x%04X recv=0x%04X\n",
  260. crc_calc, crc_recv);
  261. return -2;
  262. }
  263. *out_status = status;
  264. *out_data = (resp_len > 0) ? &rx_buf[payload_start] : NULL;
  265. *out_dlen = resp_len;
  266. return 0;
  267. }
  268. break;
  269. }
  270. }
  271. fprintf(stderr, "Incomplete response frame (parsed %u/%u bytes)\n",
  272. i, rx_len);
  273. return -3;
  274. }
  275. /* ======================================================================== */
  276. /* Helper utilities */
  277. /* ======================================================================== */
  278. /** Print a hex dump of data to stdout */
  279. static void hex_dump(const char *label, const uint8_t *data, uint16_t len)
  280. {
  281. uint16_t i;
  282. printf("%s [%u bytes]: ", label, len);
  283. for (i = 0; i < len; i++) {
  284. printf("%02X ", data[i]);
  285. }
  286. printf("\n");
  287. }
  288. /** Convert a hex string like "deadbeef" to binary, returns byte count */
  289. static int hex_to_bytes(const char *hex_str, uint8_t *out_buf, uint16_t buf_size)
  290. {
  291. uint16_t i;
  292. uint16_t slen = (uint16_t)strlen(hex_str);
  293. unsigned int val;
  294. if (slen == 0) return 0;
  295. /* If odd length or non-hex chars, treat as ASCII */
  296. if (slen % 2 != 0) goto ascii;
  297. for (i = 0; i < slen; i++) {
  298. if (!isxdigit((unsigned char)hex_str[i])) goto ascii;
  299. }
  300. /* Parse as hex pairs */
  301. if (slen / 2 > buf_size) {
  302. fprintf(stderr, "Hex data too long (%u > %u)\n", slen / 2, buf_size);
  303. return -1;
  304. }
  305. for (i = 0; i < slen / 2; i++) {
  306. sscanf(&hex_str[i * 2], "%2x", &val);
  307. out_buf[i] = (uint8_t)val;
  308. }
  309. return slen / 2;
  310. ascii:
  311. /* Treat as ASCII string */
  312. if (slen > buf_size) {
  313. fprintf(stderr, "ASCII data too long (%u > %u)\n", slen, buf_size);
  314. return -1;
  315. }
  316. memcpy(out_buf, hex_str, slen);
  317. return slen;
  318. }
  319. /** Get human-readable name for a command code */
  320. static const char *cmd_name(uint8_t cmd)
  321. {
  322. switch (cmd) {
  323. case CMD_SM2_KEYGEN: return "SM2_KEYGEN";
  324. case CMD_SM2_SIGN: return "SM2_SIGN";
  325. case CMD_SM2_VERIFY: return "SM2_VERIFY";
  326. case CMD_SM2_ENCRYPT: return "SM2_ENCRYPT";
  327. case CMD_SM2_DECRYPT: return "SM2_DECRYPT";
  328. case CMD_SM2_KEYEX: return "SM2_KEYEX";
  329. case CMD_SM3_HASH: return "SM3_HASH";
  330. case CMD_SM4_ECB_ENC: return "SM4_ECB_ENC";
  331. case CMD_SM4_ECB_DEC: return "SM4_ECB_DEC";
  332. case CMD_SM4_CBC_ENC: return "SM4_CBC_ENC";
  333. case CMD_SM4_CBC_DEC: return "SM4_CBC_DEC";
  334. case CMD_SM4_STREAM_INIT: return "SM4_STREAM_INIT";
  335. case CMD_SM4_STREAM_UPD: return "SM4_STREAM_UPD";
  336. case CMD_SM4_STREAM_FIN: return "SM4_STREAM_FIN";
  337. case CMD_EXPORT_PUBKEY: return "EXPORT_PUBKEY";
  338. case CMD_GEN_CERT: return "GEN_CERT";
  339. case CMD_GET_INFO: return "GET_INFO";
  340. default: return "UNKNOWN";
  341. }
  342. }
  343. /** Get human-readable name for a status code */
  344. static const char *status_name(uint8_t status)
  345. {
  346. switch (status) {
  347. case STAT_SUCCESS: return "SUCCESS";
  348. case STAT_BAD_CRC: return "BAD_CRC";
  349. case STAT_BAD_LEN: return "BAD_LEN";
  350. case STAT_BAD_PARAM: return "BAD_PARAM";
  351. case STAT_ERR_KEY: return "ERR_KEY";
  352. case STAT_ERR_CRYPTO: return "ERR_CRYPTO";
  353. case STAT_ERR_KEY_NOT_INIT: return "ERR_KEY_NOT_INIT";
  354. case STAT_ERR_SESSION: return "ERR_SESSION";
  355. case STAT_UNKNOWN_CMD: return "UNKNOWN_CMD";
  356. default: return "UNKNOWN";
  357. }
  358. }
  359. /* ======================================================================== */
  360. /* SPI interface */
  361. /* ======================================================================== */
  362. static int spi_fd = -1;
  363. /** Open and configure the SPI device */
  364. static int spi_open(const char *device, uint32_t speed)
  365. {
  366. uint8_t mode = DEFAULT_SPI_MODE; /* SPI_MODE_0: CPOL=0, CPHA=0 */
  367. uint8_t bits = DEFAULT_SPI_BITS;
  368. spi_fd = open(device, O_RDWR);
  369. if (spi_fd < 0) {
  370. fprintf(stderr, "Failed to open SPI device %s: %s\n",
  371. device, strerror(errno));
  372. return -1;
  373. }
  374. if (ioctl(spi_fd, SPI_IOC_WR_MODE, &mode) < 0) {
  375. fprintf(stderr, "Failed to set SPI mode: %s\n", strerror(errno));
  376. close(spi_fd);
  377. spi_fd = -1;
  378. return -1;
  379. }
  380. if (ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {
  381. fprintf(stderr, "Failed to set SPI bits per word: %s\n", strerror(errno));
  382. close(spi_fd);
  383. spi_fd = -1;
  384. return -1;
  385. }
  386. if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
  387. fprintf(stderr, "Failed to set SPI speed %u Hz: %s\n",
  388. speed, strerror(errno));
  389. close(spi_fd);
  390. spi_fd = -1;
  391. return -1;
  392. }
  393. /* Read back and verify settings */
  394. uint8_t rd_mode = 0;
  395. uint8_t rd_bits = 0;
  396. uint32_t rd_speed = 0;
  397. ioctl(spi_fd, SPI_IOC_RD_MODE, &rd_mode);
  398. ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &rd_bits);
  399. ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &rd_speed);
  400. printf("SPI device : %s\n", device);
  401. printf(" mode : %u (CPOL=%u CPHA=%u)\n",
  402. rd_mode, rd_mode & 2 ? 1 : 0, rd_mode & 1 ? 1 : 0);
  403. printf(" bits/word : %u\n", rd_bits);
  404. printf(" max speed : %u Hz (%.1f MHz)\n",
  405. rd_speed, rd_speed / 1000000.0);
  406. return 0;
  407. }
  408. /** Close the SPI device */
  409. static void spi_close(void)
  410. {
  411. if (spi_fd >= 0) {
  412. close(spi_fd);
  413. spi_fd = -1;
  414. }
  415. }
  416. /**
  417. * spi_transfer - Perform a full-duplex SPI transfer.
  418. *
  419. * @tx_buf: Transmit buffer
  420. * @rx_buf: Receive buffer
  421. * @len: Total transfer length in bytes
  422. *
  423. * Returns: 0 on success, negative on error
  424. */
  425. static int spi_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
  426. {
  427. struct spi_ioc_transfer tr;
  428. memset(&tr, 0, sizeof(tr));
  429. tr.tx_buf = (unsigned long)tx_buf;
  430. tr.rx_buf = (unsigned long)rx_buf;
  431. tr.len = len;
  432. tr.cs_change = 0;
  433. if (ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr) < 0) {
  434. fprintf(stderr, "SPI transfer failed: %s\n", strerror(errno));
  435. return -1;
  436. }
  437. return 0;
  438. }
  439. /* ======================================================================== */
  440. /* Command execution */
  441. /* ======================================================================== */
  442. /**
  443. * send_command - Send a GMSP command frame and receive a response.
  444. *
  445. * @cmd: Command code
  446. * @payload: Payload data (may be NULL)
  447. * @payload_len: Payload length
  448. * @rsp_status: Output: received status byte
  449. * @rsp_data: Output: pointer to response data in rx buffer
  450. * @rsp_dlen: Output: response data length
  451. *
  452. * Returns: 0 on success, negative on error
  453. */
  454. static int send_command(uint8_t cmd, const uint8_t *payload, uint16_t payload_len,
  455. uint8_t *rsp_status, const uint8_t **rsp_data, uint16_t *rsp_dlen)
  456. {
  457. uint8_t tx_buf[GMSP_FRAME_OVERHEAD + GMSP_MAX_PAYLOAD];
  458. uint8_t rx_buf[RESPONSE_BUF_SIZE];
  459. uint16_t tx_len;
  460. uint16_t transfer_len;
  461. int ret;
  462. /* Build command frame */
  463. tx_len = build_cmd_frame(cmd, payload, payload_len, tx_buf);
  464. printf(">>> TX: %s (0x%02X), payload %u bytes, frame %u bytes\n",
  465. cmd_name(cmd), cmd, payload_len, tx_len);
  466. hex_dump(" TX frame", tx_buf, tx_len);
  467. /*
  468. * For SPI full-duplex: we need to clock out enough bytes to receive
  469. * the response. A response frame is at most GMSP_MAX_PAYLOAD + 7 bytes.
  470. * We extend the transfer to ensure we clock in the full response.
  471. */
  472. transfer_len = tx_len;
  473. if (transfer_len < RESPONSE_BUF_SIZE) {
  474. transfer_len = RESPONSE_BUF_SIZE;
  475. }
  476. /* Zero the TX beyond the command frame (slave will ignore) */
  477. if (transfer_len > tx_len) {
  478. memset(&tx_buf[tx_len], 0x00, transfer_len - tx_len);
  479. }
  480. /* Perform SPI transfer */
  481. ret = spi_transfer(tx_buf, rx_buf, transfer_len);
  482. if (ret < 0) {
  483. return ret;
  484. }
  485. /* Skip past our TX frame in the receive buffer — the response follows */
  486. hex_dump(" RX raw", rx_buf, transfer_len > 64 ? 64 : transfer_len);
  487. /* Parse response starting after TX frame bytes */
  488. ret = parse_response(&rx_buf[tx_len], (uint16_t)(transfer_len - tx_len),
  489. rsp_status, rsp_data, rsp_dlen);
  490. if (ret < 0) {
  491. /* Try parsing from beginning of rx_buf in case echo is minimal */
  492. ret = parse_response(rx_buf, transfer_len,
  493. rsp_status, rsp_data, rsp_dlen);
  494. if (ret < 0) {
  495. fprintf(stderr, "Failed to parse response\n");
  496. return ret;
  497. }
  498. }
  499. printf("<<< RX: status=%s (0x%02X), data %u bytes\n",
  500. status_name(*rsp_status), *rsp_status, *rsp_dlen);
  501. if (*rsp_data != NULL && *rsp_dlen > 0) {
  502. hex_dump(" RX data", *rsp_data, *rsp_dlen);
  503. }
  504. /* CS interval between frames */
  505. usleep(CS_DELAY_US);
  506. return 0;
  507. }
  508. /* ======================================================================== */
  509. /* Test-all mode: iterate through every command code */
  510. /* ======================================================================== */
  511. static const uint8_t all_cmd_codes[] = {
  512. CMD_SM2_KEYGEN, /* 0x01 */
  513. CMD_SM2_SIGN, /* 0x02 */
  514. CMD_SM2_VERIFY, /* 0x03 */
  515. CMD_SM2_ENCRYPT, /* 0x04 */
  516. CMD_SM2_DECRYPT, /* 0x05 */
  517. CMD_SM2_KEYEX, /* 0x06 */
  518. CMD_SM3_HASH, /* 0x10 */
  519. CMD_SM4_ECB_ENC, /* 0x20 */
  520. CMD_SM4_ECB_DEC, /* 0x21 */
  521. CMD_SM4_CBC_ENC, /* 0x22 */
  522. CMD_SM4_CBC_DEC, /* 0x23 */
  523. CMD_SM4_STREAM_INIT, /* 0x24 */
  524. CMD_SM4_STREAM_UPD, /* 0x25 */
  525. CMD_SM4_STREAM_FIN, /* 0x26 */
  526. CMD_EXPORT_PUBKEY, /* 0x30 */
  527. CMD_GEN_CERT, /* 0x31 */
  528. CMD_GET_INFO /* 0xF0 */
  529. };
  530. #define NUM_CMD_CODES (sizeof(all_cmd_codes) / sizeof(all_cmd_codes[0]))
  531. static int test_all_commands(void)
  532. {
  533. uint16_t i;
  534. int pass_count = 0;
  535. int fail_count = 0;
  536. uint8_t status;
  537. const uint8_t *rsp_data;
  538. uint16_t rsp_dlen;
  539. int ret;
  540. printf("\n========================================\n");
  541. printf(" GMSP Test-All: %zu commands\n", NUM_CMD_CODES);
  542. printf("========================================\n\n");
  543. for (i = 0; i < NUM_CMD_CODES; i++) {
  544. uint8_t cmd = all_cmd_codes[i];
  545. printf("[Test %2u/%2zu] CMD=0x%02X (%s):\n",
  546. i + 1, NUM_CMD_CODES, cmd, cmd_name(cmd));
  547. /* Send command with no payload — most commands will return
  548. * STAT_BAD_PARAM or STAT_ERR_KEY_NOT_INIT for missing params,
  549. * but that still confirms the command is recognized.
  550. * CMD_GET_INFO (0xF0) should return STAT_SUCCESS with device info. */
  551. ret = send_command(cmd, NULL, 0, &status, &rsp_data, &rsp_dlen);
  552. if (ret < 0) {
  553. printf(" [FAIL] SPI/protocol error (%d)\n\n", ret);
  554. fail_count++;
  555. } else if (status == STAT_UNKNOWN_CMD) {
  556. printf(" [FAIL] Device returned UNKNOWN_CMD\n\n");
  557. fail_count++;
  558. } else if (status == STAT_SUCCESS ||
  559. status == STAT_BAD_PARAM ||
  560. status == STAT_ERR_KEY ||
  561. status == STAT_ERR_KEY_NOT_INIT ||
  562. status == STAT_ERR_SESSION) {
  563. /* These statuses indicate the command was recognized
  564. * even if the operation couldn't complete without parameters.
  565. * For CMD_GET_INFO, we expect STAT_SUCCESS. */
  566. if (cmd == CMD_GET_INFO && status != STAT_SUCCESS) {
  567. printf(" [WARN] GET_INFO returned %s (expected SUCCESS)\n\n",
  568. status_name(status));
  569. fail_count++;
  570. } else {
  571. printf(" [PASS] Status: %s (0x%02X)\n\n",
  572. status_name(status), status);
  573. pass_count++;
  574. }
  575. } else {
  576. printf(" [PASS] Status: %s (0x%02X)\n\n",
  577. status_name(status), status);
  578. pass_count++;
  579. }
  580. }
  581. printf("========================================\n");
  582. printf(" Results: %d PASS, %d FAIL (total %zu)\n",
  583. pass_count, fail_count, NUM_CMD_CODES);
  584. printf("========================================\n");
  585. return fail_count;
  586. }
  587. /* ======================================================================== */
  588. /* Main program */
  589. /* ======================================================================== */
  590. static void print_usage(const char *prog)
  591. {
  592. printf("GMSP SPI Test Tool for CUni360S-Z\n");
  593. printf("Usage: %s [OPTIONS]\n\n", prog);
  594. printf("Options:\n");
  595. printf(" --spi DEVICE SPI device path (default: %s)\n", DEFAULT_SPI_DEVICE);
  596. printf(" --cmd CODE Command code in hex (e.g. 0xF0)\n");
  597. printf(" --data DATA Data payload as hex string (e.g. \"deadbeef\") or ASCII\n");
  598. printf(" --speed HZ SPI clock speed in Hz (default: %d)\n", DEFAULT_SPI_SPEED);
  599. printf(" --test-all Run all 17 command tests sequentially\n");
  600. printf(" --verbose Enable verbose hex dump output\n");
  601. printf(" --help Show this help message\n\n");
  602. printf("Examples:\n");
  603. printf(" %s --spi /dev/spidev0.0 --cmd 0xF0\n", prog);
  604. printf(" %s --spi /dev/spidev0.0 --cmd 0x01 --data \"0123456789ABCDEF\"\n", prog);
  605. printf(" %s --spi /dev/spidev0.0 --speed 5000000 --test-all\n", prog);
  606. printf("\nCommand codes:\n");
  607. printf(" 0x01 SM2_KEYGEN 0x02 SM2_SIGN 0x03 SM2_VERIFY\n");
  608. printf(" 0x04 SM2_ENCRYPT 0x05 SM2_DECRYPT 0x06 SM2_KEYEX\n");
  609. printf(" 0x10 SM3_HASH\n");
  610. printf(" 0x20 SM4_ECB_ENC 0x21 SM4_ECB_DEC 0x22 SM4_CBC_ENC\n");
  611. printf(" 0x23 SM4_CBC_DEC 0x24 SM4_STREAM_INIT 0x25 SM4_STREAM_UPD\n");
  612. printf(" 0x26 SM4_STREAM_FIN\n");
  613. printf(" 0x30 EXPORT_PUBKEY 0x31 GEN_CERT\n");
  614. printf(" 0xF0 GET_INFO\n");
  615. }
  616. int main(int argc, char *argv[])
  617. {
  618. const char *spi_device = DEFAULT_SPI_DEVICE;
  619. uint32_t spi_speed = DEFAULT_SPI_SPEED;
  620. int cmd_code = -1; /* -1 = not specified */
  621. const char *data_str = NULL;
  622. int test_all = 0;
  623. int verbose = 0;
  624. static struct option long_options[] = {
  625. {"spi", required_argument, 0, 's'},
  626. {"cmd", required_argument, 0, 'c'},
  627. {"data", required_argument, 0, 'd'},
  628. {"speed", required_argument, 0, 'f'},
  629. {"test-all",no_argument, 0, 't'},
  630. {"verbose", no_argument, 0, 'v'},
  631. {"help", no_argument, 0, 'h'},
  632. {0, 0, 0, 0}
  633. };
  634. int opt;
  635. int option_index = 0;
  636. while ((opt = getopt_long(argc, argv, "s:c:d:f:tvh", long_options,
  637. &option_index)) != -1) {
  638. switch (opt) {
  639. case 's':
  640. spi_device = optarg;
  641. break;
  642. case 'c':
  643. cmd_code = (int)strtol(optarg, NULL, 0);
  644. if (cmd_code < 0 || cmd_code > 0xFF) {
  645. fprintf(stderr, "Invalid command code: %s (must be 0x00-0xFF)\n",
  646. optarg);
  647. return 1;
  648. }
  649. break;
  650. case 'd':
  651. data_str = optarg;
  652. break;
  653. case 'f':
  654. spi_speed = (uint32_t)strtoul(optarg, NULL, 0);
  655. if (spi_speed == 0) {
  656. fprintf(stderr, "Invalid SPI speed: %s\n", optarg);
  657. return 1;
  658. }
  659. break;
  660. case 't':
  661. test_all = 1;
  662. break;
  663. case 'v':
  664. verbose = 1;
  665. break;
  666. case 'h':
  667. default:
  668. print_usage(argv[0]);
  669. return (opt == 'h') ? 0 : 1;
  670. }
  671. }
  672. /* Validate arguments */
  673. if (!test_all && cmd_code < 0) {
  674. fprintf(stderr, "Error: must specify --cmd or --test-all\n\n");
  675. print_usage(argv[0]);
  676. return 1;
  677. }
  678. /* Open SPI device */
  679. if (spi_open(spi_device, spi_speed) < 0) {
  680. return 1;
  681. }
  682. int exit_code = 0;
  683. if (test_all) {
  684. /* Run all 17 command tests */
  685. exit_code = test_all_commands();
  686. } else {
  687. /* Single command */
  688. uint8_t payload_buf[GMSP_MAX_PAYLOAD];
  689. uint16_t payload_len = 0;
  690. uint8_t status;
  691. const uint8_t *rsp_data;
  692. uint16_t rsp_dlen;
  693. int ret;
  694. /* Parse payload data if provided */
  695. if (data_str != NULL) {
  696. int dlen = hex_to_bytes(data_str, payload_buf, sizeof(payload_buf));
  697. if (dlen < 0) {
  698. spi_close();
  699. return 1;
  700. }
  701. payload_len = (uint16_t)dlen;
  702. }
  703. ret = send_command((uint8_t)cmd_code, payload_buf, payload_len,
  704. &status, &rsp_data, &rsp_dlen);
  705. if (ret < 0) {
  706. exit_code = 2;
  707. } else {
  708. printf("\nResult: status=%s (0x%02X)\n",
  709. status_name(status), status);
  710. if (status != STAT_SUCCESS) {
  711. exit_code = 3;
  712. }
  713. }
  714. }
  715. spi_close();
  716. return exit_code;
  717. }