oidc-spa 8.2.11 → 8.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.
Files changed (44) hide show
  1. package/README.md +58 -6
  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 +157 -32
  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 +156 -32
  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/tanstack-start/react/withHandlingOidcPostLoginNavigation.js +13 -2
  17. package/esm/tanstack-start/react/withHandlingOidcPostLoginNavigation.js.map +1 -1
  18. package/esm/tools/Evt.js +18 -10
  19. package/esm/tools/Evt.js.map +1 -1
  20. package/package.json +2 -2
  21. package/src/core/createOidc.ts +8 -1
  22. package/src/core/earlyInit.ts +205 -42
  23. package/src/core/loginSilent.ts +18 -79
  24. package/src/tanstack-start/react/withHandlingOidcPostLoginNavigation.tsx +13 -2
  25. package/src/tools/Evt.ts +17 -16
  26. package/src/vite-plugin/handleClientEntrypoint.ts +4 -6
  27. package/tools/Evt.js +18 -10
  28. package/tools/Evt.js.map +1 -1
  29. package/vite-plugin/handleClientEntrypoint.js +3 -1
  30. package/vite-plugin/handleClientEntrypoint.js.map +1 -1
  31. package/core/iframeMessageProtection.d.ts +0 -29
  32. package/core/iframeMessageProtection.js +0 -129
  33. package/core/iframeMessageProtection.js.map +0 -1
  34. package/esm/core/iframeMessageProtection.d.ts +0 -29
  35. package/esm/core/iframeMessageProtection.js +0 -123
  36. package/esm/core/iframeMessageProtection.js.map +0 -1
  37. package/esm/tools/asymmetricEncryption.d.ts +0 -18
  38. package/esm/tools/asymmetricEncryption.js +0 -85
  39. package/esm/tools/asymmetricEncryption.js.map +0 -1
  40. package/src/core/iframeMessageProtection.ts +0 -186
  41. package/src/tools/asymmetricEncryption.ts +0 -184
  42. package/tools/asymmetricEncryption.d.ts +0 -18
  43. package/tools/asymmetricEncryption.js +0 -90
  44. package/tools/asymmetricEncryption.js.map +0 -1
@@ -1,123 +0,0 @@
1
- import { assert } from "../tools/tsafe/assert";
2
- import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
3
- let capturedApis = undefined;
4
- export function captureApisForIframeProtection() {
5
- capturedApis = {
6
- setItem: Storage.prototype.setItem,
7
- sessionStorage: window.sessionStorage,
8
- setTimeout: window.setTimeout,
9
- alert: window.alert
10
- };
11
- }
12
- const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
13
- export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
14
- const setItem_protected = function setItem(key, value) {
15
- if (key.startsWith(SESSION_STORAGE_PREFIX)) {
16
- throw new Error("Attack prevented by oidc-spa. You have malicious code running in your system");
17
- }
18
- assert(capturedApis !== undefined);
19
- return capturedApis.setItem.call(this, key, value);
20
- };
21
- {
22
- const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
23
- assert(pd !== undefined);
24
- Object.defineProperty(Storage.prototype, "setItem", {
25
- enumerable: pd.enumerable,
26
- writable: pd.writable,
27
- value: setItem_protected
28
- });
29
- }
30
- }
31
- function getSessionStorageKey(params) {
32
- const { stateUrlParamValue } = params;
33
- return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
34
- }
35
- const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
36
- function getIsEncryptedAuthResponse(params) {
37
- const { message, stateUrlParamValue } = params;
38
- return (typeof message === "string" &&
39
- message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`));
40
- }
41
- function getReadyMessage(params) {
42
- const { stateUrlParamValue } = params;
43
- return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
44
- }
45
- function getIsReadyToReadPublicKeyMessage(params) {
46
- const { message, stateUrlParamValue } = params;
47
- return message === getReadyMessage({ stateUrlParamValue });
48
- }
49
- export async function initIframeMessageProtection(params) {
50
- const { stateUrlParamValue } = params;
51
- const { publicKey, privateKey } = await generateKeys();
52
- const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
53
- let timer = undefined;
54
- function setSessionStoragePublicKey() {
55
- assert(capturedApis !== undefined);
56
- const { setItem } = capturedApis;
57
- setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
58
- }
59
- function startSessionStoragePublicKeyMaliciousWriteDetection() {
60
- assert(capturedApis !== undefined);
61
- const { alert, setTimeout } = capturedApis;
62
- sessionStorage.removeItem(sessionStorageKey);
63
- const checkTimeoutCallback = () => {
64
- const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
65
- if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
66
- while (true) {
67
- alert([
68
- "⚠️ Security Alert:",
69
- "oidc-spa detected an attack attempt.",
70
- "For your safety, please close this tab immediately",
71
- "and notify the site administrator."
72
- ].join(" "));
73
- }
74
- }
75
- check();
76
- };
77
- function check() {
78
- timer = setTimeout(checkTimeoutCallback, 5);
79
- }
80
- check();
81
- }
82
- async function decodeEncryptedAuth(params) {
83
- const { encryptedAuthResponse } = params;
84
- const { message: authResponse_str } = await asymmetricDecrypt({
85
- encryptedMessage: encryptedAuthResponse.slice(ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length),
86
- privateKey
87
- });
88
- const authResponse = JSON.parse(authResponse_str);
89
- return { authResponse };
90
- }
91
- function clearSessionStoragePublicKey() {
92
- sessionStorage.removeItem(sessionStorageKey);
93
- clearTimeout(timer);
94
- }
95
- return {
96
- getIsReadyToReadPublicKeyMessage,
97
- startSessionStoragePublicKeyMaliciousWriteDetection,
98
- setSessionStoragePublicKey,
99
- getIsEncryptedAuthResponse,
100
- decodeEncryptedAuth,
101
- clearSessionStoragePublicKey
102
- };
103
- }
104
- export async function postEncryptedAuthResponseToParent(params) {
105
- const { authResponse } = params;
106
- parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
107
- await new Promise(resolve => setTimeout(resolve, 2));
108
- let publicKey;
109
- {
110
- let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
111
- while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
112
- await new Promise(resolve => setTimeout(resolve, 2));
113
- }
114
- }
115
- await new Promise(resolve => setTimeout(resolve, 7));
116
- const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
117
- publicKey,
118
- message: JSON.stringify(authResponse)
119
- });
120
- const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
121
- parent.postMessage(encryptedMessage, location.origin);
122
- }
123
- //# 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,186 +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
- alert: typeof window.alert;
11
- }
12
- | undefined = undefined;
13
-
14
- export function captureApisForIframeProtection() {
15
- capturedApis = {
16
- setItem: Storage.prototype.setItem,
17
- sessionStorage: window.sessionStorage,
18
- setTimeout: window.setTimeout,
19
- alert: window.alert
20
- };
21
- }
22
-
23
- const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
24
-
25
- export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
26
- const setItem_protected = function setItem(this: any, key: string, value: string): void {
27
- if (key.startsWith(SESSION_STORAGE_PREFIX)) {
28
- throw new Error(
29
- "Attack prevented by oidc-spa. You have malicious code running in your system"
30
- );
31
- }
32
-
33
- assert(capturedApis !== undefined);
34
-
35
- return capturedApis.setItem.call(this, key, value);
36
- };
37
-
38
- {
39
- const pd = Object.getOwnPropertyDescriptor(Storage.prototype, "setItem");
40
-
41
- assert(pd !== undefined);
42
-
43
- Object.defineProperty(Storage.prototype, "setItem", {
44
- enumerable: pd.enumerable,
45
- writable: pd.writable,
46
- value: setItem_protected
47
- });
48
- }
49
- }
50
-
51
- function getSessionStorageKey(params: { stateUrlParamValue: string }) {
52
- const { stateUrlParamValue } = params;
53
-
54
- return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
55
- }
56
-
57
- const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
58
-
59
- function getIsEncryptedAuthResponse(params: { message: unknown; stateUrlParamValue: string }): boolean {
60
- const { message, stateUrlParamValue } = params;
61
-
62
- return (
63
- typeof message === "string" &&
64
- message.startsWith(`${ENCRYPTED_AUTH_RESPONSES_PREFIX}${stateUrlParamValue}`)
65
- );
66
- }
67
-
68
- function getReadyMessage(params: { stateUrlParamValue: string }) {
69
- const { stateUrlParamValue } = params;
70
- return `oidc-spa_ready_to_read_publicKey_${stateUrlParamValue}`;
71
- }
72
-
73
- function getIsReadyToReadPublicKeyMessage(params: { message: unknown; stateUrlParamValue: string }) {
74
- const { message, stateUrlParamValue } = params;
75
- return message === getReadyMessage({ stateUrlParamValue });
76
- }
77
-
78
- export async function initIframeMessageProtection(params: { stateUrlParamValue: string }) {
79
- const { stateUrlParamValue } = params;
80
-
81
- const { publicKey, privateKey } = await generateKeys();
82
-
83
- const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
84
-
85
- let timer: number | undefined = undefined;
86
-
87
- function setSessionStoragePublicKey() {
88
- assert(capturedApis !== undefined);
89
-
90
- const { setItem } = capturedApis;
91
-
92
- setItem.call(capturedApis.sessionStorage, sessionStorageKey, publicKey);
93
- }
94
-
95
- function startSessionStoragePublicKeyMaliciousWriteDetection() {
96
- assert(capturedApis !== undefined);
97
-
98
- const { alert, setTimeout } = capturedApis;
99
-
100
- sessionStorage.removeItem(sessionStorageKey);
101
-
102
- const checkTimeoutCallback = () => {
103
- const publicKey_inStorage = sessionStorage.getItem(sessionStorageKey);
104
-
105
- if (publicKey_inStorage !== null && publicKey_inStorage !== publicKey) {
106
- while (true) {
107
- alert(
108
- [
109
- "⚠️ Security Alert:",
110
- "oidc-spa detected an attack attempt.",
111
- "For your safety, please close this tab immediately",
112
- "and notify the site administrator."
113
- ].join(" ")
114
- );
115
- }
116
- }
117
- check();
118
- };
119
-
120
- function check() {
121
- timer = setTimeout(checkTimeoutCallback, 5);
122
- }
123
-
124
- check();
125
- }
126
-
127
- async function decodeEncryptedAuth(params: {
128
- encryptedAuthResponse: string;
129
- }): Promise<{ authResponse: AuthResponse }> {
130
- const { encryptedAuthResponse } = params;
131
-
132
- const { message: authResponse_str } = await asymmetricDecrypt({
133
- encryptedMessage: encryptedAuthResponse.slice(
134
- ENCRYPTED_AUTH_RESPONSES_PREFIX.length + stateUrlParamValue.length
135
- ),
136
- privateKey
137
- });
138
-
139
- const authResponse: AuthResponse = JSON.parse(authResponse_str);
140
-
141
- return { authResponse };
142
- }
143
-
144
- function clearSessionStoragePublicKey() {
145
- sessionStorage.removeItem(sessionStorageKey);
146
- clearTimeout(timer);
147
- }
148
-
149
- return {
150
- getIsReadyToReadPublicKeyMessage,
151
- startSessionStoragePublicKeyMaliciousWriteDetection,
152
- setSessionStoragePublicKey,
153
- getIsEncryptedAuthResponse,
154
- decodeEncryptedAuth,
155
- clearSessionStoragePublicKey
156
- };
157
- }
158
-
159
- export async function postEncryptedAuthResponseToParent(params: { authResponse: AuthResponse }) {
160
- const { authResponse } = params;
161
-
162
- parent.postMessage(getReadyMessage({ stateUrlParamValue: authResponse.state }), location.origin);
163
-
164
- await new Promise<void>(resolve => setTimeout(resolve, 2));
165
-
166
- let publicKey: string | null;
167
-
168
- {
169
- let sessionStorageKey = getSessionStorageKey({ stateUrlParamValue: authResponse.state });
170
-
171
- while ((publicKey = sessionStorage.getItem(sessionStorageKey)) === null) {
172
- await new Promise<void>(resolve => setTimeout(resolve, 2));
173
- }
174
- }
175
-
176
- await new Promise<void>(resolve => setTimeout(resolve, 7));
177
-
178
- const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
179
- publicKey,
180
- message: JSON.stringify(authResponse)
181
- });
182
-
183
- const encryptedMessage = `${ENCRYPTED_AUTH_RESPONSES_PREFIX}${authResponse.state}${encryptedMessage_withoutPrefix}`;
184
-
185
- parent.postMessage(encryptedMessage, location.origin);
186
- }
@@ -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
- }
@@ -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 {};