gmsm

国密算法库的 MoonBit 移植

crypto
sm2
sm3
sm4
gm
国密
moon add PaiGack/gmsm@0.1.2
Download zip
Author
Version
0.1.2
License
Apache-2.0
Last updated
18 days ago
Downloads
6
README

#PaiGack/gmsm

国密算法库的 MoonBit 移植。

本项目把 Go 生态里的国密算法库 tjfoc/gmsm(Apache-2.0)按 GM/T 0003-2012、GM/T 0004-2012、GM/T 0002-2012 重新实现到 MoonBit 生态。

#主要功能

#SM2

  • GenerateKey 生成密钥对,PublicKey / PrivateKey 推导公钥、点是否在曲线;
  • Sm2Sign / Sm2Verify 数字签名(含 Z 值预处理 entl ‖ uid ‖ a ‖ b ‖ Gx ‖ Gy ‖ x ‖ y 与 SM3 摘要);
  • Encrypt / Decrypt 加解密,支持 C1C3C2 与 C1C2C3 密文排布,对应 CipherMarshal / CipherUnmarshal
  • KeyExchangeA / KeyExchangeB 密钥协商,Compress / Decompress 公钥压缩 / 解压;
  • ASN.1 签名格式 SignDigitToSignData / SignDataToSignDigit

#SM3

  • Sm3Sum / Sm3SumMulti 一次性摘要;
  • 流式 SM3new / write / sum / reset / size),输出 32 字节,对齐 Go hash.Hash

#SM4

  • NewCipher 块加密,对齐 Go cipher.Block
  • Sm4Ecb / Sm4Cbc / Sm4Cfb / Sm4Ofb 各模式封装,附带 PKCS7 填充与 SetIV
  • Sm4Gcm 对应 Sm4GCM / GCMEncrypt / GCMDecrypt

#快速上手

项目在 cmd/ 下提供了三组命令行示例:

  • moon run cmd/sm2 —— 密钥生成、签名/验签、DER 签名、加密/解密(C1C2C3 与 ASN.1)、 公钥压缩/解压、密钥协商;
  • moon run cmd/sm3 —— 一次性摘要与流式摘要,并对照 GM/T 0004-2012 标准测试向量校验;
  • moon run cmd/sm4 —— 单块加解密(对照 GM/T 0002-2012 附录 A 测试向量)、ECB/CBC 模式 及 PKCS7 填充的往返验证。

以下示例为文档测试,可通过 moon test 运行:

#SM3 摘要

///|
test "sm3 digest" {
// 一次性摘要
let digest = @sm3.sm3_sum(b"abc")
assert_eq(
@byteutil.bytes_to_hex(digest),
"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0",
)
// 多段拼接摘要,等价于把各段连接后再计算
assert_eq(
@byteutil.bytes_to_hex(@sm3.sm3_sum_multi([b"a", b"b", b"c"])),
@byteutil.bytes_to_hex(digest),
)
// 流式接口,对齐 Go 的 hash.Hash(write 返回新的上下文,链式调用)
let h = @sm3.new().write(b"a").write(b"bc")
assert_eq(h.size(), 32)
assert_eq(h.block_size(), 64)
assert_eq(@byteutil.bytes_to_hex(h.sum()), @byteutil.bytes_to_hex(digest))
// reset 之后可以复用
assert_eq(
@byteutil.bytes_to_hex(h.reset().write(b"abc").sum()),
@byteutil.bytes_to_hex(digest),
)
}

#SM4 分组加密

///|
test "sm4 block and modes" {
let key = @byteutil.hex_to_bytes("0123456789abcdeffedcba9876543210")
// GM/T 0002-2012 附录 A 标准向量
let block = @sm4.new_cipher(key)
assert_eq(block.block_size(), 16)
let ct = block.encrypt(key)
assert_eq(@byteutil.bytes_to_hex(ct), "681edf34d206965e86b3e94f536e4246")
assert_eq(
@byteutil.bytes_to_hex(block.decrypt(ct)),
@byteutil.bytes_to_hex(key),
)

// ECB / CBC 模式 + PKCS7 填充
let msg = b"hello sm4, moonbit!"
let iv = @byteutil.hex_to_bytes("000102030405060708090a0b0c0d0e0f")
let padded = @byteutil.pkcs7_pad(msg, 16)
let ecb = @sm4.sm4_encrypt_ecb(key, padded)
let ecb_plain = match @byteutil.pkcs7_unpad(@sm4.sm4_decrypt_ecb(key, ecb)) {
Ok(p) => p
Err(_) => abort("unpad failed")
}
assert_true(@byteutil.bytes_eq(ecb_plain, msg))
let cbc = @sm4.sm4_encrypt_cbc(key, iv, padded)
let cbc_plain = match
@byteutil.pkcs7_unpad(@sm4.sm4_decrypt_cbc(key, iv, cbc)) {
Ok(p) => p
Err(_) => abort("unpad failed")
}
assert_true(@byteutil.bytes_eq(cbc_plain, msg))
// CFB / OFB(128 位反馈,同样按整块处理)
let cfb = @sm4.sm4_encrypt_cfb(key, iv, padded)
let cfb_plain = match
@byteutil.pkcs7_unpad(@sm4.sm4_decrypt_cfb(key, iv, cfb)) {
Ok(p) => p
Err(_) => abort("unpad failed")
}
assert_true(@byteutil.bytes_eq(cfb_plain, msg))
let ofb = @sm4.sm4_encrypt_ofb(key, iv, padded)
let ofb_plain = match
@byteutil.pkcs7_unpad(@sm4.sm4_decrypt_ofb(key, iv, ofb)) {
Ok(p) => p
Err(_) => abort("unpad failed")
}
assert_true(@byteutil.bytes_eq(ofb_plain, msg))
// GCM 认证加密
let nonce = @byteutil.hex_to_bytes("000102030405060708090a0b")
let aad = b"header"
let (gcm_ct, tag) = @sm4.sm4_gcm_encrypt(key, nonce, msg, aad)
let (plain, tag2) = @sm4.sm4_gcm_decrypt(key, nonce, gcm_ct, aad)
assert_true(@byteutil.bytes_eq(plain, msg) && @byteutil.bytes_eq(tag, tag2))
}

#SM2 签名 / 加密

///|
test "sm2 sign and encrypt" {
let sk = @sm2.generate_key(None)
let pub_key = @sm2.derive_public_key(sk)
let uid = @sm2.default_uid()
let msg = b"hello sm2"

// 裸 (r, s) 签名与 ASN.1 DER 签名
let (r, s) = @sm2.sm2_sign(sk, msg, uid, None)
assert_true(@sm2.sm2_verify(pub_key, msg, uid, r, s))
let der = @sm2.sign_digit_to_sign_data(r, s)
let (r2, s2) = @sm2.sign_data_to_sign_digit(der)
assert_true(@byteutil.bytes_eq(r, r2) && @byteutil.bytes_eq(s, s2))
assert_true(
@sm2.sm2_verify_der(
pub_key,
msg,
uid,
@sm2.sm2_sign_der(sk, msg, uid, None),
),
)

// C1C3C2 / C1C2C3 两种密文排布与 ASN.1 密文
let ct = @sm2.sm2_encrypt(pub_key, msg, None, @sm2.cipher_mode_c1c3c2())
assert_true(
@byteutil.bytes_eq(@sm2.sm2_decrypt(sk, ct, @sm2.cipher_mode_c1c3c2()), msg),
)
assert_true(
@byteutil.bytes_eq(
@sm2.sm2_decrypt_asn1(sk, @sm2.sm2_encrypt_asn1(pub_key, msg, None)),
msg,
),
)

// 公钥压缩 / 解压
let compressed = @sm2.compress_point(pub_key)
assert_eq(compressed.length(), 33)
assert_true(@sm2.is_on_curve(@sm2.decompress_point(compressed)))
}

#SM2 密钥协商

///|
test "sm2 key exchange" {
let (priv_a, rpriv_a) = (@sm2.generate_key(None), @sm2.generate_key(None))
let (priv_b, rpriv_b) = (@sm2.generate_key(None), @sm2.generate_key(None))
let pub_a = @sm2.derive_public_key(priv_a)
let pub_b = @sm2.derive_public_key(priv_b)
let rpub_a = @sm2.derive_public_key(rpriv_a)
let rpub_b = @sm2.derive_public_key(rpriv_b)
let ida = b"1234567812345678"
let idb = b"1234567812345678"
let (ka, s1a, s2a) = @sm2.key_exchange_a(
16, ida, idb, priv_a, pub_b, rpriv_a, rpub_b,
)
let (kb, s1b, s2b) = @sm2.key_exchange_b(
16, ida, idb, priv_b, pub_a, rpriv_b, rpub_a,
)
assert_true(@byteutil.bytes_eq(ka, kb)) // 协商出相同的 16 字节会话密钥
assert_true(@byteutil.bytes_eq(s1a, s1b) && @byteutil.bytes_eq(s2a, s2b)) // 密钥确认值一致
}

#构建与测试

moon check # 类型 / 语法检查 moon test # 运行测试 moon build # 编译 moon run cmd/sm2 # 运行 sm2 example 入口程序 moon run cmd/sm3 # 运行 sm3 example 入口程序 moon run cmd/sm4 # 运行 sm4 example 入口程序

#许可证

本项目基于 tjfoc/gmsm 移植,遵循 Apache License 2.0,与原项目许可证一致。详见 LICENSE

Source Files