Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "occulto",
"version": "2.0.6",
"description": "crypt utility",
"version": "2.1.0",
"description": "encryption utility",
"keywords": [
"isomorphic",
"crypto",
Expand Down Expand Up @@ -29,27 +29,22 @@
],
"scripts": {
"docs": "typedoc",
"test:node": "vitest",
"test:browsers": "zx test.browsers.js",
"test": "CI=1 run-s build test:*",
"test": "vitest",
"build": "tsc",
"clean": "rm -rf ./dist",
"dev": "vitest",
"prepublishOnly": "run-s clean test"
},
"devDependencies": {
"@tsconfig/strictest": "^2.0.5",
"@types/node": "^22.5.2",
"@vitest/browser": "^2.0.5",
"@types/node": "^22.13.0",
"@vitest/browser": "^3.0.4",
"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"
"playwright": "^1.50.1",
"typedoc": "^0.27.6",
"typedoc-material-theme": "^1.3.0",
"typescript": "^5.7.3",
"vitest": "^3.0.4"
},
"packageManager": "pnpm@9.8.0",
"engines": {
"node": ">=18"
}
"packageManager": "pnpm@9.15.4"
}
2,145 changes: 1,163 additions & 982 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

17 changes: 8 additions & 9 deletions src/crypto/aes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { type TypedArray } from '../utils/base.js'
import { getCrypto } from './crypto.js'
import { Base64, Bytes } from './encoding.js'
import { Hashes } from './hash.js'
Expand All @@ -15,7 +14,7 @@ export type KeyData = {
name: 'PBKDF2'
hash: Hashes
iterations: number
salt: TypedArray
salt: ArrayBufferLike
length: number
}

Expand All @@ -35,12 +34,12 @@ export class AES {

private static InvalidCiphertext = new Error('Invalid ciphertext')

private static async join(...args: TypedArray[]): Promise<string> {
private static async join(...args: ArrayBufferLike[]): Promise<string> {
const strings = await Promise.all(args.map(Base64.encode))
return strings.join(AES.delimiter)
}

private static async split(ciphertext: string): Promise<TypedArray[]> {
private static async split(ciphertext: string): Promise<ArrayBufferLike[]> {
const splitted = ciphertext.split(AES.delimiter)
return Promise.all(splitted.map(Base64.decode))
}
Expand All @@ -49,7 +48,7 @@ export class AES {
* 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<[ArrayBufferLike, KeyData]> {
options ??= {
name: 'PBKDF2',
hash: Hashes.SHA_512,
Expand All @@ -66,7 +65,7 @@ export class AES {
return [new Uint8Array(bits), options]
}

static async encrypt(data: TypedArray, key: TypedArray, mode: Modes = Modes.AES_GCM): Promise<string> {
static async encrypt(data: ArrayBufferLike, key: ArrayBufferLike, mode: Modes = Modes.AES_GCM): Promise<string> {
const c = await getCrypto()

let iv: Uint8Array
Expand All @@ -88,7 +87,7 @@ export class AES {
return AES.join(Bytes.encode(alg), iv, encryptedBuffer)
}

static async decrypt(ciphertext: string, key: TypedArray): Promise<TypedArray> {
static async decrypt(ciphertext: string, key: ArrayBufferLike): Promise<ArrayBufferLike> {
const c = await getCrypto()

const [alg, iv, data] = await AES.split(ciphertext)
Expand All @@ -107,7 +106,7 @@ export class AES {
return new Uint8Array(decrypted)
}

static async encryptEasy(data: string | TypedArray, key: string, mode: Modes = Modes.AES_GCM): Promise<string> {
static async encryptEasy(data: string | ArrayBufferLike, 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)

Expand Down Expand Up @@ -143,7 +142,7 @@ export class AES {
return Bytes.decode(decrypted)
}

static async generateKey(): Promise<TypedArray> {
static async generateKey(): Promise<ArrayBufferLike> {
const c = await getCrypto()
const key = await c.subtle.generateKey(
{
Expand Down
14 changes: 7 additions & 7 deletions src/crypto/encoding.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { split, type TypedArray } from '../utils/base.js'
import { split } from '../utils/base.js'

export class Base64 {
private static prefix = 'data:application/octet-stream;base64,'

static encode(s: TypedArray): Promise<string> {
static encode(s: ArrayBufferLike): Promise<string> {
return split({
async node() {
return Buffer.from(s).toString('base64')
Expand All @@ -22,7 +22,7 @@ export class Base64 {
})
}

static decode(s: string): Promise<TypedArray> {
static decode(s: string): Promise<ArrayBufferLike> {
return split({
async node() {
return Buffer.from(s, 'base64')
Expand All @@ -38,15 +38,15 @@ export class Base64 {
}

export class Hex {
static encode(buffer: TypedArray): string {
static encode(buffer: ArrayBufferLike): string {
let s = ''
for (const i of new Uint8Array(buffer)) {
s += i.toString(16).padStart(2, '0')
}
return s
}

static decode(s: string): TypedArray {
static decode(s: string): ArrayBufferLike {
const size = s.length / 2
const buffer = new Uint8Array(size)
for (let i = 0; i < size; i++) {
Expand All @@ -59,7 +59,7 @@ export class Hex {
}

export class Bytes {
static decode(data: TypedArray): string {
static decode(data: ArrayBufferLike): string {
return split({
node() {
return Buffer.from(data).toString('utf-8')
Expand All @@ -70,7 +70,7 @@ export class Bytes {
})
}

static encode(data: string): TypedArray {
static encode(data: string): ArrayBufferLike {
return split({
node() {
return Buffer.from(data)
Expand Down
5 changes: 2 additions & 3 deletions src/crypto/hash.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { type TypedArray } from '../utils/base.js'
import { getCrypto } from './crypto.js'
import { Bytes, Hex } from './encoding.js'

Expand All @@ -21,8 +20,8 @@ export enum Hashes {

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> {
static async hash(data: ArrayBufferLike, hash: Hashes): Promise<ArrayBufferLike>
static async hash(data: string | ArrayBufferLike, hash: Hashes): Promise<string | ArrayBufferLike> {
const isString = typeof data === 'string'
const c = await getCrypto()
const result = await c.subtle.digest(hash, isString ? Bytes.encode(data) : data)
Expand Down
3 changes: 1 addition & 2 deletions src/crypto/random.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type TypedArray } from '../utils/base.js'
import { getCrypto } from './crypto.js'

export async function getRandomBytes(bytes: number): Promise<TypedArray> {
export async function getRandomBytes(bytes: number): Promise<ArrayBufferLike> {
if (bytes <= 0) throw new Error('Invalid number of bytes')

const buffer = new Uint8Array(bytes)
Expand Down
10 changes: 5 additions & 5 deletions src/crypto/rsa.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { TypedArray } from '../utils/base.js'
import { getCrypto } from './crypto.js'
import { Base64 } from './encoding.js'

Expand Down Expand Up @@ -71,7 +70,8 @@ class Key {
// @ts-ignore
const mod = key?.algorithm?.modulusLength
if (isNaN(mod)) throw Constants.error.invalidKey
return mod / 8 - (2 * 512) / 8 - 2
const maxBytes = mod / 8 - (2 * 512) / 8 - 2
return maxBytes
}
}

Expand Down Expand Up @@ -100,7 +100,7 @@ export class RSA {
}
}

static async encrypt(data: TypedArray, key: string): Promise<TypedArray> {
static async encrypt(data: ArrayBufferLike, key: string): Promise<ArrayBufferLike> {
let keyObj: CryptoKey
try {
keyObj = await Key.decode(key)
Expand All @@ -112,14 +112,14 @@ export class RSA {
}

// Check if data is too large
if (data.length > Key.getMaxMessageSize(keyObj)) throw Constants.error.dataTooLong
if (new Uint8Array(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)
}

static async decrypt(data: TypedArray, key: string): Promise<TypedArray> {
static async decrypt(data: ArrayBufferLike, key: string): Promise<ArrayBufferLike> {
let keyObj: CryptoKey
try {
keyObj = await Key.decode(key)
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ 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'
11 changes: 0 additions & 11 deletions src/utils/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,6 @@ export const isBrowser = typeof window !== 'undefined'
*/
export type PromiseOrValue<T> = T | Promise<T>

export type TypedArray =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| BigInt64Array
| BigUint64Array

/**
* @internal
*/
Expand Down
10 changes: 0 additions & 10 deletions test.browsers.js

This file was deleted.

3 changes: 3 additions & 0 deletions typedoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"name": "Occulto",
"includeVersion": true,

"plugin": ["typedoc-material-theme"],
"themeColor": "#cb9820",

"excludeInternal": true,
"excludePrivate": true
}
11 changes: 11 additions & 0 deletions vitest.config.ts.old
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
browser: {
provider: 'playwright',
enabled: true,
headless: true,
instances: [{ browser: 'firefox' }, { browser: 'webkit' }, { browser: 'chromium' }],
},
},
})
21 changes: 21 additions & 0 deletions vitest.workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineWorkspace } from 'vitest/config'

export default defineWorkspace([
{
test: {
name: 'node',
environment: 'node',
},
},
{
test: {
name: 'browser',
browser: {
provider: 'playwright',
enabled: true,
headless: true,
instances: [{ browser: 'firefox' }, { browser: 'webkit' }, { browser: 'chromium' }],
},
},
},
])