@pkistudio/pvkgadgets 0.3.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/dist/app.js ADDED
@@ -0,0 +1,292 @@
1
+ import{Certificate as yn}from"./chunks/pkcs12-lDYE488P.js";import{PvkGadgetsCore as b,CERTIFICATE_KEY_USAGES as gn}from"./core.js";import{PkiStudio as ee,PkiStudioOidResolver as mn,PkiStudioCore as hn}from"./chunks/pkistudio--jw2SR0V.js";const vn=b.version;typeof window<"u"&&(window.PvkGadgetsCore=b);const wn=`
2
+ :host {
3
+ min-height: 0 !important;
4
+ width: 100% !important;
5
+ height: 100%;
6
+ overflow: hidden !important;
7
+ background: transparent !important;
8
+ }
9
+
10
+ main {
11
+ display: flex;
12
+ flex-direction: column;
13
+ width: 100% !important;
14
+ height: 100%;
15
+ min-height: 0;
16
+ margin: 0 !important;
17
+ padding: 0 !important;
18
+ }
19
+
20
+ .menu {
21
+ position: relative !important;
22
+ flex: 0 0 auto;
23
+ border-radius: 3px 3px 0 0 !important;
24
+ }
25
+
26
+ .card {
27
+ display: flex;
28
+ flex: 1 1 auto;
29
+ flex-direction: column;
30
+ gap: 6px;
31
+ min-height: 0;
32
+ border-right: 0 !important;
33
+ border-left: 0 !important;
34
+ border-radius: 0 !important;
35
+ box-shadow: none !important;
36
+ overflow: hidden !important;
37
+ }
38
+
39
+ .picker {
40
+ display: none !important;
41
+ }
42
+
43
+ .viewer {
44
+ flex: 1 1 auto;
45
+ min-height: 420px !important;
46
+ max-height: none !important;
47
+ }
48
+
49
+ :host(.pvkgadgets-viewer-readonly) [data-action="toggle-load-menu"],
50
+ :host(.pvkgadgets-viewer-readonly) [data-action="open"],
51
+ :host(.pvkgadgets-viewer-readonly) [data-action="load-clipboard-pem"],
52
+ :host(.pvkgadgets-viewer-readonly) [data-action="load-clipboard-hex"],
53
+ :host(.pvkgadgets-viewer-readonly) [data-action="close"],
54
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="edit"],
55
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="insert-before"],
56
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="insert-before-new-item"],
57
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="insert-before-clipboard-hex"],
58
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="add-child"],
59
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="add-child-new-item"],
60
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="add-child-clipboard-hex"],
61
+ :host(.pvkgadgets-viewer-readonly) [data-node-action="delete"],
62
+ .pvkgadgets-viewer-readonly [data-action="toggle-load-menu"],
63
+ .pvkgadgets-viewer-readonly [data-action="open"],
64
+ .pvkgadgets-viewer-readonly [data-action="load-clipboard-pem"],
65
+ .pvkgadgets-viewer-readonly [data-action="load-clipboard-hex"],
66
+ .pvkgadgets-viewer-readonly [data-action="close"],
67
+ .pvkgadgets-viewer-readonly [data-node-action="edit"],
68
+ .pvkgadgets-viewer-readonly [data-node-action="insert-before"],
69
+ .pvkgadgets-viewer-readonly [data-node-action="insert-before-new-item"],
70
+ .pvkgadgets-viewer-readonly [data-node-action="insert-before-clipboard-hex"],
71
+ .pvkgadgets-viewer-readonly [data-node-action="add-child"],
72
+ .pvkgadgets-viewer-readonly [data-node-action="add-child-new-item"],
73
+ .pvkgadgets-viewer-readonly [data-node-action="add-child-clipboard-hex"],
74
+ .pvkgadgets-viewer-readonly [data-node-action="delete"] {
75
+ opacity: 0.45;
76
+ pointer-events: none;
77
+ }
78
+
79
+ @media (max-width: 820px) {
80
+ .viewer {
81
+ min-height: 520px !important;
82
+ }
83
+ }
84
+ `;function En(y={}){const _=kn(y.mount??"#app");_.innerHTML=`
85
+ <main class="shell">
86
+ <nav class="toolbar" aria-label="Application">
87
+ <strong>Private Key Gadgets</strong>
88
+ <button id="aboutButton" type="button">About</button>
89
+ </nav>
90
+ <section class="workspace">
91
+ <section class="panel key-panel" aria-label="Generated key material">
92
+ <nav class="key-menu" aria-label="Key actions">
93
+ <div class="menu-group">
94
+ <button id="newKeyButton" type="button" aria-haspopup="menu" aria-expanded="false">New</button>
95
+ <div id="algorithmMenu" class="submenu" role="menu" hidden></div>
96
+ </div>
97
+ <button id="openKeyButton" type="button">Open</button>
98
+ <button id="saveKeyButton" type="button">Save</button>
99
+ <input id="openKeyInput" class="visually-hidden" type="file" accept=".p12,.pfx,application/pkcs12,application/x-pkcs12" />
100
+ <input id="certificateInput" class="visually-hidden" type="file" accept=".cer,.crt,.der,.pem,application/pkix-cert,application/x-x509-ca-cert" />
101
+ </nav>
102
+ <div id="privateKeyMenu" class="node-context-menu" role="menu" hidden>
103
+ <button id="newCsrMenuItem" type="button" role="menuitem">New CSR</button>
104
+ <button id="newSelfSignedCertMenuItem" type="button" role="menuitem">New self-signed Cert</button>
105
+ <button id="deletePrivateKeyMenuItem" type="button" role="menuitem">Delete</button>
106
+ </div>
107
+ <div id="keyPairMenu" class="node-context-menu" role="menu" hidden>
108
+ <div class="node-context-menu-group" role="none">
109
+ <button id="loadCertificateMenuItem" class="node-context-submenu-trigger" type="button" role="menuitem" aria-haspopup="menu">Load Certificate</button>
110
+ <div class="node-context-submenu" role="menu" aria-label="Load Certificate">
111
+ <button id="loadCertificateFromFileMenuItem" type="button" role="menuitem">from File</button>
112
+ <button id="loadCertificateFromClipboardPemMenuItem" type="button" role="menuitem">from Clipboard as PEM</button>
113
+ <button id="loadCertificateFromClipboardHexMenuItem" type="button" role="menuitem">from Clipboard as HEX</button>
114
+ </div>
115
+ </div>
116
+ <button id="newSubjectDnMenuItem" type="button" role="menuitem">New SubjectDN</button>
117
+ <button id="deleteKeyPairMenuItem" type="button" role="menuitem">Delete</button>
118
+ </div>
119
+ <div id="certificateMenu" class="node-context-menu" role="menu" hidden>
120
+ <button id="newCertificateSubjectDnMenuItem" type="button" role="menuitem">New SubjectDN</button>
121
+ <button id="copyCertificatePemMenuItem" type="button" role="menuitem">Copy as PEM</button>
122
+ <button id="deleteCertificateMenuItem" type="button" role="menuitem">Delete</button>
123
+ </div>
124
+ <div id="childItemMenu" class="node-context-menu" role="menu" hidden>
125
+ <button id="copyCsrPemMenuItem" type="button" role="menuitem" hidden>Copy as PEM</button>
126
+ <button id="deleteChildItemMenuItem" type="button" role="menuitem">Delete</button>
127
+ </div>
128
+ <section class="key-card">
129
+ <div id="keyTree" class="tree empty">No key generated yet.</div>
130
+ <p id="formNotice" class="notice">Generated DER is sent to the ASN.1 viewer.</p>
131
+ </section>
132
+ </section>
133
+ <div id="paneResizer" class="pane-resizer" role="separator" aria-label="Resize panes" aria-orientation="vertical" tabindex="0"></div>
134
+ <section class="viewer-panel" aria-label="ASN.1 viewer">
135
+ <div id="viewerMount" data-pkistudio-mount></div>
136
+ </section>
137
+ </section>
138
+ <div id="apiLogResizer" class="api-log-resizer" role="separator" aria-label="Resize API log" aria-orientation="horizontal" tabindex="0"></div>
139
+ <section class="api-log-panel panel" aria-label="API log">
140
+ <header class="api-log-header">
141
+ <button id="clearApiLogButton" type="button">Clear</button>
142
+ </header>
143
+ <div id="apiLogList" class="api-log-list" role="log" aria-live="polite" aria-relevant="additions"></div>
144
+ </section>
145
+ <dialog id="pkcs12PasswordDialog" class="password-dialog">
146
+ <form method="dialog" class="password-panel">
147
+ <h2 id="pkcs12PasswordTitle">Open PKCS#12</h2>
148
+ <label class="password-field">
149
+ <span>Password</span>
150
+ <input id="pkcs12PasswordInput" type="password" autocomplete="current-password" />
151
+ </label>
152
+ <div class="dialog-actions">
153
+ <button type="submit" value="cancel">Cancel</button>
154
+ <button type="submit" value="open">Open</button>
155
+ </div>
156
+ </form>
157
+ </dialog>
158
+ <dialog id="saveKeySelectionDialog" class="password-dialog save-key-dialog">
159
+ <form method="dialog" class="password-panel">
160
+ <h2>Save Key</h2>
161
+ <div id="saveKeyList" class="checkbox-list" role="group" aria-label="Key pairs"></div>
162
+ <div id="saveKeySelectionError" class="dialog-error" role="alert" hidden></div>
163
+ <div class="dialog-actions">
164
+ <button type="submit" value="cancel">Cancel</button>
165
+ <button id="saveKeySelectionNextButton" type="submit" value="next">Next</button>
166
+ </div>
167
+ </form>
168
+ </dialog>
169
+ <dialog id="csrDialog" class="password-dialog">
170
+ <form method="dialog" class="password-panel">
171
+ <h2>New CSR</h2>
172
+ <label class="password-field">
173
+ <span>subjectDN</span>
174
+ <input id="csrSubjectInput" type="text" autocomplete="off" readonly />
175
+ </label>
176
+ <div id="csrSubjectError" class="dialog-error" role="alert" hidden></div>
177
+ <label class="password-field">
178
+ <span>Hash algorithm</span>
179
+ <select id="csrHashSelect"></select>
180
+ </label>
181
+ <div class="dialog-actions">
182
+ <button type="submit" value="cancel">Cancel</button>
183
+ <button type="submit" value="create">Create</button>
184
+ </div>
185
+ </form>
186
+ </dialog>
187
+ <dialog id="selfSignedCertDialog" class="password-dialog">
188
+ <form method="dialog" class="password-panel">
189
+ <h2>New self-signed Cert</h2>
190
+ <label class="password-field">
191
+ <span>subjectDN</span>
192
+ <input id="selfSignedCertSubjectInput" type="text" autocomplete="off" readonly />
193
+ </label>
194
+ <div id="selfSignedCertSubjectError" class="dialog-error" role="alert" hidden></div>
195
+ <label class="password-field">
196
+ <span>Hash algorithm</span>
197
+ <select id="selfSignedCertHashSelect"></select>
198
+ </label>
199
+ <label class="password-field">
200
+ <span>Validity span days</span>
201
+ <input id="selfSignedCertValidityDaysInput" type="number" min="1" step="1" value="365" inputmode="numeric" />
202
+ </label>
203
+ <div id="selfSignedCertKeyUsageList" class="checkbox-list" role="group" aria-label="Key usage"></div>
204
+ <div class="dialog-actions">
205
+ <button type="submit" value="cancel">Cancel</button>
206
+ <button type="submit" value="create">Create</button>
207
+ </div>
208
+ </form>
209
+ </dialog>
210
+ <dialog id="subjectDnDialog" class="password-dialog">
211
+ <form method="dialog" class="password-panel">
212
+ <h2>New SubjectDN</h2>
213
+ <label class="password-field">
214
+ <span>subjectDN</span>
215
+ <input id="subjectDnInput" type="text" autocomplete="off" placeholder="CN=example.com, O=Example, C=JP" />
216
+ </label>
217
+ <div class="dialog-actions">
218
+ <button type="submit" value="cancel">Cancel</button>
219
+ <button type="submit" value="create">Create</button>
220
+ </div>
221
+ </form>
222
+ </dialog>
223
+ <dialog id="aboutDialog" class="about-dialog">
224
+ <section class="about-panel" role="document">
225
+ <p class="about-name">Private Key Gadgets</p>
226
+ <p class="about-version">Version ${vn}</p>
227
+ <p class="about-detail">PkiStudioJS ${ee.version??"viewer"} embedded ASN.1 viewer</p>
228
+ <div class="dialog-actions">
229
+ <button id="closeAboutButton" type="button">Close</button>
230
+ </div>
231
+ </section>
232
+ </dialog>
233
+ </main>
234
+ `;const it=o("#aboutButton"),X=o("#newKeyButton"),$=o(".workspace"),h=o("#paneResizer"),v=o("#apiLogResizer"),fe=o("#openKeyButton"),te=o("#saveKeyButton"),ne=o("#openKeyInput"),ie=o("#certificateInput"),P=o("#algorithmMenu"),R=o("#privateKeyMenu"),be=o("#newCsrMenuItem"),ye=o("#newSelfSignedCertMenuItem"),at=o("#deletePrivateKeyMenuItem"),A=o("#keyPairMenu"),rt=o("#loadCertificateFromFileMenuItem"),ot=o("#loadCertificateFromClipboardPemMenuItem"),st=o("#loadCertificateFromClipboardHexMenuItem"),ct=o("#newSubjectDnMenuItem"),dt=o("#deleteKeyPairMenuItem"),B=o("#certificateMenu"),lt=o("#newCertificateSubjectDnMenuItem"),ut=o("#copyCertificatePemMenuItem"),pt=o("#deleteCertificateMenuItem"),M=o("#childItemMenu"),De=o("#copyCsrPemMenuItem"),ft=o("#deleteChildItemMenuItem"),k=o("#keyTree"),Ee=o("#formNotice"),Ie=o("#viewerMount"),w=o("#apiLogList"),bt=o(".api-log-panel"),yt=o("#clearApiLogButton"),ae=o("#pkcs12PasswordDialog"),gt=o("#pkcs12PasswordTitle"),ge=o("#pkcs12PasswordInput"),$e=o('#pkcs12PasswordDialog button[type="submit"][value="open"]'),re=o("#saveKeySelectionDialog"),j=o("#saveKeyList"),oe=o("#saveKeySelectionError"),xe=o("#saveKeySelectionNextButton"),T=o("#csrDialog"),mt=o("#csrSubjectInput"),Ke=o("#csrSubjectError"),se=o("#csrHashSelect"),H=o("#selfSignedCertDialog"),ht=o("#selfSignedCertSubjectInput"),Pe=o("#selfSignedCertSubjectError"),ce=o("#selfSignedCertHashSelect"),me=o("#selfSignedCertValidityDaysInput"),je=o("#selfSignedCertKeyUsageList"),de=o("#subjectDnDialog"),le=o("#subjectDnInput"),Le=o("#aboutDialog"),Ne=o("#closeAboutButton");let m=null,p=[],l=null,F=[],V=null,L=null,q=null,Y=null,he=null,ve=null;const Re=["SHA-256","SHA-384","SHA-512"],vt=200;Jt(),un(),pn(),u("ready","Waiting for API activity."),g(!0),yt.addEventListener("click",()=>{w.replaceChildren(),u("clear","API log cleared.")}),it.addEventListener("click",()=>{Le.showModal(),Ne.focus()}),Ne.addEventListener("click",()=>{Le.close()});const Ae=async()=>{if(!ee){d("pkistudiojs viewer could not be loaded.",!0),u("pkistudiojs.init","Viewer API was not available.","error");return}m=ee.init({mount:Ie,oidResolver:y.viewer?.oidResolver??mn,newWindowUrl:y.viewer?.newWindowUrl??"viewer.html"}),u("pkistudiojs.init",`Viewer ${ee.version??"(unknown version)"} mounted.`),Yt(m),J(),Ht(m),await bn()};document.readyState==="loading"?window.addEventListener("DOMContentLoaded",Ae,{once:!0}):Ae(),X.addEventListener("click",()=>{X.disabled||G(Z(P))}),P.addEventListener("click",async e=>{const t=e.target instanceof Element?e.target.closest("[data-algorithm]"):null;t&&(G(!1),await wt(t.dataset.algorithm??""))}),fe.addEventListener("click",()=>{fe.disabled||(G(!1),ne.click())}),te.addEventListener("click",async()=>{if(te.disabled)return;G(!1);const e=await It();if(!e)return;const t=await Fe("Save PKCS#12",{value:"save",label:"Save"});t!==null&&await Ct(e,t)}),j.addEventListener("change",()=>{He()}),ne.addEventListener("change",async()=>{const[e]=ne.files??[];if(ne.value="",!e)return;const t=await Fe(`Open ${e.name}`,{value:"open",label:"Open"});t!==null&&await kt(e,t)}),ie.addEventListener("change",async()=>{const[e]=ie.files??[],t=he;ie.value="",he=null,!(!e||!t)&&await St(t,e)}),be.addEventListener("click",async()=>{const e=V;if(x(!1),!e)return;const t=await Kt(e);t&&await Lt(e,t.subjectDn,t.subjectBytes,t.hashAlgorithm)}),ye.addEventListener("click",async()=>{const e=V;if(x(!1),!e)return;const t=await Pt(e);t&&await Nt(e,t.subjectDn,t.subjectBytes,t.hashAlgorithm,t.validityDays,t.keyUsages)}),at.addEventListener("click",()=>{const e=V;x(!1),e&&we({keyId:e,kind:"private"})}),ct.addEventListener("click",async()=>{const e=L;if(C(!1),!e)return;const t=await jt();t!==null&&Rt(e,t)}),rt.addEventListener("click",()=>{const e=L;C(!1),e&&(he=e,ie.click())}),ot.addEventListener("click",async()=>{const e=L;C(!1),e&&await Be(e,"pem")}),st.addEventListener("click",async()=>{const e=L;C(!1),e&&await Be(e,"hex")}),dt.addEventListener("click",()=>{const e=L;C(!1),e&&Ut(e)}),lt.addEventListener("click",()=>{const e=q;K(!1),e&&At(e)}),pt.addEventListener("click",()=>{const e=q;K(!1),e&&we({keyId:e,kind:"certificate"})}),ut.addEventListener("click",async()=>{const e=q;K(!1),e&&await Ot(e)}),De.addEventListener("click",async()=>{const e=Y;N(!1),e?.kind==="csr"&&await Ue("CERTIFICATE REQUEST","CSR",e)}),ft.addEventListener("click",()=>{const e=Y;N(!1),e&&we(e)}),k.addEventListener("click",e=>{const t=e.target instanceof Element?e.target.closest("[data-key-label]"):null;if(t){e.preventDefault(),e.stopPropagation();const s=t.dataset.keyId??"";s&&(l={keyId:s,kind:"keypair"},E(),Ge(s)),t.focus();return}const n=e.target instanceof Element?e.target.closest("[data-keypair-menu]"):null;if(n){e.preventDefault(),e.stopPropagation();const s=n.dataset.keyId??"";s&&(l={keyId:s,kind:"keypair"},E(),Ge(s)),C(L!==s||Z(A),s,n);return}const i=e.target instanceof Element?e.target.closest("[data-certificate-menu]"):null;if(i){e.preventDefault(),e.stopPropagation();const s=i.dataset.keyId??"";K(q!==s||Z(B),s,i);return}const a=e.target instanceof Element?e.target.closest("[data-child-menu]"):null;if(a){e.preventDefault(),e.stopPropagation(),N(!fn(a)||Z(M),{keyId:a.dataset.keyId??"",kind:a.dataset.keyNode,csrId:a.dataset.csrId,subjectDnId:a.dataset.subjectDnId},a);return}const r=e.target instanceof Element?e.target.closest("[data-private-menu]"):null;if(r){e.preventDefault(),e.stopPropagation();const s=r.dataset.keyId??"";x(V!==s||Z(R),s,r);return}const c=e.target instanceof Element?e.target.closest("[data-key-node]"):null;x(!1),C(!1),K(!1),N(!1),c&&Zt(c.dataset.keyId??"",c.dataset.keyNode,c.dataset.csrId,c.dataset.subjectDnId)}),k.addEventListener("focusout",e=>{const t=e.target instanceof HTMLElement?e.target.closest("[data-key-label]"):null;t&&rn(t.dataset.keyId??"",t.textContent??"")}),k.addEventListener("keydown",e=>{const t=e.target instanceof HTMLElement?e.target.closest("[data-key-label]"):null;if(t&&(e.key==="Enter"&&(e.preventDefault(),t.blur()),e.key==="Escape")){e.preventDefault();const n=p.find(i=>i.id===t.dataset.keyId);t.textContent=n?.label??"",t.blur()}}),document.addEventListener("click",e=>{e.target instanceof Node&&!X.contains(e.target)&&!P.contains(e.target)&&G(!1),e.target instanceof Element&&!R.contains(e.target)&&!e.target.closest("[data-private-menu]")&&x(!1),e.target instanceof Element&&!A.contains(e.target)&&!e.target.closest("[data-keypair-menu]")&&C(!1),e.target instanceof Element&&!B.contains(e.target)&&!e.target.closest("[data-certificate-menu]")&&K(!1),e.target instanceof Element&&!M.contains(e.target)&&!e.target.closest("[data-child-menu]")&&N(!1)});async function wt(e){g(!0),d("Generating key pair...");try{u("WebCrypto.generateKey",`${e} requested.`),u("WebCrypto.exportKey","Exporting generated key pair as PKCS#8/SPKI DER.");const t=await b.generateKeyPair(e,{createId:pe});ze(t),d(`Generated ${I(t).label}.`),u("WebCrypto.generateKey",`Generated ${I(t).label}.`)}catch(t){d(t instanceof Error?t.message:String(t),!0),u("WebCrypto.generateKey",t instanceof Error?t.message:String(t),"error")}finally{g(!1)}}async function kt(e,t){g(!0),d(`Opening ${e.name}...`);try{const n=new Uint8Array(await e.arrayBuffer());u("PKCS#12.open",`Reading ${e.name} (${n.byteLength} bytes).`);const i=(await b.readPkcs12(n,t,{sourceName:e.name,createId:pe})).map(r=>({...r,label:r.label||Q(r)}));for(const r of i)ze(r);const a=i.length===1?"":"s";d(`Opened ${i.length} key${a} from ${e.name}.`),u("PKCS#12.open",`Opened ${i.length} key${a} from ${e.name}.`)}catch(n){d(n instanceof Error?n.message:String(n),!0),u("PKCS#12.open",n instanceof Error?n.message:String(n),"error")}finally{g(!1)}}async function St(e,t){await Me(e,await Dt(t),t.name)}async function Be(e,t){try{const n=await Gt(),i=t==="pem"?Te(n):tt().hexToBytes(n);u(`Clipboard.readText.${t.toUpperCase()}`,`Decoded certificate DER (${i.byteLength} bytes).`),await Me(e,i,`clipboard ${t.toUpperCase()}`)}catch(n){d(n instanceof Error?n.message:String(n),!0),u(`Clipboard.readText.${t.toUpperCase()}`,n instanceof Error?n.message:String(n),"error")}}async function Me(e,t,n){const i=p.find(a=>a.id===e);if(i){g(!0),d(`Adding Certificate from ${n}...`);try{const a=yn.fromBER(b.toArrayBuffer(t)),r=new Uint8Array(a.subjectPublicKeyInfo.toSchema().toBER(!1)),c=await Et(i,r);if(!c&&!await W("The certificate public key does not match the private key. Apply this certificate anyway?"))return;i.certificateDer=t,c&&(i.publicKeyDer=r),l={keyId:i.id,kind:"certificate"},S(),E(),d(`${c?"Added":"Applied"} Certificate from ${n}.`),u("Certificate.load",`${c?"Added":"Applied"} certificate from ${n} (${t.byteLength} bytes).`)}catch(a){d(a instanceof Error?a.message:String(a),!0),u("Certificate.load",a instanceof Error?a.message:String(a),"error")}finally{g(!1)}}}async function Ct(e,t){g(!0),d("Creating PKCS#12...");try{const n=e.map(r=>p.find(c=>c.id===r)).filter(r=>!!r);u("PKCS#12.save",`Creating PKCS#12 for ${n.length} key pair${n.length===1?"":"s"}.`);const i=await b.writePkcs12(n,t),a=on(n);await xt(i,a),d(`Saved ${n.length} key pair${n.length===1?"":"s"} to ${a}.`),u("PKCS#12.save",`Saved ${a} (${i.byteLength} bytes).`)}catch(n){d(n instanceof Error?n.message:String(n),!0),u("PKCS#12.save",n instanceof Error?n.message:String(n),"error")}finally{g(!1)}}async function Dt(e){const t=new Uint8Array(await e.arrayBuffer()),n=new TextDecoder("utf-8",{fatal:!1}).decode(t);return/-----BEGIN CERTIFICATE-----/i.test(n)?Te(n):t}function Te(e){const t=/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/i.exec(e);if(!t)throw new Error("Certificate PEM was not found.");return b.pemToDer(t[0],"CERTIFICATE")}async function Et(e,t){if(e.publicKeyDer)return b.bytesEqual(e.publicKeyDer,t);if(!e.privateKeyDer)throw new Error("PrivateKey item is required before adding a certificate.");const n=I(e);if(!n.canSign)throw new Error(`${n.label} cannot be checked against a certificate.`);u("WebCrypto.verify",`Checking certificate public key against ${n.label} private key.`);const i=await b.verifyPrivateKeyMatchesPublicKey(e.privateKeyDer,t,n);return u("WebCrypto.verify",i?"Certificate public key matched.":"Certificate public key did not match.",i?"ok":"error"),i}function It(){return $t(),oe.hidden=!0,oe.textContent="",re.returnValue="",new Promise(e=>{re.addEventListener("close",()=>{if(re.returnValue!=="next"){e(null);return}const t=[...j.querySelectorAll('input[name="save-key"]:checked')].map(n=>n.value);e(t.length>0?t:null)},{once:!0}),re.showModal(),j.querySelector('input[name="save-key"]:not(:disabled)')?.focus()})}function $t(){const e=l?.keyId,t=p.filter(n=>n.privateKeyDer);if(p.length===0){j.innerHTML='<p class="checkbox-list-empty">No key pair is available.</p>',xe.disabled=!0;return}j.innerHTML=p.map(n=>{const i=n.privateKeyDer?"":" disabled",a=n.id===e&&n.privateKeyDer?" checked":"",r=n.label||Q(n),c=n.privateKeyDer?sn(n):"No PrivateKey item";return`
235
+ <label class="checkbox-list-item">
236
+ <input type="checkbox" name="save-key" value="${f(n.id)}"${a}${i} />
237
+ <span>
238
+ <strong>${f(r)}</strong>
239
+ <small>${f(c)}</small>
240
+ </span>
241
+ </label>
242
+ `}).join(""),t.length===1&&!j.querySelector('input[name="save-key"]:checked')&&(j.querySelector('input[name="save-key"]:not(:disabled)').checked=!0),He()}function He(){const e=j.querySelectorAll('input[name="save-key"]:checked').length;xe.disabled=e===0,oe.textContent=e===0?"Select at least one key pair.":"",oe.hidden=e!==0}function Fe(e,t){return gt.textContent=e,ge.value="",ae.returnValue="",$e.value=t.value,$e.textContent=t.label,new Promise(n=>{ae.addEventListener("close",()=>{n(ae.returnValue===t.value?ge.value:null)},{once:!0}),ae.showModal(),ge.focus()})}async function xt(e,t){const n=[{description:"PKCS#12 files",accept:{"application/x-pkcs12":[".p12",".pfx"]}}];if(y.host?.saveFile){u("Host.saveFile",`Requesting host save for ${t}.`);const c=await y.host.saveFile({bytes:e,suggestedName:t,types:n});u("Host.saveFile",`Saved ${e.byteLength} bytes${c?.filename?` to ${c.filename}`:""}.`);return}const i=new Blob([b.toArrayBuffer(e)],{type:"application/x-pkcs12"});if(window.showSaveFilePicker){u("FileSystem.showSaveFilePicker",`Requesting save handle for ${t}.`);const c=await window.showSaveFilePicker({suggestedName:t,types:n}),s=await c.createWritable();await s.write(i),await s.close(),u("FileSystem.write",`Wrote ${e.byteLength} bytes to ${c.name||t}.`);return}u("Browser.download",`Downloading ${t} (${e.byteLength} bytes).`);const a=URL.createObjectURL(i),r=document.createElement("a");r.href=a,r.download=t,r.click(),URL.revokeObjectURL(a)}async function W(e){return y.host?.confirm?!!await y.host.confirm(e):window.confirm(e)}function Kt(e){const t=qe(e,"creating a CSR");mt.value=t.subjectDn,se.innerHTML=Re.map(i=>`<option value="${i}">${i}</option>`).join(""),se.disabled=!t.subjectBytes,Ke.textContent=t.error??"",Ke.hidden=!t.error;const n=T.querySelector('button[value="create"]');return n&&(n.disabled=!t.subjectBytes),T.returnValue="",new Promise(i=>{T.addEventListener("close",()=>{if(T.returnValue!=="create"){i(null);return}if(!t.subjectBytes){i(null);return}i({subjectDn:t.subjectDn,subjectBytes:t.subjectBytes,hashAlgorithm:se.value})},{once:!0}),T.showModal(),t.subjectBytes?se.focus():T.querySelector('button[value="cancel"]')?.focus()})}function Pt(e){const t=qe(e,"creating a self-signed certificate");ht.value=t.subjectDn,ce.innerHTML=Re.map(i=>`<option value="${i}">${i}</option>`).join(""),ce.disabled=!t.subjectBytes,me.value="365",me.disabled=!t.subjectBytes,Pe.textContent=t.error??"",Pe.hidden=!t.error,je.innerHTML=gn.map(i=>`
243
+ <label class="checkbox-list-item">
244
+ <input type="checkbox" name="certificate-key-usage" value="${f(i.id)}"${i.defaultChecked?" checked":""}${t.subjectBytes?"":" disabled"} />
245
+ <span><strong>${f(i.label)}</strong></span>
246
+ </label>
247
+ `).join("");const n=H.querySelector('button[value="create"]');return n&&(n.disabled=!t.subjectBytes),H.returnValue="",new Promise(i=>{H.addEventListener("close",()=>{if(H.returnValue!=="create"||!t.subjectBytes){i(null);return}const a=Number.parseInt(me.value,10),r=[...je.querySelectorAll('input[name="certificate-key-usage"]:checked')].map(c=>c.value);i({subjectDn:t.subjectDn,subjectBytes:t.subjectBytes,hashAlgorithm:ce.value,validityDays:Number.isFinite(a)&&a>0?a:365,keyUsages:r})},{once:!0}),H.showModal(),t.subjectBytes?ce.focus():H.querySelector('button[value="cancel"]')?.focus()})}function jt(){return le.value="CN=example.com, O=Example, C=JP",de.returnValue="",new Promise(e=>{de.addEventListener("close",()=>{e(de.returnValue==="create"?le.value.trim():null)},{once:!0}),de.showModal(),le.focus(),le.select()})}async function Lt(e,t,n,i){const a=p.find(r=>r.id===e);if(a){g(!0),d("Creating CSR...");try{const r=I(a);if(r.family!=="RSA"&&r.family!=="EC")throw new Error(`${r.label} is not supported for CSR signing yet.`);if(!a.privateKeyDer||!a.publicKeyDer)throw new Error("PrivateKey and PublicKey are required to create a CSR.");u("CSR.create",`Importing ${r.label} signing keys for ${i}.`),u("CSR.sign",`Signing CSR for ${t}.`);const c=await b.createCsr({privateKeyDer:a.privateKeyDer,publicKeyDer:a.publicKeyDer,subjectDn:t,subjectBytes:n,hashAlgorithm:i}),s=a.csrs?.[0];if(s&&!await W("CSR item already exists. Overwrite it?"))return;const D={id:s?.id??pe(),label:"CSR",subjectDn:t,hashAlgorithm:i,bytes:c.bytes};a.csrs=[D],l={keyId:a.id,kind:"csr",csrId:D.id},S(),E(),d(`${s?"Updated":"Created"} CSR for ${t}.`),u("CSR.create",`${s?"Updated":"Created"} CSR (${D.bytes.byteLength} bytes).`)}catch(r){d(r instanceof Error?r.message:String(r),!0),u("CSR.create",r instanceof Error?r.message:String(r),"error")}finally{g(!1)}}}async function Nt(e,t,n,i,a,r){const c=p.find(s=>s.id===e);if(c){g(!0),d("Creating self-signed Certificate...");try{const s=I(c);if(s.family!=="RSA"&&s.family!=="EC")throw new Error(`${s.label} is not supported for certificate signing yet.`);if(!c.privateKeyDer||!c.publicKeyDer)throw new Error("PrivateKey and PublicKey are required to create a self-signed certificate.");u("Certificate.selfSign",`Importing ${s.label} signing keys for ${i}; validity ${a} days.`),u("Certificate.sign",`Signing self-signed certificate for ${t}.`);const{bytes:D}=await b.createSelfSignedCertificate({privateKeyDer:c.privateKeyDer,publicKeyDer:c.publicKeyDer,subjectDn:t,subjectBytes:n,hashAlgorithm:i,validityDays:a,keyUsages:r});if(c.certificateDer&&!await W("Certificate item already exists. Overwrite it?"))return;c.certificateDer=D,l={keyId:c.id,kind:"certificate"},S(),E(),d(`Created self-signed Certificate for ${t}.`),u("Certificate.selfSign",`Created self-signed certificate (${D.byteLength} bytes).`)}catch(s){d(s instanceof Error?s.message:String(s),!0),u("Certificate.selfSign",s instanceof Error?s.message:String(s),"error")}finally{g(!1)}}}async function Rt(e,t){const n=p.find(i=>i.id===e);if(n)try{const i=await Ve(n,t,Bt(t));if(i===null)return;d(`${i?"Updated":"Created"} SubjectDN for ${t}.`)}catch(i){d(i instanceof Error?i.message:String(i),!0)}}async function At(e){const t=p.find(n=>n.id===e);if(t?.certificateDer)try{const n=Mt(t.certificateDer),i=Tt(n),a=await Ve(t,i,n);if(a===null)return;d(`${a?"Updated":"Created"} SubjectDN from certificate subject.`)}catch(n){d(n instanceof Error?n.message:String(n),!0)}}async function Ve(e,t,n){const i=e.subjectDns?.[0];if(i&&!await W("SubjectDN item already exists. Overwrite it?"))return null;const a={id:i?.id??pe(),label:"SubjectDN",subjectDn:t,bytes:n};return e.subjectDns=[a],l={keyId:e.id,kind:"subjectdn",subjectDnId:a.id},S(),E(),!!i}function Bt(e){return b.createSubjectDn(e)}function Mt(e){return b.getCertificateSubjectDn(e)}function qe(e,t){const i=p.find(a=>a.id===e)?.subjectDns?.at(-1);if(!i)return{subjectDn:"(SubjectDN item is missing)",error:`SubjectDN item is required before ${t}.`};try{return{subjectDn:b.subjectDnToString(i.bytes),subjectBytes:new Uint8Array(i.bytes)}}catch(a){return{subjectDn:"(Invalid SubjectDN item)",error:`SubjectDN item could not be decoded: ${a instanceof Error?a.message:String(a)}`}}}function Tt(e){return b.subjectDnToString(e)}function Ht(e){!e.root||!e.getNodeBytes||(e.root.addEventListener("click",Ft,!0),y.host?.openDerViewer&&e.root.addEventListener("click",t=>{Vt(t,e)},!0),e.root.addEventListener("click",en,!0),e.root.addEventListener("submit",()=>We(e),!0),e.root.addEventListener("click",t=>{t.target instanceof Element&&(t.target.closest('[data-node-action="delete"], [data-about-action], [data-edit-action="cancel"], [data-time-action="cancel"], [data-octet-action="cancel"], [data-der-action="cancel"], [data-clipboard-insert-action="cancel"]')||We(e))},!0))}function Ft(e){const n=(e.target instanceof Element?e.target:null)?.closest("[data-node-icon][data-node-id]");n?.dataset.nodeId&&(ve=n.dataset.nodeId)}async function Vt(e,t){const i=(e.target instanceof Element?e.target:null)?.closest('button[data-node-action="send-new-window"], button[data-node-action="send-new-window-extracted"]');if(!(!i||!y.host?.openDerViewer)){e.preventDefault(),e.stopImmediatePropagation();try{if(!ve||!t.getNodeBytes)throw new Error("The selected ASN.1 node was not found.");const a=i.dataset.nodeAction==="send-new-window-extracted",r=t.getNodeBytes(ve),c=a?qt(r):r,s=a?"Extracted DER from Private Key Gadgets.der":"ASN.1 subtree from Private Key Gadgets.der";await y.host.openDerViewer({title:s,bytes:c}),d(`Opened ${a?"extracted DER":"ASN.1 subtree"} in a new PKI Studio viewer.`)}catch(a){d(a instanceof Error?a.message:String(a),!0)}}}function qt(e){const t=tt(),[n]=t.parseElements(e,0,e.byteLength);if(!n?.encapsulated||!n.children.length)throw new Error("The selected node does not contain extracted DER.");return t.encodeNodes(n.children)}function We(e){window.setTimeout(()=>{if(!(!l||l.kind!=="subjectdn"||!e.getNodeBytes))try{Wt(e.getNodeBytes("1"))}catch{}})}function Wt(e){if(!l||l.kind!=="subjectdn")return;const n=p.find(i=>i.id===l?.keyId)?.subjectDns?.find(i=>i.id===l?.subjectDnId);!n||b.bytesEqual(n.bytes,e)||(n.bytes=new Uint8Array(e),S(),d(`Updated ${n.label} from viewer edits.`))}async function Ut(e){const t=p.find(n=>n.id===e);t&&await W(`Delete ${t.label||Q(t)} and all child items?`)&&(p=p.filter(n=>n.id!==e),l=p[0]?ke(p[0]):null,S(),te.disabled=p.length===0,l?E():m?.close?.(),d(`Deleted ${t.label||"KeyPair"}.`))}async function we(e){const t=p.find(n=>n.id===e.keyId);t&&await W(`Delete ${Oe(t,e)}?`)&&(e.kind==="private"&&(t.privateKeyDer=void 0),e.kind==="public"&&(t.publicKeyDer=void 0),e.kind==="certificate"&&(t.certificateDer=void 0),e.kind==="subjectdn"&&(t.subjectDns=(t.subjectDns??[]).filter(n=>n.id!==e.subjectDnId)),e.kind==="csr"&&(t.csrs=(t.csrs??[]).filter(n=>n.id!==e.csrId)),l=ke(t),S(),l?E():m?.close?.(),d(`Deleted ${Oe(t,e)}.`))}async function Ot(e){await Ue("CERTIFICATE","Certificate",{keyId:e,kind:"certificate"})}async function Ue(e,t,n){const i=p.find(r=>r.id===n.keyId);if(!i)return;const a=Ye(i,n);if(a)try{await zt(_t(e,a)),d(`Copied ${t} as PEM.`)}catch(r){d(r instanceof Error?r.message:String(r),!0)}}async function zt(e){if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e),u("Clipboard.writeText",`Wrote ${e.length} characters.`);return}const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",document.body.append(t),t.select();const n=document.execCommand("copy");if(t.remove(),!n)throw new Error("Could not copy CSR PEM to the clipboard.");u("Clipboard.execCommand",`Copied ${e.length} characters.`)}async function Gt(){if(!navigator.clipboard?.readText||!window.isSecureContext)throw new Error("Clipboard reading is not available in this browser context.");const e=await navigator.clipboard.readText();return u("Clipboard.readText",`Read ${e.length} characters.`),e}function _t(e,t){return b.derToPem(e,t)}function ke(e){return e.subjectDns?.[0]?{keyId:e.id,kind:"subjectdn",subjectDnId:e.subjectDns[0].id}:e.privateKeyDer?{keyId:e.id,kind:"private"}:e.publicKeyDer&&!e.certificateDer?{keyId:e.id,kind:"public"}:e.certificateDer?{keyId:e.id,kind:"certificate"}:e.csrs?.[0]?{keyId:e.id,kind:"csr",csrId:e.csrs[0].id}:null}function Oe(e,t){return t.kind==="subjectdn"?"SubjectDN":t.kind==="csr"?"CSR":t.kind==="private"?"PrivateKey":t.kind==="public"?"PublicKey":t.kind==="certificate"?"Certificate":e.label||"item"}function Xt(e,t){if(!m){d("pkistudiojs viewer is not ready yet.",!0);return}m.loadBytes(e,t),J()}function ze(e){e.label||=Q(e),p.push(e),l=ke(e),S(),E()}function Yt(e){if(!e.root)return;const t=document.createElement("style");t.textContent=wn,e.root.prepend(t)}function Jt(){const e=Qt();e&&(document.documentElement.dataset.pvkgadgetsTheme=e,Ie.setAttribute("data-pkistudio-theme",e))}function Qt(){if(y.theme)return y.theme;const e=new URL(window.location.href).searchParams.get("theme");return e==="dark"||e==="light"?e:null}function Zt(e,t,n,i){p.some(a=>a.id===e)&&(l={keyId:e,kind:t,csrId:n,subjectDnId:i},S(),E())}function Ge(e){for(const n of k.querySelectorAll(".tree-node > summary.selected"))n.classList.remove("selected");for(const n of k.querySelectorAll(".tree-row.selected"))n.classList.remove("selected");for(const n of k.querySelectorAll('[data-key-node][aria-pressed="true"]'))n.setAttribute("aria-pressed","false");[...k.querySelectorAll("[data-key-label]")].find(n=>n.dataset.keyId===e)?.closest("summary")?.classList.add("selected")}function E(){if(!l){m?.close?.(),J();return}const e=p.find(r=>r.id===l?.keyId);if(!e){m?.close?.(),J();return}const t=Ye(e,l);if(!t){m?.close?.(),J();return}const n=I(e),i=cn(l.kind),a=dn(e,l);Xt(t,`${n.label} ${a} (${i})`)}function en(e){if(_e())return;const n=(e.target instanceof Element?e.target:null)?.closest("button[data-action], button[data-node-action]");!n||!Xe(n)||(e.preventDefault(),e.stopImmediatePropagation(),d("Viewer editing is available only while a SubjectDN item is selected.",!0))}function J(){if(!m?.root)return;const e=_e();tn(m.root)?.classList.toggle("pvkgadgets-viewer-readonly",!e);for(const n of m.root.querySelectorAll("button[data-action], button[data-node-action]"))Xe(n)&&(n.disabled=!e,n.title=e?"":"Viewer editing is available only while a SubjectDN item is selected.")}function _e(){return l?.kind==="subjectdn"}function tn(e){return e instanceof ShadowRoot?e.host instanceof HTMLElement?e.host:null:e instanceof HTMLElement?e:null}function Xe(e){const t=e.dataset.action;if(t==="toggle-load-menu"||t==="open"||t==="load-clipboard-pem"||t==="load-clipboard-hex"||t==="close")return!0;const n=e.dataset.nodeAction;return n==="edit"||n==="delete"||n==="add-child"||n==="add-child-new-item"||n==="add-child-clipboard-hex"||n==="insert-before"||n==="insert-before-new-item"||n==="insert-before-clipboard-hex"}function S(){if(p.length===0){k.className="tree empty",k.textContent="No key generated yet.";return}k.className="tree",k.innerHTML=p.map(e=>{const t=I(e);return`
248
+ <details class="tree-node" open>
249
+ <summary class="${l?.keyId===e.id&&l.kind==="keypair"?"selected":""}">
250
+ <span class="tree-toggle" aria-hidden="true">−</span>
251
+ <button class="tree-icon-button" type="button" data-keypair-menu data-key-id="${f(e.id)}" aria-label="KeyPair actions"><span class="tree-icon folder" aria-hidden="true"></span></button>
252
+ <span class="tree-tag key-label" data-key-label data-key-id="${f(e.id)}" contenteditable="true" spellcheck="false">${f(e.label||t.label)}</span>
253
+ </summary>
254
+ <div class="tree-children">
255
+ ${(e.subjectDns??[]).map(i=>nn(e,i)).join("")}
256
+ ${e.privateKeyDer?Se(e,"private","PrivateKey",e.privateKeyDer,t.label):""}
257
+ ${e.publicKeyDer?Se(e,"public","PublicKey",e.publicKeyDer,t.label):""}
258
+ ${e.certificateDer?Se(e,"certificate","Certificate",e.certificateDer):""}
259
+ ${(e.csrs??[]).map(i=>an(e,i)).join("")}
260
+ </div>
261
+ </details>
262
+ `}).join("")}function nn(e,t){const n=l?.keyId===e.id&&l.kind==="subjectdn"&&l.subjectDnId===t.id;return`
263
+ <details class="tree-node tree-leaf">
264
+ <summary class="tree-row${n?" selected":""}">
265
+ <span class="tree-toggle" aria-hidden="true"></span>
266
+ <button class="tree-icon-button" type="button" data-child-menu data-key-id="${f(e.id)}" data-key-node="subjectdn" data-subject-dn-id="${f(t.id)}" aria-label="SubjectDN actions"><span class="tree-icon leaf" aria-hidden="true"></span></button>
267
+ <button class="tree-item" type="button" data-key-id="${f(e.id)}" data-key-node="subjectdn" data-subject-dn-id="${f(t.id)}" aria-pressed="${n}">
268
+ <span class="tree-tag">${f(t.label)} (${t.bytes.byteLength})</span>
269
+ </button>
270
+ </summary>
271
+ </details>
272
+ `}function Se(e,t,n,i,a){const r=l?.keyId===e.id&&l.kind===t,c=a&&(t==="private"||t==="public")?` // ${a}`:"",s=t==="private"?`<button class="tree-icon-button" type="button" data-private-menu data-key-id="${f(e.id)}" aria-label="PrivateKey actions"><span class="tree-icon leaf" aria-hidden="true"></span></button>`:t==="certificate"?`<button class="tree-icon-button" type="button" data-certificate-menu data-key-id="${f(e.id)}" aria-label="Certificate actions"><span class="tree-icon leaf" aria-hidden="true"></span></button>`:`<button class="tree-icon-button" type="button" data-child-menu data-key-id="${f(e.id)}" data-key-node="${t}" aria-label="${n} actions"><span class="tree-icon leaf" aria-hidden="true"></span></button>`;return`
273
+ <details class="tree-node tree-leaf">
274
+ <summary class="tree-row${r?" selected":""}">
275
+ <span class="tree-toggle" aria-hidden="true"></span>
276
+ ${s}
277
+ <button class="tree-item" type="button" data-key-id="${f(e.id)}" data-key-node="${t}" aria-pressed="${r}">
278
+ <span class="tree-tag">${n} (${i.byteLength})${f(c)}</span>
279
+ </button>
280
+ </summary>
281
+ </details>
282
+ `}function an(e,t){const n=l?.keyId===e.id&&l.kind==="csr"&&l.csrId===t.id;return`
283
+ <details class="tree-node tree-leaf">
284
+ <summary class="tree-row${n?" selected":""}">
285
+ <span class="tree-toggle" aria-hidden="true"></span>
286
+ <button class="tree-icon-button" type="button" data-child-menu data-key-id="${f(e.id)}" data-key-node="csr" data-csr-id="${f(t.id)}" aria-label="CSR actions"><span class="tree-icon leaf" aria-hidden="true"></span></button>
287
+ <button class="tree-item" type="button" data-key-id="${f(e.id)}" data-key-node="csr" data-csr-id="${f(t.id)}" aria-pressed="${n}">
288
+ <span class="tree-tag">${f(t.label)} (${t.bytes.byteLength}) // ${f(t.hashAlgorithm)}</span>
289
+ </button>
290
+ </summary>
291
+ </details>
292
+ `}function rn(e,t){const n=p.find(a=>a.id===e);if(!n)return;const i=t.trim();if(!i){d("Key pair label cannot be empty.",!0),S();return}n.label!==i&&(n.label=i,S(),d(`Renamed key pair to ${i}.`))}function Q(e){return I({privateKeyDer:e.privateKeyDer,publicKeyDer:e.publicKeyDer}).label}function on(e){return e.length!==1?"private-key-gadgets.p12":`${(e[0].label||Q(e[0])).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"keypair"}.p12`}function sn(e){const t=I(e),n=e.certificateDer?", Certificate":"";return`${t.label}, PrivateKey${n}`}function Ye(e,t){if(t.kind!=="keypair")return t.kind==="private"?e.privateKeyDer:t.kind==="public"?e.publicKeyDer:t.kind==="csr"?e.csrs?.find(n=>n.id===t.csrId)?.bytes:t.kind==="subjectdn"?e.subjectDns?.find(n=>n.id===t.subjectDnId)?.bytes:e.certificateDer}function cn(e){return e==="private"?"PKCS#8 DER":e==="public"?"SPKI DER":e==="csr"?"PKCS#10 CSR DER":e==="subjectdn"?"X.509 subject DER":"X.509 certificate DER"}function dn(e,t){if(t.kind==="private")return"private key";if(t.kind==="public")return"public key";if(t.kind==="csr"){const n=e.csrs?.find(i=>i.id===t.csrId);return n?`CSR ${n.subjectDn}`:"CSR"}if(t.kind==="subjectdn"){const n=e.subjectDns?.find(i=>i.id===t.subjectDnId);return n?`SubjectDN ${n.subjectDn}`:"SubjectDN"}return"certificate"}function g(e){X.disabled=e||F.length===0,fe.disabled=e,te.disabled=e||p.length===0;for(const t of P.querySelectorAll("button"))t.disabled=e;e&&G(!1)}function d(e,t=!1){Ee.textContent=e,Ee.classList.toggle("error",t)}function u(e,t,n="ok"){const i=document.createElement("div");i.className=`api-log-entry ${n}`;const a=new Date,r=document.createElement("time");r.dateTime=a.toISOString(),r.textContent=ln(a);const c=document.createElement("span");c.className="api-log-operation",c.textContent=e;const s=document.createElement("span");for(s.className="api-log-detail",s.textContent=t,i.append(r,c,s),w.append(i);w.childElementCount>vt;)w.firstElementChild?.remove();w.scrollTop=w.scrollHeight}function ln(e){const t=(n,i=2)=>String(n).padStart(i,"0");return`${e.getFullYear()}/${t(e.getMonth()+1)}/${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}.${t(e.getMilliseconds(),3)}`}function un(){const e=localStorage.getItem("pvkgadgets.keyPanelWidth"),t=e===null?NaN:Number(e);U(Number.isFinite(t)?t:360,!1),h.addEventListener("pointerdown",n=>{z()||(n.preventDefault(),h.setPointerCapture(n.pointerId),$.classList.add("resizing"),Ze(n.clientX))}),h.addEventListener("pointermove",n=>{h.hasPointerCapture(n.pointerId)&&Ze(n.clientX)}),h.addEventListener("pointerup",Je),h.addEventListener("pointercancel",Je),h.addEventListener("keydown",n=>{if(z()||n.key!=="ArrowLeft"&&n.key!=="ArrowRight"&&n.key!=="Home"&&n.key!=="End")return;n.preventDefault();const i=Number(getComputedStyle($).getPropertyValue("--key-panel-width").replace("px",""))||360;n.key==="Home"?U(280):n.key==="End"?U(Ce().max):U(i+(n.key==="ArrowLeft"?-24:24))}),window.addEventListener("resize",()=>{if(z())return;const n=Number(getComputedStyle($).getPropertyValue("--key-panel-width").replace("px",""))||360;U(n,!1)})}function pn(){const e=localStorage.getItem("pvkgadgets.apiLogListHeight"),t=e===null?NaN:Number(e),n=Number.isFinite(t)?t:w.getBoundingClientRect().height||120;O(n,!1),v.addEventListener("pointerdown",i=>{z()||(i.preventDefault(),v.setPointerCapture(i.pointerId),document.querySelector(".shell")?.classList.add("resizing-rows"),et(i.clientY))}),v.addEventListener("pointermove",i=>{v.hasPointerCapture(i.pointerId)&&et(i.clientY)}),v.addEventListener("pointerup",Qe),v.addEventListener("pointercancel",Qe),v.addEventListener("keydown",i=>{if(z()||i.key!=="ArrowUp"&&i.key!=="ArrowDown"&&i.key!=="Home"&&i.key!=="End")return;i.preventDefault();const a=Number(getComputedStyle(w).getPropertyValue("--api-log-list-height").replace("px",""))||w.getBoundingClientRect().height||120;i.key==="Home"?O(ue().min):i.key==="End"?O(ue().max):O(a+(i.key==="ArrowUp"?24:-24))}),window.addEventListener("resize",()=>{if(z())return;const i=Number(getComputedStyle(w).getPropertyValue("--api-log-list-height").replace("px",""))||w.getBoundingClientRect().height||120;O(i,!1)})}function Je(e){h.hasPointerCapture(e.pointerId)&&h.releasePointerCapture(e.pointerId),$.classList.remove("resizing")}function Qe(e){v.hasPointerCapture(e.pointerId)&&v.releasePointerCapture(e.pointerId),document.querySelector(".shell")?.classList.remove("resizing-rows")}function Ze(e){const t=Ce();U(e-t.left)}function U(e,t=!0){const n=Ce(),i=Math.round(Math.min(Math.max(e,n.min),n.max));$.style.setProperty("--key-panel-width",`${i}px`),h.setAttribute("aria-valuemin",String(n.min)),h.setAttribute("aria-valuemax",String(n.max)),h.setAttribute("aria-valuenow",String(i)),t&&localStorage.setItem("pvkgadgets.keyPanelWidth",String(i))}function et(e){const t=ue();O(t.bottom-e-t.resizerHeight-t.headerHeight-t.panelVerticalExtras)}function O(e,t=!0){const n=ue(),i=Math.round(Math.min(Math.max(e,n.min),n.max));w.style.setProperty("--api-log-list-height",`${i}px`),v.setAttribute("aria-valuemin",String(n.min)),v.setAttribute("aria-valuemax",String(n.max)),v.setAttribute("aria-valuenow",String(i)),t&&localStorage.setItem("pvkgadgets.apiLogListHeight",String(i))}function Ce(){const e=getComputedStyle($),t=$.getBoundingClientRect(),n=Number.parseFloat(e.paddingLeft)||0,i=Number.parseFloat(e.paddingRight)||0,a=Number.parseFloat(e.borderLeftWidth)||0,r=Number.parseFloat(e.columnGap)||0,c=h.getBoundingClientRect().width||6,s=$.clientWidth-n-i,D=280;return{left:t.left+a+n,min:D,max:Math.max(D,s-c-r*2-420)}}function ue(){const t=o(".shell").getBoundingClientRect(),n=$.getBoundingClientRect(),i=getComputedStyle(bt),a=o(".api-log-header").getBoundingClientRect().height,r=v.getBoundingClientRect().height||6,c=(Number.parseFloat(i.borderTopWidth)||0)+(Number.parseFloat(i.borderBottomWidth)||0)+(Number.parseFloat(i.paddingTop)||0)+(Number.parseFloat(i.paddingBottom)||0),s=60,D=260,nt=t.height-(n.top-t.top)-r-a-c;return{bottom:t.bottom,min:s,max:Math.max(s,nt-D),resizerHeight:r,headerHeight:a,panelVerticalExtras:c}}function z(){return window.matchMedia("(max-width: 820px)").matches}function x(e,t,n){if(!e||!t||!n){R.hidden=!0,V=null;return}C(!1),K(!1),N(!1),V=t;const i=p.find(s=>s.id===t),a=i?I(i):null,r=!!(i?.privateKeyDer&&i.publicKeyDer&&(a?.family==="RSA"||a?.family==="EC"));be.disabled=!r,be.title=r?"":"CSR creation currently supports RSA and EC signing keys.",ye.disabled=!r,ye.title=r?"":"Self-signed certificate creation currently supports RSA and EC signing keys.";const c=n.getBoundingClientRect();R.style.left=`${c.left}px`,R.style.top=`${c.bottom+2}px`,R.hidden=!1}function C(e,t,n){if(!e||!t||!n){A.hidden=!0,L=null;return}x(!1),K(!1),N(!1),L=t;const i=n.getBoundingClientRect();A.style.left=`${i.left}px`,A.style.top=`${i.bottom+2}px`,A.hidden=!1}function K(e,t,n){if(!e||!t||!n){B.hidden=!0,q=null;return}x(!1),C(!1),N(!1),q=t;const i=n.getBoundingClientRect();B.style.left=`${i.left}px`,B.style.top=`${i.bottom+2}px`,B.hidden=!1}function N(e,t,n){if(!e||!t||!n){M.hidden=!0,Y=null;return}x(!1),C(!1),K(!1),Y=t,De.hidden=t.kind!=="csr";const i=n.getBoundingClientRect();M.style.left=`${i.left}px`,M.style.top=`${i.bottom+2}px`,M.hidden=!1}function fn(e){const t=Y;return!!(t&&t.keyId===e.dataset.keyId&&t.kind===e.dataset.keyNode&&t.csrId===e.dataset.csrId&&t.subjectDnId===e.dataset.subjectDnId)}async function bn(){if(d("Detecting supported key pair algorithms..."),F=[],P.textContent="",F=await b.getSupportedKeyAlgorithms(),F.length===0){P.innerHTML='<button type="button" role="menuitem" disabled>No supported key pair algorithms</button>',d("This browser did not report support for the candidate key pair algorithms.",!0),g(!1);return}P.innerHTML=F.map(e=>`<button type="button" role="menuitem" data-algorithm="${f(e.id)}">${f(e.canonicalLabel)}</button>`).join(""),g(!1),d(`Detected ${F.length} supported key algorithms.`)}function G(e){P.hidden=!e,X.setAttribute("aria-expanded",String(e))}function I(e){return b.recognizeKeyMaterial(e)}function tt(){const e=ee.core??hn;if(!e)throw new Error("pkistudiojs CoreAPI could not be loaded.");return e}function pe(){return crypto.randomUUID?.()??String(Date.now())}function f(e){return e.replace(/[&<>"]/g,t=>{switch(t){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";default:return t}})}function o(e){const t=_.querySelector(e);if(!t)throw new Error(`Missing element: ${e}`);return t}function Z(e){return e.hidden===!0}return{get keys(){return p},get selectedNode(){return l},close(){m?.close?.()}}}function kn(y){const _=typeof y=="string"?document.querySelector(y):y;if(!(_ instanceof HTMLElement))throw new Error("Private Key Gadgets mount element was not found.");return _}export{En as initPrivateKeyGadgets};
@@ -0,0 +1 @@
1
+ (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function s(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=s(e);fetch(e.href,r)}})();