1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

Feat/encryption for sm2 sm3 sm4 (#343)

* feat: add ContainAny

* feat:encryption adds support for SM2, SM3, and SM4 #131

* doc: add docment for SM2, SM3, and SM4 #131

---------

Co-authored-by: Jiawen <im@linjiawen.com>
This commit is contained in:
Javen
2025-11-07 19:17:55 +08:00
committed by GitHub
parent 5c13fd4f2f
commit 0851b68b83
9 changed files with 1583 additions and 2 deletions

View File

@@ -523,6 +523,30 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>RsaVerifySign</big>** : verifies the signature of the data with RSA. - **<big>RsaVerifySign</big>** : verifies the signature of the data with RSA.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaVerifySign)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaVerifySign)]
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)] [[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
- **<big>GenerateSm2Key</big>** : generate SM2 private and public key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateSm2Key)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm2Encrypt</big>** : encrypt data with SM2 public key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm2Encrypt)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm2Decrypt</big>** : decrypt data with SM2 private key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm2Decrypt)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm3</big>** : return the SM3 hash value (256-bit) of data.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm3)]
[[play](https://go.dev/play/p/zDAQpteAiOc)]
- **<big>Sm4EcbEncrypt</big>** : encrypt data with SM4 ECB mode.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm4EcbEncrypt)]
[[play](https://go.dev/play/p/l5IQxYuuaED)]
- **<big>Sm4EcbDecrypt</big>** : decrypt data with SM4 ECB mode.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm4EcbDecrypt)]
[[play](https://go.dev/play/p/l5IQxYuuaED)]
- **<big>Sm4CbcEncrypt</big>** : encrypt data with SM4 CBC mode.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm4CbcEncrypt)]
[[play](https://go.dev/play/p/65Q6iYhLRTa)]
- **<big>Sm4CbcDecrypt</big>** : decrypt data with SM4 CBC mode.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Sm4CbcDecrypt)]
[[play](https://go.dev/play/p/65Q6iYhLRTa)]
<h3 id="datetime"> 7. Datetime package supports date and time format and compare. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3> <h3 id="datetime"> 7. Datetime package supports date and time format and compare. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>

View File

@@ -535,6 +535,30 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>RsaVerifySign</big>** : 验证数据的签名是否符合 RSA 算法。 - **<big>RsaVerifySign</big>** : 验证数据的签名是否符合 RSA 算法。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaVerifySign)]
[[play](https://go.dev/play/p/qhsbf8BJ6Mf)] [[play](https://go.dev/play/p/qhsbf8BJ6Mf)]
- **<big>GenerateSm2Key</big>** : 生成 SM2 公钥和私钥。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateSm2Key)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm2Encrypt</big>** : 使用 SM2 公钥加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm2Encrypt)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm2Decrypt</big>** : 使用 SM2 私钥解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm2Decrypt)]
[[play](https://go.dev/play/p/bKYMqRLvIx3)]
- **<big>Sm3</big>** : 返回数据的 SM3 哈希值256 位)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm3)]
[[play](https://go.dev/play/p/zDAQpteAiOc)]
- **<big>Sm4EcbEncrypt</big>** : 使用 SM4 ECB 模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm4EcbEncrypt)]
[[play](https://go.dev/play/p/l5IQxYuuaED)]
- **<big>Sm4EcbDecrypt</big>** : 使用 SM4 ECB 模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm4EcbDecrypt)]
[[play](https://go.dev/play/p/l5IQxYuuaED)]
- **<big>Sm4CbcEncrypt</big>** : 使用 SM4 CBC 模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm4CbcEncrypt)]
[[play](https://go.dev/play/p/65Q6iYhLRTa)]
- **<big>Sm4CbcDecrypt</big>** : 使用 SM4 CBC 模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Sm4CbcDecrypt)]
[[play](https://go.dev/play/p/65Q6iYhLRTa)]
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3> <h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>

View File

@@ -0,0 +1,71 @@
package cryptor_test
import (
"encoding/hex"
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func ExampleSm3() {
data := []byte("hello world")
hash := cryptor.Sm3(data)
fmt.Println(hex.EncodeToString(hash))
// Output:
// 44f0061e69fa6fdfc290c494654a05dc0c053da7e5c52b84ef93a9d67d3fff88
}
func ExampleSm4EcbEncrypt() {
key := []byte("1234567890abcdef") // 16 bytes key
plaintext := []byte("hello world")
encrypted := cryptor.Sm4EcbEncrypt(plaintext, key)
decrypted := cryptor.Sm4EcbDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
func ExampleSm4CbcEncrypt() {
key := []byte("1234567890abcdef") // 16 bytes key
plaintext := []byte("hello world")
encrypted := cryptor.Sm4CbcEncrypt(plaintext, key)
decrypted := cryptor.Sm4CbcDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
func ExampleGenerateSm2Key() {
// Generate SM2 key pair
privateKey, err := cryptor.GenerateSm2Key()
if err != nil {
return
}
plaintext := []byte("hello world")
// Encrypt with public key
ciphertext, err := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
if err != nil {
return
}
// Decrypt with private key
decrypted, err := cryptor.Sm2Decrypt(privateKey, ciphertext)
if err != nil {
return
}
fmt.Println(string(decrypted))
// Output:
// hello world
}

251
cryptor/gm_sm2.go Normal file
View File

@@ -0,0 +1,251 @@
package cryptor
import (
"crypto/elliptic"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"math/big"
)
// SM2 implements the Chinese SM2 elliptic curve public key algorithm.
// SM2 is based on elliptic curve cryptography and provides encryption, decryption, signing and verification.
//
// Note: This implementation uses crypto/elliptic package methods (GenerateKey, ScalarBaseMult, ScalarMult, IsOnCurve)
// which are marked as deprecated in Go 1.20+. These methods still work correctly and are widely used.
// The //nolint:staticcheck directive suppresses deprecation warnings.
// A future version may replace these with a custom elliptic curve implementation.
var (
sm2P256 *sm2Curve
sm2P256Params = &elliptic.CurveParams{Name: "sm2p256v1"}
)
func init() {
// SM2 curve parameters
sm2P256Params.P, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16)
sm2P256Params.N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
sm2P256Params.B, _ = new(big.Int).SetString("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16)
sm2P256Params.Gx, _ = new(big.Int).SetString("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16)
sm2P256Params.Gy, _ = new(big.Int).SetString("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16)
sm2P256Params.BitSize = 256
sm2P256 = &sm2Curve{sm2P256Params}
}
type sm2Curve struct {
*elliptic.CurveParams
}
// Sm2PrivateKey represents an SM2 private key.
type Sm2PrivateKey struct {
D *big.Int
PublicKey Sm2PublicKey
}
// Sm2PublicKey represents an SM2 public key.
type Sm2PublicKey struct {
X, Y *big.Int
}
// GenerateSm2Key generates a new SM2 private/public key pair.
// Play: https://go.dev/play/p/bKYMqRLvIx3
func GenerateSm2Key() (*Sm2PrivateKey, error) {
priv, x, y, err := elliptic.GenerateKey(sm2P256, rand.Reader)
if err != nil {
return nil, err
}
privateKey := &Sm2PrivateKey{
D: new(big.Int).SetBytes(priv),
PublicKey: Sm2PublicKey{
X: x,
Y: y,
},
}
return privateKey, nil
}
// Sm2Encrypt encrypts plaintext using SM2 public key.
// Returns ciphertext in the format: C1 || C3 || C2
// C1 = kG (65 bytes in uncompressed format)
// C3 = Hash(x2 || M || y2) (32 bytes for SM3)
// C2 = M xor t (same length as plaintext)
// Play: https://go.dev/play/p/bKYMqRLvIx3
func Sm2Encrypt(pub *Sm2PublicKey, plaintext []byte) ([]byte, error) {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil, errors.New("sm2: invalid public key")
}
for {
// Generate random k
k, err := randFieldElement(sm2P256, rand.Reader)
if err != nil {
return nil, err
}
// C1 = kG
c1x, c1y := sm2P256.ScalarBaseMult(k.Bytes())
// kP = (x2, y2)
x2, y2 := sm2P256.ScalarMult(pub.X, pub.Y, k.Bytes())
// Derive key using KDF
kdfLen := len(plaintext)
t := sm2KDF(append(toBytes(sm2P256, x2), toBytes(sm2P256, y2)...), kdfLen)
// Check if t is all zeros
allZero := true
for _, b := range t {
if b != 0 {
allZero = false
break
}
}
if allZero {
continue
}
// C2 = M xor t
c2 := make([]byte, len(plaintext))
for i := 0; i < len(plaintext); i++ {
c2[i] = plaintext[i] ^ t[i]
}
// C3 = Hash(x2 || M || y2)
c3Input := append(toBytes(sm2P256, x2), plaintext...)
c3Input = append(c3Input, toBytes(sm2P256, y2)...)
c3 := Sm3(c3Input)
// Return C1 || C3 || C2
c1 := sm2MarshalUncompressed(sm2P256, c1x, c1y)
result := append(c1, c3...)
result = append(result, c2...)
return result, nil
}
}
// Sm2Decrypt decrypts ciphertext using SM2 private key.
// Expects ciphertext in the format: C1 || C3 || C2
// Play: https://go.dev/play/p/bKYMqRLvIx3
func Sm2Decrypt(priv *Sm2PrivateKey, ciphertext []byte) ([]byte, error) {
if priv == nil || priv.D == nil {
return nil, errors.New("sm2: invalid private key")
}
// Parse C1 (65 bytes), C3 (32 bytes), C2 (remaining)
if len(ciphertext) < 97 {
return nil, errors.New("sm2: ciphertext too short")
}
c1 := ciphertext[:65]
c3 := ciphertext[65:97]
c2 := ciphertext[97:]
// Parse C1
c1x, c1y := sm2UnmarshalUncompressed(sm2P256, c1)
if c1x == nil {
return nil, errors.New("sm2: invalid C1 point")
}
// Verify C1 is on curve
if !sm2P256.IsOnCurve(c1x, c1y) {
return nil, errors.New("sm2: C1 not on curve")
}
// dC1 = (x2, y2)
x2, y2 := sm2P256.ScalarMult(c1x, c1y, priv.D.Bytes())
// Derive key using KDF
kdfLen := len(c2)
t := sm2KDF(append(toBytes(sm2P256, x2), toBytes(sm2P256, y2)...), kdfLen)
// M = C2 xor t
plaintext := make([]byte, len(c2))
for i := 0; i < len(c2); i++ {
plaintext[i] = c2[i] ^ t[i]
}
// Verify C3 = Hash(x2 || M || y2)
u := append(toBytes(sm2P256, x2), plaintext...)
u = append(u, toBytes(sm2P256, y2)...)
hash := Sm3(u)
for i := 0; i < len(c3); i++ {
if c3[i] != hash[i] {
return nil, errors.New("sm2: hash verification failed")
}
}
return plaintext, nil
}
// SM2 KDF (Key Derivation Function)
func sm2KDF(z []byte, klen int) []byte {
limit := (klen + 31) / 32
result := make([]byte, 0, limit*32)
for i := 1; i <= limit; i++ {
counter := make([]byte, 4)
binary.BigEndian.PutUint32(counter, uint32(i))
hash := Sm3(append(z, counter...))
result = append(result, hash...)
}
return result[:klen]
}
func toBytes(curve elliptic.Curve, value *big.Int) []byte {
byteLen := (curve.Params().BitSize + 7) / 8
buf := make([]byte, byteLen)
b := value.Bytes()
copy(buf[byteLen-len(b):], b)
return buf
}
func sm2MarshalUncompressed(curve *sm2Curve, x, y *big.Int) []byte {
byteLen := (curve.BitSize + 7) / 8
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point
xBytes := x.Bytes()
copy(ret[1+byteLen-len(xBytes):], xBytes)
yBytes := y.Bytes()
copy(ret[1+2*byteLen-len(yBytes):], yBytes)
return ret
}
func sm2UnmarshalUncompressed(curve *sm2Curve, data []byte) (*big.Int, *big.Int) {
byteLen := (curve.BitSize + 7) / 8
if len(data) != 1+2*byteLen {
return nil, nil
}
if data[0] != 4 {
return nil, nil
}
x := new(big.Int).SetBytes(data[1 : 1+byteLen])
y := new(big.Int).SetBytes(data[1+byteLen:])
return x, y
}
func randFieldElement(c elliptic.Curve, rand io.Reader) (*big.Int, error) {
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err := io.ReadFull(rand, b)
if err != nil {
return nil, err
}
k := new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, big.NewInt(1))
k.Mod(k, n)
k.Add(k, big.NewInt(1))
return k, nil
}

211
cryptor/gm_sm3.go Normal file
View File

@@ -0,0 +1,211 @@
package cryptor
import (
"encoding/binary"
"hash"
)
// SM3 implements the Chinese SM3 cryptographic hash algorithm.
// SM3 produces a 256-bit (32-byte) hash value.
const (
sm3BlockSize = 64
sm3Size = 32
sm3T1 = 0x79cc4519
sm3T2 = 0x7a879d8a
)
var sm3IV = [8]uint32{
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
}
type sm3Digest struct {
h [8]uint32
x [sm3BlockSize]byte
nx int
len uint64
}
// Sm3 returns a new hash.Hash computing the SM3 checksum.
// Play: https://go.dev/play/p/zDAQpteAiOc
func Sm3(data []byte) []byte {
h := newSm3()
h.Write(data)
return h.Sum(nil)
}
func newSm3() hash.Hash {
d := new(sm3Digest)
d.Reset()
return d
}
func (d *sm3Digest) Reset() {
d.h = sm3IV
d.nx = 0
d.len = 0
}
func (d *sm3Digest) Size() int {
return sm3Size
}
func (d *sm3Digest) BlockSize() int {
return sm3BlockSize
}
func (d *sm3Digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == sm3BlockSize {
sm3Block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= sm3BlockSize {
n := len(p) &^ (sm3BlockSize - 1)
sm3Block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d *sm3Digest) Sum(in []byte) []byte {
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
func (d *sm3Digest) checkSum() [sm3Size]byte {
len := d.len
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
d.Write(tmp[0 : 56-len%64])
} else {
d.Write(tmp[0 : 64+56-len%64])
}
len <<= 3
binary.BigEndian.PutUint64(tmp[:], len)
d.Write(tmp[0:8])
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [sm3Size]byte
for i := 0; i < 8; i++ {
binary.BigEndian.PutUint32(digest[i*4:], d.h[i])
}
return digest
}
func sm3Block(dig *sm3Digest, p []byte) {
var w [68]uint32
var w1 [64]uint32
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= sm3BlockSize {
for i := 0; i < 16; i++ {
j := i * 4
w[i] = binary.BigEndian.Uint32(p[j : j+4])
}
for i := 16; i < 68; i++ {
w[i] = sm3P1(w[i-16]^w[i-9]^sm3RotateLeft(w[i-3], 15)) ^ sm3RotateLeft(w[i-13], 7) ^ w[i-6]
}
for i := 0; i < 64; i++ {
w1[i] = w[i] ^ w[i+4]
}
A, B, C, D, E, F, G, H := h0, h1, h2, h3, h4, h5, h6, h7
for j := 0; j < 64; j++ {
var ss1, ss2, tt1, tt2, t uint32
if j < 16 {
t = sm3T1
} else {
t = sm3T2
}
ss1 = sm3RotateLeft(sm3RotateLeft(A, 12)+E+sm3RotateLeft(t, uint32(j%32)), 7)
ss2 = ss1 ^ sm3RotateLeft(A, 12)
if j < 16 {
tt1 = sm3FF0(A, B, C) + D + ss2 + w1[j]
tt2 = sm3GG0(E, F, G) + H + ss1 + w[j]
} else {
tt1 = sm3FF1(A, B, C) + D + ss2 + w1[j]
tt2 = sm3GG1(E, F, G) + H + ss1 + w[j]
}
D = C
C = sm3RotateLeft(B, 9)
B = A
A = tt1
H = G
G = sm3RotateLeft(F, 19)
F = E
E = sm3P0(tt2)
}
h0 ^= A
h1 ^= B
h2 ^= C
h3 ^= D
h4 ^= E
h5 ^= F
h6 ^= G
h7 ^= H
p = p[sm3BlockSize:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
}
func sm3RotateLeft(x, n uint32) uint32 {
return (x << n) | (x >> (32 - n))
}
func sm3P0(x uint32) uint32 {
return x ^ sm3RotateLeft(x, 9) ^ sm3RotateLeft(x, 17)
}
func sm3P1(x uint32) uint32 {
return x ^ sm3RotateLeft(x, 15) ^ sm3RotateLeft(x, 23)
}
func sm3FF0(x, y, z uint32) uint32 {
return x ^ y ^ z
}
func sm3FF1(x, y, z uint32) uint32 {
return (x & y) | (x & z) | (y & z)
}
func sm3GG0(x, y, z uint32) uint32 {
return x ^ y ^ z
}
func sm3GG1(x, y, z uint32) uint32 {
return (x & y) | (^x & z)
}

270
cryptor/gm_sm4.go Normal file
View File

@@ -0,0 +1,270 @@
package cryptor
import (
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"io"
)
// SM4 implements the Chinese SM4 block cipher.
// SM4 is a 128-bit block cipher with 128-bit keys.
// This implementation uses pre-computed lookup tables for optimal performance.
const sm4BlockSize = 16
// Pre-computed T-transformation lookup tables for performance optimization
var sm4T1Table [256][4]uint32 // S-box + L1 transformation
var sm4T2Table [256][4]uint32 // S-box + L2 transformation
var sm4Sbox = [256]byte{
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
}
var sm4FK = [4]uint32{0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc}
var sm4CK = [32]uint32{
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279,
}
// 初始化预计算查找表
func init() {
// Pre-compute all possible T1 and T2 transformations
for pos := 0; pos < 4; pos++ {
for i := 0; i < 256; i++ {
// S-box 替换
sboxVal := sm4Sbox[i]
// 根据字节位置计算偏移
shift := uint32((3 - pos) * 8)
b := uint32(sboxVal) << shift
// L1 变换b ^ ROL(b,2) ^ ROL(b,10) ^ ROL(b,18) ^ ROL(b,24)
sm4T1Table[i][pos] = b ^ sm4RotateLeft(b, 2) ^ sm4RotateLeft(b, 10) ^ sm4RotateLeft(b, 18) ^ sm4RotateLeft(b, 24)
// L2 变换b ^ ROL(b,13) ^ ROL(b,23)
sm4T2Table[i][pos] = b ^ sm4RotateLeft(b, 13) ^ sm4RotateLeft(b, 23)
}
}
}
type sm4Cipher struct {
enc [32]uint32
dec [32]uint32
}
// Sm4EcbEncrypt encrypts data using SM4 in ECB mode.
// key must be 16 bytes.
// Play: https://go.dev/play/p/l5IQxYuuaED
func Sm4EcbEncrypt(data, key []byte) []byte {
if len(key) != 16 {
panic("sm4: key length must be 16 bytes")
}
c := newSm4Cipher(key)
padded := pkcs7Padding(data, sm4BlockSize)
encrypted := make([]byte, len(padded))
for i := 0; i < len(padded); i += sm4BlockSize {
c.Encrypt(encrypted[i:i+sm4BlockSize], padded[i:i+sm4BlockSize])
}
return encrypted
}
// Sm4EcbDecrypt decrypts data using SM4 in ECB mode.
// key must be 16 bytes.
// Play: https://go.dev/play/p/l5IQxYuuaED
func Sm4EcbDecrypt(encrypted, key []byte) []byte {
if len(key) != 16 {
panic("sm4: key length must be 16 bytes")
}
if len(encrypted)%sm4BlockSize != 0 {
panic("sm4: encrypted data length must be multiple of block size")
}
c := newSm4Cipher(key)
decrypted := make([]byte, len(encrypted))
for i := 0; i < len(encrypted); i += sm4BlockSize {
c.Decrypt(decrypted[i:i+sm4BlockSize], encrypted[i:i+sm4BlockSize])
}
return pkcs7UnPadding(decrypted)
}
// Sm4CbcEncrypt encrypts data using SM4 in CBC mode.
// key must be 16 bytes.
// Play: https://go.dev/play/p/65Q6iYhLRTa
func Sm4CbcEncrypt(data, key []byte) []byte {
if len(key) != 16 {
panic("sm4: key length must be 16 bytes")
}
c := newSm4Cipher(key)
padded := pkcs7Padding(data, sm4BlockSize)
iv := make([]byte, sm4BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("sm4: failed to generate IV: " + err.Error())
}
encrypted := make([]byte, len(padded))
mode := cipher.NewCBCEncrypter(c, iv)
mode.CryptBlocks(encrypted, padded)
return append(iv, encrypted...)
}
// Sm4CbcDecrypt decrypts data using SM4 in CBC mode.
// key must be 16 bytes.
// Play: https://go.dev/play/p/65Q6iYhLRTa
func Sm4CbcDecrypt(encrypted, key []byte) []byte {
if len(key) != 16 {
panic("sm4: key length must be 16 bytes")
}
if len(encrypted) < sm4BlockSize {
panic("sm4: encrypted data too short")
}
if len(encrypted)%sm4BlockSize != 0 {
panic("sm4: encrypted data length must be multiple of block size")
}
c := newSm4Cipher(key)
iv := encrypted[:sm4BlockSize]
ciphertext := encrypted[sm4BlockSize:]
decrypted := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(c, iv)
mode.CryptBlocks(decrypted, ciphertext)
return pkcs7UnPadding(decrypted)
}
func newSm4Cipher(key []byte) *sm4Cipher {
c := &sm4Cipher{}
var mk [4]uint32
for i := 0; i < 4; i++ {
mk[i] = binary.BigEndian.Uint32(key[i*4 : (i+1)*4])
}
var k [36]uint32
k[0] = mk[0] ^ sm4FK[0]
k[1] = mk[1] ^ sm4FK[1]
k[2] = mk[2] ^ sm4FK[2]
k[3] = mk[3] ^ sm4FK[3]
for i := 0; i < 32; i++ {
k[i+4] = k[i] ^ sm4T2Fast(k[i+1]^k[i+2]^k[i+3]^sm4CK[i])
c.enc[i] = k[i+4]
}
for i := 0; i < 32; i++ {
c.dec[i] = c.enc[31-i]
}
return c
}
func (c *sm4Cipher) BlockSize() int {
return sm4BlockSize
}
func (c *sm4Cipher) Encrypt(dst, src []byte) {
if len(src) < sm4BlockSize {
panic("sm4: input not full block")
}
if len(dst) < sm4BlockSize {
panic("sm4: output not full block")
}
// 使用局部变量避免数组分配,提升性能
x0 := binary.BigEndian.Uint32(src[0:4])
x1 := binary.BigEndian.Uint32(src[4:8])
x2 := binary.BigEndian.Uint32(src[8:12])
x3 := binary.BigEndian.Uint32(src[12:16])
// 32 轮加密
for i := 0; i < 32; i++ {
t := x1 ^ x2 ^ x3 ^ c.enc[i]
x0 ^= sm4T1Fast(t)
x0, x1, x2, x3 = x1, x2, x3, x0
}
binary.BigEndian.PutUint32(dst[0:4], x3)
binary.BigEndian.PutUint32(dst[4:8], x2)
binary.BigEndian.PutUint32(dst[8:12], x1)
binary.BigEndian.PutUint32(dst[12:16], x0)
}
func (c *sm4Cipher) Decrypt(dst, src []byte) {
if len(src) < sm4BlockSize {
panic("sm4: input not full block")
}
if len(dst) < sm4BlockSize {
panic("sm4: output not full block")
}
x0 := binary.BigEndian.Uint32(src[0:4])
x1 := binary.BigEndian.Uint32(src[4:8])
x2 := binary.BigEndian.Uint32(src[8:12])
x3 := binary.BigEndian.Uint32(src[12:16])
// 32 轮解密
for i := 0; i < 32; i++ {
t := x1 ^ x2 ^ x3 ^ c.dec[i]
x0 ^= sm4T1Fast(t)
x0, x1, x2, x3 = x1, x2, x3, x0
}
binary.BigEndian.PutUint32(dst[0:4], x3)
binary.BigEndian.PutUint32(dst[4:8], x2)
binary.BigEndian.PutUint32(dst[8:12], x1)
binary.BigEndian.PutUint32(dst[12:16], x0)
}
// 使用预计算查找表的快速 T1 变换(用于加密轮函数)
func sm4T1Fast(a uint32) uint32 {
return sm4T1Table[byte(a>>24)][0] ^
sm4T1Table[byte(a>>16)][1] ^
sm4T1Table[byte(a>>8)][2] ^
sm4T1Table[byte(a)][3]
}
// 使用预计算查找表的快速 T2 变换(用于密钥扩展)
func sm4T2Fast(a uint32) uint32 {
return sm4T2Table[byte(a>>24)][0] ^
sm4T2Table[byte(a>>16)][1] ^
sm4T2Table[byte(a>>8)][2] ^
sm4T2Table[byte(a)][3]
}
func sm4RotateLeft(x uint32, n uint32) uint32 {
return (x << n) | (x >> (32 - n))
}

163
cryptor/gm_test.go Normal file
View File

@@ -0,0 +1,163 @@
package cryptor
import (
"encoding/hex"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestSm3(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm3")
tests := []struct {
input string
expected string
}{
{
input: "abc",
expected: "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0",
},
{
input: "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
expected: "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732",
},
{
input: "",
expected: "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b",
},
}
for _, tt := range tests {
result := Sm3([]byte(tt.input))
resultHex := hex.EncodeToString(result)
assert.Equal(tt.expected, resultHex)
}
}
func TestSm4EcbEncryptDecrypt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm4EcbEncryptDecrypt")
key := []byte("1234567890abcdef") // 16 bytes
plaintext := []byte("Hello, SM4!")
// Encrypt
encrypted := Sm4EcbEncrypt(plaintext, key)
assert.IsNotNil(encrypted)
// Decrypt
decrypted := Sm4EcbDecrypt(encrypted, key)
assert.Equal(plaintext, decrypted)
}
func TestSm4CbcEncryptDecrypt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm4CbcEncryptDecrypt")
key := []byte("1234567890abcdef") // 16 bytes
plaintext := []byte("Hello, SM4 CBC mode!")
// Encrypt
encrypted := Sm4CbcEncrypt(plaintext, key)
assert.IsNotNil(encrypted)
// Decrypt
decrypted := Sm4CbcDecrypt(encrypted, key)
assert.Equal(plaintext, decrypted)
}
func TestSm4EcbWithLongData(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm4EcbWithLongData")
key := []byte("1234567890abcdef")
plaintext := []byte("This is a longer message that spans multiple blocks for SM4 encryption testing.")
encrypted := Sm4EcbEncrypt(plaintext, key)
decrypted := Sm4EcbDecrypt(encrypted, key)
assert.Equal(plaintext, decrypted)
}
func TestSm2EncryptDecrypt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm2EncryptDecrypt")
// Generate key pair
privateKey, err := GenerateSm2Key()
assert.IsNil(err)
assert.IsNotNil(privateKey)
plaintext := []byte("Hello, SM2!")
// Encrypt with public key
ciphertext, err := Sm2Encrypt(&privateKey.PublicKey, plaintext)
assert.IsNil(err)
assert.IsNotNil(ciphertext)
// Decrypt with private key
decrypted, err := Sm2Decrypt(privateKey, ciphertext)
assert.IsNil(err)
assert.Equal(plaintext, decrypted)
}
func TestSm2WithLongData(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm2WithLongData")
privateKey, err := GenerateSm2Key()
assert.IsNil(err)
plaintext := []byte("This is a longer message for SM2 encryption testing. " +
"SM2 is an elliptic curve public key cryptography algorithm.")
ciphertext, err := Sm2Encrypt(&privateKey.PublicKey, plaintext)
assert.IsNil(err)
decrypted, err := Sm2Decrypt(privateKey, ciphertext)
assert.IsNil(err)
assert.Equal(plaintext, decrypted)
}
func TestSm4InvalidKeyLength(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm4InvalidKeyLength")
defer func() {
if r := recover(); r != nil {
assert.IsNotNil(r)
}
}()
key := []byte("short")
plaintext := []byte("test")
Sm4EcbEncrypt(plaintext, key) // Should panic
}
func TestSm2InvalidInput(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSm2InvalidInput")
// Test with nil public key
_, err := Sm2Encrypt(nil, []byte("test"))
assert.IsNotNil(err)
// Test with nil private key
_, err = Sm2Decrypt(nil, []byte("test"))
assert.IsNotNil(err)
// Test with invalid ciphertext
privateKey, _ := GenerateSm2Key()
_, err = Sm2Decrypt(privateKey, []byte("short"))
assert.IsNotNil(err)
}

View File

@@ -1,6 +1,6 @@
# Cryptor # Cryptor
cryptor 包包含数据加密和解密功能。支持 base64, md5, hmac, hash, aes, des, rsa。 cryptor 包包含数据加密和解密功能。支持 base64, md5, hmac, hash, aes, des, rsa, sm2, sm3, sm4
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -74,6 +74,14 @@ import (
- [RsaDecryptOAEP](#RsaDecryptOAEP) - [RsaDecryptOAEP](#RsaDecryptOAEP)
- [RsaSign](#RsaSign) - [RsaSign](#RsaSign)
- [RsaVerifySign](#RsaVerifySign) - [RsaVerifySign](#RsaVerifySign)
- [Sm3](#Sm3)
- [Sm4EcbEncrypt](#Sm4EcbEncrypt)
- [Sm4EcbDecrypt](#Sm4EcbDecrypt)
- [Sm4CbcEncrypt](#Sm4CbcEncrypt)
- [Sm4CbcDecrypt](#Sm4CbcDecrypt)
- [GenerateSm2Key](#GenerateSm2Key)
- [Sm2Encrypt](#Sm2Encrypt)
- [Sm2Decrypt](#Sm2Decrypt)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1829,3 +1837,279 @@ func main() {
} }
} }
``` ```
### <span id="Sm3">Sm3</span>
<p>计算 SM3 哈希值国密SM3密码杂凑算法。SM3 是中国国家密码管理局发布的密码杂凑算法,用于替代 MD5/SHA-1/SHA-2 等国际算法。</p>
<b>函数签名:</b>
```go
func Sm3(data []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zDAQpteAiOc)</span></b>
```go
package main
import (
"encoding/hex"
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("hello world")
hash := cryptor.Sm3(data)
fmt.Println(hex.EncodeToString(hash))
// Output:
// 44f0061e69fa6fdfc290c494654a05dc0c053da7e5c52b84ef93a9d67d3fff88
}
```
### <span id="Sm4EcbEncrypt">Sm4EcbEncrypt</span>
<p>使用 SM4 ECB 模式加密数据国密SM4分组密码算法。密钥长度必须为 16 字节。</p>
<b>函数签名:</b>
```go
func Sm4EcbEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/l5IQxYuuaED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef") // 16 bytes key
plaintext := []byte("hello world")
encrypted := cryptor.Sm4EcbEncrypt(plaintext, key)
decrypted := cryptor.Sm4EcbDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4EcbDecrypt">Sm4EcbDecrypt</span>
<p>使用 SM4 ECB 模式解密数据。密钥长度必须为 16 字节。</p>
<b>函数签名:</b>
```go
func Sm4EcbDecrypt(encrypted, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/l5IQxYuuaED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4EcbEncrypt(plaintext, key)
decrypted := cryptor.Sm4EcbDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4CbcEncrypt">Sm4CbcEncrypt</span>
<p>使用 SM4 CBC 模式加密数据。密钥长度必须为 16 字节。返回的密文包含 IV前 16 字节)。</p>
<b>函数签名:</b>
```go
func Sm4CbcEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/65Q6iYhLRTa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4CbcEncrypt(plaintext, key)
decrypted := cryptor.Sm4CbcDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4CbcDecrypt">Sm4CbcDecrypt</span>
<p>使用 SM4 CBC 模式解密数据。密钥长度必须为 16 字节。密文应包含 IV前 16 字节)。</p>
<b>函数签名:</b>
```go
func Sm4CbcDecrypt(encrypted, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/65Q6iYhLRTa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4CbcEncrypt(plaintext, key)
decrypted := cryptor.Sm4CbcDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="GenerateSm2Key">GenerateSm2Key</span>
<p>生成 SM2 密钥对国密SM2椭圆曲线公钥密码算法。SM2 是基于椭圆曲线的非对称加密算法。</p>
<b>函数签名:</b>
```go
func GenerateSm2Key() (*Sm2PrivateKey, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, err := cryptor.GenerateSm2Key()
if err != nil {
return
}
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm2Encrypt">Sm2Encrypt</span>
<p>使用 SM2 公钥加密数据。返回的密文格式为C1(65字节) || C3(32字节) || C2(明文长度)。</p>
<b>函数签名:</b>
```go
func Sm2Encrypt(pub *Sm2PublicKey, plaintext []byte) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, _ := cryptor.GenerateSm2Key()
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm2Decrypt">Sm2Decrypt</span>
<p>使用 SM2 私钥解密数据。密文格式应为C1(65字节) || C3(32字节) || C2(明文长度)。</p>
<b>函数签名:</b>
```go
func Sm2Decrypt(priv *Sm2PrivateKey, ciphertext []byte) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, _ := cryptor.GenerateSm2Key()
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```

View File

@@ -1,6 +1,6 @@
# Cryptor # Cryptor
Package cryptor contains some functions for data encryption and decryption. Support base64, md5, hmac, aes, des, rsa. Package cryptor contains some functions for data encryption and decryption. Support base64, md5, hmac, aes, des, rsa, sm2, sm3, sm4.
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -76,6 +76,14 @@ import (
- [RsaDecryptOAEP](#RsaDecryptOAEP) - [RsaDecryptOAEP](#RsaDecryptOAEP)
- [RsaSign](#RsaSign) - [RsaSign](#RsaSign)
- [RsaVerifySign](#RsaVerifySign) - [RsaVerifySign](#RsaVerifySign)
- [Sm3](#Sm3)
- [Sm4EcbEncrypt](#Sm4EcbEncrypt)
- [Sm4EcbDecrypt](#Sm4EcbDecrypt)
- [Sm4CbcEncrypt](#Sm4CbcEncrypt)
- [Sm4CbcDecrypt](#Sm4CbcDecrypt)
- [GenerateSm2Key](#GenerateSm2Key)
- [Sm2Encrypt](#Sm2Encrypt)
- [Sm2Decrypt](#Sm2Decrypt)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1831,3 +1839,278 @@ func main() {
} }
} }
``` ```
### <span id="Sm3">Sm3</span>
<p>Calculate SM3 hash (Chinese National Cryptography SM3 Hash Algorithm). SM3 is a cryptographic hash algorithm published by the Chinese State Cryptography Administration, designed to replace MD5/SHA-1/SHA-2.</p>
<b>Signature:</b>
```go
func Sm3(data []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zDAQpteAiOc)</span></b>
```go
package main
import (
"encoding/hex"
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("hello world")
hash := cryptor.Sm3(data)
fmt.Println(hex.EncodeToString(hash))
// Output:
// 44f0061e69fa6fdfc290c494654a05dc0c053da7e5c52b84ef93a9d67d3fff88
}
```
### <span id="Sm4EcbEncrypt">Sm4EcbEncrypt</span>
<p>Encrypt data using SM4 ECB mode (Chinese National Cryptography SM4 Block Cipher Algorithm). Key length must be 16 bytes.</p>
<b>Signature:</b>
```go
func Sm4EcbEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/l5IQxYuuaED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef") // 16 bytes key
plaintext := []byte("hello world")
encrypted := cryptor.Sm4EcbEncrypt(plaintext, key)
decrypted := cryptor.Sm4EcbDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4EcbDecrypt">Sm4EcbDecrypt</span>
<p>Decrypt data using SM4 ECB mode. Key length must be 16 bytes.</p>
<b>Signature:</b>
```go
func Sm4EcbDecrypt(encrypted, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/l5IQxYuuaED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4EcbEncrypt(plaintext, key)
decrypted := cryptor.Sm4EcbDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4CbcEncrypt">Sm4CbcEncrypt</span>
<p>Encrypt data using SM4 CBC mode. Key length must be 16 bytes. The returned ciphertext contains IV (first 16 bytes).</p>
<b>Signature:</b>
```go
func Sm4CbcEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/65Q6iYhLRTa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4CbcEncrypt(plaintext, key)
decrypted := cryptor.Sm4CbcDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm4CbcDecrypt">Sm4CbcDecrypt</span>
<p>Decrypt data using SM4 CBC mode. Key length must be 16 bytes. The ciphertext should contain IV (first 16 bytes).</p>
<b>Signature:</b>
```go
func Sm4CbcDecrypt(encrypted, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/65Q6iYhLRTa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
key := []byte("1234567890abcdef")
plaintext := []byte("hello world")
encrypted := cryptor.Sm4CbcEncrypt(plaintext, key)
decrypted := cryptor.Sm4CbcDecrypt(encrypted, key)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="GenerateSm2Key">GenerateSm2Key</span>
<p>Generate SM2 key pair (Chinese National Cryptography SM2 Elliptic Curve Public Key Algorithm). SM2 is an asymmetric encryption algorithm based on elliptic curve cryptography.</p>
<b>Signature:</b>
```go
func GenerateSm2Key() (*Sm2PrivateKey, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, err := cryptor.GenerateSm2Key()
if err != nil {
return
}
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm2Encrypt">Sm2Encrypt</span>
<p>Encrypt data using SM2 public key. The returned ciphertext format is: C1(65 bytes) || C3(32 bytes) || C2(plaintext length).</p>
<b>Signature:</b>
```go
func Sm2Encrypt(pub *Sm2PublicKey, plaintext []byte) ([]byte, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, _ := cryptor.GenerateSm2Key()
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```
### <span id="Sm2Decrypt">Sm2Decrypt</span>
<p>Decrypt data using SM2 private key. The ciphertext format should be: C1(65 bytes) || C3(32 bytes) || C2(plaintext length).</p>
<b>Signature:</b>
```go
func Sm2Decrypt(priv *Sm2PrivateKey, ciphertext []byte) ([]byte, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/bKYMqRLvIx3)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
privateKey, _ := cryptor.GenerateSm2Key()
plaintext := []byte("hello world")
ciphertext, _ := cryptor.Sm2Encrypt(&privateKey.PublicKey, plaintext)
decrypted, _ := cryptor.Sm2Decrypt(privateKey, ciphertext)
fmt.Println(string(decrypted))
// Output:
// hello world
}
```