mirror of
https://github.com/cupcakearmy/occulto.git
synced 2025-09-06 22:30:41 +00:00
Compare commits
9 Commits
v2.0.0-rc.
...
v2.0.1
Author | SHA1 | Date | |
---|---|---|---|
5e811edbc5 | |||
66b54dd27d | |||
2d23b95605 | |||
1ab82a78fe | |||
5babec20ce | |||
d1b07e0a81 | |||
f2f8607c39 | |||
8397b7fc26 | |||
211d5f7629 |
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: "Publish to NPM"
|
name: 'Publish to NPM'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -16,13 +16,13 @@ jobs:
|
|||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Setup PNPM
|
- name: Setup PNPM
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 8
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
6
.github/workflows/test.yaml
vendored
6
.github/workflows/test.yaml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Run Tests"
|
name: 'Run Tests'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -17,12 +17,12 @@ jobs:
|
|||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
|
|
||||||
- name: Setup PNPM
|
- name: Setup PNPM
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 8
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2.0.1]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated dependencies.
|
||||||
|
|
||||||
## [2.0.0]
|
## [2.0.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
38
README.md
38
README.md
@@ -33,8 +33,10 @@ npm i occulto
|
|||||||
```typescript
|
```typescript
|
||||||
import { RSA } from 'occulto'
|
import { RSA } from 'occulto'
|
||||||
|
|
||||||
const pair = await RSA.generateKeyPair()
|
const pair = await RSA.generateKeyPair(2 ** 11)
|
||||||
const encrypted = await RSA.encrypt('some text', pair.public)
|
const bytes = Bytes.encode(message)
|
||||||
|
|
||||||
|
const encrypted = await RSA.encrypt(bytes, pair.public)
|
||||||
const decrypted = await RSA.decrypt(encrypted, pair.private)
|
const decrypted = await RSA.decrypt(encrypted, pair.private)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -42,11 +44,29 @@ const decrypted = await RSA.decrypt(encrypted, pair.private)
|
|||||||
|
|
||||||
[Available Modes](https://occulto.pages.dev/enums/Modes)
|
[Available Modes](https://occulto.pages.dev/enums/Modes)
|
||||||
|
|
||||||
```javascript
|
There is an _easy_ API, that will take care of everything for you.
|
||||||
import { Symmetric } from 'occulto'
|
|
||||||
|
|
||||||
const encrypted = await Symmetric.encryptEasy('some string', 'myPass')
|
```typescript
|
||||||
const decrypted = await Symmetric.decryptEasy(encrypted, 'myPass')
|
import { AES } from 'occulto'
|
||||||
|
|
||||||
|
const password = 'foobar'
|
||||||
|
const message = 'this is a secret'
|
||||||
|
|
||||||
|
const encrypted = await AES.encryptEasy(message, password)
|
||||||
|
const decrypted = await AES.decryptEasy(encrypted, password)
|
||||||
|
```
|
||||||
|
|
||||||
|
The low level API is also exposed for advanced usages.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AES } from 'occulto'
|
||||||
|
|
||||||
|
const message = 'this is a secret'
|
||||||
|
const key = await AES.generateKey()
|
||||||
|
const data = Bytes.encode(message)
|
||||||
|
|
||||||
|
const ciphertext = await AES.encrypt(data, key)
|
||||||
|
const plaintext = await AES.decrypt(ciphertext, key)
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Hash](https://occulto.pages.dev/classes/Hash)
|
### [Hash](https://occulto.pages.dev/classes/Hash)
|
||||||
@@ -54,9 +74,7 @@ const decrypted = await Symmetric.decryptEasy(encrypted, 'myPass')
|
|||||||
[Available hashes](https://occulto.pages.dev/enums/Hashes)
|
[Available hashes](https://occulto.pages.dev/enums/Hashes)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Hash } from 'occulto'
|
import { Hash, Hashes } from 'occulto'
|
||||||
|
|
||||||
const hash = Hash.digest('something')
|
const hashed = await Hash.hash('Some value', Hashes.SHA_512)
|
||||||
|
|
||||||
const h = Hash.digest('something', Hash.Hashes.MD5)
|
|
||||||
```
|
```
|
||||||
|
18
package.json
18
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "occulto",
|
"name": "occulto",
|
||||||
"version": "2.0.0-rc.9",
|
"version": "2.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "crypt utility",
|
"description": "crypt utility",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"node": ">=16",
|
"node": ">=16",
|
||||||
"npm": "please-use-pnpm",
|
"npm": "please-use-pnpm",
|
||||||
"yarn": "please-use-pnpm",
|
"yarn": "please-use-pnpm",
|
||||||
"pnpm": "7"
|
"pnpm": ">=8"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -41,15 +41,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@endyjasmi/karma-playwright-launcher": "^0.0.4",
|
"@endyjasmi/karma-playwright-launcher": "^0.0.4",
|
||||||
"@types/node": "^16.11.66",
|
"@types/node": "^16.18.24",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.7",
|
||||||
"karma": "^6.4.1",
|
"karma": "^6.4.2",
|
||||||
"karma-mocha": "^2.0.1",
|
"karma-mocha": "^2.0.1",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.2.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"playwright": "^1.27.1",
|
"playwright": "^1.32.3",
|
||||||
"typedoc": "^0.23.17",
|
"typedoc": "^0.24.6",
|
||||||
"typescript": "^4.8.4"
|
"typescript": "^5.0.4"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
933
pnpm-lock.yaml
generated
933
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,8 @@ export enum Modes {
|
|||||||
export class AES {
|
export class AES {
|
||||||
static Modes = Modes
|
static Modes = Modes
|
||||||
|
|
||||||
private static delimiter = '--' // delimiter with a character that is not allowed in base64 or hex
|
// delimiter with a character that is not allowed in base64 or hex
|
||||||
|
private static delimiter = '--'
|
||||||
private static delimiterEasy = '---'
|
private static delimiterEasy = '---'
|
||||||
|
|
||||||
private static InvalidCiphertext = new Error('Invalid ciphertext')
|
private static InvalidCiphertext = new Error('Invalid ciphertext')
|
||||||
@@ -141,4 +142,18 @@ export class AES {
|
|||||||
const decrypted = await this.decrypt(data, keyDerived)
|
const decrypted = await this.decrypt(data, keyDerived)
|
||||||
return Bytes.decode(decrypted)
|
return Bytes.decode(decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async generateKey(): Promise<TypedArray> {
|
||||||
|
const c = await getCrypto()
|
||||||
|
const key = await c.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'AES-GCM',
|
||||||
|
length: 256,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
['encrypt', 'decrypt']
|
||||||
|
)
|
||||||
|
const buffer = await c.subtle.exportKey('raw', key)
|
||||||
|
return new Uint8Array(buffer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,4 +3,4 @@ export * from './crypto/encoding.js'
|
|||||||
export * from './crypto/hash.js'
|
export * from './crypto/hash.js'
|
||||||
export * from './crypto/random.js'
|
export * from './crypto/random.js'
|
||||||
export * from './crypto/rsa.js'
|
export * from './crypto/rsa.js'
|
||||||
export * from './utils/base.js'
|
export { TypedArray } from './utils/base.js'
|
||||||
|
@@ -2,14 +2,17 @@ import { AES, Bytes, Hashes, Hex } from '../dist/index.js'
|
|||||||
import { Precomputed } from './values.js'
|
import { Precomputed } from './values.js'
|
||||||
|
|
||||||
describe('AES', () => {
|
describe('AES', () => {
|
||||||
it('Basic API', async () => {
|
for (const message of Object.values(Precomputed.Crypto.Messages)) {
|
||||||
const message = Precomputed.Crypto.Messages.nietzscheIpsum
|
describe(`Message: ${message.slice(0, 8)}...`, () => {
|
||||||
|
describe('Basic API', () => {
|
||||||
|
for (const keySize of [128, 256]) {
|
||||||
|
it('Key Size: ' + keySize, async () => {
|
||||||
const data = Bytes.encode(message)
|
const data = Bytes.encode(message)
|
||||||
const [key] = await AES.derive('foo', {
|
const [key] = await AES.derive('foo', {
|
||||||
name: 'PBKDF2',
|
name: 'PBKDF2',
|
||||||
hash: Hashes.SHA_512,
|
hash: Hashes.SHA_512,
|
||||||
iterations: 1000,
|
iterations: 1000,
|
||||||
length: 256,
|
length: keySize,
|
||||||
salt: Hex.decode(Precomputed.Crypto.Bytes[16]),
|
salt: Hex.decode(Precomputed.Crypto.Bytes[16]),
|
||||||
})
|
})
|
||||||
const ciphertext = await AES.encrypt(data, key, AES.Modes.GCM)
|
const ciphertext = await AES.encrypt(data, key, AES.Modes.GCM)
|
||||||
@@ -17,12 +20,24 @@ describe('AES', () => {
|
|||||||
chai.expect(data).to.be.deep.equal(plaintext)
|
chai.expect(data).to.be.deep.equal(plaintext)
|
||||||
chai.expect(message).to.be.equal(Bytes.decode(plaintext))
|
chai.expect(message).to.be.equal(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)
|
||||||
|
chai.expect(data).to.be.deep.equal(plaintext)
|
||||||
|
chai.expect(message).to.be.equal(Bytes.decode(plaintext))
|
||||||
|
})
|
||||||
|
|
||||||
it('Easy API', async () => {
|
it('Easy API', async () => {
|
||||||
const message = Precomputed.Crypto.Messages.nietzscheIpsum
|
|
||||||
const password = 'foobar'
|
const password = 'foobar'
|
||||||
const encrypted = await AES.encryptEasy(message, password)
|
const encrypted = await AES.encryptEasy(message, password)
|
||||||
const decrypted = await AES.decryptEasy(encrypted, password)
|
const decrypted = await AES.decryptEasy(encrypted, password)
|
||||||
chai.expect(message).to.be.equal(decrypted)
|
chai.expect(message).to.be.equal(decrypted)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@@ -1,7 +1,29 @@
|
|||||||
import { Base64, Bytes } from '../dist/index.js'
|
import { Base64, Bytes, Hex } from '../dist/index.js'
|
||||||
|
import { compareArrayLike } from './utils.js'
|
||||||
import { Precomputed } from './values.js'
|
import { Precomputed } from './values.js'
|
||||||
|
|
||||||
describe('Encoding', () => {
|
describe('Encoding', () => {
|
||||||
|
describe('Bytes', () => {
|
||||||
|
for (const [input, output] of Object.entries(Precomputed.Encoding.Bytes)) {
|
||||||
|
it(`Should encode ${input} to ${output}`, async () => {
|
||||||
|
compareArrayLike(Bytes.encode(input), output)
|
||||||
|
})
|
||||||
|
it(`Should decode ${output} to ${input}`, async () => {
|
||||||
|
compareArrayLike(await Bytes.decode(output), input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
describe('Hex', () => {
|
||||||
|
for (const [input, output] of Object.entries(Precomputed.Encoding.Hex)) {
|
||||||
|
const buffer = Bytes.encode(input)
|
||||||
|
it(`Should encode ${input} to ${output}`, async () => {
|
||||||
|
chai.expect(await Hex.encode(buffer)).to.equal(output)
|
||||||
|
})
|
||||||
|
it(`Should decode ${output} to ${input}`, async () => {
|
||||||
|
chai.expect(await Hex.decode(output)).to.deep.equal(buffer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
describe('Base64', () => {
|
describe('Base64', () => {
|
||||||
for (const [input, output] of Object.entries(Precomputed.Encoding.Base64)) {
|
for (const [input, output] of Object.entries(Precomputed.Encoding.Base64)) {
|
||||||
const buffer = Bytes.encode(input)
|
const buffer = Bytes.encode(input)
|
||||||
|
@@ -6,3 +6,10 @@ export class Promises {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compareArrayLike(a, b) {
|
||||||
|
chai.expect(a.length).to.equal(b.length)
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
chai.expect(a[i]).to.equal(b[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -5,6 +5,18 @@ export const Precomputed = {
|
|||||||
test: 'dGVzdA==',
|
test: 'dGVzdA==',
|
||||||
'hello world': 'aGVsbG8gd29ybGQ=',
|
'hello world': 'aGVsbG8gd29ybGQ=',
|
||||||
},
|
},
|
||||||
|
Hex: {
|
||||||
|
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,
|
||||||
|
]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Hash: {
|
Hash: {
|
||||||
SHA_1: {
|
SHA_1: {
|
||||||
@@ -39,6 +51,8 @@ export const Precomputed = {
|
|||||||
},
|
},
|
||||||
Messages: {
|
Messages: {
|
||||||
test: 'test',
|
test: 'test',
|
||||||
|
occulto: 'occulto',
|
||||||
|
weird: 'Some 🃏 weird 🃏 text',
|
||||||
nietzscheIpsum:
|
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.',
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user