mirror of
https://github.com/cupcakearmy/occulto.git
synced 2026-06-11 04:06:48 +00:00
update deps, cleanup
This commit is contained in:
@@ -11,16 +11,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
@@ -12,15 +12,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Roadmap
|
||||
|
||||
## Todo
|
||||
|
||||
- Sym aes-gcm
|
||||
- Hash sha1-512
|
||||
+17
-22
@@ -21,35 +21,30 @@
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"docs": "typedoc",
|
||||
"test:node": "vitest",
|
||||
"test:browsers": "zx test.browsers.js",
|
||||
"test": "CI=1 run-s build test:*",
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf ./dist",
|
||||
"dev": "vitest",
|
||||
"prepublishOnly": "run-s clean test"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"prepublishOnly": "CI=1 run-s typecheck test build",
|
||||
"test": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/strictest": "^2.0.5",
|
||||
"@types/node": "^22.5.2",
|
||||
"@vitest/browser": "^2.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"playwright": "^1.46.1",
|
||||
"typedoc": "^0.26.6",
|
||||
"typescript": "^5.5.4",
|
||||
"vitest": "^2.0.5",
|
||||
"zx": "^8.1.5"
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@tsconfig/strictest": "^2.0.8",
|
||||
"@types/node": "^24.12.4",
|
||||
"@vitest/browser-playwright": "^4.1.7",
|
||||
"npm-run-all2": "^9.0.1",
|
||||
"playwright": "^1.60.0",
|
||||
"tsdown": "^0.22.1",
|
||||
"typedoc": "^0.28.19",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.7"
|
||||
},
|
||||
"packageManager": "pnpm@9.8.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
"packageManager": "pnpm@11.5.0"
|
||||
}
|
||||
|
||||
Generated
+1128
-2115
File diff suppressed because it is too large
Load Diff
+100
-75
@@ -1,133 +1,157 @@
|
||||
import { type TypedArray } from '../utils/base.js'
|
||||
import { getCrypto } from './crypto.js'
|
||||
import { Base64, Bytes } from './encoding.js'
|
||||
import { Hashes } from './hash.js'
|
||||
import { getRandomBytes } from './random.js'
|
||||
import { type TypedArray } from "../utils/base.js";
|
||||
import { getCrypto } from "./crypto.js";
|
||||
import { Base64, Bytes } from "./encoding.js";
|
||||
import { Hashes } from "./hash.js";
|
||||
import { getRandomBytes } from "./random.js";
|
||||
|
||||
const Params = {
|
||||
GCM: {
|
||||
ivLength: 12,
|
||||
tagLength: 128,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export type KeyData = {
|
||||
name: 'PBKDF2'
|
||||
hash: Hashes
|
||||
iterations: number
|
||||
salt: TypedArray
|
||||
length: number
|
||||
}
|
||||
name: "PBKDF2";
|
||||
hash: Hashes;
|
||||
iterations: number;
|
||||
salt: TypedArray;
|
||||
length: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* AES operation modes.
|
||||
*/
|
||||
export enum Modes {
|
||||
AES_GCM = 'AES-GCM',
|
||||
AES_GCM = "AES-GCM",
|
||||
}
|
||||
|
||||
export class AES {
|
||||
static Modes = Modes
|
||||
static Modes = Modes;
|
||||
|
||||
// delimiter with a character that is not allowed in base64 or hex
|
||||
private static delimiter = '--'
|
||||
private static delimiterEasy = '---'
|
||||
private static delimiter = "--";
|
||||
private static delimiterEasy = "---";
|
||||
|
||||
private static InvalidCiphertext = new Error('Invalid ciphertext')
|
||||
private static InvalidCiphertext = new Error("Invalid ciphertext");
|
||||
|
||||
private static async join(...args: TypedArray[]): Promise<string> {
|
||||
const strings = await Promise.all(args.map(Base64.encode))
|
||||
return strings.join(AES.delimiter)
|
||||
const strings = await Promise.all(args.map(Base64.encode));
|
||||
return strings.join(AES.delimiter);
|
||||
}
|
||||
|
||||
private static async split(ciphertext: string): Promise<TypedArray[]> {
|
||||
const splitted = ciphertext.split(AES.delimiter)
|
||||
return Promise.all(splitted.map(Base64.decode))
|
||||
const splitted = ciphertext.split(AES.delimiter);
|
||||
return Promise.all(splitted.map(Base64.decode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a key from a password.
|
||||
* To be used if the password is not 128, 192 or 256 bits or human made, non generated keys.
|
||||
*/
|
||||
static async derive(key: string, options?: KeyData): Promise<[TypedArray, KeyData]> {
|
||||
static async derive(
|
||||
key: string,
|
||||
options?: KeyData,
|
||||
): Promise<[TypedArray, KeyData]> {
|
||||
options ??= {
|
||||
name: 'PBKDF2',
|
||||
name: "PBKDF2",
|
||||
hash: Hashes.SHA_512,
|
||||
iterations: 100_000,
|
||||
length: 256,
|
||||
salt: await getRandomBytes(16),
|
||||
}
|
||||
const c = await getCrypto()
|
||||
const keyBuffer = await c.subtle.importKey('raw', Bytes.encode(key), options.name, false, [
|
||||
'deriveBits',
|
||||
'deriveKey',
|
||||
])
|
||||
const bits = await c.subtle.deriveBits(options, keyBuffer, options.length)
|
||||
return [new Uint8Array(bits), options]
|
||||
};
|
||||
const c = await getCrypto();
|
||||
const keyBuffer = await c.subtle.importKey(
|
||||
"raw",
|
||||
Bytes.encode(key),
|
||||
options.name,
|
||||
false,
|
||||
["deriveBits", "deriveKey"],
|
||||
);
|
||||
const bits = await c.subtle.deriveBits(options, keyBuffer, options.length);
|
||||
return [new Uint8Array(bits), options];
|
||||
}
|
||||
|
||||
static async encrypt(data: TypedArray, key: TypedArray, mode: Modes = Modes.AES_GCM): Promise<string> {
|
||||
const c = await getCrypto()
|
||||
static async encrypt(
|
||||
data: TypedArray,
|
||||
key: TypedArray,
|
||||
mode: Modes = Modes.AES_GCM,
|
||||
): Promise<string> {
|
||||
const c = await getCrypto();
|
||||
|
||||
let iv: Uint8Array
|
||||
let alg: AlgorithmIdentifier
|
||||
let iv: Uint8Array;
|
||||
let alg: AlgorithmIdentifier;
|
||||
|
||||
switch (mode) {
|
||||
case Modes.AES_GCM:
|
||||
iv = c.getRandomValues(new Uint8Array(Params.GCM.ivLength))
|
||||
alg = mode
|
||||
break
|
||||
iv = c.getRandomValues(new Uint8Array(Params.GCM.ivLength));
|
||||
alg = mode;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported mode')
|
||||
throw new Error("Unsupported mode");
|
||||
}
|
||||
|
||||
const keyObj = await c.subtle.importKey('raw', key, alg, false, ['encrypt'])
|
||||
const encrypted = await c.subtle.encrypt({ name: alg, iv }, keyObj, data)
|
||||
const encryptedBuffer = new Uint8Array(encrypted)
|
||||
const keyObj = await c.subtle.importKey("raw", key, alg, false, [
|
||||
"encrypt",
|
||||
]);
|
||||
// @ts-expect-error
|
||||
const encrypted = await c.subtle.encrypt({ name: alg, iv }, keyObj, data);
|
||||
const encryptedBuffer = new Uint8Array(encrypted);
|
||||
|
||||
return AES.join(Bytes.encode(alg), iv, encryptedBuffer)
|
||||
// @ts-expect-error
|
||||
return AES.join(Bytes.encode(alg), iv, encryptedBuffer);
|
||||
}
|
||||
|
||||
static async decrypt(ciphertext: string, key: TypedArray): Promise<TypedArray> {
|
||||
const c = await getCrypto()
|
||||
static async decrypt(
|
||||
ciphertext: string,
|
||||
key: TypedArray,
|
||||
): Promise<TypedArray> {
|
||||
const c = await getCrypto();
|
||||
|
||||
const [alg, iv, data] = await AES.split(ciphertext)
|
||||
if (!alg || !iv || !data) throw this.InvalidCiphertext
|
||||
const [alg, iv, data] = await AES.split(ciphertext);
|
||||
if (!alg || !iv || !data) throw this.InvalidCiphertext;
|
||||
|
||||
const mode = Bytes.decode(alg)
|
||||
const mode = Bytes.decode(alg);
|
||||
switch (mode) {
|
||||
case Modes.AES_GCM:
|
||||
break
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported mode')
|
||||
throw new Error("Unsupported mode");
|
||||
}
|
||||
|
||||
const keyObj = await c.subtle.importKey('raw', key, mode, false, ['decrypt'])
|
||||
const decrypted = await c.subtle.decrypt({ name: mode, iv }, keyObj, data)
|
||||
return new Uint8Array(decrypted)
|
||||
const keyObj = await c.subtle.importKey("raw", key, mode, false, [
|
||||
"decrypt",
|
||||
]);
|
||||
const decrypted = await c.subtle.decrypt({ name: mode, iv }, keyObj, data);
|
||||
return new Uint8Array(decrypted);
|
||||
}
|
||||
|
||||
static async encryptEasy(data: string | TypedArray, key: string, mode: Modes = Modes.AES_GCM): Promise<string> {
|
||||
const dataBuffer = typeof data === 'string' ? Bytes.encode(data) : data
|
||||
const [keyDerived, options] = await AES.derive(key)
|
||||
static async encryptEasy(
|
||||
data: string | TypedArray,
|
||||
key: string,
|
||||
mode: Modes = Modes.AES_GCM,
|
||||
): Promise<string> {
|
||||
const dataBuffer = typeof data === "string" ? Bytes.encode(data) : data;
|
||||
const [keyDerived, options] = await AES.derive(key);
|
||||
|
||||
const ciphertext = await this.encrypt(dataBuffer, keyDerived, mode)
|
||||
const ciphertext = await this.encrypt(dataBuffer, keyDerived, mode);
|
||||
const header = await this.join(
|
||||
Bytes.encode(options.name),
|
||||
Bytes.encode(options.hash),
|
||||
Bytes.encode(options.iterations.toString()),
|
||||
options.salt,
|
||||
Bytes.encode(options.length.toString())
|
||||
)
|
||||
Bytes.encode(options.length.toString()),
|
||||
);
|
||||
|
||||
return [header, ciphertext].join(this.delimiterEasy)
|
||||
return [header, ciphertext].join(this.delimiterEasy);
|
||||
}
|
||||
|
||||
static async decryptEasy(ciphertext: string, key: string): Promise<string> {
|
||||
const [header, data] = ciphertext.split(this.delimiterEasy)
|
||||
if (!header || !data) throw this.InvalidCiphertext
|
||||
const [name, hash, iterations, salt, length] = await this.split(header)
|
||||
if (!name || !hash || !iterations || !salt || !length) throw this.InvalidCiphertext
|
||||
const [header, data] = ciphertext.split(this.delimiterEasy);
|
||||
if (!header || !data) throw this.InvalidCiphertext;
|
||||
const [name, hash, iterations, salt, length] = await this.split(header);
|
||||
if (!name || !hash || !iterations || !salt || !length)
|
||||
throw this.InvalidCiphertext;
|
||||
|
||||
const options: KeyData = {
|
||||
name: Bytes.decode(name) as any,
|
||||
@@ -135,25 +159,26 @@ export class AES {
|
||||
iterations: parseInt(Bytes.decode(iterations)),
|
||||
salt,
|
||||
length: parseInt(Bytes.decode(length)),
|
||||
}
|
||||
if (isNaN(options.iterations) || isNaN(options.length)) throw this.InvalidCiphertext
|
||||
};
|
||||
if (isNaN(options.iterations) || isNaN(options.length))
|
||||
throw this.InvalidCiphertext;
|
||||
|
||||
const [keyDerived] = await AES.derive(key, options)
|
||||
const decrypted = await this.decrypt(data, keyDerived)
|
||||
return Bytes.decode(decrypted)
|
||||
const [keyDerived] = await AES.derive(key, options);
|
||||
const decrypted = await this.decrypt(data, keyDerived);
|
||||
return Bytes.decode(decrypted);
|
||||
}
|
||||
|
||||
static async generateKey(): Promise<TypedArray> {
|
||||
const c = await getCrypto()
|
||||
const c = await getCrypto();
|
||||
const key = await c.subtle.generateKey(
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
)
|
||||
const buffer = await c.subtle.exportKey('raw', key)
|
||||
return new Uint8Array(buffer)
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
const buffer = await c.subtle.exportKey("raw", key);
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -1,20 +1,17 @@
|
||||
import { isBrowser } from '../utils/base.js'
|
||||
import { isBrowser } from "../utils/base.js";
|
||||
|
||||
let crypto: typeof window.crypto | null = null
|
||||
let crypto: Crypto | null = null;
|
||||
|
||||
export async function getCrypto(): Promise<typeof window.crypto> {
|
||||
export async function getCrypto(): Promise<Crypto> {
|
||||
if (!crypto) {
|
||||
if (isBrowser) crypto = window.crypto
|
||||
else if (typeof require !== 'undefined') {
|
||||
const { webcrypto } = await require('crypto')
|
||||
crypto = webcrypto
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const { webcrypto } = await import('crypto')
|
||||
crypto = webcrypto as any
|
||||
if (isBrowser) {
|
||||
crypto = window.crypto;
|
||||
}
|
||||
if (typeof globalThis !== "undefined") {
|
||||
crypto = globalThis.crypto;
|
||||
}
|
||||
}
|
||||
|
||||
if (!crypto) throw new Error('No crypto available')
|
||||
return crypto
|
||||
if (!crypto) throw new Error("No crypto available");
|
||||
return crypto;
|
||||
}
|
||||
|
||||
+34
-30
@@ -1,60 +1,63 @@
|
||||
import { split, type TypedArray } from '../utils/base.js'
|
||||
import { split, type TypedArray } from "../utils/base.js";
|
||||
|
||||
export class Base64 {
|
||||
private static prefix = 'data:application/octet-stream;base64,'
|
||||
private static prefix = "data:application/octet-stream;base64,";
|
||||
|
||||
static encode(s: TypedArray): Promise<string> {
|
||||
return split({
|
||||
async node() {
|
||||
return Buffer.from(s).toString('base64')
|
||||
// @ts-expect-error
|
||||
return Buffer.from(s).toString("base64");
|
||||
},
|
||||
async browser() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
const data = event.target?.result
|
||||
if (typeof data === 'string') resolve(data.slice(Base64.prefix.length))
|
||||
else reject(new Error('Failed to read file'))
|
||||
}
|
||||
reader.readAsDataURL(new Blob([s]))
|
||||
})
|
||||
const data = event.target?.result;
|
||||
if (typeof data === "string")
|
||||
resolve(data.slice(Base64.prefix.length));
|
||||
else reject(new Error("Failed to read file"));
|
||||
};
|
||||
reader.readAsDataURL(new Blob([s]));
|
||||
});
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
static decode(s: string): Promise<TypedArray> {
|
||||
return split({
|
||||
async node() {
|
||||
return Buffer.from(s, 'base64')
|
||||
return Buffer.from(s, "base64");
|
||||
},
|
||||
async browser() {
|
||||
const ab = await fetch(Base64.prefix + s)
|
||||
.then((r) => r.blob())
|
||||
.then((b) => b.arrayBuffer())
|
||||
return new Uint8Array(ab)
|
||||
.then((b) => b.arrayBuffer());
|
||||
return new Uint8Array(ab);
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Hex {
|
||||
static encode(buffer: TypedArray): string {
|
||||
let s = ''
|
||||
let s = "";
|
||||
// @ts-expect-error
|
||||
for (const i of new Uint8Array(buffer)) {
|
||||
s += i.toString(16).padStart(2, '0')
|
||||
s += i.toString(16).padStart(2, "0");
|
||||
}
|
||||
return s
|
||||
return s;
|
||||
}
|
||||
|
||||
static decode(s: string): TypedArray {
|
||||
const size = s.length / 2
|
||||
const buffer = new Uint8Array(size)
|
||||
const size = s.length / 2;
|
||||
const buffer = new Uint8Array(size);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const idx = i * 2
|
||||
const segment = s.slice(idx, idx + 2)
|
||||
buffer[i] = parseInt(segment, 16)
|
||||
const idx = i * 2;
|
||||
const segment = s.slice(idx, idx + 2);
|
||||
buffer[i] = parseInt(segment, 16);
|
||||
}
|
||||
return buffer
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,22 +65,23 @@ export class Bytes {
|
||||
static decode(data: TypedArray): string {
|
||||
return split({
|
||||
node() {
|
||||
return Buffer.from(data).toString('utf-8')
|
||||
// @ts-expect-error
|
||||
return Buffer.from(data).toString("utf-8");
|
||||
},
|
||||
browser() {
|
||||
return new TextDecoder().decode(data)
|
||||
return new TextDecoder().decode(data);
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
static encode(data: string): TypedArray {
|
||||
return split({
|
||||
node() {
|
||||
return Buffer.from(data)
|
||||
return Buffer.from(data);
|
||||
},
|
||||
browser() {
|
||||
return new TextEncoder().encode(data)
|
||||
return new TextEncoder().encode(data);
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+21
-15
@@ -1,6 +1,6 @@
|
||||
import { type TypedArray } from '../utils/base.js'
|
||||
import { getCrypto } from './crypto.js'
|
||||
import { Bytes, Hex } from './encoding.js'
|
||||
import { type TypedArray } from "../utils/base.js";
|
||||
import { getCrypto } from "./crypto.js";
|
||||
import { Bytes, Hex } from "./encoding.js";
|
||||
|
||||
/**
|
||||
* List of available hash functions.
|
||||
@@ -13,20 +13,26 @@ export enum Hashes {
|
||||
/**
|
||||
* @remarks SHA-1 is not recommended for new applications as it's not cryptographically secure.
|
||||
*/
|
||||
SHA_1 = 'SHA-1',
|
||||
SHA_256 = 'SHA-256',
|
||||
SHA_384 = 'SHA-384',
|
||||
SHA_512 = 'SHA-512',
|
||||
SHA_1 = "SHA-1",
|
||||
SHA_256 = "SHA-256",
|
||||
SHA_384 = "SHA-384",
|
||||
SHA_512 = "SHA-512",
|
||||
}
|
||||
|
||||
export class Hash {
|
||||
static async hash(data: string, hash: Hashes): Promise<string>
|
||||
static async hash(data: TypedArray, hash: Hashes): Promise<TypedArray>
|
||||
static async hash(data: string | TypedArray, hash: Hashes): Promise<string | TypedArray> {
|
||||
const isString = typeof data === 'string'
|
||||
const c = await getCrypto()
|
||||
const result = await c.subtle.digest(hash, isString ? Bytes.encode(data) : data)
|
||||
const buf = new Uint8Array(result)
|
||||
return isString ? Hex.encode(buf) : buf
|
||||
static async hash(data: string, hash: Hashes): Promise<string>;
|
||||
static async hash(data: TypedArray, hash: Hashes): Promise<TypedArray>;
|
||||
static async hash(
|
||||
data: string | TypedArray,
|
||||
hash: Hashes,
|
||||
): Promise<string | TypedArray> {
|
||||
const isString = typeof data === "string";
|
||||
const c = await getCrypto();
|
||||
const result = await c.subtle.digest(
|
||||
hash,
|
||||
isString ? Bytes.encode(data) : data,
|
||||
);
|
||||
const buf = new Uint8Array(result);
|
||||
return isString ? Hex.encode(buf) : buf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type TypedArray } from '../utils/base.js'
|
||||
import { getCrypto } from './crypto.js'
|
||||
import { type TypedArray } from "../utils/base.js";
|
||||
import { getCrypto } from "./crypto.js";
|
||||
|
||||
export async function getRandomBytes(bytes: number): Promise<TypedArray> {
|
||||
if (bytes <= 0) throw new Error('Invalid number of bytes')
|
||||
if (bytes <= 0) throw new Error("Invalid number of bytes");
|
||||
|
||||
const buffer = new Uint8Array(bytes)
|
||||
const crypto = await getCrypto()
|
||||
crypto.getRandomValues(buffer)
|
||||
return buffer
|
||||
const buffer = new Uint8Array(bytes);
|
||||
const crypto = await getCrypto();
|
||||
crypto.getRandomValues(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
+71
-60
@@ -1,64 +1,64 @@
|
||||
import type { TypedArray } from '../utils/base.js'
|
||||
import { getCrypto } from './crypto.js'
|
||||
import { Base64 } from './encoding.js'
|
||||
import type { TypedArray } from "../utils/base.js";
|
||||
import { getCrypto } from "./crypto.js";
|
||||
import { Base64 } from "./encoding.js";
|
||||
|
||||
const Constants = Object.freeze({
|
||||
name: 'RSA-OAEP',
|
||||
hash: 'SHA-512',
|
||||
name: "RSA-OAEP",
|
||||
hash: "SHA-512",
|
||||
exponent: new Uint8Array([1, 0, 1]),
|
||||
error: {
|
||||
invalidKey: new Error('invalid key'),
|
||||
shouldBePublicKey: new Error('should be a public key'),
|
||||
shouldBePrivateKey: new Error('should be a private key'),
|
||||
dataTooLong: new Error('data too long'),
|
||||
invalidKey: new Error("invalid key"),
|
||||
shouldBePublicKey: new Error("should be a public key"),
|
||||
shouldBePrivateKey: new Error("should be a private key"),
|
||||
dataTooLong: new Error("data too long"),
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
class Key {
|
||||
/**
|
||||
* Exports a key to a PEM string.
|
||||
*/
|
||||
static async encode(key: CryptoKey): Promise<string> {
|
||||
const c = await getCrypto()
|
||||
const c = await getCrypto();
|
||||
|
||||
let type: 'pkcs8' | 'spki'
|
||||
let label: string
|
||||
let type: "pkcs8" | "spki";
|
||||
let label: string;
|
||||
switch (key.type) {
|
||||
case 'private':
|
||||
type = 'pkcs8'
|
||||
label = 'PRIVATE'
|
||||
break
|
||||
case 'public':
|
||||
type = 'spki'
|
||||
label = 'PUBLIC'
|
||||
break
|
||||
case "private":
|
||||
type = "pkcs8";
|
||||
label = "PRIVATE";
|
||||
break;
|
||||
case "public":
|
||||
type = "spki";
|
||||
label = "PUBLIC";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('invalid key type')
|
||||
throw new Error("invalid key type");
|
||||
}
|
||||
const exported = await c.subtle.exportKey(type, key)
|
||||
const exported = await c.subtle.exportKey(type, key);
|
||||
|
||||
// Export in PEM format
|
||||
const base64 = await Base64.encode(new Uint8Array(exported))
|
||||
const formatted = base64.match(/.{1,64}/g)?.join('\n')
|
||||
return `-----BEGIN ${label} KEY-----\n${formatted}\n-----END ${label} KEY-----`
|
||||
const base64 = await Base64.encode(new Uint8Array(exported));
|
||||
const formatted = base64.match(/.{1,64}/g)?.join("\n");
|
||||
return `-----BEGIN ${label} KEY-----\n${formatted}\n-----END ${label} KEY-----`;
|
||||
}
|
||||
|
||||
static async decode(s: string): Promise<CryptoKey> {
|
||||
const isPrivate = s.includes('BEGIN PRIVATE KEY')
|
||||
const cleaned = s.replace(/-----.*?-----/g, '').replace(/\s/g, '')
|
||||
const bytes = await Base64.decode(cleaned)
|
||||
const c = await getCrypto()
|
||||
const isPrivate = s.includes("BEGIN PRIVATE KEY");
|
||||
const cleaned = s.replace(/-----.*?-----/g, "").replace(/\s/g, "");
|
||||
const bytes = await Base64.decode(cleaned);
|
||||
const c = await getCrypto();
|
||||
return await c.subtle.importKey(
|
||||
isPrivate ? 'pkcs8' : 'spki',
|
||||
isPrivate ? "pkcs8" : "spki",
|
||||
bytes,
|
||||
{
|
||||
name: Constants.name,
|
||||
hash: Constants.hash,
|
||||
},
|
||||
true,
|
||||
isPrivate ? ['decrypt'] : ['encrypt']
|
||||
)
|
||||
isPrivate ? ["decrypt"] : ["encrypt"],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,20 +67,22 @@ class Key {
|
||||
* https://www.rfc-editor.org/rfc/rfc2437#section-7.1.1
|
||||
*/
|
||||
static getMaxMessageSize(key: CryptoKey): number {
|
||||
if (key.type !== 'public') throw Constants.error.shouldBePublicKey
|
||||
if (key.type !== "public") throw Constants.error.shouldBePublicKey;
|
||||
// @ts-ignore
|
||||
const mod = key?.algorithm?.modulusLength
|
||||
if (isNaN(mod)) throw Constants.error.invalidKey
|
||||
return mod / 8 - (2 * 512) / 8 - 2
|
||||
const mod = key?.algorithm?.modulusLength;
|
||||
if (isNaN(mod)) throw Constants.error.invalidKey;
|
||||
return mod / 8 - (2 * 512) / 8 - 2;
|
||||
}
|
||||
}
|
||||
|
||||
export class RSA {
|
||||
static async generateKeyPair(bits: number = 2 ** 12): Promise<{ private: string; public: string }> {
|
||||
const c = await getCrypto()
|
||||
static async generateKeyPair(
|
||||
bits: number = 2 ** 12,
|
||||
): Promise<{ private: string; public: string }> {
|
||||
const c = await getCrypto();
|
||||
|
||||
if (bits < 2 ** 11) {
|
||||
throw new Error('bit sizes below 2048 are considered insecure.')
|
||||
throw new Error("bit sizes below 2048 are considered insecure.");
|
||||
}
|
||||
|
||||
const pair = await c.subtle.generateKey(
|
||||
@@ -91,47 +93,56 @@ export class RSA {
|
||||
hash: Constants.hash,
|
||||
},
|
||||
true,
|
||||
['encrypt', 'decrypt']
|
||||
)
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
|
||||
return {
|
||||
private: await Key.encode(pair.privateKey),
|
||||
public: await Key.encode(pair.publicKey),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static async encrypt(data: TypedArray, key: string): Promise<TypedArray> {
|
||||
let keyObj: CryptoKey
|
||||
let keyObj: CryptoKey;
|
||||
try {
|
||||
keyObj = await Key.decode(key)
|
||||
keyObj = await Key.decode(key);
|
||||
} catch (e) {
|
||||
throw Constants.error.invalidKey
|
||||
throw Constants.error.invalidKey;
|
||||
}
|
||||
if (keyObj.type !== 'public') {
|
||||
throw Constants.error.shouldBePublicKey
|
||||
if (keyObj.type !== "public") {
|
||||
throw Constants.error.shouldBePublicKey;
|
||||
}
|
||||
|
||||
// Check if data is too large
|
||||
if (data.length > Key.getMaxMessageSize(keyObj)) throw Constants.error.dataTooLong
|
||||
if (data.byteLength > Key.getMaxMessageSize(keyObj))
|
||||
throw Constants.error.dataTooLong;
|
||||
|
||||
const c = await getCrypto()
|
||||
const encrypted = await c.subtle.encrypt({ name: Constants.name }, keyObj, data)
|
||||
return new Uint8Array(encrypted)
|
||||
const c = await getCrypto();
|
||||
const encrypted = await c.subtle.encrypt(
|
||||
{ name: Constants.name },
|
||||
keyObj,
|
||||
data,
|
||||
);
|
||||
return new Uint8Array(encrypted);
|
||||
}
|
||||
|
||||
static async decrypt(data: TypedArray, key: string): Promise<TypedArray> {
|
||||
let keyObj: CryptoKey
|
||||
let keyObj: CryptoKey;
|
||||
try {
|
||||
keyObj = await Key.decode(key)
|
||||
keyObj = await Key.decode(key);
|
||||
} catch (e) {
|
||||
throw Constants.error.invalidKey
|
||||
throw Constants.error.invalidKey;
|
||||
}
|
||||
if (keyObj.type !== 'private') {
|
||||
throw Constants.error.shouldBePrivateKey
|
||||
if (keyObj.type !== "private") {
|
||||
throw Constants.error.shouldBePrivateKey;
|
||||
}
|
||||
|
||||
const c = await getCrypto()
|
||||
const decrypted = await c.subtle.decrypt({ name: Constants.name }, keyObj, data)
|
||||
return new Uint8Array(decrypted)
|
||||
const c = await getCrypto();
|
||||
const decrypted = await c.subtle.decrypt(
|
||||
{ name: Constants.name },
|
||||
keyObj,
|
||||
data,
|
||||
);
|
||||
return new Uint8Array(decrypted);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
export * from './crypto/aes.js'
|
||||
export * from './crypto/encoding.js'
|
||||
export * from './crypto/hash.js'
|
||||
export * from './crypto/random.js'
|
||||
export * from './crypto/rsa.js'
|
||||
export { TypedArray } from './utils/base.js'
|
||||
export * from "./crypto/aes.js";
|
||||
export * from "./crypto/encoding.js";
|
||||
export * from "./crypto/hash.js";
|
||||
export * from "./crypto/random.js";
|
||||
export * from "./crypto/rsa.js";
|
||||
export type { TypedArray } from "./utils/base.js";
|
||||
|
||||
+20
-14
@@ -1,27 +1,33 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const isBrowser = typeof window !== 'undefined'
|
||||
export const isBrowser = typeof window !== "undefined";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type PromiseOrValue<T> = T | Promise<T>
|
||||
export type PromiseOrValue<T> = T | Promise<T>;
|
||||
|
||||
export type TypedArray =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| BigInt64Array
|
||||
| BigUint64Array
|
||||
export type TypedArray = ArrayBufferView<ArrayBuffer>;
|
||||
// | Int8Array
|
||||
// | Uint8Array
|
||||
// | Uint8ClampedArray
|
||||
// | Int16Array
|
||||
// | Uint16Array
|
||||
// | Int32Array
|
||||
// | Uint32Array
|
||||
// | BigInt64Array
|
||||
// | BigUint64Array;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function split<T>({ node, browser }: { node: () => T; browser: () => T }) {
|
||||
return isBrowser ? browser() : node()
|
||||
export function split<T>({
|
||||
node,
|
||||
browser,
|
||||
}: {
|
||||
node: () => T;
|
||||
browser: () => T;
|
||||
}) {
|
||||
return isBrowser ? browser() : node();
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env zx
|
||||
|
||||
import 'zx/globals'
|
||||
|
||||
$.verbose = true
|
||||
|
||||
const BROWSERS = ['firefox', 'chromium', 'webkit']
|
||||
await Promise.all([
|
||||
BROWSERS.map((browser) => $`pnpm vitest --browser.provider=playwright --browser.name=${browser} --browser.headless`),
|
||||
])
|
||||
+32
-32
@@ -1,44 +1,44 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { AES, Bytes, Hashes, Hex } from '../dist/index.js'
|
||||
import { Precomputed } from './values.js'
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { AES, Bytes, Hashes, Hex } from "../src/index.js";
|
||||
import { Precomputed } from "./values.js";
|
||||
|
||||
describe('AES', () => {
|
||||
describe("AES", () => {
|
||||
for (const message of Object.values(Precomputed.Crypto.Messages)) {
|
||||
describe(`Message: ${message.slice(0, 8)}...`, () => {
|
||||
describe('Basic API', () => {
|
||||
describe("Basic API", () => {
|
||||
for (const keySize of [128, 256]) {
|
||||
it('Key Size: ' + keySize, async () => {
|
||||
const data = Bytes.encode(message)
|
||||
const [key] = await AES.derive('foo', {
|
||||
name: 'PBKDF2',
|
||||
it("Key Size: " + keySize, async () => {
|
||||
const data = Bytes.encode(message);
|
||||
const [key] = await AES.derive("foo", {
|
||||
name: "PBKDF2",
|
||||
hash: Hashes.SHA_512,
|
||||
iterations: 1000,
|
||||
length: keySize,
|
||||
salt: Hex.decode(Precomputed.Crypto.Bytes[16]),
|
||||
})
|
||||
const ciphertext = await AES.encrypt(data, key, AES.Modes.AES_GCM)
|
||||
const plaintext = await AES.decrypt(ciphertext, key)
|
||||
expect(data.buffer).toEqual(plaintext.buffer)
|
||||
expect(message).toEqual(Bytes.decode(plaintext))
|
||||
})
|
||||
});
|
||||
const ciphertext = await AES.encrypt(data, key, AES.Modes.AES_GCM);
|
||||
const plaintext = await AES.decrypt(ciphertext, key);
|
||||
expect(data.buffer).toEqual(plaintext.buffer);
|
||||
expect(message).toEqual(Bytes.decode(plaintext));
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('Generated Key', async () => {
|
||||
const key = await AES.generateKey()
|
||||
const data = Bytes.encode(message)
|
||||
const ciphertext = await AES.encrypt(data, key)
|
||||
const plaintext = await AES.decrypt(ciphertext, key)
|
||||
expect(data.buffer).toEqual(plaintext.buffer)
|
||||
expect(message).toEqual(Bytes.decode(plaintext))
|
||||
})
|
||||
it("Generated Key", async () => {
|
||||
const key = await AES.generateKey();
|
||||
const data = Bytes.encode(message);
|
||||
const ciphertext = await AES.encrypt(data, key);
|
||||
const plaintext = await AES.decrypt(ciphertext, key);
|
||||
expect(data.buffer).toEqual(plaintext.buffer);
|
||||
expect(message).toEqual(Bytes.decode(plaintext));
|
||||
});
|
||||
|
||||
it('Easy API', async () => {
|
||||
const password = 'foobar'
|
||||
const encrypted = await AES.encryptEasy(message, password)
|
||||
const decrypted = await AES.decryptEasy(encrypted, password)
|
||||
expect(message).toEqual(decrypted)
|
||||
})
|
||||
})
|
||||
it("Easy API", async () => {
|
||||
const password = "foobar";
|
||||
const encrypted = await AES.encryptEasy(message, password);
|
||||
const decrypted = await AES.decryptEasy(encrypted, password);
|
||||
expect(message).toEqual(decrypted);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
+25
-25
@@ -1,38 +1,38 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Base64, Bytes, Hex } from '../dist/index.js'
|
||||
import { Precomputed } from './values.js'
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Base64, Bytes, Hex } from "../src/index.js";
|
||||
import { Precomputed } from "./values.js";
|
||||
|
||||
describe('Encoding', () => {
|
||||
describe('Bytes', () => {
|
||||
describe("Encoding", () => {
|
||||
describe("Bytes", () => {
|
||||
for (const [input, output] of Object.entries(Precomputed.Encoding.Bytes)) {
|
||||
it(`Should encode ${input} to ${output}`, async () => {
|
||||
expect(Bytes.encode(input).buffer).toEqual(output.buffer)
|
||||
})
|
||||
expect(Bytes.encode(input).buffer).toEqual(output.buffer);
|
||||
});
|
||||
it(`Should decode ${output} to ${input}`, async () => {
|
||||
expect(Bytes.decode(output)).toEqual(input)
|
||||
})
|
||||
expect(Bytes.decode(output)).toEqual(input);
|
||||
});
|
||||
}
|
||||
})
|
||||
describe('Hex', () => {
|
||||
});
|
||||
describe("Hex", () => {
|
||||
for (const [input, output] of Object.entries(Precomputed.Encoding.Hex)) {
|
||||
const buffer = Bytes.encode(input)
|
||||
const buffer = Bytes.encode(input);
|
||||
it(`Should encode ${input} to ${output}`, async () => {
|
||||
expect(Hex.encode(buffer)).toEqual(output)
|
||||
})
|
||||
expect(Hex.encode(buffer)).toEqual(output);
|
||||
});
|
||||
it(`Should decode ${output} to ${input}`, async () => {
|
||||
expect(Hex.decode(output).buffer).toEqual(buffer.buffer)
|
||||
})
|
||||
expect(Hex.decode(output).buffer).toEqual(buffer.buffer);
|
||||
});
|
||||
}
|
||||
})
|
||||
describe('Base64', () => {
|
||||
});
|
||||
describe("Base64", () => {
|
||||
for (const [input, output] of Object.entries(Precomputed.Encoding.Base64)) {
|
||||
const buffer = Bytes.encode(input)
|
||||
const buffer = Bytes.encode(input);
|
||||
it(`Should encode ${input} to ${output}`, async () => {
|
||||
expect(await Base64.encode(buffer)).toEqual(output)
|
||||
})
|
||||
expect(await Base64.encode(buffer)).toEqual(output);
|
||||
});
|
||||
it(`Should decode ${output} to ${input}`, async () => {
|
||||
expect((await Base64.decode(output)).buffer).toEqual(buffer.buffer)
|
||||
})
|
||||
expect((await Base64.decode(output)).buffer).toEqual(buffer.buffer);
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
+16
-16
@@ -1,26 +1,26 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Bytes, Hash, Hashes, Hex } from '../dist/index.js'
|
||||
import { Precomputed } from './values.js'
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Bytes, Hash, Hashes, Hex } from "../src/index.js";
|
||||
import { Precomputed } from "./values.js";
|
||||
|
||||
describe('Hash', () => {
|
||||
describe("Hash", () => {
|
||||
for (const type of Object.keys(Hashes)) {
|
||||
describe(type, () => {
|
||||
const values = Precomputed.Hash[type]
|
||||
const values = Precomputed.Hash[type];
|
||||
for (const [input, output] of Object.entries(values)) {
|
||||
if (typeof output !== 'string') throw new Error('Bad test data')
|
||||
if (typeof output !== "string") throw new Error("Bad test data");
|
||||
|
||||
it(`Should hash "${input}" to "${output.slice(0, 8)}..."`, async () => {
|
||||
const hashed = await Hash.hash(input, Hashes[type])
|
||||
expect(hashed).toEqual(output)
|
||||
})
|
||||
const hashed = await Hash.hash(input, Hashes[type]);
|
||||
expect(hashed).toEqual(output);
|
||||
});
|
||||
|
||||
it(`Should hash "${input}" to "${output.slice(0, 8)}..." as buffer`, async () => {
|
||||
const outputBuffer = Hex.decode(output)
|
||||
const inputBuffer = Bytes.encode(input)
|
||||
const hashed = await Hash.hash(inputBuffer, Hashes[type])
|
||||
expect(hashed).toEqual(outputBuffer)
|
||||
})
|
||||
const outputBuffer = Hex.decode(output);
|
||||
const inputBuffer = Bytes.encode(input);
|
||||
const hashed = await Hash.hash(inputBuffer, Hashes[type]);
|
||||
expect(hashed).toEqual(outputBuffer);
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
+19
-15
@@ -1,18 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getRandomBytes } from '../dist/index.js'
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getRandomBytes } from "../src/index.js";
|
||||
|
||||
describe('Random', () => {
|
||||
it('Should be able to create random values', async () => {
|
||||
const buffer = await getRandomBytes(8)
|
||||
expect(buffer).instanceOf(Uint8Array)
|
||||
expect(buffer.byteLength).toEqual(8)
|
||||
})
|
||||
describe("Random", () => {
|
||||
it("Should be able to create random values", async () => {
|
||||
const buffer = await getRandomBytes(8);
|
||||
expect(buffer).instanceOf(Uint8Array);
|
||||
expect(buffer.byteLength).toEqual(8);
|
||||
});
|
||||
|
||||
it('Should throw error on empty array', async () => {
|
||||
await expect(() => getRandomBytes(0)).rejects.toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
it("Should throw error on empty array", async () => {
|
||||
await expect(() =>
|
||||
getRandomBytes(0),
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it('Should throw error on negative bytes', async () => {
|
||||
await expect(() => getRandomBytes(-1)).rejects.toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
})
|
||||
it("Should throw error on negative bytes", async () => {
|
||||
await expect(() =>
|
||||
getRandomBytes(-1),
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
+37
-33
@@ -1,40 +1,44 @@
|
||||
import { describe } from 'vitest'
|
||||
import { Bytes, RSA } from '../dist/index.js'
|
||||
import { Precomputed } from './values.js'
|
||||
import { it } from 'vitest'
|
||||
import { expect } from 'vitest'
|
||||
import { describe } from "vitest";
|
||||
import { Bytes, RSA } from "../src/index.js";
|
||||
import { Precomputed } from "./values.js";
|
||||
import { it } from "vitest";
|
||||
import { expect } from "vitest";
|
||||
|
||||
describe('RSA', () => {
|
||||
describe('Generate keys', function () {
|
||||
it('Should be able to generate a keypair', async () => {
|
||||
await RSA.generateKeyPair()
|
||||
})
|
||||
it('Should be able to generate a keypair with 2048bit', async () => {
|
||||
await RSA.generateKeyPair(2048)
|
||||
})
|
||||
it('Should be able to generate a keypair with 4096bit', async () => {
|
||||
await RSA.generateKeyPair(4096)
|
||||
})
|
||||
it('Should not be able to generate a key below 2048bit', async () => {
|
||||
await expect(() => RSA.generateKeyPair(1024)).rejects.toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
it('Should not be able to generate a key below 2048bit', async () => {
|
||||
await expect(() => RSA.generateKeyPair(-1)).rejects.toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
})
|
||||
describe("RSA", () => {
|
||||
describe("Generate keys", function () {
|
||||
it("Should be able to generate a keypair", async () => {
|
||||
await RSA.generateKeyPair();
|
||||
});
|
||||
it("Should be able to generate a keypair with 2048bit", async () => {
|
||||
await RSA.generateKeyPair(2048);
|
||||
});
|
||||
it("Should be able to generate a keypair with 4096bit", async () => {
|
||||
await RSA.generateKeyPair(4096);
|
||||
});
|
||||
it("Should not be able to generate a key below 2048bit", async () => {
|
||||
await expect(() =>
|
||||
RSA.generateKeyPair(1024),
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
it("Should not be able to generate a key below 2048bit", async () => {
|
||||
await expect(() =>
|
||||
RSA.generateKeyPair(-1),
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encryption', () => {
|
||||
describe("Encryption", () => {
|
||||
for (const message of Object.values(Precomputed.Crypto.Messages)) {
|
||||
it(`Should be able to encrypt and decrypt "${message.slice(0, 8)}..."`, async () => {
|
||||
const pair = await RSA.generateKeyPair(2 ** 11)
|
||||
const bytes = Bytes.encode(message)
|
||||
const pair = await RSA.generateKeyPair(2 ** 11);
|
||||
const bytes = Bytes.encode(message);
|
||||
try {
|
||||
const encrypted = await RSA.encrypt(bytes, pair.public)
|
||||
const decrypted = await RSA.decrypt(encrypted, pair.private)
|
||||
expect(decrypted).toEqual(bytes)
|
||||
expect(message).toEqual(Bytes.decode(decrypted))
|
||||
const encrypted = await RSA.encrypt(bytes, pair.public);
|
||||
const decrypted = await RSA.decrypt(encrypted, pair.private);
|
||||
expect(decrypted).toEqual(bytes);
|
||||
expect(message).toEqual(Bytes.decode(decrypted));
|
||||
} catch {}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
+35
-30
@@ -1,60 +1,65 @@
|
||||
export const Precomputed = {
|
||||
Encoding: {
|
||||
Base64: {
|
||||
occulto: 'b2NjdWx0bw==',
|
||||
test: 'dGVzdA==',
|
||||
'hello world': 'aGVsbG8gd29ybGQ=',
|
||||
occulto: "b2NjdWx0bw==",
|
||||
test: "dGVzdA==",
|
||||
"hello world": "aGVsbG8gd29ybGQ=",
|
||||
},
|
||||
Hex: {
|
||||
test: '74657374',
|
||||
occulto: '6f6363756c746f',
|
||||
'hello world': '68656c6c6f20776f726c64',
|
||||
test: "74657374",
|
||||
occulto: "6f6363756c746f",
|
||||
"hello world": "68656c6c6f20776f726c64",
|
||||
},
|
||||
Bytes: {
|
||||
test: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
|
||||
occulto: new Uint8Array([0x6f, 0x63, 0x63, 0x75, 0x6c, 0x74, 0x6f]),
|
||||
'entropy is king': new Uint8Array([
|
||||
0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x20, 0x69, 0x73, 0x20, 0x6b, 0x69, 0x6e, 0x67,
|
||||
"entropy is king": new Uint8Array([
|
||||
0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x20, 0x69, 0x73, 0x20, 0x6b,
|
||||
0x69, 0x6e, 0x67,
|
||||
]),
|
||||
},
|
||||
},
|
||||
Hash: {
|
||||
SHA_1: {
|
||||
test: 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3',
|
||||
'hello world': '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed',
|
||||
occulto: 'f4b27cfb9e01492f409295fbbc339753fa839c0f',
|
||||
test: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
|
||||
"hello world": "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
|
||||
occulto: "f4b27cfb9e01492f409295fbbc339753fa839c0f",
|
||||
},
|
||||
SHA_256: {
|
||||
test: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
|
||||
'hello world': 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9',
|
||||
occulto: 'df2b97515886051821e4375a33df10486ce55cb3d14acd05dd7465f820ef2481',
|
||||
test: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
|
||||
"hello world":
|
||||
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
|
||||
occulto:
|
||||
"df2b97515886051821e4375a33df10486ce55cb3d14acd05dd7465f820ef2481",
|
||||
},
|
||||
SHA_384: {
|
||||
test: '768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9',
|
||||
'hello world': 'fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd',
|
||||
occulto: '133c1968e937462ed66732409fa305c63335ee62b1114dd2d0ae98b4dd8fa6aca4656c919b295e41efa2d63f0d3c9951',
|
||||
test: "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9",
|
||||
"hello world":
|
||||
"fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd",
|
||||
occulto:
|
||||
"133c1968e937462ed66732409fa305c63335ee62b1114dd2d0ae98b4dd8fa6aca4656c919b295e41efa2d63f0d3c9951",
|
||||
},
|
||||
SHA_512: {
|
||||
test: 'ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff',
|
||||
'hello world':
|
||||
'309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f',
|
||||
test: "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
|
||||
"hello world":
|
||||
"309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f",
|
||||
occulto:
|
||||
'9f7ff06148d415b12290ab7c21f021964ed627574f94f66c994aad4a8e319aa3168a9871edace3e736096cbd957cafa42dbf3feb6efd7763bf936ddc933c9470',
|
||||
"9f7ff06148d415b12290ab7c21f021964ed627574f94f66c994aad4a8e319aa3168a9871edace3e736096cbd957cafa42dbf3feb6efd7763bf936ddc933c9470",
|
||||
},
|
||||
},
|
||||
Crypto: {
|
||||
Bytes: {
|
||||
14: 'e0b28a24252963ff30dd2bb3ec9c',
|
||||
16: '65eeb2044e9eb115956dbf4d0d70cd8f',
|
||||
24: '9fa9e0aace3b0bdcbc871aa3ee3ddb1bece759b811fa4603',
|
||||
32: '848ca08f01f82e28bfa91c85d55ef2a98afd8b32c707c9c790e86b1c53a177e4',
|
||||
14: "e0b28a24252963ff30dd2bb3ec9c",
|
||||
16: "65eeb2044e9eb115956dbf4d0d70cd8f",
|
||||
24: "9fa9e0aace3b0bdcbc871aa3ee3ddb1bece759b811fa4603",
|
||||
32: "848ca08f01f82e28bfa91c85d55ef2a98afd8b32c707c9c790e86b1c53a177e4",
|
||||
},
|
||||
Messages: {
|
||||
test: 'test',
|
||||
occulto: 'occulto',
|
||||
weird: 'Some 🃏 weird 🃏 text',
|
||||
test: "test",
|
||||
occulto: "occulto",
|
||||
weird: "Some 🃏 weird 🃏 text",
|
||||
nietzscheIpsum:
|
||||
'Marvelous intentions joy deceptions overcome sexuality spirit against. Selfish of marvelous play dead war snare eternal-return ultimate. Reason aversion suicide.',
|
||||
"Marvelous intentions joy deceptions overcome sexuality spirit against. Selfish of marvelous play dead war snare eternal-return ultimate. Reason aversion suicide.",
|
||||
},
|
||||
},
|
||||
} as const
|
||||
} as const;
|
||||
|
||||
+5
-11
@@ -1,16 +1,10 @@
|
||||
{
|
||||
"extends": ["@tsconfig/strictest"],
|
||||
"compilerOptions": {
|
||||
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"module": "ES2015" /* Specify what module code is generated. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
"declarationMap": true /* Create sourcemaps for d.ts files. */,
|
||||
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
"isolatedModules": false
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["node"],
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": ["./src"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user