curtain-web-api 1.0.77 → 1.0.79
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/build/classes/curtain-api.d.ts +11 -1
- package/build/classes/curtain-api.js +13 -3
- package/build/classes/curtain-encryption.d.ts +1 -0
- package/build/classes/curtain-encryption.js +21 -0
- package/package.json +1 -1
- package/src/classes/curtain-api.ts +24 -2
- package/src/classes/curtain-encryption.ts +198 -176
|
@@ -98,16 +98,25 @@ export interface CreatePermanentLinkRequest {
|
|
|
98
98
|
requested_expiry_months?: number;
|
|
99
99
|
reason?: string;
|
|
100
100
|
}
|
|
101
|
+
export interface AccessibleCurtain {
|
|
102
|
+
id: number;
|
|
103
|
+
link_id: string;
|
|
104
|
+
description: string;
|
|
105
|
+
created: string;
|
|
106
|
+
curtain_type: string;
|
|
107
|
+
}
|
|
101
108
|
export interface CurtainCollection {
|
|
102
109
|
id: number;
|
|
103
110
|
created: string;
|
|
104
111
|
updated: string;
|
|
105
112
|
name: string;
|
|
106
113
|
description: string;
|
|
114
|
+
enable: boolean;
|
|
107
115
|
owner: number;
|
|
108
116
|
owner_username: string;
|
|
109
117
|
curtains: number[];
|
|
110
118
|
curtain_count: number;
|
|
119
|
+
accessible_curtains: AccessibleCurtain[];
|
|
111
120
|
}
|
|
112
121
|
export interface CreateCurtainCollection {
|
|
113
122
|
name: string;
|
|
@@ -117,6 +126,7 @@ export interface CreateCurtainCollection {
|
|
|
117
126
|
export interface UpdateCurtainCollection {
|
|
118
127
|
name?: string;
|
|
119
128
|
description?: string;
|
|
129
|
+
enable?: boolean;
|
|
120
130
|
curtains?: number[];
|
|
121
131
|
}
|
|
122
132
|
export interface AddCurtainToCollection {
|
|
@@ -257,7 +267,7 @@ export declare class CurtainWebAPI {
|
|
|
257
267
|
count: number;
|
|
258
268
|
results: CurtainCollection[];
|
|
259
269
|
}, any, {}>>;
|
|
260
|
-
getCurtainCollection(id: number): Promise<import("axios").AxiosResponse<CurtainCollection, any, {}>>;
|
|
270
|
+
getCurtainCollection(id: number, curtainType?: string): Promise<import("axios").AxiosResponse<CurtainCollection, any, {}>>;
|
|
261
271
|
getCurtainCollectionSessions(id: number): Promise<import("axios").AxiosResponse<{
|
|
262
272
|
collection_id: number;
|
|
263
273
|
collection_name: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { User } from "./curtain-user";
|
|
2
2
|
import axios, { AxiosHeaders } from "axios";
|
|
3
|
-
import { arrayBufferToBase64String, base64ToArrayBuffer, encryptAESData, encryptAESKey, exportAESKey, generateAESKey } from "./curtain-encryption";
|
|
3
|
+
import { arrayBufferToBase64String, base64ToArrayBuffer, decodeJWTPayload, encryptAESData, encryptAESKey, exportAESKey, generateAESKey } from "./curtain-encryption";
|
|
4
4
|
const base = "https://celsus.muttsu.xyz/";
|
|
5
5
|
export const replacer = (key, value) => {
|
|
6
6
|
if (value instanceof Map) {
|
|
@@ -175,6 +175,12 @@ export class CurtainWebAPI {
|
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
177
|
checkIfRefreshTokenExpired() {
|
|
178
|
+
if (this.user.refresh_token) {
|
|
179
|
+
const payload = decodeJWTPayload(this.user.refresh_token);
|
|
180
|
+
if (payload && payload.exp) {
|
|
181
|
+
return Date.now() >= payload.exp * 1000;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
178
184
|
let now = new Date();
|
|
179
185
|
let diff = (now.getTime() - this.user.lastRefreshTokenUpdate.getTime()) / 1000;
|
|
180
186
|
diff = diff / 60 / 60;
|
|
@@ -957,10 +963,14 @@ export class CurtainWebAPI {
|
|
|
957
963
|
}
|
|
958
964
|
return this.axiosInstance.get(this.baseURL + "curtain-collections/", { headers: headers, params: params, responseType: "json" });
|
|
959
965
|
}
|
|
960
|
-
getCurtainCollection(id) {
|
|
966
|
+
getCurtainCollection(id, curtainType) {
|
|
961
967
|
let headers = new AxiosHeaders();
|
|
962
968
|
headers["Accept"] = "application/json";
|
|
963
|
-
|
|
969
|
+
let params = new URLSearchParams();
|
|
970
|
+
if (curtainType) {
|
|
971
|
+
params.append("curtain_type", curtainType);
|
|
972
|
+
}
|
|
973
|
+
return this.axiosInstance.get(this.baseURL + "curtain-collections/" + id + "/", { headers: headers, params: params, responseType: "json" });
|
|
964
974
|
}
|
|
965
975
|
getCurtainCollectionSessions(id) {
|
|
966
976
|
let headers = new AxiosHeaders();
|
|
@@ -37,3 +37,4 @@ export declare function encryptDataAES(data: string, publicKey: CryptoKey): Prom
|
|
|
37
37
|
}>;
|
|
38
38
|
export declare function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey): Promise<string>;
|
|
39
39
|
export declare function hashData(data: string): Promise<string>;
|
|
40
|
+
export declare function decodeJWTPayload(token: string): any;
|
|
@@ -137,3 +137,24 @@ export async function hashData(data) {
|
|
|
137
137
|
const hash = await crypto.subtle.digest("SHA-256", encoded);
|
|
138
138
|
return arrayBufferToBase64String(hash);
|
|
139
139
|
}
|
|
140
|
+
export function decodeJWTPayload(token) {
|
|
141
|
+
try {
|
|
142
|
+
const parts = token.split('.');
|
|
143
|
+
if (parts.length !== 3) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const base64Url = parts[1];
|
|
147
|
+
let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
148
|
+
const pad = base64.length % 4;
|
|
149
|
+
if (pad) {
|
|
150
|
+
base64 += new Array(5 - pad).join('=');
|
|
151
|
+
}
|
|
152
|
+
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
|
|
153
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
154
|
+
}).join(''));
|
|
155
|
+
return JSON.parse(jsonPayload);
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import axios, {AxiosHeaders} from "axios";
|
|
|
3
3
|
import {
|
|
4
4
|
arrayBufferToBase64String, base64ToArrayBuffer,
|
|
5
5
|
CurtainEncryption,
|
|
6
|
+
decodeJWTPayload,
|
|
6
7
|
encryptAESData, encryptAESKey,
|
|
7
8
|
encryptDataRSA, exportAESKey,
|
|
8
9
|
generateAESKey
|
|
@@ -135,16 +136,26 @@ export interface CreatePermanentLinkRequest {
|
|
|
135
136
|
reason?: string;
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
export interface AccessibleCurtain {
|
|
140
|
+
id: number;
|
|
141
|
+
link_id: string;
|
|
142
|
+
description: string;
|
|
143
|
+
created: string;
|
|
144
|
+
curtain_type: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
138
147
|
export interface CurtainCollection {
|
|
139
148
|
id: number;
|
|
140
149
|
created: string;
|
|
141
150
|
updated: string;
|
|
142
151
|
name: string;
|
|
143
152
|
description: string;
|
|
153
|
+
enable: boolean;
|
|
144
154
|
owner: number;
|
|
145
155
|
owner_username: string;
|
|
146
156
|
curtains: number[];
|
|
147
157
|
curtain_count: number;
|
|
158
|
+
accessible_curtains: AccessibleCurtain[];
|
|
148
159
|
}
|
|
149
160
|
|
|
150
161
|
export interface CreateCurtainCollection {
|
|
@@ -156,6 +167,7 @@ export interface CreateCurtainCollection {
|
|
|
156
167
|
export interface UpdateCurtainCollection {
|
|
157
168
|
name?: string;
|
|
158
169
|
description?: string;
|
|
170
|
+
enable?: boolean;
|
|
159
171
|
curtains?: number[];
|
|
160
172
|
}
|
|
161
173
|
|
|
@@ -357,6 +369,12 @@ export class CurtainWebAPI {
|
|
|
357
369
|
}
|
|
358
370
|
|
|
359
371
|
checkIfRefreshTokenExpired() {
|
|
372
|
+
if (this.user.refresh_token) {
|
|
373
|
+
const payload = decodeJWTPayload(this.user.refresh_token);
|
|
374
|
+
if (payload && payload.exp) {
|
|
375
|
+
return Date.now() >= payload.exp * 1000;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
360
378
|
let now = new Date();
|
|
361
379
|
let diff = (now.getTime() - this.user.lastRefreshTokenUpdate.getTime()) / 1000;
|
|
362
380
|
diff = diff/60/60;
|
|
@@ -1239,10 +1257,14 @@ export class CurtainWebAPI {
|
|
|
1239
1257
|
return this.axiosInstance.get<{count: number, results: CurtainCollection[]}>(this.baseURL + "curtain-collections/", {headers: headers, params: params, responseType: "json"});
|
|
1240
1258
|
}
|
|
1241
1259
|
|
|
1242
|
-
getCurtainCollection(id: number) {
|
|
1260
|
+
getCurtainCollection(id: number, curtainType?: string) {
|
|
1243
1261
|
let headers = new AxiosHeaders();
|
|
1244
1262
|
headers["Accept"] = "application/json";
|
|
1245
|
-
|
|
1263
|
+
let params = new URLSearchParams();
|
|
1264
|
+
if (curtainType) {
|
|
1265
|
+
params.append("curtain_type", curtainType);
|
|
1266
|
+
}
|
|
1267
|
+
return this.axiosInstance.get<CurtainCollection>(this.baseURL + "curtain-collections/" + id + "/", {headers: headers, params: params, responseType: "json"});
|
|
1246
1268
|
}
|
|
1247
1269
|
|
|
1248
1270
|
getCurtainCollectionSessions(id: number) {
|
|
@@ -1,176 +1,198 @@
|
|
|
1
|
-
export function encryptDataRSA(data: string, publicKey: CryptoKey) {
|
|
2
|
-
return crypto.subtle.encrypt({name: 'RSA-OAEP'}, publicKey, new TextEncoder().encode(data))
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function decryptDataRSA(data: string, privateKey: CryptoKey) {
|
|
6
|
-
return crypto.subtle.decrypt({name: 'RSA-OAEP'}, privateKey, new TextEncoder().encode(data))
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export async function importPublicKey(key: ArrayBuffer) {
|
|
10
|
-
return await crypto.subtle.importKey('spki', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['encrypt'])
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function importPrivateKey(key: ArrayBuffer) {
|
|
14
|
-
return await crypto.subtle.importKey('pkcs8', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['decrypt'])
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function generateKeyPair(modulusLength: number = 2048, publicExponent: Uint8Array = new Uint8Array([1, 0, 1])) {
|
|
18
|
-
return await crypto.subtle.generateKey(
|
|
19
|
-
{
|
|
20
|
-
name: "RSA-OAEP",
|
|
21
|
-
modulusLength: modulusLength,
|
|
22
|
-
publicExponent: publicExponent,
|
|
23
|
-
hash: "SHA-256",
|
|
24
|
-
} as RsaHashedKeyGenParams,
|
|
25
|
-
true,
|
|
26
|
-
["encrypt", "decrypt"],
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function exportPublicKey(key: CryptoKey) {
|
|
31
|
-
return await crypto.subtle.exportKey('spki', key)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function exportPrivateKey(key: CryptoKey) {
|
|
35
|
-
return await crypto.subtle.exportKey('pkcs8', key)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function importKey(key: ArrayBuffer, type: "public"|"private") {
|
|
39
|
-
if (type === "public") {
|
|
40
|
-
return await importPublicKey(key)
|
|
41
|
-
} else {
|
|
42
|
-
return await importPrivateKey(key)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function exportKeyString(key: CryptoKey, type: "public"|"private"): Promise<string> {
|
|
47
|
-
if (type === "public") {
|
|
48
|
-
const k = await exportPublicKey(key)
|
|
49
|
-
return arrayBufferToBase64String(k)
|
|
50
|
-
} else {
|
|
51
|
-
const k = await exportPrivateKey(key)
|
|
52
|
-
return arrayBufferToBase64String(k)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export async function saveToLocalStorage(key: CryptoKey, type: "public"|"private") {
|
|
57
|
-
const k = await exportKeyString(key, type)
|
|
58
|
-
localStorage.setItem(type + "_key", k)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function loadFromLocalStorage(type: "public"|"private") {
|
|
62
|
-
const k = localStorage.getItem(type + "_key")
|
|
63
|
-
if (k) {
|
|
64
|
-
return await importKey(pemToArrayBuffer(k), type)
|
|
65
|
-
}
|
|
66
|
-
return undefined
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface CurtainEncryption {
|
|
70
|
-
encrypted: boolean,
|
|
71
|
-
publicKey?: CryptoKey,
|
|
72
|
-
e2e: boolean,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function arrayBufferToBase64String(arrayBuffer: ArrayBuffer) {
|
|
76
|
-
const byteArray = new Uint8Array(arrayBuffer)
|
|
77
|
-
let byteString = ''
|
|
78
|
-
for (let i=0; i<byteArray.byteLength; i++) {
|
|
79
|
-
byteString += String.fromCharCode(byteArray[i])
|
|
80
|
-
}
|
|
81
|
-
return btoa(byteString)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function removeLines(str_data: string) {
|
|
85
|
-
return str_data.replace("\n", "");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function base64ToArrayBuffer(b64: string): ArrayBuffer {
|
|
89
|
-
const byteString = atob(b64);
|
|
90
|
-
const byteArray = new Uint8Array(byteString.length);
|
|
91
|
-
for(let i=0; i < byteString.length; i++) {
|
|
92
|
-
byteArray[i] = byteString.charCodeAt(i);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return byteArray.buffer;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function pemToArrayBuffer(pem: string) {
|
|
99
|
-
const b64Lines = removeLines(pem);
|
|
100
|
-
let b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
|
|
101
|
-
b64Prefix = b64Prefix.replace('-----BEGIN PUBLIC KEY-----', '');
|
|
102
|
-
let b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
|
|
103
|
-
b64Final = b64Final.replace('-----END PUBLIC KEY-----', '');
|
|
104
|
-
|
|
105
|
-
return base64ToArrayBuffer(b64Final);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// a function to generate to encrypt an aes key arraybuffer with a public key
|
|
109
|
-
export async function encryptAESKey(publicKey: CryptoKey, aesKey: ArrayBuffer) {
|
|
110
|
-
return await crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, aesKey)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// a function to generate an aes key in GCM mode with a length of 256 bits
|
|
114
|
-
export async function generateAESKey() {
|
|
115
|
-
return await crypto.subtle.generateKey(
|
|
116
|
-
{
|
|
117
|
-
name: "AES-GCM",
|
|
118
|
-
length: 256,
|
|
119
|
-
},
|
|
120
|
-
true,
|
|
121
|
-
["encrypt", "decrypt"],
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// a function to encrypt a string with an aes key
|
|
126
|
-
export async function encryptAESData(aesKey: CryptoKey, data: string) {
|
|
127
|
-
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
128
|
-
const enc = new TextEncoder()
|
|
129
|
-
const encoded = enc.encode(data)
|
|
130
|
-
const encrypted = await crypto.subtle.encrypt({name: "AES-GCM", iv: iv}, aesKey, encoded)
|
|
131
|
-
return {encrypted: arrayBufferToBase64String(encrypted), iv: arrayBufferToBase64String(iv.buffer)}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// a function to decrypt a string with an aes key
|
|
135
|
-
export async function decryptAESData(aesKey: CryptoKey, data: string, iv: string) {
|
|
136
|
-
const dec = new TextDecoder()
|
|
137
|
-
const decrypted = await crypto.subtle.decrypt({name: "AES-GCM", iv: base64ToArrayBuffer(iv)}, aesKey, base64ToArrayBuffer(data))
|
|
138
|
-
return dec.decode(decrypted)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// a function to decrypt an aes key with a private key
|
|
142
|
-
export async function decryptAESKey(privateKey: CryptoKey, encryptedKey: ArrayBuffer) {
|
|
143
|
-
return await crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedKey)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// a function to export an aes key to a string
|
|
147
|
-
export async function exportAESKey(key: CryptoKey) {
|
|
148
|
-
return await crypto.subtle.exportKey("raw", key)
|
|
149
|
-
}
|
|
150
|
-
// a function to import an aes key from a string
|
|
151
|
-
export async function importAESKey(key: ArrayBuffer) {
|
|
152
|
-
return await crypto.subtle.importKey("raw", key, "AES-GCM", true, ["encrypt", "decrypt"])
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// a function to encrypt aes key with a public key and also use the aes key to encrypt a large string then return the encrypted aes key and the encrypted string
|
|
156
|
-
export async function encryptDataAES(data: string, publicKey: CryptoKey) {
|
|
157
|
-
const aesKey = await generateAESKey()
|
|
158
|
-
const encryptedKey = await encryptAESKey(publicKey, await exportAESKey(aesKey))
|
|
159
|
-
const encryptedData = await encryptAESData(aesKey, data)
|
|
160
|
-
return {encryptedKey: arrayBufferToBase64String(encryptedKey), encryptedData: encryptedData}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// a function to decrypt an aes key with a private key and use the aes key to decrypt a large string
|
|
164
|
-
export async function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey) {
|
|
165
|
-
const aesKey = await decryptAESKey(privateKey, encryptedKey)
|
|
166
|
-
//import aes key
|
|
167
|
-
return await decryptAESData(await importAESKey(aesKey), encryptedData, iv)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// a function to calculate sha-256 hash of a large string
|
|
171
|
-
export async function hashData(data: string) {
|
|
172
|
-
const enc = new TextEncoder()
|
|
173
|
-
const encoded = enc.encode(data)
|
|
174
|
-
const hash = await crypto.subtle.digest("SHA-256", encoded)
|
|
175
|
-
return arrayBufferToBase64String(hash)
|
|
176
|
-
}
|
|
1
|
+
export function encryptDataRSA(data: string, publicKey: CryptoKey) {
|
|
2
|
+
return crypto.subtle.encrypt({name: 'RSA-OAEP'}, publicKey, new TextEncoder().encode(data))
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function decryptDataRSA(data: string, privateKey: CryptoKey) {
|
|
6
|
+
return crypto.subtle.decrypt({name: 'RSA-OAEP'}, privateKey, new TextEncoder().encode(data))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function importPublicKey(key: ArrayBuffer) {
|
|
10
|
+
return await crypto.subtle.importKey('spki', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['encrypt'])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function importPrivateKey(key: ArrayBuffer) {
|
|
14
|
+
return await crypto.subtle.importKey('pkcs8', key, {name: 'RSA-OAEP', hash: 'SHA-256'}, true, ['decrypt'])
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function generateKeyPair(modulusLength: number = 2048, publicExponent: Uint8Array = new Uint8Array([1, 0, 1])) {
|
|
18
|
+
return await crypto.subtle.generateKey(
|
|
19
|
+
{
|
|
20
|
+
name: "RSA-OAEP",
|
|
21
|
+
modulusLength: modulusLength,
|
|
22
|
+
publicExponent: publicExponent,
|
|
23
|
+
hash: "SHA-256",
|
|
24
|
+
} as RsaHashedKeyGenParams,
|
|
25
|
+
true,
|
|
26
|
+
["encrypt", "decrypt"],
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function exportPublicKey(key: CryptoKey) {
|
|
31
|
+
return await crypto.subtle.exportKey('spki', key)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function exportPrivateKey(key: CryptoKey) {
|
|
35
|
+
return await crypto.subtle.exportKey('pkcs8', key)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function importKey(key: ArrayBuffer, type: "public"|"private") {
|
|
39
|
+
if (type === "public") {
|
|
40
|
+
return await importPublicKey(key)
|
|
41
|
+
} else {
|
|
42
|
+
return await importPrivateKey(key)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function exportKeyString(key: CryptoKey, type: "public"|"private"): Promise<string> {
|
|
47
|
+
if (type === "public") {
|
|
48
|
+
const k = await exportPublicKey(key)
|
|
49
|
+
return arrayBufferToBase64String(k)
|
|
50
|
+
} else {
|
|
51
|
+
const k = await exportPrivateKey(key)
|
|
52
|
+
return arrayBufferToBase64String(k)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function saveToLocalStorage(key: CryptoKey, type: "public"|"private") {
|
|
57
|
+
const k = await exportKeyString(key, type)
|
|
58
|
+
localStorage.setItem(type + "_key", k)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function loadFromLocalStorage(type: "public"|"private") {
|
|
62
|
+
const k = localStorage.getItem(type + "_key")
|
|
63
|
+
if (k) {
|
|
64
|
+
return await importKey(pemToArrayBuffer(k), type)
|
|
65
|
+
}
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CurtainEncryption {
|
|
70
|
+
encrypted: boolean,
|
|
71
|
+
publicKey?: CryptoKey,
|
|
72
|
+
e2e: boolean,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function arrayBufferToBase64String(arrayBuffer: ArrayBuffer) {
|
|
76
|
+
const byteArray = new Uint8Array(arrayBuffer)
|
|
77
|
+
let byteString = ''
|
|
78
|
+
for (let i=0; i<byteArray.byteLength; i++) {
|
|
79
|
+
byteString += String.fromCharCode(byteArray[i])
|
|
80
|
+
}
|
|
81
|
+
return btoa(byteString)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function removeLines(str_data: string) {
|
|
85
|
+
return str_data.replace("\n", "");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function base64ToArrayBuffer(b64: string): ArrayBuffer {
|
|
89
|
+
const byteString = atob(b64);
|
|
90
|
+
const byteArray = new Uint8Array(byteString.length);
|
|
91
|
+
for(let i=0; i < byteString.length; i++) {
|
|
92
|
+
byteArray[i] = byteString.charCodeAt(i);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return byteArray.buffer;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function pemToArrayBuffer(pem: string) {
|
|
99
|
+
const b64Lines = removeLines(pem);
|
|
100
|
+
let b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
|
|
101
|
+
b64Prefix = b64Prefix.replace('-----BEGIN PUBLIC KEY-----', '');
|
|
102
|
+
let b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
|
|
103
|
+
b64Final = b64Final.replace('-----END PUBLIC KEY-----', '');
|
|
104
|
+
|
|
105
|
+
return base64ToArrayBuffer(b64Final);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// a function to generate to encrypt an aes key arraybuffer with a public key
|
|
109
|
+
export async function encryptAESKey(publicKey: CryptoKey, aesKey: ArrayBuffer) {
|
|
110
|
+
return await crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, aesKey)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// a function to generate an aes key in GCM mode with a length of 256 bits
|
|
114
|
+
export async function generateAESKey() {
|
|
115
|
+
return await crypto.subtle.generateKey(
|
|
116
|
+
{
|
|
117
|
+
name: "AES-GCM",
|
|
118
|
+
length: 256,
|
|
119
|
+
},
|
|
120
|
+
true,
|
|
121
|
+
["encrypt", "decrypt"],
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// a function to encrypt a string with an aes key
|
|
126
|
+
export async function encryptAESData(aesKey: CryptoKey, data: string) {
|
|
127
|
+
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
128
|
+
const enc = new TextEncoder()
|
|
129
|
+
const encoded = enc.encode(data)
|
|
130
|
+
const encrypted = await crypto.subtle.encrypt({name: "AES-GCM", iv: iv}, aesKey, encoded)
|
|
131
|
+
return {encrypted: arrayBufferToBase64String(encrypted), iv: arrayBufferToBase64String(iv.buffer)}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// a function to decrypt a string with an aes key
|
|
135
|
+
export async function decryptAESData(aesKey: CryptoKey, data: string, iv: string) {
|
|
136
|
+
const dec = new TextDecoder()
|
|
137
|
+
const decrypted = await crypto.subtle.decrypt({name: "AES-GCM", iv: base64ToArrayBuffer(iv)}, aesKey, base64ToArrayBuffer(data))
|
|
138
|
+
return dec.decode(decrypted)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// a function to decrypt an aes key with a private key
|
|
142
|
+
export async function decryptAESKey(privateKey: CryptoKey, encryptedKey: ArrayBuffer) {
|
|
143
|
+
return await crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedKey)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// a function to export an aes key to a string
|
|
147
|
+
export async function exportAESKey(key: CryptoKey) {
|
|
148
|
+
return await crypto.subtle.exportKey ("raw", key)
|
|
149
|
+
}
|
|
150
|
+
// a function to import an aes key from a string
|
|
151
|
+
export async function importAESKey(key: ArrayBuffer) {
|
|
152
|
+
return await crypto.subtle.importKey("raw", key, "AES-GCM", true, ["encrypt", "decrypt"])
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// a function to encrypt aes key with a public key and also use the aes key to encrypt a large string then return the encrypted aes key and the encrypted string
|
|
156
|
+
export async function encryptDataAES(data: string, publicKey: CryptoKey) {
|
|
157
|
+
const aesKey = await generateAESKey()
|
|
158
|
+
const encryptedKey = await encryptAESKey(publicKey, await exportAESKey(aesKey))
|
|
159
|
+
const encryptedData = await encryptAESData(aesKey, data)
|
|
160
|
+
return {encryptedKey: arrayBufferToBase64String(encryptedKey), encryptedData: encryptedData}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// a function to decrypt an aes key with a private key and use the aes key to decrypt a large string
|
|
164
|
+
export async function decryptDataAES(encryptedKey: ArrayBuffer, encryptedData: string, iv: string, privateKey: CryptoKey) {
|
|
165
|
+
const aesKey = await decryptAESKey(privateKey, encryptedKey)
|
|
166
|
+
//import aes key
|
|
167
|
+
return await decryptAESData(await importAESKey(aesKey), encryptedData, iv)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// a function to calculate sha-256 hash of a large string
|
|
171
|
+
export async function hashData(data: string) {
|
|
172
|
+
const enc = new TextEncoder()
|
|
173
|
+
const encoded = enc.encode(data)
|
|
174
|
+
const hash = await crypto.subtle.digest("SHA-256", encoded)
|
|
175
|
+
return arrayBufferToBase64String(hash)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function decodeJWTPayload(token: string) {
|
|
179
|
+
try {
|
|
180
|
+
const parts = token.split('.');
|
|
181
|
+
if (parts.length !== 3) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const base64Url = parts[1];
|
|
185
|
+
let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
186
|
+
const pad = base64.length % 4;
|
|
187
|
+
if (pad) {
|
|
188
|
+
base64 += new Array(5 - pad).join('=');
|
|
189
|
+
}
|
|
190
|
+
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
|
|
191
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
192
|
+
}).join(''));
|
|
193
|
+
|
|
194
|
+
return JSON.parse(jsonPayload);
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|