volcengine-python-sdk-aicc 0.0.41__py3-none-any.whl

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.
Files changed (60) hide show
  1. bytedance/__init__.py +4 -0
  2. bytedance/jeddak_secure_channel/__init__.py +28 -0
  3. bytedance/jeddak_secure_channel/client.py +305 -0
  4. bytedance/jeddak_secure_channel/config.py +171 -0
  5. bytedance/jeddak_secure_channel/consts.py +5 -0
  6. bytedance/jeddak_secure_channel/crypto/__init__.py +14 -0
  7. bytedance/jeddak_secure_channel/crypto/aes.py +174 -0
  8. bytedance/jeddak_secure_channel/crypto/envelope.py +395 -0
  9. bytedance/jeddak_secure_channel/crypto/rsa.py +164 -0
  10. bytedance/jeddak_secure_channel/error.py +131 -0
  11. bytedance/jeddak_secure_channel/log/__init__.py +23 -0
  12. bytedance/jeddak_secure_channel/log/config.py +31 -0
  13. bytedance/jeddak_secure_channel/log/logger.py +88 -0
  14. bytedance/jeddak_secure_channel/logger_factory.py +134 -0
  15. bytedance/jeddak_secure_channel/ra.py +772 -0
  16. bytedance/jeddak_secure_channel/server.py +745 -0
  17. bytedance/jeddak_secure_channel/service/__init__.py +16 -0
  18. bytedance/jeddak_secure_channel/service/eps.py +133 -0
  19. bytedance/jeddak_secure_channel/service/eps_models.py +47 -0
  20. bytedance/jeddak_secure_channel/service/ras.py +88 -0
  21. bytedance/jeddak_secure_channel/service/tks.py +51 -0
  22. bytedance/jeddak_secure_channel/service/tks_models.py +334 -0
  23. bytedance/jeddak_secure_channel/utils.py +469 -0
  24. bytedance/jeddak_secure_model/model_automation_tool.py +181 -0
  25. bytedance/jeddak_secure_model/model_encryption.py +416 -0
  26. bytedance/jeddak_secure_model/model_hub_admin.py +116 -0
  27. bytedance/jeddak_secure_model/pcc_utils.py +279 -0
  28. bytedance/jeddak_secure_model/rds_utils.py +63 -0
  29. bytedance/tks/attester.py +41 -0
  30. bytedance/tks/client.py +629 -0
  31. bytedance/tks/consts.py +122 -0
  32. bytedance/tks/crypto/aes.py +47 -0
  33. bytedance/tks/crypto/ec.py +39 -0
  34. bytedance/tks/exceptions.py +22 -0
  35. bytedance/volcengine_aicc_sdk/__init__.py +14 -0
  36. bytedance/volcengine_aicc_sdk/aicc_client.py +277 -0
  37. bytedance/volcengine_aicc_sdk/aicc_inference.py +64 -0
  38. bytedance/volcengine_aicc_sdk/ark_inference_client.py +343 -0
  39. bytedance/volcengine_aicc_sdk/asr_inference_client.py +158 -0
  40. bytedance/volcengine_aicc_sdk/asr_websocket_client.py +705 -0
  41. bytedance/volcengine_aicc_sdk/auc_inference_client.py +305 -0
  42. bytedance/volcengine_aicc_sdk/common/__init__.py +0 -0
  43. bytedance/volcengine_aicc_sdk/common/aes.py +136 -0
  44. bytedance/volcengine_aicc_sdk/common/constant.py +4 -0
  45. bytedance/volcengine_aicc_sdk/common/error.py +21 -0
  46. bytedance/volcengine_aicc_sdk/common/log/__init__.py +24 -0
  47. bytedance/volcengine_aicc_sdk/common/log/config.py +31 -0
  48. bytedance/volcengine_aicc_sdk/common/log/logger.py +88 -0
  49. bytedance/volcengine_aicc_sdk/common/tos_utils.py +341 -0
  50. bytedance/volcengine_aicc_sdk/common/utils.py +45 -0
  51. bytedance/volcengine_aicc_sdk/cu_inference_client.py +292 -0
  52. bytedance/volcengine_aicc_sdk/deepseek_inference_client.py +501 -0
  53. bytedance/volcengine_aicc_sdk/secure_http_client.py +406 -0
  54. bytedance/volcengine_aicc_sdk/seedance_inference_client.py +500 -0
  55. bytedance/volcengine_aicc_sdk/seedream_inference_client.py +213 -0
  56. volcengine_python_sdk_aicc-0.0.41.dist-info/METADATA +79 -0
  57. volcengine_python_sdk_aicc-0.0.41.dist-info/RECORD +60 -0
  58. volcengine_python_sdk_aicc-0.0.41.dist-info/WHEEL +5 -0
  59. volcengine_python_sdk_aicc-0.0.41.dist-info/licenses/LICENSE +202 -0
  60. volcengine_python_sdk_aicc-0.0.41.dist-info/top_level.txt +1 -0
@@ -0,0 +1,772 @@
1
+ """
2
+ 安全通信远程证明功能.
3
+ """
4
+
5
+ __all__ = [
6
+ "RaConfig",
7
+ "RaPod",
8
+ "RaRequest",
9
+ "RaResponse",
10
+ "RaResponseMetadata",
11
+ "RaResponsePod",
12
+ "RaResponsePods",
13
+ "attest_server",
14
+ "prepare_ra_request",
15
+ "validata_ra_request",
16
+ "attest_client",
17
+ "generate_nonce",
18
+ ]
19
+
20
+ import time
21
+ import uuid
22
+ import base64
23
+ import json
24
+ from dataclasses import dataclass, field
25
+ from datetime import datetime, timezone
26
+ from typing import Tuple
27
+ import jwt
28
+ import hashlib
29
+ from pymerkle import verify_inclusion, MerkleProof
30
+ from jwcrypto import jwk
31
+ from jwt.exceptions import ExpiredSignatureError, InvalidIssuerError, InvalidSignatureError
32
+ from volcenginesdkcore.rest import ApiException
33
+ import requests
34
+ from typing_extensions import (
35
+ Dict,
36
+ List,
37
+ Literal,
38
+ NotRequired,
39
+ Optional,
40
+ TypedDict,
41
+ Union,
42
+ )
43
+
44
+ from . import error
45
+ from .crypto import PublicKey
46
+ from .log import logger
47
+ from .utils import request_bytedance_gateway
48
+
49
+ from cryptography.hazmat.primitives.asymmetric import rsa, padding
50
+ from cryptography.hazmat.primitives import hashes
51
+ from cryptography.hazmat.backends import default_backend
52
+
53
+
54
+ RA_NONCE_LEN = 12
55
+ RA_TYPE_LOCAL = "local"
56
+ RA_TYPE_TCA = "tca"
57
+
58
+
59
+ class RaPod(TypedDict):
60
+ cluster_id: str
61
+
62
+ namespace: str
63
+
64
+ deployment: str
65
+
66
+ pod: NotRequired[Literal["random", "all"]]
67
+ """默认为 random, 如果想要全部则为 all."""
68
+
69
+
70
+ @dataclass
71
+ class RaConfig:
72
+ """
73
+ 访问远程证明功能的配置.
74
+ """
75
+
76
+ ra_url: str = ""
77
+ """远程证明端点的 HTTP 地址."""
78
+
79
+ ra_type: str = RA_TYPE_TCA
80
+ """ra的类型,local:本地RA, tca:TCA的RAS_adapter接口(默认)."""
81
+
82
+ ra_service_name: str = ""
83
+ """数据接收端(服务端)的服务名. ,用于验证通过机密容器部署的服务"""
84
+
85
+ ra_pods_info: str = ""
86
+ """
87
+ 待验证的节点的pod信息,用于验证非通过机密容器部署的服务,
88
+ 格式: {cluster_id:***, namespace:***, deployment:***}
89
+ ra_service_name 和 ra_pods_info 二选一
90
+ """
91
+
92
+ ra_uid: str = ""
93
+ """用户的UID"""
94
+
95
+ ra_key_negotiation: bool = True
96
+ """是否在验证的时候需要同步进行协商密钥."""
97
+
98
+ ra_need_token: bool = True # RA 功能稳定后要改成 False
99
+ """是否要返回验证 token. 默认为是."""
100
+
101
+ attest_must: bool = False
102
+ """是否一定要返回验证报告才能采用公钥."""
103
+
104
+ ra_policy_id: str = ""
105
+ """进行验证所使用的策略, 如果 ra_need_token 为 True, 则必填."""
106
+
107
+ bytedance_top_info: str = ""
108
+ """访问bytedance TOP网关的信息."""
109
+
110
+ ra_attested_pods: List[RaPod] = field(default_factory=list)
111
+ """old 需要验证的节点信息."""
112
+
113
+ ra_policy_ids: List[str] = field(default_factory=list)
114
+ """old 进行验证所使用的策略, 如果 ra_need_token 为 True, 则必填."""
115
+
116
+ auth_token: str = ""
117
+ """用来进行鉴权的凭证"""
118
+
119
+ custom_headers: Optional[dict[str, str]] = None
120
+ """自定义的请求头."""
121
+
122
+
123
+ class RaRequest(TypedDict):
124
+ """数据发送方发出的远程证明请求."""
125
+
126
+ key_negotiation: bool
127
+ """是否在验证的时候需要同步进行协商密钥."""
128
+
129
+ token: bool
130
+ """是否要返回验证 token."""
131
+
132
+ policy_ids: List[str]
133
+ """进行验证所使用的策略."""
134
+
135
+ attested_pods: List[RaPod]
136
+ """需要验证的节点信息."""
137
+
138
+
139
+ class RaResponseMetadata(TypedDict):
140
+ Code: int
141
+ Message: str
142
+
143
+
144
+ class RaResponsePod(TypedDict):
145
+ evidence: str
146
+ token: str
147
+ key_info: dict
148
+
149
+
150
+ RaResponsePods = Dict[str, RaResponsePod]
151
+
152
+
153
+ class RaResponse(TypedDict):
154
+ """数据接收方发出的远程证明响应."""
155
+
156
+ ResponseMetadata: RaResponseMetadata
157
+
158
+ ResponseResult: RaResponsePods
159
+
160
+
161
+ def prepare_ra_request(config: RaConfig) -> RaRequest:
162
+ """
163
+ 准备远程证明请求.
164
+
165
+ Returns:
166
+ 远程证明请求. 可作为 JSON 发送给数据接收方远程证明端点.
167
+ """
168
+ return RaRequest(
169
+ key_negotiation=config.ra_key_negotiation,
170
+ token=config.ra_need_token,
171
+ policy_ids=config.ra_policy_ids,
172
+ attested_pods=config.ra_attested_pods,
173
+ )
174
+
175
+
176
+ def validata_ra_request(request: RaRequest) -> None:
177
+ """
178
+ 验证远程证明请求有效性.
179
+
180
+ Raises:
181
+ ParamError: 请求的 nonce 或 timestamp 字段无效.
182
+ """
183
+ nonce = request.get("hex_runtime_data")
184
+ if nonce:
185
+ nonce = base64.b64decode(nonce)
186
+ if len(nonce) != RA_NONCE_LEN:
187
+ raise error.ParamError("nonce", "Invalid RA request")
188
+
189
+ # time = datetime.fromtimestamp(request["timestamp"], timezone.utc)
190
+ # now = datetime.now(timezone.utc)
191
+ # if time > now + timedelta(minutes=1) or time < now - timedelta(minutes=2):
192
+ # raise error.ParamError("timestamp", "Invalid RA request")
193
+
194
+
195
+ def generate_nonce():
196
+ return ''.join(str(uuid.uuid4()).split('-'))[-16:]
197
+
198
+
199
+ def request_tca(config: RaConfig, nonce: str = None):
200
+ try:
201
+ ra_url = config.ra_url
202
+ ra_service_name = config.ra_service_name
203
+ ra_pods_info = config.ra_pods_info
204
+ ra_key_negotiation = config.ra_key_negotiation
205
+ ra_policy_id = config.ra_policy_id
206
+ ra_uid = config.ra_uid
207
+
208
+ top_info = config.bytedance_top_info
209
+ if top_info and isinstance(top_info, str):
210
+ top_info = json.loads(top_info)
211
+ # assert isinstance(top_info, dict)
212
+
213
+ headers = {"UID": ra_uid}
214
+
215
+ if not nonce:
216
+ nonce = generate_nonce()
217
+
218
+ if ra_service_name:
219
+ body = {
220
+ "Nonce": nonce,
221
+ "PolicyID": ra_policy_id,
222
+ "ServiceName": ra_service_name,
223
+ "KeyNegotiation": ra_key_negotiation,
224
+ "Token": True,
225
+ "Version": 2
226
+ }
227
+ elif ra_pods_info:
228
+ if isinstance(ra_pods_info, str):
229
+ ra_pods_info = json.loads(ra_pods_info)
230
+ body = {
231
+ "Nonce": nonce,
232
+ "PolicyID": ra_policy_id,
233
+ "AttestedPods": [ra_pods_info],
234
+ "KeyNegotiation": ra_key_negotiation,
235
+ "Token": True,
236
+ "Version": 2
237
+ }
238
+ else:
239
+ raise error.ServiceError("RA", config.ra_url, None, "call TCA error")
240
+
241
+ if top_info.get("target_uid") and not top_info.get("aicc_saas_trn"):
242
+ from bytedance.jeddak_secure_channel.utils import top_request
243
+ return top_request(ra_url, body, headers, top_info)
244
+ else:
245
+ return request_bytedance_gateway(ra_url, body, headers, top_info)
246
+ except Exception as e:
247
+ logger.critical(f"Response is not JSON: service={config.ra_url}")
248
+ raise error.ServiceError("RA", config.ra_url, None, "call TCA error") from e
249
+
250
+
251
+ def attest_server_local(token: Optional[str], config: RaConfig, nonce=None):
252
+ """
253
+ 对数据接收方进行远程证明(非TCA方式).
254
+ """
255
+ timestamp = str(int(datetime.now(timezone.utc).timestamp()))
256
+ auth_token = token
257
+ if not auth_token:
258
+ auth_token = config.auth_token
259
+
260
+ headers = {
261
+ "Content-Type": "application/json",
262
+ "timestamp": f"{timestamp}",
263
+ "Authorization": f"Bearer {auth_token}"
264
+ }
265
+
266
+ if config.custom_headers:
267
+ headers.update(config.custom_headers)
268
+
269
+ if not nonce:
270
+ nonce = generate_nonce()
271
+
272
+ request = {"Nonce": nonce, "ra_service_name": config.ra_service_name}
273
+ try:
274
+ logger.info(
275
+ f"request ra, url={config.ra_url}, header={headers}, params={request}"
276
+ )
277
+ response = requests.post(config.ra_url, headers=headers, json=request)
278
+ return response
279
+
280
+ except Exception as e:
281
+ logger.critical(f"RA Network error: url={config.ra_url}, e={e}")
282
+ raise error.NetworkError("RA", config.ra_url, endpoint=None) from e
283
+
284
+ # if response.status_code != 200:
285
+ # logger.error(
286
+ # f"Service error: service={config.ra_url} code={response.status_code}"
287
+ # )
288
+ # raise error.ServiceError(
289
+ # "RA", config.ra_url, None, f"code={response.status_code}"
290
+ # )
291
+
292
+ # try:
293
+ # response_json: RaResponse = response.json()
294
+ # logger.info(f"ra response={response_json}")
295
+ #
296
+ # res_meta_data = response_json.get("ResponseMetadata")
297
+ # res_result = response_json.get("ResponseResult")
298
+ #
299
+ # if not res_meta_data or not res_result:
300
+ # raise error.ServiceError("RA", config.ra_url, None, "wrong format")
301
+ #
302
+ # if res_meta_data.get("Code") != 0:
303
+ # raise error.ServiceError(
304
+ # "RA", config.ra_url, None, res_meta_data.get("Message", "")
305
+ # )
306
+ #
307
+ # for pod_name, pod_ra_info in res_result.items():
308
+ # # evidence = pod_ra_info.get("evidence")
309
+ # # token = pod_ra_info.get("token")
310
+ # key_info = pod_ra_info.get("key_info")
311
+ # if not key_info:
312
+ # raise error.ServiceError("RA", config.ra_url, "RSA key", "null")
313
+ #
314
+ # return True, key_info["pub_key_info"]
315
+ # except Exception as e:
316
+ # logger.critical(f"Response is not JSON: service={config.ra_url}")
317
+ # raise error.ServiceError("RA", config.ra_url, None, "not JSON") from e
318
+ #
319
+ # return False, ""
320
+
321
+
322
+ def verify_jwt_token(token):
323
+ try:
324
+ tmp_payload = token.split(".")[1]
325
+ jwk_data = json.loads(base64.urlsafe_b64decode(tmp_payload + "=" * (4 - len(tmp_payload) % 4))).get('jwk')
326
+
327
+ public_key = jwk.JWK(**jwk_data)
328
+
329
+ # 转换为 PEM 格式,以便 PyJWT 使用
330
+ pem_public_key = public_key.export_to_pem()
331
+
332
+ header = jwt.get_unverified_header(token)
333
+
334
+ time.sleep(2)
335
+
336
+ # 验证 Token
337
+ payload = jwt.decode(token, pem_public_key, algorithms=[header.get('alg')],
338
+ issuer="Bytedance-Remote-Attestation-Service",
339
+ options={"verify_signature": True})
340
+
341
+ logger.debug(f"Token is valid. Payload:{payload}")
342
+ return True
343
+ except ExpiredSignatureError:
344
+ logger.warning("verify_ra_token: Token has expired.")
345
+ return False
346
+ except InvalidIssuerError:
347
+ logger.warning("verify_ra_token: Invalid issuer.")
348
+ return False
349
+ except InvalidSignatureError:
350
+ logger.warning("verify_ra_token: Invalid signature.")
351
+ return False
352
+ except Exception as e:
353
+ logger.warning(f"verify_ra_token: {e}")
354
+ return False
355
+
356
+
357
+ def base64url_decode(encoded_str):
358
+ padded_str = encoded_str + '=' * ((4 - len(encoded_str) % 4) % 4) # 补齐padding
359
+ return base64.urlsafe_b64decode(padded_str)
360
+
361
+
362
+ def jwk_to_rsa_public_key(jwk_info: dict):
363
+ """将JWK格式的公钥转换为RSA公钥对象"""
364
+ # 从JWK中提取必要的参数
365
+ n = int.from_bytes(base64url_decode(jwk_info['n']), byteorder='big')
366
+ e = int.from_bytes(base64url_decode(jwk_info['e']), byteorder='big')
367
+ public_key = rsa.RSAPublicNumbers(e, n).public_key(default_backend())
368
+ return public_key
369
+
370
+
371
+ def verify_jwt_token_new(token):
372
+ """验证JWT token,不依赖JWT库"""
373
+ try:
374
+ # 分割JWT:header.payload.signature
375
+ parts = token.split(".")
376
+ if len(parts) != 3:
377
+ logger.error("verify_ra_token: Invalid token format")
378
+ return False
379
+
380
+ header_encoded, payload_encoded, signature_encoded = parts
381
+
382
+ header = json.loads(base64url_decode(header_encoded).decode('utf-8'))
383
+ payload = json.loads(base64url_decode(payload_encoded).decode('utf-8'))
384
+
385
+ # 验证token是否过期
386
+ if 'exp' in payload:
387
+ if payload['exp'] < int(time.time()):
388
+ logger.warning("verify_ra_token: Token has expired.")
389
+
390
+ # 验证issuer
391
+ if payload.get('iss') != "Bytedance-Remote-Attestation-Service":
392
+ logger.error("verify_ra_token: Invalid issuer.")
393
+ return False
394
+
395
+ # 签名验证
396
+ if 'jwk' in header:
397
+ public_key = jwk_to_rsa_public_key(header['jwk'])
398
+ alg = header.get("alg")
399
+ elif 'jwk' in payload:
400
+ public_key = jwk_to_rsa_public_key(payload['jwk'])
401
+ alg = payload.get('jwk').get("alg")
402
+ else:
403
+ public_key = None
404
+ alg = None
405
+ logger.warning("can not find pub_key from JWT token")
406
+
407
+ if public_key:
408
+ message = f"{header_encoded}.{payload_encoded}".encode('utf-8')
409
+ signature = base64url_decode(signature_encoded)
410
+ try:
411
+ if alg == "RS384":
412
+ public_key.verify(signature, message, padding.PKCS1v15(), hashes.SHA384())
413
+ else:
414
+ logger.warning(f"no support JWT sign algorithms: [{alg}]")
415
+ return False
416
+ except Exception as e:
417
+ logger.error(f"JWT Token Signature verification failed: {e}")
418
+ return False
419
+
420
+ return True
421
+
422
+ except Exception as e:
423
+ logger.error(f"verify_jwt_token Exception={e}")
424
+ return False
425
+
426
+
427
+ def gen_report_data(nonce_up, nonce_down, dh_param, expiration_time):
428
+ try:
429
+ nonce_up_raw = bytes.fromhex(nonce_up)
430
+ nonce_down_raw = base64.b16decode(nonce_down.upper())
431
+ dh_param_bytes = base64.b64decode(dh_param)
432
+ expiration_time_bytes = str(expiration_time).encode()
433
+ report_data_input = nonce_up_raw + nonce_down_raw + dh_param_bytes + expiration_time_bytes
434
+ h = hashlib.sha256()
435
+ h.update(report_data_input)
436
+ report_data = h.hexdigest()
437
+
438
+ h = hashlib.sha512()
439
+ h.update(report_data.encode())
440
+ report_data_bytes = h.digest()
441
+
442
+ return report_data_bytes
443
+
444
+ except Exception as e:
445
+ logger.error(f"gen report data,msg={str(e)}")
446
+ raise Exception(f"gen report data,msg={str(e)}")
447
+
448
+
449
+ def verify_nonce(proof, report_data, nonce, pub_key_info):
450
+ try:
451
+ # 前64位是nonce的sha256哈希,后64位是公钥的sha256哈希
452
+ nonce_hash = hashlib.sha256(nonce.encode()).hexdigest()
453
+
454
+ # 计算pub_key_info的SHA256哈希
455
+ if pub_key_info:
456
+ pub_key_hash = hashlib.sha256(pub_key_info.encode()).hexdigest()
457
+ else:
458
+ # 如果公钥为空,用64个0替换
459
+ pub_key_hash = "0000000000000000000000000000000000000000000000000000000000000000"
460
+
461
+ # 拼接两个哈希值
462
+ base = nonce_hash + pub_key_hash
463
+ logger.debug(f'base={base}')
464
+
465
+ if not proof:
466
+ logger.debug(f'report_data={report_data}')
467
+ return report_data == base
468
+
469
+ h = hashlib.sha512()
470
+ h.update(b'\x00' + base.encode())
471
+ base = h.hexdigest()
472
+ root = report_data
473
+
474
+ logger.debug(f"new base={base}")
475
+
476
+ proof = MerkleProof.deserialize(proof)
477
+ verify_inclusion(bytes.fromhex(base), bytes.fromhex(root), proof)
478
+ return True
479
+ except Exception as e:
480
+ logger.debug(f"verify nonce failed!!!, msg={str(e)}")
481
+ return False
482
+
483
+
484
+ def check_pubkey_valid(pk_s: str, pk_r: str, timestamp: str, version: str, service_name: str, sign: str) -> bool:
485
+ """
486
+ 对pk_s的正确性进行检验
487
+ :param pk_s:服务公钥
488
+ :param pk_r:rar公钥
489
+ :param timestamp:时间戳
490
+ :param version:version
491
+ :param service_name:version
492
+ :param sign: sign(pk_s+timestamp, pr_r)
493
+ :return: True/False
494
+ """
495
+ if not pk_s or not pk_r or not sign:
496
+ return False
497
+
498
+ rsa_pub_r = PublicKey.from_public_pem(pk_r)
499
+
500
+ if isinstance(sign, str):
501
+ try:
502
+ sign = base64.b16decode(sign.upper().encode())
503
+ except Exception as e:
504
+ logger.warning(f"ra: check_pubkey_valid b16decode sign failed, Exception={e}")
505
+ sign = base64.b64decode(sign.encode())
506
+
507
+ msg = f"{pk_s}{timestamp}{service_name}".encode()
508
+ if version:
509
+ version_prefix = int(version).to_bytes(length=8, byteorder="big", signed=False)
510
+ msg = version_prefix + msg
511
+
512
+ ret = rsa_pub_r.verify(sign, msg)
513
+
514
+ logger.debug(f"check_pubkey_valid: pk_s={pk_s}, pk_r={pk_r}, version={version}, timestamp={timestamp},"
515
+ f"service_name={service_name}, msg={msg}, sign={sign}, ret={ret}")
516
+ return ret
517
+
518
+
519
+ def attest_server(token: Optional[str], config: RaConfig, nonce: str = None) -> Tuple[bool, str]:
520
+ """
521
+ 对数据接收方进行远程证明.
522
+ :return (ra_status, pub_key)
523
+ """
524
+ try:
525
+ if config.bytedance_top_info:
526
+ response = request_tca(config, nonce)
527
+ else:
528
+ response = attest_server_local(token, config, nonce)
529
+ except ApiException as e:
530
+ logger.error(f"attest_server error: config={config} ApiException={e}")
531
+ raise error.ServiceError("RA", config.ra_url, None, "NULL")
532
+ except Exception as e:
533
+ logger.error(f"attest_server error: config={config} Exception={e}")
534
+ raise error.ServiceError("RA", config.ra_url, None, "NULL")
535
+
536
+ if response is None:
537
+ logger.error(f"RA Service error: config={config} response=NULL")
538
+ raise error.ServiceError("RA", config.ra_url, None, "NULL")
539
+
540
+ if isinstance(response, requests.Response):
541
+ if response.status_code != 200:
542
+ logger.error(
543
+ f"RA Service error: code={response.status_code}, content={response.content}, config={config}"
544
+ )
545
+ raise error.ServiceError(
546
+ "RA", config.ra_url, None, f"code={response.status_code}"
547
+ )
548
+ else:
549
+ response_text = response.text
550
+ ra_res = json.loads(response_text).get("Result")
551
+ if not ra_res:
552
+ ra_res = json.loads(response_text).get("ResponseResult")
553
+ else:
554
+ ra_res = response
555
+
556
+ if not ra_res:
557
+ logger.error(f"RA response is NULL, config={config}")
558
+ return False, ""
559
+
560
+ try:
561
+ attestation_status = False
562
+ public_key_info = ""
563
+ pub_r = ""
564
+
565
+ # 取第一个节点验证就可以
566
+ for cluster_info, res_info in ra_res.items():
567
+ # raw_evidence = res_info.get("evidence")
568
+ # if raw_evidence:
569
+ # tmp = json.loads(raw_evidence)
570
+ # raw_evidence_json = json.dumps(tmp, indent=4)
571
+ # logger.debug(f"get ra evidence:{raw_evidence_json}")
572
+ if config.ra_key_negotiation:
573
+ public_key_info = res_info.get("key_info").get("pub_key_info")
574
+ else:
575
+ public_key_info = ""
576
+
577
+ raw_token = res_info.get("token")
578
+ if not raw_token:
579
+ logger.critical("RA response token is NULL, conf={config}")
580
+ return attestation_status, public_key_info
581
+ else:
582
+ evidence_info = res_info.get("evidence")
583
+ evidence_info_json = json.loads(evidence_info)
584
+ proof = evidence_info_json.get('proof')
585
+ logger.debug(f"proof={proof}")
586
+
587
+ if config.ra_key_negotiation:
588
+ key_info = res_info.get("key_info")
589
+ pub_r = key_info.get("rar_pub_key")
590
+ timestamp = key_info.get("timestamp")
591
+ version = key_info.get("version")
592
+ sign = key_info.get("rar_signature")
593
+ service_name = config.ra_service_name.strip()
594
+
595
+ if sign:
596
+ if not check_pubkey_valid(public_key_info, pub_r, timestamp, version, service_name, sign):
597
+ logger.critical("verify sign failed")
598
+ return attestation_status, public_key_info
599
+ else:
600
+ logger.info("ra: get pub_key, verify sign success")
601
+
602
+ attestation_status = verify_ra_result(raw_token, public_key_info, pub_r, proof, nonce)
603
+
604
+ break
605
+ return attestation_status, public_key_info
606
+ except Exception as e:
607
+ logger.critical(f"Response is not JSON: conf={config}, Exception={e}")
608
+ raise error.ServiceError("RA", config.ra_url, None, "not JSON") from e
609
+
610
+
611
+ def verify_ra_result(raw_token, pub_key_info, pub_r, proof, nonce, challenge_report_data: str = None) -> bool:
612
+ try:
613
+ report_data = ""
614
+ token_parts = raw_token.split(".")
615
+ if len(token_parts) >= 2:
616
+ token_data = token_parts[1]
617
+ try:
618
+ # 使用base64.urlsafe_b64decode进行URL安全模式的解码
619
+ decoded_data = base64.urlsafe_b64decode(
620
+ token_data + "=" * (4 - len(token_data) % 4)
621
+ ).decode("utf-8")
622
+ token_dict = json.loads(decoded_data)
623
+ report_data = token_dict.get('tdx').get('report_data')
624
+ except Exception as e:
625
+ logger.error(f"解码出现错误: {e}")
626
+ raise Exception("decode token error")
627
+ logger.info(f"in ra token,report_data={report_data}")
628
+
629
+ try:
630
+ if challenge_report_data:
631
+ if report_data != challenge_report_data:
632
+ return False
633
+ else:
634
+ nonce_ret = verify_nonce(proof, report_data, nonce, pub_r) or \
635
+ verify_nonce(proof, report_data, nonce, pub_key_info)
636
+ if not nonce_ret:
637
+ logger.error("verify nonce failed")
638
+ return False
639
+
640
+ except Exception as e:
641
+ logger.error(f"verify nonce failed!!!,msg={str(e)}")
642
+ return False
643
+
644
+ if not verify_jwt_token(raw_token):
645
+ logger.warning("token verify failed")
646
+ return False
647
+ else:
648
+ token_dict = {}
649
+ token_parts = raw_token.split(".")
650
+ if len(token_parts) >= 2:
651
+ token_data = token_parts[1]
652
+ try:
653
+ # 使用base64.urlsafe_b64decode进行URL安全模式的解码
654
+ decoded_data = base64.urlsafe_b64decode(
655
+ token_data + "=" * (4 - len(token_data) % 4)
656
+ ).decode("utf-8")
657
+
658
+ token_dict = json.loads(decoded_data)
659
+ except Exception as e:
660
+ logger.critical(f"token 解码出现错误: {e}")
661
+ return False
662
+
663
+ attestation_status = len(token_dict.get("policies_matched")) > 0
664
+ return attestation_status
665
+ except Exception as e:
666
+ logger.critical("verify_ra_result error,msg={}".format(str(e)))
667
+ raise Exception("verify_ra_result error,msg={}".format(str(e)))
668
+
669
+
670
+ def attest_client(client_ra_config: dict, client_bytedance_top_info: str, nonce: str = None) -> bool:
671
+ """
672
+ 对数据发送方进行远程证明.
673
+ """
674
+ ra_service_name: Optional[str] = client_ra_config.get("ra_service_name")
675
+ ra_policy_id: Optional[str] = client_ra_config.get("ra_policy_id")
676
+ ra_pods_info = client_ra_config.get("ra_pods_info")
677
+ ra_key_negotiation: Optional[bool] = client_ra_config.get("ra_key_negotiation")
678
+
679
+ top_info = client_bytedance_top_info
680
+ if top_info and isinstance(top_info, str):
681
+ top_info = json.loads(top_info)
682
+
683
+ ra_url = client_ra_config.get("ra_url", "open.volcengineapi.com")
684
+ ra_uid = client_ra_config.get("ra_uid")
685
+ if ra_uid:
686
+ headers = {"UID": ra_uid}
687
+ else:
688
+ headers = {}
689
+
690
+ if not nonce:
691
+ nonce = generate_nonce()
692
+
693
+ if ra_service_name:
694
+ body = {
695
+ "Nonce": nonce,
696
+ "PolicyID": ra_policy_id,
697
+ "ServiceName": ra_service_name,
698
+ "KeyNegotiation": ra_key_negotiation,
699
+ "Token": True,
700
+ }
701
+ elif ra_pods_info:
702
+ if isinstance(ra_pods_info, str):
703
+ ra_pods_info = json.loads(ra_pods_info)
704
+ body = {
705
+ "Nonce": nonce,
706
+ "PolicyID": ra_policy_id,
707
+ "AttestedPods": [ra_pods_info],
708
+ "KeyNegotiation": ra_key_negotiation,
709
+ "Token": True,
710
+ }
711
+ else:
712
+ raise error.ServiceError("RA", ra_service_name, None, "call TCA error")
713
+
714
+ response = request_bytedance_gateway(ra_url, body, headers, top_info)
715
+ if not response:
716
+ logger.error("Client RA error: response NULL")
717
+ raise error.ServiceError("RA", ra_service_name, None, "NULL")
718
+
719
+ if response.status_code != 200:
720
+ logger.error(
721
+ f"client ra error: ra_service_name={ra_service_name} code={response.status_code}"
722
+ )
723
+ raise error.ServiceError(
724
+ "RA", ra_service_name, None, f"code={response.status_code}"
725
+ )
726
+
727
+ try:
728
+ ra_res = json.loads(response.text).get("Result")
729
+ if not ra_res:
730
+ logger.error(
731
+ f"attest_client failed, verify_result is NULL, ra interface response={response.text}"
732
+ )
733
+ return False
734
+
735
+ attestation_status = False
736
+ for cluster_info, res_info in ra_res.items():
737
+ # raw_evidence = res_info.get("evidence")
738
+ # tmp = json.loads(raw_evidence)
739
+ # raw_evidence_json = json.dumps(tmp, indent=4)
740
+ # logger.debug(f"get ra evidence:{raw_evidence_json}")
741
+
742
+ raw_token = res_info.get("token")
743
+ if not raw_token:
744
+ logger.critical("token is None")
745
+ return False
746
+
747
+ if not verify_jwt_token(raw_token):
748
+ logger.critical("token verify failed")
749
+ return False
750
+
751
+ token_dict = {}
752
+ token_parts = raw_token.split(".")
753
+ if len(token_parts) >= 2:
754
+ token_data = token_parts[1]
755
+ try:
756
+ # 使用base64.urlsafe_b64decode进行URL安全模式的解码
757
+ decoded_data = base64.urlsafe_b64decode(
758
+ token_data + "=" * (4 - len(token_data) % 4)
759
+ ).decode("utf-8")
760
+
761
+ token_dict = json.loads(decoded_data)
762
+ except Exception as e:
763
+ logger.critical(f"token 解码出现错误: {e}")
764
+ return False
765
+
766
+ attestation_status = len(token_dict.get("policies_matched")) > 0
767
+ break
768
+
769
+ return attestation_status
770
+ except Exception as e:
771
+ logger.critical(f"Response is not JSON: client_service_name={ra_service_name}")
772
+ raise error.ServiceError("RA", ra_service_name, None, "not JSON") from e