クマは森で用を足しますか?

アウトプットは重要です。

Pixel 7 のログから逆引きして学び始める eSIM MEP

AppleiPhone 13 から、Google は Pixel 7 から eSIM MEP (Multiple Enabled Profiles) をサポートしています。一般市民が触って遊べるようなものなのかどうかは疑問なところですが、どんな仕組みで動いているのか知っておいても良いかなと考えています。ということで、今回は ETSI TS 102 221 を読んでゆきます。

source.android.com

UICC が MEP をサポートしているかどうか

Pixel 7 のバグレポートを見ると、UiccSlot やその近辺のログに "..MultipleEnabledProfiles=true" といった情報を見つけることができます。

      UiccSlot:
       mActive=true
       mIsEuicc=true
       isEuiccSupportsMultipleEnabledProfiles=true
       mIsRemovable=false
       mLastRadioState={0=1, 1=2}
       mIccIds={0=898130005[****], 1=}
       mPortIdxToPhoneId={0=1, 1=-1}
       mEid=[****]
       mCardState=CARDSTATE_PRESENT
       mUiccCard=com.android.internal.telephony.uicc.euicc.EuiccCard@224a7c5
      UiccCard:
       mCardState=CARDSTATE_PRESENT
       mCardId=[****]
       mNumberOfPorts=1
      mIsSupportsMultipleEnabledProfiles=true

この情報がどこから来ているんだろうとソースコードを追ってみると、AnswerToReset.java による ATR の確認結果であることがわかります。

    private void checkEuiccSupportedCapabilities() {
        // eUICC is supported only if the b8 and b2 of the first tB after T=15 are set to 1.
        // MEP is supported only if the b8 and b1 of the first tB after T=15 are set to 1.
        for (int i = 0; i < mInterfaceBytes.size() - 1; i++) {
            if (mInterfaceBytes.get(i).getTD() != null
                    && (mInterfaceBytes.get(i).getTD() & T_MASK) == T_VALUE_FOR_GLOBAL_INTERFACE
                    && mInterfaceBytes.get(i + 1).getTB() != null) {
                if ((mInterfaceBytes.get(i + 1).getTB() & B8_MASK) != 0
                        && (mInterfaceBytes.get(i + 1).getTB() & B2_MASK) != 0) {
                    mIsEuiccSupported = true;
                }
                if ((mInterfaceBytes.get(i + 1).getTB() & B8_MASK) != 0
                        && (mInterfaceBytes.get(i + 1).getTB() & B1_MASK) != 0) {
                    mIsMultipleEnabledProfilesSupported = true;
                }
                return;
            }
        }
    }

TD3 の下位ニブル 'F' (Protocol T=15) の次のセットが Global Interface Bytes です。ETSI TS 102 221 によると、そのセットの TB の b8 および b1 がセットされていれば UICC が LSI (Logical SE Interface) をサポートしていることを示しているとのこと。上記のソースコードにあるコメントもそのように説明していますね。

Table 6.7: Coding of the first tB i (i > 2) after T = 15 of the ATR (ETSI TS 102 221)

Pixel 7 の ATR もログから拾ってきてみました。TD=3F (Protocol T=15) となっている 3 つめのセットの次のセットが Global Interface Bytes で、その TB=83 (b8/b1 ともに 1) が LSI のサポートを意味しているということですね。

D/AnswerToReset( 2191): Successfully parsed the ATR string
    3B9F97C00AB1FE453FC6838031E073FE211F65D002341569810F21 into AnswerToReset:{
        mConventionByte=3B,
        mFormatByte=9F,
        mInterfaceBytes={
            {TA=97,TB=null,TC=null,TD=C0}
            {TA=null,TB=null,TC=0A,TD=B1}
            {TA=FE,TB=45,TC=null,TD=3F}
            {TA=C6,TB=83,TC=null,TD=null}
        },
        mHistoricalBytes={80,31,E0,73,FE,21,1F,65,D0,02,34,15,69,81,0F,},
        mCheckByte=21
    }

LSI (Logical SE Interface) とその選択

LSI とは、ISO 7816 の物理インターフェースを複数の論理インターフェースへと多重化してアクセスする仕組みのこと。主に T = 0 プロトコルの端末の場合、MANAGE LSI コマンドを使って後続の APDU コマンドの論理インターフェースを選択したり、特定の論理インターフェースのみをリセットしたりするようです。論理インターフェースの、0 から 31 までの 32 個まで取り扱うことができますよと。

7.5 Logical secure element Interfaces
A UICC may support LSIs. If supported by the UICC, this shall be indicated in the ATR. LSIs provide a multiplexing mechanism which allows the terminal to communicate with multiple LSEs over one physical interface. LSIs supported by the UICC and the terminal shall be identified by consecutive numbers starting from zero. The maximum value for the LSI is '1F'. Management of LSIs is done via a dedicated command MANAGE LSI which allows:

  • selection of an LSI for subsequent APDUs (mandatory for protocol T = 0, optional for protocol T = 1);
  • resetting an LSE without affecting other LSEs; and
  • setup of the LSI configuration to be used during the card session

各論理インターフェースを通してアクセスする LSE については、下記のように説明されています。eUICC SE の場合、eSIM プロファイル (もしくはそれが入っている箱のようなもの) がこの LSE に相当するものとして読んで不都合なさそうです。なるほど、活性化されている複数の eSIM プロファイルのうちのひとつから REFRESH コマンドを受けたとき、従来の方法で eUICC をリセットすると eUICC 全体を (=全ての eSIM プロファイルを) リセットしてしまいますもんね。複数の論理インターフェースで多重化されているときには、MANAGE LSI コマンドを使って特定の論理インターフェースをリセットしたい旨を伝える必要があるみたいです。

8.10 Logical secure elements
A UICC may support several LSEs on the same physical element. Each LSE acts and is handled by the terminal like a separate secure element. The terminal communicates with the different LSEs via LSIs. Each LSE operates logically independently from the others. As seen on the interface, each LSE has its own file system, its own applications, its own AIDs, its own independent security status, its own CAT session (which means that several of them can run interleaved in parallel on the different LSIs), and its own runtime environment.
The terminal can switch between the LSEs by sending MANAGE LSI (select LSI) commands or by selecting the LSI via the NAD byte when using T = 1. The MANAGE LSI command also allows resetting an LSE. Such a reset shall be equivalent to a warm reset of the interface for a UICC not supporting LSEs. A cold reset or a warm reset of the physical interface results in resetting all LSEs. When the terminal receives a REFRESH proactive command with mode "UICC reset" on an LSI, the terminal shall reset only the corresponding LSE via a MANAGE LSI command

先ほど MANAGE LSI コマンドによる論理インターフェース選択について「主に T = 0 プロトコルの端末の場合」と書きましたが、T = 1 プロトコルであれば MANAGE LSI コマンドではなく NAD (Node ADdress byte) バイトを使って論理インターフェースを指定することができます。NAD バイトは、T = 1 のブロック構造のプロローグフィールド内に存在します。

Table 7.4. Node address byte (ETSI TS 102 221)

この NAD バイトにある DAD/SAD を使ってブロックの送受信の対象とする論理インターフェースを指定することになりますが、AT コマンドや PC/SC インターフェース経由で一般市民があれこれやれそうな気がしていないので、それについて言及するのはこのあたりまでにしておこうと思います。

Table 7.4a: LSI number encoding

端末と eUICC の LSI 利用に関する同意確認

UICC は ATR を使って LSI のサポートを端末に対して示しますが、それをただ見ているだけでは端末は LSI を利用することができません。

7.5 Logical secure element Interfaces
Terminal and UICC shall agree on using LSIs as follows:

  • The UICC indicates support for LSIs in the ATR.
  • If indicated by the UICC, the terminal shall indicate in PPS2 if LSIs shall be used for the card session.
  • If LSIs are used for the card session, LSI 0 is selected after the PPS.
  • Before sending a command on an LSI different from 0 or sending any other MANAGE LSI command, the terminal shall perform the setup of the LSI configuration by sending a MANAGE LSI (configure LSIs) APDU to the UICC.

端末側もまた、PPS2 を使って LSI を利用する能力と意思があることを UICC に対して示す必要があると説明されています。

6.4 PPS procedure
PPS2 shall only be used if the first tB i (i > 2) after T = 15 is present in the ATR. The coding for PPS2 is identical to that of the first tBi (i > 2) after T = 15. The value selected depends upon the features supported by the terminal.

MANAGE LSI コマンドの仕様

MANAGE LSI コマンドの CLA および INS は、下表のように規定されています。CLA は '8X' ではなく '80' なので、論理チャネル 0 (基本チャネル) に対してのみ送信可能なコマンドなんですね。

Command CLA INS
MANAGE LSI '80' '7C'

MANAGE LSI コマンドについて整理するには ETSI TS 102 221 から複数の表を引用する必要がありますので、あらためて別の投稿でそれを行うことにしようと思います。

eSIM.me を Windows 11 の LPA で使用する

このページに来られた方々は既にいろいろとご存じのことだと思われますので、まずは結論から。ふたつあります。

  • 「MBIM モードで接続可能な eSIM 対応通信モジュール」を増設すれば、着脱可能なカード型 eUICC である "eSIM.me" を Windows PC 上で制御できるようになります。
  • 「MBIM モードで接続可能な eSIM 内蔵通信モジュール」を増設すれば、Windows PC を eSIM 対応機にすることができます。

増設と書きましたが、M.2 通信モジュールを変換アダプタに載せて Windows PC の USB ポートに挿してみただけです。

eSIM 対応・内蔵通信モジュールとアダプタ

Windows に入っている LPA および設定アプリを用いて "eSIM.me" に対してそれらの操作を行うことができることを確認しました。もし仮に TelcoVillage GmbH が "eSIM.me" のサービスを継続しないような事態になっても、これで安心ですね(そうなのか)。

実際に確認してみたこと

もろもろ試す前の Windows の設定画面がこちらです。セルラー通信モジュールなんて載せていないデスクトップ PC ですので、セルラー関係の表示がありません。

モジュールを付ける前

そして、こちらが上述の通信モジュールを増設した後の Windows の設定画面です。セルラーの設定メニューが登場していますね。"TelcoVillege1" と表示されていますが、おそらく空の状態の "eSIM.me" のブートプロファイルが見えているものと思われます。

モジュールを付けた後

その後、Ubigi の eSIM をダウンロードしてみました。特に問題なくダウンロードの手続きは成功し、ダウンロード後の eSIM が普通に表示されています。

Ubigi の eSIM もダウンロードできた

今回使用したもの

Dell PC 用のこちらの通信モジュールを購入して試しました。商品の説明に "only for e-SIM Card" などと書かれていてちょっとあやしいですが、「eSIM 対応」と読んで問題なさそうです。

s.click.aliexpress.com

eSIM 対応は SIM2 カードスロットだけなのではと想像していましたが、SIM1 カードスロットに "eSIM.me" を装着しても問題なく使用できています。また、SIM2 カードスロットに "eSIM.me" を装着する必要があるものと考えていたのですが、なんと SIM2 として eSIM がこのモジュールに内蔵されていました。つまり、この通信モジュールさえ増設できれば、"eSIM.me" を持っていなくても Windows PC を eSIM 対応機にすることができます。

M.2 通信モジュールを USB 機器化するアダプタですが、SIM カードスロットがふたつ付いている下記のものを使用しました。上記の通り SIM2 カードスロットの利用が必要になるものと考えての選択でしたが、結局のところ SIM カードスロットがひとつの変換アダプタでも全然問題なかったということになります。

s.click.aliexpress.com

そしておなじみのカード型 eUICC、"eSIM.me" がこちらです。

esim.me

通信モジュールを選ぶのは何故か(未解決)

期待通りに Windows の LPA を起動させられたのは、これまでのところ上記の通信モジュールひとつだけです。カード型 eUICC (eSIM.me) を認識できるもの、また AT+CSIM コマンドを用いて ISD-R を選択できるものも幾つか見つけたのですが、それらでは Windows の設定画面に eSIM 関連オプションを表示させることができません。モバイルブロードバンド関係のログをさらっと見る限りでは、WindowsATR を提供できるかどうかがひとつポイントになってそうな感じはあります。

eSIM Service Initialization (下記 MS のサイトより引用)

下記のログは上記の通信モジュールを使用時に取得したもので、"GetAtrInfo Succeeded" になっていることが確認できます。その他の通信モジュールで試したときには、これが軒並み "GetAtrInfo Failed" になっていました。

[Microsoft-Windows-wmbclass]Solicited response received with the following parameters: Caller Request Id: 0x182 Driver Request Id: 0 Service Id: {0000004a-588e-c2f6-37f0-c94b8665f4d4} Command Name: Kミ堵UICC_CID_ATR Command Id: 1 Device Status: 0(MBB_STATUS_SUCCESS) Ndis Status: 0x0(NT=STATUS_SUCCESS) Payload Length: 32 Payload: 0x17000000080000003B9F96803FC7828031E073FE211B57AA8660F0010011EE00
...
[Microsoft-Windows-WwanProtDim]INDICATION RECEIVED
[Microsoft-Windows-WwanProtDim] StatusCode : NDIS_STATUS_WWAN_ATR_INFO (0x40041034)
[Microsoft-Windows-WwanProtDim] RequestId : 0x182
[Microsoft-Windows-WwanProtDim] DataLength : 0x30
...
[Microsoft-Windows-WWAN-SVC-EVENTS]WWAN Service event: [Info] WwanNhTraceMsmNotification: [NH] Dispatch WwanNotificationSourceMsm\WwanMsmEventTypeAtrInfo AtrLength=23 Interface: {{c39392e1-770f-4d88-aa4b-d9d9aabc10f6}}
...
[Microsoft-Windows-WWAN-SVC-EVENTS]WWAN Service event: [Info] CWwanUicc::onAtr: IsESIM 1 IsEnterpriseESIM 0 ReadyState 1 Interface: {{c39392e1-770f-4d88-aa4b-d9d9aabc10f6}}
...
[Microsoft-Windows-WWAN-SVC-EVENTS][M:0][E:0][Req:0] ExecutorType(4(WWAN_INTF_TYPE_MBIM_1)) GetAtrInfo Succeeded

ちゃんと調べるためには MBIM の仕様書を読まないといけなさそうですので、それはまた暇になったときのための宿題にしようと思います。

docs.microsoft.com
docs.microsoft.com
docs.microsoft.com

過去の関連記事

本ブログにて最初に "eSIM.me" を取り上げたのは、下記の記事でした。

cheerio-the-bear.hatenablog.com

着脱可能なカード型 eUICC としては、ソフトバンク系の通信事業者で商用化されているものもおすすめです。こちらはいつまでサービスされるのかかなり疑問ですので、レア度が高くなるものと思われます。

cheerio-the-bear.hatenablog.com

ワイモバイルのeSIMカード (Removable eUICC) はいい感じです

ソフトバンクおよびワイモバイルでは「eSIMプロファイルのダウンロードに使える物理的なeSIMカード」が2022年3月から提供されているようですが、あまり市場に出回っていないのか(そんな気はする)、それらのeSIMカードについて書かれたブログやツイートを見かけることがほとんどありません。今後もそのような物理的なeSIMカードが提供され続けるかというと、そんなことにはならないように思います。いまこのeSIMカードで充分に遊んでおかないと、後になって後悔するかもしれません。

www.ymobile.jp

ということで調達しました、ワイモバイルのYM5G-eUICCnano(n162)カードがこちらです。見た目は普通のSIMカードですが、カード型のeUICCになっています。サービス開始時期的におそらくほぼ同じ仕様だと思われますので、ソフトバンクの方の調達は見送りました(誰かお願いします)。

YM5G-eUICCnano(n162)カード

ちなみに、商用のeUICCカードを取り扱うのは今回で三度目になります。ドコモeSIMカードVer.1およびeSIM.meについては、それぞれ下記の記事をご覧ください。個人的には、eSIM.meがおすすめです。

cheerio-the-bear.hatenablog.com
cheerio-the-bear.hatenablog.com

では、ワイモバイルのYM5G-eUICCnano(n162)カードについて書いてゆきます。

他の通信事業者のeSIMも入れられます

eSIM関係の掲示板を見ると「どうせ他の通信事業者のeSIMは入らないんでしょ?」みたいなコメントも書かれていましたが、いいえちゃんと入るんです。実際にpovoのeSIMを入れてみたときのスクリーンショットがこちらです。

povo入りました

ワイモバイル以外のeSIMの有効化・無効化の操作も問題ありません。また、いずれかのeSIMが有効化された状態のeSIMカードは従来のSIMカードと互換性がありますので、それを他のeSIM非対応端末に装着して使用することも可能です。

GoogleのPixel端末などには「SIMマネージャー」アプリがインストールされていますが、このeSIMカードに対応している端末にはその代わりに「eSIMカード情報設定」アプリが入っています。利用規約を見る限りでは、ソフトバンク内製のアプリのようです。

eSIMマネージャーではない

ES10c: GetProfilesInfo

YM5G-eUICCnano(n162)カードには、2種類のeSIMがインストールされていました。ひとつめは、テスト用プロファイルです。GSMA TS.48に準拠しているもののようで、こちらは通常モードにおいては端末上に表示されることはありません。

serviceProviderName TS.48
profileClass Test (0)

もうひとつがワイモバイルのeSIMプロファイルということになるのですが、何故かプロビジョニング用プロファイルとしてインストールされています。また、PPR2が設定されていることがわかります。

serviceProviderName Y!mobile
profileClass Provisioning (1)
profilePolicyRules ppr2

PPR1/PPR2の説明のため、GSMA SGP.22から下記の記述を引用します。まだ実際に試してはいないのですが、このeSIMはユーザーの操作によって削除することはできないようになっているようです。

PprIds ::= BIT STRING {-- Definition of Profile Policy Rules identifiers
   pprUpdateControl(0), -- defines how to update PPRs via ES6
   ppr1(1), -- Indicator for PPR1 'Disabling of this Profile is not allowed'
   ppr2(2) -- Indicator for PPR2 'Deletion of this Profile is not allowed'
}

ショップ店頭でのeSIMの開通手続きの様子を見ていないので定かではありませんが、YM5G-eUICCnano(n162)カードの製造時点でこの2種類のeSIMがインストールされていたのではと想像しています。

ES10b: GetRAT

YM5G-eUICCnano(n162)カードのRAT (Rules Authorisation Table)には、特別な仕掛けはありません。MCC/MNC 'EEEEEE'はオールマイティですので、どの通信事業者にも同じルールが適用されます。また、PPRが設定されたeSIMをインストールする際には、ユーザーの同意を求めることになります。

pprIds ppr1, ppr2
allowedOperators mccMnc:EEEEEE, gid1:null, gid2:null
pprFlags consentRequired

そういえば、以前確認したドコモeSIMカード(Ver.1)には、下表のルールも記述されていました。例えば、ドコモのeSIMにPPR2が設定されていたとしても、そのインストールの際にユーザーの同意を求める必要はない、ということになるようです。

pprIds ppr1, ppr2
allowedOperators mccMnc:44F001, gid1:00FFFF, gid2:FFFFFF
pprFlags N/A

ES10b: GetEUICCInfo

さすがに最近の標準仕様バージョンに準拠しているようですが、気になるところがふたつありました。ひとつめは、rspCapabilityの値。

profileVersion 2.3.1
svn 2.2.2
uiccCapability BIT STRING '077F36CB80'
ts102241Version 9.2.0
globalplatformVersion 2.3.0
rspCapability BIT STRING '0192'
euiccCiPKIdListForVerification '81370F5125D0B1D408D4C3B232E6D25E795BEBFB'
euiccCiPKIdListForSigning '81370F5125D0B1D408D4C3B232E6D25E795BEBFB'
forbiddenProfilePolicyRules pprUpdateControl, ppr1
ppVersion 0.0.1
sasAcreditationNumber GE-NG-UP-0621

GSMA SGP.22 v2.4では、rspCapabilityの各ビットについて下記のように説明されています。additionalProfileとtestProfileSupportに加えて(6)の位置の機能をサポートしているようですが、これを書いている時点で最新のv2.4仕様書にはその説明がまだありません。

RspCapability ::= BIT STRING {
   additionalProfile(0), -- at least one more Profile can be installed
   crlSupport(1), -- CRL
   rpmSupport(2), -- Remote Profile Management
   testProfileSupport (3), -- support for test profile
   deviceInfoExtensibilitySupport (4), -- support for ASN.1 extensibility in the Device Info
   serviceSpecificDataSupport (5) -- support for Service Specific Data in the Profile Metadata
}

もうひとつは、sasAcreditationNumberとして表示されている"GE-NG-UP-0621"です。"GD-NG-UP-0621"ならSAS-UP認証済み施設として検索にも引っかかってくるのですが、これは誤記なんでしょうか。

https://www.gsma.com/security/wp-content/uploads/2019/06/GSMA-SAS_UP-GieseckeDevrient-Nanchang-China-0621.pdf

参考までに、ドコモeSIMカード(Ver.1)の同情報も再掲します。こちらは証明書が残念でしたね。

profileVersion 2.0.0
svn 2.0.0
uiccCapability BIT STRING '067F36C0'
ts102241Version 9.2.0
globalplatformVersion 2.2.1
rspCapability BIT STRING '0490'
euiccCiPKIdListForVerification '137BD996CB67F5AAB8EDBD9850D651AACB952F0F'
euiccCiPKIdListForSigning '137BD996CB67F5AAB8EDBD9850D651AACB952F0F'
forbiddenProfilePolicyRules pprUpdateControl, ppr1
ppVersion 0.0.1
sasAcreditationNumber GD-NG-0617

eSIM.me は商用かつ汎用の Removable eUICC カード

抜き差しできる eUICC カード、あります

世の中の SIM/UICC 好きの皆さま、取り外し可能なカード型の eUICC をお探しではないですか?先日ついにこの eSIM.me を見つけて、脊髄反射でポチっとやりました。ご覧ください、こちらが eSIM.me の eUICC カードです。1FF の半分のサイズのカードが、2FF/3FF/4FF 用にマルチカットされています。

f:id:cheerio-the-bear:20220227230846j:plain
eSIM.me eUICC カード

ソフトバンクも同様のカードを三月中旬以降に提供とのことですが、対象機種が AQUOS R6 と BALMUDA Phone では手を出しづらいです。Android の eUICC フレームワークに準拠したかたちで LPA を搭載するのではと思っていますが、意外とこの eSIM.me と同じソリューションだったりするのかもです。

k-tai.watch.impress.co.jp

これで、趣味の eSIM が(何それ)捗ります。

eSIM.me とは

SIM カードスロットを有する Android 端末を、eSIM 対応端末にすることができる商品です。eSIM.me より届けられる eUICC カードを Android 端末に装着し、eUICC を制御する LPA の役割を担う eSIM.me アプリを Google Play ストア経由でインストールすれば、セットアップ完了です。同アプリを使って QR コードをスキャンする等すれば、eSIM をダウンロード・インストールすることができるようになります。

f:id:cheerio-the-bear:20220227230236p:plain
CARD. APP. GO!

SIM カードスロットが eUICC に置き換わるようなものなので、カードスロットが 2 つあるデュアル SIM 端末に eSIM.me カードを 2 枚させば、両方のスロットを eSIM に対応させることが可能です。また、近年の Google Pixel 端末のように元々 eSIM に対応している端末においても、SIM カードスロットにこの eSIM.me カードを装着して利用することができます。

こちらの eSIM.me サイトからオーダーし、およそ 10 日で自宅に届きました。

esim.me

何らかの方法で(アカウント情報と EID を使ってとか?)使用可能なメーカーや機種の縛りをかけているようで、商品の種類は不自然な程に多いです。今回は、最も汎用的に使用可能な "eSIM.me Card for Android" を選択しています。予備としてもう一枚購入しておくかどうか迷います。だって、このビジネスを何年も続けてくれそうになくないですか?

IIJ の eSIM のダウンロードに成功した

以前 Android GSI をフラッシュした Google Pixel 4 がそのままほこりを被っていたので、それに eSIM.me アプリをプッシュして動作を確認しました。アクティベーションコードの入力については、QR コードのスキャンとマニュアル入力に対応しています。SM-DS は .. どうでしょうね。SM-DS で運用している eSIM 事業者があれば試してみたいところです。

f:id:cheerio-the-bear:20220302110259p:plainf:id:cheerio-the-bear:20220302110304p:plain
アプリ起動からダウンロード前まで

ダウンロード中の表示がテストアプリ・サンプルアプリのそれのようで少し気になりますが、IIJ の eSIM を問題なくダウンロード・インストールできました。eSIM の ON/OFF 制御についても特に問題はないようです。

f:id:cheerio-the-bear:20220302110317p:plainf:id:cheerio-the-bear:20220302110352p:plain
ダウンロード中からインストール完了まで

楽天モバイルの eSIM のダウンロードに失敗した (2022/04/13 追記)

一方、楽天モバイルの eSIM のダウンロードには失敗しています。プロファイルパッケージデータの端末へのダウンロード自体は滞りなく完了していたようですので、実際のところは「インストールに失敗した」という表現が正しそうです。

f:id:cheerio-the-bear:20220302110333p:plain
インストール失敗

eSIM.me の eUICC カードから返ってきたエラー情報がそのまま表示されているようで、原因は installFailedDueToPEProcessingError (12) とされています。楽天モバイルのプロファイルパッケージと eSIM.me の eUICC カードの間で、何らかの互換性の問題、仕様解釈の不一致のようなものが起きていることが予想されます。eSIM.me のサイト上でチケットを作成し、このエラーの発生について eSIM.me にレポート済みです。

2022/04/13 追記 : 次のバージョンのファームウェアで、この問題は解決されるそうです。市場投入は本年六月前後になるだろう、とのことでした。

Android OMAPI を利用して eUICC カードにアクセス

eUICC フレームワークに沿うかたちで LPA を実装することを Android は想定していると思いますが、eSIM.me アプリ (= LPA) は Android OMAPI (Secure Element API) を通して UICC SE (この場合 eUICC カード) へのアクセスを実現しています。下記のログで、ISD-R の AID (a0000005591010ffffffff8900000100) を指定して論理チャネルを開いている様子を確認できます。

I/esim.me ( 5121): AndroidOmapiApi. Check isReaderAvailable : SIM1
I/SecureElement-Terminal-SIM1( 2133): ATR : 3b9f96803fc7828031e073fe211b57aa8660f0010004fb
I/SecureElementService( 2133): openLogicalChannel() AID = a0000005591010ffffffff8900000100, P2 = 0
W/SecureElement-Terminal-SIM1( 2133): Enable access control on logical channel for esim.me
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81cadf2000
I/SecureElement-Terminal-SIM1( 2133): Received : df20082618f36467f9a02d9000
I/SecureElement-AraController( 2133): Refresh tag unchanged. Using access rules from cache.
I/SecureElement-AccessControlEnforcer( 2133): getAccessRule() appCert = 8d48ecfaf44c5752145ee28b3eb7429cc6627e98
I/SecureElement-AccessControlEnforcer( 2133): getAccessRule() appCert = a2afbbf5681bb26e40c8d69da6c72dd3a62cda7dbd74c5c2f26e7ff1fa819905
I/SecureElement-AccessRuleCache( 2133): findAccessRule() Case C REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2133):  [mPackageName=esim.me, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElementService( 2133): openLogicalChannel() Success. Channel: 1

許可されていないアプリからのアクセス要求には Android OMAPI が拒否の応答を行うはずですが、なるほど eSIM.me アプリ(を含む)からのアクセス要求を受け付けるようにアクセスルールが設定されています。下記のログから、eSIM.me の ARA-M に設定されているアクセスルールの詳細を確認することができます。上のログにあった eSIM.me アプリの署名のハッシュ値 (SHA1 : 8d48ecfaf44c5752145ee28b3eb7429cc6627e98) が、ふたつ目のルールで全許可 (AID_REF_DO: 4f00) に設定されていますね。

I/SecureElement-Terminal-SIM1( 2108): Sent : 81caff4000
I/SecureElement-Terminal-SIM1( 2108): Received : ff4081b4e222e1184f00c114a4ec39717cecd7f1e84e913b22b0555fe7dfdd8ae306d00101d10101e222e1184f00c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98e306d00101d10101e222e1184f00c11446bcaf2247253f695f19d60eece158099fe52c62e306d00101d10101e222e1184f00c114fa4abb42827a0fbceb891ddc0d34f658c21ff88ce306d00101d10101e222e1184f00c11435cc639bb5826fde0d0ef088b0f20cb69f0c49ade306d00101d101019000
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c114a4ec39717cecd7f1e84e913b22b0555fe7dfdd8a , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c11446bcaf2247253f695f19d60eece158099fe52c62 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c114fa4abb42827a0fbceb891ddc0d34f658c21ff88c , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c11435cc639bb5826fde0d0ef088b0f20cb69f0c49ad , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]

デフォルトファイルシステム

eSIM をダウンロードする前の eUICC でも、必要最低限のファイルシステムを端末側に見せる必要があります。GSMA SGP.22 の規定では MF と極少数の EF を設ければ良いことになっていますが、eSIM.me カードのデフォルトファイルシステムには DIR や ICCID、IMSI、MSISDN 等も存在します。デフォルトファイルシステムというか、それをひとつのデフォルトプロファイルとして実装しているようです。空の状態でプロファイルのリストを要求すると、以下のログのようにプロファイルがひとつ返ってきます。

I/esim.me ( 5121): AndroidOmapiApi. execute APDU command :81E2910003BF2D00
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81e2910003bf2d00
I/SecureElement-Terminal-SIM1( 2133): Received : 613e
I/SecureElement-Terminal-SIM1( 2133): Sent : 81c000003e
I/SecureElement-Terminal-SIM1( 2133): Received : bf2d3ba039e3375a0aXXXXXXXXXXXXXXXXXXXX4f10a0000005591010ffffffff89000012009f70010191076553494d2e6d6592076553494d2e6d659501019000
I/esim.me ( 5121): AndroidOmapiApi. received APDU response :BF2D3BA039E3375A0AXXXXXXXXXXXXXXXXXXXX4F10A0000005591010FFFFFFFF89000012009F70010191076553494D2E6D6592076553494D2E6D659501019000
I/esim.me ( 5121): Profile list object: profileInfoListOk: {
I/esim.me ( 5121):           {
I/esim.me ( 5121):                   iccid: XXXXXXXXXXXXXXXXXXXX,
I/esim.me ( 5121):                   isdpAid: A0000005591010FFFFFFFF8900001200,
I/esim.me ( 5121):                   profileState: 1,
I/esim.me ( 5121):                   serviceProviderName: eSIM.me,
I/esim.me ( 5121):                   profileName: eSIM.me,
I/esim.me ( 5121):                   profileClass: 1
I/esim.me ( 5121):           }
I/esim.me ( 5121):   }

MCC/MNC のペアは 262/24 でした。

eUICC カードに関する情報

参考までに、EuiccInfo2 を読みだしているログもここに貼ります。

I/esim.me ( 5121): AndroidOmapiApi. execute APDU command :81E2910003BF2200
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81e2910003bf2200
I/SecureElement-Terminal-SIM1( 2133): Received : 617b
I/SecureElement-Terminal-SIM1( 2133): Sent : 81c000007b
I/SecureElement-Terminal-SIM1( 2133): Received : bf2278810302010282030202008303040200840d81010082040007701e830239b68503017f3a8603090200870302030088020490a916041481370f5125d0b1d408d4c3b232e6d25e795bebfbaa16041481370f5125d0b1d408d4c3b232e6d25e795bebfb8b010004030100000c0d45442d5a492d55502d303832329000
I/esim.me ( 5121): AndroidOmapiApi. received APDU response :BF2278810302010282030202008303040200840D81010082040007701E830239B68503017F3A8603090200870302030088020490A916041481370F5125D0B1D408D4C3B232E6D25E795BEBFBAA16041481370F5125D0B1D408D4C3B232E6D25E795BEBFB8B010004030100000C0D45442D5A492D55502D303832329000
I/esim.me ( 5121):  - EUICC info 2 is: {
I/esim.me ( 5121):   profileVersion: 020102,
I/esim.me ( 5121):   svn: 020200,
I/esim.me ( 5121):   euiccFirmwareVer: 040200,
I/esim.me ( 5121):   extCardResource: 81010082040007701E830239B6,
I/esim.me ( 5121):   uiccCapability: 011111110011101,
I/esim.me ( 5121):   javacardVersion: 090200,
I/esim.me ( 5121):   globalplatformVersion: 020300,
I/esim.me ( 5121):   rspCapability: 1001,
I/esim.me ( 5121):   euiccCiPKIdListForVerification: {
I/esim.me ( 5121):           81370F5125D0B1D408D4C3B232E6D25E795BEBFB
I/esim.me ( 5121):   },
I/esim.me ( 5121):   euiccCiPKIdListForSigning: {
I/esim.me ( 5121):           81370F5125D0B1D408D4C3B232E6D25E795BEBFB
I/esim.me ( 5121):   },
I/esim.me ( 5121):   euiccCategory: 0,
I/esim.me ( 5121):   ppVersion: 010000,
I/esim.me ( 5121):   sasAcreditationNumber: ED-ZI-UP-0822
I/esim.me ( 5121): }

SAS-UP の認定番号が ED-ZI-UP-0822 とのことで、中国の "Eastcompeace" というスマートカードベンダによって製造されているカードのようです。

https://www.gsma.com/security/wp-content/uploads/2021/02/GSMA-SAS_UP-Certificate-Eastcompeace-Zhuhai-China-0822.pdf

iPhone 13のデュアルeSIMはもっと取り上げられてよい

ネットメディアの反応

iPhone 13では新たに「デュアルeSIM」がサポートされ、eSIMだけで(物理的なSIMカードを装着することなく)2回線を同時に利用できるようになりました。思ったよりも数は多くないという印象ですが、ネットメディアの記事でも「デュアルeSIM」が取り上げられ、概ね好意的に受け入れられているようです。日本でも大手の通信事業者からeSIMが提供されるようになりましたし、SIMカードからeSIMへの移行をほんの少し加速させるのではないでしょうか。

japanese.engadget.com

残念なのは、eSIMプロファイルを2つだけダウンロード・インストールして満足している記事が多く見受けられることです。「デュアルeSIM」なんだから、eSIMだけで2回線を同時に利用できることを確認すれば良いだろう。そう考えてのことだと思われますが、iPhone 13が実現した「デュアルeSIM」の注目すべきポイントは、(マニアの観点では)そこではありません。

今回Appleは、GSMA SGP.21で規定されている下記の要件によるプロファイル数の上限設定を、何らかの方法を使って飛び越えています。

EUICC6 At a maximum, only one Profile SHALL be enabled at any point in time.

「有効なプロファイルはひとつまで」の制限からの解放

検証のため、下記の4種類のeSIMプロファイルを契約してiPhone 13にダウンロードしてみました。ダウンロード手続きそのものに特筆すべきものは無く、また「どちらのeUICCにダウンロードしますか?」といったようなユーザーへの確認を求められることもありませんでした。「デュアルeSIM」だからといって、iPhone 13がeUICCを複数持っているとは考えにくいです。

この4種類のeSIMプロファイルの中から「デュアルeSIM」に利用する2つを選択する場合、その組み合わせは6通りあります。実際にその6通りを試してみましたが、下のスクリーンショットの在圏表示を見てわかるように、全ての組み合わせを問題なく選択することが可能です。試せてはいないですが、プロファイルの数を更に増やしたとしても、同じ結果になることが予想されます。

f:id:cheerio-the-bear:20211005232136p:plain
Rakuten - IIJ
f:id:cheerio-the-bear:20211005232142p:plain
Rakuten - Softbank
f:id:cheerio-the-bear:20211005232146p:plain
Rakuten - Ubigi
f:id:cheerio-the-bear:20211005232106p:plain
IIJ - Softbank
f:id:cheerio-the-bear:20211005232132p:plain
IIJ - Ubigi
f:id:cheerio-the-bear:20211005232152p:plain
Ubigi - Softbank

eUICCひとつあたり「同時に有効化できるプロファイルの数はひとつまで」という制限に縛られていては、(もしeUICCを4つ載せているならその限りではありませんが)これを実現することはできません。例えば、RakutenとIIJのプロファイルが同一のeUICCにダウンロードされている場合、従来の(というか現行の通常の)eUICCではその2つを同時に使用することはできません。「デュアルeSIM」だからといって、eUICCを2つ搭載すれば良いというわけではないということです。

GSMA SGP.21の現行仕様では実現できない機能を実装し、業界に先駆けてそれを商用化しているものと思われます。SIMカードレスに向かう流れはきっと進行してゆくことでしょうし、さすがはAppleといった感じです。

ドコモeSIMカードVer.1にはがっかりです

ドコモeSIMカードに他キャリアのeSIMプロファイルもダウンロードできたらいいな、なんて思っちゃいますよね。ドコモeSIMカード対応製品はここ三年近く発売されていないので、ちょっと古くなってしまったタブレットdtab Compact d-02Kを調達して確認することにしました。

こちらが、ドコモeSIMカードの表面・裏面の写真です。表面には、32桁のEIDがびっしりとプリントされています。

f:id:cheerio-the-bear:20210418222533j:plainf:id:cheerio-the-bear:20210418222527j:plain
ドコモeSIMカード

もしこのカードを使って楽天モバイルIIJのeSIMプロファイルをダウンロードできるなら、少し前に下記のエントリで書いたようにGoogle Pixelに実装されているeSIMを無理やり使う必要はなくなります。しかし、残念ながらそんなうまい話はないようです。

cheerio-the-bear.hatenablog.com

ドコモeSIMカードからEUICCInfo1やEUICCInfo2を取得してみると、CI Public Key IdentifiersがGSMAのルートCIの証明書に対応するデータを含んでいません。そのEUICCInfo1を使ってドコモ以外のキャリアのSM-DP+にES9+: InitiateAuthenticationを送信すると、下記のようにエラーが返ってきます。これでは、eSIMプロファイルを提供するキャリアをユーザーが選択することができず、解除することが許されないSIMロックを設定されているようなものです。

{
  "header" : {
    "functionExecutionStatus" : {
      "status" : "Failed",
      "statusCodeData" : {
        "subjectCode" : "8.8.2",
        "reasonCode" : "3.1",
        "message" : "None of the proposed Public Key Identifiers is supported by the SM-DP+"
      }
    }
  }
}

ドコモeSIMカードに設定されているデフォルトSM-DP+はというと、GSMAのルートCIの証明書を求めてはいないようです。

f:id:cheerio-the-bear:20210418223903p:plain
https://usmdp-001-prod-muc1-eb.venyonservices.com/

うまくいくと思って調べ始めたわけではないですが、がっかりです。GSMAのルートCI証明書に対応したeSIMカードは、商用製品としては世の中に出回っていないんでしょうか。eSIMカード探しの旅は、しばらく終わりそうにありません。

Function(ES10a): GetEuiccConfiguredAddresses (BF3C)

デフォルトSM-DP+アドレスが入っていますね。プロファイルのダウンロード手続きの際にユーザーに入力させるのはEIDの一部だけですので、SM-DP+のアドレスはこれから得ているんでしょう。

defaultDpAddress usmdp-001-prod-muc1-eb.venyonservices.com
rootDsAddress (空)

Function (ES10b): GetEUICCInfo (EUICCInfo1 - BF20)

数年前に発売された製品に同梱されていたドコモeSIMカードですので、GSMA SGP.22のバージョンはv2.0と少し前のものになります。Google Pixel等に実装されているeSIMとは異なり、CI Public Key IdentifiersがGSMAルート証明書のものを含んでいないようです。

svn 020000
euiccCiPKIdListForVerification 137BD996CB67F5AAB8EDBD9850D651AACB952F0F
euiccCiPKIdListForSigning 137BD996CB67F5AAB8EDBD9850D651AACB952F0F

Function (ES10b): GetEUICCInfo (EUICCInfo2 - BF22)

sasAcreditationNumberはGD-NG-0617ですので、Giesecke+Devrient社の中国南昌市で作られたものだということになりますか。

profileVersion 020000
svn 020000
euiccFirmwareVer 040200
extCardResource 810100820400077D72830400000FC6
uiccCapability 067F36C0 (No contactlessSupport/eapClient)
ts102241Version 090200
globalplatformVersion 020201
rspCapability 0490 (testProfileSupport + additionalProfile)
euiccCiPKIdListForVerification 137BD996CB67F5AAB8EDBD9850D651AACB952F0F
euiccCiPKIdListForSigning 137BD996CB67F5AAB8EDBD9850D651AACB952F0F
forbiddenProfilePolicyRules 06C0 (ppr1 + pprUpdateControl)
ppVersion 000001
sasAcreditationNumber GD-NG-0617

Function (ES10c): GetProfilesInfo (BF2D)

プロビジョニングプロファイルが入っていたので、その情報の一部をご紹介します。アイコンもPNG形式で入っているようですので、いつかまた時間のあるときに読んでみようと思います。

serviceProviderName NTT DOCOMO
profileName NTT DOCOMO PP
iconType 1 (png)
profileClass 1 (provisioning)
operatorId 440/10 (no gid1/gid2)

16 進文字列とバイト配列を変換する関数を C 言語で書く

下記エントリで Kotlin でやったやつを、C 言語でもやってみました。やってみましたというか、必要に迫られて手早く書きました。またいつか必要になりそうですので、ここに貼っておこうと思います。

cheerio-the-bear.hatenablog.com

C 言語もここのところずっと遠ざかっていましたので、まだ勘を取り戻すにはもう少し時間がかかりそうです。「あっ!unsigned char とかあったんだ!」みたいな感じになってます。慣れてくると、もっとスマートに書けたりするんでしょうねぇ。

16進数の文字列からバイト配列への変換

unsigned char *util_hex_string_to_byte_array(
    const char *string,
    size_t length
)
{
    unsigned char *array;
    int index;
    unsigned char octet;
    char nibble;

    if ((array = (unsigned char *) malloc(length / 2)) == NULL) {
        return NULL;
    }

    for (index = 0; index < length; index++) {
        nibble = string[index];

        if (('0' <= nibble) && (nibble <= '9')) {
            nibble = nibble - '0';
        } else if (('a' <= nibble) && (nibble <= 'f')) {
            nibble = 0x0A + (nibble - 'a');
        } else if (('A' <= nibble) && (nibble <= 'F')) {
            nibble = 0x0A + (nibble - 'A');
        } else {
            free(array);
            return NULL;
        }

        if ((index % 2) == 0) {
            octet = nibble;
        } else {
            array[index / 2] = (octet << 4) + nibble;
        }
    }

    return array;
}

バイト配列から16進数の文字列への変換

static const char *UTIL_CONVERT_TO_HEX_CHAR = "0123456789ABCDEF";

char *util_byte_array_to_hex_string(
    const unsigned char *array,
    size_t length
)
{
    char *string;
    char *destination;
    int index;
    unsigned char octet;

    if (length == 0) {
        return NULL;
    }

    if ((string = destination = (char *) malloc(length * 2 + 1)) == NULL) {
        return NULL;
    }

    for (index = 0; index < length; index++) {
        octet = array[index];
        *destination++ = UTIL_CONVERT_TO_HEX_CHAR[octet / 0x10];
        *destination++ = UTIL_CONVERT_TO_HEX_CHAR[octet % 0x10];
    }

    *destination = '\0';
    return string;
}

使い方

それぞれ、下記のコード例のように使用します。どちらの関数でも内部で malloc() したヒープメモリを返しますので、使い終わったら使用者側で free() を呼んで解放する必要があります。

    const char *data = "00017F8081FEFF";
    unsigned char *array;
    char *string;

    array = util_hex_string_to_byte_array(data, strlen(data));
    assert(array[0] == 0x00);
    assert(array[1] == 0x01);
    assert(array[2] == 0x7F);
    assert(array[3] == 0x80);
    assert(array[4] == 0x81);
    assert(array[5] == 0xFE);
    assert(array[6] == 0xFF);

    string = util_byte_array_to_hex_string(array, strlen(data) / 2);
    assert(strcmp(data, string) == 0);

    free(array);
    free(string);

github.com