From f599b7a0b329308e4057bd646d825a86764fbc73 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 14 Sep 2023 14:24:28 +0800 Subject: [PATCH 01/18] Update API and source dir --- gmssl.py | 480 +++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 19 ++ src/GmSSL.py | 398 ------------------------------------------ test.py | 152 ++++++++++++++++ 4 files changed, 651 insertions(+), 398 deletions(-) create mode 100755 gmssl.py create mode 100644 setup.py delete mode 100755 src/GmSSL.py create mode 100644 test.py diff --git a/gmssl.py b/gmssl.py new file mode 100755 index 0000000..4bfc483 --- /dev/null +++ b/gmssl.py @@ -0,0 +1,480 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# pyGmSSL - the Python binding of the GmSSL library + + +from ctypes import * +from ctypes.util import find_library + +libgmssl = find_library("gmssl") + +gmssl = cdll.LoadLibrary(libgmssl) +libc = cdll.LoadLibrary(find_library('c')) + + +GMSSL_PYTHON_VERSION = "2.0" + + +def gmssl_version_num(): + return gmssl.gmssl_version_num() + + +def gmssl_version_str(): + gmssl.gmssl_version_str.restype = c_char_p + p = gmssl.gmssl_version_str() + return p.decode('ascii') + +GMSSL_LIBRARY_VERSION = gmssl_version_str() + + +def rand_bytes(size): + buf = create_string_buffer(size) + gmssl.rand_bytes(buf, c_size_t(size)) + return buf.raw + + +class InnerError(Exception): + ''' + GmSSL libraray inner error + ''' + +SM3_DIGEST_SIZE = 32 + +class Sm3(Structure): + + SM3_STATE_WORDS = 8 + SM3_BLOCK_SIZE = 64 + + _fields_ = [ + ("dgst", c_uint32 * SM3_STATE_WORDS), + ("nblocks", c_uint64), + ("block", c_uint8 * SM3_BLOCK_SIZE), + ("num", c_size_t) + ] + + def __init__(self): + gmssl.sm3_init(byref(self)) + + def reset(self): + gmssl.sm3_init(byref(self)) + + def update(self, data): + gmssl.sm3_update(byref(self), data, c_size_t(len(data))) + + def digest(self): + dgst = create_string_buffer(SM3_DIGEST_SIZE) + gmssl.sm3_finish(byref(self), dgst) + return dgst.raw + + +SM3_HMAC_SIZE = SM3_DIGEST_SIZE +SM3_HMAC_MIN_KEY_SIZE = 16 +SM3_HMAC_MAX_KEY_SIZE = 64 + +class Sm3Hmac(Structure): + + _fields_ = [ + ("sm3_ctx", Sm3), + ("key", c_uint8 * Sm3.SM3_BLOCK_SIZE) + ] + + def __init__(self, key): + if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: + raise ValueError('Invalid SM3 HMAC key length') + gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) + + def reset(self, key): + if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: + raise ValueError('Invalid SM3 HMAC key length') + gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) + + def update(self, data): + gmssl.sm3_hmac_update(byref(self), data, c_size_t(len(data))) + + def generateMac(self): + hmac = create_string_buffer(SM3_HMAC_SIZE) + gmssl.sm3_hmac_finish(byref(self), hmac) + return hmac.raw + + +SM4_KEY_SIZE = 16 +SM4_BLOCK_SIZE = 16 + + + +class Sm4(Structure): + + SM4_NUM_ROUNDS = 32 + + _fields_ = [("rk", c_uint32 * SM4_NUM_ROUNDS)] + + def __init__(self, key, encrypt): + if len(key) != SM4_KEY_SIZE: + raise ValueError('Invalid key length') + if encrypt: + gmssl.sm4_set_encrypt_key(byref(self), key) + else: + gmssl.sm4_set_decrypt_key(byref(self), key) + + def encrypt(self, block): + if len(block) != SM4_BLOCK_SIZE: + raise ValueError('Invalid block size') + outbuf = create_string_buffer(SM4_BLOCK_SIZE) + gmssl.sm4_encrypt(byref(self), block, outbuf) + return outbuf.raw + + +class Sm4Cbc(Structure): + + _fields_ = [ + ("sm4_key", Sm4), + ("iv", c_uint8 * SM4_BLOCK_SIZE), + ("block", c_uint8 * SM4_BLOCK_SIZE), + ("block_nbytes", c_size_t) + ] + + def __init__(self, key, iv, encrypt): + if len(key) != SM4_KEY_SIZE: + raise ValueError('Invalid key length') + if len(iv) != SM4_BLOCK_SIZE: + raise ValueError('Invalid IV size') + if encrypt == True: + self._encrypt = True + if gmssl.sm4_cbc_encrypt_init(byref(self), key, iv) != 1: + raise InnerError('libgmssl inner error') + else: + self._encrypt = False + if gmssl.sm4_cbc_decrypt_init(byref(self), key, iv) != 1: + raise InnerError('libgmssl inner error') + + def update(self, data): + outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) + outlen = c_size_t() + if self._encrypt == True: + if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[0:outlen.value] + + def finish(self): + outbuf = create_string_buffer(SM4_BLOCK_SIZE) + outlen = c_size_t() + if self._encrypt == True: + if gmssl.sm4_cbc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm4_cbc_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + + +SM4_CTR_IV_SIZE = 16 + + +class Sm4Ctr(Structure): + + _fields_ = [ + ("sm4_key", Sm4), + ("ctr", c_uint8 * SM4_BLOCK_SIZE), + ("block", c_uint8 * SM4_BLOCK_SIZE), + ("block_nbytes", c_size_t) + ] + + def __init__(self, key, iv): + if len(key) != SM4_KEY_SIZE: + raise ValueError('Invalid key length') + if len(iv) != SM4_BLOCK_SIZE: + raise ValueError('Invalid IV size') + if gmssl.sm4_ctr_encrypt_init(byref(self), key, iv) != 1: + raise InnerError('libgmssl inner error') + + def update(self, data): + outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) + outlen = c_size_t() + if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[0:outlen.value] + + def finish(self): + outbuf = create_string_buffer(SM4_BLOCK_SIZE) + outlen = c_size_t() + if gmssl.sm4_ctr_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + +ZUC_KEY_SIZE = 16 +ZUC_IV_SIZE = 16 + +class ZucState(Structure): + _fields_ = [ + ("LFSR", c_uint32 * 16), + ("R1", c_uint32), + ("R2", c_uint32) + ] + +class Zuc(Structure): + + _fields_ = [ + ("zuc_state", ZucState), + ("block", c_uint8 * 4), + ("block_nbytes", c_size_t) + ] + + def __init__(self, key, iv): + if len(key) != ZUC_KEY_SIZE: + raise ValueError('Invalid key length') + if len(iv) != ZUC_IV_SIZE: + raise ValueError('Invalid IV size') + if gmssl.zuc_encrypt_init(byref(self), key, iv) != 1: + raise InnerError('libgmssl inner error') + + def update(self, data): + outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) + outlen = c_size_t() + if gmssl.zuc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[0:outlen.value] + + def finish(self): + outbuf = create_string_buffer(SM4_BLOCK_SIZE) + outlen = c_size_t() + if gmssl.zuc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + +class gf128_t(Structure): + _fields_ = [ + ("hi", c_uint64), + ("lo", c_uint64) + ] + + +class Ghash(Structure): + _fields_ = [ + ("H", gf128_t), + ("X", gf128_t), + ("aadlen", c_size_t), + ("clen", c_size_t), + ("block", c_uint8 * 16), + ("num", c_size_t) + ] + + +SM4_GCM_MIN_IV_SIZE = 1 +SM4_GCM_MAX_IV_SIZE = 64 +SM4_GCM_DEFAULT_IV_SIZE = 12 +SM4_GCM_DEFAULT_TAG_SIZE = 16 +SM4_GCM_MAX_TAG_SIZE = 16 + +class Sm4Gcm(Structure): + + _fields_ = [ + ("sm4_ctr_ctx", Sm4Ctr), + ("mac_ctx", Ghash), + ("Y", c_uint8 * 16), + ("taglen", c_size_t), + ("mac", c_uint8 * 16), + ("maclen", c_size_t) + ] + + def __init__(self, key, iv, aad, taglen, encrypt): + if len(key) != SM4_KEY_SIZE: + raise ValueError('Invalid key length') + if len(iv) < SM4_GCM_MIN_IV_SIZE or len(iv) > SM4_GCM_MAX_IV_SIZE: + raise ValueError('Invalid IV size') + if taglen < 1 or taglen > SM4_GCM_MAX_TAG_SIZE: + raise ValueError('Invalid Tag length') + if encrypt == True: + ok = gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), + iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) + else: + ok = gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), + iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) + if ok != 1: + raise InnerError('libgmssl inner error') + self._encrypt = encrypt + + + def update(self, data): + outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) + outlen = c_size_t() + if self._encrypt == True: + if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[0:outlen.value] + + def finish(self): + outbuf = create_string_buffer(SM4_BLOCK_SIZE + SM4_GCM_MAX_TAG_SIZE) + outlen = c_size_t() + if self._encrypt == True: + if gmssl.sm4_gcm_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm4_gcm_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + +SM2_DEFAULT_ID = b'1234567812345678' + +SM2_MAX_SIGNATURE_SIZE = 72 +SM2_MIN_PLAINTEXT_SIZE = 1 +SM2_MAX_PLAINTEXT_SIZE = 255 +SM2_MIN_CIPHERTEXT_SIZE = 45 +SM2_MAX_CIPHERTEXT_SIZE = 366 + + +class Sm2Point(Structure): + _fields_ = [ + ("x", c_uint8 * 32), + ("y", c_uint8 * 32) + ] + + +class Sm2Key(Structure): + + _fields_ = [ + ("public_key", Sm2Point), + ("private_key", c_uint8 * 32) + ] + + def generate_key(self): + if gmssl.sm2_key_generate(byref(self)) != 1: + raise InnerError('libgmssl inner error') + + def compute_z(self, signer_id): + z = create_string_buffer(SM3_DIGEST_SIZE) + gmssl.sm2_compute_z(z, byref(self), signer_id, c_size_t(len(signer_id))) + return z.raw + + def export_encrypted_private_key_info_pem(self, file, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(file.encode('utf-8'), 'wb') + if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), + passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def import_encrypted_private_key_info_pem(self, file, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(file.encode('utf-8'), 'rb') + if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def export_public_key_info_pem(self, file): + libc.fopen.restype = c_void_p + fp = libc.fopen(file.encode('utf-8'), 'wb') + if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def import_public_key_info_pem(self, file): + libc.fopen.restype = c_void_p + fp = libc.fopen(file.encode('utf-8'), 'rb') + if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def sign(self, dgst): + if len(dgst) != SM3_DIGEST_SIZE: + raise ValueError('Invalid SM3 digest size') + sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) + siglen = c_size_t() + if gmssl.sm2_sign(byref(self), dgst, sig, byref(siglen)) != 1: + raise InnerError('libgmssl inner error') + return sig[:siglen.value] + + def verify(self, dgst, sig): + if len(dgst) != SM3_DIGEST_SIZE: + raise ValueError('Invalid SM3 digest size') + ret = gmssl.sm2_verify(byref(self), dgst, sig, c_size_t(len(sig))) + if ret < 0: + raise InnerError('libgmssl inner error') + if ret == 0: + return False + return True + + def encrypt(self, data): + outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) + outlen = c_size_t() + if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + def decrypt(self, ciphertext): + outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) + outlen = c_size_t() + if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(ciphertext)), outbuf, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return outbuf[:outlen.value] + + +class Sm2Signature(Structure): + + _fields_ = [ + ("sm3_ctx", Sm3), + ("key", Sm2Key) + ] + + def __init__(self, sm2_key, signer_id, sign): + if sign == True: + self._sign = True + if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id, c_size_t(len(signer_id))) != 1: + raise InnerError('libgmssl inner error') + else: + self._sign = False + if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id, c_size_t(len(signer_id))) != 1: + raise InnerError('libgmssl inner error') + + + def update(self, data): + if self._sign == True: + if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: + raise InnerError('libgmssl inner error') + + def sign(self): + sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) + siglen = c_size_t() + if gmssl.sm2_sign_finish(byref(self), sig, byref(siglen)) != 1: + raise InnerError('libgmssl inner error') + return sig[:siglen.value] + + def verify(self, sig): + ret = gmssl.sm2_verify_finish(byref(self), sig, c_size_t(len(sig))) + if ret < 0: + raise InnerError('libgmssl inner error') + if ret == 0: + return False + return True + + + + + + + + + + + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..f1ab044 --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# pyGmSSL - the Python binding of the GmSSL library + + +from distutils.core import setup + +setup( + name='GmSSL-Python', + version='1.0', + py_modules=['gmssl'], +) diff --git a/src/GmSSL.py b/src/GmSSL.py deleted file mode 100755 index 145781a..0000000 --- a/src/GmSSL.py +++ /dev/null @@ -1,398 +0,0 @@ -# Copyright 2023 The GmSSL Project. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the License); you may -# not use this file except in compliance with the License. -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# pyGmSSL - the Python binding of the GmSSL library - - -from ctypes import * -from ctypes.util import find_library - -libgmssl = find_library("gmssl") - -gmssl = cdll.LoadLibrary(libgmssl) - - -def gmssl_version_num(): - return gmssl.gmssl_version_num() - - -def gmssl_version_str(): - return create_string_buffer(gmssl.gmssl_version_str()).value - -def rand_bytes(size): - buf = create_string_buffer(size) - gmssl.rand_bytes(buf, c_size_t(size)) - return buf.raw - - -class InnerError(Exception): - ''' - GmSSL libraray inner error - ''' - -SM3_DIGEST_SIZE = 32 -SM3_BLOCK_SIZE = 64 - -class SM3_CTX(Structure): - - SM3_STATE_WORDS = 8 - - _fields_ = [ - ("digest", c_uint32 * SM3_STATE_WORDS), - ("nblocks", c_uint64), - ("block", c_uint8 * SM3_BLOCK_SIZE), - ("num", c_size_t) - ] - - def __init__(self): - gmssl.sm3_init(byref(self)) - - def update(self, data): - gmssl.sm3_update(byref(self), data, c_size_t(len(data))) - - def finish(self): - dgst = create_string_buffer(SM3_DIGEST_SIZE) - gmssl.sm3_finish(byref(self), dgst) - return dgst.raw - - -SM3_HMAC_SIZE = SM3_DIGEST_SIZE - -class SM3_HMAC_CTX(Structure): - - _fields_ = [ - ("sm3_ctx", SM3_CTX), - ("key", c_uint8 * SM3_BLOCK_SIZE) - ] - - def __init__(self, key): - if len(key) < 1 or len(key) > 64: - raise ValueError('Invalid SM3 HMAC key length') - gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) - - def update(self, data): - gmssl.sm3_hmac_update(byref(self), data, c_size_t(len(data))) - - def finish(self): - hmac = create_string_buffer(SM3_HMAC_SIZE) - gmssl.sm3_hmac_finish(byref(self), hmac) - return hmac.raw - - -SM4_KEY_SIZE = 16 -SM4_BLOCK_SIZE = 16 - -class SM4_KEY(Structure): - - SM4_NUM_ROUNDS = 32 - - _fields_ = [("rk", c_uint32 * SM4_NUM_ROUNDS)] - - def set_encrypt_key(self, key): - if len(key) != self.KEY_SIZE: - raise ValueError('Invalid key length') - gmssl.sm4_set_encrypt_key(byref(self), key) - - def set_decrypt_key(self, key): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - gmssl.sm4_set_decrypt_key(byref(self), key) - - def encrypt(self, block): - if len(block) != SM4_BLOCK_SIZE: - raise ValueError('Invalid block size') - outbuf = create_string_buffer(SM4_BLOCK_SIZE) - gmssl.sm4_encrypt(byref(self), block, outbuf) - return outbuf.raw - - def decrypt(self, block): - return self.encrypt(block) - - -class SM4_CBC_CTX(Structure): - - _fields_ = [ - ("sm4_key", SM4_KEY), - ("iv", c_uint8 * SM4_BLOCK_SIZE), - ("block", c_uint8 * SM4_BLOCK_SIZE), - ("block_nbytes", c_size_t) - ] - - def encrypt_init(self, key, iv): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - if len(iv) != SM4_BLOCK_SIZE: - raise ValueError('Invalid IV size') - if gmssl.sm4_cbc_encrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') - - def encrypt_update(self, data): - outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[0:outlen].raw - - def encrypt_finish(self): - outbuf = create_string_buffer(SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_cbc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - def decrypt_init(self, key, iv): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - if len(iv) != SM4_BLOCK_SIZE: - raise ValueError('Invalid IV size') - if gmssl.sm4_cbc_decrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') - - def decrypt_update(self, data): - outbuf = create_string_buffer(len(data) + 16) - outlen = c_size_t() - if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[0:outlen].raw - - def decrypt_finish(self): - outbuf = create_string_buffer(16) - outlen = c_size_t() - if gmssl.sm4_cbc_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('decryption failure') - return outbuf[:outlen].raw - - -class SM4_CTR_CTX(Structure): - - _fields_ = [ - ("sm4_key", SM4_KEY), - ("ctr", c_uint8 * SM4_BLOCK_SIZE), - ("block", c_uint8 * SM4_BLOCK_SIZE), - ("block_nbytes", c_size_t) - ] - - def encrypt_init(self, key, iv): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - if len(iv) != SM4_BLOCK_SIZE: - raise ValueError('Invalid IV size') - if gmssl.sm4_ctr_encrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') - - def encrypt_update(self, data): - outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[0:outlen].raw - - def encrypt_finish(self): - outbuf = create_string_buffer(SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_ctr_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - def decrypt_init(self, key, iv): - return self.encrypt_init(self, key, iv) - - def decrypt_update(self, data): - return self.encrypt_update(self, data) - - def decrypt_finish(self): - return self.encrypt_finish(self) - - -class gf128_t(Structure): - _fields_ = [ - ("hi", c_uint64), - ("lo", c_uint64) - ] - - -class GHASH_CTX(Structure): - _fields_ = [ - ("H", gf128_t), - ("X", gf128_t), - ("aadlen", c_size_t), - ("clen", c_size_t), - ("block", c_uint8 * 16), - ("num", c_size_t) - ] - - -SM4_GCM_MIN_IV_SIZE = 1 -SM4_GCM_MAX_IV_SIZE = 64 -SM4_GCM_DEFAULT_IV_SIZE = 12 -SM4_GCM_DEFAULT_TAG_SIZE = 16 -SM4_GCM_MAX_TAG_SIZE = 16 - -class SM4_GCM_CTX(Structure): - - _fields_ = [ - ("sm4_ctr_ctx", SM4_CTR_CTX), - ("mac_ctx", GHASH_CTX), - ("Y", c_uint8 * 16), - ("taglen", c_size_t), - ("mac", c_uint8 * 16), - ("maclen", c_size_t) - ] - - def encrypt_init(self, key, iv, aad, taglen): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - if len(iv) < SM4_GCM_MIN_IV_SIZE or len(iv) > SM4_GCM_MAX_IV_SIZE: - raise ValueError('Invalid IV size') - if taglen < 1 or tag_len > SM4_GCM_MAX_TAG_SIZE: - raise ValueError('Invalid Tag length') - if gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) != 1: - raise InnerError('libgmssl inner error') - - def encrypt_update(self, data): - outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[0:outlen].raw - - def encrypt_finish(self): - outbuf = create_string_buffer(SM4.BLOCK_SIZE + SM4_GCM_MAX_TAG_SIZE) - outlen = c_size_t() - if gmssl.sm4_gcm_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - def decrypt_init(self, key, iv, aad, taglen): - if len(key) != SM4_KEY_SIZE: - raise ValueError('Invalid key length') - if len(iv) < SM4_GCM_MIN_IV_SIZE or len(iv) > SM4_GCM_MAX_IV_SIZE: - raise ValueError('Invalid IV size') - if taglen < 1 or tag_len > SM4_GCM_MAX_TAG_SIZE: - raise ValueError('Invalid Tag length') - if gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) != 1: - raise InnerError('libgmssl inner error') - - def decrypt_update(self, data): - outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) - outlen = c_size_t() - if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[0:outlen].raw - - def decrypt_finish(self): - outbuf = create_string_buffer(SM4.BLOCK_SIZE + SM4_GCM_MAX_TAG_SIZE) - outlen = c_size_t() - if gmssl.sm4_gcm_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - - -SM2_DEFAULT_ID = b'1234567812345678' - -SM2_MAX_SIGNATURE_SIZE = 72 -SM2_MIN_PLAINTEXT_SIZE = 1 -SM2_MAX_PLAINTEXT_SIZE = 255 -SM2_MIN_CIPHERTEXT_SIZE = 45 -SM2_MAX_CIPHERTEXT_SIZE = 366 - - -class SM2_POINT(Structure): - _fields_ = [ - ("x", c_uint8 * 32), - ("y", c_uint8 * 32) - ] - - -class SM2_KEY(Structure): - - _fields_ = [ - ("public_key", SM2_POINT), - ("private_key", c_uint8 * 32) - ] - - def generate(self): - if gmssl.sm2_key_generate(byref(self)) != 1: - raise InnerError('libgmssl inner error') - - def private_key_info_encrypt_to_pem(self, passwd, file): - return "hello" - - def sign(self, dgst): - if len(data) != SM3.DIGEST_SIZE: - raise ValueError('Invalid SM3 digest size') - sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) - siglen = c_size_t() - if gmssl.sm2_sign(byref(self), dgst, sig, byref(siglen)) != 1: - raise InnerError('libgmssl inner error') - return sig[:siglen].raw - - def verify(self, dgst, sig): - if len(data) != SM3.DIGEST_SIZE: - raise ValueError('Invalid SM3 digest size') - ret = gmssl.sm2_verify(byref(self), dgst, sig, c_size_t(len(sig))) - if ret < 0: - raise InnerError('libgmssl inner error') - if ret == 0: - return False - return True - - def encrypt(self, data): - outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) - outlen = c_size_t() - if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - def decrypt(self, ciphertext): - outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) - outlen = c_size_t() - if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') - return outbuf[:outlen].raw - - -class SM2_SIGN_CTX(Structure): - - _fields_ = [ - ("sm3_ctx", SM3_CTX), - ("key", SM2_KEY) - ] - - def sign_init(self, sign_key, signer_id): - if gmssl.sm2_sign_init(byref(self), byref(sign_key), signer_id, c_size_t(len(signer_id))) != 1: - raise InnerError('libgmssl inner error') - - def sign_update(self, data): - if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') - - def sign_finish(self): - sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) - siglen = c_size_t() - if gmssl.sm2_sign_finish(byref(self), sig, byref(siglen)) != 1: - raise InnerError('libgmssl inner error') - return sig[:siglen].raw - - def verify_init(self, pub_key, signer_id): - if gmssl.sm2_verify_init(byref(self), byref(pub_key), signer_id, c_size_t(len(signer_id))) != 1: - raise InnerError('libgmssl inner error') - - def verify_update(self, data): - if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') - - def verify_finish(self, sig): - ret = gmssl.sm2_verify_finish(byref(self), sig, c_size_t(len(sig))) - if ret < 0: - raise InnerError('libgmssl inner error') - if ret == 0: - return False - return True - - diff --git a/test.py b/test.py new file mode 100644 index 0000000..6d0cab4 --- /dev/null +++ b/test.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# pyGmSSL - the Python binding of the GmSSL library + + + +import unittest +from gmssl import * + +class TestGmSSL(unittest.TestCase): + + def test_rand(self): + keylen = 20 + key = rand_bytes(keylen) + self.assertEqual(len(key), keylen) + #print("version") + #print(GMSSL_LIBRARY_VERSION) + + def test_sm3(self): + dgst_hex = '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0' + sm3 = Sm3() + sm3.update(b'abc') + dgst = sm3.digest() + #print(dgst.hex()) + self.assertEqual(dgst, bytes.fromhex(dgst_hex)) + + def test_sm3_hmac(self): + key = b'1234567812345678' + mac_hex = '0a69401a75c5d471f5166465eec89e6a65198ae885c1fdc061556254d91c1080' + sm3_hmac = Sm3Hmac(key) + sm3_hmac.update(b'abc') + mac = sm3_hmac.generateMac() + self.assertEqual(mac, bytes.fromhex(mac_hex)) + + def test_sm4(self): + key = b'1234567812345678' + plaintext = b'block of message' + sm4 = Sm4(key, True) + ciphertext = sm4.encrypt(plaintext) + sm4 = Sm4(key, False) + decrypted = sm4.encrypt(ciphertext) + self.assertEqual(decrypted, plaintext) + + def test_sm4_cbc(self): + key = b'1234567812345678' + iv = b'0000000000000000' + plaintext = b'abc' + sm4_cbc = Sm4Cbc(key, iv, True) + ciphertext = sm4_cbc.update(plaintext) + ciphertext += sm4_cbc.finish() + sm4_cbc = Sm4Cbc(key, iv, False) + decrypted = sm4_cbc.update(ciphertext) + decrypted += sm4_cbc.finish() + self.assertEqual(decrypted, plaintext) + + def test_sm4_ctr(self): + key = b'1234567812345678' + iv = b'0000000000000000' + plaintext = b'abc' + sm4_ctr = Sm4Ctr(key, iv) + ciphertext = sm4_ctr.update(plaintext) + ciphertext += sm4_ctr.finish() + + sm4_ctr = Sm4Ctr(key, iv) + decrypted = sm4_ctr.update(ciphertext) + decrypted += sm4_ctr.finish() + self.assertEqual(decrypted, plaintext) + + def test_zuc(self): + key = b'1234567812345678' + iv = b'0000000000000000' + plaintext = b'abc' + zuc = Zuc(key, iv) + ciphertext = zuc.update(plaintext) + ciphertext += zuc.finish() + + zuc = Zuc(key, iv) + decrypted = zuc.update(ciphertext) + decrypted += zuc.finish() + self.assertEqual(decrypted, plaintext) + + def test_sm4_gcm(self): + key = b'1234567812345678' + iv = b'0000000000000000' + aad = b'AAD data' + taglen = 16 + plaintext = b'abc' + sm4_gcm = Sm4Gcm(key, iv, aad, taglen, True) + ciphertext = sm4_gcm.update(plaintext) + ciphertext += sm4_gcm.finish() + + sm4_gcm = Sm4Gcm(key, iv, aad, taglen, False) + decrypted = sm4_gcm.update(ciphertext) + decrypted += sm4_gcm.finish() + self.assertEqual(decrypted, plaintext) + + def test_sm2_key(self): + sm3 = Sm3() + sm3.update(b'abc') + dgst = sm3.digest() + + sm2 = Sm2Key() + sm2.generate_key() + + sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') + + sm2pri = Sm2Key() + sm2pri.import_encrypted_private_key_info_pem('sm2.pem', 'password') + + sm2.export_public_key_info_pem('sm2pub.pem') + + sm2pub = Sm2Key() + sm2pub.import_public_key_info_pem("sm2pub.pem"); + + z = sm2.compute_z('1234567812345678') + #print(z.hex()) + + sig = sm2pri.sign(dgst) + verify_ret = sm2pub.verify(dgst, sig) + self.assertTrue(verify_ret) + + plaintext = b'abc' + ciphertext = sm2pub.encrypt(plaintext) + decrypted = sm2pri.decrypt(ciphertext) + self.assertEqual(decrypted, plaintext) + + def test_sm2_sig(self): + sm2 = Sm2Key() + sm2.generate_key() + + sign = Sm2Signature(sm2, SM2_DEFAULT_ID, True) + sign.update(b'abc') + sig = sign.sign() + + verify = Sm2Signature(sm2, SM2_DEFAULT_ID, False) + verify.update(b'abc') + verify_ret = verify.verify(sig) + self.assertTrue(verify_ret) + + +if __name__ == '__main__': + unittest.main() + + + From 4d0d2fd38a12b3c4d846bbcf44660a288c2ed4fb Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 14 Sep 2023 14:53:13 +0800 Subject: [PATCH 02/18] Update Version and README --- README.md | 70 ++++++++++++++++++++++++++++++++++++++----------------- demo.py | 7 ++++++ setup.py | 2 +- 3 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 demo.py diff --git a/README.md b/README.md index 4ec52d8..7971364 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,64 @@ -# pyGmSSL +# GmSSL-Python -pyGmSSL is the Python binding to the GmSSL library. +## 简介 -The Python wrappers of pyGmSSL are very similar to the GmSSL v3 C API. Most class/struct and function names are the same, and the arguments are in the same order. Here is an example of encrypting a block of message by SM4 cipher in both C and Python. +本项目是GmSSL密码库的Python语言封装。GmSSL-Python目前提供了随机数生成器、SM3哈希、SM3消息认证码(HMAC-SM3)、SM4加密(包括分组加密和CBC/CTR/GCM加密模式)、ZUC加密、SM2加密/签名等功能,可以覆盖目前国密算法主要应用开发场景。 -```C -#include -#include +## 编译和安装 -unsigned char key[SM4_KEY_SIZE] = "1234567812345678"; -rand_bytes(key, sizeof(key)); +GmSSL-Java依赖GmSSL项目,在编译前需要先在系统上编译、安装并测试通过GmSSL库及工具。请在https://github.com/guanzhi/GmSSL 项目上下载最新的GmSSL代码,并完成编译、测试和安装。 -unsigned char block[SM4_BLOCK_SIZE] = "1234567812345678"; +下载最新的GmSSL-Python代码 [GmSSL-Python-main.zip](https://github.com/GmSSL/GmSSL-Python/archive/refs/heads/main.zip),解压缩,进入源代码目录。 -SM4_KEY sm4_key; -sm4_set_encrpt_key(&sm4_key, key); -sm4_encrypt(&sm4_key, block, block); +首先创建源码安装包 -sm4_set_decrpt_key(&sm4_key, key); -sm4_decrypt(&sm4_key, block, block); +```bash +python setup.py sdist ``` -The corresponding Python code: +本地安装 + +``` +python setup.py install +``` + +在安装过程中会产生`deprecated`警告,对于Python 3.11及之前的版本可忽略此警告,并可以顺利编译完成。 + +运行测试 + +```bash +$ python -m unittest +.......... +---------------------------------------------------------------------- +Ran 10 tests in 0.256s + +OK +``` + +上面的输出表明测试通过。 + +编写一个简单的测试程序`sm3.py` ```python -import GmSSL +from gmssl import * + +sm3 = Sm3() +sm3.update(b'abc') +dgst = sm3.digest() +print("sm3('abc') : " + dgst.hex()) +``` -sm4_key = GmSSL.SM4_KEY() -sm4_key.set_encrypt_key(b"1234567812345678") -ciphertext = sm4_key.encrypt(b"1234567812345678") +执行这个程序 -sm4_key.set_decrypt_key(b"1234567812345678") -plaintext = sm4_key.decrypt(ciphertext) +```bash +$ python demo.py +sm3('abc') : 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 +``` + +可以看到运行成功。通过`gmssl`命令行验证输出是正确的 + +``` +echo -n abc | gmssl sm3 ``` +可以看到输出相同的SM3哈希值 diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..8059c6f --- /dev/null +++ b/demo.py @@ -0,0 +1,7 @@ + +from gmssl import * + +sm3 = Sm3() +sm3.update(b'abc') +dgst = sm3.digest() +print("sm3('abc') : " + dgst.hex()) diff --git a/setup.py b/setup.py index f1ab044..b0a3b23 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,6 @@ setup( name='GmSSL-Python', - version='1.0', + version='2.0', py_modules=['gmssl'], ) From 07c98ba23b216322c8ff969507d39f804e39dfc5 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Sat, 16 Sep 2023 20:41:40 +0800 Subject: [PATCH 03/18] Add SM9 and Certificate --- .gitignore | 2 + gmssl.py | 468 ++++++++++++++++++++++++++++++++++++++++++++++++++++- test.py | 92 +++++++++++ 3 files changed, 555 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index b6e4761..9967b3f 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,5 @@ dmypy.json # Pyre type checker .pyre/ +*.pem + diff --git a/gmssl.py b/gmssl.py index 4bfc483..2fd0dc3 100755 --- a/gmssl.py +++ b/gmssl.py @@ -11,6 +11,8 @@ from ctypes import * from ctypes.util import find_library +import datetime + libgmssl = find_library("gmssl") gmssl = cdll.LoadLibrary(libgmssl) @@ -102,6 +104,14 @@ def generateMac(self): return hmac.raw +def sm3_pbkdf2(passwd, salt, iterator, keylen): + key = create_string_buffer(keylen) + # FIXME: len(passwd) =?= len(passwd.encode(utf8)) ???? + if gmssl.pbkdf2_hmac_sm3_genkey(passwd.encode('utf-8'), len(passwd), salt, len(salt), iterator, keylen, key) != 1: + raise InnerError('libgmssl inner error') + return key.raw + + SM4_KEY_SIZE = 16 SM4_BLOCK_SIZE = 16 @@ -328,7 +338,7 @@ def finish(self): return outbuf[:outlen.value] -SM2_DEFAULT_ID = b'1234567812345678' +SM2_DEFAULT_ID = '1234567812345678' SM2_MAX_SIGNATURE_SIZE = 72 SM2_MIN_PLAINTEXT_SIZE = 1 @@ -357,7 +367,7 @@ def generate_key(self): def compute_z(self, signer_id): z = create_string_buffer(SM3_DIGEST_SIZE) - gmssl.sm2_compute_z(z, byref(self), signer_id, c_size_t(len(signer_id))) + gmssl.sm2_compute_z(z, byref(self), signer_id.encode('utf-8'), c_size_t(len(signer_id))) return z.raw def export_encrypted_private_key_info_pem(self, file, passwd): @@ -406,9 +416,7 @@ def verify(self, dgst, sig): if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') ret = gmssl.sm2_verify(byref(self), dgst, sig, c_size_t(len(sig))) - if ret < 0: - raise InnerError('libgmssl inner error') - if ret == 0: + if ret != 1: return False return True @@ -437,11 +445,11 @@ class Sm2Signature(Structure): def __init__(self, sm2_key, signer_id, sign): if sign == True: self._sign = True - if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id, c_size_t(len(signer_id))) != 1: + if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id.encode('utf-8'), c_size_t(len(signer_id))) != 1: raise InnerError('libgmssl inner error') else: self._sign = False - if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id, c_size_t(len(signer_id))) != 1: + if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id.encode('utf-8'), c_size_t(len(signer_id))) != 1: raise InnerError('libgmssl inner error') @@ -469,12 +477,458 @@ def verify(self, sig): return True +class sm9_bn_t(Structure): + _fields_ = [ + ("d", c_uint64 * 8) + ] + +class sm9_fp2_t(Structure): + _fields_ = [ + ("d", sm9_bn_t * 2) + ] + +class Sm9Point(Structure): + _fields_ = [ + ("X", sm9_bn_t), + ("Y", sm9_bn_t), + ("Z", sm9_bn_t) + ] + +class Sm9TwistPoint(Structure): + _fields_ = [ + ("X", sm9_fp2_t), + ("Y", sm9_fp2_t), + ("Z", sm9_fp2_t) + ] + + +SM9_MAX_ID_SIZE = 63 +SM9_MAX_PLAINTEXT_SIZE = 255 +SM9_MAX_CIPHERTEXT_SIZE = 367 + +class Sm9EncKey(Structure): + _fields_ = [ + ("Ppube", Sm9Point), + ("de", Sm9TwistPoint) + ] + + def __init__(self, owner_id): + self._id = owner_id.encode('utf-8') + + def import_encrypted_private_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def export_encrypted_private_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def get_id(self): + return self._id; + + def decrypt(self, ciphertext): + plaintext = create_string_buffer(SM9_MAX_PLAINTEXT_SIZE) + outlen = c_size_t() + if gmssl.sm9_decrypt(byref(self), c_char_p(self._id), len(self._id), ciphertext, c_size_t(len(ciphertext)), plaintext, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return plaintext[0:outlen.value] + + +class Sm9EncMasterKey(Structure): + _fields_ = [ + ("Ppube", Sm9Point), + ("ke", sm9_bn_t) + ] + + def generate_master_key(self): + if gmssl.sm9_enc_master_key_generate(byref(self)) != 1: + raise InnerError('libgmssl inner error') + return True + + def extract_key(self, identity): + key = Sm9EncKey(identity) + gmssl.sm9_enc_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) + return key + + def import_encrypted_master_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def export_encrypted_master_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def export_public_master_key_pem(self, path): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_enc_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def import_public_master_key_pem(self, path): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_enc_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def encrypt(self, plaintext, to): + ciphertext = create_string_buffer(SM9_MAX_CIPHERTEXT_SIZE) + outlen = c_size_t() + if gmssl.sm9_encrypt(byref(self), c_char_p(to.encode('utf-8')), len(to), plaintext, c_size_t(len(plaintext)), ciphertext, byref(outlen)) != 1: + raise InnerError('libgmssl inner error') + return ciphertext[0:outlen.value] + + + + +class Sm9SignKey(Structure): + _fields_ = [ + ("Ppubs", Sm9TwistPoint), + ("ds", Sm9Point) + ] + + def __init__(self, owner_id): + self._id = owner_id.encode('utf-8') + + def import_encrypted_private_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def export_encrypted_private_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def get_id(self): + return self._id; + +class Sm9SignMasterKey(Structure): + _fields_ = [ + ("Ppubs", Sm9TwistPoint), + ("ks", sm9_bn_t) + ] + + def generate_master_key(self): + if gmssl.sm9_sign_master_key_generate(byref(self)) != 1: + raise InnerError('libgmssl inner error') + return True + def extract_key(self, identity): + key = Sm9SignKey(identity) + gmssl.sm9_sign_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) + return key + def import_encrypted_master_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + def export_encrypted_master_key_info_pem(self, path, passwd): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + def export_public_master_key_pem(self, path): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'wb') + if gmssl.sm9_sign_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + def import_public_master_key_pem(self, path): + libc.fopen.restype = c_void_p + fp = libc.fopen(path.encode('utf-8'), 'rb') + if gmssl.sm9_sign_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + libc.fclose(c_void_p(fp)) + return True + + +SM9_SIGNATURE_SIZE = 104 + +class Sm9Signature(Structure): + _fields_ = [ + ("sm3", Sm3) + ] + + def __init__(self, sign): + + if sign == True: + if gmssl.sm9_sign_init(byref(self)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm9_verify_init(byref(self)) != 1: + raise InnerError('libgmssl inner error') + + self._sign = sign + self._inited = True + + + def reset(self, sign): + if sign == True: + if gmssl.sm9_sign_init(byref(self)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm9_verify_init(byref(self)) != 1: + raise InnerError('libgmssl inner error') + + self._sign = sign + self._inited = True + + + def update(self, data): + + if self._inited != True: + raise InnerError('libgmssl inner error') + + if self._sign: + if gmssl.sm9_sign_update(byref(self), data, c_size_t(len(data))) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.sm9_verify_update(byref(self), data, c_size_t(len(data))) != 1: + raise InnerError('libgmssl inner error') + + return True + + def sign(self, sign_key): + + if self._inited != True: + raise InnerError('libgmssl inner error') + + if self._sign != True: + raise InnerError('libgmssl inner error') + + sig = create_string_buffer(SM9_SIGNATURE_SIZE) + siglen = c_size_t() + if gmssl.sm9_sign_finish(byref(self), byref(sign_key), sig, byref(siglen)) != 1: + raise InnerError('libgmssl inner error') + return sig[:siglen.value] + + def verify(self, sig, master_pub, signer_id): + if self._inited != True: + raise InnerError('libgmssl inner error') + + if self._sign == True: + raise InnerError('libgmssl inner error') + + ret = gmssl.sm9_verify_finish(byref(self), sig, len(sig), byref(master_pub), signer_id.encode('utf-8'), len(signer_id)) + if ret != 1: + return False + return True + + + +ASN1_TAG_IA5String = 22 +ASN1_TAG_SEQUENCE = 0x30 +ASN1_TAG_SET = 0x31 + + + +def gmssl_parse_attr_type_and_value(name, d, dlen): + oid = c_int() + tag = c_int() + val = c_void_p() + vlen = c_size_t() + + if gmssl.x509_name_type_from_der(byref(oid), byref(d), byref(dlen)) != 1: + raise InnerError('libgmssl inner error') + gmssl.x509_name_type_name.restype = c_char_p + oid_name = gmssl.x509_name_type_name(oid).decode('ascii') + + if oid_name == 'emailAddress': + if gmssl.asn1_ia5_string_from_der_ex(ASN1_TAG_IA5String, byref(val), byref(vlen), byref(d), byref(dlen)) != 1: + raise InnerError('libgmssl inner error') + else: + if gmssl.x509_directory_name_from_der(byref(tag), byref(val), byref(vlen), byref(d), byref(dlen)) != 1: + raise InnerError('libgmssl inner error') + + if dlen.value != 0: + raise InnerError('libgmssl inner error') + + value = create_string_buffer(vlen.value) + libc.memcpy(value, val, vlen) + + name[oid_name] = value.raw.decode('utf-8') + return True + +def gmssl_parse_rdn(name, d, dlen): + v = c_void_p() + vlen = c_size_t() + + while dlen.value > 0: + if gmssl.asn1_type_from_der(ASN1_TAG_SEQUENCE, byref(v), byref(vlen), byref(d), byref(dlen)) != 1: + raise InnerError('libgmssl inner error') + + if gmssl_parse_attr_type_and_value(name, v, vlen) != 1: + raise InnerError('libgmssl inner error') + + return True + +# https://stacktuts.com/how-to-correctly-pass-pointer-to-pointer-into-dll-in-python-and-ctypes# +def gmssl_parse_name(name, d, dlen): + v = c_void_p() + vlen = c_size_t() + + while dlen.value > 0: + if gmssl.asn1_nonempty_type_from_der(c_int(ASN1_TAG_SET), byref(v), byref(vlen), byref(d), byref(dlen)) != 1: + raise InnerError('libgmssl inner error') + gmssl_parse_rdn(name, v, vlen) + return True + +class Sm2Certificate: + + def import_pem(self, path): + + cert = c_void_p() + certlen = c_size_t() + if gmssl.x509_cert_new_from_file(byref(cert), byref(certlen), path.encode('utf-8')) != 1: + raise InnerError('libgmssl inner error') + + self._cert = create_string_buffer(certlen.value) + libc.memcpy(self._cert, cert, certlen) + + #libc.fopen.restype = c_void_p + #fp = libc.fopen(path.encode('utf-8'), 'rb') + #if gmssl.x509_cert_from_pem(self._cert, byref(certlen), len(self._cert), c_void_p(fp)) != 1: + # raise InnerError('libgmssl inner error') + + + def get_raw(self): + return self._cert; + + def export_pem(self, path): + libc.fopen.restype = c_void_p + fp = libc.fopen(file.encode('utf-8'), 'wb') + if gmssl.x509_cert_to_pem(self._cert, len(self._cert), c_void_p(fp)) != 1: + raise InnerError('libgmssl inner error') + return True + + def get_serial_number(self): + + serial_ptr = c_void_p() + serial_len = c_size_t() + + if gmssl.x509_cert_get_issuer_and_serial_number(self._cert, len(self._cert), None, None, byref(serial_ptr), byref(serial_len)) != 1: + raise InnerError('libgmssl inner error') + + serial = create_string_buffer(serial_len.value) + libc.memcpy(serial, serial_ptr, serial_len) + return serial.raw + + def get_issuer(self): + issuer_ptr = c_void_p() + issuer_len = c_size_t() + if gmssl.x509_cert_get_issuer(self._cert, len(self._cert), byref(issuer_ptr), byref(issuer_len)) != 1: + raise InnerError('libgmssl inner error') + issuer_raw = create_string_buffer(issuer_len.value) + libc.memcpy(issuer_raw, issuer_ptr, issuer_len) + + issuer = { "raw_data" : issuer_raw } + + gmssl_parse_name(issuer, issuer_ptr, issuer_len) + + return issuer + + def get_subject(self): + subject_ptr = c_void_p() + subject_len = c_size_t() + if gmssl.x509_cert_get_subject(self._cert, len(self._cert), byref(subject_ptr), byref(subject_len)) != 1: + raise InnerError('libgmssl inner error') + subject_raw = create_string_buffer(subject_len.value) + libc.memcpy(subject_raw, subject_ptr, subject_len) + + subject = { "raw_data" : subject_raw } + + gmssl_parse_name(subject, subject_ptr, subject_len) + + return subject + + def get_subject_public_key(self): + public_key = Sm2Key() + gmssl.x509_cert_get_subject_public_key(self._cert, len(self._cert), byref(public_key)) + # fixme: public key or private key + return public_key + + def get_not_before(self): + not_before = c_ulong() + gmssl.x509_cert_get_details(self._cert, len(self._cert), + None, + None, None, + None, + None, None, + byref(not_before), None, + None, None, + None, + None, None, + None, None, + None, None, + None, + None, None) + return datetime.datetime.fromtimestamp(not_before.value) + + def get_not_after(self): + not_after = c_ulong() + gmssl.x509_cert_get_details(self._cert, len(self._cert), + None, + None, None, + None, + None, None, + None, byref(not_after), + None, None, + None, + None, None, + None, None, + None, None, + None, + None, None) + return datetime.datetime.fromtimestamp(not_after.value) + + def verify_by_ca_certificate(self, cacert, sm2_id): + + cacert_raw = cacert.get_raw() + + ret = gmssl.x509_cert_verify_by_ca_cert(self._cert, len(self._cert), cacert_raw, len(cacert_raw), + sm2_id.encode('utf-8'), len(sm2_id)) + + if ret != 1: + return False + + return True diff --git a/test.py b/test.py index 6d0cab4..7455e7e 100644 --- a/test.py +++ b/test.py @@ -39,6 +39,14 @@ def test_sm3_hmac(self): mac = sm3_hmac.generateMac() self.assertEqual(mac, bytes.fromhex(mac_hex)) + def test_sm3_pbkdf2(self): + passwd = 'password' + salt = b'12345678' + iterator = 10000 + keylen = 32 + key = sm3_pbkdf2(passwd, salt, iterator, keylen) + self.assertEqual(len(key), keylen) + def test_sm4(self): key = b'1234567812345678' plaintext = b'block of message' @@ -144,6 +152,90 @@ def test_sm2_sig(self): verify_ret = verify.verify(sig) self.assertTrue(verify_ret) + def test_sm9_enc(self): + master_key = Sm9EncMasterKey() + master_key.generate_master_key() + master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') + master_key.export_public_master_key_pem('enc_mpk.pem') + + master_pub = Sm9EncMasterKey() + master_pub.import_public_master_key_pem('enc_mpk.pem') + ciphertext = master_pub.encrypt(b'plaintext', 'Alice') + + master = Sm9EncMasterKey() + master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') + + key = master.extract_key('Alice') + plaintext = key.decrypt(ciphertext) + self.assertEqual(plaintext, b'plaintext') + + def test_sm9_sign(self): + master_key = Sm9SignMasterKey() + master_key.generate_master_key() + master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') + master_key.export_public_master_key_pem('sign_mpk.pem') + + + master = Sm9SignMasterKey() + master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') + + key = master.extract_key('Alice') + + sign = Sm9Signature(True) + sign.update(b'message') + sig = sign.sign(key) + + master_pub = Sm9SignMasterKey() + master_pub.import_public_master_key_pem('sign_mpk.pem') + + verify = Sm9Signature(False) + verify.update(b'message') + ret = verify.verify(sig, master_pub, 'Alice') + self.assertTrue(ret) + + def test_sm2_cert(self): + cert_txt = '''-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE-----''' + + with open('ROOTCA2.pem', 'w') as file: + file.write(cert_txt) + file.close() + + + cert = Sm2Certificate() + cert.import_pem('ROOTCA2.pem') + + serial = cert.get_serial_number() + #print(serial.hex()) + + not_before = cert.get_not_before() + #print(not_before) + + not_after = cert.get_not_after() + #print(not_after) + + public_key = cert.get_subject_public_key() + public_key.export_public_key_info_pem('public_key.pem') + + issuer = cert.get_issuer() + #print(issuer) + + subject = cert.get_subject() + #print(subject) + + ret = cert.verify_by_ca_certificate(cert, SM2_DEFAULT_ID) + self.assertTrue(ret) + if __name__ == '__main__': unittest.main() From 1eccf467267202da555232bb5cad61b8851656bb Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Sat, 16 Sep 2023 21:07:27 +0800 Subject: [PATCH 04/18] Update version --- gmssl.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gmssl.py b/gmssl.py index 2fd0dc3..bdca9f1 100755 --- a/gmssl.py +++ b/gmssl.py @@ -19,7 +19,7 @@ libc = cdll.LoadLibrary(find_library('c')) -GMSSL_PYTHON_VERSION = "2.0" +GMSSL_PYTHON_VERSION = "2.1" def gmssl_version_num(): diff --git a/setup.py b/setup.py index b0a3b23..07464b7 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,6 @@ setup( name='GmSSL-Python', - version='2.0', + version='2.1', py_modules=['gmssl'], ) From 178ad8c420d7bb3ae1ca5f8cd8efb1b539b3dc3e Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Wed, 20 Sep 2023 19:27:36 +0800 Subject: [PATCH 05/18] New version * update version to 2.2.0 * minor changes of API * more error checking * use pip for install * update README --- README.md | 83 +++++++- demo.py => examples/demo.py | 0 gmssl.py | 401 +++++++++++++++++++++--------------- pyproject.toml | 23 +++ setup.py | 19 -- test.py | 158 +++++++------- 6 files changed, 402 insertions(+), 282 deletions(-) rename demo.py => examples/demo.py (100%) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/README.md b/README.md index 7971364..7810fbb 100644 --- a/README.md +++ b/README.md @@ -2,41 +2,102 @@ ## 简介 -本项目是GmSSL密码库的Python语言封装。GmSSL-Python目前提供了随机数生成器、SM3哈希、SM3消息认证码(HMAC-SM3)、SM4加密(包括分组加密和CBC/CTR/GCM加密模式)、ZUC加密、SM2加密/签名等功能,可以覆盖目前国密算法主要应用开发场景。 +`gmssl-python`是GmSSL密码库(https://gmssl.com/guanzhi/GmSSL)的Python语言封装,以`ctypes`方式实现,通过Python类和函数提供了如下密码接口: -## 编译和安装 +* 密码随机数生成器 +* SM2加密和签名,SM2密钥生成、私钥口令加密保护、密钥PEM文件导入导出 +* SM2数字证书的导入、解析和验证 +* SM3哈希函数、HMAC-SM3消息认证码、基于SM3的PBKDF2密钥导出函数 +* SM4分组加密,以及SM4的CBC、CTR、GCM三种加密模式 +* SM9加密和签名,以及SM9密钥生成、密钥口令加密保护、密钥PEM文件导入导出 +* ZUC序列密码加密 -GmSSL-Java依赖GmSSL项目,在编译前需要先在系统上编译、安装并测试通过GmSSL库及工具。请在https://github.com/guanzhi/GmSSL 项目上下载最新的GmSSL代码,并完成编译、测试和安装。 +目前`gmssl-python`功能可以覆盖除SSL/TLS/TLCP之外的国密算法主要应用开发场景。 -下载最新的GmSSL-Python代码 [GmSSL-Python-main.zip](https://github.com/GmSSL/GmSSL-Python/archive/refs/heads/main.zip),解压缩,进入源代码目录。 +## 安装 -首先创建源码安装包 +由于`gmssl-python`以`ctypes`方式实现,因此所有密码功能都是通过调用本地安装的GmSSL动态库 (如`/usr/local/lib/libgmssl.so`)实现的,在安装和调用`gmssl-python`之前必须首先在系统上安装GmSSL,然后通过Python的包管理工具`pip`从Python代码仓库安装,或者从`gmssl-python`项目的代码仓库https://github.com/GmSSL/GmSSL-Python 下载最新的源代码,从本地安装。 + +### 安装GmSSL + +首先在https://github.com/guanzhi/GmSSL 项目上下载最新的GmSSL代码[GmSSL-master.zip](https://github.com/guanzhi/GmSSL/archive/refs/heads/master.zip),编译并安装。GmSSL代码是C语言编写的,需要安装GCC、CMake来编译,在Ubuntu/Debian系统上可以执行 ```bash -python setup.py sdist +sudo install build-essentials cmake ``` -本地安装 +安装依赖的编译工具,然后解压GmSSL源代码,进入源码目录`GmSSL-master`并执行如下指令: +```bash +$ mkdir build +$ cd build +$ cmake .. +$ make +$ make test +$ sudo make install +``` + +安装完成后可以执行`gmssl`命令行工具检查是否安装完毕。 + +```bash +$ gmssl help ``` -python setup.py install + +由于`gmssl-python`需要`libgmssl`动态库,因此GmSSL安装时不要改变配置,仅以静态库安装时`gmssl-python`是不可用的。安装后执行`gmssl`命令可能提示找不到动态库,在Ubuntu系统下可以执行`sudo ldconfig`来发现新安装的动态库,在CentOS系统上需要在`/etc/ld.so.conf`配置文件中将`libgmssl`动态库的目录`/usr/local/lib`加入到配置文件中。 + +### 从Python代码仓库安装`gmssl-python` + +`gmssl-python` 会定期发布到Python代码仓库中,可以通过`pip`工具安装 + +```bash +$ pip install gmssl-python +$ pip show gmssl-python ``` +通过`pip show`命令可以查看当前安装的`gmssl-python`的版本信息。 + +### 下载源码本地安装 + +从代码仓库中安装的`gmssl-python`通常不是最新版本,可以下载最新的GmSSL-Python代码 [GmSSL-Python-main.zip](https://github.com/GmSSL/GmSSL-Python/archive/refs/heads/main.zip),本地安装。 + +解压缩并进入源代码目录`GmSSL-Python-main`。由于最新代码可能还处于开发过程中,在安装前必须进行测试确保全部功能正确,`gmssl-python`中提供了测试,执行如下命令 + 在安装过程中会产生`deprecated`警告,对于Python 3.11及之前的版本可忽略此警告,并可以顺利编译完成。 运行测试 ```bash -$ python -m unittest -.......... +$ python -m unittest -v +................ ---------------------------------------------------------------------- -Ran 10 tests in 0.256s +Ran 16 tests in 1.407s OK ``` 上面的输出表明测试通过。 +然后可以通过`pip`命令安装当前目录下的代码 + +```bash +$ pip install . +$ pip show gmssl-python +``` + +### 验证安装成功 + +注意`gmssl-python`包中只包含一个`gmssl`模块(而不是`gmssl_python`模块)。 + +可以在Python交互环境中做简单的测试 + +```python +>>> import gmssl +>>> gmssl.GMSSL_PYTHON_VERSION +>>> gmssl.GMSSL_LIBRARY_VERSION +``` + +分别查看当前`gmssl-python`的版本和`libgmssl`的版本。 + 编写一个简单的测试程序`sm3.py` ```python diff --git a/demo.py b/examples/demo.py similarity index 100% rename from demo.py rename to examples/demo.py diff --git a/gmssl.py b/gmssl.py index bdca9f1..167e44e 100755 --- a/gmssl.py +++ b/gmssl.py @@ -5,57 +5,54 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# pyGmSSL - the Python binding of the GmSSL library +# GmSSL-Python - Python binding of the GmSSL library with `ctypes` from ctypes import * from ctypes.util import find_library - import datetime -libgmssl = find_library("gmssl") +if find_library('gmssl') == None: + raise ValueError('Install GmSSL dynamic library from https://github.com/guanzhi/GmSSL') -gmssl = cdll.LoadLibrary(libgmssl) +gmssl = cdll.LoadLibrary(find_library("gmssl")) libc = cdll.LoadLibrary(find_library('c')) -GMSSL_PYTHON_VERSION = "2.1" +class InnerError(Exception): + ''' + GmSSL libraray inner error + ''' +GMSSL_PYTHON_VERSION = "2.2.0" -def gmssl_version_num(): +def gmssl_library_version_num(): return gmssl.gmssl_version_num() - -def gmssl_version_str(): +def gmssl_library_version_str(): gmssl.gmssl_version_str.restype = c_char_p - p = gmssl.gmssl_version_str() - return p.decode('ascii') + return gmssl.gmssl_version_str().decode('ascii') -GMSSL_LIBRARY_VERSION = gmssl_version_str() +GMSSL_LIBRARY_VERSION = gmssl_library_version_str() def rand_bytes(size): buf = create_string_buffer(size) - gmssl.rand_bytes(buf, c_size_t(size)) + gmssl.rand_bytes(buf, size) return buf.raw -class InnerError(Exception): - ''' - GmSSL libraray inner error - ''' SM3_DIGEST_SIZE = 32 +_SM3_STATE_WORDS = 8 +_SM3_BLOCK_SIZE = 64 class Sm3(Structure): - SM3_STATE_WORDS = 8 - SM3_BLOCK_SIZE = 64 - _fields_ = [ - ("dgst", c_uint32 * SM3_STATE_WORDS), + ("dgst", c_uint32 * _SM3_STATE_WORDS), ("nblocks", c_uint64), - ("block", c_uint8 * SM3_BLOCK_SIZE), + ("block", c_uint8 * _SM3_BLOCK_SIZE), ("num", c_size_t) ] @@ -66,7 +63,7 @@ def reset(self): gmssl.sm3_init(byref(self)) def update(self, data): - gmssl.sm3_update(byref(self), data, c_size_t(len(data))) + gmssl.sm3_update(byref(self), data, len(data)) def digest(self): dgst = create_string_buffer(SM3_DIGEST_SIZE) @@ -74,54 +71,73 @@ def digest(self): return dgst.raw -SM3_HMAC_SIZE = SM3_DIGEST_SIZE SM3_HMAC_MIN_KEY_SIZE = 16 SM3_HMAC_MAX_KEY_SIZE = 64 +SM3_HMAC_SIZE = SM3_DIGEST_SIZE class Sm3Hmac(Structure): _fields_ = [ ("sm3_ctx", Sm3), - ("key", c_uint8 * Sm3.SM3_BLOCK_SIZE) + ("key", c_uint8 * _SM3_BLOCK_SIZE) ] def __init__(self, key): if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: raise ValueError('Invalid SM3 HMAC key length') - gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) + gmssl.sm3_hmac_init(byref(self), key, len(key)) def reset(self, key): if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: raise ValueError('Invalid SM3 HMAC key length') - gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) + gmssl.sm3_hmac_init(byref(self), key, len(key)) def update(self, data): - gmssl.sm3_hmac_update(byref(self), data, c_size_t(len(data))) + gmssl.sm3_hmac_update(byref(self), data, len(data)) - def generateMac(self): + def generate_mac(self): hmac = create_string_buffer(SM3_HMAC_SIZE) gmssl.sm3_hmac_finish(byref(self), hmac) return hmac.raw + +SM3_PBKDF2_MIN_ITER = 10000 # from +SM3_PBKDF2_MAX_ITER = 16777216 # 2^24 +SM3_PBKDF2_MAX_SALT_SIZE = 64 # from +SM3_PBKDF2_DEFAULT_SALT_SIZE = 8 # from +SM3_PBKDF2_MAX_KEY_SIZE = 256 # from gmssljni.c:sm3_pbkdf2():sizeof(keybuf) + def sm3_pbkdf2(passwd, salt, iterator, keylen): + + if len(salt) > SM3_PBKDF2_MAX_SALT_SIZE: + raise ValueError('Invalid salt length') + + if iterator < SM3_PBKDF2_MIN_ITER or iterator > SM3_PBKDF2_MAX_ITER: + raise ValueError('Invalid iterator value') + + if keylen > SM3_PBKDF2_MAX_KEY_SIZE: + raise ValueError('Invalid key length') + + passwd_bytes = passwd.encode('utf-8') key = create_string_buffer(keylen) - # FIXME: len(passwd) =?= len(passwd.encode(utf8)) ???? - if gmssl.pbkdf2_hmac_sm3_genkey(passwd.encode('utf-8'), len(passwd), salt, len(salt), iterator, keylen, key) != 1: + + if gmssl.pbkdf2_hmac_sm3_genkey(passwd_bytes, len(passwd_bytes), salt, len(salt), iterator, keylen, key) != 1: raise InnerError('libgmssl inner error') + return key.raw + SM4_KEY_SIZE = 16 SM4_BLOCK_SIZE = 16 - - +_SM4_NUM_ROUNDS = 32 class Sm4(Structure): - SM4_NUM_ROUNDS = 32 - - _fields_ = [("rk", c_uint32 * SM4_NUM_ROUNDS)] + _fields_ = [ + ("rk", c_uint32 * _SM4_NUM_ROUNDS) + ] def __init__(self, key, encrypt): if len(key) != SM4_KEY_SIZE: @@ -139,6 +155,9 @@ def encrypt(self, block): return outbuf.raw +SM4_CBC_IV_SIZE = SM4_BLOCK_SIZE + + class Sm4Cbc(Structure): _fields_ = [ @@ -166,10 +185,10 @@ def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() if self._encrypt == True: - if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm4_cbc_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm4_cbc_decrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -209,7 +228,7 @@ def __init__(self, key, iv): def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm4_ctr_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -250,7 +269,7 @@ def __init__(self, key, iv): def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if gmssl.zuc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.zuc_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -297,7 +316,7 @@ class Sm4Gcm(Structure): ("maclen", c_size_t) ] - def __init__(self, key, iv, aad, taglen, encrypt): + def __init__(self, key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = True): if len(key) != SM4_KEY_SIZE: raise ValueError('Invalid key length') if len(iv) < SM4_GCM_MIN_IV_SIZE or len(iv) > SM4_GCM_MAX_IV_SIZE: @@ -305,11 +324,9 @@ def __init__(self, key, iv, aad, taglen, encrypt): if taglen < 1 or taglen > SM4_GCM_MAX_TAG_SIZE: raise ValueError('Invalid Tag length') if encrypt == True: - ok = gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), - iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) + ok = gmssl.sm4_gcm_encrypt_init(byref(self), key, len(key), iv, len(iv), aad, len(aad), taglen) else: - ok = gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), - iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) + ok = gmssl.sm4_gcm_decrypt_init(byref(self), key, len(key), iv, len(iv), aad, len(aad), taglen) if ok != 1: raise InnerError('libgmssl inner error') self._encrypt = encrypt @@ -319,10 +336,10 @@ def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() if self._encrypt == True: - if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm4_gcm_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm4_gcm_decrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -341,6 +358,7 @@ def finish(self): SM2_DEFAULT_ID = '1234567812345678' SM2_MAX_SIGNATURE_SIZE = 72 + SM2_MIN_PLAINTEXT_SIZE = 1 SM2_MAX_PLAINTEXT_SIZE = 255 SM2_MIN_CIPHERTEXT_SIZE = 45 @@ -361,23 +379,38 @@ class Sm2Key(Structure): ("private_key", c_uint8 * 32) ] + def __init__(self): + self._has_public_key = False + self._has_private_key = False + def generate_key(self): if gmssl.sm2_key_generate(byref(self)) != 1: raise InnerError('libgmssl inner error') + self._has_public_key = True + self._has_private_key = True + + def has_private_key(self): + return self._has_private_key - def compute_z(self, signer_id): + def has_public_key(self): + return self._has_public_key + + def compute_z(self, signer_id = SM2_DEFAULT_ID): + if self._has_public_key == False: + raise InnerError('libgmssl inner error') + signer_id = signer_id.encode('utf-8') z = create_string_buffer(SM3_DIGEST_SIZE) - gmssl.sm2_compute_z(z, byref(self), signer_id.encode('utf-8'), c_size_t(len(signer_id))) + gmssl.sm2_compute_z(z, byref(self), signer_id, len(signer_id)) return z.raw def export_encrypted_private_key_info_pem(self, file, passwd): + if self._has_private_key == False: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(file.encode('utf-8'), 'wb') - if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), - passwd.encode('utf-8'), c_void_p(fp)) != 1: + if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def import_encrypted_private_key_info_pem(self, file, passwd): libc.fopen.restype = c_void_p @@ -385,15 +418,17 @@ def import_encrypted_private_key_info_pem(self, file, passwd): if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = True def export_public_key_info_pem(self, file): + if self._has_public_key == False: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(file.encode('utf-8'), 'wb') if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def import_public_key_info_pem(self, file): libc.fopen.restype = c_void_p @@ -401,9 +436,12 @@ def import_public_key_info_pem(self, file): if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = False def sign(self, dgst): + if self._has_private_key == False: + raise InnerError('libgmssl inner error') if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) @@ -413,28 +451,40 @@ def sign(self, dgst): return sig[:siglen.value] def verify(self, dgst, sig): + if self._has_public_key == False: + raise InnerError('libgmssl inner error') if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') - ret = gmssl.sm2_verify(byref(self), dgst, sig, c_size_t(len(sig))) - if ret != 1: + if gmssl.sm2_verify(byref(self), dgst, sig, len(sig)) != 1: return False return True def encrypt(self, data): + if self._has_public_key == False: + raise InnerError('libgmssl inner error') + if len(data) > SM2_MAX_PLAINTEXT_SIZE: + raise InnerError('libgmssl inner error') outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() - if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: + if gmssl.sm2_encrypt(byref(self), data, len(data), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[:outlen.value] def decrypt(self, ciphertext): + if self._has_private_key == False: + raise InnerError('libgmssl inner error') outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) outlen = c_size_t() - if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(ciphertext)), outbuf, byref(outlen)) != 1: + if gmssl.sm2_decrypt(byref(self), ciphertext, len(ciphertext), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[:outlen.value] +DO_ENCRYPT = True +DO_DECRYPT = False +DO_SIGN = True +DO_VERIFY = False + class Sm2Signature(Structure): _fields_ = [ @@ -442,26 +492,31 @@ class Sm2Signature(Structure): ("key", Sm2Key) ] - def __init__(self, sm2_key, signer_id, sign): - if sign == True: - self._sign = True - if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id.encode('utf-8'), c_size_t(len(signer_id))) != 1: + def __init__(self, sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN): + signer_id = signer_id.encode('utf-8') + if sign: + if sm2_key.has_private_key() != True: + raise InnerError('libgmssl inner error') + if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id, len(signer_id)) != 1: raise InnerError('libgmssl inner error') else: - self._sign = False - if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id.encode('utf-8'), c_size_t(len(signer_id))) != 1: + if sm2_key.has_public_key() != True: raise InnerError('libgmssl inner error') - + if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id, len(signer_id)) != 1: + raise InnerError('libgmssl inner error') + self._sign = sign def update(self, data): - if self._sign == True: - if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: + if self._sign == DO_SIGN: + if gmssl.sm2_sign_update(byref(self), data, len(data)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: + if gmssl.sm2_verify_update(byref(self), data, len(data)) != 1: raise InnerError('libgmssl inner error') def sign(self): + if self._sign != DO_SIGN: + raise InnerError('libgmssl inner error') sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) siglen = c_size_t() if gmssl.sm2_sign_finish(byref(self), sig, byref(siglen)) != 1: @@ -469,10 +524,9 @@ def sign(self): return sig[:siglen.value] def verify(self, sig): - ret = gmssl.sm2_verify_finish(byref(self), sig, c_size_t(len(sig))) - if ret < 0: + if self._sign != DO_VERIFY: raise InnerError('libgmssl inner error') - if ret == 0: + if gmssl.sm2_verify_finish(byref(self), sig, len(sig)) != 1: return False return True @@ -514,6 +568,13 @@ class Sm9EncKey(Structure): def __init__(self, owner_id): self._id = owner_id.encode('utf-8') + self._has_private_key = False + + def get_id(self): + return self._id; + + def has_private_key(self): + return self._has_private_key def import_encrypted_private_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p @@ -521,23 +582,23 @@ def import_encrypted_private_key_info_pem(self, path, passwd): if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_private_key = True def export_encrypted_private_key_info_pem(self, path, passwd): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True - - def get_id(self): - return self._id; def decrypt(self, ciphertext): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') plaintext = create_string_buffer(SM9_MAX_PLAINTEXT_SIZE) outlen = c_size_t() - if gmssl.sm9_decrypt(byref(self), c_char_p(self._id), len(self._id), ciphertext, c_size_t(len(ciphertext)), plaintext, byref(outlen)) != 1: + if gmssl.sm9_decrypt(byref(self), self._id, len(self._id), ciphertext, len(ciphertext), plaintext, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return plaintext[0:outlen.value] @@ -548,14 +609,25 @@ class Sm9EncMasterKey(Structure): ("ke", sm9_bn_t) ] + def __init__(self): + self._has_public_key = False + self._has_private_key = False + def generate_master_key(self): if gmssl.sm9_enc_master_key_generate(byref(self)) != 1: raise InnerError('libgmssl inner error') - return True + self._has_public_key = True + self._has_private_key = True def extract_key(self, identity): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') key = Sm9EncKey(identity) - gmssl.sm9_enc_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) + # FIXME: identity type + if gmssl.sm9_enc_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) != 1: + raise InnerError('libgmssl inner error') + key._has_public_key = True + key._has_private_key = True return key def import_encrypted_master_key_info_pem(self, path, passwd): @@ -564,23 +636,26 @@ def import_encrypted_master_key_info_pem(self, path, passwd): if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = True def export_encrypted_master_key_info_pem(self, path, passwd): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def export_public_master_key_pem(self, path): + if self._has_public_key != True: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_enc_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def import_public_master_key_pem(self, path): libc.fopen.restype = c_void_p @@ -588,12 +663,15 @@ def import_public_master_key_pem(self, path): if gmssl.sm9_enc_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = False def encrypt(self, plaintext, to): + if self._has_public_key != True: + raise InnerError('libgmssl inner error') ciphertext = create_string_buffer(SM9_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() - if gmssl.sm9_encrypt(byref(self), c_char_p(to.encode('utf-8')), len(to), plaintext, c_size_t(len(plaintext)), ciphertext, byref(outlen)) != 1: + if gmssl.sm9_encrypt(byref(self), to.encode('utf-8'), len(to), plaintext, len(plaintext), ciphertext, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return ciphertext[0:outlen.value] @@ -608,6 +686,13 @@ class Sm9SignKey(Structure): def __init__(self, owner_id): self._id = owner_id.encode('utf-8') + self._has_private_key = False + + def get_id(self): + return self._id; + + def has_private_key(self): + return self._has_private_key def import_encrypted_private_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p @@ -615,19 +700,16 @@ def import_encrypted_private_key_info_pem(self, path, passwd): if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_private_key = True def export_encrypted_private_key_info_pem(self, path, passwd): + if self._has_private_key == False: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True - - def get_id(self): - return self._id; - class Sm9SignMasterKey(Structure): @@ -636,14 +718,25 @@ class Sm9SignMasterKey(Structure): ("ks", sm9_bn_t) ] + def __init__(self): + self._has_public_key = False + self._has_private_key = False + def generate_master_key(self): if gmssl.sm9_sign_master_key_generate(byref(self)) != 1: raise InnerError('libgmssl inner error') - return True + self._has_public_key = True + self._has_private_key = True def extract_key(self, identity): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') key = Sm9SignKey(identity) - gmssl.sm9_sign_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) + # FIXME: identity + if gmssl.sm9_sign_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) != 1: + raise InnerError('libgmssl inner error') + key._has_public_key = True + key._has_private_key = True return key def import_encrypted_master_key_info_pem(self, path, passwd): @@ -652,23 +745,26 @@ def import_encrypted_master_key_info_pem(self, path, passwd): if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = True def export_encrypted_master_key_info_pem(self, path, passwd): + if self._has_private_key != True: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def export_public_master_key_pem(self, path): + if self._has_public_key != True: + raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_sign_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True def import_public_master_key_pem(self, path): libc.fopen.restype = c_void_p @@ -676,61 +772,57 @@ def import_public_master_key_pem(self, path): if gmssl.sm9_sign_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - return True + self._has_public_key = True + self._has_private_key = False SM9_SIGNATURE_SIZE = 104 class Sm9Signature(Structure): + _fields_ = [ ("sm3", Sm3) ] - def __init__(self, sign): - - if sign == True: + def __init__(self, sign = DO_SIGN): + if sign == DO_SIGN: if gmssl.sm9_sign_init(byref(self)) != 1: raise InnerError('libgmssl inner error') else: if gmssl.sm9_verify_init(byref(self)) != 1: raise InnerError('libgmssl inner error') - self._sign = sign self._inited = True - def reset(self, sign): - if sign == True: + def reset(self): + if self._inited != True: + raise InnerError('libgmssl inner error') + + if self._sign == DO_SIGN: if gmssl.sm9_sign_init(byref(self)) != 1: raise InnerError('libgmssl inner error') else: if gmssl.sm9_verify_init(byref(self)) != 1: raise InnerError('libgmssl inner error') - self._sign = sign - self._inited = True - - def update(self, data): if self._inited != True: raise InnerError('libgmssl inner error') - if self._sign: - if gmssl.sm9_sign_update(byref(self), data, c_size_t(len(data))) != 1: + if self._sign == DO_SIGN: + if gmssl.sm9_sign_update(byref(self), data, len(data)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm9_verify_update(byref(self), data, c_size_t(len(data))) != 1: + if gmssl.sm9_verify_update(byref(self), data, len(data)) != 1: raise InnerError('libgmssl inner error') - return True def sign(self, sign_key): - if self._inited != True: raise InnerError('libgmssl inner error') - - if self._sign != True: + if self._sign != DO_SIGN: raise InnerError('libgmssl inner error') sig = create_string_buffer(SM9_SIGNATURE_SIZE) @@ -743,19 +835,20 @@ def verify(self, sig, master_pub, signer_id): if self._inited != True: raise InnerError('libgmssl inner error') - if self._sign == True: + if self._sign != DO_VERIFY: raise InnerError('libgmssl inner error') - ret = gmssl.sm9_verify_finish(byref(self), sig, len(sig), byref(master_pub), signer_id.encode('utf-8'), len(signer_id)) - if ret != 1: + signer_id = signer_id.encode('utf-8') + + if gmssl.sm9_verify_finish(byref(self), sig, len(sig), byref(master_pub), signer_id, len(signer_id)) != 1: return False return True -ASN1_TAG_IA5String = 22 -ASN1_TAG_SEQUENCE = 0x30 -ASN1_TAG_SET = 0x31 +_ASN1_TAG_IA5String = 22 +_ASN1_TAG_SEQUENCE = 0x30 +_ASN1_TAG_SET = 0x31 @@ -771,7 +864,7 @@ def gmssl_parse_attr_type_and_value(name, d, dlen): oid_name = gmssl.x509_name_type_name(oid).decode('ascii') if oid_name == 'emailAddress': - if gmssl.asn1_ia5_string_from_der_ex(ASN1_TAG_IA5String, byref(val), byref(vlen), byref(d), byref(dlen)) != 1: + if gmssl.asn1_ia5_string_from_der_ex(_ASN1_TAG_IA5String, byref(val), byref(vlen), byref(d), byref(dlen)) != 1: raise InnerError('libgmssl inner error') else: if gmssl.x509_directory_name_from_der(byref(tag), byref(val), byref(vlen), byref(d), byref(dlen)) != 1: @@ -791,7 +884,7 @@ def gmssl_parse_rdn(name, d, dlen): vlen = c_size_t() while dlen.value > 0: - if gmssl.asn1_type_from_der(ASN1_TAG_SEQUENCE, byref(v), byref(vlen), byref(d), byref(dlen)) != 1: + if gmssl.asn1_type_from_der(_ASN1_TAG_SEQUENCE, byref(v), byref(vlen), byref(d), byref(dlen)) != 1: raise InnerError('libgmssl inner error') if gmssl_parse_attr_type_and_value(name, v, vlen) != 1: @@ -805,11 +898,19 @@ def gmssl_parse_name(name, d, dlen): vlen = c_size_t() while dlen.value > 0: - if gmssl.asn1_nonempty_type_from_der(c_int(ASN1_TAG_SET), byref(v), byref(vlen), byref(d), byref(dlen)) != 1: + if gmssl.asn1_nonempty_type_from_der(c_int(_ASN1_TAG_SET), byref(v), byref(vlen), byref(d), byref(dlen)) != 1: raise InnerError('libgmssl inner error') gmssl_parse_rdn(name, v, vlen) return True + +class Validity: + + def __init__(self, not_before, not_after): + self.not_before = datetime.datetime.fromtimestamp(not_before) + self.not_after = datetime.datetime.fromtimestamp(not_after) + + class Sm2Certificate: def import_pem(self, path): @@ -821,22 +922,16 @@ def import_pem(self, path): self._cert = create_string_buffer(certlen.value) libc.memcpy(self._cert, cert, certlen) - - #libc.fopen.restype = c_void_p - #fp = libc.fopen(path.encode('utf-8'), 'rb') - #if gmssl.x509_cert_from_pem(self._cert, byref(certlen), len(self._cert), c_void_p(fp)) != 1: - # raise InnerError('libgmssl inner error') - + libc.free(cert) def get_raw(self): return self._cert; def export_pem(self, path): libc.fopen.restype = c_void_p - fp = libc.fopen(file.encode('utf-8'), 'wb') + fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.x509_cert_to_pem(self._cert, len(self._cert), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') - return True def get_serial_number(self): @@ -859,9 +954,7 @@ def get_issuer(self): libc.memcpy(issuer_raw, issuer_ptr, issuer_len) issuer = { "raw_data" : issuer_raw } - gmssl_parse_name(issuer, issuer_ptr, issuer_len) - return issuer def get_subject(self): @@ -873,50 +966,25 @@ def get_subject(self): libc.memcpy(subject_raw, subject_ptr, subject_len) subject = { "raw_data" : subject_raw } - gmssl_parse_name(subject, subject_ptr, subject_len) - return subject def get_subject_public_key(self): public_key = Sm2Key() gmssl.x509_cert_get_subject_public_key(self._cert, len(self._cert), byref(public_key)) - # fixme: public key or private key + public_key._has_private_key = False + public_key._has_public_key = True return public_key - def get_not_before(self): + def get_validity(self): not_before = c_ulong() - gmssl.x509_cert_get_details(self._cert, len(self._cert), - None, - None, None, - None, - None, None, - byref(not_before), None, - None, None, - None, - None, None, - None, None, - None, None, - None, - None, None) - return datetime.datetime.fromtimestamp(not_before.value) - - def get_not_after(self): not_after = c_ulong() - gmssl.x509_cert_get_details(self._cert, len(self._cert), - None, - None, None, - None, - None, None, - None, byref(not_after), - None, None, - None, - None, None, - None, None, - None, None, - None, - None, None) - return datetime.datetime.fromtimestamp(not_after.value) + if gmssl.x509_cert_get_details(self._cert, len(self._cert), + None, None, None, None, None, None, + byref(not_before), byref(not_after), + None, None, None, None, None, None, None, None, None, None, None, None) != 1: + raise InnerError('libgmssl inner error') + return Validity(not_before.value, not_after.value) def verify_by_ca_certificate(self, cacert, sm2_id): @@ -931,4 +999,3 @@ def verify_by_ca_certificate(self, cacert, sm2_id): return True - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..be6cab8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + + +[project] +name = "gmssl_python" +version = "2.2.0" +authors = [ + { name="Zhi Guan", email="guan@pku.edu.cn" }, +] +description = "Python binding of the GmSSL library with ctypes" +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", +] + +[project.urls] +"Homepage" = "https://gmssl.github.io/GmSSL-Python/" +"Bug Tracker" = "https://github.com/GmSSL/GmSSL-Python/issues" diff --git a/setup.py b/setup.py deleted file mode 100644 index 07464b7..0000000 --- a/setup.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2023 The GmSSL Project. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the License); you may -# not use this file except in compliance with the License. -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# pyGmSSL - the Python binding of the GmSSL library - - -from distutils.core import setup - -setup( - name='GmSSL-Python', - version='2.1', - py_modules=['gmssl'], -) diff --git a/test.py b/test.py index 7455e7e..ce80cf2 100644 --- a/test.py +++ b/test.py @@ -6,9 +6,6 @@ # not use this file except in compliance with the License. # # http://www.apache.org/licenses/LICENSE-2.0 -# -# pyGmSSL - the Python binding of the GmSSL library - import unittest @@ -16,19 +13,21 @@ class TestGmSSL(unittest.TestCase): + def test_version(self): + self.assertTrue(gmssl_library_version_num() > 0) + self.assertTrue(len(GMSSL_LIBRARY_VERSION) > 0) + self.assertTrue(len(GMSSL_PYTHON_VERSION) > 0) + def test_rand(self): keylen = 20 key = rand_bytes(keylen) self.assertEqual(len(key), keylen) - #print("version") - #print(GMSSL_LIBRARY_VERSION) def test_sm3(self): dgst_hex = '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0' sm3 = Sm3() sm3.update(b'abc') dgst = sm3.digest() - #print(dgst.hex()) self.assertEqual(dgst, bytes.fromhex(dgst_hex)) def test_sm3_hmac(self): @@ -36,7 +35,7 @@ def test_sm3_hmac(self): mac_hex = '0a69401a75c5d471f5166465eec89e6a65198ae885c1fdc061556254d91c1080' sm3_hmac = Sm3Hmac(key) sm3_hmac.update(b'abc') - mac = sm3_hmac.generateMac() + mac = sm3_hmac.generate_mac() self.assertEqual(mac, bytes.fromhex(mac_hex)) def test_sm3_pbkdf2(self): @@ -44,110 +43,119 @@ def test_sm3_pbkdf2(self): salt = b'12345678' iterator = 10000 keylen = 32 + keyhex = 'ac5b4a93a130252181434970fa9d8e6f1083badecafc4409aaf0097c813e9fc6' key = sm3_pbkdf2(passwd, salt, iterator, keylen) - self.assertEqual(len(key), keylen) + self.assertEqual(key, bytes.fromhex(keyhex)) def test_sm4(self): key = b'1234567812345678' plaintext = b'block of message' - sm4 = Sm4(key, True) + ciphertext_hex = 'dd99d30fd7baf5af2930335d2554ddb7' + sm4 = Sm4(key, DO_ENCRYPT) ciphertext = sm4.encrypt(plaintext) - sm4 = Sm4(key, False) + self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) + sm4 = Sm4(key, DO_DECRYPT) decrypted = sm4.encrypt(ciphertext) self.assertEqual(decrypted, plaintext) def test_sm4_cbc(self): key = b'1234567812345678' - iv = b'0000000000000000' + iv = b'1234567812345678' plaintext = b'abc' - sm4_cbc = Sm4Cbc(key, iv, True) + ciphertext_hex = '532b22f9a096e7e5b8d84a620f0f7078' + sm4_cbc = Sm4Cbc(key, iv, DO_ENCRYPT) ciphertext = sm4_cbc.update(plaintext) ciphertext += sm4_cbc.finish() - sm4_cbc = Sm4Cbc(key, iv, False) + self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) + sm4_cbc = Sm4Cbc(key, iv, DO_DECRYPT) decrypted = sm4_cbc.update(ciphertext) decrypted += sm4_cbc.finish() self.assertEqual(decrypted, plaintext) def test_sm4_ctr(self): key = b'1234567812345678' - iv = b'0000000000000000' + iv = b'1234567812345678' plaintext = b'abc' + ciphertext_hex = '890106' sm4_ctr = Sm4Ctr(key, iv) ciphertext = sm4_ctr.update(plaintext) ciphertext += sm4_ctr.finish() - + self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) sm4_ctr = Sm4Ctr(key, iv) decrypted = sm4_ctr.update(ciphertext) decrypted += sm4_ctr.finish() self.assertEqual(decrypted, plaintext) + def test_sm4_gcm(self): + key = b'1234567812345678' + iv = b'0123456789ab' + aad = b'Additional Authenticated Data' + taglen = 16 + plaintext = b'abc' + ciphertext_hex = '7d8bd8fdc7ea3b04c15fb61863f2292c15eeaa' + sm4_gcm = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) + ciphertext = sm4_gcm.update(plaintext) + ciphertext += sm4_gcm.finish() + self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) + sm4_gcm = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) + decrypted = sm4_gcm.update(ciphertext) + decrypted += sm4_gcm.finish() + self.assertEqual(decrypted, plaintext) + def test_zuc(self): key = b'1234567812345678' - iv = b'0000000000000000' + iv = b'1234567812345678' plaintext = b'abc' + ciphertext_hex = '3d144b' zuc = Zuc(key, iv) ciphertext = zuc.update(plaintext) ciphertext += zuc.finish() - + self.assertEqual(ciphertext, bytes.fromhex(ciphertext_hex)) zuc = Zuc(key, iv) decrypted = zuc.update(ciphertext) decrypted += zuc.finish() self.assertEqual(decrypted, plaintext) - def test_sm4_gcm(self): - key = b'1234567812345678' - iv = b'0000000000000000' - aad = b'AAD data' - taglen = 16 - plaintext = b'abc' - sm4_gcm = Sm4Gcm(key, iv, aad, taglen, True) - ciphertext = sm4_gcm.update(plaintext) - ciphertext += sm4_gcm.finish() - - sm4_gcm = Sm4Gcm(key, iv, aad, taglen, False) - decrypted = sm4_gcm.update(ciphertext) - decrypted += sm4_gcm.finish() - self.assertEqual(decrypted, plaintext) - def test_sm2_key(self): - sm3 = Sm3() - sm3.update(b'abc') - dgst = sm3.digest() - + dgst = bytes.fromhex('66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0') + plaintext = b'abc' sm2 = Sm2Key() sm2.generate_key() - sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') - + sm2.export_public_key_info_pem('sm2pub.pem') sm2pri = Sm2Key() sm2pri.import_encrypted_private_key_info_pem('sm2.pem', 'password') - - sm2.export_public_key_info_pem('sm2pub.pem') - sm2pub = Sm2Key() sm2pub.import_public_key_info_pem("sm2pub.pem"); - - z = sm2.compute_z('1234567812345678') - #print(z.hex()) - sig = sm2pri.sign(dgst) verify_ret = sm2pub.verify(dgst, sig) self.assertTrue(verify_ret) - - plaintext = b'abc' ciphertext = sm2pub.encrypt(plaintext) decrypted = sm2pri.decrypt(ciphertext) self.assertEqual(decrypted, plaintext) + def test_sm2_id(self): + pem_txt = '''\ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE+XVF76aof3ZtBVUwXobDQwQn+Sb2 +ethykPiYkXDLFdLnTrqr0b9QuA63DPdyrxJS3LZZwp9qzaMSyStai8+nrQ== +-----END PUBLIC KEY-----''' + with open('pub.pem', 'w') as file: + file.write(pem_txt) + file.close() + z_hex = '4e469c92c425960603a315491bb2181c2f25939172775e223e1759b413cfc8ba' + sm2pub = Sm2Key() + sm2pub.import_public_key_info_pem('pub.pem') + z = sm2pub.compute_z(SM2_DEFAULT_ID) + self.assertEqual(z, bytes.fromhex(z_hex)) + def test_sm2_sig(self): sm2 = Sm2Key() sm2.generate_key() - - sign = Sm2Signature(sm2, SM2_DEFAULT_ID, True) + sign = Sm2Signature(sm2, SM2_DEFAULT_ID, DO_SIGN) sign.update(b'abc') sig = sign.sign() - - verify = Sm2Signature(sm2, SM2_DEFAULT_ID, False) + verify = Sm2Signature(sm2, SM2_DEFAULT_ID, DO_VERIFY) verify.update(b'abc') verify_ret = verify.verify(sig) self.assertTrue(verify_ret) @@ -157,14 +165,11 @@ def test_sm9_enc(self): master_key.generate_master_key() master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') master_key.export_public_master_key_pem('enc_mpk.pem') - master_pub = Sm9EncMasterKey() master_pub.import_public_master_key_pem('enc_mpk.pem') ciphertext = master_pub.encrypt(b'plaintext', 'Alice') - master = Sm9EncMasterKey() master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') - key = master.extract_key('Alice') plaintext = key.decrypt(ciphertext) self.assertEqual(plaintext, b'plaintext') @@ -174,27 +179,22 @@ def test_sm9_sign(self): master_key.generate_master_key() master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') master_key.export_public_master_key_pem('sign_mpk.pem') - - master = Sm9SignMasterKey() master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') - key = master.extract_key('Alice') - - sign = Sm9Signature(True) + sign = Sm9Signature(DO_SIGN) sign.update(b'message') sig = sign.sign(key) - master_pub = Sm9SignMasterKey() master_pub.import_public_master_key_pem('sign_mpk.pem') - - verify = Sm9Signature(False) + verify = Sm9Signature(DO_VERIFY) verify.update(b'message') ret = verify.verify(sig, master_pub, 'Alice') self.assertTrue(ret) def test_sm2_cert(self): - cert_txt = '''-----BEGIN CERTIFICATE----- + cert_txt = '''\ +-----BEGIN CERTIFICATE----- MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO @@ -206,33 +206,23 @@ def test_sm2_cert(self): 53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI pDoiVhsLwg== -----END CERTIFICATE-----''' - - with open('ROOTCA2.pem', 'w') as file: + with open('ROOTCA.pem', 'w') as file: file.write(cert_txt) file.close() - cert = Sm2Certificate() - cert.import_pem('ROOTCA2.pem') - + cert.import_pem('ROOTCA.pem') serial = cert.get_serial_number() - #print(serial.hex()) - - not_before = cert.get_not_before() - #print(not_before) - - not_after = cert.get_not_after() - #print(not_after) - - public_key = cert.get_subject_public_key() - public_key.export_public_key_info_pem('public_key.pem') - + self.assertTrue(len(serial) > 0) + validity = cert.get_validity() + self.assertTrue(validity.not_before < validity.not_after) issuer = cert.get_issuer() - #print(issuer) - + self.assertTrue(len(issuer) > 1) subject = cert.get_subject() - #print(subject) - + self.assertTrue(len(subject) > 1) + public_key = cert.get_subject_public_key() + public_key.export_public_key_info_pem('public_key.pem') + public_key.import_public_key_info_pem('public_key.pem') ret = cert.verify_by_ca_certificate(cert, SM2_DEFAULT_ID) self.assertTrue(ret) @@ -240,5 +230,3 @@ def test_sm2_cert(self): if __name__ == '__main__': unittest.main() - - From f98a49998ba67e3791d060a6614894947b44da3b Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Wed, 20 Sep 2023 19:32:41 +0800 Subject: [PATCH 06/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7810fbb..f40933b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## 简介 -`gmssl-python`是GmSSL密码库(https://gmssl.com/guanzhi/GmSSL)的Python语言封装,以`ctypes`方式实现,通过Python类和函数提供了如下密码接口: +`gmssl-python`是GmSSL密码库 https://github.com/guanzhi/GmSSL 的Python语言封装,以`ctypes`方式实现,通过Python类和函数提供了如下密码接口: * 密码随机数生成器 * SM2加密和签名,SM2密钥生成、私钥口令加密保护、密钥PEM文件导入导出 From 13a9766da61dc78e18a216ae263ae60ed6b33b70 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 21 Sep 2023 10:09:26 +0800 Subject: [PATCH 07/18] Update README.md --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7810fbb..ebad22c 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,6 @@ $ pip show gmssl-python 解压缩并进入源代码目录`GmSSL-Python-main`。由于最新代码可能还处于开发过程中,在安装前必须进行测试确保全部功能正确,`gmssl-python`中提供了测试,执行如下命令 -在安装过程中会产生`deprecated`警告,对于Python 3.11及之前的版本可忽略此警告,并可以顺利编译完成。 - 运行测试 ```bash @@ -123,3 +121,130 @@ echo -n abc | gmssl sm3 ``` 可以看到输出相同的SM3哈希值 + + + + + +## 开发手册 + +### 随机数生成器 + +函数`rand_bytes`实现随机数生成功能。 + +```python +rand_bytes(size : int) -> bytes +``` + +输入参数`size` 是输出字节数组长度,返回值为`size`长度的随机字节数组。 + +通过`rand_bytes`方法生成的是具备密码安全性的随机数,可以用于密钥、IV或者其他随机数生成器的随机种子。 + +```python +>>> import gmssl +>>> key = gmssl.rand_bytes(16) +>>> print(key.hex()) +``` + +`rand_bytes`是通过调用操作系统的密码随机数生成器(如`/dev/urandom`)实现的。由于底层操作系统的限制,在一次调用`rand_bytes`时不要指定明显超过密钥长度的输出长度,例如参数`size`的值不要超过128,否则可能导致阻塞,或者产生错误和异常。如果应用需要大量的随机数据,不应使用`rand_bytes`,而是应该考虑其他伪随机数生成算法。 + +需要注意的是,`rand_bytes`的安全性依赖于底层的操作系统随机数生成器的安全性。在服务器、笔记本等主流硬件和Windows、Linux、Mac主流服务器、桌面操作系统环境上,当计算机已经启动并且经过一段时间的用户交互和网络通信后,`rand_bytes`可以输出高质量的随机数。但是在缺乏用户交互和网络通信的嵌入式设备中,`rand_bytes`返回的随机数可能存在随机性不足的问题,在这些特殊的环境中,开发者需要提前或在运行时检测`rand_bytes`是否能够提供具有充分的随机性。 + +### SM3哈希 + +SM3密码杂凑函数可以将任意长度的输入数据计算为固定32字节长度的哈希值。 + +模块`gmssl`中包含如下SM3的常量 + +* `SM3_DIGEST_SIZE` 即SM3哈希值的字节长度 + +类`Sm3`实现了SM3功能,类`Sm3`的对象是由构造函数生成的 + +``` +gmssl.Sm3() +``` + +对象sm3的方法: + +* `sm3.update(data : bytes)` 要哈希的消息是通过`update`方法输入的,输入`data`的数据类型是`bytes`类型,如果输入的数据是字符串,需要通过字符串的`encode`方法转换成`bytes`,否则无法生成正确的哈希值。 +* `sm3.digest() -> bytes` 在通过`update`输入完所有消息后,就可以通过`digest`方法获得输出的哈希值,输出的结果类型为`bytes`类型,长度为`SM3_DIGEST_SIZE`。 +* `sm3.reset()` 在SM3对象完成一个消息的哈希后,可以通过`reset`方法重置对象状态,效果等同于构造函数,重置后可以通过`update`、`digest`计算新一个消息的哈希值。`reset`方法使得应用可以只创建一个`Sm3`的对象,计算任意数量的哈希值。 + +下面的例子展示了如何通过类`Sm3`计算字符串的SM3哈希值。 + +```Python +>>> from gmssl import * +>>> sm3 = Sm3() +>>> sm3.update(b'abc') +>>> sm3.digest().hex() +``` + +注意这里提供的消息字符串是`bytes`格式的。这个例子的源代码在`examples/sm3.py`文件中,编译并运行这个例子。 + +```bash +$ python examples/sm3.py +``` + +打印出的`66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0`就是字符串`abc`的哈希值。字符串`abc`的哈希值也是SM3标准文本中给出的第一个测试数据,通过对比标准文本可以确定这个哈希值是正确的。 + +也可以通过`gmssl`命令行来验证`Sm3`类的计算是正确的。 + +```bash +$ echo -n abc | gmssl sm3 +66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 +``` + +可以看到输出的结果是一样。 + +注意,如果将字符串`abc`写入到文本文件中,文本编辑器通常会在文本结尾处增加格外的结束符,如`0x0a`字符,那么计算出的哈希值将不是上面的结果,比如可能是`12d4e804e1fcfdc181ed383aa07ba76cc69d8aedcbb7742d6e28ff4fb7776c34`。如果命令`echo`不使用`-n`的参数,也会出现同样的错误。这是很多开发者在初次进行哈希函数开发时容易遇到的错误,哈希函数的安全性质保证,即使输入的消息只差一个比特,那么输出的哈希值也完全不同。 + +如果需要哈希的数据来自于网络或者文件,那么应用可能需要多次读取才能获得全部的数据。在通过`Sm3`计算哈希值时,应用不需要通过保存一个缓冲区来保存全部的数据,而是可以通过多次调用`update`方法,将数据输入给`Sm3`对象,在数据全都输入完之后,最后调用`digest`方法得到全部数据的SM3哈希值。下面的代码片段展示了这一用法。 + +```python +>>> from gmssl import * +>>> sm3 = Sm3() +>>> sm3.update(b"Hello ") +>>> sm3.update(b"world!") +>>> dgst = sm3.digest() +``` + +这个例子中两次调用了`update`方法,效果等同于 + +```python +sm3.update(b"Hello world!"); +``` + +注意,SM3算法也支持生成空数据的哈希值,因此下面的代码片段也是合法的。 + +```java +>>> from gmssl import * +>>> sm3 = Sm3() +>>> dgst = sm3.digest() +``` + +GmSSL-Python其他类的`update`方法通常也都提供了这种形式的接口。在输入完所有的数据之后,通过调用`digest`方法就可以获得所有输入数据的SM3哈希值了。`digest`方法输出的是长度为`SM3_DIGEST_SIZE`字节(即32字节)的二进制哈希值。 + +如果应用要计算多组数据的不同SM3哈希值,可以通过`reset`方法重置`Sm3`对象的状态,然后可以再次调用`update`和`digest`方法计算新一组数据的哈希值。这样只需要一个`Sm3`对象就可以完成多组哈希值的计算。 + +```python +>>> from gmssl import * +>>> sm3 = Sm3() +>>> sm3.update(b"abc") +>>> dgst1 = sm3.digest() +>>> +>>> sm3.reset() +>>> sm3.update(b"Hello ") +>>> sm3.update(b"world!") +>>> dgst2 = sm3.digest() +``` + +GmSSL-Python的部分其他类也提供了`reset`方法。 + + + + + + + + + From 74895a09d470c6c85146b985b5faec4175dff657 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 21 Sep 2023 20:49:01 +0800 Subject: [PATCH 08/18] Add examples --- examples/demo.py | 7 ------- examples/sm3.py | 22 ++++++++++++++++++++++ examples/sm3_hmac.py | 27 +++++++++++++++++++++++++++ examples/sm3_pbkdf2.py | 30 ++++++++++++++++++++++++++++++ examples/sm4.py | 27 +++++++++++++++++++++++++++ examples/sm4_cbc.py | 31 +++++++++++++++++++++++++++++++ examples/sm4_ctr.py | 31 +++++++++++++++++++++++++++++++ examples/sm4_gcm.py | 40 ++++++++++++++++++++++++++++++++++++++++ examples/zuc.py | 31 +++++++++++++++++++++++++++++++ 9 files changed, 239 insertions(+), 7 deletions(-) delete mode 100644 examples/demo.py create mode 100644 examples/sm3.py create mode 100644 examples/sm3_hmac.py create mode 100644 examples/sm3_pbkdf2.py create mode 100644 examples/sm4.py create mode 100644 examples/sm4_cbc.py create mode 100644 examples/sm4_ctr.py create mode 100644 examples/sm4_gcm.py create mode 100644 examples/zuc.py diff --git a/examples/demo.py b/examples/demo.py deleted file mode 100644 index 8059c6f..0000000 --- a/examples/demo.py +++ /dev/null @@ -1,7 +0,0 @@ - -from gmssl import * - -sm3 = Sm3() -sm3.update(b'abc') -dgst = sm3.digest() -print("sm3('abc') : " + dgst.hex()) diff --git a/examples/sm3.py b/examples/sm3.py new file mode 100644 index 0000000..d16c104 --- /dev/null +++ b/examples/sm3.py @@ -0,0 +1,22 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("SM3_DIGEST_SIZE =", SM3_DIGEST_SIZE) + +sm3 = Sm3() +sm3.update(b'abc') +dgst = sm3.digest() +print("sm3('abc') : " + dgst.hex()) + +sm3.reset() +for i in range(16): + sm3.update(b'abcd') +dgst = sm3.digest() +print("sm3('abcd'*16) : " + dgst.hex()) + diff --git a/examples/sm3_hmac.py b/examples/sm3_hmac.py new file mode 100644 index 0000000..9315bab --- /dev/null +++ b/examples/sm3_hmac.py @@ -0,0 +1,27 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("SM3_HMAC_MIN_KEY_SIZE =", SM3_HMAC_MIN_KEY_SIZE) +print("SM3_HMAC_MAX_KEY_SIZE =", SM3_HMAC_MAX_KEY_SIZE) +print("SM3_HMAC_SIZE =", SM3_HMAC_SIZE) + +key = rand_bytes(SM3_HMAC_MIN_KEY_SIZE) + +sm3_hmac = Sm3Hmac(key) +sm3_hmac.update(b'abc') +mac = sm3_hmac.generate_mac() +print("key = " + key.hex()) +print("sm3_hmac('abc') : " + mac.hex()) + +sm3_hmac.reset(key) +for i in range(16): + sm3_hmac.update(b'abcd') +mac = sm3_hmac.generate_mac() +print("sm3_hmac('abcd'*16) : " + mac.hex()) + diff --git a/examples/sm3_pbkdf2.py b/examples/sm3_pbkdf2.py new file mode 100644 index 0000000..40aabb3 --- /dev/null +++ b/examples/sm3_pbkdf2.py @@ -0,0 +1,30 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + + +from gmssl import * + + +print("SM3_PBKDF2_MIN_ITER =", SM3_PBKDF2_MIN_ITER) +print("SM3_PBKDF2_MAX_ITER =", SM3_PBKDF2_MAX_ITER) +print("SM3_PBKDF2_MAX_SALT_SIZE =", SM3_PBKDF2_MAX_SALT_SIZE) +print("SM3_PBKDF2_DEFAULT_SALT_SIZE =", SM3_PBKDF2_DEFAULT_SALT_SIZE) +print("SM3_PBKDF2_MAX_KEY_SIZE =", SM3_PBKDF2_MAX_KEY_SIZE) +print("") + +passwd = "Password" +salt = rand_bytes(SM3_PBKDF2_DEFAULT_SALT_SIZE) +iterator = SM3_PBKDF2_MIN_ITER +keylen = 32 + +key = sm3_pbkdf2(passwd, salt, iterator, keylen) +print("Password :", passwd) +print("Salt :", salt.hex()) +print("Iterator :", iterator) +print("Keylen :", keylen) +print("sm2_pbkdf2(Password, Salt, Iter, Keylen) :", key.hex()) + diff --git a/examples/sm4.py b/examples/sm4.py new file mode 100644 index 0000000..08458c3 --- /dev/null +++ b/examples/sm4.py @@ -0,0 +1,27 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("SM4_KEY_SIZE =", SM4_KEY_SIZE) +print("SM4_BLOCK_SIZE =", SM4_BLOCK_SIZE) +print("") + +key = rand_bytes(SM4_KEY_SIZE) +plaintext = rand_bytes(SM4_BLOCK_SIZE) + +sm4_enc = Sm4(key, DO_ENCRYPT) +ciphertext = sm4_enc.encrypt(plaintext) + +sm4_dec = Sm4(key, DO_DECRYPT) +decrypted = sm4_dec.encrypt(ciphertext) + +print("key =", key.hex()) +print("plaintext =", plaintext.hex()) +print("ciphertext = ", ciphertext.hex()) +print("decrypted =", decrypted.hex()) + diff --git a/examples/sm4_cbc.py b/examples/sm4_cbc.py new file mode 100644 index 0000000..60ec98e --- /dev/null +++ b/examples/sm4_cbc.py @@ -0,0 +1,31 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("SM4_KEY_SIZE =", SM4_KEY_SIZE) +print("SM4_CBC_IV_SIZE =", SM4_CBC_IV_SIZE) +print("") + +key = rand_bytes(SM4_KEY_SIZE) +iv = rand_bytes(SM4_CBC_IV_SIZE) +plaintext = b'abc' + +sm4_enc = Sm4Cbc(key, iv, DO_ENCRYPT) +ciphertext = sm4_enc.update(plaintext) +ciphertext += sm4_enc.finish() + +sm4_dec = Sm4Cbc(key, iv, DO_DECRYPT) +decrypted = sm4_dec.update(ciphertext) +decrypted += sm4_dec.finish() + +print("key =", key.hex()) +print("iv =", iv.hex()) +print("plaintext =", plaintext.hex()) +print("ciphertext = ", ciphertext.hex()) +print("decrypted =", decrypted.hex()) + diff --git a/examples/sm4_ctr.py b/examples/sm4_ctr.py new file mode 100644 index 0000000..06fc8a2 --- /dev/null +++ b/examples/sm4_ctr.py @@ -0,0 +1,31 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("SM4_KEY_SIZE =", SM4_KEY_SIZE) +print("SM4_CTR_IV_SIZE =", SM4_CTR_IV_SIZE) +print("") + +key = rand_bytes(SM4_KEY_SIZE) +iv = rand_bytes(SM4_CTR_IV_SIZE) +plaintext = b'abc' + +sm4_enc = Sm4Ctr(key, iv) +ciphertext = sm4_enc.update(plaintext) +ciphertext += sm4_enc.finish() + +sm4_dec = Sm4Ctr(key, iv) +decrypted = sm4_dec.update(ciphertext) +decrypted += sm4_dec.finish() + +print("key =", key.hex()) +print("iv =", iv.hex()) +print("plaintext =", plaintext.hex()) +print("ciphertext = ", ciphertext.hex()) +print("decrypted =", decrypted.hex()) + diff --git a/examples/sm4_gcm.py b/examples/sm4_gcm.py new file mode 100644 index 0000000..30a1213 --- /dev/null +++ b/examples/sm4_gcm.py @@ -0,0 +1,40 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + + +print("SM4_GCM_MIN_IV_SIZE =", SM4_GCM_MIN_IV_SIZE) +print("SM4_GCM_MAX_IV_SIZE =", SM4_GCM_MAX_IV_SIZE) +print("SM4_GCM_DEFAULT_IV_SIZE =", SM4_GCM_DEFAULT_IV_SIZE) +print("SM4_GCM_DEFAULT_TAG_SIZE =", SM4_GCM_DEFAULT_TAG_SIZE) +print("SM4_GCM_MAX_TAG_SIZE =", SM4_GCM_MAX_TAG_SIZE) +print("") + + +key = rand_bytes(SM4_KEY_SIZE) +iv = rand_bytes(SM4_GCM_DEFAULT_IV_SIZE) +aad = b'Additional auth-data' +plaintext = b'abc' +taglen = SM4_GCM_DEFAULT_TAG_SIZE + +sm4_enc = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) +ciphertext = sm4_enc.update(plaintext) +ciphertext += sm4_enc.finish() + +sm4_dec = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) +decrypted = sm4_dec.update(ciphertext) +decrypted += sm4_dec.finish() + +print("key =", key.hex()) +print("iv =", iv.hex()) +print("aad =", aad.hex()) +print("taglen =", taglen) +print("plaintext =", plaintext.hex()) +print("ciphertext = ", ciphertext.hex()) +print("decrypted =", decrypted.hex()) + diff --git a/examples/zuc.py b/examples/zuc.py new file mode 100644 index 0000000..5d062fe --- /dev/null +++ b/examples/zuc.py @@ -0,0 +1,31 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +print("ZUC_KEY_SIZE =", ZUC_KEY_SIZE) +print("ZUC_IV_SIZE =", ZUC_IV_SIZE) +print("") + +key = rand_bytes(ZUC_KEY_SIZE) +iv = rand_bytes(ZUC_IV_SIZE) +plaintext = b'abc' + +zuc_enc = Zuc(key, iv) +ciphertext = zuc_enc.update(plaintext) +ciphertext += zuc_enc.finish() + +zuc_dec = Zuc(key, iv) +decrypted = zuc_dec.update(ciphertext) +decrypted += zuc_dec.finish() + +print("key =", key.hex()) +print("iv =", iv.hex()) +print("plaintext =", plaintext.hex()) +print("ciphertext = ", ciphertext.hex()) +print("decrypted =", decrypted.hex()) + From 303c5b250150600696e820648f605bd67205f8c3 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 21 Sep 2023 23:22:03 +0800 Subject: [PATCH 09/18] Add SM2 and Certificate examples --- .gitignore | 2 +- examples/sm2_cert.py | 65 ++++++++++++++++++++++++++++++++++++++++++++ examples/sm2_enc.py | 33 ++++++++++++++++++++++ examples/sm2_key.py | 28 +++++++++++++++++++ examples/sm2_sign.py | 61 +++++++++++++++++++++++++++++++++++++++++ gmssl.py | 4 +-- 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 examples/sm2_cert.py create mode 100644 examples/sm2_enc.py create mode 100644 examples/sm2_key.py create mode 100644 examples/sm2_sign.py diff --git a/.gitignore b/.gitignore index 9967b3f..b795e33 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,4 @@ dmypy.json # Pyre type checker .pyre/ *.pem - +.DS_Store diff --git a/examples/sm2_cert.py b/examples/sm2_cert.py new file mode 100644 index 0000000..1352e6e --- /dev/null +++ b/examples/sm2_cert.py @@ -0,0 +1,65 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +cert_txt = '''\ +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE-----''' +with open('ROOTCA.pem', 'w') as file: + file.write(cert_txt) + file.close() + +cert = Sm2Certificate() +cert.import_pem('ROOTCA.pem') + +print("Certificate") + +serial = cert.get_serial_number() +print("Serial :", serial.hex()) + +validity = cert.get_validity() +print("Validity.notBefore :", validity.not_before) +print("Validity.notAfter :", validity.not_after) + +issuer = cert.get_issuer() +print("Issuer :") +for key in issuer: + if key == 'raw_data': + print(" ", key, ":", issuer[key].hex()) + else: + print(" ", key, ":", issuer[key]) + + +subject = cert.get_subject() +print("Subject :") +for key in subject: + if key == 'raw_data': + print(" ", key, ":", subject[key].hex()) + else: + print(" ", key, ":", subject[key]) + +public_key = cert.get_subject_public_key() +public_key.export_public_key_info_pem('subject_public_key.pem') + +file = open('subject_public_key.pem',mode='r') +fulltext = file.read() +file.close() +print("Subject Public Key:") +print(fulltext) + + diff --git a/examples/sm2_enc.py b/examples/sm2_enc.py new file mode 100644 index 0000000..1b7a7b3 --- /dev/null +++ b/examples/sm2_enc.py @@ -0,0 +1,33 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +# run sm2_key.py first + + + +# Sender + +public_key = Sm2Key() +public_key.import_public_key_info_pem('sm2pub.pem') + +plaintext = b'Plaintext message' +ciphertext = public_key.encrypt(plaintext) + + +# Receiver + +private_key = Sm2Key() +private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') + +decrypted = private_key.decrypt(ciphertext) + +print("plaintext :", plaintext.hex()) +print("ciphertext :", ciphertext.hex()) +print("decrypted :", decrypted.hex()) + diff --git a/examples/sm2_key.py b/examples/sm2_key.py new file mode 100644 index 0000000..625182e --- /dev/null +++ b/examples/sm2_key.py @@ -0,0 +1,28 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +sm2 = Sm2Key() +sm2.generate_key() + +sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') +print('export private key to encrypted file sm2.pem') + +sm2.export_public_key_info_pem('sm2pub.pem') +print('export public key to file sm2pub.pem') + +private_key = Sm2Key() +private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') +print("private key has private key :", private_key.has_private_key()) +print("private key has public key :", private_key.has_public_key()) + +public_key = Sm2Key() +public_key.import_public_key_info_pem('sm2pub.pem') +print("public key has private key :", public_key.has_private_key()) +print("public key has public key :", public_key.has_public_key()) + diff --git a/examples/sm2_sign.py b/examples/sm2_sign.py new file mode 100644 index 0000000..8a01cbc --- /dev/null +++ b/examples/sm2_sign.py @@ -0,0 +1,61 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + +# run sm2_key.py first + + +# Signer + +private_key = Sm2Key() +private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') + +z = private_key.compute_z(SM2_DEFAULT_ID) + +sm3 = Sm3() +sm3.update(z) +sm3.update(b'abc') +dgst = sm3.digest() + +sig = private_key.sign(dgst) +print("signature1 :", sig.hex()) + +signer = Sm2Signature(private_key, SM2_DEFAULT_ID, DO_SIGN) +signer.update(b'abc') +sig2 = signer.sign() +print("signature2 :", sig2.hex()) + +# Verifier + +public_key = Sm2Key() +public_key.import_public_key_info_pem('sm2pub.pem') + +z = public_key.compute_z(SM2_DEFAULT_ID) + +sm3 = Sm3() +sm3.update(z) +sm3.update(b'abc') +dgst = sm3.digest() + +ret = public_key.verify(dgst, sig) +print("Verify signature1 success :", ret) + +ret = public_key.verify(dgst, sig2) +print("Verify signature2 success :", ret) + +verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) +verifier.update(b'abc') +ret = verifier.verify(sig) +print("Verify signature1 success :", ret) + +verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) +verifier.update(b'abc') +ret = verifier.verify(sig2) +print("Verify signature2 success :", ret) + + diff --git a/gmssl.py b/gmssl.py index 167e44e..f9b37be 100755 --- a/gmssl.py +++ b/gmssl.py @@ -953,7 +953,7 @@ def get_issuer(self): issuer_raw = create_string_buffer(issuer_len.value) libc.memcpy(issuer_raw, issuer_ptr, issuer_len) - issuer = { "raw_data" : issuer_raw } + issuer = { "raw_data" : issuer_raw.raw } gmssl_parse_name(issuer, issuer_ptr, issuer_len) return issuer @@ -965,7 +965,7 @@ def get_subject(self): subject_raw = create_string_buffer(subject_len.value) libc.memcpy(subject_raw, subject_ptr, subject_len) - subject = { "raw_data" : subject_raw } + subject = { "raw_data" : subject_raw.raw } gmssl_parse_name(subject, subject_ptr, subject_len) return subject From 11578c6a3ce80d66e31230d02dc31eed9394e062 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 22 Sep 2023 08:12:23 +0800 Subject: [PATCH 10/18] Add SM9 examples --- examples/sm2_enc.py | 7 ++++++- examples/sm2_sign.py | 4 ++++ examples/sm9_enc.py | 46 +++++++++++++++++++++++++++++++++++++++++ examples/sm9_sign.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 examples/sm9_enc.py create mode 100644 examples/sm9_sign.py diff --git a/examples/sm2_enc.py b/examples/sm2_enc.py index 1b7a7b3..b4f7ff1 100644 --- a/examples/sm2_enc.py +++ b/examples/sm2_enc.py @@ -10,13 +10,18 @@ # run sm2_key.py first +print("SM2_MIN_PLAINTEXT_SIZE =", SM2_MIN_PLAINTEXT_SIZE) +print("SM2_MAX_PLAINTEXT_SIZE =", SM2_MAX_PLAINTEXT_SIZE) +print("SM2_MIN_CIPHERTEXT_SIZE =", SM2_MIN_CIPHERTEXT_SIZE) +print("SM2_MAX_CIPHERTEXT_SIZE =", SM2_MAX_CIPHERTEXT_SIZE) +print("") # Sender public_key = Sm2Key() public_key.import_public_key_info_pem('sm2pub.pem') -plaintext = b'Plaintext message' +plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) ciphertext = public_key.encrypt(plaintext) diff --git a/examples/sm2_sign.py b/examples/sm2_sign.py index 8a01cbc..9e3a12b 100644 --- a/examples/sm2_sign.py +++ b/examples/sm2_sign.py @@ -10,6 +10,10 @@ # run sm2_key.py first +print("SM2_DEFAULT_ID =", SM2_DEFAULT_ID) +print("SM2_MAX_SIGNATURE_SIZE =", SM2_MAX_SIGNATURE_SIZE) +print("") + # Signer private_key = Sm2Key() diff --git a/examples/sm9_enc.py b/examples/sm9_enc.py new file mode 100644 index 0000000..7e16526 --- /dev/null +++ b/examples/sm9_enc.py @@ -0,0 +1,46 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + + +print("SM9_MAX_ID_SIZE =", SM9_MAX_ID_SIZE) +print("SM9_MAX_PLAINTEXT_SIZE =", SM9_MAX_PLAINTEXT_SIZE) +print("SM9_MAX_CIPHERTEXT_SIZE =", SM9_MAX_CIPHERTEXT_SIZE) +print("") + +master_key = Sm9EncMasterKey() +master_key.generate_master_key() +print("SM9 master key generated") + +master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') +master_key.export_public_master_key_pem('enc_mpk.pem') +print("Export master key and public master key") + +# Encrypt +master_pub = Sm9EncMasterKey() +master_pub.import_public_master_key_pem('enc_mpk.pem') + +plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) + +receiver_id = 'Alice' + +ciphertext = master_pub.encrypt(plaintext, receiver_id) + +# Decrypt +master = Sm9EncMasterKey() +master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') + +receiver_key = master.extract_key(receiver_id) + +decrypted = receiver_key.decrypt(ciphertext) + +print("receiver :", receiver_id) +print("plaintext :", plaintext.hex()) +print("ciphertext:", ciphertext.hex()) +print("decrypted :", decrypted.hex()) + diff --git a/examples/sm9_sign.py b/examples/sm9_sign.py new file mode 100644 index 0000000..8728ce7 --- /dev/null +++ b/examples/sm9_sign.py @@ -0,0 +1,49 @@ +# Copyright 2023 The GmSSL Project. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the License); you may +# not use this file except in compliance with the License. +# +# http://www.apache.org/licenses/LICENSE-2.0 + +from gmssl import * + + +print("SM9_MAX_ID_SIZE =", SM9_MAX_ID_SIZE) +print("SM9_SIGNATURE_SIZE =", SM9_SIGNATURE_SIZE) +print("") + + +master_key = Sm9SignMasterKey() +master_key.generate_master_key() +print("SM9 master key generated") + +master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') +master_key.export_public_master_key_pem('sign_mpk.pem') +print("Export master key and public master key") + + +master = Sm9SignMasterKey() +master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') + +signer_id = 'Alice' +key = master.extract_key(signer_id) + +message = "Message to be signed" + +sign = Sm9Signature(DO_SIGN) +sign.update(message.encode('utf-8')) +sig = sign.sign(key) + + +master_pub = Sm9SignMasterKey() +master_pub.import_public_master_key_pem('sign_mpk.pem') + +verify = Sm9Signature(DO_VERIFY) +verify.update(message.encode('utf-8')) +ret = verify.verify(sig, master_pub, signer_id) + +print("signer :", signer_id) +print("message :", message) +print("signature :", sig.hex()) +print("verify success :", ret) + From be59f8a9ed306341c9f2415ffdd3bc75e2ff754b Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 22 Sep 2023 18:50:15 +0800 Subject: [PATCH 11/18] Add ctypes cast --- gmssl.py | 186 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 78 deletions(-) diff --git a/gmssl.py b/gmssl.py index f9b37be..1bcd5ac 100755 --- a/gmssl.py +++ b/gmssl.py @@ -38,7 +38,7 @@ def gmssl_library_version_str(): def rand_bytes(size): buf = create_string_buffer(size) - gmssl.rand_bytes(buf, size) + gmssl.rand_bytes(buf, c_size_t(size)) return buf.raw @@ -63,7 +63,7 @@ def reset(self): gmssl.sm3_init(byref(self)) def update(self, data): - gmssl.sm3_update(byref(self), data, len(data)) + gmssl.sm3_update(byref(self), data, c_size_t(len(data))) def digest(self): dgst = create_string_buffer(SM3_DIGEST_SIZE) @@ -85,15 +85,15 @@ class Sm3Hmac(Structure): def __init__(self, key): if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: raise ValueError('Invalid SM3 HMAC key length') - gmssl.sm3_hmac_init(byref(self), key, len(key)) + gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) def reset(self, key): if len(key) < SM3_HMAC_MIN_KEY_SIZE or len(key) > SM3_HMAC_MAX_KEY_SIZE: raise ValueError('Invalid SM3 HMAC key length') - gmssl.sm3_hmac_init(byref(self), key, len(key)) + gmssl.sm3_hmac_init(byref(self), key, c_size_t(len(key))) def update(self, data): - gmssl.sm3_hmac_update(byref(self), data, len(data)) + gmssl.sm3_hmac_update(byref(self), data, c_size_t(len(data))) def generate_mac(self): hmac = create_string_buffer(SM3_HMAC_SIZE) @@ -119,10 +119,11 @@ def sm3_pbkdf2(passwd, salt, iterator, keylen): if keylen > SM3_PBKDF2_MAX_KEY_SIZE: raise ValueError('Invalid key length') - passwd_bytes = passwd.encode('utf-8') + passwd = passwd.encode('utf-8') key = create_string_buffer(keylen) - if gmssl.pbkdf2_hmac_sm3_genkey(passwd_bytes, len(passwd_bytes), salt, len(salt), iterator, keylen, key) != 1: + if gmssl.pbkdf2_hmac_sm3_genkey(c_char_p(passwd), c_size_t(len(passwd)), + salt, c_size_t(len(salt)), c_size_t(iterator), c_size_t(keylen), key) != 1: raise InnerError('libgmssl inner error') return key.raw @@ -172,23 +173,24 @@ def __init__(self, key, iv, encrypt): raise ValueError('Invalid key length') if len(iv) != SM4_BLOCK_SIZE: raise ValueError('Invalid IV size') - if encrypt == True: - self._encrypt = True + if encrypt == DO_ENCRYPT: if gmssl.sm4_cbc_encrypt_init(byref(self), key, iv) != 1: raise InnerError('libgmssl inner error') else: - self._encrypt = False if gmssl.sm4_cbc_decrypt_init(byref(self), key, iv) != 1: raise InnerError('libgmssl inner error') + self._encrypt = encrypt def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if self._encrypt == True: - if gmssl.sm4_cbc_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if self._encrypt == DO_ENCRYPT: + if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm4_cbc_decrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -228,7 +230,8 @@ def __init__(self, key, iv): def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if gmssl.sm4_ctr_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -269,7 +272,8 @@ def __init__(self, key, iv): def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if gmssl.zuc_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if gmssl.zuc_encrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] @@ -323,30 +327,35 @@ def __init__(self, key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = Tr raise ValueError('Invalid IV size') if taglen < 1 or taglen > SM4_GCM_MAX_TAG_SIZE: raise ValueError('Invalid Tag length') - if encrypt == True: - ok = gmssl.sm4_gcm_encrypt_init(byref(self), key, len(key), iv, len(iv), aad, len(aad), taglen) + if encrypt == DO_ENCRYPT: + if gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), + iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), + c_size_t(taglen)) != 1: + raise InnerError('libgmssl inner error') else: - ok = gmssl.sm4_gcm_decrypt_init(byref(self), key, len(key), iv, len(iv), aad, len(aad), taglen) - if ok != 1: - raise InnerError('libgmssl inner error') + if gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), + iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), + c_size_t(taglen)) != 1: + raise InnerError('libgmssl inner error') self._encrypt = encrypt - def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() - if self._encrypt == True: - if gmssl.sm4_gcm_encrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if self._encrypt == DO_ENCRYPT: + if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm4_gcm_decrypt_update(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[0:outlen.value] def finish(self): outbuf = create_string_buffer(SM4_BLOCK_SIZE + SM4_GCM_MAX_TAG_SIZE) outlen = c_size_t() - if self._encrypt == True: + if self._encrypt == DO_ENCRYPT: if gmssl.sm4_gcm_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') else: @@ -400,39 +409,41 @@ def compute_z(self, signer_id = SM2_DEFAULT_ID): raise InnerError('libgmssl inner error') signer_id = signer_id.encode('utf-8') z = create_string_buffer(SM3_DIGEST_SIZE) - gmssl.sm2_compute_z(z, byref(self), signer_id, len(signer_id)) + gmssl.sm2_compute_z(z, byref(self), c_char_p(signer_id), c_size_t(len(signer_id))) return z.raw - def export_encrypted_private_key_info_pem(self, file, passwd): + def export_encrypted_private_key_info_pem(self, path, passwd): if self._has_private_key == False: raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p - fp = libc.fopen(file.encode('utf-8'), 'wb') - if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + fp = libc.fopen(path.encode('utf-8'), 'wb') + passwd = passwd.encode('utf-8') + if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - def import_encrypted_private_key_info_pem(self, file, passwd): + def import_encrypted_private_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p - fp = libc.fopen(file.encode('utf-8'), 'rb') - if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + fp = libc.fopen(path.encode('utf-8'), 'rb') + passwd = passwd.encode('utf-8') + if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = True - def export_public_key_info_pem(self, file): + def export_public_key_info_pem(self, path): if self._has_public_key == False: raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p - fp = libc.fopen(file.encode('utf-8'), 'wb') + fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) - def import_public_key_info_pem(self, file): + def import_public_key_info_pem(self, path): libc.fopen.restype = c_void_p - fp = libc.fopen(file.encode('utf-8'), 'rb') + fp = libc.fopen(path.encode('utf-8'), 'rb') if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -450,12 +461,12 @@ def sign(self, dgst): raise InnerError('libgmssl inner error') return sig[:siglen.value] - def verify(self, dgst, sig): + def verify(self, dgst, signature): if self._has_public_key == False: raise InnerError('libgmssl inner error') if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') - if gmssl.sm2_verify(byref(self), dgst, sig, len(sig)) != 1: + if gmssl.sm2_verify(byref(self), dgst, signature, c_size_t(len(signature))) != 1: return False return True @@ -466,7 +477,8 @@ def encrypt(self, data): raise InnerError('libgmssl inner error') outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() - if gmssl.sm2_encrypt(byref(self), data, len(data), outbuf, byref(outlen)) != 1: + if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), + outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[:outlen.value] @@ -475,7 +487,8 @@ def decrypt(self, ciphertext): raise InnerError('libgmssl inner error') outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) outlen = c_size_t() - if gmssl.sm2_decrypt(byref(self), ciphertext, len(ciphertext), outbuf, byref(outlen)) != 1: + if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(ciphertext)) + , outbuf, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return outbuf[:outlen.value] @@ -494,24 +507,26 @@ class Sm2Signature(Structure): def __init__(self, sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN): signer_id = signer_id.encode('utf-8') - if sign: + if sign == DO_SIGN: if sm2_key.has_private_key() != True: raise InnerError('libgmssl inner error') - if gmssl.sm2_sign_init(byref(self), byref(sm2_key), signer_id, len(signer_id)) != 1: + if gmssl.sm2_sign_init(byref(self), byref(sm2_key), + c_char_p(signer_id), c_size_t(len(signer_id))) != 1: raise InnerError('libgmssl inner error') else: if sm2_key.has_public_key() != True: raise InnerError('libgmssl inner error') - if gmssl.sm2_verify_init(byref(self), byref(sm2_key), signer_id, len(signer_id)) != 1: + if gmssl.sm2_verify_init(byref(self), byref(sm2_key), + c_char_p(signer_id), c_size_t(len(signer_id))) != 1: raise InnerError('libgmssl inner error') self._sign = sign def update(self, data): if self._sign == DO_SIGN: - if gmssl.sm2_sign_update(byref(self), data, len(data)) != 1: + if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm2_verify_update(byref(self), data, len(data)) != 1: + if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: raise InnerError('libgmssl inner error') def sign(self): @@ -523,10 +538,10 @@ def sign(self): raise InnerError('libgmssl inner error') return sig[:siglen.value] - def verify(self, sig): + def verify(self, signature): if self._sign != DO_VERIFY: raise InnerError('libgmssl inner error') - if gmssl.sm2_verify_finish(byref(self), sig, len(sig)) != 1: + if gmssl.sm2_verify_finish(byref(self), signature, c_size_t(len(signature))) != 1: return False return True @@ -579,7 +594,8 @@ def has_private_key(self): def import_encrypted_private_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') - if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_private_key = True @@ -589,7 +605,8 @@ def export_encrypted_private_key_info_pem(self, path, passwd): raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') - if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -598,7 +615,8 @@ def decrypt(self, ciphertext): raise InnerError('libgmssl inner error') plaintext = create_string_buffer(SM9_MAX_PLAINTEXT_SIZE) outlen = c_size_t() - if gmssl.sm9_decrypt(byref(self), self._id, len(self._id), ciphertext, len(ciphertext), plaintext, byref(outlen)) != 1: + if gmssl.sm9_decrypt(byref(self), c_char_p(self._id), c_size_t(len(self._id)), + ciphertext, c_size_t(len(ciphertext)), plaintext, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return plaintext[0:outlen.value] @@ -623,8 +641,9 @@ def extract_key(self, identity): if self._has_private_key != True: raise InnerError('libgmssl inner error') key = Sm9EncKey(identity) - # FIXME: identity type - if gmssl.sm9_enc_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) != 1: + identity = identity.encode('utf-8') + if gmssl.sm9_enc_master_key_extract_key(byref(self), + c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: raise InnerError('libgmssl inner error') key._has_public_key = True key._has_private_key = True @@ -633,7 +652,8 @@ def extract_key(self, identity): def import_encrypted_master_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') - if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True @@ -644,7 +664,8 @@ def export_encrypted_master_key_info_pem(self, path, passwd): raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') - if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -669,15 +690,15 @@ def import_public_master_key_pem(self, path): def encrypt(self, plaintext, to): if self._has_public_key != True: raise InnerError('libgmssl inner error') + to = to.encode('utf-8') ciphertext = create_string_buffer(SM9_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() - if gmssl.sm9_encrypt(byref(self), to.encode('utf-8'), len(to), plaintext, len(plaintext), ciphertext, byref(outlen)) != 1: + if gmssl.sm9_encrypt(byref(self), c_char_p(to), c_size_t(len(to)), + plaintext, c_size_t(len(plaintext)), ciphertext, byref(outlen)) != 1: raise InnerError('libgmssl inner error') return ciphertext[0:outlen.value] - - class Sm9SignKey(Structure): _fields_ = [ ("Ppubs", Sm9TwistPoint), @@ -697,7 +718,8 @@ def has_private_key(self): def import_encrypted_private_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') - if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_private_key = True @@ -707,7 +729,8 @@ def export_encrypted_private_key_info_pem(self, path, passwd): raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') - if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -732,8 +755,9 @@ def extract_key(self, identity): if self._has_private_key != True: raise InnerError('libgmssl inner error') key = Sm9SignKey(identity) - # FIXME: identity - if gmssl.sm9_sign_master_key_extract_key(byref(self), c_char_p(identity.encode('utf-8')), len(identity), byref(key)) != 1: + identity = identity.encode('utf-8') + if gmssl.sm9_sign_master_key_extract_key(byref(self), + c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: raise InnerError('libgmssl inner error') key._has_public_key = True key._has_private_key = True @@ -742,7 +766,9 @@ def extract_key(self, identity): def import_encrypted_master_key_info_pem(self, path, passwd): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') - if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), + c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True @@ -753,7 +779,9 @@ def export_encrypted_master_key_info_pem(self, path, passwd): raise InnerError('libgmssl inner error') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') - if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), passwd.encode('utf-8'), c_void_p(fp)) != 1: + passwd = passwd.encode('utf-8') + if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), + c_char_p(passwd), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -812,10 +840,10 @@ def update(self, data): raise InnerError('libgmssl inner error') if self._sign == DO_SIGN: - if gmssl.sm9_sign_update(byref(self), data, len(data)) != 1: + if gmssl.sm9_sign_update(byref(self), data, c_size_t(len(data))) != 1: raise InnerError('libgmssl inner error') else: - if gmssl.sm9_verify_update(byref(self), data, len(data)) != 1: + if gmssl.sm9_verify_update(byref(self), data, c_size_t(len(data))) != 1: raise InnerError('libgmssl inner error') @@ -831,7 +859,7 @@ def sign(self, sign_key): raise InnerError('libgmssl inner error') return sig[:siglen.value] - def verify(self, sig, master_pub, signer_id): + def verify(self, signature, public_master_key, signer_id): if self._inited != True: raise InnerError('libgmssl inner error') @@ -840,7 +868,8 @@ def verify(self, sig, master_pub, signer_id): signer_id = signer_id.encode('utf-8') - if gmssl.sm9_verify_finish(byref(self), sig, len(sig), byref(master_pub), signer_id, len(signer_id)) != 1: + if gmssl.sm9_verify_finish(byref(self), signature, c_size_t(len(signature)), + byref(public_master_key), c_char_p(signer_id), c_size_t(len(signer_id))) != 1: return False return True @@ -930,7 +959,7 @@ def get_raw(self): def export_pem(self, path): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') - if gmssl.x509_cert_to_pem(self._cert, len(self._cert), c_void_p(fp)) != 1: + if gmssl.x509_cert_to_pem(self._cert, c_size_t(len(self._cert)), c_void_p(fp)) != 1: raise InnerError('libgmssl inner error') def get_serial_number(self): @@ -938,7 +967,8 @@ def get_serial_number(self): serial_ptr = c_void_p() serial_len = c_size_t() - if gmssl.x509_cert_get_issuer_and_serial_number(self._cert, len(self._cert), None, None, byref(serial_ptr), byref(serial_len)) != 1: + if gmssl.x509_cert_get_issuer_and_serial_number(self._cert, c_size_t(len(self._cert)), + None, None, byref(serial_ptr), byref(serial_len)) != 1: raise InnerError('libgmssl inner error') serial = create_string_buffer(serial_len.value) @@ -948,7 +978,8 @@ def get_serial_number(self): def get_issuer(self): issuer_ptr = c_void_p() issuer_len = c_size_t() - if gmssl.x509_cert_get_issuer(self._cert, len(self._cert), byref(issuer_ptr), byref(issuer_len)) != 1: + if gmssl.x509_cert_get_issuer(self._cert, c_size_t(len(self._cert)), + byref(issuer_ptr), byref(issuer_len)) != 1: raise InnerError('libgmssl inner error') issuer_raw = create_string_buffer(issuer_len.value) libc.memcpy(issuer_raw, issuer_ptr, issuer_len) @@ -960,7 +991,8 @@ def get_issuer(self): def get_subject(self): subject_ptr = c_void_p() subject_len = c_size_t() - if gmssl.x509_cert_get_subject(self._cert, len(self._cert), byref(subject_ptr), byref(subject_len)) != 1: + if gmssl.x509_cert_get_subject(self._cert, c_size_t(len(self._cert)), + byref(subject_ptr), byref(subject_len)) != 1: raise InnerError('libgmssl inner error') subject_raw = create_string_buffer(subject_len.value) libc.memcpy(subject_raw, subject_ptr, subject_len) @@ -971,7 +1003,7 @@ def get_subject(self): def get_subject_public_key(self): public_key = Sm2Key() - gmssl.x509_cert_get_subject_public_key(self._cert, len(self._cert), byref(public_key)) + gmssl.x509_cert_get_subject_public_key(self._cert, c_size_t(len(self._cert)), byref(public_key)) public_key._has_private_key = False public_key._has_public_key = True return public_key @@ -979,7 +1011,7 @@ def get_subject_public_key(self): def get_validity(self): not_before = c_ulong() not_after = c_ulong() - if gmssl.x509_cert_get_details(self._cert, len(self._cert), + if gmssl.x509_cert_get_details(self._cert, c_size_t(len(self._cert)), None, None, None, None, None, None, byref(not_before), byref(not_after), None, None, None, None, None, None, None, None, None, None, None, None) != 1: @@ -989,13 +1021,11 @@ def get_validity(self): def verify_by_ca_certificate(self, cacert, sm2_id): cacert_raw = cacert.get_raw() + sm2_id = sm2_id.encode('utf-8') - ret = gmssl.x509_cert_verify_by_ca_cert(self._cert, len(self._cert), cacert_raw, len(cacert_raw), - sm2_id.encode('utf-8'), len(sm2_id)) - - if ret != 1: + if gmssl.x509_cert_verify_by_ca_cert(self._cert, c_size_t(len(self._cert)), + cacert_raw, c_size_t(len(cacert_raw)), c_char_p(sm2_id), c_size_t(len(sm2_id))) != 1: return False - return True From ccd8fb07fd5c7e198fba2e70c0c47b30af7d5d42 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 22 Sep 2023 19:37:00 +0800 Subject: [PATCH 12/18] Add Exceptions --- gmssl.py | 204 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 104 insertions(+), 100 deletions(-) diff --git a/gmssl.py b/gmssl.py index 1bcd5ac..158b3a9 100755 --- a/gmssl.py +++ b/gmssl.py @@ -19,11 +19,16 @@ libc = cdll.LoadLibrary(find_library('c')) -class InnerError(Exception): +class NativeError(Exception): ''' GmSSL libraray inner error ''' +class StateError(Exception): + ''' + Crypto state error + ''' + GMSSL_PYTHON_VERSION = "2.2.0" def gmssl_library_version_num(): @@ -124,7 +129,7 @@ def sm3_pbkdf2(passwd, salt, iterator, keylen): if gmssl.pbkdf2_hmac_sm3_genkey(c_char_p(passwd), c_size_t(len(passwd)), salt, c_size_t(len(salt)), c_size_t(iterator), c_size_t(keylen), key) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return key.raw @@ -175,10 +180,10 @@ def __init__(self, key, iv, encrypt): raise ValueError('Invalid IV size') if encrypt == DO_ENCRYPT: if gmssl.sm4_cbc_encrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_cbc_decrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._encrypt = encrypt def update(self, data): @@ -187,11 +192,11 @@ def update(self, data): if self._encrypt == DO_ENCRYPT: if gmssl.sm4_cbc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_cbc_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[0:outlen.value] def finish(self): @@ -199,10 +204,10 @@ def finish(self): outlen = c_size_t() if self._encrypt == True: if gmssl.sm4_cbc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_cbc_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] @@ -225,21 +230,21 @@ def __init__(self, key, iv): if len(iv) != SM4_BLOCK_SIZE: raise ValueError('Invalid IV size') if gmssl.sm4_ctr_encrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() if gmssl.sm4_ctr_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[0:outlen.value] def finish(self): outbuf = create_string_buffer(SM4_BLOCK_SIZE) outlen = c_size_t() if gmssl.sm4_ctr_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] @@ -267,21 +272,21 @@ def __init__(self, key, iv): if len(iv) != ZUC_IV_SIZE: raise ValueError('Invalid IV size') if gmssl.zuc_encrypt_init(byref(self), key, iv) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def update(self, data): outbuf = create_string_buffer(len(data) + SM4_BLOCK_SIZE) outlen = c_size_t() if gmssl.zuc_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[0:outlen.value] def finish(self): outbuf = create_string_buffer(SM4_BLOCK_SIZE) outlen = c_size_t() if gmssl.zuc_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] @@ -331,12 +336,12 @@ def __init__(self, key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = Tr if gmssl.sm4_gcm_encrypt_init(byref(self), key, c_size_t(len(key)), iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_gcm_decrypt_init(byref(self), key, c_size_t(len(key)), iv, c_size_t(len(iv)), aad, c_size_t(len(aad)), c_size_t(taglen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._encrypt = encrypt def update(self, data): @@ -345,11 +350,11 @@ def update(self, data): if self._encrypt == DO_ENCRYPT: if gmssl.sm4_gcm_encrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_gcm_decrypt_update(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[0:outlen.value] def finish(self): @@ -357,10 +362,10 @@ def finish(self): outlen = c_size_t() if self._encrypt == DO_ENCRYPT: if gmssl.sm4_gcm_encrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm4_gcm_decrypt_finish(byref(self), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] @@ -394,7 +399,7 @@ def __init__(self): def generate_key(self): if gmssl.sm2_key_generate(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._has_public_key = True self._has_private_key = True @@ -406,7 +411,7 @@ def has_public_key(self): def compute_z(self, signer_id = SM2_DEFAULT_ID): if self._has_public_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no public key') signer_id = signer_id.encode('utf-8') z = create_string_buffer(SM3_DIGEST_SIZE) gmssl.sm2_compute_z(z, byref(self), c_char_p(signer_id), c_size_t(len(signer_id))) @@ -414,12 +419,12 @@ def compute_z(self, signer_id = SM2_DEFAULT_ID): def export_encrypted_private_key_info_pem(self, path, passwd): if self._has_private_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no private key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') passwd = passwd.encode('utf-8') if gmssl.sm2_private_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def import_encrypted_private_key_info_pem(self, path, passwd): @@ -427,43 +432,43 @@ def import_encrypted_private_key_info_pem(self, path, passwd): fp = libc.fopen(path.encode('utf-8'), 'rb') passwd = passwd.encode('utf-8') if gmssl.sm2_private_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = True def export_public_key_info_pem(self, path): if self._has_public_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no public key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def import_public_key_info_pem(self, path): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = False def sign(self, dgst): if self._has_private_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no private key') if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) siglen = c_size_t() if gmssl.sm2_sign(byref(self), dgst, sig, byref(siglen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return sig[:siglen.value] def verify(self, dgst, signature): if self._has_public_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no public key') if len(dgst) != SM3_DIGEST_SIZE: raise ValueError('Invalid SM3 digest size') if gmssl.sm2_verify(byref(self), dgst, signature, c_size_t(len(signature))) != 1: @@ -472,24 +477,24 @@ def verify(self, dgst, signature): def encrypt(self, data): if self._has_public_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no public key') if len(data) > SM2_MAX_PLAINTEXT_SIZE: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') outbuf = create_string_buffer(SM2_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() if gmssl.sm2_encrypt(byref(self), data, c_size_t(len(data)), outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] def decrypt(self, ciphertext): if self._has_private_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no private key') outbuf = create_string_buffer(SM2_MAX_PLAINTEXT_SIZE) outlen = c_size_t() if gmssl.sm2_decrypt(byref(self), ciphertext, c_size_t(len(ciphertext)) , outbuf, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return outbuf[:outlen.value] @@ -509,38 +514,38 @@ def __init__(self, sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN): signer_id = signer_id.encode('utf-8') if sign == DO_SIGN: if sm2_key.has_private_key() != True: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') if gmssl.sm2_sign_init(byref(self), byref(sm2_key), c_char_p(signer_id), c_size_t(len(signer_id))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if sm2_key.has_public_key() != True: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') if gmssl.sm2_verify_init(byref(self), byref(sm2_key), c_char_p(signer_id), c_size_t(len(signer_id))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._sign = sign def update(self, data): if self._sign == DO_SIGN: if gmssl.sm2_sign_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm2_verify_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def sign(self): if self._sign != DO_SIGN: - raise InnerError('libgmssl inner error') + raise StateError('not sign state') sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE) siglen = c_size_t() if gmssl.sm2_sign_finish(byref(self), sig, byref(siglen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return sig[:siglen.value] def verify(self, signature): if self._sign != DO_VERIFY: - raise InnerError('libgmssl inner error') + raise StateError('not verify state') if gmssl.sm2_verify_finish(byref(self), signature, c_size_t(len(signature))) != 1: return False return True @@ -596,28 +601,28 @@ def import_encrypted_private_key_info_pem(self, path, passwd): fp = libc.fopen(path.encode('utf-8'), 'rb') passwd = passwd.encode('utf-8') if gmssl.sm9_enc_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_private_key = True def export_encrypted_private_key_info_pem(self, path, passwd): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no private key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') passwd = passwd.encode('utf-8') if gmssl.sm9_enc_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def decrypt(self, ciphertext): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no private key') plaintext = create_string_buffer(SM9_MAX_PLAINTEXT_SIZE) outlen = c_size_t() if gmssl.sm9_decrypt(byref(self), c_char_p(self._id), c_size_t(len(self._id)), ciphertext, c_size_t(len(ciphertext)), plaintext, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return plaintext[0:outlen.value] @@ -633,18 +638,18 @@ def __init__(self): def generate_master_key(self): if gmssl.sm9_enc_master_key_generate(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._has_public_key = True self._has_private_key = True def extract_key(self, identity): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no master key') key = Sm9EncKey(identity) identity = identity.encode('utf-8') if gmssl.sm9_enc_master_key_extract_key(byref(self), c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') key._has_public_key = True key._has_private_key = True return key @@ -654,48 +659,48 @@ def import_encrypted_master_key_info_pem(self, path, passwd): fp = libc.fopen(path.encode('utf-8'), 'rb') passwd = passwd.encode('utf-8') if gmssl.sm9_enc_master_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = True def export_encrypted_master_key_info_pem(self, path, passwd): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no master key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') passwd = passwd.encode('utf-8') if gmssl.sm9_enc_master_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def export_public_master_key_pem(self, path): if self._has_public_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no public master key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_enc_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def import_public_master_key_pem(self, path): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') if gmssl.sm9_enc_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = False def encrypt(self, plaintext, to): if self._has_public_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no public master key') to = to.encode('utf-8') ciphertext = create_string_buffer(SM9_MAX_CIPHERTEXT_SIZE) outlen = c_size_t() if gmssl.sm9_encrypt(byref(self), c_char_p(to), c_size_t(len(to)), plaintext, c_size_t(len(plaintext)), ciphertext, byref(outlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return ciphertext[0:outlen.value] @@ -720,18 +725,18 @@ def import_encrypted_private_key_info_pem(self, path, passwd): fp = libc.fopen(path.encode('utf-8'), 'rb') passwd = passwd.encode('utf-8') if gmssl.sm9_sign_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_private_key = True def export_encrypted_private_key_info_pem(self, path, passwd): if self._has_private_key == False: - raise InnerError('libgmssl inner error') + raise TypeError('has no master key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') passwd = passwd.encode('utf-8') if gmssl.sm9_sign_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) @@ -747,18 +752,18 @@ def __init__(self): def generate_master_key(self): if gmssl.sm9_sign_master_key_generate(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._has_public_key = True self._has_private_key = True def extract_key(self, identity): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no master key') key = Sm9SignKey(identity) identity = identity.encode('utf-8') if gmssl.sm9_sign_master_key_extract_key(byref(self), c_char_p(identity), c_size_t(len(identity)), byref(key)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') key._has_public_key = True key._has_private_key = True return key @@ -769,36 +774,36 @@ def import_encrypted_master_key_info_pem(self, path, passwd): passwd = passwd.encode('utf-8') if gmssl.sm9_sign_master_key_info_decrypt_from_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = True def export_encrypted_master_key_info_pem(self, path, passwd): if self._has_private_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no master key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') passwd = passwd.encode('utf-8') if gmssl.sm9_sign_master_key_info_encrypt_to_pem(byref(self), c_char_p(passwd), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def export_public_master_key_pem(self, path): if self._has_public_key != True: - raise InnerError('libgmssl inner error') + raise TypeError('has no public master key') libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.sm9_sign_master_public_key_to_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) def import_public_master_key_pem(self, path): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'rb') if gmssl.sm9_sign_master_public_key_from_pem(byref(self), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') libc.fclose(c_void_p(fp)) self._has_public_key = True self._has_private_key = False @@ -815,56 +820,55 @@ class Sm9Signature(Structure): def __init__(self, sign = DO_SIGN): if sign == DO_SIGN: if gmssl.sm9_sign_init(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm9_verify_init(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._sign = sign self._inited = True def reset(self): if self._inited != True: - raise InnerError('libgmssl inner error') + raise StateError('not initialized') if self._sign == DO_SIGN: if gmssl.sm9_sign_init(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm9_verify_init(byref(self)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def update(self, data): if self._inited != True: - raise InnerError('libgmssl inner error') + raise StateError('not initialized') if self._sign == DO_SIGN: if gmssl.sm9_sign_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.sm9_verify_update(byref(self), data, c_size_t(len(data))) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def sign(self, sign_key): if self._inited != True: - raise InnerError('libgmssl inner error') + raise StateError('not initialized') if self._sign != DO_SIGN: - raise InnerError('libgmssl inner error') + raise StateError('not sign state') sig = create_string_buffer(SM9_SIGNATURE_SIZE) siglen = c_size_t() if gmssl.sm9_sign_finish(byref(self), byref(sign_key), sig, byref(siglen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return sig[:siglen.value] def verify(self, signature, public_master_key, signer_id): if self._inited != True: - raise InnerError('libgmssl inner error') - + raise StateError('not initialized') if self._sign != DO_VERIFY: - raise InnerError('libgmssl inner error') + raise StateError('not verify state') signer_id = signer_id.encode('utf-8') @@ -888,19 +892,19 @@ def gmssl_parse_attr_type_and_value(name, d, dlen): vlen = c_size_t() if gmssl.x509_name_type_from_der(byref(oid), byref(d), byref(dlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') gmssl.x509_name_type_name.restype = c_char_p oid_name = gmssl.x509_name_type_name(oid).decode('ascii') if oid_name == 'emailAddress': if gmssl.asn1_ia5_string_from_der_ex(_ASN1_TAG_IA5String, byref(val), byref(vlen), byref(d), byref(dlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') else: if gmssl.x509_directory_name_from_der(byref(tag), byref(val), byref(vlen), byref(d), byref(dlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') if dlen.value != 0: - raise InnerError('libgmssl inner error') + raise ValueError('invalid der encoding') value = create_string_buffer(vlen.value) libc.memcpy(value, val, vlen) @@ -914,10 +918,10 @@ def gmssl_parse_rdn(name, d, dlen): while dlen.value > 0: if gmssl.asn1_type_from_der(_ASN1_TAG_SEQUENCE, byref(v), byref(vlen), byref(d), byref(dlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') if gmssl_parse_attr_type_and_value(name, v, vlen) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return True @@ -928,7 +932,7 @@ def gmssl_parse_name(name, d, dlen): while dlen.value > 0: if gmssl.asn1_nonempty_type_from_der(c_int(_ASN1_TAG_SET), byref(v), byref(vlen), byref(d), byref(dlen)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') gmssl_parse_rdn(name, v, vlen) return True @@ -947,7 +951,7 @@ def import_pem(self, path): cert = c_void_p() certlen = c_size_t() if gmssl.x509_cert_new_from_file(byref(cert), byref(certlen), path.encode('utf-8')) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') self._cert = create_string_buffer(certlen.value) libc.memcpy(self._cert, cert, certlen) @@ -960,7 +964,7 @@ def export_pem(self, path): libc.fopen.restype = c_void_p fp = libc.fopen(path.encode('utf-8'), 'wb') if gmssl.x509_cert_to_pem(self._cert, c_size_t(len(self._cert)), c_void_p(fp)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') def get_serial_number(self): @@ -969,7 +973,7 @@ def get_serial_number(self): if gmssl.x509_cert_get_issuer_and_serial_number(self._cert, c_size_t(len(self._cert)), None, None, byref(serial_ptr), byref(serial_len)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') serial = create_string_buffer(serial_len.value) libc.memcpy(serial, serial_ptr, serial_len) @@ -980,7 +984,7 @@ def get_issuer(self): issuer_len = c_size_t() if gmssl.x509_cert_get_issuer(self._cert, c_size_t(len(self._cert)), byref(issuer_ptr), byref(issuer_len)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') issuer_raw = create_string_buffer(issuer_len.value) libc.memcpy(issuer_raw, issuer_ptr, issuer_len) @@ -993,7 +997,7 @@ def get_subject(self): subject_len = c_size_t() if gmssl.x509_cert_get_subject(self._cert, c_size_t(len(self._cert)), byref(subject_ptr), byref(subject_len)) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') subject_raw = create_string_buffer(subject_len.value) libc.memcpy(subject_raw, subject_ptr, subject_len) @@ -1015,7 +1019,7 @@ def get_validity(self): None, None, None, None, None, None, byref(not_before), byref(not_after), None, None, None, None, None, None, None, None, None, None, None, None) != 1: - raise InnerError('libgmssl inner error') + raise NativeError('libgmssl inner error') return Validity(not_before.value, not_after.value) def verify_by_ca_certificate(self, cacert, sm2_id): From df3bf683b7327626fa8aa59aeb9adfc962147683 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 22 Sep 2023 19:38:13 +0800 Subject: [PATCH 13/18] Update version fix sm4_gcm on some platforms --- gmssl.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gmssl.py b/gmssl.py index 158b3a9..a6ee402 100755 --- a/gmssl.py +++ b/gmssl.py @@ -29,7 +29,7 @@ class StateError(Exception): Crypto state error ''' -GMSSL_PYTHON_VERSION = "2.2.0" +GMSSL_PYTHON_VERSION = "2.2.1" def gmssl_library_version_num(): return gmssl.gmssl_version_num() diff --git a/pyproject.toml b/pyproject.toml index be6cab8..ff4ec58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "gmssl_python" -version = "2.2.0" +version = "2.2.1" authors = [ { name="Zhi Guan", email="guan@pku.edu.cn" }, ] From 7d1e3aa47e216ec84e697421f427b6c09743d31e Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 8 Oct 2023 19:15:18 +0800 Subject: [PATCH 14/18] Update README.md --- README.md | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) diff --git a/README.md b/README.md index 2db9a49..bff227f 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,673 @@ GmSSL-Python其他类的`update`方法通常也都提供了这种形式的接口 GmSSL-Python的部分其他类也提供了`reset`方法。 +### HMAC-SM3消息认证码 + +HMAC-SM3是基于SM3密码杂凑算法的消息认证码(MAC)算法,消息认证码算法可以看作带密钥的哈希函数,主要用于保护消息不受篡改。通信双方需要事先协商出一个密钥,比如32字节的随机字节序列,数据的发送方用这个密钥对消息计算MAC值,并且把MAC值附在消息后面。消息的接收方在收到消息后,用相同的密钥计算消息的MAC值,并且和发送消息附带的MAC值做对比,如果一致说明消息没有被篡改,如果不一致,说明消息被篡改了。 + +模块`gmssl`中包含如下Sm3Hmac的常量: + +* `SM3_HMAC_MIN_KEY_SIZE` +* `SM3_HMAC_MAX_KEY_SIZE` +* `SM3_HMAC_SIZE` HMAC-SM3密钥长度,与SM3哈希值的长度相等 + +`Sm3Hmac`类实现了基于SM3的HMAC消息认证码算法,类`Sm3Hmac`的对象是由构造函数生成的。 + +``` +gmssl.Sm3Hmac(key) +``` + +对象Sm3Hmac的方法: + +* `Sm3Hmac.update(data : bytes)` +* `Sm3Hmac.generate_mac() -> bytes` +* `Sm3Hmac.reset()` + +HMAC-SM3算法可以看作是带密钥的SM3算法,因此在生成`Sm3Hmac`对象时需要传入一个密钥`key`作为输入参数。虽然HMAC-SM3在算法和实现上对密钥长度没有限制,但是出于安全性、效率等方面的考虑,HMAC-SM3算法的密钥长度建议采用32字节(等同于SM3哈希值的长度),不应少于16字节,采用比32字节更长的密钥长度会增加计算开销而不会增加安全性。 + +下面的例子显示了如何用HMAC-SM3生成消息`abc`的MAC值。 + +```python +>>> from gmssl import * +>>> key = rand_bytes(SM3_HMAC_MIN_KEY_SIZE) +>>> sm3_hmac = Sm3Hmac(key) +>>> sm3_hmac.update(b'abc') +>>> sm3_hmac.generate_mac().hex() +``` + +`Sm3Hmac`也通过`update`方法来提供输入消息,应用可以多次调用`update`。 + +应用在通过`update`完成数据输入后,调用`generate_mac`可以获得消息认证码。 + +### 基于口令的密钥导出函数PBKDF2 + +常用软件如Word、PDF、WinRAR等支持基于口令的文件加密,字符串形式的口令相对于随机的密钥字节序列对用户来说更容易记忆和输入,对用户更加友好。但是由于口令中存在的信息熵远低于随机的二进制密钥,直接将口令字符串作为密钥,甚至无法抵御来自个人计算机的暴力破解攻击。一种典型的错误用法是直接用哈希函数计算口令的哈希值,将看起来随机的哈希值作为密钥使用。但是由于口令的空间相对较小,攻击者仍然可以尝试所有可能口令的哈希值,对于暴力破解来说,破解口令的哈希值和原始口令,在攻击难度上没有太大差别。 + +安全和规范的做法是采用一个基于口令的密钥导出函数(Password-Based Key Derivation Function, PBKDF)从口令中导出密钥。通过PBKDF导出密钥并不会降低攻击者在暴力破解时尝试的口令数量,但是可以防止攻击者通过查预计算表的方式来加速破解,并且可以大大增加攻击者尝试每一个可能口令的计算时间。PBKDF2是安全的并且使用广泛的PBKDF算法标准之一,算法采用哈希函数作为将口令映射为密钥的主要部件,通过加入随机并且公开的盐值(Salt)来抵御预计算,通过增加多轮的循环计算来增加在线破解的难度,并且支持可变的导出密钥长度。 + +模块`gmssl`中包含如下Sm3Pbkdf2的常量 + +* `SM3_PBKDF2_MIN_ITER` +* `SM3_PBKDF2_MAX_ITER` +* `SM3_PBKDF2_MAX_SALT_SIZE` +* `SM3_PBKDF2_DEFAULT_SALT_SIZE` +* `SM3_PBKDF2_MAX_KEY_SIZE` + +函数`Sm3Pbkdf2`实现了基于SM3的PBKDF2算法。 + +```python +sm3_pbkdf2(passwd, salt, iterator, keylen) +``` + +其中: + +- `passwd`用于导出密钥的用户口令。 +- `salt`是用于抵御与计算的盐值。这个值需要用随机生成(比如通过`Random`类),并且具有一定的长度。Salt值不需要保密,因此在口令加密数据时,可以直接将这个值附在密文前,传输给接收方。Salt值越长,抵御预计算攻击的效果就更好。例如当Salt为8字节(64比特)长的随机值时,攻击者预计算表就要扩大$2^{64}$倍。`Sm3Pbkdf2`提供一个推荐的Salt值长度`SM3_PBKDF2_DEFAULT_SALT_SIZE`常量,并且在实现上不支持超过`SM3_PBKDF2_MAX_KEY_SIZE`长度的Salt值。 +- `iterator`参数用于表示在导出密钥时调用SM3算法的循环次数,`iterator`值越大,暴力破解的难度越大,但是同时用户在调用这个函数时的开销也增大了。一般来说`iterator`值的应该选择在用户可接收延迟情况下的最大值,比如当`iterator = 10000`时,用户延迟为100毫秒,但是对于用户来说延迟感受不明显,但是对于暴力攻击者来说`iterator = 10000`意味着攻击的开销增加了大约1万倍。`Sm3Pbkdf2`通过`SM3_PBKDF2_MIN_ITER`和`SM3_PBKDF2_MAX_ITER`两个常量给出了`iterator`值的范围,用户可以根据当前计算机的性能及用户对延迟的可感知度,在这个范围内选择合适的值。 +- `keylen`参数表示希望导出的密钥长度,这个长度不可超过常量`SM3_PBKDF2_MAX_KEY_SIZE`。 + +下面的例子展示了如何从口令字符串导出一个密钥。 + +```python +>>> from gmssl import * +>>> passwd = "Password" +>>> salt = rand_bytes(SM3_PBKDF2_DEFAULT_SALT_SIZE) +>>> iterator = SM3_PBKDF2_MIN_ITER +>>> keylen = 32 +>>> sm3_pbkdf2(passwd, salt, iterator, keylen).hex() +``` + +### SM4分组密码 + +SM4算法是分组密码算法,其密钥长度为128比特(16字节),分组长度为128比特(16字节)。SM4算法每次只能加密或者解密一个固定16字节长度的分组,不支持加解密任意长度的消息。分组密码通常作为更高层密码方案的一个组成部分,不适合普通上层应用调用。如果应用需要保护数据和消息,那么应该优先选择采用SM4-GCM模式,或者为了兼容已有的系统,也可以使用SM4-CBC或SM4-CTR模式。 + +模块`gmssl`中包含如下SM4的常量 + +* `SM4_KEY_SIZE` +* `SM4_BLOCK_SIZE` + +`SM4`类实现了基本的SM4分组密码算法,类`SM4`的对象是由构造函数生成的。 + +``` +gmssl.Sm4(key, encrypt) +``` + +对象SM4的方法: + +* `Sm4.encrypt(block : int) -> bytes` + +`Sm4`对象在创建时需要提供`SM4_KEY_SIZE`字节长度的密钥,以及一个布尔值`DO_ENCRYPT`表示是用于加密还是解密。方法`encrypt`根据创建时的选择进行加密或解密,每次调用`encrypt`只处理一个分组,即读入`SM4_BLOCK_SIZE`长度的输入。 + +下面的例子展示SM4分组加密 + +```python +>>> from gmssl import * +>>> key = rand_bytes(SM4_KEY_SIZE) +>>> plaintext = rand_bytes(SM4_BLOCK_SIZE) +>>> sm4_enc = Sm4(key, DO_ENCRYPT) +>>> ciphertext = sm4_enc.encrypt(plaintext) +>>> sm4_dec = Sm4(key, DO_DECRYPT) +>>> decrypted = sm4_dec.encrypt(ciphertext) +``` + +多次调用`Sm4`的分组加密解密功能可以实现ECB模式,由于ECB模式在消息加密应用场景中并不安全,因此GmSSL中没有提供ECB模式。如果应用需要开发SM4的其他加密模式,也可以基于`Sm4`类来开发这些模式。 + +### SM4-CBC加密模式 + +CBC模式是应用最广泛的分组密码加密模式之一,虽然目前不建议在新的应用中继续使用CBC默认,为了保证兼容性,应用仍然可能需要使用CBC模式。 + +模块`gmssl`中包含如下Sm4Cbc的常量: + +* `SM4_CBC_IV_SIZE` + +`Sm4Cbc`类实现了基本的SM4-CBC分组密码算法,类`Sm4Cbc`的对象是由构造函数生成的。 + +``` +gmssl.Sm4Cbc(key, iv, encrypt) +``` + +对象Sm4Cbc的方法: + +* `Sm4Cbc.update(data : bytes)` +* `Sm4Cbc.finish() -> bytes` + +`Sm4Cbc`类实现了SM4的带填充CBC模式,可以实现对任意长度数据的加密。由于需要对明文进行填充,因此`Sm4Cbc`输出的密文长度总是长于明文长度,并且密文的长度是整数个分组长度。 + +通过`Sm4Cbc`加密时,`key`和`iv`都必须为16字节长度。由于CBC模式中加密和解密的计算过程不同,因此必须通过布尔值`DO_ENCRYPT`指定是加密还是解密。 + +由于`Sm4Cbc`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`SM4_CBC_IV_SIZE`长度。 + +下面的例子显示了采用SM4-CBC加密和解密的过程。 + +```python +>>> from gmssl import * +>>> key = rand_bytes(SM4_KEY_SIZE) +>>> iv = rand_bytes(SM4_CBC_IV_SIZE) +>>> plaintext = b'abc' +>>> sm4_enc = Sm4Cbc(key, iv, DO_ENCRYPT) +>>> ciphertext = sm4_enc.update(plaintext) +>>> ciphertext += sm4_enc.finish() +>>> sm4_dec = Sm4Cbc(key, iv, DO_DECRYPT) +>>> decrypted = sm4_dec.update(ciphertext) +>>> decrypted += sm4_dec.finish() +``` + +### SM4-CTR加密模式 + +CTR加密模式可以加密任意长度的消息,和CBC模式不同,并不需要采用填充方案,因此SM4-CTR加密输出的密文长度和输入的明文等长。对于存储或传输带宽有限的应用场景,SM4-CTR相对SM4-CBC模式,密文不会增加额外长度。 + +模块`gmssl`中包含如下Sm4Ctr的常量: + +* `SM4_CTR_IV_SIZE` + +`Sm4Ctr`类实现了基本的SM4-CBC分组密码算法,类`Sm4Ctr`的对象是由构造函数生成的。 + +``` +gmssl.Sm4Ctr(key, iv) +``` + +对象Sm4Cbc的方法: + +* `Sm4Ctr.update(data : bytes)` +* `Sm4Ctr.finish() -> bytes` + +SM4-CTR在加密和解密时计算过程一样,因此在初始化时不需要指定加密或解密,因此没有`Sm4Cbc`中的`DO_ENCRYPT`参数。其他过程和SM4-CBC是一样的。 + +由于`Sm4Ctr`在加解密时维护了内部的缓冲区,因此`update`的输出长度可能不等于输入长度,应该保证输出缓冲区的长度至少比输入长度长一个`SM4_BLOCK_SIZE`长度。 + +注意 ,SM4-CBC和SM4-CTR模式都不能保证消息的完整性,在使用这两个模式时,应用还需要生成一个独立的HMAC-SM3密钥,并且生成密文的MAC值。 + +### SM4-GCM认证加密模式 + +SM4的GCM模式是一种认证加密模式,和CBC、CTR等加密模式的主要区别在于,GCM模式的加密过程默认在密文最后添加完整性标签,也就是MAC标签,因此应用在采用SM4-GCM模式时,没有必要再计算并添加SM3-HMAC了。在有的应用场景中,比如对消息报文进行加密,对于消息头部的一段数据(报头字段)只需要做完整性保护,不需要加密,SM4-GCM支持这种场景。在`Sm4Gcm`类的`init`方法中,除了`key`、`iv`参数,还可以提供`aad`字节数字用于提供不需要加密的消息头部数据。 + +模块`gmssl`中包含如下Sm4Gcm的常量: + +* `SM4_GCM_MIN_IV_SIZE` +* `SM4_GCM_MAX_IV_SIZE` +* `SM4_GCM_DEFAULT_IV_SIZE` +* `SM4_GCM_DEFAULT_TAG_SIZE` +* `SM4_GCM_MAX_TAG_SIZE ` + +`Sm4Gcm`类实现了基本的SM4-CBC分组密码算法,类`Sm4Gcm`的对象是由构造函数生成的。 + +``` +gmssl.Sm4Gcm(key, iv, aad, taglen = SM4_GCM_DEFAULT_TAG_SIZE, encrypt = True) +``` + +对象Sm4Gcm的方法: + +* `Sm4Gcm.update(data : bytes)` +* `Sm4Gcm.finish() -> bytes` + +GCM模式和CBC、CTR、HMAC不同之处还在于可选的IV长度和MAC长度,其中IV的长度必须在`SM4_GCM_MIN_IV_SIZE`和`SM4_GCM_MAX_IV_SIZE`之间,长度为`SM4_GCM_DEFAULT_IV_SIZE`有最佳的计算效率。MAC的长度也是可选的,通过`init`方法中的`taglen`设定,其长度不应低于8字节,不应长于`SM4_GCM_DEFAULT_TAG_SIZE = 16`字节。 + +下面例子展示SM4-GCM加密和解密的过程。 + +```python +>>> from gmssl import * +>>> key = rand_bytes(SM4_KEY_SIZE) +>>> iv = rand_bytes(SM4_GCM_DEFAULT_IV_SIZE) +>>> aad = b'Additional auth-data' +>>> plaintext = b'abc' +>>> taglen = SM4_GCM_DEFAULT_TAG_SIZE +>>> sm4_enc = Sm4Gcm(key, iv, aad, taglen, DO_ENCRYPT) +>>> ciphertext = sm4_enc.update(plaintext) +>>> ciphertext += sm4_enc.finish() +>>> sm4_dec = Sm4Gcm(key, iv, aad, taglen, DO_DECRYPT) +>>> decrypted = sm4_dec.update(ciphertext) +>>> decrypted += sm4_dec.finish() +``` + +通过上面的例子可以看出,SM4-GCM加密模式中可以通过指定了一个不需要加密的字段`aad`,注意`aad`是不会在`update`中输出的。由于GCM模式输出个外的完整性标签,因此`update`和`finish`输出的总密文长度会比总的输入明文长度多`taglen`个字节。 + +### Zuc序列密码 + +祖冲之密码算法(ZU Cipher, ZUC)是一种序列密码,密钥和IV长度均为16字节。作为序列密码ZUC可以加密可变长度的输入数据,并且输出的密文数据长度和输入数据等长,因此适合不允许密文膨胀的应用场景。在国密算法体系中,ZUC算法的设计晚于SM4,在32位通用处理器上通常比SM4-CBC明显要快。 + +在安全性方面,不建议在一组密钥和IV的情况下用ZUC算法加密大量的数据(比如GB级或TB级),避免序列密码超长输出时安全性降低。另外ZUC算法本身并不支持数据的完整性保护,因此在采用ZUC算法加密应用数据时,应考虑配合HMAC-SM3提供完整性保护。ZUC的标准中还包括针对移动通信底层数据报文加密的128-EEA3方案和用于消息完整性保护的128-EIA3算法,目前GmSSL-Python中不支持这两个算法。 + +模块`gmssl`中包含如下Sm4Gcm的常量: + +* `ZUC_KEY_SIZE` +* `ZUC_IV_SIZE` + +`Zuc`类实现了基本的Zuc序列密码算法,类`Zuc`的对象是由构造函数生成的。 + +``` +gmssl.Zuc(key, iv) +``` + +对象Sm4Cbc的方法: + +* `Zuc.update(data : bytes)` +* `Zuc.finish() -> bytes` + +`Zuc`类的接口说明如下: + +- 序列密码通过生成密钥序列和输入数据进行异或操作的方式来加密或解密,因此序列密码的加密和解密的过程一致,因此创建`Zuc`对象时不需要格外的参数表明加密还是解密。 +- 由于CTR模式实际上是以分组密码实现了序列密码的能力,因此可以发现`Zuc`和`Sm4Cbc`的接口是完全一致的。 +- ZUC算法内部实现是以32比特字(4字节)为单位进行处理,因此`Zuc`实现加解密过程中也有内部的状态缓冲区,因此`update`的输出长度可能和输入长度不一致,调用方应该保证输出缓冲区长度比输入长度长`BLOCK_SIZE`个字节。注意,`BLOCK_SIZE`的实际值在未来也有可能会变化。 + +下面的例子展示了`Zuc`的加密和解密过程。 + +```python +>>> from gmssl import * +>>> iv = rand_bytes(ZUC_IV_SIZE) +>>> plaintext = b'abc' +>>> zuc_enc = Zuc(key, iv) +>>> ciphertext = zuc_enc.update(plaintext) +>>> ciphertext += zuc_enc.finish() +>>> zuc_dec = Zuc(key, iv) +>>> decrypted = zuc_dec.update(ciphertext) +>>> decrypted += zuc_dec.finish() +``` + +### SM2 + +SM2是国密标准中的椭圆曲线公钥密码,包含数字签名算法和公钥加密算法。SM2相关的功能由类`Sm2Key`和`Sm2Signature`实现,其中`Sm2Key`实现了SM2密钥对的生成、基础的加密和签名方案,`Sm2Signature`类实现了对任意长度消息签名的签名方案。 + +模块`gmssl`中包含如下Sm2Key的常量: + +* `SM2_DEFAULT_ID` +* `SM2_MAX_SIGNATURE_SIZE` +* `SM2_MIN_PLAINTEXT_SIZE` +* `SM2_MAX_PLAINTEXT_SIZE ` +* `SM2_MIN_CIPHERTEXT_SIZE` +* `SM2_MAX_CIPHERTEXT_SIZE` + +`Sm2Key`类实现了基本的SM4-CBC分组密码算法,类`Sm2Key`的对象是由构造函数生成的。 + +``` +gmssl.Sm2Key() +``` + +对象Sm2Key的方法: + +* `Sm2Key.generate_key()` +* `Sm2Key.compute_z()` +* `Sm2Key.export_encrypted_private_key_info_pem()` +* `Sm2Key.import_encrypted_private_key_info_pem()` +* `Sm2Key.export_public_key_info_pem()` +* `Sm2Key.import_public_key_info_pem()` +* `Sm2Key.sign()` +* `Sm2Key.verify()` +* `Sm2Key.encrypt()` +* `Sm2Key.decrypt()` + +需要注意的是,通过构造函数生成的新`Sm2Key`对象是一个空白的对象,可以通过`generate_key`方法生成一个新的密钥对,或者通过导入函数从外部导入密钥。`Sm2Key`一共提供了2个不同的导入方法: + +- `import_encrypted_private_key_info_pem` 从加密的PEM文件中导入SM2私钥,因此调用时需要提供PEM文件的路径和解密的口令(Password)。 +- `import_public_key_info_pem`从PEM文件中导入SM2公钥,只需要提供文件的路径,不需要提供口令。 + +上面2个导入函数也都有对应的导出函数。从PEM文件中导入导出公钥私钥和`gmssl`命令行工具的默认密钥格式一致,并且在处理私钥时安全性更高。因此建议在默认情况下,在导入导出私钥时默认采用加密的PEM文件格式。 + +下面的代码片段展示了`Sm2Key`密钥对和导出为加密的PEM私钥文件: + +```python +>>> sm2 = Sm2Key() +>>> sm2.generate_key() +>>> +>>> sm2.export_encrypted_private_key_info_pem('sm2.pem', 'password') +>>> private_key = Sm2Key() +>>> private_key.import_encrypted_private_key_info_pem('sm2.pem', 'password') +``` + +用文本编辑器打开`sm2.pem`文件可以看到如下内容 + +``` +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBBjBhBgkqhkiG9w0BBQ0wVDA0BgkqhkiG9w0BBQwwJwQQaADudE4Ycenuoth4 +ZqcewgIDAQAAAgEQMAsGCSqBHM9VAYMRAjAcBggqgRzPVQFoAgQQ9aUmOaXn0mZD +7xhBdd+FlQSBoKc0GG7US2SsmQIrppPNQeyDFpG8xthNI6G4R/YbSPJCvSMJ/9y3 +LQ/jrdUumuKevgg9miAcjbKndm7HC07lMYUk1ZXlaEG/1awER4RJsRvZ64GlBQOV +D7jbu93mSs9t3SDt4TniDua5WyXo5Y8S6DjkkUD5epHRzYZ4uFFC/8pTeehK7X+S +p2b6CndfB6H4LrvCGuRnjX4l5Q5AgfWDmWU= +-----END ENCRYPTED PRIVATE KEY----- +``` + +下面的代码片段展示了`Sm2Key`导出为PEM公钥文件,这是一个标准的PKCS #8 EncryptPrivateKeyInfo类型并且PEM编码的私钥文件格式,`openssl pkeyutil`命令行工具也默认采用这个格式的私钥,但是由于GmSSL在私钥文件中采用SM4-CBC、HMAC-SM3组合加密了SM2的私钥,因此对于默认使用3DES的`openssl`等工具可能无法解密这个私钥(即使这个工具包含SM2算法的实现)。 + +```python +>>> sm2.export_public_key_info_pem('sm2pub.pem') +>>> public_key = Sm2Key() +>>> public_key.import_public_key_info_pem('sm2pub.pem') +``` + +用文本编辑器打开`sm2pub.pem`文件可以看到如下内容 + +``` +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE5djp+Gw/Wdg9JwVwYDiQn1AocezI +C2qT54fqJBNWevCNru8ENwj4t/52Yf50LF5+fMlcoWPbfm/TcCgYPb49jw== +-----END PUBLIC KEY----- +``` + +由于公钥文件是不加密的,因此这个公钥可以被支持SM2的第三方工具、库打开和访问。 + +`Sm2Key`类除了`generate_key`方法之外,提供了`compute_z`、`sign`、`verify`、`encrypt`、`decrypt`这几个密码计算相关的方法。 + +其中`compute_z`是由公钥和用户的字符串ID值计算出一个称为“Z值”的哈希值,用于对消息的签名。由于`Sm2Signature`类中提供了SM2消息签名的完整功能,因此这个`compute_z`方法只是用于实验验证。 + +```python +>>> z = public_key.compute_z(SM2_DEFAULT_ID) +``` + +类`Sm2Key`的`sign`和`verify`方法实现了SM2签名的底层功能,这两个方法不支持对数据或消息的签名,只能实现对SM3哈希值的签名和验证,并没有实现SM2签名的完整功能。应用需要保证调用时提供的`dgst`参数的字节序列长度为32。只有密码协议的底层开发者才需要调用`compute_z`、`sign`、`verify`这几个底层方法。 + +```python +>>> dgst = sm3.digest() +>>> sig = private_key.sign(dgst) +>>> ret = public_key.verify(dgst, sig) +``` + +类`Sm2Key`的`encrypt`和`decrypt`方法实现了SM2加密和解密功能。注意,虽然SM2标准中没有限制加密消息的长度,但是公钥加密应该主要用于加密较短的对称密钥、主密钥等密钥数据,因此GmSSL库中限制了SM2加密消息的最大长度。应用在调用`encrypt`时,需要保证输入的明文长度不超过`SM2_MAX_PLAINTEXT_SIZE `的限制。如果需要加密引用层的消息,应该首先生成对称密钥,用SM4-GCM加密消息,再用SM2加密对称密钥。 + +```python +>>> ciphertext = public_key.encrypt(plaintext) +>>> decrypted = private_key.decrypt(ciphertext) +``` + +类`Sm2Signatue`提供了对任意长消息的签名、验签功能。 + +模块`gmssl`中包含如下Sm2Signatue的常量: + +* `DO_ENCRYPT = True` +* `DO_DECRYPT = False` +* `DO_SIGN = True` +* `DO_VERIFY = False` + +`Sm2Signatue`类实现了基本的SM4-CBC分组密码算法,类`Sm2Signatue`的对象是由构造函数生成的。 + +``` +gmssl.Sm2Signatue(sm2_key, signer_id = SM2_DEFAULT_ID, sign = DO_SIGN) +``` + +对象Sm2Signatue的方法: + +* `Sm2Signatue.update()` +* `Sm2Signatue.sign()` +* `Sm2Signatue.verify()` + +在生成`Sm2Signature`对象时,不仅需要提供`Sm2Key`,还需要提供签名方的字符串ID,以满足SM2签名的标准。如果提供的`Sm2Key`来自于导入的公钥,那么这个`Sm2Signature`对象只能进行签名验证操作,即在构造时`DO_SIGN = False`,并且只能调用`verify`方法,不能调用`sign`方法。 + +```python +signer = Sm2Signature(private_key, SM2_DEFAULT_ID, DO_SIGN) +signer.update(b'abc') +sig2 = signer.sign() + +verifier = Sm2Signature(public_key, SM2_DEFAULT_ID, DO_VERIFY) +verifier.update(b'abc') +ret = verifier.verify(sig2) +``` + +不管是`Sm2Key`的`sign`还是`Sm2Signature`的`sign`方法输出的都是DER编码的签名值。这个签名值的第一个字节总是`0x30`,并且长度是可变的,常见的长度包括70字节、71字节、72字节,也可能短于70字节。一些SM2的实现不能输出DER编码的签名,只能输出固定64字节长度的签名值。可以通过签名值的长度以及首字节的值来判断SM2签名值的格式。 + +### SM2数字证书 + +类`Sm2Certificate`实现了SM2证书的导入、导出、解析和验证等功能。这里的“SM2证书”含义和“RSA证书”类似,是指证书中的公钥字段是SM2公钥,证书中签名字段是SM2签名,证书格式就是标准的X.509v3证书。由于GmSSL库目前只支持SM2签名算法,不支持ECDSA、RSA、DSA等签名算法,因此`Sm2Certificate`类无法支持其他公钥类型的证书。注意,有一种不常见的情况,一个证书可以公钥是SM2公钥而数字签名是RSA签名,这种证书可能是采用RSA公钥的CA中心对SM2证书请求签发而产生的,由于目前GmSSL不支持SM2之外的签名算法,因此`Sm2Certificate`不支持此类证书。 + +类`Sm2Certificate`只支持SM2证书的解析和验证等功能,不支持SM2证书的签发和生成,如果应用需要实现证书申请(即生成CSR文件)或者自建CA签发证书功能,那么可以通过GmSSL库或者`gmssl`命令行工具实现,GmSSL-Python目前不考虑支持证书签发、生成的相关功能。 + +模块`gmssl`中包含如下Sm2Certificate的常量: + +* `ZUC_KEY_SIZE` +* `ZUC_IV_SIZE` + +Sm2Certificate的方法: + +* `Sm2Certificate.import_pem()` +* `Sm2Certificate.get_raw()` +* `Sm2Certificate.export_pem()` +* `Sm2Certificate.get_serial_number()` +* `Sm2Certificate.get_issuer()` +* `Sm2Certificate.get_subject()` +* `Sm2Certificate.get_subject_public_key()` +* `Sm2Certificate.get_validity()` +* `Sm2Certificate.verify_by_ca_certificate()` + +新生成的`Sm2Certificate`对象中的证书数据为空,必须通过导入证书数据才能实现真正的初始化。证书有很多种不同格式的编码,如二进制DER编码的`crt`文件或者文本PEM编码的`cer`文件或者`pem`文件,有的证书也会把二进制的证书数据编码为一串连续的十六进制字符串,也有的CA会把多个证书构成的证书链封装在一个PKCS#7格式的密码消息中,而这个密码消息可能是二进制的,也可能是PEM编码的。 + +在这些格式中最常用的格式是本文的PEM格式,这也是`Sm2Certificate`类默认支持的证书格式。下面这个例子中就是一个证书的PEM文件内容,可以看到内容是由文本构成的,并且总是以`-----BEGIN CERTIFICATE-----`一行作为开头,以`-----END CERTIFICATE-----`一行作为结尾。PEM格式的好处是很容易用文本编辑器打开来,容易作为文本被复制、传输,一个文本文件中可以依次写入多个证书,从而在一个文件中包含多个证书或证书链。因此PEM格式也是CA签发生成证书使用的最主流的格式。由于PEM文件中头尾之间的文本就是证书二进制DER数据的BASE64编码,因此PEM文件也很容易和二进制证书进行手动或自动的互相转换。 + +``` +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE----- +``` + +通过`gmssl certparse`命令可以打印这个证书的内容 + +```python +$ gmssl certparse -in ROOTCA.pemCertificate + tbsCertificate + version: v3 (2) + serialNumber: 69E2FEC0170AC67B + signature + algorithm: sm2sign-with-sm3 + parameters: NULL + issuer + countryName: CN + organizationName: NRCAC + commonName: ROOTCA + validity + notBefore: Sat Jul 14 11:11:59 2012 + notAfter: Mon Jul 7 11:11:59 2042 + subject + countryName: CN + organizationName: NRCAC + commonName: ROOTCA + subjectPulbicKeyInfo + algorithm + algorithm: ecPublicKey + namedCurve: sm2p256v1 + subjectPublicKey + ECPoint: 0430F09C6BAA6681C721B137F652705E2FDAEDA789F0FA2B64D4ACEB99B9EAA34E655309309562BEE0E22BB45740AA745357B43DBF586D92FE364EC22EB73775DB + extensions + Extension + extnID: AuthorityKeyIdentifier (2.5.29.35) + AuthorityKeyIdentifier + keyIdentifier: 4C32B197D9331BC4A605C1C6E58B625BF0977658 + Extension + extnID: BasicConstraints (2.5.29.19) + BasicConstraints + cA: true + Extension + extnID: KeyUsage (2.5.29.15) + KeyUsage: keyCertSign,cRLSign + Extension + extnID: SubjectKeyIdentifier (2.5.29.14) + SubjectKeyIdentifier: 4C32B197D9331BC4A605C1C6E58B625BF0977658 + signatureAlgorithm + algorithm: sm2sign-with-sm3 + parameters: NULL + signatureValue: 304502201B56D22DE397A77A01F07EDBE775BE08A38F9763E49E6584ABF94C86D9F6E479022100DA1C3816C5616D9C2AC18C7D7AFD6DC4CE7EFF53F563A39C48A43A22561B0BC2 +``` + +可以看到一个证书的主要内容是包含证书持有者信息的tbsCertificate字段,以及权威机构对tbsCertificate字段的签名算法signatureAlgorithm和签名值signatureValue。因为这个证书是SM2证书,因此其中的签名算法是`sm2sign-with-sm3`,签名值是`0x30`开头的DER编码的可变长度签名值。 + +证书中持有者信息包含如下字段: + +- 证书格式的版本号 version,目前版本号应该是第3版,即`v3`。 +- 证书的序列号 serialNumber,早期证书中的序列号是一个递增的整数,但是近年来的证书必须是随机值。、 +- 证书的签名算法 signature,这个字段的值必须和最后的signatureAlgorithm保持一致。 +- 证书签发机构的名字 issuer,通常是一个CA中心,issuer的内容是由多个Key-Value格式的多个字段组合而成,其中的Key包括国家countryName、省stateOrProvinceName、城市localityName、组织organizationName、组织内单位organizationUnitName、常用名commonName等,其中commonName应该是CA机构的名字。 +- 证书的有效期 validity,有效期是由起始时间notBefore和终止时间notAfter两个时间构成的,如果当前时间早于notBefore,说明证书还没有启用,如果当前时间晚于notAfter,说明证书已经过期作废。 +- 证书持有者(证书主体)的名字 subject,这个字段的数据类型和issuer是一样的,一般对于网站服务器证书来说,subject的commonName应该是服务器的域名。 +- 证书持有者的公钥信息subjectPulbicKeyInfo,对于SM2证书来说,公钥算法必须是ecPublicKey并且曲线必须是sm2p256v1,公钥的值是一个编码的椭圆曲线点,这个值总是以`0x04`开头,后跟总共64字节的点的X、Y坐标。 +- 证书中通常还有多个扩展,其中有的扩展是关键的(critical)扩展,有些则不重要,只是提供了参考信息,这里介绍两个比较重要的扩展: + - BasicConstraints (2.5.29.19) 扩展,这个扩展标明证书是权威机构的CA证书(比如北京市CA中心)还是普通用户的证书(比如某个网站的证书),如果一个证书中没有包含这个扩展,或者扩展中的`cA: true`字段不存在,那么这个证书不能作为CA证书使用。 + - KeyUsage (2.5.29.15) 扩展,这个扩展表明证书持有者公钥的用途,类似于驾驶证中的A照、B照、C照等划分大客车、大货车、小客车准驾车型,密钥用途表明证书是否可以签名、加密、签发证书等用途。如果一个数字签名附带的证书中有KeyUsage扩展并且扩展包含的密钥用途只有加密,没有签名,那么这个证书对于这个签名来说就是无效的。 + +`Sm2Certificate`类只支持第3版证书的解析,因此没有提供`getVersion`方法获取证书的版本号。GmSSL支持常用扩展的解析和验证,如果某个证书中有GmSSL不支持的非关键扩展,那么GmSSL会忽略这个扩展,如果存在GmSSL不识别或无法验证的关键性扩展,那么GmSSL在解析证书的时候会返回失败,因此如果`Sm2Certificate`类`import_pem`成功,说明证书的格式、内容是可以识别的并且是正确的。 + +拿他其他人提供的证书还必须验证该证书是否有效,首先需要检查证书的有效期。目前很多CA中心的策略是颁发有效期尽可能短的证书(比如3个月有效期),因此拿到的证书很有可能已经过期了。可以通过`get_validity()`方法获得有效期时间,判断当前时间点是否在有效期范围内。如果要验证过去某个时间点证书支持者的操作是否合法,那么应该检查那个时间点是否在证书的有效期范围内。 + +对证书最重要的验证之一是这个证书是否是由权威机构签发的。证书用户需要先通过`get_issuer`方法获得签发机构的名字,确认这个签发机构是否可信。例如,如果一个北京市政府机构的证书中的签发机构是一个商业性CA中心,那么这个证书的有效性就是存疑的。在确认CA中心名字(即整个issuer字段)无误之后,还需要通过Issuer字段从可信的渠道获得这个CA中心的证书,然后调用`verify_by_ca_certificate`方法,用获得的CA证书验证当前证书中的签名是否正确。在典型的应用中,开发者和软件发行方应该将所有可信的CA中心的证书硬编码到软件中,或者内置到软件或系统的证书库中,避免应用的用户需要手动添加、导入CA证书。 + +所有的私钥都有泄露的可能,安全性不佳的自建CA有被攻击者渗透的可能,商业性的小CA甚至有被收购、收买的可能,因此有效期范围内的证书也存在被作废的可能。检查证书是否作废主要是通过证书作废列表CRL文件检查,或者通过证书状态在线检查协议OCSP来在线查询。目前`Sm2Certificate`类没有支持证书作为查询的功能,开发者暂时可以通过`GmSSL`库或者`gmssl`命令行工具进行CRL的检查。 + +在完成所有证书检查之后,应用可以完全信任从证书中读取的持有者身份信息(subject)和支持有的公钥了,这两个信息分别通过`get_subject()`和`get_subject_public_key`方法获得。 + +### SM9基于身份的密码 + +SM9算法属于基于身份的密码。基于身份的密码是一种“高级”的公钥密码方案,在具备常规公钥密码加密、签名等密码功能的同时,基于身份的密码体系不需要CA中心和数字证书体系。SM9方案的基本原理是,可以由用户的唯一身份ID(如对方的电子邮件地址、域名或ID号等),从系统的全局主密钥中导出对应的私钥或公钥,导出密钥的正确性是由算法保证的,因此在进行加密、验签的时候,只需要获得解密方或签名方的ID即可,不再需要对方的数字证书了。因此如果应用面对的是一个内部的封闭环境,所有参与用户都是系统内用户,那么采用SM9方案而不是SM2证书和CA的方案,可以简化系统的开发、设计和使用,并降低后续CA体系的维护成本。 + +对应数字证书体系中的CA中心,SM9体系中也存在一个权威中心,用于生成全局的主密钥(MasterKey),并且为系统中的每个用户生成、分配用户的私钥。和SM2密钥对一样,SM9的主密钥也包含私钥和公钥,其中主公钥(PublicMasterKey)是可以导出并公开给系统中全体用户的。而SM9中用户的密钥对比较特殊,其中的公钥并不能从私钥中导出,SM9用户密钥需要包含用户的ID起到公钥的作用,在加密和验证签名等密码计算中,真正的用户公钥是在计算中,在运行时通过用户ID从主公钥中导出的。因此从应用的角度看,SM9中用户的公钥就是一个字符串形式的ID。 + +SM9算法体系中包括SM9加密、SM9签名和SM9密钥交换协议,GmSSL-Java中实现了SM9加密和SM9签名,没有实现SM9密钥交换。其中SM9加密功能包含`Sm9EncMasterKey`类和`Sm9EncKey`类,分别实现了SM9加密主密钥和SM9加密用户密钥,SM9签名功能包含`Sm9SignMasterKey`类、`Sm9SignKey`类和`Sm9Signature`类,分别实现了SM9签名主密钥、SM9签名用户密钥和SM9签名功能。 + +和SM2算法中相同的密钥对既可以用于加密又可以用于签名不同,SM9中加密、签名的主密钥、用户密钥的组成是完全不同的,因此GmSSL中分别实现为不同的类。SM9签名由于需要特殊的哈希过程,因此SM9用户签名私钥不提供直接签哈希值的底层签名功能实现,只能通过`Sm9Signature`实现对消息的签名、验证。 + +模块`gmssl`中包含如下Sm9EncMasterKey的常量: + +* `SM9_MAX_ID_SIZE` +* `SM9_MAX_PLAINTEXT_SIZE` +* `SM9_MAX_CIPHERTEXT_SIZE` + +SM9加密主密钥由类`Sm9EncMasterKey`实现。 + +``` +gmssl.Sm9EncMasterKey() +``` + +对象Sm9EncMasterKey的接口包括: + +* `Sm9EncMasterKey.generate_master_key()` 主密钥的生成 +* `Sm9EncMasterKey.extract_key()`用户私钥的生成 +* `Sm9EncMasterKey.import_encrypted_master_key_info_pem()` 主密钥的导入,注意`Sm2Key`的对应接口类似,这里主密钥都是以口令加密的方式导出到文件上的 +* `Sm9EncMasterKey.export_encrypted_master_key_info_pem()`主密钥的导出 +* `Sm9EncMasterKey.export_public_master_key_pem()`主公钥(主密钥的公钥部分)的导入 +* `Sm9EncMasterKey.import_public_master_key_pem()`主公钥(主密钥的公钥部分)的导出 +* `Sm9EncMasterKey.encrypt()`数据加密 + +这个类的用户包括两个不同角色,权威中心和用户。其中权威中心调用主密钥的生成、主密钥的导入导出、主公钥导出和用户私钥生成这几个接口,而用户调用主公钥导入和加密这两个接口。 + +类`Sm9EncKey`对象是由`Sm9SEncMasterKey`的`extract_key`方法生成的。 + +``` +gmssl.Sm9EncKey() +``` + +对象Sm9EncKey的方法: + +* `Sm9EncKey.get_id()` +* `Sm9EncKey.import_encrypted_private_key_info_pem()` +* `Sm9EncKey.export_encrypted_private_key_info_pem()` +* `Sm9EncKey.decrypt()` + +类`Sm9EncKey`提供了解密、导入导出等接口,由于在SM9中用户密钥总是包含私钥的,因此导出的是经过口令加密的密钥。 + +下面的例子中给出了SM9加密方案的主密钥生成、用户密钥导出、加密、解密的整个过程。 + +```python +master_key = Sm9EncMasterKey() +master_key.generate_master_key() +print("SM9 master key generated") + +master_key.export_encrypted_master_key_info_pem('enc_msk.pem', 'password') +master_key.export_public_master_key_pem('enc_mpk.pem') +print("Export master key and public master key") + +# Encrypt +master_pub = Sm9EncMasterKey() +master_pub.import_public_master_key_pem('enc_mpk.pem') + +plaintext = rand_bytes(SM4_KEY_SIZE + SM3_HMAC_MIN_KEY_SIZE) + +receiver_id = 'Alice' + +ciphertext = master_pub.encrypt(plaintext, receiver_id) + +# Decrypt +master = Sm9EncMasterKey() +master.import_encrypted_master_key_info_pem('enc_msk.pem', 'password') + +receiver_key = master.extract_key(receiver_id) + +decrypted = receiver_key.decrypt(ciphertext) +``` + +SM9签名功能由`Sm9SignMasterKey`、`Sm9SignKey`和`Sm9Signature`几个类实现,前两者在接口上和SM9加密非常类似,只是这两个类不直接提供签名、验签的功能。 + +```python +gmssl.Sm9SignMasterKey() +gmssl.Sm9SignKey(owner_id) +``` + +对象Sm9SignMasterKey的方法: + +* `Sm9SignMasterKey.generate_master_key()` +* `Sm9SignMasterKey.extract_key()` +* `Sm9SignMasterKey.import_encrypted_master_key_info_pem()` +* `Sm9SignMasterKey.export_encrypted_master_key_info_pem()` +* `Sm9SignMasterKey.export_public_master_key_pem()` +* `Sm9SignMasterKey.import_public_master_key_pem()` + +对象Sm9SignKey的方法: + +- `Sm9SignKey.get_id()` +- `Sm9SignKey.import_encrypted_private_key_info_pem()` +- `Sm9SignKey.export_encrypted_private_key_info_pem()` + +类`Sm9Signature`实现对数据的SM9签名和验证功能。SM9签名时需要提供`Sm9SignKey`类型的签名方私钥(其中包含签名者的ID),在验证签名时需要提供`Sm9SignMasterKey`格式的系统主公钥和签名方的ID。`Sm9Signature`和`Sm2Signature`提供类似的`update`、`sign`、`verify`接口,只是在验证的时候需要提供的不是公钥,而是系统的主公钥和签名方的ID。 + +```python +gmssl.Sm9Signature(sign = DO_SIGN) +``` + +模块`gmssl`中包含如下Sm9Signature的常量: + +- `SM9_SIGNATURE_SIZE` + +对象Sm9Signature的方法: + +- `Sm9Signature.reset()` +- `Sm9Signature.update()` +- `Sm9Signature.sign()` +- `Sm9Signature.verify()` + +下面的例子展示了SM9签名的主密钥生成、用户私钥生成、签名、验证的过程。 + +```python +master_key = Sm9SignMasterKey() +master_key.generate_master_key() +print("SM9 master key generated") + +master_key.export_encrypted_master_key_info_pem('sign_msk.pem', 'password') +master_key.export_public_master_key_pem('sign_mpk.pem') +print("Export master key and public master key") + + +master = Sm9SignMasterKey() +master.import_encrypted_master_key_info_pem('sign_msk.pem', 'password') + +signer_id = 'Alice' +key = master.extract_key(signer_id) + +message = "Message to be signed" + +sign = Sm9Signature(DO_SIGN) +sign.update(message.encode('utf-8')) +sig = sign.sign(key) + + +master_pub = Sm9SignMasterKey() +master_pub.import_public_master_key_pem('sign_mpk.pem') + +verify = Sm9Signature(DO_VERIFY) +verify.update(message.encode('utf-8')) +ret = verify.verify(sig, master_pub, signer_id) +``` From 89b9041b3b67b3d1e8f77e827e7247166a1bc63e Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 12 Oct 2023 19:41:17 +0800 Subject: [PATCH 15/18] Check libgmssl version and if OS is win32 --- gmssl.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gmssl.py b/gmssl.py index a6ee402..00a21b1 100755 --- a/gmssl.py +++ b/gmssl.py @@ -11,12 +11,18 @@ from ctypes import * from ctypes.util import find_library import datetime +import sys if find_library('gmssl') == None: raise ValueError('Install GmSSL dynamic library from https://github.com/guanzhi/GmSSL') - gmssl = cdll.LoadLibrary(find_library("gmssl")) -libc = cdll.LoadLibrary(find_library('c')) +if gmssl.gmssl_version_num() < 30101: + raise ValueError('GmSSL version < 3.1.1') + +if sys.platform == 'win32': + libc = cdll.LoadLibrary(find_library('msvcrt')) +else: + libc = cdll.LoadLibrary(find_library('c')) class NativeError(Exception): From 93009fe40c45541f592e7e8df7bac909f7d76569 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 12 Oct 2023 19:46:06 +0800 Subject: [PATCH 16/18] Update PHP version --- gmssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gmssl.py b/gmssl.py index 00a21b1..d9a786d 100755 --- a/gmssl.py +++ b/gmssl.py @@ -35,7 +35,7 @@ class StateError(Exception): Crypto state error ''' -GMSSL_PYTHON_VERSION = "2.2.1" +GMSSL_PYTHON_VERSION = "2.2.2" def gmssl_library_version_num(): return gmssl.gmssl_version_num() From 92483c9c726f73ec93d76f636e94cec3d59d25bc Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 12 Oct 2023 19:51:19 +0800 Subject: [PATCH 17/18] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ff4ec58..cd544f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "gmssl_python" -version = "2.2.1" +version = "2.2.2" authors = [ { name="Zhi Guan", email="guan@pku.edu.cn" }, ] From 1a4d65b475d88a6376997f7f1cf6fbcfc3681b20 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 12 Oct 2023 20:09:39 +0800 Subject: [PATCH 18/18] Create DEVELOP.md --- DEVELOP.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 DEVELOP.md diff --git a/DEVELOP.md b/DEVELOP.md new file mode 100644 index 0000000..4f8874a --- /dev/null +++ b/DEVELOP.md @@ -0,0 +1,12 @@ +# GmSSL-Python Develop + +## Publish to PiPy + +See https://packaging.python.org/distributing/ + +1. Update version in `gmssl.py` +2. Update version in `pyproject.toml` +3. Build package, run `python3 -m build` +4. Publish package to PiPy, run `python3 -m twine upload dist/*` + +