rely on native api

This commit is contained in:
cupcakearmy 2019-04-08 20:06:55 +02:00
parent a015d4f46a
commit 4f17d72ea7
4 changed files with 163 additions and 209 deletions

View File

@ -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,
}
}
}
}

View File

@ -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
View 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,
}
}
}
}

View File

@ -1,12 +1,12 @@
import AES from './AES'
import RSA from './RSA'
import Symmetric from './Symmetric'
export default {
const exp = {
RSA,
AES,
Symmetric,
}
export {
AES,
RSA,
module.exports = {
...exp,
default: exp,
}