bk-crypto-python-sdk 3.0.0__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/PKG-INFO +9 -2
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/__init__.py +8 -1
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/base.py +17 -2
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/rsa.py +78 -23
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/configs.py +10 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/constants.py +7 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/basic/ciphers.py +25 -20
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/settings.py +3 -16
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/__init__.py +8 -1
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/aes.py +35 -3
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/base.py +27 -16
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/configs.py +2 -1
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/types.py +1 -4
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/utils/convertors.py +1 -1
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/pyproject.toml +11 -3
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/readme.md +6 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/LICENSE.txt +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/sm2.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/interceptors.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/options.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/basic/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/ciphers.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/fields.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/init_configs.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/selectors.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/drf/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/sm4.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/interceptors.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/options.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/utils/__init__.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/utils/base_interceptors.py +0 -0
- {bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/utils/module_loding.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bk-crypto-python-sdk
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: bk-crypto-python-sdk is a lightweight cryptography toolkit for Python applications based on Cryptodome / tongsuopy and other encryption libraries.
|
|
5
5
|
Author: TencentBlueKing
|
|
6
6
|
Author-email: contactus_bk@tencent.com
|
|
@@ -10,9 +10,10 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Provides-Extra: gm
|
|
13
14
|
Requires-Dist: dacite (>=1.8.1,<2.0.0)
|
|
14
15
|
Requires-Dist: pycryptodomex (>=3.20.0,<4.0.0)
|
|
15
|
-
Requires-Dist: tongsuopy-crayon (>=1.0.2b5,<2.0.0)
|
|
16
|
+
Requires-Dist: tongsuopy-crayon (>=1.0.2b5,<2.0.0) ; extra == "gm"
|
|
16
17
|
Requires-Dist: wrapt (>=2.2,<3.0)
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
18
19
|
|
|
@@ -52,6 +53,12 @@ Description-Content-Type: text/markdown
|
|
|
52
53
|
$ pip install bk-crypto-python-sdk
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
仅使用 AES、RSA 时无需安装国密依赖。需要 SM2、SM4 时安装 `gm` extra:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
$ pip install "bk-crypto-python-sdk[gm]"
|
|
60
|
+
```
|
|
61
|
+
|
|
55
62
|
### Usage
|
|
56
63
|
|
|
57
64
|
> 更多用法参考:[使用文档](https://github.com/TencentBlueKing/crypto-python-sdk/blob/main/docs/usage.md)
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/__init__.py
RENAMED
|
@@ -14,4 +14,11 @@ __all__ = ["BaseAsymmetricCipher", "RSAAsymmetricCipher", "SM2AsymmetricCipher"]
|
|
|
14
14
|
|
|
15
15
|
from .base import BaseAsymmetricCipher
|
|
16
16
|
from .rsa import RSAAsymmetricCipher
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __getattr__(name):
|
|
20
|
+
if name == "SM2AsymmetricCipher":
|
|
21
|
+
from .sm2 import SM2AsymmetricCipher
|
|
22
|
+
|
|
23
|
+
return SM2AsymmetricCipher
|
|
24
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/base.py
RENAMED
|
@@ -10,7 +10,6 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o
|
|
|
10
10
|
specific language governing permissions and limitations under the License.
|
|
11
11
|
"""
|
|
12
12
|
import abc
|
|
13
|
-
import copy
|
|
14
13
|
import typing
|
|
15
14
|
from dataclasses import dataclass
|
|
16
15
|
|
|
@@ -132,7 +131,7 @@ class BaseAsymmetricCipher:
|
|
|
132
131
|
**options,
|
|
133
132
|
):
|
|
134
133
|
|
|
135
|
-
options: typing.Dict[str, typing.Any] =
|
|
134
|
+
options: typing.Dict[str, typing.Any] = dict(options)
|
|
136
135
|
|
|
137
136
|
# init config
|
|
138
137
|
self.config = from_dict(self.CONFIG_DATA_CLASS, options)
|
|
@@ -206,6 +205,11 @@ class BaseAsymmetricCipher:
|
|
|
206
205
|
ciphertext: str = self.config.convertor.to_string(ciphertext_bytes)
|
|
207
206
|
return self.config.interceptor.after_encrypt(ciphertext, cipher=self)
|
|
208
207
|
|
|
208
|
+
@key_obj_checker(constants.AsymmetricKeyAttribute.PUBLIC_KEY)
|
|
209
|
+
def encrypt_bytes(self, plaintext: bytes) -> str:
|
|
210
|
+
"""加密二进制数据,不执行文本编码转换。"""
|
|
211
|
+
return self.config.convertor.to_string(self._encrypt_bytes(plaintext))
|
|
212
|
+
|
|
209
213
|
@key_obj_checker(constants.AsymmetricKeyAttribute.PRIVATE_KEY)
|
|
210
214
|
def decrypt(self, ciphertext: str) -> str:
|
|
211
215
|
"""
|
|
@@ -219,6 +223,17 @@ class BaseAsymmetricCipher:
|
|
|
219
223
|
plaintext: str = self.config.convertor.decode_plaintext(plaintext_bytes, encoding=self.config.encoding)
|
|
220
224
|
return self.config.interceptor.after_decrypt(plaintext)
|
|
221
225
|
|
|
226
|
+
@key_obj_checker(constants.AsymmetricKeyAttribute.PRIVATE_KEY)
|
|
227
|
+
def decrypt_bytes(self, ciphertext: str) -> bytes:
|
|
228
|
+
"""解密二进制数据,不执行文本编码转换。"""
|
|
229
|
+
return self._decrypt_bytes(self.config.convertor.from_string(ciphertext))
|
|
230
|
+
|
|
231
|
+
def _encrypt_bytes(self, plaintext_bytes: bytes) -> bytes:
|
|
232
|
+
return self._encrypt(plaintext_bytes)
|
|
233
|
+
|
|
234
|
+
def _decrypt_bytes(self, ciphertext_bytes: bytes) -> bytes:
|
|
235
|
+
return self._decrypt(ciphertext_bytes)
|
|
236
|
+
|
|
222
237
|
@key_obj_checker(constants.AsymmetricKeyAttribute.PRIVATE_KEY)
|
|
223
238
|
def sign(self, plaintext: str) -> str:
|
|
224
239
|
"""
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/rsa.py
RENAMED
|
@@ -14,9 +14,10 @@ specific language governing permissions and limitations under the License.
|
|
|
14
14
|
import typing
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
|
|
17
|
-
from Cryptodome import
|
|
17
|
+
from Cryptodome.Cipher import PKCS1_OAEP
|
|
18
18
|
from Cryptodome.Hash import SHA1
|
|
19
19
|
from Cryptodome.PublicKey import RSA
|
|
20
|
+
from Cryptodome.Signature.pss import MGF1
|
|
20
21
|
|
|
21
22
|
from bkcrypto import constants, types
|
|
22
23
|
|
|
@@ -34,12 +35,22 @@ class RSAAsymmetricRuntimeConfig(configs.BaseRSAAsymmetricConfig, base.BaseAsymm
|
|
|
34
35
|
sig_scheme_maker: types.RSASigSchemeMaker = None
|
|
35
36
|
|
|
36
37
|
def __post_init__(self):
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if self.padding == constants.RSACipherPadding.PKCS1_OAEP:
|
|
39
|
+
self.cipher_maker = self._make_oaep_cipher
|
|
40
|
+
else:
|
|
41
|
+
self.cipher_maker = constants.RSACipherPadding.get_cipher_maker_by_member(self.padding)
|
|
39
42
|
self.sig_scheme_maker = constants.RSASigScheme.get_sig_scheme_maker_by_member(self.sig_scheme)
|
|
40
43
|
|
|
41
44
|
super().__post_init__()
|
|
42
45
|
|
|
46
|
+
def _make_oaep_cipher(self, key: RSA.RsaKey) -> types.RSACipher:
|
|
47
|
+
return PKCS1_OAEP.new(
|
|
48
|
+
key,
|
|
49
|
+
hashAlgo=self.oaep_hash,
|
|
50
|
+
mgfunc=lambda seed, length: MGF1(seed, length, self.mgf1_hash),
|
|
51
|
+
label=self.oaep_label or b"",
|
|
52
|
+
)
|
|
53
|
+
|
|
43
54
|
|
|
44
55
|
class RSAAsymmetricCipher(base.BaseAsymmetricCipher):
|
|
45
56
|
|
|
@@ -58,33 +69,75 @@ class RSAAsymmetricCipher(base.BaseAsymmetricCipher):
|
|
|
58
69
|
return self.config.private_key.exportKey().decode(encoding=self.config.encoding)
|
|
59
70
|
|
|
60
71
|
def _load_public_key(self, public_key_string: types.PublicKeyString) -> RSA.RsaKey:
|
|
61
|
-
|
|
72
|
+
try:
|
|
73
|
+
public_key: RSA.RsaKey = RSA.import_key(public_key_string)
|
|
74
|
+
except (IndexError, TypeError, ValueError) as error:
|
|
75
|
+
raise ValueError("Invalid RSA public key") from error
|
|
76
|
+
if public_key.has_private():
|
|
77
|
+
return public_key.publickey()
|
|
78
|
+
return public_key
|
|
62
79
|
|
|
63
80
|
def _load_private_key(self, private_key_string: types.PrivateKeyString) -> RSA.RsaKey:
|
|
64
|
-
|
|
81
|
+
try:
|
|
82
|
+
private_key: RSA.RsaKey = RSA.import_key(private_key_string)
|
|
83
|
+
except (IndexError, TypeError, ValueError) as error:
|
|
84
|
+
raise ValueError("Invalid RSA private key") from error
|
|
85
|
+
if not private_key.has_private():
|
|
86
|
+
raise ValueError("Expected an RSA private key")
|
|
87
|
+
return private_key
|
|
65
88
|
|
|
66
89
|
def generate_key_pair(self) -> typing.Tuple[types.PrivateKeyString, types.PublicKeyString]:
|
|
67
90
|
private_key_obj: RSA.RsaKey = RSA.generate(self.config.pkey_bits)
|
|
68
|
-
private_key: bytes = private_key_obj.
|
|
69
|
-
public_key: bytes = private_key_obj.publickey().
|
|
91
|
+
private_key: bytes = private_key_obj.export_key(format="PEM", pkcs=1)
|
|
92
|
+
public_key: bytes = private_key_obj.publickey().export_key(format="PEM")
|
|
70
93
|
return private_key.decode(encoding=self.config.encoding), public_key.decode(encoding=self.config.encoding)
|
|
71
94
|
|
|
72
95
|
def _encrypt(self, plaintext_bytes: bytes) -> bytes:
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
block_size: int = self._get_encrypt_block_size()
|
|
97
|
+
if not self.config.enable_segmented_encryption:
|
|
98
|
+
return self._encrypt_bytes(plaintext_bytes)
|
|
75
99
|
cipher: types.RSACipher = self.config.cipher_maker(self.config.public_key)
|
|
76
|
-
for block in self.block_list(plaintext_bytes, block_size)
|
|
77
|
-
ciphertext_bytes += cipher.encrypt(block)
|
|
78
|
-
return ciphertext_bytes
|
|
100
|
+
return b"".join(cipher.encrypt(block) for block in self.block_list(plaintext_bytes, block_size))
|
|
79
101
|
|
|
80
102
|
def _decrypt(self, ciphertext_bytes: bytes) -> bytes:
|
|
81
|
-
|
|
103
|
+
if not self.config.enable_segmented_encryption:
|
|
104
|
+
return self._decrypt_bytes(ciphertext_bytes)
|
|
105
|
+
|
|
106
|
+
block_size: int = self.get_block_size(self.config.private_key, is_encrypt=False)
|
|
107
|
+
if len(ciphertext_bytes) % block_size:
|
|
108
|
+
raise ValueError("Invalid RSA ciphertext length")
|
|
82
109
|
cipher: types.RSACipher = self.config.cipher_maker(self.config.private_key)
|
|
110
|
+
return b"".join(self._decrypt_block(cipher, block) for block in self.block_list(ciphertext_bytes, block_size))
|
|
111
|
+
|
|
112
|
+
def _encrypt_bytes(self, plaintext_bytes: bytes) -> bytes:
|
|
113
|
+
if len(plaintext_bytes) > self._get_encrypt_block_size():
|
|
114
|
+
raise ValueError("RSA plaintext is too long")
|
|
115
|
+
cipher: types.RSACipher = self.config.cipher_maker(self.config.public_key)
|
|
116
|
+
return cipher.encrypt(plaintext_bytes)
|
|
117
|
+
|
|
118
|
+
def _decrypt_bytes(self, ciphertext_bytes: bytes) -> bytes:
|
|
83
119
|
block_size: int = self.get_block_size(self.config.private_key, is_encrypt=False)
|
|
84
|
-
|
|
85
|
-
|
|
120
|
+
if len(ciphertext_bytes) != block_size:
|
|
121
|
+
raise ValueError("Invalid RSA ciphertext length")
|
|
122
|
+
cipher: types.RSACipher = self.config.cipher_maker(self.config.private_key)
|
|
123
|
+
return self._decrypt_block(cipher, ciphertext_bytes)
|
|
124
|
+
|
|
125
|
+
def _decrypt_block(self, cipher: types.RSACipher, ciphertext_bytes: bytes) -> bytes:
|
|
126
|
+
if self.config.padding == constants.RSACipherPadding.PKCS1_OAEP:
|
|
127
|
+
return cipher.decrypt(ciphertext_bytes)
|
|
128
|
+
|
|
129
|
+
plaintext_bytes: typing.Optional[bytes] = cipher.decrypt(ciphertext_bytes, None)
|
|
130
|
+
if plaintext_bytes is None:
|
|
131
|
+
raise ValueError("Invalid RSA ciphertext")
|
|
86
132
|
return plaintext_bytes
|
|
87
133
|
|
|
134
|
+
def _get_encrypt_block_size(self) -> int:
|
|
135
|
+
return self.get_block_size(
|
|
136
|
+
self.config.public_key,
|
|
137
|
+
padding=self.config.padding,
|
|
138
|
+
oaep_hash=self.config.oaep_hash,
|
|
139
|
+
)
|
|
140
|
+
|
|
88
141
|
def _sign(self, plaintext_bytes: bytes) -> bytes:
|
|
89
142
|
sig_scheme: types.RSASigScheme = self.config.sig_scheme_maker(self.config.private_key)
|
|
90
143
|
sha: SHA1.SHA1Hash = SHA1.new(plaintext_bytes)
|
|
@@ -113,18 +166,20 @@ class RSAAsymmetricCipher(base.BaseAsymmetricCipher):
|
|
|
113
166
|
yield lst[idx : idx + block_size]
|
|
114
167
|
|
|
115
168
|
@staticmethod
|
|
116
|
-
def get_block_size(
|
|
169
|
+
def get_block_size(
|
|
170
|
+
key_obj: RSA.RsaKey,
|
|
171
|
+
is_encrypt: bool = True,
|
|
172
|
+
padding: constants.RSACipherPadding = constants.RSACipherPadding.PKCS1_v1_5,
|
|
173
|
+
oaep_hash: typing.Any = SHA1,
|
|
174
|
+
) -> int:
|
|
117
175
|
"""
|
|
118
176
|
获取加解密最大片长度,用于分割过长的文本,单位:bytes
|
|
119
177
|
:param key_obj:
|
|
120
178
|
:param is_encrypt:
|
|
121
179
|
:return:
|
|
122
180
|
"""
|
|
123
|
-
# TODO(crayon) 区分不同 Cipher 的最大明文长度
|
|
124
|
-
# PKCS1_v1_5: RSA key length(bytes) - 11
|
|
125
|
-
# PKCS1_OAEP: RSA密钥长度(字节) - 2 * 哈希(hashAlgo)输出长度(字节) - 2
|
|
126
|
-
block_size = Util.number.size(key_obj.n) / 8
|
|
127
|
-
reserve_size = 11
|
|
128
181
|
if not is_encrypt:
|
|
129
|
-
|
|
130
|
-
|
|
182
|
+
return key_obj.size_in_bytes()
|
|
183
|
+
if padding == constants.RSACipherPadding.PKCS1_OAEP:
|
|
184
|
+
return key_obj.size_in_bytes() - 2 * oaep_hash.digest_size - 2
|
|
185
|
+
return key_obj.size_in_bytes() - 11
|
|
@@ -13,6 +13,8 @@ specific language governing permissions and limitations under the License.
|
|
|
13
13
|
import typing
|
|
14
14
|
from dataclasses import dataclass
|
|
15
15
|
|
|
16
|
+
from Cryptodome.Hash import SHA1
|
|
17
|
+
|
|
16
18
|
from bkcrypto import constants, types
|
|
17
19
|
from bkcrypto.utils import convertors
|
|
18
20
|
|
|
@@ -52,6 +54,14 @@ class BaseRSAAsymmetricConfig(BaseAsymmetricConfig):
|
|
|
52
54
|
|
|
53
55
|
# 加解密填充方案,默认为 `PKCS1_v1_5`
|
|
54
56
|
padding: constants.RSACipherPadding = constants.RSACipherPadding.PKCS1_v1_5
|
|
57
|
+
# OAEP 哈希算法,默认保留 PyCryptodome 的 SHA-1 行为
|
|
58
|
+
oaep_hash: typing.Any = SHA1
|
|
59
|
+
# MGF1 哈希算法,默认与历史 OAEP 行为一致
|
|
60
|
+
mgf1_hash: typing.Any = SHA1
|
|
61
|
+
# OAEP label,None 表示空 label
|
|
62
|
+
oaep_label: typing.Optional[bytes] = None
|
|
63
|
+
# 是否按 RSA 最大明文长度分段,默认保留历史行为
|
|
64
|
+
enable_segmented_encryption: bool = True
|
|
55
65
|
# 签名方案,默认为 `PKCS1_v1_5`
|
|
56
66
|
sig_scheme: constants.RSASigScheme = constants.RSASigScheme.PKCS1_v1_5
|
|
57
67
|
# 密钥长度(bit)
|
|
@@ -12,61 +12,66 @@ specific language governing permissions and limitations under the License.
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
import typing
|
|
15
|
-
from dataclasses import asdict
|
|
16
15
|
|
|
17
16
|
from bkcrypto import constants
|
|
18
|
-
from bkcrypto.asymmetric.ciphers import BaseAsymmetricCipher
|
|
17
|
+
from bkcrypto.asymmetric.ciphers.base import BaseAsymmetricCipher
|
|
19
18
|
from bkcrypto.asymmetric.options import AsymmetricOptions
|
|
20
|
-
from bkcrypto.symmetric.ciphers import
|
|
19
|
+
from bkcrypto.symmetric.ciphers.base import BaseSymmetricCipher
|
|
21
20
|
from bkcrypto.symmetric.options import SymmetricOptions
|
|
21
|
+
from bkcrypto.utils import module_loding
|
|
22
22
|
|
|
23
|
-
SYMMETRIC_CIPHER_CLASSES: typing.Dict[str,
|
|
24
|
-
constants.SymmetricCipherType.AES.value: AESSymmetricCipher,
|
|
25
|
-
constants.SymmetricCipherType.SM4.value: SM4SymmetricCipher,
|
|
23
|
+
SYMMETRIC_CIPHER_CLASSES: typing.Dict[str, str] = {
|
|
24
|
+
constants.SymmetricCipherType.AES.value: "bkcrypto.symmetric.ciphers.aes.AESSymmetricCipher",
|
|
25
|
+
constants.SymmetricCipherType.SM4.value: "bkcrypto.symmetric.ciphers.sm4.SM4SymmetricCipher",
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
ASYMMETRIC_CIPHER_CLASSES: typing.Dict[str,
|
|
30
|
-
constants.AsymmetricCipherType.RSA.value: RSAAsymmetricCipher,
|
|
31
|
-
constants.AsymmetricCipherType.SM2.value: SM2AsymmetricCipher,
|
|
29
|
+
ASYMMETRIC_CIPHER_CLASSES: typing.Dict[str, str] = {
|
|
30
|
+
constants.AsymmetricCipherType.RSA.value: "bkcrypto.asymmetric.ciphers.rsa.RSAAsymmetricCipher",
|
|
31
|
+
constants.AsymmetricCipherType.SM2.value: "bkcrypto.asymmetric.ciphers.sm2.SM2AsymmetricCipher",
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def _load_cipher_class(cipher_type: str, cipher_classes: typing.Dict[str, typing.Any]) -> typing.Type:
|
|
36
|
+
cipher_class_or_path: typing.Any = cipher_classes[cipher_type]
|
|
37
|
+
if isinstance(cipher_class_or_path, str):
|
|
38
|
+
return module_loding.import_string(cipher_class_or_path)
|
|
39
|
+
return cipher_class_or_path
|
|
40
|
+
|
|
41
|
+
|
|
35
42
|
def get_asymmetric_cipher(
|
|
36
43
|
cipher_type: typing.Optional[str] = None,
|
|
37
44
|
common: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
38
45
|
cipher_options: typing.Optional[typing.Dict[str, typing.Optional[AsymmetricOptions]]] = None,
|
|
39
|
-
asymmetric__cipher_classes: typing.Optional[typing.Dict[str,
|
|
46
|
+
asymmetric__cipher_classes: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
40
47
|
) -> BaseAsymmetricCipher:
|
|
41
48
|
cipher_type: str = cipher_type or constants.AsymmetricCipherType.RSA.value
|
|
42
|
-
asymmetric__cipher_classes
|
|
43
|
-
|
|
49
|
+
asymmetric__cipher_classes = asymmetric__cipher_classes or ASYMMETRIC_CIPHER_CLASSES
|
|
50
|
+
asymmetric_cipher_class: typing.Type[BaseAsymmetricCipher] = _load_cipher_class(
|
|
51
|
+
cipher_type, asymmetric__cipher_classes
|
|
44
52
|
)
|
|
45
|
-
asymmetric_cipher_class: typing.Type[BaseAsymmetricCipher] = asymmetric__cipher_classes[cipher_type]
|
|
46
53
|
|
|
47
54
|
common = common or {}
|
|
48
55
|
cipher_options: typing.Dict[str, typing.Optional[AsymmetricOptions]] = cipher_options or {}
|
|
49
56
|
options: AsymmetricOptions = cipher_options.get(cipher_type) or asymmetric_cipher_class.OPTIONS_DATA_CLASS()
|
|
50
57
|
|
|
51
58
|
# 同参数优先级:common > options
|
|
52
|
-
return asymmetric_cipher_class(**{**
|
|
59
|
+
return asymmetric_cipher_class(**{**vars(options), **common})
|
|
53
60
|
|
|
54
61
|
|
|
55
62
|
def get_symmetric_cipher(
|
|
56
63
|
cipher_type: typing.Optional[str] = None,
|
|
57
64
|
common: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
58
65
|
cipher_options: typing.Optional[typing.Dict[str, typing.Optional[SymmetricOptions]]] = None,
|
|
59
|
-
symmetric_cipher_classes: typing.Optional[typing.Dict[str,
|
|
66
|
+
symmetric_cipher_classes: typing.Optional[typing.Dict[str, typing.Any]] = None,
|
|
60
67
|
) -> BaseSymmetricCipher:
|
|
61
68
|
cipher_type: str = cipher_type or constants.SymmetricCipherType.AES.value
|
|
62
|
-
symmetric_cipher_classes
|
|
63
|
-
|
|
64
|
-
)
|
|
65
|
-
symmetric_cipher_class: typing.Type[BaseSymmetricCipher] = symmetric_cipher_classes[cipher_type]
|
|
69
|
+
symmetric_cipher_classes = symmetric_cipher_classes or SYMMETRIC_CIPHER_CLASSES
|
|
70
|
+
symmetric_cipher_class: typing.Type[BaseSymmetricCipher] = _load_cipher_class(cipher_type, symmetric_cipher_classes)
|
|
66
71
|
|
|
67
72
|
common = common or {}
|
|
68
73
|
cipher_options: typing.Dict[str, typing.Optional[SymmetricOptions]] = cipher_options or {}
|
|
69
74
|
options: SymmetricOptions = cipher_options.get(cipher_type) or symmetric_cipher_class.OPTIONS_DATA_CLASS()
|
|
70
75
|
|
|
71
76
|
# 同参数优先级:common > options
|
|
72
|
-
return symmetric_cipher_class(**{**
|
|
77
|
+
return symmetric_cipher_class(**{**vars(options), **common})
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/settings.py
RENAMED
|
@@ -16,22 +16,15 @@ from django.conf import settings
|
|
|
16
16
|
from django.test.signals import setting_changed
|
|
17
17
|
|
|
18
18
|
from bkcrypto import constants
|
|
19
|
-
from bkcrypto.
|
|
19
|
+
from bkcrypto.contrib.basic import ciphers as basic_ciphers
|
|
20
20
|
from bkcrypto.contrib.django.init_configs import AsymmetricCipherInitConfig, CipherInitConfig, SymmetricCipherInitConfig
|
|
21
|
-
from bkcrypto.symmetric.ciphers import AESSymmetricCipher, SM4SymmetricCipher
|
|
22
21
|
from bkcrypto.utils import module_loding
|
|
23
22
|
|
|
24
23
|
DEFAULTS = {
|
|
25
24
|
"SYMMETRIC_CIPHER_TYPE": constants.SymmetricCipherType.AES.value,
|
|
26
25
|
"ASYMMETRIC_CIPHER_TYPE": constants.AsymmetricCipherType.RSA.value,
|
|
27
|
-
"SYMMETRIC_CIPHER_CLASSES":
|
|
28
|
-
|
|
29
|
-
constants.SymmetricCipherType.SM4.value: module_loding.get_import_path(SM4SymmetricCipher),
|
|
30
|
-
},
|
|
31
|
-
"ASYMMETRIC_CIPHER_CLASSES": {
|
|
32
|
-
constants.AsymmetricCipherType.RSA.value: module_loding.get_import_path(RSAAsymmetricCipher),
|
|
33
|
-
constants.AsymmetricCipherType.SM2.value: module_loding.get_import_path(SM2AsymmetricCipher),
|
|
34
|
-
},
|
|
26
|
+
"SYMMETRIC_CIPHER_CLASSES": dict(basic_ciphers.SYMMETRIC_CIPHER_CLASSES),
|
|
27
|
+
"ASYMMETRIC_CIPHER_CLASSES": dict(basic_ciphers.ASYMMETRIC_CIPHER_CLASSES),
|
|
35
28
|
"SYMMETRIC_CIPHERS": {
|
|
36
29
|
"default": {
|
|
37
30
|
# 可选,用于在 settings 没法直接获取 key 的情况
|
|
@@ -112,12 +105,6 @@ class CryptoSettings:
|
|
|
112
105
|
# Fall back to defaults
|
|
113
106
|
val = self.defaults[attr]
|
|
114
107
|
|
|
115
|
-
if attr in ["SYMMETRIC_CIPHER_CLASSES", "ASYMMETRIC_CIPHER_CLASSES"]:
|
|
116
|
-
val = {
|
|
117
|
-
cipher_type: module_loding.import_string(cipher_import_path)
|
|
118
|
-
for cipher_type, cipher_import_path in val.items()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
108
|
if attr in ["SYMMETRIC_CIPHERS", "ASYMMETRIC_CIPHERS"]:
|
|
122
109
|
using__init_config_map: typing.Dict[str, CipherInitConfig] = {}
|
|
123
110
|
for using, init_config_params in val.items():
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/__init__.py
RENAMED
|
@@ -14,4 +14,11 @@ __all__ = ["BaseSymmetricCipher", "AESSymmetricCipher", "SM4SymmetricCipher"]
|
|
|
14
14
|
|
|
15
15
|
from .aes import AESSymmetricCipher
|
|
16
16
|
from .base import BaseSymmetricCipher
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __getattr__(name):
|
|
20
|
+
if name == "SM4SymmetricCipher":
|
|
21
|
+
from .sm4 import SM4SymmetricCipher
|
|
22
|
+
|
|
23
|
+
return SM4SymmetricCipher
|
|
24
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -14,6 +14,7 @@ from dataclasses import dataclass
|
|
|
14
14
|
|
|
15
15
|
from Cryptodome.Cipher import AES
|
|
16
16
|
from Cryptodome.Util import Counter
|
|
17
|
+
from Cryptodome.Util.Padding import pad, unpad
|
|
17
18
|
|
|
18
19
|
from bkcrypto import constants, types
|
|
19
20
|
|
|
@@ -22,7 +23,7 @@ from . import base
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
@dataclass
|
|
25
|
-
class AESSymmetricRuntimeConfig(configs.
|
|
26
|
+
class AESSymmetricRuntimeConfig(configs.BaseAESSymmetricConfig, base.BaseSymmetricRuntimeConfig):
|
|
26
27
|
|
|
27
28
|
mode_class: types.AESModeClass = None
|
|
28
29
|
|
|
@@ -32,6 +33,9 @@ class AESSymmetricRuntimeConfig(configs.BaseSM4SymmetricConfig, base.BaseSymmetr
|
|
|
32
33
|
if self.key_size not in AES.key_size:
|
|
33
34
|
raise ValueError(f"Optional key sizes are {AES.key_size}, but got {self.key_size}")
|
|
34
35
|
|
|
36
|
+
if self.mode in [constants.SymmetricMode.CBC, constants.SymmetricMode.CTR] and self.iv_size != AES.block_size:
|
|
37
|
+
raise ValueError(f"AES {self.mode.value} IV must be exactly {AES.block_size} bytes")
|
|
38
|
+
|
|
35
39
|
try:
|
|
36
40
|
self.mode_class = {
|
|
37
41
|
constants.SymmetricMode.CTR: AES.MODE_CTR,
|
|
@@ -54,14 +58,28 @@ class AESSymmetricCipher(base.BaseSymmetricCipher):
|
|
|
54
58
|
|
|
55
59
|
config: AESSymmetricRuntimeConfig = None
|
|
56
60
|
|
|
61
|
+
def __init__(self, key: typing.Optional[typing.Union[bytes, str]] = None, **options):
|
|
62
|
+
key_bytes: typing.Optional[bytes] = (
|
|
63
|
+
key.encode(options.get("encoding", "utf-8")) if isinstance(key, str) else key
|
|
64
|
+
)
|
|
65
|
+
key_size: int = options.get("key_size", 16)
|
|
66
|
+
if key_bytes is not None and len(key_bytes) != key_size:
|
|
67
|
+
raise ValueError(f"AES key must be exactly {key_size} bytes")
|
|
68
|
+
super().__init__(key, **options)
|
|
69
|
+
|
|
57
70
|
def get_block_size(self) -> int:
|
|
58
|
-
return
|
|
71
|
+
return AES.block_size
|
|
59
72
|
|
|
60
73
|
def init_ctx(self, encryption_metadata: base.EncryptionMetadata):
|
|
61
74
|
mode_init_args: typing.List[bytes] = []
|
|
62
75
|
mode_init_kwargs: typing.Dict[str : typing.Any] = {}
|
|
63
76
|
|
|
64
77
|
if self.config.enable_iv:
|
|
78
|
+
if (
|
|
79
|
+
self.config.mode in [constants.SymmetricMode.CBC, constants.SymmetricMode.CTR]
|
|
80
|
+
and len(encryption_metadata.iv) != AES.block_size
|
|
81
|
+
):
|
|
82
|
+
raise ValueError(f"AES {self.config.mode.value} IV must be exactly {AES.block_size} bytes")
|
|
65
83
|
if self.config.mode == constants.SymmetricMode.CTR:
|
|
66
84
|
# Size of the counter block must match block size
|
|
67
85
|
mode_init_kwargs["counter"] = Counter.new(
|
|
@@ -80,6 +98,9 @@ class AESSymmetricCipher(base.BaseSymmetricCipher):
|
|
|
80
98
|
|
|
81
99
|
cipher_ctx = self.init_ctx(encryption_metadata)
|
|
82
100
|
|
|
101
|
+
if self.config.mode == constants.SymmetricMode.CBC and self.config.padding == constants.SymmetricPadding.PKCS7:
|
|
102
|
+
plaintext_bytes = pad(plaintext_bytes, AES.block_size, style="pkcs7")
|
|
103
|
+
|
|
83
104
|
if self.config.mode == constants.SymmetricMode.GCM:
|
|
84
105
|
ciphertext_bytes, tag = cipher_ctx.encrypt_and_digest(plaintext_bytes)
|
|
85
106
|
encryption_metadata.tag = tag
|
|
@@ -89,10 +110,21 @@ class AESSymmetricCipher(base.BaseSymmetricCipher):
|
|
|
89
110
|
|
|
90
111
|
def _decrypt(self, ciphertext_bytes: bytes, encryption_metadata: base.EncryptionMetadata) -> bytes:
|
|
91
112
|
|
|
113
|
+
if self.config.mode == constants.SymmetricMode.CBC and (
|
|
114
|
+
not ciphertext_bytes or len(ciphertext_bytes) % AES.block_size
|
|
115
|
+
):
|
|
116
|
+
raise ValueError("AES CBC ciphertext must be non-empty and block-aligned")
|
|
117
|
+
|
|
92
118
|
cipher_ctx = self.init_ctx(encryption_metadata)
|
|
93
119
|
|
|
94
120
|
if self.config.mode == constants.SymmetricMode.GCM:
|
|
95
121
|
plaintext_bytes: bytes = cipher_ctx.decrypt_and_verify(ciphertext_bytes, encryption_metadata.tag)
|
|
96
122
|
return plaintext_bytes
|
|
97
123
|
else:
|
|
98
|
-
|
|
124
|
+
plaintext_bytes: bytes = cipher_ctx.decrypt(ciphertext_bytes)
|
|
125
|
+
if (
|
|
126
|
+
self.config.mode == constants.SymmetricMode.CBC
|
|
127
|
+
and self.config.padding == constants.SymmetricPadding.PKCS7
|
|
128
|
+
):
|
|
129
|
+
return unpad(plaintext_bytes, AES.block_size, style="pkcs7")
|
|
130
|
+
return plaintext_bytes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/ciphers/base.py
RENAMED
|
@@ -246,6 +246,31 @@ class BaseSymmetricCipher:
|
|
|
246
246
|
plaintext: str = self.config.interceptor.before_encrypt(plaintext, cipher=self)
|
|
247
247
|
plaintext_bytes: bytes = self.config.convertor.encode_plaintext(plaintext, encoding=self.config.encoding)
|
|
248
248
|
|
|
249
|
+
ciphertext: str = self._encrypt_bytes(plaintext_bytes)
|
|
250
|
+
return self.config.interceptor.after_encrypt(ciphertext, cipher=self)
|
|
251
|
+
|
|
252
|
+
def encrypt_bytes(self, plaintext: bytes) -> str:
|
|
253
|
+
"""加密二进制数据,不执行文本编码转换。"""
|
|
254
|
+
return self._encrypt_bytes(plaintext)
|
|
255
|
+
|
|
256
|
+
def decrypt(self, ciphertext: str) -> str:
|
|
257
|
+
"""
|
|
258
|
+
解密
|
|
259
|
+
:param ciphertext: 密文
|
|
260
|
+
:return: 解密后的信息
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
ciphertext: str = self.config.interceptor.before_decrypt(ciphertext, cipher=self)
|
|
264
|
+
plaintext_bytes: bytes = self.decrypt_bytes(ciphertext)
|
|
265
|
+
plaintext: str = self.config.convertor.decode_plaintext(plaintext_bytes, encoding=self.config.encoding)
|
|
266
|
+
return self.config.interceptor.after_decrypt(plaintext, cipher=self)
|
|
267
|
+
|
|
268
|
+
def decrypt_bytes(self, ciphertext: str) -> bytes:
|
|
269
|
+
"""解密二进制数据,不执行文本编码转换。"""
|
|
270
|
+
ciphertext_bytes, encryption_metadata = self.extract_encryption_metadata(ciphertext)
|
|
271
|
+
return self._decrypt(ciphertext_bytes, encryption_metadata)
|
|
272
|
+
|
|
273
|
+
def _encrypt_bytes(self, plaintext_bytes: bytes) -> str:
|
|
249
274
|
if not self.config.enable_iv:
|
|
250
275
|
iv = None
|
|
251
276
|
elif self.config.iv:
|
|
@@ -261,19 +286,5 @@ class BaseSymmetricCipher:
|
|
|
261
286
|
aad = self.generate_aad()
|
|
262
287
|
|
|
263
288
|
encryption_metadata: EncryptionMetadata = EncryptionMetadata(iv=iv, aad=aad)
|
|
264
|
-
ciphertext_bytes = self._encrypt(plaintext_bytes, encryption_metadata)
|
|
265
|
-
|
|
266
|
-
return self.config.interceptor.after_encrypt(ciphertext, cipher=self)
|
|
267
|
-
|
|
268
|
-
def decrypt(self, ciphertext: str) -> str:
|
|
269
|
-
"""
|
|
270
|
-
解密
|
|
271
|
-
:param ciphertext: 密文
|
|
272
|
-
:return: 解密后的信息
|
|
273
|
-
"""
|
|
274
|
-
|
|
275
|
-
ciphertext: str = self.config.interceptor.before_decrypt(ciphertext, cipher=self)
|
|
276
|
-
ciphertext_bytes, encryption_metadata = self.extract_encryption_metadata(ciphertext)
|
|
277
|
-
plaintext_bytes: bytes = self._decrypt(ciphertext_bytes, encryption_metadata)
|
|
278
|
-
plaintext: str = self.config.convertor.decode_plaintext(plaintext_bytes, encoding=self.config.encoding)
|
|
279
|
-
return self.config.interceptor.after_decrypt(plaintext, cipher=self)
|
|
289
|
+
ciphertext_bytes: bytes = self._encrypt(plaintext_bytes, encryption_metadata)
|
|
290
|
+
return self.combine_encryption_metadata(ciphertext_bytes, encryption_metadata)
|
|
@@ -15,7 +15,6 @@ from Cryptodome.Cipher.PKCS1_OAEP import PKCS1OAEP_Cipher
|
|
|
15
15
|
from Cryptodome.Cipher.PKCS1_v1_5 import PKCS115_Cipher
|
|
16
16
|
from Cryptodome.PublicKey.RSA import RsaKey
|
|
17
17
|
from Cryptodome.Signature import pkcs1_15, pss
|
|
18
|
-
from tongsuopy.crypto.ciphers import modes
|
|
19
18
|
|
|
20
19
|
T = typing.TypeVar("T")
|
|
21
20
|
|
|
@@ -51,8 +50,6 @@ SymmetricTag = bytes
|
|
|
51
50
|
|
|
52
51
|
SymmetricAad = bytes
|
|
53
52
|
|
|
54
|
-
SM4ModeClass = typing.
|
|
55
|
-
typing.Type[modes.CBC], typing.Type[modes.CTR], typing.Type[modes.CFB], typing.Type[modes.GCM]
|
|
56
|
-
]
|
|
53
|
+
SM4ModeClass = typing.Any
|
|
57
54
|
|
|
58
55
|
AESModeClass = typing.Any
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "bk-crypto-python-sdk"
|
|
3
|
-
version = "
|
|
3
|
+
version = "4.0.0"
|
|
4
4
|
description = "bk-crypto-python-sdk is a lightweight cryptography toolkit for Python applications based on Cryptodome / tongsuopy and other encryption libraries."
|
|
5
5
|
authors = ["TencentBlueKing <contactus_bk@tencent.com>"]
|
|
6
6
|
readme = "readme.md"
|
|
@@ -10,11 +10,14 @@ packages = [
|
|
|
10
10
|
|
|
11
11
|
[tool.poetry.dependencies]
|
|
12
12
|
python = ">=3.9,<3.15"
|
|
13
|
-
tongsuopy-crayon = "^1.0.2b5"
|
|
13
|
+
tongsuopy-crayon = { version = "^1.0.2b5", optional = true }
|
|
14
14
|
pycryptodomex = "^3.20.0"
|
|
15
15
|
dacite = "^1.8.1"
|
|
16
16
|
wrapt = "^2.2"
|
|
17
17
|
|
|
18
|
+
[tool.poetry.extras]
|
|
19
|
+
gm = ["tongsuopy-crayon"]
|
|
20
|
+
|
|
18
21
|
[tool.poetry.dev-dependencies]
|
|
19
22
|
ipython = { version = "^7.34.0", python = ">=3.7" }
|
|
20
23
|
pytest = { version = "^7.4.0", python = ">=3.7" }
|
|
@@ -23,7 +26,7 @@ isort = "^5.5.4"
|
|
|
23
26
|
black = "22.3.0"
|
|
24
27
|
pre-commit = "^2.16.0"
|
|
25
28
|
codecov = "^2.1.13"
|
|
26
|
-
Django = "^
|
|
29
|
+
Django = "^4"
|
|
27
30
|
|
|
28
31
|
[build-system]
|
|
29
32
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -71,3 +74,8 @@ exclude = '''
|
|
|
71
74
|
profile = "black"
|
|
72
75
|
line_length = 120
|
|
73
76
|
skip_glob = ["*/migrations/*", "*/node_modules/*"]
|
|
77
|
+
|
|
78
|
+
[tool.pytest.ini_options]
|
|
79
|
+
markers = [
|
|
80
|
+
"compatibility: tests migrated from dist/local that must pass before and after issue #37",
|
|
81
|
+
]
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
$ pip install bk-crypto-python-sdk
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
仅使用 AES、RSA 时无需安装国密依赖。需要 SM2、SM4 时安装 `gm` extra:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
$ pip install "bk-crypto-python-sdk[gm]"
|
|
41
|
+
```
|
|
42
|
+
|
|
37
43
|
### Usage
|
|
38
44
|
|
|
39
45
|
> 更多用法参考:[使用文档](https://github.com/TencentBlueKing/crypto-python-sdk/blob/main/docs/usage.md)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/ciphers/sm2.py
RENAMED
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/asymmetric/interceptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/basic/__init__.py
RENAMED
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/__init__.py
RENAMED
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/ciphers.py
RENAMED
|
File without changes
|
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/init_configs.py
RENAMED
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/contrib/django/selectors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/symmetric/interceptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bk_crypto_python_sdk-3.0.0 → bk_crypto_python_sdk-4.0.0}/bkcrypto/utils/base_interceptors.py
RENAMED
|
File without changes
|
|
File without changes
|