{"record":{"id":"0254790177f5d5fd","repo":"commaai/openpilot","slug":"invalid-smdpsigned2","errorCode":null,"errorMessage":"Invalid smdpSigned2","messagePattern":"Invalid smdpSigned2","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"openpilot/common/esim/lpa.py","lineNumber":499,"sourceCode":"  device_info = encode_tlv(TAG_STATUS, tac) + encode_tlv(0xA1, b\"\")\n  ctx_inner = encode_tlv(TAG_STATUS, matching_id.encode(\"utf-8\")) + encode_tlv(0xA1, device_info)\n  content = b64d(b64_signed1) + b64d(b64_sig1) + b64d(b64_pk_id) + b64d(b64_cert) + encode_tlv(0xA0, ctx_inner)\n  response = es10x_command(client, encode_tlv(TAG_AUTH_SERVER, content))\n  root = require_tag(response, TAG_AUTH_SERVER, \"AuthenticateServerResponse\")\n  error_tag = find_tag(root, 0xA1)\n  if error_tag is not None:\n    code = int.from_bytes(error_tag, \"big\") if error_tag else 0\n    raise RuntimeError(f\"AuthenticateServer rejected by eUICC: {AUTH_SERVER_ERROR_CODES.get(code, 'unknown')} (0x{code:02X})\")\n  return b64e(response)\n\n\ndef prepare_download(client: AtClient, b64_signed2: str, b64_sig2: str, b64_cert: str, cc: str | None = None) -> str:\n  smdp_signed2 = b64d(b64_signed2)\n  smdp_signature2 = b64d(b64_sig2)\n  smdp_certificate = b64d(b64_cert)\n  smdp_signed2_root = find_tag(smdp_signed2, 0x30)\n  if smdp_signed2_root is None:\n    raise RuntimeError(\"Invalid smdpSigned2\")\n  transaction_id = find_tag(smdp_signed2_root, TAG_STATUS)\n  cc_required_flag = find_tag(smdp_signed2_root, 0x01)\n  if transaction_id is None or cc_required_flag is None:\n    raise RuntimeError(\"Invalid smdpSigned2\")\n  content = smdp_signed2 + smdp_signature2\n  if int.from_bytes(cc_required_flag, \"big\") != 0:\n    if not cc:\n      raise RuntimeError(\"Confirmation code required but not provided\")\n    content += encode_tlv(0x04, hashlib.sha256(hashlib.sha256(cc.encode(\"utf-8\")).digest() + transaction_id).digest())\n  content += smdp_certificate\n  response = es10x_command(client, encode_tlv(TAG_PREPARE_DOWNLOAD, content))\n  require_tag(response, TAG_PREPARE_DOWNLOAD, \"PrepareDownloadResponse\")\n  return b64e(response)\n\n\ndef _parse_tlv_header_len(data: bytes) -> int:\n  tag_len = 2 if data[0] & 0x1F == 0x1F else 1\n  length_byte = data[tag_len]","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/common/esim/lpa.py#L481-L517","documentation":"prepare_download() decodes the SM-DP+ server's base64 smdpSigned2 blob and parses it as DER/BER-TLV looking for a root tag 0x30 (ASN.1 SEQUENCE). If no 0x30 element is found, the server response is structurally invalid from the client's perspective and RuntimeError('Invalid smdpSigned2') is raised.","triggerScenarios":"handleNotification/authenticateClient exchange returned a smdpSigned2 that is not valid DER (wrong field passed, base64 of a different value, truncated payload), or a server response format that deviates from SGP.22 expectations.","commonSituations":"Field mixups when wiring the ES9+ JSON into prepare_download (passing smdpSignature2 or the cert in the signed2 slot); carrier server bugs or SGP.22 version differences; base64 decoding of a corrupted string.","solutions":["Verify the argument mapping: prepare_download(client, b64_signed2, b64_sig2, b64_cert, cc) must receive exactly the server's smdpSigned2, smdpSignature2, smdpCertificate fields","Decode and inspect the blob offline (openssl asn1parse / base64 -d) to confirm it is a DER SEQUENCE","If the server payload genuinely lacks structure, escalate to the carrier — their ES9+ response is malformed"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"from openpilot.common.esim.lpa import find_tag\n\nraw = base64.b64decode(b64_signed2)\nif find_tag(raw, 0x30) is None:\n  raise SystemExit('smdpSigned2 is not a valid DER SEQUENCE — wrong field or server bug')","typeGuard":"def is_valid_smdp_signed2(b64: str) -> bool:\n  try:\n    return find_tag(base64.b64decode(b64), 0x30) is not None\n  except Exception:\n    return False","tryCatchPattern":null,"preventionTips":["Map ES9+ JSON fields to function args with named constants, not positional habit","Validate server payloads structurally before entering the eUICC download phase","Capture raw server responses in debug logs to diagnose field mixups"],"tags":["esim","es9p","asn1","parsing","smdp-plus","python"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}