# gmsm: ShangMi cipher suites and post-quantum algorithms in Go

> gmsm is a Go cryptography library covering the Chinese ShangMi standards, SM2, SM3, SM4, SM9 and ZUC, alongside the NIST post-quantum algorithms ML-KEM, ML-DSA and SLH-DSA, with assembly and SIMD paths for amd64, arm64, s390x, ppc64le, riscv64 and loong64. The API is shaped to sit close to the Go standard library.

**emmansun/gmsm** — ShangMi (SM) cipher suites for Go (Go语言商用密码软件)

- Repository: https://github.com/emmansun/gmsm
- Website: https://emmansun.github.io/gmsm
- Stars: 563 · Forks: 97
- Language: Go
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/emmansun-gmsm

## What the library is for

gmsm describes itself as a production-oriented Go cryptography library focused on the Chinese commercial cryptography standards, SM2, SM3, SM4, SM9 and ZUC, and on post-quantum cryptography, ML-KEM, ML-DSA and SLH-DSA, balancing a maintainable API against cross-architecture performance. The README lists five reasons to choose it. Standard coverage is broad, spanning the mainstream ShangMi standards, the PKCS ecosystem and the NIST post-quantum standards FIPS 203, 204 and 205. The interfaces are close to the Go standard library, which is what makes migration and integration practical rather than an exercise in translation. Performance comes from SIMD and assembly paths on amd64, arm64, loong64, ppc64le, s390x and riscv64. Security and compatibility are balanced through a purego fallback path and constant-time implementations on the critical paths. And the project is continuously verified, with multi-architecture CI and coverage reporting.

The audience is therefore fairly specific. If you are writing a Go service that has to interoperate with a Chinese banking or certificate authority infrastructure, or that is subject to a commercial cryptography requirement, this library is the shortcut. If you are not, the Go standard library is the better answer for everything except the ShangMi and PQC algorithms, and gmsm is mostly useful to you for the PQC half.

## Getting a first hash computed

The quick start is one command and one small program. Install the module:

```bash
go get github.com/emmansun/gmsm@latest
```

Then call SM3 as a digest function, in the shape the standard library's hash packages use:

```go
package main

import (
  "fmt"

  "github.com/emmansun/gmsm/sm3"
)

func main() {
  sum := sm3.Sum([]byte("hello gmsm"))
  fmt.Printf("SM3: %x\n", sum)
}
```

The Sum function returning a fixed-size digest for a byte slice is the standard library convention, and matching it is deliberate. The README's own framing of the library as interface-style-close to the Go standard library shows up here in the smallest possible example, which is exactly what you want from a cryptographic dependency: no new mental model, just a different digest name.

The module definition fixes the floor. The go.mod file declares module github.com/emmansun/gmsm, a go directive of 1.26.0, and a single direct dependency, golang.org/x/crypto v0.58.0, with golang.org/x/sys v0.49.0 pulled in as an indirect dependency. The presence of x/sys is the clue to the architecture story, since that is where the assembly and system-level plumbing lives.

## The assembly and SIMD matrix

The performance claims are specific per algorithm and per architecture, which is where the real content of this repository sits. SM2's core implementation lives in the internal/sm2ec package and is described as comparable in performance to the Go standard library's native P-256 implementation that is not BoringCrypto, with assembly optimizations for amd64, arm64, s390x, ppc64le, riscv64 and loong64. SM9's underlying mathematics, the prime field, extension field, elliptic curve and bilinear pairing, live in the sm9/bn256 package with optimized implementations for amd64, arm64, ppc64x, riscv64 and loong64.

The symmetric side is more granular. SM3 uses AVX2 plus BMI2 and SSE2 plus SSSE3 on amd64 for message expansion, NEON on arm64 with an implementation based on the A64 extension crypto instructions, vector instructions on s390x and ppc64x, and the Zvksh instruction set on riscv64. SM4 combines GFNI and AES-NI with AVX2 or SSE2 plus SSSE3 on amd64, AES with NEON on arm64, the vsbox instruction with vector instructions on ppc64x, and Zvksed on riscv64 with GCM implemented using Zvkg or Zvbc. ZUC uses SIMD, AES instructions and carry-less multiplication, with AES and PCLMUL on amd64, AES and PMULL on arm64, AES and VPMSUMD on ppc64x, and Zvkned with Zvbc on riscv64.

The CI badges match this list one for one, with separate workflows for arm64, smni on arm64, riscv64, loong64, ppc64le and s390x. That is a stronger signal than any release cadence, because a change to one architecture's assembly breaks that architecture's build, and the pull request cannot merge until it is fixed.

## The public key algorithms and the standard they cite

The public key half covers SM2 and SM9, and the README is careful to name the standard for each operation rather than just naming the algorithm. SM2 provides the digital signature algorithm under GB/T 32918.2-2016, the public key encryption algorithm under GB/T 32918.4-2016, the key exchange protocol under GB/T 32918.3-2016, and the key pair protection data format under GB/T 35276-2017. The last of those is worth pausing on, because it is the mechanism by which an SM2 key pair is encrypted for storage, and it is the piece most often missing from partial implementations.

SM9, the identity-based algorithm, provides key generation under GM/T 0044-2016, digital signature, a key encapsulation mechanism, public key encryption and key exchange. Identity-based cryptography is the conceptual difference from SM2: the public key is derived from an identity string rather than generated as a key pair, which removes the certificate problem but requires a trusted key generator.

Both have dedicated wiki pages in the repository covering performance optimisation and implementation strategy, which is where the design decisions are documented in more depth than the README has room for.

## Block cipher modes, and the concurrency warning in XTS

The cipher package implements the work modes defined in the GB/T 17964-2021 standard, and it is one of the more carefully annotated parts of the repository. ECB is there, along with CCM per RFC 3610, XTS per GB/T 17964-2021 and NIST SP 800-38E, HCTR, a counter mode with a general hash function that GB/T 17964-2021 added, BC as a legacy block chaining mode from GB/T 17964, and OFBNLF, an output feedback mode with a nonlinear function, also from the legacy standard.

The caveats are stated rather than buried, and one of them is a real constraint. The XTS mode implements the cipher.BlockMode interface and holds tweak state internally, so it does not support concurrent use. That is inherent to how the tweak works and it means an XTS instance cannot be shared across goroutines, which is a detail that will bite anyone using it to encrypt many disks in parallel. The README also notes that BC mode is similar in function to CBC, and that OFBNLF has limited room for software performance optimisation.

The padding package deserves the same attention because it is a security boundary. It implements PKCS#7 per RFC 5652, ISO/IEC 9797-1 method 2, ANSI X.923, ISO/IEC 9797-1 method 3 and zero padding as a legacy compatibility option. Every scheme offers both ConstantTimeUnpad() and Unpad(), and the README is unambiguous: data you have encrypted must be decrypted with ConstantTimeUnpad() to defend against timing attacks, while Unpad() exists only as a performance optimisation for non-sensitive data. Getting that backwards is exactly the kind of mistake that turns a padding oracle into a decryption oracle.

## Certificates, PKCS structures and the PKI layer

The PKI side is what turns a set of algorithms into something a certificate infrastructure can use. The smx509 package extends the Go standard library's crypto/x509 with ShangMi algorithm support, producing certificates that conform to GM/T 0015-2012, the digital certificate format specification based on the SM2 algorithm. That extension point is significant: because it extends crypto/x509 rather than replacing it, a Go program can parse and verify a mixed certificate chain through the standard library's own machinery.

The PKCS#7 package is based on the mozilla-services/pkcs7 project, which the README notes was archived in February 2024, extended with ShangMi support and conforming to RFC 2315. The PKCS#8 package is similarly based on youmark/pkcs8, extended for ShangMi and conforming to RFC 5208 and RFC 5958. Deriving from archived upstream projects is a pragmatic choice, since the RFC-conformant part of the work is done and the extension is the ShangMi part, but it does mean the base is not receiving upstream fixes.

There is also a cfca package, and the README lists a CFCA interoperability guide among the documents, alongside guides for SM2, SM3, SM4, SM9, ZUC, PKCS7, PKCS12, the DRBG and software random number generator, and post-quantum cryptography. A CFCA guide matters because CFCA is the Chinese certificate authority whose tooling many counterparties expect to interoperate with, and interoperability is the hard part of any national crypto standard.

## Randomness, key agreement and the TLS layer

Two infrastructure pieces round out the library. The ecdh package offers an interface design similar to the Go standard library's crypto/ecdh and supports key agreement over the SM2 curve, with both plain ECDH and the SM2-specific key exchange protocol listed among the supported protocols. There is a separate mention of SM2MQV in the capability summary. The drbg and rand packages provide a deterministic random bit generator and a software random number generator, each with its own application guide in the docs directory, and a shake package covers the SHAKE extendable output functions.

The tls13 directory indicates a TLS 1.3 hybrid key exchange implementation, which is the piece that connects the library to real network protocols. A hybrid key exchange combines a classical key exchange with a post-quantum one, so that traffic is protected if either primitive holds, which is the mechanism the NIST post-quantum standards are designed to slot into.

The post-quantum side is in its own directories, mlkem for ML-KEM, mldsa for ML-DSA and slhdsa for SLH-DSA, corresponding to FIPS 203, 204 and 205. The kdf package holds key derivation functions, and the cbcmac package implements the message authentication code standard GB/T 15852.1-2020 with eight schemes, including CBC-MAC, EMAC, ANSI Retail MAC, MAC-DES, CMAC per RFC 4493, LMAC, TR-CBC-MAC and CBCR-MAC.

## Where gmsm is the wrong choice

Three limits are worth stating plainly. The first is that this is not a general purpose cryptography library to replace the standard library. Go's crypto packages are audited, boring and present; gmsm's value is the ShangMi and PQC algorithms they do not have, and a project that reaches for it for AES has misunderstood the scope. The second is the Go version floor. The go.mod declares go 1.26.0, which means adopting this library means adopting that toolchain version, a constraint that is easy to miss and awkward to unwind later, especially in a large organisation with a pinned build image. The third is the interoperability surface, which is where the real work lies. The algorithm being present is not the same as the peer accepting it: certificates have to be issued in the right format, CFCA tooling has to agree, and the CFCA interoperability guide exists precisely because that agreement is not automatic.

There is also a documented note in the repository about the XTS concurrency restriction, and a DISCLAIMER.md at the root, which is a sensible thing for a cryptography library to carry and a reminder that a library passing its own test suite is not the same as a deployment having been reviewed. The licence is MIT, so the code can be used in commercial products without the copyleft obligations that a GPL library would impose, and the default branch is develop with releases cut as v0.45.0 on 2026-10-04, v0.44.1 on 2026-07-28 and v0.44.0 on 2026-06-26, with the last push to the repository on 2026-10-22.

## Conclusion

gmsm suits a Go team that has to speak the ShangMi standards, whether for CFCA interoperation, a Chinese commercial cryptography requirement or a product that has to interoperate with SM2 and SM4 peers, and the fact that its interfaces follow the Go standard library is what makes migration feasible. It is the wrong tool if you only need AES or NIST curves, since the standard library already covers those, and it is the wrong tool for a project that cannot accept a dependency that goes beyond Go 1.26.0 and golang.org/x/crypto. Verify first which algorithm profile you actually need, since XTS mode in the cipher package is documented as not safe for concurrent use, and note that the newest release is v0.45.0 from 2026-10-04 with the last push on 2026-10-22.

## FAQ

### How do I install gmsm and compute an SM3 hash in Go?

Run go get github.com/emmansun/gmsm@latest, then import github.com/emmansun/gmsm/sm3 and call sm3.Sum on a byte slice, which returns the digest in the same shape as the Go standard library's hash packages.

### Which algorithms does gmsm implement?

The ShangMi standards SM2, SM3, SM4, SM9 and ZUC, the post-quantum algorithms ML-KEM, ML-DSA and SLH-DSA following NIST FIPS 203, 204 and 205, plus CBCMAC, block cipher work modes, padding schemes, DRBG, random numbers, ECDH and SM2MQV, and a TLS 1.3 hybrid key exchange.

### Which platforms does gmsm optimise for?

Assembly and SIMD implementations exist for amd64, arm64, loong64, ppc64le, s390x and riscv64, with a purego fallback path for other platforms. The SM3 path uses AVX2 with BMI2 on amd64, NEON on arm64 and Zvksh on riscv64, and SM4 uses GFNI and AES-NI on amd64 and Zvksed on riscv64.

### Can I use the cipher package's XTS mode concurrently?

No. The README states that the XTS mode implements the cipher.BlockMode interface and holds tweak state internally, so it does not support concurrent use. The cipher package also notes that OFBNLF has limited room for software performance optimisation.

### Which unpadding method should I use when decrypting?

Use ConstantTimeUnpad() for any data you encrypted, since the README says encrypted data must be unpadded that way to defend against timing attacks. Unpad() is only a performance optimisation for non-sensitive data.

### What Go version does gmsm require?

The go.mod declares go 1.26.0, with golang.org/x/crypto v0.58.0 as the single direct dependency and golang.org/x/sys v0.49.0 as an indirect one.

## Sources

- [emmansun/gmsm on GitHub](https://github.com/emmansun/gmsm)
- [License: MIT](https://github.com/emmansun/gmsm/blob/develop/LICENSE)
- [Project website](https://emmansun.github.io/gmsm)
- [README](https://github.com/emmansun/gmsm/blob/develop/README.md)
- [Releases](https://github.com/emmansun/gmsm/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/emmansun-gmsm
