|
25 | 25 | <script type="text/babel"> |
26 | 26 | const params = new URLSearchParams(location.search); |
27 | 27 |
|
| 28 | + const toHex = (val) => val.toString(16).padStart(2, "0").toUpperCase(); |
| 29 | + const strToHexStream = (str) => str.split("").map((c) => c.charCodeAt(0).toString(16)).join("").toUpperCase(); |
| 30 | + // expose to devtool |
| 31 | + window.toHex = toHex; |
| 32 | + window.strToHexStream = strToHexStream; |
| 33 | + |
| 34 | + const copyToClipboard = (text) => { |
| 35 | + const p = document.createElement("p"); |
| 36 | + p.style.opacity = "0"; |
| 37 | + p.style.position = "absolute"; |
| 38 | + p.style.top = "0"; |
| 39 | + p.style.left = "0"; |
| 40 | + document.body.appendChild(p); |
| 41 | + p.innerHTML = text; |
| 42 | + |
| 43 | + let result; |
| 44 | + try { |
| 45 | + const range = document.createRange(); |
| 46 | + range.selectNode(p); |
| 47 | + window.getSelection().removeAllRanges(); |
| 48 | + window.getSelection().addRange(range); |
| 49 | + result = document.execCommand("copy"); |
| 50 | + window.getSelection().removeAllRanges(); |
| 51 | + } finally { |
| 52 | + document.body.removeChild(p); |
| 53 | + } |
| 54 | + return result; |
| 55 | + } |
| 56 | + |
28 | 57 | const App = (props) => { |
29 | 58 | const [priority, setPriority] = React.useState(255); |
30 | 59 | const [timer, setTimer] = React.useState(5); |
31 | 60 | const [channel, setChannel] = React.useState(5); |
32 | | - const [menuCount, setMenuCount] = React.useState(1); |
| 61 | + const [menuCount, setMenuCount] = React.useState(6); |
33 | 62 | const [entryLabelLength, setEntryLabelLength] = React.useState(32); |
34 | | - const [useUiccFs, setUseUiccFs] = React.useState(params.get("useuiccfs") === "true" || false); |
| 63 | + const [useUiccFs, setUseUiccFs] = React.useState((params.get("useuiccfs") == null) || (params.get("useuiccfs") === "true") || false); |
35 | 64 | const [adf1Aid, setAdf1Aid] = React.useState(params.get("adf1aid") || ""); |
36 | 65 |
|
37 | 66 | const [generatedUssp, setGeneratedUssp] = React.useState(""); |
| 67 | + const [generatedUsspWithTag, setGeneratedUsspWithTag] = React.useState(""); |
| 68 | + |
| 69 | + const [ramQuota, setRAMQuota] = React.useState(1); |
| 70 | + const [nvramQuota, setNVRAMQuota] = React.useState(1); |
| 71 | + |
| 72 | + const [generatedSspWithTag, setGeneratedSspWithTag] = React.useState(""); |
| 73 | + |
| 74 | + const [serverFQDN, setServerFQDN] = React.useState(params.get("serverfqdn") || "proxy.develop.sim-applet.com"); |
| 75 | + const [serverPort, setServerPort] = React.useState(80); |
| 76 | + const [connectInterval, setConnectInterval] = React.useState("005132"); |
| 77 | + const [encrypt, setEncrypt] = React.useState("01"); |
| 78 | + const [strOemEncKey, setStrOemEncKey] = React.useState("PlsCngThisEncKey"); |
| 79 | + const [hexOemEncKey, setHexOemEncKey] = React.useState(strToHexStream(strOemEncKey)); |
| 80 | + const [oemEncKeyType, setOemEncKeyType] = React.useState("str"); |
| 81 | + const [useDigestAuth, setUseDigestAuth] = React.useState(false); |
| 82 | + const [changeOemEncKey, setChangeOemEncKey] = React.useState(false); |
38 | 83 |
|
39 | | - const toHex = (val) => val.toString(16).padStart(2, "0").toUpperCase(); |
| 84 | + const [generatedAsp, setGeneratedAsp] = React.useState(""); |
| 85 | + const [generatedAspWithTag, setGeneratedAspWithTag] = React.useState(""); |
| 86 | + |
| 87 | + const generateSsp = () => { |
| 88 | + let ssp = ""; |
| 89 | + ssp += "C702" + ramQuota.toString(16).padStart(4,"0") |
| 90 | + ssp += "C802" + nvramQuota.toString(16).padStart(4,"0") |
| 91 | + ssp = "EF" + toHex(ssp.length/2) + ssp; |
| 92 | + ssp = ssp.toUpperCase(); |
| 93 | + |
| 94 | + setGeneratedSspWithTag(ssp); |
| 95 | + } |
| 96 | + |
| 97 | + const generateAsp = () => { |
| 98 | + let asp = ""; |
| 99 | + asp += "80" + toHex(serverFQDN.length) + strToHexStream(serverFQDN) |
| 100 | + asp += "8102" + serverPort.toString(16).padStart(4,"0") |
| 101 | + if (changeOemEncKey) { |
| 102 | + if (oemEncKeyType === "hex") { |
| 103 | + asp += "82" + toHex(hexOemEncKey.length/2) + hexOemEncKey; |
| 104 | + } else { |
| 105 | + const tmpOemEncKey = strToHexStream(strOemEncKey); |
| 106 | + asp += "82" + toHex(tmpOemEncKey.length/2) + tmpOemEncKey; |
| 107 | + } |
| 108 | + } |
| 109 | + asp += "8303" + connectInterval; |
| 110 | + asp += "8401" + encrypt; |
| 111 | + asp = asp.toUpperCase(); |
| 112 | + setGeneratedAsp(asp); |
| 113 | + |
| 114 | + asp = "C9" + toHex(asp.length/2) + asp |
| 115 | + setGeneratedAspWithTag(asp); |
| 116 | + } |
40 | 117 |
|
41 | 118 | const generateUssp = () => { |
42 | 119 | let ussp = ""; |
|
75 | 152 | ussp += toHex(uiccFs.length / 2); // Length of UICC Access Application specific parameters field |
76 | 153 | ussp += uiccFs; |
77 | 154 | } |
| 155 | + ussp = ussp.toUpperCase(); |
78 | 156 |
|
79 | 157 | setGeneratedUssp(ussp); |
| 158 | + |
| 159 | + let usspWithTag = "EA" + toHex(ussp.length/2) + ussp; |
| 160 | + setGeneratedUsspWithTag(usspWithTag); |
| 161 | + |
80 | 162 | } |
81 | 163 |
|
82 | 164 | React.useEffect(() => { |
83 | 165 | generateUssp(); |
84 | | - }, [priority, timer, channel, menuCount, entryLabelLength, useUiccFs, adf1Aid]); |
| 166 | + generateSsp(); |
| 167 | + generateAsp(); |
| 168 | + }, [priority, timer, channel, menuCount, entryLabelLength, useUiccFs, adf1Aid, |
| 169 | + ramQuota, nvramQuota, |
| 170 | + serverFQDN, serverPort, connectInterval, encrypt, hexOemEncKey, strOemEncKey, useDigestAuth, changeOemEncKey]); |
| 171 | + |
| 172 | + React.useEffect(() => { |
| 173 | + if (oemEncKeyType === "hex") { |
| 174 | + setHexOemEncKey(strToHexStream(strOemEncKey)); |
| 175 | + } else { |
| 176 | + setStrOemEncKey(hexOemEncKey.match(/.{1,2}/g)?.map((hex) => String.fromCharCode(parseInt(hex, 16))).join("") || ""); |
| 177 | + } |
| 178 | + }, [oemEncKeyType]); |
| 179 | + |
| 180 | + const gpParams = generatedAspWithTag + generatedSspWithTag + generatedUsspWithTag; |
85 | 181 |
|
86 | 182 | return ( |
87 | 183 | <div> |
@@ -137,8 +233,104 @@ <h2>設定</h2> |
137 | 233 | )} |
138 | 234 | </div> |
139 | 235 |
|
| 236 | + <h2>設定(System Specific Parameters)</h2> |
| 237 | + <div> |
| 238 | + <label className="config"> |
| 239 | + <div>Volatile Memory Quota(C7): </div> |
| 240 | + <div> |
| 241 | + <input type="number" value={ramQuota} onChange={(e) => setRAMQuota(parseInt(e.target.value, 10))} min={0} max={65535} /> |
| 242 | + </div> |
| 243 | + </label> |
| 244 | + <label className="config"> |
| 245 | + <div>Nonv-volatile Memory Quota(C8): </div> |
| 246 | + <div> |
| 247 | + <input type="number" value={nvramQuota} onChange={(e) => setNVRAMQuota(parseInt(e.target.value, 10))} min={0} max={65535} /> |
| 248 | + </div> |
| 249 | + </label> |
| 250 | + </div> |
| 251 | + |
| 252 | + <h2>設定(Application Specific Parameters)</h2> |
| 253 | + <div> |
| 254 | + <label className="config"> |
| 255 | + <div>(tag80)server FQDN: </div> |
| 256 | + <div style={{ flex: 1 }}> |
| 257 | + <input style={{ width: "80%" }} value={serverFQDN} maxLength="127" onChange={(e) => setServerFQDN(e.target.value) } /> |
| 258 | + </div> |
| 259 | + </label> |
| 260 | + <label className="config"> |
| 261 | + <div>(tag81)Port: </div> |
| 262 | + <div> |
| 263 | + <input type="number" value={serverPort} onChange={(e) => setServerPort(parseInt(e.target.value, 10))} min={1} max={65535} /> |
| 264 | + </div> |
| 265 | + </label> |
| 266 | + <label className="config"> |
| 267 | + <div> |
| 268 | + <input type="checkbox" checked={changeOemEncKey} onChange={(e) => setChangeOemEncKey(e.target.checked)} /> |
| 269 | + </div> |
| 270 | + <div>(tag82)OEMEncKeyを指定する</div> |
| 271 | + </label> |
| 272 | + <div style={{ marginLeft: "2rem", marginBottom: "0.5rem" }}><i>※telemetryの暗号化 と ダイジェスト認証時のPW として使用します</i></div> |
| 273 | + {changeOemEncKey && ( |
| 274 | + <label className="config" style={{ marginLeft: "2rem", marginBottom: "0.5rem" }}> |
| 275 | + <div>OEMEncKey: </div> |
| 276 | + {["str", "hex"].map(type => { |
| 277 | + return ( |
| 278 | + <label style={{ marginRight: "0.5rem" }}> |
| 279 | + <input type="radio" value={type} checked={type === oemEncKeyType} onChange={(e) => setOemEncKeyType(e.target.value)}/> |
| 280 | + <span>{type}</span> |
| 281 | + </label> |
| 282 | + ) |
| 283 | + })} |
| 284 | + <div style={{ flex: 1, display: "flex", alignItems: "baseline" }}> |
| 285 | + {oemEncKeyType === "hex" ? ( |
| 286 | + <> |
| 287 | + <input style={{ width: "80%" }} value={hexOemEncKey} onChange={(e) => setHexOemEncKey(e.target.value) } /> |
| 288 | + <span style={{ color: hexOemEncKey.length === 32 ? "inherit" : "red" }}> {hexOemEncKey.length}/32</span> |
| 289 | + </> |
| 290 | + ) : ( |
| 291 | + <> |
| 292 | + <input style={{ width: "80%" }} value={strOemEncKey} onChange={(e) => setStrOemEncKey(e.target.value) } /> |
| 293 | + <span style={{ color: strOemEncKey.length === 16 ? "inherit" : "red" }}> {strOemEncKey.length}/16</span> |
| 294 | + </> |
| 295 | + )} |
| 296 | + </div> |
| 297 | + </label> |
| 298 | + )} |
| 299 | + <label className="config"> |
| 300 | + <div>(tag83)接続間隔: </div> |
| 301 | + <div> |
| 302 | + <input type="text" value={connectInterval} onChange={(e) => setConnectInterval(e.target.value)} pattern="\d{6}" maxLength="6" /> |
| 303 | + </div><div>hHmMsS(例:15分23秒)</div> |
| 304 | + </label> |
| 305 | + |
| 306 | + <label className="config"> |
| 307 | + <div> |
| 308 | + <input type="checkbox" checked={encrypt === "01"} onChange={(e) => setEncrypt(e.target.checked ? "01" : "00")} /> |
| 309 | + </div> |
| 310 | + <div>(tag84)テレメトリデータを暗号化する</div> |
| 311 | + </label> |
| 312 | + </div> |
| 313 | + |
140 | 314 | <h2>UICC_SYSTEM_SPECIFIC_PARAMETERS</h2> |
141 | 315 | <code>{generatedUssp}</code> |
| 316 | + <div style={{ display: "flex", justifyContent: "end", marginTop: "0.5rem" }}> |
| 317 | + <button onClick={() => copyToClipboard(generatedUssp)}>クリップボードにコピー</button> |
| 318 | + </div> |
| 319 | + |
| 320 | + <h2>APP_SPECIFIC_PARAMETERS</h2> |
| 321 | + <code>{generatedAsp}</code> |
| 322 | + <div style={{ display: "flex", justifyContent: "end", marginTop: "0.5rem" }}> |
| 323 | + <button onClick={() => copyToClipboard(generatedAsp)}>クリップボードにコピー</button> |
| 324 | + </div> |
| 325 | + |
| 326 | + <h2>--params for GlobalPlatformPro</h2> |
| 327 | + <code>{gpParams}</code> |
| 328 | + <div style={{ display: "flex", justifyContent: "end", marginTop: "0.5rem" }}> |
| 329 | + <div> |
| 330 | + <div style={{ display: "flex", justifyContent: "end", color: gpParams.length > 255 ? "red" : "inherit", marginBottom: "0.5rem" }}>{gpParams.length}/255</div> |
| 331 | + <button onClick={() => copyToClipboard(gpParams)} disabled={gpParams.length > 255}>クリップボードにコピー</button> |
| 332 | + </div> |
| 333 | + </div> |
142 | 334 | </div> |
143 | 335 | ); |
144 | 336 | }; |
|
0 commit comments