@lowerdeck/encryption 1.0.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.
@@ -0,0 +1,11 @@
1
+
2
+ $ vitest run --passWithNoTests
3
+ [?25l
4
+  RUN  v3.2.4 /Users/tobias/code/metorial/metorial-enterprise/oss/src/packages/backend/encryption
5
+
6
+ No test files found, exiting with code 0
7
+
8
+ include: **/*.{test,spec}.?(c|m)[jt]s?(x)
9
+ exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*
10
+
11
+ [?25h
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@lowerdeck/encryption",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "author": "Tobias Herber",
8
+ "license": "Apache 2",
9
+ "type": "module",
10
+ "source": "src/index.ts",
11
+ "exports": {
12
+ "types": "./dist/index.d.ts",
13
+ "require": "./dist/index.cjs",
14
+ "import": "./dist/index.module.js",
15
+ "default": "./dist/index.modern.js"
16
+ },
17
+ "main": "./dist/index.cjs",
18
+ "module": "./dist/index.module.js",
19
+ "types": "dist/index.d.ts",
20
+ "unpkg": "./dist/index.umd.js",
21
+ "scripts": {
22
+ "test": "vitest run --passWithNoTests",
23
+ "lint": "prettier src/**/*.ts --check",
24
+ "build": "microbundle"
25
+ },
26
+ "dependencies": {
27
+ "@lowerdeck/base62": "^1.0.0",
28
+ "@lowerdeck/id": "^1.0.0",
29
+ "base-x": "^5.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@lowerdeck/tsconfig": "^1.0.0",
33
+ "typescript": "5.8.2",
34
+ "vitest": "^3.1.2"
35
+ }
36
+ }
package/src/base86.ts ADDED
@@ -0,0 +1,11 @@
1
+ import baseX from 'base-x';
2
+
3
+ let internal = baseX(
4
+ '!#$%()*+-.0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~§'
5
+ );
6
+
7
+ export let base86 = {
8
+ encode: (input: string | Uint8Array) =>
9
+ internal.encode(typeof input == 'string' ? new TextEncoder().encode(input) : input),
10
+ decode: (input: string) => internal.decode(input)
11
+ };
package/src/crypto.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { base62 } from '@metorial/base62';
2
+ import { base86 } from './base86';
3
+
4
+ let enc = new TextEncoder();
5
+ let dec = new TextDecoder();
6
+
7
+ let getPasswordKey = (password: string) =>
8
+ crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']);
9
+
10
+ let deriveKey = (passwordKey: CryptoKey, keyUsage: ('encrypt' | 'decrypt')[]) =>
11
+ crypto.subtle.deriveKey(
12
+ {
13
+ name: 'PBKDF2',
14
+ iterations: 250000,
15
+ hash: 'SHA-256'
16
+ },
17
+ passwordKey,
18
+ { name: 'AES-GCM', length: 256 },
19
+ false,
20
+ keyUsage
21
+ );
22
+
23
+ let encryptData = async (secretData: string, password: string) => {
24
+ let iv = crypto.getRandomValues(new Uint8Array(12));
25
+ let passwordKey = await getPasswordKey(password);
26
+ let aesKey = await deriveKey(passwordKey, ['encrypt']);
27
+ let encryptedContent = await crypto.subtle.encrypt(
28
+ {
29
+ name: 'AES-GCM',
30
+ iv
31
+ },
32
+ aesKey,
33
+ enc.encode(secretData)
34
+ );
35
+
36
+ let encryptedContentArr = new Uint8Array(encryptedContent);
37
+ let buff = new Uint8Array(iv.byteLength + encryptedContentArr.byteLength);
38
+ buff.set(iv, 0);
39
+ buff.set(encryptedContentArr, iv.byteLength);
40
+
41
+ return base86.encode(buff);
42
+ };
43
+
44
+ let decryptData = async (encryptedData: string, password: string) => {
45
+ let encryptedDataBuff = base86.decode(encryptedData);
46
+ let iv = encryptedDataBuff.slice(0, 16);
47
+ let data = encryptedDataBuff.slice(16);
48
+ let passwordKey = await getPasswordKey(password);
49
+ let aesKey = await deriveKey(passwordKey, ['decrypt']);
50
+ let decryptedContent = await crypto.subtle.decrypt(
51
+ {
52
+ name: 'AES-GCM',
53
+ iv
54
+ },
55
+ aesKey,
56
+ data
57
+ );
58
+
59
+ return dec.decode(decryptedContent);
60
+ };
61
+
62
+ let sha512 = async (data: string) => {
63
+ let hashBuffer = await crypto.subtle.digest('SHA-512', enc.encode(data));
64
+ return base62.encode(new Uint8Array(hashBuffer));
65
+ };
66
+
67
+ export let secretsCrypto = {
68
+ encrypt: encryptData,
69
+ decrypt: decryptData,
70
+ sha512
71
+ };
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { generatePlainId } from '@metorial/id';
2
+ import { secretsCrypto } from './crypto';
3
+
4
+ export class Encryption {
5
+ constructor(private readonly password: string) {}
6
+
7
+ private async getPassword(entityId: string) {
8
+ return (await secretsCrypto.sha512(`${entityId}${this.password!}`)).slice(0, 50);
9
+ }
10
+
11
+ async encrypt(input: { secret: string; entityId: string }) {
12
+ return await secretsCrypto.encrypt(
13
+ JSON.stringify({
14
+ id: generatePlainId(10),
15
+ key: input.secret
16
+ }),
17
+ await this.getPassword(input.entityId)
18
+ );
19
+ }
20
+
21
+ async decrypt(info: { encrypted: string; entityId: string }) {
22
+ let content = JSON.parse(
23
+ await secretsCrypto.decrypt(info.encrypted, await this.getPassword(info.entityId))
24
+ );
25
+
26
+ return content.key;
27
+ }
28
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@metorial/tsconfig/base.json",
4
+ "exclude": ["dist"],
5
+ "compilerOptions": {
6
+ "outDir": "dist"
7
+ }
8
+ }