๐Ÿงช fluorine.js - NIP-44 encryption

fluorine.js is a miniature nostr-tools nip44: NIP-44 v2 encryption in about 3.5kb of ChaCha20, HMAC-SHA256 and padding over crypto.subtle. It derives conversation keys through helium.js's ECDH, then encrypts and decrypts payloads that any NIP-44 client can read. Loaded after helium.js, it gives helium's local signer a nip44 face like a NIP-07 extension's, and chlorine.js and iodine.js use it for private messages and encrypted content.

<script src="helium.js"></script>
<script src="fluorine.js"></script>
<script type="module">
	let box = await helium.signer.nip44.encrypt(theirPubkey, "meet at noon")
	let text = await helium.signer.nip44.decrypt(theirPubkey, box)
</script>

The local key encrypts a message only it and the owner of theirPubkey can read, and decrypts it again.

API

function returns
fluorine.key(sk, pk) conversation key (Uint8Array(32)) for a hex secret key and hex public key
fluorine.encrypt(text, key, nonce?) base64 payload. nonce is a Uint8Array(32), random by default
fluorine.decrypt(payload, key) plaintext, or throws on a bad version, length, MAC or padding
fluorine.nip44(sk) NIP-07 shaped {encrypt(pk, text), decrypt(pk, payload)} for that key, caching conversation keys
fluorine.pad(n) padded length for an n byte message
helium.signer.nip44 {encrypt, decrypt} using helium.key(), added when helium.js is loaded first

All functions return promises except nip44 and pad. Plaintext must be 1 to 65535 bytes of UTF-8.

Caveats

ECDH runs on helium's BigInt arithmetic, which is not constant time, and the local key lives in localStorage. Prefer an extension's nostr.nip44 for keys that matter. Tests check every official NIP-44 vector and round trips against nostr-tools.

fluorine.js (3.4/1.4kb raw/brotli), prefix nfl-, needs helium

← ๐Ÿ‘ป oganesson๐Ÿ”Œ chlorine →