Skip to content

NamesMT/sencrypt

Repository files navigation

sencrypt TypeScript heart icon

npm version npm downloads Codecov Bundlejs jsDocs.io

sencrypt (SEncrypt - Stateful-salt Encryption)

SEncrypt is a collection of helpers to implement an encrypted secret system.

SEncrypt requires a storage interface to be passed in, which is used by SHash to store the stateful salt.

SEncrypt takes a plaintext, encrypts it with a secret hash generated by SHash (which requires a salt, partition, and id), and stores the ciphertext

SEncrypt extends upon and uses SHash under-the-hood to manage the secret hash key.
Please take a look at SHash.

SEncrypt supports any encryption algorithm, it is recommended to use AES-GCM for the balance of security and performance,

Note: Encryption algorithm is not included in this package, you can use any encryption algorithm you want.

Features

  • TypeScript ready!

Usage

Install package:

# npm
npm install @namesmt/sencrypt

# yarn
yarn add @namesmt/sencrypt

# pnpm (recommended)
pnpm install @namesmt/sencrypt

Import and use:

// ESM
import { SEncrypt, SEncryptEncrypterInterface, SEncryptStorageInterface } from '@namesmt/sencrypt'
import { decrypt as aesGcmDecrypt, encrypt as aesGcmEncrypt } from '@namesmt/aes-gcm'

/**
 * This is a simple in-memory storage implementation.
 * 
 * This is not recommended for production use, but it is useful for testing.
 */
export class MemoryStorage implements SEncryptStorageInterface {
  saltStore: Record<string, string> = {}

  cipherStore: Record<string, string> = {}

  async getSalt(partition: string, id: string) { return this.saltStore[`${partition}#${id}`] }
  async setSalt(partition: string, id: string, value: string) { this.saltStore[`${partition}#${id}`] = value }

  async getCiphertext(partition: string, id: string) { return this.cipherStore[`${partition}#${id}`] }
  async setCiphertext(partition: string, id: string, value: string) { this.cipherStore[`${partition}#${id}`] = value }
}

export class AesGcmEncrypter implements SEncryptEncrypterInterface {
  encrypt = aesGcmEncrypt

  decrypt = aesGcmDecrypt
}

// A simple hash function for demo purposes
function demoHash(str: string) {
  return `${str}-demohashed`
}

const {
  encrypt, // Encrypts plaintext into ciphertext, secured with a hash key created from the given salt, partition and id.
  encryptStore, // ^^^ but also stores the ciphertext into the storage.
  decrypt, // Decrypts a ciphertext that was secured with a hash key created from the given salt, partition and id, back into plaintext.
  decryptStored, // ^^^ but the ciphertext is retrieved from the storage.
  decryptStoredFlash, // ^^^ and the ciphertext is deleted from the storage after decryption.
} = new SEncrypt(new MemoryStorage(), demoHash, new AesGcmEncrypter()) // You could pass in any hashing and encryption algorithm.

const encrypted = await encrypt('salt', 'partition', 'id', 'plaintext') // encrypted string of 'plaintext'
const decrypted = await decrypt('salt', 'partition', 'id', encrypted) // returns 'plaintext'

const storedEncrypted = await encryptStore('salt', 'partition', 'id', 'plaintext') // encrypted string of 'plaintext'
const storedDecrypted = await decryptStored('salt', 'partition', 'id') // returns 'plaintext'
const storedDecryptedFlash = await decryptStoredFlash('salt', 'partition', 'id') // returns 'plaintext'

const storedDecrypted_error = await decryptStored('salt', 'partition', 'id') // Should throw an error because the ciphertext is not found.

Roadmap

  • Become the legendary 10000x developer

License License

MIT License © 2024 NamesMT