import sodium from "libsodium-wrappers" export type KeyPair = { publicKey: Uint8Array privateKey: Uint8Array } export type EncryptedPayload = { nonce: Uint8Array cipher: Uint8Array } export type EncryptedPrivateKey = { nonce: Uint8Array cipher: Uint8Array salt: Uint8Array } export const useCryptoStore = () => { let ready = false const init = async (): Promise => { if (!ready) { await sodium.ready ready = true } } /** * USER KEY PAIR */ const generateUserKeyPair = (): KeyPair => { const pair = sodium.crypto_box_keypair() return { publicKey: pair.publicKey, privateKey: pair.privateKey } } /** * MASTER KEY */ const generateMasterKey = (): Uint8Array => { return sodium.randombytes_buf( sodium.crypto_secretbox_KEYBYTES ) } /** * ENCRYPT PRIVATE KEY */ const encryptPrivateKey = ( privateKey: Uint8Array, masterKey: Uint8Array ): EncryptedPayload => { const nonce = sodium.randombytes_buf( sodium.crypto_secretbox_NONCEBYTES ) const cipher = sodium.crypto_secretbox_easy( privateKey, nonce, masterKey ) return { nonce, cipher } } /** * DECRYPT PRIVATE KEY */ const decryptPrivateKey = ( encrypted: EncryptedPayload, masterKey: Uint8Array ): Uint8Array => { return sodium.crypto_secretbox_open_easy( encrypted.cipher, encrypted.nonce, masterKey ) } /** * LIST KEY */ const generateListKey = (): Uint8Array => { return sodium.randombytes_buf( sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES ) } /** * ENCRYPT LIST DATA */ const encryptList = ( data: string, listKey: Uint8Array ): EncryptedPayload => { const nonce = sodium.randombytes_buf( sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES ) const cipher = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt( sodium.from_string(data), null, null, nonce, listKey ) return { nonce, cipher } } /** * DECRYPT LIST DATA */ const decryptList = ( encrypted: EncryptedPayload, listKey: Uint8Array ): string => { const decrypted = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt( null, encrypted.cipher, null, encrypted.nonce, listKey ) return sodium.to_string(decrypted) } /** * ENCRYPT LIST KEY FOR USER */ const encryptListKeyForUser = ( listKey: Uint8Array, recipientPublicKey: Uint8Array, senderPrivateKey: Uint8Array ): EncryptedPayload => { const nonce = sodium.randombytes_buf( sodium.crypto_box_NONCEBYTES ) const cipher = sodium.crypto_box_easy( listKey, nonce, recipientPublicKey, senderPrivateKey ) return { nonce, cipher } } /** * DECRYPT LIST KEY */ const decryptListKey = ( encrypted: EncryptedPayload, senderPublicKey: Uint8Array, recipientPrivateKey: Uint8Array ): Uint8Array => { return sodium.crypto_box_open_easy( encrypted.cipher, encrypted.nonce, senderPublicKey, recipientPrivateKey ) } return { init, generateUserKeyPair, generateMasterKey, encryptPrivateKey, decryptPrivateKey, generateListKey, encryptList, decryptList, encryptListKeyForUser, decryptListKey } }