oidc-spa 8.2.12 → 8.3.1

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.
Files changed (41) hide show
  1. package/README.md +19 -5
  2. package/core/createOidc.js +9 -2
  3. package/core/createOidc.js.map +1 -1
  4. package/core/earlyInit.d.ts +6 -2
  5. package/core/earlyInit.js +168 -31
  6. package/core/earlyInit.js.map +1 -1
  7. package/core/loginSilent.js +7 -42
  8. package/core/loginSilent.js.map +1 -1
  9. package/esm/core/createOidc.js +9 -2
  10. package/esm/core/createOidc.js.map +1 -1
  11. package/esm/core/earlyInit.d.ts +6 -2
  12. package/esm/core/earlyInit.js +167 -31
  13. package/esm/core/earlyInit.js.map +1 -1
  14. package/esm/core/loginSilent.js +7 -42
  15. package/esm/core/loginSilent.js.map +1 -1
  16. package/esm/tools/Evt.js +18 -10
  17. package/esm/tools/Evt.js.map +1 -1
  18. package/package.json +2 -2
  19. package/src/core/createOidc.ts +8 -1
  20. package/src/core/earlyInit.ts +220 -40
  21. package/src/core/loginSilent.ts +18 -79
  22. package/src/tools/Evt.ts +17 -16
  23. package/src/vite-plugin/handleClientEntrypoint.ts +4 -6
  24. package/tools/Evt.js +18 -10
  25. package/tools/Evt.js.map +1 -1
  26. package/vite-plugin/handleClientEntrypoint.js +3 -1
  27. package/vite-plugin/handleClientEntrypoint.js.map +1 -1
  28. package/core/iframeMessageProtection.d.ts +0 -32
  29. package/core/iframeMessageProtection.js +0 -154
  30. package/core/iframeMessageProtection.js.map +0 -1
  31. package/esm/core/iframeMessageProtection.d.ts +0 -32
  32. package/esm/core/iframeMessageProtection.js +0 -149
  33. package/esm/core/iframeMessageProtection.js.map +0 -1
  34. package/esm/tools/asymmetricEncryption.d.ts +0 -18
  35. package/esm/tools/asymmetricEncryption.js +0 -85
  36. package/esm/tools/asymmetricEncryption.js.map +0 -1
  37. package/src/core/iframeMessageProtection.ts +0 -219
  38. package/src/tools/asymmetricEncryption.ts +0 -184
  39. package/tools/asymmetricEncryption.d.ts +0 -18
  40. package/tools/asymmetricEncryption.js +0 -90
  41. package/tools/asymmetricEncryption.js.map +0 -1
@@ -1,149 +0,0 @@
1
- import { assert } from "../tools/tsafe/assert";
2
- import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
3
- let capturedApis = undefined;
4
- const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
5
- const getProtectedTimer_set = new Set();
6
- /**
7
- * To call while still in the safe window where no other code
8
- * has been evaluated and only before we're about to actually start the App.
9
- */
10
- export function iframeMessageProtection_captureAndLockBuiltins() {
11
- capturedApis = {
12
- setItem: Storage.prototype.setItem,
13
- sessionStorage: window.sessionStorage,
14
- setTimeout: window.setTimeout,
15
- clearTimeout: window.clearTimeout,
16
- alert: window.alert
17
- };
18
- // Ensure, at least from main window we cannot simply write on the public key.
19
- {
20
- const setItem_protected = function setItem(key, value) {
21
- if (key.startsWith(SESSION_STORAGE_PREFIX)) {
22
- throw new Error("Attack prevented by oidc-spa. You have malicious code running in your system");
23
- }
24
- assert(capturedApis !== undefined);
25
- return capturedApis.setItem.call(this, key, value);
26
- };
27
- {
28
- const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
29
- assert(pd !== undefined);
30
- Object.defineProperty(Storage.prototype, "setItem", {
31
- enumerable: pd.enumerable,
32
- writable: pd.writable,
33
- value: setItem_protected
34
- });
35
- }
36
- }
37
- window.clearTimeout = function clearTimeout(timer) {
38
- for (const getProtectedTimer of getProtectedTimer_set) {
39
- const timer_protected = getProtectedTimer();
40
- if (timer_protected === undefined) {
41
- continue;
42
- }
43
- if (timer_protected === timer) {
44
- // Probably an attack but potentially not so avoiding hard crash
45
- return;
46
- }
47
- }
48
- assert(capturedApis !== undefined);
49
- capturedApis.clearTimeout.call(window, timer);
50
- };
51
- }
52
- function getSessionStorageKey(params) {
53
- const { stateUrlParamValue } = params;
54
- return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
55
- }
56
- const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
57
- function getIsEncryptedAuthResponse(params) {
58
- const { message, stateUrlParamValue } = params;
59
- return (typeof message === "string" &&
60
- message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`));
61
- }
62
- function getReadyMessage(params) {
63
- const { stateUrlParamValue } = params;
64
- return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
65
- }
66
- function getIsReadyToReadPublicKeyMessage(params) {
67
- const { message, stateUrlParamValue } = params;
68
- return message === getReadyMessage({ stateUrlParamValue });
69
- }
70
- export async function initIframeMessageProtection(params) {
71
- const { stateUrlParamValue } = params;
72
- const { publicKey, privateKey } = await generateKeys();
73
- const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
74
- let timer = undefined;
75
- const getProtectedTimer = () => timer;
76
- getProtectedTimer_set.add(getProtectedTimer);
77
- function setSessionStoragePublicKey() {
78
- assert(capturedApis !== undefined);
79
- const { setItem } = capturedApis;
80
- setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
81
- }
82
- function startSessionStoragePublicKeyMaliciousWriteDetection() {
83
- assert(capturedApis !== undefined);
84
- const { alert, setTimeout } = capturedApis;
85
- sessionStorage.removeItem(sessionStorageKey);
86
- const checkTimeoutCallback = () => {
87
- const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
88
- if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
89
- while (true) {
90
- alert([
91
- "⚠️ Security Alert:",
92
- "oidc-spa detected an attack attempt.",
93
- "For your safety, please close this tab immediately",
94
- "and notify the site administrator."
95
- ].join(" "));
96
- }
97
- }
98
- check();
99
- };
100
- function check() {
101
- timer = setTimeout(checkTimeoutCallback, 5);
102
- }
103
- check();
104
- }
105
- async function decodeEncryptedAuth(params) {
106
- const { encryptedAuthResponse } = params;
107
- const { message: authResponse_str } = await asymmetricDecrypt({
108
- encryptedMessage: encryptedAuthResponse.slice(ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length),
109
- privateKey
110
- });
111
- const authResponse = JSON.parse(authResponse_str);
112
- return { authResponse };
113
- }
114
- function clearSessionStoragePublicKey() {
115
- assert(capturedApis !== undefined);
116
- const { clearTimeout } = capturedApis;
117
- sessionStorage.removeItem(sessionStorageKey);
118
- clearTimeout(timer);
119
- getProtectedTimer_set.delete(getProtectedTimer);
120
- }
121
- return {
122
- getIsReadyToReadPublicKeyMessage,
123
- startSessionStoragePublicKeyMaliciousWriteDetection,
124
- setSessionStoragePublicKey,
125
- getIsEncryptedAuthResponse,
126
- decodeEncryptedAuth,
127
- clearSessionStoragePublicKey
128
- };
129
- }
130
- export async function postEncryptedAuthResponseToParent(params) {
131
- const { authResponse } = params;
132
- parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
133
- await new Promise(resolve => setTimeout(resolve, 2));
134
- let publicKey;
135
- {
136
- let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
137
- while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
138
- await new Promise(resolve => setTimeout(resolve, 2));
139
- }
140
- }
141
- await new Promise(resolve => setTimeout(resolve, 7));
142
- const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
143
- publicKey,
144
- message: JSON.stringify(authResponse)
145
- });
146
- const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
147
- parent.postMessage(encryptedMessage, location.origin);
148
- }
149
- //# sourceMappingURL=iframeMessageProtection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"iframeMessageProtection.js","sourceRoot":"","sources":["../../src/core/iframeMessageProtection.ts"],"names":[],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- type AsymmetricKeys = {
2
- publicKey: string;
3
- privateKey: string;
4
- };
5
- export declare function generateKeys(): Promise<AsymmetricKeys>;
6
- export declare function asymmetricEncrypt(params: {
7
- publicKey: string;
8
- message: string;
9
- }): Promise<{
10
- encryptedMessage: string;
11
- }>;
12
- export declare function asymmetricDecrypt(params: {
13
- privateKey: string;
14
- encryptedMessage: string;
15
- }): Promise<{
16
- message: string;
17
- }>;
18
- export {};
@@ -1,85 +0,0 @@
1
- const INFO_LABEL = "oidc-spa/tools/asymmetricEncryption";
2
- export async function generateKeys() {
3
- const keyPair = await crypto.subtle.generateKey({
4
- name: "ECDH",
5
- namedCurve: "P-256"
6
- }, true, ["deriveKey", "deriveBits"]);
7
- const publicKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
8
- const privateKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
9
- return {
10
- publicKey: btoa(JSON.stringify(publicKeyRaw)),
11
- privateKey: btoa(JSON.stringify(privateKeyRaw))
12
- };
13
- }
14
- export async function asymmetricEncrypt(params) {
15
- const { publicKey, message } = params;
16
- const importedPublicKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(publicKey)), {
17
- name: "ECDH",
18
- namedCurve: "P-256"
19
- }, false, []);
20
- const ephemeralKeyPair = await crypto.subtle.generateKey({
21
- name: "ECDH",
22
- namedCurve: "P-256"
23
- }, true, ["deriveKey", "deriveBits"]);
24
- const sharedSecret = await crypto.subtle.deriveBits({
25
- name: "ECDH",
26
- public: importedPublicKey
27
- }, ephemeralKeyPair.privateKey, 256);
28
- const salt = crypto.getRandomValues(new Uint8Array(16));
29
- const infoBytes = new TextEncoder().encode(INFO_LABEL);
30
- const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
31
- const derivedKey = await crypto.subtle.deriveKey({
32
- name: "HKDF",
33
- hash: "SHA-256",
34
- salt,
35
- info: infoBytes
36
- }, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["encrypt"]);
37
- const iv = crypto.getRandomValues(new Uint8Array(12));
38
- const encodedMessage = new TextEncoder().encode(message);
39
- const ciphertext = await crypto.subtle.encrypt({
40
- name: "AES-GCM",
41
- iv
42
- }, derivedKey, encodedMessage);
43
- const ephemeralPubKeyRaw = await crypto.subtle.exportKey("jwk", ephemeralKeyPair.publicKey);
44
- const payload = {
45
- ephemeralPubKey: ephemeralPubKeyRaw,
46
- iv: Array.from(iv),
47
- salt: Array.from(salt),
48
- ciphertext: Array.from(new Uint8Array(ciphertext))
49
- };
50
- return {
51
- encryptedMessage: btoa(JSON.stringify(payload))
52
- };
53
- }
54
- export async function asymmetricDecrypt(params) {
55
- const { privateKey, encryptedMessage } = params;
56
- const { ephemeralPubKey, iv, salt, ciphertext } = JSON.parse(atob(encryptedMessage));
57
- const importedPrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(privateKey)), {
58
- name: "ECDH",
59
- namedCurve: "P-256"
60
- }, false, ["deriveKey", "deriveBits"]);
61
- const importedEphemeralPubKey = await crypto.subtle.importKey("jwk", ephemeralPubKey, {
62
- name: "ECDH",
63
- namedCurve: "P-256"
64
- }, false, []);
65
- const sharedSecret = await crypto.subtle.deriveBits({
66
- name: "ECDH",
67
- public: importedEphemeralPubKey
68
- }, importedPrivateKey, 256);
69
- const infoBytes = new TextEncoder().encode(INFO_LABEL);
70
- const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
71
- const derivedKey = await crypto.subtle.deriveKey({
72
- name: "HKDF",
73
- hash: "SHA-256",
74
- salt: new Uint8Array(salt),
75
- info: infoBytes
76
- }, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["decrypt"]);
77
- const decryptedBuffer = await crypto.subtle.decrypt({
78
- name: "AES-GCM",
79
- iv: new Uint8Array(iv)
80
- }, derivedKey, new Uint8Array(ciphertext));
81
- return {
82
- message: new TextDecoder().decode(decryptedBuffer)
83
- };
84
- }
85
- //# sourceMappingURL=asymmetricEncryption.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"asymmetricEncryption.js","sourceRoot":"","sources":["../../src/tools/asymmetricEncryption.ts"],"names":[],"mappings":"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"}
@@ -1,219 +0,0 @@
1
- import { assert } from "../tools/tsafe/assert";
2
- import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
3
- import { type AuthResponse } from "./AuthResponse";
4
-
5
- let capturedApis:
6
- | {
7
- setItem: typeof localStorage.setItem;
8
- sessionStorage: typeof window.sessionStorage;
9
- setTimeout: typeof window.setTimeout;
10
- clearTimeout: typeof window.clearTimeout;
11
- alert: typeof window.alert;
12
- }
13
- | undefined = undefined;
14
-
15
- const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
16
-
17
- const getProtectedTimer_set = new Set<() => number | undefined>();
18
-
19
- /**
20
- * To call while still in the safe window where no other code
21
- * has been evaluated and only before we're about to actually start the App.
22
- */
23
- export function iframeMessageProtection_captureAndLockBuiltins() {
24
- capturedApis = {
25
- setItem: Storage.prototype.setItem,
26
- sessionStorage: window.sessionStorage,
27
- setTimeout: window.setTimeout,
28
- clearTimeout: window.clearTimeout,
29
- alert: window.alert
30
- };
31
-
32
- // Ensure, at least from main window we cannot simply write on the public key.
33
- {
34
- const setItem_protected = function setItem(this: any, key: string, value: string): void {
35
- if (key.startsWith(SESSION_STORAGE_PREFIX)) {
36
- throw new Error(
37
- "Attack prevented by oidc-spa. You have malicious code running in your system"
38
- );
39
- }
40
-
41
- assert(capturedApis !== undefined);
42
-
43
- return capturedApis.setItem.call(this, key, value);
44
- };
45
-
46
- {
47
- const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
48
-
49
- assert(pd !== undefined);
50
-
51
- Object.defineProperty(Storage.prototype, "setItem", {
52
- enumerable: pd.enumerable,
53
- writable: pd.writable,
54
- value: setItem_protected
55
- });
56
- }
57
- }
58
-
59
- window.clearTimeout = function clearTimeout(timer) {
60
- for (const getProtectedTimer of getProtectedTimer_set) {
61
- const timer_protected = getProtectedTimer();
62
- if (timer_protected === undefined) {
63
- continue;
64
- }
65
- if (timer_protected === timer) {
66
- // Probably an attack but potentially not so avoiding hard crash
67
- return;
68
- }
69
- }
70
-
71
- assert(capturedApis !== undefined);
72
-
73
- capturedApis.clearTimeout.call(window, timer);
74
- };
75
- }
76
-
77
- function getSessionStorageKey(params: { stateUrlParamValue: string }) {
78
- const { stateUrlParamValue } = params;
79
-
80
- return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
81
- }
82
-
83
- const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
84
-
85
- function getIsEncryptedAuthResponse(params: { message: unknown; stateUrlParamValue: string }): boolean {
86
- const { message, stateUrlParamValue } = params;
87
-
88
- return (
89
- typeof message === "string" &&
90
- message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`)
91
- );
92
- }
93
-
94
- function getReadyMessage(params: { stateUrlParamValue: string }) {
95
- const { stateUrlParamValue } = params;
96
- return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
97
- }
98
-
99
- function getIsReadyToReadPublicKeyMessage(params: { message: unknown; stateUrlParamValue: string }) {
100
- const { message, stateUrlParamValue } = params;
101
- return message === getReadyMessage({ stateUrlParamValue });
102
- }
103
-
104
- export async function initIframeMessageProtection(params: { stateUrlParamValue: string }) {
105
- const { stateUrlParamValue } = params;
106
-
107
- const { publicKey, privateKey } = await generateKeys();
108
-
109
- const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
110
-
111
- let timer: number | undefined = undefined;
112
-
113
- const getProtectedTimer = () => timer;
114
-
115
- getProtectedTimer_set.add(getProtectedTimer);
116
-
117
- function setSessionStoragePublicKey() {
118
- assert(capturedApis !== undefined);
119
-
120
- const { setItem } = capturedApis;
121
-
122
- setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
123
- }
124
-
125
- function startSessionStoragePublicKeyMaliciousWriteDetection() {
126
- assert(capturedApis !== undefined);
127
-
128
- const { alert, setTimeout } = capturedApis;
129
-
130
- sessionStorage.removeItem(sessionStorageKey);
131
-
132
- const checkTimeoutCallback = () => {
133
- const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
134
-
135
- if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
136
- while (true) {
137
- alert(
138
- [
139
- "⚠️ Security Alert:",
140
- "oidc-spa detected an attack attempt.",
141
- "For your safety, please close this tab immediately",
142
- "and notify the site administrator."
143
- ].join(" ")
144
- );
145
- }
146
- }
147
- check();
148
- };
149
-
150
- function check() {
151
- timer = setTimeout(checkTimeoutCallback, 5);
152
- }
153
-
154
- check();
155
- }
156
-
157
- async function decodeEncryptedAuth(params: {
158
- encryptedAuthResponse: string;
159
- }): Promise<{ authResponse: AuthResponse }> {
160
- const { encryptedAuthResponse } = params;
161
-
162
- const { message: authResponse_str } = await asymmetricDecrypt({
163
- encryptedMessage: encryptedAuthResponse.slice(
164
- ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length
165
- ),
166
- privateKey
167
- });
168
-
169
- const authResponse: AuthResponse = JSON.parse(authResponse_str);
170
-
171
- return { authResponse };
172
- }
173
-
174
- function clearSessionStoragePublicKey() {
175
- assert(capturedApis !== undefined);
176
- const { clearTimeout } = capturedApis;
177
- sessionStorage.removeItem(sessionStorageKey);
178
- clearTimeout(timer);
179
- getProtectedTimer_set.delete(getProtectedTimer);
180
- }
181
-
182
- return {
183
- getIsReadyToReadPublicKeyMessage,
184
- startSessionStoragePublicKeyMaliciousWriteDetection,
185
- setSessionStoragePublicKey,
186
- getIsEncryptedAuthResponse,
187
- decodeEncryptedAuth,
188
- clearSessionStoragePublicKey
189
- };
190
- }
191
-
192
- export async function postEncryptedAuthResponseToParent(params: { authResponse: AuthResponse }) {
193
- const { authResponse } = params;
194
-
195
- parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
196
-
197
- await new Promise<void>(resolve => setTimeout(resolve, 2));
198
-
199
- let publicKey: string | null;
200
-
201
- {
202
- let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
203
-
204
- while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
205
- await new Promise<void>(resolve => setTimeout(resolve, 2));
206
- }
207
- }
208
-
209
- await new Promise<void>(resolve => setTimeout(resolve, 7));
210
-
211
- const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
212
- publicKey,
213
- message: JSON.stringify(authResponse)
214
- });
215
-
216
- const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
217
-
218
- parent.postMessage(encryptedMessage, location.origin);
219
- }
@@ -1,184 +0,0 @@
1
- type AsymmetricKeys = {
2
- publicKey: string; // base64-encoded JSON export of CryptoKey
3
- privateKey: string; // base64-encoded JSON export of CryptoKey
4
- };
5
-
6
- const INFO_LABEL = "oidc-spa/tools/asymmetricEncryption";
7
-
8
- export async function generateKeys(): Promise<AsymmetricKeys> {
9
- const keyPair = await crypto.subtle.generateKey(
10
- {
11
- name: "ECDH",
12
- namedCurve: "P-256"
13
- },
14
- true,
15
- ["deriveKey", "deriveBits"]
16
- );
17
-
18
- const publicKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
19
- const privateKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
20
-
21
- return {
22
- publicKey: btoa(JSON.stringify(publicKeyRaw)),
23
- privateKey: btoa(JSON.stringify(privateKeyRaw))
24
- };
25
- }
26
-
27
- export async function asymmetricEncrypt(params: {
28
- publicKey: string;
29
- message: string;
30
- }): Promise<{ encryptedMessage: string }> {
31
- const { publicKey, message } = params;
32
-
33
- const importedPublicKey = await crypto.subtle.importKey(
34
- "jwk",
35
- JSON.parse(atob(publicKey)),
36
- {
37
- name: "ECDH",
38
- namedCurve: "P-256"
39
- },
40
- false,
41
- []
42
- );
43
-
44
- const ephemeralKeyPair = await crypto.subtle.generateKey(
45
- {
46
- name: "ECDH",
47
- namedCurve: "P-256"
48
- },
49
- true,
50
- ["deriveKey", "deriveBits"]
51
- );
52
-
53
- const sharedSecret = await crypto.subtle.deriveBits(
54
- {
55
- name: "ECDH",
56
- public: importedPublicKey
57
- },
58
- ephemeralKeyPair.privateKey,
59
- 256
60
- );
61
-
62
- const salt = crypto.getRandomValues(new Uint8Array(16));
63
- const infoBytes = new TextEncoder().encode(INFO_LABEL);
64
-
65
- const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
66
-
67
- const derivedKey = await crypto.subtle.deriveKey(
68
- {
69
- name: "HKDF",
70
- hash: "SHA-256",
71
- salt,
72
- info: infoBytes
73
- },
74
- hkdfKey,
75
- { name: "AES-GCM", length: 256 },
76
- false,
77
- ["encrypt"]
78
- );
79
-
80
- const iv = crypto.getRandomValues(new Uint8Array(12));
81
- const encodedMessage = new TextEncoder().encode(message);
82
-
83
- const ciphertext = await crypto.subtle.encrypt(
84
- {
85
- name: "AES-GCM",
86
- iv
87
- },
88
- derivedKey,
89
- encodedMessage
90
- );
91
-
92
- const ephemeralPubKeyRaw = await crypto.subtle.exportKey("jwk", ephemeralKeyPair.publicKey);
93
-
94
- const payload = {
95
- ephemeralPubKey: ephemeralPubKeyRaw,
96
- iv: Array.from(iv),
97
- salt: Array.from(salt),
98
- ciphertext: Array.from(new Uint8Array(ciphertext))
99
- };
100
-
101
- return {
102
- encryptedMessage: btoa(JSON.stringify(payload))
103
- };
104
- }
105
-
106
- export async function asymmetricDecrypt(params: {
107
- privateKey: string;
108
- encryptedMessage: string;
109
- }): Promise<{ message: string }> {
110
- const { privateKey, encryptedMessage } = params;
111
-
112
- const {
113
- ephemeralPubKey,
114
- iv,
115
- salt,
116
- ciphertext
117
- }: {
118
- ephemeralPubKey: JsonWebKey;
119
- iv: number[];
120
- salt: number[];
121
- ciphertext: number[];
122
- } = JSON.parse(atob(encryptedMessage));
123
-
124
- const importedPrivateKey = await crypto.subtle.importKey(
125
- "jwk",
126
- JSON.parse(atob(privateKey)),
127
- {
128
- name: "ECDH",
129
- namedCurve: "P-256"
130
- },
131
- false,
132
- ["deriveKey", "deriveBits"]
133
- );
134
-
135
- const importedEphemeralPubKey = await crypto.subtle.importKey(
136
- "jwk",
137
- ephemeralPubKey,
138
- {
139
- name: "ECDH",
140
- namedCurve: "P-256"
141
- },
142
- false,
143
- []
144
- );
145
-
146
- const sharedSecret = await crypto.subtle.deriveBits(
147
- {
148
- name: "ECDH",
149
- public: importedEphemeralPubKey
150
- },
151
- importedPrivateKey,
152
- 256
153
- );
154
-
155
- const infoBytes = new TextEncoder().encode(INFO_LABEL);
156
-
157
- const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
158
-
159
- const derivedKey = await crypto.subtle.deriveKey(
160
- {
161
- name: "HKDF",
162
- hash: "SHA-256",
163
- salt: new Uint8Array(salt),
164
- info: infoBytes
165
- },
166
- hkdfKey,
167
- { name: "AES-GCM", length: 256 },
168
- false,
169
- ["decrypt"]
170
- );
171
-
172
- const decryptedBuffer = await crypto.subtle.decrypt(
173
- {
174
- name: "AES-GCM",
175
- iv: new Uint8Array(iv)
176
- },
177
- derivedKey,
178
- new Uint8Array(ciphertext)
179
- );
180
-
181
- return {
182
- message: new TextDecoder().decode(decryptedBuffer)
183
- };
184
- }