@syncular/client-plugin-encryption 0.0.1-100
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/crypto-utils.d.ts +7 -0
- package/dist/crypto-utils.d.ts.map +1 -0
- package/dist/crypto-utils.js +110 -0
- package/dist/crypto-utils.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +639 -0
- package/dist/index.js.map +1 -0
- package/dist/key-sharing.d.ts +124 -0
- package/dist/key-sharing.d.ts.map +1 -0
- package/dist/key-sharing.js +332 -0
- package/dist/key-sharing.js.map +1 -0
- package/package.json +65 -0
- package/src/__tests__/field-encryption-keys.test.ts +68 -0
- package/src/__tests__/key-sharing.test.ts +225 -0
- package/src/__tests__/refresh-encrypted-fields.test.ts +182 -0
- package/src/__tests__/scope-resolution.test.ts +202 -0
- package/src/crypto-utils.test.ts +84 -0
- package/src/crypto-utils.ts +125 -0
- package/src/index.ts +939 -0
- package/src/key-sharing.ts +469 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const BASE64_CHARS =
|
|
2
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
3
|
+
const BASE64_LOOKUP = new Uint8Array(256);
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < BASE64_CHARS.length; i++) {
|
|
6
|
+
BASE64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const BASE64_PATTERN =
|
|
10
|
+
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
11
|
+
const BASE64_URL_PATTERN = /^[A-Za-z0-9_-]*$/;
|
|
12
|
+
|
|
13
|
+
export function randomBytes(length: number): Uint8Array {
|
|
14
|
+
const cryptoObj = globalThis.crypto;
|
|
15
|
+
if (!cryptoObj?.getRandomValues) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'Secure random generator is not available (crypto.getRandomValues). ' +
|
|
18
|
+
'Ensure you are running in a secure context or polyfill crypto.'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const out = new Uint8Array(length);
|
|
22
|
+
cryptoObj.getRandomValues(out);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function bytesToBase64(bytes: Uint8Array): string {
|
|
27
|
+
if (typeof Buffer !== 'undefined') {
|
|
28
|
+
return Buffer.from(bytes).toString('base64');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let result = '';
|
|
32
|
+
const len = bytes.length;
|
|
33
|
+
const remainder = len % 3;
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < len - remainder; i += 3) {
|
|
36
|
+
const a = bytes[i]!;
|
|
37
|
+
const b = bytes[i + 1]!;
|
|
38
|
+
const c = bytes[i + 2]!;
|
|
39
|
+
result +=
|
|
40
|
+
BASE64_CHARS.charAt((a >> 2) & 0x3f) +
|
|
41
|
+
BASE64_CHARS.charAt(((a << 4) | (b >> 4)) & 0x3f) +
|
|
42
|
+
BASE64_CHARS.charAt(((b << 2) | (c >> 6)) & 0x3f) +
|
|
43
|
+
BASE64_CHARS.charAt(c & 0x3f);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (remainder === 1) {
|
|
47
|
+
const a = bytes[len - 1]!;
|
|
48
|
+
result +=
|
|
49
|
+
BASE64_CHARS.charAt((a >> 2) & 0x3f) +
|
|
50
|
+
BASE64_CHARS.charAt((a << 4) & 0x3f) +
|
|
51
|
+
'==';
|
|
52
|
+
} else if (remainder === 2) {
|
|
53
|
+
const a = bytes[len - 2]!;
|
|
54
|
+
const b = bytes[len - 1]!;
|
|
55
|
+
result +=
|
|
56
|
+
BASE64_CHARS.charAt((a >> 2) & 0x3f) +
|
|
57
|
+
BASE64_CHARS.charAt(((a << 4) | (b >> 4)) & 0x3f) +
|
|
58
|
+
BASE64_CHARS.charAt((b << 2) & 0x3f) +
|
|
59
|
+
'=';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function base64ToBytes(base64: string): Uint8Array {
|
|
66
|
+
if (!BASE64_PATTERN.test(base64)) {
|
|
67
|
+
throw new Error('Invalid base64 string');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof Buffer !== 'undefined') {
|
|
71
|
+
return new Uint8Array(Buffer.from(base64, 'base64'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const len = base64.length;
|
|
75
|
+
let padding = 0;
|
|
76
|
+
if (base64[len - 1] === '=') padding++;
|
|
77
|
+
if (base64[len - 2] === '=') padding++;
|
|
78
|
+
|
|
79
|
+
const outputLen = (len * 3) / 4 - padding;
|
|
80
|
+
const out = new Uint8Array(outputLen);
|
|
81
|
+
|
|
82
|
+
let outIdx = 0;
|
|
83
|
+
for (let i = 0; i < len; i += 4) {
|
|
84
|
+
const a = BASE64_LOOKUP[base64.charCodeAt(i)]!;
|
|
85
|
+
const b = BASE64_LOOKUP[base64.charCodeAt(i + 1)]!;
|
|
86
|
+
const c = BASE64_LOOKUP[base64.charCodeAt(i + 2)]!;
|
|
87
|
+
const d = BASE64_LOOKUP[base64.charCodeAt(i + 3)]!;
|
|
88
|
+
|
|
89
|
+
out[outIdx++] = (a << 2) | (b >> 4);
|
|
90
|
+
if (outIdx < outputLen) out[outIdx++] = ((b << 4) | (c >> 2)) & 0xff;
|
|
91
|
+
if (outIdx < outputLen) out[outIdx++] = ((c << 6) | d) & 0xff;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function bytesToBase64Url(bytes: Uint8Array): string {
|
|
98
|
+
return bytesToBase64(bytes)
|
|
99
|
+
.replace(/\+/g, '-')
|
|
100
|
+
.replace(/\//g, '_')
|
|
101
|
+
.replace(/=+$/g, '');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function base64UrlToBytes(base64url: string): Uint8Array {
|
|
105
|
+
if (!BASE64_URL_PATTERN.test(base64url)) {
|
|
106
|
+
throw new Error('Invalid base64url string');
|
|
107
|
+
}
|
|
108
|
+
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
|
|
109
|
+
const padded = base64 + '==='.slice((base64.length + 3) % 4);
|
|
110
|
+
return base64ToBytes(padded);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function hexToBytes(hex: string): Uint8Array {
|
|
114
|
+
const normalized = hex.trim().toLowerCase();
|
|
115
|
+
if (normalized.length % 2 !== 0) {
|
|
116
|
+
throw new Error('Invalid hex string (length must be even)');
|
|
117
|
+
}
|
|
118
|
+
const out = new Uint8Array(normalized.length / 2);
|
|
119
|
+
for (let i = 0; i < out.length; i++) {
|
|
120
|
+
const byte = Number.parseInt(normalized.slice(i * 2, i * 2 + 2), 16);
|
|
121
|
+
if (!Number.isFinite(byte)) throw new Error('Invalid hex string');
|
|
122
|
+
out[i] = byte;
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|