| 135 | |
| 136 | |
| 137 | def execute_rest(context, endpoint, payload, timeout=None): |
| 138 | # type: (RestApiContext, str, proto.ApiRequestPayload, ...) -> Optional[Union[bytes, dict]] |
| 139 | if not context.transmission_key: |
| 140 | context.transmission_key = os.urandom(32) |
| 141 | |
| 142 | if not context.server_key_id: |
| 143 | context.server_key_id = 7 |
| 144 | |
| 145 | run_request = True |
| 146 | throttle_retries = 0 |
| 147 | max_throttle_retries = 3 |
| 148 | while run_request: |
| 149 | run_request = False |
| 150 | |
| 151 | api_request = proto.ApiRequest() |
| 152 | qrc_success = False |
| 153 | |
| 154 | # Try QRC encryption if qrc_key_id is available |
| 155 | if context.qrc_key_id and context.qrc_key_id >= 100: |
| 156 | qrc_mlkem_key = SERVER_PUBLIC_KEYS.get(context.qrc_key_id) |
| 157 | if qrc_mlkem_key and isinstance(qrc_mlkem_key, bytes): |
| 158 | try: |
| 159 | logging.debug(f"Using QRC hybrid encryption (ML-KEM key ID: {context.qrc_key_id}, EC key ID: {context.server_key_id})") |
| 160 | |
| 161 | if not context.client_ec_private_key: |
| 162 | context.client_ec_private_key = crypto.generate_ec_key()[0] |
| 163 | |
| 164 | from .qrc.qrc_crypto import encrypt_qrc |
| 165 | ec_public_key = SERVER_PUBLIC_KEYS[context.server_key_id] |
| 166 | qrc_message = encrypt_qrc(context.transmission_key, context.client_ec_private_key, ec_public_key, qrc_mlkem_key) |
| 167 | |
| 168 | api_request.qrcMessageKey.clientEcPublicKey = qrc_message['client_ec_public_key'] |
| 169 | api_request.qrcMessageKey.mlKemEncapsulatedKey = qrc_message['ml_kem_encapsulated_key'] |
| 170 | api_request.qrcMessageKey.data = qrc_message['data'] |
| 171 | api_request.qrcMessageKey.msgVersion = qrc_message['msg_version'] |
| 172 | api_request.qrcMessageKey.ecKeyId = context.server_key_id |
| 173 | |
| 174 | qrc_success = True |
| 175 | except Exception as e: |
| 176 | logging.warning(f"QRC encryption failed ({e}), falling back to EC encryption") |
| 177 | context.disable_qrc() |
| 178 | |
| 179 | # Fallback to EC encryption if QRC not available or failed |
| 180 | if not qrc_success: |
| 181 | server_public_key = SERVER_PUBLIC_KEYS[context.server_key_id] |
| 182 | if isinstance(server_public_key, rsa.RSAPublicKey): |
| 183 | api_request.encryptedTransmissionKey = crypto.encrypt_rsa(context.transmission_key, server_public_key) |
| 184 | elif isinstance(server_public_key, ec.EllipticCurvePublicKey): |
| 185 | api_request.encryptedTransmissionKey = crypto.encrypt_ec(context.transmission_key, server_public_key) |
| 186 | else: |
| 187 | raise ValueError('Invalid server public key') |
| 188 | |
| 189 | api_request.publicKeyId = context.qrc_key_id if qrc_success else context.server_key_id |
| 190 | api_request.locale = context.locale or 'en_US' |
| 191 | |
| 192 | api_request.encryptedPayload = crypto.encrypt_aes_v2(payload.SerializeToString(), context.transmission_key) |
| 193 | |
| 194 | request_data = api_request.SerializeToString() |