Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for preview mode #141

Closed
wants to merge 13 commits into from
1 change: 1 addition & 0 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ There is no secondary confirmation for deleting a Profile, so please perform it
- `-m`: Matching ID, activation code. optional.
- `-c`: Confirmation Code, optional.
- `-i`: The IMEI of the device to which Profile is to be downloaded, optional.
- `-p`: Preview mode, optional.
- `-a`: LPA qrcode activation code string, e.g: `LPA:1$<sm-dp+ domain>$<matching id>`, if provided this option takes precedence over the `-s` and `-m` options, optional.

<details>
Expand Down
127 changes: 127 additions & 0 deletions euicc/es9p_ex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "es9p_ex.h"
#include "hexutil.h"
#include "derutil.h"
#include "base64.h"

int es9p_ex_get_profile_metadata(struct euicc_ctx *ctx, struct es9p_ex_profile_metadata *profile_metadata)
{
int fret = 0;

struct euicc_derutil_node n_request = {
.tag = 0xBF25, // StoreMetadataRequest
};

struct euicc_derutil_node tmpnode, n_ProfileMetadata;

uint8_t *profile_metadata_buffer = malloc(euicc_base64_decode_len(ctx->http._internal.prepare_download_param->b64_profileMetadata));
if (!profile_metadata_buffer)
{
goto err;
}

uint32_t profile_metadata_buffer_len = euicc_base64_decode(profile_metadata_buffer, ctx->http._internal.prepare_download_param->b64_profileMetadata);
if (profile_metadata_buffer_len < 0)
{
goto err;
}

if (euicc_derutil_unpack_find_tag(&n_ProfileMetadata, n_request.tag, profile_metadata_buffer, profile_metadata_buffer_len) < 0)
{
goto err;
}

profile_metadata->profileClass = ES9P_EX_PROFILE_CLASS_NULL;
profile_metadata->iconType = ES9P_EX_ICON_TYPE_NULL;
profile_metadata->icon = NULL;

tmpnode.self.ptr = n_ProfileMetadata.value;
tmpnode.self.length = 0;
while (euicc_derutil_unpack_next(&tmpnode, &tmpnode, n_ProfileMetadata.value, n_ProfileMetadata.length) == 0)
{
long tmpint;
switch (tmpnode.tag)
{
case 0x5A: // ICCID
euicc_hexutil_bin2gsmbcd(profile_metadata->iccid, sizeof(profile_metadata->iccid), tmpnode.value, tmpnode.length);
break;
case 0X91: // ISDPAID
profile_metadata->serviceProviderName = malloc(tmpnode.length + 1);
if (profile_metadata->serviceProviderName)
{
memcpy(profile_metadata->serviceProviderName, tmpnode.value, tmpnode.length);
profile_metadata->serviceProviderName[tmpnode.length] = '\0';
}
break;
case 0x92: // Profile Name
profile_metadata->profileName = malloc(tmpnode.length + 1);
if (profile_metadata->profileName)
{
memcpy(profile_metadata->profileName, tmpnode.value, tmpnode.length);
profile_metadata->profileName[tmpnode.length] = '\0';
}
break;
case 0x93: // Icon Type
tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length);
switch (tmpint)
{
case ES9P_EX_ICON_TYPE_JPEG:
case ES9P_EX_ICON_TYPE_PNG:
profile_metadata->iconType = tmpint;
break;
default:
profile_metadata->iconType = ES9P_EX_ICON_TYPE_UNDEFINED;
break;
}
break;
case 0x94: // Icon
profile_metadata->icon = malloc(euicc_base64_encode_len(tmpnode.length));
if (profile_metadata->icon)
{
euicc_base64_encode(profile_metadata->icon, tmpnode.value, tmpnode.length);
}
break;
case 0x95: // Profile Class
tmpint = euicc_derutil_convert_bin2long(tmpnode.value, tmpnode.length);
switch (tmpint)
{
case ES9P_EX_PROFILE_CLASS_TEST:
case ES9P_EX_PROFILE_CLASS_PROVISIONING:
case ES9P_EX_PROFILE_CLASS_OPERATIONAL:
profile_metadata->profileClass = tmpint;
break;
default:
profile_metadata->profileClass = ES9P_EX_PROFILE_CLASS_UNDEFINED;
break;
}
break;
default:
break;
}
}
goto exit;
err:
fret = -1;
exit:
free(profile_metadata_buffer);
return fret;
}

void es9p_ex_free(struct es9p_ex_profile_metadata *profile_metadata)
{
if (profile_metadata->serviceProviderName)
{
free(profile_metadata->serviceProviderName);
}
if (profile_metadata->profileName)
{
free(profile_metadata->profileName);
}
if (profile_metadata->icon)
{
free(profile_metadata->icon);
}
}
48 changes: 48 additions & 0 deletions euicc/es9p_ex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "euicc.h"

enum es9p_ex_icon_type
{
ES9P_EX_ICON_TYPE_NULL = -1,
ES9P_EX_ICON_TYPE_JPEG = 0,
ES9P_EX_ICON_TYPE_PNG = 1,
ES9P_EX_ICON_TYPE_UNDEFINED = 255,
};

enum es9p_ex_profile_class
{
ES9P_EX_PROFILE_CLASS_NULL = -1,
ES9P_EX_PROFILE_CLASS_TEST = 0,
ES9P_EX_PROFILE_CLASS_PROVISIONING = 1,
ES9P_EX_PROFILE_CLASS_OPERATIONAL = 2,
ES9P_EX_PROFILE_CLASS_UNDEFINED = 255,
};

struct es9p_ex_profile_metadata
{
char iccid[(10 * 2) + 1];
char isdpAid[(16 * 2) + 1];
char *serviceProviderName;
char *profileName;
enum es9p_ex_profile_class profileClass;
enum es9p_ex_icon_type iconType;
char *icon;
struct
{
char **profileManagementOperation;
char *notificationAddress;
} notificationConfigurationInfo;
struct
{
char *mccmnc;
char *gid1;
char *gid2;
} profileOwner;
struct
{
char *dpOid;
} dpProprietaryData;
char **profilePolicyRules;
};

int es9p_ex_get_profile_metadata(struct euicc_ctx *ctx, struct es9p_ex_profile_metadata *profile_metadata);
void es9p_ex_free(struct es9p_ex_profile_metadata *profile_metadata);
22 changes: 22 additions & 0 deletions euicc/tostr.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,25 @@ const char *euicc_errorreason2str(enum es10b_error_reason value)
}
return "(no_str_available)";
}

const char *euicc_cancel_session_reason2str(enum es10b_cancel_session_reason reason)
{
switch (reason)
{
case ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION:
return "end_user_rejection";
case ES10B_CANCEL_SESSION_REASON_POSTPONED:
return "post_poned";
case ES10B_CANCEL_SESSION_REASON_TIMEOUT:
return "timeout";
case ES10B_CANCEL_SESSION_REASON_PPRNOTALLOWED:
return "ppr_not_allowed";
case ES10B_CANCEL_SESSION_REASON_METADATAMISMATCH:
return "metadata_mismatch";
case ES10B_CANCEL_SESSION_REASON_LOADBPPEXECUTIONERROR:
return "load_bpp_execution_error";
case ES10B_CANCEL_SESSION_REASON_UNDEFINED:
return "undefined";
}
return "(no_str_available)";
}
1 change: 1 addition & 0 deletions euicc/tostr.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ const char *euicc_icontype2str(enum es10c_icon_type value);
const char *euicc_profilemanagementoperation2str(enum es10b_profile_management_operation value);
const char *euicc_bppcommandid2str(enum es10b_bpp_command_id value);
const char *euicc_errorreason2str(enum es10b_error_reason value);
const char *euicc_cancel_session_reason2str(enum es10b_cancel_session_reason reason);
82 changes: 80 additions & 2 deletions src/applet/profile/download.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,26 @@
#include <euicc/es10a.h>
#include <euicc/es10b.h>
#include <euicc/es9p.h>
#include <euicc/es9p_ex.h>
#include <euicc/tostr.h>

static const char *opt_string = "s:m:i:c:a:h?";
static const char *opt_string = "s:m:i:c:a:ph?";

static int applet_main(int argc, char **argv)
{
int fret;

int opt;

char *smdp = NULL;
char *matchingId = NULL;
char *imei = NULL;
char *confirmation_code = NULL;
char *activation_code = NULL;
int is_preview_mode = -1;
damonto marked this conversation as resolved.
Show resolved Hide resolved

struct es10a_euicc_configured_addresses configured_addresses = {0};
struct es10b_load_bound_profile_package_result download_result = {0};
enum es10b_cancel_session_reason cancel_reason = -1;

opt = getopt(argc, argv, opt_string);
while (opt != -1)
Expand All @@ -52,6 +54,9 @@ static int applet_main(int argc, char **argv)
activation_code += 4; // ignore uri scheme
}
break;
case 'p':
is_preview_mode = 1;
break;
case 'h':
case '?':
printf("Usage: %s [OPTIONS]\r\n", argv[0]);
Expand All @@ -60,6 +65,7 @@ static int applet_main(int argc, char **argv)
printf("\t -i IMEI\r\n");
printf("\t -c Confirmation Code (Password)\r\n");
printf("\t -a Activation Code (e.g: 'LPA:***')\r\n");
printf("\t -p Preview mode\r\n");
printf("\t -h This help info\r\n");
return -1;
default:
Expand Down Expand Up @@ -156,20 +162,60 @@ static int applet_main(int argc, char **argv)
jprint_progress("es9p_authenticate_client", smdp);
if (es9p_authenticate_client(&euicc_ctx))
{
if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION))
{
goto err;
}
jprint_error("es9p_authenticate_client", euicc_ctx.http.status.message);
goto err;
}

if (is_preview_mode == 1)
{
struct es9p_ex_profile_metadata profile_metadata;
if (es9p_ex_get_profile_metadata(&euicc_ctx, &profile_metadata))
{
goto err;
}

cJSON *jprofilemeta = cJSON_CreateObject();
cJSON_AddStringOrNullToObject(jprofilemeta, "iccid", profile_metadata.iccid);
cJSON_AddStringOrNullToObject(jprofilemeta, "isdpAid", profile_metadata.isdpAid);
cJSON_AddStringOrNullToObject(jprofilemeta, "serviceProviderName", profile_metadata.serviceProviderName);
cJSON_AddStringOrNullToObject(jprofilemeta, "profileName", profile_metadata.profileName);
cJSON_AddStringOrNullToObject(jprofilemeta, "profileClass", euicc_profileclass2str(profile_metadata.profileClass));
cJSON_AddStringOrNullToObject(jprofilemeta, "iconType", euicc_icontype2str(profile_metadata.iconType));
cJSON_AddStringOrNullToObject(jprofilemeta, "icon", profile_metadata.icon);
es9p_ex_free(&profile_metadata);

if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION))
{
goto err;
}

jprint_success(jprofilemeta);
fret = 0;
goto exit;
}

jprint_progress("es10b_prepare_download", smdp);
if (es10b_prepare_download(&euicc_ctx, confirmation_code))
{
if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION))
{
goto err;
}
jprint_error("es10b_prepare_download", NULL);
goto err;
}

jprint_progress("es9p_get_bound_profile_package", smdp);
if (es9p_get_bound_profile_package(&euicc_ctx))
{
if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_ENDUSERREJECTION))
{
goto err;
}
jprint_error("es9p_get_bound_profile_package", euicc_ctx.http.status.message);
goto err;
}
Expand All @@ -179,6 +225,20 @@ static int applet_main(int argc, char **argv)
{
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s,%s", euicc_bppcommandid2str(download_result.bppCommandId), euicc_errorreason2str(download_result.errorReason));
if (download_result.errorReason == ES10B_ERROR_REASON_PPR_NOT_ALLOWED)
{
if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_PPRNOTALLOWED))
{
goto err;
}
}
else
{
if (download_cancel_session(&euicc_ctx, ES10B_CANCEL_SESSION_REASON_LOADBPPEXECUTIONERROR))
{
goto err;
}
}
jprint_error("es10b_load_bound_profile_package", buffer);
goto err;
}
Expand All @@ -196,6 +256,24 @@ static int applet_main(int argc, char **argv)
return fret;
}

int download_cancel_session(struct euicc_ctx *ctx, enum es10b_cancel_session_reason reason)
{
const char *detail = euicc_cancel_session_reason2str(reason);
jprint_progress("es10b_cancel_session", detail);
if (es10b_cancel_session(&euicc_ctx, reason))
{
jprint_error("es10b_cancel_session", detail);
return -1;
}
jprint_progress("es9p_cancel_session", detail);
if (es9p_cancel_session(&euicc_ctx))
{
jprint_error("es9p_cancel_session", detail);
return -1;
}
return 0;
}

struct applet_entry applet_profile_download = {
.name = "download",
.main = applet_main,
Expand Down
3 changes: 3 additions & 0 deletions src/applet/profile/download.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

#include <applet.h>
#include <euicc/es10b.h>

extern struct applet_entry applet_profile_download;

int download_cancel_session(struct euicc_ctx *ctx, enum es10b_cancel_session_reason reason);