@particle/esim-tooling 1.0.0
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.
- package/README.md +187 -0
- package/dist/activation-code.d.ts +11 -0
- package/dist/activation-code.d.ts.map +1 -0
- package/dist/activation-code.js +56 -0
- package/dist/activation-code.js.map +1 -0
- package/dist/apdu.d.ts +73 -0
- package/dist/apdu.d.ts.map +1 -0
- package/dist/apdu.js +357 -0
- package/dist/apdu.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +52 -0
- package/dist/errors.js.map +1 -0
- package/dist/es10/es10b-notifications.d.ts +30 -0
- package/dist/es10/es10b-notifications.d.ts.map +1 -0
- package/dist/es10/es10b-notifications.js +294 -0
- package/dist/es10/es10b-notifications.js.map +1 -0
- package/dist/es10/es10b.d.ts +34 -0
- package/dist/es10/es10b.d.ts.map +1 -0
- package/dist/es10/es10b.js +108 -0
- package/dist/es10/es10b.js.map +1 -0
- package/dist/es10/es10c.d.ts +12 -0
- package/dist/es10/es10c.d.ts.map +1 -0
- package/dist/es10/es10c.js +133 -0
- package/dist/es10/es10c.js.map +1 -0
- package/dist/es10/iccid.d.ts +9 -0
- package/dist/es10/iccid.d.ts.map +1 -0
- package/dist/es10/iccid.js +31 -0
- package/dist/es10/iccid.js.map +1 -0
- package/dist/es10/index.d.ts +5 -0
- package/dist/es10/index.d.ts.map +1 -0
- package/dist/es10/index.js +4 -0
- package/dist/es10/index.js.map +1 -0
- package/dist/es10/tags.d.ts +55 -0
- package/dist/es10/tags.d.ts.map +1 -0
- package/dist/es10/tags.js +63 -0
- package/dist/es10/tags.js.map +1 -0
- package/dist/es9plus.d.ts +52 -0
- package/dist/es9plus.d.ts.map +1 -0
- package/dist/es9plus.js +227 -0
- package/dist/es9plus.js.map +1 -0
- package/dist/gsma-rsp2-root-ci1.d.ts +38 -0
- package/dist/gsma-rsp2-root-ci1.d.ts.map +1 -0
- package/dist/gsma-rsp2-root-ci1.js +52 -0
- package/dist/gsma-rsp2-root-ci1.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lpa.d.ts +15 -0
- package/dist/lpa.d.ts.map +1 -0
- package/dist/lpa.js +283 -0
- package/dist/lpa.js.map +1 -0
- package/dist/tlv.d.ts +14 -0
- package/dist/tlv.d.ts.map +1 -0
- package/dist/tlv.js +132 -0
- package/dist/tlv.js.map +1 -0
- package/dist/types.d.ts +230 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +108 -0
- package/dist/types.js.map +1 -0
- package/package.json +50 -0
- package/src/activation-code.ts +64 -0
- package/src/apdu.ts +419 -0
- package/src/errors.ts +69 -0
- package/src/es10/es10b-notifications.ts +331 -0
- package/src/es10/es10b.ts +163 -0
- package/src/es10/es10c.ts +168 -0
- package/src/es10/iccid.ts +32 -0
- package/src/es10/index.ts +42 -0
- package/src/es10/tags.ts +69 -0
- package/src/es9plus.ts +331 -0
- package/src/gsma-rsp2-root-ci1.ts +53 -0
- package/src/index.ts +43 -0
- package/src/lpa.ts +346 -0
- package/src/tlv.ts +137 -0
- package/src/types.ts +264 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export {
|
|
2
|
+
encodeGetEidRequest,
|
|
3
|
+
decodeGetEidResponse,
|
|
4
|
+
encodeGetProfilesInfoRequest,
|
|
5
|
+
decodeGetProfilesInfoResponse,
|
|
6
|
+
encodeEnableProfileRequest,
|
|
7
|
+
decodeEnableProfileResponse,
|
|
8
|
+
encodeDisableProfileRequest,
|
|
9
|
+
decodeDisableProfileResponse,
|
|
10
|
+
encodeDeleteProfileRequest,
|
|
11
|
+
decodeDeleteProfileResponse,
|
|
12
|
+
} from './es10c.js';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
encodeGetEuiccChallengeRequest,
|
|
16
|
+
decodeGetEuiccChallengeResponse,
|
|
17
|
+
encodeGetEuiccInfo1Request,
|
|
18
|
+
encodeAuthenticateServerRequest,
|
|
19
|
+
decodeAuthenticateServerResponse,
|
|
20
|
+
encodePrepareDownloadRequest,
|
|
21
|
+
decodePrepareDownloadResponse,
|
|
22
|
+
hashConfirmationCode,
|
|
23
|
+
encodeCancelSessionRequest,
|
|
24
|
+
} from './es10b.js';
|
|
25
|
+
|
|
26
|
+
export type {
|
|
27
|
+
AuthenticateServerParams,
|
|
28
|
+
PrepareDownloadParams,
|
|
29
|
+
AuthenticateServerDecodedResponse,
|
|
30
|
+
PrepareDownloadDecodedResponse,
|
|
31
|
+
} from './es10b.js';
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
encodeListNotificationRequest,
|
|
35
|
+
decodeListNotificationResponse,
|
|
36
|
+
encodeRetrieveNotificationsListRequest,
|
|
37
|
+
decodeRetrieveNotificationsListResponse,
|
|
38
|
+
decodeRetrieveNotificationsListFull,
|
|
39
|
+
decodeBppInstallationResult,
|
|
40
|
+
encodeRemoveNotificationRequest,
|
|
41
|
+
decodeRemoveNotificationResponse,
|
|
42
|
+
} from './es10b-notifications.js';
|
package/src/es10/tags.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// ES10c - Profile Management
|
|
2
|
+
export const TAG_PROFILE_INFO_LIST_REQUEST = 0xbf2d;
|
|
3
|
+
export const TAG_PROFILE_INFO = 0xe3;
|
|
4
|
+
export const TAG_ICCID = 0x5a;
|
|
5
|
+
export const TAG_ISDP_AID = 0x4f;
|
|
6
|
+
export const TAG_PROFILE_STATE = 0x9f70;
|
|
7
|
+
export const TAG_PROFILE_NICKNAME = 0x90;
|
|
8
|
+
export const TAG_SERVICE_PROVIDER_NAME = 0x91;
|
|
9
|
+
export const TAG_PROFILE_NAME = 0x92;
|
|
10
|
+
export const TAG_PROFILE_CLASS = 0x95;
|
|
11
|
+
export const TAG_TAG_LIST = 0x5c;
|
|
12
|
+
export const TAG_GET_EID_REQUEST = 0xbf3e;
|
|
13
|
+
export const TAG_EID = 0x5a;
|
|
14
|
+
export const TAG_ENABLE_PROFILE_REQUEST = 0xbf31;
|
|
15
|
+
export const TAG_DISABLE_PROFILE_REQUEST = 0xbf32;
|
|
16
|
+
export const TAG_DELETE_PROFILE_REQUEST = 0xbf33;
|
|
17
|
+
export const TAG_PROFILE_IDENTIFIER = 0xa0;
|
|
18
|
+
export const TAG_REFRESH_FLAG = 0x81;
|
|
19
|
+
export const TAG_RESULT = 0x80;
|
|
20
|
+
|
|
21
|
+
// ES10b - Profile Download
|
|
22
|
+
export const TAG_GET_EUICC_CHALLENGE_REQUEST = 0xbf2e;
|
|
23
|
+
export const TAG_EUICC_CHALLENGE = 0x80;
|
|
24
|
+
export const TAG_GET_EUICC_INFO1_REQUEST = 0xbf20;
|
|
25
|
+
export const TAG_AUTHENTICATE_SERVER_REQUEST = 0xbf38;
|
|
26
|
+
export const TAG_CTX_PARAMS1 = 0xa0;
|
|
27
|
+
export const TAG_MATCHING_ID = 0x80;
|
|
28
|
+
export const TAG_DEVICE_INFO = 0xa1;
|
|
29
|
+
export const TAG_PREPARE_DOWNLOAD_REQUEST = 0xbf21;
|
|
30
|
+
export const TAG_HASH_CC = 0x04;
|
|
31
|
+
export const TAG_EUICC_SIGNATURE1 = 0x5f37;
|
|
32
|
+
export const TAG_CANCEL_SESSION_REQUEST = 0xbf41;
|
|
33
|
+
export const TAG_CANCEL_SESSION_REASON = 0x80;
|
|
34
|
+
export const TAG_CANCEL_SESSION_TRANSACTION_ID = 0x80;
|
|
35
|
+
|
|
36
|
+
// ES10b - BPP Loading
|
|
37
|
+
export const TAG_BOUND_PROFILE_PACKAGE = 0xbf36;
|
|
38
|
+
|
|
39
|
+
// ES10b - Notifications
|
|
40
|
+
export const TAG_LIST_NOTIFICATION_REQUEST = 0xbf28;
|
|
41
|
+
export const TAG_NOTIFICATION_METADATA_LIST = 0xa0;
|
|
42
|
+
export const TAG_NOTIFICATION_METADATA = 0xbf2f;
|
|
43
|
+
export const TAG_NOTIFICATION_SEQ_NUMBER = 0x80;
|
|
44
|
+
export const TAG_NOTIFICATION_OPERATION = 0x81;
|
|
45
|
+
export const TAG_NOTIFICATION_ADDRESS = 0x0c;
|
|
46
|
+
export const TAG_NOTIFICATION_ICCID = 0x5a;
|
|
47
|
+
export const TAG_RETRIEVE_NOTIFICATIONS_LIST_REQUEST = 0xbf2b;
|
|
48
|
+
export const TAG_NOTIFICATION_SEARCH_CRITERIA = 0xa0;
|
|
49
|
+
export const TAG_REMOVE_NOTIFICATION_REQUEST = 0xbf30;
|
|
50
|
+
export const TAG_DELETE_NOTIFICATION_STATUS = 0x80;
|
|
51
|
+
|
|
52
|
+
// ES10b - ProfileInstallationResult (in notifications and BPP response)
|
|
53
|
+
export const TAG_PROFILE_INSTALLATION_RESULT = 0xbf37;
|
|
54
|
+
export const TAG_PROFILE_INSTALLATION_RESULT_DATA = 0xbf27;
|
|
55
|
+
export const TAG_FINAL_RESULT = 0xa2; // success - contains ICCID
|
|
56
|
+
export const TAG_ERROR_RESULT = 0xa1; // failure - contains subjectCode + reasonCode
|
|
57
|
+
export const TAG_SUBJECT_CODE = 0x80;
|
|
58
|
+
export const TAG_REASON_CODE = 0x81;
|
|
59
|
+
|
|
60
|
+
// ES10b - OtherSignedNotification (SEQUENCE tag)
|
|
61
|
+
export const TAG_OTHER_SIGNED_NOTIFICATION = 0x30;
|
|
62
|
+
|
|
63
|
+
// ES10b - AuthenticateServer response
|
|
64
|
+
export const TAG_AUTH_SERVER_RESPONSE_OK = 0xa0;
|
|
65
|
+
export const TAG_AUTH_SERVER_RESPONSE_ERROR = 0xa1;
|
|
66
|
+
|
|
67
|
+
// ES10b - PrepareDownload response
|
|
68
|
+
export const TAG_PREPARE_DOWNLOAD_RESPONSE_OK = 0xa0;
|
|
69
|
+
export const TAG_PREPARE_DOWNLOAD_RESPONSE_ERROR = 0xa1;
|
package/src/es9plus.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { Es9PlusError } from './errors.js';
|
|
2
|
+
import type { ServerAdapter } from './types.js';
|
|
3
|
+
import { tlvEncode, tlvDecode, tlvConcat, type TlvNode } from './tlv.js';
|
|
4
|
+
import { decodeIccid } from './es10/iccid.js';
|
|
5
|
+
|
|
6
|
+
// ES9+ operation tags (SGP.22 ASN.1)
|
|
7
|
+
const TAG_REMOTE_PROFILE_PROVISIONING = 0xa2; // [2] RemoteProfileProvisioningRequest/Response wrapper (SGP.22 6.6.1)
|
|
8
|
+
const TAG_INITIATE_AUTH = 0xbf39; // [57] InitiateAuthenticationRequest/Response
|
|
9
|
+
const TAG_GET_BPP = 0xbf3a; // [58] GetBoundProfilePackageRequest/Response
|
|
10
|
+
const TAG_AUTHENTICATE_CLIENT = 0xbf3b; // [59] AuthenticateClientRequest/Response
|
|
11
|
+
const TAG_HANDLE_NOTIFICATION = 0xbf3d; // [61] HandleNotification
|
|
12
|
+
const TAG_CANCEL_SESSION = 0xbf41; // [65] CancelSessionRequestEs9/Response
|
|
13
|
+
|
|
14
|
+
// Common ASN.1 tags
|
|
15
|
+
const TAG_OCTET_STRING = 0x04;
|
|
16
|
+
|
|
17
|
+
// ES9+ CHOICE response tags (AUTOMATIC TAGS → implicit context tags)
|
|
18
|
+
// Success variants are context [0], error INTEGER is context [1]
|
|
19
|
+
const TAG_RESPONSE_OK = 0xa0; // [0] IMPLICIT SEQUENCE (constructed)
|
|
20
|
+
const TAG_RESPONSE_ERROR = 0x81; // [1] IMPLICIT INTEGER (primitive)
|
|
21
|
+
|
|
22
|
+
// ES9+ error code names (SGP.22 Section 6.5.2)
|
|
23
|
+
const ES9PLUS_ERROR_NAMES: Record<number, string> = {
|
|
24
|
+
1: 'eumCertificateInvalid',
|
|
25
|
+
2: 'eumCertificateExpired',
|
|
26
|
+
3: 'euiccCertificateInvalid',
|
|
27
|
+
4: 'euiccCertificateExpired',
|
|
28
|
+
5: 'euiccSignatureInvalid',
|
|
29
|
+
6: 'matchingIdRefused',
|
|
30
|
+
7: 'eidMismatch',
|
|
31
|
+
8: 'noEligibleProfile',
|
|
32
|
+
9: 'ciPKUnknown',
|
|
33
|
+
10: 'invalidTransactionId',
|
|
34
|
+
11: 'insufficientMemory',
|
|
35
|
+
12: 'ciPKMismatch',
|
|
36
|
+
127: 'undefinedError',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Context tags for IMPLICIT fields in ES9+ SEQUENCE members
|
|
40
|
+
const TAG_TRANSACTION_ID = 0x80; // [0] TransactionId (OCTET STRING)
|
|
41
|
+
const TAG_EUICC_CHALLENGE = 0x81; // [1] Octet16
|
|
42
|
+
const TAG_SMDP_ADDRESS = 0x83; // [3] UTF8String
|
|
43
|
+
|
|
44
|
+
// Tags inside StoreMetadataRequest (profileMetadata)
|
|
45
|
+
const TAG_ICCID = 0x5a; // [APPLICATION 26] ICCID
|
|
46
|
+
const TAG_PROFILE_METADATA = 0xbf25; // [37] StoreMetadataRequest
|
|
47
|
+
const TAG_SERVICE_PROVIDER_NAME = 0x91; // [17] UTF8String
|
|
48
|
+
const TAG_PROFILE_NAME = 0x92; // [18] UTF8String
|
|
49
|
+
|
|
50
|
+
export interface InitiateAuthParams {
|
|
51
|
+
euiccChallenge: Uint8Array;
|
|
52
|
+
euiccInfo1: Uint8Array;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface InitiateAuthResponse {
|
|
56
|
+
transactionId: string;
|
|
57
|
+
serverSigned1: Uint8Array;
|
|
58
|
+
serverSignature1: Uint8Array;
|
|
59
|
+
euiccCiPKIdToBeUsed: Uint8Array;
|
|
60
|
+
serverCertificate: Uint8Array;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AuthenticateClientParams {
|
|
64
|
+
transactionId: string;
|
|
65
|
+
authenticateServerResponse: Uint8Array;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AuthenticateClientResponse {
|
|
69
|
+
transactionId: string;
|
|
70
|
+
smdpSigned2: Uint8Array;
|
|
71
|
+
smdpSignature2: Uint8Array;
|
|
72
|
+
smdpCertificate: Uint8Array;
|
|
73
|
+
profileMetadata?: ProfileMetadata;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ProfileMetadata {
|
|
77
|
+
iccid?: string;
|
|
78
|
+
serviceProviderName?: string;
|
|
79
|
+
profileName?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface GetBppParams {
|
|
83
|
+
transactionId: string;
|
|
84
|
+
prepareDownloadResponse: Uint8Array;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface GetBppResponse {
|
|
88
|
+
transactionId: string;
|
|
89
|
+
boundProfilePackage: Uint8Array;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class Es9PlusClient {
|
|
93
|
+
private server: ServerAdapter;
|
|
94
|
+
|
|
95
|
+
constructor(server: ServerAdapter) {
|
|
96
|
+
this.server = server;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async initiateAuthentication(smdpAddress: string, params: InitiateAuthParams): Promise<InitiateAuthResponse> {
|
|
100
|
+
const body = tlvConcat(
|
|
101
|
+
tlvEncode(TAG_EUICC_CHALLENGE, params.euiccChallenge),
|
|
102
|
+
tlvEncode(TAG_SMDP_ADDRESS, new TextEncoder().encode(smdpAddress)),
|
|
103
|
+
params.euiccInfo1, // already DER-encoded with BF20 tag
|
|
104
|
+
);
|
|
105
|
+
const requestData = tlvEncode(TAG_INITIATE_AUTH, body);
|
|
106
|
+
|
|
107
|
+
const responseData = await this.sendRequest(smdpAddress, 'initiateAuthentication', requestData);
|
|
108
|
+
const outer = tlvDecode(responseData);
|
|
109
|
+
this.expectTag(outer, TAG_INITIATE_AUTH, 'initiateAuthentication');
|
|
110
|
+
|
|
111
|
+
const inner = this.getFirstChild(outer, 'initiateAuthentication');
|
|
112
|
+
if (inner.tag === TAG_RESPONSE_ERROR) {
|
|
113
|
+
throw this.makeError('initiateAuthentication', inner.value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Success SEQUENCE: [0]txId, ServerSigned1, 5F37 sig, 04 ciPKId, Certificate
|
|
117
|
+
const children = this.expectChildren(inner, 5, 'initiateAuthentication');
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
transactionId: bytesToHex(children[0].value),
|
|
121
|
+
serverSigned1: children[1].raw!,
|
|
122
|
+
serverSignature1: children[2].raw!,
|
|
123
|
+
euiccCiPKIdToBeUsed: children[3].raw!,
|
|
124
|
+
serverCertificate: children[4].raw!,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async authenticateClient(smdpAddress: string, params: AuthenticateClientParams): Promise<AuthenticateClientResponse> {
|
|
129
|
+
const body = tlvConcat(
|
|
130
|
+
tlvEncode(TAG_TRANSACTION_ID, hexToBytes(params.transactionId)),
|
|
131
|
+
params.authenticateServerResponse, // already DER-encoded with BF38 tag
|
|
132
|
+
);
|
|
133
|
+
const requestData = tlvEncode(TAG_AUTHENTICATE_CLIENT, body);
|
|
134
|
+
|
|
135
|
+
const responseData = await this.sendRequest(smdpAddress, 'authenticateClient', requestData);
|
|
136
|
+
const outer = tlvDecode(responseData);
|
|
137
|
+
this.expectTag(outer, TAG_AUTHENTICATE_CLIENT, 'authenticateClient');
|
|
138
|
+
|
|
139
|
+
const inner = this.getFirstChild(outer, 'authenticateClient');
|
|
140
|
+
if (inner.tag === TAG_RESPONSE_ERROR) {
|
|
141
|
+
throw this.makeError('authenticateClient', inner.value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Success SEQUENCE: [0]txId, [37]profileMetadata?, SmdpSigned2, 5F37 sig, Certificate
|
|
145
|
+
const children = inner.children;
|
|
146
|
+
if (!children || children.length < 4) {
|
|
147
|
+
throw new Es9PlusError({ statusCode: 200, message: 'Incomplete authenticateClient response' });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let idx = 0;
|
|
151
|
+
const txId = bytesToHex(children[idx++].value);
|
|
152
|
+
|
|
153
|
+
let profileMetadata: ProfileMetadata | undefined;
|
|
154
|
+
if (idx < children.length && children[idx].tag === TAG_PROFILE_METADATA) {
|
|
155
|
+
profileMetadata = this.parseProfileMetadata(children[idx]);
|
|
156
|
+
idx++;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (idx + 3 > children.length) {
|
|
160
|
+
throw new Es9PlusError({ statusCode: 200, message: 'Incomplete authenticateClient response' });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
transactionId: txId,
|
|
165
|
+
smdpSigned2: children[idx].raw!,
|
|
166
|
+
smdpSignature2: children[idx + 1].raw!,
|
|
167
|
+
smdpCertificate: children[idx + 2].raw!,
|
|
168
|
+
profileMetadata,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async getBoundProfilePackage(smdpAddress: string, params: GetBppParams): Promise<GetBppResponse> {
|
|
173
|
+
const body = tlvConcat(
|
|
174
|
+
tlvEncode(TAG_TRANSACTION_ID, hexToBytes(params.transactionId)),
|
|
175
|
+
params.prepareDownloadResponse, // already DER-encoded with BF21 tag
|
|
176
|
+
);
|
|
177
|
+
const requestData = tlvEncode(TAG_GET_BPP, body);
|
|
178
|
+
|
|
179
|
+
const responseData = await this.sendRequest(smdpAddress, 'getBoundProfilePackage', requestData);
|
|
180
|
+
const outer = tlvDecode(responseData);
|
|
181
|
+
this.expectTag(outer, TAG_GET_BPP, 'getBoundProfilePackage');
|
|
182
|
+
|
|
183
|
+
const inner = this.getFirstChild(outer, 'getBoundProfilePackage');
|
|
184
|
+
if (inner.tag === TAG_RESPONSE_ERROR) {
|
|
185
|
+
throw this.makeError('getBoundProfilePackage', inner.value);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Success SEQUENCE: [0]txId, BF36{bpp}
|
|
189
|
+
const children = this.expectChildren(inner, 2, 'getBoundProfilePackage');
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
transactionId: bytesToHex(children[0].value),
|
|
193
|
+
boundProfilePackage: children[1].raw!,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async handleNotification(notificationAddress: string, pendingNotification: Uint8Array): Promise<void> {
|
|
198
|
+
// BF3D { pendingNotification }, wrapped in A2 per SGP.22 Section 6.6.1
|
|
199
|
+
const innerRequest = tlvEncode(TAG_HANDLE_NOTIFICATION, pendingNotification);
|
|
200
|
+
const requestData = tlvEncode(TAG_REMOTE_PROFILE_PROVISIONING, innerRequest);
|
|
201
|
+
|
|
202
|
+
const response = await this.server.sendRsp(
|
|
203
|
+
notificationAddress,
|
|
204
|
+
'handleNotification',
|
|
205
|
+
requestData,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (response.statusCode === 204) return;
|
|
209
|
+
|
|
210
|
+
if (response.statusCode !== 200) {
|
|
211
|
+
throw new Es9PlusError({
|
|
212
|
+
statusCode: response.statusCode,
|
|
213
|
+
message: `handleNotification failed: HTTP ${response.statusCode}`,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async cancelSession(smdpAddress: string, transactionId: string, cancelSessionResponse: Uint8Array): Promise<void> {
|
|
219
|
+
// CancelSessionRequestEs9: transactionId has no context tag, uses native OCTET STRING
|
|
220
|
+
const body = tlvConcat(
|
|
221
|
+
tlvEncode(TAG_OCTET_STRING, hexToBytes(transactionId)),
|
|
222
|
+
cancelSessionResponse, // already DER-encoded with BF41 tag
|
|
223
|
+
);
|
|
224
|
+
const requestData = tlvEncode(TAG_CANCEL_SESSION, body);
|
|
225
|
+
|
|
226
|
+
await this.sendRequest(smdpAddress, 'cancelSession', requestData);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async sendRequest(smdpAddress: string, endpoint: string, requestData: Uint8Array): Promise<Uint8Array> {
|
|
230
|
+
// Wrap request in RemoteProfileProvisioningRequest (A2) per SGP.22 Section 6.6.1
|
|
231
|
+
const wrappedRequest = tlvEncode(TAG_REMOTE_PROFILE_PROVISIONING, requestData);
|
|
232
|
+
|
|
233
|
+
const response = await this.server.sendRsp(smdpAddress, endpoint, wrappedRequest);
|
|
234
|
+
|
|
235
|
+
if (response.statusCode !== 200) {
|
|
236
|
+
throw new Es9PlusError({
|
|
237
|
+
statusCode: response.statusCode,
|
|
238
|
+
message: `${endpoint} failed: HTTP ${response.statusCode}`,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!response.responseData || response.responseData.length === 0) {
|
|
243
|
+
throw new Es9PlusError({
|
|
244
|
+
statusCode: response.statusCode,
|
|
245
|
+
message: `${endpoint} returned empty response`,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Unwrap RemoteProfileProvisioningResponse (A2) wrapper per SGP.22 Section 6.6.1
|
|
250
|
+
const wrapper = tlvDecode(response.responseData);
|
|
251
|
+
this.expectTag(wrapper, TAG_REMOTE_PROFILE_PROVISIONING, endpoint);
|
|
252
|
+
|
|
253
|
+
// Return the raw bytes of the inner CHOICE element
|
|
254
|
+
const inner = this.getFirstChild(wrapper, endpoint);
|
|
255
|
+
return inner.raw!;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private expectTag(node: TlvNode, tag: number, operation: string): void {
|
|
259
|
+
if (node.tag !== tag) {
|
|
260
|
+
throw new Es9PlusError({
|
|
261
|
+
statusCode: 200,
|
|
262
|
+
message: `${operation}: unexpected response tag 0x${node.tag.toString(16)}`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private getFirstChild(node: TlvNode, operation: string): TlvNode {
|
|
268
|
+
const inner = node.children?.[0];
|
|
269
|
+
if (!inner) {
|
|
270
|
+
throw new Es9PlusError({ statusCode: 200, message: `${operation}: empty response` });
|
|
271
|
+
}
|
|
272
|
+
return inner;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private expectChildren(node: TlvNode, minCount: number, operation: string): TlvNode[] {
|
|
276
|
+
const children = node.children;
|
|
277
|
+
if (!children || children.length < minCount) {
|
|
278
|
+
throw new Es9PlusError({
|
|
279
|
+
statusCode: 200,
|
|
280
|
+
message: `${operation}: incomplete response (expected ${minCount} fields, got ${children?.length ?? 0})`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return children;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private parseProfileMetadata(node: TlvNode): ProfileMetadata {
|
|
287
|
+
const result: ProfileMetadata = {};
|
|
288
|
+
if (!node.children) return result;
|
|
289
|
+
|
|
290
|
+
for (const child of node.children) {
|
|
291
|
+
switch (child.tag) {
|
|
292
|
+
case TAG_ICCID:
|
|
293
|
+
result.iccid = decodeIccid(child.value);
|
|
294
|
+
break;
|
|
295
|
+
case TAG_SERVICE_PROVIDER_NAME:
|
|
296
|
+
result.serviceProviderName = new TextDecoder().decode(child.value);
|
|
297
|
+
break;
|
|
298
|
+
case TAG_PROFILE_NAME:
|
|
299
|
+
result.profileName = new TextDecoder().decode(child.value);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private makeError(operation: string, errorValue: Uint8Array): Es9PlusError {
|
|
308
|
+
const code = errorValue.length > 0 ? errorValue[0] : 0;
|
|
309
|
+
const name = ES9PLUS_ERROR_NAMES[code] ?? `code ${code}`;
|
|
310
|
+
return new Es9PlusError({
|
|
311
|
+
statusCode: 200,
|
|
312
|
+
serverStatus: 'Failed',
|
|
313
|
+
reasonCode: String(code),
|
|
314
|
+
message: `${operation} failed: ${name}`,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function hexToBytes(hex: string): Uint8Array {
|
|
320
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
321
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
322
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
323
|
+
}
|
|
324
|
+
return bytes;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function bytesToHex(bytes: Uint8Array): string {
|
|
328
|
+
return Array.from(bytes)
|
|
329
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
330
|
+
.join('');
|
|
331
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSMA RSP2 Root CI1 Certificate
|
|
3
|
+
*
|
|
4
|
+
* This is the root Certificate Issuer (CI) for the GSMA Remote SIM Provisioning
|
|
5
|
+
* (RSP) ecosystem. It is used to:
|
|
6
|
+
*
|
|
7
|
+
* 1. Sign TLS certificates for SM-DP+ servers (enabling secure HTTPS connections)
|
|
8
|
+
* 2. Sign the SGP.22 PKI chain (EUM certificates, eUICC certificates)
|
|
9
|
+
*
|
|
10
|
+
* Certificate Details:
|
|
11
|
+
* - Subject: O=GSM Association, CN=GSM Association - RSP2 Root CI1
|
|
12
|
+
* - Issuer: Self-signed (same as subject)
|
|
13
|
+
* - Validity: Feb 22, 2017 - Feb 21, 2052
|
|
14
|
+
* - Subject Key ID: 81:37:0F:51:25:D0:B1:D4:08:D4:C3:B2:32:E6:D2:5E:79:5B:EB:FB
|
|
15
|
+
* - Algorithm: ECDSA with SHA-256 (P-256 curve)
|
|
16
|
+
*
|
|
17
|
+
* This certificate is NOT included in standard OS/browser CA stores because it's
|
|
18
|
+
* a private PKI specific to the GSMA eSIM ecosystem. Embedding it here allows
|
|
19
|
+
* the proxy to securely connect to SM-DP+ servers without disabling TLS verification.
|
|
20
|
+
*
|
|
21
|
+
* Source: Extracted from SM-DP+ server certificate chain
|
|
22
|
+
* Verified against: SGP.22 v2.2.2 Annex A, GSMA CI documentation
|
|
23
|
+
*
|
|
24
|
+
* @see https://www.gsma.com/esim/
|
|
25
|
+
* @see SGP.22 Section 4.1.1 (Certificate Issuer)
|
|
26
|
+
*/
|
|
27
|
+
export const GSMA_RSP2_ROOT_CI1_PEM = `-----BEGIN CERTIFICATE-----
|
|
28
|
+
MIICSTCCAe+gAwIBAgIQbmhWeneg7nyF7hg5Y9+qejAKBggqhkjOPQQDAjBEMRgw
|
|
29
|
+
FgYDVQQKEw9HU00gQXNzb2NpYXRpb24xKDAmBgNVBAMTH0dTTSBBc3NvY2lhdGlv
|
|
30
|
+
biAtIFJTUDIgUm9vdCBDSTEwIBcNMTcwMjIyMDAwMDAwWhgPMjA1MjAyMjEyMzU5
|
|
31
|
+
NTlaMEQxGDAWBgNVBAoTD0dTTSBBc3NvY2lhdGlvbjEoMCYGA1UEAxMfR1NNIEFz
|
|
32
|
+
c29jaWF0aW9uIC0gUlNQMiBSb290IENJMTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
|
33
|
+
A0IABJ1qutL0HCMX52GJ6/jeibsAqZfULWj/X10p/Min6seZN+hf5llovbCNuB2n
|
|
34
|
+
unLz+O8UD0SUCBUVo8e6n9X1TuajgcAwgb0wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
|
|
35
|
+
EwEB/wQFMAMBAf8wEwYDVR0RBAwwCogIKwYBBAGC6WAwFwYDVR0gAQH/BA0wCzAJ
|
|
36
|
+
BgdngRIBAgEAME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9nc21hLWNybC5zeW1h
|
|
37
|
+
dXRoLmNvbS9vZmZsaW5lY2EvZ3NtYS1yc3AyLXJvb3QtY2kxLmNybDAdBgNVHQ4E
|
|
38
|
+
FgQUgTcPUSXQsdQI1MOyMubSXnlb6/swCgYIKoZIzj0EAwIDSAAwRQIgIJdYsOMF
|
|
39
|
+
WziPK7l8nh5mu0qiRiVf25oa9ullG/OIASwCIQDqCmDrYf+GziHXBOiwJwnBaeBO
|
|
40
|
+
aFsiLzIEOaUuZwdNUw==
|
|
41
|
+
-----END CERTIFICATE-----`;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Subject Key Identifier for GSMA RSP2 Root CI1
|
|
45
|
+
* This can be used to verify that an eUICC trusts this CI
|
|
46
|
+
* (appears in euiccInfo1.euiccCiPKIdListForVerification)
|
|
47
|
+
*/
|
|
48
|
+
export const GSMA_RSP2_ROOT_CI1_SKI = '81370F5125D0B1D408D4C3B232E6D25E795BEBFB';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Common Name of the GSMA RSP2 Root CI1
|
|
52
|
+
*/
|
|
53
|
+
export const GSMA_RSP2_ROOT_CI1_CN = 'GSM Association - RSP2 Root CI1';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export { EsimLpa } from './lpa.js';
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
ProfileState,
|
|
5
|
+
ProfileClass,
|
|
6
|
+
NotificationEvent,
|
|
7
|
+
CancelSessionReason,
|
|
8
|
+
EnableProfileResult,
|
|
9
|
+
DisableProfileResult,
|
|
10
|
+
DeleteProfileResult,
|
|
11
|
+
BppInstallErrorCode,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
DeviceAdapter,
|
|
16
|
+
ServerAdapter,
|
|
17
|
+
ServerResponse,
|
|
18
|
+
Profile,
|
|
19
|
+
Notification,
|
|
20
|
+
NotificationMetadata,
|
|
21
|
+
NotificationWithResult,
|
|
22
|
+
SendResult,
|
|
23
|
+
ProcessNotificationsOptions,
|
|
24
|
+
ProcessNotificationsResult,
|
|
25
|
+
ProfileInstallationResult,
|
|
26
|
+
ActivationCode,
|
|
27
|
+
DownloadStep,
|
|
28
|
+
DownloadProgress,
|
|
29
|
+
DownloadResult,
|
|
30
|
+
InstallProfileOptions,
|
|
31
|
+
EsimLpaOptions,
|
|
32
|
+
} from './types.js';
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
EsimError,
|
|
36
|
+
DeviceError,
|
|
37
|
+
Es10Error,
|
|
38
|
+
Es9PlusError,
|
|
39
|
+
TlvError,
|
|
40
|
+
ActivationCodeError,
|
|
41
|
+
} from './errors.js';
|
|
42
|
+
|
|
43
|
+
export { parseActivationCode, isValidActivationCode, formatActivationCode } from './activation-code.js';
|