SM-DP+ (IIJ) で学ぶ OpenSSL
先日、Pixel 4 の eSIM に STORE DATA コマンドを送信し、eSIM に入っていたプロファイルの情報を読み出しました。eSIM には IIJ のプロファイルが入っていますので、同プロファイルに関する Notification の送信先アドレスも得ることができました。プロファイル情報の読み出しについては、下記のエントリをご覧ください。
cheerio-the-bear.hatenablog.com
今回はその (おそらく SM-DP+ の) アドレスを使い、OpenSSL について少し学んでみました。ここのところ担当している業務でこれ系のコードを書くことは滅多にないので、良いエクササイズになりました。
OpenSSL で接続してみる : openssl s_client
まずは、SM-DP+ の FQDN をそのまま指定して openssl s_client を実行してみます。SM-DP+ から受け取った証明書の検証結果は、下記のように X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN (19) になります。
$ openssl s_client -connect SM-V4-033-A-GTM.PR.GO-ESIM.COM:443 ... Start Time: 1612622663 Timeout : 7200 (sec) Verify return code: 19 (self signed certificate in certificate chain) Extended master secret: no
GSMA ルート CI の証明書を指定していないので、サーバー証明書の検証結果が X509_V_OK (0) にならないのは想定通りです。GSMA の下記サイトから、SGP.21 と SGP.22 に対応する方のルート CI 証明書をダウンロードして指定します。
結果は下記の通り、今度はサーバー証明書の検証結果が X509_V_OK (0) になりました。
$ openssl s_client -connect SM-V4-033-A-GTM.PR.GO-ESIM.COM:443 -CAfile ./Symantec_GSMA_RSPv2-Root-CI1.pem ... Start Time: 1612622673 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no
OpenSSL で接続してみる : 自作
次に、上記のコマンドと同じようなことをする小さな実行ファイルを、OpenSSL を使って久しぶりに C 言語で書いて作ってみました。引数で受け取った PEM ファイルを SSL_CTX_load_verify_locations() で指定して接続し、SSL_get_peer_certificate() と SSL_get_verify_result() を使ってサーバー証明書を検証します。あっ!コミットメッセージに typo ある ..
GSMA のルート CI 証明書を指定しない場合、サーバー証明書の検証結果は下記のように X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN (19) になります。IIJ の SM-DP+ でも楽天モバイルの SM-DP+ でも、結果は同じです。
$ ./sample -n SM-V4-033-A-GTM.PR.GO-ESIM.COM Verify Result: 19 $ ./sample -n rakuten.prod.ondemandconnectivity.com Verify Result: 19
GSMA のルート CI 証明書を指定すれば、X509_V_OK (0) になりました。それなりに期待した通りに動いてくれたようです。
$ ./sample -n SM-V4-033-A-GTM.PR.GO-ESIM.COM -c ./Symantec_GSMA_RSPv2-Root-CI1.pem Verify Result: 0 $ ./sample -n rakuten.prod.ondemandconnectivity.com -c ./Symantec_GSMA_RSPv2-Root-CI1.pem Verify Result: 0
eSIM が無ければ Pixel 4 を潰せばいいじゃない
リムーバブルな eSIM が手に入らない
eSIM にコマンドを送りたくなること、ありますよね。4FF/nano カードタイプのいわゆるリムーバブルな eSIM を入手し、PC/SC インターフェースを通して ES10x のコマンドを送ることができればと考えたんですけど、そもそもそんな eSIM を簡単に入手できそうにありません。
SGP.26 のテスト証明書を入れた商品は見つけましたが、それでは今後やれることが限られてしまいそうです。海外の eSIM 事業者さんにもお尋ねしてみましたが、門前払いみたいな感じでした。まぁ、そりゃそうでしょう。
リムーバブルじゃなくてもいいか(諦めた)
全く eSIM が手に入らないのかというと、そういうわけではありません。リムーバブルなカードタイプではなく、更には既に商品に組み込まれてしまっているだけのことです。所望のものが手に入らないのはもう仕方がないということで、Pixel 4 に組み込まれている eSIM を利用することにしました。
我ながら適当なワークアラウンドを考えたものです。adb shell content コマンドを使って ES10x コマンド部を Android 端末に渡し、端末内に追加する Content Provider を通して eSIM に送り込みます。アプリから見れば、ES10x コマンドの送信をお願いした先の仕組みが違うだけのことで、何ら不都合はありません。不都合として何か挙げるとするならば、Pixel 4 をルート化しないといけないことくらいでしょうか。
今回作成した Content Provider はこちら
クライアントから ES10x コマンドを受け取り、APDU ヘッダを付けて STORE DATA コマンドにして、テレフォニー API を使って eSIM に送りこむだけの Content Provider です。論理チャネルのオープン・クローズや ISD-R の選択もやりますが、どうあれ大した処理ではないのでコード量は極小です。
ProfileInfoListRequest 'BF2D' の実行例をご紹介します。Content URI の最後のスラッシュ以降が ES10b コマンド部で、そこで指定したコマンドデータが STORE DATA コマンドに載せられて eSIM まで運ばれます。要求するデータオブジェクトは profileState '9F70' と profileName '92'、profileClass '95'、notificationConfigurationInfo 'B6' の四つ。
$ adb shell content query --uri content://com.github.cheeriotb.isdrap.provider/store/BF2D075C059F709295B6
Row: 0 response=BF2D7DA07BE31D9F7001009214416E726974737520546573742050726F66696C65950100E35A9F700100920349494A950102B64C302480020410811E534D2D56342D3033332D412D47544D2E50522E474F2D4553494D2E434F4D302480020780811E534D2D56342D3033332D412D47544D2E50522E474F2D4553494D2E434F4D9000
Android 端末からは、ProfileInfoListResponse 'BF2D' とステータスワード '9000' が返っています。テストプロファイルがひとつ、そして IIJ の商用プロファイルがひとつ入っていることが読み取れます。
ProfileInfo | profileState | disabled (0) | |
---|---|---|---|
profileName | Anritsu Test Profile | ||
profileClass | test (0) | ||
ProfileInfo | profileState | disabled (0) | |
profileName | IIJ | ||
profileClass | operational (2) | ||
notificationConfigurationInfo | profileManagementOperation | notificationDelete (3) | |
notificationAddress | SM-V4-033-A-GTM.PR.GO-ESIM.COM | ||
notificationConfigurationInfo | profileManagementOperation | notificationInstall (0) | |
notificationAddress | SM-V4-033-A-GTM.PR.GO-ESIM.COM |
端末から返ってくるデータサイズが大きい場合の動作をまだ確認できていないので、うまく動かないケースがあれば都度修正しようと思います。また、現状は Pixel 端末を想定したコードになっていますが、ほんの少し書き換えるだけで Rakuten Mini 等にも転用できるはずです。
久しぶりにリフレクションしました
今回利用した API の中には @hide なものも含まれていたので、久しぶりにリフレクションを使いました。Kotlin だと、他にもっと綺麗に書く方法もあるんでしょうか。
transmit = TelephonyManager::class.java.getDeclaredMethod( "iccTransmitApduLogicalChannelBySlot", Int::class.java, Int::class.java, Int::class.java, Int::class.java, Int::class.java, Int::class.java, Int::class.java, String::class.java) val tm = context!!.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager? ?: throw UnsupportedOperationException("Telephony Manager is unavailable") transmitRsp = Response(transmit.invoke(tm, PHYSICAL_SLOT_NUM, openRsp.channel, STORE_DATA_CLA, STORE_DATA_INS, p1, seqNumber, block.length / 2, block) as String?)
あと、サービスだけどうして Java で書いちゃったのかは謎です。適当に試してみたときに、勝手に手がそう動いちゃったんだと思います。
apt-get install build-essential で Media change: please insert the disc labeled
下記コマンドで Ubuntu 20.04 LTS に "build-essential" をインストールしようとしたら、CD-ROM の挿入を求められました。
$ sudo apt-get update $ sudo apt-get install build-essential
このメッセージです。
Media change: please insert the disc labeled 'Ubuntu 20.04.01 LTS _Focal Fossa_ - Release amd64 (20200731)’ in the drive '/cdrom/' and press [Enter]
下記 FAQ のコメント欄に書かれているように、/etc/apt/sources.list の一行目の先頭に # を入れてコメントアウトすることで解決しました。
エディタはなんでもいいんですけど、
$ sudo gedit /etc/apt/sources.list
一行目をこうです。
久しぶりにデスクトップ PC を組み立てたので、各種設定を少しずつ進めているところです。
SM-DP+ で学ぶ TLS ハンドシェイク (楽天モバイル)
Rakuten Mini に tcpdump を入れて、楽天モバイルの SM-DP+ と通信する際の TLS ハンドシェイクの様子を見てみました。SM-DP+ のアドレスが "rakuten.prod.ondemandconnectivity.com" になっているので、Thales (Gemalto) のサービスを利用されているのでしょうか。なるほど、確かにそういうソリューションはアリだと思います。
@startuml "SM-DP+" <- LPA: Client Hello (rakuten.prod.ondemandconnectivity.com) "SM-DP+" -> LPA: Server Hello (TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) "SM-DP+" -> LPA: Certificate (*.prod.ondemandconnectivity.com, GSM Association - RSP2 Root CI1) "SM-DP+" -> LPA: Server Key Exchange "SM-DP+" -> LPA: Server Hello Done "SM-DP+" <- LPA: Client Key Exchange "SM-DP+" <- LPA: Change Cipher Spec "SM-DP+" <- LPA: Finished "SM-DP+" -> LPA: Change Cipher Spec "SM-DP+" -> LPA: Finished @enduml
ハンドシェイク中に流れていたメッセージとパラメータは、資料としてここに貼っておきます。
(SM-DP+ < LPA) Client Hello
Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.0 (0x0301) Length: 198 Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Length: 194 Version: TLS 1.2 (0x0303) Random: aaae079065c30cd5523bb8e3de7f8b2b5d142623b93e66d53867f07945f00146 GMT Unix Time: Sep 28, 2060 02:32:00.000000000 東京 (標準時) Random Bytes: 65c30cd5523bb8e3de7f8b2b5d142623b93e66d53867f07945f00146 Session ID Length: 0 Cipher Suites Length: 28 Cipher Suites (14 suites) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c) Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d) Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) Compression Methods Length: 1 Compression Methods (1 method) Compression Method: null (0) Extensions Length: 125 Extension: renegotiation_info (len=1) Type: renegotiation_info (65281) Length: 1 Renegotiation Info extension Renegotiation info extension length: 0 Extension: server_name (len=42) Type: server_name (0) Length: 42 Server Name Indication extension Server Name list length: 40 Server Name Type: host_name (0) Server Name length: 37 Server Name: rakuten.prod.ondemandconnectivity.com Extension: extended_master_secret (len=0) Type: extended_master_secret (23) Length: 0 Extension: session_ticket (len=0) Type: session_ticket (35) Length: 0 Data (0 bytes) Extension: signature_algorithms (len=20) Type: signature_algorithms (13) Length: 20 Signature Hash Algorithms Length: 18 Signature Hash Algorithms (9 algorithms) Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: rsa_pss_rsae_sha256 (0x0804) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (4) Signature Algorithm: rsa_pkcs1_sha256 (0x0401) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503) Signature Hash Algorithm Hash: SHA384 (5) Signature Hash Algorithm Signature: ECDSA (3) Signature Algorithm: rsa_pss_rsae_sha384 (0x0805) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (5) Signature Algorithm: rsa_pkcs1_sha384 (0x0501) Signature Hash Algorithm Hash: SHA384 (5) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: rsa_pss_rsae_sha512 (0x0806) Signature Hash Algorithm Hash: Unknown (8) Signature Hash Algorithm Signature: Unknown (6) Signature Algorithm: rsa_pkcs1_sha512 (0x0601) Signature Hash Algorithm Hash: SHA512 (6) Signature Hash Algorithm Signature: RSA (1) Signature Algorithm: rsa_pkcs1_sha1 (0x0201) Signature Hash Algorithm Hash: SHA1 (2) Signature Hash Algorithm Signature: RSA (1) Extension: status_request (len=5) Type: status_request (5) Length: 5 Certificate Status Type: OCSP (1) Responder ID list Length: 0 Request Extensions Length: 0 Extension: application_layer_protocol_negotiation (len=11) Type: application_layer_protocol_negotiation (16) Length: 11 ALPN Extension Length: 9 ALPN Protocol ALPN string length: 8 ALPN Next Protocol: http/1.1 Extension: ec_point_formats (len=2) Type: ec_point_formats (11) Length: 2 EC point formats Length: 1 Elliptic curves point formats (1) EC point format: uncompressed (0) Extension: supported_groups (len=8) Type: supported_groups (10) Length: 8 Supported Groups List Length: 6 Supported Groups (3 groups) Supported Group: x25519 (0x001d) Supported Group: secp256r1 (0x0017) Supported Group: secp384r1 (0x0018)
(SM-DP+ > LPA) Server Hello
Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Server Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 112 Handshake Protocol: Server Hello Handshake Type: Server Hello (2) Length: 108 Version: TLS 1.2 (0x0303) Random: ddfa9070ac4963c42f1ff9a24f22850c2c0ceff5b11936e6444f574e47524401 GMT Unix Time: Jan 6, 2088 09:01:52.000000000 東京 (標準時) Random Bytes: ac4963c42f1ff9a24f22850c2c0ceff5b11936e6444f574e47524401 Session ID Length: 32 Session ID: 1467e29c67b26cc0d2a711124f1f0a5430c05e88ce2d9b9294d4199780fd3652 Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Compression Method: null (0) Extensions Length: 36 Extension: renegotiation_info (len=1) Type: renegotiation_info (65281) Length: 1 Renegotiation Info extension Renegotiation info extension length: 0 Extension: server_name (len=0) Type: server_name (0) Length: 0 Extension: ec_point_formats (len=4) Type: ec_point_formats (11) Length: 4 EC point formats Length: 3 Elliptic curves point formats (3) EC point format: uncompressed (0) EC point format: ansiX962_compressed_prime (1) EC point format: ansiX962_compressed_char2 (2) Extension: application_layer_protocol_negotiation (len=11) Type: application_layer_protocol_negotiation (16) Length: 11 ALPN Extension Length: 9 ALPN Protocol ALPN string length: 8 ALPN Next Protocol: http/1.1 Extension: extended_master_secret (len=0) Type: extended_master_secret (23) Length: 0
(SM-DP+ > LPA) Certificate, Server Key Exchange, Server Hello Done
Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Certificate Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 1330 Handshake Protocol: Certificate Handshake Type: Certificate (11) Length: 1326 Certificates Length: 1323 Certificates (1323 bytes) Certificate Length: 728 Certificate: 308202d430820279a003020102021003485c90de04b1e87a12a507d3e768cd300a06082a… (id-at-commonName=*.prod.ondemandconnectivity.com,id-at-organizationName=GEMALTO SA,id-at-localityName=Tours,id-at-countryName=FR) signedCertificate version: v3 (2) serialNumber: 0x03485c90de04b1e87a12a507d3e768cd signature (ecdsa-with-SHA256) Algorithm Id: 1.2.840.10045.4.3.2 (ecdsa-with-SHA256) issuer: rdnSequence (0) rdnSequence: 2 items (id-at-commonName=GSM Association - RSP2 Root CI1,id-at-organizationName=GSM Association) RDNSequence item: 1 item (id-at-organizationName=GSM Association) RelativeDistinguishedName item (id-at-organizationName=GSM Association) Id: 2.5.4.10 (id-at-organizationName) DirectoryString: printableString (1) printableString: GSM Association RDNSequence item: 1 item (id-at-commonName=GSM Association - RSP2 Root CI1) RelativeDistinguishedName item (id-at-commonName=GSM Association - RSP2 Root CI1) Id: 2.5.4.3 (id-at-commonName) DirectoryString: printableString (1) printableString: GSM Association - RSP2 Root CI1 validity notBefore: utcTime (0) utcTime: 2020-05-22 00:00:00 (UTC) notAfter: utcTime (0) utcTime: 2022-08-24 23:59:59 (UTC) subject: rdnSequence (0) rdnSequence: 4 items (id-at-commonName=*.prod.ondemandconnectivity.com,id-at-organizationName=GEMALTO SA,id-at-localityName=Tours,id-at-countryName=FR) RDNSequence item: 1 item (id-at-countryName=FR) RelativeDistinguishedName item (id-at-countryName=FR) Id: 2.5.4.6 (id-at-countryName) CountryName: FR RDNSequence item: 1 item (id-at-localityName=Tours) RelativeDistinguishedName item (id-at-localityName=Tours) Id: 2.5.4.7 (id-at-localityName) DirectoryString: printableString (1) printableString: Tours RDNSequence item: 1 item (id-at-organizationName=GEMALTO SA) RelativeDistinguishedName item (id-at-organizationName=GEMALTO SA) Id: 2.5.4.10 (id-at-organizationName) DirectoryString: printableString (1) printableString: GEMALTO SA RDNSequence item: 1 item (id-at-commonName=*.prod.ondemandconnectivity.com) RelativeDistinguishedName item (id-at-commonName=*.prod.ondemandconnectivity.com) Id: 2.5.4.3 (id-at-commonName) DirectoryString: uTF8String (4) uTF8String: *.prod.ondemandconnectivity.com subjectPublicKeyInfo algorithm (id-ecPublicKey) Algorithm Id: 1.2.840.10045.2.1 (id-ecPublicKey) ECParameters: namedCurve (1) namedCurve: 1.2.840.10045.3.1.7 (secp256r1) Padding: 0 subjectPublicKey: 04b656ecce3584125a1e6d103b3ba786e042c1a99604d7338a46cb5eaf03c13a2efc93fd… extensions: 7 items Extension (id-ce-certificatePolicies) Extension Id: 2.5.29.32 (id-ce-certificatePolicies) CertificatePoliciesSyntax: 1 item PolicyInformation policyIdentifier: 2.23.146.1.2.1.3 (joint-iso-itu-t.23.146.1.2.1.3) Extension (id-ce-cRLDistributionPoints) Extension Id: 2.5.29.31 (id-ce-cRLDistributionPoints) CRLDistPointsSyntax: 1 item DistributionPoint distributionPoint: fullName (0) fullName: 1 item GeneralName: uniformResourceIdentifier (6) uniformResourceIdentifier: http://gsma-crl.symauth.com/offlineca/gsma-rsp2-root-ci1.crl Extension (id-ce-extKeyUsage) Extension Id: 2.5.29.37 (id-ce-extKeyUsage) critical: True KeyPurposeIDs: 2 items KeyPurposeId: 1.3.6.1.5.5.7.3.1 (id-kp-serverAuth) KeyPurposeId: 1.3.6.1.5.5.7.3.2 (id-kp-clientAuth) Extension (id-ce-keyUsage) Extension Id: 2.5.29.15 (id-ce-keyUsage) critical: True Padding: 7 KeyUsage: 80 1... .... = digitalSignature: True .0.. .... = contentCommitment: False ..0. .... = keyEncipherment: False ...0 .... = dataEncipherment: False .... 0... = keyAgreement: False .... .0.. = keyCertSign: False .... ..0. = cRLSign: False .... ...0 = encipherOnly: False 0... .... = decipherOnly: False Extension (id-ce-subjectAltName) Extension Id: 2.5.29.17 (id-ce-subjectAltName) GeneralNames: 3 items GeneralName: dNSName (2) dNSName: *.prod.ondemandconnectivity.com GeneralName: dNSName (2) dNSName: *.prod.ids-odc.gemalto.com GeneralName: registeredID (8) registeredID: 1.3.6.1.4.1.31746.1.220.100.101.2 (iso.3.6.1.4.1.31746.1.220.100.101.2) Extension (id-ce-subjectKeyIdentifier) Extension Id: 2.5.29.14 (id-ce-subjectKeyIdentifier) SubjectKeyIdentifier: 8efb03c8e210f825105a66bffe5e1a1927bcc76b Extension (id-ce-authorityKeyIdentifier) Extension Id: 2.5.29.35 (id-ce-authorityKeyIdentifier) AuthorityKeyIdentifier keyIdentifier: 81370f5125d0b1d408d4c3b232e6d25e795bebfb algorithmIdentifier (ecdsa-with-SHA256) Algorithm Id: 1.2.840.10045.4.3.2 (ecdsa-with-SHA256) Padding: 0 encrypted: 3046022100cc75a507eb5c94024aa51ffa4d7d31ed15fe044f477ad88f6cb26abb3e9a78… Certificate Length: 589 Certificate: 30820249308201efa00302010202106e68567a77a0ee7c85ee183963dfaa7a300a06082a… (id-at-commonName=GSM Association - RSP2 Root CI1,id-at-organizationName=GSM Association) signedCertificate version: v3 (2) serialNumber: 0x6e68567a77a0ee7c85ee183963dfaa7a signature (ecdsa-with-SHA256) Algorithm Id: 1.2.840.10045.4.3.2 (ecdsa-with-SHA256) issuer: rdnSequence (0) rdnSequence: 2 items (id-at-commonName=GSM Association - RSP2 Root CI1,id-at-organizationName=GSM Association) RDNSequence item: 1 item (id-at-organizationName=GSM Association) RelativeDistinguishedName item (id-at-organizationName=GSM Association) Id: 2.5.4.10 (id-at-organizationName) DirectoryString: printableString (1) printableString: GSM Association RDNSequence item: 1 item (id-at-commonName=GSM Association - RSP2 Root CI1) RelativeDistinguishedName item (id-at-commonName=GSM Association - RSP2 Root CI1) Id: 2.5.4.3 (id-at-commonName) DirectoryString: printableString (1) printableString: GSM Association - RSP2 Root CI1 validity notBefore: utcTime (0) utcTime: 2017-02-22 00:00:00 (UTC) notAfter: generalizedTime (1) generalizedTime: 2052-02-21 23:59:59 (UTC) subject: rdnSequence (0) rdnSequence: 2 items (id-at-commonName=GSM Association - RSP2 Root CI1,id-at-organizationName=GSM Association) RDNSequence item: 1 item (id-at-organizationName=GSM Association) RelativeDistinguishedName item (id-at-organizationName=GSM Association) Id: 2.5.4.10 (id-at-organizationName) DirectoryString: printableString (1) printableString: GSM Association RDNSequence item: 1 item (id-at-commonName=GSM Association - RSP2 Root CI1) RelativeDistinguishedName item (id-at-commonName=GSM Association - RSP2 Root CI1) Id: 2.5.4.3 (id-at-commonName) DirectoryString: printableString (1) printableString: GSM Association - RSP2 Root CI1 subjectPublicKeyInfo algorithm (id-ecPublicKey) Algorithm Id: 1.2.840.10045.2.1 (id-ecPublicKey) ECParameters: namedCurve (1) namedCurve: 1.2.840.10045.3.1.7 (secp256r1) Padding: 0 subjectPublicKey: 049d6abad2f41c2317e76189ebf8de89bb00a997d42d68ff5f5d29fcc8a7eac79937e85f… extensions: 6 items Extension (id-ce-keyUsage) Extension Id: 2.5.29.15 (id-ce-keyUsage) critical: True Padding: 1 KeyUsage: 06 0... .... = digitalSignature: False .0.. .... = contentCommitment: False ..0. .... = keyEncipherment: False ...0 .... = dataEncipherment: False .... 0... = keyAgreement: False .... .1.. = keyCertSign: True .... ..1. = cRLSign: True .... ...0 = encipherOnly: False 0... .... = decipherOnly: False Extension (id-ce-basicConstraints) Extension Id: 2.5.29.19 (id-ce-basicConstraints) critical: True BasicConstraintsSyntax cA: True Extension (id-ce-subjectAltName) Extension Id: 2.5.29.17 (id-ce-subjectAltName) GeneralNames: 1 item GeneralName: registeredID (8) registeredID: 1.3.6.1.4.1.46304 (iso.3.6.1.4.1.46304) Extension (id-ce-certificatePolicies) Extension Id: 2.5.29.32 (id-ce-certificatePolicies) critical: True CertificatePoliciesSyntax: 1 item PolicyInformation policyIdentifier: 2.23.146.1.2.1.0 (joint-iso-itu-t.23.146.1.2.1.0) Extension (id-ce-cRLDistributionPoints) Extension Id: 2.5.29.31 (id-ce-cRLDistributionPoints) CRLDistPointsSyntax: 1 item DistributionPoint distributionPoint: fullName (0) fullName: 1 item GeneralName: uniformResourceIdentifier (6) uniformResourceIdentifier: http://gsma-crl.symauth.com/offlineca/gsma-rsp2-root-ci1.crl Extension (id-ce-subjectKeyIdentifier) Extension Id: 2.5.29.14 (id-ce-subjectKeyIdentifier) SubjectKeyIdentifier: 81370f5125d0b1d408d4c3b232e6d25e795bebfb algorithmIdentifier (ecdsa-with-SHA256) Algorithm Id: 1.2.840.10045.4.3.2 (ecdsa-with-SHA256) Padding: 0 encrypted: 30450220209758b0e3055b388f2bb97c9e1e66bb4aa246255fdb9a1af6e9651bf388012c… Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 114 Handshake Protocol: Server Key Exchange Handshake Type: Server Key Exchange (12) Length: 110 EC Diffie-Hellman Server Params Curve Type: named_curve (0x03) Named Curve: x25519 (0x001d) Pubkey Length: 32 Pubkey: e77f9f2abe1566d1fdd3c195bff77a78e1dbca48676d3c55826c7739b2dabe5f Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: ECDSA (3) Signature Length: 70 Signature: 30440220709f62956b1cccbaf5afc73a762cd6979e6dd40345bf0b3991668b99cdcc3392… TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 4 Handshake Protocol: Server Hello Done Handshake Type: Server Hello Done (14) Length: 0
(SM-DP+ < LPA) Client Key Exchange, Change Cipher Spec, Finished
Transport Layer Security TLSv1.2 Record Layer: Handshake Protocol: Client Key Exchange Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 37 Handshake Protocol: Client Key Exchange Handshake Type: Client Key Exchange (16) Length: 33 EC Diffie-Hellman Client Params Pubkey Length: 32 Pubkey: fe4a98ccd86a53697931d3e2ac14664f483db564830c501f55317098bb2dbf54 TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec Content Type: Change Cipher Spec (20) Version: TLS 1.2 (0x0303) Length: 1 Change Cipher Spec Message TLSv1.2 Record Layer: Handshake Protocol: Encrypted Handshake Message Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 40 Handshake Protocol: Encrypted Handshake Message
(SM-DP+ > LPA) Change Cipher Spec, Finished
Transport Layer Security TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec Content Type: Change Cipher Spec (20) Version: TLS 1.2 (0x0303) Length: 1 Change Cipher Spec Message TLSv1.2 Record Layer: Handshake Protocol: Encrypted Handshake Message Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 40 Handshake Protocol: Encrypted Handshake Message
DOS/V POWER REPORT を参考にデスクトップ PC を組む
Android OS をフルビルドできるくらいのスペックの開発環境がなんとなく欲しくなってしまったので、PC を新調することにしました。昨今の状況からポータビリティーに拘る必要もあまりなくなっちゃいましたし、Windows 10 とデュアルブート仕様のデスクトップ機にしておけば困ることはないでしょう。
Windows 10 Home と Ubuntu 20.04 LTS のインストールに関する話はこちら。
cheerio-the-bear.hatenablog.com
PC の自作なんて久しぶりなので、DOS/V POWER REPORT に掲載されていた大満足自作プランを参考にさせて頂きました。7 万円プランを参考にしたはずなのに、10 万円プランプラスアルファくらいになりましたよね。でも、既製品を買うよりも全然楽しめるのでヨシ。
モニタと HDMI ケーブル、キーボードとマウスは今回新調せず、下記のものを調達しました。ほとんどのものが Amazon で揃ってしまうことにちょっと驚きました。価格的にもその時点における最安値だったり、最安値と大きな価格差がなかったり。PC パーツは PC パーツショップで、という時代では無くなっているんですね。
ケース | Thermaltake Core V21 |
---|---|
マザーボード | ASRock B550M Pro4 |
CPU | AMD Ryzen 5 3600 BOX |
CPU クーラー | サイズ 虎徹 Mark II |
グラフィックボード | MSI GeForce GTX 1660 SUPER AERO ITX OC VD7112 |
メモリ | Crucial CT2K16G4DFD832A DDR4-3200 PC4-25600 (16GB x 2) |
M.2 SSD | WD Blue SN550 NVMe WDS100T2B0C 1TB |
ATX電源ユニット | 玄人志向 KRPW-N500W/85+ |
Wi-Fi モジュール | Intel 7260HMW + Fenvi miniPCI/PCI-E wifi 変換ボード |
OS | Windows 10 Home 64 ビット DSP 版 + Ubuntu 20.04 LTS |
ケース
思ったよりも大きいとの評判のこちらのキューブ型ケース、確かに思ったよりも大きいです。でも、ケース内の空間に余裕があって組み立てやすく、良いケースだと思います。もう一台組み立てる機会があれば、Mini-ITX で Core V1 の方も使ってみたいです。
比較のために 1 ドル紙幣を置いてみましたが、このサイズ感、わかるでしょうか。おしゃれなオーブンレンジみたいで、かわいいです。もし「省スペース」が重要な場合は、キューブ型はおすすめしません。
組み立てる上でほんの少し面倒なのは、電源ユニットを支えるブラケットの部分くらいではないでしょうか。2 つのブラケット部品を組み合わせ、下の写真のようにネジで止めます。
マザーボード
各社各モデルのマザーボードの機能差分を調べるのは面倒だと思ったので、DOS/V POWER REPORT のおすすめをそのままフォロー。
CPU
Intel/AMD の CPU 性能比較グラフなんてものがたくさん出回っているじゃないですか。第 10 世代 Core i5 同等もしくはちょい上くらいがいいなと思ったので、Ryzen 5 は 3600 を選択。
グラフィックボード
本来の目的を考えれば、蛇足です。Unity とか Blender とかやりたくなっちゃう未来を見据えて、これくらいのスペックにしてみました。
メモリ
Android OS のフルビルドに必要なメモリ量は増加の一途ですので、そろそろ 32 GBくらいは必要なんだろうと考えました。
ASRock B550M Pro4 の場合、A2 スロットと B2 スロットにそれぞれ装着します。
ATX電源ユニット
これも適当に売れ筋のものを選んじゃいましたが、グラフィックボード用の電源ケーブルがもう少し長いと嬉しい感じでした。今回の構成でもちゃんと届いているので、全く問題はないんですけども。
Wi-Fi モジュール
Mini PCI 用の Wi-Fi モジュールを変換して PCI Express に装着することにしたのは、もう調べるのが面倒になったからです(正直)。マザーボード自体にもスロットがあるわけですし、こんなまどろっこしいことをする必要は本来ないはずです。
この Joekyo さんのブログを拝見して、Windows / Ubuntu の両方で問題なく利用できているとのことでしたので、そのまま乗っかった次第です。
どちらの OS でも何もしなくてもちゃんと認識してくれて、期待通りでした。ただ、グラフィックボードと並べて挿すと、グラフィックボードのファンとほんの少し干渉してしまいます。一番外側のスロットに挿すことにして、難を逃れています。尚、Wi-Fi モジュールを変換ボードに装着するのは簡単ですが、2 つのアンテナプラグの接続は指の細い人のほうが得意そうです。
Windows 10 Home と Ubuntu 20.04 LTS のデュアルブートにする
[2022/09/05 追記] その後、この PC は Windows 11 Professional と Ubuntu 22.04.1 LTS のデュアルブート機になっています。手順は同じ。
Android OS をフルビルドできるくらいのスペックの開発環境がなんとなく欲しくなってしまったので、PC を新調することにしました。昨今の状況からポータビリティーに拘る必要もあまりなくなっちゃいましたし、Windows 10 とデュアルブート仕様のデスクトップ機にしておけば困ることはないでしょう。
PC のスペックや構成するパーツに関する話はこちら。
cheerio-the-bear.hatenablog.com
インストール方法
デュアルブートを実現する方法について参考にさせて頂こうと思い、複数の個人ブログや企業ブログを見て回りました。実現方法はもちろんのこと、おまじない的なものの類まで複数あって惑わされましたが、結局のところこのシンプルな手順で普通に実現できたみたいです。しばらく使ってみていますが、今のところ不都合はありません。
1. Windows 10 Home のインストール
DSP 版で Windows 10 Home のライセンスを購入したので、ついつい届けられた DVD メディアを使ってインストールしてしまったのは失敗だったようです。
同様にしくじってしまった人は、Windows の「システム情報」の BIOS モード表示が「レガシ」になっていることに気付くことでしょう。次のスクリーンショットのように、UEFI モードでインストールされているのが期待です。
下記マイクロソフトのサイトからダウンロードできるインストールメディア作成ツールを使い、UEFI モードでインストールできる USB メモリを作成しましょう。
また、インストールした後になって、Windows OS 標準機能のリモートデスクトップが Windows 10 Home では使えないことに気付きました。価格差が大きくないことを考えると、もしかすると Windows 10 Pro にしておくべきだったかもしれません。まぁ、今のところ特に困ってはいません。
2. Ubuntu をインストールする領域を空ける
新調した 1TB の SSD に Windows 10 Home を新たにインストールしたので、そのほぼ全領域が C ドライブのためのパーティションに割り当てられました。Windows の「ディスクの管理」を使ってそのパーティションのサイズを半分近くまで縮小し、Ubuntu の基本パーティションのために領域を空けました。
空けた領域を「未割り当て」の状態にしたまま Ubuntu のインストール作業を終え、最終的にパーティション構成はこんな感じになりました。約 430GB のパーティションに Ubuntu が入っています。
Windows 上でこの空き領域を作る手続きをしてはいけないと書かれているブログも見かけましたが、今は特に問題が発生しなくなっているのかもしれません。
Google Pixel 4 がデュアル SIM 端末になるとき
Google Pixel 4 は出荷時はシングル SIM 端末ですが、その後の UI 操作によってデュアル SIM 端末へ動的に変化します。Google Pixel 4 に IIJ さんの eSIM をダウンロードしてデュアル SIM 端末にしましたので、そのときに観測できたことをメモしておこうと思います。
1. シングル SIM 動作時の端末コンフィギュレーション
1-1. FEATURE
Google Pixel 4 が eSIM を取り扱う能力がある端末であることを示す FEATURE_TELEPHONY_EUICC は、最初から表示されています。
$ pm list features ... feature:android.hardware.telephony feature:android.hardware.telephony.carrierlock feature:android.hardware.telephony.cdma feature:android.hardware.telephony.euicc // これ feature:android.hardware.telephony.gsm feature:android.hardware.telephony.ims ...
その他の FEATURE 一覧はこちら。
1-1. コンフィギュレーション用リソース
Android フレームワークが持っているリソースを、幾つか読んでみました。
config_telephonyEuiccDeviceCapabilities は概ね Google/AOSP にある下記の例の通りですが、contactlessSupportedRelease と rspCrlSupportedVersion の指定はありません。3GPP 系がリリース 11 になっているのは、どうしてでしょうか。
<!-- An array of device capabilities defined by GSMA SGP.22 v2.0. The first item is the capability name that the device supports. The second item is the major version. The minor and revision versions are default to 0s. The device capabilities and their definition in the spec are: gsm : gsmSupportedRelease utran : utranSupportedRelease cdma1x : cdma2000onexSupportedRelease hrpd : cdma2000hrpdSupportedRelease ehrpd : cdma2000ehrpdSupportedRelease eutran : eutranSupportedRelease nfc : contactlessSupportedRelease crl : rspCrlSupportedVersion --> <string-array translatable="false" name="config_telephonyEuiccDeviceCapabilities"> <item>"gsm,11"</item> <item>"utran,11"</item> <item>"cdma1x,1"</item> <item>"hrpd,3"</item> <item>"ehrpd,12"</item> <item>"eutran,11"</item> </string-array>
二番目の物理スロットが端末に実装されている eSIM なので、non_removable_euicc_slots の配列には 1 だけが設定されています。
<!-- Device-specific array of SIM slot indexes which are are embedded eUICCs. e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an eUICC, then the value of this array should be: <integer-array name="non_removable_euicc_slots"> <item>1</item> </integer-array> If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of this array should be: <integer-array name="non_removable_euicc_slots"> <item>1</item> <item>2</item> </integer-array> This is used to differentiate between removable eUICCs and built in eUICCs, and should be set by OEMs for devices which use eUICCs. --> <integer-array name="non_removable_euicc_slots"> <item>1</item> </integer-array>
この時点ではシングル SIM 端末ですが、Google Pixel 4 はデュアル SIM 端末になれる子です。config_num_physical_slots は 2 になっています。
<!-- Number of physical SIM slots on the device. This includes both eSIM and pSIM slots, and is not necessarily the same as the number of phones/logical modems supported by the device. For example, a multi-sim device can have 2 phones/logical modems, but 3 physical slots, or a single SIM device can have 1 phones/logical modems, but 2 physical slots (one eSIM and one pSIM) --> <integer name="config_num_physical_slots">2</integer>
1-3. テレフォニー API が返す値
テレフォニー API が返す値が、複数の SIM を取り扱うこと能力があることを示していることがわかります。Google Pixel 4 がデュアル SIM 端末になるときに端末の再起動を求められるのは、doesSwitchMultiSimConfigTriggerReboot() が true を返しているからですね。
API | 返値 |
---|---|
doesSwitchMultiSimConfigTriggerReboot() | true |
getActiveModemCount() | (Android Q にはまだない) |
getSupportedModemCount() | (Android Q にはまだない) |
getMaxNumberOfSimultaneouslyActiveSims() | (Android Q にはまだない) |
getMultiSimConfiguration() | UNKNOWN |
getSimCount() | 1 |
isMultiSimEnabled() | false |
isMultiSimSupported() | 0 (MULTISIM_ALLOWED) |
2. デュアル SIM 端末へ変更
2-1. 物理スロットの状態
物理スロットの状態を示す下記のログは、まだシングル SIM 端末として動作しているときにとった radio ログから抜き出したものです。物理スロット 2 にある eSIM だけがアクティブで、論理スロット 1 にマッピングされているようです。端末をアンロックしていないので、TelephonyManager.getLogicalToPhysicalSlotMapping() の返値は確認できていません。
D/RadioConfigResponse( 2047): [0009]< GET_SLOT_STATUS [ IccSlotStatus {CARDSTATE_ABSENT,SLOTSTATE_INACTIVE,logicalSlotIndex=0,atr=,iccid=,eid=}, IccSlotStatus {CARDSTATE_PRESENT,SLOTSTATE_ACTIVE,logicalSlotIndex=0,atr=3b9f96803fc6a28031e073f62157574a4d021060500002, ... ]
その状態から物理スロット 1 に SIM カードを装着すると、本投稿の冒頭に貼ったスクリーンショットのダイアログ群が表示されます。デュアル SIM 端末になること、それには再起動が必要なことに同意すると、端末の再起動の後にデュアル SIM 端末へと切り替わります。SWITCH_DUAL_SIM_CONFIG がそのために送信されるメッセージのようです。
D/PhoneCfgMgr( 2048): switchMultiSimConfig: with numOfSims = 2 D/PhoneCfgMgr( 2048): switchMultiSimConfig: sending the request for switching D/RadioConfig( 2048): [0352]> SWITCH_DUAL_SIM_CONFIG, numOfLiveModems = 2
デュアル SIM 端末になったところで、物理スロットの状態をもう一度見てみましょう。両方の物理スロットがアクティブになり、また物理・論理スロットのマッピングもストレートになっていることがわかります。
D/RadioConfigResponse( 2103): [0017]< GET_SLOT_STATUS [ IccSlotStatus {CARDSTATE_PRESENT,SLOTSTATE_ACTIVE,logicalSlotIndex=0,atr=3b9f96c00a1fc68031e073fe211f65d00233131b810ffa,iccid=898109001[****],eid=}, IccSlotStatus {CARDSTATE_PRESENT,SLOTSTATE_ACTIVE,logicalSlotIndex=1,atr=3b9f96803fc6a28031e073f62157574a4d021060500002,iccid=898103039[****], ... ]
2-2. テレフォニー API が返す値
getMultiSimConfiguration() と getSimCount()、isMultiSimEnabled() の返値が変わり、デュアル SIM 端末っぽくなっています。
API | 返値 |
---|---|
doesSwitchMultiSimConfigTriggerReboot() | true |
getActiveModemCount() | (Android Q にはまだない) |
getSupportedModemCount() | (Android Q にはまだない) |
getMaxNumberOfSimultaneouslyActiveSims() | (Android Q にはまだない) |
getMultiSimConfiguration() | DSDS |
getSimCount() | 2 |
isMultiSimEnabled() | true |
isMultiSimSupported() | 0 (MULTISIM_ALLOWED) |
2-3. システムプロパティ
元々は存在しなかったプロパティ、"persist.radio.multisim.config" が動的に追加されているのがわかります。
... [persist.mm.enable.prefetch]: [true] [persist.radio.multisim.config]: [dsds] // これ [persist.rcs.supported]: [1] ...