library-ts/node/crypt/CryptIO.ts

221 lines
5.8 KiB
TypeScript
Raw Normal View History

2025-11-10 17:41:48 +00:00
import { promises as fs } from "fs";
import { CryptContainer } from "./CryptContainer";
import * as crypto from "crypto";
import * as path from "path";
import * as bcrypt from "bcrypt";
import { CryptSettings } from "./CryptSettings";
import { Files } from "../files/Files";
import { RJLog } from "../log/RJLog";
export class CryptIO
{
static readonly encryptionSuffix = ".crypt";
static readonly debugOutput = true;
static readonly debugOutputSuffix = ".crypt.json";
private static _cryptSettings:CryptSettings;
static set cryptSettings( cryptSettings:CryptSettings )
{
this._cryptSettings = cryptSettings;
}
static createUUID()
{
return crypto.randomUUID();
}
static isDebugFilePath( filePath:string ):boolean
{
return filePath.endsWith( this.debugOutputSuffix );
}
static async hash( data:string ):Promise<string>
{
let hash = await bcrypt.hash( data, 10 );
return Promise.resolve( hash );
}
static async verifyHash( data:string, hashed:string ):Promise<boolean>
{
let isVerified = await bcrypt.compare( data, hashed );
return Promise.resolve( isVerified );
}
static async encrypt( data:string, settings?:CryptSettings ):Promise<string>
{
settings = settings || CryptIO._cryptSettings;
const algorithm = 'aes-192-cbc';
const key = crypto.scryptSync( settings.publicKey, 'salt', 24 );
const cipher = crypto.createCipheriv( algorithm, key, settings.iv );
let encrypted = cipher.update( data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return Promise.resolve( encrypted );
}
static async decrypt( data:string, settings?:CryptSettings )
{
settings = settings || CryptIO._cryptSettings;
const algorithm = 'aes-192-cbc';
const key = crypto.scryptSync( settings.publicKey, 'salt', 24 );
const decipher = crypto.createDecipheriv( algorithm, key, settings.iv );
let decrypted = decipher.update( data, 'hex', 'utf8' );
decrypted += decipher.final('utf8');
return decrypted;
}
static async createWithRandomUUID<T>( parentPath:string, data:any, onUUID?:( uuid:string, data:T ) => T ):Promise<string>
{
let uuid = CryptIO.createUUID();
let fullPath = path.join( parentPath, uuid + this.encryptionSuffix );
let pathExists = await Files.exists( fullPath );
while ( pathExists )
{
uuid = CryptIO.createUUID();
fullPath = path.join( parentPath, uuid + this.encryptionSuffix );
pathExists = await Files.exists( fullPath );
}
if ( onUUID )
{
data = onUUID( uuid, data );
}
await CryptIO.saveJSON( fullPath, data );
return Promise.resolve( uuid );
}
static async loadWithRandomUUID<T>( parentPath:string, uuid:string ):Promise<T>
{
let fullPath = path.join( parentPath, uuid + this.encryptionSuffix );
let fileExsits = await Files.exists( fullPath );
if ( ! fileExsits )
{
return Promise.resolve( null );
}
let data = await this.loadJSON<T>( fullPath );
return data;
}
static async deleteWithRandomUUID( parentPath:string, uuid:string ):Promise<boolean>
{
let fullPath = path.join( parentPath, uuid + this.encryptionSuffix );
let exists = await Files.exists( fullPath );
if ( ! exists )
{
return Promise.resolve( false );
}
await Files.deleteFile( fullPath );
return Promise.resolve( true );
}
static async updateWithRandomUUID( parentPath:string, uuid:string, data:any ):Promise<void>
{
let fullPath = path.join( parentPath, uuid + this.encryptionSuffix );
await this.saveJSON( fullPath, data );
return Promise.resolve();
}
static async saveJSON( path:string, data:any ):Promise<void>
{
let jsonString = JSON.stringify( data );
RJLog.log( "Saving ", data, ">>", jsonString, );
let container:CryptContainer =
{
key:"",
data: await this.encrypt( jsonString )
};
let containerString = await this.encrypt( JSON.stringify( container ) );
await fs.writeFile( path, containerString );
if ( this.debugOutput )
{
await fs.writeFile( path + this.debugOutputSuffix, jsonString );
}
return Promise.resolve();
}
static async saveUTF8( path:string, jsonString:string ):Promise<void>
{
RJLog.log( "Saving ", jsonString, );
let container:CryptContainer =
{
key:"",
data: await this.encrypt( jsonString )
};
let containerString = await this.encrypt( JSON.stringify( container ) );
await fs.writeFile( path, containerString );
if ( this.debugOutput )
{
await fs.writeFile( path + this.debugOutputSuffix, jsonString );
}
return Promise.resolve();
}
static async loadJSON<T>( path:string ):Promise<T>
{
let encryptedContainerString = await fs.readFile( path );
let containerString = await this.decrypt( encryptedContainerString.toString() );
let container = JSON.parse( containerString ) as CryptContainer;
let encryptedData = container.data;
let jsonString = await this.decrypt( encryptedData );
let json = JSON.parse( jsonString ) as T;
return Promise.resolve( json );
}
static async loadUTF8( path:string ):Promise<string>
{
let encryptedContainerString = await fs.readFile( path );
let containerString = await this.decrypt( encryptedContainerString.toString() );
let container = JSON.parse( containerString ) as CryptContainer;
let encryptedData = container.data;
let jsonString = await this.decrypt( encryptedData );
return Promise.resolve( jsonString );
}
}