Last active
October 23, 2025 01:50
-
-
Save JJTech0130/1bbfde1d52bfd60e023c7d21ba0b5b05 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # Configuration | |
| CONFIG_ID="8da72a14ba6942ac904c2f028aada7cb" # Simlessly eSIM config ID | |
| CBRS_PREFIX="315010" # CBRS MCC+MNC per your request | |
| CBRS_IBN="9999" # CBRS IBN for testing/shared | |
| DEFAULT_KI="$(printf 'A%.0s' {1..32})" # 32 hex chars of 'A' -> 16 bytes (testing) | |
| DEFAULT_OPC="$(printf 'A%.0s' {1..32})" # same for OPc | |
| # If you don't have open5gs-dbctl, you can install it: | |
| # sudo curl https://raw.githubusercontent.com/open5gs/open5gs/refs/heads/main/misc/db/open5gs-dbctl -o /usr/local/bin/open5gs-dbctl && sudo chmod +x /usr/local/bin/open5gs-dbctl | |
| OPEN5GS_CMD="open5gs-dbctl" # must be in PATH | |
| SIMLESSLY_BASE="https://rsp.simlessly.com/api/v1/fleet" | |
| TIMEZONE="America/New_York" | |
| # Helpers | |
| usage() { | |
| cat <<EOF | |
| Usage: $0 <5-digit-subscriber-id> [--execute] [--token <simlessly-token>] | |
| <5-digit-subscriber-id> : numeric, up to 5 digits (e.g. 1, 00001, 12345) | |
| --token <token> : Simlessly authorization token (or set SIMLESSLY_TOKEN env) | |
| EOF | |
| exit 1 | |
| } | |
| # ====== HELPERS ===== | |
| # Attribution: https://ethertubes.com/bash-luhn/ | |
| # Returns Luhn checksum for supplied sequence | |
| luhn_checksum() { | |
| sequence="$1" | |
| sequence="${sequence//[^0-9]}" # numbers only plz | |
| checksum=0 | |
| table=(0 2 4 6 8 1 3 5 7 9) | |
| # Quicker to work with even number of digits | |
| # prepend a "0" to sequence if uneven | |
| i=${#sequence} | |
| if [ $(($i % 2)) -ne 0 ]; then | |
| sequence="0$sequence" | |
| ((++i)) | |
| fi | |
| while [ $i -ne 0 ]; | |
| do | |
| # sum up the individual digits, do extra stuff w/every other digit | |
| checksum="$(($checksum + ${sequence:$((i - 1)):1}))" # Last digit | |
| # for every other digit, double the value before adding the digit | |
| # if the doubled value is over 9, subtract 9 | |
| checksum="$(($checksum + ${table[${sequence:$((i - 2)):1}]}))" # Second to last digit | |
| i=$((i - 2)) | |
| done | |
| checksum="$(($checksum % 10))" # mod 10 the sum to get single digit checksum | |
| echo "$checksum" | |
| } | |
| # Returns Luhn check digit for supplied sequence | |
| luhn_checkdigit() { | |
| check_digit=$(luhn_checksum "${1}0") | |
| if [ $check_digit -ne 0 ]; then | |
| check_digit=$((10 - $check_digit)) | |
| fi | |
| echo "$check_digit" | |
| } | |
| gen_uuid() { | |
| cat /proc/sys/kernel/random/uuid | |
| } | |
| ms_timestamp() { | |
| date +%s%3N | |
| } | |
| # ====== PARSE ARGS ===== | |
| if [ $# -lt 1 ]; then | |
| usage | |
| fi | |
| SUBSCR_ID_RAW="$1" | |
| shift || true | |
| SIMLESSLY_TOKEN="${SIMLESSLY_TOKEN:-}" | |
| while (( "$#" )); do | |
| case "$1" in | |
| --token) SIMLESSLY_TOKEN="$2"; shift 2 ;; | |
| -h|--help) usage ;; | |
| *) echo "Unknown arg: $1"; usage ;; | |
| esac | |
| done | |
| if [ -z "$SIMLESSLY_TOKEN" ]; then | |
| echo "Warning: Simlessly token not provided. You can pass --token or set SIMLESSLY_TOKEN env var." | |
| fi | |
| # Validate subscriber id numeric and <=5 digits | |
| if ! [[ "$SUBSCR_ID_RAW" =~ ^[0-9]+$ ]] || [ "${#SUBSCR_ID_RAW}" -gt 5 ]; then | |
| echo "Error: subscriber id must be numeric and up to 5 digits (e.g. 1 or 00001 or 12345)." | |
| exit 2 | |
| fi | |
| SUBSCR_ID="$(printf "%05d" "${SUBSCR_ID_RAW#0}")" | |
| IMSI="${CBRS_PREFIX}${CBRS_IBN}${SUBSCR_ID}" | |
| # Sanity check IMSI length 15 | |
| if [ "${#IMSI}" -ne 15 ]; then | |
| echo "Internal error: IMSI length is ${#IMSI}, expected 15. IMSI='$IMSI'" | |
| exit 3 | |
| fi | |
| # Build ICCID: 89 (ITU) 01 (USA) + IMSI + checkdigit | |
| ICCID_BASE="8901${IMSI}" | |
| CHECKDIGIT="$(luhn_checkdigit "${ICCID_BASE}")" | |
| ICCID="${ICCID_BASE}${CHECKDIGIT}" | |
| # Use the hardcoded Ki and OPc (testing) | |
| KI="${DEFAULT_KI}" | |
| OPC="${DEFAULT_OPC}" | |
| # ===== GENERATE ESIM ===== | |
| curl_json() { | |
| local url="$1"; shift | |
| curl -sS --location "$url" \ | |
| --header "simlessly-requestid: $(gen_uuid)" \ | |
| --header "simlessly-timestamp: $(ms_timestamp)" \ | |
| --header "simlessly-timezone: $TIMEZONE" \ | |
| --header "simlessly-token: $SIMLESSLY_TOKEN" \ | |
| --header "Content-Type: application/json" \ | |
| "$@" | |
| } | |
| # List existing eSIMs | |
| echo "Finding existing eSIMs..." | |
| LIST_JSON="$(curl_json "${SIMLESSLY_BASE}/list" --data '{}')" | |
| echo "$LIST_JSON" | jq '.obj.pageData[]? | {id, iccid, imsi, status, createTime}' || true | |
| ESIM_IDS=($(echo "$LIST_JSON" | jq -r '.obj.pageData[]?.id')) | |
| if ((${#ESIM_IDS[@]})); then | |
| echo "Found ${#ESIM_IDS[@]} existing eSIM(s): ${ESIM_IDS[*]}" | |
| else | |
| echo "No existing eSIMs found." | |
| fi | |
| # Delete existing eSIMs if any | |
| if ((${#ESIM_IDS[@]})); then | |
| echo "Deleting existing eSIMs..." | |
| IDS_JSON=$(printf '%s\n' "${ESIM_IDS[@]}" | jq -R . | jq -s '{ids:.}') | |
| DELETE_RESP="$(curl_json "${SIMLESSLY_BASE}/delete" --data "$IDS_JSON")" | |
| echo "$DELETE_RESP" | jq . | |
| echo "Deletion done." | |
| fi | |
| # --- Step 3: Generate new eSIM --- | |
| echo "Generating new eSIM with IMSI: $IMSI, ICCID: $ICCID" | |
| GEN_PAYLOAD=$(jq -n \ | |
| --arg iccid "$ICCID" \ | |
| --arg imsi "$IMSI" \ | |
| --arg ki "$KI" \ | |
| --arg opc "$OPC" \ | |
| --arg configId "$CONFIG_ID" \ | |
| '{iccid:$iccid, imsi:$imsi, ki:$ki, opc:$opc, configId:$configId, acFormats:["STRING","PICTURE","AC_LINK"]}' | |
| ) | |
| GEN_RESP="$(curl_json "${SIMLESSLY_BASE}/single-generate" --data "$GEN_PAYLOAD")" | |
| echo "$GEN_RESP" | jq . | |
| if [[ "$(echo "$GEN_RESP" | jq -r '.success')" != "true" ]]; then | |
| echo "Error: single-generate failed" | |
| exit 2 | |
| fi | |
| echo "eSIM generation done." | |
| # Add the IMSI to Open5GS DB | |
| $OPEN5GS_CMD add "${IMSI}" "${KI}" "${OPC}" | |
| echo "Added IMSI ${IMSI} to Open5GS database." | |
| # We will need to wait for AC generation; poll list endpoint until AC is available | |
| echo "Waiting for activation code generation..." | |
| AC_VALUE="" | |
| for attempt in {1..10}; do | |
| sleep 3 | |
| echo "Polling attempt $attempt/10..." | |
| LIST2_JSON="$(curl_json "${SIMLESSLY_BASE}/list" --data '{}')" | |
| AC_VALUE="$(echo "$LIST2_JSON" | jq -r --arg iccid "$ICCID" '.obj.pageData[]? | select(.iccid==$iccid) | .ac // empty')" | |
| if [[ -n "$AC_VALUE" ]]; then | |
| echo "eSIM found after $attempt attempts." | |
| ICCID_FOUND="$(echo "$LIST2_JSON" | jq -r --arg iccid "$ICCID" '.obj.pageData[]? | select(.iccid==$iccid) | .iccid')" | |
| echo "ICCID: $ICCID_FOUND" | |
| echo "Activation Code: $AC_VALUE" | |
| echo | |
| echo "QR Code (UTF-8):" | |
| qrencode -t UTF8 "LPA:$AC_VALUE" | |
| exit 0 | |
| fi | |
| done | |
| echo "Error: Activation code not found after polling." | |
| exit 3 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment