mirror of
https://github.com/cupcakearmy/occulto.git
synced 2024-11-01 06:04:17 +01:00
rely on native api
This commit is contained in:
parent
a015d4f46a
commit
4f17d72ea7
164
src/AES.ts
164
src/AES.ts
@ -1,164 +0,0 @@
|
|||||||
import forge from 'node-forge'
|
|
||||||
|
|
||||||
export enum Ciphers {
|
|
||||||
AES_256_GCM,
|
|
||||||
AES_192_GCM,
|
|
||||||
AES_128_GCM,
|
|
||||||
AES_256_CTR,
|
|
||||||
AES_192_CTR,
|
|
||||||
AES_128_CTR,
|
|
||||||
AES_256_CBC,
|
|
||||||
AES_192_CBC,
|
|
||||||
AES_128_CBC,
|
|
||||||
}
|
|
||||||
|
|
||||||
type CipherConfig = {
|
|
||||||
alg: forge.cipher.Algorithm,
|
|
||||||
saltSize: number
|
|
||||||
keySize: number
|
|
||||||
tagSize: number
|
|
||||||
it: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type EncryptedItem = {
|
|
||||||
alg: forge.cipher.Algorithm,
|
|
||||||
data: string,
|
|
||||||
keySize: number,
|
|
||||||
iv: string,
|
|
||||||
it: number
|
|
||||||
salt: string,
|
|
||||||
tag: string,
|
|
||||||
tagSize: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class AES {
|
|
||||||
|
|
||||||
static Ciphers = Ciphers
|
|
||||||
|
|
||||||
static encrypt(data: string, key: string, type: Ciphers = Ciphers.AES_256_GCM): string {
|
|
||||||
const { alg, it, keySize, saltSize, tagSize } = AES.getCipherConfig(type)
|
|
||||||
const salt = forge.random.getBytesSync(saltSize)
|
|
||||||
const iv = forge.random.getBytesSync(keySize)
|
|
||||||
|
|
||||||
const cipher = forge.cipher.createCipher(
|
|
||||||
alg,
|
|
||||||
forge.pkcs5.pbkdf2(key, salt, it, keySize),
|
|
||||||
)
|
|
||||||
|
|
||||||
cipher.start({
|
|
||||||
iv,
|
|
||||||
tagLength: tagSize,
|
|
||||||
})
|
|
||||||
cipher.update(forge.util.createBuffer(data))
|
|
||||||
cipher.finish()
|
|
||||||
|
|
||||||
const encrypted: EncryptedItem = {
|
|
||||||
alg,
|
|
||||||
data: forge.util.hexToBytes(cipher.output.toHex()),
|
|
||||||
keySize,
|
|
||||||
iv,
|
|
||||||
it,
|
|
||||||
salt,
|
|
||||||
tag: forge.util.hexToBytes(cipher.mode.tag.toHex()),
|
|
||||||
tagSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.from(JSON.stringify(encrypted)).toString('base64')
|
|
||||||
}
|
|
||||||
|
|
||||||
static decrypt(e: string, key: string): string {
|
|
||||||
const { alg, data, keySize, iv, it, salt, tag, tagSize }: EncryptedItem = JSON.parse(Buffer.from(e, 'base64').toString())
|
|
||||||
|
|
||||||
const cipher = forge.cipher.createCipher(
|
|
||||||
alg,
|
|
||||||
forge.pkcs5.pbkdf2(key, salt, it, keySize),
|
|
||||||
)
|
|
||||||
|
|
||||||
cipher.start({
|
|
||||||
iv,
|
|
||||||
tag: new forge.util.ByteStringBuffer(tag),
|
|
||||||
tagLength: tagSize,
|
|
||||||
})
|
|
||||||
cipher.update(new forge.util.ByteStringBuffer(data))
|
|
||||||
cipher.finish()
|
|
||||||
|
|
||||||
return cipher.output.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getCipherConfig = (type: Ciphers): CipherConfig => {
|
|
||||||
switch (type) {
|
|
||||||
case Ciphers.AES_128_GCM:
|
|
||||||
return {
|
|
||||||
alg: 'AES-GCM',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 16,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_192_GCM:
|
|
||||||
return {
|
|
||||||
alg: 'AES-GCM',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 24,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_256_GCM:
|
|
||||||
return {
|
|
||||||
alg: 'AES-GCM',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 32,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_128_CBC:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CBC',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 16,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_192_CBC:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CBC',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 24,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_256_CBC:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CBC',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 32,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_128_CTR:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CTR',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 16,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_192_CTR:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CTR',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 24,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
case Ciphers.AES_256_CTR:
|
|
||||||
return {
|
|
||||||
alg: 'AES-CTR',
|
|
||||||
saltSize: 128,
|
|
||||||
keySize: 32,
|
|
||||||
tagSize: 128,
|
|
||||||
it: 2 ** 12,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
39
src/RSA.ts
39
src/RSA.ts
@ -1,39 +0,0 @@
|
|||||||
import forge from 'node-forge'
|
|
||||||
import { Base64 } from './Util'
|
|
||||||
|
|
||||||
type PrivateKey = string
|
|
||||||
type PublicKey = string
|
|
||||||
|
|
||||||
export type KeyPair = {
|
|
||||||
pub: PublicKey
|
|
||||||
prv: PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default class RSA {
|
|
||||||
|
|
||||||
static gen = (size: number = 2 ** 12): Promise<KeyPair> => new Promise<KeyPair>((resolve, reject) => {
|
|
||||||
forge.pki.rsa.generateKeyPair({ bits: size }, function (err, keypair) {
|
|
||||||
if (err) reject()
|
|
||||||
else resolve({
|
|
||||||
pub: forge.pki.publicKeyToPem(keypair.publicKey),
|
|
||||||
prv: forge.pki.privateKeyToPem(keypair.privateKey),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
static encrypt(data: string, key: PublicKey): string {
|
|
||||||
const obj = forge.pki.publicKeyFromPem(key)
|
|
||||||
if (obj instanceof ArrayBuffer) throw new Error()
|
|
||||||
|
|
||||||
return Base64.encode(obj.encrypt(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
static decrypt(data: string, key: PrivateKey): string {
|
|
||||||
const obj = forge.pki.privateKeyFromPem(key)
|
|
||||||
if (obj instanceof ArrayBuffer) throw new Error()
|
|
||||||
|
|
||||||
return obj.decrypt(Base64.decode(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
157
src/Symmetric.ts
Normal file
157
src/Symmetric.ts
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import {
|
||||||
|
Cipher,
|
||||||
|
CipherCCM,
|
||||||
|
CipherGCM,
|
||||||
|
createCipheriv,
|
||||||
|
createDecipheriv,
|
||||||
|
Decipher,
|
||||||
|
DecipherCCM,
|
||||||
|
DecipherGCM,
|
||||||
|
randomBytes,
|
||||||
|
scryptSync,
|
||||||
|
} from 'crypto'
|
||||||
|
import { TransformOptions } from 'stream'
|
||||||
|
|
||||||
|
import { Base64 } from './Util'
|
||||||
|
|
||||||
|
export enum Ciphers {
|
||||||
|
ChaCha20,
|
||||||
|
ChaCha20_Poly1305,
|
||||||
|
AES_256_GCM,
|
||||||
|
AES_192_GCM,
|
||||||
|
AES_128_GCM,
|
||||||
|
AES_256_CTR,
|
||||||
|
AES_192_CTR,
|
||||||
|
AES_128_CTR,
|
||||||
|
}
|
||||||
|
|
||||||
|
type CipherConfig = {
|
||||||
|
alg: string,
|
||||||
|
keySize: number,
|
||||||
|
ivSize: number
|
||||||
|
mac?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type EncryptedItem = {
|
||||||
|
alg: string,
|
||||||
|
data: string,
|
||||||
|
iv: string,
|
||||||
|
salt: string
|
||||||
|
keySize: number,
|
||||||
|
tag?: string,
|
||||||
|
tagSize?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Symmetric {
|
||||||
|
|
||||||
|
static Ciphers = Ciphers
|
||||||
|
static Encoding: BufferEncoding = 'base64'
|
||||||
|
|
||||||
|
static encrypt(data: string, pass: string, type: Ciphers = Ciphers.AES_256_CTR): string {
|
||||||
|
const { alg, ivSize, mac, keySize } = Symmetric.getCipherConfig(type)
|
||||||
|
|
||||||
|
const iv = randomBytes(ivSize)
|
||||||
|
const salt = randomBytes(keySize)
|
||||||
|
const key = scryptSync(pass, salt, keySize)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const options: TransformOptions = mac ? { authTagLength: mac } : undefined
|
||||||
|
const cipher: CipherGCM | CipherCCM | Cipher = createCipheriv(alg, key, iv, options)
|
||||||
|
let content: Buffer = Buffer.concat([
|
||||||
|
cipher.update(data),
|
||||||
|
cipher.final(),
|
||||||
|
])
|
||||||
|
|
||||||
|
let tag: string | undefined = undefined
|
||||||
|
// @ts-ignore
|
||||||
|
if (mac) tag = cipher.getAuthTag().toString(Symmetric.Encoding)
|
||||||
|
|
||||||
|
const encrypted: EncryptedItem = {
|
||||||
|
alg,
|
||||||
|
data: content.toString(Symmetric.Encoding),
|
||||||
|
tag,
|
||||||
|
iv: iv.toString(Symmetric.Encoding),
|
||||||
|
salt: salt.toString(Symmetric.Encoding),
|
||||||
|
keySize,
|
||||||
|
tagSize: mac,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64.encode(JSON.stringify(encrypted))
|
||||||
|
}
|
||||||
|
|
||||||
|
static decrypt(e: string, pass: string): string {
|
||||||
|
const { alg, data, iv, tag, salt, keySize, tagSize }: EncryptedItem = JSON.parse(Base64.decode(e))
|
||||||
|
const key = scryptSync(pass, Buffer.from(salt, Symmetric.Encoding), keySize)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const options: TransformOptions = tag ? { authTagLength: tagSize } : undefined
|
||||||
|
const decipher: DecipherGCM | DecipherCCM | Decipher = createDecipheriv(alg, key, Buffer.from(iv, Symmetric.Encoding), options)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
if (tag) decipher.setAuthTag(Buffer.from(tag, Symmetric.Encoding))
|
||||||
|
|
||||||
|
const decrypted: Buffer = Buffer.concat([
|
||||||
|
decipher.update(Buffer.from(data, Symmetric.Encoding)),
|
||||||
|
decipher.final(),
|
||||||
|
])
|
||||||
|
|
||||||
|
return decrypted.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getCipherConfig = (type: Ciphers): CipherConfig => {
|
||||||
|
switch (type) {
|
||||||
|
case Ciphers.AES_128_GCM:
|
||||||
|
return {
|
||||||
|
alg: 'aes-128-gcm',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 16,
|
||||||
|
mac: 16,
|
||||||
|
}
|
||||||
|
case Ciphers.AES_192_GCM:
|
||||||
|
return {
|
||||||
|
alg: 'aes-192-gcm',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 24,
|
||||||
|
mac: 16,
|
||||||
|
}
|
||||||
|
case Ciphers.AES_256_GCM:
|
||||||
|
return {
|
||||||
|
alg: 'aes-256-gcm',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 32,
|
||||||
|
mac: 16,
|
||||||
|
}
|
||||||
|
case Ciphers.AES_128_CTR:
|
||||||
|
return {
|
||||||
|
alg: 'aes-128-ctr',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 16,
|
||||||
|
}
|
||||||
|
case Ciphers.AES_192_CTR:
|
||||||
|
return {
|
||||||
|
alg: 'aes-192-ctr',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 24,
|
||||||
|
}
|
||||||
|
case Ciphers.AES_256_CTR:
|
||||||
|
return {
|
||||||
|
alg: 'aes-256-ctr',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 32,
|
||||||
|
}
|
||||||
|
case Ciphers.ChaCha20:
|
||||||
|
return {
|
||||||
|
alg: 'chacha20',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 32,
|
||||||
|
}
|
||||||
|
case Ciphers.ChaCha20_Poly1305:
|
||||||
|
return {
|
||||||
|
alg: 'chacha20-poly1305',
|
||||||
|
ivSize: 16,
|
||||||
|
keySize: 32,
|
||||||
|
mac: 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/index.ts
12
src/index.ts
@ -1,12 +1,12 @@
|
|||||||
import AES from './AES'
|
|
||||||
import RSA from './RSA'
|
import RSA from './RSA'
|
||||||
|
import Symmetric from './Symmetric'
|
||||||
|
|
||||||
export default {
|
const exp = {
|
||||||
RSA,
|
RSA,
|
||||||
AES,
|
Symmetric,
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
module.exports = {
|
||||||
AES,
|
...exp,
|
||||||
RSA,
|
default: exp,
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user